From 062c33b12eaaa571fabc20d9f48facd09d47925e Mon Sep 17 00:00:00 2001 From: straker Date: Mon, 2 Sep 2019 22:17:06 -0600 Subject: [PATCH 1/6] feat(sprite): add support for flipping sprite --- docs/api/sprite.html | 32 +++++++++++++ docs/assets/js/kontra.js | 2 +- kontra.js | 49 +++++++++++++++----- kontra.min.js | 2 +- kontra.min.mjs | 2 +- kontra.mjs | 49 +++++++++++++++----- src/sprite.js | 49 +++++++++++++++----- test/unit/sprite.spec.js | 97 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 249 insertions(+), 33 deletions(-) diff --git a/docs/api/sprite.html b/docs/api/sprite.html index daf31479..87bad7a4 100644 --- a/docs/api/sprite.html +++ b/docs/api/sprite.html @@ -275,6 +275,16 @@

Properties

Sprite​.rotation +
  • + + Sprite​.scaleX + +
  • +
  • + + Sprite​.scaleY + +
  • Sprite​.sx @@ -1859,6 +1869,28 @@

    The rotation of the sprite around the origin in radians.

    + +
    +

    + + Sprite​.scaleX + + +

    + +

    The horizontal scaling factor. A negative value flips the sprite across the vertical axis.

    + +
    +
    +

    + + Sprite​.scaleY + + +

    + +

    The vertical scaling factor. A negative value flips the sprite across the horizontal axis.

    +

    diff --git a/docs/assets/js/kontra.js b/docs/assets/js/kontra.js index 1a2d45a8..a08dda61 100644 --- a/docs/assets/js/kontra.js +++ b/docs/assets/js/kontra.js @@ -1 +1 @@ -var kontra=function(){"use strict";let t,e,i={};function s(t,e){i[t]=i[t]||[],i[t].push(e)}function n(t,...e){i[t]&&i[t].map(t=>t(...e))}function h(){return t}function o(){return e}class r{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:n,height:h,margin:o=0}=t.frame;this.width=n,this.height=h,this.margin=o,this._f=0,this._a=0}clone(){return a(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:n=o()}={}){let h=this.frames[this._f]/this.spriteSheet._f|0,r=this.frames[this._f]%this.spriteSheet._f|0;n.drawImage(this.spriteSheet.image,r*this.width+(2*r+1)*this.margin,h*this.height+(2*h+1)*this.margin,this.width,this.height,t,e,i,s)}}function a(t){return new r(t)}a.prototype=r.prototype,a.class=r;let c=/(jpeg|jpg|gif|png)$/,l=/(wav|mp3|ogg|aac)$/,d=/^\//,u=/\/$/,f=new WeakMap,g="",p="",m="";function _(t,e){return new URL(t,e).href}function w(t,e){return[t.replace(u,""),t?e.replace(d,""):e].filter(t=>t).join("/")}function y(t){return t.split(".").pop()}function x(t){let e=t.replace("."+y(t),"");return 2==e.split("/").length?e.replace(d,""):e}let b={},v={},j={};function A(){window.__k||(window.__k={dm:f,u:_,d:j,i:b})}function S(t){return A(),new Promise((e,i)=>{let s,h,o;if(s=w(g,t),b[s])return e(b[s]);(h=new Image).onload=function(){o=_(s,window.location.href),b[x(t)]=b[s]=b[o]=this,n("assetLoaded",this,t),e(this)},h.onerror=function(){i(s)},h.src=s})}function O(t){return new Promise((e,i)=>{let s,h,o,r;return s=new Audio,h=function(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}(s),(t=[].concat(t).reduce((t,e)=>t||(h[y(e)]?e:null),0))?(o=w(p,t),v[o]?e(v[o]):(s.addEventListener("canplay",function(){r=_(o,window.location.href),v[x(t)]=v[o]=v[r]=this,n("assetLoaded",this,t),e(this)}),s.onerror=function(){i(o)},s.src=o,void s.load())):i(t)})}function P(t){let e,i;return A(),e=w(m,t),j[e]?Promise.resolve(j[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=_(e,window.location.href),"object"==typeof s&&f.set(s,i),j[x(t)]=j[e]=j[i]=s,n("assetLoaded",s,t),s))}const E=()=>{};function k(){let t=h();o().clearRect(0,0,t.width,t.height)}let L={},M={},I={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function z(t){let e=I[t.which];M[e]=!0,L[e]&&L[e](t)}function D(t){M[I[t.which]]=!1}function R(){M={}}function W(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function C(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}let T=[],N=[],X={},Y=[],U={},q={0:"left",1:"middle",2:"right"},F={x:0,y:0,radius:5};function K(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=F.x-Math.max(e,Math.min(F.x,e+t.width)),n=F.y-Math.max(i,Math.min(F.y,i+t.height));return s*s+n*n=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(F):K(t))return t}function B(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!0,Q(t,"onDown")}function H(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!1,Q(t,"onUp")}function J(t){Q(t,"onOver")}function G(){U={}}function Q(t,e){let i,s,n=h();if(!n)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let o=n.height/n.offsetHeight,r=n.getBoundingClientRect(),a=(i-r.left)*o,c=(s-r.top)*o;F.x=a,F.y=c,t.preventDefault();let l=$();l&&l[e]&&l[e](t),X[e]&&X[e](t,l)}class V{constructor({create:t,maxSize:e=1024}={}){this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function Z(t){return new V(t)}function tt(t,e){let i=[],s=e.x+e.width/2,n=e.y+e.height/2,h=t.y=e.y,o=t.y+t.height>=n&&t.y=e.x&&(h&&i.push(0),o&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1}}function it(t){return new et(t)}it.prototype=et.prototype,it.class=et;class st{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return nt(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function nt(t,e,i={}){let s=new st(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}nt.prototype=st.prototype,nt.class=st;class ht{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:n,ddx:h,ddy:r,width:a,height:c,image:l}=t;this.position=nt(e,i),this.velocity=nt(s,n),this.acceleration=nt(h,r),this.width=this.height=this.rotation=0,this.ttl=1/0,this.anchor={x:0,y:0},this.context=o();for(let e in t)this[e]=t[e];l&&(this.width=void 0!==a?a:l.width,this.height=void 0!==c?c:l.height),this.sx=0,this.sy=0}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,n=t.y;return t.anchor&&(s-=t.width*t.anchor.x,n-=t.height*t.anchor.y),es&&in}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;this.context.save(),this.context.translate(this.viewX,this.viewY),this.rotation&&this.context.rotate(this.rotation),this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function ot(t){return new ht(t)}function rt(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],n=+i[1],h=s;if(s=n;h--)e.push(h);return e}ot.prototype=ht.prototype,ot.class=ht;class at{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:n}={}){this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(n)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:n,loop:h}=t[i];e=[],[].concat(s).map(t=>{e=e.concat(rt(t))}),this.animations[i]=a({spriteSheet:this,frames:e,frameRate:n,loop:h})}}}function ct(t){return new at(t)}return ct.prototype=at.prototype,ct.class=at,{Animation:a,imageAssets:b,audioAssets:v,dataAssets:j,setImagePath:function(t){g=t},setAudioPath:function(t){p=t},setDataPath:function(t){m=t},loadImage:S,loadAudio:O,loadData:P,load:function(...t){return A(),Promise.all(t.map(t=>{let e=y([].concat(t)[0]);return e.match(c)?S(t):e.match(l)?O(t):P(t)}))},init:function(i){return t=document.getElementById(i)||i||document.querySelector("canvas"),(e=t.getContext("2d")).imageSmoothingEnabled=!1,n("init"),{canvas:t,context:e}},getCanvas:h,getContext:o,on:s,off:function(t,e){let s;!i[t]||(s=i[t].indexOf(e))<0||i[t].splice(s,1)},emit:n,GameLoop:function({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){let h,o,r,a,c,l=0,d=1e3/t,u=1/t,f=e?k:E;function g(){if(o=requestAnimationFrame(g),r=performance.now(),a=r-h,h=r,!(a>1e3)){for(n("tick"),l+=a;l>=d;)c.update(u),l-=d;f(),c.render()}}return c={update:i,render:s,isStopped:!0,start(){h=performance.now(),this.isStopped=!1,requestAnimationFrame(g)},stop(){this.isStopped=!0,cancelAnimationFrame(o)}}},keyMap:I,initKeys:function(){let t;for(t=0;t<26;t++)I[65+t]=(10+t).toString(36);for(t=0;t<10;t++)I[48+t]=""+t;window.addEventListener("keydown",z),window.addEventListener("keyup",D),window.addEventListener("blur",R)},bindKeys:function(t,e){[].concat(t).map(t=>L[t]=e)},unbindKeys:function(t){[].concat(t).map(t=>L[t]=0)},keyPressed:function(t){return!!M[t]},registerPlugin:function(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let n=i(t,e,...s);return n||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),n=i["_o"+s].call(this,...e);return this._aInc(this,s,n,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))},unregisterPlugin:function(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);t.startsWith("before")?C(i._inc[s].before,e[t]):t.startsWith("after")&&C(i._inc[s].after,e[t])})},extendObject:function(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})},initPointer:function(){let t=h();t.addEventListener("mousedown",B),t.addEventListener("touchstart",B),t.addEventListener("mouseup",H),t.addEventListener("touchend",H),t.addEventListener("blur",G),t.addEventListener("mousemove",J),t.addEventListener("touchmove",J),s("tick",()=>{N.length=0,T.map(t=>{N.push(t)}),T.length=0})},pointer:F,track:function(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){T.push(this),this._r()},Y.push(t))})},untrack:function(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=Y.indexOf(t);-1!==e&&Y.splice(e,1)})},pointerOver:function(t){return!!Y.includes(t)&&$()===t},onPointerDown:function(t){X.onDown=t},onPointerUp:function(t){X.onUp=t},pointerPressed:function(t){return!!U[t]},Pool:Z,Quadtree:it,Sprite:ot,SpriteSheet:ct,setStoreItem:function(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))},getStoreItem:function(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e},TileEngine:function(t={}){let{width:e,height:i,tilewidth:s,tileheight:n,context:r=o(),tilesets:a,layers:c}=t,l=e*s,d=i*n,u=document.createElement("canvas"),f=u.getContext("2d");u.width=l,u.height=d;let g={},p={},m=[],_=Object.assign({context:r,mapwidth:l,mapheight:d,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),l-h().width),m.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),d-h().height),m.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),b(u)},renderLayer(t){let e=p[t],i=g[t];e||((e=document.createElement("canvas")).width=l,e.height=d,p[t]=e,_._r(i,e.getContext("2d"))),b(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let n=w(s),h=y(i),o=w(s+e.height),r=y(i+e.width),a=g[t];for(let t=n;t<=o;t++)for(let e=h;e<=r;e++)if(a.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||w(e.y),s=e.col||y(e.x);return g[t]?g[t].data[s+i*_.width]:-1},setTileAtLayer(t,e,i){let s=e.row||w(e.y),n=e.col||y(e.x);g[t]&&(this._d=!0,g[t].data[n+s*_.width]=i)},addObject(t){m.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=m.indexOf(t);-1!==e&&(m.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=_.tilesets.length-1;e>=0&&(s=_.tilesets[e],!(t/s.firstgid>=1));e--);let n=s.tilewidth||_.tilewidth,h=s.tileheight||_.tileheight,o=s.margin||0,r=s.image,a=t-s.firstgid,c=s.columns||r.width/(n+o)|0,l=i%_.width*n,d=(i/_.width|0)*h,u=a%c*(n+o),f=(a/c|0)*(h+o);e.drawImage(r,u,f,n,h,l,d,n,h)}),e.restore()},_p:x},t);function w(t){return t/_.tileheight|0}function y(t){return t/_.tilewidth|0}function x(){_.layers&&_.layers.map(t=>{g[t.name]=t,!1!==t.visible&&_._r(t,f)})}function b(t){const{width:e,height:i}=h(),s=Math.min(t.width,e),n=Math.min(t.height,i);_.context.drawImage(t,_.sx,_.sy,s,n,0,0,s,n)}return _.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){let t=window.__k.d[window.__k.u(e.source,i)];Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){let t=window.__k.i[window.__k.u(e.image,i)];e.image=t}}),x(),_},Vector:nt}}(); \ No newline at end of file +var kontra=function(){"use strict";let t,e,i={};function s(t,e){i[t]=i[t]||[],i[t].push(e)}function n(t,...e){i[t]&&i[t].map(t=>t(...e))}function h(){return t}function r(){return e}class o{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:n,height:h,margin:r=0}=t.frame;this.width=n,this.height=h,this.margin=r,this._f=0,this._a=0}clone(){return a(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:n=r()}={}){let h=this.frames[this._f]/this.spriteSheet._f|0,o=this.frames[this._f]%this.spriteSheet._f|0;n.drawImage(this.spriteSheet.image,o*this.width+(2*o+1)*this.margin,h*this.height+(2*h+1)*this.margin,this.width,this.height,t,e,i,s)}}function a(t){return new o(t)}a.prototype=o.prototype,a.class=o;let c=/(jpeg|jpg|gif|png)$/,l=/(wav|mp3|ogg|aac)$/,d=/^\//,u=/\/$/,f=new WeakMap,g="",p="",m="";function _(t,e){return new URL(t,e).href}function w(t,e){return[t.replace(u,""),t?e.replace(d,""):e].filter(t=>t).join("/")}function y(t){return t.split(".").pop()}function x(t){let e=t.replace("."+y(t),"");return 2==e.split("/").length?e.replace(d,""):e}let b={},v={},j={};function A(){window.__k||(window.__k={dm:f,u:_,d:j,i:b})}function S(t){return A(),new Promise((e,i)=>{let s,h,r;if(s=w(g,t),b[s])return e(b[s]);(h=new Image).onload=function(){r=_(s,window.location.href),b[x(t)]=b[s]=b[r]=this,n("assetLoaded",this,t),e(this)},h.onerror=function(){i(s)},h.src=s})}function O(t){return new Promise((e,i)=>{let s,h,r,o;return s=new Audio,h=function(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}(s),(t=[].concat(t).reduce((t,e)=>t||(h[y(e)]?e:null),0))?(r=w(p,t),v[r]?e(v[r]):(s.addEventListener("canplay",function(){o=_(r,window.location.href),v[x(t)]=v[r]=v[o]=this,n("assetLoaded",this,t),e(this)}),s.onerror=function(){i(r)},s.src=r,void s.load())):i(t)})}function P(t){let e,i;return A(),e=w(m,t),j[e]?Promise.resolve(j[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=_(e,window.location.href),"object"==typeof s&&f.set(s,i),j[x(t)]=j[e]=j[i]=s,n("assetLoaded",s,t),s))}const E=()=>{};function M(){let t=h();r().clearRect(0,0,t.width,t.height)}let k={},L={},I={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function z(t){let e=I[t.which];L[e]=!0,k[e]&&k[e](t)}function D(t){L[I[t.which]]=!1}function R(){L={}}function W(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function C(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}let X=[],Y=[],T={},N=[],U={},q={0:"left",1:"middle",2:"right"},F={x:0,y:0,radius:5};function K(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=F.x-Math.max(e,Math.min(F.x,e+t.width)),n=F.y-Math.max(i,Math.min(F.y,i+t.height));return s*s+n*n=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(F):K(t))return t}function B(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!0,Q(t,"onDown")}function H(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!1,Q(t,"onUp")}function J(t){Q(t,"onOver")}function G(){U={}}function Q(t,e){let i,s,n=h();if(!n)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let r=n.height/n.offsetHeight,o=n.getBoundingClientRect(),a=(i-o.left)*r,c=(s-o.top)*r;F.x=a,F.y=c,t.preventDefault();let l=$();l&&l[e]&&l[e](t),T[e]&&T[e](t,l)}class V{constructor({create:t,maxSize:e=1024}={}){this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function Z(t){return new V(t)}function tt(t,e){let i=[],s=e.x+e.width/2,n=e.y+e.height/2,h=t.y=e.y,r=t.y+t.height>=n&&t.y=e.x&&(h&&i.push(0),r&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1}}function it(t){return new et(t)}it.prototype=et.prototype,it.class=et;class st{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return nt(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function nt(t,e,i={}){let s=new st(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}nt.prototype=st.prototype,nt.class=st;class ht{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:n,ddx:h,ddy:o,width:a,height:c,image:l}=t;this.position=nt(e,i),this.velocity=nt(s,n),this.acceleration=nt(h,o),this.width=this.height=this.rotation=0,this.ttl=1/0,this.scaleX=this.scaleY=1,this.anchor={x:0,y:0},this.context=r();for(let e in t)this[e]=t[e];l&&(this.width=void 0!==a?a:l.width,this.height=void 0!==c?c:l.height),this.sx=0,this.sy=0}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}get width(){return this._w}get height(){return this._h}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}set width(t){this.scaleX=t<0?-1:1,this._w=Math.abs(t)}set height(t){this.scaleY=t<0?-1:1,this._h=Math.abs(t)}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,n=t.y;return t.anchor&&(s-=t.width*t.anchor.x,n-=t.height*t.anchor.y),es&&in}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;this.context.save(),this.context.translate(this.viewX,this.viewY),this.context.scale(this.scaleX,this.scaleY),this.rotation&&this.context.rotate(this.rotation),this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function rt(t){return new ht(t)}function ot(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],n=+i[1],h=s;if(s=n;h--)e.push(h);return e}rt.prototype=ht.prototype,rt.class=ht;class at{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:n}={}){this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(n)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:n,loop:h}=t[i];e=[],[].concat(s).map(t=>{e=e.concat(ot(t))}),this.animations[i]=a({spriteSheet:this,frames:e,frameRate:n,loop:h})}}}function ct(t){return new at(t)}return ct.prototype=at.prototype,ct.class=at,{Animation:a,imageAssets:b,audioAssets:v,dataAssets:j,setImagePath:function(t){g=t},setAudioPath:function(t){p=t},setDataPath:function(t){m=t},loadImage:S,loadAudio:O,loadData:P,load:function(...t){return A(),Promise.all(t.map(t=>{let e=y([].concat(t)[0]);return e.match(c)?S(t):e.match(l)?O(t):P(t)}))},init:function(i){return t=document.getElementById(i)||i||document.querySelector("canvas"),(e=t.getContext("2d")).imageSmoothingEnabled=!1,n("init"),{canvas:t,context:e}},getCanvas:h,getContext:r,on:s,off:function(t,e){let s;!i[t]||(s=i[t].indexOf(e))<0||i[t].splice(s,1)},emit:n,GameLoop:function({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){let h,r,o,a,c,l=0,d=1e3/t,u=1/t,f=e?M:E;function g(){if(r=requestAnimationFrame(g),o=performance.now(),a=o-h,h=o,!(a>1e3)){for(n("tick"),l+=a;l>=d;)c.update(u),l-=d;f(),c.render()}}return c={update:i,render:s,isStopped:!0,start(){h=performance.now(),this.isStopped=!1,requestAnimationFrame(g)},stop(){this.isStopped=!0,cancelAnimationFrame(r)}}},keyMap:I,initKeys:function(){let t;for(t=0;t<26;t++)I[65+t]=(10+t).toString(36);for(t=0;t<10;t++)I[48+t]=""+t;window.addEventListener("keydown",z),window.addEventListener("keyup",D),window.addEventListener("blur",R)},bindKeys:function(t,e){[].concat(t).map(t=>k[t]=e)},unbindKeys:function(t){[].concat(t).map(t=>k[t]=0)},keyPressed:function(t){return!!L[t]},registerPlugin:function(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let n=i(t,e,...s);return n||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),n=i["_o"+s].call(this,...e);return this._aInc(this,s,n,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))},unregisterPlugin:function(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);t.startsWith("before")?C(i._inc[s].before,e[t]):t.startsWith("after")&&C(i._inc[s].after,e[t])})},extendObject:function(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})},initPointer:function(){let t=h();t.addEventListener("mousedown",B),t.addEventListener("touchstart",B),t.addEventListener("mouseup",H),t.addEventListener("touchend",H),t.addEventListener("blur",G),t.addEventListener("mousemove",J),t.addEventListener("touchmove",J),s("tick",()=>{Y.length=0,X.map(t=>{Y.push(t)}),X.length=0})},pointer:F,track:function(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){X.push(this),this._r()},N.push(t))})},untrack:function(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=N.indexOf(t);-1!==e&&N.splice(e,1)})},pointerOver:function(t){return!!N.includes(t)&&$()===t},onPointerDown:function(t){T.onDown=t},onPointerUp:function(t){T.onUp=t},pointerPressed:function(t){return!!U[t]},Pool:Z,Quadtree:it,Sprite:rt,SpriteSheet:ct,setStoreItem:function(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))},getStoreItem:function(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e},TileEngine:function(t={}){let{width:e,height:i,tilewidth:s,tileheight:n,context:o=r(),tilesets:a,layers:c}=t,l=e*s,d=i*n,u=document.createElement("canvas"),f=u.getContext("2d");u.width=l,u.height=d;let g={},p={},m=[],_=Object.assign({context:o,mapwidth:l,mapheight:d,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),l-h().width),m.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),d-h().height),m.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),b(u)},renderLayer(t){let e=p[t],i=g[t];e||((e=document.createElement("canvas")).width=l,e.height=d,p[t]=e,_._r(i,e.getContext("2d"))),b(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let n=w(s),h=y(i),r=w(s+e.height),o=y(i+e.width),a=g[t];for(let t=n;t<=r;t++)for(let e=h;e<=o;e++)if(a.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||w(e.y),s=e.col||y(e.x);return g[t]?g[t].data[s+i*_.width]:-1},setTileAtLayer(t,e,i){let s=e.row||w(e.y),n=e.col||y(e.x);g[t]&&(this._d=!0,g[t].data[n+s*_.width]=i)},addObject(t){m.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=m.indexOf(t);-1!==e&&(m.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=_.tilesets.length-1;e>=0&&(s=_.tilesets[e],!(t/s.firstgid>=1));e--);let n=s.tilewidth||_.tilewidth,h=s.tileheight||_.tileheight,r=s.margin||0,o=s.image,a=t-s.firstgid,c=s.columns||o.width/(n+r)|0,l=i%_.width*n,d=(i/_.width|0)*h,u=a%c*(n+r),f=(a/c|0)*(h+r);e.drawImage(o,u,f,n,h,l,d,n,h)}),e.restore()},_p:x},t);function w(t){return t/_.tileheight|0}function y(t){return t/_.tilewidth|0}function x(){_.layers&&_.layers.map(t=>{g[t.name]=t,!1!==t.visible&&_._r(t,f)})}function b(t){const{width:e,height:i}=h(),s=Math.min(t.width,e),n=Math.min(t.height,i);_.context.drawImage(t,_.sx,_.sy,s,n,0,0,s,n)}return _.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){let t=window.__k.d[window.__k.u(e.source,i)];Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){let t=window.__k.i[window.__k.u(e.image,i)];e.image=t}}),x(),_},Vector:nt}}(); \ No newline at end of file diff --git a/kontra.js b/kontra.js index 3dace08c..9a8bd5cf 100644 --- a/kontra.js +++ b/kontra.js @@ -2399,30 +2399,31 @@ class Sprite { // defaults /** - * The width of the sprite. If the sprite is a [rectangle sprite](api/sprite/#rectangle-sprite), it uses the passed in value. For an [image sprite](api/sprite/#image-sprite) it is the width of the image. And for an [animation sprite](api/sprite/#animation-sprite) it is the width of a single frame of the animation. + * The rotation of the sprite around the origin in radians. * @memberof Sprite - * @property {Number} width + * @property {Number} rotation */ + this.width = this.height = this.rotation = 0; /** - * The height of the sprite. If the sprite is a [rectangle sprite](api/sprite/#rectangle-sprite), it uses the passed in value. For an [image sprite](api/sprite/#image-sprite) it is the height of the image. And for an [animation sprite](api/sprite/#animation-sprite) it is the height of a single frame of the animation. + * How may frames the sprite should be alive. Primarily used by kontra.Pool to know when to recycle an object. * @memberof Sprite - * @property {Number} height + * @property {Number} ttl */ + this.ttl = Infinity; /** - * The rotation of the sprite around the origin in radians. + * The horizontal scaling factor. A negative value flips the sprite across the vertical axis. * @memberof Sprite - * @property {Number} rotation + * @property {Number} scaleX */ - this.width = this.height = this.rotation = 0; /** - * How may frames the sprite should be alive. Primarily used by kontra.Pool to know when to recycle an object. + * The vertical scaling factor. A negative value flips the sprite across the horizontal axis. * @memberof Sprite - * @property {Number} ttl + * @property {Number} scaleY */ - this.ttl = Infinity; + this.scaleX = this.scaleY = 1; /** * The x and y origin of the sprite. {x:0, y:0} is the top left corner of the sprite, {x:1, y:1} is the bottom right corner. @@ -2622,6 +2623,24 @@ class Sprite { return this.y - this.sy; } + /** + * The width of the sprite. If the sprite is a [rectangle sprite](api/sprite/#rectangle-sprite), it uses the passed in value. For an [image sprite](api/sprite/#image-sprite) it is the width of the image. And for an [animation sprite](api/sprite/#animation-sprite) it is the width of a single frame of the animation. + * @memberof Sprite + * @property {Number} width + */ + get width() { + return this._w; + } + + /** + * The height of the sprite. If the sprite is a [rectangle sprite](api/sprite/#rectangle-sprite), it uses the passed in value. For an [image sprite](api/sprite/#image-sprite) it is the height of the image. And for an [animation sprite](api/sprite/#animation-sprite) it is the height of a single frame of the animation. + * @memberof Sprite + * @property {Number} height + */ + get height() { + return this._h; + } + set x(value) { this.position.x = value; } @@ -2672,6 +2691,15 @@ class Sprite { return; } + set width(value) { + this.scaleX = value < 0 ? -1 : 1; + this._w = Math.abs(value); + } + set height(value) { + this.scaleY = value < 0 ? -1 : 1; + this._h = Math.abs(value); + } + /** * Check if the sprite is alive. Primarily used by kontra.Pool to know when to recycle an object. * @memberof Sprite @@ -2911,6 +2939,7 @@ class Sprite { this.context.save(); this.context.translate(this.viewX, this.viewY); + this.context.scale(this.scaleX, this.scaleY); if (this.rotation) { this.context.rotate(this.rotation); diff --git a/kontra.min.js b/kontra.min.js index 1a2d45a8..a08dda61 100644 --- a/kontra.min.js +++ b/kontra.min.js @@ -1 +1 @@ -var kontra=function(){"use strict";let t,e,i={};function s(t,e){i[t]=i[t]||[],i[t].push(e)}function n(t,...e){i[t]&&i[t].map(t=>t(...e))}function h(){return t}function o(){return e}class r{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:n,height:h,margin:o=0}=t.frame;this.width=n,this.height=h,this.margin=o,this._f=0,this._a=0}clone(){return a(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:n=o()}={}){let h=this.frames[this._f]/this.spriteSheet._f|0,r=this.frames[this._f]%this.spriteSheet._f|0;n.drawImage(this.spriteSheet.image,r*this.width+(2*r+1)*this.margin,h*this.height+(2*h+1)*this.margin,this.width,this.height,t,e,i,s)}}function a(t){return new r(t)}a.prototype=r.prototype,a.class=r;let c=/(jpeg|jpg|gif|png)$/,l=/(wav|mp3|ogg|aac)$/,d=/^\//,u=/\/$/,f=new WeakMap,g="",p="",m="";function _(t,e){return new URL(t,e).href}function w(t,e){return[t.replace(u,""),t?e.replace(d,""):e].filter(t=>t).join("/")}function y(t){return t.split(".").pop()}function x(t){let e=t.replace("."+y(t),"");return 2==e.split("/").length?e.replace(d,""):e}let b={},v={},j={};function A(){window.__k||(window.__k={dm:f,u:_,d:j,i:b})}function S(t){return A(),new Promise((e,i)=>{let s,h,o;if(s=w(g,t),b[s])return e(b[s]);(h=new Image).onload=function(){o=_(s,window.location.href),b[x(t)]=b[s]=b[o]=this,n("assetLoaded",this,t),e(this)},h.onerror=function(){i(s)},h.src=s})}function O(t){return new Promise((e,i)=>{let s,h,o,r;return s=new Audio,h=function(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}(s),(t=[].concat(t).reduce((t,e)=>t||(h[y(e)]?e:null),0))?(o=w(p,t),v[o]?e(v[o]):(s.addEventListener("canplay",function(){r=_(o,window.location.href),v[x(t)]=v[o]=v[r]=this,n("assetLoaded",this,t),e(this)}),s.onerror=function(){i(o)},s.src=o,void s.load())):i(t)})}function P(t){let e,i;return A(),e=w(m,t),j[e]?Promise.resolve(j[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=_(e,window.location.href),"object"==typeof s&&f.set(s,i),j[x(t)]=j[e]=j[i]=s,n("assetLoaded",s,t),s))}const E=()=>{};function k(){let t=h();o().clearRect(0,0,t.width,t.height)}let L={},M={},I={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function z(t){let e=I[t.which];M[e]=!0,L[e]&&L[e](t)}function D(t){M[I[t.which]]=!1}function R(){M={}}function W(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function C(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}let T=[],N=[],X={},Y=[],U={},q={0:"left",1:"middle",2:"right"},F={x:0,y:0,radius:5};function K(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=F.x-Math.max(e,Math.min(F.x,e+t.width)),n=F.y-Math.max(i,Math.min(F.y,i+t.height));return s*s+n*n=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(F):K(t))return t}function B(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!0,Q(t,"onDown")}function H(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!1,Q(t,"onUp")}function J(t){Q(t,"onOver")}function G(){U={}}function Q(t,e){let i,s,n=h();if(!n)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let o=n.height/n.offsetHeight,r=n.getBoundingClientRect(),a=(i-r.left)*o,c=(s-r.top)*o;F.x=a,F.y=c,t.preventDefault();let l=$();l&&l[e]&&l[e](t),X[e]&&X[e](t,l)}class V{constructor({create:t,maxSize:e=1024}={}){this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function Z(t){return new V(t)}function tt(t,e){let i=[],s=e.x+e.width/2,n=e.y+e.height/2,h=t.y=e.y,o=t.y+t.height>=n&&t.y=e.x&&(h&&i.push(0),o&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1}}function it(t){return new et(t)}it.prototype=et.prototype,it.class=et;class st{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return nt(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function nt(t,e,i={}){let s=new st(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}nt.prototype=st.prototype,nt.class=st;class ht{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:n,ddx:h,ddy:r,width:a,height:c,image:l}=t;this.position=nt(e,i),this.velocity=nt(s,n),this.acceleration=nt(h,r),this.width=this.height=this.rotation=0,this.ttl=1/0,this.anchor={x:0,y:0},this.context=o();for(let e in t)this[e]=t[e];l&&(this.width=void 0!==a?a:l.width,this.height=void 0!==c?c:l.height),this.sx=0,this.sy=0}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,n=t.y;return t.anchor&&(s-=t.width*t.anchor.x,n-=t.height*t.anchor.y),es&&in}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;this.context.save(),this.context.translate(this.viewX,this.viewY),this.rotation&&this.context.rotate(this.rotation),this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function ot(t){return new ht(t)}function rt(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],n=+i[1],h=s;if(s=n;h--)e.push(h);return e}ot.prototype=ht.prototype,ot.class=ht;class at{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:n}={}){this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(n)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:n,loop:h}=t[i];e=[],[].concat(s).map(t=>{e=e.concat(rt(t))}),this.animations[i]=a({spriteSheet:this,frames:e,frameRate:n,loop:h})}}}function ct(t){return new at(t)}return ct.prototype=at.prototype,ct.class=at,{Animation:a,imageAssets:b,audioAssets:v,dataAssets:j,setImagePath:function(t){g=t},setAudioPath:function(t){p=t},setDataPath:function(t){m=t},loadImage:S,loadAudio:O,loadData:P,load:function(...t){return A(),Promise.all(t.map(t=>{let e=y([].concat(t)[0]);return e.match(c)?S(t):e.match(l)?O(t):P(t)}))},init:function(i){return t=document.getElementById(i)||i||document.querySelector("canvas"),(e=t.getContext("2d")).imageSmoothingEnabled=!1,n("init"),{canvas:t,context:e}},getCanvas:h,getContext:o,on:s,off:function(t,e){let s;!i[t]||(s=i[t].indexOf(e))<0||i[t].splice(s,1)},emit:n,GameLoop:function({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){let h,o,r,a,c,l=0,d=1e3/t,u=1/t,f=e?k:E;function g(){if(o=requestAnimationFrame(g),r=performance.now(),a=r-h,h=r,!(a>1e3)){for(n("tick"),l+=a;l>=d;)c.update(u),l-=d;f(),c.render()}}return c={update:i,render:s,isStopped:!0,start(){h=performance.now(),this.isStopped=!1,requestAnimationFrame(g)},stop(){this.isStopped=!0,cancelAnimationFrame(o)}}},keyMap:I,initKeys:function(){let t;for(t=0;t<26;t++)I[65+t]=(10+t).toString(36);for(t=0;t<10;t++)I[48+t]=""+t;window.addEventListener("keydown",z),window.addEventListener("keyup",D),window.addEventListener("blur",R)},bindKeys:function(t,e){[].concat(t).map(t=>L[t]=e)},unbindKeys:function(t){[].concat(t).map(t=>L[t]=0)},keyPressed:function(t){return!!M[t]},registerPlugin:function(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let n=i(t,e,...s);return n||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),n=i["_o"+s].call(this,...e);return this._aInc(this,s,n,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))},unregisterPlugin:function(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);t.startsWith("before")?C(i._inc[s].before,e[t]):t.startsWith("after")&&C(i._inc[s].after,e[t])})},extendObject:function(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})},initPointer:function(){let t=h();t.addEventListener("mousedown",B),t.addEventListener("touchstart",B),t.addEventListener("mouseup",H),t.addEventListener("touchend",H),t.addEventListener("blur",G),t.addEventListener("mousemove",J),t.addEventListener("touchmove",J),s("tick",()=>{N.length=0,T.map(t=>{N.push(t)}),T.length=0})},pointer:F,track:function(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){T.push(this),this._r()},Y.push(t))})},untrack:function(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=Y.indexOf(t);-1!==e&&Y.splice(e,1)})},pointerOver:function(t){return!!Y.includes(t)&&$()===t},onPointerDown:function(t){X.onDown=t},onPointerUp:function(t){X.onUp=t},pointerPressed:function(t){return!!U[t]},Pool:Z,Quadtree:it,Sprite:ot,SpriteSheet:ct,setStoreItem:function(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))},getStoreItem:function(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e},TileEngine:function(t={}){let{width:e,height:i,tilewidth:s,tileheight:n,context:r=o(),tilesets:a,layers:c}=t,l=e*s,d=i*n,u=document.createElement("canvas"),f=u.getContext("2d");u.width=l,u.height=d;let g={},p={},m=[],_=Object.assign({context:r,mapwidth:l,mapheight:d,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),l-h().width),m.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),d-h().height),m.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),b(u)},renderLayer(t){let e=p[t],i=g[t];e||((e=document.createElement("canvas")).width=l,e.height=d,p[t]=e,_._r(i,e.getContext("2d"))),b(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let n=w(s),h=y(i),o=w(s+e.height),r=y(i+e.width),a=g[t];for(let t=n;t<=o;t++)for(let e=h;e<=r;e++)if(a.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||w(e.y),s=e.col||y(e.x);return g[t]?g[t].data[s+i*_.width]:-1},setTileAtLayer(t,e,i){let s=e.row||w(e.y),n=e.col||y(e.x);g[t]&&(this._d=!0,g[t].data[n+s*_.width]=i)},addObject(t){m.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=m.indexOf(t);-1!==e&&(m.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=_.tilesets.length-1;e>=0&&(s=_.tilesets[e],!(t/s.firstgid>=1));e--);let n=s.tilewidth||_.tilewidth,h=s.tileheight||_.tileheight,o=s.margin||0,r=s.image,a=t-s.firstgid,c=s.columns||r.width/(n+o)|0,l=i%_.width*n,d=(i/_.width|0)*h,u=a%c*(n+o),f=(a/c|0)*(h+o);e.drawImage(r,u,f,n,h,l,d,n,h)}),e.restore()},_p:x},t);function w(t){return t/_.tileheight|0}function y(t){return t/_.tilewidth|0}function x(){_.layers&&_.layers.map(t=>{g[t.name]=t,!1!==t.visible&&_._r(t,f)})}function b(t){const{width:e,height:i}=h(),s=Math.min(t.width,e),n=Math.min(t.height,i);_.context.drawImage(t,_.sx,_.sy,s,n,0,0,s,n)}return _.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){let t=window.__k.d[window.__k.u(e.source,i)];Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){let t=window.__k.i[window.__k.u(e.image,i)];e.image=t}}),x(),_},Vector:nt}}(); \ No newline at end of file +var kontra=function(){"use strict";let t,e,i={};function s(t,e){i[t]=i[t]||[],i[t].push(e)}function n(t,...e){i[t]&&i[t].map(t=>t(...e))}function h(){return t}function r(){return e}class o{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:n,height:h,margin:r=0}=t.frame;this.width=n,this.height=h,this.margin=r,this._f=0,this._a=0}clone(){return a(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:n=r()}={}){let h=this.frames[this._f]/this.spriteSheet._f|0,o=this.frames[this._f]%this.spriteSheet._f|0;n.drawImage(this.spriteSheet.image,o*this.width+(2*o+1)*this.margin,h*this.height+(2*h+1)*this.margin,this.width,this.height,t,e,i,s)}}function a(t){return new o(t)}a.prototype=o.prototype,a.class=o;let c=/(jpeg|jpg|gif|png)$/,l=/(wav|mp3|ogg|aac)$/,d=/^\//,u=/\/$/,f=new WeakMap,g="",p="",m="";function _(t,e){return new URL(t,e).href}function w(t,e){return[t.replace(u,""),t?e.replace(d,""):e].filter(t=>t).join("/")}function y(t){return t.split(".").pop()}function x(t){let e=t.replace("."+y(t),"");return 2==e.split("/").length?e.replace(d,""):e}let b={},v={},j={};function A(){window.__k||(window.__k={dm:f,u:_,d:j,i:b})}function S(t){return A(),new Promise((e,i)=>{let s,h,r;if(s=w(g,t),b[s])return e(b[s]);(h=new Image).onload=function(){r=_(s,window.location.href),b[x(t)]=b[s]=b[r]=this,n("assetLoaded",this,t),e(this)},h.onerror=function(){i(s)},h.src=s})}function O(t){return new Promise((e,i)=>{let s,h,r,o;return s=new Audio,h=function(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}(s),(t=[].concat(t).reduce((t,e)=>t||(h[y(e)]?e:null),0))?(r=w(p,t),v[r]?e(v[r]):(s.addEventListener("canplay",function(){o=_(r,window.location.href),v[x(t)]=v[r]=v[o]=this,n("assetLoaded",this,t),e(this)}),s.onerror=function(){i(r)},s.src=r,void s.load())):i(t)})}function P(t){let e,i;return A(),e=w(m,t),j[e]?Promise.resolve(j[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=_(e,window.location.href),"object"==typeof s&&f.set(s,i),j[x(t)]=j[e]=j[i]=s,n("assetLoaded",s,t),s))}const E=()=>{};function M(){let t=h();r().clearRect(0,0,t.width,t.height)}let k={},L={},I={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function z(t){let e=I[t.which];L[e]=!0,k[e]&&k[e](t)}function D(t){L[I[t.which]]=!1}function R(){L={}}function W(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function C(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}let X=[],Y=[],T={},N=[],U={},q={0:"left",1:"middle",2:"right"},F={x:0,y:0,radius:5};function K(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=F.x-Math.max(e,Math.min(F.x,e+t.width)),n=F.y-Math.max(i,Math.min(F.y,i+t.height));return s*s+n*n=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(F):K(t))return t}function B(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!0,Q(t,"onDown")}function H(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!1,Q(t,"onUp")}function J(t){Q(t,"onOver")}function G(){U={}}function Q(t,e){let i,s,n=h();if(!n)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let r=n.height/n.offsetHeight,o=n.getBoundingClientRect(),a=(i-o.left)*r,c=(s-o.top)*r;F.x=a,F.y=c,t.preventDefault();let l=$();l&&l[e]&&l[e](t),T[e]&&T[e](t,l)}class V{constructor({create:t,maxSize:e=1024}={}){this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function Z(t){return new V(t)}function tt(t,e){let i=[],s=e.x+e.width/2,n=e.y+e.height/2,h=t.y=e.y,r=t.y+t.height>=n&&t.y=e.x&&(h&&i.push(0),r&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1}}function it(t){return new et(t)}it.prototype=et.prototype,it.class=et;class st{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return nt(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function nt(t,e,i={}){let s=new st(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}nt.prototype=st.prototype,nt.class=st;class ht{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:n,ddx:h,ddy:o,width:a,height:c,image:l}=t;this.position=nt(e,i),this.velocity=nt(s,n),this.acceleration=nt(h,o),this.width=this.height=this.rotation=0,this.ttl=1/0,this.scaleX=this.scaleY=1,this.anchor={x:0,y:0},this.context=r();for(let e in t)this[e]=t[e];l&&(this.width=void 0!==a?a:l.width,this.height=void 0!==c?c:l.height),this.sx=0,this.sy=0}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}get width(){return this._w}get height(){return this._h}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}set width(t){this.scaleX=t<0?-1:1,this._w=Math.abs(t)}set height(t){this.scaleY=t<0?-1:1,this._h=Math.abs(t)}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,n=t.y;return t.anchor&&(s-=t.width*t.anchor.x,n-=t.height*t.anchor.y),es&&in}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;this.context.save(),this.context.translate(this.viewX,this.viewY),this.context.scale(this.scaleX,this.scaleY),this.rotation&&this.context.rotate(this.rotation),this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function rt(t){return new ht(t)}function ot(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],n=+i[1],h=s;if(s=n;h--)e.push(h);return e}rt.prototype=ht.prototype,rt.class=ht;class at{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:n}={}){this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(n)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:n,loop:h}=t[i];e=[],[].concat(s).map(t=>{e=e.concat(ot(t))}),this.animations[i]=a({spriteSheet:this,frames:e,frameRate:n,loop:h})}}}function ct(t){return new at(t)}return ct.prototype=at.prototype,ct.class=at,{Animation:a,imageAssets:b,audioAssets:v,dataAssets:j,setImagePath:function(t){g=t},setAudioPath:function(t){p=t},setDataPath:function(t){m=t},loadImage:S,loadAudio:O,loadData:P,load:function(...t){return A(),Promise.all(t.map(t=>{let e=y([].concat(t)[0]);return e.match(c)?S(t):e.match(l)?O(t):P(t)}))},init:function(i){return t=document.getElementById(i)||i||document.querySelector("canvas"),(e=t.getContext("2d")).imageSmoothingEnabled=!1,n("init"),{canvas:t,context:e}},getCanvas:h,getContext:r,on:s,off:function(t,e){let s;!i[t]||(s=i[t].indexOf(e))<0||i[t].splice(s,1)},emit:n,GameLoop:function({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){let h,r,o,a,c,l=0,d=1e3/t,u=1/t,f=e?M:E;function g(){if(r=requestAnimationFrame(g),o=performance.now(),a=o-h,h=o,!(a>1e3)){for(n("tick"),l+=a;l>=d;)c.update(u),l-=d;f(),c.render()}}return c={update:i,render:s,isStopped:!0,start(){h=performance.now(),this.isStopped=!1,requestAnimationFrame(g)},stop(){this.isStopped=!0,cancelAnimationFrame(r)}}},keyMap:I,initKeys:function(){let t;for(t=0;t<26;t++)I[65+t]=(10+t).toString(36);for(t=0;t<10;t++)I[48+t]=""+t;window.addEventListener("keydown",z),window.addEventListener("keyup",D),window.addEventListener("blur",R)},bindKeys:function(t,e){[].concat(t).map(t=>k[t]=e)},unbindKeys:function(t){[].concat(t).map(t=>k[t]=0)},keyPressed:function(t){return!!L[t]},registerPlugin:function(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let n=i(t,e,...s);return n||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),n=i["_o"+s].call(this,...e);return this._aInc(this,s,n,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))},unregisterPlugin:function(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);t.startsWith("before")?C(i._inc[s].before,e[t]):t.startsWith("after")&&C(i._inc[s].after,e[t])})},extendObject:function(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})},initPointer:function(){let t=h();t.addEventListener("mousedown",B),t.addEventListener("touchstart",B),t.addEventListener("mouseup",H),t.addEventListener("touchend",H),t.addEventListener("blur",G),t.addEventListener("mousemove",J),t.addEventListener("touchmove",J),s("tick",()=>{Y.length=0,X.map(t=>{Y.push(t)}),X.length=0})},pointer:F,track:function(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){X.push(this),this._r()},N.push(t))})},untrack:function(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=N.indexOf(t);-1!==e&&N.splice(e,1)})},pointerOver:function(t){return!!N.includes(t)&&$()===t},onPointerDown:function(t){T.onDown=t},onPointerUp:function(t){T.onUp=t},pointerPressed:function(t){return!!U[t]},Pool:Z,Quadtree:it,Sprite:rt,SpriteSheet:ct,setStoreItem:function(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))},getStoreItem:function(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e},TileEngine:function(t={}){let{width:e,height:i,tilewidth:s,tileheight:n,context:o=r(),tilesets:a,layers:c}=t,l=e*s,d=i*n,u=document.createElement("canvas"),f=u.getContext("2d");u.width=l,u.height=d;let g={},p={},m=[],_=Object.assign({context:o,mapwidth:l,mapheight:d,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),l-h().width),m.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),d-h().height),m.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),b(u)},renderLayer(t){let e=p[t],i=g[t];e||((e=document.createElement("canvas")).width=l,e.height=d,p[t]=e,_._r(i,e.getContext("2d"))),b(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let n=w(s),h=y(i),r=w(s+e.height),o=y(i+e.width),a=g[t];for(let t=n;t<=r;t++)for(let e=h;e<=o;e++)if(a.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||w(e.y),s=e.col||y(e.x);return g[t]?g[t].data[s+i*_.width]:-1},setTileAtLayer(t,e,i){let s=e.row||w(e.y),n=e.col||y(e.x);g[t]&&(this._d=!0,g[t].data[n+s*_.width]=i)},addObject(t){m.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=m.indexOf(t);-1!==e&&(m.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=_.tilesets.length-1;e>=0&&(s=_.tilesets[e],!(t/s.firstgid>=1));e--);let n=s.tilewidth||_.tilewidth,h=s.tileheight||_.tileheight,r=s.margin||0,o=s.image,a=t-s.firstgid,c=s.columns||o.width/(n+r)|0,l=i%_.width*n,d=(i/_.width|0)*h,u=a%c*(n+r),f=(a/c|0)*(h+r);e.drawImage(o,u,f,n,h,l,d,n,h)}),e.restore()},_p:x},t);function w(t){return t/_.tileheight|0}function y(t){return t/_.tilewidth|0}function x(){_.layers&&_.layers.map(t=>{g[t.name]=t,!1!==t.visible&&_._r(t,f)})}function b(t){const{width:e,height:i}=h(),s=Math.min(t.width,e),n=Math.min(t.height,i);_.context.drawImage(t,_.sx,_.sy,s,n,0,0,s,n)}return _.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){let t=window.__k.d[window.__k.u(e.source,i)];Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){let t=window.__k.i[window.__k.u(e.image,i)];e.image=t}}),x(),_},Vector:nt}}(); \ No newline at end of file diff --git a/kontra.min.mjs b/kontra.min.mjs index 30336bbb..9fd5d5c7 100644 --- a/kontra.min.mjs +++ b/kontra.min.mjs @@ -1 +1 @@ -let canvasEl,context,callbacks={};function on(t,e){callbacks[t]=callbacks[t]||[],callbacks[t].push(e)}function off(t,e){let i;!callbacks[t]||(i=callbacks[t].indexOf(e))<0||callbacks[t].splice(i,1)}function emit(t,...e){callbacks[t]&&callbacks[t].map(t=>t(...e))}function getCanvas(){return canvasEl}function getContext(){return context}function init(t){if(!(canvasEl=document.getElementById(t)||t||document.querySelector("canvas")))throw Error("You must provide a canvas element for the game");return(context=canvasEl.getContext("2d")).imageSmoothingEnabled=!1,emit("init"),{canvas:canvasEl,context:context}}class Animation{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:n,height:a,margin:r=0}=t.frame;this.width=n,this.height=a,this.margin=r,this._f=0,this._a=0}clone(){return animationFactory(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:n=getContext()}={}){let a=this.frames[this._f]/this.spriteSheet._f|0,r=this.frames[this._f]%this.spriteSheet._f|0;n.drawImage(this.spriteSheet.image,r*this.width+(2*r+1)*this.margin,a*this.height+(2*a+1)*this.margin,this.width,this.height,t,e,i,s)}}function animationFactory(t){return new Animation(t)}animationFactory.prototype=Animation.prototype,animationFactory.class=Animation;let imageRegex=/(jpeg|jpg|gif|png)$/,audioRegex=/(wav|mp3|ogg|aac)$/,leadingSlash=/^\//,trailingSlash=/\/$/,dataMap=new WeakMap,imagePath="",audioPath="",dataPath="";function getUrl(t,e){return new URL(t,e).href}function joinPath(t,e){return[t.replace(trailingSlash,""),t?e.replace(leadingSlash,""):e].filter(t=>t).join("/")}function getExtension(t){return t.split(".").pop()}function getName(t){let e=t.replace("."+getExtension(t),"");return 2==e.split("/").length?e.replace(leadingSlash,""):e}function getCanPlay(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}let imageAssets={},audioAssets={},dataAssets={};function addGlobal(){window.__k||(window.__k={dm:dataMap,u:getUrl,d:dataAssets,i:imageAssets})}function setImagePath(t){imagePath=t}function setAudioPath(t){audioPath=t}function setDataPath(t){dataPath=t}function loadImage(t){return addGlobal(),new Promise((e,i)=>{let s,n,a;if(s=joinPath(imagePath,t),imageAssets[s])return e(imageAssets[s]);(n=new Image).onload=function(){a=getUrl(s,window.location.href),imageAssets[getName(t)]=imageAssets[s]=imageAssets[a]=this,emit("assetLoaded",this,t),e(this)},n.onerror=function(){i("Unable to load image "+s)},n.src=s})}function loadAudio(t){return new Promise((e,i)=>{let s,n,a,r;return s=new Audio,n=getCanPlay(s),(t=[].concat(t).reduce((t,e)=>t||(n[getExtension(e)]?e:null),0))?(a=joinPath(audioPath,t),audioAssets[a]?e(audioAssets[a]):(s.addEventListener("canplay",function(){r=getUrl(a,window.location.href),audioAssets[getName(t)]=audioAssets[a]=audioAssets[r]=this,emit("assetLoaded",this,t),e(this)}),s.onerror=function(){i("Unable to load audio "+a)},s.src=a,void s.load())):i("cannot play any of the audio formats provided"+t)})}function loadData(t){let e,i;return addGlobal(),e=joinPath(dataPath,t),dataAssets[e]?Promise.resolve(dataAssets[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=getUrl(e,window.location.href),"object"==typeof s&&dataMap.set(s,i),dataAssets[getName(t)]=dataAssets[e]=dataAssets[i]=s,emit("assetLoaded",s,t),s))}function load(...t){return addGlobal(),Promise.all(t.map(t=>{let e=getExtension([].concat(t)[0]);return e.match(imageRegex)?loadImage(t):e.match(audioRegex)?loadAudio(t):loadData(t)}))}const noop=()=>{};function clear(){let t=getCanvas();getContext().clearRect(0,0,t.width,t.height)}function GameLoop({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){if(!i||!s)throw Error("You must provide update() and render() functions");let n,a,r,o,h,c=0,d=1e3/t,l=1/t,u=e?clear:noop;function p(){if(a=requestAnimationFrame(p),r=performance.now(),o=r-n,n=r,!(o>1e3)){for(emit("tick"),c+=o;c>=d;)h.update(l),c-=d;u(),h.render()}}return h={update:i,render:s,isStopped:!0,start(){n=performance.now(),this.isStopped=!1,requestAnimationFrame(p)},stop(){this.isStopped=!0,cancelAnimationFrame(a)},_frame:p,set _last(t){n=t}}}let callbacks$1={},pressedKeys={},keyMap={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function keydownEventHandler(t){let e=keyMap[t.which];pressedKeys[e]=!0,callbacks$1[e]&&callbacks$1[e](t)}function keyupEventHandler(t){pressedKeys[keyMap[t.which]]=!1}function blurEventHandler(){pressedKeys={}}function initKeys(){let t;for(t=0;t<26;t++)keyMap[65+t]=(10+t).toString(36);for(t=0;t<10;t++)keyMap[48+t]=""+t;window.addEventListener("keydown",keydownEventHandler),window.addEventListener("keyup",keyupEventHandler),window.addEventListener("blur",blurEventHandler)}function bindKeys(t,e){[].concat(t).map(t=>callbacks$1[t]=e)}function unbindKeys(t){[].concat(t).map(t=>callbacks$1[t]=0)}function keyPressed(t){return!!pressedKeys[t]}function getMethod(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function removeInterceptor(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}function registerPlugin(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let n=i(t,e,...s);return n||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=getMethod(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),n=i["_o"+s].call(this,...e);return this._aInc(this,s,n,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))}function unregisterPlugin(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=getMethod(t);t.startsWith("before")?removeInterceptor(i._inc[s].before,e[t]):t.startsWith("after")&&removeInterceptor(i._inc[s].after,e[t])})}function extendObject(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})}let thisFrameRenderOrder=[],lastFrameRenderOrder=[],callbacks$2={},trackedObjects=[],pressedButtons={},buttonMap={0:"left",1:"middle",2:"right"},pointer={x:0,y:0,radius:5};function circleRectCollision(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=pointer.x-Math.max(e,Math.min(pointer.x,e+t.width)),n=pointer.y-Math.max(i,Math.min(pointer.y,i+t.height));return s*s+n*n=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(pointer):circleRectCollision(t))return t}function pointerDownHandler(t){let e=void 0!==t.button?buttonMap[t.button]:"left";pressedButtons[e]=!0,pointerHandler(t,"onDown")}function pointerUpHandler(t){let e=void 0!==t.button?buttonMap[t.button]:"left";pressedButtons[e]=!1,pointerHandler(t,"onUp")}function mouseMoveHandler(t){pointerHandler(t,"onOver")}function blurEventHandler$1(){pressedButtons={}}function pointerHandler(t,e){let i,s,n=getCanvas();if(!n)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let a=n.height/n.offsetHeight,r=n.getBoundingClientRect(),o=(i-r.left)*a,h=(s-r.top)*a;pointer.x=o,pointer.y=h,t.preventDefault();let c=getCurrentObject();c&&c[e]&&c[e](t),callbacks$2[e]&&callbacks$2[e](t,c)}function initPointer(){let t=getCanvas();t.addEventListener("mousedown",pointerDownHandler),t.addEventListener("touchstart",pointerDownHandler),t.addEventListener("mouseup",pointerUpHandler),t.addEventListener("touchend",pointerUpHandler),t.addEventListener("blur",blurEventHandler$1),t.addEventListener("mousemove",mouseMoveHandler),t.addEventListener("touchmove",mouseMoveHandler),on("tick",()=>{lastFrameRenderOrder.length=0,thisFrameRenderOrder.map(t=>{lastFrameRenderOrder.push(t)}),thisFrameRenderOrder.length=0})}function track(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){thisFrameRenderOrder.push(this),this._r()},trackedObjects.push(t))})}function untrack(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=trackedObjects.indexOf(t);-1!==e&&trackedObjects.splice(e,1)})}function pointerOver(t){return!!trackedObjects.includes(t)&&getCurrentObject()===t}function onPointerDown(t){callbacks$2.onDown=t}function onPointerUp(t){callbacks$2.onUp=t}function pointerPressed(t){return!!pressedButtons[t]}class Pool{constructor({create:t,maxSize:e=1024}={}){let i;if(!t||!(i=t())||!(i.update&&i.init&&i.isAlive))throw Error("Must provide create() function which returns an object with init(), update(), and isAlive() functions");this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function poolFactory(t){return new Pool(t)}function getIndices(t,e){let i=[],s=e.x+e.width/2,n=e.y+e.height/2,a=t.y=e.y,r=t.y+t.height>=n&&t.y=e.x&&(a&&i.push(0),r&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1,this._s[i]._p=this}}function quadtreeFactory(t){return new Quadtree(t)}quadtreeFactory.prototype=Quadtree.prototype,quadtreeFactory.class=Quadtree;class Vector{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return vectorFactory(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function vectorFactory(t,e,i={}){let s=new Vector(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}vectorFactory.prototype=Vector.prototype,vectorFactory.class=Vector;class Sprite{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:n,ddx:a,ddy:r,width:o,height:h,image:c}=t;this.position=vectorFactory(e,i),this.velocity=vectorFactory(s,n),this.acceleration=vectorFactory(a,r),this.width=this.height=this.rotation=0,this.ttl=1/0,this.anchor={x:0,y:0},this.context=getContext();for(let e in t)this[e]=t[e];c&&(this.width=void 0!==o?o:c.width,this.height=void 0!==h?h:c.height),this.sx=0,this.sy=0}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,n=t.y;return t.anchor&&(s-=t.width*t.anchor.x,n-=t.height*t.anchor.y),es&&in}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;this.context.save(),this.context.translate(this.viewX,this.viewY),this.rotation&&this.context.rotate(this.rotation),this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function spriteFactory(t){return new Sprite(t)}function parseFrames(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],n=+i[1],a=s;if(s=n;a--)e.push(a);return e}spriteFactory.prototype=Sprite.prototype,spriteFactory.class=Sprite;class SpriteSheet{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:n}={}){if(!t)throw Error("You must provide an Image for the SpriteSheet");this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(n)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:n,loop:a}=t[i];if(e=[],void 0===s)throw Error("Animation "+i+" must provide a frames property");[].concat(s).map(t=>{e=e.concat(parseFrames(t))}),this.animations[i]=animationFactory({spriteSheet:this,frames:e,frameRate:n,loop:a})}}}function spriteSheetFactory(t){return new SpriteSheet(t)}function setStoreItem(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))}function getStoreItem(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e}function TileEngine(t={}){let{width:e,height:i,tilewidth:s,tileheight:n,context:a=getContext(),tilesets:r,layers:o}=t,h=e*s,c=i*n,d=document.createElement("canvas"),l=d.getContext("2d");d.width=h,d.height=c;let u={},p={},g=[],m=Object.assign({context:a,mapwidth:h,mapheight:c,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),h-getCanvas().width),g.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),c-getCanvas().height),g.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),_(d)},renderLayer(t){let e=p[t],i=u[t];e||((e=document.createElement("canvas")).width=h,e.height=c,p[t]=e,m._r(i,e.getContext("2d"))),_(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let n=f(s),a=y(i),r=f(s+e.height),o=y(i+e.width),h=u[t];for(let t=n;t<=r;t++)for(let e=a;e<=o;e++)if(h.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||f(e.y),s=e.col||y(e.x);return u[t]?u[t].data[s+i*m.width]:-1},setTileAtLayer(t,e,i){let s=e.row||f(e.y),n=e.col||y(e.x);u[t]&&(this._d=!0,u[t].data[n+s*m.width]=i)},addObject(t){g.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=g.indexOf(t);-1!==e&&(g.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=m.tilesets.length-1;e>=0&&(s=m.tilesets[e],!(t/s.firstgid>=1));e--);let n=s.tilewidth||m.tilewidth,a=s.tileheight||m.tileheight,r=s.margin||0,o=s.image,h=t-s.firstgid,c=s.columns||o.width/(n+r)|0,d=i%m.width*n,l=(i/m.width|0)*a,u=h%c*(n+r),p=(h/c|0)*(a+r);e.drawImage(o,u,p,n,a,d,l,n,a)}),e.restore()},_p:w,layerCanvases:p},t);function f(t){return t/m.tileheight|0}function y(t){return t/m.tilewidth|0}function w(){m.layers&&m.layers.map(t=>{u[t.name]=t,!1!==t.visible&&m._r(t,l)})}function _(t){const{width:e,height:i}=getCanvas(),s=Math.min(t.width,e),n=Math.min(t.height,i);m.context.drawImage(t,m.sx,m.sy,s,n,0,0,s,n)}return m.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){if(!window.__k)throw Error('You must use "load" or "loadData" to resolve tileset.source');let t=window.__k.d[window.__k.u(e.source,i)];if(!t)throw Error(`You must load the tileset source "${e.source}" before loading the tileset`);Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){if(!window.__k)throw Error('You must use "load" or "loadImage" to resolve tileset.image');let t=window.__k.i[window.__k.u(e.image,i)];if(!t)throw Error(`You must load the image "${e.image}" before loading the tileset`);e.image=t}}),w(),m}spriteSheetFactory.prototype=SpriteSheet.prototype,spriteSheetFactory.class=SpriteSheet;let kontra={Animation:animationFactory,imageAssets:imageAssets,audioAssets:audioAssets,dataAssets:dataAssets,setImagePath:setImagePath,setAudioPath:setAudioPath,setDataPath:setDataPath,loadImage:loadImage,loadAudio:loadAudio,loadData:loadData,load:load,init:init,getCanvas:getCanvas,getContext:getContext,on:on,off:off,emit:emit,GameLoop:GameLoop,keyMap:keyMap,initKeys:initKeys,bindKeys:bindKeys,unbindKeys:unbindKeys,keyPressed:keyPressed,registerPlugin:registerPlugin,unregisterPlugin:unregisterPlugin,extendObject:extendObject,initPointer:initPointer,pointer:pointer,track:track,untrack:untrack,pointerOver:pointerOver,onPointerDown:onPointerDown,onPointerUp:onPointerUp,pointerPressed:pointerPressed,Pool:poolFactory,Quadtree:quadtreeFactory,Sprite:spriteFactory,SpriteSheet:spriteSheetFactory,setStoreItem:setStoreItem,getStoreItem:getStoreItem,TileEngine:TileEngine,Vector:vectorFactory};export{animationFactory as Animation,imageAssets,audioAssets,dataAssets,setImagePath,setAudioPath,setDataPath,loadImage,loadAudio,loadData,load,init,getCanvas,getContext,on,off,emit,GameLoop,keyMap,initKeys,bindKeys,unbindKeys,keyPressed,registerPlugin,unregisterPlugin,extendObject,initPointer,pointer,track,untrack,pointerOver,onPointerDown,onPointerUp,pointerPressed,poolFactory as Pool,quadtreeFactory as Quadtree,spriteFactory as Sprite,spriteSheetFactory as SpriteSheet,setStoreItem,getStoreItem,TileEngine,vectorFactory as Vector};export default kontra; \ No newline at end of file +let canvasEl,context,callbacks={};function on(t,e){callbacks[t]=callbacks[t]||[],callbacks[t].push(e)}function off(t,e){let i;!callbacks[t]||(i=callbacks[t].indexOf(e))<0||callbacks[t].splice(i,1)}function emit(t,...e){callbacks[t]&&callbacks[t].map(t=>t(...e))}function getCanvas(){return canvasEl}function getContext(){return context}function init(t){if(!(canvasEl=document.getElementById(t)||t||document.querySelector("canvas")))throw Error("You must provide a canvas element for the game");return(context=canvasEl.getContext("2d")).imageSmoothingEnabled=!1,emit("init"),{canvas:canvasEl,context:context}}class Animation{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:n,height:a,margin:r=0}=t.frame;this.width=n,this.height=a,this.margin=r,this._f=0,this._a=0}clone(){return animationFactory(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:n=getContext()}={}){let a=this.frames[this._f]/this.spriteSheet._f|0,r=this.frames[this._f]%this.spriteSheet._f|0;n.drawImage(this.spriteSheet.image,r*this.width+(2*r+1)*this.margin,a*this.height+(2*a+1)*this.margin,this.width,this.height,t,e,i,s)}}function animationFactory(t){return new Animation(t)}animationFactory.prototype=Animation.prototype,animationFactory.class=Animation;let imageRegex=/(jpeg|jpg|gif|png)$/,audioRegex=/(wav|mp3|ogg|aac)$/,leadingSlash=/^\//,trailingSlash=/\/$/,dataMap=new WeakMap,imagePath="",audioPath="",dataPath="";function getUrl(t,e){return new URL(t,e).href}function joinPath(t,e){return[t.replace(trailingSlash,""),t?e.replace(leadingSlash,""):e].filter(t=>t).join("/")}function getExtension(t){return t.split(".").pop()}function getName(t){let e=t.replace("."+getExtension(t),"");return 2==e.split("/").length?e.replace(leadingSlash,""):e}function getCanPlay(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}let imageAssets={},audioAssets={},dataAssets={};function addGlobal(){window.__k||(window.__k={dm:dataMap,u:getUrl,d:dataAssets,i:imageAssets})}function setImagePath(t){imagePath=t}function setAudioPath(t){audioPath=t}function setDataPath(t){dataPath=t}function loadImage(t){return addGlobal(),new Promise((e,i)=>{let s,n,a;if(s=joinPath(imagePath,t),imageAssets[s])return e(imageAssets[s]);(n=new Image).onload=function(){a=getUrl(s,window.location.href),imageAssets[getName(t)]=imageAssets[s]=imageAssets[a]=this,emit("assetLoaded",this,t),e(this)},n.onerror=function(){i("Unable to load image "+s)},n.src=s})}function loadAudio(t){return new Promise((e,i)=>{let s,n,a,r;return s=new Audio,n=getCanPlay(s),(t=[].concat(t).reduce((t,e)=>t||(n[getExtension(e)]?e:null),0))?(a=joinPath(audioPath,t),audioAssets[a]?e(audioAssets[a]):(s.addEventListener("canplay",function(){r=getUrl(a,window.location.href),audioAssets[getName(t)]=audioAssets[a]=audioAssets[r]=this,emit("assetLoaded",this,t),e(this)}),s.onerror=function(){i("Unable to load audio "+a)},s.src=a,void s.load())):i("cannot play any of the audio formats provided"+t)})}function loadData(t){let e,i;return addGlobal(),e=joinPath(dataPath,t),dataAssets[e]?Promise.resolve(dataAssets[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=getUrl(e,window.location.href),"object"==typeof s&&dataMap.set(s,i),dataAssets[getName(t)]=dataAssets[e]=dataAssets[i]=s,emit("assetLoaded",s,t),s))}function load(...t){return addGlobal(),Promise.all(t.map(t=>{let e=getExtension([].concat(t)[0]);return e.match(imageRegex)?loadImage(t):e.match(audioRegex)?loadAudio(t):loadData(t)}))}const noop=()=>{};function clear(){let t=getCanvas();getContext().clearRect(0,0,t.width,t.height)}function GameLoop({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){if(!i||!s)throw Error("You must provide update() and render() functions");let n,a,r,o,h,c=0,d=1e3/t,l=1/t,u=e?clear:noop;function p(){if(a=requestAnimationFrame(p),r=performance.now(),o=r-n,n=r,!(o>1e3)){for(emit("tick"),c+=o;c>=d;)h.update(l),c-=d;u(),h.render()}}return h={update:i,render:s,isStopped:!0,start(){n=performance.now(),this.isStopped=!1,requestAnimationFrame(p)},stop(){this.isStopped=!0,cancelAnimationFrame(a)},_frame:p,set _last(t){n=t}}}let callbacks$1={},pressedKeys={},keyMap={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function keydownEventHandler(t){let e=keyMap[t.which];pressedKeys[e]=!0,callbacks$1[e]&&callbacks$1[e](t)}function keyupEventHandler(t){pressedKeys[keyMap[t.which]]=!1}function blurEventHandler(){pressedKeys={}}function initKeys(){let t;for(t=0;t<26;t++)keyMap[65+t]=(10+t).toString(36);for(t=0;t<10;t++)keyMap[48+t]=""+t;window.addEventListener("keydown",keydownEventHandler),window.addEventListener("keyup",keyupEventHandler),window.addEventListener("blur",blurEventHandler)}function bindKeys(t,e){[].concat(t).map(t=>callbacks$1[t]=e)}function unbindKeys(t){[].concat(t).map(t=>callbacks$1[t]=0)}function keyPressed(t){return!!pressedKeys[t]}function getMethod(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function removeInterceptor(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}function registerPlugin(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let n=i(t,e,...s);return n||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=getMethod(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),n=i["_o"+s].call(this,...e);return this._aInc(this,s,n,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))}function unregisterPlugin(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=getMethod(t);t.startsWith("before")?removeInterceptor(i._inc[s].before,e[t]):t.startsWith("after")&&removeInterceptor(i._inc[s].after,e[t])})}function extendObject(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})}let thisFrameRenderOrder=[],lastFrameRenderOrder=[],callbacks$2={},trackedObjects=[],pressedButtons={},buttonMap={0:"left",1:"middle",2:"right"},pointer={x:0,y:0,radius:5};function circleRectCollision(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=pointer.x-Math.max(e,Math.min(pointer.x,e+t.width)),n=pointer.y-Math.max(i,Math.min(pointer.y,i+t.height));return s*s+n*n=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(pointer):circleRectCollision(t))return t}function pointerDownHandler(t){let e=void 0!==t.button?buttonMap[t.button]:"left";pressedButtons[e]=!0,pointerHandler(t,"onDown")}function pointerUpHandler(t){let e=void 0!==t.button?buttonMap[t.button]:"left";pressedButtons[e]=!1,pointerHandler(t,"onUp")}function mouseMoveHandler(t){pointerHandler(t,"onOver")}function blurEventHandler$1(){pressedButtons={}}function pointerHandler(t,e){let i,s,n=getCanvas();if(!n)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let a=n.height/n.offsetHeight,r=n.getBoundingClientRect(),o=(i-r.left)*a,h=(s-r.top)*a;pointer.x=o,pointer.y=h,t.preventDefault();let c=getCurrentObject();c&&c[e]&&c[e](t),callbacks$2[e]&&callbacks$2[e](t,c)}function initPointer(){let t=getCanvas();t.addEventListener("mousedown",pointerDownHandler),t.addEventListener("touchstart",pointerDownHandler),t.addEventListener("mouseup",pointerUpHandler),t.addEventListener("touchend",pointerUpHandler),t.addEventListener("blur",blurEventHandler$1),t.addEventListener("mousemove",mouseMoveHandler),t.addEventListener("touchmove",mouseMoveHandler),on("tick",()=>{lastFrameRenderOrder.length=0,thisFrameRenderOrder.map(t=>{lastFrameRenderOrder.push(t)}),thisFrameRenderOrder.length=0})}function track(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){thisFrameRenderOrder.push(this),this._r()},trackedObjects.push(t))})}function untrack(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=trackedObjects.indexOf(t);-1!==e&&trackedObjects.splice(e,1)})}function pointerOver(t){return!!trackedObjects.includes(t)&&getCurrentObject()===t}function onPointerDown(t){callbacks$2.onDown=t}function onPointerUp(t){callbacks$2.onUp=t}function pointerPressed(t){return!!pressedButtons[t]}class Pool{constructor({create:t,maxSize:e=1024}={}){let i;if(!t||!(i=t())||!(i.update&&i.init&&i.isAlive))throw Error("Must provide create() function which returns an object with init(), update(), and isAlive() functions");this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function poolFactory(t){return new Pool(t)}function getIndices(t,e){let i=[],s=e.x+e.width/2,n=e.y+e.height/2,a=t.y=e.y,r=t.y+t.height>=n&&t.y=e.x&&(a&&i.push(0),r&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1,this._s[i]._p=this}}function quadtreeFactory(t){return new Quadtree(t)}quadtreeFactory.prototype=Quadtree.prototype,quadtreeFactory.class=Quadtree;class Vector{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return vectorFactory(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function vectorFactory(t,e,i={}){let s=new Vector(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}vectorFactory.prototype=Vector.prototype,vectorFactory.class=Vector;class Sprite{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:n,ddx:a,ddy:r,width:o,height:h,image:c}=t;this.position=vectorFactory(e,i),this.velocity=vectorFactory(s,n),this.acceleration=vectorFactory(a,r),this.width=this.height=this.rotation=0,this.ttl=1/0,this.scaleX=this.scaleY=1,this.anchor={x:0,y:0},this.context=getContext();for(let e in t)this[e]=t[e];c&&(this.width=void 0!==o?o:c.width,this.height=void 0!==h?h:c.height),this.sx=0,this.sy=0}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}get width(){return this._w}get height(){return this._h}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}set width(t){this.scaleX=t<0?-1:1,this._w=Math.abs(t)}set height(t){this.scaleY=t<0?-1:1,this._h=Math.abs(t)}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,n=t.y;return t.anchor&&(s-=t.width*t.anchor.x,n-=t.height*t.anchor.y),es&&in}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;this.context.save(),this.context.translate(this.viewX,this.viewY),this.context.scale(this.scaleX,this.scaleY),this.rotation&&this.context.rotate(this.rotation),this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function spriteFactory(t){return new Sprite(t)}function parseFrames(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],n=+i[1],a=s;if(s=n;a--)e.push(a);return e}spriteFactory.prototype=Sprite.prototype,spriteFactory.class=Sprite;class SpriteSheet{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:n}={}){if(!t)throw Error("You must provide an Image for the SpriteSheet");this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(n)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:n,loop:a}=t[i];if(e=[],void 0===s)throw Error("Animation "+i+" must provide a frames property");[].concat(s).map(t=>{e=e.concat(parseFrames(t))}),this.animations[i]=animationFactory({spriteSheet:this,frames:e,frameRate:n,loop:a})}}}function spriteSheetFactory(t){return new SpriteSheet(t)}function setStoreItem(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))}function getStoreItem(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e}function TileEngine(t={}){let{width:e,height:i,tilewidth:s,tileheight:n,context:a=getContext(),tilesets:r,layers:o}=t,h=e*s,c=i*n,d=document.createElement("canvas"),l=d.getContext("2d");d.width=h,d.height=c;let u={},p={},g=[],m=Object.assign({context:a,mapwidth:h,mapheight:c,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),h-getCanvas().width),g.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),c-getCanvas().height),g.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),_(d)},renderLayer(t){let e=p[t],i=u[t];e||((e=document.createElement("canvas")).width=h,e.height=c,p[t]=e,m._r(i,e.getContext("2d"))),_(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let n=f(s),a=y(i),r=f(s+e.height),o=y(i+e.width),h=u[t];for(let t=n;t<=r;t++)for(let e=a;e<=o;e++)if(h.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||f(e.y),s=e.col||y(e.x);return u[t]?u[t].data[s+i*m.width]:-1},setTileAtLayer(t,e,i){let s=e.row||f(e.y),n=e.col||y(e.x);u[t]&&(this._d=!0,u[t].data[n+s*m.width]=i)},addObject(t){g.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=g.indexOf(t);-1!==e&&(g.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=m.tilesets.length-1;e>=0&&(s=m.tilesets[e],!(t/s.firstgid>=1));e--);let n=s.tilewidth||m.tilewidth,a=s.tileheight||m.tileheight,r=s.margin||0,o=s.image,h=t-s.firstgid,c=s.columns||o.width/(n+r)|0,d=i%m.width*n,l=(i/m.width|0)*a,u=h%c*(n+r),p=(h/c|0)*(a+r);e.drawImage(o,u,p,n,a,d,l,n,a)}),e.restore()},_p:w,layerCanvases:p},t);function f(t){return t/m.tileheight|0}function y(t){return t/m.tilewidth|0}function w(){m.layers&&m.layers.map(t=>{u[t.name]=t,!1!==t.visible&&m._r(t,l)})}function _(t){const{width:e,height:i}=getCanvas(),s=Math.min(t.width,e),n=Math.min(t.height,i);m.context.drawImage(t,m.sx,m.sy,s,n,0,0,s,n)}return m.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){if(!window.__k)throw Error('You must use "load" or "loadData" to resolve tileset.source');let t=window.__k.d[window.__k.u(e.source,i)];if(!t)throw Error(`You must load the tileset source "${e.source}" before loading the tileset`);Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){if(!window.__k)throw Error('You must use "load" or "loadImage" to resolve tileset.image');let t=window.__k.i[window.__k.u(e.image,i)];if(!t)throw Error(`You must load the image "${e.image}" before loading the tileset`);e.image=t}}),w(),m}spriteSheetFactory.prototype=SpriteSheet.prototype,spriteSheetFactory.class=SpriteSheet;let kontra={Animation:animationFactory,imageAssets:imageAssets,audioAssets:audioAssets,dataAssets:dataAssets,setImagePath:setImagePath,setAudioPath:setAudioPath,setDataPath:setDataPath,loadImage:loadImage,loadAudio:loadAudio,loadData:loadData,load:load,init:init,getCanvas:getCanvas,getContext:getContext,on:on,off:off,emit:emit,GameLoop:GameLoop,keyMap:keyMap,initKeys:initKeys,bindKeys:bindKeys,unbindKeys:unbindKeys,keyPressed:keyPressed,registerPlugin:registerPlugin,unregisterPlugin:unregisterPlugin,extendObject:extendObject,initPointer:initPointer,pointer:pointer,track:track,untrack:untrack,pointerOver:pointerOver,onPointerDown:onPointerDown,onPointerUp:onPointerUp,pointerPressed:pointerPressed,Pool:poolFactory,Quadtree:quadtreeFactory,Sprite:spriteFactory,SpriteSheet:spriteSheetFactory,setStoreItem:setStoreItem,getStoreItem:getStoreItem,TileEngine:TileEngine,Vector:vectorFactory};export{animationFactory as Animation,imageAssets,audioAssets,dataAssets,setImagePath,setAudioPath,setDataPath,loadImage,loadAudio,loadData,load,init,getCanvas,getContext,on,off,emit,GameLoop,keyMap,initKeys,bindKeys,unbindKeys,keyPressed,registerPlugin,unregisterPlugin,extendObject,initPointer,pointer,track,untrack,pointerOver,onPointerDown,onPointerUp,pointerPressed,poolFactory as Pool,quadtreeFactory as Quadtree,spriteFactory as Sprite,spriteSheetFactory as SpriteSheet,setStoreItem,getStoreItem,TileEngine,vectorFactory as Vector};export default kontra; \ No newline at end of file diff --git a/kontra.mjs b/kontra.mjs index 57bea9d4..9bdaea53 100644 --- a/kontra.mjs +++ b/kontra.mjs @@ -2396,30 +2396,31 @@ class Sprite { // defaults /** - * The width of the sprite. If the sprite is a [rectangle sprite](api/sprite/#rectangle-sprite), it uses the passed in value. For an [image sprite](api/sprite/#image-sprite) it is the width of the image. And for an [animation sprite](api/sprite/#animation-sprite) it is the width of a single frame of the animation. + * The rotation of the sprite around the origin in radians. * @memberof Sprite - * @property {Number} width + * @property {Number} rotation */ + this.width = this.height = this.rotation = 0; /** - * The height of the sprite. If the sprite is a [rectangle sprite](api/sprite/#rectangle-sprite), it uses the passed in value. For an [image sprite](api/sprite/#image-sprite) it is the height of the image. And for an [animation sprite](api/sprite/#animation-sprite) it is the height of a single frame of the animation. + * How may frames the sprite should be alive. Primarily used by kontra.Pool to know when to recycle an object. * @memberof Sprite - * @property {Number} height + * @property {Number} ttl */ + this.ttl = Infinity; /** - * The rotation of the sprite around the origin in radians. + * The horizontal scaling factor. A negative value flips the sprite across the vertical axis. * @memberof Sprite - * @property {Number} rotation + * @property {Number} scaleX */ - this.width = this.height = this.rotation = 0; /** - * How may frames the sprite should be alive. Primarily used by kontra.Pool to know when to recycle an object. + * The vertical scaling factor. A negative value flips the sprite across the horizontal axis. * @memberof Sprite - * @property {Number} ttl + * @property {Number} scaleY */ - this.ttl = Infinity; + this.scaleX = this.scaleY = 1; /** * The x and y origin of the sprite. {x:0, y:0} is the top left corner of the sprite, {x:1, y:1} is the bottom right corner. @@ -2619,6 +2620,24 @@ class Sprite { return this.y - this.sy; } + /** + * The width of the sprite. If the sprite is a [rectangle sprite](api/sprite/#rectangle-sprite), it uses the passed in value. For an [image sprite](api/sprite/#image-sprite) it is the width of the image. And for an [animation sprite](api/sprite/#animation-sprite) it is the width of a single frame of the animation. + * @memberof Sprite + * @property {Number} width + */ + get width() { + return this._w; + } + + /** + * The height of the sprite. If the sprite is a [rectangle sprite](api/sprite/#rectangle-sprite), it uses the passed in value. For an [image sprite](api/sprite/#image-sprite) it is the height of the image. And for an [animation sprite](api/sprite/#animation-sprite) it is the height of a single frame of the animation. + * @memberof Sprite + * @property {Number} height + */ + get height() { + return this._h; + } + set x(value) { this.position.x = value; } @@ -2669,6 +2688,15 @@ class Sprite { return; } + set width(value) { + this.scaleX = value < 0 ? -1 : 1; + this._w = Math.abs(value); + } + set height(value) { + this.scaleY = value < 0 ? -1 : 1; + this._h = Math.abs(value); + } + /** * Check if the sprite is alive. Primarily used by kontra.Pool to know when to recycle an object. * @memberof Sprite @@ -2908,6 +2936,7 @@ class Sprite { this.context.save(); this.context.translate(this.viewX, this.viewY); + this.context.scale(this.scaleX, this.scaleY); if (this.rotation) { this.context.rotate(this.rotation); diff --git a/src/sprite.js b/src/sprite.js index de22fedb..b8eef867 100644 --- a/src/sprite.js +++ b/src/sprite.js @@ -73,30 +73,31 @@ class Sprite { // defaults /** - * The width of the sprite. If the sprite is a [rectangle sprite](api/sprite/#rectangle-sprite), it uses the passed in value. For an [image sprite](api/sprite/#image-sprite) it is the width of the image. And for an [animation sprite](api/sprite/#animation-sprite) it is the width of a single frame of the animation. + * The rotation of the sprite around the origin in radians. * @memberof Sprite - * @property {Number} width + * @property {Number} rotation */ + this.width = this.height = this.rotation = 0; /** - * The height of the sprite. If the sprite is a [rectangle sprite](api/sprite/#rectangle-sprite), it uses the passed in value. For an [image sprite](api/sprite/#image-sprite) it is the height of the image. And for an [animation sprite](api/sprite/#animation-sprite) it is the height of a single frame of the animation. + * How may frames the sprite should be alive. Primarily used by kontra.Pool to know when to recycle an object. * @memberof Sprite - * @property {Number} height + * @property {Number} ttl */ + this.ttl = Infinity; /** - * The rotation of the sprite around the origin in radians. + * The horizontal scaling factor. A negative value flips the sprite across the vertical axis. * @memberof Sprite - * @property {Number} rotation + * @property {Number} scaleX */ - this.width = this.height = this.rotation = 0; /** - * How may frames the sprite should be alive. Primarily used by kontra.Pool to know when to recycle an object. + * The vertical scaling factor. A negative value flips the sprite across the horizontal axis. * @memberof Sprite - * @property {Number} ttl + * @property {Number} scaleY */ - this.ttl = Infinity; + this.scaleX = this.scaleY = 1; /** * The x and y origin of the sprite. {x:0, y:0} is the top left corner of the sprite, {x:1, y:1} is the bottom right corner. @@ -296,6 +297,24 @@ class Sprite { return this.y - this.sy; } + /** + * The width of the sprite. If the sprite is a [rectangle sprite](api/sprite/#rectangle-sprite), it uses the passed in value. For an [image sprite](api/sprite/#image-sprite) it is the width of the image. And for an [animation sprite](api/sprite/#animation-sprite) it is the width of a single frame of the animation. + * @memberof Sprite + * @property {Number} width + */ + get width() { + return this._w; + } + + /** + * The height of the sprite. If the sprite is a [rectangle sprite](api/sprite/#rectangle-sprite), it uses the passed in value. For an [image sprite](api/sprite/#image-sprite) it is the height of the image. And for an [animation sprite](api/sprite/#animation-sprite) it is the height of a single frame of the animation. + * @memberof Sprite + * @property {Number} height + */ + get height() { + return this._h; + } + set x(value) { this.position.x = value; } @@ -346,6 +365,15 @@ class Sprite { return; } + set width(value) { + this.scaleX = value < 0 ? -1 : 1; + this._w = Math.abs(value); + } + set height(value) { + this.scaleY = value < 0 ? -1 : 1; + this._h = Math.abs(value); + } + /** * Check if the sprite is alive. Primarily used by kontra.Pool to know when to recycle an object. * @memberof Sprite @@ -585,6 +613,7 @@ class Sprite { this.context.save(); this.context.translate(this.viewX, this.viewY); + this.context.scale(this.scaleX, this.scaleY); if (this.rotation) { this.context.rotate(this.rotation); diff --git a/test/unit/sprite.spec.js b/test/unit/sprite.spec.js index 19bba5a7..649e0f90 100644 --- a/test/unit/sprite.spec.js +++ b/test/unit/sprite.spec.js @@ -30,6 +30,8 @@ describe('sprite', () => { expect(sprite.velocity.constructor.name).to.equal('Vector'); expect(sprite.acceleration.constructor.name).to.equal('Vector'); expect(sprite.ttl).to.equal(Infinity); + expect(sprite.scaleX).to.equal(1); + expect(sprite.scaleY).to.equal(1); }); it('should set basic properties of width, height, color, x, and y', () => { @@ -466,6 +468,38 @@ describe('sprite', () => { sprite.context.translate.restore(); }); + it('should scale the sprite to 1,1 by default', function() { + let sprite = Sprite({ + x: 10, + y: 20, + }); + + sinon.stub(sprite.context, 'scale').callsFake(noop); + + sprite.render(); + + expect(sprite.context.scale.calledWith(1,1)).to.be.ok; + + sprite.context.scale.restore(); + }); + + it('should scale the sprite by scaleX,scaleY', function() { + let sprite = Sprite({ + x: 10, + y: 20, + scaleX: 2, + scaleY: 4 + }); + + sinon.stub(sprite.context, 'scale').callsFake(noop); + + sprite.render(); + + expect(sprite.context.scale.calledWith(2,4)).to.be.ok; + + sprite.context.scale.restore(); + }); + }); @@ -722,4 +756,67 @@ describe('sprite', () => { }); + + + + + // -------------------------------------------------- + // width/height + // -------------------------------------------------- + describe('width/height', () => { + + it('should set scaleX to 1 when width is >= 0', function() { + let sprite = Sprite({ + x: 10, + y: 20, + }); + + sprite.width = 0; + + expect(sprite.scaleX).to.equal(1); + + sprite.width = 100; + + expect(sprite.scaleX).to.equal(1); + }); + + it('should set scaleX to -1 when width is < 0', function() { + let sprite = Sprite({ + x: 10, + y: 20, + }); + + sprite.width = -20; + + expect(sprite.scaleX).to.equal(-1); + }); + + it('should set scaleY to 1 when height is >= 0', function() { + let sprite = Sprite({ + x: 10, + y: 20, + }); + + sprite.height = 0; + + expect(sprite.scaleY).to.equal(1); + + sprite.height = 100; + + expect(sprite.scaleY).to.equal(1); + }); + + it('should set scaleY to -1 when height is < 0', function() { + let sprite = Sprite({ + x: 10, + y: 20, + }); + + sprite.height = -20; + + expect(sprite.scaleY).to.equal(-1); + }); + + }); + }); \ No newline at end of file From 1b8d2794ff9d85a57cbdbee961c0bad57e153eac Mon Sep 17 00:00:00 2001 From: straker Date: Wed, 4 Sep 2019 23:37:31 -0600 Subject: [PATCH 2/6] flip sprite around center --- docs/api/sprite.html | 34 +------- docs/assets/js/kontra.js | 2 +- examples/sprite/imageSprite.html | 3 +- kontra.js | 36 ++++---- kontra.min.js | 2 +- kontra.min.mjs | 2 +- kontra.mjs | 36 ++++---- src/sprite.js | 36 ++++---- test/unit/sprite.spec.js | 136 ++++++++++++++++++++----------- 9 files changed, 157 insertions(+), 130 deletions(-) diff --git a/docs/api/sprite.html b/docs/api/sprite.html index 87bad7a4..f6a5e40f 100644 --- a/docs/api/sprite.html +++ b/docs/api/sprite.html @@ -275,16 +275,6 @@

    Properties

    Sprite​.rotation

  • -
  • - - Sprite​.scaleX - -
  • -
  • - - Sprite​.scaleY - -
  • Sprite​.sx @@ -1693,6 +1683,7 @@

    The height of the sprite. If the sprite is a rectangle sprite, it uses the passed in value. For an image sprite it is the height of the image. And for an animation sprite it is the height of a single frame of the animation.

    +

    Setting the value to a negative number will result in the sprite being flipped across the horizontal axis while the height will remain a positive value.

    @@ -1869,28 +1860,6 @@

    The rotation of the sprite around the origin in radians.

    -

    -
    -

    - - Sprite​.scaleX - - -

    - -

    The horizontal scaling factor. A negative value flips the sprite across the vertical axis.

    - -
    -
    -

    - - Sprite​.scaleY - - -

    - -

    The vertical scaling factor. A negative value flips the sprite across the horizontal axis.

    -

    @@ -1987,6 +1956,7 @@

    The width of the sprite. If the sprite is a rectangle sprite, it uses the passed in value. For an image sprite it is the width of the image. And for an animation sprite it is the width of a single frame of the animation.

    +

    Setting the value to a negative number will result in the sprite being flipped across the vertical axis while the width will remain a positive value.

    diff --git a/docs/assets/js/kontra.js b/docs/assets/js/kontra.js index a08dda61..1b78e67a 100644 --- a/docs/assets/js/kontra.js +++ b/docs/assets/js/kontra.js @@ -1 +1 @@ -var kontra=function(){"use strict";let t,e,i={};function s(t,e){i[t]=i[t]||[],i[t].push(e)}function n(t,...e){i[t]&&i[t].map(t=>t(...e))}function h(){return t}function r(){return e}class o{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:n,height:h,margin:r=0}=t.frame;this.width=n,this.height=h,this.margin=r,this._f=0,this._a=0}clone(){return a(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:n=r()}={}){let h=this.frames[this._f]/this.spriteSheet._f|0,o=this.frames[this._f]%this.spriteSheet._f|0;n.drawImage(this.spriteSheet.image,o*this.width+(2*o+1)*this.margin,h*this.height+(2*h+1)*this.margin,this.width,this.height,t,e,i,s)}}function a(t){return new o(t)}a.prototype=o.prototype,a.class=o;let c=/(jpeg|jpg|gif|png)$/,l=/(wav|mp3|ogg|aac)$/,d=/^\//,u=/\/$/,f=new WeakMap,g="",p="",m="";function _(t,e){return new URL(t,e).href}function w(t,e){return[t.replace(u,""),t?e.replace(d,""):e].filter(t=>t).join("/")}function y(t){return t.split(".").pop()}function x(t){let e=t.replace("."+y(t),"");return 2==e.split("/").length?e.replace(d,""):e}let b={},v={},j={};function A(){window.__k||(window.__k={dm:f,u:_,d:j,i:b})}function S(t){return A(),new Promise((e,i)=>{let s,h,r;if(s=w(g,t),b[s])return e(b[s]);(h=new Image).onload=function(){r=_(s,window.location.href),b[x(t)]=b[s]=b[r]=this,n("assetLoaded",this,t),e(this)},h.onerror=function(){i(s)},h.src=s})}function O(t){return new Promise((e,i)=>{let s,h,r,o;return s=new Audio,h=function(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}(s),(t=[].concat(t).reduce((t,e)=>t||(h[y(e)]?e:null),0))?(r=w(p,t),v[r]?e(v[r]):(s.addEventListener("canplay",function(){o=_(r,window.location.href),v[x(t)]=v[r]=v[o]=this,n("assetLoaded",this,t),e(this)}),s.onerror=function(){i(r)},s.src=r,void s.load())):i(t)})}function P(t){let e,i;return A(),e=w(m,t),j[e]?Promise.resolve(j[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=_(e,window.location.href),"object"==typeof s&&f.set(s,i),j[x(t)]=j[e]=j[i]=s,n("assetLoaded",s,t),s))}const E=()=>{};function M(){let t=h();r().clearRect(0,0,t.width,t.height)}let k={},L={},I={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function z(t){let e=I[t.which];L[e]=!0,k[e]&&k[e](t)}function D(t){L[I[t.which]]=!1}function R(){L={}}function W(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function C(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}let X=[],Y=[],T={},N=[],U={},q={0:"left",1:"middle",2:"right"},F={x:0,y:0,radius:5};function K(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=F.x-Math.max(e,Math.min(F.x,e+t.width)),n=F.y-Math.max(i,Math.min(F.y,i+t.height));return s*s+n*n=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(F):K(t))return t}function B(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!0,Q(t,"onDown")}function H(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!1,Q(t,"onUp")}function J(t){Q(t,"onOver")}function G(){U={}}function Q(t,e){let i,s,n=h();if(!n)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let r=n.height/n.offsetHeight,o=n.getBoundingClientRect(),a=(i-o.left)*r,c=(s-o.top)*r;F.x=a,F.y=c,t.preventDefault();let l=$();l&&l[e]&&l[e](t),T[e]&&T[e](t,l)}class V{constructor({create:t,maxSize:e=1024}={}){this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function Z(t){return new V(t)}function tt(t,e){let i=[],s=e.x+e.width/2,n=e.y+e.height/2,h=t.y=e.y,r=t.y+t.height>=n&&t.y=e.x&&(h&&i.push(0),r&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1}}function it(t){return new et(t)}it.prototype=et.prototype,it.class=et;class st{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return nt(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function nt(t,e,i={}){let s=new st(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}nt.prototype=st.prototype,nt.class=st;class ht{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:n,ddx:h,ddy:o,width:a,height:c,image:l}=t;this.position=nt(e,i),this.velocity=nt(s,n),this.acceleration=nt(h,o),this.width=this.height=this.rotation=0,this.ttl=1/0,this.scaleX=this.scaleY=1,this.anchor={x:0,y:0},this.context=r();for(let e in t)this[e]=t[e];l&&(this.width=void 0!==a?a:l.width,this.height=void 0!==c?c:l.height),this.sx=0,this.sy=0}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}get width(){return this._w}get height(){return this._h}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}set width(t){this.scaleX=t<0?-1:1,this._w=Math.abs(t)}set height(t){this.scaleY=t<0?-1:1,this._h=Math.abs(t)}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,n=t.y;return t.anchor&&(s-=t.width*t.anchor.x,n-=t.height*t.anchor.y),es&&in}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;this.context.save(),this.context.translate(this.viewX,this.viewY),this.context.scale(this.scaleX,this.scaleY),this.rotation&&this.context.rotate(this.rotation),this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function rt(t){return new ht(t)}function ot(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],n=+i[1],h=s;if(s=n;h--)e.push(h);return e}rt.prototype=ht.prototype,rt.class=ht;class at{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:n}={}){this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(n)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:n,loop:h}=t[i];e=[],[].concat(s).map(t=>{e=e.concat(ot(t))}),this.animations[i]=a({spriteSheet:this,frames:e,frameRate:n,loop:h})}}}function ct(t){return new at(t)}return ct.prototype=at.prototype,ct.class=at,{Animation:a,imageAssets:b,audioAssets:v,dataAssets:j,setImagePath:function(t){g=t},setAudioPath:function(t){p=t},setDataPath:function(t){m=t},loadImage:S,loadAudio:O,loadData:P,load:function(...t){return A(),Promise.all(t.map(t=>{let e=y([].concat(t)[0]);return e.match(c)?S(t):e.match(l)?O(t):P(t)}))},init:function(i){return t=document.getElementById(i)||i||document.querySelector("canvas"),(e=t.getContext("2d")).imageSmoothingEnabled=!1,n("init"),{canvas:t,context:e}},getCanvas:h,getContext:r,on:s,off:function(t,e){let s;!i[t]||(s=i[t].indexOf(e))<0||i[t].splice(s,1)},emit:n,GameLoop:function({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){let h,r,o,a,c,l=0,d=1e3/t,u=1/t,f=e?M:E;function g(){if(r=requestAnimationFrame(g),o=performance.now(),a=o-h,h=o,!(a>1e3)){for(n("tick"),l+=a;l>=d;)c.update(u),l-=d;f(),c.render()}}return c={update:i,render:s,isStopped:!0,start(){h=performance.now(),this.isStopped=!1,requestAnimationFrame(g)},stop(){this.isStopped=!0,cancelAnimationFrame(r)}}},keyMap:I,initKeys:function(){let t;for(t=0;t<26;t++)I[65+t]=(10+t).toString(36);for(t=0;t<10;t++)I[48+t]=""+t;window.addEventListener("keydown",z),window.addEventListener("keyup",D),window.addEventListener("blur",R)},bindKeys:function(t,e){[].concat(t).map(t=>k[t]=e)},unbindKeys:function(t){[].concat(t).map(t=>k[t]=0)},keyPressed:function(t){return!!L[t]},registerPlugin:function(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let n=i(t,e,...s);return n||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),n=i["_o"+s].call(this,...e);return this._aInc(this,s,n,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))},unregisterPlugin:function(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);t.startsWith("before")?C(i._inc[s].before,e[t]):t.startsWith("after")&&C(i._inc[s].after,e[t])})},extendObject:function(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})},initPointer:function(){let t=h();t.addEventListener("mousedown",B),t.addEventListener("touchstart",B),t.addEventListener("mouseup",H),t.addEventListener("touchend",H),t.addEventListener("blur",G),t.addEventListener("mousemove",J),t.addEventListener("touchmove",J),s("tick",()=>{Y.length=0,X.map(t=>{Y.push(t)}),X.length=0})},pointer:F,track:function(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){X.push(this),this._r()},N.push(t))})},untrack:function(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=N.indexOf(t);-1!==e&&N.splice(e,1)})},pointerOver:function(t){return!!N.includes(t)&&$()===t},onPointerDown:function(t){T.onDown=t},onPointerUp:function(t){T.onUp=t},pointerPressed:function(t){return!!U[t]},Pool:Z,Quadtree:it,Sprite:rt,SpriteSheet:ct,setStoreItem:function(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))},getStoreItem:function(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e},TileEngine:function(t={}){let{width:e,height:i,tilewidth:s,tileheight:n,context:o=r(),tilesets:a,layers:c}=t,l=e*s,d=i*n,u=document.createElement("canvas"),f=u.getContext("2d");u.width=l,u.height=d;let g={},p={},m=[],_=Object.assign({context:o,mapwidth:l,mapheight:d,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),l-h().width),m.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),d-h().height),m.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),b(u)},renderLayer(t){let e=p[t],i=g[t];e||((e=document.createElement("canvas")).width=l,e.height=d,p[t]=e,_._r(i,e.getContext("2d"))),b(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let n=w(s),h=y(i),r=w(s+e.height),o=y(i+e.width),a=g[t];for(let t=n;t<=r;t++)for(let e=h;e<=o;e++)if(a.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||w(e.y),s=e.col||y(e.x);return g[t]?g[t].data[s+i*_.width]:-1},setTileAtLayer(t,e,i){let s=e.row||w(e.y),n=e.col||y(e.x);g[t]&&(this._d=!0,g[t].data[n+s*_.width]=i)},addObject(t){m.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=m.indexOf(t);-1!==e&&(m.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=_.tilesets.length-1;e>=0&&(s=_.tilesets[e],!(t/s.firstgid>=1));e--);let n=s.tilewidth||_.tilewidth,h=s.tileheight||_.tileheight,r=s.margin||0,o=s.image,a=t-s.firstgid,c=s.columns||o.width/(n+r)|0,l=i%_.width*n,d=(i/_.width|0)*h,u=a%c*(n+r),f=(a/c|0)*(h+r);e.drawImage(o,u,f,n,h,l,d,n,h)}),e.restore()},_p:x},t);function w(t){return t/_.tileheight|0}function y(t){return t/_.tilewidth|0}function x(){_.layers&&_.layers.map(t=>{g[t.name]=t,!1!==t.visible&&_._r(t,f)})}function b(t){const{width:e,height:i}=h(),s=Math.min(t.width,e),n=Math.min(t.height,i);_.context.drawImage(t,_.sx,_.sy,s,n,0,0,s,n)}return _.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){let t=window.__k.d[window.__k.u(e.source,i)];Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){let t=window.__k.i[window.__k.u(e.image,i)];e.image=t}}),x(),_},Vector:nt}}(); \ No newline at end of file +var kontra=function(){"use strict";let t,e,i={};function s(t,e){i[t]=i[t]||[],i[t].push(e)}function h(t,...e){i[t]&&i[t].map(t=>t(...e))}function n(){return t}function r(){return e}class o{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:h,height:n,margin:r=0}=t.frame;this.width=h,this.height=n,this.margin=r,this._f=0,this._a=0}clone(){return a(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:h=r()}={}){let n=this.frames[this._f]/this.spriteSheet._f|0,o=this.frames[this._f]%this.spriteSheet._f|0;h.drawImage(this.spriteSheet.image,o*this.width+(2*o+1)*this.margin,n*this.height+(2*n+1)*this.margin,this.width,this.height,t,e,i,s)}}function a(t){return new o(t)}a.prototype=o.prototype,a.class=o;let c=/(jpeg|jpg|gif|png)$/,l=/(wav|mp3|ogg|aac)$/,d=/^\//,u=/\/$/,f=new WeakMap,g="",p="",m="";function _(t,e){return new URL(t,e).href}function w(t,e){return[t.replace(u,""),t?e.replace(d,""):e].filter(t=>t).join("/")}function y(t){return t.split(".").pop()}function x(t){let e=t.replace("."+y(t),"");return 2==e.split("/").length?e.replace(d,""):e}let b={},v={},j={};function A(){window.__k||(window.__k={dm:f,u:_,d:j,i:b})}function S(t){return A(),new Promise((e,i)=>{let s,n,r;if(s=w(g,t),b[s])return e(b[s]);(n=new Image).onload=function(){r=_(s,window.location.href),b[x(t)]=b[s]=b[r]=this,h("assetLoaded",this,t),e(this)},n.onerror=function(){i(s)},n.src=s})}function O(t){return new Promise((e,i)=>{let s,n,r,o;return s=new Audio,n=function(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}(s),(t=[].concat(t).reduce((t,e)=>t||(n[y(e)]?e:null),0))?(r=w(p,t),v[r]?e(v[r]):(s.addEventListener("canplay",function(){o=_(r,window.location.href),v[x(t)]=v[r]=v[o]=this,h("assetLoaded",this,t),e(this)}),s.onerror=function(){i(r)},s.src=r,void s.load())):i(t)})}function P(t){let e,i;return A(),e=w(m,t),j[e]?Promise.resolve(j[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=_(e,window.location.href),"object"==typeof s&&f.set(s,i),j[x(t)]=j[e]=j[i]=s,h("assetLoaded",s,t),s))}const E=()=>{};function M(){let t=n();r().clearRect(0,0,t.width,t.height)}let k={},L={},I={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function z(t){let e=I[t.which];L[e]=!0,k[e]&&k[e](t)}function D(t){L[I[t.which]]=!1}function R(){L={}}function W(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function C(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}let T=[],N=[],X={},Y=[],U={},q={0:"left",1:"middle",2:"right"},F={x:0,y:0,radius:5};function K(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=F.x-Math.max(e,Math.min(F.x,e+t.width)),h=F.y-Math.max(i,Math.min(F.y,i+t.height));return s*s+h*h=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(F):K(t))return t}function B(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!0,Q(t,"onDown")}function H(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!1,Q(t,"onUp")}function J(t){Q(t,"onOver")}function G(){U={}}function Q(t,e){let i,s,h=n();if(!h)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let r=h.height/h.offsetHeight,o=h.getBoundingClientRect(),a=(i-o.left)*r,c=(s-o.top)*r;F.x=a,F.y=c,t.preventDefault();let l=$();l&&l[e]&&l[e](t),X[e]&&X[e](t,l)}class V{constructor({create:t,maxSize:e=1024}={}){this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function Z(t){return new V(t)}function tt(t,e){let i=[],s=e.x+e.width/2,h=e.y+e.height/2,n=t.y=e.y,r=t.y+t.height>=h&&t.y=e.x&&(n&&i.push(0),r&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1}}function it(t){return new et(t)}it.prototype=et.prototype,it.class=et;class st{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return ht(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function ht(t,e,i={}){let s=new st(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}ht.prototype=st.prototype,ht.class=st;class nt{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:h,ddx:n,ddy:o,width:a,height:c,image:l}=t;this.position=ht(e,i),this.velocity=ht(s,h),this.acceleration=ht(n,o),this.width=this.height=this.rotation=0,this.ttl=1/0,this.anchor={x:0,y:0},this.context=r();for(let e in t)this[e]=t[e];l&&(this.width=void 0!==a?a:l.width,this.height=void 0!==c?c:l.height),this.sx=0,this.sy=0,this._fx=this._fy=1}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}get width(){return this._w}get height(){return this._h}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}set width(t){this._fx=t<0?-1:1,this._w=Math.abs(t)}set height(t){this._fy=t<0?-1:1,this._h=Math.abs(t)}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,h=t.y;return t.anchor&&(s-=t.width*t.anchor.x,h-=t.height*t.anchor.y),es&&ih}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;if(this.context.save(),this.context.translate(this.viewX,this.viewY),this.rotation&&this.context.rotate(this.rotation),-1==this._fx||-1==this._fy){let i=this.width/2+t,s=this.height/2+e;this.context.translate(i,s),this.context.scale(this._fx,this._fy),this.context.translate(-i,-s)}this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function rt(t){return new nt(t)}function ot(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],h=+i[1],n=s;if(s=h;n--)e.push(n);return e}rt.prototype=nt.prototype,rt.class=nt;class at{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:h}={}){this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(h)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:h,loop:n}=t[i];e=[],[].concat(s).map(t=>{e=e.concat(ot(t))}),this.animations[i]=a({spriteSheet:this,frames:e,frameRate:h,loop:n})}}}function ct(t){return new at(t)}return ct.prototype=at.prototype,ct.class=at,{Animation:a,imageAssets:b,audioAssets:v,dataAssets:j,setImagePath:function(t){g=t},setAudioPath:function(t){p=t},setDataPath:function(t){m=t},loadImage:S,loadAudio:O,loadData:P,load:function(...t){return A(),Promise.all(t.map(t=>{let e=y([].concat(t)[0]);return e.match(c)?S(t):e.match(l)?O(t):P(t)}))},init:function(i){return t=document.getElementById(i)||i||document.querySelector("canvas"),(e=t.getContext("2d")).imageSmoothingEnabled=!1,h("init"),{canvas:t,context:e}},getCanvas:n,getContext:r,on:s,off:function(t,e){let s;!i[t]||(s=i[t].indexOf(e))<0||i[t].splice(s,1)},emit:h,GameLoop:function({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){let n,r,o,a,c,l=0,d=1e3/t,u=1/t,f=e?M:E;function g(){if(r=requestAnimationFrame(g),o=performance.now(),a=o-n,n=o,!(a>1e3)){for(h("tick"),l+=a;l>=d;)c.update(u),l-=d;f(),c.render()}}return c={update:i,render:s,isStopped:!0,start(){n=performance.now(),this.isStopped=!1,requestAnimationFrame(g)},stop(){this.isStopped=!0,cancelAnimationFrame(r)}}},keyMap:I,initKeys:function(){let t;for(t=0;t<26;t++)I[65+t]=(10+t).toString(36);for(t=0;t<10;t++)I[48+t]=""+t;window.addEventListener("keydown",z),window.addEventListener("keyup",D),window.addEventListener("blur",R)},bindKeys:function(t,e){[].concat(t).map(t=>k[t]=e)},unbindKeys:function(t){[].concat(t).map(t=>k[t]=0)},keyPressed:function(t){return!!L[t]},registerPlugin:function(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let h=i(t,e,...s);return h||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),h=i["_o"+s].call(this,...e);return this._aInc(this,s,h,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))},unregisterPlugin:function(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);t.startsWith("before")?C(i._inc[s].before,e[t]):t.startsWith("after")&&C(i._inc[s].after,e[t])})},extendObject:function(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})},initPointer:function(){let t=n();t.addEventListener("mousedown",B),t.addEventListener("touchstart",B),t.addEventListener("mouseup",H),t.addEventListener("touchend",H),t.addEventListener("blur",G),t.addEventListener("mousemove",J),t.addEventListener("touchmove",J),s("tick",()=>{N.length=0,T.map(t=>{N.push(t)}),T.length=0})},pointer:F,track:function(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){T.push(this),this._r()},Y.push(t))})},untrack:function(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=Y.indexOf(t);-1!==e&&Y.splice(e,1)})},pointerOver:function(t){return!!Y.includes(t)&&$()===t},onPointerDown:function(t){X.onDown=t},onPointerUp:function(t){X.onUp=t},pointerPressed:function(t){return!!U[t]},Pool:Z,Quadtree:it,Sprite:rt,SpriteSheet:ct,setStoreItem:function(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))},getStoreItem:function(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e},TileEngine:function(t={}){let{width:e,height:i,tilewidth:s,tileheight:h,context:o=r(),tilesets:a,layers:c}=t,l=e*s,d=i*h,u=document.createElement("canvas"),f=u.getContext("2d");u.width=l,u.height=d;let g={},p={},m=[],_=Object.assign({context:o,mapwidth:l,mapheight:d,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),l-n().width),m.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),d-n().height),m.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),b(u)},renderLayer(t){let e=p[t],i=g[t];e||((e=document.createElement("canvas")).width=l,e.height=d,p[t]=e,_._r(i,e.getContext("2d"))),b(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let h=w(s),n=y(i),r=w(s+e.height),o=y(i+e.width),a=g[t];for(let t=h;t<=r;t++)for(let e=n;e<=o;e++)if(a.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||w(e.y),s=e.col||y(e.x);return g[t]?g[t].data[s+i*_.width]:-1},setTileAtLayer(t,e,i){let s=e.row||w(e.y),h=e.col||y(e.x);g[t]&&(this._d=!0,g[t].data[h+s*_.width]=i)},addObject(t){m.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=m.indexOf(t);-1!==e&&(m.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=_.tilesets.length-1;e>=0&&(s=_.tilesets[e],!(t/s.firstgid>=1));e--);let h=s.tilewidth||_.tilewidth,n=s.tileheight||_.tileheight,r=s.margin||0,o=s.image,a=t-s.firstgid,c=s.columns||o.width/(h+r)|0,l=i%_.width*h,d=(i/_.width|0)*n,u=a%c*(h+r),f=(a/c|0)*(n+r);e.drawImage(o,u,f,h,n,l,d,h,n)}),e.restore()},_p:x},t);function w(t){return t/_.tileheight|0}function y(t){return t/_.tilewidth|0}function x(){_.layers&&_.layers.map(t=>{g[t.name]=t,!1!==t.visible&&_._r(t,f)})}function b(t){const{width:e,height:i}=n(),s=Math.min(t.width,e),h=Math.min(t.height,i);_.context.drawImage(t,_.sx,_.sy,s,h,0,0,s,h)}return _.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){let t=window.__k.d[window.__k.u(e.source,i)];Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){let t=window.__k.i[window.__k.u(e.image,i)];e.image=t}}),x(),_},Vector:ht}}(); \ No newline at end of file diff --git a/examples/sprite/imageSprite.html b/examples/sprite/imageSprite.html index bdef2ae9..0b25f540 100644 --- a/examples/sprite/imageSprite.html +++ b/examples/sprite/imageSprite.html @@ -24,7 +24,8 @@ }, x: 300, y: 200, - image: kontra.imageAssets.character + image: kontra.imageAssets.character, + width: -66 }); // render the sprite diff --git a/kontra.js b/kontra.js index 9a8bd5cf..05a66625 100644 --- a/kontra.js +++ b/kontra.js @@ -2412,19 +2412,6 @@ class Sprite { */ this.ttl = Infinity; - /** - * The horizontal scaling factor. A negative value flips the sprite across the vertical axis. - * @memberof Sprite - * @property {Number} scaleX - */ - - /** - * The vertical scaling factor. A negative value flips the sprite across the horizontal axis. - * @memberof Sprite - * @property {Number} scaleY - */ - this.scaleX = this.scaleY = 1; - /** * The x and y origin of the sprite. {x:0, y:0} is the top left corner of the sprite, {x:1, y:1} is the bottom right corner. * @memberof Sprite @@ -2493,6 +2480,9 @@ class Sprite { this[prop] = properties[prop]; } + // sx = flipX, sy = flipY + this._fx = this._fy = 1; + // image sprite if (image) { this.width = (width !== undefined) ? width : image.width; @@ -2625,6 +2615,8 @@ class Sprite { /** * The width of the sprite. If the sprite is a [rectangle sprite](api/sprite/#rectangle-sprite), it uses the passed in value. For an [image sprite](api/sprite/#image-sprite) it is the width of the image. And for an [animation sprite](api/sprite/#animation-sprite) it is the width of a single frame of the animation. + * + * Setting the value to a negative number will result in the sprite being flipped across the vertical axis while the width will remain a positive value. * @memberof Sprite * @property {Number} width */ @@ -2634,6 +2626,8 @@ class Sprite { /** * The height of the sprite. If the sprite is a [rectangle sprite](api/sprite/#rectangle-sprite), it uses the passed in value. For an [image sprite](api/sprite/#image-sprite) it is the height of the image. And for an [animation sprite](api/sprite/#animation-sprite) it is the height of a single frame of the animation. + * + * Setting the value to a negative number will result in the sprite being flipped across the horizontal axis while the height will remain a positive value. * @memberof Sprite * @property {Number} height */ @@ -2692,11 +2686,11 @@ class Sprite { } set width(value) { - this.scaleX = value < 0 ? -1 : 1; + this._fx = value < 0 ? -1 : 1; this._w = Math.abs(value); } set height(value) { - this.scaleY = value < 0 ? -1 : 1; + this._fy = value < 0 ? -1 : 1; this._h = Math.abs(value); } @@ -2939,12 +2933,22 @@ class Sprite { this.context.save(); this.context.translate(this.viewX, this.viewY); - this.context.scale(this.scaleX, this.scaleY); + // rotate around the anchor if (this.rotation) { this.context.rotate(this.rotation); } + // flip sprite around the center so the x/y position does not change + if (this._fx == -1 || this._fy == -1) { + let x = this.width / 2 + anchorWidth; + let y = this.height / 2 + anchorHeight; + + this.context.translate(x, y); + this.context.scale(this._fx, this._fy); + this.context.translate(-x, -y); + } + if (this.image) { this.context.drawImage( this.image, diff --git a/kontra.min.js b/kontra.min.js index a08dda61..1b78e67a 100644 --- a/kontra.min.js +++ b/kontra.min.js @@ -1 +1 @@ -var kontra=function(){"use strict";let t,e,i={};function s(t,e){i[t]=i[t]||[],i[t].push(e)}function n(t,...e){i[t]&&i[t].map(t=>t(...e))}function h(){return t}function r(){return e}class o{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:n,height:h,margin:r=0}=t.frame;this.width=n,this.height=h,this.margin=r,this._f=0,this._a=0}clone(){return a(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:n=r()}={}){let h=this.frames[this._f]/this.spriteSheet._f|0,o=this.frames[this._f]%this.spriteSheet._f|0;n.drawImage(this.spriteSheet.image,o*this.width+(2*o+1)*this.margin,h*this.height+(2*h+1)*this.margin,this.width,this.height,t,e,i,s)}}function a(t){return new o(t)}a.prototype=o.prototype,a.class=o;let c=/(jpeg|jpg|gif|png)$/,l=/(wav|mp3|ogg|aac)$/,d=/^\//,u=/\/$/,f=new WeakMap,g="",p="",m="";function _(t,e){return new URL(t,e).href}function w(t,e){return[t.replace(u,""),t?e.replace(d,""):e].filter(t=>t).join("/")}function y(t){return t.split(".").pop()}function x(t){let e=t.replace("."+y(t),"");return 2==e.split("/").length?e.replace(d,""):e}let b={},v={},j={};function A(){window.__k||(window.__k={dm:f,u:_,d:j,i:b})}function S(t){return A(),new Promise((e,i)=>{let s,h,r;if(s=w(g,t),b[s])return e(b[s]);(h=new Image).onload=function(){r=_(s,window.location.href),b[x(t)]=b[s]=b[r]=this,n("assetLoaded",this,t),e(this)},h.onerror=function(){i(s)},h.src=s})}function O(t){return new Promise((e,i)=>{let s,h,r,o;return s=new Audio,h=function(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}(s),(t=[].concat(t).reduce((t,e)=>t||(h[y(e)]?e:null),0))?(r=w(p,t),v[r]?e(v[r]):(s.addEventListener("canplay",function(){o=_(r,window.location.href),v[x(t)]=v[r]=v[o]=this,n("assetLoaded",this,t),e(this)}),s.onerror=function(){i(r)},s.src=r,void s.load())):i(t)})}function P(t){let e,i;return A(),e=w(m,t),j[e]?Promise.resolve(j[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=_(e,window.location.href),"object"==typeof s&&f.set(s,i),j[x(t)]=j[e]=j[i]=s,n("assetLoaded",s,t),s))}const E=()=>{};function M(){let t=h();r().clearRect(0,0,t.width,t.height)}let k={},L={},I={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function z(t){let e=I[t.which];L[e]=!0,k[e]&&k[e](t)}function D(t){L[I[t.which]]=!1}function R(){L={}}function W(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function C(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}let X=[],Y=[],T={},N=[],U={},q={0:"left",1:"middle",2:"right"},F={x:0,y:0,radius:5};function K(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=F.x-Math.max(e,Math.min(F.x,e+t.width)),n=F.y-Math.max(i,Math.min(F.y,i+t.height));return s*s+n*n=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(F):K(t))return t}function B(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!0,Q(t,"onDown")}function H(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!1,Q(t,"onUp")}function J(t){Q(t,"onOver")}function G(){U={}}function Q(t,e){let i,s,n=h();if(!n)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let r=n.height/n.offsetHeight,o=n.getBoundingClientRect(),a=(i-o.left)*r,c=(s-o.top)*r;F.x=a,F.y=c,t.preventDefault();let l=$();l&&l[e]&&l[e](t),T[e]&&T[e](t,l)}class V{constructor({create:t,maxSize:e=1024}={}){this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function Z(t){return new V(t)}function tt(t,e){let i=[],s=e.x+e.width/2,n=e.y+e.height/2,h=t.y=e.y,r=t.y+t.height>=n&&t.y=e.x&&(h&&i.push(0),r&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1}}function it(t){return new et(t)}it.prototype=et.prototype,it.class=et;class st{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return nt(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function nt(t,e,i={}){let s=new st(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}nt.prototype=st.prototype,nt.class=st;class ht{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:n,ddx:h,ddy:o,width:a,height:c,image:l}=t;this.position=nt(e,i),this.velocity=nt(s,n),this.acceleration=nt(h,o),this.width=this.height=this.rotation=0,this.ttl=1/0,this.scaleX=this.scaleY=1,this.anchor={x:0,y:0},this.context=r();for(let e in t)this[e]=t[e];l&&(this.width=void 0!==a?a:l.width,this.height=void 0!==c?c:l.height),this.sx=0,this.sy=0}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}get width(){return this._w}get height(){return this._h}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}set width(t){this.scaleX=t<0?-1:1,this._w=Math.abs(t)}set height(t){this.scaleY=t<0?-1:1,this._h=Math.abs(t)}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,n=t.y;return t.anchor&&(s-=t.width*t.anchor.x,n-=t.height*t.anchor.y),es&&in}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;this.context.save(),this.context.translate(this.viewX,this.viewY),this.context.scale(this.scaleX,this.scaleY),this.rotation&&this.context.rotate(this.rotation),this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function rt(t){return new ht(t)}function ot(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],n=+i[1],h=s;if(s=n;h--)e.push(h);return e}rt.prototype=ht.prototype,rt.class=ht;class at{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:n}={}){this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(n)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:n,loop:h}=t[i];e=[],[].concat(s).map(t=>{e=e.concat(ot(t))}),this.animations[i]=a({spriteSheet:this,frames:e,frameRate:n,loop:h})}}}function ct(t){return new at(t)}return ct.prototype=at.prototype,ct.class=at,{Animation:a,imageAssets:b,audioAssets:v,dataAssets:j,setImagePath:function(t){g=t},setAudioPath:function(t){p=t},setDataPath:function(t){m=t},loadImage:S,loadAudio:O,loadData:P,load:function(...t){return A(),Promise.all(t.map(t=>{let e=y([].concat(t)[0]);return e.match(c)?S(t):e.match(l)?O(t):P(t)}))},init:function(i){return t=document.getElementById(i)||i||document.querySelector("canvas"),(e=t.getContext("2d")).imageSmoothingEnabled=!1,n("init"),{canvas:t,context:e}},getCanvas:h,getContext:r,on:s,off:function(t,e){let s;!i[t]||(s=i[t].indexOf(e))<0||i[t].splice(s,1)},emit:n,GameLoop:function({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){let h,r,o,a,c,l=0,d=1e3/t,u=1/t,f=e?M:E;function g(){if(r=requestAnimationFrame(g),o=performance.now(),a=o-h,h=o,!(a>1e3)){for(n("tick"),l+=a;l>=d;)c.update(u),l-=d;f(),c.render()}}return c={update:i,render:s,isStopped:!0,start(){h=performance.now(),this.isStopped=!1,requestAnimationFrame(g)},stop(){this.isStopped=!0,cancelAnimationFrame(r)}}},keyMap:I,initKeys:function(){let t;for(t=0;t<26;t++)I[65+t]=(10+t).toString(36);for(t=0;t<10;t++)I[48+t]=""+t;window.addEventListener("keydown",z),window.addEventListener("keyup",D),window.addEventListener("blur",R)},bindKeys:function(t,e){[].concat(t).map(t=>k[t]=e)},unbindKeys:function(t){[].concat(t).map(t=>k[t]=0)},keyPressed:function(t){return!!L[t]},registerPlugin:function(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let n=i(t,e,...s);return n||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),n=i["_o"+s].call(this,...e);return this._aInc(this,s,n,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))},unregisterPlugin:function(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);t.startsWith("before")?C(i._inc[s].before,e[t]):t.startsWith("after")&&C(i._inc[s].after,e[t])})},extendObject:function(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})},initPointer:function(){let t=h();t.addEventListener("mousedown",B),t.addEventListener("touchstart",B),t.addEventListener("mouseup",H),t.addEventListener("touchend",H),t.addEventListener("blur",G),t.addEventListener("mousemove",J),t.addEventListener("touchmove",J),s("tick",()=>{Y.length=0,X.map(t=>{Y.push(t)}),X.length=0})},pointer:F,track:function(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){X.push(this),this._r()},N.push(t))})},untrack:function(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=N.indexOf(t);-1!==e&&N.splice(e,1)})},pointerOver:function(t){return!!N.includes(t)&&$()===t},onPointerDown:function(t){T.onDown=t},onPointerUp:function(t){T.onUp=t},pointerPressed:function(t){return!!U[t]},Pool:Z,Quadtree:it,Sprite:rt,SpriteSheet:ct,setStoreItem:function(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))},getStoreItem:function(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e},TileEngine:function(t={}){let{width:e,height:i,tilewidth:s,tileheight:n,context:o=r(),tilesets:a,layers:c}=t,l=e*s,d=i*n,u=document.createElement("canvas"),f=u.getContext("2d");u.width=l,u.height=d;let g={},p={},m=[],_=Object.assign({context:o,mapwidth:l,mapheight:d,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),l-h().width),m.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),d-h().height),m.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),b(u)},renderLayer(t){let e=p[t],i=g[t];e||((e=document.createElement("canvas")).width=l,e.height=d,p[t]=e,_._r(i,e.getContext("2d"))),b(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let n=w(s),h=y(i),r=w(s+e.height),o=y(i+e.width),a=g[t];for(let t=n;t<=r;t++)for(let e=h;e<=o;e++)if(a.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||w(e.y),s=e.col||y(e.x);return g[t]?g[t].data[s+i*_.width]:-1},setTileAtLayer(t,e,i){let s=e.row||w(e.y),n=e.col||y(e.x);g[t]&&(this._d=!0,g[t].data[n+s*_.width]=i)},addObject(t){m.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=m.indexOf(t);-1!==e&&(m.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=_.tilesets.length-1;e>=0&&(s=_.tilesets[e],!(t/s.firstgid>=1));e--);let n=s.tilewidth||_.tilewidth,h=s.tileheight||_.tileheight,r=s.margin||0,o=s.image,a=t-s.firstgid,c=s.columns||o.width/(n+r)|0,l=i%_.width*n,d=(i/_.width|0)*h,u=a%c*(n+r),f=(a/c|0)*(h+r);e.drawImage(o,u,f,n,h,l,d,n,h)}),e.restore()},_p:x},t);function w(t){return t/_.tileheight|0}function y(t){return t/_.tilewidth|0}function x(){_.layers&&_.layers.map(t=>{g[t.name]=t,!1!==t.visible&&_._r(t,f)})}function b(t){const{width:e,height:i}=h(),s=Math.min(t.width,e),n=Math.min(t.height,i);_.context.drawImage(t,_.sx,_.sy,s,n,0,0,s,n)}return _.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){let t=window.__k.d[window.__k.u(e.source,i)];Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){let t=window.__k.i[window.__k.u(e.image,i)];e.image=t}}),x(),_},Vector:nt}}(); \ No newline at end of file +var kontra=function(){"use strict";let t,e,i={};function s(t,e){i[t]=i[t]||[],i[t].push(e)}function h(t,...e){i[t]&&i[t].map(t=>t(...e))}function n(){return t}function r(){return e}class o{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:h,height:n,margin:r=0}=t.frame;this.width=h,this.height=n,this.margin=r,this._f=0,this._a=0}clone(){return a(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:h=r()}={}){let n=this.frames[this._f]/this.spriteSheet._f|0,o=this.frames[this._f]%this.spriteSheet._f|0;h.drawImage(this.spriteSheet.image,o*this.width+(2*o+1)*this.margin,n*this.height+(2*n+1)*this.margin,this.width,this.height,t,e,i,s)}}function a(t){return new o(t)}a.prototype=o.prototype,a.class=o;let c=/(jpeg|jpg|gif|png)$/,l=/(wav|mp3|ogg|aac)$/,d=/^\//,u=/\/$/,f=new WeakMap,g="",p="",m="";function _(t,e){return new URL(t,e).href}function w(t,e){return[t.replace(u,""),t?e.replace(d,""):e].filter(t=>t).join("/")}function y(t){return t.split(".").pop()}function x(t){let e=t.replace("."+y(t),"");return 2==e.split("/").length?e.replace(d,""):e}let b={},v={},j={};function A(){window.__k||(window.__k={dm:f,u:_,d:j,i:b})}function S(t){return A(),new Promise((e,i)=>{let s,n,r;if(s=w(g,t),b[s])return e(b[s]);(n=new Image).onload=function(){r=_(s,window.location.href),b[x(t)]=b[s]=b[r]=this,h("assetLoaded",this,t),e(this)},n.onerror=function(){i(s)},n.src=s})}function O(t){return new Promise((e,i)=>{let s,n,r,o;return s=new Audio,n=function(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}(s),(t=[].concat(t).reduce((t,e)=>t||(n[y(e)]?e:null),0))?(r=w(p,t),v[r]?e(v[r]):(s.addEventListener("canplay",function(){o=_(r,window.location.href),v[x(t)]=v[r]=v[o]=this,h("assetLoaded",this,t),e(this)}),s.onerror=function(){i(r)},s.src=r,void s.load())):i(t)})}function P(t){let e,i;return A(),e=w(m,t),j[e]?Promise.resolve(j[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=_(e,window.location.href),"object"==typeof s&&f.set(s,i),j[x(t)]=j[e]=j[i]=s,h("assetLoaded",s,t),s))}const E=()=>{};function M(){let t=n();r().clearRect(0,0,t.width,t.height)}let k={},L={},I={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function z(t){let e=I[t.which];L[e]=!0,k[e]&&k[e](t)}function D(t){L[I[t.which]]=!1}function R(){L={}}function W(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function C(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}let T=[],N=[],X={},Y=[],U={},q={0:"left",1:"middle",2:"right"},F={x:0,y:0,radius:5};function K(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=F.x-Math.max(e,Math.min(F.x,e+t.width)),h=F.y-Math.max(i,Math.min(F.y,i+t.height));return s*s+h*h=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(F):K(t))return t}function B(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!0,Q(t,"onDown")}function H(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!1,Q(t,"onUp")}function J(t){Q(t,"onOver")}function G(){U={}}function Q(t,e){let i,s,h=n();if(!h)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let r=h.height/h.offsetHeight,o=h.getBoundingClientRect(),a=(i-o.left)*r,c=(s-o.top)*r;F.x=a,F.y=c,t.preventDefault();let l=$();l&&l[e]&&l[e](t),X[e]&&X[e](t,l)}class V{constructor({create:t,maxSize:e=1024}={}){this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function Z(t){return new V(t)}function tt(t,e){let i=[],s=e.x+e.width/2,h=e.y+e.height/2,n=t.y=e.y,r=t.y+t.height>=h&&t.y=e.x&&(n&&i.push(0),r&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1}}function it(t){return new et(t)}it.prototype=et.prototype,it.class=et;class st{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return ht(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function ht(t,e,i={}){let s=new st(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}ht.prototype=st.prototype,ht.class=st;class nt{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:h,ddx:n,ddy:o,width:a,height:c,image:l}=t;this.position=ht(e,i),this.velocity=ht(s,h),this.acceleration=ht(n,o),this.width=this.height=this.rotation=0,this.ttl=1/0,this.anchor={x:0,y:0},this.context=r();for(let e in t)this[e]=t[e];l&&(this.width=void 0!==a?a:l.width,this.height=void 0!==c?c:l.height),this.sx=0,this.sy=0,this._fx=this._fy=1}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}get width(){return this._w}get height(){return this._h}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}set width(t){this._fx=t<0?-1:1,this._w=Math.abs(t)}set height(t){this._fy=t<0?-1:1,this._h=Math.abs(t)}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,h=t.y;return t.anchor&&(s-=t.width*t.anchor.x,h-=t.height*t.anchor.y),es&&ih}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;if(this.context.save(),this.context.translate(this.viewX,this.viewY),this.rotation&&this.context.rotate(this.rotation),-1==this._fx||-1==this._fy){let i=this.width/2+t,s=this.height/2+e;this.context.translate(i,s),this.context.scale(this._fx,this._fy),this.context.translate(-i,-s)}this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function rt(t){return new nt(t)}function ot(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],h=+i[1],n=s;if(s=h;n--)e.push(n);return e}rt.prototype=nt.prototype,rt.class=nt;class at{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:h}={}){this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(h)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:h,loop:n}=t[i];e=[],[].concat(s).map(t=>{e=e.concat(ot(t))}),this.animations[i]=a({spriteSheet:this,frames:e,frameRate:h,loop:n})}}}function ct(t){return new at(t)}return ct.prototype=at.prototype,ct.class=at,{Animation:a,imageAssets:b,audioAssets:v,dataAssets:j,setImagePath:function(t){g=t},setAudioPath:function(t){p=t},setDataPath:function(t){m=t},loadImage:S,loadAudio:O,loadData:P,load:function(...t){return A(),Promise.all(t.map(t=>{let e=y([].concat(t)[0]);return e.match(c)?S(t):e.match(l)?O(t):P(t)}))},init:function(i){return t=document.getElementById(i)||i||document.querySelector("canvas"),(e=t.getContext("2d")).imageSmoothingEnabled=!1,h("init"),{canvas:t,context:e}},getCanvas:n,getContext:r,on:s,off:function(t,e){let s;!i[t]||(s=i[t].indexOf(e))<0||i[t].splice(s,1)},emit:h,GameLoop:function({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){let n,r,o,a,c,l=0,d=1e3/t,u=1/t,f=e?M:E;function g(){if(r=requestAnimationFrame(g),o=performance.now(),a=o-n,n=o,!(a>1e3)){for(h("tick"),l+=a;l>=d;)c.update(u),l-=d;f(),c.render()}}return c={update:i,render:s,isStopped:!0,start(){n=performance.now(),this.isStopped=!1,requestAnimationFrame(g)},stop(){this.isStopped=!0,cancelAnimationFrame(r)}}},keyMap:I,initKeys:function(){let t;for(t=0;t<26;t++)I[65+t]=(10+t).toString(36);for(t=0;t<10;t++)I[48+t]=""+t;window.addEventListener("keydown",z),window.addEventListener("keyup",D),window.addEventListener("blur",R)},bindKeys:function(t,e){[].concat(t).map(t=>k[t]=e)},unbindKeys:function(t){[].concat(t).map(t=>k[t]=0)},keyPressed:function(t){return!!L[t]},registerPlugin:function(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let h=i(t,e,...s);return h||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),h=i["_o"+s].call(this,...e);return this._aInc(this,s,h,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))},unregisterPlugin:function(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);t.startsWith("before")?C(i._inc[s].before,e[t]):t.startsWith("after")&&C(i._inc[s].after,e[t])})},extendObject:function(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})},initPointer:function(){let t=n();t.addEventListener("mousedown",B),t.addEventListener("touchstart",B),t.addEventListener("mouseup",H),t.addEventListener("touchend",H),t.addEventListener("blur",G),t.addEventListener("mousemove",J),t.addEventListener("touchmove",J),s("tick",()=>{N.length=0,T.map(t=>{N.push(t)}),T.length=0})},pointer:F,track:function(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){T.push(this),this._r()},Y.push(t))})},untrack:function(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=Y.indexOf(t);-1!==e&&Y.splice(e,1)})},pointerOver:function(t){return!!Y.includes(t)&&$()===t},onPointerDown:function(t){X.onDown=t},onPointerUp:function(t){X.onUp=t},pointerPressed:function(t){return!!U[t]},Pool:Z,Quadtree:it,Sprite:rt,SpriteSheet:ct,setStoreItem:function(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))},getStoreItem:function(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e},TileEngine:function(t={}){let{width:e,height:i,tilewidth:s,tileheight:h,context:o=r(),tilesets:a,layers:c}=t,l=e*s,d=i*h,u=document.createElement("canvas"),f=u.getContext("2d");u.width=l,u.height=d;let g={},p={},m=[],_=Object.assign({context:o,mapwidth:l,mapheight:d,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),l-n().width),m.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),d-n().height),m.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),b(u)},renderLayer(t){let e=p[t],i=g[t];e||((e=document.createElement("canvas")).width=l,e.height=d,p[t]=e,_._r(i,e.getContext("2d"))),b(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let h=w(s),n=y(i),r=w(s+e.height),o=y(i+e.width),a=g[t];for(let t=h;t<=r;t++)for(let e=n;e<=o;e++)if(a.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||w(e.y),s=e.col||y(e.x);return g[t]?g[t].data[s+i*_.width]:-1},setTileAtLayer(t,e,i){let s=e.row||w(e.y),h=e.col||y(e.x);g[t]&&(this._d=!0,g[t].data[h+s*_.width]=i)},addObject(t){m.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=m.indexOf(t);-1!==e&&(m.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=_.tilesets.length-1;e>=0&&(s=_.tilesets[e],!(t/s.firstgid>=1));e--);let h=s.tilewidth||_.tilewidth,n=s.tileheight||_.tileheight,r=s.margin||0,o=s.image,a=t-s.firstgid,c=s.columns||o.width/(h+r)|0,l=i%_.width*h,d=(i/_.width|0)*n,u=a%c*(h+r),f=(a/c|0)*(n+r);e.drawImage(o,u,f,h,n,l,d,h,n)}),e.restore()},_p:x},t);function w(t){return t/_.tileheight|0}function y(t){return t/_.tilewidth|0}function x(){_.layers&&_.layers.map(t=>{g[t.name]=t,!1!==t.visible&&_._r(t,f)})}function b(t){const{width:e,height:i}=n(),s=Math.min(t.width,e),h=Math.min(t.height,i);_.context.drawImage(t,_.sx,_.sy,s,h,0,0,s,h)}return _.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){let t=window.__k.d[window.__k.u(e.source,i)];Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){let t=window.__k.i[window.__k.u(e.image,i)];e.image=t}}),x(),_},Vector:ht}}(); \ No newline at end of file diff --git a/kontra.min.mjs b/kontra.min.mjs index 9fd5d5c7..765339ab 100644 --- a/kontra.min.mjs +++ b/kontra.min.mjs @@ -1 +1 @@ -let canvasEl,context,callbacks={};function on(t,e){callbacks[t]=callbacks[t]||[],callbacks[t].push(e)}function off(t,e){let i;!callbacks[t]||(i=callbacks[t].indexOf(e))<0||callbacks[t].splice(i,1)}function emit(t,...e){callbacks[t]&&callbacks[t].map(t=>t(...e))}function getCanvas(){return canvasEl}function getContext(){return context}function init(t){if(!(canvasEl=document.getElementById(t)||t||document.querySelector("canvas")))throw Error("You must provide a canvas element for the game");return(context=canvasEl.getContext("2d")).imageSmoothingEnabled=!1,emit("init"),{canvas:canvasEl,context:context}}class Animation{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:n,height:a,margin:r=0}=t.frame;this.width=n,this.height=a,this.margin=r,this._f=0,this._a=0}clone(){return animationFactory(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:n=getContext()}={}){let a=this.frames[this._f]/this.spriteSheet._f|0,r=this.frames[this._f]%this.spriteSheet._f|0;n.drawImage(this.spriteSheet.image,r*this.width+(2*r+1)*this.margin,a*this.height+(2*a+1)*this.margin,this.width,this.height,t,e,i,s)}}function animationFactory(t){return new Animation(t)}animationFactory.prototype=Animation.prototype,animationFactory.class=Animation;let imageRegex=/(jpeg|jpg|gif|png)$/,audioRegex=/(wav|mp3|ogg|aac)$/,leadingSlash=/^\//,trailingSlash=/\/$/,dataMap=new WeakMap,imagePath="",audioPath="",dataPath="";function getUrl(t,e){return new URL(t,e).href}function joinPath(t,e){return[t.replace(trailingSlash,""),t?e.replace(leadingSlash,""):e].filter(t=>t).join("/")}function getExtension(t){return t.split(".").pop()}function getName(t){let e=t.replace("."+getExtension(t),"");return 2==e.split("/").length?e.replace(leadingSlash,""):e}function getCanPlay(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}let imageAssets={},audioAssets={},dataAssets={};function addGlobal(){window.__k||(window.__k={dm:dataMap,u:getUrl,d:dataAssets,i:imageAssets})}function setImagePath(t){imagePath=t}function setAudioPath(t){audioPath=t}function setDataPath(t){dataPath=t}function loadImage(t){return addGlobal(),new Promise((e,i)=>{let s,n,a;if(s=joinPath(imagePath,t),imageAssets[s])return e(imageAssets[s]);(n=new Image).onload=function(){a=getUrl(s,window.location.href),imageAssets[getName(t)]=imageAssets[s]=imageAssets[a]=this,emit("assetLoaded",this,t),e(this)},n.onerror=function(){i("Unable to load image "+s)},n.src=s})}function loadAudio(t){return new Promise((e,i)=>{let s,n,a,r;return s=new Audio,n=getCanPlay(s),(t=[].concat(t).reduce((t,e)=>t||(n[getExtension(e)]?e:null),0))?(a=joinPath(audioPath,t),audioAssets[a]?e(audioAssets[a]):(s.addEventListener("canplay",function(){r=getUrl(a,window.location.href),audioAssets[getName(t)]=audioAssets[a]=audioAssets[r]=this,emit("assetLoaded",this,t),e(this)}),s.onerror=function(){i("Unable to load audio "+a)},s.src=a,void s.load())):i("cannot play any of the audio formats provided"+t)})}function loadData(t){let e,i;return addGlobal(),e=joinPath(dataPath,t),dataAssets[e]?Promise.resolve(dataAssets[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=getUrl(e,window.location.href),"object"==typeof s&&dataMap.set(s,i),dataAssets[getName(t)]=dataAssets[e]=dataAssets[i]=s,emit("assetLoaded",s,t),s))}function load(...t){return addGlobal(),Promise.all(t.map(t=>{let e=getExtension([].concat(t)[0]);return e.match(imageRegex)?loadImage(t):e.match(audioRegex)?loadAudio(t):loadData(t)}))}const noop=()=>{};function clear(){let t=getCanvas();getContext().clearRect(0,0,t.width,t.height)}function GameLoop({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){if(!i||!s)throw Error("You must provide update() and render() functions");let n,a,r,o,h,c=0,d=1e3/t,l=1/t,u=e?clear:noop;function p(){if(a=requestAnimationFrame(p),r=performance.now(),o=r-n,n=r,!(o>1e3)){for(emit("tick"),c+=o;c>=d;)h.update(l),c-=d;u(),h.render()}}return h={update:i,render:s,isStopped:!0,start(){n=performance.now(),this.isStopped=!1,requestAnimationFrame(p)},stop(){this.isStopped=!0,cancelAnimationFrame(a)},_frame:p,set _last(t){n=t}}}let callbacks$1={},pressedKeys={},keyMap={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function keydownEventHandler(t){let e=keyMap[t.which];pressedKeys[e]=!0,callbacks$1[e]&&callbacks$1[e](t)}function keyupEventHandler(t){pressedKeys[keyMap[t.which]]=!1}function blurEventHandler(){pressedKeys={}}function initKeys(){let t;for(t=0;t<26;t++)keyMap[65+t]=(10+t).toString(36);for(t=0;t<10;t++)keyMap[48+t]=""+t;window.addEventListener("keydown",keydownEventHandler),window.addEventListener("keyup",keyupEventHandler),window.addEventListener("blur",blurEventHandler)}function bindKeys(t,e){[].concat(t).map(t=>callbacks$1[t]=e)}function unbindKeys(t){[].concat(t).map(t=>callbacks$1[t]=0)}function keyPressed(t){return!!pressedKeys[t]}function getMethod(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function removeInterceptor(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}function registerPlugin(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let n=i(t,e,...s);return n||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=getMethod(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),n=i["_o"+s].call(this,...e);return this._aInc(this,s,n,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))}function unregisterPlugin(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=getMethod(t);t.startsWith("before")?removeInterceptor(i._inc[s].before,e[t]):t.startsWith("after")&&removeInterceptor(i._inc[s].after,e[t])})}function extendObject(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})}let thisFrameRenderOrder=[],lastFrameRenderOrder=[],callbacks$2={},trackedObjects=[],pressedButtons={},buttonMap={0:"left",1:"middle",2:"right"},pointer={x:0,y:0,radius:5};function circleRectCollision(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=pointer.x-Math.max(e,Math.min(pointer.x,e+t.width)),n=pointer.y-Math.max(i,Math.min(pointer.y,i+t.height));return s*s+n*n=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(pointer):circleRectCollision(t))return t}function pointerDownHandler(t){let e=void 0!==t.button?buttonMap[t.button]:"left";pressedButtons[e]=!0,pointerHandler(t,"onDown")}function pointerUpHandler(t){let e=void 0!==t.button?buttonMap[t.button]:"left";pressedButtons[e]=!1,pointerHandler(t,"onUp")}function mouseMoveHandler(t){pointerHandler(t,"onOver")}function blurEventHandler$1(){pressedButtons={}}function pointerHandler(t,e){let i,s,n=getCanvas();if(!n)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let a=n.height/n.offsetHeight,r=n.getBoundingClientRect(),o=(i-r.left)*a,h=(s-r.top)*a;pointer.x=o,pointer.y=h,t.preventDefault();let c=getCurrentObject();c&&c[e]&&c[e](t),callbacks$2[e]&&callbacks$2[e](t,c)}function initPointer(){let t=getCanvas();t.addEventListener("mousedown",pointerDownHandler),t.addEventListener("touchstart",pointerDownHandler),t.addEventListener("mouseup",pointerUpHandler),t.addEventListener("touchend",pointerUpHandler),t.addEventListener("blur",blurEventHandler$1),t.addEventListener("mousemove",mouseMoveHandler),t.addEventListener("touchmove",mouseMoveHandler),on("tick",()=>{lastFrameRenderOrder.length=0,thisFrameRenderOrder.map(t=>{lastFrameRenderOrder.push(t)}),thisFrameRenderOrder.length=0})}function track(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){thisFrameRenderOrder.push(this),this._r()},trackedObjects.push(t))})}function untrack(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=trackedObjects.indexOf(t);-1!==e&&trackedObjects.splice(e,1)})}function pointerOver(t){return!!trackedObjects.includes(t)&&getCurrentObject()===t}function onPointerDown(t){callbacks$2.onDown=t}function onPointerUp(t){callbacks$2.onUp=t}function pointerPressed(t){return!!pressedButtons[t]}class Pool{constructor({create:t,maxSize:e=1024}={}){let i;if(!t||!(i=t())||!(i.update&&i.init&&i.isAlive))throw Error("Must provide create() function which returns an object with init(), update(), and isAlive() functions");this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function poolFactory(t){return new Pool(t)}function getIndices(t,e){let i=[],s=e.x+e.width/2,n=e.y+e.height/2,a=t.y=e.y,r=t.y+t.height>=n&&t.y=e.x&&(a&&i.push(0),r&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1,this._s[i]._p=this}}function quadtreeFactory(t){return new Quadtree(t)}quadtreeFactory.prototype=Quadtree.prototype,quadtreeFactory.class=Quadtree;class Vector{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return vectorFactory(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function vectorFactory(t,e,i={}){let s=new Vector(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}vectorFactory.prototype=Vector.prototype,vectorFactory.class=Vector;class Sprite{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:n,ddx:a,ddy:r,width:o,height:h,image:c}=t;this.position=vectorFactory(e,i),this.velocity=vectorFactory(s,n),this.acceleration=vectorFactory(a,r),this.width=this.height=this.rotation=0,this.ttl=1/0,this.scaleX=this.scaleY=1,this.anchor={x:0,y:0},this.context=getContext();for(let e in t)this[e]=t[e];c&&(this.width=void 0!==o?o:c.width,this.height=void 0!==h?h:c.height),this.sx=0,this.sy=0}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}get width(){return this._w}get height(){return this._h}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}set width(t){this.scaleX=t<0?-1:1,this._w=Math.abs(t)}set height(t){this.scaleY=t<0?-1:1,this._h=Math.abs(t)}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,n=t.y;return t.anchor&&(s-=t.width*t.anchor.x,n-=t.height*t.anchor.y),es&&in}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;this.context.save(),this.context.translate(this.viewX,this.viewY),this.context.scale(this.scaleX,this.scaleY),this.rotation&&this.context.rotate(this.rotation),this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function spriteFactory(t){return new Sprite(t)}function parseFrames(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],n=+i[1],a=s;if(s=n;a--)e.push(a);return e}spriteFactory.prototype=Sprite.prototype,spriteFactory.class=Sprite;class SpriteSheet{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:n}={}){if(!t)throw Error("You must provide an Image for the SpriteSheet");this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(n)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:n,loop:a}=t[i];if(e=[],void 0===s)throw Error("Animation "+i+" must provide a frames property");[].concat(s).map(t=>{e=e.concat(parseFrames(t))}),this.animations[i]=animationFactory({spriteSheet:this,frames:e,frameRate:n,loop:a})}}}function spriteSheetFactory(t){return new SpriteSheet(t)}function setStoreItem(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))}function getStoreItem(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e}function TileEngine(t={}){let{width:e,height:i,tilewidth:s,tileheight:n,context:a=getContext(),tilesets:r,layers:o}=t,h=e*s,c=i*n,d=document.createElement("canvas"),l=d.getContext("2d");d.width=h,d.height=c;let u={},p={},g=[],m=Object.assign({context:a,mapwidth:h,mapheight:c,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),h-getCanvas().width),g.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),c-getCanvas().height),g.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),_(d)},renderLayer(t){let e=p[t],i=u[t];e||((e=document.createElement("canvas")).width=h,e.height=c,p[t]=e,m._r(i,e.getContext("2d"))),_(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let n=f(s),a=y(i),r=f(s+e.height),o=y(i+e.width),h=u[t];for(let t=n;t<=r;t++)for(let e=a;e<=o;e++)if(h.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||f(e.y),s=e.col||y(e.x);return u[t]?u[t].data[s+i*m.width]:-1},setTileAtLayer(t,e,i){let s=e.row||f(e.y),n=e.col||y(e.x);u[t]&&(this._d=!0,u[t].data[n+s*m.width]=i)},addObject(t){g.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=g.indexOf(t);-1!==e&&(g.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=m.tilesets.length-1;e>=0&&(s=m.tilesets[e],!(t/s.firstgid>=1));e--);let n=s.tilewidth||m.tilewidth,a=s.tileheight||m.tileheight,r=s.margin||0,o=s.image,h=t-s.firstgid,c=s.columns||o.width/(n+r)|0,d=i%m.width*n,l=(i/m.width|0)*a,u=h%c*(n+r),p=(h/c|0)*(a+r);e.drawImage(o,u,p,n,a,d,l,n,a)}),e.restore()},_p:w,layerCanvases:p},t);function f(t){return t/m.tileheight|0}function y(t){return t/m.tilewidth|0}function w(){m.layers&&m.layers.map(t=>{u[t.name]=t,!1!==t.visible&&m._r(t,l)})}function _(t){const{width:e,height:i}=getCanvas(),s=Math.min(t.width,e),n=Math.min(t.height,i);m.context.drawImage(t,m.sx,m.sy,s,n,0,0,s,n)}return m.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){if(!window.__k)throw Error('You must use "load" or "loadData" to resolve tileset.source');let t=window.__k.d[window.__k.u(e.source,i)];if(!t)throw Error(`You must load the tileset source "${e.source}" before loading the tileset`);Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){if(!window.__k)throw Error('You must use "load" or "loadImage" to resolve tileset.image');let t=window.__k.i[window.__k.u(e.image,i)];if(!t)throw Error(`You must load the image "${e.image}" before loading the tileset`);e.image=t}}),w(),m}spriteSheetFactory.prototype=SpriteSheet.prototype,spriteSheetFactory.class=SpriteSheet;let kontra={Animation:animationFactory,imageAssets:imageAssets,audioAssets:audioAssets,dataAssets:dataAssets,setImagePath:setImagePath,setAudioPath:setAudioPath,setDataPath:setDataPath,loadImage:loadImage,loadAudio:loadAudio,loadData:loadData,load:load,init:init,getCanvas:getCanvas,getContext:getContext,on:on,off:off,emit:emit,GameLoop:GameLoop,keyMap:keyMap,initKeys:initKeys,bindKeys:bindKeys,unbindKeys:unbindKeys,keyPressed:keyPressed,registerPlugin:registerPlugin,unregisterPlugin:unregisterPlugin,extendObject:extendObject,initPointer:initPointer,pointer:pointer,track:track,untrack:untrack,pointerOver:pointerOver,onPointerDown:onPointerDown,onPointerUp:onPointerUp,pointerPressed:pointerPressed,Pool:poolFactory,Quadtree:quadtreeFactory,Sprite:spriteFactory,SpriteSheet:spriteSheetFactory,setStoreItem:setStoreItem,getStoreItem:getStoreItem,TileEngine:TileEngine,Vector:vectorFactory};export{animationFactory as Animation,imageAssets,audioAssets,dataAssets,setImagePath,setAudioPath,setDataPath,loadImage,loadAudio,loadData,load,init,getCanvas,getContext,on,off,emit,GameLoop,keyMap,initKeys,bindKeys,unbindKeys,keyPressed,registerPlugin,unregisterPlugin,extendObject,initPointer,pointer,track,untrack,pointerOver,onPointerDown,onPointerUp,pointerPressed,poolFactory as Pool,quadtreeFactory as Quadtree,spriteFactory as Sprite,spriteSheetFactory as SpriteSheet,setStoreItem,getStoreItem,TileEngine,vectorFactory as Vector};export default kontra; \ No newline at end of file +let canvasEl,context,callbacks={};function on(t,e){callbacks[t]=callbacks[t]||[],callbacks[t].push(e)}function off(t,e){let i;!callbacks[t]||(i=callbacks[t].indexOf(e))<0||callbacks[t].splice(i,1)}function emit(t,...e){callbacks[t]&&callbacks[t].map(t=>t(...e))}function getCanvas(){return canvasEl}function getContext(){return context}function init(t){if(!(canvasEl=document.getElementById(t)||t||document.querySelector("canvas")))throw Error("You must provide a canvas element for the game");return(context=canvasEl.getContext("2d")).imageSmoothingEnabled=!1,emit("init"),{canvas:canvasEl,context:context}}class Animation{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:n,height:a,margin:r=0}=t.frame;this.width=n,this.height=a,this.margin=r,this._f=0,this._a=0}clone(){return animationFactory(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:n=getContext()}={}){let a=this.frames[this._f]/this.spriteSheet._f|0,r=this.frames[this._f]%this.spriteSheet._f|0;n.drawImage(this.spriteSheet.image,r*this.width+(2*r+1)*this.margin,a*this.height+(2*a+1)*this.margin,this.width,this.height,t,e,i,s)}}function animationFactory(t){return new Animation(t)}animationFactory.prototype=Animation.prototype,animationFactory.class=Animation;let imageRegex=/(jpeg|jpg|gif|png)$/,audioRegex=/(wav|mp3|ogg|aac)$/,leadingSlash=/^\//,trailingSlash=/\/$/,dataMap=new WeakMap,imagePath="",audioPath="",dataPath="";function getUrl(t,e){return new URL(t,e).href}function joinPath(t,e){return[t.replace(trailingSlash,""),t?e.replace(leadingSlash,""):e].filter(t=>t).join("/")}function getExtension(t){return t.split(".").pop()}function getName(t){let e=t.replace("."+getExtension(t),"");return 2==e.split("/").length?e.replace(leadingSlash,""):e}function getCanPlay(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}let imageAssets={},audioAssets={},dataAssets={};function addGlobal(){window.__k||(window.__k={dm:dataMap,u:getUrl,d:dataAssets,i:imageAssets})}function setImagePath(t){imagePath=t}function setAudioPath(t){audioPath=t}function setDataPath(t){dataPath=t}function loadImage(t){return addGlobal(),new Promise((e,i)=>{let s,n,a;if(s=joinPath(imagePath,t),imageAssets[s])return e(imageAssets[s]);(n=new Image).onload=function(){a=getUrl(s,window.location.href),imageAssets[getName(t)]=imageAssets[s]=imageAssets[a]=this,emit("assetLoaded",this,t),e(this)},n.onerror=function(){i("Unable to load image "+s)},n.src=s})}function loadAudio(t){return new Promise((e,i)=>{let s,n,a,r;return s=new Audio,n=getCanPlay(s),(t=[].concat(t).reduce((t,e)=>t||(n[getExtension(e)]?e:null),0))?(a=joinPath(audioPath,t),audioAssets[a]?e(audioAssets[a]):(s.addEventListener("canplay",function(){r=getUrl(a,window.location.href),audioAssets[getName(t)]=audioAssets[a]=audioAssets[r]=this,emit("assetLoaded",this,t),e(this)}),s.onerror=function(){i("Unable to load audio "+a)},s.src=a,void s.load())):i("cannot play any of the audio formats provided"+t)})}function loadData(t){let e,i;return addGlobal(),e=joinPath(dataPath,t),dataAssets[e]?Promise.resolve(dataAssets[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=getUrl(e,window.location.href),"object"==typeof s&&dataMap.set(s,i),dataAssets[getName(t)]=dataAssets[e]=dataAssets[i]=s,emit("assetLoaded",s,t),s))}function load(...t){return addGlobal(),Promise.all(t.map(t=>{let e=getExtension([].concat(t)[0]);return e.match(imageRegex)?loadImage(t):e.match(audioRegex)?loadAudio(t):loadData(t)}))}const noop=()=>{};function clear(){let t=getCanvas();getContext().clearRect(0,0,t.width,t.height)}function GameLoop({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){if(!i||!s)throw Error("You must provide update() and render() functions");let n,a,r,o,h,c=0,d=1e3/t,l=1/t,u=e?clear:noop;function p(){if(a=requestAnimationFrame(p),r=performance.now(),o=r-n,n=r,!(o>1e3)){for(emit("tick"),c+=o;c>=d;)h.update(l),c-=d;u(),h.render()}}return h={update:i,render:s,isStopped:!0,start(){n=performance.now(),this.isStopped=!1,requestAnimationFrame(p)},stop(){this.isStopped=!0,cancelAnimationFrame(a)},_frame:p,set _last(t){n=t}}}let callbacks$1={},pressedKeys={},keyMap={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function keydownEventHandler(t){let e=keyMap[t.which];pressedKeys[e]=!0,callbacks$1[e]&&callbacks$1[e](t)}function keyupEventHandler(t){pressedKeys[keyMap[t.which]]=!1}function blurEventHandler(){pressedKeys={}}function initKeys(){let t;for(t=0;t<26;t++)keyMap[65+t]=(10+t).toString(36);for(t=0;t<10;t++)keyMap[48+t]=""+t;window.addEventListener("keydown",keydownEventHandler),window.addEventListener("keyup",keyupEventHandler),window.addEventListener("blur",blurEventHandler)}function bindKeys(t,e){[].concat(t).map(t=>callbacks$1[t]=e)}function unbindKeys(t){[].concat(t).map(t=>callbacks$1[t]=0)}function keyPressed(t){return!!pressedKeys[t]}function getMethod(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function removeInterceptor(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}function registerPlugin(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let n=i(t,e,...s);return n||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=getMethod(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),n=i["_o"+s].call(this,...e);return this._aInc(this,s,n,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))}function unregisterPlugin(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=getMethod(t);t.startsWith("before")?removeInterceptor(i._inc[s].before,e[t]):t.startsWith("after")&&removeInterceptor(i._inc[s].after,e[t])})}function extendObject(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})}let thisFrameRenderOrder=[],lastFrameRenderOrder=[],callbacks$2={},trackedObjects=[],pressedButtons={},buttonMap={0:"left",1:"middle",2:"right"},pointer={x:0,y:0,radius:5};function circleRectCollision(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=pointer.x-Math.max(e,Math.min(pointer.x,e+t.width)),n=pointer.y-Math.max(i,Math.min(pointer.y,i+t.height));return s*s+n*n=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(pointer):circleRectCollision(t))return t}function pointerDownHandler(t){let e=void 0!==t.button?buttonMap[t.button]:"left";pressedButtons[e]=!0,pointerHandler(t,"onDown")}function pointerUpHandler(t){let e=void 0!==t.button?buttonMap[t.button]:"left";pressedButtons[e]=!1,pointerHandler(t,"onUp")}function mouseMoveHandler(t){pointerHandler(t,"onOver")}function blurEventHandler$1(){pressedButtons={}}function pointerHandler(t,e){let i,s,n=getCanvas();if(!n)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let a=n.height/n.offsetHeight,r=n.getBoundingClientRect(),o=(i-r.left)*a,h=(s-r.top)*a;pointer.x=o,pointer.y=h,t.preventDefault();let c=getCurrentObject();c&&c[e]&&c[e](t),callbacks$2[e]&&callbacks$2[e](t,c)}function initPointer(){let t=getCanvas();t.addEventListener("mousedown",pointerDownHandler),t.addEventListener("touchstart",pointerDownHandler),t.addEventListener("mouseup",pointerUpHandler),t.addEventListener("touchend",pointerUpHandler),t.addEventListener("blur",blurEventHandler$1),t.addEventListener("mousemove",mouseMoveHandler),t.addEventListener("touchmove",mouseMoveHandler),on("tick",()=>{lastFrameRenderOrder.length=0,thisFrameRenderOrder.map(t=>{lastFrameRenderOrder.push(t)}),thisFrameRenderOrder.length=0})}function track(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){thisFrameRenderOrder.push(this),this._r()},trackedObjects.push(t))})}function untrack(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=trackedObjects.indexOf(t);-1!==e&&trackedObjects.splice(e,1)})}function pointerOver(t){return!!trackedObjects.includes(t)&&getCurrentObject()===t}function onPointerDown(t){callbacks$2.onDown=t}function onPointerUp(t){callbacks$2.onUp=t}function pointerPressed(t){return!!pressedButtons[t]}class Pool{constructor({create:t,maxSize:e=1024}={}){let i;if(!t||!(i=t())||!(i.update&&i.init&&i.isAlive))throw Error("Must provide create() function which returns an object with init(), update(), and isAlive() functions");this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function poolFactory(t){return new Pool(t)}function getIndices(t,e){let i=[],s=e.x+e.width/2,n=e.y+e.height/2,a=t.y=e.y,r=t.y+t.height>=n&&t.y=e.x&&(a&&i.push(0),r&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1,this._s[i]._p=this}}function quadtreeFactory(t){return new Quadtree(t)}quadtreeFactory.prototype=Quadtree.prototype,quadtreeFactory.class=Quadtree;class Vector{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return vectorFactory(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function vectorFactory(t,e,i={}){let s=new Vector(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}vectorFactory.prototype=Vector.prototype,vectorFactory.class=Vector;class Sprite{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:n,ddx:a,ddy:r,width:o,height:h,image:c}=t;this.position=vectorFactory(e,i),this.velocity=vectorFactory(s,n),this.acceleration=vectorFactory(a,r),this.width=this.height=this.rotation=0,this.ttl=1/0,this.anchor={x:0,y:0},this.context=getContext();for(let e in t)this[e]=t[e];c&&(this.width=void 0!==o?o:c.width,this.height=void 0!==h?h:c.height),this.sx=0,this.sy=0,this._fx=this._fy=1}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}get width(){return this._w}get height(){return this._h}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}set width(t){this._fx=t<0?-1:1,this._w=Math.abs(t)}set height(t){this._fy=t<0?-1:1,this._h=Math.abs(t)}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,n=t.y;return t.anchor&&(s-=t.width*t.anchor.x,n-=t.height*t.anchor.y),es&&in}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;if(this.context.save(),this.context.translate(this.viewX,this.viewY),this.rotation&&this.context.rotate(this.rotation),-1==this._fx||-1==this._fy){let i=this.width/2+t,s=this.height/2+e;this.context.translate(i,s),this.context.scale(this._fx,this._fy),this.context.translate(-i,-s)}this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function spriteFactory(t){return new Sprite(t)}function parseFrames(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],n=+i[1],a=s;if(s=n;a--)e.push(a);return e}spriteFactory.prototype=Sprite.prototype,spriteFactory.class=Sprite;class SpriteSheet{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:n}={}){if(!t)throw Error("You must provide an Image for the SpriteSheet");this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(n)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:n,loop:a}=t[i];if(e=[],void 0===s)throw Error("Animation "+i+" must provide a frames property");[].concat(s).map(t=>{e=e.concat(parseFrames(t))}),this.animations[i]=animationFactory({spriteSheet:this,frames:e,frameRate:n,loop:a})}}}function spriteSheetFactory(t){return new SpriteSheet(t)}function setStoreItem(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))}function getStoreItem(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e}function TileEngine(t={}){let{width:e,height:i,tilewidth:s,tileheight:n,context:a=getContext(),tilesets:r,layers:o}=t,h=e*s,c=i*n,d=document.createElement("canvas"),l=d.getContext("2d");d.width=h,d.height=c;let u={},p={},g=[],m=Object.assign({context:a,mapwidth:h,mapheight:c,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),h-getCanvas().width),g.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),c-getCanvas().height),g.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),w(d)},renderLayer(t){let e=p[t],i=u[t];e||((e=document.createElement("canvas")).width=h,e.height=c,p[t]=e,m._r(i,e.getContext("2d"))),w(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let n=f(s),a=y(i),r=f(s+e.height),o=y(i+e.width),h=u[t];for(let t=n;t<=r;t++)for(let e=a;e<=o;e++)if(h.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||f(e.y),s=e.col||y(e.x);return u[t]?u[t].data[s+i*m.width]:-1},setTileAtLayer(t,e,i){let s=e.row||f(e.y),n=e.col||y(e.x);u[t]&&(this._d=!0,u[t].data[n+s*m.width]=i)},addObject(t){g.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=g.indexOf(t);-1!==e&&(g.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=m.tilesets.length-1;e>=0&&(s=m.tilesets[e],!(t/s.firstgid>=1));e--);let n=s.tilewidth||m.tilewidth,a=s.tileheight||m.tileheight,r=s.margin||0,o=s.image,h=t-s.firstgid,c=s.columns||o.width/(n+r)|0,d=i%m.width*n,l=(i/m.width|0)*a,u=h%c*(n+r),p=(h/c|0)*(a+r);e.drawImage(o,u,p,n,a,d,l,n,a)}),e.restore()},_p:_,layerCanvases:p},t);function f(t){return t/m.tileheight|0}function y(t){return t/m.tilewidth|0}function _(){m.layers&&m.layers.map(t=>{u[t.name]=t,!1!==t.visible&&m._r(t,l)})}function w(t){const{width:e,height:i}=getCanvas(),s=Math.min(t.width,e),n=Math.min(t.height,i);m.context.drawImage(t,m.sx,m.sy,s,n,0,0,s,n)}return m.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){if(!window.__k)throw Error('You must use "load" or "loadData" to resolve tileset.source');let t=window.__k.d[window.__k.u(e.source,i)];if(!t)throw Error(`You must load the tileset source "${e.source}" before loading the tileset`);Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){if(!window.__k)throw Error('You must use "load" or "loadImage" to resolve tileset.image');let t=window.__k.i[window.__k.u(e.image,i)];if(!t)throw Error(`You must load the image "${e.image}" before loading the tileset`);e.image=t}}),_(),m}spriteSheetFactory.prototype=SpriteSheet.prototype,spriteSheetFactory.class=SpriteSheet;let kontra={Animation:animationFactory,imageAssets:imageAssets,audioAssets:audioAssets,dataAssets:dataAssets,setImagePath:setImagePath,setAudioPath:setAudioPath,setDataPath:setDataPath,loadImage:loadImage,loadAudio:loadAudio,loadData:loadData,load:load,init:init,getCanvas:getCanvas,getContext:getContext,on:on,off:off,emit:emit,GameLoop:GameLoop,keyMap:keyMap,initKeys:initKeys,bindKeys:bindKeys,unbindKeys:unbindKeys,keyPressed:keyPressed,registerPlugin:registerPlugin,unregisterPlugin:unregisterPlugin,extendObject:extendObject,initPointer:initPointer,pointer:pointer,track:track,untrack:untrack,pointerOver:pointerOver,onPointerDown:onPointerDown,onPointerUp:onPointerUp,pointerPressed:pointerPressed,Pool:poolFactory,Quadtree:quadtreeFactory,Sprite:spriteFactory,SpriteSheet:spriteSheetFactory,setStoreItem:setStoreItem,getStoreItem:getStoreItem,TileEngine:TileEngine,Vector:vectorFactory};export{animationFactory as Animation,imageAssets,audioAssets,dataAssets,setImagePath,setAudioPath,setDataPath,loadImage,loadAudio,loadData,load,init,getCanvas,getContext,on,off,emit,GameLoop,keyMap,initKeys,bindKeys,unbindKeys,keyPressed,registerPlugin,unregisterPlugin,extendObject,initPointer,pointer,track,untrack,pointerOver,onPointerDown,onPointerUp,pointerPressed,poolFactory as Pool,quadtreeFactory as Quadtree,spriteFactory as Sprite,spriteSheetFactory as SpriteSheet,setStoreItem,getStoreItem,TileEngine,vectorFactory as Vector};export default kontra; \ No newline at end of file diff --git a/kontra.mjs b/kontra.mjs index 9bdaea53..6b920e7c 100644 --- a/kontra.mjs +++ b/kontra.mjs @@ -2409,19 +2409,6 @@ class Sprite { */ this.ttl = Infinity; - /** - * The horizontal scaling factor. A negative value flips the sprite across the vertical axis. - * @memberof Sprite - * @property {Number} scaleX - */ - - /** - * The vertical scaling factor. A negative value flips the sprite across the horizontal axis. - * @memberof Sprite - * @property {Number} scaleY - */ - this.scaleX = this.scaleY = 1; - /** * The x and y origin of the sprite. {x:0, y:0} is the top left corner of the sprite, {x:1, y:1} is the bottom right corner. * @memberof Sprite @@ -2490,6 +2477,9 @@ class Sprite { this[prop] = properties[prop]; } + // sx = flipX, sy = flipY + this._fx = this._fy = 1; + // image sprite if (image) { this.width = (width !== undefined) ? width : image.width; @@ -2622,6 +2612,8 @@ class Sprite { /** * The width of the sprite. If the sprite is a [rectangle sprite](api/sprite/#rectangle-sprite), it uses the passed in value. For an [image sprite](api/sprite/#image-sprite) it is the width of the image. And for an [animation sprite](api/sprite/#animation-sprite) it is the width of a single frame of the animation. + * + * Setting the value to a negative number will result in the sprite being flipped across the vertical axis while the width will remain a positive value. * @memberof Sprite * @property {Number} width */ @@ -2631,6 +2623,8 @@ class Sprite { /** * The height of the sprite. If the sprite is a [rectangle sprite](api/sprite/#rectangle-sprite), it uses the passed in value. For an [image sprite](api/sprite/#image-sprite) it is the height of the image. And for an [animation sprite](api/sprite/#animation-sprite) it is the height of a single frame of the animation. + * + * Setting the value to a negative number will result in the sprite being flipped across the horizontal axis while the height will remain a positive value. * @memberof Sprite * @property {Number} height */ @@ -2689,11 +2683,11 @@ class Sprite { } set width(value) { - this.scaleX = value < 0 ? -1 : 1; + this._fx = value < 0 ? -1 : 1; this._w = Math.abs(value); } set height(value) { - this.scaleY = value < 0 ? -1 : 1; + this._fy = value < 0 ? -1 : 1; this._h = Math.abs(value); } @@ -2936,12 +2930,22 @@ class Sprite { this.context.save(); this.context.translate(this.viewX, this.viewY); - this.context.scale(this.scaleX, this.scaleY); + // rotate around the anchor if (this.rotation) { this.context.rotate(this.rotation); } + // flip sprite around the center so the x/y position does not change + if (this._fx == -1 || this._fy == -1) { + let x = this.width / 2 + anchorWidth; + let y = this.height / 2 + anchorHeight; + + this.context.translate(x, y); + this.context.scale(this._fx, this._fy); + this.context.translate(-x, -y); + } + if (this.image) { this.context.drawImage( this.image, diff --git a/src/sprite.js b/src/sprite.js index b8eef867..d932e9fd 100644 --- a/src/sprite.js +++ b/src/sprite.js @@ -72,6 +72,9 @@ class Sprite { // defaults + // sx = flipX, sy = flipY + this._fx = this._fy = 1; + /** * The rotation of the sprite around the origin in radians. * @memberof Sprite @@ -86,19 +89,6 @@ class Sprite { */ this.ttl = Infinity; - /** - * The horizontal scaling factor. A negative value flips the sprite across the vertical axis. - * @memberof Sprite - * @property {Number} scaleX - */ - - /** - * The vertical scaling factor. A negative value flips the sprite across the horizontal axis. - * @memberof Sprite - * @property {Number} scaleY - */ - this.scaleX = this.scaleY = 1; - /** * The x and y origin of the sprite. {x:0, y:0} is the top left corner of the sprite, {x:1, y:1} is the bottom right corner. * @memberof Sprite @@ -299,6 +289,8 @@ class Sprite { /** * The width of the sprite. If the sprite is a [rectangle sprite](api/sprite/#rectangle-sprite), it uses the passed in value. For an [image sprite](api/sprite/#image-sprite) it is the width of the image. And for an [animation sprite](api/sprite/#animation-sprite) it is the width of a single frame of the animation. + * + * Setting the value to a negative number will result in the sprite being flipped across the vertical axis while the width will remain a positive value. * @memberof Sprite * @property {Number} width */ @@ -308,6 +300,8 @@ class Sprite { /** * The height of the sprite. If the sprite is a [rectangle sprite](api/sprite/#rectangle-sprite), it uses the passed in value. For an [image sprite](api/sprite/#image-sprite) it is the height of the image. And for an [animation sprite](api/sprite/#animation-sprite) it is the height of a single frame of the animation. + * + * Setting the value to a negative number will result in the sprite being flipped across the horizontal axis while the height will remain a positive value. * @memberof Sprite * @property {Number} height */ @@ -366,11 +360,11 @@ class Sprite { } set width(value) { - this.scaleX = value < 0 ? -1 : 1; + this._fx = value < 0 ? -1 : 1; this._w = Math.abs(value); } set height(value) { - this.scaleY = value < 0 ? -1 : 1; + this._fy = value < 0 ? -1 : 1; this._h = Math.abs(value); } @@ -613,12 +607,22 @@ class Sprite { this.context.save(); this.context.translate(this.viewX, this.viewY); - this.context.scale(this.scaleX, this.scaleY); + // rotate around the anchor if (this.rotation) { this.context.rotate(this.rotation); } + // flip sprite around the center so the x/y position does not change + if (this._fx == -1 || this._fy == -1) { + let x = this.width / 2 + anchorWidth; + let y = this.height / 2 + anchorHeight; + + this.context.translate(x, y); + this.context.scale(this._fx, this._fy); + this.context.translate(-x, -y); + } + if (this.image) { this.context.drawImage( this.image, diff --git a/test/unit/sprite.spec.js b/test/unit/sprite.spec.js index 649e0f90..785bf50b 100644 --- a/test/unit/sprite.spec.js +++ b/test/unit/sprite.spec.js @@ -30,8 +30,6 @@ describe('sprite', () => { expect(sprite.velocity.constructor.name).to.equal('Vector'); expect(sprite.acceleration.constructor.name).to.equal('Vector'); expect(sprite.ttl).to.equal(Infinity); - expect(sprite.scaleX).to.equal(1); - expect(sprite.scaleY).to.equal(1); }); it('should set basic properties of width, height, color, x, and y', () => { @@ -468,38 +466,6 @@ describe('sprite', () => { sprite.context.translate.restore(); }); - it('should scale the sprite to 1,1 by default', function() { - let sprite = Sprite({ - x: 10, - y: 20, - }); - - sinon.stub(sprite.context, 'scale').callsFake(noop); - - sprite.render(); - - expect(sprite.context.scale.calledWith(1,1)).to.be.ok; - - sprite.context.scale.restore(); - }); - - it('should scale the sprite by scaleX,scaleY', function() { - let sprite = Sprite({ - x: 10, - y: 20, - scaleX: 2, - scaleY: 4 - }); - - sinon.stub(sprite.context, 'scale').callsFake(noop); - - sprite.render(); - - expect(sprite.context.scale.calledWith(2,4)).to.be.ok; - - sprite.context.scale.restore(); - }); - }); @@ -761,11 +727,11 @@ describe('sprite', () => { // -------------------------------------------------- - // width/height + // scale // -------------------------------------------------- - describe('width/height', () => { + describe('scale', () => { - it('should set scaleX to 1 when width is >= 0', function() { + it('should set _fx to 1 when width is >= 0', () => { let sprite = Sprite({ x: 10, y: 20, @@ -773,14 +739,14 @@ describe('sprite', () => { sprite.width = 0; - expect(sprite.scaleX).to.equal(1); + expect(sprite._fx).to.equal(1); sprite.width = 100; - expect(sprite.scaleX).to.equal(1); + expect(sprite._fx).to.equal(1); }); - it('should set scaleX to -1 when width is < 0', function() { + it('should set _fx to -1 when width is < 0', () => { let sprite = Sprite({ x: 10, y: 20, @@ -788,10 +754,10 @@ describe('sprite', () => { sprite.width = -20; - expect(sprite.scaleX).to.equal(-1); + expect(sprite._fx).to.equal(-1); }); - it('should set scaleY to 1 when height is >= 0', function() { + it('should set _fy to 1 when height is >= 0', () => { let sprite = Sprite({ x: 10, y: 20, @@ -799,14 +765,14 @@ describe('sprite', () => { sprite.height = 0; - expect(sprite.scaleY).to.equal(1); + expect(sprite._fy).to.equal(1); sprite.height = 100; - expect(sprite.scaleY).to.equal(1); + expect(sprite._fy).to.equal(1); }); - it('should set scaleY to -1 when height is < 0', function() { + it('should set _fy to -1 when height is < 0', () => { let sprite = Sprite({ x: 10, y: 20, @@ -814,7 +780,85 @@ describe('sprite', () => { sprite.height = -20; - expect(sprite.scaleY).to.equal(-1); + expect(sprite._fy).to.equal(-1); + }); + + it('should not scale the sprite by default', () => { + let sprite = Sprite({ + x: 10, + y: 20, + }); + + sinon.spy(sprite.context, 'scale'); + + sprite.render(); + + expect(sprite.context.scale.notCalled).to.be.true; + + sprite.context.scale.restore(); + }); + + it('should scale the sprite by _fx', () => { + let sprite = Sprite({ + x: 10, + y: 20, + width: -10 + }); + + sinon.stub(sprite.context, 'scale').callsFake(noop); + + sprite.render(); + + expect(sprite.context.scale.calledWith(-1,1)).to.be.ok; + + sprite.context.scale.restore(); + }); + + it('should scale the sprite by _fy', () => { + let sprite = Sprite({ + x: 10, + y: 20, + height: -10 + }); + + sinon.stub(sprite.context, 'scale').callsFake(noop); + + sprite.render(); + + expect(sprite.context.scale.calledWith(1,-1)).to.be.ok; + + sprite.context.scale.restore(); + }); + + it('should scale the sprite at its center', () => { + let sprite = Sprite({ + x: 10, + y: 20, + width: -10, + height: 20 + }); + + sinon.stub(sprite.context, 'translate').callsFake(noop); + + sprite.render(); + + expect(sprite.context.translate.secondCall.calledWith(sprite.width / 2, sprite.height / 2)).to.be.ok; + + sprite.context.translate.resetHistory(); + sprite.anchor = {x: 0.5, y: 0.5}; + + sprite.render(); + + expect(sprite.context.translate.secondCall.calledWith(0, 0)).to.be.ok; + + sprite.context.translate.resetHistory(); + sprite.anchor = {x: 1, y: 1}; + + sprite.render(); + + expect(sprite.context.translate.secondCall.calledWith(-sprite.width / 2, -sprite.height / 2)).to.be.ok; + + sprite.context.translate.restore(); }); }); From 825807cc0e8100d9ea027051d1faf0c2bf075b01 Mon Sep 17 00:00:00 2001 From: straker Date: Wed, 4 Sep 2019 23:39:57 -0600 Subject: [PATCH 3/6] build --- docs/assets/js/kontra.js | 2 +- kontra.js | 6 +++--- kontra.min.js | 2 +- kontra.min.mjs | 2 +- kontra.mjs | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/assets/js/kontra.js b/docs/assets/js/kontra.js index 1b78e67a..2c47f113 100644 --- a/docs/assets/js/kontra.js +++ b/docs/assets/js/kontra.js @@ -1 +1 @@ -var kontra=function(){"use strict";let t,e,i={};function s(t,e){i[t]=i[t]||[],i[t].push(e)}function h(t,...e){i[t]&&i[t].map(t=>t(...e))}function n(){return t}function r(){return e}class o{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:h,height:n,margin:r=0}=t.frame;this.width=h,this.height=n,this.margin=r,this._f=0,this._a=0}clone(){return a(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:h=r()}={}){let n=this.frames[this._f]/this.spriteSheet._f|0,o=this.frames[this._f]%this.spriteSheet._f|0;h.drawImage(this.spriteSheet.image,o*this.width+(2*o+1)*this.margin,n*this.height+(2*n+1)*this.margin,this.width,this.height,t,e,i,s)}}function a(t){return new o(t)}a.prototype=o.prototype,a.class=o;let c=/(jpeg|jpg|gif|png)$/,l=/(wav|mp3|ogg|aac)$/,d=/^\//,u=/\/$/,f=new WeakMap,g="",p="",m="";function _(t,e){return new URL(t,e).href}function w(t,e){return[t.replace(u,""),t?e.replace(d,""):e].filter(t=>t).join("/")}function y(t){return t.split(".").pop()}function x(t){let e=t.replace("."+y(t),"");return 2==e.split("/").length?e.replace(d,""):e}let b={},v={},j={};function A(){window.__k||(window.__k={dm:f,u:_,d:j,i:b})}function S(t){return A(),new Promise((e,i)=>{let s,n,r;if(s=w(g,t),b[s])return e(b[s]);(n=new Image).onload=function(){r=_(s,window.location.href),b[x(t)]=b[s]=b[r]=this,h("assetLoaded",this,t),e(this)},n.onerror=function(){i(s)},n.src=s})}function O(t){return new Promise((e,i)=>{let s,n,r,o;return s=new Audio,n=function(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}(s),(t=[].concat(t).reduce((t,e)=>t||(n[y(e)]?e:null),0))?(r=w(p,t),v[r]?e(v[r]):(s.addEventListener("canplay",function(){o=_(r,window.location.href),v[x(t)]=v[r]=v[o]=this,h("assetLoaded",this,t),e(this)}),s.onerror=function(){i(r)},s.src=r,void s.load())):i(t)})}function P(t){let e,i;return A(),e=w(m,t),j[e]?Promise.resolve(j[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=_(e,window.location.href),"object"==typeof s&&f.set(s,i),j[x(t)]=j[e]=j[i]=s,h("assetLoaded",s,t),s))}const E=()=>{};function M(){let t=n();r().clearRect(0,0,t.width,t.height)}let k={},L={},I={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function z(t){let e=I[t.which];L[e]=!0,k[e]&&k[e](t)}function D(t){L[I[t.which]]=!1}function R(){L={}}function W(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function C(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}let T=[],N=[],X={},Y=[],U={},q={0:"left",1:"middle",2:"right"},F={x:0,y:0,radius:5};function K(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=F.x-Math.max(e,Math.min(F.x,e+t.width)),h=F.y-Math.max(i,Math.min(F.y,i+t.height));return s*s+h*h=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(F):K(t))return t}function B(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!0,Q(t,"onDown")}function H(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!1,Q(t,"onUp")}function J(t){Q(t,"onOver")}function G(){U={}}function Q(t,e){let i,s,h=n();if(!h)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let r=h.height/h.offsetHeight,o=h.getBoundingClientRect(),a=(i-o.left)*r,c=(s-o.top)*r;F.x=a,F.y=c,t.preventDefault();let l=$();l&&l[e]&&l[e](t),X[e]&&X[e](t,l)}class V{constructor({create:t,maxSize:e=1024}={}){this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function Z(t){return new V(t)}function tt(t,e){let i=[],s=e.x+e.width/2,h=e.y+e.height/2,n=t.y=e.y,r=t.y+t.height>=h&&t.y=e.x&&(n&&i.push(0),r&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1}}function it(t){return new et(t)}it.prototype=et.prototype,it.class=et;class st{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return ht(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function ht(t,e,i={}){let s=new st(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}ht.prototype=st.prototype,ht.class=st;class nt{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:h,ddx:n,ddy:o,width:a,height:c,image:l}=t;this.position=ht(e,i),this.velocity=ht(s,h),this.acceleration=ht(n,o),this.width=this.height=this.rotation=0,this.ttl=1/0,this.anchor={x:0,y:0},this.context=r();for(let e in t)this[e]=t[e];l&&(this.width=void 0!==a?a:l.width,this.height=void 0!==c?c:l.height),this.sx=0,this.sy=0,this._fx=this._fy=1}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}get width(){return this._w}get height(){return this._h}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}set width(t){this._fx=t<0?-1:1,this._w=Math.abs(t)}set height(t){this._fy=t<0?-1:1,this._h=Math.abs(t)}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,h=t.y;return t.anchor&&(s-=t.width*t.anchor.x,h-=t.height*t.anchor.y),es&&ih}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;if(this.context.save(),this.context.translate(this.viewX,this.viewY),this.rotation&&this.context.rotate(this.rotation),-1==this._fx||-1==this._fy){let i=this.width/2+t,s=this.height/2+e;this.context.translate(i,s),this.context.scale(this._fx,this._fy),this.context.translate(-i,-s)}this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function rt(t){return new nt(t)}function ot(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],h=+i[1],n=s;if(s=h;n--)e.push(n);return e}rt.prototype=nt.prototype,rt.class=nt;class at{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:h}={}){this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(h)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:h,loop:n}=t[i];e=[],[].concat(s).map(t=>{e=e.concat(ot(t))}),this.animations[i]=a({spriteSheet:this,frames:e,frameRate:h,loop:n})}}}function ct(t){return new at(t)}return ct.prototype=at.prototype,ct.class=at,{Animation:a,imageAssets:b,audioAssets:v,dataAssets:j,setImagePath:function(t){g=t},setAudioPath:function(t){p=t},setDataPath:function(t){m=t},loadImage:S,loadAudio:O,loadData:P,load:function(...t){return A(),Promise.all(t.map(t=>{let e=y([].concat(t)[0]);return e.match(c)?S(t):e.match(l)?O(t):P(t)}))},init:function(i){return t=document.getElementById(i)||i||document.querySelector("canvas"),(e=t.getContext("2d")).imageSmoothingEnabled=!1,h("init"),{canvas:t,context:e}},getCanvas:n,getContext:r,on:s,off:function(t,e){let s;!i[t]||(s=i[t].indexOf(e))<0||i[t].splice(s,1)},emit:h,GameLoop:function({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){let n,r,o,a,c,l=0,d=1e3/t,u=1/t,f=e?M:E;function g(){if(r=requestAnimationFrame(g),o=performance.now(),a=o-n,n=o,!(a>1e3)){for(h("tick"),l+=a;l>=d;)c.update(u),l-=d;f(),c.render()}}return c={update:i,render:s,isStopped:!0,start(){n=performance.now(),this.isStopped=!1,requestAnimationFrame(g)},stop(){this.isStopped=!0,cancelAnimationFrame(r)}}},keyMap:I,initKeys:function(){let t;for(t=0;t<26;t++)I[65+t]=(10+t).toString(36);for(t=0;t<10;t++)I[48+t]=""+t;window.addEventListener("keydown",z),window.addEventListener("keyup",D),window.addEventListener("blur",R)},bindKeys:function(t,e){[].concat(t).map(t=>k[t]=e)},unbindKeys:function(t){[].concat(t).map(t=>k[t]=0)},keyPressed:function(t){return!!L[t]},registerPlugin:function(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let h=i(t,e,...s);return h||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),h=i["_o"+s].call(this,...e);return this._aInc(this,s,h,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))},unregisterPlugin:function(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);t.startsWith("before")?C(i._inc[s].before,e[t]):t.startsWith("after")&&C(i._inc[s].after,e[t])})},extendObject:function(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})},initPointer:function(){let t=n();t.addEventListener("mousedown",B),t.addEventListener("touchstart",B),t.addEventListener("mouseup",H),t.addEventListener("touchend",H),t.addEventListener("blur",G),t.addEventListener("mousemove",J),t.addEventListener("touchmove",J),s("tick",()=>{N.length=0,T.map(t=>{N.push(t)}),T.length=0})},pointer:F,track:function(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){T.push(this),this._r()},Y.push(t))})},untrack:function(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=Y.indexOf(t);-1!==e&&Y.splice(e,1)})},pointerOver:function(t){return!!Y.includes(t)&&$()===t},onPointerDown:function(t){X.onDown=t},onPointerUp:function(t){X.onUp=t},pointerPressed:function(t){return!!U[t]},Pool:Z,Quadtree:it,Sprite:rt,SpriteSheet:ct,setStoreItem:function(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))},getStoreItem:function(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e},TileEngine:function(t={}){let{width:e,height:i,tilewidth:s,tileheight:h,context:o=r(),tilesets:a,layers:c}=t,l=e*s,d=i*h,u=document.createElement("canvas"),f=u.getContext("2d");u.width=l,u.height=d;let g={},p={},m=[],_=Object.assign({context:o,mapwidth:l,mapheight:d,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),l-n().width),m.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),d-n().height),m.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),b(u)},renderLayer(t){let e=p[t],i=g[t];e||((e=document.createElement("canvas")).width=l,e.height=d,p[t]=e,_._r(i,e.getContext("2d"))),b(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let h=w(s),n=y(i),r=w(s+e.height),o=y(i+e.width),a=g[t];for(let t=h;t<=r;t++)for(let e=n;e<=o;e++)if(a.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||w(e.y),s=e.col||y(e.x);return g[t]?g[t].data[s+i*_.width]:-1},setTileAtLayer(t,e,i){let s=e.row||w(e.y),h=e.col||y(e.x);g[t]&&(this._d=!0,g[t].data[h+s*_.width]=i)},addObject(t){m.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=m.indexOf(t);-1!==e&&(m.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=_.tilesets.length-1;e>=0&&(s=_.tilesets[e],!(t/s.firstgid>=1));e--);let h=s.tilewidth||_.tilewidth,n=s.tileheight||_.tileheight,r=s.margin||0,o=s.image,a=t-s.firstgid,c=s.columns||o.width/(h+r)|0,l=i%_.width*h,d=(i/_.width|0)*n,u=a%c*(h+r),f=(a/c|0)*(n+r);e.drawImage(o,u,f,h,n,l,d,h,n)}),e.restore()},_p:x},t);function w(t){return t/_.tileheight|0}function y(t){return t/_.tilewidth|0}function x(){_.layers&&_.layers.map(t=>{g[t.name]=t,!1!==t.visible&&_._r(t,f)})}function b(t){const{width:e,height:i}=n(),s=Math.min(t.width,e),h=Math.min(t.height,i);_.context.drawImage(t,_.sx,_.sy,s,h,0,0,s,h)}return _.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){let t=window.__k.d[window.__k.u(e.source,i)];Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){let t=window.__k.i[window.__k.u(e.image,i)];e.image=t}}),x(),_},Vector:ht}}(); \ No newline at end of file +var kontra=function(){"use strict";let t,e,i={};function s(t,e){i[t]=i[t]||[],i[t].push(e)}function h(t,...e){i[t]&&i[t].map(t=>t(...e))}function n(){return t}function r(){return e}class o{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:h,height:n,margin:r=0}=t.frame;this.width=h,this.height=n,this.margin=r,this._f=0,this._a=0}clone(){return a(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:h=r()}={}){let n=this.frames[this._f]/this.spriteSheet._f|0,o=this.frames[this._f]%this.spriteSheet._f|0;h.drawImage(this.spriteSheet.image,o*this.width+(2*o+1)*this.margin,n*this.height+(2*n+1)*this.margin,this.width,this.height,t,e,i,s)}}function a(t){return new o(t)}a.prototype=o.prototype,a.class=o;let c=/(jpeg|jpg|gif|png)$/,l=/(wav|mp3|ogg|aac)$/,d=/^\//,u=/\/$/,f=new WeakMap,g="",p="",m="";function _(t,e){return new URL(t,e).href}function w(t,e){return[t.replace(u,""),t?e.replace(d,""):e].filter(t=>t).join("/")}function y(t){return t.split(".").pop()}function x(t){let e=t.replace("."+y(t),"");return 2==e.split("/").length?e.replace(d,""):e}let b={},v={},j={};function A(){window.__k||(window.__k={dm:f,u:_,d:j,i:b})}function S(t){return A(),new Promise((e,i)=>{let s,n,r;if(s=w(g,t),b[s])return e(b[s]);(n=new Image).onload=function(){r=_(s,window.location.href),b[x(t)]=b[s]=b[r]=this,h("assetLoaded",this,t),e(this)},n.onerror=function(){i(s)},n.src=s})}function O(t){return new Promise((e,i)=>{let s,n,r,o;return s=new Audio,n=function(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}(s),(t=[].concat(t).reduce((t,e)=>t||(n[y(e)]?e:null),0))?(r=w(p,t),v[r]?e(v[r]):(s.addEventListener("canplay",function(){o=_(r,window.location.href),v[x(t)]=v[r]=v[o]=this,h("assetLoaded",this,t),e(this)}),s.onerror=function(){i(r)},s.src=r,void s.load())):i(t)})}function P(t){let e,i;return A(),e=w(m,t),j[e]?Promise.resolve(j[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=_(e,window.location.href),"object"==typeof s&&f.set(s,i),j[x(t)]=j[e]=j[i]=s,h("assetLoaded",s,t),s))}const E=()=>{};function M(){let t=n();r().clearRect(0,0,t.width,t.height)}let k={},L={},I={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function z(t){let e=I[t.which];L[e]=!0,k[e]&&k[e](t)}function D(t){L[I[t.which]]=!1}function R(){L={}}function W(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function C(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}let T=[],N=[],X={},Y=[],U={},q={0:"left",1:"middle",2:"right"},F={x:0,y:0,radius:5};function K(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=F.x-Math.max(e,Math.min(F.x,e+t.width)),h=F.y-Math.max(i,Math.min(F.y,i+t.height));return s*s+h*h=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(F):K(t))return t}function B(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!0,Q(t,"onDown")}function H(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!1,Q(t,"onUp")}function J(t){Q(t,"onOver")}function G(){U={}}function Q(t,e){let i,s,h=n();if(!h)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let r=h.height/h.offsetHeight,o=h.getBoundingClientRect(),a=(i-o.left)*r,c=(s-o.top)*r;F.x=a,F.y=c,t.preventDefault();let l=$();l&&l[e]&&l[e](t),X[e]&&X[e](t,l)}class V{constructor({create:t,maxSize:e=1024}={}){this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function Z(t){return new V(t)}function tt(t,e){let i=[],s=e.x+e.width/2,h=e.y+e.height/2,n=t.y=e.y,r=t.y+t.height>=h&&t.y=e.x&&(n&&i.push(0),r&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1}}function it(t){return new et(t)}it.prototype=et.prototype,it.class=et;class st{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return ht(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function ht(t,e,i={}){let s=new st(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}ht.prototype=st.prototype,ht.class=st;class nt{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:h,ddx:n,ddy:o,width:a,height:c,image:l}=t;this.position=ht(e,i),this.velocity=ht(s,h),this.acceleration=ht(n,o),this._fx=this._fy=1,this.width=this.height=this.rotation=0,this.ttl=1/0,this.anchor={x:0,y:0},this.context=r();for(let e in t)this[e]=t[e];l&&(this.width=void 0!==a?a:l.width,this.height=void 0!==c?c:l.height),this.sx=0,this.sy=0}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}get width(){return this._w}get height(){return this._h}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}set width(t){this._fx=t<0?-1:1,this._w=Math.abs(t)}set height(t){this._fy=t<0?-1:1,this._h=Math.abs(t)}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,h=t.y;return t.anchor&&(s-=t.width*t.anchor.x,h-=t.height*t.anchor.y),es&&ih}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;if(this.context.save(),this.context.translate(this.viewX,this.viewY),this.rotation&&this.context.rotate(this.rotation),-1==this._fx||-1==this._fy){let i=this.width/2+t,s=this.height/2+e;this.context.translate(i,s),this.context.scale(this._fx,this._fy),this.context.translate(-i,-s)}this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function rt(t){return new nt(t)}function ot(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],h=+i[1],n=s;if(s=h;n--)e.push(n);return e}rt.prototype=nt.prototype,rt.class=nt;class at{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:h}={}){this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(h)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:h,loop:n}=t[i];e=[],[].concat(s).map(t=>{e=e.concat(ot(t))}),this.animations[i]=a({spriteSheet:this,frames:e,frameRate:h,loop:n})}}}function ct(t){return new at(t)}return ct.prototype=at.prototype,ct.class=at,{Animation:a,imageAssets:b,audioAssets:v,dataAssets:j,setImagePath:function(t){g=t},setAudioPath:function(t){p=t},setDataPath:function(t){m=t},loadImage:S,loadAudio:O,loadData:P,load:function(...t){return A(),Promise.all(t.map(t=>{let e=y([].concat(t)[0]);return e.match(c)?S(t):e.match(l)?O(t):P(t)}))},init:function(i){return t=document.getElementById(i)||i||document.querySelector("canvas"),(e=t.getContext("2d")).imageSmoothingEnabled=!1,h("init"),{canvas:t,context:e}},getCanvas:n,getContext:r,on:s,off:function(t,e){let s;!i[t]||(s=i[t].indexOf(e))<0||i[t].splice(s,1)},emit:h,GameLoop:function({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){let n,r,o,a,c,l=0,d=1e3/t,u=1/t,f=e?M:E;function g(){if(r=requestAnimationFrame(g),o=performance.now(),a=o-n,n=o,!(a>1e3)){for(h("tick"),l+=a;l>=d;)c.update(u),l-=d;f(),c.render()}}return c={update:i,render:s,isStopped:!0,start(){n=performance.now(),this.isStopped=!1,requestAnimationFrame(g)},stop(){this.isStopped=!0,cancelAnimationFrame(r)}}},keyMap:I,initKeys:function(){let t;for(t=0;t<26;t++)I[65+t]=(10+t).toString(36);for(t=0;t<10;t++)I[48+t]=""+t;window.addEventListener("keydown",z),window.addEventListener("keyup",D),window.addEventListener("blur",R)},bindKeys:function(t,e){[].concat(t).map(t=>k[t]=e)},unbindKeys:function(t){[].concat(t).map(t=>k[t]=0)},keyPressed:function(t){return!!L[t]},registerPlugin:function(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let h=i(t,e,...s);return h||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),h=i["_o"+s].call(this,...e);return this._aInc(this,s,h,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))},unregisterPlugin:function(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);t.startsWith("before")?C(i._inc[s].before,e[t]):t.startsWith("after")&&C(i._inc[s].after,e[t])})},extendObject:function(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})},initPointer:function(){let t=n();t.addEventListener("mousedown",B),t.addEventListener("touchstart",B),t.addEventListener("mouseup",H),t.addEventListener("touchend",H),t.addEventListener("blur",G),t.addEventListener("mousemove",J),t.addEventListener("touchmove",J),s("tick",()=>{N.length=0,T.map(t=>{N.push(t)}),T.length=0})},pointer:F,track:function(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){T.push(this),this._r()},Y.push(t))})},untrack:function(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=Y.indexOf(t);-1!==e&&Y.splice(e,1)})},pointerOver:function(t){return!!Y.includes(t)&&$()===t},onPointerDown:function(t){X.onDown=t},onPointerUp:function(t){X.onUp=t},pointerPressed:function(t){return!!U[t]},Pool:Z,Quadtree:it,Sprite:rt,SpriteSheet:ct,setStoreItem:function(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))},getStoreItem:function(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e},TileEngine:function(t={}){let{width:e,height:i,tilewidth:s,tileheight:h,context:o=r(),tilesets:a,layers:c}=t,l=e*s,d=i*h,u=document.createElement("canvas"),f=u.getContext("2d");u.width=l,u.height=d;let g={},p={},m=[],_=Object.assign({context:o,mapwidth:l,mapheight:d,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),l-n().width),m.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),d-n().height),m.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),b(u)},renderLayer(t){let e=p[t],i=g[t];e||((e=document.createElement("canvas")).width=l,e.height=d,p[t]=e,_._r(i,e.getContext("2d"))),b(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let h=w(s),n=y(i),r=w(s+e.height),o=y(i+e.width),a=g[t];for(let t=h;t<=r;t++)for(let e=n;e<=o;e++)if(a.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||w(e.y),s=e.col||y(e.x);return g[t]?g[t].data[s+i*_.width]:-1},setTileAtLayer(t,e,i){let s=e.row||w(e.y),h=e.col||y(e.x);g[t]&&(this._d=!0,g[t].data[h+s*_.width]=i)},addObject(t){m.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=m.indexOf(t);-1!==e&&(m.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=_.tilesets.length-1;e>=0&&(s=_.tilesets[e],!(t/s.firstgid>=1));e--);let h=s.tilewidth||_.tilewidth,n=s.tileheight||_.tileheight,r=s.margin||0,o=s.image,a=t-s.firstgid,c=s.columns||o.width/(h+r)|0,l=i%_.width*h,d=(i/_.width|0)*n,u=a%c*(h+r),f=(a/c|0)*(n+r);e.drawImage(o,u,f,h,n,l,d,h,n)}),e.restore()},_p:x},t);function w(t){return t/_.tileheight|0}function y(t){return t/_.tilewidth|0}function x(){_.layers&&_.layers.map(t=>{g[t.name]=t,!1!==t.visible&&_._r(t,f)})}function b(t){const{width:e,height:i}=n(),s=Math.min(t.width,e),h=Math.min(t.height,i);_.context.drawImage(t,_.sx,_.sy,s,h,0,0,s,h)}return _.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){let t=window.__k.d[window.__k.u(e.source,i)];Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){let t=window.__k.i[window.__k.u(e.image,i)];e.image=t}}),x(),_},Vector:ht}}(); \ No newline at end of file diff --git a/kontra.js b/kontra.js index 05a66625..488e8286 100644 --- a/kontra.js +++ b/kontra.js @@ -2398,6 +2398,9 @@ class Sprite { // defaults + // sx = flipX, sy = flipY + this._fx = this._fy = 1; + /** * The rotation of the sprite around the origin in radians. * @memberof Sprite @@ -2480,9 +2483,6 @@ class Sprite { this[prop] = properties[prop]; } - // sx = flipX, sy = flipY - this._fx = this._fy = 1; - // image sprite if (image) { this.width = (width !== undefined) ? width : image.width; diff --git a/kontra.min.js b/kontra.min.js index 1b78e67a..2c47f113 100644 --- a/kontra.min.js +++ b/kontra.min.js @@ -1 +1 @@ -var kontra=function(){"use strict";let t,e,i={};function s(t,e){i[t]=i[t]||[],i[t].push(e)}function h(t,...e){i[t]&&i[t].map(t=>t(...e))}function n(){return t}function r(){return e}class o{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:h,height:n,margin:r=0}=t.frame;this.width=h,this.height=n,this.margin=r,this._f=0,this._a=0}clone(){return a(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:h=r()}={}){let n=this.frames[this._f]/this.spriteSheet._f|0,o=this.frames[this._f]%this.spriteSheet._f|0;h.drawImage(this.spriteSheet.image,o*this.width+(2*o+1)*this.margin,n*this.height+(2*n+1)*this.margin,this.width,this.height,t,e,i,s)}}function a(t){return new o(t)}a.prototype=o.prototype,a.class=o;let c=/(jpeg|jpg|gif|png)$/,l=/(wav|mp3|ogg|aac)$/,d=/^\//,u=/\/$/,f=new WeakMap,g="",p="",m="";function _(t,e){return new URL(t,e).href}function w(t,e){return[t.replace(u,""),t?e.replace(d,""):e].filter(t=>t).join("/")}function y(t){return t.split(".").pop()}function x(t){let e=t.replace("."+y(t),"");return 2==e.split("/").length?e.replace(d,""):e}let b={},v={},j={};function A(){window.__k||(window.__k={dm:f,u:_,d:j,i:b})}function S(t){return A(),new Promise((e,i)=>{let s,n,r;if(s=w(g,t),b[s])return e(b[s]);(n=new Image).onload=function(){r=_(s,window.location.href),b[x(t)]=b[s]=b[r]=this,h("assetLoaded",this,t),e(this)},n.onerror=function(){i(s)},n.src=s})}function O(t){return new Promise((e,i)=>{let s,n,r,o;return s=new Audio,n=function(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}(s),(t=[].concat(t).reduce((t,e)=>t||(n[y(e)]?e:null),0))?(r=w(p,t),v[r]?e(v[r]):(s.addEventListener("canplay",function(){o=_(r,window.location.href),v[x(t)]=v[r]=v[o]=this,h("assetLoaded",this,t),e(this)}),s.onerror=function(){i(r)},s.src=r,void s.load())):i(t)})}function P(t){let e,i;return A(),e=w(m,t),j[e]?Promise.resolve(j[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=_(e,window.location.href),"object"==typeof s&&f.set(s,i),j[x(t)]=j[e]=j[i]=s,h("assetLoaded",s,t),s))}const E=()=>{};function M(){let t=n();r().clearRect(0,0,t.width,t.height)}let k={},L={},I={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function z(t){let e=I[t.which];L[e]=!0,k[e]&&k[e](t)}function D(t){L[I[t.which]]=!1}function R(){L={}}function W(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function C(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}let T=[],N=[],X={},Y=[],U={},q={0:"left",1:"middle",2:"right"},F={x:0,y:0,radius:5};function K(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=F.x-Math.max(e,Math.min(F.x,e+t.width)),h=F.y-Math.max(i,Math.min(F.y,i+t.height));return s*s+h*h=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(F):K(t))return t}function B(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!0,Q(t,"onDown")}function H(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!1,Q(t,"onUp")}function J(t){Q(t,"onOver")}function G(){U={}}function Q(t,e){let i,s,h=n();if(!h)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let r=h.height/h.offsetHeight,o=h.getBoundingClientRect(),a=(i-o.left)*r,c=(s-o.top)*r;F.x=a,F.y=c,t.preventDefault();let l=$();l&&l[e]&&l[e](t),X[e]&&X[e](t,l)}class V{constructor({create:t,maxSize:e=1024}={}){this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function Z(t){return new V(t)}function tt(t,e){let i=[],s=e.x+e.width/2,h=e.y+e.height/2,n=t.y=e.y,r=t.y+t.height>=h&&t.y=e.x&&(n&&i.push(0),r&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1}}function it(t){return new et(t)}it.prototype=et.prototype,it.class=et;class st{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return ht(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function ht(t,e,i={}){let s=new st(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}ht.prototype=st.prototype,ht.class=st;class nt{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:h,ddx:n,ddy:o,width:a,height:c,image:l}=t;this.position=ht(e,i),this.velocity=ht(s,h),this.acceleration=ht(n,o),this.width=this.height=this.rotation=0,this.ttl=1/0,this.anchor={x:0,y:0},this.context=r();for(let e in t)this[e]=t[e];l&&(this.width=void 0!==a?a:l.width,this.height=void 0!==c?c:l.height),this.sx=0,this.sy=0,this._fx=this._fy=1}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}get width(){return this._w}get height(){return this._h}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}set width(t){this._fx=t<0?-1:1,this._w=Math.abs(t)}set height(t){this._fy=t<0?-1:1,this._h=Math.abs(t)}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,h=t.y;return t.anchor&&(s-=t.width*t.anchor.x,h-=t.height*t.anchor.y),es&&ih}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;if(this.context.save(),this.context.translate(this.viewX,this.viewY),this.rotation&&this.context.rotate(this.rotation),-1==this._fx||-1==this._fy){let i=this.width/2+t,s=this.height/2+e;this.context.translate(i,s),this.context.scale(this._fx,this._fy),this.context.translate(-i,-s)}this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function rt(t){return new nt(t)}function ot(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],h=+i[1],n=s;if(s=h;n--)e.push(n);return e}rt.prototype=nt.prototype,rt.class=nt;class at{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:h}={}){this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(h)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:h,loop:n}=t[i];e=[],[].concat(s).map(t=>{e=e.concat(ot(t))}),this.animations[i]=a({spriteSheet:this,frames:e,frameRate:h,loop:n})}}}function ct(t){return new at(t)}return ct.prototype=at.prototype,ct.class=at,{Animation:a,imageAssets:b,audioAssets:v,dataAssets:j,setImagePath:function(t){g=t},setAudioPath:function(t){p=t},setDataPath:function(t){m=t},loadImage:S,loadAudio:O,loadData:P,load:function(...t){return A(),Promise.all(t.map(t=>{let e=y([].concat(t)[0]);return e.match(c)?S(t):e.match(l)?O(t):P(t)}))},init:function(i){return t=document.getElementById(i)||i||document.querySelector("canvas"),(e=t.getContext("2d")).imageSmoothingEnabled=!1,h("init"),{canvas:t,context:e}},getCanvas:n,getContext:r,on:s,off:function(t,e){let s;!i[t]||(s=i[t].indexOf(e))<0||i[t].splice(s,1)},emit:h,GameLoop:function({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){let n,r,o,a,c,l=0,d=1e3/t,u=1/t,f=e?M:E;function g(){if(r=requestAnimationFrame(g),o=performance.now(),a=o-n,n=o,!(a>1e3)){for(h("tick"),l+=a;l>=d;)c.update(u),l-=d;f(),c.render()}}return c={update:i,render:s,isStopped:!0,start(){n=performance.now(),this.isStopped=!1,requestAnimationFrame(g)},stop(){this.isStopped=!0,cancelAnimationFrame(r)}}},keyMap:I,initKeys:function(){let t;for(t=0;t<26;t++)I[65+t]=(10+t).toString(36);for(t=0;t<10;t++)I[48+t]=""+t;window.addEventListener("keydown",z),window.addEventListener("keyup",D),window.addEventListener("blur",R)},bindKeys:function(t,e){[].concat(t).map(t=>k[t]=e)},unbindKeys:function(t){[].concat(t).map(t=>k[t]=0)},keyPressed:function(t){return!!L[t]},registerPlugin:function(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let h=i(t,e,...s);return h||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),h=i["_o"+s].call(this,...e);return this._aInc(this,s,h,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))},unregisterPlugin:function(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);t.startsWith("before")?C(i._inc[s].before,e[t]):t.startsWith("after")&&C(i._inc[s].after,e[t])})},extendObject:function(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})},initPointer:function(){let t=n();t.addEventListener("mousedown",B),t.addEventListener("touchstart",B),t.addEventListener("mouseup",H),t.addEventListener("touchend",H),t.addEventListener("blur",G),t.addEventListener("mousemove",J),t.addEventListener("touchmove",J),s("tick",()=>{N.length=0,T.map(t=>{N.push(t)}),T.length=0})},pointer:F,track:function(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){T.push(this),this._r()},Y.push(t))})},untrack:function(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=Y.indexOf(t);-1!==e&&Y.splice(e,1)})},pointerOver:function(t){return!!Y.includes(t)&&$()===t},onPointerDown:function(t){X.onDown=t},onPointerUp:function(t){X.onUp=t},pointerPressed:function(t){return!!U[t]},Pool:Z,Quadtree:it,Sprite:rt,SpriteSheet:ct,setStoreItem:function(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))},getStoreItem:function(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e},TileEngine:function(t={}){let{width:e,height:i,tilewidth:s,tileheight:h,context:o=r(),tilesets:a,layers:c}=t,l=e*s,d=i*h,u=document.createElement("canvas"),f=u.getContext("2d");u.width=l,u.height=d;let g={},p={},m=[],_=Object.assign({context:o,mapwidth:l,mapheight:d,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),l-n().width),m.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),d-n().height),m.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),b(u)},renderLayer(t){let e=p[t],i=g[t];e||((e=document.createElement("canvas")).width=l,e.height=d,p[t]=e,_._r(i,e.getContext("2d"))),b(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let h=w(s),n=y(i),r=w(s+e.height),o=y(i+e.width),a=g[t];for(let t=h;t<=r;t++)for(let e=n;e<=o;e++)if(a.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||w(e.y),s=e.col||y(e.x);return g[t]?g[t].data[s+i*_.width]:-1},setTileAtLayer(t,e,i){let s=e.row||w(e.y),h=e.col||y(e.x);g[t]&&(this._d=!0,g[t].data[h+s*_.width]=i)},addObject(t){m.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=m.indexOf(t);-1!==e&&(m.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=_.tilesets.length-1;e>=0&&(s=_.tilesets[e],!(t/s.firstgid>=1));e--);let h=s.tilewidth||_.tilewidth,n=s.tileheight||_.tileheight,r=s.margin||0,o=s.image,a=t-s.firstgid,c=s.columns||o.width/(h+r)|0,l=i%_.width*h,d=(i/_.width|0)*n,u=a%c*(h+r),f=(a/c|0)*(n+r);e.drawImage(o,u,f,h,n,l,d,h,n)}),e.restore()},_p:x},t);function w(t){return t/_.tileheight|0}function y(t){return t/_.tilewidth|0}function x(){_.layers&&_.layers.map(t=>{g[t.name]=t,!1!==t.visible&&_._r(t,f)})}function b(t){const{width:e,height:i}=n(),s=Math.min(t.width,e),h=Math.min(t.height,i);_.context.drawImage(t,_.sx,_.sy,s,h,0,0,s,h)}return _.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){let t=window.__k.d[window.__k.u(e.source,i)];Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){let t=window.__k.i[window.__k.u(e.image,i)];e.image=t}}),x(),_},Vector:ht}}(); \ No newline at end of file +var kontra=function(){"use strict";let t,e,i={};function s(t,e){i[t]=i[t]||[],i[t].push(e)}function h(t,...e){i[t]&&i[t].map(t=>t(...e))}function n(){return t}function r(){return e}class o{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:h,height:n,margin:r=0}=t.frame;this.width=h,this.height=n,this.margin=r,this._f=0,this._a=0}clone(){return a(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:h=r()}={}){let n=this.frames[this._f]/this.spriteSheet._f|0,o=this.frames[this._f]%this.spriteSheet._f|0;h.drawImage(this.spriteSheet.image,o*this.width+(2*o+1)*this.margin,n*this.height+(2*n+1)*this.margin,this.width,this.height,t,e,i,s)}}function a(t){return new o(t)}a.prototype=o.prototype,a.class=o;let c=/(jpeg|jpg|gif|png)$/,l=/(wav|mp3|ogg|aac)$/,d=/^\//,u=/\/$/,f=new WeakMap,g="",p="",m="";function _(t,e){return new URL(t,e).href}function w(t,e){return[t.replace(u,""),t?e.replace(d,""):e].filter(t=>t).join("/")}function y(t){return t.split(".").pop()}function x(t){let e=t.replace("."+y(t),"");return 2==e.split("/").length?e.replace(d,""):e}let b={},v={},j={};function A(){window.__k||(window.__k={dm:f,u:_,d:j,i:b})}function S(t){return A(),new Promise((e,i)=>{let s,n,r;if(s=w(g,t),b[s])return e(b[s]);(n=new Image).onload=function(){r=_(s,window.location.href),b[x(t)]=b[s]=b[r]=this,h("assetLoaded",this,t),e(this)},n.onerror=function(){i(s)},n.src=s})}function O(t){return new Promise((e,i)=>{let s,n,r,o;return s=new Audio,n=function(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}(s),(t=[].concat(t).reduce((t,e)=>t||(n[y(e)]?e:null),0))?(r=w(p,t),v[r]?e(v[r]):(s.addEventListener("canplay",function(){o=_(r,window.location.href),v[x(t)]=v[r]=v[o]=this,h("assetLoaded",this,t),e(this)}),s.onerror=function(){i(r)},s.src=r,void s.load())):i(t)})}function P(t){let e,i;return A(),e=w(m,t),j[e]?Promise.resolve(j[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=_(e,window.location.href),"object"==typeof s&&f.set(s,i),j[x(t)]=j[e]=j[i]=s,h("assetLoaded",s,t),s))}const E=()=>{};function M(){let t=n();r().clearRect(0,0,t.width,t.height)}let k={},L={},I={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function z(t){let e=I[t.which];L[e]=!0,k[e]&&k[e](t)}function D(t){L[I[t.which]]=!1}function R(){L={}}function W(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function C(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}let T=[],N=[],X={},Y=[],U={},q={0:"left",1:"middle",2:"right"},F={x:0,y:0,radius:5};function K(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=F.x-Math.max(e,Math.min(F.x,e+t.width)),h=F.y-Math.max(i,Math.min(F.y,i+t.height));return s*s+h*h=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(F):K(t))return t}function B(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!0,Q(t,"onDown")}function H(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!1,Q(t,"onUp")}function J(t){Q(t,"onOver")}function G(){U={}}function Q(t,e){let i,s,h=n();if(!h)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let r=h.height/h.offsetHeight,o=h.getBoundingClientRect(),a=(i-o.left)*r,c=(s-o.top)*r;F.x=a,F.y=c,t.preventDefault();let l=$();l&&l[e]&&l[e](t),X[e]&&X[e](t,l)}class V{constructor({create:t,maxSize:e=1024}={}){this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function Z(t){return new V(t)}function tt(t,e){let i=[],s=e.x+e.width/2,h=e.y+e.height/2,n=t.y=e.y,r=t.y+t.height>=h&&t.y=e.x&&(n&&i.push(0),r&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1}}function it(t){return new et(t)}it.prototype=et.prototype,it.class=et;class st{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return ht(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function ht(t,e,i={}){let s=new st(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}ht.prototype=st.prototype,ht.class=st;class nt{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:h,ddx:n,ddy:o,width:a,height:c,image:l}=t;this.position=ht(e,i),this.velocity=ht(s,h),this.acceleration=ht(n,o),this._fx=this._fy=1,this.width=this.height=this.rotation=0,this.ttl=1/0,this.anchor={x:0,y:0},this.context=r();for(let e in t)this[e]=t[e];l&&(this.width=void 0!==a?a:l.width,this.height=void 0!==c?c:l.height),this.sx=0,this.sy=0}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}get width(){return this._w}get height(){return this._h}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}set width(t){this._fx=t<0?-1:1,this._w=Math.abs(t)}set height(t){this._fy=t<0?-1:1,this._h=Math.abs(t)}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,h=t.y;return t.anchor&&(s-=t.width*t.anchor.x,h-=t.height*t.anchor.y),es&&ih}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;if(this.context.save(),this.context.translate(this.viewX,this.viewY),this.rotation&&this.context.rotate(this.rotation),-1==this._fx||-1==this._fy){let i=this.width/2+t,s=this.height/2+e;this.context.translate(i,s),this.context.scale(this._fx,this._fy),this.context.translate(-i,-s)}this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function rt(t){return new nt(t)}function ot(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],h=+i[1],n=s;if(s=h;n--)e.push(n);return e}rt.prototype=nt.prototype,rt.class=nt;class at{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:h}={}){this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(h)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:h,loop:n}=t[i];e=[],[].concat(s).map(t=>{e=e.concat(ot(t))}),this.animations[i]=a({spriteSheet:this,frames:e,frameRate:h,loop:n})}}}function ct(t){return new at(t)}return ct.prototype=at.prototype,ct.class=at,{Animation:a,imageAssets:b,audioAssets:v,dataAssets:j,setImagePath:function(t){g=t},setAudioPath:function(t){p=t},setDataPath:function(t){m=t},loadImage:S,loadAudio:O,loadData:P,load:function(...t){return A(),Promise.all(t.map(t=>{let e=y([].concat(t)[0]);return e.match(c)?S(t):e.match(l)?O(t):P(t)}))},init:function(i){return t=document.getElementById(i)||i||document.querySelector("canvas"),(e=t.getContext("2d")).imageSmoothingEnabled=!1,h("init"),{canvas:t,context:e}},getCanvas:n,getContext:r,on:s,off:function(t,e){let s;!i[t]||(s=i[t].indexOf(e))<0||i[t].splice(s,1)},emit:h,GameLoop:function({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){let n,r,o,a,c,l=0,d=1e3/t,u=1/t,f=e?M:E;function g(){if(r=requestAnimationFrame(g),o=performance.now(),a=o-n,n=o,!(a>1e3)){for(h("tick"),l+=a;l>=d;)c.update(u),l-=d;f(),c.render()}}return c={update:i,render:s,isStopped:!0,start(){n=performance.now(),this.isStopped=!1,requestAnimationFrame(g)},stop(){this.isStopped=!0,cancelAnimationFrame(r)}}},keyMap:I,initKeys:function(){let t;for(t=0;t<26;t++)I[65+t]=(10+t).toString(36);for(t=0;t<10;t++)I[48+t]=""+t;window.addEventListener("keydown",z),window.addEventListener("keyup",D),window.addEventListener("blur",R)},bindKeys:function(t,e){[].concat(t).map(t=>k[t]=e)},unbindKeys:function(t){[].concat(t).map(t=>k[t]=0)},keyPressed:function(t){return!!L[t]},registerPlugin:function(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let h=i(t,e,...s);return h||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),h=i["_o"+s].call(this,...e);return this._aInc(this,s,h,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))},unregisterPlugin:function(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);t.startsWith("before")?C(i._inc[s].before,e[t]):t.startsWith("after")&&C(i._inc[s].after,e[t])})},extendObject:function(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})},initPointer:function(){let t=n();t.addEventListener("mousedown",B),t.addEventListener("touchstart",B),t.addEventListener("mouseup",H),t.addEventListener("touchend",H),t.addEventListener("blur",G),t.addEventListener("mousemove",J),t.addEventListener("touchmove",J),s("tick",()=>{N.length=0,T.map(t=>{N.push(t)}),T.length=0})},pointer:F,track:function(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){T.push(this),this._r()},Y.push(t))})},untrack:function(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=Y.indexOf(t);-1!==e&&Y.splice(e,1)})},pointerOver:function(t){return!!Y.includes(t)&&$()===t},onPointerDown:function(t){X.onDown=t},onPointerUp:function(t){X.onUp=t},pointerPressed:function(t){return!!U[t]},Pool:Z,Quadtree:it,Sprite:rt,SpriteSheet:ct,setStoreItem:function(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))},getStoreItem:function(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e},TileEngine:function(t={}){let{width:e,height:i,tilewidth:s,tileheight:h,context:o=r(),tilesets:a,layers:c}=t,l=e*s,d=i*h,u=document.createElement("canvas"),f=u.getContext("2d");u.width=l,u.height=d;let g={},p={},m=[],_=Object.assign({context:o,mapwidth:l,mapheight:d,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),l-n().width),m.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),d-n().height),m.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),b(u)},renderLayer(t){let e=p[t],i=g[t];e||((e=document.createElement("canvas")).width=l,e.height=d,p[t]=e,_._r(i,e.getContext("2d"))),b(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let h=w(s),n=y(i),r=w(s+e.height),o=y(i+e.width),a=g[t];for(let t=h;t<=r;t++)for(let e=n;e<=o;e++)if(a.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||w(e.y),s=e.col||y(e.x);return g[t]?g[t].data[s+i*_.width]:-1},setTileAtLayer(t,e,i){let s=e.row||w(e.y),h=e.col||y(e.x);g[t]&&(this._d=!0,g[t].data[h+s*_.width]=i)},addObject(t){m.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=m.indexOf(t);-1!==e&&(m.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=_.tilesets.length-1;e>=0&&(s=_.tilesets[e],!(t/s.firstgid>=1));e--);let h=s.tilewidth||_.tilewidth,n=s.tileheight||_.tileheight,r=s.margin||0,o=s.image,a=t-s.firstgid,c=s.columns||o.width/(h+r)|0,l=i%_.width*h,d=(i/_.width|0)*n,u=a%c*(h+r),f=(a/c|0)*(n+r);e.drawImage(o,u,f,h,n,l,d,h,n)}),e.restore()},_p:x},t);function w(t){return t/_.tileheight|0}function y(t){return t/_.tilewidth|0}function x(){_.layers&&_.layers.map(t=>{g[t.name]=t,!1!==t.visible&&_._r(t,f)})}function b(t){const{width:e,height:i}=n(),s=Math.min(t.width,e),h=Math.min(t.height,i);_.context.drawImage(t,_.sx,_.sy,s,h,0,0,s,h)}return _.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){let t=window.__k.d[window.__k.u(e.source,i)];Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){let t=window.__k.i[window.__k.u(e.image,i)];e.image=t}}),x(),_},Vector:ht}}(); \ No newline at end of file diff --git a/kontra.min.mjs b/kontra.min.mjs index 765339ab..2bb43dfc 100644 --- a/kontra.min.mjs +++ b/kontra.min.mjs @@ -1 +1 @@ -let canvasEl,context,callbacks={};function on(t,e){callbacks[t]=callbacks[t]||[],callbacks[t].push(e)}function off(t,e){let i;!callbacks[t]||(i=callbacks[t].indexOf(e))<0||callbacks[t].splice(i,1)}function emit(t,...e){callbacks[t]&&callbacks[t].map(t=>t(...e))}function getCanvas(){return canvasEl}function getContext(){return context}function init(t){if(!(canvasEl=document.getElementById(t)||t||document.querySelector("canvas")))throw Error("You must provide a canvas element for the game");return(context=canvasEl.getContext("2d")).imageSmoothingEnabled=!1,emit("init"),{canvas:canvasEl,context:context}}class Animation{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:n,height:a,margin:r=0}=t.frame;this.width=n,this.height=a,this.margin=r,this._f=0,this._a=0}clone(){return animationFactory(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:n=getContext()}={}){let a=this.frames[this._f]/this.spriteSheet._f|0,r=this.frames[this._f]%this.spriteSheet._f|0;n.drawImage(this.spriteSheet.image,r*this.width+(2*r+1)*this.margin,a*this.height+(2*a+1)*this.margin,this.width,this.height,t,e,i,s)}}function animationFactory(t){return new Animation(t)}animationFactory.prototype=Animation.prototype,animationFactory.class=Animation;let imageRegex=/(jpeg|jpg|gif|png)$/,audioRegex=/(wav|mp3|ogg|aac)$/,leadingSlash=/^\//,trailingSlash=/\/$/,dataMap=new WeakMap,imagePath="",audioPath="",dataPath="";function getUrl(t,e){return new URL(t,e).href}function joinPath(t,e){return[t.replace(trailingSlash,""),t?e.replace(leadingSlash,""):e].filter(t=>t).join("/")}function getExtension(t){return t.split(".").pop()}function getName(t){let e=t.replace("."+getExtension(t),"");return 2==e.split("/").length?e.replace(leadingSlash,""):e}function getCanPlay(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}let imageAssets={},audioAssets={},dataAssets={};function addGlobal(){window.__k||(window.__k={dm:dataMap,u:getUrl,d:dataAssets,i:imageAssets})}function setImagePath(t){imagePath=t}function setAudioPath(t){audioPath=t}function setDataPath(t){dataPath=t}function loadImage(t){return addGlobal(),new Promise((e,i)=>{let s,n,a;if(s=joinPath(imagePath,t),imageAssets[s])return e(imageAssets[s]);(n=new Image).onload=function(){a=getUrl(s,window.location.href),imageAssets[getName(t)]=imageAssets[s]=imageAssets[a]=this,emit("assetLoaded",this,t),e(this)},n.onerror=function(){i("Unable to load image "+s)},n.src=s})}function loadAudio(t){return new Promise((e,i)=>{let s,n,a,r;return s=new Audio,n=getCanPlay(s),(t=[].concat(t).reduce((t,e)=>t||(n[getExtension(e)]?e:null),0))?(a=joinPath(audioPath,t),audioAssets[a]?e(audioAssets[a]):(s.addEventListener("canplay",function(){r=getUrl(a,window.location.href),audioAssets[getName(t)]=audioAssets[a]=audioAssets[r]=this,emit("assetLoaded",this,t),e(this)}),s.onerror=function(){i("Unable to load audio "+a)},s.src=a,void s.load())):i("cannot play any of the audio formats provided"+t)})}function loadData(t){let e,i;return addGlobal(),e=joinPath(dataPath,t),dataAssets[e]?Promise.resolve(dataAssets[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=getUrl(e,window.location.href),"object"==typeof s&&dataMap.set(s,i),dataAssets[getName(t)]=dataAssets[e]=dataAssets[i]=s,emit("assetLoaded",s,t),s))}function load(...t){return addGlobal(),Promise.all(t.map(t=>{let e=getExtension([].concat(t)[0]);return e.match(imageRegex)?loadImage(t):e.match(audioRegex)?loadAudio(t):loadData(t)}))}const noop=()=>{};function clear(){let t=getCanvas();getContext().clearRect(0,0,t.width,t.height)}function GameLoop({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){if(!i||!s)throw Error("You must provide update() and render() functions");let n,a,r,o,h,c=0,d=1e3/t,l=1/t,u=e?clear:noop;function p(){if(a=requestAnimationFrame(p),r=performance.now(),o=r-n,n=r,!(o>1e3)){for(emit("tick"),c+=o;c>=d;)h.update(l),c-=d;u(),h.render()}}return h={update:i,render:s,isStopped:!0,start(){n=performance.now(),this.isStopped=!1,requestAnimationFrame(p)},stop(){this.isStopped=!0,cancelAnimationFrame(a)},_frame:p,set _last(t){n=t}}}let callbacks$1={},pressedKeys={},keyMap={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function keydownEventHandler(t){let e=keyMap[t.which];pressedKeys[e]=!0,callbacks$1[e]&&callbacks$1[e](t)}function keyupEventHandler(t){pressedKeys[keyMap[t.which]]=!1}function blurEventHandler(){pressedKeys={}}function initKeys(){let t;for(t=0;t<26;t++)keyMap[65+t]=(10+t).toString(36);for(t=0;t<10;t++)keyMap[48+t]=""+t;window.addEventListener("keydown",keydownEventHandler),window.addEventListener("keyup",keyupEventHandler),window.addEventListener("blur",blurEventHandler)}function bindKeys(t,e){[].concat(t).map(t=>callbacks$1[t]=e)}function unbindKeys(t){[].concat(t).map(t=>callbacks$1[t]=0)}function keyPressed(t){return!!pressedKeys[t]}function getMethod(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function removeInterceptor(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}function registerPlugin(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let n=i(t,e,...s);return n||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=getMethod(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),n=i["_o"+s].call(this,...e);return this._aInc(this,s,n,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))}function unregisterPlugin(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=getMethod(t);t.startsWith("before")?removeInterceptor(i._inc[s].before,e[t]):t.startsWith("after")&&removeInterceptor(i._inc[s].after,e[t])})}function extendObject(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})}let thisFrameRenderOrder=[],lastFrameRenderOrder=[],callbacks$2={},trackedObjects=[],pressedButtons={},buttonMap={0:"left",1:"middle",2:"right"},pointer={x:0,y:0,radius:5};function circleRectCollision(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=pointer.x-Math.max(e,Math.min(pointer.x,e+t.width)),n=pointer.y-Math.max(i,Math.min(pointer.y,i+t.height));return s*s+n*n=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(pointer):circleRectCollision(t))return t}function pointerDownHandler(t){let e=void 0!==t.button?buttonMap[t.button]:"left";pressedButtons[e]=!0,pointerHandler(t,"onDown")}function pointerUpHandler(t){let e=void 0!==t.button?buttonMap[t.button]:"left";pressedButtons[e]=!1,pointerHandler(t,"onUp")}function mouseMoveHandler(t){pointerHandler(t,"onOver")}function blurEventHandler$1(){pressedButtons={}}function pointerHandler(t,e){let i,s,n=getCanvas();if(!n)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let a=n.height/n.offsetHeight,r=n.getBoundingClientRect(),o=(i-r.left)*a,h=(s-r.top)*a;pointer.x=o,pointer.y=h,t.preventDefault();let c=getCurrentObject();c&&c[e]&&c[e](t),callbacks$2[e]&&callbacks$2[e](t,c)}function initPointer(){let t=getCanvas();t.addEventListener("mousedown",pointerDownHandler),t.addEventListener("touchstart",pointerDownHandler),t.addEventListener("mouseup",pointerUpHandler),t.addEventListener("touchend",pointerUpHandler),t.addEventListener("blur",blurEventHandler$1),t.addEventListener("mousemove",mouseMoveHandler),t.addEventListener("touchmove",mouseMoveHandler),on("tick",()=>{lastFrameRenderOrder.length=0,thisFrameRenderOrder.map(t=>{lastFrameRenderOrder.push(t)}),thisFrameRenderOrder.length=0})}function track(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){thisFrameRenderOrder.push(this),this._r()},trackedObjects.push(t))})}function untrack(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=trackedObjects.indexOf(t);-1!==e&&trackedObjects.splice(e,1)})}function pointerOver(t){return!!trackedObjects.includes(t)&&getCurrentObject()===t}function onPointerDown(t){callbacks$2.onDown=t}function onPointerUp(t){callbacks$2.onUp=t}function pointerPressed(t){return!!pressedButtons[t]}class Pool{constructor({create:t,maxSize:e=1024}={}){let i;if(!t||!(i=t())||!(i.update&&i.init&&i.isAlive))throw Error("Must provide create() function which returns an object with init(), update(), and isAlive() functions");this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function poolFactory(t){return new Pool(t)}function getIndices(t,e){let i=[],s=e.x+e.width/2,n=e.y+e.height/2,a=t.y=e.y,r=t.y+t.height>=n&&t.y=e.x&&(a&&i.push(0),r&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1,this._s[i]._p=this}}function quadtreeFactory(t){return new Quadtree(t)}quadtreeFactory.prototype=Quadtree.prototype,quadtreeFactory.class=Quadtree;class Vector{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return vectorFactory(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function vectorFactory(t,e,i={}){let s=new Vector(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}vectorFactory.prototype=Vector.prototype,vectorFactory.class=Vector;class Sprite{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:n,ddx:a,ddy:r,width:o,height:h,image:c}=t;this.position=vectorFactory(e,i),this.velocity=vectorFactory(s,n),this.acceleration=vectorFactory(a,r),this.width=this.height=this.rotation=0,this.ttl=1/0,this.anchor={x:0,y:0},this.context=getContext();for(let e in t)this[e]=t[e];c&&(this.width=void 0!==o?o:c.width,this.height=void 0!==h?h:c.height),this.sx=0,this.sy=0,this._fx=this._fy=1}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}get width(){return this._w}get height(){return this._h}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}set width(t){this._fx=t<0?-1:1,this._w=Math.abs(t)}set height(t){this._fy=t<0?-1:1,this._h=Math.abs(t)}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,n=t.y;return t.anchor&&(s-=t.width*t.anchor.x,n-=t.height*t.anchor.y),es&&in}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;if(this.context.save(),this.context.translate(this.viewX,this.viewY),this.rotation&&this.context.rotate(this.rotation),-1==this._fx||-1==this._fy){let i=this.width/2+t,s=this.height/2+e;this.context.translate(i,s),this.context.scale(this._fx,this._fy),this.context.translate(-i,-s)}this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function spriteFactory(t){return new Sprite(t)}function parseFrames(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],n=+i[1],a=s;if(s=n;a--)e.push(a);return e}spriteFactory.prototype=Sprite.prototype,spriteFactory.class=Sprite;class SpriteSheet{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:n}={}){if(!t)throw Error("You must provide an Image for the SpriteSheet");this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(n)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:n,loop:a}=t[i];if(e=[],void 0===s)throw Error("Animation "+i+" must provide a frames property");[].concat(s).map(t=>{e=e.concat(parseFrames(t))}),this.animations[i]=animationFactory({spriteSheet:this,frames:e,frameRate:n,loop:a})}}}function spriteSheetFactory(t){return new SpriteSheet(t)}function setStoreItem(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))}function getStoreItem(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e}function TileEngine(t={}){let{width:e,height:i,tilewidth:s,tileheight:n,context:a=getContext(),tilesets:r,layers:o}=t,h=e*s,c=i*n,d=document.createElement("canvas"),l=d.getContext("2d");d.width=h,d.height=c;let u={},p={},g=[],m=Object.assign({context:a,mapwidth:h,mapheight:c,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),h-getCanvas().width),g.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),c-getCanvas().height),g.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),w(d)},renderLayer(t){let e=p[t],i=u[t];e||((e=document.createElement("canvas")).width=h,e.height=c,p[t]=e,m._r(i,e.getContext("2d"))),w(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let n=f(s),a=y(i),r=f(s+e.height),o=y(i+e.width),h=u[t];for(let t=n;t<=r;t++)for(let e=a;e<=o;e++)if(h.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||f(e.y),s=e.col||y(e.x);return u[t]?u[t].data[s+i*m.width]:-1},setTileAtLayer(t,e,i){let s=e.row||f(e.y),n=e.col||y(e.x);u[t]&&(this._d=!0,u[t].data[n+s*m.width]=i)},addObject(t){g.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=g.indexOf(t);-1!==e&&(g.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=m.tilesets.length-1;e>=0&&(s=m.tilesets[e],!(t/s.firstgid>=1));e--);let n=s.tilewidth||m.tilewidth,a=s.tileheight||m.tileheight,r=s.margin||0,o=s.image,h=t-s.firstgid,c=s.columns||o.width/(n+r)|0,d=i%m.width*n,l=(i/m.width|0)*a,u=h%c*(n+r),p=(h/c|0)*(a+r);e.drawImage(o,u,p,n,a,d,l,n,a)}),e.restore()},_p:_,layerCanvases:p},t);function f(t){return t/m.tileheight|0}function y(t){return t/m.tilewidth|0}function _(){m.layers&&m.layers.map(t=>{u[t.name]=t,!1!==t.visible&&m._r(t,l)})}function w(t){const{width:e,height:i}=getCanvas(),s=Math.min(t.width,e),n=Math.min(t.height,i);m.context.drawImage(t,m.sx,m.sy,s,n,0,0,s,n)}return m.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){if(!window.__k)throw Error('You must use "load" or "loadData" to resolve tileset.source');let t=window.__k.d[window.__k.u(e.source,i)];if(!t)throw Error(`You must load the tileset source "${e.source}" before loading the tileset`);Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){if(!window.__k)throw Error('You must use "load" or "loadImage" to resolve tileset.image');let t=window.__k.i[window.__k.u(e.image,i)];if(!t)throw Error(`You must load the image "${e.image}" before loading the tileset`);e.image=t}}),_(),m}spriteSheetFactory.prototype=SpriteSheet.prototype,spriteSheetFactory.class=SpriteSheet;let kontra={Animation:animationFactory,imageAssets:imageAssets,audioAssets:audioAssets,dataAssets:dataAssets,setImagePath:setImagePath,setAudioPath:setAudioPath,setDataPath:setDataPath,loadImage:loadImage,loadAudio:loadAudio,loadData:loadData,load:load,init:init,getCanvas:getCanvas,getContext:getContext,on:on,off:off,emit:emit,GameLoop:GameLoop,keyMap:keyMap,initKeys:initKeys,bindKeys:bindKeys,unbindKeys:unbindKeys,keyPressed:keyPressed,registerPlugin:registerPlugin,unregisterPlugin:unregisterPlugin,extendObject:extendObject,initPointer:initPointer,pointer:pointer,track:track,untrack:untrack,pointerOver:pointerOver,onPointerDown:onPointerDown,onPointerUp:onPointerUp,pointerPressed:pointerPressed,Pool:poolFactory,Quadtree:quadtreeFactory,Sprite:spriteFactory,SpriteSheet:spriteSheetFactory,setStoreItem:setStoreItem,getStoreItem:getStoreItem,TileEngine:TileEngine,Vector:vectorFactory};export{animationFactory as Animation,imageAssets,audioAssets,dataAssets,setImagePath,setAudioPath,setDataPath,loadImage,loadAudio,loadData,load,init,getCanvas,getContext,on,off,emit,GameLoop,keyMap,initKeys,bindKeys,unbindKeys,keyPressed,registerPlugin,unregisterPlugin,extendObject,initPointer,pointer,track,untrack,pointerOver,onPointerDown,onPointerUp,pointerPressed,poolFactory as Pool,quadtreeFactory as Quadtree,spriteFactory as Sprite,spriteSheetFactory as SpriteSheet,setStoreItem,getStoreItem,TileEngine,vectorFactory as Vector};export default kontra; \ No newline at end of file +let canvasEl,context,callbacks={};function on(t,e){callbacks[t]=callbacks[t]||[],callbacks[t].push(e)}function off(t,e){let i;!callbacks[t]||(i=callbacks[t].indexOf(e))<0||callbacks[t].splice(i,1)}function emit(t,...e){callbacks[t]&&callbacks[t].map(t=>t(...e))}function getCanvas(){return canvasEl}function getContext(){return context}function init(t){if(!(canvasEl=document.getElementById(t)||t||document.querySelector("canvas")))throw Error("You must provide a canvas element for the game");return(context=canvasEl.getContext("2d")).imageSmoothingEnabled=!1,emit("init"),{canvas:canvasEl,context:context}}class Animation{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:n,height:a,margin:r=0}=t.frame;this.width=n,this.height=a,this.margin=r,this._f=0,this._a=0}clone(){return animationFactory(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:n=getContext()}={}){let a=this.frames[this._f]/this.spriteSheet._f|0,r=this.frames[this._f]%this.spriteSheet._f|0;n.drawImage(this.spriteSheet.image,r*this.width+(2*r+1)*this.margin,a*this.height+(2*a+1)*this.margin,this.width,this.height,t,e,i,s)}}function animationFactory(t){return new Animation(t)}animationFactory.prototype=Animation.prototype,animationFactory.class=Animation;let imageRegex=/(jpeg|jpg|gif|png)$/,audioRegex=/(wav|mp3|ogg|aac)$/,leadingSlash=/^\//,trailingSlash=/\/$/,dataMap=new WeakMap,imagePath="",audioPath="",dataPath="";function getUrl(t,e){return new URL(t,e).href}function joinPath(t,e){return[t.replace(trailingSlash,""),t?e.replace(leadingSlash,""):e].filter(t=>t).join("/")}function getExtension(t){return t.split(".").pop()}function getName(t){let e=t.replace("."+getExtension(t),"");return 2==e.split("/").length?e.replace(leadingSlash,""):e}function getCanPlay(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}let imageAssets={},audioAssets={},dataAssets={};function addGlobal(){window.__k||(window.__k={dm:dataMap,u:getUrl,d:dataAssets,i:imageAssets})}function setImagePath(t){imagePath=t}function setAudioPath(t){audioPath=t}function setDataPath(t){dataPath=t}function loadImage(t){return addGlobal(),new Promise((e,i)=>{let s,n,a;if(s=joinPath(imagePath,t),imageAssets[s])return e(imageAssets[s]);(n=new Image).onload=function(){a=getUrl(s,window.location.href),imageAssets[getName(t)]=imageAssets[s]=imageAssets[a]=this,emit("assetLoaded",this,t),e(this)},n.onerror=function(){i("Unable to load image "+s)},n.src=s})}function loadAudio(t){return new Promise((e,i)=>{let s,n,a,r;return s=new Audio,n=getCanPlay(s),(t=[].concat(t).reduce((t,e)=>t||(n[getExtension(e)]?e:null),0))?(a=joinPath(audioPath,t),audioAssets[a]?e(audioAssets[a]):(s.addEventListener("canplay",function(){r=getUrl(a,window.location.href),audioAssets[getName(t)]=audioAssets[a]=audioAssets[r]=this,emit("assetLoaded",this,t),e(this)}),s.onerror=function(){i("Unable to load audio "+a)},s.src=a,void s.load())):i("cannot play any of the audio formats provided"+t)})}function loadData(t){let e,i;return addGlobal(),e=joinPath(dataPath,t),dataAssets[e]?Promise.resolve(dataAssets[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=getUrl(e,window.location.href),"object"==typeof s&&dataMap.set(s,i),dataAssets[getName(t)]=dataAssets[e]=dataAssets[i]=s,emit("assetLoaded",s,t),s))}function load(...t){return addGlobal(),Promise.all(t.map(t=>{let e=getExtension([].concat(t)[0]);return e.match(imageRegex)?loadImage(t):e.match(audioRegex)?loadAudio(t):loadData(t)}))}const noop=()=>{};function clear(){let t=getCanvas();getContext().clearRect(0,0,t.width,t.height)}function GameLoop({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){if(!i||!s)throw Error("You must provide update() and render() functions");let n,a,r,o,h,c=0,d=1e3/t,l=1/t,u=e?clear:noop;function p(){if(a=requestAnimationFrame(p),r=performance.now(),o=r-n,n=r,!(o>1e3)){for(emit("tick"),c+=o;c>=d;)h.update(l),c-=d;u(),h.render()}}return h={update:i,render:s,isStopped:!0,start(){n=performance.now(),this.isStopped=!1,requestAnimationFrame(p)},stop(){this.isStopped=!0,cancelAnimationFrame(a)},_frame:p,set _last(t){n=t}}}let callbacks$1={},pressedKeys={},keyMap={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function keydownEventHandler(t){let e=keyMap[t.which];pressedKeys[e]=!0,callbacks$1[e]&&callbacks$1[e](t)}function keyupEventHandler(t){pressedKeys[keyMap[t.which]]=!1}function blurEventHandler(){pressedKeys={}}function initKeys(){let t;for(t=0;t<26;t++)keyMap[65+t]=(10+t).toString(36);for(t=0;t<10;t++)keyMap[48+t]=""+t;window.addEventListener("keydown",keydownEventHandler),window.addEventListener("keyup",keyupEventHandler),window.addEventListener("blur",blurEventHandler)}function bindKeys(t,e){[].concat(t).map(t=>callbacks$1[t]=e)}function unbindKeys(t){[].concat(t).map(t=>callbacks$1[t]=0)}function keyPressed(t){return!!pressedKeys[t]}function getMethod(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function removeInterceptor(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}function registerPlugin(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let n=i(t,e,...s);return n||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=getMethod(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),n=i["_o"+s].call(this,...e);return this._aInc(this,s,n,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))}function unregisterPlugin(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=getMethod(t);t.startsWith("before")?removeInterceptor(i._inc[s].before,e[t]):t.startsWith("after")&&removeInterceptor(i._inc[s].after,e[t])})}function extendObject(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})}let thisFrameRenderOrder=[],lastFrameRenderOrder=[],callbacks$2={},trackedObjects=[],pressedButtons={},buttonMap={0:"left",1:"middle",2:"right"},pointer={x:0,y:0,radius:5};function circleRectCollision(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=pointer.x-Math.max(e,Math.min(pointer.x,e+t.width)),n=pointer.y-Math.max(i,Math.min(pointer.y,i+t.height));return s*s+n*n=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(pointer):circleRectCollision(t))return t}function pointerDownHandler(t){let e=void 0!==t.button?buttonMap[t.button]:"left";pressedButtons[e]=!0,pointerHandler(t,"onDown")}function pointerUpHandler(t){let e=void 0!==t.button?buttonMap[t.button]:"left";pressedButtons[e]=!1,pointerHandler(t,"onUp")}function mouseMoveHandler(t){pointerHandler(t,"onOver")}function blurEventHandler$1(){pressedButtons={}}function pointerHandler(t,e){let i,s,n=getCanvas();if(!n)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let a=n.height/n.offsetHeight,r=n.getBoundingClientRect(),o=(i-r.left)*a,h=(s-r.top)*a;pointer.x=o,pointer.y=h,t.preventDefault();let c=getCurrentObject();c&&c[e]&&c[e](t),callbacks$2[e]&&callbacks$2[e](t,c)}function initPointer(){let t=getCanvas();t.addEventListener("mousedown",pointerDownHandler),t.addEventListener("touchstart",pointerDownHandler),t.addEventListener("mouseup",pointerUpHandler),t.addEventListener("touchend",pointerUpHandler),t.addEventListener("blur",blurEventHandler$1),t.addEventListener("mousemove",mouseMoveHandler),t.addEventListener("touchmove",mouseMoveHandler),on("tick",()=>{lastFrameRenderOrder.length=0,thisFrameRenderOrder.map(t=>{lastFrameRenderOrder.push(t)}),thisFrameRenderOrder.length=0})}function track(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){thisFrameRenderOrder.push(this),this._r()},trackedObjects.push(t))})}function untrack(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=trackedObjects.indexOf(t);-1!==e&&trackedObjects.splice(e,1)})}function pointerOver(t){return!!trackedObjects.includes(t)&&getCurrentObject()===t}function onPointerDown(t){callbacks$2.onDown=t}function onPointerUp(t){callbacks$2.onUp=t}function pointerPressed(t){return!!pressedButtons[t]}class Pool{constructor({create:t,maxSize:e=1024}={}){let i;if(!t||!(i=t())||!(i.update&&i.init&&i.isAlive))throw Error("Must provide create() function which returns an object with init(), update(), and isAlive() functions");this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function poolFactory(t){return new Pool(t)}function getIndices(t,e){let i=[],s=e.x+e.width/2,n=e.y+e.height/2,a=t.y=e.y,r=t.y+t.height>=n&&t.y=e.x&&(a&&i.push(0),r&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1,this._s[i]._p=this}}function quadtreeFactory(t){return new Quadtree(t)}quadtreeFactory.prototype=Quadtree.prototype,quadtreeFactory.class=Quadtree;class Vector{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return vectorFactory(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function vectorFactory(t,e,i={}){let s=new Vector(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}vectorFactory.prototype=Vector.prototype,vectorFactory.class=Vector;class Sprite{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:n,ddx:a,ddy:r,width:o,height:h,image:c}=t;this.position=vectorFactory(e,i),this.velocity=vectorFactory(s,n),this.acceleration=vectorFactory(a,r),this._fx=this._fy=1,this.width=this.height=this.rotation=0,this.ttl=1/0,this.anchor={x:0,y:0},this.context=getContext();for(let e in t)this[e]=t[e];c&&(this.width=void 0!==o?o:c.width,this.height=void 0!==h?h:c.height),this.sx=0,this.sy=0}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}get width(){return this._w}get height(){return this._h}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}set width(t){this._fx=t<0?-1:1,this._w=Math.abs(t)}set height(t){this._fy=t<0?-1:1,this._h=Math.abs(t)}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,n=t.y;return t.anchor&&(s-=t.width*t.anchor.x,n-=t.height*t.anchor.y),es&&in}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;if(this.context.save(),this.context.translate(this.viewX,this.viewY),this.rotation&&this.context.rotate(this.rotation),-1==this._fx||-1==this._fy){let i=this.width/2+t,s=this.height/2+e;this.context.translate(i,s),this.context.scale(this._fx,this._fy),this.context.translate(-i,-s)}this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function spriteFactory(t){return new Sprite(t)}function parseFrames(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],n=+i[1],a=s;if(s=n;a--)e.push(a);return e}spriteFactory.prototype=Sprite.prototype,spriteFactory.class=Sprite;class SpriteSheet{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:n}={}){if(!t)throw Error("You must provide an Image for the SpriteSheet");this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(n)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:n,loop:a}=t[i];if(e=[],void 0===s)throw Error("Animation "+i+" must provide a frames property");[].concat(s).map(t=>{e=e.concat(parseFrames(t))}),this.animations[i]=animationFactory({spriteSheet:this,frames:e,frameRate:n,loop:a})}}}function spriteSheetFactory(t){return new SpriteSheet(t)}function setStoreItem(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))}function getStoreItem(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e}function TileEngine(t={}){let{width:e,height:i,tilewidth:s,tileheight:n,context:a=getContext(),tilesets:r,layers:o}=t,h=e*s,c=i*n,d=document.createElement("canvas"),l=d.getContext("2d");d.width=h,d.height=c;let u={},p={},g=[],m=Object.assign({context:a,mapwidth:h,mapheight:c,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),h-getCanvas().width),g.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),c-getCanvas().height),g.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),w(d)},renderLayer(t){let e=p[t],i=u[t];e||((e=document.createElement("canvas")).width=h,e.height=c,p[t]=e,m._r(i,e.getContext("2d"))),w(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let n=f(s),a=y(i),r=f(s+e.height),o=y(i+e.width),h=u[t];for(let t=n;t<=r;t++)for(let e=a;e<=o;e++)if(h.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||f(e.y),s=e.col||y(e.x);return u[t]?u[t].data[s+i*m.width]:-1},setTileAtLayer(t,e,i){let s=e.row||f(e.y),n=e.col||y(e.x);u[t]&&(this._d=!0,u[t].data[n+s*m.width]=i)},addObject(t){g.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=g.indexOf(t);-1!==e&&(g.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=m.tilesets.length-1;e>=0&&(s=m.tilesets[e],!(t/s.firstgid>=1));e--);let n=s.tilewidth||m.tilewidth,a=s.tileheight||m.tileheight,r=s.margin||0,o=s.image,h=t-s.firstgid,c=s.columns||o.width/(n+r)|0,d=i%m.width*n,l=(i/m.width|0)*a,u=h%c*(n+r),p=(h/c|0)*(a+r);e.drawImage(o,u,p,n,a,d,l,n,a)}),e.restore()},_p:_,layerCanvases:p},t);function f(t){return t/m.tileheight|0}function y(t){return t/m.tilewidth|0}function _(){m.layers&&m.layers.map(t=>{u[t.name]=t,!1!==t.visible&&m._r(t,l)})}function w(t){const{width:e,height:i}=getCanvas(),s=Math.min(t.width,e),n=Math.min(t.height,i);m.context.drawImage(t,m.sx,m.sy,s,n,0,0,s,n)}return m.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){if(!window.__k)throw Error('You must use "load" or "loadData" to resolve tileset.source');let t=window.__k.d[window.__k.u(e.source,i)];if(!t)throw Error(`You must load the tileset source "${e.source}" before loading the tileset`);Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){if(!window.__k)throw Error('You must use "load" or "loadImage" to resolve tileset.image');let t=window.__k.i[window.__k.u(e.image,i)];if(!t)throw Error(`You must load the image "${e.image}" before loading the tileset`);e.image=t}}),_(),m}spriteSheetFactory.prototype=SpriteSheet.prototype,spriteSheetFactory.class=SpriteSheet;let kontra={Animation:animationFactory,imageAssets:imageAssets,audioAssets:audioAssets,dataAssets:dataAssets,setImagePath:setImagePath,setAudioPath:setAudioPath,setDataPath:setDataPath,loadImage:loadImage,loadAudio:loadAudio,loadData:loadData,load:load,init:init,getCanvas:getCanvas,getContext:getContext,on:on,off:off,emit:emit,GameLoop:GameLoop,keyMap:keyMap,initKeys:initKeys,bindKeys:bindKeys,unbindKeys:unbindKeys,keyPressed:keyPressed,registerPlugin:registerPlugin,unregisterPlugin:unregisterPlugin,extendObject:extendObject,initPointer:initPointer,pointer:pointer,track:track,untrack:untrack,pointerOver:pointerOver,onPointerDown:onPointerDown,onPointerUp:onPointerUp,pointerPressed:pointerPressed,Pool:poolFactory,Quadtree:quadtreeFactory,Sprite:spriteFactory,SpriteSheet:spriteSheetFactory,setStoreItem:setStoreItem,getStoreItem:getStoreItem,TileEngine:TileEngine,Vector:vectorFactory};export{animationFactory as Animation,imageAssets,audioAssets,dataAssets,setImagePath,setAudioPath,setDataPath,loadImage,loadAudio,loadData,load,init,getCanvas,getContext,on,off,emit,GameLoop,keyMap,initKeys,bindKeys,unbindKeys,keyPressed,registerPlugin,unregisterPlugin,extendObject,initPointer,pointer,track,untrack,pointerOver,onPointerDown,onPointerUp,pointerPressed,poolFactory as Pool,quadtreeFactory as Quadtree,spriteFactory as Sprite,spriteSheetFactory as SpriteSheet,setStoreItem,getStoreItem,TileEngine,vectorFactory as Vector};export default kontra; \ No newline at end of file diff --git a/kontra.mjs b/kontra.mjs index 6b920e7c..5676df7f 100644 --- a/kontra.mjs +++ b/kontra.mjs @@ -2395,6 +2395,9 @@ class Sprite { // defaults + // sx = flipX, sy = flipY + this._fx = this._fy = 1; + /** * The rotation of the sprite around the origin in radians. * @memberof Sprite @@ -2477,9 +2480,6 @@ class Sprite { this[prop] = properties[prop]; } - // sx = flipX, sy = flipY - this._fx = this._fy = 1; - // image sprite if (image) { this.width = (width !== undefined) ? width : image.width; From 5513f04d2ed41964c8ce1be4bacf3bc506cc40cb Mon Sep 17 00:00:00 2001 From: straker Date: Wed, 4 Sep 2019 23:40:48 -0600 Subject: [PATCH 4/6] reset example --- examples/sprite/imageSprite.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/sprite/imageSprite.html b/examples/sprite/imageSprite.html index 0b25f540..bdef2ae9 100644 --- a/examples/sprite/imageSprite.html +++ b/examples/sprite/imageSprite.html @@ -24,8 +24,7 @@ }, x: 300, y: 200, - image: kontra.imageAssets.character, - width: -66 + image: kontra.imageAssets.character }); // render the sprite From a1357777b4c74f5399e12a32232ae653557917df Mon Sep 17 00:00:00 2001 From: straker Date: Wed, 4 Sep 2019 23:51:34 -0600 Subject: [PATCH 5/6] dont use Math.abs --- src/sprite.js | 12 ++++++++---- test/unit/sprite.spec.js | 6 ++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/sprite.js b/src/sprite.js index d932e9fd..ea1cf8af 100644 --- a/src/sprite.js +++ b/src/sprite.js @@ -360,12 +360,16 @@ class Sprite { } set width(value) { - this._fx = value < 0 ? -1 : 1; - this._w = Math.abs(value); + let sign = value < 0 ? -1 : 1; + + this._fx = sign + this._w = value * sign; } set height(value) { - this._fy = value < 0 ? -1 : 1; - this._h = Math.abs(value); + let sign = value < 0 ? -1 : 1; + + this._fy = sign; + this._h = value * sign; } /** diff --git a/test/unit/sprite.spec.js b/test/unit/sprite.spec.js index 785bf50b..9add47f2 100644 --- a/test/unit/sprite.spec.js +++ b/test/unit/sprite.spec.js @@ -740,10 +740,12 @@ describe('sprite', () => { sprite.width = 0; expect(sprite._fx).to.equal(1); + expect(sprite.width).to.equal(0); sprite.width = 100; expect(sprite._fx).to.equal(1); + expect(sprite.width).to.equal(100); }); it('should set _fx to -1 when width is < 0', () => { @@ -755,6 +757,7 @@ describe('sprite', () => { sprite.width = -20; expect(sprite._fx).to.equal(-1); + expect(sprite.width).to.equal(20); }); it('should set _fy to 1 when height is >= 0', () => { @@ -766,10 +769,12 @@ describe('sprite', () => { sprite.height = 0; expect(sprite._fy).to.equal(1); + expect(sprite.height).to.equal(0); sprite.height = 100; expect(sprite._fy).to.equal(1); + expect(sprite.height).to.equal(100); }); it('should set _fy to -1 when height is < 0', () => { @@ -781,6 +786,7 @@ describe('sprite', () => { sprite.height = -20; expect(sprite._fy).to.equal(-1); + expect(sprite.height).to.equal(20); }); it('should not scale the sprite by default', () => { From 9a9422ae13c61c98f8bf51d0da4aaf405cd53792 Mon Sep 17 00:00:00 2001 From: straker Date: Wed, 4 Sep 2019 23:52:49 -0600 Subject: [PATCH 6/6] build --- docs/assets/js/kontra.js | 2 +- kontra.js | 12 ++++++++---- kontra.min.js | 2 +- kontra.min.mjs | 2 +- kontra.mjs | 12 ++++++++---- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/docs/assets/js/kontra.js b/docs/assets/js/kontra.js index 2c47f113..88f4a2f6 100644 --- a/docs/assets/js/kontra.js +++ b/docs/assets/js/kontra.js @@ -1 +1 @@ -var kontra=function(){"use strict";let t,e,i={};function s(t,e){i[t]=i[t]||[],i[t].push(e)}function h(t,...e){i[t]&&i[t].map(t=>t(...e))}function n(){return t}function r(){return e}class o{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:h,height:n,margin:r=0}=t.frame;this.width=h,this.height=n,this.margin=r,this._f=0,this._a=0}clone(){return a(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:h=r()}={}){let n=this.frames[this._f]/this.spriteSheet._f|0,o=this.frames[this._f]%this.spriteSheet._f|0;h.drawImage(this.spriteSheet.image,o*this.width+(2*o+1)*this.margin,n*this.height+(2*n+1)*this.margin,this.width,this.height,t,e,i,s)}}function a(t){return new o(t)}a.prototype=o.prototype,a.class=o;let c=/(jpeg|jpg|gif|png)$/,l=/(wav|mp3|ogg|aac)$/,d=/^\//,u=/\/$/,f=new WeakMap,g="",p="",m="";function _(t,e){return new URL(t,e).href}function w(t,e){return[t.replace(u,""),t?e.replace(d,""):e].filter(t=>t).join("/")}function y(t){return t.split(".").pop()}function x(t){let e=t.replace("."+y(t),"");return 2==e.split("/").length?e.replace(d,""):e}let b={},v={},j={};function A(){window.__k||(window.__k={dm:f,u:_,d:j,i:b})}function S(t){return A(),new Promise((e,i)=>{let s,n,r;if(s=w(g,t),b[s])return e(b[s]);(n=new Image).onload=function(){r=_(s,window.location.href),b[x(t)]=b[s]=b[r]=this,h("assetLoaded",this,t),e(this)},n.onerror=function(){i(s)},n.src=s})}function O(t){return new Promise((e,i)=>{let s,n,r,o;return s=new Audio,n=function(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}(s),(t=[].concat(t).reduce((t,e)=>t||(n[y(e)]?e:null),0))?(r=w(p,t),v[r]?e(v[r]):(s.addEventListener("canplay",function(){o=_(r,window.location.href),v[x(t)]=v[r]=v[o]=this,h("assetLoaded",this,t),e(this)}),s.onerror=function(){i(r)},s.src=r,void s.load())):i(t)})}function P(t){let e,i;return A(),e=w(m,t),j[e]?Promise.resolve(j[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=_(e,window.location.href),"object"==typeof s&&f.set(s,i),j[x(t)]=j[e]=j[i]=s,h("assetLoaded",s,t),s))}const E=()=>{};function M(){let t=n();r().clearRect(0,0,t.width,t.height)}let k={},L={},I={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function z(t){let e=I[t.which];L[e]=!0,k[e]&&k[e](t)}function D(t){L[I[t.which]]=!1}function R(){L={}}function W(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function C(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}let T=[],N=[],X={},Y=[],U={},q={0:"left",1:"middle",2:"right"},F={x:0,y:0,radius:5};function K(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=F.x-Math.max(e,Math.min(F.x,e+t.width)),h=F.y-Math.max(i,Math.min(F.y,i+t.height));return s*s+h*h=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(F):K(t))return t}function B(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!0,Q(t,"onDown")}function H(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!1,Q(t,"onUp")}function J(t){Q(t,"onOver")}function G(){U={}}function Q(t,e){let i,s,h=n();if(!h)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let r=h.height/h.offsetHeight,o=h.getBoundingClientRect(),a=(i-o.left)*r,c=(s-o.top)*r;F.x=a,F.y=c,t.preventDefault();let l=$();l&&l[e]&&l[e](t),X[e]&&X[e](t,l)}class V{constructor({create:t,maxSize:e=1024}={}){this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function Z(t){return new V(t)}function tt(t,e){let i=[],s=e.x+e.width/2,h=e.y+e.height/2,n=t.y=e.y,r=t.y+t.height>=h&&t.y=e.x&&(n&&i.push(0),r&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1}}function it(t){return new et(t)}it.prototype=et.prototype,it.class=et;class st{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return ht(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function ht(t,e,i={}){let s=new st(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}ht.prototype=st.prototype,ht.class=st;class nt{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:h,ddx:n,ddy:o,width:a,height:c,image:l}=t;this.position=ht(e,i),this.velocity=ht(s,h),this.acceleration=ht(n,o),this._fx=this._fy=1,this.width=this.height=this.rotation=0,this.ttl=1/0,this.anchor={x:0,y:0},this.context=r();for(let e in t)this[e]=t[e];l&&(this.width=void 0!==a?a:l.width,this.height=void 0!==c?c:l.height),this.sx=0,this.sy=0}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}get width(){return this._w}get height(){return this._h}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}set width(t){this._fx=t<0?-1:1,this._w=Math.abs(t)}set height(t){this._fy=t<0?-1:1,this._h=Math.abs(t)}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,h=t.y;return t.anchor&&(s-=t.width*t.anchor.x,h-=t.height*t.anchor.y),es&&ih}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;if(this.context.save(),this.context.translate(this.viewX,this.viewY),this.rotation&&this.context.rotate(this.rotation),-1==this._fx||-1==this._fy){let i=this.width/2+t,s=this.height/2+e;this.context.translate(i,s),this.context.scale(this._fx,this._fy),this.context.translate(-i,-s)}this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function rt(t){return new nt(t)}function ot(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],h=+i[1],n=s;if(s=h;n--)e.push(n);return e}rt.prototype=nt.prototype,rt.class=nt;class at{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:h}={}){this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(h)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:h,loop:n}=t[i];e=[],[].concat(s).map(t=>{e=e.concat(ot(t))}),this.animations[i]=a({spriteSheet:this,frames:e,frameRate:h,loop:n})}}}function ct(t){return new at(t)}return ct.prototype=at.prototype,ct.class=at,{Animation:a,imageAssets:b,audioAssets:v,dataAssets:j,setImagePath:function(t){g=t},setAudioPath:function(t){p=t},setDataPath:function(t){m=t},loadImage:S,loadAudio:O,loadData:P,load:function(...t){return A(),Promise.all(t.map(t=>{let e=y([].concat(t)[0]);return e.match(c)?S(t):e.match(l)?O(t):P(t)}))},init:function(i){return t=document.getElementById(i)||i||document.querySelector("canvas"),(e=t.getContext("2d")).imageSmoothingEnabled=!1,h("init"),{canvas:t,context:e}},getCanvas:n,getContext:r,on:s,off:function(t,e){let s;!i[t]||(s=i[t].indexOf(e))<0||i[t].splice(s,1)},emit:h,GameLoop:function({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){let n,r,o,a,c,l=0,d=1e3/t,u=1/t,f=e?M:E;function g(){if(r=requestAnimationFrame(g),o=performance.now(),a=o-n,n=o,!(a>1e3)){for(h("tick"),l+=a;l>=d;)c.update(u),l-=d;f(),c.render()}}return c={update:i,render:s,isStopped:!0,start(){n=performance.now(),this.isStopped=!1,requestAnimationFrame(g)},stop(){this.isStopped=!0,cancelAnimationFrame(r)}}},keyMap:I,initKeys:function(){let t;for(t=0;t<26;t++)I[65+t]=(10+t).toString(36);for(t=0;t<10;t++)I[48+t]=""+t;window.addEventListener("keydown",z),window.addEventListener("keyup",D),window.addEventListener("blur",R)},bindKeys:function(t,e){[].concat(t).map(t=>k[t]=e)},unbindKeys:function(t){[].concat(t).map(t=>k[t]=0)},keyPressed:function(t){return!!L[t]},registerPlugin:function(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let h=i(t,e,...s);return h||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),h=i["_o"+s].call(this,...e);return this._aInc(this,s,h,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))},unregisterPlugin:function(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);t.startsWith("before")?C(i._inc[s].before,e[t]):t.startsWith("after")&&C(i._inc[s].after,e[t])})},extendObject:function(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})},initPointer:function(){let t=n();t.addEventListener("mousedown",B),t.addEventListener("touchstart",B),t.addEventListener("mouseup",H),t.addEventListener("touchend",H),t.addEventListener("blur",G),t.addEventListener("mousemove",J),t.addEventListener("touchmove",J),s("tick",()=>{N.length=0,T.map(t=>{N.push(t)}),T.length=0})},pointer:F,track:function(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){T.push(this),this._r()},Y.push(t))})},untrack:function(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=Y.indexOf(t);-1!==e&&Y.splice(e,1)})},pointerOver:function(t){return!!Y.includes(t)&&$()===t},onPointerDown:function(t){X.onDown=t},onPointerUp:function(t){X.onUp=t},pointerPressed:function(t){return!!U[t]},Pool:Z,Quadtree:it,Sprite:rt,SpriteSheet:ct,setStoreItem:function(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))},getStoreItem:function(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e},TileEngine:function(t={}){let{width:e,height:i,tilewidth:s,tileheight:h,context:o=r(),tilesets:a,layers:c}=t,l=e*s,d=i*h,u=document.createElement("canvas"),f=u.getContext("2d");u.width=l,u.height=d;let g={},p={},m=[],_=Object.assign({context:o,mapwidth:l,mapheight:d,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),l-n().width),m.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),d-n().height),m.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),b(u)},renderLayer(t){let e=p[t],i=g[t];e||((e=document.createElement("canvas")).width=l,e.height=d,p[t]=e,_._r(i,e.getContext("2d"))),b(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let h=w(s),n=y(i),r=w(s+e.height),o=y(i+e.width),a=g[t];for(let t=h;t<=r;t++)for(let e=n;e<=o;e++)if(a.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||w(e.y),s=e.col||y(e.x);return g[t]?g[t].data[s+i*_.width]:-1},setTileAtLayer(t,e,i){let s=e.row||w(e.y),h=e.col||y(e.x);g[t]&&(this._d=!0,g[t].data[h+s*_.width]=i)},addObject(t){m.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=m.indexOf(t);-1!==e&&(m.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=_.tilesets.length-1;e>=0&&(s=_.tilesets[e],!(t/s.firstgid>=1));e--);let h=s.tilewidth||_.tilewidth,n=s.tileheight||_.tileheight,r=s.margin||0,o=s.image,a=t-s.firstgid,c=s.columns||o.width/(h+r)|0,l=i%_.width*h,d=(i/_.width|0)*n,u=a%c*(h+r),f=(a/c|0)*(n+r);e.drawImage(o,u,f,h,n,l,d,h,n)}),e.restore()},_p:x},t);function w(t){return t/_.tileheight|0}function y(t){return t/_.tilewidth|0}function x(){_.layers&&_.layers.map(t=>{g[t.name]=t,!1!==t.visible&&_._r(t,f)})}function b(t){const{width:e,height:i}=n(),s=Math.min(t.width,e),h=Math.min(t.height,i);_.context.drawImage(t,_.sx,_.sy,s,h,0,0,s,h)}return _.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){let t=window.__k.d[window.__k.u(e.source,i)];Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){let t=window.__k.i[window.__k.u(e.image,i)];e.image=t}}),x(),_},Vector:ht}}(); \ No newline at end of file +var kontra=function(){"use strict";let t,e,i={};function s(t,e){i[t]=i[t]||[],i[t].push(e)}function h(t,...e){i[t]&&i[t].map(t=>t(...e))}function n(){return t}function r(){return e}class o{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:h,height:n,margin:r=0}=t.frame;this.width=h,this.height=n,this.margin=r,this._f=0,this._a=0}clone(){return a(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:h=r()}={}){let n=this.frames[this._f]/this.spriteSheet._f|0,o=this.frames[this._f]%this.spriteSheet._f|0;h.drawImage(this.spriteSheet.image,o*this.width+(2*o+1)*this.margin,n*this.height+(2*n+1)*this.margin,this.width,this.height,t,e,i,s)}}function a(t){return new o(t)}a.prototype=o.prototype,a.class=o;let c=/(jpeg|jpg|gif|png)$/,l=/(wav|mp3|ogg|aac)$/,d=/^\//,u=/\/$/,f=new WeakMap,g="",p="",m="";function _(t,e){return new URL(t,e).href}function w(t,e){return[t.replace(u,""),t?e.replace(d,""):e].filter(t=>t).join("/")}function y(t){return t.split(".").pop()}function x(t){let e=t.replace("."+y(t),"");return 2==e.split("/").length?e.replace(d,""):e}let b={},v={},j={};function A(){window.__k||(window.__k={dm:f,u:_,d:j,i:b})}function S(t){return A(),new Promise((e,i)=>{let s,n,r;if(s=w(g,t),b[s])return e(b[s]);(n=new Image).onload=function(){r=_(s,window.location.href),b[x(t)]=b[s]=b[r]=this,h("assetLoaded",this,t),e(this)},n.onerror=function(){i(s)},n.src=s})}function O(t){return new Promise((e,i)=>{let s,n,r,o;return s=new Audio,n=function(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}(s),(t=[].concat(t).reduce((t,e)=>t||(n[y(e)]?e:null),0))?(r=w(p,t),v[r]?e(v[r]):(s.addEventListener("canplay",function(){o=_(r,window.location.href),v[x(t)]=v[r]=v[o]=this,h("assetLoaded",this,t),e(this)}),s.onerror=function(){i(r)},s.src=r,void s.load())):i(t)})}function P(t){let e,i;return A(),e=w(m,t),j[e]?Promise.resolve(j[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=_(e,window.location.href),"object"==typeof s&&f.set(s,i),j[x(t)]=j[e]=j[i]=s,h("assetLoaded",s,t),s))}const E=()=>{};function k(){let t=n();r().clearRect(0,0,t.width,t.height)}let L={},M={},I={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function z(t){let e=I[t.which];M[e]=!0,L[e]&&L[e](t)}function D(t){M[I[t.which]]=!1}function R(){M={}}function W(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function C(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}let T=[],N=[],X={},Y=[],U={},q={0:"left",1:"middle",2:"right"},F={x:0,y:0,radius:5};function K(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=F.x-Math.max(e,Math.min(F.x,e+t.width)),h=F.y-Math.max(i,Math.min(F.y,i+t.height));return s*s+h*h=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(F):K(t))return t}function B(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!0,Q(t,"onDown")}function H(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!1,Q(t,"onUp")}function J(t){Q(t,"onOver")}function G(){U={}}function Q(t,e){let i,s,h=n();if(!h)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let r=h.height/h.offsetHeight,o=h.getBoundingClientRect(),a=(i-o.left)*r,c=(s-o.top)*r;F.x=a,F.y=c,t.preventDefault();let l=$();l&&l[e]&&l[e](t),X[e]&&X[e](t,l)}class V{constructor({create:t,maxSize:e=1024}={}){this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function Z(t){return new V(t)}function tt(t,e){let i=[],s=e.x+e.width/2,h=e.y+e.height/2,n=t.y=e.y,r=t.y+t.height>=h&&t.y=e.x&&(n&&i.push(0),r&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1}}function it(t){return new et(t)}it.prototype=et.prototype,it.class=et;class st{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return ht(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function ht(t,e,i={}){let s=new st(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}ht.prototype=st.prototype,ht.class=st;class nt{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:h,ddx:n,ddy:o,width:a,height:c,image:l}=t;this.position=ht(e,i),this.velocity=ht(s,h),this.acceleration=ht(n,o),this._fx=this._fy=1,this.width=this.height=this.rotation=0,this.ttl=1/0,this.anchor={x:0,y:0},this.context=r();for(let e in t)this[e]=t[e];l&&(this.width=void 0!==a?a:l.width,this.height=void 0!==c?c:l.height),this.sx=0,this.sy=0}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}get width(){return this._w}get height(){return this._h}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}set width(t){let e=t<0?-1:1;this._fx=e,this._w=t*e}set height(t){let e=t<0?-1:1;this._fy=e,this._h=t*e}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,h=t.y;return t.anchor&&(s-=t.width*t.anchor.x,h-=t.height*t.anchor.y),es&&ih}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;if(this.context.save(),this.context.translate(this.viewX,this.viewY),this.rotation&&this.context.rotate(this.rotation),-1==this._fx||-1==this._fy){let i=this.width/2+t,s=this.height/2+e;this.context.translate(i,s),this.context.scale(this._fx,this._fy),this.context.translate(-i,-s)}this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function rt(t){return new nt(t)}function ot(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],h=+i[1],n=s;if(s=h;n--)e.push(n);return e}rt.prototype=nt.prototype,rt.class=nt;class at{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:h}={}){this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(h)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:h,loop:n}=t[i];e=[],[].concat(s).map(t=>{e=e.concat(ot(t))}),this.animations[i]=a({spriteSheet:this,frames:e,frameRate:h,loop:n})}}}function ct(t){return new at(t)}return ct.prototype=at.prototype,ct.class=at,{Animation:a,imageAssets:b,audioAssets:v,dataAssets:j,setImagePath:function(t){g=t},setAudioPath:function(t){p=t},setDataPath:function(t){m=t},loadImage:S,loadAudio:O,loadData:P,load:function(...t){return A(),Promise.all(t.map(t=>{let e=y([].concat(t)[0]);return e.match(c)?S(t):e.match(l)?O(t):P(t)}))},init:function(i){return t=document.getElementById(i)||i||document.querySelector("canvas"),(e=t.getContext("2d")).imageSmoothingEnabled=!1,h("init"),{canvas:t,context:e}},getCanvas:n,getContext:r,on:s,off:function(t,e){let s;!i[t]||(s=i[t].indexOf(e))<0||i[t].splice(s,1)},emit:h,GameLoop:function({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){let n,r,o,a,c,l=0,d=1e3/t,u=1/t,f=e?k:E;function g(){if(r=requestAnimationFrame(g),o=performance.now(),a=o-n,n=o,!(a>1e3)){for(h("tick"),l+=a;l>=d;)c.update(u),l-=d;f(),c.render()}}return c={update:i,render:s,isStopped:!0,start(){n=performance.now(),this.isStopped=!1,requestAnimationFrame(g)},stop(){this.isStopped=!0,cancelAnimationFrame(r)}}},keyMap:I,initKeys:function(){let t;for(t=0;t<26;t++)I[65+t]=(10+t).toString(36);for(t=0;t<10;t++)I[48+t]=""+t;window.addEventListener("keydown",z),window.addEventListener("keyup",D),window.addEventListener("blur",R)},bindKeys:function(t,e){[].concat(t).map(t=>L[t]=e)},unbindKeys:function(t){[].concat(t).map(t=>L[t]=0)},keyPressed:function(t){return!!M[t]},registerPlugin:function(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let h=i(t,e,...s);return h||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),h=i["_o"+s].call(this,...e);return this._aInc(this,s,h,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))},unregisterPlugin:function(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);t.startsWith("before")?C(i._inc[s].before,e[t]):t.startsWith("after")&&C(i._inc[s].after,e[t])})},extendObject:function(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})},initPointer:function(){let t=n();t.addEventListener("mousedown",B),t.addEventListener("touchstart",B),t.addEventListener("mouseup",H),t.addEventListener("touchend",H),t.addEventListener("blur",G),t.addEventListener("mousemove",J),t.addEventListener("touchmove",J),s("tick",()=>{N.length=0,T.map(t=>{N.push(t)}),T.length=0})},pointer:F,track:function(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){T.push(this),this._r()},Y.push(t))})},untrack:function(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=Y.indexOf(t);-1!==e&&Y.splice(e,1)})},pointerOver:function(t){return!!Y.includes(t)&&$()===t},onPointerDown:function(t){X.onDown=t},onPointerUp:function(t){X.onUp=t},pointerPressed:function(t){return!!U[t]},Pool:Z,Quadtree:it,Sprite:rt,SpriteSheet:ct,setStoreItem:function(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))},getStoreItem:function(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e},TileEngine:function(t={}){let{width:e,height:i,tilewidth:s,tileheight:h,context:o=r(),tilesets:a,layers:c}=t,l=e*s,d=i*h,u=document.createElement("canvas"),f=u.getContext("2d");u.width=l,u.height=d;let g={},p={},m=[],_=Object.assign({context:o,mapwidth:l,mapheight:d,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),l-n().width),m.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),d-n().height),m.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),b(u)},renderLayer(t){let e=p[t],i=g[t];e||((e=document.createElement("canvas")).width=l,e.height=d,p[t]=e,_._r(i,e.getContext("2d"))),b(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let h=w(s),n=y(i),r=w(s+e.height),o=y(i+e.width),a=g[t];for(let t=h;t<=r;t++)for(let e=n;e<=o;e++)if(a.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||w(e.y),s=e.col||y(e.x);return g[t]?g[t].data[s+i*_.width]:-1},setTileAtLayer(t,e,i){let s=e.row||w(e.y),h=e.col||y(e.x);g[t]&&(this._d=!0,g[t].data[h+s*_.width]=i)},addObject(t){m.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=m.indexOf(t);-1!==e&&(m.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=_.tilesets.length-1;e>=0&&(s=_.tilesets[e],!(t/s.firstgid>=1));e--);let h=s.tilewidth||_.tilewidth,n=s.tileheight||_.tileheight,r=s.margin||0,o=s.image,a=t-s.firstgid,c=s.columns||o.width/(h+r)|0,l=i%_.width*h,d=(i/_.width|0)*n,u=a%c*(h+r),f=(a/c|0)*(n+r);e.drawImage(o,u,f,h,n,l,d,h,n)}),e.restore()},_p:x},t);function w(t){return t/_.tileheight|0}function y(t){return t/_.tilewidth|0}function x(){_.layers&&_.layers.map(t=>{g[t.name]=t,!1!==t.visible&&_._r(t,f)})}function b(t){const{width:e,height:i}=n(),s=Math.min(t.width,e),h=Math.min(t.height,i);_.context.drawImage(t,_.sx,_.sy,s,h,0,0,s,h)}return _.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){let t=window.__k.d[window.__k.u(e.source,i)];Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){let t=window.__k.i[window.__k.u(e.image,i)];e.image=t}}),x(),_},Vector:ht}}(); \ No newline at end of file diff --git a/kontra.js b/kontra.js index 488e8286..75752f81 100644 --- a/kontra.js +++ b/kontra.js @@ -2686,12 +2686,16 @@ class Sprite { } set width(value) { - this._fx = value < 0 ? -1 : 1; - this._w = Math.abs(value); + let sign = value < 0 ? -1 : 1; + + this._fx = sign; + this._w = value * sign; } set height(value) { - this._fy = value < 0 ? -1 : 1; - this._h = Math.abs(value); + let sign = value < 0 ? -1 : 1; + + this._fy = sign; + this._h = value * sign; } /** diff --git a/kontra.min.js b/kontra.min.js index 2c47f113..88f4a2f6 100644 --- a/kontra.min.js +++ b/kontra.min.js @@ -1 +1 @@ -var kontra=function(){"use strict";let t,e,i={};function s(t,e){i[t]=i[t]||[],i[t].push(e)}function h(t,...e){i[t]&&i[t].map(t=>t(...e))}function n(){return t}function r(){return e}class o{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:h,height:n,margin:r=0}=t.frame;this.width=h,this.height=n,this.margin=r,this._f=0,this._a=0}clone(){return a(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:h=r()}={}){let n=this.frames[this._f]/this.spriteSheet._f|0,o=this.frames[this._f]%this.spriteSheet._f|0;h.drawImage(this.spriteSheet.image,o*this.width+(2*o+1)*this.margin,n*this.height+(2*n+1)*this.margin,this.width,this.height,t,e,i,s)}}function a(t){return new o(t)}a.prototype=o.prototype,a.class=o;let c=/(jpeg|jpg|gif|png)$/,l=/(wav|mp3|ogg|aac)$/,d=/^\//,u=/\/$/,f=new WeakMap,g="",p="",m="";function _(t,e){return new URL(t,e).href}function w(t,e){return[t.replace(u,""),t?e.replace(d,""):e].filter(t=>t).join("/")}function y(t){return t.split(".").pop()}function x(t){let e=t.replace("."+y(t),"");return 2==e.split("/").length?e.replace(d,""):e}let b={},v={},j={};function A(){window.__k||(window.__k={dm:f,u:_,d:j,i:b})}function S(t){return A(),new Promise((e,i)=>{let s,n,r;if(s=w(g,t),b[s])return e(b[s]);(n=new Image).onload=function(){r=_(s,window.location.href),b[x(t)]=b[s]=b[r]=this,h("assetLoaded",this,t),e(this)},n.onerror=function(){i(s)},n.src=s})}function O(t){return new Promise((e,i)=>{let s,n,r,o;return s=new Audio,n=function(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}(s),(t=[].concat(t).reduce((t,e)=>t||(n[y(e)]?e:null),0))?(r=w(p,t),v[r]?e(v[r]):(s.addEventListener("canplay",function(){o=_(r,window.location.href),v[x(t)]=v[r]=v[o]=this,h("assetLoaded",this,t),e(this)}),s.onerror=function(){i(r)},s.src=r,void s.load())):i(t)})}function P(t){let e,i;return A(),e=w(m,t),j[e]?Promise.resolve(j[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=_(e,window.location.href),"object"==typeof s&&f.set(s,i),j[x(t)]=j[e]=j[i]=s,h("assetLoaded",s,t),s))}const E=()=>{};function M(){let t=n();r().clearRect(0,0,t.width,t.height)}let k={},L={},I={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function z(t){let e=I[t.which];L[e]=!0,k[e]&&k[e](t)}function D(t){L[I[t.which]]=!1}function R(){L={}}function W(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function C(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}let T=[],N=[],X={},Y=[],U={},q={0:"left",1:"middle",2:"right"},F={x:0,y:0,radius:5};function K(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=F.x-Math.max(e,Math.min(F.x,e+t.width)),h=F.y-Math.max(i,Math.min(F.y,i+t.height));return s*s+h*h=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(F):K(t))return t}function B(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!0,Q(t,"onDown")}function H(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!1,Q(t,"onUp")}function J(t){Q(t,"onOver")}function G(){U={}}function Q(t,e){let i,s,h=n();if(!h)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let r=h.height/h.offsetHeight,o=h.getBoundingClientRect(),a=(i-o.left)*r,c=(s-o.top)*r;F.x=a,F.y=c,t.preventDefault();let l=$();l&&l[e]&&l[e](t),X[e]&&X[e](t,l)}class V{constructor({create:t,maxSize:e=1024}={}){this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function Z(t){return new V(t)}function tt(t,e){let i=[],s=e.x+e.width/2,h=e.y+e.height/2,n=t.y=e.y,r=t.y+t.height>=h&&t.y=e.x&&(n&&i.push(0),r&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1}}function it(t){return new et(t)}it.prototype=et.prototype,it.class=et;class st{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return ht(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function ht(t,e,i={}){let s=new st(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}ht.prototype=st.prototype,ht.class=st;class nt{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:h,ddx:n,ddy:o,width:a,height:c,image:l}=t;this.position=ht(e,i),this.velocity=ht(s,h),this.acceleration=ht(n,o),this._fx=this._fy=1,this.width=this.height=this.rotation=0,this.ttl=1/0,this.anchor={x:0,y:0},this.context=r();for(let e in t)this[e]=t[e];l&&(this.width=void 0!==a?a:l.width,this.height=void 0!==c?c:l.height),this.sx=0,this.sy=0}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}get width(){return this._w}get height(){return this._h}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}set width(t){this._fx=t<0?-1:1,this._w=Math.abs(t)}set height(t){this._fy=t<0?-1:1,this._h=Math.abs(t)}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,h=t.y;return t.anchor&&(s-=t.width*t.anchor.x,h-=t.height*t.anchor.y),es&&ih}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;if(this.context.save(),this.context.translate(this.viewX,this.viewY),this.rotation&&this.context.rotate(this.rotation),-1==this._fx||-1==this._fy){let i=this.width/2+t,s=this.height/2+e;this.context.translate(i,s),this.context.scale(this._fx,this._fy),this.context.translate(-i,-s)}this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function rt(t){return new nt(t)}function ot(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],h=+i[1],n=s;if(s=h;n--)e.push(n);return e}rt.prototype=nt.prototype,rt.class=nt;class at{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:h}={}){this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(h)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:h,loop:n}=t[i];e=[],[].concat(s).map(t=>{e=e.concat(ot(t))}),this.animations[i]=a({spriteSheet:this,frames:e,frameRate:h,loop:n})}}}function ct(t){return new at(t)}return ct.prototype=at.prototype,ct.class=at,{Animation:a,imageAssets:b,audioAssets:v,dataAssets:j,setImagePath:function(t){g=t},setAudioPath:function(t){p=t},setDataPath:function(t){m=t},loadImage:S,loadAudio:O,loadData:P,load:function(...t){return A(),Promise.all(t.map(t=>{let e=y([].concat(t)[0]);return e.match(c)?S(t):e.match(l)?O(t):P(t)}))},init:function(i){return t=document.getElementById(i)||i||document.querySelector("canvas"),(e=t.getContext("2d")).imageSmoothingEnabled=!1,h("init"),{canvas:t,context:e}},getCanvas:n,getContext:r,on:s,off:function(t,e){let s;!i[t]||(s=i[t].indexOf(e))<0||i[t].splice(s,1)},emit:h,GameLoop:function({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){let n,r,o,a,c,l=0,d=1e3/t,u=1/t,f=e?M:E;function g(){if(r=requestAnimationFrame(g),o=performance.now(),a=o-n,n=o,!(a>1e3)){for(h("tick"),l+=a;l>=d;)c.update(u),l-=d;f(),c.render()}}return c={update:i,render:s,isStopped:!0,start(){n=performance.now(),this.isStopped=!1,requestAnimationFrame(g)},stop(){this.isStopped=!0,cancelAnimationFrame(r)}}},keyMap:I,initKeys:function(){let t;for(t=0;t<26;t++)I[65+t]=(10+t).toString(36);for(t=0;t<10;t++)I[48+t]=""+t;window.addEventListener("keydown",z),window.addEventListener("keyup",D),window.addEventListener("blur",R)},bindKeys:function(t,e){[].concat(t).map(t=>k[t]=e)},unbindKeys:function(t){[].concat(t).map(t=>k[t]=0)},keyPressed:function(t){return!!L[t]},registerPlugin:function(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let h=i(t,e,...s);return h||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),h=i["_o"+s].call(this,...e);return this._aInc(this,s,h,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))},unregisterPlugin:function(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);t.startsWith("before")?C(i._inc[s].before,e[t]):t.startsWith("after")&&C(i._inc[s].after,e[t])})},extendObject:function(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})},initPointer:function(){let t=n();t.addEventListener("mousedown",B),t.addEventListener("touchstart",B),t.addEventListener("mouseup",H),t.addEventListener("touchend",H),t.addEventListener("blur",G),t.addEventListener("mousemove",J),t.addEventListener("touchmove",J),s("tick",()=>{N.length=0,T.map(t=>{N.push(t)}),T.length=0})},pointer:F,track:function(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){T.push(this),this._r()},Y.push(t))})},untrack:function(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=Y.indexOf(t);-1!==e&&Y.splice(e,1)})},pointerOver:function(t){return!!Y.includes(t)&&$()===t},onPointerDown:function(t){X.onDown=t},onPointerUp:function(t){X.onUp=t},pointerPressed:function(t){return!!U[t]},Pool:Z,Quadtree:it,Sprite:rt,SpriteSheet:ct,setStoreItem:function(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))},getStoreItem:function(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e},TileEngine:function(t={}){let{width:e,height:i,tilewidth:s,tileheight:h,context:o=r(),tilesets:a,layers:c}=t,l=e*s,d=i*h,u=document.createElement("canvas"),f=u.getContext("2d");u.width=l,u.height=d;let g={},p={},m=[],_=Object.assign({context:o,mapwidth:l,mapheight:d,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),l-n().width),m.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),d-n().height),m.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),b(u)},renderLayer(t){let e=p[t],i=g[t];e||((e=document.createElement("canvas")).width=l,e.height=d,p[t]=e,_._r(i,e.getContext("2d"))),b(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let h=w(s),n=y(i),r=w(s+e.height),o=y(i+e.width),a=g[t];for(let t=h;t<=r;t++)for(let e=n;e<=o;e++)if(a.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||w(e.y),s=e.col||y(e.x);return g[t]?g[t].data[s+i*_.width]:-1},setTileAtLayer(t,e,i){let s=e.row||w(e.y),h=e.col||y(e.x);g[t]&&(this._d=!0,g[t].data[h+s*_.width]=i)},addObject(t){m.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=m.indexOf(t);-1!==e&&(m.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=_.tilesets.length-1;e>=0&&(s=_.tilesets[e],!(t/s.firstgid>=1));e--);let h=s.tilewidth||_.tilewidth,n=s.tileheight||_.tileheight,r=s.margin||0,o=s.image,a=t-s.firstgid,c=s.columns||o.width/(h+r)|0,l=i%_.width*h,d=(i/_.width|0)*n,u=a%c*(h+r),f=(a/c|0)*(n+r);e.drawImage(o,u,f,h,n,l,d,h,n)}),e.restore()},_p:x},t);function w(t){return t/_.tileheight|0}function y(t){return t/_.tilewidth|0}function x(){_.layers&&_.layers.map(t=>{g[t.name]=t,!1!==t.visible&&_._r(t,f)})}function b(t){const{width:e,height:i}=n(),s=Math.min(t.width,e),h=Math.min(t.height,i);_.context.drawImage(t,_.sx,_.sy,s,h,0,0,s,h)}return _.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){let t=window.__k.d[window.__k.u(e.source,i)];Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){let t=window.__k.i[window.__k.u(e.image,i)];e.image=t}}),x(),_},Vector:ht}}(); \ No newline at end of file +var kontra=function(){"use strict";let t,e,i={};function s(t,e){i[t]=i[t]||[],i[t].push(e)}function h(t,...e){i[t]&&i[t].map(t=>t(...e))}function n(){return t}function r(){return e}class o{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:h,height:n,margin:r=0}=t.frame;this.width=h,this.height=n,this.margin=r,this._f=0,this._a=0}clone(){return a(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:h=r()}={}){let n=this.frames[this._f]/this.spriteSheet._f|0,o=this.frames[this._f]%this.spriteSheet._f|0;h.drawImage(this.spriteSheet.image,o*this.width+(2*o+1)*this.margin,n*this.height+(2*n+1)*this.margin,this.width,this.height,t,e,i,s)}}function a(t){return new o(t)}a.prototype=o.prototype,a.class=o;let c=/(jpeg|jpg|gif|png)$/,l=/(wav|mp3|ogg|aac)$/,d=/^\//,u=/\/$/,f=new WeakMap,g="",p="",m="";function _(t,e){return new URL(t,e).href}function w(t,e){return[t.replace(u,""),t?e.replace(d,""):e].filter(t=>t).join("/")}function y(t){return t.split(".").pop()}function x(t){let e=t.replace("."+y(t),"");return 2==e.split("/").length?e.replace(d,""):e}let b={},v={},j={};function A(){window.__k||(window.__k={dm:f,u:_,d:j,i:b})}function S(t){return A(),new Promise((e,i)=>{let s,n,r;if(s=w(g,t),b[s])return e(b[s]);(n=new Image).onload=function(){r=_(s,window.location.href),b[x(t)]=b[s]=b[r]=this,h("assetLoaded",this,t),e(this)},n.onerror=function(){i(s)},n.src=s})}function O(t){return new Promise((e,i)=>{let s,n,r,o;return s=new Audio,n=function(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}(s),(t=[].concat(t).reduce((t,e)=>t||(n[y(e)]?e:null),0))?(r=w(p,t),v[r]?e(v[r]):(s.addEventListener("canplay",function(){o=_(r,window.location.href),v[x(t)]=v[r]=v[o]=this,h("assetLoaded",this,t),e(this)}),s.onerror=function(){i(r)},s.src=r,void s.load())):i(t)})}function P(t){let e,i;return A(),e=w(m,t),j[e]?Promise.resolve(j[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=_(e,window.location.href),"object"==typeof s&&f.set(s,i),j[x(t)]=j[e]=j[i]=s,h("assetLoaded",s,t),s))}const E=()=>{};function k(){let t=n();r().clearRect(0,0,t.width,t.height)}let L={},M={},I={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function z(t){let e=I[t.which];M[e]=!0,L[e]&&L[e](t)}function D(t){M[I[t.which]]=!1}function R(){M={}}function W(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function C(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}let T=[],N=[],X={},Y=[],U={},q={0:"left",1:"middle",2:"right"},F={x:0,y:0,radius:5};function K(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=F.x-Math.max(e,Math.min(F.x,e+t.width)),h=F.y-Math.max(i,Math.min(F.y,i+t.height));return s*s+h*h=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(F):K(t))return t}function B(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!0,Q(t,"onDown")}function H(t){let e=void 0!==t.button?q[t.button]:"left";U[e]=!1,Q(t,"onUp")}function J(t){Q(t,"onOver")}function G(){U={}}function Q(t,e){let i,s,h=n();if(!h)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let r=h.height/h.offsetHeight,o=h.getBoundingClientRect(),a=(i-o.left)*r,c=(s-o.top)*r;F.x=a,F.y=c,t.preventDefault();let l=$();l&&l[e]&&l[e](t),X[e]&&X[e](t,l)}class V{constructor({create:t,maxSize:e=1024}={}){this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function Z(t){return new V(t)}function tt(t,e){let i=[],s=e.x+e.width/2,h=e.y+e.height/2,n=t.y=e.y,r=t.y+t.height>=h&&t.y=e.x&&(n&&i.push(0),r&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1}}function it(t){return new et(t)}it.prototype=et.prototype,it.class=et;class st{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return ht(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function ht(t,e,i={}){let s=new st(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}ht.prototype=st.prototype,ht.class=st;class nt{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:h,ddx:n,ddy:o,width:a,height:c,image:l}=t;this.position=ht(e,i),this.velocity=ht(s,h),this.acceleration=ht(n,o),this._fx=this._fy=1,this.width=this.height=this.rotation=0,this.ttl=1/0,this.anchor={x:0,y:0},this.context=r();for(let e in t)this[e]=t[e];l&&(this.width=void 0!==a?a:l.width,this.height=void 0!==c?c:l.height),this.sx=0,this.sy=0}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}get width(){return this._w}get height(){return this._h}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}set width(t){let e=t<0?-1:1;this._fx=e,this._w=t*e}set height(t){let e=t<0?-1:1;this._fy=e,this._h=t*e}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,h=t.y;return t.anchor&&(s-=t.width*t.anchor.x,h-=t.height*t.anchor.y),es&&ih}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;if(this.context.save(),this.context.translate(this.viewX,this.viewY),this.rotation&&this.context.rotate(this.rotation),-1==this._fx||-1==this._fy){let i=this.width/2+t,s=this.height/2+e;this.context.translate(i,s),this.context.scale(this._fx,this._fy),this.context.translate(-i,-s)}this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function rt(t){return new nt(t)}function ot(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],h=+i[1],n=s;if(s=h;n--)e.push(n);return e}rt.prototype=nt.prototype,rt.class=nt;class at{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:h}={}){this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(h)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:h,loop:n}=t[i];e=[],[].concat(s).map(t=>{e=e.concat(ot(t))}),this.animations[i]=a({spriteSheet:this,frames:e,frameRate:h,loop:n})}}}function ct(t){return new at(t)}return ct.prototype=at.prototype,ct.class=at,{Animation:a,imageAssets:b,audioAssets:v,dataAssets:j,setImagePath:function(t){g=t},setAudioPath:function(t){p=t},setDataPath:function(t){m=t},loadImage:S,loadAudio:O,loadData:P,load:function(...t){return A(),Promise.all(t.map(t=>{let e=y([].concat(t)[0]);return e.match(c)?S(t):e.match(l)?O(t):P(t)}))},init:function(i){return t=document.getElementById(i)||i||document.querySelector("canvas"),(e=t.getContext("2d")).imageSmoothingEnabled=!1,h("init"),{canvas:t,context:e}},getCanvas:n,getContext:r,on:s,off:function(t,e){let s;!i[t]||(s=i[t].indexOf(e))<0||i[t].splice(s,1)},emit:h,GameLoop:function({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){let n,r,o,a,c,l=0,d=1e3/t,u=1/t,f=e?k:E;function g(){if(r=requestAnimationFrame(g),o=performance.now(),a=o-n,n=o,!(a>1e3)){for(h("tick"),l+=a;l>=d;)c.update(u),l-=d;f(),c.render()}}return c={update:i,render:s,isStopped:!0,start(){n=performance.now(),this.isStopped=!1,requestAnimationFrame(g)},stop(){this.isStopped=!0,cancelAnimationFrame(r)}}},keyMap:I,initKeys:function(){let t;for(t=0;t<26;t++)I[65+t]=(10+t).toString(36);for(t=0;t<10;t++)I[48+t]=""+t;window.addEventListener("keydown",z),window.addEventListener("keyup",D),window.addEventListener("blur",R)},bindKeys:function(t,e){[].concat(t).map(t=>L[t]=e)},unbindKeys:function(t){[].concat(t).map(t=>L[t]=0)},keyPressed:function(t){return!!M[t]},registerPlugin:function(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let h=i(t,e,...s);return h||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),h=i["_o"+s].call(this,...e);return this._aInc(this,s,h,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))},unregisterPlugin:function(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=W(t);t.startsWith("before")?C(i._inc[s].before,e[t]):t.startsWith("after")&&C(i._inc[s].after,e[t])})},extendObject:function(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})},initPointer:function(){let t=n();t.addEventListener("mousedown",B),t.addEventListener("touchstart",B),t.addEventListener("mouseup",H),t.addEventListener("touchend",H),t.addEventListener("blur",G),t.addEventListener("mousemove",J),t.addEventListener("touchmove",J),s("tick",()=>{N.length=0,T.map(t=>{N.push(t)}),T.length=0})},pointer:F,track:function(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){T.push(this),this._r()},Y.push(t))})},untrack:function(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=Y.indexOf(t);-1!==e&&Y.splice(e,1)})},pointerOver:function(t){return!!Y.includes(t)&&$()===t},onPointerDown:function(t){X.onDown=t},onPointerUp:function(t){X.onUp=t},pointerPressed:function(t){return!!U[t]},Pool:Z,Quadtree:it,Sprite:rt,SpriteSheet:ct,setStoreItem:function(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))},getStoreItem:function(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e},TileEngine:function(t={}){let{width:e,height:i,tilewidth:s,tileheight:h,context:o=r(),tilesets:a,layers:c}=t,l=e*s,d=i*h,u=document.createElement("canvas"),f=u.getContext("2d");u.width=l,u.height=d;let g={},p={},m=[],_=Object.assign({context:o,mapwidth:l,mapheight:d,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),l-n().width),m.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),d-n().height),m.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),b(u)},renderLayer(t){let e=p[t],i=g[t];e||((e=document.createElement("canvas")).width=l,e.height=d,p[t]=e,_._r(i,e.getContext("2d"))),b(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let h=w(s),n=y(i),r=w(s+e.height),o=y(i+e.width),a=g[t];for(let t=h;t<=r;t++)for(let e=n;e<=o;e++)if(a.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||w(e.y),s=e.col||y(e.x);return g[t]?g[t].data[s+i*_.width]:-1},setTileAtLayer(t,e,i){let s=e.row||w(e.y),h=e.col||y(e.x);g[t]&&(this._d=!0,g[t].data[h+s*_.width]=i)},addObject(t){m.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=m.indexOf(t);-1!==e&&(m.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=_.tilesets.length-1;e>=0&&(s=_.tilesets[e],!(t/s.firstgid>=1));e--);let h=s.tilewidth||_.tilewidth,n=s.tileheight||_.tileheight,r=s.margin||0,o=s.image,a=t-s.firstgid,c=s.columns||o.width/(h+r)|0,l=i%_.width*h,d=(i/_.width|0)*n,u=a%c*(h+r),f=(a/c|0)*(n+r);e.drawImage(o,u,f,h,n,l,d,h,n)}),e.restore()},_p:x},t);function w(t){return t/_.tileheight|0}function y(t){return t/_.tilewidth|0}function x(){_.layers&&_.layers.map(t=>{g[t.name]=t,!1!==t.visible&&_._r(t,f)})}function b(t){const{width:e,height:i}=n(),s=Math.min(t.width,e),h=Math.min(t.height,i);_.context.drawImage(t,_.sx,_.sy,s,h,0,0,s,h)}return _.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){let t=window.__k.d[window.__k.u(e.source,i)];Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){let t=window.__k.i[window.__k.u(e.image,i)];e.image=t}}),x(),_},Vector:ht}}(); \ No newline at end of file diff --git a/kontra.min.mjs b/kontra.min.mjs index 2bb43dfc..dad781ae 100644 --- a/kontra.min.mjs +++ b/kontra.min.mjs @@ -1 +1 @@ -let canvasEl,context,callbacks={};function on(t,e){callbacks[t]=callbacks[t]||[],callbacks[t].push(e)}function off(t,e){let i;!callbacks[t]||(i=callbacks[t].indexOf(e))<0||callbacks[t].splice(i,1)}function emit(t,...e){callbacks[t]&&callbacks[t].map(t=>t(...e))}function getCanvas(){return canvasEl}function getContext(){return context}function init(t){if(!(canvasEl=document.getElementById(t)||t||document.querySelector("canvas")))throw Error("You must provide a canvas element for the game");return(context=canvasEl.getContext("2d")).imageSmoothingEnabled=!1,emit("init"),{canvas:canvasEl,context:context}}class Animation{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:n,height:a,margin:r=0}=t.frame;this.width=n,this.height=a,this.margin=r,this._f=0,this._a=0}clone(){return animationFactory(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:n=getContext()}={}){let a=this.frames[this._f]/this.spriteSheet._f|0,r=this.frames[this._f]%this.spriteSheet._f|0;n.drawImage(this.spriteSheet.image,r*this.width+(2*r+1)*this.margin,a*this.height+(2*a+1)*this.margin,this.width,this.height,t,e,i,s)}}function animationFactory(t){return new Animation(t)}animationFactory.prototype=Animation.prototype,animationFactory.class=Animation;let imageRegex=/(jpeg|jpg|gif|png)$/,audioRegex=/(wav|mp3|ogg|aac)$/,leadingSlash=/^\//,trailingSlash=/\/$/,dataMap=new WeakMap,imagePath="",audioPath="",dataPath="";function getUrl(t,e){return new URL(t,e).href}function joinPath(t,e){return[t.replace(trailingSlash,""),t?e.replace(leadingSlash,""):e].filter(t=>t).join("/")}function getExtension(t){return t.split(".").pop()}function getName(t){let e=t.replace("."+getExtension(t),"");return 2==e.split("/").length?e.replace(leadingSlash,""):e}function getCanPlay(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}let imageAssets={},audioAssets={},dataAssets={};function addGlobal(){window.__k||(window.__k={dm:dataMap,u:getUrl,d:dataAssets,i:imageAssets})}function setImagePath(t){imagePath=t}function setAudioPath(t){audioPath=t}function setDataPath(t){dataPath=t}function loadImage(t){return addGlobal(),new Promise((e,i)=>{let s,n,a;if(s=joinPath(imagePath,t),imageAssets[s])return e(imageAssets[s]);(n=new Image).onload=function(){a=getUrl(s,window.location.href),imageAssets[getName(t)]=imageAssets[s]=imageAssets[a]=this,emit("assetLoaded",this,t),e(this)},n.onerror=function(){i("Unable to load image "+s)},n.src=s})}function loadAudio(t){return new Promise((e,i)=>{let s,n,a,r;return s=new Audio,n=getCanPlay(s),(t=[].concat(t).reduce((t,e)=>t||(n[getExtension(e)]?e:null),0))?(a=joinPath(audioPath,t),audioAssets[a]?e(audioAssets[a]):(s.addEventListener("canplay",function(){r=getUrl(a,window.location.href),audioAssets[getName(t)]=audioAssets[a]=audioAssets[r]=this,emit("assetLoaded",this,t),e(this)}),s.onerror=function(){i("Unable to load audio "+a)},s.src=a,void s.load())):i("cannot play any of the audio formats provided"+t)})}function loadData(t){let e,i;return addGlobal(),e=joinPath(dataPath,t),dataAssets[e]?Promise.resolve(dataAssets[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=getUrl(e,window.location.href),"object"==typeof s&&dataMap.set(s,i),dataAssets[getName(t)]=dataAssets[e]=dataAssets[i]=s,emit("assetLoaded",s,t),s))}function load(...t){return addGlobal(),Promise.all(t.map(t=>{let e=getExtension([].concat(t)[0]);return e.match(imageRegex)?loadImage(t):e.match(audioRegex)?loadAudio(t):loadData(t)}))}const noop=()=>{};function clear(){let t=getCanvas();getContext().clearRect(0,0,t.width,t.height)}function GameLoop({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){if(!i||!s)throw Error("You must provide update() and render() functions");let n,a,r,o,h,c=0,d=1e3/t,l=1/t,u=e?clear:noop;function p(){if(a=requestAnimationFrame(p),r=performance.now(),o=r-n,n=r,!(o>1e3)){for(emit("tick"),c+=o;c>=d;)h.update(l),c-=d;u(),h.render()}}return h={update:i,render:s,isStopped:!0,start(){n=performance.now(),this.isStopped=!1,requestAnimationFrame(p)},stop(){this.isStopped=!0,cancelAnimationFrame(a)},_frame:p,set _last(t){n=t}}}let callbacks$1={},pressedKeys={},keyMap={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function keydownEventHandler(t){let e=keyMap[t.which];pressedKeys[e]=!0,callbacks$1[e]&&callbacks$1[e](t)}function keyupEventHandler(t){pressedKeys[keyMap[t.which]]=!1}function blurEventHandler(){pressedKeys={}}function initKeys(){let t;for(t=0;t<26;t++)keyMap[65+t]=(10+t).toString(36);for(t=0;t<10;t++)keyMap[48+t]=""+t;window.addEventListener("keydown",keydownEventHandler),window.addEventListener("keyup",keyupEventHandler),window.addEventListener("blur",blurEventHandler)}function bindKeys(t,e){[].concat(t).map(t=>callbacks$1[t]=e)}function unbindKeys(t){[].concat(t).map(t=>callbacks$1[t]=0)}function keyPressed(t){return!!pressedKeys[t]}function getMethod(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function removeInterceptor(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}function registerPlugin(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let n=i(t,e,...s);return n||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=getMethod(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),n=i["_o"+s].call(this,...e);return this._aInc(this,s,n,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))}function unregisterPlugin(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=getMethod(t);t.startsWith("before")?removeInterceptor(i._inc[s].before,e[t]):t.startsWith("after")&&removeInterceptor(i._inc[s].after,e[t])})}function extendObject(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})}let thisFrameRenderOrder=[],lastFrameRenderOrder=[],callbacks$2={},trackedObjects=[],pressedButtons={},buttonMap={0:"left",1:"middle",2:"right"},pointer={x:0,y:0,radius:5};function circleRectCollision(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=pointer.x-Math.max(e,Math.min(pointer.x,e+t.width)),n=pointer.y-Math.max(i,Math.min(pointer.y,i+t.height));return s*s+n*n=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(pointer):circleRectCollision(t))return t}function pointerDownHandler(t){let e=void 0!==t.button?buttonMap[t.button]:"left";pressedButtons[e]=!0,pointerHandler(t,"onDown")}function pointerUpHandler(t){let e=void 0!==t.button?buttonMap[t.button]:"left";pressedButtons[e]=!1,pointerHandler(t,"onUp")}function mouseMoveHandler(t){pointerHandler(t,"onOver")}function blurEventHandler$1(){pressedButtons={}}function pointerHandler(t,e){let i,s,n=getCanvas();if(!n)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let a=n.height/n.offsetHeight,r=n.getBoundingClientRect(),o=(i-r.left)*a,h=(s-r.top)*a;pointer.x=o,pointer.y=h,t.preventDefault();let c=getCurrentObject();c&&c[e]&&c[e](t),callbacks$2[e]&&callbacks$2[e](t,c)}function initPointer(){let t=getCanvas();t.addEventListener("mousedown",pointerDownHandler),t.addEventListener("touchstart",pointerDownHandler),t.addEventListener("mouseup",pointerUpHandler),t.addEventListener("touchend",pointerUpHandler),t.addEventListener("blur",blurEventHandler$1),t.addEventListener("mousemove",mouseMoveHandler),t.addEventListener("touchmove",mouseMoveHandler),on("tick",()=>{lastFrameRenderOrder.length=0,thisFrameRenderOrder.map(t=>{lastFrameRenderOrder.push(t)}),thisFrameRenderOrder.length=0})}function track(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){thisFrameRenderOrder.push(this),this._r()},trackedObjects.push(t))})}function untrack(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=trackedObjects.indexOf(t);-1!==e&&trackedObjects.splice(e,1)})}function pointerOver(t){return!!trackedObjects.includes(t)&&getCurrentObject()===t}function onPointerDown(t){callbacks$2.onDown=t}function onPointerUp(t){callbacks$2.onUp=t}function pointerPressed(t){return!!pressedButtons[t]}class Pool{constructor({create:t,maxSize:e=1024}={}){let i;if(!t||!(i=t())||!(i.update&&i.init&&i.isAlive))throw Error("Must provide create() function which returns an object with init(), update(), and isAlive() functions");this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function poolFactory(t){return new Pool(t)}function getIndices(t,e){let i=[],s=e.x+e.width/2,n=e.y+e.height/2,a=t.y=e.y,r=t.y+t.height>=n&&t.y=e.x&&(a&&i.push(0),r&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1,this._s[i]._p=this}}function quadtreeFactory(t){return new Quadtree(t)}quadtreeFactory.prototype=Quadtree.prototype,quadtreeFactory.class=Quadtree;class Vector{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return vectorFactory(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function vectorFactory(t,e,i={}){let s=new Vector(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}vectorFactory.prototype=Vector.prototype,vectorFactory.class=Vector;class Sprite{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:n,ddx:a,ddy:r,width:o,height:h,image:c}=t;this.position=vectorFactory(e,i),this.velocity=vectorFactory(s,n),this.acceleration=vectorFactory(a,r),this._fx=this._fy=1,this.width=this.height=this.rotation=0,this.ttl=1/0,this.anchor={x:0,y:0},this.context=getContext();for(let e in t)this[e]=t[e];c&&(this.width=void 0!==o?o:c.width,this.height=void 0!==h?h:c.height),this.sx=0,this.sy=0}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}get width(){return this._w}get height(){return this._h}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}set width(t){this._fx=t<0?-1:1,this._w=Math.abs(t)}set height(t){this._fy=t<0?-1:1,this._h=Math.abs(t)}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,n=t.y;return t.anchor&&(s-=t.width*t.anchor.x,n-=t.height*t.anchor.y),es&&in}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;if(this.context.save(),this.context.translate(this.viewX,this.viewY),this.rotation&&this.context.rotate(this.rotation),-1==this._fx||-1==this._fy){let i=this.width/2+t,s=this.height/2+e;this.context.translate(i,s),this.context.scale(this._fx,this._fy),this.context.translate(-i,-s)}this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function spriteFactory(t){return new Sprite(t)}function parseFrames(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],n=+i[1],a=s;if(s=n;a--)e.push(a);return e}spriteFactory.prototype=Sprite.prototype,spriteFactory.class=Sprite;class SpriteSheet{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:n}={}){if(!t)throw Error("You must provide an Image for the SpriteSheet");this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(n)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:n,loop:a}=t[i];if(e=[],void 0===s)throw Error("Animation "+i+" must provide a frames property");[].concat(s).map(t=>{e=e.concat(parseFrames(t))}),this.animations[i]=animationFactory({spriteSheet:this,frames:e,frameRate:n,loop:a})}}}function spriteSheetFactory(t){return new SpriteSheet(t)}function setStoreItem(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))}function getStoreItem(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e}function TileEngine(t={}){let{width:e,height:i,tilewidth:s,tileheight:n,context:a=getContext(),tilesets:r,layers:o}=t,h=e*s,c=i*n,d=document.createElement("canvas"),l=d.getContext("2d");d.width=h,d.height=c;let u={},p={},g=[],m=Object.assign({context:a,mapwidth:h,mapheight:c,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),h-getCanvas().width),g.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),c-getCanvas().height),g.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),w(d)},renderLayer(t){let e=p[t],i=u[t];e||((e=document.createElement("canvas")).width=h,e.height=c,p[t]=e,m._r(i,e.getContext("2d"))),w(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let n=f(s),a=y(i),r=f(s+e.height),o=y(i+e.width),h=u[t];for(let t=n;t<=r;t++)for(let e=a;e<=o;e++)if(h.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||f(e.y),s=e.col||y(e.x);return u[t]?u[t].data[s+i*m.width]:-1},setTileAtLayer(t,e,i){let s=e.row||f(e.y),n=e.col||y(e.x);u[t]&&(this._d=!0,u[t].data[n+s*m.width]=i)},addObject(t){g.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=g.indexOf(t);-1!==e&&(g.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=m.tilesets.length-1;e>=0&&(s=m.tilesets[e],!(t/s.firstgid>=1));e--);let n=s.tilewidth||m.tilewidth,a=s.tileheight||m.tileheight,r=s.margin||0,o=s.image,h=t-s.firstgid,c=s.columns||o.width/(n+r)|0,d=i%m.width*n,l=(i/m.width|0)*a,u=h%c*(n+r),p=(h/c|0)*(a+r);e.drawImage(o,u,p,n,a,d,l,n,a)}),e.restore()},_p:_,layerCanvases:p},t);function f(t){return t/m.tileheight|0}function y(t){return t/m.tilewidth|0}function _(){m.layers&&m.layers.map(t=>{u[t.name]=t,!1!==t.visible&&m._r(t,l)})}function w(t){const{width:e,height:i}=getCanvas(),s=Math.min(t.width,e),n=Math.min(t.height,i);m.context.drawImage(t,m.sx,m.sy,s,n,0,0,s,n)}return m.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){if(!window.__k)throw Error('You must use "load" or "loadData" to resolve tileset.source');let t=window.__k.d[window.__k.u(e.source,i)];if(!t)throw Error(`You must load the tileset source "${e.source}" before loading the tileset`);Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){if(!window.__k)throw Error('You must use "load" or "loadImage" to resolve tileset.image');let t=window.__k.i[window.__k.u(e.image,i)];if(!t)throw Error(`You must load the image "${e.image}" before loading the tileset`);e.image=t}}),_(),m}spriteSheetFactory.prototype=SpriteSheet.prototype,spriteSheetFactory.class=SpriteSheet;let kontra={Animation:animationFactory,imageAssets:imageAssets,audioAssets:audioAssets,dataAssets:dataAssets,setImagePath:setImagePath,setAudioPath:setAudioPath,setDataPath:setDataPath,loadImage:loadImage,loadAudio:loadAudio,loadData:loadData,load:load,init:init,getCanvas:getCanvas,getContext:getContext,on:on,off:off,emit:emit,GameLoop:GameLoop,keyMap:keyMap,initKeys:initKeys,bindKeys:bindKeys,unbindKeys:unbindKeys,keyPressed:keyPressed,registerPlugin:registerPlugin,unregisterPlugin:unregisterPlugin,extendObject:extendObject,initPointer:initPointer,pointer:pointer,track:track,untrack:untrack,pointerOver:pointerOver,onPointerDown:onPointerDown,onPointerUp:onPointerUp,pointerPressed:pointerPressed,Pool:poolFactory,Quadtree:quadtreeFactory,Sprite:spriteFactory,SpriteSheet:spriteSheetFactory,setStoreItem:setStoreItem,getStoreItem:getStoreItem,TileEngine:TileEngine,Vector:vectorFactory};export{animationFactory as Animation,imageAssets,audioAssets,dataAssets,setImagePath,setAudioPath,setDataPath,loadImage,loadAudio,loadData,load,init,getCanvas,getContext,on,off,emit,GameLoop,keyMap,initKeys,bindKeys,unbindKeys,keyPressed,registerPlugin,unregisterPlugin,extendObject,initPointer,pointer,track,untrack,pointerOver,onPointerDown,onPointerUp,pointerPressed,poolFactory as Pool,quadtreeFactory as Quadtree,spriteFactory as Sprite,spriteSheetFactory as SpriteSheet,setStoreItem,getStoreItem,TileEngine,vectorFactory as Vector};export default kontra; \ No newline at end of file +let canvasEl,context,callbacks={};function on(t,e){callbacks[t]=callbacks[t]||[],callbacks[t].push(e)}function off(t,e){let i;!callbacks[t]||(i=callbacks[t].indexOf(e))<0||callbacks[t].splice(i,1)}function emit(t,...e){callbacks[t]&&callbacks[t].map(t=>t(...e))}function getCanvas(){return canvasEl}function getContext(){return context}function init(t){if(!(canvasEl=document.getElementById(t)||t||document.querySelector("canvas")))throw Error("You must provide a canvas element for the game");return(context=canvasEl.getContext("2d")).imageSmoothingEnabled=!1,emit("init"),{canvas:canvasEl,context:context}}class Animation{constructor({spriteSheet:t,frames:e,frameRate:i,loop:s=!0}={}){this.spriteSheet=t,this.frames=e,this.frameRate=i,this.loop=s;let{width:n,height:a,margin:r=0}=t.frame;this.width=n,this.height=a,this.margin=r,this._f=0,this._a=0}clone(){return animationFactory(this)}reset(){this._f=0,this._a=0}update(t=1/60){if(this.loop||this._f!=this.frames.length-1)for(this._a+=t;this._a*this.frameRate>=1;)this._f=++this._f%this.frames.length,this._a-=1/this.frameRate}render({x:t,y:e,width:i=this.width,height:s=this.height,context:n=getContext()}={}){let a=this.frames[this._f]/this.spriteSheet._f|0,r=this.frames[this._f]%this.spriteSheet._f|0;n.drawImage(this.spriteSheet.image,r*this.width+(2*r+1)*this.margin,a*this.height+(2*a+1)*this.margin,this.width,this.height,t,e,i,s)}}function animationFactory(t){return new Animation(t)}animationFactory.prototype=Animation.prototype,animationFactory.class=Animation;let imageRegex=/(jpeg|jpg|gif|png)$/,audioRegex=/(wav|mp3|ogg|aac)$/,leadingSlash=/^\//,trailingSlash=/\/$/,dataMap=new WeakMap,imagePath="",audioPath="",dataPath="";function getUrl(t,e){return new URL(t,e).href}function joinPath(t,e){return[t.replace(trailingSlash,""),t?e.replace(leadingSlash,""):e].filter(t=>t).join("/")}function getExtension(t){return t.split(".").pop()}function getName(t){let e=t.replace("."+getExtension(t),"");return 2==e.split("/").length?e.replace(leadingSlash,""):e}function getCanPlay(t){return{wav:"",mp3:t.canPlayType("audio/mpeg;"),ogg:t.canPlayType('audio/ogg; codecs="vorbis"'),aac:t.canPlayType("audio/aac;")}}let imageAssets={},audioAssets={},dataAssets={};function addGlobal(){window.__k||(window.__k={dm:dataMap,u:getUrl,d:dataAssets,i:imageAssets})}function setImagePath(t){imagePath=t}function setAudioPath(t){audioPath=t}function setDataPath(t){dataPath=t}function loadImage(t){return addGlobal(),new Promise((e,i)=>{let s,n,a;if(s=joinPath(imagePath,t),imageAssets[s])return e(imageAssets[s]);(n=new Image).onload=function(){a=getUrl(s,window.location.href),imageAssets[getName(t)]=imageAssets[s]=imageAssets[a]=this,emit("assetLoaded",this,t),e(this)},n.onerror=function(){i("Unable to load image "+s)},n.src=s})}function loadAudio(t){return new Promise((e,i)=>{let s,n,a,r;return s=new Audio,n=getCanPlay(s),(t=[].concat(t).reduce((t,e)=>t||(n[getExtension(e)]?e:null),0))?(a=joinPath(audioPath,t),audioAssets[a]?e(audioAssets[a]):(s.addEventListener("canplay",function(){r=getUrl(a,window.location.href),audioAssets[getName(t)]=audioAssets[a]=audioAssets[r]=this,emit("assetLoaded",this,t),e(this)}),s.onerror=function(){i("Unable to load audio "+a)},s.src=a,void s.load())):i("cannot play any of the audio formats provided"+t)})}function loadData(t){let e,i;return addGlobal(),e=joinPath(dataPath,t),dataAssets[e]?Promise.resolve(dataAssets[e]):fetch(e).then(t=>{if(!t.ok)throw t;return t.clone().json().catch(()=>t.text())}).then(s=>(i=getUrl(e,window.location.href),"object"==typeof s&&dataMap.set(s,i),dataAssets[getName(t)]=dataAssets[e]=dataAssets[i]=s,emit("assetLoaded",s,t),s))}function load(...t){return addGlobal(),Promise.all(t.map(t=>{let e=getExtension([].concat(t)[0]);return e.match(imageRegex)?loadImage(t):e.match(audioRegex)?loadAudio(t):loadData(t)}))}const noop=()=>{};function clear(){let t=getCanvas();getContext().clearRect(0,0,t.width,t.height)}function GameLoop({fps:t=60,clearCanvas:e=!0,update:i,render:s}={}){if(!i||!s)throw Error("You must provide update() and render() functions");let n,a,r,o,h,c=0,d=1e3/t,l=1/t,u=e?clear:noop;function p(){if(a=requestAnimationFrame(p),r=performance.now(),o=r-n,n=r,!(o>1e3)){for(emit("tick"),c+=o;c>=d;)h.update(l),c-=d;u(),h.render()}}return h={update:i,render:s,isStopped:!0,start(){n=performance.now(),this.isStopped=!1,requestAnimationFrame(p)},stop(){this.isStopped=!0,cancelAnimationFrame(a)},_frame:p,set _last(t){n=t}}}let callbacks$1={},pressedKeys={},keyMap={13:"enter",27:"esc",32:"space",37:"left",38:"up",39:"right",40:"down"};function keydownEventHandler(t){let e=keyMap[t.which];pressedKeys[e]=!0,callbacks$1[e]&&callbacks$1[e](t)}function keyupEventHandler(t){pressedKeys[keyMap[t.which]]=!1}function blurEventHandler(){pressedKeys={}}function initKeys(){let t;for(t=0;t<26;t++)keyMap[65+t]=(10+t).toString(36);for(t=0;t<10;t++)keyMap[48+t]=""+t;window.addEventListener("keydown",keydownEventHandler),window.addEventListener("keyup",keyupEventHandler),window.addEventListener("blur",blurEventHandler)}function bindKeys(t,e){[].concat(t).map(t=>callbacks$1[t]=e)}function unbindKeys(t){[].concat(t).map(t=>callbacks$1[t]=0)}function keyPressed(t){return!!pressedKeys[t]}function getMethod(t){let e=t.substr(t.search(/[A-Z]/));return e[0].toLowerCase()+e.substr(1)}function removeInterceptor(t,e){let i=t.indexOf(e);-1!==i&&t.splice(i,1)}function registerPlugin(t,e){let i=t.prototype;i&&(i._inc||(i._inc={},i._bInc=function(t,e,...i){return this._inc[e].before.reduce((e,i)=>{let s=i(t,...e);return s||e},i)},i._aInc=function(t,e,i,...s){return this._inc[e].after.reduce((e,i)=>{let n=i(t,e,...s);return n||e},i)}),Object.getOwnPropertyNames(e).forEach(t=>{let s=getMethod(t);i[s]&&(i["_o"+s]||(i["_o"+s]=i[s],i[s]=function(...t){let e=this._bInc(this,s,...t),n=i["_o"+s].call(this,...e);return this._aInc(this,s,n,...t)}),i._inc[s]||(i._inc[s]={before:[],after:[]}),t.startsWith("before")?i._inc[s].before.push(e[t]):t.startsWith("after")&&i._inc[s].after.push(e[t]))}))}function unregisterPlugin(t,e){let i=t.prototype;i&&i._inc&&Object.getOwnPropertyNames(e).forEach(t=>{let s=getMethod(t);t.startsWith("before")?removeInterceptor(i._inc[s].before,e[t]):t.startsWith("after")&&removeInterceptor(i._inc[s].after,e[t])})}function extendObject(t,e){let i=t.prototype;i&&Object.getOwnPropertyNames(e).forEach(t=>{i[t]||(i[t]=e[t])})}let thisFrameRenderOrder=[],lastFrameRenderOrder=[],callbacks$2={},trackedObjects=[],pressedButtons={},buttonMap={0:"left",1:"middle",2:"right"},pointer={x:0,y:0,radius:5};function circleRectCollision(t){let e=t.x,i=t.y;t.anchor&&(e-=t.width*t.anchor.x,i-=t.height*t.anchor.y);let s=pointer.x-Math.max(e,Math.min(pointer.x,e+t.width)),n=pointer.y-Math.max(i,Math.min(pointer.y,i+t.height));return s*s+n*n=0;s--)if(e=(t=i[s]).collidesWithPointer?t.collidesWithPointer(pointer):circleRectCollision(t))return t}function pointerDownHandler(t){let e=void 0!==t.button?buttonMap[t.button]:"left";pressedButtons[e]=!0,pointerHandler(t,"onDown")}function pointerUpHandler(t){let e=void 0!==t.button?buttonMap[t.button]:"left";pressedButtons[e]=!1,pointerHandler(t,"onUp")}function mouseMoveHandler(t){pointerHandler(t,"onOver")}function blurEventHandler$1(){pressedButtons={}}function pointerHandler(t,e){let i,s,n=getCanvas();if(!n)return;-1!==["touchstart","touchmove","touchend"].indexOf(t.type)?(i=(t.touches[0]||t.changedTouches[0]).clientX,s=(t.touches[0]||t.changedTouches[0]).clientY):(i=t.clientX,s=t.clientY);let a=n.height/n.offsetHeight,r=n.getBoundingClientRect(),o=(i-r.left)*a,h=(s-r.top)*a;pointer.x=o,pointer.y=h,t.preventDefault();let c=getCurrentObject();c&&c[e]&&c[e](t),callbacks$2[e]&&callbacks$2[e](t,c)}function initPointer(){let t=getCanvas();t.addEventListener("mousedown",pointerDownHandler),t.addEventListener("touchstart",pointerDownHandler),t.addEventListener("mouseup",pointerUpHandler),t.addEventListener("touchend",pointerUpHandler),t.addEventListener("blur",blurEventHandler$1),t.addEventListener("mousemove",mouseMoveHandler),t.addEventListener("touchmove",mouseMoveHandler),on("tick",()=>{lastFrameRenderOrder.length=0,thisFrameRenderOrder.map(t=>{lastFrameRenderOrder.push(t)}),thisFrameRenderOrder.length=0})}function track(t){[].concat(t).map(t=>{t._r||(t._r=t.render,t.render=function(){thisFrameRenderOrder.push(this),this._r()},trackedObjects.push(t))})}function untrack(t){[].concat(t).map(t=>{t.render=t._r,t._r=0;let e=trackedObjects.indexOf(t);-1!==e&&trackedObjects.splice(e,1)})}function pointerOver(t){return!!trackedObjects.includes(t)&&getCurrentObject()===t}function onPointerDown(t){callbacks$2.onDown=t}function onPointerUp(t){callbacks$2.onUp=t}function pointerPressed(t){return!!pressedButtons[t]}class Pool{constructor({create:t,maxSize:e=1024}={}){let i;if(!t||!(i=t())||!(i.update&&i.init&&i.isAlive))throw Error("Must provide create() function which returns an object with init(), update(), and isAlive() functions");this._c=t,this._i=0,this.objects=[t()],this.size=1,this.maxSize=e}get(t={}){if(this.objects.length==this._i){if(this.size===this.maxSize)return;for(let t=0;t=s;)(e=this.objects[i]).update(t),e.isAlive()?i--:(this.objects=this.objects.splice(i,1).concat(this.objects),this._i--,s++)}render(){let t=Math.max(this.objects.length-this._i,0);for(let e=this.size-1;e>=t;e--)this.objects[e].render()}}function poolFactory(t){return new Pool(t)}function getIndices(t,e){let i=[],s=e.x+e.width/2,n=e.y+e.height/2,a=t.y=e.y,r=t.y+t.height>=n&&t.y=e.x&&(a&&i.push(0),r&&i.push(2)),t.x+t.width>=s&&t.xs.add(t));return Array.from(s)}return this._o.filter(e=>e!==t)}add(){let t,e,i,s;for(e=0;ethis.maxObjects&&this._d=2?e:0),width:t,height:e},maxDepth:this.maxDepth,maxObjects:this.maxObjects}),this._s[i]._d=this._d+1,this._s[i]._p=this}}function quadtreeFactory(t){return new Quadtree(t)}quadtreeFactory.prototype=Quadtree.prototype,quadtreeFactory.class=Quadtree;class Vector{constructor(t=0,e=0){this._x=t,this._y=e}add(t,e=1){return vectorFactory(this.x+(t.x||0)*e,this.y+(t.y||0)*e,this)}clamp(t,e,i,s){this._c=!0,this._a=t,this._b=e,this._d=i,this._e=s}get x(){return this._x}get y(){return this._y}set x(t){this._x=this._c?Math.min(Math.max(this._a,t),this._d):t}set y(t){this._y=this._c?Math.min(Math.max(this._b,t),this._e):t}}function vectorFactory(t,e,i={}){let s=new Vector(t,e);return i._c&&(s.clamp(i._a,i._b,i._d,i._e),s.x=t,s.y=e),s}vectorFactory.prototype=Vector.prototype,vectorFactory.class=Vector;class Sprite{constructor(t){this.init(t)}init(t={}){let{x:e,y:i,dx:s,dy:n,ddx:a,ddy:r,width:o,height:h,image:c}=t;this.position=vectorFactory(e,i),this.velocity=vectorFactory(s,n),this.acceleration=vectorFactory(a,r),this._fx=this._fy=1,this.width=this.height=this.rotation=0,this.ttl=1/0,this.anchor={x:0,y:0},this.context=getContext();for(let e in t)this[e]=t[e];c&&(this.width=void 0!==o?o:c.width,this.height=void 0!==h?h:c.height),this.sx=0,this.sy=0}get x(){return this.position.x}get y(){return this.position.y}get dx(){return this.velocity.x}get dy(){return this.velocity.y}get ddx(){return this.acceleration.x}get ddy(){return this.acceleration.y}get animations(){return this._a}get viewX(){return this.x-this.sx}get viewY(){return this.y-this.sy}get width(){return this._w}get height(){return this._h}set x(t){this.position.x=t}set y(t){this.position.y=t}set dx(t){this.velocity.x=t}set dy(t){this.velocity.y=t}set ddx(t){this.acceleration.x=t}set ddy(t){this.acceleration.y=t}set animations(t){let e,i;for(e in this._a={},t)this._a[e]=t[e].clone(),i=i||this._a[e];this.currentAnimation=i,this.width=this.width||i.width,this.height=this.height||i.height}set viewX(t){}set viewY(t){}set width(t){let e=t<0?-1:1;this._fx=e,this._w=t*e}set height(t){let e=t<0?-1:1;this._fy=e,this._h=t*e}isAlive(){return this.ttl>0}collidesWith(t){if(this.rotation||t.rotation)return null;let e=this.x-this.width*this.anchor.x,i=this.y-this.height*this.anchor.y,s=t.x,n=t.y;return t.anchor&&(s-=t.width*t.anchor.x,n-=t.height*t.anchor.y),es&&in}update(t){this.advance(t)}render(){this.draw()}playAnimation(t){this.currentAnimation=this.animations[t],this.currentAnimation.loop||this.currentAnimation.reset()}advance(t){this.velocity=this.velocity.add(this.acceleration,t),this.position=this.position.add(this.velocity,t),this.ttl--,this.currentAnimation&&this.currentAnimation.update(t)}draw(){let t=-this.width*this.anchor.x,e=-this.height*this.anchor.y;if(this.context.save(),this.context.translate(this.viewX,this.viewY),this.rotation&&this.context.rotate(this.rotation),-1==this._fx||-1==this._fy){let i=this.width/2+t,s=this.height/2+e;this.context.translate(i,s),this.context.scale(this._fx,this._fy),this.context.translate(-i,-s)}this.image?this.context.drawImage(this.image,0,0,this.image.width,this.image.height,t,e,this.width,this.height):this.currentAnimation?this.currentAnimation.render({x:t,y:e,width:this.width,height:this.height,context:this.context}):(this.context.fillStyle=this.color,this.context.fillRect(t,e,this.width,this.height)),this.context.restore()}}function spriteFactory(t){return new Sprite(t)}function parseFrames(t){if(+t===t)return t;let e=[],i=t.split(".."),s=+i[0],n=+i[1],a=s;if(s=n;a--)e.push(a);return e}spriteFactory.prototype=Sprite.prototype,spriteFactory.class=Sprite;class SpriteSheet{constructor({image:t,frameWidth:e,frameHeight:i,frameMargin:s,animations:n}={}){if(!t)throw Error("You must provide an Image for the SpriteSheet");this.animations={},this.image=t,this.frame={width:e,height:i,margin:s},this._f=t.width/e|0,this.createAnimations(n)}createAnimations(t){let e,i;for(i in t){let{frames:s,frameRate:n,loop:a}=t[i];if(e=[],void 0===s)throw Error("Animation "+i+" must provide a frames property");[].concat(s).map(t=>{e=e.concat(parseFrames(t))}),this.animations[i]=animationFactory({spriteSheet:this,frames:e,frameRate:n,loop:a})}}}function spriteSheetFactory(t){return new SpriteSheet(t)}function setStoreItem(t,e){void 0===e?localStorage.removeItem(t):localStorage.setItem(t,JSON.stringify(e))}function getStoreItem(t){let e=localStorage.getItem(t);try{e=JSON.parse(e)}catch(t){}return e}function TileEngine(t={}){let{width:e,height:i,tilewidth:s,tileheight:n,context:a=getContext(),tilesets:r,layers:o}=t,h=e*s,c=i*n,d=document.createElement("canvas"),l=d.getContext("2d");d.width=h,d.height=c;let u={},p={},g=[],m=Object.assign({context:a,mapwidth:h,mapheight:c,_sx:0,_sy:0,_d:!1,get sx(){return this._sx},get sy(){return this._sy},set sx(t){this._sx=Math.min(Math.max(0,t),h-getCanvas().width),g.forEach(t=>t.sx=this._sx)},set sy(t){this._sy=Math.min(Math.max(0,t),c-getCanvas().height),g.forEach(t=>t.sy=this._sy)},render(){this._d&&(this._d=!1,this._p()),w(d)},renderLayer(t){let e=p[t],i=u[t];e||((e=document.createElement("canvas")).width=h,e.height=c,p[t]=e,m._r(i,e.getContext("2d"))),w(e)},layerCollidesWith(t,e){let i=e.x,s=e.y;e.anchor&&(i-=e.width*e.anchor.x,s-=e.height*e.anchor.y);let n=f(s),a=y(i),r=f(s+e.height),o=y(i+e.width),h=u[t];for(let t=n;t<=r;t++)for(let e=a;e<=o;e++)if(h.data[e+t*this.width])return!0;return!1},tileAtLayer(t,e){let i=e.row||f(e.y),s=e.col||y(e.x);return u[t]?u[t].data[s+i*m.width]:-1},setTileAtLayer(t,e,i){let s=e.row||f(e.y),n=e.col||y(e.x);u[t]&&(this._d=!0,u[t].data[n+s*m.width]=i)},addObject(t){g.push(t),t.sx=this._sx,t.sy=this._sy},removeObject(t){let e=g.indexOf(t);-1!==e&&(g.splice(e,1),t.sx=t.sy=0)},_r:function(t,e){e.save(),e.globalAlpha=t.opacity,t.data.map((t,i)=>{if(!t)return;let s;for(let e=m.tilesets.length-1;e>=0&&(s=m.tilesets[e],!(t/s.firstgid>=1));e--);let n=s.tilewidth||m.tilewidth,a=s.tileheight||m.tileheight,r=s.margin||0,o=s.image,h=t-s.firstgid,c=s.columns||o.width/(n+r)|0,d=i%m.width*n,l=(i/m.width|0)*a,u=h%c*(n+r),p=(h/c|0)*(a+r);e.drawImage(o,u,p,n,a,d,l,n,a)}),e.restore()},_p:_,layerCanvases:p},t);function f(t){return t/m.tileheight|0}function y(t){return t/m.tilewidth|0}function _(){m.layers&&m.layers.map(t=>{u[t.name]=t,!1!==t.visible&&m._r(t,l)})}function w(t){const{width:e,height:i}=getCanvas(),s=Math.min(t.width,e),n=Math.min(t.height,i);m.context.drawImage(t,m.sx,m.sy,s,n,0,0,s,n)}return m.tilesets.map(e=>{let i=(window.__k?window.__k.dm.get(t):"")||window.location.href;if(e.source){if(!window.__k)throw Error('You must use "load" or "loadData" to resolve tileset.source');let t=window.__k.d[window.__k.u(e.source,i)];if(!t)throw Error(`You must load the tileset source "${e.source}" before loading the tileset`);Object.keys(t).map(i=>{e[i]=t[i]})}if(""+e.image===e.image){if(!window.__k)throw Error('You must use "load" or "loadImage" to resolve tileset.image');let t=window.__k.i[window.__k.u(e.image,i)];if(!t)throw Error(`You must load the image "${e.image}" before loading the tileset`);e.image=t}}),_(),m}spriteSheetFactory.prototype=SpriteSheet.prototype,spriteSheetFactory.class=SpriteSheet;let kontra={Animation:animationFactory,imageAssets:imageAssets,audioAssets:audioAssets,dataAssets:dataAssets,setImagePath:setImagePath,setAudioPath:setAudioPath,setDataPath:setDataPath,loadImage:loadImage,loadAudio:loadAudio,loadData:loadData,load:load,init:init,getCanvas:getCanvas,getContext:getContext,on:on,off:off,emit:emit,GameLoop:GameLoop,keyMap:keyMap,initKeys:initKeys,bindKeys:bindKeys,unbindKeys:unbindKeys,keyPressed:keyPressed,registerPlugin:registerPlugin,unregisterPlugin:unregisterPlugin,extendObject:extendObject,initPointer:initPointer,pointer:pointer,track:track,untrack:untrack,pointerOver:pointerOver,onPointerDown:onPointerDown,onPointerUp:onPointerUp,pointerPressed:pointerPressed,Pool:poolFactory,Quadtree:quadtreeFactory,Sprite:spriteFactory,SpriteSheet:spriteSheetFactory,setStoreItem:setStoreItem,getStoreItem:getStoreItem,TileEngine:TileEngine,Vector:vectorFactory};export{animationFactory as Animation,imageAssets,audioAssets,dataAssets,setImagePath,setAudioPath,setDataPath,loadImage,loadAudio,loadData,load,init,getCanvas,getContext,on,off,emit,GameLoop,keyMap,initKeys,bindKeys,unbindKeys,keyPressed,registerPlugin,unregisterPlugin,extendObject,initPointer,pointer,track,untrack,pointerOver,onPointerDown,onPointerUp,pointerPressed,poolFactory as Pool,quadtreeFactory as Quadtree,spriteFactory as Sprite,spriteSheetFactory as SpriteSheet,setStoreItem,getStoreItem,TileEngine,vectorFactory as Vector};export default kontra; \ No newline at end of file diff --git a/kontra.mjs b/kontra.mjs index 5676df7f..1b48a19c 100644 --- a/kontra.mjs +++ b/kontra.mjs @@ -2683,12 +2683,16 @@ class Sprite { } set width(value) { - this._fx = value < 0 ? -1 : 1; - this._w = Math.abs(value); + let sign = value < 0 ? -1 : 1; + + this._fx = sign; + this._w = value * sign; } set height(value) { - this._fy = value < 0 ? -1 : 1; - this._h = Math.abs(value); + let sign = value < 0 ? -1 : 1; + + this._fy = sign; + this._h = value * sign; } /**