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

123 lines
3.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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]
}