Files
ngzz-mc/internal/inventory/inventory.go

123 lines
3.0 KiB
Go
Raw Normal View History

// Package inventory 背包模型与规则表(场景/物品与背包.md §3、UI与输入.md §4)。
//
// 规则表(CanPlace / CanMerge / Merge)为**单一来源**:客户端与服务器共用同一实现,
// 防双端不一致(多人同步.md §5)。
package inventory
import (
"mc/internal/item"
)
// 背包槽位常量(UI还原.md §3.3:27 主区 + 9 热键栏)。
const (
HotbarSize = 9 // 热键栏槽数
MainSize = 27 // 主物品区槽数
TotalSize = HotbarSize + MainSize
)
// Inventory 背包(主线程所有权,无需锁——架构.md §4)。
type Inventory struct {
Slots []item.Stack // 36 槽
Selected int // 热键栏选中索引 0–8
reg *item.Registry
}
// New 创建空背包。
func New(reg *item.Registry) *Inventory {
return &Inventory{Slots: make([]item.Stack, TotalSize), reg: reg}
}
// CanMerge 两栈是否可合并:同名、同耐久、数量未满(物品与背包.md §3)。
func CanMerge(a, b item.Stack) bool {
if a.Empty() || b.Empty() || a.Name != b.Name {
return false
}
if a.Durability != b.Durability {
return false
}
return int(a.Count)+int(b.Count) <= 127
}
// Merge 合并两栈:返回合并结果与剩余量(调用方负责写回槽位)。
func Merge(limit uint8, a, b item.Stack) (item.Stack, item.Stack) {
total := int(a.Count) + int(b.Count)
if total <= int(limit) {
a.Count = uint8(total)
return a, item.Stack{}
}
a.Count = limit
b.Count = uint8(total - int(limit))
return a, b
}
// Add 把物品栈放入背包(先热键栏选中槽,再顺序找空/可合并槽),返回剩余。
func (inv *Inventory) Add(s item.Stack) item.Stack {
if s.Empty() {
return s
}
limit := s.Limit(inv.reg)
// 第一遍:合并到已有同类
for i := range inv.Slots {
if inv.Slots[i].Empty() {
continue
}
if inv.Slots[i].Name == s.Name && inv.Slots[i].Durability == s.Durability && inv.Slots[i].Count < limit {
merged, remain := Merge(limit, inv.Slots[i], s)
inv.Slots[i] = merged
s = remain
if s.Empty() {
return s
}
}
}
// 第二遍:放入空槽
for i := range inv.Slots {
if inv.Slots[i].Empty() {
inv.Slots[i] = s
return item.Stack{}
}
}
return s // 放不下:返回剩余
}
// Remove 从背包移除指定物品 count 个;不足返回 false。
func (inv *Inventory) Remove(name string, count int) bool {
if count <= 0 {
return false
}
have := 0
for i := range inv.Slots {
if inv.Slots[i].Name == name {
have += int(inv.Slots[i].Count)
}
}
if have < count {
return false
}
left := count
for i := range inv.Slots {
if inv.Slots[i].Name != name {
continue
}
if int(inv.Slots[i].Count) <= left {
left -= int(inv.Slots[i].Count)
inv.Slots[i] = item.Stack{}
} else {
inv.Slots[i].Count -= uint8(left)
left = 0
}
if left == 0 {
return true
}
}
return true
}
// SelectedStack 返回选中槽(只读)。
func (inv *Inventory) SelectedStack() item.Stack {
if inv.Selected < 0 || inv.Selected >= HotbarSize {
return item.Stack{}
}
return inv.Slots[inv.Selected]
}