@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 +26 -0
- package/dist/index.d.mts +56 -1
- package/dist/index.d.ts +56 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
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};
|