@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,773 +0,0 @@
1
- # Declarations and construction
2
-
3
- Use this reference for schema declarations, annotations, member contracts,
4
- constructors, and construction-time settlement.
5
-
6
- ## Contents
7
-
8
- - Declaration rules
9
- - Annotations and settings
10
- - Member and relation rules
11
- - Function overloads
12
- - Computed initializers and declared constructors
13
- - Required constructors and settlement
14
- - Inheritance construction
15
-
16
- ## Declaration rules
17
-
18
- Declare classes, interfaces, and enums only at the top level of definition
19
- `.neo` files. Do not nest them in a type, body, accessor, lambda, initializer,
20
- migration, or `.neoflow` file. NeoFlow has its own single-dialogue exception.
21
-
22
- Persist the declared identifier as the schema name; there is no separate
23
- technical/display name. Keep existing IDs stable.
24
-
25
- NeoScript keywords are case-sensitive. Qualified member labels, schema keys,
26
- registry entries, and enum options occupy their owner's namespace, so they may
27
- reuse unrelated builtin names such as `Set`, `Dictionary`, `List`, or `Math`.
28
- Type-token spellings such as `Set` and `Dictionary` are also legal parameters.
29
- Actual lowercase grammar keywords such as `class` and `return` remain
30
- reserved. Top-level declarations and bindings retain their own reservations:
31
- for example, neither may shadow the unqualified builtin `Math` namespace, and
32
- implicit bindings such as `this`, `root`, and `context` cannot be parameters.
33
-
34
- ```neo
35
- @id("interface-named-id")
36
- interface INamed {
37
- @id("named-name-id")
38
- string Name { get; }
39
-
40
- @id("named-use-id")
41
- void Use(SomeClass context);
42
- }
43
-
44
- @id("class-item-id")
45
- @storage(allowed: .Immutable)
46
- abstract class Item<TContext extends SomeClass> : INamed {
47
- @id("item-name-id")
48
- @settings(searchBy: .MemberKey)
49
- public virtual string Name = "";
50
-
51
- @id("item-use-id")
52
- public abstract void Use(TContext context);
53
-
54
- @id("item-load-id")
55
- public native async bool Load(TContext context);
56
- }
57
- ```
58
-
59
- Use ordinary `public`, `protected`, `private`, `static`, `virtual`, `abstract`,
60
- `sealed`, and `override` semantics. A sealed class is an instantiable leaf:
61
- it may extend another class but cannot be extended, cannot also be abstract,
62
- and can only be sealed while it has no subclasses. Interfaces can declare type
63
- parameters and pass arguments to the interfaces they extend; see Interface
64
- members.
65
- A `closed` class is implicitly abstract and defines an exhaustive subtype
66
- family within the project version. Do not combine `closed` with `abstract` or
67
- `sealed`. Descendants remain open unless explicitly sealed or closed. Every
68
- generic parameter of a direct subclass must appear in its base construction.
69
- Changing the family requires recompilation of its switch-expression consumers.
70
- Generated Unity C# uses an abstract class; C# 15 is not required.
71
-
72
- A read-only override may narrow a class or interface contract to an assignable
73
- class or derived interface, including a non-null value for a nullable contract.
74
- Generated Unity C# keeps the inherited property type for Mono compatibility;
75
- NeoScript sees the narrowed type. Stored implementations of an abstract getter
76
- remain get-only in generated C#.
77
-
78
- Do not put `@id` on inferred list/dictionary type entries or on derived
79
- structural descriptors.
80
-
81
- ```neo
82
- sealed class DirtTile : PlaceableTile {
83
- public bool IsWet = false;
84
- }
85
- ```
86
-
87
- A bodyless function must be `abstract`, `native`, or an interface contract.
88
- `async` alone does not make it valid.
89
-
90
- ## Annotations and settings
91
-
92
- Never author `@system`. It marks platform-owned system records and is rejected
93
- on project-authored declarations, members, overrides, values, and files. The
94
- system types visible in project source may themselves have system provenance;
95
- that does not authorize copying the annotation onto authored records.
96
-
97
- Use context-aware typed annotations:
98
-
99
- - `@settings(...)` for format/search/collection/editor constraints.
100
- - `@storage(allowed: ...)` on classes and storage keys on members.
101
- - `@visibility(.Hidden)` only on a class; absence means visible.
102
- - Stateful settings use contextual enum cases, such as
103
- `@settings(format: .Plain)` and `@settings(kind: .Unordered)`.
104
- - `@settings(...)` before a `List` or `Set` element or `Dictionary` value type
105
- configures the collection's entries: `List<@settings(format: .Plain) string>`,
106
- `Dictionary<string, @settings(min: 0) int>`. Only the member that declares
107
- the collection spells them; overrides share its entries.
108
- - `@relations(...)` only on the source class of a specialized direct relation.
109
- - `@distribution(kind: .Sparse | .Packed)` only on a value-owning member
110
- declaration or override. It chooses whether the member's value keeps its
111
- own row (`.Sparse`) or is stored inside its owning parent's row (`.Packed`).
112
- Omit it unless the choice must be explicit: on a root declaration absence
113
- means the automatic distribution for the member's kind and settings, and on
114
- an override it means inherit. `pull` writes the annotation only where it is
115
- explicitly stored. A packed subtree shares its parent's document-size limit,
116
- so a write that would exceed the hard limit fails instead of falling back.
117
- `.Packed` is rejected on a List whose `listKind` is unordered.
118
- - `@effect` only on a class member function. The Unity runtime runs it for
119
- every live instance and reruns it whenever data it read changes; the web
120
- editor, `neo test`, and dry runs never run effects. It must be an immediate,
121
- parameterless `void` instance function with a NeoScript or abstract body.
122
- An override without the annotation inherits; `@effect(kind: .None)` stops
123
- an inherited effect. Use it instead of polling or relaying changes by hand.
124
-
125
- Use numeric literals for numeric limits. Keep annotations separate instead of
126
- inventing a generic metadata bag.
127
-
128
- ### Storage
129
-
130
- A member's `@storage(allowed: ...)` is `.Immutable`, `.Save`, `.Session`, or
131
- `.Writable`. Omit it to inherit the placement parent's storage. `.Writable` is
132
- always writable at runtime and resolves from where the value is placed: Save
133
- under a Save parent, Session under a Session or Immutable parent. Use it for a
134
- member that NeoScript or an animation clip writes and whose value should persist
135
- exactly when its owner persists. Animation clips can target `.Writable` members.
136
-
137
- ```neo
138
- class AnimationSpriteObject(SpriteInfo sprite) : NeoSpriteObject {
139
- @id("69905c25-8c05-4d18-90fd-069f1526a38a")
140
- @storage(allowed: .Writable)
141
- public override Vector3 Size = new(1, 1, 0);
142
- }
143
-
144
- abstract class BodyLayerSprite(CharacterCosmeticConfig config, SpriteInfo sprite, int nearOrder, int farOrder) : AnimationSpriteObject(sprite: sprite) {
145
- @id("e81dcef4-9ea8-4bbc-a83b-565b7cf3feff")
146
- @storage(allowed: .Session)
147
- public override Vector3 Size = new(2, 2, 0);
148
- }
149
- ```
150
-
151
- An override below a `.Writable` member, directly or through other overrides,
152
- may declare `.Writable`, `.Save`, or `.Session`, or omit `@storage`. It may not
153
- declare `.Immutable` or `.Inherit`, and it may not be `readonly`. Static
154
- members, `readonly` members, and project roots cannot declare `.Writable`.
155
- Lowering reports each violation at the member's position.
156
-
157
- On a class, `@storage(allowed: .Writable)` means every placement must resolve
158
- to Save or Session. A subclass may narrow it to `.Save` or `.Session`. An
159
- undeclared static member of such a class is Session.
160
-
161
- `root.User` holds per-player data that every save shares, such as settings.
162
- Only User classes go under it. Declare one with `@storage(allowed: .User)` on
163
- the class; its subclasses and base must be User classes too:
164
-
165
- ```neo
166
- @storage(allowed: .User)
167
- class AudioPrefs {
168
- public float Volume = 1;
169
- }
170
- ```
171
-
172
- Members of a User class declare no `@storage` and no stored statics, and every
173
- type they name must be User-safe: primitives, enums, User classes, collections
174
- and delegates of those, and interfaces whose signatures are User-safe. Code in
175
- a User class reads only `root.User`. Other code reads `root.User` but can't
176
- assign, mutate, construct, or invoke User data. Lookups and listeners don't
177
- cross between User and Save data; Session lookups may target `root.User`.
178
-
179
- `readonly` is init-only, as in C#, and independent of storage. A field
180
- initializer, a constructor body, an object initializer block, or an authored
181
- value sets it; nothing writes it after construction. It is valid on Save,
182
- Session, Immutable, and User classes, but not with Writable storage. It may
183
- read a header parameter:
184
-
185
- ```neo
186
- class MixerGroup(string name) {
187
- public readonly string Name = name;
188
- public readonly List<MixerChannel> Children = [];
189
- public float Volume = 1;
190
- }
191
- ```
192
-
193
- Readonly is shallow. A readonly collection's membership is fixed, while its
194
- entries' members follow their own storage and mutability. User class members
195
- still declare no storage.
196
-
197
- A readonly member declared as a literal (no initializer or constructor call)
198
- that reads neither `this` nor a constructor argument and holds nothing
199
- writable is a constant. Instances share its declared value and store nothing
200
- until a constructor or initializer block writes it. A readonly collection or
201
- initializer is still built per instance.
202
-
203
- `@index(member: Field, kind: .Unique)` and
204
- `@column(member: Field, width: ..., visibility: ..., pin: ..., overflow: ...)`
205
- are repeatable List annotations. `member:` is a bare field name on the entry
206
- class. An index accepts a non-localizable string or single-select enum. A column
207
- may target `__other__` for the default layout of subclass-added fields. Declare
208
- indexes only on the original List member. Overrides inherit its indexes, and
209
- `@index` on an override is a source error. A List field may override an abstract
210
- List getter.
211
-
212
- Use `.EnumCase` only when context fixes one enum type; otherwise write
213
- `EnumType.Case`. Constructor parameters supply this context for positional
214
- and named arguments, including substituted generic parameter types. Defaults
215
- and settings must be statically analyzable. An integer is not an enum option.
216
- Instance fields, static fields, and local value definitions report incompatible
217
- enum initializers at the authored expression before value lowering. Extracting
218
- a list or dictionary into a local preserves element validation. Errors point
219
- to the entry in the local definition, including missing `.Option` or
220
- `EnumType.Option` dictionary keys.
221
-
222
- An explicit construction must be assignable to its receiving type. For example,
223
- `List<Base>` accepts `new Derived()` when `Derived` inherits `Base`, but rejects
224
- an unrelated class at `new`. Generic arguments must also be compatible.
225
-
226
- A generic class without a declared constructor is constructed with its type
227
- arguments, such as `new Box<int> { Item = 4 }` or a target-typed
228
- `new() { Item = 4 }`. A `T` field is required unless the type argument is
229
- nullable, so `new Box<int?>()` may omit it. `new Box()` is rejected as an open
230
- generic Class. Inside the class, methods and field initializers construct with
231
- the class's own parameter, such as `public Bag<T> B = new Bag<T>();`. Such an
232
- initializer with fields or entries, like `new Bag<T> { N = 3 }`, runs per
233
- instance, since each instance closes `T` differently. A type
234
- argument that wraps the parameter, such as `new Bag<List<T>>()`, is rejected.
235
- Type arguments must satisfy `extends` constraints in bodies too. A written
236
- type argument list names every parameter, so `item is Tool<Asset>` is rejected
237
- when `Tool` declares two. A bare generic name, such as `item is Tool`, stays
238
- allowed.
239
- A type argument may be a delegate, such as `Box<NeoDelegate<int, int>>`, but
240
- not a `NeoAction`, which is never a value.
241
-
242
- Primary-constructor parameters can initialize generic collection fields directly:
243
-
244
- ```neo
245
- class Box<T>(List<T> value) {
246
- public List<T> Value = value;
247
- }
248
- ```
249
-
250
- The same rule applies to `Dictionary<string, T>`, nested collections, and
251
- stored instances such as `public static Box<int> Value = new([1, 2]);`.
252
-
253
- ## Member and relation rules
254
-
255
- Infer member kind and optionality from its type and nullability. Treat the
256
- initializer as its default. Omitted fields remain virtual and resolve through
257
- the instance's recorded constructor or variant. They track later source-default
258
- edits until that field is explicitly overridden; resetting an override makes it
259
- virtual again.
260
- Editing an inline child preserves the outer construction's call-site values for
261
- its other fields. The child keeps that construction while its variant selection
262
- matches; selecting a different variant gives it an independent recipe.
263
-
264
- Declare a lookup member as `Reference<T>`. `T` can be any value type except
265
- code types; see the NeoScript reference for handle and slot references.
266
- `public Reference<Config> Config` selects one row and reads as `Config`.
267
- `public Set<Reference<Config>> Configs` selects several and reads as
268
- `Set<Config>`. `public Reference<int> TrackedStat` names another member's int.
269
- `Reference<T>` also works as a constructor or method parameter, a collection
270
- element, a dictionary value, and an unbound generic argument such as
271
- `Box<Reference<int>>`. An initializer body for a single reference returns one
272
- row, never a set. `T` with `@settings(collection:)` is an error; the language
273
- server's quick fix respells it.
274
-
275
- `@settings(collection: Owner.Member)` binds a class reference to one
276
- collection. Without it the reference is unbound: it selects any stored row of
277
- `T` or a subclass, or any member slot of type `T`, anywhere in the project. A
278
- bound reference can't be a generic argument. The selected row's store decides
279
- writes. A write through it to a Save row lands, and to an asset row it errors.
280
- Its generated C# property is the read-only interface; call `TryWritable` to
281
- write.
282
-
283
- An unbound default can name its row by a unique index:
284
-
285
- ```neo
286
- public Reference<Item> StarterSeed = root.Assets.Items.Index.Slug["seed.carrot"]!;
287
- ```
288
-
289
- The receiver is a `root` path to a List, `.Index.<Name>` is a unique index on
290
- it, and the key is a string literal. Any other shape stays initializer code.
291
-
292
- A slot reference's default wraps a member path in `Reference()`. The path may
293
- use the same index segments. A plain value isn't a default, and `this` doesn't
294
- exist yet:
295
-
296
- ```neo
297
- public Reference<int> TrackedStat = Reference(root.Player.Gold);
298
- public Reference<string> Greeting = Reference(root.Npcs.Index.Slug["mira"]!.Greeting);
299
- public Reference<NeoVariant<Tree>> Look = Reference(root.Garden.Felled);
300
- ```
301
-
302
- A `NeoVariant<T>` member's own default stays the bare variant path. A
303
- `Reference<NeoVariant<T>>` names a member that holds a variant, so its default
304
- is a `Reference()` member path.
305
-
306
- A `Reference<T>` member satisfies an interface `T Foo { get; }` or
307
- `{ get; set; }` and an abstract `T Foo { get; }`.
308
-
309
- Constructor and function parameters use `Set<T>` for several selections.
310
-
311
- ### Interface members
312
-
313
- An interface types a member anywhere a class does: a field, a nullable,
314
- `readonly`, or `static` field, a `List`, `Set`, or `Dictionary` entry, a
315
- generic argument, a parameter, and `Reference<I>`. Each row holds its own
316
- class, which must implement the interface.
317
-
318
- ```neo
319
- interface IContainer<T extends Item> {
320
- List<T> Contents { get; }
321
- }
322
-
323
- class Crate<T extends Item> : IContainer<T> {
324
- public List<T> Contents = [];
325
- }
326
-
327
- class Player {
328
- public IContainer<Seed>? Pouch = new Crate<Seed>();
329
- public List<IContainer<Seed>> Racks = [];
330
- }
331
- ```
332
-
333
- - Construct a class, never the interface: `new IContainer<Seed>()` is an error.
334
- Defaults and constructor arguments name the concrete class.
335
- - An open class can sit in an interface slot only when the interface fills
336
- every class parameter, as `Crate<T> : IContainer<T>` does. Otherwise store a
337
- closed subclass.
338
- - A class implements a given interface once, counting its `extends` chain.
339
- `IContainer<Seed>` and `IContainer<Tool>` on one class is an error.
340
- - An interface argument is a type parameter in scope or a type with none
341
- inside it. `IContainer<List<T>>` is an error.
342
- - `x is IContainer<Seed>` compares the arguments the row implements the
343
- interface with. A generic interface needs its arguments in `is`.
344
- - `NeoVariant<I>` is an error, since variants belong to a class. `@index` on a
345
- collection of interface entries is an error; `@column` can name an interface
346
- property.
347
- - Interfaces carry no storage. A row fits a slot only when its class allows
348
- the storage the row lives in. The compiler refuses an assignment that can't
349
- fit, and push and commit refuse an authored row that doesn't fit.
350
- - A variant or animation override that puts a different class in an
351
- interface slot replaces the slot's row.
352
- - A generic parameter can be constrained by an interface,
353
- `T extends IContainer<Seed>`, and an interface can be a generic argument
354
- where the parameter's constraint admits it.
355
-
356
- Generated C# types the slot by the generated interface, so a class that
357
- doesn't implement it doesn't compile. Writing a C# class that implements the
358
- interface but isn't a Neo class throws, naming the slot. So does writing a row
359
- the interface doesn't determine.
360
-
361
- ### Set members
362
-
363
- `Set<T>` is an insertion-ordered collection that can't hold duplicates. The
364
- element picks the storage:
365
-
366
- - An enum `Set<Trait>` is a multi-select Enum.
367
- - `Set<Reference<T>>` is a multi-select lookup.
368
- - `Set<NeoDialogueReference>` is a multi-select dialogue lookup.
369
- - Any other element makes an owned Set whose entries are rows, like a List.
370
-
371
- Constructor mutations such as `Bag.Add(7)` keep owned collection entries as
372
- backing rows during export and sparse replay. Existing entries retain their
373
- identities; added entries receive stable identities under the instance.
374
-
375
- An owned Set holds `bool`, `int`, `float`, `decimal`,
376
- `@settings(format: .Plain) string`, vectors, `Color`, sprites, audio clips,
377
- a class, or an interface. Elements can't be nullable, collections, variants,
378
- delegates, or actions. A class or interface entry is unique by row, a value
379
- entry by its value. `@index` and `@column` work as on a List; `kind:` is an error, since a
380
- Set is always insertion-ordered.
381
-
382
- ```neo
383
- @index(member: Slug, kind: .Unique)
384
- public Set<Quest> ActiveQuests = [];
385
- public Set<int> LuckyNumbers = [3, 7];
386
- public Set<Trait> Traits = [.Brave];
387
- ```
388
-
389
- `selection:` is an error everywhere: `Set<T>` selects many and `T` selects
390
- one. The language server's quick fix rewrites `@settings(selection: .Multi)`
391
- to a `Set<…>` type. A default literal that repeats a value fails `push`, and a
392
- `List<Trait>` stays an ordered List of enum values. Queries over a Set, such
393
- as `Where` and `OrderBy`, return a Set; `ToSet()` turns a List into one.
394
-
395
- Declare a multicast event as `NeoAction<…>`. Its value is an ordered listener
396
- set, so give it a bracketed list of method groups — never a lambda, and never
397
- a bare method group — and never mark it `required`: the rest state is the
398
- empty set.
399
-
400
- ```neo
401
- public NeoAction<int> OnDamaged = [PlayHitFlash, ApplyDamage];
402
- ```
403
-
404
- A nested field initializer such as `Watcher = new() { OnTally = [Handle] }`
405
- can target an inherited generic method on its enclosing instance. The listener
406
- uses that owning instance, including when declaration defaults are materialized.
407
- Explicit receiver targets keep their captured instance.
408
-
409
- An instance-bound listener reachable through a stable project root keeps its
410
- receiver path, for example `public NeoAction<int> OnDamaged =
411
- [root.Assets.OnDamage];`. Pull refuses a bound receiver that has no
412
- deterministic `root.*` path instead of emitting a source expression that would
413
- silently change the target.
414
-
415
- Subscribe and unsubscribe with `+=` and `-=` against the member reference
416
- itself; both are durable writes deduplicated by target identity, so
417
- re-running a subscription cannot double-subscribe. Invoke it as a statement —
418
- an action is never a value, never a return type, and never assigned with `=`.
419
-
420
- Implement a system lifecycle interface to run code when the runtime does.
421
- `INeoOnLoad` and `INeoOnUnload` run when an instance becomes live and when it
422
- stops being live. The `INeoUnity*` interfaces, such as `INeoUnityUpdate` and
423
- `INeoUnityOnTriggerEnter2D`, run for a rendered `NeoObjectBase` in play mode.
424
- Their member names, such as `OnLoad` and `Update`, are reserved: declare one
425
- only as the public, immediate instance method that implements its interface,
426
- and never mark it `@effect`. Subscribe Session actions in `OnLoad` and remove
427
- them in `OnUnload`. React to data changes with `@effect`, and keep `Update`
428
- for work that really happens every frame.
429
-
430
- Keep generic project relations as typed top-level declarations in
431
- `Relations.neo`. Let the compiler derive identities for owned descriptors and
432
- concrete generic bindings.
433
-
434
- For world layer links, instantiate a project-authored descendant of
435
- `NeoTileLayerLink` or `NeoObjectLayerLink` that resolves exactly one
436
- `targetLayer` relation. The relation is the complete binding; do not add
437
- value-level target metadata.
438
-
439
- ### Function overloads
440
-
441
- A class may declare several functions with one name when their parameter
442
- types differ:
443
-
444
- ```neo
445
- class Inventory {
446
- public int Add(Item item) { return 1; }
447
- public int Add(Item item, int count) { return count; }
448
- public int Add(List<Item> items) { return items.Count; }
449
- }
450
- ```
451
-
452
- A class's overload set for a name holds every function of that name it
453
- declares or inherits. A derived class adding `Add(Item item, int count)` next
454
- to an inherited `Add(Item item)` overloads it. Static and instance functions,
455
- NSFunctions and native functions, and generic and non-generic functions mix
456
- freely.
457
-
458
- Every two functions in a set need different parameter types after dropping
459
- nullability, substituting base type arguments, and renaming function type
460
- parameters by position. So `Add(Item)` and `Add(Item?)` collide, and so do
461
- `F()` and `F<T>()`. A `Find<T = Item>(T value)` occupies `Find(Item)`.
462
-
463
- At most one function per name and class may be dispatched: `abstract`,
464
- `virtual`, `override`, or an interface implementation. Overrides target that
465
- one function. Interfaces can't overload, so an interface declares each
466
- function name once. When several class functions share an interface
467
- function's name, the one whose parameters match implements it.
468
-
469
- `Equals` and names a platform class or interface declares, such as
470
- `OnTriggerEnter2D` and lifecycle hooks, can't be overloaded. A function still
471
- can't share a name with a field, property, getter, or action.
472
-
473
- Overloads generate C# overloads with the same name. A plain overload stores
474
- under the key `Name#<memberId>`. A dispatched or lone function keeps the key
475
- `Name`, so a project without overloads stores exactly what it did before.
476
-
477
- ## Computed initializers and declared constructors
478
-
479
- Inherited member initializers read parameters from the constructor that declared
480
- them, including when export materialization clones their bodies. A storage-only
481
- override keeps that original constructor scope.
482
-
483
- Use an expression initializer when a member default must be evaluated during
484
- construction rather than stored as a closed literal. It is replayed for omitted
485
- members in push, web, and SDK contexts. A stored override remains pinned until
486
- it is reset.
487
-
488
- Operator expressions are valid member defaults and store as computed
489
- initializers, the same way calls do — `public float Derived =
490
- Flags.Start / Flags.Scale;` is fine. Positions that store no rows still
491
- require literals: dictionary keys must be string literals or enum option
492
- literals for enum-keyed dictionaries, and Color components must be numeric
493
- literals. In a static member or its collection entries, a vector with a
494
- computed component, such as `new Vector2(Probe.X, 1)`, stores as a computed
495
- initializer. Instance member defaults, including their collection entries,
496
- still require numeric literal vector components. When a computed list or
497
- dictionary entry carries an authored `@id`, parenthesize the expression —
498
- `@id("row") (A.B / A.C)` — so the annotation names the row rather than its left
499
- operand.
500
- A push that assigns such a row's ID adds those parentheses itself.
501
-
502
- Numeric literals do not use C# suffixes: write `0` or `0.5`, not `0f` or
503
- `0.5f`. The declared/contextual type determines whether a literal is stored as
504
- an int, float, or decimal.
505
-
506
- A direct field or property read is computed too, whether it is local,
507
- qualified static, or reached through a stable root path. For example,
508
- `protected int Slots = FeatureFlags.StartInventorySize;` evaluates the current
509
- static field or getter whenever a new instance is constructed; it is not baked
510
- into a persisted literal during push.
511
- Root paths such as `root.Save.World.Time.Year` remain executable even when
512
- `World` and `Time` exist only through construction defaults. This also applies
513
- inside `new() { Year = root.Save.World.Time.Year }`: the child field retains
514
- its initializer and reads the root when the instance is constructed.
515
-
516
- Declare overloadable constructors as members when each constructor body owns
517
- its parameter scope:
518
-
519
- ```neo
520
- class Foo {
521
- public string Bar;
522
- public bool IsFun;
523
-
524
- public Foo(string bar, bool isFun) {
525
- this.Bar = bar;
526
- this.IsFun = isFun;
527
- }
528
- }
529
-
530
- Foo value = new(bar: "BAR", isFun: true) { IsFun = false };
531
- ```
532
-
533
- Constructor parameters are visible only in that constructor body. Do not read
534
- them from member initializers. Each declared constructor must settle every
535
- required member on its own; a call-site block cannot repair an invalid
536
- constructor declaration.
537
-
538
- ## Parameter default values
539
-
540
- Default a parameter with `= <constant>` on class headers, declared
541
- constructors, native functions, NSFunctions, and interface functions:
542
-
543
- ```neo
544
- class Foo(string bar = "BAR", int volume = 3) {
545
- public string Bar = bar;
546
-
547
- public string Shout(string text = "hey", bool loud = true) {
548
- return loud ? text.ToUpper() : text;
549
- }
550
- }
551
-
552
- Foo a = new(); // fully defaulted header: all defaults apply
553
- Foo b = new(bar: "bar"); // volume fills from its default
554
- ```
555
-
556
- Use only constants: bool, int, float, and decimal literals (optional leading
557
- `-`), string literals without interpolation, leading-dot enum options
558
- (`.North`), and `null` on a nullable parameter. Place every defaulted
559
- parameter after every non-defaulted one. `Type? name` stays nullable-and-
560
- mandatory unless it also declares a default — nullability and defaultedness
561
- are independent.
562
-
563
- Omit defaulted arguments at call sites: function calls bind positionally and
564
- drop only a trailing suffix; constructor calls name a subset that covers
565
- every non-defaulted parameter. When several overloads match, the one filling
566
- in the fewest defaults wins, and a residual tie is a declaration-time error.
567
- Implementations of an interface function restate its defaults exactly.
568
-
569
- Do not default parameters of deferred functions, delegates, actions,
570
- closures, or overrides. Enum defaults store the option id, so renaming the
571
- option changes the emitted `.Option` spelling without a record change.
572
-
573
- Editing a stored object's constructor arguments rebuilds it under the same
574
- identity. Initializers that read that object, including collection projections,
575
- wait for its replacement before evaluating its members.
576
-
577
- Stored objects created with an implicit constructor may have required fields
578
- settled in their object body. Keep that body when editing initializer behavior
579
- or adding a parameterless constructor; an empty stored argument list does not
580
- require inventing constructor arguments.
581
-
582
- ## Required constructors and settlement
583
-
584
- Put one required constructor on the class header when the entire class body
585
- needs the parameters:
586
-
587
- ```neo
588
- class Foo(string bar, bool isFun) {
589
- public string Bar = bar;
590
- public bool IsFun;
591
-
592
- init {
593
- if (isFun) {
594
- IsFun = true;
595
- } else {
596
- IsFun = false;
597
- }
598
- }
599
- }
600
-
601
- Foo value = new(bar: "BAR", isFun: true) { IsFun = false };
602
- ```
603
-
604
- Header parameters are in scope throughout the instance members of the class.
605
- Field initializers, collection entries, object initializers, and lambdas read
606
- them per construction, so `public NeoDelegate<int> Value = () => n;` returns
607
- each instance's own `n`. A lambda can also read `this`, as in
608
- `() => n + this.M`; the rest of an initializer can't. Static members have no
609
- construction and can't read header parameters. Permit at most one `init`
610
- block. A header constructor disables implicit parameterless `new` and forbids
611
- additional constructor overloads. Its derived constructor identity is not
612
- authored with `@id`.
613
-
614
- Treat a non-null member with no default as required. Treat a nullable member
615
- with no default as implicitly `null` and therefore settled:
616
-
617
- ```neo
618
- class Result {
619
- public string Value; // required
620
- public bool Ok = true; // settled by default
621
- public string? Note; // settled by implicit null
622
- }
623
- ```
624
-
625
- Accept settlement only from:
626
-
627
- 1. The member's own initializer expression.
628
- 2. Assignment on every reachable path in `init` or a constructor body.
629
- 3. A call-site initializer entry, in value source and NeoScript bodies alike,
630
- when using an otherwise valid construction path.
631
-
632
- Do not infer settlement from a side effect inside another function. Require
633
- full `if`/`else` coverage when an assignment is conditional.
634
-
635
- Apply construction in this order: base construction, member initializers,
636
- `init` or declared-constructor body, then the call-site initializer block. The
637
- call-site block wins when it refines an earlier value.
638
-
639
- ## Inheritance construction
640
-
641
- Treat the base clause as a construction expression:
642
-
643
- ```neo
644
- class Base(string name) {
645
- public string Name = name;
646
- }
647
-
648
- class Child(string name, int rank) : Base(name) {
649
- public int Rank = rank;
650
- }
651
- ```
652
-
653
- Call a declared base constructor with `: Base(args)`. When a base has no
654
- constructor, use a base initializer block to settle required inherited members:
655
-
656
- ```neo
657
- class Base {
658
- public string Name;
659
- public bool Enabled = true;
660
- }
661
-
662
- class Child(string name) : Base {
663
- Name = name,
664
- Enabled = false
665
- } {
666
- }
667
- ```
668
-
669
- A header base clause with no arguments, such as `: Base()` or `: Base`,
670
- runs a zero-argument or fully defaulted base constructor when available.
671
- Otherwise, if the base has only declared constructors, it uses the member
672
- initializers and base block without running a base constructor. The base block
673
- must settle any remaining required members. A required base constructor cannot
674
- be skipped. A declared constructor's `: base()` must call a base constructor
675
- when the base declares one.
676
-
677
- Bind generic base arguments explicitly when needed. Reject a subclass that
678
- omits required base arguments or leaves any inherited required member
679
- unsettled.
680
-
681
- Generic constraints may construct another generic from a parameter in the
682
- same declaration. This preserves the relationship between the parameters in
683
- source checks, stored initializer replay, and generated C#:
684
-
685
- ```neo
686
- class PlacedItem<
687
- TData extends InventoryStackData,
688
- TItem extends InventoryItem<TData>
689
- >(TItem item) {
690
- public TData Data = item.DefaultData.Clone();
691
- }
692
- ```
693
-
694
- Object initializer blocks can set `readonly` members of the newly constructed
695
- instance, including nested `new` expressions. Ordinary assignments after
696
- construction remain subject to storage and `readonly` restrictions.
697
-
698
- Runtime construction never supplies an Immutable member: an initializer block,
699
- constructor body, base-clause argument, or generated C# factory cannot set one.
700
- The value is never stored with the instance. When the declaration, including
701
- every row it owns, reads neither `this` nor a constructor argument, and no
702
- value of its type holds a writable member, the member is a constant and every
703
- instance shares one value. Otherwise each instance evaluates the declared
704
- initializer with its own `this` and constructor arguments, again after a
705
- reload. A method group such as `selector: SelectTorso` binds `this`.
706
- `Clone()` shares an authored Immutable value or a constant and re-evaluates a
707
- per-instance one. A class with a required Immutable member and
708
- no initializer cannot be constructed at runtime; only authored values construct
709
- it.
710
-
711
- Populated unordered member defaults, such as `@settings(kind: .Unordered)` with
712
- `List<Entry> Entries = [new Entry()]`, are constructed by an initializer for
713
- each instance. A declaration has no container value row to own unordered entries.
714
- Nested unordered lists already have container rows and retain their stored values.
715
- A field, collection entry, or dictionary value can't place a value that
716
- something else already owns, such as `Bag = [Shared.Pattern]` or a constructor
717
- parameter stored in an ordinary field. Call `Clone()` or use `Reference`.
718
-
719
- A lookup's declared class must be assignable to the entry class of its
720
- `@settings(collection: Owner.Member)` collection. A subclass may narrow the
721
- selection. Constructor arguments and member initializers must select compatible
722
- targets too. An `NPC` lookup cannot select from `List<Quest>`, even when its
723
- initializer is typed `NPC`; status reports the conflicting collection annotation.
724
-
725
- Generic constructions can nest the same generic class, including through
726
- collections, such as `Box<Box<int>>` or `Box<List<Box<int>>>`. Each `new` uses
727
- its own type arguments during first push and stored replay. Editing a nested
728
- constructor argument rebuilds its projected fields in place, including fields
729
- stored inside a packed parent; push followed by pull leaves a clean checkout.
730
-
731
- Sparse children with their own declared constructor replay that constructor
732
- when reconciling edits, including when passed as an aggregate argument.
733
-
734
- Schema export retains a constructed default's generic arguments, including
735
- nested collection references. For example, a `NeoAnimationClip<ArmPart>`
736
- default constructs `new NeoAnimationClip<ArmPart>` and its frames reference
737
- `List<NeoAnimationFrame<ArmPart>>`.
738
-
739
- When a construction leaves a required member unsettled, CLI diagnostics point to
740
- `new` and include the member declaration as a related location. Fix that
741
- construction before pushing. Valid root initializers still participate in
742
- reference checking, so an unrelated root error does not imply their values are
743
- missing or deleted. Independent reference errors remain visible.
744
-
745
- Object initializer assignments use commas. The last comma is optional; the
746
- semicolon terminating the enclosing declaration is valid:
747
-
748
- ```neo
749
- AudioSettings AUDIO_SETTINGS = new() {
750
- Main = new AudioMixerGroupSettingsFolder("Main", "Master", "MasterVolume", []),
751
- };
752
- ```
753
-
754
- A semicolon after `Main = ...` inside the initializer is invalid. Editor and CLI
755
- diagnostics highlight that separator in the authored file, including global,
756
- static member, and protected root initializers. Statement semicolons inside
757
- function or delegate bodies remain valid.
758
-
759
- ## Stored tuples
760
-
761
- Declare fixed positions with ordinary types and optional labels:
762
-
763
- ```neo
764
- public (int Count, bool Enabled) State = (3, true);
765
- public (int, (string Label, int Count))? Detail = null;
766
- ```
767
-
768
- Each position has a derived member descriptor. Do not author descriptor IDs.
769
- Changing a label preserves its position and identity. Changing arity or moving
770
- stored positions requires migrating existing values. A required tuple without
771
- a default must be supplied during construction; it has no automatic zero tuple.
772
- Owned class and collection elements keep the ordinary single-owner rule. Use
773
- an explicit reference or clone when another stored value already owns them.