@neocompose/cli 0.87.1 → 0.88.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +14 -0
  3. package/dist/neo-runtime.mjs +3789 -1141
  4. package/dist/test-worker.mjs +2450 -116
  5. package/package.json +1 -1
  6. package/skills/neocompose-cli/SKILL.md +95 -279
  7. package/skills/neocompose-cli/references/cli-development.md +101 -117
  8. package/skills/neocompose-cli/references/declarations/annotations-and-storage.md +112 -0
  9. package/skills/neocompose-cli/references/declarations/constructors.md +164 -0
  10. package/skills/neocompose-cli/references/declarations/declarations.md +103 -0
  11. package/skills/neocompose-cli/references/declarations/initializers.md +100 -0
  12. package/skills/neocompose-cli/references/declarations/member-kinds.md +107 -0
  13. package/skills/neocompose-cli/references/declarations/overloads.md +50 -0
  14. package/skills/neocompose-cli/references/dialogues.md +145 -0
  15. package/skills/neocompose-cli/references/neoscript/collections.md +98 -0
  16. package/skills/neocompose-cli/references/neoscript/control-flow.md +123 -0
  17. package/skills/neocompose-cli/references/neoscript/delegates-and-listeners.md +107 -0
  18. package/skills/neocompose-cli/references/neoscript/math-and-random.md +114 -0
  19. package/skills/neocompose-cli/references/neoscript/neoscript.md +112 -0
  20. package/skills/neocompose-cli/references/neoscript/strings.md +47 -0
  21. package/skills/neocompose-cli/references/neoscript/testing-and-migrations.md +73 -0
  22. package/skills/neocompose-cli/references/neoscript/types-and-narrowing.md +111 -0
  23. package/skills/neocompose-cli/references/sync/branches-and-history.md +69 -0
  24. package/skills/neocompose-cli/references/sync/conflicts-and-recovery.md +68 -0
  25. package/skills/neocompose-cli/references/sync/sync.md +96 -0
  26. package/skills/neocompose-cli/references/sync/unity-export.md +47 -0
  27. package/skills/neocompose-cli/references/values/defaults-and-localization.md +86 -0
  28. package/skills/neocompose-cli/references/values/files.md +71 -0
  29. package/skills/neocompose-cli/references/values/identities.md +97 -0
  30. package/skills/neocompose-cli/references/values/references.md +151 -0
  31. package/skills/neocompose-cli/references/values/values.md +110 -0
  32. package/skills/neocompose-cli/references/world/animation.md +133 -0
  33. package/skills/neocompose-cli/references/world/grids-and-cell-patterns.md +55 -0
  34. package/skills/neocompose-cli/references/world/variants.md +112 -0
  35. package/skills/neocompose-cli/references/world/world.md +105 -0
  36. package/skills/neocompose-cli/references/animation-and-world-authoring.md +0 -582
  37. package/skills/neocompose-cli/references/commands-and-sync.md +0 -408
  38. package/skills/neocompose-cli/references/declarations-and-construction.md +0 -773
  39. package/skills/neocompose-cli/references/neoscript.md +0 -990
  40. package/skills/neocompose-cli/references/values-identities-and-references.md +0 -421
