package items import ( "fmt" "log" "strconv" "strings" "sync" "time" ) // NewItem creates a new item instance func NewItem() *Item { return &Item{ Details: ItemCore{ UniqueID: NextUniqueID(), Count: 1, }, GenericInfo: GenericInfo{ Condition: DefaultItemCondition, }, GroupedCharIDs: make(map[int32]bool), Created: time.Now(), } } // NewItemFromTemplate creates a new item from an existing item template func NewItemFromTemplate(template *Item) *Item { if template == nil { return NewItem() } item := &Item{ // Copy basic information LowerName: template.LowerName, Name: template.Name, Description: template.Description, StackCount: template.StackCount, SellPrice: template.SellPrice, SellStatus: template.SellStatus, MaxSellValue: template.MaxSellValue, // Copy metadata WeaponType: template.WeaponType, SpellID: template.SpellID, SpellTier: template.SpellTier, ItemScript: template.ItemScript, BookLanguage: template.BookLanguage, EffectType: template.EffectType, // Initialize new instance data Details: ItemCore{ ItemID: template.Details.ItemID, UniqueID: NextUniqueID(), Count: 1, Icon: template.Details.Icon, ClassicIcon: template.Details.ClassicIcon, Tier: template.Details.Tier, RecommendedLevel: template.Details.RecommendedLevel, }, GenericInfo: template.GenericInfo, GroupedCharIDs: make(map[int32]bool), Created: time.Now(), } // Copy type-specific information if template.WeaponInfo != nil { weaponInfo := *template.WeaponInfo item.WeaponInfo = &weaponInfo } if template.RangedInfo != nil { rangedInfo := *template.RangedInfo item.RangedInfo = &rangedInfo } if template.ArmorInfo != nil { armorInfo := *template.ArmorInfo item.ArmorInfo = &armorInfo } if template.AdornmentInfo != nil { adornmentInfo := *template.AdornmentInfo item.AdornmentInfo = &adornmentInfo } if template.BagInfo != nil { bagInfo := *template.BagInfo item.BagInfo = &bagInfo } if template.FoodInfo != nil { foodInfo := *template.FoodInfo item.FoodInfo = &foodInfo } if template.BaubleInfo != nil { baubleInfo := *template.BaubleInfo item.BaubleInfo = &baubleInfo } if template.BookInfo != nil { bookInfo := *template.BookInfo item.BookInfo = &bookInfo } if template.HouseItemInfo != nil { houseItemInfo := *template.HouseItemInfo item.HouseItemInfo = &houseItemInfo } if template.HouseContainerInfo != nil { houseContainerInfo := *template.HouseContainerInfo item.HouseContainerInfo = &houseContainerInfo } if template.SkillInfo != nil { skillInfo := *template.SkillInfo item.SkillInfo = &skillInfo } if template.RecipeBookInfo != nil { recipeBookInfo := *template.RecipeBookInfo item.RecipeBookInfo = &recipeBookInfo } if template.ItemSetInfo != nil { itemSetInfo := *template.ItemSetInfo item.ItemSetInfo = &itemSetInfo } if template.ThrownInfo != nil { thrownInfo := *template.ThrownInfo item.ThrownInfo = &thrownInfo } // Copy collections (deep copy) if len(template.Classifications) > 0 { item.Classifications = make([]*Classifications, len(template.Classifications)) for i, c := range template.Classifications { classification := *c item.Classifications[i] = &classification } } if len(template.ItemStats) > 0 { item.ItemStats = make([]*ItemStat, len(template.ItemStats)) for i, s := range template.ItemStats { stat := *s item.ItemStats[i] = &stat } } if len(template.ItemSets) > 0 { item.ItemSets = make([]*ItemSet, len(template.ItemSets)) for i, s := range template.ItemSets { set := *s item.ItemSets[i] = &set } } if len(template.ItemStringStats) > 0 { item.ItemStringStats = make([]*ItemStatString, len(template.ItemStringStats)) for i, s := range template.ItemStringStats { stat := *s item.ItemStringStats[i] = &stat } } if len(template.ItemLevelOverrides) > 0 { item.ItemLevelOverrides = make([]*ItemLevelOverride, len(template.ItemLevelOverrides)) for i, o := range template.ItemLevelOverrides { override := *o item.ItemLevelOverrides[i] = &override } } if len(template.ItemEffects) > 0 { item.ItemEffects = make([]*ItemEffect, len(template.ItemEffects)) for i, e := range template.ItemEffects { effect := *e item.ItemEffects[i] = &effect } } if len(template.BookPages) > 0 { item.BookPages = make([]*BookPage, len(template.BookPages)) for i, p := range template.BookPages { page := *p item.BookPages[i] = &page } } if len(template.SlotData) > 0 { item.SlotData = make([]int8, len(template.SlotData)) copy(item.SlotData, template.SlotData) } return item } // Copy creates a deep copy of the item func (i *Item) Copy() *Item { if i == nil { return nil } i.mutex.RLock() defer i.mutex.RUnlock() return NewItemFromTemplate(i) } // AddEffect adds an effect to the item func (i *Item) AddEffect(effect string, percentage int8, subBulletFlag int8) { i.mutex.Lock() defer i.mutex.Unlock() itemEffect := &ItemEffect{ Effect: effect, Percentage: percentage, SubBulletFlag: subBulletFlag, } i.ItemEffects = append(i.ItemEffects, itemEffect) } // AddBookPage adds a page to the book item func (i *Item) AddBookPage(page int8, pageText string, vAlign int8, hAlign int8) { i.mutex.Lock() defer i.mutex.Unlock() bookPage := &BookPage{ Page: page, PageText: pageText, VAlign: vAlign, HAlign: hAlign, } i.BookPages = append(i.BookPages, bookPage) } // GetMaxSellValue returns the maximum sell value for the item func (i *Item) GetMaxSellValue() int32 { i.mutex.RLock() defer i.mutex.RUnlock() return i.MaxSellValue } // SetMaxSellValue sets the maximum sell value for the item func (i *Item) SetMaxSellValue(val int32) { i.mutex.Lock() defer i.mutex.Unlock() i.MaxSellValue = val } // GetOverrideLevel gets the level override for specific classes func (i *Item) GetOverrideLevel(adventureClass int8, tradeskillClass int8) int16 { i.mutex.RLock() defer i.mutex.RUnlock() for _, override := range i.ItemLevelOverrides { if override.AdventureClass == adventureClass && override.TradeskillClass == tradeskillClass { return override.Level } } return 0 } // AddLevelOverride adds a level override for specific classes func (i *Item) AddLevelOverride(adventureClass int8, tradeskillClass int8, level int16) { i.mutex.Lock() defer i.mutex.Unlock() override := &ItemLevelOverride{ AdventureClass: adventureClass, TradeskillClass: tradeskillClass, Level: level, } i.ItemLevelOverrides = append(i.ItemLevelOverrides, override) } // CheckClassLevel checks if the item meets class and level requirements func (i *Item) CheckClassLevel(adventureClass int8, tradeskillClass int8, level int16) bool { i.mutex.RLock() defer i.mutex.RUnlock() // Check for specific level override overrideLevel := i.GetOverrideLevel(adventureClass, tradeskillClass) if overrideLevel > 0 { return level >= overrideLevel } // Check general requirements if adventureClass > 0 && i.GenericInfo.AdventureDefaultLevel > 0 { return level >= i.GenericInfo.AdventureDefaultLevel } if tradeskillClass > 0 && i.GenericInfo.TradeskillDefaultLevel > 0 { return level >= i.GenericInfo.TradeskillDefaultLevel } return true } // CheckClass checks if the item can be used by the specified classes func (i *Item) CheckClass(adventureClass int8, tradeskillClass int8) bool { i.mutex.RLock() defer i.mutex.RUnlock() // Check adventure class requirements if adventureClass > 0 && i.GenericInfo.AdventureClasses > 0 { classBit := int64(1 << uint(adventureClass)) if (i.GenericInfo.AdventureClasses & classBit) == 0 { return false } } // Check tradeskill class requirements if tradeskillClass > 0 && i.GenericInfo.TradeskillClasses > 0 { classBit := int64(1 << uint(tradeskillClass)) if (i.GenericInfo.TradeskillClasses & classBit) == 0 { return false } } return true } // SetAppearance sets the appearance information for the item func (i *Item) SetAppearance(appearanceType int16, red int8, green int8, blue int8, highlightRed int8, highlightGreen int8, highlightBlue int8) { i.mutex.Lock() defer i.mutex.Unlock() i.GenericInfo.AppearanceID = appearanceType i.GenericInfo.AppearanceRed = red i.GenericInfo.AppearanceGreen = green i.GenericInfo.AppearanceBlue = blue i.GenericInfo.AppearanceHighlightRed = highlightRed i.GenericInfo.AppearanceHighlightGreen = highlightGreen i.GenericInfo.AppearanceHighlightBlue = highlightBlue } // AddStat adds a stat to the item func (i *Item) AddStat(stat *ItemStat) { if stat == nil { return } i.mutex.Lock() defer i.mutex.Unlock() statCopy := *stat i.ItemStats = append(i.ItemStats, &statCopy) } // AddStatByValues adds a stat using individual values func (i *Item) AddStatByValues(statType int32, subType int16, value float32, level int8, name string) { i.mutex.Lock() defer i.mutex.Unlock() stat := &ItemStat{ StatName: name, StatType: statType, StatSubtype: subType, StatTypeCombined: (int16(statType) << 8) | subType, Value: value, Level: level, } i.ItemStats = append(i.ItemStats, stat) } // HasStat checks if the item has a specific stat func (i *Item) HasStat(statID uint32, statName string) bool { i.mutex.RLock() defer i.mutex.RUnlock() for _, stat := range i.ItemStats { if statName != "" && strings.EqualFold(stat.StatName, statName) { return true } if statID > 0 && uint32(stat.StatTypeCombined) == statID { return true } } return false } // AddSet adds an item set to the item func (i *Item) AddSet(set *ItemSet) { if set == nil { return } i.mutex.Lock() defer i.mutex.Unlock() setCopy := *set i.ItemSets = append(i.ItemSets, &setCopy) } // AddSetByValues adds an item set using individual values func (i *Item) AddSetByValues(itemID int32, itemCRC int32, itemIcon int16, itemStackSize int32, itemListColor int32, name string, language int8) { i.mutex.Lock() defer i.mutex.Unlock() set := &ItemSet{ ItemID: itemID, ItemCRC: itemCRC, ItemIcon: itemIcon, ItemStackSize: int16(itemStackSize), ItemListColor: itemListColor, Name: name, Language: language, } i.ItemSets = append(i.ItemSets, set) } // DeleteItemSets removes all item sets from the item func (i *Item) DeleteItemSets() { i.mutex.Lock() defer i.mutex.Unlock() i.ItemSets = nil } // AddStatString adds a string stat to the item func (i *Item) AddStatString(statString *ItemStatString) { if statString == nil { return } i.mutex.Lock() defer i.mutex.Unlock() statCopy := *statString i.ItemStringStats = append(i.ItemStringStats, &statCopy) } // SetWeaponType sets the weapon type func (i *Item) SetWeaponType(weaponType int8) { i.mutex.Lock() defer i.mutex.Unlock() i.WeaponType = weaponType } // GetWeaponType gets the weapon type func (i *Item) GetWeaponType() int8 { i.mutex.RLock() defer i.mutex.RUnlock() return i.WeaponType } // HasSlot checks if the item can be equipped in specific slots func (i *Item) HasSlot(slot int8, slot2 int8) bool { i.mutex.RLock() defer i.mutex.RUnlock() for _, slotData := range i.SlotData { if slotData == slot || (slot2 != -1 && slotData == slot2) { return true } } return false } // HasAdorn0 checks if the item has an adornment in slot 0 func (i *Item) HasAdorn0() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.Adorn0 > 0 } // HasAdorn1 checks if the item has an adornment in slot 1 func (i *Item) HasAdorn1() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.Adorn1 > 0 } // HasAdorn2 checks if the item has an adornment in slot 2 func (i *Item) HasAdorn2() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.Adorn2 > 0 } // SetItemType sets the item type func (i *Item) SetItemType(itemType int8) { i.mutex.Lock() defer i.mutex.Unlock() i.GenericInfo.ItemType = itemType } // CheckFlag checks if the item has a specific flag set func (i *Item) CheckFlag(flag int32) bool { i.mutex.RLock() defer i.mutex.RUnlock() return (int32(i.GenericInfo.ItemFlags) & flag) != 0 } // CheckFlag2 checks if the item has a specific flag2 set func (i *Item) CheckFlag2(flag int32) bool { i.mutex.RLock() defer i.mutex.RUnlock() return (int32(i.GenericInfo.ItemFlags2) & flag) != 0 } // AddSlot adds a slot to the item's slot data func (i *Item) AddSlot(slotID int8) { i.mutex.Lock() defer i.mutex.Unlock() // Check if slot already exists for _, slot := range i.SlotData { if slot == slotID { return } } i.SlotData = append(i.SlotData, slotID) } // SetSlots sets the slots using a bitmask func (i *Item) SetSlots(slots int32) { i.mutex.Lock() defer i.mutex.Unlock() i.SlotData = nil // Convert bitmask to slot array for slotID := int8(0); slotID < 32; slotID++ { if (slots & (1 << uint(slotID))) != 0 { i.SlotData = append(i.SlotData, slotID) } } } // GetIcon returns the appropriate icon for the given client version func (i *Item) GetIcon(version int16) int16 { i.mutex.RLock() defer i.mutex.RUnlock() // Use classic icon for older clients if version < 1000 && i.Details.ClassicIcon > 0 { return i.Details.ClassicIcon } return i.Details.Icon } // TryLockItem attempts to lock the item for a specific reason func (i *Item) TryLockItem(reason LockReason) bool { i.mutex.Lock() defer i.mutex.Unlock() if i.Details.ItemLocked { // Check if already locked for this reason if (LockReason(i.Details.LockFlags) & reason) != 0 { return true // Already locked for this reason } return false // Locked for different reason } i.Details.ItemLocked = true i.Details.LockFlags = int32(reason) return true } // TryUnlockItem attempts to unlock the item for a specific reason func (i *Item) TryUnlockItem(reason LockReason) bool { i.mutex.Lock() defer i.mutex.Unlock() if !i.Details.ItemLocked { return true // Already unlocked } // Remove the specific lock reason currentFlags := LockReason(i.Details.LockFlags) newFlags := currentFlags & ^reason if newFlags == 0 { // No more lock reasons, unlock the item i.Details.ItemLocked = false i.Details.LockFlags = 0 } else { // Still have other lock reasons i.Details.LockFlags = int32(newFlags) } return true } // IsItemLocked checks if the item is locked func (i *Item) IsItemLocked() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.Details.ItemLocked } // IsItemLockedFor checks if the item is locked for a specific reason func (i *Item) IsItemLockedFor(reason LockReason) bool { i.mutex.RLock() defer i.mutex.RUnlock() if !i.Details.ItemLocked { return false } return (LockReason(i.Details.LockFlags) & reason) != 0 } // SetItemScript sets the item script func (i *Item) SetItemScript(script string) { i.mutex.Lock() defer i.mutex.Unlock() i.ItemScript = script } // GetItemScript returns the item script func (i *Item) GetItemScript() string { i.mutex.RLock() defer i.mutex.RUnlock() return i.ItemScript } // CalculateRepairCost calculates the repair cost for the item func (i *Item) CalculateRepairCost() int32 { i.mutex.RLock() defer i.mutex.RUnlock() // Basic repair cost calculation based on item level and condition baseRepairCost := int32(i.Details.RecommendedLevel * 10) // Adjust based on condition (lower condition = higher repair cost) conditionMultiplier := float32(100-i.GenericInfo.Condition) / 100.0 return int32(float32(baseRepairCost) * conditionMultiplier) } // CreateItemLink creates an item link for chat/display func (i *Item) CreateItemLink(clientVersion int16, useUniqueID bool) string { i.mutex.RLock() defer i.mutex.RUnlock() var builder strings.Builder builder.WriteString("[item:") if useUniqueID { builder.WriteString(strconv.FormatInt(i.Details.UniqueID, 10)) } else { builder.WriteString(strconv.FormatInt(int64(i.Details.ItemID), 10)) } builder.WriteString(":") builder.WriteString(strconv.FormatInt(int64(i.Details.Count), 10)) builder.WriteString(":") builder.WriteString(strconv.FormatInt(int64(i.Details.Tier), 10)) builder.WriteString("]") builder.WriteString(i.Name) builder.WriteString("[/item]") return builder.String() } // Validate validates the item data func (i *Item) Validate() *ItemValidationResult { i.mutex.RLock() defer i.mutex.RUnlock() result := &ItemValidationResult{Valid: true} // Check required fields if i.Name == "" { result.Valid = false result.Errors = append(result.Errors, "item name is required") } if len(i.Name) > MaxItemNameLength { result.Valid = false result.Errors = append(result.Errors, fmt.Sprintf("item name exceeds maximum length of %d", MaxItemNameLength)) } if len(i.Description) > MaxItemDescLength { result.Valid = false result.Errors = append(result.Errors, fmt.Sprintf("item description exceeds maximum length of %d", MaxItemDescLength)) } if i.Details.ItemID <= 0 { result.Valid = false result.Errors = append(result.Errors, "item ID must be positive") } if i.Details.Count <= 0 { result.Valid = false result.Errors = append(result.Errors, "item count must be positive") } if i.GenericInfo.Condition < 0 || i.GenericInfo.Condition > 100 { result.Valid = false result.Errors = append(result.Errors, "item condition must be between 0 and 100") } // Validate item type-specific data switch i.GenericInfo.ItemType { case ItemTypeWeapon: if i.WeaponInfo == nil { result.Valid = false result.Errors = append(result.Errors, "weapon items must have weapon info") } case ItemTypeArmor: if i.ArmorInfo == nil { result.Valid = false result.Errors = append(result.Errors, "armor items must have armor info") } case ItemTypeBag: if i.BagInfo == nil { result.Valid = false result.Errors = append(result.Errors, "bag items must have bag info") } } return result } // String returns a string representation of the item func (i *Item) String() string { i.mutex.RLock() defer i.mutex.RUnlock() return fmt.Sprintf("Item{ID: %d, Name: %s, Type: %d, Count: %d}", i.Details.ItemID, i.Name, i.GenericInfo.ItemType, i.Details.Count) } // Global unique ID counter var ( uniqueIDCounter int64 = 1 uniqueIDMutex sync.Mutex ) // NextUniqueID generates the next unique ID for items func NextUniqueID() int64 { uniqueIDMutex.Lock() defer uniqueIDMutex.Unlock() id := uniqueIDCounter uniqueIDCounter++ return id } // Item type checking methods // IsNormal checks if the item is a normal item func (i *Item) IsNormal() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.GenericInfo.ItemType == ItemTypeNormal } // IsWeapon checks if the item is a weapon func (i *Item) IsWeapon() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.GenericInfo.ItemType == ItemTypeWeapon } // IsArmor checks if the item is armor func (i *Item) IsArmor() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.GenericInfo.ItemType == ItemTypeArmor } // IsRanged checks if the item is a ranged weapon func (i *Item) IsRanged() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.GenericInfo.ItemType == ItemTypeRanged } // IsBag checks if the item is a bag func (i *Item) IsBag() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.GenericInfo.ItemType == ItemTypeBag } // IsFood checks if the item is food func (i *Item) IsFood() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.GenericInfo.ItemType == ItemTypeFood } // IsBauble checks if the item is a bauble func (i *Item) IsBauble() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.GenericInfo.ItemType == ItemTypeBauble } // IsSkill checks if the item is a skill item func (i *Item) IsSkill() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.GenericInfo.ItemType == ItemTypeSkill } // IsHouseItem checks if the item is a house item func (i *Item) IsHouseItem() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.GenericInfo.ItemType == ItemTypeHouse } // IsHouseContainer checks if the item is a house container func (i *Item) IsHouseContainer() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.GenericInfo.ItemType == ItemTypeHouseContainer } // IsShield checks if the item is a shield func (i *Item) IsShield() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.GenericInfo.ItemType == ItemTypeShield } // IsAdornment checks if the item is an adornment func (i *Item) IsAdornment() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.GenericInfo.ItemType == ItemTypeAdornment } // IsBook checks if the item is a book func (i *Item) IsBook() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.GenericInfo.ItemType == ItemTypeBook } // IsThrown checks if the item is a thrown weapon func (i *Item) IsThrown() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.GenericInfo.ItemType == ItemTypeThrown } // IsHarvest checks if the item is harvestable func (i *Item) IsHarvest() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.GenericInfo.Harvest > 0 } // IsBodyDrop checks if the item drops on death func (i *Item) IsBodyDrop() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.GenericInfo.BodyDrop > 0 } // IsCollectable checks if the item is collectable func (i *Item) IsCollectable() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.GenericInfo.Collectable > 0 } // Additional broker-specific type checks // IsAmmo checks if the item is ammunition func (i *Item) IsAmmo() bool { // TODO: Implement ammo detection logic based on item properties return false } // IsChainArmor checks if the item is chain armor func (i *Item) IsChainArmor() bool { // TODO: Implement chain armor detection logic return false } // IsCloak checks if the item is a cloak func (i *Item) IsCloak() bool { // TODO: Implement cloak detection logic return false } // IsClothArmor checks if the item is cloth armor func (i *Item) IsClothArmor() bool { // TODO: Implement cloth armor detection logic return false } // IsCrushWeapon checks if the item is a crush weapon func (i *Item) IsCrushWeapon() bool { // TODO: Implement crush weapon detection logic return false } // IsFoodFood checks if the item is food (not drink) func (i *Item) IsFoodFood() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.IsFood() && i.FoodInfo != nil && i.FoodInfo.Type == 1 } // IsFoodDrink checks if the item is a drink func (i *Item) IsFoodDrink() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.IsFood() && i.FoodInfo != nil && i.FoodInfo.Type == 0 } // IsJewelry checks if the item is jewelry func (i *Item) IsJewelry() bool { // TODO: Implement jewelry detection logic return false } // IsLeatherArmor checks if the item is leather armor func (i *Item) IsLeatherArmor() bool { // TODO: Implement leather armor detection logic return false } // IsMisc checks if the item is miscellaneous func (i *Item) IsMisc() bool { // TODO: Implement misc item detection logic return false } // IsPierceWeapon checks if the item is a pierce weapon func (i *Item) IsPierceWeapon() bool { // TODO: Implement pierce weapon detection logic return false } // IsPlateArmor checks if the item is plate armor func (i *Item) IsPlateArmor() bool { // TODO: Implement plate armor detection logic return false } // IsPoison checks if the item is poison func (i *Item) IsPoison() bool { // TODO: Implement poison detection logic return false } // IsPotion checks if the item is a potion func (i *Item) IsPotion() bool { // TODO: Implement potion detection logic return false } // IsRecipeBook checks if the item is a recipe book func (i *Item) IsRecipeBook() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.GenericInfo.ItemType == ItemTypeRecipe } // IsSalesDisplay checks if the item is a sales display func (i *Item) IsSalesDisplay() bool { // TODO: Implement sales display detection logic return false } // IsSlashWeapon checks if the item is a slash weapon func (i *Item) IsSlashWeapon() bool { // TODO: Implement slash weapon detection logic return false } // IsSpellScroll checks if the item is a spell scroll func (i *Item) IsSpellScroll() bool { // TODO: Implement spell scroll detection logic return false } // IsTinkered checks if the item is tinkered func (i *Item) IsTinkered() bool { i.mutex.RLock() defer i.mutex.RUnlock() return i.Tinkered } // IsTradeskill checks if the item is a tradeskill item func (i *Item) IsTradeskill() bool { // TODO: Implement tradeskill item detection logic return false } // Log a message when the item system is initialized func init() { log.Printf("Items system initialized") }