@onceworlds/cli 0.12.0 → 0.13.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/dist/emulator.js CHANGED
@@ -1,6 +1,6 @@
1
- "use strict";(()=>{var He=["#f6dcc5","#ebc29f","#d4a07a","#b07a55","#875839","#5e3d29","#93c9bb","#a8c595","#94bcd3","#ab9bd9","#dca3ad","#b3bccb"],be=["#c95f56","#dc8a4f","#e2bd52","#9cc27a","#5a8a5e","#4f9a8e","#6fb7c8","#5b87c5","#465089","#8b68a8","#dc8fae","#f2eee6","#a9a8ad","#5d5c63","#2b2a30","#80604d"],Fe=["Happy","Grin","Shades","Wink","Surprised","Determined","Joyful","Shy"],qe=["None","Sprout","Cap","Beanie","Crown","Headphones","Horns","Antenna","Bow","Halo","Party hat","Cat ears","Short hair","Tuft","Bob","Spikes","Bun","Curls","Long","Mop"];var Ke=["Plain","Band","Spots","Stripes","Star"];var jo={skin:He.length,shirt:be.length,pants:be.length,face:Fe.length,hat:qe.length,top:Ke.length},Xt=[[5,6],[3,4],[6,7],[9,8],[10,0],[12,13]];function Ve(t){let e=2166136261;for(let c of t)e=Math.imul(e^c.charCodeAt(0),16777619)>>>0;let o=c=>(e=Math.imul(e^e>>>13,2654435761)>>>0,e%c),r=o(He.length),n=r>=6?Xt[r-6][o(2)]:o(be.length),a=o(2)===0?n:[8,13,14,15][o(4)],i=o(5)===0?1:12+o(qe.length-12),s=o(2)===0?0:o(Ke.length);return{skin:r,shirt:n,pants:a,face:o(Fe.length),hat:i,top:s}}var E={roomMaxPlayersDefault:16,roomMaxPlayersCap:30,teamsMax:8,messageBytes:16*1024,presenceBytes:1024,stateKeyLength:64,stateValueBytes:16*1024,stateKeys:256,privateKeys:16,privateValueBytes:4*1024,privateBytesPerPlayer:8*1024,messagesPerSecond:60,messagesPerSecondMin:10,roomBytesPerSecond:128*1024,roomBytesPerSecondMin:16*1024,presenceHz:20,presenceHzMin:5,roomMessagesPerSecond:900,roomBytesPerSecondTotal:2*1024*1024,matchCountdownMs:3e3,matchCountdownMaxMs:1e4,matchIdleMs:45e3,matchAbandonMs:12e4,idleAfterMs:3e4,idleAbandonMs:5*6e4,rankedPairMatchesPerDay:5,chatLength:200,chatIntervalMs:750,chatHistory:50,chatChannelsPerPlayer:10,saveValueBytes:64*1024,saveKeysPerGame:100,saveBytesPerGame:1024*1024,guestSaveBytesPerGame:128*1024,serverDataKeys:128,serverDataBytes:512*1024,serverDataServersPerGame:5e3,serverDataBytesPerGame:64*1024*1024,serverDataIdleDays:120,gameDataKeys:32,gameDataListMax:100,gameDataItemBytes:1024,gameDataValueBytes:8*1024,gameDataMapFields:1e3,gameDataMapPerPlayer:100,gameDataNumberMax:1e15,gameDataWritesPerMinute:30,deployFiles:2e3,deployFileBytes:25*1024*1024,deployHtmlBytes:10*1024*1024,deployPathLength:200,deployTotalBytes:100*1024*1024,envVars:50,envValueBytes:4*1024,envTotalBytes:16*1024,proxyRequestBytes:1024*1024,proxyTextRequestBytes:256*1024,proxySpeechRequestBytes:16*1024,proxyResponseBytes:10*1024*1024,proxyMaxOutputTokens:4096,proxyCallsPerDay:2e3,proxyCallsPerDayMax:1e5,proxyCallsPerPlayerPerDay:100,proxyCallsPerNetworkPerDay:1e3,proxyMediaCallsPerDay:100,proxyMediaCallsPerDayMax:1e4,proxyMediaCallsPerPlayerPerDay:20,proxyMediaCallsPerNetworkPerDay:25,proxyUnitsPerDay:2e6,proxyUnitsPerDayMax:1e8,proxyUnitsPerPlayerPerDay:25e4,proxyUnitsPerGuestPerDay:1e5,proxyUnitsPerNetworkPerDay:5e5,errorReportsPerPage:10,consoleLineLength:2e3,consoleLinesPerSecond:50,gameNameLength:60,gameDescriptionLength:1e3,galleryItems:5,mediaImageBytes:5*1024*1024,mediaVideoBytes:30*1024*1024,mediaGifBytes:1024*1024,mediaGifSide:512,mediaUploadsPerDay:200,mediaBytesPerDay:500*1024*1024,badgesPerGame:50,controlHints:12,controlHintKeys:4,controlKeyLength:12,controlActionLength:40,helpLength:400,badgeNameLength:50,badgeDescriptionLength:200,leaderboardsPerGame:25,leaderboardTop:100,gamesPerOwner:50,gamesCreatedPerCreatorPerDay:3,uploadAccountAgeMs:10080*6e4,deploysPerOwnerPerDay:100,draftsPerOwnerPerDay:1e3,draftsKeptPerGame:3,cliTokensPerAccount:10,cliDeviceCodeSeconds:600,deploymentsKeptPerGame:10,deployBytesPerOwnerPerDay:2*1024*1024*1024,deployEntriesPerOwnerPerDay:1e5,bioLength:500,exportRows:2e3,exportSaveChars:4*1024*1024,exportEveryMs:60*6e4,friendsMax:200,friendRequestsMax:100,friendRequestsIncomingMax:50,friendRequestsPerDay:20,friendRequestsPerNetworkPerDay:120,friendRequestPushesPerDay:5,passwordMinLength:8,passwordMaxLength:72,signupsPerNetworkPerDay:100,signupsPerWidePerDay:300,guestsPerNetworkPerDay:300,guestsPerWidePerDay:1500,visitsPerNetworkPerDay:10,visitSeconds:10,errorsPerDeployment:100,controlButtons:4,controlLabelLength:12,partySize:6,partyChatHistory:50,messageLength:500,messagesPerPage:50,groupChatMembers:20,groupChatNameLength:40,conversationsListed:50,notificationsListed:50,invitesPerRequest:10,inviteLiveMs:30*6e4,partyInviteMs:60*6e4};var Ye=/^[a-z0-9]{12}$/,Z=2;function we(t){return`${t.mode}.${t.max}.${t.teams}.${t.ranked?1:0}${t.min!==Z?`.${t.min}`:""}`}var Zt=/^pub:([a-z0-9-]+)\.(\d+)\.(\d)\.([01])(?:\.(\d+))?:(\d+)$/;function Je(t){let e=Zt.exec(t);if(!e)return null;let o={mode:e[1],max:Number(e[2]),teams:Number(e[3]),ranked:e[4]==="1",min:e[5]===void 0?Z:Number(e[5])};return{...o,name:we(o),n:Number(e[6])}}function Xe(t,e,o){let r=typeof t=="string"?Number(t):t;return typeof r!="number"||!Number.isInteger(r)?Math.min(o,e):Math.min(e,Math.max(1,r))}var We=t=>t.replace(/[\u0000-\u001f\u007f]/g," ").replace(/\s+/g," ").trim();var Qt=/^[a-z0-9][a-z0-9-]{0,19}$/;function Ze(t){if(!t||typeof t!="object")return null;let e=t,o=[];for(let n of Array.isArray(e.buttons)?e.buttons:[]){if(o.length>=E.controlButtons)break;let{id:a,label:i}=n??{};if(typeof a!="string"||!Qt.test(a)||o.some(c=>c.id===a))continue;let s=typeof i=="string"?i.replace(/[\u0000-\u001f\u007f]/g,"").replace(/\s+/g," ").trim().slice(0,E.controlLabelLength).trim():"";o.push({id:a,label:s||a})}let r=e.stick===!0;return r||o.length>0?{stick:r,zone:e.zone==="corner"?"corner":"wide",buttons:o}:null}var K="host:ow.settings",eo=/^[a-z][a-zA-Z0-9]{0,15}$/;function Qe(t){if(!Array.isArray(t))return[];let e=[];for(let o of t){if(e.length>=6)break;let{id:r,label:n,options:a,default:i}=o??{};if(typeof r!="string"||!eo.test(r)||e.some(d=>d.id===r)||!Array.isArray(a))continue;let s=[];for(let d of a){let p=typeof d=="object"&&d!==null?d.value:d;if(typeof p!="string"&&typeof p!="number"||typeof p=="number"&&!Number.isFinite(p)||s.length>=8||s.some(u=>u.value===p))continue;let m=typeof d=="object"&&d!==null&&typeof d.label=="string"?d.label:String(p);s.push({value:p,label:We(m).slice(0,20)||String(p)})}if(s.length<2)continue;let c=typeof n=="string"?We(n).slice(0,24):"";e.push({id:r,label:c||r,options:s,default:s.some(d=>d.value===i)?i:s[0].value})}return e}function et(t,e){let o=e!==null&&typeof e=="object"?e[t.id]:void 0;return t.options.some(r=>r.value===o)?o:t.default}var ve='{"t":"ping"}';var tt=4001,ot=4003,rt=4008,nt=4006;var at="onceworlds:hello",it="onceworlds:connect",to=/^[a-z0-9]{1,32}~(?:(?:0|[1-9]\d{0,3})\.(?:0|[1-9]\d{0,4})\.(?:0|[1-9]\d{0,5}))?~[0-9a-z]{1,11}~[lp]~[A-Za-z0-9_-]{43}$/;function st(t){let e=t!==null&&typeof t=="object"?t.build:void 0;return typeof e=="string"&&to.test(e)?e:null}var lt="onceworlds-console";var pt="allow-scripts allow-pointer-lock allow-forms allow-orientation-lock",dt=["autoplay","gamepad","screen-wake-lock","accelerometer","gyroscope"].map(t=>`${t} *`).join("; "),ct=100*1024*1024;var gt=new Set(["server","join","deploy","watch","player","embed","k","console","tester","live","demo",lt]);var ye="onceworlds:page",oo=["ran","blocked","failed","threw","loaded"];function ht(t){if(t===null||typeof t!="object")return null;let{what:e,detail:o}=t;return typeof e!="string"||!oo.includes(e)?null:{what:e,detail:typeof o=="string"?o.slice(0,200):""}}var ro=`(function(){var n=0,s=function(w,d){if(n++<12)try{parent.postMessage({type:${JSON.stringify(ye)},what:w,detail:String(d).slice(0,200)},"*")}catch(e){}};s("ran",document.readyState);addEventListener("securitypolicyviolation",function(e){s("blocked",e.effectiveDirective+" "+e.blockedURI)});addEventListener("error",function(e){var t=e.target;t&&t.tagName?s("failed",t.tagName+" "+(t.src||t.href)):s("threw",e.message+" "+e.filename+":"+e.lineno)},true);addEventListener("load",function(){s("loaded",typeof onceworlds)})})();`,Fo=`<script>${ro}<\/script>`;function ut(t){return t?.ownCountdown!==!0}var no=/^#(?:[0-9a-f]{3}){1,2}$/i,C={panel:"#111216",text:"#f7f7f8",accent:"#d33e00"},Se=4.5,ke=3,Q=["#4ade80","#15803d"];function xt(t){let e=[];if(t==null)return{look:null,notes:e};if(typeof t!="object")return{look:null,notes:["a look is { accent, panel, text } or null"]};let o=t,r=p=>{let m=o[p];return m==null?null:typeof m=="string"&&no.test(m)?ft(m):(e.push(`${p} is not #rgb or #rrggbb`),null)},n=r("panel"),a=r("text"),i=r("accent");if(n||a){let p=A(a??C.text,n??C.panel);p<Se&&(e.push(`text on panel is ${p.toFixed(1)}:1, under ${Se}:1: the platform's are used`),n=a=null)}let s=n??C.panel,c=a??C.text;if(i){let p=A(i,s);p<ke&&(e.push(`accent on panel is ${p.toFixed(1)}:1, under ${ke}:1: the platform's is used`),i=null)}if(!i&&n&&A(C.accent,n)<ke&&(i=c),!n&&!a&&!i)return{look:null,notes:e};let d=i?ao(i):null;return{look:{panel:n,text:a,muted:n||a?io(c,s):null,accent:i,accentHover:i?bt(i,d==="#ffffff"?"#000000":"#ffffff",.14):null,onAccent:d,ok:n?A(Q[0],n)>=A(Q[1],n)?Q[0]:Q[1]:null},notes:e}}function ft(t){let e=t.slice(1).toLowerCase();return`#${e.length===3?[...e].map(o=>o+o).join(""):e}`}function V(t){let e=ft(t);return[parseInt(e.slice(1,3),16),parseInt(e.slice(3,5),16),parseInt(e.slice(5,7),16)]}function mt(t){let[e,o,r]=V(t).map(n=>{let a=n/255;return a<=.04045?a/12.92:((a+.055)/1.055)**2.4});return .2126*e+.7152*o+.0722*r}function A(t,e){let[o,r]=[mt(t),mt(e)].sort((n,a)=>a-n);return(o+.05)/(r+.05)}function ao(t){return A(t,"#000000")>A(t,"#ffffff")?"#000000":"#ffffff"}function bt(t,e,o){let[r,n]=[V(t),V(e)];return`#${r.map((a,i)=>Math.round(a+(n[i]-a)*o).toString(16).padStart(2,"0")).join("")}`}function io(t,e){for(let o=.3;o>.001;o-=.05){let r=bt(t,e,o);if(A(r,e)>=Se)return r}return t}var ee=class{port=null;handlers;events;onConnect;solo;demo;onActivity;onPage;constructor(e,o,r,n,a={}){this.handlers=o,this.events=r,this.onConnect=n,this.solo=a.solo===!0,this.demo=a.demo===!0,this.onActivity=a.onActivity??null,this.onPage=a.onPage??null,window.addEventListener("message",i=>{if(!(i.source!==e.contentWindow||i.origin!=="null")&&(i.data?.type===at&&this.connect(i.source,st(i.data)),i.data?.type===ye)){let s=ht(i.data);s&&this.onPage?.(s)}})}activity(e,o){try{this.onActivity?.(e,o)}catch{}}emit(e,o){this.port?.postMessage({kind:"event",name:e,data:o})}connect(e,o){this.port?.close();let r=new MessageChannel,n=r.port1;this.port=n,n.onmessage=i=>this.receive(n,i.data);let a={type:it,...this.solo?{solo:!0}:{},...this.demo?{demo:!0}:{}};e.postMessage(a,"*",[r.port2]),this.onConnect(o)}async receive(e,o){if(!(e!==this.port||!o||typeof o!="object")){if(o.kind==="event"){Object.hasOwn(this.events,o.name)&&(this.events[o.name](o.data),this.activity(String(o.name),null));return}if(o.kind==="req")try{if(!Object.hasOwn(this.handlers,o.method))throw new Error(`Unknown method: ${String(o.method)}`);let r=this.handlers[o.method],n=await r(o.params);e.postMessage({kind:"res",id:o.id,ok:!0,result:n??null}),this.activity(String(o.method),null)}catch(r){let n=r instanceof Error?r.message:String(r);e.postMessage({kind:"res",id:o.id,ok:!1,error:n}),this.activity(String(o.method),n)}}}};var so="ow-game-storage",lo=256,wt="guest";function ze(t){let e=0,o=new Set,r=[t];for(;r.length;){let n=r.pop();if(typeof n=="string")e+=n.length*2;else if(typeof n!="object"||n===null)e+=8;else{if(o.has(n))continue;if(o.add(n),n instanceof ArrayBuffer)e+=n.byteLength;else if(ArrayBuffer.isView(n))e+=n.buffer.byteLength;else if(typeof Blob=="function"&&n instanceof Blob)e+=n.size;else if(n instanceof Map)for(let[a,i]of n)r.push(a,i);else if(n instanceof Set)for(let a of n)r.push(a);else if(Array.isArray(n))r.push(...n.slice(0,1e5),...n.length>1e5?[n.slice(1e5)]:[]);else for(let[a,i]of Object.entries(n))r.push(a,i)}}return e}function $(t){if(typeof t!="string"||t.length>lo)throw new Error("Bad name");return t}function vt(t){if(t===null)return null;if(typeof t=="string")return $(t);if(Array.isArray(t)&&t.length<=32)return t.map($);throw new Error("Bad key path")}function po(t){if(typeof t!="object"||t===null)throw new Error("Bad stores");let e=Object.keys(t);if(e.length>1e3)throw new Error("Too many stores");let o={};for(let r of e){let n=t[$(r)];if(typeof n!="object"||n===null||!Array.isArray(n.indexes)||n.indexes.length>1e3)throw new Error("Bad store");let a=Number(n.nextKey),i=n.indexes.map(s=>{let c=vt(s?.keyPath);if(c===null)throw new Error("Bad index");return{name:$(s.name),keyPath:c,unique:s.unique===!0,multiEntry:s.multiEntry===!0}});o[r]={keyPath:vt(n.keyPath),autoIncrement:n.autoIncrement===!0,nextKey:Number.isFinite(a)?a:1,indexes:i}}return o}function y(t){return new Promise((e,o)=>{t.onsuccess=()=>e(t.result),t.onerror=()=>o(t.error??new Error("Storage failed"))})}function Re(t){return new Promise((e,o)=>{t.oncomplete=()=>e(),t.onabort=t.onerror=()=>o(t.error??new Error("Storage failed"))})}var te=class{database=null;named=null;owner;factory;limit;constructor(e,o={}){this.owner=e,this.factory=o.factory??(()=>indexedDB),this.limit=o.limit??ct}open(){return this.database??=new Promise((e,o)=>{let r=this.factory().open(so,1);r.onupgradeneeded=()=>{r.result.createObjectStore("dbs",{keyPath:["owner","db"]}),r.result.createObjectStore("records")},r.onsuccess=()=>e(r.result),r.onerror=()=>o(r.error??new Error("No storage in this browser"))}),this.database}who(){return this.named??=this.owner().then(async({game:e,player:o})=>{let r=`${e}
2
- ${o??wt}`;return o!==null&&await this.adopt(`${e}
3
- ${wt}`,r),r}),this.named}async adopt(e,o){let n=(await this.open()).transaction(["dbs","records"],"readwrite"),a=Re(n),i=n.objectStore("dbs"),s=n.objectStore("records"),c=await y(i.getAll(this.ownerRange(e)));for(let d of c){let p=this.dbRange(e,d.db);if(!await y(i.getKey([o,d.db]))){let m=await y(s.getAllKeys(p));for(let u of m){let R=await y(s.get(u));s.put(R,[o,...u.slice(1)])}i.put({...d,owner:o})}s.delete(p),i.delete([e,d.db])}await a}ownerRange(e){return IDBKeyRange.bound([e],[`${e}\0`],!1,!0)}dbRange(e,o){return IDBKeyRange.bound([e,o],[e,`${o}\0`],!1,!0)}storeRange(e,o,r){return IDBKeyRange.bound([e,o,r],[e,o,`${r}\0`],!1,!0)}async read({db:e}){$(e);let o=await this.who(),n=(await this.open()).transaction(["dbs","records"]),a=await y(n.objectStore("dbs").get([o,e]));if(!a)return null;let i=n.objectStore("records"),s=this.dbRange(o,e),[c,d]=await Promise.all([y(i.getAllKeys(s)),y(i.getAll(s))]);return{version:a.version,stores:a.stores,records:c.map((p,m)=>({store:p[2],key:p[3],value:d[m].value}))}}async commit(e){let o=$(e?.db),r=Number(e.version);if(!Number.isFinite(r)||r<0)throw new Error("Bad version");let n=e.ops;if(!Array.isArray(n))throw new Error("Bad changes");let a=e.stores===void 0?void 0:po(e.stores),i=await this.who(),c=(await this.open()).transaction(["dbs","records"],"readwrite"),d=Re(c),p=c.objectStore("dbs"),m=c.objectStore("records");try{let u=await y(p.getAll(this.ownerRange(i))),R=u.find(l=>l.db===o)??{owner:i,db:o,version:r,stores:{},bytes:0},q=u.reduce((l,x)=>l+(x===R?0:x.bytes),0),b=R.bytes;for(let l of n){let x=$(l?.store);if("clear"in l){b-=await this.sum(m,this.storeRange(i,o,x)),m.delete(this.storeRange(i,o,x));continue}let fe=[i,o,x,"put"in l?l.put:l.remove],Ge=await y(m.get(fe));if(Ge&&(b-=Ge.bytes),"put"in l){let Ue=ze(l.value)+ze(l.put)+64;b+=Ue,m.put({value:l.value,bytes:Ue},fe)}else m.delete(fe)}if(a)for(let l of Object.keys(R.stores))Object.hasOwn(a,l)||(b-=await this.sum(m,this.storeRange(i,o,l)),m.delete(this.storeRange(i,o,l)));if(q+=ze(a??R.stores),q+b>this.limit)throw new Error("QuotaExceededError");p.put({owner:i,db:o,version:r,stores:a??R.stores,bytes:Math.max(0,b)})}catch(u){try{c.abort()}catch{}throw d.catch(()=>{}),u}return await d,null}async sum(e,o){return(await y(e.getAll(o))).reduce((n,a)=>n+a.bytes,0)}async remove({db:e}){$(e);let o=await this.who(),n=(await this.open()).transaction(["dbs","records"],"readwrite"),a=Re(n);return n.objectStore("records").delete(this.dbRange(o,e)),n.objectStore("dbs").delete([o,e]),await a,null}async list(){let e=await this.who(),o=await this.open();return(await y(o.transaction("dbs").objectStore("dbs").getAll(this.ownerRange(e)))).map(n=>({name:n.db,version:n.version}))}};var oe=class{pressed=new Set;press(e){this.pressed.add(e)}take(e){let o=new Set(e),r=!1;for(let n of this.pressed)o.has(n)||(o.add(n),r=!0);return this.pressed.clear(),{buttons:[...o],owed:r}}};var yt=["--ow-panel","--ow-face-edge","--ow-hover","--ow-on-accent","--text","--muted","--line","--line-strong","--fill","--fill-strong","--ok","--accent-fill","--accent-fill-hover","--accent-hover"],kt=["--ow-ctl-edge","--ow-ctl-fill","--ow-ctl-stick","--ow-ctl-knob","--ow-ctl-label","--ow-ctl-shadow","--ow-ctl-held","--ow-ctl-main","--ow-ctl-main-edge","--ow-ctl-main-label","--ow-ctl-main-shadow","--ow-ctl-main-held"];function w(t,e){return`rgba(${V(t).join(", ")}, ${e})`}function St(t){let e={};if(!t)return e;if(t.panel&&(e["--ow-panel"]=t.panel,e["--ow-face-edge"]=t.panel),t.ok&&(e["--ok"]=t.ok),t.panel||t.text){let o=t.text??C.text;t.text&&(e["--text"]=t.text),t.muted&&(e["--muted"]=t.muted),e["--line"]=w(o,.09),e["--line-strong"]=w(o,.16),e["--fill"]=w(o,.06),e["--fill-strong"]=w(o,.1),e["--ow-hover"]=w(o,.16)}return t.accent&&t.accentHover&&t.onAccent&&(e["--accent-fill"]=t.accent,e["--accent-fill-hover"]=t.accentHover,e["--accent-hover"]=t.accent,e["--ow-on-accent"]=t.onAccent),e}function zt(t){let e={};return t&&(t.text&&(e["--ow-ctl-edge"]=w(t.text,.4),e["--ow-ctl-knob"]=w(t.text,.9),e["--ow-ctl-label"]=t.text,e["--ow-ctl-held"]=w(t.text,.35)),t.panel&&(e["--ow-ctl-fill"]=w(t.panel,.4),e["--ow-ctl-stick"]=w(t.panel,.3),e["--ow-ctl-shadow"]=w(t.panel,.5)),t.accent&&t.accentHover&&t.onAccent&&(e["--ow-ctl-main"]=w(t.accent,.85),e["--ow-ctl-main-edge"]=t.accent,e["--ow-ctl-main-label"]=t.onAccent,e["--ow-ctl-main-shadow"]="none",e["--ow-ctl-main-held"]=t.accentHover)),e}function re(t,e,o){for(let r of e)Object.hasOwn(o,r)?t.style.setProperty(r,o[r]):t.style.removeProperty(r);t.toggleAttribute("data-look",e.some(r=>Object.hasOwn(o,r)))}var L=52,Ee=.12,Pe=96,co=400,go=2e4,ne=class{parent;root;zone;base;knob;buttonBox;send;onLayout;layout=null;touch;blocked=!1;keyboard=!1;stickPointer=null;center={x:0,y:0};x=0;y=0;buttons=new Map;held=new Map;fingers=new Map;report=new oe;queued=0;sent="";placed={stick:null,buttons:null};constructor(e,o,r=()=>{}){this.send=o,this.onLayout=r,this.parent=e,this.touch=matchMedia("(pointer: coarse)").matches,this.root=T("div","ow-controls"),this.root.hidden=!0,this.zone=T("div","ow-stick-zone"),this.base=T("div","ow-stick"),this.knob=T("div","ow-stick-knob"),this.base.append(this.knob),this.zone.append(this.base),this.buttonBox=T("div","ow-control-buttons"),this.root.append(this.zone,this.buttonBox),e.append(this.root),this.zone.addEventListener("pointerdown",n=>this.stickDown(n)),this.zone.addEventListener("pointermove",n=>this.stickMove(n));for(let n of["pointerup","pointercancel","lostpointercapture"])this.zone.addEventListener(n,a=>this.stickUp(a));this.root.addEventListener("contextmenu",n=>n.preventDefault())}setLayout(e){this.layout=Ze(e);let o=this.buttons;this.buttons=new Map,(this.layout?.buttons??[]).forEach((r,n)=>{let a=o.get(r.id)??this.button(r.id);o.delete(r.id),a.className=`ow-control-btn ow-control-btn-${n}`,a.classList.toggle("ow-held",[...this.held.values()].includes(r.id)),a.textContent=r.label,a.isConnected||this.buttonBox.append(a),this.buttons.set(r.id,a)});for(let[r,n]of o){for(let[a,i]of this.held)i===r&&this.letGo(a);n.remove()}this.zone.hidden=!this.layout?.stick,this.zone.classList.toggle("ow-stick-corner",this.layout?.zone==="corner"),this.zone.hidden&&this.stickPointer!==null&&this.letGo(this.stickPointer),this.render(),this.onLayout()}setLook(e){re(this.root,kt,e)}reservedBoxes(){if(this.root.hidden)return this.blocked&&this.layout&&this.touch&&!this.keyboard?this.placed:{stick:null,buttons:null};let e=this.zone.hidden?null:Et(this.base),o=null;for(let r of this.buttons.values()){let n=Et(r);!n.width||!n.height||(o=o?uo(o,n):n)}return this.placed={stick:e,buttons:o},this.placed}setTouch(e){this.touch=e,this.render()}setKeyboard(e){this.keyboard=e,this.render()}setBlocked(e){this.blocked=e,this.render()}render(){let e=!!this.layout&&this.touch&&!this.blocked&&!this.keyboard;this.root.hidden!==!e&&(e||this.reset(),this.root.hidden=!e,this.onLayout())}reset(){for(let e of[...this.held.keys()])this.letGo(e);this.stickPointer!==null&&this.letGo(this.stickPointer),this.endStick()}letGo(e){e===this.stickPointer&&this.endStick();let o=this.held.get(e);this.held.delete(e),o!==void 0&&![...this.held.values()].includes(o)&&this.buttons.get(o)?.classList.remove("ow-held");let r=this.fingers.get(e);this.fingers.delete(e),r&&ho(this.parent,e,r),this.changed()}button(e){let o=T("button","ow-control-btn");o.type="button",o.tabIndex=-1,o.addEventListener("pointerdown",n=>{n.preventDefault(),Rt(o,n.pointerId),o.classList.add("ow-held"),this.held.set(n.pointerId,e),this.report.press(e),this.fingers.set(n.pointerId,{x:n.clientX,y:n.clientY}),this.changed()}),o.addEventListener("pointermove",n=>{this.held.get(n.pointerId)===e&&this.fingers.set(n.pointerId,{x:n.clientX,y:n.clientY})});let r=n=>{this.held.get(n.pointerId)===e&&(this.held.delete(n.pointerId),this.fingers.delete(n.pointerId),[...this.held.values()].includes(e)||o.classList.remove("ow-held"),this.changed())};for(let n of["pointerup","pointercancel","lostpointercapture"])o.addEventListener(n,a=>r(a));return o}stickDown(e){if(this.stickPointer!==null)return;e.preventDefault(),Rt(this.zone,e.pointerId),this.stickPointer=e.pointerId,this.fingers.set(e.pointerId,{x:e.clientX,y:e.clientY});let o=this.zone.getBoundingClientRect(),r=this.base.getBoundingClientRect(),n=L+8;this.center={x:Math.min(Math.max(e.clientX,o.left+n),o.right-n),y:Math.min(Math.max(e.clientY,o.top+n),o.bottom-n)},this.base.style.transform=`translate(${this.center.x-(r.left+r.width/2)}px, ${this.center.y-(r.top+r.height/2)}px)`,this.base.classList.add("ow-active"),this.stickMove(e)}stickMove(e){if(e.pointerId!==this.stickPointer)return;this.fingers.set(e.pointerId,{x:e.clientX,y:e.clientY});let o=e.clientX-this.center.x,r=e.clientY-this.center.y,n=Math.hypot(o,r);n>L&&(o=o/n*L,r=r/n*L),this.knob.style.transform=`translate(${o}px, ${r}px)`;let a=Math.min(n,L)/L,i=a<Ee?0:(a-Ee)/(1-Ee)/(a||1);this.x=Pt(o/L*i),this.y=Pt(r/L*i),this.changed()}stickUp(e){e.pointerId===this.stickPointer&&(this.fingers.delete(e.pointerId),this.endStick())}endStick(){this.stickPointer=null,this.x=0,this.y=0,this.base.classList.remove("ow-active"),this.base.style.transform="",this.knob.style.transform="",this.changed()}changed(){this.queued||(this.queued=requestAnimationFrame(()=>{this.queued=0;let{buttons:e,owed:o}=this.report.take(this.held.values()),r={x:this.x,y:this.y,buttons:e};o&&this.changed();let n=JSON.stringify(r);n!==this.sent&&(this.sent=n,this.send(r))}))}};function ho(t,e,o){let r=T("div","ow-tap-shield");r.style.left=`${o.x-Pe/2}px`,r.style.top=`${o.y-Pe/2}px`,r.style.width=r.style.height=`${Pe}px`,t.append(r);let n=setTimeout(()=>r.remove(),go),a=()=>{clearTimeout(n),n=setTimeout(()=>r.remove(),co)};for(let i of["pointerup","pointercancel","lostpointercapture"])r.addEventListener(i,s=>s.pointerId===e&&a());r.addEventListener("contextmenu",i=>i.preventDefault());try{r.setPointerCapture(e)}catch{a()}}function Rt(t,e){try{t.setPointerCapture(e)}catch{}}function Et(t){let e=t.getBoundingClientRect(),o=getComputedStyle(t).transform;if(!o||o==="none")return e;let r=new DOMMatrixReadOnly(o);if(r.b||r.c||!r.a||!r.d)return e;let n=e.width/Math.abs(r.a),a=e.height/Math.abs(r.d),i=e.left+e.width/2-r.e,s=e.top+e.height/2-r.f;return{left:i-n/2,top:s-a/2,width:n,height:a}}function uo(t,e){let o=Math.min(t.left,e.left),r=Math.min(t.top,e.top);return{left:o,top:r,width:Math.max(t.left+t.width,e.left+e.width)-o,height:Math.max(t.top+t.height,e.top+e.height)-r}}function Pt(t){return Math.round(t*1e3)/1e3}function T(t,e,o){let r=document.createElement(t);return r.className=e,o!==void 0&&(r.textContent=o),r}function mo(t,e){return t!=="lobby"?{surface:t,hidden:!1}:{surface:e?"none":"lobby",hidden:e}}var Me=t=>Math.max(0,Math.ceil(t/1e3)),Ae=t=>{let e=t.slice(0,2).map(o=>o.name);return t.length>2?`${e.join(", ")} +${t.length-2}`:e.join(", ")};function xo(t){let{you:e,host:o,kind:r,match:n,now:a}=t,i=[...t.players].sort((l,x)=>l.id===e?-1:x.id===e?1:l.id===o?-1:x.id===o?1:l.name.localeCompare(x.name)).map(l=>({id:l.id,name:l.name,ready:!!l.ready,host:l.id===o,you:l.id===e,away:l.connected===!1,idle:l.connected!==!1&&!!l.idle})),s=t.players.filter(l=>l.connected!==!1),c=t.players.find(l=>l.id===e),d=o===e&&n.phase==="lobby",p=t.schema.map(l=>({id:l.id,label:l.label,value:et(l,t.written),options:l.options,editable:d})),m={n:s.filter(l=>l.ready||r!=="public"&&l.id===o).length,of:s.length},u={surface:"none",rows:i,count:`${t.players.length} / ${t.max}`,ready:m,status:"",action:{kind:"none",label:"",disabled:!0},settings:p,seconds:null},R=n.participants?.includes(e)??!1,q=o===e&&r!=="public";if(n.phase==="starting"){if(!R)return{...u,surface:"watching",status:"Watching"};let l=q?{kind:"stop",label:"Stop",disabled:!1}:{kind:"unready",label:"Not ready",disabled:!1};return t.ownCountdown?{...u,surface:"countdown",action:l,seconds:null,status:"Get ready"}:{...u,surface:"countdown",action:l,seconds:Math.max(1,Me((n.startsAt??a)-a)),status:"Get ready"}}if(n.phase==="playing"){if(n.paused){let l=s.filter(x=>n.participants?.includes(x.id)).length;return n.paused.reason==="host"?{...u,surface:"paused",status:"Paused"}:{...u,surface:"paused",seconds:Me(n.paused.since+E.matchAbandonMs-a),status:`Waiting for players \xB7 ${l} / ${n.min}`}}return R?u:{...u,surface:"watching",status:"Watching"}}let b=s.filter(l=>!l.ready&&(r==="public"||l.id!==o));if(u.surface="lobby",q){let l=s.length<n.min;return u.action={kind:"start",label:"Start",disabled:l||b.length>0},u.status=l?`Need ${n.min} players`:b.length?`Waiting for ${Ae(b)}`:"",u}if(u.action={kind:c?.ready?"unready":"ready",label:c?.ready?"Not ready":"Ready",disabled:!1},r==="public")s.length<n.min?u.status=`Need ${n.min} players`:n.waitUntil!==void 0&&b.length?(u.seconds=Me(n.waitUntil-a),u.status=`${Ae(b)} \xB7 ${u.seconds}s`):u.status=`${s.length-b.length} / ${s.length} ready`;else{let l=b.filter(x=>x.id!==e);u.status=c?.ready?l.length?`Waiting for ${Ae(l)}`:`Waiting for ${t.players.find(x=>x.id===o)?.name??"the host"}`:""}return u}function h(t,e="",o){let r=document.createElement(t);return e&&(r.className=e),o!==void 0&&(r.textContent=o),r}var ae=class{mode=null;ownCountdown=!1;hiddenByGame=!1;state=null;schema=[];timer=0;opts;root;card;title;count;list;settingsBox;status;action;invite;bar;barFaces;barStatus;barAction;countdown;numeral;cancel;pause;pauseStatus;pauseTimer;chip;last=null;lastNumeral=-1;listSignature="";settingsSignature="";faceSignature="";constructor(e,o){this.opts=o,this.root=h("div","ow-lobby"),this.root.hidden=!0,this.card=h("section","ow-lobby-card"),this.card.setAttribute("aria-label","Lobby");let r=h("header","ow-lobby-head");this.title=h("h2","","Lobby"),this.count=h("span","ow-lobby-count"),r.append(this.title,this.count),this.list=h("ul","ow-lobby-players"),this.settingsBox=h("div","ow-lobby-settings"),this.status=h("p","ow-lobby-status"),this.status.setAttribute("role","status"),this.action=this.button("ow-btn ow-btn-primary ow-lobby-action",()=>this.press()),this.invite=this.button("ow-btn ow-lobby-invite",()=>this.opts.onInvite()),this.invite.textContent="Invite";let n=h("div","ow-lobby-actions");n.append(this.action,this.invite),this.card.append(r,this.list,this.settingsBox,this.status,n),this.bar=h("div","ow-lobby-bar"),this.barFaces=h("span","ow-lobby-faces"),this.barStatus=h("span","ow-lobby-bar-status"),this.barStatus.setAttribute("role","status"),this.barAction=this.button("ow-btn ow-btn-primary ow-lobby-action",()=>this.press()),this.bar.append(this.barFaces,this.barStatus,this.barAction),this.countdown=h("div","ow-lobby-countdown"),this.numeral=h("b"),this.numeral.setAttribute("aria-hidden","true");let a=h("span","","Get ready");a.setAttribute("aria-hidden","true"),this.cancel=this.button("ow-btn ow-lobby-cancel",()=>this.press()),this.countdown.append(this.numeral,a,this.cancel),this.pause=h("div","ow-lobby-pause");let i=h("div","ow-lobby-pause-box");this.pauseStatus=h("p","ow-lobby-pause-status"),this.pauseStatus.setAttribute("role","status"),this.pauseTimer=h("p","ow-lobby-pause-timer"),i.append(this.pauseStatus,this.pauseTimer),this.pause.append(i),this.chip=h("div","ow-lobby-chip"),this.chip.setAttribute("role","status"),this.root.append(this.card,this.bar,this.countdown,this.pause,this.chip),e.append(this.root)}setSchema(e){this.schema=e,this.render()}enable(e,o={}){this.mode=e,this.ownCountdown=o.ownCountdown===!0,this.root.dataset.mode=e??"",e||this.reset(),this.render()}setLook(e){re(this.root,yt,e)}reservedBox(){if(this.root.hidden)return null;let e=!this.countdown.hidden&&this.countdown.classList.contains("ow-lobby-countdown-bare"),o=this.bar.hidden?e&&!this.cancel.hidden?this.cancel:null:this.bar;return o?o.getBoundingClientRect():null}setHidden(e){this.hiddenByGame=e,this.render()}setRoom(e,o=0){if(!e)return this.reset();this.state={you:e.you,host:e.host,kind:e.room.startsWith("private:")?"private":e.room.startsWith("room:")?"named":"public",max:Math.max(o,e.players.length),players:e.players.map(r=>({...r})),match:e.match??{phase:"lobby",n:0,min:1},written:e.state?.[K]},this.render()}handle(e){let o=this.state;if(o){switch(e.t){case"join":o.players=[...o.players.filter(r=>r.id!==e.player.id),{...e.player}];break;case"leave":o.players=o.players.filter(r=>r.id!==e.id);break;case"away":o.players=o.players.map(r=>r.id===e.id?{...r,connected:!1}:r);break;case"back":o.players=o.players.map(r=>{if(r.id!==e.id)return r;let{connected:n,...a}=r;return a});break;case"ready":o.players=o.players.map(r=>{if(r.id!==e.id)return r;let{ready:n,...a}=r;return e.ready?{...a,ready:!0}:a});break;case"unready":o.players=o.players.map(({ready:r,...n})=>n);break;case"host":o.host=e.id;break;case"idle":o.players=o.players.map(r=>{if(r.id!==e.id)return r;let{idle:n,...a}=r;return e.idle?{...a,idle:!0}:a});break;case"name":o.players=o.players.map(r=>r.id===e.id?{...r,name:e.name}:r);break;case"match":o.match=e.match;break;case"state":if(e.k!==K)return;o.written=e.v;break;default:return}this.render()}}chooseSetting(e,o){let r=this.state,n=this.schema.find(i=>i.id===e);if(!r||!n||!n.options.some(i=>i.value===o))return null;let a={...r.written!==null&&typeof r.written=="object"?r.written:{},[e]:o};return r.written=a,this.render(),a}setReadyLocal(e){let o=this.state?.players.find(r=>r.id===this.state?.you);o&&(e?o.ready=!0:delete o.ready,this.render())}button(e,o){let r=h("button",e);return r.type="button",r.addEventListener("pointerdown",n=>n.preventDefault()),r.addEventListener("click",o),r}press(){let e=this.last?.action.kind;e==="start"?this.opts.onStart():e==="stop"?this.opts.onStop():e==="ready"?(this.setReadyLocal(!0),this.opts.onReady(!0)):e==="unready"&&(this.setReadyLocal(!1),this.opts.onReady(!1))}reset(){this.state=null,this.last=null,this.hiddenByGame=!1,window.clearInterval(this.timer),this.timer=0,this.root.hidden=!0,this.opts.onLayout?.()}render(){this.draw(),this.opts.onLayout?.()}draw(){if(!this.mode||!this.state){this.root.hidden=!0;return}let e=xo({...this.state,schema:this.schema,now:this.opts.now(),ownCountdown:this.ownCountdown});this.last=e;let o=mo(e.surface,this.hiddenByGame);this.hiddenByGame=o.hidden;let r=o.surface==="lobby";if(this.root.hidden=o.surface==="none",this.card.hidden=!(r&&this.mode==="card"),this.bar.hidden=!(r&&this.mode==="bar"),this.countdown.hidden=e.surface!=="countdown",this.countdown.classList.toggle("ow-lobby-countdown-bare",e.surface==="countdown"&&e.seconds===null),this.pause.hidden=e.surface!=="paused",this.chip.hidden=e.surface!=="watching",this.root.classList.toggle("ow-lobby-dim",r&&this.mode==="card"),r&&this.mode==="card"&&this.renderCard(e),r&&this.mode==="bar"&&this.renderBar(e),e.surface==="countdown"&&e.seconds!==null&&e.seconds!==this.lastNumeral&&(this.lastNumeral=e.seconds,this.numeral.textContent=String(e.seconds),this.numeral.style.animation="none",this.numeral.offsetWidth,this.numeral.style.animation=""),e.surface==="countdown"&&(this.cancel.textContent=e.action.label),e.surface!=="countdown"&&(this.lastNumeral=-1),e.surface==="paused"&&(this.pauseStatus.textContent=e.status,this.pauseTimer.textContent=e.seconds!==null?`Ends in ${fo(e.seconds)}`:""),e.surface==="watching"){let[a,...i]=e.status.split(" \xB7 "),s=h("span","ow-lobby-chip-more",i.length?` \xB7 ${i.join(" \xB7 ")}`:"");this.chip.replaceChildren(a,s)}let n=e.seconds!==null;n&&!this.timer?this.timer=window.setInterval(()=>this.render(),250):!n&&this.timer&&(window.clearInterval(this.timer),this.timer=0)}renderCard(e){this.count.textContent=e.count,this.settingsBox.hidden=e.settings.length===0,this.status.textContent=e.status,this.status.hidden=e.status==="",this.setAction(this.action,e);let o=JSON.stringify(e.settings);o!==this.settingsSignature&&(this.settingsSignature=o,this.settingsBox.replaceChildren(...e.settings.map(n=>this.settingRow(n))));let r=JSON.stringify(e.rows);r!==this.listSignature&&(this.listSignature=r,this.list.replaceChildren(...e.rows.map(n=>{let a=h("li",n.away||n.idle?"ow-lobby-away":n.ready?"ow-lobby-ready":""),i=h("img","ow-headshot");i.src=`/avatar/${encodeURIComponent(n.id)}/head.svg`,i.alt="";let s=h("span","ow-lobby-name",n.name),c=[n.away?"Reconnecting":n.idle?"Idle":"",n.you?"You":"",n.host?"Host":""].filter(Boolean).join(" \xB7 ");return a.append(i,s),c&&a.append(h("span","ow-player-notes",c)),a.append(h("span",`ow-lobby-check${n.ready?" ow-lobby-check-on":""}`,n.ready?"Ready":"")),a})))}settingRow(e){let o=h("div","ow-lobby-setting");if(o.append(h("span","ow-lobby-setting-label",e.label)),!e.editable)return o.append(h("span","ow-lobby-setting-value",e.options.find(n=>n.value===e.value)?.label??String(e.value))),o;let r=h("div","ow-segments");r.setAttribute("role","radiogroup"),r.setAttribute("aria-label",e.label);for(let n of e.options){let a=this.button(`ow-segment${n.value===e.value?" ow-segment-on":""}`,()=>this.opts.onSetting(e.id,n.value));a.textContent=n.label,a.setAttribute("role","radio"),a.setAttribute("aria-checked",String(n.value===e.value)),r.append(a)}return o.append(r),o}renderBar(e){this.barStatus.textContent=e.status||`${e.ready.n} / ${e.ready.of} ready`,this.setAction(this.barAction,e);let o=JSON.stringify(e.rows.slice(0,5).map(r=>[r.id,r.ready]));o!==this.faceSignature&&(this.faceSignature=o,this.barFaces.replaceChildren(...e.rows.slice(0,5).map(r=>{let n=h("img",`ow-headshot${r.ready?" ow-lobby-face-ready":""}`);return n.src=`/avatar/${encodeURIComponent(r.id)}/head.svg`,n.alt="",n})))}setAction(e,o){e.hidden=o.action.kind==="none",e.textContent=o.action.label,e.disabled=o.action.disabled,e.classList.toggle("ow-btn-primary",o.action.kind!=="unready")}};function fo(t){return`${Math.floor(t/60)}:${String(t%60).padStart(2,"0")}`}function Mt(t,e){let o=[];for(let{kind:r,box:n}of e){if(!n||!(n.width>0)||!(n.height>0))continue;let a=Math.max(0,n.left-t.left),i=Math.max(0,n.top-t.top),s=Math.min(t.width,n.left+n.width-t.left),c=Math.min(t.height,n.top+n.height-t.top);if(!(s-a>.01&&c-i>.01))continue;let d=Math.floor(a+.01),p=Math.floor(i+.01);o.push({kind:r,x:d,y:p,w:Math.ceil(s-.01)-d,h:Math.ceil(c-.01)-p})}return o}var ie=class{measure;send;nextFrame;queued=!1;sent=null;constructor(e,o,r=n=>requestAnimationFrame(n)){this.measure=e,this.send=o,this.nextFrame=r}changed(){this.queued||(this.queued=!0,this.nextFrame(()=>{this.queued=!1;let e=this.measure();if(!e)return;let o=JSON.stringify(e);o!==this.sent&&(this.sent=o,this.send(e))}))}resend(){this.sent=null,this.changed()}};var $e=t=>`ow:resume:${t}`;function _(t,e){try{e?sessionStorage.setItem($e(t),JSON.stringify({...e,at:Date.now()})):sessionStorage.removeItem($e(t))}catch{}}function At(t){try{let e=sessionStorage.getItem($e(t));if(!e)return null;let o=performance.getEntriesByType("navigation")[0];if(o&&o.type!=="reload"&&o.type!=="back_forward")return se(t);let r=JSON.parse(e);return typeof r.key!="string"||typeof r.rejoin!="string"||typeof r.at!="number"||Date.now()-r.at>18e4||Number(r.rejoin.split(".")[0])*1e3<Date.now()?se(t):{key:r.key,rejoin:r.rejoin}}catch{return null}}var bo=t=>`ow:own-server:${t}`;function $t(t,e){try{sessionStorage.setItem(bo(t),e)}catch{}}function se(t){return _(t,null),null}var le=class extends Error{constructor(o,r=0,n=0,a=""){super(o);this.status=r;this.retryAfterMs=n;this.code=a;this.name="RequestError"}status;retryAfterMs;code;get retryable(){return this.status===0||this.status===408||this.status===429||this.status>=500}};var pe=class{constructor(e=48*1024){this.max=e}max;used=0;async run(e,o){let r=new TextEncoder().encode(e).byteLength;if(this.used+r>this.max)throw new le("Progress is waiting to sync when you return to the game.");this.used+=r;try{return await o()}finally{this.used-=r}}};var Le=/^[2-9]$/.test(new URLSearchParams(location.search).get("player")??"")?new URLSearchParams(location.search).get("player"):null;var wo=null;function Lt(){return wo?.n??null}var yr=new pe;var Ct=[300,700,1500,3e3,5e3,8e3],vo=9e4,yo=1e4,Dt=()=>Array.from(crypto.getRandomValues(new Uint8Array(10)),t=>"abcdefghijklmnopqrstuvwxyz0123456789"[t%36]).join(""),Ot=Dt();function _t(){Ot=Dt()}var Tt=null;var It=!1;function jt(t){It=t}var ko=5e4,So=4e3,de=class{ws=null;key=null;rejoin=null;you="";closed=!1;attempt=0;droppedAt=0;sleeping=!1;lastHeard=0;heardCount=0;pingAt=0;hintedHidden=!1;pingTimer=0;watchTimer=0;retryTimer=0;page=Ot;slug;options;callbacks;onVisible=()=>{this.hint(),document.visibilityState==="visible"&&this.wake()};onOnline=()=>this.wake();onShow=e=>e.persisted&&this.wake();onHide=()=>this.hint();constructor(e,o,r){this.slug=e,this.options=o,this.callbacks=r,this.key=o.key??null,this.rejoin=o.rejoin??null,document.addEventListener("visibilitychange",this.onVisible),addEventListener("online",this.onOnline),addEventListener("pageshow",this.onShow),addEventListener("pagehide",this.onHide)}join(){return new Promise((e,o)=>this.open(e,o))}send(e){this.ws?.readyState===WebSocket.OPEN&&this.ws.send(JSON.stringify(e))}hint(){let e=document.visibilityState==="hidden";e===this.hintedHidden||this.ws?.readyState!==WebSocket.OPEN||(this.hintedHidden=e,this.send({t:"vis",hidden:e}))}ping(){let e=this.ws;e?.readyState===WebSocket.OPEN&&(this.pingAt&&Date.now()-this.pingAt<5e3||(this.pingAt=Date.now(),e.send(ve)))}close(){this.closed=!0,_(this.slug,null),this.dispose(),this.ws?.close(1e3)}reload(){return this.closed=!0,this.dispose(),this.ws?.close(nt,"Updating"),this.key?.startsWith("pub:")&&this.rejoin?{key:this.key,rejoin:this.rejoin}:null}open(e,o){this.retryTimer=0;let r=new URL(`/api/games/${this.slug}/rooms/connect`,location.href);r.protocol=location.protocol==="https:"?"wss:":"ws:",this.key?(r.searchParams.set("key",this.key),this.rejoin&&r.searchParams.set("rejoin",this.rejoin)):this.options.server?(r.searchParams.set("server",this.options.server),this.options.private&&r.searchParams.set("own","1")):this.options.name?r.searchParams.set("name",this.options.name):(this.options.mode&&r.searchParams.set("mode",this.options.mode),this.options.follow&&r.searchParams.set("follow",this.options.follow)),this.options.maxPlayers&&r.searchParams.set("max",String(this.options.maxPlayers)),this.options.minPlayers&&r.searchParams.set("min",String(this.options.minPlayers)),this.options.countdown!==void 0&&r.searchParams.set("countdown",String(this.options.countdown)),this.options.teams&&r.searchParams.set("teams",String(this.options.teams)),this.options.ranked&&r.searchParams.set("ranked","1"),Le&&r.searchParams.set("devplayer",Le);let n=Lt();n&&r.searchParams.set("tester",n),r.searchParams.set("page",this.page),this.options.build&&r.searchParams.set("build",this.options.build),Tt&&r.searchParams.set("pass",Tt),It&&r.searchParams.set("demo","1");let a=new WebSocket(r);this.ws=a,this.lastHeard=Date.now();let i=!1;a.onopen=()=>{this.lastHeard=Date.now(),window.clearInterval(this.pingTimer),window.clearInterval(this.watchTimer),this.pingTimer=window.setInterval(()=>a.readyState===WebSocket.OPEN&&this.ping(),yo),this.watchTimer=window.setInterval(()=>{a===this.ws&&a.readyState===WebSocket.OPEN&&Date.now()-this.lastHeard>ko&&this.lost(a)},5e3)},a.onmessage=d=>{this.lastHeard=Date.now(),this.heardCount++;let p;try{p=JSON.parse(d.data)}catch{return}if(p.t==="pong"){this.pingAt&&this.callbacks.onRtt?.(Date.now()-this.pingAt),this.pingAt=0;return}if(p.t==="welcome"&&(p.rejoin&&(this.rejoin=p.rejoin),delete p.rejoin,p.room.startsWith("pub:")&&this.rejoin&&_(this.slug,{key:p.room,rejoin:this.rejoin})),p.t==="leave"&&p.kicked&&p.id===this.you){this.close(),this.finish("kicked");return}if(p.t==="welcome"&&(this.hintedHidden=!1,this.hint()),p.t==="welcome"&&!i&&(i=!0,this.key=p.room,this.you=p.you,this.attempt=0,this.droppedAt&&(this.droppedAt=0,this.callbacks.onConnection?.(!0)),e)){let{t:m,...u}=p;e(u),e=void 0;return}this.callbacks.onMessage(p)};let s=!1,c=d=>{if(!s&&(s=!0,window.clearInterval(this.pingTimer),window.clearInterval(this.watchTimer),!(this.closed||a!==this.ws))){if(d.code===tt)return this.finish("replaced");if(d.code===rt&&i&&document.visibilityState==="hidden"){this.sleeping=!0,this.droppedAt||(this.droppedAt=Date.now(),this.callbacks.onConnection?.(!1));return}if(d.code===ot){if(i||!o)return this.finish("kicked");this.closed=!0,this.dispose(),_(this.slug,null),o(Object.assign(new Error("You were removed from this server."),{kicked:!0}));return}if(!i&&o){this.closed=!0,this.dispose(),o(new Error(this.options.name||this.options.server?"Could not join that server. It may be full.":"Could not join a server. Try again soon."));return}this.retry()}};a.onclose=c,a.onerror=()=>{window.setTimeout(()=>{a.readyState===WebSocket.CLOSED&&c({code:1006})},1e3)}}retry(){if(this.closed)return;let e=Date.now();if(this.droppedAt||(this.droppedAt=e,this.callbacks.onConnection?.(!1)),e-this.droppedAt>vo)return this.finish("disconnected");if(this.retryTimer)return;let o=Ct[Math.min(this.attempt++,Ct.length-1)]*(.5+Math.random());this.retryTimer=window.setTimeout(()=>{this.retryTimer=0,this.closed||this.open()},o)}lost(e){if(!(e!==this.ws||this.closed)){e.onopen=e.onmessage=e.onclose=e.onerror=null,window.clearInterval(this.pingTimer),window.clearInterval(this.watchTimer);try{e.close()}catch{}this.retry()}}wake(){if(this.closed)return;if(this.sleeping){this.sleeping=!1,this.droppedAt=Date.now(),this.attempt=0,this.open();return}let e=this.ws;if(!e||e.readyState===WebSocket.CLOSING||e.readyState===WebSocket.CLOSED){this.retryTimer&&(window.clearTimeout(this.retryTimer),this.retryTimer=0,this.open());return}if(e.readyState!==WebSocket.OPEN)return;let o=this.heardCount;try{e.send(ve)}catch{return this.lost(e)}window.setTimeout(()=>e===this.ws&&!this.closed&&this.heardCount===o&&this.lost(e),So)}dispose(){window.clearInterval(this.pingTimer),window.clearInterval(this.watchTimer),window.clearTimeout(this.retryTimer),this.retryTimer=0,document.removeEventListener("visibilitychange",this.onVisible),removeEventListener("online",this.onOnline),removeEventListener("pageshow",this.onShow),removeEventListener("pagehide",this.onHide)}finish(e){this.closed=!0,this.dispose(),_(this.slug,null),this.callbacks.onClose(e)}};var Bt=`:root {
1
+ "use strict";(()=>{var He=["#f6dcc5","#ebc29f","#d4a07a","#b07a55","#875839","#5e3d29","#93c9bb","#a8c595","#94bcd3","#ab9bd9","#dca3ad","#b3bccb"],ve=["#c95f56","#dc8a4f","#e2bd52","#9cc27a","#5a8a5e","#4f9a8e","#6fb7c8","#5b87c5","#465089","#8b68a8","#dc8fae","#f2eee6","#a9a8ad","#5d5c63","#2b2a30","#80604d"],Fe=["Happy","Grin","Shades","Wink","Surprised","Determined","Joyful","Shy"],Ke=["None","Sprout","Cap","Beanie","Crown","Headphones","Horns","Antenna","Bow","Halo","Party hat","Cat ears","Short hair","Tuft","Bob","Spikes","Bun","Curls","Long","Mop"];var qe=["Plain","Band","Spots","Stripes","Star"];var Ho={skin:He.length,shirt:ve.length,pants:ve.length,face:Fe.length,hat:Ke.length,top:qe.length},ro=[[5,6],[3,4],[6,7],[9,8],[10,0],[12,13]];function Ve(t){let e=2166136261;for(let d of t)e=Math.imul(e^d.charCodeAt(0),16777619)>>>0;let o=d=>(e=Math.imul(e^e>>>13,2654435761)>>>0,e%d),r=o(He.length),n=r>=6?ro[r-6][o(2)]:o(ve.length),a=o(2)===0?n:[8,13,14,15][o(4)],i=o(5)===0?1:12+o(Ke.length-12),s=o(2)===0?0:o(qe.length);return{skin:r,shirt:n,pants:a,face:o(Fe.length),hat:i,top:s}}var P={roomMaxPlayersDefault:16,roomMaxPlayersCap:30,teamsMax:8,messageBytes:16*1024,presenceBytes:1024,stateKeyLength:64,stateValueBytes:16*1024,stateKeys:256,privateKeys:16,privateValueBytes:4*1024,privateBytesPerPlayer:8*1024,messagesPerSecond:60,messagesPerSecondMin:10,roomBytesPerSecond:128*1024,roomBytesPerSecondMin:16*1024,presenceHz:20,presenceHzMin:5,roomMessagesPerSecond:900,roomBytesPerSecondTotal:2*1024*1024,matchCountdownMs:3e3,matchCountdownMaxMs:1e4,matchIdleMs:45e3,matchAbandonMs:12e4,idleAfterMs:3e4,idleAbandonMs:5*6e4,rankedPairMatchesPerDay:5,chatLength:200,chatIntervalMs:750,chatHistory:50,chatChannelsPerPlayer:10,saveValueBytes:64*1024,saveKeysPerGame:100,saveBytesPerGame:1024*1024,guestSaveBytesPerGame:128*1024,serverDataKeys:128,serverDataBytes:512*1024,serverDataServersPerGame:5e3,serverDataBytesPerGame:64*1024*1024,serverDataIdleDays:120,gameDataKeys:32,gameDataListMax:100,gameDataItemBytes:1024,gameDataValueBytes:8*1024,gameDataMapFields:1e3,gameDataMapPerPlayer:100,gameDataNumberMax:1e15,gameDataWritesPerMinute:30,deployFiles:2e3,deployFileBytes:25*1024*1024,deployHtmlBytes:10*1024*1024,deployPathLength:200,deployTotalBytes:100*1024*1024,envVars:50,envValueBytes:4*1024,envTotalBytes:16*1024,proxyRequestBytes:1024*1024,proxyTextRequestBytes:256*1024,proxySpeechRequestBytes:16*1024,proxyResponseBytes:10*1024*1024,proxyMaxOutputTokens:4096,proxyCallsPerDay:2e3,proxyCallsPerDayMax:1e5,proxyCallsPerPlayerPerDay:100,proxyCallsPerNetworkPerDay:1e3,proxyMediaCallsPerDay:100,proxyMediaCallsPerDayMax:1e4,proxyMediaCallsPerPlayerPerDay:20,proxyMediaCallsPerNetworkPerDay:25,proxyUnitsPerDay:2e6,proxyUnitsPerDayMax:1e8,proxyUnitsPerPlayerPerDay:25e4,proxyUnitsPerGuestPerDay:1e5,proxyUnitsPerNetworkPerDay:5e5,errorReportsPerPage:10,consoleLineLength:2e3,consoleLinesPerSecond:50,gameNameLength:60,gameDescriptionLength:1e3,galleryItems:5,mediaImageBytes:5*1024*1024,mediaVideoBytes:30*1024*1024,mediaGifBytes:1024*1024,mediaGifSide:512,mediaUploadsPerDay:200,mediaBytesPerDay:500*1024*1024,badgesPerGame:50,controlHints:12,controlHintKeys:4,controlKeyLength:12,controlActionLength:40,helpLength:400,badgeNameLength:50,badgeDescriptionLength:200,leaderboardsPerGame:25,leaderboardTop:100,gamesPerOwner:50,gamesCreatedPerCreatorPerDay:3,uploadAccountAgeMs:10080*6e4,deploysPerOwnerPerDay:100,draftsPerOwnerPerDay:1e3,draftsKeptPerGame:3,cliTokensPerAccount:10,cliDeviceCodeSeconds:600,deploymentsKeptPerGame:10,deployBytesPerOwnerPerDay:2*1024*1024*1024,deployEntriesPerOwnerPerDay:1e5,bioLength:500,exportRows:2e3,exportSaveChars:4*1024*1024,exportEveryMs:60*6e4,friendsMax:200,friendRequestsMax:100,friendRequestsIncomingMax:50,friendRequestsPerDay:20,friendRequestsPerNetworkPerDay:120,friendRequestPushesPerDay:5,passwordMinLength:8,passwordMaxLength:72,signupsPerNetworkPerDay:100,signupsPerWidePerDay:300,guestsPerNetworkPerDay:300,guestsPerWidePerDay:1500,visitsPerNetworkPerDay:10,visitSeconds:10,errorsPerDeployment:100,controlButtons:4,controlLabelLength:12,partySize:6,partyChatHistory:50,messageLength:500,messagesPerPage:50,groupChatMembers:20,groupChatNameLength:40,conversationsListed:50,notificationsListed:50,invitesPerRequest:10,inviteLiveMs:30*6e4,partyInviteMs:60*6e4};var Ye=/^[a-z0-9]{12}$/,ee=2;function ke(t){return`${t.mode}.${t.max}.${t.teams}.${t.ranked?1:0}${t.min!==ee?`.${t.min}`:""}`}var no=/^pub:([a-z0-9-]+)\.(\d+)\.(\d)\.([01])(?:\.(\d+))?:(\d+)$/;function Je(t){let e=no.exec(t);if(!e)return null;let o={mode:e[1],max:Number(e[2]),teams:Number(e[3]),ranked:e[4]==="1",min:e[5]===void 0?ee:Number(e[5])};return{...o,name:ke(o),n:Number(e[6])}}function Xe(t,e,o){let r=typeof t=="string"?Number(t):t;return typeof r!="number"||!Number.isInteger(r)?Math.min(o,e):Math.min(e,Math.max(1,r))}var We=t=>t.replace(/[\u0000-\u001f\u007f]/g," ").replace(/\s+/g," ").trim();var ao=/^[a-z0-9][a-z0-9-]{0,19}$/;function Ze(t){if(!t||typeof t!="object")return null;let e=t,o=[];for(let n of Array.isArray(e.buttons)?e.buttons:[]){if(o.length>=P.controlButtons)break;let{id:a,label:i}=n??{};if(typeof a!="string"||!ao.test(a)||o.some(d=>d.id===a))continue;let s=typeof i=="string"?i.replace(/[\u0000-\u001f\u007f]/g,"").replace(/\s+/g," ").trim().slice(0,P.controlLabelLength).trim():"";o.push({id:a,label:s||a})}let r=e.stick===!0;return r||o.length>0?{stick:r,zone:e.zone==="corner"?"corner":"wide",buttons:o}:null}var V="host:ow.settings",io=/^[a-z][a-zA-Z0-9]{0,15}$/;function Qe(t){if(!Array.isArray(t))return[];let e=[];for(let o of t){if(e.length>=6)break;let{id:r,label:n,options:a,default:i}=o??{};if(typeof r!="string"||!io.test(r)||e.some(c=>c.id===r)||!Array.isArray(a))continue;let s=[];for(let c of a){let p=typeof c=="object"&&c!==null?c.value:c;if(typeof p!="string"&&typeof p!="number"||typeof p=="number"&&!Number.isFinite(p)||s.length>=8||s.some(u=>u.value===p))continue;let x=typeof c=="object"&&c!==null&&typeof c.label=="string"?c.label:String(p);s.push({value:p,label:We(x).slice(0,20)||String(p)})}if(s.length<2)continue;let d=typeof n=="string"?We(n).slice(0,24):"";e.push({id:r,label:d||r,options:s,default:s.some(c=>c.value===i)?i:s[0].value})}return e}function et(t,e){let o=e!==null&&typeof e=="object"?e[t.id]:void 0;return t.options.some(r=>r.value===o)?o:t.default}var Se='{"t":"ping"}';var tt=4001,ot=4003,rt=4008,nt=4006;var at="onceworlds:hello",it="onceworlds:connect",so=/^[a-z0-9]{1,32}~(?:(?:0|[1-9]\d{0,3})\.(?:0|[1-9]\d{0,4})\.(?:0|[1-9]\d{0,5}))?~[0-9a-z]{1,11}~[lp]~[A-Za-z0-9_-]{43}$/;function st(t){let e=t!==null&&typeof t=="object"?t.build:void 0;return typeof e=="string"&&so.test(e)?e:null}var lt="onceworlds-console";var pt="allow-scripts allow-pointer-lock allow-forms allow-orientation-lock",dt=["autoplay","gamepad","screen-wake-lock","accelerometer","gyroscope"].map(t=>`${t} *`).join("; "),ct=100*1024*1024;var gt=new Set(["server","join","deploy","watch","player","embed","k","console","tester","live","demo",lt]);var ze="onceworlds:page",lo=["ran","blocked","failed","threw","loaded"];function ht(t){if(t===null||typeof t!="object")return null;let{what:e,detail:o}=t;return typeof e!="string"||!lo.includes(e)?null:{what:e,detail:typeof o=="string"?o.slice(0,200):""}}var po=`(function(){var n=0,s=function(w,d){if(n++<12)try{parent.postMessage({type:${JSON.stringify(ze)},what:w,detail:String(d).slice(0,200)},"*")}catch(e){}};s("ran",document.readyState);addEventListener("securitypolicyviolation",function(e){s("blocked",e.effectiveDirective+" "+e.blockedURI)});addEventListener("error",function(e){var t=e.target;t&&t.tagName?s("failed",t.tagName+" "+(t.src||t.href)):s("threw",e.message+" "+e.filename+":"+e.lineno)},true);addEventListener("load",function(){s("loaded",typeof onceworlds)})})();`,Yo=`<script>${po}<\/script>`;function ut(t){return t?.ownCountdown!==!0}function mt(t){let e=t?.lobby;return e==="card"||e==="bar"?e:e==="game"?null:typeof t?.minPlayers=="number"||typeof t?.minPlayersPrivate=="number"||typeof t?.countdown=="number"||Array.isArray(t?.settings)&&t.settings.length>0?"bar":null}function xt(t){return t.key==="Enter"&&!t.isComposing&&!t.ctrlKey&&!t.metaKey&&!t.altKey&&!t.shiftKey}var ft=400;var co=/^#(?:[0-9a-f]{3}){1,2}$/i,T={panel:"#111216",text:"#f7f7f8",accent:"#d33e00"},Ee=4.5,Re=3,te=["#4ade80","#15803d"];function wt(t){let e=[];if(t==null)return{look:null,notes:e};if(typeof t!="object")return{look:null,notes:["a look is { accent, panel, text } or null"]};let o=t,r=p=>{let x=o[p];return x==null?null:typeof x=="string"&&co.test(x)?yt(x):(e.push(`${p} is not #rgb or #rrggbb`),null)},n=r("panel"),a=r("text"),i=r("accent");if(n||a){let p=A(a??T.text,n??T.panel);p<Ee&&(e.push(`text on panel is ${p.toFixed(1)}:1, under ${Ee}:1: the platform's are used`),n=a=null)}let s=n??T.panel,d=a??T.text;if(i){let p=A(i,s);p<Re&&(e.push(`accent on panel is ${p.toFixed(1)}:1, under ${Re}:1: the platform's is used`),i=null)}if(!i&&n&&A(T.accent,n)<Re&&(i=d),!n&&!a&&!i)return{look:null,notes:e};let c=i?go(i):null;return{look:{panel:n,text:a,muted:n||a?ho(d,s):null,accent:i,accentHover:i?vt(i,c==="#ffffff"?"#000000":"#ffffff",.14):null,onAccent:c,ok:n?A(te[0],n)>=A(te[1],n)?te[0]:te[1]:null},notes:e}}function yt(t){let e=t.slice(1).toLowerCase();return`#${e.length===3?[...e].map(o=>o+o).join(""):e}`}function W(t){let e=yt(t);return[parseInt(e.slice(1,3),16),parseInt(e.slice(3,5),16),parseInt(e.slice(5,7),16)]}function bt(t){let[e,o,r]=W(t).map(n=>{let a=n/255;return a<=.04045?a/12.92:((a+.055)/1.055)**2.4});return .2126*e+.7152*o+.0722*r}function A(t,e){let[o,r]=[bt(t),bt(e)].sort((n,a)=>a-n);return(o+.05)/(r+.05)}function go(t){return A(t,"#000000")>A(t,"#ffffff")?"#000000":"#ffffff"}function vt(t,e,o){let[r,n]=[W(t),W(e)];return`#${r.map((a,i)=>Math.round(a+(n[i]-a)*o).toString(16).padStart(2,"0")).join("")}`}function ho(t,e){for(let o=.3;o>.001;o-=.05){let r=vt(t,e,o);if(A(r,e)>=Ee)return r}return t}var oe=class{port=null;handlers;events;onConnect;solo;demo;onActivity;onPage;constructor(e,o,r,n,a={}){this.handlers=o,this.events=r,this.onConnect=n,this.solo=a.solo===!0,this.demo=a.demo===!0,this.onActivity=a.onActivity??null,this.onPage=a.onPage??null,window.addEventListener("message",i=>{if(!(i.source!==e.contentWindow||i.origin!=="null")&&(i.data?.type===at&&this.connect(i.source,st(i.data)),i.data?.type===ze)){let s=ht(i.data);s&&this.onPage?.(s)}})}activity(e,o){try{this.onActivity?.(e,o)}catch{}}emit(e,o){this.port?.postMessage({kind:"event",name:e,data:o})}connect(e,o){this.port?.close();let r=new MessageChannel,n=r.port1;this.port=n,n.onmessage=i=>this.receive(n,i.data);let a={type:it,...this.solo?{solo:!0}:{},...this.demo?{demo:!0}:{}};e.postMessage(a,"*",[r.port2]),this.onConnect(o)}async receive(e,o){if(!(e!==this.port||!o||typeof o!="object")){if(o.kind==="event"){Object.hasOwn(this.events,o.name)&&(this.events[o.name](o.data),this.activity(String(o.name),null));return}if(o.kind==="req")try{if(!Object.hasOwn(this.handlers,o.method))throw new Error(`Unknown method: ${String(o.method)}`);let r=this.handlers[o.method],n=await r(o.params);e.postMessage({kind:"res",id:o.id,ok:!0,result:n??null}),this.activity(String(o.method),null)}catch(r){let n=r instanceof Error?r.message:String(r);e.postMessage({kind:"res",id:o.id,ok:!1,error:n}),this.activity(String(o.method),n)}}}};var uo="ow-game-storage",mo=256,kt="guest";function Pe(t){let e=0,o=new Set,r=[t];for(;r.length;){let n=r.pop();if(typeof n=="string")e+=n.length*2;else if(typeof n!="object"||n===null)e+=8;else{if(o.has(n))continue;if(o.add(n),n instanceof ArrayBuffer)e+=n.byteLength;else if(ArrayBuffer.isView(n))e+=n.buffer.byteLength;else if(typeof Blob=="function"&&n instanceof Blob)e+=n.size;else if(n instanceof Map)for(let[a,i]of n)r.push(a,i);else if(n instanceof Set)for(let a of n)r.push(a);else if(Array.isArray(n))r.push(...n.slice(0,1e5),...n.length>1e5?[n.slice(1e5)]:[]);else for(let[a,i]of Object.entries(n))r.push(a,i)}}return e}function L(t){if(typeof t!="string"||t.length>mo)throw new Error("Bad name");return t}function St(t){if(t===null)return null;if(typeof t=="string")return L(t);if(Array.isArray(t)&&t.length<=32)return t.map(L);throw new Error("Bad key path")}function xo(t){if(typeof t!="object"||t===null)throw new Error("Bad stores");let e=Object.keys(t);if(e.length>1e3)throw new Error("Too many stores");let o={};for(let r of e){let n=t[L(r)];if(typeof n!="object"||n===null||!Array.isArray(n.indexes)||n.indexes.length>1e3)throw new Error("Bad store");let a=Number(n.nextKey),i=n.indexes.map(s=>{let d=St(s?.keyPath);if(d===null)throw new Error("Bad index");return{name:L(s.name),keyPath:d,unique:s.unique===!0,multiEntry:s.multiEntry===!0}});o[r]={keyPath:St(n.keyPath),autoIncrement:n.autoIncrement===!0,nextKey:Number.isFinite(a)?a:1,indexes:i}}return o}function k(t){return new Promise((e,o)=>{t.onsuccess=()=>e(t.result),t.onerror=()=>o(t.error??new Error("Storage failed"))})}function Me(t){return new Promise((e,o)=>{t.oncomplete=()=>e(),t.onabort=t.onerror=()=>o(t.error??new Error("Storage failed"))})}var re=class{database=null;named=null;owner;factory;limit;constructor(e,o={}){this.owner=e,this.factory=o.factory??(()=>indexedDB),this.limit=o.limit??ct}open(){return this.database??=new Promise((e,o)=>{let r=this.factory().open(uo,1);r.onupgradeneeded=()=>{r.result.createObjectStore("dbs",{keyPath:["owner","db"]}),r.result.createObjectStore("records")},r.onsuccess=()=>e(r.result),r.onerror=()=>o(r.error??new Error("No storage in this browser"))}),this.database}who(){return this.named??=this.owner().then(async({game:e,player:o})=>{let r=`${e}
2
+ ${o??kt}`;return o!==null&&await this.adopt(`${e}
3
+ ${kt}`,r),r}),this.named}async adopt(e,o){let n=(await this.open()).transaction(["dbs","records"],"readwrite"),a=Me(n),i=n.objectStore("dbs"),s=n.objectStore("records"),d=await k(i.getAll(this.ownerRange(e)));for(let c of d){let p=this.dbRange(e,c.db);if(!await k(i.getKey([o,c.db]))){let x=await k(s.getAllKeys(p));for(let u of x){let E=await k(s.get(u));s.put(E,[o,...u.slice(1)])}i.put({...c,owner:o})}s.delete(p),i.delete([e,c.db])}await a}ownerRange(e){return IDBKeyRange.bound([e],[`${e}\0`],!1,!0)}dbRange(e,o){return IDBKeyRange.bound([e,o],[e,`${o}\0`],!1,!0)}storeRange(e,o,r){return IDBKeyRange.bound([e,o,r],[e,o,`${r}\0`],!1,!0)}async read({db:e}){L(e);let o=await this.who(),n=(await this.open()).transaction(["dbs","records"]),a=await k(n.objectStore("dbs").get([o,e]));if(!a)return null;let i=n.objectStore("records"),s=this.dbRange(o,e),[d,c]=await Promise.all([k(i.getAllKeys(s)),k(i.getAll(s))]);return{version:a.version,stores:a.stores,records:d.map((p,x)=>({store:p[2],key:p[3],value:c[x].value}))}}async commit(e){let o=L(e?.db),r=Number(e.version);if(!Number.isFinite(r)||r<0)throw new Error("Bad version");let n=e.ops;if(!Array.isArray(n))throw new Error("Bad changes");let a=e.stores===void 0?void 0:xo(e.stores),i=await this.who(),d=(await this.open()).transaction(["dbs","records"],"readwrite"),c=Me(d),p=d.objectStore("dbs"),x=d.objectStore("records");try{let u=await k(p.getAll(this.ownerRange(i))),E=u.find(l=>l.db===o)??{owner:i,db:o,version:r,stores:{},bytes:0},q=u.reduce((l,f)=>l+(f===E?0:f.bytes),0),b=E.bytes;for(let l of n){let f=L(l?.store);if("clear"in l){b-=await this.sum(x,this.storeRange(i,o,f)),x.delete(this.storeRange(i,o,f));continue}let ye=[i,o,f,"put"in l?l.put:l.remove],Ge=await k(x.get(ye));if(Ge&&(b-=Ge.bytes),"put"in l){let Ue=Pe(l.value)+Pe(l.put)+64;b+=Ue,x.put({value:l.value,bytes:Ue},ye)}else x.delete(ye)}if(a)for(let l of Object.keys(E.stores))Object.hasOwn(a,l)||(b-=await this.sum(x,this.storeRange(i,o,l)),x.delete(this.storeRange(i,o,l)));if(q+=Pe(a??E.stores),q+b>this.limit)throw new Error("QuotaExceededError");p.put({owner:i,db:o,version:r,stores:a??E.stores,bytes:Math.max(0,b)})}catch(u){try{d.abort()}catch{}throw c.catch(()=>{}),u}return await c,null}async sum(e,o){return(await k(e.getAll(o))).reduce((n,a)=>n+a.bytes,0)}async remove({db:e}){L(e);let o=await this.who(),n=(await this.open()).transaction(["dbs","records"],"readwrite"),a=Me(n);return n.objectStore("records").delete(this.dbRange(o,e)),n.objectStore("dbs").delete([o,e]),await a,null}async list(){let e=await this.who(),o=await this.open();return(await k(o.transaction("dbs").objectStore("dbs").getAll(this.ownerRange(e)))).map(n=>({name:n.db,version:n.version}))}};var ne=class{pressed=new Set;press(e){this.pressed.add(e)}take(e){let o=new Set(e),r=!1;for(let n of this.pressed)o.has(n)||(o.add(n),r=!0);return this.pressed.clear(),{buttons:[...o],owed:r}}};var zt=["--ow-panel","--ow-face-edge","--ow-hover","--ow-on-accent","--text","--muted","--line","--line-strong","--fill","--fill-strong","--ok","--accent-fill","--accent-fill-hover","--accent-hover"],Rt=["--ow-ctl-edge","--ow-ctl-fill","--ow-ctl-stick","--ow-ctl-knob","--ow-ctl-label","--ow-ctl-shadow","--ow-ctl-held","--ow-ctl-main","--ow-ctl-main-edge","--ow-ctl-main-label","--ow-ctl-main-shadow","--ow-ctl-main-held"];function y(t,e){return`rgba(${W(t).join(", ")}, ${e})`}function Et(t){let e={};if(!t)return e;if(t.panel&&(e["--ow-panel"]=t.panel,e["--ow-face-edge"]=t.panel),t.ok&&(e["--ok"]=t.ok),t.panel||t.text){let o=t.text??T.text;t.text&&(e["--text"]=t.text),t.muted&&(e["--muted"]=t.muted),e["--line"]=y(o,.09),e["--line-strong"]=y(o,.16),e["--fill"]=y(o,.06),e["--fill-strong"]=y(o,.1),e["--ow-hover"]=y(o,.16)}return t.accent&&t.accentHover&&t.onAccent&&(e["--accent-fill"]=t.accent,e["--accent-fill-hover"]=t.accentHover,e["--accent-hover"]=t.accent,e["--ow-on-accent"]=t.onAccent),e}function Pt(t){let e={};return t&&(t.text&&(e["--ow-ctl-edge"]=y(t.text,.4),e["--ow-ctl-knob"]=y(t.text,.9),e["--ow-ctl-label"]=t.text,e["--ow-ctl-held"]=y(t.text,.35)),t.panel&&(e["--ow-ctl-fill"]=y(t.panel,.4),e["--ow-ctl-stick"]=y(t.panel,.3),e["--ow-ctl-shadow"]=y(t.panel,.5)),t.accent&&t.accentHover&&t.onAccent&&(e["--ow-ctl-main"]=y(t.accent,.85),e["--ow-ctl-main-edge"]=t.accent,e["--ow-ctl-main-label"]=t.onAccent,e["--ow-ctl-main-shadow"]="none",e["--ow-ctl-main-held"]=t.accentHover)),e}function ae(t,e,o){for(let r of e)Object.hasOwn(o,r)?t.style.setProperty(r,o[r]):t.style.removeProperty(r);t.toggleAttribute("data-look",e.some(r=>Object.hasOwn(o,r)))}var $=52,Ae=.12,Le=96,fo=400,bo=2e4,ie=class{parent;root;zone;base;knob;buttonBox;send;onLayout;layout=null;touch;blocked=!1;keyboard=!1;stickPointer=null;center={x:0,y:0};x=0;y=0;buttons=new Map;held=new Map;fingers=new Map;report=new ne;queued=0;sent="";placed={stick:null,buttons:null};constructor(e,o,r=()=>{}){this.send=o,this.onLayout=r,this.parent=e,this.touch=matchMedia("(pointer: coarse)").matches,this.root=D("div","ow-controls"),this.root.hidden=!0,this.zone=D("div","ow-stick-zone"),this.base=D("div","ow-stick"),this.knob=D("div","ow-stick-knob"),this.base.append(this.knob),this.zone.append(this.base),this.buttonBox=D("div","ow-control-buttons"),this.root.append(this.zone,this.buttonBox),e.append(this.root),this.zone.addEventListener("pointerdown",n=>this.stickDown(n)),this.zone.addEventListener("pointermove",n=>this.stickMove(n));for(let n of["pointerup","pointercancel","lostpointercapture"])this.zone.addEventListener(n,a=>this.stickUp(a));this.root.addEventListener("contextmenu",n=>n.preventDefault())}setLayout(e){this.layout=Ze(e);let o=this.buttons;this.buttons=new Map,(this.layout?.buttons??[]).forEach((r,n)=>{let a=o.get(r.id)??this.button(r.id);o.delete(r.id),a.className=`ow-control-btn ow-control-btn-${n}`,a.classList.toggle("ow-held",[...this.held.values()].includes(r.id)),a.textContent=r.label,a.isConnected||this.buttonBox.append(a),this.buttons.set(r.id,a)});for(let[r,n]of o){for(let[a,i]of this.held)i===r&&this.letGo(a);n.remove()}this.zone.hidden=!this.layout?.stick,this.zone.classList.toggle("ow-stick-corner",this.layout?.zone==="corner"),this.zone.hidden&&this.stickPointer!==null&&this.letGo(this.stickPointer),this.render(),this.onLayout()}setLook(e){ae(this.root,Rt,e)}reservedBoxes(){if(this.root.hidden)return this.blocked&&this.layout&&this.touch&&!this.keyboard?this.placed:{stick:null,buttons:null};let e=this.zone.hidden?null:At(this.base),o=null;for(let r of this.buttons.values()){let n=At(r);!n.width||!n.height||(o=o?yo(o,n):n)}return this.placed={stick:e,buttons:o},this.placed}setTouch(e){this.touch=e,this.render()}setKeyboard(e){this.keyboard=e,this.render()}setBlocked(e){this.blocked=e,this.render()}render(){let e=!!this.layout&&this.touch&&!this.blocked&&!this.keyboard;this.root.hidden!==!e&&(e||this.reset(),this.root.hidden=!e,this.onLayout())}reset(){for(let e of[...this.held.keys()])this.letGo(e);this.stickPointer!==null&&this.letGo(this.stickPointer),this.endStick()}letGo(e){e===this.stickPointer&&this.endStick();let o=this.held.get(e);this.held.delete(e),o!==void 0&&![...this.held.values()].includes(o)&&this.buttons.get(o)?.classList.remove("ow-held");let r=this.fingers.get(e);this.fingers.delete(e),r&&wo(this.parent,e,r),this.changed()}button(e){let o=D("button","ow-control-btn");o.type="button",o.tabIndex=-1,o.addEventListener("pointerdown",n=>{n.preventDefault(),Mt(o,n.pointerId),o.classList.add("ow-held"),this.held.set(n.pointerId,e),this.report.press(e),this.fingers.set(n.pointerId,{x:n.clientX,y:n.clientY}),this.changed()}),o.addEventListener("pointermove",n=>{this.held.get(n.pointerId)===e&&this.fingers.set(n.pointerId,{x:n.clientX,y:n.clientY})});let r=n=>{this.held.get(n.pointerId)===e&&(this.held.delete(n.pointerId),this.fingers.delete(n.pointerId),[...this.held.values()].includes(e)||o.classList.remove("ow-held"),this.changed())};for(let n of["pointerup","pointercancel","lostpointercapture"])o.addEventListener(n,a=>r(a));return o}stickDown(e){if(this.stickPointer!==null)return;e.preventDefault(),Mt(this.zone,e.pointerId),this.stickPointer=e.pointerId,this.fingers.set(e.pointerId,{x:e.clientX,y:e.clientY});let o=this.zone.getBoundingClientRect(),r=this.base.getBoundingClientRect(),n=$+8;this.center={x:Math.min(Math.max(e.clientX,o.left+n),o.right-n),y:Math.min(Math.max(e.clientY,o.top+n),o.bottom-n)},this.base.style.transform=`translate(${this.center.x-(r.left+r.width/2)}px, ${this.center.y-(r.top+r.height/2)}px)`,this.base.classList.add("ow-active"),this.stickMove(e)}stickMove(e){if(e.pointerId!==this.stickPointer)return;this.fingers.set(e.pointerId,{x:e.clientX,y:e.clientY});let o=e.clientX-this.center.x,r=e.clientY-this.center.y,n=Math.hypot(o,r);n>$&&(o=o/n*$,r=r/n*$),this.knob.style.transform=`translate(${o}px, ${r}px)`;let a=Math.min(n,$)/$,i=a<Ae?0:(a-Ae)/(1-Ae)/(a||1);this.x=Lt(o/$*i),this.y=Lt(r/$*i),this.changed()}stickUp(e){e.pointerId===this.stickPointer&&(this.fingers.delete(e.pointerId),this.endStick())}endStick(){this.stickPointer=null,this.x=0,this.y=0,this.base.classList.remove("ow-active"),this.base.style.transform="",this.knob.style.transform="",this.changed()}changed(){this.queued||(this.queued=requestAnimationFrame(()=>{this.queued=0;let{buttons:e,owed:o}=this.report.take(this.held.values()),r={x:this.x,y:this.y,buttons:e};o&&this.changed();let n=JSON.stringify(r);n!==this.sent&&(this.sent=n,this.send(r))}))}};function wo(t,e,o){let r=D("div","ow-tap-shield");r.style.left=`${o.x-Le/2}px`,r.style.top=`${o.y-Le/2}px`,r.style.width=r.style.height=`${Le}px`,t.append(r);let n=setTimeout(()=>r.remove(),bo),a=()=>{clearTimeout(n),n=setTimeout(()=>r.remove(),fo)};for(let i of["pointerup","pointercancel","lostpointercapture"])r.addEventListener(i,s=>s.pointerId===e&&a());r.addEventListener("contextmenu",i=>i.preventDefault());try{r.setPointerCapture(e)}catch{a()}}function Mt(t,e){try{t.setPointerCapture(e)}catch{}}function At(t){let e=t.getBoundingClientRect(),o=getComputedStyle(t).transform;if(!o||o==="none")return e;let r=new DOMMatrixReadOnly(o);if(r.b||r.c||!r.a||!r.d)return e;let n=e.width/Math.abs(r.a),a=e.height/Math.abs(r.d),i=e.left+e.width/2-r.e,s=e.top+e.height/2-r.f;return{left:i-n/2,top:s-a/2,width:n,height:a}}function yo(t,e){let o=Math.min(t.left,e.left),r=Math.min(t.top,e.top);return{left:o,top:r,width:Math.max(t.left+t.width,e.left+e.width)-o,height:Math.max(t.top+t.height,e.top+e.height)-r}}function Lt(t){return Math.round(t*1e3)/1e3}function D(t,e,o){let r=document.createElement(t);return r.className=e,o!==void 0&&(r.textContent=o),r}function Y(t,e){let o=[];for(let{kind:r,box:n}of e){if(!n||!(n.width>0)||!(n.height>0))continue;let a=Math.max(0,n.left-t.left),i=Math.max(0,n.top-t.top),s=Math.min(t.width,n.left+n.width-t.left),d=Math.min(t.height,n.top+n.height-t.top);if(!(s-a>.01&&d-i>.01))continue;let c=Math.floor(a+.01),p=Math.floor(i+.01);o.push({kind:r,x:c,y:p,w:Math.ceil(s-.01)-c,h:Math.ceil(d-.01)-p})}return o}var se=class{measure;send;nextFrame;queued=!1;sent=null;constructor(e,o,r=n=>requestAnimationFrame(n)){this.measure=e,this.send=o,this.nextFrame=r}changed(){this.queued||(this.queued=!0,this.nextFrame(()=>{this.queued=!1;let e=this.measure();if(!e)return;let o=JSON.stringify(e);o!==this.sent&&(this.sent=o,this.send(e))}))}resend(){this.sent=null,this.changed()}};var $t=t=>Math.max(0,Math.ceil(t/1e3)),$e=t=>{let e=t.slice(0,2).map(o=>o.name);return t.length>2?`${e.join(", ")} +${t.length-2}`:e.join(", ")};function vo(t){let{you:e,host:o,kind:r,match:n,now:a}=t,i=[...t.players].sort((l,f)=>l.id===e?-1:f.id===e?1:l.id===o?-1:f.id===o?1:l.name.localeCompare(f.name)).map(l=>({id:l.id,name:l.name,ready:!!l.ready,host:l.id===o,you:l.id===e,away:l.connected===!1,idle:l.connected!==!1&&!!l.idle})),s=t.players.filter(l=>l.connected!==!1),d=t.players.find(l=>l.id===e),c=o===e&&n.phase==="lobby",p=t.schema.map(l=>({id:l.id,label:l.label,value:et(l,t.written),options:l.options,editable:c})),x={n:s.filter(l=>l.ready||r!=="public"&&l.id===o).length,of:s.length},u={surface:"none",rows:i,count:`${t.players.length} / ${t.max}`,ready:x,status:"",action:{kind:"none",label:"",disabled:!0},settings:p,seconds:null},E=n.participants?.includes(e)??!1,q=o===e&&r!=="public";if(n.phase==="starting"){if(!E)return{...u,surface:"watching",status:"Watching"};let l=q?{kind:"stop",label:"Stop",disabled:!1}:{kind:"unready",label:"Not ready",disabled:!1};return t.ownCountdown?{...u,surface:"countdown",action:l,seconds:null,status:"Get ready"}:{...u,surface:"countdown",action:l,seconds:Math.max(1,$t((n.startsAt??a)-a)),status:"Get ready"}}if(n.phase==="playing"){if(n.paused){let l=s.filter(f=>n.participants?.includes(f.id)).length;return n.paused.reason==="host"?{...u,surface:"paused",status:"Paused"}:{...u,surface:"paused",seconds:$t(n.paused.since+P.matchAbandonMs-a),status:`Waiting for players \xB7 ${l} / ${n.min}`}}return E?u:{...u,surface:"watching",status:"Watching"}}let b=s.filter(l=>!l.ready&&(r==="public"||l.id!==o));if(u.surface="lobby",q){let l=s.length<n.min;return u.action={kind:"start",label:"Start",disabled:l||b.length>0},u.status=l?`Need ${n.min} players`:b.length?`Waiting for ${$e(b)}`:"",u}if(u.action={kind:d?.ready?"unready":"ready",label:d?.ready?"Not ready":"Ready",disabled:!1},r==="public")s.length<n.min?u.status=`Need ${n.min} players`:n.waitUntil!==void 0&&b.length?u.status=b.some(l=>l.id===e)?"Waiting for you":`Waiting for ${$e(b)}`:u.status=`${s.length-b.length} / ${s.length} ready`;else{let l=b.filter(f=>f.id!==e);u.status=d?.ready?l.length?`Waiting for ${$e(l)}`:`Waiting for ${t.players.find(f=>f.id===o)?.name??"the host"}`:""}return u}function h(t,e="",o){let r=document.createElement(t);return e&&(r.className=e),o!==void 0&&(r.textContent=o),r}var le=class{mode=null;ownCountdown=!1;targetOk=!1;targetTimer=0;lastKey=-1/0;state=null;schema=[];timer=0;opts;root;card;title;count;list;settingsBox;status;action;invite;bar;barFaces;barStatus;barAction;countdown;numeral;cancel;pause;pauseStatus;pauseTimer;chip;last=null;lastNumeral=-1;listSignature="";settingsSignature="";faceSignature="";constructor(e,o){this.opts=o,this.root=h("div","ow-lobby"),this.root.hidden=!0;for(let s of["pointerdown","mousedown"])this.root.addEventListener(s,d=>d.preventDefault());this.card=h("section","ow-lobby-card"),this.card.setAttribute("aria-label","Lobby");let r=h("header","ow-lobby-head");this.title=h("h2","","Lobby"),this.count=h("span","ow-lobby-count"),r.append(this.title,this.count),this.list=h("ul","ow-lobby-players"),this.settingsBox=h("div","ow-lobby-settings"),this.status=h("p","ow-lobby-status"),this.status.setAttribute("role","status"),this.action=this.button("ow-btn ow-btn-primary ow-lobby-action",()=>this.press()),this.invite=this.button("ow-btn ow-lobby-invite",()=>this.opts.onInvite()),this.invite.textContent="Invite";let n=h("div","ow-lobby-actions");n.append(this.action,this.invite),this.card.append(r,this.list,this.settingsBox,this.status,n),this.bar=h("div","ow-lobby-bar"),this.barFaces=h("span","ow-lobby-faces"),this.barStatus=h("span","ow-lobby-bar-status"),this.barStatus.setAttribute("role","status"),this.barAction=this.button("ow-btn ow-btn-primary ow-lobby-action",()=>this.press()),this.bar.append(this.barFaces,this.barStatus,this.barAction),this.countdown=h("div","ow-lobby-countdown"),this.numeral=h("b"),this.numeral.setAttribute("aria-hidden","true");let a=h("span","","Get ready");a.setAttribute("aria-hidden","true"),this.cancel=this.button("ow-btn ow-lobby-cancel",()=>this.press()),this.countdown.append(this.numeral,a,this.cancel),this.pause=h("div","ow-lobby-pause");let i=h("div","ow-lobby-pause-box");this.pauseStatus=h("p","ow-lobby-pause-status"),this.pauseStatus.setAttribute("role","status"),this.pauseTimer=h("p","ow-lobby-pause-timer"),i.append(this.pauseStatus,this.pauseTimer),this.pause.append(i),this.chip=h("div","ow-lobby-chip"),this.chip.setAttribute("role","status"),this.root.append(this.card,this.bar,this.countdown,this.pause,this.chip),e.append(this.root)}setSchema(e){this.schema=e,this.render()}enable(e,o={}){this.mode=e,this.ownCountdown=o.ownCountdown===!0,this.root.dataset.mode=e??"",e||this.reset(),this.render()}setLook(e){ae(this.root,zt,e)}reservedBox(){if(this.root.hidden)return null;let e=!this.countdown.hidden&&this.countdown.classList.contains("ow-lobby-countdown-bare"),o=this.bar.hidden?e&&!this.cancel.hidden?this.cancel:null:this.bar;return o?o.getBoundingClientRect():null}link(e){let o=!this.root.hidden&&(this.last?.surface==="lobby"||this.last?.surface==="countdown"),r=this.pressable()?.getBoundingClientRect()??null;if(!(r!==null&&r.left>=e.left&&r.top>=e.top&&r.left+r.width<=e.left+e.width&&r.top+r.height<=e.top+e.height))return{keys:o,button:null,cover:null};let[a]=Y(e,[{kind:"lobby",box:r}]),[i]=this.mode==="bar"?Y(e,[{kind:"lobby",box:this.bar.getBoundingClientRect()}]):[{x:0,y:0,w:e.width,h:e.height}];return!a||!i?{keys:o,button:null,cover:null}:{keys:o,button:{x:a.x,y:a.y,w:a.w,h:a.h},cover:{x:i.x,y:i.y,w:i.w,h:i.h}}}setTarget(e,o){window.clearTimeout(this.targetTimer),e&&(this.targetTimer=window.setTimeout(()=>this.setTarget(!1,!1),1600)),this.targetOk!==e&&(this.targetOk=e,this.applyTarget()),this.pressable()?.classList.toggle("ow-lobby-hover",e&&o)}pressKey(e=performance.now()){this.pressable()===null||e-this.lastKey<ft||(this.lastKey=e,this.press())}pressTarget(){this.pressable()&&this.press()}setKeyMark(e){this.root.classList.toggle("ow-lobby-keys",e)}pressable(){let e=this.last;return this.root.hidden||!e||e.surface!=="lobby"||e.action.kind==="none"||e.action.disabled?null:this.mode==="card"?this.action:this.barAction}applyTarget(){let e=this.targetOk&&this.pressable()!==null;if(this.root.classList.toggle("ow-lobby-through",e),!e)for(let o of[this.action,this.barAction])o.classList.remove("ow-lobby-hover")}setRoom(e,o=0){if(!e)return this.reset();this.state={you:e.you,host:e.host,kind:e.room.startsWith("private:")?"private":e.room.startsWith("room:")?"named":"public",max:Math.max(o,e.players.length),players:e.players.map(r=>({...r})),match:e.match??{phase:"lobby",n:0,min:1},written:e.state?.[V]},this.render()}handle(e){let o=this.state;if(o){switch(e.t){case"join":o.players=[...o.players.filter(r=>r.id!==e.player.id),{...e.player}];break;case"leave":o.players=o.players.filter(r=>r.id!==e.id);break;case"away":o.players=o.players.map(r=>r.id===e.id?{...r,connected:!1}:r);break;case"back":o.players=o.players.map(r=>{if(r.id!==e.id)return r;let{connected:n,...a}=r;return a});break;case"ready":o.players=o.players.map(r=>{if(r.id!==e.id)return r;let{ready:n,...a}=r;return e.ready?{...a,ready:!0}:a});break;case"unready":o.players=o.players.map(({ready:r,...n})=>n);break;case"host":o.host=e.id;break;case"idle":o.players=o.players.map(r=>{if(r.id!==e.id)return r;let{idle:n,...a}=r;return e.idle?{...a,idle:!0}:a});break;case"name":o.players=o.players.map(r=>r.id===e.id?{...r,name:e.name}:r);break;case"match":o.match=e.match;break;case"state":if(e.k!==V)return;o.written=e.v;break;default:return}this.render()}}chooseSetting(e,o){let r=this.state,n=this.schema.find(i=>i.id===e);if(!r||!n||!n.options.some(i=>i.value===o))return null;let a={...r.written!==null&&typeof r.written=="object"?r.written:{},[e]:o};return r.written=a,this.render(),a}setReadyLocal(e){let o=this.state?.players.find(r=>r.id===this.state?.you);o&&(e?o.ready=!0:delete o.ready,this.render())}button(e,o){let r=h("button",e);return r.type="button",r.addEventListener("pointerdown",n=>n.preventDefault()),r.addEventListener("click",o),r}press(){let e=this.last?.action.kind;e==="start"?this.opts.onStart():e==="stop"?this.opts.onStop():e==="ready"?(this.setReadyLocal(!0),this.opts.onReady(!0)):e==="unready"&&(this.setReadyLocal(!1),this.opts.onReady(!1)),this.opts.onPressed?.()}reset(){this.state=null,this.last=null,this.setTarget(!1,!1),window.clearInterval(this.timer),this.timer=0,this.root.hidden=!0,this.opts.onLayout?.()}render(){this.draw(),this.opts.onLayout?.()}draw(){if(!this.mode||!this.state){this.root.hidden=!0;return}let e=vo({...this.state,schema:this.schema,now:this.opts.now(),ownCountdown:this.ownCountdown});this.last=e;let o=e.surface==="lobby";if(this.root.hidden=e.surface==="none",this.card.hidden=!(o&&this.mode==="card"),this.bar.hidden=!(o&&this.mode==="bar"),this.countdown.hidden=e.surface!=="countdown",this.countdown.classList.toggle("ow-lobby-countdown-bare",e.surface==="countdown"&&e.seconds===null),this.pause.hidden=e.surface!=="paused",this.chip.hidden=e.surface!=="watching",this.root.classList.toggle("ow-lobby-dim",o&&this.mode==="card"),o&&this.mode==="card"&&this.renderCard(e),o&&this.mode==="bar"&&this.renderBar(e),this.applyTarget(),e.surface==="countdown"&&e.seconds!==null&&e.seconds!==this.lastNumeral&&(this.lastNumeral=e.seconds,this.numeral.textContent=String(e.seconds),this.numeral.style.animation="none",this.numeral.offsetWidth,this.numeral.style.animation=""),e.surface==="countdown"&&(this.cancel.textContent=e.action.label),e.surface!=="countdown"&&(this.lastNumeral=-1),e.surface==="paused"&&(this.pauseStatus.textContent=e.status,this.pauseTimer.textContent=e.seconds!==null?`Ends in ${ko(e.seconds)}`:""),e.surface==="watching"){let[n,...a]=e.status.split(" \xB7 "),i=h("span","ow-lobby-chip-more",a.length?` \xB7 ${a.join(" \xB7 ")}`:"");this.chip.replaceChildren(n,i)}let r=e.seconds!==null;r&&!this.timer?this.timer=window.setInterval(()=>this.render(),250):!r&&this.timer&&(window.clearInterval(this.timer),this.timer=0)}renderCard(e){this.count.textContent=e.count,this.settingsBox.hidden=e.settings.length===0,this.status.textContent=e.status,this.status.hidden=e.status==="",this.setAction(this.action,e);let o=JSON.stringify(e.settings);o!==this.settingsSignature&&(this.settingsSignature=o,this.settingsBox.replaceChildren(...e.settings.map(n=>this.settingRow(n))));let r=JSON.stringify(e.rows);r!==this.listSignature&&(this.listSignature=r,this.list.replaceChildren(...e.rows.map(n=>{let a=h("li",n.away||n.idle?"ow-lobby-away":n.ready?"ow-lobby-ready":""),i=h("img","ow-headshot");i.src=`/avatar/${encodeURIComponent(n.id)}/head.svg`,i.alt="";let s=h("span","ow-lobby-name",n.name),d=[n.away?"Reconnecting":n.idle?"Idle":"",n.you?"You":"",n.host?"Host":""].filter(Boolean).join(" \xB7 ");return a.append(i,s),d&&a.append(h("span","ow-player-notes",d)),a.append(h("span",`ow-lobby-check${n.ready?" ow-lobby-check-on":""}`,n.ready?"Ready":"")),a})))}settingRow(e){let o=h("div","ow-lobby-setting");if(o.append(h("span","ow-lobby-setting-label",e.label)),!e.editable)return o.append(h("span","ow-lobby-setting-value",e.options.find(n=>n.value===e.value)?.label??String(e.value))),o;let r=h("div","ow-segments");r.setAttribute("role","radiogroup"),r.setAttribute("aria-label",e.label);for(let n of e.options){let a=this.button(`ow-segment${n.value===e.value?" ow-segment-on":""}`,()=>this.opts.onSetting(e.id,n.value));a.textContent=n.label,a.setAttribute("role","radio"),a.setAttribute("aria-checked",String(n.value===e.value)),r.append(a)}return o.append(r),o}renderBar(e){this.barStatus.textContent=e.status||`${e.ready.n} / ${e.ready.of} ready`,this.setAction(this.barAction,e);let o=JSON.stringify(e.rows.slice(0,5).map(r=>[r.id,r.ready]));o!==this.faceSignature&&(this.faceSignature=o,this.barFaces.replaceChildren(...e.rows.slice(0,5).map(r=>{let n=h("img",`ow-headshot${r.ready?" ow-lobby-face-ready":""}`);return n.src=`/avatar/${encodeURIComponent(r.id)}/head.svg`,n.alt="",n})))}setAction(e,o){e.hidden=o.action.kind==="none",e.textContent=o.action.label,e.disabled=o.action.disabled,e.setAttribute("aria-keyshortcuts","Enter"),e.classList.toggle("ow-btn-primary",o.action.kind!=="unready")}};function ko(t){return`${Math.floor(t/60)}:${String(t%60).padStart(2,"0")}`}var Ce=t=>`ow:resume:${t}`;function I(t,e){try{e?sessionStorage.setItem(Ce(t),JSON.stringify({...e,at:Date.now()})):sessionStorage.removeItem(Ce(t))}catch{}}function Ct(t){try{let e=sessionStorage.getItem(Ce(t));if(!e)return null;let o=performance.getEntriesByType("navigation")[0];if(o&&o.type!=="reload"&&o.type!=="back_forward")return pe(t);let r=JSON.parse(e);return typeof r.key!="string"||typeof r.rejoin!="string"||typeof r.at!="number"||Date.now()-r.at>18e4||Number(r.rejoin.split(".")[0])*1e3<Date.now()?pe(t):{key:r.key,rejoin:r.rejoin}}catch{return null}}var So=t=>`ow:own-server:${t}`;function Tt(t,e){try{sessionStorage.setItem(So(t),e)}catch{}}function pe(t){return I(t,null),null}var de=class extends Error{constructor(o,r=0,n=0,a=""){super(o);this.status=r;this.retryAfterMs=n;this.code=a;this.name="RequestError"}status;retryAfterMs;code;get retryable(){return this.status===0||this.status===408||this.status===429||this.status>=500}};var ce=class{constructor(e=48*1024){this.max=e}max;used=0;async run(e,o){let r=new TextEncoder().encode(e).byteLength;if(this.used+r>this.max)throw new de("Progress is waiting to sync when you return to the game.");this.used+=r;try{return await o()}finally{this.used-=r}}};var Te=/^[2-9]$/.test(new URLSearchParams(location.search).get("player")??"")?new URLSearchParams(location.search).get("player"):null;var zo=null;function Dt(){return zo?.n??null}var Mr=new ce;var Ot=[300,700,1500,3e3,5e3,8e3],Ro=9e4,Eo=1e4,It=()=>Array.from(crypto.getRandomValues(new Uint8Array(10)),t=>"abcdefghijklmnopqrstuvwxyz0123456789"[t%36]).join(""),Bt=It();function jt(){Bt=It()}var _t=null;var Nt=!1;function Gt(t){Nt=t}var Po=5e4,Mo=4e3,ge=class{ws=null;key=null;rejoin=null;you="";closed=!1;attempt=0;droppedAt=0;sleeping=!1;lastHeard=0;heardCount=0;pingAt=0;hintedHidden=!1;pingTimer=0;watchTimer=0;retryTimer=0;page=Bt;slug;options;callbacks;onVisible=()=>{this.hint(),document.visibilityState==="visible"&&this.wake()};onOnline=()=>this.wake();onShow=e=>e.persisted&&this.wake();onHide=()=>this.hint();constructor(e,o,r){this.slug=e,this.options=o,this.callbacks=r,this.key=o.key??null,this.rejoin=o.rejoin??null,document.addEventListener("visibilitychange",this.onVisible),addEventListener("online",this.onOnline),addEventListener("pageshow",this.onShow),addEventListener("pagehide",this.onHide)}join(){return new Promise((e,o)=>this.open(e,o))}send(e){this.ws?.readyState===WebSocket.OPEN&&this.ws.send(JSON.stringify(e))}hint(){let e=document.visibilityState==="hidden";e===this.hintedHidden||this.ws?.readyState!==WebSocket.OPEN||(this.hintedHidden=e,this.send({t:"vis",hidden:e}))}ping(){let e=this.ws;e?.readyState===WebSocket.OPEN&&(this.pingAt&&Date.now()-this.pingAt<5e3||(this.pingAt=Date.now(),e.send(Se)))}close(){this.closed=!0,I(this.slug,null),this.dispose(),this.ws?.close(1e3)}reload(){return this.closed=!0,this.dispose(),this.ws?.close(nt,"Updating"),this.key?.startsWith("pub:")&&this.rejoin?{key:this.key,rejoin:this.rejoin}:null}open(e,o){this.retryTimer=0;let r=new URL(`/api/games/${this.slug}/rooms/connect`,location.href);r.protocol=location.protocol==="https:"?"wss:":"ws:",this.key?(r.searchParams.set("key",this.key),this.rejoin&&r.searchParams.set("rejoin",this.rejoin)):this.options.server?(r.searchParams.set("server",this.options.server),this.options.private&&r.searchParams.set("own","1")):this.options.name?r.searchParams.set("name",this.options.name):(this.options.mode&&r.searchParams.set("mode",this.options.mode),this.options.follow&&r.searchParams.set("follow",this.options.follow)),this.options.maxPlayers&&r.searchParams.set("max",String(this.options.maxPlayers)),this.options.minPlayers&&r.searchParams.set("min",String(this.options.minPlayers)),this.options.countdown!==void 0&&r.searchParams.set("countdown",String(this.options.countdown)),this.options.teams&&r.searchParams.set("teams",String(this.options.teams)),this.options.ranked&&r.searchParams.set("ranked","1"),Te&&r.searchParams.set("devplayer",Te);let n=Dt();n&&r.searchParams.set("tester",n),r.searchParams.set("page",this.page),this.options.build&&r.searchParams.set("build",this.options.build),_t&&r.searchParams.set("pass",_t),Nt&&r.searchParams.set("demo","1");let a=new WebSocket(r);this.ws=a,this.lastHeard=Date.now();let i=!1;a.onopen=()=>{this.lastHeard=Date.now(),window.clearInterval(this.pingTimer),window.clearInterval(this.watchTimer),this.pingTimer=window.setInterval(()=>a.readyState===WebSocket.OPEN&&this.ping(),Eo),this.watchTimer=window.setInterval(()=>{a===this.ws&&a.readyState===WebSocket.OPEN&&Date.now()-this.lastHeard>Po&&this.lost(a)},5e3)},a.onmessage=c=>{this.lastHeard=Date.now(),this.heardCount++;let p;try{p=JSON.parse(c.data)}catch{return}if(p.t==="pong"){this.pingAt&&this.callbacks.onRtt?.(Date.now()-this.pingAt),this.pingAt=0;return}if(p.t==="welcome"&&(p.rejoin&&(this.rejoin=p.rejoin),delete p.rejoin,p.room.startsWith("pub:")&&this.rejoin&&I(this.slug,{key:p.room,rejoin:this.rejoin})),p.t==="leave"&&p.kicked&&p.id===this.you){this.close(),this.finish("kicked");return}if(p.t==="welcome"&&(this.hintedHidden=!1,this.hint()),p.t==="welcome"&&!i&&(i=!0,this.key=p.room,this.you=p.you,this.attempt=0,this.droppedAt&&(this.droppedAt=0,this.callbacks.onConnection?.(!0)),e)){let{t:x,...u}=p;e(u),e=void 0;return}this.callbacks.onMessage(p)};let s=!1,d=c=>{if(!s&&(s=!0,window.clearInterval(this.pingTimer),window.clearInterval(this.watchTimer),!(this.closed||a!==this.ws))){if(c.code===tt)return this.finish("replaced");if(c.code===rt&&i&&document.visibilityState==="hidden"){this.sleeping=!0,this.droppedAt||(this.droppedAt=Date.now(),this.callbacks.onConnection?.(!1));return}if(c.code===ot){if(i||!o)return this.finish("kicked");this.closed=!0,this.dispose(),I(this.slug,null),o(Object.assign(new Error("You were removed from this server."),{kicked:!0}));return}if(!i&&o){this.closed=!0,this.dispose(),o(new Error(this.options.name||this.options.server?"Could not join that server. It may be full.":"Could not join a server. Try again soon."));return}this.retry()}};a.onclose=d,a.onerror=()=>{window.setTimeout(()=>{a.readyState===WebSocket.CLOSED&&d({code:1006})},1e3)}}retry(){if(this.closed)return;let e=Date.now();if(this.droppedAt||(this.droppedAt=e,this.callbacks.onConnection?.(!1)),e-this.droppedAt>Ro)return this.finish("disconnected");if(this.retryTimer)return;let o=Ot[Math.min(this.attempt++,Ot.length-1)]*(.5+Math.random());this.retryTimer=window.setTimeout(()=>{this.retryTimer=0,this.closed||this.open()},o)}lost(e){if(!(e!==this.ws||this.closed)){e.onopen=e.onmessage=e.onclose=e.onerror=null,window.clearInterval(this.pingTimer),window.clearInterval(this.watchTimer);try{e.close()}catch{}this.retry()}}wake(){if(this.closed)return;if(this.sleeping){this.sleeping=!1,this.droppedAt=Date.now(),this.attempt=0,this.open();return}let e=this.ws;if(!e||e.readyState===WebSocket.CLOSING||e.readyState===WebSocket.CLOSED){this.retryTimer&&(window.clearTimeout(this.retryTimer),this.retryTimer=0,this.open());return}if(e.readyState!==WebSocket.OPEN)return;let o=this.heardCount;try{e.send(Se)}catch{return this.lost(e)}window.setTimeout(()=>e===this.ws&&!this.closed&&this.heardCount===o&&this.lost(e),Mo)}dispose(){window.clearInterval(this.pingTimer),window.clearInterval(this.watchTimer),window.clearTimeout(this.retryTimer),this.retryTimer=0,document.removeEventListener("visibilitychange",this.onVisible),removeEventListener("online",this.onOnline),removeEventListener("pageshow",this.onShow),removeEventListener("pagehide",this.onHide)}finish(e){this.closed=!0,this.dispose(),I(this.slug,null),this.callbacks.onClose(e)}};var Ut=`:root {
4
4
  color-scheme: light;
5
5
  /* Site pages: as wide as this at most, with gutters that grow with the window. */
6
6
  --page: 1440px;
@@ -59,9 +59,8 @@ ${wt}`,r),r}),this.named}async adopt(e,o){let n=(await this.open()).transaction(
59
59
  --play-fill-hover: #ff8366;
60
60
  --play-ink: #121418;
61
61
  --play-text: var(--ok);
62
- /* Live: players in a game right now. The store's signal colour: a dot, a count, a Join. */
62
+ /* Live: players in a game right now. The store's signal colour: a dot, a count. */
63
63
  --live: #3ddc84;
64
- --live-ink: #0b2a17;
65
64
  --live-text: #12813f;
66
65
  /* The phone tab bar (and popups opened from it). */
67
66
  --dock-bg: var(--bar);
@@ -385,12 +384,33 @@ h3 {
385
384
  color: var(--danger);
386
385
  }
387
386
 
388
- .btn-small {
387
+ /* (A row's buttons are always this size: a list reads the same whatever its row can do.) */
388
+ .btn-small,
389
+ .row-actions .btn {
389
390
  min-height: 32px;
390
391
  padding: 0 12px;
391
392
  font-size: 13px;
392
393
  }
393
394
 
395
+ /* Fingers: small buttons and links stay easy to hit (padding on the links, which doesn't move anything around them). */
396
+ @media (pointer: coarse) {
397
+ .btn-small,
398
+ .row-actions .btn {
399
+ min-height: 40px;
400
+ }
401
+
402
+ .see-all,
403
+ .clear-search {
404
+ margin: -10px 0;
405
+ padding: 10px 0;
406
+ }
407
+
408
+ .label-row a,
409
+ .auth-switch a {
410
+ padding: 10px 4px;
411
+ }
412
+ }
413
+
394
414
  /* A plain file picker (a form that posts its file, like Upload files' Cover): its button is a small .btn. */
395
415
  input[type='file'] {
396
416
  max-width: 100%;
@@ -440,10 +460,14 @@ input[type='file']::file-selector-button:hover {
440
460
 
441
461
  .link-btn {
442
462
  padding: 6px 10px;
463
+ border: 0;
464
+ background: none;
443
465
  color: var(--text);
466
+ font-family: inherit;
444
467
  font-weight: 600;
445
468
  font-size: 14px;
446
469
  text-decoration: none;
470
+ cursor: pointer;
447
471
  }
448
472
 
449
473
  .link-btn:hover {
@@ -463,6 +487,7 @@ input[type='text'],
463
487
  input[type='email'],
464
488
  input[type='password'],
465
489
  input[type='search'],
490
+ input[type='number'],
466
491
  input:not([type]),
467
492
  select,
468
493
  textarea {
@@ -1270,6 +1295,15 @@ button.icon-btn:hover {
1270
1295
  background: var(--accent);
1271
1296
  }
1272
1297
 
1298
+ /* The chat is open over the page: one tab is lit, Chat. */
1299
+ .tabbar:has(> button[aria-expanded='true']) > a[aria-current='page'] {
1300
+ color: var(--dock-muted);
1301
+ }
1302
+
1303
+ .tabbar:has(> button[aria-expanded='true']) > a[aria-current='page']::before {
1304
+ content: none;
1305
+ }
1306
+
1273
1307
  .tabbar > a:focus-visible,
1274
1308
  .tabbar > button:focus-visible {
1275
1309
  outline: 2px solid var(--accent);
@@ -1530,7 +1564,6 @@ body:has(.tabbar) .footer-inner {
1530
1564
  color: var(--muted);
1531
1565
  font-size: 14px;
1532
1566
  font-weight: 600;
1533
- font-variant-numeric: tabular-nums;
1534
1567
  }
1535
1568
 
1536
1569
  .see-all {
@@ -1773,6 +1806,21 @@ body:has(.tabbar) .footer-inner {
1773
1806
  left: 0;
1774
1807
  }
1775
1808
 
1809
+ @media (max-width: 560px) {
1810
+ .storenav {
1811
+ flex-wrap: wrap;
1812
+ height: auto;
1813
+ }
1814
+ .storenav-links {
1815
+ flex: 1 1 100%;
1816
+ height: 46px;
1817
+ }
1818
+ .storenav-filters,
1819
+ .storenav-random {
1820
+ height: 40px;
1821
+ }
1822
+ }
1823
+
1776
1824
  .storenav-filters {
1777
1825
  display: flex;
1778
1826
  }
@@ -2425,17 +2473,6 @@ body:has(.tabbar) .footer-inner {
2425
2473
  background: var(--live);
2426
2474
  }
2427
2475
 
2428
- .btn.btn-live {
2429
- border-color: transparent;
2430
- background: var(--live);
2431
- color: var(--live-ink);
2432
- font-weight: 700;
2433
- }
2434
-
2435
- .btn.btn-live:hover {
2436
- background: color-mix(in srgb, var(--live) 85%, #fff);
2437
- }
2438
-
2439
2476
  /* Every game: its title (with See all on home, Clear on a search), the filters that are on, and the grid. */
2440
2477
  .browse-head {
2441
2478
  display: flex;
@@ -2900,6 +2937,14 @@ body:has(.tabbar) .footer-inner {
2900
2937
  padding: 96px 16px;
2901
2938
  }
2902
2939
 
2940
+ .lib-empty-actions {
2941
+ display: flex;
2942
+ flex-wrap: wrap;
2943
+ justify-content: center;
2944
+ gap: 10px;
2945
+ margin-top: 20px;
2946
+ }
2947
+
2903
2948
  /* A repo whose first deploy failed, in the side: its square says so. */
2904
2949
  .lib-icon.mine-failed {
2905
2950
  color: var(--danger);
@@ -2952,11 +2997,15 @@ body:has(.tabbar) .footer-inner {
2952
2997
  height: clamp(22px, 10.4cqi, 36px);
2953
2998
  border-radius: 5px;
2954
2999
  object-fit: cover;
2955
- background: var(--fill);
2956
3000
  font-size: 12px;
2957
3001
  font-weight: 800;
2958
3002
  }
2959
3003
 
3004
+ /* (A letter's square keeps its game's colour.) */
3005
+ .lib-icon:not(.gcard-letter) {
3006
+ background: var(--fill);
3007
+ }
3008
+
2960
3009
  @container side (min-width: 320px) {
2961
3010
  .lib-list a {
2962
3011
  min-height: 42px;
@@ -3685,6 +3734,11 @@ body:has(.tabbar) .footer-inner {
3685
3734
  align-items: start;
3686
3735
  }
3687
3736
 
3737
+ /* A page with nothing else: More like this keeps the gap it has under the game's body. */
3738
+ .game-top + .section {
3739
+ margin-top: 44px;
3740
+ }
3741
+
3688
3742
  /* One column: Play comes first in the panel, right under the gallery. */
3689
3743
  @media (max-width: 1000px) {
3690
3744
  .game-top {
@@ -3981,6 +4035,12 @@ body:has(.tabbar) .footer-inner {
3981
4035
  list-style: none;
3982
4036
  }
3983
4037
 
4038
+ .play-pay > summary,
4039
+ a.play-pay {
4040
+ padding: 6px 0;
4041
+ text-decoration: underline;
4042
+ }
4043
+
3984
4044
  .play-pay[open] > summary {
3985
4045
  margin-bottom: 8px;
3986
4046
  }
@@ -4367,9 +4427,11 @@ body:has(.tabbar) .footer-inner {
4367
4427
  }
4368
4428
 
4369
4429
  .fold-count {
4430
+ margin-left: 4px;
4370
4431
  color: var(--muted);
4371
4432
  font-size: 14px;
4372
4433
  font-variant-numeric: tabular-nums;
4434
+ white-space: nowrap;
4373
4435
  }
4374
4436
 
4375
4437
  .fold-caret {
@@ -4549,6 +4611,11 @@ body:has(.tabbar) .footer-inner {
4549
4611
  margin: 0 0 18px;
4550
4612
  }
4551
4613
 
4614
+ /* An order: its Play or Open, then what was bought. */
4615
+ .market-order .form-row:has(+ .kv) {
4616
+ margin-bottom: 18px;
4617
+ }
4618
+
4552
4619
  .kv dt {
4553
4620
  color: var(--muted);
4554
4621
  font-size: 12px;
@@ -7213,8 +7280,7 @@ body.play {
7213
7280
  body.play .ow-menu,
7214
7281
  body.play .ow-social-body,
7215
7282
  body.play .ow-chat-log,
7216
- body.play .ow-players,
7217
- body.play .ow-badges {
7283
+ body.play .ow-players {
7218
7284
  touch-action: pan-y;
7219
7285
  }
7220
7286
 
@@ -7744,9 +7810,11 @@ body.play input {
7744
7810
  padding-bottom: 16px;
7745
7811
  }
7746
7812
 
7813
+ /* Its first line level with the head's buttons. */
7747
7814
  .ow-menu-title {
7748
7815
  flex: 1;
7749
7816
  min-width: 0;
7817
+ padding-top: 4px;
7750
7818
  }
7751
7819
 
7752
7820
  .ow-menu-head h2 {
@@ -7799,8 +7867,7 @@ body.play input {
7799
7867
  }
7800
7868
 
7801
7869
  .ow-section[hidden],
7802
- .ow-setting[hidden],
7803
- .ow-guest[hidden] {
7870
+ .ow-setting[hidden] {
7804
7871
  display: none;
7805
7872
  }
7806
7873
 
@@ -7863,11 +7930,14 @@ body.play input {
7863
7930
  overflow-y: auto;
7864
7931
  }
7865
7932
 
7933
+ /* A row with more buttons than room takes a second line for them: the name stays readable. */
7866
7934
  .ow-players li {
7867
7935
  display: flex;
7936
+ flex-wrap: wrap;
7868
7937
  align-items: center;
7869
- gap: 10px;
7938
+ gap: 2px 10px;
7870
7939
  min-height: 40px;
7940
+ padding: 2px 0;
7871
7941
  font-size: 14px;
7872
7942
  }
7873
7943
 
@@ -7903,8 +7973,10 @@ a.ow-player-name:focus-visible {
7903
7973
  }
7904
7974
 
7905
7975
  .ow-player-actions {
7906
- flex: none;
7976
+ flex: 0 1 auto;
7907
7977
  display: flex;
7978
+ flex-wrap: wrap;
7979
+ justify-content: flex-end;
7908
7980
  gap: 6px;
7909
7981
  margin-left: auto;
7910
7982
  }
@@ -8046,17 +8118,6 @@ a.ow-player-name:focus-visible {
8046
8118
  white-space: nowrap;
8047
8119
  }
8048
8120
 
8049
- .ow-server {
8050
- display: flex;
8051
- flex-wrap: wrap;
8052
- align-items: center;
8053
- justify-content: space-between;
8054
- gap: 4px 12px;
8055
- margin-top: 6px;
8056
- color: var(--muted);
8057
- font-size: 13px;
8058
- }
8059
-
8060
8121
  .ow-text-btn {
8061
8122
  padding: 0;
8062
8123
  border: 0;
@@ -8120,24 +8181,6 @@ button.ow-setting-icon:hover {
8120
8181
  outline-offset: 2px;
8121
8182
  }
8122
8183
 
8123
- .ow-guest {
8124
- margin: 0;
8125
- padding: 12px 0;
8126
- border-top: 1px solid var(--line);
8127
- color: var(--muted);
8128
- font-size: 13px;
8129
- }
8130
-
8131
- .ow-guest a {
8132
- color: var(--accent-text);
8133
- font-weight: 600;
8134
- text-decoration: none;
8135
- }
8136
-
8137
- .ow-guest a:hover {
8138
- text-decoration: underline;
8139
- }
8140
-
8141
8184
  .ow-menu-foot {
8142
8185
  display: flex;
8143
8186
  gap: 8px;
@@ -8307,80 +8350,14 @@ button.ow-setting-icon:hover {
8307
8350
  letter-spacing: 0.06em;
8308
8351
  }
8309
8352
 
8310
- .ow-badges {
8311
- list-style: none;
8312
- margin: 0;
8313
- padding: 0;
8314
- display: flex;
8315
- flex-wrap: wrap;
8316
- gap: 8px;
8317
- }
8318
-
8319
- .ow-badge-item {
8320
- display: block;
8321
- padding: 0;
8322
- border: 0;
8323
- border-radius: 50%;
8324
- background: none;
8325
- cursor: pointer;
8326
- opacity: 0.35;
8327
- filter: grayscale(1);
8328
- transition: opacity 0.15s ease;
8329
- }
8330
-
8331
- .ow-badge-item:hover {
8332
- opacity: 0.55;
8333
- }
8334
-
8335
- .ow-badge-item.ow-earned {
8336
- opacity: 1;
8337
- filter: none;
8338
- }
8339
-
8340
- .ow-badge-item:focus-visible {
8341
- outline: 2px solid var(--accent-hover);
8342
- outline-offset: 2px;
8343
- }
8344
-
8345
- .ow-badge-icon {
8346
- display: block;
8347
- width: 40px;
8348
- height: 40px;
8349
- border-radius: 50%;
8350
- object-fit: cover;
8351
- }
8352
-
8353
- .ow-badge-blank {
8353
+ .ow-toast .ow-badge-blank {
8354
8354
  display: grid;
8355
8355
  place-items: center;
8356
- background: var(--fill-strong);
8357
- color: var(--text);
8358
- font-size: 18px;
8359
- }
8360
-
8361
- .ow-earned .ow-badge-blank,
8362
- .ow-toast .ow-badge-blank {
8363
8356
  background: var(--accent-soft);
8364
8357
  color: var(--accent-hover);
8365
- }
8366
-
8367
- .ow-toast .ow-badge-blank {
8368
8358
  font-size: 20px;
8369
8359
  }
8370
8360
 
8371
- .ow-badge-caption {
8372
- margin: 10px 0 0;
8373
- color: var(--muted);
8374
- font-size: 13px;
8375
- line-height: 1.45;
8376
- }
8377
-
8378
- .ow-badge-caption b {
8379
- margin-right: 2px;
8380
- color: var(--text);
8381
- font-weight: 600;
8382
- }
8383
-
8384
8361
  /* loading card shown until the game's page has loaded */
8385
8362
  .ow-loading {
8386
8363
  position: fixed;
@@ -8611,6 +8588,26 @@ button.ow-setting-icon:hover {
8611
8588
  .ow-menu {
8612
8589
  padding: 16px;
8613
8590
  }
8591
+ .ow-menu-title {
8592
+ padding-top: 8px;
8593
+ }
8594
+ }
8595
+
8596
+ /* The narrowest phones: rows wrap instead of pushing the menu sideways. */
8597
+ @media (max-width: 360px) {
8598
+ .ow-menu {
8599
+ padding: 12px;
8600
+ overflow-x: hidden;
8601
+ }
8602
+ .ow-menu-foot {
8603
+ flex-wrap: wrap;
8604
+ }
8605
+ .ow-menu-foot .ow-btn {
8606
+ flex: 1 1 100%;
8607
+ }
8608
+ .ow-section-head {
8609
+ flex-wrap: wrap;
8610
+ }
8614
8611
  }
8615
8612
 
8616
8613
  @media (prefers-reduced-motion: reduce) {
@@ -10437,6 +10434,38 @@ body.play .ow-card {
10437
10434
  border-radius: 999px;
10438
10435
  }
10439
10436
 
10437
+ /* Enter presses the button: one small mark, for keyboards. */
10438
+ .ow-lobby-keys .ow-lobby-action:not(:disabled)::after {
10439
+ content: '\\21B5' / '';
10440
+ margin-left: 8px;
10441
+ font-size: 0.85em;
10442
+ font-weight: 400;
10443
+ opacity: 0.65;
10444
+ }
10445
+
10446
+ /* A press goes through to the target under the button, inside the game's frame (shell/lobby.ts, link): it is then the player's gesture
10447
+ there. The strip goes through whole, and the card's own parts stay on this page. */
10448
+ .ow-lobby.ow-lobby-through,
10449
+ .ow-lobby-through > .ow-lobby-card,
10450
+ .ow-lobby-through > .ow-lobby-bar,
10451
+ .ow-lobby-through .ow-lobby-actions,
10452
+ .ow-lobby-through .ow-lobby-action {
10453
+ pointer-events: none;
10454
+ }
10455
+
10456
+ .ow-lobby-through > .ow-lobby-card > :not(.ow-lobby-actions),
10457
+ .ow-lobby-through .ow-lobby-invite {
10458
+ pointer-events: auto;
10459
+ }
10460
+
10461
+ .ow-lobby-hover.ow-btn {
10462
+ background: rgba(255, 255, 255, 0.16);
10463
+ }
10464
+
10465
+ .ow-lobby-hover.ow-btn-primary {
10466
+ background: var(--accent-fill-hover);
10467
+ }
10468
+
10440
10469
  /* 3, 2, 1. */
10441
10470
  .ow-lobby-countdown {
10442
10471
  position: fixed;
@@ -10846,6 +10875,32 @@ body.ow-calm .ow-fold-caret {
10846
10875
  overflow-wrap: anywhere;
10847
10876
  }
10848
10877
 
10878
+ .upload-kinds dl {
10879
+ margin: 0;
10880
+ font-size: 14px;
10881
+ }
10882
+
10883
+ .upload-kinds dl > div {
10884
+ display: grid;
10885
+ grid-template-columns: 7.5em minmax(0, 1fr);
10886
+ gap: 12px;
10887
+ padding: 7px 0;
10888
+ border-top: 1px solid var(--line);
10889
+ }
10890
+
10891
+ .upload-kinds dt {
10892
+ font-weight: 600;
10893
+ }
10894
+
10895
+ .upload-kinds dd {
10896
+ margin: 0;
10897
+ color: var(--muted);
10898
+ }
10899
+
10900
+ .upload-kinds .fine {
10901
+ margin: 7px 0 12px;
10902
+ }
10903
+
10849
10904
  .upload-form .flash {
10850
10905
  margin: 0;
10851
10906
  }
@@ -13572,35 +13627,6 @@ body.share-frame {
13572
13627
  }
13573
13628
  }
13574
13629
 
13575
- /* The play page's menu: Share (the server's invite link, on X or a phone's share sheet), and for guests one quiet line. */
13576
- .ow-share {
13577
- display: flex;
13578
- gap: 8px;
13579
- padding: 12px 0;
13580
- border-top: 1px solid var(--line);
13581
- }
13582
-
13583
- .ow-share .ow-pill {
13584
- text-decoration: none;
13585
- }
13586
-
13587
- .ow-made {
13588
- margin: 0;
13589
- padding: 0 0 12px;
13590
- color: var(--muted);
13591
- font-size: 12px;
13592
- }
13593
-
13594
- .ow-made a {
13595
- color: inherit;
13596
- font-weight: 600;
13597
- text-decoration: none;
13598
- }
13599
-
13600
- .ow-made a:hover {
13601
- color: var(--text);
13602
- }
13603
-
13604
13630
  /* A clip frame records the game alone: the platform's corner buttons and chat stay out of the creator's view. */
13605
13631
  .ow-clip-frame .ow-hud,
13606
13632
  .ow-clip-frame .ow-stats {
@@ -13777,7 +13803,7 @@ body.share-frame {
13777
13803
  gap: 16px;
13778
13804
  margin-top: 12px;
13779
13805
  }
13780
- `;var Y=JSON.parse(document.getElementById("ow-data").textContent),J=Y.game.slug,j=new URL(location.href).searchParams,De=/^[2-9]$/.test(j.get("player")??"")?j.get("player"):null,Ce=j.get("demo"),I=Ce===null||Ce==="0"?null:{paid:Ce!=="free"};jt(I!==null);var Ht=document.createElement("style");Ht.textContent=`${Bt}
13806
+ `;var X=JSON.parse(document.getElementById("ow-data").textContent),Z=X.game.slug,N=new URL(location.href).searchParams,_e=/^[2-9]$/.test(N.get("player")??"")?N.get("player"):null,De=N.get("demo"),B=De===null||De==="0"?null:{paid:De!=="free"};Gt(B!==null);var Vt=document.createElement("style");Vt.textContent=`${Ut}
13781
13807
  .ow-local-panel { position: fixed; z-index: 30; top: 64px; left: 10px; max-width: min(340px, calc(100vw - 20px)); padding: 12px 14px; border-radius: 12px; background: var(--glass); border: 1px solid var(--line); color: var(--text); font: 14px/1.45 var(--font); backdrop-filter: blur(12px); }
13782
13808
  .ow-local-panel[hidden] { display: none; }
13783
13809
  .ow-local-panel b { font-weight: 650; }
@@ -13785,4 +13811,4 @@ body.share-frame {
13785
13811
  .ow-local-panel p { margin: 0 0 6px; }
13786
13812
  .ow-local-toast { position: fixed; z-index: 40; left: 50%; bottom: 24px; transform: translateX(-50%); max-width: calc(100vw - 32px); padding: 10px 14px; border-radius: 10px; background: var(--glass); color: var(--text); font: 14px/1.4 var(--font); border: 1px solid var(--line); }
13787
13813
  .ow-local-toast[hidden] { display: none; }
13788
- `;document.head.append(Ht);document.body.classList.add("play");var M=document.getElementById("ow-game");M.setAttribute("sandbox",pt);M.setAttribute("allow",dt);var F=document.getElementById("ow-root");function Ro(){let t=new URLSearchParams;for(let[o,r]of j)gt.has(o)||t.append(o,r);let e=t.toString();return e&&e.length<=400?`?${e}`:""}var B=S("div","ow-hud ow-pos-top-left"),_e=S("div","ow-bar"),Eo='<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="8" fill="none" stroke="currentColor" stroke-width="2"/><circle cx="12" cy="12" r="3" fill="currentColor"/></svg>',Po='<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 5.5A2.5 2.5 0 0 1 6.5 3h11A2.5 2.5 0 0 1 20 5.5v8a2.5 2.5 0 0 1-2.5 2.5H10l-4.2 3.4c-.5.4-1.3 0-1.3-.6V16A2.5 2.5 0 0 1 4 13.5z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/></svg>',Mo='<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="10" cy="8" r="3.5" fill="none" stroke="currentColor" stroke-width="1.8"/><path d="M3.5 19.5c.8-3.4 3.3-5.5 6.5-5.5s5.7 2.1 6.5 5.5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/><path d="M19 7.5v6M16 10.5h6" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>',Ft=Ie("Local platform",Eo),qt=Ie("Chat (not on this computer)",Po),Kt=Ie("Another player",Mo);_e.append(Ft,qt,Kt);B.append(_e);var D=S("div","ow-local-panel");D.hidden=!0;F.append(B,D);var W=S("div","ow-local-toast");W.hidden=!0;F.append(W);var Nt=0;function N(t){W.textContent=t,W.hidden=!1,window.clearTimeout(Nt),Nt=window.setTimeout(()=>W.hidden=!0,4e3)}Ft.addEventListener("click",()=>{D.hidden=!D.hidden,D.hidden||Ao()});qt.addEventListener("click",()=>N("Chat is the platform\u2019s: it isn\u2019t on this computer."));Kt.addEventListener("click",()=>{window.open(X(),"_blank")});function X(){let t=new URL(location.href),e=Math.min(9,Number(De??1)+1);return t.searchParams.set("player",String(e)),P&&t.searchParams.set("server",P),t.href}function Ao(){D.replaceChildren();let t=e=>{let o=document.createElement("p");o.innerHTML=e,D.append(o)};t(`<b>${ce(Y.game.name)}</b> on this computer`),t(`You are <b>${ce(Vt?.name??"\u2026")}</b>${G?` in <code>${ce(G)}</code>`:""}`),t(`<a href="${ce(X())}" target="_blank">Open another player</a> (up to 9: <code>?player=2</code>\u2026)`),t("Saves, badges and scores are kept by <code>onceworlds serve</code> while it runs.")}function S(t,e=""){let o=document.createElement(t);return e&&(o.className=e),o}function Ie(t,e){let o=S("button","ow-icon-btn");return o.type="button",o.title=t,o.setAttribute("aria-label",t),o.innerHTML=e,o}function ce(t){return t.replace(/[&<>"']/g,e=>`&#${e.charCodeAt(0)};`)}async function k(t,e={}){let o=new URL(`/__emulator/api/${t}`,location.href);De&&o.searchParams.set("player",De);let r=await fetch(o,{...e,headers:e.body?{"Content-Type":"application/json"}:void 0}),n=await r.json().catch(()=>null);if(!r.ok)throw new Error(n?.message??`The local platform answered ${r.status}`);return n}var $o=Date.now(),me=k("me").then(({player:t,now:e})=>({player:t,timeOffset:Math.round(e-($o+Date.now())/2)})),Vt=null;me.then(t=>Vt=t.player).catch(()=>{});var g=null,G=null,U=null,xe="",je=null,Be=!1,Oe=0,Wt=0;me.then(t=>Wt=t.timeOffset).catch(()=>{});var P=Ye.test(j.get("server")??"")?j.get("server"):null,ge={value:At(J)},z={hellos:0,joins:0,errors:[],lastAlive:0,closed:null},f=new ae(F,{now:()=>Date.now()+Wt,onSetting:(t,e)=>{let o=f.chooseSetting(t,e);o&&(g?.send({t:"state",k:K,v:o}),g?.send({t:"unready"}),f.handle({t:"unready"}),v.emit("room",{t:"unready"}))},onReady:t=>Yt(t),onStart:()=>g?.send({t:"start"}),onStop:()=>g?.send({t:"end"}),onInvite:()=>window.open(X(),"_blank"),onLayout:()=>H.changed()}),O=new ne(F,t=>v.emit("controls",t),()=>H.changed());function Yt(t){g?.send({t:"ready",ready:t}),f.setReadyLocal(t),v.emit("room",{t:"ready",id:xe,ready:t})}function Ne(t,e=!1){O.touch!==t&&(O.setTouch(t),e||v.emit("input",{touch:t}))}addEventListener("pointerdown",t=>t.isTrusted&&Ne(t.pointerType!=="mouse"),!0);addEventListener("keydown",t=>t.isTrusted&&Ne(!1),!0);var H=new ie(Jt,t=>v.emit("reserved",t)),he=new te(async()=>({game:J,player:null})),v=new ee(M,{async"session.get"(){let{player:t,timeOffset:e}=await me;return{player:t,game:{slug:J,name:Y.game.name,owner:"local"},audio:{volume:1,muted:!1},settings:{quality:"auto",reducedMotion:!1,showStats:!1},timeOffset:e,platformOrigin:location.origin}},"save.get":({key:t})=>k(`save?key=${encodeURIComponent(String(t))}`),"save.set":({key:t,value:e})=>k(`save?key=${encodeURIComponent(String(t))}`,{method:"PUT",body:JSON.stringify({value:e??null})}).then(()=>null),"save.delete":({key:t})=>k(`save?key=${encodeURIComponent(String(t))}`,{method:"DELETE"}).then(()=>null),"save.list":()=>k("saves"),"storage.open":t=>he.read(t),"storage.commit":t=>he.commit(t),"storage.delete":t=>he.remove(t),"storage.list":()=>he.list(),async"room.join"(t){await me;let e=t??{};if(je=e.lobby==="card"||e.lobby==="bar"?e.lobby:null,Be=!ut(e),f.setSchema(Qe(e.settings)),e.party&&g)throw new Error("Parties go into matchmaking on onceworlds.com: the local platform keeps everyone where they are.");ue();let o=!e.name&&!P&&!e.private&&ge.value&&To(ge.value.key,e)?ge.value:null;if(ge.value=null,o){let r=await Ut({key:o.key,rejoin:o.rejoin,maxPlayers:e.maxPlayers,minPlayers:e.minPlayers,countdown:e.countdown,teams:e.teams,ranked:e.ranked}).catch(()=>null);if(r)return r;ue()}return Ut(Oo(e))},"room.leave"(){return ue(),null},"net.fetch":()=>{throw new Error("onceworlds.fetch fills in secrets on onceworlds.com: it does nothing on this computer. Push a draft (onceworlds deploy) to try it.")},"badges.list":()=>k("badges"),"badges.award":({key:t})=>I?.paid?{awarded:!1}:k("badges",{method:"POST",body:JSON.stringify({key:String(t)})}).then(e=>(e.awarded&&N(`Badge earned: ${e.badge?.name??t}`),{awarded:e.awarded})),"leaderboards.submit":({board:t,score:e,lowerIsBetter:o})=>I?.paid?null:k("leaderboards",{method:"POST",body:JSON.stringify({board:String(t),score:Number(e),lowerIsBetter:o===!0})}),"leaderboards.top":({board:t,limit:e,lowerIsBetter:o})=>k(`leaderboards?board=${encodeURIComponent(String(t))}&limit=${Number(e)||10}&lower=${o===!0?1:0}`),"ratings.get":()=>null,"ratings.top":()=>[],"ui.invite"(){return window.open(X(),"_blank"),null},"ui.fullscreen"(){return document.documentElement.requestFullscreen?.().catch(()=>{}),null},"ui.tapped":()=>null,"ui.controls"(t){return O.setLayout(t),null},"ui.orientation":()=>null,"ui.menuPosition"(t){let e=t?.position;return(e==="top-left"||e==="top-right"||e==="bottom-left"||e==="bottom-right")&&(B.className=`ow-hud ow-pos-${e}`,H.changed()),null},"ui.look"(t){return Gt(t),null},"player.rename":()=>null,"demo.end":()=>I?Lo(I.paid):!1,"data.game.get":({key:t})=>k(`gamedata?key=${encodeURIComponent(String(t))}`),"data.game.op":t=>k("gamedata",{method:"POST",body:JSON.stringify(t)}),"player.avatar":t=>{let e=t?.id;return typeof e=="string"&&/^[A-Za-z0-9-]{1,64}$/.test(e)?Ve(e):null}},{"room.send":({d:t,to:e})=>g?.send({t:"msg",d:t,to:e}),"room.presence":({d:t})=>g?.send({t:"presence",d:t}),"room.hideLobby":({hidden:t})=>f.setHidden(t===!0),"room.state":({k:t,v:e,w:o})=>g?.send({t:"state",k:t,v:e,...typeof o=="number"&&Number.isSafeInteger(o)&&o>0&&o<=2147483647?{w:o}:{}}),"room.team":({id:t,team:e})=>g?.send({t:"team",id:String(t),team:Number(e)}),"room.open":({open:t})=>g?.send({t:"open",open:t===!0}),"room.result":({result:t})=>g?.send({t:"result",result:t}),"room.kick":({id:t})=>g?.send({t:"kick",id:String(t)}),"room.votekick":({id:t})=>g?.send({t:"votekick",id:String(t)}),"room.ready":({ready:t})=>{g?.send({t:"ready",ready:t===!0}),f.setReadyLocal(t===!0)},"room.unready":()=>{g?.send({t:"unready"}),f.handle({t:"unready"})},"room.transfer":({id:t})=>g?.send({t:"transfer",id:String(t)}),"room.start":({countdown:t,open:e})=>g?.send({t:"start",...Number.isFinite(t)?{countdown:Number(t)}:{},...e===!0?{open:!0}:{}}),"room.end":()=>g?.send({t:"end"}),"room.pause":({paused:t})=>g?.send({t:"pause",paused:t===!0}),"room.idle":({idle:t})=>g?.send({t:"idle",idle:t===!0}),"room.private":({k:t,v:e,id:o})=>typeof t=="string"&&g?.send({t:"private",k:t,v:e,...typeof o=="string"?{id:o}:{}}),"room.datareset":()=>g?.send({t:"datareset"}),"room.admit":({ids:t})=>g?.send({t:"admit",ids:Array.isArray(t)?t.filter(e=>typeof e=="string").slice(0,50):[]}),alive:()=>{z.lastAlive=Date.now()},stats:()=>{},hotkey:()=>{},vibrate:()=>{},input:t=>Ne(t?.touch===!0,!0),error:t=>{z.errors.length<20&&z.errors.push(String(t?.message??t).slice(0,300))},"input.first":()=>{},console:()=>{},debug:()=>{}},()=>{z.hellos++,_t(),ue(),B.className="ow-hud ow-pos-top-left",O.setLayout(null),Gt(null),v.emit("input",{touch:O.touch}),H.resend()},{demo:I!==null}),Te=null;function Lo(t){return Te??=new Promise(e=>{v.emit("pause",null);let o=S("div","ow-name ow-demo-end"),r=S("div","ow-name-form ow-demo-box"),n=S("h2");n.textContent="End of demo";let a=S("p","ow-demo-name");a.textContent=Y.game.name;let i=S("button","ow-btn ow-btn-primary");i.type="button",i.textContent=t?"Buy":"Favorite",i.addEventListener("click",()=>N(t?"On onceworlds.com, Buy opens the game\u2019s store page.":"On onceworlds.com, Favorite follows the game."));let s=S("button","ow-text-btn ow-name-login");s.type="button",s.textContent="Back to game",s.addEventListener("click",()=>{o.remove(),Te=null,v.emit("resume",null),M.focus(),e(!0)}),r.append(n,a,i,s),o.append(r),F.append(o),i.focus()}),Te}function Gt(t){let{look:e}=xt(t);f.setLook(St(e)),O.setLook(zt(e))}function Jt(){let t=M.getBoundingClientRect();if(!t.width||!t.height)return null;let{stick:e,buttons:o}=O.reservedBoxes(),r=_e.getBoundingClientRect(),n=parseFloat(getComputedStyle(B).paddingTop)||0,a=r.width&&r.height?{left:r.left-n,top:r.top-n,width:r.width+2*n,height:r.height+2*n}:null;return Mt(t,[{kind:"menu",box:B.hidden?null:a},{kind:"lobby",box:f.reservedBox()},{kind:"stick",box:e},{kind:"buttons",box:o}])}for(let t of["resize","orientationchange"])addEventListener(t,()=>H.changed());typeof ResizeObserver=="function"&&new ResizeObserver(()=>H.changed()).observe(M);function Co(t){return t.slice(0,t.lastIndexOf(":"))}function To(t,e){let o=(e.teams??0)>=2?e.teams:0,r=Math.min(E.roomMaxPlayersCap,Math.max(2,e.maxPlayers??16)),n=Xe(e.minPlayers,r,Z);return Co(t)===`pub:${we({mode:e.mode||"default",max:r,teams:o,ranked:e.ranked===!0,min:n})}`}function Do(){let t="abcdefghijklmnopqrstuvwxyz0123456789";return Array.from(crypto.getRandomValues(new Uint8Array(12)),e=>t[e%t.length]).join("")}function Oo(t){let{name:e,maxPlayers:o,minPlayers:r,countdown:n,mode:a,teams:i,ranked:s}=t,c=t.minPlayersPrivate??r;if(e)return{name:e,maxPlayers:o,minPlayers:c,countdown:n,teams:i};if(t.private&&!P){P=Do(),$t(J,P);let d=new URL(location.href);d.searchParams.set("server",P),history.replaceState(null,"",d.href)}return P?{server:P,maxPlayers:o,minPlayers:c,countdown:n,teams:i,private:t.private}:{mode:a,maxPlayers:o,minPlayers:r,countdown:n,teams:i,ranked:s}}async function Ut(t){let e=new de(J,t,{onMessage:n=>e===g&&_o(n),onClose:n=>e===g&&Io(n),onConnection:n=>{e===g&&(n||v.emit("room.connection",{connected:!1}))}});g=e;let o=await e.join();if(e!==g)throw new Error("Left the server while joining");let r=o.room.startsWith("private:")?{...o,invite:X()}:o;return xe=o.you,U=o.match??null,G=o.room,z.joins++,Oe=Je(o.room)?.max??Math.min(t.maxPlayers??E.roomMaxPlayersDefault,E.roomMaxPlayersCap),f.enable(je,{ownCountdown:Be}),f.setRoom(r,Oe),r}function _o(t){if(t.t==="update"||t.t==="move")return;if(t.t==="datareset"){N("This server\u2019s world was started over."),M.src=M.src;return}t.t==="chat"&&(t.entry.to||t.entry.channel)||t.t==="channel"||v.emit("room",t),t.t!=="welcome"&&f.handle(t),t.t==="welcome"?(f.setRoom(t,Oe),U=t.match??null):t.t==="match"?U=t.match:t.t==="error"&&t.code==="cooldown"&&(N(t.message),f.setReadyLocal(!1),v.emit("room",{t:"ready",id:xe,ready:!1}))}function Io(t){g=null,G=null,U=null,z.closed=t,f.setRoom(null),N(t==="replaced"?"You opened this game in another tab, so multiplayer moved there.":t==="kicked"?"You were removed from the server.":"Disconnected from the server."),v.emit("room.closed",{reason:t})}function ue(){g&&(g.close(),g=null,G=null,U=null,f.setRoom(null))}window.__owLocal={get you(){return xe},get room(){return G},get match(){return U},get lobby(){return je},get ownCountdown(){return Be},get hellos(){return z.hellos},get joins(){return z.joins},get errors(){return z.errors},get lastAlive(){return z.lastAlive},get closed(){return z.closed},get reserved(){return Jt()},ready(t){return g?(Yt(t),!0):!1},start(t){return g?(g.send({t:"start",...Number.isFinite(t)?{countdown:Number(t)}:{}}),!0):!1},chrome(t){F.style.visibility=t?"":"hidden"}};M.src=`${Y.gameOrigin}/${Ro()}`;})();
13814
+ `;document.head.append(Vt);document.body.classList.add("play");var v=document.getElementById("ow-game");v.setAttribute("sandbox",pt);v.setAttribute("allow",dt);var K=document.getElementById("ow-root");function Lo(){let t=new URLSearchParams;for(let[o,r]of N)gt.has(o)||t.append(o,r);let e=t.toString();return e&&e.length<=400?`?${e}`:""}var G=z("div","ow-hud ow-pos-top-left"),Be=z("div","ow-bar"),$o='<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="8" fill="none" stroke="currentColor" stroke-width="2"/><circle cx="12" cy="12" r="3" fill="currentColor"/></svg>',Co='<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 5.5A2.5 2.5 0 0 1 6.5 3h11A2.5 2.5 0 0 1 20 5.5v8a2.5 2.5 0 0 1-2.5 2.5H10l-4.2 3.4c-.5.4-1.3 0-1.3-.6V16A2.5 2.5 0 0 1 4 13.5z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/></svg>',To='<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="10" cy="8" r="3.5" fill="none" stroke="currentColor" stroke-width="1.8"/><path d="M3.5 19.5c.8-3.4 3.3-5.5 6.5-5.5s5.7 2.1 6.5 5.5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/><path d="M19 7.5v6M16 10.5h6" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>',Wt=je("Local platform",$o),Yt=je("Chat (not on this computer)",Co),Jt=je("Another player",To);Be.append(Wt,Yt,Jt);G.append(Be);var O=z("div","ow-local-panel");O.hidden=!0;K.append(G,O);var J=z("div","ow-local-toast");J.hidden=!0;K.append(J);var Ht=0;function U(t){J.textContent=t,J.hidden=!1,window.clearTimeout(Ht),Ht=window.setTimeout(()=>J.hidden=!0,4e3)}Wt.addEventListener("click",()=>{O.hidden=!O.hidden,O.hidden||Do()});Yt.addEventListener("click",()=>U("Chat is the platform\u2019s: it isn\u2019t on this computer."));Jt.addEventListener("click",()=>{window.open(Q(),"_blank")});function Q(){let t=new URL(location.href),e=Math.min(9,Number(_e??1)+1);return t.searchParams.set("player",String(e)),M&&t.searchParams.set("server",M),t.href}function Do(){O.replaceChildren();let t=e=>{let o=document.createElement("p");o.innerHTML=e,O.append(o)};t(`<b>${he(X.game.name)}</b> on this computer`),t(`You are <b>${he(Xt?.name??"\u2026")}</b>${H?` in <code>${he(H)}</code>`:""}`),t(`<a href="${he(Q())}" target="_blank">Open another player</a> (up to 9: <code>?player=2</code>\u2026)`),t("Saves, badges and scores are kept by <code>onceworlds serve</code> while it runs.")}function z(t,e=""){let o=document.createElement(t);return e&&(o.className=e),o}function je(t,e){let o=z("button","ow-icon-btn");return o.type="button",o.title=t,o.setAttribute("aria-label",t),o.innerHTML=e,o}function he(t){return t.replace(/[&<>"']/g,e=>`&#${e.charCodeAt(0)};`)}async function S(t,e={}){let o=new URL(`/__emulator/api/${t}`,location.href);_e&&o.searchParams.set("player",_e);let r=await fetch(o,{...e,headers:e.body?{"Content-Type":"application/json"}:void 0}),n=await r.json().catch(()=>null);if(!r.ok)throw new Error(n?.message??`The local platform answered ${r.status}`);return n}var Oo=Date.now(),fe=S("me").then(({player:t,now:e})=>({player:t,timeOffset:Math.round(e-(Oo+Date.now())/2)})),Xt=null;fe.then(t=>Xt=t.player).catch(()=>{});var g=null,H=null,_=null,be="",j=null,we=!1,Zt=!1,Ie=0,Qt=0;fe.then(t=>Qt=t.timeOffset).catch(()=>{});var M=Ye.test(N.get("server")??"")?N.get("server"):null,ue={value:Ct(Z)},R={hellos:0,joins:0,errors:[],lastAlive:0,closed:null},m=new le(K,{now:()=>Date.now()+Qt,onSetting:(t,e)=>{let o=m.chooseSetting(t,e);o&&(g?.send({t:"state",k:V,v:o}),g?.send({t:"unready"}),m.handle({t:"unready"}),w.emit("room",{t:"unready"}))},onReady:t=>eo(t),onStart:()=>g?.send({t:"start"}),onStop:()=>g?.send({t:"end"}),onInvite:()=>window.open(Q(),"_blank"),onLayout:()=>F.changed(),onPressed:to}),C=new ie(K,t=>w.emit("controls",t),()=>F.changed());m.setKeyMark(!C.touch);function eo(t){g?.send({t:"ready",ready:t}),m.setReadyLocal(t),w.emit("room",{t:"ready",id:be,ready:t})}function Ne(t,e=!1){C.touch!==t&&(C.setTouch(t),m.setKeyMark(!t),e||w.emit("input",{touch:t}))}addEventListener("pointerdown",t=>t.isTrusted&&Ne(t.pointerType!=="mouse"),!0);addEventListener("keydown",t=>t.isTrusted&&Ne(!1),!0);addEventListener("keydown",t=>{let e=document.activeElement;t.isTrusted&&!t.repeat&&xt(t)&&(!e||e===document.body)&&m.pressKey()});function to(){v.focus(),v.contentWindow?.focus()}function Ft(){j||Zt||(j="bar",m.enable(j,{ownCountdown:we}))}var F=new se(()=>{let t=oo();return t&&{areas:t,link:m.link(v.getBoundingClientRect())}},({areas:t,link:e})=>{w.emit("reserved",t),w.emit("lobby",e)}),me=new re(async()=>({game:Z,player:null})),w=new oe(v,{async"session.get"(){let{player:t,timeOffset:e}=await fe;return{player:t,game:{slug:Z,name:X.game.name,owner:"local"},audio:{volume:1,muted:!1},settings:{quality:"auto",reducedMotion:!1,showStats:!1},timeOffset:e,platformOrigin:location.origin}},"save.get":({key:t})=>S(`save?key=${encodeURIComponent(String(t))}`),"save.set":({key:t,value:e})=>S(`save?key=${encodeURIComponent(String(t))}`,{method:"PUT",body:JSON.stringify({value:e??null})}).then(()=>null),"save.delete":({key:t})=>S(`save?key=${encodeURIComponent(String(t))}`,{method:"DELETE"}).then(()=>null),"save.list":()=>S("saves"),"storage.open":t=>me.read(t),"storage.commit":t=>me.commit(t),"storage.delete":t=>me.remove(t),"storage.list":()=>me.list(),async"room.join"(t){await fe;let e=t??{};if(j=mt(e),Zt=e.lobby==="game",we=!ut(e),m.setSchema(Qe(e.settings)),e.party&&g)throw new Error("Parties go into matchmaking on onceworlds.com: the local platform keeps everyone where they are.");xe();let o=!e.name&&!M&&!e.private&&ue.value&&Bo(ue.value.key,e)?ue.value:null;if(ue.value=null,o){let r=await qt({key:o.key,rejoin:o.rejoin,maxPlayers:e.maxPlayers,minPlayers:e.minPlayers,countdown:e.countdown,teams:e.teams,ranked:e.ranked}).catch(()=>null);if(r)return r;xe()}return qt(No(e))},"room.leave"(){return xe(),null},"net.fetch":()=>{throw new Error("onceworlds.fetch fills in secrets on onceworlds.com: it does nothing on this computer. Push a draft (onceworlds deploy) to try it.")},"badges.list":()=>S("badges"),"badges.award":({key:t})=>B?.paid?{awarded:!1}:S("badges",{method:"POST",body:JSON.stringify({key:String(t)})}).then(e=>(e.awarded&&U(`Badge earned: ${e.badge?.name??t}`),{awarded:e.awarded})),"leaderboards.submit":({board:t,score:e,lowerIsBetter:o})=>B?.paid?null:S("leaderboards",{method:"POST",body:JSON.stringify({board:String(t),score:Number(e),lowerIsBetter:o===!0})}),"leaderboards.top":({board:t,limit:e,lowerIsBetter:o})=>S(`leaderboards?board=${encodeURIComponent(String(t))}&limit=${Number(e)||10}&lower=${o===!0?1:0}`),"ratings.get":()=>null,"ratings.top":()=>[],"ui.invite"(){return window.open(Q(),"_blank"),null},"ui.fullscreen"(){return document.documentElement.requestFullscreen?.().catch(()=>{}),null},"ui.tapped":()=>null,"ui.controls"(t){return C.setLayout(t),null},"ui.orientation":()=>null,"ui.menuPosition"(t){let e=t?.position;return(e==="top-left"||e==="top-right"||e==="bottom-left"||e==="bottom-right")&&(G.className=`ow-hud ow-pos-${e}`,F.changed()),null},"ui.look"(t){return Kt(t),null},"player.rename":()=>null,"demo.end":()=>B?_o(B.paid):!1,"data.game.get":({key:t})=>S(`gamedata?key=${encodeURIComponent(String(t))}`),"data.game.op":t=>S("gamedata",{method:"POST",body:JSON.stringify(t)}),"player.avatar":t=>{let e=t?.id;return typeof e=="string"&&/^[A-Za-z0-9-]{1,64}$/.test(e)?Ve(e):null}},{"room.send":({d:t,to:e})=>g?.send({t:"msg",d:t,to:e}),"room.presence":({d:t})=>g?.send({t:"presence",d:t}),"room.hideLobby":()=>{},"lobby.press":()=>m.pressTarget(),"lobby.target":t=>m.setTarget(t?.ok===!0,t?.hover===!0),"room.state":({k:t,v:e,w:o})=>g?.send({t:"state",k:t,v:e,...typeof o=="number"&&Number.isSafeInteger(o)&&o>0&&o<=2147483647?{w:o}:{}}),"room.team":({id:t,team:e})=>g?.send({t:"team",id:String(t),team:Number(e)}),"room.open":({open:t})=>g?.send({t:"open",open:t===!0}),"room.result":({result:t})=>g?.send({t:"result",result:t}),"room.kick":({id:t})=>g?.send({t:"kick",id:String(t)}),"room.votekick":({id:t})=>g?.send({t:"votekick",id:String(t)}),"room.ready":({ready:t})=>{Ft(),g?.send({t:"ready",ready:t===!0}),m.setReadyLocal(t===!0)},"room.unready":()=>{g?.send({t:"unready"}),m.handle({t:"unready"})},"room.transfer":({id:t})=>g?.send({t:"transfer",id:String(t)}),"room.start":({countdown:t,open:e})=>{Ft(),g?.send({t:"start",...Number.isFinite(t)?{countdown:Number(t)}:{},...e===!0?{open:!0}:{}})},"room.end":()=>g?.send({t:"end"}),"room.pause":({paused:t})=>g?.send({t:"pause",paused:t===!0}),"room.idle":({idle:t})=>g?.send({t:"idle",idle:t===!0}),"room.private":({k:t,v:e,id:o})=>typeof t=="string"&&g?.send({t:"private",k:t,v:e,...typeof o=="string"?{id:o}:{}}),"room.datareset":()=>g?.send({t:"datareset"}),"room.admit":({ids:t})=>g?.send({t:"admit",ids:Array.isArray(t)?t.filter(e=>typeof e=="string").slice(0,50):[]}),alive:()=>{R.lastAlive=Date.now()},stats:()=>{},hotkey:({key:t})=>{t==="Enter"&&m.pressKey()},vibrate:()=>{},input:t=>Ne(t?.touch===!0,!0),error:t=>{R.errors.length<20&&R.errors.push(String(t?.message??t).slice(0,300))},"input.first":()=>{},console:()=>{},debug:()=>{}},()=>{R.hellos++,jt(),xe(),G.className="ow-hud ow-pos-top-left",C.setLayout(null),Kt(null),w.emit("input",{touch:C.touch}),F.resend()},{demo:B!==null}),Oe=null;function _o(t){return Oe??=new Promise(e=>{w.emit("pause",null);let o=z("div","ow-name ow-demo-end"),r=z("div","ow-name-form ow-demo-box"),n=z("h2");n.textContent="End of demo";let a=z("p","ow-demo-name");a.textContent=X.game.name;let i=z("button","ow-btn ow-btn-primary");i.type="button",i.textContent=t?"Buy":"Favorite",i.addEventListener("click",()=>U(t?"On onceworlds.com, Buy opens the game\u2019s store page.":"On onceworlds.com, Favorite follows the game."));let s=z("button","ow-text-btn ow-name-login");s.type="button",s.textContent="Back to game",s.addEventListener("click",()=>{o.remove(),Oe=null,w.emit("resume",null),v.focus(),e(!0)}),r.append(n,a,i,s),o.append(r),K.append(o),i.focus()}),Oe}function Kt(t){let{look:e}=wt(t);m.setLook(Et(e)),C.setLook(Pt(e))}function oo(){let t=v.getBoundingClientRect();if(!t.width||!t.height)return null;let{stick:e,buttons:o}=C.reservedBoxes(),r=Be.getBoundingClientRect(),n=parseFloat(getComputedStyle(G).paddingTop)||0,a=r.width&&r.height?{left:r.left-n,top:r.top-n,width:r.width+2*n,height:r.height+2*n}:null;return Y(t,[{kind:"menu",box:G.hidden?null:a},{kind:"lobby",box:m.reservedBox()},{kind:"stick",box:e},{kind:"buttons",box:o}])}for(let t of["resize","orientationchange"])addEventListener(t,()=>F.changed());typeof ResizeObserver=="function"&&new ResizeObserver(()=>F.changed()).observe(v);function Io(t){return t.slice(0,t.lastIndexOf(":"))}function Bo(t,e){let o=(e.teams??0)>=2?e.teams:0,r=Math.min(P.roomMaxPlayersCap,Math.max(2,e.maxPlayers??16)),n=Xe(e.minPlayers,r,ee);return Io(t)===`pub:${ke({mode:e.mode||"default",max:r,teams:o,ranked:e.ranked===!0,min:n})}`}function jo(){let t="abcdefghijklmnopqrstuvwxyz0123456789";return Array.from(crypto.getRandomValues(new Uint8Array(12)),e=>t[e%t.length]).join("")}function No(t){let{name:e,maxPlayers:o,minPlayers:r,countdown:n,mode:a,teams:i,ranked:s}=t,d=t.minPlayersPrivate??r;if(e)return{name:e,maxPlayers:o,minPlayers:d,countdown:n,teams:i};if(t.private&&!M){M=jo(),Tt(Z,M);let c=new URL(location.href);c.searchParams.set("server",M),history.replaceState(null,"",c.href)}return M?{server:M,maxPlayers:o,minPlayers:d,countdown:n,teams:i,private:t.private}:{mode:a,maxPlayers:o,minPlayers:r,countdown:n,teams:i,ranked:s}}async function qt(t){let e=new ge(Z,t,{onMessage:n=>e===g&&Go(n),onClose:n=>e===g&&Uo(n),onConnection:n=>{e===g&&(n||w.emit("room.connection",{connected:!1}))}});g=e;let o=await e.join();if(e!==g)throw new Error("Left the server while joining");let r=o.room.startsWith("private:")?{...o,invite:Q()}:o;return be=o.you,_=o.match??null,H=o.room,R.joins++,Ie=Je(o.room)?.max??Math.min(t.maxPlayers??P.roomMaxPlayersDefault,P.roomMaxPlayersCap),m.enable(j,{ownCountdown:we}),m.setRoom(r,Ie),r}function Go(t){if(t.t==="update"||t.t==="move")return;if(t.t==="datareset"){U("This server\u2019s world was started over."),v.src=v.src;return}t.t==="chat"&&(t.entry.to||t.entry.channel)||t.t==="channel"||w.emit("room",t),t.t!=="welcome"&&m.handle(t),t.t==="welcome"?(m.setRoom(t,Ie),_=t.match??null):t.t==="match"?(t.match.phase!=="lobby"&&t.match.phase!==_?.phase&&to(),_=t.match):t.t==="error"&&t.code==="cooldown"&&(U(t.message),m.setReadyLocal(!1),w.emit("room",{t:"ready",id:be,ready:!1}))}function Uo(t){g=null,H=null,_=null,R.closed=t,m.setRoom(null),U(t==="replaced"?"You opened this game in another tab, so multiplayer moved there.":t==="kicked"?"You were removed from the server.":"Disconnected from the server."),w.emit("room.closed",{reason:t})}function xe(){g&&(g.close(),g=null,H=null,_=null,m.setRoom(null))}window.__owLocal={get you(){return be},get room(){return H},get match(){return _},get lobby(){return j},get ownCountdown(){return we},get hellos(){return R.hellos},get joins(){return R.joins},get errors(){return R.errors},get lastAlive(){return R.lastAlive},get closed(){return R.closed},get reserved(){return oo()},ready(t){return g?(eo(t),!0):!1},start(t){return g?(g.send({t:"start",...Number.isFinite(t)?{countdown:Number(t)}:{}}),!0):!1},chrome(t){K.style.visibility=t?"":"hidden"}};v.src=`${X.gameOrigin}/${Lo()}`;})();