diff --git a/README.md b/README.md index 14ee398..4cdc191 100644 --- a/README.md +++ b/README.md @@ -296,12 +296,29 @@ register into them instead unless that is what you want. ```go func (g *Game) StartAt(name string) *Game // entry scene -func (g *Game) OnStart(a Action) *Game // action run after the scene's OnEnter +func (g *Game) OnStart(script string) *Game // script run after the scene's OnEnter +func (g *Game) OnFinale(script string) *Game // closing script, queued after OnStart func (g *Game) Validate() error // cross-check name references func (g *Game) Run() error // same as inkwell.Run(g) ``` -`StartAt` and `OnStart` return `*Game` so they chain at the end of `Build`. +`OnStart` and `OnFinale` name registered scripts rather than taking an action, +so the opening a game plays is content like any other — a `Script` in the +`ScriptManager`, reachable by name and editable without touching the wiring. +`Validate` rejects a name that is not registered. + +`OnFinale` is queued directly after the start script, which is what a "boot +straight into the ending" debug flag wants: + +```go +g.StartAt(start) +g.OnStart(ScriptOpening) +if finale { + g.OnFinale(ScriptFinale) +} +``` + +All three return `*Game` so they chain at the end of `Build`. ### 4.3 Theme accessors @@ -1540,8 +1557,8 @@ func Run(g *Game) error // toplevel — same as g.Run() 3. If `UIManager` is empty, call `RegisterDefaultUI(g)`. 4. Place the start scene directly (no transition), bump `State.NoteVisit`, position registered actors, kick off music. -5. Compose `Seq(scene.OnEnter, game.OnStart)` and queue it as the initial - action — the first script tick runs both in order. +5. Compose `Seq(scene.OnEnter, OnStart script, OnFinale script)` and queue + it as the initial action — the first script tick runs them in order. 6. `ebiten.SetWindowSize(Width*4, Height*4)`, `ebiten.SetWindowTitle(g.Title)`, then `ebiten.RunGame(&engine{g})`. diff --git a/core.dsl.go b/core.dsl.go index 29a1cf0..691c145 100644 --- a/core.dsl.go +++ b/core.dsl.go @@ -34,8 +34,11 @@ func Run(g *Game) error { if s.OnEnter != nil { seq = append(seq, s.OnEnter) } - if g.onStart != nil { - seq = append(seq, g.onStart) + if g.onStart != "" { + seq = append(seq, RunScript(g.onStart)) + } + if g.onFinale != "" { + seq = append(seq, RunScript(g.onFinale)) } if len(seq) > 0 { g.queueAction(Seq(seq...), "init") diff --git a/core.game.go b/core.game.go index 8e1bd9b..f8d0971 100644 --- a/core.game.go +++ b/core.game.go @@ -42,7 +42,8 @@ type Game struct { Input *Input startID string - onStart Action + onStart string + onFinale string activeTheme string // runtime @@ -190,7 +191,15 @@ func (g *Game) SceneHotspots(name string) []Hotspot { g.exitHotspots[name] = hs return hs } -func (g *Game) OnStart(a Action) *Game { g.onStart = a; return g } + +// OnStart names the script queued after the start scene's OnEnter, once, +// when the game boots. +func (g *Game) OnStart(script string) *Game { g.onStart = script; return g } + +// OnFinale names the game's closing script, queued directly after the start +// script — which is what a "boot straight into the ending" debug flag wants. +// Leave it unset for an ordinary run. +func (g *Game) OnFinale(script string) *Game { g.onFinale = script; return g } // ----- theme + UI conveniences ------------------------------------------ @@ -327,6 +336,11 @@ func (g *Game) Validate() error { } } } + for _, name := range []string{g.onStart, g.onFinale} { + if name != "" && !g.ScriptManager.Has(name) { + return fmt.Errorf("%w: %q", ErrUnknownScript, name) + } + } if g.activeTheme == "" || !g.ThemeManager.Has(g.activeTheme) { return fmt.Errorf("inkwell: no active theme (got %q)", g.activeTheme) }