@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,107 @@
|
|
|
1
|
+
# Delegates, actions, and listeners
|
|
2
|
+
|
|
3
|
+
Lambdas, `NeoDelegate`, method groups, `NeoAction` events, `OnChanged`
|
|
4
|
+
subscriptions, and `@listeners` wiring.
|
|
5
|
+
|
|
6
|
+
## Lambdas and delegates
|
|
7
|
+
|
|
8
|
+
```neo
|
|
9
|
+
NeoDelegate<int, int> twice = (x) => x * 2;
|
|
10
|
+
List<Item> ready = this.Items.Where((item) => item.Count > 0);
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
- Lambdas may be returned, stored, or assigned to typed locals. They capture
|
|
14
|
+
surrounding locals and parameters by value when created.
|
|
15
|
+
- `(x) => x * 2` equals `(x) => { return x * 2; }`. In a void delegate the
|
|
16
|
+
expression must call or assign. Parenthesize a dictionary-literal body:
|
|
17
|
+
`() => ({ "a": 1 })`.
|
|
18
|
+
- Call any non-null delegate expression directly: `callbacks[0](value)`,
|
|
19
|
+
`(cond ? a : b)()`, `Factory.Make()()`. Force-unwrap a nullable one first:
|
|
20
|
+
`callbacks["key"]!()`. Void delegates are statements only.
|
|
21
|
+
- Inside a class body, invoke delegate and action members directly:
|
|
22
|
+
`Selector()`, `OnUpdated()`.
|
|
23
|
+
|
|
24
|
+
## Delegate field defaults
|
|
25
|
+
|
|
26
|
+
- Instance delegate defaults compile against the declared signature; each
|
|
27
|
+
instance binds `this` to itself. Invalid bodies are reported at the
|
|
28
|
+
declaration.
|
|
29
|
+
- Static delegate fields use static class scope: no `this`, no instance
|
|
30
|
+
members. A static delegate cannot copy another class's delegate field,
|
|
31
|
+
inherited ones included; wrap it as `() => Other.Call()`.
|
|
32
|
+
- A default may name a method group bare. `Value = Read` or `this.Read` binds
|
|
33
|
+
each instance to its own `Read`. A qualified group (`Holder.Read`) must be
|
|
34
|
+
static. Signature mismatches are reported at the declaration.
|
|
35
|
+
- When a function is overloaded, a method group picks the overload whose
|
|
36
|
+
parameters exactly match the target delegate type. `var f = Read` is an
|
|
37
|
+
error then; give `f` a delegate type. See [overloads](../declarations/overloads.md).
|
|
38
|
+
|
|
39
|
+
## Actions
|
|
40
|
+
|
|
41
|
+
`NeoAction<...>` is a multicast event whose value is an ordered listener set.
|
|
42
|
+
Bare `NeoAction` takes no arguments; `NeoAction<void>` is invalid.
|
|
43
|
+
|
|
44
|
+
```neo
|
|
45
|
+
public NeoAction<int> OnDamaged = [PlayHitFlash, ApplyDamage];
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
- Default it with a bracketed list of method groups. Never a lambda, never a
|
|
49
|
+
bare method group, never `required`; its rest state is the empty set.
|
|
50
|
+
- Subscribe and unsubscribe with `+=` and `-=` on the member. These are
|
|
51
|
+
durable writes deduplicated by target, so re-running a subscription can't
|
|
52
|
+
double-subscribe. External public callers may subscribe and unsubscribe
|
|
53
|
+
only.
|
|
54
|
+
- Only the declaring class and subclasses may invoke it (as a statement) or
|
|
55
|
+
call `this.OnUpdated.Clear()`. `Clear()` follows the member's storage and
|
|
56
|
+
writability. An action is never a value, a return type, or assigned with `=`.
|
|
57
|
+
- A nested default such as `Watcher = new() { OnTally = [Handle] }` may
|
|
58
|
+
target an inherited generic method on the enclosing instance and binds to
|
|
59
|
+
that instance. Explicit receivers keep their captured instance.
|
|
60
|
+
- An instance-bound listener reachable from a stable root keeps its path
|
|
61
|
+
(`[root.Assets.OnDamage]`). Pull refuses a bound receiver with no
|
|
62
|
+
deterministic `root.*` path.
|
|
63
|
+
|
|
64
|
+
## OnChanged on fields
|
|
65
|
+
|
|
66
|
+
```neo
|
|
67
|
+
owner.Field.OnChanged += receiver.Handler;
|
|
68
|
+
owner.Field.OnChanged -= receiver.Handler;
|
|
69
|
+
owner.Field.OnChanged.Clear();
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The field must be a writable stored instance member. Handlers are
|
|
73
|
+
noncapturing, return void, and accept the field's type. Bindings to Session
|
|
74
|
+
receivers stay Session-only. Gameplay hosts dispatch after the write batch;
|
|
75
|
+
authoring, replay, migration, and dialogue dry-run hosts keep the wiring but
|
|
76
|
+
never call handlers. `Clear()` removes every handler, `@listeners` defaults
|
|
77
|
+
included, in both Save and Session wiring.
|
|
78
|
+
|
|
79
|
+
## OnChanged on getters
|
|
80
|
+
|
|
81
|
+
`this._quantity.Value.OnChanged += this.QuantityChanged` subscribes to an
|
|
82
|
+
instance getter. The subscription lives in memory for the play session, so
|
|
83
|
+
subscribe from a function, effect, lifecycle hook, or dialogue action, never
|
|
84
|
+
a constructor, initializer, or migration.
|
|
85
|
+
|
|
86
|
+
- Subscribing reads the getter once. The handler then gets a fresh result
|
|
87
|
+
whenever a dependency changes, even if the result is equal; several changes
|
|
88
|
+
before the next call produce one call.
|
|
89
|
+
- `Clear()` ends it; constructors can't call it on a getter. Static getters,
|
|
90
|
+
`base.Value`, and `@listeners` can't observe getters.
|
|
91
|
+
- `neo test` dispatches after each test-body statement, so the next statement
|
|
92
|
+
sees handler effects. Getters reading through `Clone()` or grid queries may
|
|
93
|
+
fire more often there than in Unity. Other TypeScript hosts validate but
|
|
94
|
+
never call handlers.
|
|
95
|
+
|
|
96
|
+
## Persisted wiring
|
|
97
|
+
|
|
98
|
+
```neo
|
|
99
|
+
@listeners(Count: [root.Save.Tracker.CountChanged], Label: [root.Save.Tracker.LabelChanged])
|
|
100
|
+
new Counter()
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Each named argument must be a writable stored field; order is free. For a
|
|
104
|
+
virtual owner, use the existing nested positional override; changing only
|
|
105
|
+
its listeners must not add a value override. An omitted field inherits its
|
|
106
|
+
listeners. `[]` suppresses inherited listeners and disappears when nothing is
|
|
107
|
+
inherited.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Math and random
|
|
2
|
+
|
|
3
|
+
Numeric types, operators, `Math`, `NeoRandom`, and vector/Color math.
|
|
4
|
+
|
|
5
|
+
## Numeric types
|
|
6
|
+
|
|
7
|
+
`int` retains whole-number precision; division truncates toward zero.
|
|
8
|
+
`float` literals, stored values, and
|
|
9
|
+
every float arithmetic step round to 32 bits, matching Unity. `decimal` is
|
|
10
|
+
exact base-10; use it for currency and other values that must not drift.
|
|
11
|
+
Float literals use `0.5`, not `0.5f`; decimal literals use `0.5m`.
|
|
12
|
+
|
|
13
|
+
## Bitwise operators
|
|
14
|
+
|
|
15
|
+
`~`, `&`, `^`, `|`, `<<`, `>>`, `>>>` and their compound forms take
|
|
16
|
+
non-nullable `int` operands only. Enums, bools, float literals such as `1.0`,
|
|
17
|
+
and decimals are rejected; use `Set<Enum>` for enum membership.
|
|
18
|
+
|
|
19
|
+
```neo
|
|
20
|
+
int mask = (region << 8) | (tile & 255);
|
|
21
|
+
mask &= ~2;
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Operands wrap to signed int32 and every result is signed int32. Shift counts
|
|
25
|
+
use their low five bits (32 means 0, -1 means 31). `>>` extends the sign;
|
|
26
|
+
`>>>` fills with zero but still returns signed, so `-1 >>> 1` is 2147483647.
|
|
27
|
+
Operands evaluate once, left to right. Write shifts without spaces
|
|
28
|
+
(`x >> 1`, never `x > > 1`).
|
|
29
|
+
|
|
30
|
+
Precedence, tightest first: unary, switch expressions, `* / %`, `+ -`, shifts,
|
|
31
|
+
comparisons, equality and `is`, `&`, `^`, `|`, `&&`, `||`, `??`, `?:`.
|
|
32
|
+
|
|
33
|
+
## Math
|
|
34
|
+
|
|
35
|
+
`Math` has C# names and semantics: `Min`, `Max`, `Clamp`, `Round`, `Floor`,
|
|
36
|
+
`Ceiling`, `Truncate`, `Abs`, `Sign`, `Sqrt`, `PI`, `E`.
|
|
37
|
+
|
|
38
|
+
```neo
|
|
39
|
+
return Math.Clamp(this.Quality + bonus, 0, 10);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Results follow the argument types: all-`int` gives `int`, any `float` gives
|
|
43
|
+
`float`, any `decimal` gives `decimal`, and `decimal` never mixes with `float`.
|
|
44
|
+
`Round`, `Floor`, `Ceiling`, and `Truncate` are the float-to-int conversions:
|
|
45
|
+
they return `int` for int/float input, keep `decimal` as `decimal`, and throw
|
|
46
|
+
on non-finite input. `Round` rounds midpoints to even. `Sign` returns `int`;
|
|
47
|
+
`Sqrt` takes int or float only.
|
|
48
|
+
|
|
49
|
+
`Pow`, `Exp`, `Log`, `Log10`, `Sin`, `Cos`, `Tan`, `Asin`, `Acos`, `Atan`,
|
|
50
|
+
`Atan2(y, x)`, `Lerp`, `InverseLerp`, `MoveTowards`, `Repeat`, and `PingPong`
|
|
51
|
+
take int/float and return float, rounding int inputs to float32 first.
|
|
52
|
+
Decimal and nullable operands are rejected. Angles are radians. `Lerp` and
|
|
53
|
+
`InverseLerp` clamp to [0, 1]; a negative `MoveTowards` delta moves away;
|
|
54
|
+
`Repeat`/`PingPong` need finite t and a finite positive length.
|
|
55
|
+
|
|
56
|
+
`Math` is a builtin qualifier, not a value. No class, enum, parameter, or
|
|
57
|
+
local may shadow it, but a qualified member may be named `Math`
|
|
58
|
+
(`this.Math`).
|
|
59
|
+
|
|
60
|
+
## Random
|
|
61
|
+
|
|
62
|
+
```neo
|
|
63
|
+
NeoRandom.Seed = 17; // reset the shared stream
|
|
64
|
+
int index = NeoRandom.Range(0, items.Count);
|
|
65
|
+
float chance = NeoRandom.Value();
|
|
66
|
+
var weather = new NeoRandom(42); // independent stream; seed required
|
|
67
|
+
float temperature = weather.Range(-1.0, 1.0);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
- Shared and instance streams have the same methods. Keep an instance in a
|
|
71
|
+
field when calls should share its progress; constructing it inside each call
|
|
72
|
+
restarts from the seed.
|
|
73
|
+
- `Value()` includes 0 and 1. Int `Range(min, max)` excludes max; float
|
|
74
|
+
`Range` includes both bounds. Equal bounds consume no draw. Reversed int
|
|
75
|
+
bounds still include the original min and exclude the original max;
|
|
76
|
+
reversed float bounds swap.
|
|
77
|
+
- Int bounds must fit int32, float bounds must be finite, seeds must be safe
|
|
78
|
+
integers. Reading `Seed` returns the assigned value; every assignment
|
|
79
|
+
restarts the stream.
|
|
80
|
+
- Stream progress and reseeding last for the session only. Getters that read
|
|
81
|
+
`Seed` refresh after reseeding; getters that only draw cache normally.
|
|
82
|
+
- Stored initializers replay independently of gameplay streams. A
|
|
83
|
+
construction that drew random values keeps its owned values as overrides
|
|
84
|
+
until it is edited or rebuilt.
|
|
85
|
+
|
|
86
|
+
For repeatable runs pass a signed decimal seed: `neo test --seed 17` or
|
|
87
|
+
`neo script eval --seed 17 --returns float 'return NeoRandom.Value();'`. Each
|
|
88
|
+
test gets a fresh stream shared by its hooks and body. Without `--seed`, tests
|
|
89
|
+
use the project-fingerprint seed printed in the report and other sessions use
|
|
90
|
+
host entropy.
|
|
91
|
+
|
|
92
|
+
## Vectors and Color
|
|
93
|
+
|
|
94
|
+
`Vector2`, `Vector3`, `Vector2Int`, and `Vector3Int` support `+`, `-`, unary
|
|
95
|
+
`-`, scalar multiply in either order, and scalar divide. Both vector operands
|
|
96
|
+
must be the same type. Int vectors take only int scalars and truncate on
|
|
97
|
+
division; float vectors take int or float. Division by zero throws. `==` is
|
|
98
|
+
exact component equality. Int vectors never convert implicitly to float
|
|
99
|
+
vectors.
|
|
100
|
+
|
|
101
|
+
```neo
|
|
102
|
+
Vector2 heading = (target - position).normalized;
|
|
103
|
+
Vector2 step = Vector2.MoveTowards(position, target, speed);
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Every vector has read-only `magnitude` and `sqrMagnitude`, static `Distance`,
|
|
107
|
+
`Min`, `Max`, and constants `zero`, `one`, `up`, `down`, `left`, `right` (3D
|
|
108
|
+
adds `forward`, `back`). Float vectors add `normalized`, `Dot`, `Lerp`, and
|
|
109
|
+
`MoveTowards`; `Vector3` adds `Cross`. `normalized` returns zero at magnitude
|
|
110
|
+
0.00001 or less.
|
|
111
|
+
|
|
112
|
+
`Color.Lerp(a, b, t)` interpolates all four channels. Vector and Color `Lerp`
|
|
113
|
+
clamp t to [0, 1]. Arithmetic results and constants are fresh values;
|
|
114
|
+
component writes keep the ordinary vector aliasing rules.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# NeoScript
|
|
2
|
+
|
|
3
|
+
NeoScript is C#-shaped, statically typed, and sandboxed. This page holds the
|
|
4
|
+
rules every body follows. Open a leaf from [References](#references) only when
|
|
5
|
+
the task uses that API.
|
|
6
|
+
|
|
7
|
+
## Bodies live inline
|
|
8
|
+
|
|
9
|
+
Write getters, setters, functions, constructors, dialogue logic, and group
|
|
10
|
+
logic inline in their owning `.neo` or `.neoflow` declaration. Never create
|
|
11
|
+
`Scripts/<Class>/<Member>.neo` sidecars.
|
|
12
|
+
|
|
13
|
+
```neo
|
|
14
|
+
public bool HasVisited => this.VisitCount > 0;
|
|
15
|
+
|
|
16
|
+
public int Clamped {
|
|
17
|
+
get => Math.Clamp(this.Raw, 0, 10);
|
|
18
|
+
set(int value) { this.Raw = value; }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public bool Unlocked(int level) => this.Level >= level;
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
- `=> expr;` is sugar for a one-`return` block, on getter-only properties,
|
|
25
|
+
accessors, and functions. In a void context it must call or assign
|
|
26
|
+
(`void Ping() => this.OnUpdated();`). Pull writes one-line single returns as
|
|
27
|
+
`=>` and keeps setters as blocks; push keeps whichever form you wrote.
|
|
28
|
+
- `=>` after an `=` initializer is a NeoFlow destination, not a body.
|
|
29
|
+
- A property has at most one getter and one setter. Setters are
|
|
30
|
+
`set(int value) { ... }` with the property type and the name `value`; an
|
|
31
|
+
empty body is a valid no-op.
|
|
32
|
+
- Bodyless members must be `abstract` (`public abstract int Count { get; }`,
|
|
33
|
+
even in an abstract class), `native` (host-provided only), or interface
|
|
34
|
+
contracts. `async` alone is not enough. Use a field such as
|
|
35
|
+
`public int Count = 0;` for stored data.
|
|
36
|
+
- In an override, `base.Member(args)` or `base.Property` calls the nearest
|
|
37
|
+
ancestor implementation with the same `this`. `base` outside an override,
|
|
38
|
+
on an abstract member, for base constructors, or for stored-field reads is
|
|
39
|
+
an error.
|
|
40
|
+
|
|
41
|
+
## Names and calls
|
|
42
|
+
|
|
43
|
+
- Inside a class body, unqualified names resolve against that class.
|
|
44
|
+
- Calls may be statements even when they return a value; the result is
|
|
45
|
+
discarded (`this.Items.Remove(item);`).
|
|
46
|
+
- `new MissingClass(5)` needs a declared type everywhere. Contextual `new(...)`
|
|
47
|
+
uses the expected type.
|
|
48
|
+
- An error inside a referenced initializer is reported at that initializer's
|
|
49
|
+
own file and position. Fix it there, not at the `Reference(...)` use.
|
|
50
|
+
|
|
51
|
+
## Nullability
|
|
52
|
+
|
|
53
|
+
Never access a nullable receiver directly. Narrow with a condition, or use
|
|
54
|
+
`?.`, `??`, or `!` when failure is intended:
|
|
55
|
+
|
|
56
|
+
```neo
|
|
57
|
+
if (player.Target != null) {
|
|
58
|
+
return player.Target.Name;
|
|
59
|
+
}
|
|
60
|
+
return player.Target?.Name ?? "Unknown";
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Narrowing details, `is` patterns, and interface guards are in
|
|
64
|
+
[types and narrowing](types-and-narrowing.md).
|
|
65
|
+
|
|
66
|
+
## Identity and writes
|
|
67
|
+
|
|
68
|
+
- `==`, `Contains`, `IndexOf`, and `Remove` compare classes, interfaces, and
|
|
69
|
+
collections by identity, and primitives, enums, vectors, and media by value.
|
|
70
|
+
- Runtime ownership decides whether a write is allowed: Immutable, Save,
|
|
71
|
+
Session, setter, or otherwise writable. The body kind does not.
|
|
72
|
+
A constructor writes only its own `this`. A structured-leaf field inherits
|
|
73
|
+
its receiver's storage.
|
|
74
|
+
- Object initializers work in bodies and specs:
|
|
75
|
+
`var tile = new DirtWetTile { Cell = Vector2Int(3, -2) };`. Entries that
|
|
76
|
+
settle required members run with construction; the rest are ordinary
|
|
77
|
+
assignments in source order.
|
|
78
|
+
|
|
79
|
+
## Check your work
|
|
80
|
+
|
|
81
|
+
Run `neo script check --all` or `neo test` after NeoScript edits. Both build
|
|
82
|
+
the prospective project from local source, so new code needs no push first.
|
|
83
|
+
|
|
84
|
+
## References
|
|
85
|
+
|
|
86
|
+
Read only the leaf the task needs:
|
|
87
|
+
|
|
88
|
+
- [Math and random](math-and-random.md): numeric types, bitwise operators,
|
|
89
|
+
`Math`, `NeoRandom` streams and seeds, vector and Color math.
|
|
90
|
+
- [Strings](strings.md): string members, `Parse`/`TryParse`, number formats.
|
|
91
|
+
- [Collections](collections.md): LINQ-style queries, reductions, ordering,
|
|
92
|
+
`Insert`/`Remove`/`Sort`, `List.Repeat`, membership equality.
|
|
93
|
+
- [Control flow](control-flow.md): `?:`, loops, dictionary deconstruction,
|
|
94
|
+
switch statements and expressions, try/catch/finally, error types.
|
|
95
|
+
- [Types and narrowing](types-and-narrowing.md): null and type narrowing,
|
|
96
|
+
patterns, `Equals`, generic functions and inference, tuples.
|
|
97
|
+
- [Delegates and listeners](delegates-and-listeners.md): lambdas, method
|
|
98
|
+
groups, `NeoAction`, `OnChanged` subscriptions, `@listeners`.
|
|
99
|
+
- [Checking, testing, and migrations](testing-and-migrations.md):
|
|
100
|
+
`neo script`, `neo test`, `Debug`, `neo migrate`.
|
|
101
|
+
|
|
102
|
+
Related branches:
|
|
103
|
+
|
|
104
|
+
- [References](../values/references.md): `Reference<T>` handles and slots.
|
|
105
|
+
- [Overloads](../declarations/overloads.md): declaring and calling overloads.
|
|
106
|
+
- [Constructors](../declarations/constructors.md) and
|
|
107
|
+
[initializers](../declarations/initializers.md): construction order and
|
|
108
|
+
what member defaults may read.
|
|
109
|
+
- [Dialogues](../dialogues.md#triggering-dialogues-from-neoscript):
|
|
110
|
+
`TryTrigger`, `CanTrigger`, visit counts.
|
|
111
|
+
- [Grids and cell patterns](../world/grids-and-cell-patterns.md):
|
|
112
|
+
`GetObjects`, `GetTiles`, `GetArea`.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Strings, parsing, and formatting
|
|
2
|
+
|
|
3
|
+
## String members
|
|
4
|
+
|
|
5
|
+
Strings have `Length`, `Substring(startIndex[, length])`, `IndexOf`,
|
|
6
|
+
`LastIndexOf`, `Replace(search, replacement)`, `Split(separator)`,
|
|
7
|
+
`PadLeft`/`PadRight(totalWidth[, paddingChar])`, `Trim`, `TrimStart`, and
|
|
8
|
+
`TrimEnd`, plus statics `string.Join(separator, values)`,
|
|
9
|
+
`string.IsNullOrEmpty`, and `string.IsNullOrWhiteSpace`.
|
|
10
|
+
|
|
11
|
+
- Lengths and indices count UTF-16 code units. Every comparison is ordinal and
|
|
12
|
+
case-sensitive. Whitespace is C#'s `char.IsWhiteSpace` set.
|
|
13
|
+
- `Replace` replaces every non-overlapping match and returns a new string. The
|
|
14
|
+
search must be nonempty and neither argument may be null.
|
|
15
|
+
- `Split` keeps empty entries and returns a new list.
|
|
16
|
+
- `Join` takes `List<string>` or `Set<string>`. Convert other entries first:
|
|
17
|
+
`values.Select(v => $"{v}")`.
|
|
18
|
+
- `s?.Length` is an error. Use `string.IsNullOrEmpty(s)` or narrow first.
|
|
19
|
+
|
|
20
|
+
```neo
|
|
21
|
+
List<string> parts = this.Text.Trim().Split(" ");
|
|
22
|
+
int quantity = int.TryParse(parts[1]) ?? 0;
|
|
23
|
+
return $"{slot.ToString("D3")} {price.ToString("N2")}";
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Parsing
|
|
27
|
+
|
|
28
|
+
`int.Parse`, `float.Parse`, and `decimal.Parse` accept ASCII digits with an
|
|
29
|
+
optional sign (plus a fraction for float/decimal and an exponent for float)
|
|
30
|
+
after trimming ASCII whitespace. They throw on anything else or on a value the
|
|
31
|
+
type can't hold; `TryParse` returns `null` instead. `decimal.Parse` never
|
|
32
|
+
rounds: more than 28 significant or fractional digits is an error.
|
|
33
|
+
|
|
34
|
+
## Formatting
|
|
35
|
+
|
|
36
|
+
Numbers format only with an explicit format: `D<n>` (int only, zero-padded),
|
|
37
|
+
`F<n>` (fixed point), or `N<n>` (fixed point with `,` grouping). `n` is 0 to
|
|
38
|
+
28 and defaults to 2 (1 for `D`). Float rounds half to even from its exact
|
|
39
|
+
value, decimal rounds half away from zero, and an all-zero result has no `-`.
|
|
40
|
+
For plain text, interpolate: `$"{value}"`.
|
|
41
|
+
|
|
42
|
+
## Int-to-float conversion
|
|
43
|
+
|
|
44
|
+
An int assigned to `float` rounds to float32. This applies to arguments,
|
|
45
|
+
returns, collection entries, `List.Repeat` with a float entry type, and int
|
|
46
|
+
search arguments to `Contains`/`IndexOf` on float collections. Nullable
|
|
47
|
+
assignments keep `null`.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Checking, testing, debugging, and migrations
|
|
2
|
+
|
|
3
|
+
## Script commands
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
neo script check --all
|
|
7
|
+
neo script check --this Outpost --returns string 'return $"{this.Name}!";'
|
|
8
|
+
neo script check --mode setter --member ComputedName 'root.Session.Name = value;'
|
|
9
|
+
neo script check --mode nsfunction --member Outpost.RefreshUnlock 'return this.Level > 0;'
|
|
10
|
+
neo script compile --mode nsfunction --member Outpost.RefreshUnlock 'return this.Level > 0;'
|
|
11
|
+
neo script eval --returns string 'return root.Assets.Outposts[0].FullDisplayText;'
|
|
12
|
+
neo script eval --function Outpost.RefreshUnlock --this-value <id> --args '[3]'
|
|
13
|
+
neo script eval --function "Inventory.Add(Item, int)"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Prefer `--json` in automation. `check --all` rebuilds the prospective project
|
|
17
|
+
from local source only, including dialogue, migration, and text-variable
|
|
18
|
+
bodies, so new declarations need no prior push or network fetch. `eval` uses
|
|
19
|
+
authored values and the web evaluator, reports effects, and never commits
|
|
20
|
+
them. A signature selects one overload of an overloaded function.
|
|
21
|
+
|
|
22
|
+
## neo test
|
|
23
|
+
|
|
24
|
+
`neo test` compiles the complete pending candidate, including idless nested
|
|
25
|
+
construction rows, and validates the graph. Tests must pass before the first
|
|
26
|
+
push; do not add IDs or push to get compiled initializer bodies. Invalid
|
|
27
|
+
production source stops the run. It skips the extra source round trip that
|
|
28
|
+
`neo push --dry-run` performs.
|
|
29
|
+
|
|
30
|
+
- Specs live in colocated `*.spec.neo` files that never sync. Object
|
|
31
|
+
initializers work there as in bodies.
|
|
32
|
+
- Files run in parallel on up to four CPU workers. Set `test.maxWorkers` in
|
|
33
|
+
`neo.json`, pass `--maxWorkers <n>`, or `--runInBand`. Hook and test order
|
|
34
|
+
within a file is preserved; fixtures and mocks stay isolated.
|
|
35
|
+
- `--seed <n>` replays `NeoRandom`; see [math and random](math-and-random.md#random).
|
|
36
|
+
- Unchanged project and spec inputs reuse local caches. JSON
|
|
37
|
+
`timings.executeMs` and `timings.workerStartupMs` give elapsed phase times;
|
|
38
|
+
summed worker time can exceed them.
|
|
39
|
+
- Mock or spy on an overloaded function by naming its delegate type:
|
|
40
|
+
`mock.function<NeoDelegate<int, Item, int>>(Pricing.Quote)`.
|
|
41
|
+
|
|
42
|
+
## Debug output
|
|
43
|
+
|
|
44
|
+
`Debug.Log`, `Debug.LogWarning`, and `Debug.LogError` are void statements.
|
|
45
|
+
`Debug.Assert(condition, message)` logs when the condition is false. None of
|
|
46
|
+
them throw. Assert evaluates both arguments every time, even when a host sink
|
|
47
|
+
is disabled.
|
|
48
|
+
|
|
49
|
+
```neo
|
|
50
|
+
Debug.Assert(this.Items.Any(), "Expected at least one item");
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Messages carry NeoScript line/column locations and stack frames when source
|
|
54
|
+
metadata exists. The CLI logs to stderr so JSON stdout stays parseable.
|
|
55
|
+
Getter re-evaluation can log again. `Debug` is a builtin qualifier; an
|
|
56
|
+
authored local or class named `Debug` wins.
|
|
57
|
+
|
|
58
|
+
## Migrations
|
|
59
|
+
|
|
60
|
+
Migrations are tracked action files under `Migrations/`:
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
neo migrate new <name> --target <ClassName|project>
|
|
64
|
+
neo migrate list
|
|
65
|
+
neo migrate check
|
|
66
|
+
neo migrate run [--dry-run] [--skip-invalid]
|
|
67
|
+
neo migrate prune
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Run `check` first. Review storage ownership and every write; compiling does
|
|
71
|
+
not make an action writable. `run` reads the project from the server and
|
|
72
|
+
prepares writes locally like push. If another commit lands meanwhile, it is
|
|
73
|
+
refused; run it again.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Types, narrowing, and tuples
|
|
2
|
+
|
|
3
|
+
Null and type narrowing, patterns, equality, generic functions, and tuples.
|
|
4
|
+
|
|
5
|
+
## Null narrowing
|
|
6
|
+
|
|
7
|
+
- Null propagation continues through later required accesses in the same
|
|
8
|
+
chain: `config.Hat?.Head.Idle.Down.Frames` is null when `Hat` is null. A
|
|
9
|
+
nullable descendant still needs its own `?.`, `??`, `!`, or guard.
|
|
10
|
+
- `Member` and `this.Member` share narrowing. A local or parameter named
|
|
11
|
+
`Member` shadows the member; `this.Member` still names the field.
|
|
12
|
+
- Reassigning a narrowed path to a nullable value clears its facts and its
|
|
13
|
+
descendants' facts. Writes to unrelated members keep guards, also in loops.
|
|
14
|
+
A false earlier condition narrows later `else if` conditions.
|
|
15
|
+
- `a ?? b` is non-null when `b` is non-null, including in constructor
|
|
16
|
+
arguments such as `new Sprite(item.Sprite ?? SpriteInfo.Empty)`.
|
|
17
|
+
- Where a reference is expected, `first ?? fallback` picks the reference; a
|
|
18
|
+
present reference whose target is null does not select the fallback. In a
|
|
19
|
+
scalar context the reference reads its target first.
|
|
20
|
+
- Lookup results follow the same rules.
|
|
21
|
+
|
|
22
|
+
## Type patterns
|
|
23
|
+
|
|
24
|
+
```neo
|
|
25
|
+
if (item is not BedItem bedItem) {
|
|
26
|
+
throw "Expected a bed item";
|
|
27
|
+
}
|
|
28
|
+
return bedItem.Name;
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- A binding stays in scope after a guard when every path that falls through
|
|
32
|
+
matched, including when other paths return from a nested block.
|
|
33
|
+
- `value is not T name` binds only where the test is known false: the `else`
|
|
34
|
+
branch, the right of `||`, or after a rejecting guard. The operand evaluates
|
|
35
|
+
once. `not` is an ordinary identifier elsewhere.
|
|
36
|
+
- Patterns follow the possible runtime type. An open class can match an
|
|
37
|
+
unrelated interface through a subclass that implements it; a sealed class
|
|
38
|
+
without it is a compile-time impossible pattern. A class implements each
|
|
39
|
+
interface once, so a value known to be `IBox<int>` never passes
|
|
40
|
+
`is IBox<string>`.
|
|
41
|
+
- An interface guard such as `if (item is not IReceiver) continue;` narrows
|
|
42
|
+
`item` itself: it keeps its declared class or generic `T` members and gains
|
|
43
|
+
`IReceiver` members. Later class guards accumulate (`IReceiver` then
|
|
44
|
+
`Furnace` exposes both). Guards that return, throw, or break work the same.
|
|
45
|
+
- A declaration pattern keeps the original receiver's type too: after
|
|
46
|
+
`if (item is not IReceiver receiver) return;`, `item` is unchanged and
|
|
47
|
+
`receiver` exposes `IReceiver`.
|
|
48
|
+
- Generic `is` tests check whether the bound can overlap the tested type.
|
|
49
|
+
- Guards also narrow named globals and static member paths, qualified or not,
|
|
50
|
+
inherited included. Assignment clears the proof. Closures must guard again
|
|
51
|
+
because the stored value can change before they run.
|
|
52
|
+
- A generic receiver may call members of its class or interface constraint;
|
|
53
|
+
nullable receivers still need narrowing.
|
|
54
|
+
|
|
55
|
+
## Equality
|
|
56
|
+
|
|
57
|
+
`==`, `!=`, `Contains`, `IndexOf`, and `Remove` compare class, interface,
|
|
58
|
+
List, Set, and Dictionary values by identity. Primitives, enums, vectors, and
|
|
59
|
+
media references compare by value. The static type decides, so an
|
|
60
|
+
unconstrained `T` compares by value.
|
|
61
|
+
|
|
62
|
+
`Equals(other)` on a non-null generic value calls the runtime class's
|
|
63
|
+
one-argument `bool Equals` (including overrides) when it has one, otherwise
|
|
64
|
+
falls back to value equality. Local sets fall back to comparing current
|
|
65
|
+
entries in insertion order.
|
|
66
|
+
|
|
67
|
+
## Generic functions
|
|
68
|
+
|
|
69
|
+
Functions, natives, and interface functions may declare type parameters with
|
|
70
|
+
optional constraints and defaults:
|
|
71
|
+
|
|
72
|
+
```neo
|
|
73
|
+
public static T? FirstOf<T extends Item = Item>(List<T> items) => items.FirstOrDefault();
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
- Call with every type argument, omit a defaulted tail
|
|
77
|
+
(`FirstOf<Seed>(items)`), or omit `<...>` to infer from arguments.
|
|
78
|
+
- Lambda inputs bind first so their returns can infer outputs, as in
|
|
79
|
+
`Map(seed, (value) => value.Price)`. Defaults fill parameters with no
|
|
80
|
+
evidence. The expected return type is not evidence. Explicit `<...>`
|
|
81
|
+
never infers the omitted tail. Conflicting types need explicit arguments;
|
|
82
|
+
inference does not invent a common ancestor.
|
|
83
|
+
- Function type parameters take no `@id`. Overrides declare the same count,
|
|
84
|
+
constraints, and defaults but may rename them.
|
|
85
|
+
|
|
86
|
+
Generic class arguments are invariant through inheritance: `IntBox : Box<int>`
|
|
87
|
+
assigns to `Box<int>`, never `Box<string>`. Typed locals accept generic
|
|
88
|
+
classes (`Box<int> box = new(1);`). Explicit lambda parameter types accept
|
|
89
|
+
the full declaration grammar, including `decimal`, generics, collections, and
|
|
90
|
+
nullables.
|
|
91
|
+
|
|
92
|
+
## Tuples
|
|
93
|
+
|
|
94
|
+
```neo
|
|
95
|
+
(int Row, int Col) position = (Row: 2, Col: 3);
|
|
96
|
+
var (row, col) = position;
|
|
97
|
+
(int first, (bool enabled, int _)) = (1, (true, 9));
|
|
98
|
+
var score = positions.Select(((r, c)) => r + c);
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
- Two or more elements. Labels are optional and don't affect type identity;
|
|
102
|
+
`Item1`, `Item2`, ... always work. Slots are immutable: replace the tuple.
|
|
103
|
+
- `((r, c)) =>` takes one tuple; `(r, c) =>` takes two parameters. Recursive
|
|
104
|
+
patterns work in declarations, loop bindings, and lambda parameters. `_`
|
|
105
|
+
discards. Assignment deconstruction is unsupported.
|
|
106
|
+
- Equality is recursive by element; decimal scale is ignored, classes and
|
|
107
|
+
collections compare by identity.
|
|
108
|
+
- `(int, int)?` is a nullable tuple; `(int?, int)` has a nullable element.
|
|
109
|
+
|
|
110
|
+
Stored tuple members are covered in
|
|
111
|
+
[member kinds](../declarations/member-kinds.md#stored-tuples).
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Branches, releases, and access
|
|
2
|
+
|
|
3
|
+
## Branches and releases
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
neo branch list
|
|
7
|
+
neo branch create <name> [--from <ref>]
|
|
8
|
+
neo branch switch <nameOrId>
|
|
9
|
+
neo branch refresh [--dry-run]
|
|
10
|
+
neo merge <branch> [--dry-run] [--migrate]
|
|
11
|
+
neo release cut [--bump major|minor|patch] [--dry-run]
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
- Branches are copy-on-write forks; releases are immutable snapshots.
|
|
15
|
+
- Switching needs a clean checkout. Unpushed source or binaries, conflicts,
|
|
16
|
+
source errors, and pending push recovery stop it before anything changes.
|
|
17
|
+
Push or back up first; `neo pull --force` only when discarding is intended.
|
|
18
|
+
- Creation waits until the durable copy publishes. The source stays readable
|
|
19
|
+
while copying.
|
|
20
|
+
- The server derives the minimum compatibility bump; a caller may only raise
|
|
21
|
+
it. Releases need the `release` login profile.
|
|
22
|
+
- In Unity projects using `unityConfigPath`, branch and version switches
|
|
23
|
+
update `NeoComposeConfig.asset`.
|
|
24
|
+
|
|
25
|
+
## History
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
neo history inspect
|
|
29
|
+
neo history log
|
|
30
|
+
neo history prune [--project <id|@latest|@current>]
|
|
31
|
+
neo history flatten [--project <id|@latest|@current>] --version <id|@latest|@current>
|
|
32
|
+
neo history apply --plan <plan-id> [--confirm-scope <project-id>[/<version-id>]]
|
|
33
|
+
neo history status --run <run-id>
|
|
34
|
+
neo history resume --run <run-id>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Rewrites need project owner or admin. `prune` and `flatten` only create
|
|
38
|
+
immutable plans. `apply` starts the destructive run once the plan seals;
|
|
39
|
+
production also needs the exact scope confirmation the plan shows.
|
|
40
|
+
|
|
41
|
+
## Login and credentials
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
neo login [--api <url>] [--profile editor|release] [--save-project <id>]
|
|
45
|
+
neo whoami
|
|
46
|
+
neo logout [--api <url>]
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
- Inside a workspace these use its `apiBaseUrl`; `--api` wins; outside, the
|
|
50
|
+
build's default origin. Login prints the origin before storing anything.
|
|
51
|
+
- Tokens use the OS credential store, falling back to a protected file. In CI
|
|
52
|
+
use `NEO_COMPOSE_TOKEN` or `--token-stdin`. The CLI refreshes its
|
|
53
|
+
short-lived JWT during long commands.
|
|
54
|
+
- The editor profile can't publish releases; server scopes are the security
|
|
55
|
+
boundary.
|
|
56
|
+
- `logout` deletes the credential for exactly one origin and revokes the
|
|
57
|
+
session best-effort.
|
|
58
|
+
- `NEO_COMPOSE_CONFIG_HOME` (absolute path replacing
|
|
59
|
+
`$XDG_CONFIG_HOME/neo-compose`) and `NEO_COMPOSE_CREDENTIAL_NAMESPACE`
|
|
60
|
+
(keychain account scope) isolate concurrent environments on one OS user.
|
|
61
|
+
|
|
62
|
+
## Automation
|
|
63
|
+
|
|
64
|
+
- Pass explicit project/version IDs and flags, prefer `--json`, and never
|
|
65
|
+
depend on pickers or confirms. Non-TTY runs never wait for them.
|
|
66
|
+
- Boolean flags are bare switches; `--dry-run=true` is rejected.
|
|
67
|
+
- `--dry-run` exists on push, merge, refresh, branch refresh, release cut, and
|
|
68
|
+
migrate run; elsewhere it is rejected before any action. `branch create`
|
|
69
|
+
has none.
|