@@ -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
|
||||
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`,
|
||||
`content`.
|
||||
- `[category].manager.go` is the file that ties a category together — its
|
||||
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()`.
|
||||
|
||||
```
|
||||
@@ -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.*.go custom widgets, coloured text
|
||||
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/item.*.go inventory
|
||||
inc/dialog.*.go dialogue trees
|
||||
inc/script.*.go named action sequences
|
||||
inc/screen.manager.go the Screen type, the deck, the scene builder
|
||||
inc/screen.exit.go the connections between screens
|
||||
inc/screen.*.go one file per screen
|
||||
inc/scene.manager.go the Scene alias, the defaults every scene gets
|
||||
inc/scene.selector.go the map screen: its pins are derived from the graph
|
||||
inc/scene.*.go one file per scene
|
||||
```
|
||||
|
||||
`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
|
||||
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
|
||||
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
|
||||
registration order.
|
||||
|
||||
`Screen` is the one entity type that is not an alias: a screen carries exits and
|
||||
an `OnSelector` flag that inkwell's `Scene` knows nothing about.
|
||||
Every entity type is an alias, `Scene` included. It was once a struct of our
|
||||
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
|
||||
|
||||
@@ -144,7 +145,7 @@ variables are initialised before any `init()` runs, so the managers exist by the
|
||||
time the first file registers into one.
|
||||
|
||||
`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.
|
||||
|
||||
### Handing a category to the engine
|
||||
@@ -159,13 +160,13 @@ func registerContent() {
|
||||
registerAll(ItemManager, World.G.ItemManager.Register)
|
||||
registerAll(DialogManager, World.G.DialogueManager.Register)
|
||||
registerAll(ScriptManager, World.G.ScriptManager.Register)
|
||||
registerScreen()
|
||||
registerScene()
|
||||
}
|
||||
```
|
||||
|
||||
`registerScreen` is the one that needs its own function, because a screen has to
|
||||
be turned into an `inkwell.Scene` first, and because the selector's pins are
|
||||
derived from whichever screens marked themselves `OnSelector`.
|
||||
`registerScene` is the one that needs its own function: it fills in the defaults
|
||||
a scene may leave out (Paul's starting position, the floor walkbox), and it
|
||||
derives the selector's pins by reading the exit graph backwards.
|
||||
|
||||
## The world
|
||||
|
||||
@@ -185,7 +186,7 @@ One package means one namespace, so an entity's constructor carries its
|
||||
category as a prefix:
|
||||
|
||||
```go
|
||||
screenFloor screenBuild registerScreen fillSelectorPins
|
||||
sceneFloor sceneDefaults registerScene fillSelectorPins
|
||||
```
|
||||
|
||||
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
|
||||
(`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
|
||||
wiki, the concept-art deck and the beat tables all count screens. "Scene"
|
||||
survives only where the code talks to the engine (`EnterScene`,
|
||||
`SceneManager`).
|
||||
The word is **scene**, the engine's own. The wiki and the concept-art deck count
|
||||
*screens*, and this code used to as well, but everything a screen had that a
|
||||
scene did not now lives in inkwell. "Screen" survives only where it means the
|
||||
display: `ScreenW`, `ScreenH`.
|
||||
|
||||
## Layering
|
||||
|
||||
@@ -219,10 +220,10 @@ names ← theme ← world ← ui
|
||||
`world` knows nothing about the HUD or the content. Both build on it, never the
|
||||
other way round.
|
||||
|
||||
## Adding a screen
|
||||
## Adding a scene
|
||||
|
||||
1. Constants in `inc/names.manager.go`: `Screen<Name>`, `Bg<Name>`
|
||||
2. `inc/screen.<name>.go` — an `init()` registering a `Screen`
|
||||
1. Constants in `inc/names.manager.go`: `Scene<Name>`, `Bg<Name>`
|
||||
2. `inc/scene.<name>.go` — an `init()` registering a `Scene`
|
||||
3. `inc/background.<name>.go` — an `init()` registering a `Background`
|
||||
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 wasm js/wasm build into dist/
|
||||
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
|
||||
```
|
||||
|
||||
|
||||
@@ -25,11 +25,11 @@ go run .
|
||||
```bash
|
||||
go run . # the alley (beat 3)
|
||||
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 . -screen hospital # any screen in the deck, e.g. to look at the art
|
||||
go run . -scene selector # the location selector: the map the city hangs off
|
||||
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`,
|
||||
`ice_cream_shop`, `trinket_shop`, `small_restaurant`, `secret_club`,
|
||||
`bbs_terminal`, `street`, `hospital`, `server_farm`, `secret_lab`,
|
||||
@@ -62,12 +62,12 @@ Release metadata lives in `metadata.json`.
|
||||
|---|---|
|
||||
| left click | run the selected verb |
|
||||
| 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 |
|
||||
| `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
|
||||
are a reviewing tool on top of that, walking every screen in turn. The walk is
|
||||
The scenes are connected to each other (see **The map** below); the arrow keys
|
||||
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
|
||||
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:
|
||||
|
||||
```
|
||||
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
|
||||
item.*.go item.noodle_letter.go item.black_market_armilla.go …
|
||||
dialog.*.go dialog.dex_talk.go dialog.mystery_tape_silent.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.
|
||||
|
||||
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
|
||||
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
|
||||
step with the files by hand, and the deck is a thing to look at, not a thing to
|
||||
play through.
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
type every `screen.*.go` file returns — but the wiki, the concept-art deck and
|
||||
the beat tables all count *screens*, so the category, the files and the flag say
|
||||
screen. "Scene" survives only where the code is talking to the engine
|
||||
(`EnterScene`, `SceneManager`).
|
||||
**On the word "scene".** The wiki, the concept-art deck and the beat tables all
|
||||
count *screens*, and this code used to as well: it carried its own `Screen`
|
||||
struct, because inkwell's `Scene` could not hold the things a screen needs —
|
||||
its exits above all. Those went into the engine instead (inkwell `Exit`,
|
||||
`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
|
||||
js/wasm has no OS filesystem and `make binaries` packages the executable alone.
|
||||
|
||||
## 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
|
||||
(`projects/realworld/story#helyszín-gráf-vázlat`). That graph has two kinds of
|
||||
edge and so does the code:
|
||||
|
||||
- **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 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
|
||||
valódi helyszín, hanem a menü-képernyő": a screen every main location connects
|
||||
to both ways. A screen marks itself `onSelector: true`; `screen/selector.go`
|
||||
builds the other half of each of those edges, so the list of locations on the
|
||||
map is never written down twice.
|
||||
valódi helyszín, hanem a menü-képernyő": a scene every main location connects
|
||||
to both ways. A main location lists `exitToSelector` among its exits, and
|
||||
`scene.selector.go` derives the other half of each edge by reading the graph
|
||||
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
|
||||
is visible and named from the first visit, and stays shut until the shopkeeper
|
||||
has been shown the underwater sun.
|
||||
|
||||
Until the doors are measured on the paintings, an exit is a strip along one edge
|
||||
of the picture (`sideLeft`, `sideRight`, `sideBack`, `sideNear`) — the
|
||||
convention every 1990s point & click used, and one field to re-aim later. A
|
||||
screen 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 is `back`.
|
||||
of the picture (`inkwell.ExitLeft`, `ExitRight`, `ExitBack`, `ExitNear`), sized
|
||||
from `Game.SceneRect` so it lands inside the painting rather than under the HUD
|
||||
— the convention every 1990s point & click used, and one field to re-aim later.
|
||||
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 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
|
||||
|
||||
Five 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
|
||||
Four inkwell limits turned up during implementation that the engine README does
|
||||
not mention. All four are worked around on the domain side; each is a candidate
|
||||
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
|
||||
`_ = c` and rendering goes through `ebitenutil.DebugPrintAt`, which only
|
||||
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
|
||||
character instead, escalating on repeats.
|
||||
|
||||
4. **No exported `CurrentScene()`**, but the pump needs `Ctx.Scene`.
|
||||
*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
|
||||
4. **Widgets have no `Visible` field and the `Manager` cannot unregister**, so
|
||||
the built-in HUD cannot be hidden during a cutscene. *Workaround:* the
|
||||
`gate` wrapper in `ui/ui.go` forwards `Tick`/`Draw`/`BlocksClickAt` only
|
||||
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`
|
||||
widget resizes once on the first tick, since Run sets the size before
|
||||
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
|
||||
picture. Either the deck gets recut so nothing that matters lives in its
|
||||
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
|
||||
there is nothing to do but leave again.
|
||||
- **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
|
||||
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
|
||||
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`
|
||||
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
|
||||
|
||||
@@ -3,7 +3,7 @@ module git.teletypegames.org/games/realworld
|
||||
go 1.26.3
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
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.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/go.mod h1:lKJoeixeJwnFmYsBny4vvCJGVFc3aYDalhuDsfZzWHI=
|
||||
github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE=
|
||||
|
||||
+10
-4
@@ -5,18 +5,25 @@ import (
|
||||
)
|
||||
|
||||
type Opts struct {
|
||||
Screen string
|
||||
Scene string
|
||||
Finale bool
|
||||
}
|
||||
|
||||
func New(o Opts) *inkwell.Game {
|
||||
start := o.Screen
|
||||
start := o.Scene
|
||||
if start == "" {
|
||||
start = ScreenAlley
|
||||
start = SceneAlley
|
||||
}
|
||||
|
||||
g := inkwell.NewGame("Real World", ScreenW, ScreenH)
|
||||
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)
|
||||
registerTheme()
|
||||
@@ -32,7 +39,6 @@ func New(o Opts) *inkwell.Game {
|
||||
|
||||
func bootOpening(start string, finale bool) []inkwell.Action {
|
||||
acts := []inkwell.Action{
|
||||
EnterScene(start),
|
||||
inkwell.SetFlag(FlagPoliceTip),
|
||||
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?"),
|
||||
|
||||
@@ -6,5 +6,5 @@ func registerContent() {
|
||||
registerAll(ItemManager, World.G.ItemManager.Register)
|
||||
registerAll(DialogManager, World.G.DialogueManager.Register)
|
||||
registerAll(ScriptManager, World.G.ScriptManager.Register)
|
||||
registerScreen()
|
||||
registerScene()
|
||||
}
|
||||
|
||||
@@ -12,6 +12,6 @@ var (
|
||||
_ ManagerInterface[Dialog] = DialogManager
|
||||
_ ManagerInterface[Item] = ItemManager
|
||||
_ ManagerInterface[Script] = ScriptManager
|
||||
_ ManagerInterface[Screen] = ScreenManager
|
||||
_ ManagerInterface[Scene] = SceneManager
|
||||
_ ManagerInterface[Theme] = ThemeManager
|
||||
)
|
||||
|
||||
+24
-24
@@ -36,30 +36,30 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
ScreenSelector = "selector"
|
||||
ScreenPaulShop = "paul_shop"
|
||||
ScreenNoodleHouse = "noodle_house"
|
||||
ScreenNormanApartment = "norman_apartment"
|
||||
ScreenPoliceStation = "police_station"
|
||||
ScreenAlley = "alley"
|
||||
ScreenHackerspace = "hackerspace"
|
||||
ScreenIceCreamShop = "ice_cream_shop"
|
||||
ScreenTrinketShop = "trinket_shop"
|
||||
ScreenSmallRestaurant = "small_restaurant"
|
||||
ScreenSecretClub = "secret_club"
|
||||
ScreenBBSTerminal = "bbs_terminal"
|
||||
ScreenStreet = "street"
|
||||
ScreenHospital = "hospital"
|
||||
ScreenServerFarm = "server_farm"
|
||||
ScreenSecretLab = "secret_lab"
|
||||
ScreenRooftopHideout = "rooftop_hideout"
|
||||
ScreenPublicBBS = "public_bbs"
|
||||
ScreenBVKBranch = "bvk_branch"
|
||||
ScreenCuratorShop = "curator_shop"
|
||||
ScreenScrapMarket = "scrap_market"
|
||||
ScreenSamizdatPress = "samizdat_press"
|
||||
ScreenShowroom = "showroom"
|
||||
ScreenColumbarium = "columbarium"
|
||||
SceneSelector = "selector"
|
||||
ScenePaulShop = "paul_shop"
|
||||
SceneNoodleHouse = "noodle_house"
|
||||
SceneNormanApartment = "norman_apartment"
|
||||
ScenePoliceStation = "police_station"
|
||||
SceneAlley = "alley"
|
||||
SceneHackerspace = "hackerspace"
|
||||
SceneIceCreamShop = "ice_cream_shop"
|
||||
SceneTrinketShop = "trinket_shop"
|
||||
SceneSmallRestaurant = "small_restaurant"
|
||||
SceneSecretClub = "secret_club"
|
||||
SceneBBSTerminal = "bbs_terminal"
|
||||
SceneStreet = "street"
|
||||
SceneHospital = "hospital"
|
||||
SceneServerFarm = "server_farm"
|
||||
SceneSecretLab = "secret_lab"
|
||||
SceneRooftopHideout = "rooftop_hideout"
|
||||
ScenePublicBBS = "public_bbs"
|
||||
SceneBVKBranch = "bvk_branch"
|
||||
SceneCuratorShop = "curator_shop"
|
||||
SceneScrapMarket = "scrap_market"
|
||||
SceneSamizdatPress = "samizdat_press"
|
||||
SceneShowroom = "showroom"
|
||||
SceneColumbarium = "columbarium"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -5,15 +5,15 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
ScreenManager.Register(Screen{
|
||||
Name: ScreenAlley,
|
||||
SceneManager.Register(Scene{
|
||||
Name: SceneAlley,
|
||||
Title: "ALLEY — BEHIND NOODLE'S HOUSE",
|
||||
Background: BgAlley,
|
||||
Exits: []exit{
|
||||
Exits: []inkwell.Exit{
|
||||
{
|
||||
to: ScreenNoodleHouse,
|
||||
label: "back out to the street",
|
||||
side: sideLeft,
|
||||
To: SceneNoodleHouse,
|
||||
Label: "back out to the street",
|
||||
Side: inkwell.ExitLeft,
|
||||
},
|
||||
},
|
||||
Actors: []inkwell.SceneActor{
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
var exitToSelector = inkwell.Exit{
|
||||
To: SceneSelector,
|
||||
Label: "the rest of the city",
|
||||
Side: inkwell.ExitNear,
|
||||
}
|
||||
|
||||
func init() {
|
||||
ScreenManager.Register(Screen{
|
||||
Name: ScreenSelector,
|
||||
SceneManager.Register(Scene{
|
||||
Name: SceneSelector,
|
||||
Title: "SAN FRANCISCO",
|
||||
Background: BgSelector,
|
||||
Actors: []inkwell.SceneActor{},
|
||||
@@ -17,23 +23,32 @@ func init() {
|
||||
}
|
||||
|
||||
func fillSelectorPins() {
|
||||
selector, ok := ScreenManager.GetByName(ScreenSelector)
|
||||
selector, ok := SceneManager.GetByName(SceneSelector)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var exits []exit
|
||||
for _, entity := range ScreenManager.GetAll() {
|
||||
if !entity.OnSelector {
|
||||
var exits []inkwell.Exit
|
||||
for _, entity := range SceneManager.GetAll() {
|
||||
if !leadsToSelector(entity) {
|
||||
continue
|
||||
}
|
||||
exits = append(exits, exit{
|
||||
to: entity.Name,
|
||||
label: pinLabel(entity.Title),
|
||||
area: pin(len(exits)),
|
||||
exits = append(exits, inkwell.Exit{
|
||||
To: entity.Name,
|
||||
Label: pinLabel(entity.Title),
|
||||
Area: pin(len(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 (
|
||||
@@ -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,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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."),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package inc
|
||||
|
||||
func init() {
|
||||
ScreenManager.Register(Screen{
|
||||
Name: ScreenBVKBranch,
|
||||
Title: "BVK — SAN FRANCISCO BRANCH",
|
||||
Background: BgBVKBranch,
|
||||
OnSelector: true,
|
||||
})
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package inc
|
||||
|
||||
func init() {
|
||||
ScreenManager.Register(Screen{
|
||||
Name: ScreenColumbarium,
|
||||
Title: "COLUMBARIUM — DEX'S MEMORIAL",
|
||||
Background: BgColumbarium,
|
||||
OnSelector: true,
|
||||
})
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package inc
|
||||
|
||||
func init() {
|
||||
ScreenManager.Register(Screen{
|
||||
Name: ScreenCuratorShop,
|
||||
Title: "CURATOR SERVICE — THE MASTER'S WORKSHOP",
|
||||
Background: BgCuratorShop,
|
||||
OnSelector: true,
|
||||
})
|
||||
}
|
||||
@@ -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."),
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package inc
|
||||
|
||||
func init() {
|
||||
ScreenManager.Register(Screen{
|
||||
Name: ScreenHackerspace,
|
||||
Title: "HACKERSPACE",
|
||||
Background: BgHackerspace,
|
||||
OnSelector: true,
|
||||
})
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package inc
|
||||
|
||||
func init() {
|
||||
ScreenManager.Register(Screen{
|
||||
Name: ScreenHospital,
|
||||
Title: "HOSPITAL — PSYCHIATRIC WING",
|
||||
Background: BgHospital,
|
||||
OnSelector: true,
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package inc
|
||||
|
||||
func init() {
|
||||
ScreenManager.Register(Screen{
|
||||
Name: ScreenPoliceStation,
|
||||
Title: "SFPD — STATION AND HOLDING",
|
||||
Background: BgPoliceStation,
|
||||
OnSelector: true,
|
||||
})
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package inc
|
||||
|
||||
func init() {
|
||||
ScreenManager.Register(Screen{
|
||||
Name: ScreenPublicBBS,
|
||||
Title: "PUBLIC BBS TERMINAL",
|
||||
Background: BgPublicBBS,
|
||||
OnSelector: true,
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package inc
|
||||
|
||||
func init() {
|
||||
ScreenManager.Register(Screen{
|
||||
Name: ScreenSamizdatPress,
|
||||
Title: "SAMIZDAT PRINTING HOUSE",
|
||||
Background: BgSamizdatPress,
|
||||
OnSelector: true,
|
||||
})
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package inc
|
||||
|
||||
func init() {
|
||||
ScreenManager.Register(Screen{
|
||||
Name: ScreenScrapMarket,
|
||||
Title: "SCRAP MARKET",
|
||||
Background: BgScrapMarket,
|
||||
OnSelector: true,
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package inc
|
||||
|
||||
func init() {
|
||||
ScreenManager.Register(Screen{
|
||||
Name: ScreenSecretLab,
|
||||
Title: "SECRET RESEARCH LABORATORY",
|
||||
Background: BgSecretLab,
|
||||
OnSelector: true,
|
||||
})
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package inc
|
||||
|
||||
func init() {
|
||||
ScreenManager.Register(Screen{
|
||||
Name: ScreenServerFarm,
|
||||
Title: "SERVER FARM",
|
||||
Background: BgServerFarm,
|
||||
OnSelector: true,
|
||||
})
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package inc
|
||||
|
||||
func init() {
|
||||
ScreenManager.Register(Screen{
|
||||
Name: ScreenShowroom,
|
||||
Title: "NEUMATRONIC SHOWROOM",
|
||||
Background: BgShowroom,
|
||||
OnSelector: true,
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package inc
|
||||
|
||||
func init() {
|
||||
ScreenManager.Register(Screen{
|
||||
Name: ScreenStreet,
|
||||
Title: "STREET",
|
||||
Background: BgStreet,
|
||||
OnSelector: true,
|
||||
})
|
||||
}
|
||||
@@ -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
@@ -36,8 +36,8 @@ func registerUI() {
|
||||
g.UIManager.Register(&actionPump{
|
||||
Name: "pump",
|
||||
})
|
||||
g.UIManager.Register(&screenNav{
|
||||
Name: "screen_nav",
|
||||
g.UIManager.Register(&sceneNav{
|
||||
Name: "scene_nav",
|
||||
})
|
||||
g.UIManager.Register(&windowSizer{
|
||||
Name: "window",
|
||||
|
||||
@@ -6,14 +6,14 @@ import (
|
||||
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||
)
|
||||
|
||||
type screenNav struct {
|
||||
type sceneNav struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (n *screenNav) GetName() string { return n.Name }
|
||||
func (n *screenNav) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {}
|
||||
func (n *sceneNav) GetName() string { return n.Name }
|
||||
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() {
|
||||
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()
|
||||
if len(deck) < 2 {
|
||||
return ""
|
||||
}
|
||||
for i, name := range deck {
|
||||
if name == World.Scene() {
|
||||
if name == World.G.CurrentScene() {
|
||||
return deck[((i+step)%len(deck)+len(deck))%len(deck)]
|
||||
}
|
||||
}
|
||||
+2
-20
@@ -32,7 +32,7 @@ func (w *world) PumpTick(dt float64) {
|
||||
Game: w.G,
|
||||
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
|
||||
}
|
||||
if w.running.Tick(ctx) != inkwell.StatusRunning {
|
||||
@@ -41,7 +41,7 @@ func (w *world) PumpTick(dt float64) {
|
||||
}
|
||||
|
||||
func (w *world) sceneTitle() string {
|
||||
s, ok := w.G.SceneManager.Get(w.scene)
|
||||
s, ok := w.G.SceneManager.Get(w.G.CurrentScene())
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
@@ -88,24 +88,6 @@ func SetMode(m Mode) inkwell.Action {
|
||||
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 {
|
||||
return &tapeSayAction{
|
||||
speaker: speaker,
|
||||
|
||||
@@ -17,8 +17,6 @@ type world struct {
|
||||
|
||||
mode Mode
|
||||
|
||||
scene string
|
||||
prev string
|
||||
paused bool
|
||||
pending string
|
||||
queue []inkwell.Action
|
||||
@@ -34,8 +32,6 @@ var World = &world{}
|
||||
func (w *world) attach(g *inkwell.Game) {
|
||||
w.G = g
|
||||
w.mode = ModePlay
|
||||
w.scene = ""
|
||||
w.prev = ""
|
||||
w.paused = false
|
||||
w.pending = ""
|
||||
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) Scene() string { return w.scene }
|
||||
|
||||
func (w *world) Previous() string { return w.prev }
|
||||
|
||||
func (w *world) Paused() bool { return w.paused }
|
||||
|
||||
func (w *world) SetTopBar(t *inkwell.TopBar) { w.top = t }
|
||||
|
||||
@@ -17,7 +17,7 @@ func init() { inkwell.AssetFS = assets }
|
||||
|
||||
func main() {
|
||||
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,
|
||||
"start with the finale, to try the Nokia-punk theme switch")
|
||||
flag.Parse()
|
||||
|
||||
Reference in New Issue
Block a user