From 2b0dc392da77ef6338e1cdee794f3ca2afadda4e Mon Sep 17 00:00:00 2001 From: Zutatensuppe Date: Thu, 8 Jul 2021 00:00:17 +0200 Subject: [PATCH] fix older replays --- build/public/assets/index.855f4dd3.js | 1 - build/public/assets/index.cc6b4801.js | 1 + build/public/index.html | 2 +- build/server/main.js | 92 +++++++++++++++++---------- src/common/GameCommon.ts | 4 +- src/common/Types.ts | 27 +++++++- src/common/Util.ts | 6 +- src/server/GameLog.ts | 12 +++- src/server/GameStorage.ts | 8 +-- src/server/main.ts | 6 +- 10 files changed, 105 insertions(+), 54 deletions(-) delete mode 100644 build/public/assets/index.855f4dd3.js create mode 100644 build/public/assets/index.cc6b4801.js diff --git a/build/public/assets/index.855f4dd3.js b/build/public/assets/index.855f4dd3.js deleted file mode 100644 index 5e9d456..0000000 --- a/build/public/assets/index.855f4dd3.js +++ /dev/null @@ -1 +0,0 @@ -import{d as e,c as t,a as n,w as o,b as l,r as a,o as s,e as i,t as r,F as d,f as c,g as u,h as p,v as g,i as h,j as m,p as y,k as f,l as w,m as v,n as b,q as C,s as x,u as k,x as P,y as A}from"./vendor.684f7bc8.js";var S=e({name:"app",computed:{showNav(){return!["game","replay"].includes(String(this.$route.name))}}});const T={id:"app"},z={key:0,class:"nav"},I=i("Games overview"),D=i("New game");S.render=function(e,i,r,d,c,u){const p=a("router-link"),g=a("router-view");return s(),t("div",T,[e.showNav?(s(),t("ul",z,[n("li",null,[n(p,{class:"btn",to:{name:"index"}},{default:o((()=>[I])),_:1})]),n("li",null,[n(p,{class:"btn",to:{name:"new-game"}},{default:o((()=>[D])),_:1})])])):l("",!0),n(g)])};const E=864e5,N=e=>{const t=Math.floor(e/E);e%=E;const n=Math.floor(e/36e5);e%=36e5;const o=Math.floor(e/6e4);e%=6e4;return`${t}d ${n}h ${o}m ${Math.floor(e/1e3)}s`};var M=1,_=1e3,V=()=>{const e=new Date;return Date.UTC(e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate(),e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds(),e.getUTCMilliseconds())},O=(e,t)=>N(t-e),U=N,B=e({name:"game-teaser",props:{game:{type:Object,required:!0}},computed:{style(){return{"background-image":`url("${this.game.imageUrl.replace("uploads/","uploads/r/")+"-375x210.webp"}")`}}},methods:{time(e,t){const n=t?"🏁":"⏳",o=e,l=t||V();return`${n} ${O(o,l)}`}}});const R={class:"game-info-text"},G=n("br",null,null,-1),$=n("br",null,null,-1),L=n("br",null,null,-1),F=i(" β†ͺ️ Watch replay ");B.render=function(e,d,c,u,p,g){const h=a("router-link");return s(),t("div",{class:"game-teaser",style:e.style},[n(h,{class:"game-info",to:{name:"game",params:{id:e.game.id}}},{default:o((()=>[n("span",R,[i(" 🧩 "+r(e.game.tilesFinished)+"/"+r(e.game.tilesTotal),1),G,i(" πŸ‘₯ "+r(e.game.players),1),$,i(" "+r(e.time(e.game.started,e.game.finished)),1),L])])),_:1},8,["to"]),e.game.hasReplay?(s(),t(h,{key:0,class:"game-replay",to:{name:"replay",params:{id:e.game.id}}},{default:o((()=>[F])),_:1},8,["to"])):l("",!0)],4)};var j=e({components:{GameTeaser:B},data:()=>({gamesRunning:[],gamesFinished:[]}),async created(){const e=await fetch("/api/index-data"),t=await e.json();this.gamesRunning=t.gamesRunning,this.gamesFinished=t.gamesFinished}});const W=n("h1",null,"Running games",-1),K=n("h1",null,"Finished games",-1);j.render=function(e,o,l,i,r,u){const p=a("game-teaser");return s(),t("div",null,[W,(s(!0),t(d,null,c(e.gamesRunning,((e,o)=>(s(),t("div",{class:"game-teaser-wrap",key:o},[n(p,{game:e},null,8,["game"])])))),128)),K,(s(!0),t(d,null,c(e.gamesFinished,((e,o)=>(s(),t("div",{class:"game-teaser-wrap",key:o},[n(p,{game:e},null,8,["game"])])))),128))])};var H=e({name:"image-teaser",props:{image:{type:Object,required:!0}},computed:{style(){return{backgroundImage:`url("${this.image.url.replace("uploads/","uploads/r/")+"-150x100.webp"}")`}}},emits:{click:null,editClick:null},methods:{onClick(){this.$emit("click")},onEditClick(){this.$emit("editClick")}}});H.render=function(e,o,l,a,i,r){return s(),t("div",{class:"imageteaser",style:e.style,onClick:o[2]||(o[2]=(...t)=>e.onClick&&e.onClick(...t))},[n("div",{class:"btn edit",onClick:o[1]||(o[1]=u(((...t)=>e.onEditClick&&e.onEditClick(...t)),["stop"]))},"✏️")],4)};var Y,q,Q,Z,X,J,ee,te,ne=e({name:"image-library",components:{ImageTeaser:H},props:{images:{type:Array,required:!0}},emits:{imageClicked:null,imageEditClicked:null},methods:{imageClicked(e){this.$emit("imageClicked",e)},imageEditClicked(e){this.$emit("imageEditClicked",e)}}});ne.render=function(e,n,o,l,i,r){const u=a("image-teaser");return s(),t("div",null,[(s(!0),t(d,null,c(e.images,((n,o)=>(s(),t(u,{image:n,onClick:t=>e.imageClicked(n),onEditClick:t=>e.imageEditClicked(n),key:o},null,8,["image","onClick","onEditClick"])))),128))])},(q=Y||(Y={}))[q.Flat=0]="Flat",q[q.Out=1]="Out",q[q.In=-1]="In",(Z=Q||(Q={}))[Z.FINAL=0]="FINAL",Z[Z.ANY=1]="ANY",(J=X||(X={}))[J.NORMAL=0]="NORMAL",J[J.ANY=1]="ANY",J[J.FLAT=2]="FLAT",(te=ee||(ee={}))[te.NORMAL=0]="NORMAL",te[te.REAL=1]="REAL";class oe{constructor(e){this.rand_high=e||3735929054,this.rand_low=1231121986^e}random(e,t){this.rand_high=(this.rand_high<<16)+(this.rand_high>>16)+this.rand_low&4294967295,this.rand_low=this.rand_low+this.rand_high&4294967295;return e+(this.rand_high>>>0)/4294967295*(t-e+1)|0}choice(e){return e[this.random(0,e.length-1)]}shuffle(e){const t=e.slice();for(let n=0;n<=t.length-2;n++){const e=this.random(n,t.length-1),o=t[n];t[n]=t[e],t[e]=o}return t}static serialize(e){return{rand_high:e.rand_high,rand_low:e.rand_low}}static unserialize(e){const t=new oe(0);return t.rand_high=e.rand_high,t.rand_low=e.rand_low,t}}const le=(e,t)=>{const n=`${e}`;return n.length>=t.length?n:t.substr(0,t.length-n.length)+n},ae=(...e)=>{const t=t=>(...n)=>{const o=new Date,l=le(o.getHours(),"00"),a=le(o.getMinutes(),"00"),s=le(o.getSeconds(),"00");console[t](`${l}:${a}:${s}`,...e,...n)};return{log:t("log"),error:t("error"),info:t("info")}};var se={hash:e=>{let t=0;for(let n=0;n{let t=e.toLowerCase();return t=t.replace(/[^a-z0-9]+/g,"-"),t=t.replace(/^-|-$/,""),t},uniqId:()=>Date.now().toString(36)+Math.random().toString(36).substring(2),encodeShape:function(e){return e.top+1<<0|e.right+1<<2|e.bottom+1<<4|e.left+1<<6},decodeShape:function(e){return{top:(e>>0&3)-1,right:(e>>2&3)-1,bottom:(e>>4&3)-1,left:(e>>6&3)-1}},encodePiece:function(e){return[e.idx,e.pos.x,e.pos.y,e.z,e.owner,e.group]},decodePiece:function(e){return{idx:e[0],pos:{x:e[1],y:e[2]},z:e[3],owner:e[4],group:e[5]}},encodePlayer:function(e){return[e.id,e.x,e.y,e.d,e.name,e.color,e.bgcolor,e.points,e.ts]},decodePlayer:function(e){return{id:e[0],x:e[1],y:e[2],d:e[3],name:e[4],color:e[5],bgcolor:e[6],points:e[7],ts:e[8]}},encodeGame:function(e){return[e.id,e.rng.type||"",oe.serialize(e.rng.obj),e.puzzle,e.players,e.evtInfos,e.scoreMode||Q.FINAL,e.shapeMode||X.ANY,e.snapMode||ee.NORMAL]},decodeGame:function(e){return{id:e[0],rng:{type:e[1],obj:oe.unserialize(e[2])},puzzle:e[3],players:e[4],evtInfos:e[5],scoreMode:e[6],shapeMode:e[7],snapMode:e[8]}},coordByPieceIdx:function(e,t){const n=e.width/e.tileSize;return{x:t%n,y:Math.floor(t/n)}},asQueryArgs:function(e){const t=[];for(const n in e){const o=[n,e[n]].map(encodeURIComponent);t.push(o.join("="))}return 0===t.length?"":`?${t.join("&")}`}};const ie={name:"responsive-image",props:{src:String,title:{type:String,default:""},height:{type:String,default:"100%"},width:{type:String,default:"100%"}},computed:{style(){return{display:"inline-block",verticalAlign:"text-bottom",backgroundImage:`url('${this.src}')`,backgroundRepeat:"no-repeat",backgroundSize:"contain",backgroundPosition:"center",width:this.width,height:this.height}}}};ie.render=function(e,n,o,l,a,i){return s(),t("div",{style:i.style,title:o.title},null,12,["title"])};var re=e({name:"tags-input",props:{modelValue:{type:Array,required:!0},autocompleteTags:{type:Function}},emits:{"update:modelValue":null},data:()=>({input:"",values:[],autocomplete:{idx:-1,values:[]}}),created(){this.values=this.modelValue},methods:{onKeyUp(e){return"ArrowDown"===e.code&&this.autocomplete.values.length>0?(this.autocomplete.idx0?(this.autocomplete.idx>0&&this.autocomplete.idx--,e.stopPropagation(),!1):","===e.key?(this.add(),e.stopPropagation(),!1):void(this.input&&this.autocompleteTags?(this.autocomplete.values=this.autocompleteTags(this.input,this.values),this.autocomplete.idx=-1):(this.autocomplete.values=[],this.autocomplete.idx=-1))},addVal(e){const t=e.replace(/,/g,"").trim();t&&(this.values.includes(t)||this.values.push(t),this.input="",this.autocomplete.values=[],this.autocomplete.idx=-1,this.$emit("update:modelValue",this.values),this.$refs.input.focus())},add(){const e=this.autocomplete.idx>=0?this.autocomplete.values[this.autocomplete.idx]:this.input;this.addVal(e)},rm(e){this.values=this.values.filter((t=>t!==e)),this.$emit("update:modelValue",this.values)}}});const de=m();y("data-v-a4fa5e7e");const ce={key:0,class:"autocomplete"};f();const ue=de(((e,o,a,i,u,m)=>(s(),t("div",null,[p(n("input",{ref:"input",class:"input",type:"text","onUpdate:modelValue":o[1]||(o[1]=t=>e.input=t),placeholder:"Plants, People",onChange:o[2]||(o[2]=(...t)=>e.onChange&&e.onChange(...t)),onKeydown:o[3]||(o[3]=h(((...t)=>e.add&&e.add(...t)),["enter"])),onKeyup:o[4]||(o[4]=(...t)=>e.onKeyUp&&e.onKeyUp(...t))},null,544),[[g,e.input]]),e.autocomplete.values?(s(),t("div",ce,[n("ul",null,[(s(!0),t(d,null,c(e.autocomplete.values,((n,o)=>(s(),t("li",{key:o,class:{active:o===e.autocomplete.idx},onClick:t=>e.addVal(n)},r(n),11,["onClick"])))),128))])])):l("",!0),(s(!0),t(d,null,c(e.values,((n,o)=>(s(),t("span",{key:o,class:"bit",onClick:t=>e.rm(n)},r(n)+" βœ–",9,["onClick"])))),128))]))));re.render=ue,re.__scopeId="data-v-a4fa5e7e";const pe=ae("NewImageDialog.vue");var ge=e({name:"new-image-dialog",components:{ResponsiveImage:ie,TagsInput:re},props:{autocompleteTags:{type:Function},uploadProgress:{type:Number},uploading:{type:String}},emits:{bgclick:null,setupGameClick:null,postToGalleryClick:null},data:()=>({previewUrl:"",file:null,title:"",tags:[],droppable:!1}),computed:{uploadProgressPercent(){return this.uploadProgress?Math.round(100*this.uploadProgress):0},canPostToGallery(){return!this.uploading&&!(!this.previewUrl||!this.file)},canSetupGameClick(){return!this.uploading&&!(!this.previewUrl||!this.file)}},methods:{imageFromDragEvt(e){var t;const n=null==(t=e.dataTransfer)?void 0:t.items;if(!n||0===n.length)return null;const o=n[0];return o.type.startsWith("image/")?o:null},onFileSelect(e){const t=e.target;if(!t.files)return;const n=t.files[0];n&&this.preview(n)},preview(e){const t=new FileReader;t.readAsDataURL(e),t.onload=t=>{this.previewUrl=t.target.result,this.file=e}},postToGallery(){this.$emit("postToGalleryClick",{file:this.file,title:this.title,tags:this.tags})},setupGameClick(){this.$emit("setupGameClick",{file:this.file,title:this.title,tags:this.tags})},onDrop(e){this.droppable=!1;const t=this.imageFromDragEvt(e);if(!t)return!1;const n=t.getAsFile();return!!n&&(this.file=n,this.preview(n),e.preventDefault(),!1)},onDragover(e){return!!this.imageFromDragEvt(e)&&(this.droppable=!0,e.preventDefault(),!1)},onDragleave(){pe.info("onDragleave"),this.droppable=!1}}});const he=n("div",{class:"drop-target"},null,-1),me={key:0,class:"has-image"},ye={key:1},fe={class:"upload"},we=n("span",{class:"btn"},"Upload File",-1),ve={class:"area-settings"},be=n("td",null,[n("label",null,"Title")],-1),Ce=n("tr",null,[n("td",{colspan:"2"},[n("div",{class:"hint"},"Feel free to leave a credit to the artist/photographer in the title :)")])],-1),xe=n("td",null,[n("label",null,"Tags")],-1),ke={class:"area-buttons"},Pe=i("πŸ–ΌοΈ Post to gallery"),Ae=i("🧩 Post to gallery "),Se=n("br",null,null,-1),Te=i(" + set up game");ge.render=function(e,o,l,c,h,m){const y=a("responsive-image"),f=a("tags-input");return s(),t("div",{class:"overlay new-image-dialog",onClick:o[11]||(o[11]=t=>e.$emit("bgclick"))},[n("div",{class:"overlay-content",onClick:o[10]||(o[10]=u((()=>{}),["stop"]))},[n("div",{class:["area-image",{"has-image":!!e.previewUrl,"no-image":!e.previewUrl,droppable:e.droppable}],onDrop:o[3]||(o[3]=(...t)=>e.onDrop&&e.onDrop(...t)),onDragover:o[4]||(o[4]=(...t)=>e.onDragover&&e.onDragover(...t)),onDragleave:o[5]||(o[5]=(...t)=>e.onDragleave&&e.onDragleave(...t))},[he,e.previewUrl?(s(),t("div",me,[n("span",{class:"remove btn",onClick:o[1]||(o[1]=t=>e.previewUrl="")},"X"),n(y,{src:e.previewUrl},null,8,["src"])])):(s(),t("div",ye,[n("label",fe,[n("input",{type:"file",style:{display:"none"},onChange:o[2]||(o[2]=(...t)=>e.onFileSelect&&e.onFileSelect(...t)),accept:"image/*"},null,32),we])]))],34),n("div",ve,[n("table",null,[n("tr",null,[be,n("td",null,[p(n("input",{type:"text","onUpdate:modelValue":o[6]||(o[6]=t=>e.title=t),placeholder:"Flower by @artist"},null,512),[[g,e.title]])])]),Ce,n("tr",null,[xe,n("td",null,[n(f,{modelValue:e.tags,"onUpdate:modelValue":o[7]||(o[7]=t=>e.tags=t),autocompleteTags:e.autocompleteTags},null,8,["modelValue","autocompleteTags"])])])])]),n("div",ke,[n("button",{class:"btn",disabled:!e.canPostToGallery,onClick:o[8]||(o[8]=(...t)=>e.postToGallery&&e.postToGallery(...t))},["postToGallery"===e.uploading?(s(),t(d,{key:0},[i("Uploading ("+r(e.uploadProgressPercent)+"%)",1)],64)):(s(),t(d,{key:1},[Pe],64))],8,["disabled"]),n("button",{class:"btn",disabled:!e.canSetupGameClick,onClick:o[9]||(o[9]=(...t)=>e.setupGameClick&&e.setupGameClick(...t))},["setupGame"===e.uploading?(s(),t(d,{key:0},[i("Uploading ("+r(e.uploadProgressPercent)+"%)",1)],64)):(s(),t(d,{key:1},[Ae,Se,Te],64))],8,["disabled"])])])])};var ze=e({name:"edit-image-dialog",components:{ResponsiveImage:ie,TagsInput:re},props:{image:{type:Object,required:!0},autocompleteTags:{type:Function}},emits:{bgclick:null,saveClick:null},data:()=>({title:"",tags:[]}),created(){this.title=this.image.title,this.tags=this.image.tags.map((e=>e.title))},methods:{saveImage(){this.$emit("saveClick",{id:this.image.id,title:this.title,tags:this.tags})}}});const Ie={class:"area-image"},De={class:"has-image"},Ee={class:"area-settings"},Ne=n("td",null,[n("label",null,"Title")],-1),Me=n("tr",null,[n("td",{colspan:"2"},[n("div",{class:"hint"},"Feel free to leave a credit to the artist/photographer in the title :)")])],-1),_e=n("td",null,[n("label",null,"Tags")],-1),Ve={class:"area-buttons"};ze.render=function(e,o,l,i,r,d){const c=a("responsive-image"),h=a("tags-input");return s(),t("div",{class:"overlay edit-image-dialog",onClick:o[5]||(o[5]=t=>e.$emit("bgclick"))},[n("div",{class:"overlay-content",onClick:o[4]||(o[4]=u((()=>{}),["stop"]))},[n("div",Ie,[n("div",De,[n(c,{src:e.image.url,title:e.image.title},null,8,["src","title"])])]),n("div",Ee,[n("table",null,[n("tr",null,[Ne,n("td",null,[p(n("input",{type:"text","onUpdate:modelValue":o[1]||(o[1]=t=>e.title=t),placeholder:"Flower by @artist"},null,512),[[g,e.title]])])]),Me,n("tr",null,[_e,n("td",null,[n(h,{modelValue:e.tags,"onUpdate:modelValue":o[2]||(o[2]=t=>e.tags=t),autocompleteTags:e.autocompleteTags},null,8,["modelValue","autocompleteTags"])])])])]),n("div",Ve,[n("button",{class:"btn",onClick:o[3]||(o[3]=(...t)=>e.saveImage&&e.saveImage(...t))},"πŸ–ΌοΈ Save image")])])])};var Oe=e({name:"new-game-dialog",components:{ResponsiveImage:ie},props:{image:{type:Object,required:!0}},emits:{newGame:null,bgclick:null},data:()=>({tiles:1e3,scoreMode:Q.ANY,shapeMode:X.NORMAL,snapMode:ee.NORMAL}),methods:{onNewGameClick(){this.$emit("newGame",{tiles:this.tilesInt,image:this.image,scoreMode:this.scoreModeInt,shapeMode:this.shapeModeInt,snapMode:this.snapModeInt})}},computed:{canStartNewGame(){return!!(this.tilesInt&&this.image&&this.image.url&&[0,1].includes(this.scoreModeInt))},scoreModeInt(){return parseInt(`${this.scoreMode}`,10)},shapeModeInt(){return parseInt(`${this.shapeMode}`,10)},snapModeInt(){return parseInt(`${this.snapMode}`,10)},tilesInt(){return parseInt(`${this.tiles}`,10)}}});const Ue={class:"area-image"},Be={class:"has-image"},Re={key:0,class:"image-title"},Ge={key:0,class:"image-title-title"},$e={key:1,class:"image-title-dim"},Le={class:"area-settings"},Fe=n("td",null,[n("label",null,"Pieces")],-1),je=n("td",null,[n("label",null,"Scoring: ")],-1),We=i(" Any (Score when pieces are connected to each other or on final location)"),Ke=n("br",null,null,-1),He=i(" Final (Score when pieces are put to their final location)"),Ye=n("td",null,[n("label",null,"Shapes: ")],-1),qe=i(" Normal"),Qe=n("br",null,null,-1),Ze=i(" Any (flat pieces can occur anywhere)"),Xe=n("br",null,null,-1),Je=i(" Flat (all pieces flat on all sides)"),et=n("td",null,[n("label",null,"Snapping: ")],-1),tt=i(" Normal (pieces snap to final destination and to each other)"),nt=n("br",null,null,-1),ot=i(" Real (pieces snap only to corners, already snapped pieces and to each other)"),lt={class:"area-buttons"};Oe.render=function(e,o,i,d,c,h){const m=a("responsive-image");return s(),t("div",{class:"overlay new-game-dialog",onClick:o[11]||(o[11]=t=>e.$emit("bgclick"))},[n("div",{class:"overlay-content",onClick:o[10]||(o[10]=u((()=>{}),["stop"]))},[n("div",Ue,[n("div",Be,[n(m,{src:e.image.url,title:e.image.title},null,8,["src","title"])]),e.image.title||e.image.width||e.image.height?(s(),t("div",Re,[e.image.title?(s(),t("span",Ge,'"'+r(e.image.title)+'"',1)):l("",!0),e.image.width||e.image.height?(s(),t("span",$e,"("+r(e.image.width)+" βœ• "+r(e.image.height)+")",1)):l("",!0)])):l("",!0)]),n("div",Le,[n("table",null,[n("tr",null,[Fe,n("td",null,[p(n("input",{type:"text","onUpdate:modelValue":o[1]||(o[1]=t=>e.tiles=t)},null,512),[[g,e.tiles]])])]),n("tr",null,[je,n("td",null,[n("label",null,[p(n("input",{type:"radio","onUpdate:modelValue":o[2]||(o[2]=t=>e.scoreMode=t),value:"1"},null,512),[[w,e.scoreMode]]),We]),Ke,n("label",null,[p(n("input",{type:"radio","onUpdate:modelValue":o[3]||(o[3]=t=>e.scoreMode=t),value:"0"},null,512),[[w,e.scoreMode]]),He])])]),n("tr",null,[Ye,n("td",null,[n("label",null,[p(n("input",{type:"radio","onUpdate:modelValue":o[4]||(o[4]=t=>e.shapeMode=t),value:"0"},null,512),[[w,e.shapeMode]]),qe]),Qe,n("label",null,[p(n("input",{type:"radio","onUpdate:modelValue":o[5]||(o[5]=t=>e.shapeMode=t),value:"1"},null,512),[[w,e.shapeMode]]),Ze]),Xe,n("label",null,[p(n("input",{type:"radio","onUpdate:modelValue":o[6]||(o[6]=t=>e.shapeMode=t),value:"2"},null,512),[[w,e.shapeMode]]),Je])])]),n("tr",null,[et,n("td",null,[n("label",null,[p(n("input",{type:"radio","onUpdate:modelValue":o[7]||(o[7]=t=>e.snapMode=t),value:"0"},null,512),[[w,e.snapMode]]),tt]),nt,n("label",null,[p(n("input",{type:"radio","onUpdate:modelValue":o[8]||(o[8]=t=>e.snapMode=t),value:"1"},null,512),[[w,e.snapMode]]),ot])])])])]),n("div",lt,[n("button",{class:"btn",disabled:!e.canStartNewGame,onClick:o[9]||(o[9]=(...t)=>e.onNewGameClick&&e.onNewGameClick(...t))}," 🧩 Generate Puzzle ",8,["disabled"])])])])};const at=async(e,t,n)=>new Promise(((o,l)=>{const a=new window.XMLHttpRequest;a.open(e,t,!0),a.withCredentials=!0;for(const e in n.headers||{})a.setRequestHeader(e,n.headers[e]);a.addEventListener("load",(function(e){o({status:this.status,text:this.responseText,json:async()=>JSON.parse(this.responseText)})})),a.addEventListener("error",(function(e){l(new Error("xhr error"))})),a.upload&&n.onUploadProgress&&a.upload.addEventListener("progress",(function(e){n.onUploadProgress&&n.onUploadProgress(e)})),a.send(n.body)}));var st=(e,t)=>at("post",e,t),it=e({components:{ImageLibrary:ne,NewImageDialog:ge,EditImageDialog:ze,NewGameDialog:Oe},data:()=>({filters:{sort:"date_desc",tags:[]},images:[],tags:[],image:{id:0,filename:"",file:"",url:"",title:"",tags:[],created:0},dialog:"",uploading:"",uploadProgress:0}),async created(){await this.loadImages()},computed:{relevantTags(){return this.tags.filter((e=>e.total>0))}},methods:{autocompleteTags(e,t){return this.tags.filter((n=>!t.includes(n.title)&&n.title.toLowerCase().startsWith(e.toLowerCase()))).slice(0,10).map((e=>e.title))},toggleTag(e){this.filters.tags.includes(e.slug)?this.filters.tags=this.filters.tags.filter((t=>t!==e.slug)):this.filters.tags.push(e.slug),this.filtersChanged()},async loadImages(){const e=await fetch(`/api/newgame-data${se.asQueryArgs(this.filters)}`),t=await e.json();this.images=t.images,this.tags=t.tags},async filtersChanged(){await this.loadImages()},onImageClicked(e){this.image=e,this.dialog="new-game"},onImageEditClicked(e){this.image=e,this.dialog="edit-image"},async uploadImage(e){this.uploadProgress=0;const t=new FormData;t.append("file",e.file,e.file.name),t.append("title",e.title),t.append("tags",e.tags);const n=await st("/api/upload",{body:t,onUploadProgress:e=>{this.uploadProgress=e.loaded/e.total}});return this.uploadProgress=1,await n.json()},async saveImage(e){const t=await fetch("/api/save-image",{method:"post",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify({id:e.id,title:e.title,tags:e.tags})});return await t.json()},async onSaveImageClick(e){await this.saveImage(e),this.dialog="",await this.loadImages()},async postToGalleryClick(e){this.uploading="postToGallery",await this.uploadImage(e),this.uploading="",this.dialog="",await this.loadImages()},async setupGameClick(e){this.uploading="setupGame";const t=await this.uploadImage(e);this.uploading="",this.loadImages(),this.image=t,this.dialog="new-game"},async onNewGame(e){const t=await fetch("/api/newgame",{method:"post",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(e)});if(200===t.status){const e=await t.json();this.$router.push({name:"game",params:{id:e.id}})}}}});const rt={class:"upload-image-teaser"},dt=n("div",{class:"hint"},"(The image you upload will be added to the public gallery.)",-1),ct={key:0},ut=i(" Tags: "),pt=i(" Sort by: "),gt=n("option",{value:"date_desc"},"Newest first",-1),ht=n("option",{value:"date_asc"},"Oldest first",-1),mt=n("option",{value:"alpha_asc"},"A-Z",-1),yt=n("option",{value:"alpha_desc"},"Z-A",-1);it.render=function(e,o,i,u,g,h){const m=a("image-library"),y=a("new-image-dialog"),f=a("edit-image-dialog"),w=a("new-game-dialog");return s(),t("div",null,[n("div",rt,[n("div",{class:"btn btn-big",onClick:o[1]||(o[1]=t=>e.dialog="new-image")},"Upload your image"),dt]),n("div",null,[e.tags.length>0?(s(),t("label",ct,[ut,(s(!0),t(d,null,c(e.relevantTags,((n,o)=>(s(),t("span",{class:["bit",{on:e.filters.tags.includes(n.slug)}],key:o,onClick:t=>e.toggleTag(n)},r(n.title)+" ("+r(n.total)+")",11,["onClick"])))),128))])):l("",!0),n("label",null,[pt,p(n("select",{"onUpdate:modelValue":o[2]||(o[2]=t=>e.filters.sort=t),onChange:o[3]||(o[3]=(...t)=>e.filtersChanged&&e.filtersChanged(...t))},[gt,ht,mt,yt],544),[[v,e.filters.sort]])])]),n(m,{images:e.images,onImageClicked:e.onImageClicked,onImageEditClicked:e.onImageEditClicked},null,8,["images","onImageClicked","onImageEditClicked"]),"new-image"===e.dialog?(s(),t(y,{key:0,autocompleteTags:e.autocompleteTags,onBgclick:o[4]||(o[4]=t=>e.dialog=""),uploadProgress:e.uploadProgress,uploading:e.uploading,onPostToGalleryClick:e.postToGalleryClick,onSetupGameClick:e.setupGameClick},null,8,["autocompleteTags","uploadProgress","uploading","onPostToGalleryClick","onSetupGameClick"])):l("",!0),"edit-image"===e.dialog?(s(),t(f,{key:1,autocompleteTags:e.autocompleteTags,onBgclick:o[5]||(o[5]=t=>e.dialog=""),onSaveClick:e.onSaveImageClick,image:e.image},null,8,["autocompleteTags","onSaveClick","image"])):l("",!0),e.image&&"new-game"===e.dialog?(s(),t(w,{key:2,onBgclick:o[6]||(o[6]=t=>e.dialog=""),onNewGame:e.onNewGame,image:e.image},null,8,["onNewGame","image"])):l("",!0)])};var ft=e({name:"scores",props:{activePlayers:{type:Array,required:!0},idlePlayers:{type:Array,required:!0}},computed:{actives(){return this.activePlayers.sort(((e,t)=>t.points-e.points)),this.activePlayers},idles(){return this.idlePlayers.sort(((e,t)=>t.points-e.points)),this.idlePlayers}}});const wt={class:"scores"},vt=n("div",null,"Scores",-1),bt=n("td",null,"⚑",-1),Ct=n("td",null,"πŸ’€",-1);ft.render=function(e,o,l,a,i,u){return s(),t("div",wt,[vt,n("table",null,[(s(!0),t(d,null,c(e.actives,((e,o)=>(s(),t("tr",{key:o,style:{color:e.color}},[bt,n("td",null,r(e.name),1),n("td",null,r(e.points),1)],4)))),128)),(s(!0),t(d,null,c(e.idles,((e,o)=>(s(),t("tr",{key:o,style:{color:e.color}},[Ct,n("td",null,r(e.name),1),n("td",null,r(e.points),1)],4)))),128))])])};var xt=e({name:"puzzle-status",props:{finished:{type:Boolean,required:!0},duration:{type:Number,required:!0},piecesDone:{type:Number,required:!0},piecesTotal:{type:Number,required:!0}},computed:{icon(){return this.finished?"🏁":"⏳"},durationStr(){return U(this.duration)}}});const kt={class:"timer"};xt.render=function(e,o,l,a,i,d){return s(),t("div",kt,[n("div",null," 🧩 "+r(e.piecesDone)+"/"+r(e.piecesTotal),1),n("div",null,r(e.icon)+" "+r(e.durationStr),1),b(e.$slots,"default")])};var Pt=e({name:"settings-overlay",emits:{bgclick:null,"update:modelValue":null},props:{modelValue:{type:Object,required:!0}},methods:{updateVolume(e){this.modelValue.soundsVolume=e.target.value},decreaseVolume(){const e=parseInt(this.modelValue.soundsVolume,10)-5;this.modelValue.soundsVolume=Math.max(0,e)},increaseVolume(){const e=parseInt(this.modelValue.soundsVolume,10)+5;this.modelValue.soundsVolume=Math.min(100,e)}},created(){this.$watch("modelValue",(e=>{this.$emit("update:modelValue",e)}),{deep:!0})}});const At=m();y("data-v-4d56fc17");const St=n("td",null,[n("label",null,"Background: ")],-1),Tt=n("td",null,[n("label",null,"Color: ")],-1),zt=n("td",null,[n("label",null,"Name: ")],-1),It=n("td",null,[n("label",null,"Sounds: ")],-1),Dt=n("td",null,[n("label",null,"Sounds Volume: ")],-1),Et={class:"sound-volume"},Nt=n("td",null,[n("label",null,"Show player names: ")],-1);f();const Mt=At(((e,o,l,a,i,r)=>(s(),t("div",{class:"overlay transparent",onClick:o[10]||(o[10]=t=>e.$emit("bgclick"))},[n("table",{class:"overlay-content settings",onClick:o[9]||(o[9]=u((()=>{}),["stop"]))},[n("tr",null,[St,n("td",null,[p(n("input",{type:"color","onUpdate:modelValue":o[1]||(o[1]=t=>e.modelValue.background=t)},null,512),[[g,e.modelValue.background]])])]),n("tr",null,[Tt,n("td",null,[p(n("input",{type:"color","onUpdate:modelValue":o[2]||(o[2]=t=>e.modelValue.color=t)},null,512),[[g,e.modelValue.color]])])]),n("tr",null,[zt,n("td",null,[p(n("input",{type:"text",maxLength:"16","onUpdate:modelValue":o[3]||(o[3]=t=>e.modelValue.name=t)},null,512),[[g,e.modelValue.name]])])]),n("tr",null,[It,n("td",null,[p(n("input",{type:"checkbox","onUpdate:modelValue":o[4]||(o[4]=t=>e.modelValue.soundsEnabled=t)},null,512),[[C,e.modelValue.soundsEnabled]])])]),n("tr",null,[Dt,n("td",Et,[n("span",{onClick:o[5]||(o[5]=(...t)=>e.decreaseVolume&&e.decreaseVolume(...t))},"πŸ”‰"),n("input",{type:"range",min:"0",max:"100",value:e.modelValue.soundsVolume,onChange:o[6]||(o[6]=(...t)=>e.updateVolume&&e.updateVolume(...t))},null,40,["value"]),n("span",{onClick:o[7]||(o[7]=(...t)=>e.increaseVolume&&e.increaseVolume(...t))},"πŸ”Š")])]),n("tr",null,[Nt,n("td",null,[p(n("input",{type:"checkbox","onUpdate:modelValue":o[8]||(o[8]=t=>e.modelValue.showPlayerNames=t)},null,512),[[C,e.modelValue.showPlayerNames]])])])])]))));Pt.render=Mt,Pt.__scopeId="data-v-4d56fc17";var _t=e({name:"preview-overlay",props:{img:String},emits:{bgclick:null},computed:{previewStyle(){return{backgroundImage:`url('${this.img}')`}}}});const Vt={class:"preview"};_t.render=function(e,o,l,a,i,r){return s(),t("div",{class:"overlay",onClick:o[1]||(o[1]=t=>e.$emit("bgclick"))},[n("div",Vt,[n("div",{class:"img",style:e.previewStyle},null,4)])])};var Ot=1,Ut=4,Bt=2,Rt=3,Gt=2,$t=4,Lt=3,Ft=9,jt=1,Wt=2,Kt=3,Ht=4,Yt=5,qt=6,Qt=7,Zt=8,Xt=10,Jt=11,en=12,tn=13,nn=14,on=15,ln=16,an=1,sn=2,rn=3;const dn=ae("Communication.js");let cn,un=[],pn=e=>{un.push(e)},gn=[],hn=e=>{gn.push(e)};let mn=0;const yn=e=>{mn!==e&&(mn=e,hn(e))};function fn(e){if(2===mn)try{cn.send(JSON.stringify(e))}catch(t){dn.info("unable to send message.. maybe because ws is invalid?")}}let wn,vn;var bn={connect:function(e,t,n){return wn=0,vn={},yn(3),new Promise((o=>{cn=new WebSocket(e,n+"|"+t),cn.onopen=()=>{yn(2),fn([Rt])},cn.onmessage=e=>{const t=JSON.parse(e.data),l=t[0];if(l===Ut){const e=t[1];o(e)}else{if(l!==Ot)throw`[ 2021-05-09 invalid connect msgType ${l} ]`;{const e=t[1],o=t[2];if(e===n&&vn[o])return void delete vn[o];pn(t)}}},cn.onerror=()=>{throw yn(1),"[ 2021-05-15 onerror ]"},cn.onclose=e=>{4e3===e.code||1001===e.code?yn(4):yn(1)}}))},requestReplayData:async function(e,t){const n={gameId:e,offset:t},o=await fetch(`/api/replay-data${se.asQueryArgs(n)}`);return await o.json()},disconnect:function(){cn&&cn.close(4e3),wn=0,vn={}},sendClientEvent:function(e){wn++,vn[wn]=e,fn([Bt,wn,vn[wn]])},onServerChange:function(e){pn=e;for(const t of un)pn(t);un=[]},onConnectionStateChange:function(e){hn=e;for(const t of gn)hn(t);gn=[]},CODE_CUSTOM_DISCONNECT:4e3,CONN_STATE_NOT_CONNECTED:0,CONN_STATE_DISCONNECTED:1,CONN_STATE_CLOSED:4,CONN_STATE_CONNECTED:2,CONN_STATE_CONNECTING:3},Cn=e({name:"connection-overlay",emits:{reconnect:null},props:{connectionState:Number},computed:{lostConnection(){return this.connectionState===bn.CONN_STATE_DISCONNECTED},connecting(){return this.connectionState===bn.CONN_STATE_CONNECTING},show(){return!(!this.lostConnection&&!this.connecting)}}});const xn={key:0,class:"overlay connection-lost"},kn={key:0,class:"overlay-content"},Pn=n("div",null,"⁉️ LOST CONNECTION ⁉️",-1),An={key:1,class:"overlay-content"},Sn=n("div",null,"Connecting...",-1);Cn.render=function(e,o,a,i,r,d){return e.show?(s(),t("div",xn,[e.lostConnection?(s(),t("div",kn,[Pn,n("span",{class:"btn",onClick:o[1]||(o[1]=t=>e.$emit("reconnect"))},"Reconnect")])):l("",!0),e.connecting?(s(),t("div",An,[Sn])):l("",!0)])):l("",!0)};var Tn=e({name:"help-overlay",emits:{bgclick:null}});const zn=n("tr",null,[n("td",null,"⬆️ Move up:"),n("td",null,[n("div",null,[n("kbd",null,"W"),i("/"),n("kbd",null,"↑"),i("/πŸ–±οΈ")])])],-1),In=n("tr",null,[n("td",null,"⬇️ Move down:"),n("td",null,[n("div",null,[n("kbd",null,"S"),i("/"),n("kbd",null,"↓"),i("/πŸ–±οΈ")])])],-1),Dn=n("tr",null,[n("td",null,"⬅️ Move left:"),n("td",null,[n("div",null,[n("kbd",null,"A"),i("/"),n("kbd",null,"←"),i("/πŸ–±οΈ")])])],-1),En=n("tr",null,[n("td",null,"➑️ Move right:"),n("td",null,[n("div",null,[n("kbd",null,"D"),i("/"),n("kbd",null,"β†’"),i("/πŸ–±οΈ")])])],-1),Nn=n("tr",null,[n("td"),n("td",null,[n("div",null,[i("Move faster by holding "),n("kbd",null,"Shift")])])],-1),Mn=n("tr",null,[n("td",null,"πŸ”+ Zoom in:"),n("td",null,[n("div",null,[n("kbd",null,"E"),i("/πŸ–±οΈ-Wheel")])])],-1),_n=n("tr",null,[n("td",null,"πŸ”- Zoom out:"),n("td",null,[n("div",null,[n("kbd",null,"Q"),i("/πŸ–±οΈ-Wheel")])])],-1),Vn=n("tr",null,[n("td",null,"πŸ–ΌοΈ Toggle preview:"),n("td",null,[n("div",null,[n("kbd",null,"Space")])])],-1),On=n("tr",null,[n("td",null,"🎯 Center puzzle in screen:"),n("td",null,[n("div",null,[n("kbd",null,"C")])])],-1),Un=n("tr",null,[n("td",null,"πŸ§©βœ”οΈ Toggle fixed pieces:"),n("td",null,[n("div",null,[n("kbd",null,"F")])])],-1),Bn=n("tr",null,[n("td",null,"πŸ§©β“ Toggle loose pieces:"),n("td",null,[n("div",null,[n("kbd",null,"G")])])],-1),Rn=n("tr",null,[n("td",null,"πŸ‘€ Toggle player names:"),n("td",null,[n("div",null,[n("kbd",null,"N")])])],-1),Gn=n("tr",null,[n("td",null,"πŸ”‰ Toggle sounds:"),n("td",null,[n("div",null,[n("kbd",null,"M")])])],-1),$n=n("tr",null,[n("td",null,"⏫ Speed up (replay):"),n("td",null,[n("div",null,[n("kbd",null,"I")])])],-1),Ln=n("tr",null,[n("td",null,"⏬ Speed down (replay):"),n("td",null,[n("div",null,[n("kbd",null,"O")])])],-1),Fn=n("tr",null,[n("td",null,"⏸️ Pause (replay):"),n("td",null,[n("div",null,[n("kbd",null,"P")])])],-1);Tn.render=function(e,o,l,a,i,r){return s(),t("div",{class:"overlay transparent",onClick:o[2]||(o[2]=t=>e.$emit("bgclick"))},[n("table",{class:"overlay-content help",onClick:o[1]||(o[1]=u((()=>{}),["stop"]))},[zn,In,Dn,En,Nn,Mn,_n,Vn,On,Un,Bn,Rn,Gn,$n,Ln,Fn])])};var jn=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"/assets/click.bb97cb07.mp3"}),Wn=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAW0lEQVQ4je1RywrAIAxLxP//5exixRWlVgZelpOKeTQFfnDypgy3eLIkSLLL8mxGPoHsU2hPAgDHBLvRX6hZZw/fwT0BtlLSONqCbWAmEIqMZOCDDlaDR3N03gOyDCn+y4DWmAAAAABJRU5ErkJggg=="}),Kn=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAARElEQVQ4jWNgGAU0Af+hmBCbgYGBgYERhwHEAEYGBgYGJtIdiApYyLAZBVDsAqoagC1ACQJyY4ERg0GCISh6KA4DigEAou8LC+LnIJoAAAAASUVORK5CYII="}),Hn=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAcUlEQVQ4ja1TQQ7AIAgD///n7jCozA2Hbk00jbG1KIrcARszTugoBs49qioZj7r2kKACptkyAOCJsJuA+GzglwHjvMSSWFVaENWVASxh5eRLiq5fN/ASjI89VcP2K3hHpq1cEXNaMfnrL3TDZP2tDuoOA6MzCCXWr38AAAAASUVORK5CYII="}),Yn=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAU0lEQVQ4jWNgoAH4D8X42HDARKlt5BoAd82AuQAOGLGIYQQUPv0wF5CiCQUge4EsQ5C9QI4BjMguwBYeBAElscCIy1ZivMKIwSDBEBQ9FCckigEAU3QOD7TGvY4AAAAASUVORK5CYII="});function qn(e=0,t=0){const n=document.createElement("canvas");return n.width=e,n.height=t,n}var Qn={createCanvas:qn,loadImageToBitmap:async function(e){return new Promise((t=>{const n=new Image;n.onload=()=>{createImageBitmap(n).then(t)},n.src=e}))},resizeBitmap:async function(e,t,n){const o=qn(t,n);return o.getContext("2d").drawImage(e,0,0,e.width,e.height,0,0,t,n),await createImageBitmap(o)},colorizedCanvas:function(e,t,n){const o=qn(e.width,e.height),l=o.getContext("2d");return l.save(),l.drawImage(t,0,0),l.fillStyle=n,l.globalCompositeOperation="source-in",l.fillRect(0,0,t.width,t.height),l.restore(),l.save(),l.globalCompositeOperation="destination-over",l.drawImage(e,0,0),l.restore(),o}};const Zn=ae("Debug.js");let Xn=0,Jn=0;var eo=e=>{Xn=performance.now(),Jn=e},to=e=>{const t=performance.now(),n=t-Xn;n>Jn&&Zn.log(e+": "+n),Xn=t};function no(e,t){const n=e.x-t.x,o=e.y-t.y;return Math.sqrt(n*n+o*o)}function oo(e){return{x:e.x+e.w/2,y:e.y+e.h/2}}var lo={pointSub:function(e,t){return{x:e.x-t.x,y:e.y-t.y}},pointAdd:function(e,t){return{x:e.x+t.x,y:e.y+t.y}},pointDistance:no,pointInBounds:function(e,t){return e.x>=t.x&&e.x<=t.x+t.w&&e.y>=t.y&&e.y<=t.y+t.h},rectCenter:oo,rectMoved:function(e,t,n){return{x:e.x+t,y:e.y+n,w:e.w,h:e.h}},rectCenterDistance:function(e,t){return no(oo(e),oo(t))},rectsOverlap:function(e,t){return!(t.x>e.x+e.w||e.x>t.x+t.w||t.y>e.y+e.h||e.y>t.y+t.h)}};const ao=ae("PuzzleGraphics.js");function so(e,t){const n=se.coordByPieceIdx(e,t);return{x:n.x*e.tileSize,y:n.y*e.tileSize,w:e.tileSize,h:e.tileSize}}var io={loadPuzzleBitmaps:async function(e){const t=await Qn.loadImageToBitmap(e.info.imageUrl),n=await Qn.resizeBitmap(t,e.info.width,e.info.height);return await async function(e,t,n){ao.log("start createPuzzleTileBitmaps");const o=n.tileSize,l=n.tileMarginWidth,a=n.tileDrawSize,s=o/100,i=[0,0,40,15,37,5,37,5,40,0,38,-5,38,-5,20,-20,50,-20,50,-20,80,-20,62,-5,62,-5,60,0,63,5,63,5,65,15,100,0],r=new Array(t.length),d={};function c(e){const t=`${e.top}${e.right}${e.left}${e.bottom}`;if(d[t])return d[t];const n=new Path2D,a={x:l,y:l},r=lo.pointAdd(a,{x:o,y:0}),c=lo.pointAdd(r,{x:0,y:o}),u=lo.pointSub(c,{x:o,y:0});if(n.moveTo(a.x,a.y),0!==e.top)for(let o=0;ose.decodePiece(ro[e].puzzle.tiles[t]),Po=(e,t)=>ko(e,t).group,Ao=(e,t)=>{const n=ro[e].puzzle.info;return 0===t||t===n.tilesX-1||t===n.tiles-n.tilesX||t===n.tiles-1},So=(e,t)=>{const n=ro[e].puzzle.info,o={x:(n.table.width-n.width)/2,y:(n.table.height-n.height)/2},l=function(e,t){const n=ro[e].puzzle.info,o=se.coordByPieceIdx(n,t),l=o.x*n.tileSize,a=o.y*n.tileSize;return{x:l,y:a}}(e,t);return lo.pointAdd(o,l)},To=(e,t)=>ko(e,t).pos,zo=e=>{const t=Wo(e),n=Ko(e),o=Math.round(t/4),l=Math.round(n/4);return{x:0-o,y:0-l,w:t+2*o,h:n+2*l}},Io=(e,t)=>{const n=Mo(e),o=ko(e,t);return{x:o.pos.x,y:o.pos.y,w:n,h:n}},Do=(e,t)=>ko(e,t).z,Eo=(e,t)=>{for(const n of ro[e].puzzle.tiles){const e=se.decodePiece(n);if(e.owner===t)return e.idx}return-1},No=e=>ro[e].puzzle.info.tileDrawSize,Mo=e=>ro[e].puzzle.info.tileSize,_o=e=>ro[e].puzzle.data.maxGroup,Vo=e=>ro[e].puzzle.data.maxZ;function Oo(e,t){const n=ro[e].puzzle.info,o=se.coordByPieceIdx(n,t);return[o.y>0?t-n.tilesX:-1,o.x0?t-1:-1]}const Uo=(e,t,n)=>{for(const o of t)xo(e,o,{z:n})},Bo=(e,t,n)=>{const o=To(e,t);xo(e,t,{pos:lo.pointAdd(o,n)})},Ro=(e,t,n)=>{const o=No(e),l=zo(e),a=n;for(const s of t){const t=ko(e,s);t.pos.x+n.xl.x+l.w&&(a.x=Math.min(l.x+l.w-t.pos.x+o,a.x)),t.pos.y+n.yl.y+l.h&&(a.y=Math.min(l.y+l.h-t.pos.y+o,a.y))}for(const s of t)Bo(e,s,a)},Go=(e,t)=>ko(e,t).owner,$o=(e,t)=>{for(const n of t)xo(e,n,{owner:-1,z:1})},Lo=(e,t,n)=>{for(const o of t)xo(e,o,{owner:n})};function Fo(e,t){const n=ro[e].puzzle.tiles,o=se.decodePiece(n[t]),l=[];if(o.group)for(const a of n){const e=se.decodePiece(a);e.group===o.group&&l.push(e.idx)}else l.push(o.idx);return l}const jo=(e,t)=>{const n=uo(e,t);return n?n.points:0},Wo=e=>ro[e].puzzle.info.table.width,Ko=e=>ro[e].puzzle.info.table.height;var Ho={setGame:function(e,t){ro[e]=t},exists:function(e){return!!ro[e]||!1},playerExists:go,getActivePlayers:function(e,t){const n=t-30*_;return ho(e).filter((e=>e.ts>=n))},getIdlePlayers:function(e,t){const n=t-30*_;return ho(e).filter((e=>e.ts0))},addPlayer:function(e,t,n){go(e,t)?bo(e,t,{ts:n}):po(e,t,function(e,t){return{id:e,x:0,y:0,d:0,name:null,color:null,bgcolor:null,points:0,ts:t}}(t,n))},getFinishedPiecesCount:vo,getPieceCount:mo,getImageUrl:function(e){return ro[e].puzzle.info.imageUrl},setImageUrl:function(e,t){ro[e].puzzle.info.imageUrl=t},get:function(e){return ro[e]||null},getAllGames:function(){return Object.values(ro).sort(((e,t)=>wo(e.id)===wo(t.id)?t.puzzle.data.started-e.puzzle.data.started:wo(e.id)?1:-1))},getPlayerBgColor:(e,t)=>{const n=uo(e,t);return n?n.bgcolor:null},getPlayerColor:(e,t)=>{const n=uo(e,t);return n?n.color:null},getPlayerName:(e,t)=>{const n=uo(e,t);return n?n.name:null},getPlayerIndexById:co,getPlayerIdByIndex:function(e,t){return ro[e].players.length>t?se.decodePlayer(ro[e].players[t]).id:null},changePlayer:bo,setPlayer:po,setPiece:function(e,t,n){ro[e].puzzle.tiles[t]=se.encodePiece(n)},setPuzzleData:function(e,t){ro[e].puzzle.data=t},getTableWidth:Wo,getTableHeight:Ko,getPuzzle:e=>ro[e].puzzle,getRng:e=>ro[e].rng.obj,getPuzzleWidth:e=>ro[e].puzzle.info.width,getPuzzleHeight:e=>ro[e].puzzle.info.height,getPiecesSortedByZIndex:function(e){return ro[e].puzzle.tiles.map(se.decodePiece).sort(((e,t)=>e.z-t.z))},getFirstOwnedPiece:(e,t)=>{const n=Eo(e,t);return n<0?null:ro[e].puzzle.tiles[n]},getPieceDrawOffset:e=>ro[e].puzzle.info.tileDrawOffset,getPieceDrawSize:No,getFinalPiecePos:So,getStartTs:e=>ro[e].puzzle.data.started,getFinishTs:e=>ro[e].puzzle.data.finished,handleInput:function(e,t,n,o,l){const a=ro[e].puzzle,s=function(e,t){return t in ro[e].evtInfos?ro[e].evtInfos[t]:{_last_mouse:null,_last_mouse_down:null}}(e,t),i=[],r=()=>{i.push([an,a.data])},d=t=>{i.push([sn,se.encodePiece(ko(e,t))])},c=e=>{for(const t of e)d(t)},u=()=>{const n=uo(e,t);n&&i.push([rn,se.encodePlayer(n)])},p=n[0];if(p===qt){const l=n[1];bo(e,t,{bgcolor:l,ts:o}),u()}else if(p===Qt){const l=n[1];bo(e,t,{color:l,ts:o}),u()}else if(p===Zt){const l=`${n[1]}`.substr(0,16);bo(e,t,{name:l,ts:o}),u()}else if(p===Ft){const l=n[1],a=n[2],s=uo(e,t);if(s){const n=s.x-l,i=s.y-a;bo(e,t,{ts:o,x:n,y:i}),u()}}else if(p===jt){const l={x:n[1],y:n[2]};bo(e,t,{d:1,ts:o}),u(),s._last_mouse_down=l;const a=((e,t)=>{const n=ro[e].puzzle.info,o=ro[e].puzzle.tiles;let l=-1,a=-1;for(let s=0;sl)&&(l=e.z,a=s)}return a})(e,l);if(a>=0){const n=Vo(e)+1;Co(e,{maxZ:n}),r();const o=Fo(e,a);Uo(e,o,Vo(e)),Lo(e,o,t),c(o)}s._last_mouse=l}else if(p===Kt){const l=n[1],a=n[2],i={x:l,y:a};if(null===s._last_mouse_down)bo(e,t,{x:l,y:a,ts:o}),u();else{const n=Eo(e,t);if(n>=0){bo(e,t,{x:l,y:a,ts:o}),u();const r=Fo(e,n);let d=lo.pointInBounds(i,zo(e))&&lo.pointInBounds(s._last_mouse_down,zo(e));for(const t of r){const n=Io(e,t);if(lo.pointInBounds(i,n)){d=!0;break}}if(d){const t=l-s._last_mouse_down.x,n=a-s._last_mouse_down.y;Ro(e,r,{x:t,y:n}),c(r)}}else bo(e,t,{ts:o}),u();s._last_mouse_down=i}s._last_mouse=i}else if(p===Wt){const i={x:n[1],y:n[2]},p=0;s._last_mouse_down=null;const g=Eo(e,t);if(g>=0){const n=Fo(e,g);Lo(e,n,0),c(n);const s=To(e,g),i=So(e,g);let h=!1;if(fo(e)===ee.REAL){for(const t of n)if(Ao(e,t)){h=!0;break}}else h=!0;if(h&&lo.pointDistance(i,s){const l=ro[e].puzzle.info;if(n<0)return!1;if(((e,t,n)=>{const o=Po(e,t),l=Po(e,n);return!(!o||o!==l)})(e,t,n))return!1;const a=To(e,t),s=lo.pointAdd(To(e,n),{x:o[0]*l.tileSize,y:o[1]*l.tileSize});if(lo.pointDistance(a,s){const o=ro[e].puzzle.tiles,l=Po(e,t),a=Po(e,n);let s;const i=[];l&&i.push(l),a&&i.push(a),l?s=l:a?s=a:(Co(e,{maxGroup:_o(e)+1}),r(),s=_o(e));if(xo(e,t,{group:s}),d(t),xo(e,n,{group:s}),d(n),i.length>0)for(const r of o){const t=se.decodePiece(r);i.includes(t.group)&&(xo(e,t.idx,{group:s}),d(t.idx))}})(e,t,n),l=Fo(e,t),((e,t)=>-1===Go(e,t))(e,n))$o(e,l);else{const t=((e,t)=>{let n=0;for(const o of t){const t=Do(e,o);t>n&&(n=t)}return n})(e,l);Uo(e,l,t)}return c(l),!0}return!1};let a=!1;for(const t of Fo(e,g)){const o=Oo(e,t);if(n(e,t,o[0],[0,1])||n(e,t,o[1],[-1,0])||n(e,t,o[2],[0,-1])||n(e,t,o[3],[1,0])){a=!0;break}}if(a&&yo(e)===Q.ANY){const n=jo(e,t)+1;bo(e,t,{d:p,ts:o,points:n}),u()}else bo(e,t,{d:p,ts:o}),u();a&&fo(e)===ee.REAL&&vo(e)===mo(e)&&(Co(e,{finished:o}),r()),a&&l&&l(t)}}else bo(e,t,{d:p,ts:o}),u();s._last_mouse=i}else if(p===Ht){const l=n[1],a=n[2];bo(e,t,{x:l,y:a,ts:o}),u(),s._last_mouse={x:l,y:a}}else if(p===Yt){const l=n[1],a=n[2];bo(e,t,{x:l,y:a,ts:o}),u(),s._last_mouse={x:l,y:a}}else bo(e,t,{ts:o}),u();return function(e,t,n){ro[e].evtInfos[t]=n}(e,t,s),i}};let Yo=-10,qo=20,Qo=2,Zo=15;class Xo{constructor(e){this.radius=10,this.previousRadius=10,this.explodingDuration=100,this.hasExploded=!1,this.alive=!0,this.color=function(e){return"rgba("+e.random(0,255)+","+e.random(0,255)+","+e.random(0,255)+", 0.8)"}(e),this.px=window.innerWidth/4+Math.random()*window.innerWidth/2,this.py=window.innerHeight,this.vx=Yo+Math.random()*qo,this.vy=-1*(Qo+Math.random()*Zo),this.duration=0}update(e){if(this.hasExploded){const e=200-this.radius;this.previousRadius=this.radius,this.radius+=e/10,this.explodingDuration--,0==this.explodingDuration&&(this.alive=!1)}else this.vx+=0,this.vy+=1,this.vy>=0&&e&&this.explode(e),this.px+=this.vx,this.py+=this.vy}draw(e){e.beginPath(),e.arc(this.px,this.py,this.previousRadius,0,2*Math.PI,!1),this.hasExploded||(e.fillStyle=this.color,e.lineWidth=1,e.fill())}explode(e){this.hasExploded=!0;const t=3+Math.floor(3*Math.random());for(let n=0;n{this.resize()}))}setSpeedParams(){let e=0,t=0;for(;e=0;)t+=1,e+=t;Qo=t/2,Zo=t-Qo;const n=1/4*this.canvas.width/(t/2);Yo=-n,qo=2*n}resize(){this.setSpeedParams()}init(){this.readyBombs=[],this.explodedBombs=[],this.particles=[];for(let e=0;e<1;e++)this.readyBombs.push(new Xo(this.rng))}update(){100*Math.random()<5&&this.readyBombs.push(new Xo(this.rng));const e=[];for(;this.explodedBombs.length>0;){const t=this.explodedBombs.shift();if(!t)break;t.update(),t.alive&&e.push(t)}this.explodedBombs=e;const t=[];for(;this.readyBombs.length>0;){const e=this.readyBombs.shift();if(!e)break;e.update(this.particles),e.hasExploded?this.explodedBombs.push(e):t.push(e)}this.readyBombs=t;const n=[];for(;this.particles.length>0;){const e=this.particles.shift();if(!e)break;e.update(),e.alive&&n.push(e)}this.particles=n}render(){this.ctx.beginPath(),this.ctx.fillStyle="rgba(0, 0, 0, 0.1)",this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height);for(let e=0;e{localStorage.setItem(e,t)},rl=e=>localStorage.getItem(e);var dl=(e,t)=>{il(e,`${t}`)},cl=(e,t)=>{const n=rl(e);if(null===n)return t;const o=parseInt(n,10);return isNaN(o)?t:o},ul=(e,t)=>{il(e,t?"1":"0")},pl=(e,t)=>{const n=rl(e);return null===n?t:"1"===n},gl=(e,t)=>{il(e,t)},hl=(e,t)=>{const n=rl(e);return null===n?t:n};const ml={"./grab.png":Wn,"./grab_mask.png":Kn,"./hand.png":Hn,"./hand_mask.png":Yn},yl={"./click.mp3":jn};let fl=!0,wl=!0;let vl=!0;async function bl(e,t,n,o,l,a){void 0===window.DEBUG&&(window.DEBUG=!1);const s=yl["./click.mp3"].default,i=new Audio(s),r=await Qn.loadImageToBitmap(ml["./grab.png"].default),d=await Qn.loadImageToBitmap(ml["./hand.png"].default),c=await Qn.loadImageToBitmap(ml["./grab_mask.png"].default),u=await Qn.loadImageToBitmap(ml["./hand_mask.png"].default),p=r.width,g=Math.round(p/2),h=r.height,m=Math.round(h/2),y={},f=async e=>{const t=e.color+" "+e.d;if(!y[t]){const n=e.d?r:d;if(e.color){const o=e.d?c:u;y[t]=await createImageBitmap(Qn.colorizedCanvas(n,o,e.color))}else y[t]=n}return y[t]},w=function(e,t){return t.width=window.innerWidth,t.height=window.innerHeight,e.appendChild(t),window.addEventListener("resize",(()=>{t.width=window.innerWidth,t.height=window.innerHeight,vl=!0})),t}(l,Qn.createCanvas()),v={final:!1,log:[],logPointer:0,speeds:[.5,1,2,5,10,20,50,100,250,500],speedIdx:1,paused:!1,lastRealTs:0,lastGameTs:0,gameStartTs:0,skipNonActionPhases:!0,dataOffset:0};bn.onConnectionStateChange((e=>{a.setConnectionState(e)}));const b=async e=>{const t=v.dataOffset;v.dataOffset+=1e4;const n=await bn.requestReplayData(e,t);return v.log=v.log.slice(v.logPointer),v.logPointer=0,v.log.push(...n.log),0===n.log.length&&(v.final=!0),n};let C=()=>0;const x=async()=>{if("play"===o){const o=await bn.connect(n,e,t),l=se.decodeGame(o);Ho.setGame(l.id,l),C=()=>V()}else{if("replay"!==o)throw"[ 2020-12-22 MODE invalid, must be play|replay ]";{const t=await b(e);if(!t.game)throw"[ 2021-05-29 no game received ]";const n=se.decodeGame(t.game);Ho.setGame(n.id,n),v.lastRealTs=V(),v.gameStartTs=parseInt(t.log[0][4],10),v.lastGameTs=v.gameStartTs,C=()=>v.lastGameTs}}vl=!0};await x();const k=Ho.getPieceDrawOffset(e),P=Ho.getPieceDrawSize(e),A=Ho.getPuzzleWidth(e),S=Ho.getPuzzleHeight(e),T=Ho.getTableWidth(e),z=Ho.getTableHeight(e),I={x:(T-A)/2,y:(z-S)/2},D={w:A,h:S},E={w:P,h:P},N=await io.loadPuzzleBitmaps(Ho.getPuzzle(e)),_=new el(w,Ho.getRng(e));_.init();const O=w.getContext("2d");w.classList.add("loaded");const U=function(){let e=0,t=0,n=1;const o=(o,l)=>{e+=o/n,t+=l/n},l=e=>{const t=n+.05*n*("in"===e?1:-1);return Math.min(Math.max(t,.1),6)},a=(e,t)=>{if(n==e)return!1;const l=1-n/e;return o(-t.x*l,-t.y*l),n=e,!0},s=o=>({x:o.x/n-e,y:o.y/n-t}),i=o=>({x:(o.x+e)*n,y:(o.y+t)*n}),r=e=>({w:e.w*n,h:e.h*n}),d=e=>({w:e.w/n,h:e.h/n});return{getCurrentZoom:()=>n,reset:()=>{e=0,t=0,n=1},move:o,canZoom:e=>n!=l(e),zoom:(e,t)=>a(l(e),t),setZoom:a,worldToViewport:e=>{const{x:t,y:n}=i(e);return{x:Math.round(t),y:Math.round(n)}},worldToViewportRaw:i,worldDimToViewport:e=>{const{w:t,h:n}=r(e);return{w:Math.round(t),h:Math.round(n)}},worldDimToViewportRaw:r,viewportToWorld:e=>{const{x:t,y:n}=s(e);return{x:Math.round(t),y:Math.round(n)}},viewportToWorldRaw:s,viewportDimToWorld:e=>{const{w:t,h:n}=d(e);return{w:Math.round(t),h:Math.round(n)}},viewportDimToWorldRaw:d}}(),B=()=>{U.reset(),U.move(-(T-w.width)/2,-(z-w.height)/2);const e=U.worldDimToViewport(D),t=w.width-40,n=w.height-40;if(e.w>t||e.h>n||e.w{const o=n.viewportToWorld({x:e,y:t});return[o.x,o.y]},h=e=>g(e.offsetX,e.offsetY),m=()=>g(e.width/2,e.height/2),y=(e,t)=>{a&&("ShiftLeft"===t.code||"ShiftRight"===t.code?p=e:"ArrowUp"===t.code||"KeyW"===t.code?r=e:"ArrowDown"===t.code||"KeyS"===t.code?d=e:"ArrowLeft"===t.code||"KeyA"===t.code?s=e:"ArrowRight"===t.code||"KeyD"===t.code?i=e:"KeyQ"===t.code?u=e:"KeyE"===t.code&&(c=e))};let f=null;e.addEventListener("mousedown",(e=>{f=h(e),0===e.button&&w([jt,...f])})),e.addEventListener("mouseup",(e=>{f=h(e),0===e.button&&w([Wt,...f])})),e.addEventListener("mousemove",(e=>{f=h(e),w([Kt,...f])})),e.addEventListener("wheel",(e=>{if(f=h(e),n.canZoom(e.deltaY<0?"in":"out")){const t=e.deltaY<0?Ht:Yt;w([t,...f])}})),t.addEventListener("keydown",(e=>y(!0,e))),t.addEventListener("keyup",(e=>y(!1,e))),t.addEventListener("keypress",(e=>{a&&("Space"===e.code&&w([Xt]),"replay"===o&&("KeyI"===e.code&&w([tn]),"KeyO"===e.code&&w([nn]),"KeyP"===e.code&&w([en])),"KeyF"===e.code&&(fl=!fl,vl=!0),"KeyG"===e.code&&(wl=!wl,vl=!0),"KeyM"===e.code&&w([Jt]),"KeyN"===e.code&&w([on]),"KeyC"===e.code&&w([ln]))}));const w=e=>{l.push(e)};return{addEvent:w,consumeAll:()=>{if(0===l.length)return[];const e=l.slice();return l=[],e},createKeyEvents:()=>{const e=(s?1:0)-(i?1:0),t=(r?1:0)-(d?1:0);if(0!==e||0!==t){const o=(p?24:12)*Math.sqrt(n.getCurrentZoom()),l=n.viewportDimToWorld({w:e*o,h:t*o});w([Ft,l.w,l.h]),f&&(f[0]-=l.w,f[1]-=l.h)}if(c&&u);else if(c){if(n.canZoom("in")){const e=f||m();w([Ht,...e])}}else if(u&&n.canZoom("out")){const e=f||m();w([Yt,...e])}},setHotkeys:e=>{a=e}}}(w,window,U,o),G=Ho.getImageUrl(e),$=()=>{const t=Ho.getStartTs(e),n=Ho.getFinishTs(e),o=C();a.setFinished(!!n),a.setDuration((n||o)-t)};$(),a.setPiecesDone(Ho.getFinishedPiecesCount(e)),a.setPiecesTotal(Ho.getPieceCount(e));const L=C();a.setActivePlayers(Ho.getActivePlayers(e,L)),a.setIdlePlayers(Ho.getIdlePlayers(e,L));const F=!!Ho.getFinishTs(e);let j=F;const W=()=>j&&!F,K=()=>cl(tl,100),H=()=>pl(nl,!1),Y=()=>pl(sl,!0),q=()=>{const e=K();i.volume=e/100,i.play()},Q=()=>"replay"===o?hl(ol,"#222222"):Ho.getPlayerBgColor(e,t)||hl(ol,"#222222"),Z=()=>"replay"===o?hl(ll,"#ffffff"):Ho.getPlayerColor(e,t)||hl(ll,"#ffffff");let X="",J="",ee=!1;const te=e=>{ee=e;const[t,n]=e?[X,"grab"]:[J,"default"];w.style.cursor=`url('${t}') ${g} ${m}, ${n}`},ne=e=>{X=Qn.colorizedCanvas(r,c,e).toDataURL(),J=Qn.colorizedCanvas(d,u,e).toDataURL(),te(ee)};ne(Z());const oe=()=>{a.setReplaySpeed&&a.setReplaySpeed(v.speeds[v.speedIdx]),a.setReplayPaused&&a.setReplayPaused(v.paused)},le=()=>{v.speedIdx+1{v.speedIdx>=1&&(v.speedIdx--,oe())},ie=()=>{v.paused=!v.paused,oe()},re=[];let de;let ce;if("play"===o?re.push(setInterval((()=>{$()}),1e3)):"replay"===o&&oe(),"play"===o)bn.onServerChange((n=>{n[0],n[1],n[2];const o=n[3];for(const[l,a]of o)switch(l){case rn:{const n=se.decodePlayer(a);n.id!==t&&(Ho.setPlayer(e,n.id,n),vl=!0)}break;case sn:{const t=se.decodePiece(a);Ho.setPiece(e,t.idx,t),vl=!0}break;case an:Ho.setPuzzleData(e,a),vl=!0}j=!!Ho.getFinishTs(e)}));else if("replay"===o){const t=(t,n)=>{const o=t;if(o[0]===Gt){const t=o[1];return Ho.addPlayer(e,t,n),!0}if(o[0]===$t){const t=Ho.getPlayerIdByIndex(e,o[1]);if(!t)throw"[ 2021-05-17 player not found (update player) ]";return Ho.addPlayer(e,t,n),!0}if(o[0]===Lt){const t=Ho.getPlayerIdByIndex(e,o[1]);if(!t)throw"[ 2021-05-17 player not found (handle input) ]";const l=o[2];return Ho.handleInput(e,t,l,n),!0}return!1};let n=v.lastGameTs;const o=async()=>{v.logPointer+1>=v.log.length&&await b(e);const l=V();if(v.paused)return v.lastRealTs=l,void(de=setTimeout(o,50));const a=(l-v.lastRealTs)*v.speeds[v.speedIdx];let s=v.lastGameTs+a;for(;;){if(v.paused)break;const e=v.logPointer+1;if(e>=v.log.length)break;const o=v.log[v.logPointer],l=n+o[o.length-1],a=v.log[e],i=a[a.length-1],r=l+i;if(r>s){s+500*M{let t=!1;const n=e.fps||60,o=e.slow||1,l=e.update,a=e.render,s=window.requestAnimationFrame,i=1/n,r=o*i;let d,c=0,u=window.performance.now();const p=()=>{for(d=window.performance.now(),c+=Math.min(1,(d-u)/1e3);c>r;)c-=r,l(i);a(c/o),u=d,t||s(p)};return s(p),{stop:()=>{t=!0}}})({update:()=>{R.createKeyEvents();for(const n of R.consumeAll())if("play"===o){const o=n[0];if(o===Ft){const e=n[1],t=n[2],o=U.worldDimToViewport({w:e,h:t});vl=!0,U.move(o.w,o.h)}else if(o===Kt){if(ue&&!Ho.getFirstOwnedPiece(e,t)){const e={x:n[1],y:n[2]},t=U.worldToViewport(e),o=Math.round(t.x-ue.x),l=Math.round(t.y-ue.y);vl=!0,U.move(o,l),ue=t}}else if(o===Qt)ne(n[1]);else if(o===jt){const e={x:n[1],y:n[2]};ue=U.worldToViewport(e),te(!0)}else if(o===Wt)ue=null,te(!1);else if(o===Ht){const e={x:n[1],y:n[2]};vl=!0,U.zoom("in",U.worldToViewport(e))}else if(o===Yt){const e={x:n[1],y:n[2]};vl=!0,U.zoom("out",U.worldToViewport(e))}else o===Xt?a.togglePreview():o===Jt?a.toggleSoundsEnabled():o===on?a.togglePlayerNames():o===ln&&B();const l=C();Ho.handleInput(e,t,n,l,(e=>{H()&&q()})).length>0&&(vl=!0),bn.sendClientEvent(n)}else if("replay"===o){const e=n[0];if(e===en)ie();else if(e===nn)ae();else if(e===tn)le();else if(e===Ft){const e=n[1],t=n[2];vl=!0,U.move(e,t)}else if(e===Kt){if(ue){const e={x:n[1],y:n[2]},t=U.worldToViewport(e),o=Math.round(t.x-ue.x),l=Math.round(t.y-ue.y);vl=!0,U.move(o,l),ue=t}}else if(e===Qt)ne(n[1]);else if(e===jt){const e={x:n[1],y:n[2]};ue=U.worldToViewport(e),te(!0)}else if(e===Wt)ue=null,te(!1);else if(e===Ht){const e={x:n[1],y:n[2]};vl=!0,U.zoom("in",U.worldToViewport(e))}else if(e===Yt){const e={x:n[1],y:n[2]};vl=!0,U.zoom("out",U.worldToViewport(e))}else e===Xt?a.togglePreview():e===Jt?a.toggleSoundsEnabled():e===on?a.togglePlayerNames():e===ln&&B()}j=!!Ho.getFinishTs(e),W()&&(_.update(),vl=!0)},render:async()=>{if(!vl)return;const n=C();let l,s,i;window.DEBUG&&eo(0),O.fillStyle=Q(),O.fillRect(0,0,w.width,w.height),window.DEBUG&&to("clear done"),l=U.worldToViewportRaw(I),s=U.worldDimToViewportRaw(D),O.fillStyle="rgba(255, 255, 255, .3)",O.fillRect(l.x,l.y,s.w,s.h),window.DEBUG&&to("board done");const r=Ho.getPiecesSortedByZIndex(e);window.DEBUG&&to("get tiles done"),s=U.worldDimToViewportRaw(E);for(const e of r)(-1===e.owner?fl:wl)&&(i=N[e.idx],l=U.worldToViewportRaw({x:k+e.pos.x,y:k+e.pos.y}),O.drawImage(i,0,0,i.width,i.height,l.x,l.y,s.w,s.h));window.DEBUG&&to("tiles done");const d=[];for(const a of Ho.getActivePlayers(e,n))c=a,("replay"===o||c.id!==t)&&(i=await f(a),l=U.worldToViewport(a),O.drawImage(i,l.x-g,l.y-m),Y()&&d.push([`${a.name} (${a.points})`,l.x,l.y+h]));var c;O.fillStyle="white",O.textAlign="center";for(const[e,t,o]of d)O.fillText(e,t,o);window.DEBUG&&to("players done"),a.setActivePlayers(Ho.getActivePlayers(e,n)),a.setIdlePlayers(Ho.getIdlePlayers(e,n)),a.setPiecesDone(Ho.getFinishedPiecesCount(e)),window.DEBUG&&to("HUD done"),W()&&_.render(),vl=!1}}),{setHotkeys:e=>{R.setHotkeys(e)},onBgChange:e=>{gl(ol,e),R.addEvent([qt,e])},onColorChange:e=>{gl(ll,e),R.addEvent([Qt,e])},onNameChange:e=>{gl(al,e),R.addEvent([Zt,e])},onSoundsEnabledChange:e=>{ul(nl,e)},onSoundsVolumeChange:e=>{dl(tl,e),q()},onShowPlayerNamesChange:e=>{ul(sl,e)},replayOnSpeedUp:le,replayOnSpeedDown:ae,replayOnPauseToggle:ie,previewImageUrl:G,player:{background:Q(),color:Z(),name:"replay"===o?hl(al,"anon"):Ho.getPlayerName(e,t)||hl(al,"anon"),soundsEnabled:H(),soundsVolume:K(),showPlayerNames:Y()},disconnect:bn.disconnect,connect:x,unload:()=>{re.forEach((e=>{clearInterval(e)})),de&&clearTimeout(de),ce&&ce.stop()}}}var Cl=e({name:"game",components:{PuzzleStatus:xt,Scores:ft,SettingsOverlay:Pt,PreviewOverlay:_t,ConnectionOverlay:Cn,HelpOverlay:Tn},data:()=>({activePlayers:[],idlePlayers:[],finished:!1,duration:0,piecesDone:0,piecesTotal:0,overlay:"",connectionState:0,g:{player:{background:"",color:"",name:"",soundsEnabled:!1,soundsVolume:100,showPlayerNames:!0},previewImageUrl:"",setHotkeys:e=>{},onBgChange:e=>{},onColorChange:e=>{},onNameChange:e=>{},onSoundsEnabledChange:e=>{},onSoundsVolumeChange:e=>{},onShowPlayerNamesChange:e=>{},connect:()=>{},disconnect:()=>{},unload:()=>{}}}),async mounted(){this.$route.params.id&&(this.$watch((()=>this.g.player.background),(e=>{this.g.onBgChange(e)})),this.$watch((()=>this.g.player.color),(e=>{this.g.onColorChange(e)})),this.$watch((()=>this.g.player.name),(e=>{this.g.onNameChange(e)})),this.$watch((()=>this.g.player.soundsEnabled),(e=>{this.g.onSoundsEnabledChange(e)})),this.$watch((()=>this.g.player.soundsVolume),(e=>{this.g.onSoundsVolumeChange(e)})),this.$watch((()=>this.g.player.showPlayerNames),(e=>{this.g.onShowPlayerNamesChange(e)})),this.g=await bl(`${this.$route.params.id}`,this.$clientId,this.$config.WS_ADDRESS,"play",this.$el,{setActivePlayers:e=>{this.activePlayers=e},setIdlePlayers:e=>{this.idlePlayers=e},setFinished:e=>{this.finished=e},setDuration:e=>{this.duration=e},setPiecesDone:e=>{this.piecesDone=e},setPiecesTotal:e=>{this.piecesTotal=e},togglePreview:()=>{this.toggle("preview",!1)},setConnectionState:e=>{this.connectionState=e},toggleSoundsEnabled:()=>{this.g.player.soundsEnabled=!this.g.player.soundsEnabled},togglePlayerNames:()=>{this.g.player.showPlayerNames=!this.g.player.showPlayerNames}}))},unmounted(){this.g.unload(),this.g.disconnect()},methods:{reconnect(){this.g.connect()},toggle(e,t){""===this.overlay?(this.overlay=e,t&&this.g.setHotkeys(!1)):(this.overlay="",t&&this.g.setHotkeys(!0))}}});const xl={id:"game"},kl={class:"menu"},Pl={class:"tabs"},Al=i("🧩 Puzzles");Cl.render=function(e,l,i,r,d,c){const u=a("settings-overlay"),g=a("preview-overlay"),h=a("help-overlay"),m=a("connection-overlay"),y=a("puzzle-status"),f=a("router-link"),w=a("scores");return s(),t("div",xl,[p(n(u,{onBgclick:l[1]||(l[1]=t=>e.toggle("settings",!0)),modelValue:e.g.player,"onUpdate:modelValue":l[2]||(l[2]=t=>e.g.player=t)},null,8,["modelValue"]),[[x,"settings"===e.overlay]]),p(n(g,{onBgclick:l[3]||(l[3]=t=>e.toggle("preview",!1)),img:e.g.previewImageUrl},null,8,["img"]),[[x,"preview"===e.overlay]]),p(n(h,{onBgclick:l[4]||(l[4]=t=>e.toggle("help",!0))},null,512),[[x,"help"===e.overlay]]),n(m,{connectionState:e.connectionState,onReconnect:e.reconnect},null,8,["connectionState","onReconnect"]),n(y,{finished:e.finished,duration:e.duration,piecesDone:e.piecesDone,piecesTotal:e.piecesTotal},null,8,["finished","duration","piecesDone","piecesTotal"]),n("div",kl,[n("div",Pl,[n(f,{class:"opener",to:{name:"index"},target:"_blank"},{default:o((()=>[Al])),_:1}),n("div",{class:"opener",onClick:l[5]||(l[5]=t=>e.toggle("preview",!1))},"πŸ–ΌοΈ Preview"),n("div",{class:"opener",onClick:l[6]||(l[6]=t=>e.toggle("settings",!0))},"πŸ› οΈ Settings"),n("div",{class:"opener",onClick:l[7]||(l[7]=t=>e.toggle("help",!0))},"ℹ️ Hotkeys")])]),n(w,{activePlayers:e.activePlayers,idlePlayers:e.idlePlayers},null,8,["activePlayers","idlePlayers"])])};var Sl=e({name:"replay",components:{PuzzleStatus:xt,Scores:ft,SettingsOverlay:Pt,PreviewOverlay:_t,HelpOverlay:Tn},data:()=>({activePlayers:[],idlePlayers:[],finished:!1,duration:0,piecesDone:0,piecesTotal:0,overlay:"",connectionState:0,g:{player:{background:"",color:"",name:"",soundsEnabled:!1,soundsVolume:100,showPlayerNames:!0},previewImageUrl:"",setHotkeys:e=>{},onBgChange:e=>{},onColorChange:e=>{},onNameChange:e=>{},onSoundsEnabledChange:e=>{},onSoundsVolumeChange:e=>{},onShowPlayerNamesChange:e=>{},replayOnSpeedUp:()=>{},replayOnSpeedDown:()=>{},replayOnPauseToggle:()=>{},connect:()=>{},disconnect:()=>{},unload:()=>{}},replay:{speed:1,paused:!1}}),async mounted(){this.$route.params.id&&(this.$watch((()=>this.g.player.background),(e=>{this.g.onBgChange(e)})),this.$watch((()=>this.g.player.color),(e=>{this.g.onColorChange(e)})),this.$watch((()=>this.g.player.name),(e=>{this.g.onNameChange(e)})),this.$watch((()=>this.g.player.soundsEnabled),(e=>{this.g.onSoundsEnabledChange(e)})),this.$watch((()=>this.g.player.soundsVolume),(e=>{this.g.onSoundsVolumeChange(e)})),this.$watch((()=>this.g.player.showPlayerNames),(e=>{this.g.onShowPlayerNamesChange(e)})),this.g=await bl(`${this.$route.params.id}`,this.$clientId,this.$config.WS_ADDRESS,"replay",this.$el,{setActivePlayers:e=>{this.activePlayers=e},setIdlePlayers:e=>{this.idlePlayers=e},setFinished:e=>{this.finished=e},setDuration:e=>{this.duration=e},setPiecesDone:e=>{this.piecesDone=e},setPiecesTotal:e=>{this.piecesTotal=e},togglePreview:()=>{this.toggle("preview",!1)},setConnectionState:e=>{this.connectionState=e},setReplaySpeed:e=>{this.replay.speed=e},setReplayPaused:e=>{this.replay.paused=e},toggleSoundsEnabled:()=>{this.g.player.soundsEnabled=!this.g.player.soundsEnabled},togglePlayerNames:()=>{this.g.player.showPlayerNames=!this.g.player.showPlayerNames}}))},unmounted(){this.g.unload(),this.g.disconnect()},methods:{toggle(e,t){""===this.overlay?(this.overlay=e,t&&this.g.setHotkeys(!1)):(this.overlay="",t&&this.g.setHotkeys(!0))}},computed:{replayText(){return"Replay-Speed: "+this.replay.speed+"x"+(this.replay.paused?" Paused":"")}}});const Tl={id:"replay"},zl={class:"menu"},Il={class:"tabs"},Dl=i("🧩 Puzzles");Sl.render=function(e,l,i,d,c,u){const g=a("settings-overlay"),h=a("preview-overlay"),m=a("help-overlay"),y=a("puzzle-status"),f=a("router-link"),w=a("scores");return s(),t("div",Tl,[p(n(g,{onBgclick:l[1]||(l[1]=t=>e.toggle("settings",!0)),modelValue:e.g.player,"onUpdate:modelValue":l[2]||(l[2]=t=>e.g.player=t)},null,8,["modelValue"]),[[x,"settings"===e.overlay]]),p(n(h,{onBgclick:l[3]||(l[3]=t=>e.toggle("preview",!1)),img:e.g.previewImageUrl},null,8,["img"]),[[x,"preview"===e.overlay]]),p(n(m,{onBgclick:l[4]||(l[4]=t=>e.toggle("help",!0))},null,512),[[x,"help"===e.overlay]]),n(y,{finished:e.finished,duration:e.duration,piecesDone:e.piecesDone,piecesTotal:e.piecesTotal},{default:o((()=>[n("div",null,[n("div",null,r(e.replayText),1),n("button",{class:"btn",onClick:l[5]||(l[5]=t=>e.g.replayOnSpeedUp())},"⏫"),n("button",{class:"btn",onClick:l[6]||(l[6]=t=>e.g.replayOnSpeedDown())},"⏬"),n("button",{class:"btn",onClick:l[7]||(l[7]=t=>e.g.replayOnPauseToggle())},"⏸️")])])),_:1},8,["finished","duration","piecesDone","piecesTotal"]),n("div",zl,[n("div",Il,[n(f,{class:"opener",to:{name:"index"},target:"_blank"},{default:o((()=>[Dl])),_:1}),n("div",{class:"opener",onClick:l[8]||(l[8]=t=>e.toggle("preview",!1))},"πŸ–ΌοΈ Preview"),n("div",{class:"opener",onClick:l[9]||(l[9]=t=>e.toggle("settings",!0))},"πŸ› οΈ Settings"),n("div",{class:"opener",onClick:l[10]||(l[10]=t=>e.toggle("help",!0))},"ℹ️ Hotkeys")])]),n(w,{activePlayers:e.activePlayers,idlePlayers:e.idlePlayers},null,8,["activePlayers","idlePlayers"])])},(async()=>{const e=await fetch("/api/conf"),t=await e.json();const n=k({history:P(),routes:[{name:"index",path:"/",component:j},{name:"new-game",path:"/new-game",component:it},{name:"game",path:"/g/:id",component:Cl},{name:"replay",path:"/replay/:id",component:Sl}]});n.beforeEach(((e,t)=>{t.name&&document.documentElement.classList.remove(`view-${String(t.name)}`),document.documentElement.classList.add(`view-${String(e.name)}`)}));const o=A(S);o.config.globalProperties.$config=t,o.config.globalProperties.$clientId=function(){let e=localStorage.getItem("ID");return e||(e=se.uniqId(),localStorage.setItem("ID",e)),e}(),o.use(n),o.mount("#app")})(); diff --git a/build/public/assets/index.cc6b4801.js b/build/public/assets/index.cc6b4801.js new file mode 100644 index 0000000..7e3c4be --- /dev/null +++ b/build/public/assets/index.cc6b4801.js @@ -0,0 +1 @@ +import{d as e,c as t,a as n,w as o,b as l,r as a,o as s,e as i,t as r,F as d,f as c,g as u,h as p,v as g,i as h,j as m,p as y,k as f,l as w,m as v,n as b,q as C,s as x,u as k,x as P,y as A}from"./vendor.684f7bc8.js";var S=e({name:"app",computed:{showNav(){return!["game","replay"].includes(String(this.$route.name))}}});const T={id:"app"},z={key:0,class:"nav"},I=i("Games overview"),D=i("New game");S.render=function(e,i,r,d,c,u){const p=a("router-link"),g=a("router-view");return s(),t("div",T,[e.showNav?(s(),t("ul",z,[n("li",null,[n(p,{class:"btn",to:{name:"index"}},{default:o((()=>[I])),_:1})]),n("li",null,[n(p,{class:"btn",to:{name:"new-game"}},{default:o((()=>[D])),_:1})])])):l("",!0),n(g)])};const E=864e5,M=e=>{const t=Math.floor(e/E);e%=E;const n=Math.floor(e/36e5);e%=36e5;const o=Math.floor(e/6e4);e%=6e4;return`${t}d ${n}h ${o}m ${Math.floor(e/1e3)}s`};var N=1,_=1e3,V=()=>{const e=new Date;return Date.UTC(e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate(),e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds(),e.getUTCMilliseconds())},O=(e,t)=>M(t-e),U=M,B=e({name:"game-teaser",props:{game:{type:Object,required:!0}},computed:{style(){return{"background-image":`url("${this.game.imageUrl.replace("uploads/","uploads/r/")+"-375x210.webp"}")`}}},methods:{time(e,t){const n=t?"🏁":"⏳",o=e,l=t||V();return`${n} ${O(o,l)}`}}});const R={class:"game-info-text"},G=n("br",null,null,-1),$=n("br",null,null,-1),L=n("br",null,null,-1),F=i(" β†ͺ️ Watch replay ");B.render=function(e,d,c,u,p,g){const h=a("router-link");return s(),t("div",{class:"game-teaser",style:e.style},[n(h,{class:"game-info",to:{name:"game",params:{id:e.game.id}}},{default:o((()=>[n("span",R,[i(" 🧩 "+r(e.game.tilesFinished)+"/"+r(e.game.tilesTotal),1),G,i(" πŸ‘₯ "+r(e.game.players),1),$,i(" "+r(e.time(e.game.started,e.game.finished)),1),L])])),_:1},8,["to"]),e.game.hasReplay?(s(),t(h,{key:0,class:"game-replay",to:{name:"replay",params:{id:e.game.id}}},{default:o((()=>[F])),_:1},8,["to"])):l("",!0)],4)};var j=e({components:{GameTeaser:B},data:()=>({gamesRunning:[],gamesFinished:[]}),async created(){const e=await fetch("/api/index-data"),t=await e.json();this.gamesRunning=t.gamesRunning,this.gamesFinished=t.gamesFinished}});const W=n("h1",null,"Running games",-1),K=n("h1",null,"Finished games",-1);j.render=function(e,o,l,i,r,u){const p=a("game-teaser");return s(),t("div",null,[W,(s(!0),t(d,null,c(e.gamesRunning,((e,o)=>(s(),t("div",{class:"game-teaser-wrap",key:o},[n(p,{game:e},null,8,["game"])])))),128)),K,(s(!0),t(d,null,c(e.gamesFinished,((e,o)=>(s(),t("div",{class:"game-teaser-wrap",key:o},[n(p,{game:e},null,8,["game"])])))),128))])};var H=e({name:"image-teaser",props:{image:{type:Object,required:!0}},computed:{style(){return{backgroundImage:`url("${this.image.url.replace("uploads/","uploads/r/")+"-150x100.webp"}")`}}},emits:{click:null,editClick:null},methods:{onClick(){this.$emit("click")},onEditClick(){this.$emit("editClick")}}});H.render=function(e,o,l,a,i,r){return s(),t("div",{class:"imageteaser",style:e.style,onClick:o[2]||(o[2]=(...t)=>e.onClick&&e.onClick(...t))},[n("div",{class:"btn edit",onClick:o[1]||(o[1]=u(((...t)=>e.onEditClick&&e.onEditClick(...t)),["stop"]))},"✏️")],4)};var Y=e({name:"image-library",components:{ImageTeaser:H},props:{images:{type:Array,required:!0}},emits:{imageClicked:null,imageEditClicked:null},methods:{imageClicked(e){this.$emit("imageClicked",e)},imageEditClicked(e){this.$emit("imageEditClicked",e)}}});Y.render=function(e,n,o,l,i,r){const u=a("image-teaser");return s(),t("div",null,[(s(!0),t(d,null,c(e.images,((n,o)=>(s(),t(u,{image:n,onClick:t=>e.imageClicked(n),onEditClick:t=>e.imageEditClicked(n),key:o},null,8,["image","onClick","onEditClick"])))),128))])};class q{constructor(e){this.rand_high=e||3735929054,this.rand_low=1231121986^e}random(e,t){this.rand_high=(this.rand_high<<16)+(this.rand_high>>16)+this.rand_low&4294967295,this.rand_low=this.rand_low+this.rand_high&4294967295;return e+(this.rand_high>>>0)/4294967295*(t-e+1)|0}choice(e){return e[this.random(0,e.length-1)]}shuffle(e){const t=e.slice();for(let n=0;n<=t.length-2;n++){const e=this.random(n,t.length-1),o=t[n];t[n]=t[e],t[e]=o}return t}static serialize(e){return{rand_high:e.rand_high,rand_low:e.rand_low}}static unserialize(e){const t=new q(0);return t.rand_high=e.rand_high,t.rand_low=e.rand_low,t}}const Q=(e,t)=>{const n=`${e}`;return n.length>=t.length?n:t.substr(0,t.length-n.length)+n},Z=(...e)=>{const t=t=>(...n)=>{const o=new Date,l=Q(o.getHours(),"00"),a=Q(o.getMinutes(),"00"),s=Q(o.getSeconds(),"00");console[t](`${l}:${a}:${s}`,...e,...n)};return{log:t("log"),error:t("error"),info:t("info")}};var X={hash:e=>{let t=0;for(let n=0;n{let t=e.toLowerCase();return t=t.replace(/[^a-z0-9]+/g,"-"),t=t.replace(/^-|-$/,""),t},uniqId:()=>Date.now().toString(36)+Math.random().toString(36).substring(2),encodeShape:function(e){return e.top+1<<0|e.right+1<<2|e.bottom+1<<4|e.left+1<<6},decodeShape:function(e){return{top:(e>>0&3)-1,right:(e>>2&3)-1,bottom:(e>>4&3)-1,left:(e>>6&3)-1}},encodePiece:function(e){return[e.idx,e.pos.x,e.pos.y,e.z,e.owner,e.group]},decodePiece:function(e){return{idx:e[0],pos:{x:e[1],y:e[2]},z:e[3],owner:e[4],group:e[5]}},encodePlayer:function(e){return[e.id,e.x,e.y,e.d,e.name,e.color,e.bgcolor,e.points,e.ts]},decodePlayer:function(e){return{id:e[0],x:e[1],y:e[2],d:e[3],name:e[4],color:e[5],bgcolor:e[6],points:e[7],ts:e[8]}},encodeGame:function(e){return[e.id,e.rng.type||"",q.serialize(e.rng.obj),e.puzzle,e.players,e.evtInfos,e.scoreMode,e.shapeMode,e.snapMode]},decodeGame:function(e){return{id:e[0],rng:{type:e[1],obj:q.unserialize(e[2])},puzzle:e[3],players:e[4],evtInfos:e[5],scoreMode:e[6],shapeMode:e[7],snapMode:e[8]}},coordByPieceIdx:function(e,t){const n=e.width/e.tileSize;return{x:t%n,y:Math.floor(t/n)}},asQueryArgs:function(e){const t=[];for(const n in e){const o=[n,e[n]].map(encodeURIComponent);t.push(o.join("="))}return 0===t.length?"":`?${t.join("&")}`}};const J={name:"responsive-image",props:{src:String,title:{type:String,default:""},height:{type:String,default:"100%"},width:{type:String,default:"100%"}},computed:{style(){return{display:"inline-block",verticalAlign:"text-bottom",backgroundImage:`url('${this.src}')`,backgroundRepeat:"no-repeat",backgroundSize:"contain",backgroundPosition:"center",width:this.width,height:this.height}}}};J.render=function(e,n,o,l,a,i){return s(),t("div",{style:i.style,title:o.title},null,12,["title"])};var ee=e({name:"tags-input",props:{modelValue:{type:Array,required:!0},autocompleteTags:{type:Function}},emits:{"update:modelValue":null},data:()=>({input:"",values:[],autocomplete:{idx:-1,values:[]}}),created(){this.values=this.modelValue},methods:{onKeyUp(e){return"ArrowDown"===e.code&&this.autocomplete.values.length>0?(this.autocomplete.idx0?(this.autocomplete.idx>0&&this.autocomplete.idx--,e.stopPropagation(),!1):","===e.key?(this.add(),e.stopPropagation(),!1):void(this.input&&this.autocompleteTags?(this.autocomplete.values=this.autocompleteTags(this.input,this.values),this.autocomplete.idx=-1):(this.autocomplete.values=[],this.autocomplete.idx=-1))},addVal(e){const t=e.replace(/,/g,"").trim();t&&(this.values.includes(t)||this.values.push(t),this.input="",this.autocomplete.values=[],this.autocomplete.idx=-1,this.$emit("update:modelValue",this.values),this.$refs.input.focus())},add(){const e=this.autocomplete.idx>=0?this.autocomplete.values[this.autocomplete.idx]:this.input;this.addVal(e)},rm(e){this.values=this.values.filter((t=>t!==e)),this.$emit("update:modelValue",this.values)}}});const te=m();y("data-v-a4fa5e7e");const ne={key:0,class:"autocomplete"};f();const oe=te(((e,o,a,i,u,m)=>(s(),t("div",null,[p(n("input",{ref:"input",class:"input",type:"text","onUpdate:modelValue":o[1]||(o[1]=t=>e.input=t),placeholder:"Plants, People",onChange:o[2]||(o[2]=(...t)=>e.onChange&&e.onChange(...t)),onKeydown:o[3]||(o[3]=h(((...t)=>e.add&&e.add(...t)),["enter"])),onKeyup:o[4]||(o[4]=(...t)=>e.onKeyUp&&e.onKeyUp(...t))},null,544),[[g,e.input]]),e.autocomplete.values?(s(),t("div",ne,[n("ul",null,[(s(!0),t(d,null,c(e.autocomplete.values,((n,o)=>(s(),t("li",{key:o,class:{active:o===e.autocomplete.idx},onClick:t=>e.addVal(n)},r(n),11,["onClick"])))),128))])])):l("",!0),(s(!0),t(d,null,c(e.values,((n,o)=>(s(),t("span",{key:o,class:"bit",onClick:t=>e.rm(n)},r(n)+" βœ–",9,["onClick"])))),128))]))));ee.render=oe,ee.__scopeId="data-v-a4fa5e7e";const le=Z("NewImageDialog.vue");var ae=e({name:"new-image-dialog",components:{ResponsiveImage:J,TagsInput:ee},props:{autocompleteTags:{type:Function},uploadProgress:{type:Number},uploading:{type:String}},emits:{bgclick:null,setupGameClick:null,postToGalleryClick:null},data:()=>({previewUrl:"",file:null,title:"",tags:[],droppable:!1}),computed:{uploadProgressPercent(){return this.uploadProgress?Math.round(100*this.uploadProgress):0},canPostToGallery(){return!this.uploading&&!(!this.previewUrl||!this.file)},canSetupGameClick(){return!this.uploading&&!(!this.previewUrl||!this.file)}},methods:{imageFromDragEvt(e){var t;const n=null==(t=e.dataTransfer)?void 0:t.items;if(!n||0===n.length)return null;const o=n[0];return o.type.startsWith("image/")?o:null},onFileSelect(e){const t=e.target;if(!t.files)return;const n=t.files[0];n&&this.preview(n)},preview(e){const t=new FileReader;t.readAsDataURL(e),t.onload=t=>{this.previewUrl=t.target.result,this.file=e}},postToGallery(){this.$emit("postToGalleryClick",{file:this.file,title:this.title,tags:this.tags})},setupGameClick(){this.$emit("setupGameClick",{file:this.file,title:this.title,tags:this.tags})},onDrop(e){this.droppable=!1;const t=this.imageFromDragEvt(e);if(!t)return!1;const n=t.getAsFile();return!!n&&(this.file=n,this.preview(n),e.preventDefault(),!1)},onDragover(e){return!!this.imageFromDragEvt(e)&&(this.droppable=!0,e.preventDefault(),!1)},onDragleave(){le.info("onDragleave"),this.droppable=!1}}});const se=n("div",{class:"drop-target"},null,-1),ie={key:0,class:"has-image"},re={key:1},de={class:"upload"},ce=n("span",{class:"btn"},"Upload File",-1),ue={class:"area-settings"},pe=n("td",null,[n("label",null,"Title")],-1),ge=n("tr",null,[n("td",{colspan:"2"},[n("div",{class:"hint"},"Feel free to leave a credit to the artist/photographer in the title :)")])],-1),he=n("td",null,[n("label",null,"Tags")],-1),me={class:"area-buttons"},ye=i("πŸ–ΌοΈ Post to gallery"),fe=i("🧩 Post to gallery "),we=n("br",null,null,-1),ve=i(" + set up game");ae.render=function(e,o,l,c,h,m){const y=a("responsive-image"),f=a("tags-input");return s(),t("div",{class:"overlay new-image-dialog",onClick:o[11]||(o[11]=t=>e.$emit("bgclick"))},[n("div",{class:"overlay-content",onClick:o[10]||(o[10]=u((()=>{}),["stop"]))},[n("div",{class:["area-image",{"has-image":!!e.previewUrl,"no-image":!e.previewUrl,droppable:e.droppable}],onDrop:o[3]||(o[3]=(...t)=>e.onDrop&&e.onDrop(...t)),onDragover:o[4]||(o[4]=(...t)=>e.onDragover&&e.onDragover(...t)),onDragleave:o[5]||(o[5]=(...t)=>e.onDragleave&&e.onDragleave(...t))},[se,e.previewUrl?(s(),t("div",ie,[n("span",{class:"remove btn",onClick:o[1]||(o[1]=t=>e.previewUrl="")},"X"),n(y,{src:e.previewUrl},null,8,["src"])])):(s(),t("div",re,[n("label",de,[n("input",{type:"file",style:{display:"none"},onChange:o[2]||(o[2]=(...t)=>e.onFileSelect&&e.onFileSelect(...t)),accept:"image/*"},null,32),ce])]))],34),n("div",ue,[n("table",null,[n("tr",null,[pe,n("td",null,[p(n("input",{type:"text","onUpdate:modelValue":o[6]||(o[6]=t=>e.title=t),placeholder:"Flower by @artist"},null,512),[[g,e.title]])])]),ge,n("tr",null,[he,n("td",null,[n(f,{modelValue:e.tags,"onUpdate:modelValue":o[7]||(o[7]=t=>e.tags=t),autocompleteTags:e.autocompleteTags},null,8,["modelValue","autocompleteTags"])])])])]),n("div",me,[n("button",{class:"btn",disabled:!e.canPostToGallery,onClick:o[8]||(o[8]=(...t)=>e.postToGallery&&e.postToGallery(...t))},["postToGallery"===e.uploading?(s(),t(d,{key:0},[i("Uploading ("+r(e.uploadProgressPercent)+"%)",1)],64)):(s(),t(d,{key:1},[ye],64))],8,["disabled"]),n("button",{class:"btn",disabled:!e.canSetupGameClick,onClick:o[9]||(o[9]=(...t)=>e.setupGameClick&&e.setupGameClick(...t))},["setupGame"===e.uploading?(s(),t(d,{key:0},[i("Uploading ("+r(e.uploadProgressPercent)+"%)",1)],64)):(s(),t(d,{key:1},[fe,we,ve],64))],8,["disabled"])])])])};var be=e({name:"edit-image-dialog",components:{ResponsiveImage:J,TagsInput:ee},props:{image:{type:Object,required:!0},autocompleteTags:{type:Function}},emits:{bgclick:null,saveClick:null},data:()=>({title:"",tags:[]}),created(){this.title=this.image.title,this.tags=this.image.tags.map((e=>e.title))},methods:{saveImage(){this.$emit("saveClick",{id:this.image.id,title:this.title,tags:this.tags})}}});const Ce={class:"area-image"},xe={class:"has-image"},ke={class:"area-settings"},Pe=n("td",null,[n("label",null,"Title")],-1),Ae=n("tr",null,[n("td",{colspan:"2"},[n("div",{class:"hint"},"Feel free to leave a credit to the artist/photographer in the title :)")])],-1),Se=n("td",null,[n("label",null,"Tags")],-1),Te={class:"area-buttons"};var ze,Ie,De,Ee,Me,Ne,_e,Ve;be.render=function(e,o,l,i,r,d){const c=a("responsive-image"),h=a("tags-input");return s(),t("div",{class:"overlay edit-image-dialog",onClick:o[5]||(o[5]=t=>e.$emit("bgclick"))},[n("div",{class:"overlay-content",onClick:o[4]||(o[4]=u((()=>{}),["stop"]))},[n("div",Ce,[n("div",xe,[n(c,{src:e.image.url,title:e.image.title},null,8,["src","title"])])]),n("div",ke,[n("table",null,[n("tr",null,[Pe,n("td",null,[p(n("input",{type:"text","onUpdate:modelValue":o[1]||(o[1]=t=>e.title=t),placeholder:"Flower by @artist"},null,512),[[g,e.title]])])]),Ae,n("tr",null,[Se,n("td",null,[n(h,{modelValue:e.tags,"onUpdate:modelValue":o[2]||(o[2]=t=>e.tags=t),autocompleteTags:e.autocompleteTags},null,8,["modelValue","autocompleteTags"])])])])]),n("div",Te,[n("button",{class:"btn",onClick:o[3]||(o[3]=(...t)=>e.saveImage&&e.saveImage(...t))},"πŸ–ΌοΈ Save image")])])])},(Ie=ze||(ze={}))[Ie.Flat=0]="Flat",Ie[Ie.Out=1]="Out",Ie[Ie.In=-1]="In",(Ee=De||(De={}))[Ee.FINAL=0]="FINAL",Ee[Ee.ANY=1]="ANY",(Ne=Me||(Me={}))[Ne.NORMAL=0]="NORMAL",Ne[Ne.ANY=1]="ANY",Ne[Ne.FLAT=2]="FLAT",(Ve=_e||(_e={}))[Ve.NORMAL=0]="NORMAL",Ve[Ve.REAL=1]="REAL";var Oe=e({name:"new-game-dialog",components:{ResponsiveImage:J},props:{image:{type:Object,required:!0}},emits:{newGame:null,bgclick:null},data:()=>({tiles:1e3,scoreMode:De.ANY,shapeMode:Me.NORMAL,snapMode:_e.NORMAL}),methods:{onNewGameClick(){this.$emit("newGame",{tiles:this.tilesInt,image:this.image,scoreMode:this.scoreModeInt,shapeMode:this.shapeModeInt,snapMode:this.snapModeInt})}},computed:{canStartNewGame(){return!!(this.tilesInt&&this.image&&this.image.url&&[0,1].includes(this.scoreModeInt))},scoreModeInt(){return parseInt(`${this.scoreMode}`,10)},shapeModeInt(){return parseInt(`${this.shapeMode}`,10)},snapModeInt(){return parseInt(`${this.snapMode}`,10)},tilesInt(){return parseInt(`${this.tiles}`,10)}}});const Ue={class:"area-image"},Be={class:"has-image"},Re={key:0,class:"image-title"},Ge={key:0,class:"image-title-title"},$e={key:1,class:"image-title-dim"},Le={class:"area-settings"},Fe=n("td",null,[n("label",null,"Pieces")],-1),je=n("td",null,[n("label",null,"Scoring: ")],-1),We=i(" Any (Score when pieces are connected to each other or on final location)"),Ke=n("br",null,null,-1),He=i(" Final (Score when pieces are put to their final location)"),Ye=n("td",null,[n("label",null,"Shapes: ")],-1),qe=i(" Normal"),Qe=n("br",null,null,-1),Ze=i(" Any (flat pieces can occur anywhere)"),Xe=n("br",null,null,-1),Je=i(" Flat (all pieces flat on all sides)"),et=n("td",null,[n("label",null,"Snapping: ")],-1),tt=i(" Normal (pieces snap to final destination and to each other)"),nt=n("br",null,null,-1),ot=i(" Real (pieces snap only to corners, already snapped pieces and to each other)"),lt={class:"area-buttons"};Oe.render=function(e,o,i,d,c,h){const m=a("responsive-image");return s(),t("div",{class:"overlay new-game-dialog",onClick:o[11]||(o[11]=t=>e.$emit("bgclick"))},[n("div",{class:"overlay-content",onClick:o[10]||(o[10]=u((()=>{}),["stop"]))},[n("div",Ue,[n("div",Be,[n(m,{src:e.image.url,title:e.image.title},null,8,["src","title"])]),e.image.title||e.image.width||e.image.height?(s(),t("div",Re,[e.image.title?(s(),t("span",Ge,'"'+r(e.image.title)+'"',1)):l("",!0),e.image.width||e.image.height?(s(),t("span",$e,"("+r(e.image.width)+" βœ• "+r(e.image.height)+")",1)):l("",!0)])):l("",!0)]),n("div",Le,[n("table",null,[n("tr",null,[Fe,n("td",null,[p(n("input",{type:"text","onUpdate:modelValue":o[1]||(o[1]=t=>e.tiles=t)},null,512),[[g,e.tiles]])])]),n("tr",null,[je,n("td",null,[n("label",null,[p(n("input",{type:"radio","onUpdate:modelValue":o[2]||(o[2]=t=>e.scoreMode=t),value:"1"},null,512),[[w,e.scoreMode]]),We]),Ke,n("label",null,[p(n("input",{type:"radio","onUpdate:modelValue":o[3]||(o[3]=t=>e.scoreMode=t),value:"0"},null,512),[[w,e.scoreMode]]),He])])]),n("tr",null,[Ye,n("td",null,[n("label",null,[p(n("input",{type:"radio","onUpdate:modelValue":o[4]||(o[4]=t=>e.shapeMode=t),value:"0"},null,512),[[w,e.shapeMode]]),qe]),Qe,n("label",null,[p(n("input",{type:"radio","onUpdate:modelValue":o[5]||(o[5]=t=>e.shapeMode=t),value:"1"},null,512),[[w,e.shapeMode]]),Ze]),Xe,n("label",null,[p(n("input",{type:"radio","onUpdate:modelValue":o[6]||(o[6]=t=>e.shapeMode=t),value:"2"},null,512),[[w,e.shapeMode]]),Je])])]),n("tr",null,[et,n("td",null,[n("label",null,[p(n("input",{type:"radio","onUpdate:modelValue":o[7]||(o[7]=t=>e.snapMode=t),value:"0"},null,512),[[w,e.snapMode]]),tt]),nt,n("label",null,[p(n("input",{type:"radio","onUpdate:modelValue":o[8]||(o[8]=t=>e.snapMode=t),value:"1"},null,512),[[w,e.snapMode]]),ot])])])])]),n("div",lt,[n("button",{class:"btn",disabled:!e.canStartNewGame,onClick:o[9]||(o[9]=(...t)=>e.onNewGameClick&&e.onNewGameClick(...t))}," 🧩 Generate Puzzle ",8,["disabled"])])])])};const at=async(e,t,n)=>new Promise(((o,l)=>{const a=new window.XMLHttpRequest;a.open(e,t,!0),a.withCredentials=!0;for(const e in n.headers||{})a.setRequestHeader(e,n.headers[e]);a.addEventListener("load",(function(e){o({status:this.status,text:this.responseText,json:async()=>JSON.parse(this.responseText)})})),a.addEventListener("error",(function(e){l(new Error("xhr error"))})),a.upload&&n.onUploadProgress&&a.upload.addEventListener("progress",(function(e){n.onUploadProgress&&n.onUploadProgress(e)})),a.send(n.body)}));var st=(e,t)=>at("post",e,t),it=e({components:{ImageLibrary:Y,NewImageDialog:ae,EditImageDialog:be,NewGameDialog:Oe},data:()=>({filters:{sort:"date_desc",tags:[]},images:[],tags:[],image:{id:0,filename:"",file:"",url:"",title:"",tags:[],created:0},dialog:"",uploading:"",uploadProgress:0}),async created(){await this.loadImages()},computed:{relevantTags(){return this.tags.filter((e=>e.total>0))}},methods:{autocompleteTags(e,t){return this.tags.filter((n=>!t.includes(n.title)&&n.title.toLowerCase().startsWith(e.toLowerCase()))).slice(0,10).map((e=>e.title))},toggleTag(e){this.filters.tags.includes(e.slug)?this.filters.tags=this.filters.tags.filter((t=>t!==e.slug)):this.filters.tags.push(e.slug),this.filtersChanged()},async loadImages(){const e=await fetch(`/api/newgame-data${X.asQueryArgs(this.filters)}`),t=await e.json();this.images=t.images,this.tags=t.tags},async filtersChanged(){await this.loadImages()},onImageClicked(e){this.image=e,this.dialog="new-game"},onImageEditClicked(e){this.image=e,this.dialog="edit-image"},async uploadImage(e){this.uploadProgress=0;const t=new FormData;t.append("file",e.file,e.file.name),t.append("title",e.title),t.append("tags",e.tags);const n=await st("/api/upload",{body:t,onUploadProgress:e=>{this.uploadProgress=e.loaded/e.total}});return this.uploadProgress=1,await n.json()},async saveImage(e){const t=await fetch("/api/save-image",{method:"post",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify({id:e.id,title:e.title,tags:e.tags})});return await t.json()},async onSaveImageClick(e){await this.saveImage(e),this.dialog="",await this.loadImages()},async postToGalleryClick(e){this.uploading="postToGallery",await this.uploadImage(e),this.uploading="",this.dialog="",await this.loadImages()},async setupGameClick(e){this.uploading="setupGame";const t=await this.uploadImage(e);this.uploading="",this.loadImages(),this.image=t,this.dialog="new-game"},async onNewGame(e){const t=await fetch("/api/newgame",{method:"post",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(e)});if(200===t.status){const e=await t.json();this.$router.push({name:"game",params:{id:e.id}})}}}});const rt={class:"upload-image-teaser"},dt=n("div",{class:"hint"},"(The image you upload will be added to the public gallery.)",-1),ct={key:0},ut=i(" Tags: "),pt=i(" Sort by: "),gt=n("option",{value:"date_desc"},"Newest first",-1),ht=n("option",{value:"date_asc"},"Oldest first",-1),mt=n("option",{value:"alpha_asc"},"A-Z",-1),yt=n("option",{value:"alpha_desc"},"Z-A",-1);it.render=function(e,o,i,u,g,h){const m=a("image-library"),y=a("new-image-dialog"),f=a("edit-image-dialog"),w=a("new-game-dialog");return s(),t("div",null,[n("div",rt,[n("div",{class:"btn btn-big",onClick:o[1]||(o[1]=t=>e.dialog="new-image")},"Upload your image"),dt]),n("div",null,[e.tags.length>0?(s(),t("label",ct,[ut,(s(!0),t(d,null,c(e.relevantTags,((n,o)=>(s(),t("span",{class:["bit",{on:e.filters.tags.includes(n.slug)}],key:o,onClick:t=>e.toggleTag(n)},r(n.title)+" ("+r(n.total)+")",11,["onClick"])))),128))])):l("",!0),n("label",null,[pt,p(n("select",{"onUpdate:modelValue":o[2]||(o[2]=t=>e.filters.sort=t),onChange:o[3]||(o[3]=(...t)=>e.filtersChanged&&e.filtersChanged(...t))},[gt,ht,mt,yt],544),[[v,e.filters.sort]])])]),n(m,{images:e.images,onImageClicked:e.onImageClicked,onImageEditClicked:e.onImageEditClicked},null,8,["images","onImageClicked","onImageEditClicked"]),"new-image"===e.dialog?(s(),t(y,{key:0,autocompleteTags:e.autocompleteTags,onBgclick:o[4]||(o[4]=t=>e.dialog=""),uploadProgress:e.uploadProgress,uploading:e.uploading,onPostToGalleryClick:e.postToGalleryClick,onSetupGameClick:e.setupGameClick},null,8,["autocompleteTags","uploadProgress","uploading","onPostToGalleryClick","onSetupGameClick"])):l("",!0),"edit-image"===e.dialog?(s(),t(f,{key:1,autocompleteTags:e.autocompleteTags,onBgclick:o[5]||(o[5]=t=>e.dialog=""),onSaveClick:e.onSaveImageClick,image:e.image},null,8,["autocompleteTags","onSaveClick","image"])):l("",!0),e.image&&"new-game"===e.dialog?(s(),t(w,{key:2,onBgclick:o[6]||(o[6]=t=>e.dialog=""),onNewGame:e.onNewGame,image:e.image},null,8,["onNewGame","image"])):l("",!0)])};var ft=e({name:"scores",props:{activePlayers:{type:Array,required:!0},idlePlayers:{type:Array,required:!0}},computed:{actives(){return this.activePlayers.sort(((e,t)=>t.points-e.points)),this.activePlayers},idles(){return this.idlePlayers.sort(((e,t)=>t.points-e.points)),this.idlePlayers}}});const wt={class:"scores"},vt=n("div",null,"Scores",-1),bt=n("td",null,"⚑",-1),Ct=n("td",null,"πŸ’€",-1);ft.render=function(e,o,l,a,i,u){return s(),t("div",wt,[vt,n("table",null,[(s(!0),t(d,null,c(e.actives,((e,o)=>(s(),t("tr",{key:o,style:{color:e.color}},[bt,n("td",null,r(e.name),1),n("td",null,r(e.points),1)],4)))),128)),(s(!0),t(d,null,c(e.idles,((e,o)=>(s(),t("tr",{key:o,style:{color:e.color}},[Ct,n("td",null,r(e.name),1),n("td",null,r(e.points),1)],4)))),128))])])};var xt=e({name:"puzzle-status",props:{finished:{type:Boolean,required:!0},duration:{type:Number,required:!0},piecesDone:{type:Number,required:!0},piecesTotal:{type:Number,required:!0}},computed:{icon(){return this.finished?"🏁":"⏳"},durationStr(){return U(this.duration)}}});const kt={class:"timer"};xt.render=function(e,o,l,a,i,d){return s(),t("div",kt,[n("div",null," 🧩 "+r(e.piecesDone)+"/"+r(e.piecesTotal),1),n("div",null,r(e.icon)+" "+r(e.durationStr),1),b(e.$slots,"default")])};var Pt=e({name:"settings-overlay",emits:{bgclick:null,"update:modelValue":null},props:{modelValue:{type:Object,required:!0}},methods:{updateVolume(e){this.modelValue.soundsVolume=e.target.value},decreaseVolume(){const e=parseInt(this.modelValue.soundsVolume,10)-5;this.modelValue.soundsVolume=Math.max(0,e)},increaseVolume(){const e=parseInt(this.modelValue.soundsVolume,10)+5;this.modelValue.soundsVolume=Math.min(100,e)}},created(){this.$watch("modelValue",(e=>{this.$emit("update:modelValue",e)}),{deep:!0})}});const At=m();y("data-v-4d56fc17");const St=n("td",null,[n("label",null,"Background: ")],-1),Tt=n("td",null,[n("label",null,"Color: ")],-1),zt=n("td",null,[n("label",null,"Name: ")],-1),It=n("td",null,[n("label",null,"Sounds: ")],-1),Dt=n("td",null,[n("label",null,"Sounds Volume: ")],-1),Et={class:"sound-volume"},Mt=n("td",null,[n("label",null,"Show player names: ")],-1);f();const Nt=At(((e,o,l,a,i,r)=>(s(),t("div",{class:"overlay transparent",onClick:o[10]||(o[10]=t=>e.$emit("bgclick"))},[n("table",{class:"overlay-content settings",onClick:o[9]||(o[9]=u((()=>{}),["stop"]))},[n("tr",null,[St,n("td",null,[p(n("input",{type:"color","onUpdate:modelValue":o[1]||(o[1]=t=>e.modelValue.background=t)},null,512),[[g,e.modelValue.background]])])]),n("tr",null,[Tt,n("td",null,[p(n("input",{type:"color","onUpdate:modelValue":o[2]||(o[2]=t=>e.modelValue.color=t)},null,512),[[g,e.modelValue.color]])])]),n("tr",null,[zt,n("td",null,[p(n("input",{type:"text",maxLength:"16","onUpdate:modelValue":o[3]||(o[3]=t=>e.modelValue.name=t)},null,512),[[g,e.modelValue.name]])])]),n("tr",null,[It,n("td",null,[p(n("input",{type:"checkbox","onUpdate:modelValue":o[4]||(o[4]=t=>e.modelValue.soundsEnabled=t)},null,512),[[C,e.modelValue.soundsEnabled]])])]),n("tr",null,[Dt,n("td",Et,[n("span",{onClick:o[5]||(o[5]=(...t)=>e.decreaseVolume&&e.decreaseVolume(...t))},"πŸ”‰"),n("input",{type:"range",min:"0",max:"100",value:e.modelValue.soundsVolume,onChange:o[6]||(o[6]=(...t)=>e.updateVolume&&e.updateVolume(...t))},null,40,["value"]),n("span",{onClick:o[7]||(o[7]=(...t)=>e.increaseVolume&&e.increaseVolume(...t))},"πŸ”Š")])]),n("tr",null,[Mt,n("td",null,[p(n("input",{type:"checkbox","onUpdate:modelValue":o[8]||(o[8]=t=>e.modelValue.showPlayerNames=t)},null,512),[[C,e.modelValue.showPlayerNames]])])])])]))));Pt.render=Nt,Pt.__scopeId="data-v-4d56fc17";var _t=e({name:"preview-overlay",props:{img:String},emits:{bgclick:null},computed:{previewStyle(){return{backgroundImage:`url('${this.img}')`}}}});const Vt={class:"preview"};_t.render=function(e,o,l,a,i,r){return s(),t("div",{class:"overlay",onClick:o[1]||(o[1]=t=>e.$emit("bgclick"))},[n("div",Vt,[n("div",{class:"img",style:e.previewStyle},null,4)])])};var Ot=1,Ut=4,Bt=2,Rt=3,Gt=2,$t=4,Lt=3,Ft=9,jt=1,Wt=2,Kt=3,Ht=4,Yt=5,qt=6,Qt=7,Zt=8,Xt=10,Jt=11,en=12,tn=13,nn=14,on=15,ln=16,an=1,sn=2,rn=3;const dn=Z("Communication.js");let cn,un=[],pn=e=>{un.push(e)},gn=[],hn=e=>{gn.push(e)};let mn=0;const yn=e=>{mn!==e&&(mn=e,hn(e))};function fn(e){if(2===mn)try{cn.send(JSON.stringify(e))}catch(t){dn.info("unable to send message.. maybe because ws is invalid?")}}let wn,vn;var bn={connect:function(e,t,n){return wn=0,vn={},yn(3),new Promise((o=>{cn=new WebSocket(e,n+"|"+t),cn.onopen=()=>{yn(2),fn([Rt])},cn.onmessage=e=>{const t=JSON.parse(e.data),l=t[0];if(l===Ut){const e=t[1];o(e)}else{if(l!==Ot)throw`[ 2021-05-09 invalid connect msgType ${l} ]`;{const e=t[1],o=t[2];if(e===n&&vn[o])return void delete vn[o];pn(t)}}},cn.onerror=()=>{throw yn(1),"[ 2021-05-15 onerror ]"},cn.onclose=e=>{4e3===e.code||1001===e.code?yn(4):yn(1)}}))},requestReplayData:async function(e,t){const n={gameId:e,offset:t},o=await fetch(`/api/replay-data${X.asQueryArgs(n)}`);return await o.json()},disconnect:function(){cn&&cn.close(4e3),wn=0,vn={}},sendClientEvent:function(e){wn++,vn[wn]=e,fn([Bt,wn,vn[wn]])},onServerChange:function(e){pn=e;for(const t of un)pn(t);un=[]},onConnectionStateChange:function(e){hn=e;for(const t of gn)hn(t);gn=[]},CODE_CUSTOM_DISCONNECT:4e3,CONN_STATE_NOT_CONNECTED:0,CONN_STATE_DISCONNECTED:1,CONN_STATE_CLOSED:4,CONN_STATE_CONNECTED:2,CONN_STATE_CONNECTING:3},Cn=e({name:"connection-overlay",emits:{reconnect:null},props:{connectionState:Number},computed:{lostConnection(){return this.connectionState===bn.CONN_STATE_DISCONNECTED},connecting(){return this.connectionState===bn.CONN_STATE_CONNECTING},show(){return!(!this.lostConnection&&!this.connecting)}}});const xn={key:0,class:"overlay connection-lost"},kn={key:0,class:"overlay-content"},Pn=n("div",null,"⁉️ LOST CONNECTION ⁉️",-1),An={key:1,class:"overlay-content"},Sn=n("div",null,"Connecting...",-1);Cn.render=function(e,o,a,i,r,d){return e.show?(s(),t("div",xn,[e.lostConnection?(s(),t("div",kn,[Pn,n("span",{class:"btn",onClick:o[1]||(o[1]=t=>e.$emit("reconnect"))},"Reconnect")])):l("",!0),e.connecting?(s(),t("div",An,[Sn])):l("",!0)])):l("",!0)};var Tn=e({name:"help-overlay",emits:{bgclick:null}});const zn=n("tr",null,[n("td",null,"⬆️ Move up:"),n("td",null,[n("div",null,[n("kbd",null,"W"),i("/"),n("kbd",null,"↑"),i("/πŸ–±οΈ")])])],-1),In=n("tr",null,[n("td",null,"⬇️ Move down:"),n("td",null,[n("div",null,[n("kbd",null,"S"),i("/"),n("kbd",null,"↓"),i("/πŸ–±οΈ")])])],-1),Dn=n("tr",null,[n("td",null,"⬅️ Move left:"),n("td",null,[n("div",null,[n("kbd",null,"A"),i("/"),n("kbd",null,"←"),i("/πŸ–±οΈ")])])],-1),En=n("tr",null,[n("td",null,"➑️ Move right:"),n("td",null,[n("div",null,[n("kbd",null,"D"),i("/"),n("kbd",null,"β†’"),i("/πŸ–±οΈ")])])],-1),Mn=n("tr",null,[n("td"),n("td",null,[n("div",null,[i("Move faster by holding "),n("kbd",null,"Shift")])])],-1),Nn=n("tr",null,[n("td",null,"πŸ”+ Zoom in:"),n("td",null,[n("div",null,[n("kbd",null,"E"),i("/πŸ–±οΈ-Wheel")])])],-1),_n=n("tr",null,[n("td",null,"πŸ”- Zoom out:"),n("td",null,[n("div",null,[n("kbd",null,"Q"),i("/πŸ–±οΈ-Wheel")])])],-1),Vn=n("tr",null,[n("td",null,"πŸ–ΌοΈ Toggle preview:"),n("td",null,[n("div",null,[n("kbd",null,"Space")])])],-1),On=n("tr",null,[n("td",null,"🎯 Center puzzle in screen:"),n("td",null,[n("div",null,[n("kbd",null,"C")])])],-1),Un=n("tr",null,[n("td",null,"πŸ§©βœ”οΈ Toggle fixed pieces:"),n("td",null,[n("div",null,[n("kbd",null,"F")])])],-1),Bn=n("tr",null,[n("td",null,"πŸ§©β“ Toggle loose pieces:"),n("td",null,[n("div",null,[n("kbd",null,"G")])])],-1),Rn=n("tr",null,[n("td",null,"πŸ‘€ Toggle player names:"),n("td",null,[n("div",null,[n("kbd",null,"N")])])],-1),Gn=n("tr",null,[n("td",null,"πŸ”‰ Toggle sounds:"),n("td",null,[n("div",null,[n("kbd",null,"M")])])],-1),$n=n("tr",null,[n("td",null,"⏫ Speed up (replay):"),n("td",null,[n("div",null,[n("kbd",null,"I")])])],-1),Ln=n("tr",null,[n("td",null,"⏬ Speed down (replay):"),n("td",null,[n("div",null,[n("kbd",null,"O")])])],-1),Fn=n("tr",null,[n("td",null,"⏸️ Pause (replay):"),n("td",null,[n("div",null,[n("kbd",null,"P")])])],-1);Tn.render=function(e,o,l,a,i,r){return s(),t("div",{class:"overlay transparent",onClick:o[2]||(o[2]=t=>e.$emit("bgclick"))},[n("table",{class:"overlay-content help",onClick:o[1]||(o[1]=u((()=>{}),["stop"]))},[zn,In,Dn,En,Mn,Nn,_n,Vn,On,Un,Bn,Rn,Gn,$n,Ln,Fn])])};var jn=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"/assets/click.bb97cb07.mp3"}),Wn=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAW0lEQVQ4je1RywrAIAxLxP//5exixRWlVgZelpOKeTQFfnDypgy3eLIkSLLL8mxGPoHsU2hPAgDHBLvRX6hZZw/fwT0BtlLSONqCbWAmEIqMZOCDDlaDR3N03gOyDCn+y4DWmAAAAABJRU5ErkJggg=="}),Kn=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAARElEQVQ4jWNgGAU0Af+hmBCbgYGBgYERhwHEAEYGBgYGJtIdiApYyLAZBVDsAqoagC1ACQJyY4ERg0GCISh6KA4DigEAou8LC+LnIJoAAAAASUVORK5CYII="}),Hn=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAcUlEQVQ4ja1TQQ7AIAgD///n7jCozA2Hbk00jbG1KIrcARszTugoBs49qioZj7r2kKACptkyAOCJsJuA+GzglwHjvMSSWFVaENWVASxh5eRLiq5fN/ASjI89VcP2K3hHpq1cEXNaMfnrL3TDZP2tDuoOA6MzCCXWr38AAAAASUVORK5CYII="}),Yn=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAU0lEQVQ4jWNgoAH4D8X42HDARKlt5BoAd82AuQAOGLGIYQQUPv0wF5CiCQUge4EsQ5C9QI4BjMguwBYeBAElscCIy1ZivMKIwSDBEBQ9FCckigEAU3QOD7TGvY4AAAAASUVORK5CYII="});function qn(e=0,t=0){const n=document.createElement("canvas");return n.width=e,n.height=t,n}var Qn={createCanvas:qn,loadImageToBitmap:async function(e){return new Promise((t=>{const n=new Image;n.onload=()=>{createImageBitmap(n).then(t)},n.src=e}))},resizeBitmap:async function(e,t,n){const o=qn(t,n);return o.getContext("2d").drawImage(e,0,0,e.width,e.height,0,0,t,n),await createImageBitmap(o)},colorizedCanvas:function(e,t,n){const o=qn(e.width,e.height),l=o.getContext("2d");return l.save(),l.drawImage(t,0,0),l.fillStyle=n,l.globalCompositeOperation="source-in",l.fillRect(0,0,t.width,t.height),l.restore(),l.save(),l.globalCompositeOperation="destination-over",l.drawImage(e,0,0),l.restore(),o}};const Zn=Z("Debug.js");let Xn=0,Jn=0;var eo=e=>{Xn=performance.now(),Jn=e},to=e=>{const t=performance.now(),n=t-Xn;n>Jn&&Zn.log(e+": "+n),Xn=t};function no(e,t){const n=e.x-t.x,o=e.y-t.y;return Math.sqrt(n*n+o*o)}function oo(e){return{x:e.x+e.w/2,y:e.y+e.h/2}}var lo={pointSub:function(e,t){return{x:e.x-t.x,y:e.y-t.y}},pointAdd:function(e,t){return{x:e.x+t.x,y:e.y+t.y}},pointDistance:no,pointInBounds:function(e,t){return e.x>=t.x&&e.x<=t.x+t.w&&e.y>=t.y&&e.y<=t.y+t.h},rectCenter:oo,rectMoved:function(e,t,n){return{x:e.x+t,y:e.y+n,w:e.w,h:e.h}},rectCenterDistance:function(e,t){return no(oo(e),oo(t))},rectsOverlap:function(e,t){return!(t.x>e.x+e.w||e.x>t.x+t.w||t.y>e.y+e.h||e.y>t.y+t.h)}};const ao=Z("PuzzleGraphics.js");function so(e,t){const n=X.coordByPieceIdx(e,t);return{x:n.x*e.tileSize,y:n.y*e.tileSize,w:e.tileSize,h:e.tileSize}}var io={loadPuzzleBitmaps:async function(e){const t=await Qn.loadImageToBitmap(e.info.imageUrl),n=await Qn.resizeBitmap(t,e.info.width,e.info.height);return await async function(e,t,n){ao.log("start createPuzzleTileBitmaps");const o=n.tileSize,l=n.tileMarginWidth,a=n.tileDrawSize,s=o/100,i=[0,0,40,15,37,5,37,5,40,0,38,-5,38,-5,20,-20,50,-20,50,-20,80,-20,62,-5,62,-5,60,0,63,5,63,5,65,15,100,0],r=new Array(t.length),d={};function c(e){const t=`${e.top}${e.right}${e.left}${e.bottom}`;if(d[t])return d[t];const n=new Path2D,a={x:l,y:l},r=lo.pointAdd(a,{x:o,y:0}),c=lo.pointAdd(r,{x:0,y:o}),u=lo.pointSub(c,{x:o,y:0});if(n.moveTo(a.x,a.y),0!==e.top)for(let o=0;oX.decodePiece(ro[e].puzzle.tiles[t]),Po=(e,t)=>ko(e,t).group,Ao=(e,t)=>{const n=ro[e].puzzle.info;return 0===t||t===n.tilesX-1||t===n.tiles-n.tilesX||t===n.tiles-1},So=(e,t)=>{const n=ro[e].puzzle.info,o={x:(n.table.width-n.width)/2,y:(n.table.height-n.height)/2},l=function(e,t){const n=ro[e].puzzle.info,o=X.coordByPieceIdx(n,t),l=o.x*n.tileSize,a=o.y*n.tileSize;return{x:l,y:a}}(e,t);return lo.pointAdd(o,l)},To=(e,t)=>ko(e,t).pos,zo=e=>{const t=Wo(e),n=Ko(e),o=Math.round(t/4),l=Math.round(n/4);return{x:0-o,y:0-l,w:t+2*o,h:n+2*l}},Io=(e,t)=>{const n=No(e),o=ko(e,t);return{x:o.pos.x,y:o.pos.y,w:n,h:n}},Do=(e,t)=>ko(e,t).z,Eo=(e,t)=>{for(const n of ro[e].puzzle.tiles){const e=X.decodePiece(n);if(e.owner===t)return e.idx}return-1},Mo=e=>ro[e].puzzle.info.tileDrawSize,No=e=>ro[e].puzzle.info.tileSize,_o=e=>ro[e].puzzle.data.maxGroup,Vo=e=>ro[e].puzzle.data.maxZ;function Oo(e,t){const n=ro[e].puzzle.info,o=X.coordByPieceIdx(n,t);return[o.y>0?t-n.tilesX:-1,o.x0?t-1:-1]}const Uo=(e,t,n)=>{for(const o of t)xo(e,o,{z:n})},Bo=(e,t,n)=>{const o=To(e,t);xo(e,t,{pos:lo.pointAdd(o,n)})},Ro=(e,t,n)=>{const o=Mo(e),l=zo(e),a=n;for(const s of t){const t=ko(e,s);t.pos.x+n.xl.x+l.w&&(a.x=Math.min(l.x+l.w-t.pos.x+o,a.x)),t.pos.y+n.yl.y+l.h&&(a.y=Math.min(l.y+l.h-t.pos.y+o,a.y))}for(const s of t)Bo(e,s,a)},Go=(e,t)=>ko(e,t).owner,$o=(e,t)=>{for(const n of t)xo(e,n,{owner:-1,z:1})},Lo=(e,t,n)=>{for(const o of t)xo(e,o,{owner:n})};function Fo(e,t){const n=ro[e].puzzle.tiles,o=X.decodePiece(n[t]),l=[];if(o.group)for(const a of n){const e=X.decodePiece(a);e.group===o.group&&l.push(e.idx)}else l.push(o.idx);return l}const jo=(e,t)=>{const n=uo(e,t);return n?n.points:0},Wo=e=>ro[e].puzzle.info.table.width,Ko=e=>ro[e].puzzle.info.table.height;var Ho={setGame:function(e,t){ro[e]=t},exists:function(e){return!!ro[e]||!1},playerExists:go,getActivePlayers:function(e,t){const n=t-30*_;return ho(e).filter((e=>e.ts>=n))},getIdlePlayers:function(e,t){const n=t-30*_;return ho(e).filter((e=>e.ts0))},addPlayer:function(e,t,n){go(e,t)?bo(e,t,{ts:n}):po(e,t,function(e,t){return{id:e,x:0,y:0,d:0,name:null,color:null,bgcolor:null,points:0,ts:t}}(t,n))},getFinishedPiecesCount:vo,getPieceCount:mo,getImageUrl:function(e){return ro[e].puzzle.info.imageUrl},setImageUrl:function(e,t){ro[e].puzzle.info.imageUrl=t},get:function(e){return ro[e]||null},getAllGames:function(){return Object.values(ro).sort(((e,t)=>wo(e.id)===wo(t.id)?t.puzzle.data.started-e.puzzle.data.started:wo(e.id)?1:-1))},getPlayerBgColor:(e,t)=>{const n=uo(e,t);return n?n.bgcolor:null},getPlayerColor:(e,t)=>{const n=uo(e,t);return n?n.color:null},getPlayerName:(e,t)=>{const n=uo(e,t);return n?n.name:null},getPlayerIndexById:co,getPlayerIdByIndex:function(e,t){return ro[e].players.length>t?X.decodePlayer(ro[e].players[t]).id:null},changePlayer:bo,setPlayer:po,setPiece:function(e,t,n){ro[e].puzzle.tiles[t]=X.encodePiece(n)},setPuzzleData:function(e,t){ro[e].puzzle.data=t},getTableWidth:Wo,getTableHeight:Ko,getPuzzle:e=>ro[e].puzzle,getRng:e=>ro[e].rng.obj,getPuzzleWidth:e=>ro[e].puzzle.info.width,getPuzzleHeight:e=>ro[e].puzzle.info.height,getPiecesSortedByZIndex:function(e){return ro[e].puzzle.tiles.map(X.decodePiece).sort(((e,t)=>e.z-t.z))},getFirstOwnedPiece:(e,t)=>{const n=Eo(e,t);return n<0?null:ro[e].puzzle.tiles[n]},getPieceDrawOffset:e=>ro[e].puzzle.info.tileDrawOffset,getPieceDrawSize:Mo,getFinalPiecePos:So,getStartTs:e=>ro[e].puzzle.data.started,getFinishTs:e=>ro[e].puzzle.data.finished,handleInput:function(e,t,n,o,l){const a=ro[e].puzzle,s=function(e,t){return t in ro[e].evtInfos?ro[e].evtInfos[t]:{_last_mouse:null,_last_mouse_down:null}}(e,t),i=[],r=()=>{i.push([an,a.data])},d=t=>{i.push([sn,X.encodePiece(ko(e,t))])},c=e=>{for(const t of e)d(t)},u=()=>{const n=uo(e,t);n&&i.push([rn,X.encodePlayer(n)])},p=n[0];if(p===qt){const l=n[1];bo(e,t,{bgcolor:l,ts:o}),u()}else if(p===Qt){const l=n[1];bo(e,t,{color:l,ts:o}),u()}else if(p===Zt){const l=`${n[1]}`.substr(0,16);bo(e,t,{name:l,ts:o}),u()}else if(p===Ft){const l=n[1],a=n[2],s=uo(e,t);if(s){const n=s.x-l,i=s.y-a;bo(e,t,{ts:o,x:n,y:i}),u()}}else if(p===jt){const l={x:n[1],y:n[2]};bo(e,t,{d:1,ts:o}),u(),s._last_mouse_down=l;const a=((e,t)=>{const n=ro[e].puzzle.info,o=ro[e].puzzle.tiles;let l=-1,a=-1;for(let s=0;sl)&&(l=e.z,a=s)}return a})(e,l);if(a>=0){const n=Vo(e)+1;Co(e,{maxZ:n}),r();const o=Fo(e,a);Uo(e,o,Vo(e)),Lo(e,o,t),c(o)}s._last_mouse=l}else if(p===Kt){const l=n[1],a=n[2],i={x:l,y:a};if(null===s._last_mouse_down)bo(e,t,{x:l,y:a,ts:o}),u();else{const n=Eo(e,t);if(n>=0){bo(e,t,{x:l,y:a,ts:o}),u();const r=Fo(e,n);let d=lo.pointInBounds(i,zo(e))&&lo.pointInBounds(s._last_mouse_down,zo(e));for(const t of r){const n=Io(e,t);if(lo.pointInBounds(i,n)){d=!0;break}}if(d){const t=l-s._last_mouse_down.x,n=a-s._last_mouse_down.y;Ro(e,r,{x:t,y:n}),c(r)}}else bo(e,t,{ts:o}),u();s._last_mouse_down=i}s._last_mouse=i}else if(p===Wt){const i={x:n[1],y:n[2]},p=0;s._last_mouse_down=null;const g=Eo(e,t);if(g>=0){const n=Fo(e,g);Lo(e,n,0),c(n);const s=To(e,g),i=So(e,g);let h=!1;if(fo(e)===_e.REAL){for(const t of n)if(Ao(e,t)){h=!0;break}}else h=!0;if(h&&lo.pointDistance(i,s){const l=ro[e].puzzle.info;if(n<0)return!1;if(((e,t,n)=>{const o=Po(e,t),l=Po(e,n);return!(!o||o!==l)})(e,t,n))return!1;const a=To(e,t),s=lo.pointAdd(To(e,n),{x:o[0]*l.tileSize,y:o[1]*l.tileSize});if(lo.pointDistance(a,s){const o=ro[e].puzzle.tiles,l=Po(e,t),a=Po(e,n);let s;const i=[];l&&i.push(l),a&&i.push(a),l?s=l:a?s=a:(Co(e,{maxGroup:_o(e)+1}),r(),s=_o(e));if(xo(e,t,{group:s}),d(t),xo(e,n,{group:s}),d(n),i.length>0)for(const r of o){const t=X.decodePiece(r);i.includes(t.group)&&(xo(e,t.idx,{group:s}),d(t.idx))}})(e,t,n),l=Fo(e,t),((e,t)=>-1===Go(e,t))(e,n))$o(e,l);else{const t=((e,t)=>{let n=0;for(const o of t){const t=Do(e,o);t>n&&(n=t)}return n})(e,l);Uo(e,l,t)}return c(l),!0}return!1};let a=!1;for(const t of Fo(e,g)){const o=Oo(e,t);if(n(e,t,o[0],[0,1])||n(e,t,o[1],[-1,0])||n(e,t,o[2],[0,-1])||n(e,t,o[3],[1,0])){a=!0;break}}if(a&&yo(e)===De.ANY){const n=jo(e,t)+1;bo(e,t,{d:p,ts:o,points:n}),u()}else bo(e,t,{d:p,ts:o}),u();a&&fo(e)===_e.REAL&&vo(e)===mo(e)&&(Co(e,{finished:o}),r()),a&&l&&l(t)}}else bo(e,t,{d:p,ts:o}),u();s._last_mouse=i}else if(p===Ht){const l=n[1],a=n[2];bo(e,t,{x:l,y:a,ts:o}),u(),s._last_mouse={x:l,y:a}}else if(p===Yt){const l=n[1],a=n[2];bo(e,t,{x:l,y:a,ts:o}),u(),s._last_mouse={x:l,y:a}}else bo(e,t,{ts:o}),u();return function(e,t,n){ro[e].evtInfos[t]=n}(e,t,s),i}};let Yo=-10,qo=20,Qo=2,Zo=15;class Xo{constructor(e){this.radius=10,this.previousRadius=10,this.explodingDuration=100,this.hasExploded=!1,this.alive=!0,this.color=function(e){return"rgba("+e.random(0,255)+","+e.random(0,255)+","+e.random(0,255)+", 0.8)"}(e),this.px=window.innerWidth/4+Math.random()*window.innerWidth/2,this.py=window.innerHeight,this.vx=Yo+Math.random()*qo,this.vy=-1*(Qo+Math.random()*Zo),this.duration=0}update(e){if(this.hasExploded){const e=200-this.radius;this.previousRadius=this.radius,this.radius+=e/10,this.explodingDuration--,0==this.explodingDuration&&(this.alive=!1)}else this.vx+=0,this.vy+=1,this.vy>=0&&e&&this.explode(e),this.px+=this.vx,this.py+=this.vy}draw(e){e.beginPath(),e.arc(this.px,this.py,this.previousRadius,0,2*Math.PI,!1),this.hasExploded||(e.fillStyle=this.color,e.lineWidth=1,e.fill())}explode(e){this.hasExploded=!0;const t=3+Math.floor(3*Math.random());for(let n=0;n{this.resize()}))}setSpeedParams(){let e=0,t=0;for(;e=0;)t+=1,e+=t;Qo=t/2,Zo=t-Qo;const n=1/4*this.canvas.width/(t/2);Yo=-n,qo=2*n}resize(){this.setSpeedParams()}init(){this.readyBombs=[],this.explodedBombs=[],this.particles=[];for(let e=0;e<1;e++)this.readyBombs.push(new Xo(this.rng))}update(){100*Math.random()<5&&this.readyBombs.push(new Xo(this.rng));const e=[];for(;this.explodedBombs.length>0;){const t=this.explodedBombs.shift();if(!t)break;t.update(),t.alive&&e.push(t)}this.explodedBombs=e;const t=[];for(;this.readyBombs.length>0;){const e=this.readyBombs.shift();if(!e)break;e.update(this.particles),e.hasExploded?this.explodedBombs.push(e):t.push(e)}this.readyBombs=t;const n=[];for(;this.particles.length>0;){const e=this.particles.shift();if(!e)break;e.update(),e.alive&&n.push(e)}this.particles=n}render(){this.ctx.beginPath(),this.ctx.fillStyle="rgba(0, 0, 0, 0.1)",this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height);for(let e=0;e{localStorage.setItem(e,t)},rl=e=>localStorage.getItem(e);var dl=(e,t)=>{il(e,`${t}`)},cl=(e,t)=>{const n=rl(e);if(null===n)return t;const o=parseInt(n,10);return isNaN(o)?t:o},ul=(e,t)=>{il(e,t?"1":"0")},pl=(e,t)=>{const n=rl(e);return null===n?t:"1"===n},gl=(e,t)=>{il(e,t)},hl=(e,t)=>{const n=rl(e);return null===n?t:n};const ml={"./grab.png":Wn,"./grab_mask.png":Kn,"./hand.png":Hn,"./hand_mask.png":Yn},yl={"./click.mp3":jn};let fl=!0,wl=!0;let vl=!0;async function bl(e,t,n,o,l,a){void 0===window.DEBUG&&(window.DEBUG=!1);const s=yl["./click.mp3"].default,i=new Audio(s),r=await Qn.loadImageToBitmap(ml["./grab.png"].default),d=await Qn.loadImageToBitmap(ml["./hand.png"].default),c=await Qn.loadImageToBitmap(ml["./grab_mask.png"].default),u=await Qn.loadImageToBitmap(ml["./hand_mask.png"].default),p=r.width,g=Math.round(p/2),h=r.height,m=Math.round(h/2),y={},f=async e=>{const t=e.color+" "+e.d;if(!y[t]){const n=e.d?r:d;if(e.color){const o=e.d?c:u;y[t]=await createImageBitmap(Qn.colorizedCanvas(n,o,e.color))}else y[t]=n}return y[t]},w=function(e,t){return t.width=window.innerWidth,t.height=window.innerHeight,e.appendChild(t),window.addEventListener("resize",(()=>{t.width=window.innerWidth,t.height=window.innerHeight,vl=!0})),t}(l,Qn.createCanvas()),v={final:!1,log:[],logPointer:0,speeds:[.5,1,2,5,10,20,50,100,250,500],speedIdx:1,paused:!1,lastRealTs:0,lastGameTs:0,gameStartTs:0,skipNonActionPhases:!0,dataOffset:0};bn.onConnectionStateChange((e=>{a.setConnectionState(e)}));const b=async e=>{const t=v.dataOffset;v.dataOffset+=1e4;const n=await bn.requestReplayData(e,t);return v.log=v.log.slice(v.logPointer),v.logPointer=0,v.log.push(...n.log),0===n.log.length&&(v.final=!0),n};let C=()=>0;const x=async()=>{if("play"===o){const o=await bn.connect(n,e,t),l=X.decodeGame(o);Ho.setGame(l.id,l),C=()=>V()}else{if("replay"!==o)throw"[ 2020-12-22 MODE invalid, must be play|replay ]";{const t=await b(e);if(!t.game)throw"[ 2021-05-29 no game received ]";const n=X.decodeGame(t.game);Ho.setGame(n.id,n),v.lastRealTs=V(),v.gameStartTs=parseInt(t.log[0][4],10),v.lastGameTs=v.gameStartTs,C=()=>v.lastGameTs}}vl=!0};await x();const k=Ho.getPieceDrawOffset(e),P=Ho.getPieceDrawSize(e),A=Ho.getPuzzleWidth(e),S=Ho.getPuzzleHeight(e),T=Ho.getTableWidth(e),z=Ho.getTableHeight(e),I={x:(T-A)/2,y:(z-S)/2},D={w:A,h:S},E={w:P,h:P},M=await io.loadPuzzleBitmaps(Ho.getPuzzle(e)),_=new el(w,Ho.getRng(e));_.init();const O=w.getContext("2d");w.classList.add("loaded");const U=function(){let e=0,t=0,n=1;const o=(o,l)=>{e+=o/n,t+=l/n},l=e=>{const t=n+.05*n*("in"===e?1:-1);return Math.min(Math.max(t,.1),6)},a=(e,t)=>{if(n==e)return!1;const l=1-n/e;return o(-t.x*l,-t.y*l),n=e,!0},s=o=>({x:o.x/n-e,y:o.y/n-t}),i=o=>({x:(o.x+e)*n,y:(o.y+t)*n}),r=e=>({w:e.w*n,h:e.h*n}),d=e=>({w:e.w/n,h:e.h/n});return{getCurrentZoom:()=>n,reset:()=>{e=0,t=0,n=1},move:o,canZoom:e=>n!=l(e),zoom:(e,t)=>a(l(e),t),setZoom:a,worldToViewport:e=>{const{x:t,y:n}=i(e);return{x:Math.round(t),y:Math.round(n)}},worldToViewportRaw:i,worldDimToViewport:e=>{const{w:t,h:n}=r(e);return{w:Math.round(t),h:Math.round(n)}},worldDimToViewportRaw:r,viewportToWorld:e=>{const{x:t,y:n}=s(e);return{x:Math.round(t),y:Math.round(n)}},viewportToWorldRaw:s,viewportDimToWorld:e=>{const{w:t,h:n}=d(e);return{w:Math.round(t),h:Math.round(n)}},viewportDimToWorldRaw:d}}(),B=()=>{U.reset(),U.move(-(T-w.width)/2,-(z-w.height)/2);const e=U.worldDimToViewport(D),t=w.width-40,n=w.height-40;if(e.w>t||e.h>n||e.w{const o=n.viewportToWorld({x:e,y:t});return[o.x,o.y]},h=e=>g(e.offsetX,e.offsetY),m=()=>g(e.width/2,e.height/2),y=(e,t)=>{a&&("ShiftLeft"===t.code||"ShiftRight"===t.code?p=e:"ArrowUp"===t.code||"KeyW"===t.code?r=e:"ArrowDown"===t.code||"KeyS"===t.code?d=e:"ArrowLeft"===t.code||"KeyA"===t.code?s=e:"ArrowRight"===t.code||"KeyD"===t.code?i=e:"KeyQ"===t.code?u=e:"KeyE"===t.code&&(c=e))};let f=null;e.addEventListener("mousedown",(e=>{f=h(e),0===e.button&&w([jt,...f])})),e.addEventListener("mouseup",(e=>{f=h(e),0===e.button&&w([Wt,...f])})),e.addEventListener("mousemove",(e=>{f=h(e),w([Kt,...f])})),e.addEventListener("wheel",(e=>{if(f=h(e),n.canZoom(e.deltaY<0?"in":"out")){const t=e.deltaY<0?Ht:Yt;w([t,...f])}})),t.addEventListener("keydown",(e=>y(!0,e))),t.addEventListener("keyup",(e=>y(!1,e))),t.addEventListener("keypress",(e=>{a&&("Space"===e.code&&w([Xt]),"replay"===o&&("KeyI"===e.code&&w([tn]),"KeyO"===e.code&&w([nn]),"KeyP"===e.code&&w([en])),"KeyF"===e.code&&(fl=!fl,vl=!0),"KeyG"===e.code&&(wl=!wl,vl=!0),"KeyM"===e.code&&w([Jt]),"KeyN"===e.code&&w([on]),"KeyC"===e.code&&w([ln]))}));const w=e=>{l.push(e)};return{addEvent:w,consumeAll:()=>{if(0===l.length)return[];const e=l.slice();return l=[],e},createKeyEvents:()=>{const e=(s?1:0)-(i?1:0),t=(r?1:0)-(d?1:0);if(0!==e||0!==t){const o=(p?24:12)*Math.sqrt(n.getCurrentZoom()),l=n.viewportDimToWorld({w:e*o,h:t*o});w([Ft,l.w,l.h]),f&&(f[0]-=l.w,f[1]-=l.h)}if(c&&u);else if(c){if(n.canZoom("in")){const e=f||m();w([Ht,...e])}}else if(u&&n.canZoom("out")){const e=f||m();w([Yt,...e])}},setHotkeys:e=>{a=e}}}(w,window,U,o),G=Ho.getImageUrl(e),$=()=>{const t=Ho.getStartTs(e),n=Ho.getFinishTs(e),o=C();a.setFinished(!!n),a.setDuration((n||o)-t)};$(),a.setPiecesDone(Ho.getFinishedPiecesCount(e)),a.setPiecesTotal(Ho.getPieceCount(e));const L=C();a.setActivePlayers(Ho.getActivePlayers(e,L)),a.setIdlePlayers(Ho.getIdlePlayers(e,L));const F=!!Ho.getFinishTs(e);let j=F;const W=()=>j&&!F,K=()=>cl(tl,100),H=()=>pl(nl,!1),Y=()=>pl(sl,!0),q=()=>{const e=K();i.volume=e/100,i.play()},Q=()=>"replay"===o?hl(ol,"#222222"):Ho.getPlayerBgColor(e,t)||hl(ol,"#222222"),Z=()=>"replay"===o?hl(ll,"#ffffff"):Ho.getPlayerColor(e,t)||hl(ll,"#ffffff");let J="",ee="",te=!1;const ne=e=>{te=e;const[t,n]=e?[J,"grab"]:[ee,"default"];w.style.cursor=`url('${t}') ${g} ${m}, ${n}`},oe=e=>{J=Qn.colorizedCanvas(r,c,e).toDataURL(),ee=Qn.colorizedCanvas(d,u,e).toDataURL(),ne(te)};oe(Z());const le=()=>{a.setReplaySpeed&&a.setReplaySpeed(v.speeds[v.speedIdx]),a.setReplayPaused&&a.setReplayPaused(v.paused)},ae=()=>{v.speedIdx+1{v.speedIdx>=1&&(v.speedIdx--,le())},ie=()=>{v.paused=!v.paused,le()},re=[];let de;let ce;if("play"===o?re.push(setInterval((()=>{$()}),1e3)):"replay"===o&&le(),"play"===o)bn.onServerChange((n=>{n[0],n[1],n[2];const o=n[3];for(const[l,a]of o)switch(l){case rn:{const n=X.decodePlayer(a);n.id!==t&&(Ho.setPlayer(e,n.id,n),vl=!0)}break;case sn:{const t=X.decodePiece(a);Ho.setPiece(e,t.idx,t),vl=!0}break;case an:Ho.setPuzzleData(e,a),vl=!0}j=!!Ho.getFinishTs(e)}));else if("replay"===o){const t=(t,n)=>{const o=t;if(o[0]===Gt){const t=o[1];return Ho.addPlayer(e,t,n),!0}if(o[0]===$t){const t=Ho.getPlayerIdByIndex(e,o[1]);if(!t)throw"[ 2021-05-17 player not found (update player) ]";return Ho.addPlayer(e,t,n),!0}if(o[0]===Lt){const t=Ho.getPlayerIdByIndex(e,o[1]);if(!t)throw"[ 2021-05-17 player not found (handle input) ]";const l=o[2];return Ho.handleInput(e,t,l,n),!0}return!1};let n=v.lastGameTs;const o=async()=>{v.logPointer+1>=v.log.length&&await b(e);const l=V();if(v.paused)return v.lastRealTs=l,void(de=setTimeout(o,50));const a=(l-v.lastRealTs)*v.speeds[v.speedIdx];let s=v.lastGameTs+a;for(;;){if(v.paused)break;const e=v.logPointer+1;if(e>=v.log.length)break;const o=v.log[v.logPointer],l=n+o[o.length-1],a=v.log[e],i=a[a.length-1],r=l+i;if(r>s){s+500*N{let t=!1;const n=e.fps||60,o=e.slow||1,l=e.update,a=e.render,s=window.requestAnimationFrame,i=1/n,r=o*i;let d,c=0,u=window.performance.now();const p=()=>{for(d=window.performance.now(),c+=Math.min(1,(d-u)/1e3);c>r;)c-=r,l(i);a(c/o),u=d,t||s(p)};return s(p),{stop:()=>{t=!0}}})({update:()=>{R.createKeyEvents();for(const n of R.consumeAll())if("play"===o){const o=n[0];if(o===Ft){const e=n[1],t=n[2],o=U.worldDimToViewport({w:e,h:t});vl=!0,U.move(o.w,o.h)}else if(o===Kt){if(ue&&!Ho.getFirstOwnedPiece(e,t)){const e={x:n[1],y:n[2]},t=U.worldToViewport(e),o=Math.round(t.x-ue.x),l=Math.round(t.y-ue.y);vl=!0,U.move(o,l),ue=t}}else if(o===Qt)oe(n[1]);else if(o===jt){const e={x:n[1],y:n[2]};ue=U.worldToViewport(e),ne(!0)}else if(o===Wt)ue=null,ne(!1);else if(o===Ht){const e={x:n[1],y:n[2]};vl=!0,U.zoom("in",U.worldToViewport(e))}else if(o===Yt){const e={x:n[1],y:n[2]};vl=!0,U.zoom("out",U.worldToViewport(e))}else o===Xt?a.togglePreview():o===Jt?a.toggleSoundsEnabled():o===on?a.togglePlayerNames():o===ln&&B();const l=C();Ho.handleInput(e,t,n,l,(e=>{H()&&q()})).length>0&&(vl=!0),bn.sendClientEvent(n)}else if("replay"===o){const e=n[0];if(e===en)ie();else if(e===nn)se();else if(e===tn)ae();else if(e===Ft){const e=n[1],t=n[2];vl=!0,U.move(e,t)}else if(e===Kt){if(ue){const e={x:n[1],y:n[2]},t=U.worldToViewport(e),o=Math.round(t.x-ue.x),l=Math.round(t.y-ue.y);vl=!0,U.move(o,l),ue=t}}else if(e===Qt)oe(n[1]);else if(e===jt){const e={x:n[1],y:n[2]};ue=U.worldToViewport(e),ne(!0)}else if(e===Wt)ue=null,ne(!1);else if(e===Ht){const e={x:n[1],y:n[2]};vl=!0,U.zoom("in",U.worldToViewport(e))}else if(e===Yt){const e={x:n[1],y:n[2]};vl=!0,U.zoom("out",U.worldToViewport(e))}else e===Xt?a.togglePreview():e===Jt?a.toggleSoundsEnabled():e===on?a.togglePlayerNames():e===ln&&B()}j=!!Ho.getFinishTs(e),W()&&(_.update(),vl=!0)},render:async()=>{if(!vl)return;const n=C();let l,s,i;window.DEBUG&&eo(0),O.fillStyle=Q(),O.fillRect(0,0,w.width,w.height),window.DEBUG&&to("clear done"),l=U.worldToViewportRaw(I),s=U.worldDimToViewportRaw(D),O.fillStyle="rgba(255, 255, 255, .3)",O.fillRect(l.x,l.y,s.w,s.h),window.DEBUG&&to("board done");const r=Ho.getPiecesSortedByZIndex(e);window.DEBUG&&to("get tiles done"),s=U.worldDimToViewportRaw(E);for(const e of r)(-1===e.owner?fl:wl)&&(i=M[e.idx],l=U.worldToViewportRaw({x:k+e.pos.x,y:k+e.pos.y}),O.drawImage(i,0,0,i.width,i.height,l.x,l.y,s.w,s.h));window.DEBUG&&to("tiles done");const d=[];for(const a of Ho.getActivePlayers(e,n))c=a,("replay"===o||c.id!==t)&&(i=await f(a),l=U.worldToViewport(a),O.drawImage(i,l.x-g,l.y-m),Y()&&d.push([`${a.name} (${a.points})`,l.x,l.y+h]));var c;O.fillStyle="white",O.textAlign="center";for(const[e,t,o]of d)O.fillText(e,t,o);window.DEBUG&&to("players done"),a.setActivePlayers(Ho.getActivePlayers(e,n)),a.setIdlePlayers(Ho.getIdlePlayers(e,n)),a.setPiecesDone(Ho.getFinishedPiecesCount(e)),window.DEBUG&&to("HUD done"),W()&&_.render(),vl=!1}}),{setHotkeys:e=>{R.setHotkeys(e)},onBgChange:e=>{gl(ol,e),R.addEvent([qt,e])},onColorChange:e=>{gl(ll,e),R.addEvent([Qt,e])},onNameChange:e=>{gl(al,e),R.addEvent([Zt,e])},onSoundsEnabledChange:e=>{ul(nl,e)},onSoundsVolumeChange:e=>{dl(tl,e),q()},onShowPlayerNamesChange:e=>{ul(sl,e)},replayOnSpeedUp:ae,replayOnSpeedDown:se,replayOnPauseToggle:ie,previewImageUrl:G,player:{background:Q(),color:Z(),name:"replay"===o?hl(al,"anon"):Ho.getPlayerName(e,t)||hl(al,"anon"),soundsEnabled:H(),soundsVolume:K(),showPlayerNames:Y()},disconnect:bn.disconnect,connect:x,unload:()=>{re.forEach((e=>{clearInterval(e)})),de&&clearTimeout(de),ce&&ce.stop()}}}var Cl=e({name:"game",components:{PuzzleStatus:xt,Scores:ft,SettingsOverlay:Pt,PreviewOverlay:_t,ConnectionOverlay:Cn,HelpOverlay:Tn},data:()=>({activePlayers:[],idlePlayers:[],finished:!1,duration:0,piecesDone:0,piecesTotal:0,overlay:"",connectionState:0,g:{player:{background:"",color:"",name:"",soundsEnabled:!1,soundsVolume:100,showPlayerNames:!0},previewImageUrl:"",setHotkeys:e=>{},onBgChange:e=>{},onColorChange:e=>{},onNameChange:e=>{},onSoundsEnabledChange:e=>{},onSoundsVolumeChange:e=>{},onShowPlayerNamesChange:e=>{},connect:()=>{},disconnect:()=>{},unload:()=>{}}}),async mounted(){this.$route.params.id&&(this.$watch((()=>this.g.player.background),(e=>{this.g.onBgChange(e)})),this.$watch((()=>this.g.player.color),(e=>{this.g.onColorChange(e)})),this.$watch((()=>this.g.player.name),(e=>{this.g.onNameChange(e)})),this.$watch((()=>this.g.player.soundsEnabled),(e=>{this.g.onSoundsEnabledChange(e)})),this.$watch((()=>this.g.player.soundsVolume),(e=>{this.g.onSoundsVolumeChange(e)})),this.$watch((()=>this.g.player.showPlayerNames),(e=>{this.g.onShowPlayerNamesChange(e)})),this.g=await bl(`${this.$route.params.id}`,this.$clientId,this.$config.WS_ADDRESS,"play",this.$el,{setActivePlayers:e=>{this.activePlayers=e},setIdlePlayers:e=>{this.idlePlayers=e},setFinished:e=>{this.finished=e},setDuration:e=>{this.duration=e},setPiecesDone:e=>{this.piecesDone=e},setPiecesTotal:e=>{this.piecesTotal=e},togglePreview:()=>{this.toggle("preview",!1)},setConnectionState:e=>{this.connectionState=e},toggleSoundsEnabled:()=>{this.g.player.soundsEnabled=!this.g.player.soundsEnabled},togglePlayerNames:()=>{this.g.player.showPlayerNames=!this.g.player.showPlayerNames}}))},unmounted(){this.g.unload(),this.g.disconnect()},methods:{reconnect(){this.g.connect()},toggle(e,t){""===this.overlay?(this.overlay=e,t&&this.g.setHotkeys(!1)):(this.overlay="",t&&this.g.setHotkeys(!0))}}});const xl={id:"game"},kl={class:"menu"},Pl={class:"tabs"},Al=i("🧩 Puzzles");Cl.render=function(e,l,i,r,d,c){const u=a("settings-overlay"),g=a("preview-overlay"),h=a("help-overlay"),m=a("connection-overlay"),y=a("puzzle-status"),f=a("router-link"),w=a("scores");return s(),t("div",xl,[p(n(u,{onBgclick:l[1]||(l[1]=t=>e.toggle("settings",!0)),modelValue:e.g.player,"onUpdate:modelValue":l[2]||(l[2]=t=>e.g.player=t)},null,8,["modelValue"]),[[x,"settings"===e.overlay]]),p(n(g,{onBgclick:l[3]||(l[3]=t=>e.toggle("preview",!1)),img:e.g.previewImageUrl},null,8,["img"]),[[x,"preview"===e.overlay]]),p(n(h,{onBgclick:l[4]||(l[4]=t=>e.toggle("help",!0))},null,512),[[x,"help"===e.overlay]]),n(m,{connectionState:e.connectionState,onReconnect:e.reconnect},null,8,["connectionState","onReconnect"]),n(y,{finished:e.finished,duration:e.duration,piecesDone:e.piecesDone,piecesTotal:e.piecesTotal},null,8,["finished","duration","piecesDone","piecesTotal"]),n("div",kl,[n("div",Pl,[n(f,{class:"opener",to:{name:"index"},target:"_blank"},{default:o((()=>[Al])),_:1}),n("div",{class:"opener",onClick:l[5]||(l[5]=t=>e.toggle("preview",!1))},"πŸ–ΌοΈ Preview"),n("div",{class:"opener",onClick:l[6]||(l[6]=t=>e.toggle("settings",!0))},"πŸ› οΈ Settings"),n("div",{class:"opener",onClick:l[7]||(l[7]=t=>e.toggle("help",!0))},"ℹ️ Hotkeys")])]),n(w,{activePlayers:e.activePlayers,idlePlayers:e.idlePlayers},null,8,["activePlayers","idlePlayers"])])};var Sl=e({name:"replay",components:{PuzzleStatus:xt,Scores:ft,SettingsOverlay:Pt,PreviewOverlay:_t,HelpOverlay:Tn},data:()=>({activePlayers:[],idlePlayers:[],finished:!1,duration:0,piecesDone:0,piecesTotal:0,overlay:"",connectionState:0,g:{player:{background:"",color:"",name:"",soundsEnabled:!1,soundsVolume:100,showPlayerNames:!0},previewImageUrl:"",setHotkeys:e=>{},onBgChange:e=>{},onColorChange:e=>{},onNameChange:e=>{},onSoundsEnabledChange:e=>{},onSoundsVolumeChange:e=>{},onShowPlayerNamesChange:e=>{},replayOnSpeedUp:()=>{},replayOnSpeedDown:()=>{},replayOnPauseToggle:()=>{},connect:()=>{},disconnect:()=>{},unload:()=>{}},replay:{speed:1,paused:!1}}),async mounted(){this.$route.params.id&&(this.$watch((()=>this.g.player.background),(e=>{this.g.onBgChange(e)})),this.$watch((()=>this.g.player.color),(e=>{this.g.onColorChange(e)})),this.$watch((()=>this.g.player.name),(e=>{this.g.onNameChange(e)})),this.$watch((()=>this.g.player.soundsEnabled),(e=>{this.g.onSoundsEnabledChange(e)})),this.$watch((()=>this.g.player.soundsVolume),(e=>{this.g.onSoundsVolumeChange(e)})),this.$watch((()=>this.g.player.showPlayerNames),(e=>{this.g.onShowPlayerNamesChange(e)})),this.g=await bl(`${this.$route.params.id}`,this.$clientId,this.$config.WS_ADDRESS,"replay",this.$el,{setActivePlayers:e=>{this.activePlayers=e},setIdlePlayers:e=>{this.idlePlayers=e},setFinished:e=>{this.finished=e},setDuration:e=>{this.duration=e},setPiecesDone:e=>{this.piecesDone=e},setPiecesTotal:e=>{this.piecesTotal=e},togglePreview:()=>{this.toggle("preview",!1)},setConnectionState:e=>{this.connectionState=e},setReplaySpeed:e=>{this.replay.speed=e},setReplayPaused:e=>{this.replay.paused=e},toggleSoundsEnabled:()=>{this.g.player.soundsEnabled=!this.g.player.soundsEnabled},togglePlayerNames:()=>{this.g.player.showPlayerNames=!this.g.player.showPlayerNames}}))},unmounted(){this.g.unload(),this.g.disconnect()},methods:{toggle(e,t){""===this.overlay?(this.overlay=e,t&&this.g.setHotkeys(!1)):(this.overlay="",t&&this.g.setHotkeys(!0))}},computed:{replayText(){return"Replay-Speed: "+this.replay.speed+"x"+(this.replay.paused?" Paused":"")}}});const Tl={id:"replay"},zl={class:"menu"},Il={class:"tabs"},Dl=i("🧩 Puzzles");Sl.render=function(e,l,i,d,c,u){const g=a("settings-overlay"),h=a("preview-overlay"),m=a("help-overlay"),y=a("puzzle-status"),f=a("router-link"),w=a("scores");return s(),t("div",Tl,[p(n(g,{onBgclick:l[1]||(l[1]=t=>e.toggle("settings",!0)),modelValue:e.g.player,"onUpdate:modelValue":l[2]||(l[2]=t=>e.g.player=t)},null,8,["modelValue"]),[[x,"settings"===e.overlay]]),p(n(h,{onBgclick:l[3]||(l[3]=t=>e.toggle("preview",!1)),img:e.g.previewImageUrl},null,8,["img"]),[[x,"preview"===e.overlay]]),p(n(m,{onBgclick:l[4]||(l[4]=t=>e.toggle("help",!0))},null,512),[[x,"help"===e.overlay]]),n(y,{finished:e.finished,duration:e.duration,piecesDone:e.piecesDone,piecesTotal:e.piecesTotal},{default:o((()=>[n("div",null,[n("div",null,r(e.replayText),1),n("button",{class:"btn",onClick:l[5]||(l[5]=t=>e.g.replayOnSpeedUp())},"⏫"),n("button",{class:"btn",onClick:l[6]||(l[6]=t=>e.g.replayOnSpeedDown())},"⏬"),n("button",{class:"btn",onClick:l[7]||(l[7]=t=>e.g.replayOnPauseToggle())},"⏸️")])])),_:1},8,["finished","duration","piecesDone","piecesTotal"]),n("div",zl,[n("div",Il,[n(f,{class:"opener",to:{name:"index"},target:"_blank"},{default:o((()=>[Dl])),_:1}),n("div",{class:"opener",onClick:l[8]||(l[8]=t=>e.toggle("preview",!1))},"πŸ–ΌοΈ Preview"),n("div",{class:"opener",onClick:l[9]||(l[9]=t=>e.toggle("settings",!0))},"πŸ› οΈ Settings"),n("div",{class:"opener",onClick:l[10]||(l[10]=t=>e.toggle("help",!0))},"ℹ️ Hotkeys")])]),n(w,{activePlayers:e.activePlayers,idlePlayers:e.idlePlayers},null,8,["activePlayers","idlePlayers"])])},(async()=>{const e=await fetch("/api/conf"),t=await e.json();const n=k({history:P(),routes:[{name:"index",path:"/",component:j},{name:"new-game",path:"/new-game",component:it},{name:"game",path:"/g/:id",component:Cl},{name:"replay",path:"/replay/:id",component:Sl}]});n.beforeEach(((e,t)=>{t.name&&document.documentElement.classList.remove(`view-${String(t.name)}`),document.documentElement.classList.add(`view-${String(e.name)}`)}));const o=A(S);o.config.globalProperties.$config=t,o.config.globalProperties.$clientId=function(){let e=localStorage.getItem("ID");return e||(e=X.uniqId(),localStorage.setItem("ID",e)),e}(),o.use(n),o.mount("#app")})(); diff --git a/build/public/index.html b/build/public/index.html index d0cec69..24f5542 100644 --- a/build/public/index.html +++ b/build/public/index.html @@ -4,7 +4,7 @@ 🧩 jigsaw.hyottoko.club - + diff --git a/build/server/main.js b/build/server/main.js index 4a39918..7e7a143 100644 --- a/build/server/main.js +++ b/build/server/main.js @@ -11,29 +11,6 @@ import sharp from 'sharp'; import v8 from 'v8'; import bsqlite from 'better-sqlite3'; -var PieceEdge; -(function (PieceEdge) { - PieceEdge[PieceEdge["Flat"] = 0] = "Flat"; - PieceEdge[PieceEdge["Out"] = 1] = "Out"; - PieceEdge[PieceEdge["In"] = -1] = "In"; -})(PieceEdge || (PieceEdge = {})); -var ScoreMode; -(function (ScoreMode) { - ScoreMode[ScoreMode["FINAL"] = 0] = "FINAL"; - ScoreMode[ScoreMode["ANY"] = 1] = "ANY"; -})(ScoreMode || (ScoreMode = {})); -var ShapeMode; -(function (ShapeMode) { - ShapeMode[ShapeMode["NORMAL"] = 0] = "NORMAL"; - ShapeMode[ShapeMode["ANY"] = 1] = "ANY"; - ShapeMode[ShapeMode["FLAT"] = 2] = "FLAT"; -})(ShapeMode || (ShapeMode = {})); -var SnapMode; -(function (SnapMode) { - SnapMode[SnapMode["NORMAL"] = 0] = "NORMAL"; - SnapMode[SnapMode["REAL"] = 1] = "REAL"; -})(SnapMode || (SnapMode = {})); - class Rng { constructor(seed) { this.rand_high = seed || 0xDEADC0DE; @@ -175,9 +152,9 @@ function encodeGame(data) { data.puzzle, data.players, data.evtInfos, - data.scoreMode || ScoreMode.FINAL, - data.shapeMode || ShapeMode.ANY, - data.snapMode || SnapMode.NORMAL, + data.scoreMode, + data.shapeMode, + data.snapMode, ]; } function decodeGame(data) { @@ -494,6 +471,47 @@ var Time = { durationStr, }; +var PieceEdge; +(function (PieceEdge) { + PieceEdge[PieceEdge["Flat"] = 0] = "Flat"; + PieceEdge[PieceEdge["Out"] = 1] = "Out"; + PieceEdge[PieceEdge["In"] = -1] = "In"; +})(PieceEdge || (PieceEdge = {})); +var ScoreMode; +(function (ScoreMode) { + ScoreMode[ScoreMode["FINAL"] = 0] = "FINAL"; + ScoreMode[ScoreMode["ANY"] = 1] = "ANY"; +})(ScoreMode || (ScoreMode = {})); +var ShapeMode; +(function (ShapeMode) { + ShapeMode[ShapeMode["NORMAL"] = 0] = "NORMAL"; + ShapeMode[ShapeMode["ANY"] = 1] = "ANY"; + ShapeMode[ShapeMode["FLAT"] = 2] = "FLAT"; +})(ShapeMode || (ShapeMode = {})); +var SnapMode; +(function (SnapMode) { + SnapMode[SnapMode["NORMAL"] = 0] = "NORMAL"; + SnapMode[SnapMode["REAL"] = 1] = "REAL"; +})(SnapMode || (SnapMode = {})); +const DefaultScoreMode = (v) => { + if (v === ScoreMode.FINAL || v === ScoreMode.ANY) { + return v; + } + return ScoreMode.FINAL; +}; +const DefaultShapeMode = (v) => { + if (v === ShapeMode.NORMAL || v === ShapeMode.ANY || v === ShapeMode.FLAT) { + return v; + } + return ShapeMode.NORMAL; +}; +const DefaultSnapMode = (v) => { + if (v === SnapMode.NORMAL || v === SnapMode.REAL) { + return v; + } + return SnapMode.NORMAL; +}; + const IDLE_TIMEOUT_SEC = 30; // Map const GAMES = {}; @@ -614,10 +632,10 @@ function setImageUrl(gameId, imageUrl) { GAMES[gameId].puzzle.info.imageUrl = imageUrl; } function getScoreMode(gameId) { - return GAMES[gameId].scoreMode || ScoreMode.FINAL; + return GAMES[gameId].scoreMode; } function getSnapMode(gameId) { - return GAMES[gameId].snapMode || SnapMode.NORMAL; + return GAMES[gameId].snapMode; } function isFinished(gameId) { return getFinishedPiecesCount(gameId) === getPieceCount(gameId); @@ -1323,10 +1341,16 @@ const get = (gameId, offset = 0) => { if (!fs.existsSync(file)) { return []; } - const log = fs.readFileSync(file, 'utf-8').split("\n"); - return log.filter(line => !!line).map(line => { + const lines = fs.readFileSync(file, 'utf-8').split("\n"); + const log = lines.filter(line => !!line).map(line => { return JSON.parse(`[${line}]`); }); + if (offset === 0 && log.length > 0) { + log[0][5] = DefaultScoreMode(log[0][5]); + log[0][6] = DefaultShapeMode(log[0][6]); + log[0][7] = DefaultSnapMode(log[0][7]); + } + return log; }; var GameLog = { shouldLog, @@ -1759,9 +1783,9 @@ function loadGame(gameId) { puzzle: game.puzzle, players: game.players, evtInfos: {}, - scoreMode: game.scoreMode || ScoreMode.FINAL, - shapeMode: game.shapeMode || ShapeMode.ANY, - snapMode: game.snapMode || SnapMode.NORMAL, + scoreMode: DefaultScoreMode(game.scoreMode), + shapeMode: DefaultShapeMode(game.shapeMode), + snapMode: DefaultSnapMode(game.snapMode), }; GameCommon.setGame(gameObject.id, gameObject); } @@ -2090,7 +2114,7 @@ app.get('/api/replay-data', async (req, res) => { let game = null; if (offset === 0) { // also need the game - game = await Game.createGameObject(gameId, log[0][2], log[0][3], log[0][4], log[0][5] || ScoreMode.FINAL, log[0][6] || ShapeMode.NORMAL, log[0][7] || SnapMode.NORMAL); + game = await Game.createGameObject(gameId, log[0][2], log[0][3], log[0][4], log[0][5], log[0][6], log[0][7]); } res.send({ log, game: game ? Util.encodeGame(game) : null }); }); diff --git a/src/common/GameCommon.ts b/src/common/GameCommon.ts index c2793aa..e2f18eb 100644 --- a/src/common/GameCommon.ts +++ b/src/common/GameCommon.ts @@ -170,11 +170,11 @@ function setImageUrl(gameId: string, imageUrl: string): void { } function getScoreMode(gameId: string): ScoreMode { - return GAMES[gameId].scoreMode || ScoreMode.FINAL + return GAMES[gameId].scoreMode } function getSnapMode(gameId: string): SnapMode { - return GAMES[gameId].snapMode || SnapMode.NORMAL + return GAMES[gameId].snapMode } function isFinished(gameId: string): boolean { diff --git a/src/common/Types.ts b/src/common/Types.ts index 90de5b3..0e8d21d 100644 --- a/src/common/Types.ts +++ b/src/common/Types.ts @@ -75,9 +75,9 @@ export interface Game { players: Array puzzle: Puzzle evtInfos: Record - scoreMode?: ScoreMode - shapeMode?: ShapeMode - snapMode?: SnapMode + scoreMode: ScoreMode + shapeMode: ShapeMode + snapMode: SnapMode rng: GameRng } @@ -216,3 +216,24 @@ export enum SnapMode { NORMAL = 0, REAL = 1, } + +export const DefaultScoreMode = (v: any): ScoreMode => { + if (v === ScoreMode.FINAL || v === ScoreMode.ANY) { + return v + } + return ScoreMode.FINAL +} + +export const DefaultShapeMode = (v: any): ShapeMode => { + if (v === ShapeMode.NORMAL || v === ShapeMode.ANY || v === ShapeMode.FLAT) { + return v + } + return ShapeMode.NORMAL +} + +export const DefaultSnapMode = (v: any): SnapMode => { + if (v === SnapMode.NORMAL || v === SnapMode.REAL) { + return v + } + return SnapMode.NORMAL +} diff --git a/src/common/Util.ts b/src/common/Util.ts index 4077834..dbf94b6 100644 --- a/src/common/Util.ts +++ b/src/common/Util.ts @@ -130,9 +130,9 @@ function encodeGame(data: Game): EncodedGame { data.puzzle, data.players, data.evtInfos, - data.scoreMode || ScoreMode.FINAL, - data.shapeMode || ShapeMode.ANY, - data.snapMode || SnapMode.NORMAL, + data.scoreMode, + data.shapeMode, + data.snapMode, ] } diff --git a/src/server/GameLog.ts b/src/server/GameLog.ts index 859610f..c6a14ad 100644 --- a/src/server/GameLog.ts +++ b/src/server/GameLog.ts @@ -1,7 +1,7 @@ import fs from 'fs' import Protocol from '../common/Protocol' import Time from '../common/Time' -import { Timestamp } from '../common/Types' +import { DefaultScoreMode, DefaultShapeMode, DefaultSnapMode, Timestamp } from '../common/Types' import { logger } from './../common/Util' import { DATA_DIR } from './../server/Dirs' @@ -82,10 +82,16 @@ const get = ( return [] } - const log = fs.readFileSync(file, 'utf-8').split("\n") - return log.filter(line => !!line).map(line => { + const lines = fs.readFileSync(file, 'utf-8').split("\n") + const log = lines.filter(line => !!line).map(line => { return JSON.parse(`[${line}]`) }) + if (offset === 0 && log.length > 0) { + log[0][5] = DefaultScoreMode(log[0][5]) + log[0][6] = DefaultShapeMode(log[0][6]) + log[0][7] = DefaultSnapMode(log[0][7]) + } + return log } export default { diff --git a/src/server/GameStorage.ts b/src/server/GameStorage.ts index b02c908..35d0023 100644 --- a/src/server/GameStorage.ts +++ b/src/server/GameStorage.ts @@ -1,6 +1,6 @@ import fs from 'fs' import GameCommon from './../common/GameCommon' -import { Game, Piece, ScoreMode, ShapeMode, SnapMode } from './../common/Types' +import { DefaultScoreMode, DefaultShapeMode, DefaultSnapMode, Game, Piece } from './../common/Types' import Util, { logger } from './../common/Util' import { Rng } from './../common/Rng' import { DATA_DIR } from './Dirs' @@ -58,9 +58,9 @@ function loadGame(gameId: string): void { puzzle: game.puzzle, players: game.players, evtInfos: {}, - scoreMode: game.scoreMode || ScoreMode.FINAL, - shapeMode: game.shapeMode || ShapeMode.ANY, - snapMode: game.snapMode || SnapMode.NORMAL, + scoreMode: DefaultScoreMode(game.scoreMode), + shapeMode: DefaultShapeMode(game.shapeMode), + snapMode: DefaultSnapMode(game.snapMode), } GameCommon.setGame(gameObject.id, gameObject) } diff --git a/src/server/main.ts b/src/server/main.ts index db403c5..119bf9a 100644 --- a/src/server/main.ts +++ b/src/server/main.ts @@ -89,9 +89,9 @@ app.get('/api/replay-data', async (req, res): Promise => { log[0][2], log[0][3], log[0][4], - log[0][5] || ScoreMode.FINAL, - log[0][6] || ShapeMode.NORMAL, - log[0][7] || SnapMode.NORMAL, + log[0][5], + log[0][6], + log[0][7], ) } res.send({ log, game: game ? Util.encodeGame(game) : null })