@ouidesigner/toubani 0.1.0 → 0.1.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.
package/README.md CHANGED
@@ -70,6 +70,32 @@ game.start();
70
70
  - **Physics/collision**: `PhysicsBody` for AABB tile collisions with gravity/friction and `CollisionSystem` for tag-based collider handlers.
71
71
  - **Input/network**: `InputManager` tracks key and mouse pressed/held state per frame; `WebSocketTransport` uses `@ouidesigner/ouider-network` under the hood.
72
72
 
73
+ ## Network reconnects
74
+ `WebSocketTransport` reconnects by default after unexpected close/connect failures and retries immediately when the app returns from background. Manual `close()` stops reconnecting.
75
+
76
+ Use `NetworkClient.onOpen()` for session setup that must run after both initial connect and reconnect:
77
+ ```ts
78
+ const socket = new NetworkClient(new WebSocketTransport({
79
+ reconnect: {
80
+ initialDelayMs: 500,
81
+ maxDelayMs: 10000,
82
+ maxAttempts: Infinity,
83
+ },
84
+ }));
85
+
86
+ socket.onOpen(() => {
87
+ void socket.send("joinRoom", { roomId });
88
+ });
89
+
90
+ socket.onReconnect(({ attempt, reason }) => {
91
+ console.log("reconnecting", attempt, reason);
92
+ });
93
+
94
+ await socket.connect("wss://example.com/game");
95
+ ```
96
+
97
+ Disable reconnecting with `new WebSocketTransport({ reconnect: false })`. Pending RPC calls are rejected on disconnect; reconnect handlers should request fresh state from the server instead of replaying stale gameplay messages.
98
+
73
99
  ## Examples
74
100
  See `examples/simple` for runnable demos:
75
101
  - `src/Game.ts`: side-scroller style scene showing tile rendering, physics, camera follow, and sprite animations.
package/dist/index.d.mts CHANGED
@@ -350,23 +350,59 @@ interface NetworkTransport {
350
350
  onOpen(cb: () => void): void;
351
351
  onClose(cb: (ev?: any) => void): void;
352
352
  onError(cb: (err: any) => void): void;
353
+ onReconnect?(cb: (event: WebSocketReconnectEvent) => void): void;
354
+ offOpen?(cb: () => void): void;
355
+ offClose?(cb: (ev?: any) => void): void;
356
+ offError?(cb: (err: any) => void): void;
357
+ offReconnect?(cb: (event: WebSocketReconnectEvent) => void): void;
353
358
  }
354
359
  interface WebSocketTransportOptions extends WebSocketOptions {
355
360
  protocols?: string | string[];
356
361
  connectTimeoutMs?: number;
362
+ reconnect?: boolean | WebSocketReconnectOptions;
363
+ }
364
+ interface WebSocketReconnectOptions {
365
+ enabled?: boolean;
366
+ initialDelayMs?: number;
367
+ maxDelayMs?: number;
368
+ factor?: number;
369
+ maxAttempts?: number;
370
+ jitterRatio?: number;
371
+ reconnectOnForeground?: boolean;
372
+ shouldReconnect?: (event?: any) => boolean;
373
+ }
374
+ interface WebSocketReconnectEvent {
375
+ attempt: number;
376
+ url: string;
377
+ reason: "close" | "error" | "foreground";
378
+ cause?: any;
357
379
  }
