incanto 0.74.0 → 0.75.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/bin/incanto-multiplay.mjs +52 -4
- package/dist/2d.d.ts +83 -7
- package/dist/2d.js +4 -3
- package/dist/3d.d.ts +743 -167
- package/dist/3d.js +6 -6
- package/dist/{agent8-CHTT4unP.js → agent8-B-bh3n0X.js} +1 -1
- package/dist/{audio-player-BOrk4eQx.d.ts → audio-player-BRo2uvG6.d.ts} +23 -2
- package/dist/{behavior-J1cmVah0.d.ts → behavior-B_245qRy.d.ts} +15 -1
- package/dist/{create-game-BAiA-FjP.js → create-game-DVUhHs2B.js} +79 -10
- package/dist/{create-game-ssvGvP7W.js → create-game-DnwhJ5nW.js} +83 -7
- package/dist/debug.d.ts +1 -1
- package/dist/{editor-switch-CFU9mCec.d.ts → editor-switch-O8mxpgRX.d.ts} +1 -1
- package/dist/editor.js +1600 -1529
- package/dist/env.d.ts +1 -1
- package/dist/{environment-presets-BkqlWewf.js → environment-presets-BP77Dl0K.js} +2005 -60
- package/dist/{gameplay-Bxe1sMVT.js → gameplay-D-JuGura.js} +9309 -6595
- package/dist/gameplay.d.ts +573 -31
- package/dist/gameplay.js +3 -2
- package/dist/index.d.ts +150 -6
- package/dist/index.js +6 -6
- package/dist/{loader-BAeWMYfF.d.ts → loader-CbkVdXL8.d.ts} +2 -2
- package/dist/net.d.ts +2 -2
- package/dist/net.js +2 -2
- package/dist/{physics-2d-C-SfvjzH.js → physics-2d-x3bCrQf0.js} +250 -31
- package/dist/{physics-3d-rOzeg890.js → physics-3d-Bz2VPQ6D.js} +673 -51
- package/dist/{picking-BMV34Pjl.js → picking-Buita5T9.js} +2 -2
- package/dist/{save-slots-CEUGeLuB.js → pose-ByFB_J3O.js} +217 -11
- package/dist/react.d.ts +2 -2
- package/dist/react.js +1 -1
- package/dist/{register-CjociOtt.js → register-B768Qedj.js} +596 -2
- package/dist/{register-DRQyZKGU.js → register-DxAbTO2T.js} +191 -430
- package/dist/{replay-B6BbgGM_.js → replay-DrqQ6dZj.js} +133 -18
- package/dist/{replay-wZK5Ok5c.d.ts → replay-W5nCw_cU.d.ts} +21 -2
- package/dist/{rng-Bb-IutXB.d.ts → rng-BsXZg3D6.d.ts} +7 -0
- package/dist/{split-screen-BxZ5rBe7.js → split-screen-B-dIcQsQ.js} +10 -5
- package/dist/{split-screen-D6sx8z3Z.d.ts → split-screen-CiLJf2-C.d.ts} +3 -3
- package/dist/{sprite-animation-Bz3DHD_4.js → sprite-animation-SQa5gIu2.js} +8 -6
- package/dist/{src-CqtvYtSN.js → src-DLKVc6Hj.js} +1 -1
- package/dist/{test-CEl0y2Sw.js → test-HlVUeCSn.js} +487 -82
- package/dist/test.d.ts +90 -12
- package/dist/test.js +2 -2
- package/dist/vite.js +3 -3
- package/editor/assets/{agent8-BlzturGi.js → agent8-BXlYM9rv.js} +1 -1
- package/editor/assets/{debug-BMUi8usj.js → debug-S42XGP4Y.js} +1 -1
- package/editor/assets/index-DgrgUKno.js +11046 -0
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/schemas/scene.schema.json +1630 -299
- package/skills/incanto-3d-character.md +358 -20
- package/skills/incanto-audio.md +24 -2
- package/skills/incanto-building-2d-games.md +21 -2
- package/skills/incanto-building-3d-games.md +38 -3
- package/skills/incanto-editor.md +17 -6
- package/skills/incanto-environment.md +227 -2
- package/skills/incanto-game-feel.md +60 -1
- package/skills/incanto-gameplay-behaviors.md +668 -20
- package/skills/incanto-hud.md +116 -0
- package/skills/incanto-multiplayer.md +141 -2
- package/skills/incanto-node-reference.md +393 -16
- package/skills/incanto-performance.md +32 -0
- package/skills/incanto-physics-and-input.md +241 -13
- package/skills/incanto-playtesting.md +2 -2
- package/skills/incanto-save-slots.md +61 -2
- package/skills/incanto-scene-json-authoring.md +13 -0
- package/skills/incanto-verifying-your-game.md +86 -3
- package/templates-app/beacon-isle-3d/PROJECT/Status.md +12 -0
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/beacon-isle-3d/src/game.scene.json +151 -2
- package/templates-app/molehill-2d/PROJECT/Status.md +7 -0
- package/templates-app/molehill-2d/package.json +1 -1
- package/templates-app/molehill-2d/src/game.scene.json +129 -2
- package/templates-app/platformer-2d/PROJECT/Status.md +7 -0
- package/templates-app/platformer-2d/docs/project-2d-rules.md +3 -2
- package/templates-app/platformer-2d/package.json +1 -1
- package/templates-app/platformer-2d/src/behaviors.ts +40 -26
- package/templates-app/platformer-2d/src/game.scene.json +144 -2
- package/templates-app/platformer-2d/verify.ts +18 -1
- package/templates-app/star-survivor/PROJECT/Status.md +7 -0
- package/templates-app/star-survivor/package.json +1 -1
- package/templates-app/star-survivor/src/behaviors.ts +9 -8
- package/templates-app/star-survivor/src/game.scene.json +142 -4
- package/templates-app/star-survivor/verify.ts +13 -0
- package/templates-app/tps-3d/PROJECT/Status.md +6 -0
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/tps-3d/src/game.scene.json +71 -10
- package/templates-app/village-quest-3d/PROJECT/Status.md +7 -0
- package/templates-app/village-quest-3d/package.json +1 -1
- package/templates-app/village-quest-3d/src/behaviors.ts +8 -16
- package/templates-app/village-quest-3d/src/grove.scene.json +121 -5
- package/templates-app/village-quest-3d/src/village.scene.json +126 -2
- package/templates-app/village-quest-3d/verify.ts +52 -2
- package/editor/assets/index-CrUCQoaB.js +0 -11046
package/dist/gameplay.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { b as Engine, d as PropSchema, jt as Node, n as BehaviorCtor, t as Behavior } from "./behavior-
|
|
2
|
-
import { c as JsonValue, s as JsonObject } from "./rng-
|
|
1
|
+
import { b as Engine, d as PropSchema, jt as Node, n as BehaviorCtor, t as Behavior } from "./behavior-B_245qRy.js";
|
|
2
|
+
import { c as JsonValue, s as JsonObject } from "./rng-BsXZg3D6.js";
|
|
3
3
|
|
|
4
4
|
//#region src/gameplay/chase.d.ts
|
|
5
5
|
/**
|
|
@@ -28,10 +28,14 @@ declare class Chase extends Behavior {
|
|
|
28
28
|
speed: number;
|
|
29
29
|
/** The group member currently chased (`targetGroup`), or null. */
|
|
30
30
|
private picked;
|
|
31
|
+
/** Read-only: the node being chased this frame, or null (out of sight, none in the group, no `target`). */
|
|
32
|
+
get chasing(): Node | null;
|
|
33
|
+
private current;
|
|
31
34
|
private sincePick;
|
|
32
35
|
stopRange: number;
|
|
33
36
|
loseRange: number;
|
|
34
37
|
moveParent: boolean;
|
|
38
|
+
requireSight: boolean;
|
|
35
39
|
ground: boolean | null;
|
|
36
40
|
facePath: string;
|
|
37
41
|
/** Signed fall speed accumulated while a ground chaser is off the floor. */
|
|
@@ -54,11 +58,44 @@ declare class Chase extends Behavior {
|
|
|
54
58
|
private dropIfAirborne;
|
|
55
59
|
/** The nearest live member of `targetGroup` — re-picked every `retargetEvery` seconds, never the mover itself. */
|
|
56
60
|
private nearestOfGroup;
|
|
61
|
+
/** `requireSight`: a static-only ray from half a metre over the mover to half a metre over the target reaches it. */
|
|
62
|
+
private canSee;
|
|
57
63
|
/** See `ground`: an explicit answer, else "is the mover a 3D kinematic capsule". */
|
|
58
64
|
private walksTheGround;
|
|
59
65
|
override update(dt: number): void;
|
|
60
66
|
}
|
|
61
67
|
//#endregion
|
|
68
|
+
//#region src/gameplay/chase-camera.d.ts
|
|
69
|
+
/**
|
|
70
|
+
* A camera BEHIND something that turns — a car, a boat, a tank, a horse.
|
|
71
|
+
*
|
|
72
|
+
* `FollowCamera` keeps a fixed world offset, which is right for a top-down or
|
|
73
|
+
* side view and wrong for a vehicle: the offset has to swing round with the
|
|
74
|
+
* target's heading, and lazily, or every corner snaps the view. Four examples
|
|
75
|
+
* (a delivery van, a harbour boat, an errand car, a race car) wrote this same
|
|
76
|
+
* behaviour by hand — `back`, `up`, a smoothed yaw — before it existed.
|
|
77
|
+
*
|
|
78
|
+
* The heading comes from the target's rotation BASIS, not from `rotation[1]`:
|
|
79
|
+
* a tilted body's Euler XYZ triple reads `[180, 180−yaw, 180]` past 90° of
|
|
80
|
+
* yaw, and a camera that read the middle number swung to the wrong side. Aim
|
|
81
|
+
* the camera with `Camera3D.lookAt` (or `Camera2D.follow`); this only places it.
|
|
82
|
+
*/
|
|
83
|
+
declare class ChaseCamera extends Behavior {
|
|
84
|
+
static readonly props: PropSchema;
|
|
85
|
+
static readonly signals: readonly string[];
|
|
86
|
+
target: string;
|
|
87
|
+
back: number;
|
|
88
|
+
up: number;
|
|
89
|
+
smoothing: number;
|
|
90
|
+
/** The heading the camera currently trails (radians) — a harness reads it. */
|
|
91
|
+
heading: number | null;
|
|
92
|
+
override onReady(): void;
|
|
93
|
+
override update(dt: number): void;
|
|
94
|
+
private placed;
|
|
95
|
+
/** Put the camera on its mark NOW (a scene start, a respawn) instead of flying there. */
|
|
96
|
+
snap(): void;
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
62
99
|
//#region src/gameplay/collector.d.ts
|
|
63
100
|
/**
|
|
64
101
|
* A tally for the "collect N things" pattern, living on the collector itself
|
|
@@ -109,8 +146,23 @@ declare class Currency extends Behavior {
|
|
|
109
146
|
canAfford(cost: number): boolean;
|
|
110
147
|
/** Pay `cost` if it is there. Returns whether it went through. */
|
|
111
148
|
spend(cost: number): boolean;
|
|
112
|
-
/**
|
|
149
|
+
/**
|
|
150
|
+
* Take payment in — and say what did NOT fit.
|
|
151
|
+
*
|
|
152
|
+
* This clamped at `max` and emitted `earned(gain)` anyway, so a purse with a
|
|
153
|
+
* ceiling took a coin it could not hold and told the game it had. Measured on
|
|
154
|
+
* a game whose purse holds four: the fifth pickup vanished off the floor, the
|
|
155
|
+
* HUD said `4 / 4`, and nothing anywhere had the pickup. The game then wrote
|
|
156
|
+
* `if (purse.amount >= purse.max)` itself — the comparison `spend`'s
|
|
157
|
+
* `refused` exists so that nobody repeats.
|
|
158
|
+
*
|
|
159
|
+
* `earned` is now what was actually TAKEN, and `refused` carries the
|
|
160
|
+
* remainder. A purse with no ceiling never refuses, which is every purse
|
|
161
|
+
* written before this.
|
|
162
|
+
*/
|
|
113
163
|
earn(gain: number): void;
|
|
164
|
+
/** How much more this purse can hold (Infinity with no ceiling). */
|
|
165
|
+
get room(): number;
|
|
114
166
|
/** What has already been complained about, so a loop says it once. */
|
|
115
167
|
private complained;
|
|
116
168
|
/**
|
|
@@ -154,6 +206,29 @@ declare class Health extends Behavior {
|
|
|
154
206
|
invulnerableFor: number;
|
|
155
207
|
freeOnDeath: boolean;
|
|
156
208
|
freeParent: boolean;
|
|
209
|
+
knockback: number;
|
|
210
|
+
knockbackSeconds: number;
|
|
211
|
+
knockUp: number;
|
|
212
|
+
staggerSeconds: number;
|
|
213
|
+
applyKnockback: boolean;
|
|
214
|
+
/** Unit direction of the last hit that had a source, source → this body (ground plane). */
|
|
215
|
+
hitDirection: number[];
|
|
216
|
+
private staggerLeft;
|
|
217
|
+
private kickLeft;
|
|
218
|
+
private kick;
|
|
219
|
+
private kickFresh;
|
|
220
|
+
/** True while a hit's stagger holds this body's movers and controllers. */
|
|
221
|
+
get staggered(): boolean;
|
|
222
|
+
/** True while a hit's shove is still moving this body. */
|
|
223
|
+
get knocked(): boolean;
|
|
224
|
+
/** The shove's current velocity (decaying), empty when not knocked — a controller that drives the body reads it. */
|
|
225
|
+
get kickVelocity(): number[];
|
|
226
|
+
/** The vertical part of the shove, ONCE (a lift is an impulse, not a per-frame write); 0 after. */
|
|
227
|
+
takeLift(): number;
|
|
228
|
+
/** A body driven by a character controller gets its shove from the controller, not from here. */
|
|
229
|
+
private drivenByController;
|
|
230
|
+
/** The body a hit moves: this node if it is spatial, else the nearest spatial ancestor (a Health on a child `Hp` node moves its parent). */
|
|
231
|
+
private bodyNode;
|
|
157
232
|
/** Current hit points (set to `max` on ready). */
|
|
158
233
|
current: number;
|
|
159
234
|
private invulnTimer;
|
|
@@ -165,7 +240,15 @@ declare class Health extends Behavior {
|
|
|
165
240
|
override onReady(): void;
|
|
166
241
|
override update(dt: number): void;
|
|
167
242
|
/** Apply `n` damage. No-op while invulnerable or already dead. Emits `damaged`. */
|
|
168
|
-
|
|
243
|
+
/**
|
|
244
|
+
* The shove, frame by frame, decaying linearly — written EVERY frame like a
|
|
245
|
+
* dash, because a controller brakes toward its own pace and a one-frame
|
|
246
|
+
* velocity dies. A dynamic body gets its velocity written (the vertical part
|
|
247
|
+
* once, as a lift); a kinematic or plain node is displaced through
|
|
248
|
+
* `moveBody` with `force`, so it still slides along walls.
|
|
249
|
+
*/
|
|
250
|
+
private applyKick;
|
|
251
|
+
damage(n: number, from?: readonly number[] | undefined): void;
|
|
169
252
|
/**
|
|
170
253
|
* Be immune for `seconds` from NOW — a dodge roll, a spawn, a cutscene.
|
|
171
254
|
*
|
|
@@ -389,6 +472,7 @@ declare class Interactable extends Behavior {
|
|
|
389
472
|
focusedBy: Node | null;
|
|
390
473
|
/** Actions already reported missing, so a per-frame read says it once. */
|
|
391
474
|
private readonly reportedActions;
|
|
475
|
+
override onReady(): void;
|
|
392
476
|
override update(): void;
|
|
393
477
|
/** Is THIS the Interactable `actor` means — the nearest (that it faces) of every one in reach? */
|
|
394
478
|
private claims;
|
|
@@ -423,6 +507,52 @@ declare class Lifetime extends Behavior {
|
|
|
423
507
|
override update(dt: number): void;
|
|
424
508
|
}
|
|
425
509
|
//#endregion
|
|
510
|
+
//#region src/gameplay/mount.d.ts
|
|
511
|
+
/**
|
|
512
|
+
* Get on something and drive it; get off beside it.
|
|
513
|
+
*
|
|
514
|
+
* A horse, a car, a boat, a turret seat: the player's controller lets go, the
|
|
515
|
+
* player's body PARKS (collider off, hidden, weightless, riding along so
|
|
516
|
+
* everything that targets the player follows), the player's `skin` moves onto
|
|
517
|
+
* the steed's `saddle`, and the steed's `drive` — a `CharacterController3D` or
|
|
518
|
+
* a `Vehicle3D` — is enabled. `dismount()` puts everything back beside the
|
|
519
|
+
* steed. Two examples (an errand car, a saddled horse) wrote these sixty lines
|
|
520
|
+
* before it existed, and both forgot something the other remembered.
|
|
521
|
+
*
|
|
522
|
+
* Wire it from an `Interactable`: `interacted → Mount.toggle`.
|
|
523
|
+
*/
|
|
524
|
+
declare class Mount extends Behavior {
|
|
525
|
+
static readonly props: PropSchema;
|
|
526
|
+
static readonly signals: readonly string[];
|
|
527
|
+
rider: string;
|
|
528
|
+
steed: string;
|
|
529
|
+
drive: string;
|
|
530
|
+
walk: string;
|
|
531
|
+
skin: string;
|
|
532
|
+
saddle: string;
|
|
533
|
+
skinOnSaddle: number[];
|
|
534
|
+
skinOnRider: number[];
|
|
535
|
+
dismountAt: number[];
|
|
536
|
+
/** True while the rider is on. */
|
|
537
|
+
mounted: boolean;
|
|
538
|
+
mounts: number;
|
|
539
|
+
private parked;
|
|
540
|
+
override onReady(): void;
|
|
541
|
+
private riderNode;
|
|
542
|
+
private steedNode;
|
|
543
|
+
/** The first child (or the named one) carrying a boolean `enabled` and a `yaw` or a `throttle` — a controller or a vehicle. */
|
|
544
|
+
private switchOf;
|
|
545
|
+
private skinNode;
|
|
546
|
+
private saddleNode;
|
|
547
|
+
/** Get on. False when already on. */
|
|
548
|
+
mount(): boolean;
|
|
549
|
+
/** Get off beside the steed. False when not on. */
|
|
550
|
+
dismount(): boolean;
|
|
551
|
+
/** On if off, off if on — the handler an `Interactable`'s `interacted` wires to. */
|
|
552
|
+
toggle(): void;
|
|
553
|
+
override update(): void;
|
|
554
|
+
}
|
|
555
|
+
//#endregion
|
|
426
556
|
//#region src/gameplay/tween.d.ts
|
|
427
557
|
/** Easing curves shared by the tween behaviors (MoveTo, …). Pure math. */
|
|
428
558
|
type EaseName = "linear" | "easeIn" | "easeOut" | "easeInOut";
|
|
@@ -451,6 +581,12 @@ declare class MoveTo extends Behavior {
|
|
|
451
581
|
override onReady(): void;
|
|
452
582
|
/** Begin (or restart) the move from the node's CURRENT position. */
|
|
453
583
|
start(): void;
|
|
584
|
+
/**
|
|
585
|
+
* Land on `to` NOW and say `arrived` (once). A skipped cutscene must leave
|
|
586
|
+
* the world as the whole cutscene would have — the door it opens, open.
|
|
587
|
+
* Wire `skipped → Door.finish`.
|
|
588
|
+
*/
|
|
589
|
+
finish(): void;
|
|
454
590
|
override update(dt: number): void;
|
|
455
591
|
}
|
|
456
592
|
//#endregion
|
|
@@ -565,6 +701,54 @@ declare class Patrol extends Behavior {
|
|
|
565
701
|
private advance;
|
|
566
702
|
}
|
|
567
703
|
//#endregion
|
|
704
|
+
//#region src/gameplay/phases.d.ts
|
|
705
|
+
/**
|
|
706
|
+
* A boss that changes at two thirds and again at a third.
|
|
707
|
+
*
|
|
708
|
+
* Every other common wiring in this engine became declarative — score to
|
|
709
|
+
* screen, damage to a health bar, waves, win, lose, the save, the restore — and
|
|
710
|
+
* a fight's PHASES stayed the one thing that needed TypeScript. The shipped
|
|
711
|
+
* boss reads `healthChanged(current, max)`, divides, compares against two
|
|
712
|
+
* constants and calls its own `enterPhase`; every boss anyone builds writes
|
|
713
|
+
* those eight lines again.
|
|
714
|
+
*
|
|
715
|
+
* This is the threshold half and only that half. WHAT a phase does is the
|
|
716
|
+
* game's business, and it is reached through connections:
|
|
717
|
+
*
|
|
718
|
+
* ```json
|
|
719
|
+
* { "name": "Mood", "type": "Node", "uid": "n_…",
|
|
720
|
+
* "script": { "name": "Phases", "props": { "at": [0.66, 0.33] } } }
|
|
721
|
+
* ```
|
|
722
|
+
* ```json
|
|
723
|
+
* { "signal": "phase2", "from": "Boss/Mood", "to": "Adds", "handler": "start" },
|
|
724
|
+
* { "signal": "phase3", "from": "Boss/Mood", "to": "Roar", "handler": "play" },
|
|
725
|
+
* { "signal": "phaseChanged", "from": "Boss/Mood", "to": "Hud", "handler": "onPhase" }
|
|
726
|
+
* ```
|
|
727
|
+
*
|
|
728
|
+
* The numbered signals exist because a connection cannot filter on a NUMBER: a
|
|
729
|
+
* `filter` gates on a node's group or tags, so `phaseChanged(2)` could only ever
|
|
730
|
+
* reach one handler that switches. `phase2`…`phase5` is what makes "phase two
|
|
731
|
+
* starts the adds" a line in the scene file.
|
|
732
|
+
*/
|
|
733
|
+
declare class Phases extends Behavior {
|
|
734
|
+
static readonly props: PropSchema;
|
|
735
|
+
static readonly signals: readonly string[];
|
|
736
|
+
at: number[];
|
|
737
|
+
health: string;
|
|
738
|
+
/** Which phase the fight is in: 1 until the first threshold is crossed. */
|
|
739
|
+
phase: number;
|
|
740
|
+
private watched;
|
|
741
|
+
override onReady(): void;
|
|
742
|
+
/** Wire this to a `healthChanged` yourself when the health is somewhere odd. */
|
|
743
|
+
read(current: number, max: number): void;
|
|
744
|
+
/** The phase survives a save, like every other run-state number here. */
|
|
745
|
+
override serialize(): {
|
|
746
|
+
phase: number;
|
|
747
|
+
};
|
|
748
|
+
override deserialize(data: unknown): void;
|
|
749
|
+
override announce(): void;
|
|
750
|
+
}
|
|
751
|
+
//#endregion
|
|
568
752
|
//#region src/gameplay/pickup.d.ts
|
|
569
753
|
/**
|
|
570
754
|
* A collectible that vanishes when a collector overlaps it. Must sit on an
|
|
@@ -653,7 +837,15 @@ declare class Projectile extends Behavior {
|
|
|
653
837
|
* This is what a `spawned(node)` handler calls after turning the clone, and
|
|
654
838
|
* the only way a `'forward'` projectile ever re-reads its rotation.
|
|
655
839
|
*/
|
|
656
|
-
|
|
840
|
+
/**
|
|
841
|
+
* Bake the velocity from `direction` × `speed` — plus `carry`, the SHOOTER's
|
|
842
|
+
* velocity, so a shell fired from a tank doing 16 m/s flies with it instead
|
|
843
|
+
* of at the standing tank's speed (a tank could outrun its own shot; a
|
|
844
|
+
* sideways shell landed behind what the barrel pointed at). Added once, at
|
|
845
|
+
* launch; a later `speed` or `direction` write re-bakes with the same carry.
|
|
846
|
+
*/
|
|
847
|
+
aim(direction?: JsonValue | undefined, carry?: readonly number[] | undefined): void;
|
|
848
|
+
private carry;
|
|
657
849
|
override onReady(): void;
|
|
658
850
|
override update(dt: number): void;
|
|
659
851
|
/** Resolve `direction` to a unit vector, deriving 'forward' from rotation. */
|
|
@@ -692,6 +884,9 @@ declare class SavePoint extends Behavior {
|
|
|
692
884
|
scene: string;
|
|
693
885
|
restoreOnReady: boolean;
|
|
694
886
|
probeOnReady: boolean;
|
|
887
|
+
keep: string[];
|
|
888
|
+
/** Poses read out of the slot, waiting for `announce()` to put them back. */
|
|
889
|
+
private keptPoses;
|
|
695
890
|
/** Seconds this run has been playing — written into the slot as `playtime`. */
|
|
696
891
|
private elapsed;
|
|
697
892
|
private slots;
|
|
@@ -747,9 +942,20 @@ declare class SavePoint extends Behavior {
|
|
|
747
942
|
restore(): void;
|
|
748
943
|
/** Forget this slot — a "delete save" button. */
|
|
749
944
|
clear(): void;
|
|
750
|
-
/** The run's own clock
|
|
945
|
+
/** The run's own clock — and the poses the scene asked to keep. */
|
|
751
946
|
override serialize(): JsonValue;
|
|
752
947
|
override deserialize(data: JsonValue): void;
|
|
948
|
+
/**
|
|
949
|
+
* Put the kept nodes back where the run left them.
|
|
950
|
+
*
|
|
951
|
+
* In `announce()` and not in `deserialize()`, for the reason the hook exists:
|
|
952
|
+
* a pose written mid-pass can be overwritten by another behavior's restore
|
|
953
|
+
* (a camera rig, a controller reading its target), and announce runs after
|
|
954
|
+
* the whole pass has landed.
|
|
955
|
+
*/
|
|
956
|
+
override announce(): void;
|
|
957
|
+
/** Read the poses the scene named — reporting the ones it cannot. */
|
|
958
|
+
private readPoses;
|
|
753
959
|
}
|
|
754
960
|
//#endregion
|
|
755
961
|
//#region src/gameplay/score-keeper.d.ts
|
|
@@ -983,6 +1189,88 @@ declare class ZombieAI extends Behavior {
|
|
|
983
1189
|
private pickGoal;
|
|
984
1190
|
}
|
|
985
1191
|
//#endregion
|
|
1192
|
+
//#region src/gameplay/ballistics.d.ts
|
|
1193
|
+
/**
|
|
1194
|
+
* The two questions a lobbed shot asks: **at what angle**, and **where will it
|
|
1195
|
+
* land**.
|
|
1196
|
+
*
|
|
1197
|
+
* `Projectile.gravity` has flown arcing shells since it shipped, and nothing
|
|
1198
|
+
* could aim one: `Turret` pointed straight at its target, so a gun firing a
|
|
1199
|
+
* falling shell at anything further than a few metres dropped it short — a
|
|
1200
|
+
* measured 66 m short, on an artillery duel built from the package. Every game
|
|
1201
|
+
* that wanted a mortar, a grenade, a catapult or a lobbed spell wrote this
|
|
1202
|
+
* arithmetic itself, and a preview drawn with slightly different arithmetic
|
|
1203
|
+
* lands somewhere the shell does not.
|
|
1204
|
+
*/
|
|
1205
|
+
/**
|
|
1206
|
+
* The unit direction that puts a shot from `from` onto `to`, or `null` when
|
|
1207
|
+
* the speed cannot reach it.
|
|
1208
|
+
*
|
|
1209
|
+
* `arc` picks the root: `'low'` is the flat, fast one (a rifle grenade),
|
|
1210
|
+
* `'high'` the mortar's — same landing point, a much taller flight, and the
|
|
1211
|
+
* one that clears a wall in between.
|
|
1212
|
+
*
|
|
1213
|
+
* Dimension-agnostic, like everything else here: a 3D shot spreads over x/z
|
|
1214
|
+
* with +y up, a 2D one over x with −y up (the y-down screen convention
|
|
1215
|
+
* `Projectile.gravity` already follows).
|
|
1216
|
+
*/
|
|
1217
|
+
declare function ballisticAim(from: readonly number[], to: readonly number[], speed: number, gravity: number, arc?: "low" | "high"): number[] | null;
|
|
1218
|
+
/** The angle that carries a shot FURTHEST — what a gun fires when it cannot reach. */
|
|
1219
|
+
declare function maxRangeAim(from: readonly number[], to: readonly number[]): number[] | null;
|
|
1220
|
+
interface BallisticPathOptions {
|
|
1221
|
+
/** How long to walk, in seconds (default 12). */
|
|
1222
|
+
seconds?: number;
|
|
1223
|
+
/** The step — leave it at the engine's fixed step so the walk matches the flight. */
|
|
1224
|
+
dt?: number;
|
|
1225
|
+
/** Called with each point; return true to stop (the ground, a wall, the map edge). */
|
|
1226
|
+
stop?: (at: readonly number[]) => boolean;
|
|
1227
|
+
}
|
|
1228
|
+
/**
|
|
1229
|
+
* Walk the flight a `Projectile` with this `speed`, `direction` and `gravity`
|
|
1230
|
+
* WOULD fly — the same integration, step for step.
|
|
1231
|
+
*
|
|
1232
|
+
* That is the point: an aiming line drawn from the closed-form parabola drifts
|
|
1233
|
+
* from the shell the engine actually flies (Euler with the fall accumulated
|
|
1234
|
+
* first), and the player aims by the line. Same arithmetic, same landing.
|
|
1235
|
+
*/
|
|
1236
|
+
declare function ballisticPath(from: readonly number[], direction: readonly number[], speed: number, gravity: number, options?: BallisticPathOptions | undefined): number[][];
|
|
1237
|
+
//#endregion
|
|
1238
|
+
//#region src/gameplay/blast.d.ts
|
|
1239
|
+
declare class Blast extends Behavior {
|
|
1240
|
+
static readonly props: PropSchema;
|
|
1241
|
+
static readonly signals: readonly string[];
|
|
1242
|
+
radius: number;
|
|
1243
|
+
damage: number;
|
|
1244
|
+
falloff: "linear" | "none";
|
|
1245
|
+
groups: string;
|
|
1246
|
+
when: string;
|
|
1247
|
+
self: boolean;
|
|
1248
|
+
throughWalls: boolean;
|
|
1249
|
+
private went;
|
|
1250
|
+
/** Has it gone off? A keg explodes once. */
|
|
1251
|
+
get spent(): boolean;
|
|
1252
|
+
private get carrier();
|
|
1253
|
+
override onReady(): void;
|
|
1254
|
+
/** Go off now: everything with a `Health` inside `radius` takes its share. */
|
|
1255
|
+
blast(): number;
|
|
1256
|
+
/** Who is even a candidate: the named groups, or every HEALTH in the tree. */
|
|
1257
|
+
private candidates;
|
|
1258
|
+
/**
|
|
1259
|
+
* `throughWalls: false` — a static-only ray, the same one
|
|
1260
|
+
* `Turret.requireSight` casts, with the two things a BLAST has to know that
|
|
1261
|
+
* a turret does not.
|
|
1262
|
+
*
|
|
1263
|
+
* A bomb lies on the ground, so its ray starts inside the floor's collider
|
|
1264
|
+
* and Rapier reports a hit at zero distance: every victim looked shielded,
|
|
1265
|
+
* by the ground the bomb was standing on. And a crate solid enough to walk
|
|
1266
|
+
* into is a collider between the blast and the crate's own centre, so the
|
|
1267
|
+
* first thing a bomb could never destroy was the box beside it. Neither is a
|
|
1268
|
+
* wall: a hit at the origin is what the blast is sitting on, and a hit that
|
|
1269
|
+
* belongs to the target is the target.
|
|
1270
|
+
*/
|
|
1271
|
+
private reaches;
|
|
1272
|
+
}
|
|
1273
|
+
//#endregion
|
|
986
1274
|
//#region src/gameplay/checkpoint.d.ts
|
|
987
1275
|
/**
|
|
988
1276
|
* A checkpoint: touch it and a `Respawn` puts you back HERE from now on.
|
|
@@ -1007,7 +1295,38 @@ declare class Checkpoint extends Behavior {
|
|
|
1007
1295
|
dropBelow: number;
|
|
1008
1296
|
/** True after the first activation. */
|
|
1009
1297
|
lit: boolean;
|
|
1298
|
+
/**
|
|
1299
|
+
* Where this checkpoint sent the catcher, and WHEN it did.
|
|
1300
|
+
*
|
|
1301
|
+
* The position is not derivable at restore: a checkpoint can be lit while its
|
|
1302
|
+
* own node has since moved (a lift, a boat), and `dropBelow` is applied once
|
|
1303
|
+
* at lighting. The clock is what orders three lit checkpoints back into the
|
|
1304
|
+
* order the run walked them.
|
|
1305
|
+
*/
|
|
1306
|
+
private sentTo;
|
|
1307
|
+
private litAt;
|
|
1010
1308
|
override onReady(): void;
|
|
1309
|
+
/**
|
|
1310
|
+
* What a save has to carry: WHICH checkpoint the run had reached.
|
|
1311
|
+
*
|
|
1312
|
+
* `Checkpoint` had none of this, so a continued run came back with every
|
|
1313
|
+
* checkpoint dark and the `Respawn` pointing at the level's start — the first
|
|
1314
|
+
* fall after a continue undid the whole session. It was not in the skill's
|
|
1315
|
+
* list of built-ins that save either, so nothing said it would not.
|
|
1316
|
+
*/
|
|
1317
|
+
override serialize(): JsonValue;
|
|
1318
|
+
override deserialize(data: JsonValue): void;
|
|
1319
|
+
/**
|
|
1320
|
+
* Point the catcher back where the run left it.
|
|
1321
|
+
*
|
|
1322
|
+
* Every lit checkpoint sharing a catcher runs this, and they must not fight:
|
|
1323
|
+
* each one writes only if no lit sibling on the same `Respawn` was walked
|
|
1324
|
+
* LATER, so all of them reach the same answer and the order they announce in
|
|
1325
|
+
* does not matter.
|
|
1326
|
+
*/
|
|
1327
|
+
override announce(): void;
|
|
1328
|
+
/** Was another lit checkpoint on this same catcher reached after this one? */
|
|
1329
|
+
private laterSiblingLit;
|
|
1011
1330
|
}
|
|
1012
1331
|
//#endregion
|
|
1013
1332
|
//#region src/gameplay/day-night.d.ts
|
|
@@ -1194,8 +1513,112 @@ declare class GameFlow extends Behavior {
|
|
|
1194
1513
|
* game will have. `UiPanel` appeared in zero template scenes before this.
|
|
1195
1514
|
*/
|
|
1196
1515
|
pausePanelPath: string;
|
|
1516
|
+
/**
|
|
1517
|
+
* A screen shown at BOOT, holding the world until something resumes.
|
|
1518
|
+
*
|
|
1519
|
+
* '' (the default) is a game that starts playing, which is every game this
|
|
1520
|
+
* engine shipped until now. Naming a panel here is the whole of a title
|
|
1521
|
+
* screen: the flow pauses in `onReady`, shows this instead of the pause
|
|
1522
|
+
* panel, and a `PLAY` button wires straight to `resume` —
|
|
1523
|
+
*
|
|
1524
|
+
* ```json
|
|
1525
|
+
* { "from": "%Play", "signal": "pressed", "to": ".", "handler": "resume" }
|
|
1526
|
+
* ```
|
|
1527
|
+
*
|
|
1528
|
+
* Every shipped shell wrote that in TypeScript, together with the eighty
|
|
1529
|
+
* lines below it, and `incanto-hud.md` told authors to copy one.
|
|
1530
|
+
*/
|
|
1531
|
+
titlePanelPath: string;
|
|
1532
|
+
/**
|
|
1533
|
+
* Where this game keeps its saves — a `SaveSlots` namespace. `''` = no saving.
|
|
1534
|
+
*
|
|
1535
|
+
* `SaveSlots`, `captureState` and `restoreState` have existed since the save
|
|
1536
|
+
* round and no shipped starter used any of them: a scaffolded game could not
|
|
1537
|
+
* be closed and come back. The API was never the problem; the SEQUENCE was —
|
|
1538
|
+
* write a slot, reload the scene from source, wait for the reload, restore by
|
|
1539
|
+
* uid — and every game that wanted a save menu had to get that right before it
|
|
1540
|
+
* had one. `restart()` is already the engine's verb for "reload this scene",
|
|
1541
|
+
* wired from a button with no TypeScript; `save()` and `continueFrom()` are
|
|
1542
|
+
* its two siblings.
|
|
1543
|
+
*
|
|
1544
|
+
* WHEN to save is still yours. The skill's rule stands — there is no autosave
|
|
1545
|
+
* — you wire `save` to whatever signal marks the moment: a checkpoint, a level
|
|
1546
|
+
* end, a button in the pause menu.
|
|
1547
|
+
*/
|
|
1548
|
+
saveSlots: string;
|
|
1549
|
+
/** Lazily built, so a game with no `saveSlots` never touches storage. */
|
|
1550
|
+
private slots;
|
|
1551
|
+
private store;
|
|
1552
|
+
/**
|
|
1553
|
+
* Write this scene's behaviour state into a slot. `false` = nothing was
|
|
1554
|
+
* written, and the log says why.
|
|
1555
|
+
*/
|
|
1556
|
+
save(slot?: unknown): boolean;
|
|
1557
|
+
/** Is there something in this slot to come back to? */
|
|
1558
|
+
hasSave(slot?: unknown): boolean;
|
|
1559
|
+
/**
|
|
1560
|
+
* Reload this scene and hand every behaviour its state back.
|
|
1561
|
+
*
|
|
1562
|
+
* The order is the whole feature: `restoreState` has to run AFTER the reload
|
|
1563
|
+
* and after `onReady`, because `onReady` is where a behaviour sets its
|
|
1564
|
+
* starting values and restoring first would be overwritten.
|
|
1565
|
+
*/
|
|
1566
|
+
continueFrom(slot?: unknown): boolean;
|
|
1567
|
+
/**
|
|
1568
|
+
* Screens pushed OVER the base one — options, credits, controls.
|
|
1569
|
+
*
|
|
1570
|
+
* The genuinely hard part of a shell is BACK. Options opens from the title
|
|
1571
|
+
* AND from the pause menu and has to return to whichever asked, which is a
|
|
1572
|
+
* stack; every game that hand-rolls it eventually gets a screen stuck. Two
|
|
1573
|
+
* connection rows now:
|
|
1574
|
+
*
|
|
1575
|
+
* ```json
|
|
1576
|
+
* { "from": "%TitleOptions", "signal": "pressed", "to": ".",
|
|
1577
|
+
* "handler": "screen", "args": ["%Options"] },
|
|
1578
|
+
* { "from": "%Back", "signal": "pressed", "to": ".", "handler": "back" }
|
|
1579
|
+
* ```
|
|
1580
|
+
*/
|
|
1581
|
+
private readonly screens;
|
|
1582
|
+
/**
|
|
1583
|
+
* Every screen this flow has ever raised — the set it is allowed to HIDE.
|
|
1584
|
+
*
|
|
1585
|
+
* Popping the stack before syncing would drop the closing screen out of the
|
|
1586
|
+
* list of panels to consider, so `back()` showed the title again and left
|
|
1587
|
+
* Options on top of it. A screen that has been opened stays this flow's to
|
|
1588
|
+
* close.
|
|
1589
|
+
*/
|
|
1590
|
+
private readonly everShown;
|
|
1591
|
+
/**
|
|
1592
|
+
* Is the BASE screen the title rather than the pause menu?
|
|
1593
|
+
*
|
|
1594
|
+
* Both are "the world is held and a panel is up", and only this tells them
|
|
1595
|
+
* apart — so `back()` from Options knows which one to bring back.
|
|
1596
|
+
*/
|
|
1597
|
+
private onTitle;
|
|
1598
|
+
/**
|
|
1599
|
+
* Push a screen over whatever is showing, and hold the world while it is up.
|
|
1600
|
+
*
|
|
1601
|
+
* A connection handler, so a button reaches it with no script:
|
|
1602
|
+
* `{"handler": "screen", "args": ["%Options"]}` — the bound argument is the
|
|
1603
|
+
* panel's node path.
|
|
1604
|
+
*
|
|
1605
|
+
* Opening a screen during PLAY freezes the world (an inventory, a map), and
|
|
1606
|
+
* `back()` gives it back. Opening one while already paused keeps the pause.
|
|
1607
|
+
*/
|
|
1608
|
+
screen(path: unknown): void;
|
|
1609
|
+
/**
|
|
1610
|
+
* Close the top screen and show whoever asked for it.
|
|
1611
|
+
*
|
|
1612
|
+
* With nothing left on the stack this returns to the base screen — the title
|
|
1613
|
+
* at boot, the pause menu while paused — and if there is no base screen
|
|
1614
|
+
* either, it resumes: a screen opened during play closes back into the game.
|
|
1615
|
+
*/
|
|
1616
|
+
back(): void;
|
|
1197
1617
|
state: GameFlowState;
|
|
1198
1618
|
private frozeScale;
|
|
1619
|
+
/** Dropped when this flow's node leaves the tree — see `onReady`. */
|
|
1620
|
+
private unwatchScene;
|
|
1621
|
+
override onExitTree(): void;
|
|
1199
1622
|
override onReady(): void;
|
|
1200
1623
|
/**
|
|
1201
1624
|
* The panel follows the state; nothing else may own its visibility.
|
|
@@ -1230,6 +1653,52 @@ declare class GameFlow extends Behavior {
|
|
|
1230
1653
|
override update(): void;
|
|
1231
1654
|
}
|
|
1232
1655
|
//#endregion
|
|
1656
|
+
//#region src/gameplay/group-camera.d.ts
|
|
1657
|
+
/**
|
|
1658
|
+
* One camera, several players — the couch co-op camera.
|
|
1659
|
+
*
|
|
1660
|
+
* `FollowCamera` follows ONE target and `ChaseCamera` rides behind ONE
|
|
1661
|
+
* character, so a game with two players at the same keyboard had nothing to
|
|
1662
|
+
* frame them both: whoever the camera followed could walk the other off screen,
|
|
1663
|
+
* and the other player's half of the game went with them.
|
|
1664
|
+
*
|
|
1665
|
+
* This keeps EVERY target in frame: the camera sits at their centroid plus
|
|
1666
|
+
* `offset`, and pulls BACK along that offset until the pair that is furthest
|
|
1667
|
+
* apart fits inside `padding` of the view. It is the Overcooked / Castle
|
|
1668
|
+
* Crashers / Lovers-in-a-Dangerous-Spacetime camera.
|
|
1669
|
+
*
|
|
1670
|
+
* ```json
|
|
1671
|
+
* { "name": "Camera", "type": "Camera3D", "props": { "current": true, "fov": 50 },
|
|
1672
|
+
* "script": { "name": "GroupCamera",
|
|
1673
|
+
* "props": { "targets": "player", "offset": [0, 12, 14],
|
|
1674
|
+
* "minZoom": 0.8, "maxZoom": 2.6, "smoothing": 0.12 } } }
|
|
1675
|
+
* ```
|
|
1676
|
+
*
|
|
1677
|
+
* Pair it with `CharacterController3D`'s `camera: "none"` on every player, or
|
|
1678
|
+
* the controllers will fight it for the camera every frame.
|
|
1679
|
+
*/
|
|
1680
|
+
declare class GroupCamera extends Behavior {
|
|
1681
|
+
static readonly props: PropSchema;
|
|
1682
|
+
static readonly signals: readonly string[];
|
|
1683
|
+
targets: string;
|
|
1684
|
+
offset: number[];
|
|
1685
|
+
minZoom: number;
|
|
1686
|
+
maxZoom: number;
|
|
1687
|
+
padding: number;
|
|
1688
|
+
smoothing: number;
|
|
1689
|
+
lookAtGroup: boolean;
|
|
1690
|
+
/** The zoom actually in use — 1 is the authored offset. */
|
|
1691
|
+
zoom: number;
|
|
1692
|
+
/** The point the camera is framing. */
|
|
1693
|
+
centre: number[];
|
|
1694
|
+
override onReady(): void;
|
|
1695
|
+
/** Everyone the camera is trying to keep on screen, right now. */
|
|
1696
|
+
members(): Node[];
|
|
1697
|
+
override update(dt: number): void;
|
|
1698
|
+
/** Snap to the group now — after a teleport, a scene swap, a respawn. */
|
|
1699
|
+
snap(): void;
|
|
1700
|
+
}
|
|
1701
|
+
//#endregion
|
|
1233
1702
|
//#region src/gameplay/juice.d.ts
|
|
1234
1703
|
/**
|
|
1235
1704
|
* Game-feel primitives ("juice"): the weapon cooldown, camera shake, screen
|
|
@@ -1339,6 +1808,9 @@ declare class PathFollow extends Behavior {
|
|
|
1339
1808
|
speed: number;
|
|
1340
1809
|
loop: boolean;
|
|
1341
1810
|
moveParent: boolean;
|
|
1811
|
+
path: string;
|
|
1812
|
+
autoStart: boolean;
|
|
1813
|
+
private declared;
|
|
1342
1814
|
facePath: string;
|
|
1343
1815
|
turnSpeed: number;
|
|
1344
1816
|
private waypoints;
|
|
@@ -1346,6 +1818,9 @@ declare class PathFollow extends Behavior {
|
|
|
1346
1818
|
/** True while there is somewhere left to go. */
|
|
1347
1819
|
get moving(): boolean;
|
|
1348
1820
|
setPath(waypoints: readonly (readonly number[])[]): void;
|
|
1821
|
+
override onReady(): void;
|
|
1822
|
+
/** Walk the declared `path` from its first waypoint — the cue a connection can give. */
|
|
1823
|
+
start(): void;
|
|
1349
1824
|
/** Stop in place (keeps the node where it is). */
|
|
1350
1825
|
stop(): void;
|
|
1351
1826
|
override update(dt: number): void;
|
|
@@ -1402,6 +1877,16 @@ declare class PrefabShelf extends Behavior {
|
|
|
1402
1877
|
/** What this shelf holds, in authored order. */
|
|
1403
1878
|
names(): string[];
|
|
1404
1879
|
/**
|
|
1880
|
+
* The held template itself — to ASK it something, never to change it.
|
|
1881
|
+
*
|
|
1882
|
+
* `Turret.arc` needs the shell's `speed` and `gravity` to solve a firing
|
|
1883
|
+
* angle, and it needs them BEFORE it makes a clone (the barrel elevates
|
|
1884
|
+
* every frame, not only when the gun fires). Re-declaring those numbers on
|
|
1885
|
+
* the turret would be two surfaces free to disagree; reading the one that
|
|
1886
|
+
* flies is the answer. Mutating what comes back changes every future clone.
|
|
1887
|
+
*/
|
|
1888
|
+
template(name: string): Node | null;
|
|
1889
|
+
/**
|
|
1405
1890
|
* A live clone of one template, DETACHED — `addChild` it yourself.
|
|
1406
1891
|
*
|
|
1407
1892
|
* Detached for the same reason `duplicateNode` is, and it is not a
|
|
@@ -1418,38 +1903,76 @@ declare class PrefabShelf extends Behavior {
|
|
|
1418
1903
|
private childNames;
|
|
1419
1904
|
}
|
|
1420
1905
|
//#endregion
|
|
1421
|
-
//#region src/gameplay/
|
|
1906
|
+
//#region src/gameplay/sight.d.ts
|
|
1422
1907
|
/**
|
|
1423
|
-
*
|
|
1424
|
-
*
|
|
1425
|
-
* The tree composes transforms for DRAWING — three does it for free — and every
|
|
1426
|
-
* piece of engine logic that reads `node.position` gets the LOCAL number
|
|
1427
|
-
* instead. Physics already knew (`worldPosition2D`/`worldPosition3D` in the
|
|
1428
|
-
* adapters); the camera did not, and neither did anything that chases a target.
|
|
1429
|
-
*
|
|
1430
|
-
* A camera parented to the player is the Godot and Phaser idiom, and the
|
|
1431
|
-
* authoring skill explicitly permits it — *"EVERY node type can hold children …
|
|
1432
|
-
* any node works as a grouping container"*. Measured on exactly that scene:
|
|
1908
|
+
* Eyes for a watcher — a guard, a turret, a camera, a beast.
|
|
1433
1909
|
*
|
|
1910
|
+
* ```json
|
|
1911
|
+
* { "name": "Guard", "type": "CharacterBody3D", "children": [
|
|
1912
|
+
* { "name": "Skin", "type": "ModelInstance3D", "props": { "model": "$base" } },
|
|
1913
|
+
* { "name": "Eyes", "type": "Node",
|
|
1914
|
+
* "script": { "name": "Sight", "props": { "range": 12, "coneDeg": 70, "fillSeconds": 1 } } },
|
|
1915
|
+
* { "name": "Hunt", "type": "Node", "script": { "name": "Chase", "props": { "enabled": false } } }
|
|
1916
|
+
* ] }
|
|
1434
1917
|
* ```
|
|
1435
|
-
*
|
|
1436
|
-
*
|
|
1437
|
-
*
|
|
1438
|
-
* 1 in view, 0 outside it
|
|
1918
|
+
* ```jsonc
|
|
1919
|
+
* { "signal": "spotted", "from": "Guard/Eyes", "to": "Guard/Hunt", "handler": "enable" },
|
|
1920
|
+
* { "signal": "lost", "from": "Guard/Eyes", "to": "Guard/Hunt", "handler": "disable" }
|
|
1439
1921
|
* ```
|
|
1440
1922
|
*
|
|
1441
|
-
*
|
|
1442
|
-
*
|
|
1443
|
-
*
|
|
1444
|
-
*
|
|
1445
|
-
*
|
|
1446
|
-
*
|
|
1923
|
+
* On a child of the watcher's body. Every frame it looks the way the node at
|
|
1924
|
+
* `facingPath` faces (the Skin the controller turns, +Z-forward) and sees
|
|
1925
|
+
* anything in `targetGroup` inside `coneDeg` and `range` with nothing
|
|
1926
|
+
* STATIC between its eye and their chest (`los`). `suspicion` fills over
|
|
1927
|
+
* `fillSeconds` while something is seen and drains when nothing is;
|
|
1928
|
+
* `spotted(node)` fires when it reaches 1, `lost(node)` when it empties —
|
|
1929
|
+
* once each, an edge, so a connection can flip a state. `sees(node)`,
|
|
1930
|
+
* `seen()` and `target` (the nearest) answer any frame.
|
|
1931
|
+
*
|
|
1932
|
+
* Five examples wrote this by hand before it existed, and `Chase` still
|
|
1933
|
+
* chases through walls — give an enemy eyes and it stops.
|
|
1447
1934
|
*/
|
|
1935
|
+
declare class Sight extends Behavior {
|
|
1936
|
+
static readonly props: PropSchema;
|
|
1937
|
+
static readonly signals: readonly string[];
|
|
1938
|
+
range: number;
|
|
1939
|
+
coneDeg: number;
|
|
1940
|
+
targetGroup: string;
|
|
1941
|
+
facingPath: string;
|
|
1942
|
+
eyeHeight: number;
|
|
1943
|
+
chestHeight: number;
|
|
1944
|
+
fillSeconds: number;
|
|
1945
|
+
los: boolean;
|
|
1946
|
+
/** 0..1 — how sure it is; 1 is `spotted`. */
|
|
1947
|
+
suspicion: number;
|
|
1948
|
+
/** The nearest node it sees right now, or null. */
|
|
1949
|
+
target: Node | null;
|
|
1950
|
+
private seenNow;
|
|
1951
|
+
private alarmed;
|
|
1952
|
+
private lastTarget;
|
|
1953
|
+
/** Is this node in sight right now? */
|
|
1954
|
+
sees(node: Node): boolean;
|
|
1955
|
+
/** Everything in sight right now, nearest first. */
|
|
1956
|
+
seen(): Node[];
|
|
1957
|
+
override update(dt: number): void;
|
|
1958
|
+
private facing;
|
|
1959
|
+
private distanceTo;
|
|
1960
|
+
}
|
|
1961
|
+
//#endregion
|
|
1962
|
+
//#region src/core/pose.d.ts
|
|
1963
|
+
/** The node's position in WORLD space — its parents' positions and rotations composed. */
|
|
1448
1964
|
declare function worldPosition(node: Node): number[];
|
|
1449
1965
|
/**
|
|
1450
|
-
*
|
|
1451
|
-
*
|
|
1966
|
+
* A LOCAL vector turned into world space by the node's own rotation and every
|
|
1967
|
+
* ancestor's — a barrel's forward, a muzzle's offset, a hardpoint's direction.
|
|
1968
|
+
*
|
|
1969
|
+
* `worldPosition` answers where a node IS; this answers which way it points,
|
|
1970
|
+
* and two games wrote it by hand (a tank's `forwardOf`, an artillery piece's
|
|
1971
|
+
* barrel tip) because nothing else could say it. `worldDirection(node, [0,0,1])`
|
|
1972
|
+
* is the +Z-forward convention every 3D skin in this engine follows.
|
|
1452
1973
|
*/
|
|
1974
|
+
declare function worldDirection(node: Node, local: readonly number[]): number[];
|
|
1975
|
+
/** The local `position` that puts `node` at `world` — the inverse of `worldPosition`. */
|
|
1453
1976
|
declare function localFromWorld(node: Node, world: readonly number[]): number[];
|
|
1454
1977
|
//#endregion
|
|
1455
1978
|
//#region src/gameplay/turret.d.ts
|
|
@@ -1468,7 +1991,13 @@ declare function localFromWorld(node: Node, world: readonly number[]): number[];
|
|
|
1468
1991
|
* shot takes one, puts it at the turret plus `muzzle`, points its
|
|
1469
1992
|
* `Projectile` at the target and adds it under `into` (the turret's parent
|
|
1470
1993
|
* when empty). The clone does the hurting then — give it `DamageOnContact`.
|
|
1471
|
-
* - `facePath` turns a barrel toward the target at `turnSpeed
|
|
1994
|
+
* - `facePath` turns a barrel toward the target at `turnSpeed` — and the
|
|
1995
|
+
* `muzzle` offset rides THAT node, so a shell leaves the tip of the barrel
|
|
1996
|
+
* wherever it happens to be pointing.
|
|
1997
|
+
* - **`arc`** lobs: a shell with `Projectile.gravity` aimed straight at
|
|
1998
|
+
* anything far away falls out of the sky long before it arrives, so a
|
|
1999
|
+
* lobbing gun solves its launch angle (`'low'` flat and fast, `'high'` over
|
|
2000
|
+
* whatever stands in between) and ELEVATES the barrel to match.
|
|
1472
2001
|
*
|
|
1473
2002
|
* Signals: `acquired(target)` when something comes in range, `fired(target)`
|
|
1474
2003
|
* per shot, `lost()` when nothing live is in range any more.
|
|
@@ -1477,6 +2006,7 @@ declare class Turret extends Behavior {
|
|
|
1477
2006
|
static readonly props: PropSchema;
|
|
1478
2007
|
static readonly signals: readonly string[];
|
|
1479
2008
|
targetGroup: string;
|
|
2009
|
+
requireSight: boolean;
|
|
1480
2010
|
range: number;
|
|
1481
2011
|
damage: number;
|
|
1482
2012
|
cooldown: number;
|
|
@@ -1484,6 +2014,7 @@ declare class Turret extends Behavior {
|
|
|
1484
2014
|
prefab: string;
|
|
1485
2015
|
muzzle: number[];
|
|
1486
2016
|
into: string;
|
|
2017
|
+
arc: "flat" | "low" | "high";
|
|
1487
2018
|
facePath: string;
|
|
1488
2019
|
turnSpeed: number;
|
|
1489
2020
|
/** What it is shooting at right now, or null. */
|
|
@@ -1492,7 +2023,18 @@ declare class Turret extends Behavior {
|
|
|
1492
2023
|
shots: number;
|
|
1493
2024
|
private wait;
|
|
1494
2025
|
override onReady(): void;
|
|
2026
|
+
/** `requireSight`: a static-only ray from the turret to the target reaches it. */
|
|
2027
|
+
private canSee;
|
|
1495
2028
|
override update(dt: number): void;
|
|
2029
|
+
/** Where a clone actually leaves from: the turret plus its muzzle, turned by the barrel. */
|
|
2030
|
+
private muzzleAt;
|
|
2031
|
+
/**
|
|
2032
|
+
* The heading of this shot: straight at the target, or the arc that lands on
|
|
2033
|
+
* it. Solved from the MUZZLE, which is where the shell starts.
|
|
2034
|
+
*/
|
|
2035
|
+
private aimAt;
|
|
2036
|
+
/** The shell's own numbers, read off the template that will be cloned. */
|
|
2037
|
+
private shellBallistics;
|
|
1496
2038
|
/** One clone from the shelf, at the muzzle, aimed at the target, into the container. */
|
|
1497
2039
|
private launch;
|
|
1498
2040
|
}
|
|
@@ -1509,4 +2051,4 @@ declare function registerGameplayBehaviors(opts?: {
|
|
|
1509
2051
|
replace?: boolean;
|
|
1510
2052
|
}): void;
|
|
1511
2053
|
//#endregion
|
|
1512
|
-
export { CameraShake, Chase, Checkpoint, Collector, Cooldown, Currency, DamageOnContact, DayNight, type DayPhase, FaceTarget, FloatAway, FollowCamera, GAMEPLAY_BEHAVIORS, GameFlow, type GameFlowState, Health, Interactable, Lifetime, MoveTo, Oscillate, PathFollow, Patrol, Pickup, PrefabShelf, Projectile, SavePoint, ScoreKeeper, Spawner, Turret, Wander, WaveSpawner, ZombieAI, goToScene, hitStop, localFromWorld, phaseOf, registerGameplayBehaviors, restartScene, screenFlash, worldPosition };
|
|
2054
|
+
export { type BallisticPathOptions, Blast, CameraShake, Chase, ChaseCamera, Checkpoint, Collector, Cooldown, Currency, DamageOnContact, DayNight, type DayPhase, FaceTarget, FloatAway, FollowCamera, GAMEPLAY_BEHAVIORS, GameFlow, type GameFlowState, GroupCamera, Health, Interactable, Lifetime, Mount, MoveTo, Oscillate, PathFollow, Patrol, Phases, Pickup, PrefabShelf, Projectile, SavePoint, ScoreKeeper, Sight, Spawner, Turret, Wander, WaveSpawner, ZombieAI, ballisticAim, ballisticPath, goToScene, hitStop, localFromWorld, maxRangeAim, phaseOf, registerGameplayBehaviors, restartScene, screenFlash, worldDirection, worldPosition };
|