simplify object
This commit is contained in:
parent
d74f309ade
commit
bab7d9abf9
@ -22,6 +22,7 @@ This document outlines how we successfully simplified the EverQuest II housing p
|
|||||||
- NPC
|
- NPC
|
||||||
- NPC/AI
|
- NPC/AI
|
||||||
- NPC/Race Types
|
- NPC/Race Types
|
||||||
|
- Object
|
||||||
|
|
||||||
## Before: Complex Architecture (8 Files, ~2000+ Lines)
|
## Before: Complex Architecture (8 Files, ~2000+ Lines)
|
||||||
|
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
package object
|
|
||||||
|
|
||||||
// Object constants converted from C++ Object.cpp
|
|
||||||
const (
|
|
||||||
// Object spawn type (from C++ constructor)
|
|
||||||
ObjectSpawnType = 2
|
|
||||||
|
|
||||||
// Object appearance defaults (from C++ constructor)
|
|
||||||
ObjectActivityStatus = 64 // Default activity status
|
|
||||||
ObjectPosState = 1 // Default position state
|
|
||||||
ObjectDifficulty = 0 // Default difficulty
|
|
||||||
|
|
||||||
// Object interaction constants
|
|
||||||
ObjectShowCommandIcon = 1 // Show command icon when interactable
|
|
||||||
)
|
|
||||||
|
|
||||||
// Object device ID constants
|
|
||||||
const (
|
|
||||||
DeviceIDNone = 0 // No device ID assigned
|
|
||||||
DeviceIDDefault = 0 // Default device ID
|
|
||||||
)
|
|
||||||
|
|
||||||
// Object state constants
|
|
||||||
const (
|
|
||||||
ObjectStateInactive = 0 // Object is inactive
|
|
||||||
ObjectStateActive = 1 // Object is active and usable
|
|
||||||
)
|
|
||||||
|
|
||||||
// Object interaction types
|
|
||||||
const (
|
|
||||||
InteractionTypeNone = 0 // No interaction available
|
|
||||||
InteractionTypeCommand = 1 // Command-based interaction
|
|
||||||
InteractionTypeTransport = 2 // Transport/teleport interaction
|
|
||||||
InteractionTypeDevice = 3 // Device-based interaction
|
|
||||||
)
|
|
@ -1,387 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
@ -1,320 +1,53 @@
|
|||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
// ObjectInterface defines the core interface for interactive objects
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SpawnInterface defines the interface that spawn-based objects must implement
|
|
||||||
// This allows objects to integrate with systems expecting spawn entities
|
|
||||||
type SpawnInterface interface {
|
|
||||||
// Basic identification
|
|
||||||
GetID() int32
|
|
||||||
GetDatabaseID() int32
|
|
||||||
|
|
||||||
// Zone and positioning
|
|
||||||
GetZoneName() string
|
|
||||||
SetZoneName(string)
|
|
||||||
GetX() float32
|
|
||||||
GetY() float32
|
|
||||||
GetZ() float32
|
|
||||||
GetHeading() float32
|
|
||||||
|
|
||||||
// Spawn properties
|
|
||||||
GetSpawnType() int8
|
|
||||||
SetSpawnType(int8)
|
|
||||||
GetSize() int16
|
|
||||||
SetSize(int16)
|
|
||||||
|
|
||||||
// State flags
|
|
||||||
IsAlive() bool
|
|
||||||
SetAlive(bool)
|
|
||||||
IsRunning() bool
|
|
||||||
SetRunning(bool)
|
|
||||||
|
|
||||||
// Entity properties for spell/trade integration
|
|
||||||
GetFactionID() int32
|
|
||||||
SetFactionID(int32)
|
|
||||||
GetTarget() int32
|
|
||||||
SetTarget(int32)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ObjectInterface defines the interface for interactive objects
|
|
||||||
type ObjectInterface interface {
|
type ObjectInterface interface {
|
||||||
SpawnInterface
|
// Object identification
|
||||||
|
|
||||||
// Object-specific properties
|
|
||||||
IsObject() bool
|
IsObject() bool
|
||||||
|
GetObjectType() string
|
||||||
|
|
||||||
|
// Interaction capabilities
|
||||||
IsClickable() bool
|
IsClickable() bool
|
||||||
SetClickable(bool)
|
SetClickable(bool)
|
||||||
|
CanInteract() bool
|
||||||
|
GetInteractionType() int
|
||||||
|
|
||||||
|
// Device functionality
|
||||||
GetDeviceID() int8
|
GetDeviceID() int8
|
||||||
SetDeviceID(int8)
|
SetDeviceID(int8)
|
||||||
|
|
||||||
// Interaction
|
|
||||||
HandleUse(clientID int32, command string) error
|
|
||||||
ShowsCommandIcon() bool
|
|
||||||
SetShowCommandIcon(bool)
|
|
||||||
|
|
||||||
// Merchant functionality
|
// Merchant functionality
|
||||||
|
IsMerchant() bool
|
||||||
GetMerchantID() int32
|
GetMerchantID() int32
|
||||||
SetMerchantID(int32)
|
SetMerchantID(int32)
|
||||||
GetMerchantType() int8
|
GetMerchantType() int8
|
||||||
SetMerchantType(int8)
|
SetMerchantType(int8)
|
||||||
IsCollector() bool
|
|
||||||
SetCollector(bool)
|
|
||||||
|
|
||||||
// Transport functionality
|
// Transport functionality
|
||||||
|
IsTransporter() bool
|
||||||
GetTransporterID() int32
|
GetTransporterID() int32
|
||||||
SetTransporterID(int32)
|
SetTransporterID(int32)
|
||||||
|
|
||||||
// Copying
|
// Collector functionality
|
||||||
Copy() *ObjectSpawn
|
IsCollector() bool
|
||||||
|
SetCollector(bool)
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
Validate() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// EntityInterface defines the interface for entities in trade/spell systems
|
// ObjectManager interface for dependency injection
|
||||||
// ObjectSpawn implements this to integrate with existing systems
|
type ObjectManagerInterface interface {
|
||||||
type EntityInterface interface {
|
AddObject(*Object) error
|
||||||
GetID() int32
|
RemoveObject(int32) error
|
||||||
GetName() string
|
GetObject(int32) *Object
|
||||||
IsPlayer() bool
|
GetObjectsByZone(string) []*Object
|
||||||
IsBot() bool
|
GetObjectsByType(string) []*Object
|
||||||
HasCoins(amount int64) bool
|
GetInteractiveObjects() []*Object
|
||||||
GetClientVersion() int32
|
GetMerchantObjects() []*Object
|
||||||
}
|
GetTransportObjects() []*Object
|
||||||
|
GetCollectorObjects() []*Object
|
||||||
// ItemInterface defines the interface for items that objects might provide
|
GetObjectCount() int
|
||||||
// This allows objects to act as item sources (merchants, containers, etc.)
|
Clear()
|
||||||
type ItemInterface interface {
|
GetObjectStatistics() ObjectStatistics
|
||||||
GetID() int32
|
}
|
||||||
GetName() string
|
|
||||||
GetQuantity() int32
|
|
||||||
GetIcon(version int32) int32
|
|
||||||
IsNoTrade() bool
|
|
||||||
IsHeirloom() bool
|
|
||||||
IsAttuned() bool
|
|
||||||
GetCreationTime() time.Time
|
|
||||||
GetGroupCharacterIDs() []int32
|
|
||||||
}
|
|
||||||
|
|
||||||
// ObjectSpawnAsEntity is an adapter that makes ObjectSpawn implement EntityInterface
|
|
||||||
// This allows objects to be used in trade and spell systems
|
|
||||||
type ObjectSpawnAsEntity struct {
|
|
||||||
*ObjectSpawn
|
|
||||||
name string
|
|
||||||
isPlayer bool
|
|
||||||
isBot bool
|
|
||||||
coinsAmount int64
|
|
||||||
clientVersion int32
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewObjectSpawnAsEntity creates an entity adapter for an object spawn
|
|
||||||
func NewObjectSpawnAsEntity(objectSpawn *ObjectSpawn) *ObjectSpawnAsEntity {
|
|
||||||
return &ObjectSpawnAsEntity{
|
|
||||||
ObjectSpawn: objectSpawn,
|
|
||||||
name: "Object", // Default name, should be loaded from data
|
|
||||||
isPlayer: false,
|
|
||||||
isBot: false,
|
|
||||||
coinsAmount: 0,
|
|
||||||
clientVersion: 1000,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EntityInterface implementation for ObjectSpawnAsEntity
|
|
||||||
|
|
||||||
// GetName returns the object's name
|
|
||||||
func (osae *ObjectSpawnAsEntity) GetName() string {
|
|
||||||
return osae.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetName sets the object's name
|
|
||||||
func (osae *ObjectSpawnAsEntity) SetName(name string) {
|
|
||||||
osae.name = name
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsPlayer returns false for objects
|
|
||||||
func (osae *ObjectSpawnAsEntity) IsPlayer() bool {
|
|
||||||
return osae.isPlayer
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsBot returns whether this object behaves like a bot
|
|
||||||
func (osae *ObjectSpawnAsEntity) IsBot() bool {
|
|
||||||
return osae.isBot
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBot sets whether this object behaves like a bot
|
|
||||||
func (osae *ObjectSpawnAsEntity) SetBot(isBot bool) {
|
|
||||||
osae.isBot = isBot
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasCoins returns whether the object has sufficient coins (for merchant objects)
|
|
||||||
func (osae *ObjectSpawnAsEntity) HasCoins(amount int64) bool {
|
|
||||||
return osae.coinsAmount >= amount
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCoins sets the object's coin amount (for merchant objects)
|
|
||||||
func (osae *ObjectSpawnAsEntity) SetCoins(amount int64) {
|
|
||||||
osae.coinsAmount = amount
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetClientVersion returns the client version (default for objects)
|
|
||||||
func (osae *ObjectSpawnAsEntity) GetClientVersion() int32 {
|
|
||||||
return osae.clientVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetClientVersion sets the client version
|
|
||||||
func (osae *ObjectSpawnAsEntity) SetClientVersion(version int32) {
|
|
||||||
osae.clientVersion = version
|
|
||||||
}
|
|
||||||
|
|
||||||
// ObjectItem represents an item provided by an object (merchants, containers, etc.)
|
|
||||||
type ObjectItem struct {
|
|
||||||
id int32
|
|
||||||
name string
|
|
||||||
quantity int32
|
|
||||||
iconID int32
|
|
||||||
noTrade bool
|
|
||||||
heirloom bool
|
|
||||||
attuned bool
|
|
||||||
creationTime time.Time
|
|
||||||
groupCharacterIDs []int32
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewObjectItem creates a new object item
|
|
||||||
func NewObjectItem(id int32, name string, quantity int32) *ObjectItem {
|
|
||||||
return &ObjectItem{
|
|
||||||
id: id,
|
|
||||||
name: name,
|
|
||||||
quantity: quantity,
|
|
||||||
iconID: 0,
|
|
||||||
noTrade: false,
|
|
||||||
heirloom: false,
|
|
||||||
attuned: false,
|
|
||||||
creationTime: time.Now(),
|
|
||||||
groupCharacterIDs: make([]int32, 0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ItemInterface implementation for ObjectItem
|
|
||||||
|
|
||||||
// GetID returns the item ID
|
|
||||||
func (oi *ObjectItem) GetID() int32 {
|
|
||||||
return oi.id
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetName returns the item name
|
|
||||||
func (oi *ObjectItem) GetName() string {
|
|
||||||
return oi.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetQuantity returns the item quantity
|
|
||||||
func (oi *ObjectItem) GetQuantity() int32 {
|
|
||||||
return oi.quantity
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetQuantity sets the item quantity
|
|
||||||
func (oi *ObjectItem) SetQuantity(quantity int32) {
|
|
||||||
oi.quantity = quantity
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetIcon returns the item icon ID
|
|
||||||
func (oi *ObjectItem) GetIcon(version int32) int32 {
|
|
||||||
return oi.iconID
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetIcon sets the item icon ID
|
|
||||||
func (oi *ObjectItem) SetIcon(iconID int32) {
|
|
||||||
oi.iconID = iconID
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNoTrade returns whether the item is no-trade
|
|
||||||
func (oi *ObjectItem) IsNoTrade() bool {
|
|
||||||
return oi.noTrade
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetNoTrade sets whether the item is no-trade
|
|
||||||
func (oi *ObjectItem) SetNoTrade(noTrade bool) {
|
|
||||||
oi.noTrade = noTrade
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsHeirloom returns whether the item is heirloom
|
|
||||||
func (oi *ObjectItem) IsHeirloom() bool {
|
|
||||||
return oi.heirloom
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetHeirloom sets whether the item is heirloom
|
|
||||||
func (oi *ObjectItem) SetHeirloom(heirloom bool) {
|
|
||||||
oi.heirloom = heirloom
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsAttuned returns whether the item is attuned
|
|
||||||
func (oi *ObjectItem) IsAttuned() bool {
|
|
||||||
return oi.attuned
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetAttuned sets whether the item is attuned
|
|
||||||
func (oi *ObjectItem) SetAttuned(attuned bool) {
|
|
||||||
oi.attuned = attuned
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCreationTime returns when the item was created
|
|
||||||
func (oi *ObjectItem) GetCreationTime() time.Time {
|
|
||||||
return oi.creationTime
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCreationTime sets when the item was created
|
|
||||||
func (oi *ObjectItem) SetCreationTime(creationTime time.Time) {
|
|
||||||
oi.creationTime = creationTime
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetGroupCharacterIDs returns the group character IDs for heirloom sharing
|
|
||||||
func (oi *ObjectItem) GetGroupCharacterIDs() []int32 {
|
|
||||||
return oi.groupCharacterIDs
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetGroupCharacterIDs sets the group character IDs for heirloom sharing
|
|
||||||
func (oi *ObjectItem) SetGroupCharacterIDs(characterIDs []int32) {
|
|
||||||
oi.groupCharacterIDs = make([]int32, len(characterIDs))
|
|
||||||
copy(oi.groupCharacterIDs, characterIDs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Utility functions for integration
|
|
||||||
|
|
||||||
// CreateMerchantObjectSpawn creates an object spawn configured as a merchant
|
|
||||||
func CreateMerchantObjectSpawn(merchantID int32, merchantType int8) *ObjectSpawn {
|
|
||||||
objectSpawn := NewObjectSpawn()
|
|
||||||
objectSpawn.SetMerchantID(merchantID)
|
|
||||||
objectSpawn.SetMerchantType(merchantType)
|
|
||||||
objectSpawn.SetClickable(true)
|
|
||||||
objectSpawn.SetShowCommandIcon(true)
|
|
||||||
|
|
||||||
return objectSpawn
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateTransportObjectSpawn creates an object spawn configured as a transporter
|
|
||||||
func CreateTransportObjectSpawn(transporterID int32) *ObjectSpawn {
|
|
||||||
objectSpawn := NewObjectSpawn()
|
|
||||||
objectSpawn.SetTransporterID(transporterID)
|
|
||||||
objectSpawn.SetClickable(true)
|
|
||||||
objectSpawn.SetShowCommandIcon(true)
|
|
||||||
|
|
||||||
return objectSpawn
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateDeviceObjectSpawn creates an object spawn configured as an interactive device
|
|
||||||
func CreateDeviceObjectSpawn(deviceID int8) *ObjectSpawn {
|
|
||||||
objectSpawn := NewObjectSpawn()
|
|
||||||
objectSpawn.SetDeviceID(deviceID)
|
|
||||||
objectSpawn.SetClickable(true)
|
|
||||||
objectSpawn.SetShowCommandIcon(true)
|
|
||||||
|
|
||||||
return objectSpawn
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateCollectorObjectSpawn creates an object spawn configured as a collector
|
|
||||||
func CreateCollectorObjectSpawn() *ObjectSpawn {
|
|
||||||
objectSpawn := NewObjectSpawn()
|
|
||||||
objectSpawn.SetCollector(true)
|
|
||||||
objectSpawn.SetClickable(true)
|
|
||||||
objectSpawn.SetShowCommandIcon(true)
|
|
||||||
|
|
||||||
return objectSpawn
|
|
||||||
}
|
|
@ -7,30 +7,16 @@ import (
|
|||||||
|
|
||||||
// ObjectManager manages all objects in the game world
|
// ObjectManager manages all objects in the game world
|
||||||
type ObjectManager struct {
|
type ObjectManager struct {
|
||||||
objects map[int32]*Object // Objects by database ID
|
objects map[int32]*Object // Objects by database ID
|
||||||
|
|
||||||
// Zone-based indexing
|
|
||||||
objectsByZone map[string][]*Object // Objects grouped by zone
|
objectsByZone map[string][]*Object // Objects grouped by zone
|
||||||
|
mutex sync.RWMutex // Thread safety
|
||||||
// Type-based indexing
|
|
||||||
interactiveObjects []*Object // Objects that can be interacted with
|
|
||||||
transportObjects []*Object // Objects that provide transport
|
|
||||||
merchantObjects []*Object // Objects that are merchants
|
|
||||||
collectorObjects []*Object // Objects that are collectors
|
|
||||||
|
|
||||||
// Thread safety
|
|
||||||
mutex sync.RWMutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewObjectManager creates a new object manager
|
// NewObjectManager creates a new object manager
|
||||||
func NewObjectManager() *ObjectManager {
|
func NewObjectManager() *ObjectManager {
|
||||||
return &ObjectManager{
|
return &ObjectManager{
|
||||||
objects: make(map[int32]*Object),
|
objects: make(map[int32]*Object),
|
||||||
objectsByZone: make(map[string][]*Object),
|
objectsByZone: make(map[string][]*Object),
|
||||||
interactiveObjects: make([]*Object, 0),
|
|
||||||
transportObjects: make([]*Object, 0),
|
|
||||||
merchantObjects: make([]*Object, 0),
|
|
||||||
collectorObjects: make([]*Object, 0),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,6 +26,10 @@ func (om *ObjectManager) AddObject(object *Object) error {
|
|||||||
return fmt.Errorf("cannot add nil object")
|
return fmt.Errorf("cannot add nil object")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := object.Validate(); err != nil {
|
||||||
|
return fmt.Errorf("invalid object: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
om.mutex.Lock()
|
om.mutex.Lock()
|
||||||
defer om.mutex.Unlock()
|
defer om.mutex.Unlock()
|
||||||
|
|
||||||
@ -53,18 +43,15 @@ func (om *ObjectManager) AddObject(object *Object) error {
|
|||||||
return fmt.Errorf("object with database ID %d already exists", databaseID)
|
return fmt.Errorf("object with database ID %d already exists", databaseID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to main collection
|
// Add to main index
|
||||||
om.objects[databaseID] = object
|
om.objects[databaseID] = object
|
||||||
|
|
||||||
// Add to zone collection
|
// Add to zone index
|
||||||
zoneName := object.GetZoneName()
|
zoneName := object.GetZone()
|
||||||
if zoneName != "" {
|
if zoneName != "" {
|
||||||
om.objectsByZone[zoneName] = append(om.objectsByZone[zoneName], object)
|
om.objectsByZone[zoneName] = append(om.objectsByZone[zoneName], object)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to type-based collections
|
|
||||||
om.updateObjectIndices(object, true)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,29 +65,26 @@ func (om *ObjectManager) RemoveObject(databaseID int32) error {
|
|||||||
return fmt.Errorf("object with database ID %d not found", databaseID)
|
return fmt.Errorf("object with database ID %d not found", databaseID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove from main collection
|
// Remove from main index
|
||||||
delete(om.objects, databaseID)
|
delete(om.objects, databaseID)
|
||||||
|
|
||||||
// Remove from zone collection
|
// Remove from zone index
|
||||||
zoneName := object.GetZoneName()
|
zoneName := object.GetZone()
|
||||||
if zoneName != "" {
|
if zoneName != "" {
|
||||||
if zoneObjects, exists := om.objectsByZone[zoneName]; exists {
|
objects := om.objectsByZone[zoneName]
|
||||||
for i, obj := range zoneObjects {
|
for i, obj := range objects {
|
||||||
if obj.GetDatabaseID() == databaseID {
|
if obj.GetDatabaseID() == databaseID {
|
||||||
om.objectsByZone[zoneName] = append(zoneObjects[:i], zoneObjects[i+1:]...)
|
// Remove from slice
|
||||||
break
|
om.objectsByZone[zoneName] = append(objects[:i], objects[i+1:]...)
|
||||||
}
|
break
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up empty zone collection
|
|
||||||
if len(om.objectsByZone[zoneName]) == 0 {
|
|
||||||
delete(om.objectsByZone, zoneName)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Remove from type-based collections
|
// Clean up empty zone entries
|
||||||
om.updateObjectIndices(object, false)
|
if len(om.objectsByZone[zoneName]) == 0 {
|
||||||
|
delete(om.objectsByZone, zoneName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -109,7 +93,6 @@ func (om *ObjectManager) RemoveObject(databaseID int32) error {
|
|||||||
func (om *ObjectManager) GetObject(databaseID int32) *Object {
|
func (om *ObjectManager) GetObject(databaseID int32) *Object {
|
||||||
om.mutex.RLock()
|
om.mutex.RLock()
|
||||||
defer om.mutex.RUnlock()
|
defer om.mutex.RUnlock()
|
||||||
|
|
||||||
return om.objects[databaseID]
|
return om.objects[databaseID]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,14 +101,29 @@ func (om *ObjectManager) GetObjectsByZone(zoneName string) []*Object {
|
|||||||
om.mutex.RLock()
|
om.mutex.RLock()
|
||||||
defer om.mutex.RUnlock()
|
defer om.mutex.RUnlock()
|
||||||
|
|
||||||
if objects, exists := om.objectsByZone[zoneName]; exists {
|
objects := om.objectsByZone[zoneName]
|
||||||
// Return a copy to prevent external modification
|
if objects == nil {
|
||||||
result := make([]*Object, len(objects))
|
return []*Object{}
|
||||||
copy(result, objects)
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return make([]*Object, 0)
|
// Return a copy to prevent external modification
|
||||||
|
result := make([]*Object, len(objects))
|
||||||
|
copy(result, objects)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetObjectsByType retrieves objects by their type
|
||||||
|
func (om *ObjectManager) GetObjectsByType(objectType string) []*Object {
|
||||||
|
om.mutex.RLock()
|
||||||
|
defer om.mutex.RUnlock()
|
||||||
|
|
||||||
|
var result []*Object
|
||||||
|
for _, object := range om.objects {
|
||||||
|
if object.GetObjectType() == objectType {
|
||||||
|
result = append(result, object)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInteractiveObjects returns all objects that can be interacted with
|
// GetInteractiveObjects returns all objects that can be interacted with
|
||||||
@ -133,18 +131,12 @@ func (om *ObjectManager) GetInteractiveObjects() []*Object {
|
|||||||
om.mutex.RLock()
|
om.mutex.RLock()
|
||||||
defer om.mutex.RUnlock()
|
defer om.mutex.RUnlock()
|
||||||
|
|
||||||
result := make([]*Object, len(om.interactiveObjects))
|
var result []*Object
|
||||||
copy(result, om.interactiveObjects)
|
for _, object := range om.objects {
|
||||||
return result
|
if object.CanInteract() {
|
||||||
}
|
result = append(result, object)
|
||||||
|
}
|
||||||
// GetTransportObjects returns all objects that provide transport
|
}
|
||||||
func (om *ObjectManager) GetTransportObjects() []*Object {
|
|
||||||
om.mutex.RLock()
|
|
||||||
defer om.mutex.RUnlock()
|
|
||||||
|
|
||||||
result := make([]*Object, len(om.transportObjects))
|
|
||||||
copy(result, om.transportObjects)
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,8 +145,26 @@ func (om *ObjectManager) GetMerchantObjects() []*Object {
|
|||||||
om.mutex.RLock()
|
om.mutex.RLock()
|
||||||
defer om.mutex.RUnlock()
|
defer om.mutex.RUnlock()
|
||||||
|
|
||||||
result := make([]*Object, len(om.merchantObjects))
|
var result []*Object
|
||||||
copy(result, om.merchantObjects)
|
for _, object := range om.objects {
|
||||||
|
if object.IsMerchant() {
|
||||||
|
result = append(result, object)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTransportObjects returns all transport objects
|
||||||
|
func (om *ObjectManager) GetTransportObjects() []*Object {
|
||||||
|
om.mutex.RLock()
|
||||||
|
defer om.mutex.RUnlock()
|
||||||
|
|
||||||
|
var result []*Object
|
||||||
|
for _, object := range om.objects {
|
||||||
|
if object.IsTransporter() {
|
||||||
|
result = append(result, object)
|
||||||
|
}
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,86 +173,17 @@ func (om *ObjectManager) GetCollectorObjects() []*Object {
|
|||||||
om.mutex.RLock()
|
om.mutex.RLock()
|
||||||
defer om.mutex.RUnlock()
|
defer om.mutex.RUnlock()
|
||||||
|
|
||||||
result := make([]*Object, len(om.collectorObjects))
|
var result []*Object
|
||||||
copy(result, om.collectorObjects)
|
for _, object := range om.objects {
|
||||||
return result
|
if object.IsCollector() {
|
||||||
}
|
result = append(result, object)
|
||||||
|
|
||||||
// GetObjectCount returns the total number of objects
|
|
||||||
func (om *ObjectManager) GetObjectCount() int {
|
|
||||||
om.mutex.RLock()
|
|
||||||
defer om.mutex.RUnlock()
|
|
||||||
|
|
||||||
return len(om.objects)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetZoneCount returns the number of zones with objects
|
|
||||||
func (om *ObjectManager) GetZoneCount() int {
|
|
||||||
om.mutex.RLock()
|
|
||||||
defer om.mutex.RUnlock()
|
|
||||||
|
|
||||||
return len(om.objectsByZone)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetObjectsByType returns objects filtered by specific criteria
|
|
||||||
func (om *ObjectManager) GetObjectsByType(objectType string) []*Object {
|
|
||||||
om.mutex.RLock()
|
|
||||||
defer om.mutex.RUnlock()
|
|
||||||
|
|
||||||
switch objectType {
|
|
||||||
case "interactive":
|
|
||||||
result := make([]*Object, len(om.interactiveObjects))
|
|
||||||
copy(result, om.interactiveObjects)
|
|
||||||
return result
|
|
||||||
case "transport":
|
|
||||||
result := make([]*Object, len(om.transportObjects))
|
|
||||||
copy(result, om.transportObjects)
|
|
||||||
return result
|
|
||||||
case "merchant":
|
|
||||||
result := make([]*Object, len(om.merchantObjects))
|
|
||||||
copy(result, om.merchantObjects)
|
|
||||||
return result
|
|
||||||
case "collector":
|
|
||||||
result := make([]*Object, len(om.collectorObjects))
|
|
||||||
copy(result, om.collectorObjects)
|
|
||||||
return result
|
|
||||||
default:
|
|
||||||
return make([]*Object, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindObjectsInZone finds objects in a zone matching specific criteria
|
|
||||||
func (om *ObjectManager) FindObjectsInZone(zoneName string, filter func(*Object) bool) []*Object {
|
|
||||||
om.mutex.RLock()
|
|
||||||
defer om.mutex.RUnlock()
|
|
||||||
|
|
||||||
zoneObjects, exists := om.objectsByZone[zoneName]
|
|
||||||
if !exists {
|
|
||||||
return make([]*Object, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
result := make([]*Object, 0)
|
|
||||||
for _, obj := range zoneObjects {
|
|
||||||
if filter == nil || filter(obj) {
|
|
||||||
result = append(result, obj)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindObjectByName finds the first object with a matching name (placeholder)
|
// UpdateObjectZone updates an object's zone (called when object moves zones)
|
||||||
func (om *ObjectManager) FindObjectByName(name string) *Object {
|
func (om *ObjectManager) UpdateObjectZone(databaseID int32, newZoneName string) error {
|
||||||
om.mutex.RLock()
|
|
||||||
defer om.mutex.RUnlock()
|
|
||||||
|
|
||||||
// TODO: Implement name searching when spawn name system is integrated
|
|
||||||
// For now, return nil
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateObject updates an existing object's properties
|
|
||||||
func (om *ObjectManager) UpdateObject(databaseID int32, updateFn func(*Object)) error {
|
|
||||||
om.mutex.Lock()
|
om.mutex.Lock()
|
||||||
defer om.mutex.Unlock()
|
defer om.mutex.Unlock()
|
||||||
|
|
||||||
@ -251,169 +192,113 @@ func (om *ObjectManager) UpdateObject(databaseID int32, updateFn func(*Object))
|
|||||||
return fmt.Errorf("object with database ID %d not found", databaseID)
|
return fmt.Errorf("object with database ID %d not found", databaseID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store old zone for potential reindexing
|
oldZoneName := object.GetZone()
|
||||||
oldZone := object.GetZoneName()
|
|
||||||
|
|
||||||
// Apply updates
|
// Remove from old zone index
|
||||||
updateFn(object)
|
if oldZoneName != "" {
|
||||||
|
objects := om.objectsByZone[oldZoneName]
|
||||||
// Check if zone changed and reindex if necessary
|
for i, obj := range objects {
|
||||||
newZone := object.GetZoneName()
|
if obj.GetDatabaseID() == databaseID {
|
||||||
if oldZone != newZone {
|
om.objectsByZone[oldZoneName] = append(objects[:i], objects[i+1:]...)
|
||||||
// Remove from old zone
|
break
|
||||||
if oldZone != "" {
|
|
||||||
if zoneObjects, exists := om.objectsByZone[oldZone]; exists {
|
|
||||||
for i, obj := range zoneObjects {
|
|
||||||
if obj.GetDatabaseID() == databaseID {
|
|
||||||
om.objectsByZone[oldZone] = append(zoneObjects[:i], zoneObjects[i+1:]...)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up empty zone collection
|
|
||||||
if len(om.objectsByZone[oldZone]) == 0 {
|
|
||||||
delete(om.objectsByZone, oldZone)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to new zone
|
// Clean up empty zone entries
|
||||||
if newZone != "" {
|
if len(om.objectsByZone[oldZoneName]) == 0 {
|
||||||
om.objectsByZone[newZone] = append(om.objectsByZone[newZone], object)
|
delete(om.objectsByZone, oldZoneName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update type-based indices
|
// Update object zone
|
||||||
om.rebuildIndicesForObject(object)
|
object.SetZone(newZoneName)
|
||||||
|
|
||||||
|
// Add to new zone index
|
||||||
|
if newZoneName != "" {
|
||||||
|
om.objectsByZone[newZoneName] = append(om.objectsByZone[newZoneName], object)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearZone removes all objects from a specific zone
|
// GetObjectCount returns the total number of objects
|
||||||
func (om *ObjectManager) ClearZone(zoneName string) int {
|
func (om *ObjectManager) GetObjectCount() int {
|
||||||
om.mutex.Lock()
|
om.mutex.RLock()
|
||||||
defer om.mutex.Unlock()
|
defer om.mutex.RUnlock()
|
||||||
|
return len(om.objects)
|
||||||
zoneObjects, exists := om.objectsByZone[zoneName]
|
|
||||||
if !exists {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
count := len(zoneObjects)
|
|
||||||
|
|
||||||
// Remove objects from main collection and indices
|
|
||||||
for _, obj := range zoneObjects {
|
|
||||||
databaseID := obj.GetDatabaseID()
|
|
||||||
delete(om.objects, databaseID)
|
|
||||||
om.updateObjectIndices(obj, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear zone collection
|
|
||||||
delete(om.objectsByZone, zoneName)
|
|
||||||
|
|
||||||
return count
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStatistics returns statistics about objects in the manager
|
// GetZoneCount returns the number of zones with objects
|
||||||
func (om *ObjectManager) GetStatistics() map[string]any {
|
func (om *ObjectManager) GetZoneCount() int {
|
||||||
|
om.mutex.RLock()
|
||||||
|
defer om.mutex.RUnlock()
|
||||||
|
return len(om.objectsByZone)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllObjects returns all objects (use with caution for large datasets)
|
||||||
|
func (om *ObjectManager) GetAllObjects() []*Object {
|
||||||
om.mutex.RLock()
|
om.mutex.RLock()
|
||||||
defer om.mutex.RUnlock()
|
defer om.mutex.RUnlock()
|
||||||
|
|
||||||
stats := make(map[string]any)
|
result := make([]*Object, 0, len(om.objects))
|
||||||
stats["total_objects"] = len(om.objects)
|
for _, object := range om.objects {
|
||||||
stats["zones_with_objects"] = len(om.objectsByZone)
|
result = append(result, object)
|
||||||
stats["interactive_objects"] = len(om.interactiveObjects)
|
|
||||||
stats["transport_objects"] = len(om.transportObjects)
|
|
||||||
stats["merchant_objects"] = len(om.merchantObjects)
|
|
||||||
stats["collector_objects"] = len(om.collectorObjects)
|
|
||||||
|
|
||||||
// Zone breakdown
|
|
||||||
zoneStats := make(map[string]int)
|
|
||||||
for zoneName, objects := range om.objectsByZone {
|
|
||||||
zoneStats[zoneName] = len(objects)
|
|
||||||
}
|
}
|
||||||
stats["objects_by_zone"] = zoneStats
|
return result
|
||||||
|
|
||||||
return stats
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shutdown clears all objects and prepares for shutdown
|
// Clear removes all objects from the manager
|
||||||
func (om *ObjectManager) Shutdown() {
|
func (om *ObjectManager) Clear() {
|
||||||
om.mutex.Lock()
|
om.mutex.Lock()
|
||||||
defer om.mutex.Unlock()
|
defer om.mutex.Unlock()
|
||||||
|
|
||||||
om.objects = make(map[int32]*Object)
|
om.objects = make(map[int32]*Object)
|
||||||
om.objectsByZone = make(map[string][]*Object)
|
om.objectsByZone = make(map[string][]*Object)
|
||||||
om.interactiveObjects = make([]*Object, 0)
|
|
||||||
om.transportObjects = make([]*Object, 0)
|
|
||||||
om.merchantObjects = make([]*Object, 0)
|
|
||||||
om.collectorObjects = make([]*Object, 0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private helper methods
|
// GetObjectStatistics returns statistics about objects in the manager
|
||||||
|
func (om *ObjectManager) GetObjectStatistics() ObjectStatistics {
|
||||||
|
om.mutex.RLock()
|
||||||
|
defer om.mutex.RUnlock()
|
||||||
|
|
||||||
// updateObjectIndices updates type-based indices for an object
|
stats := ObjectStatistics{
|
||||||
func (om *ObjectManager) updateObjectIndices(object *Object, add bool) {
|
TotalObjects: len(om.objects),
|
||||||
if add {
|
ZoneCount: len(om.objectsByZone),
|
||||||
// Add to type-based collections
|
TypeCounts: make(map[string]int),
|
||||||
if object.IsClickable() || len(object.GetPrimaryCommands()) > 0 || len(object.GetSecondaryCommands()) > 0 {
|
|
||||||
om.interactiveObjects = append(om.interactiveObjects, object)
|
|
||||||
}
|
|
||||||
|
|
||||||
if object.GetTransporterID() > 0 {
|
|
||||||
om.transportObjects = append(om.transportObjects, object)
|
|
||||||
}
|
|
||||||
|
|
||||||
if object.GetMerchantID() > 0 {
|
|
||||||
om.merchantObjects = append(om.merchantObjects, object)
|
|
||||||
}
|
|
||||||
|
|
||||||
if object.IsCollector() {
|
|
||||||
om.collectorObjects = append(om.collectorObjects, object)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Remove from type-based collections
|
|
||||||
databaseID := object.GetDatabaseID()
|
|
||||||
|
|
||||||
om.interactiveObjects = removeObjectFromSlice(om.interactiveObjects, databaseID)
|
|
||||||
om.transportObjects = removeObjectFromSlice(om.transportObjects, databaseID)
|
|
||||||
om.merchantObjects = removeObjectFromSlice(om.merchantObjects, databaseID)
|
|
||||||
om.collectorObjects = removeObjectFromSlice(om.collectorObjects, databaseID)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// rebuildIndicesForObject rebuilds type-based indices for an object (used after updates)
|
// Count by type
|
||||||
func (om *ObjectManager) rebuildIndicesForObject(object *Object) {
|
for _, object := range om.objects {
|
||||||
databaseID := object.GetDatabaseID()
|
objectType := object.GetObjectType()
|
||||||
|
stats.TypeCounts[objectType]++
|
||||||
|
}
|
||||||
|
|
||||||
// Remove from all type-based collections
|
// Count interactive objects
|
||||||
om.interactiveObjects = removeObjectFromSlice(om.interactiveObjects, databaseID)
|
for _, object := range om.objects {
|
||||||
om.transportObjects = removeObjectFromSlice(om.transportObjects, databaseID)
|
if object.CanInteract() {
|
||||||
om.merchantObjects = removeObjectFromSlice(om.merchantObjects, databaseID)
|
stats.InteractiveObjects++
|
||||||
om.collectorObjects = removeObjectFromSlice(om.collectorObjects, databaseID)
|
|
||||||
|
|
||||||
// Re-add based on current properties
|
|
||||||
om.updateObjectIndices(object, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// removeObjectFromSlice removes an object from a slice by database ID
|
|
||||||
func removeObjectFromSlice(slice []*Object, databaseID int32) []*Object {
|
|
||||||
for i, obj := range slice {
|
|
||||||
if obj.GetDatabaseID() == databaseID {
|
|
||||||
return append(slice[:i], slice[i+1:]...)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return slice
|
|
||||||
|
return stats
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObjectStatistics contains statistics about objects
|
||||||
|
type ObjectStatistics struct {
|
||||||
|
TotalObjects int `json:"total_objects"`
|
||||||
|
InteractiveObjects int `json:"interactive_objects"`
|
||||||
|
ZoneCount int `json:"zone_count"`
|
||||||
|
TypeCounts map[string]int `json:"type_counts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global object manager instance
|
// Global object manager instance
|
||||||
var globalObjectManager *ObjectManager
|
var globalObjectManager *ObjectManager
|
||||||
var initObjectManagerOnce sync.Once
|
var globalObjectManagerOnce sync.Once
|
||||||
|
|
||||||
// GetGlobalObjectManager returns the global object manager (singleton)
|
// GetGlobalObjectManager returns the global object manager instance
|
||||||
func GetGlobalObjectManager() *ObjectManager {
|
func GetGlobalObjectManager() *ObjectManager {
|
||||||
initObjectManagerOnce.Do(func() {
|
globalObjectManagerOnce.Do(func() {
|
||||||
globalObjectManager = NewObjectManager()
|
globalObjectManager = NewObjectManager()
|
||||||
})
|
})
|
||||||
return globalObjectManager
|
return globalObjectManager
|
||||||
}
|
}
|
@ -2,81 +2,87 @@ package object
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
"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
|
// Object represents a game object that extends spawn functionality
|
||||||
// Converted from C++ Object class
|
|
||||||
type Object struct {
|
type Object struct {
|
||||||
// Embed spawn functionality - TODO: Use actual spawn.Spawn when integrated
|
*spawn.Spawn // Embed spawn functionality
|
||||||
// spawn.Spawn
|
|
||||||
|
|
||||||
// Object-specific properties
|
// Object-specific properties
|
||||||
clickable bool // Whether the object can be clicked/interacted with
|
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
|
||||||
deviceID int8 // Device ID for interactive objects
|
|
||||||
|
|
||||||
// Inherited spawn properties (placeholder until spawn integration)
|
// Merchant properties
|
||||||
databaseID int32
|
merchantID int32
|
||||||
size int16
|
merchantType int8
|
||||||
sizeOffset int8
|
merchantMinLevel int32
|
||||||
merchantID int32
|
merchantMaxLevel int32
|
||||||
merchantType int8
|
isCollector bool
|
||||||
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
|
// Transport properties
|
||||||
appearanceActivityStatus int8
|
transporterID int32
|
||||||
appearancePosState int8
|
|
||||||
appearanceDifficulty int8
|
|
||||||
appearanceShowCommandIcon int8
|
|
||||||
|
|
||||||
// Command lists - TODO: Use actual command structures
|
// Zone tracking (since spawn doesn't expose zone methods yet)
|
||||||
primaryCommands []string
|
zoneName string
|
||||||
secondaryCommands []string
|
|
||||||
|
|
||||||
// Thread safety
|
// Thread safety
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewObject creates a new object with default values
|
// NewObject creates a new object with default values
|
||||||
// Converted from C++ Object::Object constructor
|
|
||||||
func NewObject() *Object {
|
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{
|
return &Object{
|
||||||
clickable: false,
|
Spawn: baseSpawn,
|
||||||
zoneName: "",
|
clickable: false,
|
||||||
deviceID: DeviceIDNone,
|
deviceID: 0,
|
||||||
appearanceActivityStatus: ObjectActivityStatus,
|
zoneName: "",
|
||||||
appearancePosState: ObjectPosState,
|
|
||||||
appearanceDifficulty: ObjectDifficulty,
|
|
||||||
appearanceShowCommandIcon: 0,
|
|
||||||
primaryCommands: make([]string, 0),
|
|
||||||
secondaryCommands: make([]string, 0),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetClickable sets whether the object can be clicked
|
// Object-specific methods
|
||||||
func (o *Object) SetClickable(clickable bool) {
|
|
||||||
o.mutex.Lock()
|
// IsObject returns true (implements ObjectInterface)
|
||||||
defer o.mutex.Unlock()
|
func (o *Object) IsObject() bool {
|
||||||
o.clickable = clickable
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsClickable returns whether the object can be clicked
|
// IsClickable returns whether the object can be clicked
|
||||||
@ -86,192 +92,41 @@ func (o *Object) IsClickable() bool {
|
|||||||
return o.clickable
|
return o.clickable
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetZone sets the zone name for this object
|
// SetClickable sets whether the object can be clicked
|
||||||
// Converted from C++ Object::SetZone
|
func (o *Object) SetClickable(clickable bool) {
|
||||||
func (o *Object) SetZone(zoneName string) {
|
|
||||||
o.mutex.Lock()
|
o.mutex.Lock()
|
||||||
defer o.mutex.Unlock()
|
defer o.mutex.Unlock()
|
||||||
o.zoneName = zoneName
|
o.clickable = clickable
|
||||||
}
|
|
||||||
|
|
||||||
// GetZoneName returns the zone name for this object
|
// Update appearance to show/hide command icon
|
||||||
func (o *Object) GetZoneName() string {
|
if clickable {
|
||||||
o.mutex.RLock()
|
o.GetAppearanceData().ShowCommandIcon = ObjectShowCommandIcon
|
||||||
defer o.mutex.RUnlock()
|
} else {
|
||||||
return o.zoneName
|
o.GetAppearanceData().ShowCommandIcon = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// GetDeviceID returns the device ID
|
||||||
// Converted from C++ Object::GetDeviceID
|
|
||||||
func (o *Object) GetDeviceID() int8 {
|
func (o *Object) GetDeviceID() int8 {
|
||||||
o.mutex.RLock()
|
o.mutex.RLock()
|
||||||
defer o.mutex.RUnlock()
|
defer o.mutex.RUnlock()
|
||||||
return o.deviceID
|
return o.deviceID
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsObject always returns true for Object instances
|
// SetDeviceID sets the device ID
|
||||||
// Converted from C++ Object::IsObject
|
func (o *Object) SetDeviceID(deviceID int8) {
|
||||||
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()
|
o.mutex.Lock()
|
||||||
defer o.mutex.Unlock()
|
defer o.mutex.Unlock()
|
||||||
o.isCollector = isCollector
|
o.deviceID = deviceID
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsCollector returns whether this object is a collector
|
// Merchant methods
|
||||||
func (o *Object) IsCollector() bool {
|
|
||||||
|
// GetMerchantID returns the merchant ID
|
||||||
|
func (o *Object) GetMerchantID() int32 {
|
||||||
o.mutex.RLock()
|
o.mutex.RLock()
|
||||||
defer o.mutex.RUnlock()
|
defer o.mutex.RUnlock()
|
||||||
return o.isCollector
|
return o.merchantID
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMerchantID sets the merchant ID
|
// SetMerchantID sets the merchant ID
|
||||||
@ -281,11 +136,11 @@ func (o *Object) SetMerchantID(merchantID int32) {
|
|||||||
o.merchantID = merchantID
|
o.merchantID = merchantID
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMerchantID returns the merchant ID
|
// GetMerchantType returns the merchant type
|
||||||
func (o *Object) GetMerchantID() int32 {
|
func (o *Object) GetMerchantType() int8 {
|
||||||
o.mutex.RLock()
|
o.mutex.RLock()
|
||||||
defer o.mutex.RUnlock()
|
defer o.mutex.RUnlock()
|
||||||
return o.merchantID
|
return o.merchantType
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMerchantType sets the merchant type
|
// SetMerchantType sets the merchant type
|
||||||
@ -295,179 +150,34 @@ func (o *Object) SetMerchantType(merchantType int8) {
|
|||||||
o.merchantType = merchantType
|
o.merchantType = merchantType
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMerchantType returns the merchant type
|
// IsMerchant returns whether this object is a merchant
|
||||||
func (o *Object) GetMerchantType() int8 {
|
func (o *Object) IsMerchant() bool {
|
||||||
o.mutex.RLock()
|
o.mutex.RLock()
|
||||||
defer o.mutex.RUnlock()
|
defer o.mutex.RUnlock()
|
||||||
return o.merchantType
|
return o.merchantID > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMerchantLevelRange sets the merchant level range
|
// IsCollector returns whether this object is a collector
|
||||||
func (o *Object) SetMerchantLevelRange(minLevel, maxLevel int8) {
|
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()
|
o.mutex.Lock()
|
||||||
defer o.mutex.Unlock()
|
defer o.mutex.Unlock()
|
||||||
o.merchantMinLevel = minLevel
|
o.isCollector = isCollector
|
||||||
o.merchantMaxLevel = maxLevel
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMerchantMinLevel returns the minimum merchant level
|
// Transport methods
|
||||||
func (o *Object) GetMerchantMinLevel() int8 {
|
|
||||||
|
// GetTransporterID returns the transporter ID
|
||||||
|
func (o *Object) GetTransporterID() int32 {
|
||||||
o.mutex.RLock()
|
o.mutex.RLock()
|
||||||
defer o.mutex.RUnlock()
|
defer o.mutex.RUnlock()
|
||||||
return o.merchantMinLevel
|
return o.transporterID
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
// SetTransporterID sets the transporter ID
|
||||||
@ -477,127 +187,144 @@ func (o *Object) SetTransporterID(transporterID int32) {
|
|||||||
o.transporterID = transporterID
|
o.transporterID = transporterID
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTransporterID returns the transporter ID
|
// IsTransporter returns whether this object is a transporter
|
||||||
func (o *Object) GetTransporterID() int32 {
|
func (o *Object) IsTransporter() bool {
|
||||||
o.mutex.RLock()
|
o.mutex.RLock()
|
||||||
defer o.mutex.RUnlock()
|
defer o.mutex.RUnlock()
|
||||||
return o.transporterID
|
return o.transporterID > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetSoundsDisabled sets whether sounds are disabled
|
// Interaction methods
|
||||||
func (o *Object) SetSoundsDisabled(disabled bool) {
|
|
||||||
|
// 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()
|
o.mutex.Lock()
|
||||||
defer o.mutex.Unlock()
|
defer o.mutex.Unlock()
|
||||||
o.soundsDisabled = disabled
|
o.zoneName = zoneName
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsSoundsDisabled returns whether sounds are disabled
|
// Utility methods
|
||||||
func (o *Object) IsSoundsDisabled() bool {
|
|
||||||
o.mutex.RLock()
|
|
||||||
defer o.mutex.RUnlock()
|
|
||||||
return o.soundsDisabled
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetOmittedByDBFlag sets the omitted by DB flag
|
// GetObjectType returns a string describing the object type
|
||||||
func (o *Object) SetOmittedByDBFlag(omitted bool) {
|
func (o *Object) GetObjectType() string {
|
||||||
o.mutex.Lock()
|
switch {
|
||||||
defer o.mutex.Unlock()
|
case o.IsTransporter():
|
||||||
o.omittedByDBFlag = omitted
|
return "Transport"
|
||||||
}
|
case o.IsMerchant():
|
||||||
|
return "Merchant"
|
||||||
// IsOmittedByDBFlag returns the omitted by DB flag
|
case o.IsCollector():
|
||||||
func (o *Object) IsOmittedByDBFlag() bool {
|
return "Collector"
|
||||||
o.mutex.RLock()
|
case o.GetDeviceID() > 0:
|
||||||
defer o.mutex.RUnlock()
|
return "Device"
|
||||||
return o.omittedByDBFlag
|
case o.IsClickable():
|
||||||
}
|
return "Interactive"
|
||||||
|
default:
|
||||||
// SetLootTier sets the loot tier
|
return "Static"
|
||||||
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
|
// Validate checks if the object is properly configured
|
||||||
func (o *Object) ShowsCommandIcon() bool {
|
func (o *Object) Validate() error {
|
||||||
o.mutex.RLock()
|
if o.Spawn == nil {
|
||||||
defer o.mutex.RUnlock()
|
return fmt.Errorf("object must have spawn data")
|
||||||
return o.appearanceShowCommandIcon == ObjectShowCommandIcon
|
}
|
||||||
|
|
||||||
|
if o.GetSpawnType() != ObjectSpawnType {
|
||||||
|
return fmt.Errorf("object must have spawn type %d", ObjectSpawnType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetObjectInfo returns comprehensive information about the object
|
// Clone creates a copy of the object
|
||||||
func (o *Object) GetObjectInfo() map[string]any {
|
func (o *Object) Clone() *Object {
|
||||||
o.mutex.RLock()
|
o.mutex.RLock()
|
||||||
defer o.mutex.RUnlock()
|
defer o.mutex.RUnlock()
|
||||||
|
|
||||||
info := make(map[string]any)
|
newObject := &Object{
|
||||||
info["clickable"] = o.clickable
|
Spawn: o.Spawn, // Note: This is a shallow copy of the spawn
|
||||||
info["zone_name"] = o.zoneName
|
clickable: o.clickable,
|
||||||
info["device_id"] = o.deviceID
|
deviceID: o.deviceID,
|
||||||
info["database_id"] = o.databaseID
|
merchantID: o.merchantID,
|
||||||
info["size"] = o.size
|
merchantType: o.merchantType,
|
||||||
info["merchant_id"] = o.merchantID
|
merchantMinLevel: o.merchantMinLevel,
|
||||||
info["transporter_id"] = o.transporterID
|
merchantMaxLevel: o.merchantMaxLevel,
|
||||||
info["is_collector"] = o.isCollector
|
isCollector: o.isCollector,
|
||||||
info["sounds_disabled"] = o.soundsDisabled
|
transporterID: o.transporterID,
|
||||||
info["primary_commands"] = len(o.primaryCommands)
|
}
|
||||||
info["secondary_commands"] = len(o.secondaryCommands)
|
|
||||||
info["shows_command_icon"] = o.ShowsCommandIcon()
|
|
||||||
|
|
||||||
return info
|
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
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user