@zylem/game-lib 0.6.2 → 0.6.4

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 (82) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +9 -16
  3. package/dist/actions.d.ts +30 -21
  4. package/dist/actions.js +793 -146
  5. package/dist/actions.js.map +1 -1
  6. package/dist/behavior/jumper-2d.d.ts +114 -0
  7. package/dist/behavior/jumper-2d.js +711 -0
  8. package/dist/behavior/jumper-2d.js.map +1 -0
  9. package/dist/behavior/platformer-3d.d.ts +296 -0
  10. package/dist/behavior/platformer-3d.js +761 -0
  11. package/dist/behavior/platformer-3d.js.map +1 -0
  12. package/dist/behavior/ricochet-2d.d.ts +275 -0
  13. package/dist/behavior/ricochet-2d.js +425 -0
  14. package/dist/behavior/ricochet-2d.js.map +1 -0
  15. package/dist/behavior/ricochet-3d.d.ts +117 -0
  16. package/dist/behavior/ricochet-3d.js +443 -0
  17. package/dist/behavior/ricochet-3d.js.map +1 -0
  18. package/dist/behavior/screen-visibility.d.ts +79 -0
  19. package/dist/behavior/screen-visibility.js +358 -0
  20. package/dist/behavior/screen-visibility.js.map +1 -0
  21. package/dist/behavior/screen-wrap.d.ts +87 -0
  22. package/dist/behavior/screen-wrap.js +246 -0
  23. package/dist/behavior/screen-wrap.js.map +1 -0
  24. package/dist/behavior/shooter-2d.d.ts +79 -0
  25. package/dist/behavior/shooter-2d.js +180 -0
  26. package/dist/behavior/shooter-2d.js.map +1 -0
  27. package/dist/behavior/thruster.d.ts +11 -0
  28. package/dist/behavior/thruster.js +292 -0
  29. package/dist/behavior/thruster.js.map +1 -0
  30. package/dist/behavior/top-down-movement.d.ts +56 -0
  31. package/dist/behavior/top-down-movement.js +125 -0
  32. package/dist/behavior/top-down-movement.js.map +1 -0
  33. package/dist/behavior/world-boundary-2d.d.ts +142 -0
  34. package/dist/behavior/world-boundary-2d.js +235 -0
  35. package/dist/behavior/world-boundary-2d.js.map +1 -0
  36. package/dist/behavior/world-boundary-3d.d.ts +76 -0
  37. package/dist/behavior/world-boundary-3d.js +274 -0
  38. package/dist/behavior/world-boundary-3d.js.map +1 -0
  39. package/dist/behavior-descriptor-BXnVR8Ki.d.ts +159 -0
  40. package/dist/{blueprints-Cq3Ko6_G.d.ts → blueprints-DmbK2dki.d.ts} +2 -2
  41. package/dist/camera-4XO5gbQH.d.ts +905 -0
  42. package/dist/camera.d.ts +3 -2
  43. package/dist/camera.js +1653 -377
  44. package/dist/camera.js.map +1 -1
  45. package/dist/composition-BASvMKrW.d.ts +218 -0
  46. package/dist/{core-bO8TzV7u.d.ts → core-CARRaS55.d.ts} +110 -69
  47. package/dist/core.d.ts +11 -6
  48. package/dist/core.js +10766 -5626
  49. package/dist/core.js.map +1 -1
  50. package/dist/{entities-DvByhMGU.d.ts → entities-ChFirVL9.d.ts} +133 -29
  51. package/dist/entities.d.ts +5 -3
  52. package/dist/entities.js +4679 -3202
  53. package/dist/entities.js.map +1 -1
  54. package/dist/entity-vj-HTjzU.d.ts +1169 -0
  55. package/dist/global-change-2JvMaz44.d.ts +25 -0
  56. package/dist/main.d.ts +1118 -16
  57. package/dist/main.js +17538 -8499
  58. package/dist/main.js.map +1 -1
  59. package/dist/physics-pose-DCc4oE44.d.ts +25 -0
  60. package/dist/physics-protocol-BDD3P5W2.d.ts +200 -0
  61. package/dist/physics-worker.d.ts +21 -0
  62. package/dist/physics-worker.js +306 -0
  63. package/dist/physics-worker.js.map +1 -0
  64. package/dist/physics.d.ts +205 -0
  65. package/dist/physics.js +577 -0
  66. package/dist/physics.js.map +1 -0
  67. package/dist/stage-types-C19IhuzA.d.ts +731 -0
  68. package/dist/stage.d.ts +11 -7
  69. package/dist/stage.js +8024 -3852
  70. package/dist/stage.js.map +1 -1
  71. package/dist/sync-state-machine-CZyspBpj.d.ts +16 -0
  72. package/dist/thruster-23lzoPZd.d.ts +180 -0
  73. package/dist/world-DfgxoNMt.d.ts +105 -0
  74. package/package.json +53 -13
  75. package/dist/behaviors.d.ts +0 -854
  76. package/dist/behaviors.js +0 -1209
  77. package/dist/behaviors.js.map +0 -1
  78. package/dist/camera-CeJPAgGg.d.ts +0 -116
  79. package/dist/moveable-B_vyA6cw.d.ts +0 -67
  80. package/dist/stage-types-Bd-KtcYT.d.ts +0 -375
  81. package/dist/transformable-CUhvyuYO.d.ts +0 -67
  82. package/dist/world-C8tQ7Plj.d.ts +0 -774
