@thewhateverapp/tile-sdk 0.13.37 → 0.14.1

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 (38) hide show
  1. package/dist/scene/SceneContext.d.ts +167 -0
  2. package/dist/scene/SceneContext.d.ts.map +1 -0
  3. package/dist/scene/SceneContext.js +86 -0
  4. package/dist/scene/SceneFromJson.d.ts +27 -0
  5. package/dist/scene/SceneFromJson.d.ts.map +1 -0
  6. package/dist/scene/SceneFromJson.js +74 -0
  7. package/dist/scene/SceneRenderer.d.ts +26 -0
  8. package/dist/scene/SceneRenderer.d.ts.map +1 -0
  9. package/dist/scene/SceneRenderer.js +201 -0
  10. package/dist/scene/camera/CameraController.d.ts +6 -0
  11. package/dist/scene/camera/CameraController.d.ts.map +1 -0
  12. package/dist/scene/camera/CameraController.js +84 -0
  13. package/dist/scene/components/ComponentRunner.d.ts +22 -0
  14. package/dist/scene/components/ComponentRunner.d.ts.map +1 -0
  15. package/dist/scene/components/ComponentRunner.js +197 -0
  16. package/dist/scene/effects/GlowFilter.d.ts +38 -0
  17. package/dist/scene/effects/GlowFilter.d.ts.map +1 -0
  18. package/dist/scene/effects/GlowFilter.js +40 -0
  19. package/dist/scene/effects/ParticleSystem.d.ts +52 -0
  20. package/dist/scene/effects/ParticleSystem.d.ts.map +1 -0
  21. package/dist/scene/effects/ParticleSystem.js +107 -0
  22. package/dist/scene/entities/EntityRenderer.d.ts +14 -0
  23. package/dist/scene/entities/EntityRenderer.d.ts.map +1 -0
  24. package/dist/scene/entities/EntityRenderer.js +203 -0
  25. package/dist/scene/index.d.ts +46 -0
  26. package/dist/scene/index.d.ts.map +1 -0
  27. package/dist/scene/index.js +50 -0
  28. package/dist/scene/input/InputManager.d.ts +18 -0
  29. package/dist/scene/input/InputManager.d.ts.map +1 -0
  30. package/dist/scene/input/InputManager.js +86 -0
  31. package/dist/scene/physics/PhysicsEngine.d.ts +15 -0
  32. package/dist/scene/physics/PhysicsEngine.d.ts.map +1 -0
  33. package/dist/scene/physics/PhysicsEngine.js +252 -0
  34. package/dist/scene/timeline/TimelineExecutor.d.ts +6 -0
  35. package/dist/scene/timeline/TimelineExecutor.d.ts.map +1 -0
  36. package/dist/scene/timeline/TimelineExecutor.js +236 -0
  37. package/dist/spec/schema.d.ts +12 -12
  38. package/package.json +14 -2
