@thewhateverapp/tile-sdk 0.15.3 → 0.15.5
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/bridge/TileBridge.d.ts +29 -0
- package/dist/bridge/TileBridge.d.ts.map +1 -1
- package/dist/bridge/TileBridge.js +78 -0
- package/dist/excalibur/index.d.ts +48 -0
- package/dist/excalibur/index.d.ts.map +1 -0
- package/dist/excalibur/index.js +51 -0
- package/dist/react/ExcaliburGame.d.ts +109 -0
- package/dist/react/ExcaliburGame.d.ts.map +1 -0
- package/dist/react/ExcaliburGame.js +215 -0
- package/dist/react/index.js +3 -3
- package/dist/scene/index.d.ts +3 -41
- package/dist/scene/index.d.ts.map +1 -1
- package/dist/scene/index.js +1 -49
- package/dist/spec/schema.d.ts +12 -12
- package/package.json +7 -7
- package/dist/pixi/index.d.ts +0 -43
- package/dist/pixi/index.d.ts.map +0 -1
- package/dist/pixi/index.js +0 -46
- package/dist/react/PixiGame.d.ts +0 -138
- package/dist/react/PixiGame.d.ts.map +0 -1
- package/dist/react/PixiGame.js +0 -237
- package/dist/scene/SceneContext.d.ts +0 -173
- package/dist/scene/SceneContext.d.ts.map +0 -1
- package/dist/scene/SceneContext.js +0 -89
- package/dist/scene/SceneFromJson.d.ts +0 -34
- package/dist/scene/SceneFromJson.d.ts.map +0 -1
- package/dist/scene/SceneFromJson.js +0 -97
- package/dist/scene/SceneRenderer.d.ts +0 -29
- package/dist/scene/SceneRenderer.d.ts.map +0 -1
- package/dist/scene/SceneRenderer.js +0 -312
- package/dist/scene/camera/CameraController.d.ts +0 -6
- package/dist/scene/camera/CameraController.d.ts.map +0 -1
- package/dist/scene/camera/CameraController.js +0 -90
- package/dist/scene/components/ComponentRunner.d.ts +0 -22
- package/dist/scene/components/ComponentRunner.d.ts.map +0 -1
- package/dist/scene/components/ComponentRunner.js +0 -210
- package/dist/scene/effects/GlowFilter.d.ts +0 -38
- package/dist/scene/effects/GlowFilter.d.ts.map +0 -1
- package/dist/scene/effects/GlowFilter.js +0 -40
- package/dist/scene/effects/ParticleSystem.d.ts +0 -52
- package/dist/scene/effects/ParticleSystem.d.ts.map +0 -1
- package/dist/scene/effects/ParticleSystem.js +0 -107
- package/dist/scene/entities/EntityGraphics.d.ts +0 -26
- package/dist/scene/entities/EntityGraphics.d.ts.map +0 -1
- package/dist/scene/entities/EntityGraphics.js +0 -226
- package/dist/scene/input/InputManager.d.ts +0 -18
- package/dist/scene/input/InputManager.d.ts.map +0 -1
- package/dist/scene/input/InputManager.js +0 -86
- package/dist/scene/physics/PhysicsEngine.d.ts +0 -15
- package/dist/scene/physics/PhysicsEngine.d.ts.map +0 -1
- package/dist/scene/physics/PhysicsEngine.js +0 -260
- package/dist/scene/timeline/TimelineExecutor.d.ts +0 -6
- package/dist/scene/timeline/TimelineExecutor.d.ts.map +0 -1
- package/dist/scene/timeline/TimelineExecutor.js +0 -241
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { useEffect, useRef } from 'react';
|
|
3
|
-
import Matter from 'matter-js';
|
|
4
|
-
import { useGameLoop } from '../../pixi/index.js';
|
|
5
|
-
const { Engine, World, Bodies, Body, Events } = Matter;
|
|
6
|
-
/**
|
|
7
|
-
* Creates a Matter.js body from an entity state
|
|
8
|
-
*/
|
|
9
|
-
function createBody(state, worldGravity) {
|
|
10
|
-
const { entity } = state;
|
|
11
|
-
const bodyConfig = entity.body;
|
|
12
|
-
// No physics body
|
|
13
|
-
if (!bodyConfig || bodyConfig.type === 'none') {
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
const transform = entity.transform;
|
|
17
|
-
const x = transform.x;
|
|
18
|
-
const y = transform.y;
|
|
19
|
-
const rotation = (transform.rotation ?? 0) * (Math.PI / 180);
|
|
20
|
-
// Common body options
|
|
21
|
-
const options = {
|
|
22
|
-
isStatic: bodyConfig.isStatic ?? false,
|
|
23
|
-
isSensor: bodyConfig.type === 'sensor',
|
|
24
|
-
restitution: bodyConfig.restitution ?? 0,
|
|
25
|
-
friction: bodyConfig.friction ?? 0.1,
|
|
26
|
-
frictionAir: 0.01,
|
|
27
|
-
angle: rotation,
|
|
28
|
-
label: entity.id,
|
|
29
|
-
collisionFilter: {
|
|
30
|
-
category: bodyConfig.category ?? 0x0001,
|
|
31
|
-
mask: bodyConfig.mask ?? 0xFFFF,
|
|
32
|
-
},
|
|
33
|
-
};
|
|
34
|
-
let body = null;
|
|
35
|
-
// Create body based on shape
|
|
36
|
-
switch (bodyConfig.shape) {
|
|
37
|
-
case 'rect': {
|
|
38
|
-
const geom = entity.geom;
|
|
39
|
-
body = Bodies.rectangle(x, y, geom.w, geom.h, options);
|
|
40
|
-
break;
|
|
41
|
-
}
|
|
42
|
-
case 'circle': {
|
|
43
|
-
const geom = entity.geom;
|
|
44
|
-
body = Bodies.circle(x, y, geom.r, options);
|
|
45
|
-
break;
|
|
46
|
-
}
|
|
47
|
-
case 'poly': {
|
|
48
|
-
const geom = entity.geom;
|
|
49
|
-
// Convert points to Matter.js format
|
|
50
|
-
const vertices = geom.points.map(([px, py]) => ({ x: px, y: py }));
|
|
51
|
-
body = Bodies.fromVertices(x, y, [vertices], options);
|
|
52
|
-
break;
|
|
53
|
-
}
|
|
54
|
-
default:
|
|
55
|
-
console.warn(`Unknown body shape: ${bodyConfig.shape}`);
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
return body;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Hook to initialize and run the physics engine
|
|
62
|
-
*/
|
|
63
|
-
export function usePhysicsEngine(context, width, height) {
|
|
64
|
-
const { spec, entities, player, engine: engineRef, emitEvent } = context;
|
|
65
|
-
const bodiesMapRef = useRef(new Map());
|
|
66
|
-
// Get gravity from spec
|
|
67
|
-
const gravity = spec.world?.gravity ?? 2600;
|
|
68
|
-
// Initialize engine
|
|
69
|
-
useEffect(() => {
|
|
70
|
-
// Create engine with gravity
|
|
71
|
-
const engine = Engine.create({
|
|
72
|
-
gravity: {
|
|
73
|
-
x: 0,
|
|
74
|
-
y: gravity / 1000, // Matter.js uses different scale
|
|
75
|
-
scale: 0.001,
|
|
76
|
-
},
|
|
77
|
-
});
|
|
78
|
-
engineRef.current = engine;
|
|
79
|
-
// Create bodies for all entities
|
|
80
|
-
const bodiesMap = new Map();
|
|
81
|
-
const bodiesToAdd = [];
|
|
82
|
-
for (const [id, state] of entities.current) {
|
|
83
|
-
const body = createBody(state, gravity);
|
|
84
|
-
if (body) {
|
|
85
|
-
bodiesMap.set(id, body);
|
|
86
|
-
bodiesToAdd.push(body);
|
|
87
|
-
state.body = body;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
// Add all bodies to world
|
|
91
|
-
World.add(engine.world, bodiesToAdd);
|
|
92
|
-
bodiesMapRef.current = bodiesMap;
|
|
93
|
-
// Set up collision events
|
|
94
|
-
Events.on(engine, 'collisionStart', (event) => {
|
|
95
|
-
for (const pair of event.pairs) {
|
|
96
|
-
const entityA = entities.current.get(pair.bodyA.label);
|
|
97
|
-
const entityB = entities.current.get(pair.bodyB.label);
|
|
98
|
-
if (entityA && entityB) {
|
|
99
|
-
handleCollision(entityA, entityB, context);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
// Cleanup
|
|
104
|
-
return () => {
|
|
105
|
-
Events.off(engine, 'collisionStart');
|
|
106
|
-
World.clear(engine.world, false);
|
|
107
|
-
Engine.clear(engine);
|
|
108
|
-
engineRef.current = null;
|
|
109
|
-
};
|
|
110
|
-
}, [spec, gravity, engineRef, entities]);
|
|
111
|
-
// Physics update loop
|
|
112
|
-
useGameLoop((delta) => {
|
|
113
|
-
const engine = engineRef.current;
|
|
114
|
-
if (!engine)
|
|
115
|
-
return;
|
|
116
|
-
const playerState = player.current;
|
|
117
|
-
// Update invincibility timer
|
|
118
|
-
if (playerState.invincible && playerState.invincibilityTimeRemaining > 0) {
|
|
119
|
-
playerState.invincibilityTimeRemaining -= 16.67 * delta;
|
|
120
|
-
if (playerState.invincibilityTimeRemaining <= 0) {
|
|
121
|
-
playerState.invincible = false;
|
|
122
|
-
playerState.invincibilityTimeRemaining = 0;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
// Update gravity direction for player
|
|
126
|
-
engine.gravity.y = (gravity / 1000) * playerState.gravityDir;
|
|
127
|
-
// Step physics (delta is ~1 at 60fps, so we use 16.67ms per frame)
|
|
128
|
-
Engine.update(engine, 16.67 * delta);
|
|
129
|
-
// Sync entity positions from physics bodies
|
|
130
|
-
for (const [id, body] of bodiesMapRef.current) {
|
|
131
|
-
const state = entities.current.get(id);
|
|
132
|
-
if (!state || state.destroyed)
|
|
133
|
-
continue;
|
|
134
|
-
// Update entity position from body
|
|
135
|
-
state.x = body.position.x;
|
|
136
|
-
state.y = body.position.y;
|
|
137
|
-
state.rotation = body.angle;
|
|
138
|
-
state.velocityX = body.velocity.x;
|
|
139
|
-
state.velocityY = body.velocity.y;
|
|
140
|
-
}
|
|
141
|
-
// Check if player is grounded
|
|
142
|
-
const playerEntity = findPlayerEntity(entities.current);
|
|
143
|
-
if (playerEntity?.body) {
|
|
144
|
-
const body = playerEntity.body;
|
|
145
|
-
// Simple ground check - if velocity.y is very small and we're not in the air
|
|
146
|
-
playerState.grounded = Math.abs(body.velocity.y) < 0.5;
|
|
147
|
-
}
|
|
148
|
-
// Check for death (fell off screen) - only if height is valid and not invincible
|
|
149
|
-
if (playerEntity && height > 0 && playerEntity.y > height + 200) {
|
|
150
|
-
if (!playerState.dead && !playerState.invincible) {
|
|
151
|
-
playerState.dead = true;
|
|
152
|
-
playerState.deaths++;
|
|
153
|
-
emitEvent('player.death', { cause: 'fall', deaths: playerState.deaths });
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Handle collision between two entities
|
|
160
|
-
*/
|
|
161
|
-
function handleCollision(entityA, entityB, context) {
|
|
162
|
-
const { player, emitEvent } = context;
|
|
163
|
-
const playerState = player.current;
|
|
164
|
-
// Check if either entity is the player
|
|
165
|
-
const isPlayerA = entityA.entity.tags?.includes('player');
|
|
166
|
-
const isPlayerB = entityB.entity.tags?.includes('player');
|
|
167
|
-
if (!isPlayerA && !isPlayerB)
|
|
168
|
-
return;
|
|
169
|
-
const playerEntity = isPlayerA ? entityA : entityB;
|
|
170
|
-
const otherEntity = isPlayerA ? entityB : entityA;
|
|
171
|
-
const otherTags = otherEntity.entity.tags ?? [];
|
|
172
|
-
// Check for hazard collision (only if not invincible)
|
|
173
|
-
if (otherTags.includes('hazard') || hasComponent(otherEntity, 'KillOnTouch')) {
|
|
174
|
-
if (!playerState.dead && !playerState.invincible) {
|
|
175
|
-
playerState.dead = true;
|
|
176
|
-
playerState.deaths++;
|
|
177
|
-
emitEvent('player.death', { cause: 'hazard', deaths: playerState.deaths });
|
|
178
|
-
}
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
// Check for checkpoint collision
|
|
182
|
-
if (otherTags.includes('checkpoint') || hasComponent(otherEntity, 'Checkpoint')) {
|
|
183
|
-
playerState.checkpointX = otherEntity.x;
|
|
184
|
-
playerState.checkpointY = otherEntity.y;
|
|
185
|
-
emitEvent('checkpoint.reached', { x: otherEntity.x, y: otherEntity.y });
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
// Check for finish line collision
|
|
189
|
-
if (otherTags.includes('finish') || hasComponent(otherEntity, 'FinishLine')) {
|
|
190
|
-
if (!playerState.complete) {
|
|
191
|
-
playerState.complete = true;
|
|
192
|
-
emitEvent('level.complete', { deaths: playerState.deaths });
|
|
193
|
-
}
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
// Check for jump orb collision
|
|
197
|
-
if (otherTags.includes('orb') || hasComponent(otherEntity, 'JumpOrb')) {
|
|
198
|
-
playerState.touchingOrb = otherEntity.entity.id;
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
// Check for speed portal collision
|
|
202
|
-
if (hasComponent(otherEntity, 'SpeedPortal')) {
|
|
203
|
-
const component = getComponent(otherEntity, 'SpeedPortal');
|
|
204
|
-
const params = component?.params;
|
|
205
|
-
if (params?.speed) {
|
|
206
|
-
playerState.speedMultiplier = params.speed / 320; // Base speed
|
|
207
|
-
emitEvent('speed.changed', { speed: params.speed });
|
|
208
|
-
}
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
// Check for gravity portal collision
|
|
212
|
-
if (hasComponent(otherEntity, 'GravityPortal')) {
|
|
213
|
-
playerState.gravityDir *= -1;
|
|
214
|
-
emitEvent('gravity.flipped', { direction: playerState.gravityDir });
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
// Ground collision - reset jump count
|
|
218
|
-
if (otherEntity.entity.body?.isStatic && !otherEntity.entity.body?.type) {
|
|
219
|
-
playerState.grounded = true;
|
|
220
|
-
playerState.jumpCount = 0;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* Find the player entity
|
|
225
|
-
*/
|
|
226
|
-
function findPlayerEntity(entities) {
|
|
227
|
-
for (const [, state] of entities) {
|
|
228
|
-
if (state.entity.tags?.includes('player')) {
|
|
229
|
-
return state;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
return null;
|
|
233
|
-
}
|
|
234
|
-
/**
|
|
235
|
-
* Check if entity has a component
|
|
236
|
-
*/
|
|
237
|
-
function hasComponent(state, type) {
|
|
238
|
-
return state.entity.components?.some((c) => c.type === type) ?? false;
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Get a component from an entity
|
|
242
|
-
*/
|
|
243
|
-
function getComponent(state, type) {
|
|
244
|
-
return state.entity.components?.find((c) => c.type === type);
|
|
245
|
-
}
|
|
246
|
-
/**
|
|
247
|
-
* Apply impulse to a body
|
|
248
|
-
*/
|
|
249
|
-
export function applyImpulse(body, impulseX, impulseY) {
|
|
250
|
-
Body.applyForce(body, body.position, {
|
|
251
|
-
x: impulseX * 0.001,
|
|
252
|
-
y: impulseY * 0.001,
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
/**
|
|
256
|
-
* Set velocity on a body
|
|
257
|
-
*/
|
|
258
|
-
export function setVelocity(body, velocityX, velocityY) {
|
|
259
|
-
Body.setVelocity(body, { x: velocityX, y: velocityY });
|
|
260
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TimelineExecutor.d.ts","sourceRoot":"","sources":["../../../src/scene/timeline/TimelineExecutor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAe,MAAM,oBAAoB,CAAC;AAIzE;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,iBAAiB,QA4C7D"}
|
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { useGameLoop } from '../../pixi/index.js';
|
|
3
|
-
import { getEasing } from '@thewhateverapp/scene-sdk';
|
|
4
|
-
/**
|
|
5
|
-
* Hook to execute timeline events
|
|
6
|
-
*/
|
|
7
|
-
export function useTimelineExecutor(context) {
|
|
8
|
-
const { spec, timeline, entities, camera, emitEvent, spawnEntity, bpm, player } = context;
|
|
9
|
-
const timelineEvents = spec.timeline ?? [];
|
|
10
|
-
useGameLoop((delta) => {
|
|
11
|
-
const state = timeline.current;
|
|
12
|
-
const playerState = player.current;
|
|
13
|
-
const elapsedMs = state.elapsedMs;
|
|
14
|
-
const currentBeat = state.currentBeat;
|
|
15
|
-
// Don't process timeline until game has started
|
|
16
|
-
if (!playerState.started) {
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
// Process pending timeline events
|
|
20
|
-
while (state.nextEventIndex < timelineEvents.length) {
|
|
21
|
-
const event = timelineEvents[state.nextEventIndex];
|
|
22
|
-
// Calculate event trigger time
|
|
23
|
-
let triggerTime;
|
|
24
|
-
if (event.atBeat !== undefined) {
|
|
25
|
-
triggerTime = beatToMs(event.atBeat, bpm);
|
|
26
|
-
}
|
|
27
|
-
else if (event.atTimeMs !== undefined) {
|
|
28
|
-
triggerTime = event.atTimeMs;
|
|
29
|
-
}
|
|
30
|
-
else {
|
|
31
|
-
// No timing specified, skip
|
|
32
|
-
state.nextEventIndex++;
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
// Check if event should fire
|
|
36
|
-
if (elapsedMs >= triggerTime) {
|
|
37
|
-
executeEvent(event, context);
|
|
38
|
-
state.nextEventIndex++;
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
// Events are ordered, so we can stop here
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
// Update active tweens
|
|
46
|
-
updateTweens(state.activeTweens, elapsedMs, context);
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Convert beat number to milliseconds
|
|
51
|
-
*/
|
|
52
|
-
function beatToMs(beat, bpm) {
|
|
53
|
-
return (beat / (bpm / 60)) * 1000;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Execute a timeline event
|
|
57
|
-
*/
|
|
58
|
-
function executeEvent(event, context) {
|
|
59
|
-
const { entities, camera, timeline, emitEvent, spawnEntity } = context;
|
|
60
|
-
switch (event.type) {
|
|
61
|
-
case 'tween': {
|
|
62
|
-
const targetEntity = event.target ? entities.current.get(event.target) : null;
|
|
63
|
-
if (!targetEntity)
|
|
64
|
-
break;
|
|
65
|
-
const params = event.params;
|
|
66
|
-
const startValue = params.from ?? getPropertyValue(targetEntity, params.property);
|
|
67
|
-
const endValue = params.to;
|
|
68
|
-
const duration = params.duration;
|
|
69
|
-
// Add to active tweens
|
|
70
|
-
timeline.current.activeTweens.push({
|
|
71
|
-
targetId: event.target,
|
|
72
|
-
property: params.property,
|
|
73
|
-
startValue,
|
|
74
|
-
endValue,
|
|
75
|
-
startTime: timeline.current.elapsedMs,
|
|
76
|
-
duration,
|
|
77
|
-
easing: params.easing ?? 'linear',
|
|
78
|
-
});
|
|
79
|
-
break;
|
|
80
|
-
}
|
|
81
|
-
case 'pulse': {
|
|
82
|
-
const targetEntity = event.target ? entities.current.get(event.target) : null;
|
|
83
|
-
if (!targetEntity)
|
|
84
|
-
break;
|
|
85
|
-
const params = event.params;
|
|
86
|
-
const originalScale = targetEntity.scaleX;
|
|
87
|
-
const targetScale = params.scale ?? 1.2;
|
|
88
|
-
const duration = params.durationMs ?? (params.durationBeats ? beatToMs(params.durationBeats, context.bpm) : 200);
|
|
89
|
-
// Create scale up tween
|
|
90
|
-
timeline.current.activeTweens.push({
|
|
91
|
-
targetId: event.target,
|
|
92
|
-
property: 'scaleX',
|
|
93
|
-
startValue: originalScale,
|
|
94
|
-
endValue: originalScale * targetScale,
|
|
95
|
-
startTime: timeline.current.elapsedMs,
|
|
96
|
-
duration: duration / 2,
|
|
97
|
-
easing: 'easeOut',
|
|
98
|
-
});
|
|
99
|
-
// Create scale down tween (delayed)
|
|
100
|
-
setTimeout(() => {
|
|
101
|
-
timeline.current.activeTweens.push({
|
|
102
|
-
targetId: event.target,
|
|
103
|
-
property: 'scaleX',
|
|
104
|
-
startValue: originalScale * targetScale,
|
|
105
|
-
endValue: originalScale,
|
|
106
|
-
startTime: timeline.current.elapsedMs,
|
|
107
|
-
duration: duration / 2,
|
|
108
|
-
easing: 'easeIn',
|
|
109
|
-
});
|
|
110
|
-
}, duration / 2);
|
|
111
|
-
break;
|
|
112
|
-
}
|
|
113
|
-
case 'colorSwap': {
|
|
114
|
-
const targetEntity = event.target ? entities.current.get(event.target) : null;
|
|
115
|
-
if (!targetEntity)
|
|
116
|
-
break;
|
|
117
|
-
targetEntity.fill = event.params.to;
|
|
118
|
-
break;
|
|
119
|
-
}
|
|
120
|
-
case 'cameraShake': {
|
|
121
|
-
const params = event.params;
|
|
122
|
-
const duration = params.durationMs ?? (params.durationBeats ? beatToMs(params.durationBeats, context.bpm) : 200);
|
|
123
|
-
camera.current.shakeIntensity = params.strength;
|
|
124
|
-
camera.current.shakeTimeRemaining = duration;
|
|
125
|
-
break;
|
|
126
|
-
}
|
|
127
|
-
case 'spawn': {
|
|
128
|
-
const params = event.params;
|
|
129
|
-
if (params.prefabId) {
|
|
130
|
-
const pos = params.at ?? { x: 0, y: 0 };
|
|
131
|
-
spawnEntity(params.prefabId, pos.x, pos.y);
|
|
132
|
-
}
|
|
133
|
-
break;
|
|
134
|
-
}
|
|
135
|
-
case 'toggle': {
|
|
136
|
-
const targetEntity = event.target ? entities.current.get(event.target) : null;
|
|
137
|
-
if (!targetEntity)
|
|
138
|
-
break;
|
|
139
|
-
const property = event.params.property ?? 'visible';
|
|
140
|
-
if (property === 'visible') {
|
|
141
|
-
targetEntity.visible = event.params.value ?? !targetEntity.visible;
|
|
142
|
-
}
|
|
143
|
-
break;
|
|
144
|
-
}
|
|
145
|
-
case 'set': {
|
|
146
|
-
const targetEntity = event.target ? entities.current.get(event.target) : null;
|
|
147
|
-
if (!targetEntity)
|
|
148
|
-
break;
|
|
149
|
-
setPropertyValue(targetEntity, event.params.path, event.params.value);
|
|
150
|
-
break;
|
|
151
|
-
}
|
|
152
|
-
case 'emit': {
|
|
153
|
-
emitEvent(event.params.event, event.params.payload);
|
|
154
|
-
break;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Update active tweens
|
|
160
|
-
*/
|
|
161
|
-
function updateTweens(tweens, elapsedMs, context) {
|
|
162
|
-
const { entities } = context;
|
|
163
|
-
const completedIndices = [];
|
|
164
|
-
for (let i = 0; i < tweens.length; i++) {
|
|
165
|
-
const tween = tweens[i];
|
|
166
|
-
const targetEntity = entities.current.get(tween.targetId);
|
|
167
|
-
if (!targetEntity) {
|
|
168
|
-
completedIndices.push(i);
|
|
169
|
-
continue;
|
|
170
|
-
}
|
|
171
|
-
const elapsed = elapsedMs - tween.startTime;
|
|
172
|
-
const progress = Math.min(elapsed / tween.duration, 1);
|
|
173
|
-
// Apply easing
|
|
174
|
-
const easingFn = getEasing(tween.easing);
|
|
175
|
-
const easedProgress = easingFn ? easingFn(progress) : progress;
|
|
176
|
-
// Interpolate value
|
|
177
|
-
const value = tween.startValue + (tween.endValue - tween.startValue) * easedProgress;
|
|
178
|
-
// Set property
|
|
179
|
-
setPropertyValue(targetEntity, tween.property, value);
|
|
180
|
-
// Check if complete
|
|
181
|
-
if (progress >= 1) {
|
|
182
|
-
completedIndices.push(i);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
// Remove completed tweens (in reverse order to preserve indices)
|
|
186
|
-
for (let i = completedIndices.length - 1; i >= 0; i--) {
|
|
187
|
-
tweens.splice(completedIndices[i], 1);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* Get a property value from an entity
|
|
192
|
-
*/
|
|
193
|
-
function getPropertyValue(entity, path) {
|
|
194
|
-
switch (path) {
|
|
195
|
-
case 'x':
|
|
196
|
-
return entity.x;
|
|
197
|
-
case 'y':
|
|
198
|
-
return entity.y;
|
|
199
|
-
case 'rotation':
|
|
200
|
-
return entity.rotation;
|
|
201
|
-
case 'scaleX':
|
|
202
|
-
return entity.scaleX;
|
|
203
|
-
case 'scaleY':
|
|
204
|
-
return entity.scaleY;
|
|
205
|
-
case 'alpha':
|
|
206
|
-
return entity.alpha;
|
|
207
|
-
default:
|
|
208
|
-
return 0;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* Set a property value on an entity
|
|
213
|
-
*/
|
|
214
|
-
function setPropertyValue(entity, path, value) {
|
|
215
|
-
switch (path) {
|
|
216
|
-
case 'x':
|
|
217
|
-
entity.x = value;
|
|
218
|
-
break;
|
|
219
|
-
case 'y':
|
|
220
|
-
entity.y = value;
|
|
221
|
-
break;
|
|
222
|
-
case 'rotation':
|
|
223
|
-
entity.rotation = value;
|
|
224
|
-
break;
|
|
225
|
-
case 'scaleX':
|
|
226
|
-
entity.scaleX = value;
|
|
227
|
-
break;
|
|
228
|
-
case 'scaleY':
|
|
229
|
-
entity.scaleY = value;
|
|
230
|
-
break;
|
|
231
|
-
case 'alpha':
|
|
232
|
-
entity.alpha = value;
|
|
233
|
-
break;
|
|
234
|
-
case 'visible':
|
|
235
|
-
entity.visible = value;
|
|
236
|
-
break;
|
|
237
|
-
case 'fill':
|
|
238
|
-
entity.fill = value;
|
|
239
|
-
break;
|
|
240
|
-
}
|
|
241
|
-
}
|