dirs
ci/woodpecker/push/ebitengine Pipeline was successful

This commit is contained in:
2026-08-30 23:21:06 +02:00
parent 28b1662195
commit c5c0c02c03
115 changed files with 1083 additions and 950 deletions
+120 -108
View File
@@ -45,71 +45,72 @@ This holds for nested literals too, including small ones such as
## Layout ## Layout
All game code lives in one flat package, `inc`. There are no subdirectories: a One category, one package, one directory. A file keeps the category in its own
file's name carries the structure, in the form `[category].[name].go`. name anyway — `[category].[name].go` inside `inc/[category]/` — because the
prefix is what makes a file findable in a list of editor tabs, a grep, or a
diff, where the directory has already fallen off.
- The category is always **singular**: `item`, `scene`, `background`, - The category is always **singular**: `item`, `scene`, `background`,
`character`, `tape`, `dialog`, `script`, `world`, `widget`, `theme`, `names`. `character`, `tape`, `dialog`, `script`, `world`, `widget`, `theme`.
- `[category].manager.go` is the file that ties a category together — its - `[category].manager.go` is the file that ties a package together — its entity
entity type and whatever else the category shares. type, its `Manager`, and whatever else the category shares.
- Every other file in a category holds exactly one entity: one scene, one - Every other file in a package 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()`.
- `boot.go` is the exception that has no category: it declares every manager - Two files have no category. `inc/constants.go` is the vocabulary every
and builds the game. package imports, and `inc/boot/boot.go` builds the game.
``` ```
main.go flags + inkwell.Run main.go flags + inkwell.Run
inc/boot.go the managers; New(Opts) builds the game inc/constants.go entity names and world-state keys
inc/names.manager.go entity names and world-state keys inc/boot/boot.go New(Opts) builds the game
inc/theme.manager.go the Theme alias and the colour helpers inc/theme/theme.manager.go the Theme alias, its Manager, RGB/RGBA
inc/theme.realworld.go realworld-93 inc/theme/theme.realworld.go realworld-93
inc/theme.nokia_punk.go nokia-punk inc/theme/theme.nokia_punk.go nokia-punk
inc/world.manager.go unsaved runtime state inc/world/world.manager.go unsaved runtime state
inc/world.action.go custom actions inc/world/world.action.go custom actions
inc/widget.manager.go the Widget alias, HUD layout, the shared conditions inc/tape/tape.manager.go the Tape entity and the lookups over it
inc/widget.*.go one widget per file, registering itself inc/tape/tape.*.go one tape per file
inc/background.*.go one image asset per scene inc/widget/widget.manager.go the Widget alias, HUD layout, the conditions
inc/character.*.go the cast, tapes included inc/widget/widget.*.go one widget per file, registering itself
inc/tape.manager.go the Tape entity and the lookups over it inc/background/background.*.go one image asset per scene
inc/tape.*.go one tape per file inc/character/character.*.go the cast, tapes included
inc/item.*.go inventory inc/item/item.*.go inventory
inc/dialog.*.go dialogue trees inc/dialog/dialog.*.go dialogue trees
inc/script.*.go named action sequences inc/script/script.*.go named action sequences
inc/scene.manager.go the Scene alias, the defaults every scene gets inc/scene/scene.manager.go the Scene alias, its Manager, the floor
inc/scene.selector.go the map screen: its pins are derived from the graph inc/scene/scene.selector.go the map screen: pins derived from the graph
inc/scene.*.go one file per scene inc/scene/scene.*.go one file per scene
``` ```
`inc` is constants and nothing else, so every package can import it and it can
import none of them. That is also why `New` sits in `inc/boot` rather than in
`inc`: a package cannot be imported by what it imports, and the assembly is the
one thing that has to name every package at once.
## Managers ## Managers
Every category that owns a collection of entities has a manager, and they are Every category that owns a collection of entities has a manager, and they are
all the engine's own `inkwell.Manager[T]` — the same registry type the `*Game` all the engine's own `inkwell.Manager[T]` — the same registry type the `*Game`
hangs its content off. The game defines no registry of its own. hangs its content off. The game defines no registry of its own.
All nine are declared together, in `boot.go`, so the list of what the game Each one is declared in its own package's manager file, beside the alias it
holds is one block rather than a line hidden in each category file: holds, and it needs no prefix because the package already is one:
```go ```go
var ( // inc/character/character.manager.go
BackgroundManager = inkwell.NewManager[Background]() package character
CharacterManager = inkwell.NewManager[Character]()
DialogManager = inkwell.NewManager[Dialog]()
ItemManager = inkwell.NewManager[Item]()
SceneManager = inkwell.NewManager[Scene]()
ScriptManager = inkwell.NewManager[Script]()
TapeManager = inkwell.NewManager[Tape]()
ThemeManager = inkwell.NewManager[Theme]()
WidgetManager = inkwell.NewManager[Widget]()
)
```
The entity types stay in their own category files, one alias each, and that is
all a manager file holds now:
```go
type Character = inkwell.Character type Character = inkwell.Character
var Manager = inkwell.NewManager[Character]()
``` ```
Read from `inc/boot` the nine of them still line up as a list —
`background.Manager`, `character.Manager`, `dialog.Manager`, `item.Manager`,
`scene.Manager`, `script.Manager`, `tape.Manager`, `theme.Manager`,
`widget.Manager` — only now the list is a consequence of the imports rather
than a block someone has to keep in step.
Aliases of engine structs already carry `GetName()` and satisfy Aliases of engine structs already carry `GetName()` and satisfy
`inkwell.Named`, which is the whole of what a manager asks of them. `inkwell.Named`, which is the whole of what a manager asks of them.
@@ -126,8 +127,8 @@ the engine, so there is nothing left for a second type to hold.
`Tape` is the exception, and it is a real one: a tape is a cassette that speaks, `Tape` is the exception, and it is a real one: a tape is a cassette that speaks,
a thing inkwell has no notion of. It names the character whose voice it is, the a thing inkwell has no notion of. It names the character whose voice it is, the
item that carries it and the dialogue it plays, and `tape.manager.go` holds the item that carries it and the dialogue it plays, and `tape.manager.go` holds the
four lookups over the registry — `IsTape`, `TapeOf`, `IsTapeItem`, four lookups over the registry — `tape.Is`, `tape.Of`, `tape.IsItem`,
`TapeDialogue`. What a tape *sounds* like is not in it: the display name and the `tape.Dialogue`. What a tape *sounds* like is not in it: the display name and the
log-only voice are `Character.Label` and `Character.Voice`, because those are log-only voice are `Character.Label` and `Character.Voice`, because those are
facts about a speaker, not about a cassette. facts about a speaker, not about a cassette.
@@ -138,11 +139,11 @@ somewhere else to keep in step — the file hands itself to its manager in an
`init()`: `init()`:
```go ```go
package inc package background
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgServerFarm, Name: inc.BgServerFarm,
Path: "assets/bg/server_farm.png", Path: "assets/bg/server_farm.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -151,13 +152,18 @@ func init() {
Adding an entity is adding a file. Deleting one is deleting a file. Package-level Adding an entity is adding a file. Deleting one is deleting a file. Package-level
variables are initialised before any `init()` runs, whatever file each sits in, variables are initialised before any `init()` runs, whatever file each sits in,
so the managers in `boot.go` exist by the time the first entity file registers so a package's `Manager` exists by the time its first entity file registers into
into one. 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 scenes, 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.
An `init()` only runs if something imports the package. `inc/boot` names eight
of the nine for their `Manager`, which is import enough; `tape` is the one
nobody names, so it is there as a blank import. That list is **per package, not
per entity** — a new scene file still touches nothing but itself.
### Widgets name their layer instead of their order ### Widgets name their layer instead of their order
A widget is a file like any other entity — `widget.<name>.go`, one widget, A widget is a file like any other entity — `widget.<name>.go`, one widget,
@@ -166,7 +172,7 @@ which the game registers as literals the same way it registers a background:
```go ```go
func init() { func init() {
WidgetManager.Register(&inkwell.Cursor{ Manager.Register(&inkwell.Cursor{
Name: "cursor", Name: "cursor",
}) })
} }
@@ -193,7 +199,7 @@ hidden for a cutscene, and that is a condition over game state, not a wrapper
and not an `if` at the top of every `Draw`: and not an `if` at the top of every `Draw`:
```go ```go
WidgetManager.Register(&inkwell.StatusLine{ Manager.Register(&inkwell.StatusLine{
Name: "status", Name: "status",
When: whenPlaying, When: whenPlaying,
Y: statusY, Y: statusY,
@@ -201,7 +207,7 @@ WidgetManager.Register(&inkwell.StatusLine{
``` ```
`whenPlaying`, `whenCutscene` and `whenUnpaused` are in `widget.manager.go`, and `whenPlaying`, `whenCutscene` and `whenUnpaused` are in `widget.manager.go`, and
they read `VarMode` and `VarNote` out of the engine's own `State`. A widget of they read `inc.VarMode` and `inc.VarNote` out of the engine's own `State`. A widget of
ours says the same thing with a method — `VisibleWhen() inkwell.Condition` ours says the same thing with a method — `VisibleWhen() inkwell.Condition`
because it has no literal to put a field in. A widget that is switched off because it has no literal to put a field in. A widget that is switched off
neither ticks nor draws nor blocks a click, so nothing else has to ask. neither ticks nor draws nor blocks a click, so nothing else has to ask.
@@ -213,14 +219,14 @@ hands ours over in their place — the types are identical, so the engine and th
game end up sharing one registry per category rather than two in step: game end up sharing one registry per category rather than two in step:
```go ```go
g.AssetManager = BackgroundManager g.AssetManager = background.Manager
g.CharacterManager = CharacterManager g.CharacterManager = character.Manager
g.DialogueManager = DialogManager g.DialogueManager = dialog.Manager
g.ItemManager = ItemManager g.ItemManager = item.Manager
g.SceneManager = SceneManager g.SceneManager = scene.Manager
g.ScriptManager = ScriptManager g.ScriptManager = script.Manager
g.WidgetManager = WidgetManager g.WidgetManager = widget.Manager
ThemeManager.Each(g.ThemeManager.Register) theme.Manager.Each(g.ThemeManager.Register)
``` ```
Themes are the odd one out and stay a copy: `NewGame` puts four preset themes Themes are the odd one out and stay a copy: `NewGame` puts four preset themes
@@ -241,14 +247,14 @@ saying "the usual", and the selector opts out by declaring both empty.
### What runs at boot ### What runs at boot
`New` names two scripts and nothing else — the opening a player sees is content `New` names two scripts and nothing else — the opening a player sees is content
like every other script, in `script.opening.go`, not a slice built in the like every other script, in `script/script.opening.go`, not a slice built in the
wiring: wiring:
```go ```go
g.StartAt(start) g.StartAt(start)
g.OnStart(ScriptOpening) g.OnStart(inc.ScriptOpening)
if o.Finale { if o.Finale {
g.OnFinale(ScriptFinale) g.OnFinale(inc.ScriptFinale)
} }
``` ```
@@ -261,60 +267,64 @@ something this package used to carry itself:
```go ```go
g.WindowScale = 2 g.WindowScale = 2
g.Player = Paul g.Player = inc.Paul
g.Walkboxes = sceneFloor g.Walkboxes = scene.Floor
g.UseWithFail = useWithFail g.UseWithFail = script.UseWithFail
``` ```
`UseWithFail` is the response to a use-with pair nobody authored — the engine `UseWithFail` is the response to a use-with pair nobody authored — the engine
asks for it the same way it asks for `ExitLook` and `ExitTake`, and the lines asks for it the same way it asks for `ExitLook` and `ExitTake`, and the lines
live with the rest of the writing, in `script.usewith_fail.go`. live with the rest of the writing, in `script/script.usewith_fail.go`.
## The world ## The world
There is exactly one game, so there is exactly one world: `World`, a There is exactly one game, so there is exactly one world — and since there is
package-level singleton in `world.manager.go`. Nothing takes a `*world` exactly one, the **package is the singleton**. There is no `World` value to pass
parameter and no widget holds a back-reference — `World.Do(…)`, `World.Mode()`, around and no back-reference for a widget to hold: `world.Do(…)`,
`World.Slot2()` are reachable from anywhere in the package. `New` calls `world.Slot2()`, `world.SetPending(…)` are reachable from anywhere that imports
`World.attach(g)`, which binds the engine and resets the runtime state, so `world`. `New` calls `world.Attach(g)`, which binds the engine and resets the
building the game twice is clean. run state, so building the game twice is clean.
`World` holds as little as it can get away with. The mode and the top bar's `world` holds as little as it can get away with. The mode and the top bar's note
note are `State` vars (`VarMode`, `VarNote`), because state the engine can see are `State` vars (`inc.VarMode`, `inc.VarNote`), because state the engine can
is state a `Condition` can read — that is what makes a widget's `When` possible see is state a `Condition` can read — that is what makes a widget's `When`
and what puts "— paused" in the top bar without anyone pushing it there. possible and what puts "— paused" in the top bar without anyone pushing it
`World.Do` hands its action to `g.Do`, the engine's queue, so the game runs one there. `world.Do` hands its action to `g.Do`, the engine's queue, so the game
action at a time without a pump of its own. What is left in the struct is the runs one action at a time without a pump of its own. What is left in package
two things the engine has no notion of: the tape waiting to speak, and the variables is the two things the engine has no notion of: the tape waiting to
cassette on its way into slot 2. speak, and the cassette on its way into slot 2. Only `world.Attach` resets them.
This is what lets an entity file be a literal: the tape-insert script closes This is what lets an entity file be a literal: the tape-insert script closes
over `World`, not over a parameter it would have had to be handed. over the `world` package, not over a parameter it would have had to be handed.
## Naming ## Naming
One package means one namespace, so an entity's constructor carries its The package is the namespace now, so an identifier never repeats what the
category as a prefix: package already says. It is `scene.Floor`, not `scene.SceneFloor`;
`tape.Dialogue`, not `tape.TapeDialogue`; `widget.Manager`, not
`widget.WidgetManager`; `world.Do`, not `world.WorldDo`. Read the call site, not
the declaration, when choosing a name:
```go ```go
sceneFloor fillSelectorPins tapeSlots useWithFail scene.FillSelectorPins() tape.Is(speaker) world.Do(a) widget.ScreenW
``` ```
Entities themselves need no name at all — they are anonymous literals inside Entities themselves need no name at all — they are anonymous literals inside
their file's `init()`, and the file name says which one it is. their file's `init()`, and the file name says which one it is.
Exported names are the authoring vocabulary — what a content file spells out: Exported names are the authoring vocabulary — what a content file spells out:
`New` and `Opts`, `World`, the managers and the entity types they hold, the `boot.New` and `boot.Opts`, the constants in `inc/constants.go`, each package's
constants in `names.manager.go`, the colour tokens, the tape lookups `Manager` and entity type, the colour tokens, the tape lookups (`tape.Is`,
(`IsTape`, `TapeOf`, `IsTapeItem`, `TapeDialogue`) and the action constructors `tape.Of`, `tape.IsItem`, `tape.Dialogue`), the world (`world.Do`,
(`Paused`, `SetMode`, `UseTheme`, `TapeOffer`, `Fn`). A tape's line is `world.Slot2`, …) and the action constructors (`world.Paused`, `world.SetMode`,
`inkwell.Say` like anyone else's — the tape voice is on the character, not on a `world.UseTheme`, `world.TapeOffer`, `world.Fn`). A tape's line is `inkwell.Say`
second spelling of Say. like anyone else's — the tape voice is on the character, not on a second
spelling of Say.
Everything else is machinery and stays unexported: the HUD widgets Everything else stays unexported, and now the compiler holds the line: the HUD
(`tapeSlots`, `letterbox`, `hudFrame`, …), the conditions (`whenPlaying`, widgets (`tapeSlots`, `letterbox`, `hudFrame`, …), the conditions
`whenCutscene`, `whenUnpaused`), the runners, `useWithFail`, (`whenPlaying`, `whenCutscene`, `whenUnpaused`), the runners, `setMode`,
`fillSelectorPins`. `setNote`, `setVarIfEmpty`.
The word is **scene**, the engine's own. The wiki and the concept-art deck count 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 *screens*, and this code used to as well, but everything a screen had that a
@@ -323,23 +333,25 @@ display: `ScreenW`, `ScreenH`.
## Layering ## Layering
Nothing is enforced by the compiler any more, so the layering is a rule kept by The layering is the import graph, so the compiler keeps it:
hand:
``` ```
names ← theme ← world ← widget inc ← theme ← world ← tape ← widget ← boot ← main
↑ ↑
content ───── boot ← main └── content ───────────────────-┘
``` ```
`world` knows nothing about the HUD or the content. Both build on it, never the `inc` imports nothing and everything imports it. `world` knows nothing about the
other way round. HUD or the content; both build on it, never the other way round — and an arrow
pointing back is now an import cycle, not a review comment. The one edge worth
knowing is `widget → tape`: the tape slots ask which cassette a tape is in, so
the tape concept sits below the HUD rather than beside it.
## Adding a scene ## Adding a scene
1. Constants in `inc/names.manager.go`: `Scene<Name>`, `Bg<Name>` 1. Constants in `inc/constants.go`: `Scene<Name>`, `Bg<Name>`
2. `inc/scene.<name>.go` — an `init()` registering a `Scene` 2. `inc/scene/scene.<name>.go` — an `init()` registering a `Scene`
3. `inc/background.<name>.go` — an `init()` registering a `Background` 3. `inc/background/background.<name>.go` — an `init()` registering a `Background`
4. A 640×380 PNG in `assets/bg/` 4. A 640×380 PNG in `assets/bg/`
Nothing else moves. There is no list to update. Nothing else moves. There is no list to update.
+53 -47
View File
@@ -110,7 +110,7 @@ Every pixel it gives back is a pixel of art.
``` ```
Every vertical measurement derives from `LineH` (glyph cell + leading) and from Every vertical measurement derives from `LineH` (glyph cell + leading) and from
`ScreenH`, both in `widget.manager.go`, not from the wireframe deck's ratios, so `ScreenH`, both in `widget/widget.manager.go`, not from the wireframe deck's ratios, so
the layout cannot drift out of step with the font — or with the art — again; the layout cannot drift out of step with the font — or with the art — again;
`TestLayoutFitsTheFont` guards it. The tape channel comes out at 4 rows of 38 `TestLayoutFitsTheFont` guards it. The tape channel comes out at 4 rows of 38
columns, which is about one and a half of Dex's remarks on screen at once. columns, which is about one and a half of Dex's remarks on screen at once.
@@ -150,74 +150,76 @@ licence.
## Structure ## Structure
The game is one flat package, `inc`. There are no subdirectories: a file's One category, one package, one directory. A file still says which entity it
name says where it belongs, in the form `[category].[name].go`. The category is holds in its own name — `[category].[name].go`, the prefix repeated inside the
always singular, and `[category].manager.go` is the file that ties that category directory that already carries it, because a file called `alley.go` tells you
together — its entity type and whatever else the category shares. `boot.go` is nothing in a list of open editor tabs.
the one file with no category: it declares every manager and builds the game.
``` ```
main.go flags + inkwell.Run main.go flags + inkwell.Run
inc/boot.go the managers, and New(Opts) inc/constants.go every name and state key; imports nothing
inc/names.manager.go entity names and world-state keys inc/boot/ New(Opts): the hand-off to the engine
inc/theme.*.go realworld-93 + nokia-punk inc/theme/ the Theme alias, RGB, realworld-93, nokia-punk
inc/world.*.go unsaved runtime state and custom actions inc/world/ the run state and the custom actions
inc/widget.manager.go HUD layout and the shared visibility conditions inc/tape/ the Tape entity and the lookups over it
inc/widget.*.go one widget per file inc/widget/ HUD layout, the visibility conditions, 14 widgets
inc/<kind>.<name>.go one file per registered entity, by kind inc/scene/ inc/background/ one file per location, twice
inc/item/ inc/character/ inc/dialog/ inc/script/
``` ```
Nothing is enforced by the compiler any more, so the layering is a rule the `inc` itself holds nothing but constants, which is why every package can import
code keeps by hand: the world holds no opinion about the HUD or the content, and it and it can import none of them. The consequence is that the assembly moved
both build on it, never the other way round. down rather than up: `New` lives in `inc/boot`, because a package cannot be
imported by what it imports.
The layering is no longer a rule kept by hand — it is the import graph, and the
compiler rejects the arrow that points the wrong way:
``` ```
names ← theme ← world ← widget inc ← theme ← world ← tape ← widget ← boot ← main
↑ ↑
content ───── boot ← main └── content ───────────────────-┘
``` ```
Content is one registered entity per file, and the category prefix groups them Adding a scene means adding `inc/scene/scene.<name>.go` and
the way directories used to: `inc/background/background.<name>.go`. Nothing else moves.
```
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
scene.*.go one file per scene: scene.alley.go, … + scene.selector.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 engine's own Every category owns a manager, and they are all the engine's own
`inkwell.Manager[T]` — the same registry the `*Game` hangs its content off, kept `inkwell.Manager[T]` — the same registry the `*Game` hangs its content off, kept
in registration order and addressed by name. There is no second registry type in registration order and addressed by name. There is no second registry type
here: the entity types are aliases of engine structs, so they already satisfy here: the entity types are aliases of engine structs, so they already satisfy
`inkwell.Named` and need no help reading their own name. The eight of them are `inkwell.Named` and need no help reading their own name. Each one lives in its
declared as one block in `boot.go`, and a category's manager file is left own package's manager file, next to the alias it holds, and it needs no prefix
holding the alias: because the package already is one:
```go ```go
// inc/character/character.manager.go
type Character = inkwell.Character type Character = inkwell.Character
var Manager = inkwell.NewManager[Character]()
``` ```
An entity file is a literal that hands itself over in an `init()`: An entity file is a literal that hands itself over in an `init()`:
```go ```go
package background
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgServerFarm, Name: inc.BgServerFarm,
Path: "assets/bg/server_farm.png", Path: "assets/bg/server_farm.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
} }
``` ```
Widgets go the same way, the engine's own included: `widget.cursor.go` registers The `init()` only runs if something imports the package, so a package whose
an `inkwell.Cursor` exactly as `background.street.go` registers an image. What entities nobody names by symbol needs a blank import in `inc/boot`. Exactly one
does — `tape`, everything else is imported for its `Manager` — and the list is
per package, not per entity, so adding a file is still adding a file.
Widgets go the same way, the engine's own included: `widget/widget.cursor.go` registers
an `inkwell.Cursor` exactly as `background/background.street.go` registers an image. What
they cannot take from alphabetical order is their place in the stack, so each they cannot take from alphabetical order is their place in the stack, so each
widget names a **layer**`LayerScene`, `LayerPanel`, `LayerHUD`, `LayerSpeech`, widget names a **layer**`LayerScene`, `LayerPanel`, `LayerHUD`, `LayerSpeech`,
`LayerDialog`, `LayerMenu`, `LayerCurtain`, `LayerCursor` — and inkwell draws `LayerDialog`, `LayerMenu`, `LayerCurtain`, `LayerCursor` — and inkwell draws
@@ -248,7 +250,7 @@ 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:
`SceneManager.Get("alley")` answers long before there is a `*Game` to ask. And `scene.Manager.Get("alley")` answers long before there is a `*Game` to ask. And
when the game is built, nothing is copied into it — `New` assigns our managers when the game is built, nothing is copied into it — `New` assigns our managers
onto the `*Game` in place of the empty ones `NewGame` made, so engine and game onto the `*Game` in place of the empty ones `NewGame` made, so engine and game
share one registry per category instead of keeping two of them in step. Themes share one registry per category instead of keeping two of them in step. Themes
@@ -261,12 +263,16 @@ his `Start`, and `g.Walkboxes` is the floor a scene without one walks on. Both
are fields on the `*Game`, so a scene file that says nothing about either means are fields on the `*Game`, so a scene file that says nothing about either means
"the usual", and the selector opts out by declaring both empty. What is still "the usual", and the selector opts out by declaring both empty. What is still
written back before the hand-off is the derived half of the map: written back before the hand-off is the derived half of the map:
`fillSelectorPins` reads the exit graph backwards and `Set`s the selector. `scene.FillSelectorPins` reads the exit graph backwards and `Set`s the selector.
There is one world, and it is a package-level singleton: `World`. Nothing takes There is one world, and now the package *is* the singleton: `world.Do(…)`,
a `*world` parameter and no widget holds a back-reference, which is what lets a `world.Slot2()`, `world.SetPending(…)`. Nothing takes a world parameter and no
script file be a literal — the tape-insert script closes over `World` rather widget holds a back-reference, which is what lets a script file be a literal —
than over a parameter someone would have had to thread to it. the tape-insert script closes over the `world` package rather than over a
parameter someone would have had to thread to it. What little it still keeps is
in package-level variables that only `world.Attach` may reset; the mode and the
top bar's note are not among them, because those are `State` vars the engine
itself can see.
**On the word "scene".** The wiki, the concept-art deck and the beat tables all **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` count *screens*, and this code used to as well: it carried its own `Screen`
@@ -293,7 +299,7 @@ edge and so does the code:
- **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 scene every main location connects 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 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 `scene/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 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. list of locations on the map is never written down twice.
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgAlley, Name: inc.BgAlley,
Path: "assets/bg/alley.png", Path: "assets/bg/alley.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgBBSTerminal, Name: inc.BgBBSTerminal,
Path: "assets/bg/bbs_terminal.png", Path: "assets/bg/bbs_terminal.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgBVKBranch, Name: inc.BgBVKBranch,
Path: "assets/bg/bvk_branch.png", Path: "assets/bg/bvk_branch.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgColumbarium, Name: inc.BgColumbarium,
Path: "assets/bg/columbarium.png", Path: "assets/bg/columbarium.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgCuratorShop, Name: inc.BgCuratorShop,
Path: "assets/bg/curator_shop.png", Path: "assets/bg/curator_shop.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgHackerspace, Name: inc.BgHackerspace,
Path: "assets/bg/hackerspace.png", Path: "assets/bg/hackerspace.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgHospital, Name: inc.BgHospital,
Path: "assets/bg/hospital.png", Path: "assets/bg/hospital.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgIceCreamShop, Name: inc.BgIceCreamShop,
Path: "assets/bg/ice_cream_shop.png", Path: "assets/bg/ice_cream_shop.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -1,7 +1,9 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
) )
type Background = inkwell.Asset type Background = inkwell.Asset
var Manager = inkwell.NewManager[Background]()
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgNoodleHouse, Name: inc.BgNoodleHouse,
Path: "assets/bg/noodle_house.png", Path: "assets/bg/noodle_house.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgNormanApartment, Name: inc.BgNormanApartment,
Path: "assets/bg/norman_apartment.png", Path: "assets/bg/norman_apartment.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgPaulShop, Name: inc.BgPaulShop,
Path: "assets/bg/paul_shop.png", Path: "assets/bg/paul_shop.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgPoliceStation, Name: inc.BgPoliceStation,
Path: "assets/bg/police_station.png", Path: "assets/bg/police_station.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgPublicBBS, Name: inc.BgPublicBBS,
Path: "assets/bg/public_bbs.png", Path: "assets/bg/public_bbs.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgRooftopHideout, Name: inc.BgRooftopHideout,
Path: "assets/bg/rooftop_hideout.png", Path: "assets/bg/rooftop_hideout.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgSamizdatPress, Name: inc.BgSamizdatPress,
Path: "assets/bg/samizdat_press.png", Path: "assets/bg/samizdat_press.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgScrapMarket, Name: inc.BgScrapMarket,
Path: "assets/bg/scrap_market.png", Path: "assets/bg/scrap_market.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgSecretClub, Name: inc.BgSecretClub,
Path: "assets/bg/secret_club.png", Path: "assets/bg/secret_club.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgSecretLab, Name: inc.BgSecretLab,
Path: "assets/bg/secret_lab.png", Path: "assets/bg/secret_lab.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgSelector, Name: inc.BgSelector,
Path: "assets/bg/selector.png", Path: "assets/bg/selector.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgServerFarm, Name: inc.BgServerFarm,
Path: "assets/bg/server_farm.png", Path: "assets/bg/server_farm.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgShowroom, Name: inc.BgShowroom,
Path: "assets/bg/showroom.png", Path: "assets/bg/showroom.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgSmallRestaurant, Name: inc.BgSmallRestaurant,
Path: "assets/bg/small_restaurant.png", Path: "assets/bg/small_restaurant.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgStreet, Name: inc.BgStreet,
Path: "assets/bg/street.png", Path: "assets/bg/street.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
@@ -1,12 +1,13 @@
package inc package background
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
BackgroundManager.Register(Background{ Manager.Register(Background{
Name: BgTrinketShop, Name: inc.BgTrinketShop,
Path: "assets/bg/trinket_shop.png", Path: "assets/bg/trinket_shop.png",
Kind: inkwell.AssetImage, Kind: inkwell.AssetImage,
}) })
-64
View File
@@ -1,64 +0,0 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
var (
BackgroundManager = inkwell.NewManager[Background]()
CharacterManager = inkwell.NewManager[Character]()
DialogManager = inkwell.NewManager[Dialog]()
ItemManager = inkwell.NewManager[Item]()
SceneManager = inkwell.NewManager[Scene]()
ScriptManager = inkwell.NewManager[Script]()
TapeManager = inkwell.NewManager[Tape]()
ThemeManager = inkwell.NewManager[Theme]()
WidgetManager = inkwell.NewManager[Widget]()
)
type Opts struct {
Scene string
Finale bool
}
func New(o Opts) *inkwell.Game {
start := o.Scene
if start == "" {
start = SceneAlley
}
fillSelectorPins()
g := inkwell.NewGame("Real World", ScreenW, ScreenH)
g.MaxLogLines = 64
g.WindowScale = 2
g.SceneRect = inkwell.Rect(0, TopBarH, ScreenW, HUDTop-TopBarH)
g.Player = Paul
g.Walkboxes = sceneFloor
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.")
}
g.UseWithFail = useWithFail
g.AssetManager = BackgroundManager
g.CharacterManager = CharacterManager
g.DialogueManager = DialogManager
g.ItemManager = ItemManager
g.SceneManager = SceneManager
g.ScriptManager = ScriptManager
g.WidgetManager = WidgetManager
ThemeManager.Each(g.ThemeManager.Register)
World.attach(g)
g.UseTheme(RealWorld)
g.StartAt(start)
g.OnStart(ScriptOpening)
if o.Finale {
g.OnFinale(ScriptFinale)
}
return g
}
+63
View File
@@ -0,0 +1,63 @@
package boot
import (
inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
"git.teletypegames.org/games/realworld/inc/background"
"git.teletypegames.org/games/realworld/inc/character"
"git.teletypegames.org/games/realworld/inc/dialog"
"git.teletypegames.org/games/realworld/inc/item"
"git.teletypegames.org/games/realworld/inc/scene"
"git.teletypegames.org/games/realworld/inc/script"
_ "git.teletypegames.org/games/realworld/inc/tape"
"git.teletypegames.org/games/realworld/inc/theme"
"git.teletypegames.org/games/realworld/inc/widget"
"git.teletypegames.org/games/realworld/inc/world"
)
type Opts struct {
Scene string
Finale bool
}
func New(o Opts) *inkwell.Game {
start := o.Scene
if start == "" {
start = inc.SceneAlley
}
scene.FillSelectorPins()
g := inkwell.NewGame("Real World", widget.ScreenW, widget.ScreenH)
g.MaxLogLines = 64
g.WindowScale = 2
g.SceneRect = inkwell.Rect(0, widget.TopBarH, widget.ScreenW, widget.HUDTop-widget.TopBarH)
g.Player = inc.Paul
g.Walkboxes = scene.Floor
g.ExitLook = func(e inkwell.Exit) inkwell.Action {
return inkwell.Say(inc.Paul, "That way: "+e.Label+".")
}
g.ExitTake = func(inkwell.Exit) inkwell.Action {
return inkwell.Say(inc.Paul, "It's a way out, not a thing.")
}
g.UseWithFail = script.UseWithFail
g.AssetManager = background.Manager
g.CharacterManager = character.Manager
g.DialogueManager = dialog.Manager
g.ItemManager = item.Manager
g.SceneManager = scene.Manager
g.ScriptManager = script.Manager
g.WidgetManager = widget.Manager
theme.Manager.Each(g.ThemeManager.Register)
world.Attach(g)
g.UseTheme(inc.RealWorld)
g.StartAt(start)
g.OnStart(inc.ScriptOpening)
if o.Finale {
g.OnFinale(inc.ScriptFinale)
}
return g
}
-14
View File
@@ -1,14 +0,0 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
CharacterManager.Register(Character{
Name: Dex,
Label: "DEX",
Voice: inkwell.VoiceLog,
SpeechColor: Amber,
})
}
-14
View File
@@ -1,14 +0,0 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
CharacterManager.Register(Character{
Name: TapeMystery,
Label: "UNKNOWN TAPE",
Voice: inkwell.VoiceLog,
SpeechColor: RGB(0xB9A98A),
})
}
-19
View File
@@ -1,19 +0,0 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
CharacterManager.Register(Character{
Name: Paul,
Speed: 96,
W: 28,
H: 68,
Start: inkwell.Point{
X: 320,
Y: 232,
},
SpeechColor: Ink,
})
}
-14
View File
@@ -1,14 +0,0 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
CharacterManager.Register(Character{
Name: TapeSupport,
Label: "TECH SUPPORT",
Voice: inkwell.VoiceLog,
SpeechColor: RGB(0xE8C86A),
})
}
+16
View File
@@ -0,0 +1,16 @@
package character
import (
inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
"git.teletypegames.org/games/realworld/inc/theme"
)
func init() {
Manager.Register(Character{
Name: inc.Dex,
Label: "DEX",
Voice: inkwell.VoiceLog,
SpeechColor: theme.Amber,
})
}
@@ -1,7 +1,9 @@
package inc package character
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
) )
type Character = inkwell.Character type Character = inkwell.Character
var Manager = inkwell.NewManager[Character]()
+16
View File
@@ -0,0 +1,16 @@
package character
import (
inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
"git.teletypegames.org/games/realworld/inc/theme"
)
func init() {
Manager.Register(Character{
Name: inc.TapeMystery,
Label: "UNKNOWN TAPE",
Voice: inkwell.VoiceLog,
SpeechColor: theme.RGB(0xB9A98A),
})
}
+21
View File
@@ -0,0 +1,21 @@
package character
import (
inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
"git.teletypegames.org/games/realworld/inc/theme"
)
func init() {
Manager.Register(Character{
Name: inc.Paul,
Speed: 96,
W: 28,
H: 68,
Start: inkwell.Point{
X: 320,
Y: 232,
},
SpeechColor: theme.Ink,
})
}
+16
View File
@@ -0,0 +1,16 @@
package character
import (
inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
"git.teletypegames.org/games/realworld/inc/theme"
)
func init() {
Manager.Register(Character{
Name: inc.TapeSupport,
Label: "TECH SUPPORT",
Voice: inkwell.VoiceLog,
SpeechColor: theme.RGB(0xE8C86A),
})
}
@@ -14,6 +14,11 @@ const (
ItemBlackArmilla = "black_market_armilla" ItemBlackArmilla = "black_market_armilla"
) )
const (
RealWorld = "realworld-93"
NokiaPunk = "nokia-punk"
)
const ( const (
DlgDex = "dex_talk" DlgDex = "dex_talk"
DlgMysteryTape = "mystery_tape_silent" DlgMysteryTape = "mystery_tape_silent"
@@ -1,40 +1,41 @@
package inc package dialog
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
DialogManager.Register(Dialog{ Manager.Register(Dialog{
Name: DlgDex, Name: inc.DlgDex,
Start: "root", Start: "root",
Nodes: []inkwell.DialogueNode{{ Nodes: []inkwell.DialogueNode{{
Name: "root", Name: "root",
Lines: []inkwell.DialogueLine{{ Lines: []inkwell.DialogueLine{{
Speaker: Dex, Speaker: inc.Dex,
Text: "Talk.", Text: "Talk.",
}}, }},
Choices: []inkwell.DialogueChoice{ Choices: []inkwell.DialogueChoice{
{ {
Text: "What am I doing here, Dex?", Text: "What am I doing here, Dex?",
Actions: []inkwell.Action{ Actions: []inkwell.Action{
inkwell.Say(Dex, "You got a letter from a man you hadn't seen in twenty years. And you came. That says more about you than it does about him."), inkwell.Say(inc.Dex, "You got a letter from a man you hadn't seen in twenty years. And you came. That says more about you than it does about him."),
inkwell.GotoNode("root"), inkwell.GotoNode("root"),
}, },
}, },
{ {
Text: "What do you know about Noodle?", Text: "What do you know about Noodle?",
Show: inkwell.Not(inkwell.Flag(FlagHasTape)), Show: inkwell.Not(inkwell.Flag(inc.FlagHasTape)),
Actions: []inkwell.Action{ Actions: []inkwell.Action{
inkwell.Say(Dex, "He traded cracked Armillas. That isn't charity, Paul, that's a business. The kind they take you in for."), inkwell.Say(inc.Dex, "He traded cracked Armillas. That isn't charity, Paul, that's a business. The kind they take you in for."),
inkwell.GotoNode("root"), inkwell.GotoNode("root"),
}, },
}, },
{ {
Text: "What do you make of this tape?", Text: "What do you make of this tape?",
Show: inkwell.Flag(FlagHasTape), Show: inkwell.Flag(inc.FlagHasTape),
Actions: []inkwell.Action{ Actions: []inkwell.Action{
inkwell.Say(Dex, "I make of it that you found a password-locked tape in a gap between two bins, on a tip from a government office. Count how many times that has ended well."), inkwell.Say(inc.Dex, "I make of it that you found a password-locked tape in a gap between two bins, on a tip from a government office. Count how many times that has ended well."),
inkwell.GotoNode("root"), inkwell.GotoNode("root"),
}, },
}, },
@@ -42,7 +43,7 @@ func init() {
Text: "I miss you.", Text: "I miss you.",
Once: true, Once: true,
Actions: []inkwell.Action{ Actions: []inkwell.Action{
inkwell.Say(Dex, "I'm four kilobytes of a dead man. Don't do this to yourself."), inkwell.Say(inc.Dex, "I'm four kilobytes of a dead man. Don't do this to yourself."),
inkwell.GotoNode("root"), inkwell.GotoNode("root"),
}, },
}, },
@@ -1,7 +1,9 @@
package inc package dialog
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
) )
type Dialog = inkwell.Dialogue type Dialog = inkwell.Dialogue
var Manager = inkwell.NewManager[Dialog]()
@@ -1,24 +1,25 @@
package inc package dialog
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
DialogManager.Register(Dialog{ Manager.Register(Dialog{
Name: DlgMysteryTape, Name: inc.DlgMysteryTape,
Start: "root", Start: "root",
Nodes: []inkwell.DialogueNode{{ Nodes: []inkwell.DialogueNode{{
Name: "root", Name: "root",
Lines: []inkwell.DialogueLine{{ Lines: []inkwell.DialogueLine{{
Speaker: Dex, Speaker: inc.Dex,
Text: "Nothing. Warm, spinning, and silent.", Text: "Nothing. Warm, spinning, and silent.",
}}, }},
Choices: []inkwell.DialogueChoice{ Choices: []inkwell.DialogueChoice{
{ {
Text: "Are you sure it works?", Text: "Are you sure it works?",
Actions: []inkwell.Action{ Actions: []inkwell.Action{
inkwell.Say(Dex, "It works. It just isn't talking to you. That is not the same as silence."), inkwell.Say(inc.Dex, "It works. It just isn't talking to you. That is not the same as silence."),
inkwell.GotoNode("root"), inkwell.GotoNode("root"),
}, },
}, },
-22
View File
@@ -1,22 +0,0 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
ItemManager.Register(Item{
Name: ItemBlackArmilla,
Description: "black-market Armilla",
OnUseSelf: inkwell.Seq(
inkwell.Say(Paul, "Jailbroken. Pushes ads, but it was cheap."),
inkwell.Say(Dex, "Two slots, and both of them lie about the temperature. Don't trade me in for it."),
),
OnUseWith: map[string]inkwell.Action{
ItemNoodleLetter: inkwell.Seq(
inkwell.Say(Paul, "A letter and a bracelet. Brilliant."),
inkwell.Say(Dex, "Nothing. Which is what I'd charge for it."),
),
},
})
}
-16
View File
@@ -1,16 +0,0 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
ItemManager.Register(Item{
Name: ItemMysteryTape,
Description: "unmarked Personal Tape",
OnUseSelf: inkwell.Seq(
inkwell.Say(Paul, "Password-locked. Of course."),
inkwell.Say(Dex, "Put it in the second slot if you care that much. I did warn you."),
),
})
}
-16
View File
@@ -1,16 +0,0 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
ItemManager.Register(Item{
Name: ItemNoodleLetter,
Description: "Noodle's letter",
OnUseSelf: inkwell.Seq(
inkwell.Say(Paul, "“Come out, Paul. Not a phone conversation.” That's all it says."),
inkwell.Say(Dex, "And now you're here. And he isn't."),
),
})
}
+23
View File
@@ -0,0 +1,23 @@
package item
import (
inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
)
func init() {
Manager.Register(Item{
Name: inc.ItemBlackArmilla,
Description: "black-market Armilla",
OnUseSelf: inkwell.Seq(
inkwell.Say(inc.Paul, "Jailbroken. Pushes ads, but it was cheap."),
inkwell.Say(inc.Dex, "Two slots, and both of them lie about the temperature. Don't trade me in for it."),
),
OnUseWith: map[string]inkwell.Action{
inc.ItemNoodleLetter: inkwell.Seq(
inkwell.Say(inc.Paul, "A letter and a bracelet. Brilliant."),
inkwell.Say(inc.Dex, "Nothing. Which is what I'd charge for it."),
),
},
})
}
@@ -1,7 +1,9 @@
package inc package item
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
) )
type Item = inkwell.Item type Item = inkwell.Item
var Manager = inkwell.NewManager[Item]()
+17
View File
@@ -0,0 +1,17 @@
package item
import (
inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
)
func init() {
Manager.Register(Item{
Name: inc.ItemMysteryTape,
Description: "unmarked Personal Tape",
OnUseSelf: inkwell.Seq(
inkwell.Say(inc.Paul, "Password-locked. Of course."),
inkwell.Say(inc.Dex, "Put it in the second slot if you care that much. I did warn you."),
),
})
}
+17
View File
@@ -0,0 +1,17 @@
package item
import (
inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
)
func init() {
Manager.Register(Item{
Name: inc.ItemNoodleLetter,
Description: "Noodle's letter",
OnUseSelf: inkwell.Seq(
inkwell.Say(inc.Paul, "“Come out, Paul. Not a phone conversation.” That's all it says."),
inkwell.Say(inc.Dex, "And now you're here. And he isn't."),
),
})
}
-100
View File
@@ -1,100 +0,0 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
SceneManager.Register(Scene{
Name: SceneAlley,
Title: "ALLEY — BEHIND NOODLE'S HOUSE",
Background: BgAlley,
Exits: []inkwell.Exit{
{
To: SceneNoodleHouse,
Label: "back out to the street",
Side: inkwell.ExitLeft,
},
},
Actors: []inkwell.SceneActor{
{
CharacterName: Paul,
At: inkwell.Point{
X: 120,
Y: 232,
},
},
},
OnEnter: setVarIfEmpty(VarArmillaStrip, "SRP: 1 tape"),
Hotspots: []inkwell.Hotspot{hidingPlace(), backDoor(), bin(), fireEscape()},
})
}
func hidingPlace() inkwell.Hotspot {
return inkwell.Hotspot{
Name: "hiding_place",
Label: "gap at the foot of the wall",
Area: inkwell.Rect(416, 150, 68, 52),
OnLook: inkwell.If(inkwell.Flag(FlagHasTape),
inkwell.Say(Paul, "Empty. Whatever was in there is on me now."),
inkwell.Say(Paul, "Loose brick. There's a gap behind it."),
),
OnUse: inkwell.If(inkwell.Flag(FlagPoliceTip),
inkwell.If(inkwell.Flag(FlagHasTape),
inkwell.Say(Paul, "There's nothing else in there."),
inkwell.Seq(
inkwell.Say(Paul, "“Behind the brick.” All right then."),
inkwell.Give(ItemMysteryTape),
inkwell.SetFlag(FlagHasTape),
inkwell.Say(Paul, "A Personal Tape. No label, no seal."),
inkwell.Say(Dex, "Password-locked. And somebody very much did not want it found."),
),
),
inkwell.Say(Paul, "One loose brick. I'm not taking the alley apart over it."),
),
OnTake: inkwell.Say(Paul, "I'm not carrying a wall."),
}
}
func backDoor() inkwell.Hotspot {
return inkwell.Hotspot{
Name: "back_door",
Label: "locked back door",
Area: inkwell.Rect(72, 106, 80, 124),
OnLook: inkwell.Say(Paul, "Keypad. Four digits, worn keys."),
OnUse: inkwell.Seq(
inkwell.Say(Paul, "Locked. And I'm not guessing four digits."),
inkwell.Say(Dex, "The wear is heaviest on the two and the seven. That isn't a code. It's fewer options."),
),
OnTalk: inkwell.Say(Paul, "To the door? I'm not there yet."),
OnUseWith: map[string]inkwell.Action{
ItemBlackArmilla: inkwell.Seq(
inkwell.Say(Paul, "A black-market bracelet doesn't open a keypad."),
inkwell.Say(Dex, "But you do get an advert out of it."),
),
},
}
}
func bin() inkwell.Hotspot {
return inkwell.Hotspot{
Name: "bin",
Label: "toppled bin",
Area: inkwell.Rect(240, 170, 88, 60),
OnLook: inkwell.Say(Paul, "Somebody's been through it. Thoroughly."),
OnUse: inkwell.Seq(
inkwell.Say(Paul, "Already turned out. I'm not going to be the second one."),
inkwell.Say(Dex, "The police don't go through bins. So who did?"),
),
}
}
func fireEscape() inkwell.Hotspot {
return inkwell.Hotspot{
Name: "fire_escape",
Label: "fire escape",
Area: inkwell.Rect(528, 44, 88, 160),
OnLook: inkwell.Say(Paul, "Runs all the way to the roof. Bottom rung is two metres over my head."),
OnUse: inkwell.Say(Paul, "Can't reach it. I'd need something to stand on."),
}
}
-28
View File
@@ -1,28 +0,0 @@
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."),
},
},
})
}
+101
View File
@@ -0,0 +1,101 @@
package scene
import (
inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
)
func init() {
Manager.Register(Scene{
Name: inc.SceneAlley,
Title: "ALLEY — BEHIND NOODLE'S HOUSE",
Background: inc.BgAlley,
Exits: []inkwell.Exit{
{
To: inc.SceneNoodleHouse,
Label: "back out to the street",
Side: inkwell.ExitLeft,
},
},
Actors: []inkwell.SceneActor{
{
CharacterName: inc.Paul,
At: inkwell.Point{
X: 120,
Y: 232,
},
},
},
OnEnter: setVarIfEmpty(inc.VarArmillaStrip, "SRP: 1 tape"),
Hotspots: []inkwell.Hotspot{hidingPlace(), backDoor(), bin(), fireEscape()},
})
}
func hidingPlace() inkwell.Hotspot {
return inkwell.Hotspot{
Name: "hiding_place",
Label: "gap at the foot of the wall",
Area: inkwell.Rect(416, 150, 68, 52),
OnLook: inkwell.If(inkwell.Flag(inc.FlagHasTape),
inkwell.Say(inc.Paul, "Empty. Whatever was in there is on me now."),
inkwell.Say(inc.Paul, "Loose brick. There's a gap behind it."),
),
OnUse: inkwell.If(inkwell.Flag(inc.FlagPoliceTip),
inkwell.If(inkwell.Flag(inc.FlagHasTape),
inkwell.Say(inc.Paul, "There's nothing else in there."),
inkwell.Seq(
inkwell.Say(inc.Paul, "“Behind the brick.” All right then."),
inkwell.Give(inc.ItemMysteryTape),
inkwell.SetFlag(inc.FlagHasTape),
inkwell.Say(inc.Paul, "A Personal Tape. No label, no seal."),
inkwell.Say(inc.Dex, "Password-locked. And somebody very much did not want it found."),
),
),
inkwell.Say(inc.Paul, "One loose brick. I'm not taking the alley apart over it."),
),
OnTake: inkwell.Say(inc.Paul, "I'm not carrying a wall."),
}
}
func backDoor() inkwell.Hotspot {
return inkwell.Hotspot{
Name: "back_door",
Label: "locked back door",
Area: inkwell.Rect(72, 106, 80, 124),
OnLook: inkwell.Say(inc.Paul, "Keypad. Four digits, worn keys."),
OnUse: inkwell.Seq(
inkwell.Say(inc.Paul, "Locked. And I'm not guessing four digits."),
inkwell.Say(inc.Dex, "The wear is heaviest on the two and the seven. That isn't a code. It's fewer options."),
),
OnTalk: inkwell.Say(inc.Paul, "To the door? I'm not there yet."),
OnUseWith: map[string]inkwell.Action{
inc.ItemBlackArmilla: inkwell.Seq(
inkwell.Say(inc.Paul, "A black-market bracelet doesn't open a keypad."),
inkwell.Say(inc.Dex, "But you do get an advert out of it."),
),
},
}
}
func bin() inkwell.Hotspot {
return inkwell.Hotspot{
Name: "bin",
Label: "toppled bin",
Area: inkwell.Rect(240, 170, 88, 60),
OnLook: inkwell.Say(inc.Paul, "Somebody's been through it. Thoroughly."),
OnUse: inkwell.Seq(
inkwell.Say(inc.Paul, "Already turned out. I'm not going to be the second one."),
inkwell.Say(inc.Dex, "The police don't go through bins. So who did?"),
),
}
}
func fireEscape() inkwell.Hotspot {
return inkwell.Hotspot{
Name: "fire_escape",
Label: "fire escape",
Area: inkwell.Rect(528, 44, 88, 160),
OnLook: inkwell.Say(inc.Paul, "Runs all the way to the roof. Bottom rung is two metres over my head."),
OnUse: inkwell.Say(inc.Paul, "Can't reach it. I'd need something to stand on."),
}
}
@@ -1,14 +1,15 @@
package inc package scene
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
SceneManager.Register(Scene{ Manager.Register(Scene{
Name: SceneBBSTerminal, Name: inc.SceneBBSTerminal,
Title: "TERMINAL — BBS ACCESS POINT", Title: "TERMINAL — BBS ACCESS POINT",
Background: BgBBSTerminal, Background: inc.BgBBSTerminal,
Exits: []inkwell.Exit{ Exits: []inkwell.Exit{
{ {
Label: "step back from the terminal", Label: "step back from the terminal",
@@ -1,14 +1,15 @@
package inc package scene
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
SceneManager.Register(Scene{ Manager.Register(Scene{
Name: SceneBVKBranch, Name: inc.SceneBVKBranch,
Title: "BVK — SAN FRANCISCO BRANCH", Title: "BVK — SAN FRANCISCO BRANCH",
Background: BgBVKBranch, Background: inc.BgBVKBranch,
Exits: []inkwell.Exit{ Exits: []inkwell.Exit{
exitToSelector, exitToSelector,
}, },
@@ -1,14 +1,15 @@
package inc package scene
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
SceneManager.Register(Scene{ Manager.Register(Scene{
Name: SceneColumbarium, Name: inc.SceneColumbarium,
Title: "COLUMBARIUM — DEX'S MEMORIAL", Title: "COLUMBARIUM — DEX'S MEMORIAL",
Background: BgColumbarium, Background: inc.BgColumbarium,
Exits: []inkwell.Exit{ Exits: []inkwell.Exit{
exitToSelector, exitToSelector,
}, },
@@ -1,14 +1,15 @@
package inc package scene
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
SceneManager.Register(Scene{ Manager.Register(Scene{
Name: SceneCuratorShop, Name: inc.SceneCuratorShop,
Title: "CURATOR SERVICE — THE MASTER'S WORKSHOP", Title: "CURATOR SERVICE — THE MASTER'S WORKSHOP",
Background: BgCuratorShop, Background: inc.BgCuratorShop,
Exits: []inkwell.Exit{ Exits: []inkwell.Exit{
exitToSelector, exitToSelector,
}, },
@@ -1,14 +1,15 @@
package inc package scene
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
SceneManager.Register(Scene{ Manager.Register(Scene{
Name: SceneHackerspace, Name: inc.SceneHackerspace,
Title: "HACKERSPACE", Title: "HACKERSPACE",
Background: BgHackerspace, Background: inc.BgHackerspace,
Exits: []inkwell.Exit{ Exits: []inkwell.Exit{
exitToSelector, exitToSelector,
}, },
@@ -1,14 +1,15 @@
package inc package scene
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
SceneManager.Register(Scene{ Manager.Register(Scene{
Name: SceneHospital, Name: inc.SceneHospital,
Title: "HOSPITAL — PSYCHIATRIC WING", Title: "HOSPITAL — PSYCHIATRIC WING",
Background: BgHospital, Background: inc.BgHospital,
Exits: []inkwell.Exit{ Exits: []inkwell.Exit{
exitToSelector, exitToSelector,
}, },
@@ -1,14 +1,15 @@
package inc package scene
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
SceneManager.Register(Scene{ Manager.Register(Scene{
Name: SceneIceCreamShop, Name: inc.SceneIceCreamShop,
Title: "ICE CREAM SHOP — WHERE THE BAR WAS", Title: "ICE CREAM SHOP — WHERE THE BAR WAS",
Background: BgIceCreamShop, Background: inc.BgIceCreamShop,
Exits: []inkwell.Exit{ Exits: []inkwell.Exit{
exitToSelector, exitToSelector,
}, },
@@ -1,12 +1,15 @@
package inc package scene
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc/world"
) )
type Scene = inkwell.Scene type Scene = inkwell.Scene
var sceneFloor = []inkwell.Polygon{ var Manager = inkwell.NewManager[Scene]()
var Floor = []inkwell.Polygon{
inkwell.Poly( inkwell.Poly(
inkwell.Point{ inkwell.Point{
X: 16, X: 16,
@@ -26,7 +29,7 @@ var sceneFloor = []inkwell.Polygon{
} }
func setVarIfEmpty(name string, v any) inkwell.Action { func setVarIfEmpty(name string, v any) inkwell.Action {
return Fn(func(ctx *inkwell.Ctx) { return world.Fn(func(ctx *inkwell.Ctx) {
if ctx.Game.State.Var(name) == nil { if ctx.Game.State.Var(name) == nil {
ctx.Game.State.SetVar(name, v) ctx.Game.State.SetVar(name, v)
} }
@@ -1,18 +1,19 @@
package inc package scene
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
SceneManager.Register(Scene{ Manager.Register(Scene{
Name: SceneNoodleHouse, Name: inc.SceneNoodleHouse,
Title: "NOODLE'S HOUSE — SEALED", Title: "NOODLE'S HOUSE — SEALED",
Background: BgNoodleHouse, Background: inc.BgNoodleHouse,
Exits: []inkwell.Exit{ Exits: []inkwell.Exit{
exitToSelector, exitToSelector,
{ {
To: SceneAlley, To: inc.SceneAlley,
Label: "the alley behind the house", Label: "the alley behind the house",
Side: inkwell.ExitRight, Side: inkwell.ExitRight,
}, },
@@ -1,23 +1,24 @@
package inc package scene
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
SceneManager.Register(Scene{ Manager.Register(Scene{
Name: SceneNormanApartment, Name: inc.SceneNormanApartment,
Title: "NORMAN'S APARTMENT", Title: "NORMAN'S APARTMENT",
Background: BgNormanApartment, Background: inc.BgNormanApartment,
Exits: []inkwell.Exit{ Exits: []inkwell.Exit{
exitToSelector, exitToSelector,
{ {
To: SceneRooftopHideout, To: inc.SceneRooftopHideout,
Label: "the stairs up to the roof", Label: "the stairs up to the roof",
Side: inkwell.ExitBack, Side: inkwell.ExitBack,
}, },
{ {
To: SceneBBSTerminal, To: inc.SceneBBSTerminal,
Label: "Norman's terminal", Label: "Norman's terminal",
Side: inkwell.ExitRight, Side: inkwell.ExitRight,
}, },
@@ -1,17 +1,18 @@
package inc package scene
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
SceneManager.Register(Scene{ Manager.Register(Scene{
Name: ScenePaulShop, Name: inc.ScenePaulShop,
Title: "PAUL'S SHOP — JUNK AND GARAGE", Title: "PAUL'S SHOP — JUNK AND GARAGE",
Background: BgPaulShop, Background: inc.BgPaulShop,
Exits: []inkwell.Exit{ Exits: []inkwell.Exit{
{ {
To: SceneNoodleHouse, To: inc.SceneNoodleHouse,
Label: "the bus to San Francisco", Label: "the bus to San Francisco",
Side: inkwell.ExitNear, Side: inkwell.ExitNear,
}, },
@@ -1,14 +1,15 @@
package inc package scene
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
SceneManager.Register(Scene{ Manager.Register(Scene{
Name: ScenePoliceStation, Name: inc.ScenePoliceStation,
Title: "SFPD — STATION AND HOLDING", Title: "SFPD — STATION AND HOLDING",
Background: BgPoliceStation, Background: inc.BgPoliceStation,
Exits: []inkwell.Exit{ Exits: []inkwell.Exit{
exitToSelector, exitToSelector,
}, },
@@ -1,14 +1,15 @@
package inc package scene
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
SceneManager.Register(Scene{ Manager.Register(Scene{
Name: ScenePublicBBS, Name: inc.ScenePublicBBS,
Title: "PUBLIC BBS TERMINAL", Title: "PUBLIC BBS TERMINAL",
Background: BgPublicBBS, Background: inc.BgPublicBBS,
Exits: []inkwell.Exit{ Exits: []inkwell.Exit{
exitToSelector, exitToSelector,
}, },
@@ -1,22 +1,23 @@
package inc package scene
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
SceneManager.Register(Scene{ Manager.Register(Scene{
Name: SceneRooftopHideout, Name: inc.SceneRooftopHideout,
Title: "NORMAN'S ROOFTOP HIDEOUT", Title: "NORMAN'S ROOFTOP HIDEOUT",
Background: BgRooftopHideout, Background: inc.BgRooftopHideout,
Exits: []inkwell.Exit{ Exits: []inkwell.Exit{
{ {
To: SceneNormanApartment, To: inc.SceneNormanApartment,
Label: "back down into the flat", Label: "back down into the flat",
Side: inkwell.ExitNear, Side: inkwell.ExitNear,
}, },
{ {
To: SceneBBSTerminal, To: inc.SceneBBSTerminal,
Label: "the old terminal", Label: "the old terminal",
Side: inkwell.ExitRight, Side: inkwell.ExitRight,
}, },
@@ -1,14 +1,15 @@
package inc package scene
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
SceneManager.Register(Scene{ Manager.Register(Scene{
Name: SceneSamizdatPress, Name: inc.SceneSamizdatPress,
Title: "SAMIZDAT PRINTING HOUSE", Title: "SAMIZDAT PRINTING HOUSE",
Background: BgSamizdatPress, Background: inc.BgSamizdatPress,
Exits: []inkwell.Exit{ Exits: []inkwell.Exit{
exitToSelector, exitToSelector,
}, },
@@ -1,14 +1,15 @@
package inc package scene
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
SceneManager.Register(Scene{ Manager.Register(Scene{
Name: SceneScrapMarket, Name: inc.SceneScrapMarket,
Title: "SCRAP MARKET", Title: "SCRAP MARKET",
Background: BgScrapMarket, Background: inc.BgScrapMarket,
Exits: []inkwell.Exit{ Exits: []inkwell.Exit{
exitToSelector, exitToSelector,
}, },
@@ -1,22 +1,23 @@
package inc package scene
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
SceneManager.Register(Scene{ Manager.Register(Scene{
Name: SceneSecretClub, Name: inc.SceneSecretClub,
Title: "SECRET CLUB — THE UNDERWATER SUN", Title: "SECRET CLUB — THE UNDERWATER SUN",
Background: BgSecretClub, Background: inc.BgSecretClub,
Exits: []inkwell.Exit{ Exits: []inkwell.Exit{
{ {
To: SceneBBSTerminal, To: inc.SceneBBSTerminal,
Label: "the terminal in the corner", Label: "the terminal in the corner",
Side: inkwell.ExitBack, Side: inkwell.ExitBack,
}, },
{ {
To: SceneTrinketShop, To: inc.SceneTrinketShop,
Label: "back out through the shop", Label: "back out through the shop",
Side: inkwell.ExitNear, Side: inkwell.ExitNear,
}, },
@@ -1,14 +1,15 @@
package inc package scene
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
SceneManager.Register(Scene{ Manager.Register(Scene{
Name: SceneSecretLab, Name: inc.SceneSecretLab,
Title: "SECRET RESEARCH LABORATORY", Title: "SECRET RESEARCH LABORATORY",
Background: BgSecretLab, Background: inc.BgSecretLab,
Exits: []inkwell.Exit{ Exits: []inkwell.Exit{
exitToSelector, exitToSelector,
}, },
@@ -1,34 +1,35 @@
package inc package scene
import ( import (
"strings" "strings"
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
var exitToSelector = inkwell.Exit{ var exitToSelector = inkwell.Exit{
To: SceneSelector, To: inc.SceneSelector,
Label: "the rest of the city", Label: "the rest of the city",
Side: inkwell.ExitNear, Side: inkwell.ExitNear,
} }
func init() { func init() {
SceneManager.Register(Scene{ Manager.Register(Scene{
Name: SceneSelector, Name: inc.SceneSelector,
Title: "SAN FRANCISCO", Title: "SAN FRANCISCO",
Background: BgSelector, Background: inc.BgSelector,
Actors: []inkwell.SceneActor{}, Actors: []inkwell.SceneActor{},
Walkboxes: []inkwell.Polygon{}, Walkboxes: []inkwell.Polygon{},
}) })
} }
func fillSelectorPins() { func FillSelectorPins() {
selector, ok := SceneManager.Get(SceneSelector) selector, ok := Manager.Get(inc.SceneSelector)
if !ok { if !ok {
return return
} }
var exits []inkwell.Exit var exits []inkwell.Exit
for _, entity := range SceneManager.All() { for _, entity := range Manager.All() {
if !leadsToSelector(entity) { if !leadsToSelector(entity) {
continue continue
} }
@@ -39,12 +40,12 @@ func fillSelectorPins() {
}) })
} }
selector.Exits = exits selector.Exits = exits
SceneManager.Set(selector) Manager.Set(selector)
} }
func leadsToSelector(s Scene) bool { func leadsToSelector(s Scene) bool {
for _, e := range s.Exits { for _, e := range s.Exits {
if e.To == SceneSelector { if e.To == inc.SceneSelector {
return true return true
} }
} }
@@ -1,14 +1,15 @@
package inc package scene
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
SceneManager.Register(Scene{ Manager.Register(Scene{
Name: SceneServerFarm, Name: inc.SceneServerFarm,
Title: "SERVER FARM", Title: "SERVER FARM",
Background: BgServerFarm, Background: inc.BgServerFarm,
Exits: []inkwell.Exit{ Exits: []inkwell.Exit{
exitToSelector, exitToSelector,
}, },
@@ -1,14 +1,15 @@
package inc package scene
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
SceneManager.Register(Scene{ Manager.Register(Scene{
Name: SceneShowroom, Name: inc.SceneShowroom,
Title: "NEUMATRONIC SHOWROOM", Title: "NEUMATRONIC SHOWROOM",
Background: BgShowroom, Background: inc.BgShowroom,
Exits: []inkwell.Exit{ Exits: []inkwell.Exit{
exitToSelector, exitToSelector,
}, },
@@ -1,17 +1,18 @@
package inc package scene
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
SceneManager.Register(Scene{ Manager.Register(Scene{
Name: SceneSmallRestaurant, Name: inc.SceneSmallRestaurant,
Title: "SMALL RESTAURANT — NEXT DOOR", Title: "SMALL RESTAURANT — NEXT DOOR",
Background: BgSmallRestaurant, Background: inc.BgSmallRestaurant,
Exits: []inkwell.Exit{ Exits: []inkwell.Exit{
{ {
To: SceneTrinketShop, To: inc.SceneTrinketShop,
Label: "back to the trinket shop", Label: "back to the trinket shop",
Side: inkwell.ExitLeft, Side: inkwell.ExitLeft,
}, },
@@ -1,14 +1,15 @@
package inc package scene
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
) )
func init() { func init() {
SceneManager.Register(Scene{ Manager.Register(Scene{
Name: SceneStreet, Name: inc.SceneStreet,
Title: "STREET", Title: "STREET",
Background: BgStreet, Background: inc.BgStreet,
Exits: []inkwell.Exit{ Exits: []inkwell.Exit{
exitToSelector, exitToSelector,
}, },
+29
View File
@@ -0,0 +1,29 @@
package scene
import (
inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
)
func init() {
Manager.Register(Scene{
Name: inc.SceneTrinketShop,
Title: "CHINATOWN — TRINKET SHOP",
Background: inc.BgTrinketShop,
Exits: []inkwell.Exit{
exitToSelector,
{
To: inc.SceneSmallRestaurant,
Label: "the eating place next door",
Side: inkwell.ExitRight,
},
{
To: inc.SceneSecretClub,
Label: "the way in at the back",
Side: inkwell.ExitBack,
Needs: inc.FlagClubEntry,
Blocked: inkwell.Say(inc.Paul, "Just a shop, as far as the man behind the counter is concerned."),
},
},
})
}
-23
View File
@@ -1,23 +0,0 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
ScriptManager.Register(Script{
Name: ScriptFinale,
Actions: inkwell.Seq(
SetMode(ModeCutscene),
inkwell.Wait(0.6),
inkwell.Say(Paul, "I'm putting it in. That's all this is."),
inkwell.Wait(0.4),
UseTheme(NokiaPunk),
inkwell.Say("norman", "No — this isn't what you were supposed to do!"),
inkwell.Wait(0.6),
inkwell.Say(TapeMystery, "Thank you, Paul. You did exactly what I asked, the whole way through."),
inkwell.Wait(1.0),
inkwell.ShowEnd("REAL WORLD — end of game two"),
),
})
}
-16
View File
@@ -1,16 +0,0 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
ScriptManager.Register(Script{
Name: ScriptOpening,
Actions: inkwell.Seq(
inkwell.SetFlag(FlagPoliceTip),
inkwell.Say(Paul, "Back alley. Just like the clerk said."),
inkwell.Say(Dex, "A government office tipped you off to remove something from a scene. That doesn't strike you as odd?"),
),
})
}
-25
View File
@@ -1,25 +0,0 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
ScriptManager.Register(Script{
Name: ScriptTapeInsert,
Actions: inkwell.Seq(
Fn(func(ctx *inkwell.Ctx) {
item := World.TakePending()
if item == "" {
return
}
World.SetSlot2(item)
ctx.Game.Inventory.Remove(item)
ctx.Game.State.SetVar(VarArmillaStrip, "SLOT2: READING")
}),
inkwell.Say(Paul, "Right. Let's see who you are."),
inkwell.Say(Dex, "Clicked in. Spinning. And now: nothing."),
inkwell.Say(Dex, "A stranger's tape in your own Armilla, Paul. I hope you know what you're doing."),
),
})
}
+25
View File
@@ -0,0 +1,25 @@
package script
import (
inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
"git.teletypegames.org/games/realworld/inc/world"
)
func init() {
Manager.Register(Script{
Name: inc.ScriptFinale,
Actions: inkwell.Seq(
world.SetMode(world.ModeCutscene),
inkwell.Wait(0.6),
inkwell.Say(inc.Paul, "I'm putting it in. That's all this is."),
inkwell.Wait(0.4),
world.UseTheme(inc.NokiaPunk),
inkwell.Say("norman", "No — this isn't what you were supposed to do!"),
inkwell.Wait(0.6),
inkwell.Say(inc.TapeMystery, "Thank you, Paul. You did exactly what I asked, the whole way through."),
inkwell.Wait(1.0),
inkwell.ShowEnd("REAL WORLD — end of game two"),
),
})
}
@@ -1,7 +1,9 @@
package inc package script
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
) )
type Script = inkwell.Script type Script = inkwell.Script
var Manager = inkwell.NewManager[Script]()
+17
View File
@@ -0,0 +1,17 @@
package script
import (
inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
)
func init() {
Manager.Register(Script{
Name: inc.ScriptOpening,
Actions: inkwell.Seq(
inkwell.SetFlag(inc.FlagPoliceTip),
inkwell.Say(inc.Paul, "Back alley. Just like the clerk said."),
inkwell.Say(inc.Dex, "A government office tipped you off to remove something from a scene. That doesn't strike you as odd?"),
),
})
}
+27
View File
@@ -0,0 +1,27 @@
package script
import (
inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
"git.teletypegames.org/games/realworld/inc/world"
)
func init() {
Manager.Register(Script{
Name: inc.ScriptTapeInsert,
Actions: inkwell.Seq(
world.Fn(func(ctx *inkwell.Ctx) {
item := world.TakePending()
if item == "" {
return
}
world.SetSlot2(item)
ctx.Game.Inventory.Remove(item)
ctx.Game.State.SetVar(inc.VarArmillaStrip, "SLOT2: READING")
}),
inkwell.Say(inc.Paul, "Right. Let's see who you are."),
inkwell.Say(inc.Dex, "Clicked in. Spinning. And now: nothing."),
inkwell.Say(inc.Dex, "A stranger's tape in your own Armilla, Paul. I hope you know what you're doing."),
),
})
}
@@ -1,19 +1,21 @@
package inc package script
import ( import (
"math/rand" "math/rand"
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
"git.teletypegames.org/games/realworld/inc/world"
) )
func useWithFail(item string, h *inkwell.Hotspot) inkwell.Action { func UseWithFail(item string, h *inkwell.Hotspot) inkwell.Action {
key := "fail." + item + "." + h.Name key := "fail." + item + "." + h.Name
World.G.State.NoteTalked(key) world.Game().State.NoteTalked(key)
n := World.G.State.Talked(key) n := world.Game().State.Talked(key)
return inkwell.Seq( return inkwell.Seq(
inkwell.Say(Paul, pick(paulFails, n)), inkwell.Say(inc.Paul, pick(paulFails, n)),
inkwell.Say(Dex, dexFail(n)), inkwell.Say(inc.Dex, dexFail(n)),
) )
} }
-8
View File
@@ -1,8 +0,0 @@
package inc
func init() {
TapeManager.Register(Tape{
Name: Dex,
Dialogue: DlgDex,
})
}
-32
View File
@@ -1,32 +0,0 @@
package inc
type Tape struct {
Name string
Item string
Dialogue string
}
func (t Tape) GetName() string { return t.Name }
func IsTape(name string) bool { return TapeManager.Has(name) }
func TapeOf(item string) (Tape, bool) {
for _, t := range TapeManager.All() {
if t.Item != "" && t.Item == item {
return t, true
}
}
return Tape{}, false
}
func IsTapeItem(item string) bool {
_, ok := TapeOf(item)
return ok
}
func TapeDialogue(item string) string {
if t, ok := TapeOf(item); ok && t.Dialogue != "" {
return t.Dialogue
}
return DlgDex
}
-9
View File
@@ -1,9 +0,0 @@
package inc
func init() {
TapeManager.Register(Tape{
Name: TapeMystery,
Item: ItemMysteryTape,
Dialogue: DlgMysteryTape,
})
}
-7
View File
@@ -1,7 +0,0 @@
package inc
func init() {
TapeManager.Register(Tape{
Name: TapeSupport,
})
}
+12
View File
@@ -0,0 +1,12 @@
package tape
import (
"git.teletypegames.org/games/realworld/inc"
)
func init() {
Manager.Register(Tape{
Name: inc.Dex,
Dialogue: inc.DlgDex,
})
}
+39
View File
@@ -0,0 +1,39 @@
package tape
import (
inkwell "git.teletypegames.org/engines/inkwell"
"git.teletypegames.org/games/realworld/inc"
)
type Tape struct {
Name string
Item string
Dialogue string
}
var Manager = inkwell.NewManager[Tape]()
func (t Tape) GetName() string { return t.Name }
func Is(name string) bool { return Manager.Has(name) }
func Of(item string) (Tape, bool) {
for _, t := range Manager.All() {
if t.Item != "" && t.Item == item {
return t, true
}
}
return Tape{}, false
}
func IsItem(item string) bool {
_, ok := Of(item)
return ok
}
func Dialogue(item string) string {
if t, ok := Of(item); ok && t.Dialogue != "" {
return t.Dialogue
}
return inc.DlgDex
}
+13
View File
@@ -0,0 +1,13 @@
package tape
import (
"git.teletypegames.org/games/realworld/inc"
)
func init() {
Manager.Register(Tape{
Name: inc.TapeMystery,
Item: inc.ItemMysteryTape,
Dialogue: inc.DlgMysteryTape,
})
}
+11
View File
@@ -0,0 +1,11 @@
package tape
import (
"git.teletypegames.org/games/realworld/inc"
)
func init() {
Manager.Register(Tape{
Name: inc.TapeSupport,
})
}
@@ -1,4 +1,4 @@
package inc package theme
import ( import (
"image/color" "image/color"
@@ -8,10 +8,7 @@ import (
type Theme = inkwell.Theme type Theme = inkwell.Theme
const ( var Manager = inkwell.NewManager[Theme]()
RealWorld = "realworld-93"
NokiaPunk = "nokia-punk"
)
func RGB(hex uint32) color.Color { func RGB(hex uint32) color.Color {
return color.RGBA{ return color.RGBA{
@@ -1,4 +1,8 @@
package inc package theme
import (
"git.teletypegames.org/games/realworld/inc"
)
var ( var (
Green = RGB(0x3BE86B) Green = RGB(0x3BE86B)
@@ -7,8 +11,8 @@ var (
) )
func init() { func init() {
ThemeManager.Register(Theme{ Manager.Register(Theme{
Name: NokiaPunk, Name: inc.NokiaPunk,
PanelBG: npBlack, PanelBG: npBlack,
StatusText: Green, StatusText: Green,
FlashText: RGB(0xD8F06A), FlashText: RGB(0xD8F06A),
@@ -1,4 +1,8 @@
package inc package theme
import (
"git.teletypegames.org/games/realworld/inc"
)
var ( var (
Ink = RGB(0xDCD5C4) Ink = RGB(0xDCD5C4)
@@ -11,8 +15,8 @@ var (
) )
func init() { func init() {
ThemeManager.Register(Theme{ Manager.Register(Theme{
Name: RealWorld, Name: inc.RealWorld,
PanelBG: black, PanelBG: black,
StatusText: Ink, StatusText: Ink,
FlashText: Amber, FlashText: Amber,
-15
View File
@@ -1,15 +0,0 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func init() {
WidgetManager.Register(&inkwell.TopBar{
Name: "topbar",
When: whenPlaying,
Height: TopBarH,
TimeVar: VarArmillaStrip,
NoteVar: VarNote,
})
}
@@ -1,11 +1,11 @@
package inc package widget
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
) )
func init() { func init() {
WidgetManager.Register(&inkwell.Cursor{ Manager.Register(&inkwell.Cursor{
Name: "cursor", Name: "cursor",
}) })
} }
@@ -1,11 +1,11 @@
package inc package widget
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
) )
func init() { func init() {
WidgetManager.Register(&inkwell.DialogBox{ Manager.Register(&inkwell.DialogBox{
Name: "dialog", Name: "dialog",
Bounds: inkwell.Rect(0, ScreenH-LineH*9, DividerX, LineH*9), Bounds: inkwell.Rect(0, ScreenH-LineH*9, DividerX, LineH*9),
LineHeight: LineH, LineHeight: LineH,
@@ -1,11 +1,11 @@
package inc package widget
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
) )
func init() { func init() {
WidgetManager.Register(&inkwell.EndCard{ Manager.Register(&inkwell.EndCard{
Name: "endcard", Name: "endcard",
}) })
} }
@@ -1,11 +1,11 @@
package inc package widget
import ( import (
inkwell "git.teletypegames.org/engines/inkwell" inkwell "git.teletypegames.org/engines/inkwell"
) )
func init() { func init() {
WidgetManager.Register(&inkwell.HotspotDebug{ Manager.Register(&inkwell.HotspotDebug{
Name: "hotspot_debug", Name: "hotspot_debug",
}) })
} }

Some files were not shown because too many files have changed in this diff Show More