@datamoc/mw_games 0.36.0 → 0.36.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,21 @@ the public API may still change between minor versions.
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.36.1] - 2026-10-13
11
+
12
+ A maintenance release: the optional Lua dependency catches up with Pyroeis 0.9.0, and the CI no longer hangs on a stuck browser.
13
+
14
+ ### Changed
15
+
16
+ - The optional `@datamoc/pyroeis` dependency moves from `^0.6.0` to `^0.9.0`. No API change on the mwg side.
17
+ - The Actions group is bumped (`actions/setup-node` 7.1.0 and `actions/upload-artifact`).
18
+ - The scheduled browser benchmark uses floors fit for a software-rendered runner (10 fps, p95 250 ms) instead of the GPU defaults it failed every week; the 15% history comparison still catches regressions.
19
+
20
+ ### Fixed
21
+
22
+ - The browser driver kills the whole browser process group when it times out or closes, instead of only the launcher. Surviving helper processes held the output pipes and kept `npm test` alive until the runner's 6 hour limit.
23
+ - The browser start-up budget is 90 s and the CI `check` job is capped at 30 minutes; Linux Chrome runs with `--disable-dev-shm-usage --disable-gpu`.
24
+
10
25
  ## [0.36.0] - 2026-10-12
11
26
 
12
27
  The optional Lua integration uses Pyroeis, with a matching public subpath, factories and types. Games using the Lua host must update their imports to `mwl/pyroeis` and the `Pyroeis` names.
