diff --git a/game/builtin/item.go b/game/builtin/item.go index d9b7e3d..398efbd 100644 --- a/game/builtin/item.go +++ b/game/builtin/item.go @@ -1,24 +1,22 @@ package builtin -func NewItem[ItemID comparable](id ItemID, options ...ItemOption[ItemID]) *Item[ItemID] { +import "github.com/kercylan98/minotaur/game" + +func NewItem[ItemID comparable](id ItemID) *Item[ItemID] { item := &Item[ItemID]{ id: id, } - for _, option := range options { - option(item) - } return item } type Item[ItemID comparable] struct { - id ItemID - guid int64 + id ItemID } func (slf *Item[ItemID]) GetID() ItemID { return slf.id } -func (slf *Item[ItemID]) GetGUID() int64 { - return slf.guid +func (slf *Item[ItemID]) IsSame(item game.Item[ItemID]) bool { + return slf.id == item.GetID() } diff --git a/game/builtin/item_container.go b/game/builtin/item_container.go index fa6b056..d8b3a3a 100644 --- a/game/builtin/item_container.go +++ b/game/builtin/item_container.go @@ -3,12 +3,14 @@ package builtin import ( "github.com/kercylan98/minotaur/game" "github.com/kercylan98/minotaur/utils/huge" - "github.com/kercylan98/minotaur/utils/slice" + "sync/atomic" ) func NewItemContainer[ItemID comparable, Item game.Item[ItemID]](options ...ItemContainerOption[ItemID, Item]) *ItemContainer[ItemID, Item] { itemContainer := &ItemContainer[ItemID, Item]{ - items: map[ItemID]map[int64]*ItemContainerMember[ItemID]{}, + items: map[int64]*ItemContainerMember[ItemID, Item]{}, + itemIdGuidRef: map[ItemID]map[int64]bool{}, + stackLimits: map[ItemID]*huge.Int{}, } for _, option := range options { option(itemContainer) @@ -17,137 +19,128 @@ func NewItemContainer[ItemID comparable, Item game.Item[ItemID]](options ...Item } type ItemContainer[ItemID comparable, Item game.Item[ItemID]] struct { - sizeLimit int - size int - expandSize int - items map[ItemID]map[int64]*ItemContainerMember[ItemID] - sort []*itemContainerSort[ItemID] + guid atomic.Int64 + sizeLimit int + size int + expandSize int + items map[int64]*ItemContainerMember[ItemID, Item] + itemIdGuidRef map[ItemID]map[int64]bool + sort []*itemContainerSort[ItemID] + stackLimits map[ItemID]*huge.Int } func (slf *ItemContainer[ItemID, Item]) GetSize() int { - return slf.size + return slf.size + slf.expandSize +} + +func (slf *ItemContainer[ItemID, Item]) GetSizeLimit() int { + return slf.sizeLimit } func (slf *ItemContainer[ItemID, Item]) SetExpandSize(size int) { slf.expandSize = size } -func (slf *ItemContainer[ItemID, Item]) GetSizeLimit() int { - return slf.sizeLimit + slf.expandSize -} - -func (slf *ItemContainer[ItemID, Item]) GetItem(id ItemID) (game.ItemContainerMember[ItemID], error) { - for _, member := range slf.items[id] { - return member, nil - } - return nil, ErrItemNotExist -} - -func (slf *ItemContainer[ItemID, Item]) GetItemWithGuid(id ItemID, guid int64) (game.ItemContainerMember[ItemID], error) { - member, exist := slf.items[id][guid] +func (slf *ItemContainer[ItemID, Item]) GetItem(guid int64) (game.ItemContainerMember[ItemID, Item], error) { + item, exist := slf.items[guid] if !exist { return nil, ErrItemNotExist } - return member, nil + return item, nil } -func (slf *ItemContainer[ItemID, Item]) GetItems() []game.ItemContainerMember[ItemID] { - var items []game.ItemContainerMember[ItemID] - for _, sort := range slf.sort { - items = append(items, slf.items[sort.id][sort.guid]) +func (slf *ItemContainer[ItemID, Item]) GetItems() []game.ItemContainerMember[ItemID, Item] { + //TODO implement me + panic("implement me") +} + +func (slf *ItemContainer[ItemID, Item]) GetItemsMap() map[int64]game.ItemContainerMember[ItemID, Item] { + var m = make(map[int64]game.ItemContainerMember[ItemID, Item]) + for k, v := range slf.items { + m[k] = v } - return items + return m +} + +func (slf *ItemContainer[ItemID, Item]) ExistItem(guid int64) bool { + _, exist := slf.items[guid] + return exist +} + +func (slf *ItemContainer[ItemID, Item]) ExistItemWithID(id ItemID) bool { + return len(slf.itemIdGuidRef[id]) > 0 } func (slf *ItemContainer[ItemID, Item]) AddItem(item Item, count *huge.Int) error { if count.LessThanOrEqualTo(huge.IntZero) { - return ErrCannotAddNegativeItem + return ErrCannotAddNegativeOrZeroItem } - members, exist := slf.items[item.GetID()] - if !exist { - members = map[int64]*ItemContainerMember[ItemID]{} - slf.items[item.GetID()] = members - - } - member, exist := members[item.GetGUID()] - if !exist { - if slf.GetSizeLimit() >= slf.GetSize() { - return ErrItemContainerIsFull + for guid := range slf.itemIdGuidRef[item.GetID()] { + member := slf.items[guid] + if member.GetItem().IsSame(item) { + member.count = member.count.Add(count) + return nil } - members[item.GetGUID()] = &ItemContainerMember[ItemID]{ - sort: len(slf.sort), - Item: item, - count: count, - } - slf.sort = append(slf.sort, &itemContainerSort[ItemID]{ - id: item.GetID(), - guid: item.GetGUID(), - }) - slf.size++ - } else { - member.count = member.count.Add(count) } + if slf.size >= slf.GetSizeLimit() { + return ErrItemContainerIsFull + } + guid := slf.guid.Add(1) + slf.items[guid] = &ItemContainerMember[ItemID, Item]{ + item: item, + guid: guid, + count: count.Copy(), + } + guids, exist := slf.itemIdGuidRef[item.GetID()] + if !exist { + guids = map[int64]bool{} + slf.itemIdGuidRef[item.GetID()] = guids + } + guids[guid] = true + slf.size++ return nil } -func (slf *ItemContainer[ItemID, Item]) DeductItem(id ItemID, count *huge.Int) error { - members, exist := slf.items[id] - if !exist || len(members) == 0 { +func (slf *ItemContainer[ItemID, Item]) DeductItem(guid int64, count *huge.Int) error { + if !slf.ExistItem(guid) { return ErrItemNotExist } - - var backupMembers = make(map[int64]*ItemContainerMember[ItemID]) - var pending = count.Copy() - var deductMembers []*ItemContainerMember[ItemID] - for guid, member := range members { - member.bakCount = member.count.Copy() - backupMembers[guid] = member - - if pending.GreaterThanOrEqualTo(member.count) { - pending = pending.Sub(member.count) - member.count = huge.IntZero - } else { - member.count = member.count.Sub(pending) - pending = huge.IntZero - } - - if member.count.EqualTo(huge.IntZero) { - delete(members, guid) - deductMembers = append(deductMembers, member) - } - if pending.EqualTo(huge.IntZero) { - break - } - } - if pending.GreaterThan(huge.IntZero) { - for guid, member := range backupMembers { - members[guid] = member - member.count = member.bakCount - member.bakCount = nil - } - return ErrItemInsufficientQuantity - } - slf.size -= len(deductMembers) - for _, member := range deductMembers { - slice.Del(&slf.sort, member.sort) - } - return nil -} - -func (slf *ItemContainer[ItemID, Item]) DeductItemWithGuid(id ItemID, guid int64, count *huge.Int) error { - member, exist := slf.items[id][guid] - if !exist { - return ErrItemNotExist - } - if count.GreaterThan(member.count) { - return ErrItemInsufficientQuantity - } else { + member := slf.items[guid] + if member.count.GreaterThanOrEqualTo(count) { member.count = member.count.Sub(count) + if member.count.EqualTo(huge.IntZero) { + slf.size-- + delete(slf.items, guid) + } + return nil + } else { + var need = count.Copy() + var handles []func() + var guids = slf.itemIdGuidRef[member.GetID()] + for guid := range guids { + member := slf.items[guid] + if need.GreaterThanOrEqualTo(member.count) { + need = need.Sub(member.count) + handles = append(handles, func() { + member.count = huge.IntZero.Copy() + slf.size-- + delete(guids, guid) + delete(slf.items, guid) + if len(guids) == 0 { + delete(slf.itemIdGuidRef, member.GetID()) + } + }) + } else { + member.count = member.count.Sub(need) + need = huge.IntZero + } + } + if need.GreaterThan(huge.IntZero) { + return ErrItemInsufficientQuantity + } + for _, handle := range handles { + handle() + } + return nil } - if member.count.EqualTo(huge.IntZero) { - delete(slf.items[id], guid) - slice.Del(&slf.sort, member.sort) - slf.size-- - } - return nil } diff --git a/game/builtin/item_container_errors.go b/game/builtin/item_container_errors.go index 2201203..8c22915 100644 --- a/game/builtin/item_container_errors.go +++ b/game/builtin/item_container_errors.go @@ -3,8 +3,8 @@ package builtin import "errors" var ( - ErrCannotAddNegativeItem = errors.New("cannot add items with negative quantities") - ErrItemNotExist = errors.New("item not exist") - ErrItemInsufficientQuantity = errors.New("item insufficient quantity") - ErrItemContainerIsFull = errors.New("item container is full") + ErrCannotAddNegativeOrZeroItem = errors.New("cannot add items with negative quantities or zero") + ErrItemNotExist = errors.New("item not exist") + ErrItemInsufficientQuantity = errors.New("item insufficient quantity") + ErrItemContainerIsFull = errors.New("item container is full") ) diff --git a/game/builtin/item_container_member.go b/game/builtin/item_container_member.go index df7d9ab..578cc35 100644 --- a/game/builtin/item_container_member.go +++ b/game/builtin/item_container_member.go @@ -5,13 +5,33 @@ import ( "github.com/kercylan98/minotaur/utils/huge" ) -type ItemContainerMember[ItemID comparable] struct { - game.Item[ItemID] +func NewItemContainerMember[ItemID comparable, I game.Item[ItemID]](guid int64, item I) *ItemContainerMember[ItemID, I] { + return &ItemContainerMember[ItemID, I]{ + item: item, + guid: guid, + } +} + +type ItemContainerMember[ItemID comparable, I game.Item[ItemID]] struct { + item I + guid int64 sort int count *huge.Int bakCount *huge.Int } -func (slf *ItemContainerMember[ItemID]) GetCount() *huge.Int { +func (slf *ItemContainerMember[ItemID, I]) GetID() ItemID { + return slf.item.GetID() +} + +func (slf *ItemContainerMember[ItemID, I]) GetGUID() int64 { + return slf.guid +} + +func (slf *ItemContainerMember[ItemID, I]) GetCount() *huge.Int { return slf.count.Copy() } + +func (slf *ItemContainerMember[ItemID, I]) GetItem() I { + return slf.item +} diff --git a/game/builtin/item_options.go b/game/builtin/item_options.go deleted file mode 100644 index 1f2e3a9..0000000 --- a/game/builtin/item_options.go +++ /dev/null @@ -1,10 +0,0 @@ -package builtin - -type ItemOption[ItemID comparable] func(item *Item[ItemID]) - -// WithItemGuid 通过特定的guid创建物品 -func WithItemGuid[ItemID comparable](guid int64) ItemOption[ItemID] { - return func(item *Item[ItemID]) { - item.guid = guid - } -} diff --git a/game/item.go b/game/item.go index be78ba6..5605c53 100644 --- a/game/item.go +++ b/game/item.go @@ -3,8 +3,6 @@ package game type Item[ID comparable] interface { // GetID 获取物品ID GetID() ID - // GetGUID 获取物品GUID - // - 用于标识同一件物品不同的特征 - // - 负数的GUID在内置功能中可能会被用于特殊判定,如果需要负数建议另外对特殊功能进行实现 - GetGUID() int64 + // IsSame 与另一个物品比较是否相同 + IsSame(item Item[ID]) bool } diff --git a/game/item_container.go b/game/item_container.go index a87c659..33ea5b5 100644 --- a/game/item_container.go +++ b/game/item_container.go @@ -12,20 +12,18 @@ type ItemContainer[ItemID comparable, I Item[ItemID]] interface { SetExpandSize(size int) // GetItem 获取物品 - GetItem(id ItemID) (ItemContainerMember[ItemID], error) - // GetItemWithGuid 根据guid获取物品 - GetItemWithGuid(id ItemID, guid int64) (ItemContainerMember[ItemID], error) + GetItem(guid int64) (ItemContainerMember[ItemID, I], error) // GetItems 获取所有物品 - GetItems() []ItemContainerMember[ItemID] + GetItems() []ItemContainerMember[ItemID, I] + // GetItemsMap 获取所有物品 + GetItemsMap() map[int64]ItemContainerMember[ItemID, I] + // ExistItem 物品是否存在 + ExistItem(guid int64) bool + // ExistItemWithID 是否存在特定ID的物品 + ExistItemWithID(id ItemID) bool // AddItem 添加物品 - // - 当物品guid相同时,如果相同物品id及guid的堆叠数量未达到上限,将增加数量,否则增加非堆叠数量 - // - 当物品guid不同时,堆叠将不可用,每次都将增加非堆叠数量 AddItem(item I, count *huge.Int) error // DeductItem 扣除特定物品数量,当数量为0将被移除,数量不足时将不进行任何改变 - // - 将查找特定id的物品,无论guid是否相同,都有可能被扣除直到达到扣除数量 - // - 当count为负数时,由于负负得正,无论guid是否相同,都有可能被增加物品数量直到达到扣除数量 - DeductItem(id ItemID, count *huge.Int) error - // DeductItemWithGuid 更为精准的扣除特定物品数量,可参考 DeductItem - DeductItemWithGuid(id ItemID, guid int64, count *huge.Int) error + DeductItem(guid int64, count *huge.Int) error } diff --git a/game/item_container_member.go b/game/item_container_member.go index 0c512f2..78301c1 100644 --- a/game/item_container_member.go +++ b/game/item_container_member.go @@ -3,8 +3,13 @@ package game import "github.com/kercylan98/minotaur/utils/huge" // ItemContainerMember 物品容器成员信息 -type ItemContainerMember[ItemID comparable] interface { - Item[ItemID] +type ItemContainerMember[ItemID comparable, I Item[ItemID]] interface { + // GetID 获取物品ID + GetID() ItemID + // GetGUID 获取物品GUID + GetGUID() int64 // GetCount 获取物品数量 GetCount() *huge.Int + // GetItem 获取物品 + GetItem() I }