@game_engine/core 0.1.0-alpha
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/dist/index.d.ts +209 -0
- package/dist/index.js +431 -0
- package/dist/index.js.map +1 -0
- package/package.json +39 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
type EntityId = string;
|
|
2
|
+
type ComponentName = string;
|
|
3
|
+
type ComponentValue = unknown;
|
|
4
|
+
type Vector2 = [number, number];
|
|
5
|
+
type Vector3 = [number, number, number];
|
|
6
|
+
type InputButton = string;
|
|
7
|
+
declare class InputState {
|
|
8
|
+
private readonly pressedButtons;
|
|
9
|
+
constructor(initialButtons?: Iterable<InputButton>);
|
|
10
|
+
get pressed(): Record<InputButton, boolean>;
|
|
11
|
+
press(button: InputButton): void;
|
|
12
|
+
release(button: InputButton): void;
|
|
13
|
+
isPressed(button: InputButton): boolean;
|
|
14
|
+
clear(): void;
|
|
15
|
+
listPressed(): InputButton[];
|
|
16
|
+
toJSON(): {
|
|
17
|
+
pressed: Record<InputButton, boolean>;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
interface Transform {
|
|
21
|
+
position: Vector2;
|
|
22
|
+
rotation: number;
|
|
23
|
+
scale: Vector2;
|
|
24
|
+
}
|
|
25
|
+
interface Velocity {
|
|
26
|
+
linear: Vector2;
|
|
27
|
+
}
|
|
28
|
+
interface CharacterController {
|
|
29
|
+
speed: number;
|
|
30
|
+
jumpVelocity: number;
|
|
31
|
+
groundEntityId?: EntityId;
|
|
32
|
+
targetEntityId?: EntityId;
|
|
33
|
+
}
|
|
34
|
+
interface Collider {
|
|
35
|
+
shape: "box";
|
|
36
|
+
size: Vector2;
|
|
37
|
+
static: boolean;
|
|
38
|
+
sensor?: boolean;
|
|
39
|
+
}
|
|
40
|
+
interface RigidBody {
|
|
41
|
+
type: "static" | "dynamic" | "kinematic";
|
|
42
|
+
gravityScale?: number;
|
|
43
|
+
lockedRotation?: boolean;
|
|
44
|
+
}
|
|
45
|
+
interface PhysicsMaterial {
|
|
46
|
+
friction: number;
|
|
47
|
+
restitution: number;
|
|
48
|
+
}
|
|
49
|
+
interface GroundedState {
|
|
50
|
+
grounded: boolean;
|
|
51
|
+
groundEntityId?: EntityId;
|
|
52
|
+
}
|
|
53
|
+
interface Parent {
|
|
54
|
+
entityId: EntityId;
|
|
55
|
+
}
|
|
56
|
+
interface SpriteFrame {
|
|
57
|
+
id: string;
|
|
58
|
+
durationFrames: number;
|
|
59
|
+
sprite?: string;
|
|
60
|
+
rect?: [number, number, number, number];
|
|
61
|
+
}
|
|
62
|
+
interface AnimationClip {
|
|
63
|
+
id: string;
|
|
64
|
+
fps?: number;
|
|
65
|
+
frameOrder?: string[];
|
|
66
|
+
frames: SpriteFrame[];
|
|
67
|
+
}
|
|
68
|
+
interface Animator {
|
|
69
|
+
clips: string[];
|
|
70
|
+
defaultClip?: string;
|
|
71
|
+
currentClip: string;
|
|
72
|
+
activeClip?: string;
|
|
73
|
+
activeFrameId?: string;
|
|
74
|
+
autoplay: boolean;
|
|
75
|
+
speed: number;
|
|
76
|
+
loop: "once" | "loop";
|
|
77
|
+
playing?: boolean;
|
|
78
|
+
currentFrameIndex?: number;
|
|
79
|
+
elapsedFrames?: number;
|
|
80
|
+
elapsedSeconds?: number;
|
|
81
|
+
}
|
|
82
|
+
interface GeneratedMap {
|
|
83
|
+
generator: "procedural-map-v0";
|
|
84
|
+
seed: number;
|
|
85
|
+
width: number;
|
|
86
|
+
height: number;
|
|
87
|
+
platformCount: number;
|
|
88
|
+
enemyCount: number;
|
|
89
|
+
configHash: string;
|
|
90
|
+
constraints?: Record<string, unknown>;
|
|
91
|
+
}
|
|
92
|
+
interface Transform3D {
|
|
93
|
+
position: Vector3;
|
|
94
|
+
rotation: Vector3;
|
|
95
|
+
scale: Vector3;
|
|
96
|
+
}
|
|
97
|
+
interface Camera3D {
|
|
98
|
+
projection: "perspective" | "orthographic";
|
|
99
|
+
near: number;
|
|
100
|
+
far: number;
|
|
101
|
+
fov?: number;
|
|
102
|
+
orthographicSize?: number;
|
|
103
|
+
targetEntityId?: EntityId;
|
|
104
|
+
}
|
|
105
|
+
interface MeshRef {
|
|
106
|
+
id: string;
|
|
107
|
+
path: string;
|
|
108
|
+
format?: "obj" | "gltf" | "glb";
|
|
109
|
+
}
|
|
110
|
+
interface MeshRenderer3D {
|
|
111
|
+
mesh: string;
|
|
112
|
+
visible?: boolean;
|
|
113
|
+
material?: string;
|
|
114
|
+
}
|
|
115
|
+
interface Collider3D {
|
|
116
|
+
shape: "box";
|
|
117
|
+
size: Vector3;
|
|
118
|
+
static: boolean;
|
|
119
|
+
sensor?: boolean;
|
|
120
|
+
}
|
|
121
|
+
interface BuiltInComponents {
|
|
122
|
+
Transform: Transform;
|
|
123
|
+
Velocity: Velocity;
|
|
124
|
+
CharacterController: CharacterController;
|
|
125
|
+
Collider: Collider;
|
|
126
|
+
RigidBody: RigidBody;
|
|
127
|
+
PhysicsMaterial: PhysicsMaterial;
|
|
128
|
+
GroundedState: GroundedState;
|
|
129
|
+
Parent: Parent;
|
|
130
|
+
AnimationClip: AnimationClip;
|
|
131
|
+
Animator: Animator;
|
|
132
|
+
GeneratedMap: GeneratedMap;
|
|
133
|
+
Transform3D: Transform3D;
|
|
134
|
+
Camera3D: Camera3D;
|
|
135
|
+
MeshRef: MeshRef;
|
|
136
|
+
MeshRenderer3D: MeshRenderer3D;
|
|
137
|
+
Collider3D: Collider3D;
|
|
138
|
+
}
|
|
139
|
+
interface EntityRecord {
|
|
140
|
+
id: EntityId;
|
|
141
|
+
components: Record<ComponentName, ComponentValue>;
|
|
142
|
+
}
|
|
143
|
+
interface System {
|
|
144
|
+
name: string;
|
|
145
|
+
update(world: World, context: SystemUpdateContext): void;
|
|
146
|
+
}
|
|
147
|
+
interface SystemUpdateContext {
|
|
148
|
+
deltaSeconds: number;
|
|
149
|
+
frame: number;
|
|
150
|
+
input: InputState;
|
|
151
|
+
}
|
|
152
|
+
interface SystemRunContext {
|
|
153
|
+
deltaSeconds: number;
|
|
154
|
+
frame: number;
|
|
155
|
+
input?: InputState;
|
|
156
|
+
}
|
|
157
|
+
declare class World {
|
|
158
|
+
private readonly entities;
|
|
159
|
+
readonly input: InputState;
|
|
160
|
+
constructor(input?: InputState);
|
|
161
|
+
createEntity(id: EntityId, components?: Record<ComponentName, ComponentValue>): EntityRecord;
|
|
162
|
+
getEntity(id: EntityId): EntityRecord | undefined;
|
|
163
|
+
requireEntity(id: EntityId): EntityRecord;
|
|
164
|
+
addComponent(id: EntityId, name: ComponentName, value: ComponentValue): void;
|
|
165
|
+
removeComponent(id: EntityId, name: ComponentName): void;
|
|
166
|
+
query(requiredComponents: ComponentName[]): EntityRecord[];
|
|
167
|
+
listEntities(): EntityRecord[];
|
|
168
|
+
toJSON(): {
|
|
169
|
+
entities: EntityRecord[];
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
declare class ComponentRegistry {
|
|
173
|
+
private readonly names;
|
|
174
|
+
register(name: ComponentName): void;
|
|
175
|
+
has(name: ComponentName): boolean;
|
|
176
|
+
list(): ComponentName[];
|
|
177
|
+
}
|
|
178
|
+
declare class SystemRegistry {
|
|
179
|
+
private readonly systems;
|
|
180
|
+
register(system: System): void;
|
|
181
|
+
runFrame(world: World, context: SystemRunContext): void;
|
|
182
|
+
run(world: World, deltaSeconds: number): void;
|
|
183
|
+
list(): string[];
|
|
184
|
+
}
|
|
185
|
+
declare class CharacterControllerInputSystem implements System {
|
|
186
|
+
readonly name = "CharacterControllerInput";
|
|
187
|
+
update(world: World, context: SystemUpdateContext): void;
|
|
188
|
+
}
|
|
189
|
+
declare class VelocityIntegrationSystem implements System {
|
|
190
|
+
readonly name = "VelocityIntegration";
|
|
191
|
+
update(world: World, context: SystemUpdateContext): void;
|
|
192
|
+
}
|
|
193
|
+
declare class KinematicPhysicsSystem implements System {
|
|
194
|
+
readonly name = "KinematicPhysics";
|
|
195
|
+
update(world: World, context: SystemUpdateContext): void;
|
|
196
|
+
}
|
|
197
|
+
declare class GroundedCheckSystem implements System {
|
|
198
|
+
readonly name = "GroundedCheck";
|
|
199
|
+
update(world: World): void;
|
|
200
|
+
}
|
|
201
|
+
declare class AnimationSystem implements System {
|
|
202
|
+
readonly name = "Animation";
|
|
203
|
+
update(world: World, context: SystemUpdateContext): void;
|
|
204
|
+
}
|
|
205
|
+
declare function createBuiltInComponentRegistry(): ComponentRegistry;
|
|
206
|
+
declare function createBuiltInSystemRegistry(): SystemRegistry;
|
|
207
|
+
declare function createInputState(initialButtons?: Iterable<InputButton>): InputState;
|
|
208
|
+
|
|
209
|
+
export { type AnimationClip, AnimationSystem, type Animator, type BuiltInComponents, type Camera3D, type CharacterController, CharacterControllerInputSystem, type Collider, type Collider3D, type ComponentName, ComponentRegistry, type ComponentValue, type EntityId, type EntityRecord, type GeneratedMap, GroundedCheckSystem, type GroundedState, type InputButton, InputState, KinematicPhysicsSystem, type MeshRef, type MeshRenderer3D, type Parent, type PhysicsMaterial, type RigidBody, type SpriteFrame, type System, SystemRegistry, type SystemRunContext, type SystemUpdateContext, type Transform, type Transform3D, type Vector2, type Vector3, type Velocity, VelocityIntegrationSystem, World, createBuiltInComponentRegistry, createBuiltInSystemRegistry, createInputState };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var InputState = class {
|
|
3
|
+
pressedButtons = /* @__PURE__ */ new Set();
|
|
4
|
+
constructor(initialButtons = []) {
|
|
5
|
+
for (const button of initialButtons) {
|
|
6
|
+
this.press(button);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
get pressed() {
|
|
10
|
+
return Object.fromEntries(this.listPressed().map((button) => [button, true]));
|
|
11
|
+
}
|
|
12
|
+
press(button) {
|
|
13
|
+
this.pressedButtons.add(validateButton(button));
|
|
14
|
+
}
|
|
15
|
+
release(button) {
|
|
16
|
+
this.pressedButtons.delete(validateButton(button));
|
|
17
|
+
}
|
|
18
|
+
isPressed(button) {
|
|
19
|
+
return this.pressedButtons.has(validateButton(button));
|
|
20
|
+
}
|
|
21
|
+
clear() {
|
|
22
|
+
this.pressedButtons.clear();
|
|
23
|
+
}
|
|
24
|
+
listPressed() {
|
|
25
|
+
return [...this.pressedButtons].sort();
|
|
26
|
+
}
|
|
27
|
+
toJSON() {
|
|
28
|
+
return {
|
|
29
|
+
pressed: this.pressed
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
var World = class {
|
|
34
|
+
entities = /* @__PURE__ */ new Map();
|
|
35
|
+
input;
|
|
36
|
+
constructor(input = createInputState()) {
|
|
37
|
+
this.input = input;
|
|
38
|
+
}
|
|
39
|
+
createEntity(id, components = {}) {
|
|
40
|
+
if (this.entities.has(id)) {
|
|
41
|
+
throw new Error(`Entity already exists: ${id}`);
|
|
42
|
+
}
|
|
43
|
+
const entity = { id, components: cloneComponents(components) };
|
|
44
|
+
this.entities.set(id, entity);
|
|
45
|
+
return entity;
|
|
46
|
+
}
|
|
47
|
+
getEntity(id) {
|
|
48
|
+
return this.entities.get(id);
|
|
49
|
+
}
|
|
50
|
+
requireEntity(id) {
|
|
51
|
+
const entity = this.entities.get(id);
|
|
52
|
+
if (!entity) {
|
|
53
|
+
throw new Error(`Unknown entity: ${id}`);
|
|
54
|
+
}
|
|
55
|
+
return entity;
|
|
56
|
+
}
|
|
57
|
+
addComponent(id, name, value) {
|
|
58
|
+
const entity = this.requireEntity(id);
|
|
59
|
+
entity.components[name] = cloneValue(value);
|
|
60
|
+
}
|
|
61
|
+
removeComponent(id, name) {
|
|
62
|
+
const entity = this.requireEntity(id);
|
|
63
|
+
delete entity.components[name];
|
|
64
|
+
}
|
|
65
|
+
query(requiredComponents) {
|
|
66
|
+
return [...this.entities.values()].filter(
|
|
67
|
+
(entity) => requiredComponents.every((componentName) => Object.hasOwn(entity.components, componentName))
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
listEntities() {
|
|
71
|
+
return [...this.entities.values()];
|
|
72
|
+
}
|
|
73
|
+
toJSON() {
|
|
74
|
+
return {
|
|
75
|
+
entities: [...this.entities.values()].map((entity) => ({
|
|
76
|
+
id: entity.id,
|
|
77
|
+
components: cloneComponents(entity.components)
|
|
78
|
+
}))
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
var ComponentRegistry = class {
|
|
83
|
+
names = /* @__PURE__ */ new Set();
|
|
84
|
+
register(name) {
|
|
85
|
+
this.names.add(name);
|
|
86
|
+
}
|
|
87
|
+
has(name) {
|
|
88
|
+
return this.names.has(name);
|
|
89
|
+
}
|
|
90
|
+
list() {
|
|
91
|
+
return [...this.names].sort();
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
var SystemRegistry = class {
|
|
95
|
+
systems = [];
|
|
96
|
+
register(system) {
|
|
97
|
+
this.systems.push(system);
|
|
98
|
+
}
|
|
99
|
+
runFrame(world, context) {
|
|
100
|
+
const updateContext = {
|
|
101
|
+
...context,
|
|
102
|
+
input: context.input ?? world.input
|
|
103
|
+
};
|
|
104
|
+
for (const system of this.systems) {
|
|
105
|
+
system.update(world, updateContext);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
run(world, deltaSeconds) {
|
|
109
|
+
this.runFrame(world, { deltaSeconds, frame: 0 });
|
|
110
|
+
}
|
|
111
|
+
list() {
|
|
112
|
+
return this.systems.map((system) => system.name);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
var CharacterControllerInputSystem = class {
|
|
116
|
+
name = "CharacterControllerInput";
|
|
117
|
+
update(world, context) {
|
|
118
|
+
for (const entity of world.query(["Velocity", "CharacterController"])) {
|
|
119
|
+
if (entity.id !== "player") {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const velocity = entity.components.Velocity;
|
|
123
|
+
const controller = entity.components.CharacterController;
|
|
124
|
+
const horizontal = (context.input.isPressed("Right") ? 1 : 0) - (context.input.isPressed("Left") ? 1 : 0);
|
|
125
|
+
velocity.linear = [roundDeterministic(horizontal * controller.speed), velocity.linear[1]];
|
|
126
|
+
if (context.input.isPressed("Jump") && canJump(entity.components, controller)) {
|
|
127
|
+
velocity.linear = [velocity.linear[0], roundDeterministic(controller.jumpVelocity)];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
var VelocityIntegrationSystem = class {
|
|
133
|
+
name = "VelocityIntegration";
|
|
134
|
+
update(world, context) {
|
|
135
|
+
integrateMovableVelocity(world, context);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
var KinematicPhysicsSystem = class {
|
|
139
|
+
name = "KinematicPhysics";
|
|
140
|
+
update(world, context) {
|
|
141
|
+
integrateMovableVelocity(world, context);
|
|
142
|
+
updateGroundedStates(world);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
var GroundedCheckSystem = class {
|
|
146
|
+
name = "GroundedCheck";
|
|
147
|
+
update(world) {
|
|
148
|
+
updateGroundedStates(world);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
var AnimationSystem = class {
|
|
152
|
+
name = "Animation";
|
|
153
|
+
update(world, context) {
|
|
154
|
+
const clips = collectAnimationClips(world);
|
|
155
|
+
for (const entity of world.query(["Animator"])) {
|
|
156
|
+
const animator = entity.components.Animator;
|
|
157
|
+
const activeClipId = resolveActiveClipId(animator);
|
|
158
|
+
const clip = clips.get(activeClipId);
|
|
159
|
+
const orderedFrames = clip ? orderAnimationFrames(clip) : [];
|
|
160
|
+
if (!clip || orderedFrames.length === 0) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const playing = animator.playing ?? animator.autoplay;
|
|
164
|
+
animator.playing = playing;
|
|
165
|
+
animator.activeClip = activeClipId;
|
|
166
|
+
if (!playing || animator.speed === 0) {
|
|
167
|
+
const playbackState2 = resolveAnimationPlaybackState(clip, animator, context.deltaSeconds, false);
|
|
168
|
+
applyAnimationPlaybackState(animator, playbackState2, orderedFrames);
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
const playbackState = resolveAnimationPlaybackState(clip, animator, context.deltaSeconds, true);
|
|
172
|
+
applyAnimationPlaybackState(animator, playbackState, orderedFrames);
|
|
173
|
+
if (animator.loop === "once" && playbackState.finished) {
|
|
174
|
+
animator.playing = false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
function createBuiltInComponentRegistry() {
|
|
180
|
+
const registry = new ComponentRegistry();
|
|
181
|
+
for (const name of [
|
|
182
|
+
"Transform",
|
|
183
|
+
"Velocity",
|
|
184
|
+
"CharacterController",
|
|
185
|
+
"Collider",
|
|
186
|
+
"RigidBody",
|
|
187
|
+
"PhysicsMaterial",
|
|
188
|
+
"GroundedState",
|
|
189
|
+
"Parent",
|
|
190
|
+
"AnimationClip",
|
|
191
|
+
"Animator",
|
|
192
|
+
"GeneratedMap",
|
|
193
|
+
"Transform3D",
|
|
194
|
+
"Camera3D",
|
|
195
|
+
"MeshRef",
|
|
196
|
+
"MeshRenderer3D",
|
|
197
|
+
"Collider3D"
|
|
198
|
+
]) {
|
|
199
|
+
registry.register(name);
|
|
200
|
+
}
|
|
201
|
+
return registry;
|
|
202
|
+
}
|
|
203
|
+
function createBuiltInSystemRegistry() {
|
|
204
|
+
const registry = new SystemRegistry();
|
|
205
|
+
registry.register(new CharacterControllerInputSystem());
|
|
206
|
+
registry.register(new KinematicPhysicsSystem());
|
|
207
|
+
registry.register(new AnimationSystem());
|
|
208
|
+
return registry;
|
|
209
|
+
}
|
|
210
|
+
function createInputState(initialButtons = []) {
|
|
211
|
+
return new InputState(initialButtons);
|
|
212
|
+
}
|
|
213
|
+
function cloneComponents(components) {
|
|
214
|
+
return Object.fromEntries(Object.entries(components).map(([name, value]) => [name, cloneValue(value)]));
|
|
215
|
+
}
|
|
216
|
+
function cloneValue(value) {
|
|
217
|
+
if (value === void 0) {
|
|
218
|
+
return void 0;
|
|
219
|
+
}
|
|
220
|
+
return JSON.parse(JSON.stringify(value));
|
|
221
|
+
}
|
|
222
|
+
function roundDeterministic(value) {
|
|
223
|
+
const rounded = Number(value.toFixed(12));
|
|
224
|
+
const nearestInteger = Math.round(rounded);
|
|
225
|
+
if (Math.abs(rounded - nearestInteger) < 1e-9) {
|
|
226
|
+
return nearestInteger;
|
|
227
|
+
}
|
|
228
|
+
return rounded;
|
|
229
|
+
}
|
|
230
|
+
function canJump(components, controller) {
|
|
231
|
+
const groundedState = components.GroundedState;
|
|
232
|
+
if (groundedState) {
|
|
233
|
+
return groundedState.grounded === true;
|
|
234
|
+
}
|
|
235
|
+
return typeof controller.groundEntityId === "string" && controller.groundEntityId.length > 0;
|
|
236
|
+
}
|
|
237
|
+
function integrateMovableVelocity(world, context) {
|
|
238
|
+
for (const entity of world.query(["Transform", "Velocity"])) {
|
|
239
|
+
if (isStaticBody(entity)) {
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
const transform = entity.components.Transform;
|
|
243
|
+
const velocity = entity.components.Velocity;
|
|
244
|
+
transform.position = [
|
|
245
|
+
roundDeterministic(transform.position[0] + velocity.linear[0] * context.deltaSeconds),
|
|
246
|
+
roundDeterministic(transform.position[1] + velocity.linear[1] * context.deltaSeconds)
|
|
247
|
+
];
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
function updateGroundedStates(world) {
|
|
251
|
+
const staticColliders = world.query(["Transform", "Collider"]).filter((entity) => isStaticCollider(entity) && !isSensorCollider(entity)).map((entity) => ({
|
|
252
|
+
entity,
|
|
253
|
+
aabb: getBoxAabb(entity)
|
|
254
|
+
})).filter((entry) => entry.aabb !== void 0);
|
|
255
|
+
for (const entity of world.query(["Transform", "Collider", "GroundedState"])) {
|
|
256
|
+
const groundedState = entity.components.GroundedState;
|
|
257
|
+
if (isStaticBody(entity) || isSensorCollider(entity)) {
|
|
258
|
+
groundedState.grounded = false;
|
|
259
|
+
delete groundedState.groundEntityId;
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
const aabb = getBoxAabb(entity);
|
|
263
|
+
const ground = aabb ? staticColliders.find((candidate) => candidate.entity.id !== entity.id && isGroundContact(aabb, candidate.aabb)) : void 0;
|
|
264
|
+
groundedState.grounded = ground !== void 0;
|
|
265
|
+
if (ground) {
|
|
266
|
+
groundedState.groundEntityId = ground.entity.id;
|
|
267
|
+
} else {
|
|
268
|
+
delete groundedState.groundEntityId;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
function collectAnimationClips(world) {
|
|
273
|
+
const clips = /* @__PURE__ */ new Map();
|
|
274
|
+
for (const entity of world.query(["AnimationClip"])) {
|
|
275
|
+
const clip = entity.components.AnimationClip;
|
|
276
|
+
if (typeof clip.id === "string" && Array.isArray(clip.frames)) {
|
|
277
|
+
clips.set(clip.id, clip);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return clips;
|
|
281
|
+
}
|
|
282
|
+
function resolveActiveClipId(animator) {
|
|
283
|
+
return animator.activeClip ?? animator.defaultClip ?? animator.currentClip;
|
|
284
|
+
}
|
|
285
|
+
function resolveAnimationPlaybackState(clip, animator, deltaSeconds, advance) {
|
|
286
|
+
const orderedFrames = orderAnimationFrames(clip);
|
|
287
|
+
const frameCount = orderedFrames.length;
|
|
288
|
+
if (frameCount === 0) {
|
|
289
|
+
return {
|
|
290
|
+
frameIndex: 0,
|
|
291
|
+
rawFrameIndex: 0,
|
|
292
|
+
elapsedFrames: 0,
|
|
293
|
+
elapsedSeconds: 0,
|
|
294
|
+
finished: true
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
if (typeof clip.fps === "number" && clip.fps > 0) {
|
|
298
|
+
const elapsedSeconds = roundDeterministic(
|
|
299
|
+
(animator.elapsedSeconds ?? 0) + (advance ? deltaSeconds * animator.speed : 0)
|
|
300
|
+
);
|
|
301
|
+
const rawFrameIndex2 = Math.floor(roundDeterministic(elapsedSeconds * clip.fps));
|
|
302
|
+
const frameIndex2 = resolveBoundedFrameIndex(rawFrameIndex2, frameCount, animator.loop);
|
|
303
|
+
return {
|
|
304
|
+
frameIndex: frameIndex2,
|
|
305
|
+
rawFrameIndex: rawFrameIndex2,
|
|
306
|
+
elapsedFrames: frameIndex2,
|
|
307
|
+
elapsedSeconds,
|
|
308
|
+
finished: rawFrameIndex2 >= Math.max(frameCount - 1, 0)
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
const totalFrames = animationClipDurationFrames(orderedFrames);
|
|
312
|
+
const nextElapsedFrames = roundDeterministic((animator.elapsedFrames ?? 0) + (advance ? animator.speed : 0));
|
|
313
|
+
const rawFrameIndex = nextElapsedFrames;
|
|
314
|
+
const elapsedFrames = animator.loop === "loop" ? wrapAnimationFrame(nextElapsedFrames, totalFrames) : Math.min(nextElapsedFrames, Math.max(totalFrames - 1, 0));
|
|
315
|
+
const frameIndex = resolveAnimationFrameIndex(orderedFrames, elapsedFrames, animator.loop);
|
|
316
|
+
return {
|
|
317
|
+
frameIndex,
|
|
318
|
+
rawFrameIndex,
|
|
319
|
+
elapsedFrames,
|
|
320
|
+
elapsedSeconds: roundDeterministic(elapsedFrames * deltaSeconds),
|
|
321
|
+
finished: rawFrameIndex >= Math.max(totalFrames - 1, 0)
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
function applyAnimationPlaybackState(animator, playbackState, orderedFrames) {
|
|
325
|
+
animator.currentFrameIndex = playbackState.frameIndex;
|
|
326
|
+
animator.activeFrameId = orderedFrames[playbackState.frameIndex]?.id;
|
|
327
|
+
animator.elapsedFrames = playbackState.elapsedFrames;
|
|
328
|
+
animator.elapsedSeconds = playbackState.elapsedSeconds;
|
|
329
|
+
}
|
|
330
|
+
function orderAnimationFrames(clip) {
|
|
331
|
+
if (!Array.isArray(clip.frameOrder) || clip.frameOrder.length === 0) {
|
|
332
|
+
return clip.frames;
|
|
333
|
+
}
|
|
334
|
+
const framesById = new Map(clip.frames.map((frame) => [frame.id, frame]));
|
|
335
|
+
const orderedFrames = clip.frameOrder.flatMap((frameId) => {
|
|
336
|
+
const frame = framesById.get(frameId);
|
|
337
|
+
return frame ? [frame] : [];
|
|
338
|
+
});
|
|
339
|
+
return orderedFrames.length > 0 ? orderedFrames : clip.frames;
|
|
340
|
+
}
|
|
341
|
+
function animationClipDurationFrames(frames) {
|
|
342
|
+
return frames.reduce((total, frame) => total + frame.durationFrames, 0);
|
|
343
|
+
}
|
|
344
|
+
function resolveAnimationFrameIndex(frames, elapsedFrames, loop) {
|
|
345
|
+
const totalFrames = animationClipDurationFrames(frames);
|
|
346
|
+
if (totalFrames <= 0) {
|
|
347
|
+
return 0;
|
|
348
|
+
}
|
|
349
|
+
let frameCursor = loop === "loop" ? wrapAnimationFrame(elapsedFrames, totalFrames) : Math.min(elapsedFrames, totalFrames - 1);
|
|
350
|
+
for (const [index, frame] of frames.entries()) {
|
|
351
|
+
if (frameCursor < frame.durationFrames) {
|
|
352
|
+
return index;
|
|
353
|
+
}
|
|
354
|
+
frameCursor -= frame.durationFrames;
|
|
355
|
+
}
|
|
356
|
+
return frames.length - 1;
|
|
357
|
+
}
|
|
358
|
+
function resolveBoundedFrameIndex(frameIndex, frameCount, loop) {
|
|
359
|
+
if (frameCount <= 0) {
|
|
360
|
+
return 0;
|
|
361
|
+
}
|
|
362
|
+
return loop === "loop" ? frameIndex % frameCount : Math.min(frameIndex, frameCount - 1);
|
|
363
|
+
}
|
|
364
|
+
function wrapAnimationFrame(frame, totalFrames) {
|
|
365
|
+
if (totalFrames <= 0) {
|
|
366
|
+
return 0;
|
|
367
|
+
}
|
|
368
|
+
return roundDeterministic((frame % totalFrames + totalFrames) % totalFrames);
|
|
369
|
+
}
|
|
370
|
+
function isStaticBody(entity) {
|
|
371
|
+
const rigidBody = entity.components.RigidBody;
|
|
372
|
+
if (rigidBody) {
|
|
373
|
+
return rigidBody.type === "static";
|
|
374
|
+
}
|
|
375
|
+
const collider = entity.components.Collider;
|
|
376
|
+
return collider?.static === true;
|
|
377
|
+
}
|
|
378
|
+
function isStaticCollider(entity) {
|
|
379
|
+
const collider = entity.components.Collider;
|
|
380
|
+
if (!collider) {
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
const rigidBody = entity.components.RigidBody;
|
|
384
|
+
return rigidBody ? rigidBody.type === "static" : collider.static === true;
|
|
385
|
+
}
|
|
386
|
+
function isSensorCollider(entity) {
|
|
387
|
+
return entity.components.Collider?.sensor === true;
|
|
388
|
+
}
|
|
389
|
+
function getBoxAabb(entity) {
|
|
390
|
+
const transform = entity.components.Transform;
|
|
391
|
+
const collider = entity.components.Collider;
|
|
392
|
+
if (!transform || !collider || collider.shape !== "box") {
|
|
393
|
+
return void 0;
|
|
394
|
+
}
|
|
395
|
+
const halfWidth = Math.abs(collider.size[0] * transform.scale[0]) / 2;
|
|
396
|
+
const halfHeight = Math.abs(collider.size[1] * transform.scale[1]) / 2;
|
|
397
|
+
return {
|
|
398
|
+
minX: roundDeterministic(transform.position[0] - halfWidth),
|
|
399
|
+
maxX: roundDeterministic(transform.position[0] + halfWidth),
|
|
400
|
+
minY: roundDeterministic(transform.position[1] - halfHeight),
|
|
401
|
+
maxY: roundDeterministic(transform.position[1] + halfHeight)
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function isGroundContact(movable, ground) {
|
|
405
|
+
const epsilon = 1e-9;
|
|
406
|
+
const horizontalOverlap = movable.maxX >= ground.minX - epsilon && movable.minX <= ground.maxX + epsilon;
|
|
407
|
+
const groundTopTouchesOrOverlaps = movable.minY <= ground.maxY + epsilon && movable.maxY >= ground.maxY - epsilon;
|
|
408
|
+
const groundIsBelow = ground.maxY <= movable.maxY + epsilon;
|
|
409
|
+
return horizontalOverlap && groundTopTouchesOrOverlaps && groundIsBelow;
|
|
410
|
+
}
|
|
411
|
+
function validateButton(button) {
|
|
412
|
+
if (typeof button !== "string" || button.length === 0) {
|
|
413
|
+
throw new Error("Input button must be a non-empty string.");
|
|
414
|
+
}
|
|
415
|
+
return button;
|
|
416
|
+
}
|
|
417
|
+
export {
|
|
418
|
+
AnimationSystem,
|
|
419
|
+
CharacterControllerInputSystem,
|
|
420
|
+
ComponentRegistry,
|
|
421
|
+
GroundedCheckSystem,
|
|
422
|
+
InputState,
|
|
423
|
+
KinematicPhysicsSystem,
|
|
424
|
+
SystemRegistry,
|
|
425
|
+
VelocityIntegrationSystem,
|
|
426
|
+
World,
|
|
427
|
+
createBuiltInComponentRegistry,
|
|
428
|
+
createBuiltInSystemRegistry,
|
|
429
|
+
createInputState
|
|
430
|
+
};
|
|
431
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type EntityId = string;\nexport type ComponentName = string;\nexport type ComponentValue = unknown;\nexport type Vector2 = [number, number];\nexport type Vector3 = [number, number, number];\nexport type InputButton = string;\n\nexport class InputState {\n private readonly pressedButtons = new Set<InputButton>();\n\n constructor(initialButtons: Iterable<InputButton> = []) {\n for (const button of initialButtons) {\n this.press(button);\n }\n }\n\n get pressed(): Record<InputButton, boolean> {\n return Object.fromEntries(this.listPressed().map((button) => [button, true]));\n }\n\n press(button: InputButton): void {\n this.pressedButtons.add(validateButton(button));\n }\n\n release(button: InputButton): void {\n this.pressedButtons.delete(validateButton(button));\n }\n\n isPressed(button: InputButton): boolean {\n return this.pressedButtons.has(validateButton(button));\n }\n\n clear(): void {\n this.pressedButtons.clear();\n }\n\n listPressed(): InputButton[] {\n return [...this.pressedButtons].sort();\n }\n\n toJSON(): { pressed: Record<InputButton, boolean> } {\n return {\n pressed: this.pressed\n };\n }\n}\n\nexport interface Transform {\n position: Vector2;\n rotation: number;\n scale: Vector2;\n}\n\nexport interface Velocity {\n linear: Vector2;\n}\n\nexport interface CharacterController {\n speed: number;\n jumpVelocity: number;\n groundEntityId?: EntityId;\n targetEntityId?: EntityId;\n}\n\nexport interface Collider {\n shape: \"box\";\n size: Vector2;\n static: boolean;\n sensor?: boolean;\n}\n\nexport interface RigidBody {\n type: \"static\" | \"dynamic\" | \"kinematic\";\n gravityScale?: number;\n lockedRotation?: boolean;\n}\n\nexport interface PhysicsMaterial {\n friction: number;\n restitution: number;\n}\n\nexport interface GroundedState {\n grounded: boolean;\n groundEntityId?: EntityId;\n}\n\nexport interface Parent {\n entityId: EntityId;\n}\n\nexport interface SpriteFrame {\n id: string;\n durationFrames: number;\n sprite?: string;\n rect?: [number, number, number, number];\n}\n\nexport interface AnimationClip {\n id: string;\n fps?: number;\n frameOrder?: string[];\n frames: SpriteFrame[];\n}\n\nexport interface Animator {\n clips: string[];\n defaultClip?: string;\n currentClip: string;\n activeClip?: string;\n activeFrameId?: string;\n autoplay: boolean;\n speed: number;\n loop: \"once\" | \"loop\";\n playing?: boolean;\n currentFrameIndex?: number;\n elapsedFrames?: number;\n elapsedSeconds?: number;\n}\n\nexport interface GeneratedMap {\n generator: \"procedural-map-v0\";\n seed: number;\n width: number;\n height: number;\n platformCount: number;\n enemyCount: number;\n configHash: string;\n constraints?: Record<string, unknown>;\n}\n\nexport interface Transform3D {\n position: Vector3;\n rotation: Vector3;\n scale: Vector3;\n}\n\nexport interface Camera3D {\n projection: \"perspective\" | \"orthographic\";\n near: number;\n far: number;\n fov?: number;\n orthographicSize?: number;\n targetEntityId?: EntityId;\n}\n\nexport interface MeshRef {\n id: string;\n path: string;\n format?: \"obj\" | \"gltf\" | \"glb\";\n}\n\nexport interface MeshRenderer3D {\n mesh: string;\n visible?: boolean;\n material?: string;\n}\n\nexport interface Collider3D {\n shape: \"box\";\n size: Vector3;\n static: boolean;\n sensor?: boolean;\n}\n\nexport interface BuiltInComponents {\n Transform: Transform;\n Velocity: Velocity;\n CharacterController: CharacterController;\n Collider: Collider;\n RigidBody: RigidBody;\n PhysicsMaterial: PhysicsMaterial;\n GroundedState: GroundedState;\n Parent: Parent;\n AnimationClip: AnimationClip;\n Animator: Animator;\n GeneratedMap: GeneratedMap;\n Transform3D: Transform3D;\n Camera3D: Camera3D;\n MeshRef: MeshRef;\n MeshRenderer3D: MeshRenderer3D;\n Collider3D: Collider3D;\n}\n\nexport interface EntityRecord {\n id: EntityId;\n components: Record<ComponentName, ComponentValue>;\n}\n\nexport interface System {\n name: string;\n update(world: World, context: SystemUpdateContext): void;\n}\n\nexport interface SystemUpdateContext {\n deltaSeconds: number;\n frame: number;\n input: InputState;\n}\n\nexport interface SystemRunContext {\n deltaSeconds: number;\n frame: number;\n input?: InputState;\n}\n\nexport class World {\n private readonly entities = new Map<EntityId, EntityRecord>();\n readonly input: InputState;\n\n constructor(input: InputState = createInputState()) {\n this.input = input;\n }\n\n createEntity(id: EntityId, components: Record<ComponentName, ComponentValue> = {}): EntityRecord {\n if (this.entities.has(id)) {\n throw new Error(`Entity already exists: ${id}`);\n }\n\n const entity: EntityRecord = { id, components: cloneComponents(components) };\n this.entities.set(id, entity);\n return entity;\n }\n\n getEntity(id: EntityId): EntityRecord | undefined {\n return this.entities.get(id);\n }\n\n requireEntity(id: EntityId): EntityRecord {\n const entity = this.entities.get(id);\n if (!entity) {\n throw new Error(`Unknown entity: ${id}`);\n }\n return entity;\n }\n\n addComponent(id: EntityId, name: ComponentName, value: ComponentValue): void {\n const entity = this.requireEntity(id);\n entity.components[name] = cloneValue(value);\n }\n\n removeComponent(id: EntityId, name: ComponentName): void {\n const entity = this.requireEntity(id);\n delete entity.components[name];\n }\n\n query(requiredComponents: ComponentName[]): EntityRecord[] {\n return [...this.entities.values()].filter((entity) =>\n requiredComponents.every((componentName) => Object.hasOwn(entity.components, componentName))\n );\n }\n\n listEntities(): EntityRecord[] {\n return [...this.entities.values()];\n }\n\n toJSON(): { entities: EntityRecord[] } {\n return {\n entities: [...this.entities.values()].map((entity) => ({\n id: entity.id,\n components: cloneComponents(entity.components)\n }))\n };\n }\n}\n\nexport class ComponentRegistry {\n private readonly names = new Set<ComponentName>();\n\n register(name: ComponentName): void {\n this.names.add(name);\n }\n\n has(name: ComponentName): boolean {\n return this.names.has(name);\n }\n\n list(): ComponentName[] {\n return [...this.names].sort();\n }\n}\n\nexport class SystemRegistry {\n private readonly systems: System[] = [];\n\n register(system: System): void {\n this.systems.push(system);\n }\n\n runFrame(world: World, context: SystemRunContext): void {\n const updateContext: SystemUpdateContext = {\n ...context,\n input: context.input ?? world.input\n };\n\n for (const system of this.systems) {\n system.update(world, updateContext);\n }\n }\n\n run(world: World, deltaSeconds: number): void {\n this.runFrame(world, { deltaSeconds, frame: 0 });\n }\n\n list(): string[] {\n return this.systems.map((system) => system.name);\n }\n}\n\nexport class CharacterControllerInputSystem implements System {\n readonly name = \"CharacterControllerInput\";\n\n update(world: World, context: SystemUpdateContext): void {\n for (const entity of world.query([\"Velocity\", \"CharacterController\"])) {\n if (entity.id !== \"player\") {\n continue;\n }\n\n const velocity = entity.components.Velocity as Velocity;\n const controller = entity.components.CharacterController as CharacterController;\n const horizontal =\n (context.input.isPressed(\"Right\") ? 1 : 0) - (context.input.isPressed(\"Left\") ? 1 : 0);\n\n velocity.linear = [roundDeterministic(horizontal * controller.speed), velocity.linear[1]];\n\n if (context.input.isPressed(\"Jump\") && canJump(entity.components, controller)) {\n velocity.linear = [velocity.linear[0], roundDeterministic(controller.jumpVelocity)];\n }\n }\n }\n}\n\nexport class VelocityIntegrationSystem implements System {\n readonly name = \"VelocityIntegration\";\n\n update(world: World, context: SystemUpdateContext): void {\n integrateMovableVelocity(world, context);\n }\n}\n\nexport class KinematicPhysicsSystem implements System {\n readonly name = \"KinematicPhysics\";\n\n update(world: World, context: SystemUpdateContext): void {\n integrateMovableVelocity(world, context);\n updateGroundedStates(world);\n }\n}\n\nexport class GroundedCheckSystem implements System {\n readonly name = \"GroundedCheck\";\n\n update(world: World): void {\n updateGroundedStates(world);\n }\n}\n\nexport class AnimationSystem implements System {\n readonly name = \"Animation\";\n\n update(world: World, context: SystemUpdateContext): void {\n const clips = collectAnimationClips(world);\n\n for (const entity of world.query([\"Animator\"])) {\n const animator = entity.components.Animator as Animator;\n const activeClipId = resolveActiveClipId(animator);\n const clip = clips.get(activeClipId);\n const orderedFrames = clip ? orderAnimationFrames(clip) : [];\n if (!clip || orderedFrames.length === 0) {\n continue;\n }\n\n const playing = animator.playing ?? animator.autoplay;\n animator.playing = playing;\n animator.activeClip = activeClipId;\n\n if (!playing || animator.speed === 0) {\n const playbackState = resolveAnimationPlaybackState(clip, animator, context.deltaSeconds, false);\n applyAnimationPlaybackState(animator, playbackState, orderedFrames);\n continue;\n }\n\n const playbackState = resolveAnimationPlaybackState(clip, animator, context.deltaSeconds, true);\n applyAnimationPlaybackState(animator, playbackState, orderedFrames);\n\n if (animator.loop === \"once\" && playbackState.finished) {\n animator.playing = false;\n }\n }\n }\n}\n\nexport function createBuiltInComponentRegistry(): ComponentRegistry {\n const registry = new ComponentRegistry();\n for (const name of [\n \"Transform\",\n \"Velocity\",\n \"CharacterController\",\n \"Collider\",\n \"RigidBody\",\n \"PhysicsMaterial\",\n \"GroundedState\",\n \"Parent\",\n \"AnimationClip\",\n \"Animator\",\n \"GeneratedMap\",\n \"Transform3D\",\n \"Camera3D\",\n \"MeshRef\",\n \"MeshRenderer3D\",\n \"Collider3D\"\n ]) {\n registry.register(name);\n }\n return registry;\n}\n\nexport function createBuiltInSystemRegistry(): SystemRegistry {\n const registry = new SystemRegistry();\n registry.register(new CharacterControllerInputSystem());\n registry.register(new KinematicPhysicsSystem());\n registry.register(new AnimationSystem());\n return registry;\n}\n\nexport function createInputState(initialButtons: Iterable<InputButton> = []): InputState {\n return new InputState(initialButtons);\n}\n\nfunction cloneComponents(components: Record<ComponentName, ComponentValue>): Record<ComponentName, ComponentValue> {\n return Object.fromEntries(Object.entries(components).map(([name, value]) => [name, cloneValue(value)]));\n}\n\nfunction cloneValue(value: ComponentValue): ComponentValue {\n if (value === undefined) {\n return undefined;\n }\n return JSON.parse(JSON.stringify(value));\n}\n\nfunction roundDeterministic(value: number): number {\n const rounded = Number(value.toFixed(12));\n const nearestInteger = Math.round(rounded);\n\n if (Math.abs(rounded - nearestInteger) < 1e-9) {\n return nearestInteger;\n }\n\n return rounded;\n}\n\nfunction canJump(components: Record<ComponentName, ComponentValue>, controller: CharacterController): boolean {\n const groundedState = components.GroundedState as GroundedState | undefined;\n if (groundedState) {\n return groundedState.grounded === true;\n }\n\n return typeof controller.groundEntityId === \"string\" && controller.groundEntityId.length > 0;\n}\n\nfunction integrateMovableVelocity(world: World, context: SystemUpdateContext): void {\n for (const entity of world.query([\"Transform\", \"Velocity\"])) {\n if (isStaticBody(entity)) {\n continue;\n }\n\n const transform = entity.components.Transform as Transform;\n const velocity = entity.components.Velocity as Velocity;\n transform.position = [\n roundDeterministic(transform.position[0] + velocity.linear[0] * context.deltaSeconds),\n roundDeterministic(transform.position[1] + velocity.linear[1] * context.deltaSeconds)\n ];\n }\n}\n\nfunction updateGroundedStates(world: World): void {\n const staticColliders = world\n .query([\"Transform\", \"Collider\"])\n .filter((entity) => isStaticCollider(entity) && !isSensorCollider(entity))\n .map((entity) => ({\n entity,\n aabb: getBoxAabb(entity)\n }))\n .filter((entry): entry is { entity: EntityRecord; aabb: Aabb } => entry.aabb !== undefined);\n\n for (const entity of world.query([\"Transform\", \"Collider\", \"GroundedState\"])) {\n const groundedState = entity.components.GroundedState as GroundedState;\n\n if (isStaticBody(entity) || isSensorCollider(entity)) {\n groundedState.grounded = false;\n delete groundedState.groundEntityId;\n continue;\n }\n\n const aabb = getBoxAabb(entity);\n const ground = aabb\n ? staticColliders.find((candidate) => candidate.entity.id !== entity.id && isGroundContact(aabb, candidate.aabb))\n : undefined;\n\n groundedState.grounded = ground !== undefined;\n if (ground) {\n groundedState.groundEntityId = ground.entity.id;\n } else {\n delete groundedState.groundEntityId;\n }\n }\n}\n\nfunction collectAnimationClips(world: World): Map<string, AnimationClip> {\n const clips = new Map<string, AnimationClip>();\n\n for (const entity of world.query([\"AnimationClip\"])) {\n const clip = entity.components.AnimationClip as AnimationClip;\n if (typeof clip.id === \"string\" && Array.isArray(clip.frames)) {\n clips.set(clip.id, clip);\n }\n }\n\n return clips;\n}\n\nfunction resolveActiveClipId(animator: Animator): string {\n return animator.activeClip ?? animator.defaultClip ?? animator.currentClip;\n}\n\ninterface AnimationPlaybackState {\n frameIndex: number;\n rawFrameIndex: number;\n elapsedFrames: number;\n elapsedSeconds: number;\n finished: boolean;\n}\n\nfunction resolveAnimationPlaybackState(\n clip: AnimationClip,\n animator: Animator,\n deltaSeconds: number,\n advance: boolean\n): AnimationPlaybackState {\n const orderedFrames = orderAnimationFrames(clip);\n const frameCount = orderedFrames.length;\n if (frameCount === 0) {\n return {\n frameIndex: 0,\n rawFrameIndex: 0,\n elapsedFrames: 0,\n elapsedSeconds: 0,\n finished: true\n };\n }\n\n if (typeof clip.fps === \"number\" && clip.fps > 0) {\n const elapsedSeconds = roundDeterministic(\n (animator.elapsedSeconds ?? 0) + (advance ? deltaSeconds * animator.speed : 0)\n );\n const rawFrameIndex = Math.floor(roundDeterministic(elapsedSeconds * clip.fps));\n const frameIndex = resolveBoundedFrameIndex(rawFrameIndex, frameCount, animator.loop);\n\n return {\n frameIndex,\n rawFrameIndex,\n elapsedFrames: frameIndex,\n elapsedSeconds,\n finished: rawFrameIndex >= Math.max(frameCount - 1, 0)\n };\n }\n\n const totalFrames = animationClipDurationFrames(orderedFrames);\n const nextElapsedFrames = roundDeterministic((animator.elapsedFrames ?? 0) + (advance ? animator.speed : 0));\n const rawFrameIndex = nextElapsedFrames;\n const elapsedFrames =\n animator.loop === \"loop\"\n ? wrapAnimationFrame(nextElapsedFrames, totalFrames)\n : Math.min(nextElapsedFrames, Math.max(totalFrames - 1, 0));\n const frameIndex = resolveAnimationFrameIndex(orderedFrames, elapsedFrames, animator.loop);\n\n return {\n frameIndex,\n rawFrameIndex,\n elapsedFrames,\n elapsedSeconds: roundDeterministic(elapsedFrames * deltaSeconds),\n finished: rawFrameIndex >= Math.max(totalFrames - 1, 0)\n };\n}\n\nfunction applyAnimationPlaybackState(\n animator: Animator,\n playbackState: AnimationPlaybackState,\n orderedFrames: SpriteFrame[]\n): void {\n animator.currentFrameIndex = playbackState.frameIndex;\n animator.activeFrameId = orderedFrames[playbackState.frameIndex]?.id;\n animator.elapsedFrames = playbackState.elapsedFrames;\n animator.elapsedSeconds = playbackState.elapsedSeconds;\n}\n\nfunction orderAnimationFrames(clip: AnimationClip): SpriteFrame[] {\n if (!Array.isArray(clip.frameOrder) || clip.frameOrder.length === 0) {\n return clip.frames;\n }\n\n const framesById = new Map(clip.frames.map((frame) => [frame.id, frame]));\n const orderedFrames = clip.frameOrder.flatMap((frameId) => {\n const frame = framesById.get(frameId);\n return frame ? [frame] : [];\n });\n\n return orderedFrames.length > 0 ? orderedFrames : clip.frames;\n}\n\nfunction animationClipDurationFrames(frames: SpriteFrame[]): number {\n return frames.reduce((total, frame) => total + frame.durationFrames, 0);\n}\n\nfunction resolveAnimationFrameIndex(frames: SpriteFrame[], elapsedFrames: number, loop: Animator[\"loop\"]): number {\n const totalFrames = animationClipDurationFrames(frames);\n if (totalFrames <= 0) {\n return 0;\n }\n\n let frameCursor = loop === \"loop\" ? wrapAnimationFrame(elapsedFrames, totalFrames) : Math.min(elapsedFrames, totalFrames - 1);\n for (const [index, frame] of frames.entries()) {\n if (frameCursor < frame.durationFrames) {\n return index;\n }\n frameCursor -= frame.durationFrames;\n }\n\n return frames.length - 1;\n}\n\nfunction resolveBoundedFrameIndex(frameIndex: number, frameCount: number, loop: Animator[\"loop\"]): number {\n if (frameCount <= 0) {\n return 0;\n }\n\n return loop === \"loop\" ? frameIndex % frameCount : Math.min(frameIndex, frameCount - 1);\n}\n\nfunction wrapAnimationFrame(frame: number, totalFrames: number): number {\n if (totalFrames <= 0) {\n return 0;\n }\n\n return roundDeterministic(((frame % totalFrames) + totalFrames) % totalFrames);\n}\n\nfunction isStaticBody(entity: EntityRecord): boolean {\n const rigidBody = entity.components.RigidBody as RigidBody | undefined;\n if (rigidBody) {\n return rigidBody.type === \"static\";\n }\n\n const collider = entity.components.Collider as Collider | undefined;\n return collider?.static === true;\n}\n\nfunction isStaticCollider(entity: EntityRecord): boolean {\n const collider = entity.components.Collider as Collider | undefined;\n if (!collider) {\n return false;\n }\n\n const rigidBody = entity.components.RigidBody as RigidBody | undefined;\n return rigidBody ? rigidBody.type === \"static\" : collider.static === true;\n}\n\nfunction isSensorCollider(entity: EntityRecord): boolean {\n return (entity.components.Collider as Collider | undefined)?.sensor === true;\n}\n\ninterface Aabb {\n minX: number;\n maxX: number;\n minY: number;\n maxY: number;\n}\n\nfunction getBoxAabb(entity: EntityRecord): Aabb | undefined {\n const transform = entity.components.Transform as Transform | undefined;\n const collider = entity.components.Collider as Collider | undefined;\n\n if (!transform || !collider || collider.shape !== \"box\") {\n return undefined;\n }\n\n const halfWidth = Math.abs(collider.size[0] * transform.scale[0]) / 2;\n const halfHeight = Math.abs(collider.size[1] * transform.scale[1]) / 2;\n\n return {\n minX: roundDeterministic(transform.position[0] - halfWidth),\n maxX: roundDeterministic(transform.position[0] + halfWidth),\n minY: roundDeterministic(transform.position[1] - halfHeight),\n maxY: roundDeterministic(transform.position[1] + halfHeight)\n };\n}\n\nfunction isGroundContact(movable: Aabb, ground: Aabb): boolean {\n const epsilon = 1e-9;\n const horizontalOverlap = movable.maxX >= ground.minX - epsilon && movable.minX <= ground.maxX + epsilon;\n const groundTopTouchesOrOverlaps = movable.minY <= ground.maxY + epsilon && movable.maxY >= ground.maxY - epsilon;\n const groundIsBelow = ground.maxY <= movable.maxY + epsilon;\n\n return horizontalOverlap && groundTopTouchesOrOverlaps && groundIsBelow;\n}\n\nfunction validateButton(button: InputButton): InputButton {\n if (typeof button !== \"string\" || button.length === 0) {\n throw new Error(\"Input button must be a non-empty string.\");\n }\n\n return button;\n}\n"],"mappings":";AAOO,IAAM,aAAN,MAAiB;AAAA,EACL,iBAAiB,oBAAI,IAAiB;AAAA,EAEvD,YAAY,iBAAwC,CAAC,GAAG;AACtD,eAAW,UAAU,gBAAgB;AACnC,WAAK,MAAM,MAAM;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,IAAI,UAAwC;AAC1C,WAAO,OAAO,YAAY,KAAK,YAAY,EAAE,IAAI,CAAC,WAAW,CAAC,QAAQ,IAAI,CAAC,CAAC;AAAA,EAC9E;AAAA,EAEA,MAAM,QAA2B;AAC/B,SAAK,eAAe,IAAI,eAAe,MAAM,CAAC;AAAA,EAChD;AAAA,EAEA,QAAQ,QAA2B;AACjC,SAAK,eAAe,OAAO,eAAe,MAAM,CAAC;AAAA,EACnD;AAAA,EAEA,UAAU,QAA8B;AACtC,WAAO,KAAK,eAAe,IAAI,eAAe,MAAM,CAAC;AAAA,EACvD;AAAA,EAEA,QAAc;AACZ,SAAK,eAAe,MAAM;AAAA,EAC5B;AAAA,EAEA,cAA6B;AAC3B,WAAO,CAAC,GAAG,KAAK,cAAc,EAAE,KAAK;AAAA,EACvC;AAAA,EAEA,SAAoD;AAClD,WAAO;AAAA,MACL,SAAS,KAAK;AAAA,IAChB;AAAA,EACF;AACF;AAiKO,IAAM,QAAN,MAAY;AAAA,EACA,WAAW,oBAAI,IAA4B;AAAA,EACnD;AAAA,EAET,YAAY,QAAoB,iBAAiB,GAAG;AAClD,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,aAAa,IAAc,aAAoD,CAAC,GAAiB;AAC/F,QAAI,KAAK,SAAS,IAAI,EAAE,GAAG;AACzB,YAAM,IAAI,MAAM,0BAA0B,EAAE,EAAE;AAAA,IAChD;AAEA,UAAM,SAAuB,EAAE,IAAI,YAAY,gBAAgB,UAAU,EAAE;AAC3E,SAAK,SAAS,IAAI,IAAI,MAAM;AAC5B,WAAO;AAAA,EACT;AAAA,EAEA,UAAU,IAAwC;AAChD,WAAO,KAAK,SAAS,IAAI,EAAE;AAAA,EAC7B;AAAA,EAEA,cAAc,IAA4B;AACxC,UAAM,SAAS,KAAK,SAAS,IAAI,EAAE;AACnC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,mBAAmB,EAAE,EAAE;AAAA,IACzC;AACA,WAAO;AAAA,EACT;AAAA,EAEA,aAAa,IAAc,MAAqB,OAA6B;AAC3E,UAAM,SAAS,KAAK,cAAc,EAAE;AACpC,WAAO,WAAW,IAAI,IAAI,WAAW,KAAK;AAAA,EAC5C;AAAA,EAEA,gBAAgB,IAAc,MAA2B;AACvD,UAAM,SAAS,KAAK,cAAc,EAAE;AACpC,WAAO,OAAO,WAAW,IAAI;AAAA,EAC/B;AAAA,EAEA,MAAM,oBAAqD;AACzD,WAAO,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC,EAAE;AAAA,MAAO,CAAC,WACzC,mBAAmB,MAAM,CAAC,kBAAkB,OAAO,OAAO,OAAO,YAAY,aAAa,CAAC;AAAA,IAC7F;AAAA,EACF;AAAA,EAEA,eAA+B;AAC7B,WAAO,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC;AAAA,EACnC;AAAA,EAEA,SAAuC;AACrC,WAAO;AAAA,MACL,UAAU,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC,EAAE,IAAI,CAAC,YAAY;AAAA,QACrD,IAAI,OAAO;AAAA,QACX,YAAY,gBAAgB,OAAO,UAAU;AAAA,MAC/C,EAAE;AAAA,IACJ;AAAA,EACF;AACF;AAEO,IAAM,oBAAN,MAAwB;AAAA,EACZ,QAAQ,oBAAI,IAAmB;AAAA,EAEhD,SAAS,MAA2B;AAClC,SAAK,MAAM,IAAI,IAAI;AAAA,EACrB;AAAA,EAEA,IAAI,MAA8B;AAChC,WAAO,KAAK,MAAM,IAAI,IAAI;AAAA,EAC5B;AAAA,EAEA,OAAwB;AACtB,WAAO,CAAC,GAAG,KAAK,KAAK,EAAE,KAAK;AAAA,EAC9B;AACF;AAEO,IAAM,iBAAN,MAAqB;AAAA,EACT,UAAoB,CAAC;AAAA,EAEtC,SAAS,QAAsB;AAC7B,SAAK,QAAQ,KAAK,MAAM;AAAA,EAC1B;AAAA,EAEA,SAAS,OAAc,SAAiC;AACtD,UAAM,gBAAqC;AAAA,MACzC,GAAG;AAAA,MACH,OAAO,QAAQ,SAAS,MAAM;AAAA,IAChC;AAEA,eAAW,UAAU,KAAK,SAAS;AACjC,aAAO,OAAO,OAAO,aAAa;AAAA,IACpC;AAAA,EACF;AAAA,EAEA,IAAI,OAAc,cAA4B;AAC5C,SAAK,SAAS,OAAO,EAAE,cAAc,OAAO,EAAE,CAAC;AAAA,EACjD;AAAA,EAEA,OAAiB;AACf,WAAO,KAAK,QAAQ,IAAI,CAAC,WAAW,OAAO,IAAI;AAAA,EACjD;AACF;AAEO,IAAM,iCAAN,MAAuD;AAAA,EACnD,OAAO;AAAA,EAEhB,OAAO,OAAc,SAAoC;AACvD,eAAW,UAAU,MAAM,MAAM,CAAC,YAAY,qBAAqB,CAAC,GAAG;AACrE,UAAI,OAAO,OAAO,UAAU;AAC1B;AAAA,MACF;AAEA,YAAM,WAAW,OAAO,WAAW;AACnC,YAAM,aAAa,OAAO,WAAW;AACrC,YAAM,cACH,QAAQ,MAAM,UAAU,OAAO,IAAI,IAAI,MAAM,QAAQ,MAAM,UAAU,MAAM,IAAI,IAAI;AAEtF,eAAS,SAAS,CAAC,mBAAmB,aAAa,WAAW,KAAK,GAAG,SAAS,OAAO,CAAC,CAAC;AAExF,UAAI,QAAQ,MAAM,UAAU,MAAM,KAAK,QAAQ,OAAO,YAAY,UAAU,GAAG;AAC7E,iBAAS,SAAS,CAAC,SAAS,OAAO,CAAC,GAAG,mBAAmB,WAAW,YAAY,CAAC;AAAA,MACpF;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,4BAAN,MAAkD;AAAA,EAC9C,OAAO;AAAA,EAEhB,OAAO,OAAc,SAAoC;AACvD,6BAAyB,OAAO,OAAO;AAAA,EACzC;AACF;AAEO,IAAM,yBAAN,MAA+C;AAAA,EAC3C,OAAO;AAAA,EAEhB,OAAO,OAAc,SAAoC;AACvD,6BAAyB,OAAO,OAAO;AACvC,yBAAqB,KAAK;AAAA,EAC5B;AACF;AAEO,IAAM,sBAAN,MAA4C;AAAA,EACxC,OAAO;AAAA,EAEhB,OAAO,OAAoB;AACzB,yBAAqB,KAAK;AAAA,EAC5B;AACF;AAEO,IAAM,kBAAN,MAAwC;AAAA,EACpC,OAAO;AAAA,EAEhB,OAAO,OAAc,SAAoC;AACvD,UAAM,QAAQ,sBAAsB,KAAK;AAEzC,eAAW,UAAU,MAAM,MAAM,CAAC,UAAU,CAAC,GAAG;AAC9C,YAAM,WAAW,OAAO,WAAW;AACnC,YAAM,eAAe,oBAAoB,QAAQ;AACjD,YAAM,OAAO,MAAM,IAAI,YAAY;AACnC,YAAM,gBAAgB,OAAO,qBAAqB,IAAI,IAAI,CAAC;AAC3D,UAAI,CAAC,QAAQ,cAAc,WAAW,GAAG;AACvC;AAAA,MACF;AAEA,YAAM,UAAU,SAAS,WAAW,SAAS;AAC7C,eAAS,UAAU;AACnB,eAAS,aAAa;AAEtB,UAAI,CAAC,WAAW,SAAS,UAAU,GAAG;AACpC,cAAMA,iBAAgB,8BAA8B,MAAM,UAAU,QAAQ,cAAc,KAAK;AAC/F,oCAA4B,UAAUA,gBAAe,aAAa;AAClE;AAAA,MACF;AAEA,YAAM,gBAAgB,8BAA8B,MAAM,UAAU,QAAQ,cAAc,IAAI;AAC9F,kCAA4B,UAAU,eAAe,aAAa;AAElE,UAAI,SAAS,SAAS,UAAU,cAAc,UAAU;AACtD,iBAAS,UAAU;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,iCAAoD;AAClE,QAAM,WAAW,IAAI,kBAAkB;AACvC,aAAW,QAAQ;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAAG;AACD,aAAS,SAAS,IAAI;AAAA,EACxB;AACA,SAAO;AACT;AAEO,SAAS,8BAA8C;AAC5D,QAAM,WAAW,IAAI,eAAe;AACpC,WAAS,SAAS,IAAI,+BAA+B,CAAC;AACtD,WAAS,SAAS,IAAI,uBAAuB,CAAC;AAC9C,WAAS,SAAS,IAAI,gBAAgB,CAAC;AACvC,SAAO;AACT;AAEO,SAAS,iBAAiB,iBAAwC,CAAC,GAAe;AACvF,SAAO,IAAI,WAAW,cAAc;AACtC;AAEA,SAAS,gBAAgB,YAA0F;AACjH,SAAO,OAAO,YAAY,OAAO,QAAQ,UAAU,EAAE,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,WAAW,KAAK,CAAC,CAAC,CAAC;AACxG;AAEA,SAAS,WAAW,OAAuC;AACzD,MAAI,UAAU,QAAW;AACvB,WAAO;AAAA,EACT;AACA,SAAO,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC;AACzC;AAEA,SAAS,mBAAmB,OAAuB;AACjD,QAAM,UAAU,OAAO,MAAM,QAAQ,EAAE,CAAC;AACxC,QAAM,iBAAiB,KAAK,MAAM,OAAO;AAEzC,MAAI,KAAK,IAAI,UAAU,cAAc,IAAI,MAAM;AAC7C,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,QAAQ,YAAmD,YAA0C;AAC5G,QAAM,gBAAgB,WAAW;AACjC,MAAI,eAAe;AACjB,WAAO,cAAc,aAAa;AAAA,EACpC;AAEA,SAAO,OAAO,WAAW,mBAAmB,YAAY,WAAW,eAAe,SAAS;AAC7F;AAEA,SAAS,yBAAyB,OAAc,SAAoC;AAClF,aAAW,UAAU,MAAM,MAAM,CAAC,aAAa,UAAU,CAAC,GAAG;AAC3D,QAAI,aAAa,MAAM,GAAG;AACxB;AAAA,IACF;AAEA,UAAM,YAAY,OAAO,WAAW;AACpC,UAAM,WAAW,OAAO,WAAW;AACnC,cAAU,WAAW;AAAA,MACnB,mBAAmB,UAAU,SAAS,CAAC,IAAI,SAAS,OAAO,CAAC,IAAI,QAAQ,YAAY;AAAA,MACpF,mBAAmB,UAAU,SAAS,CAAC,IAAI,SAAS,OAAO,CAAC,IAAI,QAAQ,YAAY;AAAA,IACtF;AAAA,EACF;AACF;AAEA,SAAS,qBAAqB,OAAoB;AAChD,QAAM,kBAAkB,MACrB,MAAM,CAAC,aAAa,UAAU,CAAC,EAC/B,OAAO,CAAC,WAAW,iBAAiB,MAAM,KAAK,CAAC,iBAAiB,MAAM,CAAC,EACxE,IAAI,CAAC,YAAY;AAAA,IAChB;AAAA,IACA,MAAM,WAAW,MAAM;AAAA,EACzB,EAAE,EACD,OAAO,CAAC,UAAyD,MAAM,SAAS,MAAS;AAE5F,aAAW,UAAU,MAAM,MAAM,CAAC,aAAa,YAAY,eAAe,CAAC,GAAG;AAC5E,UAAM,gBAAgB,OAAO,WAAW;AAExC,QAAI,aAAa,MAAM,KAAK,iBAAiB,MAAM,GAAG;AACpD,oBAAc,WAAW;AACzB,aAAO,cAAc;AACrB;AAAA,IACF;AAEA,UAAM,OAAO,WAAW,MAAM;AAC9B,UAAM,SAAS,OACX,gBAAgB,KAAK,CAAC,cAAc,UAAU,OAAO,OAAO,OAAO,MAAM,gBAAgB,MAAM,UAAU,IAAI,CAAC,IAC9G;AAEJ,kBAAc,WAAW,WAAW;AACpC,QAAI,QAAQ;AACV,oBAAc,iBAAiB,OAAO,OAAO;AAAA,IAC/C,OAAO;AACL,aAAO,cAAc;AAAA,IACvB;AAAA,EACF;AACF;AAEA,SAAS,sBAAsB,OAA0C;AACvE,QAAM,QAAQ,oBAAI,IAA2B;AAE7C,aAAW,UAAU,MAAM,MAAM,CAAC,eAAe,CAAC,GAAG;AACnD,UAAM,OAAO,OAAO,WAAW;AAC/B,QAAI,OAAO,KAAK,OAAO,YAAY,MAAM,QAAQ,KAAK,MAAM,GAAG;AAC7D,YAAM,IAAI,KAAK,IAAI,IAAI;AAAA,IACzB;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,oBAAoB,UAA4B;AACvD,SAAO,SAAS,cAAc,SAAS,eAAe,SAAS;AACjE;AAUA,SAAS,8BACP,MACA,UACA,cACA,SACwB;AACxB,QAAM,gBAAgB,qBAAqB,IAAI;AAC/C,QAAM,aAAa,cAAc;AACjC,MAAI,eAAe,GAAG;AACpB,WAAO;AAAA,MACL,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,eAAe;AAAA,MACf,gBAAgB;AAAA,MAChB,UAAU;AAAA,IACZ;AAAA,EACF;AAEA,MAAI,OAAO,KAAK,QAAQ,YAAY,KAAK,MAAM,GAAG;AAChD,UAAM,iBAAiB;AAAA,OACpB,SAAS,kBAAkB,MAAM,UAAU,eAAe,SAAS,QAAQ;AAAA,IAC9E;AACA,UAAMC,iBAAgB,KAAK,MAAM,mBAAmB,iBAAiB,KAAK,GAAG,CAAC;AAC9E,UAAMC,cAAa,yBAAyBD,gBAAe,YAAY,SAAS,IAAI;AAEpF,WAAO;AAAA,MACL,YAAAC;AAAA,MACA,eAAAD;AAAA,MACA,eAAeC;AAAA,MACf;AAAA,MACA,UAAUD,kBAAiB,KAAK,IAAI,aAAa,GAAG,CAAC;AAAA,IACvD;AAAA,EACF;AAEA,QAAM,cAAc,4BAA4B,aAAa;AAC7D,QAAM,oBAAoB,oBAAoB,SAAS,iBAAiB,MAAM,UAAU,SAAS,QAAQ,EAAE;AAC3G,QAAM,gBAAgB;AACtB,QAAM,gBACJ,SAAS,SAAS,SACd,mBAAmB,mBAAmB,WAAW,IACjD,KAAK,IAAI,mBAAmB,KAAK,IAAI,cAAc,GAAG,CAAC,CAAC;AAC9D,QAAM,aAAa,2BAA2B,eAAe,eAAe,SAAS,IAAI;AAEzF,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,gBAAgB,mBAAmB,gBAAgB,YAAY;AAAA,IAC/D,UAAU,iBAAiB,KAAK,IAAI,cAAc,GAAG,CAAC;AAAA,EACxD;AACF;AAEA,SAAS,4BACP,UACA,eACA,eACM;AACN,WAAS,oBAAoB,cAAc;AAC3C,WAAS,gBAAgB,cAAc,cAAc,UAAU,GAAG;AAClE,WAAS,gBAAgB,cAAc;AACvC,WAAS,iBAAiB,cAAc;AAC1C;AAEA,SAAS,qBAAqB,MAAoC;AAChE,MAAI,CAAC,MAAM,QAAQ,KAAK,UAAU,KAAK,KAAK,WAAW,WAAW,GAAG;AACnE,WAAO,KAAK;AAAA,EACd;AAEA,QAAM,aAAa,IAAI,IAAI,KAAK,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC;AACxE,QAAM,gBAAgB,KAAK,WAAW,QAAQ,CAAC,YAAY;AACzD,UAAM,QAAQ,WAAW,IAAI,OAAO;AACpC,WAAO,QAAQ,CAAC,KAAK,IAAI,CAAC;AAAA,EAC5B,CAAC;AAED,SAAO,cAAc,SAAS,IAAI,gBAAgB,KAAK;AACzD;AAEA,SAAS,4BAA4B,QAA+B;AAClE,SAAO,OAAO,OAAO,CAAC,OAAO,UAAU,QAAQ,MAAM,gBAAgB,CAAC;AACxE;AAEA,SAAS,2BAA2B,QAAuB,eAAuB,MAAgC;AAChH,QAAM,cAAc,4BAA4B,MAAM;AACtD,MAAI,eAAe,GAAG;AACpB,WAAO;AAAA,EACT;AAEA,MAAI,cAAc,SAAS,SAAS,mBAAmB,eAAe,WAAW,IAAI,KAAK,IAAI,eAAe,cAAc,CAAC;AAC5H,aAAW,CAAC,OAAO,KAAK,KAAK,OAAO,QAAQ,GAAG;AAC7C,QAAI,cAAc,MAAM,gBAAgB;AACtC,aAAO;AAAA,IACT;AACA,mBAAe,MAAM;AAAA,EACvB;AAEA,SAAO,OAAO,SAAS;AACzB;AAEA,SAAS,yBAAyB,YAAoB,YAAoB,MAAgC;AACxG,MAAI,cAAc,GAAG;AACnB,WAAO;AAAA,EACT;AAEA,SAAO,SAAS,SAAS,aAAa,aAAa,KAAK,IAAI,YAAY,aAAa,CAAC;AACxF;AAEA,SAAS,mBAAmB,OAAe,aAA6B;AACtE,MAAI,eAAe,GAAG;AACpB,WAAO;AAAA,EACT;AAEA,SAAO,oBAAqB,QAAQ,cAAe,eAAe,WAAW;AAC/E;AAEA,SAAS,aAAa,QAA+B;AACnD,QAAM,YAAY,OAAO,WAAW;AACpC,MAAI,WAAW;AACb,WAAO,UAAU,SAAS;AAAA,EAC5B;AAEA,QAAM,WAAW,OAAO,WAAW;AACnC,SAAO,UAAU,WAAW;AAC9B;AAEA,SAAS,iBAAiB,QAA+B;AACvD,QAAM,WAAW,OAAO,WAAW;AACnC,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,OAAO,WAAW;AACpC,SAAO,YAAY,UAAU,SAAS,WAAW,SAAS,WAAW;AACvE;AAEA,SAAS,iBAAiB,QAA+B;AACvD,SAAQ,OAAO,WAAW,UAAmC,WAAW;AAC1E;AASA,SAAS,WAAW,QAAwC;AAC1D,QAAM,YAAY,OAAO,WAAW;AACpC,QAAM,WAAW,OAAO,WAAW;AAEnC,MAAI,CAAC,aAAa,CAAC,YAAY,SAAS,UAAU,OAAO;AACvD,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,KAAK,IAAI,SAAS,KAAK,CAAC,IAAI,UAAU,MAAM,CAAC,CAAC,IAAI;AACpE,QAAM,aAAa,KAAK,IAAI,SAAS,KAAK,CAAC,IAAI,UAAU,MAAM,CAAC,CAAC,IAAI;AAErE,SAAO;AAAA,IACL,MAAM,mBAAmB,UAAU,SAAS,CAAC,IAAI,SAAS;AAAA,IAC1D,MAAM,mBAAmB,UAAU,SAAS,CAAC,IAAI,SAAS;AAAA,IAC1D,MAAM,mBAAmB,UAAU,SAAS,CAAC,IAAI,UAAU;AAAA,IAC3D,MAAM,mBAAmB,UAAU,SAAS,CAAC,IAAI,UAAU;AAAA,EAC7D;AACF;AAEA,SAAS,gBAAgB,SAAe,QAAuB;AAC7D,QAAM,UAAU;AAChB,QAAM,oBAAoB,QAAQ,QAAQ,OAAO,OAAO,WAAW,QAAQ,QAAQ,OAAO,OAAO;AACjG,QAAM,6BAA6B,QAAQ,QAAQ,OAAO,OAAO,WAAW,QAAQ,QAAQ,OAAO,OAAO;AAC1G,QAAM,gBAAgB,OAAO,QAAQ,QAAQ,OAAO;AAEpD,SAAO,qBAAqB,8BAA8B;AAC5D;AAEA,SAAS,eAAe,QAAkC;AACxD,MAAI,OAAO,WAAW,YAAY,OAAO,WAAW,GAAG;AACrD,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,SAAO;AACT;","names":["playbackState","rawFrameIndex","frameIndex"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@game_engine/core",
|
|
3
|
+
"version": "0.1.0-alpha",
|
|
4
|
+
"description": "Portable ECS world, components, systems, input state, and deterministic built-ins for Agent Engine.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/rktkdduq01/game_engine.git",
|
|
9
|
+
"directory": "packages/core"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"agent-engine",
|
|
13
|
+
"game-engine",
|
|
14
|
+
"ai-agent",
|
|
15
|
+
"ecs"
|
|
16
|
+
],
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"type": "module",
|
|
24
|
+
"main": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"import": "./dist/index.js"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsup src/index.ts --format esm --dts --sourcemap --clean"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"tsup": "^8.3.5",
|
|
37
|
+
"typescript": "^5.7.2"
|
|
38
|
+
}
|
|
39
|
+
}
|