package object import ( "fmt" "math/rand" "strings" "sync" ) // Object represents a game object that extends spawn functionality // Converted from C++ Object class type Object struct { // Embed spawn functionality - TODO: Use actual spawn.Spawn when integrated // spawn.Spawn // Object-specific properties clickable bool // Whether the object can be clicked/interacted with zoneName string // Name of the zone this object belongs to deviceID int8 // Device ID for interactive objects // Inherited spawn properties (placeholder until spawn integration) databaseID int32 size int16 sizeOffset int8 merchantID int32 merchantType int8 merchantMinLevel int8 merchantMaxLevel int8 isCollector bool factionID int32 totalHP int32 totalPower int32 currentHP int32 currentPower int32 transporterID int32 soundsDisabled bool omittedByDBFlag bool lootTier int8 lootDropType int8 spawnScript string spawnScriptSetDB bool primaryCommandListID int32 secondaryCommandListID int32 // Appearance data placeholder - TODO: Use actual appearance struct appearanceActivityStatus int8 appearancePosState int8 appearanceDifficulty int8 appearanceShowCommandIcon int8 // Command lists - TODO: Use actual command structures primaryCommands []string secondaryCommands []string // Thread safety mutex sync.RWMutex } // NewObject creates a new object with default values // Converted from C++ Object::Object constructor func NewObject() *Object { return &Object{ clickable: false, zoneName: "", deviceID: DeviceIDNone, appearanceActivityStatus: ObjectActivityStatus, appearancePosState: ObjectPosState, appearanceDifficulty: ObjectDifficulty, appearanceShowCommandIcon: 0, primaryCommands: make([]string, 0), secondaryCommands: make([]string, 0), } } // SetClickable sets whether the object can be clicked func (o *Object) SetClickable(clickable bool) { o.mutex.Lock() defer o.mutex.Unlock() o.clickable = clickable } // IsClickable returns whether the object can be clicked func (o *Object) IsClickable() bool { o.mutex.RLock() defer o.mutex.RUnlock() return o.clickable } // SetZone sets the zone name for this object // Converted from C++ Object::SetZone func (o *Object) SetZone(zoneName string) { o.mutex.Lock() defer o.mutex.Unlock() o.zoneName = zoneName } // GetZoneName returns the zone name for this object func (o *Object) GetZoneName() string { o.mutex.RLock() defer o.mutex.RUnlock() return o.zoneName } // SetDeviceID sets the device ID for interactive objects // Converted from C++ Object::SetDeviceID func (o *Object) SetDeviceID(deviceID int8) { o.mutex.Lock() defer o.mutex.Unlock() o.deviceID = deviceID } // GetDeviceID returns the device ID // Converted from C++ Object::GetDeviceID func (o *Object) GetDeviceID() int8 { o.mutex.RLock() defer o.mutex.RUnlock() return o.deviceID } // IsObject always returns true for Object instances // Converted from C++ Object::IsObject func (o *Object) IsObject() bool { return true } // Copy creates a deep copy of the object // Converted from C++ Object::Copy func (o *Object) Copy() *Object { o.mutex.RLock() defer o.mutex.RUnlock() newObject := NewObject() // Copy basic properties newObject.clickable = o.clickable newObject.zoneName = o.zoneName newObject.deviceID = o.deviceID newObject.databaseID = o.databaseID newObject.merchantID = o.merchantID newObject.merchantType = o.merchantType newObject.merchantMinLevel = o.merchantMinLevel newObject.merchantMaxLevel = o.merchantMaxLevel newObject.isCollector = o.isCollector newObject.factionID = o.factionID newObject.totalHP = o.totalHP newObject.totalPower = o.totalPower newObject.currentHP = o.currentHP newObject.currentPower = o.currentPower newObject.transporterID = o.transporterID newObject.soundsDisabled = o.soundsDisabled newObject.omittedByDBFlag = o.omittedByDBFlag newObject.lootTier = o.lootTier newObject.lootDropType = o.lootDropType newObject.spawnScript = o.spawnScript newObject.spawnScriptSetDB = o.spawnScriptSetDB newObject.primaryCommandListID = o.primaryCommandListID newObject.secondaryCommandListID = o.secondaryCommandListID // Copy appearance data newObject.appearanceActivityStatus = o.appearanceActivityStatus newObject.appearancePosState = o.appearancePosState newObject.appearanceDifficulty = o.appearanceDifficulty newObject.appearanceShowCommandIcon = o.appearanceShowCommandIcon // Handle size with random offset (from C++ logic) if o.sizeOffset > 0 { offset := o.sizeOffset + 1 tmpSize := int32(o.size) + (rand.Int31n(int32(offset)) - rand.Int31n(int32(offset))) if tmpSize < 0 { tmpSize = 1 } else if tmpSize >= 0xFFFF { tmpSize = 0xFFFF } newObject.size = int16(tmpSize) } else { newObject.size = o.size } // Copy command lists newObject.primaryCommands = make([]string, len(o.primaryCommands)) copy(newObject.primaryCommands, o.primaryCommands) newObject.secondaryCommands = make([]string, len(o.secondaryCommands)) copy(newObject.secondaryCommands, o.secondaryCommands) return newObject } // HandleUse processes object interaction by a client // Converted from C++ Object::HandleUse func (o *Object) HandleUse(clientID int32, command string) error { o.mutex.RLock() defer o.mutex.RUnlock() // TODO: Implement transport destination handling when zone system is available // This would check for transporter ID and process teleportation if o.transporterID > 0 { // Handle transport logic return o.handleTransport(clientID) } // Handle command-based interaction if len(command) > 0 && o.appearanceShowCommandIcon == ObjectShowCommandIcon { return o.handleCommand(clientID, command) } return fmt.Errorf("object is not interactive") } // handleTransport processes transport/teleport functionality func (o *Object) handleTransport(clientID int32) error { // TODO: Implement when zone and transport systems are available // This would: // 1. Get transport destinations for this object // 2. Present options to the client // 3. Process teleportation request return fmt.Errorf("transport system not yet implemented") } // handleCommand processes command-based object interaction func (o *Object) handleCommand(clientID int32, command string) error { // TODO: Implement when entity command system is available // This would: // 1. Find the entity command by name // 2. Validate client permissions // 3. Execute the command command = strings.TrimSpace(strings.ToLower(command)) // Check if command exists in primary or secondary commands for _, cmd := range o.primaryCommands { if strings.ToLower(cmd) == command { return o.executeCommand(clientID, cmd) } } for _, cmd := range o.secondaryCommands { if strings.ToLower(cmd) == command { return o.executeCommand(clientID, cmd) } } return fmt.Errorf("command '%s' not found", command) } // executeCommand executes a specific command for a client func (o *Object) executeCommand(clientID int32, command string) error { // TODO: Implement actual command execution when command system is available // For now, just return success for valid commands return nil } // Serialize returns packet data for this object // Converted from C++ Object::serialize func (o *Object) Serialize(playerID int32, version int16) ([]byte, error) { // TODO: Implement actual serialization when packet system is available // This would call the spawn serialization method return nil, fmt.Errorf("serialization not yet implemented") } // Getter/Setter methods for inherited spawn properties // SetCollector sets whether this object is a collector func (o *Object) SetCollector(isCollector bool) { o.mutex.Lock() defer o.mutex.Unlock() o.isCollector = isCollector } // IsCollector returns whether this object is a collector func (o *Object) IsCollector() bool { o.mutex.RLock() defer o.mutex.RUnlock() return o.isCollector } // SetMerchantID sets the merchant ID func (o *Object) SetMerchantID(merchantID int32) { o.mutex.Lock() defer o.mutex.Unlock() o.merchantID = merchantID } // GetMerchantID returns the merchant ID func (o *Object) GetMerchantID() int32 { o.mutex.RLock() defer o.mutex.RUnlock() return o.merchantID } // SetMerchantType sets the merchant type func (o *Object) SetMerchantType(merchantType int8) { o.mutex.Lock() defer o.mutex.Unlock() o.merchantType = merchantType } // GetMerchantType returns the merchant type func (o *Object) GetMerchantType() int8 { o.mutex.RLock() defer o.mutex.RUnlock() return o.merchantType } // SetMerchantLevelRange sets the merchant level range func (o *Object) SetMerchantLevelRange(minLevel, maxLevel int8) { o.mutex.Lock() defer o.mutex.Unlock() o.merchantMinLevel = minLevel o.merchantMaxLevel = maxLevel } // GetMerchantMinLevel returns the minimum merchant level func (o *Object) GetMerchantMinLevel() int8 { o.mutex.RLock() defer o.mutex.RUnlock() return o.merchantMinLevel } // GetMerchantMaxLevel returns the maximum merchant level func (o *Object) GetMerchantMaxLevel() int8 { o.mutex.RLock() defer o.mutex.RUnlock() return o.merchantMaxLevel } // SetSize sets the object size func (o *Object) SetSize(size int16) { o.mutex.Lock() defer o.mutex.Unlock() o.size = size } // GetSize returns the object size func (o *Object) GetSize() int16 { o.mutex.RLock() defer o.mutex.RUnlock() return o.size } // SetSizeOffset sets the size randomization offset func (o *Object) SetSizeOffset(offset int8) { o.mutex.Lock() defer o.mutex.Unlock() o.sizeOffset = offset } // GetSizeOffset returns the size randomization offset func (o *Object) GetSizeOffset() int8 { o.mutex.RLock() defer o.mutex.RUnlock() return o.sizeOffset } // SetPrimaryCommands sets the primary command list func (o *Object) SetPrimaryCommands(commands []string) { o.mutex.Lock() defer o.mutex.Unlock() o.primaryCommands = make([]string, len(commands)) copy(o.primaryCommands, commands) } // GetPrimaryCommands returns the primary command list func (o *Object) GetPrimaryCommands() []string { o.mutex.RLock() defer o.mutex.RUnlock() commands := make([]string, len(o.primaryCommands)) copy(commands, o.primaryCommands) return commands } // SetSecondaryCommands sets the secondary command list func (o *Object) SetSecondaryCommands(commands []string) { o.mutex.Lock() defer o.mutex.Unlock() o.secondaryCommands = make([]string, len(commands)) copy(o.secondaryCommands, commands) } // GetSecondaryCommands returns the secondary command list func (o *Object) GetSecondaryCommands() []string { o.mutex.RLock() defer o.mutex.RUnlock() commands := make([]string, len(o.secondaryCommands)) copy(commands, o.secondaryCommands) return commands } // SetDatabaseID sets the database ID func (o *Object) SetDatabaseID(id int32) { o.mutex.Lock() defer o.mutex.Unlock() o.databaseID = id } // GetDatabaseID returns the database ID func (o *Object) GetDatabaseID() int32 { o.mutex.RLock() defer o.mutex.RUnlock() return o.databaseID } // SetFactionID sets the faction ID func (o *Object) SetFactionID(factionID int32) { o.mutex.Lock() defer o.mutex.Unlock() o.factionID = factionID } // GetFactionID returns the faction ID func (o *Object) GetFactionID() int32 { o.mutex.RLock() defer o.mutex.RUnlock() return o.factionID } // SetTotalHP sets the total hit points func (o *Object) SetTotalHP(hp int32) { o.mutex.Lock() defer o.mutex.Unlock() o.totalHP = hp } // GetTotalHP returns the total hit points func (o *Object) GetTotalHP() int32 { o.mutex.RLock() defer o.mutex.RUnlock() return o.totalHP } // SetTotalPower sets the total power func (o *Object) SetTotalPower(power int32) { o.mutex.Lock() defer o.mutex.Unlock() o.totalPower = power } // GetTotalPower returns the total power func (o *Object) GetTotalPower() int32 { o.mutex.RLock() defer o.mutex.RUnlock() return o.totalPower } // SetHP sets the current hit points func (o *Object) SetHP(hp int32) { o.mutex.Lock() defer o.mutex.Unlock() o.currentHP = hp } // GetHP returns the current hit points func (o *Object) GetHP() int32 { o.mutex.RLock() defer o.mutex.RUnlock() return o.currentHP } // SetPower sets the current power func (o *Object) SetPower(power int32) { o.mutex.Lock() defer o.mutex.Unlock() o.currentPower = power } // GetPower returns the current power func (o *Object) GetPower() int32 { o.mutex.RLock() defer o.mutex.RUnlock() return o.currentPower } // SetTransporterID sets the transporter ID func (o *Object) SetTransporterID(transporterID int32) { o.mutex.Lock() defer o.mutex.Unlock() o.transporterID = transporterID } // GetTransporterID returns the transporter ID func (o *Object) GetTransporterID() int32 { o.mutex.RLock() defer o.mutex.RUnlock() return o.transporterID } // SetSoundsDisabled sets whether sounds are disabled func (o *Object) SetSoundsDisabled(disabled bool) { o.mutex.Lock() defer o.mutex.Unlock() o.soundsDisabled = disabled } // IsSoundsDisabled returns whether sounds are disabled func (o *Object) IsSoundsDisabled() bool { o.mutex.RLock() defer o.mutex.RUnlock() return o.soundsDisabled } // SetOmittedByDBFlag sets the omitted by DB flag func (o *Object) SetOmittedByDBFlag(omitted bool) { o.mutex.Lock() defer o.mutex.Unlock() o.omittedByDBFlag = omitted } // IsOmittedByDBFlag returns the omitted by DB flag func (o *Object) IsOmittedByDBFlag() bool { o.mutex.RLock() defer o.mutex.RUnlock() return o.omittedByDBFlag } // SetLootTier sets the loot tier func (o *Object) SetLootTier(tier int8) { o.mutex.Lock() defer o.mutex.Unlock() o.lootTier = tier } // GetLootTier returns the loot tier func (o *Object) GetLootTier() int8 { o.mutex.RLock() defer o.mutex.RUnlock() return o.lootTier } // SetLootDropType sets the loot drop type func (o *Object) SetLootDropType(dropType int8) { o.mutex.Lock() defer o.mutex.Unlock() o.lootDropType = dropType } // GetLootDropType returns the loot drop type func (o *Object) GetLootDropType() int8 { o.mutex.RLock() defer o.mutex.RUnlock() return o.lootDropType } // SetSpawnScript sets the spawn script func (o *Object) SetSpawnScript(script string) { o.mutex.Lock() defer o.mutex.Unlock() o.spawnScript = script o.spawnScriptSetDB = len(script) > 0 } // GetSpawnScript returns the spawn script func (o *Object) GetSpawnScript() string { o.mutex.RLock() defer o.mutex.RUnlock() return o.spawnScript } // GetSpawnScriptSetDB returns whether spawn script is set from DB func (o *Object) GetSpawnScriptSetDB() bool { o.mutex.RLock() defer o.mutex.RUnlock() return o.spawnScriptSetDB } // SetShowCommandIcon sets whether to show the command icon func (o *Object) SetShowCommandIcon(show bool) { o.mutex.Lock() defer o.mutex.Unlock() if show { o.appearanceShowCommandIcon = ObjectShowCommandIcon } else { o.appearanceShowCommandIcon = 0 } } // ShowsCommandIcon returns whether the command icon is shown func (o *Object) ShowsCommandIcon() bool { o.mutex.RLock() defer o.mutex.RUnlock() return o.appearanceShowCommandIcon == ObjectShowCommandIcon } // GetObjectInfo returns comprehensive information about the object func (o *Object) GetObjectInfo() map[string]any { o.mutex.RLock() defer o.mutex.RUnlock() info := make(map[string]any) info["clickable"] = o.clickable info["zone_name"] = o.zoneName info["device_id"] = o.deviceID info["database_id"] = o.databaseID info["size"] = o.size info["merchant_id"] = o.merchantID info["transporter_id"] = o.transporterID info["is_collector"] = o.isCollector info["sounds_disabled"] = o.soundsDisabled info["primary_commands"] = len(o.primaryCommands) info["secondary_commands"] = len(o.secondaryCommands) info["shows_command_icon"] = o.ShowsCommandIcon() return info }