remove demo game
This commit is contained in:
134
ui.dialog_box.go
Normal file
134
ui.dialog_box.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package pncdsl
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||
)
|
||||
|
||||
// DialogBox renders the active conversation: one line at a time (advanced
|
||||
// on click) followed by the player's choices. The widget is dormant
|
||||
// unless ctx.Game.dialogueActive() — and while active it consumes input
|
||||
// so nothing underneath reacts.
|
||||
type DialogBox struct {
|
||||
Name string
|
||||
Bounds Rectangle
|
||||
LineHeight int
|
||||
Padding int
|
||||
}
|
||||
|
||||
func (d *DialogBox) GetName() string { return d.Name }
|
||||
|
||||
func (d *DialogBox) Tick(ctx *UICtx) {
|
||||
g := ctx.Game
|
||||
if !g.dialogueActive() {
|
||||
return
|
||||
}
|
||||
// while a dialog is open, ALL clicks belong to it
|
||||
if g.Input.RightClicked() {
|
||||
g.Input.ConsumeRight()
|
||||
}
|
||||
if !g.Input.LeftClicked() {
|
||||
return
|
||||
}
|
||||
g.Input.ConsumeLeft()
|
||||
mp := g.Input.Point()
|
||||
|
||||
dlg := g.dialog
|
||||
if dlg.LineIdx >= 0 && dlg.LineIdx < len(dlg.Node.Lines) {
|
||||
dlg.LineIdx++
|
||||
if dlg.LineIdx >= len(dlg.Node.Lines) {
|
||||
dlg.LineIdx = -1
|
||||
dlg.ChoiceHits = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
// pick the clicked choice
|
||||
for i, r := range dlg.ChoiceHits {
|
||||
if r.Contains(mp) {
|
||||
choice := d.resolveChoices(ctx)[i]
|
||||
if choice.Once {
|
||||
g.State.NoteTalked(dlg.Node.Name + ":" + choice.Text)
|
||||
}
|
||||
if len(choice.Actions) > 0 {
|
||||
g.queueAction(Seq(choice.Actions...), "choice")
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DialogBox) resolveChoices(ctx *UICtx) []DialogueChoice {
|
||||
g := ctx.Game
|
||||
dlg := g.dialog
|
||||
out := make([]DialogueChoice, 0, len(dlg.Node.Choices))
|
||||
for _, c := range dlg.Node.Choices {
|
||||
gctx := g.makeCtx()
|
||||
if c.Show != nil && !c.Show.Eval(gctx) {
|
||||
continue
|
||||
}
|
||||
if c.Once && g.State.Talked(dlg.Node.Name+":"+c.Text) > 0 {
|
||||
continue
|
||||
}
|
||||
out = append(out, c)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// BlocksClickAt — when a dialog is open, every click is ours.
|
||||
func (d *DialogBox) BlocksClickAt(p Point) bool {
|
||||
// without ctx we can't know if a dialog is active; conservatively block
|
||||
// when the cursor is in the dialog bounds (used only by RadialVerbs).
|
||||
b := d.Bounds
|
||||
if b.W == 0 {
|
||||
return false
|
||||
}
|
||||
return b.Contains(p)
|
||||
}
|
||||
|
||||
func (d *DialogBox) Draw(dst *ebiten.Image, ctx *UICtx) {
|
||||
g := ctx.Game
|
||||
if !g.dialogueActive() {
|
||||
return
|
||||
}
|
||||
th := g.Theme()
|
||||
b := d.Bounds
|
||||
if b.W == 0 {
|
||||
b = Rect(0, 140, float64(g.Width), 60)
|
||||
}
|
||||
pad := d.Padding
|
||||
if pad <= 0 {
|
||||
pad = 6
|
||||
}
|
||||
lh := d.LineHeight
|
||||
if lh <= 0 {
|
||||
lh = 14
|
||||
}
|
||||
|
||||
vector.DrawFilledRect(dst, float32(b.X), float32(b.Y), float32(b.W), float32(b.H), th.DialogBG, false)
|
||||
vector.StrokeRect(dst, float32(b.X), float32(b.Y), float32(b.W), float32(b.H), 1, th.DialogBorder, false)
|
||||
|
||||
dlg := g.dialog
|
||||
if dlg.LineIdx >= 0 && dlg.LineIdx < len(dlg.Node.Lines) {
|
||||
ln := dlg.Node.Lines[dlg.LineIdx]
|
||||
g.DrawText(dst, ln.Speaker+":", int(b.X)+pad, int(b.Y)+2, th.DialogSpeaker)
|
||||
lines := wrapText(ln.Text, int(b.W)-pad*2)
|
||||
for i, l := range lines {
|
||||
g.DrawText(dst, l, int(b.X)+pad, int(b.Y)+18+i*lh, th.DialogText)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
choices := d.resolveChoices(ctx)
|
||||
dlg.ChoiceHits = dlg.ChoiceHits[:0]
|
||||
mp := g.Input.Point()
|
||||
for i, c := range choices {
|
||||
r := Rect(b.X+float64(pad), b.Y+4+float64(i*lh), b.W-float64(pad)*2, float64(lh-1))
|
||||
dlg.ChoiceHits = append(dlg.ChoiceHits, r)
|
||||
bg := th.DialogChoiceBG
|
||||
if r.Contains(mp) {
|
||||
bg = th.DialogChoiceHover
|
||||
}
|
||||
vector.DrawFilledRect(dst, float32(r.X), float32(r.Y), float32(r.W), float32(r.H), bg, false)
|
||||
g.DrawText(dst, c.Text, int(r.X)+2, int(r.Y)-1, th.DialogText)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user