package object import ( "fmt" "eq2emu/internal/spawn" ) // ObjectSpawn represents an object that extends spawn functionality // This properly integrates with the existing spawn system type ObjectSpawn struct { *spawn.Spawn // Embed the spawn functionality // Object-specific properties clickable bool // Whether the object can be clicked/interacted with deviceID int8 // Device ID for interactive objects // Merchant properties (duplicated from spawn since fields are unexported) merchantID int32 merchantType int8 merchantMinLevel int32 merchantMaxLevel int32 isCollector bool // Transport properties (duplicated from spawn since fields are unexported) transporterID int32 } // NewObjectSpawn creates a new object spawn with default values func NewObjectSpawn() *ObjectSpawn { // Create base spawn baseSpawn := spawn.NewSpawn() // Set object-specific spawn properties baseSpawn.SetSpawnType(ObjectSpawnType) // Set object appearance defaults appearance := baseSpawn.GetAppearanceData() appearance.ActivityStatus = ObjectActivityStatus appearance.Pos.State = ObjectPosState appearance.Difficulty = ObjectDifficulty // Note: No SetAppearance method, but appearance is modified by reference return &ObjectSpawn{ Spawn: baseSpawn, clickable: false, deviceID: DeviceIDNone, } } // SetClickable sets whether the object can be clicked func (os *ObjectSpawn) SetClickable(clickable bool) { os.clickable = clickable } // IsClickable returns whether the object can be clicked func (os *ObjectSpawn) IsClickable() bool { return os.clickable } // SetDeviceID sets the device ID for interactive objects func (os *ObjectSpawn) SetDeviceID(deviceID int8) { os.deviceID = deviceID } // GetDeviceID returns the device ID func (os *ObjectSpawn) GetDeviceID() int8 { return os.deviceID } // IsObject always returns true for object spawns func (os *ObjectSpawn) IsObject() bool { return true } // Copy creates a deep copy of the object spawn func (os *ObjectSpawn) Copy() *ObjectSpawn { // Create new object spawn with new spawn newObjectSpawn := NewObjectSpawn() // Copy properties from original newObjectSpawn.clickable = os.clickable newObjectSpawn.deviceID = os.deviceID // Copy spawn properties newObjectSpawn.SetDatabaseID(os.GetDatabaseID()) newObjectSpawn.SetID(os.GetID()) newObjectSpawn.SetName(os.GetName()) newObjectSpawn.SetLevel(os.GetLevel()) newObjectSpawn.SetSize(os.GetSize()) newObjectSpawn.SetSpawnType(os.GetSpawnType()) newObjectSpawn.SetX(os.GetX()) newObjectSpawn.SetY(os.GetY(), false) newObjectSpawn.SetZ(os.GetZ()) newObjectSpawn.SetHeading(int16(os.GetHeading()), int16(os.GetHeading())) newObjectSpawn.SetFactionID(os.GetFactionID()) newObjectSpawn.SetTarget(os.GetTarget()) newObjectSpawn.SetAlive(os.IsAlive()) // Copy merchant properties if they exist if os.merchantID > 0 { newObjectSpawn.merchantID = os.merchantID newObjectSpawn.merchantType = os.merchantType newObjectSpawn.merchantMinLevel = os.merchantMinLevel newObjectSpawn.merchantMaxLevel = os.merchantMaxLevel } // Copy transport properties if they exist if os.transporterID > 0 { newObjectSpawn.transporterID = os.transporterID } newObjectSpawn.isCollector = os.isCollector return newObjectSpawn } // HandleUse processes object interaction by a client func (os *ObjectSpawn) HandleUse(clientID int32, command string) error { // Use the base object's HandleUse logic but with spawn integration object := &Object{} // Copy relevant properties for handling object.clickable = os.clickable object.deviceID = os.deviceID object.transporterID = os.transporterID object.appearanceShowCommandIcon = int8(0) if os.GetAppearanceData().ShowCommandIcon == 1 { object.appearanceShowCommandIcon = ObjectShowCommandIcon } // TODO: Copy command lists when they're integrated with spawn system return object.HandleUse(clientID, command) } // SetShowCommandIcon sets whether to show the command icon func (os *ObjectSpawn) SetShowCommandIcon(show bool) { appearance := os.GetAppearanceData() if show { appearance.ShowCommandIcon = ObjectShowCommandIcon } else { appearance.ShowCommandIcon = 0 } // Appearance is modified by reference, no need to set it back } // ShowsCommandIcon returns whether the command icon is shown func (os *ObjectSpawn) ShowsCommandIcon() bool { return os.GetAppearanceData().ShowCommandIcon == ObjectShowCommandIcon } // GetObjectInfo returns comprehensive information about the object spawn func (os *ObjectSpawn) GetObjectInfo() map[string]any { info := make(map[string]any) // Add spawn info info["spawn_id"] = os.GetID() info["database_id"] = os.GetDatabaseID() info["zone_name"] = os.GetZoneName() info["spawn_type"] = os.GetSpawnType() info["size"] = os.GetSize() // Add object-specific info info["clickable"] = os.clickable info["device_id"] = os.deviceID info["shows_command_icon"] = os.ShowsCommandIcon() info["transporter_id"] = os.transporterID info["merchant_id"] = os.merchantID info["is_collector"] = os.isCollector // Add position info appearance := os.GetAppearanceData() info["x"] = appearance.Pos.X info["y"] = appearance.Pos.Y info["z"] = appearance.Pos.Z info["heading"] = appearance.Pos.Dir1 return info } // Getter and setter methods for object-specific properties // GetMerchantID returns the merchant ID func (os *ObjectSpawn) GetMerchantID() int32 { return os.merchantID } // SetMerchantID sets the merchant ID func (os *ObjectSpawn) SetMerchantID(merchantID int32) { os.merchantID = merchantID } // GetMerchantType returns the merchant type func (os *ObjectSpawn) GetMerchantType() int8 { return os.merchantType } // SetMerchantType sets the merchant type func (os *ObjectSpawn) SetMerchantType(merchantType int8) { os.merchantType = merchantType } // GetMerchantMinLevel returns the minimum merchant level func (os *ObjectSpawn) GetMerchantMinLevel() int8 { return int8(os.merchantMinLevel) } // GetMerchantMaxLevel returns the maximum merchant level func (os *ObjectSpawn) GetMerchantMaxLevel() int8 { return int8(os.merchantMaxLevel) } // SetMerchantLevelRange sets the merchant level range func (os *ObjectSpawn) SetMerchantLevelRange(minLevel, maxLevel int8) { os.merchantMinLevel = int32(minLevel) os.merchantMaxLevel = int32(maxLevel) } // GetTransporterID returns the transporter ID func (os *ObjectSpawn) GetTransporterID() int32 { return os.transporterID } // SetTransporterID sets the transporter ID func (os *ObjectSpawn) SetTransporterID(transporterID int32) { os.transporterID = transporterID } // IsCollector returns whether this object is a collector func (os *ObjectSpawn) IsCollector() bool { return os.isCollector } // SetCollector sets whether this object is a collector func (os *ObjectSpawn) SetCollector(isCollector bool) { os.isCollector = isCollector } // GetZoneName returns the zone name (from spawn system) func (os *ObjectSpawn) GetZoneName() string { // TODO: Implement when zone system is integrated // For now return empty string return "" } // SetZoneName sets the zone name (placeholder for spawn system) func (os *ObjectSpawn) SetZoneName(zoneName string) { // TODO: Implement when zone system is integrated // This would be handled by the spawn system } // ObjectSpawnManager manages object spawns specifically type ObjectSpawnManager struct { objects map[int32]*ObjectSpawn // Object spawns by spawn ID // TODO: Add reference to spawn manager when it exists } // NewObjectSpawnManager creates a new object spawn manager func NewObjectSpawnManager() *ObjectSpawnManager { return &ObjectSpawnManager{ objects: make(map[int32]*ObjectSpawn), } } // AddObjectSpawn adds an object spawn to the manager func (osm *ObjectSpawnManager) AddObjectSpawn(objectSpawn *ObjectSpawn) error { if objectSpawn == nil { return fmt.Errorf("cannot add nil object spawn") } // Add to object tracking osm.objects[objectSpawn.GetID()] = objectSpawn // TODO: Add to global spawn manager when it exists return nil } // RemoveObjectSpawn removes an object spawn from the manager func (osm *ObjectSpawnManager) RemoveObjectSpawn(spawnID int32) error { // Remove from object tracking if _, exists := osm.objects[spawnID]; !exists { return fmt.Errorf("object spawn %d not found", spawnID) } delete(osm.objects, spawnID) // TODO: Remove from global spawn manager when it exists return nil } // GetObjectSpawn retrieves an object spawn by ID func (osm *ObjectSpawnManager) GetObjectSpawn(spawnID int32) *ObjectSpawn { return osm.objects[spawnID] } // GetObjectSpawnsByZone returns all object spawns in a zone func (osm *ObjectSpawnManager) GetObjectSpawnsByZone(zoneName string) []*ObjectSpawn { result := make([]*ObjectSpawn, 0) // TODO: Filter by zone when zone system is implemented // For now, return empty slice return result } // GetInteractiveObjectSpawns returns all interactive object spawns func (osm *ObjectSpawnManager) GetInteractiveObjectSpawns() []*ObjectSpawn { result := make([]*ObjectSpawn, 0) for _, objectSpawn := range osm.objects { if objectSpawn.IsClickable() || objectSpawn.ShowsCommandIcon() { result = append(result, objectSpawn) } } return result } // ProcessObjectInteraction handles interaction with an object spawn func (osm *ObjectSpawnManager) ProcessObjectInteraction(spawnID, clientID int32, command string) error { objectSpawn := osm.GetObjectSpawn(spawnID) if objectSpawn == nil { return fmt.Errorf("object spawn %d not found", spawnID) } return objectSpawn.HandleUse(clientID, command) } // ConvertSpawnToObject converts a regular spawn to an object spawn (if applicable) func ConvertSpawnToObject(spawn *spawn.Spawn) *ObjectSpawn { if spawn.GetSpawnType() != ObjectSpawnType { return nil } objectSpawn := &ObjectSpawn{ Spawn: spawn, clickable: false, // Default, should be loaded from data deviceID: DeviceIDNone, } // Set clickable based on appearance flags or other indicators appearance := spawn.GetAppearanceData() if appearance.ShowCommandIcon == ObjectShowCommandIcon { objectSpawn.clickable = true } return objectSpawn } // LoadObjectSpawnFromData loads object spawn data from database/config // This would be called when loading spawns from the database func LoadObjectSpawnFromData(spawnData map[string]any) *ObjectSpawn { objectSpawn := NewObjectSpawn() // Load basic spawn data if databaseID, ok := spawnData["database_id"].(int32); ok { objectSpawn.SetDatabaseID(databaseID) } if zoneName, ok := spawnData["zone"].(string); ok { objectSpawn.SetZoneName(zoneName) } // Load object-specific data if clickable, ok := spawnData["clickable"].(bool); ok { objectSpawn.SetClickable(clickable) } if deviceID, ok := spawnData["device_id"].(int8); ok { objectSpawn.SetDeviceID(deviceID) } // Load position data if x, ok := spawnData["x"].(float32); ok { objectSpawn.SetX(x) } if y, ok := spawnData["y"].(float32); ok { objectSpawn.SetY(y, false) } if z, ok := spawnData["z"].(float32); ok { objectSpawn.SetZ(z) } // TODO: Load other properties as needed return objectSpawn }