714 lines
20 KiB
Go
714 lines
20 KiB
Go
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
|
|
// Basic stats
|
|
mil.AddMappedItemStat(ItemStatStr, "strength")
|
|
mil.AddMappedItemStat(ItemStatSta, "stamina")
|
|
mil.AddMappedItemStat(ItemStatAgi, "agility")
|
|
mil.AddMappedItemStat(ItemStatWis, "wisdom")
|
|
mil.AddMappedItemStat(ItemStatInt, "intelligence")
|
|
|
|
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
|
|
// log.Printf("Added stat mapping: %s -> %d", lowerCaseName, id)
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// GetMappedStatCount returns the number of mapped stats (for debugging)
|
|
func (mil *MasterItemList) GetMappedStatCount() int {
|
|
mil.mutex.RLock()
|
|
defer mil.mutex.RUnlock()
|
|
return len(mil.mappedItemStatsStrings)
|
|
}
|
|
|
|
// 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
|
|
// Added item to master list
|
|
}
|
|
|
|
// 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, nil)
|
|
}
|
|
|
|
// CalculateItemBonusesWithEntity calculates the stat bonuses for an item with entity-specific modifiers
|
|
func (mil *MasterItemList) CalculateItemBonusesWithEntity(itemID int32, entity Entity) *ItemStatsValues {
|
|
item := mil.GetItem(itemID)
|
|
if item == nil {
|
|
return nil
|
|
}
|
|
|
|
return mil.CalculateItemBonusesFromItem(item, entity)
|
|
}
|
|
|
|
// CalculateItemBonusesFromItem calculates stat bonuses from an item instance
|
|
func (mil *MasterItemList) CalculateItemBonusesFromItem(item *Item, entity Entity) *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() {
|
|
// Master item list system initialized
|
|
}
|