@neocompose/cli 0.87.2 → 0.89.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 +23 -0
- package/README.md +14 -0
- package/dist/neo-runtime.mjs +11153 -3060
- package/dist/test-worker.mjs +15214 -9707
- 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/extensions.md +46 -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 +138 -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 +113 -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 +63 -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,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.
|