308 lines
7.9 KiB
Go
308 lines
7.9 KiB
Go
package widget
|
|
|
|
import (
|
|
"eq2emu/internal/spawn"
|
|
)
|
|
|
|
// WidgetAction represents an action that can be performed on a widget
|
|
type WidgetAction interface {
|
|
Execute(w *Widget, caller *spawn.Spawn) error
|
|
}
|
|
|
|
// OpenDoor opens the widget (door/lift)
|
|
func (w *Widget) OpenDoor() {
|
|
w.mutex.Lock()
|
|
defer w.mutex.Unlock()
|
|
|
|
// Set heading if specified
|
|
if w.openHeading >= 0 {
|
|
w.SetHeadingFromFloat(w.openHeading)
|
|
}
|
|
|
|
// Handle position changes
|
|
openX := w.openX
|
|
openY := w.openY
|
|
openZ := w.openZ
|
|
|
|
if openX != 0 || openY != 0 || openZ != 0 {
|
|
x := w.GetX()
|
|
y := w.GetY()
|
|
z := w.GetZ()
|
|
|
|
// Use open positions if specified
|
|
if openX != 0 {
|
|
x = openX
|
|
}
|
|
if openY != 0 {
|
|
y = openY
|
|
}
|
|
if openZ != 0 {
|
|
z = openZ
|
|
}
|
|
|
|
// Add movement to the open position
|
|
// Speed of 4 units per second (from C++)
|
|
w.AddRunningLocation(x, y, z, 4)
|
|
|
|
// Calculate movement duration
|
|
diff := calculateDistance(w.GetX(), w.GetY(), w.GetZ(), x, y, z)
|
|
if diff < 0 {
|
|
diff = -diff
|
|
}
|
|
|
|
// Schedule timer for movement completion
|
|
// TODO: Zone will need to handle widget timers
|
|
// GetZone()->AddWidgetTimer(this, diff / 4)
|
|
}
|
|
|
|
// Update activity status for non-lifts
|
|
if w.widgetType != WidgetTypeLift {
|
|
w.SetActivityStatus(DefaultActivityOpen)
|
|
}
|
|
|
|
// Set open state
|
|
w.isOpen = true
|
|
|
|
// Schedule auto-close timer if duration is set
|
|
if w.openDuration > 0 {
|
|
// TODO: Zone will need to handle widget timers
|
|
// GetZone()->AddWidgetTimer(this, open_duration)
|
|
}
|
|
|
|
// TODO: Notify zone of spawn changes
|
|
// GetZone()->SendSpawnChanges(this)
|
|
}
|
|
|
|
// CloseDoor closes the widget (door/lift)
|
|
func (w *Widget) CloseDoor() {
|
|
w.mutex.Lock()
|
|
defer w.mutex.Unlock()
|
|
|
|
// Set heading
|
|
if w.closedHeading > 0 {
|
|
w.SetHeadingFromFloat(w.closedHeading)
|
|
} else if w.openHeading >= 0 {
|
|
// Fall back to original heading
|
|
w.SetHeadingFromFloat(w.GetSpawnOrigHeading())
|
|
}
|
|
|
|
// Update activity status for non-lifts
|
|
if w.widgetType != WidgetTypeLift {
|
|
w.SetActivityStatus(DefaultActivityClosed)
|
|
}
|
|
|
|
// Handle position changes
|
|
if w.closeX != 0 || w.closeY != 0 || w.closeZ != 0 || w.openX != 0 || w.openY != 0 || w.openZ != 0 {
|
|
// Default to original spawn position
|
|
x := w.GetSpawnOrigX()
|
|
y := w.GetSpawnOrigY()
|
|
z := w.GetSpawnOrigZ()
|
|
|
|
// Use close positions if specified
|
|
if w.closeX != 0 {
|
|
x = w.closeX
|
|
}
|
|
if w.closeY != 0 {
|
|
y = w.closeY
|
|
}
|
|
if w.closeZ != 0 {
|
|
z = w.closeZ
|
|
}
|
|
|
|
// Add movement to the close position
|
|
w.AddRunningLocation(x, y, z, 4)
|
|
|
|
// Calculate movement duration
|
|
diff := calculateDistance(w.GetX(), w.GetY(), w.GetZ(), x, y, z)
|
|
if diff < 0 {
|
|
diff = -diff
|
|
}
|
|
|
|
// Schedule timer for movement completion
|
|
// TODO: Zone will need to handle widget timers
|
|
// GetZone()->AddWidgetTimer(this, diff / 4)
|
|
}
|
|
|
|
// Set closed state
|
|
w.isOpen = false
|
|
|
|
// TODO: Notify zone of spawn changes
|
|
// GetZone()->SendSpawnChanges(this)
|
|
}
|
|
|
|
// ProcessUse processes the use of this widget
|
|
func (w *Widget) ProcessUse(caller *spawn.Spawn) {
|
|
// Skip if this is a lift that's currently in use
|
|
if w.widgetType == WidgetTypeLift {
|
|
// TODO: Check if widget has active timer in zone
|
|
// if GetZone()->HasWidgetTimer(this) return
|
|
}
|
|
|
|
// TODO: Call spawn script for custom handling
|
|
// if GetZone()->CallSpawnScript(this, SPAWN_SCRIPT_USEDOOR, caller, "", is_open)
|
|
// return // handled in lua
|
|
|
|
// Default behavior: toggle open/closed state
|
|
wasOpen := w.IsOpen()
|
|
if wasOpen {
|
|
w.CloseDoor()
|
|
} else {
|
|
w.OpenDoor()
|
|
}
|
|
|
|
// Play appropriate sound
|
|
if w.IsOpen() && w.openSound != "" {
|
|
// TODO: Play sound through zone
|
|
// GetZone()->PlaySoundFile(0, openSound, widgetX, widgetY, widgetZ)
|
|
} else if !w.IsOpen() && w.closeSound != "" {
|
|
// TODO: Play sound through zone
|
|
// GetZone()->PlaySoundFile(0, closeSound, widgetX, widgetY, widgetZ)
|
|
}
|
|
}
|
|
|
|
// HandleTimerUpdate handles widget timer updates
|
|
func (w *Widget) HandleTimerUpdate() {
|
|
// Lifts don't auto-close
|
|
if w.widgetType == WidgetTypeLift {
|
|
return
|
|
}
|
|
|
|
// Auto-close open doors
|
|
if w.widgetType == WidgetTypeDoor && w.IsOpen() {
|
|
w.HandleUse(nil, "")
|
|
}
|
|
}
|
|
|
|
// HandleUse handles widget interaction from a client
|
|
func (w *Widget) HandleUse(client ClientInterface, command string) {
|
|
// Handle override widget type for scripted behavior
|
|
overrideWidgetType := w.widgetType
|
|
|
|
// Client validation
|
|
if client != nil {
|
|
// TODO: Check quest requirements
|
|
// meetsQuestReqs := w.MeetsSpawnAccessRequirements(client.GetPlayer())
|
|
// if !meetsQuestReqs && (w.GetQuestsRequiredOverride() & 2) == 0 {
|
|
// return
|
|
// }
|
|
// if meetsQuestReqs && w.GetShowCommandIcon() != 1 {
|
|
// return
|
|
// }
|
|
}
|
|
|
|
// Handle transporter functionality
|
|
if client != nil && w.GetTransporterID() > 0 {
|
|
// TODO: Handle transporter destinations
|
|
// client.SetTemporaryTransportID(0)
|
|
// destinations := GetZone()->GetTransporters(client, w.GetTransporterID())
|
|
// if len(destinations) > 0 {
|
|
// client.ProcessTeleport(w, destinations, w.GetTransporterID())
|
|
// return
|
|
// }
|
|
}
|
|
|
|
// Skip house commands for certain operations
|
|
skipHouseCommands := isCommand(command, "access") || isCommand(command, "visit")
|
|
|
|
// Handle door/lift widgets
|
|
if !skipHouseCommands && (overrideWidgetType == WidgetTypeDoor || overrideWidgetType == WidgetTypeLift) {
|
|
// Resolve action spawn if needed
|
|
if w.actionSpawn == nil && w.actionSpawnID > 0 {
|
|
// TODO: Get spawn from zone
|
|
// spawn := GetZone()->GetSpawnByDatabaseID(w.actionSpawnID)
|
|
// if spawn != nil && spawn.IsWidget() {
|
|
// w.actionSpawn = spawn.(*Widget)
|
|
// }
|
|
}
|
|
|
|
// Resolve linked spawn if needed
|
|
if w.linkedSpawn == nil && w.linkedSpawnID > 0 {
|
|
// TODO: Get spawn from zone
|
|
// spawn := GetZone()->GetSpawnByDatabaseID(w.linkedSpawnID)
|
|
// if spawn != nil && spawn.IsWidget() {
|
|
// w.linkedSpawn = spawn.(*Widget)
|
|
// }
|
|
}
|
|
|
|
// Process linked spawns
|
|
widget := w
|
|
if w.linkedSpawn != nil {
|
|
widget = w.linkedSpawn
|
|
// Fire the first door
|
|
var caller *spawn.Spawn
|
|
if client != nil {
|
|
caller = client.GetPlayer()
|
|
}
|
|
w.ProcessUse(caller)
|
|
} else if w.actionSpawn != nil {
|
|
widget = w.actionSpawn
|
|
// Resolve action spawn's linked spawn if needed
|
|
if widget.linkedSpawn == nil && widget.linkedSpawnID > 0 {
|
|
// TODO: Get spawn from zone
|
|
// spawn := GetZone()->GetSpawnByDatabaseID(widget.linkedSpawnID)
|
|
// if spawn != nil && spawn.IsWidget() {
|
|
// widget.linkedSpawn = spawn.(*Widget)
|
|
// }
|
|
}
|
|
|
|
// Process linked spawn first
|
|
if widget.linkedSpawn != nil {
|
|
var caller *spawn.Spawn
|
|
if client != nil {
|
|
caller = client.GetPlayer()
|
|
}
|
|
widget.linkedSpawn.ProcessUse(caller)
|
|
}
|
|
}
|
|
|
|
// Process the main widget
|
|
var caller *spawn.Spawn
|
|
if client != nil {
|
|
caller = client.GetPlayer()
|
|
}
|
|
widget.ProcessUse(caller)
|
|
} else if client != nil && isCommand(command, "access") && w.houseID > 0 {
|
|
// Handle house access
|
|
// TODO: Implement house access functionality
|
|
// This involves PlayerHouse and HouseZone systems
|
|
} else if client != nil && isCommand(command, "visit") && w.houseID > 0 {
|
|
// Handle house visit
|
|
// TODO: Implement house visit functionality
|
|
} else if client != nil && command != "" {
|
|
// Handle other entity commands
|
|
// TODO: Process entity commands
|
|
// entityCommand := w.FindEntityCommand(command)
|
|
// if entityCommand != nil {
|
|
// GetZone()->ProcessEntityCommand(entityCommand, client.GetPlayer(), client.GetPlayer().GetTarget())
|
|
// }
|
|
}
|
|
}
|
|
|
|
// SetLinkedSpawn sets the linked spawn widget
|
|
func (w *Widget) SetLinkedSpawn(linked *Widget) {
|
|
w.mutex.Lock()
|
|
defer w.mutex.Unlock()
|
|
w.linkedSpawn = linked
|
|
}
|
|
|
|
// GetLinkedSpawn returns the linked spawn widget
|
|
func (w *Widget) GetLinkedSpawn() *Widget {
|
|
w.mutex.RLock()
|
|
defer w.mutex.RUnlock()
|
|
return w.linkedSpawn
|
|
}
|
|
|
|
// SetActionSpawn sets the action spawn widget
|
|
func (w *Widget) SetActionSpawn(action *Widget) {
|
|
w.mutex.Lock()
|
|
defer w.mutex.Unlock()
|
|
w.actionSpawn = action
|
|
}
|
|
|
|
// GetActionSpawn returns the action spawn widget
|
|
func (w *Widget) GetActionSpawn() *Widget {
|
|
w.mutex.RLock()
|
|
defer w.mutex.RUnlock()
|
|
return w.actionSpawn
|
|
}
|