Files
realworld/inc/ui.manager.go
T
2026-08-29 23:33:29 +02:00

210 lines
4.2 KiB
Go

package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/vector"
)
const (
ScreenW = 640
ScreenH = 380
LineH = glyphH + 2
Pad = 6
TopBarH = LineH + 2
DividerX = 394
HUDH = Pad + LineH + 4 + invSlot*2 + invGap + Pad
HUDTop = ScreenH - HUDH
statusY = HUDTop + Pad
slotsY = HUDTop + Pad + LineH
slotsH = LineH*2 + Pad*2
invY = slotsY + 4
invSlot = 40
invGap = 4
)
func registerUI(w *World) {
g := w.G
g.UIManager.Register(&UseWithGuard{
Name: "usewith_guard",
W: w,
})
g.UIManager.Register(&ActionPump{
Name: "pump",
W: w,
})
g.UIManager.Register(&screenNav{
Name: "screen_nav",
W: w,
})
g.UIManager.Register(&windowSizer{
Name: "window",
Scale: 2,
})
g.UIManager.Register(&inkwell.HotspotDebug{
Name: "hotspot_debug",
})
top := &inkwell.TopBar{
Name: "topbar",
Height: TopBarH,
TimeVar: VarArmillaStrip,
}
w.SetTopBar(top)
g.UIManager.Register(gated(w, "topbar_gate", top))
g.UIManager.Register(&Letterbox{
Name: "letterbox",
W: w,
Bar: 36,
})
g.UIManager.Register(&hudFrame{
Name: "hudframe",
W: w,
})
g.UIManager.Register(gated(w, "status_gate", &inkwell.StatusLine{
Name: "status",
Y: statusY,
Align: inkwell.AlignLeft,
ScreenWidth: DividerX,
}))
g.UIManager.Register(gated(w, "inventory_gate", &inkwell.InventoryBar{
Name: "inventory",
Origin: inkwell.Point{
X: Pad + 2,
Y: invY,
},
Slots: 8,
Cols: 4,
SlotSize: invSlot,
Gap: invGap,
}))
g.UIManager.Register(&TapeSlots{
Name: "tapes",
W: w,
Bounds: inkwell.Rect(192, slotsY, 194, slotsH),
})
g.UIManager.Register(&TapeChannel{
Name: "channel",
W: w,
Bounds: inkwell.Rect(DividerX+2, HUDTop+2, ScreenW-DividerX-4, ScreenH-HUDTop-4),
})
g.UIManager.Register(&inkwell.SpeechBubble{
Name: "speech",
MaxWidth: 360,
Padding: Pad,
OffsetY: 8,
FallbackY: TopBarH + Pad,
})
g.UIManager.Register(&inkwell.DialogBox{
Name: "dialog",
Bounds: inkwell.Rect(0, ScreenH-LineH*9, DividerX, LineH*9),
LineHeight: LineH,
Padding: Pad,
})
g.UIManager.Register(&inkwell.RadialVerbs{
Name: "verbs",
Trigger: inkwell.MouseButtonRight,
Radius: 58,
Labels: map[string]string{
"look": "Look", "use": "Use", "talk": "Talk", "take": "Take",
},
})
g.UIManager.Register(&inkwell.EndCard{
Name: "endcard",
})
g.UIManager.Register(&inkwell.Cursor{
Name: "cursor",
})
}
type windowSizer struct {
Name string
Scale int
done bool
}
func (s *windowSizer) GetName() string { return s.Name }
func (s *windowSizer) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {}
func (s *windowSizer) Tick(ctx *inkwell.UICtx) {
if s.done {
return
}
s.done = true
scale := s.Scale
if scale <= 0 {
scale = 2
}
ebiten.SetWindowSize(ctx.Game.Width*scale, ctx.Game.Height*scale)
}
type gate struct {
Name string
W *World
Inner inkwell.Widget
}
func gated(w *World, name string, inner inkwell.Widget) inkwell.Widget {
return &gate{
Name: name,
W: w,
Inner: inner,
}
}
func (n *gate) GetName() string { return n.Name }
func (n *gate) Tick(ctx *inkwell.UICtx) {
if n.W.HUDVisible() {
n.Inner.Tick(ctx)
}
}
func (n *gate) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {
if n.W.HUDVisible() {
n.Inner.Draw(dst, ctx)
}
}
func (n *gate) BlocksClickAt(p inkwell.Point) bool {
if !n.W.HUDVisible() {
return false
}
b, ok := n.Inner.(interface{ BlocksClickAt(inkwell.Point) bool })
return ok && b.BlocksClickAt(p)
}
type hudFrame struct {
Name string
W *World
}
func (h *hudFrame) GetName() string { return h.Name }
func (h *hudFrame) Tick(ctx *inkwell.UICtx) {}
func (h *hudFrame) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {
if !h.W.HUDVisible() {
return
}
th := ctx.Game.Theme()
vector.DrawFilledRect(dst, 0, HUDTop+1, ScreenW, ScreenH-HUDTop-1, th.PanelBG, false)
vector.StrokeLine(dst, 0, HUDTop, ScreenW, HUDTop, 1, th.CharacterPanelBorder, false)
vector.StrokeLine(dst, DividerX, HUDTop+1, DividerX, ScreenH, 1, th.CharacterPanelBorder, false)
}
func (h *hudFrame) BlocksClickAt(p inkwell.Point) bool {
return h.W.HUDVisible() && p.Y >= HUDTop
}