@@ -1,582 +0,0 @@
1
- # Animation and world authoring
2
-
3
- Use this reference for world objects, structured leaves, animation, and
4
- NeoFlow dialogue graphs.
5
-
6
- ## Contents
7
-
8
- - World object contracts
9
- - Cell patterns and grid queries
10
- - Sorting and optional children
11
- - Structured leaves
12
- - Object variants and lookup folders
13
- - Animation overrides
14
- - Segments and tracks
15
- - NeoFlow dialogues
16
-
17
- ## World object contracts
18
-
19
- Extend visible system world classes with ordinary project-authored classes and
20
- overrides. Never copy the system schema's `@system` provenance annotation into
21
- project source.
22
-
23
- For world layer links, derive a concrete project class from
24
- `NeoTileLayerLink` or `NeoObjectLayerLink` and resolve exactly one
25
- `targetLayer` relation. Do not add value-level target metadata or ask painting
26
- operations to create/repair relation targets.
27
-
28
- ## Cell patterns and grid queries
29
-
30
- `NeoCellPattern` stores an immutable ordered list of `Vector2Int` offsets.
31
- Its constructor copies the input list and preserves duplicates. Use `Box`,
32
- `Cross`, `Ring`, `Rect`, or `Line` for common shapes, and `Center`,
33
- `FourNeighbors`, `EightNeighbors`, or `Empty` for presets. `Cells(origin)`
34
- returns absolute cells in pattern order.
35
-
36
- ```neo
37
- NeoCellPattern neighbors = NeoCellPattern.Box(1, excluding: .Center);
38
- NeoCellPattern custom = new(offsets: [new(-1, 0), new(2, 1)]);
39
- ```
40
-
41
- `NeoCellPattern` is an ordinary stored class, and the presets are stored
42
- static values. A field that owns its pattern can't default to a preset or any
43
- other stored static, because the static already owns that row. `neo status`
44
- and push refuse `= NeoCellPattern.EightNeighbors` there. Call `.Clone()` to
45
- give each instance its own copy, or declare `Reference<NeoCellPattern>` to
46
- share the preset or any stored pattern:
47
-
48
- ```neo
49
- class Sprinkler : NeoObject {
50
- public NeoCellPattern Reach = NeoCellPattern.EightNeighbors.Clone();
51
- public Reference<NeoCellPattern> Splash = NeoCellPattern.Center;
52
- }
53
- ```
54
-
55
- On a placed `NeoObject`, `this.Cell` returns its grid cell.
56
- `this.GetObjects<Plant>(neighbors)` returns matching placed objects in the
57
- same grid, filtered to `Plant` and subclasses. `this.GetTile(neighbors)`
58
- returns the first tile found in pattern order, or null.
59
- `this.GetTiles(neighbors)` returns the tile at each occupied cell in pattern
60
- order and skips empty cells. A type argument filters each cell's top tile:
61
- `this.GetTiles<DirtDryTile>(neighbors)` returns `List<DirtDryTile>` and skips
62
- a cell whose top tile is something else, even when a dry tile sits under it. Each query takes an optional `offset` that shifts
63
- the whole pattern, so `this.GetTiles(neighbors, new(0, 2))` queries around the
64
- cell two rows up without building a translated pattern. These queries track
65
- empty cells as well as hits, so a getter updates when a new neighbor appears.
66
- Calling them on an unplaced object is an error.
67
-
68
- `this.GetArea()` returns the nearest `NeoTileGrid` that owns the object or
69
- tile, or null when no grid owns it. It works on any `NeoObjectBase` or
70
- `NeoTile`: a sprite child, an object held in another object's field, or a tile
71
- in a nested layer link answers with its placed root's grid. A type argument
72
- filters the answer, so `this.GetArea<FarmGrid>()` is null in any other grid.
73
- It sees spawns, moves, and removals made earlier in the same evaluation.
74
-
75
- `WithCenter`, `WithoutCenter`, `Translate`, and `Union` return patterns.
76
- `Union` removes duplicate offsets while preserving first occurrence order.
77
- Box, Cross, and Line include the origin by default. Use `excluding: .Center`
78
- to omit it. Line's `length` is the number of steps beyond the origin, so
79
- `Line(new(1, 0), 3)` contains four cells.
80
-
81
- In C#, the generated `NeoCellPattern` implements `INeoCellPattern`, an
82
- `IReadOnlyList<Vector2Int>` of offsets, and SDK grid helpers accept any
83
- `INeoCellPattern`. Replace old boolean factory arguments with
84
- `NeoCellPatternExcluding.None` or `.Center`. To retain Line's old default,
85
- pass `excluding: NeoCellPatternExcluding.Center` explicitly.
86
-
87
- ## Tile placements
88
-
89
- A placement is an ordinary concrete `NeoTile` value with a `Cell`:
90
-
91
- ```neo
92
- new GlassFloorTile { Cell = new(9, -4) }
93
- ```
94
-
95
- Layer-link `Tiles` lists contain these values directly. `NeoTileInstance` and
96
- `@tile(...)` are removed. A tile's `Name`, `Sprite`, and `SmartTile` are
97
- class-level `readonly` members: tiles render per class, so override them on a
98
- subclass. Push rejects a placement that sets them. `Cell` is the placement's
99
- writable field.
100
-
101
- Object `PlacementTiles` is a `List<NeoPlacementTile>`. Each entry marks a
102
- footprint cell with its own `Cell` member. It is independent of `NeoTile`. Rendered
103
- tile classes do not belong in this list. An object with no placement tiles
104
- occupies no cells. It doesn't block other placements, and cell queries don't
105
- return it. A placed object with no tiles still has a `Cell`.
106
-
107
- `NeoSpriteObject` extends `NeoObject`. Place a sprite class like any object,
108
- give it `Children`, and override `OnObjectSpawned`/`OnObjectDespawned` on it.
109
- Hooks fire on every object in a placed hierarchy, parent first; despawn runs
110
- children first. `NeoObjectBehaviour.IsRoot` is true only on the placed root.
111
-
112
- Convert a writable placement without changing its row identity or cell:
113
-
114
- ```neo
115
- tile.TryConvert<GlassFloorTile>();
116
- tile.TryConvert(nextTile);
117
- ```
118
-
119
- The second form uses the target value's runtime class. It does not copy the
120
- target's fields. Both forms return `false` when placement validation rejects
121
- the new class. The grid's imports and compatible-layer relations still apply.
122
-
123
- `Tiles.Add` and `Tiles.Remove` change placement data directly. The SDK validates
124
- placement writes and updates live rendering from the resulting data changes,
125
- including script writes and remote batches. For flattened SDK layers,
126
- `TrySetTile<T>(cell)` remains available. Spawn objects with
127
- `TrySpawn(cell, constructedObject)`; successful adoption preserves the object's
128
- identity. Clone an existing owned object explicitly before placing another copy.
129
-
130
- Use an object's real `ToVariant` API to apply a variant of its class. The
131
- object inspector lists variants and their folders. Painting a different object
132
- class replaces the placement with a new row; it does not change the old object's
133
- class in place. The old `TrySwapVariant` class-swap APIs are removed.
134
-
135
- ## Object variants
136
-
137
- A variant is a named configuration of one `NeoObject`-derived class: how to
138
- construct a fresh instance, and how to move an existing one into the
139
- configuration. Variants are records parented to their class, never values
140
- inside it, so they live in their own `Variants/` family rather than in the
141
- class file.
142
-
143
- ```neo
144
- // Variants/ExampleObject.neo
145
- @id("5f0c2b9e-7d41-4a58-9b1a-3f6f0e2d8c11")
146
- NeoVariant<ExampleObject> Down = new(
147
- initialize: () => { return new ExampleObject(.Down); },
148
- apply: (source) => { source.FacingDir = .Down; },
149
- );
150
- ```
151
-
152
- - `initialize` takes no parameters and returns the target class. It is
153
- required: it is the only way to construct through the variant.
154
- - `apply` takes exactly one parameter of the target class and is void. Omit it
155
- for a variant that is only ever constructed.
156
- - Both are ordinary delegate closures: parenthesized parameter list, then
157
- either a block body or an expression body. `initialize` returns, so
158
- `() => new ExampleObject(.Down)` works; `apply` is void, so its expression
159
- body must be a call or an assignment, as in
160
- `(source) => source.FacingDir = .Down`. The server compiles them, so authored
161
- source carries only the code. Each closure has its own lexical scope, so
162
- local names may be reused between `initialize` and `apply`.
163
- - `overrides` and each child override's `overrides` value are `Partial<T>`
164
- deltas over existing instances. They may use inferred `new { ... }` or
165
- explicit `new Partial<T> { ... }`, and never pass arguments to or invoke
166
- `T`'s constructor—even when `T` has a required constructor.
167
- - `Variants.X.Initialize()` runs `initialize`, then `overrides` and child
168
- overrides. `ToVariant` runs `apply`, then `overrides` and child overrides; it
169
- never reruns `initialize`. An instance remembers both: the variant that built
170
- it and the variant last applied. The applied variant's overrides win where
171
- they answer, `apply` writes stay as instance overrides, and
172
- `ToVariant(Base)` removes the applied layer. On an object nested inside
173
- another instance's construction, `ToVariant` applies the layers in place and
174
- records nothing.
175
-
176
- Folders are records too, declared as their own globals and assigned per
177
- variant. Nesting is spelled in the path with `/`; intermediate segments are
178
- implicit, so `new("Trees/Oak")` needs no `Trees` record.
179
-
180
- ```neo
181
- // Variants/ExampleObject/Inverted.neo
182
- @id("b7d2e9f0-3c15-4a68-8b24-9e0f1a2c3d44")
183
- NeoVariantFolder<ExampleObject> Inverted = new("Inverted");
184
-
185
- @id("e3b9c0d1-4a26-4f81-92e7-6b5a4c3d2e66")
186
- @settings(folder: Inverted)
187
- NeoVariant<ExampleObject> Down = new(
188
- initialize: () => { return new ExampleObject(.Down); },
189
- );
190
- ```
191
-
192
- Rules push enforces, and the language service reports live:
193
-
194
- - A variant or folder belongs to the class its single type argument names, and
195
- a variant may only reference a folder of its own class.
196
- - Variant names and folder path segments share one generated static scope per
197
- folder, and are unique within it case-insensitively. Two classes may each
198
- declare a `Down`.
199
- - `Base` is reserved case-insensitively: `<Class>.Variants.Base` denotes the
200
- class with no variant applied.
201
- - Every name must survive C# identifier normalization, since each becomes a
202
- member of the generated `Variants` tree.
203
-
204
- Push is folder-agnostic as it is for every other record family: a variant
205
- declared in any `.neo` file anywhere pushes identically. `Variants/<Class>.neo`
206
- and `Variants/<Class>/<Folder path>.neo` are the canonical layout `neo pull`
207
- materializes, one file per (class, folder), folder record first and variants
208
- ordered by name.
209
-
210
- A lookup folder binds one concrete list value and enumerates the same authored
211
- variants for every row in that list. Its second type argument is the list entry
212
- class. An empty path binds the class root; ordinary folders still require a
213
- non-empty path.
214
-
215
- ```neo
216
- // Variants/ExampleObject.neo
217
- @id("18b93b80-5733-4747-bf5f-3609336e4f17")
218
- NeoLookupVariantFolder<ExampleObject, NeoSortingLayer> Root = new(
219
- path: "",
220
- collection: Assets.SortingLayers,
221
- collectionValue: Reference(root.Assets.SortingLayers),
222
- );
223
-
224
- @id("736dddf8-2881-47e1-a1d3-55fb65ea9f2a")
225
- @settings(folder: Root)
226
- NeoLookupVariant<ExampleObject, NeoSortingLayer> Layered = new(
227
- initialize: (value) => { return new ExampleObject(); },
228
- );
229
- ```
230
-
231
- - `collection` names the eligible list member; `collectionValue` names the
232
- concrete stored list whose rows the folder enumerates. Prefer the canonical
233
- `Reference(root...)` path emitted by pull; an explicit ID remains valid.
234
- - Lookup `initialize` receives the selected row and returns the target class.
235
- Lookup `apply` receives the target object followed by the selected row.
236
- - `NeoLookupVariant<TObject, TValue>` members retain an unbound lookup handle.
237
- A plain `NeoVariant<TObject>` member may select one concrete row with
238
- `.Bind(Reference(...))`; do not bind a row to the lookup-handle member type.
239
- - Variant selections compare equal with `==` when their target class, variant,
240
- and bound lookup row match; repeated reads of `Variants.Base` are equal.
241
- `!=` is the inverse, and an unset optional selection remains distinct from Base.
242
- - `NeoVariantFolder` and `NeoLookupVariantFolder` are top-level authoring
243
- records, never member or parameter types. Variant handle types are supported
244
- as the dedicated Variant member kind, but remain invalid as collection entry,
245
- lookup target, parameter, or generic-binding types.
246
-
247
- ## Sorting and optional children
248
-
249
- Attach `NeoSortingGroup` as an optional nested member on the project object
250
- classes that need it:
251
-
252
- ```neo
253
- class Tree : NeoObject {
254
- public virtual NeoSortingGroup? SortingGroup = null;
255
- }
256
- ```
257
-
258
- Presence of a non-null group makes the object and descendants sort as one unit.
259
- `SortAtRoot` hoists a nested group against the scene root. `SortPoint` is the
260
- point the group Y-sorts at, in cells from the object's origin-cell corner
261
- (default `(0.5, 0)`, the origin cell's bottom center). Moving it never moves
262
- art. Set it per class, variant, or animation frame, like any other value.
263
-
264
- Objects sort by sorting layer, then order in layer, then sort-point Y (lower
265
- draws in front), then document order. `NeoSpriteObject` exposes `FlipX`,
266
- `FlipY`, `MaskInteraction`, and optional `SortingOrder`. Treat `SortingOrder`
267
- as an offset added to the layer's sorting order; it is not an absolute
268
- replacement.
269
-
270
- Use `NeoObjectBase.Enabled` to hide an object and its entire subtree without
271
- removing its authored `Children` row. A disabled object is not rendered or
272
- collided with, but its live values and running animation continue. Prefer this
273
- for fixed optional equipment/content slots so their provenance and animation
274
- targets remain stable.
275
-
276
- Do not treat P45 runtime-child provenance as available. Runtime structural
277
- children remain deferred; author known slots and toggle `Enabled`.
278
-
279
- ## Structured leaves
280
-
281
- Address fields of these leaf values:
282
-
283
- - `SpriteInfo`: `FileId`, `SliceIndex`
284
- - Vector values: lowercase components such as `x`, `y`, `z`, `w` where
285
- supported by the concrete vector type
286
- - Color: lowercase `r`, `g`, `b`, `a`
287
-
288
- Keep the casing exact. `FileId` and `SliceIndex` are PascalCase; vector and
289
- color components are lowercase.
290
-
291
- Use complete values in normal member initialization:
292
-
293
- ```neo
294
- Sprite = Images.Hero.Slice(0);
295
- Placeholder = SpriteInfo.Empty;
296
- Position = new(0, 0, 0);
297
- Tint = Color(1, 1, 1, 1);
298
- ```
299
-
300
- A bare `Images.Hero` yields a file ID only in a file-valued field context.
301
- Use `.Slice(index)` for a complete `SpriteInfo`.
302
- Use `SpriteInfo.Empty` for an authored non-null empty sprite. Use `null` only
303
- when the declared type is nullable `SpriteInfo?`.
304
-
305
- NeoScript may read or write structured fields when the receiver's effective
306
- storage permits it:
307
-
308
- ```neo
309
- root.Session.Debug.Slice = Leg.Sprite.SliceIndex;
310
- Leg.Sprite.SliceIndex = 2;
311
- Leg.Position.y = 0.25;
312
- Leg.Tint.a = 0.5;
313
- ```
314
-
315
- Treat writes as read-modify-write against the current whole leaf. Reject
316
- unknown fields, vector components unsupported by the concrete type, non-integer
317
- integer-vector values, and color channels outside `[0, 1]`.
318
-
319
- ## Animation overrides
320
-
321
- Use partial structured literals only inside animation override graphs:
322
-
323
- ```neo
324
- Overrides = new {
325
- Sprite = new { SliceIndex = 1 },
326
- Position = new { y = 0.25 },
327
- Tint = new { a = 0.5 },
328
- };
329
- ```
330
-
331
- An empty partial `new { }` means no change. A partial override changes only
332
- the named fields and preserves the leaf's other current fields. A whole-leaf
333
- override replaces the whole leaf.
334
-
335
- Frame overrides may change `Enabled`, `FlipX`, and `SortingOrder`; they may
336
- not add, remove, or reorder `Children`.
337
-
338
- Address direct children through selector delegates. Prefer the project's own
339
- stable identity contract, such as an immutable `Name`, slug, or semantic
340
- position. When a selector means one specific authored child slot whose
341
- placement clones carry `sourceValueId`, return
342
- `Reference<T>(id: "<authored-child-id>", withProvenance: true)`. The flag is
343
- opt-in and defaults to `false`; without it the ID is matched exactly. Missing
344
- optional child slots are skipped with diagnostics when the authored slot is
345
- absent, while stale pre-provenance placements may still fail closed.
346
-
347
- Authored object placements can carry variant and source metadata beside their
348
- schema keys. Preserve that metadata when editing the row; do not declare it as
349
- project schema fields. Raw tile placements use their row class and `Cell`, with
350
- no `assetClassId` or `assetValueId` sidecars.
351
-
352
- ## Segments and tracks
353
-
354
- Use `NeoAnimationSegment<T>` for a frame-indexed value lane. The shipped
355
- sprite specialization is `NeoSpriteAnimationSegment`, whose
356
- `NeoAnimationSegmentFrame<SpriteInfo>` rows each provide `Index` and
357
- `Value`. Sparse frame values hold until the next row or `Duration`.
358
- Pass `Value` through the frame constructor (for example,
359
- `new NeoAnimationSegmentFrame<int>(value: 60)`). The constructor argument is
360
- the frame's authored value; it must not collapse to the concrete generic
361
- binding's fallback while the segment is materialized.
362
-
363
- Use one polymorphic `NeoAnimationClip.Tracks` list:
364
-
365
- - `NeoAnimationChildTrack` schedules `ClipKey` on the child returned by its
366
- `Selector` delegate.
367
- - A project subclass of
368
- `NeoAnimationSegmentTrack<TChild, TValue>` schedules a segment onto one
369
- target member. Implement its abstract `Segment` member as a stored value,
370
- lookup, or computed getter.
371
- - The shipped
372
- `NeoSpriteAnimationSegmentTrack<TChild extends NeoSpriteObject>` pairing
373
- targets `NeoSpriteObject.Sprite`.
374
-
375
- Declare a concrete project track by implementing `Segment`, expose a compatible
376
- selector function on the owning object, then pass that function to each row:
377
-
378
- ```neo
379
- class StoredPantsTrack : NeoSpriteAnimationSegmentTrack<PantsSprite> {
380
- public override NeoSpriteAnimationSegment Segment = new();
381
- }
382
-
383
- class LegPart : NeoObject {
384
- public PantsSprite SelectPants() {
385
- var child = this.Children.FirstOrDefault((candidate) => {
386
- return candidate.Name == "Pants";
387
- });
388
- if (child is PantsSprite pants) {
389
- return pants;
390
- }
391
- throw "Pants child is missing or has the wrong type";
392
- }
393
-
394
- public NeoAnimationClip<LegPart> Walk = new NeoAnimationClip<LegPart> {
395
- Tracks = [
396
- new StoredPantsTrack(selector: this.SelectPants) {
397
- StartFrame = 0,
398
- Direction = .Reverse,
399
- OffsetEndIndex = 3,
400
- },
401
- ]
402
- };
403
- }
404
- ```
405
-
406
- Selectors may also be compatible inline closures. Prefer a named function when
407
- the identity rule is reused or deserves an explicit failure message. An exact
408
- `Reference<T>(id: "...")` recreates the row-ID coupling selectors were
409
- introduced to remove; use `withProvenance: true` only for a deliberately
410
- authored slot identity. For new track rows, omit `@id` and let the successful
411
- push assign it. Preserve IDs already present on pulled rows.
412
-
413
- A selector argument such as `this.SelectPants` is evaluated in the lexical
414
- scope of the class that declares the clip. Here `this` is the `LegPart`, not
415
- the nested track row or an outer object instance that later receives a copy of
416
- the declaration default. A named method group lowers to the P60 member-target
417
- form. Its declaration default carries `valueId: null` because no instance row
418
- exists yet; materializing the declaration binds it to the concrete `LegPart`
419
- row. An inline closure remains compiled closure source and reconstructs its
420
- lexical owner from the materialized graph. Do not interchange these two forms.
421
-
422
- Each track has `StartFrame`, `Direction` (`.Forward` or `.Reverse`), and a
423
- crop window `OffsetStartIndex`/`OffsetEndIndex`. Crop before reversing, then
424
- schedule on the owning clip. Content beyond the owning clip truncates. Reject
425
- empty/inverted crop windows and tracks that can never enter the clip.
426
-
427
- Treat track target selection as project-authored identity logic, not row data.
428
- The selector return type must be compatible with `TChild`; the segment value
429
- must be compatible with `TValue`. `Refresh` controls selector reevaluation,
430
- while track reads and segment getters are re-resolved for the next applied
431
- frame after a watched dependency changes.
432
-
433
- When multiple tracks write the same member in one frame, apply list order and
434
- let the last write win.
435
-
436
- A track can target a Class member. Each frame writes its whole value, and a
437
- `null` frame clears the member. Every `NeoObjectBase`, sprites included,
438
- declares `Collider`, so a per-frame trigger such as an attack hit box is a
439
- Session `Collider` override on the child plus a track of `NeoCollider?`
440
- frames:
441
-
442
- ```neo
443
- class ColliderAnimationSegment : NeoAnimationSegment<NeoCollider?> {
444
- }
445
-
446
- @settings(target: NeoObjectBase.Collider)
447
- class SwingColliderTrack(NeoDelegate<SwordSprite> selector)
448
- : NeoAnimationSegmentTrack<SwordSprite, NeoCollider?>(selector: selector) {
449
- public override ColliderAnimationSegment Segment = new ColliderAnimationSegment() {
450
- Duration = 3,
451
- Frames = [
452
- new NeoAnimationSegmentFrame<NeoCollider?>(value: new NeoCollider() {
453
- Size = new(1, 1),
454
- Offset = new(1, 1),
455
- IsTrigger = true
456
- }) { Index = 1 },
457
- new NeoAnimationSegmentFrame<NeoCollider?>(value: null) { Index = 2 }
458
- ]
459
- };
460
- }
461
- ```
462
-
463
- The segment's class must fit the target member's class. Like any track target,
464
- the member must be a writable Save, Session, or Writable leaf on the child, for
465
- example `@storage(allowed: .Session) public override NeoCollider? Collider;`.
466
-
467
- ## NeoFlow dialogues
468
-
469
- Lookup dialogue groups use the same collection paths as lookup members:
470
-
471
- ```neo
472
- NeoDialogueGroup FishingVictory = new(
473
- kind: .Lookup,
474
- collection: Assets.FishEncounters,
475
- collectionValue: Reference(root.Assets.FishEncounters),
476
- );
477
- ```
478
-
479
- The collection member and its value may be created in the same push without
480
- explicit IDs. Pull emits readable paths when the collection has a stable root
481
- path. Raw `Reference<Member>(id: "...")` and `Reference(id: "...")` forms remain
482
- readable, and values without a root path retain the ID form.
483
-
484
- Node and option prose lowers to localized-text records with canonical node or
485
- choice backlinks. Existing translations and unrelated localization links are
486
- preserved when the source is pushed.
487
-
488
- Put exactly one top-level `sealed class Name : NeoDialogue` in each `.neoflow`
489
- file. Make it non-generic, derive directly from compiler-owned `NeoDialogue`, and
490
- override `Trigger Trigger` exactly once. Do not add any other type declaration.
491
-
492
- ```neoflow
493
- @id("capitol-dialogue-id")
494
- @settings(name: "Capitol: cold boot", saveOptionChoices: true)
495
- sealed class CapitolColdBoot : NeoDialogue {
496
- @primary Outpost capitol = Assets.Capitol;
497
- Player player = Player.Current;
498
-
499
- @id("trigger-id")
500
- override Trigger Trigger = new(group: CapitolDialogues.High) => Welcome;
501
-
502
- @id("welcome-node-id")
503
- Text Welcome = new(name: "Welcome!") {
504
- """
505
- Hello, {player.Name}.
506
- """
507
-
508
- @id("continue-option-id")
509
- Option Continue = new() {
510
- """
511
- Continue.
512
- """
513
- return Finish;
514
- }
515
- }
516
-
517
- @id("finish-node-id")
518
- Text Finish = new() {
519
- """
520
- Until next time.
521
- """
522
- }
523
- }
524
- ```
525
-
526
- Use `@primary` explicitly; never infer it. In a `.Lookup` group, the
527
- dialogue's `@primary` is the lookup value that triggers it; a lookup dialogue
528
- without one never triggers. Keep dialogue bindings flow-wide and
529
- node bindings scoped to their node/children. Give each persisted condition use,
530
- action invocation, mutation, pause, option, and node its own owner-scoped ID.
531
-
532
- Use `=> Next` or a trailing block with `return Next;`; do not author a `to:`
533
- argument. A destination arrow always follows an `=` initializer, as in
534
- `= new(...) => Welcome;`; an arrow that instead follows a member name or a
535
- parameter list's `)` is a NeoScript expression body, so the two never collide.
536
- Require destinations for triggers, options, and outcomes. Permit terminal
537
- text/actions to fall through. Keep all statements for
538
- `Actions Empty = new();` inside its body.
539
-
540
- After editing, run `neo dialogue dryrun <dialogue-ref>`. It prepares the local
541
- project candidate, including new dialogues and pending schema or value changes,
542
- without fetching or committing server content. Exit 1 means the graph
543
- would fail on device. Keep every dialogue edit in the tracked `.neoflow` source
544
- and commit it with `neo push`.
545
-
546
- ### Dialogue group names and local symbols
547
-
548
- Use `NeoDialogueGroup QuestUnlocks = new(kind: .Standard, parent: Quests, name: "Unlocks")`
549
- when the local identifier should differ from the record's display name. Omitting
550
- `name` uses the identifier. Unity generates its dialogue API from the stored
551
- name (`Quests.Unlocks`), independently of the local identifier. Pull, force-pull,
552
- and reset retain the local identifier and authored file path by `@id`; a remote
553
- rename updates the `name:` argument.
554
-
555
- Trigger groups accept both `group: QuestUnlocks.Medium` and
556
- `group: Quests.Unlocks.Medium`. Qualified paths use the group's display name
557
- converted to an identifier; priority options use their declared names.
558
- Completion, hover, navigation, and diagnostics resolve both forms. Sibling
559
- groups that produce the same path segment require their distinct local aliases.
560
-
561
- ### NeoFlow display names
562
-
563
- For `Text`, `Actions`, `Conditions`, `Option`, and `Outcome`, omitting `name:`
564
- infers the label from a custom authored symbol. `Option Accept = new()` gets
565
- label `Accept`, regardless of its prose. Dialogue classes use the same rule
566
- when `@settings(name: ...)` is omitted.
567
-
568
- Generated symbols such as `Text1`, `Actions2`, `Option1_2`, `Outcome1`, and the
569
- identity-based `Node_...` forms stay unlabeled for their corresponding kinds.
570
- A symbol from another kind is custom: `Option Text1` gets label `Text1`.
571
- Explicit names win, including
572
- `name: ""` for clearing a label, or `name: null` on optional graph labels. Dialogues and outcomes store
573
- an empty string for an unset label; optional graph labels store null. Pull
574
- writes explicit empty overrides where omission would otherwise infer a name,
575
- and omits a redundant label equal to the inferred symbol.
576
-
577
- The fixed `Trigger` member does not infer a label. Reusable condition and action
578
- functions keep their function identifiers as `sourceFunctionName`; they have
579
- no separate display-name field or `name:` constructor. Rename the function
580
- and its uses to change that source name. Linked-value aliases likewise retain
581
- their `sourceName`. These names never come from prose, and the naming rule does
582
- not change IDs, destinations, or condition/action use identities.