@@ -0,0 +1,142 @@
1
+ import { c as BehaviorDescriptor } from '../behavior-descriptor-BXnVR8Ki.js';
2
+ import { S as SyncStateMachine } from '../sync-state-machine-CZyspBpj.js';
3
+ import 'bitecs';
4
+ import 'typescript-fsm';
5
+
6
+ /**
7
+ * WorldBoundary2DFSM
8
+ *
9
+ * Minimal FSM + extended state to track which world boundaries were hit.
10
+ *
11
+ * Notes:
12
+ * - "Hit boundaries" is inherently a *set* (can hit left+bottom in one frame),
13
+ * so we store it as extended state (`lastHits`) rather than a single FSM state.
14
+ * - The FSM state is still useful for coarse status like "inside" vs "touching".
15
+ */
16
+
17
+ type WorldBoundary2DHit = 'top' | 'bottom' | 'left' | 'right';
18
+ type WorldBoundary2DHits = Record<WorldBoundary2DHit, boolean>;
19
+ interface WorldBoundary2DPosition {
20
+ x: number;
21
+ y: number;
22
+ }
23
+ interface WorldBoundary2DBounds {
24
+ top: number;
25
+ bottom: number;
26
+ left: number;
27
+ right: number;
28
+ }
29
+ declare enum WorldBoundary2DState {
30
+ Inside = "inside",
31
+ Touching = "touching"
32
+ }
33
+ declare enum WorldBoundary2DEvent {
34
+ EnterInside = "enter-inside",
35
+ TouchBoundary = "touch-boundary"
36
+ }
37
+ /**
38
+ * Compute which boundaries are being hit for a position and bounds.
39
+ * This matches the semantics of the legacy `boundary2d` behavior:
40
+ * - left hit if x <= left
41
+ * - right hit if x >= right
42
+ * - bottom hit if y <= bottom
43
+ * - top hit if y >= top
44
+ */
45
+ declare function computeWorldBoundary2DHits(position: WorldBoundary2DPosition, bounds: WorldBoundary2DBounds): WorldBoundary2DHits;
46
+ declare function hasAnyWorldBoundary2DHit(hits: WorldBoundary2DHits): boolean;
47
+ /**
48
+ * FSM wrapper with "extended state" (lastHits / lastPosition).
49
+ * Systems should call `update(...)` once per frame.
50
+ */
51
+ declare class WorldBoundary2DFSM {
52
+ readonly machine: SyncStateMachine<WorldBoundary2DState, WorldBoundary2DEvent, never>;
53
+ private lastHits;
54
+ private lastPosition;
55
+ private lastUpdatedAtMs;
56
+ constructor();
57
+ getState(): WorldBoundary2DState;
58
+ /**
59
+ * Returns the last computed hits (always available after first update call).
60
+ */
61
+ getLastHits(): WorldBoundary2DHits;
62
+ /**
63
+ * Returns adjusted movement values based on boundary hits.
64
+ * If the entity is touching a boundary and trying to move further into it,
65
+ * that axis component is zeroed out.
66
+ *
67
+ * @param moveX - The desired X movement
68
+ * @param moveY - The desired Y movement
69
+ * @returns Adjusted { moveX, moveY } with boundary-blocked axes zeroed
70
+ */
71
+ getMovement(moveX: number, moveY: number): {
72
+ moveX: number;
73
+ moveY: number;
74
+ };
75
+ /**
76
+ * Returns the last position passed to `update`, if any.
77
+ */
78
+ getLastPosition(): WorldBoundary2DPosition | null;
79
+ /**
80
+ * Best-effort timestamp (ms) of the last `update(...)` call.
81
+ * This is optional metadata; systems can ignore it.
82
+ */
83
+ getLastUpdatedAtMs(): number | null;
84
+ /**
85
+ * Update FSM + extended state based on current position and bounds.
86
+ * Returns the computed hits for convenience.
87
+ */
88
+ update(position: WorldBoundary2DPosition, bounds: WorldBoundary2DBounds): WorldBoundary2DHits;
89
+ private dispatch;
90
+ }
91
+
92
+ interface WorldBoundary2DOptions {
93
+ /**
94
+ * World boundaries (in world units).
95
+ * - left hit if x <= left
96
+ * - right hit if x >= right
97
+ * - bottom hit if y <= bottom
98
+ * - top hit if y >= top
99
+ */
100
+ boundaries: WorldBoundary2DBounds;
101
+ }
102
+ /**
103
+ * Handle methods provided by WorldBoundary2DBehavior
104
+ */
105
+ interface WorldBoundary2DHandle {
106
+ /**
107
+ * Get the last computed boundary hits.
108
+ * Returns null until entity is spawned and FSM is initialized.
109
+ */
110
+ getLastHits(): WorldBoundary2DHits | null;
111
+ /**
112
+ * Get adjusted movement values based on boundary hits.
113
+ * Zeros out movement into boundaries the entity is touching.
114
+ */
115
+ getMovement(moveX: number, moveY: number): {
116
+ moveX: number;
117
+ moveY: number;
118
+ };
119
+ }
120
+ /**
121
+ * WorldBoundary2DBehavior
122
+ *
123
+ * @example
124
+ * ```ts
125
+ * import { WorldBoundary2DBehavior } from "@zylem/game-lib";
126
+ *
127
+ * const ship = createSprite({ ... });
128
+ * const boundary = ship.use(WorldBoundary2DBehavior, {
129
+ * boundaries: { left: -10, right: 10, bottom: -7.5, top: 7.5 },
130
+ * });
131
+ *
132
+ * ship.onUpdate(({ me }) => {
133
+ * let moveX = ..., moveY = ...;
134
+ * const hits = boundary.getLastHits(); // Fully typed!
135
+ * ({ moveX, moveY } = boundary.getMovement(moveX, moveY));
136
+ * me.moveXY(moveX, moveY);
137
+ * });
138
+ * ```
139
+ */
140
+ declare const WorldBoundary2DBehavior: BehaviorDescriptor<WorldBoundary2DOptions, WorldBoundary2DHandle, unknown>;
141
+
142
+ export { WorldBoundary2DBehavior, type WorldBoundary2DBounds, WorldBoundary2DEvent, WorldBoundary2DFSM, type WorldBoundary2DHandle, type WorldBoundary2DHit, type WorldBoundary2DHits, type WorldBoundary2DOptions, type WorldBoundary2DPosition, WorldBoundary2DState, computeWorldBoundary2DHits, hasAnyWorldBoundary2DHit };
@@ -0,0 +1,235 @@
1
+ // src/lib/behaviors/behavior-descriptor.ts
2
+ function defineBehavior(config) {
3
+ return {
4
+ key: /* @__PURE__ */ Symbol.for(`zylem:behavior:${config.name}`),
5
+ defaultOptions: config.defaultOptions,
6
+ systemFactory: config.systemFactory,
7
+ createHandle: config.createHandle
8
+ };
9
+ }
10
+
11
+ // src/lib/core/utility/sync-state-machine.ts
12
+ import {
13
+ StateMachine as BaseStateMachine
14
+ } from "typescript-fsm";
15
+ import { t } from "typescript-fsm";
16
+ var SyncStateMachine = class extends BaseStateMachine {
17
+ constructor(init, transitions = [], logger = console) {
18
+ super(init, transitions, logger);
19
+ }
20
+ dispatch(_event, ..._args) {
21
+ throw new Error("SyncStateMachine does not support async dispatch.");
22
+ }
23
+ syncDispatch(event, ...args) {
24
+ const found = this.transitions.some((transition) => {
25
+ if (transition.fromState !== this._current || transition.event !== event) {
26
+ return false;
27
+ }
28
+ const current = this._current;
29
+ this._current = transition.toState;
30
+ if (!transition.cb) {
31
+ return true;
32
+ }
33
+ try {
34
+ transition.cb(...args);
35
+ return true;
36
+ } catch (error) {
37
+ this._current = current;
38
+ this.logger.error("Exception in callback", error);
39
+ throw error;
40
+ }
41
+ });
42
+ if (!found) {
43
+ const errorMessage = this.formatErr(this._current, event);
44
+ this.logger.error(errorMessage);
45
+ }
46
+ return found;
47
+ }
48
+ };
49
+
50
+ // src/lib/behaviors/shared/bounds-2d.ts
51
+ function computeBounds2DHits(position, bounds) {
52
+ const hits = {
53
+ top: false,
54
+ bottom: false,
55
+ left: false,
56
+ right: false
57
+ };
58
+ if (position.x <= bounds.minX) hits.left = true;
59
+ else if (position.x >= bounds.maxX) hits.right = true;
60
+ if (position.y <= bounds.minY) hits.bottom = true;
61
+ else if (position.y >= bounds.maxY) hits.top = true;
62
+ return hits;
63
+ }
64
+ function hasAnyBounds2DHit(hits) {
65
+ return hits.left || hits.right || hits.top || hits.bottom;
66
+ }
67
+ function constrainMovementToBounds2D(hits, moveX, moveY) {
68
+ let adjustedX = moveX;
69
+ let adjustedY = moveY;
70
+ if (hits.left && moveX < 0 || hits.right && moveX > 0) {
71
+ adjustedX = 0;
72
+ }
73
+ if (hits.bottom && moveY < 0 || hits.top && moveY > 0) {
74
+ adjustedY = 0;
75
+ }
76
+ return { moveX: adjustedX, moveY: adjustedY };
77
+ }
78
+
79
+ // src/lib/behaviors/world-boundary-2d/world-boundary-2d-fsm.ts
80
+ var WorldBoundary2DState = /* @__PURE__ */ ((WorldBoundary2DState2) => {
81
+ WorldBoundary2DState2["Inside"] = "inside";
82
+ WorldBoundary2DState2["Touching"] = "touching";
83
+ return WorldBoundary2DState2;
84
+ })(WorldBoundary2DState || {});
85
+ var WorldBoundary2DEvent = /* @__PURE__ */ ((WorldBoundary2DEvent2) => {
86
+ WorldBoundary2DEvent2["EnterInside"] = "enter-inside";
87
+ WorldBoundary2DEvent2["TouchBoundary"] = "touch-boundary";
88
+ return WorldBoundary2DEvent2;
89
+ })(WorldBoundary2DEvent || {});
90
+ function computeWorldBoundary2DHits(position, bounds) {
91
+ return computeBounds2DHits(position, {
92
+ minX: bounds.left,
93
+ maxX: bounds.right,
94
+ minY: bounds.bottom,
95
+ maxY: bounds.top
96
+ });
97
+ }
98
+ function hasAnyWorldBoundary2DHit(hits) {
99
+ return hasAnyBounds2DHit(hits);
100
+ }
101
+ var WorldBoundary2DFSM = class {
102
+ machine;
103
+ lastHits = { top: false, bottom: false, left: false, right: false };
104
+ lastPosition = null;
105
+ lastUpdatedAtMs = null;
106
+ constructor() {
107
+ this.machine = new SyncStateMachine(
108
+ "inside" /* Inside */,
109
+ [
110
+ t("inside" /* Inside */, "touch-boundary" /* TouchBoundary */, "touching" /* Touching */),
111
+ t("touching" /* Touching */, "enter-inside" /* EnterInside */, "inside" /* Inside */),
112
+ // Self transitions (no-ops)
113
+ t("inside" /* Inside */, "enter-inside" /* EnterInside */, "inside" /* Inside */),
114
+ t("touching" /* Touching */, "touch-boundary" /* TouchBoundary */, "touching" /* Touching */)
115
+ ]
116
+ );
117
+ }
118
+ getState() {
119
+ return this.machine.getState();
120
+ }
121
+ /**
122
+ * Returns the last computed hits (always available after first update call).
123
+ */
124
+ getLastHits() {
125
+ return this.lastHits;
126
+ }
127
+ /**
128
+ * Returns adjusted movement values based on boundary hits.
129
+ * If the entity is touching a boundary and trying to move further into it,
130
+ * that axis component is zeroed out.
131
+ *
132
+ * @param moveX - The desired X movement
133
+ * @param moveY - The desired Y movement
134
+ * @returns Adjusted { moveX, moveY } with boundary-blocked axes zeroed
135
+ */
136
+ getMovement(moveX, moveY) {
137
+ return constrainMovementToBounds2D(this.lastHits, moveX, moveY);
138
+ }
139
+ /**
140
+ * Returns the last position passed to `update`, if any.
141
+ */
142
+ getLastPosition() {
143
+ return this.lastPosition;
144
+ }
145
+ /**
146
+ * Best-effort timestamp (ms) of the last `update(...)` call.
147
+ * This is optional metadata; systems can ignore it.
148
+ */
149
+ getLastUpdatedAtMs() {
150
+ return this.lastUpdatedAtMs;
151
+ }
152
+ /**
153
+ * Update FSM + extended state based on current position and bounds.
154
+ * Returns the computed hits for convenience.
155
+ */
156
+ update(position, bounds) {
157
+ const hits = computeWorldBoundary2DHits(position, bounds);
158
+ this.lastHits = hits;
159
+ this.lastPosition = { x: position.x, y: position.y };
160
+ this.lastUpdatedAtMs = Date.now();
161
+ if (hasAnyWorldBoundary2DHit(hits)) {
162
+ this.dispatch("touch-boundary" /* TouchBoundary */);
163
+ } else {
164
+ this.dispatch("enter-inside" /* EnterInside */);
165
+ }
166
+ return hits;
167
+ }
168
+ dispatch(event) {
169
+ if (this.machine.can(event)) {
170
+ this.machine.syncDispatch(event);
171
+ }
172
+ }
173
+ };
174
+
175
+ // src/lib/behaviors/world-boundary-2d/world-boundary-2d.descriptor.ts
176
+ var defaultOptions = {
177
+ boundaries: { top: 0, bottom: 0, left: 0, right: 0 }
178
+ };
179
+ var WORLD_BOUNDARY_BEHAVIOR_KEY = /* @__PURE__ */ Symbol.for(
180
+ "zylem:behavior:world-boundary-2d"
181
+ );
182
+ function createWorldBoundary2DHandle(ref) {
183
+ return {
184
+ getLastHits: () => {
185
+ const fsm = ref.fsm;
186
+ return fsm?.getLastHits() ?? null;
187
+ },
188
+ getMovement: (moveX, moveY) => {
189
+ const fsm = ref.fsm;
190
+ return fsm?.getMovement(moveX, moveY) ?? { moveX, moveY };
191
+ }
192
+ };
193
+ }
194
+ var WorldBoundary2DSystem = class {
195
+ constructor(world, getBehaviorLinks) {
196
+ this.world = world;
197
+ this.getBehaviorLinks = getBehaviorLinks;
198
+ }
199
+ update(_ecs, _delta) {
200
+ const links = this.getBehaviorLinks?.(WORLD_BOUNDARY_BEHAVIOR_KEY);
201
+ if (!links) return;
202
+ for (const link of links) {
203
+ const gameEntity = link.entity;
204
+ const boundaryRef = link.ref;
205
+ if (!gameEntity.body) continue;
206
+ const options = boundaryRef.options;
207
+ if (!boundaryRef.fsm) {
208
+ boundaryRef.fsm = new WorldBoundary2DFSM();
209
+ }
210
+ const body = gameEntity.body;
211
+ const pos = body.translation();
212
+ boundaryRef.fsm.update(
213
+ { x: pos.x, y: pos.y },
214
+ options.boundaries
215
+ );
216
+ }
217
+ }
218
+ destroy(_ecs) {
219
+ }
220
+ };
221
+ var WorldBoundary2DBehavior = defineBehavior({
222
+ name: "world-boundary-2d",
223
+ defaultOptions,
224
+ systemFactory: (ctx) => new WorldBoundary2DSystem(ctx.world, ctx.getBehaviorLinks),
225
+ createHandle: createWorldBoundary2DHandle
226
+ });
227
+ export {
228
+ WorldBoundary2DBehavior,
229
+ WorldBoundary2DEvent,
230
+ WorldBoundary2DFSM,
231
+ WorldBoundary2DState,
232
+ computeWorldBoundary2DHits,
233
+ hasAnyWorldBoundary2DHit
234
+ };
235
+ //# sourceMappingURL=world-boundary-2d.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/behaviors/behavior-descriptor.ts","../../src/lib/core/utility/sync-state-machine.ts","../../src/lib/behaviors/shared/bounds-2d.ts","../../src/lib/behaviors/world-boundary-2d/world-boundary-2d-fsm.ts","../../src/lib/behaviors/world-boundary-2d/world-boundary-2d.descriptor.ts"],"sourcesContent":["/**\n * BehaviorDescriptor\n *\n * Type-safe behavior descriptors that provide options inference.\n * Used with entity.use() to declaratively attach behaviors to entities.\n *\n * Each behavior can define its own handle type via `createHandle`,\n * providing behavior-specific methods with full type safety.\n */\n\nimport type { BehaviorSystemFactory } from './behavior-system';\n\n/**\n * Base handle returned by entity.use() for lazy access to behavior runtime.\n * FSM is null until entity is spawned and components are initialized.\n */\nexport interface BaseBehaviorHandle<\n O extends Record<string, any> = Record<string, any>,\n> {\n /** Get the FSM instance (null until entity is spawned) */\n getFSM(): any | null;\n /** Get the current options */\n getOptions(): O;\n /** Access the underlying behavior ref */\n readonly ref: BehaviorRef<O>;\n}\n\n/**\n * Reference to a behavior stored on an entity\n */\nexport interface BehaviorRef<\n O extends Record<string, any> = Record<string, any>,\n> {\n /** The behavior descriptor */\n descriptor: BehaviorDescriptor<O, any>;\n /** Merged options (defaults + overrides) */\n options: O;\n /** Optional FSM instance - set lazily when entity is spawned */\n fsm?: any;\n}\n\n/**\n * A typed behavior descriptor that associates a symbol key with:\n * - Default options (providing type inference)\n * - A system factory to create the behavior system\n * - An optional handle factory for behavior-specific methods\n */\nexport interface BehaviorDescriptor<\n O extends Record<string, any> = Record<string, any>,\n H extends Record<string, any> = Record<string, never>,\n I = unknown,\n> {\n /** Unique symbol identifying this behavior */\n readonly key: symbol;\n /** Default options (used for type inference) */\n readonly defaultOptions: O;\n /** Factory to create the behavior system */\n readonly systemFactory: BehaviorSystemFactory;\n /**\n * Optional factory to create behavior-specific handle methods.\n * These methods are merged into the handle returned by entity.use().\n */\n readonly createHandle?: (ref: BehaviorRef<O>) => H;\n}\n\n/**\n * The full handle type returned by entity.use().\n * Combines base handle with behavior-specific methods.\n */\nexport type BehaviorHandle<\n O extends Record<string, any> = Record<string, any>,\n H extends Record<string, any> = Record<string, never>,\n> = BaseBehaviorHandle<O> & H;\n\n/**\n * Configuration for defining a new behavior\n */\nexport interface DefineBehaviorConfig<\n O extends Record<string, any>,\n H extends Record<string, any> = Record<string, never>,\n I = unknown,\n> {\n /** Human-readable name for debugging */\n name: string;\n /** Default options - these define the type */\n defaultOptions: O;\n /** Factory function to create the system */\n systemFactory: BehaviorSystemFactory;\n /**\n * Optional factory to create behavior-specific handle methods.\n * The returned object is merged into the handle returned by entity.use().\n *\n * @example\n * ```typescript\n * createHandle: (ref) => ({\n * getLastHits: () => ref.fsm?.getLastHits() ?? null,\n * getMovement: (moveX, moveY) => ref.fsm?.getMovement(moveX, moveY) ?? { moveX, moveY },\n * }),\n * ```\n */\n createHandle?: (ref: BehaviorRef<O>) => H;\n}\n\n/**\n * Define a typed behavior descriptor.\n *\n * @example\n * ```typescript\n * export const WorldBoundary2DBehavior = defineBehavior({\n * name: 'world-boundary-2d',\n * defaultOptions: { boundaries: { top: 0, bottom: 0, left: 0, right: 0 } },\n * systemFactory: (ctx) => new WorldBoundary2DSystem(ctx.world),\n * createHandle: (ref) => ({\n * getLastHits: () => ref.fsm?.getLastHits() ?? null,\n * getMovement: (moveX: number, moveY: number) =>\n * ref.fsm?.getMovement(moveX, moveY) ?? { moveX, moveY },\n * }),\n * });\n *\n * // Usage - handle has getLastHits and getMovement with full types\n * const boundary = ship.use(WorldBoundary2DBehavior, { ... });\n * const hits = boundary.getLastHits(); // Fully typed!\n * ```\n */\nexport function defineBehavior<\n O extends Record<string, any>,\n H extends Record<string, any> = Record<string, never>,\n I = unknown,\n>(\n config: DefineBehaviorConfig<O, H, I>,\n): BehaviorDescriptor<O, H, I> {\n return {\n key: Symbol.for(`zylem:behavior:${config.name}`),\n defaultOptions: config.defaultOptions,\n systemFactory: config.systemFactory,\n createHandle: config.createHandle,\n };\n}\n","import {\n\tStateMachine as BaseStateMachine,\n\ttype ILogger,\n\ttype ITransition,\n\ttype SyncCallback,\n} from 'typescript-fsm';\n\nexport { t } from 'typescript-fsm';\nexport type { ILogger, ITransition, SyncCallback };\n\n/**\n * Local wrapper around typescript-fsm's SyncStateMachine.\n *\n * typescript-fsm@1.6.0 incorrectly reports callback-less transitions as\n * unhandled even after moving to the next state, which causes noisy\n * `No transition...` console errors for valid FSM dispatches.\n */\nexport class SyncStateMachine<\n\tSTATE extends string | number | symbol,\n\tEVENT extends string | number | symbol,\n\tCALLBACK extends Record<EVENT, SyncCallback> = Record<EVENT, SyncCallback>,\n> extends BaseStateMachine<STATE, EVENT, CALLBACK> {\n\tconstructor(\n\t\tinit: STATE,\n\t\ttransitions: ITransition<STATE, EVENT, CALLBACK[EVENT]>[] = [],\n\t\tlogger: ILogger = console,\n\t) {\n\t\tsuper(init, transitions, logger);\n\t}\n\n\tdispatch<E extends EVENT>(_event: E, ..._args: unknown[]): Promise<void> {\n\t\tthrow new Error('SyncStateMachine does not support async dispatch.');\n\t}\n\n\tsyncDispatch<E extends EVENT>(event: E, ...args: unknown[]): boolean {\n\t\tconst found = this.transitions.some((transition) => {\n\t\t\tif (transition.fromState !== this._current || transition.event !== event) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst current = this._current;\n\t\t\tthis._current = transition.toState;\n\n\t\t\tif (!transition.cb) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\ttransition.cb(...args);\n\t\t\t\treturn true;\n\t\t\t} catch (error) {\n\t\t\t\tthis._current = current;\n\t\t\t\tthis.logger.error('Exception in callback', error);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t});\n\n\t\tif (!found) {\n\t\t\tconst errorMessage = this.formatErr(this._current, event);\n\t\t\tthis.logger.error(errorMessage);\n\t\t}\n\n\t\treturn found;\n\t}\n}\n","export interface Bounds2DPoint {\n\tx: number;\n\ty: number;\n}\n\nexport interface Bounds2DRect {\n\tminX: number;\n\tmaxX: number;\n\tminY: number;\n\tmaxY: number;\n}\n\nexport interface Bounds2DSize {\n\twidth: number;\n\theight: number;\n\tcenterX?: number;\n\tcenterY?: number;\n}\n\nexport interface Bounds2DHits {\n\tleft: boolean;\n\tright: boolean;\n\ttop: boolean;\n\tbottom: boolean;\n}\n\nexport interface Bounds2DWrapResult {\n\tx: number;\n\ty: number;\n\twrapped: boolean;\n}\n\nexport function createBounds2DRect({\n\twidth,\n\theight,\n\tcenterX = 0,\n\tcenterY = 0,\n}: Bounds2DSize): Bounds2DRect {\n\tconst halfWidth = width / 2;\n\tconst halfHeight = height / 2;\n\treturn {\n\t\tminX: centerX - halfWidth,\n\t\tmaxX: centerX + halfWidth,\n\t\tminY: centerY - halfHeight,\n\t\tmaxY: centerY + halfHeight,\n\t};\n}\n\nexport function computeBounds2DHits(\n\tposition: Bounds2DPoint,\n\tbounds: Bounds2DRect,\n): Bounds2DHits {\n\tconst hits: Bounds2DHits = {\n\t\ttop: false,\n\t\tbottom: false,\n\t\tleft: false,\n\t\tright: false,\n\t};\n\n\tif (position.x <= bounds.minX) hits.left = true;\n\telse if (position.x >= bounds.maxX) hits.right = true;\n\n\tif (position.y <= bounds.minY) hits.bottom = true;\n\telse if (position.y >= bounds.maxY) hits.top = true;\n\n\treturn hits;\n}\n\nexport function hasAnyBounds2DHit(hits: Bounds2DHits): boolean {\n\treturn hits.left || hits.right || hits.top || hits.bottom;\n}\n\nexport function constrainMovementToBounds2D(\n\thits: Bounds2DHits,\n\tmoveX: number,\n\tmoveY: number,\n): { moveX: number; moveY: number } {\n\tlet adjustedX = moveX;\n\tlet adjustedY = moveY;\n\n\tif ((hits.left && moveX < 0) || (hits.right && moveX > 0)) {\n\t\tadjustedX = 0;\n\t}\n\n\tif ((hits.bottom && moveY < 0) || (hits.top && moveY > 0)) {\n\t\tadjustedY = 0;\n\t}\n\n\treturn { moveX: adjustedX, moveY: adjustedY };\n}\n\nexport function wrapPoint2D(\n\tposition: Bounds2DPoint,\n\tbounds: Bounds2DRect,\n): Bounds2DWrapResult {\n\tlet x = position.x;\n\tlet y = position.y;\n\tlet wrapped = false;\n\n\tif (position.x < bounds.minX) {\n\t\tx = bounds.maxX - (bounds.minX - position.x);\n\t\twrapped = true;\n\t} else if (position.x > bounds.maxX) {\n\t\tx = bounds.minX + (position.x - bounds.maxX);\n\t\twrapped = true;\n\t}\n\n\tif (position.y < bounds.minY) {\n\t\ty = bounds.maxY - (bounds.minY - position.y);\n\t\twrapped = true;\n\t} else if (position.y > bounds.maxY) {\n\t\ty = bounds.minY + (position.y - bounds.maxY);\n\t\twrapped = true;\n\t}\n\n\treturn { x, y, wrapped };\n}\n\nexport function getBounds2DNormalFromHits(\n\thits: Bounds2DHits,\n): { x: number; y: number } {\n\tlet x = 0;\n\tlet y = 0;\n\n\tif (hits.left) x = 1;\n\tif (hits.right) x = -1;\n\tif (hits.bottom) y = 1;\n\tif (hits.top) y = -1;\n\n\treturn { x, y };\n}\n","/**\n * WorldBoundary2DFSM\n *\n * Minimal FSM + extended state to track which world boundaries were hit.\n *\n * Notes:\n * - \"Hit boundaries\" is inherently a *set* (can hit left+bottom in one frame),\n * so we store it as extended state (`lastHits`) rather than a single FSM state.\n * - The FSM state is still useful for coarse status like \"inside\" vs \"touching\".\n */\n\nimport { SyncStateMachine, t } from '../../core/utility/sync-state-machine';\nimport {\n\tcomputeBounds2DHits,\n\tconstrainMovementToBounds2D,\n\thasAnyBounds2DHit,\n} from '../shared/bounds-2d';\n\nexport type WorldBoundary2DHit = 'top' | 'bottom' | 'left' | 'right';\nexport type WorldBoundary2DHits = Record<WorldBoundary2DHit, boolean>;\n\nexport interface WorldBoundary2DPosition {\n\tx: number;\n\ty: number;\n}\n\nexport interface WorldBoundary2DBounds {\n\ttop: number;\n\tbottom: number;\n\tleft: number;\n\tright: number;\n}\n\nexport enum WorldBoundary2DState {\n\tInside = 'inside',\n\tTouching = 'touching',\n}\n\nexport enum WorldBoundary2DEvent {\n\tEnterInside = 'enter-inside',\n\tTouchBoundary = 'touch-boundary',\n}\n\n/**\n * Compute which boundaries are being hit for a position and bounds.\n * This matches the semantics of the legacy `boundary2d` behavior:\n * - left hit if x <= left\n * - right hit if x >= right\n * - bottom hit if y <= bottom\n * - top hit if y >= top\n */\nexport function computeWorldBoundary2DHits(\n\tposition: WorldBoundary2DPosition,\n\tbounds: WorldBoundary2DBounds\n): WorldBoundary2DHits {\n\treturn computeBounds2DHits(position, {\n\t\tminX: bounds.left,\n\t\tmaxX: bounds.right,\n\t\tminY: bounds.bottom,\n\t\tmaxY: bounds.top,\n\t});\n}\n\nexport function hasAnyWorldBoundary2DHit(hits: WorldBoundary2DHits): boolean {\n\treturn hasAnyBounds2DHit(hits);\n}\n\n/**\n * FSM wrapper with \"extended state\" (lastHits / lastPosition).\n * Systems should call `update(...)` once per frame.\n */\nexport class WorldBoundary2DFSM {\n\tpublic readonly machine: SyncStateMachine<WorldBoundary2DState, WorldBoundary2DEvent, never>;\n\n\tprivate lastHits: WorldBoundary2DHits = { top: false, bottom: false, left: false, right: false };\n\tprivate lastPosition: WorldBoundary2DPosition | null = null;\n\tprivate lastUpdatedAtMs: number | null = null;\n\n\tconstructor() {\n\t\tthis.machine = new SyncStateMachine<WorldBoundary2DState, WorldBoundary2DEvent, never>(\n\t\t\tWorldBoundary2DState.Inside,\n\t\t\t[\n\t\t\t\tt(WorldBoundary2DState.Inside, WorldBoundary2DEvent.TouchBoundary, WorldBoundary2DState.Touching),\n\t\t\t\tt(WorldBoundary2DState.Touching, WorldBoundary2DEvent.EnterInside, WorldBoundary2DState.Inside),\n\n\t\t\t\t// Self transitions (no-ops)\n\t\t\t\tt(WorldBoundary2DState.Inside, WorldBoundary2DEvent.EnterInside, WorldBoundary2DState.Inside),\n\t\t\t\tt(WorldBoundary2DState.Touching, WorldBoundary2DEvent.TouchBoundary, WorldBoundary2DState.Touching),\n\t\t\t]\n\t\t);\n\t}\n\n\tgetState(): WorldBoundary2DState {\n\t\treturn this.machine.getState();\n\t}\n\n\t/**\n\t * Returns the last computed hits (always available after first update call).\n\t */\n\tgetLastHits(): WorldBoundary2DHits {\n\t\treturn this.lastHits;\n\t}\n\n\t/**\n\t * Returns adjusted movement values based on boundary hits.\n\t * If the entity is touching a boundary and trying to move further into it,\n\t * that axis component is zeroed out.\n\t *\n\t * @param moveX - The desired X movement\n\t * @param moveY - The desired Y movement\n\t * @returns Adjusted { moveX, moveY } with boundary-blocked axes zeroed\n\t */\n\tgetMovement(moveX: number, moveY: number): { moveX: number; moveY: number } {\n\t\treturn constrainMovementToBounds2D(this.lastHits, moveX, moveY);\n\t}\n\n\t/**\n\t * Returns the last position passed to `update`, if any.\n\t */\n\tgetLastPosition(): WorldBoundary2DPosition | null {\n\t\treturn this.lastPosition;\n\t}\n\n\t/**\n\t * Best-effort timestamp (ms) of the last `update(...)` call.\n\t * This is optional metadata; systems can ignore it.\n\t */\n\tgetLastUpdatedAtMs(): number | null {\n\t\treturn this.lastUpdatedAtMs;\n\t}\n\n\t/**\n\t * Update FSM + extended state based on current position and bounds.\n\t * Returns the computed hits for convenience.\n\t */\n\tupdate(position: WorldBoundary2DPosition, bounds: WorldBoundary2DBounds): WorldBoundary2DHits {\n\t\tconst hits = computeWorldBoundary2DHits(position, bounds);\n\n\t\tthis.lastHits = hits;\n\t\tthis.lastPosition = { x: position.x, y: position.y };\n\t\tthis.lastUpdatedAtMs = Date.now();\n\n\t\tif (hasAnyWorldBoundary2DHit(hits)) {\n\t\t\tthis.dispatch(WorldBoundary2DEvent.TouchBoundary);\n\t\t} else {\n\t\t\tthis.dispatch(WorldBoundary2DEvent.EnterInside);\n\t\t}\n\n\t\treturn hits;\n\t}\n\n\tprivate dispatch(event: WorldBoundary2DEvent): void {\n\t\tif (this.machine.can(event)) {\n\t\t\tthis.machine.syncDispatch(event);\n\t\t}\n\t}\n}\n","/**\n * WorldBoundary2DBehavior\n *\n * Tracks which 2D world boundaries an entity is touching.\n * Use `getMovement()` on the behavior handle to adjust movement based on hits.\n *\n * Source of truth: `entity.body` (Rapier rigid body), consistent with other new behaviors.\n */\n\nimport type { IWorld } from 'bitecs';\nimport { defineBehavior, type BehaviorRef } from '../behavior-descriptor';\nimport type { BehaviorEntityLink, BehaviorSystem } from '../behavior-system';\nimport {\n\tWorldBoundary2DFSM,\n\ttype WorldBoundary2DBounds,\n\ttype WorldBoundary2DHits,\n} from './world-boundary-2d-fsm';\n\nexport interface WorldBoundary2DOptions {\n\t/**\n\t * World boundaries (in world units).\n\t * - left hit if x <= left\n\t * - right hit if x >= right\n\t * - bottom hit if y <= bottom\n\t * - top hit if y >= top\n\t */\n\tboundaries: WorldBoundary2DBounds;\n}\n\n/**\n * Handle methods provided by WorldBoundary2DBehavior\n */\nexport interface WorldBoundary2DHandle {\n\t/**\n\t * Get the last computed boundary hits.\n\t * Returns null until entity is spawned and FSM is initialized.\n\t */\n\tgetLastHits(): WorldBoundary2DHits | null;\n\n\t/**\n\t * Get adjusted movement values based on boundary hits.\n\t * Zeros out movement into boundaries the entity is touching.\n\t */\n\tgetMovement(moveX: number, moveY: number): { moveX: number; moveY: number };\n}\n\nconst defaultOptions: WorldBoundary2DOptions = {\n\tboundaries: { top: 0, bottom: 0, left: 0, right: 0 },\n};\n\nconst WORLD_BOUNDARY_BEHAVIOR_KEY = Symbol.for(\n\t'zylem:behavior:world-boundary-2d',\n);\n\n/**\n * Creates behavior-specific handle methods for WorldBoundary2DBehavior.\n */\nfunction createWorldBoundary2DHandle(\n\tref: BehaviorRef<WorldBoundary2DOptions>\n): WorldBoundary2DHandle {\n\treturn {\n\t\tgetLastHits: () => {\n\t\t\tconst fsm = ref.fsm as WorldBoundary2DFSM | undefined;\n\t\t\treturn fsm?.getLastHits() ?? null;\n\t\t},\n\t\tgetMovement: (moveX: number, moveY: number) => {\n\t\t\tconst fsm = ref.fsm as WorldBoundary2DFSM | undefined;\n\t\t\treturn fsm?.getMovement(moveX, moveY) ?? { moveX, moveY };\n\t\t},\n\t};\n}\n\n/**\n * WorldBoundary2DSystem\n *\n * Stage-level system that:\n * - finds entities with this behavior attached\n * - computes and tracks boundary hits using the FSM\n */\nclass WorldBoundary2DSystem implements BehaviorSystem {\n\tconstructor(\n\t\tprivate world: any,\n\t\tprivate getBehaviorLinks?: (key: symbol) => Iterable<BehaviorEntityLink>,\n\t) {}\n\n\tupdate(_ecs: IWorld, _delta: number): void {\n\t\tconst links = this.getBehaviorLinks?.(WORLD_BOUNDARY_BEHAVIOR_KEY);\n\t\tif (!links) return;\n\n\t\tfor (const link of links) {\n\t\t\tconst gameEntity = link.entity as any;\n\t\t\tconst boundaryRef = link.ref as any;\n\t\t\tif (!gameEntity.body) continue;\n\n\t\t\tconst options = boundaryRef.options as WorldBoundary2DOptions;\n\n\t\t\t// Create FSM lazily on first update after spawn\n\t\t\tif (!boundaryRef.fsm) {\n\t\t\t\tboundaryRef.fsm = new WorldBoundary2DFSM();\n\t\t\t}\n\n\t\t\tconst body = gameEntity.body;\n\t\t\tconst pos = body.translation();\n\n\t\t\t// Update FSM with current position - consumers use getMovement() to act on hits\n\t\t\tboundaryRef.fsm.update(\n\t\t\t\t{ x: pos.x, y: pos.y },\n\t\t\t\toptions.boundaries\n\t\t\t);\n\t\t}\n\t}\n\n\tdestroy(_ecs: IWorld): void {\n\t\t// No explicit cleanup required (FSMs are stored on behavior refs)\n\t}\n}\n\n/**\n * WorldBoundary2DBehavior\n *\n * @example\n * ```ts\n * import { WorldBoundary2DBehavior } from \"@zylem/game-lib\";\n *\n * const ship = createSprite({ ... });\n * const boundary = ship.use(WorldBoundary2DBehavior, {\n * boundaries: { left: -10, right: 10, bottom: -7.5, top: 7.5 },\n * });\n *\n * ship.onUpdate(({ me }) => {\n * let moveX = ..., moveY = ...;\n * const hits = boundary.getLastHits(); // Fully typed!\n * ({ moveX, moveY } = boundary.getMovement(moveX, moveY));\n * me.moveXY(moveX, moveY);\n * });\n * ```\n */\nexport const WorldBoundary2DBehavior = defineBehavior({\n\tname: 'world-boundary-2d',\n\tdefaultOptions,\n\tsystemFactory: (ctx) =>\n\t\tnew WorldBoundary2DSystem(ctx.world, ctx.getBehaviorLinks),\n\tcreateHandle: createWorldBoundary2DHandle,\n});\n"],"mappings":"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","names":["WorldBoundary2DState","WorldBoundary2DEvent"]}
@@ -0,0 +1,76 @@
1
+ import { c as BehaviorDescriptor } from '../behavior-descriptor-BXnVR8Ki.js';
2
+ import { S as SyncStateMachine } from '../sync-state-machine-CZyspBpj.js';
3
+ import 'bitecs';
4
+ import 'typescript-fsm';
5
+
6
+ interface Bounds3DPoint {
7
+ x: number;
8
+ y: number;
9
+ z: number;
10
+ }
11
+ interface Bounds3DPadding {
12
+ x: number;
13
+ y: number;
14
+ z: number;
15
+ }
16
+ type Bounds3DPaddingInput = number | Partial<Bounds3DPadding> | undefined;
17
+
18
+ type WorldBoundary3DHit = 'top' | 'bottom' | 'left' | 'right' | 'back' | 'front';
19
+ type WorldBoundary3DHits = Record<WorldBoundary3DHit, boolean>;
20
+ interface WorldBoundary3DPosition extends Bounds3DPoint {
21
+ }
22
+ interface WorldBoundary3DBounds {
23
+ top: number;
24
+ bottom: number;
25
+ left: number;
26
+ right: number;
27
+ back: number;
28
+ front: number;
29
+ }
30
+ declare enum WorldBoundary3DState {
31
+ Inside = "inside",
32
+ Touching = "touching"
33
+ }
34
+ declare enum WorldBoundary3DEvent {
35
+ EnterInside = "enter-inside",
36
+ TouchBoundary = "touch-boundary"
37
+ }
38
+ declare function computeWorldBoundary3DHits(position: WorldBoundary3DPosition, bounds: WorldBoundary3DBounds, padding?: Bounds3DPaddingInput): WorldBoundary3DHits;
39
+ declare function hasAnyWorldBoundary3DHit(hits: WorldBoundary3DHits): boolean;
40
+ declare class WorldBoundary3DFSM {
41
+ readonly machine: SyncStateMachine<WorldBoundary3DState, WorldBoundary3DEvent, never>;
42
+ private lastHits;
43
+ private lastPosition;
44
+ private lastClampedPosition;
45
+ private lastUpdatedAtMs;
46
+ constructor();
47
+ getState(): WorldBoundary3DState;
48
+ getLastHits(): WorldBoundary3DHits;
49
+ getMovement(moveX: number, moveY: number, moveZ: number): {
50
+ moveX: number;
51
+ moveY: number;
52
+ moveZ: number;
53
+ };
54
+ getLastPosition(): WorldBoundary3DPosition | null;
55
+ getLastClampedPosition(): WorldBoundary3DPosition | null;
56
+ getLastUpdatedAtMs(): number | null;
57
+ update(position: WorldBoundary3DPosition, bounds: WorldBoundary3DBounds, padding?: Bounds3DPaddingInput): WorldBoundary3DHits;
58
+ private dispatch;
59
+ }
60
+
61
+ interface WorldBoundary3DOptions {
62
+ boundaries: WorldBoundary3DBounds;
63
+ padding?: Bounds3DPaddingInput;
64
+ }
65
+ interface WorldBoundary3DHandle {
66
+ getLastHits(): WorldBoundary3DHits | null;
67
+ getMovement(moveX: number, moveY: number, moveZ: number): {
68
+ moveX: number;
69
+ moveY: number;
70
+ moveZ: number;
71
+ };
72
+ getLastClampedPosition(): WorldBoundary3DPosition | null;
73
+ }
74
+ declare const WorldBoundary3DBehavior: BehaviorDescriptor<WorldBoundary3DOptions, WorldBoundary3DHandle, unknown>;
75
+
76
+ export { WorldBoundary3DBehavior, type WorldBoundary3DBounds, WorldBoundary3DEvent, WorldBoundary3DFSM, type WorldBoundary3DHandle, type WorldBoundary3DHit, type WorldBoundary3DHits, type WorldBoundary3DOptions, type WorldBoundary3DPosition, WorldBoundary3DState, computeWorldBoundary3DHits, hasAnyWorldBoundary3DHit };