rename pncdsl to inkwell

This commit is contained in:
2026-07-30 12:16:39 +02:00
parent 99902e0ab4
commit fd7cd6d1a9
57 changed files with 157 additions and 157 deletions
+76 -76
View File
@@ -1,4 +1,4 @@
# pncdsl — reference manual # inkwell — reference manual
A point-and-click adventure game framework for Go, built on top of A point-and-click adventure game framework for Go, built on top of
[Ebitengine](https://ebitengine.org/). Games are composed by registering [Ebitengine](https://ebitengine.org/). Games are composed by registering
@@ -7,15 +7,15 @@ single `*Game` root. The library handles input, rendering, dialog trees,
cutscenes, HUD, themes and lazy asset loading; the domain only declares cutscenes, HUD, themes and lazy asset loading; the domain only declares
content. content.
This document is the full library reference. The companion `pncdsl-demo` This document is the full library reference. The companion `inkwell-demo`
repo at <ssh://git@git.teletypegames.org:2222/games/pncdsl-demo> is a small repo at <ssh://git@git.teletypegames.org:2222/games/inkwell-demo> is a small
end-to-end example ("Morning Coffee") that consumes this library as a end-to-end example ("Morning Coffee") that consumes this library as a
plain Go module. plain Go module.
**Module path:** `git.teletypegames.org/games/pncdsl` **Module path:** `git.teletypegames.org/games/inkwell`
```go ```go
import "git.teletypegames.org/games/pncdsl" import "git.teletypegames.org/games/inkwell"
``` ```
--- ---
@@ -69,7 +69,7 @@ import "git.teletypegames.org/games/pncdsl"
## 1. Introduction ## 1. Introduction
`pncdsl` is **not** a generic engine — it is a thin Go-level DSL layer on `inkwell` is **not** a generic engine — it is a thin Go-level DSL layer on
top of Ebitengine that implements the fixed skeleton of the point-and-click top of Ebitengine that implements the fixed skeleton of the point-and-click
adventure genre as it existed in the LucasArts SCUMM era (Maniac Mansion, adventure genre as it existed in the LucasArts SCUMM era (Maniac Mansion,
Monkey Island, Day of the Tentacle): Monkey Island, Day of the Tentacle):
@@ -90,7 +90,7 @@ items, scenes, characters, dialogues, scripts, assets, verbs, widgets and
themes alike. themes alike.
A complete example game ("Morning Coffee") lives in the separate A complete example game ("Morning Coffee") lives in the separate
[`pncdsl-demo`](ssh://git@git.teletypegames.org:2222/games/pncdsl-demo) repo [`inkwell-demo`](ssh://git@git.teletypegames.org:2222/games/inkwell-demo) repo
and consumes this library as a regular Go module. and consumes this library as a regular Go module.
--- ---
@@ -104,7 +104,7 @@ context. Ebitengine handles windowing and the render loop.
```bash ```bash
go mod init my-game go mod init my-game
go get git.teletypegames.org/games/pncdsl go get git.teletypegames.org/games/inkwell
``` ```
If the private host can't be reached over HTTPS, point Go at the SSH If the private host can't be reached over HTTPS, point Go at the SSH
@@ -123,31 +123,31 @@ package main
import ( import (
"log" "log"
"git.teletypegames.org/games/pncdsl" "git.teletypegames.org/games/inkwell"
) )
func main() { func main() {
g := pncdsl.NewGame("Sample", 320, 200) g := inkwell.NewGame("Sample", 320, 200)
g.AssetManager.Register(pncdsl.Asset{ g.AssetManager.Register(inkwell.Asset{
Name: "bg/room", Path: "assets/bg/room.png", Kind: pncdsl.AssetImage, Name: "bg/room", Path: "assets/bg/room.png", Kind: inkwell.AssetImage,
}) })
g.CharacterManager.Register(pncdsl.Character{Name: "player", W: 28, H: 62}) g.CharacterManager.Register(inkwell.Character{Name: "player", W: 28, H: 62})
g.SceneManager.Register(pncdsl.Scene{ g.SceneManager.Register(inkwell.Scene{
Name: "room", Name: "room",
Background: "bg/room", Background: "bg/room",
Actors: []pncdsl.SceneActor{{CharacterName: "player", At: pncdsl.Point{X: 160, Y: 140}}}, Actors: []inkwell.SceneActor{{CharacterName: "player", At: inkwell.Point{X: 160, Y: 140}}},
Hotspots: []pncdsl.Hotspot{{ Hotspots: []inkwell.Hotspot{{
Name: "door", Name: "door",
Area: pncdsl.Rect(260, 50, 40, 90), Area: inkwell.Rect(260, 50, 40, 90),
Label: "door", Label: "door",
OnLook: pncdsl.Say("player", "A wooden door."), OnLook: inkwell.Say("player", "A wooden door."),
}}, }},
}) })
g.StartAt("room") g.StartAt("room")
pncdsl.RegisterDefaultUI(g) // optional; auto-called by Run if no widgets registered inkwell.RegisterDefaultUI(g) // optional; auto-called by Run if no widgets registered
if err := pncdsl.Run(g); err != nil { if err := inkwell.Run(g); err != nil {
log.Fatal(err) log.Fatal(err)
} }
} }
@@ -160,7 +160,7 @@ func main() {
Every name-addressable entity goes through one shared generic type: Every name-addressable entity goes through one shared generic type:
```go ```go
// pncdsl/core.manager.go // inkwell/core.manager.go
type Named interface { type Named interface {
GetName() string GetName() string
@@ -220,7 +220,7 @@ shadowed entities at runtime.
## 4. The Game aggregate ## 4. The Game aggregate
```go ```go
// pncdsl/core.game.go // inkwell/core.game.go
type Game struct { type Game struct {
Title string Title string
@@ -268,7 +268,7 @@ explicitly, or let `Run` install the default set if `UIManager` is empty.
func (g *Game) StartAt(name string) *Game // entry scene func (g *Game) StartAt(name string) *Game // entry scene
func (g *Game) OnStart(a Action) *Game // action run after the scene's OnEnter func (g *Game) OnStart(a Action) *Game // action run after the scene's OnEnter
func (g *Game) Validate() error // cross-check name references func (g *Game) Validate() error // cross-check name references
func (g *Game) Run() error // same as pncdsl.Run(g) func (g *Game) Run() error // same as inkwell.Run(g)
``` ```
`StartAt` and `OnStart` return `*Game` so they chain at the end of `Build`. `StartAt` and `OnStart` return `*Game` so they chain at the end of `Build`.
@@ -350,7 +350,7 @@ library can swap to `text/v2` later without changing call sites.
## 5. Geometry primitives ## 5. Geometry primitives
```go ```go
// pncdsl/util.geometry.go // inkwell/util.geometry.go
type Point struct{ X, Y float64 } type Point struct{ X, Y float64 }
@@ -388,7 +388,7 @@ each frame.
### 6.1 Asset ### 6.1 Asset
```go ```go
// pncdsl/asset.def.go // inkwell/asset.def.go
type AssetKind int type AssetKind int
const ( const (
@@ -410,17 +410,17 @@ deterministic colored placeholder so the game runs without any art —
useful for prototyping or CI. useful for prototyping or CI.
```go ```go
g.AssetManager.Register(pncdsl.Asset{ g.AssetManager.Register(inkwell.Asset{
Name: "bg/kitchen", Name: "bg/kitchen",
Path: "assets/bg/kitchen.png", Path: "assets/bg/kitchen.png",
Kind: pncdsl.AssetImage, Kind: inkwell.AssetImage,
}) })
``` ```
### 6.2 Scene ### 6.2 Scene
```go ```go
// pncdsl/scene.def.go // inkwell/scene.def.go
type Scene struct { type Scene struct {
Name string Name string
@@ -458,7 +458,7 @@ engine samples each trigger once per idle frame; see [§6.4](#64-trigger).
### 6.3 Hotspot ### 6.3 Hotspot
```go ```go
// pncdsl/scene.hotspot.go // inkwell/scene.hotspot.go
type Hotspot struct { type Hotspot struct {
Name string Name string
@@ -500,7 +500,7 @@ for custom verbs.
### 6.4 Trigger ### 6.4 Trigger
```go ```go
// pncdsl/scene.trigger.go // inkwell/scene.trigger.go
type Trigger struct { type Trigger struct {
Name string Name string
@@ -525,7 +525,7 @@ re-entry.
### 6.5 Item ### 6.5 Item
```go ```go
// pncdsl/item.def.go // inkwell/item.def.go
type Item struct { type Item struct {
Name string Name string
@@ -548,7 +548,7 @@ hotspot with an item selected resolves the action via, in order:
### 6.6 Inventory ### 6.6 Inventory
```go ```go
// pncdsl/item.inventory.go // inkwell/item.inventory.go
type Inventory struct{ /* unexported */ } type Inventory struct{ /* unexported */ }
@@ -567,7 +567,7 @@ Not a manager — pure runtime state owned by `Game`. Mutated by actions
### 6.7 Character ### 6.7 Character
```go ```go
// pncdsl/actor.def.go // inkwell/actor.def.go
type Character struct { type Character struct {
Name string Name string
@@ -608,7 +608,7 @@ computed by [walkbox routing](#62-scene).
### 6.8 Dialogue ### 6.8 Dialogue
```go ```go
// pncdsl/dialog.def.go // inkwell/dialog.def.go
type Dialogue struct { type Dialogue struct {
Name string Name string
@@ -650,7 +650,7 @@ The dialog flow:
### 6.9 Script ### 6.9 Script
```go ```go
// pncdsl/action.script.go // inkwell/action.script.go
type Script struct { type Script struct {
Name string Name string
@@ -665,7 +665,7 @@ Fire one with `RunScript("name")`.
### 6.10 Verb ### 6.10 Verb
```go ```go
// pncdsl/ui.verb.go // inkwell/ui.verb.go
type Verb struct { type Verb struct {
Name string // canonical id, e.g. "look" Name string // canonical id, e.g. "look"
@@ -678,9 +678,9 @@ type Verb struct {
`take`). Add your own: `take`). Add your own:
```go ```go
g.VerbManager.Register(pncdsl.Verb{ g.VerbManager.Register(inkwell.Verb{
Name: "push", Label: "Lökd", Name: "push", Label: "Lökd",
Default: pncdsl.Say("player", "Nem mozdul."), Default: inkwell.Say("player", "Nem mozdul."),
}) })
``` ```
@@ -694,7 +694,7 @@ frame, so adding a verb at runtime is enough to make it appear.
### 7.1 Action and Runner ### 7.1 Action and Runner
```go ```go
// pncdsl/action.def.go // inkwell/action.def.go
type Action interface { type Action interface {
Start() Runner Start() Runner
@@ -794,10 +794,10 @@ func (r *fadeRunner) Tick(ctx *Ctx) Status {
For one-shot mutations, `Custom` is shorter: For one-shot mutations, `Custom` is shorter:
```go ```go
bumpScore := pncdsl.Custom(func(ctx *pncdsl.Ctx) pncdsl.Status { bumpScore := inkwell.Custom(func(ctx *inkwell.Ctx) inkwell.Status {
cur := ctx.Game.State.Var("score").(int) cur := ctx.Game.State.Var("score").(int)
ctx.Game.State.SetVar("score", cur+10) ctx.Game.State.SetVar("score", cur+10)
return pncdsl.StatusDone return inkwell.StatusDone
}) })
``` ```
@@ -806,7 +806,7 @@ bumpScore := pncdsl.Custom(func(ctx *pncdsl.Ctx) pncdsl.Status {
## 8. Conditions ## 8. Conditions
```go ```go
// pncdsl/action.condition.go // inkwell/action.condition.go
type Condition interface { type Condition interface {
Eval(ctx *Ctx) bool Eval(ctx *Ctx) bool
@@ -832,7 +832,7 @@ Conditions are stateless — they are evaluated lazily, including inside
## 9. World state ## 9. World state
```go ```go
// pncdsl/state.def.go // inkwell/state.def.go
type State struct{ /* unexported */ } type State struct{ /* unexported */ }
@@ -872,7 +872,7 @@ hotbars — without touching the library.
### 10.1 Widget interface ### 10.1 Widget interface
```go ```go
// pncdsl/ui.widget.go // inkwell/ui.widget.go
type Widget interface { type Widget interface {
Named Named
@@ -922,7 +922,7 @@ that frame.
### 10.3 Built-in widgets ### 10.3 Built-in widgets
Each widget lives in its own `pncdsl/ui.<name>.go` file. Fields with a Each widget lives in its own `inkwell/ui.<name>.go` file. Fields with a
zero value fall back to a sensible default. zero value fall back to a sensible default.
#### `Panel` (`ui.panel.go`) #### `Panel` (`ui.panel.go`)
@@ -1184,7 +1184,7 @@ zones for the radial menu.
### 10.5 Registration helpers ### 10.5 Registration helpers
```go ```go
// pncdsl/ui.defaults.go // inkwell/ui.defaults.go
func RegisterDefaultUI(g *Game) // SCUMM-style verb bar + inventory func RegisterDefaultUI(g *Game) // SCUMM-style verb bar + inventory
func RegisterRadialVerbUI(g *Game) // verb-coin (right-click) + wider inventory func RegisterRadialVerbUI(g *Game) // verb-coin (right-click) + wider inventory
@@ -1205,18 +1205,18 @@ Ebitengine primitives, and you're done.
```go ```go
type Minimap struct { type Minimap struct {
Name string Name string
Bounds pncdsl.Rectangle Bounds inkwell.Rectangle
} }
func (m *Minimap) GetName() string { return m.Name } func (m *Minimap) GetName() string { return m.Name }
func (m *Minimap) Tick(ctx *pncdsl.UICtx) {} func (m *Minimap) Tick(ctx *inkwell.UICtx) {}
func (m *Minimap) Draw(dst *ebiten.Image, ctx *pncdsl.UICtx) { func (m *Minimap) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {
// ... render scene thumbnail, mark NPCs, etc. // ... render scene thumbnail, mark NPCs, etc.
} }
g.UIManager.Register(&Minimap{ g.UIManager.Register(&Minimap{
Name: "minimap", Name: "minimap",
Bounds: pncdsl.Rect(220, 4, 96, 56), Bounds: inkwell.Rect(220, 4, 96, 56),
}) })
``` ```
@@ -1225,7 +1225,7 @@ g.UIManager.Register(&Minimap{
## 11. Themes ## 11. Themes
```go ```go
// pncdsl/ui.theme.go // inkwell/ui.theme.go
type Theme struct { type Theme struct {
Name string Name string
@@ -1296,7 +1296,7 @@ type Theme struct {
Register your own: Register your own:
```go ```go
g.ThemeManager.Register(pncdsl.Theme{ g.ThemeManager.Register(inkwell.Theme{
Name: "midnight-noir", Name: "midnight-noir",
PanelBG: color.RGBA{6, 6, 12, 255}, PanelBG: color.RGBA{6, 6, 12, 255},
StatusText: color.White, StatusText: color.White,
@@ -1344,7 +1344,7 @@ same registry but are not loaded by the library at this milestone.
## 13. Audio ## 13. Audio
```go ```go
// pncdsl/asset.audio.go // inkwell/asset.audio.go
type AudioPlayer struct{ /* unexported */ } type AudioPlayer struct{ /* unexported */ }
@@ -1381,7 +1381,7 @@ methods, so a domain authored against the original stub still works.
## 14. Input ## 14. Input
```go ```go
// pncdsl/input.def.go // inkwell/input.def.go
type Input struct{ /* unexported */ } type Input struct{ /* unexported */ }
@@ -1406,7 +1406,7 @@ directly.
## 15. The game loop ## 15. The game loop
```go ```go
// pncdsl/core.dsl.go // inkwell/core.dsl.go
func Run(g *Game) error // toplevel — same as g.Run() func Run(g *Game) error // toplevel — same as g.Run()
``` ```
@@ -1493,7 +1493,7 @@ the whole-sprite / placeholder draw when no clip applies.
## 16. Scene transitions ## 16. Scene transitions
```go ```go
// pncdsl/scene.transition.go (unexported) // inkwell/scene.transition.go (unexported)
type transition struct{ /* unexported */ } type transition struct{ /* unexported */ }
``` ```
@@ -1541,7 +1541,7 @@ eagerly at `Register` time via panic.
## 18. Save and load ## 18. Save and load
```go ```go
// pncdsl/state.save.go // inkwell/state.save.go
func (g *Game) Save(slot int) error func (g *Game) Save(slot int) error
func (g *Game) Load(slot int) error func (g *Game) Load(slot int) error
@@ -1585,20 +1585,20 @@ future version returns an error rather than silently truncating fields.
## 19. Errors ## 19. Errors
```go ```go
// pncdsl/core.errors.go // inkwell/core.errors.go
var ( var (
ErrUnknownAsset = errors.New("pncdsl: unknown asset") ErrUnknownAsset = errors.New("inkwell: unknown asset")
ErrUnknownScene = errors.New("pncdsl: unknown scene") ErrUnknownScene = errors.New("inkwell: unknown scene")
ErrUnknownItem = errors.New("pncdsl: unknown item") ErrUnknownItem = errors.New("inkwell: unknown item")
ErrUnknownCharacter = errors.New("pncdsl: unknown character") ErrUnknownCharacter = errors.New("inkwell: unknown character")
ErrUnknownDialogue = errors.New("pncdsl: unknown dialogue") ErrUnknownDialogue = errors.New("inkwell: unknown dialogue")
ErrUnknownDialogueNode = errors.New("pncdsl: unknown dialogue node") ErrUnknownDialogueNode = errors.New("inkwell: unknown dialogue node")
ErrUnknownScript = errors.New("pncdsl: unknown script") ErrUnknownScript = errors.New("inkwell: unknown script")
ErrUnknownVerb = errors.New("pncdsl: unknown verb") ErrUnknownVerb = errors.New("inkwell: unknown verb")
ErrDuplicateName = errors.New("pncdsl: duplicate name") ErrDuplicateName = errors.New("inkwell: duplicate name")
ErrNoStartScene = errors.New("pncdsl: StartAt not set or unknown scene") ErrNoStartScene = errors.New("inkwell: StartAt not set or unknown scene")
ErrSceneMissingBackground = errors.New("pncdsl: scene has no background") ErrSceneMissingBackground = errors.New("inkwell: scene has no background")
) )
``` ```
@@ -1617,7 +1617,7 @@ go build ./...
The library is single-package and has no in-tree unit tests at this The library is single-package and has no in-tree unit tests at this
milestone — the manager + action primitives are intentionally small milestone — the manager + action primitives are intentionally small
enough that the `pncdsl-demo` repo's `build_test.go` (a headless enough that the `inkwell-demo` repo's `build_test.go` (a headless
`Build() + Validate()` smoke test) covers the integration surface: `Build() + Validate()` smoke test) covers the integration surface:
```go ```go
@@ -1639,8 +1639,8 @@ of the demo for the time being.
### 20.1 Useful runtime debug switches ### 20.1 Useful runtime debug switches
```go ```go
pncdsl.DebugLog = true // logf output to stderr (script queue, audio, scene change) inkwell.DebugLog = true // logf output to stderr (script queue, audio, scene change)
&pncdsl.HotspotDebug{Enabled: true} // start with the F1 overlay on &inkwell.HotspotDebug{Enabled: true} // start with the F1 overlay on
``` ```
--- ---
@@ -1652,7 +1652,7 @@ directory. The naming convention is `theme.identifier.go` — `ls core.*`
or `ls ui.*` instantly groups related sources. or `ls ui.*` instantly groups related sources.
``` ```
pncdsl/ # module git.teletypegames.org/games/pncdsl inkwell/ # module git.teletypegames.org/games/inkwell
├── go.mod ├── go.mod
├── go.sum ├── go.sum
@@ -1725,9 +1725,9 @@ pncdsl/ # module git.teletypegames.org/games/pncdsl
└── README.md # this file └── README.md # this file
``` ```
The companion demo project (`pncdsl-demo`) lives in its own repo at The companion demo project (`inkwell-demo`) lives in its own repo at
<ssh://git@git.teletypegames.org:2222/games/pncdsl-demo>. It consumes this <ssh://git@git.teletypegames.org:2222/games/inkwell-demo>. It consumes this
package via `require git.teletypegames.org/games/pncdsl …` in its `go.mod` package via `require git.teletypegames.org/games/inkwell …` in its `go.mod`
no in-tree coupling. no in-tree coupling.
--- ---
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
type Condition interface { type Condition interface {
Eval(ctx *Ctx) bool Eval(ctx *Ctx) bool
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
// Status is the outcome of one tick of a Runner. // Status is the outcome of one tick of a Runner.
type Status int type Status int
+1 -1
View File
@@ -1,3 +1,3 @@
package pncdsl package inkwell
type ScriptManager = Manager[Script] type ScriptManager = Manager[Script]
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
type Script struct { type Script struct {
Name string Name string
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import "image" import "image"
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import "image/color" import "image/color"
+1 -1
View File
@@ -1,3 +1,3 @@
package pncdsl package inkwell
type CharacterManager = Manager[Character] type CharacterManager = Manager[Character]
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import ( import (
"bytes" "bytes"
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
type AssetKind int type AssetKind int
+2 -2
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import ( import (
"io/fs" "io/fs"
@@ -14,7 +14,7 @@ import (
// //go:embed assets // //go:embed assets
// var assetsFS embed.FS // var assetsFS embed.FS
// //
// func init() { pncdsl.AssetFS = assetsFS } // func init() { inkwell.AssetFS = assetsFS }
// //
// Asset.Path values are used as-is, so they must be slash-separated // Asset.Path values are used as-is, so they must be slash-separated
// and relative (e.g. "assets/bg/bedroom.png") — the same form that // and relative (e.g. "assets/bg/bedroom.png") — the same form that
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import ( import (
"hash/fnv" "hash/fnv"
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import ( import (
"image/color" "image/color"
+2 -2
View File
@@ -1,6 +1,6 @@
// Package pncdsl is a point-and-click adventure game library built on top of // Package inkwell is a point-and-click adventure game library built on top of
// Ebitengine. Games are composed declaratively by registering plain struct // Ebitengine. Games are composed declaratively by registering plain struct
// literals into per-entity *Manager registries hanging off the *Game root. // literals into per-entity *Manager registries hanging off the *Game root.
// //
// See PLAN.md in the repo root for the full design notes. // See PLAN.md in the repo root for the full design notes.
package pncdsl package inkwell
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import ( import (
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import ( import (
"image/color" "image/color"
+12 -12
View File
@@ -1,17 +1,17 @@
package pncdsl package inkwell
import "errors" import "errors"
var ( var (
ErrUnknownAsset = errors.New("pncdsl: unknown asset") ErrUnknownAsset = errors.New("inkwell: unknown asset")
ErrUnknownScene = errors.New("pncdsl: unknown scene") ErrUnknownScene = errors.New("inkwell: unknown scene")
ErrUnknownItem = errors.New("pncdsl: unknown item") ErrUnknownItem = errors.New("inkwell: unknown item")
ErrUnknownCharacter = errors.New("pncdsl: unknown character") ErrUnknownCharacter = errors.New("inkwell: unknown character")
ErrUnknownDialogue = errors.New("pncdsl: unknown dialogue") ErrUnknownDialogue = errors.New("inkwell: unknown dialogue")
ErrUnknownDialogueNode = errors.New("pncdsl: unknown dialogue node") ErrUnknownDialogueNode = errors.New("inkwell: unknown dialogue node")
ErrUnknownScript = errors.New("pncdsl: unknown script") ErrUnknownScript = errors.New("inkwell: unknown script")
ErrUnknownVerb = errors.New("pncdsl: unknown verb") ErrUnknownVerb = errors.New("inkwell: unknown verb")
ErrDuplicateName = errors.New("pncdsl: duplicate name") ErrDuplicateName = errors.New("inkwell: duplicate name")
ErrNoStartScene = errors.New("pncdsl: StartAt not set or unknown scene") ErrNoStartScene = errors.New("inkwell: StartAt not set or unknown scene")
ErrSceneMissingBackground = errors.New("pncdsl: scene has no background") ErrSceneMissingBackground = errors.New("inkwell: scene has no background")
) )
+2 -2
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import ( import (
"fmt" "fmt"
@@ -268,7 +268,7 @@ func (g *Game) Validate() error {
} }
} }
if g.activeTheme == "" || !g.ThemeManager.Has(g.activeTheme) { if g.activeTheme == "" || !g.ThemeManager.Has(g.activeTheme) {
return fmt.Errorf("pncdsl: no active theme (got %q)", g.activeTheme) return fmt.Errorf("inkwell: no active theme (got %q)", g.activeTheme)
} }
return nil return nil
} }
+4 -4
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import "sort" import "sort"
@@ -24,10 +24,10 @@ func NewManager[T Named]() *Manager[T] {
func (m *Manager[T]) Register(v T) { func (m *Manager[T]) Register(v T) {
name := v.GetName() name := v.GetName()
if name == "" { if name == "" {
panic("pncdsl: Register: empty Name on " + typeName(v)) panic("inkwell: Register: empty Name on " + typeName(v))
} }
if _, dup := m.items[name]; dup { if _, dup := m.items[name]; dup {
panic("pncdsl: Register: duplicate " + typeName(v) + " name: " + name) panic("inkwell: Register: duplicate " + typeName(v) + " name: " + name)
} }
m.items[name] = v m.items[name] = v
m.order = append(m.order, name) m.order = append(m.order, name)
@@ -41,7 +41,7 @@ func (m *Manager[T]) Get(name string) (T, bool) {
func (m *Manager[T]) MustGet(name string) T { func (m *Manager[T]) MustGet(name string) T {
v, ok := m.items[name] v, ok := m.items[name]
if !ok { if !ok {
panic("pncdsl: MustGet: unknown name: " + name) panic("inkwell: MustGet: unknown name: " + name)
} }
return v return v
} }
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
type Dialogue struct { type Dialogue struct {
Name string Name string
+1 -1
View File
@@ -1,3 +1,3 @@
package pncdsl package inkwell
type DialogueManager = Manager[Dialogue] type DialogueManager = Manager[Dialogue]
+1 -1
View File
@@ -1,4 +1,4 @@
module git.teletypegames.org/games/pncdsl module git.teletypegames.org/games/inkwell
go 1.26.3 go 1.26.3
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import ( import (
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
type Item struct { type Item struct {
Name string Name string
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
type Inventory struct { type Inventory struct {
items []string items []string
+1 -1
View File
@@ -1,3 +1,3 @@
package pncdsl package inkwell
type ItemManager = Manager[Item] type ItemManager = Manager[Item]
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
// Camera is a stub identity transform — no scrolling in this milestone. // Camera is a stub identity transform — no scrolling in this milestone.
type Camera struct { type Camera struct {
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
type Scene struct { type Scene struct {
Name string Name string
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
type CursorKind int type CursorKind int
+1 -1
View File
@@ -1,3 +1,3 @@
package pncdsl package inkwell
type SceneManager = Manager[Scene] type SceneManager = Manager[Scene]
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import "math" import "math"
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import ( import (
"image/color" "image/color"
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
// Trigger fires Do when its When condition first becomes true after the // Trigger fires Do when its When condition first becomes true after the
// trigger arms (on scene enter). Rising-edge semantics: the Do action is // trigger arms (on scene enter). Rising-edge semantics: the Do action is
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
type State struct { type State struct {
flags map[string]bool flags map[string]bool
+8 -8
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import ( import (
"encoding/json" "encoding/json"
@@ -61,15 +61,15 @@ func (g *Game) slotPath(slot int) string {
// dropped — saves capture state at idle/ready boundaries. // dropped — saves capture state at idle/ready boundaries.
func (g *Game) Save(slot int) error { func (g *Game) Save(slot int) error {
if err := os.MkdirAll(g.saveDir(), 0o755); err != nil { if err := os.MkdirAll(g.saveDir(), 0o755); err != nil {
return fmt.Errorf("pncdsl: save mkdir: %w", err) return fmt.Errorf("inkwell: save mkdir: %w", err)
} }
sf := g.buildSave() sf := g.buildSave()
data, err := json.MarshalIndent(sf, "", " ") data, err := json.MarshalIndent(sf, "", " ")
if err != nil { if err != nil {
return fmt.Errorf("pncdsl: save marshal: %w", err) return fmt.Errorf("inkwell: save marshal: %w", err)
} }
if err := os.WriteFile(g.slotPath(slot), data, 0o644); err != nil { if err := os.WriteFile(g.slotPath(slot), data, 0o644); err != nil {
return fmt.Errorf("pncdsl: save write: %w", err) return fmt.Errorf("inkwell: save write: %w", err)
} }
return nil return nil
} }
@@ -81,14 +81,14 @@ func (g *Game) Save(slot int) error {
func (g *Game) Load(slot int) error { func (g *Game) Load(slot int) error {
data, err := os.ReadFile(g.slotPath(slot)) data, err := os.ReadFile(g.slotPath(slot))
if err != nil { if err != nil {
return fmt.Errorf("pncdsl: load read: %w", err) return fmt.Errorf("inkwell: load read: %w", err)
} }
var sf saveFile var sf saveFile
if err := json.Unmarshal(data, &sf); err != nil { if err := json.Unmarshal(data, &sf); err != nil {
return fmt.Errorf("pncdsl: load unmarshal: %w", err) return fmt.Errorf("inkwell: load unmarshal: %w", err)
} }
if sf.Version > saveVersion { if sf.Version > saveVersion {
return fmt.Errorf("pncdsl: save version %d unsupported (max %d)", sf.Version, saveVersion) return fmt.Errorf("inkwell: save version %d unsupported (max %d)", sf.Version, saveVersion)
} }
return g.applySave(&sf) return g.applySave(&sf)
} }
@@ -129,7 +129,7 @@ func (g *Game) applySave(sf *saveFile) error {
return fmt.Errorf("%w: %q (in save)", ErrUnknownScene, sf.CurrentScene) return fmt.Errorf("%w: %q (in save)", ErrUnknownScene, sf.CurrentScene)
} }
if sf.ActiveTheme != "" && !g.ThemeManager.Has(sf.ActiveTheme) { if sf.ActiveTheme != "" && !g.ThemeManager.Has(sf.ActiveTheme) {
return fmt.Errorf("pncdsl: unknown theme %q in save", sf.ActiveTheme) return fmt.Errorf("inkwell: unknown theme %q in save", sf.ActiveTheme)
} }
g.scriptRunner = nil g.scriptRunner = nil
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import ( import (
"fmt" "fmt"
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import ( import (
"image/color" "image/color"
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import ( import (
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
// RegisterDefaultUI installs the SCUMM-style HUD widgets into g.UIManager // RegisterDefaultUI installs the SCUMM-style HUD widgets into g.UIManager
// in the conventional Z-order (HotspotDebug at the back, Cursor on top). // in the conventional Z-order (HotspotDebug at the back, Cursor on top).
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import ( import (
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import ( import (
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import ( import (
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import ( import (
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
// UIManager registers Widget instances. Same shape as every other manager. // UIManager registers Widget instances. Same shape as every other manager.
type UIManager = Manager[Widget] type UIManager = Manager[Widget]
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import ( import (
"image/color" "image/color"
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import ( import (
"image/color" "image/color"
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import "github.com/hajimehoshi/ebiten/v2" import "github.com/hajimehoshi/ebiten/v2"
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import "image/color" import "image/color"
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import "image/color" import "image/color"
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import ( import (
"fmt" "fmt"
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
// Verb is a registered "action word" (Look, Use, Talk, Take, …). The // Verb is a registered "action word" (Look, Use, Talk, Take, …). The
// VerbBar / RadialVerbs widgets read the VerbManager to know which verbs // VerbBar / RadialVerbs widgets read the VerbManager to know which verbs
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import ( import (
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import ( import (
"image/color" "image/color"
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import "github.com/hajimehoshi/ebiten/v2" import "github.com/hajimehoshi/ebiten/v2"
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import "math" import "math"
+2 -2
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
import ( import (
"fmt" "fmt"
@@ -11,5 +11,5 @@ func logf(format string, args ...any) {
if !DebugLog { if !DebugLog {
return return
} }
fmt.Fprintf(os.Stderr, "[pncdsl] "+format+"\n", args...) fmt.Fprintf(os.Stderr, "[inkwell] "+format+"\n", args...)
} }
+1 -1
View File
@@ -1,4 +1,4 @@
package pncdsl package inkwell
// Timer accumulates seconds. Tick advances it; Reset zeroes it. // Timer accumulates seconds. Tick advances it; Reset zeroes it.
type Timer struct { type Timer struct {