@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.
- package/CHANGELOG.md +18 -0
- package/README.md +14 -0
- package/dist/neo-runtime.mjs +3789 -1141
- package/dist/test-worker.mjs +2450 -116
- package/package.json +1 -1
- package/skills/neocompose-cli/SKILL.md +95 -279
- package/skills/neocompose-cli/references/cli-development.md +101 -117
- package/skills/neocompose-cli/references/declarations/annotations-and-storage.md +112 -0
- package/skills/neocompose-cli/references/declarations/constructors.md +164 -0
- package/skills/neocompose-cli/references/declarations/declarations.md +103 -0
- package/skills/neocompose-cli/references/declarations/initializers.md +100 -0
- package/skills/neocompose-cli/references/declarations/member-kinds.md +107 -0
- package/skills/neocompose-cli/references/declarations/overloads.md +50 -0
- package/skills/neocompose-cli/references/dialogues.md +145 -0
- package/skills/neocompose-cli/references/neoscript/collections.md +98 -0
- package/skills/neocompose-cli/references/neoscript/control-flow.md +123 -0
- package/skills/neocompose-cli/references/neoscript/delegates-and-listeners.md +107 -0
- package/skills/neocompose-cli/references/neoscript/math-and-random.md +114 -0
- package/skills/neocompose-cli/references/neoscript/neoscript.md +112 -0
- package/skills/neocompose-cli/references/neoscript/strings.md +47 -0
- package/skills/neocompose-cli/references/neoscript/testing-and-migrations.md +73 -0
- package/skills/neocompose-cli/references/neoscript/types-and-narrowing.md +111 -0
- package/skills/neocompose-cli/references/sync/branches-and-history.md +69 -0
- package/skills/neocompose-cli/references/sync/conflicts-and-recovery.md +68 -0
- package/skills/neocompose-cli/references/sync/sync.md +96 -0
- package/skills/neocompose-cli/references/sync/unity-export.md +47 -0
- package/skills/neocompose-cli/references/values/defaults-and-localization.md +86 -0
- package/skills/neocompose-cli/references/values/files.md +71 -0
- package/skills/neocompose-cli/references/values/identities.md +97 -0
- package/skills/neocompose-cli/references/values/references.md +151 -0
- package/skills/neocompose-cli/references/values/values.md +110 -0
- package/skills/neocompose-cli/references/world/animation.md +133 -0
- package/skills/neocompose-cli/references/world/grids-and-cell-patterns.md +55 -0
- package/skills/neocompose-cli/references/world/variants.md +112 -0
- package/skills/neocompose-cli/references/world/world.md +105 -0
- package/skills/neocompose-cli/references/animation-and-world-authoring.md +0 -582
- package/skills/neocompose-cli/references/commands-and-sync.md +0 -408
- package/skills/neocompose-cli/references/declarations-and-construction.md +0 -773
- package/skills/neocompose-cli/references/neoscript.md +0 -990
- 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.
|