@@ -1,4 +1,4 @@
1
- var mw_games=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=Object.create,n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.getPrototypeOf,o=Object.prototype.hasOwnProperty,s=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(e){throw n=[e],e}},c=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),l=(e,t)=>{let r={};for(var i in e)n(r,i,{get:e[i],enumerable:!0});return t||n(r,Symbol.toStringTag,{value:`Module`}),r},u=(e,t,a,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var c=i(t),l=0,u=c.length,d;l<u;l++)d=c[l],!o.call(e,d)&&d!==a&&n(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(s=r(t,d))||s.enumerable});return e},d=(e,r,i)=>(i=e==null?{}:t(a(e)),u(r||!e||!e.__esModule||!o.call(e,`default`)?n(i,`default`,{value:e,enumerable:!0}):i,e)),f=`0.36.0`,p=class{constructor(e=!1){this.listeners=[],this.stackMode=e}add(e){this.listeners.includes(e)||(this.stackMode?this.listeners.unshift(e):this.listeners.push(e))}remove(e){let t=this.listeners.indexOf(e);t!==-1&&this.listeners.splice(t,1)}removeAll(){this.listeners.length=0}get size(){return this.listeners.length}dispatch(e){let t=this.listeners.slice();for(let n of t)if(this.listeners.includes(n)&&n(e)===!0)return!0;return!1}},m=class{constructor(){this.onDestroy=new p,this.destroyed=!1}update(e){}resize(e,t){}onSuspend(){}onResume(e){}destroy(){this.destroyed||(this.destroyed=!0,this.onDestroy.dispatch(),this.onDestroy.removeAll(),this.teardown())}teardown(){}get isDestroyed(){return this.destroyed}},h=class{constructor(){this.scenes=[]}get current(){return this.scenes.length===0?null:this.scenes[this.scenes.length-1]}get depth(){return this.scenes.length}replace(e){for(let e of this.scenes)e.destroy();this.scenes=[e],e.create()}push(e){this.current?.onSuspend(),this.scenes.push(e),e.create()}pop(e){if(this.scenes.length<=1)throw Error(`cannot pop the last scene off the stack - switchScene somewhere instead`);this.scenes.pop()?.destroy(),this.current?.onResume(e)}update(e){this.current?.update(e)}resize(e,t){for(let n of this.scenes)n.resize(e,t)}destroy(){for(let e of this.scenes)e.destroy();this.scenes=[]}},g=class{constructor(){this.items=new Map}register(e,t){if(this.items.has(e))throw Error(`"${e}" is already registered`);this.items.set(e,t)}get(e){let t=this.items.get(e);if(t===void 0)throw Error(`no registration named "${e}"`);return t}has(e){return this.items.has(e)}list(){return[...this.items.keys()]}};function _(){let e=1,t=new Map;return{get size(){return t.size},put(n){let r=e++;return t.set(r,n),r},get(e){let n=t.get(e);if(n===void 0)throw Error(`unknown handle ${e}`);return n},drop(e){t.delete(e)},clear(){t.clear()},with(n,r){let i=e++;t.set(i,n);try{return r(i)}finally{t.delete(i)}}}}var v=class{constructor(){this.order=[],this.registry=new g}add(e,t){this.registry.register(e.name,e),this.order.push(e),e.create?.(t)}has(e){return this.registry.has(e)}get(e){return this.registry.get(e)}update(e,t){for(let n of this.order)n.update?.(e,t)}resize(e,t,n){for(let r of this.order)r.resize?.(e,t,n)}onSuspend(e){for(let t of this.order)t.onSuspend?.(e)}onResume(e,t){for(let n of this.order)n.onResume?.(e,t)}destroy(e){for(let t=this.order.length-1;t>=0;t--)this.order[t].destroy?.(e);this.order=[],this.registry=new g}},y={debug:0,info:1,warn:2,error:3},b=class{constructor(e,t={}){if(!e)throw Error(`a logger needs a category`);this.category=e,this.level=t.level??`debug`,this.sink=t.sink??x}setLevel(e){this.level=e}debug(e,t){this.write(`debug`,e,t)}info(e,t){this.write(`info`,e,t)}warn(e,t){this.write(`warn`,e,t)}error(e,t){this.write(`error`,e,t)}write(e,t,n){y[e]<y[this.level]||this.sink({level:e,category:this.category,message:t,data:n,time:Date.now()})}};function x(e){let t=`[${e.category}] ${e.message}`;e.level===`debug`?console.debug(t,e.data??``):e.level===`info`?console.info(t,e.data??``):e.level===`warn`?console.warn(t,e.data??``):console.error(t,e.data??``)}var S=class{constructor(){this.hooks=[]}on(e,t,n){this.hooks.push({event:e,handler:t,source:n})}off(e){this.hooks=this.hooks.filter(t=>t.handler!==e)}offSource(e){this.hooks=this.hooks.filter(t=>t.source!==e)}emit(e,...t){for(let n of[...this.hooks])n.event===e&&n.handler(...t)}get size(){return this.hooks.length}clear(){this.hooks=[]}},C=l({Generator:()=>M,MersenneTwister:()=>me,RandomStreams:()=>he,active:()=>ee,chance:()=>j,element:()=>ne,float:()=>O,int:()=>k,normalRange:()=>A,pop:()=>T,push:()=>w,range:()=>te,reset:()=>D,shuffle:()=>ae,weighted:()=>re,weightedKey:()=>ie,withSeed:()=>E});function ee(){return ce()}function w(e){let t=new M(e);return se.push(t),t}function T(){if(se.length===1)throw Error(`the base random generator cannot be popped`);se.pop()}function E(e,t){w(e);try{return t()}finally{T()}}function D(){se.length=0,se.push(new M)}function O(e,t,n){let r=n??ce();return e===void 0?r.float():t===void 0?r.float()*e:e+r.float()*(t-e)}function k(e,t,n){let r=n??ce();return t===void 0?r.int(e):e+r.int(t-e)}function te(e,t,n){return e+(n??ce()).int(t-e+1)}function A(e,t,n){let r=n??ce();return e+Math.floor((r.float()+r.float())*(t-e+1)/2)}function j(e,t){return(t??ce()).float()<e}function ne(e,t){return e.length>0?e[(t??ce()).int(e.length)]:null}function re(e,t){let n=0;for(let t of e)n+=t;if(n<=0)return null;let r=(t??ce()).float()*n;for(let t=0;t<e.length;t++)if(r-=e[t],r<0)return t;return e.length-1}function ie(e,t){let n=[...e.keys()],r=re(n.map(t=>e.get(t)??0),t);return r===null?null:n[r]}function ae(e,t){for(let n=e.length-1;n>0;n--){let r=(t??ce()).int(n+1);[e[n],e[r]]=[e[r],e[n]]}return e}function oe(e){let t=2166136261;for(let n=0;n<e.length;n++)t^=e.charCodeAt(n),t=Math.imul(t,16777619)>>>0;return t>>>0}var M,se,ce,le,ue,de,fe,pe,me,he,ge=s((()=>{M=class{constructor(e){this.s0=0,this.s1=0,this.s2=0,this.s3=0,this.seed=e===void 0?Math.random()*4294967296>>>0:e>>>0;let t=this.seed,n=()=>{t=t+2654435769>>>0;let e=t;return e=Math.imul(e^e>>>16,569420461),e=Math.imul(e^e>>>15,1935289751),(e^e>>>15)>>>0};this.s0=n(),this.s1=n(),this.s2=n(),this.s3=n(),(this.s0|this.s1|this.s2|this.s3)===0&&(this.s0=1)}nextUint32(){let e=Math.imul(this.rotl(Math.imul(this.s1,5),7),9)>>>0>>>0,t=this.s1<<9>>>0;return this.s2^=this.s0,this.s3^=this.s1,this.s1^=this.s2,this.s0^=this.s3,this.s2^=t,this.s3=this.rotl(this.s3,11),e}float(){return this.nextUint32()/4294967296}int(e){if(e<=0)return 0;let t=4294967296-4294967296%e,n=this.nextUint32();for(;n>=t;)n=this.nextUint32();return n%e}getState(){return[this.s0,this.s1,this.s2,this.s3]}setState(e){[this.s0,this.s1,this.s2,this.s3]=e}rotl(e,t){return(e<<t|e>>>32-t)>>>0}},se=[new M],ce=()=>se[se.length-1],le=624,ue=397,de=2567483615,fe=2147483648,pe=2147483647,me=class{constructor(e=5489){this.state=new Uint32Array(le),this.index=le,this.seedValue=5489,this.produced=0,this.seed(e)}seed(e){this.seedValue=e>>>0,this.state[0]=this.seedValue;for(let e=1;e<le;e++){let t=this.state[e-1]^this.state[e-1]>>>30;this.state[e]=Math.imul(1812433253,t)+e>>>0}this.index=le,this.produced=0}generate(){for(let e=0;e<le;e++){let t=this.state[e]&fe|this.state[(e+1)%le]&pe,n=this.state[(e+ue)%le]^t>>>1;t&1&&(n^=de),this.state[e]=n>>>0}this.index=0}nextUint32(){this.index>=le&&this.generate();let e=this.state[this.index++];return e^=e>>>11,e^=e<<7&2636928640,e^=e<<15&4022730752,e^=e>>>18,this.produced++,e>>>0}float(){return this.nextUint32()/4294967296}int(e){if(e<=0)return 0;let t=4294967296-4294967296%e,n=this.nextUint32();for(;n>=t;)n=this.nextUint32();return n%e}discard(e){for(let t=0;t<e;t++)this.nextUint32()}get discardCount(){return this.produced}getState(){return{seed:this.seedValue,state:[...this.state],index:this.index,discard:this.produced}}setState(e){this.seedValue=e.seed>>>0;for(let t=0;t<le;t++)this.state[t]=(e.state[t]??0)>>>0;this.index=e.index,this.produced=e.discard}},he=class{constructor(e=0){this.streams=new Map,this.baseSeed=e>>>0}stream(e){let t=this.streams.get(e);if(!t){let n=(this.baseSeed^oe(e))>>>0;t={seed:n,engine:new me(n)},this.streams.set(e,t)}return t.engine}seedOf(e){return this.streams.get(e)?.seed}reseed(e){this.baseSeed=e>>>0,this.streams.clear()}names(){return[...this.streams.keys()]}getState(){let e={};for(let[t,n]of this.streams)e[t]=n.engine.getState();return e}setState(e){for(let[t,n]of Object.entries(e)){let e=new me(n.seed);e.setState(n),this.streams.set(t,{seed:n.seed,engine:e})}}}})),_e=l({DEFAULT_BINDINGS:()=>Ae,actionsForKey:()=>Ne,attach:()=>mt,attachSwipe:()=>pt,bind:()=>je,bindAxis:()=>it,bindButton:()=>rt,bindTouch:()=>ct,detach:()=>ht,dispatchAction:()=>we,dispatchComposition:()=>Te,dispatchText:()=>Ce,endFrame:()=>Ge,exportBindings:()=>Ie,gamepadAxisCode:()=>nt,gamepadButtonCode:()=>tt,importBindings:()=>Le,isDown:()=>He,justPressed:()=>Ue,justReleased:()=>We,keysFor:()=>Fe,onAction:()=>F,onComposition:()=>I,onKey:()=>be,onText:()=>xe,onWheel:()=>Ee,pollGamepads:()=>at,pressTouch:()=>lt,releaseTouch:()=>ut,resetBindings:()=>Re,rumble:()=>ot,textFromKey:()=>Se,touchCode:()=>st,unbind:()=>Pe,wheelActionFor:()=>De}),N=new Map,P=new Set,ve=new Set,ye=new Set,F=new p(!0),be=new p(!0),xe=new p(!0),I=new p(!0);function Se(e){return e.isComposing||e.ctrlKey||e.metaKey||e.altKey?null:e.key.length===1?e.key:null}function Ce(e){e.length>0&&xe.dispatch(e)}function we(e){F.dispatch(e)}function Te(e){I.dispatch({phase:e.phase,text:e.text})}var Ee=new p(!0);function De(e){return e.ctrlKey||e.metaKey?`zoom`:e.shiftKey?`scrollHorizontal`:`scroll`}function Oe(e){let t=De(e);t===`zoom`&&e.preventDefault();let n=t===`scrollHorizontal`&&e.deltaX!==0?e.deltaX:e.deltaY;Ee.dispatch({action:t,delta:n})}var ke=!1,Ae={up:[`ArrowUp`,`KeyW`,`Numpad8`],down:[`ArrowDown`,`KeyS`,`Numpad2`],left:[`ArrowLeft`,`KeyA`,`Numpad4`],right:[`ArrowRight`,`KeyD`,`Numpad6`],upLeft:[`Numpad7`],upRight:[`Numpad9`],downLeft:[`Numpad1`],downRight:[`Numpad3`],wait:[`Numpad5`,`Period`],confirm:[`Enter`,`Space`,`NumpadEnter`],cancel:[`Escape`,`Backspace`],menu:[`KeyI`,`Tab`]};function je(e,t){N.set(e,new Set(t))}function Me(e,t){let n=N.get(e)??new Set;for(let e of t)n.add(e);N.set(e,n)}function Ne(e){return ze(e)}function Pe(e){N.delete(e)}function Fe(e){return[...N.get(e)??[]]}function Ie(){let e={};for(let[t,n]of N)e[t]=[...n];return e}function Le(e){N.clear();for(let[t,n]of Object.entries(e))je(t,n)}function Re(){Le(Ae)}Re();function ze(e){let t=[];for(let[n,r]of N)r.has(e)&&t.push(n);return t}function Be(e){if(!P.has(e)){P.add(e);for(let t of ze(e))ve.add(t),F.dispatch(t)}}function Ve(e){if(P.has(e)){P.delete(e);for(let t of ze(e))ye.add(t)}}function He(e){let t=N.get(e);if(!t)return!1;for(let e of t)if(P.has(e))return!0;return!1}function Ue(e){return ve.has(e)}function We(e){return ye.has(e)}function Ge(){ve.clear(),ye.clear()}function Ke(e){let t=Se(e);t!==null&&Ce(t)}function qe(e){e.repeat?(be.dispatch(e),Ke(e)):(Be(e.code),be.dispatch(e),Ke(e))}function Je(e){Ve(e.code)}function Ye(e){Te({phase:`start`,text:e.data??``})}function Xe(e){Te({phase:`update`,text:e.data??``})}function Ze(e){Te({phase:`end`,text:e.data??``})}function Qe(){P.clear(),ve.clear(),ye.clear()}var $e=.5,et=new Set;function tt(e,t){return`Gamepad${e}Button${t}`}function nt(e,t,n){return`Gamepad${e}Axis${t}${n>0?`+`:`-`}`}function rt(e,t,n){Me(e,n.map(e=>tt(t,e)))}function it(e,t,n,r){Me(e,[nt(t,n,r)])}function at(e=typeof navigator?.getGamepads==`function`?navigator.getGamepads():[]){et.clear();for(let t of e)if(t){for(let e=0;e<t.buttons.length;e++)t.buttons[e].pressed&&et.add(tt(t.index,e));for(let e=0;e<t.axes.length;e++){let n=t.axes[e];n>=$e?et.add(nt(t.index,e,1)):n<=-.5&&et.add(nt(t.index,e,-1))}}for(let e of et)Be(e);for(let e of P)!et.has(e)&&e.startsWith(`Gamepad`)&&Ve(e)}function ot(e,t,n=typeof navigator?.getGamepads==`function`?navigator.getGamepads():[]){(n[e]?.vibrationActuator)?.playEffect(`dual-rumble`,{duration:t.duration,weakMagnitude:t.weakMagnitude??1,strongMagnitude:t.strongMagnitude??1})}function st(e){return`Touch:${e}`}function ct(e,t=e){Me(e,[st(t)])}function lt(e){Be(st(e))}function ut(e){Ve(st(e))}var dt={up:`up`,down:`down`,left:`left`,right:`right`,upLeft:`upLeft`,upRight:`upRight`,downLeft:`downLeft`,downRight:`downRight`},ft=[`right`,`downRight`,`down`,`downLeft`,`left`,`upLeft`,`up`,`upRight`];function pt(e,t={}){let n={...dt,...t.actions},r=t.minDistance??24;for(let e of[...ft,`tap`]){let t=n[e];t&&ct(t,`swipe:${e}`)}let i=!1,a=-1,o=0,s=0;function c(e){lt(`swipe:${e}`),ut(`swipe:${e}`)}function l(e){i=!0,a=e.pointerId,o=e.clientX,s=e.clientY}function u(e){if(!i||e.pointerId!==a)return;i=!1;let t=e.clientX-o,l=e.clientY-s;if(Math.hypot(t,l)<r){n.tap&&c(`tap`);return}let u=ft[(Math.round(Math.atan2(l,t)/(Math.PI/4))%8+8)%8];n[u]&&c(u)}return e.addEventListener(`pointerdown`,l),e.addEventListener(`pointerup`,u),e.addEventListener(`pointercancel`,u),()=>{e.removeEventListener(`pointerdown`,l),e.removeEventListener(`pointerup`,u),e.removeEventListener(`pointercancel`,u)}}function mt(e=window){ke||(ke=!0,e.addEventListener(`keydown`,qe),e.addEventListener(`keyup`,Je),e.addEventListener(`compositionstart`,Ye),e.addEventListener(`compositionupdate`,Xe),e.addEventListener(`compositionend`,Ze),window.addEventListener(`blur`,Qe),e.addEventListener(`wheel`,Oe,{passive:!1}))}function ht(e=window){ke&&(ke=!1,e.removeEventListener(`keydown`,qe),e.removeEventListener(`keyup`,Je),e.removeEventListener(`compositionstart`,Ye),e.removeEventListener(`compositionupdate`,Xe),e.removeEventListener(`compositionend`,Ze),e.removeEventListener(`wheel`,Oe),window.removeEventListener(`blur`,Qe),Qe())}var gt=class{constructor(e,t={}){if(!e)throw Error(`a player needs a non-empty id`);this.id=e,this.padIndex=t.padIndex}scoped(e){return`${this.id}:${e}`}bind(e,t){je(this.scoped(e),t)}bindButton(e,t){if(this.padIndex===void 0)throw Error(`player "${this.id}" has no padIndex to bind a gamepad button to`);rt(this.scoped(e),this.padIndex,t)}bindAxis(e,t,n){if(this.padIndex===void 0)throw Error(`player "${this.id}" has no padIndex to bind a gamepad axis to`);it(this.scoped(e),this.padIndex,t,n)}bindTouch(e,t=e){ct(this.scoped(e),`${this.id}:${t}`)}pressTouch(e){lt(`${this.id}:${e}`)}releaseTouch(e){ut(`${this.id}:${e}`)}isDown(e){return He(this.scoped(e))}justPressed(e){return Ue(this.scoped(e))}justReleased(e){return We(this.scoped(e))}keysFor(e){return Fe(this.scoped(e))}},_t=25214903917n,vt=11n,yt=(1n<<48n)-1n,bt=class{constructor(e=0,t={}){this.state=0n,this.onDraw=t.onDraw,this.setSeed(e)}setSeed(e){this.state=(BigInt.asIntN(64,BigInt(e))^_t)&yt}next(e){if(!Number.isInteger(e)||e<1||e>32)throw Error(`next needs 1 to 32 bits, got ${e}`);this.state=this.state*_t+vt&yt;let t=Number(this.state>>BigInt(48-e)),n=e===32?t|0:t;return this.onDraw?.({bits:e,value:n}),n}nextInt(e){if(e===void 0)return this.next(32);if(!Number.isInteger(e)||e<=0||e>2147483647)throw Error(`nextInt needs a bound from 1 to 2147483647, got ${e}`);if((e&-e)===e)return Math.floor(e*this.next(31)/2147483648);let t,n;do t=this.next(31),n=t%e;while(t-n+(e-1)>2147483647);return n}nextLong(){let e=BigInt(this.next(32)),t=BigInt(this.next(32));return BigInt.asIntN(64,(e<<32n)+t)}nextDouble(){return(this.next(26)*134217728+this.next(27))/2**53}nextFloat(){return this.next(24)/16777216}nextBoolean(){return this.next(1)!==0}};ge();function xt(e,t){if(!t)throw Error(`scramble needs a non-empty key`);let n=new TextEncoder().encode(e),r=new TextEncoder().encode(t),i=new Uint8Array(n.length);for(let e=0;e<n.length;e++)i[e]=n[e]^r[e%r.length];return Ct(i)}function St(e,t){if(!t)throw Error(`scramble needs a non-empty key`);let n=wt(e),r=new TextEncoder().encode(t),i=new Uint8Array(n.length);for(let e=0;e<n.length;e++)i[e]=n[e]^r[e%r.length];return new TextDecoder(`utf-8`,{fatal:!0}).decode(i)}function Ct(e){let t=``;for(let n of e)t+=String.fromCharCode(n);return btoa(t)}function wt(e){let t=atob(e),n=new Uint8Array(t.length);for(let e=0;e<t.length;e++)n[e]=t.charCodeAt(e);return n}var Tt=10485760;function Et(e,t={}){let n=t.maxBytes??Tt,r=typeof e==`string`?new TextEncoder().encode(e).length:e.length;if(r>n)throw Error(`inbound data exceeds the ${n}-byte limit (${r} bytes)`)}function Dt(e){for(let t=0;t<e.length;t++){let n=e.charCodeAt(t);if(n!==9&&n!==10&&n!==13&&(n<=31||n===127))throw Error(`inbound data contains a control character (0x${n.toString(16).padStart(2,`0`)}) at index ${t}`)}}function Ot(e,t={}){return Et(e,t),Dt(e),e}var kt=new Set([`__proto__`,`constructor`,`prototype`]);function At(e,t=`$`){if(Array.isArray(e))e.forEach((e,n)=>At(e,`${t}[${n}]`));else if(typeof e==`object`&&e)for(let n of Object.keys(e)){if(kt.has(n))throw Error(`${t} contains a forbidden key "${n}"`);At(e[n],`${t}.${n}`)}}function jt(e,t,n=`$`){if(e===void 0){if(t.optional)return;throw Error(`${n} is required`)}switch(t.type){case`string`:if(typeof e!=`string`)throw Error(`${n} must be a string`);return;case`number`:if(typeof e!=`number`||!Number.isFinite(e))throw Error(`${n} must be a finite number`);if(t.min!==void 0&&e<t.min)throw Error(`${n} must be >= ${t.min}`);if(t.max!==void 0&&e>t.max)throw Error(`${n} must be <= ${t.max}`);return;case`boolean`:if(typeof e!=`boolean`)throw Error(`${n} must be a boolean`);return;case`array`:if(!Array.isArray(e))throw Error(`${n} must be an array`);if(t.maxLength!==void 0&&e.length>t.maxLength)throw Error(`${n} exceeds its maximum length of ${t.maxLength}`);e.forEach((e,r)=>jt(e,t.items,`${n}[${r}]`));return;case`object`:if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`${n} must be an object`);for(let t of Object.keys(e))if(kt.has(t))throw Error(`${n} contains a forbidden key "${t}"`);for(let[r,i]of Object.entries(t.fields))jt(e[r],i,`${n}.${r}`);return}}function Mt(e,t={}){let n=t.label??`inbound data`;Ot(e,t);try{return JSON.parse(e,(e,t)=>{if(kt.has(e))throw new Nt(e);return t})}catch(e){throw e instanceof Nt?Error(`${n} contains a forbidden key "${e.key}"`):Error(`${n} is not valid JSON (${e instanceof Error?e.message:String(e)})`)}}var Nt=class{constructor(e){this.key=e}};function Pt(e,t){let n=Mt(e,{label:t}),r=n?.meta;if(typeof n!=`object`||!n||Array.isArray(n)||!(`state`in n))throw Error(`${t} is not save data (expected { meta, state })`);if(typeof r!=`object`||!r||!Number.isSafeInteger(r.version)||r.version<0)throw Error(`${t} has no valid meta.version`);if(typeof r.savedAt!=`number`||!Number.isFinite(r.savedAt))throw Error(`${t} has no valid meta.savedAt`);return n}var Ft=class{constructor(){this.data=new Map}read(e){return this.data.get(e)??null}write(e,t){this.data.set(e,t)}remove(e){this.data.delete(e)}keys(){return[...this.data.keys()]}};function It(){return typeof localStorage>`u`?new Ft:{read:e=>localStorage.getItem(e),write:(e,t)=>localStorage.setItem(e,t),remove:e=>localStorage.removeItem(e),keys:()=>Object.keys(localStorage)}}var Lt=class{constructor(e){this.namespace=e.namespace,this.version=e.version,this.migrations=e.migrations??{},this.storage=e.storage??It()}key(e){return`mwg-save:${this.namespace}:${e}`}save(e,t,n){let r={meta:{version:this.version,savedAt:Date.now(),preview:n},state:t};this.storage.write(this.key(e),JSON.stringify(r))}load(e){let t=this.storage.read(this.key(e));if(!t)return null;try{let n=Pt(t,`save slot "${e}"`),r=n.state;for(let e=n.meta.version;e<this.version;e++)r=(this.migrations[e]??(e=>e))(r);return{meta:{...n.meta,version:this.version},state:r}}catch{return null}}importExternal(e,t,n,r){Et(t);let i=n(t);for(let e=0;e<this.version;e++)i=(this.migrations[e]??(e=>e))(i);let a={meta:{version:this.version,savedAt:Date.now(),preview:r},state:i};this.storage.write(this.key(e),JSON.stringify(a))}delete(e){this.storage.remove(this.key(e))}exportSlot(e,t){let n=this.storage.read(this.key(e));return n===null?null:t?xt(n,t):n}importSlot(e,t,n){Et(t);let r=Pt(n?St(t,n):t,`SaveSystem.importSlot: payload`),i=r.state;for(let e=r.meta.version;e<this.version;e++)i=(this.migrations[e]??(e=>e))(i);let a={meta:{...r.meta,version:this.version},state:i};this.storage.write(this.key(e),JSON.stringify(a))}list(){let e=`mwg-save:${this.namespace}:`,t=[];for(let n of this.storage.keys()){if(!n.startsWith(e))continue;let r=this.storage.read(n);if(r)try{t.push({slot:n.slice(e.length),meta:Pt(r,n).meta})}catch{}}return t}};function Rt(e,t=`value`){return zt(e,t,new Set)}function zt(e,t,n){if(typeof e==`function`||typeof e==`symbol`)return t;if(typeof e!=`object`||!e||n.has(e)||(n.add(e),Bt(e)))return null;let r=[];if(e instanceof Map)for(let[n,i]of e)r.push([`${t}.get(${JSON.stringify(n)??String(n)})`,i]);else if(e instanceof Set)for(let n of e)r.push([`${t}.item`,n]);else if(Array.isArray(e))e.forEach((e,n)=>r.push([`${t}[${n}]`,e]));else for(let[n,i]of Object.entries(e))r.push([`${t}.${n}`,i]);for(let[e,t]of r){let r=zt(t,e,n);if(r)return r}return t}function Bt(e){try{return structuredClone(e),!0}catch{return!1}}function Vt(e,t){try{return structuredClone(e)}catch(n){let r=Rt(e,t)??t;throw TypeError(`${r} cannot be structured-cloned (${n instanceof Error?n.message:String(n)}). Journals, snapshots and undo checkpoints copy their contents with structuredClone, so they must hold plain data: refer to a live object (an actor, anything carrying callbacks such as a ReactionTable) by its id and resolve it inside the rule.`,{cause:n})}}var Ht=class{constructor(e,t={}){this._state=Vt(e,`initial state`),this.version=t.version??1,this.migrations=t.migrations??{},this.extensions=t.extensions??new Ut}get state(){return structuredClone(this._state)}set(e){this._state=Vt(e,`state`)}update(e){let t=e(this.state);return this.set(t),this.state}snapshot(){let e=this.extensions.snapshot();return{version:this.version,state:this.state,extensions:e.extensions,extensionVersions:e.versions}}restore(e,t={}){if(!Number.isSafeInteger(e.version)||e.version<1)throw Error(`canonical state snapshot version must be a positive integer`);if(e.version>this.version)throw Error(`canonical state snapshot version ${e.version} is newer than ${this.version}`);let n=this.snapshot();try{let n=Vt(e.state,`snapshot.state`);for(let t=e.version;t<this.version;t++){let e=this.migrations[t+1];if(!e)throw Error(`missing canonical state migration to version ${t+1}`);n=e(n)}return this._state=Vt(n,`migrated state`),this.extensions.restore({extensions:e.extensions,versions:e.extensionVersions},t)}catch(e){throw this._state=structuredClone(n.state),this.extensions.restore({extensions:n.extensions,versions:n.extensionVersions},{missing:`keep`}),e}}transaction(e){let t=this.snapshot();try{return e(this)}catch(e){throw this.restore(t),e}}},Ut=class{constructor(){this.extensions=new Map}register(e){if(!/^[A-Za-z_][A-Za-z0-9_.-]*$/.test(e.id))throw Error(`invalid state extension id: ${e.id}`);if(this.extensions.has(e.id))throw Error(`state extension already registered: ${e.id}`);return this.extensions.set(e.id,{id:e.id,capture:()=>e.capture(),restore:t=>e.restore(t),version:e.version??1,migrations:e.migrations,reset:e.reset,remove:e.remove}),()=>this.extensions.delete(e.id)}snapshot(){let e={};for(let[t,n]of this.extensions)e[t]=Vt(n.capture(),`extension ${t}`);let t={};for(let[e,n]of this.extensions)t[e]=n.version??1;return{extensions:e,versions:t}}restore(e,t={}){let n=this.snapshot(),r=[];try{for(let[n,i]of this.extensions){if(!(n in e.extensions)){t.missing===`reset`?(i.reset&&i.reset(),r.push({extension:n,from:0,to:i.version??1,status:`reset`})):t.missing===`remove`&&(i.remove&&i.remove(),r.push({extension:n,from:0,to:i.version??1,status:`removed`}));continue}let a=e.versions?.[n]??1,o=i.version??1,s=Vt(e.extensions[n],`snapshot.extensions.${n}`);for(let e=a;e<o;e++){let t=i.migrations?.[e+1];if(!t)throw Error(`missing state migration for ${n} to version ${e+1}`);s=t(s)}i.restore(Vt(s,`migrated extension ${n}`)),r.push({extension:n,from:a,to:o,status:a===o?`unchanged`:`migrated`})}}catch(e){for(let[e,t]of this.extensions)e in n.extensions&&t.restore(structuredClone(n.extensions[e]));throw e}for(let e of r)t.onDiagnostic?.(e);return r}transaction(e){let t=this.snapshot();try{return e()}catch(e){throw this.restore(t),e}}},Wt=new Set([`localhost`,`127.0.0.1`,`[::1]`]);function Gt(e,t,n=!1){if(n||!/^[a-z][a-z0-9+.-]*:/i.test(e))return;let r=new URL(e);if(r.protocol!==`https:`&&r.protocol!==`wss:`&&(r.protocol!==`http:`&&r.protocol!==`ws:`||!Wt.has(r.hostname)))throw Error(`${t} url must use https: or wss: (got ${r.protocol}//${r.host}); pass allowInsecure: true to accept it`)}var Kt=class{constructor(e,t){if(!e.endpoint)throw Error(`${t} endpoint is required`);if(Gt(e.endpoint,`${t} endpoint`,e.allowInsecure),this.endpoint=e.endpoint,this.label=t,this.maxResponseBytes=e.maxResponseBytes??10485760,this.timeoutMs=e.timeoutMs??1e4,!(this.timeoutMs>0))throw Error(`${t} timeout must be positive`);if(this.fetchFn=e.fetch??globalThis.fetch,!this.fetchFn)throw Error(`fetch is unavailable; provide ${t}Options.fetch`)}async withTimeout(e){let t=new AbortController,n=setTimeout(()=>t.abort(),this.timeoutMs);try{return await e(t.signal)}finally{clearTimeout(n)}}async readText(e){let t=this.maxResponseBytes,n=Number(e.headers?.get?.(`content-length`));if(n>t)throw Error(`${this.label} response exceeds the ${t}-byte limit (${n} bytes)`);if(!e.body){let n=await e.text();return Et(n,{maxBytes:t}),n}let r=e.body.getReader(),i=[],a=0;for(;;){let{done:e,value:n}=await r.read();if(e)break;if(a+=n.byteLength,a>t)throw await r.cancel(),Error(`${this.label} response exceeds the ${t}-byte limit`);i.push(n)}let o=new Uint8Array(a),s=0;for(let e of i)o.set(e,s),s+=e.byteLength;return new TextDecoder().decode(o)}async readJson(e,t){return Mt(await this.readText(e),{maxBytes:this.maxResponseBytes,label:`${this.label} ${t}`})}},qt=class extends Kt{constructor(e){super(e,`save sync`)}async upload(e,t){if(!e)throw Error(`save sync slot name is required`);if(!t)throw Error(`save sync payload cannot be empty`);return this.withTimeout(async n=>{let r=await this.fetchFn(this.slotUrl(e),{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({payload:t}),signal:n});if(!r.ok)throw Error(`save upload failed with HTTP ${r.status}`);return{ok:!0,status:r.status}})}async download(e){if(!e)throw Error(`save sync slot name is required`);return this.withTimeout(async t=>{let n=await this.fetchFn(this.slotUrl(e),{signal:t});if(!n.ok)throw Error(`save download failed with HTTP ${n.status}`);let r=await this.readJson(n,`download`);if(typeof r?.payload!=`string`)throw Error(`save download response missing a string payload`);return r.payload})}async list(){return this.withTimeout(async e=>{let t=await this.fetchFn(this.endpoint,{signal:e});if(!t.ok)throw Error(`save slot list failed with HTTP ${t.status}`);let n=await this.readJson(t,`slot list`);if(!Array.isArray(n?.slots)||!n.slots.every(e=>typeof e==`string`))throw Error(`save slot list response missing a string array of slots`);return n.slots})}slotUrl(e){let t=this.endpoint.includes(`?`)?`&`:`?`;return`${this.endpoint}${t}slot=${encodeURIComponent(e)}`}},Jt=1,Yt=class{constructor(e){if(this.onWelcome=new p,this.onTick=new p,this.onReject=new p,this.onDesync=new p,this.onClose=new p,this.onProtocolError=new p,this.socket=null,this._id=null,!e.url)throw Error(`lockstep client url is required`);Gt(e.url,`lockstep client`,e.allowInsecure),this.url=e.url,this.createSocket=e.create??(e=>new WebSocket(e)),this.validateInput=e.validateInput,this.maxMessageBytes=e.maxMessageBytes??1048576}get id(){return this._id}get connected(){return this.socket!==null&&this.socket.readyState===Jt}connect(){if(this.socket)throw Error(`lockstep client is already connected`);let e=this.createSocket(this.url);e.onmessage=e=>this.handleMessage(e.data),e.onclose=()=>{this.socket=null,this._id=null,this.onClose.dispatch()},this.socket=e}submitInput(e,t){if(!this.connected)return;let n=this.validateInput?.(e);n===!1||typeof n==`string`?this.onReject.dispatch({reason:n===!1?`input rejected by client validation`:n}):this.socket?.send(JSON.stringify({type:`input`,payload:e,...t===void 0?{}:{checksum:t}}))}close(){this.socket?.close()}handleMessage(e){let t;try{if(typeof e!=`string`)throw Error(`lockstep message is not text`);t=Zt(Mt(e,{maxBytes:this.maxMessageBytes,label:`lockstep message`}))}catch(e){this.onProtocolError.dispatch({reason:e instanceof Error?e.message:String(e)});return}if(t.type===`welcome`)this._id=t.id,this.onWelcome.dispatch({id:t.id,...t.seed===void 0?{}:{seed:t.seed},...t.initialState===void 0?{}:{initialState:t.initialState}});else if(t.type===`tick`){let e={tick:t.tick,inputs:t.inputs,...t.checksums===void 0?{}:{checksums:t.checksums}};if(this.onTick.dispatch(e),t.checksums){let e=Object.values(t.checksums);e.some(t=>t!==e[0])&&this.onDesync.dispatch({tick:t.tick,checksums:t.checksums})}}else this.onReject.dispatch({reason:t.reason??`input rejected by server`})}},Xt=e=>typeof e==`object`&&!!e&&!Array.isArray(e);function Zt(e){if(!Xt(e)||typeof e.type!=`string`)throw Error(`lockstep message has no type`);switch(e.type){case`welcome`:if(typeof e.id!=`string`||e.id===``)throw Error(`welcome needs a string id`);if(e.seed!==void 0&&!Number.isSafeInteger(e.seed))throw Error(`welcome seed must be an integer`);return e;case`tick`:if(!Number.isSafeInteger(e.tick)||e.tick<0)throw Error(`tick needs a non-negative integer tick`);if(!Xt(e.inputs))throw Error(`tick needs an inputs object`);if(e.checksums!==void 0&&(!Xt(e.checksums)||Object.values(e.checksums).some(e=>typeof e!=`number`)))throw Error(`tick checksums must map ids to numbers`);return e;case`rejected`:if(e.reason!==void 0&&typeof e.reason!=`string`)throw Error(`rejected reason must be a string`);return e;default:throw Error(`unknown lockstep message type "${e.type}"`)}}var Qt=class{constructor(e,t){this.storage=e,this.key=t}read(e){let t=this.storage.read(this.key);return t?Mt(t,{label:`stored value "${this.key}"`}):e}write(e){this.storage.write(this.key,JSON.stringify(e))}remove(){this.storage.remove(this.key)}},$t=class{constructor(e){this.store=new Qt(e.storage??It(),`mwg-runs:${e.namespace}`),this.limit=e.limit}readAll(){return this.store.read([])}record(e){let t=this.readAll(),n={id:`${Date.now()}-${t.length}`,endedAt:Date.now(),summary:e};return t.push(n),this.limit!==void 0&&t.length>this.limit&&t.splice(0,t.length-this.limit),this.store.write(t),n}all(){return this.readAll()}ranked(e,t=`desc`){let n=this.readAll(),r=t===`desc`?-1:1;return n.sort((t,n)=>r*(e(t.summary)-e(n.summary)))}clear(){this.store.remove()}},en=class{constructor(e){this.store=new Qt(e.storage??It(),`mwg-stats:${e.namespace}`),this.initial=e.initial,this.combine=e.combine}get(){return this.store.read(this.initial)}record(e){let t=this.combine(this.get(),e);return this.store.write(t),t}reset(){this.store.remove()}},tn=class extends Kt{constructor(e){super(e,`telemetry`),this.consented=!1,this.maxStringLength=e.maxStringLength??256,this.maxProperties=e.maxProperties??32,this.allowed=e.allowedProperties?new Set(e.allowedProperties):null}get hasConsent(){return this.consented}setConsent(e){this.consented=e}async send(e){if(!e.name)throw Error(`telemetry event needs a name`);return this.consented?this.withTimeout(async t=>{let n=await this.fetchFn(this.endpoint,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify(this.bounded(e)),signal:t});if(!n.ok)throw Error(`telemetry request failed with HTTP ${n.status}`);return{ok:!0,status:n.status}}):null}bounded(e){let t=e=>e.length>this.maxStringLength?e.slice(0,this.maxStringLength):e;if(!e.properties)return{name:t(e.name)};let n={},r=0;for(let[i,a]of Object.entries(e.properties))if(!this.allowed||this.allowed.has(i)){if(r++>=this.maxProperties)break;n[t(i)]=typeof a==`string`?t(a):a}return{name:t(e.name),properties:n}}},nn=class extends Kt{constructor(e){super(e,`news`)}async fetchItems(){return this.withTimeout(async e=>{let t=await this.fetchFn(this.endpoint,{signal:e});if(!t.ok)throw Error(`news request failed with HTTP ${t.status}`);let n=await this.readJson(t,`response`);if(!Array.isArray(n))throw Error(`news response was not an array`);return n.map((e,t)=>rn(e,t))})}};function rn(e,t){if(typeof e!=`object`||!e)throw Error(`news item ${t} was not an object`);let n=e;if(typeof n.id!=`string`||!n.id)throw Error(`news item ${t} is missing a string id`);if(typeof n.title!=`string`)throw Error(`news item ${t} is missing a string title`);if(typeof n.body!=`string`)throw Error(`news item ${t} is missing a string body`);return{id:n.id,title:n.title,body:n.body,publishedAt:typeof n.publishedAt==`number`?n.publishedAt:void 0}}var an=class{constructor(e){this.store=new Qt(e.storage??It(),`mwg-news-seen:${e.namespace}`)}readSeen(){return new Set(this.store.read([]))}isSeen(e){return this.readSeen().has(e)}markSeen(e){let t=this.readSeen();t.add(e),this.store.write([...t])}unseen(e){let t=this.readSeen();return e.filter(e=>!t.has(e.id))}},on=class{constructor(e,t={}){if(!e)throw Error(`a collection needs a name`);this.prefix=`mwg-db:${t.namespace??`default`}:${e}:`,this.storage=t.storage??It()}get size(){return this.keys().length}all(){return this.keys().map(e=>this.read(e))}get(e){let t=this.storage.read(this.prefix+e);return t===null?void 0:this.parse(t,this.prefix+e)}put(e){if(typeof e!=`object`||!e||Array.isArray(e)||typeof e.id!=`string`||e.id===``)throw Error(`a record must be an object with a string id`);this.storage.write(this.prefix+e.id,JSON.stringify(e))}remove(e){this.storage.remove(this.prefix+e)}where(e){return this.all().filter(e)}clear(){for(let e of this.keys())this.storage.remove(e)}keys(){return this.storage.keys().filter(e=>e.startsWith(this.prefix))}read(e){return this.parse(this.storage.read(e),e)}parse(e,t){return Mt(e,{label:`record "${t}"`})}},sn=class{constructor(e,t,n){if(this.recorded=[],this.fromFrame=n?.fromFrame??0,!Number.isInteger(this.fromFrame)||this.fromFrame<0)throw RangeError(`a resumed recorder needs a non-negative fromFrame`);this.frameStamp=this.fromFrame,this.actions=e,this.frames=t,this.onAction=e=>{this.recorded.push({frame:this.frameStamp,action:e})},this.onFrame=()=>{this.frameStamp++},e.add(this.onAction),t.add(this.onFrame)}get events(){return this.recorded}get frame(){return this.frameStamp}toJSON(){return this.recorded.map(e=>({frame:e.frame,action:e.action}))}stop(){this.actions.remove(this.onAction),this.frames.remove(this.onFrame)}},cn=class{constructor(e,t,n,r){if(this.events=e,this.dispatch=t,this.frames=n,this.fromFrame=r?.fromFrame??0,!Number.isInteger(this.fromFrame)||this.fromFrame<0)throw RangeError(`a resumed player needs a non-negative fromFrame`);this.index=this.events.findIndex(e=>e.frame>=this.fromFrame),this.index<0&&(this.index=this.events.length),this.frame=this.fromFrame,this.onFrame=()=>this.pump(),n.add(this.onFrame)}get done(){return this.index>=this.events.length}stop(){this.frames.remove(this.onFrame)}pump(){for(;this.index<this.events.length&&this.events[this.index].frame<=this.frame;)this.dispatch(this.events[this.index].action),this.index++;this.frame++}};function ln(e){return JSON.stringify(e)}function un(e){return dn(JSON.parse(e))}function dn(e){if(!Array.isArray(e))throw Error(`a replay must be an array of {frame, action}`);return e.map(e=>{if(typeof e!=`object`||!e||!Number.isInteger(e.frame)||e.frame<0||typeof e.action!=`string`)throw Error(`a replay entry must be {frame: a non-negative integer, action: a string}`);return{frame:e.frame,action:e.action}})}function fn(e,t){if(typeof t!=`string`||!t.length)throw TypeError(`a replay file needs a non-empty ${e}`);return t}function pn(e){let t=e;if(!Number.isInteger(t)||t<0||t>4294967295)throw RangeError(`a replay seed must be uint32`);return t}function mn(e,t){let n={format:`mwg-replay`,version:1,framework:fn(`framework`,t.framework),game:fn(`game`,t.game),events:dn(e)};if(t.seed!==void 0&&(n.seed=pn(t.seed)),t.recordedAt!==void 0){if(typeof t.recordedAt!=`number`||!Number.isFinite(t.recordedAt))throw TypeError(`recordedAt must be a finite number`);n.recordedAt=t.recordedAt}return JSON.stringify(n)}function hn(e,t){let n=Mt(e,{label:`replay file`});if(typeof n!=`object`||!n||Array.isArray(n))throw Error(`a replay file must be a JSON object`);if(n.format!==`mwg-replay`)throw Error(`not a replay file: expected format "mwg-replay", found ${JSON.stringify(n.format)}`);if(n.version!==1)throw Error(`replay file version ${JSON.stringify(n.version)} is not supported (this build reads 1)`);if(n.game!==t.game)throw Error(`a replay of ${JSON.stringify(n.game)} cannot be imported into ${JSON.stringify(t.game)}`);if(n.framework!==t.framework)throw Error(`a replay recorded against mwg ${JSON.stringify(n.framework)} cannot be imported into ${JSON.stringify(t.framework)}`);let r=dn(n.events);return{format:`mwg-replay`,version:1,framework:n.framework,game:n.game,...n.seed===void 0?{}:{seed:pn(n.seed)},...n.recordedAt===void 0?{}:{recordedAt:n.recordedAt},events:r}}var gn=class{constructor(e){if(typeof e.namespace!=`string`||!e.namespace.length)throw TypeError(`a last-run store needs a non-empty namespace`);let t=e.maxEvents??1e4;if(!Number.isInteger(t)||t<1||t>1e6)throw RangeError(`last-run maxEvents must be in 1..1000000`);this.maxEvents=t,this.store=new Qt(e.storage??It(),`mwg-last-run:${e.namespace}`)}keep(e,t){let n=dn(e);n.length>this.maxEvents&&(n=n.slice(n.length-this.maxEvents));let r=t===void 0?{version:1,events:n}:{version:1,seed:pn(t),events:n};Et(JSON.stringify(r)),this.store.write(r)}load(){let e=this.store.read(null);if(e===null)return null;if(e?.version!==1)throw Error(`the kept last-run recording is not version 1; clear() it and record again`);return{version:1,...e.seed===void 0?{}:{seed:pn(e.seed)},events:dn(e.events)}}clear(){this.store.remove()}};function _n(e,t){return{version:1,frame:e.frame,events:e.toJSON(),state:t}}function vn(e,t,n){if(typeof e!=`object`||!e||e.version!==1)throw Error(`a run checkpoint must be { version: 1, frame, events, state }`);return new cn(dn(e.events),t,n,{fromFrame:e.frame})}function yn(e,t){if(typeof document>`u`)throw Error(`downloadReplayFile needs a browser document`);let n=document.createElement(`a`);n.href=URL.createObjectURL(new Blob([e],{type:`application/json`})),n.download=t,document.body.appendChild(n),n.click(),n.remove(),setTimeout(()=>URL.revokeObjectURL(n.href),0)}async function bn(e){return await e.text()}async function xn(e=`.json`){if(typeof document>`u`)throw Error(`pickReplayFile needs a browser document`);return await new Promise(t=>{let n=document.createElement(`input`);n.type=`file`,n.accept=e,n.style.display=`none`;let r=!1,i=e=>{r||(r=!0,n.remove(),t(e))};n.addEventListener(`change`,()=>i(n.files?.[0]??null)),n.addEventListener(`cancel`,()=>i(null)),document.body.appendChild(n),n.click()})}var Sn=class e{constructor(){this.entries=[],this.nextSequence=0}append(e,t=[]){let n={sequence:this.nextSequence,action:Vt(e,`action`),events:Vt([...t],`events`)};return this.nextSequence++,this.entries.push(n),structuredClone(n)}get size(){return this.entries.length}get all(){return structuredClone(this.entries)}mark(){return this.nextSequence}since(e){return structuredClone(this.entries.filter(t=>t.sequence>=e))}truncate(e){this.entries=this.entries.filter(t=>t.sequence<e),this.nextSequence=e}toJSON(){return this.all}replace(t){let n=e.fromJSON(t);this.entries=n.entries,this.nextSequence=n.nextSequence}static fromJSON(t){let n=new e,r=0;for(let e of t){if(!Number.isSafeInteger(e.sequence)||e.sequence!==r)throw Error(`action journal sequences must be contiguous from zero`);n.entries.push(structuredClone({sequence:e.sequence,action:e.action,events:[...e.events]})),r++}return n.nextSequence=r,n}};function Cn(e){if(e.criteria)return e.criteria;if(e.counter===void 0||e.target===void 0)throw Error(`achievement "${e.id}" needs either counter/target or criteria`);return[{counter:e.counter,target:e.target}]}var wn=class e{constructor(){this.definitions=new Map,this.counts=new Map,this.fresh=[]}define(e){this.definitions.set(e.id,e)}count(e){return this.counts.get(e)??0}increment(e,t=1){let n=this.count(e);this.counts.set(e,n+t);let r=[];for(let t of this.definitions.values()){let i=Cn(t);i.some(t=>t.counter===e)&&(i.every(t=>t.counter===e?n>=t.target:this.count(t.counter)>=t.target)||i.every(e=>this.count(e.counter)>=e.target)&&(r.push(t.id),this.fresh.push(t.id)))}return r}unlocked(e){let t=this.definitions.get(e);if(!t)throw Error(`no such achievement: "${e}"`);return Cn(t).every(e=>this.count(e.counter)>=e.target)}progress(e){let t=this.definitions.get(e);if(!t)throw Error(`no such achievement: "${e}"`);let n=Cn(t);if(n.length!==1)throw Error(`achievement "${e}" has several criteria - use subProgress`);return{count:this.count(n[0].counter),target:n[0].target}}subProgress(e){let t=this.definitions.get(e);if(!t)throw Error(`no such achievement: "${e}"`);return Cn(t).map(e=>{let t=this.count(e.counter);return{counter:e.counter,count:t,target:e.target,met:t>=e.target}})}drainNew(){let e=this.fresh;return this.fresh=[],e}toJSON(){return{counts:[...this.counts]}}static fromJSON(t,n){let r=new e;for(let e of t)r.define(e);for(let[e,t]of n.counts)r.counts.set(e,t);return r}},Tn=class e{constructor(e=[]){this.active=new Set,this.spent=new Set,this.rules=[...e]}add(e){this.rules.push(e)}remove(e){this.rules=this.rules.filter(t=>t.id!==e),this.active.delete(e)}check(e){let t=[];for(let n of this.rules){if(n.once&&this.spent.has(n.id))continue;let r=n.when(e);r&&!this.active.has(n.id)&&(n.action(e),t.push(n.id),n.once&&this.spent.add(n.id)),r?this.active.add(n.id):this.active.delete(n.id)}return t}isActive(e){return this.active.has(e)}reset(){this.active.clear(),this.spent.clear()}toJSON(){return{active:[...this.active],spent:[...this.spent]}}static fromJSON(t,n){let r=new e(t);for(let e of n.active)r.active.add(e);for(let e of n.spent)r.spent.add(e);return r}},En=class{constructor(e={}){let t=e.storage??It(),n=`mwg-session:${e.namespace??`default`}`,r=Number(t.read(n)??`0`);this.launches=(Number.isFinite(r)?r:0)+1,t.write(n,String(this.launches))}};function Dn(e,t,n){return e<t?t:e>n?n:e}function On(){return{musicVolume:1,sfxVolume:1,muted:!1,zoom:1,bindings:Pn(Ae),custom:{}}}function kn(e){return e.muted?0:e.musicVolume}function An(e){return e.muted?0:e.sfxVolume}var jn=class{constructor(e={}){this.storage=e.storage??It(),this.key=`mwg-settings:${e.namespace??`default`}`,this.value=zn(this.storage,this.key),Le(this.value.bindings)}get current(){return{...this.value,bindings:Pn(this.value.bindings),custom:{...this.value.custom}}}getCustom(e,t){return this.value.custom[e]??t}setCustom(e,t){this.value.custom[e]=t,Bn(this.storage,this.key,this.value)}setMusicVolume(e){this.update({musicVolume:e})}setSfxVolume(e){this.update({sfxVolume:e})}setMuted(e){this.update({muted:e})}setZoom(e){this.update({zoom:e})}setBindings(e){this.update({bindings:e})}update(e){e.musicVolume!==void 0&&(this.value.musicVolume=Mn(e.musicVolume)),e.sfxVolume!==void 0&&(this.value.sfxVolume=Mn(e.sfxVolume)),e.muted!==void 0&&(this.value.muted=e.muted),e.zoom!==void 0&&(this.value.zoom=Nn(e.zoom)),e.bindings!==void 0&&(this.value.bindings=Fn(e.bindings),Le(this.value.bindings)),e.custom!==void 0&&(this.value.custom=Ln(e.custom)),Bn(this.storage,this.key,this.value)}applyBindings(){Le(this.value.bindings)}reset(){this.value=On(),Le(this.value.bindings),Bn(this.storage,this.key,this.value)}};function Mn(e){return Number.isFinite(e)?Dn(e,0,1):1}function Nn(e){return Number.isFinite(e)?Math.max(.01,e):1}function Pn(e){let t={};for(let[n,r]of Object.entries(e))t[n]=[...r];return t}function Fn(e){if(typeof e!=`object`||!e||Array.isArray(e))return Pn(Ae);let t={};for(let[n,r]of Object.entries(e)){if(!Array.isArray(r))continue;let e=r.filter(e=>typeof e==`string`);e.length>0&&(t[n]=e)}return Object.keys(t).length>0?t:Pn(Ae)}function In(e){let t=typeof e;return t===`string`||t===`number`||t===`boolean`}function Ln(e){if(typeof e!=`object`||!e||Array.isArray(e))return{};let t={};for(let[n,r]of Object.entries(e))In(r)&&(t[n]=r);return t}function Rn(e){let t=On();if(typeof e!=`object`||!e||Array.isArray(e))return t;let n=e;return{musicVolume:typeof n.musicVolume==`number`?Mn(n.musicVolume):t.musicVolume,sfxVolume:typeof n.sfxVolume==`number`?Mn(n.sfxVolume):t.sfxVolume,muted:typeof n.muted==`boolean`?n.muted:t.muted,zoom:typeof n.zoom==`number`?Nn(n.zoom):t.zoom,bindings:Fn(n.bindings),custom:Ln(n.custom)}}function zn(e,t){let n=e.read(t);if(!n)return On();try{return Rn(Mt(n,{label:`settings`}))}catch{return On()}}function Bn(e,t,n){e.write(t,JSON.stringify(n))}var Vn=class extends Kt{constructor(e){super(e,`feedback`)}async submit(e){if(!e.message?.trim())throw Error(`feedback message cannot be empty`);return this.withTimeout(async t=>{let n=await this.fetchFn(this.endpoint,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify(e),signal:t});if(!n.ok)throw Error(`feedback request failed with HTTP ${n.status}`);return{ok:!0,status:n.status}})}},Hn=class{constructor(e){this.schedule=[],this.cursor=0,this.elapsed=0,this.startedWaves=new Set,this.completed=!1,this.onSpawn=e.onSpawn,this.onWaveStart=e.onWaveStart,this.onComplete=e.onComplete,e.waves.forEach((e,t)=>{let n=e.entries.reduce((e,t)=>e+t.count,0),r=e.duration??0,i=0;for(let a of e.entries)for(let o=0;o<a.count;o++,i++){let o=n>1&&r>0?i/(n-1)*r:0;this.schedule.push({time:e.delay+o,kind:a.kind,waveIndex:t})}}),this.schedule.sort((e,t)=>e.time-t.time),this.schedule.length===0&&(this.completed=!0,this.onComplete?.())}update(e){if(!this.completed){for(this.elapsed+=e;this.cursor<this.schedule.length&&this.schedule[this.cursor].time<=this.elapsed;){let e=this.schedule[this.cursor];this.startedWaves.has(e.waveIndex)||(this.startedWaves.add(e.waveIndex),this.onWaveStart?.(e.waveIndex)),this.onSpawn(e.kind),this.cursor++}this.cursor>=this.schedule.length&&(this.completed=!0,this.onComplete?.())}}get isComplete(){return this.completed}};function Un(e,t){let n=e,r=t-(e-(e&1))/2;return{x:n,y:-n-r,z:r}}function Wn(e){return{x:e.x,y:e.z+(e.x-(e.x&1))/2}}var Gn=[{x:1,y:-1,z:0},{x:1,y:0,z:-1},{x:0,y:1,z:-1},{x:-1,y:1,z:0},{x:-1,y:0,z:1},{x:0,y:-1,z:1}];function Kn(e,t){let n=Un(e,t);return Gn.map(e=>Wn({x:n.x+e.x,y:n.y+e.y,z:n.z+e.z}))}function qn(e,t){let n=Un(e.x,e.y),r=Un(t.x,t.y);return Math.max(Math.abs(n.x-r.x),Math.abs(n.y-r.y),Math.abs(n.z-r.z))}function Jn(e){let t=Math.round(e.x),n=Math.round(e.y),r=Math.round(e.z),i=Math.abs(t-e.x),a=Math.abs(n-e.y),o=Math.abs(r-e.z);return i>a&&i>o?t=-n-r:a>o?n=-t-r:r=-t-n,{x:t,y:n,z:r}}function Yn(e,t){let n=qn(e,t);if(n===0)return[{x:e.x,y:e.y}];let r=Un(e.x,e.y),i=Un(t.x,t.y),a=[];for(let e=0;e<=n;e++){let t=e/n;a.push(Wn(Jn({x:r.x+(i.x-r.x)*t,y:r.y+(i.y-r.y)*t,z:r.z+(i.z-r.z)*t})))}return a}function Xn(e,t){let n=Un(e.x,e.y),r=[];for(let e=-t;e<=t;e++)for(let i=Math.max(-t,-e-t);i<=Math.min(t,-e+t);i++){let t=-e-i;r.push(Wn({x:n.x+e,y:n.y+i,z:n.z+t}))}return r}function Zn(e,t,n,r,i={}){let a=+((i.offset??`odd`)===`odd`);return(i.orientation??`flat-top`)===`pointy-top`?{x:e*n+((t&1)===a?n/2:0)+n/2,y:t*r*.75+r/2}:{x:e*n*.75+n/2,y:t*r+((e&1)===a?r/2:0)+r/2}}function Qn(e,t,n,r,i={}){let a=(i.orientation??`flat-top`)===`pointy-top`,o=a?n:n*.75,s=a?r*.75:r,c=Math.round((e-n/2)/o),l=Math.round((t-r/2)/s),u={x:c,y:l},d=1/0;for(let a=-2;a<=2;a++)for(let o=-2;o<=2;o++){let s={x:c+a,y:l+o},f=Zn(s.x,s.y,n,r,i),p=(f.x-e)**2+(f.y-t)**2;p<d&&(d=p,u=s)}return u}function $n(e,t,n,r){return n>=0&&r>=0&&n<e&&r<t}function er(e,t,n){return n*e+t}function tr(e,t){return t%e}function nr(e,t){return Math.floor(t/e)}function rr(e,t){return`${e},${t}`}function ir(e){let t=/^(-?\d+),(-?\d+)$/.exec(e);return t?{x:Number(t[1]),y:Number(t[2])}:null}var ar=[[0,-1],[1,0],[0,1],[-1,0]],or=[[-1,-1],[0,-1],[1,-1],[-1,0],[1,0],[-1,1],[0,1],[1,1]],sr=[[0,-1],[1,-1],[1,0],[1,1],[0,1],[-1,1],[-1,0],[-1,-1]];function cr(e,t){return Math.hypot(e.x-t.x,e.y-t.y)}function lr(e,t){return Math.max(Math.abs(e.x-t.x),Math.abs(e.y-t.y))}function ur(e,t){return Math.abs(e.x-t.x)+Math.abs(e.y-t.y)}function dr(e,t,n){switch(e){case`euclidean`:return cr(t,n);case`manhattan`:return ur(t,n);case`chebyshev`:return lr(t,n);default:throw RangeError(`unknown distance metric: ${e}`)}}var fr=.12,pr=null,mr,hr=new p;function gr(){if(mr===void 0&&(mr=typeof matchMedia==`function`?matchMedia(`(prefers-reduced-motion: reduce)`):null,mr)){let e=()=>{hr.dispatch(vr())};typeof mr.addEventListener==`function`?mr.addEventListener(`change`,e):typeof mr.addListener==`function`&&mr.addListener(e)}return mr}function _r(){return gr()?.matches??!1}function vr(){return pr??_r()}function yr(e){pr=e,hr.dispatch(vr())}function br(e){return gr(),hr.add(e),()=>hr.remove(e)}function xr(e,t=`decorative`){return vr()?t===`meaningful`?Math.min(e,fr):0:e}var Sr=class e{constructor(e,t){this.width=e,this.height=t,this.volume=new Float32Array(e*t)}index(e,t){return e<0||t<0||e>=this.width||t>=this.height?-1:t*this.width+e}volumeAt(e,t){let n=this.index(e,t);return n<0?0:this.volume[n]}total(){let e=0;for(let t=0;t<this.volume.length;t++)e+=this.volume[t];return e}seed(e,t,n){let r=this.index(e,t);r>=0&&(this.volume[r]+=n)}clear(e,t){let n=this.index(e,t);n>=0&&(this.volume[n]=0)}spread(e,t=.25,n=.9){let r=new Float32Array(this.volume.length);for(let n=0;n<this.height;n++)for(let i=0;i<this.width;i++){let a=n*this.width+i,o=this.volume[a];if(o<=0)continue;let s=[];for(let[t,r]of ar){let a=i+t,o=n+r,c=this.index(a,o);c>=0&&e(a,o)&&s.push(c)}if(s.length===0)r[a]+=o;else{let e=o*t/s.length;r[a]+=o-e*s.length;for(let t of s)r[t]+=e}}let i=[];for(let e=0;e<r.length;e++){let t=r[e]*n,a=t<.001?0:t;a===0&&this.volume[e]>0&&i.push({x:e%this.width,y:Math.floor(e/this.width)}),this.volume[e]=a}return i}cellsAbove(e){let t=[];for(let n=0;n<this.height;n++)for(let r=0;r<this.width;r++){let i=this.volume[n*this.width+r];i>=e&&t.push({x:r,y:n,volume:i})}return t}toJSON(){return{width:this.width,height:this.height,volume:[...this.volume]}}static fromJSON(t){let n=new e(t.width,t.height);return n.volume.set(t.volume.slice(0,n.volume.length)),n}},Cr={linear:e=>e,easeInQuad:e=>e*e,easeOutQuad:e=>e*(2-e),easeInOutQuad:e=>e<.5?2*e*e:1-(-2*e+2)**2/2,easeInCubic:e=>e*e*e,easeOutCubic:e=>1-(1-e)**3,easeInOutCubic:e=>e<.5?4*e*e*e:1-(-2*e+2)**3/2},wr=class{constructor(){this.tweens=[]}tween(e,t,n=Cr.linear){let{ease:r=Cr.linear,intent:i=`decorative`,alternate:a}=typeof n==`function`?{ease:n}:n,o=vr()&&a!==void 0,s=o?a:t,c=xr(e,o?`meaningful`:i);return c>0?new Promise(e=>{this.tweens.push({elapsed:0,duration:c,ease:r,apply:s,resolve:e,finishOnReduced:!o&&i===`decorative`})}):(s(1),Promise.resolve())}update(e){if(this.tweens.length!==0)for(let t of[...this.tweens]){if(t.finishOnReduced&&vr()){this.tweens.splice(this.tweens.indexOf(t),1),t.apply(1),t.resolve();continue}t.elapsed+=e;let n=Math.min(1,t.elapsed/t.duration);t.apply(t.ease(n)),n>=1&&(this.tweens.splice(this.tweens.indexOf(t),1),t.resolve())}}get isBusy(){return this.tweens.length>0}clear(){this.tweens=[]}},Tr=class{constructor(e={}){this.history=[],this.cursor=-1,this.limit=Math.max(1,e.limit??50)}push(e){this.history.length=this.cursor+1,this.history.push(e),this.history.length>this.limit&&this.history.shift(),this.cursor=this.history.length-1}get canUndo(){return this.cursor>0}get canRedo(){return this.cursor<this.history.length-1}undo(){return this.canUndo?(this.cursor--,this.history[this.cursor]):null}redo(){return this.canRedo?(this.cursor++,this.history[this.cursor]):null}get current(){return this.cursor>=0?this.history[this.cursor]:null}clear(){this.history=[],this.cursor=-1}};function Er(e,t){if(!Number.isFinite(t.maxCost)||t.maxCost<0)return new Map;let n=t.key(e),r=new Map([[n,{cell:e,cost:0}]]),i=[{cell:e,key:n,cost:0}];for(;i.length>0;){let e=Or(i),n=r.get(e.key);if(n&&e.cost===n.cost&&!t.stop?.(e.cell,e.cost))for(let n of t.neighbors(e.cell)){let a=t.cost(e.cell,n);if(!Number.isFinite(a)||a<0)continue;let o=e.cost+a;if(o>t.maxCost||t.canEnter&&!t.canEnter(n,e.cell,o))continue;let s=t.key(n);if(o>=(r.get(s)?.cost??1/0))continue;let c={cell:n,key:s,cost:o};r.set(s,c),Dr(i,c)}}return r}function Dr(e,t){e.push(t);let n=e.length-1;for(;n>0;){let t=Math.floor((n-1)/2);if(e[t].cost<=e[n].cost)break;[e[t],e[n]]=[e[n],e[t]],n=t}}function Or(e){let t=e[0],n=e.pop();if(!t||!n||e.length===0)return t;e[0]=n;let r=0;for(;;){let t=r*2+1,n=t+1,i=r;if(t<e.length&&e[t].cost<e[i].cost&&(i=t),n<e.length&&e[n].cost<e[i].cost&&(i=n),i===r)break;[e[r],e[i]]=[e[i],e[r]],r=i}return t}var kr=class{constructor(){this.changed=new p,this.tasks=[],this.progress=new Map,this.status_=`idle`,this.current_=null,this.error_=null,this.cancelled=!1}add(e){if(!e.id)throw Error(`load task needs an id`);if(this.tasks.some(t=>t.id===e.id))throw Error(`duplicate load task "${e.id}"`);if(e.weight!==void 0&&!(e.weight>0))throw Error(`load task weight must be positive`);if(this.status_===`loading`)throw Error(`cannot add a task while loading`);return this.tasks.push(e),this.progress.set(e.id,0),this.emit(),this}get snapshot(){let e=this.tasks.reduce((e,t)=>e+(t.weight??1),0),t=this.tasks.reduce((e,t)=>e+(t.weight??1)*(this.progress.get(t.id)??0),0);return{status:this.status_,completed:t,total:e,current:this.current_,error:this.error_}}async start(){if(this.status_===`loading`)throw Error(`load queue is already running`);this.status_=`loading`,this.cancelled=!1,this.error_=null,this.emit();try{for(let e of this.tasks){if(this.cancelled)break;this.current_=e.id,this.emit();let t=this;await e.run({get cancelled(){return t.cancelled},report:t=>this.report(e.id,t)}),this.cancelled||this.report(e.id,1)}this.current_=null,this.status_=this.cancelled?`cancelled`:`ready`,this.emit()}catch(e){throw this.current_=null,this.error_=e,this.status_=`failed`,this.emit(),e}}cancel(){this.status_===`loading`&&(this.cancelled=!0)}retry(){if(this.status_===`loading`)throw Error(`cannot retry while loading`);this.status_=`idle`,this.current_=null,this.error_=null,this.cancelled=!1;for(let e of this.tasks)this.progress.set(e.id,0);this.emit()}report(e,t){if(!Number.isFinite(t))throw Error(`load progress must be finite`);this.progress.set(e,Math.max(0,Math.min(1,t))),this.emit()}emit(){this.changed.dispatch(this.snapshot)}},Ar=class{constructor(){this.entities=new Map,this.ids=new Map,this.sequence=0}add(e,t){let n=this.ids.get(e);if(n!==void 0){if(t!==void 0&&t!==n)throw Error(`entity already has id ${n}`);return n}let r=t;if(r!==void 0){if(r.length===0)throw Error(`entity id must not be empty`);if(this.entities.has(r))throw Error(`entity id already exists: ${r}`)}else do r=`e${this.sequence++}`;while(this.entities.has(r));return this.entities.set(r,e),this.ids.set(e,r),r}get(e){return this.entities.get(e)}idOf(e){return this.ids.get(e)}has(e){return this.entities.has(e)}remove(e){let t=this.entities.get(e);return t!==void 0&&(this.entities.delete(e),this.ids.delete(t),!0)}get size(){return this.entities.size}},jr=class{constructor(e){this.queue=[],this.busy=!1,this.remaining=0,this.play=e.play}enqueue(e){this.queue.push(...e),this.busy||this.advance()}update(e){this.busy&&(this.remaining-=e,this.remaining<=0&&this.advance())}get isBusy(){return this.busy}clear(){this.queue=[],this.busy=!1,this.remaining=0}advance(){let e=this.queue.shift();if(e===void 0){this.busy=!1,this.remaining=0;return}this.busy=!0;let t=this.play(e)||0;t>0?this.remaining=t:this.advance()}},Mr=2166136261,Nr=16777619;function Pr(e,t=!1){if(e==null)return`null`;switch(typeof e){case`boolean`:return e?`true`:`false`;case`number`:return Number.isFinite(e)?String(e):`null`;case`string`:return JSON.stringify(e);case`bigint`:return JSON.stringify(e.toString());case`function`:case`symbol`:return t?`null`:`undefined`}return Array.isArray(e)?`[${e.map(e=>Pr(e,!0)).join(`,`)}]`:`{${Object.entries(e).filter(([,e])=>e!==void 0&&typeof e!=`function`&&typeof e!=`symbol`).sort(([e],[t])=>e<t?-1:+(e>t)).map(([e,t])=>`${JSON.stringify(e)}:${Pr(t)}`).join(`,`)}}`}function Fr(e){let t=Pr(e),n=Mr;for(let e=0;e<t.length;e++)n^=t.charCodeAt(e),n=Math.imul(n,Nr)>>>0;return n>>>0}var Ir=class{constructor(){this.reference=new Map,this.desyncTick=null}observe(e,t){let n=this.reference.get(e);return n===void 0?(this.reference.set(e,t),!0):n===t||(this.desyncTick===null&&(this.desyncTick=e),!1)}get divergent(){return this.desyncTick!==null}get atTick(){return this.desyncTick}reset(){this.reference.clear(),this.desyncTick=null}};function Lr(e,t={}){let n=Br(e);if(n.length===0)return[];let[r,...i]=n,a=t.listDelimiter??`;`,o=t.mapDelimiter??`=`;return i.map((e,n)=>{let i={};return r.forEach((r,s)=>{let c=e[s]??``;c!==``&&(i[r]=Rr(c,t.columns?.[r]??`string`,r,n,a,o))}),i})}function Rr(e,t,n,r,i,a){return zr(e,t,{column:n,rowIndex:r,listDelimiter:i,mapDelimiter:a,error:()=>t===`number`?`CSV row ${r+1}, column "${n}": "${e}" is not a number`:t===`boolean`?`CSV row ${r+1}, column "${n}": "${e}" is not "true" or "false"`:t===`map`?`CSV row ${r+1}, column "${n}": "${e.split(i).map(e=>e.trim()).find(e=>e&&!e.includes(a))??e}" has no "${a}" to split a key from its value`:`CSV row ${r+1}, column "${n}": "${e}" is invalid`})}function zr(e,t,n={}){let r=n.column??`value`,i=n.listDelimiter??`;`,a=n.mapDelimiter??`=`,o=e=>{throw Error(n.error?.(e)??e)};switch(t){case`string`:return e;case`number`:{let t=Number(e);return Number.isFinite(t)||o(`${r} must be a number`),t}case`boolean`:{let t=e.toLowerCase();if(t===`true`)return!0;if(t===`false`)return!1;if(n.acceptYesNo&&t===`yes`)return!0;if(n.acceptYesNo&&t===`no`)return!1;o(`${r} must be true, false${n.acceptYesNo?`, yes, or no`:``}`)}case`list`:return e.split(i).map(e=>e.trim()).filter(e=>e.length>0);case`map`:{let t={};for(let n of e.split(i).map(e=>e.trim()).filter(e=>e.length>0)){let e=n.indexOf(a);e===-1&&o(`${r} map entry must contain ${a}`),t[n.slice(0,e).trim()]=n.slice(e+a.length).trim()}return t}}}function Br(e){let t=[],n=[],r=``,i=!1,a=0;for(;a<e.length;){let o=e[a];if(i){if(o===`"`){if(e[a+1]===`"`){r+=`"`,a+=2;continue}i=!1,a++;continue}r+=o,a++}else o===`"`?(i=!0,a++):o===`,`?(n.push(r),r=``,a++):o===`\r`?a++:o===`
1
+ var mw_games=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=Object.create,n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.getPrototypeOf,o=Object.prototype.hasOwnProperty,s=(e,t,n)=>()=>{if(n)throw n[0];try{return e&&(t=e(e=0)),t}catch(e){throw n=[e],e}},c=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),l=(e,t)=>{let r={};for(var i in e)n(r,i,{get:e[i],enumerable:!0});return t||n(r,Symbol.toStringTag,{value:`Module`}),r},u=(e,t,a,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var c=i(t),l=0,u=c.length,d;l<u;l++)d=c[l],!o.call(e,d)&&d!==a&&n(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(s=r(t,d))||s.enumerable});return e},d=(e,r,i)=>(i=e==null?{}:t(a(e)),u(r||!e||!e.__esModule||!o.call(e,`default`)?n(i,`default`,{value:e,enumerable:!0}):i,e)),f=`0.36.1`,p=class{constructor(e=!1){this.listeners=[],this.stackMode=e}add(e){this.listeners.includes(e)||(this.stackMode?this.listeners.unshift(e):this.listeners.push(e))}remove(e){let t=this.listeners.indexOf(e);t!==-1&&this.listeners.splice(t,1)}removeAll(){this.listeners.length=0}get size(){return this.listeners.length}dispatch(e){let t=this.listeners.slice();for(let n of t)if(this.listeners.includes(n)&&n(e)===!0)return!0;return!1}},m=class{constructor(){this.onDestroy=new p,this.destroyed=!1}update(e){}resize(e,t){}onSuspend(){}onResume(e){}destroy(){this.destroyed||(this.destroyed=!0,this.onDestroy.dispatch(),this.onDestroy.removeAll(),this.teardown())}teardown(){}get isDestroyed(){return this.destroyed}},h=class{constructor(){this.scenes=[]}get current(){return this.scenes.length===0?null:this.scenes[this.scenes.length-1]}get depth(){return this.scenes.length}replace(e){for(let e of this.scenes)e.destroy();this.scenes=[e],e.create()}push(e){this.current?.onSuspend(),this.scenes.push(e),e.create()}pop(e){if(this.scenes.length<=1)throw Error(`cannot pop the last scene off the stack - switchScene somewhere instead`);this.scenes.pop()?.destroy(),this.current?.onResume(e)}update(e){this.current?.update(e)}resize(e,t){for(let n of this.scenes)n.resize(e,t)}destroy(){for(let e of this.scenes)e.destroy();this.scenes=[]}},g=class{constructor(){this.items=new Map}register(e,t){if(this.items.has(e))throw Error(`"${e}" is already registered`);this.items.set(e,t)}get(e){let t=this.items.get(e);if(t===void 0)throw Error(`no registration named "${e}"`);return t}has(e){return this.items.has(e)}list(){return[...this.items.keys()]}};function _(){let e=1,t=new Map;return{get size(){return t.size},put(n){let r=e++;return t.set(r,n),r},get(e){let n=t.get(e);if(n===void 0)throw Error(`unknown handle ${e}`);return n},drop(e){t.delete(e)},clear(){t.clear()},with(n,r){let i=e++;t.set(i,n);try{return r(i)}finally{t.delete(i)}}}}var v=class{constructor(){this.order=[],this.registry=new g}add(e,t){this.registry.register(e.name,e),this.order.push(e),e.create?.(t)}has(e){return this.registry.has(e)}get(e){return this.registry.get(e)}update(e,t){for(let n of this.order)n.update?.(e,t)}resize(e,t,n){for(let r of this.order)r.resize?.(e,t,n)}onSuspend(e){for(let t of this.order)t.onSuspend?.(e)}onResume(e,t){for(let n of this.order)n.onResume?.(e,t)}destroy(e){for(let t=this.order.length-1;t>=0;t--)this.order[t].destroy?.(e);this.order=[],this.registry=new g}},y={debug:0,info:1,warn:2,error:3},b=class{constructor(e,t={}){if(!e)throw Error(`a logger needs a category`);this.category=e,this.level=t.level??`debug`,this.sink=t.sink??x}setLevel(e){this.level=e}debug(e,t){this.write(`debug`,e,t)}info(e,t){this.write(`info`,e,t)}warn(e,t){this.write(`warn`,e,t)}error(e,t){this.write(`error`,e,t)}write(e,t,n){y[e]<y[this.level]||this.sink({level:e,category:this.category,message:t,data:n,time:Date.now()})}};function x(e){let t=`[${e.category}] ${e.message}`;e.level===`debug`?console.debug(t,e.data??``):e.level===`info`?console.info(t,e.data??``):e.level===`warn`?console.warn(t,e.data??``):console.error(t,e.data??``)}var S=class{constructor(){this.hooks=[]}on(e,t,n){this.hooks.push({event:e,handler:t,source:n})}off(e){this.hooks=this.hooks.filter(t=>t.handler!==e)}offSource(e){this.hooks=this.hooks.filter(t=>t.source!==e)}emit(e,...t){for(let n of[...this.hooks])n.event===e&&n.handler(...t)}get size(){return this.hooks.length}clear(){this.hooks=[]}},C=l({Generator:()=>M,MersenneTwister:()=>me,RandomStreams:()=>he,active:()=>ee,chance:()=>j,element:()=>ne,float:()=>O,int:()=>k,normalRange:()=>A,pop:()=>T,push:()=>w,range:()=>te,reset:()=>D,shuffle:()=>ae,weighted:()=>re,weightedKey:()=>ie,withSeed:()=>E});function ee(){return ce()}function w(e){let t=new M(e);return se.push(t),t}function T(){if(se.length===1)throw Error(`the base random generator cannot be popped`);se.pop()}function E(e,t){w(e);try{return t()}finally{T()}}function D(){se.length=0,se.push(new M)}function O(e,t,n){let r=n??ce();return e===void 0?r.float():t===void 0?r.float()*e:e+r.float()*(t-e)}function k(e,t,n){let r=n??ce();return t===void 0?r.int(e):e+r.int(t-e)}function te(e,t,n){return e+(n??ce()).int(t-e+1)}function A(e,t,n){let r=n??ce();return e+Math.floor((r.float()+r.float())*(t-e+1)/2)}function j(e,t){return(t??ce()).float()<e}function ne(e,t){return e.length>0?e[(t??ce()).int(e.length)]:null}function re(e,t){let n=0;for(let t of e)n+=t;if(n<=0)return null;let r=(t??ce()).float()*n;for(let t=0;t<e.length;t++)if(r-=e[t],r<0)return t;return e.length-1}function ie(e,t){let n=[...e.keys()],r=re(n.map(t=>e.get(t)??0),t);return r===null?null:n[r]}function ae(e,t){for(let n=e.length-1;n>0;n--){let r=(t??ce()).int(n+1);[e[n],e[r]]=[e[r],e[n]]}return e}function oe(e){let t=2166136261;for(let n=0;n<e.length;n++)t^=e.charCodeAt(n),t=Math.imul(t,16777619)>>>0;return t>>>0}var M,se,ce,le,ue,de,fe,pe,me,he,ge=s((()=>{M=class{constructor(e){this.s0=0,this.s1=0,this.s2=0,this.s3=0,this.seed=e===void 0?Math.random()*4294967296>>>0:e>>>0;let t=this.seed,n=()=>{t=t+2654435769>>>0;let e=t;return e=Math.imul(e^e>>>16,569420461),e=Math.imul(e^e>>>15,1935289751),(e^e>>>15)>>>0};this.s0=n(),this.s1=n(),this.s2=n(),this.s3=n(),(this.s0|this.s1|this.s2|this.s3)===0&&(this.s0=1)}nextUint32(){let e=Math.imul(this.rotl(Math.imul(this.s1,5),7),9)>>>0>>>0,t=this.s1<<9>>>0;return this.s2^=this.s0,this.s3^=this.s1,this.s1^=this.s2,this.s0^=this.s3,this.s2^=t,this.s3=this.rotl(this.s3,11),e}float(){return this.nextUint32()/4294967296}int(e){if(e<=0)return 0;let t=4294967296-4294967296%e,n=this.nextUint32();for(;n>=t;)n=this.nextUint32();return n%e}getState(){return[this.s0,this.s1,this.s2,this.s3]}setState(e){[this.s0,this.s1,this.s2,this.s3]=e}rotl(e,t){return(e<<t|e>>>32-t)>>>0}},se=[new M],ce=()=>se[se.length-1],le=624,ue=397,de=2567483615,fe=2147483648,pe=2147483647,me=class{constructor(e=5489){this.state=new Uint32Array(le),this.index=le,this.seedValue=5489,this.produced=0,this.seed(e)}seed(e){this.seedValue=e>>>0,this.state[0]=this.seedValue;for(let e=1;e<le;e++){let t=this.state[e-1]^this.state[e-1]>>>30;this.state[e]=Math.imul(1812433253,t)+e>>>0}this.index=le,this.produced=0}generate(){for(let e=0;e<le;e++){let t=this.state[e]&fe|this.state[(e+1)%le]&pe,n=this.state[(e+ue)%le]^t>>>1;t&1&&(n^=de),this.state[e]=n>>>0}this.index=0}nextUint32(){this.index>=le&&this.generate();let e=this.state[this.index++];return e^=e>>>11,e^=e<<7&2636928640,e^=e<<15&4022730752,e^=e>>>18,this.produced++,e>>>0}float(){return this.nextUint32()/4294967296}int(e){if(e<=0)return 0;let t=4294967296-4294967296%e,n=this.nextUint32();for(;n>=t;)n=this.nextUint32();return n%e}discard(e){for(let t=0;t<e;t++)this.nextUint32()}get discardCount(){return this.produced}getState(){return{seed:this.seedValue,state:[...this.state],index:this.index,discard:this.produced}}setState(e){this.seedValue=e.seed>>>0;for(let t=0;t<le;t++)this.state[t]=(e.state[t]??0)>>>0;this.index=e.index,this.produced=e.discard}},he=class{constructor(e=0){this.streams=new Map,this.baseSeed=e>>>0}stream(e){let t=this.streams.get(e);if(!t){let n=(this.baseSeed^oe(e))>>>0;t={seed:n,engine:new me(n)},this.streams.set(e,t)}return t.engine}seedOf(e){return this.streams.get(e)?.seed}reseed(e){this.baseSeed=e>>>0,this.streams.clear()}names(){return[...this.streams.keys()]}getState(){let e={};for(let[t,n]of this.streams)e[t]=n.engine.getState();return e}setState(e){for(let[t,n]of Object.entries(e)){let e=new me(n.seed);e.setState(n),this.streams.set(t,{seed:n.seed,engine:e})}}}})),_e=l({DEFAULT_BINDINGS:()=>Ae,actionsForKey:()=>Ne,attach:()=>mt,attachSwipe:()=>pt,bind:()=>je,bindAxis:()=>it,bindButton:()=>rt,bindTouch:()=>ct,detach:()=>ht,dispatchAction:()=>we,dispatchComposition:()=>Te,dispatchText:()=>Ce,endFrame:()=>Ge,exportBindings:()=>Ie,gamepadAxisCode:()=>nt,gamepadButtonCode:()=>tt,importBindings:()=>Le,isDown:()=>He,justPressed:()=>Ue,justReleased:()=>We,keysFor:()=>Fe,onAction:()=>F,onComposition:()=>I,onKey:()=>be,onText:()=>xe,onWheel:()=>Ee,pollGamepads:()=>at,pressTouch:()=>lt,releaseTouch:()=>ut,resetBindings:()=>Re,rumble:()=>ot,textFromKey:()=>Se,touchCode:()=>st,unbind:()=>Pe,wheelActionFor:()=>De}),N=new Map,P=new Set,ve=new Set,ye=new Set,F=new p(!0),be=new p(!0),xe=new p(!0),I=new p(!0);function Se(e){return e.isComposing||e.ctrlKey||e.metaKey||e.altKey?null:e.key.length===1?e.key:null}function Ce(e){e.length>0&&xe.dispatch(e)}function we(e){F.dispatch(e)}function Te(e){I.dispatch({phase:e.phase,text:e.text})}var Ee=new p(!0);function De(e){return e.ctrlKey||e.metaKey?`zoom`:e.shiftKey?`scrollHorizontal`:`scroll`}function Oe(e){let t=De(e);t===`zoom`&&e.preventDefault();let n=t===`scrollHorizontal`&&e.deltaX!==0?e.deltaX:e.deltaY;Ee.dispatch({action:t,delta:n})}var ke=!1,Ae={up:[`ArrowUp`,`KeyW`,`Numpad8`],down:[`ArrowDown`,`KeyS`,`Numpad2`],left:[`ArrowLeft`,`KeyA`,`Numpad4`],right:[`ArrowRight`,`KeyD`,`Numpad6`],upLeft:[`Numpad7`],upRight:[`Numpad9`],downLeft:[`Numpad1`],downRight:[`Numpad3`],wait:[`Numpad5`,`Period`],confirm:[`Enter`,`Space`,`NumpadEnter`],cancel:[`Escape`,`Backspace`],menu:[`KeyI`,`Tab`]};function je(e,t){N.set(e,new Set(t))}function Me(e,t){let n=N.get(e)??new Set;for(let e of t)n.add(e);N.set(e,n)}function Ne(e){return ze(e)}function Pe(e){N.delete(e)}function Fe(e){return[...N.get(e)??[]]}function Ie(){let e={};for(let[t,n]of N)e[t]=[...n];return e}function Le(e){N.clear();for(let[t,n]of Object.entries(e))je(t,n)}function Re(){Le(Ae)}Re();function ze(e){let t=[];for(let[n,r]of N)r.has(e)&&t.push(n);return t}function Be(e){if(!P.has(e)){P.add(e);for(let t of ze(e))ve.add(t),F.dispatch(t)}}function Ve(e){if(P.has(e)){P.delete(e);for(let t of ze(e))ye.add(t)}}function He(e){let t=N.get(e);if(!t)return!1;for(let e of t)if(P.has(e))return!0;return!1}function Ue(e){return ve.has(e)}function We(e){return ye.has(e)}function Ge(){ve.clear(),ye.clear()}function Ke(e){let t=Se(e);t!==null&&Ce(t)}function qe(e){e.repeat?(be.dispatch(e),Ke(e)):(Be(e.code),be.dispatch(e),Ke(e))}function Je(e){Ve(e.code)}function Ye(e){Te({phase:`start`,text:e.data??``})}function Xe(e){Te({phase:`update`,text:e.data??``})}function Ze(e){Te({phase:`end`,text:e.data??``})}function Qe(){P.clear(),ve.clear(),ye.clear()}var $e=.5,et=new Set;function tt(e,t){return`Gamepad${e}Button${t}`}function nt(e,t,n){return`Gamepad${e}Axis${t}${n>0?`+`:`-`}`}function rt(e,t,n){Me(e,n.map(e=>tt(t,e)))}function it(e,t,n,r){Me(e,[nt(t,n,r)])}function at(e=typeof navigator?.getGamepads==`function`?navigator.getGamepads():[]){et.clear();for(let t of e)if(t){for(let e=0;e<t.buttons.length;e++)t.buttons[e].pressed&&et.add(tt(t.index,e));for(let e=0;e<t.axes.length;e++){let n=t.axes[e];n>=$e?et.add(nt(t.index,e,1)):n<=-.5&&et.add(nt(t.index,e,-1))}}for(let e of et)Be(e);for(let e of P)!et.has(e)&&e.startsWith(`Gamepad`)&&Ve(e)}function ot(e,t,n=typeof navigator?.getGamepads==`function`?navigator.getGamepads():[]){(n[e]?.vibrationActuator)?.playEffect(`dual-rumble`,{duration:t.duration,weakMagnitude:t.weakMagnitude??1,strongMagnitude:t.strongMagnitude??1})}function st(e){return`Touch:${e}`}function ct(e,t=e){Me(e,[st(t)])}function lt(e){Be(st(e))}function ut(e){Ve(st(e))}var dt={up:`up`,down:`down`,left:`left`,right:`right`,upLeft:`upLeft`,upRight:`upRight`,downLeft:`downLeft`,downRight:`downRight`},ft=[`right`,`downRight`,`down`,`downLeft`,`left`,`upLeft`,`up`,`upRight`];function pt(e,t={}){let n={...dt,...t.actions},r=t.minDistance??24;for(let e of[...ft,`tap`]){let t=n[e];t&&ct(t,`swipe:${e}`)}let i=!1,a=-1,o=0,s=0;function c(e){lt(`swipe:${e}`),ut(`swipe:${e}`)}function l(e){i=!0,a=e.pointerId,o=e.clientX,s=e.clientY}function u(e){if(!i||e.pointerId!==a)return;i=!1;let t=e.clientX-o,l=e.clientY-s;if(Math.hypot(t,l)<r){n.tap&&c(`tap`);return}let u=ft[(Math.round(Math.atan2(l,t)/(Math.PI/4))%8+8)%8];n[u]&&c(u)}return e.addEventListener(`pointerdown`,l),e.addEventListener(`pointerup`,u),e.addEventListener(`pointercancel`,u),()=>{e.removeEventListener(`pointerdown`,l),e.removeEventListener(`pointerup`,u),e.removeEventListener(`pointercancel`,u)}}function mt(e=window){ke||(ke=!0,e.addEventListener(`keydown`,qe),e.addEventListener(`keyup`,Je),e.addEventListener(`compositionstart`,Ye),e.addEventListener(`compositionupdate`,Xe),e.addEventListener(`compositionend`,Ze),window.addEventListener(`blur`,Qe),e.addEventListener(`wheel`,Oe,{passive:!1}))}function ht(e=window){ke&&(ke=!1,e.removeEventListener(`keydown`,qe),e.removeEventListener(`keyup`,Je),e.removeEventListener(`compositionstart`,Ye),e.removeEventListener(`compositionupdate`,Xe),e.removeEventListener(`compositionend`,Ze),e.removeEventListener(`wheel`,Oe),window.removeEventListener(`blur`,Qe),Qe())}var gt=class{constructor(e,t={}){if(!e)throw Error(`a player needs a non-empty id`);this.id=e,this.padIndex=t.padIndex}scoped(e){return`${this.id}:${e}`}bind(e,t){je(this.scoped(e),t)}bindButton(e,t){if(this.padIndex===void 0)throw Error(`player "${this.id}" has no padIndex to bind a gamepad button to`);rt(this.scoped(e),this.padIndex,t)}bindAxis(e,t,n){if(this.padIndex===void 0)throw Error(`player "${this.id}" has no padIndex to bind a gamepad axis to`);it(this.scoped(e),this.padIndex,t,n)}bindTouch(e,t=e){ct(this.scoped(e),`${this.id}:${t}`)}pressTouch(e){lt(`${this.id}:${e}`)}releaseTouch(e){ut(`${this.id}:${e}`)}isDown(e){return He(this.scoped(e))}justPressed(e){return Ue(this.scoped(e))}justReleased(e){return We(this.scoped(e))}keysFor(e){return Fe(this.scoped(e))}},_t=25214903917n,vt=11n,yt=(1n<<48n)-1n,bt=class{constructor(e=0,t={}){this.state=0n,this.onDraw=t.onDraw,this.setSeed(e)}setSeed(e){this.state=(BigInt.asIntN(64,BigInt(e))^_t)&yt}next(e){if(!Number.isInteger(e)||e<1||e>32)throw Error(`next needs 1 to 32 bits, got ${e}`);this.state=this.state*_t+vt&yt;let t=Number(this.state>>BigInt(48-e)),n=e===32?t|0:t;return this.onDraw?.({bits:e,value:n}),n}nextInt(e){if(e===void 0)return this.next(32);if(!Number.isInteger(e)||e<=0||e>2147483647)throw Error(`nextInt needs a bound from 1 to 2147483647, got ${e}`);if((e&-e)===e)return Math.floor(e*this.next(31)/2147483648);let t,n;do t=this.next(31),n=t%e;while(t-n+(e-1)>2147483647);return n}nextLong(){let e=BigInt(this.next(32)),t=BigInt(this.next(32));return BigInt.asIntN(64,(e<<32n)+t)}nextDouble(){return(this.next(26)*134217728+this.next(27))/2**53}nextFloat(){return this.next(24)/16777216}nextBoolean(){return this.next(1)!==0}};ge();function xt(e,t){if(!t)throw Error(`scramble needs a non-empty key`);let n=new TextEncoder().encode(e),r=new TextEncoder().encode(t),i=new Uint8Array(n.length);for(let e=0;e<n.length;e++)i[e]=n[e]^r[e%r.length];return Ct(i)}function St(e,t){if(!t)throw Error(`scramble needs a non-empty key`);let n=wt(e),r=new TextEncoder().encode(t),i=new Uint8Array(n.length);for(let e=0;e<n.length;e++)i[e]=n[e]^r[e%r.length];return new TextDecoder(`utf-8`,{fatal:!0}).decode(i)}function Ct(e){let t=``;for(let n of e)t+=String.fromCharCode(n);return btoa(t)}function wt(e){let t=atob(e),n=new Uint8Array(t.length);for(let e=0;e<t.length;e++)n[e]=t.charCodeAt(e);return n}var Tt=10485760;function Et(e,t={}){let n=t.maxBytes??Tt,r=typeof e==`string`?new TextEncoder().encode(e).length:e.length;if(r>n)throw Error(`inbound data exceeds the ${n}-byte limit (${r} bytes)`)}function Dt(e){for(let t=0;t<e.length;t++){let n=e.charCodeAt(t);if(n!==9&&n!==10&&n!==13&&(n<=31||n===127))throw Error(`inbound data contains a control character (0x${n.toString(16).padStart(2,`0`)}) at index ${t}`)}}function Ot(e,t={}){return Et(e,t),Dt(e),e}var kt=new Set([`__proto__`,`constructor`,`prototype`]);function At(e,t=`$`){if(Array.isArray(e))e.forEach((e,n)=>At(e,`${t}[${n}]`));else if(typeof e==`object`&&e)for(let n of Object.keys(e)){if(kt.has(n))throw Error(`${t} contains a forbidden key "${n}"`);At(e[n],`${t}.${n}`)}}function jt(e,t,n=`$`){if(e===void 0){if(t.optional)return;throw Error(`${n} is required`)}switch(t.type){case`string`:if(typeof e!=`string`)throw Error(`${n} must be a string`);return;case`number`:if(typeof e!=`number`||!Number.isFinite(e))throw Error(`${n} must be a finite number`);if(t.min!==void 0&&e<t.min)throw Error(`${n} must be >= ${t.min}`);if(t.max!==void 0&&e>t.max)throw Error(`${n} must be <= ${t.max}`);return;case`boolean`:if(typeof e!=`boolean`)throw Error(`${n} must be a boolean`);return;case`array`:if(!Array.isArray(e))throw Error(`${n} must be an array`);if(t.maxLength!==void 0&&e.length>t.maxLength)throw Error(`${n} exceeds its maximum length of ${t.maxLength}`);e.forEach((e,r)=>jt(e,t.items,`${n}[${r}]`));return;case`object`:if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`${n} must be an object`);for(let t of Object.keys(e))if(kt.has(t))throw Error(`${n} contains a forbidden key "${t}"`);for(let[r,i]of Object.entries(t.fields))jt(e[r],i,`${n}.${r}`);return}}function Mt(e,t={}){let n=t.label??`inbound data`;Ot(e,t);try{return JSON.parse(e,(e,t)=>{if(kt.has(e))throw new Nt(e);return t})}catch(e){throw e instanceof Nt?Error(`${n} contains a forbidden key "${e.key}"`):Error(`${n} is not valid JSON (${e instanceof Error?e.message:String(e)})`)}}var Nt=class{constructor(e){this.key=e}};function Pt(e,t){let n=Mt(e,{label:t}),r=n?.meta;if(typeof n!=`object`||!n||Array.isArray(n)||!(`state`in n))throw Error(`${t} is not save data (expected { meta, state })`);if(typeof r!=`object`||!r||!Number.isSafeInteger(r.version)||r.version<0)throw Error(`${t} has no valid meta.version`);if(typeof r.savedAt!=`number`||!Number.isFinite(r.savedAt))throw Error(`${t} has no valid meta.savedAt`);return n}var Ft=class{constructor(){this.data=new Map}read(e){return this.data.get(e)??null}write(e,t){this.data.set(e,t)}remove(e){this.data.delete(e)}keys(){return[...this.data.keys()]}};function It(){return typeof localStorage>`u`?new Ft:{read:e=>localStorage.getItem(e),write:(e,t)=>localStorage.setItem(e,t),remove:e=>localStorage.removeItem(e),keys:()=>Object.keys(localStorage)}}var Lt=class{constructor(e){this.namespace=e.namespace,this.version=e.version,this.migrations=e.migrations??{},this.storage=e.storage??It()}key(e){return`mwg-save:${this.namespace}:${e}`}save(e,t,n){let r={meta:{version:this.version,savedAt:Date.now(),preview:n},state:t};this.storage.write(this.key(e),JSON.stringify(r))}load(e){let t=this.storage.read(this.key(e));if(!t)return null;try{let n=Pt(t,`save slot "${e}"`),r=n.state;for(let e=n.meta.version;e<this.version;e++)r=(this.migrations[e]??(e=>e))(r);return{meta:{...n.meta,version:this.version},state:r}}catch{return null}}importExternal(e,t,n,r){Et(t);let i=n(t);for(let e=0;e<this.version;e++)i=(this.migrations[e]??(e=>e))(i);let a={meta:{version:this.version,savedAt:Date.now(),preview:r},state:i};this.storage.write(this.key(e),JSON.stringify(a))}delete(e){this.storage.remove(this.key(e))}exportSlot(e,t){let n=this.storage.read(this.key(e));return n===null?null:t?xt(n,t):n}importSlot(e,t,n){Et(t);let r=Pt(n?St(t,n):t,`SaveSystem.importSlot: payload`),i=r.state;for(let e=r.meta.version;e<this.version;e++)i=(this.migrations[e]??(e=>e))(i);let a={meta:{...r.meta,version:this.version},state:i};this.storage.write(this.key(e),JSON.stringify(a))}list(){let e=`mwg-save:${this.namespace}:`,t=[];for(let n of this.storage.keys()){if(!n.startsWith(e))continue;let r=this.storage.read(n);if(r)try{t.push({slot:n.slice(e.length),meta:Pt(r,n).meta})}catch{}}return t}};function Rt(e,t=`value`){return zt(e,t,new Set)}function zt(e,t,n){if(typeof e==`function`||typeof e==`symbol`)return t;if(typeof e!=`object`||!e||n.has(e)||(n.add(e),Bt(e)))return null;let r=[];if(e instanceof Map)for(let[n,i]of e)r.push([`${t}.get(${JSON.stringify(n)??String(n)})`,i]);else if(e instanceof Set)for(let n of e)r.push([`${t}.item`,n]);else if(Array.isArray(e))e.forEach((e,n)=>r.push([`${t}[${n}]`,e]));else for(let[n,i]of Object.entries(e))r.push([`${t}.${n}`,i]);for(let[e,t]of r){let r=zt(t,e,n);if(r)return r}return t}function Bt(e){try{return structuredClone(e),!0}catch{return!1}}function Vt(e,t){try{return structuredClone(e)}catch(n){let r=Rt(e,t)??t;throw TypeError(`${r} cannot be structured-cloned (${n instanceof Error?n.message:String(n)}). Journals, snapshots and undo checkpoints copy their contents with structuredClone, so they must hold plain data: refer to a live object (an actor, anything carrying callbacks such as a ReactionTable) by its id and resolve it inside the rule.`,{cause:n})}}var Ht=class{constructor(e,t={}){this._state=Vt(e,`initial state`),this.version=t.version??1,this.migrations=t.migrations??{},this.extensions=t.extensions??new Ut}get state(){return structuredClone(this._state)}set(e){this._state=Vt(e,`state`)}update(e){let t=e(this.state);return this.set(t),this.state}snapshot(){let e=this.extensions.snapshot();return{version:this.version,state:this.state,extensions:e.extensions,extensionVersions:e.versions}}restore(e,t={}){if(!Number.isSafeInteger(e.version)||e.version<1)throw Error(`canonical state snapshot version must be a positive integer`);if(e.version>this.version)throw Error(`canonical state snapshot version ${e.version} is newer than ${this.version}`);let n=this.snapshot();try{let n=Vt(e.state,`snapshot.state`);for(let t=e.version;t<this.version;t++){let e=this.migrations[t+1];if(!e)throw Error(`missing canonical state migration to version ${t+1}`);n=e(n)}return this._state=Vt(n,`migrated state`),this.extensions.restore({extensions:e.extensions,versions:e.extensionVersions},t)}catch(e){throw this._state=structuredClone(n.state),this.extensions.restore({extensions:n.extensions,versions:n.extensionVersions},{missing:`keep`}),e}}transaction(e){let t=this.snapshot();try{return e(this)}catch(e){throw this.restore(t),e}}},Ut=class{constructor(){this.extensions=new Map}register(e){if(!/^[A-Za-z_][A-Za-z0-9_.-]*$/.test(e.id))throw Error(`invalid state extension id: ${e.id}`);if(this.extensions.has(e.id))throw Error(`state extension already registered: ${e.id}`);return this.extensions.set(e.id,{id:e.id,capture:()=>e.capture(),restore:t=>e.restore(t),version:e.version??1,migrations:e.migrations,reset:e.reset,remove:e.remove}),()=>this.extensions.delete(e.id)}snapshot(){let e={};for(let[t,n]of this.extensions)e[t]=Vt(n.capture(),`extension ${t}`);let t={};for(let[e,n]of this.extensions)t[e]=n.version??1;return{extensions:e,versions:t}}restore(e,t={}){let n=this.snapshot(),r=[];try{for(let[n,i]of this.extensions){if(!(n in e.extensions)){t.missing===`reset`?(i.reset&&i.reset(),r.push({extension:n,from:0,to:i.version??1,status:`reset`})):t.missing===`remove`&&(i.remove&&i.remove(),r.push({extension:n,from:0,to:i.version??1,status:`removed`}));continue}let a=e.versions?.[n]??1,o=i.version??1,s=Vt(e.extensions[n],`snapshot.extensions.${n}`);for(let e=a;e<o;e++){let t=i.migrations?.[e+1];if(!t)throw Error(`missing state migration for ${n} to version ${e+1}`);s=t(s)}i.restore(Vt(s,`migrated extension ${n}`)),r.push({extension:n,from:a,to:o,status:a===o?`unchanged`:`migrated`})}}catch(e){for(let[e,t]of this.extensions)e in n.extensions&&t.restore(structuredClone(n.extensions[e]));throw e}for(let e of r)t.onDiagnostic?.(e);return r}transaction(e){let t=this.snapshot();try{return e()}catch(e){throw this.restore(t),e}}},Wt=new Set([`localhost`,`127.0.0.1`,`[::1]`]);function Gt(e,t,n=!1){if(n||!/^[a-z][a-z0-9+.-]*:/i.test(e))return;let r=new URL(e);if(r.protocol!==`https:`&&r.protocol!==`wss:`&&(r.protocol!==`http:`&&r.protocol!==`ws:`||!Wt.has(r.hostname)))throw Error(`${t} url must use https: or wss: (got ${r.protocol}//${r.host}); pass allowInsecure: true to accept it`)}var Kt=class{constructor(e,t){if(!e.endpoint)throw Error(`${t} endpoint is required`);if(Gt(e.endpoint,`${t} endpoint`,e.allowInsecure),this.endpoint=e.endpoint,this.label=t,this.maxResponseBytes=e.maxResponseBytes??10485760,this.timeoutMs=e.timeoutMs??1e4,!(this.timeoutMs>0))throw Error(`${t} timeout must be positive`);if(this.fetchFn=e.fetch??globalThis.fetch,!this.fetchFn)throw Error(`fetch is unavailable; provide ${t}Options.fetch`)}async withTimeout(e){let t=new AbortController,n=setTimeout(()=>t.abort(),this.timeoutMs);try{return await e(t.signal)}finally{clearTimeout(n)}}async readText(e){let t=this.maxResponseBytes,n=Number(e.headers?.get?.(`content-length`));if(n>t)throw Error(`${this.label} response exceeds the ${t}-byte limit (${n} bytes)`);if(!e.body){let n=await e.text();return Et(n,{maxBytes:t}),n}let r=e.body.getReader(),i=[],a=0;for(;;){let{done:e,value:n}=await r.read();if(e)break;if(a+=n.byteLength,a>t)throw await r.cancel(),Error(`${this.label} response exceeds the ${t}-byte limit`);i.push(n)}let o=new Uint8Array(a),s=0;for(let e of i)o.set(e,s),s+=e.byteLength;return new TextDecoder().decode(o)}async readJson(e,t){return Mt(await this.readText(e),{maxBytes:this.maxResponseBytes,label:`${this.label} ${t}`})}},qt=class extends Kt{constructor(e){super(e,`save sync`)}async upload(e,t){if(!e)throw Error(`save sync slot name is required`);if(!t)throw Error(`save sync payload cannot be empty`);return this.withTimeout(async n=>{let r=await this.fetchFn(this.slotUrl(e),{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({payload:t}),signal:n});if(!r.ok)throw Error(`save upload failed with HTTP ${r.status}`);return{ok:!0,status:r.status}})}async download(e){if(!e)throw Error(`save sync slot name is required`);return this.withTimeout(async t=>{let n=await this.fetchFn(this.slotUrl(e),{signal:t});if(!n.ok)throw Error(`save download failed with HTTP ${n.status}`);let r=await this.readJson(n,`download`);if(typeof r?.payload!=`string`)throw Error(`save download response missing a string payload`);return r.payload})}async list(){return this.withTimeout(async e=>{let t=await this.fetchFn(this.endpoint,{signal:e});if(!t.ok)throw Error(`save slot list failed with HTTP ${t.status}`);let n=await this.readJson(t,`slot list`);if(!Array.isArray(n?.slots)||!n.slots.every(e=>typeof e==`string`))throw Error(`save slot list response missing a string array of slots`);return n.slots})}slotUrl(e){let t=this.endpoint.includes(`?`)?`&`:`?`;return`${this.endpoint}${t}slot=${encodeURIComponent(e)}`}},Jt=1,Yt=class{constructor(e){if(this.onWelcome=new p,this.onTick=new p,this.onReject=new p,this.onDesync=new p,this.onClose=new p,this.onProtocolError=new p,this.socket=null,this._id=null,!e.url)throw Error(`lockstep client url is required`);Gt(e.url,`lockstep client`,e.allowInsecure),this.url=e.url,this.createSocket=e.create??(e=>new WebSocket(e)),this.validateInput=e.validateInput,this.maxMessageBytes=e.maxMessageBytes??1048576}get id(){return this._id}get connected(){return this.socket!==null&&this.socket.readyState===Jt}connect(){if(this.socket)throw Error(`lockstep client is already connected`);let e=this.createSocket(this.url);e.onmessage=e=>this.handleMessage(e.data),e.onclose=()=>{this.socket=null,this._id=null,this.onClose.dispatch()},this.socket=e}submitInput(e,t){if(!this.connected)return;let n=this.validateInput?.(e);n===!1||typeof n==`string`?this.onReject.dispatch({reason:n===!1?`input rejected by client validation`:n}):this.socket?.send(JSON.stringify({type:`input`,payload:e,...t===void 0?{}:{checksum:t}}))}close(){this.socket?.close()}handleMessage(e){let t;try{if(typeof e!=`string`)throw Error(`lockstep message is not text`);t=Zt(Mt(e,{maxBytes:this.maxMessageBytes,label:`lockstep message`}))}catch(e){this.onProtocolError.dispatch({reason:e instanceof Error?e.message:String(e)});return}if(t.type===`welcome`)this._id=t.id,this.onWelcome.dispatch({id:t.id,...t.seed===void 0?{}:{seed:t.seed},...t.initialState===void 0?{}:{initialState:t.initialState}});else if(t.type===`tick`){let e={tick:t.tick,inputs:t.inputs,...t.checksums===void 0?{}:{checksums:t.checksums}};if(this.onTick.dispatch(e),t.checksums){let e=Object.values(t.checksums);e.some(t=>t!==e[0])&&this.onDesync.dispatch({tick:t.tick,checksums:t.checksums})}}else this.onReject.dispatch({reason:t.reason??`input rejected by server`})}},Xt=e=>typeof e==`object`&&!!e&&!Array.isArray(e);function Zt(e){if(!Xt(e)||typeof e.type!=`string`)throw Error(`lockstep message has no type`);switch(e.type){case`welcome`:if(typeof e.id!=`string`||e.id===``)throw Error(`welcome needs a string id`);if(e.seed!==void 0&&!Number.isSafeInteger(e.seed))throw Error(`welcome seed must be an integer`);return e;case`tick`:if(!Number.isSafeInteger(e.tick)||e.tick<0)throw Error(`tick needs a non-negative integer tick`);if(!Xt(e.inputs))throw Error(`tick needs an inputs object`);if(e.checksums!==void 0&&(!Xt(e.checksums)||Object.values(e.checksums).some(e=>typeof e!=`number`)))throw Error(`tick checksums must map ids to numbers`);return e;case`rejected`:if(e.reason!==void 0&&typeof e.reason!=`string`)throw Error(`rejected reason must be a string`);return e;default:throw Error(`unknown lockstep message type "${e.type}"`)}}var Qt=class{constructor(e,t){this.storage=e,this.key=t}read(e){let t=this.storage.read(this.key);return t?Mt(t,{label:`stored value "${this.key}"`}):e}write(e){this.storage.write(this.key,JSON.stringify(e))}remove(){this.storage.remove(this.key)}},$t=class{constructor(e){this.store=new Qt(e.storage??It(),`mwg-runs:${e.namespace}`),this.limit=e.limit}readAll(){return this.store.read([])}record(e){let t=this.readAll(),n={id:`${Date.now()}-${t.length}`,endedAt:Date.now(),summary:e};return t.push(n),this.limit!==void 0&&t.length>this.limit&&t.splice(0,t.length-this.limit),this.store.write(t),n}all(){return this.readAll()}ranked(e,t=`desc`){let n=this.readAll(),r=t===`desc`?-1:1;return n.sort((t,n)=>r*(e(t.summary)-e(n.summary)))}clear(){this.store.remove()}},en=class{constructor(e){this.store=new Qt(e.storage??It(),`mwg-stats:${e.namespace}`),this.initial=e.initial,this.combine=e.combine}get(){return this.store.read(this.initial)}record(e){let t=this.combine(this.get(),e);return this.store.write(t),t}reset(){this.store.remove()}},tn=class extends Kt{constructor(e){super(e,`telemetry`),this.consented=!1,this.maxStringLength=e.maxStringLength??256,this.maxProperties=e.maxProperties??32,this.allowed=e.allowedProperties?new Set(e.allowedProperties):null}get hasConsent(){return this.consented}setConsent(e){this.consented=e}async send(e){if(!e.name)throw Error(`telemetry event needs a name`);return this.consented?this.withTimeout(async t=>{let n=await this.fetchFn(this.endpoint,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify(this.bounded(e)),signal:t});if(!n.ok)throw Error(`telemetry request failed with HTTP ${n.status}`);return{ok:!0,status:n.status}}):null}bounded(e){let t=e=>e.length>this.maxStringLength?e.slice(0,this.maxStringLength):e;if(!e.properties)return{name:t(e.name)};let n={},r=0;for(let[i,a]of Object.entries(e.properties))if(!this.allowed||this.allowed.has(i)){if(r++>=this.maxProperties)break;n[t(i)]=typeof a==`string`?t(a):a}return{name:t(e.name),properties:n}}},nn=class extends Kt{constructor(e){super(e,`news`)}async fetchItems(){return this.withTimeout(async e=>{let t=await this.fetchFn(this.endpoint,{signal:e});if(!t.ok)throw Error(`news request failed with HTTP ${t.status}`);let n=await this.readJson(t,`response`);if(!Array.isArray(n))throw Error(`news response was not an array`);return n.map((e,t)=>rn(e,t))})}};function rn(e,t){if(typeof e!=`object`||!e)throw Error(`news item ${t} was not an object`);let n=e;if(typeof n.id!=`string`||!n.id)throw Error(`news item ${t} is missing a string id`);if(typeof n.title!=`string`)throw Error(`news item ${t} is missing a string title`);if(typeof n.body!=`string`)throw Error(`news item ${t} is missing a string body`);return{id:n.id,title:n.title,body:n.body,publishedAt:typeof n.publishedAt==`number`?n.publishedAt:void 0}}var an=class{constructor(e){this.store=new Qt(e.storage??It(),`mwg-news-seen:${e.namespace}`)}readSeen(){return new Set(this.store.read([]))}isSeen(e){return this.readSeen().has(e)}markSeen(e){let t=this.readSeen();t.add(e),this.store.write([...t])}unseen(e){let t=this.readSeen();return e.filter(e=>!t.has(e.id))}},on=class{constructor(e,t={}){if(!e)throw Error(`a collection needs a name`);this.prefix=`mwg-db:${t.namespace??`default`}:${e}:`,this.storage=t.storage??It()}get size(){return this.keys().length}all(){return this.keys().map(e=>this.read(e))}get(e){let t=this.storage.read(this.prefix+e);return t===null?void 0:this.parse(t,this.prefix+e)}put(e){if(typeof e!=`object`||!e||Array.isArray(e)||typeof e.id!=`string`||e.id===``)throw Error(`a record must be an object with a string id`);this.storage.write(this.prefix+e.id,JSON.stringify(e))}remove(e){this.storage.remove(this.prefix+e)}where(e){return this.all().filter(e)}clear(){for(let e of this.keys())this.storage.remove(e)}keys(){return this.storage.keys().filter(e=>e.startsWith(this.prefix))}read(e){return this.parse(this.storage.read(e),e)}parse(e,t){return Mt(e,{label:`record "${t}"`})}},sn=class{constructor(e,t,n){if(this.recorded=[],this.fromFrame=n?.fromFrame??0,!Number.isInteger(this.fromFrame)||this.fromFrame<0)throw RangeError(`a resumed recorder needs a non-negative fromFrame`);this.frameStamp=this.fromFrame,this.actions=e,this.frames=t,this.onAction=e=>{this.recorded.push({frame:this.frameStamp,action:e})},this.onFrame=()=>{this.frameStamp++},e.add(this.onAction),t.add(this.onFrame)}get events(){return this.recorded}get frame(){return this.frameStamp}toJSON(){return this.recorded.map(e=>({frame:e.frame,action:e.action}))}stop(){this.actions.remove(this.onAction),this.frames.remove(this.onFrame)}},cn=class{constructor(e,t,n,r){if(this.events=e,this.dispatch=t,this.frames=n,this.fromFrame=r?.fromFrame??0,!Number.isInteger(this.fromFrame)||this.fromFrame<0)throw RangeError(`a resumed player needs a non-negative fromFrame`);this.index=this.events.findIndex(e=>e.frame>=this.fromFrame),this.index<0&&(this.index=this.events.length),this.frame=this.fromFrame,this.onFrame=()=>this.pump(),n.add(this.onFrame)}get done(){return this.index>=this.events.length}stop(){this.frames.remove(this.onFrame)}pump(){for(;this.index<this.events.length&&this.events[this.index].frame<=this.frame;)this.dispatch(this.events[this.index].action),this.index++;this.frame++}};function ln(e){return JSON.stringify(e)}function un(e){return dn(JSON.parse(e))}function dn(e){if(!Array.isArray(e))throw Error(`a replay must be an array of {frame, action}`);return e.map(e=>{if(typeof e!=`object`||!e||!Number.isInteger(e.frame)||e.frame<0||typeof e.action!=`string`)throw Error(`a replay entry must be {frame: a non-negative integer, action: a string}`);return{frame:e.frame,action:e.action}})}function fn(e,t){if(typeof t!=`string`||!t.length)throw TypeError(`a replay file needs a non-empty ${e}`);return t}function pn(e){let t=e;if(!Number.isInteger(t)||t<0||t>4294967295)throw RangeError(`a replay seed must be uint32`);return t}function mn(e,t){let n={format:`mwg-replay`,version:1,framework:fn(`framework`,t.framework),game:fn(`game`,t.game),events:dn(e)};if(t.seed!==void 0&&(n.seed=pn(t.seed)),t.recordedAt!==void 0){if(typeof t.recordedAt!=`number`||!Number.isFinite(t.recordedAt))throw TypeError(`recordedAt must be a finite number`);n.recordedAt=t.recordedAt}return JSON.stringify(n)}function hn(e,t){let n=Mt(e,{label:`replay file`});if(typeof n!=`object`||!n||Array.isArray(n))throw Error(`a replay file must be a JSON object`);if(n.format!==`mwg-replay`)throw Error(`not a replay file: expected format "mwg-replay", found ${JSON.stringify(n.format)}`);if(n.version!==1)throw Error(`replay file version ${JSON.stringify(n.version)} is not supported (this build reads 1)`);if(n.game!==t.game)throw Error(`a replay of ${JSON.stringify(n.game)} cannot be imported into ${JSON.stringify(t.game)}`);if(n.framework!==t.framework)throw Error(`a replay recorded against mwg ${JSON.stringify(n.framework)} cannot be imported into ${JSON.stringify(t.framework)}`);let r=dn(n.events);return{format:`mwg-replay`,version:1,framework:n.framework,game:n.game,...n.seed===void 0?{}:{seed:pn(n.seed)},...n.recordedAt===void 0?{}:{recordedAt:n.recordedAt},events:r}}var gn=class{constructor(e){if(typeof e.namespace!=`string`||!e.namespace.length)throw TypeError(`a last-run store needs a non-empty namespace`);let t=e.maxEvents??1e4;if(!Number.isInteger(t)||t<1||t>1e6)throw RangeError(`last-run maxEvents must be in 1..1000000`);this.maxEvents=t,this.store=new Qt(e.storage??It(),`mwg-last-run:${e.namespace}`)}keep(e,t){let n=dn(e);n.length>this.maxEvents&&(n=n.slice(n.length-this.maxEvents));let r=t===void 0?{version:1,events:n}:{version:1,seed:pn(t),events:n};Et(JSON.stringify(r)),this.store.write(r)}load(){let e=this.store.read(null);if(e===null)return null;if(e?.version!==1)throw Error(`the kept last-run recording is not version 1; clear() it and record again`);return{version:1,...e.seed===void 0?{}:{seed:pn(e.seed)},events:dn(e.events)}}clear(){this.store.remove()}};function _n(e,t){return{version:1,frame:e.frame,events:e.toJSON(),state:t}}function vn(e,t,n){if(typeof e!=`object`||!e||e.version!==1)throw Error(`a run checkpoint must be { version: 1, frame, events, state }`);return new cn(dn(e.events),t,n,{fromFrame:e.frame})}function yn(e,t){if(typeof document>`u`)throw Error(`downloadReplayFile needs a browser document`);let n=document.createElement(`a`);n.href=URL.createObjectURL(new Blob([e],{type:`application/json`})),n.download=t,document.body.appendChild(n),n.click(),n.remove(),setTimeout(()=>URL.revokeObjectURL(n.href),0)}async function bn(e){return await e.text()}async function xn(e=`.json`){if(typeof document>`u`)throw Error(`pickReplayFile needs a browser document`);return await new Promise(t=>{let n=document.createElement(`input`);n.type=`file`,n.accept=e,n.style.display=`none`;let r=!1,i=e=>{r||(r=!0,n.remove(),t(e))};n.addEventListener(`change`,()=>i(n.files?.[0]??null)),n.addEventListener(`cancel`,()=>i(null)),document.body.appendChild(n),n.click()})}var Sn=class e{constructor(){this.entries=[],this.nextSequence=0}append(e,t=[]){let n={sequence:this.nextSequence,action:Vt(e,`action`),events:Vt([...t],`events`)};return this.nextSequence++,this.entries.push(n),structuredClone(n)}get size(){return this.entries.length}get all(){return structuredClone(this.entries)}mark(){return this.nextSequence}since(e){return structuredClone(this.entries.filter(t=>t.sequence>=e))}truncate(e){this.entries=this.entries.filter(t=>t.sequence<e),this.nextSequence=e}toJSON(){return this.all}replace(t){let n=e.fromJSON(t);this.entries=n.entries,this.nextSequence=n.nextSequence}static fromJSON(t){let n=new e,r=0;for(let e of t){if(!Number.isSafeInteger(e.sequence)||e.sequence!==r)throw Error(`action journal sequences must be contiguous from zero`);n.entries.push(structuredClone({sequence:e.sequence,action:e.action,events:[...e.events]})),r++}return n.nextSequence=r,n}};function Cn(e){if(e.criteria)return e.criteria;if(e.counter===void 0||e.target===void 0)throw Error(`achievement "${e.id}" needs either counter/target or criteria`);return[{counter:e.counter,target:e.target}]}var wn=class e{constructor(){this.definitions=new Map,this.counts=new Map,this.fresh=[]}define(e){this.definitions.set(e.id,e)}count(e){return this.counts.get(e)??0}increment(e,t=1){let n=this.count(e);this.counts.set(e,n+t);let r=[];for(let t of this.definitions.values()){let i=Cn(t);i.some(t=>t.counter===e)&&(i.every(t=>t.counter===e?n>=t.target:this.count(t.counter)>=t.target)||i.every(e=>this.count(e.counter)>=e.target)&&(r.push(t.id),this.fresh.push(t.id)))}return r}unlocked(e){let t=this.definitions.get(e);if(!t)throw Error(`no such achievement: "${e}"`);return Cn(t).every(e=>this.count(e.counter)>=e.target)}progress(e){let t=this.definitions.get(e);if(!t)throw Error(`no such achievement: "${e}"`);let n=Cn(t);if(n.length!==1)throw Error(`achievement "${e}" has several criteria - use subProgress`);return{count:this.count(n[0].counter),target:n[0].target}}subProgress(e){let t=this.definitions.get(e);if(!t)throw Error(`no such achievement: "${e}"`);return Cn(t).map(e=>{let t=this.count(e.counter);return{counter:e.counter,count:t,target:e.target,met:t>=e.target}})}drainNew(){let e=this.fresh;return this.fresh=[],e}toJSON(){return{counts:[...this.counts]}}static fromJSON(t,n){let r=new e;for(let e of t)r.define(e);for(let[e,t]of n.counts)r.counts.set(e,t);return r}},Tn=class e{constructor(e=[]){this.active=new Set,this.spent=new Set,this.rules=[...e]}add(e){this.rules.push(e)}remove(e){this.rules=this.rules.filter(t=>t.id!==e),this.active.delete(e)}check(e){let t=[];for(let n of this.rules){if(n.once&&this.spent.has(n.id))continue;let r=n.when(e);r&&!this.active.has(n.id)&&(n.action(e),t.push(n.id),n.once&&this.spent.add(n.id)),r?this.active.add(n.id):this.active.delete(n.id)}return t}isActive(e){return this.active.has(e)}reset(){this.active.clear(),this.spent.clear()}toJSON(){return{active:[...this.active],spent:[...this.spent]}}static fromJSON(t,n){let r=new e(t);for(let e of n.active)r.active.add(e);for(let e of n.spent)r.spent.add(e);return r}},En=class{constructor(e={}){let t=e.storage??It(),n=`mwg-session:${e.namespace??`default`}`,r=Number(t.read(n)??`0`);this.launches=(Number.isFinite(r)?r:0)+1,t.write(n,String(this.launches))}};function Dn(e,t,n){return e<t?t:e>n?n:e}function On(){return{musicVolume:1,sfxVolume:1,muted:!1,zoom:1,bindings:Pn(Ae),custom:{}}}function kn(e){return e.muted?0:e.musicVolume}function An(e){return e.muted?0:e.sfxVolume}var jn=class{constructor(e={}){this.storage=e.storage??It(),this.key=`mwg-settings:${e.namespace??`default`}`,this.value=zn(this.storage,this.key),Le(this.value.bindings)}get current(){return{...this.value,bindings:Pn(this.value.bindings),custom:{...this.value.custom}}}getCustom(e,t){return this.value.custom[e]??t}setCustom(e,t){this.value.custom[e]=t,Bn(this.storage,this.key,this.value)}setMusicVolume(e){this.update({musicVolume:e})}setSfxVolume(e){this.update({sfxVolume:e})}setMuted(e){this.update({muted:e})}setZoom(e){this.update({zoom:e})}setBindings(e){this.update({bindings:e})}update(e){e.musicVolume!==void 0&&(this.value.musicVolume=Mn(e.musicVolume)),e.sfxVolume!==void 0&&(this.value.sfxVolume=Mn(e.sfxVolume)),e.muted!==void 0&&(this.value.muted=e.muted),e.zoom!==void 0&&(this.value.zoom=Nn(e.zoom)),e.bindings!==void 0&&(this.value.bindings=Fn(e.bindings),Le(this.value.bindings)),e.custom!==void 0&&(this.value.custom=Ln(e.custom)),Bn(this.storage,this.key,this.value)}applyBindings(){Le(this.value.bindings)}reset(){this.value=On(),Le(this.value.bindings),Bn(this.storage,this.key,this.value)}};function Mn(e){return Number.isFinite(e)?Dn(e,0,1):1}function Nn(e){return Number.isFinite(e)?Math.max(.01,e):1}function Pn(e){let t={};for(let[n,r]of Object.entries(e))t[n]=[...r];return t}function Fn(e){if(typeof e!=`object`||!e||Array.isArray(e))return Pn(Ae);let t={};for(let[n,r]of Object.entries(e)){if(!Array.isArray(r))continue;let e=r.filter(e=>typeof e==`string`);e.length>0&&(t[n]=e)}return Object.keys(t).length>0?t:Pn(Ae)}function In(e){let t=typeof e;return t===`string`||t===`number`||t===`boolean`}function Ln(e){if(typeof e!=`object`||!e||Array.isArray(e))return{};let t={};for(let[n,r]of Object.entries(e))In(r)&&(t[n]=r);return t}function Rn(e){let t=On();if(typeof e!=`object`||!e||Array.isArray(e))return t;let n=e;return{musicVolume:typeof n.musicVolume==`number`?Mn(n.musicVolume):t.musicVolume,sfxVolume:typeof n.sfxVolume==`number`?Mn(n.sfxVolume):t.sfxVolume,muted:typeof n.muted==`boolean`?n.muted:t.muted,zoom:typeof n.zoom==`number`?Nn(n.zoom):t.zoom,bindings:Fn(n.bindings),custom:Ln(n.custom)}}function zn(e,t){let n=e.read(t);if(!n)return On();try{return Rn(Mt(n,{label:`settings`}))}catch{return On()}}function Bn(e,t,n){e.write(t,JSON.stringify(n))}var Vn=class extends Kt{constructor(e){super(e,`feedback`)}async submit(e){if(!e.message?.trim())throw Error(`feedback message cannot be empty`);return this.withTimeout(async t=>{let n=await this.fetchFn(this.endpoint,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify(e),signal:t});if(!n.ok)throw Error(`feedback request failed with HTTP ${n.status}`);return{ok:!0,status:n.status}})}},Hn=class{constructor(e){this.schedule=[],this.cursor=0,this.elapsed=0,this.startedWaves=new Set,this.completed=!1,this.onSpawn=e.onSpawn,this.onWaveStart=e.onWaveStart,this.onComplete=e.onComplete,e.waves.forEach((e,t)=>{let n=e.entries.reduce((e,t)=>e+t.count,0),r=e.duration??0,i=0;for(let a of e.entries)for(let o=0;o<a.count;o++,i++){let o=n>1&&r>0?i/(n-1)*r:0;this.schedule.push({time:e.delay+o,kind:a.kind,waveIndex:t})}}),this.schedule.sort((e,t)=>e.time-t.time),this.schedule.length===0&&(this.completed=!0,this.onComplete?.())}update(e){if(!this.completed){for(this.elapsed+=e;this.cursor<this.schedule.length&&this.schedule[this.cursor].time<=this.elapsed;){let e=this.schedule[this.cursor];this.startedWaves.has(e.waveIndex)||(this.startedWaves.add(e.waveIndex),this.onWaveStart?.(e.waveIndex)),this.onSpawn(e.kind),this.cursor++}this.cursor>=this.schedule.length&&(this.completed=!0,this.onComplete?.())}}get isComplete(){return this.completed}};function Un(e,t){let n=e,r=t-(e-(e&1))/2;return{x:n,y:-n-r,z:r}}function Wn(e){return{x:e.x,y:e.z+(e.x-(e.x&1))/2}}var Gn=[{x:1,y:-1,z:0},{x:1,y:0,z:-1},{x:0,y:1,z:-1},{x:-1,y:1,z:0},{x:-1,y:0,z:1},{x:0,y:-1,z:1}];function Kn(e,t){let n=Un(e,t);return Gn.map(e=>Wn({x:n.x+e.x,y:n.y+e.y,z:n.z+e.z}))}function qn(e,t){let n=Un(e.x,e.y),r=Un(t.x,t.y);return Math.max(Math.abs(n.x-r.x),Math.abs(n.y-r.y),Math.abs(n.z-r.z))}function Jn(e){let t=Math.round(e.x),n=Math.round(e.y),r=Math.round(e.z),i=Math.abs(t-e.x),a=Math.abs(n-e.y),o=Math.abs(r-e.z);return i>a&&i>o?t=-n-r:a>o?n=-t-r:r=-t-n,{x:t,y:n,z:r}}function Yn(e,t){let n=qn(e,t);if(n===0)return[{x:e.x,y:e.y}];let r=Un(e.x,e.y),i=Un(t.x,t.y),a=[];for(let e=0;e<=n;e++){let t=e/n;a.push(Wn(Jn({x:r.x+(i.x-r.x)*t,y:r.y+(i.y-r.y)*t,z:r.z+(i.z-r.z)*t})))}return a}function Xn(e,t){let n=Un(e.x,e.y),r=[];for(let e=-t;e<=t;e++)for(let i=Math.max(-t,-e-t);i<=Math.min(t,-e+t);i++){let t=-e-i;r.push(Wn({x:n.x+e,y:n.y+i,z:n.z+t}))}return r}function Zn(e,t,n,r,i={}){let a=+((i.offset??`odd`)===`odd`);return(i.orientation??`flat-top`)===`pointy-top`?{x:e*n+((t&1)===a?n/2:0)+n/2,y:t*r*.75+r/2}:{x:e*n*.75+n/2,y:t*r+((e&1)===a?r/2:0)+r/2}}function Qn(e,t,n,r,i={}){let a=(i.orientation??`flat-top`)===`pointy-top`,o=a?n:n*.75,s=a?r*.75:r,c=Math.round((e-n/2)/o),l=Math.round((t-r/2)/s),u={x:c,y:l},d=1/0;for(let a=-2;a<=2;a++)for(let o=-2;o<=2;o++){let s={x:c+a,y:l+o},f=Zn(s.x,s.y,n,r,i),p=(f.x-e)**2+(f.y-t)**2;p<d&&(d=p,u=s)}return u}function $n(e,t,n,r){return n>=0&&r>=0&&n<e&&r<t}function er(e,t,n){return n*e+t}function tr(e,t){return t%e}function nr(e,t){return Math.floor(t/e)}function rr(e,t){return`${e},${t}`}function ir(e){let t=/^(-?\d+),(-?\d+)$/.exec(e);return t?{x:Number(t[1]),y:Number(t[2])}:null}var ar=[[0,-1],[1,0],[0,1],[-1,0]],or=[[-1,-1],[0,-1],[1,-1],[-1,0],[1,0],[-1,1],[0,1],[1,1]],sr=[[0,-1],[1,-1],[1,0],[1,1],[0,1],[-1,1],[-1,0],[-1,-1]];function cr(e,t){return Math.hypot(e.x-t.x,e.y-t.y)}function lr(e,t){return Math.max(Math.abs(e.x-t.x),Math.abs(e.y-t.y))}function ur(e,t){return Math.abs(e.x-t.x)+Math.abs(e.y-t.y)}function dr(e,t,n){switch(e){case`euclidean`:return cr(t,n);case`manhattan`:return ur(t,n);case`chebyshev`:return lr(t,n);default:throw RangeError(`unknown distance metric: ${e}`)}}var fr=.12,pr=null,mr,hr=new p;function gr(){if(mr===void 0&&(mr=typeof matchMedia==`function`?matchMedia(`(prefers-reduced-motion: reduce)`):null,mr)){let e=()=>{hr.dispatch(vr())};typeof mr.addEventListener==`function`?mr.addEventListener(`change`,e):typeof mr.addListener==`function`&&mr.addListener(e)}return mr}function _r(){return gr()?.matches??!1}function vr(){return pr??_r()}function yr(e){pr=e,hr.dispatch(vr())}function br(e){return gr(),hr.add(e),()=>hr.remove(e)}function xr(e,t=`decorative`){return vr()?t===`meaningful`?Math.min(e,fr):0:e}var Sr=class e{constructor(e,t){this.width=e,this.height=t,this.volume=new Float32Array(e*t)}index(e,t){return e<0||t<0||e>=this.width||t>=this.height?-1:t*this.width+e}volumeAt(e,t){let n=this.index(e,t);return n<0?0:this.volume[n]}total(){let e=0;for(let t=0;t<this.volume.length;t++)e+=this.volume[t];return e}seed(e,t,n){let r=this.index(e,t);r>=0&&(this.volume[r]+=n)}clear(e,t){let n=this.index(e,t);n>=0&&(this.volume[n]=0)}spread(e,t=.25,n=.9){let r=new Float32Array(this.volume.length);for(let n=0;n<this.height;n++)for(let i=0;i<this.width;i++){let a=n*this.width+i,o=this.volume[a];if(o<=0)continue;let s=[];for(let[t,r]of ar){let a=i+t,o=n+r,c=this.index(a,o);c>=0&&e(a,o)&&s.push(c)}if(s.length===0)r[a]+=o;else{let e=o*t/s.length;r[a]+=o-e*s.length;for(let t of s)r[t]+=e}}let i=[];for(let e=0;e<r.length;e++){let t=r[e]*n,a=t<.001?0:t;a===0&&this.volume[e]>0&&i.push({x:e%this.width,y:Math.floor(e/this.width)}),this.volume[e]=a}return i}cellsAbove(e){let t=[];for(let n=0;n<this.height;n++)for(let r=0;r<this.width;r++){let i=this.volume[n*this.width+r];i>=e&&t.push({x:r,y:n,volume:i})}return t}toJSON(){return{width:this.width,height:this.height,volume:[...this.volume]}}static fromJSON(t){let n=new e(t.width,t.height);return n.volume.set(t.volume.slice(0,n.volume.length)),n}},Cr={linear:e=>e,easeInQuad:e=>e*e,easeOutQuad:e=>e*(2-e),easeInOutQuad:e=>e<.5?2*e*e:1-(-2*e+2)**2/2,easeInCubic:e=>e*e*e,easeOutCubic:e=>1-(1-e)**3,easeInOutCubic:e=>e<.5?4*e*e*e:1-(-2*e+2)**3/2},wr=class{constructor(){this.tweens=[]}tween(e,t,n=Cr.linear){let{ease:r=Cr.linear,intent:i=`decorative`,alternate:a}=typeof n==`function`?{ease:n}:n,o=vr()&&a!==void 0,s=o?a:t,c=xr(e,o?`meaningful`:i);return c>0?new Promise(e=>{this.tweens.push({elapsed:0,duration:c,ease:r,apply:s,resolve:e,finishOnReduced:!o&&i===`decorative`})}):(s(1),Promise.resolve())}update(e){if(this.tweens.length!==0)for(let t of[...this.tweens]){if(t.finishOnReduced&&vr()){this.tweens.splice(this.tweens.indexOf(t),1),t.apply(1),t.resolve();continue}t.elapsed+=e;let n=Math.min(1,t.elapsed/t.duration);t.apply(t.ease(n)),n>=1&&(this.tweens.splice(this.tweens.indexOf(t),1),t.resolve())}}get isBusy(){return this.tweens.length>0}clear(){this.tweens=[]}},Tr=class{constructor(e={}){this.history=[],this.cursor=-1,this.limit=Math.max(1,e.limit??50)}push(e){this.history.length=this.cursor+1,this.history.push(e),this.history.length>this.limit&&this.history.shift(),this.cursor=this.history.length-1}get canUndo(){return this.cursor>0}get canRedo(){return this.cursor<this.history.length-1}undo(){return this.canUndo?(this.cursor--,this.history[this.cursor]):null}redo(){return this.canRedo?(this.cursor++,this.history[this.cursor]):null}get current(){return this.cursor>=0?this.history[this.cursor]:null}clear(){this.history=[],this.cursor=-1}};function Er(e,t){if(!Number.isFinite(t.maxCost)||t.maxCost<0)return new Map;let n=t.key(e),r=new Map([[n,{cell:e,cost:0}]]),i=[{cell:e,key:n,cost:0}];for(;i.length>0;){let e=Or(i),n=r.get(e.key);if(n&&e.cost===n.cost&&!t.stop?.(e.cell,e.cost))for(let n of t.neighbors(e.cell)){let a=t.cost(e.cell,n);if(!Number.isFinite(a)||a<0)continue;let o=e.cost+a;if(o>t.maxCost||t.canEnter&&!t.canEnter(n,e.cell,o))continue;let s=t.key(n);if(o>=(r.get(s)?.cost??1/0))continue;let c={cell:n,key:s,cost:o};r.set(s,c),Dr(i,c)}}return r}function Dr(e,t){e.push(t);let n=e.length-1;for(;n>0;){let t=Math.floor((n-1)/2);if(e[t].cost<=e[n].cost)break;[e[t],e[n]]=[e[n],e[t]],n=t}}function Or(e){let t=e[0],n=e.pop();if(!t||!n||e.length===0)return t;e[0]=n;let r=0;for(;;){let t=r*2+1,n=t+1,i=r;if(t<e.length&&e[t].cost<e[i].cost&&(i=t),n<e.length&&e[n].cost<e[i].cost&&(i=n),i===r)break;[e[r],e[i]]=[e[i],e[r]],r=i}return t}var kr=class{constructor(){this.changed=new p,this.tasks=[],this.progress=new Map,this.status_=`idle`,this.current_=null,this.error_=null,this.cancelled=!1}add(e){if(!e.id)throw Error(`load task needs an id`);if(this.tasks.some(t=>t.id===e.id))throw Error(`duplicate load task "${e.id}"`);if(e.weight!==void 0&&!(e.weight>0))throw Error(`load task weight must be positive`);if(this.status_===`loading`)throw Error(`cannot add a task while loading`);return this.tasks.push(e),this.progress.set(e.id,0),this.emit(),this}get snapshot(){let e=this.tasks.reduce((e,t)=>e+(t.weight??1),0),t=this.tasks.reduce((e,t)=>e+(t.weight??1)*(this.progress.get(t.id)??0),0);return{status:this.status_,completed:t,total:e,current:this.current_,error:this.error_}}async start(){if(this.status_===`loading`)throw Error(`load queue is already running`);this.status_=`loading`,this.cancelled=!1,this.error_=null,this.emit();try{for(let e of this.tasks){if(this.cancelled)break;this.current_=e.id,this.emit();let t=this;await e.run({get cancelled(){return t.cancelled},report:t=>this.report(e.id,t)}),this.cancelled||this.report(e.id,1)}this.current_=null,this.status_=this.cancelled?`cancelled`:`ready`,this.emit()}catch(e){throw this.current_=null,this.error_=e,this.status_=`failed`,this.emit(),e}}cancel(){this.status_===`loading`&&(this.cancelled=!0)}retry(){if(this.status_===`loading`)throw Error(`cannot retry while loading`);this.status_=`idle`,this.current_=null,this.error_=null,this.cancelled=!1;for(let e of this.tasks)this.progress.set(e.id,0);this.emit()}report(e,t){if(!Number.isFinite(t))throw Error(`load progress must be finite`);this.progress.set(e,Math.max(0,Math.min(1,t))),this.emit()}emit(){this.changed.dispatch(this.snapshot)}},Ar=class{constructor(){this.entities=new Map,this.ids=new Map,this.sequence=0}add(e,t){let n=this.ids.get(e);if(n!==void 0){if(t!==void 0&&t!==n)throw Error(`entity already has id ${n}`);return n}let r=t;if(r!==void 0){if(r.length===0)throw Error(`entity id must not be empty`);if(this.entities.has(r))throw Error(`entity id already exists: ${r}`)}else do r=`e${this.sequence++}`;while(this.entities.has(r));return this.entities.set(r,e),this.ids.set(e,r),r}get(e){return this.entities.get(e)}idOf(e){return this.ids.get(e)}has(e){return this.entities.has(e)}remove(e){let t=this.entities.get(e);return t!==void 0&&(this.entities.delete(e),this.ids.delete(t),!0)}get size(){return this.entities.size}},jr=class{constructor(e){this.queue=[],this.busy=!1,this.remaining=0,this.play=e.play}enqueue(e){this.queue.push(...e),this.busy||this.advance()}update(e){this.busy&&(this.remaining-=e,this.remaining<=0&&this.advance())}get isBusy(){return this.busy}clear(){this.queue=[],this.busy=!1,this.remaining=0}advance(){let e=this.queue.shift();if(e===void 0){this.busy=!1,this.remaining=0;return}this.busy=!0;let t=this.play(e)||0;t>0?this.remaining=t:this.advance()}},Mr=2166136261,Nr=16777619;function Pr(e,t=!1){if(e==null)return`null`;switch(typeof e){case`boolean`:return e?`true`:`false`;case`number`:return Number.isFinite(e)?String(e):`null`;case`string`:return JSON.stringify(e);case`bigint`:return JSON.stringify(e.toString());case`function`:case`symbol`:return t?`null`:`undefined`}return Array.isArray(e)?`[${e.map(e=>Pr(e,!0)).join(`,`)}]`:`{${Object.entries(e).filter(([,e])=>e!==void 0&&typeof e!=`function`&&typeof e!=`symbol`).sort(([e],[t])=>e<t?-1:+(e>t)).map(([e,t])=>`${JSON.stringify(e)}:${Pr(t)}`).join(`,`)}}`}function Fr(e){let t=Pr(e),n=Mr;for(let e=0;e<t.length;e++)n^=t.charCodeAt(e),n=Math.imul(n,Nr)>>>0;return n>>>0}var Ir=class{constructor(){this.reference=new Map,this.desyncTick=null}observe(e,t){let n=this.reference.get(e);return n===void 0?(this.reference.set(e,t),!0):n===t||(this.desyncTick===null&&(this.desyncTick=e),!1)}get divergent(){return this.desyncTick!==null}get atTick(){return this.desyncTick}reset(){this.reference.clear(),this.desyncTick=null}};function Lr(e,t={}){let n=Br(e);if(n.length===0)return[];let[r,...i]=n,a=t.listDelimiter??`;`,o=t.mapDelimiter??`=`;return i.map((e,n)=>{let i={};return r.forEach((r,s)=>{let c=e[s]??``;c!==``&&(i[r]=Rr(c,t.columns?.[r]??`string`,r,n,a,o))}),i})}function Rr(e,t,n,r,i,a){return zr(e,t,{column:n,rowIndex:r,listDelimiter:i,mapDelimiter:a,error:()=>t===`number`?`CSV row ${r+1}, column "${n}": "${e}" is not a number`:t===`boolean`?`CSV row ${r+1}, column "${n}": "${e}" is not "true" or "false"`:t===`map`?`CSV row ${r+1}, column "${n}": "${e.split(i).map(e=>e.trim()).find(e=>e&&!e.includes(a))??e}" has no "${a}" to split a key from its value`:`CSV row ${r+1}, column "${n}": "${e}" is invalid`})}function zr(e,t,n={}){let r=n.column??`value`,i=n.listDelimiter??`;`,a=n.mapDelimiter??`=`,o=e=>{throw Error(n.error?.(e)??e)};switch(t){case`string`:return e;case`number`:{let t=Number(e);return Number.isFinite(t)||o(`${r} must be a number`),t}case`boolean`:{let t=e.toLowerCase();if(t===`true`)return!0;if(t===`false`)return!1;if(n.acceptYesNo&&t===`yes`)return!0;if(n.acceptYesNo&&t===`no`)return!1;o(`${r} must be true, false${n.acceptYesNo?`, yes, or no`:``}`)}case`list`:return e.split(i).map(e=>e.trim()).filter(e=>e.length>0);case`map`:{let t={};for(let n of e.split(i).map(e=>e.trim()).filter(e=>e.length>0)){let e=n.indexOf(a);e===-1&&o(`${r} map entry must contain ${a}`),t[n.slice(0,e).trim()]=n.slice(e+a.length).trim()}return t}}}function Br(e){let t=[],n=[],r=``,i=!1,a=0;for(;a<e.length;){let o=e[a];if(i){if(o===`"`){if(e[a+1]===`"`){r+=`"`,a+=2;continue}i=!1,a++;continue}r+=o,a++}else o===`"`?(i=!0,a++):o===`,`?(n.push(r),r=``,a++):o===`\r`?a++:o===`
2
2
  `?(n.push(r),t.push(n),n=[],r=``,a++):(r+=o,a++)}return(r.length>0||n.length>0)&&(n.push(r),t.push(n)),t.filter(e=>e.length!==1||e[0]!==``)}function Vr(e){let t=[],n=[],r,i=e=>{if(!n.includes(e)){if(n.length>=2)throw Error(`dialogue text supports at most two speakers for the "-" shorthand; found a third speaker "${e}" after "${n[0]}" and "${n[1]}" were already established`);n.push(e)}};for(let a of e.split(`
3
3
  `)){let e=a.trim();if(e===``)continue;let o=/^@(\S+)(?:\s+(.*))?$/.exec(e);if(o){let[,e,n]=o;i(e),n&&(t.push({text:n.trim(),speaker:e}),r=e)}else if(e.startsWith(`-`)){if(n.length<2)throw Error(`a "-" line needs two established speakers first (write two "@id text" lines before it)`);let i=e.slice(1).trim(),a=n.find(e=>e!==r)??n[0];t.push({text:i,speaker:a}),r=a}else t.push({text:e}),r=void 0}return t}function Hr(e){let t=Ur(e);if(t.length===0)throw Error(`this twee source has no passages - passages start with ":: Name"`);let n={},r,i;for(let e of t)if(e.name===`StoryTitle`)r=e.body.trim()||void 0;else if(e.name===`StoryData`)i=Gr(e.body);else{if(Object.prototype.hasOwnProperty.call(n,e.name))throw Error(`this twee source defines "${e.name}" twice`);n[e.name]=Kr(e.name,e.body)}let a=Object.keys(n);if(a.length===0)throw Error(`this twee source has no story passages, only StoryTitle/StoryData`);if(i!==void 0&&!Object.prototype.hasOwnProperty.call(n,i))throw Error(`StoryData starts at "${i}", which is not a passage in this file`);for(let[e,t]of Object.entries(n))for(let r of t)if(`ask`in r){for(let t of r.choices)if(t.goto!==void 0&&!Object.prototype.hasOwnProperty.call(n,t.goto))throw Error(`passage "${e}" links to "${t.goto}", which is not a passage in this file`)}return{story:n,start:i??a[0],title:r}}function Ur(e){let t=[],n=null,r=[],i=()=>{n!==null&&t.push({name:n,body:r.join(`
4
4
  `)})};for(let t of e.split(`
package/dist/version.d.ts CHANGED
@@ -9,5 +9,5 @@
9
9
  * this from `package.json` automatically, chosen because this project's own build already
10
10
  * has no step before `tsc`/`vite` that this would need to run ahead of.
11
11
  */
12
- export declare const version = "0.36.0";
12
+ export declare const version = "0.36.1";
13
13
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -9,5 +9,5 @@
9
9
  * this from `package.json` automatically, chosen because this project's own build already
10
10
  * has no step before `tsc`/`vite` that this would need to run ahead of.
11
11
  */
12
- export const version = '0.36.0';
12
+ export const version = '0.36.1';
13
13
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datamoc/mw_games",
3
- "version": "0.36.0",
3
+ "version": "0.36.1",
4
4
  "description": "A local-file game framework: renderer-free game logic, drawn through PixiJS in 2D or Babylon.js in 3D, with mobile and desktop packaging.",
5
5
  "license": "MPL-2.0",
6
6
  "repository": {
@@ -417,6 +417,6 @@
417
417
  "types": "./dist/index.d.ts",
418
418
  "unpkg": "./dist/mw_games.global.js",
419
419
  "optionalDependencies": {
420
- "@datamoc/pyroeis": "^0.6.0"
420
+ "@datamoc/pyroeis": "^0.9.0"
421
421
  }
422
422
  }
package/sbom.cdx.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "bomFormat": "CycloneDX",
3
3
  "specVersion": "1.6",
4
- "serialNumber": "urn:uuid:d6286cd5-19f5-5fa4-a70a-a97edfc4b9dd",
4
+ "serialNumber": "urn:uuid:17cd32d6-94ff-58fa-90e1-9bec6171c507",
5
5
  "version": 1,
6
6
  "metadata": {
7
7
  "tools": {
@@ -10,16 +10,16 @@
10
10
  "type": "application",
11
11
  "group": "datamoc",
12
12
  "name": "tools/sbom.mjs",
13
- "version": "0.36.0"
13
+ "version": "0.36.1"
14
14
  }
15
15
  ]
16
16
  },
17
17
  "component": {
18
18
  "type": "library",
19
- "bom-ref": "pkg:npm/%40datamoc/mw_games@0.36.0",
19
+ "bom-ref": "pkg:npm/%40datamoc/mw_games@0.36.1",
20
20
  "name": "@datamoc/mw_games",
21
- "version": "0.36.0",
22
- "purl": "pkg:npm/%40datamoc/mw_games@0.36.0",
21
+ "version": "0.36.1",
22
+ "purl": "pkg:npm/%40datamoc/mw_games@0.36.1",
23
23
  "description": "A local-file game framework: renderer-free game logic, drawn through PixiJS in 2D or Babylon.js in 3D, with mobile and desktop packaging.",
24
24
  "licenses": [
25
25
  {
@@ -159,15 +159,15 @@
159
159
  },
160
160
  {
161
161
  "type": "library",
162
- "bom-ref": "pkg:npm/%40datamoc/pyroeis@0.6.0",
162
+ "bom-ref": "pkg:npm/%40datamoc/pyroeis@0.9.0",
163
163
  "name": "@datamoc/pyroeis",
164
- "version": "0.6.0",
165
- "purl": "pkg:npm/%40datamoc/pyroeis@0.6.0",
164
+ "version": "0.9.0",
165
+ "purl": "pkg:npm/%40datamoc/pyroeis@0.9.0",
166
166
  "scope": "optional",
167
167
  "hashes": [
168
168
  {
169
169
  "alg": "SHA-512",
170
- "content": "cd5087e67203c62762e4da82e249f4c662743c1b3479d0906e1dd774394b909e0819c1fe67aabf79212b035260af10752fcfa5e8259af909ce4b970f33d6c7dc"
170
+ "content": "22c2104fc4df7b87209157a3168afb143eefd46abdb5b4f92c45125244c26265badca578a81b78be2bc6c5f59c29973b5b20dc0b940143aa00016abebed6da22"
171
171
  }
172
172
  ],
173
173
  "licenses": [
@@ -4601,7 +4601,7 @@
4601
4601
  ]
4602
4602
  },
4603
4603
  {
4604
- "ref": "pkg:npm/%40datamoc/mw_games@0.36.0",
4604
+ "ref": "pkg:npm/%40datamoc/mw_games@0.36.1",
4605
4605
  "dependsOn": [
4606
4606
  "pkg:npm/%40babylonjs/core@9.30.0",
4607
4607
  "pkg:npm/%40babylonjs/loaders@9.30.0",
@@ -4609,7 +4609,7 @@
4609
4609
  "pkg:npm/%40capacitor/cli@8.5.3",
4610
4610
  "pkg:npm/%40capacitor/core@8.5.3",
4611
4611
  "pkg:npm/%40capacitor/ios@8.5.3",
4612
- "pkg:npm/%40datamoc/pyroeis@0.6.0",
4612
+ "pkg:npm/%40datamoc/pyroeis@0.9.0",
4613
4613
  "pkg:npm/%40types/node@26.6.5",
4614
4614
  "pkg:npm/%40types/ws@8.18.2",
4615
4615
  "pkg:npm/json5@2.2.3",
@@ -4625,7 +4625,7 @@
4625
4625
  ]
4626
4626
  },
4627
4627
  {
4628
- "ref": "pkg:npm/%40datamoc/pyroeis@0.6.0",
4628
+ "ref": "pkg:npm/%40datamoc/pyroeis@0.9.0",
4629
4629
  "dependsOn": [
4630
4630
  "pkg:npm/readline-sync@1.4.10",
4631
4631
  "pkg:npm/sprintf-js@1.1.3",
@@ -355,17 +355,34 @@ export function findBrowser(kind) {
355
355
  return null;
356
356
  }
357
357
 
358
+ /**
359
+ * Ends a browser and its helper processes. `child.kill()` reaches only the launcher: the
360
+ * zygote, renderers and crashpad handlers survive it holding the output pipes, which keeps
361
+ * the whole Node process (a test run, a CI job) alive until the runner's own timeout. On
362
+ * POSIX the browser is spawned as a process-group leader, so the group goes in one signal.
363
+ */
364
+ function killBrowser(child) {
365
+ try {
366
+ if (process.platform !== 'win32' && child.pid) process.kill(-child.pid, 'SIGKILL');
367
+ else child.kill();
368
+ } catch {
369
+ child.kill();
370
+ }
371
+ child.stdout?.destroy();
372
+ child.stderr?.destroy();
373
+ }
374
+
358
375
  /**
359
376
  * Waits for `match` in the child's output, watching both stdout and stderr: which of the
360
377
  * two a given browser build prints its debugging endpoint on is not something to bet on.
361
378
  * The timeout message carries what was seen, because "no endpoint" alone cannot tell a
362
379
  * slow cold start from a browser that started and refused to open one.
363
380
  */
364
- function waitForEndpoint(child, match, timeoutMs = 30000) {
381
+ function waitForEndpoint(child, match, timeoutMs = 90000) {
365
382
  return new Promise((resolveEndpoint, reject) => {
366
383
  let seen = '';
367
384
  const timer = setTimeout(() => {
368
- child.kill();
385
+ killBrowser(child);
369
386
  reject(
370
387
  new Error(
371
388
  `browser printed no debugging endpoint within ${timeoutMs} ms; output so far:\n${seen || '(none)'}`,
@@ -388,6 +405,7 @@ function waitForEndpoint(child, match, timeoutMs = 30000) {
388
405
  });
389
406
  child.once('exit', () => {
390
407
  clearTimeout(timer);
408
+ killBrowser(child);
391
409
  reject(new Error(`browser exited before printing a debugging endpoint:\n${seen}`));
392
410
  });
393
411
  });
@@ -534,7 +552,7 @@ export async function driveChrome(options = {}) {
534
552
  ...(options.chromeArgs ?? extraArgs),
535
553
  'about:blank',
536
554
  ],
537
- { stdio: ['ignore', 'pipe', 'pipe'] },
555
+ { stdio: ['ignore', 'pipe', 'pipe'], detached: process.platform !== 'win32' },
538
556
  );
539
557
  await waitForEndpoint(child, parseDevToolsEndpoint);
540
558
 
@@ -623,11 +641,7 @@ export async function driveChrome(options = {}) {
623
641
  } catch {
624
642
  // already closed
625
643
  }
626
- child.kill();
627
- //the pipes must go: a browser's surviving helper processes can hold them (and so
628
- //the whole suite) open past the main process's death
629
- child.stdout?.destroy();
630
- child.stderr?.destroy();
644
+ killBrowser(child);
631
645
  if (ownsProfile) {
632
646
  //a browser releases its profile files briefly after the kill, so wait for
633
647
  //the exit and retry: a profile left behind is clutter, not a failure
@@ -676,7 +690,7 @@ export async function driveFirefox(options = {}) {
676
690
  ...(options.firefoxArgs ?? []),
677
691
  'about:blank',
678
692
  ],
679
- { stdio: ['ignore', 'ignore', 'pipe'] },
693
+ { stdio: ['ignore', 'ignore', 'pipe'], detached: process.platform !== 'win32' },
680
694
  );
681
695
  const { endpoint } = await waitForEndpoint(child, parseBidiEndpoint);
682
696
  const socket = await openSocket(endpoint);
@@ -779,7 +793,7 @@ export async function driveFirefox(options = {}) {
779
793
  } catch {
780
794
  // already closed
781
795
  }
782
- child.kill();
796
+ killBrowser(child);
783
797
  },
784
798
  };
785
799
  }