initial commit
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user