@zylem/behaviors 0.2.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/README.md +26 -0
- package/dist/behavior-system-DYbx-x_-.d.ts +307 -0
- package/dist/chunk-2C3Z5GE7.js +176 -0
- package/dist/chunk-2C3Z5GE7.js.map +1 -0
- package/dist/chunk-2CLT36VB.js +241 -0
- package/dist/chunk-2CLT36VB.js.map +1 -0
- package/dist/chunk-3PYGRHAL.js +35 -0
- package/dist/chunk-3PYGRHAL.js.map +1 -0
- package/dist/chunk-4I6E6DGV.js +212 -0
- package/dist/chunk-4I6E6DGV.js.map +1 -0
- package/dist/chunk-4URF65BR.js +44 -0
- package/dist/chunk-4URF65BR.js.map +1 -0
- package/dist/chunk-6C6NXNEW.js +38 -0
- package/dist/chunk-6C6NXNEW.js.map +1 -0
- package/dist/chunk-ARJWUNRE.js +431 -0
- package/dist/chunk-ARJWUNRE.js.map +1 -0
- package/dist/chunk-BXP4K7RF.js +1205 -0
- package/dist/chunk-BXP4K7RF.js.map +1 -0
- package/dist/chunk-DYLH6K7B.js +44 -0
- package/dist/chunk-DYLH6K7B.js.map +1 -0
- package/dist/chunk-EIWQM6U3.js +27 -0
- package/dist/chunk-EIWQM6U3.js.map +1 -0
- package/dist/chunk-EZNCCO4L.js +14 -0
- package/dist/chunk-EZNCCO4L.js.map +1 -0
- package/dist/chunk-F3KRX2V4.js +457 -0
- package/dist/chunk-F3KRX2V4.js.map +1 -0
- package/dist/chunk-FPTWVCPN.js +53 -0
- package/dist/chunk-FPTWVCPN.js.map +1 -0
- package/dist/chunk-GCNRXAKH.js +37 -0
- package/dist/chunk-GCNRXAKH.js.map +1 -0
- package/dist/chunk-GYURWCIQ.js +1985 -0
- package/dist/chunk-GYURWCIQ.js.map +1 -0
- package/dist/chunk-HZMY3TT4.js +244 -0
- package/dist/chunk-HZMY3TT4.js.map +1 -0
- package/dist/chunk-ID5QJYF5.js +50 -0
- package/dist/chunk-ID5QJYF5.js.map +1 -0
- package/dist/chunk-JAN34G6S.js +552 -0
- package/dist/chunk-JAN34G6S.js.map +1 -0
- package/dist/chunk-K3UNLQ7W.js +118 -0
- package/dist/chunk-K3UNLQ7W.js.map +1 -0
- package/dist/chunk-KDY5N6J4.js +82 -0
- package/dist/chunk-KDY5N6J4.js.map +1 -0
- package/dist/chunk-KOZUNIIE.js +413 -0
- package/dist/chunk-KOZUNIIE.js.map +1 -0
- package/dist/chunk-KS72RZHX.js +30 -0
- package/dist/chunk-KS72RZHX.js.map +1 -0
- package/dist/chunk-LD5SMIB3.js +32 -0
- package/dist/chunk-LD5SMIB3.js.map +1 -0
- package/dist/chunk-OHCPV2NT.js +86 -0
- package/dist/chunk-OHCPV2NT.js.map +1 -0
- package/dist/chunk-OYWFPIHF.js +115 -0
- package/dist/chunk-OYWFPIHF.js.map +1 -0
- package/dist/chunk-POZER3MQ.js +407 -0
- package/dist/chunk-POZER3MQ.js.map +1 -0
- package/dist/chunk-RTRVBEIB.js +56 -0
- package/dist/chunk-RTRVBEIB.js.map +1 -0
- package/dist/chunk-RTVRUNYR.js +29 -0
- package/dist/chunk-RTVRUNYR.js.map +1 -0
- package/dist/chunk-SS7ELMJ7.js +406 -0
- package/dist/chunk-SS7ELMJ7.js.map +1 -0
- package/dist/chunk-STCCNLPC.js +160 -0
- package/dist/chunk-STCCNLPC.js.map +1 -0
- package/dist/chunk-SVLNF434.js +261 -0
- package/dist/chunk-SVLNF434.js.map +1 -0
- package/dist/chunk-UYFOLI2X.js +24 -0
- package/dist/chunk-UYFOLI2X.js.map +1 -0
- package/dist/chunk-VAN4WW3F.js +67 -0
- package/dist/chunk-VAN4WW3F.js.map +1 -0
- package/dist/chunk-WOMHL23C.js +369 -0
- package/dist/chunk-WOMHL23C.js.map +1 -0
- package/dist/chunk-WWVD5DR5.js +120 -0
- package/dist/chunk-WWVD5DR5.js.map +1 -0
- package/dist/chunk-XG2YMCJW.js +201 -0
- package/dist/chunk-XG2YMCJW.js.map +1 -0
- package/dist/chunk-XIJNE67Y.js +210 -0
- package/dist/chunk-XIJNE67Y.js.map +1 -0
- package/dist/chunk-Y6UJOUE5.js +44 -0
- package/dist/chunk-Y6UJOUE5.js.map +1 -0
- package/dist/chunk-YUO6SKJD.js +348 -0
- package/dist/chunk-YUO6SKJD.js.map +1 -0
- package/dist/components--hjBMTnz.d.ts +120 -0
- package/dist/components-B29WHGQN.d.ts +39 -0
- package/dist/components-BWG1GkZA.d.ts +20 -0
- package/dist/components-XCpL6IYt.d.ts +18 -0
- package/dist/components-qWwOsvwh.d.ts +43 -0
- package/dist/cooldown.d.ts +116 -0
- package/dist/cooldown.js +20 -0
- package/dist/cooldown.js.map +1 -0
- package/dist/coordinators/boundary-ricochet-3d.d.ts +19 -0
- package/dist/coordinators/boundary-ricochet-3d.js +8 -0
- package/dist/coordinators/boundary-ricochet-3d.js.map +1 -0
- package/dist/coordinators/boundary-ricochet.d.ts +34 -0
- package/dist/coordinators/boundary-ricochet.js +8 -0
- package/dist/coordinators/boundary-ricochet.js.map +1 -0
- package/dist/coordinators/first-person-shooter.d.ts +35 -0
- package/dist/coordinators/first-person-shooter.js +9 -0
- package/dist/coordinators/first-person-shooter.js.map +1 -0
- package/dist/coordinators/multidirectional-space-shooter.d.ts +27 -0
- package/dist/coordinators/multidirectional-space-shooter.js +10 -0
- package/dist/coordinators/multidirectional-space-shooter.js.map +1 -0
- package/dist/coordinators/top-down-shooter.d.ts +27 -0
- package/dist/coordinators/top-down-shooter.js +10 -0
- package/dist/coordinators/top-down-shooter.js.map +1 -0
- package/dist/core.d.ts +25 -0
- package/dist/core.js +327 -0
- package/dist/core.js.map +1 -0
- package/dist/destructible-3d.d.ts +42 -0
- package/dist/destructible-3d.js +13 -0
- package/dist/destructible-3d.js.map +1 -0
- package/dist/destructible-prebake.d.ts +28 -0
- package/dist/destructible-prebake.js +23 -0
- package/dist/destructible-prebake.js.map +1 -0
- package/dist/entity-types-CmtQ-IBp.d.ts +15 -0
- package/dist/first-person-perspective-C9Mptl0-.d.ts +23 -0
- package/dist/first-person.d.ts +204 -0
- package/dist/first-person.js +27 -0
- package/dist/first-person.js.map +1 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +263 -0
- package/dist/index.js.map +1 -0
- package/dist/jumper-2d.d.ts +113 -0
- package/dist/jumper-2d.js +27 -0
- package/dist/jumper-2d.js.map +1 -0
- package/dist/jumper-3d.d.ts +164 -0
- package/dist/jumper-3d.js +29 -0
- package/dist/jumper-3d.js.map +1 -0
- package/dist/moveable-BCRpjAQb.d.ts +176 -0
- package/dist/particle-emitter/presets.d.ts +46 -0
- package/dist/particle-emitter/presets.js +15 -0
- package/dist/particle-emitter/presets.js.map +1 -0
- package/dist/particle-emitter.d.ts +78 -0
- package/dist/particle-emitter.js +15 -0
- package/dist/particle-emitter.js.map +1 -0
- package/dist/payload-BQzu-oY_.d.ts +89 -0
- package/dist/platformer-3d.d.ts +347 -0
- package/dist/platformer-3d.js +23 -0
- package/dist/platformer-3d.js.map +1 -0
- package/dist/ricochet-2d.d.ts +274 -0
- package/dist/ricochet-2d.js +16 -0
- package/dist/ricochet-2d.js.map +1 -0
- package/dist/ricochet-3d.d.ts +116 -0
- package/dist/ricochet-3d.js +16 -0
- package/dist/ricochet-3d.js.map +1 -0
- package/dist/runtime-2d.d.ts +2 -0
- package/dist/runtime-2d.js +32 -0
- package/dist/runtime-2d.js.map +1 -0
- package/dist/runtime-pong.d.ts +92 -0
- package/dist/runtime-pong.js +38 -0
- package/dist/runtime-pong.js.map +1 -0
- package/dist/screen-visibility.d.ts +74 -0
- package/dist/screen-visibility.js +10 -0
- package/dist/screen-visibility.js.map +1 -0
- package/dist/screen-wrap.d.ts +86 -0
- package/dist/screen-wrap.js +16 -0
- package/dist/screen-wrap.js.map +1 -0
- package/dist/shooter-2d.d.ts +77 -0
- package/dist/shooter-2d.js +12 -0
- package/dist/shooter-2d.js.map +1 -0
- package/dist/spark-DDlIg1nM.d.ts +148 -0
- package/dist/sync-state-machine-CZyspBpj.d.ts +16 -0
- package/dist/thruster.d.ts +132 -0
- package/dist/thruster.js +26 -0
- package/dist/thruster.js.map +1 -0
- package/dist/top-down-movement.d.ts +40 -0
- package/dist/top-down-movement.js +20 -0
- package/dist/top-down-movement.js.map +1 -0
- package/dist/use-behavior-D_82jAL3.d.ts +13 -0
- package/dist/vector-Bm65-FOG.d.ts +45 -0
- package/dist/world-boundary-2d.d.ts +141 -0
- package/dist/world-boundary-2d.js +21 -0
- package/dist/world-boundary-2d.js.map +1 -0
- package/dist/world-boundary-3d.d.ts +75 -0
- package/dist/world-boundary-3d.js +21 -0
- package/dist/world-boundary-3d.js.map +1 -0
- package/package.json +161 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createThrusterInputComponent,
|
|
3
|
+
createThrusterMovementComponent
|
|
4
|
+
} from "./chunk-KS72RZHX.js";
|
|
5
|
+
import {
|
|
6
|
+
getRotationAngle2D,
|
|
7
|
+
normalizeDirection2D
|
|
8
|
+
} from "./chunk-ID5QJYF5.js";
|
|
9
|
+
import {
|
|
10
|
+
SyncStateMachine,
|
|
11
|
+
t
|
|
12
|
+
} from "./chunk-4URF65BR.js";
|
|
13
|
+
import {
|
|
14
|
+
defineBehavior
|
|
15
|
+
} from "./chunk-EZNCCO4L.js";
|
|
16
|
+
|
|
17
|
+
// src/lib/behaviors/thruster/thruster-fsm.ts
|
|
18
|
+
var ThrusterState = /* @__PURE__ */ ((ThrusterState2) => {
|
|
19
|
+
ThrusterState2["Idle"] = "idle";
|
|
20
|
+
ThrusterState2["Active"] = "active";
|
|
21
|
+
ThrusterState2["Boosting"] = "boosting";
|
|
22
|
+
ThrusterState2["Disabled"] = "disabled";
|
|
23
|
+
ThrusterState2["Docked"] = "docked";
|
|
24
|
+
return ThrusterState2;
|
|
25
|
+
})(ThrusterState || {});
|
|
26
|
+
var ThrusterEvent = /* @__PURE__ */ ((ThrusterEvent2) => {
|
|
27
|
+
ThrusterEvent2["Activate"] = "activate";
|
|
28
|
+
ThrusterEvent2["Deactivate"] = "deactivate";
|
|
29
|
+
ThrusterEvent2["Boost"] = "boost";
|
|
30
|
+
ThrusterEvent2["EndBoost"] = "endBoost";
|
|
31
|
+
ThrusterEvent2["Disable"] = "disable";
|
|
32
|
+
ThrusterEvent2["Enable"] = "enable";
|
|
33
|
+
ThrusterEvent2["Dock"] = "dock";
|
|
34
|
+
ThrusterEvent2["Undock"] = "undock";
|
|
35
|
+
return ThrusterEvent2;
|
|
36
|
+
})(ThrusterEvent || {});
|
|
37
|
+
var ThrusterFSM = class {
|
|
38
|
+
constructor(ctx) {
|
|
39
|
+
this.ctx = ctx;
|
|
40
|
+
this.machine = new SyncStateMachine(
|
|
41
|
+
"idle" /* Idle */,
|
|
42
|
+
[
|
|
43
|
+
// Core transitions
|
|
44
|
+
t("idle" /* Idle */, "activate" /* Activate */, "active" /* Active */),
|
|
45
|
+
t("active" /* Active */, "deactivate" /* Deactivate */, "idle" /* Idle */),
|
|
46
|
+
t("active" /* Active */, "boost" /* Boost */, "boosting" /* Boosting */),
|
|
47
|
+
t("active" /* Active */, "disable" /* Disable */, "disabled" /* Disabled */),
|
|
48
|
+
t("active" /* Active */, "dock" /* Dock */, "docked" /* Docked */),
|
|
49
|
+
t("boosting" /* Boosting */, "endBoost" /* EndBoost */, "active" /* Active */),
|
|
50
|
+
t("boosting" /* Boosting */, "disable" /* Disable */, "disabled" /* Disabled */),
|
|
51
|
+
t("disabled" /* Disabled */, "enable" /* Enable */, "idle" /* Idle */),
|
|
52
|
+
t("docked" /* Docked */, "undock" /* Undock */, "idle" /* Idle */),
|
|
53
|
+
// Self-transitions (no-ops for redundant events)
|
|
54
|
+
t("idle" /* Idle */, "deactivate" /* Deactivate */, "idle" /* Idle */),
|
|
55
|
+
t("active" /* Active */, "activate" /* Activate */, "active" /* Active */)
|
|
56
|
+
]
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
ctx;
|
|
60
|
+
machine;
|
|
61
|
+
/**
|
|
62
|
+
* Get current state
|
|
63
|
+
*/
|
|
64
|
+
getState() {
|
|
65
|
+
return this.machine.getState();
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Dispatch an event to transition state
|
|
69
|
+
*/
|
|
70
|
+
dispatch(event) {
|
|
71
|
+
if (this.machine.can(event)) {
|
|
72
|
+
this.machine.syncDispatch(event);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Update FSM state based on player input.
|
|
77
|
+
* Auto-transitions between Idle/Active to report current state.
|
|
78
|
+
* Does NOT modify input - just observes and reports.
|
|
79
|
+
*/
|
|
80
|
+
update(playerInput) {
|
|
81
|
+
const state = this.machine.getState();
|
|
82
|
+
const hasInput = Math.abs(playerInput.thrust) > 0.01 || Math.abs(playerInput.rotate) > 0.01 || Math.abs(playerInput.thrustX ?? 0) > 0.01 || Math.abs(playerInput.thrustY ?? 0) > 0.01;
|
|
83
|
+
if (hasInput && state === "idle" /* Idle */) {
|
|
84
|
+
this.dispatch("activate" /* Activate */);
|
|
85
|
+
} else if (!hasInput && state === "active" /* Active */) {
|
|
86
|
+
this.dispatch("deactivate" /* Deactivate */);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// src/lib/behaviors/thruster/thruster-movement.behavior.ts
|
|
92
|
+
var ThrusterMovementBehavior = class {
|
|
93
|
+
constructor(world) {
|
|
94
|
+
this.world = world;
|
|
95
|
+
}
|
|
96
|
+
world;
|
|
97
|
+
/**
|
|
98
|
+
* Update a single thruster-enabled entity.
|
|
99
|
+
*/
|
|
100
|
+
updateEntity(gameEntity, _dt) {
|
|
101
|
+
if (!gameEntity.physics?.body || !gameEntity.thruster || !gameEntity.$thruster) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const e = {
|
|
105
|
+
physics: gameEntity.physics,
|
|
106
|
+
thruster: gameEntity.thruster,
|
|
107
|
+
$thruster: gameEntity.$thruster
|
|
108
|
+
};
|
|
109
|
+
const body = e.physics.body;
|
|
110
|
+
const thruster = e.thruster;
|
|
111
|
+
const input = e.$thruster;
|
|
112
|
+
if (thruster.linearDamping != null && typeof body.setLinearDamping === "function") {
|
|
113
|
+
body.setLinearDamping(thruster.linearDamping);
|
|
114
|
+
}
|
|
115
|
+
const rotationZ = getRotationAngle2D(body.rotation());
|
|
116
|
+
const currentVel = body.linvel();
|
|
117
|
+
const vectorThrust = normalizeDirection2D(input.thrustX, input.thrustY);
|
|
118
|
+
if (vectorThrust) {
|
|
119
|
+
const thrustScale = Math.min(1, Math.hypot(input.thrustX, input.thrustY));
|
|
120
|
+
const thrustAmount = thruster.linearThrust * thrustScale * 0.1;
|
|
121
|
+
body.setLinvel({
|
|
122
|
+
x: currentVel.x + vectorThrust.x * thrustAmount,
|
|
123
|
+
y: currentVel.y + vectorThrust.y * thrustAmount,
|
|
124
|
+
z: currentVel.z
|
|
125
|
+
}, true);
|
|
126
|
+
} else if (input.thrust !== 0) {
|
|
127
|
+
const forwardX = Math.sin(-rotationZ);
|
|
128
|
+
const forwardY = Math.cos(-rotationZ);
|
|
129
|
+
const thrustAmount = thruster.linearThrust * input.thrust * 0.1;
|
|
130
|
+
body.setLinvel({
|
|
131
|
+
x: currentVel.x + forwardX * thrustAmount,
|
|
132
|
+
y: currentVel.y + forwardY * thrustAmount,
|
|
133
|
+
z: currentVel.z
|
|
134
|
+
}, true);
|
|
135
|
+
}
|
|
136
|
+
if (input.rotate !== 0) {
|
|
137
|
+
body.setAngvel({ x: 0, y: 0, z: -thruster.angularThrust * input.rotate }, true);
|
|
138
|
+
} else {
|
|
139
|
+
const angVel = body.angvel();
|
|
140
|
+
body.setAngvel({ x: angVel.x, y: angVel.y, z: 0 }, true);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
update(dt) {
|
|
144
|
+
if (!this.world?.collisionMap) return;
|
|
145
|
+
for (const [, entity] of this.world.collisionMap) {
|
|
146
|
+
this.updateEntity(entity, dt);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
// src/lib/behaviors/thruster/thruster.descriptor.ts
|
|
152
|
+
var defaultOptions = {
|
|
153
|
+
linearThrust: 10,
|
|
154
|
+
angularThrust: 5,
|
|
155
|
+
linearDamping: void 0
|
|
156
|
+
};
|
|
157
|
+
var THRUSTER_BEHAVIOR_KEY = /* @__PURE__ */ Symbol.for("zylem:behavior:thruster");
|
|
158
|
+
var ThrusterBehaviorSystem = class {
|
|
159
|
+
constructor(world, wasmStage, getBehaviorLinks) {
|
|
160
|
+
this.world = world;
|
|
161
|
+
this.wasmStage = wasmStage;
|
|
162
|
+
this.getBehaviorLinks = getBehaviorLinks;
|
|
163
|
+
this.movementBehavior = new ThrusterMovementBehavior(world);
|
|
164
|
+
}
|
|
165
|
+
world;
|
|
166
|
+
wasmStage;
|
|
167
|
+
getBehaviorLinks;
|
|
168
|
+
movementBehavior;
|
|
169
|
+
attachedRuntimeSlots = /* @__PURE__ */ new Set();
|
|
170
|
+
update(_ecs, delta) {
|
|
171
|
+
const links = this.getBehaviorLinks?.(THRUSTER_BEHAVIOR_KEY);
|
|
172
|
+
if (!links) return;
|
|
173
|
+
for (const link of links) {
|
|
174
|
+
const gameEntity = link.entity;
|
|
175
|
+
const thrusterRef = link.ref;
|
|
176
|
+
const options = thrusterRef.options;
|
|
177
|
+
if (!gameEntity.thruster) {
|
|
178
|
+
gameEntity.thruster = createThrusterMovementComponent(
|
|
179
|
+
options.linearThrust,
|
|
180
|
+
options.angularThrust,
|
|
181
|
+
{ linearDamping: options.linearDamping }
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
if (!gameEntity.$thruster) {
|
|
185
|
+
gameEntity.$thruster = createThrusterInputComponent();
|
|
186
|
+
}
|
|
187
|
+
if (!thrusterRef.fsm && gameEntity.$thruster) {
|
|
188
|
+
thrusterRef.fsm = new ThrusterFSM({ input: gameEntity.$thruster });
|
|
189
|
+
}
|
|
190
|
+
if (thrusterRef.fsm && gameEntity.$thruster) {
|
|
191
|
+
thrusterRef.fsm.update({
|
|
192
|
+
thrust: gameEntity.$thruster.thrust,
|
|
193
|
+
rotate: gameEntity.$thruster.rotate,
|
|
194
|
+
thrustX: gameEntity.$thruster.thrustX,
|
|
195
|
+
thrustY: gameEntity.$thruster.thrustY
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
const handle = gameEntity.runtimeHandle ?? -1;
|
|
199
|
+
if (this.wasmStage && handle >= 0) {
|
|
200
|
+
this.ensureWasmAttached(handle, options);
|
|
201
|
+
const input = gameEntity.$thruster;
|
|
202
|
+
if (input) {
|
|
203
|
+
this.wasmStage.setThrusterInput(handle, input.thrust, input.rotate);
|
|
204
|
+
}
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (!gameEntity.body) continue;
|
|
208
|
+
if (!gameEntity.physics) {
|
|
209
|
+
gameEntity.physics = { body: gameEntity.body };
|
|
210
|
+
}
|
|
211
|
+
this.movementBehavior.updateEntity(gameEntity, delta);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
ensureWasmAttached(handle, options) {
|
|
215
|
+
if (!this.wasmStage || this.attachedRuntimeSlots.has(handle)) return;
|
|
216
|
+
this.wasmStage.attachThruster(handle, {
|
|
217
|
+
maxSpeed: 50,
|
|
218
|
+
acceleration: options.linearThrust,
|
|
219
|
+
turnRateRadPerSec: options.angularThrust,
|
|
220
|
+
linearDamping: options.linearDamping ?? 0
|
|
221
|
+
});
|
|
222
|
+
this.attachedRuntimeSlots.add(handle);
|
|
223
|
+
}
|
|
224
|
+
destroy(_ecs) {
|
|
225
|
+
this.attachedRuntimeSlots.clear();
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
var ThrusterBehavior = defineBehavior({
|
|
229
|
+
name: "thruster",
|
|
230
|
+
defaultOptions,
|
|
231
|
+
systemFactory: (ctx) => new ThrusterBehaviorSystem(ctx.world, ctx.wasmStage ?? null, ctx.getBehaviorLinks)
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
export {
|
|
235
|
+
ThrusterState,
|
|
236
|
+
ThrusterEvent,
|
|
237
|
+
ThrusterFSM,
|
|
238
|
+
ThrusterMovementBehavior,
|
|
239
|
+
ThrusterBehavior
|
|
240
|
+
};
|
|
241
|
+
//# sourceMappingURL=chunk-2CLT36VB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/behaviors/thruster/thruster-fsm.ts","../src/lib/behaviors/thruster/thruster-movement.behavior.ts","../src/lib/behaviors/thruster/thruster.descriptor.ts"],"sourcesContent":["/**\n * ThrusterFSM\n * \n * State machine controller for thruster behavior.\n * FSM does NOT touch physics or ThrusterMovementBehavior - it only writes ThrusterInputComponent.\n */\n\nimport { SyncStateMachine, t } from '../../core/utility/sync-state-machine';\nimport type { ThrusterInputComponent } from './components';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// FSM State Model\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport enum ThrusterState {\n\tIdle = 'idle',\n\tActive = 'active',\n\tBoosting = 'boosting',\n\tDisabled = 'disabled',\n\tDocked = 'docked',\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// FSM Events\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport enum ThrusterEvent {\n\tActivate = 'activate',\n\tDeactivate = 'deactivate',\n\tBoost = 'boost',\n\tEndBoost = 'endBoost',\n\tDisable = 'disable',\n\tEnable = 'enable',\n\tDock = 'dock',\n\tUndock = 'undock',\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// FSM Context Object\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ThrusterFSMContext {\n\tinput: ThrusterInputComponent;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Player Input (raw input from controller/keyboard)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface PlayerInput {\n\tthrust: number;\n\trotate: number;\n\tthrustX?: number;\n\tthrustY?: number;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ThrusterFSM Controller\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class ThrusterFSM {\n\tmachine: SyncStateMachine<ThrusterState, ThrusterEvent, never>;\n\n\tconstructor(private ctx: ThrusterFSMContext) {\n\t\tthis.machine = new SyncStateMachine<ThrusterState, ThrusterEvent, never>(\n\t\t\tThrusterState.Idle,\n\t\t\t[\n\t\t\t\t// Core transitions\n\t\t\t\tt(ThrusterState.Idle, ThrusterEvent.Activate, ThrusterState.Active),\n\t\t\t\tt(ThrusterState.Active, ThrusterEvent.Deactivate, ThrusterState.Idle),\n\t\t\t\tt(ThrusterState.Active, ThrusterEvent.Boost, ThrusterState.Boosting),\n\t\t\t\tt(ThrusterState.Active, ThrusterEvent.Disable, ThrusterState.Disabled),\n\t\t\t\tt(ThrusterState.Active, ThrusterEvent.Dock, ThrusterState.Docked),\n\t\t\t\tt(ThrusterState.Boosting, ThrusterEvent.EndBoost, ThrusterState.Active),\n\t\t\t\tt(ThrusterState.Boosting, ThrusterEvent.Disable, ThrusterState.Disabled),\n\t\t\t\tt(ThrusterState.Disabled, ThrusterEvent.Enable, ThrusterState.Idle),\n\t\t\t\tt(ThrusterState.Docked, ThrusterEvent.Undock, ThrusterState.Idle),\n\t\t\t\t// Self-transitions (no-ops for redundant events)\n\t\t\t\tt(ThrusterState.Idle, ThrusterEvent.Deactivate, ThrusterState.Idle),\n\t\t\t\tt(ThrusterState.Active, ThrusterEvent.Activate, ThrusterState.Active),\n\t\t\t]\n\t\t);\n\t}\n\n\t/**\n\t * Get current state\n\t */\n\tgetState(): ThrusterState {\n\t\treturn this.machine.getState();\n\t}\n\n\t/**\n\t * Dispatch an event to transition state\n\t */\n\tdispatch(event: ThrusterEvent): void {\n\t\tif (this.machine.can(event)) {\n\t\t\tthis.machine.syncDispatch(event);\n\t\t}\n\t}\n\n\t/**\n\t * Update FSM state based on player input.\n\t * Auto-transitions between Idle/Active to report current state.\n\t * Does NOT modify input - just observes and reports.\n\t */\n\tupdate(playerInput: PlayerInput): void {\n\t\tconst state = this.machine.getState();\n\t\tconst hasInput =\n\t\t\tMath.abs(playerInput.thrust) > 0.01\n\t\t\t|| Math.abs(playerInput.rotate) > 0.01\n\t\t\t|| Math.abs(playerInput.thrustX ?? 0) > 0.01\n\t\t\t|| Math.abs(playerInput.thrustY ?? 0) > 0.01;\n\n\t\t// Auto-transition to report state based on input\n\t\tif (hasInput && state === ThrusterState.Idle) {\n\t\t\tthis.dispatch(ThrusterEvent.Activate);\n\t\t} else if (!hasInput && state === ThrusterState.Active) {\n\t\t\tthis.dispatch(ThrusterEvent.Deactivate);\n\t\t}\n\t}\n}\n","/**\n * ThrusterMovementBehavior\n * \n * This is the heart of the thruster movement system - a pure, stateless force generator.\n * Works identically for player, AI, and replay.\n */\n\nimport type { ZylemWorld } from '../../collision/world';\nimport type { PhysicsBodyComponent } from '../components';\nimport type { ThrusterMovementComponent, ThrusterInputComponent } from './components';\nimport {\n\tgetRotationAngle2D,\n\tnormalizeDirection2D,\n} from '../shared/direction-2d';\n\n/**\n * Zylem-style Behavior interface\n */\nexport interface Behavior {\n\tupdate(dt: number): void;\n}\n\n/**\n * Entity with thruster components\n */\nexport interface ThrusterEntity {\n\tphysics: PhysicsBodyComponent;\n\tthruster: ThrusterMovementComponent;\n\t$thruster: ThrusterInputComponent;\n}\n\n/**\n * ThrusterMovementBehavior - Force generator for thruster-equipped entities\n * \n * Responsibilities:\n * - Query entities with PhysicsBody, ThrusterMovement, and ThrusterInput components\n * - Apply velocities based on thrust input (2D mode)\n * - Apply angular velocity based on rotation input\n */\nexport class ThrusterMovementBehavior implements Behavior {\n\tconstructor(private world: ZylemWorld) {}\n\n\t/**\n\t * Update a single thruster-enabled entity.\n\t */\n\tupdateEntity(gameEntity: any, _dt: number): void {\n\t\tif (!gameEntity.physics?.body || !gameEntity.thruster || !gameEntity.$thruster) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst e: ThrusterEntity = {\n\t\t\tphysics: gameEntity.physics,\n\t\t\tthruster: gameEntity.thruster,\n\t\t\t$thruster: gameEntity.$thruster,\n\t\t};\n\n\t\tconst body = e.physics.body;\n\t\tconst thruster = e.thruster;\n\t\tconst input = e.$thruster;\n\n\t\tif (thruster.linearDamping != null && typeof body.setLinearDamping === 'function') {\n\t\t\tbody.setLinearDamping(thruster.linearDamping);\n\t\t}\n\n\t\t// Get Z rotation from quaternion (for 2D sprites)\n\t\tconst rotationZ = getRotationAngle2D(body.rotation());\n\t\tconst currentVel = body.linvel();\n\t\tconst vectorThrust = normalizeDirection2D(input.thrustX, input.thrustY);\n\n\t\t// --- Linear thrust ---\n\t\tif (vectorThrust) {\n\t\t\tconst thrustScale = Math.min(1, Math.hypot(input.thrustX, input.thrustY));\n\t\t\tconst thrustAmount = thruster.linearThrust * thrustScale * 0.1;\n\t\t\tbody.setLinvel({\n\t\t\t\tx: currentVel.x + vectorThrust.x * thrustAmount,\n\t\t\t\ty: currentVel.y + vectorThrust.y * thrustAmount,\n\t\t\t\tz: currentVel.z,\n\t\t\t}, true);\n\t\t} else if (input.thrust !== 0) {\n\t\t\tconst forwardX = Math.sin(-rotationZ);\n\t\t\tconst forwardY = Math.cos(-rotationZ);\n\t\t\tconst thrustAmount = thruster.linearThrust * input.thrust * 0.1;\n\t\t\tbody.setLinvel({\n\t\t\t\tx: currentVel.x + forwardX * thrustAmount,\n\t\t\t\ty: currentVel.y + forwardY * thrustAmount,\n\t\t\t\tz: currentVel.z\n\t\t\t}, true);\n\t\t}\n\n\t\t// --- Angular thrust (Z-axis for 2D) ---\n\t\tif (input.rotate !== 0) {\n\t\t\tbody.setAngvel({ x: 0, y: 0, z: -thruster.angularThrust * input.rotate }, true);\n\t\t} else {\n\t\t\t// Stop rotation when no input\n\t\t\tconst angVel = body.angvel();\n\t\t\tbody.setAngvel({ x: angVel.x, y: angVel.y, z: 0 }, true);\n\t\t}\n\t}\n\n\tupdate(dt: number): void {\n\t\tif (!this.world?.collisionMap) return;\n\t\tfor (const [, entity] of this.world.collisionMap) {\n\t\t\tthis.updateEntity(entity, dt);\n\t\t}\n\t}\n}\n","/**\n * Thruster Behavior Descriptor\n * \n * Type-safe descriptor for the thruster behavior system using the new entity.use() API.\n * This wraps the existing ThrusterMovementBehavior and components.\n */\n\nimport { defineBehavior } from '../behavior-descriptor';\nimport type { BehaviorEntityLink, BehaviorSystem } from '../behavior-system';\nimport { ThrusterMovementBehavior, ThrusterEntity } from './thruster-movement.behavior';\nimport { ThrusterFSM } from './thruster-fsm';\nimport {\n\tcreateThrusterInputComponent,\n\tcreateThrusterMovementComponent,\n\ttype ThrusterMovementComponent,\n\ttype ThrusterInputComponent,\n} from './components';\nimport type { WasmStageRuntime } from '../../runtime/wasm-stage-runtime';\n\n/**\n * Thruster behavior options (typed for entity.use() autocomplete)\n */\nexport interface ThrusterBehaviorOptions {\n\t/** Forward thrust force (default: 10) */\n\tlinearThrust: number;\n\t/** Rotation torque (default: 5) */\n\tangularThrust: number;\n\t/** Optional linear damping override */\n\tlinearDamping?: number;\n}\n\nconst defaultOptions: ThrusterBehaviorOptions = {\n\tlinearThrust: 10,\n\tangularThrust: 5,\n\tlinearDamping: undefined,\n};\n\nconst THRUSTER_BEHAVIOR_KEY = Symbol.for('zylem:behavior:thruster');\n\n/**\n * Adapter that wraps ThrusterMovementBehavior as a BehaviorSystem.\n * \n * This bridges the entity.use() pattern with the ECS component-based approach:\n * - Reads options from entity behaviorRefs\n * - Initializes ThrusterMovementComponent and ThrusterInputComponent on entities\n * - Creates FSM lazily and attaches to BehaviorRef for access via handle.getFSM()\n * - Delegates physics to ThrusterMovementBehavior\n * \n * NOTE: Input is controlled by the user via entity.onUpdate() - set entity.input.thrust/rotate\n */\nclass ThrusterBehaviorSystem implements BehaviorSystem {\n\tprivate movementBehavior: ThrusterMovementBehavior;\n\tprivate attachedRuntimeSlots = new Set<number>();\n\n\tconstructor(\n\t\tprivate world: any,\n\t\tprivate wasmStage: WasmStageRuntime | null,\n\t\tprivate getBehaviorLinks?: (key: symbol) => Iterable<BehaviorEntityLink>,\n\t) {\n\t\tthis.movementBehavior = new ThrusterMovementBehavior(world);\n\t}\n\n\tupdate(_ecs: unknown, delta: number): void {\n\t\tconst links = this.getBehaviorLinks?.(THRUSTER_BEHAVIOR_KEY);\n\t\tif (!links) return;\n\n\t\tfor (const link of links) {\n\t\t\tconst gameEntity = link.entity as any;\n\t\t\tconst thrusterRef = link.ref as any;\n\n\t\t\tconst options = thrusterRef.options as ThrusterBehaviorOptions;\n\n\t\t\tif (!gameEntity.thruster) {\n\t\t\t\tgameEntity.thruster = createThrusterMovementComponent(\n\t\t\t\t\toptions.linearThrust,\n\t\t\t\t\toptions.angularThrust,\n\t\t\t\t\t{ linearDamping: options.linearDamping },\n\t\t\t\t) as ThrusterMovementComponent;\n\t\t\t}\n\n\t\t\tif (!gameEntity.$thruster) {\n\t\t\t\tgameEntity.$thruster = createThrusterInputComponent() as ThrusterInputComponent;\n\t\t\t}\n\n\t\t\tif (!thrusterRef.fsm && gameEntity.$thruster) {\n\t\t\t\tthrusterRef.fsm = new ThrusterFSM({ input: gameEntity.$thruster });\n\t\t\t}\n\n\t\t\tif (thrusterRef.fsm && gameEntity.$thruster) {\n\t\t\t\tthrusterRef.fsm.update({\n\t\t\t\t\tthrust: gameEntity.$thruster.thrust,\n\t\t\t\t\trotate: gameEntity.$thruster.rotate,\n\t\t\t\t\tthrustX: gameEntity.$thruster.thrustX,\n\t\t\t\t\tthrustY: gameEntity.$thruster.thrustY,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst handle = (gameEntity.runtimeHandle ?? -1) as number;\n\t\t\tif (this.wasmStage && handle >= 0) {\n\t\t\t\tthis.ensureWasmAttached(handle, options);\n\t\t\t\tconst input = gameEntity.$thruster as ThrusterInputComponent | undefined;\n\t\t\t\tif (input) {\n\t\t\t\t\tthis.wasmStage.setThrusterInput(handle, input.thrust, input.rotate);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!gameEntity.body) continue;\n\t\t\tif (!gameEntity.physics) {\n\t\t\t\tgameEntity.physics = { body: gameEntity.body };\n\t\t\t}\n\t\t\tthis.movementBehavior.updateEntity(gameEntity, delta);\n\t\t}\n\t}\n\n\tprivate ensureWasmAttached(handle: number, options: ThrusterBehaviorOptions): void {\n\t\tif (!this.wasmStage || this.attachedRuntimeSlots.has(handle)) return;\n\t\tthis.wasmStage.attachThruster(handle, {\n\t\t\tmaxSpeed: 50,\n\t\t\tacceleration: options.linearThrust,\n\t\t\tturnRateRadPerSec: options.angularThrust,\n\t\t\tlinearDamping: options.linearDamping ?? 0,\n\t\t});\n\t\tthis.attachedRuntimeSlots.add(handle);\n\t}\n\n\tdestroy(_ecs: unknown): void {\n\t\tthis.attachedRuntimeSlots.clear();\n\t}\n}\n\n/**\n * ThrusterBehavior - typed descriptor for thruster movement.\n * \n * Uses the existing ThrusterMovementBehavior under the hood.\n * \n * @example\n * ```typescript\n * import { ThrusterBehavior } from \"@zylem/game-lib/behavior\";\n * \n * const ship = createSprite({ ... });\n * ship.use(ThrusterBehavior, { linearThrust: 15, angularThrust: 8 });\n * ```\n */\nexport const ThrusterBehavior = defineBehavior<ThrusterBehaviorOptions, Record<string, never>, ThrusterEntity>({\n\tname: 'thruster',\n\tdefaultOptions,\n\tsystemFactory: (ctx) =>\n\t\tnew ThrusterBehaviorSystem(ctx.world, ctx.wasmStage ?? null, ctx.getBehaviorLinks),\n});\n"],"mappings":"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","names":["ThrusterState","ThrusterEvent"]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getBounds3DNormalFromHits
|
|
3
|
+
} from "./chunk-OHCPV2NT.js";
|
|
4
|
+
|
|
5
|
+
// src/lib/coordinators/boundary-ricochet-3d.coordinator.ts
|
|
6
|
+
var BoundaryRicochet3DCoordinator = class {
|
|
7
|
+
constructor(entity, boundary, ricochet) {
|
|
8
|
+
this.entity = entity;
|
|
9
|
+
this.boundary = boundary;
|
|
10
|
+
this.ricochet = ricochet;
|
|
11
|
+
}
|
|
12
|
+
entity;
|
|
13
|
+
boundary;
|
|
14
|
+
ricochet;
|
|
15
|
+
update() {
|
|
16
|
+
const hits = this.boundary.getLastHits();
|
|
17
|
+
if (!hits) return false;
|
|
18
|
+
const anyHit = hits.left || hits.right || hits.top || hits.bottom || hits.back || hits.front;
|
|
19
|
+
if (!anyHit) return false;
|
|
20
|
+
const clamped = this.boundary.getLastClampedPosition();
|
|
21
|
+
if (clamped && this.entity.body) {
|
|
22
|
+
this.entity.body.setTranslation(clamped, true);
|
|
23
|
+
}
|
|
24
|
+
const normal = getBounds3DNormalFromHits(hits);
|
|
25
|
+
return this.ricochet.applyRicochet({
|
|
26
|
+
entity: this.entity,
|
|
27
|
+
contact: { normal }
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export {
|
|
33
|
+
BoundaryRicochet3DCoordinator
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=chunk-3PYGRHAL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/coordinators/boundary-ricochet-3d.coordinator.ts"],"sourcesContent":["import { GameEntity } from '../entities/entity';\nimport type { MoveableEntity } from '../actions/capabilities/moveable';\nimport type { Ricochet3DHandle } from '../behaviors/ricochet-3d/ricochet-3d.descriptor';\nimport type { WorldBoundary3DHandle } from '../behaviors/world-boundary-3d/world-boundary-3d.descriptor';\nimport { getBounds3DNormalFromHits } from '../behaviors/shared/bounds-3d';\n\nexport class BoundaryRicochet3DCoordinator {\n\tconstructor(\n\t\tprivate entity: GameEntity<any> & MoveableEntity,\n\t\tprivate boundary: WorldBoundary3DHandle,\n\t\tprivate ricochet: Ricochet3DHandle,\n\t) {}\n\n\tpublic update(): boolean {\n\t\tconst hits = this.boundary.getLastHits();\n\t\tif (!hits) return false;\n\n\t\tconst anyHit =\n\t\t\thits.left || hits.right || hits.top || hits.bottom || hits.back || hits.front;\n\t\tif (!anyHit) return false;\n\n\t\tconst clamped = this.boundary.getLastClampedPosition();\n\t\tif (clamped && this.entity.body) {\n\t\t\tthis.entity.body.setTranslation(clamped, true);\n\t\t}\n\n\t\tconst normal = getBounds3DNormalFromHits(hits);\n\t\treturn this.ricochet.applyRicochet({\n\t\t\tentity: this.entity,\n\t\t\tcontact: { normal },\n\t\t});\n\t}\n}\n"],"mappings":";;;;;AAMO,IAAM,gCAAN,MAAoC;AAAA,EAC1C,YACS,QACA,UACA,UACP;AAHO;AACA;AACA;AAAA,EACN;AAAA,EAHM;AAAA,EACA;AAAA,EACA;AAAA,EAGF,SAAkB;AACxB,UAAM,OAAO,KAAK,SAAS,YAAY;AACvC,QAAI,CAAC,KAAM,QAAO;AAElB,UAAM,SACL,KAAK,QAAQ,KAAK,SAAS,KAAK,OAAO,KAAK,UAAU,KAAK,QAAQ,KAAK;AACzE,QAAI,CAAC,OAAQ,QAAO;AAEpB,UAAM,UAAU,KAAK,SAAS,uBAAuB;AACrD,QAAI,WAAW,KAAK,OAAO,MAAM;AAChC,WAAK,OAAO,KAAK,eAAe,SAAS,IAAI;AAAA,IAC9C;AAEA,UAAM,SAAS,0BAA0B,IAAI;AAC7C,WAAO,KAAK,SAAS,cAAc;AAAA,MAClC,QAAQ,KAAK;AAAA,MACb,SAAS,EAAE,OAAO;AAAA,IACnB,CAAC;AAAA,EACF;AACD;","names":[]}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import {
|
|
2
|
+
clampPointToBounds3D,
|
|
3
|
+
computeBounds3DHits,
|
|
4
|
+
constrainMovementToBounds3D,
|
|
5
|
+
hasAnyBounds3DHit,
|
|
6
|
+
insetBounds3D
|
|
7
|
+
} from "./chunk-OHCPV2NT.js";
|
|
8
|
+
import {
|
|
9
|
+
StageBoundaryDim
|
|
10
|
+
} from "./chunk-Y6UJOUE5.js";
|
|
11
|
+
import {
|
|
12
|
+
SyncStateMachine,
|
|
13
|
+
t
|
|
14
|
+
} from "./chunk-4URF65BR.js";
|
|
15
|
+
import {
|
|
16
|
+
defineBehavior
|
|
17
|
+
} from "./chunk-EZNCCO4L.js";
|
|
18
|
+
|
|
19
|
+
// src/lib/behaviors/world-boundary-3d/world-boundary-3d-fsm.ts
|
|
20
|
+
var WorldBoundary3DState = /* @__PURE__ */ ((WorldBoundary3DState2) => {
|
|
21
|
+
WorldBoundary3DState2["Inside"] = "inside";
|
|
22
|
+
WorldBoundary3DState2["Touching"] = "touching";
|
|
23
|
+
return WorldBoundary3DState2;
|
|
24
|
+
})(WorldBoundary3DState || {});
|
|
25
|
+
var WorldBoundary3DEvent = /* @__PURE__ */ ((WorldBoundary3DEvent2) => {
|
|
26
|
+
WorldBoundary3DEvent2["EnterInside"] = "enter-inside";
|
|
27
|
+
WorldBoundary3DEvent2["TouchBoundary"] = "touch-boundary";
|
|
28
|
+
return WorldBoundary3DEvent2;
|
|
29
|
+
})(WorldBoundary3DEvent || {});
|
|
30
|
+
function computeWorldBoundary3DHits(position, bounds, padding) {
|
|
31
|
+
return computeBounds3DHits(position, insetBounds3D({
|
|
32
|
+
minX: bounds.left,
|
|
33
|
+
maxX: bounds.right,
|
|
34
|
+
minY: bounds.bottom,
|
|
35
|
+
maxY: bounds.top,
|
|
36
|
+
minZ: bounds.back,
|
|
37
|
+
maxZ: bounds.front
|
|
38
|
+
}, padding));
|
|
39
|
+
}
|
|
40
|
+
function hasAnyWorldBoundary3DHit(hits) {
|
|
41
|
+
return hasAnyBounds3DHit(hits);
|
|
42
|
+
}
|
|
43
|
+
var WorldBoundary3DFSM = class {
|
|
44
|
+
machine;
|
|
45
|
+
lastHits = {
|
|
46
|
+
top: false,
|
|
47
|
+
bottom: false,
|
|
48
|
+
left: false,
|
|
49
|
+
right: false,
|
|
50
|
+
back: false,
|
|
51
|
+
front: false
|
|
52
|
+
};
|
|
53
|
+
lastPosition = null;
|
|
54
|
+
lastClampedPosition = null;
|
|
55
|
+
lastUpdatedAtMs = null;
|
|
56
|
+
constructor() {
|
|
57
|
+
this.machine = new SyncStateMachine(
|
|
58
|
+
"inside" /* Inside */,
|
|
59
|
+
[
|
|
60
|
+
t("inside" /* Inside */, "touch-boundary" /* TouchBoundary */, "touching" /* Touching */),
|
|
61
|
+
t("touching" /* Touching */, "enter-inside" /* EnterInside */, "inside" /* Inside */),
|
|
62
|
+
t("inside" /* Inside */, "enter-inside" /* EnterInside */, "inside" /* Inside */),
|
|
63
|
+
t("touching" /* Touching */, "touch-boundary" /* TouchBoundary */, "touching" /* Touching */)
|
|
64
|
+
]
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
getState() {
|
|
68
|
+
return this.machine.getState();
|
|
69
|
+
}
|
|
70
|
+
getLastHits() {
|
|
71
|
+
return this.lastHits;
|
|
72
|
+
}
|
|
73
|
+
getMovement(moveX, moveY, moveZ) {
|
|
74
|
+
return constrainMovementToBounds3D(this.lastHits, moveX, moveY, moveZ);
|
|
75
|
+
}
|
|
76
|
+
getLastPosition() {
|
|
77
|
+
return this.lastPosition;
|
|
78
|
+
}
|
|
79
|
+
getLastClampedPosition() {
|
|
80
|
+
return this.lastClampedPosition;
|
|
81
|
+
}
|
|
82
|
+
getLastUpdatedAtMs() {
|
|
83
|
+
return this.lastUpdatedAtMs;
|
|
84
|
+
}
|
|
85
|
+
update(position, bounds, padding) {
|
|
86
|
+
const effectiveBounds = insetBounds3D({
|
|
87
|
+
minX: bounds.left,
|
|
88
|
+
maxX: bounds.right,
|
|
89
|
+
minY: bounds.bottom,
|
|
90
|
+
maxY: bounds.top,
|
|
91
|
+
minZ: bounds.back,
|
|
92
|
+
maxZ: bounds.front
|
|
93
|
+
}, padding);
|
|
94
|
+
const hits = computeBounds3DHits(position, effectiveBounds);
|
|
95
|
+
this.lastHits = hits;
|
|
96
|
+
this.lastPosition = { x: position.x, y: position.y, z: position.z };
|
|
97
|
+
this.lastClampedPosition = clampPointToBounds3D(position, effectiveBounds);
|
|
98
|
+
this.lastUpdatedAtMs = Date.now();
|
|
99
|
+
if (hasAnyWorldBoundary3DHit(hits)) {
|
|
100
|
+
this.dispatch("touch-boundary" /* TouchBoundary */);
|
|
101
|
+
} else {
|
|
102
|
+
this.dispatch("enter-inside" /* EnterInside */);
|
|
103
|
+
}
|
|
104
|
+
return hits;
|
|
105
|
+
}
|
|
106
|
+
dispatch(event) {
|
|
107
|
+
if (this.machine.can(event)) {
|
|
108
|
+
this.machine.syncDispatch(event);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// src/lib/behaviors/world-boundary-3d/world-boundary-3d.descriptor.ts
|
|
114
|
+
var defaultOptions = {
|
|
115
|
+
boundaries: { top: 0, bottom: 0, left: 0, right: 0, back: 0, front: 0 },
|
|
116
|
+
padding: 0
|
|
117
|
+
};
|
|
118
|
+
var WORLD_BOUNDARY_3D_BEHAVIOR_KEY = /* @__PURE__ */ Symbol.for(
|
|
119
|
+
"zylem:behavior:world-boundary-3d"
|
|
120
|
+
);
|
|
121
|
+
function createWorldBoundary3DHandle(ref) {
|
|
122
|
+
return {
|
|
123
|
+
getLastHits: () => {
|
|
124
|
+
const fsm = ref.fsm;
|
|
125
|
+
return fsm?.getLastHits() ?? null;
|
|
126
|
+
},
|
|
127
|
+
getMovement: (moveX, moveY, moveZ) => {
|
|
128
|
+
const fsm = ref.fsm;
|
|
129
|
+
return fsm?.getMovement(moveX, moveY, moveZ) ?? { moveX, moveY, moveZ };
|
|
130
|
+
},
|
|
131
|
+
getLastClampedPosition: () => {
|
|
132
|
+
const fsm = ref.fsm;
|
|
133
|
+
return fsm?.getLastClampedPosition() ?? null;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
var WorldBoundary3DSystem = class {
|
|
138
|
+
constructor(world, wasmStage, getBehaviorLinks) {
|
|
139
|
+
this.world = world;
|
|
140
|
+
this.wasmStage = wasmStage;
|
|
141
|
+
this.getBehaviorLinks = getBehaviorLinks;
|
|
142
|
+
}
|
|
143
|
+
world;
|
|
144
|
+
wasmStage;
|
|
145
|
+
getBehaviorLinks;
|
|
146
|
+
attachedRuntimeSlots = /* @__PURE__ */ new Set();
|
|
147
|
+
update(_ecs, _delta) {
|
|
148
|
+
const links = this.getBehaviorLinks?.(WORLD_BOUNDARY_3D_BEHAVIOR_KEY);
|
|
149
|
+
if (!links) return;
|
|
150
|
+
for (const link of links) {
|
|
151
|
+
const gameEntity = link.entity;
|
|
152
|
+
const boundaryRef = link.ref;
|
|
153
|
+
const options = boundaryRef.options;
|
|
154
|
+
if (!boundaryRef.fsm) {
|
|
155
|
+
boundaryRef.fsm = new WorldBoundary3DFSM();
|
|
156
|
+
}
|
|
157
|
+
const handle = gameEntity.runtimeHandle ?? -1;
|
|
158
|
+
if (this.wasmStage && handle >= 0) {
|
|
159
|
+
this.ensureWasmAttached(handle, options);
|
|
160
|
+
const query = this.wasmStage.queryWorldBoundary(handle);
|
|
161
|
+
if (query) {
|
|
162
|
+
boundaryRef.fsm.update(
|
|
163
|
+
{ x: query.clamped[0], y: query.clamped[1], z: query.clamped[2] },
|
|
164
|
+
options.boundaries,
|
|
165
|
+
options.padding
|
|
166
|
+
);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (!gameEntity.body) continue;
|
|
171
|
+
const pos = gameEntity.body.translation();
|
|
172
|
+
boundaryRef.fsm.update(
|
|
173
|
+
{ x: pos.x, y: pos.y, z: pos.z },
|
|
174
|
+
options.boundaries,
|
|
175
|
+
options.padding
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
ensureWasmAttached(handle, options) {
|
|
180
|
+
if (!this.wasmStage || this.attachedRuntimeSlots.has(handle)) return;
|
|
181
|
+
const padding = typeof options.padding === "number" ? options.padding : 0;
|
|
182
|
+
this.wasmStage.attachWorldBoundary(handle, StageBoundaryDim.Three, {
|
|
183
|
+
top: options.boundaries.top,
|
|
184
|
+
bottom: options.boundaries.bottom,
|
|
185
|
+
left: options.boundaries.left,
|
|
186
|
+
right: options.boundaries.right,
|
|
187
|
+
front: options.boundaries.front,
|
|
188
|
+
back: options.boundaries.back,
|
|
189
|
+
padding
|
|
190
|
+
});
|
|
191
|
+
this.attachedRuntimeSlots.add(handle);
|
|
192
|
+
}
|
|
193
|
+
destroy(_ecs) {
|
|
194
|
+
this.attachedRuntimeSlots.clear();
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
var WorldBoundary3DBehavior = defineBehavior({
|
|
198
|
+
name: "world-boundary-3d",
|
|
199
|
+
defaultOptions,
|
|
200
|
+
systemFactory: (ctx) => new WorldBoundary3DSystem(ctx.world, ctx.wasmStage ?? null, ctx.getBehaviorLinks),
|
|
201
|
+
createHandle: createWorldBoundary3DHandle
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
export {
|
|
205
|
+
WorldBoundary3DState,
|
|
206
|
+
WorldBoundary3DEvent,
|
|
207
|
+
computeWorldBoundary3DHits,
|
|
208
|
+
hasAnyWorldBoundary3DHit,
|
|
209
|
+
WorldBoundary3DFSM,
|
|
210
|
+
WorldBoundary3DBehavior
|
|
211
|
+
};
|
|
212
|
+
//# sourceMappingURL=chunk-4I6E6DGV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/behaviors/world-boundary-3d/world-boundary-3d-fsm.ts","../src/lib/behaviors/world-boundary-3d/world-boundary-3d.descriptor.ts"],"sourcesContent":["import { SyncStateMachine, t } from '../../core/utility/sync-state-machine';\nimport {\n\tclampPointToBounds3D,\n\tcomputeBounds3DHits,\n\tconstrainMovementToBounds3D,\n\thasAnyBounds3DHit,\n\tinsetBounds3D,\n\ttype Bounds3DPaddingInput,\n\ttype Bounds3DPoint,\n} from '../shared/bounds-3d';\n\nexport type WorldBoundary3DHit =\n\t| 'top'\n\t| 'bottom'\n\t| 'left'\n\t| 'right'\n\t| 'back'\n\t| 'front';\n\nexport type WorldBoundary3DHits = Record<WorldBoundary3DHit, boolean>;\n\nexport interface WorldBoundary3DPosition extends Bounds3DPoint {}\n\nexport interface WorldBoundary3DBounds {\n\ttop: number;\n\tbottom: number;\n\tleft: number;\n\tright: number;\n\tback: number;\n\tfront: number;\n}\n\nexport enum WorldBoundary3DState {\n\tInside = 'inside',\n\tTouching = 'touching',\n}\n\nexport enum WorldBoundary3DEvent {\n\tEnterInside = 'enter-inside',\n\tTouchBoundary = 'touch-boundary',\n}\n\nexport function computeWorldBoundary3DHits(\n\tposition: WorldBoundary3DPosition,\n\tbounds: WorldBoundary3DBounds,\n\tpadding?: Bounds3DPaddingInput,\n): WorldBoundary3DHits {\n\treturn computeBounds3DHits(position, insetBounds3D({\n\t\tminX: bounds.left,\n\t\tmaxX: bounds.right,\n\t\tminY: bounds.bottom,\n\t\tmaxY: bounds.top,\n\t\tminZ: bounds.back,\n\t\tmaxZ: bounds.front,\n\t}, padding));\n}\n\nexport function hasAnyWorldBoundary3DHit(hits: WorldBoundary3DHits): boolean {\n\treturn hasAnyBounds3DHit(hits);\n}\n\nexport class WorldBoundary3DFSM {\n\tpublic readonly machine: SyncStateMachine<WorldBoundary3DState, WorldBoundary3DEvent, never>;\n\n\tprivate lastHits: WorldBoundary3DHits = {\n\t\ttop: false,\n\t\tbottom: false,\n\t\tleft: false,\n\t\tright: false,\n\t\tback: false,\n\t\tfront: false,\n\t};\n\tprivate lastPosition: WorldBoundary3DPosition | null = null;\n\tprivate lastClampedPosition: WorldBoundary3DPosition | null = null;\n\tprivate lastUpdatedAtMs: number | null = null;\n\n\tconstructor() {\n\t\tthis.machine = new SyncStateMachine<WorldBoundary3DState, WorldBoundary3DEvent, never>(\n\t\t\tWorldBoundary3DState.Inside,\n\t\t\t[\n\t\t\t\tt(WorldBoundary3DState.Inside, WorldBoundary3DEvent.TouchBoundary, WorldBoundary3DState.Touching),\n\t\t\t\tt(WorldBoundary3DState.Touching, WorldBoundary3DEvent.EnterInside, WorldBoundary3DState.Inside),\n\t\t\t\tt(WorldBoundary3DState.Inside, WorldBoundary3DEvent.EnterInside, WorldBoundary3DState.Inside),\n\t\t\t\tt(WorldBoundary3DState.Touching, WorldBoundary3DEvent.TouchBoundary, WorldBoundary3DState.Touching),\n\t\t\t],\n\t\t);\n\t}\n\n\tgetState(): WorldBoundary3DState {\n\t\treturn this.machine.getState();\n\t}\n\n\tgetLastHits(): WorldBoundary3DHits {\n\t\treturn this.lastHits;\n\t}\n\n\tgetMovement(\n\t\tmoveX: number,\n\t\tmoveY: number,\n\t\tmoveZ: number,\n\t): { moveX: number; moveY: number; moveZ: number } {\n\t\treturn constrainMovementToBounds3D(this.lastHits, moveX, moveY, moveZ);\n\t}\n\n\tgetLastPosition(): WorldBoundary3DPosition | null {\n\t\treturn this.lastPosition;\n\t}\n\n\tgetLastClampedPosition(): WorldBoundary3DPosition | null {\n\t\treturn this.lastClampedPosition;\n\t}\n\n\tgetLastUpdatedAtMs(): number | null {\n\t\treturn this.lastUpdatedAtMs;\n\t}\n\n\tupdate(\n\t\tposition: WorldBoundary3DPosition,\n\t\tbounds: WorldBoundary3DBounds,\n\t\tpadding?: Bounds3DPaddingInput,\n\t): WorldBoundary3DHits {\n\t\tconst effectiveBounds = insetBounds3D({\n\t\t\tminX: bounds.left,\n\t\t\tmaxX: bounds.right,\n\t\t\tminY: bounds.bottom,\n\t\t\tmaxY: bounds.top,\n\t\t\tminZ: bounds.back,\n\t\t\tmaxZ: bounds.front,\n\t\t}, padding);\n\n\t\tconst hits = computeBounds3DHits(position, effectiveBounds);\n\t\tthis.lastHits = hits;\n\t\tthis.lastPosition = { x: position.x, y: position.y, z: position.z };\n\t\tthis.lastClampedPosition = clampPointToBounds3D(position, effectiveBounds);\n\t\tthis.lastUpdatedAtMs = Date.now();\n\n\t\tif (hasAnyWorldBoundary3DHit(hits)) {\n\t\t\tthis.dispatch(WorldBoundary3DEvent.TouchBoundary);\n\t\t} else {\n\t\t\tthis.dispatch(WorldBoundary3DEvent.EnterInside);\n\t\t}\n\n\t\treturn hits;\n\t}\n\n\tprivate dispatch(event: WorldBoundary3DEvent): void {\n\t\tif (this.machine.can(event)) {\n\t\t\tthis.machine.syncDispatch(event);\n\t\t}\n\t}\n}\n","import { defineBehavior, type BehaviorRef } from '../behavior-descriptor';\nimport type { BehaviorEntityLink, BehaviorSystem } from '../behavior-system';\nimport type { Bounds3DPaddingInput } from '../shared/bounds-3d';\nimport {\n\tWorldBoundary3DFSM,\n\ttype WorldBoundary3DBounds,\n\ttype WorldBoundary3DHits,\n\ttype WorldBoundary3DPosition,\n} from './world-boundary-3d-fsm';\nimport {\n\tStageBoundaryDim,\n\ttype WasmStageRuntime,\n} from '../../runtime/wasm-stage-runtime';\n\nexport interface WorldBoundary3DOptions {\n\tboundaries: WorldBoundary3DBounds;\n\tpadding?: Bounds3DPaddingInput;\n}\n\nexport interface WorldBoundary3DHandle {\n\tgetLastHits(): WorldBoundary3DHits | null;\n\tgetMovement(\n\t\tmoveX: number,\n\t\tmoveY: number,\n\t\tmoveZ: number,\n\t): { moveX: number; moveY: number; moveZ: number };\n\tgetLastClampedPosition(): WorldBoundary3DPosition | null;\n}\n\nconst defaultOptions: WorldBoundary3DOptions = {\n\tboundaries: { top: 0, bottom: 0, left: 0, right: 0, back: 0, front: 0 },\n\tpadding: 0,\n};\n\nconst WORLD_BOUNDARY_3D_BEHAVIOR_KEY = Symbol.for(\n\t'zylem:behavior:world-boundary-3d',\n);\n\nfunction createWorldBoundary3DHandle(\n\tref: BehaviorRef<WorldBoundary3DOptions>,\n): WorldBoundary3DHandle {\n\treturn {\n\t\tgetLastHits: () => {\n\t\t\tconst fsm = ref.fsm as WorldBoundary3DFSM | undefined;\n\t\t\treturn fsm?.getLastHits() ?? null;\n\t\t},\n\t\tgetMovement: (moveX: number, moveY: number, moveZ: number) => {\n\t\t\tconst fsm = ref.fsm as WorldBoundary3DFSM | undefined;\n\t\t\treturn fsm?.getMovement(moveX, moveY, moveZ) ?? { moveX, moveY, moveZ };\n\t\t},\n\t\tgetLastClampedPosition: () => {\n\t\t\tconst fsm = ref.fsm as WorldBoundary3DFSM | undefined;\n\t\t\treturn fsm?.getLastClampedPosition() ?? null;\n\t\t},\n\t};\n}\n\nclass WorldBoundary3DSystem implements BehaviorSystem {\n\tprivate attachedRuntimeSlots = new Set<number>();\n\n\tconstructor(\n\t\tprivate world: any,\n\t\tprivate wasmStage: WasmStageRuntime | null,\n\t\tprivate getBehaviorLinks?: (key: symbol) => Iterable<BehaviorEntityLink>,\n\t) {}\n\n\tupdate(_ecs: unknown, _delta: number): void {\n\t\tconst links = this.getBehaviorLinks?.(WORLD_BOUNDARY_3D_BEHAVIOR_KEY);\n\t\tif (!links) return;\n\n\t\tfor (const link of links) {\n\t\t\tconst gameEntity = link.entity as any;\n\t\t\tconst boundaryRef = link.ref as any;\n\t\t\tconst options = boundaryRef.options as WorldBoundary3DOptions;\n\t\t\tif (!boundaryRef.fsm) {\n\t\t\t\tboundaryRef.fsm = new WorldBoundary3DFSM();\n\t\t\t}\n\n\t\t\tconst handle = (gameEntity.runtimeHandle ?? -1) as number;\n\t\t\tif (this.wasmStage && handle >= 0) {\n\t\t\t\tthis.ensureWasmAttached(handle, options);\n\t\t\t\tconst query = this.wasmStage.queryWorldBoundary(handle);\n\t\t\t\tif (query) {\n\t\t\t\t\tboundaryRef.fsm.update(\n\t\t\t\t\t\t{ x: query.clamped[0], y: query.clamped[1], z: query.clamped[2] },\n\t\t\t\t\t\toptions.boundaries,\n\t\t\t\t\t\toptions.padding,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!gameEntity.body) continue;\n\t\t\tconst pos = gameEntity.body.translation();\n\t\t\tboundaryRef.fsm.update(\n\t\t\t\t{ x: pos.x, y: pos.y, z: pos.z },\n\t\t\t\toptions.boundaries,\n\t\t\t\toptions.padding,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate ensureWasmAttached(handle: number, options: WorldBoundary3DOptions): void {\n\t\tif (!this.wasmStage || this.attachedRuntimeSlots.has(handle)) return;\n\t\tconst padding = typeof options.padding === 'number' ? options.padding : 0;\n\t\tthis.wasmStage.attachWorldBoundary(handle, StageBoundaryDim.Three, {\n\t\t\ttop: options.boundaries.top,\n\t\t\tbottom: options.boundaries.bottom,\n\t\t\tleft: options.boundaries.left,\n\t\t\tright: options.boundaries.right,\n\t\t\tfront: options.boundaries.front,\n\t\t\tback: options.boundaries.back,\n\t\t\tpadding,\n\t\t});\n\t\tthis.attachedRuntimeSlots.add(handle);\n\t}\n\n\tdestroy(_ecs: unknown): void {\n\t\tthis.attachedRuntimeSlots.clear();\n\t}\n}\n\nexport const WorldBoundary3DBehavior = defineBehavior({\n\tname: 'world-boundary-3d',\n\tdefaultOptions,\n\tsystemFactory: (ctx) =>\n\t\tnew WorldBoundary3DSystem(ctx.world, ctx.wasmStage ?? null, ctx.getBehaviorLinks),\n\tcreateHandle: createWorldBoundary3DHandle,\n});\n"],"mappings":"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","names":["WorldBoundary3DState","WorldBoundary3DEvent"]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// src/lib/core/utility/sync-state-machine.ts
|
|
2
|
+
import {
|
|
3
|
+
StateMachine as BaseStateMachine
|
|
4
|
+
} from "typescript-fsm";
|
|
5
|
+
import { t } from "typescript-fsm";
|
|
6
|
+
var SyncStateMachine = class extends BaseStateMachine {
|
|
7
|
+
constructor(init, transitions = [], logger = console) {
|
|
8
|
+
super(init, transitions, logger);
|
|
9
|
+
}
|
|
10
|
+
dispatch(_event, ..._args) {
|
|
11
|
+
throw new Error("SyncStateMachine does not support async dispatch.");
|
|
12
|
+
}
|
|
13
|
+
syncDispatch(event, ...args) {
|
|
14
|
+
const found = this.transitions.some((transition) => {
|
|
15
|
+
if (transition.fromState !== this._current || transition.event !== event) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
const current = this._current;
|
|
19
|
+
this._current = transition.toState;
|
|
20
|
+
if (!transition.cb) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
transition.cb(...args);
|
|
25
|
+
return true;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
this._current = current;
|
|
28
|
+
this.logger.error("Exception in callback", error);
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
if (!found) {
|
|
33
|
+
const errorMessage = this.formatErr(this._current, event);
|
|
34
|
+
this.logger.error(errorMessage);
|
|
35
|
+
}
|
|
36
|
+
return found;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
SyncStateMachine,
|
|
42
|
+
t
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=chunk-4URF65BR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/core/utility/sync-state-machine.ts"],"sourcesContent":["import {\n\tStateMachine as BaseStateMachine,\n\ttype ILogger,\n\ttype ITransition,\n\ttype SyncCallback,\n} from 'typescript-fsm';\n\nexport { t } from 'typescript-fsm';\nexport type { ILogger, ITransition, SyncCallback };\n\n/**\n * Local wrapper around typescript-fsm's SyncStateMachine.\n *\n * typescript-fsm@1.6.0 incorrectly reports callback-less transitions as\n * unhandled even after moving to the next state, which causes noisy\n * `No transition...` console errors for valid FSM dispatches.\n */\nexport class SyncStateMachine<\n\tSTATE extends string | number | symbol,\n\tEVENT extends string | number | symbol,\n\tCALLBACK extends Record<EVENT, SyncCallback> = Record<EVENT, SyncCallback>,\n> extends BaseStateMachine<STATE, EVENT, CALLBACK> {\n\tconstructor(\n\t\tinit: STATE,\n\t\ttransitions: ITransition<STATE, EVENT, CALLBACK[EVENT]>[] = [],\n\t\tlogger: ILogger = console,\n\t) {\n\t\tsuper(init, transitions, logger);\n\t}\n\n\tdispatch<E extends EVENT>(_event: E, ..._args: unknown[]): Promise<void> {\n\t\tthrow new Error('SyncStateMachine does not support async dispatch.');\n\t}\n\n\tsyncDispatch<E extends EVENT>(event: E, ...args: unknown[]): boolean {\n\t\tconst found = this.transitions.some((transition) => {\n\t\t\tif (transition.fromState !== this._current || transition.event !== event) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst current = this._current;\n\t\t\tthis._current = transition.toState;\n\n\t\t\tif (!transition.cb) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\ttransition.cb(...args);\n\t\t\t\treturn true;\n\t\t\t} catch (error) {\n\t\t\t\tthis._current = current;\n\t\t\t\tthis.logger.error('Exception in callback', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t});\n\n\t\tif (!found) {\n\t\t\tconst errorMessage = this.formatErr(this._current, event);\n\t\t\tthis.logger.error(errorMessage);\n\t\t}\n\n\t\treturn found;\n\t}\n}\n"],"mappings":";AAAA;AAAA,EACC,gBAAgB;AAAA,OAIV;AAEP,SAAS,SAAS;AAUX,IAAM,mBAAN,cAIG,iBAAyC;AAAA,EAClD,YACC,MACA,cAA4D,CAAC,GAC7D,SAAkB,SACjB;AACD,UAAM,MAAM,aAAa,MAAM;AAAA,EAChC;AAAA,EAEA,SAA0B,WAAc,OAAiC;AACxE,UAAM,IAAI,MAAM,mDAAmD;AAAA,EACpE;AAAA,EAEA,aAA8B,UAAa,MAA0B;AACpE,UAAM,QAAQ,KAAK,YAAY,KAAK,CAAC,eAAe;AACnD,UAAI,WAAW,cAAc,KAAK,YAAY,WAAW,UAAU,OAAO;AACzE,eAAO;AAAA,MACR;AAEA,YAAM,UAAU,KAAK;AACrB,WAAK,WAAW,WAAW;AAE3B,UAAI,CAAC,WAAW,IAAI;AACnB,eAAO;AAAA,MACR;AAEA,UAAI;AACH,mBAAW,GAAG,GAAG,IAAI;AACrB,eAAO;AAAA,MACR,SAAS,OAAO;AACf,aAAK,WAAW;AAChB,aAAK,OAAO,MAAM,yBAAyB,KAAK;AAChD,cAAM;AAAA,MACP;AAAA,IACD,CAAC;AAED,QAAI,CAAC,OAAO;AACX,YAAM,eAAe,KAAK,UAAU,KAAK,UAAU,KAAK;AACxD,WAAK,OAAO,MAAM,YAAY;AAAA,IAC/B;AAEA,WAAO;AAAA,EACR;AACD;","names":[]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {
|
|
2
|
+
angleFromDirection2D,
|
|
3
|
+
directionFromAngle2D,
|
|
4
|
+
getRotationAngle2D,
|
|
5
|
+
normalizeDirection2D
|
|
6
|
+
} from "./chunk-ID5QJYF5.js";
|
|
7
|
+
|
|
8
|
+
// src/lib/coordinators/shared/top-down-aim.ts
|
|
9
|
+
function getEntityFacingAngle2D(entity) {
|
|
10
|
+
if (entity.topDownMovementState) {
|
|
11
|
+
return entity.topDownMovementState.facingAngle;
|
|
12
|
+
}
|
|
13
|
+
if (entity.body?.rotation) {
|
|
14
|
+
return getRotationAngle2D(entity.body.rotation());
|
|
15
|
+
}
|
|
16
|
+
return entity._rotation2DAngle ?? 0;
|
|
17
|
+
}
|
|
18
|
+
function resolveAim2D(aimX, aimY, fallbackAngle) {
|
|
19
|
+
const direction = normalizeDirection2D(aimX, aimY);
|
|
20
|
+
if (direction) {
|
|
21
|
+
return {
|
|
22
|
+
angle: angleFromDirection2D(direction),
|
|
23
|
+
direction,
|
|
24
|
+
hasAimInput: true
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
angle: fallbackAngle,
|
|
29
|
+
direction: directionFromAngle2D(fallbackAngle),
|
|
30
|
+
hasAimInput: false
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export {
|
|
35
|
+
getEntityFacingAngle2D,
|
|
36
|
+
resolveAim2D
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=chunk-6C6NXNEW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/coordinators/shared/top-down-aim.ts"],"sourcesContent":["import {\n\tangleFromDirection2D,\n\tdirectionFromAngle2D,\n\tgetRotationAngle2D,\n\tnormalizeDirection2D,\n} from '../../behaviors/shared/direction-2d';\n\nexport interface ResolvedAim2D {\n\tangle: number;\n\tdirection: { x: number; y: number };\n\thasAimInput: boolean;\n}\n\nexport function getEntityFacingAngle2D(entity: any): number {\n\tif (entity.topDownMovementState) {\n\t\treturn entity.topDownMovementState.facingAngle;\n\t}\n\n\tif (entity.body?.rotation) {\n\t\treturn getRotationAngle2D(entity.body.rotation());\n\t}\n\n\treturn entity._rotation2DAngle ?? 0;\n}\n\nexport function resolveAim2D(\n\taimX: number,\n\taimY: number,\n\tfallbackAngle: number,\n): ResolvedAim2D {\n\tconst direction = normalizeDirection2D(aimX, aimY);\n\tif (direction) {\n\t\treturn {\n\t\t\tangle: angleFromDirection2D(direction),\n\t\t\tdirection,\n\t\t\thasAimInput: true,\n\t\t};\n\t}\n\n\treturn {\n\t\tangle: fallbackAngle,\n\t\tdirection: directionFromAngle2D(fallbackAngle),\n\t\thasAimInput: false,\n\t};\n}\n"],"mappings":";;;;;;;;AAaO,SAAS,uBAAuB,QAAqB;AAC3D,MAAI,OAAO,sBAAsB;AAChC,WAAO,OAAO,qBAAqB;AAAA,EACpC;AAEA,MAAI,OAAO,MAAM,UAAU;AAC1B,WAAO,mBAAmB,OAAO,KAAK,SAAS,CAAC;AAAA,EACjD;AAEA,SAAO,OAAO,oBAAoB;AACnC;AAEO,SAAS,aACf,MACA,MACA,eACgB;AAChB,QAAM,YAAY,qBAAqB,MAAM,IAAI;AACjD,MAAI,WAAW;AACd,WAAO;AAAA,MACN,OAAO,qBAAqB,SAAS;AAAA,MACrC;AAAA,MACA,aAAa;AAAA,IACd;AAAA,EACD;AAEA,SAAO;AAAA,IACN,OAAO;AAAA,IACP,WAAW,qBAAqB,aAAa;AAAA,IAC7C,aAAa;AAAA,EACd;AACD;","names":[]}
|