@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.
Files changed (54) hide show
  1. package/dist/bridge/TileBridge.d.ts +29 -0
  2. package/dist/bridge/TileBridge.d.ts.map +1 -1
  3. package/dist/bridge/TileBridge.js +78 -0
  4. package/dist/excalibur/index.d.ts +48 -0
  5. package/dist/excalibur/index.d.ts.map +1 -0
  6. package/dist/excalibur/index.js +51 -0
  7. package/dist/react/ExcaliburGame.d.ts +109 -0
  8. package/dist/react/ExcaliburGame.d.ts.map +1 -0
  9. package/dist/react/ExcaliburGame.js +215 -0
  10. package/dist/react/index.js +3 -3
  11. package/dist/scene/index.d.ts +3 -41
  12. package/dist/scene/index.d.ts.map +1 -1
  13. package/dist/scene/index.js +1 -49
  14. package/dist/spec/schema.d.ts +12 -12
  15. package/package.json +7 -7
  16. package/dist/pixi/index.d.ts +0 -43
  17. package/dist/pixi/index.d.ts.map +0 -1
  18. package/dist/pixi/index.js +0 -46
  19. package/dist/react/PixiGame.d.ts +0 -138
  20. package/dist/react/PixiGame.d.ts.map +0 -1
  21. package/dist/react/PixiGame.js +0 -237
  22. package/dist/scene/SceneContext.d.ts +0 -173
  23. package/dist/scene/SceneContext.d.ts.map +0 -1
  24. package/dist/scene/SceneContext.js +0 -89
  25. package/dist/scene/SceneFromJson.d.ts +0 -34
  26. package/dist/scene/SceneFromJson.d.ts.map +0 -1
  27. package/dist/scene/SceneFromJson.js +0 -97
  28. package/dist/scene/SceneRenderer.d.ts +0 -29
  29. package/dist/scene/SceneRenderer.d.ts.map +0 -1
  30. package/dist/scene/SceneRenderer.js +0 -312
  31. package/dist/scene/camera/CameraController.d.ts +0 -6
  32. package/dist/scene/camera/CameraController.d.ts.map +0 -1
  33. package/dist/scene/camera/CameraController.js +0 -90
  34. package/dist/scene/components/ComponentRunner.d.ts +0 -22
  35. package/dist/scene/components/ComponentRunner.d.ts.map +0 -1
  36. package/dist/scene/components/ComponentRunner.js +0 -210
  37. package/dist/scene/effects/GlowFilter.d.ts +0 -38
  38. package/dist/scene/effects/GlowFilter.d.ts.map +0 -1
  39. package/dist/scene/effects/GlowFilter.js +0 -40
  40. package/dist/scene/effects/ParticleSystem.d.ts +0 -52
  41. package/dist/scene/effects/ParticleSystem.d.ts.map +0 -1
  42. package/dist/scene/effects/ParticleSystem.js +0 -107
  43. package/dist/scene/entities/EntityGraphics.d.ts +0 -26
  44. package/dist/scene/entities/EntityGraphics.d.ts.map +0 -1
  45. package/dist/scene/entities/EntityGraphics.js +0 -226
  46. package/dist/scene/input/InputManager.d.ts +0 -18
  47. package/dist/scene/input/InputManager.d.ts.map +0 -1
  48. package/dist/scene/input/InputManager.js +0 -86
  49. package/dist/scene/physics/PhysicsEngine.d.ts +0 -15
  50. package/dist/scene/physics/PhysicsEngine.d.ts.map +0 -1
  51. package/dist/scene/physics/PhysicsEngine.js +0 -260
  52. package/dist/scene/timeline/TimelineExecutor.d.ts +0 -6
  53. package/dist/scene/timeline/TimelineExecutor.d.ts.map +0 -1
  54. 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,6 +0,0 @@
1
- import type { SceneContextValue } from '../SceneContext.js';
2
- /**
3
- * Hook to execute timeline events
4
- */
5
- export declare function useTimelineExecutor(context: SceneContextValue): void;
6
- //# sourceMappingURL=TimelineExecutor.d.ts.map
@@ -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
- }