358
380
  declare class WebSocketTransport implements NetworkTransport {
359
381
  private options;
360
382
  private opened;
361
383
  private socket;
362
384
  private currentUrl;
385
+ private desiredUrl;
363
386
  private offMessage;
364
387
  private msgHandlers;
365
388
  private openHandlers;
366
389
  private closeHandlers;
367
390
  private errorHandlers;
391
+ private reconnectHandlers;
392
+ private reconnectAttempts;
393
+ private reconnectTimerId;
394
+ private reconnectTimerPending;
395
+ private reconnectTimerToken;
396
+ private reconnecting;
397
+ private manuallyClosed;
398
+ private appBackgrounded;
399
+ private offForeground;
400
+ private offBackground;
401
+ private foregroundListenerPending;
402
+ private backgroundListenerPending;
368
403
  constructor(options?: WebSocketTransportOptions);
369
404
  connect(url: string): Promise<void>;
405
+ private openSocket;
370
406
  send(data: string): Promise<void>;
371
407
  close(code?: number, reason?: string): Promise<void>;
372
408
  isConnected(): boolean;
@@ -377,6 +413,17 @@ declare class WebSocketTransport implements NetworkTransport {
377
413
  offClose(cb: (ev?: any) => void): void;
378
414
  onError(cb: (err: any) => void): void;
379
415
  offError(cb: (err: any) => void): void;
416
+ onReconnect(cb: (event: WebSocketReconnectEvent) => void): void;
417
+ offReconnect(cb: (event: WebSocketReconnectEvent) => void): void;
418
+ private getReconnectOptions;
419
+ private scheduleReconnect;
420
+ private reconnectNow;
421
+ private reconnectDelay;
422
+ private clearReconnectTimer;
423
+ private ensureAppStateListeners;
424
+ private ensureBackgroundListener;
425
+ private ensureForegroundListener;
426
+ private removeAppStateListeners;
380
427
  private cleanupSocket;
381
428
  }
382
429
  type Handler<T = any> = (msg: T) => void;
@@ -388,6 +435,14 @@ declare class NetworkClient {
388
435
  constructor(transport: NetworkTransport);
389
436
  connect(url: string): Promise<void>;
390
437
  isConnected(): boolean;
438
+ onOpen(handler: () => void): void;
439
+ offOpen(handler: () => void): void;
440
+ onClose(handler: (event?: any) => void): void;
441
+ offClose(handler: (event?: any) => void): void;
442
+ onError(handler: (error: any) => void): void;
443
+ offError(handler: (error: any) => void): void;
444
+ onReconnect(handler: (event: WebSocketReconnectEvent) => void): void;
445
+ offReconnect(handler: (event: WebSocketReconnectEvent) => void): void;
391
446
  send<TType extends string, TPayload>(type: TType, payload: TPayload): Promise<void>;
392
447
  on<TPayload = any>(type: string, handler: Handler<NetMessage<string, TPayload>>): void;
393
448
  off(type: string, handler: Handler): void;
@@ -417,4 +472,4 @@ declare class StateSyncClient<TState extends object> {
417
472
  sendPatch(patch: Partial<TState>): Promise<void>;
418
473
  }
419
474
 
420
- export { type AABB, AssetManager, type AssetRegistry, AudioManager, Camera2D, type Collider, type CollisionHandler, CollisionSystem, Game, type GameConfig, type GameContext, InputManager, type NetMessage, NetworkClient, type NetworkTransport, PhysicsBody, type PhysicsBodySettings, Scene, SceneLayer, SceneNode, Sprite, SpriteAnimation, SpriteLayer, SpriteNode, type SpriteOptions, SpriteSheet, type SpriteSheetConfig, type StatePatch, StateSyncClient, type TileMap, TileRenderer, type TileRendererOptions, type TileSet, WebSocketTransport, type WebSocketTransportOptions, aabbOverlap, createSpritePhysicsCollider, getTileSourceRect, objectSize, tulon, worldToTileX, worldToTileY };
475
+ export { type AABB, AssetManager, type AssetRegistry, AudioManager, Camera2D, type Collider, type CollisionHandler, CollisionSystem, Game, type GameConfig, type GameContext, InputManager, type NetMessage, NetworkClient, type NetworkTransport, PhysicsBody, type PhysicsBodySettings, Scene, SceneLayer, SceneNode, Sprite, SpriteAnimation, SpriteLayer, SpriteNode, type SpriteOptions, SpriteSheet, type SpriteSheetConfig, type StatePatch, StateSyncClient, type TileMap, TileRenderer, type TileRendererOptions, type TileSet, type WebSocketReconnectEvent, type WebSocketReconnectOptions, WebSocketTransport, type WebSocketTransportOptions, aabbOverlap, createSpritePhysicsCollider, getTileSourceRect, objectSize, tulon, worldToTileX, worldToTileY };
package/dist/index.d.ts CHANGED
@@ -350,23 +350,59 @@ interface NetworkTransport {
350
350
  onOpen(cb: () => void): void;
351
351
  onClose(cb: (ev?: any) => void): void;
352
352
  onError(cb: (err: any) => void): void;
353
+ onReconnect?(cb: (event: WebSocketReconnectEvent) => void): void;
354
+ offOpen?(cb: () => void): void;
355
+ offClose?(cb: (ev?: any) => void): void;
356
+ offError?(cb: (err: any) => void): void;
357
+ offReconnect?(cb: (event: WebSocketReconnectEvent) => void): void;
353
358
  }
354
359
  interface WebSocketTransportOptions extends WebSocketOptions {
355
360
  protocols?: string | string[];
356
361
  connectTimeoutMs?: number;
362
+ reconnect?: boolean | WebSocketReconnectOptions;
363
+ }
364
+ interface WebSocketReconnectOptions {
365
+ enabled?: boolean;
366
+ initialDelayMs?: number;
367
+ maxDelayMs?: number;
368
+ factor?: number;
369
+ maxAttempts?: number;
370
+ jitterRatio?: number;
371
+ reconnectOnForeground?: boolean;
372
+ shouldReconnect?: (event?: any) => boolean;
373
+ }
374
+ interface WebSocketReconnectEvent {
375
+ attempt: number;
376
+ url: string;
377
+ reason: "close" | "error" | "foreground";
378
+ cause?: any;
357
379
  }
358
380
  declare class WebSocketTransport implements NetworkTransport {
359
381
  private options;
360
382
  private opened;
361
383
  private socket;
362
384
  private currentUrl;
385
+ private desiredUrl;
363
386
  private offMessage;
364
387
  private msgHandlers;
365
388
  private openHandlers;
366
389
  private closeHandlers;
367
390
  private errorHandlers;
391
+ private reconnectHandlers;
392
+ private reconnectAttempts;
393
+ private reconnectTimerId;
394
+ private reconnectTimerPending;
395
+ private reconnectTimerToken;
396
+ private reconnecting;
397
+ private manuallyClosed;
398
+ private appBackgrounded;
399
+ private offForeground;
400
+ private offBackground;
401
+ private foregroundListenerPending;
402
+ private backgroundListenerPending;
368
403
  constructor(options?: WebSocketTransportOptions);
369
404
  connect(url: string): Promise<void>;
405
+ private openSocket;
370
406
  send(data: string): Promise<void>;
371
407
  close(code?: number, reason?: string): Promise<void>;
372
408
  isConnected(): boolean;
@@ -377,6 +413,17 @@ declare class WebSocketTransport implements NetworkTransport {
377
413
  offClose(cb: (ev?: any) => void): void;
378
414
  onError(cb: (err: any) => void): void;
379
415
  offError(cb: (err: any) => void): void;
416
+ onReconnect(cb: (event: WebSocketReconnectEvent) => void): void;
417
+ offReconnect(cb: (event: WebSocketReconnectEvent) => void): void;
418
+ private getReconnectOptions;
419
+ private scheduleReconnect;
420
+ private reconnectNow;
421
+ private reconnectDelay;
422
+ private clearReconnectTimer;
423
+ private ensureAppStateListeners;
424
+ private ensureBackgroundListener;
425
+ private ensureForegroundListener;
426
+ private removeAppStateListeners;
380
427
  private cleanupSocket;
381
428
  }
382
429
  type Handler<T = any> = (msg: T) => void;
@@ -388,6 +435,14 @@ declare class NetworkClient {
388
435
  constructor(transport: NetworkTransport);
389
436
  connect(url: string): Promise<void>;
390
437
  isConnected(): boolean;
438
+ onOpen(handler: () => void): void;
439
+ offOpen(handler: () => void): void;
440
+ onClose(handler: (event?: any) => void): void;
441
+ offClose(handler: (event?: any) => void): void;
442
+ onError(handler: (error: any) => void): void;
443
+ offError(handler: (error: any) => void): void;
444
+ onReconnect(handler: (event: WebSocketReconnectEvent) => void): void;
445
+ offReconnect(handler: (event: WebSocketReconnectEvent) => void): void;
391
446
  send<TType extends string, TPayload>(type: TType, payload: TPayload): Promise<void>;
392
447
  on<TPayload = any>(type: string, handler: Handler<NetMessage<string, TPayload>>): void;
393
448
  off(type: string, handler: Handler): void;
@@ -417,4 +472,4 @@ declare class StateSyncClient<TState extends object> {
417
472
  sendPatch(patch: Partial<TState>): Promise<void>;
418
473
  }
419
474
 
420
- export { type AABB, AssetManager, type AssetRegistry, AudioManager, Camera2D, type Collider, type CollisionHandler, CollisionSystem, Game, type GameConfig, type GameContext, InputManager, type NetMessage, NetworkClient, type NetworkTransport, PhysicsBody, type PhysicsBodySettings, Scene, SceneLayer, SceneNode, Sprite, SpriteAnimation, SpriteLayer, SpriteNode, type SpriteOptions, SpriteSheet, type SpriteSheetConfig, type StatePatch, StateSyncClient, type TileMap, TileRenderer, type TileRendererOptions, type TileSet, WebSocketTransport, type WebSocketTransportOptions, aabbOverlap, createSpritePhysicsCollider, getTileSourceRect, objectSize, tulon, worldToTileX, worldToTileY };
475
+ export { type AABB, AssetManager, type AssetRegistry, AudioManager, Camera2D, type Collider, type CollisionHandler, CollisionSystem, Game, type GameConfig, type GameContext, InputManager, type NetMessage, NetworkClient, type NetworkTransport, PhysicsBody, type PhysicsBodySettings, Scene, SceneLayer, SceneNode, Sprite, SpriteAnimation, SpriteLayer, SpriteNode, type SpriteOptions, SpriteSheet, type SpriteSheetConfig, type StatePatch, StateSyncClient, type TileMap, TileRenderer, type TileRendererOptions, type TileSet, type WebSocketReconnectEvent, type WebSocketReconnectOptions, WebSocketTransport, type WebSocketTransportOptions, aabbOverlap, createSpritePhysicsCollider, getTileSourceRect, objectSize, tulon, worldToTileX, worldToTileY };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- 'use strict';var ouider=require('@ouidesigner/ouider');require('@ouidesigner/ouider-device');var ouiderNetwork=require('@ouidesigner/ouider-network');var st=Object.defineProperty,Pt=Object.defineProperties;var bt=Object.getOwnPropertyDescriptors;var tt=Object.getOwnPropertySymbols;var Dt=Object.prototype.hasOwnProperty,Xt=Object.prototype.propertyIsEnumerable;var E=(l,t,s)=>t in l?st(l,t,{enumerable:true,configurable:true,writable:true,value:s}):l[t]=s,et=(l,t)=>{for(var s in t||(t={}))Dt.call(t,s)&&E(l,s,t[s]);if(tt)for(var s of tt(t))Xt.call(t,s)&&E(l,s,t[s]);return l},it=(l,t)=>Pt(l,bt(t)),c=(l,t)=>st(l,"name",{value:t,configurable:true});var i=(l,t,s)=>E(l,typeof t!="symbol"?t+"":t,s);var S=class S{constructor(t,s,e,r,o=0){i(this,"image");i(this,"frameWidth");i(this,"frameHeight");i(this,"frameCount");i(this,"from");i(this,"columns");this.image=t,this.frameWidth=s,this.frameHeight=e,this.frameCount=r,this.from=o;let n=H(t);this.columns=Math.max(1,Math.floor(n.width/s));}};c(S,"SpriteSheet");var D=S,C=class C{constructor(t,s=10,e=true){i(this,"sheet");i(this,"fps");i(this,"loop");i(this,"time",0);this.sheet=t,this.fps=s,this.loop=e;}update(t){this.time+=t;let s=this.sheet.frameCount/this.fps;this.loop?this.time=this.time%s:this.time>s&&(this.time=s);}draw(t,s,e,r=1,o=1){let n=Math.floor(this.time*this.fps)+this.sheet.from,a=Math.min(n,this.sheet.frameCount+this.sheet.from-1),h=this.sheet.columns,d=a%h*this.sheet.frameWidth,p=Math.floor(a/h)*this.sheet.frameHeight,u=this.sheet.frameWidth*r,f=this.sheet.frameHeight*o;t.drawImage(this.sheet.image,d,p,this.sheet.frameWidth,this.sheet.frameHeight,s,e,u,f);}reset(){this.time=0;}};c(C,"SpriteAnimation");var X=C,R=class R{constructor(t){i(this,"animation");i(this,"width");i(this,"height");i(this,"visible");i(this,"tag");i(this,"data");var s,e,r,o;this.animation=t.animation,this.visible=(s=t.visible)!=null?s:true,this.tag=t.tag,this.data=(e=t.data)!=null?e:{},this.width=(r=t.width)!=null?r:this.animation.sheet.frameWidth,this.height=(o=t.height)!=null?o:this.animation.sheet.frameHeight;}update(t){this.animation.update(t);}draw(t,s=1,e=1){this.visible&&this.animation.draw(t,0,0,s,e);}};c(R,"Sprite");var ot=R,W=class W{constructor(){i(this,"sprites",[]);}add(t){return this.sprites.push(t),t}remove(t){let s=this.sprites.indexOf(t);s>=0&&this.sprites.splice(s,1);}clear(){this.sprites.length=0;}update(t){for(let s of this.sprites)s.update(t);}draw(t){for(let s of this.sprites)s.draw(t);}withTag(t){return this.sprites.filter(s=>s.tag===t)}};c(W,"SpriteLayer");var rt=W;var L=class L{constructor(){i(this,"imageDefs",new Map);i(this,"soundDefs",new Map);i(this,"sheetDefs",new Map);i(this,"images",new Map);i(this,"sounds",new Map);i(this,"sheets",new Map);}image(t,s){this.imageDefs.set(t,s);}sound(t,s){this.soundDefs.set(t,s);}spriteSheet(t,s){this.sheetDefs.set(t,s);}async loadAll(){var e,r;let t=[],s=[];for(let[o,n]of this.imageDefs.entries())t.push(this.loadImage(n).then(a=>{this.images.set(o,a);}));for(let[o,n]of this.soundDefs.entries())s.push(this.loadSound(n).then(a=>{this.sounds.set(o,a);}));await Promise.all([...t,...s]);for(let[o,n]of this.sheetDefs.entries()){let a=(e=this.getImage(n.image))!=null?e:this.images.get(n.image);if(!a)throw new Error(`SpriteSheet "${o}" missing image "${n.image}"`);let h=new D(a,n.frameWidth,n.frameHeight,n.frameCount,(r=n.from)!=null?r:0);this.sheets.set(o,h);}}async loadImage(t){let s=await ouider.ODOM.Image.new();if(!s)throw new Error(`Unable to create image for "${t}"`);let e=null,r=null,o=false,n=c(async()=>{let h=[e,r].filter(Boolean);e=null,r=null,await Promise.all(h.map(d=>s.dettachEventListener(d).catch(()=>{})));},"detach"),a=c(async()=>{let[h,d,p,u]=await Promise.all([s.naturalWidth(),s.naturalHeight(),s.width(),s.height()]);s.__local_size__={width:h||p||0,height:d||u||0};},"cacheSize");return new Promise(async(h,d)=>{let p=c(async()=>{if(!o)try{await a(),o=!0,await n(),h(s);}catch(f){await u(f);}},"done"),u=c(async f=>{o||(o=true,await n(),await s.release().catch(()=>{}),d(new Error(`Unable to load image "${t}"`)));},"fail");try{e=await s.addEventListener("load",()=>{p();},{options:{once:!0}}),r=await s.addEventListener("error",()=>{u();},{options:{once:!0}}),await s.setSrc(t);let[f,m]=await Promise.all([s.getProperty("complete").catch(()=>!1),s.naturalWidth().catch(()=>0)]);f&&m>0&&await p();}catch(f){await u(f);}})}async loadSound(t){return await ouider.OUID.media.audio.decode(t),t}getImage(t){return this.images.get(t)}getSpriteSheet(t){return this.sheets.get(t)}createAnimation(t,s,e){let r=this.getSpriteSheet(t);if(!r)throw new Error(`SpriteSheet "${t}" not found`);return new X(r,s!=null?s:10,e!=null?e:true)}getSound(t){return this.sounds.get(t)}clean(){this.images.forEach(t=>t.release()),this.images.clear(),this.sounds.clear(),this.sheets.clear();}};c(L,"AssetManager");var Y=L;var O=class O{constructor(t){i(this,"assets");this.assets=t;}clean(){ouider.OUID.media.audio.close();}async play(t,s){let e=this.assets.getSound(t);e&&await ouider.OUID.media.audio.play(e,s);}};c(O,"AudioManager");var T=O;var U=class U{constructor(t){i(this,"keysDown",new Set);i(this,"keysPressed",new Set);i(this,"buttonsPressed",new Set);i(this,"buttonsDown",new Set);i(this,"mouseX",-1);i(this,"mouseY",-1);i(this,"offsetX",-1);i(this,"offsetY",-1);i(this,"ids",[]);i(this,"disposed",false);this.track(OUID.addEventListener("window","keydown",s=>{this.keysDown.has(s.code)||this.keysPressed.add(s.code),this.keysDown.add(s.code);})),this.track(OUID.addEventListener("window","keyup",s=>{this.keysDown.delete(s.code);})),this.track(t.addEventListener("mousedown",s=>{let e=s.button===0?"MouseLeft":"MouseRight";this.buttonsDown.has(e)||this.buttonsPressed.add(e),this.buttonsDown.add(e),this.updateMousePosition(s);})),this.track(t.addEventListener("mouseup",s=>{let e=s.button===0?"MouseLeft":"MouseRight";this.buttonsDown.delete(e),this.updateMousePosition(s);})),this.track(t.addEventListener("mousemove",s=>{this.updateMousePosition(s);})),this.track(t.addEventListener("mouseleave",()=>{this.buttonsDown.clear();}));}update(){this.keysPressed.clear(),this.buttonsPressed.clear();}isDown(t){return this.keysDown.has(t)||this.buttonsDown.has(t)}isPressed(t){return this.keysPressed.has(t)||this.buttonsPressed.has(t)}clean(){this.disposed=true,this.ids.forEach(t=>{OUID.dettachEventListener(t);}),this.ids=[],this.keysDown.clear(),this.keysPressed.clear(),this.buttonsDown.clear(),this.buttonsPressed.clear();}track(t){t.then(s=>{this.disposed?OUID.dettachEventListener(s):this.ids.push(s);}).catch(()=>{});}updateMousePosition(t){var s,e,r,o;this.mouseX=(s=t.x)!=null?s:this.mouseX,this.mouseY=(e=t.y)!=null?e:this.mouseY,this.offsetX=(r=t.offsetX)!=null?r:this.offsetX,this.offsetY=(o=t.offsetY)!=null?o:this.offsetY;}};c(U,"InputManager");var z=U;var B=class B{constructor(t){i(this,"config");i(this,"canvas");i(this,"ctx2d");i(this,"assets");i(this,"audio");i(this,"input");i(this,"world",{});i(this,"running",false);i(this,"initialized",false);i(this,"lastTime",0);i(this,"frameId",null);i(this,"resizeListenerId",null);this.config=t,this.canvas=t.canvas,this.assets=new Y,this.audio=new T(this.assets),this.input=new z(this.canvas);}buildContext(t){return {dt:t,canvas:this.canvas,ctx2d:this.ctx2d,world:this.world,assets:this.assets,audio:this.audio,input:this.input}}async updateLocalSize(){var e,r;let t=(e=await this.canvas.width())!=null?e:0,s=(r=await this.canvas.height())!=null?r:0;this.canvas.__local_size__=this.ctx2d.__local_size__={width:t,height:s};}async syncSize(){var a,h;let[t,s,e,r]=await Promise.all([this.canvas.getProperty("clientWidth"),this.canvas.getProperty("clientHeight"),this.canvas.width(),this.canvas.height()]),o=(a=t!=null?t:e)!=null?a:0,n=(h=s!=null?s:r)!=null?h:0;await this.canvas.setProperty("width",o),await this.canvas.setProperty("height",n);}async init(){if(this.initialized)return;let t=await this.canvas.getContext("2d");if(!t)throw new Error("Unable to create a context");this.ctx2d=t,this.resizeListenerId=await this.canvas.addEventListener("resize",()=>{this.syncSize().then(()=>this.updateLocalSize());}),this.config.preload&&(this.config.preload(this.assets),await this.assets.loadAll()),await this.syncSize(),await this.updateLocalSize(),this.config.setup&&this.config.setup(this.buildContext(0)),this.initialized=true;}start(){this.running||(this.running=true,this.lastTime=performance.now(),this.scheduleFrame());}stop(){var t;!this.running&&!this.initialized||(this.running=false,this.frameId!=null&&(OUID.cancelAnimationFrame(this.frameId),this.frameId=null),this.resizeListenerId&&(OUID.dettachEventListener(this.resizeListenerId),this.resizeListenerId=null),this.audio.clean(),this.assets.clean(),this.input.clean(),(t=this.ctx2d)==null||t.release(),this.initialized=false);}scheduleFrame(){OUID.requestAnimationFrame(t=>{this.frameId=null,this.tick(t);}).then(t=>{this.running?this.frameId=t:OUID.cancelAnimationFrame(t);});}async tick(t){var r,o,n,a;if(!this.running)return;let e=Math.min((t-this.lastTime)/1e3,.1);this.lastTime=t;try{let h=this.buildContext(e);(o=(r=this.config).update)==null||o.call(r,h),this.input.update(),await((a=(n=this.config).draw)==null?void 0:a.call(n,h)),await this.ctx2d.commit();}catch(h){console.error("[Toubani] game loop error",h);}finally{this.running&&this.scheduleFrame();}}};c(B,"Game");var A=B;function Kt(l){return new A(l)}c(Kt,"tulon");function H(l){var t;return (t=l.__local_size__)!=null?t:{width:0,height:0}}c(H,"objectSize");function y(l,t){return Math.floor(l/t.tileWidth)}c(y,"worldToTileX");function v(l,t){return Math.floor(l/t.tileHeight)}c(v,"worldToTileY");function Tt(l,t){let{image:s,tileWidth:e,tileHeight:r,margin:o=0,spacing:n=0}=t,a=H(s),h=Math.max(1,Math.floor((a.width-2*o+n)/(e+n))),d=l-1,p=d%h,u=Math.floor(d/h),f=o+p*(e+n),m=o+u*(r+n);return {sx:f,sy:m,sw:e,sh:r}}c(Tt,"getTileSourceRect");var $=class ${constructor(t){i(this,"tileset");i(this,"sourceRectCache",new Map);this.tileset=t;}draw(t,s,e,r,o){let{tileWidth:n,tileHeight:a}=s,h=r*e.focusX,d=o*e.focusY,p=e.zoom,u=e.x-h/p,f=e.y-d/p,m=e.x+(r-h)/p,w=e.y+(o-d)/p,g=Math.max(0,Math.floor(u/n)),x=Math.min(s.width-1,Math.floor(m/n)),I=Math.max(0,Math.floor(f/a)),mt=Math.min(s.height-1,Math.floor(w/a));for(let P=I;P<=mt;P++)for(let b=g;b<=x;b++){let Z=s.getTile(b,P);if(Z<=0)continue;let{sx:xt,sy:yt,sw:vt,sh:Mt}=this.getSourceRect(Z),kt=b*n,It=P*a;t.drawImage(this.tileset.image,xt,yt,vt,Mt,kt,It,n,a);}}getSourceRect(t){let s=this.sourceRectCache.get(t);return s||(s=Tt(t,this.tileset),this.sourceRectCache.set(t,s)),s}};c($,"TileRenderer");var at=$;var j=class j{constructor(t,s){i(this,"node");i(this,"settings");i(this,"vx",0);i(this,"vy",0);i(this,"onGround",false);this.node=t,this.settings=s;}update(t){var f,m,w,g,x;let s=this.settings,e=(f=s.gravityX)!=null?f:0,r=(m=s.gravityY)!=null?m:1e3,o=(w=s.maxFallSpeed)!=null?w:2e3,n=(g=s.friction)!=null?g:.8,a=(x=s.airFriction)!=null?x:.98;this.vx+=e*t,this.vy+=r*t,this.vy>o&&(this.vy=o);let h=Math.max(1,Math.min(s.tileMap.tileWidth,s.tileMap.tileHeight)*.5),d=Math.max(Math.abs(this.vx*t),Math.abs(this.vy*t)),p=Math.max(1,Math.ceil(d/h)),u=t/p;this.onGround=false;for(let I=0;I<p;I++)this.moveAndCollideX(u),this.moveAndCollideY(u);this.onGround?this.vx*=n:this.vx*=a;}moveAndCollideX(t){let s=this.settings.tileMap,e=this.settings.width,r=this.settings.height,o=this.node.x+this.vx*t,n=this.node.y,a=Math.sign(this.vx);if(a===0){this.node.x=o;return}let h=o,d=o+e,p=n,u=n+r,f=Math.max(0,v(p,s)),m=Math.min(s.height-1,v(u-1,s));if(a>0){let w=y(d,s);for(let g=f;g<=m;g++)if(this.isSolid(w,g)){d=w*s.tileWidth,h=d-e,this.vx=0;break}}else {let w=y(h,s);for(let g=f;g<=m;g++)if(this.isSolid(w,g)){h=(w+1)*s.tileWidth,d=h+e,this.vx=0;break}}this.node.x=h;}moveAndCollideY(t){let s=this.settings.tileMap,e=this.settings.width,r=this.settings.height,o=this.node.x,n=this.node.y+this.vy*t,a=Math.sign(this.vy);if(a===0){this.node.y=n;return}let h=n,d=n+r,p=o,u=o+e,f=Math.max(0,y(p,s)),m=Math.min(s.width-1,y(u-1,s));if(a>0){let w=v(d,s);for(let g=f;g<=m;g++)if(this.isSolid(g,w)){d=w*s.tileHeight,h=d-r,this.vy=0,this.onGround=true;break}}else {let w=v(h,s);for(let g=f;g<=m;g++)if(this.isSolid(g,w)){h=(w+1)*s.tileHeight,d=h+r,this.vy=0;break}}this.node.y=h;}isSolid(t,s){let e=this.settings.tileMap;return t<0||s<0||t>=e.width||s>=e.height?false:e.isSolid(t,s)}};c(j,"PhysicsBody");var ht=j;var F=class F{constructor(){i(this,"x",0);i(this,"y",0);i(this,"zoom",1);i(this,"rotation",0);i(this,"followTarget",null);i(this,"followLerp",.1);i(this,"focusX",.5);i(this,"focusY",.5);}follow(t,s=.1){this.followTarget=t,this.followLerp=s;}unfollow(){this.followTarget=null;}update(t){if(!this.followTarget)return;let s=this.followTarget.x,e=this.followTarget.y;this.x+=(s-this.x)*this.followLerp,this.y+=(e-this.y)*this.followLerp;}begin(t,s,e){t.save();let r=s*this.focusX,o=e*this.focusY;t.translate(r,o),this.rotation!==0&&t.rotate(-this.rotation),this.zoom!==1&&t.scale(this.zoom,this.zoom),t.translate(-this.x,-this.y);}end(t){t.restore();}};c(F,"Camera2D");var ct=F;var J=class J{constructor(){i(this,"x",0);i(this,"y",0);i(this,"rotation",0);i(this,"scaleX",1);i(this,"scaleY",1);i(this,"visible",true);i(this,"parent",null);i(this,"children",[]);}add(t){return t.parent=this,this.children.push(t),t}remove(t){let s=this.children.indexOf(t);s>=0&&(this.children.splice(s,1),t.parent=null);}clear(){for(let t of this.children)t.parent=null;this.children.length=0;}update(t){}drawSelf(t){}draw(t){if(this.visible){t.save(),t.translate(this.x,this.y),this.rotation!==0&&t.rotate(this.rotation),(this.scaleX!==1||this.scaleY!==1)&&t.scale(this.scaleX,this.scaleY),this.drawSelf(t);for(let s of this.children)s.draw(t);t.restore();}}updateTree(t){this.update(t);for(let s of this.children)s.updateTree(t);}getWorldPosition(){let t=this.x,s=this.y,e=this.parent;for(;e;)t+=e.x,s+=e.y,e=e.parent;return {x:t,y:s}}};c(J,"SceneNode");var k=J,G=class G extends k{constructor(s){super();i(this,"sprite");this.sprite=s;}update(s){this.sprite.update(s);}drawSelf(s){this.sprite.draw(s);}getWorldBounds(){let s=this.getWorldPosition();return {x:s.x,y:s.y,width:this.sprite.width*this.scaleX,height:this.sprite.height*this.scaleY}}};c(G,"SpriteNode");var lt=G,N=class N extends k{constructor(s=1,e=1){super();i(this,"parallaxX",1);i(this,"parallaxY",1);this.parallaxX=s,this.parallaxY=e;}};c(N,"SceneLayer");var _=N,M=class M{constructor(){i(this,"id");i(this,"root",new k);i(this,"layers",[]);this.id=M.id++;}addLayer(t=1,s=1){let e=new _(t,s);return this.layers.push(e),this.root.add(e),e}update(t){this.root.updateTree(t);}draw(t){this.root.draw(t);}};c(M,"Scene"),i(M,"id",0);var dt=M;function ut(l,t){return !(l.x+l.width<=t.x||l.x>=t.x+t.width||l.y+l.height<=t.y||l.y>=t.y+t.height)}c(ut,"aabbOverlap");var V=class V{constructor(){i(this,"colliders",[]);i(this,"rules",[]);i(this,"rulesByPair",new Map);}add(t){this.colliders.push(t);}remove(t){let s=this.colliders.indexOf(t);s>=0&&this.colliders.splice(s,1);}clear(){this.colliders.length=0;}onCollision(t,s,e){var a;let r={tagA:t,tagB:s,handler:e};this.rules.push(r);let o=this.ruleKey(t,s),n=(a=this.rulesByPair.get(o))!=null?a:[];n.push(r),this.rulesByPair.set(o,n);}update(){var r;let t=new Map,s=new Map;for(let o of this.colliders){if(!o.isActive)continue;let n=(r=t.get(o.tag))!=null?r:[];n.push(o),t.set(o.tag,n);}let e=c(o=>{let n=s.get(o);return n||(n=o.getBounds(),s.set(o,n)),n},"getBounds");for(let[o,n]of this.rulesByPair.entries()){let[a,h]=o.split("\0"),d=t.get(a),p=t.get(h);if(!(!d||!p))if(a===h)for(let u=0;u<d.length;u++){let f=d[u],m=e(f);for(let w=u+1;w<d.length;w++){let g=d[w];ut(m,e(g))&&this.triggerRules(f,g,n);}}else for(let u of d){let f=e(u);for(let m of p)ut(f,e(m))&&this.triggerRules(u,m,n);}}}ruleKey(t,s){return t<=s?`${t}\0${s}`:`${s}\0${t}`}triggerRules(t,s,e){for(let r of e){let o=r.tagA===t.tag&&r.tagB===s.tag,n=r.tagA===s.tag&&r.tagB===t.tag;o?r.handler(t,s):n&&r.handler(s,t);}}};c(V,"CollisionSystem");var ft=V,zt=0;function ls(l,t,s,e){return {id:`col-${zt++}`,tag:l,isActive:true,data:{node:t,body:s,sprite:e},getBounds(){let o=t.getWorldPosition(),n=s.settings.width,a=s.settings.height;return {x:o.x,y:o.y,width:n,height:a}}}}c(ls,"createSpritePhysicsCollider");var q=class q{constructor(t={}){i(this,"options");i(this,"opened",false);i(this,"socket",null);i(this,"currentUrl",null);i(this,"offMessage",null);i(this,"msgHandlers",[]);i(this,"openHandlers",[]);i(this,"closeHandlers",[]);i(this,"errorHandlers",[]);this.options=t;}async connect(t){if(this.socket){if(this.currentUrl===t&&this.socket.isConnected())return;if(this.currentUrl===t&&!this.opened){await this.socket.connect();return}await this.cleanupSocket(true);}let{protocols:s,connectTimeoutMs:e}=this.options,r=ouiderNetwork.OUIDNetwork.websocket(t,s,{connectTimeoutMs:e});this.socket=r,this.currentUrl=t,r.onOpen(()=>{this.opened=true,this.openHandlers.forEach(o=>o());}),this.offMessage=await r.onMessage(o=>{var a;let n=typeof o=="string"?o:(a=JSON.stringify(o))!=null?a:"";this.msgHandlers.forEach(h=>h(n));}),r.onClose(o=>{this.opened=false,this.closeHandlers.forEach(n=>n(o));}),r.onError(o=>{this.errorHandlers.forEach(n=>n(o));});try{await r.connect();}catch(o){throw this.socket===r&&await this.cleanupSocket(false),o}}async send(t){var s;await((s=this.socket)==null?void 0:s.send(t));}async close(t,s){await this.cleanupSocket(true,t,s);}isConnected(){return this.opened}onMessage(t){this.msgHandlers.push(t);}onOpen(t){this.openHandlers.push(t);}offOpen(t){this.openHandlers=this.openHandlers.filter(s=>s!==t);}onClose(t){this.closeHandlers.push(t);}offClose(t){this.closeHandlers=this.closeHandlers.filter(s=>s!==t);}onError(t){this.errorHandlers.push(t);}offError(t){this.errorHandlers=this.errorHandlers.filter(s=>s!==t);}async cleanupSocket(t,s,e){let r=this.socket;this.socket=null,this.currentUrl=null,this.opened=false;let o=this.offMessage;this.offMessage=null,await(o==null?void 0:o().catch(()=>{})),t&&await(r==null?void 0:r.close(s,e));}};c(q,"WebSocketTransport");var pt=q,K=class K{constructor(t){i(this,"transport");i(this,"handlers",new Map);i(this,"rpcResolvers",new Map);i(this,"rpcCounter",0);i(this,"handleRawMessage",c(t=>{if(!t)return;let s;try{s=JSON.parse(t);}catch(o){return}let e=this.extractRpcId(s);if(e){let o=this.rpcResolvers.get(e);o&&(o.resolve(this.extractRpcValue(s)),this.rpcResolvers.delete(e));}let r=this.handlers.get(s.type);if(r)for(let o of r)o(s);},"handleRawMessage"));this.transport=t,this.transport.onMessage(this.handleRawMessage),this.transport.onClose(()=>this.rejectPendingRpc(new Error("Network connection closed"))),this.transport.onError(s=>{this.rejectPendingRpc(s instanceof Error?s:new Error("Network connection error"));});}async connect(t){await this.transport.connect(t);}isConnected(){return this.transport.isConnected()}send(t,s){let e={type:t,payload:s};return this.transport.send(JSON.stringify(e))}on(t,s){var r;let e=(r=this.handlers.get(t))!=null?r:[];e.push(s),this.handlers.set(t,e);}off(t,s){let e=this.handlers.get(t);e&&this.handlers.set(t,e.filter(r=>r!==s));}call(t,s){let e=`rpc_${++this.rpcCounter}_${Date.now()}`,r=s&&typeof s=="object"?it(et({},s),{rpcId:e}):{value:s,rpcId:e},o={type:t,payload:r};return new Promise((n,a)=>{this.rpcResolvers.set(e,{resolve:n,reject:a}),this.transport.send(JSON.stringify(o)).catch(h=>{this.rpcResolvers.delete(e),a(h);});})}close(){return this.rejectPendingRpc(new Error("Network client closed")),this.transport.close()}extractRpcId(t){if(typeof t.rpcId=="string")return t.rpcId;if(t.payload&&typeof t.payload=="object"&&"rpcId"in t.payload){let s=t.payload.rpcId;return typeof s=="string"?s:void 0}}extractRpcValue(t){return "result"in t?t.result:t.payload}rejectPendingRpc(t){this.rpcResolvers.forEach(({reject:s})=>s(t)),this.rpcResolvers.clear();}};c(K,"NetworkClient");var gt=K,Q=class Q{constructor(t,s,e){i(this,"net");i(this,"channels");i(this,"state");i(this,"onChangeHandlers",[]);var r;this.net=t,this.channels=e,this.state=s,this.net.on((r=e==null?void 0:e.update)!=null?r:"state_update",o=>{let{payload:n}=o;n.full?this.state=n.full:n.patch&&(this.state=Object.assign({},this.state,n.patch)),this.emitChange();});}getState(){return this.state}onChange(t){this.onChangeHandlers.push(t);}emitChange(){for(let t of this.onChangeHandlers)t(this.state);}sendPatch(t){var s,e;return this.net.send((e=(s=this.channels)==null?void 0:s.patch)!=null?e:"state_patch",t)}};c(Q,"StateSyncClient");var wt=Q;exports.AssetManager=Y;exports.AudioManager=T;exports.Camera2D=ct;exports.CollisionSystem=ft;exports.Game=A;exports.InputManager=z;exports.NetworkClient=gt;exports.PhysicsBody=ht;exports.Scene=dt;exports.SceneLayer=_;exports.SceneNode=k;exports.Sprite=ot;exports.SpriteAnimation=X;exports.SpriteLayer=rt;exports.SpriteNode=lt;exports.SpriteSheet=D;exports.StateSyncClient=wt;exports.TileRenderer=at;exports.WebSocketTransport=pt;exports.aabbOverlap=ut;exports.createSpritePhysicsCollider=ls;exports.getTileSourceRect=Tt;exports.objectSize=H;exports.tulon=Kt;exports.worldToTileX=y;exports.worldToTileY=v;
1
+ 'use strict';var ouider=require('@ouidesigner/ouider');require('@ouidesigner/ouider-device');var ouiderNetwork=require('@ouidesigner/ouider-network');var st=Object.defineProperty,Rt=Object.defineProperties;var It=Object.getOwnPropertyDescriptors;var et=Object.getOwnPropertySymbols;var Dt=Object.prototype.hasOwnProperty,Ct=Object.prototype.propertyIsEnumerable;var S=(d,t,e)=>t in d?st(d,t,{enumerable:true,configurable:true,writable:true,value:e}):d[t]=e,it=(d,t)=>{for(var e in t||(t={}))Dt.call(t,e)&&S(d,e,t[e]);if(et)for(var e of et(t))Ct.call(t,e)&&S(d,e,t[e]);return d},nt=(d,t)=>Rt(d,It(t)),c=(d,t)=>st(d,"name",{value:t,configurable:true});var i=(d,t,e)=>S(d,typeof t!="symbol"?t+"":t,e);var E=class E{constructor(t,e,s,o,n=0){i(this,"image");i(this,"frameWidth");i(this,"frameHeight");i(this,"frameCount");i(this,"from");i(this,"columns");this.image=t,this.frameWidth=e,this.frameHeight=s,this.frameCount=o,this.from=n;let r=b(t);this.columns=Math.max(1,Math.floor(r.width/e));}};c(E,"SpriteSheet");var D=E,X=class X{constructor(t,e=10,s=true){i(this,"sheet");i(this,"fps");i(this,"loop");i(this,"time",0);this.sheet=t,this.fps=e,this.loop=s;}update(t){this.time+=t;let e=this.sheet.frameCount/this.fps;this.loop?this.time=this.time%e:this.time>e&&(this.time=e);}draw(t,e,s,o=1,n=1){let r=Math.floor(this.time*this.fps)+this.sheet.from,h=Math.min(r,this.sheet.frameCount+this.sheet.from-1),a=this.sheet.columns,l=h%a*this.sheet.frameWidth,p=Math.floor(h/a)*this.sheet.frameHeight,u=this.sheet.frameWidth*o,f=this.sheet.frameHeight*n;t.drawImage(this.sheet.image,l,p,this.sheet.frameWidth,this.sheet.frameHeight,e,s,u,f);}reset(){this.time=0;}};c(X,"SpriteAnimation");var C=X,Y=class Y{constructor(t){i(this,"animation");i(this,"width");i(this,"height");i(this,"visible");i(this,"tag");i(this,"data");var e,s,o,n;this.animation=t.animation,this.visible=(e=t.visible)!=null?e:true,this.tag=t.tag,this.data=(s=t.data)!=null?s:{},this.width=(o=t.width)!=null?o:this.animation.sheet.frameWidth,this.height=(n=t.height)!=null?n:this.animation.sheet.frameHeight;}update(t){this.animation.update(t);}draw(t,e=1,s=1){this.visible&&this.animation.draw(t,0,0,e,s);}};c(Y,"Sprite");var ot=Y,z=class z{constructor(){i(this,"sprites",[]);}add(t){return this.sprites.push(t),t}remove(t){let e=this.sprites.indexOf(t);e>=0&&this.sprites.splice(e,1);}clear(){this.sprites.length=0;}update(t){for(let e of this.sprites)e.update(t);}draw(t){for(let e of this.sprites)e.draw(t);}withTag(t){return this.sprites.filter(e=>e.tag===t)}};c(z,"SpriteLayer");var rt=z;var A=class A{constructor(){i(this,"imageDefs",new Map);i(this,"soundDefs",new Map);i(this,"sheetDefs",new Map);i(this,"images",new Map);i(this,"sounds",new Map);i(this,"sheets",new Map);}image(t,e){this.imageDefs.set(t,e);}sound(t,e){this.soundDefs.set(t,e);}spriteSheet(t,e){this.sheetDefs.set(t,e);}async loadAll(){var s,o;let t=[],e=[];for(let[n,r]of this.imageDefs.entries())t.push(this.loadImage(r).then(h=>{this.images.set(n,h);}));for(let[n,r]of this.soundDefs.entries())e.push(this.loadSound(r).then(h=>{this.sounds.set(n,h);}));await Promise.all([...t,...e]);for(let[n,r]of this.sheetDefs.entries()){let h=(s=this.getImage(r.image))!=null?s:this.images.get(r.image);if(!h)throw new Error(`SpriteSheet "${n}" missing image "${r.image}"`);let a=new D(h,r.frameWidth,r.frameHeight,r.frameCount,(o=r.from)!=null?o:0);this.sheets.set(n,a);}}async loadImage(t){let e=await ouider.ODOM.Image.new();if(!e)throw new Error(`Unable to create image for "${t}"`);let s=null,o=null,n=false,r=c(async()=>{let a=[s,o].filter(Boolean);s=null,o=null,await Promise.all(a.map(l=>e.dettachEventListener(l).catch(()=>{})));},"detach"),h=c(async()=>{let[a,l,p,u]=await Promise.all([e.naturalWidth(),e.naturalHeight(),e.width(),e.height()]);e.__local_size__={width:a||p||0,height:l||u||0};},"cacheSize");return new Promise(async(a,l)=>{let p=c(async()=>{if(!n)try{await h(),n=!0,await r(),a(e);}catch(f){await u(f);}},"done"),u=c(async f=>{n||(n=true,await r(),await e.release().catch(()=>{}),l(new Error(`Unable to load image "${t}"`)));},"fail");try{s=await e.addEventListener("load",()=>{p();},{options:{once:!0}}),o=await e.addEventListener("error",()=>{u();},{options:{once:!0}}),await e.setSrc(t);let[f,w]=await Promise.all([e.getProperty("complete").catch(()=>!1),e.naturalWidth().catch(()=>0)]);f&&w>0&&await p();}catch(f){await u(f);}})}async loadSound(t){return await ouider.OUID.media.audio.decode(t),t}getImage(t){return this.images.get(t)}getSpriteSheet(t){return this.sheets.get(t)}createAnimation(t,e,s){let o=this.getSpriteSheet(t);if(!o)throw new Error(`SpriteSheet "${t}" not found`);return new C(o,e!=null?e:10,s!=null?s:true)}getSound(t){return this.sounds.get(t)}clean(){this.images.forEach(t=>t.release()),this.images.clear(),this.sounds.clear(),this.sheets.clear();}};c(A,"AssetManager");var L=A;var U=class U{constructor(t){i(this,"assets");this.assets=t;}clean(){ouider.OUID.media.audio.close();}async play(t,e){let s=this.assets.getSound(t);s&&await ouider.OUID.media.audio.play(s,e);}};c(U,"AudioManager");var H=U;var W=class W{constructor(t){i(this,"keysDown",new Set);i(this,"keysPressed",new Set);i(this,"buttonsPressed",new Set);i(this,"buttonsDown",new Set);i(this,"mouseX",-1);i(this,"mouseY",-1);i(this,"offsetX",-1);i(this,"offsetY",-1);i(this,"ids",[]);i(this,"disposed",false);this.track(OUID.addEventListener("window","keydown",e=>{this.keysDown.has(e.code)||this.keysPressed.add(e.code),this.keysDown.add(e.code);})),this.track(OUID.addEventListener("window","keyup",e=>{this.keysDown.delete(e.code);})),this.track(t.addEventListener("mousedown",e=>{let s=e.button===0?"MouseLeft":"MouseRight";this.buttonsDown.has(s)||this.buttonsPressed.add(s),this.buttonsDown.add(s),this.updateMousePosition(e);})),this.track(t.addEventListener("mouseup",e=>{let s=e.button===0?"MouseLeft":"MouseRight";this.buttonsDown.delete(s),this.updateMousePosition(e);})),this.track(t.addEventListener("mousemove",e=>{this.updateMousePosition(e);})),this.track(t.addEventListener("mouseleave",()=>{this.buttonsDown.clear();}));}update(){this.keysPressed.clear(),this.buttonsPressed.clear();}isDown(t){return this.keysDown.has(t)||this.buttonsDown.has(t)}isPressed(t){return this.keysPressed.has(t)||this.buttonsPressed.has(t)}clean(){this.disposed=true,this.ids.forEach(t=>{OUID.dettachEventListener(t);}),this.ids=[],this.keysDown.clear(),this.keysPressed.clear(),this.buttonsDown.clear(),this.buttonsPressed.clear();}track(t){t.then(e=>{this.disposed?OUID.dettachEventListener(e):this.ids.push(e);}).catch(()=>{});}updateMousePosition(t){var e,s,o,n;this.mouseX=(e=t.x)!=null?e:this.mouseX,this.mouseY=(s=t.y)!=null?s:this.mouseY,this.offsetX=(o=t.offsetX)!=null?o:this.offsetX,this.offsetY=(n=t.offsetY)!=null?n:this.offsetY;}};c(W,"InputManager");var O=W;var F=class F{constructor(t){i(this,"config");i(this,"canvas");i(this,"ctx2d");i(this,"assets");i(this,"audio");i(this,"input");i(this,"world",{});i(this,"running",false);i(this,"initialized",false);i(this,"lastTime",0);i(this,"frameId",null);i(this,"resizeListenerId",null);this.config=t,this.canvas=t.canvas,this.assets=new L,this.audio=new H(this.assets),this.input=new O(this.canvas);}buildContext(t){return {dt:t,canvas:this.canvas,ctx2d:this.ctx2d,world:this.world,assets:this.assets,audio:this.audio,input:this.input}}async updateLocalSize(){var s,o;let t=(s=await this.canvas.width())!=null?s:0,e=(o=await this.canvas.height())!=null?o:0;this.canvas.__local_size__=this.ctx2d.__local_size__={width:t,height:e};}async syncSize(){var h,a;let[t,e,s,o]=await Promise.all([this.canvas.getProperty("clientWidth"),this.canvas.getProperty("clientHeight"),this.canvas.width(),this.canvas.height()]),n=(h=t!=null?t:s)!=null?h:0,r=(a=e!=null?e:o)!=null?a:0;await this.canvas.setProperty("width",n),await this.canvas.setProperty("height",r);}async init(){if(this.initialized)return;let t=await this.canvas.getContext("2d");if(!t)throw new Error("Unable to create a context");this.ctx2d=t,this.resizeListenerId=await this.canvas.addEventListener("resize",()=>{this.syncSize().then(()=>this.updateLocalSize());}),this.config.preload&&(this.config.preload(this.assets),await this.assets.loadAll()),await this.syncSize(),await this.updateLocalSize(),this.config.setup&&this.config.setup(this.buildContext(0)),this.initialized=true;}start(){this.running||(this.running=true,this.lastTime=performance.now(),this.scheduleFrame());}stop(){var t;!this.running&&!this.initialized||(this.running=false,this.frameId!=null&&(OUID.cancelAnimationFrame(this.frameId),this.frameId=null),this.resizeListenerId&&(OUID.dettachEventListener(this.resizeListenerId),this.resizeListenerId=null),this.audio.clean(),this.assets.clean(),this.input.clean(),(t=this.ctx2d)==null||t.release(),this.initialized=false);}scheduleFrame(){OUID.requestAnimationFrame(t=>{this.frameId=null,this.tick(t);}).then(t=>{this.running?this.frameId=t:OUID.cancelAnimationFrame(t);});}async tick(t){var o,n,r,h;if(!this.running)return;let s=Math.min((t-this.lastTime)/1e3,.1);this.lastTime=t;try{let a=this.buildContext(s);(n=(o=this.config).update)==null||n.call(o,a),this.input.update(),await((h=(r=this.config).draw)==null?void 0:h.call(r,a)),await this.ctx2d.commit();}catch(a){console.error("[Toubani] game loop error",a);}finally{this.running&&this.scheduleFrame();}}};c(F,"Game");var B=F;function Qt(d){return new B(d)}c(Qt,"tulon");function b(d){var t;return (t=d.__local_size__)!=null?t:{width:0,height:0}}c(b,"objectSize");function x(d,t){return Math.floor(d/t.tileWidth)}c(x,"worldToTileX");function v(d,t){return Math.floor(d/t.tileHeight)}c(v,"worldToTileY");function Ht(d,t){let{image:e,tileWidth:s,tileHeight:o,margin:n=0,spacing:r=0}=t,h=b(e),a=Math.max(1,Math.floor((h.width-2*n+r)/(s+r))),l=d-1,p=l%a,u=Math.floor(l/a),f=n+p*(s+r),w=n+u*(o+r);return {sx:f,sy:w,sw:s,sh:o}}c(Ht,"getTileSourceRect");var j=class j{constructor(t){i(this,"tileset");i(this,"sourceRectCache",new Map);this.tileset=t;}draw(t,e,s,o,n){let{tileWidth:r,tileHeight:h}=e,a=o*s.focusX,l=n*s.focusY,p=s.zoom,u=s.x-a/p,f=s.y-l/p,w=s.x+(o-a)/p,m=s.y+(n-l)/p,g=Math.max(0,Math.floor(u/r)),y=Math.min(e.width-1,Math.floor(w/r)),P=Math.max(0,Math.floor(f/h)),yt=Math.min(e.height-1,Math.floor(m/h));for(let R=P;R<=yt;R++)for(let I=g;I<=y;I++){let tt=e.getTile(I,R);if(tt<=0)continue;let{sx:xt,sy:vt,sw:Mt,sh:kt}=this.getSourceRect(tt),Tt=I*r,Pt=R*h;t.drawImage(this.tileset.image,xt,vt,Mt,kt,Tt,Pt,r,h);}}getSourceRect(t){let e=this.sourceRectCache.get(t);return e||(e=Ht(t,this.tileset),this.sourceRectCache.set(t,e)),e}};c(j,"TileRenderer");var ht=j;var $=class ${constructor(t,e){i(this,"node");i(this,"settings");i(this,"vx",0);i(this,"vy",0);i(this,"onGround",false);this.node=t,this.settings=e;}update(t){var f,w,m,g,y;let e=this.settings,s=(f=e.gravityX)!=null?f:0,o=(w=e.gravityY)!=null?w:1e3,n=(m=e.maxFallSpeed)!=null?m:2e3,r=(g=e.friction)!=null?g:.8,h=(y=e.airFriction)!=null?y:.98;this.vx+=s*t,this.vy+=o*t,this.vy>n&&(this.vy=n);let a=Math.max(1,Math.min(e.tileMap.tileWidth,e.tileMap.tileHeight)*.5),l=Math.max(Math.abs(this.vx*t),Math.abs(this.vy*t)),p=Math.max(1,Math.ceil(l/a)),u=t/p;this.onGround=false;for(let P=0;P<p;P++)this.moveAndCollideX(u),this.moveAndCollideY(u);this.onGround?this.vx*=r:this.vx*=h;}moveAndCollideX(t){let e=this.settings.tileMap,s=this.settings.width,o=this.settings.height,n=this.node.x+this.vx*t,r=this.node.y,h=Math.sign(this.vx);if(h===0){this.node.x=n;return}let a=n,l=n+s,p=r,u=r+o,f=Math.max(0,v(p,e)),w=Math.min(e.height-1,v(u-1,e));if(h>0){let m=x(l,e);for(let g=f;g<=w;g++)if(this.isSolid(m,g)){l=m*e.tileWidth,a=l-s,this.vx=0;break}}else {let m=x(a,e);for(let g=f;g<=w;g++)if(this.isSolid(m,g)){a=(m+1)*e.tileWidth,l=a+s,this.vx=0;break}}this.node.x=a;}moveAndCollideY(t){let e=this.settings.tileMap,s=this.settings.width,o=this.settings.height,n=this.node.x,r=this.node.y+this.vy*t,h=Math.sign(this.vy);if(h===0){this.node.y=r;return}let a=r,l=r+o,p=n,u=n+s,f=Math.max(0,x(p,e)),w=Math.min(e.width-1,x(u-1,e));if(h>0){let m=v(l,e);for(let g=f;g<=w;g++)if(this.isSolid(g,m)){l=m*e.tileHeight,a=l-o,this.vy=0,this.onGround=true;break}}else {let m=v(a,e);for(let g=f;g<=w;g++)if(this.isSolid(g,m)){a=(m+1)*e.tileHeight,l=a+o,this.vy=0;break}}this.node.y=a;}isSolid(t,e){let s=this.settings.tileMap;return t<0||e<0||t>=s.width||e>=s.height?false:s.isSolid(t,e)}};c($,"PhysicsBody");var ct=$;var N=class N{constructor(){i(this,"x",0);i(this,"y",0);i(this,"zoom",1);i(this,"rotation",0);i(this,"followTarget",null);i(this,"followLerp",.1);i(this,"focusX",.5);i(this,"focusY",.5);}follow(t,e=.1){this.followTarget=t,this.followLerp=e;}unfollow(){this.followTarget=null;}update(t){if(!this.followTarget)return;let e=this.followTarget.x,s=this.followTarget.y;this.x+=(e-this.x)*this.followLerp,this.y+=(s-this.y)*this.followLerp;}begin(t,e,s){t.save();let o=e*this.focusX,n=s*this.focusY;t.translate(o,n),this.rotation!==0&&t.rotate(-this.rotation),this.zoom!==1&&t.scale(this.zoom,this.zoom),t.translate(-this.x,-this.y);}end(t){t.restore();}};c(N,"Camera2D");var lt=N;var J=class J{constructor(){i(this,"x",0);i(this,"y",0);i(this,"rotation",0);i(this,"scaleX",1);i(this,"scaleY",1);i(this,"visible",true);i(this,"parent",null);i(this,"children",[]);}add(t){return t.parent=this,this.children.push(t),t}remove(t){let e=this.children.indexOf(t);e>=0&&(this.children.splice(e,1),t.parent=null);}clear(){for(let t of this.children)t.parent=null;this.children.length=0;}update(t){}drawSelf(t){}draw(t){if(this.visible){t.save(),t.translate(this.x,this.y),this.rotation!==0&&t.rotate(this.rotation),(this.scaleX!==1||this.scaleY!==1)&&t.scale(this.scaleX,this.scaleY),this.drawSelf(t);for(let e of this.children)e.draw(t);t.restore();}}updateTree(t){this.update(t);for(let e of this.children)e.updateTree(t);}getWorldPosition(){let t=this.x,e=this.y,s=this.parent;for(;s;)t+=s.x,e+=s.y,s=s.parent;return {x:t,y:e}}};c(J,"SceneNode");var k=J,V=class V extends k{constructor(e){super();i(this,"sprite");this.sprite=e;}update(e){this.sprite.update(e);}drawSelf(e){this.sprite.draw(e);}getWorldBounds(){let e=this.getWorldPosition();return {x:e.x,y:e.y,width:this.sprite.width*this.scaleX,height:this.sprite.height*this.scaleY}}};c(V,"SpriteNode");var dt=V,G=class G extends k{constructor(e=1,s=1){super();i(this,"parallaxX",1);i(this,"parallaxY",1);this.parallaxX=e,this.parallaxY=s;}};c(G,"SceneLayer");var _=G,M=class M{constructor(){i(this,"id");i(this,"root",new k);i(this,"layers",[]);this.id=M.id++;}addLayer(t=1,e=1){let s=new _(t,e);return this.layers.push(s),this.root.add(s),s}update(t){this.root.updateTree(t);}draw(t){this.root.draw(t);}};c(M,"Scene"),i(M,"id",0);var ut=M;function ft(d,t){return !(d.x+d.width<=t.x||d.x>=t.x+t.width||d.y+d.height<=t.y||d.y>=t.y+t.height)}c(ft,"aabbOverlap");var q=class q{constructor(){i(this,"colliders",[]);i(this,"rules",[]);i(this,"rulesByPair",new Map);}add(t){this.colliders.push(t);}remove(t){let e=this.colliders.indexOf(t);e>=0&&this.colliders.splice(e,1);}clear(){this.colliders.length=0;}onCollision(t,e,s){var h;let o={tagA:t,tagB:e,handler:s};this.rules.push(o);let n=this.ruleKey(t,e),r=(h=this.rulesByPair.get(n))!=null?h:[];r.push(o),this.rulesByPair.set(n,r);}update(){var o;let t=new Map,e=new Map;for(let n of this.colliders){if(!n.isActive)continue;let r=(o=t.get(n.tag))!=null?o:[];r.push(n),t.set(n.tag,r);}let s=c(n=>{let r=e.get(n);return r||(r=n.getBounds(),e.set(n,r)),r},"getBounds");for(let[n,r]of this.rulesByPair.entries()){let[h,a]=n.split("\0"),l=t.get(h),p=t.get(a);if(!(!l||!p))if(h===a)for(let u=0;u<l.length;u++){let f=l[u],w=s(f);for(let m=u+1;m<l.length;m++){let g=l[m];ft(w,s(g))&&this.triggerRules(f,g,r);}}else for(let u of l){let f=s(u);for(let w of p)ft(f,s(w))&&this.triggerRules(u,w,r);}}}ruleKey(t,e){return t<=e?`${t}\0${e}`:`${e}\0${t}`}triggerRules(t,e,s){for(let o of s){let n=o.tagA===t.tag&&o.tagB===e.tag,r=o.tagA===e.tag&&o.tagB===t.tag;n?o.handler(t,e):r&&o.handler(e,t);}}};c(q,"CollisionSystem");var pt=q,Ot=0;function de(d,t,e,s){return {id:`col-${Ot++}`,tag:d,isActive:true,data:{node:t,body:e,sprite:s},getBounds(){let n=t.getWorldPosition(),r=e.settings.width,h=e.settings.height;return {x:n.x,y:n.y,width:r,height:h}}}}c(de,"createSpritePhysicsCollider");var K=class K{constructor(t={}){i(this,"options");i(this,"opened",false);i(this,"socket",null);i(this,"currentUrl",null);i(this,"desiredUrl",null);i(this,"offMessage",null);i(this,"msgHandlers",[]);i(this,"openHandlers",[]);i(this,"closeHandlers",[]);i(this,"errorHandlers",[]);i(this,"reconnectHandlers",[]);i(this,"reconnectAttempts",0);i(this,"reconnectTimerId",null);i(this,"reconnectTimerPending",false);i(this,"reconnectTimerToken",0);i(this,"reconnecting",null);i(this,"manuallyClosed",true);i(this,"appBackgrounded",false);i(this,"offForeground",null);i(this,"offBackground",null);i(this,"foregroundListenerPending",false);i(this,"backgroundListenerPending",false);this.options=t;}async connect(t){this.desiredUrl=t,this.manuallyClosed=false,this.reconnectAttempts=0,await this.clearReconnectTimer(),this.ensureAppStateListeners(),await this.openSocket(t);}async openSocket(t){if(this.socket){if(this.currentUrl===t&&this.socket.isConnected())return;if(this.currentUrl===t&&!this.opened){let n=this.socket;try{await n.connect();}catch(r){throw this.socket===n&&await this.cleanupSocket(false),this.manuallyClosed||this.scheduleReconnect(r,"error"),r}return}await this.cleanupSocket(true);}let{protocols:e,connectTimeoutMs:s}=this.options,o=ouiderNetwork.OUIDNetwork.websocket(t,e,{connectTimeoutMs:s});this.socket=o,this.currentUrl=t,o.onOpen(()=>{this.socket!==o||this.manuallyClosed||(this.opened=true,this.reconnectAttempts=0,this.openHandlers.forEach(n=>n()));}),this.offMessage=await o.onMessage(n=>{var h;let r=typeof n=="string"?n:(h=JSON.stringify(n))!=null?h:"";this.msgHandlers.forEach(a=>a(r));}),o.onClose(n=>{this.socket!==o||this.manuallyClosed||(this.opened=false,this.closeHandlers.forEach(r=>r(n)),this.scheduleReconnect(n,"close"));}),o.onError(n=>{this.socket!==o||this.manuallyClosed||this.errorHandlers.forEach(r=>r(n));});try{await o.connect();}catch(n){throw this.socket===o&&await this.cleanupSocket(false),this.manuallyClosed||this.scheduleReconnect(n,"error"),n}}async send(t){var e;await((e=this.socket)==null?void 0:e.send(t));}async close(t,e){this.manuallyClosed=true,this.desiredUrl=null,this.reconnectAttempts=0,await this.clearReconnectTimer(),await this.removeAppStateListeners(),await this.cleanupSocket(true,t,e,true);}isConnected(){return this.opened}onMessage(t){this.msgHandlers.push(t);}onOpen(t){this.openHandlers.push(t);}offOpen(t){this.openHandlers=this.openHandlers.filter(e=>e!==t);}onClose(t){this.closeHandlers.push(t);}offClose(t){this.closeHandlers=this.closeHandlers.filter(e=>e!==t);}onError(t){this.errorHandlers.push(t);}offError(t){this.errorHandlers=this.errorHandlers.filter(e=>e!==t);}onReconnect(t){this.reconnectHandlers.push(t);}offReconnect(t){this.reconnectHandlers=this.reconnectHandlers.filter(e=>e!==t);}getReconnectOptions(){var s,o,n,r,h,a;let t=this.options.reconnect;if(t===false)return null;let e=t===true||t==null?{}:t;return e.enabled===false?null:{initialDelayMs:Math.max(0,(s=e.initialDelayMs)!=null?s:500),maxDelayMs:Math.max(0,(o=e.maxDelayMs)!=null?o:1e4),factor:Math.max(1,(n=e.factor)!=null?n:2),maxAttempts:(r=e.maxAttempts)!=null?r:Number.POSITIVE_INFINITY,jitterRatio:Math.max(0,Math.min(1,(h=e.jitterRatio)!=null?h:.2)),reconnectOnForeground:(a=e.reconnectOnForeground)!=null?a:true,shouldReconnect:e.shouldReconnect}}scheduleReconnect(t,e="close"){let s=this.getReconnectOptions(),o=this.desiredUrl;if(!s||!o||this.manuallyClosed||this.opened||this.reconnectTimerId!=null||this.reconnectTimerPending||s.shouldReconnect&&!s.shouldReconnect(t)||this.reconnectAttempts>=s.maxAttempts)return;let n=this.reconnectDelay(s,this.reconnectAttempts+1),r=++this.reconnectTimerToken,h=false;this.reconnectTimerPending=true,ouider.OUID.setTimeout(()=>{r===this.reconnectTimerToken&&(h=true,this.reconnectTimerId=null,this.reconnectTimerPending=false,this.reconnectNow(e,t));},n).then(a=>{if(h||r!==this.reconnectTimerToken||this.manuallyClosed||this.opened){this.reconnectTimerPending=false,ouider.OUID.clearTimeout(a);return}this.reconnectTimerId=a,this.reconnectTimerPending=false;}).catch(a=>{this.reconnectTimerPending=false,this.errorHandlers.forEach(l=>l(a));});}async reconnectNow(t,e,s=false){let o=this.getReconnectOptions(),n=this.desiredUrl;if(!o||!n||this.manuallyClosed||o.shouldReconnect&&!o.shouldReconnect(e)||this.opened&&!s)return;if(this.reconnecting)return this.reconnecting;if(this.reconnectAttempts>=o.maxAttempts)return;await this.clearReconnectTimer();let r=this.reconnectAttempts+1;this.reconnectAttempts=r;let h={attempt:r,url:n,reason:t,cause:e};this.reconnectHandlers.forEach(l=>l(h));let a=(async()=>{try{s&&this.socket&&await this.cleanupSocket(!0).catch(()=>{}),this.manuallyClosed||await this.openSocket(n);}catch(l){this.manuallyClosed||this.scheduleReconnect(l,"error");}})();this.reconnecting=a;try{await a;}finally{this.reconnecting===a&&(this.reconnecting=null);}}reconnectDelay(t,e){let s=Math.min(t.maxDelayMs,t.initialDelayMs*Math.pow(t.factor,Math.max(0,e-1)));if(s<=0||t.jitterRatio<=0)return Math.round(s);let o=s*t.jitterRatio;return Math.max(0,Math.round(s-o+Math.random()*o*2))}async clearReconnectTimer(){this.reconnectTimerToken++,this.reconnectTimerPending=false;let t=this.reconnectTimerId;this.reconnectTimerId=null,t!=null&&await ouider.OUID.clearTimeout(t).catch(()=>{});}ensureAppStateListeners(){let t=this.getReconnectOptions();t!=null&&t.reconnectOnForeground&&(this.ensureBackgroundListener(),this.ensureForegroundListener());}ensureBackgroundListener(){this.offBackground||this.backgroundListenerPending||(this.backgroundListenerPending=true,ouider.OUID.system.onBackground(()=>{this.appBackgrounded=true;}).then(t=>{var e;this.backgroundListenerPending=false,this.manuallyClosed||!((e=this.getReconnectOptions())!=null&&e.reconnectOnForeground)?t():this.offBackground=t;}).catch(()=>{this.backgroundListenerPending=false;}));}ensureForegroundListener(){this.offForeground||this.foregroundListenerPending||(this.foregroundListenerPending=true,ouider.OUID.system.onForeground(()=>{let t=this.appBackgrounded;this.appBackgrounded=false,!(this.manuallyClosed||!this.desiredUrl)&&this.reconnectNow("foreground",void 0,t);}).then(t=>{var e;this.foregroundListenerPending=false,this.manuallyClosed||!((e=this.getReconnectOptions())!=null&&e.reconnectOnForeground)?t():this.offForeground=t;}).catch(()=>{this.foregroundListenerPending=false;}));}async removeAppStateListeners(){let t=this.offForeground,e=this.offBackground;this.offForeground=null,this.offBackground=null,this.appBackgrounded=false,await Promise.all([t==null?void 0:t().catch(()=>{}),e==null?void 0:e().catch(()=>{})]);}async cleanupSocket(t,e,s,o=false){let n=this.socket;this.socket=null,this.currentUrl=null,o&&(this.desiredUrl=null),this.opened=false;let r=this.offMessage;this.offMessage=null,await(r==null?void 0:r().catch(()=>{})),t&&await(n==null?void 0:n.close(e,s));}};c(K,"WebSocketTransport");var gt=K,Q=class Q{constructor(t){i(this,"transport");i(this,"handlers",new Map);i(this,"rpcResolvers",new Map);i(this,"rpcCounter",0);i(this,"handleRawMessage",c(t=>{if(!t)return;let e;try{e=JSON.parse(t);}catch(n){return}let s=this.extractRpcId(e);if(s){let n=this.rpcResolvers.get(s);n&&(n.resolve(this.extractRpcValue(e)),this.rpcResolvers.delete(s));}let o=this.handlers.get(e.type);if(o)for(let n of o)n(e);},"handleRawMessage"));this.transport=t,this.transport.onMessage(this.handleRawMessage),this.transport.onClose(()=>this.rejectPendingRpc(new Error("Network connection closed"))),this.transport.onError(e=>{this.rejectPendingRpc(e instanceof Error?e:new Error("Network connection error"));});}async connect(t){await this.transport.connect(t);}isConnected(){return this.transport.isConnected()}onOpen(t){this.transport.onOpen(t);}offOpen(t){var e,s;(s=(e=this.transport).offOpen)==null||s.call(e,t);}onClose(t){this.transport.onClose(t);}offClose(t){var e,s;(s=(e=this.transport).offClose)==null||s.call(e,t);}onError(t){this.transport.onError(t);}offError(t){var e,s;(s=(e=this.transport).offError)==null||s.call(e,t);}onReconnect(t){var e,s;(s=(e=this.transport).onReconnect)==null||s.call(e,t);}offReconnect(t){var e,s;(s=(e=this.transport).offReconnect)==null||s.call(e,t);}send(t,e){let s={type:t,payload:e};return this.transport.send(JSON.stringify(s))}on(t,e){var o;let s=(o=this.handlers.get(t))!=null?o:[];s.push(e),this.handlers.set(t,s);}off(t,e){let s=this.handlers.get(t);s&&this.handlers.set(t,s.filter(o=>o!==e));}call(t,e){let s=`rpc_${++this.rpcCounter}_${Date.now()}`,o=e&&typeof e=="object"?nt(it({},e),{rpcId:s}):{value:e,rpcId:s},n={type:t,payload:o};return new Promise((r,h)=>{this.rpcResolvers.set(s,{resolve:r,reject:h}),this.transport.send(JSON.stringify(n)).catch(a=>{this.rpcResolvers.delete(s),h(a);});})}close(){return this.rejectPendingRpc(new Error("Network client closed")),this.transport.close()}extractRpcId(t){if(typeof t.rpcId=="string")return t.rpcId;if(t.payload&&typeof t.payload=="object"&&"rpcId"in t.payload){let e=t.payload.rpcId;return typeof e=="string"?e:void 0}}extractRpcValue(t){return "result"in t?t.result:t.payload}rejectPendingRpc(t){this.rpcResolvers.forEach(({reject:e})=>e(t)),this.rpcResolvers.clear();}};c(Q,"NetworkClient");var mt=Q,Z=class Z{constructor(t,e,s){i(this,"net");i(this,"channels");i(this,"state");i(this,"onChangeHandlers",[]);var o;this.net=t,this.channels=s,this.state=e,this.net.on((o=s==null?void 0:s.update)!=null?o:"state_update",n=>{let{payload:r}=n;r.full?this.state=r.full:r.patch&&(this.state=Object.assign({},this.state,r.patch)),this.emitChange();});}getState(){return this.state}onChange(t){this.onChangeHandlers.push(t);}emitChange(){for(let t of this.onChangeHandlers)t(this.state);}sendPatch(t){var e,s;return this.net.send((s=(e=this.channels)==null?void 0:e.patch)!=null?s:"state_patch",t)}};c(Z,"StateSyncClient");var wt=Z;exports.AssetManager=L;exports.AudioManager=H;exports.Camera2D=lt;exports.CollisionSystem=pt;exports.Game=B;exports.InputManager=O;exports.NetworkClient=mt;exports.PhysicsBody=ct;exports.Scene=ut;exports.SceneLayer=_;exports.SceneNode=k;exports.Sprite=ot;exports.SpriteAnimation=C;exports.SpriteLayer=rt;exports.SpriteNode=dt;exports.SpriteSheet=D;exports.StateSyncClient=wt;exports.TileRenderer=ht;exports.WebSocketTransport=gt;exports.aabbOverlap=ft;exports.createSpritePhysicsCollider=de;exports.getTileSourceRect=Ht;exports.objectSize=b;exports.tulon=Qt;exports.worldToTileX=x;exports.worldToTileY=v;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import {ODOM,OUID as OUID$1}from'@ouidesigner/ouider';import'@ouidesigner/ouider-device';import {OUIDNetwork}from'@ouidesigner/ouider-network';var st=Object.defineProperty,Pt=Object.defineProperties;var bt=Object.getOwnPropertyDescriptors;var tt=Object.getOwnPropertySymbols;var Dt=Object.prototype.hasOwnProperty,Xt=Object.prototype.propertyIsEnumerable;var E=(l,t,s)=>t in l?st(l,t,{enumerable:true,configurable:true,writable:true,value:s}):l[t]=s,et=(l,t)=>{for(var s in t||(t={}))Dt.call(t,s)&&E(l,s,t[s]);if(tt)for(var s of tt(t))Xt.call(t,s)&&E(l,s,t[s]);return l},it=(l,t)=>Pt(l,bt(t)),c=(l,t)=>st(l,"name",{value:t,configurable:true});var i=(l,t,s)=>E(l,typeof t!="symbol"?t+"":t,s);var S=class S{constructor(t,s,e,r,o=0){i(this,"image");i(this,"frameWidth");i(this,"frameHeight");i(this,"frameCount");i(this,"from");i(this,"columns");this.image=t,this.frameWidth=s,this.frameHeight=e,this.frameCount=r,this.from=o;let n=H(t);this.columns=Math.max(1,Math.floor(n.width/s));}};c(S,"SpriteSheet");var D=S,C=class C{constructor(t,s=10,e=true){i(this,"sheet");i(this,"fps");i(this,"loop");i(this,"time",0);this.sheet=t,this.fps=s,this.loop=e;}update(t){this.time+=t;let s=this.sheet.frameCount/this.fps;this.loop?this.time=this.time%s:this.time>s&&(this.time=s);}draw(t,s,e,r=1,o=1){let n=Math.floor(this.time*this.fps)+this.sheet.from,a=Math.min(n,this.sheet.frameCount+this.sheet.from-1),h=this.sheet.columns,d=a%h*this.sheet.frameWidth,p=Math.floor(a/h)*this.sheet.frameHeight,u=this.sheet.frameWidth*r,f=this.sheet.frameHeight*o;t.drawImage(this.sheet.image,d,p,this.sheet.frameWidth,this.sheet.frameHeight,s,e,u,f);}reset(){this.time=0;}};c(C,"SpriteAnimation");var X=C,R=class R{constructor(t){i(this,"animation");i(this,"width");i(this,"height");i(this,"visible");i(this,"tag");i(this,"data");var s,e,r,o;this.animation=t.animation,this.visible=(s=t.visible)!=null?s:true,this.tag=t.tag,this.data=(e=t.data)!=null?e:{},this.width=(r=t.width)!=null?r:this.animation.sheet.frameWidth,this.height=(o=t.height)!=null?o:this.animation.sheet.frameHeight;}update(t){this.animation.update(t);}draw(t,s=1,e=1){this.visible&&this.animation.draw(t,0,0,s,e);}};c(R,"Sprite");var ot=R,W=class W{constructor(){i(this,"sprites",[]);}add(t){return this.sprites.push(t),t}remove(t){let s=this.sprites.indexOf(t);s>=0&&this.sprites.splice(s,1);}clear(){this.sprites.length=0;}update(t){for(let s of this.sprites)s.update(t);}draw(t){for(let s of this.sprites)s.draw(t);}withTag(t){return this.sprites.filter(s=>s.tag===t)}};c(W,"SpriteLayer");var rt=W;var L=class L{constructor(){i(this,"imageDefs",new Map);i(this,"soundDefs",new Map);i(this,"sheetDefs",new Map);i(this,"images",new Map);i(this,"sounds",new Map);i(this,"sheets",new Map);}image(t,s){this.imageDefs.set(t,s);}sound(t,s){this.soundDefs.set(t,s);}spriteSheet(t,s){this.sheetDefs.set(t,s);}async loadAll(){var e,r;let t=[],s=[];for(let[o,n]of this.imageDefs.entries())t.push(this.loadImage(n).then(a=>{this.images.set(o,a);}));for(let[o,n]of this.soundDefs.entries())s.push(this.loadSound(n).then(a=>{this.sounds.set(o,a);}));await Promise.all([...t,...s]);for(let[o,n]of this.sheetDefs.entries()){let a=(e=this.getImage(n.image))!=null?e:this.images.get(n.image);if(!a)throw new Error(`SpriteSheet "${o}" missing image "${n.image}"`);let h=new D(a,n.frameWidth,n.frameHeight,n.frameCount,(r=n.from)!=null?r:0);this.sheets.set(o,h);}}async loadImage(t){let s=await ODOM.Image.new();if(!s)throw new Error(`Unable to create image for "${t}"`);let e=null,r=null,o=false,n=c(async()=>{let h=[e,r].filter(Boolean);e=null,r=null,await Promise.all(h.map(d=>s.dettachEventListener(d).catch(()=>{})));},"detach"),a=c(async()=>{let[h,d,p,u]=await Promise.all([s.naturalWidth(),s.naturalHeight(),s.width(),s.height()]);s.__local_size__={width:h||p||0,height:d||u||0};},"cacheSize");return new Promise(async(h,d)=>{let p=c(async()=>{if(!o)try{await a(),o=!0,await n(),h(s);}catch(f){await u(f);}},"done"),u=c(async f=>{o||(o=true,await n(),await s.release().catch(()=>{}),d(new Error(`Unable to load image "${t}"`)));},"fail");try{e=await s.addEventListener("load",()=>{p();},{options:{once:!0}}),r=await s.addEventListener("error",()=>{u();},{options:{once:!0}}),await s.setSrc(t);let[f,m]=await Promise.all([s.getProperty("complete").catch(()=>!1),s.naturalWidth().catch(()=>0)]);f&&m>0&&await p();}catch(f){await u(f);}})}async loadSound(t){return await OUID$1.media.audio.decode(t),t}getImage(t){return this.images.get(t)}getSpriteSheet(t){return this.sheets.get(t)}createAnimation(t,s,e){let r=this.getSpriteSheet(t);if(!r)throw new Error(`SpriteSheet "${t}" not found`);return new X(r,s!=null?s:10,e!=null?e:true)}getSound(t){return this.sounds.get(t)}clean(){this.images.forEach(t=>t.release()),this.images.clear(),this.sounds.clear(),this.sheets.clear();}};c(L,"AssetManager");var Y=L;var O=class O{constructor(t){i(this,"assets");this.assets=t;}clean(){OUID$1.media.audio.close();}async play(t,s){let e=this.assets.getSound(t);e&&await OUID$1.media.audio.play(e,s);}};c(O,"AudioManager");var T=O;var U=class U{constructor(t){i(this,"keysDown",new Set);i(this,"keysPressed",new Set);i(this,"buttonsPressed",new Set);i(this,"buttonsDown",new Set);i(this,"mouseX",-1);i(this,"mouseY",-1);i(this,"offsetX",-1);i(this,"offsetY",-1);i(this,"ids",[]);i(this,"disposed",false);this.track(OUID.addEventListener("window","keydown",s=>{this.keysDown.has(s.code)||this.keysPressed.add(s.code),this.keysDown.add(s.code);})),this.track(OUID.addEventListener("window","keyup",s=>{this.keysDown.delete(s.code);})),this.track(t.addEventListener("mousedown",s=>{let e=s.button===0?"MouseLeft":"MouseRight";this.buttonsDown.has(e)||this.buttonsPressed.add(e),this.buttonsDown.add(e),this.updateMousePosition(s);})),this.track(t.addEventListener("mouseup",s=>{let e=s.button===0?"MouseLeft":"MouseRight";this.buttonsDown.delete(e),this.updateMousePosition(s);})),this.track(t.addEventListener("mousemove",s=>{this.updateMousePosition(s);})),this.track(t.addEventListener("mouseleave",()=>{this.buttonsDown.clear();}));}update(){this.keysPressed.clear(),this.buttonsPressed.clear();}isDown(t){return this.keysDown.has(t)||this.buttonsDown.has(t)}isPressed(t){return this.keysPressed.has(t)||this.buttonsPressed.has(t)}clean(){this.disposed=true,this.ids.forEach(t=>{OUID.dettachEventListener(t);}),this.ids=[],this.keysDown.clear(),this.keysPressed.clear(),this.buttonsDown.clear(),this.buttonsPressed.clear();}track(t){t.then(s=>{this.disposed?OUID.dettachEventListener(s):this.ids.push(s);}).catch(()=>{});}updateMousePosition(t){var s,e,r,o;this.mouseX=(s=t.x)!=null?s:this.mouseX,this.mouseY=(e=t.y)!=null?e:this.mouseY,this.offsetX=(r=t.offsetX)!=null?r:this.offsetX,this.offsetY=(o=t.offsetY)!=null?o:this.offsetY;}};c(U,"InputManager");var z=U;var B=class B{constructor(t){i(this,"config");i(this,"canvas");i(this,"ctx2d");i(this,"assets");i(this,"audio");i(this,"input");i(this,"world",{});i(this,"running",false);i(this,"initialized",false);i(this,"lastTime",0);i(this,"frameId",null);i(this,"resizeListenerId",null);this.config=t,this.canvas=t.canvas,this.assets=new Y,this.audio=new T(this.assets),this.input=new z(this.canvas);}buildContext(t){return {dt:t,canvas:this.canvas,ctx2d:this.ctx2d,world:this.world,assets:this.assets,audio:this.audio,input:this.input}}async updateLocalSize(){var e,r;let t=(e=await this.canvas.width())!=null?e:0,s=(r=await this.canvas.height())!=null?r:0;this.canvas.__local_size__=this.ctx2d.__local_size__={width:t,height:s};}async syncSize(){var a,h;let[t,s,e,r]=await Promise.all([this.canvas.getProperty("clientWidth"),this.canvas.getProperty("clientHeight"),this.canvas.width(),this.canvas.height()]),o=(a=t!=null?t:e)!=null?a:0,n=(h=s!=null?s:r)!=null?h:0;await this.canvas.setProperty("width",o),await this.canvas.setProperty("height",n);}async init(){if(this.initialized)return;let t=await this.canvas.getContext("2d");if(!t)throw new Error("Unable to create a context");this.ctx2d=t,this.resizeListenerId=await this.canvas.addEventListener("resize",()=>{this.syncSize().then(()=>this.updateLocalSize());}),this.config.preload&&(this.config.preload(this.assets),await this.assets.loadAll()),await this.syncSize(),await this.updateLocalSize(),this.config.setup&&this.config.setup(this.buildContext(0)),this.initialized=true;}start(){this.running||(this.running=true,this.lastTime=performance.now(),this.scheduleFrame());}stop(){var t;!this.running&&!this.initialized||(this.running=false,this.frameId!=null&&(OUID.cancelAnimationFrame(this.frameId),this.frameId=null),this.resizeListenerId&&(OUID.dettachEventListener(this.resizeListenerId),this.resizeListenerId=null),this.audio.clean(),this.assets.clean(),this.input.clean(),(t=this.ctx2d)==null||t.release(),this.initialized=false);}scheduleFrame(){OUID.requestAnimationFrame(t=>{this.frameId=null,this.tick(t);}).then(t=>{this.running?this.frameId=t:OUID.cancelAnimationFrame(t);});}async tick(t){var r,o,n,a;if(!this.running)return;let e=Math.min((t-this.lastTime)/1e3,.1);this.lastTime=t;try{let h=this.buildContext(e);(o=(r=this.config).update)==null||o.call(r,h),this.input.update(),await((a=(n=this.config).draw)==null?void 0:a.call(n,h)),await this.ctx2d.commit();}catch(h){console.error("[Toubani] game loop error",h);}finally{this.running&&this.scheduleFrame();}}};c(B,"Game");var A=B;function Kt(l){return new A(l)}c(Kt,"tulon");function H(l){var t;return (t=l.__local_size__)!=null?t:{width:0,height:0}}c(H,"objectSize");function y(l,t){return Math.floor(l/t.tileWidth)}c(y,"worldToTileX");function v(l,t){return Math.floor(l/t.tileHeight)}c(v,"worldToTileY");function Tt(l,t){let{image:s,tileWidth:e,tileHeight:r,margin:o=0,spacing:n=0}=t,a=H(s),h=Math.max(1,Math.floor((a.width-2*o+n)/(e+n))),d=l-1,p=d%h,u=Math.floor(d/h),f=o+p*(e+n),m=o+u*(r+n);return {sx:f,sy:m,sw:e,sh:r}}c(Tt,"getTileSourceRect");var $=class ${constructor(t){i(this,"tileset");i(this,"sourceRectCache",new Map);this.tileset=t;}draw(t,s,e,r,o){let{tileWidth:n,tileHeight:a}=s,h=r*e.focusX,d=o*e.focusY,p=e.zoom,u=e.x-h/p,f=e.y-d/p,m=e.x+(r-h)/p,w=e.y+(o-d)/p,g=Math.max(0,Math.floor(u/n)),x=Math.min(s.width-1,Math.floor(m/n)),I=Math.max(0,Math.floor(f/a)),mt=Math.min(s.height-1,Math.floor(w/a));for(let P=I;P<=mt;P++)for(let b=g;b<=x;b++){let Z=s.getTile(b,P);if(Z<=0)continue;let{sx:xt,sy:yt,sw:vt,sh:Mt}=this.getSourceRect(Z),kt=b*n,It=P*a;t.drawImage(this.tileset.image,xt,yt,vt,Mt,kt,It,n,a);}}getSourceRect(t){let s=this.sourceRectCache.get(t);return s||(s=Tt(t,this.tileset),this.sourceRectCache.set(t,s)),s}};c($,"TileRenderer");var at=$;var j=class j{constructor(t,s){i(this,"node");i(this,"settings");i(this,"vx",0);i(this,"vy",0);i(this,"onGround",false);this.node=t,this.settings=s;}update(t){var f,m,w,g,x;let s=this.settings,e=(f=s.gravityX)!=null?f:0,r=(m=s.gravityY)!=null?m:1e3,o=(w=s.maxFallSpeed)!=null?w:2e3,n=(g=s.friction)!=null?g:.8,a=(x=s.airFriction)!=null?x:.98;this.vx+=e*t,this.vy+=r*t,this.vy>o&&(this.vy=o);let h=Math.max(1,Math.min(s.tileMap.tileWidth,s.tileMap.tileHeight)*.5),d=Math.max(Math.abs(this.vx*t),Math.abs(this.vy*t)),p=Math.max(1,Math.ceil(d/h)),u=t/p;this.onGround=false;for(let I=0;I<p;I++)this.moveAndCollideX(u),this.moveAndCollideY(u);this.onGround?this.vx*=n:this.vx*=a;}moveAndCollideX(t){let s=this.settings.tileMap,e=this.settings.width,r=this.settings.height,o=this.node.x+this.vx*t,n=this.node.y,a=Math.sign(this.vx);if(a===0){this.node.x=o;return}let h=o,d=o+e,p=n,u=n+r,f=Math.max(0,v(p,s)),m=Math.min(s.height-1,v(u-1,s));if(a>0){let w=y(d,s);for(let g=f;g<=m;g++)if(this.isSolid(w,g)){d=w*s.tileWidth,h=d-e,this.vx=0;break}}else {let w=y(h,s);for(let g=f;g<=m;g++)if(this.isSolid(w,g)){h=(w+1)*s.tileWidth,d=h+e,this.vx=0;break}}this.node.x=h;}moveAndCollideY(t){let s=this.settings.tileMap,e=this.settings.width,r=this.settings.height,o=this.node.x,n=this.node.y+this.vy*t,a=Math.sign(this.vy);if(a===0){this.node.y=n;return}let h=n,d=n+r,p=o,u=o+e,f=Math.max(0,y(p,s)),m=Math.min(s.width-1,y(u-1,s));if(a>0){let w=v(d,s);for(let g=f;g<=m;g++)if(this.isSolid(g,w)){d=w*s.tileHeight,h=d-r,this.vy=0,this.onGround=true;break}}else {let w=v(h,s);for(let g=f;g<=m;g++)if(this.isSolid(g,w)){h=(w+1)*s.tileHeight,d=h+r,this.vy=0;break}}this.node.y=h;}isSolid(t,s){let e=this.settings.tileMap;return t<0||s<0||t>=e.width||s>=e.height?false:e.isSolid(t,s)}};c(j,"PhysicsBody");var ht=j;var F=class F{constructor(){i(this,"x",0);i(this,"y",0);i(this,"zoom",1);i(this,"rotation",0);i(this,"followTarget",null);i(this,"followLerp",.1);i(this,"focusX",.5);i(this,"focusY",.5);}follow(t,s=.1){this.followTarget=t,this.followLerp=s;}unfollow(){this.followTarget=null;}update(t){if(!this.followTarget)return;let s=this.followTarget.x,e=this.followTarget.y;this.x+=(s-this.x)*this.followLerp,this.y+=(e-this.y)*this.followLerp;}begin(t,s,e){t.save();let r=s*this.focusX,o=e*this.focusY;t.translate(r,o),this.rotation!==0&&t.rotate(-this.rotation),this.zoom!==1&&t.scale(this.zoom,this.zoom),t.translate(-this.x,-this.y);}end(t){t.restore();}};c(F,"Camera2D");var ct=F;var J=class J{constructor(){i(this,"x",0);i(this,"y",0);i(this,"rotation",0);i(this,"scaleX",1);i(this,"scaleY",1);i(this,"visible",true);i(this,"parent",null);i(this,"children",[]);}add(t){return t.parent=this,this.children.push(t),t}remove(t){let s=this.children.indexOf(t);s>=0&&(this.children.splice(s,1),t.parent=null);}clear(){for(let t of this.children)t.parent=null;this.children.length=0;}update(t){}drawSelf(t){}draw(t){if(this.visible){t.save(),t.translate(this.x,this.y),this.rotation!==0&&t.rotate(this.rotation),(this.scaleX!==1||this.scaleY!==1)&&t.scale(this.scaleX,this.scaleY),this.drawSelf(t);for(let s of this.children)s.draw(t);t.restore();}}updateTree(t){this.update(t);for(let s of this.children)s.updateTree(t);}getWorldPosition(){let t=this.x,s=this.y,e=this.parent;for(;e;)t+=e.x,s+=e.y,e=e.parent;return {x:t,y:s}}};c(J,"SceneNode");var k=J,G=class G extends k{constructor(s){super();i(this,"sprite");this.sprite=s;}update(s){this.sprite.update(s);}drawSelf(s){this.sprite.draw(s);}getWorldBounds(){let s=this.getWorldPosition();return {x:s.x,y:s.y,width:this.sprite.width*this.scaleX,height:this.sprite.height*this.scaleY}}};c(G,"SpriteNode");var lt=G,N=class N extends k{constructor(s=1,e=1){super();i(this,"parallaxX",1);i(this,"parallaxY",1);this.parallaxX=s,this.parallaxY=e;}};c(N,"SceneLayer");var _=N,M=class M{constructor(){i(this,"id");i(this,"root",new k);i(this,"layers",[]);this.id=M.id++;}addLayer(t=1,s=1){let e=new _(t,s);return this.layers.push(e),this.root.add(e),e}update(t){this.root.updateTree(t);}draw(t){this.root.draw(t);}};c(M,"Scene"),i(M,"id",0);var dt=M;function ut(l,t){return !(l.x+l.width<=t.x||l.x>=t.x+t.width||l.y+l.height<=t.y||l.y>=t.y+t.height)}c(ut,"aabbOverlap");var V=class V{constructor(){i(this,"colliders",[]);i(this,"rules",[]);i(this,"rulesByPair",new Map);}add(t){this.colliders.push(t);}remove(t){let s=this.colliders.indexOf(t);s>=0&&this.colliders.splice(s,1);}clear(){this.colliders.length=0;}onCollision(t,s,e){var a;let r={tagA:t,tagB:s,handler:e};this.rules.push(r);let o=this.ruleKey(t,s),n=(a=this.rulesByPair.get(o))!=null?a:[];n.push(r),this.rulesByPair.set(o,n);}update(){var r;let t=new Map,s=new Map;for(let o of this.colliders){if(!o.isActive)continue;let n=(r=t.get(o.tag))!=null?r:[];n.push(o),t.set(o.tag,n);}let e=c(o=>{let n=s.get(o);return n||(n=o.getBounds(),s.set(o,n)),n},"getBounds");for(let[o,n]of this.rulesByPair.entries()){let[a,h]=o.split("\0"),d=t.get(a),p=t.get(h);if(!(!d||!p))if(a===h)for(let u=0;u<d.length;u++){let f=d[u],m=e(f);for(let w=u+1;w<d.length;w++){let g=d[w];ut(m,e(g))&&this.triggerRules(f,g,n);}}else for(let u of d){let f=e(u);for(let m of p)ut(f,e(m))&&this.triggerRules(u,m,n);}}}ruleKey(t,s){return t<=s?`${t}\0${s}`:`${s}\0${t}`}triggerRules(t,s,e){for(let r of e){let o=r.tagA===t.tag&&r.tagB===s.tag,n=r.tagA===s.tag&&r.tagB===t.tag;o?r.handler(t,s):n&&r.handler(s,t);}}};c(V,"CollisionSystem");var ft=V,zt=0;function ls(l,t,s,e){return {id:`col-${zt++}`,tag:l,isActive:true,data:{node:t,body:s,sprite:e},getBounds(){let o=t.getWorldPosition(),n=s.settings.width,a=s.settings.height;return {x:o.x,y:o.y,width:n,height:a}}}}c(ls,"createSpritePhysicsCollider");var q=class q{constructor(t={}){i(this,"options");i(this,"opened",false);i(this,"socket",null);i(this,"currentUrl",null);i(this,"offMessage",null);i(this,"msgHandlers",[]);i(this,"openHandlers",[]);i(this,"closeHandlers",[]);i(this,"errorHandlers",[]);this.options=t;}async connect(t){if(this.socket){if(this.currentUrl===t&&this.socket.isConnected())return;if(this.currentUrl===t&&!this.opened){await this.socket.connect();return}await this.cleanupSocket(true);}let{protocols:s,connectTimeoutMs:e}=this.options,r=OUIDNetwork.websocket(t,s,{connectTimeoutMs:e});this.socket=r,this.currentUrl=t,r.onOpen(()=>{this.opened=true,this.openHandlers.forEach(o=>o());}),this.offMessage=await r.onMessage(o=>{var a;let n=typeof o=="string"?o:(a=JSON.stringify(o))!=null?a:"";this.msgHandlers.forEach(h=>h(n));}),r.onClose(o=>{this.opened=false,this.closeHandlers.forEach(n=>n(o));}),r.onError(o=>{this.errorHandlers.forEach(n=>n(o));});try{await r.connect();}catch(o){throw this.socket===r&&await this.cleanupSocket(false),o}}async send(t){var s;await((s=this.socket)==null?void 0:s.send(t));}async close(t,s){await this.cleanupSocket(true,t,s);}isConnected(){return this.opened}onMessage(t){this.msgHandlers.push(t);}onOpen(t){this.openHandlers.push(t);}offOpen(t){this.openHandlers=this.openHandlers.filter(s=>s!==t);}onClose(t){this.closeHandlers.push(t);}offClose(t){this.closeHandlers=this.closeHandlers.filter(s=>s!==t);}onError(t){this.errorHandlers.push(t);}offError(t){this.errorHandlers=this.errorHandlers.filter(s=>s!==t);}async cleanupSocket(t,s,e){let r=this.socket;this.socket=null,this.currentUrl=null,this.opened=false;let o=this.offMessage;this.offMessage=null,await(o==null?void 0:o().catch(()=>{})),t&&await(r==null?void 0:r.close(s,e));}};c(q,"WebSocketTransport");var pt=q,K=class K{constructor(t){i(this,"transport");i(this,"handlers",new Map);i(this,"rpcResolvers",new Map);i(this,"rpcCounter",0);i(this,"handleRawMessage",c(t=>{if(!t)return;let s;try{s=JSON.parse(t);}catch(o){return}let e=this.extractRpcId(s);if(e){let o=this.rpcResolvers.get(e);o&&(o.resolve(this.extractRpcValue(s)),this.rpcResolvers.delete(e));}let r=this.handlers.get(s.type);if(r)for(let o of r)o(s);},"handleRawMessage"));this.transport=t,this.transport.onMessage(this.handleRawMessage),this.transport.onClose(()=>this.rejectPendingRpc(new Error("Network connection closed"))),this.transport.onError(s=>{this.rejectPendingRpc(s instanceof Error?s:new Error("Network connection error"));});}async connect(t){await this.transport.connect(t);}isConnected(){return this.transport.isConnected()}send(t,s){let e={type:t,payload:s};return this.transport.send(JSON.stringify(e))}on(t,s){var r;let e=(r=this.handlers.get(t))!=null?r:[];e.push(s),this.handlers.set(t,e);}off(t,s){let e=this.handlers.get(t);e&&this.handlers.set(t,e.filter(r=>r!==s));}call(t,s){let e=`rpc_${++this.rpcCounter}_${Date.now()}`,r=s&&typeof s=="object"?it(et({},s),{rpcId:e}):{value:s,rpcId:e},o={type:t,payload:r};return new Promise((n,a)=>{this.rpcResolvers.set(e,{resolve:n,reject:a}),this.transport.send(JSON.stringify(o)).catch(h=>{this.rpcResolvers.delete(e),a(h);});})}close(){return this.rejectPendingRpc(new Error("Network client closed")),this.transport.close()}extractRpcId(t){if(typeof t.rpcId=="string")return t.rpcId;if(t.payload&&typeof t.payload=="object"&&"rpcId"in t.payload){let s=t.payload.rpcId;return typeof s=="string"?s:void 0}}extractRpcValue(t){return "result"in t?t.result:t.payload}rejectPendingRpc(t){this.rpcResolvers.forEach(({reject:s})=>s(t)),this.rpcResolvers.clear();}};c(K,"NetworkClient");var gt=K,Q=class Q{constructor(t,s,e){i(this,"net");i(this,"channels");i(this,"state");i(this,"onChangeHandlers",[]);var r;this.net=t,this.channels=e,this.state=s,this.net.on((r=e==null?void 0:e.update)!=null?r:"state_update",o=>{let{payload:n}=o;n.full?this.state=n.full:n.patch&&(this.state=Object.assign({},this.state,n.patch)),this.emitChange();});}getState(){return this.state}onChange(t){this.onChangeHandlers.push(t);}emitChange(){for(let t of this.onChangeHandlers)t(this.state);}sendPatch(t){var s,e;return this.net.send((e=(s=this.channels)==null?void 0:s.patch)!=null?e:"state_patch",t)}};c(Q,"StateSyncClient");var wt=Q;export{Y as AssetManager,T as AudioManager,ct as Camera2D,ft as CollisionSystem,A as Game,z as InputManager,gt as NetworkClient,ht as PhysicsBody,dt as Scene,_ as SceneLayer,k as SceneNode,ot as Sprite,X as SpriteAnimation,rt as SpriteLayer,lt as SpriteNode,D as SpriteSheet,wt as StateSyncClient,at as TileRenderer,pt as WebSocketTransport,ut as aabbOverlap,ls as createSpritePhysicsCollider,Tt as getTileSourceRect,H as objectSize,Kt as tulon,y as worldToTileX,v as worldToTileY};
1
+ import {ODOM,OUID as OUID$1}from'@ouidesigner/ouider';import'@ouidesigner/ouider-device';import {OUIDNetwork}from'@ouidesigner/ouider-network';var st=Object.defineProperty,Rt=Object.defineProperties;var It=Object.getOwnPropertyDescriptors;var et=Object.getOwnPropertySymbols;var Dt=Object.prototype.hasOwnProperty,Ct=Object.prototype.propertyIsEnumerable;var S=(d,t,e)=>t in d?st(d,t,{enumerable:true,configurable:true,writable:true,value:e}):d[t]=e,it=(d,t)=>{for(var e in t||(t={}))Dt.call(t,e)&&S(d,e,t[e]);if(et)for(var e of et(t))Ct.call(t,e)&&S(d,e,t[e]);return d},nt=(d,t)=>Rt(d,It(t)),c=(d,t)=>st(d,"name",{value:t,configurable:true});var i=(d,t,e)=>S(d,typeof t!="symbol"?t+"":t,e);var E=class E{constructor(t,e,s,o,n=0){i(this,"image");i(this,"frameWidth");i(this,"frameHeight");i(this,"frameCount");i(this,"from");i(this,"columns");this.image=t,this.frameWidth=e,this.frameHeight=s,this.frameCount=o,this.from=n;let r=b(t);this.columns=Math.max(1,Math.floor(r.width/e));}};c(E,"SpriteSheet");var D=E,X=class X{constructor(t,e=10,s=true){i(this,"sheet");i(this,"fps");i(this,"loop");i(this,"time",0);this.sheet=t,this.fps=e,this.loop=s;}update(t){this.time+=t;let e=this.sheet.frameCount/this.fps;this.loop?this.time=this.time%e:this.time>e&&(this.time=e);}draw(t,e,s,o=1,n=1){let r=Math.floor(this.time*this.fps)+this.sheet.from,h=Math.min(r,this.sheet.frameCount+this.sheet.from-1),a=this.sheet.columns,l=h%a*this.sheet.frameWidth,p=Math.floor(h/a)*this.sheet.frameHeight,u=this.sheet.frameWidth*o,f=this.sheet.frameHeight*n;t.drawImage(this.sheet.image,l,p,this.sheet.frameWidth,this.sheet.frameHeight,e,s,u,f);}reset(){this.time=0;}};c(X,"SpriteAnimation");var C=X,Y=class Y{constructor(t){i(this,"animation");i(this,"width");i(this,"height");i(this,"visible");i(this,"tag");i(this,"data");var e,s,o,n;this.animation=t.animation,this.visible=(e=t.visible)!=null?e:true,this.tag=t.tag,this.data=(s=t.data)!=null?s:{},this.width=(o=t.width)!=null?o:this.animation.sheet.frameWidth,this.height=(n=t.height)!=null?n:this.animation.sheet.frameHeight;}update(t){this.animation.update(t);}draw(t,e=1,s=1){this.visible&&this.animation.draw(t,0,0,e,s);}};c(Y,"Sprite");var ot=Y,z=class z{constructor(){i(this,"sprites",[]);}add(t){return this.sprites.push(t),t}remove(t){let e=this.sprites.indexOf(t);e>=0&&this.sprites.splice(e,1);}clear(){this.sprites.length=0;}update(t){for(let e of this.sprites)e.update(t);}draw(t){for(let e of this.sprites)e.draw(t);}withTag(t){return this.sprites.filter(e=>e.tag===t)}};c(z,"SpriteLayer");var rt=z;var A=class A{constructor(){i(this,"imageDefs",new Map);i(this,"soundDefs",new Map);i(this,"sheetDefs",new Map);i(this,"images",new Map);i(this,"sounds",new Map);i(this,"sheets",new Map);}image(t,e){this.imageDefs.set(t,e);}sound(t,e){this.soundDefs.set(t,e);}spriteSheet(t,e){this.sheetDefs.set(t,e);}async loadAll(){var s,o;let t=[],e=[];for(let[n,r]of this.imageDefs.entries())t.push(this.loadImage(r).then(h=>{this.images.set(n,h);}));for(let[n,r]of this.soundDefs.entries())e.push(this.loadSound(r).then(h=>{this.sounds.set(n,h);}));await Promise.all([...t,...e]);for(let[n,r]of this.sheetDefs.entries()){let h=(s=this.getImage(r.image))!=null?s:this.images.get(r.image);if(!h)throw new Error(`SpriteSheet "${n}" missing image "${r.image}"`);let a=new D(h,r.frameWidth,r.frameHeight,r.frameCount,(o=r.from)!=null?o:0);this.sheets.set(n,a);}}async loadImage(t){let e=await ODOM.Image.new();if(!e)throw new Error(`Unable to create image for "${t}"`);let s=null,o=null,n=false,r=c(async()=>{let a=[s,o].filter(Boolean);s=null,o=null,await Promise.all(a.map(l=>e.dettachEventListener(l).catch(()=>{})));},"detach"),h=c(async()=>{let[a,l,p,u]=await Promise.all([e.naturalWidth(),e.naturalHeight(),e.width(),e.height()]);e.__local_size__={width:a||p||0,height:l||u||0};},"cacheSize");return new Promise(async(a,l)=>{let p=c(async()=>{if(!n)try{await h(),n=!0,await r(),a(e);}catch(f){await u(f);}},"done"),u=c(async f=>{n||(n=true,await r(),await e.release().catch(()=>{}),l(new Error(`Unable to load image "${t}"`)));},"fail");try{s=await e.addEventListener("load",()=>{p();},{options:{once:!0}}),o=await e.addEventListener("error",()=>{u();},{options:{once:!0}}),await e.setSrc(t);let[f,w]=await Promise.all([e.getProperty("complete").catch(()=>!1),e.naturalWidth().catch(()=>0)]);f&&w>0&&await p();}catch(f){await u(f);}})}async loadSound(t){return await OUID$1.media.audio.decode(t),t}getImage(t){return this.images.get(t)}getSpriteSheet(t){return this.sheets.get(t)}createAnimation(t,e,s){let o=this.getSpriteSheet(t);if(!o)throw new Error(`SpriteSheet "${t}" not found`);return new C(o,e!=null?e:10,s!=null?s:true)}getSound(t){return this.sounds.get(t)}clean(){this.images.forEach(t=>t.release()),this.images.clear(),this.sounds.clear(),this.sheets.clear();}};c(A,"AssetManager");var L=A;var U=class U{constructor(t){i(this,"assets");this.assets=t;}clean(){OUID$1.media.audio.close();}async play(t,e){let s=this.assets.getSound(t);s&&await OUID$1.media.audio.play(s,e);}};c(U,"AudioManager");var H=U;var W=class W{constructor(t){i(this,"keysDown",new Set);i(this,"keysPressed",new Set);i(this,"buttonsPressed",new Set);i(this,"buttonsDown",new Set);i(this,"mouseX",-1);i(this,"mouseY",-1);i(this,"offsetX",-1);i(this,"offsetY",-1);i(this,"ids",[]);i(this,"disposed",false);this.track(OUID.addEventListener("window","keydown",e=>{this.keysDown.has(e.code)||this.keysPressed.add(e.code),this.keysDown.add(e.code);})),this.track(OUID.addEventListener("window","keyup",e=>{this.keysDown.delete(e.code);})),this.track(t.addEventListener("mousedown",e=>{let s=e.button===0?"MouseLeft":"MouseRight";this.buttonsDown.has(s)||this.buttonsPressed.add(s),this.buttonsDown.add(s),this.updateMousePosition(e);})),this.track(t.addEventListener("mouseup",e=>{let s=e.button===0?"MouseLeft":"MouseRight";this.buttonsDown.delete(s),this.updateMousePosition(e);})),this.track(t.addEventListener("mousemove",e=>{this.updateMousePosition(e);})),this.track(t.addEventListener("mouseleave",()=>{this.buttonsDown.clear();}));}update(){this.keysPressed.clear(),this.buttonsPressed.clear();}isDown(t){return this.keysDown.has(t)||this.buttonsDown.has(t)}isPressed(t){return this.keysPressed.has(t)||this.buttonsPressed.has(t)}clean(){this.disposed=true,this.ids.forEach(t=>{OUID.dettachEventListener(t);}),this.ids=[],this.keysDown.clear(),this.keysPressed.clear(),this.buttonsDown.clear(),this.buttonsPressed.clear();}track(t){t.then(e=>{this.disposed?OUID.dettachEventListener(e):this.ids.push(e);}).catch(()=>{});}updateMousePosition(t){var e,s,o,n;this.mouseX=(e=t.x)!=null?e:this.mouseX,this.mouseY=(s=t.y)!=null?s:this.mouseY,this.offsetX=(o=t.offsetX)!=null?o:this.offsetX,this.offsetY=(n=t.offsetY)!=null?n:this.offsetY;}};c(W,"InputManager");var O=W;var F=class F{constructor(t){i(this,"config");i(this,"canvas");i(this,"ctx2d");i(this,"assets");i(this,"audio");i(this,"input");i(this,"world",{});i(this,"running",false);i(this,"initialized",false);i(this,"lastTime",0);i(this,"frameId",null);i(this,"resizeListenerId",null);this.config=t,this.canvas=t.canvas,this.assets=new L,this.audio=new H(this.assets),this.input=new O(this.canvas);}buildContext(t){return {dt:t,canvas:this.canvas,ctx2d:this.ctx2d,world:this.world,assets:this.assets,audio:this.audio,input:this.input}}async updateLocalSize(){var s,o;let t=(s=await this.canvas.width())!=null?s:0,e=(o=await this.canvas.height())!=null?o:0;this.canvas.__local_size__=this.ctx2d.__local_size__={width:t,height:e};}async syncSize(){var h,a;let[t,e,s,o]=await Promise.all([this.canvas.getProperty("clientWidth"),this.canvas.getProperty("clientHeight"),this.canvas.width(),this.canvas.height()]),n=(h=t!=null?t:s)!=null?h:0,r=(a=e!=null?e:o)!=null?a:0;await this.canvas.setProperty("width",n),await this.canvas.setProperty("height",r);}async init(){if(this.initialized)return;let t=await this.canvas.getContext("2d");if(!t)throw new Error("Unable to create a context");this.ctx2d=t,this.resizeListenerId=await this.canvas.addEventListener("resize",()=>{this.syncSize().then(()=>this.updateLocalSize());}),this.config.preload&&(this.config.preload(this.assets),await this.assets.loadAll()),await this.syncSize(),await this.updateLocalSize(),this.config.setup&&this.config.setup(this.buildContext(0)),this.initialized=true;}start(){this.running||(this.running=true,this.lastTime=performance.now(),this.scheduleFrame());}stop(){var t;!this.running&&!this.initialized||(this.running=false,this.frameId!=null&&(OUID.cancelAnimationFrame(this.frameId),this.frameId=null),this.resizeListenerId&&(OUID.dettachEventListener(this.resizeListenerId),this.resizeListenerId=null),this.audio.clean(),this.assets.clean(),this.input.clean(),(t=this.ctx2d)==null||t.release(),this.initialized=false);}scheduleFrame(){OUID.requestAnimationFrame(t=>{this.frameId=null,this.tick(t);}).then(t=>{this.running?this.frameId=t:OUID.cancelAnimationFrame(t);});}async tick(t){var o,n,r,h;if(!this.running)return;let s=Math.min((t-this.lastTime)/1e3,.1);this.lastTime=t;try{let a=this.buildContext(s);(n=(o=this.config).update)==null||n.call(o,a),this.input.update(),await((h=(r=this.config).draw)==null?void 0:h.call(r,a)),await this.ctx2d.commit();}catch(a){console.error("[Toubani] game loop error",a);}finally{this.running&&this.scheduleFrame();}}};c(F,"Game");var B=F;function Qt(d){return new B(d)}c(Qt,"tulon");function b(d){var t;return (t=d.__local_size__)!=null?t:{width:0,height:0}}c(b,"objectSize");function x(d,t){return Math.floor(d/t.tileWidth)}c(x,"worldToTileX");function v(d,t){return Math.floor(d/t.tileHeight)}c(v,"worldToTileY");function Ht(d,t){let{image:e,tileWidth:s,tileHeight:o,margin:n=0,spacing:r=0}=t,h=b(e),a=Math.max(1,Math.floor((h.width-2*n+r)/(s+r))),l=d-1,p=l%a,u=Math.floor(l/a),f=n+p*(s+r),w=n+u*(o+r);return {sx:f,sy:w,sw:s,sh:o}}c(Ht,"getTileSourceRect");var j=class j{constructor(t){i(this,"tileset");i(this,"sourceRectCache",new Map);this.tileset=t;}draw(t,e,s,o,n){let{tileWidth:r,tileHeight:h}=e,a=o*s.focusX,l=n*s.focusY,p=s.zoom,u=s.x-a/p,f=s.y-l/p,w=s.x+(o-a)/p,m=s.y+(n-l)/p,g=Math.max(0,Math.floor(u/r)),y=Math.min(e.width-1,Math.floor(w/r)),P=Math.max(0,Math.floor(f/h)),yt=Math.min(e.height-1,Math.floor(m/h));for(let R=P;R<=yt;R++)for(let I=g;I<=y;I++){let tt=e.getTile(I,R);if(tt<=0)continue;let{sx:xt,sy:vt,sw:Mt,sh:kt}=this.getSourceRect(tt),Tt=I*r,Pt=R*h;t.drawImage(this.tileset.image,xt,vt,Mt,kt,Tt,Pt,r,h);}}getSourceRect(t){let e=this.sourceRectCache.get(t);return e||(e=Ht(t,this.tileset),this.sourceRectCache.set(t,e)),e}};c(j,"TileRenderer");var ht=j;var $=class ${constructor(t,e){i(this,"node");i(this,"settings");i(this,"vx",0);i(this,"vy",0);i(this,"onGround",false);this.node=t,this.settings=e;}update(t){var f,w,m,g,y;let e=this.settings,s=(f=e.gravityX)!=null?f:0,o=(w=e.gravityY)!=null?w:1e3,n=(m=e.maxFallSpeed)!=null?m:2e3,r=(g=e.friction)!=null?g:.8,h=(y=e.airFriction)!=null?y:.98;this.vx+=s*t,this.vy+=o*t,this.vy>n&&(this.vy=n);let a=Math.max(1,Math.min(e.tileMap.tileWidth,e.tileMap.tileHeight)*.5),l=Math.max(Math.abs(this.vx*t),Math.abs(this.vy*t)),p=Math.max(1,Math.ceil(l/a)),u=t/p;this.onGround=false;for(let P=0;P<p;P++)this.moveAndCollideX(u),this.moveAndCollideY(u);this.onGround?this.vx*=r:this.vx*=h;}moveAndCollideX(t){let e=this.settings.tileMap,s=this.settings.width,o=this.settings.height,n=this.node.x+this.vx*t,r=this.node.y,h=Math.sign(this.vx);if(h===0){this.node.x=n;return}let a=n,l=n+s,p=r,u=r+o,f=Math.max(0,v(p,e)),w=Math.min(e.height-1,v(u-1,e));if(h>0){let m=x(l,e);for(let g=f;g<=w;g++)if(this.isSolid(m,g)){l=m*e.tileWidth,a=l-s,this.vx=0;break}}else {let m=x(a,e);for(let g=f;g<=w;g++)if(this.isSolid(m,g)){a=(m+1)*e.tileWidth,l=a+s,this.vx=0;break}}this.node.x=a;}moveAndCollideY(t){let e=this.settings.tileMap,s=this.settings.width,o=this.settings.height,n=this.node.x,r=this.node.y+this.vy*t,h=Math.sign(this.vy);if(h===0){this.node.y=r;return}let a=r,l=r+o,p=n,u=n+s,f=Math.max(0,x(p,e)),w=Math.min(e.width-1,x(u-1,e));if(h>0){let m=v(l,e);for(let g=f;g<=w;g++)if(this.isSolid(g,m)){l=m*e.tileHeight,a=l-o,this.vy=0,this.onGround=true;break}}else {let m=v(a,e);for(let g=f;g<=w;g++)if(this.isSolid(g,m)){a=(m+1)*e.tileHeight,l=a+o,this.vy=0;break}}this.node.y=a;}isSolid(t,e){let s=this.settings.tileMap;return t<0||e<0||t>=s.width||e>=s.height?false:s.isSolid(t,e)}};c($,"PhysicsBody");var ct=$;var N=class N{constructor(){i(this,"x",0);i(this,"y",0);i(this,"zoom",1);i(this,"rotation",0);i(this,"followTarget",null);i(this,"followLerp",.1);i(this,"focusX",.5);i(this,"focusY",.5);}follow(t,e=.1){this.followTarget=t,this.followLerp=e;}unfollow(){this.followTarget=null;}update(t){if(!this.followTarget)return;let e=this.followTarget.x,s=this.followTarget.y;this.x+=(e-this.x)*this.followLerp,this.y+=(s-this.y)*this.followLerp;}begin(t,e,s){t.save();let o=e*this.focusX,n=s*this.focusY;t.translate(o,n),this.rotation!==0&&t.rotate(-this.rotation),this.zoom!==1&&t.scale(this.zoom,this.zoom),t.translate(-this.x,-this.y);}end(t){t.restore();}};c(N,"Camera2D");var lt=N;var J=class J{constructor(){i(this,"x",0);i(this,"y",0);i(this,"rotation",0);i(this,"scaleX",1);i(this,"scaleY",1);i(this,"visible",true);i(this,"parent",null);i(this,"children",[]);}add(t){return t.parent=this,this.children.push(t),t}remove(t){let e=this.children.indexOf(t);e>=0&&(this.children.splice(e,1),t.parent=null);}clear(){for(let t of this.children)t.parent=null;this.children.length=0;}update(t){}drawSelf(t){}draw(t){if(this.visible){t.save(),t.translate(this.x,this.y),this.rotation!==0&&t.rotate(this.rotation),(this.scaleX!==1||this.scaleY!==1)&&t.scale(this.scaleX,this.scaleY),this.drawSelf(t);for(let e of this.children)e.draw(t);t.restore();}}updateTree(t){this.update(t);for(let e of this.children)e.updateTree(t);}getWorldPosition(){let t=this.x,e=this.y,s=this.parent;for(;s;)t+=s.x,e+=s.y,s=s.parent;return {x:t,y:e}}};c(J,"SceneNode");var k=J,V=class V extends k{constructor(e){super();i(this,"sprite");this.sprite=e;}update(e){this.sprite.update(e);}drawSelf(e){this.sprite.draw(e);}getWorldBounds(){let e=this.getWorldPosition();return {x:e.x,y:e.y,width:this.sprite.width*this.scaleX,height:this.sprite.height*this.scaleY}}};c(V,"SpriteNode");var dt=V,G=class G extends k{constructor(e=1,s=1){super();i(this,"parallaxX",1);i(this,"parallaxY",1);this.parallaxX=e,this.parallaxY=s;}};c(G,"SceneLayer");var _=G,M=class M{constructor(){i(this,"id");i(this,"root",new k);i(this,"layers",[]);this.id=M.id++;}addLayer(t=1,e=1){let s=new _(t,e);return this.layers.push(s),this.root.add(s),s}update(t){this.root.updateTree(t);}draw(t){this.root.draw(t);}};c(M,"Scene"),i(M,"id",0);var ut=M;function ft(d,t){return !(d.x+d.width<=t.x||d.x>=t.x+t.width||d.y+d.height<=t.y||d.y>=t.y+t.height)}c(ft,"aabbOverlap");var q=class q{constructor(){i(this,"colliders",[]);i(this,"rules",[]);i(this,"rulesByPair",new Map);}add(t){this.colliders.push(t);}remove(t){let e=this.colliders.indexOf(t);e>=0&&this.colliders.splice(e,1);}clear(){this.colliders.length=0;}onCollision(t,e,s){var h;let o={tagA:t,tagB:e,handler:s};this.rules.push(o);let n=this.ruleKey(t,e),r=(h=this.rulesByPair.get(n))!=null?h:[];r.push(o),this.rulesByPair.set(n,r);}update(){var o;let t=new Map,e=new Map;for(let n of this.colliders){if(!n.isActive)continue;let r=(o=t.get(n.tag))!=null?o:[];r.push(n),t.set(n.tag,r);}let s=c(n=>{let r=e.get(n);return r||(r=n.getBounds(),e.set(n,r)),r},"getBounds");for(let[n,r]of this.rulesByPair.entries()){let[h,a]=n.split("\0"),l=t.get(h),p=t.get(a);if(!(!l||!p))if(h===a)for(let u=0;u<l.length;u++){let f=l[u],w=s(f);for(let m=u+1;m<l.length;m++){let g=l[m];ft(w,s(g))&&this.triggerRules(f,g,r);}}else for(let u of l){let f=s(u);for(let w of p)ft(f,s(w))&&this.triggerRules(u,w,r);}}}ruleKey(t,e){return t<=e?`${t}\0${e}`:`${e}\0${t}`}triggerRules(t,e,s){for(let o of s){let n=o.tagA===t.tag&&o.tagB===e.tag,r=o.tagA===e.tag&&o.tagB===t.tag;n?o.handler(t,e):r&&o.handler(e,t);}}};c(q,"CollisionSystem");var pt=q,Ot=0;function de(d,t,e,s){return {id:`col-${Ot++}`,tag:d,isActive:true,data:{node:t,body:e,sprite:s},getBounds(){let n=t.getWorldPosition(),r=e.settings.width,h=e.settings.height;return {x:n.x,y:n.y,width:r,height:h}}}}c(de,"createSpritePhysicsCollider");var K=class K{constructor(t={}){i(this,"options");i(this,"opened",false);i(this,"socket",null);i(this,"currentUrl",null);i(this,"desiredUrl",null);i(this,"offMessage",null);i(this,"msgHandlers",[]);i(this,"openHandlers",[]);i(this,"closeHandlers",[]);i(this,"errorHandlers",[]);i(this,"reconnectHandlers",[]);i(this,"reconnectAttempts",0);i(this,"reconnectTimerId",null);i(this,"reconnectTimerPending",false);i(this,"reconnectTimerToken",0);i(this,"reconnecting",null);i(this,"manuallyClosed",true);i(this,"appBackgrounded",false);i(this,"offForeground",null);i(this,"offBackground",null);i(this,"foregroundListenerPending",false);i(this,"backgroundListenerPending",false);this.options=t;}async connect(t){this.desiredUrl=t,this.manuallyClosed=false,this.reconnectAttempts=0,await this.clearReconnectTimer(),this.ensureAppStateListeners(),await this.openSocket(t);}async openSocket(t){if(this.socket){if(this.currentUrl===t&&this.socket.isConnected())return;if(this.currentUrl===t&&!this.opened){let n=this.socket;try{await n.connect();}catch(r){throw this.socket===n&&await this.cleanupSocket(false),this.manuallyClosed||this.scheduleReconnect(r,"error"),r}return}await this.cleanupSocket(true);}let{protocols:e,connectTimeoutMs:s}=this.options,o=OUIDNetwork.websocket(t,e,{connectTimeoutMs:s});this.socket=o,this.currentUrl=t,o.onOpen(()=>{this.socket!==o||this.manuallyClosed||(this.opened=true,this.reconnectAttempts=0,this.openHandlers.forEach(n=>n()));}),this.offMessage=await o.onMessage(n=>{var h;let r=typeof n=="string"?n:(h=JSON.stringify(n))!=null?h:"";this.msgHandlers.forEach(a=>a(r));}),o.onClose(n=>{this.socket!==o||this.manuallyClosed||(this.opened=false,this.closeHandlers.forEach(r=>r(n)),this.scheduleReconnect(n,"close"));}),o.onError(n=>{this.socket!==o||this.manuallyClosed||this.errorHandlers.forEach(r=>r(n));});try{await o.connect();}catch(n){throw this.socket===o&&await this.cleanupSocket(false),this.manuallyClosed||this.scheduleReconnect(n,"error"),n}}async send(t){var e;await((e=this.socket)==null?void 0:e.send(t));}async close(t,e){this.manuallyClosed=true,this.desiredUrl=null,this.reconnectAttempts=0,await this.clearReconnectTimer(),await this.removeAppStateListeners(),await this.cleanupSocket(true,t,e,true);}isConnected(){return this.opened}onMessage(t){this.msgHandlers.push(t);}onOpen(t){this.openHandlers.push(t);}offOpen(t){this.openHandlers=this.openHandlers.filter(e=>e!==t);}onClose(t){this.closeHandlers.push(t);}offClose(t){this.closeHandlers=this.closeHandlers.filter(e=>e!==t);}onError(t){this.errorHandlers.push(t);}offError(t){this.errorHandlers=this.errorHandlers.filter(e=>e!==t);}onReconnect(t){this.reconnectHandlers.push(t);}offReconnect(t){this.reconnectHandlers=this.reconnectHandlers.filter(e=>e!==t);}getReconnectOptions(){var s,o,n,r,h,a;let t=this.options.reconnect;if(t===false)return null;let e=t===true||t==null?{}:t;return e.enabled===false?null:{initialDelayMs:Math.max(0,(s=e.initialDelayMs)!=null?s:500),maxDelayMs:Math.max(0,(o=e.maxDelayMs)!=null?o:1e4),factor:Math.max(1,(n=e.factor)!=null?n:2),maxAttempts:(r=e.maxAttempts)!=null?r:Number.POSITIVE_INFINITY,jitterRatio:Math.max(0,Math.min(1,(h=e.jitterRatio)!=null?h:.2)),reconnectOnForeground:(a=e.reconnectOnForeground)!=null?a:true,shouldReconnect:e.shouldReconnect}}scheduleReconnect(t,e="close"){let s=this.getReconnectOptions(),o=this.desiredUrl;if(!s||!o||this.manuallyClosed||this.opened||this.reconnectTimerId!=null||this.reconnectTimerPending||s.shouldReconnect&&!s.shouldReconnect(t)||this.reconnectAttempts>=s.maxAttempts)return;let n=this.reconnectDelay(s,this.reconnectAttempts+1),r=++this.reconnectTimerToken,h=false;this.reconnectTimerPending=true,OUID$1.setTimeout(()=>{r===this.reconnectTimerToken&&(h=true,this.reconnectTimerId=null,this.reconnectTimerPending=false,this.reconnectNow(e,t));},n).then(a=>{if(h||r!==this.reconnectTimerToken||this.manuallyClosed||this.opened){this.reconnectTimerPending=false,OUID$1.clearTimeout(a);return}this.reconnectTimerId=a,this.reconnectTimerPending=false;}).catch(a=>{this.reconnectTimerPending=false,this.errorHandlers.forEach(l=>l(a));});}async reconnectNow(t,e,s=false){let o=this.getReconnectOptions(),n=this.desiredUrl;if(!o||!n||this.manuallyClosed||o.shouldReconnect&&!o.shouldReconnect(e)||this.opened&&!s)return;if(this.reconnecting)return this.reconnecting;if(this.reconnectAttempts>=o.maxAttempts)return;await this.clearReconnectTimer();let r=this.reconnectAttempts+1;this.reconnectAttempts=r;let h={attempt:r,url:n,reason:t,cause:e};this.reconnectHandlers.forEach(l=>l(h));let a=(async()=>{try{s&&this.socket&&await this.cleanupSocket(!0).catch(()=>{}),this.manuallyClosed||await this.openSocket(n);}catch(l){this.manuallyClosed||this.scheduleReconnect(l,"error");}})();this.reconnecting=a;try{await a;}finally{this.reconnecting===a&&(this.reconnecting=null);}}reconnectDelay(t,e){let s=Math.min(t.maxDelayMs,t.initialDelayMs*Math.pow(t.factor,Math.max(0,e-1)));if(s<=0||t.jitterRatio<=0)return Math.round(s);let o=s*t.jitterRatio;return Math.max(0,Math.round(s-o+Math.random()*o*2))}async clearReconnectTimer(){this.reconnectTimerToken++,this.reconnectTimerPending=false;let t=this.reconnectTimerId;this.reconnectTimerId=null,t!=null&&await OUID$1.clearTimeout(t).catch(()=>{});}ensureAppStateListeners(){let t=this.getReconnectOptions();t!=null&&t.reconnectOnForeground&&(this.ensureBackgroundListener(),this.ensureForegroundListener());}ensureBackgroundListener(){this.offBackground||this.backgroundListenerPending||(this.backgroundListenerPending=true,OUID$1.system.onBackground(()=>{this.appBackgrounded=true;}).then(t=>{var e;this.backgroundListenerPending=false,this.manuallyClosed||!((e=this.getReconnectOptions())!=null&&e.reconnectOnForeground)?t():this.offBackground=t;}).catch(()=>{this.backgroundListenerPending=false;}));}ensureForegroundListener(){this.offForeground||this.foregroundListenerPending||(this.foregroundListenerPending=true,OUID$1.system.onForeground(()=>{let t=this.appBackgrounded;this.appBackgrounded=false,!(this.manuallyClosed||!this.desiredUrl)&&this.reconnectNow("foreground",void 0,t);}).then(t=>{var e;this.foregroundListenerPending=false,this.manuallyClosed||!((e=this.getReconnectOptions())!=null&&e.reconnectOnForeground)?t():this.offForeground=t;}).catch(()=>{this.foregroundListenerPending=false;}));}async removeAppStateListeners(){let t=this.offForeground,e=this.offBackground;this.offForeground=null,this.offBackground=null,this.appBackgrounded=false,await Promise.all([t==null?void 0:t().catch(()=>{}),e==null?void 0:e().catch(()=>{})]);}async cleanupSocket(t,e,s,o=false){let n=this.socket;this.socket=null,this.currentUrl=null,o&&(this.desiredUrl=null),this.opened=false;let r=this.offMessage;this.offMessage=null,await(r==null?void 0:r().catch(()=>{})),t&&await(n==null?void 0:n.close(e,s));}};c(K,"WebSocketTransport");var gt=K,Q=class Q{constructor(t){i(this,"transport");i(this,"handlers",new Map);i(this,"rpcResolvers",new Map);i(this,"rpcCounter",0);i(this,"handleRawMessage",c(t=>{if(!t)return;let e;try{e=JSON.parse(t);}catch(n){return}let s=this.extractRpcId(e);if(s){let n=this.rpcResolvers.get(s);n&&(n.resolve(this.extractRpcValue(e)),this.rpcResolvers.delete(s));}let o=this.handlers.get(e.type);if(o)for(let n of o)n(e);},"handleRawMessage"));this.transport=t,this.transport.onMessage(this.handleRawMessage),this.transport.onClose(()=>this.rejectPendingRpc(new Error("Network connection closed"))),this.transport.onError(e=>{this.rejectPendingRpc(e instanceof Error?e:new Error("Network connection error"));});}async connect(t){await this.transport.connect(t);}isConnected(){return this.transport.isConnected()}onOpen(t){this.transport.onOpen(t);}offOpen(t){var e,s;(s=(e=this.transport).offOpen)==null||s.call(e,t);}onClose(t){this.transport.onClose(t);}offClose(t){var e,s;(s=(e=this.transport).offClose)==null||s.call(e,t);}onError(t){this.transport.onError(t);}offError(t){var e,s;(s=(e=this.transport).offError)==null||s.call(e,t);}onReconnect(t){var e,s;(s=(e=this.transport).onReconnect)==null||s.call(e,t);}offReconnect(t){var e,s;(s=(e=this.transport).offReconnect)==null||s.call(e,t);}send(t,e){let s={type:t,payload:e};return this.transport.send(JSON.stringify(s))}on(t,e){var o;let s=(o=this.handlers.get(t))!=null?o:[];s.push(e),this.handlers.set(t,s);}off(t,e){let s=this.handlers.get(t);s&&this.handlers.set(t,s.filter(o=>o!==e));}call(t,e){let s=`rpc_${++this.rpcCounter}_${Date.now()}`,o=e&&typeof e=="object"?nt(it({},e),{rpcId:s}):{value:e,rpcId:s},n={type:t,payload:o};return new Promise((r,h)=>{this.rpcResolvers.set(s,{resolve:r,reject:h}),this.transport.send(JSON.stringify(n)).catch(a=>{this.rpcResolvers.delete(s),h(a);});})}close(){return this.rejectPendingRpc(new Error("Network client closed")),this.transport.close()}extractRpcId(t){if(typeof t.rpcId=="string")return t.rpcId;if(t.payload&&typeof t.payload=="object"&&"rpcId"in t.payload){let e=t.payload.rpcId;return typeof e=="string"?e:void 0}}extractRpcValue(t){return "result"in t?t.result:t.payload}rejectPendingRpc(t){this.rpcResolvers.forEach(({reject:e})=>e(t)),this.rpcResolvers.clear();}};c(Q,"NetworkClient");var mt=Q,Z=class Z{constructor(t,e,s){i(this,"net");i(this,"channels");i(this,"state");i(this,"onChangeHandlers",[]);var o;this.net=t,this.channels=s,this.state=e,this.net.on((o=s==null?void 0:s.update)!=null?o:"state_update",n=>{let{payload:r}=n;r.full?this.state=r.full:r.patch&&(this.state=Object.assign({},this.state,r.patch)),this.emitChange();});}getState(){return this.state}onChange(t){this.onChangeHandlers.push(t);}emitChange(){for(let t of this.onChangeHandlers)t(this.state);}sendPatch(t){var e,s;return this.net.send((s=(e=this.channels)==null?void 0:e.patch)!=null?s:"state_patch",t)}};c(Z,"StateSyncClient");var wt=Z;export{L as AssetManager,H as AudioManager,lt as Camera2D,pt as CollisionSystem,B as Game,O as InputManager,mt as NetworkClient,ct as PhysicsBody,ut as Scene,_ as SceneLayer,k as SceneNode,ot as Sprite,C as SpriteAnimation,rt as SpriteLayer,dt as SpriteNode,D as SpriteSheet,wt as StateSyncClient,ht as TileRenderer,gt as WebSocketTransport,ft as aabbOverlap,de as createSpritePhysicsCollider,Ht as getTileSourceRect,b as objectSize,Qt as tulon,x as worldToTileX,v as worldToTileY};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouidesigner/toubani",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {