package pncdsl import ( "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/vector" ) // InventoryBar shows the player's items as clickable slots. Left-click on // a slot under the "look" verb prints the item's description; under any // other verb, it toggles selection (the cursor "picks up" the item). type InventoryBar struct { Name string Origin Point Slots int Cols int SlotSize int Gap int PanelBG bool } func (b *InventoryBar) GetName() string { return b.Name } func (b *InventoryBar) slotRect(idx int) Rectangle { cols := b.Cols if cols <= 0 { cols = b.Slots } step := b.SlotSize + b.Gap col := idx % cols row := idx / cols x := b.Origin.X + float64(col*step) y := b.Origin.Y + float64(row*step) return Rect(x, y, float64(b.SlotSize), float64(b.SlotSize)) } func (b *InventoryBar) Tick(ctx *UICtx) { g := ctx.Game mp := g.Input.Point() items := g.Inventory.Items() // hover label: when over a slot with an item, show its description for i := 0; i < min(b.Slots, len(items)); i++ { if b.slotRect(i).Contains(mp) { if it, ok := g.ItemManager.Get(items[i]); ok && it.Description != "" { g.SetHoverLabel(it.Description) } else { g.SetHoverLabel(items[i]) } break } } if !g.Input.LeftClicked() { return } for i := 0; i < min(b.Slots, len(items)); i++ { if b.slotRect(i).Contains(mp) { g.Input.ConsumeLeft() name := items[i] if g.SelectedVerb() == "look" { if it, ok := g.ItemManager.Get(name); ok && it.Description != "" { g.queueAction(Say("player", it.Description), "inv-look") return } } if g.Inventory.Selected() == name { g.Inventory.Select("") } else { g.Inventory.Select(name) } return } } } func (b *InventoryBar) Draw(dst *ebiten.Image, ctx *UICtx) { g := ctx.Game th := g.Theme() items := g.Inventory.Items() if b.PanelBG { last := b.slotRect(b.Slots - 1) first := b.slotRect(0) x := first.X - 2 y := first.Y - 2 w := (last.X + last.W) - first.X + 4 h := (last.Y + last.H) - first.Y + 4 vector.DrawFilledRect(dst, float32(x), float32(y), float32(w), float32(h), th.PanelBG, false) } for i := 0; i < b.Slots; i++ { r := b.slotRect(i) bg := th.InventorySlotBG if i < len(items) && items[i] == g.Inventory.Selected() { bg = th.InventorySlotSelectedBG } vector.DrawFilledRect(dst, float32(r.X), float32(r.Y), float32(r.W), float32(r.H), bg, false) if i < len(items) { it, ok := g.ItemManager.Get(items[i]) if !ok { continue } img := g.loaded.image(g.AssetManager, it.Sprite) sw, sh := img.Bounds().Dx(), img.Bounds().Dy() if sw == 0 || sh == 0 { continue } if g.loaded.isPlaceholder(it.Sprite) { // draw a small colored square as a stand-in inset := float32(2) vector.DrawFilledRect(dst, float32(r.X)+inset, float32(r.Y)+inset, float32(r.W)-2*inset, float32(r.H)-2*inset, th.VerbButtonSelectedBG, false) continue } op := &ebiten.DrawImageOptions{} inset := 1.0 tgtW := r.W - 2*inset tgtH := r.H - 2*inset op.GeoM.Scale(tgtW/float64(sw), tgtH/float64(sh)) op.GeoM.Translate(r.X+inset, r.Y+inset) dst.DrawImage(img, op) } } } func min(a, b int) int { if a < b { return a } return b } // BlocksClickAt — clickBlocker contract for the RadialVerbs widget. func (b *InventoryBar) BlocksClickAt(p Point) bool { if b.Slots <= 0 { return false } first := b.slotRect(0) last := b.slotRect(b.Slots - 1) x := first.X y := first.Y w := last.X + last.W - first.X h := last.Y + last.H - first.Y return Rect(x, y, w, h).Contains(p) }