123 lines
3.0 KiB
Go
123 lines
3.0 KiB
Go
// 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]
|
||
}
|