-
Notifications
You must be signed in to change notification settings - Fork 0
/
bagel.min.js
5 lines (5 loc) · 137 KB
/
bagel.min.js
1
2
3
4
5
/*
Bagel.js by hedgehog125, see https://github.com/hedgehog125/Bagel.js. License information can be found in license.txt
Button sounds from: https://scratch.mit.edu/projects/42854414/ under CC BY-SA 2.0
*/
Bagel={init:e=>{let t=Bagel.internal.current,n=Bagel.internal.subFunctions.init;return Bagel.internal.saveCurrent(),t.game=e,e=n.check(e),n.misc(e),Bagel.internal.loadPlugin(Bagel.internal.plugin,e,{}),n.listeners(e,e.internal.renderer.canvas.addEventListener),n.loadingScreen(e),e.config.disableBagelJSMessage||console.log("Bagel.js | 🥯🥯🥯 | 2d Canvas\nhttps://github.com/hedgehog125/Bagel.js"),Bagel.internal.games[e.id]=e,Bagel.internal.loadCurrent(),e},internal:{plugin:{info:{id:"Internal",description:"The built-in plugin, adds an image based sprite type, a canvas and a renderer. Also contains some useful methods."},plugin:{types:{assets:{imgs:{args:{},description:'Images give a sprite (only the sprite type though) its appearance. Just set its "img" argument to the id of the image you want to use.',init:(e,t,n,i,r)=>{let s=new Image;(e=>{e.onload=(()=>{t(e)})})(s),s.src=e.src},get:"img"},snds:{args:{},description:"Sounds can be played by anything. They're played using game.playSound(<id>)",init:(e,t,n,i,r)=>{let s=new Audio;(e=>{e.onloadedmetadata=(()=>{t(e)})})(s),s.preload="metadata",s.src=e.src},get:"snd",forcePreload:!0},spritesheets:{args:{frames:{required:!0,check:e=>{if("number"!=typeof e)return"Oops, this should be a number and you used "+Bagel.internal.an(Bagel.internal.getTypeOf(e))+"."},checkEach:!0,types:["array"],description:"How many frames there are for each animation. Animation frames go along the x axis."},animations:{required:!0,check:e=>{if("string"!=typeof e)return"Oops, this should be a string and you used "+Bagel.internal.an(Bagel.internal.getTypeOf(e))+"."},checkEach:!0,types:["array"],description:"The names of the different animations. Separate animations go down the y axis."}},init:(e,t,n,i,r)=>{let s=new Image;((e,n,i)=>{e.onload=(()=>{let r=0;for(let e in n.frames)n.frames[e]>r&&(r=n.frames[e]);let s=!1;e.width%r!=0&&(console.warn("The image width isn't divisible by the \"frames\" argument. You should probably check if both of them're correct."),s=!0),e.height%n.animations.length&&(console.warn("The image height isn't divisible by the length of the \"animations\" argument. You should probably check if both of them're correct."),s=!0),s&&console.log("For the image asset "+JSON.stringify(n.id)+".");let a=e.width/r,o=e.height/n.animations.length,l=(i.internal.assets.assets,0);for(;l<n.animations.length;){let t=n.animations[l],r=0;for(;r<n.frames[l];){let s=n.id+"."+t+"."+r,d=document.createElement("canvas");d.width=a,d.height=o,d.getContext("2d").drawImage(e,-r*a,-l*o),i.set.asset.img(d,s,!1,!0)&&(console.error("Hmm, Bagel.js ran into a problem with the spritesheet "+JSON.stringify(n.id)+". The image id "+JSON.stringify(s)+" has already been taken. Double check the ids of your assets."),Bagel.internal.oops(i)),r++}l++}t(n)})})(s,e,n),s.src=e.src},description:"Contains many separate images. Useful for animations.",get:"spritesheet"}},sprites:{sprite:{args:{x:{required:!1,default:"centred",types:["number","string","function"],description:'The x position for the sprite. Can also be set to "centred" to centre it along the x axis, or set to a function that returns a position when the game loads. e.g:\n"(me, game) => game.width - 50"'},y:{required:!1,default:"centred",types:["number","string","function"],description:'The y position for the sprite. Can also be set to "centred" to centre it along the y axis, or set to a function that returns a position when the game loads. e.g:\n"(me, game) => game.height - 50"'},img:{required:!1,default:null,types:["string","undefined"],description:"The image for the sprite to use to start with. If set to null or not specified, the sprite will be invisible."},width:{required:!1,default:"1x",types:["number","string"],description:'The width for the sprite. Defaults to the width of the image. You can also set it to a multiple of the image width by setting it to "1x", "2x", etc.'},height:{required:!1,default:"1x",types:["number","string"],description:'The height for the sprite. Defaults to the height of the image. You can also set it to a multiple of the image height by setting it to "1x", "2x", etc.'},scale:{required:!1,types:["number"],description:"The scale of the sprite. If both the width and height are unspecified, the sprite width and height are set to the image width and height multiplied by the scale."},alpha:{required:!1,default:1,types:["number"],description:"The alpha of the sprite. 1 is fully visible, 0.5 is partially and 0's invisible."},angle:{required:!1,default:90,types:["number"],description:"The angle of the sprite. In degrees. 0º = up. 180º = down. -90º = left. 90º = right."}},cloneArgs:{x:{syntax:{description:'The x position for the clone. Can also be set to "centred" to centre it along the x axis, or set to a function that returns a position when the game loads. e.g:\n"(me, game) => game.width - 50"'},mode:"replace"},y:{syntax:{description:'The y position for the clone. Can also be set to "centred" to centre it along the y axis, or set to a function that returns a position when the game loads. e.g:\n"(me, game) => game.height - 50"'},mode:"replace"},img:{syntax:{description:"The image for the clone to use to start with. If set to null or not specified anywhere, the clone will be invisible."},mode:"replace"},width:{syntax:{description:'The width for the clone. Defaults to the width of the image. You can also set it to a multiple of the image width by setting it to "1x", "2x", etc.'},mode:"replace"},height:{syntax:{description:'The height for the clone. Defaults to the height of the image. You can also set it to a multiple of the image height by setting it to "1x", "2x", etc.'},mode:"replace"},scale:{syntax:{description:"The scale of the clone. If both the width and height are unspecified, the sprite width and height are set to the image width and height multiplied by the scale."},mode:"replace"},alpha:{syntax:{description:"The alpha of the clone. 1 is fully visible, 0.5 is partially and 0's invisible."},mode:"replace"},angle:{syntax:{description:"The angle of the clone. In degrees. 0º = up. 180º = down. -90º = left. 90º = right."},mode:"replace"}},listeners:{fns:{xy:(e,t,n,i,r,s,a)=>{"string"!=typeof t||"centred"!=t?"function"!=typeof t?"number"!=typeof t&&(console.error('Oops, this can only be a function, a number or the string "centred". In the sprite '+JSON.stringify(s.id)+"."+n+". You tried to set it to "+JSON.stringify(t)+"."),Bagel.internal.oops(i)):e[n]=t(s,i):e[n]=i["x"==n?"width":"height"]/2},dimensions:(e,t,n,i,r,s)=>{if("string"==typeof t&&t.includes("x")){let i=parseFloat(t.split("x")[0]);if(null==e.img)return void(e[n]=1);let r=Bagel.get.asset.img(e.img);if("boolean"==typeof r)return".rerun";e[n]=r[n]*i;let s=e.width/r.width,a=e.height/r.height;e.scale=(s+a)/2}else if("function"!=typeof t){if("number"!=typeof t)null==t&&e.scale?s[n]=e.scale+"x":(console.error("Hmm. This can only be a function, a multiple of its image "+n+" (e.g 1x, 2x, 0.3x etc.) or a number. In the sprite "+JSON.stringify(s.id)+"."+n+". You tried to set it to "+JSON.stringify(t)+"."),Bagel.internal.oops(i));else if(e.img){let t=Bagel.get.asset.img(e.img);if("boolean"==typeof t)return".rerun";let n=e.width/t.width,i=e.height/t.height;e.scale=(n+i)/2}}else if(e[n]=t(s,i),e.img){let t=Bagel.get.asset.img(e.img);if("boolean"==typeof t)return".rerun";let n=e.width/t.width,i=e.height/t.height;e.scale=(n+i)/2}}},property:{x:{set:"xy"},y:{set:"xy"},img:{set:(e,t,n,i,r,s,a,o)=>{if(!o&&e.img){let t=Bagel.get.asset.img(e.img);if("boolean"==typeof t)return".rerun";s.width=t.width,s.height=t.height}}},width:{set:"dimensions"},height:{set:"dimensions"},scale:{set:(e,t,n,i,r,s)=>{null!=t&&("number"==typeof t?(s.width=t+"x",s.height=t+"x"):(console.error("Erm, this can only be a number. In the sprite "+JSON.stringify(s.id)+".scale. You tried to set it to "+JSON.stringify(t)+"."),Bagel.internal.oops(i)))}},angle:{set:(e,t,n,i,r,s)=>{let a=s.internal.cache,o=Bagel.maths.degToRad(e.angle+90);a.cos=Math.cos(o),a.sin=Math.sin(o),e.angle=(e.angle+180)%360-180}}},trigger:!0},description:"A basic type of sprite. Has the appearance of the image specified.",check:(e,t,n,i,r)=>{},init:(e,t,n)=>{e.last={collision:null},e.internal.cache={}},render:{ctx:(e,t,n,i,r,s,a)=>{if(null==e.img)return;let o=Bagel.get.asset.img(e.img,i,!0);if("boolean"==typeof o){if(o)return;console.error("Huh, the sprite "+JSON.stringify(e.id)+"'s image doesn't exist, it doesn't appear to be loading either. Check game.game.assets to make sure your asset is called "+JSON.stringify(e.img)+", or change the sprite image to something else. (don't forget, it's case sensitive!)"),Bagel.internal.oops(i)}t.globalAlpha=e.alpha;let l=e.width>=0?1:-1,d=e.height>=0?1:-1;s*=l,a*=d,t.scale(s,a);let u=Math.abs(e.width)/2,h=Math.abs(e.height)/2,c=Bagel.internal.roundX,g=Bagel.internal.roundY;if(90==e.angle)t.drawImage(o,(c(e.x)-c(u))*l,(g(e.y)-g(h))*d,c(e.width),g(e.height));else{let n=Bagel.maths.degToRad(e.angle-90);t.translate(c(e.x*l),g(e.y*d)),t.rotate(n),t.drawImage(o,-u,-h,e.width,e.height)}t.setTransform(1,0,0,1,0,0),t.globalAlpha=1},clean:!0}},canvas:{args:{x:{required:!1,default:"centred",types:["number","string","function"],description:'The x position for the canvas. Can also be set to "centred" to centre it along the x axis, or set to a function that returns a position when the game loads. e.g:\n"(me, game) => game.width - 50"'},y:{required:!1,default:"centred",types:["number","string","function"],description:'The y position for the canvas. Can also be set to "centred" to centre it along the y axis, or set to a function that returns a position when the game loads. e.g:\n"(me, game) => game.height - 50"'},width:{required:!0,types:["number","function"],description:'The width for the canvas. Can also be a function that returns a position when the game loads. e.g:\n"(me, game) => game.width * 0.2"'},height:{required:!0,types:["number","function"],description:'The height for the canvas. Can also be a function that returns a position when the game loads. e.g:\n"(me, game) => game.height * 0.2"'},alpha:{required:!1,default:1,types:["number"],description:"The alpha of the canvas. 1 is fully visible, 0.5 is partially and 0's invisible."},fullRes:{required:!1,default:!1,types:["boolean"],description:"If true, the canvas width and height will be automatically changed to ensure it's rendered at the full resolution."},render:{required:!1,types:["function"],description:'Renders each frame for the canvas. The arguments provided are: "sprite", "game", "ctx" and "canvas".'}},cloneArgs:{x:{syntax:{description:'The x position for the clone. Can also be set to "centred" to centre it along the x axis, or set to a function that returns a position when the game loads. e.g:\n"(me, game) => game.width - 50"'},mode:"replace"},y:{syntax:{description:'The y position for the clone. Can also be set to "centred" to centre it along the x axis, or set to a function that returns a position when the game loads. e.g:\n"(me, game) => game.height - 50"'},mode:"replace"},width:{syntax:{description:'The width for the clone. Can also be a function that returns a position when the game loads. e.g:\n"(me, game) => game.width * 0.2"'},mode:"replace"},height:{syntax:{description:'The height for the clone. Can also be a function that returns a position when the game loads. e.g:\n"(me, game) => game.height * 0.2"'},mode:"replace"},alpha:{syntax:{description:"The alpha of the clone. 1 is fully visible, 0.5 is partially and 0's invisible."},mode:"replace"},fullRes:{mode:"replace"},render:{syntax:{description:'Renders each frame for the clone. The arguments provided are: "sprite", "game", "ctx" and "canvas".'},mode:"replace"}},listeners:{fns:{xy:(e,t,n,i,r,s)=>{if("string"!=typeof t||"centred"!=t)return"function"==typeof t?(console.log(s.id,e[n]),void(e[n]=t(s,i))):void("number"!=typeof t&&(console.error('Oops, this can only be a function, a number or the string "centred". In the sprite '+JSON.stringify(s.id)+"."+n+". You tried to set it to "+JSON.stringify(t)+"."),Bagel.internal.oops(i)));e[n]=i["x"==n?"width":"height"]/2},dimensions:(e,t,n,i,r,s)=>{"function"!=typeof t?"number"!=typeof t&&(console.error("Oops, this can only be a function or a number. In the sprite "+JSON.stringify(s.id)+"."+n+". You tried to set it to "+JSON.stringify(t)+"."),Bagel.internal.oops(i)):e[n]=t(s,i)}},property:{x:{set:"xy"},y:{set:"xy"},width:{set:"dimensions"},height:{set:"dimensions"}},trigger:!0},description:'A "2d" canvas sprite. Anything rendered onto the canvas gets rendered onto the main canvas.',init:(e,t)=>{let n=document.createElement("canvas"),i=n.getContext("2d"),r=t.internal.renderer.canvas.width/t.width,s=t.internal.renderer.canvas.height/t.height;e.fullRes&&(n.width*=r,n.height*=s),e.canvas=n,e.ctx=i,e.internal.last={}},render:{ctx:(e,t,n,i,r,s,a)=>{let o,l;e.fullRes?(o=e.width*s,l=e.height*a):(o=e.width*window.devicePixelRatio,l=e.height*window.devicePixelRatio);let d=e.internal.last;d.width==o&&d.height==l||(e.canvas.width=o,e.canvas.height=l,d.width=o,d.height=l);let u=Bagel.internal.current;Bagel.internal.saveCurrent(),u.plugin=null,u.sprite=e,e.render&&e.render(e,i,e.ctx,e.canvas),Bagel.internal.loadCurrent();let h=Bagel.internal.roundX,c=Bagel.internal.roundY;t.globalAlpha=e.alpha,t.scale(s,a),t.drawImage(e.canvas,h(e.x)-h(e.width/2),c(e.y)-c(e.height/2),h(e.width),c(e.height)),t.setTransform(1,0,0,1,0,0),t.globalAlpha=1},clean:!0}},text:{args:{x:{required:!1,default:"centred",types:["number","string","function"],description:'The x position for the text. Can also be set to "centred" to centre it along the x axis, or set to a function that returns a position when the game loads. e.g:\n"(me, game) => game.width - 50"'},y:{required:!1,default:"centred",types:["number","string","function"],description:'The y position for the text. Can also be set to "centred" to centre it along the y axis, or set to a function that returns a position when the game loads. e.g:\n"(me, game) => game.height - 50"'},alpha:{required:!1,default:1,types:["number"],description:"The alpha of the text. 1 is fully visible, 0.5 is partially and 0's invisible."},text:{required:!0,types:["string"],description:"The text that this sprite should display."},font:{required:!1,default:"30px Helvetica",types:["string"],description:"The font to use. Same as ctx.font."},colour:{required:!1,default:"black",types:["string"],description:'The colour for the text. An HTML colour. (e.g "red", "rgb(1, 2, 3)" etc.)'}},cloneArgs:{x:{syntax:{description:'The x position for the clone. Can also be set to "centred" to centre it along the x axis, or set to a function that returns a position when the game loads. e.g:\n"(me, game) => game.width - 50"'},mode:"replace"},y:{syntax:{description:'The y position for the clone. Can also be set to "centred" to centre it along the y axis, or set to a function that returns a position when the game loads. e.g:\n"(me, game) => game.height - 50"'},mode:"replace"},alpha:{syntax:{description:"The alpha of the clone. 1 is fully visible, 0.5 is partially and 0's invisible."},mode:"replace"},text:{syntax:{description:"The text that this clone should display."},mode:"replace"},font:{mode:"replace"},colour:{mode:"replace"}},listeners:{fns:{xy:(e,t,n,i,r,s,a)=>{"string"!=typeof t||"centred"!=t?"function"!=typeof t?"number"!=typeof t&&(console.error('Oops, this can only be a function, a number or the string "centred". In the sprite '+JSON.stringify(s.id)+"."+n+". You tried to set it to "+JSON.stringify(t)+"."),Bagel.internal.oops(i)):e[n]=t(s,i):e[n]=i["x"==n?"width":"height"]/2},rerender:(e,t,n,i,r,s,a,o)=>{o||s.internal.prerender(s,i.internal.renderer.canvas)}},property:{x:{set:"xy"},y:{set:"xy"},text:{set:"rerender"},font:{set:"rerender"},colour:{set:"rerender"}},trigger:!0},description:"A text sprite. Allows you to easily display text onscreen.",init:(e,t)=>{let n=e.internal;n.last={},n.canvas=document.createElement("canvas"),n.ctx=n.canvas.getContext("2d"),n.prerender=((e,t)=>{let n=e.internal.last,i=e.internal.canvas,r=e.internal.ctx,s=t.width/e.game.width,a=t.height/e.game.height;r.font=e.font;let o=1.5*r.measureText("M").width*a;i.width=r.measureText(e.text).width*s,i.height=Math.ceil(o),r.font=e.font,r.textBaseline="middle",r.fillStyle=e.colour,r.scale(s,a),r.fillText(e.text,0,i.height/2/a),r.setTransform(1,0,0,1,0,0),n.scaleX=s,n.scaleY=a,e.width=Math.round(i.width/s),e.height=Math.round(i.height/a)}),n.prerender(e,t.internal.renderer.canvas)},render:{ctx:(e,t,n,i,r,s,a)=>{let o=e.internal.last,l=e.internal.canvas,d=e.internal.ctx,u=Bagel.internal.roundX,h=Bagel.internal.roundY;o.scaleX==s&&o.scaleY==a||e.internal.prerender(e,n),t.globalAlpha=e.alpha,t.drawImage(l,u(e.x*s)-u(l.width/2),h(e.y*a)-u(l.height/2),u(l.width),h(l.height)),d.globalAlpha=1},clean:!0}}}},methods:{bagel:{maths:{category:{radToDeg:{fn:{normal:!0,fn:e=>180*e/Math.PI}},degToRad:{fn:{normal:!0,fn:e=>e*(Math.PI/180)}},get:{category:{direction:{fn:{normal:!0,fn:(e,t,n,i)=>Bagel.maths.radToDeg(Math.atan2(i-t,n-e))-90}},distance:{fn:{normal:!0,fn:(e,t,n,i)=>Math.sqrt(Math.pow(Math.abs(n-e),2)+Math.pow(Math.abs(i-t),2))}}}},hex:{fn:{normal:!0,fn:e=>1==(e=e.toString(16)).length?"0"+e:e}}}},download:{fn:{obArg:!1,args:{data:{required:!0,types:["string"],description:"The data for the file. Or the data URL if isUrl is set to true"},fileName:{required:!0,types:["string"],description:"The file name for the file to be downloaded."},isURL:{required:!1,default:!1,types:["boolean"],description:"If the data is a URL or not. This may be useful if you want to download a canvas using .toDataURL()."},mime:{required:!1,default:"text/plain",types:["string"],description:"The MIME type for the file."}},fn:e=>{let t;if(e.isURL)t=e.data;else{let n=new Blob([e.data],{type:e.mime});t=window.URL.createObjectURL(n)}let n=document.createElement("a");n.download=e.fileName,n.href=t,(e=>{e.onclick=(()=>e.remove())})(n),n.style.display="none",document.body.appendChild(n),n.click()}}},upload:{fn:{obArg:!1,args:{handler:{required:!0,types:["function"],description:"The handler function. It's given the data URL of the file as its first argument and the second is the file number, starting at 0 (for use with the 2nd argument set to true)"},multiple:{required:!1,default:!1,types:["boolean"],description:"If multiple files can be uploaded or not. The handler will be called once per file."}},fn:e=>{let t=document.createElement("input");t.type="file",t.style.display="none",t.multiple=e.multiple,(e=>{let n=0;t.addEventListener("change",()=>{let i=new FileReader;i.onload=(r=>{e.handler(r.target.result,n),++n<t.files.length&&i.readAsDataURL(t.files[n])}),i.readAsDataURL(t.files[0])},!1)})(e),Bagel.internal.inputAction.queue(e=>{e.click()},t)}}},pwa:{category:{init:{fn:{obArg:!0,args:{worker:{required:!1,types:["string"],description:"The URL of the service worker. They can be generated using Bagel.pwa.generate.worker. Its arguments are the game, extra files (e.g js files) and an optional fileName for the worker that will be downloaded by it."},icons:{required:!1,default:!1,types:["boolean"],description:"If the icons exist or not. Generate them using Bagel.pwa.generate.icons."},manifest:{required:!1,types:["string"],description:"The src of the manifest. Generate one using Bagel.pwa.generate.manifest."},debugManifest:{required:!1,types:["string"],description:"The src of your debug manifest. It allows you to test your PWA without putting it on a production server. (don't test things on production! :P)"},versions:{required:!1,types:["string"],description:"The src of the version JSON file. Generate versions using Bagel.pwa.generate.version."},version:{required:!1,types:["string"],description:"The src of the version file (the one that contains the latest version name). Generate versions using Bagel.pwa.generate.version."},versionStorageName:{required:!1,types:["string"],description:'The name for the localStorage that contains the current downloaded version. e.g "Marble game version". localStorage is shared on a website so make sure the name is unique to this game. It\'s explained in Bagel.pwa.generate.version.'},cacheStorageName:{required:!1,types:["string"],description:"The cache storage name provided by Bagel.pwa.generate.worker."},multiTabStorageName:{required:!1,types:["string"],description:"A name unique to this page for detecting multiple instances of the game and preventing them. (don't forget that domains share localStorage)"},minified:{required:!1,default:!1,types:["boolean"],description:"If you've minified your main JavaScript file. You should also use the minified version of Bagel.js."},debug:{required:!1,default:!0,types:["boolean"],description:"If debug mode should be enabled. This disables the service worker so the page updates properly when reloading. Make sure you disable this before you put this game online though."}},fn:e=>{if(Bagel.internal.pwaInitialised&&console.error("Erm, you can only run this function once per page. The PWA's already initialised."),e.worker?navigator.serviceWorker&&(e.debug?navigator.serviceWorker.getRegistrations().then(e=>{for(let t of e)t.unregister()}):navigator.serviceWorker.register(e.worker)):console.warn("The Bagel.js service worker's missing. Generate one using Bagel.pwa.generate.worker."),e.icons||console.warn("The Bagel.js icons are missing. Generate the icons using Bagel.pwa.generate.icons."),e.manifest){let t=e.debug?e.debugManifest:e.manifest;if(null==e.debugManifest&&console.warn('No debug manifest specified. One should have been generated by Bagel.pwa.generate.manifest. Once you\'ve got it, link it to your game by setting the "debugManifest" argument in this function to its src.'),e.debugManifest||!e.debug){let e=document.createElement("link");e.rel="manifest",e.href=t,document.head.appendChild(e)}}else console.warn("The Bagel.js manifest is missing. Generate one using Bagel.pwa.generate.manifest once you've generated the icons using Bagel.pwa.generate.icons.");e.versions||console.warn("The Bagel.js version JSON file's missing. Use Bagel.pwa.generate.version."),e.version?navigator.onLine?e.versionStorageName&&e.versions&&e.cacheStorageName&&("undefined"==typeof caches?(console.error("Huh, looks like you're trying to run this on an insecure server (the traffic isn't encrypted). Browsers block cache storage for insecure websites for security reasons. If this is your test server, you can try connecting using the url 127.0.0.0:<port of your current url>. If this is your main server, you should be using HTTPS, it's 2020! :P"),Bagel.internal.oops(game)):fetch(e.version).then(t=>t.text().then(t=>{t=t.split("\n").join("");let n=localStorage.getItem(e.versionStorageName);null==n&&(n=0),Bagel.pwa.version=n,n!=t&&fetch(e.versions).then(t=>t.json().then(t=>{caches.open(e.cacheStorageName).then(i=>{let r=n;for(;n<t.versions.length;){let e=t.versions[n].changed;for(let t in e)i.delete(e[t]);n++}localStorage.setItem(e.versionStorageName,n),Bagel.pwa.version=n;let s=!1;Bagel.events.pwaUpdate&&(s=Bagel.events.pwaUpdate(n,r,t)),s||location.reload()})}))}))):console.log("You're offline."):console.warn("The Bagel.js latest version file's missing. Use Bagel.pwa.generate.version."),e.multiTabStorageName?(null==localStorage.getItem(e.multiTabStorageName)&&localStorage.setItem(e.multiTabStorageName,"0"),((t,n)=>{setTimeout(()=>{if(localStorage.getItem(e.multiTabStorageName)==t){setInterval(()=>{++n>100&&(n=0),localStorage.setItem(e.multiTabStorageName,n)},500)}else alert("Erm. Looks like you have two of the same tab open. Please close one. Data loss is possible if you continue.")},1e3)})(localStorage.getItem(e.multiTabStorageName),0)):console.warn("The Bagel.js multi tab storage name is missing. This is a name unique to this page for detecting multiple instances of the game and preventing them. (don't forget that domains share localStorage)"),e.versionStorageName||console.warn("The Bagel.js version storage name's missing. This is explained in Bagel.pwa.generate.version."),e.cacheStorageName||console.warn("The Bagel.js cache name's missing. This should've been added after running Bagel.pwa.generate.worker."),e.minified||console.warn("Your code isn't minified. Look up an online tool to help. Once you're done, set \"minified\" in Bagel.pwa.init to true. Also, make sure to run lighthouse or an equivalent so you can follow the best practices :)"),e.debug&&console.warn('PWA debug mode is enabled, make sure you disable it before releasing by setting "debug" in Bagel.pwa.init to false.'),Bagel.internal.pwaInitialised=!0}}},generate:{category:{worker:{fn:{obArg:!1,args:{game:{required:!0,types:["object"],description:"The game object."},icons:{required:!0,types:["string"],description:"The src of the folder containing the icons. Generate them with Bagel.pwa.generate.icons."},extraFiles:{required:!0,types:["array"],description:"Any extra files that aren't assets but are needed. e.g main.js, bagel.js etc. The index.html file is automatically included"},storageID:{required:!1,types:["string"],description:'The id for the cache storage the worker uses. Defaults to "Bagel.js" followed by a space and then the name of the game specified.'},manifest:{required:!1,default:"manifest.json",types:["string"],description:"The src of your manifest or what will be the src when the website is properly online."},worker:{required:!1,default:"worker.js",types:["string"],description:"The file name for the worker JavaScript file. Determines the name when it's downloaded but also the src of the file so it can be cached."}},fn:e=>{let t=e.extraFiles;for(let e in game.game.assets)for(let n in game.game.assets[e]){let i=game.game.assets[e][n].src,r=i.split(":")[0];"data"!=r&&"blob"!=r&&t.push(i)}for(let e in game.game.plugins){let n=game.game.plugins[e].src,i=n.split(":")[0];"data"!=i&&"blob"!=i&&t.push(n)}"/"!=e.icons[e.icons.length-1]&&(e.icons+="/");let n=[128,144,152,192,256,512];for(let i in n)t.push(e.icons+n[i]+"x"+n[i]+".png");t.includes(e.manifest)||t.push(e.manifest),t.includes(e.worker)||t.push(e.worker);let i='let index=location.href.split("/");index.pop();const toCache=[index=index.join("/")+"/",...<CACHE>];self.addEventListener("install",e=>{self.skipWaiting()});const useCache=e=>cache?e():caches.open(<NAME>).then(s=>(cache=s,e()));let cache;self.addEventListener("fetch",e=>{e.respondWith(useCache(s=>cache.match(e.request).then(s=>{if(s)return s;{let s=fetch(e.request);return s.then(s=>{let t=e.request.url,n=t.replace(index,"");(t==index||toCache.includes(n))&&cache.put(e.request,s.clone())}),s.catch(s=>{console.warn("A Bagel.js service worker failed to fetch "+e.request.url+". Request:"),console.log({...e.request})}),s}})))});'.replace("<CACHE>",JSON.stringify(t));null==e.storageID&&(e.storageID="Bagel.js "+e.game.id),i=i.replace("<NAME>",JSON.stringify(e.storageID)),Bagel.download(i,e.worker,!1,"application/javascript"),console.log("Your service worker has been generated. Make sure to place this in the root directory of your project, also make sure that this page is in the root directory. You should also make sure that the array provided for the second argument contains the SRCs (not URLs!) of your JavaScript files (including the Bagel.js file).\nA new worker will need to be generated for each version (unless there's no new files) (versions can be generated using Bagel.pwa.generate.version)"),console.log('Make sure you enable the worker by setting the "worker" argument to '+JSON.stringify(e.fileName)+' and by setting "cacheStorageName" to '+JSON.stringify(e.storageID)+" in Bagel.pwa.init. You should also generate a version using Bagel.pwa.generate.version if you haven't already."),console.log("\nAlso make sure to save the code you used so you can generate the next worker more easilly.")}}},icons:{fn:{obArg:!1,args:{src:{required:!0,types:["string"],description:"The src of the 512x512 resolution icon. (it can be any resolution if it's pixel art)"},pixelArt:{required:!0,default:!0,types:["boolean"],description:"If the icon is pixel art or not. If it is, anti-aliasing will be disabled and there'll be no warning for having a low resolution icon."}},fn:e=>{let t=new Image;(t=>{t.onload=(()=>{t.width!=t.height&&console.warn("Huh, the image width doesn't match image height."),e.pixelArt||512==t.width&&512==t.height||console.warn("Hmm, the image isn't 512x512 and it's not pixel art.");let n=document.createElement("canvas"),i=n.getContext("2d"),r=[128,144,152,192,256,512];for(let s in r){let a=r[s];n.width=a,n.height=a,i.imageSmoothingEnabled=!e.pixelArt,i.clearRect(0,0,n.width,n.height),i.drawImage(t,0,0,n.width,n.height),Bagel.download(n.toDataURL("image/png"),a+"x"+a+".png",!0)}console.log('128, 144, 152, 192, 256 and 512 pixel resolutions have been generated. You may need to enable automatic downloads. These should be in a folder in your project directory (or subfolder). You can add them to your PWA by generating a manifest. You should also make sure to set the "icons" argument in Bagel.pwa.init to true.')})})(t),t.src=e.src,console.log("Loading image...")}}},manifest:{fn:{obArg:!0,args:{icons:{required:!0,types:["string"],description:"The src of the folder containing the icons. Ending it in a slash is optional. Generate these using Bagel.pwa.generate.icons, give it the src of your highest resolution image followed by if it's pixel art or not."},name:{required:!0,types:["string"],description:"The name of your PWA. Usually the name shown in an app list."},shortName:{required:!0,types:["string"],description:"A shorter name."},startURL:{required:!0,types:["string"],description:"The URL for the PWA to start at when it's opened."},backgroundColour:{required:!1,types:["string"],description:"The background colour for the PWA. Is an HTML colour. Defaults to the page's background colour."},themeColour:{required:!1,types:["string"],description:"The theme colour for the app. See https://developer.mozilla.org/en-US/docs/Web/Manifest/theme_color"},categories:{required:!1,types:["array"],description:"Any categories your PWA fits into. e.g games."},description:{required:!1,types:["string"],description:"A brief description of what this PWA is or does."},dir:{required:!1,types:["string"],description:'The direction of the text. Probably not needed as the Bagel.js pwa creator only works for games and not websites with games in them. Either "auto", "ltr" or "rtl".'},display:{required:!1,default:"fullscreen",types:["string"],description:'The display mode for the PWA. Can be "fullscreen", "standalone", "minimal-ui" or "browser".'},iarcRatingId:{required:!1,types:["string"],description:"An id for if your website's been age rated or not."},lang:{required:!1,types:["string"],description:'The language of your PWA. e.g "en-UK".'},orientation:{required:!1,default:"any",types:["string"],description:'The default orientation for the PWA. Most of the time, you probably want to set this to "landscape" or leave it so it\'s "any".'},preferRelatedApplications:{required:!1,types:["boolean"],description:"Tells the browser to encourage users to install a similar app, e.g your native app instead. You probably won't want to use this."},relatedApplications:{required:!1,types:["array"],description:"See https://developer.mozilla.org/en-US/docs/Web/Manifest/related_applications"},screenshots:{required:!1,types:["array"],description:"Intended to be used by PWA stores. See https://developer.mozilla.org/en-US/docs/Web/Manifest/screenshots"},scope:{required:!1,types:["string"],description:"The different URLs that the manifest applies to. Defaults to your argument for the start URL, which is probably what you want most of the time."}},fn:e=>{let t={icons:"icons",name:"name",shortName:"short_name",backgroundColour:"background_color",themeColour:"theme_color",categories:"categories",description:"description",dir:"dir",display:"display",iarcRatingId:"iarc_rating_id",lang:"lang",orientation:"orientation",preferRelatedApplications:"prefer_related_applications",relatedApplications:"related_applications",scope:"scope",screenshots:"screenshots",startURL:"start_url"},n={};for(let i in e)null==e[i]?"backgroundColour"==i?document.body&&(n[t[i]]=document.body.bgColor):"scope"==i&&(n[t[i]]=e.startURL):n[t[i]]=e[i];"/"!=(e=n).icons[e.icons.length-1]&&(e.icons+="/");let i=[128,144,152,192,256,512],r=[];for(let t in i){let n=i[t],s=n+"x"+n;r.push({src:e.icons+s+".png",sizes:s,type:"image/png"})}e.icons=r;let s=Bagel.internal.deepClone(e);s.start_url=location.href,s.scope=location.href,Bagel.download(JSON.stringify(e),"manifest.json",!1,"application/json"),Bagel.download(JSON.stringify(s),"debugManifest.json",!1,"application/json"),console.log('Manifests generated. Put them in the root directory of your project and set the "manifest" argument in Bagel.init.pwa to normal manifest src. You should also set the "debugManifest" argument to the other manifest\'s src if you want to be able to test your PWA.'),console.log("(You might need to enable automatic downloads to get both the files)"),console.log("Tip: you should also save the code you just ran so you can update your manifest more easily."),console.log('\nYou should also add these elements to your HTML "head" tag if you haven\'t already...');let a=r[r.length-1].src,o=document.createElement("link");o.rel="icon",o.type="image/png",o.href=a;let l=document.createElement("p");l.appendChild(o),console.log(l.innerHTML),(o=document.createElement("link")).rel="apple-touch-icon",o.sizes="512x512",o.type="image/png",o.href=a,(l=document.createElement("p")).appendChild(o),console.log(l.innerHTML)}}},version:{fn:{obArg:!1,args:{name:{required:!0,types:["string"],description:"The name of this version. e.g 1.0"},changed:{required:!0,types:["array"],description:"The srcs of files that have changed. This should include removed files but not new files. A rename should be treated as a removed file and then a new file. If you regenerated your worker file or manifest, it should also be included. If this is your first version, this should be empty."},versions:{required:!1,default:{syntax:1,versions:[]},types:["object"],description:"The current version JSON."},fileName:{required:!1,default:"versions.json",types:["string"],description:"The file name for the versions JSON file."}},fn:e=>{let t=0!=e.versions.versions.length;e.versions.versions.push({name:e.name,changed:e.changed}),Bagel.download(JSON.stringify(e.versions),e.fileName,!1,"application/json"),t||1==parseFloat(e.name)||console.warn("No previous version JSON was specified and this doesn't appear to be the first version.\nIf this isn't the first version, rerun this with the 3rd argument set to your current version JSON (not as a string)."),console.log("New version file generated. If there was no warning or you think it's incorrect, you should now "+(t?"replace your existing versions.json file":"move this file into your root directory")+"."),t?console.log("Don't forget to update your version file with\n"+JSON.stringify(e.versions.versions.length)+" :)"):(console.log('You also need a file to specifiy what the latest version is. Create a plain text file called "version.txt" in your root directory and put '+JSON.stringify(e.versions.versions.length)+" in it. New lines will be ignored."),console.log('Finally, link these two into your PWA by setting the "versions" and "version" arguments in Bagel.pwa.init to their corresponding file srcs, "versionStorageName" should also be set to something unique to the game (it\'s where the installed version is saved). If you\'ve followed all the other steps properly, your PWA should now be working. (make sure you\'re running on an HTTPS server or localhost)'))}}}}},help:{fn:{normal:!0,fn:()=>{console.log("Full tutorial here: https://github.com/hedgehog125/Bagel.js/wiki/PWA-Tutorial :)")}}}}}},game:{playSound:{fn:{obArg:!1,args:{id:{required:!0,types:["string"],description:"The id of the sound to play."},loop:{required:!1,default:!1,types:["boolean"],description:"If the audio should loop or not."},startTime:{required:!1,default:0,types:["number"],description:"The starting time for the audio in seconds."}},fn:(e,t,n)=>{let i=Bagel.get.asset.snd(t.id,e);if(i.currentTime=t.startTime,i.loop=t.loop,n.vars.audio.autoPlay){let r=i.play();null!=r&&(n=>{r.then(()=>{n.vars.audio.autoPlay=!0}).catch(()=>{let r=Bagel.internal.current;Bagel.internal.saveCurrent(),r.plugin=n,n.vars.audio.autoPlay=!1,(t.loop||i.duration>=5)&&(n.vars.audio.createUnmute(n,e),n.vars.audio.queue.push(t.id)),Bagel.internal.loadCurrent()})})(n)}}}},stopSound:{fn:{obArg:!1,args:{id:{required:!0,types:["string"],description:"The id of the sound to stop."}},fn:(e,t,n)=>{Bagel.get.asset.snd(t.id,e).stop()}}}},sprite:{move:{fn:{appliesTo:["sprite"],obArg:!1,args:{amount:{required:!0,types:["number"],description:"The number of in game pixels (independent of the rendered canvas width and height) to move the sprite."},angle:{required:!1,types:["number"],description:"The angle in degrees for the sprite to move in. 0° -> Straight up. -180/180° -> Straight down. 90° -> Right (default of sprites). Defaults to the value of sprite.angle."}},fn:(e,t,n)=>{let i=e.internal.cache;if(null==t.angle)e.x-=i.cos*t.amount,e.y-=i.sin*t.amount;else{let n=Bagel.maths.degToRad(t.angle+90);e.x-=Math.cos(n)*t.amount,e.y-=Math.sin(n)*t.amount}}}},layer:{category:{bringToFront:{fn:{appliesTo:["sprite","canvas"],obArg:!1,args:{},fn:(e,t,n)=>{if(1==n.game.sprites.length)return;let i=n.internal.renderer.layers,r=i.indexOf(e.idIndex);if(i[i.length-1]==e.idIndex)return;let s=i[i.length-1];i[i.length-1]=e.idIndex,i[r]=null;let a=i.length-2;for(;a>=0;){if(null==i[a])return void(i[a]=s);let e=i[a];i[a]=s,s=e,a--}}}},bringForwards:{fn:{appliesTo:["sprite","canvas","renderer"],obArg:!1,args:{},fn:(e,t,n)=>{if(1==n.game.sprites.length)return;let i=n.internal.renderer.layers,r=i.indexOf(e.idIndex);if(i[i.length-1]==e.idIndex)return;let s=i[r+1];i[r+1]=e.idIndex,i[r]=s}}},sendToBack:{fn:{appliesTo:["sprite","canvas","renderer"],obArg:!1,args:{},fn:(e,t,n)=>{if(1==n.game.sprites.length)return;let i=n.internal.renderer.layers,r=i.indexOf(e.idIndex);if(i[0]==e.idIndex)return;let s=i[0];i[0]=e.idIndex,i[r]=null;let a=1;for(;a<i.length;){if(null==i[a])return void(i[a]=s);let e=i[a];i[a]=s,s=e,a++}}}},sendBackwards:{fn:{appliesTo:["sprite","canvas","renderer"],obArg:!1,args:{},fn:(e,t,n)=>{if(1==n.game.sprites.length)return;let i=n.internal.renderer.layers,r=i.indexOf(e.idIndex);if(i[0]==e.idIndex)return;let s=i[r-1];i[r-1]=e.idIndex,i[r]=s}}}}},touching:{category:{mouse:{fn:{appliesTo:["sprite","canvas"],obArg:!0,args:{box:{required:!1,types:["object"],subcheck:{x:{required:!0,types:["number"],description:"The x position of the middle of the bounding box."},y:{required:!0,types:["number"],description:"The y position of the middle of the bounding box."},width:{required:!0,types:["number"],description:"The width of the bounding box."},height:{required:!0,types:["number"],description:"The height of the bounding box."}},description:"The bounding box to be used. If unspecified, the sprite's width, height, x and y coordinates will be used to make one."},mouseSize:{required:!1,default:0,types:["number"],description:"The size of the bounding box for the mouse. Defaults to one pixel."},mode:{required:!1,default:"touching",check:e=>{if(!["touching","overlap"].includes(e))return'Huh, looks like you used an invalid option for the "mode" argument. It can only be "touching" or "overlap" and you put '+JSON.stringify(e)+"."},description:'The touching mode. Defaults to "touching" but can also be "overlap".'}},fn:(e,t,n)=>{null==t.box?t.box={x:e.x-Math.abs(e.width/2),y:e.y-Math.abs(e.height/2),width:Math.abs(e.width),height:Math.abs(e.height)}:(t.box.x-=e.width/2,t.box.y-=e.height/2),"touching"==t.mode&&(t.box.width+=2,t.box.height+=2,t.box.x--,t.box.y--);let i,r=t.mouseSize/2;i=Bagel.device.is.touchscreen?n.input.touches:[{x:n.input.mouse.x,y:n.input.mouse.y}];let s=t.box;for(let t in i){let n=i[t];if(n.x-r<s.x+s.width&&n.x+r>s.x&&n.y-r<s.y+s.height&&n.y+r>s.y)return e.last.collision={x:n.x,y:n.y,type:"mouse"},!0}return!1}}},mouseCircles:{fn:{appliesTo:["sprite","canvas"],obArg:!0,args:{radius:{required:!1,types:["number"],description:"The radius of the bounding box. If unspecified, the sprite's width and height will be used to make one."},mouseRadius:{required:!1,default:1,types:["number"],description:"The radius of the bounding box for the mouse. Defaults to one pixel."},mode:{required:!1,default:"overlap",check:e=>{if(!["touching","overlap"].includes(e))return'Huh, looks like you used an invalid option for the "mode" argument. It can only be "touching" or "overlap" and you put '+JSON.stringify(e)+"."},description:'The touching mode. Defaults to "overlap" but can also be "touching".'}},fn:(e,t,n)=>{let i,r={x:e.x,y:e.y};null==t.radius?r.radius=Math.max(Math.abs(e.width),Math.abs(e.height))/2:r.radius=t.radius,"touching"==t.mode&&r.radius++,i=Bagel.device.is.touchscreen?n.input.touches:[{x:n.input.mouse.x,y:n.input.mouse.y}];let s=t.mouseRadius+r.radius;for(let t in i){let n=i[t];if(Math.sqrt(Math.pow(Math.abs(r.x-n.x),2)+Math.pow(Math.abs(r.y-n.y),2))<=s)return e.last.collision={x:n.x,y:n.y,type:"mouse"},!0}return!1}}},sprite:{fn:{appliesTo:["sprite","canvas"],obArg:!1,args:{sprite:{required:!0,types:["string"],description:"The id of the sprite to check against for a collision."},options:{required:!1,default:{},subcheck:{box:{required:!1,types:["object"],subcheck:{x:{required:!0,types:["number"],description:"The x position of the middle of the bounding box."},y:{required:!0,types:["number"],description:"The y position of the middle of the bounding box."},width:{required:!0,types:["number"],description:"The width of the bounding box."},height:{required:!0,types:["number"],description:"The height of the bounding box."}},description:"The bounding box to be used. If unspecified, the sprite's width, height, x and y coordinates will be used to make one."},mode:{required:!1,default:"overlap",types:["string"],check:e=>{if(!["touching","overlap"].includes(e))return'Huh, looks like you used an invalid option for the "mode" argument. It can only be "touching" or "overlap" and you put '+JSON.stringify(e)+"."},description:'The touching mode. Defaults to "overlap" but can also be "touching".'},include:{required:!1,default:{},types:["object"],subcheck:{clones:{required:!1,default:!0,types:["boolean"],description:"If this collision check includes clones or not."},invisibles:{required:!1,default:!1,types:["boolean"],description:"If this collision check includes invisible sprites or not."}},description:"A few options for whether or not some sprites should be included in the checks."}},types:["object"],description:"A few other options for this function."},check:{required:!1,types:["function"],description:"A function that does an additional check before a collision is reported. It's given the sprite that's being checked against, the current sprite and the game. (in that order)"}},fn:(e,t,n)=>{null==t.options.box?t.options.box={x:e.x-Math.abs(e.width/2),y:e.y-Math.abs(e.height/2),width:Math.abs(e.width),height:Math.abs(e.height)}:(t.options.box.x-=e.width/2,t.options.box.y-=e.height/2);let i=t.options.box;"touching"==t.options.mode&&(i.width+=2,i.height+=2,i.x--,i.y--);let r=[t.sprite],s=Bagel.get.sprite(t.sprite,n);t.options.includeClones&&(r=[...r,...s.cloneIDs]);let a=null==t.check;for(let s in r){let o=r[s];if(!t.options.include.invisibles&&!o.visible)continue;let l=e.x-Math.abs(e.width/2),d=e.y-Math.abs(e.height/2);if(i.x<l+Math.abs(e.width)&&i.x+i.width>l&&i.y<d+Math.abs(e.height)&&i.y+i.height>d&&(t.check&&(a=t.check(o,e,n)),a))return e.last.collision={sprite:o,type:"sprite"},!0}return!1}}}}}}},listeners:{prepState:(e,t)=>{let n=t.internal.scripts.index.sprites.init[e];if(null==n)return;for(let i in n){let r=n[i].sprite;if("sprite"==r.type&&r.img&&Bagel.get.asset.img(r.img,t,!0),r.request&&r.request[e])for(let n in r.request[e]){let i=t.internal.combinedPlugins.types.assets[n];null==i&&(console.error("Oops, the (plural) asset type "+JSON.stringify(n)+" doesn't exist in this game."),console.log("These are the only types:"),console.log(Object.keys(t.internal.combinedPlugins.types.assets).join("\n")),Bagel.internal.oops(t)),i=i.get;for(let s in r.request[e][n])Bagel.get.asset[i](r.request[e][n][s],t,!0)}}let r=t.internal.assets.assets.snds;if(r)for(i in r)r[i].pause()}}},vars:{audio:{autoPlay:!0,queue:[],createUnmute:(e,t)=>{if(!Bagel.get.sprite(".Internal.unmute",t,!0)){let n='plugin Internal\'s function "game.playSound"';t.add.asset.img({id:".Internal.unmuteButtonMuted",src:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUEAYAAADdGcFOAAAABmJLR0QA/wAAAAAzJ3zzAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH5AUECgYpH/xRLwAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAFGSURBVEhLpVbbtcMwCCs9d4brjbJzvVGmcD9clQBRIY4+mtgRWBZ+VB5LGCNjnEMkY1DMIdnA6PfPKtbjw4wg9HyusXeMMSrDiYhotI/gzpIPOugMPaauCcowBedCXUcM0NKPcfTVDiCyKjwT+vQBnqDCZqLzhDkQZ0uNimjPtm/7tof8cfHCEesM3q/wbTvje55zMD8GwKh66B1jmEKjk3/nRLY2FFaols4PdBdkDdaBXa7bpeZYFbcFAt7RX9wroAJtqXJPwPeO3oUTGNPaXZZD1zDa+vsLbM0/8dkSPe364l91dO5ebZMS83RrQvF2vJkmmHO99dabCD0Hs2PmqlAF4mx+7xyQXnU1oexrBPKxAx/OffmPEtjmyR1kE/SOvf57603P1W+8D2TA1TNnmLEjWAm9Y3rgf54xpAbyryOFF8SAzG9tVk73YdUDkgAAAABJRU5ErkJggg=="},n),t.add.asset.img({id:".Internal.unmuteButton",src:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUEAYAAADdGcFOAAAABmJLR0QA/wAAAAAzJ3zzAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH5AUECgoeC/S7LAAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAElSURBVEhLrZbREYQgDETNFaEdac3akTaR+2D2lODOBs/3w4gbWEJAbXiEu1LcY6YUkWRAa2je533e9YTbtE3bxBak44XgHJgZWsd1XMfY27Icy7EczDA3Sl5wY1lDCjMzM230Uz/mjWGC0g7duLu7x8i2FILBvLEYp2ALikbL/M346HAvAgSebdTVLdffx2fHv2Qweyp7yZZAMdpmstlibGm9JXpLs7peGoP/ks1YltcNvg01WNeEzkmsobf4GWSfpPqUaXr1gNXwB6/RgU9SpDdDvXqAa45w3kPIBGtrPdfdx/H7L17UoQbPFatro0zQnyEYi+OzzKX/Zp4aiihj5SxcEjWkYIdHG2Y7oYwBOQFAbZSBlLqFbSEzBtIGI+SvQ6IMRb66bmr2BeoT1QAAAABJRU5ErkJggg=="},n),t.add.asset.snd({id:".Internal.unmuteButtonClick",src:"data:audio/mpeg;base64,//OAxAAAAAAAAAAAAFhpbmcAAAAPAAAAAwAABBIAYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBg0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ////////////////////////////////////////////AAAAUExBTUUzLjk5cgRuAAAAAAAAAAA1CCQCzSEAAeAAAAQSSYAuqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/zoMQAEnACyvdAAACoh3exu8jLeoIAgcKAgCAYLggCAIHCgIAhggGIPv5cEFg+D+sH/Lg4CHDHKHP/E4PghrBwMcEAQygY//BB3//+UBAEAx/KAhlAQcmYqIp0mTVUZDWGgyrJEUQYImG/BhWy0KoEQVVjZUyyAxi+zMxAcDuX+WiWnPTQwFZ4BkIgoSlO0HWRLRa6sZiyXtxgTnQ+IAlwFSpHstGDFzlpJ1J2pzS2mZiuluTRlbS2DUE23jTFgFMVrERfV4GUv7edHTZbTQ3CYA37KYccp4lAmlpzMpkEOspbRYs7LIi8j8MQbxBMzxd8NvvB7KbTgw4yl1W5P67sEQS7sYhl3Zx/ZbS7xUzi6X+TZJFYkkYszmT+NETCdJdz7uFIpTWyjsNSdyauojEpdBNNLm408Oz/87DE4loUgqsfmMAkgxuzs47j+VGWS1u8Uf913bjdZa8ehyAaaIu7EXJhhwZC4VK+0ifZr0F/j++Y/vHW6WVZwzAUWZ7FrTIbUonIxGL0OUt+9G5e5cXmm3gp+aBnEQfaROjKqF/Zl/ZQ+smaVSuk8zSmJNFZVG2Gv9BMO/dpccfkF6/er5y/LC67MYfWchq4/0fVrKAIu7lfHLva4zAQE2q//1VFBQEBKMzKuGAhVCgJqs+qAnVAVL/6vxmKMx///9VTqr/V9VXjMUZvgYCAQNP9YKrBX8S8ShsSgrEVZ0RBwRPg1Bp5U7rBUeGpUNiU78RBwRPUIoilTusFR4KqTEFNRTMuOTkuM6qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqv/zYMTZGtmybx/DGACqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqg=="},n),t.add.asset.snd({id:".Internal.unmuteButtonClickUp",src:"data:audio/mpeg;base64,//OAxAAAAAAAAAAAAFhpbmcAAAAPAAAABAAABMkATk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnfn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn/////////////////////////////////AAAAUExBTUUzLjk5cgRuAAAAAAAAAAA1CCQDLCEAAeAAAATJpqKjmQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/zoMQAFogGux9BAAC6qruoVWtAv0iG8oCDgQcUBA4Jz8pLg+D+IAQd5cHwfB/rP/BAEDkoGPxACYPg+D5+GPUCAIA+/8HwQBAEAQBMH/h/8EAQBB3UCAPg+H/wQDH8HwfD8uD4Pg+qushml2hmlAYBrCAMEjTYbZgXKSJMQKAAkRyGgr9CoNCahLjLRnVAAhorQiAL13knoq8qAdYdH1rCt6FqmTC2dsHT1HCMoc2jWLUjdBRNoyJv4RMO0+0ZrvFNRFbrNnkWi/kVZar+lnZySwWwG4+ixJ1YkCQ+8pbp5BEMtK0x06WUrt+hjNymv+3B3YjhSwzfl+pU8dytnvDLdWPVbMpuUdPNWprdLVi9irfi0sqU17Ofj8Oyrct1AT9y2td13+c+7WpqbfJqlpatarGeX5Xf7rD/86DE0Up0bqMfmsAAw5zWN6btVKDKzU7yzzKbyps6amyrU1/uWWOOP8paa13k1DVLjTV7tumz7llnhjZyws6t1pfMyHcons7H42c6avS27mprVaNVbUzS/vdL3u/5l3+5fv7N/qaMNZRoSaM02g0LO2aTFcDsQUEAsfvVoGoStQqLLLRgFURCVLcDdYiz5huShkdWIiu/qVSyzERIxDHQ0pUtZNFTUECvRA6UymyVysMPMABBJfQHFAUZwC8TIEEyk3udBhrpMhGCRxJYZOULpBco1Sk7HGyX0/l6VsgUOZnGH2U2MI0A8GLUeKwWfCoQ8E0F/6BprcYalVlOdmj4tNyf6VXuLTLIuC/LorRbMhlMYw1BN2yzJs8dd2zGcZmTN3lUWlUzOQ3H4CpuPEw6G8qsZxtNxgFx//OwxNNQVC56/5jICGCqtSrclTdpucj0tjkRoKGIwTcd2njO6CtVptyqNXJuK0t2kpst61lv+a139Y6nN092prC19Ncls5QzfIlc3CL8BS+tasZXvm3RityJRaIxGw+0pj0Na7jvWWst/v//////UpprXcqYjJN1BQCkqkxBTUUzLjk5LjOqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqr/8xDE8QAAA/wBwAAAqqqqqqqqqqqqqqqqqg=="},n),t.add.asset.snd({id:".Internal.unmuteButtonMouseTouch",src:"data:audio/mpeg;base64,//OAxAAAAAAAAAAAAFhpbmcAAAAPAAAABAAABP0ASkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlfn5+fn5+fn5+fn5+fn5+fn5+fn5+fn5+fn/////////////////////////////////AAAAUExBTUUzLjk5cgRuAAAAAAAAAAA1CCQDKiEAAeAAAAT9uAw0pwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/zoMQAEjkqnedDMACyMBL0ufxEQvkRC3REREKgYACCEe7JpkEIy7Jp3Ed7Jp3vezyad7+9nkyd7BMmH8uf8Tg+8uD4f/l///Ln/5d//h///rB9//g+z7q7iFlKYTSjQDSknrKLQgSoGlObS2JMhgJeYvuY7AQqCdmwWOKDMmn7BoHzfVVVDBTsLMmppKqLp0vOKCTvSCZkpdHXbYjEnpVuWSqo6TdY6z5ur2PA3FrEqV1EYgu8tyumPPoyarCXtXFGr8QhiZqr8pX8uxX4koFKn9eB8HDaw7rWJFKXioqVfzRW6vxFrMeu5RKAaDjxU8JmvtRDCV2s7eEsvZyCV49gGxGsaOrQvzT1XFsZczpYai2PYeyq3GyRSXSh3n8sSyN237rWKSKdhqLdhtiVC9kzIIlEpq69DRb/86DE406karb/mMAFHbtWq4NWxKsdSq9Wpq3JuxT27ff5X7T3+U+X2/yntXm4vk5WpFjMzsGWX6qVJdG5dlTRyHoL7Wz/Gl7rKm3KvrV8cfpr8Zw5vm+/vt/G9V5re6lXCtV5KptKs5xLtdN7LRLNNperiCirq5ChNOZN0QkNyLgShrjSgaOXwEQpl1LzMZFtXeJAWG2ugwCMCNzUA833wAgJFXiAwSlUnkYugAJmMGF1gGAPCykUA0eGnu4YEOjRSYWOGAildqzYmsuUy5iUvmJQAQcyg7MCIzJAUw00r8huy/cNv9U28b9Oy0iBBCHGPhRiiWYIThCaZeuGOoyQ7WYu15yXHdWd9Z6msOrSf+Mpywm2TF6mlNJAaDCEAQJmBCbMc6sImYkzqn5TydmE5FtTFmXQHCpN//PAxNRcDG6O/5nYDVKDH5bfR9WU0pr5ggqBA8oDTAQqCi/xcpSpqVz8LlBLOfbs5VZXujdmZpdX5qLSKjpb0upn9qw66lmtflMqp05X1a6/rouqyKIwwBQqKvq+Evp6ksmbcY7m78VoYzhVr5T7cG3l0W5dptdpH/ll+zVtWfx5LUJyri9r6yHdWgjThQG12U2M5TSRrGgXNn3///3/P////99pfGdgs1VMQU1FVUxBTUUzLjk5LjNVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//MQxPIAAAP8AcAAAFVVVVVVVVVVVVVVVVU="},n);let i=Math.min(t.width,t.height)/10;t.add.sprite({id:".Internal.unmute",type:"sprite",img:".Internal.unmuteButtonMuted",visible:!1,scripts:{steps:{appearAnimation:e=>{e.vars.delay<30?e.vars.delay++:(e.visible=!0,e.width!=e.vars.size&&(e.width*=1.4,e.width>=e.vars.size&&(e.width=e.vars.size,e.vars.appearAnimation=!1),e.height=e.width))},deleteAnimation:e=>{e.width/=1.4,e.height=e.width,e.width<1&&e.delete()},expandAnimation:e=>{e.width*=1.025,e.width>e.vars.expandedSize&&(e.width=e.vars.expandedSize),e.height=e.width},shrinkAnimation:e=>{e.width!=e.vars.size&&(e.width/=1.025,e.width<e.vars.size&&(e.width=e.vars.size),e.height=e.width),e.vars.plugin.vars.audio.autoPlay&&(e.vars.delete=!0)},play:e=>{let n=e.vars.plugin.vars;for(let e in n.audio.queue){Bagel.get.asset.snd(n.audio.queue[e],t).play().then().catch()}n.audio.autoPlay=!0,n.audio.queue=[],e.img=".Internal.unmuteButton"},pause:e=>{let n=e.vars.plugin.vars;for(let e in t.internal.assets.assets.snds){let i=t.internal.assets.assets.snds[e];i.paused||(i.pause(),i.loop||i.duration>=5?n.audio.queue.push(e):i.currentTime=0)}n.audio.autoPlay=!1,e.img=".Internal.unmuteButtonMuted"}},all:[(e,t,n)=>{if(e.layer.bringToFront(),e.vars.appearAnimation)n("appearAnimation");else{t.input.mouse.down||(e.vars.clicked&&t.playSound(".Internal.unmuteButtonClickUp"),e.vars.clicked=!1);let i=e.vars.plugin.vars;e.vars.delete?n("deleteAnimation"):e.touching.mouseCircles()?(e.vars.touching||(t.playSound(".Internal.unmuteButtonMouseTouch"),e.vars.touching=!0),e.width!=e.vars.expandedSize&&n("expandAnimation"),t.input.mouse.down&&!e.vars.clicked&&(t.playSound(".Internal.unmuteButtonClick"),i.audio.autoPlay?n("pause"):n("play"),e.vars.clicked=!0)):(e.vars.touching=!1,n("shrinkAnimation"))}}]},vars:{plugin:e,size:i,expandedSize:1.1*i,delay:0,clicked:!1,delete:!1,touching:!1,appearAnimation:!0},x:i,y:t.height-i,width:1,height:1},'plugin Internal, function "game.playSound" (via Game.add.sprite)')}}}}},loadPlugin:(e,t,n,i)=>{let r=Bagel.internal.subFunctions.loadPlugin;e=Bagel.internal.deepClone(e);let s=Bagel.internal.current;Bagel.internal.saveCurrent(),s.plugin=e,e.args=n,e.plugin&&e.plugin.scripts&&e.plugin.scripts.preload&&e.plugin.scripts.preload(e,t,Bagel.step.plugin.scripts),(e=r.check(t,e,i)).args=Bagel.internal.deepClone(n);let a=r.merge;a.types.assets(t,e),a.types.sprites(t,e),a.methods(t,e),e.plugin.scripts.init&&e.plugin.scripts.init(e,t,Bagel.step.plugin.scripts),a.listeners(t,e),t.internal.plugins[e.info.id]=e,Bagel.internal.loadCurrent()},loadAsset:(e,t,n,i,r,s,a)=>{let o=Bagel.internal.current;Bagel.internal.saveCurrent(),o.asset=e,o.assetType=n,o.i=r,o.where=i,o.game=t;let l=t.internal.combinedPlugins.types.assets[n];if(null==l)return void console.warn("The asset type "+JSON.stringify(n)+" doesn't appear to exist for this game. You might want to check that the plugin that adds it's been loaded. In the game "+JSON.stringify(t.id)+".game.assets."+n+" item "+r+".");let d="dynamic"!=t.config.loading.mode||s||l.forcePreload&&!a,u=l.internal.plugin;o.plugin=u;let h,c=t.internal.assets;null==c.assets[n]&&(c.assets[n]={}),e=Bagel.check({ob:e,where:i,syntax:l.internal.args},Bagel.internal.checks.disableArgCheck),l.check&&(h=l.check(e,t,Bagel.internal.check,u,r)),h&&(Bagel.internal.loadCurrent(),console.error(h),console.log("In plugin "+JSON.stringify(u.info.id)+"."),Bagel.internal.oops(t));let g=((e,t)=>i=>{let r=t.internal.assets,s=t.internal.combinedPlugins,a=s.types.internal.pluralAssetTypes[s.types.assets[n].get];r.loadingIDs[a][e.id]=!1,r.assets[a][e.id]=i,r.loaded++,r.loading--,0==r.loading&&t.config.loading.skip&&(t.loaded=!0,Bagel.internal.subFunctions.init.onload(t))})(e,t);if(d){let i=t.internal.combinedPlugins,s=i.types.internal.pluralAssetTypes[i.types.assets[n].get];l.init(e,g,t,l.internal.plugin,r),c.loading++,null==c.loadingIDs[s]&&(c.loadingIDs[s]={}),c.loadingIDs[s][e.id]=!0}else{let s=t.internal.assets.toLoad;null==s[n]&&(s[n]={}),s[n][e.id]={ready:g,asset:e,assetLoader:l,i:r,where:i,game:t}}Bagel.internal.loadCurrent()},createSprite:(e,t,n,i,r,s)=>{let a=Bagel.internal.subFunctions.createSprite,o=t.internal.combinedPlugins;n?(e.type&&e.type!=n.type&&(console.error('Oops, clones have to have the same type as the parent. You can fix this by removing the "type" argument for this clone. If it needs to be that type, you should make a different parent for creating clones of that type.'),Bagel.internal.oops(t)),e.type=n.type):e.type=null==e.type?o.defaults.sprites.type:e.type;let l=o.types.sprites[e.type];if(null==l){let n=Object.keys(o.types.sprites);n.includes(e.type)||(e.id?console.error("Oops, you used an invalid sprite type. You tried to use "+JSON.stringify(e.type)+" for the sprite "+JSON.stringify(e.id)+". It can only be one of these:\n"+n.reduce((e,t)=>e+" • "+JSON.stringify(t)+" -> "+o.types.sprites[t].description+"\n","")):s?console.error("Oops, you used an invalid sprite type. You tried to use "+JSON.stringify(e.type)+" for sprite "+s+". It can only be one of these:\n"+n.reduce((e,t)=>e+" • "+JSON.stringify(t)+" -> "+o.types.sprites[t].description+"\n","")):console.error("Oops, you used an invalid sprite type. You tried to use "+JSON.stringify(e.type)+". It can only be one of these:\n"+n.reduce((e,t)=>e+" • "+JSON.stringify(t)+" -> "+o.types.sprites[t].description+"\n","")),Bagel.internal.oops(t))}let d=Bagel.internal.current,u=d.plugin?d.plugin.info.id:null;Bagel.internal.saveCurrent(),d.sprite=e,d.game=t,d.plugin=l.internal.plugin,r||(e=a.check(e,t,n,i,u)),e.internal={scripts:{init:[],main:[],all:[]},rerunListeners:[]},e.cloneIDs=[],e.cloneCount=0,e.isClone=!!n,e.idIndex=s;let h=a.register;return a.extraChecks(e,t,i,s),h.scripts("init",e,t,n),h.scripts("main",e,t,n),h.scripts("all",e,t,n),h.methods(e,t),h.listeners(e,t,n),t.internal.idIndex[e.id]=s,t.internal.renderer.layers.push(s),e.debug={renderTime:0,scriptTime:0},e.game=t,(t=>{e.clone=(e=>{let n=t,i=n.game;e=e||{};let r,s=Bagel.internal.findCloneID(n,i);null==e.id?(r=n.id+"#"+s,e.id=r):r=e.id,n.cloneIDs[s]=r,n.cloneCount++;let a=Bagel.internal.findSpriteID(i);(e=Bagel.internal.createSprite(e,i,n,'the function "sprite.clone"',!1,a)).cloneID=s,e.parent=n,i.game.sprites[a]=e,Bagel.internal.current.sprite=e;for(let t in e.scripts.init)e.scripts.init[t](e,i,Bagel.step.sprite);return Bagel.internal.current.sprite=n,e}),e.delete=(()=>{let e=t.game,n=Bagel.internal.subFunctions.delete;n.layers(t,e),n.scripts("init",t,e),n.scripts("main",t,e),n.scripts("all",t,e),n.misc(t,e)})})(e),a.init(e,t,a),Bagel.internal.loadCurrent(),e},tick:()=>{let e=Bagel.internal.subFunctions.tick,t=new Date;if("complete"==document.readyState)for(let t in Bagel.internal.games){let n=new Date,i=Bagel.internal.games[t];Bagel.internal.current.game=i,e.scaleCanvas(i),i.internal.pluginsDone?(i.state!=i.internal.lastPrepState&&(Bagel.internal.triggerPluginListener("prepState",i,i.state),0!=i.internal.assets.loading&&(i.loaded=!1,null==i.internal.loadingScreen&&Bagel.internal.subFunctions.init.loadingScreen(i)),i.internal.lastPrepState=i.state),i.loaded?e.loaded(i)&&(Bagel.internal.subFunctions.init.loadingScreen(i),e.loading(i)):e.loading(i)):0==i.internal.pluginsLoading&&(i.internal.pluginsDone=!0,Bagel.internal.subFunctions.init.onPluginsReady(i));let r=new Date;i.internal.FPSFrames++,i.maxPossibleFPS=1e3/(r-n),r-i.internal.lastFPSUpdate>=1e3&&(i.currentFPS=i.internal.FPSFrames,i.internal.FPSFrames=0,i.internal.lastFPSUpdate=r)}Bagel.internal.resetCurrent();new Date;e.tick()},subFunctions:{init:{check:e=>(null==e&&(console.log("Hmm, looks like you forgot the first argument for this function: the game JSON. It should be an object."),Bagel.internal.oops()),"object"!=typeof e&&(console.error("Oh no! Your game JSON seems to be the wrong type. It must be an object but you used "+Bagel.internal.an(Bagel.internal.getTypeOf(e))+"."),Bagel.internal.oops()),null==e.id&&(console.error("Oh no! You forgot to specifiy an id for the game."),Bagel.internal.oops()),e.internal={renderer:{type:"canvas",width:e.width,height:e.height,lastRender:new Date,layers:[],canvas:document.createElement("canvas"),ratio:e.width/e.height},ids:[],idIndex:{},FPSFrames:0,lastFPSUpdate:new Date,scripts:{index:{init:{},main:{},all:[],sprites:{init:{},main:{},all:[]}}},assets:{loading:0,loaded:0,assets:{},toLoad:{},loadingIDs:{},ranTasks:!1,assetsLoading:0},combinedPlugins:{types:{internal:{pluralAssetTypes:{}}},methods:{bagel:{},game:{},sprite:{}},defaults:{sprites:{type:"sprite"}},listeners:{}},lastState:!e.state,lastPrepState:!e.state,plugins:{},pluginsLoading:0,pluginsDone:!1},e=Bagel.check({ob:e,where:"GameJSON",syntax:Bagel.internal.checks.game},Bagel.internal.checks.disableArgCheck)),listeners:(e,t)=>{e.input={touches:[],mouse:{down:!1,x:e.width/2,y:e.height/2},keys:{keys:{}},lookup:{left:37,right:39,up:38,down:40,space:32,w:87,a:65,s:83,d:68}},(e=>{t("mousemove",t=>{let n=e.internal.renderer,i=n.canvas.getBoundingClientRect(),r=e.input.mouse;r.x=(t.clientX-i.left)/n.styleWidth*e.width,r.y=(t.clientY-i.top)/n.styleHeight*e.height},!1),t("mousedown",t=>{Bagel.device.is.touchscreen=!1,e.input.mouse.down=!0},!1),t("mouseup",t=>{e.input.mouse.down=!1,Bagel.internal.inputAction.input()},!1),t("touchstart",t=>{Bagel.device.is.touchscreen=!0;let n=e.internal.renderer,i=n.canvas.getBoundingClientRect(),r=e.input.mouse;if(null==t.touches)r.x=(t.clientX-i.left)/n.styleWidth*e.width,r.y=(t.clientY-i.top)/n.styleHeight*e.height,e.input.touches=[{x:e.input.mouse.x,y:e.input.mouse.y}];else{r.x=(t.touches[0].clientX-i.left)/n.styleWidth*e.width,r.y=(t.touches[0].clientY-i.top)/n.styleHeight*e.height,e.input.touches=[];for(let r in t.touches)e.input.touches.push({x:(t.touches[r].clientX-i.left)/n.styleWidth*e.width,y:(t.touches[r].clientY-i.top)/n.styleHeight*e.height})}r.down=!0,t.cancelable&&t.preventDefault()},!1),t("touchmove",t=>{Bagel.device.is.touchscreen=!0;let n=e.internal.renderer,i=n.canvas.getBoundingClientRect(),r=e.input.mouse;if(null==t.touches)r.x=(t.clientX-i.left)/n.styleWidth*e.width,r.y=(t.clientY-i.top)/n.styleHeight*e.height,e.input.touches=[{x:r.x,y:r.y}];else{r.x=(t.touches[0].clientX-i.left)/n.styleWidth*e.width,r.y=(t.touches[0].clientY-i.top)/n.styleHeight*e.height,e.input.touches=[];for(let r in t.touches)e.input.touches.push({x:(t.touches[r].clientX-i.left)/n.styleWidth*e.width,y:(t.touches[r].clientY-i.top)/n.styleHeight*e.height})}r.down=!0,t.cancelable&&t.preventDefault()},!1),t("touchend",t=>{Bagel.device.is.touchscreen=!0,e.input.touches=[],e.input.mouse.down=!1,t.cancelable&&t.preventDefault(),Bagel.internal.inputAction.input()},!1),e.input.keys.isDown=(t=>!!e.input.keys.keys[t]),"complete"==document.readyState?Bagel.internal.subFunctions.init.documentReady(e):document.addEventListener("readystatechange",()=>{"complete"==document.readyState&&Bagel.internal.subFunctions.init.documentReady(e)})})(e),0==Object.keys(Bagel.internal.games).length&&(document.addEventListener("keydown",e=>{for(let t in Bagel.internal.games){Bagel.internal.games[t].input.keys.keys[e.keyCode]=!0}},!1),document.addEventListener("keyup",e=>{for(let t in Bagel.internal.games){Bagel.internal.games[t].input.keys.keys[e.keyCode]=!1}},!1))},misc:e=>{e.loaded=!1,e.paused=!1,e.currentFPS=60,e.maxPossibleFPS=60;let t=e.internal.renderer;if("auto"==e.config.display.renderer){let t=document.createElement("canvas"),n=t.getContext("webgl")||t.getContext("experimental-webgl");e.config.display.renderer=null==n?"ctx":"webgl"}e.config.display.renderer="ctx","webgl"==e.config.display.renderer?t.ctx=t.canvas.getContext("webgl")||t.canvas.getContext("experimental-webgl"):t.ctx=t.canvas.getContext("2d"),t.canvas.id="Bagel.js "+e.id,t.ctx.imageSmoothingEnabled=e.config.display.antialiasing,t.canvas.width=e.width,t.canvas.height=e.height,"fill"==e.config.display.mode?t.canvas.style="margin:0;position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);":t.canvas.style="display: block; touch-action: none; user-select: none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0);",Bagel.internal.subFunctions.tick.scaleCanvas(e),(e=>{e.add={sprite:(t,n="the function Game.add.sprite")=>{n+=" -> the first argument";let i=Bagel.internal.findSpriteID(e);if(t=Bagel.internal.createSprite(t,e,!1,n,!1,i),e.game.sprites[i]=t,e.state==e.internal.lastState){let n=Bagel.internal.current;Bagel.internal.saveCurrent(),n.sprite=t,n.game=e;for(let n in t.scripts.init){let i=t.scripts.init[n];i.stateToRun==e.state&&"function"==typeof i.code&&i.code(t,e,Bagel.step.sprite)}Bagel.internal.loadCurrent()}return t},asset:{}},e.get={asset:{},sprite:(t,n)=>Bagel.get.sprite(t,e,n)},e.set={asset:{}},e.delete=(()=>{e.config.display.dom&&e.internal.renderer.canvas.remove(),delete Bagel.internal.games[e.id]})})(e)},scripts:(e,t)=>{let n=e.game.scripts[t],i=e.internal.scripts.index[t];for(let e in n){let r=n[e];if("all"==t)i.push({script:e});else{let t=r.stateToRun;null==i[t]&&(i[t]=[]),i[t].push({script:e})}}},initScripts:e=>{let t=Bagel.internal.subFunctions.init.scripts;t(e,"init"),t(e,"main"),t(e,"all")},plugins:e=>{for(let t in e.game.plugins){let n=e.game.plugins[t];e.internal.pluginsLoading++,((e,t,n,i)=>{fetch(t).then(r=>r.text().then(r=>{e.internal.pluginsLoading--,"object"!=typeof(r=new Function("return "+r)())&&(console.error("Erm, the plugin with the src "+JSON.stringify(t)+"isn't an object, it's "+Bagel.internal.an(Bagel.internal.getTypeOf(r))),Bagel.internal.oops(e)),Bagel.internal.loadPlugin(r,e,n,i)}))})(e,n.src,n.args?Bagel.internal.deepClone(n.args):{},t)}},assets:(e,t)=>{let n=e.game.assets;for(let i in n){let r=n[i];for(let n in r){let s=r[n];Bagel.internal.loadAsset(s,e,i,"GameJSON.game.assets."+i+" item "+n,n,!1,t)}}0==e.internal.assets.loading&&(e=>{e.loaded=!0,e.internal.loadingScreen&&(e.internal.loadingScreen.delete(),delete e.internal.loadingScreen),setTimeout(()=>{e.loaded&&Bagel.internal.subFunctions.init.onload(e)},0)})(e)},preloadTasks:e=>{let t=e.internal.assets;t.assetsLoading=t.loading;let n=e.game.scripts.preload;t.loading+=n.tasks.length,n.misc&&t.loading++},methods:e=>{let t=e.internal.combinedPlugins.methods.game;for(let n in t){let i=t[n];Bagel.internal.subFunctions.init.subMethods(e,i,n,e)}},subMethods:(e,t,n,i)=>{let r=!0;if(t.internal&&t.internal.isNotCategory&&(r=!1),r){i[n]||(i[n]={});for(let r in t)Bagel.internal.subFunctions.init.subMethods(e,t[r],r,i[n])}else{let r=!0;i.hasOwnProperty(n)&&(t.fn.overwrite||(r=!1,console.warn("Oops. We've got a conflict. Plugin "+JSON.stringify(t.internal.plugin.info.id)+" tried to overwrite the "+JSON.stringify(n)+" property in the game "+JSON.stringify(e.id)+' without having the correct tag. The overwrite has been blocked.\nIf you want to overwrite the older type definition, add this to the function JSON: "overwrite: true".'))),r&&((e,t,n,i)=>{e.fn.normal?i[n]=e.fn.fn:e.fn.obArg?i[n]=(i=>{null==i&&(console.error("Oops, this function takes one argument: an object. You didn't give any arguments."),Bagel.internal.oops(t)),"object"!=Bagel.internal.getTypeOf(i)&&(console.error("Huh, looks like you used "+Bagel.internal.an(Bagel.internal.getTypeOf(i))+" instead of an object."),Bagel.internal.oops(t)),Bagel.internal.saveCurrent(),current.game=t,current.plugin=e.internal.plugin,i=Bagel.check({ob:i,syntax:e.fn.args,where:"game "+t.id+"'s "+JSON.stringify(n)+" method"},Bagel.internal.checks.disableArgCheck);let r=e.fn.fn(t,i,current.plugin);return Bagel.internal.loadCurrent(),r}):i[n]=((...i)=>{let r=Object.keys(e.fn.args),s={};for(let e in i)null==r[e]&&(r[e]="Your "+Bagel.internal.th(parseInt(e))+" argument"),s[r[e]]=i[e];let a=Bagel.internal.current;Bagel.internal.saveCurrent(),a.game=t,a.plugin=e.internal.plugin,s=Bagel.check({ob:s,syntax:e.fn.args,where:"game "+t.id+"'s "+JSON.stringify(n)+" method"},Bagel.internal.checks.disableArgCheck,!1,"Btw, the arguments go in this order: "+r.join(", ")+".");let o=e.fn.fn(t,s,e.internal.plugin);return Bagel.internal.loadCurrent(),o})})(t,e,n,i)}},initSprites:e=>{for(let t in e.game.sprites){Bagel.internal.createSprite(e.game.sprites[t],e,!1,"GameJSON.game.sprites item "+t,!1,parseInt(t))}},loadingScreen:e=>{if(!e.config.loading.skip){Bagel.internal.saveCurrent(),Bagel.internal.current.plugin=e.internal.plugins.Internal;let t=Bagel.internal.deepClone(e.config.loading.animation);t.id=".Internal.loadingScreen."+e.id,t.width=e.width,t.height=e.height,t.config={loading:{skip:!0,mode:"preload"},display:{resolution:"fixed",dom:!1,backgroundColour:"transparent"},disableBagelJSMessage:!0},null==t.vars&&(t.vars={}),t.vars.loading={progress:0,loaded:0,loading:e.internal.assets.loading,done:!1,game:e},t=Bagel.init(t),e.internal.loadingScreen=t,Bagel.internal.loadCurrent()}},documentReady:e=>{e.config.display.dom&&(e.config.display.htmlElementID?document.getElementById(e.config.display.htmlElementID).appendChild(e.internal.renderer.canvas):null!=document.body?document.body.appendChild(e.internal.renderer.canvas):document.appendChild(e.internal.renderer.canvas)),Bagel.internal.subFunctions.init.plugins(e)},onload:e=>{let t=e.game.sprites;for(let n in t)Bagel.internal.subFunctions.createSprite.triggerListeners(t[n],e)},onPluginsReady:e=>{let t=Bagel.internal.subFunctions.init;t.methods(e),t.assets(e),t.preloadTasks(e),t.initScripts(e),t.initSprites(e)}},loadPlugin:{check:(e,t,n)=>{let i=Bagel.internal.current;return Bagel.internal.saveCurrent(),i.plugin=t,i.game=e,t=Bagel.check({ob:t,syntax:Bagel.internal.checks.plugin,where:t.info&&t.info.id?"plugin "+t.info.id:"GameJSON.game.plugins item "+n},Bagel.internal.checks.disableArgCheck),Bagel.internal.loadCurrent(),t},merge:{types:{assets:(e,t)=>{let n=t.plugin.types.assets,i=e.internal.combinedPlugins;for(let r in n){let s=n[r];null==i.types.assets&&(i.types.assets={});let a=!1;null!=i.types.assets[r]?s.overwrite?a=!0:console.warn("Oops. We've got a conflict. Plugin "+JSON.stringify(t.info.id)+" tried to overwrite the "+JSON.stringify(r)+' asset type without having the correct tag. The overwrite has been blocked.\nIf you want to overwrite the older type definition, add this to the new type JSON: "overwrite: true".'):a=!0,a&&(i.types.assets[r]=s,i.types.assets[r].internal={plugin:t,args:{...Bagel.internal.checks.assets,...s.args}},i.types.internal.pluralAssetTypes[s.get]=r,((t,n,i,r)=>{Bagel.get.asset[i.get]=((e,s,a)=>{let o=Bagel.internal.current,l=n.internal.combinedPlugins.types.internal.pluralAssetTypes[i.get];null==(s=null==s?o.game:s)&&(console.error("Oops. Looks like you're trying to run this function outside of a script. Try moving it and trying again. Alternatively, you can pass the game object in as the second argument to this function to fix this issue."),Bagel.internal.oops()),null==e&&(console.error('Huh, looks like you forgot the "id" argument (the first argument). That\'s the id for the asset you want to get.'),Bagel.internal.oops(s)),Bagel.internal.saveCurrent(),o.assetType=t,o.assetTypeName=i.get,o.game=s,o.plugin=r;let d=s.internal.assets,u=d.assets[t],h=!0;if(u&&u[e]&&(h=!1),h){let t=d.toLoad[l];if(t&&(t=t[e]),!t&&d.loadingIDs[l]&&d.loadingIDs[l][e])return Bagel.internal.loadCurrent(),!0;if(t){let n=t;return o.i=n.i,o.where=n.where,n.assetLoader.init({...n.asset},n.ready,n.game,n.assetLoader.internal.plugin,n.i),n.game.internal.assets.loading++,null==d.loadingIDs[l]&&(d.loadingIDs[l]={}),d.loadingIDs[l][n.asset.id]=!0,d.toLoad[l]&&d.toLoad[l][e]&&delete d.toLoad[l][e],Bagel.internal.loadCurrent(),!0}if(Bagel.internal.loadCurrent(),a)return!1;console.error("Oops. That asset doesn't exist. You tried to get the asset with the id "+JSON.stringify(e)+"."),Bagel.internal.oops(n)}let c=u[e];return Bagel.internal.loadCurrent(),c}),n.get.asset[i.get]=((e,t)=>Bagel.get.asset[i.get](e,n,t)),n.add.asset[i.get]=((t,r)=>{null==t&&(console.error('Oops, looks like you forgot the "asset" argument (the first argument). That\'s the arguments for the asset as an object.'),Bagel.internal.oops(e)),"object"!=typeof t&&(console.error('Huh, looks like you used the wrong type for the "asset" argument (the first argument). That\'s the arguments for the asset as an object. You tried to use '+JSON.stringify(t)+"."),Bagel.internal.oops(e)),r||(r="the function Game.add.asset."+i.get);let s=e.internal.combinedPlugins.types.internal.pluralAssetTypes[i.get];Bagel.internal.loadAsset(t,n,s,r,!0)}),n.set.asset[i.get]=((t,r,s,a,o)=>{null==r&&(console.error('Oops, looks like you forgot the "asset" argument (the first argument). That\'s the value for this asset to be set to.'),Bagel.internal.oops(e)),null==t&&(console.error('Hmm, looks like you forgot the "id" argument (the second argument). It\'s the id of the asset to be changed.'),Bagel.internal.oops(e)),"string"!=typeof t&&(console.error("Oops, looks like you used the wrong type for the \"id\" argument (the second argument). It's the id of the asset to be changed. It's supposed to be a string but you tried to use "+Bagel.internal.an(Bagel.internal.getTypeOf(t))+"."),Bagel.internal.oops(e)),o||(o="the function Game.set.asset."+i.get);let l=n.internal.assets.assets,d=n.internal.combinedPlugins.types.internal.pluralAssetTypes[i.get];if(null==l[d]&&(l[d]={}),l[d][t]&&!s){if(a)return!0;console.error('Huh, looks like that id is already being used. You can try setting the "overwrite" argument (the 3rd one) to true if you\'re happy with overwriting. Otherwise check the id or use the "check" argument (the 4th) to return true instead of an error when the asset already exists.'),Bagel.internal.oops(e)}l[d][t]=r})})(r,e,s,t))}},sprites:(e,t)=>{let n=t.plugin.types.sprites,i=e.internal.combinedPlugins;for(let e in n){let r=n[e];null==i.types.sprites&&(i.types.sprites={});let s=!1;if(null!=i.types.sprites[e]?r.overwrite?s=!0:console.warn("Oops. We've got a conflict. Plugin "+JSON.stringify(t.info.id)+" tried to overwrite the "+JSON.stringify(e)+' sprite type without having the correct tag. The overwrite has been blocked.\nIf you want to overwrite the older type definition, add this to the new type JSON: "overwrite: true".'):s=!0,s){let n={...Bagel.internal.checks.sprite.clones.syntax};if(r.cloneArgs){for(let e in r.cloneArgs)n[e]=r.cloneArgs[e].syntax;r.cloneArgs={...r.cloneArgs,...Bagel.internal.checks.sprite.clones.args}}r.args={...r.args,...Bagel.internal.checks.sprite.sprite},r.internal={plugin:t,cloneSyntax:n},i.types.sprites[e]=r}}}},method:(e,t,n,i,r,s,a,o,l)=>{let d=!1;null==r[a]?d=!0:s.overwrite?d=!0:i?console.warn("Oops. We've got a conflict. Plugin "+JSON.stringify(t.info.id)+" tried to overwrite the "+JSON.stringify(a)+" method for the "+i+' type without having the correct tag. The overwrite has been blocked.\nIf you want to overwrite the older method, add this to the method JSON: "overwrite: true".'):console.warn("Oops. We've got a conflict. Plugin "+JSON.stringify(t.info.id)+" tried to overwrite the "+JSON.stringify(a)+" "+n+' method without having the correct tag. The overwrite has been blocked.\nIf you want to overwrite the older method, add this to the method JSON: "overwrite: true".'),d&&(s.internal={plugin:t,isNotCategory:!0},"bagel"==n?s.fn.normal?o[a]=s.fn.fn:((e,t,n,i,r)=>{i.fn.obArg?o[t]=(e=>{null==e&&(e={}),"object"!=Bagel.internal.getTypeOf(e)&&(console.error("Huh, looks like you used "+Bagel.internal.an(Bagel.internal.getTypeOf(e))+" instead of an object."),Bagel.internal.oops()),e=Bagel.check({ob:e,syntax:i.fn.args,where:"the Bagel.js method "+JSON.stringify(r+"."+t)},Bagel.internal.checks.disableArgCheck);let n=Bagel.internal.current;Bagel.internal.saveCurrent(),n.plugin=i.internal.plugin;let s=i.fn.fn(e,n.plugin);return Bagel.internal.loadCurrent(),s}):o[t]=((...e)=>{let n=Object.keys(i.fn.args),s={};for(let t in e)null==n[t]&&(n[t]="Your "+Bagel.internal.th(parseInt(t))+" argument"),s[n[t]]=e[t];s=Bagel.check({ob:s,syntax:i.fn.args,where:"the Bagel.js method "+JSON.stringify(r+"."+t)},Bagel.internal.checks.disableArgCheck,!1,"Btw, the arguments go in this order: "+n.join(", ")+".");let a=Bagel.internal.current;Bagel.internal.saveCurrent(),a.plugin=i.internal.plugin;let o=i.fn.fn(s,a.plugin);return Bagel.internal.loadCurrent(),o})})(0,a,0,s,l):r[a]=s)},subMethods:(e,t,n,r,s,a,o,l,d,u)=>{let h=Bagel.internal.subFunctions.loadPlugin.merge;if(r.category){let i=a;"bagel"==n?(d[s]||(d[s]={}),d=d[s],""!=u&&(u+="."),u+=s):"sprite"!=n&&(a[s]||(a[s]={}),a=a[s],""!=u&&(u+="."),u+=s),l.push(s);for(let s in r.category)h.subMethods(e,t,n,r.category[s],s,a,i,l,d,u)}else if("sprite"==n){for(let i in r.fn.appliesTo){let o=a,u=r.fn.appliesTo[i];null==a[u]&&(a[u]={}),a=a[u];for(let e in l){let t=l[e];null==a[t]&&(a[t]={}),a=a[t]}h.method(e,t,n,u,a,r,s,d),a=o}if(r.apply){null==o[r.apply.from]&&(console.error("Oops, Bagel.js can't copy that method from the sprite type "+JSON.stringify(r.apply.from)+" because it doesn't seem to exist. Check the plugin that adds it has been loaded first. Also check the names and categories. In plugin "+t.info.id+"."),Bagel.internal.oops(e));let u=o[r.apply.from][s];for(i in r.apply.to){let o=a,c=r.apply.to[i];null==a[c]&&(a[c]={}),a=a[c];for(let e in l){let t=l[e];null==a[t]&&(a[t]={}),a=a[t]}h.method(e,t,n,c,a,u,s,d),a=o}}}else h.method(e,t,n,null,a,r,s,d,u)},methods:(e,t)=>{let n=e.internal.combinedPlugins,i=["bagel","game","sprite"];for(let r in i){let s=i[r],a=t.plugin.methods[s];for(let i in a){let r=a[i],o=n.methods[s];Bagel.internal.subFunctions.loadPlugin.merge.subMethods(e,t,s,r,i,o,o,[],Bagel,"")}}},listeners:(e,t)=>{let n=e.internal.combinedPlugins.listeners,i=t.plugin.listeners;for(let e in i){let r=i[e];r&&(null==n[e]&&(n[e]=[]),n[e].push({fn:r,plugin:t}))}}}},createSprite:{check:(e,t,n,i,r)=>{let s=t.internal.combinedPlugins.types.sprites[e.type];if(n){null==s.cloneArgs&&(console.error("Oops, the sprite type "+JSON.stringify(n.type)+" doesn't support clones."),Bagel.internal.oops(t)),e=Bagel.check({ob:e,where:i,syntax:s.internal.cloneSyntax},{args:!0,missing:!0});let r=Bagel.internal.deepClone;for(let t in s.cloneArgs){let i=s.cloneArgs[t];if("replace"==i.mode)e.hasOwnProperty(t)||(n.clones.hasOwnProperty(t)?e[t]=r(n.clones[t]):e[t]=r(n[t]));else if("merge"==i.mode){if(!(n.hasOwnProperty(t)||n.clones.hasOwnProperty(t)||e.hasOwnProperty(t))){e[t]=i.syntax.default;continue}e[t]=Object.assign(r(n[t]),r(n.clones[t]),r(e[t]))}else"ignore"==i.mode&&(e.hasOwnProperty(t)||(n.clones.hasOwnProperty(t)?e[t]=r(n.clones[t]):e[t]=i.syntax.default))}if(s.check){let n=s.check(e,t,Bagel.check,i);n&&(console.error(n),Bagel.internal.oops(t))}return e}let a=Bagel.internal.current;return Bagel.internal.saveCurrent(),a.sprite=e,a.game=t,e=Bagel.check({ob:e,where:i,syntax:n?s.internal.cloneSyntax:s.args},Bagel.internal.checks.disableArgCheck),Bagel.internal.loadCurrent(),e},extraChecks:(e,t,n,i)=>{let r=t.internal.combinedPlugins.types.sprites[e.type];if(null==r.check)return;let s=Bagel.internal.current,a=r.check(e,t,Bagel.check,s.plugin,i,n);a&&(console.error(a),console.log("In "+n),Bagel.internal.oops(t))},init:(e,t,n)=>{let i=Bagel.internal.current;Bagel.internal.saveCurrent(),i.sprite=e,i.game=t;let r=t.internal.combinedPlugins.types.sprites[e.type],s=r.internal.plugin;i.plugin=s,r.init&&r.init(e,t,i.plugin),t.loaded&&n.triggerListeners(e,t),Bagel.internal.loadCurrent()},triggerListeners:(e,t)=>{let n=t.internal.combinedPlugins.types.sprites[e.type];n.internal.plugin;if(n.listeners.trigger)for(let i in n.listeners.property)Bagel.internal.triggerSpriteListener("set",i,e,t,!0)},register:{scripts:(e,t,n,i)=>{let r,s=t.scripts[e],a=n.internal.scripts.index.sprites[e];for(let o in s)r=i?n.state:"all"==e?null:s[o].stateToRun,"all"==e?(a.push({script:o,sprite:t,isClone:t.isClone}),t.internal.scripts[e].push({id:a.length-1})):(null==a[r]&&(a[r]=[]),a[r].push({script:o,sprite:t,isClone:t.isClone}),t.internal.scripts[e].push({id:a[r].length-1,state:r}))},subMethods:(e,t,n,i,r)=>{let s=!0;if(e.internal&&e.internal.isNotCategory&&(s=!1),s){i[t]||(i[t]={});for(let s in e)Bagel.internal.subFunctions.createSprite.register.subMethods(e[s],s,n,i[t],r)}else((e,t,n,i,r)=>{let s=e.fn;s.normal?i[r]=s.fn:s.obArg?i[r]=(i=>{null==i&&(i={}),"object"!=Bagel.internal.getTypeOf(i)&&(console.error("Huh, looks like you used "+Bagel.internal.an(Bagel.internal.getTypeOf(i))+" instead of an object."),Bagel.internal.oops(n)),i=Bagel.check({ob:i,syntax:s.args,where:"the sprite "+t.id+"'s "+JSON.stringify(r)+" method"},Bagel.internal.checks.disableArgCheck);let a=Bagel.internal.current;Bagel.internal.saveCurrent(),a.sprite=t,a.game=n,a.plugin=e.internal.plugin;let o=s.fn(t,i,n,a.plugin);return Bagel.internal.loadCurrent(),o}):i[r]=((...i)=>{let a=Object.keys(s.args),o={};for(let e in i)null==a[e]&&(a[e]="Your "+Bagel.internal.th(parseInt(e))+" argument"),o[a[e]]=i[e];o=Bagel.check({ob:o,syntax:s.args,where:"the sprite "+t.id+"'s "+JSON.stringify(r)+" method"},Bagel.internal.checks.disableArgCheck,!1,"Btw, the arguments go in this order: "+a.join(", ")+".");let l=Bagel.internal.current;Bagel.internal.saveCurrent(),l.sprite=t,l.game=n,l.plugin=e.internal.plugin;let d=s.fn(t,o,n,l.plugin);return Bagel.internal.loadCurrent(),d})})(e,n,r,i,t)},methods:(e,t)=>{let n=t.internal.combinedPlugins.methods.sprite[e.type];if(null!=n)for(let i in n)Bagel.internal.subFunctions.createSprite.register.subMethods(n[i],i,e,e,t)},listeners:(e,t,n)=>{let i=t.internal.combinedPlugins.types.sprites[e.type],r=i.listeners;e.internal.properties={};for(let n in r.property){let s=r.property[n];e.internal.properties[n]=e[n],((e,t,n,i,r)=>{let s=()=>(Bagel.internal.triggerSpriteListener("get",t,e,n,!1),e.internal.properties[t]),a=i=>{e.internal.properties[t]!=i&&(e.internal.properties[t]=i,Bagel.internal.triggerSpriteListener("set",t,e,n,!1))};(r.get||r.set)&&(r.get&&r.set?Object.defineProperty(e,t,{get:s,set:a}):r.get?Object.defineProperty(e,t,{get:s,set:n=>{e.internal.properties[t]=n}}):Object.defineProperty(e,t,{get:()=>e.internal.properties[t],set:a}))})(e,n,t,i.internal.plugin,s)}}}},tick:{scripts:(e,t,n,i)=>{if(null==Bagel.internal.games[n.id])return;let r;if(null==(r=t?"all"==e?n.internal.scripts.index.sprites[e]:n.internal.scripts.index.sprites[e][i]:"all"==e?n.internal.scripts.index[e]:n.internal.scripts.index[e][i]))return;let s={};for(let i in r){let a=r[i];if(null!=a)if(t){let t,i=a.sprite;if(Bagel.internal.current.sprite=i,"init"==e&&(i.visible=!0),"function"==typeof(t="all"==e||i.isClone?i.scripts[e][a.script]:i.scripts[e][a.script].code)){s[i.id]||(s[i.id]=!0,i.debug.scriptTime=0);let e=performance.now();t(i,n,Bagel.step.sprite),i.debug.scriptTime+=performance.now()-e}}else{let t;(t="all"==e?n.game.scripts[e][a.script]:n.game.scripts[e][a.script].code)(n,Bagel.step.sprite)}}},pluginScripts:e=>{for(let t in e.internal.plugins){let n=e.internal.plugins[t];Bagel.internal.saveCurrent(),Bagel.internal.current.plugin=n,n.plugin.scripts.main&&n.plugin.scripts.main(n,e,Bagel.step.plugin.scripts),Bagel.internal.loadCurrent()}},hideSprites:e=>{let t=e.game.sprites,n=0;for(;n<t.length;){let e=t[n];null!=e?(e.isClone?e.delete():0==e.scripts.all.length&&(e.visible=!1),n++):n++}},render:{ctx:e=>{let t=e.internal.renderer,n=t.canvas,i=t.ctx,r=n.width/e.width,s=n.height/e.height,a=t.layers,o=e.internal.combinedPlugins.types.sprites,l=e.config.display.backgroundColour;"transparent"==l?i.clearRect(0,0,n.width,n.height):(i.fillStyle=l,i.fillRect(0,0,n.width,n.height));for(let t in a){let l=e.game.sprites[a[t]],d=o[l.type];if(l.visible&&null!=d.render){d.render.clean||i.save();let t=performance.now();d.render.ctx(l,i,n,e,d.internal.plugin,r,s),l.debug.renderTime=performance.now()-t,d.render.clean||i.restore()}}},webgl:e=>{}},processSprites:e=>{for(let t in e.game.sprites){let n=e.game.sprites[t];if(null==n)continue;let i=[...n.internal.rerunListeners];n.internal.rerunListeners=[];for(let t in i)Bagel.internal.triggerSpriteListener(i[t][0],i[t][1],n,e,!1)}},loaded:e=>{let t=Bagel.internal.subFunctions.tick;if(!e.paused){let n=e.state;if(n!=e.internal.lastState){if(Bagel.internal.triggerPluginListener("state",e,n),0!=e.internal.assets.loading)return e.loaded=!1,!0;t.hideSprites(e),t.scripts("init",!0,e,n),t.scripts("init",!1,e,n),e.internal.lastState=n}if(t.pluginScripts(e),t.processSprites(e),t.scripts("main",!0,e,n),t.scripts("main",!1,e,n),t.scripts("all",!0,e,n),t.scripts("all",!1,e,n),null==Bagel.internal.games[e.id])return;t.render[e.config.display.renderer](e)}},loading:e=>{if(!e.config.loading.skip){let t=e.internal.renderer,n=t.canvas,i=t.ctx,r=e.internal.loadingScreen,s=e.internal.assets,a=r.vars.loading;if(s.loading+s.loaded==0?a.progress=100:a.progress=s.loaded/(s.loading+s.loaded)*100,a.loaded=s.loaded,a.loading=s.loading,0!=s.loading){let t=e.game.scripts.preload;if(s.loading<=s.assetsLoading)if(s.ranTasks)t.misc&&1==s.loading&&(t.misc(e),s.loaded++,s.loading--);else{let n=(e=>()=>{e.loaded++,e.loading--})(s);for(let i in t.tasks)t.tasks[i](e,n);s.ranTasks=!0}}let o=e.config.display.backgroundColour;"transparent"==o?i.clearRect(0,0,n.width,n.height):(i.fillStyle=o,i.fillRect(0,0,n.width,n.height)),i.drawImage(r.internal.renderer.canvas,0,0,n.width,n.height),r.vars.loading.done&&(e.loaded=!0,Bagel.internal.subFunctions.init.onload(e),r.delete(),delete e.internal.loadingScreen)}},tick:()=>{Bagel.internal.requestAnimationFrame.call(window,Bagel.internal.tick)},scaleCanvas:e=>{let t,n,i=window.innerWidth,r=window.innerHeight,s=e.internal.renderer.ratio,a=i/s;r>a?r=a:r!=a&&(i=r*s);let o=e.config.display.resolution;"full"==o?(t=i*window.devicePixelRatio,n=r*window.devicePixelRatio):"fixed"==o?(t=e.width,n=e.height):(t=o[0],n=o[1]),t=Math.ceil(t),n=Math.ceil(n);let l=e.internal.renderer,d=l.canvas;d.width==t&&d.height==n||(d.width=t,d.height=n),d.style.width=i+"px",d.style.height=r+"px",l.styleWidth=i,l.styleHeight=r,l.ctx.imageSmoothingEnabled=e.config.display.antialiasing}},delete:{layers:(e,t)=>{let n=t.internal.renderer,i=n.layers.indexOf(e.idIndex);n.layers=n.layers.filter((e,t)=>t!=i)},scripts:(e,t,n)=>{let r=t.internal.scripts[e];if(0==Object.keys(r).length)return;let s=n.internal.scripts.index.sprites[e];for(let t in r){let n=r[t];"all"==e?s[n.id]=null:s[n.state][n.id]=null}let a=[];for(i in s){let t=0;"all"!=e&&(a=[]);let n="all"==e?"a":s[i];for(let r in n){let o;null==(o="all"==e?s[i]:n[r])?t++:("all"==e?o.sprite.internal.scripts[o.script].id-=t:o.sprite.internal.scripts[e][o.script].id-=t,a.push(o))}"all"!=e&&(s[i]=a)}"all"==e&&(n.internal.scripts.index.sprites[e]=a)},misc:(e,t)=>{t.game.sprites[e.idIndex]=null,t.internal.idIndex[e.id]=null,e.isClone&&(e.parent.cloneCount--,e.parent.cloneIDs[e.parent.cloneIDs.indexOf(e.id)]=null)}}},checks:{game:{id:{required:!0,check:e=>{if(null!=Bagel.internal.games[e])return"Oh no! You used an id for your game that is already being used. Try and think of something else.\nYou used "+JSON.stringify(e)+' in "GameJSON.id".';Bagel.internal.current;let t=e.split(".")[1],n=Bagel.internal.currentStack,i=0==n.length?null:n[n.length-1].plugin;i&&(i=i.info.id),"."==e[0]&&(null==i?(console.error("This is awkward... IDs starting with a dot are only for plugins. You tried to use the id "+JSON.stringify(e)+". In GameJSON.id.\nIf it's important that it has this name, you could write a plugin instead, just make sure its id is set to "+JSON.stringify(t)+" ;)"),Bagel.internal.oops()):t!=i&&(console.error("Erm... the only reserved prefix you can use in this plugin is "+JSON.stringify("."+i)+" and you tried to use the id "+JSON.stringify(e)+'In GameJSON.id.\nYou can fix this by changing the prefix, removing it or changing the plugin id in "Plugin.info.id".'),Bagel.internal.oops()))},types:["string"],description:"An id for the game canvas so it can be referenced later in the program."},width:{required:!0,types:["number"],description:"The virtual width for the game. Independent from the rendered width."},height:{required:!0,types:["number"],description:"The virtual height for the game. Independent from the rendered height."},game:{required:!1,default:{},subcheck:{assets:{required:!1,default:{},types:["object"],description:"The assets you want to load for your game, organised by type. e.g imgs: [<asset1>,<asset2>]"},sprites:{required:!1,default:[],types:["array"],description:"The array that contains the all the sprite JSON."},scripts:{required:!1,default:{},subcheck:{init:{required:!1,default:[],arrayLike:!0,subcheck:{code:{required:!0,types:["function"],description:'The code to be run when the "stateToRun" property matches the game state.'},stateToRun:{required:!0,types:["string"],description:"The state when this script will be run."}},types:["array"],description:"Init scripts. They run on a state change."},main:{required:!1,default:[],arrayLike:!0,subcheck:{code:{required:!0,types:["function"],description:'The code to be run when the "stateToRun" property matches the game state.'},stateToRun:{required:!0,types:["string"],description:"The state when this script will be run."}},types:["array"],description:"Main scripts. They run every frame where the states match."},all:{required:!1,default:[],check:e=>{if("function"!=typeof e)return"Huh. This should be a function but you used "+Bagel.internal.an(Bagel.internal.getTypeOf(e))+"."},checkEach:!0,types:["array"],description:'"All" scripts. They run every frame regardless of game state.'},preload:{required:!1,default:{},subcheck:{tasks:{required:!1,default:[],types:["array"],check:e=>{if("function"!=typeof e)return"Oops, this is supposed to be a function but you tried to use "+Bagel.internal.an(Bagel.internal.getTypeOf(e))+"."},checkEach:!0,description:"Tasks that need to be completed before the game loads (but after the assets have loaded). The arguments provided are the game object followed by a ready function. This function must be called once the task is completed (otherwise the game won't load)."},misc:{required:!1,types:["function"],description:"A function for performing miscellaneous preload tasks that can be completed asynchronously. The function is called with the game object as its first argument."}},types:["object"],description:'Code that needs to be run before the game can load. "tasks" and "misc".'}},types:["object"],description:'The object that contains all the game scripts ("init" and "main") that aren\'t for a sprite.'},plugins:{required:!1,default:[],arrayLike:!0,subcheck:{src:{required:!0,types:["string"],description:"The src of the plugin file. Should be a json or js file."},options:{required:!1,types:["object"],description:"May not apply to all plugins but includes options for how they behave."}},types:["array"],description:"The plugins to load for this game. Plugins enhance Bagel.js' abilities or make certain things easier."}},types:["object"],description:"Where most of the properties are."},state:{required:!0,types:["string"],description:"The game's initial state. Game states control which sprites are active."},config:{required:!1,default:{},subcheck:{display:{required:!1,default:{},types:["object"],subcheck:{mode:{required:!1,default:"fill",check:e=>{if(!["fill","static"].includes(e))return"Oops! You used an invalid option. You used "+JSON.stringify(e)+', it can only be either "fill" or "static".'},types:["string"],description:"The display mode. e.g static (always the same size) or fill (fills the whole window)."},resolution:{required:!1,default:"full",types:["string","array"],check:e=>{if("string"==typeof e){if(!["full","fixed"].includes(e))return'Oops, this can only be "full", "fixed" or a custom resolution using an array.'}else{if(!Array.isArray(e))return"Erm, this can only be a number or an array.";if(2!=e.length)return"Huh, the array can only have two items: the width and height to render at.";if("number"!=typeof e[0])return"Hmm, looks like the first item of the custom resolution isn't a number, it's "+Bagel.internal.an(Bagel.internal.getTypeOf(e[0]))+".";if("number"!=typeof e[1])return"Oops, looks like the second item of the custom resolution isn't a number, it's "+Bagel.internal.an(Bagel.internal.getTypeOf(e[1]))+"."}},description:'The resolution for the game to be rendered at. Either "full", "fixed" or a custom resolution using an array containing the width and height (in that order). Full renders the game at the full resolution which makes it good for vector graphics. Fixed is good for pixel art because it means that resources aren\'t wasted rendering extra pixels as it renders at the game\'s width and height. And custom\'s good if you want to do something more advanced.'},antialiasing:{required:!1,default:!1,types:["boolean"],description:"If antialiasing is used or not. Antialiasing smooths out lower resolution stuff at the a slight cost to performance. However, it doesn't work well with pixel art so should be disabled for that. Disabling it also rounds coordinates during the rendering which removes fuzzy edges but can make motion less smooth."},renderer:{required:!1,default:"auto",check:e=>{if(!["auto","canvas","webgl"].includes(e))return"Oops. You used an invalid option. You used "+JSON.stringify(e)+', it can only be either "auto", "canvas" or "webgl".'},types:["string"],description:'The renderer for this game. Either "auto", "canvas" or "webgl". "auto" will use WebGL if it\'s supported by the browser, otherwise it\'ll use the basic 2d renderer (slower). Currently, this input will be ignored as there\'s no WebGL renderer support yet.'},dom:{required:!1,default:!0,types:["boolean"],description:"If the canvas should be part of the DOM or not."},htmlElementID:{required:!1,check:e=>{if(null==document.getElementById(e)&&null!=e)return"Oops, you specified the element to add the game canvas to but it doesn't seem to exist.\nYou tried to use "+JSON.stringify(e)+". You might want to check that the HTML that creates the element is before your JavaScript."},types:["string"],description:"An element to append the canvas to. If unspecified, it will be added to the document or body."},backgroundColour:{required:!1,default:"white",types:["string"],description:'The HTML colour for the canvas background. Can also be "transparent".'}},description:"Contains a few options for how the game is displayed."},loading:{required:!1,default:{},subcheck:{mode:{required:!1,default:"dynamic",check:e=>{if(!["preload","dynamic"].includes(e))return'Oh no! This only accepts "preload" and "function" but you used '+JSON.stringify(e)+"."},types:["string"],description:'How assets should be loaded. Either "preload" or "dynamic". Preload loads all the assets before the game runs and dynamic only loads the assets when they\'re requested internally or by using the get function. A loading screen will show for those assets if they were requested on the first frame after a state change. Assets requested any other time won\'t trigger a loading screen.'},skip:{required:!1,default:!0,types:["boolean"],description:"If the loading screen should be skipped or not. If true, nothing will show until the game's loaded. This can annoy the user as it delays the page load but if it's short enough it saves time because they don't have to wait for the loading animation to finish."},animation:{required:!1,default:{game:{assets:{imgs:[{id:"Bagel",src:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADEAAAAxCAYAAABznEEcAAAHBklEQVRoQ8Wa+28UVRTHz126s2C3u1BA0wpUQBBa3NKWl/6qoj/6iNGoMRo1RsVo1D9B8YFvFDWoEKOJifEHf/cVH7yKvAQi0iIsNa3RUlNI27CzHXPue2bvzNyZ3Uh/abePmfnc8z3nfM/ZEmjwh/vVeo8QgAwBAPwCP+OH9prcslt8tyF3r/tiE1+ukw/NnpmwZ48BYVCMjmzcVddzpP7j81+s9eThEvbgePppQRCI3JQOJjHE2Odr6MkTQthDC9XYgLQvBxj+3SctPSLia3JjMhhriL8/6/PYSSu5JAJBgJmtABfHokFcF8BpoqDkBjsYK4i/Pu1jpw9CMslBZixerxKdg3iuC8TJ8sioHJHRsQSJhRj+pNdz3So4TpMsNmki0oQQlXMAubkAF88BjJysqVomadlEJBJiaGevv/IIKQUi4rouOE42NEec9mUAuVYWCQQZPhlaftOAhEKUd/RwCQUqTwhIVLLPbL+aRQDvVhmD6eGTvj4yXXEhQ/NAVAr8PcwNv9TCcsQI8cdHPTyJ/SVTltCEILOuWicB3OEB2UdEBE0NUUYkAGMCqYEY3L7aX0IDtR9BkuRIM42CklJlZCBVQ4xK9hqIAYTQan+V6l0kdXJp5TvW8lwYg6mRgbobIk30QB/xQZz4oNvLaFLx6dwQEfy5jEpI+S1QCHYqk+V+2dndCisGYRYlrvzqID6I397v5tWI9wEtIioflD+yyZFim5ATS+rJkcGGWxQJcXwbA1AnQ4AmXpZLSdiMkIjITg4AVbcqrzNRAZiTz0KhbSn8e/YEl6bZa+H9cnqV0t3vlcvZww2fYF1R81oS4ti2kqc7UGbokkUEpYV/N1lR0Vry8CFy+mNeLIBAS449i5BSrGkUfmvJdaxJaiDC/VKII++UVEnVrHQSECwAExUGfc2jh2P7D8JYgSzeoAwjhQDlvbiNpzc7vLUUOhPYgGAE8PQ7HzsSa2PEFHR2Z48XCyJMo2iCp/f4GyJKauNu/CnAobev9YSmTeFFjeveSa9aqOPJCoFVT9gDxIFkqU3hHR7NIvoscTyy/vPUuJlDHHwLIZTNNoGYIoJwky5AadOv1hEIzqPBiMzE7l4ZYw+dmwvTZ/bGjrpk/5urvAzPg2q1CllejWxA8PdRRt1PpodAj4aymt2xQnV2ftrumX00xyKrFh5+/xurWD74EpqAqVOL8itKKAL0PnU0dRREVBBk4bIV/PRbYarcn6izk32vMwgTSNjcjN+nUqoA9D1dP8SZHau9FofAnI4VMIHeymbU1XKE7H1NQSQBEfmwpgEQ2EfyOYAc2pCYmd0kLbLn1S5ZmUwREXmi50i1ihUJYN0zx+qWkpBUEhCRJ9hnaOfetaXLsACozZGgtBoJcupDFglWxtkWhRlE9jpM1mJBR37e0kWtd+3cHA2CDW6qArD+2fqjgTOMgki+DiI/vYJyYoUBpSMMH5PRjJqqJU6FQrgAGxoAMbC928s7RDVUy70WmwybgPz4cqfKiYQRaQQIzjA0CtkmqQb5cNqCToyyRmlhYiUFKV7eIbU7VD4F1z+XXlI4w0gppdhrObfuY97ph5cwGtp2LyQixfkdkHGKfFYgQNxx+npo8GAqkOPvlby8oxJayDrJXit3G4f4/sVObbuhZgj9YsX5i4BwADpnuOP8NYGx8gGYdEkikKPvlrxmn4ziR13T7nfW7Rziu80sEqZxkz18QS2QRclzx+H8P2VZAtGmIAheIyrZD29lZrNZRIDbHZtRF8ttVZZeppzL7uAQKCkTSGHeQshwAHHyGXz40bKxaiEIXhirlui82BDRZIrX+RzOj0JC9e218nf203SQHffbzSvleKqfSqGtS/UQlBAhcGEUI+DvI3pnF2MqXgc7e7OjFtDBmV1WIi0itnutliAEEgVBMBJSSu44XBg9G1jt23V2Ze+Tzez68iGY7LPv2i8D4PM+37yw0rd/LbZ1sodGgHNDIat9BWKaR+ipyo1J8uVDGMicu0MgMBoChEYhW4BMdRxItmgNkmZClG/WWKyDMCJz7/nFd/g1LvTr51k0CvMWUAh6gypKaYh+jRac2pGad43ipSVcsj7qqt0Wf/ssAKKGMyXF+ffGQGA0dJALo3/6XWTERpwuFDTphDlQdMCYJ0n3Wni9K+7zA/iqU3CAFyCsiyZZJKeLiDCgDE5tIvV7t91/wDi/RA41lwJE5FRwxl/wgBkgMhIiMpcSRERk0YMHIw/barwMA9FLathg5c8TJkt5yhYr08UPRQNYReL/iogp2Zc+csjqkK1+SU/6YEM0GTd9QhTrHZuqJUpw1ELa9B8tiSHERcK8VvhMEF+1Oh8P36ZH/TtOaghx0TAbz3JhBp3bhXcK22uVNiVfRutQdUMET8hm1G3Ewk2/73+GkYKJ4ZsYiQAAAABJRU5ErkJggg=="},{id:"Loading.Black",src:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACcAAAAGCAYAAABAU4emAAABhWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AYht+mSou0ONhBRCFDdbIgKuIoVSyChdJWaNXB5NI/aNKQpLg4Cq4FB38Wqw4uzro6uAqC4A+Ik6OToouU+F1SaBHjHcc9vPe9L3ffAUKzylSzZwJQNctIJ+JiLr8qBl4RwAjCNIMSM/VkZjELz/F1Dx/f72I8y7vuzxFWCiYDfCLxHNMNi3iDeGbT0jnvE0dYWVKIz4nHDbog8SPXZZffOJccFnhmxMim54kjxGKpi+UuZmVDJZ4mjiqqRvlCzmWF8xZntVpn7XvyF4YK2kqG67SGkcASkkhBhIw6KqjCQox2jRQTaTqPe/iHHH+KXDK5KmDkWEANKiTHD/4Hv3trFqcm3aRQHOh9se2PUSCwC7Qatv19bNutE8D/DFxpHX+tCcx+kt7oaNEjoH8buLjuaPIecLkDDD7pkiE5kp+WUCwC72f0TXlg4BboW3P71j7H6QOQpV4t3wAHh8BYibLXPd4d7O7bvzXt/v0AOZJykKeF/tkAAAAGYktHRAD/AOwAAJPfVTcAAAAJcEhZcwAALiMAAC4jAXilP3YAAAAHdElNRQfkBx0TEyFitOyDAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAAAIZJREFUKFPFkUEOQiEMRGeMN5D7n7D/DM8NaEEQXPkSAh0G2rSWBCEr4SJyTMg7jZD1QLbfGtg2+W7qG/JLkot0H8VGe/BRwEx7Jba7giouAmqci9xw2xn+ybJzM8bRntK62YlptKsu/tQ5Qj4ZxxGXBWz/Ii9irRFSPo8eoNdqnO+++ob9Cdg1gf0PGCdvAAAAAElFTkSuQmCC"},{id:"Loading.White",src:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACcAAAAGCAYAAABAU4emAAABhWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AYht+mSou0ONhBRCFDdbIgKuIoVSyChdJWaNXB5NI/aNKQpLg4Cq4FB38Wqw4uzro6uAqC4A+Ik6OToouU+F1SaBHjHcc9vPe9L3ffAUKzylSzZwJQNctIJ+JiLr8qBl4RwAjCNIMSM/VkZjELz/F1Dx/f72I8y7vuzxFWCiYDfCLxHNMNi3iDeGbT0jnvE0dYWVKIz4nHDbog8SPXZZffOJccFnhmxMim54kjxGKpi+UuZmVDJZ4mjiqqRvlCzmWF8xZntVpn7XvyF4YK2kqG67SGkcASkkhBhIw6KqjCQox2jRQTaTqPe/iHHH+KXDK5KmDkWEANKiTHD/4Hv3trFqcm3aRQHOh9se2PUSCwC7Qatv19bNutE8D/DFxpHX+tCcx+kt7oaNEjoH8buLjuaPIecLkDDD7pkiE5kp+WUCwC72f0TXlg4BboW3P71j7H6QOQpV4t3wAHh8BYibLXPd4d7O7bvzXt/v0AOZJykKeF/tkAAAAGYktHRAD/AOwAAJPfVTcAAAAJcEhZcwAALiMAAC4jAXilP3YAAAAHdElNRQfkBx0TEjVhdQm/AAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAAAIlJREFUKFPNk8ERwyAMBPcy6cDpv0NquDw0OEKBYP+yLzgECOmQbdMkMoc9zJu01WIOkOMEeFibxdX7Ab3gUcWTJn1tWmkQiTZBJDRemB+Rk5ydl1gn9wc8dwEDtbXXifZmcmsX1btXuU0bbpEtsCAqV41dtV/0uGr00Lv/4HD/HDGe+fBTYdn4DcirSc3L3FGIAAAAAElFTkSuQmCC"}]},sprites:[{id:"Bagel",type:"canvas",fullRes:!1,scripts:{init:[{code:e=>{e.vars.img=Bagel.get.asset.img("Bagel")},stateToRun:"loading"}]},render:(e,t,n,i)=>{let r=e.vars.img,s=i.width/2;n.imageSmoothingEnabled=!1,i.width=r.width,i.height=r.height;let a=t.vars.loading.game.config;if("transparent"==a.display.backgroundColour?n.fillStyle=document.body.bgColor:n.fillStyle=a.display.backgroundColour,n.clearRect(0,0,i.width,i.height),0==t.vars.stage){-90!=t.vars.angle&&n.drawImage(r,0,0,i.width,i.height);let e=t.vars.loading.progress/100*360-90;if(e>t.vars.angle&&(t.vars.velocity+=5,t.vars.angle+=t.vars.velocity,t.vars.velocity*=.9,e<=t.vars.angle&&(t.vars.velocity=0,t.vars.angle=e)),100==t.vars.loading.progress&&0==t.vars.velocity)return void t.vars.stage++;n.beginPath(),n.moveTo(s,s),n.arc(s,s,2*s,Bagel.maths.degToRad(-90),Bagel.maths.degToRad(t.vars.angle),!0),n.lineTo(s,s),n.fill(),n.setTransform(1,0,0,1,0,0)}else 0==t.vars.delay?(t.vars.velocity+=1,e.width-=t.vars.velocity,e.height-=t.vars.velocity,t.vars.velocity*=.9,n.drawImage(r,0,0,i.width,i.height),e.width<=0&&(e.width=1,e.height=1,n.clearRect(0,0,1,1),t.vars.delay++)):(t.vars.delay++,t.vars.delay>10&&(t.vars.loading.done=!0))},width:(e,t)=>Math.max(t.width,t.height)/5,height:(e,t)=>Math.max(t.width,t.height)/5},{id:"Text",type:"sprite",img:"Loading.Black",scripts:{init:[{code:(e,t,n)=>{let i=document.createElement("canvas");i.width=1,i.height=1,e.vars.ctx=i.getContext("2d"),e.vars.halfBagelHeight=Bagel.get.sprite("Bagel").height/2,n("calculateSize"),n("calculateColour")},stateToRun:"loading"}],main:[{code:(e,t,n)=>{n("calculateColour"),n("calculateSize")},stateToRun:"loading"}],steps:{calculateSize:(e,t)=>{let n=e.height/e.width;e.width=Math.min(t.width,t.height)/2,e.height=e.width*n,e.y=t.height/2+e.vars.halfBagelHeight,e.y+=e.height},calculateColour:(e,t)=>{let n=e.vars.ctx,i=t.vars.loading.game.config.display.backgroundColour;"transparent"==i&&(i=document.body.bgColor),n.fillStyle=i;let r=i=n.fillStyle;(parseInt(r[1]+r[2],16)+parseInt(r[3]+r[4],16)+parseInt(r[5]+r[6],16))/3<=127&&(e.img="Loading.White")}}}}]},state:"loading",vars:{angle:-90,velocity:0,stage:0,delay:0}},types:["object"],description:"The loading screen animation. Defaults to a Bagel.js themed one.\nIt's a game object and works exactly the same as a game except its loading screen is disabled, Game.vars.loading is automatically created and the id, width, height and config given for the game is ignored. Game.vars.loading contains the following:\n progress -> The percentage of the assets loaded\n loaded -> The number of assets loaded\n loading -> The number currently loading\n done -> Starts as false, set this to true when you're done (loaded should be 0 when you do this)"}},types:["object"],description:"A few options for how Bagel.js should handle loading assets."},disableBagelJSMessage:{required:!1,default:!1,types:["boolean"],description:"Disables the console message when the game is initialised. As crediting is required to use Bagel.js, please put a link to the GitHub page somewhere else in your program. e.g in the credits."}},types:["object"],description:"A bunch of other options for the game."},internal:{required:!1,default:{},types:["object"],description:"Very hush hush. (Contains stuff that Bagel.js needs to make the game work)"},vars:{required:!1,default:{},types:["object"],description:"Can be used to store variables for the game."}},sprite:{sprite:{id:{required:!0,check:(e,t,n,i)=>{let r=Bagel.internal.currentStack,s=0==r.length?null:r[r.length-1].plugin;s&&(s=s.info.id);let a=e.split(".")[1];if("."==e[0]){if(null==s)return"This is awkward... ids starting with a dot are only for plugins. You tried to use the id "+JSON.stringify(e)+".\nIf it's important that it has this name, you could write a plugin instead, just make sure its id (Plugin.info.id) is set to "+JSON.stringify(a)+" ;)";if(a!=s)return"Erm... the only reserved prefix you can use in this plugin is "+JSON.stringify("."+s)+" and you tried to use the id "+JSON.stringify(e)+'.\nYou can fix this by changing the prefix (the bit after a full stop starting but before the next full stop), removing it or changing the plugin id in "Plugin.info.id".'}if(i.internal.idIndex[t.id])return"Oops, you've used this id before. You might want to check what sprite has this id or if you're adding this sprite to the right game."},types:["string"],description:"The id for the sprite to be targeted by."},type:{required:!0,types:["string"],description:"The type of sprite."},visible:{required:!1,default:!0,types:["boolean"],description:"If the sprite is visible or not."},clones:{required:!1,default:{},check:(e,t,n,i,r)=>{let s=i.internal.combinedPlugins.types.sprites[t.type].internal.cloneSyntax;Bagel.check({ob:e,where:"the sprite "+JSON.stringify(t.id)+'\'s "clone" argument',syntax:s},{args:!0,missing:!0})},types:["object"],description:"The default data for a clone of this sprite.\nAll arguments are optional as the clone will adopt the arguments from the clone function and the parent sprite (in that priority)"},scripts:{required:!1,default:{},subcheck:{init:{required:!1,default:[],subcheck:{code:{required:!0,types:["function","undefined"],description:'The code to be run when the "stateToRun" property matches the game state.'},stateToRun:{required:!0,types:["string"],description:"The state when this script will be run."}},arrayLike:!0,types:["array"],description:"Contains init scripts. They run when the game state first changes to the script's state."},main:{required:!1,default:[],arrayLike:!0,subcheck:{code:{required:!0,types:["function","undefined"],description:'The code to be run when the "stateToRun" property matches the game state.'},stateToRun:{required:!0,types:["string"],description:"The state when this script will be run."}},types:["array"],description:"Contains main scripts. They run for every frame where the script's state and the game's state match."},all:{required:!1,default:[],check:e=>{if("function"!=typeof e)return"Huh. This should be a function but you used "+Bagel.internal.an(Bagel.internal.getTypeOf(e))+"."},checkEach:!0,types:["array"],description:'Contains "all" scripts. They run on every frame and aren\'t affected by the game state.'},steps:{required:!1,default:{},types:["object"],description:"Contains steps: mini scripts that can be called from scripts. The key is the id and the value is the function."}},types:["object"],description:"The sprite's scripts."},vars:{required:!1,default:{},types:["object"],description:"An object you can use to store data for the sprite."},request:{required:!1,default:{},types:["object"],description:"Contains assets the sprite needs before it becomes active. Used as part of dynamic loading. The keys are the game states and the values are an object with the keys being the asset type (plural) and the value being an array of the assets of that type that need to be loaded."}},clones:{syntax:{id:{types:["string"],description:"The id for the clone to be targeted by. Defaults to the parent's id followed by a hashtag and then the lowest number starting from 0 that hasn't already been used."},type:{types:["string"],description:"The type of clone."},visible:{types:["boolean"],description:"If the clone is visible or not."},clones:{types:["object"],description:"The default data for a clone of this clone.\nAll arguments are optional as the clone will adopt the arguments from the clone function and the parent sprite (in that priority)"},scripts:{subcheck:{init:{required:!1,default:[],types:["array"],check:e=>{if("function"!=typeof e)return"Oops. Looks like you used the wrong type, you used "+Bagel.internal.an(Bagel.internal.getTypeOf(e))+" instead of a function."},checkEach:!0,description:"An array of functions to run when this clone is initialised."},main:{required:!1,default:[],types:["array"],description:"An array of functions to run on every frame for this clone."}},types:["object"],description:"The clones's scripts."},vars:{types:["object"],description:"An object you can use to store data for the clone."}},args:{id:{syntax:{description:"The id for the clone to be targeted by. Defaults to the parent's id followed by a hashtag and then the lowest number starting from 0 that hasn't already been used."},mode:"replace"},type:{syntax:{required:!0,types:["string"],description:"The type of clone."},mode:"replace"},visible:{syntax:{description:"If the clone is visible or not."},mode:"replace"},clones:{syntax:{required:!1,default:{},types:["object"],description:"The default data for a clone of this clone.\nAll arguments are optional as the clone will adopt the arguments from the clone function and the parent sprite (in that priority)"},mode:"ignore"},scripts:{syntax:{subcheck:{init:{required:!1,default:[],types:["array"],check:e=>{if("function"!=typeof e)return"Oops. Looks like you used the wrong type, you used "+Bagel.internal.an(Bagel.internal.getTypeOf(e))+" instead of a function."},checkEach:!0,description:"An array of functions to run when this clone is initialised."},main:{required:!1,default:[],types:["array"],description:"An array of functions to run on every frame for this clone."}},description:"The clones's scripts.",default:{}},mode:"ignore"},vars:{syntax:{description:"An object you can use to store data for the clone."},mode:"merge"}}}},plugin:{info:{required:!0,types:["object"],subcheck:{id:{required:!0,check:e=>{if(Bagel.internal.current.game.internal.plugins[e])return"Oops, you used an id for a plugin that's already been used. You tried to use the id "+JSON.stringify(e)+" in the game "+JSON.stringify(Bagel.internal.current.game.id)+". This plugin might've already been loaded or maybe the plugins are too similar? If you're making this plugin, you could try changing Plugin.info.id."},types:["string"],description:"The unique id for the plugin."},description:{required:!0,types:["string"],description:"A brief description of what the plugin is and what it does."}},description:"Contains some information about the plugin."},plugin:{required:!1,default:{},subcheck:{types:{required:!1,default:{},subcheck:{assets:{required:!1,default:{},arrayLike:!0,subcheck:{args:{required:!0,types:["object"],description:["The required and optional arguments for the asset type. Is an object where the key is the argument name. e.g {"," foo: {"," required: false,"," default: 1,"," types: [",' "number"'," ],",' description: "The first argument for this asset type.'," }","}"].join("\n")},description:{required:!0,types:["string"],description:"The description of this asset type, make this short and clear to help people when they use the wrong syntax."},check:{required:!1,types:["function"],description:["Your check function for this asset type. ","A good check function will avoid a standard JavaScript error when the user inputs something wrong (e.g a can't read property X of null error).","\nFortunately, Bagel.js helps you out in a few ways:\n",' You can use the check function provided (while the check function is being run) to easily check an object to make sure it has the desired properties as well as setting defaults. (works in the same way as the "args" argument.)\n',' You should also make use of the "args" argument as you can easily choose which data types you want to allow for each arguments as well as setting defaults and required arguments.\n',' "standardChecks" has, well... some standard checks. If you want to make sure an id isn\'t used twice use "standardChecks.id(<whichever argument is used for the id (defaults to "id")>)". ',' You might also want to use the "isInternal" check with the arguments working the same as the previous but also having a second argument for the isInternal argument. This might be useful if you want to reserve some IDs for plugins as it\'ll block any IDs starting with a dot and without the asset having "isInternal" set to true.\n'," You probably want to use it like this:\n"," let error = standardChecks.id();\nif (error) return error;",' And if you find any problems with the user input, just use the return statement in the check function (e.g return "Error";) and Bagel.js will stop what it\'s doing, throw the error you specified and pause the game.\n',"Some tips on making custom errors though:\n"," Always specifiy where the error is! Bagel.js will say which game it's in but, you know more than it about the error. You should specify which type they were making, the index of the problematic error and ideally how to fix it.\n"," Also, try to include information about the inputs the user provided. For example, if they used a duplicate ID, say what that id was in the error itself.\n"," Lastly, be nice to the programmer. Treat them like a user. It's helpful to know that you can just put in something you know's wrong and get a helpful mini-tutorial.\n","\nOne more thing: the arguments for the function is structured like this:\n","(asset, game, check, standardChecks, plugin, index) => {\n};\n","Where standardChecks contains functions and check is a function that checks objects.","\n\nGood luck! :P"].join("")},init:{required:!0,types:["function"],description:["Where you make the asset object. When it's ready, simply use the \"ready\" function to tell Bagel.js that the asset's loaded.","Here's an example:","(asset, ready, game, plugin, index) => {"," let img = new Image();"," img.onload = () => {"," ready(img);"," };",' img.src = "foo";',"};"].join("\n")},get:{required:!0,types:["string"],description:"The name of the function. Usually the singular version of the asset type."},forcePreload:{required:!1,default:!1,types:["boolean"],description:'If this asset must be preloaded even when the loading mode is set to "dynamic". Be careful about how you use this because it can increase loading times.'}},types:["object"],description:"Contains the new asset types, the key is the name of type. (should be plural)"},sprites:{required:!1,default:{},arrayLike:!0,subcheck:{description:{required:!0,types:["string"],description:"A short explaination of what this sprite type does."},args:{required:!0,types:["object"],arrayLike:!0,subcheck:{description:{required:!0,types:["string"],description:"A brief description of what this property does."},types:{required:!0,types:["array"],description:"The different data types this property accepts. e.g string, array, object etc."},required:{required:!0,types:["boolean"],description:"If the argument is required or not. Most of the time, it should be optional."},check:{required:!1,types:["function"],description:"The check function."},subcheck:{required:!1,types:["object"],description:'The subcheck. Same as a "syntax" argument but there\'s no checks on what you put in here.'},arrayLike:{required:!1,default:!1,types:["boolean"],description:"If each item should be checked or not. Works for both objects and arrays."},default:{required:!1,types:"any",description:"The default value (only applies if required is false)"}},check:(e,t,n,i,r,s)=>{if(s.ob.cloneArgs&&null==s.ob.cloneArgs[n])return"Oops, there's no matching cloneArg variant for the "+JSON.stringify(n)+' argument. Make sure "cloneArgs" exists for this sprite type. Clone arguments are a variant of the arguments for clones, each argument is an object with two items: "syntax" and "mode". The syntax is in the same format as the syntax in the normal args but anything unspecified defaults to the normal variant and mode is how the value\'s calculated. Check the syntax for cloneArgs for more info.'},checkEach:!0,description:'Same as the "syntax" argument for the check function. These checks are only run on original sprites, not clones.'},cloneArgs:{required:!0,types:["object","undefined"],arrayLike:!0,subcheck:{syntax:{required:!1,default:{},subcheck:{description:{required:!1,check:(e,t,n,i,r)=>{null==e&&(t[n]=r.prev.ob.args[r.prevName].description)},types:["string"],description:"A brief description of what this property does. You might want to change this to mention clones instead of sprites."},types:{required:!1,check:(e,t,n,i,r)=>{null==e&&(t[n]=r.prev.ob.args[r.prevName].types)},types:["array"],description:"The different data types this property accepts. e.g string, array, object etc."},required:{required:!1,check:(e,t,n,i,r)=>{null==e&&(t[n]=r.prev.ob.args[r.prevName].required)},types:["boolean"],description:"If the argument is required or not. Most of the time, it should be optional."},check:{required:!1,check:(e,t,n,i,r)=>{null==e&&(t[n]=r.prev.ob.args[r.prevName].check)},types:["function","undefined"],description:"The check function."},subcheck:{required:!1,check:(e,t,n,i,r)=>{null==e&&(t[n]=r.prev.ob.args[r.prevName].subcheck)},types:["object","undefined"],description:'The subcheck. Same as a "syntax" argument.'},default:{required:!1,check:(e,t,n,i,r)=>{null==e&&(t[n]=r.prev.ob.args[r.prevName].default)},types:"any",description:'The default value for when "ignore" mode is used and no value is found for a property.'}},types:["object"],description:'The syntax for clones of this sprite type. Any unspecified arguments will default to the values specified in the "args" argument for normal sprites.'},mode:{required:!1,default:"replace",check:e=>{if(!["replace","merge","ignore"].includes(e))return'Huh, looks like you used an invalid option for this. It can only be "replace", "merge" or "ignore".'},types:["string"],description:["The adoption method for this property. Either:",' • "replace" -> The value is given based on the order of preference (from high to low): the "clone" function inputs, the "clones" attribute in the parent and the parent sprite\'s properties.',' • "merge" -> Only for objects and arrays. They are merged together, in the event of a conflict, the order of preference applies.',' • "ignore" -> Ignores the parent\'s properties. However, properties will still be taken from the parent\'s "clones" argument and the "clone" function using the order of preference. The property will be set to the default from either the parent or the clone\'s arguments if no value is assigned.'].join("\n")}},description:'Same as the "syntax" argument for the check function. These checks are only run on clones, not original sprites. Unspecified properties will mean that the property doesn\'t exist for clones.'},listeners:{required:!1,default:{},subcheck:{steps:{required:!1,default:{},check:e=>{if("function"!=typeof e)return"Oops, steps can only be functions. You used "+Bagel.internal.an(Bagel.internal.getTypeOf(e))+"."},checkEach:!0,types:["object"],description:'Short functions that do a task. Can be called from any of the other functions using "Bagel.step.plugin.spriteListener(<step id>)".'},fns:{required:!1,default:{},check:e=>{if("function"!=typeof e)return"Oops, functions can only be, well... functions. You used "+Bagel.internal.an(Bagel.internal.getTypeOf(e))+"."},checkEach:!0,types:["object"],description:"Functions that can replace the functions in listeners. The key is the id for it. The id can be used in place of this function in listeners."},property:{required:!1,default:{},subcheck:{set:{required:!1,default:null,check:(e,t,n,i,r)=>{if("string"==typeof e){if(!r.ob.fns.hasOwnProperty(e))return"Huh, looks like you used an invalid id for a function. You used "+JSON.stringify(e)+".";t[n]=r.ob.fns[e]}},types:["function","string"],description:"A function that's run after the property is changed. Can also be the name of a function defined in SpriteJSON.listeners.fns. The arguments given are these: sprite, value, property, game, plugin and triggerSprite."},get:{required:!1,default:null,check:(e,t,n,i,r)=>{if("string"==typeof e){if(!r.ob.fns.hasOwnProperty(e))return"Hmm, looks like you used an invalid id for a function. You used "+JSON.stringify(e)+".";t[n]=r.ob.fns[e]}},types:["function","string"],description:"A function that's run before the value is sent back to the code that requested it. Can also be the name of a function defined in SpriteJSON.listeners.fns. The arguments given are these: sprite, value, property, game, plugin and triggerSprite."}},arrayLike:!0,types:["object"],description:'Contains the "set" and "get" listener functions.'},trigger:{required:!1,default:!1,types:["boolean"],description:"If the listeners should be triggered during sprite initialisation. This can be useful in some situations."}},types:["object"],description:"Functions that can run when certain conditions are met."},check:{required:!1,default:null,types:["function"],description:"A function that does extra checks. Use return <error message> in the function to create an error. These are the arguments given: sprite, game, check and where."},init:{required:!1,default:null,types:["function"],description:"Initialises the sprite. Is a function. Can be used to define attributes. These are the arguments given: sprite, game and plugin."},render:{required:!1,default:{},subcheck:{ctx:{required:!1,default:null,types:["function"],description:"The ctx render function. Runs every frame. Called with these arguments: sprite, ctx, canvas, game, plugin, scaleX and scaleY."},webgl:{required:!1,default:{},subcheck:{shaders:{required:!1,default:{},subcheck:{vertex:{required:!1,default:[],types:["array"],description:"An array of vertex shaders to run from first to last."},fragment:{required:!1,default:[],types:["array"],description:"An array of fragment shaders to run from first to last."}},types:["object"],description:'Contains the "vertex" and "fragment" shaders.'}},types:["object"],description:'The webgl renderer. Runs every frame. Contains "shaders" and an optional "render" function which allows for extra processing before the vertex and fragment shaders are run.'},clean:{required:!1,default:!1,types:["boolean"],description:"If the ctx settings are returned to normal by the render functions. Ideally, ctx.save and ctx.restore should be avoided and this should be set to true."}},types:["object"],description:'The render functions for this sprite type. Ideally should have both a webgl renderer and a fallback ctx renderer. For maximum performance, "clean" should be set to true and ctx.save and ctx.restore should be avoided (but ctx settings must be reset to what they were before after rendering)'}},types:["object"],description:"Contains the new sprite types, the key is the name of type. (should be singular)"}},types:["object"],description:"Creates new types. (assets and sprites)"},methods:{required:!1,default:{},subcheck:{bagel:{required:!1,default:{},arrayLike:!0,subcheck:{fn:{required:!1,subcheck:{args:{required:!1,check:(e,t)=>{if(!t.normal&&null==e)return"Oops, looks like you missed this argument."},types:["object"],description:'The syntax for the arguments. These is always an object, even if you set "obArg" to false.'},fn:{required:!0,types:["function"],description:"The method itself. The arguments are the arguments (an object) and the plugin."},obArg:{required:!1,check:(e,t)=>{if(!t.normal&&null==e)return"Oops, looks like you missed this argument."},types:["boolean"],description:"If the arguments should be inputted as an object or should use a normal function input. You probably only want to use the 2nd one if there aren't many arguments."},normal:{required:!1,default:!1,types:["boolean"],description:"If the method should just be a normal function. This can increase performance but obArg and args won't work anymore. You will also have to rely on Bagel.internal.current for finding out the current game, sprite, etc."}},types:["object"],description:"The method itself."},category:{required:!1,types:["object"],description:"Contains categories where the key is the name of the category and their contents have the same syntax as here. Note: These aren't checked."}},types:["object"],description:"Contains framework functions. (Bagel.<function>...) The key is the name and the value is the function."},game:{required:!1,default:{},arrayLike:!0,subcheck:{fn:{required:!1,subcheck:{args:{required:!1,check:(e,t)=>{if(!t.normal&&null==e)return"Oops, looks like you missed this argument."},types:["object"],description:'The syntax for the arguments. These is always an object, even if you set "obArg" to false.'},fn:{required:!0,types:["function"],description:"The method itself. The arguments are the arguments (an object) and the plugin."},obArg:{required:!1,check:(e,t)=>{if(!t.normal&&null==e)return"Oops, looks like you missed this argument."},types:["boolean"],description:"If the arguments should be inputted as an object or should use a normal function input. You probably only want to use the 2nd one if there aren't many arguments."},normal:{required:!1,default:!1,types:["boolean"],description:"If the method should just be a normal function. This can increase performance but obArg and args won't work anymore. You will also have to rely on Bagel.internal.current for finding out the current game, sprite, etc."}},types:["object"],description:"The method itself."},category:{required:!1,types:["object"],description:"Contains categories where the key is the name of the category and their contents have the same syntax as here. Note: These aren't checked."}},types:["object"],description:"Contains game functions. (Game.<function>...) The key is the name and the value is the function."},sprite:{required:!1,default:{},arrayLike:!0,subcheck:{fn:{required:!1,subcheck:{appliesTo:{required:!0,types:["array"],description:"The sprite types that this method is added to."},args:{required:!1,check:(e,t)=>{if(!t.normal&&null==e)return"Oops, looks like you missed this argument."},types:["object"],description:'The syntax for the arguments. These is always an object, even if you set "obArg" to false.'},fn:{required:!0,types:["function"],description:"The method itself. The arguments are the arguments (an object) and the plugin."},obArg:{required:!1,check:(e,t)=>{if(!t.normal&&null==e)return"Oops, looks like you missed this argument."},types:["boolean"],description:"If the arguments should be inputted as an object or should use a normal function input. You probably only want to use the 2nd one if there aren't many arguments."},normal:{required:!1,default:!1,types:["boolean"],description:"If the method should just be a normal function. This can increase performance but obArg and args won't work anymore. You will also have to rely on Bagel.internal.current for finding out the current game, sprite, etc."}},types:["object"],description:"The method itself."},category:{required:!1,types:["object"],description:"Contains categories where the key is the name of the category and their contents have the same syntax as here. Note: These aren't checked."},apply:{required:!1,subcheck:{to:{required:!1,default:[],check:e=>{if("string"!=typeof e)return"Erm, these can only be a string and you used "+Bagel.internal.an(Bagel.internal.getTypeOf(e))+"."},checkEach:!0,types:["array"],description:"Which sprite types to apply it to."},from:{required:!1,check:(e,t,n,i,r)=>{if(r.ob.to&&null==e)return"Oh, looks like you forgot this argument."},types:["string"],description:"A sprite type that has the existing method that you want to apply to a new sprite type."}},types:["object"],description:"Allows a pre existing method from another plugin to also be applied to a sprite type created by this plugin. It's an array of the sprite types."}},types:["object"],description:"Contains sprite functions. (me.<function>...) The key is the name and the value is the function."}},types:["object"],description:'Contains the 3 different method types: "bagel", "game" and "sprite".'},scripts:{required:!1,default:{},subcheck:{preload:{required:!1,types:["function"],description:"The preload function. It runs before the plugin is checked or initialised making it useful for changing some values before loading."},init:{required:!1,types:["function"],description:"The init function. It runs once the plugin's been checked and mostly initialised. This function finishes it by doing stuff specific to this plugin."},main:{required:!1,types:["function"],description:'The "main" function. Runs on every frame before any other scripts run.'},steps:{required:!1,default:{},check:e=>{if("function"!=typeof e)return"Huh, looks like you used the wrong type, it should be a function and you used "+Bagel.internal.an(Bagel.internal.getTypeOf(e)+".")},checkEach:!0,types:["object"],description:'Mini functions. They can help make your code clearer by spitting functions into the individual steps. Can use them with "Bagel.step.plugin.scripts" or the step function provided as an argument to the function.'}},types:["object"],description:'Contains the plugin\'s scripts. "preload", "init" and "main". Steps can also be used. The arguments provided are the plugin, the game and then the step function.'},listeners:{required:!1,default:{},subcheck:{prepState:{required:!1,types:["function"],description:"Runs on the first frame of a new game state. Regardless of if the game's loaded or not. Any loading triggered here will be part of a loading screen."},state:{required:!1,types:["function"],description:"The game state listener function. Triggers on the first frame with the new state from the start. Runs before init scripts and the loading screen. Only runs once the game's loaded but can also trigger a loading screen by requesting or adding an asset."}},types:["object"],description:"Contains listener functions for different things."}},types:["object"],description:"Contains most of the plugin stuff. e.g the new types it adds, methods and defaults."},vars:{required:!1,default:{},types:["object"],description:"An object you can use to store data for the sprite."},args:{required:!1,types:["object"],description:"The arguments provided when the plugin was loaded. You shouldn't change any of these values in the plugin, that's what the \"vars\" property is for."}},assets:{id:{required:!0,types:["string"],description:"The id to target the asset by."},src:{required:!0,types:["string"],description:'The src of the asset. e.g "assets/imgs/bagel.png"'}},disableArgCheck:{args:!0}},th:e=>e+1+(e>8&&e<20?"th":["st","nd","rd","th","th","th","th","th","th"][parseInt(e.toString()[e.toString().length-1])]),an:e=>["a","e","i","o","u"].includes(e[0].toLowerCase())?"an "+e:"a "+e,list:(e,t,n)=>{let i="";for(let n in e){let r=e[n];i+=Bagel.internal.an(r),n==e.length-2?i+=" "+t+" ":n!=e.length-1&&(i+=", ")}return i},getTypeOf:e=>Array.isArray(e)?"array":null==e?"undefined":typeof e,deepClone:e=>{if("object"!=typeof e||null==e)return e;let t;t=Array.isArray(e)?[]:{};let n=Object.keys(e),i=0;for(;i<n.length;)"object"==typeof e[n[i]]?t[n[i]]=Bagel.internal.deepClone(e[n[i]]):t[n[i]]=e[n[i]],i++;return t},findCloneID:(e,t)=>{for(let t in e.cloneIDs)if(null==e.cloneIDs[t])return t;return e.cloneIDs.length},findSpriteID:e=>{for(let t in e.game.sprites)if(null==e.game.sprites[t])return parseInt(t);return e.game.sprites.length},oops:e=>{if(null==e)throw"Critical Bagel.js error, please look at the error above for more info. ^-^";throw e.paused=!0,"Critical Bagel.js error in the game "+JSON.stringify(e.id)+", look at the error for some help. ^-^"},current:{sprite:null,game:null,asset:null,assetType:null,assetTypeName:null,i:null,where:null,plugin:null},saveCurrent:()=>{let e=Bagel.internal,t=e.current;e.currentStack.push({asset:t.asset,assetType:t.assetType,assetTypeName:t.assetTypeName,game:t.game,i:t.i,plugin:t.plugin,sprite:t.sprite,where:t.where})},loadCurrent:()=>{let e=Bagel.internal;e.current=e.currentStack.pop()},resetCurrent:()=>{Bagel.internal.current={sprite:null,game:null,asset:null,assetType:null,assetTypeName:null,i:null,where:null,plugin:null},Bagel.internal.currentStack=[]},currentStack:[],roundX:e=>{let t=Bagel.internal.current.game;if(t.config.display.antialiasing)return e;let n=t.internal.renderer.ctx.getTransform().a;return e*=n,Math.round(e)/n},roundY:e=>{let t=Bagel.internal.current.game;if(t.config.display.antialiasing)return e;let n=t.internal.renderer.ctx.getTransform().d;return e*=n,Math.round(e)/n},inputAction:{queued:[],queue:(e,t)=>{Bagel.internal.inputAction.queued.push([e,t])},input:()=>{let e=Bagel.internal.inputAction.queued;for(let t in e)e[t][0](e[t][1]);Bagel.internal.inputAction.queued=[]}},triggerPluginListener:(e,t,n)=>{let i=t.internal.combinedPlugins.listeners[e];if(i){let e=Bagel.internal.current;Bagel.internal.saveCurrent(),e.game=t;for(let r in i){let s=i[r];e.plugin=s.plugin,s.fn(n,t,s.plugin)}Bagel.internal.loadCurrent()}},triggerSpriteListener:(e,t,n,i,r)=>{let s=i.internal.combinedPlugins.types.sprites[n.type],a=s.internal.plugin,o=Bagel.internal.current;Bagel.internal.saveCurrent(),o.sprite=n,o.game=i,o.plugin=a;let l=s.listeners.property[t][e](n.internal.properties,n.internal.properties[t],t,i,a,n,Bagel.step.plugin.spriteListener,r);l&&((l=".rerun")?n.internal.rerunListeners.push([e,t]):(console.error(l),Bagel.internal.oops(i))),Bagel.internal.loadCurrent()},requestAnimationFrame:window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame,debug:{add:e=>{Bagel.internal.debug.queue.push(e)},warn:e=>Bagel.internal.debug.add(["warning",e]),log:e=>Bagel.internal.debug.add(["log",e]),send:()=>{let e=Bagel.internal.debug.queue;if(0==e.length)return;let t=Bagel.internal.debug.logList,n=JSON.stringify(e);if(t.includes(n))return Bagel.internal.debug.queue=[],!1;t.push(n);for(let t in e){let n=e[t];"warning"==n[0]?console.warn(n[1]):console.log(n[1])}return Bagel.internal.debug.queue=[],!0},queue:[],logList:[]},games:{}},check:(e,t={},n,i)=>{if(e.prev||t.args||(e=Bagel.check({ob:e,where:e.where?where:"the check function. (Bagel.check)",syntax:{ob:{required:!0,types:["object","array"],description:"The object or array of objects to check."},where:{required:!0,types:["string"],description:"A description of what it's checking."},syntax:{required:!0,types:["object"],description:["The syntax for the object. e.g {",' // "foo" is the argument name'," foo: {",' required: true, // If the "foo" argument is required or not.',' //default: "foo", // If it\'s not required, it needs a default',' types: ["string"], // The different data types to accept. e.g array, object, string, number etc.',' description: "foo" // A clear and to the point explaination of what this argument does'," }","}"].join("\n")},game:{required:!1,default:Bagel.internal.current.game,types:["object"],description:"The game object. Optional if this is being run in a script."}}},Bagel.internal.checks.disableArgCheck,!0)),t.missing&&t.types&&t.useless)return e.ob;e.hasOwnProperty("game")||(e.game=Bagel.internal.current.game);let r,s=Bagel.internal.debug,a=["array","string"],o=[],l=[],d=[],u=[];r=t.missing?e.ob:{...e.ob,...e.syntax};let h={};for(let n in r){if(h[n])continue;h[n]=!0;let i=e.syntax[n],r=e.ob[n];if(null==i){t.useless||o.push(n);continue}if("ignore"==i)continue;let s=!1;e.ob.hasOwnProperty(n)||(i.required?l.push(n):(e.ob[n]=Bagel.internal.deepClone(i.default),r=e.ob[n],s=!0)),t.types||s||0==l.length&&(null==i.types&&(console.error("The syntax for "+e.where+"."+n+' is missing the "types" argument.'),console.log("In "+e.where+"."),console.log("Syntax:"),console.log(i),Bagel.internal.oops(e.game)),a.includes(Bagel.internal.getTypeOf(i.types))||(console.error("The syntax for "+e.where+"."+n+' has the wrong data type for the "types" argument. You used '+Bagel.internal.an(Bagel.internal.getTypeOf(i.types))+"."),console.log("In "+e.where+"."),console.log("Syntax:"),console.log(i),Bagel.internal.oops(e.game)),i.types.includes(Bagel.internal.getTypeOf(r))||"any"==i.types||d.push(n)),(i.subcheck||i.check)&&u.push(n)}let c=0!=l.length||0!=d.length;if(o.length>0&&(1==o.length?s.warn("Oops, looks like you used an unsupported argument"+(c?"":" in "+e.where)+": "+JSON.stringify(o[0])+". You can leave this alone if you want, but it doesn't need to be there."):s.warn("Hmm, looks like you used some unsupported arguments"+(c?"":" in "+e.where)+":\n • "+o.map((e,t)=>JSON.stringify(e)).join("\n • ")+"\n\nYou can leave these if you want, but they don't need to be there."),i&&s.log(i)),l.length>0&&(1==l.length?console.error("Hmm, looks like you forgot the "+JSON.stringify(l[0])+" argument."):console.error("Whelp, looks like you forgot some arguments:\n"+l.map(t=>" • "+JSON.stringify(t)+" -> "+e.syntax[t].description).join("\n")),i&&console.log(i)),d.length>0&&(1==d.length?console.error(":/ looks like you used the wrong type for the "+JSON.stringify(d[0])+" argument. You used "+Bagel.internal.an(Bagel.internal.getTypeOf(e.ob[d[0]]))+" instead of "+Bagel.internal.list(e.syntax[d[0]].types,"or",!0)+"."):console.error("Hmm, looks like you got some types wrong:\n"+d.map((t,n)=>" • "+JSON.stringify(t)+" -> Should be "+Bagel.internal.list(e.syntax[t].types,"or",!0)+". You used "+Bagel.internal.an(Bagel.internal.getTypeOf(e.ob[d[n]]))+".").join("\n")),i&&console.log(i)),o.length+d.length!=0&&s.warn("FYI, these are the arguments:\n"+Object.keys(e.syntax).map(t=>" • "+(e.syntax[t].required?"":"(optional) ")+JSON.stringify(t)+" -> "+e.syntax[t].description+"\n Can "+(1==e.syntax[t].types.length?"only ":"")+"use "+Bagel.internal.list(e.syntax[t].types,"or",!0)+".").join("\n\n")),c&&(s.log("In "+e.where+"."),s.send()&&(console.log("Object:"),console.log(e.ob),i&&console.log(i)),Bagel.internal.oops(e.game)),s.send(),u.length>0)for(let t in u){let n=u[t],i=e.syntax[n];if(i.subcheck&&"object"==typeof e.ob[n]){let t=Array.isArray(e.ob[n]);if(t||i.arrayLike)for(let r in e.ob[n])Bagel.check({ob:e.ob[n][r],where:t?e.where+"."+n+" item "+r:e.where+"."+n+"."+r,syntax:i.subcheck,prev:e,prevName:r},Bagel.internal.checks.disableArgCheck);else Bagel.check({ob:e.ob[n],where:e.where+"."+n,syntax:e.syntax[n].subcheck,prev:e,prevName:n},Bagel.internal.checks.disableArgCheck)}if(i.check)if(i.checkEach){let t=e;for(let r in e.ob[n]){let s=i.check(e.ob[n][r],e.ob[n],r,n,e.game,t,e);s&&(console.error(s),isNaN(r)?console.log("In "+e.where+"."+n+"."+r+"."):console.log("In "+e.where+"."+n+" item "+r+"."),console.log("Object:"),console.log(e.ob[n][r]),Bagel.internal.oops(e.game))}}else{let t=i.check(e.ob[n],e.ob,n,e.game,e.prev,e);t&&(console.error(t),console.log("In "+e.where+"."+n+"."),console.log("Object:"),console.log(e.ob[n]),Bagel.internal.oops(e.game))}}return e.ob},get:{asset:{},sprite:(e,t,n)=>{if(null==t&&(t=Bagel.internal.current.game),null==t&&(console.error("Oops. Looks like you're trying to run this function outside of a script. Try moving it and trying again. Alternatively, you can pass the game object in as the second argument to this function to fix this issue."),Bagel.internal.oops()),null==e&&console.error("Oops, you forgot the first argument: the id. It's the id for the sprite you want to get").Bagel.internal.oops(t),"string"!=typeof e&&(console.error("Oops, the id for Bagel.get.sprite can only be a string but you used "+Bagel.internal.an(Bagel.internal.getTypeOf(e))+"."),Bagel.internal.oops(t)),null==t.internal.idIndex[e]){if(n)return!1;console.error("Hmm, Bagel.js couldn't get the sprite "+JSON.stringify(e)+" because it doesn't seem to exist. You might want to check its id and the game this is running in or on."),Bagel.internal.oops(t)}return t.game.sprites[t.internal.idIndex[e]]},game:e=>{if(null==e&&console.error("Oops, you forgot the first argument: the id. It's the id for the game you want to get.").Bagel.internal.oops(Bagel.internal.current.game),null==Bagel.internal.games[e]){if(check)return!1;console.error(":/ Bagel.js couldn't get the game "+JSON.stringify(e)+" because it doesn't seem to exist."),Bagel.internal.oops(Bagel.internal.current.game)}return Bagel.internal.games[e]}},step:{sprite:(e,t,n)=>{let i=Bagel.internal.current;n=n||i.game,null==(t=t||i.sprite)&&(console.error("Oops, this must be run in a sprite script."),Bagel.internal.oops(n)),null==n&&(console.error("Huh, this isn't being run inside a game and you didn't specifiy a game. You can fix this either by moving it inside a script or providing the game object as the last argument."),Bagel.internal.oops());let r=t.scripts.steps[e];return null==r&&(console.error("Huh, the step "+JSON.stringify(e)+" doesn't exist in the sprite "+JSON.stringify(t.id)+"."),Bagel.internal.oops(n)),r(t,n,Bagel.step.sprite)},plugin:{scripts:e=>{let t=Bagel.internal.current,n=t.game,i=t.plugin;null==i&&(console.error("Oops, this must be run inside a plugin."),Bagel.internal.oops(n));let r=i.plugin.scripts.steps[e];return null==r&&(console.error("Huh, the step "+JSON.stringify(e)+" doesn't exist in plugin "+i.info.id+"."),Bagel.internal.oops(n)),r(i,n,Bagel.step.plugin.scripts)},spriteListener:e=>{let t=Bagel.internal.current,n=t.sprite,i=t.game,r=t.plugin;null==r&&(console.error("Oops, this must be run inside a plugin."),Bagel.internal.oops(i));let s=r.plugin.types.sprites[n.type].listeners.steps[e];return null==s&&(console.error("Huh, the step "+JSON.stringify(e)+" doesn't exist in plugin "+r.info.id+".plugin.types.sprites."+n.type+".listeners.steps. Make sure the id is correct, also remember it's case sensitive."),Bagel.internal.oops(i)),s(r,i,Bagel.step.plugin.spriteListener)}}},config:{flags:{warnOfUselessParameters:!0}},device:{is:{touchscreen:null===document.ontouchstart}},events:{pwaUpdate:null},version:"1.5.1b"},Bagel.internal.requestAnimationFrame.call(window,Bagel.internal.tick);