eq2go/internal/widget/actions.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.SetHeading(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.SetHeading(w.closedHeading)
} else if w.openHeading >= 0 {
// Fall back to original heading
w.SetHeading(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
}