@datamoc/mw_games 0.34.0 → 0.35.0
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 +16 -0
- package/api-surface.json +60 -0
- package/dist/mw_games.global.js +1 -1
- package/dist/mwl/fengari-runtime.d.ts +75 -0
- package/dist/mwl/fengari-runtime.d.ts.map +1 -0
- package/dist/mwl/fengari-runtime.js +534 -0
- package/dist/mwl/fengari-runtime.js.map +1 -0
- package/dist/mwl/fengari.d.ts +18 -0
- package/dist/mwl/fengari.d.ts.map +1 -1
- package/dist/mwl/fengari.js +46 -49
- package/dist/mwl/fengari.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/sbom.cdx.json +12 -12
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,22 @@ the public API may still change between minor versions.
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.35.0] - 2026-10-11
|
|
11
|
+
|
|
12
|
+
A Lua integration host for games that need synchronous API results and resumable dialogue, an offline Lua to JavaScript translator, and the Lua dependency moves to `@datamoc/fengari` 0.5.2.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- `createFengariRuntime` on the optional `mwl/fengari` subpath: synchronous host functions with results, live indexed table bindings, owned Lua callback/table references, embedded `require`/`dofile`, and host-driven coroutines with request values and scene answers. `luaToJs` translates Lua source to readable JavaScript offline (fengari 0.5.0's `lua2js`), flagging untranslatable constructs, for a game leaving Lua scripting. The existing JSON-only `ScriptHost` stays unchanged. Calls, module compilation and coroutine resumes share bounded execution, with a fresh budget for each top-level operation.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- The optional Lua dependency moves to `@datamoc/fengari` `^0.5.2`, whose shipped TypeScript declarations replace the old `@ts-expect-error` import and the hand-written `luaToJs` types, and whose `vm.protect` and `vm.throwLuaError` replace the host-side protected-call code. The host reuses `FengariVm`'s execution guard while retaining text-only compilation, JSON-shaped tables and seeded randomness.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Lua instruction limits now interrupt native table moves and string searches, and cannot be swallowed by `pcall`, `xpcall` message handlers or coroutine resumes. Parsing and execution still share the per-call string/buffer allocation budget. The `ScriptHost` sandbox also drops `string.dump` and fengari's internal `__random`, which scripts could still reach.
|
|
25
|
+
|
|
10
26
|
## [0.34.0] - 2026-10-10
|
|
11
27
|
|
|
12
28
|
Hex grids for the cave, dungeon and overworld generators, and the Lua host moves to the `@datamoc/fengari` 0.3.0 fork (`<const>`, `<close>`, `coroutine.close`).
|
package/api-surface.json
CHANGED
|
@@ -8963,18 +8963,78 @@
|
|
|
8963
8963
|
"subpath": "./mwl",
|
|
8964
8964
|
"declaringModule": "mwl/schema.d.ts"
|
|
8965
8965
|
},
|
|
8966
|
+
{
|
|
8967
|
+
"name": "createFengariRuntime",
|
|
8968
|
+
"kind": "function",
|
|
8969
|
+
"subpath": "./mwl/fengari",
|
|
8970
|
+
"declaringModule": "mwl/fengari-runtime.d.ts"
|
|
8971
|
+
},
|
|
8966
8972
|
{
|
|
8967
8973
|
"name": "createFengariScriptHost",
|
|
8968
8974
|
"kind": "function",
|
|
8969
8975
|
"subpath": "./mwl/fengari",
|
|
8970
8976
|
"declaringModule": "mwl/fengari.d.ts"
|
|
8971
8977
|
},
|
|
8978
|
+
{
|
|
8979
|
+
"name": "FengariCoroutine",
|
|
8980
|
+
"kind": "interface",
|
|
8981
|
+
"subpath": "./mwl/fengari",
|
|
8982
|
+
"declaringModule": "mwl/fengari-runtime.d.ts"
|
|
8983
|
+
},
|
|
8984
|
+
{
|
|
8985
|
+
"name": "FengariReference",
|
|
8986
|
+
"kind": "interface",
|
|
8987
|
+
"subpath": "./mwl/fengari",
|
|
8988
|
+
"declaringModule": "mwl/fengari-runtime.d.ts"
|
|
8989
|
+
},
|
|
8990
|
+
{
|
|
8991
|
+
"name": "FengariRuntime",
|
|
8992
|
+
"kind": "interface",
|
|
8993
|
+
"subpath": "./mwl/fengari",
|
|
8994
|
+
"declaringModule": "mwl/fengari-runtime.d.ts"
|
|
8995
|
+
},
|
|
8996
|
+
{
|
|
8997
|
+
"name": "FengariRuntimeOptions",
|
|
8998
|
+
"kind": "interface",
|
|
8999
|
+
"subpath": "./mwl/fengari",
|
|
9000
|
+
"declaringModule": "mwl/fengari-runtime.d.ts"
|
|
9001
|
+
},
|
|
8972
9002
|
{
|
|
8973
9003
|
"name": "FengariScriptHostOptions",
|
|
8974
9004
|
"kind": "interface",
|
|
8975
9005
|
"subpath": "./mwl/fengari",
|
|
8976
9006
|
"declaringModule": "mwl/fengari.d.ts"
|
|
8977
9007
|
},
|
|
9008
|
+
{
|
|
9009
|
+
"name": "FengariTableBinding",
|
|
9010
|
+
"kind": "interface",
|
|
9011
|
+
"subpath": "./mwl/fengari",
|
|
9012
|
+
"declaringModule": "mwl/fengari-runtime.d.ts"
|
|
9013
|
+
},
|
|
9014
|
+
{
|
|
9015
|
+
"name": "FengariValue",
|
|
9016
|
+
"kind": "type",
|
|
9017
|
+
"subpath": "./mwl/fengari",
|
|
9018
|
+
"declaringModule": "mwl/fengari-runtime.d.ts"
|
|
9019
|
+
},
|
|
9020
|
+
{
|
|
9021
|
+
"name": "luaToJs",
|
|
9022
|
+
"kind": "function",
|
|
9023
|
+
"subpath": "./mwl/fengari",
|
|
9024
|
+
"declaringModule": "mwl/fengari.d.ts"
|
|
9025
|
+
},
|
|
9026
|
+
{
|
|
9027
|
+
"name": "LuaToJsOptions",
|
|
9028
|
+
"kind": "type",
|
|
9029
|
+
"subpath": "./mwl/fengari",
|
|
9030
|
+
"declaringModule": "mwl/fengari.d.ts"
|
|
9031
|
+
},
|
|
9032
|
+
{
|
|
9033
|
+
"name": "LuaToJsResult",
|
|
9034
|
+
"kind": "type",
|
|
9035
|
+
"subpath": "./mwl/fengari",
|
|
9036
|
+
"declaringModule": "mwl/fengari.d.ts"
|
|
9037
|
+
},
|
|
8978
9038
|
{
|
|
8979
9039
|
"name": "AbilityCycle",
|
|
8980
9040
|
"kind": "class",
|
package/dist/mw_games.global.js
CHANGED
|
@@ -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.34.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.35.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===`
|
|
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(`
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { FengariScriptHostOptions } from './fengari.ts';
|
|
2
|
+
/** Values for the integration host; Lua functions and live tables can retain their identity. */
|
|
3
|
+
export type FengariValue = null | boolean | number | string | bigint | Uint8Array | FengariReference | FengariValue[] | {
|
|
4
|
+
readonly [key: string]: FengariValue;
|
|
5
|
+
} | Map<FengariValue, FengariValue>;
|
|
6
|
+
/** An owned Lua registry reference. Use runtime.call/coroutine for functions; dispose when no longer needed. */
|
|
7
|
+
export interface FengariReference {
|
|
8
|
+
readonly type: 'function' | 'table' | 'thread' | 'userdata';
|
|
9
|
+
dispose(): void;
|
|
10
|
+
}
|
|
11
|
+
export interface FengariCoroutine {
|
|
12
|
+
readonly status: 'suspended' | 'running' | 'dead';
|
|
13
|
+
resume(args?: readonly FengariValue[]): {
|
|
14
|
+
readonly done: boolean;
|
|
15
|
+
readonly values: readonly FengariValue[];
|
|
16
|
+
};
|
|
17
|
+
/** Runs pending close handlers with a fresh, bounded execution budget. */
|
|
18
|
+
close(): void;
|
|
19
|
+
}
|
|
20
|
+
/** An empty Lua proxy table whose ordinary indexing always consults the game. */
|
|
21
|
+
export interface FengariTableBinding {
|
|
22
|
+
get(key: string | number): FengariValue;
|
|
23
|
+
set?(key: string | number, value: FengariValue): void;
|
|
24
|
+
keys?(): readonly (string | number)[];
|
|
25
|
+
length?(): number;
|
|
26
|
+
}
|
|
27
|
+
export interface FengariRuntimeOptions extends FengariScriptHostOptions {
|
|
28
|
+
/** Exact module IDs mapped to embedded text sources; never filesystem or network paths. */
|
|
29
|
+
readonly modules?: Readonly<Record<string, string>>;
|
|
30
|
+
/** Optional wall-clock budget per call/resume, host callbacks included. */
|
|
31
|
+
readonly timeLimit?: number;
|
|
32
|
+
}
|
|
33
|
+
export interface FengariRuntime {
|
|
34
|
+
run(source: string, chunkName?: string): FengariValue;
|
|
35
|
+
call(target: string | FengariReference, args?: readonly FengariValue[]): FengariValue;
|
|
36
|
+
get(path: string): FengariValue;
|
|
37
|
+
set(path: string, value: FengariValue): void;
|
|
38
|
+
reference(path: string): FengariReference;
|
|
39
|
+
defineFunction(path: string, callback: (...args: FengariValue[]) => FengariValue | void): void;
|
|
40
|
+
/** The returned values are yielded to the host; resume arguments become the Lua call's results. */
|
|
41
|
+
defineYieldingFunction(path: string, callback: (...args: FengariValue[]) => readonly FengariValue[]): void;
|
|
42
|
+
createTable(binding: FengariTableBinding): FengariReference;
|
|
43
|
+
/** Creates a thread from a function or adopts an existing Lua thread, with one host controller. */
|
|
44
|
+
coroutine(target: string | FengariReference): FengariCoroutine;
|
|
45
|
+
dispose(): void;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Optional Lua integration host for a game-owned API, embedded modules and resumable handlers.
|
|
49
|
+
* The JSON-only ScriptHost stays independent. This host opens no system libraries, compiles
|
|
50
|
+
* text only and bounds calls, compilation and coroutine resumes. Tables read normally become
|
|
51
|
+
* arrays/objects/Maps; reference() and createTable() retain Lua identity explicitly.
|
|
52
|
+
* Host callbacks are trusted synchronous code. Proxy tables follow Lua's ordinary metatable
|
|
53
|
+
* rules: rawget/rawset bypass bindings. Neither heap size nor arbitrary host code is bounded.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* import { createFengariRuntime } from '@datamoc/mw_games/mwl/fengari';
|
|
58
|
+
*
|
|
59
|
+
* const runtime = createFengariRuntime({ modules: { rules: 'return { cost = 4 }' } });
|
|
60
|
+
* let gold = 10;
|
|
61
|
+
* runtime.set('game.side', runtime.createTable({
|
|
62
|
+
* get: (key) => key === 'gold' ? gold : null,
|
|
63
|
+
* set: (key, value) => { if (key === 'gold' && typeof value === 'number') gold = value; },
|
|
64
|
+
* }));
|
|
65
|
+
* runtime.defineFunction('game.balance', () => gold);
|
|
66
|
+
* runtime.defineYieldingFunction('game.ask', (prompt) => [prompt]);
|
|
67
|
+
* runtime.run('function dialogue() game.side.gold = game.side.gold - require("rules").cost; return game.ask("Continue?") end');
|
|
68
|
+
* const dialogue = runtime.coroutine('dialogue');
|
|
69
|
+
* console.log(dialogue.resume().values); // ['Continue?']
|
|
70
|
+
* console.log(dialogue.resume([true]).values); // [true]
|
|
71
|
+
* runtime.dispose();
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export declare function createFengariRuntime(options?: FengariRuntimeOptions): FengariRuntime;
|
|
75
|
+
//# sourceMappingURL=fengari-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fengari-runtime.d.ts","sourceRoot":"","sources":["../../src/mwl/fengari-runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAE7D,gGAAgG;AAChG,MAAM,MAAM,YAAY,GACrB,IAAI,GACJ,OAAO,GACP,MAAM,GACN,MAAM,GACN,MAAM,GACN,UAAU,GACV,gBAAgB,GAChB,YAAY,EAAE,GACd;IAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAAA;CAAE,GACxC,GAAG,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;AAEnC,gHAAgH;AAChH,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAC;IAC5D,OAAO,IAAI,IAAI,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,MAAM,CAAC;IAClD,MAAM,CAAC,IAAI,CAAC,EAAE,SAAS,YAAY,EAAE,GAAG;QAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,CAAA;KAAE,CAAC;IAC7G,0EAA0E;IAC1E,KAAK,IAAI,IAAI,CAAC;CACd;AAED,iFAAiF;AACjF,MAAM,WAAW,mBAAmB;IACnC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,YAAY,CAAC;IACxC,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IACtD,IAAI,CAAC,IAAI,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IACtC,MAAM,CAAC,IAAI,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,qBAAsB,SAAQ,wBAAwB;IACtE,2FAA2F;IAC3F,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD,2EAA2E;IAC3E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC9B,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;IACtD,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,EAAE,IAAI,CAAC,EAAE,SAAS,YAAY,EAAE,GAAG,YAAY,CAAC;IACtF,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAAC;IAChC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IAC7C,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAC1C,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,YAAY,EAAE,KAAK,YAAY,GAAG,IAAI,GAAG,IAAI,CAAC;IAC/F,mGAAmG;IACnG,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,YAAY,EAAE,KAAK,SAAS,YAAY,EAAE,GAAG,IAAI,CAAC;IAC3G,WAAW,CAAC,OAAO,EAAE,mBAAmB,GAAG,gBAAgB,CAAC;IAC5D,mGAAmG;IACnG,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,GAAG,gBAAgB,CAAC;IAC/D,OAAO,IAAI,IAAI,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,qBAA0B,GAAG,cAAc,CAgdxF"}
|