物品、物品容器重新设计
This commit is contained in:
parent
dd0b486356
commit
aa5a070fe6
|
@ -1,24 +1,22 @@
|
||||||
package builtin
|
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]{
|
item := &Item[ItemID]{
|
||||||
id: id,
|
id: id,
|
||||||
}
|
}
|
||||||
for _, option := range options {
|
|
||||||
option(item)
|
|
||||||
}
|
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
type Item[ItemID comparable] struct {
|
type Item[ItemID comparable] struct {
|
||||||
id ItemID
|
id ItemID
|
||||||
guid int64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *Item[ItemID]) GetID() ItemID {
|
func (slf *Item[ItemID]) GetID() ItemID {
|
||||||
return slf.id
|
return slf.id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *Item[ItemID]) GetGUID() int64 {
|
func (slf *Item[ItemID]) IsSame(item game.Item[ItemID]) bool {
|
||||||
return slf.guid
|
return slf.id == item.GetID()
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,14 @@ package builtin
|
||||||
import (
|
import (
|
||||||
"github.com/kercylan98/minotaur/game"
|
"github.com/kercylan98/minotaur/game"
|
||||||
"github.com/kercylan98/minotaur/utils/huge"
|
"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] {
|
func NewItemContainer[ItemID comparable, Item game.Item[ItemID]](options ...ItemContainerOption[ItemID, Item]) *ItemContainer[ItemID, Item] {
|
||||||
itemContainer := &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 {
|
for _, option := range options {
|
||||||
option(itemContainer)
|
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 {
|
type ItemContainer[ItemID comparable, Item game.Item[ItemID]] struct {
|
||||||
sizeLimit int
|
guid atomic.Int64
|
||||||
size int
|
sizeLimit int
|
||||||
expandSize int
|
size int
|
||||||
items map[ItemID]map[int64]*ItemContainerMember[ItemID]
|
expandSize int
|
||||||
sort []*itemContainerSort[ItemID]
|
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 {
|
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) {
|
func (slf *ItemContainer[ItemID, Item]) SetExpandSize(size int) {
|
||||||
slf.expandSize = size
|
slf.expandSize = size
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *ItemContainer[ItemID, Item]) GetSizeLimit() int {
|
func (slf *ItemContainer[ItemID, Item]) GetItem(guid int64) (game.ItemContainerMember[ItemID, Item], error) {
|
||||||
return slf.sizeLimit + slf.expandSize
|
item, exist := slf.items[guid]
|
||||||
}
|
|
||||||
|
|
||||||
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]
|
|
||||||
if !exist {
|
if !exist {
|
||||||
return nil, ErrItemNotExist
|
return nil, ErrItemNotExist
|
||||||
}
|
}
|
||||||
return member, nil
|
return item, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *ItemContainer[ItemID, Item]) GetItems() []game.ItemContainerMember[ItemID] {
|
func (slf *ItemContainer[ItemID, Item]) GetItems() []game.ItemContainerMember[ItemID, Item] {
|
||||||
var items []game.ItemContainerMember[ItemID]
|
//TODO implement me
|
||||||
for _, sort := range slf.sort {
|
panic("implement me")
|
||||||
items = append(items, slf.items[sort.id][sort.guid])
|
}
|
||||||
|
|
||||||
|
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 {
|
func (slf *ItemContainer[ItemID, Item]) AddItem(item Item, count *huge.Int) error {
|
||||||
if count.LessThanOrEqualTo(huge.IntZero) {
|
if count.LessThanOrEqualTo(huge.IntZero) {
|
||||||
return ErrCannotAddNegativeItem
|
return ErrCannotAddNegativeOrZeroItem
|
||||||
}
|
}
|
||||||
members, exist := slf.items[item.GetID()]
|
for guid := range slf.itemIdGuidRef[item.GetID()] {
|
||||||
if !exist {
|
member := slf.items[guid]
|
||||||
members = map[int64]*ItemContainerMember[ItemID]{}
|
if member.GetItem().IsSame(item) {
|
||||||
slf.items[item.GetID()] = members
|
member.count = member.count.Add(count)
|
||||||
|
return nil
|
||||||
}
|
|
||||||
member, exist := members[item.GetGUID()]
|
|
||||||
if !exist {
|
|
||||||
if slf.GetSizeLimit() >= slf.GetSize() {
|
|
||||||
return ErrItemContainerIsFull
|
|
||||||
}
|
}
|
||||||
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *ItemContainer[ItemID, Item]) DeductItem(id ItemID, count *huge.Int) error {
|
func (slf *ItemContainer[ItemID, Item]) DeductItem(guid int64, count *huge.Int) error {
|
||||||
members, exist := slf.items[id]
|
if !slf.ExistItem(guid) {
|
||||||
if !exist || len(members) == 0 {
|
|
||||||
return ErrItemNotExist
|
return ErrItemNotExist
|
||||||
}
|
}
|
||||||
|
member := slf.items[guid]
|
||||||
var backupMembers = make(map[int64]*ItemContainerMember[ItemID])
|
if member.count.GreaterThanOrEqualTo(count) {
|
||||||
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.count = member.count.Sub(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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,8 @@ package builtin
|
||||||
import "errors"
|
import "errors"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrCannotAddNegativeItem = errors.New("cannot add items with negative quantities")
|
ErrCannotAddNegativeOrZeroItem = errors.New("cannot add items with negative quantities or zero")
|
||||||
ErrItemNotExist = errors.New("item not exist")
|
ErrItemNotExist = errors.New("item not exist")
|
||||||
ErrItemInsufficientQuantity = errors.New("item insufficient quantity")
|
ErrItemInsufficientQuantity = errors.New("item insufficient quantity")
|
||||||
ErrItemContainerIsFull = errors.New("item container is full")
|
ErrItemContainerIsFull = errors.New("item container is full")
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,13 +5,33 @@ import (
|
||||||
"github.com/kercylan98/minotaur/utils/huge"
|
"github.com/kercylan98/minotaur/utils/huge"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ItemContainerMember[ItemID comparable] struct {
|
func NewItemContainerMember[ItemID comparable, I game.Item[ItemID]](guid int64, item I) *ItemContainerMember[ItemID, I] {
|
||||||
game.Item[ItemID]
|
return &ItemContainerMember[ItemID, I]{
|
||||||
|
item: item,
|
||||||
|
guid: guid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ItemContainerMember[ItemID comparable, I game.Item[ItemID]] struct {
|
||||||
|
item I
|
||||||
|
guid int64
|
||||||
sort int
|
sort int
|
||||||
count *huge.Int
|
count *huge.Int
|
||||||
bakCount *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()
|
return slf.count.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (slf *ItemContainerMember[ItemID, I]) GetItem() I {
|
||||||
|
return slf.item
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,8 +3,6 @@ package game
|
||||||
type Item[ID comparable] interface {
|
type Item[ID comparable] interface {
|
||||||
// GetID 获取物品ID
|
// GetID 获取物品ID
|
||||||
GetID() ID
|
GetID() ID
|
||||||
// GetGUID 获取物品GUID
|
// IsSame 与另一个物品比较是否相同
|
||||||
// - 用于标识同一件物品不同的特征
|
IsSame(item Item[ID]) bool
|
||||||
// - 负数的GUID在内置功能中可能会被用于特殊判定,如果需要负数建议另外对特殊功能进行实现
|
|
||||||
GetGUID() int64
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,20 +12,18 @@ type ItemContainer[ItemID comparable, I Item[ItemID]] interface {
|
||||||
SetExpandSize(size int)
|
SetExpandSize(size int)
|
||||||
|
|
||||||
// GetItem 获取物品
|
// GetItem 获取物品
|
||||||
GetItem(id ItemID) (ItemContainerMember[ItemID], error)
|
GetItem(guid int64) (ItemContainerMember[ItemID, I], error)
|
||||||
// GetItemWithGuid 根据guid获取物品
|
|
||||||
GetItemWithGuid(id ItemID, guid int64) (ItemContainerMember[ItemID], error)
|
|
||||||
// GetItems 获取所有物品
|
// 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 添加物品
|
// AddItem 添加物品
|
||||||
// - 当物品guid相同时,如果相同物品id及guid的堆叠数量未达到上限,将增加数量,否则增加非堆叠数量
|
|
||||||
// - 当物品guid不同时,堆叠将不可用,每次都将增加非堆叠数量
|
|
||||||
AddItem(item I, count *huge.Int) error
|
AddItem(item I, count *huge.Int) error
|
||||||
// DeductItem 扣除特定物品数量,当数量为0将被移除,数量不足时将不进行任何改变
|
// DeductItem 扣除特定物品数量,当数量为0将被移除,数量不足时将不进行任何改变
|
||||||
// - 将查找特定id的物品,无论guid是否相同,都有可能被扣除直到达到扣除数量
|
DeductItem(guid int64, count *huge.Int) error
|
||||||
// - 当count为负数时,由于负负得正,无论guid是否相同,都有可能被增加物品数量直到达到扣除数量
|
|
||||||
DeductItem(id ItemID, count *huge.Int) error
|
|
||||||
// DeductItemWithGuid 更为精准的扣除特定物品数量,可参考 DeductItem
|
|
||||||
DeductItemWithGuid(id ItemID, guid int64, count *huge.Int) error
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,13 @@ package game
|
||||||
import "github.com/kercylan98/minotaur/utils/huge"
|
import "github.com/kercylan98/minotaur/utils/huge"
|
||||||
|
|
||||||
// ItemContainerMember 物品容器成员信息
|
// ItemContainerMember 物品容器成员信息
|
||||||
type ItemContainerMember[ItemID comparable] interface {
|
type ItemContainerMember[ItemID comparable, I Item[ItemID]] interface {
|
||||||
Item[ItemID]
|
// GetID 获取物品ID
|
||||||
|
GetID() ItemID
|
||||||
|
// GetGUID 获取物品GUID
|
||||||
|
GetGUID() int64
|
||||||
// GetCount 获取物品数量
|
// GetCount 获取物品数量
|
||||||
GetCount() *huge.Int
|
GetCount() *huge.Int
|
||||||
|
// GetItem 获取物品
|
||||||
|
GetItem() I
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue