@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
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Extensions
|
|
2
|
+
|
|
3
|
+
Declare functions and computed properties in top-level extension blocks. Each
|
|
4
|
+
member has its own `@id`; the block has no identity. Blocks can share any
|
|
5
|
+
`.neo` definition file. Pull retains member file placement and otherwise uses
|
|
6
|
+
`Extensions/<Receiver>Extensions.neo`.
|
|
7
|
+
|
|
8
|
+
```neo
|
|
9
|
+
extension Person {
|
|
10
|
+
@id("person-full-name")
|
|
11
|
+
public string FullName => $"{this.FirstName} {this.LastName}";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
extension List<Item> {
|
|
15
|
+
@id("item-best")
|
|
16
|
+
Item? Best() => this.OrderByDescending((i) => i.Quality).FirstOrDefault();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
extension<T> List<T> {
|
|
20
|
+
@id("list-first-value")
|
|
21
|
+
T? FirstValue => this.FirstOrDefault();
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Always use `this` to access the extended instance. Interface receivers are
|
|
26
|
+
supported and use the ordinary writable-member rules. Extensions add no
|
|
27
|
+
stored fields, constructors, inheritance, or operators. They are visible
|
|
28
|
+
project-wide. Applicable declared members take priority over extensions;
|
|
29
|
+
applicable extensions take priority over built-ins. Ambiguous candidates are
|
|
30
|
+
errors. Receiver type parameters must appear in the receiver and cannot have
|
|
31
|
+
defaults. Functions may also declare their own type parameters.
|
|
32
|
+
|
|
33
|
+
Computed properties support getters and explicit `set(Type value)` bodies.
|
|
34
|
+
Static extensions have no `this` and are called through the receiver type.
|
|
35
|
+
Extensions cannot be listener or spy/mock targets or use builder bodies.
|
|
36
|
+
|
|
37
|
+
Use `neo script check --all` to check local declarations. For direct checks,
|
|
38
|
+
select an extension with `--member <stable-member-id>`. Do not pass `--this`.
|
|
39
|
+
Direct evaluation accepts `--this-value <valueId>` only for a closed class or
|
|
40
|
+
interface receiver. Static extensions reject `--this-value`. Evaluate scalar,
|
|
41
|
+
collection, receiver-generic, and explicitly generic calls in an ordinary
|
|
42
|
+
`neo script eval --source '...'` expression. Evaluate setters through ordinary
|
|
43
|
+
assignments in source.
|
|
44
|
+
|
|
45
|
+
Both evaluators support extension members independently of the generated C#
|
|
46
|
+
version. See [Unity export](../sync/unity-export.md) for wrapper availability.
|
|
@@ -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,138 @@
|
|
|
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.
|
|
99
|
+
|
|
100
|
+
## Sequences and generators
|
|
101
|
+
|
|
102
|
+
Built-in collections implement `IEnumerable<T>` for their exact value type.
|
|
103
|
+
A dictionary enumerates its values. `GetEnumerator()` creates an independent
|
|
104
|
+
`IEnumerator<T>` with `MoveNext()`, `Current`, and `Dispose()`. Built-in cursors
|
|
105
|
+
snapshot membership when acquired. `Current` requires a successful `MoveNext`.
|
|
106
|
+
|
|
107
|
+
```neo
|
|
108
|
+
public IEnumerable<int> Neighbors(int cell) {
|
|
109
|
+
yield return cell - 1;
|
|
110
|
+
yield return cell + 1;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public List<int> Collect(IEnumerable<int> values) => values;
|
|
114
|
+
public IEnumerator<int> Cursor(List<int> values) => values;
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Typed assignments, parameters, and returns acquire a cursor or collect a list
|
|
118
|
+
implicitly. Collecting a cursor consumes its remaining values, excluding its
|
|
119
|
+
current value, and disposes it. Element types must match exactly. A collection
|
|
120
|
+
itself does not own an iteration position.
|
|
121
|
+
|
|
122
|
+
A generator returns non-nullable `IEnumerable<T>` or `IEnumerator<T>`. Arguments
|
|
123
|
+
are evaluated when called; its body starts on the first `MoveNext`. Use
|
|
124
|
+
`yield return value` and `yield break`. `foreach` disposes its cursor on normal
|
|
125
|
+
completion, `break`, `return`, and catchable errors. A suspended generator runs
|
|
126
|
+
entered `finally` blocks when disposed. Yields cannot appear in catch/finally
|
|
127
|
+
blocks or a try block with catches; generators cannot invoke async functions.
|
|
128
|
+
|
|
129
|
+
`start..end` is an ascending, upper-exclusive int sequence. Reversed bounds are
|
|
130
|
+
empty. `foreach (var i in 0..count)` uses a counted loop without allocating a
|
|
131
|
+
cursor. Ranges require safe integer bounds.
|
|
132
|
+
|
|
133
|
+
`IEnumerator<T>` and derived cursor interfaces cannot occur in stored member
|
|
134
|
+
shapes, including Session storage, nested collection entries, or tuples. Use
|
|
135
|
+
computed properties and callable signatures for cursors; store `List<T>` data
|
|
136
|
+
explicitly. Stored enumerable interface members accept authored class rows.
|
|
137
|
+
Runtime sequences are shown as unenumerated and are not expanded for previews.
|
|
138
|
+
`ToSet()` rejects enumerable/cursor interface elements.
|