package items import ( "fmt" "log" "sync" "time" ) // SpellManager defines the interface for spell-related operations needed by items type SpellManager interface { // GetSpell retrieves spell information by ID and tier GetSpell(spellID uint32, tier int8) (Spell, error) // GetSpellsBySkill gets spells associated with a skill GetSpellsBySkill(skillID uint32) ([]uint32, error) // ValidateSpellID checks if a spell ID is valid ValidateSpellID(spellID uint32) bool } // PlayerManager defines the interface for player-related operations needed by items type PlayerManager interface { // GetPlayer retrieves player information by ID GetPlayer(playerID uint32) (Player, error) // GetPlayerLevel gets a player's current level GetPlayerLevel(playerID uint32) (int16, error) // GetPlayerClass gets a player's adventure class GetPlayerClass(playerID uint32) (int8, error) // GetPlayerRace gets a player's race GetPlayerRace(playerID uint32) (int8, error) // SendMessageToPlayer sends a message to a player SendMessageToPlayer(playerID uint32, channel int8, message string) error // GetPlayerName gets a player's name GetPlayerName(playerID uint32) (string, error) } // PacketManager defines the interface for packet-related operations type PacketManager interface { // SendPacketToPlayer sends a packet to a specific player SendPacketToPlayer(playerID uint32, packetData []byte) error // QueuePacketForPlayer queues a packet for delayed sending QueuePacketForPlayer(playerID uint32, packetData []byte) error // GetClientVersion gets the client version for a player GetClientVersion(playerID uint32) (int16, error) // SerializeItem serializes an item for network transmission SerializeItem(item *Item, clientVersion int16, player Player) ([]byte, error) } // RuleManager defines the interface for rules/configuration access type RuleManager interface { // GetBool retrieves a boolean rule value GetBool(category, rule string) bool // GetInt32 retrieves an int32 rule value GetInt32(category, rule string) int32 // GetFloat retrieves a float rule value GetFloat(category, rule string) float32 // GetString retrieves a string rule value GetString(category, rule string) string } // DatabaseService defines the interface for item persistence operations type DatabaseService interface { // LoadItems loads all item templates from the database LoadItems(masterList *MasterItemList) error // SaveItem saves an item template to the database SaveItem(item *Item) error // DeleteItem removes an item template from the database DeleteItem(itemID int32) error // LoadPlayerItems loads a player's inventory from the database LoadPlayerItems(playerID uint32) (*PlayerItemList, error) // SavePlayerItems saves a player's inventory to the database SavePlayerItems(playerID uint32, itemList *PlayerItemList) error // LoadPlayerEquipment loads a player's equipment from the database LoadPlayerEquipment(playerID uint32, appearanceType int8) (*EquipmentItemList, error) // SavePlayerEquipment saves a player's equipment to the database SavePlayerEquipment(playerID uint32, equipment *EquipmentItemList) error // LoadItemStats loads item stat mappings from the database LoadItemStats() (map[string]int32, map[int32]string, error) // SaveItemStat saves an item stat mapping to the database SaveItemStat(statID int32, statName string) error } // QuestManager defines the interface for quest-related item operations type QuestManager interface { // CheckQuestPrerequisites checks if a player meets quest prerequisites for an item CheckQuestPrerequisites(playerID uint32, questID int32) bool // GetQuestRewards gets quest rewards for an item GetQuestRewards(questID int32) ([]*QuestRewardData, error) // IsQuestItem checks if an item is a quest item IsQuestItem(itemID int32) bool } // BrokerManager defines the interface for broker/marketplace operations type BrokerManager interface { // SearchItems searches for items on the broker SearchItems(criteria *ItemSearchCriteria) ([]*Item, error) // ListItem lists an item on the broker ListItem(playerID uint32, item *Item, price int64) error // BuyItem purchases an item from the broker BuyItem(playerID uint32, itemID int32, sellerID uint32) error // GetItemPrice gets the current market price for an item GetItemPrice(itemID int32) (int64, error) } // CraftingManager defines the interface for crafting-related item operations type CraftingManager interface { // CanCraftItem checks if a player can craft an item CanCraftItem(playerID uint32, itemID int32) bool // GetCraftingRequirements gets crafting requirements for an item GetCraftingRequirements(itemID int32) ([]CraftingRequirement, error) // CraftItem handles item crafting CraftItem(playerID uint32, itemID int32, quality int8) (*Item, error) } // HousingManager defines the interface for housing-related item operations type HousingManager interface { // CanPlaceItem checks if an item can be placed in a house CanPlaceItem(playerID uint32, houseID int32, item *Item) bool // PlaceItem places an item in a house PlaceItem(playerID uint32, houseID int32, item *Item, location HouseLocation) error // RemoveItem removes an item from a house RemoveItem(playerID uint32, houseID int32, itemID int32) error // GetHouseItems gets all items in a house GetHouseItems(houseID int32) ([]*Item, error) } // LootManager defines the interface for loot-related operations type LootManager interface { // GenerateLoot generates loot for a loot table GenerateLoot(lootTableID int32, playerLevel int16) ([]*Item, error) // DistributeLoot distributes loot to players DistributeLoot(items []*Item, playerIDs []uint32, lootMethod int8) error // CanLootItem checks if a player can loot an item CanLootItem(playerID uint32, item *Item) bool } // Data structures used by the interfaces // Spell represents a spell in the game type Spell interface { GetID() uint32 GetName() string GetIcon() uint32 GetIconBackdrop() uint32 GetTier() int8 GetDescription() string } // Player represents a player in the game type Player interface { GetID() uint32 GetName() string GetLevel() int16 GetAdventureClass() int8 GetTradeskillClass() int8 GetRace() int8 GetGender() int8 GetAlignment() int8 } // Entity represents an entity (player or NPC) that can have items type Entity interface { GetID() uint32 GetName() string GetLevel() int16 GetRace() int8 GetGender() int8 GetAlignment() int8 IsPlayer() bool IsNPC() bool // GetStatValueByName gets a stat value by name for item calculations GetStatValueByName(statName string) float64 // GetSkillValueByName gets a skill value by name for item calculations GetSkillValueByName(skillName string) int32 } // CraftingRequirement represents a crafting requirement type CraftingRequirement struct { ItemID int32 `json:"item_id"` Quantity int16 `json:"quantity"` Skill int32 `json:"skill"` Level int16 `json:"level"` } // HouseLocation represents a location within a house type HouseLocation struct { X float32 `json:"x"` Y float32 `json:"y"` Z float32 `json:"z"` Heading float32 `json:"heading"` Pitch float32 `json:"pitch"` Roll float32 `json:"roll"` Location int8 `json:"location"` // 0=floor, 1=ceiling, 2=wall } // ItemSystemAdapter provides a high-level interface to the complete item system type ItemSystemAdapter struct { masterList *MasterItemList playerLists map[uint32]*PlayerItemList equipmentLists map[uint32]*EquipmentItemList spellManager SpellManager playerManager PlayerManager packetManager PacketManager ruleManager RuleManager databaseService DatabaseService questManager QuestManager brokerManager BrokerManager craftingManager CraftingManager housingManager HousingManager lootManager LootManager mutex sync.RWMutex } // NewItemSystemAdapter creates a new item system adapter with all dependencies func NewItemSystemAdapter( masterList *MasterItemList, spellManager SpellManager, playerManager PlayerManager, packetManager PacketManager, ruleManager RuleManager, databaseService DatabaseService, questManager QuestManager, brokerManager BrokerManager, craftingManager CraftingManager, housingManager HousingManager, lootManager LootManager, ) *ItemSystemAdapter { return &ItemSystemAdapter{ masterList: masterList, playerLists: make(map[uint32]*PlayerItemList), equipmentLists: make(map[uint32]*EquipmentItemList), spellManager: spellManager, playerManager: playerManager, packetManager: packetManager, ruleManager: ruleManager, databaseService: databaseService, questManager: questManager, brokerManager: brokerManager, craftingManager: craftingManager, housingManager: housingManager, lootManager: lootManager, } } // Initialize sets up the item system (loads items from database, etc.) func (isa *ItemSystemAdapter) Initialize() error { // Load items from database err := isa.databaseService.LoadItems(isa.masterList) if err != nil { return err } // Load item stat mappings statsStrings, statsIDs, err := isa.databaseService.LoadItemStats() if err != nil { return err } isa.masterList.mutex.Lock() isa.masterList.mappedItemStatsStrings = statsStrings isa.masterList.mappedItemStatTypeIDs = statsIDs isa.masterList.mutex.Unlock() return nil } // GetPlayerInventory gets or loads a player's inventory func (isa *ItemSystemAdapter) GetPlayerInventory(playerID uint32) (*PlayerItemList, error) { isa.mutex.Lock() defer isa.mutex.Unlock() if itemList, exists := isa.playerLists[playerID]; exists { return itemList, nil } // Load from database itemList, err := isa.databaseService.LoadPlayerItems(playerID) if err != nil { return nil, err } if itemList == nil { itemList = NewPlayerItemList() } isa.playerLists[playerID] = itemList return itemList, nil } // GetPlayerEquipment gets or loads a player's equipment func (isa *ItemSystemAdapter) GetPlayerEquipment(playerID uint32, appearanceType int8) (*EquipmentItemList, error) { isa.mutex.Lock() defer isa.mutex.Unlock() key := uint32(playerID)*10 + uint32(appearanceType) if equipment, exists := isa.equipmentLists[key]; exists { return equipment, nil } // Load from database equipment, err := isa.databaseService.LoadPlayerEquipment(playerID, appearanceType) if err != nil { return nil, err } if equipment == nil { equipment = NewEquipmentItemList() equipment.SetAppearanceType(appearanceType) } isa.equipmentLists[key] = equipment return equipment, nil } // SavePlayerData saves a player's item data func (isa *ItemSystemAdapter) SavePlayerData(playerID uint32) error { isa.mutex.RLock() defer isa.mutex.RUnlock() // Save inventory if itemList, exists := isa.playerLists[playerID]; exists { err := isa.databaseService.SavePlayerItems(playerID, itemList) if err != nil { return err } } // Save equipment (both normal and appearance) for key, equipment := range isa.equipmentLists { if key/10 == playerID { err := isa.databaseService.SavePlayerEquipment(playerID, equipment) if err != nil { return err } } } return nil } // GiveItemToPlayer gives an item to a player func (isa *ItemSystemAdapter) GiveItemToPlayer(playerID uint32, itemID int32, quantity int16, addType AddItemType) error { // Get item template itemTemplate := isa.masterList.GetItem(itemID) if itemTemplate == nil { return ErrItemNotFound } // Create item instance item := NewItemFromTemplate(itemTemplate) item.Details.Count = quantity // Get player inventory inventory, err := isa.GetPlayerInventory(playerID) if err != nil { return err } // Try to add item to inventory if !inventory.AddItem(item) { return ErrInsufficientSpace } // Send update to player player, err := isa.playerManager.GetPlayer(playerID) if err != nil { return err } clientVersion, _ := isa.packetManager.GetClientVersion(playerID) packetData, err := isa.packetManager.SerializeItem(item, clientVersion, player) if err != nil { return err } return isa.packetManager.SendPacketToPlayer(playerID, packetData) } // RemoveItemFromPlayer removes an item from a player func (isa *ItemSystemAdapter) RemoveItemFromPlayer(playerID uint32, uniqueID int32, quantity int16) error { inventory, err := isa.GetPlayerInventory(playerID) if err != nil { return err } item := inventory.GetItemFromUniqueID(uniqueID, true, true) if item == nil { return ErrItemNotFound } // Check if item can be removed if item.IsItemLocked() { return ErrItemLocked } if item.Details.Count <= quantity { // Remove entire stack inventory.RemoveItem(item, true, true) } else { // Reduce quantity item.Details.Count -= quantity } return nil } // EquipItem equips an item for a player func (isa *ItemSystemAdapter) EquipItem(playerID uint32, uniqueID int32, slot int8, appearanceType int8) error { inventory, err := isa.GetPlayerInventory(playerID) if err != nil { return err } equipment, err := isa.GetPlayerEquipment(playerID, appearanceType) if err != nil { return err } // Get item from inventory item := inventory.GetItemFromUniqueID(uniqueID, false, true) if item == nil { return ErrItemNotFound } // Check if item can be equipped if !equipment.CanItemBeEquippedInSlot(item, slot) { return ErrCannotEquip } // Check class/race/level requirements player, err := isa.playerManager.GetPlayer(playerID) if err != nil { return err } if !item.CheckClass(player.GetAdventureClass(), player.GetTradeskillClass()) { return ErrCannotEquip } if !item.CheckClassLevel(player.GetAdventureClass(), player.GetTradeskillClass(), player.GetLevel()) { return ErrCannotEquip } // Remove from inventory inventory.RemoveItem(item, false, true) // Check if slot is occupied and unequip current item currentItem := equipment.GetItem(slot) if currentItem != nil { equipment.RemoveItem(slot, false) inventory.AddItem(currentItem) } // Equip new item equipment.SetItem(slot, item, false) return nil } // UnequipItem unequips an item for a player func (isa *ItemSystemAdapter) UnequipItem(playerID uint32, slot int8, appearanceType int8) error { inventory, err := isa.GetPlayerInventory(playerID) if err != nil { return err } equipment, err := isa.GetPlayerEquipment(playerID, appearanceType) if err != nil { return err } // Get equipped item item := equipment.GetItem(slot) if item == nil { return ErrItemNotFound } // Check if item can be unequipped if item.IsItemLocked() { return ErrItemLocked } // Remove from equipment equipment.RemoveItem(slot, false) // Add to inventory if !inventory.AddItem(item) { // Inventory full, add to overflow inventory.AddOverflowItem(item) } return nil } // MoveItem moves an item within a player's inventory func (isa *ItemSystemAdapter) MoveItem(playerID uint32, fromBagID int32, fromSlot int16, toBagID int32, toSlot int16, appearanceType int8) error { inventory, err := isa.GetPlayerInventory(playerID) if err != nil { return err } // Get item from source location item := inventory.GetItem(fromBagID, fromSlot, appearanceType) if item == nil { return ErrItemNotFound } // Check if item is locked if item.IsItemLocked() { return ErrItemLocked } // Move item inventory.MoveItem(item, toBagID, toSlot, appearanceType, true) return nil } // SearchBrokerItems searches for items on the broker func (isa *ItemSystemAdapter) SearchBrokerItems(criteria *ItemSearchCriteria) ([]*Item, error) { if isa.brokerManager == nil { return nil, fmt.Errorf("broker manager not available") } return isa.brokerManager.SearchItems(criteria) } // CraftItem handles item crafting func (isa *ItemSystemAdapter) CraftItem(playerID uint32, itemID int32, quality int8) (*Item, error) { if isa.craftingManager == nil { return nil, fmt.Errorf("crafting manager not available") } // Check if player can craft the item if !isa.craftingManager.CanCraftItem(playerID, itemID) { return nil, fmt.Errorf("player cannot craft this item") } // Craft the item return isa.craftingManager.CraftItem(playerID, itemID, quality) } // GetPlayerItemStats returns statistics about a player's items func (isa *ItemSystemAdapter) GetPlayerItemStats(playerID uint32) (map[string]any, error) { inventory, err := isa.GetPlayerInventory(playerID) if err != nil { return nil, err } equipment, err := isa.GetPlayerEquipment(playerID, BaseEquipment) if err != nil { return nil, err } // Calculate equipment bonuses bonuses := equipment.CalculateEquipmentBonuses() return map[string]any{ "player_id": playerID, "total_items": inventory.GetNumberOfItems(), "equipped_items": equipment.GetNumberOfItems(), "inventory_weight": inventory.GetWeight(), "equipment_weight": equipment.GetWeight(), "free_slots": inventory.GetNumberOfFreeSlots(), "overflow_items": len(inventory.GetOverflowItemList()), "stat_bonuses": bonuses, "last_update": time.Now(), }, nil } // GetSystemStats returns comprehensive statistics about the item system func (isa *ItemSystemAdapter) GetSystemStats() map[string]any { isa.mutex.RLock() defer isa.mutex.RUnlock() masterStats := isa.masterList.GetStats() return map[string]any{ "total_item_templates": masterStats.TotalItems, "items_by_type": masterStats.ItemsByType, "items_by_tier": masterStats.ItemsByTier, "active_players": len(isa.playerLists), "cached_inventories": len(isa.playerLists), "cached_equipment": len(isa.equipmentLists), "last_update": time.Now(), } } // ClearPlayerData removes cached data for a player (e.g., when they log out) func (isa *ItemSystemAdapter) ClearPlayerData(playerID uint32) { isa.mutex.Lock() defer isa.mutex.Unlock() // Remove inventory delete(isa.playerLists, playerID) // Remove equipment keysToDelete := make([]uint32, 0) for key := range isa.equipmentLists { if key/10 == playerID { keysToDelete = append(keysToDelete, key) } } for _, key := range keysToDelete { delete(isa.equipmentLists, key) } } // ValidatePlayerItems validates all items for a player func (isa *ItemSystemAdapter) ValidatePlayerItems(playerID uint32) *ItemValidationResult { result := &ItemValidationResult{Valid: true} // Validate inventory inventory, err := isa.GetPlayerInventory(playerID) if err != nil { result.Valid = false result.Errors = append(result.Errors, fmt.Sprintf("Failed to load inventory: %v", err)) return result } allItems := inventory.GetAllItems() for index, item := range allItems { itemResult := item.Validate() if !itemResult.Valid { result.Valid = false for _, itemErr := range itemResult.Errors { result.Errors = append(result.Errors, fmt.Sprintf("Inventory item %d: %s", index, itemErr)) } } } // Validate equipment equipment, err := isa.GetPlayerEquipment(playerID, BaseEquipment) if err != nil { result.Valid = false result.Errors = append(result.Errors, fmt.Sprintf("Failed to load equipment: %v", err)) return result } equipResult := equipment.ValidateEquipment() if !equipResult.Valid { result.Valid = false result.Errors = append(result.Errors, equipResult.Errors...) } return result } // MockImplementations for testing // MockSpellManager is a mock implementation of SpellManager for testing type MockSpellManager struct { spells map[uint32]MockSpell } // MockSpell is a mock implementation of Spell for testing type MockSpell struct { id uint32 name string icon uint32 iconBackdrop uint32 tier int8 description string } func (ms MockSpell) GetID() uint32 { return ms.id } func (ms MockSpell) GetName() string { return ms.name } func (ms MockSpell) GetIcon() uint32 { return ms.icon } func (ms MockSpell) GetIconBackdrop() uint32 { return ms.iconBackdrop } func (ms MockSpell) GetTier() int8 { return ms.tier } func (ms MockSpell) GetDescription() string { return ms.description } func (msm *MockSpellManager) GetSpell(spellID uint32, tier int8) (Spell, error) { if spell, exists := msm.spells[spellID]; exists { return spell, nil } return nil, fmt.Errorf("spell not found: %d", spellID) } func (msm *MockSpellManager) GetSpellsBySkill(skillID uint32) ([]uint32, error) { return []uint32{}, nil } func (msm *MockSpellManager) ValidateSpellID(spellID uint32) bool { _, exists := msm.spells[spellID] return exists } // NewMockSpellManager creates a new mock spell manager func NewMockSpellManager() *MockSpellManager { return &MockSpellManager{ spells: make(map[uint32]MockSpell), } } // AddMockSpell adds a mock spell for testing func (msm *MockSpellManager) AddMockSpell(id uint32, name string, icon uint32, tier int8, description string) { msm.spells[id] = MockSpell{ id: id, name: name, icon: icon, iconBackdrop: icon + 1000, tier: tier, description: description, } } func init() { log.Printf("Item system interfaces initialized") }