diff --git a/Behaviors.md b/Behaviors.md index ca2f36c..2bb4ffb 100644 --- a/Behaviors.md +++ b/Behaviors.md @@ -156,10 +156,13 @@ playing use the same two words. `Select` (`true` if the condition holds, else `false` — the same action with one of two values, such as a walk or an idle animation for one `Play animation`; `Branch` is for two different actions), lists `List` (a constant), `Fill` (one value repeated — how an empty board is made), `Count`, - `Item at`, `Set item` (a *new* list, the one that came in is left alone), `Add item` (a new + `Item at`, `Set item` (a *new* list, the one that came in is left alone; with a **Type** it + sets only a struct with exactly that struct asset's fields, like `Add item`), `Add item` (a new list with `value` as exactly one more item: a struct or a list goes in whole, where `Join lists` would take it apart; at the end, or before `index`, and an index outside the list appends; - the item takes the kind the list's items share, like `Set item`), `Sub list`, + the item takes the kind the list's items share, like `Set item`; with a **Type** it adds only a + struct with exactly that struct asset's fields and reports anything else in the console, see + [Structs](Structs)), `Sub list`, `Join lists`, `Index of` (by identity where the items are objects), structs `Pack`/`Expand`, `Length`, `Distance`, `Normalize`, `Axis input` (four keys → a normalized direction, so diagonals are not faster), `Compare` (two numbers count as equal up to diff --git a/Development.md b/Development.md index 0b53a28..8fa103c 100644 --- a/Development.md +++ b/Development.md @@ -15,7 +15,7 @@ npm run build # writes dist/ and regenerates index.html php tools/make-user.php [admin|user] # an account; every tool signs in (see Accounts) php tests/php/accounts.test.php # the API's account rules, no server needed (CI's gate job) php tests/php/snapshots.test.php # the snapshot store (#231), the same way -node tools/seed-e2e.mjs && node tools/e2e.mjs # 1500 editor checks, screenshots in /tmp/gm-e2e-*.png +node tools/seed-e2e.mjs && node tools/e2e.mjs # 1513 editor checks, screenshots in /tmp/gm-e2e-*.png GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step ./e2e.sh start [GM_STEPS] # the same run detached, see below ./e2e.sh status # live progress, or the last run's result diff --git a/Structs.md b/Structs.md index b68251a..2b70198 100644 --- a/Structs.md +++ b/Structs.md @@ -61,23 +61,63 @@ takes all of it back: This also works through several levels: if `Chest` holds a `Loot` and a variable holds a `Chest`, a field renamed on `Loot` is renamed inside that variable's value too. +## Typed lists + +A list says nothing about its items by default: each item carries its own kind, and a struct item +its own fields. Such a list is **mixed**. A list can also be **typed**: every item is then one +struct asset. + +Every list variable, and every list field inside a struct, has an **Items** row under its head. It +reads **Mixed** until you pick a type. Its menu offers: + +- **Mixed** — the list declares nothing again. Its items keep their type each (see below), so + nothing is lost. +- **every struct asset** — every item takes on that asset's fields, keeping what it holds under the + same name and taking the default for the rest, exactly like a struct given a type. A list that + holds **anything but structs** (a number, a text, a list) cannot take a type: the edit is refused + and the console says how many items are in the way. Placed objects count too — an override + holding a number refuses the type as well. +- **Make struct type** — makes a new struct asset out of the list's items and types the list with + it. The asset gets **every field any item has**, at the value of the first item that has it, so + no item loses a field. + +In a typed list: + +- **+ Item** adds an item of the type at its defaults straight away. There is no kind to pick. +- The items' fields are the type's: their names cannot be changed, and there is no **+ Field** and + no remove button on a field. The **values** are edited as before. +- A list field of the type is typed through it: if `Bag` has a list `inside` of `Loot`, the `inside` + of every `Bag` item is a typed list of `Loot` too. + +### A type for one item + +In a **mixed** list, every struct item has a **Type** row of its own, as does a struct field inside +such an item. Pick a struct asset there, and that one item takes on its fields and follows it, while +the other items stay as they are. **Own fields** turns it back. + +### When the asset changes + +Typed items follow their asset like every other place of that type — the table above applies to +them too: a field added, removed or renamed reaches every typed item, in the list's own value, in +the overrides of placed objects and in lists nested inside other items, in one undo step. + ## Deleting a struct asset -The delete dialog counts the structs and `Pack`/`Expand` nodes that use it. After deleting, each of -them **keeps the fields it last had**, now as its own fields — no value is lost, only the link. Its -Type row reads **Own fields** again. +The delete dialog counts the structs, the typed lists and the `Pack`/`Expand` nodes that use it. +After deleting, each of them **keeps the fields it last had**, now as its own fields — no value is +lost, only the link. A Type row reads **Own fields** again, an Items row **Mixed**. ## In the running game -Nothing changes there. A struct value is the same plain bundle of fields, with or without a type: -the type does not travel with the value, so `Equals`, `Index of`, saving and loading treat it as -before. Edges do not check the type either — any struct fits any struct port. +A struct value is the same plain bundle of fields, with or without a type: the type does not travel +with the value, so `Equals`, `Index of`, saving and loading treat it as before. Edges do not check +the type either — any struct fits any struct port. -## What it does not do (yet) - -Lists stay **untyped**: a list of structs still takes each item's shape from the item itself, and -**+ Item → Struct** starts an empty struct. Typed lists, where every item is one struct type, are -[issue #254](https://git.nockal.com/meleki/game-maker/issues/254). +The places that check are **Add item** and **Set item**, each with its **Type** setting. With a +type they take a value only if it is a struct with **exactly** the type's field names — none +missing, none more. Anything else is not added or set: the list comes out as it went in, and the +console names the value and the type. An item taken out of a typed list, and a `Pack` with the same +type, always fit. Without a type (or with one that was deleted) both take anything, as before. ## Under the hood @@ -89,3 +129,16 @@ project is loaded, so a hand-edited file heals too. Its writes go through a `Rec (`src/core/util/recorder.ts`), which is what makes an edit of unknown reach one undo step. A struct asset declares its fields as `variables`, so the variable commands and the inspector's variable rows edit them unchanged. + +A list declares nothing about its items, so an item carries its type itself: a typed list names the +asset in `itemType`, and every struct item in it holds the asset's id under the key `$struct`. A +struct item with a type of its own in a mixed list holds the same key. No field can have that name, +since field names never start with `$`. The same sync brings every marked item into shape, wherever +it sits. A run never sees the key: it is taken out where values enter the game +(`withoutItemTypes` in `src/core/model/Properties.ts`), which is why `Add item` and `Set item` can +compare field names exactly. + +A typed item keeps a field's value as plain JSON, the way a declared struct does: an asset id or a +texture id is a text, a color a number. What kind a field is comes from the type, so the item rows +draw each field as the type declares it — an asset field with its asset menu, not as the text it +holds.