@kayelaa/canvas 0.1.0

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/README.md ADDED
@@ -0,0 +1,397 @@
1
+ # @kayelaa/canvas
2
+
3
+ **Build fast 2D canvas games with hooks that feel like React — but actually run like a real game engine**
4
+
5
+ This library gives you:
6
+ - JSX-like syntax to spawn and organize game objects (players, enemies, bullets, UI, particles)
7
+ - Hooks (`useTick`, `usePaint`, `useSelf`, `useEntity`, `useState`, `useRef`, `useEffect`, `useExports`) that feel familiar but are **game-optimized**
8
+ - Direct access to LEA's fast imperative core (positions, drawing, mouse, z-order, collisions)
9
+ - Built-in audio helpers (`LiaAudio`, `LiaOscSFX`) for UI/game sound effects
10
+ - Math & utility tools (`Vector2`, easing functions, raycasting, serializers, etc.)
11
+
12
+ **Important:** This is **not** React-for-canvas.
13
+ There is **no virtual DOM**, **no per-frame re-renders**, **no prop diffing**.
14
+ It's a **very thin declarative layer** on top of LEA — all performance-critical code stays **imperative** and **direct**.
15
+
16
+ Made by **Kayelaa Cagara** (@LianeKayee39 on X / GitHub) in Calamba, Philippines.
17
+
18
+ ## Quick Start — Fully Typed Version (copy-paste this)
19
+
20
+ ```ts
21
+ // main.ts
22
+ import {
23
+ createGame,
24
+ createScene,
25
+ createRenderer,
26
+ useSelf,
27
+ useTick,
28
+ usePaint,
29
+ useEntity,
30
+ FC,
31
+ FCProps,
32
+ Vector2
33
+ useEffect,
34
+ useRef,
35
+ } from '@kayelaa/canvas';
36
+
37
+ // Define typed props (optional but recommended)
38
+ interface PlayerProps extends FCProps {
39
+ initialX?: number;
40
+ initialY?: number;
41
+ }
42
+
43
+ // Optional: expose methods to parent
44
+ interface PlayerExports {
45
+ jump(): void;
46
+ getHealth(): number;
47
+ }
48
+
49
+ // Typed component
50
+ const Player: FC<PlayerProps, PlayerExports> = ({ initialX = 0, initialY = 0 }) => {
51
+ const entity = useEntity();
52
+ const mounted = useRef(false);
53
+
54
+ useEffect(() => {
55
+ if (mounted.current === true) return;
56
+ mounted.current = true;
57
+ entity.x = initialX;
58
+ entity.y = initialY;
59
+ })
60
+
61
+ const self = useSelf(() => ({
62
+ speed: 300,
63
+ health: 100,
64
+
65
+ tick(delta: number) {
66
+ const mouse = renderer.getMousePos();
67
+ const dx = mouse.x - entity.x;
68
+ const dy = mouse.y - entity.y;
69
+ const dist = Math.hypot(dx, dy);
70
+ if (dist > 5) {
71
+ entity.x += (dx / dist) * this.speed * delta;
72
+ entity.y += (dy / dist) * this.speed * delta;
73
+ }
74
+ },
75
+
76
+ jump() {
77
+ entity.y -= 100; // simple jump example
78
+ }
79
+ }));
80
+
81
+ useTick((delta) => self.tick(delta));
82
+
83
+ usePaint((ctx) => {
84
+ ctx.fillStyle = self.health > 50 ? "#00aaff" : "#ff4444";
85
+ ctx.fillRect(entity.x - 20, entity.y - 20, 40, 40);
86
+ });
87
+
88
+ // Expose public API to parent (optional)
89
+ useExports(Player, () => ({
90
+ jump: self.jump,
91
+ getHealth: () => self.health
92
+ }));
93
+
94
+ return null;
95
+ };
96
+
97
+ // Setup (run once)
98
+ const canvas = document.getElementById('game') as HTMLCanvasElement;
99
+ const renderer = createRenderer(canvas);
100
+ renderer.listenPointerUpdates();
101
+
102
+ const game = createGame({ width: 800, height: 600, updateHz: 60 });
103
+ const scene = createScene("main");
104
+
105
+ scene.spawn(<Player initialX={400} initialY={300} />);
106
+
107
+ renderer.attachTo(game);
108
+ scene.attachTo(game);
109
+
110
+ game.start();
111
+ ```
112
+
113
+ ```html
114
+ <!-- index.html -->
115
+ <!DOCTYPE html>
116
+ <html lang="en">
117
+ <head>
118
+ <meta charset="UTF-8" />
119
+ <title>My Game</title>
120
+ <style>
121
+ body { margin: 0; background: #000; }
122
+ canvas { display: block; }
123
+ </style>
124
+ </head>
125
+ <body>
126
+ <canvas id="game" width="800" height="600"></canvas>
127
+ <script type="module" src="/main.ts"></script>
128
+ </body>
129
+ </html>
130
+ ```
131
+
132
+ Run with Vite (recommended):
133
+
134
+ ```bash
135
+ npm create vite@latest my-game -- --template vanilla-ts
136
+ cd my-game
137
+ npm install @kayelaa/canvas
138
+ npm run dev
139
+ ```
140
+
141
+ ## Core Rules — Read This First or Your Game Will Lag
142
+
143
+ These rules are **not suggestions**. Breaking them causes serious performance problems in real games.
144
+
145
+ | Do this | Don't do this | Why it matters (seriously) |
146
+ |-------------------------------------------------------------------------|-------------------------------------------------------------------------------|-----------------------------|
147
+ | Define typed props with `interface MyProps extends FCProps { ... }` | Pass props without types — let TS infer everything | Better autocompletion, catches mistakes early |
148
+ | Use `FC<Props, Exports>` for components that expose methods | Forget to type exports — parent gets `any` | Parent can safely call `jump()`, `takeDamage()` |
149
+ | Mutate `entity.x`, `entity.y`, `entity.z` directly | Duplicate position in `self.pos` unless needed | LEA uses real position for sorting/collision — duplication = bugs + slowdown |
150
+ | Put mutable game logic/state in `useSelf` | Put velocity/health/timers in `useState` | `useState` refresh = full re-run + child re-spawn → lag + GC pressure |
151
+ | Use `key="enemy-${id}"` on dynamic lists | Spawn `<Enemy />` in loop without `key` | No key = no pooling/reuse → memory leak + thrashing |
152
+ | Check `exportsRef.current !== null` before use | Assume child always exists after spawn | Child can die → ref becomes stale → crash |
153
+ | Use `event.preventDefault()` when paused/dead | Run full AI/physics when game-over screen is visible | Wastes CPU for invisible work |
154
+ | Return **clean, typed public API** from `useExports` | Return entire `self` or `entity` to parent | Breaks encapsulation — parent can break internals |
155
+
156
+ ## The Main Hooks — Explained with Real, Typed Code Snippets
157
+
158
+ ### useSelf — Your main stable god-object (use this for almost everything)
159
+
160
+ ```ts
161
+ const self = useSelf(() => ({
162
+ // This object never gets recreated — mutate it freely
163
+ health: 100,
164
+ pos: new Vector2(400, 300),
165
+ vel: new Vector2(0, -200),
166
+
167
+ tick(delta: number) {
168
+ this.pos.x += this.vel.x * delta;
169
+ this.pos.y += this.vel.y * delta;
170
+
171
+ if (this.pos.y < 0) this.vel.y *= -1; // bounce top
172
+ if (this.health <= 0) {
173
+ // death logic — no refresh needed here
174
+ }
175
+ }
176
+ }));
177
+
178
+ useTick((delta: number) => self.tick(delta));
179
+ ```
180
+
181
+ **Edge case — one-time initialization with ref guard**
182
+
183
+ ```ts
184
+ const self = useSelf(() => {
185
+ const audio = new Audio("bgm.mp3");
186
+ audio.loop = true;
187
+ audio.volume = 0.3;
188
+ return { audio, initialized: true };
189
+ });
190
+ ```
191
+
192
+ ### useEntity — Direct access to the real LEA entity
193
+
194
+ ```ts
195
+ const entity = useEntity();
196
+
197
+ useTick(() => {
198
+ entity.x += 5; // move right
199
+ entity.z = entity.y; // sort by Y position (higher = drawn later)
200
+ entity.width = 64; // resize dynamically
201
+ });
202
+ ```
203
+
204
+ **Edge case — dynamic z-layer based on parent**
205
+
206
+ ```ts
207
+ useTick(() => {
208
+ entity.z = playerEntity.y > entity.y ? 10 : -10; // behind or in front of player
209
+ });
210
+ ```
211
+
212
+ ### useTick — Where all game logic lives
213
+
214
+ ```ts
215
+ useTick((delta: number, event: KaylaEvent) => {
216
+ if (isPaused) {
217
+ event.preventDefault(); // skip rest of tick for this object
218
+ return;
219
+ }
220
+
221
+ entity.x += speed * delta;
222
+ });
223
+ ```
224
+
225
+ **Edge case — completely skip when dead**
226
+
227
+ ```ts
228
+ useTick((delta: number, event: KaylaEvent) => {
229
+ if (self.dead) {
230
+ event.preventDefault();
231
+ return;
232
+ }
233
+ // normal movement & AI
234
+ });
235
+ ```
236
+
237
+ ### usePaint — All your drawing code goes here
238
+
239
+ ```ts
240
+ usePaint((ctx: CanvasRenderingContext2D, event: KaylaEvent) => {
241
+ ctx.fillStyle = self.health > 50 ? "#00ff00" : "#ff4444";
242
+ ctx.fillRect(entity.x - 20, entity.y - 20, 40, 40);
243
+ });
244
+ ```
245
+
246
+ **Edge case — disable LEA's default rectangle fill**
247
+
248
+ ```ts
249
+ usePaint((ctx: CanvasRenderingContext2D, event: KaylaEvent) => {
250
+ event.preventDefault(); // skip LEA auto-draw
251
+ // your custom sprite / circle / text
252
+ ctx.beginPath();
253
+ ctx.arc(entity.x, entity.y, 20, 0, Math.PI * 2);
254
+ ctx.fillStyle = "red";
255
+ ctx.fill();
256
+ });
257
+ ```
258
+
259
+ ### useState — Only for rare structural changes
260
+
261
+ ```ts
262
+ const isGameOver = useState<boolean>(false);
263
+
264
+ if (lives <= 0) {
265
+ isGameOver.set(true); // → full refresh → show game over UI, spawn particles
266
+ }
267
+ ```
268
+
269
+ **Dangerous example — never do this**
270
+
271
+ ```ts
272
+ const pos = useState<Vector2>(new Vector2(400, 300));
273
+ // every frame pos.set(...) → game freezes in 2 seconds
274
+ ```
275
+
276
+ **Safe & common use cases**
277
+ - `isDead` → spawn death animation / particles
278
+ - `currentLevel` → reload map / change music
279
+ - `weaponType` → swap sprite / fire rate
280
+ - `gameMode` → menu → playing → pause → game over
281
+
282
+ ### useRef — Cheap, persistent storage
283
+
284
+ ```ts
285
+ const timer = useRef<number>(0);
286
+ useTick((delta) => {
287
+ timer.current += delta;
288
+ });
289
+ ```
290
+
291
+ **Edge case — keep audio alive across refreshes**
292
+
293
+ ```ts
294
+ const sound = useRef<HTMLAudioElement | null>(null);
295
+
296
+ useEffect(() => {
297
+ sound.current = new Audio("hit.mp3");
298
+ return () => {
299
+ sound.current?.pause();
300
+ };
301
+ }, []);
302
+ ```
303
+
304
+ ### useExports — Let parents control you (with types)
305
+
306
+ ```ts
307
+ interface PlayerExports {
308
+ jump(): void;
309
+ takeDamage(amount: number): void;
310
+ getHealth(): number;
311
+ }
312
+
313
+ const Player: FC<PlayerProps, PlayerExports> = () => {
314
+ // ...
315
+
316
+ useExports(Player, () => ({
317
+ jump() {
318
+ self.vel.y = -400;
319
+ },
320
+ takeDamage(amount: number) {
321
+ self.health -= amount;
322
+ },
323
+ getHealth() {
324
+ return self.health;
325
+ }
326
+ }));
327
+ };
328
+ ```
329
+
330
+ Parent usage:
331
+
332
+ ```ts
333
+ const playerApi = useRef<PlayerExports | null>(null);
334
+ <Player exportsRef={playerApi} />
335
+
336
+ useTick(() => {
337
+ if (playerApi.current) {
338
+ playerApi.current.jump();
339
+ }
340
+ });
341
+ ```
342
+
343
+ **Best practice** — always type exports and return clean API
344
+
345
+ ```ts
346
+ useExports(Player, () => ({
347
+ jump: self.jump,
348
+ takeDamage: (amt: number) => { self.health -= amt; },
349
+ // defensive copy to prevent parent mutation
350
+ getPosition: (): { x: number; y: number } => ({ x: entity.x, y: entity.y })
351
+ }));
352
+ ```
353
+
354
+ ## Do / Don't Table — Semantic Best Practices
355
+
356
+ | Do this | Don't do this | Why it matters (semantic & performance) |
357
+ |-------------------------------------------------------------------------|-------------------------------------------------------------------------------|-----------------------------------------|
358
+ | Define `interface MyProps extends FCProps { ... }` | Pass props without types — let TS infer everything | Better autocompletion, catches mistakes early |
359
+ | Use `FC<Props, Exports>` when component exposes methods | Forget to type exports — parent gets `any` | Parent can safely call `jump()`, `takeDamage()` |
360
+ | Mutate `entity.x`, `entity.y`, `entity.z` directly | Duplicate position in `self.pos` unless needed | LEA uses real position for sorting/collision — duplication = bugs + slowdown |
361
+ | Put mutable game logic/state in `useSelf` | Put velocity/health/timers in `useState` | `useState` refresh = full re-run + child re-spawn → lag + GC pressure |
362
+ | Use `key="enemy-${id}"` on dynamic lists | Spawn `<Enemy />` in loop without `key` | No key = no pooling/reuse → memory leak + thrashing |
363
+ | Check `exportsRef.current !== null` before use | Assume child always exists after spawn | Child can die → ref becomes stale → crash |
364
+ | Use `event.preventDefault()` when paused/dead | Run full AI/physics when game-over screen is visible | Wastes CPU for invisible work |
365
+ | Return **clean, typed public API** from `useExports` | Return entire `self` or `entity` to parent | Breaks encapsulation — parent can break internals |
366
+
367
+ ## Compatibility & Tooling Notes
368
+
369
+ - **Primary target**: Browser + Vite (vanilla TS or JS)
370
+ - **React coexistence**: Yes — works side-by-side without conflict.
371
+ Just add to `tsconfig.json` or `vite.config.ts`:
372
+
373
+ ```json
374
+ {
375
+ "compilerOptions": {
376
+ "jsx": "react-jsx",
377
+ "jsxImportSource": "@kayelaa/canvas"
378
+ }
379
+ }
380
+ ```
381
+
382
+ - **No JSX?** Use `createElement` as fallback:
383
+
384
+ ```ts
385
+ scene.spawn(createElement(Player, { speed: 300 }));
386
+ ```
387
+
388
+ - **Node.js**: Non-canvas parts (`Vector2`, math utils, serializers, timeouts, tickers) work fine.
389
+ Canvas/audio parts throw or do nothing (no DOM/Web Audio API).
390
+
391
+ - **Typescript**: Make sure to use this with typescript tooling for enhanced descriptions/documentation in the IDE.
392
+
393
+ Happy building!
394
+ If you make something cool, tag me on X @LianeKayee39 — I’d love to see it.
395
+
396
+ Made with love in Calamba, Philippines — 2026
397
+ Kayelaa Cagara
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";var $=Object.defineProperty;var Te=Object.getOwnPropertyDescriptor;var Se=Object.getOwnPropertyNames;var Ie=Object.prototype.hasOwnProperty;var L=(n,e)=>{for(var t in e)$(n,t,{get:e[t],enumerable:!0})},Ke=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of Se(e))!Ie.call(n,s)&&s!==t&&$(n,s,{get:()=>e[s],enumerable:!(r=Te(e,s))||r.enumerable});return n};var Re=n=>Ke($({},"__esModule",{value:!0}),n);var Ne={};L(Ne,{Kayla:()=>B,LEA:()=>O,default:()=>Ge});module.exports=Re(Ne);var O={};L(O,{DeltaTweenII:()=>N,ENVIRONMENT:()=>j,GEmitterMemory:()=>W,LeaEntityII:()=>K,LeaEventEmitter:()=>w,LeaGameII:()=>A,LeaRendererII:()=>M,LeaSceneII:()=>_,LeaSerializers:()=>J,LeaTickerII:()=>G,LeaTimeout:()=>z,LeaUtilsII:()=>g,LiaAudio:()=>R,LiaAudioSrc:()=>I,LiaOscSFX:()=>C,LiaSFXMap:()=>Fe,NOTE_NAMES:()=>te,RectLeaEntity:()=>F,Vector2:()=>S,colToRGBA:()=>Pe,defaultSFXConfig:()=>ne,editRGBA:()=>Me,generateUUID:()=>ue,getAvoidAngle:()=>he,getEnvironment:()=>ee,getNormalizedColor:()=>ke,getRayHit:()=>ce,isInitiallyMobile:()=>Le,isMobile:()=>pe,isNode:()=>V,isNote:()=>Ae,isWeb:()=>v,parseFillStyle:()=>Z,raycastAvoid:()=>De,scaleCoord:()=>_e,setAnimInterval:()=>le,sfxHit:()=>oe,sfxJump:()=>ie,sfxLaser:()=>ae,sfxUIClick:()=>re,sfxUIHover:()=>se,shortUID:()=>Ve,tinyUID:()=>Y});var w=class{#e=new Map;constructor(){this.#e=new Map}on(e,t){let r=this.#e.get(e)||[];return r.push(t),this.#e.set(e,r),this}once(e,t){let r=(...s)=>{this.off(e,r),t(...s)};return this.on(e,r),this}off(e,t){let r=this.#e.get(e);if(!r)return this;let s=r.indexOf(t);return s>=0&&r.splice(s,1),this}emit(e,...t){let r=this.#e.get(e);if(!r||r.length===0){if(e==="error")throw t[0];return!1}return r.slice().forEach(s=>s(...t)),!0}removeAllListeners(e){return e?this.#e.delete(e):this.#e.clear(),this}listenerCount(e){return this.#e.get(e)?.length??0}},M=class extends w{canvas;ctx;running;_rafId;_fps;_frameCount;_fpsTimer;_lastFrameTime;constructor(e,{viewportWidth:t,viewportHeight:r,cameraWidth:s,cameraHeight:u}={}){if(!v)throw new Error("Web-Only");super(),this.canvas=e,this.ctx=e.getContext("2d"),this.automatic=!0,this.#e=t??e.width,this.#t=r??e.height,this.#n=s??e.width,this.#r=u??e.height,this.running=!1,this._rafId=null,this._loop=this._loop.bind(this),this.updateCanvasResolution(),this._fps=60,this._frameCount=0,this._fpsTimer=0}automatic;retransform(){this.ctx.setTransform(1,0,0,1,0,0);let e=this.#n/this.#e,t=this.#r/this.#t;this.ctx.translate(this.#n/2,this.#r/2),this.ctx.scale(e,t),this.ctx.translate(-this.#e/2,-this.#t/2)}#e=0;#t=0;#n=0;#r=0;get viewportWidth(){return this.#e}set viewportWidth(e){this.#e=e,this.retransform()}get width(){return this.#e}get height(){return this.#t}get centerX(){return this.#e/2}get centerY(){return this.#t/2}get left(){return 0}get top(){return 0}get right(){return this.#e}get bottom(){return this.#t}get viewportHeight(){return this.#t}set viewportHeight(e){this.#t=e,this.retransform()}get cameraWidth(){return this.#n}set cameraWidth(e){this.#n=e,this.updateCanvasResolution(),this.retransform()}get cameraHeight(){return this.#r}set cameraHeight(e){this.#r=e,this.updateCanvasResolution(),this.retransform()}updateCanvasResolution(){this.canvas.width=this.#n,this.canvas.height=this.#r}applyTransform(){this.retransform()}get FPS(){return this._fps}_loop(){this.automatic&&this.update(),this._rafId=requestAnimationFrame(this._loop)}update(){if(!this.running||!v)return;let e=performance.now(),t=(e-(this._lastFrameTime??e))/1e3;this._lastFrameTime=e,this._frameCount++,this._fpsTimer+=t,this._fpsTimer>=1&&(this._fps=Math.round(this._frameCount/this._fpsTimer),this._frameCount=0,this._fpsTimer=0),this.ctx.clearRect(0,0,this.#n,this.#r),this.emit("draw",this.ctx)}start(){if(!this.running){if(!v||typeof globalThis.requestAnimationFrame>"u")throw new Error("Raf is not supported");this.running=!0,this._rafId=requestAnimationFrame(this._loop)}}stop(){if(!v||typeof globalThis.requestAnimationFrame>"u")throw new Error("Raf is not supported");this.running=!1,this._rafId!==null&&(cancelAnimationFrame(this._rafId),this._rafId=null)}},G=class extends w{setNow(e){this.#e=e}#e=0;__intervalId=null;__lastTime=0;constructor(e=16){super(),this.#t=e,this.speedHackDT=1}speedHackDT;get isRaf(){return this.tickInterval===1/0}#t=15;get tickInterval(){return this.#t}set tickInterval(e){this.#t=e,this.__intervalId&&(this.stop(),this.start())}now(){return this.#e}__tick(){let e=performance.now(),t=(e-this.__lastTime)/1e3*this.speedHackDT;this.__lastTime=e,this.#e+=t*1e3,this.emit("tick",t)}createTimeout(e){return new z(e,this)}createTween(e,t=()=>{}){let r=new N(e),s=(u=0)=>{if(r.finished){this.off("tick",s);return}r.update(u)};return r.on("finish",()=>{this.off("tick",s)}),r.on("delta",u=>{t(u)}),this.on("tick",s),r}start(){this.__intervalId===null&&(this.__lastTime=performance.now(),this.__intervalId=v&&!isFinite(this.tickInterval)?le(()=>this.__tick()).clear:setInterval(()=>this.__tick(),this.tickInterval))}stop(){this.__intervalId!==null&&(typeof this.__intervalId=="function"?this.__intervalId():clearInterval(this.__intervalId)),this.__intervalId=null}getSineMod(e,t=0){return .5+.5*Math.sin((this.now()%e/e+t)*2*Math.PI)}},J;(l=>{function n(f){return f===!0?1:0}l.booleanExport=n;function e(f){return f===0?!1:f===1}l.booleanImport=e;function t(f){return`${f.x}|${f.y}`}l.vec2Export=t;function r(f){let[m,b]=f.split("|"),E=parseFloat(m),D=parseFloat(b);if(isNaN(E)||isNaN(D))throw new Error(`Invalid Vector2 string: ${f}`);return new S(E,D)}l.vec2Import=r,l.booleanMap={mapExport:n,mapImport:e};function u(f){return m=>Number(m.toFixed(f))}l.createRounder=u;function c(f=10){return{mapExport(m){return Math.round(m/f)},mapImport(m){return m*f}}}l.createLowPrecision=c;function h(f){return Math.round(f)}l.lightWeightRounder=h;function a(f=100){return{mapExport(m){return Math.round(m*f)},mapImport(m){return m/f}}}l.createPercent=a;function o(f){let m=new Map(Object.entries(f));return{mapExport:b=>m.get(b)??null,mapImport:b=>Array.from(m.entries()).find(([E,D])=>D===b)?.[0]??null}}l.createLookup=o;function d(f){let m=f*(180/Math.PI);return Math.round((m%360+360)%360)}l.radToDeg=d;function y(f){return f*(Math.PI/180)}l.degToRad=y,l.angleRadToDeg={mapExport:d,mapImport:y};function p(f=10){let m=c(f);return{mapExport(b){return m.mapExport(d(b))},mapImport(b){return m.mapImport(y(b))}}}l.createLowPrecisionRadToDeg=p})(J||={});var K=class n extends w{name="";scaleRotate=0;scale=1;constructor(e,t=0,r=0){super(),this.autoTranslate=!1,this.name=e,this.z=0,this.___pos=new S(t,r),this.nonSerializableProperties=[],this.nonSerializableProperties.push("___pos","autoTranslate","arraySerializeMap"),this.forceSerializableProperties=[],this.forceSerializableProperties.push("x","y")}arraySerializeMap;autoTranslate;z;___pos;nonSerializableProperties;forceSerializableProperties;get pos(){return this.___pos}get x(){return this.pos.x}get y(){return this.pos.y}set x(e){this.pos.x=e}set y(e){this.pos.y=e}handleUpdate(e){if(this.update)try{this.emit("update",e),this.update(e)}catch(t){this.emit("error",t)}}handleDraw(e){if(!(V||!this.draw)){e.save(),this.autoTranslate&&e.translate(this.x,this.y);try{this.emit("draw",e),this.draw(e)}catch(t){this.emit("error",t)}e.restore()}}serialize(){if(Array.isArray(this.arraySerializeMap))return this.arraySerializeMap.map(([s,{mapExport:u}])=>{let c=Reflect.get(this,s);return u?u(c):c});let e=["_events","_eventsCount","_maxListeners","nonSerializableProperties","forceSerializableProperties"],t=[...Reflect.ownKeys(this),...this.forceSerializableProperties].filter(s=>!this.nonSerializableProperties.includes(s)&&!e.includes(s.toString())),r=Object.fromEntries(t.map(s=>{let u=Reflect.get(this,s);if(V&&typeof u=="number"){let c=u.toString().split("."),a=(c[1]?c[1].length:0)>2?Number(u.toFixed(2)):u;return[s,a]}return[s,u]}));return JSON.parse(JSON.stringify(r))}toLocal(e){return e.subtract(this.pos)}toWorld(e){return e.add(this.pos)}deserializeArray(e){return n.deserializeArray(this.arraySerializeMap,e)}static deserializeArray(e,t){if(!e||!Array.isArray(t))return t;let r={};for(let s=0;s<t.length;s++){let u=e[s];if(!u)break;let[c,{mapImport:h}]=u,a=t[s];if(h&&(a=h(a)),typeof c!="string")break;try{Reflect.set(r,c,a)}catch(o){console.error(o)}}return r}},_=class extends w{name="";entities=new Map;paused=!0;constructor(e){super(),this.name=e}handleUpdate(e){if(!this.paused){this.emit("update",e);for(let t of this.entities.values())t.handleUpdate(e)}}handleDraw(e){if(!V&&!this.paused){this.emit("draw",e);for(let t of[...this.entities.values()].sort((r,s)=>r.z-s.z))t.handleDraw(e)}}addEntity(e){if(!(e instanceof K))throw new Error("invalid entity");if(!e.name)throw new Error("Entity must have a name.");this.entities.set(e.name,e)}removeEntity(e){if(!(e instanceof K))throw new Error("invalid entity");this.entities.delete(e.name)}getEntity(e){return this.entities.get(e)}},A=class{scenes;ticker;get centerX(){return this.width/2}get centerY(){return this.height/2}get left(){return 0}get top(){return 0}get right(){return this.width}get bottom(){return this.height}width;height;constructor(e,t,r=16){this.ticker=new G(r),this.scenes=new Map,this.width=e,this.height=t,this.ticker.on("tick",s=>{for(let u of this.scenes.values())u.paused||u.handleUpdate(s)})}addScene(e){if(!e.name)throw new Error("Scene must have a name.");e.paused=!1,this.scenes.set(e.name,e)}removeScene(e){e&&(e.paused=!0),this.scenes.delete(e.name)}now(){return this.ticker.now()}start(){this.ticker.start()}stop(){this.ticker.stop()}},g={lerp(n,e,t){return n+(e-n)*t},clamp(n,e,t){return Math.min(t,Math.max(e,n))},clamp01(n){return Math.min(1,Math.max(0,n))},easeLinear(n){return n},easeInQuad(n){return n*n},easeOutQuad(n){return 1-(1-n)*(1-n)},easeInOutQuad(n){return n<.5?2*n*n:1-Math.pow(-2*n+2,2)/2},easeInSine(n){return 1-Math.cos(n*Math.PI/2)},easeOutSine(n){return Math.sin(n*Math.PI/2)},easeInOutSine(n){return-(Math.cos(Math.PI*n)-1)/2},easeInExpo(n){return n===0?0:Math.pow(2,10*n-10)},easeOutExpo(n){return n===1?1:1-Math.pow(2,-10*n)},easeInOutExpo(n){return n===0?0:n===1?1:n<.5?Math.pow(2,20*n-10)/2:(2-Math.pow(2,-20*n+10))/2},smoothstep(n){return n=g.clamp(n,0,1),n*n*(3-2*n)},randomLerp(n,e){return g.lerp(n,e,Math.random())},randomInt(n,e){return Math.floor(Math.random()*(e-n+1))+n},randomArrayValue(n){return n[g.randomInt(0,n.length-1)]},createBezier(n,e,t,r){function s(h,a,o,d,y){let i=1-h;return i*i*i*a+3*i*i*h*o+3*i*h*h*d+h*h*h*y}function u(h,a,o,d,y){let i=1-h;return 3*i*i*(o-a)+6*i*h*(d-o)+3*h*h*(y-d)}function c(h){let a=h;for(let o=0;o<6;o++){let d=s(a,0,n,t,1),y=u(a,0,n,t,1);if(y===0)break;a-=(d-h)/y}return g.clamp(a,0,1)}return function(a){a=g.clamp(a,0,1);let o=c(a);return s(o,0,e,r,1)}},lengthSquared(...n){return n.reduce((e,t)=>e+t*t,0)},normalizeRad(n){let e=2*Math.PI;return n=n%e,n<0&&(n+=e),n},angleInvertY(n){return g.normalizeRad(-n)},degToRadFlipY(n){return g.angleInvertY(n*Math.PI/180)},minimalAngularDirection(n,e){n=g.normalizeRad(n),e=g.normalizeRad(e);let t=g.normalizeRad(e-n),r=g.normalizeRad(n-e);return t<=r?1:-1}},N=class extends w{constructor({delta:e,ms:t,easing:r}){super(),this.delta=e,this.duration=t,this.elapsed=0,this.easing=r??(s=>s),this.lastValue=0,this.finished=!1}delta;duration;elapsed;lastValue;finished;easing;update(e){this.elapsed+=e*1e3;let t=g.clamp(this.elapsed/this.duration,0,1),r=this.easing(t),s=this.delta*r,u=s-this.lastValue;this.lastValue=s,this.emit("delta",u),t>=1&&(this.finished=!0,this.emit("finish",void 0))}},F=class extends K{constructor(e,t=0,r=0,s=50,u=50){super(e,t,r),this.width=s,this.height=u}width;height;get left(){return this.x-this.width/2}set left(e){this.x=e+this.width/2}get right(){return this.x+this.width/2}set right(e){this.x=e-this.width/2}get top(){return this.y-this.height/2}set top(e){this.y=e+this.height/2}get bottom(){return this.y+this.height/2}set bottom(e){this.y=e-this.height/2}get lx(){return 0}get ly(){return 0}get lleft(){return-this.width/2}get lright(){return this.width/2}get ltop(){return-this.height/2}get lbottom(){return this.height/2}isCollidingWith(e){return!(this.right<e.left||this.left>e.right||this.bottom<e.top||this.top>e.bottom)}color="rgba(0, 0, 255, 0.3)";draw(e){e.translate(this.x,this.y),e.rotate(this.scaleRotate),e.fillStyle=this.color,e.fillRect(-this.width/2,-this.height/2,this.width,this.height)}},z=class extends w{duration;ticker;elapsed;finished;_resolve;promise;_timeoutId;constructor(e,t=null){super(),this.duration=e,this.ticker=t,this.elapsed=0,this.finished=!1,this._resolve=null,this.promise=new Promise(r=>this._resolve=r),this.update=this.update.bind(this),this.ticker&&this.ticker.on("tick",this.update)}update(e=0){this.finished||(this.elapsed+=e*1e3,this.elapsed>=this.duration&&this.finish())}finish(){this.finished||(this.finished=!0,this.emit("finish",void 0),this._resolve&&this._resolve(),this.ticker&&this.ticker.off("tick",this.update))}start(){return this.ticker||(this._timeoutId=setTimeout(()=>this.finish(),this.duration)),this}cancel(){this.finished||(this.finished=!0,!this.ticker&&this._timeoutId!=null&&clearTimeout(this._timeoutId),this.ticker&&this.ticker.off("tick",this.update))}then(e,t){return this.promise.then(e,t)}after(e,t){return this.promise.then(e,t)}},S=class n{constructor(e=0,t=0){this.x=e,this.y=t}x;y;toJSON(){return{x:this.x,y:this.y,vec2:!0}}static isVec2(e){return e&&typeof e=="object"&&"x"in e&&"y"in e&&e.vec2===!0}static fromSerialized(e){return this.isVec2(e)?new this(e.x,e.y):null}rotate(e){let t=Math.cos(e),r=Math.sin(e);return new n(this.x*t-this.y*r,this.x*r+this.y*t)}static from(e){return new n(Math.cos(e),Math.sin(e))}get angle(){return Math.atan2(this.y,this.x)}get length(){return Math.hypot(this.x,this.y)}get lengthSquared(){return this.x*this.x+this.y*this.y}normalized(){let e=this.length;return e===0?new n(0,0):new n(this.x/e,this.y/e)}project(e){return e.scale(this.dotNormalized(e))}reflect(e){let t=this.dot(e);return this.subtract(e.scale(2*t))}dotNormalized(e){let t=this.normalized(),r=e.normalized();return t.x*r.x+t.y*r.y}lengthSquaredTo(e){let t=this.x-e.x,r=this.y-e.y;return t*t+r*r}dot(e){return this.x*e.x+this.y*e.y}distanceTo(e){return Math.hypot(this.x-e.x,this.y-e.y)}distanceToCheap(e){let t=this.x-e.x,r=this.y-e.y;return t*t+r*r}directionTo(e){return new n(e.x-this.x,e.y-this.y).normalized()}add(e){return new n(this.x+e.x,this.y+e.y)}addRaw(e){return new n(this.x+e,this.y+e)}overwite(e){this.x=e.x,this.y=e.y}subtract(e){return new n(this.x-e.x,this.y-e.y)}scale(e){return new n(this.x*e,this.y*e)}toString(){return`Vector2(${this.x}, ${this.y})`}clone(){return new n(this.x,this.y)}};function ke(n){let e=/^rgba?\(([^)]+)\)$/,t=n.match(e);if(t){let[r,s,u,c=1]=t[1].split(",").map(h=>parseFloat(h));return{r:Math.max(0,Math.min(255,Math.floor(r))),g:Math.max(0,Math.min(255,Math.floor(s))),b:Math.max(0,Math.min(255,Math.floor(u))),a:Math.max(0,Math.min(1,Math.floor(c*255)/255))}}return Z(n)}function Pe(n){return`rgba(${n.r}, ${n.g}, ${n.b}, ${n.a})`}function Z(n){if(n=n.trim().toLowerCase(),n[0]==="#"){let t,r,s;if(n.length===7)t=parseInt(n.slice(1,3),16),r=parseInt(n.slice(3,5),16),s=parseInt(n.slice(5,7),16);else if(n.length===4)t=parseInt(n[1]+n[1],16),r=parseInt(n[2]+n[2],16),s=parseInt(n[3]+n[3],16);else throw new Error("Invalid hex color");return{r:t,g:r,b:s,a:1}}let e=n.match(/^rgba?\s*\(\s*(\d+)[, ]\s*(\d+)[, ]\s*(\d+)(?:[, ]\s*([\d.]+))?\s*\)$/);if(e)return{r:parseInt(e[1]),g:parseInt(e[2]),b:parseInt(e[3]),a:e[4]!==void 0?parseFloat(e[4]):1};throw new Error("Unsupported fillStyle format")}function Me(n,{r:e,g:t,b:r,a:s}={}){let[u,c,h,a,o]=n.match(/rgba?\((\d+),\s*(\d+),\s*(\d+),?\s*([\d.]*)\)?/)||[];return`rgba(${e??c}, ${t??h}, ${r??a}, ${s??(o||1)})`}function _e(n,e,t){return t+(n-t)*e}function ee(){return typeof process<"u"&&process.release?.name==="node"?"node":typeof window<"u"||typeof self<"u"?"web":"unknown"}var j=ee(),V=j==="node",v=j==="web",R=class n{static unlock(){v&&this.audioCtx.state!=="running"&&this.audioCtx.resume()}static audioCtx=v?new AudioContext:null;static masterGain=v?this.audioCtx.createGain():null;static musicGain=v?this.audioCtx.createGain():null;static sfxGain=v?this.audioCtx.createGain():null;static{v&&(this.masterGain.gain.value=1,this.musicGain.gain.value=1,this.sfxGain.gain.value=1,this.sfxGain.connect(this.masterGain),this.musicGain.connect(this.masterGain),this.masterGain.connect(this.audioCtx.destination))}static get SFX_VOLUME(){return this.sfxGain.gain.value}static get MUSIC_VOLUME(){return this.musicGain.gain.value}static get VOLUME(){return this.masterGain.gain.value}static set SFX_VOLUME(e){this.sfxGain.gain.value=e}static set MUSIC_VOLUME(e){this.musicGain.gain.value=e}static set VOLUME(e){this.masterGain.gain.value=e}static noteToHz(e){return this.tuningFreq*Math.pow(2,(e-9)/12)}static tuningFreq=440;static audioBufferCache=new Map;static loops=new Map;static sfsx=new Map;static loopIdCounter=0;static CACHE_NAME="lia-audio-cache-v1";static async preLoad(e){if(this.audioBufferCache.has(e))return this.audioBufferCache.get(e);let t=n.CACHE_NAME,r,s;if(typeof caches<"u"&&(s=await caches.open(t),r=await s.match(e)),!r){if(r=await fetch(e),!r.ok)throw new Error(`Failed to fetch ${e}`);s&&await s.put(e,r.clone())}let u=await r.arrayBuffer(),c=await this.audioCtx.decodeAudioData(u);return this.audioBufferCache.set(e,c),c}static async getOnlyDownloadedCache(e){let t=n.CACHE_NAME;return await(await caches.open(t)).match(e)}static getCached(e){return this.audioBufferCache.get(e)||null}static async playSound(e,t=.2,r=1,s=!0){try{this.sfsx.has(e)&&this.sfsx.get(e).source.stop(),this.audioBufferCache.has(e)||await this.preLoad(e);let u=this.getCached(e);if(!u)return;let c=new I(u);c.buffer=u;let h=1-.12,a=1+.12;c.playbackRate=r??h+Math.random()*(a-h);let o=this.audioCtx.createGain();o.gain.value=t,c.tempGain=o,c.connect(o),o.connect(this.sfxGain),c.onended=()=>{c.disconnect(),o.disconnect()},c.start(),s&&this.sfsx.set(e,{source:c,gain:o})}catch(u){console.error(u)}}static async playLoop(e,t=1,{loopStart:r=0,loopEnd:s=null,exclusive:u=!0,skipMS:c=0}={}){if(u)for(let y of this.loops.keys())this.stopLoop(y);this.audioBufferCache.has(e)||await this.preLoad(e);let h=this.getCached(e);if(!h)return;let a=new I(h);a.buffer=h,a.loop=!0,typeof r=="number"&&(a.loopStart=r),typeof s=="number"&&(a.loopEnd=s);let o=this.audioCtx.createGain();o.gain.value=t,a.tempGain=o,a.playbackRate=1,a.onended=()=>{a.disconnect(),o.disconnect()},a.connect(o),o.connect(this.musicGain),a.start(0,c/1e3);let d=++this.loopIdCounter;return this.loops.set(d,{source:a,gain:o}),d}static stopLoop(e){let t=this.loops.get(e);t&&(t.source.stop(),t.source.notIndependent||(t.source.disconnect(),t.gain.disconnect()),this.loops.delete(e))}static async createLiaSource(e,{volume:t=1,speed:r=1,loop:s=!1,loopStart:u=0,loopEnd:c=null,isMusic:h=!1,gain:a=null}={}){try{this.audioBufferCache.has(e)||await this.preLoad(e);let o=this.getCached(e);if(!o)return null;let d=new I(o);d.loop=s,d.loopStart=u,d.loopEnd=typeof c=="number"?c:o.duration,d.playbackRate=r;let y=this.audioCtx.createGain();return y.gain.value=t,d.connect(y),d.tempGain=y,y.connect(a||(h?this.musicGain:this.sfxGain)),d.onended=()=>{d.disconnect(),y.disconnect()},d}catch(o){return console.error("Failed to create LiaAudioSrc:",o),null}}},te=["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"];function Ae(n){return te.includes(n)}var I=class{numberOfInputs;numberOfOutputs;constructor(e,t=R.audioCtx){this.context=t,this.buffer=e,this.source=null,this.startTime=0,this.pauseTime=0,this.playbackRate=1,this.isPlaying=!1,this.loop=!1,this.output=v?t.createGain():null,this.output.gain.value=1,this.channelCount=2,this.channelCountMode="max",this.channelInterpretation="speakers",this.numberOfInputs=0,this.numberOfOutputs=1,this.onended=null}channelCount;channelCountMode;channelInterpretation;onended;source;buffer;context;_createSource(){this.source&&(this.source.onended=null);let e=this.context.createBufferSource();return e.buffer=this.buffer,e.playbackRate.value=this.playbackRate,e.loop=this.loop,e.loopStart=this.loopStart,e.loopEnd=this.loopEnd>0?this.loopEnd:this.buffer.duration,e.connect(this.output),this.source=e,e.onended=()=>{this.source===e&&(this.isPlaying&&!this.loop&&(this.isPlaying=!1,this.pauseTime=0),typeof this.onended=="function"&&this.onended())},e}play({fadeIn:e=0,offset:t=null}={}){let r=t!==null?t:this.pauseTime;if(this.source){this.source.onended=null;try{this.source.stop()}catch{}}this.source=this._createSource(),this.startTime=this.context.currentTime-r/this.playbackRate,this.source.start(0,r),e>0?(this.output.gain.setValueAtTime(0,this.context.currentTime),this.output.gain.linearRampToValueAtTime(1,this.context.currentTime+e)):this.output.gain.setValueAtTime(1,this.context.currentTime),this.isPlaying=!0}start(e=0,t=0,r){this.isPlaying||(this.source=this._createSource(),this.startTime=this.context.currentTime+e-t/this.playbackRate,r!==void 0?this.source.start(this.context.currentTime+e,t,r):this.source.start(this.context.currentTime+e,t),this.pauseTime=t,this.isPlaying=!0)}pause({fadeOut:e=0}={}){if(!this.isPlaying)return;let t=this.getElapsed()/1e3;if(this.loop&&this.loopEnd>this.loopStart){let r=this.loopEnd-this.loopStart;t=this.loopStart+(t-this.loopStart)%r}if(this.isPlaying=!1,e>0){let r=this.context.currentTime;this.output.gain.setValueAtTime(this.output.gain.value,r),this.output.gain.linearRampToValueAtTime(0,r+e),setTimeout(()=>{if(this.source){this.source.onended=null;try{this.source.stop()}catch{}}this.pauseTime=t,this.output.gain.setValueAtTime(1,this.context.currentTime)},e*1e3)}else{if(this.source){this.source.onended=null;try{this.source.stop()}catch{}}this.pauseTime=t}}getElapsed(){return this.isPlaying?(this.context.currentTime-this.startTime)*this.playbackRate*1e3:this.pauseTime*1e3}setSpeed(e){if(e<=0)throw new Error("Playback rate must be positive");let t=this.getElapsed()/1e3;this.playbackRate=e,this.isPlaying&&(this.pause(),this.pauseTime=t,this.play())}setLoop(e=!0){this.loop=e,this.source&&(this.source.loop=e)}loop;playbackRate;startTime;tempGain=null;connect(e,t=0,r=0){if("value"in e)this.output.connect(e,t);else return this.output.connect(e,t,r),e}disconnect(e,t,r){e===void 0?this.output.disconnect():this.output.disconnect(e,t,r)}output;stop(e=0){if(this.notIndependent)return this.pause();if(!this.source)return;let t=this.context.currentTime+e;this.source.stop(t),this.isPlaying=!1,this.notIndependent||(this.pauseTime=0)}pauseTime;isPlaying;notIndependent=!1;loopStart=0;loopEnd=0},ne={osc:{enabled:!0,type:"sine",freq:440,detune:0},noise:{enabled:!1,level:.5},ampEnv:{attack:.005,decay:.1,sustain:0,release:.1,volume:.3},pitchEnv:{amount:0,decay:.2},filter:{enabled:!1,type:"lowpass",freq:1200,Q:1,envAmount:0,decay:.2},lfo:{enabled:!1,target:"freq",rate:8,depth:20},duration:.4},C=class n{constructor(e={}){this.ctx=R.audioCtx,this.cfg=structuredClone(ne),Object.assign(this.cfg,e)}ctx;cfg;play(e=R.sfxGain){let t=this.ctx,r=t.currentTime,s=this.cfg,u=t.createGain();u.gain.setValueAtTime(1e-4,r);let c=s.ampEnv;u.gain.exponentialRampToValueAtTime(c.volume,r+c.attack),u.gain.exponentialRampToValueAtTime(Math.max(1e-4,c.sustain*c.volume),r+c.attack+c.decay),u.gain.exponentialRampToValueAtTime(1e-4,r+s.duration+c.release);let h=u,a;s.filter.enabled&&(a=t.createBiquadFilter(),a.type=s.filter.type,a.frequency.value=s.filter.freq,a.Q.value=s.filter.Q,a.connect(u),h=a);let o;if(s.osc.enabled){if(o=t.createOscillator(),o.type=s.osc.type,o.frequency.value=s.osc.freq,o.detune.value=s.osc.detune,s.pitchEnv.amount!==0){let f=Math.pow(2,s.pitchEnv.amount/12);o.frequency.exponentialRampToValueAtTime(s.osc.freq*f,r+s.pitchEnv.decay)}o.connect(h),o.start(r),o.stop(r+s.duration+c.release)}let d;if(s.noise.enabled){let f=t.sampleRate*s.duration,m=t.createBuffer(1,f,t.sampleRate),b=m.getChannelData(0);for(let E=0;E<f;E++)b[E]=(Math.random()*2-1)*s.noise.level;d=t.createBufferSource(),d.buffer=m,d.connect(h),d.start(r),d.stop(r+s.duration+c.release)}let y,i;s.lfo.enabled&&o&&(y=t.createOscillator(),y.frequency.value=s.lfo.rate,i=t.createGain(),i.gain.value=s.lfo.depth,y.connect(i),s.lfo.target==="freq"?i.connect(o.frequency):s.lfo.target==="gain"?i.connect(u.gain):s.lfo.target==="filter"&&a&&i.connect(a.frequency),y.start(r),y.stop(r+s.duration)),u.connect(e);let p=[o,d,y,i,a,u],l=r+s.duration+c.release;setTimeout(()=>{p.forEach(f=>f?.disconnect())},(l-t.currentTime)*1e3)}getConfig(){return structuredClone(this.cfg)}setConfig(e){return Object.assign(this.cfg,e),this}getKey(e){return this.cfg[e]}setKey(e,t){return Object.assign(this.cfg[e],t),this}setFreq(e){return this.cfg.osc.freq=e,this}setWave(e){return this.cfg.osc.type=e,this}setVolume(e){return this.cfg.ampEnv.volume=e,this}setAmpEnv(e,t,r,s){return Object.assign(this.cfg.ampEnv,{attack:e,decay:t,sustain:r,release:s}),this}setPitchEnv(e,t){return Object.assign(this.cfg.pitchEnv,{amount:e,decay:t}),this}setNoiseEnabled(e){return this.cfg.noise.enabled=e,this}setNoiseLevel(e){return this.cfg.noise.level=e,this}setFilterEnabled(e){return this.cfg.filter.enabled=e,this}setFilter(e,t,r){return Object.assign(this.cfg.filter,{type:e,freq:t,Q:r,enabled:!0}),this}setLFOEnabled(e){return this.cfg.lfo.enabled=e,this}setLFO(e,t,r){return Object.assign(this.cfg.lfo,{target:e,rate:t,depth:r,enabled:!0}),this}setDuration(e){return this.cfg.duration=e,this}clone(){return new n(structuredClone(this.cfg))}},re=new C({osc:{enabled:!0,type:"square",freq:900,detune:0},ampEnv:{attack:.002,decay:.04,sustain:0,release:.02,volume:.15},duration:.05}),se=new C({osc:{enabled:!0,type:"sine",freq:600,detune:0},ampEnv:{attack:.01,decay:.08,sustain:0,release:.04,volume:.12},duration:.1}),ie=new C({osc:{enabled:!0,type:"triangle",freq:500,detune:0},pitchEnv:{amount:12,decay:.15},ampEnv:{attack:.01,decay:.12,sustain:0,release:.05,volume:.25},duration:.18}),ae=new C({osc:{enabled:!0,type:"sawtooth",freq:1200,detune:0},pitchEnv:{amount:-24,decay:.3},ampEnv:{attack:.005,decay:.25,sustain:0,release:.05,volume:.3},duration:.35}),oe=new C({osc:{enabled:!0,type:"square",freq:180,detune:0},noise:{enabled:!0,level:.4},ampEnv:{attack:.002,decay:.15,sustain:0,release:.05,volume:.35},duration:.2}),Fe=new Map([["ui_click",re],["ui_hover",se],["jump",ie],["laser",ae],["hit",oe]]);function Ve(n=12){let e=Date.now().toString(36),t=crypto.getRandomValues(new Uint8Array(n)),r=Array.from(t).map(s=>s.toString(36).padStart(2,"0")).join("");return(e+r).slice(0,n)}function Y(n=12){let e=Date.now().toString(36),t=crypto.getRandomValues(new Uint8Array(4)),r=Array.from(t).map(s=>s.toString(36).padStart(2,"0")).join("");return(e+r).slice(0,n)}var W=class{peer=null;key;events={};mbAcc=0;connected=!1;constructor(){this.key=`${ue()}_${Date.now()}`}connect(e){e&&(this.peer=e,e.peer=this,this.connected=!0,e.connected=!0),this._emit("open")}isConnected(){return this.connected&&!!this.peer}on(e,t){this.events[e]||(this.events[e]=[]),this.events[e].push(t)}off(e,t){this.events[e]&&(this.events[e]=this.events[e].filter(r=>r!==t))}_emit(e,t){this.events[e]?.forEach(r=>r(t))}send(e,t){if(!this.peer)throw new Error("No peer connected");this.peer._receive(e,t)}_receive(e,t){e?this._emit(e,t):this._emit("message",t)}close(){if(this.connected=!1,this.peer){let e=this.peer;this.peer=null,e.peer=null,e.connected=!1,e._emit("close")}this._emit("close")}getKey(){return this.key}};function ue(){if(typeof crypto<"u"&&typeof crypto.randomUUID=="function")return crypto.randomUUID();if(typeof crypto<"u"&&typeof crypto.getRandomValues=="function"){let n=new Uint8Array(16);return crypto.getRandomValues(n),n[6]=n[6]&15|64,n[8]=n[8]&63|128,[...n].map(e=>e.toString(16).padStart(2,"0")).join("").replace(/^(.{8})(.{4})(.{4})(.{4})(.{12})$/,"$1-$2-$3-$4-$5")}return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,n=>{let e=Math.random()*16|0;return(n==="x"?e:e&3|8).toString(16)})}function le(n){let e=!0;function t(){e&&(n(),requestAnimationFrame(t))}return requestAnimationFrame(t),{clear:()=>e=!1}}function ce(n,e,t,r,s){let u=Math.cos(s),c=Math.sin(s),h=[];if(u!==0){let a=(0-n)/u,o=e+a*c;a>0&&o>=0&&o<=r&&h.push({side:"left",t:a})}if(u!==0){let a=(t-n)/u,o=e+a*c;a>0&&o>=0&&o<=r&&h.push({side:"right",t:a})}if(c!==0){let a=(0-e)/c,o=n+a*u;a>0&&o>=0&&o<=t&&h.push({side:"top",t:a})}if(c!==0){let a=(r-e)/c,o=n+a*u;a>0&&o>=0&&o<=t&&h.push({side:"bottom",t:a})}return h.length===0?null:(h.sort((a,o)=>a.t-o.t),h[0])}function he(n,e){let t;switch(e){case"left":case"right":t=Math.PI-n;break;case"top":case"bottom":t=2*Math.PI-n;break}return(t%(2*Math.PI)+2*Math.PI)%(2*Math.PI)}function De(n,e,t,r,s){let u=ce(n,e,t,r,s);return u?{...u,avoidAngle:he(s,u.side)}:null}function pe(){return V?!1:/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)}var Le=pe();var B={};L(B,{Kayla:()=>q,KaylaFragment:()=>Ee,KaylaInternals:()=>T,UI:()=>Ce,createElement:()=>ye,createGame:()=>fe,createRenderer:()=>me,createScene:()=>de,self:()=>P,useDisposableRef:()=>we,useEffect:()=>X,useEntity:()=>ve,useExports:()=>xe,useNextStack:()=>Q,usePaint:()=>U,useRef:()=>k,useSelf:()=>ge,useState:()=>H,useTick:()=>be});var q={};L(q,{KaylaFragment:()=>Ee,KaylaInternals:()=>T,createElement:()=>ye,createGame:()=>fe,createRenderer:()=>me,createScene:()=>de,self:()=>P,useDisposableRef:()=>we,useEffect:()=>X,useEntity:()=>ve,useExports:()=>xe,useNextStack:()=>Q,usePaint:()=>U,useRef:()=>k,useSelf:()=>ge,useState:()=>H,useTick:()=>be});function fe({width:n,height:e,updateHz:t="frames"}){return new T.KaylaGame(n,e,t==="frames"?1/0:t)}function de(n){let e=new _(n);return new T.KaylaScene(e)}function me(n){return new T.KaylaRenderer(n)}function ye(n,e){return{type:n,props:e}}var T;(d=>{class n extends A{#e;#t;constructor(i,p,l){super(i,p,l),this.#e=new Set,this.#t=!1}get started(){return this.#t}start(){if(!this.#t){for(let i of this.#e)i.start();this.#t=!0,super.start()}}stop(){if(this.#t){for(let i of this.#e)i.stop();this.#t=!1,super.stop()}}addRenderer(i){this.#e.has(i)||(this.#e.add(i),this.#t&&i.start(),i.game=this)}deleteRenderer(i){this.#e.has(i)&&(this.#e.delete(i),this.#t&&i.stop(),delete i.game)}}d.KaylaGame=n;class e extends M{game;pointerX;pointerY;#e;constructor(i){super(i),this.useDraw=this.useDraw.bind(this),this.on("draw",p=>{if(this.game)for(let l of this.game.scenes.values())l.handleDraw(p)}),this.pointerX=0,this.pointerY=0,this.#e=this.#t.bind(this)}#t(i){this.pointerX=i.clientX,this.pointerY=i.clientY}listenPointerUpdates(){this.canvas.addEventListener("pointermove",this.#e)}unlistenPointerUpdates(){this.canvas.removeEventListener("pointermove",this.#e)}pointerPosToWorldPos(i,p){let l=this.canvas.getBoundingClientRect(),f=(i-l.left)/l.width,m=(p-l.top)/l.height,b=f*this.viewportWidth*this.cameraWidth/l.width,E=m*this.viewportHeight*this.cameraHeight/l.height;return new S(b,E)}getMousePos(){return this.pointerPosToWorldPos(this.pointerX,this.pointerY)}useDraw(i){let p=l=>{i(l,new r)};return this.on("draw",p),()=>{this.off("draw",p)}}attachTo(i){i.addRenderer(this),this.game=i}detach(){this.game&&this.game.deleteRenderer(this)}}d.KaylaRenderer=e;class t{current;saves;constructor(){this.saves=[]}useTick(i){if(!this.current)throw new Error("Hook 'useTick' must be executed in the top level scope of a component.");this.current.onTick[this.current.useStepCallIndex]=i,this.current.useStepCallIndex++}usePaint(i){if(!this.current)throw new Error("Hook 'usePaint' must be executed in the top level scope of a component.");this.current.onPaint[this.current.useDrawCallIndex]=i,this.current.useDrawCallIndex++}useEntity(){return k(P)}useSelf(i){let p=k(null);return(p.current===null||p.current===void 0)&&(p.current=i()),p.current}useState(i,{alwaysRecall:p=!1}={}){if(!this.current)throw new Error("Hook 'useState' must be executed in the top level scope of a component.");let f=this.current.state[this.current.useStateCallIndex]??new s(this.current,i,{alwaysRecall:p});return this.current.state[this.current.useStateCallIndex]=f,this.current.useStateCallIndex++,f}useRef(i){if(!this.current)throw new Error("Hook 'useRef' must be executed in the top level scope of a component.");let l=this.current.refs[this.current.useRefCallIndex]??new u(this.current,i??null);return this.current.refs[this.current.useRefCallIndex]=l,this.current.useRefCallIndex++,l}useEffect(i){if(!this.current)throw new Error("Hook 'useEffect' must be executed in the top level scope of a component.");this.current.onEffect[this.current.useEffectCallIndex]=i,this.current.useEffectCallIndex++}useExports(i,p){if(!this.current)throw new Error("Hook 'useExports' must be executed in the top level scope of a component.");this.current.onExport=p}save(){this.saves.push({current:this.current})}restore(){let i=this.saves.pop();if(!i)throw new Error("Cannot restore without saving.");this.current=i.current}}d.GlobalKayla=t;class r{preventDefault(){this.#e=!0}#e=!1;isPrevented(){return this.#e}}d.KaylaEvent=r;class s{#e;#t;#n;alwaysRecall;constructor(i,p,{alwaysRecall:l=!1}={}){this.#t=i,this.#e=p??void 0,this.#n=Date.now(),this.alwaysRecall=l}get(){return this.#e}add(i,{recall:p}={}){this.set(this.get()+i,{recall:p})}multiply(i,{recall:p}={}){this.set(this.get()*i,{recall:p})}set(i,{recall:p=!1}={}){let l=this.#e;i!==l&&(this.#e=i,(p||this.alwaysRecall)&&this.#t.refresh(),this.#n=Date.now())}get value(){return this.#e}get lastChanged(){return this.#n}}d.KaylaState=s;class u{#e;#t;constructor(i,p){this.#t=i,this.#e=p??void 0}get current(){return this.#e}set current(i){this.#e=i}}d.KaylaRef=u;class c{state;refs;global;callProps;scene;exports;constructor(i,p,l){if(!l)throw new Error("Empty element");this.scene=p,this.state=[],this.refs=[],this.fc=l.type,this.callProps=l.props??{},this.global=i,this.lastStateDeps=[],this.entity=null,this.lastChildren=[],this.onEffect=[],this.onUnEffect=[],this.onPaint=[],this.onTick=[]}get key(){return this.callProps.key}set key(i){this.callProps.key=i}get children(){return this.callProps.children}set children(i){this.callProps.children=i}entity;onExport=()=>({});onEffect;onUnEffect;onEffectDeps;onPaint;onTick;fc;useStateCallIndex=0;useEffectCallIndex=0;useDrawCallIndex=0;useStepCallIndex=0;useRefCallIndex=0;lastStateDeps;refresh(){let i;this.global.save(),this.global.current=this,this.useStateCallIndex=0,this.useRefCallIndex=0;let p=[];try{let l=this.fc(this.callProps)??[];if(l&&!Array.isArray(l)&&(l=[l]),!Array.isArray(l))throw new Error("Non array or non undefined children received.");p=l}catch(l){i=l}this.useStateCallIndex=0,this.useRefCallIndex=0,this.useDrawCallIndex=0,this.useStepCallIndex=0,this.useEffectCallIndex=0,this.global.restore();try{let l=0,f=[];for(let m of p){let b=this.lastChildren.at(l)??new c(this.global,this.scene,m);this.lastChildren[l]=b,f.push(b),l++}for(let m of this.lastChildren.filter(b=>!f.includes(b))){let b=this.lastChildren.indexOf(m);b!==1&&this.lastChildren.splice(b,1),m.unuse()}for(let m of this.lastChildren)try{m.refresh()}catch(b){console.error(b)}Q(()=>{this.use()})}catch(l){console.error(l)}if(i)throw i}lastChildren;isFirstUse=!0;use(){try{let i=!0;if(this.lastStateDeps??=[],Array.isArray(this.onEffectDeps)&&(i=this.onEffectDeps.some((p,l)=>{let f=this.lastStateDeps.at(l);if(f){let m=p.lastChanged,b=f.stamp;return m>b}})),i||this.isFirstUse){this.isFirstUse=!1,Array.isArray(this.onEffectDeps)&&(this.lastStateDeps=this.onEffectDeps.map(l=>({stamp:l.lastChanged,stateRef:l}))),this.key??=this.fc.name+"_"+Y(),this.entity&&this.scene.getScene().removeEntity(this.entity),this.entity&&this.entity.name!==this.key&&(this.entity=null),this.entity=this.entity??new h(this,this.key);for(let l of this.refs)l.current===P&&(l.current=this.entity);this.callProps.ref instanceof u&&(this.callProps.ref.current=this.entity),this.exports=this.onExport(),this.callProps.exportsRef instanceof u&&(this.callProps.exportsRef.current=this.exports),this.scene.getScene().addEntity(this.entity);let p=this.onEffect.map(l=>l()).filter(l=>l!==void 0);this.onUnEffect=p}}catch(i){throw i}}unuse(){try{for(let i of this.onUnEffect)i()}catch(i){console.error(i)}this.scene.getScene().removeEntity(this.entity)}}d.KaylaInternalComponent=c;class h extends F{#e;constructor(i,p){super(p,0,0,0,0),this.#e=i}update(i){let p=new r;if(this.#e.onTick)for(let l of this.#e.onTick)l(i,p);p.isPrevented()}draw(i){let p=new r;if(this.#e.onPaint)for(let l of this.#e.onPaint)l(i,p);p.isPrevented()||super.draw(i)}}d.KaylaRectEntity=h;class a{constructor(i){this.#t=i,this.#e=null,i.on("update",this.tickHandler.bind(this)),this.drawHandler=this.drawHandler.bind(this)}#e;#t;getScene(){return this.#t}drawHandler(i){return this.#t.handleDraw(i)}attachTo(i){i.addScene(this.#t),this.#e=i}spawn(i){new c(d.singleGlobalInstance,this,i).refresh()}detach(){this.#e&&this.#e.removeScene(this.#t)}tickHandler(){}}d.KaylaScene=a,d.singleGlobalInstance=new d.GlobalKayla})(T||={});var x=T.singleGlobalInstance,H=x.useState.bind(x),k=x.useRef.bind(x),X=x.useEffect.bind(x),be=x.useTick.bind(x),U=x.usePaint.bind(x),xe=x.useExports.bind(x),ge=x.useSelf.bind(x),ve=x.useEntity.bind(x),P=Symbol("self_ref");var Q=n=>{Promise.resolve(1).then(()=>n())},Ee=({children:n})=>Array.isArray(n)?[...n]:n;function we(n){return new T.KaylaRef(null,n)}Reflect.set(globalThis,"Kayla",q);var Ce;(e=>e.UIComponent=({x:t,y:r,width:s,height:u,children:c})=>{let h=k(P),a=H(!1);return X(()=>{if(a.get()===!0)return;a.set(!0);let o=h.current;o.width=s,o.height=u,o.x=t,o.y=r}),U(o=>{o.fillStyle="rgba(0, 0, 0, 0.5)",o.fillRect(t,r,s,u)}),c??[]})(Ce||={});var Ge={LEA:O,Kayla:B};0&&(module.exports={Kayla,LEA});