package world import ( "fmt" "sync" "eq2emu/internal/database" "eq2emu/internal/items" ) // ItemManager manages items for the world server type ItemManager struct { masterItemList *items.MasterItemList itemSystemAdapter *items.ItemSystemAdapter database *database.Database world *World // Reference to world server // World-specific item tracking playerInventories map[uint32]*items.PlayerItemList // Player ID -> Inventory playerEquipment map[uint32]*items.EquipmentItemList // Player ID -> Equipment worldDrops map[int32][]*items.Item // Zone ID -> Ground items mutex sync.RWMutex } // NewItemManager creates a new item manager for the world server func NewItemManager(db *database.Database) *ItemManager { // Create master item list masterList := items.NewMasterItemList() // Create adapters for the item system dbAdapter := &WorldItemDatabaseAdapter{db: db} playerAdapter := &WorldItemPlayerAdapter{} packetAdapter := &WorldItemPacketAdapter{} ruleAdapter := &WorldItemRuleAdapter{} // Create mock adapters for optional dependencies questAdapter := &MockItemQuestAdapter{} brokerAdapter := &MockItemBrokerAdapter{} craftingAdapter := &MockItemCraftingAdapter{} housingAdapter := &MockItemHousingAdapter{} // Create loot manager adapter lootAdapter := &WorldItemLootAdapter{} // Create item system adapter systemAdapter := items.NewItemSystemAdapter( masterList, nil, // SpellManager - will be set later when spells are integrated playerAdapter, packetAdapter, ruleAdapter, dbAdapter, questAdapter, brokerAdapter, craftingAdapter, housingAdapter, lootAdapter, ) return &ItemManager{ masterItemList: masterList, itemSystemAdapter: systemAdapter, database: db, playerInventories: make(map[uint32]*items.PlayerItemList), playerEquipment: make(map[uint32]*items.EquipmentItemList), worldDrops: make(map[int32][]*items.Item), } } // SetWorld sets the world server reference func (im *ItemManager) SetWorld(world *World) { im.world = world } // LoadItems loads all item templates and data from database func (im *ItemManager) LoadItems() error { fmt.Println("Loading item data...") // Initialize the item system adapter err := im.itemSystemAdapter.Initialize() if err != nil { return fmt.Errorf("failed to initialize item system: %w", err) } stats := im.masterItemList.GetStats() fmt.Printf("Loaded %d item templates\n", stats.TotalItems) return nil } // GetPlayerInventory gets a player's inventory func (im *ItemManager) GetPlayerInventory(playerID uint32) (*items.PlayerItemList, error) { im.mutex.RLock() defer im.mutex.RUnlock() return im.itemSystemAdapter.GetPlayerInventory(playerID) } // GetPlayerEquipment gets a player's equipment func (im *ItemManager) GetPlayerEquipment(playerID uint32, appearanceType int8) (*items.EquipmentItemList, error) { im.mutex.RLock() defer im.mutex.RUnlock() return im.itemSystemAdapter.GetPlayerEquipment(playerID, appearanceType) } // GiveItemToPlayer gives an item to a player func (im *ItemManager) GiveItemToPlayer(playerID uint32, itemID int32, quantity int16) error { return im.itemSystemAdapter.GiveItemToPlayer(playerID, itemID, quantity, items.NotSet) } // RemoveItemFromPlayer removes an item from a player func (im *ItemManager) RemoveItemFromPlayer(playerID uint32, uniqueID int32, quantity int16) error { return im.itemSystemAdapter.RemoveItemFromPlayer(playerID, uniqueID, quantity) } // EquipItem equips an item for a player func (im *ItemManager) EquipItem(playerID uint32, uniqueID int32, slot int8) error { return im.itemSystemAdapter.EquipItem(playerID, uniqueID, slot, 0) // Base equipment } // UnequipItem unequips an item for a player func (im *ItemManager) UnequipItem(playerID uint32, slot int8) error { return im.itemSystemAdapter.UnequipItem(playerID, slot, 0) // Base equipment } // MoveItem moves an item within a player's inventory func (im *ItemManager) MoveItem(playerID uint32, fromBagID int32, fromSlot int16, toBagID int32, toSlot int16) error { return im.itemSystemAdapter.MoveItem(playerID, fromBagID, fromSlot, toBagID, toSlot, 0) // Base equipment } // CreateWorldDrop creates an item drop in the world func (im *ItemManager) CreateWorldDrop(itemID int32, quantity int16, x, y, z float32, zoneID int32) error { im.mutex.Lock() defer im.mutex.Unlock() // Get item template itemTemplate := im.masterItemList.GetItem(itemID) if itemTemplate == nil { return fmt.Errorf("item template not found: %d", itemID) } // Create item instance item := items.NewItemFromTemplate(itemTemplate) item.Details.Count = quantity // Set position data (would normally be in a WorldItem wrapper) // TODO: Create WorldItem wrapper with position data // Add to world drops tracking im.worldDrops[zoneID] = append(im.worldDrops[zoneID], item) fmt.Printf("Created world drop: %s x%d at (%.2f, %.2f, %.2f) in zone %d\n", item.Name, quantity, x, y, z, zoneID) return nil } // GetWorldDrops gets all ground items in a zone func (im *ItemManager) GetWorldDrops(zoneID int32) []*items.Item { im.mutex.RLock() defer im.mutex.RUnlock() if drops, exists := im.worldDrops[zoneID]; exists { // Return a copy to avoid concurrent modification result := make([]*items.Item, len(drops)) copy(result, drops) return result } return nil } // PickupWorldDrop handles picking up a ground item func (im *ItemManager) PickupWorldDrop(playerID uint32, itemUniqueID int32, zoneID int32) error { im.mutex.Lock() defer im.mutex.Unlock() // Find the item in world drops drops, exists := im.worldDrops[zoneID] if !exists { return fmt.Errorf("no drops in zone %d", zoneID) } var foundItem *items.Item var itemIndex int = -1 for i, item := range drops { if int32(item.Details.UniqueID) == itemUniqueID { foundItem = item itemIndex = i break } } if foundItem == nil { return fmt.Errorf("item not found: %d", itemUniqueID) } // Try to give item to player err := im.GiveItemToPlayer(playerID, foundItem.Details.ItemID, foundItem.Details.Count) if err != nil { return fmt.Errorf("failed to give item to player: %w", err) } // Remove from world drops im.worldDrops[zoneID] = append(drops[:itemIndex], drops[itemIndex+1:]...) fmt.Printf("Player %d picked up item: %s x%d\n", playerID, foundItem.Name, foundItem.Details.Count) return nil } // GenerateLootForNPC generates loot for an NPC kill func (im *ItemManager) GenerateLootForNPC(npcID int32, killerLevel int16) ([]*items.Item, error) { // TODO: Implement proper loot table lookup and generation // For now, create some basic test loot testLoot := []*items.Item{} // Example: Generate some coins based on NPC level // This would normally come from loot tables in the database fmt.Printf("Generated loot for NPC %d (placeholder implementation)\n", npcID) return testLoot, nil } // OnPlayerLogin handles player login - loads their item data func (im *ItemManager) OnPlayerLogin(playerID uint32) error { fmt.Printf("Loading item data for player %d...\n", playerID) // Pre-load player inventory and equipment _, err := im.GetPlayerInventory(playerID) if err != nil { return fmt.Errorf("failed to load player inventory: %w", err) } _, err = im.GetPlayerEquipment(playerID, items.BaseEquipment) if err != nil { return fmt.Errorf("failed to load player equipment: %w", err) } fmt.Printf("Item data loaded for player %d\n", playerID) return nil } // OnPlayerLogout handles player logout - saves and clears their item data func (im *ItemManager) OnPlayerLogout(playerID uint32) error { fmt.Printf("Saving item data for player %d...\n", playerID) // Save player data err := im.itemSystemAdapter.SavePlayerData(playerID) if err != nil { return fmt.Errorf("failed to save player data: %w", err) } // Clear cached data im.itemSystemAdapter.ClearPlayerData(playerID) fmt.Printf("Item data saved and cleared for player %d\n", playerID) return nil } // GetItemTemplate gets an item template by ID func (im *ItemManager) GetItemTemplate(itemID int32) *items.Item { return im.masterItemList.GetItem(itemID) } // SearchItems searches for item templates by name func (im *ItemManager) SearchItems(name string, maxResults int32) []*items.Item { // TODO: Implement proper search - for now return empty slice return []*items.Item{} } // GetStatistics returns item system statistics func (im *ItemManager) GetStatistics() map[string]any { im.mutex.RLock() defer im.mutex.RUnlock() systemStats := im.itemSystemAdapter.GetSystemStats() // Add world-specific statistics totalWorldDrops := 0 for _, drops := range im.worldDrops { totalWorldDrops += len(drops) } result := make(map[string]any) for k, v := range systemStats { result[k] = v } result["world_drops"] = totalWorldDrops result["zones_with_drops"] = len(im.worldDrops) return result } // ValidatePlayerItems validates a player's items func (im *ItemManager) ValidatePlayerItems(playerID uint32) *items.ItemValidationResult { return im.itemSystemAdapter.ValidatePlayerItems(playerID) } // Shutdown gracefully shuts down the item manager func (im *ItemManager) Shutdown() { fmt.Println("Shutting down item manager...") im.mutex.Lock() defer im.mutex.Unlock() // Clear all tracking im.playerInventories = make(map[uint32]*items.PlayerItemList) im.playerEquipment = make(map[uint32]*items.EquipmentItemList) im.worldDrops = make(map[int32][]*items.Item) fmt.Println("Item manager shutdown complete") } // WorldItemDatabaseAdapter adapts the world database for item use type WorldItemDatabaseAdapter struct { db *database.Database } // LoadItems implements items.DatabaseService interface func (wdb *WorldItemDatabaseAdapter) LoadItems(masterList *items.MasterItemList) error { fmt.Println("Loading items from database...") // Load item templates from database rows, err := wdb.db.Query(` SELECT id, name, item_type, icon, description, tier, level, classes, races, adventure_classes, tradeskill_classes, stack_count, weight, generic_info, bag_info, food_info, weapon_info, ranged_info, details, appearance FROM items ORDER BY id `) if err != nil { return fmt.Errorf("failed to query items: %w", err) } defer rows.Close() itemCount := 0 for rows.Next() { // Create new item from database data item := items.NewItem() var id, itemType, icon, tier, level, stackCount int32 var weight, genericInfo, bagInfo, foodInfo, weaponInfo, rangedInfo int32 var details, appearance int32 var name, description, classes, races, adventureClasses, tradeskillClasses string err := rows.Scan(&id, &name, &itemType, &icon, &description, &tier, &level, &classes, &races, &adventureClasses, &tradeskillClasses, &stackCount, &weight, &genericInfo, &bagInfo, &foodInfo, &weaponInfo, &rangedInfo, &details, &appearance) if err != nil { fmt.Printf("Error scanning item row: %v\n", err) continue } // Set item properties item.Details.ItemID = id item.Name = name item.Details.Icon = int16(icon) item.Description = description item.Details.Tier = int8(tier) item.Details.RecommendedLevel = int16(level) item.StackCount = int16(stackCount) // TODO: Set weight when field is available // item.Weight = weight // TODO: Parse class/race strings and set appropriate fields // item.SetClasses(parseClassString(classes)) // item.SetRaces(parseRaceString(races)) // Add to master list masterList.AddItem(item) itemCount++ } if err := rows.Err(); err != nil { return fmt.Errorf("error iterating item rows: %w", err) } fmt.Printf("Successfully loaded %d item templates from database\n", itemCount) return nil } // SaveItem implements items.DatabaseService interface func (wdb *WorldItemDatabaseAdapter) SaveItem(item *items.Item) error { // TODO: Implement item template saving fmt.Printf("Saving item template: %s (ID: %d) - not yet implemented\n", item.Name, item.Details.ItemID) return nil } // DeleteItem implements items.DatabaseService interface func (wdb *WorldItemDatabaseAdapter) DeleteItem(itemID int32) error { // TODO: Implement item template deletion fmt.Printf("Deleting item template: %d - not yet implemented\n", itemID) return nil } // LoadPlayerItems implements items.DatabaseService interface func (wdb *WorldItemDatabaseAdapter) LoadPlayerItems(playerID uint32) (*items.PlayerItemList, error) { // TODO: Implement player inventory loading from database fmt.Printf("Loading player inventory for player %d - creating empty inventory\n", playerID) return items.NewPlayerItemList(), nil } // SavePlayerItems implements items.DatabaseService interface func (wdb *WorldItemDatabaseAdapter) SavePlayerItems(playerID uint32, itemList *items.PlayerItemList) error { // TODO: Implement player inventory saving to database fmt.Printf("Saving player inventory for player %d - not yet implemented\n", playerID) return nil } // LoadPlayerEquipment implements items.DatabaseService interface func (wdb *WorldItemDatabaseAdapter) LoadPlayerEquipment(playerID uint32, appearanceType int8) (*items.EquipmentItemList, error) { // TODO: Implement player equipment loading from database fmt.Printf("Loading player equipment for player %d (type %d) - creating empty equipment\n", playerID, appearanceType) equipment := items.NewEquipmentItemList() equipment.SetAppearanceType(appearanceType) return equipment, nil } // SavePlayerEquipment implements items.DatabaseService interface func (wdb *WorldItemDatabaseAdapter) SavePlayerEquipment(playerID uint32, equipment *items.EquipmentItemList) error { // TODO: Implement player equipment saving to database fmt.Printf("Saving player equipment for player %d - not yet implemented\n", playerID) return nil } // LoadItemStats implements items.DatabaseService interface func (wdb *WorldItemDatabaseAdapter) LoadItemStats() (map[string]int32, map[int32]string, error) { // TODO: Implement item stat mapping loading fmt.Println("Loading item stats - using placeholder data") statsStrings := map[string]int32{ "health": 1, "power": 2, "strength": 3, "stamina": 4, "agility": 5, "wisdom": 6, "intelligence": 7, } statsIDs := map[int32]string{ 1: "health", 2: "power", 3: "strength", 4: "stamina", 5: "agility", 6: "wisdom", 7: "intelligence", } return statsStrings, statsIDs, nil } // SaveItemStat implements items.DatabaseService interface func (wdb *WorldItemDatabaseAdapter) SaveItemStat(statID int32, statName string) error { // TODO: Implement item stat mapping saving fmt.Printf("Saving item stat mapping: %d = %s - not yet implemented\n", statID, statName) return nil } // WorldItemPlayerAdapter adapts world player functionality for items type WorldItemPlayerAdapter struct{} // GetPlayer implements items.PlayerManager interface func (wpa *WorldItemPlayerAdapter) GetPlayer(playerID uint32) (items.Player, error) { // TODO: Get actual player from world server when player system is integrated return &MockPlayer{id: playerID, name: fmt.Sprintf("Player_%d", playerID), level: 50}, nil } // GetPlayerLevel implements items.PlayerManager interface func (wpa *WorldItemPlayerAdapter) GetPlayerLevel(playerID uint32) (int16, error) { // TODO: Get actual player level from world server return 50, nil // Placeholder } // GetPlayerClass implements items.PlayerManager interface func (wpa *WorldItemPlayerAdapter) GetPlayerClass(playerID uint32) (int8, error) { // TODO: Get actual player class from world server return 1, nil // Placeholder } // GetPlayerRace implements items.PlayerManager interface func (wpa *WorldItemPlayerAdapter) GetPlayerRace(playerID uint32) (int8, error) { // TODO: Get actual player race from world server return 1, nil // Placeholder } // SendMessageToPlayer implements items.PlayerManager interface func (wpa *WorldItemPlayerAdapter) SendMessageToPlayer(playerID uint32, channel int8, message string) error { // TODO: Send actual message via world server fmt.Printf("[CHAT] Player %d: %s\n", playerID, message) return nil } // GetPlayerName implements items.PlayerManager interface func (wpa *WorldItemPlayerAdapter) GetPlayerName(playerID uint32) (string, error) { // TODO: Get actual player name from world server return fmt.Sprintf("Player_%d", playerID), nil } // WorldItemPacketAdapter adapts world packet functionality for items type WorldItemPacketAdapter struct{} // SendPacketToPlayer implements items.PacketManager interface func (wpa *WorldItemPacketAdapter) SendPacketToPlayer(playerID uint32, packetData []byte) error { // TODO: Send actual packet via world server fmt.Printf("Sending item packet to player %d (%d bytes)\n", playerID, len(packetData)) return nil } // QueuePacketForPlayer implements items.PacketManager interface func (wpa *WorldItemPacketAdapter) QueuePacketForPlayer(playerID uint32, packetData []byte) error { // TODO: Queue packet via world server fmt.Printf("Queuing item packet for player %d (%d bytes)\n", playerID, len(packetData)) return nil } // GetClientVersion implements items.PacketManager interface func (wpa *WorldItemPacketAdapter) GetClientVersion(playerID uint32) (int16, error) { // TODO: Get actual client version from world server return 1096, nil // Default EQ2 client version } // SerializeItem implements items.PacketManager interface func (wpa *WorldItemPacketAdapter) SerializeItem(item *items.Item, clientVersion int16, player items.Player) ([]byte, error) { // TODO: Implement actual item serialization for network transmission fmt.Printf("Serializing item %s for client version %d\n", item.Name, clientVersion) return []byte{0x01, 0x02, 0x03}, nil // Placeholder data } // WorldItemRuleAdapter adapts world rules functionality for items type WorldItemRuleAdapter struct{} // GetBool implements items.RuleManager interface func (wra *WorldItemRuleAdapter) GetBool(category, rule string) bool { // TODO: Get actual rule values from world server rules system fmt.Printf("Getting rule %s:%s (bool) - using default\n", category, rule) return true // Default value } // GetInt32 implements items.RuleManager interface func (wra *WorldItemRuleAdapter) GetInt32(category, rule string) int32 { // TODO: Get actual rule values from world server rules system fmt.Printf("Getting rule %s:%s (int32) - using default\n", category, rule) return 100 // Default value } // GetFloat implements items.RuleManager interface func (wra *WorldItemRuleAdapter) GetFloat(category, rule string) float32 { // TODO: Get actual rule values from world server rules system fmt.Printf("Getting rule %s:%s (float) - using default\n", category, rule) return 1.0 // Default value } // GetString implements items.RuleManager interface func (wra *WorldItemRuleAdapter) GetString(category, rule string) string { // TODO: Get actual rule values from world server rules system fmt.Printf("Getting rule %s:%s (string) - using default\n", category, rule) return "default" // Default value } // WorldItemLootAdapter adapts world loot functionality for items type WorldItemLootAdapter struct{} // GenerateLoot implements items.LootManager interface func (wla *WorldItemLootAdapter) GenerateLoot(lootTableID int32, playerLevel int16) ([]*items.Item, error) { // TODO: Implement actual loot generation from loot tables fmt.Printf("Generating loot from table %d for level %d player\n", lootTableID, playerLevel) return []*items.Item{}, nil } // DistributeLoot implements items.LootManager interface func (wla *WorldItemLootAdapter) DistributeLoot(lootItems []*items.Item, playerIDs []uint32, lootMethod int8) error { // TODO: Implement actual loot distribution fmt.Printf("Distributing %d items to %d players using method %d\n", len(lootItems), len(playerIDs), lootMethod) return nil } // CanLootItem implements items.LootManager interface func (wla *WorldItemLootAdapter) CanLootItem(playerID uint32, item *items.Item) bool { // TODO: Implement actual loot permission checking return true // Allow all looting for now } // Mock implementations for optional dependencies // MockPlayer implements items.Player interface for testing type MockPlayer struct { id uint32 name string level int16 adventureClass int8 tradeskillClass int8 race int8 gender int8 alignment int8 } func (mp *MockPlayer) GetID() uint32 { return mp.id } func (mp *MockPlayer) GetName() string { return mp.name } func (mp *MockPlayer) GetLevel() int16 { return mp.level } func (mp *MockPlayer) GetAdventureClass() int8 { return mp.adventureClass } func (mp *MockPlayer) GetTradeskillClass() int8 { return mp.tradeskillClass } func (mp *MockPlayer) GetRace() int8 { return mp.race } func (mp *MockPlayer) GetGender() int8 { return mp.gender } func (mp *MockPlayer) GetAlignment() int8 { return mp.alignment } // MockItemQuestAdapter provides mock quest functionality type MockItemQuestAdapter struct{} func (mqa *MockItemQuestAdapter) CheckQuestPrerequisites(playerID uint32, questID int32) bool { return true // Allow all for now } func (mqa *MockItemQuestAdapter) GetQuestRewards(questID int32) ([]*items.QuestRewardData, error) { return []*items.QuestRewardData{}, nil } func (mqa *MockItemQuestAdapter) IsQuestItem(itemID int32) bool { return false // No quest items for now } // MockItemBrokerAdapter provides mock broker functionality type MockItemBrokerAdapter struct{} func (mba *MockItemBrokerAdapter) SearchItems(criteria *items.ItemSearchCriteria) ([]*items.Item, error) { return []*items.Item{}, nil } func (mba *MockItemBrokerAdapter) ListItem(playerID uint32, item *items.Item, price int64) error { return fmt.Errorf("broker not implemented") } func (mba *MockItemBrokerAdapter) BuyItem(playerID uint32, itemID int32, sellerID uint32) error { return fmt.Errorf("broker not implemented") } func (mba *MockItemBrokerAdapter) GetItemPrice(itemID int32) (int64, error) { return 0, fmt.Errorf("broker not implemented") } // MockItemCraftingAdapter provides mock crafting functionality type MockItemCraftingAdapter struct{} func (mca *MockItemCraftingAdapter) CanCraftItem(playerID uint32, itemID int32) bool { return false // No crafting for now } func (mca *MockItemCraftingAdapter) GetCraftingRequirements(itemID int32) ([]items.CraftingRequirement, error) { return []items.CraftingRequirement{}, nil } func (mca *MockItemCraftingAdapter) CraftItem(playerID uint32, itemID int32, quality int8) (*items.Item, error) { return nil, fmt.Errorf("crafting not implemented") } // MockItemHousingAdapter provides mock housing functionality type MockItemHousingAdapter struct{} func (mha *MockItemHousingAdapter) CanPlaceItem(playerID uint32, houseID int32, item *items.Item) bool { return false // No housing for now } func (mha *MockItemHousingAdapter) PlaceItem(playerID uint32, houseID int32, item *items.Item, location items.HouseLocation) error { return fmt.Errorf("housing not implemented") } func (mha *MockItemHousingAdapter) RemoveItem(playerID uint32, houseID int32, itemID int32) error { return fmt.Errorf("housing not implemented") } func (mha *MockItemHousingAdapter) GetHouseItems(houseID int32) ([]*items.Item, error) { return []*items.Item{}, nil }