1012 lines
24 KiB
Go
1012 lines
24 KiB
Go
package items
|
|
|
|
import (
|
|
"fmt"
|
|
"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
|
|
// Ensure StatTypeCombined is set correctly
|
|
statCopy.StatTypeCombined = (int16(statCopy.StatType) << 8) | statCopy.StatSubtype
|
|
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
|
|
}
|
|
// Check by stat ID - removed > 0 check since ItemStatStr is 0
|
|
if statName == "" && 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
|
|
}
|
|
|
|
// Item system initialized
|
|
func init() {
|
|
// Items system initialized
|
|
}
|