-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathruntime-embedded-min.js
More file actions
1 lines (1 loc) · 53.4 KB
/
Copy pathruntime-embedded-min.js
File metadata and controls
1 lines (1 loc) · 53.4 KB
1
"use strict";function rdpSimplify(t,e){if(t.length<3)return t.slice();const s=t=>t*t,i=(t,e,i)=>{const n=i[0]-e[0],a=i[1]-e[1];if(0===n&&0===a)return s(t[0]-e[0])+s(t[1]-e[1]);const o=((t[0]-e[0])*n+(t[1]-e[1])*a)/(n*n+a*a),r=Math.max(0,Math.min(1,o));return s(t[0]-(e[0]+r*n))+s(t[1]-(e[1]+r*a))},n=e*e,a=new Uint8Array(t.length);a[0]=1,a[t.length-1]=1;const o=[[0,t.length-1]];for(;o.length;){const[e,s]=o.pop();let r=0,h=-1;for(let n=e+1;n<s;n++){const a=i(t[n],t[e],t[s]);a>r&&(r=a,h=n)}r>n&&h>0&&(a[h]=1,o.push([e,h],[h,s]))}const r=[];for(let e=0;e<t.length;e++)a[e]&&r.push(t[e]);return r}const SF_KEY={0:"KeyA",1:"KeyB",2:"KeyC",3:"KeyD",4:"KeyE",5:"KeyF",6:"KeyG",7:"KeyH",8:"KeyI",9:"KeyJ",10:"KeyK",11:"KeyL",12:"KeyM",13:"KeyN",14:"KeyO",15:"KeyP",16:"KeyQ",17:"KeyR",18:"KeyS",19:"KeyT",20:"KeyU",21:"KeyV",22:"KeyW",23:"KeyX",24:"KeyY",25:"KeyZ",26:"Digit0",27:"Digit1",28:"Digit2",29:"Digit3",30:"Digit4",31:"Digit5",32:"Digit6",33:"Digit7",34:"Digit8",35:"Digit9",36:"Escape",57:"Space",58:"Enter",59:"Backspace",71:"ArrowLeft",72:"ArrowRight",73:"ArrowUp",74:"ArrowDown",75:"Numpad0",76:"Numpad1",77:"Numpad2",78:"Numpad3",79:"Numpad4",80:"Numpad5",81:"Numpad6",82:"Numpad7",83:"Numpad8"},DOM_TO_SF=(()=>{const t=new Map;for(const e of Object.keys(SF_KEY))t.set(SF_KEY[e],Number(e));return t})(),EVT={Closed:0,Resized:1,LostFocus:2,GainedFocus:3,TextEntered:4,KeyPressed:5,KeyReleased:6,MouseWheelMoved:7,MouseWheelScrolled:8,MouseButtonPressed:9,MouseButtonReleased:10,MouseMoved:11,TouchBegan:19,TouchMoved:20,TouchEnded:21},CFG=Object.assign({logicalWidth:1184,logicalHeight:666,wasmUrl:"game.wasm",assetRoot:"../assets",canvasId:"game",title:null,cacheBust:""},"undefined"!=typeof window&&window.WASMWEB||{}),LOGICAL_W=CFG.logicalWidth,LOGICAL_H=CFG.logicalHeight,BUST=CFG.cacheBust?"?v="+encodeURIComponent(CFG.cacheBust):"",bust=t=>t+(BUST&&!t.includes("?")?BUST:""),TEXT_DEFAULT_EMOJI=new Set([9968,9978,9962,9970,9973,9752,9728,9729,9730,9748,9928,10052,10024,9732,9986,9992,9993,9999,10002,9874,9876,9878,9879,9881,9935,9937,9939,9961,9969,9760,9762,9763,9884]),IS_IOS="undefined"!=typeof navigator&&(/iP(hone|od|ad)/.test(navigator.userAgent)||"MacIntel"===navigator.platform&&navigator.maxTouchPoints>1),ASSET_ROOT=CFG.assetRoot;class Runtime{constructor(t){this.canvas=t;const e=t.getContext("2d",{alpha:!1,colorSpace:"display-p3"});this.isP3=!(!e||!e.getContextAttributes||"display-p3"!==e.getContextAttributes().colorSpace),this.ctx=e||t.getContext("2d",{alpha:!1}),this.wasmMemory=null,this.exports=null,this.textDecoder=new TextDecoder("utf-8"),this.textEncoder=new TextEncoder,this.targets=[{canvas:t,ctx:this.ctx}],this.curTarget=0,this._textBaselineMode=2,this._ttsPreferred=[],this._ttsRobotic=[],this._ttsFemale=[],this._ttsVoice=null,this._ttsPending=[],"undefined"!=typeof speechSynthesis&&(speechSynthesis.getVoices(),window.bossmanVoices=()=>speechSynthesis.getVoices().map(t=>({name:t.name,lang:t.lang,default:t.default})),window.bossmanTry=(t,e)=>{const s=speechSynthesis.getVoices().find(e=>(e.name||"").toLowerCase().includes((t||"").toLowerCase())),i=new SpeechSynthesisUtterance(e||"Did you get the memo about the TPS reports?");return s&&(i.voice=s),i.rate=.85,i.pitch=.55,speechSynthesis.speak(i),s?s.name:"(no match — default voice)"},speechSynthesis.onvoiceschanged=()=>{this._ttsVoice=null;const t=this._pickTTSVoice();if(!t||!this._ttsPending.length)return;const e=this._ttsPending;this._ttsPending=[];for(const s of e){s.voice=t;try{speechSynthesis.speak(s)}catch(t){}}}),this.images=[null],this.shaders=[null],this.engineNodes=[],this.glCanvas=null,this.gl=null,this.glQuadVAO=null,this.glTexFromImage=new Map,this.fonts=[null],this.sounds=[null],this.imageByName=new Map,this.soundByName=new Map,this.fontByName=new Map;for(const[t,e]of[["AmericanTypewriter",'"American Typewriter", "AmericanTypewriter", Courier, monospace'],["Courier",'Courier, "Courier New", monospace'],["CourierNew",'"Courier New", Courier, monospace'],["Helvetica","Helvetica, Arial, sans-serif"],["HelveticaNeue",'"Helvetica Neue", Helvetica, Arial, sans-serif'],["ArialMT","Arial, Helvetica, sans-serif"],["Arial","Arial, Helvetica, sans-serif"],["ChalkboardSE-Regular",'"Chalkboard SE", "Comic Sans MS", cursive'],["MarkerFelt-Thin",'"Marker Felt", "Comic Sans MS", cursive'],["TimesNewRomanPSMT",'"Times New Roman", Times, serif'],["Menlo-Regular","Menlo, monospace"]]){this.fonts.push(e);const s=this.fonts.length-1;this.fontByName.set(t,s),this.fontByName.set(t.replace(/[\s-]/g,"").toLowerCase(),s)}this.texts=new Map,this._glyphCache=new Map,this._glyphCacheCap=2048,this.audioCtx=null,this.voices=new Map,this.nextVoice=1,this.duckFactor=1,this.pressed=new Set,this.mouseDown=[!1,!1],this.mouseX=0,this.mouseY=0,this.baseScale=1,this.offX=0,this.offY=0,this.events=[],this.gpEnabled=!0,this.gpMapToKeys=!0,this.gpAxisDeadzone=.35,this.gpPrev=[null,null,null,null],this.defaultFontFamily="JetBrainsMono-Bold, ui-monospace, Menlo, monospace"}dv(){return new DataView(this.wasmMemory.buffer)}u8(t,e){return new Uint8Array(this.wasmMemory.buffer,t,e)}cstr(t,e){return this.textDecoder.decode(this.u8(t,e))}emojify(t){if(!t||t.length<1)return t;let e=!1;for(let s=0;s<t.length;s++){const i=t.codePointAt(s);if(i>65535&&s++,TEXT_DEFAULT_EMOJI.has(i)){e=!0;break}}if(!e)return t;const s=Array.from(t);let i="";for(let t=0;t<s.length;t++)i+=s[t],TEXT_DEFAULT_EMOJI.has(s[t].codePointAt(0))&&"️"!==s[t+1]&&(i+="️");return i}drawFpsOverlay(){const t=this.ctx;if(!t)return;const e=0|this._overlayFlags;if(0===e)return;const s=this._fps||0,i=[],n=this._frameMs||0,a=this._msImg||0,o=this._msTxt||0,r=this._frameSamples?this._frameSum/this._frameSamples:n;if(1&e&&i.push(`FPS ${s.toFixed(0)} frame ${n.toFixed(1)} min ${(this._minFrameMs||0).toFixed(1)} avg ${r.toFixed(1)} max ${(this._maxFrameMs||0).toFixed(1)}ms`),2&e&&i.push(`img ${0|this._dcImg}/${a.toFixed(1)}ms txt ${0|this._dcTxt}/${o.toFixed(1)}ms rest ${Math.max(0,n-a-o).toFixed(1)}ms`),2&e){const t=this.wasmMemory&&this.wasmMemory.buffer?(this.wasmMemory.buffer.byteLength/1048576).toFixed(0):"?",e=this.images?Array.isArray(this.images)?this.images.filter(Boolean).length:Object.keys(this.images).length:0;i.push(`mem ${t}MB cvs ${((this._canvasBytes||0)/1048576).toFixed(0)}MB glyph ${this._glyphCache?this._glyphCache.size:0} img# ${e}`)}t.save(),t.setTransform(1,0,0,1,0,0),t.globalAlpha=1,t.filter="none",t.font="16px monospace",t.textAlign="right",t.textBaseline="top";const h=t.canvas.width,c=t.canvas.height;let l=0;for(const e of i){const s=t.measureText(e).width;s>l&&(l=s)}const u=l+16,d=19*i.length+8,g=h-u-6,m=c-d-6;t.fillStyle="rgba(0,0,0,0.7)",t.fillRect(g,m,u,d),t.fillStyle=s>=50?"#3f6":s>=30?"#fd0":"#f44";const f=h-6-8;for(let e=0;e<i.length;e++)t.fillText(i[e],f,m+4+19*e);t.restore()}_rasterizeText(t,e,s,i,n,a){if(!e)return null;const o=this.ctx2d();this.applyFont(o,t,s,0);const r=o.measureText(e),h=null!=r.actualBoundingBoxAscent&&isFinite(r.actualBoundingBoxAscent)?r.actualBoundingBoxAscent:.9*s,c=null!=r.actualBoundingBoxDescent&&isFinite(r.actualBoundingBoxDescent)?r.actualBoundingBoxDescent:.3*s,l=null!=r.actualBoundingBoxLeft&&isFinite(r.actualBoundingBoxLeft)?r.actualBoundingBoxLeft:0,u=null!=r.actualBoundingBoxRight&&isFinite(r.actualBoundingBoxRight)?r.actualBoundingBoxRight:r.width,d=Math.max(1,Math.ceil(l+u)+4),g=Math.max(1,Math.ceil(h+c)+4),m=Math.max(1,Math.min(Math.ceil(d*a),8192)),f=Math.max(1,Math.min(Math.ceil(g*a),8192));let p,_;try{if(p=document.createElement("canvas"),p.width=m,p.height=f,_=p.getContext("2d"),!_)return null}catch(t){return null}_.setTransform(a,0,0,a,0,0),this.applyFont(_,t,s,0),_.textAlign="left",_.textBaseline="alphabetic",_.fillStyle=this.css(n);const x=2+l,y=2+h;try{_.fillText(e,x,y)}catch(t){return null}let v;switch(i){case 1:v=(h-c)/2;break;case 2:v=h;break;case 3:v=-c;break;default:v=0}return{canvas:p,ox:-l-2,oy:v-y,dw:d,dh:g}}wasiImports(){return new Proxy({fd_write:(t,e,s,i)=>{const n=this.dv(),a=[];let o=0;for(let t=0;t<s;t++){const s=e+8*t,i=n.getUint32(s,!0),r=n.getUint32(s+4,!0);a.push(this.textDecoder.decode(this.u8(i,r))),o+=r}const r=a.join("").replace(/\n$/,"");return r.length&&(2===t?console.error:console.log)("[wasm] "+r),n.setUint32(i,o,!0),0},fd_read:(t,e,s,i)=>(this.dv().setUint32(i,0,!0),0),fd_close:()=>0,fd_seek:(t,e,s,i,n)=>{const a=this.dv();return a.setUint32(n,0,!0),a.setUint32(n+4,0,!0),0},fd_prestat_get:()=>8,fd_prestat_dir_name:()=>8,fd_fdstat_get:(t,e)=>{const s=this.dv();for(let t=0;t<24;t++)s.setUint8(e+t,0);return 0},fd_fdstat_set_flags:(t,e)=>0,fd_filestat_get:(t,e)=>{const s=this.dv();for(let t=0;t<64;t++)s.setUint8(e+t,0);return 0},path_filestat_get:(t,e,s,i,n)=>{const a=this.dv();for(let t=0;t<64;t++)a.setUint8(n+t,0);return 44},fd_pread:(t,e,s,i,n,a)=>(this.dv().setUint32(a,0,!0),0),fd_readdir:(t,e,s,i,n,a)=>(this.dv().setUint32(a,0,!0),0),fd_tell:(t,e)=>{const s=this.dv();return s.setUint32(e,0,!0),s.setUint32(e+4,0,!0),0},path_readlink:(t,e,s,i,n,a)=>(this.dv().setUint32(a,0,!0),44),environ_sizes_get:(t,e)=>{const s=this.dv();return s.setUint32(t,0,!0),s.setUint32(e,0,!0),0},environ_get:()=>0,args_sizes_get:(t,e)=>{const s=this.dv();return s.setUint32(t,0,!0),s.setUint32(e,0,!0),0},args_get:()=>0,path_open:()=>44,clock_time_get:(t,e,s)=>{const i=BigInt(Math.round(1e6*performance.now()));return this.dv().setBigUint64(s,i,!0),0},clock_res_get:(t,e)=>(this.dv().setBigUint64(e,1000n,!0),0),random_get:(t,e)=>{const s=this.u8(t,e);return crypto.getRandomValues(s),0},proc_exit:t=>{throw new Error("wasm proc_exit("+t+")")},poll_oneoff:(t,e,s,i)=>(this.dv().setUint32(i,0,!0),0),sched_yield:()=>0},{get:(t,e)=>e in t?t[e]:()=>0})}envImports(){return{__cxa_allocate_exception:t=>0,__cxa_throw:(t,e,s)=>{throw console.error("[boss] __cxa_throw reached — uncaught C++ exception in wasm"),new Error("uncaught C++ exception from wasm")},js_log:(t,e)=>{console.log("%c[boss] "+this.cstr(t,e),"color:#e6b800")},gfx_target:t=>{this.curTarget=t>0&&t<this.targets.length?t:0},gfx_clear:t=>{const e=this.ctx2d();e.setTransform(1,0,0,1,0,0),e.globalAlpha=1,e.globalCompositeOperation="source-over",e.fillStyle=this.css(t),e.fillRect(0,0,e.canvas.width,e.canvas.height),0===this.curTarget&&e.setTransform(this.baseScale,0,0,this.baseScale,this.offX,this.offY)},gfx_save:()=>this.ctx2d().save(),gfx_restore:()=>this.ctx2d().restore(),gfx_translate:(t,e)=>this.ctx2d().translate(t,e),gfx_snap_translation:()=>{const t=this.ctx2d(),e=t.getTransform();e.e=Math.round(e.e),e.f=Math.round(e.f),t.setTransform(e)},gfx_scale:(t,e)=>this.ctx2d().scale(t,e),gfx_rotate:t=>this.ctx2d().rotate(t*Math.PI/180),gfx_set_alpha:t=>{this.ctx2d().globalAlpha=t},gfx_set_line_style:(t,e,s)=>{const i=this.ctx2d();i.lineJoin=1===t?"round":2===t?"bevel":"miter",i.lineCap=1===e?"round":2===e?"square":"butt",i.miterLimit=s>0?s:10},gfx_set_blend:t=>{const e=this.ctx2d();switch(e.globalAlpha=1,t){case 1:e.globalCompositeOperation="lighter";break;case 2:e.globalCompositeOperation="multiply";break;case 3:e.globalCompositeOperation="screen";break;default:e.globalCompositeOperation="source-over"}},gfx_set_tint:(t,e,s,i)=>{this._partTint=i>0?{r:Math.round(255*t),g:Math.round(255*e),b:Math.round(255*s),bf:Math.min(1,i)}:null},gfx_fill_rect:(t,e,s,i,n)=>{const a=this.ctx2d();a.fillStyle=this.css(n),a.fillRect(t,e,s,i)},gfx_stroke_rect:(t,e,s,i,n,a)=>{const o=this.ctx2d();o.lineWidth=n,o.strokeStyle=this.css(a),o.strokeRect(t,e,s,i)},gfx_fill_circle:(t,e,s,i)=>{if(!(s>0&&isFinite(t)&&isFinite(e)))return;const n=this.ctx2d();n.fillStyle=this.css(i),n.beginPath(),n.arc(t,e,s,0,2*Math.PI),n.fill()},gfx_stroke_circle:(t,e,s,i,n)=>{if(!(s>0&&isFinite(t)&&isFinite(e)))return;const a=this.ctx2d();a.lineWidth=i,a.strokeStyle=this.css(n),a.beginPath(),a.arc(t,e,s,0,2*Math.PI),a.stroke()},gfx_fill_poly:(t,e,s)=>{if(e<2)return;const i=this.ctx2d(),n=this.dv();i.fillStyle=this.css(s),i.beginPath(),i.moveTo(n.getFloat32(t,!0),n.getFloat32(t+4,!0));for(let s=1;s<e;s++)i.lineTo(n.getFloat32(t+8*s,!0),n.getFloat32(t+8*s+4,!0));i.closePath(),i.fill()},gfx_stroke_poly:(t,e,s,i,n)=>{if(e<2)return;const a=this.ctx2d(),o=this.dv();a.strokeStyle=this.css(n),a.lineWidth=i,a.lineJoin="round",a.beginPath(),a.moveTo(o.getFloat32(t,!0),o.getFloat32(t+4,!0));for(let s=1;s<e;s++)a.lineTo(o.getFloat32(t+8*s,!0),o.getFloat32(t+8*s+4,!0));s&&a.closePath(),a.stroke()},gfx_draw_image:(t,e,s,i,n,a,o,r,h,c)=>{this._dcImg=1+(0|this._dcImg);const l=performance.now();try{const l=this._partTint;this._partTint=null;const u=this.images[t];if(!u)return;const d=this.ctx2d(),g=(255&c)/255;if(l){const t=this._blendTint(u,e,s,i,n,r,h,l,d);if(t){const e=d.globalAlpha;d.globalAlpha=e*g;try{d.drawImage(t,a,o,r,h)}catch(t){}return void(d.globalAlpha=e)}}const m=d.globalAlpha;d.globalAlpha=m*g;const f=c>>>24&255,p=c>>>16&255,_=c>>>8&255,x=255!==f||255!==p||255!==_;try{if(u.svg){if(x){const t=this._tintSVG(u,r,h,f,p,_,d);if(t)return d.drawImage(t,a,o,r,h),void(d.globalAlpha=m)}return this.drawSVG(d,u,e,s,i,n,a,o,r,h),void(d.globalAlpha=m)}if(x){const t=this._tintRaster(u,e,s,i,n,f,p,_);if(t)return d.drawImage(t,a,o,r,h),void(d.globalAlpha=m)}i<0||n<0?d.drawImage(u.source,a,o,r,h):d.drawImage(u.source,e,s,i,n,a,o,r,h)}catch(t){}d.globalAlpha=m}finally{this._msImg=(this._msImg||0)+(performance.now()-l)}},txt_width:(t,e,s,i,n)=>{const a=this.ctx2d(),o=this.emojify(this.cstr(e,s));this.applyFont(a,t,i,n);let r=a.measureText(o).width;return!this.hasLetterSpacing&&n&&(r+=n*Math.max(0,[...o].length-1)),Math.ceil(r)},gfx_set_text_baseline:t=>{this._textBaselineMode=0|t},gfx_draw_text:(t,e,s,i,n,a,o,r)=>{this._dcTxt=1+(0|this._dcTxt);const h=performance.now();try{const h=this.ctx2d(),c=this.emojify(this.cstr(e,s));if(!c)return;const l=0|this._textBaselineMode;if(!(!this.hasLetterSpacing&&!!r)){const e=h.getTransform(),s=Math.hypot(e.a,e.b)||1,u=Math.hypot(e.c,e.d)||1,d=Math.max(s,u),g=Math.max(1,Math.min(Math.round(4*d)/4,16)),m=t+"|"+a+"|"+l+"|"+(o>>>0)+"|"+(r||0)+"|"+g+"|"+c;let f=this._glyphCache.get(m);if(void 0!==f)this._glyphCache.delete(m),this._glyphCache.set(m,f);else for(f=this._rasterizeText(t,c,a,l,o,g)||null,this._glyphCache.set(m,f);this._glyphCache.size>this._glyphCacheCap;){const t=this._glyphCache.keys().next().value;this._glyphCache.delete(t)}if(f){try{h.drawImage(f.canvas,i+f.ox,n+f.oy,f.dw,f.dh)}catch(t){}return}}if(this.applyFont(h,t,a,r),h.textAlign="left",h.fillStyle=this.css(o),1===l){h.textBaseline="alphabetic";const t=h.measureText(c),e=t.actualBoundingBoxAscent||.8*a,s=n+(e-(t.actualBoundingBoxDescent||.2*a))/2;if(this.hasLetterSpacing||!r)h.fillText(c,i,s);else{let t=i;for(const e of c)h.fillText(e,t,s),t+=h.measureText(e).width+r}return}if(h.textBaseline=2===l?"top":3===l?"bottom":0===l?"alphabetic":"top",this.hasLetterSpacing||!r)h.fillText(c,i,n);else{let t=i;for(const e of c)h.fillText(e,t,n),t+=h.measureText(e).width+r}}finally{this._msTxt=(this._msTxt||0)+(performance.now()-h)}},img_by_name:(t,e)=>{const s=this.cstr(t,e);return this.lookupImage(s)},img_from_rgba:(t,e,s)=>{const i=this.u8(t,e*s*4).slice(),n=document.createElement("canvas");n.width=e,n.height=s;const a=n.getContext("2d"),o=new ImageData(new Uint8ClampedArray(i.buffer),e,s);return a.putImageData(o,0,0),this.images.push({source:n,width:e,height:s}),this.images.length-1},img_width:t=>{const e=this.images[t];return e?e.width:0},img_height:t=>{const e=this.images[t];return e?e.height:0},font_by_name:(t,e)=>{const s=this.cstr(t,e);return this.lookupFont(s)},asset_exists:(t,e)=>{const s=this.cstr(t,e),i=this.basename(s),n=t=>t.has(s)||t.has(i);return n(this.soundByName)||n(this.imageByName)||n(this.fontByName)||this.texts.has(s)||this.texts.has(i)?1:0},asset_text:(t,e,s,i)=>{const n=this.cstr(t,e),a=this.texts.get(n);if(void 0===a)return-1;const o=this.textEncoder.encode(a);if(i>0&&s){const t=Math.min(o.length,i);this.u8(s,t).set(o.subarray(0,t))}return o.length},rt_create:(t,e)=>{const s=document.createElement("canvas");s.width=t,s.height=e;const i=this.makeCtx(s);return this.targets.push({canvas:s,ctx:i}),this.targets.length-1},rt_image:t=>{const e=this.targets[t];return e?(e.imageHandle||(this.images.push({source:e.canvas,width:e.canvas.width,height:e.canvas.height}),e.imageHandle=this.images.length-1),e.imageHandle):0},snd_from_samples:(t,e,s,i)=>{const n=this.ensureAudio();if(!n||e<=0||s<=0)return 0;const a=this.dv(),o=n.createBuffer(s,e,i);for(let i=0;i<s;i++){const n=o.getChannelData(i);for(let o=0;o<e;o++){const e=a.getInt16(t+2*(o*s+i),!0);n[o]=e<0?e/32768:e/32767}}return this.sounds.push(o),this.sounds.length-1},snd_by_name:(t,e)=>{const s=this.cstr(t,e);return this.lookupSound(s)},snd_create_pcm:(t,e,s)=>{const i=this.ensureAudio();if(!i||e<=0)return 0;const n=s>0?s:i.sampleRate,a=i.createBuffer(1,e,n),o=a.getChannelData(0),r=new Float32Array(this.wasmMemory.buffer,t,e);return o.set(r),this.sounds.push(a),this.sounds.length-1},snd_play:(t,e,s)=>{const i=this.ensureAudio(),n=this.sounds[t];if(!i||!n)return 0;const a=i.createBufferSource();a.buffer=n,a.loop=!!s;const o=i.createGain(),r=Math.max(0,Math.min(1,e/100));o.gain.value=r*this.duckFactor,a.connect(o).connect(i.destination);const h=this.nextVoice++,c={source:a,gain:o,base:r,state:1};return this.voices.set(h,c),a.onended=()=>{c.state=0,this.voices.delete(h)},a.start(),h},snd_stop:t=>{const e=this.voices.get(t);if(e){try{e.source.onended=null,e.source.stop()}catch(t){}e.state=0,this.voices.delete(t)}},snd_set_volume:(t,e)=>{const s=this.voices.get(t);s&&this.audioCtx&&(s.base=Math.max(0,Math.min(1,e/100)),s.gain.gain.setTargetAtTime(s.base*this.duckFactor,this.audioCtx.currentTime,.02))},snd_status:t=>{const e=this.voices.get(t);return e?e.state:0},snd_pause_all:()=>{this.audioCtx&&"running"===this.audioCtx.state&&this.audioCtx.suspend()},snd_resume_all:()=>{this.audioCtx&&"running"!==this.audioCtx.state&&"closed"!==this.audioCtx.state&&this.audioCtx.resume()},key_pressed:t=>{const e=SF_KEY[t];return e&&this.pressed.has(e)?1:0},mouse_button:t=>0===t?this.mouseDown[0]?1:0:1===t&&this.mouseDown[1]?1:0,mouse_x:()=>0|this.mouseX,mouse_y:()=>0|this.mouseY,gp_connected:t=>this.gpSnap&&this.gpSnap[t]?1:0,gp_button:(t,e)=>{const s=this.gpSnap&&this.gpSnap[t];return s&&s.buttons[e]?1:0},gp_button_value:(t,e)=>{const s=this.gpSnap&&this.gpSnap[t];return s&&s.values[e]||0},gp_axis:(t,e)=>{const s=this.gpSnap&&this.gpSnap[t];return s&&s.axes[e]||0},gp_map_to_keys:t=>{this.gpMapToKeys=!!t},tts_set_preferred_voices:(t,e)=>{const s=this.cstr(t,e).toLowerCase();this._ttsPreferred=s.split(",").map(t=>t.trim()).filter(Boolean),this._ttsVoice=null},tts_set_robotic_voices:(t,e)=>{const s=this.cstr(t,e).toLowerCase();this._ttsRobotic=s.split(",").map(t=>t.trim()).filter(Boolean),this._ttsVoice=null},tts_set_female_voices:(t,e)=>{const s=this.cstr(t,e).toLowerCase();this._ttsFemale=s.split(",").map(t=>t.trim()).filter(Boolean),this._ttsVoice=null},tts_speak:(t,e,s,i,n)=>{if("undefined"==typeof speechSynthesis||IS_IOS)return 0;try{speechSynthesis.resume()}catch(t){}const a=this.cstr(t,e),o=new SpeechSynthesisUtterance(a);o.rate=Math.max(.1,Math.min(s||1,10)),o.pitch=Math.max(0,Math.min(i||1,2)),o.volume=Math.max(0,Math.min(n||1,1)),o.onstart=()=>this._setDuck(.25),o.onend=()=>this._setDuck(1),o.onerror=()=>this._setDuck(1);const r=this._pickTTSVoice();if(r){o.voice=r;try{return speechSynthesis.speak(o),1}catch(t){return 0}}return this._ttsPending.push(o),1},tts_cancel:()=>{"undefined"!=typeof speechSynthesis&&speechSynthesis.cancel(),this._setDuck(1)},gfx_shader_compile:(t,e)=>{const s=this.ensureGL();if(!s)return 0;const i=this.cstr(t,e),n=this.linkShaderProgram(s,this.buildShaderFrag(i));if(!n)return 0;const a=this.shaders.length;return this.shaders.push({program:n,uniformLocs:new Map,srcText:i}),a},gfx_shader_release:t=>{if(t<=0||t>=this.shaders.length)return;const e=this.shaders[t];e&&(this.gl&&e.program&&this.gl.deleteProgram(e.program),this.shaders[t]=null)},gfx_shader_set_uniform_f:(t,e,s,i)=>{if(!this.gl)return;const n=this.shaders[t];if(!n)return;this.gl.useProgram(n.program);const a=this.uniLoc(t,this.cstr(e,s));a&&this.gl.uniform1f(a,i)},gfx_shader_set_uniform_v2:(t,e,s,i,n)=>{if(!this.gl)return;const a=this.shaders[t];if(!a)return;this.gl.useProgram(a.program);const o=this.uniLoc(t,this.cstr(e,s));o&&this.gl.uniform2f(o,i,n)},gfx_shader_set_uniform_v3:(t,e,s,i,n,a)=>{if(!this.gl)return;const o=this.shaders[t];if(!o)return;this.gl.useProgram(o.program);const r=this.uniLoc(t,this.cstr(e,s));r&&this.gl.uniform3f(r,i,n,a)},gfx_shader_set_uniform_v4:(t,e,s,i,n,a,o)=>{if(!this.gl)return;const r=this.shaders[t];if(!r)return;this.gl.useProgram(r.program);const h=this.uniLoc(t,this.cstr(e,s));h&&this.gl.uniform4f(h,i,n,a,o)},gfx_shader_set_uniform_t:(t,e,s,i)=>{if(!this.gl)return;const n=this.shaders[t];if(!n)return;const a=this.glTextureFor(i);if(!a)return;this.gl.useProgram(n.program),n.userTexUnits=n.userTexUnits||new Map;let o=n.userTexUnits.get(e);void 0===o&&(o=2+n.userTexUnits.size,n.userTexUnits.set(e,o)),this.gl.activeTexture(this.gl.TEXTURE0+o),this.gl.bindTexture(this.gl.TEXTURE_2D,a);const r=this.uniLoc(t,this.cstr(e,s));r&&this.gl.uniform1i(r,o)},gfx_shader_draw:(t,e,s,i,n,a,o,r)=>{if(t<=0||t>=this.shaders.length||!this.shaders[t]){const t=this.images[e];return void(t&&t.source&&this.ctx2d().drawImage(t.source,s,i,n,a))}this.glRunPassToCanvas(t,e,0,s,i,n,a,o,r,null)},gfx_lighting_draw:(t,e,s,i,n,a,o,r,h)=>{const c=this.ensureLightingShader();if(!c)return;const l=this.dv(),u=Math.max(0,Math.min(8,i)),d=new Float32Array(32),g=new Float32Array(32);for(let t=0;t<u;t++){const e=s+32*t;d[4*t+0]=l.getFloat32(e+0,!0),d[4*t+1]=l.getFloat32(e+4,!0),d[4*t+2]=0,d[4*t+3]=l.getFloat32(e+8,!0),g[4*t+0]=l.getFloat32(e+16,!0),g[4*t+1]=l.getFloat32(e+20,!0),g[4*t+2]=l.getFloat32(e+24,!0),g[4*t+3]=l.getFloat32(e+28,!0)}this.glRunPassToCanvas(c,t,e,n,a,o,r,0,h,(t,e)=>{const s=e.uniLoc(c,"u_lightPositions"),i=e.uniLoc(c,"u_lightColors"),n=e.uniLoc(c,"u_lightCount"),a=e.uniLoc(c,"u_ambient");s&&t.uniform4fv(s,d),i&&t.uniform4fv(i,g),n&&t.uniform1i(n,u),a&&t.uniform4f(a,.2,.2,.2,1)})},gfx_warp_draw:(t,e,s,i,n,a,o,r,h,c)=>{const l=this.ensureGL();if(!l)return;const u=this.glTextureFor(t);if(!u)return;const d=(e+1)*(s+1),g=this.dv(),m=new Float32Array(2*d),f=new Float32Array(2*d);for(let t=0;t<d;t++)m[2*t+0]=2*g.getFloat32(n+8*t+0,!0)-1,m[2*t+1]=1-2*g.getFloat32(n+8*t+4,!0),f[2*t+0]=g.getFloat32(i+8*t+0,!0),f[2*t+1]=g.getFloat32(i+8*t+4,!0);const p=new Uint16Array(e*s*6);let _=0;for(let t=0;t<s;t++)for(let s=0;s<e;s++){const i=t*(e+1)+s,n=i+1,a=i+(e+1),o=a+1;p[_++]=i,p[_++]=n,p[_++]=o,p[_++]=i,p[_++]=o,p[_++]=a}if(null==this.warpShader){const t="void main(){ gl_FragColor = SKDefaultShading(); }";this.warpShader=this.linkShaderProgram(l,this.buildShaderFrag(t))}if(!this.warpShader)return;this.glResize(r,h),l.useProgram(this.warpShader);const x=new Float32Array(4*d);for(let t=0;t<d;t++)x[4*t+0]=m[2*t+0],x[4*t+1]=m[2*t+1],x[4*t+2]=f[2*t+0],x[4*t+3]=f[2*t+1];const y=l.createBuffer();l.bindBuffer(l.ARRAY_BUFFER,y),l.bufferData(l.ARRAY_BUFFER,x,l.STREAM_DRAW),l.enableVertexAttribArray(0),l.vertexAttribPointer(0,2,l.FLOAT,!1,16,0),l.enableVertexAttribArray(1),l.vertexAttribPointer(1,2,l.FLOAT,!1,16,8);const v=l.createBuffer();l.bindBuffer(l.ELEMENT_ARRAY_BUFFER,v),l.bufferData(l.ELEMENT_ARRAY_BUFFER,p,l.STREAM_DRAW),l.activeTexture(l.TEXTURE0),l.bindTexture(l.TEXTURE_2D,u);const w=(c>>>24&255)/255,b=(c>>>16&255)/255,T=(c>>>8&255)/255,S=(255&c)/255,C=this.warpShader,M=l.getUniformLocation(C,"u_texture");M&&l.uniform1i(M,0);const A=l.getUniformLocation(C,"u_color_mix");A&&l.uniform4f(A,w,b,T,S),l.drawElements(l.TRIANGLES,p.length,l.UNSIGNED_SHORT,0),l.deleteBuffer(y),l.deleteBuffer(v),this.ctx2d().drawImage(this.glCanvas,0,0,this.glCanvas.width,this.glCanvas.height,a,o,r,h)},gfx_3d_draw_billboard:(t,e,s,i,n,a,o,r,h)=>{const c=this.images[t];if(c&&c.source){const t=this.ctx2d(),h=60*Math.PI/180,l=1/(1+Math.max(.001,Math.sqrt(e*e+s*s+Math.max(i,.001)*Math.max(i,.001)))*Math.tan(h/2)*.01),u=o*l,d=r*l;t.drawImage(c.source,n+(o-u)/2,a+(r-d)/2,u,d)}},eng_player_create:()=>{this.ensureAudio();const t=this.engineNodes.length,e=this.audioCtx.createGain();return this.engineNodes.push({kind:"player",source:null,gain:e,queue:[],loops:0}),t},eng_player_release:t=>{const e=this.engineNodes[t];if(e){try{e.source&&e.source.stop()}catch(t){}try{e.gain.disconnect()}catch(t){}this.engineNodes[t]=null}},eng_mixer_create:()=>{this.ensureAudio();const t=this.engineNodes.length,e=this.audioCtx.createGain();return this.engineNodes.push({kind:"mixer",gain:e}),t},eng_node_set_volume:(t,e)=>{const s=this.engineNodes[t];s&&(s.gain.gain.value=e)},eng_node_set_pan:(t,e)=>{const s=this.engineNodes[t];if(s){if(!s.panner&&this.audioCtx.createStereoPanner){s.panner=this.audioCtx.createStereoPanner();try{s.gain.disconnect(),s.gain.connect(s.panner)}catch(t){}s.pannerDownstream&&s.panner.connect(s.pannerDownstream)}s.panner&&(s.panner.pan.value=e)}},eng_connect:(t,e)=>{const s=this.engineNodes[t];if(!s)return;const i=e<0?this.audioCtx.destination:this.engineNodes[e]?this.engineNodes[e].gain:null;if(i)try{(s.panner||s.gain).connect(i),s.panner&&(s.pannerDownstream=i)}catch(t){}},eng_player_schedule_buffer:(t,e,s)=>{const i=this.engineNodes[t];if(!i||"player"!==i.kind)return 0;const n=this.sounds[e];return n?(i.queue.push({buffer:n,loops:s}),1):0},eng_player_play:t=>{const e=this.engineNodes[t];if(!e||"player"!==e.kind)return;if(0===e.queue.length)return;const s=e.queue.shift(),i=this.audioCtx.createBufferSource();i.buffer=s.buffer,i.loop=s.loops<0,i.connect(e.gain);try{i.start()}catch(t){}e.source=i},eng_player_stop:t=>{const e=this.engineNodes[t];if(e&&"player"===e.kind){try{e.source&&e.source.stop()}catch(t){}e.source=null,e.queue=[]}},eng_start:()=>{this.ensureAudio(),"suspended"===this.audioCtx.state&&this.audioCtx.resume()},eng_stop:()=>{this.audioCtx&&"running"===this.audioCtx.state&&this.audioCtx.suspend()},gfx_upload_pixels:(t,e,s,i,n)=>{let a,o=t;if(t<=0){const t=document.createElement("canvas");t.width=e,t.height=s,a={source:t,width:e,height:s},o=this.images.length,this.images.push(a)}else if(a=this.images[t],!a)return 0;let r=a.source;r instanceof HTMLCanvasElement||(r=document.createElement("canvas"),a.source=r),r.width===e&&r.height===s||(r.width=e,r.height=s);const h=r.getContext("2d"),c=h.createImageData(e,s),l=new Uint8Array(this.wasmMemory.buffer,i,Math.min(n,e*s*4));return c.data.set(l),h.putImageData(c,0,0),a.width=e,a.height=s,this.glTexFromImage.delete(o),o},gfx_offscreen_begin:(t,e)=>{const s=window.devicePixelRatio||1,i=document.createElement("canvas");i.width=Math.max(1,Math.round(t*s)),i.height=Math.max(1,Math.round(e*s));const n=this.makeCtx(i,{alpha:!0});n.scale(s,s);const a=this.targets.length;return this.targets.push({canvas:i,ctx:n,logical:{w:t,h:e},savedTarget:this.curTarget}),this.curTarget=a,a},gfx_offscreen_end_to_image:t=>{if(t<=0||t>=this.targets.length)return 0;const e=this.targets[t];this.curTarget=null!=e.savedTarget?e.savedTarget:0;const s={source:e.canvas,width:e.canvas.width,height:e.canvas.height};let i;return this.freeImageSlots&&this.freeImageSlots.length?(i=this.freeImageSlots.pop(),this.images[i]=s):(i=this.images.length,this.images.push(s)),t===this.targets.length-1?this.targets.pop():this.targets[t]=null,i},gfx_offscreen_end_discard:t=>{if(t<=0||t>=this.targets.length)return;const e=this.targets[t];this.curTarget=null!=e.savedTarget?e.savedTarget:0,t===this.targets.length-1?this.targets.pop():this.targets[t]=null},gfx_free_image:t=>{t>0&&this.images&&this.images[t]&&(this.images[t]=null,(this.freeImageSlots||(this.freeImageSlots=[])).push(t))},gfx_draw_shadow_image:(t,e,s,i,n,a,o)=>{const r=this.images[t];if(!r||!r.source)return;const h=this.ctx2d(),c=h.getTransform(),l=window.devicePixelRatio||1,u=1e5,d=c.a*e+c.c*s+c.e,g=c.b*e+c.d*s+c.f,m=i*c.a,f=n*c.d;h.save(),h.setTransform(1,0,0,1,0,0),h.shadowColor=this.css(o),h.shadowBlur=a*l+25,h.shadowOffsetX=d+u,h.shadowOffsetY=g,h.drawImage(r.source,-u,0,m,f),h.restore()},gfx_set_filter:(t,e)=>{this.ctx2d().filter=this.cstr(t,e)},gfx_clear_filter:()=>{this.ctx2d().filter="none"},gfx_set_shadow:(t,e,s,i)=>{const n=this.ctx2d();n.shadowBlur=t,n.shadowOffsetX=e,n.shadowOffsetY=s,n.shadowColor=this.css(i)},gfx_clear_shadow:()=>{const t=this.ctx2d();t.shadowBlur=0,t.shadowOffsetX=0,t.shadowOffsetY=0,t.shadowColor="transparent"},gfx_set_composite:t=>{this.ctx2d().globalCompositeOperation=["source-over","destination-in","destination-out","lighter","multiply","screen","overlay"][t]||"source-over"},vid_load:(t,e)=>{const s=this.cstr(t,e),i=document.createElement("video");i.src=(this.assetRoot||"")+"/videos/"+s,i.preload="auto",i.playsInline=!0,i.muted=!1,i.style.position="absolute",i.style.pointerEvents="none",i.style.display="none",document.body.appendChild(i),this.videos||(this.videos=[]);const n=this.videos.length;return this.videos.push(i),n},vid_play:t=>{this.videos&&this.videos[t]&&this.videos[t].play().catch(()=>{})},vid_pause:t=>{this.videos&&this.videos[t]&&this.videos[t].pause()},vid_stop:t=>{const e=this.videos&&this.videos[t];e&&(e.pause(),e.currentTime=0)},vid_set_rect:(t,e,s,i,n)=>{const a=this.videos&&this.videos[t];if(!a)return;const o=this.canvas.getBoundingClientRect(),r=Math.min(o.width/LOGICAL_W,o.height/LOGICAL_H);a.style.left=o.left+e*r+"px",a.style.top=o.top+s*r+"px",a.style.width=i*r+"px",a.style.height=n*r+"px",a.style.display=""},vid_set_visible:(t,e)=>{const s=this.videos&&this.videos[t];s&&(s.style.display=e?"":"none")},snd_set_pan:(t,e)=>{const s=this.voices.get(t);if(s){if(!s.panner&&this.audioCtx&&this.audioCtx.createStereoPanner){s.panner=this.audioCtx.createStereoPanner();try{s.gain.disconnect(),s.gain.connect(s.panner),s.panner.connect(this.audioCtx.destination)}catch(t){}}s.panner&&(s.panner.pan.value=Math.max(-1,Math.min(1,e)))}},snd_set_rate:(t,e)=>{const s=this.voices.get(t);if(s&&s.source)try{s.source.playbackRate.value=Math.max(.0625,Math.min(e,16))}catch(t){}},img_polygon_from_alpha:(t,e,s,i)=>{const n=this.images&&this.images[t];if(!n||!n.source)return 0;const a=n.width||n.source.naturalWidth||n.source.width,o=n.height||n.source.naturalHeight||n.source.height;if(!a||!o)return 0;const r=document.createElement("canvas");r.width=a,r.height=o;const h=r.getContext("2d",{willReadFrequently:!0});let c;h.drawImage(n.source,0,0);try{c=h.getImageData(0,0,a,o).data}catch(t){return 0}const l=255*Math.max(0,Math.min(e,1)),u=(t,e)=>t>=0&&e>=0&&t<a&&e<o&&c[4*(e*a+t)+3]>=l;let d=-1,g=-1;t:for(let t=0;t<o;t++)for(let e=0;e<a;e++)if(u(e,t)){d=e,g=t;break t}if(d<0)return 0;const m=[[1,0],[1,1],[0,1],[-1,1],[-1,0],[-1,-1],[0,-1],[1,-1]];let f=d,p=g,_=0;const x=[[f,p]],y=4*(a+o);for(let t=0;t<y;t++){let t=!1;for(let e=0;e<8;e++){const s=_+6+e&7,i=f+m[s][0],n=p+m[s][1];if(u(i,n)){f=i,p=n,_=s,x.push([f,p]),t=!0;break}}if(!t)break;if(f===d&&p===g&&x.length>2)break}const v=rdpSimplify(x,Math.max(.5,Math.min(a,o)/64)),w=v.length>i?v.slice(0,i):v,b=this.dv();for(let t=0;t<w.length;t++){const e=w[t][0]-a/2,i=o/2-w[t][1];b.setFloat32(s+8*t,e,!0),b.setFloat32(s+8*t+4,i,!0)}return w.length},evt_poll:(t,e,s,i,n)=>{const a=this.events.shift();if(!a)return 0;const o=this.dv();return o.setInt32(t,0|a.type,!0),o.setInt32(e,0|a.a,!0),o.setInt32(s,0|a.b,!0),o.setInt32(i,0|a.c,!0),o.setInt32(n,0|a.d,!0),1},win_set_title:(t,e)=>{document.title=this.cstr(t,e)},win_width:()=>LOGICAL_W,win_height:()=>LOGICAL_H,dbg_set_overlays:t=>{this._overlayFlags=0|t},win_request_fullscreen:()=>{const t=this.canvas;if(document.fullscreenEnabled)t.requestFullscreen().catch(()=>this._pseudoFullscreen(!0));else if(document.webkitFullscreenEnabled)try{t.webkitRequestFullscreen()}catch(t){this._pseudoFullscreen(!0)}else this._pseudoFullscreen(!0)},win_exit_fullscreen:()=>{document.exitFullscreen?document.exitFullscreen().catch(()=>{}):document.webkitExitFullscreen&&document.webkitExitFullscreen(),this._pseudoFullscreen(!1)},win_download:(t,e,s,i)=>{const n=this.cstr(t,e)||"download.json",a=this.cstr(s,i);try{const t=URL.createObjectURL(new Blob([a],{type:"application/json"})),e=document.createElement("a");e.href=t,e.download=n,document.body.appendChild(e),e.click(),e.remove(),setTimeout(()=>URL.revokeObjectURL(t),1e3)}catch(t){}},store_get:(t,e,s,i)=>{const n=this.cstr(t,e),a=localStorage.getItem(n);if(null===a)return-1;const o=this.textEncoder.encode(a),r=Math.min(o.length,i);return this.u8(s,r).set(o.subarray(0,r)),o.length},store_set:(t,e,s,i)=>{const n=this.cstr(t,e),a=this.cstr(s,i);try{localStorage.setItem(n,a)}catch(t){}}}}ctx2d(){return this.targets[this.curTarget].ctx}ensureGL(){if(this.gl)return this.gl;const t=document.createElement("canvas");t.width=1024,t.height=1024;const e=t.getContext("webgl2",{premultipliedAlpha:!1,alpha:!0});if(!e)return console.warn("[boss] WebGL2 unavailable; shaders will no-op"),null;this.glCanvas=t,this.gl=e;const s=e.createVertexArray();e.bindVertexArray(s);const i=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,i);const n=new Float32Array([-1,-1,0,1,1,-1,1,1,-1,1,0,0,1,1,1,0]);return e.bufferData(e.ARRAY_BUFFER,n,e.STATIC_DRAW),e.enableVertexAttribArray(0),e.vertexAttribPointer(0,2,e.FLOAT,!1,16,0),e.enableVertexAttribArray(1),e.vertexAttribPointer(1,2,e.FLOAT,!1,16,8),e.bindVertexArray(null),this.glQuadVAO=s,e}buildShaderFrag(t){return`#version 300 es\nprecision highp float;\nin vec2 v_tex_coord;\nin vec4 v_color_mix;\nuniform sampler2D u_texture;\nuniform float u_time;\nout vec4 _outColor;\n#define gl_FragColor _outColor\n#define texture2D texture\nvec4 SKDefaultShading() {\n return texture(u_texture, v_tex_coord) * v_color_mix;\n}\n${t}\n`}buildShaderVert(){return"#version 300 es\nin vec2 a_position;\nin vec2 a_uv;\nout vec2 v_tex_coord;\nout vec4 v_color_mix;\nuniform vec4 u_color_mix;\nvoid main() {\n v_tex_coord = a_uv;\n v_color_mix = u_color_mix;\n gl_Position = vec4(a_position, 0.0, 1.0);\n}"}compileShaderObj(t,e,s){const i=t.createShader(e);if(t.shaderSource(i,s),t.compileShader(i),!t.getShaderParameter(i,t.COMPILE_STATUS)){const e=t.getShaderInfoLog(i);return console.warn("[boss] shader compile failed:",e,"\n\nSource:\n",s),t.deleteShader(i),null}return i}linkShaderProgram(t,e){const s=this.compileShaderObj(t,t.VERTEX_SHADER,this.buildShaderVert()),i=this.compileShaderObj(t,t.FRAGMENT_SHADER,e);if(!s||!i)return null;const n=t.createProgram();return t.attachShader(n,s),t.attachShader(n,i),t.bindAttribLocation(n,0,"a_position"),t.bindAttribLocation(n,1,"a_uv"),t.linkProgram(n),t.getProgramParameter(n,t.LINK_STATUS)?n:(console.warn("[boss] program link failed:",t.getProgramInfoLog(n)),null)}glTextureFor(t){const e=this.gl;let s=this.glTexFromImage.get(t);if(s)return s;const i=this.images[t];return i&&i.source?(s=e.createTexture(),e.bindTexture(e.TEXTURE_2D,s),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,e.RGBA,e.UNSIGNED_BYTE,i.source),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),this.glTexFromImage.set(t,s),s):null}glResize(t,e){const s=this.gl,i=Math.max(1,Math.round(t)),n=Math.max(1,Math.round(e));this.glCanvas.width===i&&this.glCanvas.height===n||(this.glCanvas.width=i,this.glCanvas.height=n),s.viewport(0,0,i,n),s.clearColor(0,0,0,0),s.clear(s.COLOR_BUFFER_BIT)}uniLoc(t,e){const s=this.shaders[t];if(!s)return null;let i=s.uniformLocs.get(e);return void 0!==i||(i=this.gl.getUniformLocation(s.program,e),s.uniformLocs.set(e,i)),i}ensureLightingShader(){if(null!=this.lightingShader)return this.lightingShader;const t=this.ensureGL();if(!t)return null;const e=this.linkShaderProgram(t,this.buildShaderFrag("\nuniform sampler2D u_normal;\nuniform vec4 u_ambient;\nuniform vec4 u_lightPositions[8];\nuniform vec4 u_lightColors[8];\nuniform int u_lightCount;\nvoid main() {\n vec4 base = SKDefaultShading();\n vec3 normal = vec3(0.0, 0.0, 1.0);\n vec4 nm = texture(u_normal, v_tex_coord);\n // If a real normal map texture is bound (alpha != 0 placeholder), use it.\n if (nm.a > 0.001) { normal = normalize(nm.xyz * 2.0 - 1.0); }\n vec3 accum = u_ambient.rgb * base.rgb;\n for (int i = 0; i < 8; i++) {\n if (i >= u_lightCount) break;\n vec3 toLight = vec3(u_lightPositions[i].xy - gl_FragCoord.xy, u_lightPositions[i].z);\n float dist = length(toLight);\n vec3 dir = normalize(toLight);\n float diff = max(dot(normal, dir), 0.0);\n float fall = 1.0 / (1.0 + u_lightColors[i].a * dist);\n accum += base.rgb * u_lightColors[i].rgb * diff * fall * u_lightPositions[i].w;\n }\n gl_FragColor = vec4(accum, base.a);\n}"));if(!e)return this.lightingShader=-1,null;const s=this.shaders.length;return this.shaders.push({program:e,uniformLocs:new Map,srcText:"<built-in lighting>"}),this.lightingShader=s,s}glRunPassToCanvas(t,e,s,i,n,a,o,r,h,c){const l=this.ensureGL();if(!l)return;const u=this.shaders[t];if(!u)return;const d=this.glTextureFor(e);if(!d)return;const g=s>0?this.glTextureFor(s):null;this.glResize(a,o),l.useProgram(u.program),l.bindVertexArray(this.glQuadVAO),l.activeTexture(l.TEXTURE0),l.bindTexture(l.TEXTURE_2D,d);const m=this.uniLoc(t,"u_texture");m&&l.uniform1i(m,0);const f=this.uniLoc(t,"u_time");f&&l.uniform1f(f,r);const p=this.uniLoc(t,"u_color_mix");if(p){const t=(h>>>24&255)/255,e=(h>>>16&255)/255,s=(h>>>8&255)/255,i=(255&h)/255;l.uniform4f(p,t,e,s,i)}if(g){l.activeTexture(l.TEXTURE1),l.bindTexture(l.TEXTURE_2D,g);const e=this.uniLoc(t,"u_normal");e&&l.uniform1i(e,1)}c&&c(l,this),l.drawArrays(l.TRIANGLE_STRIP,0,4);this.ctx2d().drawImage(this.glCanvas,0,0,this.glCanvas.width,this.glCanvas.height,i,n,a,o)}makeCtx(t,e={}){return this.isP3&&(e={...e,colorSpace:"display-p3"}),t.getContext("2d",e)}css(t){const e=t>>>24&255,s=t>>>16&255,i=t>>>8&255,n=(255&t)/255;return this.isP3?`color(display-p3 ${(e/255).toFixed(4)} ${(s/255).toFixed(4)} ${(i/255).toFixed(4)} / ${n})`:`rgba(${e},${s},${i},${n})`}applyFont(t,e,s,i){const n=e>0&&this.fonts[e]?this.fonts[e]:this.defaultFontFamily;t.font=`${s}px ${n}`,void 0===this.hasLetterSpacing&&(this.hasLetterSpacing="letterSpacing"in t),this.hasLetterSpacing&&(t.letterSpacing=`${i||0}px`)}_tintRaster(t,e,s,i,n,a,o,r){const h=t.source,c=h&&(h.naturalWidth||h.width),l=h&&(h.naturalHeight||h.height);if(!c||!l)return null;const u=i<0||n<0,d=u?0:e,g=u?0:s,m=Math.max(1,Math.ceil(u?c:i)),f=Math.max(1,Math.ceil(u?l:n)),p="r,"+d+","+g+","+m+","+f+","+a+","+o+","+r;return this._tintInto(t,p,h,d,g,m,f,a,o,r)}_tintSVG(t,e,s,i,n,a,o){const r=o.getTransform(),h=Math.hypot(r.a,r.b)||1,c=Math.hypot(r.c,r.d)||1,l=this.svgRaster(t,Math.ceil(e*h),Math.ceil(s*c));if(!l||!l.width||!l.height)return null;const u="s,"+l.width+"x"+l.height+","+i+","+n+","+a;return this._tintInto(t,u,l,0,0,l.width,l.height,i,n,a)}_tintInto(t,e,s,i,n,a,o,r,h,c){t._tintCache||(t._tintCache=new Map);let l=t._tintCache.get(e);if(l)return t._tintCache.delete(e),t._tintCache.set(e,l),l;l=document.createElement("canvas"),l.width=a,l.height=o;const u=l.getContext("2d");try{u.drawImage(s,i,n,a,o,0,0,a,o),u.globalCompositeOperation="multiply",u.fillStyle=`rgb(${r},${h},${c})`,u.fillRect(0,0,a,o),u.globalCompositeOperation="destination-in",u.drawImage(s,i,n,a,o,0,0,a,o)}catch(t){return null}for(l._bytes=a*o*4,this._canvasBytes=(this._canvasBytes||0)+l._bytes,t._tintCache.set(e,l);t._tintCache.size>10;){const e=t._tintCache.keys().next().value,s=t._tintCache.get(e);s&&(this._canvasBytes-=s._bytes||0,s.width=0,s.height=0),t._tintCache.delete(e)}return l}lookupImage(t){let e=this.imageByName.get(t);if(void 0!==e)return e;if(e=this.imageByName.get(this.basename(t)),void 0!==e)return e;if(!this._imageByNameLC){this._imageByNameLC=new Map;for(const[t,e]of this.imageByName)this._imageByNameLC.set(t.toLowerCase(),e)}return e=this._imageByNameLC.get(String(t).toLowerCase()),void 0!==e?e:(e=this._imageByNameLC.get(this.basename(t).toLowerCase()),void 0!==e?e:0)}lookupSound(t){let e=this.soundByName.get(t);return void 0!==e?e:(e=this.soundByName.get(this.basename(t)),void 0!==e?e:0)}lookupFont(t){let e=this.fontByName.get(t);return void 0!==e?e:(e=this.fontByName.get(this.basename(t)),void 0!==e?e:(e=this.fontByName.get(String(t).replace(/[\s-]/g,"").toLowerCase()),void 0!==e?e:0))}basename(t){const e=t.split("/").pop(),s=e.lastIndexOf(".");return s>0?e.slice(0,s):e}async loadSVG(t){let e=t,s=0,i=0;const n=e.match(/viewBox\s*=\s*["']\s*[-\d.]+\s+[-\d.]+\s+([-\d.]+)\s+([-\d.]+)/);n&&(s=parseFloat(n[1]),i=parseFloat(n[2]));const a=/<svg[^>]*\swidth\s*=/.test(e),o=/<svg[^>]*\sheight\s*=/.test(e);a&&o||!s||!i||(e=e.replace(/<svg/,`<svg width="${s}" height="${i}"`));const r=URL.createObjectURL(new Blob([e],{type:"image/svg+xml"})),h=new Image;h.src=r;try{await h.decode()}catch(t){await new Promise(t=>{h.onload=t,h.onerror=t})}h._svgURL=r;const c=h.naturalWidth||s||1,l=h.naturalHeight||i||1;return{source:h,width:c,height:l,svg:!0,_svgCache:new Map}}drawSVG(t,e,s,i,n,a,o,r,h,c){const l=t.getTransform(),u=Math.hypot(l.a,l.b)||1,d=Math.hypot(l.c,l.d)||1;if(n<0||a<0){const s=this.svgRaster(e,Math.ceil(h*u),Math.ceil(c*d));t.drawImage(s,o,r,h,c)}else{const l=this.svgRaster(e,Math.ceil(e.width*u),Math.ceil(e.height*d)),g=l.width/e.width,m=l.height/e.height;t.drawImage(l,s*g,i*m,n*g,a*m,o,r,h,c)}}_blendTint(t,e,s,i,n,a,o,r,h){let c,l,u,d=0,g=0;if(t.svg){const e=h.getTransform(),s=Math.hypot(e.a,e.b)||1,i=Math.hypot(e.c,e.d)||1;if(c=this.svgRaster(t,Math.ceil(a*s),Math.ceil(o*i)),!c||!c.width)return null;l=c.width,u=c.height}else{if(c=t.source,!c)return null;const a=i<0||n<0;d=a?0:e,g=a?0:s,l=Math.max(1,Math.ceil(a?c.naturalWidth||c.width:i)),u=Math.max(1,Math.ceil(a?c.naturalHeight||c.height:n))}t._blendCache||(t._blendCache=new Map);const m=l+"x"+u+","+(248&r.r)+","+(248&r.g)+","+(248&r.b)+","+Math.round(16*r.bf);let f=t._blendCache.get(m);if(f&&f.width)return t._blendCache.delete(m),t._blendCache.set(m,f),f;f=document.createElement("canvas"),f.width=l,f.height=u;const p=f.getContext("2d");try{p.drawImage(c,d,g,l,u,0,0,l,u),p.globalCompositeOperation="source-atop",p.globalAlpha=r.bf,p.fillStyle="rgb("+r.r+","+r.g+","+r.b+")",p.fillRect(0,0,l,u)}catch(t){return null}for(f._bytes=l*u*4,this._canvasBytes=(this._canvasBytes||0)+f._bytes,t._blendCache.set(m,f);t._blendCache.size>64;){const e=t._blendCache.keys().next().value,s=t._blendCache.get(e);s&&(this._canvasBytes-=s._bytes||0,s.width=0,s.height=0),t._blendCache.delete(e)}return f}svgRaster(t,e,s){e=Math.max(1,Math.min(0|e,8192)),s=Math.max(1,Math.min(0|s,8192));const i=Math.max(t.width,t.height)||1,n=Math.max(e,s);let a=1;for(;i*a<n&&a<64;)a*=2;const o=(e=Math.max(1,Math.min(Math.round(t.width*a),8192)))+"x"+(s=Math.max(1,Math.min(Math.round(t.height*a),8192))),r=t._svgCache;let h=r.get(o);if(h)return r.delete(o),r.set(o,h),h;h=document.createElement("canvas"),h.width=e,h.height=s;const c=h.getContext("2d");c.imageSmoothingEnabled=!0,c.imageSmoothingQuality="high";try{c.drawImage(t.source,0,0,e,s)}catch(t){}for(h._bytes=e*s*4,this._canvasBytes=(this._canvasBytes||0)+h._bytes,r.set(o,h);r.size>8;){const t=r.keys().next().value,e=r.get(t);e&&(this._canvasBytes-=e._bytes||0,e.width=0,e.height=0),r.delete(t)}return h}_pickTTSVoice(){if(this._ttsVoice)return this._ttsVoice;if("undefined"==typeof speechSynthesis)return null;const t=speechSynthesis.getVoices();if(!t||!t.length)return null;const e=this._ttsRobotic,s=this._ttsFemale,i=(t,e)=>{const s=((t.voiceURI||"")+" "+(t.name||"")).toLowerCase();return e.some(t=>s.includes(t))},n=t.filter(t=>!i(t,e)),a=n.filter(t=>!i(t,s)),o=n.filter(t=>i(t,s)),r=t=>t.filter(t=>"en-US"===t.lang),h=t=>t.filter(t=>(t.lang||"").startsWith("en")),c=t=>{const e=((t.voiceURI||"")+" "+(t.name||"")).toLowerCase();return e.includes("premium")?2:e.includes("enhanced")?1:0},l=t=>{if(!t.length)return null;for(const e of this._ttsPreferred){const s=t.filter(t=>((t.voiceURI||"")+" "+(t.name||"")).toLowerCase().includes(e));if(s.length)return s.sort((t,e)=>c(e)-c(t)),s[0]}return t.slice().sort((t,e)=>c(e)-c(t))[0]},u=l(r(a))||l(h(a))||l(a)||l(r(o))||l(h(o))||l(o);return this._ttsVoice=u||null,this._ttsVoice}_primeSpeech(){if(this._speechPrimed||"undefined"==typeof speechSynthesis||IS_IOS)return;this._speechPrimed=!0;try{speechSynthesis.resume();const t=new SpeechSynthesisUtterance(" ");t.volume=0,speechSynthesis.speak(t)}catch(t){}let t=0;const e=()=>{this._ttsVoice=null;const s=this._pickTTSVoice();if(s&&this._ttsPending.length){const t=this._ttsPending;this._ttsPending=[];for(const e of t){e.voice=s;try{speechSynthesis.speak(e)}catch(t){}}}!s&&++t<8&&setTimeout(e,250)};e()}_reprimeSpeech(){if("undefined"!=typeof speechSynthesis&&!IS_IOS)try{if(speechSynthesis.resume(),!speechSynthesis.speaking&&!speechSynthesis.pending){const t=new SpeechSynthesisUtterance(" ");t.volume=0,speechSynthesis.speak(t)}}catch(t){}}_setDuck(t){if(this.duckFactor=t,this.audioCtx)for(const e of this.voices.values())e.gain&&e.gain.gain.setTargetAtTime((e.base??1)*t,this.audioCtx.currentTime,.05)}_pseudoFullscreen(t){this._pseudoFsOn=t;const e=this.canvas.style;t?(e.position="fixed",e.top="0",e.left="0",e.width="100vw",e.height="100dvh",e.maxWidth="none",e.maxHeight="none",e.zIndex="99999"):(e.position="",e.top="",e.left="",e.width="",e.height="",e.maxWidth="",e.maxHeight="",e.zIndex="");try{window.parent&&window.parent!==window&&window.parent.postMessage(t?"wasmweb:fullscreen":"wasmweb:exit-fullscreen","*")}catch(t){}setTimeout(()=>{try{window.dispatchEvent(new Event("resize"))}catch(t){}},0)}ensureAudio(){if(!this.audioCtx){const t=window.AudioContext||window.webkitAudioContext;if(t)try{this.audioCtx=new t;const e=()=>{try{this.audioCtx&&this.audioCtx.close()}catch(t){}this.audioCtx=null};window.addEventListener("pagehide",e),window.addEventListener("beforeunload",e)}catch(t){console.error("AudioContext construction failed — Chrome hardware-context limit (6) likely reached from earlier reloads. Quit and reopen the browser to reset it.",t),this.audioCtx=null}}return this.audioCtx&&"running"!==this.audioCtx.state&&"closed"!==this.audioCtx.state&&!document.hidden&&this.audioCtx.resume(),this.audioCtx}async preload(){const t=await this.discoverAssets();await Promise.all(t.fonts.map(async t=>{const e=this.basename(t);try{const s=await fetch(bust(`${ASSET_ROOT}/${t}`)),i=await s.arrayBuffer(),n=new FontFace(e,i);await n.load(),document.fonts.add(n),this.fonts.push(e);const a=this.fonts.length-1;this.registerName(this.fontByName,t,e,a)}catch(e){console.warn("font load failed",t,e)}})),await Promise.all(t.images.map(async t=>{try{const e=await fetch(bust(`${ASSET_ROOT}/${t}`));if(/\.svg$/i.test(t))this.images.push(await this.loadSVG(await e.text()));else{const t=await createImageBitmap(await e.blob());this.images.push({source:t,width:t.width,height:t.height})}const s=this.images.length-1;this.registerName(this.imageByName,t,this.basename(t),s)}catch(e){console.warn("image load failed",t,e)}}));const e=this.ensureAudio();await Promise.all(t.sounds.map(async t=>{try{const s=await fetch(bust(`${ASSET_ROOT}/${t}`)),i=await s.arrayBuffer(),n=await e.decodeAudioData(i);this.sounds.push(n);const a=this.sounds.length-1;this.registerName(this.soundByName,t,this.basename(t),a)}catch(e){console.warn("sound load failed",t,e)}})),await Promise.all((t.texts||["levels.json"]).map(async t=>{try{const e=await fetch(bust(`${ASSET_ROOT}/${t}`));if(!e.ok)return;const s=await e.text(),i=t.split("/").pop();this.texts.set(t,s),this.texts.set("assets/"+t,s),this.texts.set(i,s),this.texts.set(this.basename(t),s)}catch(e){console.warn("text load failed",t,e)}}))}registerName(t,e,s,i){t.set(e,i),t.set("assets/"+e,i),t.set(s,i)}async discoverAssets(){const t=await fetch(bust("manifest.json")).then(t=>t.ok?t.json():null).catch(()=>null);return t||{fonts:["fonts/JetBrainsMono-Bold.ttf","fonts/MarkerFelt-Thin.ttf","fonts/MarkerFelt-Wide.ttf"],images:["images/red-stapler.png"],sounds:[]}}wireInput(){const t=()=>{this.ensureAudio(),this._primeSpeech()};addEventListener("keydown",t,{once:!0}),addEventListener("mousedown",t,{once:!0}),addEventListener("touchstart",t,{once:!0}),document.addEventListener("visibilitychange",()=>{document.hidden?this.audioCtx&&"running"===this.audioCtx.state&&this.audioCtx.suspend():this.ensureAudio()}),addEventListener("keydown",t=>{t.repeat||this._reprimeSpeech();const e=DOM_TO_SF.get(t.code);if(void 0===e)return;t.preventDefault();const s=this.pressed.has(t.code);this.pressed.add(t.code),s||this.events.push({type:EVT.KeyPressed,a:e,b:t.shiftKey?1:0,c:t.metaKey||t.ctrlKey?1:0,d:0})}),addEventListener("keyup",t=>{const e=DOM_TO_SF.get(t.code);void 0!==e&&(t.preventDefault(),this.pressed.delete(t.code),this.events.push({type:EVT.KeyReleased,a:e,b:t.shiftKey?1:0,c:t.metaKey||t.ctrlKey?1:0,d:0}))}),this.canvas.addEventListener("mousedown",t=>{this._reprimeSpeech();const e=2===t.button?1:0===t.button?0:-1;if(e<0)return;this.mouseDown[e]=!0;const s=this.toLogical(t);this.events.push({type:EVT.MouseButtonPressed,a:e,b:s.x,c:s.y,d:0})}),addEventListener("mouseup",t=>{const e=2===t.button?1:0===t.button?0:-1;if(e<0)return;this.mouseDown[e]=!1;const s=this.toLogical(t);this.events.push({type:EVT.MouseButtonReleased,a:e,b:s.x,c:s.y,d:0})}),this.canvas.addEventListener("mousemove",t=>{const e=this.toLogical(t);this.mouseX=e.x,this.mouseY=e.y,this.events.push({type:EVT.MouseMoved,a:e.x,b:e.y,c:0,d:0})}),this.canvas.addEventListener("contextmenu",t=>t.preventDefault()),this.canvas.style.touchAction="none";const e=t=>this.toLogical({clientX:t.clientX,clientY:t.clientY});this._fingerSlots=new Map;const s=t=>{if(!this._fingerSlots.has(t)){let e=0;const s=new Set(this._fingerSlots.values());for(;s.has(e);)e++;this._fingerSlots.set(t,e)}return this._fingerSlots.get(t)};this.canvas.addEventListener("touchstart",t=>{if(this._reprimeSpeech(),!t.changedTouches.length)return;this.mouseDown[0]=!0;const i=e(t.changedTouches[0]);this._touchStartX=i.x,this._touchStartY=i.y,this._touchMoved=!1,this.mouseX=i.x,this.mouseY=i.y,this.events.push({type:EVT.MouseButtonPressed,a:0,b:i.x,c:i.y,d:0});for(const i of t.changedTouches){const t=e(i);this.events.push({type:EVT.TouchBegan,a:s(i.identifier),b:t.x,c:t.y,d:0})}t.preventDefault()},{passive:!1}),this.canvas.addEventListener("touchmove",t=>{if(!t.touches.length)return;const i=e(t.touches[0]);(Math.abs(i.x-this._touchStartX)>16||Math.abs(i.y-this._touchStartY)>16)&&(this._touchMoved=!0),this.mouseX=i.x,this.mouseY=i.y,this.events.push({type:EVT.MouseMoved,a:i.x,b:i.y,c:0,d:0});for(const i of t.changedTouches){const t=e(i);this.events.push({type:EVT.TouchMoved,a:s(i.identifier),b:t.x,c:t.y,d:0})}t.preventDefault()},{passive:!1});const i=t=>{if(!t.changedTouches.length)return;this.mouseDown[0]=!1;const i=e(t.changedTouches[0]);this.events.push({type:EVT.MouseButtonReleased,a:0,b:i.x,c:i.y,d:0});for(const i of t.changedTouches){const t=e(i);this.events.push({type:EVT.TouchEnded,a:s(i.identifier),b:t.x,c:t.y,d:0}),this._fingerSlots.delete(i.identifier)}t.preventDefault()};this.canvas.addEventListener("touchend",i,{passive:!1}),this.canvas.addEventListener("touchcancel",i,{passive:!1});const n=()=>requestAnimationFrame(()=>{this.layout(),this.events.push({type:EVT.Resized,a:LOGICAL_W,b:LOGICAL_H,c:0,d:0})});addEventListener("resize",n),document.addEventListener("fullscreenchange",n),document.addEventListener("webkitfullscreenchange",n),addEventListener("beforeunload",()=>{this.events.push({type:EVT.Closed,a:0,b:0,c:0,d:0})})}pollGamepads(){if(!this.gpEnabled||!navigator.getGamepads)return;const t=navigator.getGamepads(),e=[],s=this.gpAxisDeadzone;for(let i=0;i<4;i++){const n=t[i];if(!n){e[i]=null;continue}const a=new Array(n.buttons.length),o=new Array(n.buttons.length);for(let t=0;t<n.buttons.length;t++){const e=n.buttons[t],s="object"==typeof e?e.value:e?1:0,i="object"==typeof e?e.pressed:!!e;a[t]=i?1:0,o[t]=s}const r=n.axes||[];if(e[i]={buttons:a,values:o,axes:r},!this.gpMapToKeys)continue;const h=a[14]||(r[0]||0)<-s,c=a[15]||(r[0]||0)>s,l=a[12]||(r[1]||0)<-s,u=a[13]||(r[1]||0)>s,d=a[0],g=a[9],m=this.gpPrev[i]||{left:0,right:0,up:0,down:0,fire:0,pause:0};this.emitSynthKey("ArrowLeft",h,m.left),this.emitSynthKey("ArrowRight",c,m.right),this.emitSynthKey("ArrowUp",l,m.up),this.emitSynthKey("ArrowDown",u,m.down),this.emitSynthKey("Space",d,m.fire),this.emitSynthKey("KeyP",g,m.pause),this.gpPrev[i]={left:h,right:c,up:l,down:u,fire:d,pause:g}}this.gpSnap=e}emitSynthKey(t,e,s){if(!e==!s)return;const i=DOM_TO_SF.get(t);e?(this.pressed.add(t),void 0!==i&&this.events.push({type:EVT.KeyPressed,a:i,b:0,c:0,d:0})):(this.pressed.delete(t),void 0!==i&&this.events.push({type:EVT.KeyReleased,a:i,b:0,c:0,d:0}))}layout(){const t=window.devicePixelRatio||1,e=this.canvas.getBoundingClientRect(),s=(e.width||LOGICAL_W)*t,i=(e.height||LOGICAL_H)*t,n=Math.max(1,Math.min(s/LOGICAL_W,i/LOGICAL_H)),a=Math.round(LOGICAL_W*n),o=Math.round(LOGICAL_H*n);this.canvas.width!==a&&(this.canvas.width=a),this.canvas.height!==o&&(this.canvas.height=o),this.baseScale=n,this.offX=0,this.offY=0}toLogical(t){const e=this.canvas.getBoundingClientRect(),s=Math.min(e.width/LOGICAL_W,e.height/LOGICAL_H),i=e.left+(e.width-LOGICAL_W*s)/2,n=e.top+(e.height-LOGICAL_H*s)/2;return{x:Math.round((t.clientX-i)/s),y:Math.round((t.clientY-n)/s)}}async load(t){await this.preload();const e={env:this.envImports(),wasi_snapshot_preview1:this.wasiImports()};let s;try{({instance:s}=await WebAssembly.instantiateStreaming(fetch(bust(t)),e))}catch(i){const n=await fetch(bust(t)).then(t=>t.arrayBuffer());({instance:s}=await WebAssembly.instantiate(n,e))}this.exports=s.exports,this.wasmMemory=this.exports.memory,this.layout(),this.exports._initialize(),this.exports.boot(),this.wireInput();let i=performance.now();const n=t=>{const e=t-i;i=t,this._dcImg=0,this._dcTxt=0,this._msImg=0,this._msTxt=0,this._partTint=null;try{this.pollGamepads();const t=performance.now();this.exports.frame(e),this._frameMs=performance.now()-t,this._frameCount=(this._frameCount||0)+1,this._frameCount>45&&(this._maxFrameMs=Math.max(this._frameMs,this._maxFrameMs||0),this._minFrameMs=null==this._minFrameMs?this._frameMs:Math.min(this._frameMs,this._minFrameMs),this._frameSum=(this._frameSum||0)+this._frameMs,this._frameSamples=(this._frameSamples||0)+1)}catch(t){return void console.error("frame() threw",t)}e>0&&(this._fps=this._fps?.9*this._fps+1e3/e*.1:1e3/e),this.drawFpsOverlay(),requestAnimationFrame(n)};requestAnimationFrame(n)}}window.addEventListener("DOMContentLoaded",()=>{CFG.title&&(document.title=CFG.title);const t=document.getElementById(CFG.canvasId);new Runtime(t).load(CFG.wasmUrl).catch(t=>{console.error("runtime failed to start",t)})});