@neocompose/cli 0.87.2 → 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 +12 -0
- package/README.md +14 -0
- package/dist/neo-runtime.mjs +3288 -724
- package/dist/test-worker.mjs +2445 -114
- 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
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Initializers
|
|
2
|
+
|
|
3
|
+
Member defaults: what they may read, when they compute, generic and
|
|
4
|
+
Immutable construction, and edits to stored constructions.
|
|
5
|
+
|
|
6
|
+
## Initializer scope
|
|
7
|
+
|
|
8
|
+
- Field initializers may read their class's static members unqualified,
|
|
9
|
+
inherited ones included: `public int First = Run();`,
|
|
10
|
+
`public static int Copy = Number;`.
|
|
11
|
+
- Eager initializers can't read `this` or instance members. Defer such reads
|
|
12
|
+
in a lambda or method group. `List<int> Values = [this.Number + 1]` is an
|
|
13
|
+
error; `() => this.Number`, `Read`, and `new Callback { Read = Read }` are
|
|
14
|
+
fine.
|
|
15
|
+
- A static naming another static copies a value
|
|
16
|
+
(`public static int B = Holder.A;`). Class and collection statics already
|
|
17
|
+
own their value; use `Clone()` or `Reference`.
|
|
18
|
+
- Inherited initializers read parameters from the constructor that declared
|
|
19
|
+
them; a storage-only override keeps that scope.
|
|
20
|
+
- Omitted constructor fields use their defaults. Writable collection defaults
|
|
21
|
+
belong to each instance. Fields without defaults stay absent, and `null`
|
|
22
|
+
arguments to generated member-wise constructors mean omission.
|
|
23
|
+
|
|
24
|
+
## Computed initializers
|
|
25
|
+
|
|
26
|
+
A default that isn't a closed literal stores as a computed initializer and
|
|
27
|
+
replays for omitted members in push, web, and SDK. A stored override stays
|
|
28
|
+
pinned until reset.
|
|
29
|
+
|
|
30
|
+
- Calls, operators (`public float Derived = Flags.Start / Flags.Scale;`), and
|
|
31
|
+
direct field or property reads (local, qualified static, or via a stable
|
|
32
|
+
root path) are computed. `protected int Slots = FeatureFlags.StartInventorySize;`
|
|
33
|
+
reads the current value at each construction instead of baking it in.
|
|
34
|
+
- Root paths such as `root.Save.World.Time.Year` stay executable even when
|
|
35
|
+
`World` and `Time` exist only through construction defaults, including
|
|
36
|
+
inside `new() { Year = root.Save.World.Time.Year }`.
|
|
37
|
+
- Literal-only positions: dictionary keys (string literals, or option literals
|
|
38
|
+
for enum keys) and Color components. Instance member defaults need numeric
|
|
39
|
+
literal vector components; statics may compute them
|
|
40
|
+
(`new Vector2(Probe.X, 1)`).
|
|
41
|
+
- Parenthesize a computed list or dictionary entry that has an `@id`:
|
|
42
|
+
`@id("row") (A.B / A.C)`. Push adds these parentheses when it assigns IDs.
|
|
43
|
+
|
|
44
|
+
## Generic construction
|
|
45
|
+
|
|
46
|
+
- A generic class without a declared constructor is built with its type
|
|
47
|
+
arguments: `new Box<int> { Item = 4 }` or target-typed `new() { Item = 4 }`.
|
|
48
|
+
`new Box()` is an open generic and rejected. A `T` field is required unless
|
|
49
|
+
`T` is nullable (`new Box<int?>()` may omit it).
|
|
50
|
+
- Inside the class, construct with its own parameter
|
|
51
|
+
(`public Bag<T> B = new Bag<T>();`); one with fields or entries runs per
|
|
52
|
+
instance. Wrapping the parameter (`new Bag<List<T>>()`) is rejected.
|
|
53
|
+
- Type arguments must satisfy `extends` in bodies too. A written argument list
|
|
54
|
+
names every parameter (`item is Tool<Asset>` fails when `Tool` has two); a
|
|
55
|
+
bare `item is Tool` is allowed.
|
|
56
|
+
- Delegates may be type arguments (`Box<NeoDelegate<int, int>>`); `NeoAction`
|
|
57
|
+
may not.
|
|
58
|
+
- Header parameters may initialize generic collection fields
|
|
59
|
+
(`class Box<T>(List<T> value) { public List<T> Value = value; }`), also for
|
|
60
|
+
`Dictionary<string, T>`, nested collections, and stored instances such as
|
|
61
|
+
`public static Box<int> Value = new([1, 2]);`.
|
|
62
|
+
- Collection type arguments such as `new Box<List<int>>([1, 2])` work even if
|
|
63
|
+
no field declares that collection type. Every authored type argument is
|
|
64
|
+
validated, nested ones included: `new Box<List<MissingType>>()` fails.
|
|
65
|
+
- Generics may nest (`Box<Box<int>>`, `Box<List<Box<int>>>`); each `new` keeps
|
|
66
|
+
its own type arguments.
|
|
67
|
+
|
|
68
|
+
## Immutable members
|
|
69
|
+
|
|
70
|
+
Runtime construction never supplies an Immutable member: no initializer
|
|
71
|
+
block, constructor body, base argument, or generated C# factory can set one,
|
|
72
|
+
and it is never stored with the instance.
|
|
73
|
+
|
|
74
|
+
- If the declaration and every row it owns read neither `this` nor
|
|
75
|
+
constructor arguments, and no value of its type holds a writable member, it
|
|
76
|
+
is a constant shared by every instance.
|
|
77
|
+
- Otherwise each instance evaluates the initializer with its own `this` and
|
|
78
|
+
arguments, again after reload. A method group such as
|
|
79
|
+
`selector: SelectTorso` binds `this`.
|
|
80
|
+
- `Clone()` shares an authored or constant Immutable value and re-evaluates a
|
|
81
|
+
per-instance one.
|
|
82
|
+
- A class with a required Immutable member and no initializer can only be
|
|
83
|
+
constructed by authored values.
|
|
84
|
+
|
|
85
|
+
## Stored constructions
|
|
86
|
+
|
|
87
|
+
- Editing a stored object's constructor arguments rebuilds it in place under
|
|
88
|
+
the same identity; see [defaults and overrides](../values/defaults-and-localization.md#construction-rebuilds).
|
|
89
|
+
Initializers that read it wait for the replacement.
|
|
90
|
+
- Objects built by an implicit constructor may settle required fields in
|
|
91
|
+
their object body. Keep that body when adding a parameterless constructor;
|
|
92
|
+
an empty argument list needs no invented arguments.
|
|
93
|
+
- Populated unordered defaults (`@settings(kind: .Unordered)` with
|
|
94
|
+
`List<Entry> Entries = [new Entry()]`) are built per instance by an
|
|
95
|
+
initializer. Nested unordered lists keep their stored values.
|
|
96
|
+
- A field, entry, or dictionary value can't place something already owned
|
|
97
|
+
elsewhere (`Bag = [Shared.Pattern]`, or a constructor parameter stored in an
|
|
98
|
+
ordinary field). Use `Clone()` or `Reference`.
|
|
99
|
+
- You may remove a constructor and replace every call in the same edit; push
|
|
100
|
+
prepares the new calls with the schema change.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Member kinds
|
|
2
|
+
|
|
3
|
+
Interface-typed members, sets, actions, lifecycle hooks, and stored tuples.
|
|
4
|
+
`Reference<T>` lookups are in [references](../values/references.md).
|
|
5
|
+
|
|
6
|
+
## Interface-typed members
|
|
7
|
+
|
|
8
|
+
An interface may type a field (nullable, `readonly`, or `static` too), a
|
|
9
|
+
`List`/`Set`/`Dictionary` entry, a generic argument, a parameter, or
|
|
10
|
+
`Reference<I>`. Each row holds its own class, which must implement it.
|
|
11
|
+
|
|
12
|
+
```neo
|
|
13
|
+
interface IContainer<T extends Item> {
|
|
14
|
+
List<T> Contents { get; }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
class Crate<T extends Item> : IContainer<T> {
|
|
18
|
+
public List<T> Contents = [];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
class Player {
|
|
22
|
+
public IContainer<Seed>? Pouch = new Crate<Seed>();
|
|
23
|
+
public List<IContainer<Seed>> Racks = [];
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- Construct a class, never the interface (`new IContainer<Seed>()` is an
|
|
28
|
+
error).
|
|
29
|
+
- An open class fits an interface slot only when the interface fills every
|
|
30
|
+
class parameter, as `Crate<T> : IContainer<T>` does; otherwise store a
|
|
31
|
+
closed subclass.
|
|
32
|
+
- A class implements a given interface once across its `extends` chain.
|
|
33
|
+
Interface arguments are in-scope type parameters or types containing none
|
|
34
|
+
(`IContainer<List<T>>` is an error). `is` checks need the arguments.
|
|
35
|
+
- `NeoVariant<I>` is an error. `@index` on interface entries is an error;
|
|
36
|
+
`@column` may name an interface property.
|
|
37
|
+
- Interfaces carry no storage. A row fits only when its class allows the
|
|
38
|
+
storage it lives in; the compiler, push, and commit refuse misfits.
|
|
39
|
+
- A variant or animation override that puts a different class in an
|
|
40
|
+
interface slot replaces the slot's row.
|
|
41
|
+
- Constraints may be interfaces (`T extends IContainer<Seed>`).
|
|
42
|
+
- Generated C# types the slot by the generated interface. Assigning a non-Neo
|
|
43
|
+
C# implementation, or a row the interface doesn't determine, throws and
|
|
44
|
+
names the slot.
|
|
45
|
+
|
|
46
|
+
## Sets
|
|
47
|
+
|
|
48
|
+
`Set<T>` is insertion-ordered and holds no duplicates. The element decides
|
|
49
|
+
storage:
|
|
50
|
+
|
|
51
|
+
- enum `Set<Trait>`: multi-select enum
|
|
52
|
+
- `Set<Reference<T>>`: multi-select lookup
|
|
53
|
+
- `Set<NeoDialogueReference>`: multi-select dialogue lookup
|
|
54
|
+
- anything else: an owned set of rows, like a List
|
|
55
|
+
|
|
56
|
+
```neo
|
|
57
|
+
@index(member: Slug, kind: .Unique)
|
|
58
|
+
public Set<Quest> ActiveQuests = [];
|
|
59
|
+
public Set<int> LuckyNumbers = [3, 7];
|
|
60
|
+
public Set<Trait> Traits = [.Brave];
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- Owned sets hold `bool`, `int`, `float`, `decimal`,
|
|
64
|
+
`@settings(format: .Plain) string`, vectors, `Color`, sprites, audio clips,
|
|
65
|
+
classes, or interfaces. Not nullables, collections, variants, delegates, or
|
|
66
|
+
actions. Class entries are unique by row, values by value.
|
|
67
|
+
- `@index`/`@column` work as on a List; `kind:` is an error.
|
|
68
|
+
- `selection:` is an error everywhere: `Set<T>` selects many, `T` one. The
|
|
69
|
+
language server's quick fix rewrites `@settings(selection: .Multi)` to a
|
|
70
|
+
`Set<...>` type.
|
|
71
|
+
- A default literal that repeats a value fails push. `List<Trait>` stays an
|
|
72
|
+
ordered list.
|
|
73
|
+
- Constructor mutations such as `Bag.Add(7)` keep entries as backing rows;
|
|
74
|
+
existing entries keep their identities.
|
|
75
|
+
- Use `Set<T>` parameters for several selections.
|
|
76
|
+
|
|
77
|
+
## Actions
|
|
78
|
+
|
|
79
|
+
Declare multicast events as `NeoAction<...>` with a bracketed list of method
|
|
80
|
+
groups: `public NeoAction<int> OnDamaged = [PlayHitFlash, ApplyDamage];`.
|
|
81
|
+
Invocation, subscription, and `Clear()` rules are in
|
|
82
|
+
[delegates and listeners](../neoscript/delegates-and-listeners.md#actions).
|
|
83
|
+
|
|
84
|
+
## Lifecycle interfaces
|
|
85
|
+
|
|
86
|
+
- `INeoOnLoad`/`INeoOnUnload` run when an instance becomes live and stops
|
|
87
|
+
being live.
|
|
88
|
+
- `INeoUnity*` interfaces (`INeoUnityUpdate`, `INeoUnityOnTriggerEnter2D`, ...)
|
|
89
|
+
run for a rendered `NeoObjectBase` in play mode.
|
|
90
|
+
- Their member names (`OnLoad`, `Update`, ...) are reserved: declare one only
|
|
91
|
+
as the public immediate instance method implementing its interface, never
|
|
92
|
+
`@effect`.
|
|
93
|
+
- Subscribe Session actions in `OnLoad` and remove them in `OnUnload`. Use
|
|
94
|
+
`@effect` for data reactions and keep `Update` for true per-frame work.
|
|
95
|
+
|
|
96
|
+
## Stored tuples
|
|
97
|
+
|
|
98
|
+
```neo
|
|
99
|
+
public (int Count, bool Enabled) State = (3, true);
|
|
100
|
+
public (int, (string Label, int Count))? Detail = null;
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Each position has a derived descriptor; author no descriptor IDs. Relabeling
|
|
104
|
+
keeps identity. Changing arity or moving positions needs a migration. A
|
|
105
|
+
required tuple without a default must be supplied at construction. Owned
|
|
106
|
+
class and collection elements follow the single-owner rule. Tuple
|
|
107
|
+
expressions are in [types and narrowing](../neoscript/types-and-narrowing.md#tuples).
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Overloads
|
|
2
|
+
|
|
3
|
+
## Declaring
|
|
4
|
+
|
|
5
|
+
```neo
|
|
6
|
+
class Inventory {
|
|
7
|
+
public int Add(Item item) { return 1; }
|
|
8
|
+
public int Add(Item item, int count) { return count; }
|
|
9
|
+
public int Add(List<Item> items) { return items.Count; }
|
|
10
|
+
}
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
- A class's overload set holds every same-named function it declares or
|
|
14
|
+
inherits. Static and instance, NSFunction and native, generic and
|
|
15
|
+
non-generic functions mix freely.
|
|
16
|
+
- Any two need different parameter types after dropping nullability,
|
|
17
|
+
substituting base type arguments, and renaming function type parameters by
|
|
18
|
+
position. `Add(Item)` and `Add(Item?)` collide, so do `F()` and `F<T>()`;
|
|
19
|
+
`Find<T = Item>(T value)` occupies `Find(Item)`.
|
|
20
|
+
- At most one function per name and class may be dispatched (`abstract`,
|
|
21
|
+
`virtual`, `override`, or an interface implementation). Overrides target
|
|
22
|
+
that one.
|
|
23
|
+
- Interfaces can't overload. When several class functions share an interface
|
|
24
|
+
function's name, the one with matching parameters implements it.
|
|
25
|
+
- `Equals` and names a platform type declares (lifecycle hooks,
|
|
26
|
+
`OnTriggerEnter2D`) can't be overloaded. A function can't share a name with
|
|
27
|
+
a field, property, getter, or action.
|
|
28
|
+
- Overloads generate C# overloads. A plain overload stores under the key
|
|
29
|
+
`Name#<memberId>`; a dispatched or lone function keeps `Name`.
|
|
30
|
+
|
|
31
|
+
## Calling
|
|
32
|
+
|
|
33
|
+
Resolution happens at compile time over every overload the receiver's type
|
|
34
|
+
sees. A type qualifier sees static ones, an instance receiver instance ones.
|
|
35
|
+
|
|
36
|
+
- An overload applies when the argument count fits its required plus default
|
|
37
|
+
parameters and each argument converts. Lambdas, method groups, `null`,
|
|
38
|
+
collection literals, and `.North` apply by shape.
|
|
39
|
+
- Closer parameter types win: exact beats conversion, `Bag` beats `Item` when
|
|
40
|
+
`Bag : Item`. Ties go to the overload filling no defaults, then the
|
|
41
|
+
non-generic one. Anything else is ambiguous: `Scale(float)` and
|
|
42
|
+
`Scale(decimal)` can't take an `int`; write `2.0` or `2m`.
|
|
43
|
+
- Generic overloads take explicit type arguments or infer them. One whose
|
|
44
|
+
arguments can't be inferred isn't a candidate: beside `F(int a, string b)`,
|
|
45
|
+
`F<T>(T a, T b)` takes `F(1, 2)` but not `F(1, "a")`.
|
|
46
|
+
- Method groups pick the overload exactly matching the target delegate type
|
|
47
|
+
in assignments, `+=`, `OnChanged`, `@listeners`, and delegate arguments.
|
|
48
|
+
`var f = Read` is an error for an overloaded `Read`.
|
|
49
|
+
- Tests and eval name a delegate type or signature; see
|
|
50
|
+
[testing](../neoscript/testing-and-migrations.md).
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Dialogues
|
|
2
|
+
|
|
3
|
+
NeoFlow `.neoflow` files, dialogue groups, labels, and triggering dialogues
|
|
4
|
+
from NeoScript.
|
|
5
|
+
|
|
6
|
+
## Dialogue files
|
|
7
|
+
|
|
8
|
+
Each `.neoflow` file has exactly one top-level, non-generic
|
|
9
|
+
`sealed class Name : NeoDialogue` that overrides `Trigger Trigger` once and
|
|
10
|
+
declares no other types.
|
|
11
|
+
|
|
12
|
+
```neoflow
|
|
13
|
+
@id("capitol-dialogue-id")
|
|
14
|
+
@settings(name: "Capitol: cold boot", saveOptionChoices: true)
|
|
15
|
+
sealed class CapitolColdBoot : NeoDialogue {
|
|
16
|
+
@primary Outpost capitol = Assets.Capitol;
|
|
17
|
+
Player player = Player.Current;
|
|
18
|
+
|
|
19
|
+
@id("trigger-id")
|
|
20
|
+
override Trigger Trigger = new(group: CapitolDialogues.High) => Welcome;
|
|
21
|
+
|
|
22
|
+
@id("welcome-node-id")
|
|
23
|
+
Text Welcome = new(name: "Welcome!") {
|
|
24
|
+
"""
|
|
25
|
+
Hello, {player.Name}.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
@id("continue-option-id")
|
|
29
|
+
Option Continue = new() {
|
|
30
|
+
"""
|
|
31
|
+
Continue.
|
|
32
|
+
"""
|
|
33
|
+
return Finish;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@id("finish-node-id")
|
|
38
|
+
Text Finish = new() {
|
|
39
|
+
"""
|
|
40
|
+
Until next time.
|
|
41
|
+
"""
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- Mark `@primary` explicitly; never infer it. In a `.Lookup` group the
|
|
47
|
+
`@primary` binding is the lookup value that triggers the dialogue; without
|
|
48
|
+
one it never triggers.
|
|
49
|
+
- Dialogue bindings are flow-wide; node bindings are scoped to the node and
|
|
50
|
+
its children.
|
|
51
|
+
- Each persisted condition use, action invocation, mutation, pause, option,
|
|
52
|
+
and node has its own owner-scoped ID. New ones omit it until push.
|
|
53
|
+
- Go to a destination with `= new(...) => Next;` or a trailing block with
|
|
54
|
+
`return Next;`. Never author `to:`. Triggers, options, and outcomes need
|
|
55
|
+
destinations; terminal text and actions may fall through. Keep every
|
|
56
|
+
statement of `Actions Empty = new();` inside its body.
|
|
57
|
+
- Prose lowers to localized-text records. Existing translations survive
|
|
58
|
+
pushes. A prose block's content and closing `"""` follow its opening
|
|
59
|
+
`"""`, so re-indenting never changes the text.
|
|
60
|
+
- New getters used in prose interpolation need no `@id`.
|
|
61
|
+
|
|
62
|
+
After editing, run `neo dialogue dryrun <dialogue-ref>`. It prepares the
|
|
63
|
+
local candidate, including pending schema and value changes, without fetching
|
|
64
|
+
or committing. Exit 1 means the graph would fail on device.
|
|
65
|
+
|
|
66
|
+
## Groups
|
|
67
|
+
|
|
68
|
+
```neo
|
|
69
|
+
NeoDialogueGroup QuestUnlocks = new(kind: .Standard, parent: Quests, name: "Unlocks");
|
|
70
|
+
|
|
71
|
+
NeoDialogueGroup FishingVictory = new(
|
|
72
|
+
kind: .Lookup,
|
|
73
|
+
collection: Assets.FishEncounters,
|
|
74
|
+
collectionValue: Reference(root.Assets.FishEncounters),
|
|
75
|
+
);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
- `name:` sets the stored name when it should differ from the local
|
|
79
|
+
identifier; omitted, it uses the identifier. Unity generates its API from
|
|
80
|
+
the stored name (`Quests.Unlocks`). Pull keeps the local identifier and file
|
|
81
|
+
by `@id`; a remote rename updates `name:`.
|
|
82
|
+
- Triggers accept `group: QuestUnlocks.Medium` or
|
|
83
|
+
`group: Quests.Unlocks.Medium`. Qualified paths use display names converted
|
|
84
|
+
to identifiers. Sibling groups that collide on a segment need distinct local
|
|
85
|
+
aliases.
|
|
86
|
+
- Priority groups are `NeoPriorityGroup` declarations.
|
|
87
|
+
- Lookup groups use the same collection paths as lookup members. The
|
|
88
|
+
collection may be created in the same push without IDs. Values without a
|
|
89
|
+
root path keep `Reference(id: "...")`.
|
|
90
|
+
|
|
91
|
+
## Labels
|
|
92
|
+
|
|
93
|
+
For `Text`, `Actions`, `Conditions`, `Option`, and `Outcome`, an omitted
|
|
94
|
+
`name:` takes the label from a custom symbol: `Option Accept = new()` is
|
|
95
|
+
labeled `Accept` whatever its prose. Dialogue classes do the same without
|
|
96
|
+
`@settings(name:)`.
|
|
97
|
+
|
|
98
|
+
- Generated symbols (`Text1`, `Actions2`, `Option1_2`, `Outcome1`, `Node_...`)
|
|
99
|
+
stay unlabeled for their own kind. A symbol from another kind is custom:
|
|
100
|
+
`Option Text1` is labeled `Text1`.
|
|
101
|
+
- Explicit names win: `name: ""` clears a label, `name: null` clears an
|
|
102
|
+
optional graph label. Pull writes explicit empty names where omission would
|
|
103
|
+
infer one and drops names equal to the inferred symbol.
|
|
104
|
+
- `Trigger` never infers a label. Reusable condition and action functions use
|
|
105
|
+
their function name as `sourceFunctionName`; rename the function and its
|
|
106
|
+
uses to change it. Linked-value aliases keep their `sourceName`. Labels
|
|
107
|
+
never affect IDs or destinations.
|
|
108
|
+
- `neo pull --regenerate-source-names` replaces generated dialogue symbols
|
|
109
|
+
with readable ones and rewrites references.
|
|
110
|
+
|
|
111
|
+
## Triggering dialogues from NeoScript
|
|
112
|
+
|
|
113
|
+
`root.Dialogues` mirrors the generated C# `Dialogues` folders and groups.
|
|
114
|
+
|
|
115
|
+
```neo
|
|
116
|
+
public bool TryTalk() {
|
|
117
|
+
return root.Dialogues.NPCs.Talk.TryTrigger(this, () => {
|
|
118
|
+
this.AfterTalk();
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
public bool HasSomethingToSay => root.Dialogues.NPCs.Talk.CanTrigger(this);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
- `root.Dialogues` has `TryTrigger(string dialogueId, onFinish)`,
|
|
126
|
+
`CanTrigger(string dialogueId)`, `VisitCount(pointer)`, and
|
|
127
|
+
`HasVisited(pointer)`.
|
|
128
|
+
- A standard group has `TryTrigger(onFinish)` and `CanTrigger()`. A lookup
|
|
129
|
+
group takes a stored row of its entry class: `TryTrigger(value, onFinish)`,
|
|
130
|
+
`CanTrigger(value)`.
|
|
131
|
+
- `NeoDialogueReference` has `id`, `TryTrigger(onFinish)`, and `CanTrigger()`.
|
|
132
|
+
`Set<NeoDialogueReference>` elements are bare IDs; pass them to
|
|
133
|
+
`root.Dialogues.TryTrigger`.
|
|
134
|
+
- `onFinish` is an optional `NeoDelegate<void>` that runs when the dialogue
|
|
135
|
+
finishes, fails, or is disposed.
|
|
136
|
+
- `TryTrigger` compiles only in functions (effects, lifecycle hooks,
|
|
137
|
+
listeners included) and dialogue actions. `CanTrigger`, `VisitCount`, and
|
|
138
|
+
`HasVisited` are reads. Folders and groups aren't values. User-class code
|
|
139
|
+
can't trigger dialogues.
|
|
140
|
+
- Unity queues accepted requests and shows one at a time through
|
|
141
|
+
`Dialogues.OnTrigger`. Requests for the running or a queued dialogue return
|
|
142
|
+
false, as does everything when the game has no `OnTrigger` handler.
|
|
143
|
+
- `neo test`, `neo script eval`, dry runs, and the web editor have no dialogue
|
|
144
|
+
runtime: `TryTrigger` and `CanTrigger` return false and `onFinish` never
|
|
145
|
+
runs.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Collection builtins
|
|
2
|
+
|
|
3
|
+
Queries and mutations on `List`, `Set`, and `Dictionary` values. Collection
|
|
4
|
+
declarations and storage live in
|
|
5
|
+
[member kinds](../declarations/member-kinds.md).
|
|
6
|
+
|
|
7
|
+
## Counting and matching
|
|
8
|
+
|
|
9
|
+
- `items.Count` and `items.Count()` give the size; `items.Count(pred)` counts
|
|
10
|
+
matches without building a list. Same forms on Set and Dictionary.
|
|
11
|
+
- `Any()`, `Any(pred)`, and `All(pred)` short-circuit. Empty input has
|
|
12
|
+
`Any() == false` and `All(...) == true`.
|
|
13
|
+
- `First`, `FirstOrDefault`, `Last`, `LastOrDefault`, `Single`, and
|
|
14
|
+
`SingleOrDefault` take zero arguments or one predicate. `First`/`Last` throw
|
|
15
|
+
on no match. `Single` throws unless exactly one matches; `SingleOrDefault`
|
|
16
|
+
still throws on several. `OrDefault` forms return nullable entries and
|
|
17
|
+
`null` on no match. Enum sets return enum values.
|
|
18
|
+
- Dictionary callbacks take `(key, value)`, as in
|
|
19
|
+
`Where((key, value) => key == ToolType.Pickaxe)`.
|
|
20
|
+
|
|
21
|
+
## Queries
|
|
22
|
+
|
|
23
|
+
`Where`, `Select`, `SelectMany`, `OrderBy`, `OrderByDescending`, `Skip`,
|
|
24
|
+
`Take`, `SkipWhile`, `TakeWhile`, `Reverse()`, and `Concat(other)` return
|
|
25
|
+
eager, read-only lists. Queries over a Set, such as `Where` and `OrderBy`,
|
|
26
|
+
return a Set; `ToSet()` turns a List into one.
|
|
27
|
+
|
|
28
|
+
```neo
|
|
29
|
+
return this.Beds
|
|
30
|
+
.SelectMany((bed) => bed.Plants)
|
|
31
|
+
.Where((plant) => plant.Ready)
|
|
32
|
+
.OrderBy((plant) => plant.Name)
|
|
33
|
+
.Take(10);
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
- `SelectMany` needs a non-null list or set from its selector.
|
|
37
|
+
- Sorting is stable, evaluates each key once, compares strings ordinally, and
|
|
38
|
+
accepts bool, int, float, decimal, or string keys (nullable too). Null sorts
|
|
39
|
+
first ascending and last descending.
|
|
40
|
+
- `ThenBy`/`ThenByDescending` follow `OrderBy`. An inferred `var` keeps that
|
|
41
|
+
ability; List/Set annotations, function boundaries, and non-ordering queries
|
|
42
|
+
erase it. Ordering costs O(n log n * k) for k key levels. Mutating the
|
|
43
|
+
snapshot through a writable alias makes `ThenBy` throw until a new `OrderBy`.
|
|
44
|
+
- Negative skip/take counts behave as zero.
|
|
45
|
+
- Read-only results keep entry identity and grant no extra write access to
|
|
46
|
+
entries.
|
|
47
|
+
|
|
48
|
+
## Reductions
|
|
49
|
+
|
|
50
|
+
- `Sum`, `Average`, `Min`, `Max` take zero arguments or one selector.
|
|
51
|
+
Sum/Average need int, float, or decimal; Min/Max also accept strings and
|
|
52
|
+
bools. Nulls are skipped. `Sum` of empty input is zero; the others return
|
|
53
|
+
null for nullable input and throw for required input. Average of ints is
|
|
54
|
+
float. Decimal math stays exact; decimal Average rounds half-even within 28
|
|
55
|
+
digits. Float `Min` stops at the first NaN; `Max` prefers ordinary values.
|
|
56
|
+
- `MinBy(sel)`/`MaxBy(sel)` return the first entry with the winning key,
|
|
57
|
+
skipping null keys unless all are null. Empty input throws unless the entry
|
|
58
|
+
type is nullable.
|
|
59
|
+
- List and Set have `Aggregate(fn)`, `Aggregate(seed, fn)`, `Distinct()`, and
|
|
60
|
+
`Zip(other, fn)`. An empty unseeded `Aggregate` throws. `Distinct` keeps
|
|
61
|
+
first occurrences by `Contains` equality. `Zip` stops at the shorter input.
|
|
62
|
+
- `GroupBy(sel)` returns a read-only dictionary of read-only lists.
|
|
63
|
+
`ToDictionary(keySel[, valueSel])` keys must be non-null strings or enums;
|
|
64
|
+
duplicate keys throw.
|
|
65
|
+
- `.Keys` and `.Values` return read-only lists in dictionary order;
|
|
66
|
+
integer-looking string keys come before other strings, in numeric order.
|
|
67
|
+
|
|
68
|
+
## Mutation
|
|
69
|
+
|
|
70
|
+
These apply to writable collections and are statements:
|
|
71
|
+
|
|
72
|
+
- `items.Insert(index, value)` accepts 0 through `items.Count` (append);
|
|
73
|
+
other indices throw.
|
|
74
|
+
- `items.Remove(value)` removes the first equal entry; a missing value is a
|
|
75
|
+
no-op. `items.IndexOf(value)` (lists only) returns the first index or `-1`.
|
|
76
|
+
- `items.Sort()` sorts scalar entries; `items.Sort((l, r) => l.Score - r.Score)`
|
|
77
|
+
takes an int comparison. Sort is stable, keeps identity, rejects mutating
|
|
78
|
+
the list through aliases while comparing, and leaves the order unchanged if
|
|
79
|
+
a comparison fails.
|
|
80
|
+
|
|
81
|
+
`Contains`, `IndexOf`, `Remove`, `Distinct`, and `ToSet` compare classes,
|
|
82
|
+
interfaces, collections, and references by identity and other values by value.
|
|
83
|
+
Decimals compare numerically (`1.0` equals `1.00`) while keeping their scale
|
|
84
|
+
for display; strings such as `"1.0"` and `"1.00"` stay distinct.
|
|
85
|
+
|
|
86
|
+
## List.Repeat
|
|
87
|
+
|
|
88
|
+
```neo
|
|
89
|
+
List<int> scores = List.Repeat(0, 10);
|
|
90
|
+
List<InventoryItemStack?> slots = List.Repeat<InventoryItemStack?>(null, slotCount);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The entry type comes from the value joined with the assignment context, like
|
|
94
|
+
a list literal, so `List.Repeat(0, 4)` assigned to `List<float>` holds floats.
|
|
95
|
+
Put type arguments on the call, never the qualifier: `List<int>.Repeat` is an
|
|
96
|
+
error and bare `List` is not a value. `count` must be an `int`; a negative
|
|
97
|
+
count throws at runtime. The value and then the count evaluate exactly once,
|
|
98
|
+
so a reference-typed value repeats the same reference.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Control flow
|
|
2
|
+
|
|
3
|
+
Conditionals, loops, switch, and errors. The shapes match C#; this page lists
|
|
4
|
+
where NeoScript differs or restricts.
|
|
5
|
+
|
|
6
|
+
## Conditional and logical operators
|
|
7
|
+
|
|
8
|
+
`cond ? a : b` binds looser than `??`, `||`, and `&&`, associates right, and
|
|
9
|
+
evaluates only the chosen arm. Both arms need a common assignable type or must
|
|
10
|
+
fit the expected type. `&&` binds tighter than `||`; both short-circuit.
|
|
11
|
+
|
|
12
|
+
## Loops
|
|
13
|
+
|
|
14
|
+
`for`, `foreach`, `while`, and `do { } while (...);` work as in C#. `while`
|
|
15
|
+
and `do` bodies need braces. `break` exits the nearest loop or switch; `continue` targets the
|
|
16
|
+
nearest loop, including from a switch inside a loop, and re-checks `while`
|
|
17
|
+
conditions.
|
|
18
|
+
|
|
19
|
+
A `foreach` receiver must be non-null. The loop snapshots membership and order
|
|
20
|
+
at entry, so adding or removing rows does not change its iterations.
|
|
21
|
+
`foreach (var value in dictionary)` binds values. Deconstruct to get keys:
|
|
22
|
+
|
|
23
|
+
```neo
|
|
24
|
+
foreach (var (key, value) in this.Damage) {
|
|
25
|
+
if (key == ToolType.Pickaxe) {
|
|
26
|
+
ApplyDamage(value);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
foreach ((ToolType key, float value) in this.Damage) { ApplyDamage(value); }
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Deconstruction needs a dictionary local or member path; assign a computed
|
|
33
|
+
receiver to a local first. Keys are snapshotted at entry and each value is
|
|
34
|
+
read through `dictionary[key]`, so a removed key follows normal missing-key
|
|
35
|
+
behavior. Both bindings are read-only; writes through a writable entry still
|
|
36
|
+
work. Enum-keyed dictionaries yield enum keys.
|
|
37
|
+
|
|
38
|
+
## Switch statements
|
|
39
|
+
|
|
40
|
+
Selectors are `int`, `string`, `bool`, enum, or nullable forms of these, with
|
|
41
|
+
compile-time constant labels. Stack labels to share a body. End every
|
|
42
|
+
reachable non-empty section with `break`, `return`, `throw`, or a valid
|
|
43
|
+
`continue`. No implicit fallthrough, pattern cases, guards, or `goto`.
|
|
44
|
+
|
|
45
|
+
```neo
|
|
46
|
+
switch (direction) {
|
|
47
|
+
case Direction.North:
|
|
48
|
+
Move(0, 1);
|
|
49
|
+
break;
|
|
50
|
+
case Direction.East:
|
|
51
|
+
case Direction.West:
|
|
52
|
+
Stop();
|
|
53
|
+
break;
|
|
54
|
+
default:
|
|
55
|
+
throw $"Unknown direction: {direction}";
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Switch expressions
|
|
60
|
+
|
|
61
|
+
`value switch { pattern => result, ... }` arms take constants, `null`, type
|
|
62
|
+
patterns (`Damage`), declaration patterns (`Damage damage`), or `_`, each
|
|
63
|
+
with an optional `when` guard. Arms test in order; the selector runs once.
|
|
64
|
+
|
|
65
|
+
```neo
|
|
66
|
+
closed class HitResult {}
|
|
67
|
+
sealed class Miss : HitResult {}
|
|
68
|
+
sealed class Damage : HitResult { public int Amount = 0; }
|
|
69
|
+
|
|
70
|
+
class Combat {
|
|
71
|
+
public string Describe(HitResult result) => result switch {
|
|
72
|
+
Miss => "miss",
|
|
73
|
+
Damage damage when damage.Amount > 10 => "strong hit",
|
|
74
|
+
Damage damage => $"{damage.Amount}",
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Switch expressions must be exhaustive: every bool/enum option or
|
|
80
|
+
closed-class branch, plus `null` for nullable input; other types need a
|
|
81
|
+
covering type or `_`. A guard counts toward coverage only if it is literally
|
|
82
|
+
`true`. Missing and unreachable arms are compile errors. Property,
|
|
83
|
+
relational, list, and positional patterns are unsupported.
|
|
84
|
+
|
|
85
|
+
## Errors
|
|
86
|
+
|
|
87
|
+
Throw a string or a `NeoError` subclass; a string throw creates a `NeoError`.
|
|
88
|
+
Every error has a read-only `Message`. Subclass `NeoError` for typed data.
|
|
89
|
+
|
|
90
|
+
```neo
|
|
91
|
+
try {
|
|
92
|
+
return GetUnsafe();
|
|
93
|
+
}
|
|
94
|
+
catch (NeoArgumentError error) when (error.Message != "") {
|
|
95
|
+
Report(error.Message);
|
|
96
|
+
throw;
|
|
97
|
+
}
|
|
98
|
+
catch (NeoError error) {
|
|
99
|
+
return Fallback();
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
ReleaseResource();
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
- A try needs a catch, a finally, or both. Catch bindings are read-only.
|
|
107
|
+
- Typed catches match subclasses. `catch (string message)` matches every
|
|
108
|
+
ordinary error and binds its Message. An unfiltered base catch must follow
|
|
109
|
+
the catches it covers.
|
|
110
|
+
- Filters run before intervening finally blocks, even across calls. A false or
|
|
111
|
+
failing filter continues the search. Bare `throw;` rethrows from a catch.
|
|
112
|
+
- Finally runs on normal completion, return, break, continue, and ordinary
|
|
113
|
+
errors. The return value is computed before cleanup. An error escaping
|
|
114
|
+
finally replaces the pending one; an error caught inside it does not.
|
|
115
|
+
Return and loop transfers can't leave a finally block (a nested loop may
|
|
116
|
+
break or continue), and bare `throw` inside it needs its own inner catch.
|
|
117
|
+
- Runtime failures are `NeoNullReferenceError`, `NeoIndexOutOfRangeError`,
|
|
118
|
+
`NeoKeyNotFoundError`, `NeoArgumentError`, `NeoArithmeticError`, and
|
|
119
|
+
`NeoInvalidOperationError`. Unexpected native failures become `NeoHostError`
|
|
120
|
+
with Message `"Check logs for more details."`.
|
|
121
|
+
- Execution limits, unavailable preview natives, cancellation, and invalid
|
|
122
|
+
compiled data can't be caught. Completed writes are never rolled back.
|
|
123
|
+
Suspended Unity calls keep their cleanup until resumed; abandonment skips it.
|