@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,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.
|
|
@@ -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,113 @@
|
|
|
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
|
+
- [Extensions](../declarations/extensions.md): typed receiver functions and computed properties.
|
|
106
|
+
- [Overloads](../declarations/overloads.md): declaring and calling overloads.
|
|
107
|
+
- [Constructors](../declarations/constructors.md) and
|
|
108
|
+
[initializers](../declarations/initializers.md): construction order and
|
|
109
|
+
what member defaults may read.
|
|
110
|
+
- [Dialogues](../dialogues.md#triggering-dialogues-from-neoscript):
|
|
111
|
+
`TryTrigger`, `CanTrigger`, visit counts.
|
|
112
|
+
- [Grids and cell patterns](../world/grids-and-cell-patterns.md):
|
|
113
|
+
`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.
|