@zylem/behaviors 0.8.9 → 0.8.11

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.
Files changed (168) hide show
  1. package/dist/acrobatics-2d.d.ts +25 -0
  2. package/dist/acrobatics-2d.js +37 -0
  3. package/dist/action-rpg.d.ts +21 -0
  4. package/dist/action-rpg.js +39 -0
  5. package/dist/adventure-tools.d.ts +22 -0
  6. package/dist/adventure-tools.js +39 -0
  7. package/dist/behavior-descriptor-NS_qnzzp.d.ts +4527 -0
  8. package/dist/behavior-metadata-Drm1zxI2.d.ts +13 -0
  9. package/dist/catalog.d.ts +2143 -0
  10. package/dist/catalog.js +9 -0
  11. package/dist/chunk-2BE5BW7D.js +130 -0
  12. package/dist/{chunk-CTSYM56P.js → chunk-2QCCLHJC.js} +10 -6
  13. package/dist/chunk-2UGT4MIF.js +94 -0
  14. package/dist/chunk-3J3ISPFQ.js +99 -0
  15. package/dist/chunk-3LBGEWT6.js +124 -0
  16. package/dist/{chunk-UUMH3DC3.js → chunk-3QBNWT7B.js} +5 -1
  17. package/dist/chunk-3T44EDXU.js +114 -0
  18. package/dist/chunk-5AJJ44QK.js +129 -0
  19. package/dist/{chunk-YDLO5M7S.js → chunk-5SMXQ6AU.js} +1 -0
  20. package/dist/chunk-5VJIP535.js +71 -0
  21. package/dist/chunk-6DTIJB7B.js +166 -0
  22. package/dist/chunk-73MHJY6R.js +78 -0
  23. package/dist/chunk-73YASAYS.js +137 -0
  24. package/dist/{chunk-LLDLVVG4.js → chunk-7HGDADLB.js} +25 -9
  25. package/dist/chunk-7IOQEXYR.js +92 -0
  26. package/dist/{chunk-OU2SW4U5.js → chunk-APAM55J3.js} +10 -6
  27. package/dist/chunk-BLTBZJPG.js +126 -0
  28. package/dist/chunk-BN6QZDR5.js +90 -0
  29. package/dist/chunk-BR33KT4Z.js +3186 -0
  30. package/dist/chunk-C5AYL42A.js +138 -0
  31. package/dist/{chunk-OBF6BIA2.js → chunk-CBQSC6W3.js} +2 -2
  32. package/dist/chunk-DDLZLJ7W.js +8 -0
  33. package/dist/chunk-DRIXFB4C.js +132 -0
  34. package/dist/{chunk-7CE6OHLW.js → chunk-DTLUKSX2.js} +6 -2
  35. package/dist/{chunk-TK3AXXXI.js → chunk-EMEA45JQ.js} +10 -6
  36. package/dist/chunk-EQFLPRF7.js +130 -0
  37. package/dist/{chunk-M25QJBDN.js → chunk-GNRMH7D3.js} +18 -6
  38. package/dist/chunk-GYEKT5AY.js +102 -0
  39. package/dist/{chunk-SHZZ2QRV.js → chunk-HCWLIPXP.js} +5 -1
  40. package/dist/{chunk-WHKPWIQO.js → chunk-HMVEQB2T.js} +1 -0
  41. package/dist/chunk-HQ2UFI3S.js +116 -0
  42. package/dist/chunk-I52PC5IU.js +128 -0
  43. package/dist/{chunk-CB3C7CRD.js → chunk-IAIJG2PB.js} +10 -6
  44. package/dist/{chunk-NDJO56BB.js → chunk-IJILTFEL.js} +5 -1
  45. package/dist/{chunk-7GGR26YA.js → chunk-IRNU4JGV.js} +10 -6
  46. package/dist/chunk-IRPBFVAK.js +141 -0
  47. package/dist/chunk-JFBR4FNX.js +140 -0
  48. package/dist/{chunk-ERS6SNA5.js → chunk-K34N2CBL.js} +5 -1
  49. package/dist/chunk-LMMF7KXF.js +178 -0
  50. package/dist/chunk-LWGJ3QQZ.js +107 -0
  51. package/dist/chunk-MPNGQLHZ.js +132 -0
  52. package/dist/chunk-OH5LJAEJ.js +135 -0
  53. package/dist/{chunk-57GLKGEX.js → chunk-P6TMZSWF.js} +32 -7
  54. package/dist/chunk-PDHHOVVQ.js +126 -0
  55. package/dist/{chunk-7TIIRJKF.js → chunk-RUBKGAPS.js} +5 -1
  56. package/dist/chunk-S44ZHYE6.js +1912 -0
  57. package/dist/chunk-SFMJNAW3.js +128 -0
  58. package/dist/{chunk-E2MOFIDH.js → chunk-SKKVOH6D.js} +13 -7
  59. package/dist/{chunk-RR3XNDKG.js → chunk-T2LAQQDS.js} +1 -1
  60. package/dist/chunk-TSYXRRS4.js +125 -0
  61. package/dist/chunk-TZHO6P22.js +150 -0
  62. package/dist/chunk-V26L6MY6.js +127 -0
  63. package/dist/chunk-V4M46BF6.js +105 -0
  64. package/dist/chunk-VSFWETXS.js +127 -0
  65. package/dist/chunk-VU4LTE2X.js +147 -0
  66. package/dist/chunk-WF6HC5U3.js +8 -0
  67. package/dist/chunk-WG5NCDS4.js +99 -0
  68. package/dist/{chunk-7LHXAEOZ.js → chunk-WUYHS6JZ.js} +6 -2
  69. package/dist/chunk-XNRY6NOC.js +120 -0
  70. package/dist/{chunk-55CFXXZH.js → chunk-XT47QZQ6.js} +32 -7
  71. package/dist/chunk-Y5CCNMJI.js +58 -0
  72. package/dist/chunk-YVVB6YMG.js +133 -0
  73. package/dist/chunk-ZZXTGVGN.js +131 -0
  74. package/dist/combat-ai.d.ts +21 -0
  75. package/dist/combat-ai.js +41 -0
  76. package/dist/{components--RTwuT4S.d.ts → components-ClmBORMP.d.ts} +1 -1
  77. package/dist/cooldown.d.ts +2 -8
  78. package/dist/cooldown.js +3 -2
  79. package/dist/coordinators/boundary-ricochet-3d.d.ts +3 -2
  80. package/dist/coordinators/boundary-ricochet-3d.js +2 -2
  81. package/dist/coordinators/boundary-ricochet.d.ts +3 -2
  82. package/dist/coordinators/boundary-ricochet.js +2 -2
  83. package/dist/coordinators/multidirectional-space-shooter.d.ts +3 -2
  84. package/dist/coordinators/multidirectional-space-shooter.js +2 -2
  85. package/dist/coordinators/top-down-shooter.d.ts +3 -2
  86. package/dist/coordinators/top-down-shooter.js +2 -2
  87. package/dist/core.d.ts +387 -26
  88. package/dist/core.js +1583 -6
  89. package/dist/destructible-3d.d.ts +3 -2
  90. package/dist/destructible-3d.js +3 -2
  91. package/dist/destructible-prebake.d.ts +1 -1
  92. package/dist/environmental-locomotion.d.ts +23 -0
  93. package/dist/environmental-locomotion.js +35 -0
  94. package/dist/first-person.d.ts +3 -2
  95. package/dist/first-person.js +25 -4
  96. package/dist/golf-racket.d.ts +21 -0
  97. package/dist/golf-racket.js +37 -0
  98. package/dist/index.d.ts +28 -6
  99. package/dist/index.js +766 -82
  100. package/dist/jumper-2d.d.ts +2 -1
  101. package/dist/jumper-2d.js +25 -4
  102. package/dist/jumper-3d.d.ts +2 -1
  103. package/dist/jumper-3d.js +25 -4
  104. package/dist/lane-defense.d.ts +21 -0
  105. package/dist/lane-defense.js +39 -0
  106. package/dist/life-simulation.d.ts +21 -0
  107. package/dist/life-simulation.js +39 -0
  108. package/dist/matching-pinball.d.ts +21 -0
  109. package/dist/matching-pinball.js +39 -0
  110. package/dist/melee-combat.d.ts +22 -0
  111. package/dist/melee-combat.js +45 -0
  112. package/dist/{moveable-CFOHmblC.d.ts → moveable-CXK2wEZL.d.ts} +2 -2
  113. package/dist/particle-emitter/presets.d.ts +1 -1
  114. package/dist/particle-emitter.d.ts +5 -4
  115. package/dist/particle-emitter.js +3 -2
  116. package/dist/{payload-BQzu-oY_.d.ts → payload-WEzfgjSL.d.ts} +1 -1
  117. package/dist/platformer-2d.d.ts +2 -1
  118. package/dist/platformer-2d.js +25 -4
  119. package/dist/platformer-3d.d.ts +2 -1
  120. package/dist/platformer-3d.js +25 -4
  121. package/dist/racing-vehicles.d.ts +21 -0
  122. package/dist/racing-vehicles.js +37 -0
  123. package/dist/rhythm-performance.d.ts +21 -0
  124. package/dist/rhythm-performance.js +39 -0
  125. package/dist/ricochet-2d.d.ts +2 -1
  126. package/dist/ricochet-2d.js +25 -4
  127. package/dist/ricochet-3d.d.ts +2 -1
  128. package/dist/ricochet-3d.js +25 -4
  129. package/dist/runtime-2d.d.ts +2 -1
  130. package/dist/runtime-2d.js +10 -1
  131. package/dist/screen-visibility.d.ts +2 -1
  132. package/dist/screen-visibility.js +3 -2
  133. package/dist/screen-wrap.d.ts +2 -7
  134. package/dist/screen-wrap.js +4 -3
  135. package/dist/shooter-2d.d.ts +3 -2
  136. package/dist/shooter-2d.js +3 -2
  137. package/dist/skate-snowboard.d.ts +21 -0
  138. package/dist/skate-snowboard.js +37 -0
  139. package/dist/{spark-C4Tp-52_.d.ts → spark-VoQoemz_.d.ts} +1 -1
  140. package/dist/spatial-puzzles.d.ts +21 -0
  141. package/dist/spatial-puzzles.js +35 -0
  142. package/dist/squad-rts.d.ts +21 -0
  143. package/dist/squad-rts.js +39 -0
  144. package/dist/stealth-city.d.ts +21 -0
  145. package/dist/stealth-city.js +41 -0
  146. package/dist/survival-resources.d.ts +21 -0
  147. package/dist/survival-resources.js +37 -0
  148. package/dist/tactical-grid.d.ts +21 -0
  149. package/dist/tactical-grid.js +39 -0
  150. package/dist/thruster.d.ts +2 -1
  151. package/dist/thruster.js +4 -3
  152. package/dist/top-down-movement.d.ts +2 -1
  153. package/dist/top-down-movement.js +24 -3
  154. package/dist/turn-based-rpg.d.ts +21 -0
  155. package/dist/turn-based-rpg.js +39 -0
  156. package/dist/{vector-DjhLUrak.d.ts → vector-s7GbCrVn.d.ts} +1 -1
  157. package/dist/weapon-systems.d.ts +22 -0
  158. package/dist/weapon-systems.js +45 -0
  159. package/dist/world-boundary-2d.d.ts +2 -1
  160. package/dist/world-boundary-2d.js +25 -4
  161. package/dist/world-boundary-3d.d.ts +2 -1
  162. package/dist/world-boundary-3d.js +26 -5
  163. package/package.json +90 -5
  164. package/dist/behavior-system-DkFAwLb_.d.ts +0 -914
  165. package/dist/chunk-GZ3DQIIZ.js +0 -888
  166. package/dist/{chunk-OSPQFWQF.js → chunk-4K37UL53.js} +3 -3
  167. package/dist/{chunk-DD4U42FG.js → chunk-AQB246W5.js} +3 -3
  168. package/dist/{chunk-UKGBQTHL.js → chunk-CXDNR4R4.js} +3 -3
