package items import ( "fmt" "log" "strings" "time" ) // NewMasterItemList creates a new master item list func NewMasterItemList() *MasterItemList { mil := &MasterItemList{ items: make(map[int32]*Item), mappedItemStatsStrings: make(map[string]int32), mappedItemStatTypeIDs: make(map[int32]string), brokerItemMap: make(map[*VersionRange]map[int64]int64), } // Initialize mapped item stats mil.initializeMappedStats() return mil } // initializeMappedStats initializes the mapped item stats func (mil *MasterItemList) initializeMappedStats() { // Add all the mapped item stats as in the C++ constructor mil.AddMappedItemStat(ItemStatAdorning, "adorning") mil.AddMappedItemStat(ItemStatAggression, "aggression") mil.AddMappedItemStat(ItemStatArtificing, "artificing") mil.AddMappedItemStat(ItemStatArtistry, "artistry") mil.AddMappedItemStat(ItemStatChemistry, "chemistry") mil.AddMappedItemStat(ItemStatCrushing, "crushing") mil.AddMappedItemStat(ItemStatDefense, "defense") mil.AddMappedItemStat(ItemStatDeflection, "deflection") mil.AddMappedItemStat(ItemStatDisruption, "disruption") mil.AddMappedItemStat(ItemStatFishing, "fishing") mil.AddMappedItemStat(ItemStatFletching, "fletching") mil.AddMappedItemStat(ItemStatFocus, "focus") mil.AddMappedItemStat(ItemStatForesting, "foresting") mil.AddMappedItemStat(ItemStatGathering, "gathering") mil.AddMappedItemStat(ItemStatMetalShaping, "metal shaping") mil.AddMappedItemStat(ItemStatMetalworking, "metalworking") mil.AddMappedItemStat(ItemStatMining, "mining") mil.AddMappedItemStat(ItemStatMinistration, "ministration") mil.AddMappedItemStat(ItemStatOrdination, "ordination") mil.AddMappedItemStat(ItemStatParry, "parry") mil.AddMappedItemStat(ItemStatPiercing, "piercing") mil.AddMappedItemStat(ItemStatRanged, "ranged") mil.AddMappedItemStat(ItemStatSafeFall, "safe fall") mil.AddMappedItemStat(ItemStatScribing, "scribing") mil.AddMappedItemStat(ItemStatSculpting, "sculpting") mil.AddMappedItemStat(ItemStatSlashing, "slashing") mil.AddMappedItemStat(ItemStatSubjugation, "subjugation") mil.AddMappedItemStat(ItemStatSwimming, "swimming") mil.AddMappedItemStat(ItemStatTailoring, "tailoring") mil.AddMappedItemStat(ItemStatTinkering, "tinkering") mil.AddMappedItemStat(ItemStatTransmuting, "transmuting") mil.AddMappedItemStat(ItemStatTrapping, "trapping") mil.AddMappedItemStat(ItemStatWeaponSkills, "weapon skills") mil.AddMappedItemStat(ItemStatPowerCostReduction, "power cost reduction") mil.AddMappedItemStat(ItemStatSpellAvoidance, "spell avoidance") } // AddMappedItemStat adds a mapping between stat ID and name func (mil *MasterItemList) AddMappedItemStat(id int32, lowerCaseName string) { mil.mutex.Lock() defer mil.mutex.Unlock() mil.mappedItemStatsStrings[lowerCaseName] = id mil.mappedItemStatTypeIDs[id] = lowerCaseName } // GetItemStatIDByName gets the stat ID by name func (mil *MasterItemList) GetItemStatIDByName(name string) int32 { mil.mutex.RLock() defer mil.mutex.RUnlock() lowerName := strings.ToLower(name) if id, exists := mil.mappedItemStatsStrings[lowerName]; exists { return id } return 0 } // GetItemStatNameByID gets the stat name by ID func (mil *MasterItemList) GetItemStatNameByID(id int32) string { mil.mutex.RLock() defer mil.mutex.RUnlock() if name, exists := mil.mappedItemStatTypeIDs[id]; exists { return name } return "" } // AddItem adds an item to the master list func (mil *MasterItemList) AddItem(item *Item) { if item == nil { return } mil.mutex.Lock() defer mil.mutex.Unlock() mil.items[item.Details.ItemID] = item log.Printf("Added item %d (%s) to master list", item.Details.ItemID, item.Name) } // GetItem retrieves an item by ID func (mil *MasterItemList) GetItem(itemID int32) *Item { mil.mutex.RLock() defer mil.mutex.RUnlock() if item, exists := mil.items[itemID]; exists { return item.Copy() // Return a copy to prevent external modifications } return nil } // GetItemByName retrieves an item by name (case-insensitive) func (mil *MasterItemList) GetItemByName(name string) *Item { mil.mutex.RLock() defer mil.mutex.RUnlock() lowerName := strings.ToLower(name) for _, item := range mil.items { if strings.ToLower(item.Name) == lowerName { return item.Copy() } } return nil } // IsBag checks if an item ID represents a bag func (mil *MasterItemList) IsBag(itemID int32) bool { item := mil.GetItem(itemID) if item == nil { return false } return item.IsBag() } // RemoveAll removes all items from the master list func (mil *MasterItemList) RemoveAll() { mil.mutex.Lock() defer mil.mutex.Unlock() count := len(mil.items) mil.items = make(map[int32]*Item) log.Printf("Removed %d items from master list", count) } // GetItemCount returns the total number of items func (mil *MasterItemList) GetItemCount() int { mil.mutex.RLock() defer mil.mutex.RUnlock() return len(mil.items) } // CalculateItemBonuses calculates the stat bonuses for an item func (mil *MasterItemList) CalculateItemBonuses(itemID int32) *ItemStatsValues { item := mil.GetItem(itemID) if item == nil { return nil } return mil.CalculateItemBonusesFromItem(item) } // CalculateItemBonusesFromItem calculates stat bonuses from an item instance func (mil *MasterItemList) CalculateItemBonusesFromItem(item *Item) *ItemStatsValues { if item == nil { return nil } item.mutex.RLock() defer item.mutex.RUnlock() values := &ItemStatsValues{} // Process all item stats for _, stat := range item.ItemStats { switch stat.StatType { case ItemStatStr: values.Str += int16(stat.Value) case ItemStatSta: values.Sta += int16(stat.Value) case ItemStatAgi: values.Agi += int16(stat.Value) case ItemStatWis: values.Wis += int16(stat.Value) case ItemStatInt: values.Int += int16(stat.Value) case ItemStatVsSlash: values.VsSlash += int16(stat.Value) case ItemStatVsCrush: values.VsCrush += int16(stat.Value) case ItemStatVsPierce: values.VsPierce += int16(stat.Value) case ItemStatVsPhysical: values.VsPhysical += int16(stat.Value) case ItemStatVsHeat: values.VsHeat += int16(stat.Value) case ItemStatVsCold: values.VsCold += int16(stat.Value) case ItemStatVsMagic: values.VsMagic += int16(stat.Value) case ItemStatVsMental: values.VsMental += int16(stat.Value) case ItemStatVsDivine: values.VsDivine += int16(stat.Value) case ItemStatVsDisease: values.VsDisease += int16(stat.Value) case ItemStatVsPoison: values.VsPoison += int16(stat.Value) case ItemStatHealth: values.Health += int16(stat.Value) case ItemStatPower: values.Power += int16(stat.Value) case ItemStatConcentration: values.Concentration += int8(stat.Value) case ItemStatAbilityModifier: values.AbilityModifier += int16(stat.Value) case ItemStatCriticalMitigation: values.CriticalMitigation += int16(stat.Value) case ItemStatExtraShieldBlockChance: values.ExtraShieldBlockChance += int16(stat.Value) case ItemStatBeneficialCritChance: values.BeneficialCritChance += int16(stat.Value) case ItemStatCritBonus: values.CritBonus += int16(stat.Value) case ItemStatPotency: values.Potency += int16(stat.Value) case ItemStatHateGainMod: values.HateGainMod += int16(stat.Value) case ItemStatAbilityReuseSpeed: values.AbilityReuseSpeed += int16(stat.Value) case ItemStatAbilityCastingSpeed: values.AbilityCastingSpeed += int16(stat.Value) case ItemStatAbilityRecoverySpeed: values.AbilityRecoverySpeed += int16(stat.Value) case ItemStatSpellReuseSpeed: values.SpellReuseSpeed += int16(stat.Value) case ItemStatSpellMultiAttackChance: values.SpellMultiAttackChance += int16(stat.Value) case ItemStatDPS: values.DPS += int16(stat.Value) case ItemStatAttackSpeed: values.AttackSpeed += int16(stat.Value) case ItemStatMultiattackChance: values.MultiAttackChance += int16(stat.Value) case ItemStatFlurry: values.Flurry += int16(stat.Value) case ItemStatAEAutoattackChance: values.AEAutoattackChance += int16(stat.Value) case ItemStatStrikethrough: values.Strikethrough += int16(stat.Value) case ItemStatAccuracy: values.Accuracy += int16(stat.Value) case ItemStatOffensiveSpeed: values.OffensiveSpeed += int16(stat.Value) case ItemStatUncontestedParry: values.UncontestedParry += stat.Value case ItemStatUncontestedBlock: values.UncontestedBlock += stat.Value case ItemStatUncontestedDodge: values.UncontestedDodge += stat.Value case ItemStatUncontestedRiposte: values.UncontestedRiposte += stat.Value case ItemStatSizeMod: values.SizeMod += stat.Value } } return values } // Broker-related methods // AddBrokerItemMapRange adds a broker item mapping range func (mil *MasterItemList) AddBrokerItemMapRange(minVersion int32, maxVersion int32, clientBitmask int64, serverBitmask int64) { mil.mutex.Lock() defer mil.mutex.Unlock() // Find existing range var targetRange *VersionRange for versionRange := range mil.brokerItemMap { if versionRange.MinVersion == minVersion && versionRange.MaxVersion == maxVersion { targetRange = versionRange break } } // Create new range if not found if targetRange == nil { targetRange = &VersionRange{ MinVersion: minVersion, MaxVersion: maxVersion, } mil.brokerItemMap[targetRange] = make(map[int64]int64) } mil.brokerItemMap[targetRange][clientBitmask] = serverBitmask } // FindBrokerItemMapVersionRange finds a broker item map by version range func (mil *MasterItemList) FindBrokerItemMapVersionRange(minVersion int32, maxVersion int32) map[int64]int64 { mil.mutex.RLock() defer mil.mutex.RUnlock() for versionRange, mapping := range mil.brokerItemMap { // Check if min and max version are both in range if versionRange.MinVersion <= minVersion && maxVersion <= versionRange.MaxVersion { return mapping } // Check if the min version is in range, but max range is 0 if versionRange.MinVersion <= minVersion && versionRange.MaxVersion == 0 { return mapping } // Check if min version is 0 and max_version has a cap if versionRange.MinVersion == 0 && maxVersion <= versionRange.MaxVersion { return mapping } } return nil } // FindBrokerItemMapByVersion finds a broker item map by specific version func (mil *MasterItemList) FindBrokerItemMapByVersion(version int32) map[int64]int64 { mil.mutex.RLock() defer mil.mutex.RUnlock() var defaultMapping map[int64]int64 for versionRange, mapping := range mil.brokerItemMap { // Check for default range (0,0) if versionRange.MinVersion == 0 && versionRange.MaxVersion == 0 { defaultMapping = mapping continue } // Check if version is in range if version >= versionRange.MinVersion && version <= versionRange.MaxVersion { return mapping } } return defaultMapping } // ShouldAddItemBrokerType checks if an item should be added to broker by type func (mil *MasterItemList) ShouldAddItemBrokerType(item *Item, itemType int64) bool { if item == nil { return false } switch itemType { case ItemBrokerTypeAdornment: return item.IsAdornment() case ItemBrokerTypeAmmo: return item.IsAmmo() case ItemBrokerTypeAttuneable: return item.CheckFlag(Attuneable) case ItemBrokerTypeBag: return item.IsBag() case ItemBrokerTypeBauble: return item.IsBauble() case ItemBrokerTypeBook: return item.IsBook() case ItemBrokerTypeChainarmor: return item.IsChainArmor() case ItemBrokerTypeCloak: return item.IsCloak() case ItemBrokerTypeClotharmor: return item.IsClothArmor() case ItemBrokerTypeCollectable: return item.IsCollectable() case ItemBrokerTypeCrushweapon: return item.IsCrushWeapon() case ItemBrokerTypeDrink: return item.IsFoodDrink() case ItemBrokerTypeFood: return item.IsFoodFood() case ItemBrokerTypeHouseitem: return item.IsHouseItem() case ItemBrokerTypeJewelry: return item.IsJewelry() case ItemBrokerTypeLeatherarmor: return item.IsLeatherArmor() case ItemBrokerTypeLore: return item.CheckFlag(Lore) case ItemBrokerTypeMisc: return item.IsMisc() case ItemBrokerTypePierceweapon: return item.IsPierceWeapon() case ItemBrokerTypePlatearmor: return item.IsPlateArmor() case ItemBrokerTypePoison: return item.IsPoison() case ItemBrokerTypePotion: return item.IsPotion() case ItemBrokerTypeRecipebook: return item.IsRecipeBook() case ItemBrokerTypeSalesdisplay: return item.IsSalesDisplay() case ItemBrokerTypeShield: return item.IsShield() case ItemBrokerTypeSlashweapon: return item.IsSlashWeapon() case ItemBrokerTypeSpellscroll: return item.IsSpellScroll() case ItemBrokerTypeTinkered: return item.IsTinkered() case ItemBrokerTypeTradeskill: return item.IsTradeskill() } return false } // ShouldAddItemBrokerSlot checks if an item should be added to broker by slot func (mil *MasterItemList) ShouldAddItemBrokerSlot(item *Item, slotType int64) bool { if item == nil { return false } switch slotType { case ItemBrokerSlotPrimary: return item.HasSlot(EQ2PrimarySlot, -1) case ItemBrokerSlotPrimary2H: return item.HasSlot(EQ2PrimarySlot, -1) || item.HasSlot(EQ2SecondarySlot, -1) case ItemBrokerSlotSecondary: return item.HasSlot(EQ2SecondarySlot, -1) case ItemBrokerSlotHead: return item.HasSlot(EQ2HeadSlot, -1) case ItemBrokerSlotChest: return item.HasSlot(EQ2ChestSlot, -1) case ItemBrokerSlotShoulders: return item.HasSlot(EQ2ShouldersSlot, -1) case ItemBrokerSlotForearms: return item.HasSlot(EQ2ForearmsSlot, -1) case ItemBrokerSlotHands: return item.HasSlot(EQ2HandsSlot, -1) case ItemBrokerSlotLegs: return item.HasSlot(EQ2LegsSlot, -1) case ItemBrokerSlotFeet: return item.HasSlot(EQ2FeetSlot, -1) case ItemBrokerSlotRing: return item.HasSlot(EQ2LRingSlot, EQ2RRingSlot) case ItemBrokerSlotEars: return item.HasSlot(EQ2EarsSlot1, EQ2EarsSlot2) case ItemBrokerSlotNeck: return item.HasSlot(EQ2NeckSlot, -1) case ItemBrokerSlotWrist: return item.HasSlot(EQ2LWristSlot, EQ2RWristSlot) case ItemBrokerSlotRangeWeapon: return item.HasSlot(EQ2RangeSlot, -1) case ItemBrokerSlotAmmo: return item.HasSlot(EQ2AmmoSlot, -1) case ItemBrokerSlotWaist: return item.HasSlot(EQ2WaistSlot, -1) case ItemBrokerSlotCloak: return item.HasSlot(EQ2CloakSlot, -1) case ItemBrokerSlotCharm: return item.HasSlot(EQ2CharmSlot1, EQ2CharmSlot2) case ItemBrokerSlotFood: return item.HasSlot(EQ2FoodSlot, -1) case ItemBrokerSlotDrink: return item.HasSlot(EQ2DrinkSlot, -1) } return false } // ShouldAddItemBrokerStat checks if an item should be added to broker by stat func (mil *MasterItemList) ShouldAddItemBrokerStat(item *Item, statType int64) bool { if item == nil { return false } // Check if the item has the requested stat type for _, stat := range item.ItemStats { switch statType { case ItemBrokerStatTypeStr: if stat.StatType == ItemStatStr { return true } case ItemBrokerStatTypeSta: if stat.StatType == ItemStatSta { return true } case ItemBrokerStatTypeAgi: if stat.StatType == ItemStatAgi { return true } case ItemBrokerStatTypeWis: if stat.StatType == ItemStatWis { return true } case ItemBrokerStatTypeInt: if stat.StatType == ItemStatInt { return true } case ItemBrokerStatTypeHealth: if stat.StatType == ItemStatHealth { return true } case ItemBrokerStatTypePower: if stat.StatType == ItemStatPower { return true } case ItemBrokerStatTypePotency: if stat.StatType == ItemStatPotency { return true } case ItemBrokerStatTypeCritical: if stat.StatType == ItemStatMeleeCritChance || stat.StatType == ItemStatBeneficialCritChance { return true } case ItemBrokerStatTypeAttackspeed: if stat.StatType == ItemStatAttackSpeed { return true } case ItemBrokerStatTypeDPS: if stat.StatType == ItemStatDPS { return true } // Add more stat type checks as needed } } return false } // GetItems searches for items based on criteria func (mil *MasterItemList) GetItems(criteria *ItemSearchCriteria) []*Item { if criteria == nil { return nil } mil.mutex.RLock() defer mil.mutex.RUnlock() var results []*Item for _, item := range mil.items { if mil.matchesCriteria(item, criteria) { results = append(results, item.Copy()) } } return results } // matchesCriteria checks if an item matches the search criteria func (mil *MasterItemList) matchesCriteria(item *Item, criteria *ItemSearchCriteria) bool { // Name matching if criteria.Name != "" { if !strings.Contains(strings.ToLower(item.Name), strings.ToLower(criteria.Name)) { return false } } // Price range if criteria.MinPrice > 0 && item.BrokerPrice < criteria.MinPrice { return false } if criteria.MaxPrice > 0 && item.BrokerPrice > criteria.MaxPrice { return false } // Tier range if criteria.MinTier > 0 && item.Details.Tier < criteria.MinTier { return false } if criteria.MaxTier > 0 && item.Details.Tier > criteria.MaxTier { return false } // Level range if criteria.MinLevel > 0 && item.Details.RecommendedLevel < criteria.MinLevel { return false } if criteria.MaxLevel > 0 && item.Details.RecommendedLevel > criteria.MaxLevel { return false } // Item type matching if criteria.ItemType != 0 { if !mil.ShouldAddItemBrokerType(item, criteria.ItemType) { return false } } // Location type matching (slot compatibility) if criteria.LocationType != 0 { if !mil.ShouldAddItemBrokerSlot(item, criteria.LocationType) { return false } } // Broker type matching (stat requirements) if criteria.BrokerType != 0 { if !mil.ShouldAddItemBrokerStat(item, criteria.BrokerType) { return false } } // Seller matching if criteria.Seller != "" { if !strings.Contains(strings.ToLower(item.SellerName), strings.ToLower(criteria.Seller)) { return false } } // Adornment matching if criteria.Adornment != "" { if !strings.Contains(strings.ToLower(item.Adornment), strings.ToLower(criteria.Adornment)) { return false } } return true } // GetStats returns statistics about the master item list func (mil *MasterItemList) GetStats() *ItemManagerStats { mil.mutex.RLock() defer mil.mutex.RUnlock() stats := &ItemManagerStats{ TotalItems: int32(len(mil.items)), ItemsByType: make(map[int8]int32), ItemsByTier: make(map[int8]int32), LastUpdate: time.Now(), } // Count items by type and tier for _, item := range mil.items { stats.ItemsByType[item.GenericInfo.ItemType]++ stats.ItemsByTier[item.Details.Tier]++ } return stats } // Validate validates the master item list func (mil *MasterItemList) Validate() *ItemValidationResult { mil.mutex.RLock() defer mil.mutex.RUnlock() result := &ItemValidationResult{Valid: true} for itemID, item := range mil.items { itemResult := item.Validate() if !itemResult.Valid { result.Valid = false for _, err := range itemResult.Errors { result.Errors = append(result.Errors, fmt.Sprintf("Item %d: %s", itemID, err)) } } } return result } // Size returns the number of items in the master list func (mil *MasterItemList) Size() int { return mil.GetItemCount() } // Clear removes all items from the master list func (mil *MasterItemList) Clear() { mil.RemoveAll() } func init() { log.Printf("Master item list system initialized") }