register logic

This commit is contained in:
2026-08-29 23:59:56 +02:00
parent ddea3b0893
commit 94649a38fe
83 changed files with 830 additions and 699 deletions
+126 -19
View File
@@ -22,8 +22,8 @@ one-to-one.
### No tests
Do not write tests, and do not add test cases to the ones that already exist.
Correctness is checked by running the game.
Do not write tests and do not add test files. Correctness is checked by running
the game.
### Struct literals are always multi-line
@@ -52,15 +52,19 @@ file's name carries the structure, in the form `[category].[name].go`.
`character`, `dialog`, `script`, `world`, `ui`, `theme`, `names`, `boot`,
`content`.
- `[category].manager.go` is the file that ties a category together — its
`register…` function, its shared types, its list of entities.
manager, its entity type, whatever the category shares.
- Every other file in a category holds exactly one entity: one screen, one
background, one item.
background, one item. The file registers it itself, in an `init()`.
```
main.go flags + inkwell.Run
inc/manager.manager.go Manager[T]: the generic registry every category uses
inc/manager.interface.go ManagerInterface and the compile-time assertions
inc/boot.manager.go wiring; New(Opts) builds the game
inc/names.manager.go entity names and world-state keys
inc/theme.manager.go realworld-93 + nokia-punk
inc/theme.manager.go the theme manager and the colour helpers
inc/theme.realworld.go realworld-93
inc/theme.nokia_punk.go nokia-punk
inc/world.manager.go unsaved runtime state
inc/world.action.go custom actions and the action pump
inc/ui.manager.go HUD layout and widget registration
@@ -71,25 +75,130 @@ 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 deck, the scene builder
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
```
`manager` is the one category with no entities of its own: it holds the registry
every other category is built from.
## Managers
Every category that owns a collection of entities has a manager, and they are
all the same generic type — `Manager[T]` in `manager.manager.go`. It keeps one
slice in registration order and one `map[string]int` beside it, so `GetByName`
is a map lookup, not a scan.
Since the entity types are aliases of engine structs, no method can be attached
to them; the manager is told how to read a name instead, which is all it needs:
```go
type Character = inkwell.Character
var CharacterManager = NewManager(func(entity Character) string { return entity.Name })
```
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`.
`manager.interface.go` holds the contract they all keep, and asserts each one
against it. A new manager goes on that list.
```go
type ManagerInterface[T any] interface {
Register(entity T)
GetByName(name string) (T, bool)
GetAll() []T
}
```
`Register` replaces by name and keeps the entity's position, so registering
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.
### Entities register themselves
An entity file is a literal and nothing else. No constructor function, no list
somewhere else to keep in step — the file hands itself to its manager in an
`init()`:
```go
package inc
func init() {
BackgroundManager.Register(Background{
Name: BgServerFarm,
Path: "assets/bg/server_farm.png",
Kind: inkwell.AssetImage,
})
}
```
Adding an entity is adding a file. Deleting one is deleting a file. Package-level
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,
which is simply the order the files sit in.
### Handing a category to the engine
Once the game exists, `registerAll` walks a manager and gives every entity to
the engine's own manager. `registerContent` is the whole of it:
```go
func registerContent() {
registerAll(BackgroundManager, World.G.AssetManager.Register)
registerAll(CharacterManager, World.G.CharacterManager.Register)
registerAll(ItemManager, World.G.ItemManager.Register)
registerAll(DialogManager, World.G.DialogueManager.Register)
registerAll(ScriptManager, World.G.ScriptManager.Register)
registerScreen()
}
```
`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`.
## The world
There is exactly one game, so there is exactly one world: `World`, a
package-level singleton in `world.manager.go`. Nothing takes a `*world`
parameter and no widget holds a back-reference — `World.Do(…)`,
`World.HUDVisible()`, `World.Slot2()` are reachable from anywhere in the
package. `New` calls `World.attach(g)`, which binds the engine and resets the
runtime state, so building the game twice is clean.
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.
## Naming
One package means one namespace, so an entity's constructor carries its
category as a prefix:
```go
screenAlley() backgroundAlley()
itemMysteryTape() characterMysteryTape()
dialogDexTalk() scriptTapeInsert()
screenFloor screenBuild registerScreen fillSelectorPins
```
Category-level functions follow the same shape: `registerScreen`,
`registerBackground`, `registerItem`, and so on. Only `New` and `Opts` are
exported — they are what `main.go` needs, and nothing else leaves the package.
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.
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
constants in `names.manager.go`, the colour tokens, and the action constructors
(`TapeSay`, `Paused`, `SetMode`, `EnterScene`, `Back`, `Fn`).
Everything else is machinery and stays unexported: the HUD widgets
(`tapeSlots`, `letterbox`, `hudFrame`, …), the `register…` functions, the
runners, `screenBuild`, `exit`.
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"
@@ -112,14 +221,12 @@ other way round.
## Adding a screen
1. `inc/screen.<name>.go` `screen<Name>() screen`
2. `inc/background.<name>.go``background<Name>() inkwell.Asset`
3. One line in `screenDeck` (`inc/screen.manager.go`)
4. One line in `registerBackground` (`inc/background.manager.go`)
5. Constants in `inc/names.manager.go`: `Screen<Name>`, `Bg<Name>`
6. A 640×380 PNG in `assets/bg/`
1. Constants in `inc/names.manager.go`: `Screen<Name>`, `Bg<Name>`
2. `inc/screen.<name>.go`an `init()` registering a `Screen`
3. `inc/background.<name>.go` — an `init()` registering a `Background`
4. A 640×380 PNG in `assets/bg/`
Nothing else moves.
Nothing else moves. There is no list to update.
## Commands
+74 -25
View File
@@ -48,7 +48,6 @@ go mod edit -dropreplace git.teletypegames.org/engines/inkwell
```bash
make build # native binary into bin/
make test # headless: validates content and HUD layout
make wasm # dist/game.wasm + wasm_exec.js
make export VERSION=0.1 # zipped HTML/WASM bundle
make binaries VERSION=0.1 # win-x86, win-x64, linux-x64 zips
@@ -63,13 +62,13 @@ Release metadata lives in `metadata.json`.
|---|---|
| left click | run the selected verb |
| right click | verb coin (Look / Use / Talk / Take) |
| `←` `→` | previous / next screen, in concept-art order, wrapping |
| `←` `→` | previous / next screen, 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 the deck in concept-art order. The
walk is inert during a cutscene, a menu, or a stopped world, so it can never cut
are a reviewing tool on top of that, walking every screen in turn. The walk is
inert during a cutscene, a menu, or a stopped world, so it can never cut
across an authored beat.
Screenshots: `EBITEN_SCREENSHOT_KEY=q go run .`, then press `q` in the window.
@@ -154,20 +153,22 @@ licence.
The game is one flat package, `inc`. There are no subdirectories: a file's
name says where it belongs, in the form `[category].[name].go`. The category is
always singular, and `[category].manager.go` is the file that ties that category
together — its `register…`, its shared types, its list.
together — its manager, its shared types, its hand-off to the engine.
```
main.go flags + inkwell.Run
inc/names.manager.go entity names and world-state keys
inc/theme.manager.go realworld-93 + nokia-punk
inc/world.*.go unsaved runtime state, custom actions, action pump
inc/ui.*.go HUD: layout, custom widgets, coloured text
inc/<kind>.<name>.go one file per registered entity, by kind
inc/boot.manager.go wiring
main.go flags + inkwell.Run
inc/manager.manager.go Manager[T], the generic registry
inc/manager.interface.go ManagerInterface, the contract every manager keeps
inc/names.manager.go entity names and world-state keys
inc/theme.*.go realworld-93 + nokia-punk
inc/world.*.go unsaved runtime state, custom actions, action pump
inc/ui.*.go HUD: layout, custom widgets, coloured text
inc/<kind>.<name>.go one file per registered entity, by kind
inc/boot.manager.go wiring
```
Nothing is enforced by the compiler any more, so the layering is a rule the
code keeps by hand: the world knows nothing about the HUD or the content, and
code keeps by hand: the world holds no opinion about the HUD or the content, and
both build on it, never the other way round.
```
@@ -188,12 +189,60 @@ script.*.go script.tape_insert.go script.awakening_finale.go
screen.*.go one file per screen: screen.alley.go, … + screen.exit.go
```
Adding a screen means adding `screen.<name>.go` and `background.<name>.go`, and
one line in each category's manager list. Nothing else moves.
Adding a screen means adding `screen.<name>.go` and `background.<name>.go`.
Nothing else moves.
Since everything shares one namespace, an entity's constructor carries its
category: `screenAlley()` is the screen, `backgroundAlley()` the painting behind
it, `itemMysteryTape()` the prop and `characterMysteryTape()` the voice on it.
Every category owns a manager, and they are all the same generic type,
`Manager[T]` — one slice in registration order, one `map[string]int` beside it,
so a lookup by name is a map hit rather than a scan. The entity types are
aliases of engine structs and cannot carry methods, so the manager is handed a
function that reads the name instead. A category's manager file is an alias and
one line:
```go
type Character = inkwell.Character
var CharacterManager = NewManager(func(entity Character) string { return entity.Name })
```
They all keep the same contract, asserted in `manager.interface.go`:
```go
type ManagerInterface[T any] interface {
Register(entity T)
GetByName(name string) (T, bool)
GetAll() []T
}
```
An entity file is a literal that hands itself over in an `init()`:
```go
func init() {
BackgroundManager.Register(Background{
Name: BgServerFarm,
Path: "assets/bg/server_farm.png",
Kind: inkwell.AssetImage,
})
}
```
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.
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
over. `registerContent` is where the hand-off happens, one `registerAll` call
per category.
There is one world, and it is a package-level singleton: `World`. Nothing takes
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
@@ -231,9 +280,8 @@ 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`.
Three tests keep the map honest: every exit names a registered screen, every
screen has a way out, and every screen can be walked to from Paul's shop through
the exits alone — the arrow keys do not count.
The graph stays machine-readable: exit hotspots are named `exit:<target>`, so
the connections can be read straight back out of the registered screens.
## Engine workarounds
@@ -297,9 +345,10 @@ Also: the inkwell README gives the module path as
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
`DialogBox`.
- **Only beat 3 exists.** The alley is a vertical slice; `boot.opening` sets the
- **Only beat 3 exists.** The alley is a vertical slice; `bootOpening` sets the
police-tip flag that beat 2 will eventually set.
- **The render has only been inspected once, by the author of this repo.**
Geometry is covered by tests and a layout dump, but this environment cannot
take a screenshot (both synthetic keystrokes and screen capture are blocked by
macOS privacy permissions), so every visual judgement has to come from you.
Geometry is derived from one font cell and one screen size, but this
environment cannot take a screenshot (both synthetic keystrokes and screen
capture are blocked by macOS privacy permissions), so every visual judgement
has to come from you.
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundAlley() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgAlley,
Path: "assets/bg/alley.png",
Kind: inkwell.AssetImage,
}
})
}
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundBbsTerminal() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgBBSTerminal,
Path: "assets/bg/bbs_terminal.png",
Kind: inkwell.AssetImage,
}
})
}
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundBvkBranch() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgBVKBranch,
Path: "assets/bg/bvk_branch.png",
Kind: inkwell.AssetImage,
}
})
}
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundColumbarium() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgColumbarium,
Path: "assets/bg/columbarium.png",
Kind: inkwell.AssetImage,
}
})
}
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundCuratorShop() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgCuratorShop,
Path: "assets/bg/curator_shop.png",
Kind: inkwell.AssetImage,
}
})
}
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundHackerspace() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgHackerspace,
Path: "assets/bg/hackerspace.png",
Kind: inkwell.AssetImage,
}
})
}
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundHospital() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgHospital,
Path: "assets/bg/hospital.png",
Kind: inkwell.AssetImage,
}
})
}
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundIceCreamShop() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgIceCreamShop,
Path: "assets/bg/ice_cream_shop.png",
Kind: inkwell.AssetImage,
}
})
}
+3 -30
View File
@@ -4,33 +4,6 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func registerBackground(w *World) {
for _, a := range []inkwell.Asset{
backgroundSelector(),
backgroundPaulShop(),
backgroundNoodleHouse(),
backgroundNormanApartment(),
backgroundPoliceStation(),
backgroundAlley(),
backgroundHackerspace(),
backgroundIceCreamShop(),
backgroundTrinketShop(),
backgroundSmallRestaurant(),
backgroundSecretClub(),
backgroundBbsTerminal(),
backgroundStreet(),
backgroundHospital(),
backgroundServerFarm(),
backgroundSecretLab(),
backgroundRooftopHideout(),
backgroundPublicBBS(),
backgroundBvkBranch(),
backgroundCuratorShop(),
backgroundScrapMarket(),
backgroundSamizdatPress(),
backgroundShowroom(),
backgroundColumbarium(),
} {
w.G.AssetManager.Register(a)
}
}
type Background = inkwell.Asset
var BackgroundManager = NewManager(func(entity Background) string { return entity.Name })
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundNoodleHouse() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgNoodleHouse,
Path: "assets/bg/noodle_house.png",
Kind: inkwell.AssetImage,
}
})
}
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundNormanApartment() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgNormanApartment,
Path: "assets/bg/norman_apartment.png",
Kind: inkwell.AssetImage,
}
})
}
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundPaulShop() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgPaulShop,
Path: "assets/bg/paul_shop.png",
Kind: inkwell.AssetImage,
}
})
}
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundPoliceStation() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgPoliceStation,
Path: "assets/bg/police_station.png",
Kind: inkwell.AssetImage,
}
})
}
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundPublicBBS() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgPublicBBS,
Path: "assets/bg/public_bbs.png",
Kind: inkwell.AssetImage,
}
})
}
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundRooftopHideout() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgRooftopHideout,
Path: "assets/bg/rooftop_hideout.png",
Kind: inkwell.AssetImage,
}
})
}
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundSamizdatPress() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgSamizdatPress,
Path: "assets/bg/samizdat_press.png",
Kind: inkwell.AssetImage,
}
})
}
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundScrapMarket() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgScrapMarket,
Path: "assets/bg/scrap_market.png",
Kind: inkwell.AssetImage,
}
})
}
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundSecretClub() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgSecretClub,
Path: "assets/bg/secret_club.png",
Kind: inkwell.AssetImage,
}
})
}
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundSecretLab() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgSecretLab,
Path: "assets/bg/secret_lab.png",
Kind: inkwell.AssetImage,
}
})
}
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundSelector() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgSelector,
Path: "assets/bg/selector.png",
Kind: inkwell.AssetImage,
}
})
}
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundServerFarm() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgServerFarm,
Path: "assets/bg/server_farm.png",
Kind: inkwell.AssetImage,
}
})
}
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundShowroom() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgShowroom,
Path: "assets/bg/showroom.png",
Kind: inkwell.AssetImage,
}
})
}
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundSmallRestaurant() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgSmallRestaurant,
Path: "assets/bg/small_restaurant.png",
Kind: inkwell.AssetImage,
}
})
}
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundStreet() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgStreet,
Path: "assets/bg/street.png",
Kind: inkwell.AssetImage,
}
})
}
+3 -3
View File
@@ -4,10 +4,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func backgroundTrinketShop() inkwell.Asset {
return inkwell.Asset{
func init() {
BackgroundManager.Register(Background{
Name: BgTrinketShop,
Path: "assets/bg/trinket_shop.png",
Kind: inkwell.AssetImage,
}
})
}
+7 -7
View File
@@ -18,21 +18,21 @@ func New(o Opts) *inkwell.Game {
g := inkwell.NewGame("Real World", ScreenW, ScreenH)
g.MaxLogLines = 64
w := newWorld(g)
registerTheme(g)
registerContent(w)
World.attach(g)
registerTheme()
registerContent()
g.UseTheme(RealWorld)
registerUI(w)
registerUI()
g.StartAt(start)
g.OnStart(inkwell.Seq(bootOpening(w, start, o.Finale)...))
g.OnStart(inkwell.Seq(bootOpening(start, o.Finale)...))
return g
}
func bootOpening(w *World, start string, finale bool) []inkwell.Action {
func bootOpening(start string, finale bool) []inkwell.Action {
acts := []inkwell.Action{
EnterScene(w, start),
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?"),
+3 -7
View File
@@ -1,12 +1,8 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func characterDex() inkwell.Character {
return inkwell.Character{
func init() {
CharacterManager.Register(Character{
Name: Dex,
SpeechColor: Amber,
}
})
}
+3 -10
View File
@@ -4,13 +4,6 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func registerCharacter(w *World) {
for _, c := range []inkwell.Character{
characterPaul(),
characterDex(),
characterMysteryTape(),
characterSupportTape(),
} {
w.G.CharacterManager.Register(c)
}
}
type Character = inkwell.Character
var CharacterManager = NewManager(func(entity Character) string { return entity.Name })
+3 -7
View File
@@ -1,12 +1,8 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func characterMysteryTape() inkwell.Character {
return inkwell.Character{
func init() {
CharacterManager.Register(Character{
Name: TapeMystery,
SpeechColor: RGB(0xB9A98A),
}
})
}
+3 -3
View File
@@ -4,8 +4,8 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func characterPaul() inkwell.Character {
return inkwell.Character{
func init() {
CharacterManager.Register(Character{
Name: Paul,
Speed: 96,
W: 28,
@@ -15,5 +15,5 @@ func characterPaul() inkwell.Character {
Y: 232,
},
SpeechColor: Ink,
}
})
}
+3 -7
View File
@@ -1,12 +1,8 @@
package inc
import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func characterSupportTape() inkwell.Character {
return inkwell.Character{
func init() {
CharacterManager.Register(Character{
Name: TapeSupport,
SpeechColor: RGB(0xE8C86A),
}
})
}
+7 -7
View File
@@ -1,10 +1,10 @@
package inc
func registerContent(w *World) {
registerBackground(w)
registerCharacter(w)
registerItem(w)
registerDialog(w)
registerScript(w)
registerScreen(w)
func registerContent() {
registerAll(BackgroundManager, World.G.AssetManager.Register)
registerAll(CharacterManager, World.G.CharacterManager.Register)
registerAll(ItemManager, World.G.ItemManager.Register)
registerAll(DialogManager, World.G.DialogueManager.Register)
registerAll(ScriptManager, World.G.ScriptManager.Register)
registerScreen()
}
+3 -3
View File
@@ -4,8 +4,8 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func dialogDexTalk() inkwell.Dialogue {
return inkwell.Dialogue{
func init() {
DialogManager.Register(Dialog{
Name: DlgDex,
Start: "root",
Nodes: []inkwell.DialogueNode{{
@@ -52,5 +52,5 @@ func dialogDexTalk() inkwell.Dialogue {
},
},
}},
}
})
}
+3 -8
View File
@@ -4,11 +4,6 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func registerDialog(w *World) {
for _, d := range []inkwell.Dialogue{
dialogDexTalk(),
dialogMysteryTapeSilent(),
} {
w.G.DialogueManager.Register(d)
}
}
type Dialog = inkwell.Dialogue
var DialogManager = NewManager(func(entity Dialog) string { return entity.Name })
+3 -3
View File
@@ -4,8 +4,8 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func dialogMysteryTapeSilent() inkwell.Dialogue {
return inkwell.Dialogue{
func init() {
DialogManager.Register(Dialog{
Name: DlgMysteryTape,
Start: "root",
Nodes: []inkwell.DialogueNode{{
@@ -28,5 +28,5 @@ func dialogMysteryTapeSilent() inkwell.Dialogue {
},
},
}},
}
})
}
+3 -3
View File
@@ -4,8 +4,8 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func itemBlackMarketArmilla() inkwell.Item {
return inkwell.Item{
func init() {
ItemManager.Register(Item{
Name: ItemBlackArmilla,
Description: "black-market Armilla",
OnUseSelf: inkwell.Seq(
@@ -18,5 +18,5 @@ func itemBlackMarketArmilla() inkwell.Item {
TapeSay(Dex, "Nothing. Which is what I'd charge for it."),
),
},
}
})
}
+3 -9
View File
@@ -4,12 +4,6 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func registerItem(w *World) {
for _, i := range []inkwell.Item{
itemNoodleLetter(),
itemBlackMarketArmilla(),
itemMysteryTape(),
} {
w.G.ItemManager.Register(i)
}
}
type Item = inkwell.Item
var ItemManager = NewManager(func(entity Item) string { return entity.Name })
+3 -3
View File
@@ -4,13 +4,13 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func itemMysteryTape() inkwell.Item {
return inkwell.Item{
func init() {
ItemManager.Register(Item{
Name: ItemMysteryTape,
Description: "unmarked Personal Tape",
OnUseSelf: inkwell.Seq(
inkwell.Say(Paul, "Password-locked. Of course."),
TapeSay(Dex, "Put it in the second slot if you care that much. I did warn you."),
),
}
})
}
+3 -3
View File
@@ -4,13 +4,13 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func itemNoodleLetter() inkwell.Item {
return inkwell.Item{
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."),
TapeSay(Dex, "And now you're here. And he isn't."),
),
}
})
}
+17
View File
@@ -0,0 +1,17 @@
package inc
type ManagerInterface[T any] interface {
Register(entity T)
GetByName(name string) (T, bool)
GetAll() []T
}
var (
_ ManagerInterface[Background] = BackgroundManager
_ ManagerInterface[Character] = CharacterManager
_ ManagerInterface[Dialog] = DialogManager
_ ManagerInterface[Item] = ItemManager
_ ManagerInterface[Script] = ScriptManager
_ ManagerInterface[Screen] = ScreenManager
_ ManagerInterface[Theme] = ThemeManager
)
+43
View File
@@ -0,0 +1,43 @@
package inc
type Manager[T any] struct {
nameOf func(T) string
entities []T
index map[string]int
}
func NewManager[T any](nameOf func(T) string) *Manager[T] {
return &Manager[T]{
nameOf: nameOf,
index: map[string]int{},
}
}
func (m *Manager[T]) Register(entity T) {
name := m.nameOf(entity)
if i, ok := m.index[name]; ok {
m.entities[i] = entity
return
}
m.index[name] = len(m.entities)
m.entities = append(m.entities, entity)
}
func (m *Manager[T]) GetByName(name string) (T, bool) {
i, ok := m.index[name]
if !ok {
var missing T
return missing, false
}
return m.entities[i], true
}
func (m *Manager[T]) GetAll() []T {
return m.entities
}
func registerAll[T any](m *Manager[T], register func(T)) {
for _, entity := range m.GetAll() {
register(entity)
}
}
+10 -10
View File
@@ -4,19 +4,19 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func screenAlley() screen {
return screen{
name: ScreenAlley,
title: "ALLEY — BEHIND NOODLE'S HOUSE",
background: BgAlley,
exits: []exit{
func init() {
ScreenManager.Register(Screen{
Name: ScreenAlley,
Title: "ALLEY — BEHIND NOODLE'S HOUSE",
Background: BgAlley,
Exits: []exit{
{
to: ScreenNoodleHouse,
label: "back out to the street",
side: sideLeft,
},
},
actors: []inkwell.SceneActor{
Actors: []inkwell.SceneActor{
{
CharacterName: Paul,
At: inkwell.Point{
@@ -25,9 +25,9 @@ func screenAlley() screen {
},
},
},
onEnter: setVarIfEmpty(VarArmillaStrip, "SRP: 1 tape"),
hotspots: []inkwell.Hotspot{hidingPlace(), backDoor(), bin(), fireEscape()},
}
OnEnter: setVarIfEmpty(VarArmillaStrip, "SRP: 1 tape"),
Hotspots: []inkwell.Hotspot{hidingPlace(), backDoor(), bin(), fireEscape()},
})
}
func hidingPlace() inkwell.Hotspot {
+7 -7
View File
@@ -1,15 +1,15 @@
package inc
func screenBbsTerminal() screen {
return screen{
name: ScreenBBSTerminal,
title: "TERMINAL — BBS ACCESS POINT",
background: BgBBSTerminal,
exits: []exit{
func init() {
ScreenManager.Register(Screen{
Name: ScreenBBSTerminal,
Title: "TERMINAL — BBS ACCESS POINT",
Background: BgBBSTerminal,
Exits: []exit{
{
label: "step back from the terminal",
side: sideNear,
},
},
}
})
}
+7 -7
View File
@@ -1,10 +1,10 @@
package inc
func screenBvkBranch() screen {
return screen{
name: ScreenBVKBranch,
title: "BVK — SAN FRANCISCO BRANCH",
background: BgBVKBranch,
onSelector: true,
}
func init() {
ScreenManager.Register(Screen{
Name: ScreenBVKBranch,
Title: "BVK — SAN FRANCISCO BRANCH",
Background: BgBVKBranch,
OnSelector: true,
})
}
+7 -7
View File
@@ -1,10 +1,10 @@
package inc
func screenColumbarium() screen {
return screen{
name: ScreenColumbarium,
title: "COLUMBARIUM — DEX'S MEMORIAL",
background: BgColumbarium,
onSelector: true,
}
func init() {
ScreenManager.Register(Screen{
Name: ScreenColumbarium,
Title: "COLUMBARIUM — DEX'S MEMORIAL",
Background: BgColumbarium,
OnSelector: true,
})
}
+7 -7
View File
@@ -1,10 +1,10 @@
package inc
func screenCuratorShop() screen {
return screen{
name: ScreenCuratorShop,
title: "CURATOR SERVICE — THE MASTER'S WORKSHOP",
background: BgCuratorShop,
onSelector: true,
}
func init() {
ScreenManager.Register(Screen{
Name: ScreenCuratorShop,
Title: "CURATOR SERVICE — THE MASTER'S WORKSHOP",
Background: BgCuratorShop,
OnSelector: true,
})
}
+2 -2
View File
@@ -52,10 +52,10 @@ func exitName(to string) string {
return "exit:" + to
}
func (e exit) hotspot(w *World) inkwell.Hotspot {
func (e exit) hotspot() inkwell.Hotspot {
var travel inkwell.Action = inkwell.GoTo(e.to)
if e.to == "" {
travel = Back(w)
travel = Back()
}
if e.needs != "" {
travel = inkwell.If(inkwell.Flag(e.needs), travel, inkwell.Say(Paul, e.blocked))
+7 -7
View File
@@ -1,10 +1,10 @@
package inc
func screenHackerspace() screen {
return screen{
name: ScreenHackerspace,
title: "HACKERSPACE",
background: BgHackerspace,
onSelector: true,
}
func init() {
ScreenManager.Register(Screen{
Name: ScreenHackerspace,
Title: "HACKERSPACE",
Background: BgHackerspace,
OnSelector: true,
})
}
+7 -7
View File
@@ -1,10 +1,10 @@
package inc
func screenHospital() screen {
return screen{
name: ScreenHospital,
title: "HOSPITAL — PSYCHIATRIC WING",
background: BgHospital,
onSelector: true,
}
func init() {
ScreenManager.Register(Screen{
Name: ScreenHospital,
Title: "HOSPITAL — PSYCHIATRIC WING",
Background: BgHospital,
OnSelector: true,
})
}
+7 -7
View File
@@ -1,10 +1,10 @@
package inc
func screenIceCreamShop() screen {
return screen{
name: ScreenIceCreamShop,
title: "ICE CREAM SHOP — WHERE THE BAR WAS",
background: BgIceCreamShop,
onSelector: true,
}
func init() {
ScreenManager.Register(Screen{
Name: ScreenIceCreamShop,
Title: "ICE CREAM SHOP — WHERE THE BAR WAS",
Background: BgIceCreamShop,
OnSelector: true,
})
}
+30 -57
View File
@@ -4,55 +4,28 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
type screen struct {
name string
title string
background string
type Screen struct {
Name string
Title string
Background string
exits []exit
Exits []exit
onSelector bool
OnSelector bool
hotspots []inkwell.Hotspot
actors []inkwell.SceneActor
walkboxes []inkwell.Polygon
onEnter inkwell.Action
Hotspots []inkwell.Hotspot
Actors []inkwell.SceneActor
Walkboxes []inkwell.Polygon
OnEnter inkwell.Action
}
var screenDeck = []func() screen{
screenPaulShop,
screenNoodleHouse,
screenAlley,
screenNormanApartment,
screenPoliceStation,
screenHackerspace,
screenIceCreamShop,
screenTrinketShop,
screenSmallRestaurant,
screenSecretClub,
screenBbsTerminal,
screenStreet,
screenHospital,
screenServerFarm,
screenSecretLab,
screenRooftopHideout,
screenPublicBBS,
screenBvkBranch,
screenCuratorShop,
screenScrapMarket,
screenSamizdatPress,
screenShowroom,
screenColumbarium,
}
var ScreenManager = NewManager(func(entity Screen) string { return entity.Name })
func registerScreen(w *World) {
screens := make([]screen, 0, len(screenDeck)+1)
for _, f := range screenDeck {
screens = append(screens, f())
}
for _, s := range append([]screen{screenSelector(screens)}, screens...) {
w.G.SceneManager.Register(screenBuild(w, s))
}
func registerScreen() {
fillSelectorPins()
registerAll(ScreenManager, func(entity Screen) {
World.G.SceneManager.Register(screenBuild(entity))
})
}
var screenFloor = []inkwell.Polygon{
@@ -74,18 +47,18 @@ var screenFloor = []inkwell.Polygon{
),
}
func screenBuild(w *World, s screen) inkwell.Scene {
exits := s.exits
if s.onSelector {
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...)
hotspots := make([]inkwell.Hotspot, 0, len(exits)+len(s.Hotspots))
hotspots = append(hotspots, s.Hotspots...)
for _, e := range exits {
hotspots = append(hotspots, e.hotspot(w))
hotspots = append(hotspots, e.hotspot())
}
actors := s.actors
actors := s.Actors
if actors == nil {
actors = []inkwell.SceneActor{
{
@@ -97,19 +70,19 @@ func screenBuild(w *World, s screen) inkwell.Scene {
},
}
}
walkboxes := s.walkboxes
walkboxes := s.Walkboxes
if walkboxes == nil {
walkboxes = screenFloor
}
onEnter := inkwell.Action(EnterScene(w, s.name))
if s.onEnter != nil {
onEnter = inkwell.Seq(onEnter, s.onEnter)
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,
Name: s.Name,
Title: s.Title,
Background: s.Background,
Actors: actors,
Walkboxes: walkboxes,
Hotspots: hotspots,
+8 -8
View File
@@ -1,17 +1,17 @@
package inc
func screenNoodleHouse() screen {
return screen{
name: ScreenNoodleHouse,
title: "NOODLE'S HOUSE — SEALED",
background: BgNoodleHouse,
onSelector: true,
exits: []exit{
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,
},
},
}
})
}
+8 -8
View File
@@ -1,12 +1,12 @@
package inc
func screenNormanApartment() screen {
return screen{
name: ScreenNormanApartment,
title: "NORMAN'S APARTMENT",
background: BgNormanApartment,
onSelector: true,
exits: []exit{
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",
@@ -18,5 +18,5 @@ func screenNormanApartment() screen {
side: sideRight,
},
},
}
})
}
+7 -7
View File
@@ -1,16 +1,16 @@
package inc
func screenPaulShop() screen {
return screen{
name: ScreenPaulShop,
title: "PAUL'S SHOP — JUNK AND GARAGE",
background: BgPaulShop,
exits: []exit{
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,
},
},
}
})
}
+7 -7
View File
@@ -1,10 +1,10 @@
package inc
func screenPoliceStation() screen {
return screen{
name: ScreenPoliceStation,
title: "SFPD — STATION AND HOLDING",
background: BgPoliceStation,
onSelector: true,
}
func init() {
ScreenManager.Register(Screen{
Name: ScreenPoliceStation,
Title: "SFPD — STATION AND HOLDING",
Background: BgPoliceStation,
OnSelector: true,
})
}
+7 -7
View File
@@ -1,10 +1,10 @@
package inc
func screenPublicBBS() screen {
return screen{
name: ScreenPublicBBS,
title: "PUBLIC BBS TERMINAL",
background: BgPublicBBS,
onSelector: true,
}
func init() {
ScreenManager.Register(Screen{
Name: ScreenPublicBBS,
Title: "PUBLIC BBS TERMINAL",
Background: BgPublicBBS,
OnSelector: true,
})
}
+7 -7
View File
@@ -1,11 +1,11 @@
package inc
func screenRooftopHideout() screen {
return screen{
name: ScreenRooftopHideout,
title: "NORMAN'S ROOFTOP HIDEOUT",
background: BgRooftopHideout,
exits: []exit{
func init() {
ScreenManager.Register(Screen{
Name: ScreenRooftopHideout,
Title: "NORMAN'S ROOFTOP HIDEOUT",
Background: BgRooftopHideout,
Exits: []exit{
{
to: ScreenNormanApartment,
label: "back down into the flat",
@@ -17,5 +17,5 @@ func screenRooftopHideout() screen {
side: sideRight,
},
},
}
})
}
+7 -7
View File
@@ -1,10 +1,10 @@
package inc
func screenSamizdatPress() screen {
return screen{
name: ScreenSamizdatPress,
title: "SAMIZDAT PRINTING HOUSE",
background: BgSamizdatPress,
onSelector: true,
}
func init() {
ScreenManager.Register(Screen{
Name: ScreenSamizdatPress,
Title: "SAMIZDAT PRINTING HOUSE",
Background: BgSamizdatPress,
OnSelector: true,
})
}
+7 -7
View File
@@ -1,10 +1,10 @@
package inc
func screenScrapMarket() screen {
return screen{
name: ScreenScrapMarket,
title: "SCRAP MARKET",
background: BgScrapMarket,
onSelector: true,
}
func init() {
ScreenManager.Register(Screen{
Name: ScreenScrapMarket,
Title: "SCRAP MARKET",
Background: BgScrapMarket,
OnSelector: true,
})
}
+7 -7
View File
@@ -1,11 +1,11 @@
package inc
func screenSecretClub() screen {
return screen{
name: ScreenSecretClub,
title: "SECRET CLUB — THE UNDERWATER SUN",
background: BgSecretClub,
exits: []exit{
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",
@@ -17,5 +17,5 @@ func screenSecretClub() screen {
side: sideNear,
},
},
}
})
}
+7 -7
View File
@@ -1,10 +1,10 @@
package inc
func screenSecretLab() screen {
return screen{
name: ScreenSecretLab,
title: "SECRET RESEARCH LABORATORY",
background: BgSecretLab,
onSelector: true,
}
func init() {
ScreenManager.Register(Screen{
Name: ScreenSecretLab,
Title: "SECRET RESEARCH LABORATORY",
Background: BgSecretLab,
OnSelector: true,
})
}
+21 -13
View File
@@ -6,26 +6,34 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func screenSelector(rest []screen) screen {
func init() {
ScreenManager.Register(Screen{
Name: ScreenSelector,
Title: "SAN FRANCISCO",
Background: BgSelector,
Actors: []inkwell.SceneActor{},
Walkboxes: []inkwell.Polygon{},
})
}
func fillSelectorPins() {
selector, ok := ScreenManager.GetByName(ScreenSelector)
if !ok {
return
}
var exits []exit
for _, s := range rest {
if !s.onSelector {
for _, entity := range ScreenManager.GetAll() {
if !entity.OnSelector {
continue
}
exits = append(exits, exit{
to: s.name,
label: pinLabel(s.title),
to: entity.Name,
label: pinLabel(entity.Title),
area: pin(len(exits)),
})
}
return screen{
name: ScreenSelector,
title: "SAN FRANCISCO",
background: BgSelector,
exits: exits,
actors: []inkwell.SceneActor{},
walkboxes: []inkwell.Polygon{},
}
selector.Exits = exits
ScreenManager.Register(selector)
}
const (
+7 -7
View File
@@ -1,10 +1,10 @@
package inc
func screenServerFarm() screen {
return screen{
name: ScreenServerFarm,
title: "SERVER FARM",
background: BgServerFarm,
onSelector: true,
}
func init() {
ScreenManager.Register(Screen{
Name: ScreenServerFarm,
Title: "SERVER FARM",
Background: BgServerFarm,
OnSelector: true,
})
}
+7 -7
View File
@@ -1,10 +1,10 @@
package inc
func screenShowroom() screen {
return screen{
name: ScreenShowroom,
title: "NEUMATRONIC SHOWROOM",
background: BgShowroom,
onSelector: true,
}
func init() {
ScreenManager.Register(Screen{
Name: ScreenShowroom,
Title: "NEUMATRONIC SHOWROOM",
Background: BgShowroom,
OnSelector: true,
})
}
+7 -7
View File
@@ -1,16 +1,16 @@
package inc
func screenSmallRestaurant() screen {
return screen{
name: ScreenSmallRestaurant,
title: "SMALL RESTAURANT — NEXT DOOR",
background: BgSmallRestaurant,
exits: []exit{
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,
},
},
}
})
}
+7 -7
View File
@@ -1,10 +1,10 @@
package inc
func screenStreet() screen {
return screen{
name: ScreenStreet,
title: "STREET",
background: BgStreet,
onSelector: true,
}
func init() {
ScreenManager.Register(Screen{
Name: ScreenStreet,
Title: "STREET",
Background: BgStreet,
OnSelector: true,
})
}
+8 -8
View File
@@ -1,12 +1,12 @@
package inc
func screenTrinketShop() screen {
return screen{
name: ScreenTrinketShop,
title: "CHINATOWN — TRINKET SHOP",
background: BgTrinketShop,
onSelector: true,
exits: []exit{
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",
@@ -20,5 +20,5 @@ func screenTrinketShop() screen {
blocked: "Just a shop, as far as the man behind the counter is concerned.",
},
},
}
})
}
+4 -4
View File
@@ -4,11 +4,11 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func scriptAwakeningFinale(w *World) inkwell.Script {
return inkwell.Script{
func init() {
ScriptManager.Register(Script{
Name: ScriptFinale,
Actions: inkwell.Seq(
SetMode(w, ModeCutscene),
SetMode(ModeCutscene),
inkwell.Wait(0.6),
inkwell.Say(Paul, "I'm putting it in. That's all this is."),
inkwell.Wait(0.4),
@@ -19,5 +19,5 @@ func scriptAwakeningFinale(w *World) inkwell.Script {
inkwell.Wait(1.0),
inkwell.ShowEnd("REAL WORLD — end of game two"),
),
}
})
}
+3 -8
View File
@@ -4,11 +4,6 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func registerScript(w *World) {
for _, s := range []inkwell.Script{
scriptTapeInsert(w),
scriptAwakeningFinale(w),
} {
w.G.ScriptManager.Register(s)
}
}
type Script = inkwell.Script
var ScriptManager = NewManager(func(entity Script) string { return entity.Name })
+5 -5
View File
@@ -4,16 +4,16 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
func scriptTapeInsert(w *World) inkwell.Script {
return inkwell.Script{
func init() {
ScriptManager.Register(Script{
Name: ScriptTapeInsert,
Actions: inkwell.Seq(
Fn(func(ctx *inkwell.Ctx) {
item := w.TakePending()
item := World.TakePending()
if item == "" {
return
}
w.SetSlot2(item)
World.SetSlot2(item)
ctx.Game.Inventory.Remove(item)
ctx.Game.State.SetVar(VarArmillaStrip, "SLOT2: READING")
}),
@@ -21,5 +21,5 @@ func scriptTapeInsert(w *World) inkwell.Script {
TapeSay(Dex, "Clicked in. Spinning. And now: nothing."),
TapeSay(Dex, "A stranger's tape in your own Armilla, Paul. I hope you know what you're doing."),
),
}
})
}
+6 -93
View File
@@ -6,6 +6,10 @@ import (
inkwell "git.teletypegames.org/engines/inkwell"
)
type Theme = inkwell.Theme
var ThemeManager = NewManager(func(entity Theme) string { return entity.Name })
const (
RealWorld = "realworld-93"
NokiaPunk = "nokia-punk"
@@ -29,97 +33,6 @@ func RGBA(hex uint32, a uint8) color.Color {
}
}
var (
Ink = RGB(0xDCD5C4)
InkDim = RGB(0x8A806B)
Amber = RGB(0xE0A33E)
AmberLo = RGB(0xA8701A)
black = RGB(0x14120E)
panel = RGB(0x0D0B08)
sceneBG = RGB(0x241F18)
)
func realWorldTheme() inkwell.Theme {
return inkwell.Theme{
Name: RealWorld,
PanelBG: black,
StatusText: Ink,
FlashText: Amber,
VerbButtonBG: RGB(0x1E1A14),
VerbButtonSelectedBG: AmberLo,
VerbButtonText: Ink,
InventorySlotBG: RGB(0x1E1A14),
InventorySlotSelectedBG: AmberLo,
SpeechBubbleBG: RGBA(0x14120E, 0xDC),
SpeechDefaultText: Ink,
DialogBG: RGBA(0x0D0B08, 0xF0),
DialogBorder: AmberLo,
DialogChoiceBG: RGB(0x1E1A14),
DialogChoiceHover: Amber,
DialogSpeaker: Amber,
DialogText: Ink,
EndCardBG: RGBA(0x000000, 0xFA),
EndCardText: Ink,
CursorColor: Amber,
HotspotOutline: RGB(0x7A6A44),
SceneBackdrop: sceneBG,
TopBarBG: panel,
TopBarText: InkDim,
TopBarAccent: Amber,
ChatLogBG: panel,
ChatLogPrompt: InkDim,
ChatLogResponse: Amber,
ChatLogSystem: RGB(0x6B6353),
CharacterPanelBG: panel,
CharacterPanelBorder: AmberLo,
CharacterPanelTitle: Amber,
}
}
var (
Green = RGB(0x3BE86B)
GreenLo = RGB(0x1F7A39)
npBlack = RGB(0x030603)
)
func nokiaPunkTheme() inkwell.Theme {
return inkwell.Theme{
Name: NokiaPunk,
PanelBG: npBlack,
StatusText: Green,
FlashText: RGB(0xD8F06A),
VerbButtonBG: RGB(0x08140A),
VerbButtonSelectedBG: GreenLo,
VerbButtonText: Green,
InventorySlotBG: RGB(0x08140A),
InventorySlotSelectedBG: GreenLo,
SpeechBubbleBG: RGBA(0x030603, 0xDC),
SpeechDefaultText: Green,
DialogBG: RGBA(0x030603, 0xF0),
DialogBorder: Green,
DialogChoiceBG: RGB(0x08140A),
DialogChoiceHover: RGB(0xB4FFC8),
DialogSpeaker: RGB(0xB4FFC8),
DialogText: Green,
EndCardBG: RGBA(0x000000, 0xFA),
EndCardText: Green,
CursorColor: Green,
HotspotOutline: GreenLo,
SceneBackdrop: npBlack,
TopBarBG: RGB(0x061006),
TopBarText: GreenLo,
TopBarAccent: Green,
ChatLogBG: RGB(0x040A04),
ChatLogPrompt: GreenLo,
ChatLogResponse: Green,
ChatLogSystem: RGB(0x156030),
CharacterPanelBG: RGB(0x040A04),
CharacterPanelBorder: Green,
CharacterPanelTitle: RGB(0xB4FFC8),
}
}
func registerTheme(g *inkwell.Game) {
g.ThemeManager.Register(realWorldTheme())
g.ThemeManager.Register(nokiaPunkTheme())
func registerTheme() {
registerAll(ThemeManager, World.G.ThemeManager.Register)
}
+44
View File
@@ -0,0 +1,44 @@
package inc
var (
Green = RGB(0x3BE86B)
GreenLo = RGB(0x1F7A39)
npBlack = RGB(0x030603)
)
func init() {
ThemeManager.Register(Theme{
Name: NokiaPunk,
PanelBG: npBlack,
StatusText: Green,
FlashText: RGB(0xD8F06A),
VerbButtonBG: RGB(0x08140A),
VerbButtonSelectedBG: GreenLo,
VerbButtonText: Green,
InventorySlotBG: RGB(0x08140A),
InventorySlotSelectedBG: GreenLo,
SpeechBubbleBG: RGBA(0x030603, 0xDC),
SpeechDefaultText: Green,
DialogBG: RGBA(0x030603, 0xF0),
DialogBorder: Green,
DialogChoiceBG: RGB(0x08140A),
DialogChoiceHover: RGB(0xB4FFC8),
DialogSpeaker: RGB(0xB4FFC8),
DialogText: Green,
EndCardBG: RGBA(0x000000, 0xFA),
EndCardText: Green,
CursorColor: Green,
HotspotOutline: GreenLo,
SceneBackdrop: npBlack,
TopBarBG: RGB(0x061006),
TopBarText: GreenLo,
TopBarAccent: Green,
ChatLogBG: RGB(0x040A04),
ChatLogPrompt: GreenLo,
ChatLogResponse: Green,
ChatLogSystem: RGB(0x156030),
CharacterPanelBG: RGB(0x040A04),
CharacterPanelBorder: Green,
CharacterPanelTitle: RGB(0xB4FFC8),
})
}
+48
View File
@@ -0,0 +1,48 @@
package inc
var (
Ink = RGB(0xDCD5C4)
InkDim = RGB(0x8A806B)
Amber = RGB(0xE0A33E)
AmberLo = RGB(0xA8701A)
black = RGB(0x14120E)
panel = RGB(0x0D0B08)
sceneBG = RGB(0x241F18)
)
func init() {
ThemeManager.Register(Theme{
Name: RealWorld,
PanelBG: black,
StatusText: Ink,
FlashText: Amber,
VerbButtonBG: RGB(0x1E1A14),
VerbButtonSelectedBG: AmberLo,
VerbButtonText: Ink,
InventorySlotBG: RGB(0x1E1A14),
InventorySlotSelectedBG: AmberLo,
SpeechBubbleBG: RGBA(0x14120E, 0xDC),
SpeechDefaultText: Ink,
DialogBG: RGBA(0x0D0B08, 0xF0),
DialogBorder: AmberLo,
DialogChoiceBG: RGB(0x1E1A14),
DialogChoiceHover: Amber,
DialogSpeaker: Amber,
DialogText: Ink,
EndCardBG: RGBA(0x000000, 0xFA),
EndCardText: Ink,
CursorColor: Amber,
HotspotOutline: RGB(0x7A6A44),
SceneBackdrop: sceneBG,
TopBarBG: panel,
TopBarText: InkDim,
TopBarAccent: Amber,
ChatLogBG: panel,
ChatLogPrompt: InkDim,
ChatLogResponse: Amber,
ChatLogSystem: RGB(0x6B6353),
CharacterPanelBG: panel,
CharacterPanelBorder: AmberLo,
CharacterPanelTitle: Amber,
})
}
+8 -9
View File
@@ -7,26 +7,25 @@ import (
"github.com/hajimehoshi/ebiten/v2/vector"
)
type Letterbox struct {
type letterbox struct {
Name string
W *World
Bar float64
}
func (l *Letterbox) GetName() string { return l.Name }
func (l *letterbox) GetName() string { return l.Name }
func (l *Letterbox) Tick(ctx *inkwell.UICtx) {
if l.W.Mode() != ModeCutscene || !l.W.TapeWaiting() {
func (l *letterbox) Tick(ctx *inkwell.UICtx) {
if World.Mode() != ModeCutscene || !World.TapeWaiting() {
return
}
if inpututil.IsKeyJustPressed(ebiten.KeySpace) {
l.W.TakeTapeOffer()
World.TakeTapeOffer()
}
}
func (l *Letterbox) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {
if l.W.Mode() != ModeCutscene {
func (l *letterbox) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {
if World.Mode() != ModeCutscene {
return
}
g := ctx.Game
@@ -40,7 +39,7 @@ func (l *Letterbox) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {
vector.DrawFilledRect(dst, 0, 0, w, float32(bar), black, false)
vector.DrawFilledRect(dst, 0, h-float32(bar), w, float32(bar), black, false)
if l.W.TapeWaiting() {
if World.TapeWaiting() {
msg := "SPACE — " + TapeDisplayName(Dex) + " has something to say"
drawTextC(dst, msg, g.Width-textW(msg)-4, g.Height-int(bar)+4, th.ChatLogResponse)
}
+17 -27
View File
@@ -27,20 +27,17 @@ const (
invGap = 4
)
func registerUI(w *World) {
g := w.G
func registerUI() {
g := World.G
g.UIManager.Register(&UseWithGuard{
g.UIManager.Register(&useWithGuard{
Name: "usewith_guard",
W: w,
})
g.UIManager.Register(&ActionPump{
g.UIManager.Register(&actionPump{
Name: "pump",
W: w,
})
g.UIManager.Register(&screenNav{
Name: "screen_nav",
W: w,
})
g.UIManager.Register(&windowSizer{
Name: "window",
@@ -55,26 +52,24 @@ func registerUI(w *World) {
Height: TopBarH,
TimeVar: VarArmillaStrip,
}
w.SetTopBar(top)
g.UIManager.Register(gated(w, "topbar_gate", top))
World.SetTopBar(top)
g.UIManager.Register(gated("topbar_gate", top))
g.UIManager.Register(&Letterbox{
g.UIManager.Register(&letterbox{
Name: "letterbox",
W: w,
Bar: 36,
})
g.UIManager.Register(&hudFrame{
Name: "hudframe",
W: w,
})
g.UIManager.Register(gated(w, "status_gate", &inkwell.StatusLine{
g.UIManager.Register(gated("status_gate", &inkwell.StatusLine{
Name: "status",
Y: statusY,
Align: inkwell.AlignLeft,
ScreenWidth: DividerX,
}))
g.UIManager.Register(gated(w, "inventory_gate", &inkwell.InventoryBar{
g.UIManager.Register(gated("inventory_gate", &inkwell.InventoryBar{
Name: "inventory",
Origin: inkwell.Point{
X: Pad + 2,
@@ -85,14 +80,12 @@ func registerUI(w *World) {
SlotSize: invSlot,
Gap: invGap,
}))
g.UIManager.Register(&TapeSlots{
g.UIManager.Register(&tapeSlots{
Name: "tapes",
W: w,
Bounds: inkwell.Rect(192, slotsY, 194, slotsH),
})
g.UIManager.Register(&TapeChannel{
g.UIManager.Register(&tapeChannel{
Name: "channel",
W: w,
Bounds: inkwell.Rect(DividerX+2, HUDTop+2, ScreenW-DividerX-4, ScreenH-HUDTop-4),
})
@@ -150,14 +143,12 @@ func (s *windowSizer) Tick(ctx *inkwell.UICtx) {
type gate struct {
Name string
W *World
Inner inkwell.Widget
}
func gated(w *World, name string, inner inkwell.Widget) inkwell.Widget {
func gated(name string, inner inkwell.Widget) inkwell.Widget {
return &gate{
Name: name,
W: w,
Inner: inner,
}
}
@@ -165,19 +156,19 @@ func gated(w *World, name string, inner inkwell.Widget) inkwell.Widget {
func (n *gate) GetName() string { return n.Name }
func (n *gate) Tick(ctx *inkwell.UICtx) {
if n.W.HUDVisible() {
if World.HUDVisible() {
n.Inner.Tick(ctx)
}
}
func (n *gate) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {
if n.W.HUDVisible() {
if World.HUDVisible() {
n.Inner.Draw(dst, ctx)
}
}
func (n *gate) BlocksClickAt(p inkwell.Point) bool {
if !n.W.HUDVisible() {
if !World.HUDVisible() {
return false
}
b, ok := n.Inner.(interface{ BlocksClickAt(inkwell.Point) bool })
@@ -186,14 +177,13 @@ func (n *gate) BlocksClickAt(p inkwell.Point) bool {
type hudFrame struct {
Name string
W *World
}
func (h *hudFrame) GetName() string { return h.Name }
func (h *hudFrame) Tick(ctx *inkwell.UICtx) {}
func (h *hudFrame) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {
if !h.W.HUDVisible() {
if !World.HUDVisible() {
return
}
th := ctx.Game.Theme()
@@ -205,5 +195,5 @@ func (h *hudFrame) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {
}
func (h *hudFrame) BlocksClickAt(p inkwell.Point) bool {
return h.W.HUDVisible() && p.Y >= HUDTop
return World.HUDVisible() && p.Y >= HUDTop
}
+3 -4
View File
@@ -8,14 +8,13 @@ import (
type screenNav struct {
Name string
W *World
}
func (n *screenNav) GetName() string { return n.Name }
func (n *screenNav) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {}
func (n *screenNav) Tick(ctx *inkwell.UICtx) {
if !n.W.HUDVisible() || n.W.Paused() {
if !World.HUDVisible() || World.Paused() {
return
}
step := 0
@@ -29,7 +28,7 @@ func (n *screenNav) Tick(ctx *inkwell.UICtx) {
return
}
if next := n.neighbour(ctx.Game, step); next != "" {
n.W.Do(inkwell.GoTo(next))
World.Do(inkwell.GoTo(next))
}
}
@@ -39,7 +38,7 @@ func (n *screenNav) neighbour(g *inkwell.Game, step int) string {
return ""
}
for i, name := range deck {
if name == n.W.Scene() {
if name == World.Scene() {
return deck[((i+step)%len(deck)+len(deck))%len(deck)]
}
}
+8 -9
View File
@@ -8,22 +8,21 @@ import (
"github.com/hajimehoshi/ebiten/v2/vector"
)
type TapeChannel struct {
type tapeChannel struct {
Name string
W *World
Bounds inkwell.Rectangle
}
func (t *TapeChannel) GetName() string { return t.Name }
func (t *tapeChannel) GetName() string { return t.Name }
func (t *TapeChannel) Tick(ctx *inkwell.UICtx) {}
func (t *tapeChannel) Tick(ctx *inkwell.UICtx) {}
func (t *TapeChannel) BlocksClickAt(p inkwell.Point) bool {
return t.W.HUDVisible() && t.Bounds.Contains(p)
func (t *tapeChannel) BlocksClickAt(p inkwell.Point) bool {
return World.HUDVisible() && t.Bounds.Contains(p)
}
func (t *TapeChannel) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {
if !t.W.HUDVisible() {
func (t *tapeChannel) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {
if !World.HUDVisible() {
return
}
g := ctx.Game
@@ -84,7 +83,7 @@ func (t *TapeChannel) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {
}
}
func (t *TapeChannel) lastSpeaker(g *inkwell.Game, th inkwell.Theme) (string, color.Color) {
func (t *tapeChannel) lastSpeaker(g *inkwell.Game, th inkwell.Theme) (string, color.Color) {
msgs := g.Messages()
for i := len(msgs) - 1; i >= 0; i-- {
m := msgs[i]
+23 -24
View File
@@ -6,26 +6,25 @@ import (
"github.com/hajimehoshi/ebiten/v2/vector"
)
type TapeSlots struct {
type tapeSlots struct {
Name string
W *World
Bounds inkwell.Rectangle
}
func (t *TapeSlots) GetName() string { return t.Name }
func (t *tapeSlots) GetName() string { return t.Name }
func (t *TapeSlots) BlocksClickAt(p inkwell.Point) bool {
return t.W.HUDVisible() && t.Bounds.Contains(p)
func (t *tapeSlots) BlocksClickAt(p inkwell.Point) bool {
return World.HUDVisible() && t.Bounds.Contains(p)
}
func (t *TapeSlots) slotRects() (inkwell.Rectangle, inkwell.Rectangle) {
func (t *tapeSlots) slotRects() (inkwell.Rectangle, inkwell.Rectangle) {
b := t.Bounds
w := (b.W - Pad) / 2
return inkwell.Rect(b.X, b.Y, w, b.H), inkwell.Rect(b.X+w+Pad, b.Y, w, b.H)
}
func (t *TapeSlots) Tick(ctx *inkwell.UICtx) {
if !t.W.HUDVisible() {
func (t *tapeSlots) Tick(ctx *inkwell.UICtx) {
if !World.HUDVisible() {
return
}
g := ctx.Game
@@ -36,7 +35,7 @@ func (t *TapeSlots) Tick(ctx *inkwell.UICtx) {
case r1.Contains(mp):
g.SetHoverLabel(TapeDisplayName(Dex))
case r2.Contains(mp):
if s := t.W.Slot2(); s != "" {
if s := World.Slot2(); s != "" {
g.SetHoverLabel(itemLabel(g, s))
} else {
g.SetHoverLabel("empty tape slot")
@@ -57,45 +56,45 @@ func (t *TapeSlots) Tick(ctx *inkwell.UICtx) {
if slot2 && sel != "" {
g.Inventory.Select("")
if IsTapeItem(sel) {
t.W.SetPending(sel)
t.W.Do(inkwell.RunScript(ScriptTapeInsert))
World.SetPending(sel)
World.Do(inkwell.RunScript(ScriptTapeInsert))
return
}
t.W.Do(TapeSay(Dex, "That isn't a tape, Paul. That's an object. There is a difference."))
World.Do(TapeSay(Dex, "That isn't a tape, Paul. That's an object. There is a difference."))
return
}
if !slot2 && sel != "" {
g.Inventory.Select("")
t.W.Do(TapeSay(Dex, "The first slot is mine. I'm not moving."))
World.Do(TapeSay(Dex, "The first slot is mine. I'm not moving."))
return
}
switch g.SelectedVerb() {
case "talk":
if !slot2 {
t.W.Do(Paused(t.W, inkwell.RunDialogue(DlgDex)))
World.Do(Paused(inkwell.RunDialogue(DlgDex)))
return
}
if s := t.W.Slot2(); s != "" {
t.W.Do(Paused(t.W, inkwell.RunDialogue(TapeDialogue(s))))
if s := World.Slot2(); s != "" {
World.Do(Paused(inkwell.RunDialogue(TapeDialogue(s))))
return
}
t.W.Do(TapeSay(Dex, "Empty. Nobody to talk to."))
World.Do(TapeSay(Dex, "Empty. Nobody to talk to."))
default:
if !slot2 {
t.W.Do(TapeSay(Dex, "Me. Four kilobytes of a dead man. Be grateful."))
World.Do(TapeSay(Dex, "Me. Four kilobytes of a dead man. Be grateful."))
return
}
if s := t.W.Slot2(); s != "" {
t.W.Do(TapeSay(Dex, "That is the second slot. Be careful what you let in."))
if s := World.Slot2(); s != "" {
World.Do(TapeSay(Dex, "That is the second slot. Be careful what you let in."))
return
}
t.W.Do(TapeSay(Dex, "The second slot is empty. That's the rarer state."))
World.Do(TapeSay(Dex, "The second slot is empty. That's the rarer state."))
}
}
func (t *TapeSlots) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {
if !t.W.HUDVisible() {
func (t *tapeSlots) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {
if !World.HUDVisible() {
return
}
g := ctx.Game
@@ -116,7 +115,7 @@ func (t *TapeSlots) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {
}
drawSlot(r1, "SLOT 1", TapeDisplayName(Dex), true)
if s := t.W.Slot2(); s != "" {
if s := World.Slot2(); s != "" {
drawSlot(r2, "SLOT 2", itemLabel(g, s), true)
} else {
drawSlot(r2, "SLOT 2", "empty", false)
+6 -7
View File
@@ -7,17 +7,16 @@ import (
"github.com/hajimehoshi/ebiten/v2"
)
type UseWithGuard struct {
type useWithGuard struct {
Name string
W *World
}
func (u *UseWithGuard) GetName() string { return u.Name }
func (u *UseWithGuard) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {}
func (u *useWithGuard) GetName() string { return u.Name }
func (u *useWithGuard) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {}
func (u *UseWithGuard) Tick(ctx *inkwell.UICtx) {
func (u *useWithGuard) Tick(ctx *inkwell.UICtx) {
g := ctx.Game
if !u.W.HUDVisible() || !g.Input.LeftClicked() {
if !World.HUDVisible() || !g.Input.LeftClicked() {
return
}
sel := g.Inventory.Selected()
@@ -42,7 +41,7 @@ func (u *UseWithGuard) Tick(ctx *inkwell.UICtx) {
g.State.NoteTalked(key)
n := g.State.Talked(key)
u.W.Do(inkwell.Seq(
World.Do(inkwell.Seq(
inkwell.Say(Paul, pick(paulFails, n)),
TapeSay(Dex, dexFail(n)),
))
+23 -26
View File
@@ -5,13 +5,13 @@ import (
"github.com/hajimehoshi/ebiten/v2"
)
func (w *World) Do(a inkwell.Action) {
func (w *world) Do(a inkwell.Action) {
if a != nil {
w.queue = append(w.queue, a)
}
}
func (w *World) PumpTick(dt float64) {
func (w *world) PumpTick(dt float64) {
if w.top != nil {
title := w.sceneTitle()
@@ -40,7 +40,7 @@ func (w *World) PumpTick(dt float64) {
}
}
func (w *World) sceneTitle() string {
func (w *world) sceneTitle() string {
s, ok := w.G.SceneManager.Get(w.scene)
if !ok {
return ""
@@ -51,14 +51,13 @@ func (w *World) sceneTitle() string {
return s.Name
}
type ActionPump struct {
type actionPump struct {
Name string
W *World
}
func (p *ActionPump) GetName() string { return p.Name }
func (p *ActionPump) Tick(ctx *inkwell.UICtx) { p.W.PumpTick(ctx.DT) }
func (p *ActionPump) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {}
func (p *actionPump) GetName() string { return p.Name }
func (p *actionPump) Tick(ctx *inkwell.UICtx) { World.PumpTick(ctx.DT) }
func (p *actionPump) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {}
type fnAction struct{ fn func(*inkwell.Ctx) }
@@ -85,25 +84,25 @@ func UseTheme(name string) inkwell.Action {
return Fn(func(ctx *inkwell.Ctx) { ctx.Game.UseTheme(name) })
}
func SetMode(w *World, m Mode) inkwell.Action {
return Fn(func(*inkwell.Ctx) { w.SetMode(m) })
func SetMode(m Mode) inkwell.Action {
return Fn(func(*inkwell.Ctx) { World.SetMode(m) })
}
func EnterScene(w *World, name string) inkwell.Action {
func EnterScene(name string) inkwell.Action {
return Fn(func(*inkwell.Ctx) {
if name != w.scene {
w.prev = w.scene
if name != World.scene {
World.prev = World.scene
}
w.scene = name
World.scene = name
})
}
func Back(w *World) inkwell.Action {
func Back() inkwell.Action {
return Fn(func(ctx *inkwell.Ctx) {
if w.prev == "" {
if World.prev == "" {
return
}
inkwell.GoTo(w.prev).Start().Tick(ctx)
inkwell.GoTo(World.prev).Start().Tick(ctx)
})
}
@@ -143,14 +142,14 @@ func (r *tapeSayRunner) Tick(ctx *inkwell.Ctx) inkwell.Status {
return inkwell.StatusRunning
}
func TapeOffer(w *World, line inkwell.Action) inkwell.Action {
func TapeOffer(line inkwell.Action) inkwell.Action {
return Fn(func(*inkwell.Ctx) {
w.tapeLine = line
w.tapeWaiting = true
World.tapeLine = line
World.tapeWaiting = true
})
}
func (w *World) TakeTapeOffer() {
func (w *world) TakeTapeOffer() {
if !w.tapeWaiting {
return
}
@@ -160,15 +159,13 @@ func (w *World) TakeTapeOffer() {
w.Do(line)
}
func Paused(w *World, inner inkwell.Action) inkwell.Action {
func Paused(inner inkwell.Action) inkwell.Action {
return &pausedAction{
w: w,
inner: inner,
}
}
type pausedAction struct {
w *World
inner inkwell.Action
}
@@ -188,11 +185,11 @@ type pausedRunner struct {
func (r *pausedRunner) Tick(ctx *inkwell.Ctx) inkwell.Status {
if !r.started {
r.started = true
r.spec.w.paused = true
World.paused = true
}
s := r.inner.Tick(ctx)
if s != inkwell.StatusRunning {
r.spec.w.paused = false
World.paused = false
}
return s
}
+27 -18
View File
@@ -12,7 +12,7 @@ const (
ModeMenu Mode = "menu"
)
type World struct {
type world struct {
G *inkwell.Game
mode Mode
@@ -29,42 +29,51 @@ type World struct {
tapeLine inkwell.Action
}
func newWorld(g *inkwell.Game) *World {
return &World{
G: g,
mode: ModePlay,
}
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
w.running = nil
w.top = nil
w.tapeWaiting = false
w.tapeLine = nil
}
func (w *World) Mode() Mode { return w.mode }
func (w *world) Mode() Mode { return w.mode }
func (w *World) SetMode(m Mode) { w.mode = m }
func (w *world) SetMode(m Mode) { w.mode = m }
func (w *World) HUDVisible() bool { return w.mode == ModePlay }
func (w *world) HUDVisible() bool { return w.mode == ModePlay }
func (w *World) Scene() string { return w.scene }
func (w *world) Scene() string { return w.scene }
func (w *World) Previous() string { return w.prev }
func (w *world) Previous() string { return w.prev }
func (w *World) Paused() bool { return w.paused }
func (w *world) Paused() bool { return w.paused }
func (w *World) SetTopBar(t *inkwell.TopBar) { w.top = t }
func (w *world) SetTopBar(t *inkwell.TopBar) { w.top = t }
func (w *World) Slot2() string {
func (w *world) Slot2() string {
if v, ok := w.G.State.Var(VarSlot2).(string); ok {
return v
}
return ""
}
func (w *World) SetSlot2(item string) { w.G.State.SetVar(VarSlot2, item) }
func (w *world) SetSlot2(item string) { w.G.State.SetVar(VarSlot2, item) }
func (w *World) SetPending(item string) { w.pending = item }
func (w *world) SetPending(item string) { w.pending = item }
func (w *World) TakePending() string {
func (w *world) TakePending() string {
item := w.pending
w.pending = ""
return item
}
func (w *World) TapeWaiting() bool { return w.tapeWaiting }
func (w *world) TapeWaiting() bool { return w.tapeWaiting }