@@ -0,0 +1,84 @@
1
+ 'use client';
2
+ import { useGameLoop } from '../../pixi';
3
+ /**
4
+ * Hook to control the camera
5
+ */
6
+ export function useCameraController(context, width, height) {
7
+ const { spec, camera, entities, player, timeline } = context;
8
+ const cameraConfig = spec.camera;
9
+ useGameLoop((delta) => {
10
+ const cameraState = camera.current;
11
+ const playerState = player.current;
12
+ const elapsedMs = timeline.current.elapsedMs;
13
+ // Get camera mode
14
+ const mode = cameraConfig?.mode ?? 'static';
15
+ switch (mode) {
16
+ case 'static': {
17
+ // Camera stays at initial position
18
+ cameraState.x = cameraConfig?.initialX ?? width / 2;
19
+ cameraState.y = cameraConfig?.initialY ?? height / 2;
20
+ break;
21
+ }
22
+ case 'scroll': {
23
+ // Auto-scroll camera
24
+ const scrollSpeed = (cameraConfig?.scrollSpeed ?? 320) * playerState.speedMultiplier;
25
+ const direction = cameraConfig?.scrollDirection ?? 'right';
26
+ switch (direction) {
27
+ case 'right':
28
+ cameraState.x += scrollSpeed * delta * 0.016;
29
+ break;
30
+ case 'left':
31
+ cameraState.x -= scrollSpeed * delta * 0.016;
32
+ break;
33
+ case 'down':
34
+ cameraState.y += scrollSpeed * delta * 0.016;
35
+ break;
36
+ case 'up':
37
+ cameraState.y -= scrollSpeed * delta * 0.016;
38
+ break;
39
+ }
40
+ break;
41
+ }
42
+ case 'follow': {
43
+ // Follow target entity
44
+ const targetId = cameraConfig?.followTarget;
45
+ if (!targetId)
46
+ break;
47
+ const targetEntity = entities.current.get(targetId);
48
+ if (!targetEntity)
49
+ break;
50
+ const smoothing = cameraConfig?.smoothing ?? 0.1;
51
+ const targetX = targetEntity.x;
52
+ const targetY = targetEntity.y;
53
+ // Lerp camera towards target
54
+ cameraState.x += (targetX - cameraState.x) * smoothing;
55
+ cameraState.y += (targetY - cameraState.y) * smoothing;
56
+ break;
57
+ }
58
+ }
59
+ // Apply camera bounds
60
+ const bounds = cameraConfig?.bounds;
61
+ if (bounds) {
62
+ if (bounds.minX !== undefined)
63
+ cameraState.x = Math.max(cameraState.x, bounds.minX);
64
+ if (bounds.maxX !== undefined)
65
+ cameraState.x = Math.min(cameraState.x, bounds.maxX);
66
+ if (bounds.minY !== undefined)
67
+ cameraState.y = Math.max(cameraState.y, bounds.minY);
68
+ if (bounds.maxY !== undefined)
69
+ cameraState.y = Math.min(cameraState.y, bounds.maxY);
70
+ }
71
+ // Apply camera shake
72
+ if (cameraState.shakeTimeRemaining > 0) {
73
+ const intensity = cameraState.shakeIntensity;
74
+ cameraState.x += (Math.random() - 0.5) * intensity;
75
+ cameraState.y += (Math.random() - 0.5) * intensity;
76
+ cameraState.shakeTimeRemaining -= delta * 16.67;
77
+ if (cameraState.shakeTimeRemaining <= 0) {
78
+ cameraState.shakeIntensity = 0;
79
+ }
80
+ }
81
+ // Apply zoom
82
+ cameraState.zoom = cameraConfig?.zoom ?? 1;
83
+ });
84
+ }
@@ -0,0 +1,22 @@
1
+ import type { SceneContextValue, EntityState } from '../SceneContext';
2
+ /**
3
+ * Generic component type
4
+ */
5
+ interface GenericComponent {
6
+ type: string;
7
+ params?: Record<string, unknown>;
8
+ }
9
+ /**
10
+ * Component handler function type
11
+ */
12
+ type ComponentHandler = (state: EntityState, component: GenericComponent, context: SceneContextValue, delta: number) => void;
13
+ /**
14
+ * Hook to run component logic each frame
15
+ */
16
+ export declare function useComponentRunner(context: SceneContextValue): void;
17
+ /**
18
+ * Register a custom component handler
19
+ */
20
+ export declare function registerComponent(type: string, handler: ComponentHandler): void;
21
+ export {};
22
+ //# sourceMappingURL=ComponentRunner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ComponentRunner.d.ts","sourceRoot":"","sources":["../../../src/scene/components/ComponentRunner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAatE;;GAEG;AACH,UAAU,gBAAgB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED;;GAEG;AACH,KAAK,gBAAgB,GAAG,CACtB,KAAK,EAAE,WAAW,EAClB,SAAS,EAAE,gBAAgB,EAC3B,OAAO,EAAE,iBAAiB,EAC1B,KAAK,EAAE,MAAM,KACV,IAAI,CAAC;AAcV;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,QAkB5D;AA+MD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,QAExE"}
@@ -0,0 +1,197 @@
1
+ 'use client';
2
+ import { useGameLoop } from '../../pixi';
3
+ import { isJumpPressed } from '../input/InputManager';
4
+ import { setVelocity } from '../physics/PhysicsEngine';
5
+ import Matter from 'matter-js';
6
+ const { Body } = Matter;
7
+ /**
8
+ * Registry of component handlers
9
+ */
10
+ const componentHandlers = {
11
+ Move: handleMove,
12
+ Rotate: handleRotate,
13
+ Oscillate: handleOscillate,
14
+ Follow: handleFollow,
15
+ DashPlayer: handleDashPlayer,
16
+ // Add more handlers as needed
17
+ };
18
+ /**
19
+ * Hook to run component logic each frame
20
+ */
21
+ export function useComponentRunner(context) {
22
+ const { entities } = context;
23
+ useGameLoop((delta) => {
24
+ for (const [, state] of entities.current) {
25
+ if (state.destroyed)
26
+ continue;
27
+ const components = state.entity.components;
28
+ if (!components)
29
+ continue;
30
+ for (const component of components) {
31
+ const handler = componentHandlers[component.type];
32
+ if (handler) {
33
+ handler(state, component, context, delta);
34
+ }
35
+ }
36
+ }
37
+ });
38
+ }
39
+ /**
40
+ * Move component - applies constant velocity/acceleration
41
+ */
42
+ function handleMove(state, component, context, delta) {
43
+ const params = (component.params ?? {});
44
+ // Apply acceleration
45
+ if (params.accelX) {
46
+ state.velocityX += params.accelX * delta * 0.016;
47
+ }
48
+ if (params.accelY) {
49
+ state.velocityY += params.accelY * delta * 0.016;
50
+ }
51
+ // Apply constant velocity (overrides physics for non-physics entities)
52
+ if (!state.body) {
53
+ if (params.velocityX !== undefined) {
54
+ state.x += params.velocityX * delta * 0.016;
55
+ }
56
+ if (params.velocityY !== undefined) {
57
+ state.y += params.velocityY * delta * 0.016;
58
+ }
59
+ }
60
+ else if (params.velocityX !== undefined || params.velocityY !== undefined) {
61
+ // For physics bodies, set velocity directly
62
+ setVelocity(state.body, params.velocityX ?? state.velocityX, params.velocityY ?? state.velocityY);
63
+ }
64
+ }
65
+ /**
66
+ * Rotate component - spins the entity
67
+ */
68
+ function handleRotate(state, component, context, delta) {
69
+ const params = (component.params ?? { speed: 0 });
70
+ const rotationSpeed = (params.speed * Math.PI) / 180; // Convert to radians/second
71
+ state.rotation += rotationSpeed * delta * 0.016;
72
+ // Update physics body rotation if present
73
+ if (state.body) {
74
+ Body.setAngle(state.body, state.rotation);
75
+ }
76
+ }
77
+ /**
78
+ * Oscillate component - sine wave motion
79
+ */
80
+ function handleOscillate(state, component, context, delta) {
81
+ const params = (component.params ?? { axis: 'y', amplitude: 10, frequency: 1 });
82
+ const { axis, amplitude, frequency, phase = 0 } = params;
83
+ // Get or initialize oscillation state
84
+ if (state.componentState.oscillateTime === undefined) {
85
+ state.componentState.oscillateTime = 0;
86
+ state.componentState.oscillateStartX = state.x;
87
+ state.componentState.oscillateStartY = state.y;
88
+ }
89
+ state.componentState.oscillateTime += delta * 0.016;
90
+ const time = state.componentState.oscillateTime;
91
+ const offset = Math.sin(time * frequency * Math.PI * 2 + phase) * amplitude;
92
+ if (axis === 'x') {
93
+ state.x = state.componentState.oscillateStartX + offset;
94
+ }
95
+ else {
96
+ state.y = state.componentState.oscillateStartY + offset;
97
+ }
98
+ // Update physics body position if present and static
99
+ if (state.body && state.entity.body?.isStatic) {
100
+ Body.setPosition(state.body, { x: state.x, y: state.y });
101
+ }
102
+ }
103
+ /**
104
+ * Follow component - tracks another entity
105
+ */
106
+ function handleFollow(state, component, context, delta) {
107
+ const params = (component.params ?? { target: '' });
108
+ const { target, offsetX = 0, offsetY = 0, smoothing = 0.1 } = params;
109
+ const targetEntity = context.getEntity(target);
110
+ if (!targetEntity)
111
+ return;
112
+ const targetX = targetEntity.x + offsetX;
113
+ const targetY = targetEntity.y + offsetY;
114
+ // Lerp towards target
115
+ state.x += (targetX - state.x) * smoothing;
116
+ state.y += (targetY - state.y) * smoothing;
117
+ // Update physics body if present
118
+ if (state.body) {
119
+ Body.setPosition(state.body, { x: state.x, y: state.y });
120
+ }
121
+ }
122
+ /**
123
+ * DashPlayer component - main player controller for Geometry Dash style games
124
+ */
125
+ function handleDashPlayer(state, component, context, delta) {
126
+ const { player, camera, emitEvent, respawnPlayer } = context;
127
+ const playerState = player.current;
128
+ // Handle death
129
+ if (playerState.dead) {
130
+ // Wait a moment then respawn
131
+ if (state.componentState.deathTimer === undefined) {
132
+ state.componentState.deathTimer = 0;
133
+ }
134
+ state.componentState.deathTimer += delta * 16.67;
135
+ if (state.componentState.deathTimer > 500) {
136
+ state.componentState.deathTimer = 0;
137
+ respawnPlayer();
138
+ }
139
+ return;
140
+ }
141
+ // Handle jump input
142
+ const jumpPressed = isJumpPressed(context);
143
+ const wasJumpPressed = state.componentState.wasJumpPressed ?? false;
144
+ const justPressed = jumpPressed && !wasJumpPressed;
145
+ state.componentState.wasJumpPressed = jumpPressed;
146
+ if (justPressed) {
147
+ // Check if touching an orb
148
+ if (playerState.touchingOrb) {
149
+ const orb = context.getEntity(playerState.touchingOrb);
150
+ if (orb) {
151
+ const orbComponent = orb.entity.components?.find((c) => c.type === 'JumpOrb');
152
+ const impulse = orbComponent?.params?.impulse ?? -900;
153
+ if (state.body) {
154
+ Body.setVelocity(state.body, {
155
+ x: state.body.velocity.x,
156
+ y: impulse * 0.01 * playerState.gravityDir,
157
+ });
158
+ }
159
+ emitEvent('orb.used', { orbId: playerState.touchingOrb });
160
+ playerState.touchingOrb = null;
161
+ }
162
+ }
163
+ // Normal jump from ground
164
+ else if (playerState.grounded) {
165
+ const jumpVelocity = -15 * playerState.gravityDir;
166
+ if (state.body) {
167
+ Body.setVelocity(state.body, {
168
+ x: state.body.velocity.x,
169
+ y: jumpVelocity,
170
+ });
171
+ }
172
+ playerState.grounded = false;
173
+ playerState.jumpCount++;
174
+ emitEvent('player.jump', { jumpCount: playerState.jumpCount });
175
+ }
176
+ }
177
+ // Clear orb touching state if not colliding anymore
178
+ // This is handled in physics collision detection
179
+ // Rotate player based on movement (visual only)
180
+ if (state.body && Math.abs(state.body.velocity.y) > 1) {
181
+ state.rotation += 0.1 * delta * playerState.gravityDir;
182
+ }
183
+ // Apply constant forward velocity (for auto-runners)
184
+ const scrollSpeed = (context.spec.camera?.scrollSpeed ?? 320) * playerState.speedMultiplier;
185
+ if (state.body) {
186
+ Body.setVelocity(state.body, {
187
+ x: scrollSpeed * 0.01,
188
+ y: state.body.velocity.y,
189
+ });
190
+ }
191
+ }
192
+ /**
193
+ * Register a custom component handler
194
+ */
195
+ export function registerComponent(type, handler) {
196
+ componentHandlers[type] = handler;
197
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Glow effect utilities
3
+ *
4
+ * Note: Full glow filter implementation requires @pixi/filter-glow
5
+ * which is an optional peer dependency. This file provides helper
6
+ * functions for creating and managing glow effects.
7
+ */
8
+ export interface GlowOptions {
9
+ color?: string;
10
+ strength?: number;
11
+ blur?: number;
12
+ quality?: number;
13
+ }
14
+ /**
15
+ * Parse color string to number
16
+ */
17
+ export declare function parseColor(color: string): number;
18
+ /**
19
+ * Create glow filter options
20
+ * Returns options compatible with @pixi/filter-glow GlowFilter
21
+ */
22
+ export declare function createGlowOptions(options?: GlowOptions): {
23
+ color: number;
24
+ outerStrength: number;
25
+ innerStrength: number;
26
+ distance: number;
27
+ quality: number;
28
+ };
29
+ /**
30
+ * Check if glow filter is available
31
+ * Returns true if @pixi/filter-glow is installed
32
+ */
33
+ export declare function isGlowFilterAvailable(): boolean;
34
+ /**
35
+ * Create a CSS text-shadow style for glow effect (fallback)
36
+ */
37
+ export declare function createGlowShadow(color: string, strength: number, blur: number): string;
38
+ //# sourceMappingURL=GlowFilter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GlowFilter.d.ts","sourceRoot":"","sources":["../../../src/scene/effects/GlowFilter.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,WAAgB;;;;;;EAe1D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAO/C;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtF"}
@@ -0,0 +1,40 @@
1
+ 'use client';
2
+ /**
3
+ * Parse color string to number
4
+ */
5
+ export function parseColor(color) {
6
+ return parseInt(color.replace('#', ''), 16);
7
+ }
8
+ /**
9
+ * Create glow filter options
10
+ * Returns options compatible with @pixi/filter-glow GlowFilter
11
+ */
12
+ export function createGlowOptions(options = {}) {
13
+ const { color = '#ffffff', strength = 1, blur = 4, quality = 0.1, } = options;
14
+ return {
15
+ color: parseColor(color),
16
+ outerStrength: strength,
17
+ innerStrength: 0,
18
+ distance: blur * 2,
19
+ quality,
20
+ };
21
+ }
22
+ /**
23
+ * Check if glow filter is available
24
+ * Returns true if @pixi/filter-glow is installed
25
+ */
26
+ export function isGlowFilterAvailable() {
27
+ try {
28
+ // Dynamic import check
29
+ return typeof window !== 'undefined';
30
+ }
31
+ catch {
32
+ return false;
33
+ }
34
+ }
35
+ /**
36
+ * Create a CSS text-shadow style for glow effect (fallback)
37
+ */
38
+ export function createGlowShadow(color, strength, blur) {
39
+ return `0 0 ${blur}px ${color}, 0 0 ${blur * 2}px ${color}`;
40
+ }
@@ -0,0 +1,52 @@
1
+ import type { EmitterGeometry } from '@thewhateverapp/scene-sdk';
2
+ /**
3
+ * Particle representation
4
+ */
5
+ export interface Particle {
6
+ x: number;
7
+ y: number;
8
+ velocityX: number;
9
+ velocityY: number;
10
+ life: number;
11
+ maxLife: number;
12
+ scale: number;
13
+ scaleStart: number;
14
+ scaleEnd: number;
15
+ color: number;
16
+ alpha: number;
17
+ }
18
+ /**
19
+ * Particle emitter state
20
+ */
21
+ export interface ParticleEmitter {
22
+ /** Particles in this emitter */
23
+ particles: Particle[];
24
+ /** Emitter configuration */
25
+ config: EmitterGeometry;
26
+ /** Position */
27
+ x: number;
28
+ y: number;
29
+ /** Time since last emission */
30
+ timeSinceEmit: number;
31
+ /** Whether emitter is active */
32
+ active: boolean;
33
+ }
34
+ /**
35
+ * Create a new particle emitter
36
+ */
37
+ export declare function createEmitter(config: EmitterGeometry, x: number, y: number): ParticleEmitter;
38
+ /**
39
+ * Update particle emitter
40
+ * @param emitter The emitter to update
41
+ * @param deltaMs Time since last update in milliseconds
42
+ */
43
+ export declare function updateEmitter(emitter: ParticleEmitter, deltaMs: number): void;
44
+ /**
45
+ * Draw particles to a Graphics object
46
+ */
47
+ export declare function drawParticles(graphics: import('pixi.js').Graphics, emitter: ParticleEmitter): void;
48
+ /**
49
+ * Burst emit multiple particles at once
50
+ */
51
+ export declare function burstEmit(emitter: ParticleEmitter, count: number): void;
52
+ //# sourceMappingURL=ParticleSystem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ParticleSystem.d.ts","sourceRoot":"","sources":["../../../src/scene/effects/ParticleSystem.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAEjE;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,gCAAgC;IAChC,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,4BAA4B;IAC5B,MAAM,EAAE,eAAe,CAAC;IACxB,eAAe;IACf,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,+BAA+B;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,gCAAgC;IAChC,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,eAAe,EACvB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,GACR,eAAe,CASjB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAoC7E;AAyCD;;GAEG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,OAAO,SAAS,EAAE,QAAQ,EACpC,OAAO,EAAE,eAAe,GACvB,IAAI,CAQN;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAIvE"}
@@ -0,0 +1,107 @@
1
+ 'use client';
2
+ /**
3
+ * Create a new particle emitter
4
+ */
5
+ export function createEmitter(config, x, y) {
6
+ return {
7
+ particles: [],
8
+ config,
9
+ x,
10
+ y,
11
+ timeSinceEmit: 0,
12
+ active: true,
13
+ };
14
+ }
15
+ /**
16
+ * Update particle emitter
17
+ * @param emitter The emitter to update
18
+ * @param deltaMs Time since last update in milliseconds
19
+ */
20
+ export function updateEmitter(emitter, deltaMs) {
21
+ const { config, particles } = emitter;
22
+ // Update existing particles
23
+ for (let i = particles.length - 1; i >= 0; i--) {
24
+ const particle = particles[i];
25
+ // Update life
26
+ particle.life -= deltaMs;
27
+ if (particle.life <= 0) {
28
+ particles.splice(i, 1);
29
+ continue;
30
+ }
31
+ // Update position
32
+ particle.x += particle.velocityX * (deltaMs / 1000);
33
+ particle.y += particle.velocityY * (deltaMs / 1000);
34
+ // Update scale
35
+ const lifeRatio = particle.life / particle.maxLife;
36
+ particle.scale = particle.scaleStart + (particle.scaleEnd - particle.scaleStart) * (1 - lifeRatio);
37
+ // Update alpha (fade out)
38
+ particle.alpha = lifeRatio;
39
+ }
40
+ // Emit new particles
41
+ if (emitter.active) {
42
+ const emitInterval = 1000 / config.rate;
43
+ emitter.timeSinceEmit += deltaMs;
44
+ while (emitter.timeSinceEmit >= emitInterval) {
45
+ emitter.timeSinceEmit -= emitInterval;
46
+ emitParticle(emitter);
47
+ }
48
+ }
49
+ }
50
+ /**
51
+ * Emit a single particle
52
+ */
53
+ function emitParticle(emitter) {
54
+ const { config, x, y, particles } = emitter;
55
+ // Calculate random velocity
56
+ const speed = randomRange(config.speed.min, config.speed.max);
57
+ const angle = config.angle
58
+ ? randomRange(config.angle.min, config.angle.max) * (Math.PI / 180)
59
+ : Math.random() * Math.PI * 2;
60
+ const velocityX = Math.cos(angle) * speed;
61
+ const velocityY = Math.sin(angle) * speed;
62
+ // Calculate scale
63
+ const scaleStart = config.scale?.start ?? 1;
64
+ const scaleEnd = config.scale?.end ?? 0;
65
+ // Pick random color
66
+ const colors = config.colors ?? ['#ffffff'];
67
+ const colorStr = colors[Math.floor(Math.random() * colors.length)];
68
+ const color = parseInt(colorStr.replace('#', ''), 16);
69
+ particles.push({
70
+ x,
71
+ y,
72
+ velocityX,
73
+ velocityY,
74
+ life: config.lifetime,
75
+ maxLife: config.lifetime,
76
+ scale: scaleStart,
77
+ scaleStart,
78
+ scaleEnd,
79
+ color,
80
+ alpha: 1,
81
+ });
82
+ }
83
+ /**
84
+ * Draw particles to a Graphics object
85
+ */
86
+ export function drawParticles(graphics, emitter) {
87
+ graphics.clear();
88
+ for (const particle of emitter.particles) {
89
+ graphics.beginFill(particle.color, particle.alpha);
90
+ graphics.drawCircle(particle.x, particle.y, 3 * particle.scale);
91
+ graphics.endFill();
92
+ }
93
+ }
94
+ /**
95
+ * Burst emit multiple particles at once
96
+ */
97
+ export function burstEmit(emitter, count) {
98
+ for (let i = 0; i < count; i++) {
99
+ emitParticle(emitter);
100
+ }
101
+ }
102
+ /**
103
+ * Generate random number in range
104
+ */
105
+ function randomRange(min, max) {
106
+ return min + Math.random() * (max - min);
107
+ }
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ import type { EntityState } from '../SceneContext';
3
+ /**
4
+ * Props for EntityRenderer
5
+ */
6
+ export interface EntityRendererProps {
7
+ state: EntityState;
8
+ debug?: boolean;
9
+ }
10
+ /**
11
+ * Renders a single entity based on its kind
12
+ */
13
+ export declare function EntityRenderer({ state, debug }: EntityRendererProps): React.JSX.Element | null;
14
+ //# sourceMappingURL=EntityRenderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EntityRenderer.d.ts","sourceRoot":"","sources":["../../../src/scene/entities/EntityRenderer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA+B,MAAM,OAAO,CAAC;AAEpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAWnD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,KAAa,EAAE,EAAE,mBAAmB,4BAyG3E"}