149 lines
3.5 KiB
Go
149 lines
3.5 KiB
Go
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)
|
|
}
|