Files
8bitMyGoUI/data/inventory.go
T
ZacharyZhang-NY f48075230f Add game components 246-268; data.DrawSlot, Canvas.TextVeil
Health, mana and experience bars, quarter hearts, lives, a spinning
coin counter, score board, stage timer, minimap with fog, compass,
hotbar, paper-doll equipment, skill tree, skill buttons with cooldown
wipes, status effects, a boss bar, damage numbers, quest log and
tracker, party frames, character sheet, level-up screen and shop.
data exports DrawSlot so every slot looks alike; pixel adds TextVeil
for dissolving text. Messages the font cannot draw are fixed and
TestMessagesHaveGlyphs holds every message to the font.
2026-10-12 01:53:20 -04:00

98 lines
2.6 KiB
Go

package data
import (
"fmt"
"log/slog"
"github.com/egoist/mygo/ui"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
)
type InventoryOptions struct {
// Columns is the grid's width; zero means 6.
Columns int
Disabled bool
}
type InventoryResult struct {
Element *ui.Element
// Swapped reports two slots traded by a drag.
Changed, Swapped bool
}
type bagKey struct{}
// bag tracks a drag; a dead one never swaps.
type bag struct {
from, over int
held, dead bool
}
// Inventory is a slot grid; arrows choose, drags swap.
func Inventory(c *ui.Context, text string, items []Item, selected *int, o InventoryOptions) InventoryResult {
k := pixel.K(c)
cols := pixel.Or("Inventory Columns", o.Columns, 6)
off := o.Disabled || pixel.Current(c).Inert
if *selected < 0 || *selected >= len(items) {
panic(fmt.Sprintf("data: Inventory slot %d of %d", *selected, len(items)))
}
old := *selected
shell, base, st, _ := pixel.Pad(c, text, cols, len(items), SlotSize, SlotSize, selected, o.Disabled)
b := ui.Local(base, bagKey{}, func() bag { return bag{} })
res := InventoryResult{Element: shell, Changed: *selected != old}
px, py, _ := base.PointerPosition()
at := func() int {
col, row := int(px/k.Dot())/SlotSize, int(py/k.Dot())/SlotSize
if px < 0 || py < 0 || col >= cols || row*cols+col >= len(items) {
return -1
}
return row*cols + col
}
n := len(items)
switch _, _, pressed := base.Dragged(); {
case pressed && (off || b.held && max(b.from, b.over) >= n):
b.held, b.dead = false, true
case pressed && b.dead:
case pressed && !b.held:
b.held, b.from, b.over = true, at(), at()
case pressed:
b.over = at()
case b.held:
if !off && min(b.from, b.over) >= 0 && max(b.from, b.over) < n && b.from != b.over {
items[b.from], items[b.over] = items[b.over], items[b.from]
res.Swapped = true
}
b.held = false
default:
b.dead = false
}
base.Description(items[*selected].String())
drag := -1
if b.held && b.over != b.from {
drag = b.from
}
shown := append([]Item(nil), items...)
shell.Draw(func(p *ui.Painter, r ui.Rect) {
g := k.Canvas(p, r)
for i, it := range shown {
if i == drag {
it = Item{}
}
DrawSlot(g, i%cols*SlotSize, i/cols*SlotSize, it, ItemSlotOptions{Selected: i == *selected})
}
// The dragged item rides with the pointer.
if drag >= 0 && shown[drag].Sprite != nil {
g.Sprite(int(px/k.Dot())-6, int(py/k.Dot())-6, shown[drag].Sprite, 0, pixel.Ink, 1)
}
g.Finish(st)
})
if res.Changed {
slog.Debug("8bit: inventory chose", "label", text, "slot", *selected)
}
if res.Swapped {
slog.Debug("8bit: inventory swap", "label", text, "from", b.from, "to", b.over)
}
return res
}