screen to scene
ci/woodpecker/push/ebitengine Pipeline was successful

This commit is contained in:
2026-08-30 00:15:26 +02:00
parent 94649a38fe
commit da1410cb4a
62 changed files with 623 additions and 615 deletions
+25 -24
View File
@@ -48,12 +48,12 @@ This holds for nested literals too, including small ones such as
All game code lives in one flat package, `inc`. There are no subdirectories: a All game code lives in one flat package, `inc`. There are no subdirectories: a
file's name carries the structure, in the form `[category].[name].go`. file's name carries the structure, in the form `[category].[name].go`.
- The category is always **singular**: `item`, `screen`, `background`, - The category is always **singular**: `item`, `scene`, `background`,
`character`, `dialog`, `script`, `world`, `ui`, `theme`, `names`, `boot`, `character`, `dialog`, `script`, `world`, `ui`, `theme`, `names`, `boot`,
`content`. `content`.
- `[category].manager.go` is the file that ties a category together — its - `[category].manager.go` is the file that ties a category together — its
manager, its entity type, whatever the category shares. manager, its entity type, whatever the category shares.
- Every other file in a category holds exactly one entity: one screen, one - Every other file in a category holds exactly one entity: one scene, one
background, one item. The file registers it itself, in an `init()`. background, one item. The file registers it itself, in an `init()`.
``` ```
@@ -70,14 +70,14 @@ inc/world.action.go custom actions and the action pump
inc/ui.manager.go HUD layout and widget registration inc/ui.manager.go HUD layout and widget registration
inc/ui.*.go custom widgets, coloured text inc/ui.*.go custom widgets, coloured text
inc/content.manager.go composition root; calls every register… inc/content.manager.go composition root; calls every register…
inc/background.*.go one image asset per screen inc/background.*.go one image asset per scene
inc/character.*.go the cast, tapes included inc/character.*.go the cast, tapes included
inc/item.*.go inventory inc/item.*.go inventory
inc/dialog.*.go dialogue trees inc/dialog.*.go dialogue trees
inc/script.*.go named action sequences inc/script.*.go named action sequences
inc/screen.manager.go the Screen type, the deck, the scene builder inc/scene.manager.go the Scene alias, the defaults every scene gets
inc/screen.exit.go the connections between screens inc/scene.selector.go the map screen: its pins are derived from the graph
inc/screen.*.go one file per screen inc/scene.*.go one file per scene
``` ```
`manager` is the one category with no entities of its own: it holds the registry `manager` is the one category with no entities of its own: it holds the registry
@@ -101,7 +101,7 @@ var CharacterManager = NewManager(func(entity Character) string { return entity.
That is the whole of a category's manager file — an alias and one line. There That is the whole of a category's manager file — an alias and one line. There
are seven managers: `BackgroundManager`, `CharacterManager`, `DialogManager`, are seven managers: `BackgroundManager`, `CharacterManager`, `DialogManager`,
`ItemManager`, `ScriptManager`, `ScreenManager`, `ThemeManager`. `ItemManager`, `ScriptManager`, `SceneManager`, `ThemeManager`.
`manager.interface.go` holds the contract they all keep, and asserts each one `manager.interface.go` holds the contract they all keep, and asserts each one
against it. A new manager goes on that list. against it. A new manager goes on that list.
@@ -118,8 +118,9 @@ type ManagerInterface[T any] interface {
twice is an update, never a duplicate. `GetAll` returns the slice itself, in twice is an update, never a duplicate. `GetAll` returns the slice itself, in
registration order. registration order.
`Screen` is the one entity type that is not an alias: a screen carries exits and Every entity type is an alias, `Scene` included. It was once a struct of our
an `OnSelector` flag that inkwell's `Scene` knows nothing about. own, because inkwell's `Scene` could not carry exits; that gap was closed in the
engine, so there is nothing left for a second type to hold.
### Entities register themselves ### Entities register themselves
@@ -144,7 +145,7 @@ variables are initialised before any `init()` runs, so the managers exist by the
time the first file registers into one. time the first file registers into one.
`init()` order is file-name order, so **registration order is alphabetical**. `init()` order is file-name order, so **registration order is alphabetical**.
Nothing may depend on it — including the order the arrow keys walk the screens, Nothing may depend on it — including the order the arrow keys walk the scenes,
which is simply the order the files sit in. which is simply the order the files sit in.
### Handing a category to the engine ### Handing a category to the engine
@@ -159,13 +160,13 @@ func registerContent() {
registerAll(ItemManager, World.G.ItemManager.Register) registerAll(ItemManager, World.G.ItemManager.Register)
registerAll(DialogManager, World.G.DialogueManager.Register) registerAll(DialogManager, World.G.DialogueManager.Register)
registerAll(ScriptManager, World.G.ScriptManager.Register) registerAll(ScriptManager, World.G.ScriptManager.Register)
registerScreen() registerScene()
} }
``` ```
`registerScreen` is the one that needs its own function, because a screen has to `registerScene` is the one that needs its own function: it fills in the defaults
be turned into an `inkwell.Scene` first, and because the selector's pins are a scene may leave out (Paul's starting position, the floor walkbox), and it
derived from whichever screens marked themselves `OnSelector`. derives the selector's pins by reading the exit graph backwards.
## The world ## The world
@@ -185,7 +186,7 @@ One package means one namespace, so an entity's constructor carries its
category as a prefix: category as a prefix:
```go ```go
screenFloor screenBuild registerScreen fillSelectorPins sceneFloor sceneDefaults registerScene fillSelectorPins
``` ```
Entities themselves need no name at all — they are anonymous literals inside Entities themselves need no name at all — they are anonymous literals inside
@@ -198,12 +199,12 @@ constants in `names.manager.go`, the colour tokens, and the action constructors
Everything else is machinery and stays unexported: the HUD widgets Everything else is machinery and stays unexported: the HUD widgets
(`tapeSlots`, `letterbox`, `hudFrame`, …), the `register…` functions, the (`tapeSlots`, `letterbox`, `hudFrame`, …), the `register…` functions, the
runners, `screenBuild`, `exit`. runners, `sceneDefaults`, `fillSelectorPins`.
The engine's word for a screen is `Scene`. Ours is **screen**, because the The word is **scene**, the engine's own. The wiki and the concept-art deck count
wiki, the concept-art deck and the beat tables all count screens. "Scene" *screens*, and this code used to as well, but everything a screen had that a
survives only where the code talks to the engine (`EnterScene`, scene did not now lives in inkwell. "Screen" survives only where it means the
`SceneManager`). display: `ScreenW`, `ScreenH`.
## Layering ## Layering
@@ -219,10 +220,10 @@ names ← theme ← world ← ui
`world` knows nothing about the HUD or the content. Both build on it, never the `world` knows nothing about the HUD or the content. Both build on it, never the
other way round. other way round.
## Adding a screen ## Adding a scene
1. Constants in `inc/names.manager.go`: `Screen<Name>`, `Bg<Name>` 1. Constants in `inc/names.manager.go`: `Scene<Name>`, `Bg<Name>`
2. `inc/screen.<name>.go` — an `init()` registering a `Screen` 2. `inc/scene.<name>.go` — an `init()` registering a `Scene`
3. `inc/background.<name>.go` — an `init()` registering a `Background` 3. `inc/background.<name>.go` — an `init()` registering a `Background`
4. A 640×380 PNG in `assets/bg/` 4. A 640×380 PNG in `assets/bg/`
@@ -234,7 +235,7 @@ Nothing else moves. There is no list to update.
make build native binary into bin/ make build native binary into bin/
make wasm js/wasm build into dist/ make wasm js/wasm build into dist/
make watch rebuild on change make watch rebuild on change
go run . -screen <name> start on a given screen go run . -scene <name> start on a given scene
go run . -finale start with the finale go run . -finale start with the finale
``` ```
+47 -39
View File
@@ -25,11 +25,11 @@ go run .
```bash ```bash
go run . # the alley (beat 3) go run . # the alley (beat 3)
go run . -finale # the finale — try the Nokia-punk theme switch go run . -finale # the finale — try the Nokia-punk theme switch
go run . -screen selector # the location selector: the map the city hangs off go run . -scene selector # the location selector: the map the city hangs off
go run . -screen hospital # any screen in the deck, e.g. to look at the art go run . -scene hospital # any scene in the deck, e.g. to look at the art
``` ```
Screen names are the constants in `names.manager.go` (`selector`, `paul_shop`, Scene names are the constants in `names.manager.go` (`selector`, `paul_shop`,
`noodle_house`, `alley`, `norman_apartment`, `police_station`, `hackerspace`, `noodle_house`, `alley`, `norman_apartment`, `police_station`, `hackerspace`,
`ice_cream_shop`, `trinket_shop`, `small_restaurant`, `secret_club`, `ice_cream_shop`, `trinket_shop`, `small_restaurant`, `secret_club`,
`bbs_terminal`, `street`, `hospital`, `server_farm`, `secret_lab`, `bbs_terminal`, `street`, `hospital`, `server_farm`, `secret_lab`,
@@ -62,12 +62,12 @@ Release metadata lives in `metadata.json`.
|---|---| |---|---|
| left click | run the selected verb | | left click | run the selected verb |
| right click | verb coin (Look / Use / Talk / Take) | | right click | verb coin (Look / Use / Talk / Take) |
| `←` `→` | previous / next screen, in file order, wrapping | | `←` `→` | previous / next scene, in file order, wrapping |
| `F1` | toggle hotspot outlines | | `F1` | toggle hotspot outlines |
| `SPACE` | during a cutscene: let the tape speak, if it offered | | `SPACE` | during a cutscene: let the tape speak, if it offered |
The screens are connected to each other (see **The map** below); the arrow keys The scenes are connected to each other (see **The map** below); the arrow keys
are a reviewing tool on top of that, walking every screen in turn. The walk is are a reviewing tool on top of that, walking every scene in turn. The walk is
inert during a cutscene, a menu, or a stopped world, so it can never cut inert during a cutscene, a menu, or a stopped world, so it can never cut
across an authored beat. across an authored beat.
@@ -181,15 +181,15 @@ Content is one registered entity per file, and the category prefix groups them
the way directories used to: the way directories used to:
``` ```
background.*.go one file per screen: background.paul_shop.go, … background.*.go one file per scene: background.paul_shop.go, …
character.*.go character.paul.go character.dex.go character.mystery_tape.go character.*.go character.paul.go character.dex.go character.mystery_tape.go
item.*.go item.noodle_letter.go item.black_market_armilla.go … item.*.go item.noodle_letter.go item.black_market_armilla.go …
dialog.*.go dialog.dex_talk.go dialog.mystery_tape_silent.go dialog.*.go dialog.dex_talk.go dialog.mystery_tape_silent.go
script.*.go script.tape_insert.go script.awakening_finale.go script.*.go script.tape_insert.go script.awakening_finale.go
screen.*.go one file per screen: screen.alley.go, … + screen.exit.go scene.*.go one file per scene: scene.alley.go, … + scene.selector.go
``` ```
Adding a screen means adding `screen.<name>.go` and `background.<name>.go`. Adding a scene means adding `scene.<name>.go` and `background.<name>.go`.
Nothing else moves. Nothing else moves.
Every category owns a manager, and they are all the same generic type, Every category owns a manager, and they are all the same generic type,
@@ -229,13 +229,13 @@ func init() {
So adding an entity is adding a file, and there is no second list to keep in So adding an entity is adding a file, and there is no second list to keep in
step. The price is that registration order is file-name order: the arrow keys step. The price is that registration order is file-name order: the arrow keys
walk the screens alphabetically rather than in the concept-art deck's order. walk the scenes alphabetically rather than in the concept-art deck's order.
That was a deliberate trade — the deck order was a list that had to be kept in That was a deliberate trade — the deck order was a list that had to be kept in
step with the files by hand, and the deck is a thing to look at, not a thing to step with the files by hand, and the deck is a thing to look at, not a thing to
play through. play through.
The game's own catalogue is readable without going through the engine: The game's own catalogue is readable without going through the engine:
`ScreenManager.GetByName("alley")` answers before a single scene has been handed `SceneManager.GetByName("alley")` answers before a single scene has been handed
over. `registerContent` is where the hand-off happens, one `registerAll` call over. `registerContent` is where the hand-off happens, one `registerAll` call
per category. per category.
@@ -244,51 +244,63 @@ a `*world` parameter and no widget holds a back-reference, which is what lets a
script file be a literal — the tape-insert script closes over `World` rather script file be a literal — the tape-insert script closes over `World` rather
than over a parameter someone would have had to thread to it. than over a parameter someone would have had to thread to it.
**On the word "screen".** inkwell's entity is called a `Scene`, and that is the **On the word "scene".** The wiki, the concept-art deck and the beat tables all
type every `screen.*.go` file returns — but the wiki, the concept-art deck and count *screens*, and this code used to as well: it carried its own `Screen`
the beat tables all count *screens*, so the category, the files and the flag say struct, because inkwell's `Scene` could not hold the things a screen needs —
screen. "Scene" survives only where the code is talking to the engine its exits above all. Those went into the engine instead (inkwell `Exit`,
(`EnterScene`, `SceneManager`). `Game.SceneRect`, `CurrentScene`/`PreviousScene`, `Back()`), and with the gap
closed there was nothing left for a second type to carry. The code says scene,
because that is what the thing is. "Screen" survives only where it means the
display: `ScreenW`, `ScreenH`.
Art lives in `assets/bg/` and is embedded into the binary (`main.go`), because Art lives in `assets/bg/` and is embedded into the binary (`main.go`), because
js/wasm has no OS filesystem and `make binaries` packages the executable alone. js/wasm has no OS filesystem and `make binaries` packages the executable alone.
## The map ## The map
Where a screen leads is recorded **in that screen**, in its own file, and it Where a scene leads is recorded **in that scene**, in its own file, and it
comes from the wiki's location graph comes from the wiki's location graph
(`projects/realworld/story#helyszín-gráf-vázlat`). That graph has two kinds of (`projects/realworld/story#helyszín-gráf-vázlat`). That graph has two kinds of
edge and so does the code: edge and so does the code:
- **Physical adjacency** — the alley is behind Noodle's house, the eating place - **Physical adjacency** — the alley is behind Noodle's house, the eating place
is next door to the trinket shop, the club is through its back room, the roof is next door to the trinket shop, the club is through its back room, the roof
is up Norman's stairs. Declared with `to:` in the screen's `exits`. is up Norman's stairs. Declared with `To:` in the scene's `Exits`.
- **The selector** — the wiki centres its map on a *Helyszínválasztó*, "nem - **The selector** — the wiki centres its map on a *Helyszínválasztó*, "nem
valódi helyszín, hanem a menü-képernyő": a screen every main location connects valódi helyszín, hanem a menü-képernyő": a scene every main location connects
to both ways. A screen marks itself `onSelector: true`; `screen/selector.go` to both ways. A main location lists `exitToSelector` among its exits, and
builds the other half of each of those edges, so the list of locations on the `scene.selector.go` derives the other half of each edge by reading the graph
map is never written down twice. backwards — every scene with an exit to the selector gets a pin on it. The
list of locations on the map is never written down twice.
The wiki's conditional arrows survive too: `needs:` holds the flag an exit waits The wiki's conditional arrows survive too: `Needs:` holds the flag an exit waits
for, which is how *Bolt →|beengedés| Klub* is expressed — the way into the club for, which is how *Bolt →|beengedés| Klub* is expressed — the way into the club
is visible and named from the first visit, and stays shut until the shopkeeper is visible and named from the first visit, and stays shut until the shopkeeper
has been shown the underwater sun. has been shown the underwater sun.
Until the doors are measured on the paintings, an exit is a strip along one edge Until the doors are measured on the paintings, an exit is a strip along one edge
of the picture (`sideLeft`, `sideRight`, `sideBack`, `sideNear`) — the of the picture (`inkwell.ExitLeft`, `ExitRight`, `ExitBack`, `ExitNear`), sized
convention every 1990s point & click used, and one field to re-aim later. A from `Game.SceneRect` so it lands inside the painting rather than under the HUD
screen reached from several rooms has no fixed way out: the BBS terminal is the — the convention every 1990s point & click used, and one field to re-aim later.
same terminal from the club, the flat or the roof, so its exit is `back`. A scene reached from several rooms has no fixed way out: the BBS terminal is the
same terminal from the club, the flat or the roof, so its exit leaves `To` empty
and the engine binds it to `Back()`.
The graph stays machine-readable: exit hotspots are named `exit:<target>`, so The graph stays machine-readable: exit hotspots are named `exit:<target>`, so
the connections can be read straight back out of the registered screens. the connections can be read straight back out of the registered scenes — and
`Validate` rejects an exit that names a scene nobody registered.
## Engine workarounds ## Engine workarounds
Five inkwell limits turned up during implementation that the engine README does Four inkwell limits turned up during implementation that the engine README does
not mention. All five are worked around on the domain side; each is a candidate not mention. All four are worked around on the domain side; each is a candidate
for a small engine change. for a small engine change.
Two others have been fixed in the engine since: exits are now
[`inkwell.Exit`](https://git.teletypegames.org/engines/inkwell) on the scene
itself, and `Game.CurrentScene()` / `PreviousScene()` mean the domain no longer
has to shadow where the player is.
1. **`drawText` discards colour.** In `asset.text.go` the colour argument is 1. **`drawText` discards colour.** In `asset.text.go` the colour argument is
`_ = c` and rendering goes through `ebitenutil.DebugPrintAt`, which only `_ = c` and rendering goes through `ebitenutil.DebugPrintAt`, which only
draws white. Every text colour in `Theme` is therefore inert. *Workaround:* draws white. Every text colour in `Theme` is therefore inert. *Workaround:*
@@ -308,16 +320,12 @@ for a small engine change.
`handleSceneInput` and can consume the click, so unauthored pairs fail in `handleSceneInput` and can consume the click, so unauthored pairs fail in
character instead, escalating on repeats. character instead, escalating on repeats.
4. **No exported `CurrentScene()`**, but the pump needs `Ctx.Scene`. 4. **Widgets have no `Visible` field and the `Manager` cannot unregister**, so
*Workaround:* `EnterScene` is the first step of every scene's
`OnEnter`; the domain tracks it.
5. **Widgets have no `Visible` field and the `Manager` cannot unregister**, so
the built-in HUD cannot be hidden during a cutscene. *Workaround:* the the built-in HUD cannot be hidden during a cutscene. *Workaround:* the
`gate` wrapper in `ui/ui.go` forwards `Tick`/`Draw`/`BlocksClickAt` only `gate` wrapper in `ui/ui.go` forwards `Tick`/`Draw`/`BlocksClickAt` only
while the HUD is visible. while the HUD is visible.
6. **`Run` hardcodes a 4× window** (`core.dsl.go`), which at 640×400 would be 5. **`Run` hardcodes a 4× window** (`core.dsl.go`), which at 640×400 would be
2560×1600 — bigger than most laptop screens. *Workaround:* the `windowSizer` 2560×1600 — bigger than most laptop screens. *Workaround:* the `windowSizer`
widget resizes once on the first tick, since Run sets the size before widget resizes once on the first tick, since Run sets the size before
entering the loop. entering the loop.
@@ -332,15 +340,15 @@ Also: the inkwell README gives the module path as
stretched, but the strip is opaque and 118px of the 380 sit on top of the stretched, but the strip is opaque and 118px of the 380 sit on top of the
picture. Either the deck gets recut so nothing that matters lives in its picture. Either the deck gets recut so nothing that matters lives in its
bottom third, or the HUD gets a translucent panel treatment. bottom third, or the HUD gets a translucent panel treatment.
- **The screens are empty.** 22 backgrounds are in and they are wired to each - **The scenes are empty.** 22 backgrounds are in and they are wired to each
other, but only the alley has hotspots, NPCs and dialogue; everywhere else other, but only the alley has hotspots, NPCs and dialogue; everywhere else
there is nothing to do but leave again. there is nothing to do but leave again.
- **The selector has no art.** The wiki's Helyszínválasztó is the centre of the - **The selector has no art.** The wiki's Helyszínválasztó is the centre of the
map and the deck does not include it, so it renders as a placeholder with one map and the deck does not include it, so it renders as a placeholder with one
labelled pin per location on a plain grid. labelled pin per location on a plain grid.
- **Two screens are missing from the deck.** The alley (the one authored beat) - **Two scenes are missing from the deck.** The alley (the one authored beat)
and deck 05, Norman's workplace — which is in the wiki's catalogue but has no and deck 05, Norman's workplace — which is in the wiki's catalogue but has no
screen file yet, so the workplace thread of the story has nowhere to happen. scene file yet, so the workplace thread of the story has nowhere to happen.
- **Spent dialogue choices are hidden, not struck through.** `DialogueChoice.Once` - **Spent dialogue choices are hidden, not struck through.** `DialogueChoice.Once`
hides them; the deck wanted them struck through but visible, so the list hides them; the deck wanted them struck through but visible, so the list
becomes a memory of what you already tried. Needs a thin override over becomes a memory of what you already tried. Needs a thin override over
+1 -1
View File
@@ -3,7 +3,7 @@ module git.teletypegames.org/games/realworld
go 1.26.3 go 1.26.3
require ( require (
git.teletypegames.org/engines/inkwell v0.1.0 git.teletypegames.org/engines/inkwell v0.1.1-0.20260829220920-f9745e426624
github.com/hajimehoshi/ebiten/v2 v2.9.9 github.com/hajimehoshi/ebiten/v2 v2.9.9
) )
+2
View File
@@ -1,5 +1,7 @@
git.teletypegames.org/engines/inkwell v0.1.0 h1:NJgT924aR3e0uLVRiTEhzLrXJY+Vnj+FArkKqMJJlVI= git.teletypegames.org/engines/inkwell v0.1.0 h1:NJgT924aR3e0uLVRiTEhzLrXJY+Vnj+FArkKqMJJlVI=
git.teletypegames.org/engines/inkwell v0.1.0/go.mod h1:/v6QtismTE8+e7kobbVR5B/CpTSrd4j2DYwJ8DvINhs= git.teletypegames.org/engines/inkwell v0.1.0/go.mod h1:/v6QtismTE8+e7kobbVR5B/CpTSrd4j2DYwJ8DvINhs=
git.teletypegames.org/engines/inkwell v0.1.1-0.20260829220920-f9745e426624 h1:lzKbik6RzcqIEub13X8a1J3V/MCQOPNo2XF8oOKm+Ts=
git.teletypegames.org/engines/inkwell v0.1.1-0.20260829220920-f9745e426624/go.mod h1:/v6QtismTE8+e7kobbVR5B/CpTSrd4j2DYwJ8DvINhs=
github.com/ebitengine/gomobile v0.0.0-20250923094054-ea854a63cce1 h1:+kz5iTT3L7uU+VhlMfTb8hHcxLO3TlaELlX8wa4XjA0= github.com/ebitengine/gomobile v0.0.0-20250923094054-ea854a63cce1 h1:+kz5iTT3L7uU+VhlMfTb8hHcxLO3TlaELlX8wa4XjA0=
github.com/ebitengine/gomobile v0.0.0-20250923094054-ea854a63cce1/go.mod h1:lKJoeixeJwnFmYsBny4vvCJGVFc3aYDalhuDsfZzWHI= github.com/ebitengine/gomobile v0.0.0-20250923094054-ea854a63cce1/go.mod h1:lKJoeixeJwnFmYsBny4vvCJGVFc3aYDalhuDsfZzWHI=
github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE=
+10 -4
View File
@@ -5,18 +5,25 @@ import (
) )
type Opts struct { type Opts struct {
Screen string Scene string
Finale bool Finale bool
} }
func New(o Opts) *inkwell.Game { func New(o Opts) *inkwell.Game {
start := o.Screen start := o.Scene
if start == "" { if start == "" {
start = ScreenAlley start = SceneAlley
} }
g := inkwell.NewGame("Real World", ScreenW, ScreenH) g := inkwell.NewGame("Real World", ScreenW, ScreenH)
g.MaxLogLines = 64 g.MaxLogLines = 64
g.SceneRect = inkwell.Rect(0, TopBarH, ScreenW, HUDTop-TopBarH)
g.ExitLook = func(e inkwell.Exit) inkwell.Action {
return inkwell.Say(Paul, "That way: "+e.Label+".")
}
g.ExitTake = func(inkwell.Exit) inkwell.Action {
return inkwell.Say(Paul, "It's a way out, not a thing.")
}
World.attach(g) World.attach(g)
registerTheme() registerTheme()
@@ -32,7 +39,6 @@ func New(o Opts) *inkwell.Game {
func bootOpening(start string, finale bool) []inkwell.Action { func bootOpening(start string, finale bool) []inkwell.Action {
acts := []inkwell.Action{ acts := []inkwell.Action{
EnterScene(start),
inkwell.SetFlag(FlagPoliceTip), inkwell.SetFlag(FlagPoliceTip),
inkwell.Say(Paul, "Back alley. Just like the clerk said."), inkwell.Say(Paul, "Back alley. Just like the clerk said."),
TapeSay(Dex, "A government office tipped you off to remove something from a scene. That doesn't strike you as odd?"), TapeSay(Dex, "A government office tipped you off to remove something from a scene. That doesn't strike you as odd?"),
+1 -1
View File
@@ -6,5 +6,5 @@ func registerContent() {
registerAll(ItemManager, World.G.ItemManager.Register) registerAll(ItemManager, World.G.ItemManager.Register)
registerAll(DialogManager, World.G.DialogueManager.Register) registerAll(DialogManager, World.G.DialogueManager.Register)
registerAll(ScriptManager, World.G.ScriptManager.Register) registerAll(ScriptManager, World.G.ScriptManager.Register)
registerScreen() registerScene()
} }
+1 -1
View File
@@ -12,6 +12,6 @@ var (
_ ManagerInterface[Dialog] = DialogManager _ ManagerInterface[Dialog] = DialogManager
_ ManagerInterface[Item] = ItemManager _ ManagerInterface[Item] = ItemManager
_ ManagerInterface[Script] = ScriptManager _ ManagerInterface[Script] = ScriptManager
_ ManagerInterface[Screen] = ScreenManager _ ManagerInterface[Scene] = SceneManager
_ ManagerInterface[Theme] = ThemeManager _ ManagerInterface[Theme] = ThemeManager
) )
+24 -24
View File
@@ -36,30 +36,30 @@ const (
) )
const ( const (
ScreenSelector = "selector" SceneSelector = "selector"
ScreenPaulShop = "paul_shop" ScenePaulShop = "paul_shop"
ScreenNoodleHouse = "noodle_house" SceneNoodleHouse = "noodle_house"
ScreenNormanApartment = "norman_apartment" SceneNormanApartment = "norman_apartment"
ScreenPoliceStation = "police_station" ScenePoliceStation = "police_station"
ScreenAlley = "alley" SceneAlley = "alley"
ScreenHackerspace = "hackerspace" SceneHackerspace = "hackerspace"
ScreenIceCreamShop = "ice_cream_shop" SceneIceCreamShop = "ice_cream_shop"
ScreenTrinketShop = "trinket_shop" SceneTrinketShop = "trinket_shop"
ScreenSmallRestaurant = "small_restaurant" SceneSmallRestaurant = "small_restaurant"
ScreenSecretClub = "secret_club" SceneSecretClub = "secret_club"
ScreenBBSTerminal = "bbs_terminal" SceneBBSTerminal = "bbs_terminal"
ScreenStreet = "street" SceneStreet = "street"
ScreenHospital = "hospital" SceneHospital = "hospital"
ScreenServerFarm = "server_farm" SceneServerFarm = "server_farm"
ScreenSecretLab = "secret_lab" SceneSecretLab = "secret_lab"
ScreenRooftopHideout = "rooftop_hideout" SceneRooftopHideout = "rooftop_hideout"
ScreenPublicBBS = "public_bbs" ScenePublicBBS = "public_bbs"
ScreenBVKBranch = "bvk_branch" SceneBVKBranch = "bvk_branch"
ScreenCuratorShop = "curator_shop" SceneCuratorShop = "curator_shop"
ScreenScrapMarket = "scrap_market" SceneScrapMarket = "scrap_market"
ScreenSamizdatPress = "samizdat_press" SceneSamizdatPress = "samizdat_press"
ScreenShowroom = "showroom" SceneShowroom = "showroom"
ScreenColumbarium = "columbarium" SceneColumbarium = "columbarium"
) )
const ( const (
+6 -6
View File
@@ -5,15 +5,15 @@ import (
) )
func init() { func init() {
ScreenManager.Register(Screen{ SceneManager.Register(Scene{
Name: ScreenAlley, Name: SceneAlley,
Title: "ALLEY — BEHIND NOODLE'S HOUSE", Title: "ALLEY — BEHIND NOODLE'S HOUSE",
Background: BgAlley, Background: BgAlley,
Exits: []exit{ Exits: []inkwell.Exit{
{ {
to: ScreenNoodleHouse, To: SceneNoodleHouse,
label: "back out to the street", Label: "back out to the street",
side: sideLeft, Side: inkwell.ExitLeft,
}, },
}, },
Actors: []inkwell.SceneActor{ Actors: []inkwell.SceneActor{
+19
View File
@@ -0,0 +1,19 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
SceneManager.Register(Scene{
Name: SceneBBSTerminal,
Title: "TERMINAL — BBS ACCESS POINT",
Background: BgBBSTerminal,
Exits: []inkwell.Exit{
{
Label: "step back from the terminal",
Side: inkwell.ExitNear,
},
},
})
}
+16
View File
@@ -0,0 +1,16 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
SceneManager.Register(Scene{
Name: SceneBVKBranch,
Title: "BVK — SAN FRANCISCO BRANCH",
Background: BgBVKBranch,
Exits: []inkwell.Exit{
exitToSelector,
},
})
}
+16
View File
@@ -0,0 +1,16 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
SceneManager.Register(Scene{
Name: SceneColumbarium,
Title: "COLUMBARIUM — DEX'S MEMORIAL",
Background: BgColumbarium,
Exits: []inkwell.Exit{
exitToSelector,
},
})
}
+16
View File
@@ -0,0 +1,16 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
SceneManager.Register(Scene{
Name: SceneCuratorShop,
Title: "CURATOR SERVICE — THE MASTER'S WORKSHOP",
Background: BgCuratorShop,
Exits: []inkwell.Exit{
exitToSelector,
},
})
}
+16
View File
@@ -0,0 +1,16 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
SceneManager.Register(Scene{
Name: SceneHackerspace,
Title: "HACKERSPACE",
Background: BgHackerspace,
Exits: []inkwell.Exit{
exitToSelector,
},
})
}
+16
View File
@@ -0,0 +1,16 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
SceneManager.Register(Scene{
Name: SceneHospital,
Title: "HOSPITAL — PSYCHIATRIC WING",
Background: BgHospital,
Exits: []inkwell.Exit{
exitToSelector,
},
})
}
+16
View File
@@ -0,0 +1,16 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
SceneManager.Register(Scene{
Name: SceneIceCreamShop,
Title: "ICE CREAM SHOP — WHERE THE BAR WAS",
Background: BgIceCreamShop,
Exits: []inkwell.Exit{
exitToSelector,
},
})
}
+61
View File
@@ -0,0 +1,61 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
type Scene = inkwell.Scene
var SceneManager = NewManager(func(entity Scene) string { return entity.Name })
func registerScene() {
fillSelectorPins()
registerAll(SceneManager, func(entity Scene) {
World.G.SceneManager.Register(sceneDefaults(entity))
})
}
var sceneFloor = []inkwell.Polygon{
inkwell.Poly(
inkwell.Point{
X: 16,
Y: 196,
}, inkwell.Point{
X: 624,
Y: 196,
},
inkwell.Point{
X: 624,
Y: 258,
}, inkwell.Point{
X: 16,
Y: 258,
},
),
}
func sceneDefaults(s Scene) Scene {
if s.Actors == nil {
s.Actors = []inkwell.SceneActor{
{
CharacterName: Paul,
At: inkwell.Point{
X: 320,
Y: 232,
},
},
}
}
if s.Walkboxes == nil {
s.Walkboxes = sceneFloor
}
return s
}
func setVarIfEmpty(name string, v any) inkwell.Action {
return Fn(func(ctx *inkwell.Ctx) {
if ctx.Game.State.Var(name) == nil {
ctx.Game.State.SetVar(name, v)
}
})
}
+21
View File
@@ -0,0 +1,21 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
SceneManager.Register(Scene{
Name: SceneNoodleHouse,
Title: "NOODLE'S HOUSE — SEALED",
Background: BgNoodleHouse,
Exits: []inkwell.Exit{
exitToSelector,
{
To: SceneAlley,
Label: "the alley behind the house",
Side: inkwell.ExitRight,
},
},
})
}
+26
View File
@@ -0,0 +1,26 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
SceneManager.Register(Scene{
Name: SceneNormanApartment,
Title: "NORMAN'S APARTMENT",
Background: BgNormanApartment,
Exits: []inkwell.Exit{
exitToSelector,
{
To: SceneRooftopHideout,
Label: "the stairs up to the roof",
Side: inkwell.ExitBack,
},
{
To: SceneBBSTerminal,
Label: "Norman's terminal",
Side: inkwell.ExitRight,
},
},
})
}
+20
View File
@@ -0,0 +1,20 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
SceneManager.Register(Scene{
Name: ScenePaulShop,
Title: "PAUL'S SHOP — JUNK AND GARAGE",
Background: BgPaulShop,
Exits: []inkwell.Exit{
{
To: SceneNoodleHouse,
Label: "the bus to San Francisco",
Side: inkwell.ExitNear,
},
},
})
}
+16
View File
@@ -0,0 +1,16 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
SceneManager.Register(Scene{
Name: ScenePoliceStation,
Title: "SFPD — STATION AND HOLDING",
Background: BgPoliceStation,
Exits: []inkwell.Exit{
exitToSelector,
},
})
}
+16
View File
@@ -0,0 +1,16 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
SceneManager.Register(Scene{
Name: ScenePublicBBS,
Title: "PUBLIC BBS TERMINAL",
Background: BgPublicBBS,
Exits: []inkwell.Exit{
exitToSelector,
},
})
}
+25
View File
@@ -0,0 +1,25 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
SceneManager.Register(Scene{
Name: SceneRooftopHideout,
Title: "NORMAN'S ROOFTOP HIDEOUT",
Background: BgRooftopHideout,
Exits: []inkwell.Exit{
{
To: SceneNormanApartment,
Label: "back down into the flat",
Side: inkwell.ExitNear,
},
{
To: SceneBBSTerminal,
Label: "the old terminal",
Side: inkwell.ExitRight,
},
},
})
}
+16
View File
@@ -0,0 +1,16 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
SceneManager.Register(Scene{
Name: SceneSamizdatPress,
Title: "SAMIZDAT PRINTING HOUSE",
Background: BgSamizdatPress,
Exits: []inkwell.Exit{
exitToSelector,
},
})
}
+16
View File
@@ -0,0 +1,16 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
SceneManager.Register(Scene{
Name: SceneScrapMarket,
Title: "SCRAP MARKET",
Background: BgScrapMarket,
Exits: []inkwell.Exit{
exitToSelector,
},
})
}
+25
View File
@@ -0,0 +1,25 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
SceneManager.Register(Scene{
Name: SceneSecretClub,
Title: "SECRET CLUB — THE UNDERWATER SUN",
Background: BgSecretClub,
Exits: []inkwell.Exit{
{
To: SceneBBSTerminal,
Label: "the terminal in the corner",
Side: inkwell.ExitBack,
},
{
To: SceneTrinketShop,
Label: "back out through the shop",
Side: inkwell.ExitNear,
},
},
})
}
+16
View File
@@ -0,0 +1,16 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
SceneManager.Register(Scene{
Name: SceneSecretLab,
Title: "SECRET RESEARCH LABORATORY",
Background: BgSecretLab,
Exits: []inkwell.Exit{
exitToSelector,
},
})
}
@@ -6,9 +6,15 @@ import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
) )
var exitToSelector = inkwell.Exit{
To: SceneSelector,
Label: "the rest of the city",
Side: inkwell.ExitNear,
}
func init() { func init() {
ScreenManager.Register(Screen{ SceneManager.Register(Scene{
Name: ScreenSelector, Name: SceneSelector,
Title: "SAN FRANCISCO", Title: "SAN FRANCISCO",
Background: BgSelector, Background: BgSelector,
Actors: []inkwell.SceneActor{}, Actors: []inkwell.SceneActor{},
@@ -17,23 +23,32 @@ func init() {
} }
func fillSelectorPins() { func fillSelectorPins() {
selector, ok := ScreenManager.GetByName(ScreenSelector) selector, ok := SceneManager.GetByName(SceneSelector)
if !ok { if !ok {
return return
} }
var exits []exit var exits []inkwell.Exit
for _, entity := range ScreenManager.GetAll() { for _, entity := range SceneManager.GetAll() {
if !entity.OnSelector { if !leadsToSelector(entity) {
continue continue
} }
exits = append(exits, exit{ exits = append(exits, inkwell.Exit{
to: entity.Name, To: entity.Name,
label: pinLabel(entity.Title), Label: pinLabel(entity.Title),
area: pin(len(exits)), Area: pin(len(exits)),
}) })
} }
selector.Exits = exits selector.Exits = exits
ScreenManager.Register(selector) SceneManager.Register(selector)
}
func leadsToSelector(s Scene) bool {
for _, e := range s.Exits {
if e.To == SceneSelector {
return true
}
}
return false
} }
const ( const (
+16
View File
@@ -0,0 +1,16 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
SceneManager.Register(Scene{
Name: SceneServerFarm,
Title: "SERVER FARM",
Background: BgServerFarm,
Exits: []inkwell.Exit{
exitToSelector,
},
})
}
+16
View File
@@ -0,0 +1,16 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
SceneManager.Register(Scene{
Name: SceneShowroom,
Title: "NEUMATRONIC SHOWROOM",
Background: BgShowroom,
Exits: []inkwell.Exit{
exitToSelector,
},
})
}
+20
View File
@@ -0,0 +1,20 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
SceneManager.Register(Scene{
Name: SceneSmallRestaurant,
Title: "SMALL RESTAURANT — NEXT DOOR",
Background: BgSmallRestaurant,
Exits: []inkwell.Exit{
{
To: SceneTrinketShop,
Label: "back to the trinket shop",
Side: inkwell.ExitLeft,
},
},
})
}
+16
View File
@@ -0,0 +1,16 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
SceneManager.Register(Scene{
Name: SceneStreet,
Title: "STREET",
Background: BgStreet,
Exits: []inkwell.Exit{
exitToSelector,
},
})
}
+28
View File
@@ -0,0 +1,28 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
SceneManager.Register(Scene{
Name: SceneTrinketShop,
Title: "CHINATOWN — TRINKET SHOP",
Background: BgTrinketShop,
Exits: []inkwell.Exit{
exitToSelector,
{
To: SceneSmallRestaurant,
Label: "the eating place next door",
Side: inkwell.ExitRight,
},
{
To: SceneSecretClub,
Label: "the way in at the back",
Side: inkwell.ExitBack,
Needs: FlagClubEntry,
Blocked: inkwell.Say(Paul, "Just a shop, as far as the man behind the counter is concerned."),
},
},
})
}
-15
View File
@@ -1,15 +0,0 @@
package inc
func init() {
ScreenManager.Register(Screen{
Name: ScreenBBSTerminal,
Title: "TERMINAL — BBS ACCESS POINT",
Background: BgBBSTerminal,
Exits: []exit{
{
label: "step back from the terminal",
side: sideNear,
},
},
})
}
-10
View File
@@ -1,10 +0,0 @@
package inc
func init() {
ScreenManager.Register(Screen{
Name: ScreenBVKBranch,
Title: "BVK — SAN FRANCISCO BRANCH",
Background: BgBVKBranch,
OnSelector: true,
})
}
-10
View File
@@ -1,10 +0,0 @@
package inc
func init() {
ScreenManager.Register(Screen{
Name: ScreenColumbarium,
Title: "COLUMBARIUM — DEX'S MEMORIAL",
Background: BgColumbarium,
OnSelector: true,
})
}
-10
View File
@@ -1,10 +0,0 @@
package inc
func init() {
ScreenManager.Register(Screen{
Name: ScreenCuratorShop,
Title: "CURATOR SERVICE — THE MASTER'S WORKSHOP",
Background: BgCuratorShop,
OnSelector: true,
})
}
-76
View File
@@ -1,76 +0,0 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
type side int
const (
sideLeft side = iota
sideRight
sideBack
sideNear
)
func (s side) area() inkwell.Shape {
switch s {
case sideLeft:
return inkwell.Rect(0, 40, 56, 212)
case sideRight:
return inkwell.Rect(584, 40, 56, 212)
case sideBack:
return inkwell.Rect(232, 28, 176, 84)
default:
return inkwell.Rect(232, 214, 176, 48)
}
}
type exit struct {
to string
label string
side side
area inkwell.Shape
needs string
blocked string
}
func toSelector() exit {
return exit{
to: ScreenSelector,
label: "the rest of the city",
side: sideNear,
}
}
func exitName(to string) string {
if to == "" {
return "exit:back"
}
return "exit:" + to
}
func (e exit) hotspot() inkwell.Hotspot {
var travel inkwell.Action = inkwell.GoTo(e.to)
if e.to == "" {
travel = Back()
}
if e.needs != "" {
travel = inkwell.If(inkwell.Flag(e.needs), travel, inkwell.Say(Paul, e.blocked))
}
area := e.area
if area == nil {
area = e.side.area()
}
return inkwell.Hotspot{
Name: exitName(e.to),
Label: e.label,
Area: area,
Cursor: inkwell.CursorExit,
OnLook: inkwell.Say(Paul, "That way: "+e.label+"."),
OnUse: travel,
OnTake: inkwell.Say(Paul, "It's a way out, not a thing."),
}
}
-10
View File
@@ -1,10 +0,0 @@
package inc
func init() {
ScreenManager.Register(Screen{
Name: ScreenHackerspace,
Title: "HACKERSPACE",
Background: BgHackerspace,
OnSelector: true,
})
}
-10
View File
@@ -1,10 +0,0 @@
package inc
func init() {
ScreenManager.Register(Screen{
Name: ScreenHospital,
Title: "HOSPITAL — PSYCHIATRIC WING",
Background: BgHospital,
OnSelector: true,
})
}
-10
View File
@@ -1,10 +0,0 @@
package inc
func init() {
ScreenManager.Register(Screen{
Name: ScreenIceCreamShop,
Title: "ICE CREAM SHOP — WHERE THE BAR WAS",
Background: BgIceCreamShop,
OnSelector: true,
})
}
-99
View File
@@ -1,99 +0,0 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
type Screen struct {
Name string
Title string
Background string
Exits []exit
OnSelector bool
Hotspots []inkwell.Hotspot
Actors []inkwell.SceneActor
Walkboxes []inkwell.Polygon
OnEnter inkwell.Action
}
var ScreenManager = NewManager(func(entity Screen) string { return entity.Name })
func registerScreen() {
fillSelectorPins()
registerAll(ScreenManager, func(entity Screen) {
World.G.SceneManager.Register(screenBuild(entity))
})
}
var screenFloor = []inkwell.Polygon{
inkwell.Poly(
inkwell.Point{
X: 16,
Y: 196,
}, inkwell.Point{
X: 624,
Y: 196,
},
inkwell.Point{
X: 624,
Y: 258,
}, inkwell.Point{
X: 16,
Y: 258,
},
),
}
func screenBuild(s Screen) inkwell.Scene {
exits := s.Exits
if s.OnSelector {
exits = append(exits, toSelector())
}
hotspots := make([]inkwell.Hotspot, 0, len(exits)+len(s.Hotspots))
hotspots = append(hotspots, s.Hotspots...)
for _, e := range exits {
hotspots = append(hotspots, e.hotspot())
}
actors := s.Actors
if actors == nil {
actors = []inkwell.SceneActor{
{
CharacterName: Paul,
At: inkwell.Point{
X: 320,
Y: 232,
},
},
}
}
walkboxes := s.Walkboxes
if walkboxes == nil {
walkboxes = screenFloor
}
onEnter := inkwell.Action(EnterScene(s.Name))
if s.OnEnter != nil {
onEnter = inkwell.Seq(onEnter, s.OnEnter)
}
return inkwell.Scene{
Name: s.Name,
Title: s.Title,
Background: s.Background,
Actors: actors,
Walkboxes: walkboxes,
Hotspots: hotspots,
OnEnter: onEnter,
}
}
func setVarIfEmpty(name string, v any) inkwell.Action {
return Fn(func(ctx *inkwell.Ctx) {
if ctx.Game.State.Var(name) == nil {
ctx.Game.State.SetVar(name, v)
}
})
}
-17
View File
@@ -1,17 +0,0 @@
package inc
func init() {
ScreenManager.Register(Screen{
Name: ScreenNoodleHouse,
Title: "NOODLE'S HOUSE — SEALED",
Background: BgNoodleHouse,
OnSelector: true,
Exits: []exit{
{
to: ScreenAlley,
label: "the alley behind the house",
side: sideRight,
},
},
})
}
-22
View File
@@ -1,22 +0,0 @@
package inc
func init() {
ScreenManager.Register(Screen{
Name: ScreenNormanApartment,
Title: "NORMAN'S APARTMENT",
Background: BgNormanApartment,
OnSelector: true,
Exits: []exit{
{
to: ScreenRooftopHideout,
label: "the stairs up to the roof",
side: sideBack,
},
{
to: ScreenBBSTerminal,
label: "Norman's terminal",
side: sideRight,
},
},
})
}
-16
View File
@@ -1,16 +0,0 @@
package inc
func init() {
ScreenManager.Register(Screen{
Name: ScreenPaulShop,
Title: "PAUL'S SHOP — JUNK AND GARAGE",
Background: BgPaulShop,
Exits: []exit{
{
to: ScreenNoodleHouse,
label: "the bus to San Francisco",
side: sideNear,
},
},
})
}
-10
View File
@@ -1,10 +0,0 @@
package inc
func init() {
ScreenManager.Register(Screen{
Name: ScreenPoliceStation,
Title: "SFPD — STATION AND HOLDING",
Background: BgPoliceStation,
OnSelector: true,
})
}
-10
View File
@@ -1,10 +0,0 @@
package inc
func init() {
ScreenManager.Register(Screen{
Name: ScreenPublicBBS,
Title: "PUBLIC BBS TERMINAL",
Background: BgPublicBBS,
OnSelector: true,
})
}
-21
View File
@@ -1,21 +0,0 @@
package inc
func init() {
ScreenManager.Register(Screen{
Name: ScreenRooftopHideout,
Title: "NORMAN'S ROOFTOP HIDEOUT",
Background: BgRooftopHideout,
Exits: []exit{
{
to: ScreenNormanApartment,
label: "back down into the flat",
side: sideNear,
},
{
to: ScreenBBSTerminal,
label: "the old terminal",
side: sideRight,
},
},
})
}
-10
View File
@@ -1,10 +0,0 @@
package inc
func init() {
ScreenManager.Register(Screen{
Name: ScreenSamizdatPress,
Title: "SAMIZDAT PRINTING HOUSE",
Background: BgSamizdatPress,
OnSelector: true,
})
}
-10
View File
@@ -1,10 +0,0 @@
package inc
func init() {
ScreenManager.Register(Screen{
Name: ScreenScrapMarket,
Title: "SCRAP MARKET",
Background: BgScrapMarket,
OnSelector: true,
})
}
-21
View File
@@ -1,21 +0,0 @@
package inc
func init() {
ScreenManager.Register(Screen{
Name: ScreenSecretClub,
Title: "SECRET CLUB — THE UNDERWATER SUN",
Background: BgSecretClub,
Exits: []exit{
{
to: ScreenBBSTerminal,
label: "the terminal in the corner",
side: sideBack,
},
{
to: ScreenTrinketShop,
label: "back out through the shop",
side: sideNear,
},
},
})
}
-10
View File
@@ -1,10 +0,0 @@
package inc
func init() {
ScreenManager.Register(Screen{
Name: ScreenSecretLab,
Title: "SECRET RESEARCH LABORATORY",
Background: BgSecretLab,
OnSelector: true,
})
}
-10
View File
@@ -1,10 +0,0 @@
package inc
func init() {
ScreenManager.Register(Screen{
Name: ScreenServerFarm,
Title: "SERVER FARM",
Background: BgServerFarm,
OnSelector: true,
})
}
-10
View File
@@ -1,10 +0,0 @@
package inc
func init() {
ScreenManager.Register(Screen{
Name: ScreenShowroom,
Title: "NEUMATRONIC SHOWROOM",
Background: BgShowroom,
OnSelector: true,
})
}
-16
View File
@@ -1,16 +0,0 @@
package inc
func init() {
ScreenManager.Register(Screen{
Name: ScreenSmallRestaurant,
Title: "SMALL RESTAURANT — NEXT DOOR",
Background: BgSmallRestaurant,
Exits: []exit{
{
to: ScreenTrinketShop,
label: "back to the trinket shop",
side: sideLeft,
},
},
})
}
-10
View File
@@ -1,10 +0,0 @@
package inc
func init() {
ScreenManager.Register(Screen{
Name: ScreenStreet,
Title: "STREET",
Background: BgStreet,
OnSelector: true,
})
}
-24
View File
@@ -1,24 +0,0 @@
package inc
func init() {
ScreenManager.Register(Screen{
Name: ScreenTrinketShop,
Title: "CHINATOWN — TRINKET SHOP",
Background: BgTrinketShop,
OnSelector: true,
Exits: []exit{
{
to: ScreenSmallRestaurant,
label: "the eating place next door",
side: sideRight,
},
{
to: ScreenSecretClub,
label: "the way in at the back",
side: sideBack,
needs: FlagClubEntry,
blocked: "Just a shop, as far as the man behind the counter is concerned.",
},
},
})
}
+2 -2
View File
@@ -36,8 +36,8 @@ func registerUI() {
g.UIManager.Register(&actionPump{ g.UIManager.Register(&actionPump{
Name: "pump", Name: "pump",
}) })
g.UIManager.Register(&screenNav{ g.UIManager.Register(&sceneNav{
Name: "screen_nav", Name: "scene_nav",
}) })
g.UIManager.Register(&windowSizer{ g.UIManager.Register(&windowSizer{
Name: "window", Name: "window",
+6 -6
View File
@@ -6,14 +6,14 @@ import (
"github.com/hajimehoshi/ebiten/v2/inpututil" "github.com/hajimehoshi/ebiten/v2/inpututil"
) )
type screenNav struct { type sceneNav struct {
Name string Name string
} }
func (n *screenNav) GetName() string { return n.Name } func (n *sceneNav) GetName() string { return n.Name }
func (n *screenNav) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {} func (n *sceneNav) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {}
func (n *screenNav) Tick(ctx *inkwell.UICtx) { func (n *sceneNav) Tick(ctx *inkwell.UICtx) {
if !World.HUDVisible() || World.Paused() { if !World.HUDVisible() || World.Paused() {
return return
} }
@@ -32,13 +32,13 @@ func (n *screenNav) Tick(ctx *inkwell.UICtx) {
} }
} }
func (n *screenNav) neighbour(g *inkwell.Game, step int) string { func (n *sceneNav) neighbour(g *inkwell.Game, step int) string {
deck := g.SceneManager.Names() deck := g.SceneManager.Names()
if len(deck) < 2 { if len(deck) < 2 {
return "" return ""
} }
for i, name := range deck { for i, name := range deck {
if name == World.Scene() { if name == World.G.CurrentScene() {
return deck[((i+step)%len(deck)+len(deck))%len(deck)] return deck[((i+step)%len(deck)+len(deck))%len(deck)]
} }
} }
+2 -20
View File
@@ -32,7 +32,7 @@ func (w *world) PumpTick(dt float64) {
Game: w.G, Game: w.G,
DT: dt, DT: dt,
} }
if s, ok := w.G.SceneManager.Get(w.scene); ok { if s, ok := w.G.SceneManager.Get(w.G.CurrentScene()); ok {
ctx.Scene = &s ctx.Scene = &s
} }
if w.running.Tick(ctx) != inkwell.StatusRunning { if w.running.Tick(ctx) != inkwell.StatusRunning {
@@ -41,7 +41,7 @@ func (w *world) PumpTick(dt float64) {
} }
func (w *world) sceneTitle() string { func (w *world) sceneTitle() string {
s, ok := w.G.SceneManager.Get(w.scene) s, ok := w.G.SceneManager.Get(w.G.CurrentScene())
if !ok { if !ok {
return "" return ""
} }
@@ -88,24 +88,6 @@ func SetMode(m Mode) inkwell.Action {
return Fn(func(*inkwell.Ctx) { World.SetMode(m) }) return Fn(func(*inkwell.Ctx) { World.SetMode(m) })
} }
func EnterScene(name string) inkwell.Action {
return Fn(func(*inkwell.Ctx) {
if name != World.scene {
World.prev = World.scene
}
World.scene = name
})
}
func Back() inkwell.Action {
return Fn(func(ctx *inkwell.Ctx) {
if World.prev == "" {
return
}
inkwell.GoTo(World.prev).Start().Tick(ctx)
})
}
func TapeSay(speaker, text string) inkwell.Action { func TapeSay(speaker, text string) inkwell.Action {
return &tapeSayAction{ return &tapeSayAction{
speaker: speaker, speaker: speaker,
-8
View File
@@ -17,8 +17,6 @@ type world struct {
mode Mode mode Mode
scene string
prev string
paused bool paused bool
pending string pending string
queue []inkwell.Action queue []inkwell.Action
@@ -34,8 +32,6 @@ var World = &world{}
func (w *world) attach(g *inkwell.Game) { func (w *world) attach(g *inkwell.Game) {
w.G = g w.G = g
w.mode = ModePlay w.mode = ModePlay
w.scene = ""
w.prev = ""
w.paused = false w.paused = false
w.pending = "" w.pending = ""
w.queue = nil w.queue = nil
@@ -51,10 +47,6 @@ func (w *world) SetMode(m Mode) { w.mode = m }
func (w *world) HUDVisible() bool { return w.mode == ModePlay } func (w *world) HUDVisible() bool { return w.mode == ModePlay }
func (w *world) Scene() string { return w.scene }
func (w *world) Previous() string { return w.prev }
func (w *world) Paused() bool { return w.paused } func (w *world) Paused() bool { return w.paused }
func (w *world) SetTopBar(t *inkwell.TopBar) { w.top = t } func (w *world) SetTopBar(t *inkwell.TopBar) { w.top = t }
+1 -1
View File
@@ -17,7 +17,7 @@ func init() { inkwell.AssetFS = assets }
func main() { func main() {
var o inc.Opts var o inc.Opts
flag.StringVar(&o.Screen, "screen", "", "starting screen (empty: the alley)") flag.StringVar(&o.Scene, "scene", "", "starting scene (empty: the alley)")
flag.BoolVar(&o.Finale, "finale", false, flag.BoolVar(&o.Finale, "finale", false,
"start with the finale, to try the Nokia-punk theme switch") "start with the finale, to try the Nokia-punk theme switch")
flag.Parse() flag.Parse()