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 }