package object import ( "fmt" "sync" "eq2emu/internal/spawn" ) // Object constants const ( // Object spawn type ObjectSpawnType = 2 // Object appearance defaults ObjectActivityStatus = 64 ObjectPosState = 1 ObjectDifficulty = 0 // Object interaction constants ObjectShowCommandIcon = 1 // Object states ObjectStateInactive = 0 ObjectStateActive = 1 // Interaction types InteractionTypeNone = 0 InteractionTypeCommand = 1 InteractionTypeTransport = 2 InteractionTypeDevice = 3 ) // Object represents a game object that extends spawn functionality type Object struct { *spawn.Spawn // Embed spawn functionality // Object-specific properties clickable bool // Whether the object can be clicked/interacted with deviceID int8 // Device ID for interactive objects // Merchant properties merchantID int32 merchantType int8 merchantMinLevel int32 merchantMaxLevel int32 isCollector bool // Transport properties transporterID int32 // Zone tracking (since spawn doesn't expose zone methods yet) zoneName string // Thread safety mutex sync.RWMutex } // NewObject creates a new object with default values func NewObject() *Object { // 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 return &Object{ Spawn: baseSpawn, clickable: false, deviceID: 0, zoneName: "", } } // Object-specific methods // IsObject returns true (implements ObjectInterface) func (o *Object) IsObject() bool { return true } // IsClickable returns whether the object can be clicked func (o *Object) IsClickable() bool { o.mutex.RLock() defer o.mutex.RUnlock() return o.clickable } // SetClickable sets whether the object can be clicked func (o *Object) SetClickable(clickable bool) { o.mutex.Lock() defer o.mutex.Unlock() o.clickable = clickable // Update appearance to show/hide command icon if clickable { o.GetAppearanceData().ShowCommandIcon = ObjectShowCommandIcon } else { o.GetAppearanceData().ShowCommandIcon = 0 } } // GetDeviceID returns the device ID func (o *Object) GetDeviceID() int8 { o.mutex.RLock() defer o.mutex.RUnlock() return o.deviceID } // SetDeviceID sets the device ID func (o *Object) SetDeviceID(deviceID int8) { o.mutex.Lock() defer o.mutex.Unlock() o.deviceID = deviceID } // Merchant methods // GetMerchantID returns the merchant ID func (o *Object) GetMerchantID() int32 { o.mutex.RLock() defer o.mutex.RUnlock() return o.merchantID } // SetMerchantID sets the merchant ID func (o *Object) SetMerchantID(merchantID int32) { o.mutex.Lock() defer o.mutex.Unlock() o.merchantID = merchantID } // GetMerchantType returns the merchant type func (o *Object) GetMerchantType() int8 { o.mutex.RLock() defer o.mutex.RUnlock() return o.merchantType } // SetMerchantType sets the merchant type func (o *Object) SetMerchantType(merchantType int8) { o.mutex.Lock() defer o.mutex.Unlock() o.merchantType = merchantType } // IsMerchant returns whether this object is a merchant func (o *Object) IsMerchant() bool { o.mutex.RLock() defer o.mutex.RUnlock() return o.merchantID > 0 } // IsCollector returns whether this object is a collector func (o *Object) IsCollector() bool { o.mutex.RLock() defer o.mutex.RUnlock() return o.isCollector } // SetCollector sets whether this object is a collector func (o *Object) SetCollector(isCollector bool) { o.mutex.Lock() defer o.mutex.Unlock() o.isCollector = isCollector } // Transport methods // GetTransporterID returns the transporter ID func (o *Object) GetTransporterID() int32 { o.mutex.RLock() defer o.mutex.RUnlock() return o.transporterID } // SetTransporterID sets the transporter ID func (o *Object) SetTransporterID(transporterID int32) { o.mutex.Lock() defer o.mutex.Unlock() o.transporterID = transporterID } // IsTransporter returns whether this object is a transporter func (o *Object) IsTransporter() bool { o.mutex.RLock() defer o.mutex.RUnlock() return o.transporterID > 0 } // Interaction methods // GetInteractionType returns the type of interaction this object supports func (o *Object) GetInteractionType() int { o.mutex.RLock() defer o.mutex.RUnlock() if o.transporterID > 0 { return InteractionTypeTransport } if o.deviceID > 0 { return InteractionTypeDevice } if o.clickable { return InteractionTypeCommand } return InteractionTypeNone } // CanInteract returns whether this object can be interacted with func (o *Object) CanInteract() bool { return o.GetInteractionType() != InteractionTypeNone } // Zone methods // GetZone returns the zone name func (o *Object) GetZone() string { o.mutex.RLock() defer o.mutex.RUnlock() return o.zoneName } // SetZone sets the zone name func (o *Object) SetZone(zoneName string) { o.mutex.Lock() defer o.mutex.Unlock() o.zoneName = zoneName } // Utility methods // GetObjectType returns a string describing the object type func (o *Object) GetObjectType() string { switch { case o.IsTransporter(): return "Transport" case o.IsMerchant(): return "Merchant" case o.IsCollector(): return "Collector" case o.GetDeviceID() > 0: return "Device" case o.IsClickable(): return "Interactive" default: return "Static" } } // Validate checks if the object is properly configured func (o *Object) Validate() error { if o.Spawn == nil { return fmt.Errorf("object must have spawn data") } if o.GetSpawnType() != ObjectSpawnType { return fmt.Errorf("object must have spawn type %d", ObjectSpawnType) } return nil } // Clone creates a copy of the object func (o *Object) Clone() *Object { o.mutex.RLock() defer o.mutex.RUnlock() newObject := &Object{ Spawn: o.Spawn, // Note: This is a shallow copy of the spawn clickable: o.clickable, deviceID: o.deviceID, merchantID: o.merchantID, merchantType: o.merchantType, merchantMinLevel: o.merchantMinLevel, merchantMaxLevel: o.merchantMaxLevel, isCollector: o.isCollector, transporterID: o.transporterID, } return newObject } // Factory functions for different object types // CreateMerchantObject creates an object configured as a merchant func CreateMerchantObject(merchantID int32, merchantType int8) *Object { obj := NewObject() obj.SetMerchantID(merchantID) obj.SetMerchantType(merchantType) obj.SetClickable(true) return obj } // CreateTransportObject creates an object configured as a transporter func CreateTransportObject(transporterID int32) *Object { obj := NewObject() obj.SetTransporterID(transporterID) obj.SetClickable(true) return obj } // CreateDeviceObject creates an object configured as a device func CreateDeviceObject(deviceID int8) *Object { obj := NewObject() obj.SetDeviceID(deviceID) obj.SetClickable(true) return obj } // CreateCollectorObject creates an object configured as a collector func CreateCollectorObject() *Object { obj := NewObject() obj.SetCollector(true) obj.SetClickable(true) return obj } // CreateStaticObject creates a non-interactive object func CreateStaticObject() *Object { obj := NewObject() obj.SetClickable(false) return obj }