@@ -0,0 +1,3186 @@
1
+ import {
2
+ PUZZLE_ABILITIES,
3
+ puzzleCommandData,
4
+ puzzleData
5
+ } from "./chunk-XNRY6NOC.js";
6
+ import {
7
+ ADVENTURE_ABILITIES,
8
+ ADVENTURE_BUTTONS,
9
+ ADVENTURE_FLAGS
10
+ } from "./chunk-Y5CCNMJI.js";
11
+ import {
12
+ ENVIRONMENT_ABILITIES
13
+ } from "./chunk-DDLZLJ7W.js";
14
+ import {
15
+ TRAVERSAL_ABILITIES
16
+ } from "./chunk-WF6HC5U3.js";
17
+ import {
18
+ SPORTS_ABILITIES,
19
+ sportsData,
20
+ sportsOperation
21
+ } from "./chunk-7IOQEXYR.js";
22
+ import {
23
+ SKATE_ABILITIES,
24
+ skateData,
25
+ skateOperation
26
+ } from "./chunk-3J3ISPFQ.js";
27
+ import {
28
+ RACING_ABILITIES,
29
+ racingData,
30
+ racingOperation
31
+ } from "./chunk-2UGT4MIF.js";
32
+ import {
33
+ SURVIVAL_ABILITIES,
34
+ survivalData,
35
+ survivalOperation
36
+ } from "./chunk-PDHHOVVQ.js";
37
+ import {
38
+ CITY_ABILITIES,
39
+ CITY_EVENTS,
40
+ CITY_INTENTS,
41
+ cityData
42
+ } from "./chunk-5VJIP535.js";
43
+ import {
44
+ ENCOUNTER_ABILITIES,
45
+ ENCOUNTER_EVENTS,
46
+ ENCOUNTER_INTENTS,
47
+ encounterData
48
+ } from "./chunk-73MHJY6R.js";
49
+ import {
50
+ WEAPON_ABILITIES,
51
+ WEAPON_EVENTS,
52
+ WEAPON_PHASES,
53
+ weaponButtons,
54
+ weaponData,
55
+ weaponModeData,
56
+ weaponProjectileData
57
+ } from "./chunk-GYEKT5AY.js";
58
+ import {
59
+ COMBAT_ABILITIES,
60
+ COMBAT_EVENTS,
61
+ COMBAT_PHASES,
62
+ combatAttackData,
63
+ combatButtons,
64
+ combatData,
65
+ combatHitData
66
+ } from "./chunk-V4M46BF6.js";
67
+ import {
68
+ RHYTHM_ABILITIES,
69
+ rhythmData,
70
+ rhythmOperation
71
+ } from "./chunk-LWGJ3QQZ.js";
72
+ import {
73
+ MATCHING_ABILITIES,
74
+ matchingData,
75
+ matchingOperation
76
+ } from "./chunk-5AJJ44QK.js";
77
+ import {
78
+ LIFE_ABILITIES,
79
+ lifeData,
80
+ lifeOperation
81
+ } from "./chunk-TZHO6P22.js";
82
+ import {
83
+ DEFENSE_ABILITIES,
84
+ defenseData,
85
+ defenseOperation
86
+ } from "./chunk-SFMJNAW3.js";
87
+ import {
88
+ SQUAD_ABILITIES,
89
+ squadData,
90
+ squadOperation
91
+ } from "./chunk-TSYXRRS4.js";
92
+ import {
93
+ TACTICS_ABILITIES,
94
+ tacticsData,
95
+ tacticsOperation
96
+ } from "./chunk-6DTIJB7B.js";
97
+ import {
98
+ BATTLE_ABILITIES,
99
+ battleData,
100
+ battleOperation
101
+ } from "./chunk-LMMF7KXF.js";
102
+ import {
103
+ RPG_ABILITIES,
104
+ rpgData,
105
+ rpgOperation
106
+ } from "./chunk-VU4LTE2X.js";
107
+
108
+ // src/lib/runtime/wasm-stage-runtime.ts
109
+ import {
110
+ loadStageFfi,
111
+ assertRuntimeCompatibility,
112
+ assertRuntimeBehavior,
113
+ STAGE_RENDER_STRIDE,
114
+ STAGE_INPUT_STRIDE,
115
+ STAGE_SNAPSHOT_STRIDE,
116
+ STAGE_EVENT_STRIDE
117
+ } from "@zylem/runtime";
118
+ var STAGE_INVALID_SLOT = 4294967295;
119
+ var StageBodyKind = {
120
+ Dynamic: 0,
121
+ Static: 1,
122
+ KinematicPosition: 2,
123
+ KinematicVelocity: 3
124
+ };
125
+ var StageBoundaryDim = {
126
+ Two: 2,
127
+ Three: 3
128
+ };
129
+ var StageRicochetDim = {
130
+ Two: 2,
131
+ Three: 3
132
+ };
133
+ var StageRicochetReflection = {
134
+ Mirror: 0,
135
+ Angled: 1
136
+ };
137
+ var StageTopDownPlane = {
138
+ Xy: 0,
139
+ Xz: 1
140
+ };
141
+ var StageEventType = {
142
+ CollisionStarted: 1,
143
+ CollisionStopped: 2,
144
+ BoundaryHit: 3,
145
+ Ricochet: 4,
146
+ RegionEntered: 5,
147
+ JumpStarted: 6,
148
+ Landed: 7,
149
+ Wrapped: 8,
150
+ CooldownFired: 9,
151
+ GroundPoundImpact: 10,
152
+ DrowningDamage: 11,
153
+ Adventure: 12,
154
+ Puzzle: 13,
155
+ Combat: 14,
156
+ Weapon: 15,
157
+ Encounter: 16,
158
+ City: 17,
159
+ Survival: 18,
160
+ Racing: 19,
161
+ Skate: 20,
162
+ Sports: 21,
163
+ Rpg: 22,
164
+ Matching: 28,
165
+ Rhythm: 29,
166
+ Life: 27,
167
+ Defense: 26,
168
+ Squad: 25,
169
+ Tactics: 24,
170
+ Battle: 23
171
+ };
172
+ var StagePlatformerShapeSource = {
173
+ Auto: 0,
174
+ Override: 1
175
+ };
176
+ var STAGE_INPUT_BUTTON_JUMP = 1;
177
+ var STAGE_INPUT_BUTTON_RUN = 2;
178
+ function readBehaviorSnapshot(ref) {
179
+ const backref = ref;
180
+ const wasmStage = backref.__wasmStage;
181
+ const handle = backref.__entity?.runtimeHandle ?? -1;
182
+ if (!wasmStage || handle < 0) return null;
183
+ return wasmStage.readSnapshot(handle);
184
+ }
185
+ function readStageRenderSlot(view, slot, out) {
186
+ const base = slot * STAGE_RENDER_STRIDE;
187
+ if (slot < 0 || base + STAGE_RENDER_STRIDE > view.length) {
188
+ return null;
189
+ }
190
+ const result = out ?? {
191
+ position: [0, 0, 0],
192
+ rotation: [0, 0, 0, 1],
193
+ scale: 1,
194
+ custom: [0, 0, 0, 0],
195
+ isSleeping: false
196
+ };
197
+ result.position[0] = view[base];
198
+ result.position[1] = view[base + 1];
199
+ result.position[2] = view[base + 2];
200
+ result.rotation[0] = view[base + 3];
201
+ result.rotation[1] = view[base + 4];
202
+ result.rotation[2] = view[base + 5];
203
+ result.rotation[3] = view[base + 6];
204
+ const scale = view[base + 7];
205
+ result.scale = scale === 0 ? 1 : scale;
206
+ result.custom[0] = view[base + 8];
207
+ result.custom[1] = view[base + 9];
208
+ result.custom[2] = view[base + 10];
209
+ result.custom[3] = view[base + 11];
210
+ result.isSleeping = result.custom[3] > 0.5;
211
+ return result;
212
+ }
213
+ var WasmStageRuntime = class {
214
+ exports;
215
+ compatibility;
216
+ memory;
217
+ renderView = new Float32Array(0);
218
+ inputView = new Float32Array(0);
219
+ snapshotView = new Float32Array(0);
220
+ eventView = new Float32Array(0);
221
+ poseView = new Float32Array(0);
222
+ vec3View = new Float32Array(0);
223
+ queryView = new Float32Array(0);
224
+ scratchView = new Float32Array(0);
225
+ constructor(exports, options = {}) {
226
+ this.compatibility = assertRuntimeCompatibility(exports, options.requiredBehaviors);
227
+ this.exports = exports;
228
+ this.memory = exports.memory;
229
+ const capacity = Math.max(options.initialCapacity ?? 64, 8);
230
+ exports.zylem_stage_create(capacity);
231
+ if (options.gravity) {
232
+ exports.zylem_stage_set_gravity(
233
+ options.gravity[0],
234
+ options.gravity[1],
235
+ options.gravity[2]
236
+ );
237
+ }
238
+ this.refreshViews();
239
+ }
240
+ /** Recreate typed views over wasm memory. Call after step() or whenever memory may have grown. */
241
+ refreshViews() {
242
+ const buffer = this.memory.buffer;
243
+ const renderPtr = this.exports.zylem_stage_render_ptr();
244
+ const renderLen = this.exports.zylem_stage_render_len();
245
+ const inputPtr = this.exports.zylem_stage_input_ptr();
246
+ const inputLen = this.exports.zylem_stage_input_len();
247
+ const snapshotPtr = this.exports.zylem_stage_snapshot_ptr();
248
+ const snapshotLen = this.exports.zylem_stage_snapshot_len();
249
+ const eventPtr = this.exports.zylem_stage_event_ptr();
250
+ const eventLen = this.exports.zylem_stage_event_len();
251
+ const posePtr = this.exports.zylem_stage_pose_ptr();
252
+ const poseLen = this.exports.zylem_stage_pose_len();
253
+ const vec3Ptr = this.exports.zylem_stage_vec3_ptr();
254
+ const queryPtr = this.exports.zylem_stage_query_ptr();
255
+ const queryLen = this.exports.zylem_stage_query_len();
256
+ const scratchPtr = this.exports.zylem_stage_scratch_ptr();
257
+ const scratchCap = this.exports.zylem_stage_scratch_capacity();
258
+ this.renderView = renderLen > 0 ? new Float32Array(buffer, renderPtr, renderLen) : new Float32Array(0);
259
+ this.inputView = inputLen > 0 ? new Float32Array(buffer, inputPtr, inputLen) : new Float32Array(0);
260
+ this.snapshotView = snapshotLen > 0 ? new Float32Array(buffer, snapshotPtr, snapshotLen) : new Float32Array(0);
261
+ this.eventView = eventLen > 0 ? new Float32Array(buffer, eventPtr, eventLen) : new Float32Array(0);
262
+ this.poseView = poseLen > 0 ? new Float32Array(buffer, posePtr, poseLen) : new Float32Array(0);
263
+ this.vec3View = vec3Ptr !== 0 ? new Float32Array(buffer, vec3Ptr, 3) : new Float32Array(0);
264
+ this.queryView = queryLen > 0 ? new Float32Array(buffer, queryPtr, queryLen) : new Float32Array(0);
265
+ this.scratchView = scratchCap > 0 ? new Float32Array(buffer, scratchPtr, scratchCap) : new Float32Array(0);
266
+ }
267
+ /** Step the simulation by `dt` seconds. Refreshes views afterwards. */
268
+ step(dt) {
269
+ const active = this.exports.zylem_stage_step(dt);
270
+ this.refreshViews();
271
+ return active;
272
+ }
273
+ dispose() {
274
+ this.exports.zylem_stage_destroy();
275
+ }
276
+ get tickCount() {
277
+ return this.exports.zylem_stage_tick_count() >>> 0;
278
+ }
279
+ get activeCount() {
280
+ return this.exports.zylem_stage_active_count() >>> 0;
281
+ }
282
+ setGravity(x, y, z) {
283
+ this.exports.zylem_stage_set_gravity(x, y, z);
284
+ }
285
+ /**
286
+ * Toggle per-step collision-event collection in the wasm stage. Hosts
287
+ * with no collision listeners should disable it — with thousands of
288
+ * touching bodies the per-step contact-pair hashing is a significant
289
+ * fraction of `step()`.
290
+ */
291
+ setEventsEnabled(enabled) {
292
+ this.exports.zylem_stage_set_events_enabled(enabled ? 1 : 0);
293
+ }
294
+ /** Cap Rapier contact solver iterations (default 4). */
295
+ setSolverIterations(iterations) {
296
+ this.exports.zylem_stage_set_solver_iterations(Math.max(1, iterations | 0));
297
+ }
298
+ /**
299
+ * Override sleep activation thresholds for sleep-capable dynamic bodies.
300
+ * Higher velocity thresholds / lower `timeUntilSleep` put piles to sleep
301
+ * sooner.
302
+ */
303
+ setSleepThresholds(normalizedLinearThreshold, angularThreshold, timeUntilSleep) {
304
+ this.exports.zylem_stage_set_sleep_thresholds(
305
+ normalizedLinearThreshold,
306
+ angularThreshold,
307
+ timeUntilSleep
308
+ );
309
+ }
310
+ /** Number of awake (non-sleeping) dynamic rigid bodies. */
311
+ awakeDynamicCount() {
312
+ return this.exports.zylem_stage_awake_dynamic_count();
313
+ }
314
+ // ─── Entity lifecycle ───────────────────────────────────────────────
315
+ createEntity() {
316
+ const slot = this.exports.zylem_stage_create_entity() >>> 0;
317
+ this.refreshViews();
318
+ return slot;
319
+ }
320
+ destroyEntity(slot) {
321
+ return this.exports.zylem_stage_destroy_entity(slot) !== 0;
322
+ }
323
+ // ─── Body / collider attach ────────────────────────────────────────
324
+ attachBody(slot, config) {
325
+ const ok = this.exports.zylem_stage_attach_body(
326
+ slot,
327
+ config.kind,
328
+ config.position[0],
329
+ config.position[1],
330
+ config.position[2],
331
+ config.rotation[0],
332
+ config.rotation[1],
333
+ config.rotation[2],
334
+ config.rotation[3],
335
+ config.linearDamping,
336
+ config.angularDamping,
337
+ config.gravityScale,
338
+ config.canSleep ? 1 : 0,
339
+ config.ccdEnabled ? 1 : 0,
340
+ config.lockRotation[0] ? 1 : 0,
341
+ config.lockRotation[1] ? 1 : 0,
342
+ config.lockRotation[2] ? 1 : 0,
343
+ config.lockTranslation[0] ? 1 : 0,
344
+ config.lockTranslation[1] ? 1 : 0,
345
+ config.lockTranslation[2] ? 1 : 0
346
+ );
347
+ return ok !== 0;
348
+ }
349
+ addCollider(slot, config) {
350
+ const offset = config.offset;
351
+ const sensor = config.sensor ? 1 : 0;
352
+ const groups = config.collisionGroups >>> 0;
353
+ const friction = config.friction;
354
+ const restitution = config.restitution;
355
+ switch (config.shape.type) {
356
+ case "box": {
357
+ const [hx, hy, hz] = config.shape.halfExtents;
358
+ return this.exports.zylem_stage_add_collider_box(
359
+ slot,
360
+ hx,
361
+ hy,
362
+ hz,
363
+ offset[0],
364
+ offset[1],
365
+ offset[2],
366
+ friction,
367
+ restitution,
368
+ sensor,
369
+ groups
370
+ ) !== 0;
371
+ }
372
+ case "sphere": {
373
+ return this.exports.zylem_stage_add_collider_sphere(
374
+ slot,
375
+ config.shape.radius,
376
+ offset[0],
377
+ offset[1],
378
+ offset[2],
379
+ friction,
380
+ restitution,
381
+ sensor,
382
+ groups
383
+ ) !== 0;
384
+ }
385
+ case "capsule": {
386
+ return this.exports.zylem_stage_add_collider_capsule(
387
+ slot,
388
+ config.shape.halfHeight,
389
+ config.shape.radius,
390
+ offset[0],
391
+ offset[1],
392
+ offset[2],
393
+ friction,
394
+ restitution,
395
+ sensor,
396
+ groups
397
+ ) !== 0;
398
+ }
399
+ case "cylinder": {
400
+ return this.exports.zylem_stage_add_collider_cylinder(
401
+ slot,
402
+ config.shape.halfHeight,
403
+ config.shape.radius,
404
+ offset[0],
405
+ offset[1],
406
+ offset[2],
407
+ friction,
408
+ restitution,
409
+ sensor,
410
+ groups
411
+ ) !== 0;
412
+ }
413
+ case "cone": {
414
+ return this.exports.zylem_stage_add_collider_cone(
415
+ slot,
416
+ config.shape.halfHeight,
417
+ config.shape.radius,
418
+ offset[0],
419
+ offset[1],
420
+ offset[2],
421
+ friction,
422
+ restitution,
423
+ sensor,
424
+ groups
425
+ ) !== 0;
426
+ }
427
+ case "convexHull": {
428
+ const verts = config.shape.vertices;
429
+ if (verts.length % 3 !== 0) return false;
430
+ const offsetFloats = this.stageScratch(verts);
431
+ return this.exports.zylem_stage_add_collider_convex_hull(
432
+ slot,
433
+ offsetFloats,
434
+ verts.length / 3,
435
+ offset[0],
436
+ offset[1],
437
+ offset[2],
438
+ friction,
439
+ restitution,
440
+ sensor,
441
+ groups
442
+ ) !== 0;
443
+ }
444
+ case "trimesh": {
445
+ const verts = config.shape.vertices;
446
+ const indices = config.shape.indices;
447
+ if (verts.length % 3 !== 0 || indices.length % 3 !== 0) return false;
448
+ const vertOffset = this.stageScratch(verts);
449
+ const indexOffset = this.stageScratchIndices(indices, vertOffset + verts.length);
450
+ return this.exports.zylem_stage_add_collider_trimesh(
451
+ slot,
452
+ vertOffset,
453
+ verts.length / 3,
454
+ indexOffset,
455
+ indices.length,
456
+ offset[0],
457
+ offset[1],
458
+ offset[2],
459
+ friction,
460
+ restitution,
461
+ sensor,
462
+ groups
463
+ ) !== 0;
464
+ }
465
+ case "heightfield": {
466
+ const heights = config.shape.heights;
467
+ const heightOffset = this.stageScratch(heights);
468
+ return this.exports.zylem_stage_add_collider_heightfield(
469
+ slot,
470
+ config.shape.rows,
471
+ config.shape.cols,
472
+ config.shape.scale[0],
473
+ config.shape.scale[1],
474
+ config.shape.scale[2],
475
+ heightOffset,
476
+ offset[0],
477
+ offset[1],
478
+ offset[2],
479
+ friction,
480
+ restitution,
481
+ sensor,
482
+ groups
483
+ ) !== 0;
484
+ }
485
+ default: {
486
+ const _exhaustive = config.shape;
487
+ void _exhaustive;
488
+ return false;
489
+ }
490
+ }
491
+ }
492
+ stageScratch(values, baseOffset = 0) {
493
+ this.scratchView.set(values, baseOffset);
494
+ return baseOffset;
495
+ }
496
+ stageScratchIndices(indices, baseOffset) {
497
+ for (let i = 0; i < indices.length; i++) {
498
+ this.scratchView[baseOffset + i] = indices[i];
499
+ }
500
+ return baseOffset;
501
+ }
502
+ // ─── Pose / velocity ───────────────────────────────────────────────
503
+ getPose(slot, out) {
504
+ const ok = this.exports.zylem_stage_get_pose(slot) !== 0;
505
+ if (!ok) return null;
506
+ const v = this.poseView;
507
+ const result = out ?? {
508
+ position: [0, 0, 0],
509
+ rotation: [0, 0, 0, 1]
510
+ };
511
+ result.position[0] = v[0];
512
+ result.position[1] = v[1];
513
+ result.position[2] = v[2];
514
+ result.rotation[0] = v[3];
515
+ result.rotation[1] = v[4];
516
+ result.rotation[2] = v[5];
517
+ result.rotation[3] = v[6];
518
+ return result;
519
+ }
520
+ setPosition(slot, x, y, z) {
521
+ return this.exports.zylem_stage_set_position(slot, x, y, z) !== 0;
522
+ }
523
+ setRotation(slot, x, y, z, w) {
524
+ return this.exports.zylem_stage_set_rotation(slot, x, y, z, w) !== 0;
525
+ }
526
+ setLinearVelocity(slot, x, y, z) {
527
+ return this.exports.zylem_stage_set_linvel(slot, x, y, z) !== 0;
528
+ }
529
+ getLinearVelocity(slot, out) {
530
+ if (this.exports.zylem_stage_get_linvel(slot) === 0) return null;
531
+ const v = this.vec3View;
532
+ const result = out ?? [0, 0, 0];
533
+ result[0] = v[0];
534
+ result[1] = v[1];
535
+ result[2] = v[2];
536
+ return result;
537
+ }
538
+ setAngularVelocity(slot, x, y, z) {
539
+ return this.exports.zylem_stage_set_angvel(slot, x, y, z) !== 0;
540
+ }
541
+ getAngularVelocity(slot, out) {
542
+ if (this.exports.zylem_stage_get_angvel(slot) === 0) return null;
543
+ const v = this.vec3View;
544
+ const result = out ?? [0, 0, 0];
545
+ result[0] = v[0];
546
+ result[1] = v[1];
547
+ result[2] = v[2];
548
+ return result;
549
+ }
550
+ applyImpulse(slot, x, y, z) {
551
+ return this.exports.zylem_stage_apply_impulse(slot, x, y, z) !== 0;
552
+ }
553
+ // ─── Queries / diagnostics ─────────────────────────────────────────
554
+ /** Cast a ray against the physics world. Returns the closest hit or `null`. */
555
+ castRay(origin, dir, maxDistance, solidOnly = true) {
556
+ const hit = this.exports.zylem_stage_cast_ray(
557
+ origin[0],
558
+ origin[1],
559
+ origin[2],
560
+ dir[0],
561
+ dir[1],
562
+ dir[2],
563
+ maxDistance,
564
+ solidOnly ? 1 : 0
565
+ );
566
+ if (hit === 0) return null;
567
+ const v = this.queryView;
568
+ return {
569
+ slot: v[0] >>> 0,
570
+ distance: v[1],
571
+ normal: [v[2], v[3], v[4]]
572
+ };
573
+ }
574
+ /** Body introspection for debug tooling. */
575
+ getBodyInfo(slot) {
576
+ if (this.exports.zylem_stage_body_info(slot) === 0) return null;
577
+ const v = this.queryView;
578
+ return {
579
+ bodyType: v[0] | 0,
580
+ mass: v[1],
581
+ isSleeping: v[2] > 0.5,
582
+ isEnabled: v[3] > 0.5,
583
+ linearVelocity: [v[4], v[5], v[6]]
584
+ };
585
+ }
586
+ /** Wireframe debug render of every collider (all shapes). */
587
+ getDebugRender() {
588
+ const vertexCount = this.exports.zylem_stage_debug_render() >>> 0;
589
+ this.refreshViews();
590
+ if (vertexCount === 0) return null;
591
+ const buffer = this.memory.buffer;
592
+ const vertices = new Float32Array(
593
+ buffer,
594
+ this.exports.zylem_stage_debug_vertices_ptr(),
595
+ this.exports.zylem_stage_debug_vertices_len()
596
+ );
597
+ const colors = new Float32Array(
598
+ buffer,
599
+ this.exports.zylem_stage_debug_colors_ptr(),
600
+ this.exports.zylem_stage_debug_colors_len()
601
+ );
602
+ return {
603
+ vertices: new Float32Array(vertices),
604
+ colors: new Float32Array(colors)
605
+ };
606
+ }
607
+ // ─── Render / event reads ──────────────────────────────────────────
608
+ /** Read-only view over the render buffer; one slot occupies {@link STAGE_RENDER_STRIDE} floats. */
609
+ get render() {
610
+ return this.renderView;
611
+ }
612
+ get renderStride() {
613
+ return STAGE_RENDER_STRIDE;
614
+ }
615
+ /**
616
+ * Decode a single entity slot from the live render buffer. Convenience
617
+ * wrapper over {@link readStageRenderSlot} bound to this runtime's view.
618
+ */
619
+ readRenderSlot(slot, out) {
620
+ return readStageRenderSlot(this.renderView, slot, out);
621
+ }
622
+ /** Drain the wasm event buffer into a fresh array of typed events. */
623
+ drainEvents() {
624
+ const count = this.exports.zylem_stage_event_count() >>> 0;
625
+ if (count === 0) return [];
626
+ const view = this.eventView;
627
+ const events = new Array(count);
628
+ for (let i = 0; i < count; i++) {
629
+ const base = i * STAGE_EVENT_STRIDE;
630
+ const primaryRaw = view[base + 1];
631
+ const secondaryRaw = view[base + 2];
632
+ events[i] = {
633
+ type: view[base] | 0,
634
+ primarySlot: Number.isNaN(primaryRaw) ? null : primaryRaw >>> 0,
635
+ secondarySlot: Number.isNaN(secondaryRaw) ? null : secondaryRaw >>> 0,
636
+ payload: [view[base + 3], view[base + 4], view[base + 5]]
637
+ };
638
+ }
639
+ return events;
640
+ }
641
+ // ─── Batched input / snapshot buffers ─────────────────────────────
642
+ /** Writable view over the slot-indexed input buffer ({@link STAGE_INPUT_STRIDE} floats/slot). */
643
+ get input() {
644
+ return this.inputView;
645
+ }
646
+ get inputStride() {
647
+ return STAGE_INPUT_STRIDE;
648
+ }
649
+ /** Read-only view over the per-slot behavior snapshot buffer ({@link STAGE_SNAPSHOT_STRIDE} floats/slot). */
650
+ get snapshot() {
651
+ return this.snapshotView;
652
+ }
653
+ get snapshotStride() {
654
+ return STAGE_SNAPSHOT_STRIDE;
655
+ }
656
+ /**
657
+ * Write the primary movement axes for a slot into the slot-indexed input
658
+ * buffer and mark the slot dirty. Field-scoped (doesn't touch look
659
+ * deltas or buttons) so multiple behaviors composed on one entity can
660
+ * each write their part.
661
+ */
662
+ writeInputAxes(slot, axisA, axisB) {
663
+ const base = slot * STAGE_INPUT_STRIDE;
664
+ const view = this.inputView;
665
+ if (slot < 0 || base + STAGE_INPUT_STRIDE > view.length) return false;
666
+ view[base] = axisA;
667
+ view[base + 1] = axisB;
668
+ view[base + 7] = 1;
669
+ return true;
670
+ }
671
+ /** Write the look deltas (first-person yaw/pitch) for a slot. */
672
+ writeInputLook(slot, yawDelta, pitchDelta) {
673
+ const base = slot * STAGE_INPUT_STRIDE;
674
+ const view = this.inputView;
675
+ if (slot < 0 || base + STAGE_INPUT_STRIDE > view.length) return false;
676
+ view[base + 2] = yawDelta;
677
+ view[base + 3] = pitchDelta;
678
+ view[base + 7] = 1;
679
+ return true;
680
+ }
681
+ /** Write reserved input slots (thruster world-space vector X/Y). */
682
+ writeInputReserved(slot, reservedA, reservedB) {
683
+ const base = slot * STAGE_INPUT_STRIDE;
684
+ const view = this.inputView;
685
+ if (slot < 0 || base + STAGE_INPUT_STRIDE > view.length) return false;
686
+ view[base + 5] = reservedA;
687
+ view[base + 6] = reservedB;
688
+ view[base + 7] = 1;
689
+ return true;
690
+ }
691
+ /**
692
+ * Set or clear one button bit ({@link STAGE_INPUT_BUTTON_JUMP} /
693
+ * {@link STAGE_INPUT_BUTTON_RUN}) in a slot's input bitmask.
694
+ */
695
+ writeInputButton(slot, buttonMask, pressed) {
696
+ const base = slot * STAGE_INPUT_STRIDE;
697
+ const view = this.inputView;
698
+ if (slot < 0 || base + STAGE_INPUT_STRIDE > view.length) return false;
699
+ const current = view[base + 4] | 0;
700
+ view[base + 4] = pressed ? current | buttonMask : current & ~buttonMask;
701
+ view[base + 7] = 1;
702
+ return true;
703
+ }
704
+ /**
705
+ * Decode a slot's behavior snapshot from the buffer written at the end of
706
+ * the previous step. Returns `null` when the slot has no behavior data.
707
+ */
708
+ readSnapshot(slot, out) {
709
+ const base = slot * STAGE_SNAPSHOT_STRIDE;
710
+ const view = this.snapshotView;
711
+ if (slot < 0 || base + STAGE_SNAPSHOT_STRIDE > view.length) return null;
712
+ if (view[base + 7] === 0) return null;
713
+ const result = out ?? {
714
+ grounded: false,
715
+ fsmState: 0,
716
+ jumpCount: 0,
717
+ verticalVelocity: 0,
718
+ speed: 0,
719
+ yaw: 0,
720
+ pitch: 0
721
+ };
722
+ result.grounded = view[base] > 0.5;
723
+ result.fsmState = view[base + 1] | 0;
724
+ result.jumpCount = view[base + 2] | 0;
725
+ result.verticalVelocity = view[base + 3];
726
+ result.speed = view[base + 4];
727
+ result.yaw = view[base + 5];
728
+ result.pitch = view[base + 6];
729
+ return result;
730
+ }
731
+ // ─── Behavior helpers ─────────────────────────────────────────────
732
+ get queryScratch() {
733
+ return this.queryView;
734
+ }
735
+ attachCooldown(slot) {
736
+ assertRuntimeBehavior(this.compatibility, "cooldown");
737
+ return this.exports.zylem_stage_attach_cooldown(slot) !== 0;
738
+ }
739
+ registerCooldown(slot, id, durationSeconds, immediate = false) {
740
+ return this.exports.zylem_stage_cooldown_register(slot, id >>> 0, durationSeconds, immediate ? 1 : 0) !== 0;
741
+ }
742
+ fireCooldown(slot, id) {
743
+ return this.exports.zylem_stage_cooldown_fire(slot, id >>> 0) !== 0;
744
+ }
745
+ resetCooldown(slot, id) {
746
+ return this.exports.zylem_stage_cooldown_reset(slot, id >>> 0) !== 0;
747
+ }
748
+ cooldownRemaining(slot, id) {
749
+ return this.exports.zylem_stage_cooldown_remaining(slot, id >>> 0);
750
+ }
751
+ attachWorldBoundary(slot, dim, bounds) {
752
+ assertRuntimeBehavior(this.compatibility, "world-boundary");
753
+ return this.exports.zylem_stage_attach_world_boundary(
754
+ slot,
755
+ dim,
756
+ bounds.top,
757
+ bounds.bottom,
758
+ bounds.left,
759
+ bounds.right,
760
+ bounds.front ?? 0,
761
+ bounds.back ?? 0,
762
+ bounds.padding ?? 0
763
+ ) !== 0;
764
+ }
765
+ queryWorldBoundary(slot) {
766
+ if (this.exports.zylem_stage_query_world_boundary(slot) === 0) return null;
767
+ const v = this.queryView;
768
+ return {
769
+ hits: v[0] | 0,
770
+ clamped: [v[1], v[2], v[3]]
771
+ };
772
+ }
773
+ attachScreenWrap(slot, opts) {
774
+ assertRuntimeBehavior(this.compatibility, "screen-wrap");
775
+ return this.exports.zylem_stage_attach_screen_wrap(
776
+ slot,
777
+ opts.width,
778
+ opts.height,
779
+ opts.centerX ?? 0,
780
+ opts.centerY ?? 0,
781
+ opts.edgeThreshold ?? 0
782
+ ) !== 0;
783
+ }
784
+ queryScreenWrap(slot) {
785
+ if (this.exports.zylem_stage_query_screen_wrap(slot) === 0) return null;
786
+ const v = this.queryView;
787
+ return { fsm: v[0] | 0, lastWrappedAxes: v[1] | 0 };
788
+ }
789
+ attachRicochet(slot, dim, opts) {
790
+ assertRuntimeBehavior(this.compatibility, "ricochet");
791
+ return this.exports.zylem_stage_attach_ricochet(
792
+ slot,
793
+ dim,
794
+ opts.minSpeed,
795
+ opts.maxSpeed,
796
+ opts.speedMultiplier,
797
+ opts.maxAngleDeg,
798
+ opts.reflectionMode
799
+ ) !== 0;
800
+ }
801
+ queryRicochet(slot) {
802
+ if (this.exports.zylem_stage_query_ricochet(slot) === 0) return null;
803
+ const v = this.queryView;
804
+ return { bounces: v[0] | 0, lastKind: v[1] | 0 };
805
+ }
806
+ attachThruster(slot, opts) {
807
+ assertRuntimeBehavior(this.compatibility, "thruster");
808
+ return this.exports.zylem_stage_attach_thruster(
809
+ slot,
810
+ opts.maxSpeed,
811
+ opts.acceleration,
812
+ opts.turnRateRadPerSec,
813
+ opts.linearDamping
814
+ ) !== 0;
815
+ }
816
+ setThrusterInput(slot, thrust, turn, vectorX = 0, vectorY = 0) {
817
+ this.exports.zylem_stage_set_thruster_input(slot, thrust, turn, vectorX, vectorY);
818
+ }
819
+ queryThruster(slot) {
820
+ if (this.exports.zylem_stage_query_thruster(slot) === 0) return null;
821
+ const v = this.queryView;
822
+ return { headingRad: v[0], speed: v[1] };
823
+ }
824
+ attachRhythm(slot, abilities, options = {}) {
825
+ let mask = 0;
826
+ for (const ability of abilities) {
827
+ const i = RHYTHM_ABILITIES.indexOf(ability);
828
+ if (i < 0) return false;
829
+ assertRuntimeBehavior(this.compatibility, ability);
830
+ mask |= 1 << i;
831
+ }
832
+ const data = rhythmData(options);
833
+ if (!mask || data.some((x) => !Number.isFinite(x))) return false;
834
+ this.refreshEnvironmentViews();
835
+ this.queryView.set(data);
836
+ const ok = this.exports.zylem_stage_attach_rhythm?.(slot, mask) === 1;
837
+ this.refreshEnvironmentViews();
838
+ return ok;
839
+ }
840
+ detachRhythm(slot) {
841
+ const ok = this.exports.zylem_stage_detach_rhythm?.(slot) === 1;
842
+ this.refreshEnvironmentViews();
843
+ return ok;
844
+ }
845
+ rhythmAction(slot, action) {
846
+ const encoded = rhythmOperation(action);
847
+ if (!encoded) return false;
848
+ const { op, data } = encoded;
849
+ const index = Math.floor(op / 4);
850
+ if (op < 40)
851
+ assertRuntimeBehavior(this.compatibility, RHYTHM_ABILITIES[index]);
852
+ const target = 4294967295;
853
+ if (!Number.isInteger(action.requestId) || action.requestId < 1 || action.requestId > 16777215 || !Number.isInteger(target) || target < 0 || target > 4294967295 || data.length > 16 || data.some((x) => !Number.isFinite(x)))
854
+ return false;
855
+ this.refreshEnvironmentViews();
856
+ this.queryView.fill(0);
857
+ this.queryView.set(data);
858
+ const ok = this.exports.zylem_stage_rhythm_action?.(
859
+ slot,
860
+ target,
861
+ op,
862
+ action.requestId
863
+ ) === 1;
864
+ this.refreshEnvironmentViews();
865
+ return ok;
866
+ }
867
+ queryRhythm(slot) {
868
+ const page = (n) => {
869
+ if (this.exports.zylem_stage_query_rhythm?.(slot, n) !== 1)
870
+ return null;
871
+ this.refreshEnvironmentViews();
872
+ return Array.from(this.queryView);
873
+ };
874
+ const a = page(0), b = page(1);
875
+ if (!a || !b) return null;
876
+ const notes = [];
877
+ for (let i = 0; i < a[9]; i++) {
878
+ const n = page(i + 2);
879
+ if (!n) return null;
880
+ notes.push({
881
+ kind: n[0],
882
+ timestamp: n[1],
883
+ end: n[2],
884
+ lanes: n[3],
885
+ scene: n[4],
886
+ eligible: !!n[5],
887
+ phase: n[6],
888
+ result: n[7],
889
+ headResult: n[8],
890
+ offset: n[9],
891
+ releaseOffset: n[10],
892
+ ticks: n[11],
893
+ cue: n[12],
894
+ responseCue: !!n[13]
895
+ });
896
+ }
897
+ return {
898
+ abilities: RHYTHM_ABILITIES.filter((_, i) => !!(a[0] & 1 << i)),
899
+ lastRequest: a[1],
900
+ revision: a[2],
901
+ clock: a[3],
902
+ started: !!a[4],
903
+ paused: !!a[5],
904
+ score: a[6],
905
+ hits: a[7],
906
+ misses: a[8],
907
+ scene: a[10],
908
+ held: a[11],
909
+ chain: !!a[12],
910
+ meter: a[13],
911
+ powerUntil: a[14],
912
+ failed: !!a[15],
913
+ performance: b[0],
914
+ transitions: b[1],
915
+ cues: b[2],
916
+ tempoSegments: b[3],
917
+ settledPhrases: b[4],
918
+ callPhase: b[5],
919
+ chartCursor: b[6],
920
+ patternResult: b[7],
921
+ notes
922
+ };
923
+ }
924
+ attachMatching(slot, abilities, options = {}) {
925
+ let mask = 0;
926
+ for (const ability of abilities) {
927
+ const i = MATCHING_ABILITIES.indexOf(ability);
928
+ if (i < 0) return false;
929
+ assertRuntimeBehavior(this.compatibility, ability);
930
+ mask |= 1 << i;
931
+ }
932
+ const data = matchingData(options);
933
+ if (!mask || data.some((x) => !Number.isFinite(x))) return false;
934
+ this.refreshEnvironmentViews();
935
+ this.queryView.set(data);
936
+ const ok = this.exports.zylem_stage_attach_matching?.(slot, mask) === 1;
937
+ this.refreshEnvironmentViews();
938
+ return ok;
939
+ }
940
+ detachMatching(slot) {
941
+ const ok = this.exports.zylem_stage_detach_matching?.(slot) === 1;
942
+ this.refreshEnvironmentViews();
943
+ return ok;
944
+ }
945
+ matchingAction(slot, action) {
946
+ const encoded = matchingOperation(action);
947
+ if (!encoded) return false;
948
+ const { op, data } = encoded;
949
+ const index = Math.floor(op / 4);
950
+ if (op < 40)
951
+ assertRuntimeBehavior(
952
+ this.compatibility,
953
+ MATCHING_ABILITIES[index]
954
+ );
955
+ const target = "target" in action ? action.target : 4294967295;
956
+ if (!Number.isInteger(action.requestId) || action.requestId < 1 || action.requestId > 16777215 || !Number.isInteger(target) || target < 0 || target > 4294967295 || data.length > 16 || data.some((x) => !Number.isFinite(x)))
957
+ return false;
958
+ this.refreshEnvironmentViews();
959
+ this.queryView.fill(0);
960
+ this.queryView.set(data);
961
+ const ok = this.exports.zylem_stage_matching_action?.(
962
+ slot,
963
+ target,
964
+ op,
965
+ action.requestId
966
+ ) === 1;
967
+ this.refreshEnvironmentViews();
968
+ return ok;
969
+ }
970
+ queryMatching(slot) {
971
+ const pages = [];
972
+ for (let n = 0; n < 27; n++) {
973
+ if (this.exports.zylem_stage_query_matching?.(slot, n) !== 1)
974
+ return null;
975
+ this.refreshEnvironmentViews();
976
+ pages.push(Array.from(this.queryView));
977
+ }
978
+ const a = pages[0], b = pages[1];
979
+ return {
980
+ abilities: MATCHING_ABILITIES.filter(
981
+ (_, i) => !!(a[0] & 1 << i)
982
+ ),
983
+ lastRequest: a[1],
984
+ revision: a[2],
985
+ clock: a[3],
986
+ width: a[4],
987
+ height: a[5],
988
+ started: !!a[6],
989
+ chain: a[7],
990
+ score: a[8],
991
+ cleared: a[9],
992
+ lastMatch: a[10],
993
+ rows: a[11],
994
+ riseProgress: a[12],
995
+ dangerElapsed: a[13],
996
+ lost: !!a[14],
997
+ generation: a[15],
998
+ flipperConfigured: !!b[0],
999
+ flipperPressed: !!b[1],
1000
+ flipperAngle: b[2],
1001
+ charge: b[3],
1002
+ staged: !!b[4],
1003
+ launches: b[5],
1004
+ bumperHits: b[6],
1005
+ bumperReadyAt: b[7],
1006
+ modePhase: b[8],
1007
+ modeCursor: b[9],
1008
+ modeDeadline: b[10],
1009
+ wins: b[11],
1010
+ patternLength: b[12],
1011
+ pressure: !!b[13],
1012
+ pauseUntil: b[14],
1013
+ chainUntil: b[15],
1014
+ cells: Array.from({ length: 96 }, (_, i) => {
1015
+ const u = pages[2 + Math.floor(i / 4)], j = i % 4 * 4;
1016
+ return {
1017
+ color: u[j],
1018
+ phase: u[j + 1],
1019
+ generation: u[j + 2],
1020
+ garbage: u[j + 3]
1021
+ };
1022
+ }),
1023
+ pattern: pages[26].slice(0, 8),
1024
+ targets: pages[26].slice(8, 16).map(Boolean)
1025
+ };
1026
+ }
1027
+ attachLife(slot, abilities, options = {}) {
1028
+ let mask = 0;
1029
+ for (const ability of abilities) {
1030
+ const i = LIFE_ABILITIES.indexOf(ability);
1031
+ if (i < 0) return false;
1032
+ assertRuntimeBehavior(this.compatibility, ability);
1033
+ mask |= 1 << i;
1034
+ }
1035
+ const data = lifeData(options);
1036
+ if (!mask || data.some((x) => !Number.isFinite(x))) return false;
1037
+ this.refreshEnvironmentViews();
1038
+ this.queryView.set(data);
1039
+ const ok = this.exports.zylem_stage_attach_life?.(slot, mask) === 1;
1040
+ this.refreshEnvironmentViews();
1041
+ return ok;
1042
+ }
1043
+ detachLife(slot) {
1044
+ const ok = this.exports.zylem_stage_detach_life?.(slot) === 1;
1045
+ this.refreshEnvironmentViews();
1046
+ return ok;
1047
+ }
1048
+ lifeAction(slot, action) {
1049
+ const encoded = lifeOperation(action);
1050
+ if (!encoded) return false;
1051
+ const { op, data } = encoded;
1052
+ const index = Math.floor(op / 4);
1053
+ if (op < 40)
1054
+ assertRuntimeBehavior(this.compatibility, LIFE_ABILITIES[index]);
1055
+ const target = "target" in action ? action.target : 4294967295;
1056
+ if (!Number.isInteger(action.requestId) || action.requestId < 1 || action.requestId > 16777215 || !Number.isInteger(target) || target < 0 || target > 4294967295 || data.length > 16 || data.some((x) => !Number.isFinite(x)))
1057
+ return false;
1058
+ this.refreshEnvironmentViews();
1059
+ this.queryView.fill(0);
1060
+ this.queryView.set(data);
1061
+ const ok = this.exports.zylem_stage_life_action?.(
1062
+ slot,
1063
+ target,
1064
+ op,
1065
+ action.requestId
1066
+ ) === 1;
1067
+ this.refreshEnvironmentViews();
1068
+ return ok;
1069
+ }
1070
+ queryLife(slot) {
1071
+ const pages = [];
1072
+ for (let n = 0; n < 74; n++) {
1073
+ if (this.exports.zylem_stage_query_life?.(slot, n) !== 1)
1074
+ return null;
1075
+ this.refreshEnvironmentViews();
1076
+ pages.push(Array.from(this.queryView));
1077
+ }
1078
+ const a = pages[0], b = pages[1];
1079
+ return {
1080
+ abilities: LIFE_ABILITIES.filter((_, i) => !!(a[0] & 1 << i)),
1081
+ lastRequest: a[1],
1082
+ revision: a[2],
1083
+ day: a[3],
1084
+ season: a[4],
1085
+ minute: a[5],
1086
+ rain: a[6],
1087
+ stamina: a[7],
1088
+ fatigue: a[8],
1089
+ route: a[9],
1090
+ activity: a[10],
1091
+ destinationX: a[11],
1092
+ destinationY: a[12],
1093
+ destinationZ: a[13],
1094
+ overrideUntil: a[14],
1095
+ roomScore: a[15],
1096
+ fishPhase: b[0],
1097
+ fishClock: b[1],
1098
+ biteAt: b[2],
1099
+ deadline: b[3],
1100
+ catches: b[4],
1101
+ castX: b[5],
1102
+ castY: b[6],
1103
+ castZ: b[7],
1104
+ fruitSerial: b[8],
1105
+ memoryCount: b[9],
1106
+ roomWidth: b[10],
1107
+ roomHeight: b[11],
1108
+ slept: !!b[12],
1109
+ plots: Array.from({ length: 8 }, (_, i) => {
1110
+ const u = pages[2 + i];
1111
+ return {
1112
+ configured: !!u[0],
1113
+ phase: u[1],
1114
+ stage: u[2],
1115
+ progress: u[3],
1116
+ moisture: u[4],
1117
+ seasons: u[5],
1118
+ harvests: u[6],
1119
+ capacity: u[7],
1120
+ moistureRequired: u[8],
1121
+ regrowDays: u[9],
1122
+ regrowing: !!u[10]
1123
+ };
1124
+ }),
1125
+ animals: Array.from({ length: 8 }, (_, i) => {
1126
+ const u = pages[10 + i];
1127
+ return {
1128
+ configured: !!u[0],
1129
+ health: u[1],
1130
+ affection: u[2],
1131
+ care: u[3],
1132
+ product: u[4],
1133
+ collected: u[5]
1134
+ };
1135
+ }),
1136
+ people: Array.from({ length: 8 }, (_, i) => {
1137
+ const u = pages[18 + i];
1138
+ return {
1139
+ configured: !!u[0],
1140
+ score: u[1],
1141
+ eventSerial: u[2],
1142
+ rewards: u[3]
1143
+ };
1144
+ }),
1145
+ memories: Array.from({ length: 8 }, (_, i) => {
1146
+ const u = pages[26 + i];
1147
+ return {
1148
+ eventSerial: u[0],
1149
+ row: u[1],
1150
+ kind: u[2],
1151
+ delta: u[3],
1152
+ day: u[4]
1153
+ };
1154
+ }),
1155
+ trees: Array.from({ length: 8 }, (_, i) => {
1156
+ const u = pages[34 + i];
1157
+ return {
1158
+ configured: !!u[0],
1159
+ fruit: u[1],
1160
+ capacity: u[2],
1161
+ due: u[3],
1162
+ receipt: u[4],
1163
+ pending: !!u[5],
1164
+ bound: !!u[6],
1165
+ x: u[7],
1166
+ y: u[8],
1167
+ z: u[9],
1168
+ regrowDays: u[10]
1169
+ };
1170
+ }),
1171
+ furniture: Array.from({ length: 8 }, (_, i) => {
1172
+ const u = pages[42 + i];
1173
+ return {
1174
+ configured: !!u[0],
1175
+ placed: !!u[1],
1176
+ width: u[2],
1177
+ height: u[3],
1178
+ x: u[4],
1179
+ y: u[5],
1180
+ rotation: u[6],
1181
+ score: u[7],
1182
+ style: u[8]
1183
+ };
1184
+ }),
1185
+ schedules: Array.from({ length: 8 }, (_, i) => {
1186
+ const u = pages[50 + i];
1187
+ return {
1188
+ configured: !!u[0],
1189
+ start: u[1],
1190
+ end: u[2],
1191
+ weekdays: u[3],
1192
+ seasons: u[4],
1193
+ activity: u[5],
1194
+ x: u[6],
1195
+ y: u[7],
1196
+ z: u[8],
1197
+ priority: u[9]
1198
+ };
1199
+ }),
1200
+ blocked: pages.slice(58, 74).flat().map(Boolean)
1201
+ };
1202
+ }
1203
+ attachDefense(slot, abilities, options = {}) {
1204
+ let mask = 0;
1205
+ for (const ability of abilities) {
1206
+ const i = DEFENSE_ABILITIES.indexOf(ability);
1207
+ if (i < 0) return false;
1208
+ assertRuntimeBehavior(this.compatibility, ability);
1209
+ mask |= 1 << i;
1210
+ }
1211
+ const data = defenseData(options);
1212
+ if (!mask || data.some((x) => !Number.isFinite(x))) return false;
1213
+ this.refreshEnvironmentViews();
1214
+ this.queryView.set(data);
1215
+ const ok = this.exports.zylem_stage_attach_defense?.(slot, mask) === 1;
1216
+ this.refreshEnvironmentViews();
1217
+ return ok;
1218
+ }
1219
+ detachDefense(slot) {
1220
+ const ok = this.exports.zylem_stage_detach_defense?.(slot) === 1;
1221
+ this.refreshEnvironmentViews();
1222
+ return ok;
1223
+ }
1224
+ defenseAction(slot, action) {
1225
+ const encoded = defenseOperation(action);
1226
+ if (!encoded) return false;
1227
+ const { op, data } = encoded;
1228
+ const index = Math.floor(op / 4);
1229
+ if (op < 40)
1230
+ assertRuntimeBehavior(
1231
+ this.compatibility,
1232
+ DEFENSE_ABILITIES[index]
1233
+ );
1234
+ const target = "target" in action ? action.target : 4294967295;
1235
+ if (!Number.isInteger(action.requestId) || action.requestId < 1 || action.requestId > 16777215 || !Number.isInteger(target) || target < 0 || target > 4294967295 || data.length > 16 || data.some((x) => !Number.isFinite(x)))
1236
+ return false;
1237
+ this.refreshEnvironmentViews();
1238
+ this.queryView.fill(0);
1239
+ this.queryView.set(data);
1240
+ const ok = this.exports.zylem_stage_defense_action?.(
1241
+ slot,
1242
+ target,
1243
+ op,
1244
+ action.requestId
1245
+ ) === 1;
1246
+ this.refreshEnvironmentViews();
1247
+ return ok;
1248
+ }
1249
+ queryDefense(slot) {
1250
+ const pages = [];
1251
+ for (let n = 0; n < 37; n++) {
1252
+ if (this.exports.zylem_stage_query_defense?.(slot, n) !== 1)
1253
+ return null;
1254
+ this.refreshEnvironmentViews();
1255
+ pages.push(Array.from(this.queryView));
1256
+ }
1257
+ const a = pages[0], b = pages[1], c = pages[2];
1258
+ return {
1259
+ abilities: DEFENSE_ABILITIES.filter((_, i) => !!(a[0] & 1 << i)),
1260
+ lastRequest: a[1],
1261
+ revision: a[2],
1262
+ bank: a[3],
1263
+ health: a[4],
1264
+ direction: a[5],
1265
+ speed: a[6],
1266
+ blocked: a[7],
1267
+ selected: a[8],
1268
+ shots: a[9],
1269
+ chewed: a[10],
1270
+ bites: a[11],
1271
+ breaks: a[12],
1272
+ damageSerial: a[13],
1273
+ firing: !!a[14],
1274
+ advancing: !!a[15],
1275
+ minePhase: b[0],
1276
+ mineRemaining: b[1],
1277
+ detonations: b[2],
1278
+ producing: !!b[3],
1279
+ productionClock: b[4],
1280
+ resourceSerial: b[5],
1281
+ collected: b[6],
1282
+ wavePhase: b[7],
1283
+ waveSerial: b[8],
1284
+ budget: b[9],
1285
+ spawnSerial: b[10],
1286
+ spawnCount: b[11],
1287
+ deploymentSerial: b[12],
1288
+ sweepPhase: b[13],
1289
+ sweepHits: b[14],
1290
+ outstandingResources: b[15],
1291
+ x: c[0],
1292
+ y: c[1],
1293
+ z: c[2],
1294
+ vaultUsed: !!c[3],
1295
+ vaultPhase: c[4],
1296
+ vaultTime: c[5],
1297
+ vaultDuration: c[6],
1298
+ burrowPhase: c[7],
1299
+ burrowGoal: c[8],
1300
+ emergeRemaining: c[9],
1301
+ sweepEnd: c[10],
1302
+ activeSpawns: c[11],
1303
+ shotClock: c[12],
1304
+ chewClock: c[13],
1305
+ waveClock: c[14],
1306
+ creditSerial: c[15],
1307
+ targets: Array.from({ length: 8 }, (_, i) => {
1308
+ const u = pages[3 + i];
1309
+ return {
1310
+ present: !!u[0],
1311
+ kind: u[1],
1312
+ lane: u[2],
1313
+ health: u[3],
1314
+ flags: u[4],
1315
+ x: u[5],
1316
+ y: u[6],
1317
+ z: u[7]
1318
+ };
1319
+ }),
1320
+ resources: Array.from({ length: 8 }, (_, i) => {
1321
+ const u = pages[11 + i];
1322
+ return {
1323
+ receipt: u[0],
1324
+ phase: u[1],
1325
+ value: u[2],
1326
+ lane: u[3],
1327
+ bound: !!u[4]
1328
+ };
1329
+ }),
1330
+ spawns: Array.from({ length: 8 }, (_, i) => {
1331
+ const u = pages[19 + i];
1332
+ return {
1333
+ receipt: u[0],
1334
+ phase: u[1],
1335
+ value: u[2],
1336
+ lane: u[3],
1337
+ bound: !!u[4]
1338
+ };
1339
+ }),
1340
+ tiles: Array.from({ length: 8 }, (_, i) => {
1341
+ const u = pages[27 + i];
1342
+ return {
1343
+ configured: !!u[0],
1344
+ terrainMask: u[1],
1345
+ x: u[2],
1346
+ y: u[3],
1347
+ z: u[4],
1348
+ phase: u[5],
1349
+ receipt: u[6],
1350
+ card: u[7],
1351
+ cost: u[8],
1352
+ bound: !!u[9]
1353
+ };
1354
+ }),
1355
+ cards: Array.from({ length: 4 }, (_, i) => ({
1356
+ configured: !!pages[35][i * 4],
1357
+ cost: pages[35][i * 4 + 1],
1358
+ remaining: pages[35][i * 4 + 2],
1359
+ terrainMask: pages[35][i * 4 + 3]
1360
+ })),
1361
+ armor: Array.from({ length: 3 }, (_, i) => ({
1362
+ health: pages[36][i * 2],
1363
+ damageMask: pages[36][i * 2 + 1]
1364
+ }))
1365
+ };
1366
+ }
1367
+ attachSquad(slot, abilities, options = {}) {
1368
+ let mask = 0;
1369
+ for (const ability of abilities) {
1370
+ const i = SQUAD_ABILITIES.indexOf(ability);
1371
+ if (i < 0) return false;
1372
+ assertRuntimeBehavior(this.compatibility, ability);
1373
+ mask |= 1 << i;
1374
+ }
1375
+ const data = squadData(options);
1376
+ if (!mask || data.some((x) => !Number.isFinite(x))) return false;
1377
+ this.refreshEnvironmentViews();
1378
+ this.queryView.set(data);
1379
+ const ok = this.exports.zylem_stage_attach_squad?.(slot, mask) === 1;
1380
+ this.refreshEnvironmentViews();
1381
+ return ok;
1382
+ }
1383
+ detachSquad(slot) {
1384
+ const ok = this.exports.zylem_stage_detach_squad?.(slot) === 1;
1385
+ this.refreshEnvironmentViews();
1386
+ return ok;
1387
+ }
1388
+ squadAction(slot, action) {
1389
+ const encoded = squadOperation(action);
1390
+ if (!encoded) return false;
1391
+ const { op, data } = encoded;
1392
+ const index = Math.floor(op / 4);
1393
+ if (op < 40)
1394
+ assertRuntimeBehavior(this.compatibility, SQUAD_ABILITIES[index]);
1395
+ const target = "target" in action ? action.target : 4294967295;
1396
+ if (!Number.isInteger(action.requestId) || action.requestId < 1 || action.requestId > 16777215 || !Number.isInteger(target) || target < 0 || target > 4294967295 || data.length > 16 || data.some((x) => !Number.isFinite(x)))
1397
+ return false;
1398
+ this.refreshEnvironmentViews();
1399
+ this.queryView.fill(0);
1400
+ this.queryView.set(data);
1401
+ const ok = this.exports.zylem_stage_squad_action?.(
1402
+ slot,
1403
+ target,
1404
+ op,
1405
+ action.requestId
1406
+ ) === 1;
1407
+ this.refreshEnvironmentViews();
1408
+ return ok;
1409
+ }
1410
+ querySquad(slot) {
1411
+ const pages = [];
1412
+ for (let n = 0; n < 28; n++) {
1413
+ if (this.exports.zylem_stage_query_squad?.(slot, n) !== 1)
1414
+ return null;
1415
+ this.refreshEnvironmentViews();
1416
+ pages.push(Array.from(this.queryView));
1417
+ }
1418
+ const a = pages[0], b = pages[1];
1419
+ return {
1420
+ abilities: SQUAD_ABILITIES.filter((_, i) => !!(a[0] & 1 << i)),
1421
+ lastRequest: a[1],
1422
+ revision: a[2],
1423
+ whistling: !!a[3],
1424
+ radius: a[4],
1425
+ maximumRadius: a[5],
1426
+ recalls: a[6],
1427
+ flightSerial: a[7],
1428
+ completed: a[8],
1429
+ hitSerial: a[9],
1430
+ lossSerial: a[10],
1431
+ deliverySerial: a[11],
1432
+ spawnSerial: a[12],
1433
+ daySerial: a[13],
1434
+ dayPhase: a[14],
1435
+ dayRemaining: a[15],
1436
+ dayBase: b[0],
1437
+ safeRadius: b[1],
1438
+ safe: b[2],
1439
+ lost: b[3],
1440
+ blocked: b[4],
1441
+ workers: Array.from({ length: 8 }, (_, i) => {
1442
+ const u = pages[2 + i], p = pages[10 + i];
1443
+ return {
1444
+ present: !!u[0],
1445
+ mode: u[1],
1446
+ species: u[2],
1447
+ growth: u[3],
1448
+ task: u[4],
1449
+ speed: u[5],
1450
+ workRate: u[6],
1451
+ strength: u[7],
1452
+ damage: u[8],
1453
+ recallable: !!u[9],
1454
+ protected: !!u[10],
1455
+ hazard: u[11],
1456
+ panicRemaining: u[12],
1457
+ flightResult: u[13],
1458
+ growthReceipt: u[14],
1459
+ lossReceipt: u[15],
1460
+ x: p[0],
1461
+ y: p[1],
1462
+ z: p[2],
1463
+ goalX: p[3],
1464
+ goalY: p[4],
1465
+ goalZ: p[5],
1466
+ slotX: p[6],
1467
+ slotY: p[7],
1468
+ slotZ: p[8],
1469
+ flightTime: p[9],
1470
+ flightDuration: p[10],
1471
+ attackClock: p[11],
1472
+ dealt: p[12],
1473
+ landedTask: p[13]
1474
+ };
1475
+ }),
1476
+ tasks: Array.from({ length: 4 }, (_, i) => {
1477
+ const u = pages[18 + i], p = pages[22 + i];
1478
+ return {
1479
+ present: !!u[0],
1480
+ kind: u[1],
1481
+ phase: u[2],
1482
+ required: u[3],
1483
+ progress: u[4],
1484
+ capacity: u[5],
1485
+ value: u[6],
1486
+ health: u[7],
1487
+ stored: u[8],
1488
+ active: u[9],
1489
+ populationCapacity: u[10],
1490
+ activeLimit: u[11],
1491
+ receipt: u[12],
1492
+ assigned: u[13],
1493
+ strength: u[14],
1494
+ speed: u[15],
1495
+ x: p[0],
1496
+ y: p[1],
1497
+ z: p[2],
1498
+ destinationX: p[3],
1499
+ destinationY: p[4],
1500
+ destinationZ: p[5],
1501
+ routed: !!p[6]
1502
+ };
1503
+ }),
1504
+ growth: Array.from({ length: 3 }, (_, i) => ({
1505
+ speed: pages[26][i * 2],
1506
+ work: pages[26][i * 2 + 1]
1507
+ })),
1508
+ affinities: Array.from({ length: 4 }, (_, i) => ({
1509
+ immuneMask: pages[27][i * 3],
1510
+ rescueMask: pages[27][i * 3 + 1],
1511
+ panicDuration: pages[27][i * 3 + 2]
1512
+ }))
1513
+ };
1514
+ }
1515
+ attachTactics(slot, abilities, options = {}) {
1516
+ let mask = 0;
1517
+ for (const ability of abilities) {
1518
+ const i = TACTICS_ABILITIES.indexOf(ability);
1519
+ if (i < 0) return false;
1520
+ assertRuntimeBehavior(this.compatibility, ability);
1521
+ mask |= 1 << i;
1522
+ }
1523
+ const data = tacticsData(options);
1524
+ if (!mask || data.some((x) => !Number.isFinite(x))) return false;
1525
+ this.refreshEnvironmentViews();
1526
+ this.queryView.set(data);
1527
+ const ok = this.exports.zylem_stage_attach_tactics?.(slot, mask) === 1;
1528
+ this.refreshEnvironmentViews();
1529
+ return ok;
1530
+ }
1531
+ detachTactics(slot) {
1532
+ const ok = this.exports.zylem_stage_detach_tactics?.(slot) === 1;
1533
+ this.refreshEnvironmentViews();
1534
+ return ok;
1535
+ }
1536
+ tacticsAction(slot, action) {
1537
+ const encoded = tacticsOperation(action);
1538
+ if (!encoded) return false;
1539
+ const { op, data } = encoded;
1540
+ const index = Math.floor(op / 4);
1541
+ if (op < 40)
1542
+ assertRuntimeBehavior(
1543
+ this.compatibility,
1544
+ TACTICS_ABILITIES[index]
1545
+ );
1546
+ const target = "target" in action ? action.target : 4294967295;
1547
+ if (!Number.isInteger(action.requestId) || action.requestId < 1 || action.requestId > 16777215 || !Number.isInteger(target) || target < 0 || target > 4294967295 || data.length > 16 || data.some((x) => !Number.isFinite(x)))
1548
+ return false;
1549
+ this.refreshEnvironmentViews();
1550
+ this.queryView.fill(0);
1551
+ this.queryView.set(data);
1552
+ const ok = this.exports.zylem_stage_tactics_action?.(
1553
+ slot,
1554
+ target,
1555
+ op,
1556
+ action.requestId
1557
+ ) === 1;
1558
+ this.refreshEnvironmentViews();
1559
+ return ok;
1560
+ }
1561
+ queryTactics(slot) {
1562
+ const pages = [];
1563
+ for (let n = 0; n < 34; n++) {
1564
+ if (this.exports.zylem_stage_query_tactics?.(slot, n) !== 1)
1565
+ return null;
1566
+ this.refreshEnvironmentViews();
1567
+ pages.push(Array.from(this.queryView));
1568
+ }
1569
+ const a = pages[0], b = pages[1];
1570
+ return {
1571
+ abilities: TACTICS_ABILITIES.filter((_, i) => !!(a[0] & 1 << i)),
1572
+ lastRequest: a[1],
1573
+ revision: a[2],
1574
+ width: a[3],
1575
+ height: a[4],
1576
+ phase: a[5],
1577
+ team: a[6],
1578
+ movementUnit: a[7],
1579
+ rangeUnit: a[8],
1580
+ rangeAllowed: !!a[9],
1581
+ rangeDistance: a[10],
1582
+ terrainDefense: a[11],
1583
+ terrainAvoid: a[12],
1584
+ healed: a[13],
1585
+ previewDamage: a[14],
1586
+ lastCost: a[15],
1587
+ combatSerial: b[0],
1588
+ combatPhase: b[1],
1589
+ cursor: b[2],
1590
+ strikeCount: b[3],
1591
+ lastAttacker: b[4],
1592
+ lastDefender: b[5],
1593
+ damage: b[6],
1594
+ hit: !!b[7],
1595
+ lossSerial: b[8],
1596
+ captureSerial: b[9],
1597
+ supplySerial: b[10],
1598
+ transportSerial: b[11],
1599
+ advantageDamage: b[12],
1600
+ advantageHit: b[13],
1601
+ powerSerial: b[14],
1602
+ units: Array.from({ length: 8 }, (_, i) => {
1603
+ const u = [...pages[2 + i], ...pages[10 + i]];
1604
+ return {
1605
+ present: !!u[0],
1606
+ x: u[1],
1607
+ y: u[2],
1608
+ team: u[3],
1609
+ unitClass: u[4],
1610
+ health: u[5],
1611
+ maximumHealth: u[6],
1612
+ movementRemaining: u[7],
1613
+ attack: u[8],
1614
+ defense: u[9],
1615
+ speed: u[10],
1616
+ weapon: u[11],
1617
+ minimumRange: u[12],
1618
+ maximumRange: u[13],
1619
+ fuel: u[14],
1620
+ ammo: u[15],
1621
+ loss: u[16],
1622
+ lossSerial: u[17],
1623
+ exempt: !!u[18],
1624
+ carrier: u[19],
1625
+ size: u[20],
1626
+ capacity: u[21],
1627
+ cargoMask: u[22],
1628
+ supplyMask: u[23],
1629
+ durability: u[24],
1630
+ acted: !!u[25],
1631
+ spent: u[26],
1632
+ resourceSerial: u[27],
1633
+ healPhase: u[28],
1634
+ maximumFuel: u[29],
1635
+ maximumAmmo: u[30],
1636
+ lineOfSight: !!u[31]
1637
+ };
1638
+ }),
1639
+ structures: Array.from({ length: 8 }, (_, i) => {
1640
+ const u = pages[26 + Math.floor(i / 2)].slice(
1641
+ i % 2 * 8,
1642
+ i % 2 * 8 + 8
1643
+ );
1644
+ return {
1645
+ present: !!u[0],
1646
+ x: u[1],
1647
+ y: u[2],
1648
+ owner: u[3],
1649
+ progress: u[4],
1650
+ maximum: u[5],
1651
+ fuel: u[6],
1652
+ ammo: u[7]
1653
+ };
1654
+ }),
1655
+ powers: Array.from({ length: 4 }, (_, i) => {
1656
+ const u = pages[30 + i];
1657
+ return {
1658
+ charge: u[0],
1659
+ threshold: u[1],
1660
+ duration: u[2],
1661
+ active: !!u[3],
1662
+ remainingTurns: u[4],
1663
+ attack: u[5],
1664
+ defense: u[6],
1665
+ movement: u[7],
1666
+ chargeSerial: u[8],
1667
+ turns: u[9]
1668
+ };
1669
+ }),
1670
+ cells: Array.from({ length: 64 }, (_, i) => {
1671
+ const p = pages[18 + Math.floor(i / 8)], code = p[i % 8];
1672
+ return {
1673
+ x: i % 8,
1674
+ y: Math.floor(i / 8),
1675
+ terrain: code & 7,
1676
+ blocked: !!(code & 8),
1677
+ occupant: code >> 4 & 15,
1678
+ structure: code >> 8 & 15,
1679
+ inRange: !!(code & 4096),
1680
+ cost: p[i % 8 + 8]
1681
+ };
1682
+ }).filter((c) => c.x < a[3] && c.y < a[4])
1683
+ };
1684
+ }
1685
+ attachBattle(slot, abilities, options = {}) {
1686
+ let mask = 0;
1687
+ for (const ability of abilities) {
1688
+ const i = BATTLE_ABILITIES.indexOf(ability);
1689
+ if (i < 0) return false;
1690
+ assertRuntimeBehavior(this.compatibility, ability);
1691
+ mask |= 1 << i;
1692
+ }
1693
+ const data = battleData(options);
1694
+ if (!mask || data.some((x) => !Number.isFinite(x))) return false;
1695
+ this.refreshEnvironmentViews();
1696
+ this.queryView.set(data);
1697
+ const ok = this.exports.zylem_stage_attach_battle?.(slot, mask) === 1;
1698
+ this.refreshEnvironmentViews();
1699
+ return ok;
1700
+ }
1701
+ detachBattle(slot) {
1702
+ const ok = this.exports.zylem_stage_detach_battle?.(slot) === 1;
1703
+ this.refreshEnvironmentViews();
1704
+ return ok;
1705
+ }
1706
+ battleAction(slot, action) {
1707
+ const encoded = battleOperation(action);
1708
+ if (!encoded) return false;
1709
+ const { op, data } = encoded;
1710
+ const index = Math.floor(op / 4);
1711
+ assertRuntimeBehavior(this.compatibility, BATTLE_ABILITIES[index]);
1712
+ const target = "target" in action ? action.target : 4294967295;
1713
+ if (!Number.isInteger(action.requestId) || action.requestId < 1 || action.requestId > 16777215 || !Number.isInteger(target) || target < 0 || target > 4294967295 || data.length > 16 || data.some((x) => !Number.isFinite(x)))
1714
+ return false;
1715
+ this.refreshEnvironmentViews();
1716
+ this.queryView.fill(0);
1717
+ this.queryView.set(data);
1718
+ const ok = this.exports.zylem_stage_battle_action?.(
1719
+ slot,
1720
+ target,
1721
+ op,
1722
+ action.requestId
1723
+ ) === 1;
1724
+ this.refreshEnvironmentViews();
1725
+ return ok;
1726
+ }
1727
+ queryBattle(slot) {
1728
+ const pages = [];
1729
+ for (let n = 0; n < 10; n++) {
1730
+ if (this.exports.zylem_stage_query_battle?.(slot, n) !== 1)
1731
+ return null;
1732
+ this.refreshEnvironmentViews();
1733
+ pages.push(Array.from(this.queryView));
1734
+ }
1735
+ const [a, b, c, d, e, f, g, h, i, j] = pages;
1736
+ const v = (p, i2) => [
1737
+ p[i2],
1738
+ p[i2 + 1],
1739
+ p[i2 + 2]
1740
+ ];
1741
+ const q = (p, i2) => [
1742
+ p[i2],
1743
+ p[i2 + 1],
1744
+ p[i2 + 2],
1745
+ p[i2 + 3]
1746
+ ];
1747
+ return {
1748
+ abilities: BATTLE_ABILITIES.filter((_, i2) => !!(a[0] & 1 << i2)),
1749
+ lastRequest: a[1],
1750
+ revision: a[2],
1751
+ outcome: a[3],
1752
+ points: a[4],
1753
+ budgetTurn: a[5],
1754
+ turn: a[6],
1755
+ frozen: !!a[7],
1756
+ orderCount: a[8],
1757
+ cursor: a[9],
1758
+ lastAction: a[10],
1759
+ canceled: a[11],
1760
+ typeSerial: a[12],
1761
+ multiplier: a[13],
1762
+ damage: a[14],
1763
+ reaction: a[15],
1764
+ hook: b[0],
1765
+ tickAmount: b[1],
1766
+ blocked: !!b[2],
1767
+ captureSerial: b[3],
1768
+ captureResult: b[4],
1769
+ captureChance: b[5],
1770
+ captureRoll: b[6],
1771
+ captureStock: b[7],
1772
+ captureItem: b[8],
1773
+ species: b[9],
1774
+ level: b[10],
1775
+ friendship: b[11],
1776
+ evolutionSerial: b[12],
1777
+ usedItem: b[13],
1778
+ stats: v(c, 0),
1779
+ moves: q(c, 3),
1780
+ moveSerial: c[7],
1781
+ timingPhase: c[8],
1782
+ clock: c[9],
1783
+ inputCount: c[10],
1784
+ inputNext: c[11],
1785
+ hits: c[12],
1786
+ quality: c[13],
1787
+ actionMultiplier: c[14],
1788
+ window: c[15],
1789
+ times: q(d, 0),
1790
+ buttons: q(d, 4),
1791
+ activePartner: d[8],
1792
+ swapSerial: d[9],
1793
+ passiveBudget: d[10],
1794
+ spent: d[11],
1795
+ modifiers: v(d, 12),
1796
+ bonus: d[15],
1797
+ openingPhase: e[0],
1798
+ openingSide: e[1],
1799
+ openingAttack: e[2],
1800
+ openingSerial: e[3],
1801
+ escapeAttempts: e[4],
1802
+ escapeResult: e[5],
1803
+ escapeChance: e[6],
1804
+ escapeRoll: e[7],
1805
+ order: f.slice(0, 8),
1806
+ queued: f.slice(8).map(Boolean),
1807
+ partnerHealth: g.slice(0, 8),
1808
+ partnerMaximum: g.slice(8),
1809
+ passiveCounts: h.slice(0, 8),
1810
+ passiveIds: h.slice(8),
1811
+ statusIds: i.slice(0, 4),
1812
+ statusTurns: i.slice(4, 8),
1813
+ statusAmounts: i.slice(8, 12),
1814
+ statusPhases: i.slice(12),
1815
+ typeMatrix: j
1816
+ };
1817
+ }
1818
+ attachRpg(slot, abilities, options = {}) {
1819
+ let mask = 0;
1820
+ for (const ability of abilities) {
1821
+ const i = RPG_ABILITIES.indexOf(ability);
1822
+ if (i < 0) return false;
1823
+ assertRuntimeBehavior(this.compatibility, ability);
1824
+ mask |= 1 << i;
1825
+ }
1826
+ const data = rpgData(options);
1827
+ if (!mask || data.some((x) => !Number.isFinite(x))) return false;
1828
+ this.refreshEnvironmentViews();
1829
+ this.queryView.set(data);
1830
+ const ok = this.exports.zylem_stage_attach_rpg?.(slot, mask) === 1;
1831
+ this.refreshEnvironmentViews();
1832
+ return ok;
1833
+ }
1834
+ detachRpg(slot) {
1835
+ const ok = this.exports.zylem_stage_detach_rpg?.(slot) === 1;
1836
+ this.refreshEnvironmentViews();
1837
+ return ok;
1838
+ }
1839
+ rpgAction(slot, action) {
1840
+ const encoded = rpgOperation(action);
1841
+ if (!encoded) return false;
1842
+ const { op, data } = encoded;
1843
+ const index = Math.floor(op / 4);
1844
+ assertRuntimeBehavior(this.compatibility, RPG_ABILITIES[index]);
1845
+ const target = "target" in action ? action.target : 4294967295;
1846
+ if (!Number.isInteger(action.requestId) || action.requestId < 1 || action.requestId > 16777215 || !Number.isInteger(target) || target < 0 || target > 4294967295 || data.some((x) => !Number.isFinite(x)))
1847
+ return false;
1848
+ this.refreshEnvironmentViews();
1849
+ this.queryView.fill(0);
1850
+ this.queryView.set(data);
1851
+ const ok = this.exports.zylem_stage_rpg_action?.(
1852
+ slot,
1853
+ target,
1854
+ op,
1855
+ action.requestId
1856
+ ) === 1;
1857
+ this.refreshEnvironmentViews();
1858
+ return ok;
1859
+ }
1860
+ queryRpg(slot) {
1861
+ const pages = [];
1862
+ for (let n = 0; n < 14; n++) {
1863
+ if (this.exports.zylem_stage_query_rpg?.(slot, n) !== 1)
1864
+ return null;
1865
+ this.refreshEnvironmentViews();
1866
+ pages.push(Array.from(this.queryView));
1867
+ }
1868
+ const [a, b, c, d] = pages;
1869
+ const v = (p, i) => [
1870
+ p[i],
1871
+ p[i + 1],
1872
+ p[i + 2]
1873
+ ];
1874
+ const affixes = (p) => Array.from({ length: 4 }, (_, i) => ({
1875
+ id: p[i * 4],
1876
+ stat: p[i * 4 + 1],
1877
+ amount: p[i * 4 + 2],
1878
+ cost: p[i * 4 + 3]
1879
+ })).filter((f) => f.id > 0);
1880
+ return {
1881
+ abilities: RPG_ABILITIES.filter((_, i) => !!(a[0] & 1 << i)),
1882
+ lastRequest: a[1],
1883
+ revision: a[2],
1884
+ base: v(a, 3),
1885
+ derived: v(a, 6),
1886
+ health: a[9],
1887
+ mana: a[10],
1888
+ currency: a[11],
1889
+ repairCost: a[12],
1890
+ beltSerial: a[13],
1891
+ healthGained: a[14],
1892
+ manaGained: a[15],
1893
+ affixSerial: b[0],
1894
+ affixCount: b[1],
1895
+ affixSpent: b[2],
1896
+ affixSeed: b[3],
1897
+ lootSerial: b[4],
1898
+ lootType: b[5],
1899
+ lootRarity: b[6],
1900
+ lootSeed: b[7],
1901
+ dropPhase: b[8],
1902
+ deathSerial: b[9],
1903
+ dropType: b[10],
1904
+ dropRarity: b[11],
1905
+ reservation: b[12],
1906
+ dropAffixCount: b[13],
1907
+ leechSerial: b[14],
1908
+ damageSerial: c[0],
1909
+ damage: c[1],
1910
+ damageType: c[2],
1911
+ damageFlags: c[3],
1912
+ reaction: c[4],
1913
+ familiarPhase: c[5],
1914
+ destination: v(c, 6),
1915
+ actionSerial: c[9],
1916
+ cooldown: c[10],
1917
+ form: c[11],
1918
+ formPhase: c[12],
1919
+ formRadius: c[13],
1920
+ formSpeed: c[14],
1921
+ formSerial: c[15],
1922
+ equipmentSlots: d.slice(0, 4),
1923
+ beltSlots: d.slice(4, 8),
1924
+ resistances: d.slice(8, 12),
1925
+ immunities: d.slice(12, 16).map(Boolean),
1926
+ items: pages.slice(4, 12).map((i) => ({
1927
+ owned: !!i[0],
1928
+ equipment: !!i[1],
1929
+ slotMask: i[2],
1930
+ requiredStrength: i[3],
1931
+ flat: v(i, 4),
1932
+ percent: v(i, 7),
1933
+ durability: i[10],
1934
+ maximumDurability: i[11],
1935
+ count: i[12],
1936
+ kind: i[13],
1937
+ potency: i[14],
1938
+ lastWear: i[15]
1939
+ })),
1940
+ affixes: affixes(pages[12]),
1941
+ dropAffixes: affixes(pages[13])
1942
+ };
1943
+ }
1944
+ attachSports(slot, abilities, options = {}) {
1945
+ let mask = 0;
1946
+ for (const ability of abilities) {
1947
+ const i = SPORTS_ABILITIES.indexOf(ability);
1948
+ if (i < 0) return false;
1949
+ assertRuntimeBehavior(this.compatibility, ability);
1950
+ mask |= 1 << i;
1951
+ }
1952
+ const data = sportsData(options);
1953
+ if (!mask || data.some((x) => !Number.isFinite(x))) return false;
1954
+ this.refreshEnvironmentViews();
1955
+ this.queryView.set(data);
1956
+ const ok = this.exports.zylem_stage_attach_sports?.(slot, mask) === 1;
1957
+ this.refreshEnvironmentViews();
1958
+ return ok;
1959
+ }
1960
+ detachSports(slot) {
1961
+ const ok = this.exports.zylem_stage_detach_sports?.(slot) === 1;
1962
+ this.refreshEnvironmentViews();
1963
+ return ok;
1964
+ }
1965
+ sportsAction(slot, action) {
1966
+ const encoded = sportsOperation(action);
1967
+ if (!encoded) return false;
1968
+ const { op, data } = encoded;
1969
+ const index = Math.floor(op / 4);
1970
+ assertRuntimeBehavior(this.compatibility, SPORTS_ABILITIES[index]);
1971
+ const target = "target" in action ? action.target : 4294967295;
1972
+ if (!Number.isInteger(action.requestId) || action.requestId < 1 || action.requestId > 16777215 || !Number.isInteger(target) || target < 0 || target > 4294967295 || data.some((x) => !Number.isFinite(x)))
1973
+ return false;
1974
+ this.refreshEnvironmentViews();
1975
+ this.queryView.fill(0);
1976
+ this.queryView.set(data);
1977
+ const ok = this.exports.zylem_stage_sports_action?.(
1978
+ slot,
1979
+ target,
1980
+ op,
1981
+ action.requestId
1982
+ ) === 1;
1983
+ this.refreshEnvironmentViews();
1984
+ return ok;
1985
+ }
1986
+ querySports(slot) {
1987
+ const page = (n) => {
1988
+ if (this.exports.zylem_stage_query_sports?.(slot, n) !== 1)
1989
+ return null;
1990
+ this.refreshEnvironmentViews();
1991
+ return Array.from(this.queryView);
1992
+ };
1993
+ const a = page(0), b = page(1), c = page(2), d = page(3);
1994
+ if (!a || !b || !c || !d) return null;
1995
+ const v = (p, i) => [
1996
+ p[i],
1997
+ p[i + 1],
1998
+ p[i + 2]
1999
+ ];
2000
+ return {
2001
+ abilities: SPORTS_ABILITIES.filter((_, i) => !!(a[0] & 1 << i)),
2002
+ lastRequest: a[1],
2003
+ revision: a[2],
2004
+ swingPhase: a[3],
2005
+ swingTime: a[4],
2006
+ power: a[5],
2007
+ directionError: a[6],
2008
+ swingSerial: a[7],
2009
+ shotSerial: a[8],
2010
+ shotVelocity: v(a, 9),
2011
+ shotSpin: v(a, 12),
2012
+ flightPhase: a[15],
2013
+ destination: v(b, 0),
2014
+ velocity: v(b, 3),
2015
+ spin: v(b, 6),
2016
+ collisionSerial: b[9],
2017
+ impactSerial: b[10],
2018
+ impactVelocity: v(b, 11),
2019
+ puttPhase: b[14],
2020
+ bounceEpoch: b[15],
2021
+ racketPhase: c[0],
2022
+ racketTime: c[1],
2023
+ contactQuality: c[2],
2024
+ contactSerial: c[3],
2025
+ contactAge: c[4],
2026
+ placementTime: c[5],
2027
+ placement: v(c, 6),
2028
+ servePhase: c[9],
2029
+ attempt: c[10],
2030
+ server: c[11],
2031
+ netTouched: !!c[12],
2032
+ lets: c[13],
2033
+ rallyPhase: c[14],
2034
+ lastHitter: c[15],
2035
+ bounceCount: d[0],
2036
+ crossedNet: !!d[1],
2037
+ winner: d[2],
2038
+ pointSerial: d[3],
2039
+ pointReason: d[4],
2040
+ supported: !!d[5],
2041
+ wind: v(d, 6),
2042
+ consumedShot: d[9],
2043
+ consumedSwing: d[10],
2044
+ consumedContact: d[11],
2045
+ adjudicatedContact: d[12],
2046
+ adjudicatedBounce: d[13]
2047
+ };
2048
+ }
2049
+ attachSkate(slot, abilities, options = {}) {
2050
+ let mask = 0;
2051
+ for (const ability of abilities) {
2052
+ const i = SKATE_ABILITIES.indexOf(ability);
2053
+ if (i < 0) return false;
2054
+ assertRuntimeBehavior(this.compatibility, ability);
2055
+ mask |= 1 << i;
2056
+ }
2057
+ const data = skateData(options);
2058
+ if (!mask || data.some((x) => !Number.isFinite(x))) return false;
2059
+ this.refreshEnvironmentViews();
2060
+ this.queryView.set(data);
2061
+ const ok = this.exports.zylem_stage_attach_skate?.(slot, mask) === 1;
2062
+ this.refreshEnvironmentViews();
2063
+ return ok;
2064
+ }
2065
+ detachSkate(slot) {
2066
+ const ok = this.exports.zylem_stage_detach_skate?.(slot) === 1;
2067
+ this.refreshEnvironmentViews();
2068
+ return ok;
2069
+ }
2070
+ skateAction(slot, action) {
2071
+ const encoded = skateOperation(action);
2072
+ if (!encoded) return false;
2073
+ const { op, data } = encoded;
2074
+ const index = Math.floor(op / 4);
2075
+ assertRuntimeBehavior(this.compatibility, SKATE_ABILITIES[index]);
2076
+ const target = "target" in action ? action.target : 4294967295;
2077
+ if (!Number.isInteger(action.requestId) || action.requestId < 1 || action.requestId > 16777215 || !Number.isInteger(target) || target < 0 || target > 4294967295 || data.some((x) => !Number.isFinite(x)))
2078
+ return false;
2079
+ this.refreshEnvironmentViews();
2080
+ this.queryView.fill(0);
2081
+ this.queryView.set(data);
2082
+ const ok = this.exports.zylem_stage_skate_action?.(
2083
+ slot,
2084
+ target,
2085
+ op,
2086
+ action.requestId
2087
+ ) === 1;
2088
+ this.refreshEnvironmentViews();
2089
+ return ok;
2090
+ }
2091
+ querySkate(slot) {
2092
+ const page = (n) => {
2093
+ if (this.exports.zylem_stage_query_skate?.(slot, n) !== 1)
2094
+ return null;
2095
+ this.refreshEnvironmentViews();
2096
+ return Array.from(this.queryView);
2097
+ };
2098
+ const a = page(0), b = page(1), c = page(2);
2099
+ if (!a || !b || !c) return null;
2100
+ const phases = [
2101
+ "idle",
2102
+ "active",
2103
+ "ended",
2104
+ "bailed",
2105
+ "blocked"
2106
+ ];
2107
+ return {
2108
+ abilities: SKATE_ABILITIES.filter((_, i) => !!(a[0] & 1 << i)),
2109
+ lastRequest: a[1],
2110
+ revision: a[2],
2111
+ railPhase: phases[a[3]],
2112
+ railCoordinate: a[4],
2113
+ balance: a[5],
2114
+ balanceTime: a[6],
2115
+ wallPhase: phases[a[7]],
2116
+ wallRemaining: a[8],
2117
+ manualPhase: phases[a[9]],
2118
+ destination: [a[10], a[11], a[12]],
2119
+ velocity: [a[13], a[14], a[15]],
2120
+ launchSerial: b[0],
2121
+ launchVelocity: [b[1], b[2], b[3]],
2122
+ flight: b[4],
2123
+ comboOpen: !!b[5],
2124
+ landing: ["none", "landed", "bailed"][b[6]],
2125
+ resolvedFlight: b[7],
2126
+ pendingBase: b[8],
2127
+ trickCount: b[9],
2128
+ totalScore: b[10],
2129
+ lastBank: b[11],
2130
+ bankSerial: b[12],
2131
+ repeatFactor: b[13],
2132
+ repeatCount: b[14],
2133
+ historySize: b[15],
2134
+ grab: c[0],
2135
+ grabHeld: !!c[1],
2136
+ grabTime: c[2],
2137
+ releaseAge: c[3],
2138
+ grabValid: !!c[4],
2139
+ windup: c[5],
2140
+ spinRate: c[6],
2141
+ spinAngle: c[7],
2142
+ energy: c[8],
2143
+ boosting: !!c[9],
2144
+ boostFraction: c[10],
2145
+ touchdownCaptured: !!c[11],
2146
+ wallNormal: [c[12], c[13], c[14]],
2147
+ lipRemaining: c[15]
2148
+ };
2149
+ }
2150
+ attachRacing(slot, abilities, options = {}) {
2151
+ let mask = 0;
2152
+ for (const ability of abilities) {
2153
+ const i = RACING_ABILITIES.indexOf(ability);
2154
+ if (i < 0) return false;
2155
+ assertRuntimeBehavior(this.compatibility, ability);
2156
+ mask |= 1 << i;
2157
+ }
2158
+ const data = racingData(options);
2159
+ if (!mask || data.some((x) => !Number.isFinite(x))) return false;
2160
+ this.refreshEnvironmentViews();
2161
+ this.queryView.set(data);
2162
+ const ok = this.exports.zylem_stage_attach_racing?.(slot, mask) === 1;
2163
+ this.refreshEnvironmentViews();
2164
+ return ok;
2165
+ }
2166
+ detachRacing(slot) {
2167
+ const ok = this.exports.zylem_stage_detach_racing?.(slot) === 1;
2168
+ this.refreshEnvironmentViews();
2169
+ return ok;
2170
+ }
2171
+ racingAction(slot, action) {
2172
+ const encoded = racingOperation(action);
2173
+ if (!encoded) return false;
2174
+ const { op, data } = encoded;
2175
+ const index = Math.floor(op / 4);
2176
+ assertRuntimeBehavior(this.compatibility, RACING_ABILITIES[index]);
2177
+ const target = "target" in action ? action.target : 4294967295;
2178
+ if (!Number.isInteger(action.requestId) || action.requestId < 1 || action.requestId > 16777215 || !Number.isInteger(target) || target < 0 || target > 4294967295 || data.some((x) => !Number.isFinite(x)))
2179
+ return false;
2180
+ this.refreshEnvironmentViews();
2181
+ this.queryView.fill(0);
2182
+ this.queryView.set(data);
2183
+ const ok = this.exports.zylem_stage_racing_action?.(
2184
+ slot,
2185
+ target,
2186
+ op,
2187
+ action.requestId
2188
+ ) === 1;
2189
+ this.refreshEnvironmentViews();
2190
+ return ok;
2191
+ }
2192
+ queryRacing(slot) {
2193
+ const page = (n) => {
2194
+ if (this.exports.zylem_stage_query_racing?.(slot, n) !== 1)
2195
+ return null;
2196
+ this.refreshEnvironmentViews();
2197
+ return Array.from(this.queryView);
2198
+ };
2199
+ const a = page(0), b = page(1), c = page(2);
2200
+ if (!a || !b || !c) return null;
2201
+ return {
2202
+ abilities: RACING_ABILITIES.filter((_, i) => !!(a[0] & 1 << i)),
2203
+ lastRequest: a[1],
2204
+ revision: a[2],
2205
+ driftCharge: a[3],
2206
+ driftTier: a[4],
2207
+ driftBoost: a[5],
2208
+ draftDwell: a[6],
2209
+ draftBoost: a[7],
2210
+ item: a[8],
2211
+ itemRank: a[9],
2212
+ routeIndex: a[10],
2213
+ homing: ["idle", "following", "hit", "arrived", "cancelled"][a[11]],
2214
+ destination: [a[12], a[13], a[14]],
2215
+ randomState: a[15],
2216
+ nextCheckpoint: b[0],
2217
+ lap: b[1],
2218
+ laps: b[2],
2219
+ racing: !!b[3],
2220
+ supportProbes: b[4],
2221
+ hoverAcceleration: [b[5], b[6], b[7]],
2222
+ trackUp: [b[8], b[9], b[10]],
2223
+ energy: b[11],
2224
+ boostFraction: b[12],
2225
+ attackRemaining: b[13],
2226
+ attackCooldown: b[14],
2227
+ attackHits: b[15],
2228
+ integrity: c[0],
2229
+ damage: ["healthy", "smoke", "fire", "destroyed"][c[1]],
2230
+ burnElapsed: c[2],
2231
+ handlingMultiplier: c[3],
2232
+ seatPhase: ["free", "entering", "occupied", "exiting"][c[4]],
2233
+ seat: c[5],
2234
+ seatDestination: [c[6], c[7], c[8]],
2235
+ seatRemaining: c[9],
2236
+ weightCount: c[10],
2237
+ routePoints: c[11],
2238
+ checkpointCount: c[12],
2239
+ drifting: !!c[13],
2240
+ boosting: !!c[14],
2241
+ attackDirection: c[15]
2242
+ };
2243
+ }
2244
+ attachSurvival(slot, abilities, options = {}) {
2245
+ let mask = 0;
2246
+ for (const ability of abilities) {
2247
+ const i = SURVIVAL_ABILITIES.indexOf(ability);
2248
+ if (i < 0) return false;
2249
+ assertRuntimeBehavior(this.compatibility, ability);
2250
+ mask |= 1 << i;
2251
+ }
2252
+ const data = survivalData(options);
2253
+ if (!mask || data.some((x) => !Number.isFinite(x))) return false;
2254
+ this.refreshEnvironmentViews();
2255
+ this.queryView.set(data);
2256
+ const ok = this.exports.zylem_stage_attach_survival?.(slot, mask) === 1;
2257
+ this.refreshEnvironmentViews();
2258
+ return ok;
2259
+ }
2260
+ detachSurvival(slot) {
2261
+ const ok = this.exports.zylem_stage_detach_survival?.(slot) === 1;
2262
+ this.refreshEnvironmentViews();
2263
+ return ok;
2264
+ }
2265
+ survivalAction(slot, action) {
2266
+ const encoded = survivalOperation(action);
2267
+ if (!encoded) return false;
2268
+ const { op, data } = encoded;
2269
+ const index = op <= 2 ? 0 : op <= 4 ? 1 : op <= 6 ? 2 : op <= 8 ? 3 : op <= 10 ? 4 : op <= 13 ? 5 : op <= 16 ? 6 : op <= 19 ? 7 : op <= 23 ? 8 : 9;
2270
+ assertRuntimeBehavior(this.compatibility, SURVIVAL_ABILITIES[index]);
2271
+ const target = "target" in action ? action.target : 4294967295;
2272
+ if (!Number.isInteger(action.requestId) || action.requestId < 1 || action.requestId > 16777215 || !Number.isInteger(target) || target < 0 || target > 4294967295 || data.some((x) => !Number.isFinite(x)))
2273
+ return false;
2274
+ this.refreshEnvironmentViews();
2275
+ this.queryView.fill(0);
2276
+ this.queryView.set(data);
2277
+ const ok = this.exports.zylem_stage_survival_action?.(
2278
+ slot,
2279
+ target,
2280
+ op,
2281
+ action.requestId
2282
+ ) === 1;
2283
+ this.refreshEnvironmentViews();
2284
+ return ok;
2285
+ }
2286
+ querySurvival(slot) {
2287
+ const page = (n) => {
2288
+ if (this.exports.zylem_stage_query_survival?.(slot, n) !== 1)
2289
+ return null;
2290
+ this.refreshEnvironmentViews();
2291
+ return Array.from(this.queryView);
2292
+ };
2293
+ const a = page(0), b = page(1);
2294
+ if (!a || !b) return null;
2295
+ const item = (v) => ({
2296
+ id: v[0],
2297
+ kind: v[1],
2298
+ quantity: v[2],
2299
+ limit: v[3],
2300
+ shape: v[4],
2301
+ x: v[5],
2302
+ y: v[6],
2303
+ rotated: !!v[7]
2304
+ });
2305
+ const items = [];
2306
+ for (let i = 0; i < b[0]; i++) {
2307
+ const v = page(2 + i);
2308
+ if (!v) return null;
2309
+ items.push(item(v));
2310
+ }
2311
+ let checkpoint = null;
2312
+ if (b[7]) {
2313
+ const v = page(34);
2314
+ if (!v) return null;
2315
+ const saved = [];
2316
+ for (let i = 0; i < v[8]; i++) {
2317
+ const q = page(35 + i);
2318
+ if (!q) return null;
2319
+ saved.push(item(q));
2320
+ }
2321
+ checkpoint = {
2322
+ version: 1,
2323
+ checkpoint: v[1],
2324
+ requestId: v[2],
2325
+ revision: v[3],
2326
+ currency: v[4],
2327
+ upgradeLevel: v[5],
2328
+ weaponValue: v[6],
2329
+ pressure: v[7],
2330
+ items: saved
2331
+ };
2332
+ }
2333
+ return {
2334
+ abilities: SURVIVAL_ABILITIES.filter(
2335
+ (_, i) => !!(a[0] & 1 << i)
2336
+ ),
2337
+ lastRequest: a[1],
2338
+ revision: a[2],
2339
+ escortMode: ["wait", "follow", "safe"][a[3]],
2340
+ intent: ["hold", "move", "transport", "vault"][a[4]],
2341
+ destination: [a[5], a[6], a[7]],
2342
+ capture: ["free", "captured", "failed"][a[8]],
2343
+ vault: ["idle", "active", "completed", "cancelled"][a[9]],
2344
+ finisherRemaining: a[10],
2345
+ staggerClass: a[11],
2346
+ finisherConsumed: !!a[12],
2347
+ pressure: a[13],
2348
+ nextPressure: a[14],
2349
+ sampleCount: a[15],
2350
+ items,
2351
+ currency: b[1],
2352
+ upgradeLevel: b[2],
2353
+ weaponValue: b[3],
2354
+ spawnObligations: b[4],
2355
+ unresolvedEnemies: b[5],
2356
+ locked: !!b[6],
2357
+ savePending: !!b[7],
2358
+ lastCheckpoint: b[8],
2359
+ gateCount: b[9],
2360
+ vaultElapsed: b[10],
2361
+ vaultDuration: b[11],
2362
+ recipeCount: b[12],
2363
+ columns: b[13],
2364
+ rows: b[14],
2365
+ saveAllowed: !!b[15],
2366
+ checkpoint
2367
+ };
2368
+ }
2369
+ attachCity(slot, abilities, options = {}) {
2370
+ let mask = 0;
2371
+ for (const ability of abilities) {
2372
+ const i = CITY_ABILITIES.indexOf(ability);
2373
+ if (i < 0) return false;
2374
+ assertRuntimeBehavior(this.compatibility, ability);
2375
+ mask |= 1 << i;
2376
+ }
2377
+ const data = cityData(options);
2378
+ if (!mask || data.some((x) => !Number.isFinite(x))) return false;
2379
+ this.refreshEnvironmentViews();
2380
+ this.queryView.set(data);
2381
+ const ok = this.exports.zylem_stage_attach_city?.(slot, mask) === 1;
2382
+ this.refreshEnvironmentViews();
2383
+ return ok;
2384
+ }
2385
+ detachCity(slot) {
2386
+ const ok = this.exports.zylem_stage_detach_city?.(slot) === 1;
2387
+ this.refreshEnvironmentViews();
2388
+ return ok;
2389
+ }
2390
+ cityData(data) {
2391
+ if (data.length > 8 || data.some((x) => !Number.isFinite(x)))
2392
+ return false;
2393
+ this.refreshEnvironmentViews();
2394
+ this.queryView.fill(0);
2395
+ this.queryView.set(data);
2396
+ return true;
2397
+ }
2398
+ cityLink(slot, other, kind, data) {
2399
+ if (!Number.isInteger(kind) || kind < 0 || kind > 4 || !this.cityData(data))
2400
+ return false;
2401
+ const ok = this.exports.zylem_stage_city_link?.(slot, other, kind) === 1;
2402
+ this.refreshEnvironmentViews();
2403
+ return ok;
2404
+ }
2405
+ cityUnlink(slot, other, kind) {
2406
+ if (!Number.isInteger(kind) || kind < 0 || kind > 4) return false;
2407
+ const ok = this.exports.zylem_stage_city_unlink?.(slot, other, kind) === 1;
2408
+ this.refreshEnvironmentViews();
2409
+ return ok;
2410
+ }
2411
+ cityCommand(slot, kind, data) {
2412
+ if (!Number.isInteger(kind) || kind < 0 || kind > 6 || !this.cityData(data))
2413
+ return false;
2414
+ const ok = this.exports.zylem_stage_city_command?.(slot, kind) === 1;
2415
+ this.refreshEnvironmentViews();
2416
+ return ok;
2417
+ }
2418
+ citySignal(slot, source, kind, sequence, data) {
2419
+ if (!Number.isInteger(kind) || kind < 0 || kind > 4 || !Number.isInteger(sequence) || sequence < 1 || sequence > 16777215 || !this.cityData(data))
2420
+ return false;
2421
+ const ok = this.exports.zylem_stage_city_signal?.(
2422
+ slot,
2423
+ source,
2424
+ kind,
2425
+ sequence
2426
+ ) === 1;
2427
+ this.refreshEnvironmentViews();
2428
+ return ok;
2429
+ }
2430
+ queryCity(slot) {
2431
+ if (this.exports.zylem_stage_query_city?.(slot, 0) !== 1) return null;
2432
+ this.refreshEnvironmentViews();
2433
+ const a = Array.from(this.queryView);
2434
+ if (this.exports.zylem_stage_query_city?.(slot, 1) !== 1) return null;
2435
+ this.refreshEnvironmentViews();
2436
+ const b = this.queryView;
2437
+ return {
2438
+ abilities: CITY_ABILITIES.filter((_, i) => !!(a[0] & 1 << i)),
2439
+ suspicion: a[1],
2440
+ visible: !!a[2],
2441
+ intent: CITY_INTENTS[a[3]],
2442
+ destination: [a[4], a[5], a[6]],
2443
+ disguiseModifier: a[7],
2444
+ searchRemaining: a[8],
2445
+ noiseRemaining: a[9],
2446
+ trailCount: a[10],
2447
+ alert: !!a[11],
2448
+ reportRemaining: a[12],
2449
+ wantedLevel: a[13],
2450
+ pursuitScore: a[14],
2451
+ targetSlot: a[15] < 0 ? null : a[15],
2452
+ responseMask: b[0],
2453
+ unseenFor: b[1],
2454
+ panicRemaining: b[2],
2455
+ reserved: !!b[3],
2456
+ desiredSpeed: b[4],
2457
+ trafficReason: ["clear", "no-request", "red", "blocked", "conflict"][b[5]],
2458
+ trailSequence: b[6],
2459
+ reportsSent: b[7],
2460
+ lastEvent: CITY_EVENTS[b[8]],
2461
+ lastSeen: [b[9], b[10], b[11]],
2462
+ searchIndex: b[12],
2463
+ noiseCategory: ["footstep", "impact", "gunshot"][b[13]],
2464
+ receiptCount: b[14],
2465
+ linkCount: b[15]
2466
+ };
2467
+ }
2468
+ attachEncounters(slot, abilities, options = {}) {
2469
+ let mask = 0;
2470
+ for (const ability of abilities) {
2471
+ const i = ENCOUNTER_ABILITIES.indexOf(ability);
2472
+ if (i < 0) return false;
2473
+ assertRuntimeBehavior(this.compatibility, ability);
2474
+ mask |= 1 << i;
2475
+ }
2476
+ const data = encounterData(options);
2477
+ if (!mask || data.some((x) => !Number.isFinite(x))) return false;
2478
+ this.refreshEnvironmentViews();
2479
+ this.queryView.set(data);
2480
+ const ok = this.exports.zylem_stage_attach_encounters?.(slot, mask) === 1;
2481
+ this.refreshEnvironmentViews();
2482
+ return ok;
2483
+ }
2484
+ detachEncounters(slot) {
2485
+ const ok = this.exports.zylem_stage_detach_encounters?.(slot) === 1;
2486
+ this.refreshEnvironmentViews();
2487
+ return ok;
2488
+ }
2489
+ encounterLink(slot, other, kind, data) {
2490
+ if (!Number.isInteger(kind) || kind < 0 || kind > 5 || data.length > 8 || data.some((x) => !Number.isFinite(x)))
2491
+ return false;
2492
+ this.refreshEnvironmentViews();
2493
+ this.queryView.fill(0);
2494
+ this.queryView.set(data);
2495
+ const ok = this.exports.zylem_stage_encounter_link?.(slot, other, kind) === 1;
2496
+ this.refreshEnvironmentViews();
2497
+ return ok;
2498
+ }
2499
+ encounterUnlink(slot, other, kind) {
2500
+ if (!Number.isInteger(kind) || kind < 0 || kind > 5) return false;
2501
+ const ok = this.exports.zylem_stage_encounter_unlink?.(slot, other, kind) === 1;
2502
+ this.refreshEnvironmentViews();
2503
+ return ok;
2504
+ }
2505
+ encounterCommand(slot, kind, value, data = []) {
2506
+ if (!Number.isInteger(kind) || kind < 0 || kind > 3 || !Number.isInteger(value) || value < 0 || value > 1e4 || data.length > 8 || data.some((x) => !Number.isFinite(x)))
2507
+ return false;
2508
+ this.refreshEnvironmentViews();
2509
+ this.queryView.fill(0);
2510
+ this.queryView.set(data);
2511
+ const ok = this.exports.zylem_stage_encounter_command?.(slot, kind, value) === 1;
2512
+ this.refreshEnvironmentViews();
2513
+ return ok;
2514
+ }
2515
+ encounterHit(slot, source, region, hit) {
2516
+ if (!Number.isFinite(hit.damage) || !Number.isInteger(hit.team) || hit.team < 0 || hit.team > 1e4 || !Number.isInteger(hit.sequence) || hit.sequence < 1 || hit.sequence > 4294967295)
2517
+ return false;
2518
+ const ok = this.exports.zylem_stage_encounter_hit?.(
2519
+ slot,
2520
+ source,
2521
+ region ?? -1,
2522
+ hit.damage,
2523
+ hit.team,
2524
+ hit.sequence,
2525
+ hit.impact ? 1 : 0
2526
+ ) === 1;
2527
+ this.refreshEnvironmentViews();
2528
+ return ok;
2529
+ }
2530
+ queryEncounters(slot) {
2531
+ if (this.exports.zylem_stage_query_encounters?.(slot, 0) !== 1)
2532
+ return null;
2533
+ this.refreshEnvironmentViews();
2534
+ const a = Array.from(this.queryView);
2535
+ if (this.exports.zylem_stage_query_encounters?.(slot, 1) !== 1)
2536
+ return null;
2537
+ this.refreshEnvironmentViews();
2538
+ const b = this.queryView;
2539
+ return {
2540
+ abilities: ENCOUNTER_ABILITIES.filter(
2541
+ (_, i) => !!(a[0] & 1 << i)
2542
+ ),
2543
+ health: a[1],
2544
+ morale: a[2],
2545
+ intent: ENCOUNTER_INTENTS[a[3]],
2546
+ destination: [a[4], a[5], a[6]],
2547
+ targetSlot: a[7] < 0 ? null : a[7],
2548
+ flankSlot: a[8] < 0 ? null : a[8],
2549
+ coverPhase: ["concealed", "observing", "attacking"][a[9]],
2550
+ activation: ["dormant", "activating", "active"][a[10]],
2551
+ bossPhase: a[11],
2552
+ attackMask: a[12],
2553
+ vulnerability: a[13],
2554
+ shotSequence: a[14],
2555
+ terminal: ["live", "intercepted", "impact"][a[15]],
2556
+ burstRemaining: b[0],
2557
+ shotBudget: b[1],
2558
+ activationRemaining: b[2],
2559
+ staggerRemaining: b[3],
2560
+ lastDamage: b[4],
2561
+ lastEvent: ENCOUNTER_EVENTS[b[5]],
2562
+ retiredAttackMask: b[6],
2563
+ attackEpoch: b[7],
2564
+ leaderLost: !!b[8],
2565
+ coverElapsed: b[9],
2566
+ targetScore: b[10]
2567
+ };
2568
+ }
2569
+ attachWeapons(slot, abilities, options = {}) {
2570
+ let mask = 0;
2571
+ for (const a of abilities) {
2572
+ const index = WEAPON_ABILITIES.indexOf(a);
2573
+ if (index < 0) return false;
2574
+ assertRuntimeBehavior(this.compatibility, a);
2575
+ mask |= 1 << index;
2576
+ }
2577
+ const data = weaponData(options), secondary = options.secondaryAmmo ?? 5;
2578
+ if (data.some((x) => !Number.isFinite(x)) || !Number.isInteger(secondary) || secondary < 0 || secondary > 1e4) return false;
2579
+ this.refreshEnvironmentViews();
2580
+ this.queryView.set(data);
2581
+ const ok = this.exports.zylem_stage_attach_weapons?.(slot, mask, secondary) === 1;
2582
+ this.refreshEnvironmentViews();
2583
+ return ok;
2584
+ }
2585
+ detachWeapons(slot) {
2586
+ const ok = this.exports.zylem_stage_detach_weapons?.(slot) === 1;
2587
+ this.refreshEnvironmentViews();
2588
+ return ok;
2589
+ }
2590
+ weaponInput(slot, input) {
2591
+ const ok = this.exports.zylem_stage_weapon_input?.(slot, input.moveX ?? 0, input.moveZ ?? 0, input.turnYaw ?? 0, input.turnPitch ?? 0, weaponButtons(input)) === 1;
2592
+ this.refreshEnvironmentViews();
2593
+ return ok;
2594
+ }
2595
+ weaponMode(slot, index, mode) {
2596
+ if (index !== 0 && index !== 1) return false;
2597
+ const data = weaponModeData(mode);
2598
+ if (data.some((x) => !Number.isFinite(x))) return false;
2599
+ this.refreshEnvironmentViews();
2600
+ this.queryView.set(data);
2601
+ const ok = this.exports.zylem_stage_weapon_mode?.(slot, index) === 1;
2602
+ this.refreshEnvironmentViews();
2603
+ return ok;
2604
+ }
2605
+ weaponDamage(slot, amount, source) {
2606
+ const ok = this.exports.zylem_stage_weapon_damage?.(slot, source ?? -1, amount) === 1;
2607
+ this.refreshEnvironmentViews();
2608
+ return ok;
2609
+ }
2610
+ weaponSurface(slot, surface) {
2611
+ const ok = this.exports.zylem_stage_weapon_surface?.(slot, (surface.penetrable ? 1 : 0) | (surface.sticky ? 2 : 0), surface.resistance ?? 1) === 1;
2612
+ this.refreshEnvironmentViews();
2613
+ return ok;
2614
+ }
2615
+ launchWeaponProjectile(owner, slot, kind, options = {}) {
2616
+ if (kind !== "sticky" && kind !== "guided") return false;
2617
+ assertRuntimeBehavior(this.compatibility, kind === "sticky" ? "sticky-grenade-attachment" : "remote-guided-projectile");
2618
+ const data = weaponProjectileData(options);
2619
+ if (data.some((x) => !Number.isFinite(x))) return false;
2620
+ this.refreshEnvironmentViews();
2621
+ this.queryView.fill(0);
2622
+ this.queryView.set(data);
2623
+ const ok = this.exports.zylem_stage_launch_weapon_projectile?.(owner, slot, kind === "sticky" ? 0 : 1) === 1;
2624
+ this.refreshEnvironmentViews();
2625
+ return ok;
2626
+ }
2627
+ queryWeapons(slot) {
2628
+ if (this.exports.zylem_stage_query_weapons?.(slot, 0) !== 1) return null;
2629
+ this.refreshEnvironmentViews();
2630
+ const a = Array.from(this.queryView);
2631
+ if (this.exports.zylem_stage_query_weapons?.(slot, 1) !== 1) return null;
2632
+ this.refreshEnvironmentViews();
2633
+ const b = this.queryView;
2634
+ return {
2635
+ abilities: WEAPON_ABILITIES.filter((_, i) => (a[0] & 1 << i) !== 0),
2636
+ phase: WEAPON_PHASES[a[1]],
2637
+ health: a[2],
2638
+ shield: a[3],
2639
+ heat: a[4],
2640
+ magazine: a[5],
2641
+ reserve: a[6],
2642
+ secondaryAmmo: a[7],
2643
+ mode: a[8],
2644
+ charge: a[9],
2645
+ cooldown: a[10],
2646
+ reloadTime: a[11] < 0 ? null : a[11],
2647
+ reloadCommitted: a[12] === 1,
2648
+ yaw: a[13],
2649
+ pitch: a[14],
2650
+ lastEvent: WEAPON_EVENTS[a[15]],
2651
+ shot: { sequence: b[0], kind: ["normal", "charged", "combo"][b[1]], damage: b[2], from: [b[3], b[4], b[5]], to: [b[6], b[7], b[8]], intersections: b[9] },
2652
+ aiming: b[10] === 1,
2653
+ guidedSlot: b[11] < 0 ? null : b[11],
2654
+ overheated: b[12] === 1,
2655
+ ventTime: b[13],
2656
+ switchTime: b[14],
2657
+ timeSinceDamage: b[15]
2658
+ };
2659
+ }
2660
+ queryWeaponProjectile(slot) {
2661
+ if (this.exports.zylem_stage_query_weapon_projectile?.(slot) !== 1) return null;
2662
+ this.refreshEnvironmentViews();
2663
+ const v = this.queryView;
2664
+ return { kind: v[0] === 0 ? "sticky" : "guided", phase: ["flying", "attached", "detonated", "cancelled"][v[1]], remaining: v[2], position: [v[3], v[4], v[5]], yaw: v[6], pitch: v[7], ownerSlot: v[8] < 0 ? null : v[8], hostSlot: v[9] < 0 ? null : v[9], radius: v[10] };
2665
+ }
2666
+ weaponCameraIntent(slot) {
2667
+ const s = this.queryWeapons(slot);
2668
+ if (!s) return null;
2669
+ const q = s.guidedSlot === null ? null : this.queryWeaponProjectile(s.guidedSlot);
2670
+ return q ? { mode: "projectile", slot: s.guidedSlot, yaw: q.yaw, pitch: q.pitch } : { mode: s.aiming ? "precision" : "actor", slot, yaw: s.yaw, pitch: s.pitch };
2671
+ }
2672
+ attachCombat(slot, abilities, options = {}) {
2673
+ let mask = 0;
2674
+ for (const a of abilities) {
2675
+ const index = COMBAT_ABILITIES.indexOf(a);
2676
+ if (index < 0) return false;
2677
+ assertRuntimeBehavior(this.compatibility, a);
2678
+ mask |= 1 << index;
2679
+ }
2680
+ const data = combatData(options);
2681
+ if (data.some((x) => !Number.isFinite(x))) return false;
2682
+ this.refreshEnvironmentViews();
2683
+ this.queryView.set(data);
2684
+ const ok = this.exports.zylem_stage_attach_combat?.(slot, mask) === 1;
2685
+ this.refreshEnvironmentViews();
2686
+ return ok;
2687
+ }
2688
+ detachCombat(slot) {
2689
+ const ok = this.exports.zylem_stage_detach_combat?.(slot) === 1;
2690
+ this.refreshEnvironmentViews();
2691
+ return ok;
2692
+ }
2693
+ combatInput(slot, input) {
2694
+ const ok = this.exports.zylem_stage_combat_input?.(slot, input.moveX ?? 0, input.moveY ?? 0, combatButtons(input)) === 1;
2695
+ this.refreshEnvironmentViews();
2696
+ return ok;
2697
+ }
2698
+ combatAttack(slot, index, attack) {
2699
+ if (!Number.isInteger(index) || index < 0 || index >= 8) return false;
2700
+ const data = combatAttackData(attack);
2701
+ if (data.length !== 16 || data.some((x) => !Number.isFinite(x))) return false;
2702
+ this.refreshEnvironmentViews();
2703
+ this.queryView.set(data);
2704
+ const ok = this.exports.zylem_stage_combat_attack?.(slot, index) === 1;
2705
+ this.refreshEnvironmentViews();
2706
+ return ok;
2707
+ }
2708
+ combatHit(slot, hit, source) {
2709
+ const data = combatHitData(hit);
2710
+ if (data.some((x) => !Number.isFinite(x))) return false;
2711
+ this.refreshEnvironmentViews();
2712
+ this.queryView.fill(0);
2713
+ this.queryView.set(data);
2714
+ const ok = this.exports.zylem_stage_combat_hit?.(slot, source ?? -1) === 1;
2715
+ this.refreshEnvironmentViews();
2716
+ return ok;
2717
+ }
2718
+ queryCombat(slot) {
2719
+ if (this.exports.zylem_stage_query_combat?.(slot) !== 1) return null;
2720
+ this.refreshEnvironmentViews();
2721
+ const v = this.queryView;
2722
+ return { abilities: COMBAT_ABILITIES.filter((_, i) => (v[0] & 1 << i) !== 0), phase: COMBAT_PHASES[v[1]], attackIndex: v[2] < 0 ? null : v[2], actionTime: v[3], damage: v[4], shield: v[5], hitstop: v[6], hitstun: v[7], dodge: v[8], recovery: v[9], partnerSlot: v[10] < 0 ? null : v[10], confirmedHits: v[11], lastEvent: COMBAT_EVENTS[v[12]], launch: [v[13], v[14], 0], grounded: (v[15] & 1) !== 0, facing: (v[15] & 2) !== 0 ? -1 : 1 };
2723
+ }
2724
+ attachPuzzle(slot, ability, options = {}) {
2725
+ const index = PUZZLE_ABILITIES.indexOf(ability);
2726
+ if (index < 0) return false;
2727
+ assertRuntimeBehavior(this.compatibility, ability);
2728
+ const data = puzzleData(ability, options);
2729
+ if (data.length !== 16 || data.some((x) => !Number.isFinite(x))) return false;
2730
+ this.refreshEnvironmentViews();
2731
+ this.queryView.set(data);
2732
+ const ok = this.exports.zylem_stage_attach_puzzle?.(slot, index + 1) === 1;
2733
+ this.refreshEnvironmentViews();
2734
+ return ok;
2735
+ }
2736
+ detachPuzzle(slot) {
2737
+ const ok = this.exports.zylem_stage_detach_puzzle?.(slot) === 1;
2738
+ this.refreshEnvironmentViews();
2739
+ return ok;
2740
+ }
2741
+ queryPuzzle(slot) {
2742
+ if (this.exports.zylem_stage_query_puzzle?.(slot) !== 1) return null;
2743
+ this.refreshEnvironmentViews();
2744
+ const v = this.queryView;
2745
+ return { ability: PUZZLE_ABILITIES[v[0] - 1], active: v[1] === 1, value: v[2], remaining: v[3], blocked: v[4] === 1, transitions: v[5], auxiliary: v[6], storedImpulse: [v[7], v[8], v[9]], target: [v[10], v[11], v[12]], segmentCount: v[13] };
2746
+ }
2747
+ puzzleCommand(slot, command) {
2748
+ const data = puzzleCommandData(command);
2749
+ if (!data || data.length !== 4 || data.some((x) => !Number.isFinite(x))) return false;
2750
+ const ok = this.exports.zylem_stage_puzzle_command?.(slot, ...data) === 1;
2751
+ this.refreshEnvironmentViews();
2752
+ return ok;
2753
+ }
2754
+ puzzleLink(slot, other) {
2755
+ const ok = this.exports.zylem_stage_puzzle_link?.(slot, other ?? -1) === 1;
2756
+ this.refreshEnvironmentViews();
2757
+ return ok;
2758
+ }
2759
+ puzzleSegments(slot) {
2760
+ const n = this.queryPuzzle(slot)?.segmentCount ?? 0, result = [];
2761
+ for (let i = 0; i < Math.min(n, 16); i++) {
2762
+ if (this.exports.zylem_stage_puzzle_segment?.(slot, i) !== 1) break;
2763
+ this.refreshEnvironmentViews();
2764
+ const v = this.queryView;
2765
+ result.push({ from: [v[0], v[1], v[2]], to: [v[3], v[4], v[5]] });
2766
+ }
2767
+ return result;
2768
+ }
2769
+ attachAdventure(slot, abilities, o = {}) {
2770
+ let mask = 0;
2771
+ for (const ability of abilities) {
2772
+ const index = ADVENTURE_ABILITIES.indexOf(ability);
2773
+ if (index < 0) throw new Error(`Unknown adventure ability: ${ability}`);
2774
+ assertRuntimeBehavior(this.compatibility, ability);
2775
+ mask |= 1 << index;
2776
+ }
2777
+ const ok = this.exports.zylem_stage_attach_adventure?.(slot, mask, o.speed ?? 6, o.gravity ?? 9.81, o.range ?? 12, o.scanDuration ?? 1, o.maxCarryMass ?? 10) === 1;
2778
+ this.refreshEnvironmentViews();
2779
+ return ok;
2780
+ }
2781
+ detachAdventure(slot) {
2782
+ const ok = this.exports.zylem_stage_detach_adventure?.(slot) === 1;
2783
+ this.refreshEnvironmentViews();
2784
+ return ok;
2785
+ }
2786
+ queryAdventure(slot) {
2787
+ if (this.exports.zylem_stage_query_adventure?.(slot) !== 1) return null;
2788
+ this.refreshEnvironmentViews();
2789
+ const v = this.queryView;
2790
+ return { mode: v[0], targetSlot: v[1] < 0 ? null : v[1], ball: v[2] === 1, scanProgress: v[3], discoveries: v[4], hits: v[5], unlocks: v[6], heldSlot: v[7] < 0 ? null : v[7], projectile: [v[8], v[9], v[10]], projectilePhase: v[11], ropeLength: v[12], blocked: v[13] === 1, detonations: v[14], hookPhase: v[15] };
2791
+ }
2792
+ writeAdventureInput(slot, input) {
2793
+ this.refreshEnvironmentViews();
2794
+ const base = slot * 8;
2795
+ if (base + 8 > this.inputView.length) return;
2796
+ const buttons = ADVENTURE_BUTTONS.reduce((m, key, i) => m | (input[key] ? 1 << i : 0), 0);
2797
+ this.inputView.set([input.moveX ?? 0, input.moveY ?? 0, input.moveZ ?? 0, input.yaw ?? 0, buttons, input.pitch ?? 0, 0, 1], base);
2798
+ }
2799
+ setAdventureObject(slot, object) {
2800
+ if (!this.exports.zylem_stage_set_adventure_object) return false;
2801
+ let flags = 0;
2802
+ for (const role of object?.roles ?? []) {
2803
+ if (!Object.hasOwn(ADVENTURE_FLAGS, role)) return false;
2804
+ flags |= ADVENTURE_FLAGS[role];
2805
+ }
2806
+ const data = [object?.key ?? 0, object?.consumeToken === false ? 0 : 1, object?.fuse ?? 2, object?.blastRadius ?? 3, ...object?.trackEnd ?? [1, 0, 0], ...object?.trackNormal ?? [0, 1, 0], 0, 0, 0, 0, 0, 0];
2807
+ if (data.length !== 16 || data.some((v) => !Number.isFinite(v))) return false;
2808
+ this.refreshEnvironmentViews();
2809
+ this.queryView.set(data);
2810
+ const ok = this.exports.zylem_stage_set_adventure_object(slot, flags) === 1;
2811
+ this.refreshEnvironmentViews();
2812
+ return ok;
2813
+ }
2814
+ queryAdventureObject(slot) {
2815
+ if (this.exports.zylem_stage_query_adventure_object?.(slot) !== 1) return null;
2816
+ this.refreshEnvironmentViews();
2817
+ const v = this.queryView;
2818
+ return { flags: v[0], key: v[1], opened: v[2] === 1, armed: v[3] === 1, fuse: v[4], detonated: v[5] === 1, ownerSlot: v[6] < 0 ? null : v[6], hits: v[7] };
2819
+ }
2820
+ adventureProgress(slot, kind, key, value = 0) {
2821
+ if (!Number.isInteger(key) || key <= 0 || key > 16777215 || !Number.isInteger(value) || value < 0 || value > 1e6) return 0;
2822
+ const result = this.exports.zylem_stage_adventure_progress?.(slot, kind, key, value) ?? 0;
2823
+ this.refreshEnvironmentViews();
2824
+ return result;
2825
+ }
2826
+ refreshEnvironmentViews() {
2827
+ if (this.queryView.buffer !== this.memory.buffer) this.refreshViews();
2828
+ }
2829
+ attachEnvironment(slot, abilities, o = {}) {
2830
+ let mask = 0;
2831
+ for (const ability of abilities) {
2832
+ const index = ENVIRONMENT_ABILITIES.indexOf(ability);
2833
+ if (index < 0) throw new Error(`Unknown environmental ability: ${ability}`);
2834
+ assertRuntimeBehavior(this.compatibility, ability);
2835
+ mask |= 1 << index;
2836
+ }
2837
+ const attached = this.exports.zylem_stage_attach_environment?.(
2838
+ slot,
2839
+ mask,
2840
+ o.gravity ?? 9.81,
2841
+ o.speed ?? 6,
2842
+ o.drag ?? 1.5,
2843
+ o.buoyancy ?? 1.6,
2844
+ o.turnRate ?? Math.PI,
2845
+ o.breathCapacity ?? 8,
2846
+ o.staminaCapacity ?? 5
2847
+ ) === 1;
2848
+ this.refreshEnvironmentViews();
2849
+ return attached;
2850
+ }
2851
+ detachEnvironment(slot) {
2852
+ return this.exports.zylem_stage_detach_environment?.(slot) === 1;
2853
+ }
2854
+ queryEnvironment(slot) {
2855
+ if (this.exports.zylem_stage_query_environment?.(slot) !== 1) return null;
2856
+ this.refreshEnvironmentViews();
2857
+ const v = this.queryView;
2858
+ return {
2859
+ mode: v[0],
2860
+ submerged: v[1],
2861
+ breath: v[2],
2862
+ stamina: v[3],
2863
+ strokes: v[4],
2864
+ breaches: v[5],
2865
+ damage: v[6],
2866
+ railDistance: v[7],
2867
+ railAngle: v[8],
2868
+ up: [v[9], v[10], v[11]],
2869
+ velocity: [v[12], v[13], v[14]],
2870
+ blocked: v[15] > 0.5
2871
+ };
2872
+ }
2873
+ writeEnvironmentInput(slot, input) {
2874
+ this.refreshEnvironmentViews();
2875
+ this.writeInputAxes(slot, input.moveX ?? 0, input.moveY ?? 0);
2876
+ this.writeInputLook(slot, input.moveZ ?? 0, input.sailTrim ?? 0);
2877
+ [input.stroke, input.breach, input.glide, input.sail, input.ride, input.release].forEach((value, i) => this.writeInputButton(slot, 1 << i, !!value));
2878
+ }
2879
+ setEnvironmentVolume(slot, volume) {
2880
+ if (!this.exports.zylem_stage_set_environment_volume) return false;
2881
+ if (!volume) return this.exports.zylem_stage_set_environment_volume(slot, 0) === 1;
2882
+ const kinds = ["directional-gravity", "point-gravity", "segment-gravity", "water", "wind", "updraft"];
2883
+ const kind = kinds.indexOf(volume.kind) + 1;
2884
+ if (!kind) return false;
2885
+ const vector = volume.kind === "directional-gravity" ? volume.direction : volume.kind === "point-gravity" ? volume.point : volume.kind === "segment-gravity" ? volume.start : volume.kind === "water" ? volume.current ?? [0, 0, 0] : volume.velocity;
2886
+ const end = volume.kind === "segment-gravity" ? volume.end : volume.kind === "water" ? [volume.falloff ?? 0, 0, 0] : [0, 0, 0];
2887
+ const data = [
2888
+ ...volume.min,
2889
+ ...volume.max,
2890
+ ...vector,
2891
+ ...end,
2892
+ volume.kind === "water" ? volume.density ?? 1 : volume.strength ?? (kind <= 3 ? 9.81 : 1),
2893
+ volume.priority ?? 0,
2894
+ volume.kind === "water" ? volume.waveAmplitude ?? 0 : 0,
2895
+ volume.kind === "water" ? volume.waveFrequency ?? 0 : 0
2896
+ ];
2897
+ if (data.length !== 16 || data.some((n) => !Number.isFinite(n))) return false;
2898
+ this.refreshEnvironmentViews();
2899
+ this.queryView.set(data);
2900
+ const accepted = this.exports.zylem_stage_set_environment_volume(slot, kind) === 1;
2901
+ this.refreshEnvironmentViews();
2902
+ return accepted;
2903
+ }
2904
+ setTubeRail(slot, rail) {
2905
+ if (!this.exports.zylem_stage_set_tube_rail) return false;
2906
+ if (!rail) return this.exports.zylem_stage_set_tube_rail(slot, 0) === 1;
2907
+ assertRuntimeBehavior(this.compatibility, "tube-slide-steering");
2908
+ const data = [...rail.points.flat(), rail.radius, ...rail.up ?? [0, 1, 0]];
2909
+ if (data.length !== 16 || data.some((n) => !Number.isFinite(n))) return false;
2910
+ this.refreshEnvironmentViews();
2911
+ this.queryView.set(data);
2912
+ const accepted = this.exports.zylem_stage_set_tube_rail(slot, 1) === 1;
2913
+ this.refreshEnvironmentViews();
2914
+ return accepted;
2915
+ }
2916
+ attachAcrobatics(slot, abilities, opts = {}) {
2917
+ const names = TRAVERSAL_ABILITIES;
2918
+ let mask = 0;
2919
+ for (const ability of abilities) {
2920
+ if (!names.includes(ability)) throw new Error(`Unknown acrobatics ability: ${ability}`);
2921
+ assertRuntimeBehavior(this.compatibility, ability);
2922
+ mask |= 1 << names.indexOf(ability);
2923
+ }
2924
+ return this.exports.zylem_stage_attach_acrobatics?.(
2925
+ slot,
2926
+ mask,
2927
+ opts.moveSpeed ?? 6,
2928
+ opts.jumpSpeed ?? 10,
2929
+ opts.gravity ?? 24,
2930
+ opts.chainWindow ?? 0.25
2931
+ ) === 1;
2932
+ }
2933
+ queryAcrobatics(slot) {
2934
+ const query = this.exports.zylem_stage_query_traversal ?? this.exports.zylem_stage_query_acrobatics;
2935
+ if (query?.(slot) !== 1) return null;
2936
+ const extended = !!this.exports.zylem_stage_query_traversal;
2937
+ const v = this.queryView;
2938
+ return {
2939
+ grounded: v[0] > 0.5,
2940
+ action: v[1],
2941
+ chain: v[2],
2942
+ remaining: v[3],
2943
+ facing: v[4],
2944
+ launches: v[5],
2945
+ velocity: [v[6], v[7]],
2946
+ impacts: extended ? v[8] : 0,
2947
+ phaseRemaining: extended ? v[9] : 0,
2948
+ poleAngle: extended ? v[10] : 0,
2949
+ wallWindow: extended ? v[11] : 0,
2950
+ reattachLockout: extended ? v[12] : 0,
2951
+ velocityZ: extended ? v[13] : 0,
2952
+ impactThisStep: extended && v[14] > 0.5,
2953
+ impactSpeed: extended ? v[15] : 0
2954
+ };
2955
+ }
2956
+ detachAcrobatics(slot) {
2957
+ return this.exports.zylem_stage_detach_acrobatics?.(slot) === 1;
2958
+ }
2959
+ setTraversalSurface(slot, kind) {
2960
+ if (kind) assertRuntimeBehavior(this.compatibility, kind === "pole" ? "pole-climb" : "ceiling-hang-traverse");
2961
+ return this.exports.zylem_stage_set_traversal_surface?.(slot, kind === "pole" ? 1 : kind === "ceiling" ? 2 : 0) === 1;
2962
+ }
2963
+ setPossessable(slot, enabled) {
2964
+ assertRuntimeBehavior(this.compatibility, "temporary-actor-possession");
2965
+ return this.exports.zylem_stage_set_possessable?.(slot, Number(enabled)) === 1;
2966
+ }
2967
+ startPossession(owner, target, seconds = 5) {
2968
+ assertRuntimeBehavior(this.compatibility, "temporary-actor-possession");
2969
+ return this.exports.zylem_stage_start_possession?.(owner, target, seconds) === 1;
2970
+ }
2971
+ endPossession(owner) {
2972
+ return this.exports.zylem_stage_end_possession?.(owner) === 1;
2973
+ }
2974
+ queryPossession(owner) {
2975
+ if (this.exports.zylem_stage_query_possession?.(owner) !== 1) return null;
2976
+ return { targetSlot: this.queryView[0], remaining: this.queryView[1] };
2977
+ }
2978
+ attachTopDown(slot, opts) {
2979
+ assertRuntimeBehavior(this.compatibility, "top-down-movement");
2980
+ return this.exports.zylem_stage_attach_top_down(slot, opts.plane, opts.speed, opts.faceMovement ? 1 : 0) !== 0;
2981
+ }
2982
+ setTopDownInput(slot, axisA, axisB) {
2983
+ this.exports.zylem_stage_set_top_down_input(slot, axisA, axisB);
2984
+ }
2985
+ attachJumper2D(slot, opts) {
2986
+ assertRuntimeBehavior(this.compatibility, "jumper-2d");
2987
+ return this.exports.zylem_stage_attach_jumper_2d(
2988
+ slot,
2989
+ opts.jumpForce,
2990
+ opts.maxJumps >>> 0,
2991
+ opts.jumpBufferTime,
2992
+ opts.coyoteTime,
2993
+ opts.gravity
2994
+ ) !== 0;
2995
+ }
2996
+ setJumper2DInput(slot, jumpPressed) {
2997
+ this.exports.zylem_stage_set_jumper_2d_input(slot, jumpPressed ? 1 : 0);
2998
+ }
2999
+ queryJumper2D(slot) {
3000
+ if (this.exports.zylem_stage_query_jumper_2d(slot) === 0) return null;
3001
+ const v = this.queryView;
3002
+ return {
3003
+ grounded: v[0] > 0.5,
3004
+ jumpCount: v[1] | 0,
3005
+ coyote: v[2],
3006
+ jumpBuffer: v[3]
3007
+ };
3008
+ }
3009
+ attachJumper3D(slot, opts) {
3010
+ assertRuntimeBehavior(this.compatibility, "jumper-3d");
3011
+ return this.exports.zylem_stage_attach_jumper_3d(
3012
+ slot,
3013
+ opts.jumpForce,
3014
+ opts.maxJumps >>> 0,
3015
+ opts.gravity,
3016
+ opts.coyoteTime ?? 0.1,
3017
+ opts.jumpBufferTime ?? 0.08
3018
+ ) !== 0;
3019
+ }
3020
+ setJumper3DInput(slot, jump) {
3021
+ this.exports.zylem_stage_set_jumper_3d_input(slot, jump ? 1 : 0);
3022
+ }
3023
+ queryJumper3D(slot) {
3024
+ if (this.exports.zylem_stage_query_jumper_3d(slot) === 0) return null;
3025
+ const v = this.queryView;
3026
+ return { grounded: v[0] > 0.5, jumpCount: v[1] | 0, verticalVelocity: v[2] };
3027
+ }
3028
+ attachShooter2D(slot, opts) {
3029
+ assertRuntimeBehavior(this.compatibility, "shooter-2d");
3030
+ return this.exports.zylem_stage_attach_shooter_2d(
3031
+ slot,
3032
+ opts.fireRateHz,
3033
+ opts.muzzleOffsetX,
3034
+ opts.muzzleOffsetY,
3035
+ opts.muzzleSpeed
3036
+ ) !== 0;
3037
+ }
3038
+ fireShooter2D(slot, x, y, z, yaw) {
3039
+ return this.exports.zylem_stage_shooter_2d_fire(slot, x, y, z, yaw) !== 0;
3040
+ }
3041
+ queryShooter2D(slot) {
3042
+ if (this.exports.zylem_stage_query_shooter_2d(slot) === 0) return null;
3043
+ const v = this.queryView;
3044
+ return { cooldown: v[0], firedCount: v[1] | 0 };
3045
+ }
3046
+ attachPlatformer3D(slot, opts) {
3047
+ assertRuntimeBehavior(this.compatibility, "platformer-3d");
3048
+ return this.exports.zylem_stage_attach_platformer_3d(
3049
+ slot,
3050
+ opts.halfHeight,
3051
+ opts.radius,
3052
+ opts.walkSpeed,
3053
+ opts.runSpeed,
3054
+ opts.jumpForce,
3055
+ opts.maxJumps >>> 0,
3056
+ opts.gravity,
3057
+ opts.coyoteTime ?? 0.1,
3058
+ opts.jumpBufferTime ?? 0.1,
3059
+ opts.jumpCutMultiplier ?? 0.5,
3060
+ opts.multiJumpWindow ?? 0.15,
3061
+ opts.slopeClimbAngle ?? 50,
3062
+ opts.slopeSlideAngle ?? 40,
3063
+ opts.autostepHeight ?? 0.3,
3064
+ opts.autostepMinWidth ?? 0.1,
3065
+ opts.snapToGroundDistance ?? 0.3,
3066
+ opts.characterOffset ?? 0.05,
3067
+ opts.offsetX ?? 0,
3068
+ opts.offsetY ?? 0,
3069
+ opts.offsetZ ?? 0,
3070
+ opts.shapeSource ?? StagePlatformerShapeSource.Auto
3071
+ ) !== 0;
3072
+ }
3073
+ setPlatformer3DInputAxes(slot, moveX, moveZ) {
3074
+ this.exports.zylem_stage_set_platformer_3d_input_axes(slot, moveX, moveZ);
3075
+ }
3076
+ setPlatformer3DInputButtons(slot, jump, run) {
3077
+ this.exports.zylem_stage_set_platformer_3d_input_buttons(slot, jump ? 1 : 0, run ? 1 : 0);
3078
+ }
3079
+ queryPlatformer3D(slot) {
3080
+ if (this.exports.zylem_stage_query_platformer_3d(slot) === 0) return null;
3081
+ const v = this.queryView;
3082
+ return {
3083
+ grounded: v[0] > 0.5,
3084
+ jumpCount: v[1] | 0,
3085
+ fsmState: v[2] | 0,
3086
+ speed: v[3]
3087
+ };
3088
+ }
3089
+ attachPlatformer2D(slot, opts) {
3090
+ assertRuntimeBehavior(this.compatibility, "platformer-2d");
3091
+ return this.exports.zylem_stage_attach_platformer_2d(
3092
+ slot,
3093
+ opts.halfHeight,
3094
+ opts.radius,
3095
+ opts.walkSpeed,
3096
+ opts.runSpeed,
3097
+ opts.jumpForce,
3098
+ opts.maxJumps >>> 0,
3099
+ opts.gravity,
3100
+ opts.coyoteTime ?? 0.1,
3101
+ opts.jumpBufferTime ?? 0.1,
3102
+ opts.jumpCutMultiplier ?? 0.5,
3103
+ opts.multiJumpWindow ?? 0.15,
3104
+ opts.slopeClimbAngle ?? 50,
3105
+ opts.slopeSlideAngle ?? 40,
3106
+ opts.autostepHeight ?? 0.25,
3107
+ opts.autostepMinWidth ?? 0.05,
3108
+ opts.snapToGroundDistance ?? 0.2,
3109
+ opts.characterOffset ?? 0.05,
3110
+ opts.offsetX ?? 0,
3111
+ opts.offsetY ?? 0,
3112
+ opts.offsetZ ?? 0,
3113
+ opts.shapeSource ?? StagePlatformerShapeSource.Auto
3114
+ ) !== 0;
3115
+ }
3116
+ setPlatformer2DInputAxes(slot, moveX) {
3117
+ this.exports.zylem_stage_set_platformer_2d_input_axes(slot, moveX);
3118
+ }
3119
+ setPlatformer2DInputButtons(slot, jump, run) {
3120
+ this.exports.zylem_stage_set_platformer_2d_input_buttons(slot, jump ? 1 : 0, run ? 1 : 0);
3121
+ }
3122
+ queryPlatformer2D(slot) {
3123
+ if (this.exports.zylem_stage_query_platformer_2d(slot) === 0) return null;
3124
+ const v = this.queryView;
3125
+ return {
3126
+ grounded: v[0] > 0.5,
3127
+ jumpCount: v[1] | 0,
3128
+ fsmState: v[2] | 0,
3129
+ verticalVelocity: v[3]
3130
+ };
3131
+ }
3132
+ attachFirstPerson(slot, opts) {
3133
+ assertRuntimeBehavior(this.compatibility, "first-person");
3134
+ return this.exports.zylem_stage_attach_first_person(
3135
+ slot,
3136
+ opts.walkSpeed,
3137
+ opts.runSpeed,
3138
+ opts.eyeHeight,
3139
+ opts.lookSensitivity
3140
+ ) !== 0;
3141
+ }
3142
+ setFirstPersonInput(slot, input) {
3143
+ this.exports.zylem_stage_set_first_person_input(
3144
+ slot,
3145
+ input.moveX,
3146
+ input.moveZ,
3147
+ input.lookYawDelta,
3148
+ input.lookPitchDelta,
3149
+ input.run ? 1 : 0
3150
+ );
3151
+ }
3152
+ queryFirstPerson(slot) {
3153
+ if (this.exports.zylem_stage_query_first_person(slot) === 0) return null;
3154
+ const v = this.queryView;
3155
+ return { yaw: v[0], pitch: v[1], speed: v[2], eyeHeight: v[3] };
3156
+ }
3157
+ };
3158
+ async function createWasmStageRuntime(options = {}) {
3159
+ const { exports } = await loadStageFfi({
3160
+ source: options.source,
3161
+ imports: options.imports,
3162
+ requiredBehaviors: options.requiredBehaviors
3163
+ });
3164
+ return new WasmStageRuntime(exports, options);
3165
+ }
3166
+
3167
+ export {
3168
+ STAGE_RENDER_STRIDE,
3169
+ STAGE_INPUT_STRIDE,
3170
+ STAGE_SNAPSHOT_STRIDE,
3171
+ STAGE_EVENT_STRIDE,
3172
+ STAGE_INVALID_SLOT,
3173
+ StageBodyKind,
3174
+ StageBoundaryDim,
3175
+ StageRicochetDim,
3176
+ StageRicochetReflection,
3177
+ StageTopDownPlane,
3178
+ StageEventType,
3179
+ StagePlatformerShapeSource,
3180
+ STAGE_INPUT_BUTTON_JUMP,
3181
+ STAGE_INPUT_BUTTON_RUN,
3182
+ readBehaviorSnapshot,
3183
+ readStageRenderSlot,
3184
+ WasmStageRuntime,
3185
+ createWasmStageRuntime
3186
+ };