rename pncdsl to inkwell
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# pncdsl — reference manual
|
||||
# inkwell — reference manual
|
||||
|
||||
A point-and-click adventure game framework for Go, built on top of
|
||||
[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
|
||||
content.
|
||||
|
||||
This document is the full library reference. The companion `pncdsl-demo`
|
||||
repo at <ssh://git@git.teletypegames.org:2222/games/pncdsl-demo> is a small
|
||||
This document is the full library reference. The companion `inkwell-demo`
|
||||
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
|
||||
plain Go module.
|
||||
|
||||
**Module path:** `git.teletypegames.org/games/pncdsl`
|
||||
**Module path:** `git.teletypegames.org/games/inkwell`
|
||||
|
||||
```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
|
||||
|
||||
`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
|
||||
adventure genre as it existed in the LucasArts SCUMM era (Maniac Mansion,
|
||||
Monkey Island, Day of the Tentacle):
|
||||
@@ -90,7 +90,7 @@ items, scenes, characters, dialogues, scripts, assets, verbs, widgets and
|
||||
themes alike.
|
||||
|
||||
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.
|
||||
|
||||
---
|
||||
@@ -104,7 +104,7 @@ context. Ebitengine handles windowing and the render loop.
|
||||
|
||||
```bash
|
||||
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
|
||||
@@ -123,31 +123,31 @@ package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"git.teletypegames.org/games/pncdsl"
|
||||
"git.teletypegames.org/games/inkwell"
|
||||
)
|
||||
|
||||
func main() {
|
||||
g := pncdsl.NewGame("Sample", 320, 200)
|
||||
g := inkwell.NewGame("Sample", 320, 200)
|
||||
|
||||
g.AssetManager.Register(pncdsl.Asset{
|
||||
Name: "bg/room", Path: "assets/bg/room.png", Kind: pncdsl.AssetImage,
|
||||
g.AssetManager.Register(inkwell.Asset{
|
||||
Name: "bg/room", Path: "assets/bg/room.png", Kind: inkwell.AssetImage,
|
||||
})
|
||||
g.CharacterManager.Register(pncdsl.Character{Name: "player", W: 28, H: 62})
|
||||
g.SceneManager.Register(pncdsl.Scene{
|
||||
g.CharacterManager.Register(inkwell.Character{Name: "player", W: 28, H: 62})
|
||||
g.SceneManager.Register(inkwell.Scene{
|
||||
Name: "room",
|
||||
Background: "bg/room",
|
||||
Actors: []pncdsl.SceneActor{{CharacterName: "player", At: pncdsl.Point{X: 160, Y: 140}}},
|
||||
Hotspots: []pncdsl.Hotspot{{
|
||||
Actors: []inkwell.SceneActor{{CharacterName: "player", At: inkwell.Point{X: 160, Y: 140}}},
|
||||
Hotspots: []inkwell.Hotspot{{
|
||||
Name: "door",
|
||||
Area: pncdsl.Rect(260, 50, 40, 90),
|
||||
Area: inkwell.Rect(260, 50, 40, 90),
|
||||
Label: "door",
|
||||
OnLook: pncdsl.Say("player", "A wooden door."),
|
||||
OnLook: inkwell.Say("player", "A wooden door."),
|
||||
}},
|
||||
})
|
||||
|
||||
g.StartAt("room")
|
||||
pncdsl.RegisterDefaultUI(g) // optional; auto-called by Run if no widgets registered
|
||||
if err := pncdsl.Run(g); err != nil {
|
||||
inkwell.RegisterDefaultUI(g) // optional; auto-called by Run if no widgets registered
|
||||
if err := inkwell.Run(g); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -160,7 +160,7 @@ func main() {
|
||||
Every name-addressable entity goes through one shared generic type:
|
||||
|
||||
```go
|
||||
// pncdsl/core.manager.go
|
||||
// inkwell/core.manager.go
|
||||
|
||||
type Named interface {
|
||||
GetName() string
|
||||
@@ -220,7 +220,7 @@ shadowed entities at runtime.
|
||||
## 4. The Game aggregate
|
||||
|
||||
```go
|
||||
// pncdsl/core.game.go
|
||||
// inkwell/core.game.go
|
||||
|
||||
type Game struct {
|
||||
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) OnStart(a Action) *Game // action run after the scene's OnEnter
|
||||
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`.
|
||||
@@ -350,7 +350,7 @@ library can swap to `text/v2` later without changing call sites.
|
||||
## 5. Geometry primitives
|
||||
|
||||
```go
|
||||
// pncdsl/util.geometry.go
|
||||
// inkwell/util.geometry.go
|
||||
|
||||
type Point struct{ X, Y float64 }
|
||||
|
||||
@@ -388,7 +388,7 @@ each frame.
|
||||
### 6.1 Asset
|
||||
|
||||
```go
|
||||
// pncdsl/asset.def.go
|
||||
// inkwell/asset.def.go
|
||||
|
||||
type AssetKind int
|
||||
const (
|
||||
@@ -410,17 +410,17 @@ deterministic colored placeholder so the game runs without any art —
|
||||
useful for prototyping or CI.
|
||||
|
||||
```go
|
||||
g.AssetManager.Register(pncdsl.Asset{
|
||||
g.AssetManager.Register(inkwell.Asset{
|
||||
Name: "bg/kitchen",
|
||||
Path: "assets/bg/kitchen.png",
|
||||
Kind: pncdsl.AssetImage,
|
||||
Kind: inkwell.AssetImage,
|
||||
})
|
||||
```
|
||||
|
||||
### 6.2 Scene
|
||||
|
||||
```go
|
||||
// pncdsl/scene.def.go
|
||||
// inkwell/scene.def.go
|
||||
|
||||
type Scene struct {
|
||||
Name string
|
||||
@@ -458,7 +458,7 @@ engine samples each trigger once per idle frame; see [§6.4](#64-trigger).
|
||||
### 6.3 Hotspot
|
||||
|
||||
```go
|
||||
// pncdsl/scene.hotspot.go
|
||||
// inkwell/scene.hotspot.go
|
||||
|
||||
type Hotspot struct {
|
||||
Name string
|
||||
@@ -500,7 +500,7 @@ for custom verbs.
|
||||
### 6.4 Trigger
|
||||
|
||||
```go
|
||||
// pncdsl/scene.trigger.go
|
||||
// inkwell/scene.trigger.go
|
||||
|
||||
type Trigger struct {
|
||||
Name string
|
||||
@@ -525,7 +525,7 @@ re-entry.
|
||||
### 6.5 Item
|
||||
|
||||
```go
|
||||
// pncdsl/item.def.go
|
||||
// inkwell/item.def.go
|
||||
|
||||
type Item struct {
|
||||
Name string
|
||||
@@ -548,7 +548,7 @@ hotspot with an item selected resolves the action via, in order:
|
||||
### 6.6 Inventory
|
||||
|
||||
```go
|
||||
// pncdsl/item.inventory.go
|
||||
// inkwell/item.inventory.go
|
||||
|
||||
type Inventory struct{ /* unexported */ }
|
||||
|
||||
@@ -567,7 +567,7 @@ Not a manager — pure runtime state owned by `Game`. Mutated by actions
|
||||
### 6.7 Character
|
||||
|
||||
```go
|
||||
// pncdsl/actor.def.go
|
||||
// inkwell/actor.def.go
|
||||
|
||||
type Character struct {
|
||||
Name string
|
||||
@@ -608,7 +608,7 @@ computed by [walkbox routing](#62-scene).
|
||||
### 6.8 Dialogue
|
||||
|
||||
```go
|
||||
// pncdsl/dialog.def.go
|
||||
// inkwell/dialog.def.go
|
||||
|
||||
type Dialogue struct {
|
||||
Name string
|
||||
@@ -650,7 +650,7 @@ The dialog flow:
|
||||
### 6.9 Script
|
||||
|
||||
```go
|
||||
// pncdsl/action.script.go
|
||||
// inkwell/action.script.go
|
||||
|
||||
type Script struct {
|
||||
Name string
|
||||
@@ -665,7 +665,7 @@ Fire one with `RunScript("name")`.
|
||||
### 6.10 Verb
|
||||
|
||||
```go
|
||||
// pncdsl/ui.verb.go
|
||||
// inkwell/ui.verb.go
|
||||
|
||||
type Verb struct {
|
||||
Name string // canonical id, e.g. "look"
|
||||
@@ -678,9 +678,9 @@ type Verb struct {
|
||||
`take`). Add your own:
|
||||
|
||||
```go
|
||||
g.VerbManager.Register(pncdsl.Verb{
|
||||
g.VerbManager.Register(inkwell.Verb{
|
||||
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
|
||||
|
||||
```go
|
||||
// pncdsl/action.def.go
|
||||
// inkwell/action.def.go
|
||||
|
||||
type Action interface {
|
||||
Start() Runner
|
||||
@@ -794,10 +794,10 @@ func (r *fadeRunner) Tick(ctx *Ctx) Status {
|
||||
For one-shot mutations, `Custom` is shorter:
|
||||
|
||||
```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)
|
||||
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
|
||||
|
||||
```go
|
||||
// pncdsl/action.condition.go
|
||||
// inkwell/action.condition.go
|
||||
|
||||
type Condition interface {
|
||||
Eval(ctx *Ctx) bool
|
||||
@@ -832,7 +832,7 @@ Conditions are stateless — they are evaluated lazily, including inside
|
||||
## 9. World state
|
||||
|
||||
```go
|
||||
// pncdsl/state.def.go
|
||||
// inkwell/state.def.go
|
||||
|
||||
type State struct{ /* unexported */ }
|
||||
|
||||
@@ -872,7 +872,7 @@ hotbars — without touching the library.
|
||||
### 10.1 Widget interface
|
||||
|
||||
```go
|
||||
// pncdsl/ui.widget.go
|
||||
// inkwell/ui.widget.go
|
||||
|
||||
type Widget interface {
|
||||
Named
|
||||
@@ -922,7 +922,7 @@ that frame.
|
||||
|
||||
### 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.
|
||||
|
||||
#### `Panel` (`ui.panel.go`)
|
||||
@@ -1184,7 +1184,7 @@ zones for the radial menu.
|
||||
### 10.5 Registration helpers
|
||||
|
||||
```go
|
||||
// pncdsl/ui.defaults.go
|
||||
// inkwell/ui.defaults.go
|
||||
|
||||
func RegisterDefaultUI(g *Game) // SCUMM-style verb bar + inventory
|
||||
func RegisterRadialVerbUI(g *Game) // verb-coin (right-click) + wider inventory
|
||||
@@ -1205,18 +1205,18 @@ Ebitengine primitives, and you're done.
|
||||
```go
|
||||
type Minimap struct {
|
||||
Name string
|
||||
Bounds pncdsl.Rectangle
|
||||
Bounds inkwell.Rectangle
|
||||
}
|
||||
|
||||
func (m *Minimap) GetName() string { return m.Name }
|
||||
func (m *Minimap) Tick(ctx *pncdsl.UICtx) {}
|
||||
func (m *Minimap) Draw(dst *ebiten.Image, ctx *pncdsl.UICtx) {
|
||||
func (m *Minimap) Tick(ctx *inkwell.UICtx) {}
|
||||
func (m *Minimap) Draw(dst *ebiten.Image, ctx *inkwell.UICtx) {
|
||||
// ... render scene thumbnail, mark NPCs, etc.
|
||||
}
|
||||
|
||||
g.UIManager.Register(&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
|
||||
|
||||
```go
|
||||
// pncdsl/ui.theme.go
|
||||
// inkwell/ui.theme.go
|
||||
|
||||
type Theme struct {
|
||||
Name string
|
||||
@@ -1296,7 +1296,7 @@ type Theme struct {
|
||||
Register your own:
|
||||
|
||||
```go
|
||||
g.ThemeManager.Register(pncdsl.Theme{
|
||||
g.ThemeManager.Register(inkwell.Theme{
|
||||
Name: "midnight-noir",
|
||||
PanelBG: color.RGBA{6, 6, 12, 255},
|
||||
StatusText: color.White,
|
||||
@@ -1344,7 +1344,7 @@ same registry but are not loaded by the library at this milestone.
|
||||
## 13. Audio
|
||||
|
||||
```go
|
||||
// pncdsl/asset.audio.go
|
||||
// inkwell/asset.audio.go
|
||||
|
||||
type AudioPlayer struct{ /* unexported */ }
|
||||
|
||||
@@ -1381,7 +1381,7 @@ methods, so a domain authored against the original stub still works.
|
||||
## 14. Input
|
||||
|
||||
```go
|
||||
// pncdsl/input.def.go
|
||||
// inkwell/input.def.go
|
||||
|
||||
type Input struct{ /* unexported */ }
|
||||
|
||||
@@ -1406,7 +1406,7 @@ directly.
|
||||
## 15. The game loop
|
||||
|
||||
```go
|
||||
// pncdsl/core.dsl.go
|
||||
// inkwell/core.dsl.go
|
||||
|
||||
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
|
||||
|
||||
```go
|
||||
// pncdsl/scene.transition.go (unexported)
|
||||
// inkwell/scene.transition.go (unexported)
|
||||
|
||||
type transition struct{ /* unexported */ }
|
||||
```
|
||||
@@ -1541,7 +1541,7 @@ eagerly at `Register` time via panic.
|
||||
## 18. Save and load
|
||||
|
||||
```go
|
||||
// pncdsl/state.save.go
|
||||
// inkwell/state.save.go
|
||||
|
||||
func (g *Game) Save(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
|
||||
|
||||
```go
|
||||
// pncdsl/core.errors.go
|
||||
// inkwell/core.errors.go
|
||||
|
||||
var (
|
||||
ErrUnknownAsset = errors.New("pncdsl: unknown asset")
|
||||
ErrUnknownScene = errors.New("pncdsl: unknown scene")
|
||||
ErrUnknownItem = errors.New("pncdsl: unknown item")
|
||||
ErrUnknownCharacter = errors.New("pncdsl: unknown character")
|
||||
ErrUnknownDialogue = errors.New("pncdsl: unknown dialogue")
|
||||
ErrUnknownDialogueNode = errors.New("pncdsl: unknown dialogue node")
|
||||
ErrUnknownScript = errors.New("pncdsl: unknown script")
|
||||
ErrUnknownVerb = errors.New("pncdsl: unknown verb")
|
||||
ErrDuplicateName = errors.New("pncdsl: duplicate name")
|
||||
ErrNoStartScene = errors.New("pncdsl: StartAt not set or unknown scene")
|
||||
ErrSceneMissingBackground = errors.New("pncdsl: scene has no background")
|
||||
ErrUnknownAsset = errors.New("inkwell: unknown asset")
|
||||
ErrUnknownScene = errors.New("inkwell: unknown scene")
|
||||
ErrUnknownItem = errors.New("inkwell: unknown item")
|
||||
ErrUnknownCharacter = errors.New("inkwell: unknown character")
|
||||
ErrUnknownDialogue = errors.New("inkwell: unknown dialogue")
|
||||
ErrUnknownDialogueNode = errors.New("inkwell: unknown dialogue node")
|
||||
ErrUnknownScript = errors.New("inkwell: unknown script")
|
||||
ErrUnknownVerb = errors.New("inkwell: unknown verb")
|
||||
ErrDuplicateName = errors.New("inkwell: duplicate name")
|
||||
ErrNoStartScene = errors.New("inkwell: StartAt not set or unknown scene")
|
||||
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
|
||||
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:
|
||||
|
||||
```go
|
||||
@@ -1639,8 +1639,8 @@ of the demo for the time being.
|
||||
### 20.1 Useful runtime debug switches
|
||||
|
||||
```go
|
||||
pncdsl.DebugLog = true // logf output to stderr (script queue, audio, scene change)
|
||||
&pncdsl.HotspotDebug{Enabled: true} // start with the F1 overlay on
|
||||
inkwell.DebugLog = true // logf output to stderr (script queue, audio, scene change)
|
||||
&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.
|
||||
|
||||
```
|
||||
pncdsl/ # module git.teletypegames.org/games/pncdsl
|
||||
inkwell/ # module git.teletypegames.org/games/inkwell
|
||||
├── go.mod
|
||||
├── go.sum
|
||||
│
|
||||
@@ -1725,9 +1725,9 @@ pncdsl/ # module git.teletypegames.org/games/pncdsl
|
||||
└── README.md # this file
|
||||
```
|
||||
|
||||
The companion demo project (`pncdsl-demo`) lives in its own repo at
|
||||
<ssh://git@git.teletypegames.org:2222/games/pncdsl-demo>. It consumes this
|
||||
package via `require git.teletypegames.org/games/pncdsl …` in its `go.mod` —
|
||||
The companion demo project (`inkwell-demo`) lives in its own repo at
|
||||
<ssh://git@git.teletypegames.org:2222/games/inkwell-demo>. It consumes this
|
||||
package via `require git.teletypegames.org/games/inkwell …` in its `go.mod` —
|
||||
no in-tree coupling.
|
||||
|
||||
---
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
type Condition interface {
|
||||
Eval(ctx *Ctx) bool
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
// Status is the outcome of one tick of a Runner.
|
||||
type Status int
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
type ScriptManager = Manager[Script]
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
type Script struct {
|
||||
Name string
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import "image"
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import "image/color"
|
||||
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
type CharacterManager = Manager[Character]
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
type AssetKind int
|
||||
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
// //go:embed assets
|
||||
// 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
|
||||
// and relative (e.g. "assets/bg/bedroom.png") — the same form that
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import (
|
||||
"hash/fnv"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
+2
-2
@@ -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
|
||||
// literals into per-entity *Manager registries hanging off the *Game root.
|
||||
//
|
||||
// See PLAN.md in the repo root for the full design notes.
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
+12
-12
@@ -1,17 +1,17 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrUnknownAsset = errors.New("pncdsl: unknown asset")
|
||||
ErrUnknownScene = errors.New("pncdsl: unknown scene")
|
||||
ErrUnknownItem = errors.New("pncdsl: unknown item")
|
||||
ErrUnknownCharacter = errors.New("pncdsl: unknown character")
|
||||
ErrUnknownDialogue = errors.New("pncdsl: unknown dialogue")
|
||||
ErrUnknownDialogueNode = errors.New("pncdsl: unknown dialogue node")
|
||||
ErrUnknownScript = errors.New("pncdsl: unknown script")
|
||||
ErrUnknownVerb = errors.New("pncdsl: unknown verb")
|
||||
ErrDuplicateName = errors.New("pncdsl: duplicate name")
|
||||
ErrNoStartScene = errors.New("pncdsl: StartAt not set or unknown scene")
|
||||
ErrSceneMissingBackground = errors.New("pncdsl: scene has no background")
|
||||
ErrUnknownAsset = errors.New("inkwell: unknown asset")
|
||||
ErrUnknownScene = errors.New("inkwell: unknown scene")
|
||||
ErrUnknownItem = errors.New("inkwell: unknown item")
|
||||
ErrUnknownCharacter = errors.New("inkwell: unknown character")
|
||||
ErrUnknownDialogue = errors.New("inkwell: unknown dialogue")
|
||||
ErrUnknownDialogueNode = errors.New("inkwell: unknown dialogue node")
|
||||
ErrUnknownScript = errors.New("inkwell: unknown script")
|
||||
ErrUnknownVerb = errors.New("inkwell: unknown verb")
|
||||
ErrDuplicateName = errors.New("inkwell: duplicate name")
|
||||
ErrNoStartScene = errors.New("inkwell: StartAt not set or unknown scene")
|
||||
ErrSceneMissingBackground = errors.New("inkwell: scene has no background")
|
||||
)
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -268,7 +268,7 @@ func (g *Game) Validate() error {
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import "sort"
|
||||
|
||||
@@ -24,10 +24,10 @@ func NewManager[T Named]() *Manager[T] {
|
||||
func (m *Manager[T]) Register(v T) {
|
||||
name := v.GetName()
|
||||
if name == "" {
|
||||
panic("pncdsl: Register: empty Name on " + typeName(v))
|
||||
panic("inkwell: Register: empty Name on " + typeName(v))
|
||||
}
|
||||
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.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 {
|
||||
v, ok := m.items[name]
|
||||
if !ok {
|
||||
panic("pncdsl: MustGet: unknown name: " + name)
|
||||
panic("inkwell: MustGet: unknown name: " + name)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
type Dialogue struct {
|
||||
Name string
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
type DialogueManager = Manager[Dialogue]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module git.teletypegames.org/games/pncdsl
|
||||
module git.teletypegames.org/games/inkwell
|
||||
|
||||
go 1.26.3
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
type Item struct {
|
||||
Name string
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
type Inventory struct {
|
||||
items []string
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
type ItemManager = Manager[Item]
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
// Camera is a stub identity transform — no scrolling in this milestone.
|
||||
type Camera struct {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
type Scene struct {
|
||||
Name string
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
type CursorKind int
|
||||
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
type SceneManager = Manager[Scene]
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import "math"
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
// Trigger fires Do when its When condition first becomes true after the
|
||||
// trigger arms (on scene enter). Rising-edge semantics: the Do action is
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
type State struct {
|
||||
flags map[string]bool
|
||||
|
||||
+8
-8
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -61,15 +61,15 @@ func (g *Game) slotPath(slot int) string {
|
||||
// dropped — saves capture state at idle/ready boundaries.
|
||||
func (g *Game) Save(slot int) error {
|
||||
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()
|
||||
data, err := json.MarshalIndent(sf, "", " ")
|
||||
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 {
|
||||
return fmt.Errorf("pncdsl: save write: %w", err)
|
||||
return fmt.Errorf("inkwell: save write: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -81,14 +81,14 @@ func (g *Game) Save(slot int) error {
|
||||
func (g *Game) Load(slot int) error {
|
||||
data, err := os.ReadFile(g.slotPath(slot))
|
||||
if err != nil {
|
||||
return fmt.Errorf("pncdsl: load read: %w", err)
|
||||
return fmt.Errorf("inkwell: load read: %w", err)
|
||||
}
|
||||
var sf saveFile
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
@@ -129,7 +129,7 @@ func (g *Game) applySave(sf *saveFile) error {
|
||||
return fmt.Errorf("%w: %q (in save)", ErrUnknownScene, sf.CurrentScene)
|
||||
}
|
||||
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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
// RegisterDefaultUI installs the SCUMM-style HUD widgets into g.UIManager
|
||||
// in the conventional Z-order (HotspotDebug at the back, Cursor on top).
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
// UIManager registers Widget instances. Same shape as every other manager.
|
||||
type UIManager = Manager[Widget]
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import "github.com/hajimehoshi/ebiten/v2"
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import "image/color"
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import "image/color"
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
// Verb is a registered "action word" (Look, Use, Talk, Take, …). The
|
||||
// VerbBar / RadialVerbs widgets read the VerbManager to know which verbs
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import "github.com/hajimehoshi/ebiten/v2"
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import "math"
|
||||
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -11,5 +11,5 @@ func logf(format string, args ...any) {
|
||||
if !DebugLog {
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "[pncdsl] "+format+"\n", args...)
|
||||
fmt.Fprintf(os.Stderr, "[inkwell] "+format+"\n", args...)
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package pncdsl
|
||||
package inkwell
|
||||
|
||||
// Timer accumulates seconds. Tick advances it; Reset zeroes it.
|
||||
type Timer struct {
|
||||
|
||||
Reference in New Issue
Block a user