plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
9d0ff39
— Peter John
2026-11-03T21:55:51+05:30
Move the pure and functional scene styles into their own folders
- README.md +7 -7
- docs/plans/2026-11-03-state-view-parity.review.md +5 -10
- docs/plans/2026-11-03-supercompiler.review.md +1 -1
- plum-cli/src/game.rs +31 -4
- plum-examples/{game_state_view.plum → games/functional/game_state_view.plum} +2 -2
- plum-examples/{game_pure_scene.plum → games/pure/game_pure_scene.plum} +5 -5
- plum-game/tests/host_test.rs +2 -2
- plum-game/web-test/fixtures/stress_pure_scene.plum +5 -5
- plum-game/web-test/fixtures/stress_state_view.plum +1 -1
- plum-game/web-test/fixtures/stress_state_view_deep.plum +1 -1
- plum-game/web-test/tests/web_check.rs +1 -1
- plum-std/game/{Picture.plum → functional/Picture.plum} +0 -0
- plum-std/game/{Actions.plum → pure/Actions.plum} +0 -0
- plum-std/game/{PureScene.plum → pure/PureScene.plum} +2 -2
- plum-std/game/{SceneInput.plum → pure/SceneInput.plum} +3 -3
- plum-std/game/{SceneTree.plum → pure/SceneTree.plum} +2 -2
README.md
CHANGED
|
@@ -1377,13 +1377,13 @@ The page calls `init` once after the assets are loaded, then `update` and `draw`
|
|
|
1377
1377
|
|
|
1378
1378
|
### Pure scene graph
|
|
1379
1379
|
|
|
1380
|
-
The pure scene graph is the same kind of tree as `std/game/Scene`, but a function never changes a value it was only given: updates take an owned actor (`own self`, `own` parameters, see [Own records](#own-records)), change it in place and hand it back, so no `clone()` is needed. `Actor` is an `own trait` and `Group`, `Sprite`, `Label`, `Button` and `Stage` are `own record`s. The graph is split into four modules: `std/game/Actions` (timed actions such as `forever(rotateBy(...))`), `std/game/PureScene` (the `Actor` trait and its leaves, `group`, `sprite`, `button`, and the draw walk), `std/game/SceneTree` (update, hit testing, paths and event routing) and `std/game/SceneInput` (`Stage`, pointer capture and `UiEvent`). A game imports them by name:
|
|
1380
|
+
The pure scene graph is the same kind of tree as `std/game/Scene`, but a function never changes a value it was only given: updates take an owned actor (`own self`, `own` parameters, see [Own records](#own-records)), change it in place and hand it back, so no `clone()` is needed. `Actor` is an `own trait` and `Group`, `Sprite`, `Label`, `Button` and `Stage` are `own record`s. The graph is split into four modules: `std/game/pure/Actions` (timed actions such as `forever(rotateBy(...))`), `std/game/pure/PureScene` (the `Actor` trait and its leaves, `group`, `sprite`, `button`, and the draw walk), `std/game/pure/SceneTree` (update, hit testing, paths and event routing) and `std/game/pure/SceneInput` (`Stage`, pointer capture and `UiEvent`). A game imports them by name:
|
|
1381
1381
|
|
|
1382
1382
|
```
|
|
1383
|
-
import std/game/Actions
|
|
1383
|
+
import std/game/pure/Actions
|
|
1384
|
-
import std/game/PureScene
|
|
1384
|
+
import std/game/pure/PureScene
|
|
1385
|
-
import std/game/SceneTree
|
|
1385
|
+
import std/game/pure/SceneTree
|
|
1386
|
-
import std/game/SceneInput
|
|
1386
|
+
import std/game/pure/SceneInput
|
|
1387
1387
|
```
|
|
1388
1388
|
|
|
1389
1389
|
A minimal game holds a `Stage` and replaces it each frame:
|
|
@@ -1411,8 +1411,8 @@ export fun draw(w: World) =
|
|
|
1411
1411
|
|
|
1412
1412
|
- **Immediate mode** ([`game.plum`](plum-examples/game.plum)): one state record and a `draw` that issues the calls every frame. Use it for flat arcade games with a few kinds of thing.
|
|
1413
1413
|
- **Scene** ([`game_scene.plum`](plum-examples/game_scene.plum)): a retained tree of mutable actors. Use it when you want objects that change in place and a familiar `act`/`draw` object model.
|
|
1414
|
-
- **Pure scene** ([`game_pure_scene.plum`](plum-examples/game_pure_scene.plum)): every update takes an owned value and hands it back, pointer clicks arrive as `UiEvent`s, and the rules can be tested without a host. Use it when you want pure updates and tests that run without a game host.
|
|
1415
|
-
- **State/view** ([`game_state_view.plum`](plum-examples/game_state_view.plum)): the game state is plain data and a `view` turns it into `std/game/Picture`, a recursive picture type drawn by recursive functions. Use it when you want to draw from state instead of keeping a tree of actors.
|
|
1414
|
+
- **Pure scene** ([`game_pure_scene.plum`](plum-examples/games/pure/game_pure_scene.plum)): every update takes an owned value and hands it back, pointer clicks arrive as `UiEvent`s, and the rules can be tested without a host. Use it when you want pure updates and tests that run without a game host.
|
|
1415
|
+
- **State/view** ([`game_state_view.plum`](plum-examples/games/functional/game_state_view.plum)): the game state is plain data and a `view` turns it into `std/game/functional/Picture`, a recursive picture type drawn by recursive functions. Use it when you want to draw from state instead of keeping a tree of actors.
|
|
1416
1416
|
|
|
1417
1417
|
**Browser check:** `cargo run --manifest-path plum-game/web-test/Cargo.toml -- plum-examples/game.plum --screenshot /tmp/orb-run.png` builds the static web version, serves it and drives headless Chromium (software WebGPU; `--browser webkit` drives Playwright's WebKit, the Safari engine, instead) through [playwright-rs](https://crates.io/crates/playwright-rs): it fails if the canvas stays blank or stops changing, or the page logs an error, and reports whether audio started. It also takes `--assets DIR`, `--require-audio`, `--measure SECS` (with `--json`, `draws` is the GPU draw calls per frame), `--headed`, `--dpr N` (a device pixel ratio of N), `--lose-device` (destroys the GPU device after a few frames and requires rendering to resume) and `--expect-pixel x,y,r,g,b[,tol]` (repeatable; the screenshot pixel at CSS (x, y)), `--expect-unsupported-webgpu` (hides `navigator.gpu` and requires the page's "WebGPU is not supported" message while plum.wasm still loads) and `--no-module-introspection` (makes `WebAssembly.Module.imports/exports/customSections` throw, as Safari does). `plum-game/web-test/fixtures/` has `interleave.plum`, `text_sizes.plum` and `rendertarget.plum` for these. One-time setup: `cargo run --manifest-path plum-game/web-test/Cargo.toml --example install-browsers`. See [`plum-game/web-test/README.md`](plum-game/web-test/README.md); `docs/game.png` above was made with `--screenshot`. [`plum-game/web-test/BENCHMARK.md`](plum-game/web-test/BENCHMARK.md) records the renderer benchmark that led to this design.
|
|
1418
1418
|
|
docs/plans/2026-11-03-state-view-parity.review.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Functional state/view at feature parity with PureScene: review plan
|
|
2
2
|
|
|
3
|
-
**Builds on:** master
|
|
3
|
+
**Builds on:** master 53de7e3 (pure-scene-enums merged; T7 dropped).
|
|
4
4
|
**Goal (user, 2026-11-03):** the most functional approach, where a frame is a recursive value built from state and walked recursively, with the same features as PureScene. Performance is measured and reported, not chased: matching the mutable scene isn't a goal ("otherwise I would just stick with mutate").
|
|
5
5
|
|
|
6
6
|
## Summary
|
|
@@ -28,7 +28,9 @@ The three scene styles currently share `plum-std/game/`, which is confusing. Eac
|
|
|
28
28
|
| `plum-examples/games/pure/` | `game_pure_scene.plum` | |
|
|
29
29
|
| `plum-examples/games/functional/` | `game_state_view.plum` | |
|
|
30
30
|
|
|
31
|
-
The mutable examples (`game.plum`, `game_scene.plum`, `scene.plum`, `trait_scene.plum`) stay at the top of `plum-examples`.
|
|
31
|
+
The mutable examples (`game.plum`, `game_scene.plum`, `scene.plum`, `trait_scene.plum`) stay at the top of `plum-examples`.
|
|
32
|
+
|
|
33
|
+
**Done before this plan** (user, 2026-11-03: "do that first"), as a separate change on master. This plan builds on the new paths: the new `Anim` and `Ui` go in `plum-std/game/functional/`.
|
|
32
34
|
|
|
33
35
|
## The design
|
|
34
36
|
- **Animations are functions of time, not stepped state.**
|
|
@@ -47,11 +49,6 @@ The mutable examples (`game.plum`, `game_scene.plum`, `scene.plum`, `trait_scene
|
|
|
47
49
|
- capture is dropped when the captured id is no longer in the picture.
|
|
48
50
|
- Button visuals come from the UI state: `Ui.pressed(ui, id)`, `Ui.hovered(ui, id)`. `Ui.button(id, label, rect, ui)` builds the button's `SceneNode`.
|
|
49
51
|
- Events are `UiEvent(id, kind)`, as in PureScene, and the game handles them in `step`.
|
|
50
|
-
- **Static subtree reuse (user, 2026-11-03).** Parts of a picture that never change are built once and returned as the same value every frame. Examples: a starfield's layout, terrain tiles, HUD frames, panel backgrounds.
|
|
51
|
-
- `view` places them as-is (`Group(.., children: Cons(s.staticBg, ...))`), so they cost no allocation per frame. Only the moving parts are rebuilt.
|
|
52
|
-
- Anything that varies over time is applied around the shared subtree, not inside it: a twinkle via `Filter(Alpha(..), child: staticStar)`, a scroll via `Group(Translate(..), child: staticMap)`.
|
|
53
|
-
- Stored in `State` (built in `newState`), or as a module-level constant when it depends on nothing.
|
|
54
|
-
- Test: a wasmparser/allocation count shows the static part adds no `struct.new` per frame. `game_state_view`'s starfield becomes static (positions fixed, twinkle as a per-frame alpha wrapper).
|
|
55
52
|
- **The game shell stays thin.** `update` reads input into a `Msg`, then calls `step`, then `Ui.step` against `view(state)`, then `step` again for each `UiEvent`. Sound remains the only side effect.
|
|
56
53
|
|
|
57
54
|
## What you will get
|
|
@@ -68,12 +65,10 @@ The mutable examples (`game.plum`, `game_scene.plum`, `scene.plum`, `trait_scene
|
|
|
68
65
|
## What will be built
|
|
69
66
|
| Part | What it does | Size |
|
|
70
67
|
|---|---|---|
|
|
71
|
-
| Folders | move pure and functional modules and their example games into their folders; update every import and test path | S |
|
|
72
68
|
| Anim | `enum Anim`, `at`, `length`, poses; tests for zero-length, sequences, parallel and forever without drift | M |
|
|
73
69
|
| Picture tags | `Tagged(id, child)`, `hitId`, draw/bounds pass-through; tests | S |
|
|
74
70
|
| Ui | `UiState`, `Ui.step`, events, capture, `pressed`/`hovered`, `button`; PureScene-equivalent tests | M |
|
|
75
71
|
| Orb Run state/view | restart via Ui, Anim for the title and hit flash; tests unchanged plus Ui/Anim cases | M |
|
|
76
|
-
| Static subtrees | starfield built once in `State`, twinkle as an alpha wrapper; allocation test | S |
|
|
77
72
|
| Stress fixture | tags, buttons, animations in `stress_state_view.plum` | S |
|
|
78
73
|
| Benchmark + README | headed table, "Functional scenes" section | S |
|
|
79
74
|
|
|
@@ -102,10 +97,10 @@ flowchart LR
|
|
|
102
97
|
## Risks
|
|
103
98
|
- **R1: allocation.** `view` rebuilds the tree every frame, and the UI adds a hit test per input. Measured and reported; the supercompiler's view-into-draw fusion is the later lever.
|
|
104
99
|
- **R2: hit test against the view.** `Ui.step` needs `view(state)` for `hitId`. Building it twice per frame (once for input, once for draw) is allowed, measured, and may be shared later.
|
|
105
|
-
- **R4: nested std paths.** No std module lives three levels deep yet (`std/game/pure/X`). If the loader, LSP or `plum game` asset/bundle code assumes two levels, the Folders part fixes that in the same part, with a test.
|
|
106
100
|
- **R3: compiler gaps.** Recursive types and generic `List` code have hit gaps before. Each gap is reported as an incident and fixed in the compiler, not worked around.
|
|
107
101
|
|
|
108
102
|
## Out of scope
|
|
103
|
+
- Static subtree reuse: building unchanging parts of a picture (starfield layout, terrain, HUD frames) once and sharing them every frame. Deferred by the user, 2026-11-03, as a later optimisation.
|
|
109
104
|
- Removing PureScene, SceneTree, SceneInput or Actions (a later decision).
|
|
110
105
|
- Fixed timestep and replays (separate queued plan).
|
|
111
106
|
- The supercompiler.
|
docs/plans/2026-11-03-supercompiler.review.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Supercompiler: fusing List chains and view-into-draw: review plan
|
|
2
2
|
|
|
3
|
-
**Builds on:** master after `2026-11-03-pure-scene-enums` (
|
|
3
|
+
**Builds on:** master after `2026-11-03-pure-scene-enums` (53de7e3) and the folder split (`std/game/pure/`, `std/game/functional/Picture`): closure specialisation, tail calls and TRMC, unboxed variants, std cons `List`, recursive `Picture` and the state/view Orb Run.
|
|
4
4
|
|
|
5
5
|
**Added 2026-11-03 (user):** a uniqueness stage. `own` stays for now. If the compiler can prove for itself that nobody else still holds a value, `own` becomes redundant and can be removed in a later plan. See "Uniqueness without `own`" below.
|
|
6
6
|
|
plum-cli/src/game.rs
CHANGED
|
@@ -20,16 +20,43 @@ fn repoRoot() -> PathBuf {
|
|
|
20
20
|
.to_path_buf()
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
/// The asset root: `--assets`, else `assets` beside the source file
|
|
23
|
+
/// The asset root: `--assets`, else the nearest `assets` directory beside the source file or in
|
|
24
|
+
/// a parent folder (so games in subfolders share one), else `assets` beside the source file.
|
|
24
25
|
fn assetRoot(file: &Path, cli: Option<PathBuf>) -> PathBuf {
|
|
25
26
|
cli.unwrap_or_else(|| {
|
|
27
|
+
let dir = file
|
|
26
|
-
|
|
28
|
+
.parent()
|
|
27
29
|
.filter(|p| !p.as_os_str().is_empty())
|
|
28
|
-
.unwrap_or(Path::new("."))
|
|
30
|
+
.unwrap_or(Path::new("."));
|
|
31
|
+
let abs = dir.canonicalize().unwrap_or_else(|_| dir.to_path_buf());
|
|
32
|
+
for up in abs.ancestors() {
|
|
33
|
+
if up.join("assets").is_dir() {
|
|
34
|
+
return if up == abs { dir.join("assets") } else { up.join("assets") };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
29
|
-
|
|
37
|
+
dir.join("assets")
|
|
30
38
|
})
|
|
31
39
|
}
|
|
32
40
|
|
|
41
|
+
#[cfg(test)]
|
|
42
|
+
mod assetRootTests {
|
|
43
|
+
use super::*;
|
|
44
|
+
|
|
45
|
+
#[test]
|
|
46
|
+
fn finds_assets_in_a_parent_folder() {
|
|
47
|
+
let root = std::env::temp_dir().join(format!("plum_assets_{}", std::process::id()));
|
|
48
|
+
let deep = root.join("games/pure");
|
|
49
|
+
fs::create_dir_all(&deep).unwrap();
|
|
50
|
+
fs::create_dir_all(root.join("assets")).unwrap();
|
|
51
|
+
let found = assetRoot(&deep.join("g.plum"), None);
|
|
52
|
+
assert_eq!(found.canonicalize().unwrap(), root.join("assets").canonicalize().unwrap());
|
|
53
|
+
fs::create_dir_all(deep.join("assets")).unwrap();
|
|
54
|
+
assert_eq!(assetRoot(&deep.join("g.plum"), None), deep.join("assets"));
|
|
55
|
+
assert_eq!(assetRoot(&deep.join("g.plum"), Some("x".into())), PathBuf::from("x"));
|
|
56
|
+
fs::remove_dir_all(&root).ok();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
33
60
|
/// Every file under `dir` (dotfiles skipped), as `/`-separated paths relative to it, sorted.
|
|
34
61
|
fn listFiles(dir: &Path) -> Result<Vec<String>> {
|
|
35
62
|
fn walk(dir: &Path, prefix: &str, out: &mut Vec<String>) -> Result<()> {
|
plum-examples/{game_state_view.plum → games/functional/game_state_view.plum}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Orb Run as state and view (`plum game plum-examples/game_state_view.plum`): the same
|
|
1
|
+
# Orb Run as state and view (`plum game plum-examples/games/functional/game_state_view.plum`): the same
|
|
2
2
|
# game as game_pure_scene.plum (same arena, rules, sprites, sounds, keys and scoring),
|
|
3
3
|
# written the way a Rust state/view program is. Steer the ship with the arrow keys or
|
|
4
4
|
# WASD, collect the drifting orbs and avoid the mines. Three lives; Enter, R or the
|
|
@@ -24,7 +24,7 @@ import std/Math
|
|
|
24
24
|
import std/game/Vec2
|
|
25
25
|
import std/game/Color
|
|
26
26
|
import std/game/Game
|
|
27
|
-
import std/game/Picture
|
|
27
|
+
import std/game/functional/Picture
|
|
28
28
|
|
|
29
29
|
primitive Rules =
|
|
30
30
|
ArenaW = 800.0
|
plum-examples/{game_pure_scene.plum → games/pure/game_pure_scene.plum}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Orb Run on the pure scene graph (`plum game plum-examples/game_pure_scene.plum`): the same
|
|
1
|
+
# Orb Run on the pure scene graph (`plum game plum-examples/games/pure/game_pure_scene.plum`): the same
|
|
2
2
|
# game as game_scene.plum (same arena, rules, sprites, sounds, keys and scoring), but the
|
|
3
3
|
# scene is made of values. Steer the ship with the arrow keys or WASD, collect the
|
|
4
4
|
# drifting orbs and avoid the mines. Three lives; Enter, R or the "Again" button
|
|
@@ -25,10 +25,10 @@ import std/game/Vec2
|
|
|
25
25
|
import std/game/Color
|
|
26
26
|
import std/game/Affine
|
|
27
27
|
import std/game/Game
|
|
28
|
-
import std/game/Actions
|
|
28
|
+
import std/game/pure/Actions
|
|
29
|
-
import std/game/PureScene
|
|
29
|
+
import std/game/pure/PureScene
|
|
30
|
-
import std/game/SceneTree
|
|
30
|
+
import std/game/pure/SceneTree
|
|
31
|
-
import std/game/SceneInput
|
|
31
|
+
import std/game/pure/SceneInput
|
|
32
32
|
|
|
33
33
|
primitive Rules =
|
|
34
34
|
ArenaW = 800.0
|
plum-game/tests/host_test.rs
CHANGED
|
@@ -265,7 +265,7 @@ fn sceneExampleRunsFrames() {
|
|
|
265
265
|
|
|
266
266
|
#[test]
|
|
267
267
|
fn pureSceneExampleRunsFrames() {
|
|
268
|
-
let wasm = compile(&root().join("plum-examples/game_pure_scene.plum"));
|
|
268
|
+
let wasm = compile(&root().join("plum-examples/games/pure/game_pure_scene.plum"));
|
|
269
269
|
let mut g = PlumGame::load(&wasm, Recorder::with_assets(examples_assets())).expect("load game");
|
|
270
270
|
g.init().expect("init");
|
|
271
271
|
for _ in 0..10 {
|
|
@@ -284,7 +284,7 @@ fn pureSceneMatchesSceneDrawCounts() {
|
|
|
284
284
|
g
|
|
285
285
|
};
|
|
286
286
|
let mut a = load("plum-examples/game_scene.plum");
|
|
287
|
-
let mut b = load("plum-examples/game_pure_scene.plum");
|
|
287
|
+
let mut b = load("plum-examples/games/pure/game_pure_scene.plum");
|
|
288
288
|
let tex_ids = |calls: &[String]| {
|
|
289
289
|
let mut v: Vec<String> = calls
|
|
290
290
|
.iter()
|
plum-game/web-test/fixtures/stress_pure_scene.plum
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# The stress scene of stress_scene.plum on the PURE scene graph (std/game/PureScene):
|
|
1
|
+
# The stress scene of stress_scene.plum on the PURE scene graph (std/game/pure/PureScene):
|
|
2
2
|
# 5000 sprites in 50 slowly rotating groups, 500 rects and 200 circles (sprites of a
|
|
3
3
|
# 1x1 white texture), 50 texts with a changing number, drawn through a camera. Every
|
|
4
4
|
# node is an own value: each frame changes the tree in place with SceneTree.update. Group and
|
|
@@ -13,10 +13,10 @@ import std/game/Vec2
|
|
|
13
13
|
import std/game/Color
|
|
14
14
|
import std/game/Affine
|
|
15
15
|
import std/game/Game
|
|
16
|
-
import std/game/Actions
|
|
16
|
+
import std/game/pure/Actions
|
|
17
|
-
import std/game/PureScene
|
|
17
|
+
import std/game/pure/PureScene
|
|
18
|
-
import std/game/SceneTree
|
|
18
|
+
import std/game/pure/SceneTree
|
|
19
|
-
import std/game/SceneInput
|
|
19
|
+
import std/game/pure/SceneInput
|
|
20
20
|
|
|
21
21
|
primitive Counts =
|
|
22
22
|
Sprites = 5000
|
plum-game/web-test/fixtures/stress_state_view.plum
CHANGED
|
@@ -15,7 +15,7 @@ import std/List
|
|
|
15
15
|
import std/game/Vec2
|
|
16
16
|
import std/game/Color
|
|
17
17
|
import std/game/Game
|
|
18
|
-
import std/game/Picture
|
|
18
|
+
import std/game/functional/Picture
|
|
19
19
|
|
|
20
20
|
primitive Counts =
|
|
21
21
|
Sprites = 5000
|
plum-game/web-test/fixtures/stress_state_view_deep.plum
CHANGED
|
@@ -17,7 +17,7 @@ import std/List
|
|
|
17
17
|
import std/game/Vec2
|
|
18
18
|
import std/game/Color
|
|
19
19
|
import std/game/Game
|
|
20
|
-
import std/game/Picture
|
|
20
|
+
import std/game/functional/Picture
|
|
21
21
|
|
|
22
22
|
primitive Counts =
|
|
23
23
|
Sprites = 5000
|
plum-game/web-test/tests/web_check.rs
CHANGED
|
@@ -12,7 +12,7 @@ fn orb_run_scene_renders_and_animates() {
|
|
|
12
12
|
|
|
13
13
|
#[test]
|
|
14
14
|
fn pure_scene_renders_and_animates() {
|
|
15
|
-
orb_run("plum-examples/game_pure_scene.plum");
|
|
15
|
+
orb_run("plum-examples/games/pure/game_pure_scene.plum");
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
#[test]
|
plum-std/game/{Picture.plum → functional/Picture.plum}
RENAMED
|
File without changes
|
plum-std/game/{Actions.plum → pure/Actions.plum}
RENAMED
|
File without changes
|
plum-std/game/{PureScene.plum → pure/PureScene.plum}
RENAMED
|
@@ -8,14 +8,14 @@ import std/game/Vec2
|
|
|
8
8
|
import std/game/Color
|
|
9
9
|
import std/game/Affine
|
|
10
10
|
import std/game/Game
|
|
11
|
-
import std/game/Actions
|
|
11
|
+
import std/game/pure/Actions
|
|
12
12
|
|
|
13
13
|
# A pure scene graph: a node is an own record (`Node`) holding a `Transform` and
|
|
14
14
|
# an `enum Kind`. Group, Sprite, Label and Button are built in; games add their
|
|
15
15
|
# own behaviour with `Custom(actor: Actor)`, a behaviour-only trait their records
|
|
16
16
|
# embed (like Rust's `Box<dyn Actor>`). `onPointer` takes the node, changes it in
|
|
17
17
|
# place and hands it back; readers borrow. Only `drawTree` talks to the host,
|
|
18
|
-
# through `std/game/Game`. Actions and tree-wide updates live in std/game/SceneTree.
|
|
18
|
+
# through `std/game/Game`. Actions and tree-wide updates live in std/game/pure/SceneTree.
|
|
19
19
|
|
|
20
20
|
enum Touchable =
|
|
21
21
|
| Enabled
|
plum-std/game/{SceneInput.plum → pure/SceneInput.plum}
RENAMED
|
@@ -7,9 +7,9 @@ import std/game/Vec2
|
|
|
7
7
|
import std/game/Color
|
|
8
8
|
import std/game/Affine
|
|
9
9
|
import std/game/Game
|
|
10
|
-
import std/game/Actions
|
|
10
|
+
import std/game/pure/Actions
|
|
11
|
-
import std/game/PureScene
|
|
11
|
+
import std/game/pure/PureScene
|
|
12
|
-
import std/game/SceneTree
|
|
12
|
+
import std/game/pure/SceneTree
|
|
13
13
|
|
|
14
14
|
# The root of a pure scene tree, its camera and pointer capture. A Stage is an
|
|
15
15
|
# own value: `input` and `frame` take it, change it in place and hand it back
|
plum-std/game/{SceneTree.plum → pure/SceneTree.plum}
RENAMED
|
@@ -5,8 +5,8 @@ import std/Slice
|
|
|
5
5
|
import std/Str
|
|
6
6
|
import std/game/Vec2
|
|
7
7
|
import std/game/Affine
|
|
8
|
-
import std/game/Actions
|
|
8
|
+
import std/game/pure/Actions
|
|
9
|
-
import std/game/PureScene
|
|
9
|
+
import std/game/pure/PureScene
|
|
10
10
|
|
|
11
11
|
fun treeActorOf(n: Node) -> Actor? =
|
|
12
12
|
match n.kind
|