98 lines
2.6 KiB
Go
98 lines
2.6 KiB
Go
package pncdsl
|
|
|
|
import (
|
|
"image/color"
|
|
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
"github.com/hajimehoshi/ebiten/v2/vector"
|
|
)
|
|
|
|
// dialogBox is the active-conversation UI: it shows the current node's
|
|
// lines (one at a time, advanced on click) followed by the visible choices.
|
|
type dialogBox struct {
|
|
dialogue *Dialogue
|
|
node *DialogueNode
|
|
lineIdx int // -1 = lines done, showing choices
|
|
choiceHits []Rectangle
|
|
}
|
|
|
|
func newDialogBox(d *Dialogue, n *DialogueNode) *dialogBox {
|
|
return &dialogBox{dialogue: d, node: n, lineIdx: 0}
|
|
}
|
|
|
|
// resolveChoices returns the list of choices currently visible to the player.
|
|
func (db *dialogBox) resolveChoices(ctx *Ctx) []DialogueChoice {
|
|
out := make([]DialogueChoice, 0, len(db.node.Choices))
|
|
for _, c := range db.node.Choices {
|
|
if c.Show != nil && !c.Show.Eval(ctx) {
|
|
continue
|
|
}
|
|
if c.Once && ctx.Game.State.Talked(db.node.Name+":"+c.Text) > 0 {
|
|
continue
|
|
}
|
|
out = append(out, c)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// handleClick advances the dialog state. Returns the picked choice (if any).
|
|
func (db *dialogBox) handleClick(ctx *Ctx, p Point) (picked *DialogueChoice) {
|
|
if db.lineIdx >= 0 && db.lineIdx < len(db.node.Lines) {
|
|
db.lineIdx++
|
|
if db.lineIdx >= len(db.node.Lines) {
|
|
db.lineIdx = -1
|
|
db.choiceHits = nil
|
|
}
|
|
return nil
|
|
}
|
|
for i, r := range db.choiceHits {
|
|
if r.Contains(p) {
|
|
ch := db.resolveChoices(ctx)[i]
|
|
return &ch
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (db *dialogBox) currentLine() (DialogueLine, bool) {
|
|
if db.lineIdx >= 0 && db.lineIdx < len(db.node.Lines) {
|
|
return db.node.Lines[db.lineIdx], true
|
|
}
|
|
return DialogueLine{}, false
|
|
}
|
|
|
|
func (db *dialogBox) draw(dst *ebiten.Image, g *Game) {
|
|
// box across the bottom 60 px
|
|
vector.DrawFilledRect(dst, 0, 140, 320, 60, color.RGBA{15, 15, 25, 240}, false)
|
|
vector.StrokeRect(dst, 0, 140, 320, 60, 1, color.RGBA{80, 80, 110, 255}, false)
|
|
|
|
if ln, ok := db.currentLine(); ok {
|
|
speakerCol := color.RGBA{220, 220, 120, 255}
|
|
drawText(dst, ln.Speaker+":", 6, 142, speakerCol)
|
|
lines := wrapText(ln.Text, 300)
|
|
for i, l := range lines {
|
|
drawText(dst, l, 6, 158+i*14, color.White)
|
|
}
|
|
return
|
|
}
|
|
|
|
// choices
|
|
ctx := g.makeCtx()
|
|
choices := db.resolveChoices(ctx)
|
|
db.choiceHits = db.choiceHits[:0]
|
|
for i, c := range choices {
|
|
y := 144 + i*14
|
|
r := Rect(6, float64(y), 308, 13)
|
|
db.choiceHits = append(db.choiceHits, r)
|
|
mx, my := g.input.Pos()
|
|
hover := r.Contains(Point{X: float64(mx), Y: float64(my)})
|
|
bg := color.RGBA{30, 30, 45, 255}
|
|
if hover {
|
|
bg = color.RGBA{70, 70, 100, 255}
|
|
}
|
|
vector.DrawFilledRect(dst, float32(r.X), float32(r.Y), float32(r.W), float32(r.H), bg, false)
|
|
drawText(dst, c.Text, int(r.X)+2, int(r.Y)-1, color.White)
|
|
_ = i
|
|
}
|
|
}
|