Add enable/disable across ECS scopes + @OnEnable/@OnDisable hooks
Three enable/disable statement scopes, each a reversible flag flip:
- `disable P on e` / `enable P on e` — one property on one entity. Clears
the has-flag so queries stop matching; field data persists in storage, so
enable restores it untouched. @OnDisable(P)/@OnEnable(P) handler hooks run
at the toggle point with the property bound by name.
- `disable Model` / `enable Model` — @ME_<Model> global flag; the model's
entities drop out of every query while disabled.
- `disable Handler` / `enable Handler` — @HE_<Handler> global flag; the
handler stops being called each phase while disabled.
Nothing is copied or freed — each toggle is one global store or one has-flag
store. Reduces entirely to existing ECS machinery (has-flags, kind filter,
per-phase call guards), so the data-oriented model is untouched.
New AST node S_TOGGLE; emit_toggle lowers it. Query {Model} filter now ANDs
@ME_; phase calls now guard on @HE_. Parser gains enable/disable statements
and @OnEnable/@OnDisable annotations.
Vocabulary: `on` promoted from RESERVED to CLAUSE (parser now dispatches on
it); enable/disable added as STMT keywords — synced across ludic_syntax.h,
the TextMate grammar, and LudicTokens.kt (check-vocabulary.py clean).
examples/toggle.ludic demonstrates all three scopes (prints 6 0 7 1 0);
test.sh smoke asserts it. Reseeded; C-free fixpoint holds; goldens identical.
Also: stop tracking tools/.idea/ (gitignored).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
bfc868c546
commit
0bded528df
17 changed files with 4772 additions and 3911 deletions
40
LANGUAGE.md
40
LANGUAGE.md
|
|
@ -323,15 +323,46 @@ boot ── @OnStart ─▶ spawn ── @OnAttach(P), @OnSpawn(M) ─▶ …
|
|||
@OnQuit handler Save { save() } # once, at shutdown
|
||||
```
|
||||
|
||||
**Enable / disable.** `enable` and `disable` are statements that flip something on
|
||||
or off without destroying it. There are three scopes:
|
||||
|
||||
- **`disable P on e` / `enable P on e`** — one *property* on one entity. Disabling
|
||||
clears the entity's has-flag, so queries stop matching it, but the field values
|
||||
stay in storage — a later `enable` restores them untouched. `@OnDisable(P)` and
|
||||
`@OnEnable(P)` are handler annotations that run at the toggle point with the
|
||||
property bound by name (like a one-entity `@OnSpawn`).
|
||||
- **`disable Model` / `enable Model`** — a whole *model*. Its entities drop out of
|
||||
every query while disabled; the entities and their data are left alone.
|
||||
- **`disable Handler` / `enable Handler`** — a *handler*. It stops being called
|
||||
each phase while disabled, and resumes on `enable`.
|
||||
|
||||
Each toggle is one global flag flip (or one has-flag store), so nothing is copied
|
||||
or freed — enable/disable is cheap and fully reversible.
|
||||
|
||||
```ludic
|
||||
# doc-check: skip — enable/disable
|
||||
@OnDisable(Shield) handler Down { play("shield_break.wav") }
|
||||
@OnEnable(Shield) handler Up { play("shield_up.wav") }
|
||||
|
||||
disable Shield on self() # this entity loses its shield; data kept for later
|
||||
enable Shield on self() # shield back, amount unchanged
|
||||
disable Gravity # a whole model sits out every query
|
||||
disable AiThink # a handler stops running each phase
|
||||
```
|
||||
|
||||
See [`examples/toggle.ludic`](examples/toggle.ludic) for all three scopes in one
|
||||
frame. Still to come: **`@OnDetach`** (the paired hook for a property leaving,
|
||||
needing the same per-property runtime dispatch as despawn).
|
||||
|
||||
**`@Handles` — the handlers a program drives.** Written in front of the
|
||||
`program`, `@Handles(Move)` names the handlers it uses. It parses and reads as
|
||||
documentation; every declared handler still runs (registration is implicit).
|
||||
|
||||
See [`examples/annotations.ludic`](examples/annotations.ludic) (queries, computed
|
||||
fields, one hook) and [`examples/lifecycle.ludic`](examples/lifecycle.ludic) (the
|
||||
whole timeline). Still to come: **`@OnDetach`** (needs the same runtime kind
|
||||
dispatch as despawn, per property) and **scene** hooks (`@OnEnter`/`@OnExit`),
|
||||
which wait on `scene` support landing in the compiler.
|
||||
whole timeline), plus [`examples/toggle.ludic`](examples/toggle.ludic)
|
||||
(enable/disable). Still to come: **scene** hooks (`@OnEnter`/`@OnExit`), which
|
||||
wait on `scene` support landing in the compiler.
|
||||
|
||||
## Structs, arrays and slices
|
||||
|
||||
|
|
@ -425,7 +456,8 @@ another `.ludic` file (see `examples/lib/`).
|
|||
`let x = expr` · `x = expr` (`+= -= *= /=`) · `if/else` · `when cond { }`
|
||||
(if-without-else) · `while cond { }` · `for i in a .. b { }` (numeric range) ·
|
||||
`for (…) in query […] { }` · `break` · `continue` · `return` · `spawn` ·
|
||||
`despawn` · `match` · `machine`.
|
||||
`despawn` · `enable` / `disable` (a property `on e`, a model, or a handler) ·
|
||||
`match` · `machine`.
|
||||
|
||||
**Statements are separated by a newline or `;`** (both lex to the same separator
|
||||
token). Two statements may not sit adjacent with only spaces between them — the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue