@@ -0,0 +1,11 @@
|
||||
package widget
|
||||
|
||||
import (
|
||||
inkwell "git.teletypegames.org/engines/inkwell"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Manager.Register(&inkwell.Cursor{
|
||||
Name: "cursor",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package widget
|
||||
|
||||
import (
|
||||
inkwell "git.teletypegames.org/engines/inkwell"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Manager.Register(&inkwell.DialogBox{
|
||||
Name: "dialog",
|
||||
Bounds: inkwell.Rect(0, ScreenH-LineH*9, DividerX, LineH*9),
|
||||
LineHeight: LineH,
|
||||
Padding: Pad,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package widget
|
||||
|
||||
import (
|
||||
inkwell "git.teletypegames.org/engines/inkwell"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Manager.Register(&inkwell.EndCard{
|
||||
Name: "endcard",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package widget
|
||||
|
||||
import (
|
||||
inkwell "git.teletypegames.org/engines/inkwell"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Manager.Register(&inkwell.HotspotDebug{
|
||||
Name: "hotspot_debug",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package widget
|
||||
|
||||
import (
|
||||
inkwell "git.teletypegames.org/engines/inkwell"
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Manager.Register(&hudFrame{
|
||||
Name: "hudframe",
|
||||
})
|
||||
}
|
||||
|
||||
type hudFrame struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (h *hudFrame) GetName() string { return h.Name }
|
||||
func (h *hudFrame) VisibleWhen() inkwell.Condition { return whenPlaying }
|
||||
func (h *hudFrame) Layer() inkwell.Layer { return inkwell.LayerPanel }
|
||||
func (h *hudFrame) Tick(ctx *inkwell.UICtx) {}
|
||||
|
||||
func (h *hudFrame) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {
|
||||
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 p.Y >= HUDTop }
|
||||
@@ -0,0 +1,20 @@
|
||||
package widget
|
||||
|
||||
import (
|
||||
inkwell "git.teletypegames.org/engines/inkwell"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Manager.Register(&inkwell.InventoryBar{
|
||||
Name: "inventory",
|
||||
When: whenPlaying,
|
||||
Origin: inkwell.Point{
|
||||
X: Pad + 2,
|
||||
Y: invY,
|
||||
},
|
||||
Slots: 8,
|
||||
Cols: 4,
|
||||
SlotSize: invSlot,
|
||||
Gap: invGap,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package widget
|
||||
|
||||
import (
|
||||
inkwell "git.teletypegames.org/engines/inkwell"
|
||||
"git.teletypegames.org/games/realworld/inc"
|
||||
"git.teletypegames.org/games/realworld/inc/theme"
|
||||
"git.teletypegames.org/games/realworld/inc/world"
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Manager.Register(&letterbox{
|
||||
Name: "letterbox",
|
||||
Bar: 36,
|
||||
})
|
||||
}
|
||||
|
||||
type letterbox struct {
|
||||
Name string
|
||||
Bar float64
|
||||
}
|
||||
|
||||
func (l *letterbox) GetName() string { return l.Name }
|
||||
func (l *letterbox) VisibleWhen() inkwell.Condition { return whenCutscene }
|
||||
func (l *letterbox) Layer() inkwell.Layer { return inkwell.LayerScene }
|
||||
|
||||
func (l *letterbox) Tick(ctx *inkwell.UICtx) {
|
||||
if !world.TapeWaiting() {
|
||||
return
|
||||
}
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeySpace) {
|
||||
world.TakeTapeOffer()
|
||||
}
|
||||
}
|
||||
|
||||
func (l *letterbox) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {
|
||||
g := ctx.Game
|
||||
th := g.Theme()
|
||||
bar := l.Bar
|
||||
if bar <= 0 {
|
||||
bar = 18
|
||||
}
|
||||
w, h := float32(g.Width), float32(g.Height)
|
||||
black := theme.RGB(0x000000)
|
||||
vector.DrawFilledRect(dst, 0, 0, w, float32(bar), black, false)
|
||||
vector.DrawFilledRect(dst, 0, h-float32(bar), w, float32(bar), black, false)
|
||||
|
||||
if world.TapeWaiting() {
|
||||
msg := "SPACE — " + g.CharacterLabel(inc.Dex) + " has something to say"
|
||||
g.DrawText(dst, msg, g.Width-inkwell.TextWidth(msg)-4, g.Height-int(bar)+4, th.ChatLogResponse)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package widget
|
||||
|
||||
import (
|
||||
inkwell "git.teletypegames.org/engines/inkwell"
|
||||
"git.teletypegames.org/games/realworld/inc"
|
||||
"git.teletypegames.org/games/realworld/inc/world"
|
||||
)
|
||||
|
||||
type Widget = inkwell.Widget
|
||||
|
||||
var Manager = inkwell.NewManager[Widget]()
|
||||
|
||||
const (
|
||||
ScreenW = 640
|
||||
ScreenH = 380
|
||||
|
||||
LineH = inkwell.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
|
||||
)
|
||||
|
||||
var (
|
||||
whenPlaying = inkwell.VarEq(inc.VarMode, string(world.ModePlay))
|
||||
whenCutscene = inkwell.VarEq(inc.VarMode, string(world.ModeCutscene))
|
||||
whenUnpaused = inkwell.And(whenPlaying, inkwell.Not(inkwell.VarEq(inc.VarNote, inc.NotePaused)))
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
package widget
|
||||
|
||||
import (
|
||||
inkwell "git.teletypegames.org/engines/inkwell"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Manager.Register(&inkwell.RadialVerbs{
|
||||
Name: "verbs",
|
||||
Trigger: inkwell.MouseButtonRight,
|
||||
Radius: 58,
|
||||
Labels: map[string]string{
|
||||
"look": "Look", "use": "Use", "talk": "Talk", "take": "Take",
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package widget
|
||||
|
||||
import (
|
||||
inkwell "git.teletypegames.org/engines/inkwell"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Manager.Register(&inkwell.SceneNav{
|
||||
Name: "scene_nav",
|
||||
When: whenUnpaused,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package widget
|
||||
|
||||
import (
|
||||
inkwell "git.teletypegames.org/engines/inkwell"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Manager.Register(&inkwell.SpeechBubble{
|
||||
Name: "speech",
|
||||
MaxWidth: 360,
|
||||
Padding: Pad,
|
||||
OffsetY: 8,
|
||||
FallbackY: TopBarH + Pad,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package widget
|
||||
|
||||
import (
|
||||
inkwell "git.teletypegames.org/engines/inkwell"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Manager.Register(&inkwell.StatusLine{
|
||||
Name: "status",
|
||||
When: whenPlaying,
|
||||
Y: statusY,
|
||||
Align: inkwell.AlignLeft,
|
||||
ScreenWidth: DividerX,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package widget
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
inkwell "git.teletypegames.org/engines/inkwell"
|
||||
"git.teletypegames.org/games/realworld/inc"
|
||||
"git.teletypegames.org/games/realworld/inc/tape"
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Manager.Register(&tapeChannel{
|
||||
Name: "channel",
|
||||
Bounds: inkwell.Rect(DividerX+2, HUDTop+2, ScreenW-DividerX-4, ScreenH-HUDTop-4),
|
||||
})
|
||||
}
|
||||
|
||||
type tapeChannel struct {
|
||||
Name string
|
||||
Bounds inkwell.Rectangle
|
||||
}
|
||||
|
||||
func (t *tapeChannel) GetName() string { return t.Name }
|
||||
func (t *tapeChannel) VisibleWhen() inkwell.Condition { return whenPlaying }
|
||||
func (t *tapeChannel) Layer() inkwell.Layer { return inkwell.LayerHUD }
|
||||
|
||||
func (t *tapeChannel) Tick(ctx *inkwell.UICtx) {}
|
||||
|
||||
func (t *tapeChannel) BlocksClickAt(p inkwell.Point) bool { return t.Bounds.Contains(p) }
|
||||
|
||||
func (t *tapeChannel) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {
|
||||
g := ctx.Game
|
||||
th := g.Theme()
|
||||
b := t.Bounds
|
||||
if b.W <= 0 || b.H <= 0 {
|
||||
return
|
||||
}
|
||||
const lh, pad = LineH, Pad
|
||||
|
||||
vector.DrawFilledRect(dst, float32(b.X), float32(b.Y), float32(b.W), float32(b.H), th.ChatLogBG, false)
|
||||
|
||||
speaker, speakerCol := t.lastSpeaker(g, th)
|
||||
g.DrawText(dst, speaker, int(b.X)+pad, int(b.Y)+pad, speakerCol)
|
||||
hy := float32(b.Y+pad+lh) + 2
|
||||
vector.StrokeLine(dst, float32(b.X)+pad, hy,
|
||||
float32(b.X+b.W)-pad, hy, 1, th.CharacterPanelBorder, false)
|
||||
|
||||
type line struct {
|
||||
text string
|
||||
col color.Color
|
||||
}
|
||||
wrapW := int(b.W) - pad*2
|
||||
var rendered []line
|
||||
for _, m := range g.Messages() {
|
||||
var col color.Color
|
||||
var txt string
|
||||
switch m.Kind {
|
||||
case inkwell.LogAction:
|
||||
|
||||
col, txt = th.ChatLogPrompt, m.Text
|
||||
case inkwell.LogResponse:
|
||||
if !tape.Is(m.Speaker) {
|
||||
continue
|
||||
}
|
||||
col, txt = speechColor(g, m.Speaker, th.ChatLogResponse), m.Text
|
||||
default:
|
||||
col, txt = th.ChatLogSystem, m.Text
|
||||
}
|
||||
for _, wl := range inkwell.WrapText(txt, wrapW) {
|
||||
rendered = append(rendered, line{
|
||||
text: wl,
|
||||
col: col,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
top := int(b.Y) + pad + lh + 6
|
||||
maxLines := (int(b.Y+b.H) - pad - top) / lh
|
||||
if maxLines <= 0 {
|
||||
return
|
||||
}
|
||||
if start := len(rendered) - maxLines; start > 0 {
|
||||
rendered = rendered[start:]
|
||||
}
|
||||
for i, ln := range rendered {
|
||||
g.DrawText(dst, ln.text, int(b.X)+pad, top+i*lh, ln.col)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tapeChannel) lastSpeaker(g *inkwell.Game, th inkwell.Theme) (string, color.Color) {
|
||||
msgs := g.Messages()
|
||||
for i := len(msgs) - 1; i >= 0; i-- {
|
||||
m := msgs[i]
|
||||
if m.Kind == inkwell.LogResponse && tape.Is(m.Speaker) {
|
||||
return g.CharacterLabel(m.Speaker), speechColor(g, m.Speaker, th.ChatLogResponse)
|
||||
}
|
||||
}
|
||||
return g.CharacterLabel(inc.Dex), speechColor(g, inc.Dex, th.ChatLogResponse)
|
||||
}
|
||||
|
||||
func speechColor(g *inkwell.Game, name string, fallback color.Color) color.Color {
|
||||
if c, ok := g.CharacterManager.Get(name); ok && c.SpeechColor != nil {
|
||||
return c.SpeechColor
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package widget
|
||||
|
||||
import (
|
||||
inkwell "git.teletypegames.org/engines/inkwell"
|
||||
"git.teletypegames.org/games/realworld/inc"
|
||||
"git.teletypegames.org/games/realworld/inc/tape"
|
||||
"git.teletypegames.org/games/realworld/inc/world"
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Manager.Register(&tapeSlots{
|
||||
Name: "tapes",
|
||||
Bounds: inkwell.Rect(192, slotsY, 194, slotsH),
|
||||
})
|
||||
}
|
||||
|
||||
type tapeSlots struct {
|
||||
Name string
|
||||
Bounds inkwell.Rectangle
|
||||
}
|
||||
|
||||
func (t *tapeSlots) GetName() string { return t.Name }
|
||||
func (t *tapeSlots) VisibleWhen() inkwell.Condition { return whenPlaying }
|
||||
func (t *tapeSlots) Layer() inkwell.Layer { return inkwell.LayerHUD }
|
||||
|
||||
func (t *tapeSlots) BlocksClickAt(p inkwell.Point) bool { return t.Bounds.Contains(p) }
|
||||
|
||||
func (t *tapeSlots) slotRects() (inkwell.Rectangle, inkwell.Rectangle) {
|
||||
b := t.Bounds
|
||||
w := (b.W - Pad) / 2
|
||||
return inkwell.Rect(b.X, b.Y, w, b.H), inkwell.Rect(b.X+w+Pad, b.Y, w, b.H)
|
||||
}
|
||||
|
||||
func (t *tapeSlots) Tick(ctx *inkwell.UICtx) {
|
||||
g := ctx.Game
|
||||
mp := g.Input.Point()
|
||||
r1, r2 := t.slotRects()
|
||||
|
||||
switch {
|
||||
case r1.Contains(mp):
|
||||
g.SetHoverLabel(g.CharacterLabel(inc.Dex))
|
||||
case r2.Contains(mp):
|
||||
if s := world.Slot2(); s != "" {
|
||||
g.SetHoverLabel(itemLabel(g, s))
|
||||
} else {
|
||||
g.SetHoverLabel("empty tape slot")
|
||||
}
|
||||
}
|
||||
|
||||
if !g.Input.LeftClicked() {
|
||||
return
|
||||
}
|
||||
if !r1.Contains(mp) && !r2.Contains(mp) {
|
||||
return
|
||||
}
|
||||
g.Input.ConsumeLeft()
|
||||
|
||||
slot2 := r2.Contains(mp)
|
||||
sel := g.Inventory.Selected()
|
||||
|
||||
if slot2 && sel != "" {
|
||||
g.Inventory.Select("")
|
||||
if tape.IsItem(sel) {
|
||||
world.SetPending(sel)
|
||||
world.Do(inkwell.RunScript(inc.ScriptTapeInsert))
|
||||
return
|
||||
}
|
||||
world.Do(inkwell.Say(inc.Dex, "That isn't a tape, Paul. That's an object. There is a difference."))
|
||||
return
|
||||
}
|
||||
if !slot2 && sel != "" {
|
||||
g.Inventory.Select("")
|
||||
world.Do(inkwell.Say(inc.Dex, "The first slot is mine. I'm not moving."))
|
||||
return
|
||||
}
|
||||
|
||||
switch g.SelectedVerb() {
|
||||
case "talk":
|
||||
if !slot2 {
|
||||
world.Do(world.Paused(inkwell.RunDialogue(inc.DlgDex)))
|
||||
return
|
||||
}
|
||||
if s := world.Slot2(); s != "" {
|
||||
world.Do(world.Paused(inkwell.RunDialogue(tape.Dialogue(s))))
|
||||
return
|
||||
}
|
||||
world.Do(inkwell.Say(inc.Dex, "Empty. Nobody to talk to."))
|
||||
default:
|
||||
if !slot2 {
|
||||
world.Do(inkwell.Say(inc.Dex, "Me. Four kilobytes of a dead man. Be grateful."))
|
||||
return
|
||||
}
|
||||
if s := world.Slot2(); s != "" {
|
||||
world.Do(inkwell.Say(inc.Dex, "That is the second slot. Be careful what you let in."))
|
||||
return
|
||||
}
|
||||
world.Do(inkwell.Say(inc.Dex, "The second slot is empty. That's the rarer state."))
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tapeSlots) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {
|
||||
g := ctx.Game
|
||||
th := g.Theme()
|
||||
r1, r2 := t.slotRects()
|
||||
|
||||
drawSlot := func(r inkwell.Rectangle, label, body string, filled bool) {
|
||||
bg, border, col := th.InventorySlotBG, th.ChatLogSystem, th.ChatLogSystem
|
||||
if filled {
|
||||
bg, border, col = th.PanelBG, th.CharacterPanelBorder, th.ChatLogResponse
|
||||
}
|
||||
vector.DrawFilledRect(dst, float32(r.X), float32(r.Y), float32(r.W), float32(r.H), bg, false)
|
||||
vector.StrokeRect(dst, float32(r.X), float32(r.Y), float32(r.W), float32(r.H), 1, border, false)
|
||||
|
||||
x, inner := int(r.X)+Pad, int(r.W)-Pad*2
|
||||
g.DrawText(dst, label, x, int(r.Y)+Pad/2, th.ChatLogSystem)
|
||||
g.DrawText(dst, inkwell.ClipText(body, inner), x, int(r.Y)+Pad/2+LineH, col)
|
||||
}
|
||||
|
||||
drawSlot(r1, "SLOT 1", g.CharacterLabel(inc.Dex), true)
|
||||
if s := world.Slot2(); s != "" {
|
||||
drawSlot(r2, "SLOT 2", itemLabel(g, s), true)
|
||||
} else {
|
||||
drawSlot(r2, "SLOT 2", "empty", false)
|
||||
}
|
||||
}
|
||||
|
||||
func itemLabel(g *inkwell.Game, name string) string {
|
||||
if it, ok := g.ItemManager.Get(name); ok && it.Description != "" {
|
||||
return it.Description
|
||||
}
|
||||
return name
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package widget
|
||||
|
||||
import (
|
||||
inkwell "git.teletypegames.org/engines/inkwell"
|
||||
"git.teletypegames.org/games/realworld/inc"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Manager.Register(&inkwell.TopBar{
|
||||
Name: "topbar",
|
||||
When: whenPlaying,
|
||||
Height: TopBarH,
|
||||
TimeVar: inc.VarArmillaStrip,
|
||||
NoteVar: inc.VarNote,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user