add item support to world server, first pass
This commit is contained in:
parent
80e3bf39b4
commit
31dbfa0fc3
@ -81,6 +81,15 @@ const (
|
||||
OP_NPCSpellCastMsg
|
||||
OP_NPCMovementMsg
|
||||
|
||||
// Item system
|
||||
OP_ItemMoveMsg
|
||||
OP_ItemEquipMsg
|
||||
OP_ItemUnequipMsg
|
||||
OP_ItemPickupMsg
|
||||
OP_ItemDropMsg
|
||||
OP_ItemExamineMsg
|
||||
OP_ItemUpdateMsg
|
||||
|
||||
// EverQuest specific commands - Core
|
||||
OP_EqHearChatCmd
|
||||
OP_EqDisplayTextCmd
|
||||
@ -143,6 +152,13 @@ var OpcodeNames = map[InternalOpcode]string{
|
||||
OP_NPCInfoMsg: "OP_NPCInfoMsg",
|
||||
OP_NPCSpellCastMsg: "OP_NPCSpellCastMsg",
|
||||
OP_NPCMovementMsg: "OP_NPCMovementMsg",
|
||||
OP_ItemMoveMsg: "OP_ItemMoveMsg",
|
||||
OP_ItemEquipMsg: "OP_ItemEquipMsg",
|
||||
OP_ItemUnequipMsg: "OP_ItemUnequipMsg",
|
||||
OP_ItemPickupMsg: "OP_ItemPickupMsg",
|
||||
OP_ItemDropMsg: "OP_ItemDropMsg",
|
||||
OP_ItemExamineMsg: "OP_ItemExamineMsg",
|
||||
OP_ItemUpdateMsg: "OP_ItemUpdateMsg",
|
||||
OP_EqHearChatCmd: "OP_EqHearChatCmd",
|
||||
OP_EqDisplayTextCmd: "OP_EqDisplayTextCmd",
|
||||
OP_EqCreateGhostCmd: "OP_EqCreateGhostCmd",
|
||||
|
679
internal/world/item_manager.go
Normal file
679
internal/world/item_manager.go
Normal file
@ -0,0 +1,679 @@
|
||||
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]interface{} {
|
||||
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]interface{})
|
||||
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
|
||||
}
|
@ -273,6 +273,35 @@ func (nm *NPCManager) OnNPCKilled(npcID int32, killerCharacterID int32) {
|
||||
}
|
||||
}
|
||||
|
||||
// Generate and distribute loot
|
||||
if nm.world != nil && nm.world.itemMgr != nil {
|
||||
npcInfo := nm.GetNPCInfo(npcID)
|
||||
if npcInfo != nil {
|
||||
// Generate loot for this NPC kill
|
||||
loot, err := nm.world.itemMgr.GenerateLootForNPC(npcID, int16(npcInfo.Level))
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to generate loot for NPC %d: %v\n", npcID, err)
|
||||
} else if len(loot) > 0 {
|
||||
// Create loot drops in the world (for now, just create basic drops)
|
||||
// TODO: Get NPC position for proper loot positioning
|
||||
x, y, z := float32(100), float32(100), float32(50)
|
||||
zoneID := npcInfo.ZoneID
|
||||
if zoneID == 0 {
|
||||
zoneID = 1 // Default zone
|
||||
}
|
||||
|
||||
for _, item := range loot {
|
||||
err := nm.world.itemMgr.CreateWorldDrop(item.Details.ItemID, item.Details.Count, x, y, z, zoneID)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to create loot drop for item %d: %v\n", item.Details.ItemID, err)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("Generated %d loot items for NPC %d kill\n", len(loot), npcID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("NPC %d killed by character %d\n", npcID, killerCharacterID)
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,16 @@ func (w *World) RegisterPacketHandlers() {
|
||||
packets.RegisterGlobalHandler(packets.OP_NPCSpellCastMsg, w.HandleNPCSpellCast)
|
||||
packets.RegisterGlobalHandler(packets.OP_NPCMovementMsg, w.HandleNPCMovement)
|
||||
|
||||
fmt.Printf("Registered %d packet handlers\n", 21)
|
||||
// Item system
|
||||
packets.RegisterGlobalHandler(packets.OP_ItemMoveMsg, w.HandleItemMove)
|
||||
packets.RegisterGlobalHandler(packets.OP_ItemEquipMsg, w.HandleItemEquip)
|
||||
packets.RegisterGlobalHandler(packets.OP_ItemUnequipMsg, w.HandleItemUnequip)
|
||||
packets.RegisterGlobalHandler(packets.OP_ItemPickupMsg, w.HandleItemPickup)
|
||||
packets.RegisterGlobalHandler(packets.OP_ItemDropMsg, w.HandleItemDrop)
|
||||
packets.RegisterGlobalHandler(packets.OP_ItemExamineMsg, w.HandleItemExamine)
|
||||
packets.RegisterGlobalHandler(packets.OP_ItemUpdateMsg, w.HandleItemUpdate)
|
||||
|
||||
fmt.Printf("Registered %d packet handlers\n", 28)
|
||||
}
|
||||
|
||||
// HandleDoneLoadingZoneResources handles when client finishes loading zone resources
|
||||
@ -702,4 +711,268 @@ func (w *World) CreatePacketContext(client *Client) *packets.PacketContext {
|
||||
World: &WorldServerAdapter{world: w},
|
||||
Database: &WorldDatabaseAdapter{world: w},
|
||||
}
|
||||
}
|
||||
|
||||
// Item Packet Handlers
|
||||
|
||||
// HandleItemMove handles item movement within player inventory
|
||||
func (w *World) HandleItemMove(ctx *packets.PacketContext, packet *packets.PacketData) error {
|
||||
fmt.Printf("Client %s sent item move packet\n", ctx.Client.GetCharacterName())
|
||||
|
||||
client := w.clients.GetByCharacterID(ctx.Client.GetCharacterID())
|
||||
if client != nil {
|
||||
client.UpdateActivity()
|
||||
|
||||
// TODO: Parse move data from packet (fromBagID, fromSlot, toBagID, toSlot)
|
||||
// For now, use placeholder values for testing
|
||||
fromBagID := int32(0)
|
||||
fromSlot := int16(0)
|
||||
toBagID := int32(0)
|
||||
toSlot := int16(1)
|
||||
|
||||
if w.itemMgr != nil {
|
||||
err := w.itemMgr.MoveItem(uint32(ctx.Client.GetCharacterID()),
|
||||
fromBagID, fromSlot, toBagID, toSlot)
|
||||
if err != nil {
|
||||
client.SendSimpleMessage(fmt.Sprintf("Item move failed: %v", err))
|
||||
} else {
|
||||
client.SendSimpleMessage("Item moved successfully")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HandleItemEquip handles item equipping
|
||||
func (w *World) HandleItemEquip(ctx *packets.PacketContext, packet *packets.PacketData) error {
|
||||
fmt.Printf("Client %s sent item equip packet\n", ctx.Client.GetCharacterName())
|
||||
|
||||
client := w.clients.GetByCharacterID(ctx.Client.GetCharacterID())
|
||||
if client != nil {
|
||||
client.UpdateActivity()
|
||||
|
||||
// TODO: Parse equip data from packet (uniqueID, slot)
|
||||
// For now, use placeholder values for testing
|
||||
uniqueID := int32(1001)
|
||||
slot := int8(0) // Primary hand
|
||||
|
||||
if w.itemMgr != nil {
|
||||
err := w.itemMgr.EquipItem(uint32(ctx.Client.GetCharacterID()), uniqueID, slot)
|
||||
if err != nil {
|
||||
client.SendSimpleMessage(fmt.Sprintf("Item equip failed: %v", err))
|
||||
} else {
|
||||
client.SendSimpleMessage(fmt.Sprintf("Item equipped to slot %d", slot))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HandleItemUnequip handles item unequipping
|
||||
func (w *World) HandleItemUnequip(ctx *packets.PacketContext, packet *packets.PacketData) error {
|
||||
fmt.Printf("Client %s sent item unequip packet\n", ctx.Client.GetCharacterName())
|
||||
|
||||
client := w.clients.GetByCharacterID(ctx.Client.GetCharacterID())
|
||||
if client != nil {
|
||||
client.UpdateActivity()
|
||||
|
||||
// TODO: Parse unequip data from packet (slot)
|
||||
// For now, use placeholder values for testing
|
||||
slot := int8(0) // Primary hand
|
||||
|
||||
if w.itemMgr != nil {
|
||||
err := w.itemMgr.UnequipItem(uint32(ctx.Client.GetCharacterID()), slot)
|
||||
if err != nil {
|
||||
client.SendSimpleMessage(fmt.Sprintf("Item unequip failed: %v", err))
|
||||
} else {
|
||||
client.SendSimpleMessage(fmt.Sprintf("Item unequipped from slot %d", slot))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HandleItemPickup handles picking up world drops
|
||||
func (w *World) HandleItemPickup(ctx *packets.PacketContext, packet *packets.PacketData) error {
|
||||
fmt.Printf("Client %s sent item pickup packet\n", ctx.Client.GetCharacterName())
|
||||
|
||||
client := w.clients.GetByCharacterID(ctx.Client.GetCharacterID())
|
||||
if client != nil {
|
||||
client.UpdateActivity()
|
||||
|
||||
// TODO: Parse pickup data from packet (itemUniqueID)
|
||||
// For now, use placeholder values for testing
|
||||
itemUniqueID := int32(5001)
|
||||
zoneID := int32(1) // Current zone
|
||||
|
||||
if w.itemMgr != nil {
|
||||
err := w.itemMgr.PickupWorldDrop(uint32(ctx.Client.GetCharacterID()), itemUniqueID, zoneID)
|
||||
if err != nil {
|
||||
client.SendSimpleMessage(fmt.Sprintf("Item pickup failed: %v", err))
|
||||
} else {
|
||||
client.SendSimpleMessage("Item picked up")
|
||||
// TODO: Remove item from world display for other players
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HandleItemDrop handles dropping items to the world
|
||||
func (w *World) HandleItemDrop(ctx *packets.PacketContext, packet *packets.PacketData) error {
|
||||
fmt.Printf("Client %s sent item drop packet\n", ctx.Client.GetCharacterName())
|
||||
|
||||
client := w.clients.GetByCharacterID(ctx.Client.GetCharacterID())
|
||||
if client != nil {
|
||||
client.UpdateActivity()
|
||||
|
||||
// TODO: Parse drop data from packet (uniqueID, quantity, x, y, z)
|
||||
// For now, use placeholder values for testing
|
||||
itemID := int32(1001)
|
||||
quantity := int16(1)
|
||||
x, y, z := float32(100), float32(100), float32(50)
|
||||
zoneID := int32(1)
|
||||
|
||||
if w.itemMgr != nil {
|
||||
// First remove from player inventory (would need to look up by uniqueID)
|
||||
// TODO: Get uniqueID from packet and remove from player
|
||||
|
||||
// Create world drop
|
||||
err := w.itemMgr.CreateWorldDrop(itemID, quantity, x, y, z, zoneID)
|
||||
if err != nil {
|
||||
client.SendSimpleMessage(fmt.Sprintf("Item drop failed: %v", err))
|
||||
} else {
|
||||
client.SendSimpleMessage("Item dropped")
|
||||
// TODO: Show item to other players in range
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HandleItemExamine handles item examination requests
|
||||
func (w *World) HandleItemExamine(ctx *packets.PacketContext, packet *packets.PacketData) error {
|
||||
fmt.Printf("Client %s sent item examine packet\n", ctx.Client.GetCharacterName())
|
||||
|
||||
client := w.clients.GetByCharacterID(ctx.Client.GetCharacterID())
|
||||
if client != nil {
|
||||
client.UpdateActivity()
|
||||
|
||||
// TODO: Parse examine data from packet (uniqueID or itemID)
|
||||
// For now, use placeholder values for testing
|
||||
itemID := int32(1001)
|
||||
|
||||
if w.itemMgr != nil {
|
||||
itemTemplate := w.itemMgr.GetItemTemplate(itemID)
|
||||
if itemTemplate == nil {
|
||||
client.SendSimpleMessage("Item not found")
|
||||
} else {
|
||||
// Send item details to client
|
||||
w.SendItemDetails(client, itemTemplate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HandleItemUpdate handles item update notifications
|
||||
func (w *World) HandleItemUpdate(ctx *packets.PacketContext, packet *packets.PacketData) error {
|
||||
fmt.Printf("Client %s sent item update packet\n", ctx.Client.GetCharacterName())
|
||||
|
||||
client := w.clients.GetByCharacterID(ctx.Client.GetCharacterID())
|
||||
if client != nil {
|
||||
client.UpdateActivity()
|
||||
|
||||
// TODO: Parse update data from packet
|
||||
// This might be triggered when client needs updated item information
|
||||
|
||||
if w.itemMgr != nil {
|
||||
// Send updated inventory to client
|
||||
w.SendPlayerInventory(client)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendItemDetails sends detailed item information to a client
|
||||
func (w *World) SendItemDetails(client *Client, item interface{}) {
|
||||
if w.itemMgr == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Implement actual item detail packet building
|
||||
// This would include stats, description, level requirements, etc.
|
||||
|
||||
fmt.Printf("Sending item details to %s\n", client.CharacterName)
|
||||
|
||||
// Placeholder - send basic item info as chat message
|
||||
client.SendSimpleMessage("Item Details: [Item information would be displayed here]")
|
||||
}
|
||||
|
||||
// SendPlayerInventory sends complete inventory to a client
|
||||
func (w *World) SendPlayerInventory(client *Client) {
|
||||
if w.itemMgr == nil {
|
||||
return
|
||||
}
|
||||
|
||||
playerID := uint32(client.CharacterID)
|
||||
|
||||
// Get player inventory and equipment
|
||||
inventory, err := w.itemMgr.GetPlayerInventory(playerID)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to get inventory for player %d: %v\n", playerID, err)
|
||||
return
|
||||
}
|
||||
|
||||
equipment, err := w.itemMgr.GetPlayerEquipment(playerID, 0) // Base equipment
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to get equipment for player %d: %v\n", playerID, err)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Build and send inventory packet
|
||||
fmt.Printf("Sending inventory to %s: %d inventory items, %d equipped items\n",
|
||||
client.CharacterName, inventory.GetNumberOfItems(), equipment.GetNumberOfItems())
|
||||
|
||||
// Placeholder - send summary as chat message
|
||||
client.SendSimpleMessage(fmt.Sprintf("Inventory Update: %d items in inventory, %d equipped",
|
||||
inventory.GetNumberOfItems(), equipment.GetNumberOfItems()))
|
||||
}
|
||||
|
||||
// SendItemUpdate sends item update to client
|
||||
func (w *World) SendItemUpdate(client *Client, updateType string, itemData map[string]interface{}) {
|
||||
if w.itemMgr == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Build and send item update packet
|
||||
fmt.Printf("Sending item update to %s: %s - %v\n",
|
||||
client.CharacterName, updateType, itemData)
|
||||
|
||||
// Placeholder - send update as chat message
|
||||
client.SendSimpleMessage(fmt.Sprintf("Item Update: %s", updateType))
|
||||
}
|
||||
|
||||
// BroadcastItemUpdate broadcasts item updates to nearby players
|
||||
func (w *World) BroadcastItemUpdate(sourcePlayerID uint32, updateType string, itemData map[string]interface{}) {
|
||||
// TODO: Implement item update broadcasting (for things like equipment changes visible to others)
|
||||
|
||||
fmt.Printf("Broadcasting item update from player %d: %s - %v\n",
|
||||
sourcePlayerID, updateType, itemData)
|
||||
|
||||
// Send to players in range (placeholder)
|
||||
clients := w.clients.GetAll()
|
||||
for _, client := range clients {
|
||||
if client.CurrentZone != nil && client.CharacterID != int32(sourcePlayerID) {
|
||||
// TODO: Check if client is in range
|
||||
client.SendSimpleMessage(fmt.Sprintf("Player item update: %s", updateType))
|
||||
}
|
||||
}
|
||||
}
|
@ -44,9 +44,11 @@ type World struct {
|
||||
// NPC system
|
||||
npcMgr *NPCManager
|
||||
|
||||
// Item system
|
||||
itemMgr *ItemManager
|
||||
|
||||
// Master lists (singletons)
|
||||
masterSpells interface{} // TODO: implement spell manager
|
||||
masterItems interface{} // TODO: implement item manager
|
||||
masterQuests interface{} // TODO: implement quest manager
|
||||
masterSkills interface{} // TODO: implement skill manager
|
||||
masterFactions interface{} // TODO: implement faction manager
|
||||
@ -159,6 +161,9 @@ func NewWorld(config *WorldConfig) (*World, error) {
|
||||
// Initialize NPC manager
|
||||
npcMgr := NewNPCManager(db)
|
||||
|
||||
// Initialize item manager
|
||||
itemMgr := NewItemManager(db)
|
||||
|
||||
// Create context
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
@ -169,6 +174,7 @@ func NewWorld(config *WorldConfig) (*World, error) {
|
||||
achievementMgr: achievementMgr,
|
||||
titleMgr: titleMgr,
|
||||
npcMgr: npcMgr,
|
||||
itemMgr: itemMgr,
|
||||
config: config,
|
||||
startTime: time.Now(),
|
||||
worldTime: &WorldTime{Year: 3721, Month: 1, Day: 1, Hour: 12, Minute: 0},
|
||||
@ -184,6 +190,7 @@ func NewWorld(config *WorldConfig) (*World, error) {
|
||||
// Set world references for cross-system communication
|
||||
achievementMgr.SetWorld(w)
|
||||
npcMgr.SetWorld(w)
|
||||
itemMgr.SetWorld(w)
|
||||
|
||||
// Load server data from database
|
||||
if err := w.loadServerData(); err != nil {
|
||||
@ -436,6 +443,12 @@ func (w *World) loadServerData() error {
|
||||
// Don't fail startup if NPCs don't load - server can still run
|
||||
}
|
||||
|
||||
// Load items
|
||||
if err := w.itemMgr.LoadItems(); err != nil {
|
||||
fmt.Printf("Warning: Failed to load items: %v\n", err)
|
||||
// Don't fail startup if items don't load - server can still run
|
||||
}
|
||||
|
||||
// Setup title and achievement integration
|
||||
w.setupTitleAchievementIntegration()
|
||||
|
||||
@ -605,6 +618,13 @@ func (w *World) loadSampleOpcodeMappings() {
|
||||
"OP_NPCInfoMsg": 0x0097,
|
||||
"OP_NPCSpellCastMsg": 0x0098,
|
||||
"OP_NPCMovementMsg": 0x0099,
|
||||
"OP_ItemMoveMsg": 0x00A0,
|
||||
"OP_ItemEquipMsg": 0x00A1,
|
||||
"OP_ItemUnequipMsg": 0x00A2,
|
||||
"OP_ItemPickupMsg": 0x00A3,
|
||||
"OP_ItemDropMsg": 0x00A4,
|
||||
"OP_ItemExamineMsg": 0x00A5,
|
||||
"OP_ItemUpdateMsg": 0x00A6,
|
||||
"OP_EqHearChatCmd": 0x1000,
|
||||
"OP_EqDisplayTextCmd": 0x1001,
|
||||
"OP_EqCreateGhostCmd": 0x1002,
|
||||
|
Loading…
x
Reference in New Issue
Block a user