@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.
- package/LICENSE +21 -0
- package/README.md +9 -16
- package/dist/actions.d.ts +30 -21
- package/dist/actions.js +793 -146
- package/dist/actions.js.map +1 -1
- package/dist/behavior/jumper-2d.d.ts +114 -0
- package/dist/behavior/jumper-2d.js +711 -0
- package/dist/behavior/jumper-2d.js.map +1 -0
- package/dist/behavior/platformer-3d.d.ts +296 -0
- package/dist/behavior/platformer-3d.js +761 -0
- package/dist/behavior/platformer-3d.js.map +1 -0
- package/dist/behavior/ricochet-2d.d.ts +275 -0
- package/dist/behavior/ricochet-2d.js +425 -0
- package/dist/behavior/ricochet-2d.js.map +1 -0
- package/dist/behavior/ricochet-3d.d.ts +117 -0
- package/dist/behavior/ricochet-3d.js +443 -0
- package/dist/behavior/ricochet-3d.js.map +1 -0
- package/dist/behavior/screen-visibility.d.ts +79 -0
- package/dist/behavior/screen-visibility.js +358 -0
- package/dist/behavior/screen-visibility.js.map +1 -0
- package/dist/behavior/screen-wrap.d.ts +87 -0
- package/dist/behavior/screen-wrap.js +246 -0
- package/dist/behavior/screen-wrap.js.map +1 -0
- package/dist/behavior/shooter-2d.d.ts +79 -0
- package/dist/behavior/shooter-2d.js +180 -0
- package/dist/behavior/shooter-2d.js.map +1 -0
- package/dist/behavior/thruster.d.ts +11 -0
- package/dist/behavior/thruster.js +292 -0
- package/dist/behavior/thruster.js.map +1 -0
- package/dist/behavior/top-down-movement.d.ts +56 -0
- package/dist/behavior/top-down-movement.js +125 -0
- package/dist/behavior/top-down-movement.js.map +1 -0
- package/dist/behavior/world-boundary-2d.d.ts +142 -0
- package/dist/behavior/world-boundary-2d.js +235 -0
- package/dist/behavior/world-boundary-2d.js.map +1 -0
- package/dist/behavior/world-boundary-3d.d.ts +76 -0
- package/dist/behavior/world-boundary-3d.js +274 -0
- package/dist/behavior/world-boundary-3d.js.map +1 -0
- package/dist/behavior-descriptor-BXnVR8Ki.d.ts +159 -0
- package/dist/{blueprints-Cq3Ko6_G.d.ts → blueprints-DmbK2dki.d.ts} +2 -2
- package/dist/camera-4XO5gbQH.d.ts +905 -0
- package/dist/camera.d.ts +3 -2
- package/dist/camera.js +1653 -377
- package/dist/camera.js.map +1 -1
- package/dist/composition-BASvMKrW.d.ts +218 -0
- package/dist/{core-bO8TzV7u.d.ts → core-CARRaS55.d.ts} +110 -69
- package/dist/core.d.ts +11 -6
- package/dist/core.js +10766 -5626
- package/dist/core.js.map +1 -1
- package/dist/{entities-DvByhMGU.d.ts → entities-ChFirVL9.d.ts} +133 -29
- package/dist/entities.d.ts +5 -3
- package/dist/entities.js +4679 -3202
- package/dist/entities.js.map +1 -1
- package/dist/entity-vj-HTjzU.d.ts +1169 -0
- package/dist/global-change-2JvMaz44.d.ts +25 -0
- package/dist/main.d.ts +1118 -16
- package/dist/main.js +17538 -8499
- package/dist/main.js.map +1 -1
- package/dist/physics-pose-DCc4oE44.d.ts +25 -0
- package/dist/physics-protocol-BDD3P5W2.d.ts +200 -0
- package/dist/physics-worker.d.ts +21 -0
- package/dist/physics-worker.js +306 -0
- package/dist/physics-worker.js.map +1 -0
- package/dist/physics.d.ts +205 -0
- package/dist/physics.js +577 -0
- package/dist/physics.js.map +1 -0
- package/dist/stage-types-C19IhuzA.d.ts +731 -0
- package/dist/stage.d.ts +11 -7
- package/dist/stage.js +8024 -3852
- package/dist/stage.js.map +1 -1
- package/dist/sync-state-machine-CZyspBpj.d.ts +16 -0
- package/dist/thruster-23lzoPZd.d.ts +180 -0
- package/dist/world-DfgxoNMt.d.ts +105 -0
- package/package.json +53 -13
- package/dist/behaviors.d.ts +0 -854
- package/dist/behaviors.js +0 -1209
- package/dist/behaviors.js.map +0 -1
- package/dist/camera-CeJPAgGg.d.ts +0 -116
- package/dist/moveable-B_vyA6cw.d.ts +0 -67
- package/dist/stage-types-Bd-KtcYT.d.ts +0 -375
- package/dist/transformable-CUhvyuYO.d.ts +0 -67
- package/dist/world-C8tQ7Plj.d.ts +0 -774
|
@@ -0,0 +1,274 @@
|
|
|
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-3d.ts
|
|
51
|
+
function normalizeBounds3DPadding(padding) {
|
|
52
|
+
if (typeof padding === "number") {
|
|
53
|
+
return { x: padding, y: padding, z: padding };
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
x: padding?.x ?? 0,
|
|
57
|
+
y: padding?.y ?? 0,
|
|
58
|
+
z: padding?.z ?? 0
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function insetBounds3D(bounds, padding) {
|
|
62
|
+
const resolved = normalizeBounds3DPadding(padding);
|
|
63
|
+
return {
|
|
64
|
+
minX: bounds.minX + resolved.x,
|
|
65
|
+
maxX: bounds.maxX - resolved.x,
|
|
66
|
+
minY: bounds.minY + resolved.y,
|
|
67
|
+
maxY: bounds.maxY - resolved.y,
|
|
68
|
+
minZ: bounds.minZ + resolved.z,
|
|
69
|
+
maxZ: bounds.maxZ - resolved.z
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function computeBounds3DHits(position, bounds) {
|
|
73
|
+
const hits = {
|
|
74
|
+
left: false,
|
|
75
|
+
right: false,
|
|
76
|
+
top: false,
|
|
77
|
+
bottom: false,
|
|
78
|
+
back: false,
|
|
79
|
+
front: false
|
|
80
|
+
};
|
|
81
|
+
if (position.x <= bounds.minX) hits.left = true;
|
|
82
|
+
else if (position.x >= bounds.maxX) hits.right = true;
|
|
83
|
+
if (position.y <= bounds.minY) hits.bottom = true;
|
|
84
|
+
else if (position.y >= bounds.maxY) hits.top = true;
|
|
85
|
+
if (position.z <= bounds.minZ) hits.back = true;
|
|
86
|
+
else if (position.z >= bounds.maxZ) hits.front = true;
|
|
87
|
+
return hits;
|
|
88
|
+
}
|
|
89
|
+
function hasAnyBounds3DHit(hits) {
|
|
90
|
+
return hits.left || hits.right || hits.top || hits.bottom || hits.back || hits.front;
|
|
91
|
+
}
|
|
92
|
+
function constrainMovementToBounds3D(hits, moveX, moveY, moveZ) {
|
|
93
|
+
let adjustedX = moveX;
|
|
94
|
+
let adjustedY = moveY;
|
|
95
|
+
let adjustedZ = moveZ;
|
|
96
|
+
if (hits.left && moveX < 0 || hits.right && moveX > 0) {
|
|
97
|
+
adjustedX = 0;
|
|
98
|
+
}
|
|
99
|
+
if (hits.bottom && moveY < 0 || hits.top && moveY > 0) {
|
|
100
|
+
adjustedY = 0;
|
|
101
|
+
}
|
|
102
|
+
if (hits.back && moveZ < 0 || hits.front && moveZ > 0) {
|
|
103
|
+
adjustedZ = 0;
|
|
104
|
+
}
|
|
105
|
+
return { moveX: adjustedX, moveY: adjustedY, moveZ: adjustedZ };
|
|
106
|
+
}
|
|
107
|
+
function clampPointToBounds3D(position, bounds) {
|
|
108
|
+
return {
|
|
109
|
+
x: Math.min(bounds.maxX, Math.max(bounds.minX, position.x)),
|
|
110
|
+
y: Math.min(bounds.maxY, Math.max(bounds.minY, position.y)),
|
|
111
|
+
z: Math.min(bounds.maxZ, Math.max(bounds.minZ, position.z))
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// src/lib/behaviors/world-boundary-3d/world-boundary-3d-fsm.ts
|
|
116
|
+
var WorldBoundary3DState = /* @__PURE__ */ ((WorldBoundary3DState2) => {
|
|
117
|
+
WorldBoundary3DState2["Inside"] = "inside";
|
|
118
|
+
WorldBoundary3DState2["Touching"] = "touching";
|
|
119
|
+
return WorldBoundary3DState2;
|
|
120
|
+
})(WorldBoundary3DState || {});
|
|
121
|
+
var WorldBoundary3DEvent = /* @__PURE__ */ ((WorldBoundary3DEvent2) => {
|
|
122
|
+
WorldBoundary3DEvent2["EnterInside"] = "enter-inside";
|
|
123
|
+
WorldBoundary3DEvent2["TouchBoundary"] = "touch-boundary";
|
|
124
|
+
return WorldBoundary3DEvent2;
|
|
125
|
+
})(WorldBoundary3DEvent || {});
|
|
126
|
+
function computeWorldBoundary3DHits(position, bounds, padding) {
|
|
127
|
+
return computeBounds3DHits(position, insetBounds3D({
|
|
128
|
+
minX: bounds.left,
|
|
129
|
+
maxX: bounds.right,
|
|
130
|
+
minY: bounds.bottom,
|
|
131
|
+
maxY: bounds.top,
|
|
132
|
+
minZ: bounds.back,
|
|
133
|
+
maxZ: bounds.front
|
|
134
|
+
}, padding));
|
|
135
|
+
}
|
|
136
|
+
function hasAnyWorldBoundary3DHit(hits) {
|
|
137
|
+
return hasAnyBounds3DHit(hits);
|
|
138
|
+
}
|
|
139
|
+
var WorldBoundary3DFSM = class {
|
|
140
|
+
machine;
|
|
141
|
+
lastHits = {
|
|
142
|
+
top: false,
|
|
143
|
+
bottom: false,
|
|
144
|
+
left: false,
|
|
145
|
+
right: false,
|
|
146
|
+
back: false,
|
|
147
|
+
front: false
|
|
148
|
+
};
|
|
149
|
+
lastPosition = null;
|
|
150
|
+
lastClampedPosition = null;
|
|
151
|
+
lastUpdatedAtMs = null;
|
|
152
|
+
constructor() {
|
|
153
|
+
this.machine = new SyncStateMachine(
|
|
154
|
+
"inside" /* Inside */,
|
|
155
|
+
[
|
|
156
|
+
t("inside" /* Inside */, "touch-boundary" /* TouchBoundary */, "touching" /* Touching */),
|
|
157
|
+
t("touching" /* Touching */, "enter-inside" /* EnterInside */, "inside" /* Inside */),
|
|
158
|
+
t("inside" /* Inside */, "enter-inside" /* EnterInside */, "inside" /* Inside */),
|
|
159
|
+
t("touching" /* Touching */, "touch-boundary" /* TouchBoundary */, "touching" /* Touching */)
|
|
160
|
+
]
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
getState() {
|
|
164
|
+
return this.machine.getState();
|
|
165
|
+
}
|
|
166
|
+
getLastHits() {
|
|
167
|
+
return this.lastHits;
|
|
168
|
+
}
|
|
169
|
+
getMovement(moveX, moveY, moveZ) {
|
|
170
|
+
return constrainMovementToBounds3D(this.lastHits, moveX, moveY, moveZ);
|
|
171
|
+
}
|
|
172
|
+
getLastPosition() {
|
|
173
|
+
return this.lastPosition;
|
|
174
|
+
}
|
|
175
|
+
getLastClampedPosition() {
|
|
176
|
+
return this.lastClampedPosition;
|
|
177
|
+
}
|
|
178
|
+
getLastUpdatedAtMs() {
|
|
179
|
+
return this.lastUpdatedAtMs;
|
|
180
|
+
}
|
|
181
|
+
update(position, bounds, padding) {
|
|
182
|
+
const effectiveBounds = insetBounds3D({
|
|
183
|
+
minX: bounds.left,
|
|
184
|
+
maxX: bounds.right,
|
|
185
|
+
minY: bounds.bottom,
|
|
186
|
+
maxY: bounds.top,
|
|
187
|
+
minZ: bounds.back,
|
|
188
|
+
maxZ: bounds.front
|
|
189
|
+
}, padding);
|
|
190
|
+
const hits = computeBounds3DHits(position, effectiveBounds);
|
|
191
|
+
this.lastHits = hits;
|
|
192
|
+
this.lastPosition = { x: position.x, y: position.y, z: position.z };
|
|
193
|
+
this.lastClampedPosition = clampPointToBounds3D(position, effectiveBounds);
|
|
194
|
+
this.lastUpdatedAtMs = Date.now();
|
|
195
|
+
if (hasAnyWorldBoundary3DHit(hits)) {
|
|
196
|
+
this.dispatch("touch-boundary" /* TouchBoundary */);
|
|
197
|
+
} else {
|
|
198
|
+
this.dispatch("enter-inside" /* EnterInside */);
|
|
199
|
+
}
|
|
200
|
+
return hits;
|
|
201
|
+
}
|
|
202
|
+
dispatch(event) {
|
|
203
|
+
if (this.machine.can(event)) {
|
|
204
|
+
this.machine.syncDispatch(event);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
// src/lib/behaviors/world-boundary-3d/world-boundary-3d.descriptor.ts
|
|
210
|
+
var defaultOptions = {
|
|
211
|
+
boundaries: { top: 0, bottom: 0, left: 0, right: 0, back: 0, front: 0 },
|
|
212
|
+
padding: 0
|
|
213
|
+
};
|
|
214
|
+
var WORLD_BOUNDARY_3D_BEHAVIOR_KEY = /* @__PURE__ */ Symbol.for(
|
|
215
|
+
"zylem:behavior:world-boundary-3d"
|
|
216
|
+
);
|
|
217
|
+
function createWorldBoundary3DHandle(ref) {
|
|
218
|
+
return {
|
|
219
|
+
getLastHits: () => {
|
|
220
|
+
const fsm = ref.fsm;
|
|
221
|
+
return fsm?.getLastHits() ?? null;
|
|
222
|
+
},
|
|
223
|
+
getMovement: (moveX, moveY, moveZ) => {
|
|
224
|
+
const fsm = ref.fsm;
|
|
225
|
+
return fsm?.getMovement(moveX, moveY, moveZ) ?? { moveX, moveY, moveZ };
|
|
226
|
+
},
|
|
227
|
+
getLastClampedPosition: () => {
|
|
228
|
+
const fsm = ref.fsm;
|
|
229
|
+
return fsm?.getLastClampedPosition() ?? null;
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
var WorldBoundary3DSystem = class {
|
|
234
|
+
constructor(world, getBehaviorLinks) {
|
|
235
|
+
this.world = world;
|
|
236
|
+
this.getBehaviorLinks = getBehaviorLinks;
|
|
237
|
+
}
|
|
238
|
+
update(_ecs, _delta) {
|
|
239
|
+
const links = this.getBehaviorLinks?.(WORLD_BOUNDARY_3D_BEHAVIOR_KEY);
|
|
240
|
+
if (!links) return;
|
|
241
|
+
for (const link of links) {
|
|
242
|
+
const gameEntity = link.entity;
|
|
243
|
+
const boundaryRef = link.ref;
|
|
244
|
+
if (!gameEntity.body) continue;
|
|
245
|
+
const options = boundaryRef.options;
|
|
246
|
+
if (!boundaryRef.fsm) {
|
|
247
|
+
boundaryRef.fsm = new WorldBoundary3DFSM();
|
|
248
|
+
}
|
|
249
|
+
const pos = gameEntity.body.translation();
|
|
250
|
+
boundaryRef.fsm.update(
|
|
251
|
+
{ x: pos.x, y: pos.y, z: pos.z },
|
|
252
|
+
options.boundaries,
|
|
253
|
+
options.padding
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
destroy(_ecs) {
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
var WorldBoundary3DBehavior = defineBehavior({
|
|
261
|
+
name: "world-boundary-3d",
|
|
262
|
+
defaultOptions,
|
|
263
|
+
systemFactory: (ctx) => new WorldBoundary3DSystem(ctx.world, ctx.getBehaviorLinks),
|
|
264
|
+
createHandle: createWorldBoundary3DHandle
|
|
265
|
+
});
|
|
266
|
+
export {
|
|
267
|
+
WorldBoundary3DBehavior,
|
|
268
|
+
WorldBoundary3DEvent,
|
|
269
|
+
WorldBoundary3DFSM,
|
|
270
|
+
WorldBoundary3DState,
|
|
271
|
+
computeWorldBoundary3DHits,
|
|
272
|
+
hasAnyWorldBoundary3DHit
|
|
273
|
+
};
|
|
274
|
+
//# sourceMappingURL=world-boundary-3d.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-3d.ts","../../src/lib/behaviors/world-boundary-3d/world-boundary-3d-fsm.ts","../../src/lib/behaviors/world-boundary-3d/world-boundary-3d.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 Bounds3DPoint {\n\tx: number;\n\ty: number;\n\tz: number;\n}\n\nexport interface Bounds3DBox {\n\tminX: number;\n\tmaxX: number;\n\tminY: number;\n\tmaxY: number;\n\tminZ: number;\n\tmaxZ: number;\n}\n\nexport interface Bounds3DSize {\n\twidth: number;\n\theight: number;\n\tdepth: number;\n\tcenterX?: number;\n\tcenterY?: number;\n\tcenterZ?: number;\n}\n\nexport interface Bounds3DHits {\n\tleft: boolean;\n\tright: boolean;\n\ttop: boolean;\n\tbottom: boolean;\n\tback: boolean;\n\tfront: boolean;\n}\n\nexport interface Bounds3DPadding {\n\tx: number;\n\ty: number;\n\tz: number;\n}\n\nexport type Bounds3DPaddingInput =\n\t| number\n\t| Partial<Bounds3DPadding>\n\t| undefined;\n\nexport function createBounds3DBox({\n\twidth,\n\theight,\n\tdepth,\n\tcenterX = 0,\n\tcenterY = 0,\n\tcenterZ = 0,\n}: Bounds3DSize): Bounds3DBox {\n\tconst halfWidth = width / 2;\n\tconst halfHeight = height / 2;\n\tconst halfDepth = depth / 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\tminZ: centerZ - halfDepth,\n\t\tmaxZ: centerZ + halfDepth,\n\t};\n}\n\nexport function normalizeBounds3DPadding(\n\tpadding?: Bounds3DPaddingInput,\n): Bounds3DPadding {\n\tif (typeof padding === 'number') {\n\t\treturn { x: padding, y: padding, z: padding };\n\t}\n\n\treturn {\n\t\tx: padding?.x ?? 0,\n\t\ty: padding?.y ?? 0,\n\t\tz: padding?.z ?? 0,\n\t};\n}\n\nexport function insetBounds3D(\n\tbounds: Bounds3DBox,\n\tpadding?: Bounds3DPaddingInput,\n): Bounds3DBox {\n\tconst resolved = normalizeBounds3DPadding(padding);\n\treturn {\n\t\tminX: bounds.minX + resolved.x,\n\t\tmaxX: bounds.maxX - resolved.x,\n\t\tminY: bounds.minY + resolved.y,\n\t\tmaxY: bounds.maxY - resolved.y,\n\t\tminZ: bounds.minZ + resolved.z,\n\t\tmaxZ: bounds.maxZ - resolved.z,\n\t};\n}\n\nexport function computeBounds3DHits(\n\tposition: Bounds3DPoint,\n\tbounds: Bounds3DBox,\n): Bounds3DHits {\n\tconst hits: Bounds3DHits = {\n\t\tleft: false,\n\t\tright: false,\n\t\ttop: false,\n\t\tbottom: false,\n\t\tback: false,\n\t\tfront: 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\tif (position.z <= bounds.minZ) hits.back = true;\n\telse if (position.z >= bounds.maxZ) hits.front = true;\n\n\treturn hits;\n}\n\nexport function hasAnyBounds3DHit(hits: Bounds3DHits): boolean {\n\treturn hits.left || hits.right || hits.top || hits.bottom || hits.back || hits.front;\n}\n\nexport function constrainMovementToBounds3D(\n\thits: Bounds3DHits,\n\tmoveX: number,\n\tmoveY: number,\n\tmoveZ: number,\n): { moveX: number; moveY: number; moveZ: number } {\n\tlet adjustedX = moveX;\n\tlet adjustedY = moveY;\n\tlet adjustedZ = moveZ;\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\tif ((hits.back && moveZ < 0) || (hits.front && moveZ > 0)) {\n\t\tadjustedZ = 0;\n\t}\n\n\treturn { moveX: adjustedX, moveY: adjustedY, moveZ: adjustedZ };\n}\n\nexport function clampPointToBounds3D(\n\tposition: Bounds3DPoint,\n\tbounds: Bounds3DBox,\n): Bounds3DPoint {\n\treturn {\n\t\tx: Math.min(bounds.maxX, Math.max(bounds.minX, position.x)),\n\t\ty: Math.min(bounds.maxY, Math.max(bounds.minY, position.y)),\n\t\tz: Math.min(bounds.maxZ, Math.max(bounds.minZ, position.z)),\n\t};\n}\n\nexport function getBounds3DNormalFromHits(\n\thits: Bounds3DHits,\n): { x: number; y: number; z: number } {\n\tlet x = 0;\n\tlet y = 0;\n\tlet z = 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\tif (hits.back) z = 1;\n\tif (hits.front) z = -1;\n\n\treturn { x, y, z };\n}\n","import { SyncStateMachine, t } from '../../core/utility/sync-state-machine';\nimport {\n\tclampPointToBounds3D,\n\tcomputeBounds3DHits,\n\tconstrainMovementToBounds3D,\n\thasAnyBounds3DHit,\n\tinsetBounds3D,\n\ttype Bounds3DPaddingInput,\n\ttype Bounds3DPoint,\n} from '../shared/bounds-3d';\n\nexport type WorldBoundary3DHit =\n\t| 'top'\n\t| 'bottom'\n\t| 'left'\n\t| 'right'\n\t| 'back'\n\t| 'front';\n\nexport type WorldBoundary3DHits = Record<WorldBoundary3DHit, boolean>;\n\nexport interface WorldBoundary3DPosition extends Bounds3DPoint {}\n\nexport interface WorldBoundary3DBounds {\n\ttop: number;\n\tbottom: number;\n\tleft: number;\n\tright: number;\n\tback: number;\n\tfront: number;\n}\n\nexport enum WorldBoundary3DState {\n\tInside = 'inside',\n\tTouching = 'touching',\n}\n\nexport enum WorldBoundary3DEvent {\n\tEnterInside = 'enter-inside',\n\tTouchBoundary = 'touch-boundary',\n}\n\nexport function computeWorldBoundary3DHits(\n\tposition: WorldBoundary3DPosition,\n\tbounds: WorldBoundary3DBounds,\n\tpadding?: Bounds3DPaddingInput,\n): WorldBoundary3DHits {\n\treturn computeBounds3DHits(position, insetBounds3D({\n\t\tminX: bounds.left,\n\t\tmaxX: bounds.right,\n\t\tminY: bounds.bottom,\n\t\tmaxY: bounds.top,\n\t\tminZ: bounds.back,\n\t\tmaxZ: bounds.front,\n\t}, padding));\n}\n\nexport function hasAnyWorldBoundary3DHit(hits: WorldBoundary3DHits): boolean {\n\treturn hasAnyBounds3DHit(hits);\n}\n\nexport class WorldBoundary3DFSM {\n\tpublic readonly machine: SyncStateMachine<WorldBoundary3DState, WorldBoundary3DEvent, never>;\n\n\tprivate lastHits: WorldBoundary3DHits = {\n\t\ttop: false,\n\t\tbottom: false,\n\t\tleft: false,\n\t\tright: false,\n\t\tback: false,\n\t\tfront: false,\n\t};\n\tprivate lastPosition: WorldBoundary3DPosition | null = null;\n\tprivate lastClampedPosition: WorldBoundary3DPosition | null = null;\n\tprivate lastUpdatedAtMs: number | null = null;\n\n\tconstructor() {\n\t\tthis.machine = new SyncStateMachine<WorldBoundary3DState, WorldBoundary3DEvent, never>(\n\t\t\tWorldBoundary3DState.Inside,\n\t\t\t[\n\t\t\t\tt(WorldBoundary3DState.Inside, WorldBoundary3DEvent.TouchBoundary, WorldBoundary3DState.Touching),\n\t\t\t\tt(WorldBoundary3DState.Touching, WorldBoundary3DEvent.EnterInside, WorldBoundary3DState.Inside),\n\t\t\t\tt(WorldBoundary3DState.Inside, WorldBoundary3DEvent.EnterInside, WorldBoundary3DState.Inside),\n\t\t\t\tt(WorldBoundary3DState.Touching, WorldBoundary3DEvent.TouchBoundary, WorldBoundary3DState.Touching),\n\t\t\t],\n\t\t);\n\t}\n\n\tgetState(): WorldBoundary3DState {\n\t\treturn this.machine.getState();\n\t}\n\n\tgetLastHits(): WorldBoundary3DHits {\n\t\treturn this.lastHits;\n\t}\n\n\tgetMovement(\n\t\tmoveX: number,\n\t\tmoveY: number,\n\t\tmoveZ: number,\n\t): { moveX: number; moveY: number; moveZ: number } {\n\t\treturn constrainMovementToBounds3D(this.lastHits, moveX, moveY, moveZ);\n\t}\n\n\tgetLastPosition(): WorldBoundary3DPosition | null {\n\t\treturn this.lastPosition;\n\t}\n\n\tgetLastClampedPosition(): WorldBoundary3DPosition | null {\n\t\treturn this.lastClampedPosition;\n\t}\n\n\tgetLastUpdatedAtMs(): number | null {\n\t\treturn this.lastUpdatedAtMs;\n\t}\n\n\tupdate(\n\t\tposition: WorldBoundary3DPosition,\n\t\tbounds: WorldBoundary3DBounds,\n\t\tpadding?: Bounds3DPaddingInput,\n\t): WorldBoundary3DHits {\n\t\tconst effectiveBounds = insetBounds3D({\n\t\t\tminX: bounds.left,\n\t\t\tmaxX: bounds.right,\n\t\t\tminY: bounds.bottom,\n\t\t\tmaxY: bounds.top,\n\t\t\tminZ: bounds.back,\n\t\t\tmaxZ: bounds.front,\n\t\t}, padding);\n\n\t\tconst hits = computeBounds3DHits(position, effectiveBounds);\n\t\tthis.lastHits = hits;\n\t\tthis.lastPosition = { x: position.x, y: position.y, z: position.z };\n\t\tthis.lastClampedPosition = clampPointToBounds3D(position, effectiveBounds);\n\t\tthis.lastUpdatedAtMs = Date.now();\n\n\t\tif (hasAnyWorldBoundary3DHit(hits)) {\n\t\t\tthis.dispatch(WorldBoundary3DEvent.TouchBoundary);\n\t\t} else {\n\t\t\tthis.dispatch(WorldBoundary3DEvent.EnterInside);\n\t\t}\n\n\t\treturn hits;\n\t}\n\n\tprivate dispatch(event: WorldBoundary3DEvent): void {\n\t\tif (this.machine.can(event)) {\n\t\t\tthis.machine.syncDispatch(event);\n\t\t}\n\t}\n}\n","import type { IWorld } from 'bitecs';\nimport { defineBehavior, type BehaviorRef } from '../behavior-descriptor';\nimport type { BehaviorEntityLink, BehaviorSystem } from '../behavior-system';\nimport type { Bounds3DPaddingInput } from '../shared/bounds-3d';\nimport {\n\tWorldBoundary3DFSM,\n\ttype WorldBoundary3DBounds,\n\ttype WorldBoundary3DHits,\n\ttype WorldBoundary3DPosition,\n} from './world-boundary-3d-fsm';\n\nexport interface WorldBoundary3DOptions {\n\tboundaries: WorldBoundary3DBounds;\n\tpadding?: Bounds3DPaddingInput;\n}\n\nexport interface WorldBoundary3DHandle {\n\tgetLastHits(): WorldBoundary3DHits | null;\n\tgetMovement(\n\t\tmoveX: number,\n\t\tmoveY: number,\n\t\tmoveZ: number,\n\t): { moveX: number; moveY: number; moveZ: number };\n\tgetLastClampedPosition(): WorldBoundary3DPosition | null;\n}\n\nconst defaultOptions: WorldBoundary3DOptions = {\n\tboundaries: { top: 0, bottom: 0, left: 0, right: 0, back: 0, front: 0 },\n\tpadding: 0,\n};\n\nconst WORLD_BOUNDARY_3D_BEHAVIOR_KEY = Symbol.for(\n\t'zylem:behavior:world-boundary-3d',\n);\n\nfunction createWorldBoundary3DHandle(\n\tref: BehaviorRef<WorldBoundary3DOptions>,\n): WorldBoundary3DHandle {\n\treturn {\n\t\tgetLastHits: () => {\n\t\t\tconst fsm = ref.fsm as WorldBoundary3DFSM | undefined;\n\t\t\treturn fsm?.getLastHits() ?? null;\n\t\t},\n\t\tgetMovement: (moveX: number, moveY: number, moveZ: number) => {\n\t\t\tconst fsm = ref.fsm as WorldBoundary3DFSM | undefined;\n\t\t\treturn fsm?.getMovement(moveX, moveY, moveZ) ?? { moveX, moveY, moveZ };\n\t\t},\n\t\tgetLastClampedPosition: () => {\n\t\t\tconst fsm = ref.fsm as WorldBoundary3DFSM | undefined;\n\t\t\treturn fsm?.getLastClampedPosition() ?? null;\n\t\t},\n\t};\n}\n\nclass WorldBoundary3DSystem implements BehaviorSystem {\n\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_3D_BEHAVIOR_KEY);\n\t\tif (!links) return;\n\n\t\tfor (const link of links) {\n\t\t\tconst gameEntity = link.entity as any;\n\t\t\tconst boundaryRef = link.ref as any;\n\t\t\tif (!gameEntity.body) continue;\n\n\t\t\tconst options = boundaryRef.options as WorldBoundary3DOptions;\n\t\t\tif (!boundaryRef.fsm) {\n\t\t\t\tboundaryRef.fsm = new WorldBoundary3DFSM();\n\t\t\t}\n\n\t\t\tconst pos = gameEntity.body.translation();\n\t\t\tboundaryRef.fsm.update(\n\t\t\t\t{ x: pos.x, y: pos.y, z: pos.z },\n\t\t\t\toptions.boundaries,\n\t\t\t\toptions.padding,\n\t\t\t);\n\t\t}\n\t}\n\n\tdestroy(_ecs: IWorld): void {}\n}\n\nexport const WorldBoundary3DBehavior = defineBehavior({\n\tname: 'world-boundary-3d',\n\tdefaultOptions,\n\tsystemFactory: (ctx) =>\n\t\tnew WorldBoundary3DSystem(ctx.world, ctx.getBehaviorLinks),\n\tcreateHandle: createWorldBoundary3DHandle,\n});\n"],"mappings":"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","names":["WorldBoundary3DState","WorldBoundary3DEvent"]}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { IWorld } from 'bitecs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* BehaviorSystem Interface
|
|
5
|
+
*
|
|
6
|
+
* Base interface for ECS-based behavior systems that run at the stage level.
|
|
7
|
+
* Systems query entities with matching components and process them each frame.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A behavior system that processes entities with specific components.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* class ThrusterMovementSystem implements BehaviorSystem {
|
|
16
|
+
* update(ecs: IWorld, delta: number): void {
|
|
17
|
+
* // Query and process entities with thruster components
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
interface BehaviorSystem {
|
|
23
|
+
/** Called once per frame with ECS world and delta time */
|
|
24
|
+
update(ecs: IWorld, delta: number): void;
|
|
25
|
+
/** Optional cleanup when stage is destroyed */
|
|
26
|
+
destroy?(ecs: IWorld): void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Runtime link between a spawned entity and one of its behavior refs.
|
|
30
|
+
* Systems can iterate these links to avoid scanning all world entities.
|
|
31
|
+
*/
|
|
32
|
+
interface BehaviorEntityLink {
|
|
33
|
+
entity: any;
|
|
34
|
+
ref: any;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Context provided to behavior-system factories.
|
|
38
|
+
*/
|
|
39
|
+
interface BehaviorSystemContext {
|
|
40
|
+
world: any;
|
|
41
|
+
ecs: IWorld;
|
|
42
|
+
scene: any;
|
|
43
|
+
/**
|
|
44
|
+
* Returns live behavior links for a descriptor key.
|
|
45
|
+
* O(1) lookup into a pre-built stage index.
|
|
46
|
+
*/
|
|
47
|
+
getBehaviorLinks?: (key: symbol) => Iterable<BehaviorEntityLink>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Factory function that creates a BehaviorSystem.
|
|
51
|
+
* Receives the stage for access to world, scene, etc.
|
|
52
|
+
*/
|
|
53
|
+
type BehaviorSystemFactory<T extends BehaviorSystem = BehaviorSystem> = (stage: BehaviorSystemContext) => T;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* BehaviorDescriptor
|
|
57
|
+
*
|
|
58
|
+
* Type-safe behavior descriptors that provide options inference.
|
|
59
|
+
* Used with entity.use() to declaratively attach behaviors to entities.
|
|
60
|
+
*
|
|
61
|
+
* Each behavior can define its own handle type via `createHandle`,
|
|
62
|
+
* providing behavior-specific methods with full type safety.
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Base handle returned by entity.use() for lazy access to behavior runtime.
|
|
67
|
+
* FSM is null until entity is spawned and components are initialized.
|
|
68
|
+
*/
|
|
69
|
+
interface BaseBehaviorHandle<O extends Record<string, any> = Record<string, any>> {
|
|
70
|
+
/** Get the FSM instance (null until entity is spawned) */
|
|
71
|
+
getFSM(): any | null;
|
|
72
|
+
/** Get the current options */
|
|
73
|
+
getOptions(): O;
|
|
74
|
+
/** Access the underlying behavior ref */
|
|
75
|
+
readonly ref: BehaviorRef<O>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Reference to a behavior stored on an entity
|
|
79
|
+
*/
|
|
80
|
+
interface BehaviorRef<O extends Record<string, any> = Record<string, any>> {
|
|
81
|
+
/** The behavior descriptor */
|
|
82
|
+
descriptor: BehaviorDescriptor<O, any>;
|
|
83
|
+
/** Merged options (defaults + overrides) */
|
|
84
|
+
options: O;
|
|
85
|
+
/** Optional FSM instance - set lazily when entity is spawned */
|
|
86
|
+
fsm?: any;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* A typed behavior descriptor that associates a symbol key with:
|
|
90
|
+
* - Default options (providing type inference)
|
|
91
|
+
* - A system factory to create the behavior system
|
|
92
|
+
* - An optional handle factory for behavior-specific methods
|
|
93
|
+
*/
|
|
94
|
+
interface BehaviorDescriptor<O extends Record<string, any> = Record<string, any>, H extends Record<string, any> = Record<string, never>, I = unknown> {
|
|
95
|
+
/** Unique symbol identifying this behavior */
|
|
96
|
+
readonly key: symbol;
|
|
97
|
+
/** Default options (used for type inference) */
|
|
98
|
+
readonly defaultOptions: O;
|
|
99
|
+
/** Factory to create the behavior system */
|
|
100
|
+
readonly systemFactory: BehaviorSystemFactory;
|
|
101
|
+
/**
|
|
102
|
+
* Optional factory to create behavior-specific handle methods.
|
|
103
|
+
* These methods are merged into the handle returned by entity.use().
|
|
104
|
+
*/
|
|
105
|
+
readonly createHandle?: (ref: BehaviorRef<O>) => H;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The full handle type returned by entity.use().
|
|
109
|
+
* Combines base handle with behavior-specific methods.
|
|
110
|
+
*/
|
|
111
|
+
type BehaviorHandle<O extends Record<string, any> = Record<string, any>, H extends Record<string, any> = Record<string, never>> = BaseBehaviorHandle<O> & H;
|
|
112
|
+
/**
|
|
113
|
+
* Configuration for defining a new behavior
|
|
114
|
+
*/
|
|
115
|
+
interface DefineBehaviorConfig<O extends Record<string, any>, H extends Record<string, any> = Record<string, never>, I = unknown> {
|
|
116
|
+
/** Human-readable name for debugging */
|
|
117
|
+
name: string;
|
|
118
|
+
/** Default options - these define the type */
|
|
119
|
+
defaultOptions: O;
|
|
120
|
+
/** Factory function to create the system */
|
|
121
|
+
systemFactory: BehaviorSystemFactory;
|
|
122
|
+
/**
|
|
123
|
+
* Optional factory to create behavior-specific handle methods.
|
|
124
|
+
* The returned object is merged into the handle returned by entity.use().
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```typescript
|
|
128
|
+
* createHandle: (ref) => ({
|
|
129
|
+
* getLastHits: () => ref.fsm?.getLastHits() ?? null,
|
|
130
|
+
* getMovement: (moveX, moveY) => ref.fsm?.getMovement(moveX, moveY) ?? { moveX, moveY },
|
|
131
|
+
* }),
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
createHandle?: (ref: BehaviorRef<O>) => H;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Define a typed behavior descriptor.
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```typescript
|
|
141
|
+
* export const WorldBoundary2DBehavior = defineBehavior({
|
|
142
|
+
* name: 'world-boundary-2d',
|
|
143
|
+
* defaultOptions: { boundaries: { top: 0, bottom: 0, left: 0, right: 0 } },
|
|
144
|
+
* systemFactory: (ctx) => new WorldBoundary2DSystem(ctx.world),
|
|
145
|
+
* createHandle: (ref) => ({
|
|
146
|
+
* getLastHits: () => ref.fsm?.getLastHits() ?? null,
|
|
147
|
+
* getMovement: (moveX: number, moveY: number) =>
|
|
148
|
+
* ref.fsm?.getMovement(moveX, moveY) ?? { moveX, moveY },
|
|
149
|
+
* }),
|
|
150
|
+
* });
|
|
151
|
+
*
|
|
152
|
+
* // Usage - handle has getLastHits and getMovement with full types
|
|
153
|
+
* const boundary = ship.use(WorldBoundary2DBehavior, { ... });
|
|
154
|
+
* const hits = boundary.getLastHits(); // Fully typed!
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
declare function defineBehavior<O extends Record<string, any>, H extends Record<string, any> = Record<string, never>, I = unknown>(config: DefineBehaviorConfig<O, H, I>): BehaviorDescriptor<O, H, I>;
|
|
158
|
+
|
|
159
|
+
export { type BehaviorSystem as B, type DefineBehaviorConfig as D, type BehaviorEntityLink as a, type BehaviorSystemFactory as b, type BehaviorDescriptor as c, type BehaviorHandle as d, type BehaviorRef as e, defineBehavior as f };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Vector2 } from 'three';
|
|
2
|
-
import {
|
|
3
|
-
import { b as Stage } from './stage-types-
|
|
2
|
+
import { I as GameEntity } from './entity-vj-HTjzU.js';
|
|
3
|
+
import { b as Stage } from './stage-types-C19IhuzA.js';
|
|
4
4
|
import * as _sinclair_typebox from '@sinclair/typebox';
|
|
5
5
|
import { Static } from '@sinclair/typebox';
|
|
6
6
|
|