@ouidesigner/toubani 0.0.7 → 0.0.9

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/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { ODOM } from 'ouider';
1
+ import { ODOM } from '@ouidesigner/ouider';
2
2
 
3
3
  interface SpriteSheetConfig {
4
4
  image: string;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ODOM } from 'ouider';
1
+ import { ODOM } from '@ouidesigner/ouider';
2
2
 
3
3
  interface SpriteSheetConfig {
4
4
  image: string;
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- 'use strict';var ouider=require('ouider');var et=Object.defineProperty,bt=Object.defineProperties;var Dt=Object.getOwnPropertyDescriptors;var tt=Object.getOwnPropertySymbols;var Ct=Object.prototype.hasOwnProperty,Et=Object.prototype.propertyIsEnumerable;var Y=(d,t,e)=>t in d?et(d,t,{enumerable:true,configurable:true,writable:true,value:e}):d[t]=e,st=(d,t)=>{for(var e in t||(t={}))Ct.call(t,e)&&Y(d,e,t[e]);if(tt)for(var e of tt(t))Et.call(t,e)&&Y(d,e,t[e]);return d},it=(d,t)=>bt(d,Dt(t)),c=(d,t)=>et(d,"name",{value:t,configurable:true});var i=(d,t,e)=>Y(d,typeof t!="symbol"?t+"":t,e);var z=class z{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=I(t);this.columns=Math.max(1,Math.floor(r.width/e));}};c(z,"SpriteSheet");var E=z,S=class S{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,a=Math.min(r,this.sheet.frameCount+this.sheet.from-1),h=this.sheet.columns,l=a%h*this.sheet.frameWidth,p=Math.floor(a/h)*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(S,"SpriteAnimation");var O=S,L=class L{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(L,"Sprite");var ot=L,W=class W{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(W,"SpriteLayer");var nt=W;var B=class B{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(a=>{this.images.set(n,a);}));for(let[n,r]of this.soundDefs.entries())e.push(this.loadSound(r).then(a=>{this.sounds.set(n,a);}));await Promise.all([...t,...e]);for(let[n,r]of this.sheetDefs.entries()){let a=(s=this.getImage(r.image))!=null?s:this.images.get(r.image);if(!a)throw new Error(`SpriteSheet "${n}" missing image "${r.image}"`);let h=new E(a,r.frameWidth,r.frameHeight,r.frameCount,(o=r.from)!=null?o:0);this.sheets.set(n,h);}}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 h=[s,o].filter(Boolean);s=null,o=null,await Promise.all(h.map(l=>e.dettachEventListener(l).catch(()=>{})));},"detach"),a=c(async()=>{let[h,l,p,u]=await Promise.all([e.naturalWidth(),e.naturalHeight(),e.width(),e.height()]);e.__local_size__={width:h||p||0,height:l||u||0};},"cacheSize");return new Promise(async(h,l)=>{let p=c(async()=>{if(!n)try{await a(),n=!0,await r(),h(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,m]=await Promise.all([e.getProperty("complete").catch(()=>!1),e.naturalWidth().catch(()=>0)]);f&&m>0&&await p();}catch(f){await u(f);}})}async loadSound(t){return await OUID.fetch(t,{method:"GET",headers:{},body:null,credentials:"omit"},"buffer")}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 O(o,e!=null?e:10,s!=null?s:true)}getSound(t){return this.sounds.get(t)}clean(){this.images.forEach(t=>t.release()),this.sounds.forEach(t=>t.release()),this.images.clear(),this.sounds.clear(),this.sheets.clear();}};c(B,"AssetManager");var H=B;var A=class A{constructor(t){i(this,"assets");i(this,"audioContext",null);i(this,"audioDestination",null);i(this,"decodedBuffers",new Map);this.assets=t;}async getContext(){if(this.audioContext)return this.audioContext;let t=await OUID.newObject("AudioContext");return t?(this.audioContext=ouider.ODOM.OObject.toInstance(t),this.audioContext):null}async getDestination(t){if(this.audioDestination)return this.audioDestination;let e=await t.getPropertyAndHold("destination");return e?(this.audioDestination=e,this.audioDestination):null}async getBuffer(t){if(this.decodedBuffers.has(t))return this.decodedBuffers.get(t);let e=this.assets.getSound(t);if(!e)return null;let s=await this.getContext();if(!s)return null;let o=await s.invokeAndHold("decodeAudioData",e.ref);return o&&this.decodedBuffers.set(t,o),o}clean(){this.decodedBuffers.forEach(t=>t.release()),this.decodedBuffers.clear(),this.audioDestination&&(this.audioDestination.release(),this.audioDestination=null),this.audioContext&&(this.audioContext.invoke("close"),this.audioContext.release(),this.audioContext=null);}async play(t,e){let s=await this.getContext();if(!s)return;let o=await this.getBuffer(t);if(!o)return;await s.invoke("resume");let n=await s.invokeAndHold("createBufferSource");if(!n)return;await n.setProperty("buffer",o.ref),(e==null?void 0:e.loop)!=null&&await n.setProperty("loop",e.loop);let r=await this.getDestination(s);if(!r){n.release();return}if((e==null?void 0:e.volume)!=null){let a=await s.invokeAndHold("createGain");if(a){let h=await a.getPropertyAndHold("gain");h&&(await h.setProperty("value",e.volume),h.release()),await n.invoke("connect",a.ref),await a.invoke("connect",r.ref),await n.invoke("start",0),a.release(),n.release();return}}await n.invoke("connect",r.ref),await n.invoke("start",0),n.release();}};c(A,"AudioManager");var X=A;var R=class R{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(R,"InputManager");var T=R;var $=class ${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 H,this.audio=new X(this.assets),this.input=new T(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 a,h;let[t,e,s,o]=await Promise.all([this.canvas.getProperty("clientWidth"),this.canvas.getProperty("clientHeight"),this.canvas.width(),this.canvas.height()]),n=(a=t!=null?t:s)!=null?a:0,r=(h=e!=null?e:o)!=null?h: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,a;if(!this.running)return;let s=Math.min((t-this.lastTime)/1e3,.1);this.lastTime=t;try{let h=this.buildContext(s);(n=(o=this.config).update)==null||n.call(o,h),this.input.update(),await((a=(r=this.config).draw)==null?void 0:a.call(r,h)),await this.ctx2d.commit();}catch(h){console.error("[Toubani] game loop error",h);}finally{this.running&&this.scheduleFrame();}}};c($,"Game");var U=$;function Kt(d){return new U(d)}c(Kt,"tulon");function I(d){var t;return (t=d.__local_size__)!=null?t:{width:0,height:0}}c(I,"objectSize");function v(d,t){return Math.floor(d/t.tileWidth)}c(v,"worldToTileX");function M(d,t){return Math.floor(d/t.tileHeight)}c(M,"worldToTileY");function Ht(d,t){let{image:e,tileWidth:s,tileHeight:o,margin:n=0,spacing:r=0}=t,a=I(e),h=Math.max(1,Math.floor((a.width-2*n+r)/(s+r))),l=d-1,p=l%h,u=Math.floor(l/h),f=n+p*(s+r),m=n+u*(o+r);return {sx:f,sy:m,sw:s,sh:o}}c(Ht,"getTileSourceRect");var N=class N{constructor(t){i(this,"tileset");i(this,"sourceRectCache",new Map);this.tileset=t;}draw(t,e,s,o,n){let{tileWidth:r,tileHeight:a}=e,h=o*s.focusX,l=n*s.focusY,p=s.zoom,u=s.x-h/p,f=s.y-l/p,m=s.x+(o-h)/p,w=s.y+(n-l)/p,g=Math.max(0,Math.floor(u/r)),y=Math.min(e.width-1,Math.floor(m/r)),b=Math.max(0,Math.floor(f/a)),mt=Math.min(e.height-1,Math.floor(w/a));for(let D=b;D<=mt;D++)for(let C=g;C<=y;C++){let _=e.getTile(C,D);if(_<=0)continue;let{sx:xt,sy:yt,sw:vt,sh:Mt}=this.getSourceRect(_),kt=C*r,Pt=D*a;t.drawImage(this.tileset.image,xt,yt,vt,Mt,kt,Pt,r,a);}}getSourceRect(t){let e=this.sourceRectCache.get(t);return e||(e=Ht(t,this.tileset),this.sourceRectCache.set(t,e)),e}};c(N,"TileRenderer");var rt=N;var j=class j{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,m,w,g,y;let e=this.settings,s=(f=e.gravityX)!=null?f:0,o=(m=e.gravityY)!=null?m:1e3,n=(w=e.maxFallSpeed)!=null?w:2e3,r=(g=e.friction)!=null?g:.8,a=(y=e.airFriction)!=null?y:.98;this.vx+=s*t,this.vy+=o*t,this.vy>n&&(this.vy=n);let h=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/h)),u=t/p;for(let b=0;b<p;b++)this.moveAndCollideX(u),this.moveAndCollideY(u);this.onGround?this.vx*=r:this.vx*=a;}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,a=Math.sign(this.vx);if(a===0){this.node.x=n;return}let h=n,l=n+s,p=r,u=r+o,f=Math.max(0,M(p,e)),m=Math.min(e.height-1,M(u-1,e));if(a>0){let w=v(l,e);for(let g=f;g<=m;g++)if(this.isSolid(w,g)){l=w*e.tileWidth,h=l-s,this.vx=0;break}}else {let w=v(h,e);for(let g=f;g<=m;g++)if(this.isSolid(w,g)){h=(w+1)*e.tileWidth,l=h+s,this.vx=0;break}}this.node.x=h;}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,a=Math.sign(this.vy);if(this.onGround=false,a===0){this.node.y=r;return}let h=r,l=r+o,p=n,u=n+s,f=Math.max(0,v(p,e)),m=Math.min(e.width-1,v(u-1,e));if(a>0){let w=M(l,e);for(let g=f;g<=m;g++)if(this.isSolid(g,w)){l=w*e.tileHeight,h=l-o,this.vy=0,this.onGround=true;break}}else {let w=M(h,e);for(let g=f;g<=m;g++)if(this.isSolid(g,w)){h=(w+1)*e.tileHeight,l=h+o,this.vy=0;break}}this.node.y=h;}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(j,"PhysicsBody");var at=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,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(F,"Camera2D");var ht=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 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 P=J,q=class q extends P{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(q,"SpriteNode");var ct=q,K=class K extends P{constructor(e=1,s=1){super();i(this,"parallaxX",1);i(this,"parallaxY",1);this.parallaxX=e,this.parallaxY=s;}};c(K,"SceneLayer");var G=K,k=class k{constructor(){i(this,"id");i(this,"root",new P);i(this,"layers",[]);this.id=k.id++;}addLayer(t=1,e=1){let s=new G(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(k,"Scene"),i(k,"id",0);var lt=k;function dt(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(dt,"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 a;let o={tagA:t,tagB:e,handler:s};this.rules.push(o);let n=this.ruleKey(t,e),r=(a=this.rulesByPair.get(n))!=null?a:[];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[a,h]=n.split("\0"),l=t.get(a),p=t.get(h);if(!(!l||!p))if(a===h)for(let u=0;u<l.length;u++){let f=l[u],m=s(f);for(let w=u+1;w<l.length;w++){let g=l[w];dt(m,s(g))&&this.triggerRules(f,g,r);}}else for(let u of l){let f=s(u);for(let m of p)dt(f,s(m))&&this.triggerRules(u,m,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 ut=Q,Xt=0;function he(d,t,e,s){return {id:`col-${Xt++}`,tag:d,isActive:true,data:{node:t,body:e,sprite:s},getBounds(){let n=t.getWorldPosition(),r=e.settings.width,a=e.settings.height;return {x:n.x,y:n.y,width:r,height:a}}}}c(he,"createSpritePhysicsCollider");var x=class x{constructor(){i(this,"opened",false);i(this,"socket",null);i(this,"msgHandlers",[]);i(this,"openHandlers",[]);i(this,"closeHandlers",[]);i(this,"errorHandlers",[]);i(this,"ids",[]);}async connect(t){if(this.socket){let s=await this.socket.getProperty("readyState");if(s===x.OPEN||s===x.CONNECTING)return;await this.cleanupSocket(false);}let e=await ouider.OUID.newObject("WebSocket",t);if(!e)throw new Error(`Unable to create WebSocket for ${t}`);this.socket=ouider.ODOM.OObject.toInstance(e),this.ids.push(await this.socket.addEventListener("open",()=>{this.opened=true,this.openHandlers.forEach(s=>s());})),this.ids.push(await this.socket.addEventListener("message",s=>{let o=typeof s.data=="string"?s.data:"";this.msgHandlers.forEach(n=>n(o));})),this.ids.push(await this.socket.addEventListener("close",s=>{this.opened=false,this.closeHandlers.forEach(o=>o(s));})),this.ids.push(await this.socket.addEventListener("error",s=>{this.errorHandlers.forEach(o=>o(s));}));try{await new Promise((s,o)=>{let n=c(()=>{h(),s();},"onOpen"),r=c(l=>{h(),o(l);},"onError"),a=c(l=>{h(),o(l);},"onClose"),h=c(()=>{this.offOpen(n),this.offError(r),this.offClose(a);},"cleanup");this.onOpen(n),this.onError(r),this.onClose(a);});}catch(s){throw await this.cleanupSocket(false),s}}async send(t){this.socket&&await this.socket.getProperty("readyState")===x.OPEN&&await this.socket.invoke("send",t);}async close(t,e){await this.cleanupSocket(true,t,e);}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);}async cleanupSocket(t,e,s){let o=this.socket;this.socket=null,this.opened=false;let n=this.ids.splice(0);if(await Promise.all(n.map(r=>ouider.OUID.dettachEventListener(r).catch(()=>{}))),!!o)try{if(t){let r=await o.getProperty("readyState").catch(()=>null);(r===x.OPEN||r===x.CONNECTING)&&await o.invoke("close",e,s);}}finally{await o.release().catch(()=>{});}}};c(x,"WebSocketTransport"),i(x,"OPEN",1),i(x,"CONNECTING",0);var pt=x,V=class V{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(o){return}if(e.payload&&typeof e.payload=="object"&&"rpcId"in e.payload){let o=e.payload.rpcId,n=this.rpcResolvers.get(o);n&&(n.resolve(e.payload),this.rpcResolvers.delete(o));}let s=this.handlers.get(e.type);if(s)for(let o of s)o(e);},"handleRawMessage"));this.transport=t,this.transport.onMessage(this.handleRawMessage);}async connect(t){await this.transport.connect(t);}isConnected(){return this.transport.isConnected()}send(t,e){let s={type:t,payload:e};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"?it(st({},e),{rpcId:s}):{value:e,rpcId:s},n={type:t,payload:o};return new Promise((r,a)=>{this.rpcResolvers.set(s,{resolve:r,reject:a}),this.transport.send(JSON.stringify(n)).catch(h=>{this.rpcResolvers.delete(s),a(h);});})}close(){return this.rpcResolvers.forEach(({reject:t})=>t(new Error("Network client closed"))),this.rpcResolvers.clear(),this.transport.close()}};c(V,"NetworkClient");var gt=V,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;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=H;exports.AudioManager=X;exports.Camera2D=ht;exports.CollisionSystem=ut;exports.Game=U;exports.InputManager=T;exports.NetworkClient=gt;exports.PhysicsBody=at;exports.Scene=lt;exports.SceneLayer=G;exports.SceneNode=P;exports.Sprite=ot;exports.SpriteAnimation=O;exports.SpriteLayer=nt;exports.SpriteNode=ct;exports.SpriteSheet=E;exports.StateSyncClient=wt;exports.TileRenderer=rt;exports.WebSocketTransport=pt;exports.aabbOverlap=dt;exports.createSpritePhysicsCollider=he;exports.getTileSourceRect=Ht;exports.objectSize=I;exports.tulon=Kt;exports.worldToTileX=v;exports.worldToTileY=M;
1
+ 'use strict';var ouider=require('@ouidesigner/ouider');var et=Object.defineProperty,bt=Object.defineProperties;var Dt=Object.getOwnPropertyDescriptors;var tt=Object.getOwnPropertySymbols;var Ct=Object.prototype.hasOwnProperty,Et=Object.prototype.propertyIsEnumerable;var Y=(d,t,e)=>t in d?et(d,t,{enumerable:true,configurable:true,writable:true,value:e}):d[t]=e,st=(d,t)=>{for(var e in t||(t={}))Ct.call(t,e)&&Y(d,e,t[e]);if(tt)for(var e of tt(t))Et.call(t,e)&&Y(d,e,t[e]);return d},it=(d,t)=>bt(d,Dt(t)),c=(d,t)=>et(d,"name",{value:t,configurable:true});var i=(d,t,e)=>Y(d,typeof t!="symbol"?t+"":t,e);var z=class z{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=I(t);this.columns=Math.max(1,Math.floor(r.width/e));}};c(z,"SpriteSheet");var E=z,S=class S{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,a=Math.min(r,this.sheet.frameCount+this.sheet.from-1),h=this.sheet.columns,l=a%h*this.sheet.frameWidth,p=Math.floor(a/h)*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(S,"SpriteAnimation");var O=S,L=class L{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(L,"Sprite");var ot=L,W=class W{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(W,"SpriteLayer");var nt=W;var B=class B{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(a=>{this.images.set(n,a);}));for(let[n,r]of this.soundDefs.entries())e.push(this.loadSound(r).then(a=>{this.sounds.set(n,a);}));await Promise.all([...t,...e]);for(let[n,r]of this.sheetDefs.entries()){let a=(s=this.getImage(r.image))!=null?s:this.images.get(r.image);if(!a)throw new Error(`SpriteSheet "${n}" missing image "${r.image}"`);let h=new E(a,r.frameWidth,r.frameHeight,r.frameCount,(o=r.from)!=null?o:0);this.sheets.set(n,h);}}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 h=[s,o].filter(Boolean);s=null,o=null,await Promise.all(h.map(l=>e.dettachEventListener(l).catch(()=>{})));},"detach"),a=c(async()=>{let[h,l,p,u]=await Promise.all([e.naturalWidth(),e.naturalHeight(),e.width(),e.height()]);e.__local_size__={width:h||p||0,height:l||u||0};},"cacheSize");return new Promise(async(h,l)=>{let p=c(async()=>{if(!n)try{await a(),n=!0,await r(),h(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,m]=await Promise.all([e.getProperty("complete").catch(()=>!1),e.naturalWidth().catch(()=>0)]);f&&m>0&&await p();}catch(f){await u(f);}})}async loadSound(t){return await OUID.fetch(t,{method:"GET",headers:{},body:null,credentials:"omit"},"buffer")}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 O(o,e!=null?e:10,s!=null?s:true)}getSound(t){return this.sounds.get(t)}clean(){this.images.forEach(t=>t.release()),this.sounds.forEach(t=>t.release()),this.images.clear(),this.sounds.clear(),this.sheets.clear();}};c(B,"AssetManager");var H=B;var A=class A{constructor(t){i(this,"assets");i(this,"audioContext",null);i(this,"audioDestination",null);i(this,"decodedBuffers",new Map);this.assets=t;}async getContext(){if(this.audioContext)return this.audioContext;let t=await OUID.newObject("AudioContext");return t?(this.audioContext=ouider.ODOM.OObject.toInstance(t),this.audioContext):null}async getDestination(t){if(this.audioDestination)return this.audioDestination;let e=await t.getPropertyAndHold("destination");return e?(this.audioDestination=e,this.audioDestination):null}async getBuffer(t){if(this.decodedBuffers.has(t))return this.decodedBuffers.get(t);let e=this.assets.getSound(t);if(!e)return null;let s=await this.getContext();if(!s)return null;let o=await s.invokeAndHold("decodeAudioData",e.ref);return o&&this.decodedBuffers.set(t,o),o}clean(){this.decodedBuffers.forEach(t=>t.release()),this.decodedBuffers.clear(),this.audioDestination&&(this.audioDestination.release(),this.audioDestination=null),this.audioContext&&(this.audioContext.invoke("close"),this.audioContext.release(),this.audioContext=null);}async play(t,e){let s=await this.getContext();if(!s)return;let o=await this.getBuffer(t);if(!o)return;await s.invoke("resume");let n=await s.invokeAndHold("createBufferSource");if(!n)return;await n.setProperty("buffer",o.ref),(e==null?void 0:e.loop)!=null&&await n.setProperty("loop",e.loop);let r=await this.getDestination(s);if(!r){n.release();return}if((e==null?void 0:e.volume)!=null){let a=await s.invokeAndHold("createGain");if(a){let h=await a.getPropertyAndHold("gain");h&&(await h.setProperty("value",e.volume),h.release()),await n.invoke("connect",a.ref),await a.invoke("connect",r.ref),await n.invoke("start",0),a.release(),n.release();return}}await n.invoke("connect",r.ref),await n.invoke("start",0),n.release();}};c(A,"AudioManager");var X=A;var R=class R{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(R,"InputManager");var T=R;var $=class ${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 H,this.audio=new X(this.assets),this.input=new T(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 a,h;let[t,e,s,o]=await Promise.all([this.canvas.getProperty("clientWidth"),this.canvas.getProperty("clientHeight"),this.canvas.width(),this.canvas.height()]),n=(a=t!=null?t:s)!=null?a:0,r=(h=e!=null?e:o)!=null?h: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,a;if(!this.running)return;let s=Math.min((t-this.lastTime)/1e3,.1);this.lastTime=t;try{let h=this.buildContext(s);(n=(o=this.config).update)==null||n.call(o,h),this.input.update(),await((a=(r=this.config).draw)==null?void 0:a.call(r,h)),await this.ctx2d.commit();}catch(h){console.error("[Toubani] game loop error",h);}finally{this.running&&this.scheduleFrame();}}};c($,"Game");var U=$;function Kt(d){return new U(d)}c(Kt,"tulon");function I(d){var t;return (t=d.__local_size__)!=null?t:{width:0,height:0}}c(I,"objectSize");function v(d,t){return Math.floor(d/t.tileWidth)}c(v,"worldToTileX");function M(d,t){return Math.floor(d/t.tileHeight)}c(M,"worldToTileY");function Ht(d,t){let{image:e,tileWidth:s,tileHeight:o,margin:n=0,spacing:r=0}=t,a=I(e),h=Math.max(1,Math.floor((a.width-2*n+r)/(s+r))),l=d-1,p=l%h,u=Math.floor(l/h),f=n+p*(s+r),m=n+u*(o+r);return {sx:f,sy:m,sw:s,sh:o}}c(Ht,"getTileSourceRect");var N=class N{constructor(t){i(this,"tileset");i(this,"sourceRectCache",new Map);this.tileset=t;}draw(t,e,s,o,n){let{tileWidth:r,tileHeight:a}=e,h=o*s.focusX,l=n*s.focusY,p=s.zoom,u=s.x-h/p,f=s.y-l/p,m=s.x+(o-h)/p,w=s.y+(n-l)/p,g=Math.max(0,Math.floor(u/r)),y=Math.min(e.width-1,Math.floor(m/r)),b=Math.max(0,Math.floor(f/a)),mt=Math.min(e.height-1,Math.floor(w/a));for(let D=b;D<=mt;D++)for(let C=g;C<=y;C++){let _=e.getTile(C,D);if(_<=0)continue;let{sx:xt,sy:yt,sw:vt,sh:Mt}=this.getSourceRect(_),kt=C*r,Pt=D*a;t.drawImage(this.tileset.image,xt,yt,vt,Mt,kt,Pt,r,a);}}getSourceRect(t){let e=this.sourceRectCache.get(t);return e||(e=Ht(t,this.tileset),this.sourceRectCache.set(t,e)),e}};c(N,"TileRenderer");var rt=N;var j=class j{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,m,w,g,y;let e=this.settings,s=(f=e.gravityX)!=null?f:0,o=(m=e.gravityY)!=null?m:1e3,n=(w=e.maxFallSpeed)!=null?w:2e3,r=(g=e.friction)!=null?g:.8,a=(y=e.airFriction)!=null?y:.98;this.vx+=s*t,this.vy+=o*t,this.vy>n&&(this.vy=n);let h=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/h)),u=t/p;for(let b=0;b<p;b++)this.moveAndCollideX(u),this.moveAndCollideY(u);this.onGround?this.vx*=r:this.vx*=a;}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,a=Math.sign(this.vx);if(a===0){this.node.x=n;return}let h=n,l=n+s,p=r,u=r+o,f=Math.max(0,M(p,e)),m=Math.min(e.height-1,M(u-1,e));if(a>0){let w=v(l,e);for(let g=f;g<=m;g++)if(this.isSolid(w,g)){l=w*e.tileWidth,h=l-s,this.vx=0;break}}else {let w=v(h,e);for(let g=f;g<=m;g++)if(this.isSolid(w,g)){h=(w+1)*e.tileWidth,l=h+s,this.vx=0;break}}this.node.x=h;}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,a=Math.sign(this.vy);if(this.onGround=false,a===0){this.node.y=r;return}let h=r,l=r+o,p=n,u=n+s,f=Math.max(0,v(p,e)),m=Math.min(e.width-1,v(u-1,e));if(a>0){let w=M(l,e);for(let g=f;g<=m;g++)if(this.isSolid(g,w)){l=w*e.tileHeight,h=l-o,this.vy=0,this.onGround=true;break}}else {let w=M(h,e);for(let g=f;g<=m;g++)if(this.isSolid(g,w)){h=(w+1)*e.tileHeight,l=h+o,this.vy=0;break}}this.node.y=h;}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(j,"PhysicsBody");var at=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,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(F,"Camera2D");var ht=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 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 P=J,q=class q extends P{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(q,"SpriteNode");var ct=q,K=class K extends P{constructor(e=1,s=1){super();i(this,"parallaxX",1);i(this,"parallaxY",1);this.parallaxX=e,this.parallaxY=s;}};c(K,"SceneLayer");var G=K,k=class k{constructor(){i(this,"id");i(this,"root",new P);i(this,"layers",[]);this.id=k.id++;}addLayer(t=1,e=1){let s=new G(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(k,"Scene"),i(k,"id",0);var lt=k;function dt(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(dt,"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 a;let o={tagA:t,tagB:e,handler:s};this.rules.push(o);let n=this.ruleKey(t,e),r=(a=this.rulesByPair.get(n))!=null?a:[];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[a,h]=n.split("\0"),l=t.get(a),p=t.get(h);if(!(!l||!p))if(a===h)for(let u=0;u<l.length;u++){let f=l[u],m=s(f);for(let w=u+1;w<l.length;w++){let g=l[w];dt(m,s(g))&&this.triggerRules(f,g,r);}}else for(let u of l){let f=s(u);for(let m of p)dt(f,s(m))&&this.triggerRules(u,m,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 ut=Q,Xt=0;function he(d,t,e,s){return {id:`col-${Xt++}`,tag:d,isActive:true,data:{node:t,body:e,sprite:s},getBounds(){let n=t.getWorldPosition(),r=e.settings.width,a=e.settings.height;return {x:n.x,y:n.y,width:r,height:a}}}}c(he,"createSpritePhysicsCollider");var x=class x{constructor(){i(this,"opened",false);i(this,"socket",null);i(this,"msgHandlers",[]);i(this,"openHandlers",[]);i(this,"closeHandlers",[]);i(this,"errorHandlers",[]);i(this,"ids",[]);}async connect(t){if(this.socket){let s=await this.socket.getProperty("readyState");if(s===x.OPEN||s===x.CONNECTING)return;await this.cleanupSocket(false);}let e=await ouider.OUID.newObject("WebSocket",t);if(!e)throw new Error(`Unable to create WebSocket for ${t}`);this.socket=ouider.ODOM.OObject.toInstance(e),this.ids.push(await this.socket.addEventListener("open",()=>{this.opened=true,this.openHandlers.forEach(s=>s());})),this.ids.push(await this.socket.addEventListener("message",s=>{let o=typeof s.data=="string"?s.data:"";this.msgHandlers.forEach(n=>n(o));})),this.ids.push(await this.socket.addEventListener("close",s=>{this.opened=false,this.closeHandlers.forEach(o=>o(s));})),this.ids.push(await this.socket.addEventListener("error",s=>{this.errorHandlers.forEach(o=>o(s));}));try{await new Promise((s,o)=>{let n=c(()=>{h(),s();},"onOpen"),r=c(l=>{h(),o(l);},"onError"),a=c(l=>{h(),o(l);},"onClose"),h=c(()=>{this.offOpen(n),this.offError(r),this.offClose(a);},"cleanup");this.onOpen(n),this.onError(r),this.onClose(a);});}catch(s){throw await this.cleanupSocket(false),s}}async send(t){this.socket&&await this.socket.getProperty("readyState")===x.OPEN&&await this.socket.invoke("send",t);}async close(t,e){await this.cleanupSocket(true,t,e);}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);}async cleanupSocket(t,e,s){let o=this.socket;this.socket=null,this.opened=false;let n=this.ids.splice(0);if(await Promise.all(n.map(r=>ouider.OUID.dettachEventListener(r).catch(()=>{}))),!!o)try{if(t){let r=await o.getProperty("readyState").catch(()=>null);(r===x.OPEN||r===x.CONNECTING)&&await o.invoke("close",e,s);}}finally{await o.release().catch(()=>{});}}};c(x,"WebSocketTransport"),i(x,"OPEN",1),i(x,"CONNECTING",0);var pt=x,V=class V{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(o){return}if(e.payload&&typeof e.payload=="object"&&"rpcId"in e.payload){let o=e.payload.rpcId,n=this.rpcResolvers.get(o);n&&(n.resolve(e.payload),this.rpcResolvers.delete(o));}let s=this.handlers.get(e.type);if(s)for(let o of s)o(e);},"handleRawMessage"));this.transport=t,this.transport.onMessage(this.handleRawMessage);}async connect(t){await this.transport.connect(t);}isConnected(){return this.transport.isConnected()}send(t,e){let s={type:t,payload:e};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"?it(st({},e),{rpcId:s}):{value:e,rpcId:s},n={type:t,payload:o};return new Promise((r,a)=>{this.rpcResolvers.set(s,{resolve:r,reject:a}),this.transport.send(JSON.stringify(n)).catch(h=>{this.rpcResolvers.delete(s),a(h);});})}close(){return this.rpcResolvers.forEach(({reject:t})=>t(new Error("Network client closed"))),this.rpcResolvers.clear(),this.transport.close()}};c(V,"NetworkClient");var gt=V,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;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=H;exports.AudioManager=X;exports.Camera2D=ht;exports.CollisionSystem=ut;exports.Game=U;exports.InputManager=T;exports.NetworkClient=gt;exports.PhysicsBody=at;exports.Scene=lt;exports.SceneLayer=G;exports.SceneNode=P;exports.Sprite=ot;exports.SpriteAnimation=O;exports.SpriteLayer=nt;exports.SpriteNode=ct;exports.SpriteSheet=E;exports.StateSyncClient=wt;exports.TileRenderer=rt;exports.WebSocketTransport=pt;exports.aabbOverlap=dt;exports.createSpritePhysicsCollider=he;exports.getTileSourceRect=Ht;exports.objectSize=I;exports.tulon=Kt;exports.worldToTileX=v;exports.worldToTileY=M;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import {ODOM,OUID as OUID$1}from'ouider';var et=Object.defineProperty,bt=Object.defineProperties;var Dt=Object.getOwnPropertyDescriptors;var tt=Object.getOwnPropertySymbols;var Ct=Object.prototype.hasOwnProperty,Et=Object.prototype.propertyIsEnumerable;var Y=(d,t,e)=>t in d?et(d,t,{enumerable:true,configurable:true,writable:true,value:e}):d[t]=e,st=(d,t)=>{for(var e in t||(t={}))Ct.call(t,e)&&Y(d,e,t[e]);if(tt)for(var e of tt(t))Et.call(t,e)&&Y(d,e,t[e]);return d},it=(d,t)=>bt(d,Dt(t)),c=(d,t)=>et(d,"name",{value:t,configurable:true});var i=(d,t,e)=>Y(d,typeof t!="symbol"?t+"":t,e);var z=class z{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=I(t);this.columns=Math.max(1,Math.floor(r.width/e));}};c(z,"SpriteSheet");var E=z,S=class S{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,a=Math.min(r,this.sheet.frameCount+this.sheet.from-1),h=this.sheet.columns,l=a%h*this.sheet.frameWidth,p=Math.floor(a/h)*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(S,"SpriteAnimation");var O=S,L=class L{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(L,"Sprite");var ot=L,W=class W{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(W,"SpriteLayer");var nt=W;var B=class B{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(a=>{this.images.set(n,a);}));for(let[n,r]of this.soundDefs.entries())e.push(this.loadSound(r).then(a=>{this.sounds.set(n,a);}));await Promise.all([...t,...e]);for(let[n,r]of this.sheetDefs.entries()){let a=(s=this.getImage(r.image))!=null?s:this.images.get(r.image);if(!a)throw new Error(`SpriteSheet "${n}" missing image "${r.image}"`);let h=new E(a,r.frameWidth,r.frameHeight,r.frameCount,(o=r.from)!=null?o:0);this.sheets.set(n,h);}}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 h=[s,o].filter(Boolean);s=null,o=null,await Promise.all(h.map(l=>e.dettachEventListener(l).catch(()=>{})));},"detach"),a=c(async()=>{let[h,l,p,u]=await Promise.all([e.naturalWidth(),e.naturalHeight(),e.width(),e.height()]);e.__local_size__={width:h||p||0,height:l||u||0};},"cacheSize");return new Promise(async(h,l)=>{let p=c(async()=>{if(!n)try{await a(),n=!0,await r(),h(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,m]=await Promise.all([e.getProperty("complete").catch(()=>!1),e.naturalWidth().catch(()=>0)]);f&&m>0&&await p();}catch(f){await u(f);}})}async loadSound(t){return await OUID.fetch(t,{method:"GET",headers:{},body:null,credentials:"omit"},"buffer")}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 O(o,e!=null?e:10,s!=null?s:true)}getSound(t){return this.sounds.get(t)}clean(){this.images.forEach(t=>t.release()),this.sounds.forEach(t=>t.release()),this.images.clear(),this.sounds.clear(),this.sheets.clear();}};c(B,"AssetManager");var H=B;var A=class A{constructor(t){i(this,"assets");i(this,"audioContext",null);i(this,"audioDestination",null);i(this,"decodedBuffers",new Map);this.assets=t;}async getContext(){if(this.audioContext)return this.audioContext;let t=await OUID.newObject("AudioContext");return t?(this.audioContext=ODOM.OObject.toInstance(t),this.audioContext):null}async getDestination(t){if(this.audioDestination)return this.audioDestination;let e=await t.getPropertyAndHold("destination");return e?(this.audioDestination=e,this.audioDestination):null}async getBuffer(t){if(this.decodedBuffers.has(t))return this.decodedBuffers.get(t);let e=this.assets.getSound(t);if(!e)return null;let s=await this.getContext();if(!s)return null;let o=await s.invokeAndHold("decodeAudioData",e.ref);return o&&this.decodedBuffers.set(t,o),o}clean(){this.decodedBuffers.forEach(t=>t.release()),this.decodedBuffers.clear(),this.audioDestination&&(this.audioDestination.release(),this.audioDestination=null),this.audioContext&&(this.audioContext.invoke("close"),this.audioContext.release(),this.audioContext=null);}async play(t,e){let s=await this.getContext();if(!s)return;let o=await this.getBuffer(t);if(!o)return;await s.invoke("resume");let n=await s.invokeAndHold("createBufferSource");if(!n)return;await n.setProperty("buffer",o.ref),(e==null?void 0:e.loop)!=null&&await n.setProperty("loop",e.loop);let r=await this.getDestination(s);if(!r){n.release();return}if((e==null?void 0:e.volume)!=null){let a=await s.invokeAndHold("createGain");if(a){let h=await a.getPropertyAndHold("gain");h&&(await h.setProperty("value",e.volume),h.release()),await n.invoke("connect",a.ref),await a.invoke("connect",r.ref),await n.invoke("start",0),a.release(),n.release();return}}await n.invoke("connect",r.ref),await n.invoke("start",0),n.release();}};c(A,"AudioManager");var X=A;var R=class R{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(R,"InputManager");var T=R;var $=class ${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 H,this.audio=new X(this.assets),this.input=new T(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 a,h;let[t,e,s,o]=await Promise.all([this.canvas.getProperty("clientWidth"),this.canvas.getProperty("clientHeight"),this.canvas.width(),this.canvas.height()]),n=(a=t!=null?t:s)!=null?a:0,r=(h=e!=null?e:o)!=null?h: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,a;if(!this.running)return;let s=Math.min((t-this.lastTime)/1e3,.1);this.lastTime=t;try{let h=this.buildContext(s);(n=(o=this.config).update)==null||n.call(o,h),this.input.update(),await((a=(r=this.config).draw)==null?void 0:a.call(r,h)),await this.ctx2d.commit();}catch(h){console.error("[Toubani] game loop error",h);}finally{this.running&&this.scheduleFrame();}}};c($,"Game");var U=$;function Kt(d){return new U(d)}c(Kt,"tulon");function I(d){var t;return (t=d.__local_size__)!=null?t:{width:0,height:0}}c(I,"objectSize");function v(d,t){return Math.floor(d/t.tileWidth)}c(v,"worldToTileX");function M(d,t){return Math.floor(d/t.tileHeight)}c(M,"worldToTileY");function Ht(d,t){let{image:e,tileWidth:s,tileHeight:o,margin:n=0,spacing:r=0}=t,a=I(e),h=Math.max(1,Math.floor((a.width-2*n+r)/(s+r))),l=d-1,p=l%h,u=Math.floor(l/h),f=n+p*(s+r),m=n+u*(o+r);return {sx:f,sy:m,sw:s,sh:o}}c(Ht,"getTileSourceRect");var N=class N{constructor(t){i(this,"tileset");i(this,"sourceRectCache",new Map);this.tileset=t;}draw(t,e,s,o,n){let{tileWidth:r,tileHeight:a}=e,h=o*s.focusX,l=n*s.focusY,p=s.zoom,u=s.x-h/p,f=s.y-l/p,m=s.x+(o-h)/p,w=s.y+(n-l)/p,g=Math.max(0,Math.floor(u/r)),y=Math.min(e.width-1,Math.floor(m/r)),b=Math.max(0,Math.floor(f/a)),mt=Math.min(e.height-1,Math.floor(w/a));for(let D=b;D<=mt;D++)for(let C=g;C<=y;C++){let _=e.getTile(C,D);if(_<=0)continue;let{sx:xt,sy:yt,sw:vt,sh:Mt}=this.getSourceRect(_),kt=C*r,Pt=D*a;t.drawImage(this.tileset.image,xt,yt,vt,Mt,kt,Pt,r,a);}}getSourceRect(t){let e=this.sourceRectCache.get(t);return e||(e=Ht(t,this.tileset),this.sourceRectCache.set(t,e)),e}};c(N,"TileRenderer");var rt=N;var j=class j{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,m,w,g,y;let e=this.settings,s=(f=e.gravityX)!=null?f:0,o=(m=e.gravityY)!=null?m:1e3,n=(w=e.maxFallSpeed)!=null?w:2e3,r=(g=e.friction)!=null?g:.8,a=(y=e.airFriction)!=null?y:.98;this.vx+=s*t,this.vy+=o*t,this.vy>n&&(this.vy=n);let h=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/h)),u=t/p;for(let b=0;b<p;b++)this.moveAndCollideX(u),this.moveAndCollideY(u);this.onGround?this.vx*=r:this.vx*=a;}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,a=Math.sign(this.vx);if(a===0){this.node.x=n;return}let h=n,l=n+s,p=r,u=r+o,f=Math.max(0,M(p,e)),m=Math.min(e.height-1,M(u-1,e));if(a>0){let w=v(l,e);for(let g=f;g<=m;g++)if(this.isSolid(w,g)){l=w*e.tileWidth,h=l-s,this.vx=0;break}}else {let w=v(h,e);for(let g=f;g<=m;g++)if(this.isSolid(w,g)){h=(w+1)*e.tileWidth,l=h+s,this.vx=0;break}}this.node.x=h;}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,a=Math.sign(this.vy);if(this.onGround=false,a===0){this.node.y=r;return}let h=r,l=r+o,p=n,u=n+s,f=Math.max(0,v(p,e)),m=Math.min(e.width-1,v(u-1,e));if(a>0){let w=M(l,e);for(let g=f;g<=m;g++)if(this.isSolid(g,w)){l=w*e.tileHeight,h=l-o,this.vy=0,this.onGround=true;break}}else {let w=M(h,e);for(let g=f;g<=m;g++)if(this.isSolid(g,w)){h=(w+1)*e.tileHeight,l=h+o,this.vy=0;break}}this.node.y=h;}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(j,"PhysicsBody");var at=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,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(F,"Camera2D");var ht=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 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 P=J,q=class q extends P{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(q,"SpriteNode");var ct=q,K=class K extends P{constructor(e=1,s=1){super();i(this,"parallaxX",1);i(this,"parallaxY",1);this.parallaxX=e,this.parallaxY=s;}};c(K,"SceneLayer");var G=K,k=class k{constructor(){i(this,"id");i(this,"root",new P);i(this,"layers",[]);this.id=k.id++;}addLayer(t=1,e=1){let s=new G(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(k,"Scene"),i(k,"id",0);var lt=k;function dt(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(dt,"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 a;let o={tagA:t,tagB:e,handler:s};this.rules.push(o);let n=this.ruleKey(t,e),r=(a=this.rulesByPair.get(n))!=null?a:[];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[a,h]=n.split("\0"),l=t.get(a),p=t.get(h);if(!(!l||!p))if(a===h)for(let u=0;u<l.length;u++){let f=l[u],m=s(f);for(let w=u+1;w<l.length;w++){let g=l[w];dt(m,s(g))&&this.triggerRules(f,g,r);}}else for(let u of l){let f=s(u);for(let m of p)dt(f,s(m))&&this.triggerRules(u,m,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 ut=Q,Xt=0;function he(d,t,e,s){return {id:`col-${Xt++}`,tag:d,isActive:true,data:{node:t,body:e,sprite:s},getBounds(){let n=t.getWorldPosition(),r=e.settings.width,a=e.settings.height;return {x:n.x,y:n.y,width:r,height:a}}}}c(he,"createSpritePhysicsCollider");var x=class x{constructor(){i(this,"opened",false);i(this,"socket",null);i(this,"msgHandlers",[]);i(this,"openHandlers",[]);i(this,"closeHandlers",[]);i(this,"errorHandlers",[]);i(this,"ids",[]);}async connect(t){if(this.socket){let s=await this.socket.getProperty("readyState");if(s===x.OPEN||s===x.CONNECTING)return;await this.cleanupSocket(false);}let e=await OUID$1.newObject("WebSocket",t);if(!e)throw new Error(`Unable to create WebSocket for ${t}`);this.socket=ODOM.OObject.toInstance(e),this.ids.push(await this.socket.addEventListener("open",()=>{this.opened=true,this.openHandlers.forEach(s=>s());})),this.ids.push(await this.socket.addEventListener("message",s=>{let o=typeof s.data=="string"?s.data:"";this.msgHandlers.forEach(n=>n(o));})),this.ids.push(await this.socket.addEventListener("close",s=>{this.opened=false,this.closeHandlers.forEach(o=>o(s));})),this.ids.push(await this.socket.addEventListener("error",s=>{this.errorHandlers.forEach(o=>o(s));}));try{await new Promise((s,o)=>{let n=c(()=>{h(),s();},"onOpen"),r=c(l=>{h(),o(l);},"onError"),a=c(l=>{h(),o(l);},"onClose"),h=c(()=>{this.offOpen(n),this.offError(r),this.offClose(a);},"cleanup");this.onOpen(n),this.onError(r),this.onClose(a);});}catch(s){throw await this.cleanupSocket(false),s}}async send(t){this.socket&&await this.socket.getProperty("readyState")===x.OPEN&&await this.socket.invoke("send",t);}async close(t,e){await this.cleanupSocket(true,t,e);}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);}async cleanupSocket(t,e,s){let o=this.socket;this.socket=null,this.opened=false;let n=this.ids.splice(0);if(await Promise.all(n.map(r=>OUID$1.dettachEventListener(r).catch(()=>{}))),!!o)try{if(t){let r=await o.getProperty("readyState").catch(()=>null);(r===x.OPEN||r===x.CONNECTING)&&await o.invoke("close",e,s);}}finally{await o.release().catch(()=>{});}}};c(x,"WebSocketTransport"),i(x,"OPEN",1),i(x,"CONNECTING",0);var pt=x,V=class V{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(o){return}if(e.payload&&typeof e.payload=="object"&&"rpcId"in e.payload){let o=e.payload.rpcId,n=this.rpcResolvers.get(o);n&&(n.resolve(e.payload),this.rpcResolvers.delete(o));}let s=this.handlers.get(e.type);if(s)for(let o of s)o(e);},"handleRawMessage"));this.transport=t,this.transport.onMessage(this.handleRawMessage);}async connect(t){await this.transport.connect(t);}isConnected(){return this.transport.isConnected()}send(t,e){let s={type:t,payload:e};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"?it(st({},e),{rpcId:s}):{value:e,rpcId:s},n={type:t,payload:o};return new Promise((r,a)=>{this.rpcResolvers.set(s,{resolve:r,reject:a}),this.transport.send(JSON.stringify(n)).catch(h=>{this.rpcResolvers.delete(s),a(h);});})}close(){return this.rpcResolvers.forEach(({reject:t})=>t(new Error("Network client closed"))),this.rpcResolvers.clear(),this.transport.close()}};c(V,"NetworkClient");var gt=V,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;this.net.send((s=(e=this.channels)==null?void 0:e.patch)!=null?s:"state_patch",t);}};c(Z,"StateSyncClient");var wt=Z;export{H as AssetManager,X as AudioManager,ht as Camera2D,ut as CollisionSystem,U as Game,T as InputManager,gt as NetworkClient,at as PhysicsBody,lt as Scene,G as SceneLayer,P as SceneNode,ot as Sprite,O as SpriteAnimation,nt as SpriteLayer,ct as SpriteNode,E as SpriteSheet,wt as StateSyncClient,rt as TileRenderer,pt as WebSocketTransport,dt as aabbOverlap,he as createSpritePhysicsCollider,Ht as getTileSourceRect,I as objectSize,Kt as tulon,v as worldToTileX,M as worldToTileY};
1
+ import {ODOM,OUID as OUID$1}from'@ouidesigner/ouider';var et=Object.defineProperty,bt=Object.defineProperties;var Dt=Object.getOwnPropertyDescriptors;var tt=Object.getOwnPropertySymbols;var Ct=Object.prototype.hasOwnProperty,Et=Object.prototype.propertyIsEnumerable;var Y=(d,t,e)=>t in d?et(d,t,{enumerable:true,configurable:true,writable:true,value:e}):d[t]=e,st=(d,t)=>{for(var e in t||(t={}))Ct.call(t,e)&&Y(d,e,t[e]);if(tt)for(var e of tt(t))Et.call(t,e)&&Y(d,e,t[e]);return d},it=(d,t)=>bt(d,Dt(t)),c=(d,t)=>et(d,"name",{value:t,configurable:true});var i=(d,t,e)=>Y(d,typeof t!="symbol"?t+"":t,e);var z=class z{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=I(t);this.columns=Math.max(1,Math.floor(r.width/e));}};c(z,"SpriteSheet");var E=z,S=class S{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,a=Math.min(r,this.sheet.frameCount+this.sheet.from-1),h=this.sheet.columns,l=a%h*this.sheet.frameWidth,p=Math.floor(a/h)*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(S,"SpriteAnimation");var O=S,L=class L{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(L,"Sprite");var ot=L,W=class W{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(W,"SpriteLayer");var nt=W;var B=class B{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(a=>{this.images.set(n,a);}));for(let[n,r]of this.soundDefs.entries())e.push(this.loadSound(r).then(a=>{this.sounds.set(n,a);}));await Promise.all([...t,...e]);for(let[n,r]of this.sheetDefs.entries()){let a=(s=this.getImage(r.image))!=null?s:this.images.get(r.image);if(!a)throw new Error(`SpriteSheet "${n}" missing image "${r.image}"`);let h=new E(a,r.frameWidth,r.frameHeight,r.frameCount,(o=r.from)!=null?o:0);this.sheets.set(n,h);}}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 h=[s,o].filter(Boolean);s=null,o=null,await Promise.all(h.map(l=>e.dettachEventListener(l).catch(()=>{})));},"detach"),a=c(async()=>{let[h,l,p,u]=await Promise.all([e.naturalWidth(),e.naturalHeight(),e.width(),e.height()]);e.__local_size__={width:h||p||0,height:l||u||0};},"cacheSize");return new Promise(async(h,l)=>{let p=c(async()=>{if(!n)try{await a(),n=!0,await r(),h(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,m]=await Promise.all([e.getProperty("complete").catch(()=>!1),e.naturalWidth().catch(()=>0)]);f&&m>0&&await p();}catch(f){await u(f);}})}async loadSound(t){return await OUID.fetch(t,{method:"GET",headers:{},body:null,credentials:"omit"},"buffer")}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 O(o,e!=null?e:10,s!=null?s:true)}getSound(t){return this.sounds.get(t)}clean(){this.images.forEach(t=>t.release()),this.sounds.forEach(t=>t.release()),this.images.clear(),this.sounds.clear(),this.sheets.clear();}};c(B,"AssetManager");var H=B;var A=class A{constructor(t){i(this,"assets");i(this,"audioContext",null);i(this,"audioDestination",null);i(this,"decodedBuffers",new Map);this.assets=t;}async getContext(){if(this.audioContext)return this.audioContext;let t=await OUID.newObject("AudioContext");return t?(this.audioContext=ODOM.OObject.toInstance(t),this.audioContext):null}async getDestination(t){if(this.audioDestination)return this.audioDestination;let e=await t.getPropertyAndHold("destination");return e?(this.audioDestination=e,this.audioDestination):null}async getBuffer(t){if(this.decodedBuffers.has(t))return this.decodedBuffers.get(t);let e=this.assets.getSound(t);if(!e)return null;let s=await this.getContext();if(!s)return null;let o=await s.invokeAndHold("decodeAudioData",e.ref);return o&&this.decodedBuffers.set(t,o),o}clean(){this.decodedBuffers.forEach(t=>t.release()),this.decodedBuffers.clear(),this.audioDestination&&(this.audioDestination.release(),this.audioDestination=null),this.audioContext&&(this.audioContext.invoke("close"),this.audioContext.release(),this.audioContext=null);}async play(t,e){let s=await this.getContext();if(!s)return;let o=await this.getBuffer(t);if(!o)return;await s.invoke("resume");let n=await s.invokeAndHold("createBufferSource");if(!n)return;await n.setProperty("buffer",o.ref),(e==null?void 0:e.loop)!=null&&await n.setProperty("loop",e.loop);let r=await this.getDestination(s);if(!r){n.release();return}if((e==null?void 0:e.volume)!=null){let a=await s.invokeAndHold("createGain");if(a){let h=await a.getPropertyAndHold("gain");h&&(await h.setProperty("value",e.volume),h.release()),await n.invoke("connect",a.ref),await a.invoke("connect",r.ref),await n.invoke("start",0),a.release(),n.release();return}}await n.invoke("connect",r.ref),await n.invoke("start",0),n.release();}};c(A,"AudioManager");var X=A;var R=class R{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(R,"InputManager");var T=R;var $=class ${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 H,this.audio=new X(this.assets),this.input=new T(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 a,h;let[t,e,s,o]=await Promise.all([this.canvas.getProperty("clientWidth"),this.canvas.getProperty("clientHeight"),this.canvas.width(),this.canvas.height()]),n=(a=t!=null?t:s)!=null?a:0,r=(h=e!=null?e:o)!=null?h: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,a;if(!this.running)return;let s=Math.min((t-this.lastTime)/1e3,.1);this.lastTime=t;try{let h=this.buildContext(s);(n=(o=this.config).update)==null||n.call(o,h),this.input.update(),await((a=(r=this.config).draw)==null?void 0:a.call(r,h)),await this.ctx2d.commit();}catch(h){console.error("[Toubani] game loop error",h);}finally{this.running&&this.scheduleFrame();}}};c($,"Game");var U=$;function Kt(d){return new U(d)}c(Kt,"tulon");function I(d){var t;return (t=d.__local_size__)!=null?t:{width:0,height:0}}c(I,"objectSize");function v(d,t){return Math.floor(d/t.tileWidth)}c(v,"worldToTileX");function M(d,t){return Math.floor(d/t.tileHeight)}c(M,"worldToTileY");function Ht(d,t){let{image:e,tileWidth:s,tileHeight:o,margin:n=0,spacing:r=0}=t,a=I(e),h=Math.max(1,Math.floor((a.width-2*n+r)/(s+r))),l=d-1,p=l%h,u=Math.floor(l/h),f=n+p*(s+r),m=n+u*(o+r);return {sx:f,sy:m,sw:s,sh:o}}c(Ht,"getTileSourceRect");var N=class N{constructor(t){i(this,"tileset");i(this,"sourceRectCache",new Map);this.tileset=t;}draw(t,e,s,o,n){let{tileWidth:r,tileHeight:a}=e,h=o*s.focusX,l=n*s.focusY,p=s.zoom,u=s.x-h/p,f=s.y-l/p,m=s.x+(o-h)/p,w=s.y+(n-l)/p,g=Math.max(0,Math.floor(u/r)),y=Math.min(e.width-1,Math.floor(m/r)),b=Math.max(0,Math.floor(f/a)),mt=Math.min(e.height-1,Math.floor(w/a));for(let D=b;D<=mt;D++)for(let C=g;C<=y;C++){let _=e.getTile(C,D);if(_<=0)continue;let{sx:xt,sy:yt,sw:vt,sh:Mt}=this.getSourceRect(_),kt=C*r,Pt=D*a;t.drawImage(this.tileset.image,xt,yt,vt,Mt,kt,Pt,r,a);}}getSourceRect(t){let e=this.sourceRectCache.get(t);return e||(e=Ht(t,this.tileset),this.sourceRectCache.set(t,e)),e}};c(N,"TileRenderer");var rt=N;var j=class j{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,m,w,g,y;let e=this.settings,s=(f=e.gravityX)!=null?f:0,o=(m=e.gravityY)!=null?m:1e3,n=(w=e.maxFallSpeed)!=null?w:2e3,r=(g=e.friction)!=null?g:.8,a=(y=e.airFriction)!=null?y:.98;this.vx+=s*t,this.vy+=o*t,this.vy>n&&(this.vy=n);let h=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/h)),u=t/p;for(let b=0;b<p;b++)this.moveAndCollideX(u),this.moveAndCollideY(u);this.onGround?this.vx*=r:this.vx*=a;}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,a=Math.sign(this.vx);if(a===0){this.node.x=n;return}let h=n,l=n+s,p=r,u=r+o,f=Math.max(0,M(p,e)),m=Math.min(e.height-1,M(u-1,e));if(a>0){let w=v(l,e);for(let g=f;g<=m;g++)if(this.isSolid(w,g)){l=w*e.tileWidth,h=l-s,this.vx=0;break}}else {let w=v(h,e);for(let g=f;g<=m;g++)if(this.isSolid(w,g)){h=(w+1)*e.tileWidth,l=h+s,this.vx=0;break}}this.node.x=h;}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,a=Math.sign(this.vy);if(this.onGround=false,a===0){this.node.y=r;return}let h=r,l=r+o,p=n,u=n+s,f=Math.max(0,v(p,e)),m=Math.min(e.width-1,v(u-1,e));if(a>0){let w=M(l,e);for(let g=f;g<=m;g++)if(this.isSolid(g,w)){l=w*e.tileHeight,h=l-o,this.vy=0,this.onGround=true;break}}else {let w=M(h,e);for(let g=f;g<=m;g++)if(this.isSolid(g,w)){h=(w+1)*e.tileHeight,l=h+o,this.vy=0;break}}this.node.y=h;}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(j,"PhysicsBody");var at=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,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(F,"Camera2D");var ht=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 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 P=J,q=class q extends P{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(q,"SpriteNode");var ct=q,K=class K extends P{constructor(e=1,s=1){super();i(this,"parallaxX",1);i(this,"parallaxY",1);this.parallaxX=e,this.parallaxY=s;}};c(K,"SceneLayer");var G=K,k=class k{constructor(){i(this,"id");i(this,"root",new P);i(this,"layers",[]);this.id=k.id++;}addLayer(t=1,e=1){let s=new G(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(k,"Scene"),i(k,"id",0);var lt=k;function dt(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(dt,"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 a;let o={tagA:t,tagB:e,handler:s};this.rules.push(o);let n=this.ruleKey(t,e),r=(a=this.rulesByPair.get(n))!=null?a:[];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[a,h]=n.split("\0"),l=t.get(a),p=t.get(h);if(!(!l||!p))if(a===h)for(let u=0;u<l.length;u++){let f=l[u],m=s(f);for(let w=u+1;w<l.length;w++){let g=l[w];dt(m,s(g))&&this.triggerRules(f,g,r);}}else for(let u of l){let f=s(u);for(let m of p)dt(f,s(m))&&this.triggerRules(u,m,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 ut=Q,Xt=0;function he(d,t,e,s){return {id:`col-${Xt++}`,tag:d,isActive:true,data:{node:t,body:e,sprite:s},getBounds(){let n=t.getWorldPosition(),r=e.settings.width,a=e.settings.height;return {x:n.x,y:n.y,width:r,height:a}}}}c(he,"createSpritePhysicsCollider");var x=class x{constructor(){i(this,"opened",false);i(this,"socket",null);i(this,"msgHandlers",[]);i(this,"openHandlers",[]);i(this,"closeHandlers",[]);i(this,"errorHandlers",[]);i(this,"ids",[]);}async connect(t){if(this.socket){let s=await this.socket.getProperty("readyState");if(s===x.OPEN||s===x.CONNECTING)return;await this.cleanupSocket(false);}let e=await OUID$1.newObject("WebSocket",t);if(!e)throw new Error(`Unable to create WebSocket for ${t}`);this.socket=ODOM.OObject.toInstance(e),this.ids.push(await this.socket.addEventListener("open",()=>{this.opened=true,this.openHandlers.forEach(s=>s());})),this.ids.push(await this.socket.addEventListener("message",s=>{let o=typeof s.data=="string"?s.data:"";this.msgHandlers.forEach(n=>n(o));})),this.ids.push(await this.socket.addEventListener("close",s=>{this.opened=false,this.closeHandlers.forEach(o=>o(s));})),this.ids.push(await this.socket.addEventListener("error",s=>{this.errorHandlers.forEach(o=>o(s));}));try{await new Promise((s,o)=>{let n=c(()=>{h(),s();},"onOpen"),r=c(l=>{h(),o(l);},"onError"),a=c(l=>{h(),o(l);},"onClose"),h=c(()=>{this.offOpen(n),this.offError(r),this.offClose(a);},"cleanup");this.onOpen(n),this.onError(r),this.onClose(a);});}catch(s){throw await this.cleanupSocket(false),s}}async send(t){this.socket&&await this.socket.getProperty("readyState")===x.OPEN&&await this.socket.invoke("send",t);}async close(t,e){await this.cleanupSocket(true,t,e);}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);}async cleanupSocket(t,e,s){let o=this.socket;this.socket=null,this.opened=false;let n=this.ids.splice(0);if(await Promise.all(n.map(r=>OUID$1.dettachEventListener(r).catch(()=>{}))),!!o)try{if(t){let r=await o.getProperty("readyState").catch(()=>null);(r===x.OPEN||r===x.CONNECTING)&&await o.invoke("close",e,s);}}finally{await o.release().catch(()=>{});}}};c(x,"WebSocketTransport"),i(x,"OPEN",1),i(x,"CONNECTING",0);var pt=x,V=class V{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(o){return}if(e.payload&&typeof e.payload=="object"&&"rpcId"in e.payload){let o=e.payload.rpcId,n=this.rpcResolvers.get(o);n&&(n.resolve(e.payload),this.rpcResolvers.delete(o));}let s=this.handlers.get(e.type);if(s)for(let o of s)o(e);},"handleRawMessage"));this.transport=t,this.transport.onMessage(this.handleRawMessage);}async connect(t){await this.transport.connect(t);}isConnected(){return this.transport.isConnected()}send(t,e){let s={type:t,payload:e};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"?it(st({},e),{rpcId:s}):{value:e,rpcId:s},n={type:t,payload:o};return new Promise((r,a)=>{this.rpcResolvers.set(s,{resolve:r,reject:a}),this.transport.send(JSON.stringify(n)).catch(h=>{this.rpcResolvers.delete(s),a(h);});})}close(){return this.rpcResolvers.forEach(({reject:t})=>t(new Error("Network client closed"))),this.rpcResolvers.clear(),this.transport.close()}};c(V,"NetworkClient");var gt=V,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;this.net.send((s=(e=this.channels)==null?void 0:e.patch)!=null?s:"state_patch",t);}};c(Z,"StateSyncClient");var wt=Z;export{H as AssetManager,X as AudioManager,ht as Camera2D,ut as CollisionSystem,U as Game,T as InputManager,gt as NetworkClient,at as PhysicsBody,lt as Scene,G as SceneLayer,P as SceneNode,ot as Sprite,O as SpriteAnimation,nt as SpriteLayer,ct as SpriteNode,E as SpriteSheet,wt as StateSyncClient,rt as TileRenderer,pt as WebSocketTransport,dt as aabbOverlap,he as createSpritePhysicsCollider,Ht as getTileSourceRect,I as objectSize,Kt as tulon,v as worldToTileX,M as worldToTileY};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouidesigner/toubani",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -13,7 +13,7 @@
13
13
  "license": "ISC",
14
14
  "type": "commonjs",
15
15
  "peerDependencies": {
16
- "ouider": "^0.1.9"
16
+ "@ouidesigner/ouider": "^0.3.0"
17
17
  },
18
18
  "devDependencies": {
19
19
  "@types/node": "^24.10.1",