diff --git a/build/public/assets/index.3208e2e9.js b/build/public/assets/index.3208e2e9.js new file mode 100644 index 0000000..a1e26f5 --- /dev/null +++ b/build/public/assets/index.3208e2e9.js @@ -0,0 +1 @@ +import{d as e,c as t,a as n,w as l,b as o,r as i,o as a,e as s,t as r,F as d,f as c,g as u,h as g,v as p,i as h,j as m,k as y,l as f,m as w,n as v,p as b,q as x,s as C,u as k}from"./vendor.18cd2d7e.js";var A=e({name:"app",computed:{showNav(){return!["game","replay"].includes(String(this.$route.name))}}});const S={id:"app"},z={key:0,class:"nav"},P=s("Index"),I=s("New game");A.render=function(e,s,r,d,c,u){const g=i("router-link"),p=i("router-view");return a(),t("div",S,[e.showNav?(a(),t("ul",z,[n("li",null,[n(g,{class:"btn",to:{name:"index"}},{default:l((()=>[P])),_:1})]),n("li",null,[n(g,{class:"btn",to:{name:"new-game"}},{default:l((()=>[I])),_:1})])])):o("",!0),n(p)])};const T=864e5,_=e=>{const t=Math.floor(e/T);e%=T;const n=Math.floor(e/36e5);e%=36e5;const l=Math.floor(e/6e4);e%=6e4;return`${t}d ${n}h ${l}m ${Math.floor(e/1e3)}s`};var E=1e3,D=()=>{const e=new Date;return Date.UTC(e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate(),e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds(),e.getUTCMilliseconds())},M=(e,t)=>_(t-e),B=_,O=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?"🏁":"⏳",l=e,o=t||D();return`${n} ${M(l,o)}`}}});const N={class:"game-info-text"},U=n("br",null,null,-1),G=n("br",null,null,-1),$=n("br",null,null,-1),R=s(" β†ͺ️ Watch replay ");O.render=function(e,d,c,u,g,p){const h=i("router-link");return a(),t("div",{class:"game-teaser",style:e.style},[n(h,{class:"game-info",to:{name:"game",params:{id:e.game.id}}},{default:l((()=>[n("span",N,[s(" 🧩 "+r(e.game.tilesFinished)+"/"+r(e.game.tilesTotal),1),U,s(" πŸ‘₯ "+r(e.game.players),1),G,s(" "+r(e.time(e.game.started,e.game.finished)),1),$])])),_:1},8,["to"]),e.game.hasReplay?(a(),t(h,{key:0,class:"game-replay",to:{name:"replay",params:{id:e.game.id}}},{default:l((()=>[R])),_:1},8,["to"])):o("",!0)],4)};var V=e({components:{GameTeaser:O},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 F=n("h1",null,"Running games",-1),j=n("h1",null,"Finished games",-1);V.render=function(e,l,o,s,r,u){const g=i("game-teaser");return a(),t("div",null,[F,(a(!0),t(d,null,c(e.gamesRunning,((e,l)=>(a(),t("div",{class:"game-teaser-wrap",key:l},[n(g,{game:e},null,8,["game"])])))),128)),j,(a(!0),t(d,null,c(e.gamesFinished,((e,l)=>(a(),t("div",{class:"game-teaser-wrap",key:l},[n(g,{game:e},null,8,["game"])])))),128))])};var L=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")}}});L.render=function(e,l,o,i,s,r){return a(),t("div",{class:"imageteaser",style:e.style,onClick:l[2]||(l[2]=(...t)=>e.onClick&&e.onClick(...t))},[n("div",{class:"btn edit",onClick:l[1]||(l[1]=u(((...t)=>e.onEditClick&&e.onEditClick(...t)),["stop"]))},"✏️")],4)};var W=e({name:"image-library",components:{ImageTeaser:L},props:{images:{type:Array,required:!0}},emits:{imageClicked:null,imageEditClicked:null},methods:{imageClicked(e){this.$emit("imageClicked",e)},imageEditClicked(e){this.$emit("imageEditClicked",e)}}});W.render=function(e,n,l,o,s,r){const u=i("image-teaser");return a(),t("div",null,[(a(!0),t(d,null,c(e.images,((n,l)=>(a(),t(u,{image:n,onClick:t=>e.imageClicked(n),onEditClick:t=>e.imageEditClicked(n),key:l},null,8,["image","onClick","onEditClick"])))),128))])};const q={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}}}};q.render=function(e,n,l,o,i,s){return a(),t("div",{style:s.style,title:l.title},null,12,["title"])};var H=e({name:"tags-input",props:{modelValue:{type:Array,required:!0}},emits:{"update:modelValue":null},data:()=>({input:"",values:[]}),created(){this.values=this.modelValue},methods:{onKeyUp(e){if(","===e.key)return this.add(),e.stopPropagation(),!1},add(){const e=this.input.replace(/,/g,"").trim();e&&(this.values.includes(e)||this.values.push(e),this.input="",this.$emit("update:modelValue",this.values))},rm(e){this.values=this.values.filter((t=>t!==e)),this.$emit("update:modelValue",this.values)}}});const Y=m()(((e,l,o,i,s,u)=>(a(),t("div",null,[g(n("input",{class:"input",type:"text","onUpdate:modelValue":l[1]||(l[1]=t=>e.input=t),placeholder:"Plants, People",onKeydown:l[2]||(l[2]=h(((...t)=>e.add&&e.add(...t)),["enter"])),onKeyup:l[3]||(l[3]=(...t)=>e.onKeyUp&&e.onKeyUp(...t))},null,544),[[p,e.input]]),(a(!0),t(d,null,c(e.values,((n,l)=>(a(),t("span",{key:l,class:"bit",onClick:t=>e.rm(n)},r(n)+" βœ–",9,["onClick"])))),128))]))));H.render=Y,H.__scopeId="data-v-771460ae";var Q=e({name:"new-image-dialog",components:{ResponsiveImage:q,TagsInput:H},emits:{bgclick:null,setupGameClick:null,postToGalleryClick:null},data:()=>({previewUrl:"",file:null,title:"",tags:[]}),computed:{canPostToGallery(){return!(!this.previewUrl||!this.file)},canSetupGameClick(){return!(!this.previewUrl||!this.file)}},methods:{preview(e){const t=e.target;if(!t.files)return;const n=t.files[0];if(!n)return;const l=new FileReader;l.readAsDataURL(n),l.onload=e=>{this.previewUrl=e.target.result,this.file=n}},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})}}});const Z={key:0,class:"has-image"},K={key:1},J={class:"upload"},X=n("span",{class:"btn"},"Upload File",-1),ee={class:"area-settings"},te=n("td",null,[n("label",null,"Title")],-1),ne=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),le=n("td",null,[n("label",null,"Tags")],-1),oe={class:"area-buttons"},ie=s("🧩 Post to gallery "),ae=n("br",null,null,-1),se=s(" + set up game");Q.render=function(e,l,o,s,r,d){const c=i("responsive-image"),h=i("tags-input");return a(),t("div",{class:"overlay new-image-dialog",onClick:l[8]||(l[8]=t=>e.$emit("bgclick"))},[n("div",{class:"overlay-content",onClick:l[7]||(l[7]=u((()=>{}),["stop"]))},[n("div",{class:["area-image",{"has-image":!!e.previewUrl,"no-image":!e.previewUrl}]},[e.previewUrl?(a(),t("div",Z,[n("span",{class:"remove btn",onClick:l[1]||(l[1]=t=>e.previewUrl="")},"X"),n(c,{src:e.previewUrl},null,8,["src"])])):(a(),t("div",K,[n("label",J,[n("input",{type:"file",style:{display:"none"},onChange:l[2]||(l[2]=(...t)=>e.preview&&e.preview(...t)),accept:"image/*"},null,32),X])]))],2),n("div",ee,[n("table",null,[n("tr",null,[te,n("td",null,[g(n("input",{type:"text","onUpdate:modelValue":l[3]||(l[3]=t=>e.title=t),placeholder:"Flower by @artist"},null,512),[[p,e.title]])])]),ne,n("tr",null,[le,n("td",null,[n(h,{modelValue:e.tags,"onUpdate:modelValue":l[4]||(l[4]=t=>e.tags=t)},null,8,["modelValue"])])])])]),n("div",oe,[n("button",{class:"btn",disabled:!e.canPostToGallery,onClick:l[5]||(l[5]=(...t)=>e.postToGallery&&e.postToGallery(...t))},"πŸ–ΌοΈ Post to gallery",8,["disabled"]),n("button",{class:"btn",disabled:!e.canSetupGameClick,onClick:l[6]||(l[6]=(...t)=>e.setupGameClick&&e.setupGameClick(...t))},[ie,ae,se],8,["disabled"])])])])};var re=e({name:"edit-image-dialog",components:{ResponsiveImage:q,TagsInput:H},props:{image:{type:Object,required:!0}},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 de={class:"area-image"},ce={class:"has-image"},ue={class:"area-settings"},ge=n("td",null,[n("label",null,"Title")],-1),pe=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"};var ye,fe,we,ve,be,xe;re.render=function(e,l,o,s,r,d){const c=i("responsive-image"),h=i("tags-input");return a(),t("div",{class:"overlay edit-image-dialog",onClick:l[5]||(l[5]=t=>e.$emit("bgclick"))},[n("div",{class:"overlay-content",onClick:l[4]||(l[4]=u((()=>{}),["stop"]))},[n("div",de,[n("div",ce,[n(c,{src:e.image.url,title:e.image.title},null,8,["src","title"])])]),n("div",ue,[n("table",null,[n("tr",null,[ge,n("td",null,[g(n("input",{type:"text","onUpdate:modelValue":l[1]||(l[1]=t=>e.title=t),placeholder:"Flower by @artist"},null,512),[[p,e.title]])])]),pe,n("tr",null,[he,n("td",null,[n(h,{modelValue:e.tags,"onUpdate:modelValue":l[2]||(l[2]=t=>e.tags=t)},null,8,["modelValue"])])])])]),n("div",me,[n("button",{class:"btn",onClick:l[3]||(l[3]=(...t)=>e.saveImage&&e.saveImage(...t))},"πŸ–ΌοΈ Save image")])])])},(fe=ye||(ye={}))[fe.Flat=0]="Flat",fe[fe.Out=1]="Out",fe[fe.In=-1]="In",(ve=we||(we={}))[ve.FINAL=0]="FINAL",ve[ve.ANY=1]="ANY",(xe=be||(be={}))[xe.NORMAL=0]="NORMAL",xe[xe.ANY=1]="ANY",xe[xe.FLAT=2]="FLAT";var Ce=e({name:"new-game-dialog",components:{ResponsiveImage:q},props:{image:{type:Object,required:!0}},emits:{newGame:null,bgclick:null},data:()=>({tiles:1e3,scoreMode:we.ANY,shapeMode:be.NORMAL}),methods:{onNewGameClick(){this.$emit("newGame",{tiles:this.tilesInt,image:this.image,scoreMode:this.scoreModeInt,shapeMode:this.shapeModeInt})}},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)},tilesInt(){return parseInt(`${this.tiles}`,10)}}});const ke={class:"area-image"},Ae={class:"has-image"},Se={class:"area-settings"},ze=n("td",null,[n("label",null,"Pieces")],-1),Pe=n("td",null,[n("label",null,"Scoring: ")],-1),Ie=s(" Any (Score when pieces are connected to each other or on final location)"),Te=n("br",null,null,-1),_e=s(" Final (Score when pieces are put to their final location)"),Ee=n("td",null,[n("label",null,"shapes: ")],-1),De=s(" Normal"),Me=n("br",null,null,-1),Be=s(" Any (flat pieces can occur anywhere)"),Oe=n("br",null,null,-1),Ne=s(" Flat (all pieces flat on all sides)"),Ue={class:"area-buttons"};Ce.render=function(e,l,o,s,r,d){const c=i("responsive-image");return a(),t("div",{class:"overlay new-game-dialog",onClick:l[9]||(l[9]=t=>e.$emit("bgclick"))},[n("div",{class:"overlay-content",onClick:l[8]||(l[8]=u((()=>{}),["stop"]))},[n("div",ke,[n("div",Ae,[n(c,{src:e.image.url,title:e.image.title},null,8,["src","title"])])]),n("div",Se,[n("table",null,[n("tr",null,[ze,n("td",null,[g(n("input",{type:"text","onUpdate:modelValue":l[1]||(l[1]=t=>e.tiles=t)},null,512),[[p,e.tiles]])])]),n("tr",null,[Pe,n("td",null,[n("label",null,[g(n("input",{type:"radio","onUpdate:modelValue":l[2]||(l[2]=t=>e.scoreMode=t),value:"1"},null,512),[[y,e.scoreMode]]),Ie]),Te,n("label",null,[g(n("input",{type:"radio","onUpdate:modelValue":l[3]||(l[3]=t=>e.scoreMode=t),value:"0"},null,512),[[y,e.scoreMode]]),_e])])]),n("tr",null,[Ee,n("td",null,[n("label",null,[g(n("input",{type:"radio","onUpdate:modelValue":l[4]||(l[4]=t=>e.shapeMode=t),value:"0"},null,512),[[y,e.shapeMode]]),De]),Me,n("label",null,[g(n("input",{type:"radio","onUpdate:modelValue":l[5]||(l[5]=t=>e.shapeMode=t),value:"1"},null,512),[[y,e.shapeMode]]),Be]),Oe,n("label",null,[g(n("input",{type:"radio","onUpdate:modelValue":l[6]||(l[6]=t=>e.shapeMode=t),value:"2"},null,512),[[y,e.shapeMode]]),Ne])])])])]),n("div",Ue,[n("button",{class:"btn",disabled:!e.canStartNewGame,onClick:l[7]||(l[7]=(...t)=>e.onNewGameClick&&e.onNewGameClick(...t))}," 🧩 Generate Puzzle ",8,["disabled"])])])])};class Ge{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),l=t[n];t[n]=t[e],t[e]=l}return t}static serialize(e){return{rand_high:e.rand_high,rand_low:e.rand_low}}static unserialize(e){const t=new Ge(0);return t.rand_high=e.rand_high,t.rand_low=e.rand_low,t}}const $e=(e,t)=>{const n=`${e}`;return n.length>=t.length?n:t.substr(0,t.length-n.length)+n},Re=(...e)=>{const t=t=>(...n)=>{const l=new Date,o=$e(l.getHours(),"00"),i=$e(l.getMinutes(),"00"),a=$e(l.getSeconds(),"00");console[t](`${o}:${i}:${a}`,...e,...n)};return{log:t("log"),error:t("error"),info:t("info")}};var Ve={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||"",Ge.serialize(e.rng.obj),e.puzzle,e.players,e.evtInfos,e.scoreMode||we.FINAL]},decodeGame:function(e){return{id:e[0],rng:{type:e[1],obj:Ge.unserialize(e[2])},puzzle:e[3],players:e[4],evtInfos:e[5],scoreMode:e[6]}},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 l=[n,e[n]].map(encodeURIComponent);t.push(l.join("="))}return 0===t.length?"":`?${t.join("&")}`}},Fe=e({components:{ImageLibrary:W,NewImageDialog:Q,EditImageDialog:re,NewGameDialog:Ce},data:()=>({filters:{sort:"date_desc",tags:[]},images:[],tags:[],image:{id:0,filename:"",file:"",url:"",title:"",tags:[],created:0},dialog:""}),async created(){await this.loadImages()},methods:{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${Ve.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){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 fetch("/api/upload",{method:"post",body:t});return 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){await this.uploadImage(e),this.dialog="",await this.loadImages()},async setupGameClick(e){const t=await this.uploadImage(e);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 je={class:"upload-image-teaser"},Le=n("div",{class:"hint"},"(The image you upload will be added to the public gallery.)",-1),We={key:0},qe=s(" Tags: "),He=s(" Sort by: "),Ye=n("option",{value:"date_desc"},"Newest first",-1),Qe=n("option",{value:"date_asc"},"Oldest first",-1),Ze=n("option",{value:"alpha_asc"},"A-Z",-1),Ke=n("option",{value:"alpha_desc"},"Z-A",-1);Fe.render=function(e,l,s,u,p,h){const m=i("image-library"),y=i("new-image-dialog"),w=i("edit-image-dialog"),v=i("new-game-dialog");return a(),t("div",null,[n("div",je,[n("div",{class:"btn btn-big",onClick:l[1]||(l[1]=t=>e.dialog="new-image")},"Upload your image"),Le]),n("div",null,[e.tags.length>0?(a(),t("label",We,[qe,(a(!0),t(d,null,c(e.tags,((n,l)=>(a(),t("span",{class:["bit",{on:e.filters.tags.includes(n.slug)}],key:l,onClick:t=>e.toggleTag(n)},r(n.title),11,["onClick"])))),128))])):o("",!0),n("label",null,[He,g(n("select",{"onUpdate:modelValue":l[2]||(l[2]=t=>e.filters.sort=t),onChange:l[3]||(l[3]=(...t)=>e.filtersChanged&&e.filtersChanged(...t))},[Ye,Qe,Ze,Ke],544),[[f,e.filters.sort]])])]),n(m,{images:e.images,onImageClicked:e.onImageClicked,onImageEditClicked:e.onImageEditClicked},null,8,["images","onImageClicked","onImageEditClicked"]),"new-image"===e.dialog?(a(),t(y,{key:0,onBgclick:l[4]||(l[4]=t=>e.dialog=""),onPostToGalleryClick:e.postToGalleryClick,onSetupGameClick:e.setupGameClick},null,8,["onPostToGalleryClick","onSetupGameClick"])):o("",!0),"edit-image"===e.dialog?(a(),t(w,{key:1,onBgclick:l[5]||(l[5]=t=>e.dialog=""),onSaveClick:e.onSaveImageClick,image:e.image},null,8,["onSaveClick","image"])):o("",!0),e.image&&"new-game"===e.dialog?(a(),t(v,{key:2,onBgclick:l[6]||(l[6]=t=>e.dialog=""),onNewGame:e.onNewGame,image:e.image},null,8,["onNewGame","image"])):o("",!0)])};var Je=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 Xe={class:"scores"},et=n("div",null,"Scores",-1),tt=n("td",null,"⚑",-1),nt=n("td",null,"πŸ’€",-1);Je.render=function(e,l,o,i,s,u){return a(),t("div",Xe,[et,n("table",null,[(a(!0),t(d,null,c(e.actives,((e,l)=>(a(),t("tr",{key:l,style:{color:e.color}},[tt,n("td",null,r(e.name),1),n("td",null,r(e.points),1)],4)))),128)),(a(!0),t(d,null,c(e.idles,((e,l)=>(a(),t("tr",{key:l,style:{color:e.color}},[nt,n("td",null,r(e.name),1),n("td",null,r(e.points),1)],4)))),128))])])};var lt=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 B(this.duration)}}});const ot={class:"timer"};lt.render=function(e,l,o,i,s,d){return a(),t("div",ot,[n("div",null," 🧩 "+r(e.piecesDone)+"/"+r(e.piecesTotal),1),n("div",null,r(e.icon)+" "+r(e.durationStr),1),w(e.$slots,"default")])};var it=e({name:"settings-overlay",emits:{bgclick:null,"update:modelValue":null},props:{modelValue:Object},created(){this.$watch("modelValue",(e=>{this.$emit("update:modelValue",e)}),{deep:!0})}});const at=n("td",null,[n("label",null,"Background: ")],-1),st=n("td",null,[n("label",null,"Color: ")],-1),rt=n("td",null,[n("label",null,"Name: ")],-1),dt=n("td",null,[n("label",null,"Sounds: ")],-1);it.render=function(e,l,o,i,s,r){return a(),t("div",{class:"overlay transparent",onClick:l[6]||(l[6]=t=>e.$emit("bgclick"))},[n("table",{class:"overlay-content settings",onClick:l[5]||(l[5]=u((()=>{}),["stop"]))},[n("tr",null,[at,n("td",null,[g(n("input",{type:"color","onUpdate:modelValue":l[1]||(l[1]=t=>e.modelValue.background=t)},null,512),[[p,e.modelValue.background]])])]),n("tr",null,[st,n("td",null,[g(n("input",{type:"color","onUpdate:modelValue":l[2]||(l[2]=t=>e.modelValue.color=t)},null,512),[[p,e.modelValue.color]])])]),n("tr",null,[rt,n("td",null,[g(n("input",{type:"text",maxLength:"16","onUpdate:modelValue":l[3]||(l[3]=t=>e.modelValue.name=t)},null,512),[[p,e.modelValue.name]])])]),n("tr",null,[dt,n("td",null,[g(n("input",{type:"checkbox","onUpdate:modelValue":l[4]||(l[4]=t=>e.modelValue.soundsEnabled=t)},null,512),[[v,e.modelValue.soundsEnabled]])])])])])};var ct=e({name:"preview-overlay",props:{img:String},emits:{bgclick:null},computed:{previewStyle(){return{backgroundImage:`url('${this.img}')`}}}});const ut={class:"preview"};ct.render=function(e,l,o,i,s,r){return a(),t("div",{class:"overlay",onClick:l[1]||(l[1]=t=>e.$emit("bgclick"))},[n("div",ut,[n("div",{class:"img",style:e.previewStyle},null,4)])])};var gt=1,pt=4,ht=2,mt=3,yt=2,ft=4,wt=3,vt=9,bt=1,xt=2,Ct=3,kt=4,At=5,St=6,zt=7,Pt=8,It=10,Tt=11,_t=1,Et=2,Dt=3;const Mt=Re("Communication.js");let Bt,Ot=[],Nt=e=>{Ot.push(e)},Ut=[],Gt=e=>{Ut.push(e)};let $t=0;const Rt=e=>{$t!==e&&($t=e,Gt(e))};function Vt(e){if(2===$t)try{Bt.send(JSON.stringify(e))}catch(t){Mt.info("unable to send message.. maybe because ws is invalid?")}}let Ft,jt;var Lt={connect:function(e,t,n){return Ft=0,jt={},Rt(3),new Promise((l=>{Bt=new WebSocket(e,n+"|"+t),Bt.onopen=()=>{Rt(2),Vt([mt])},Bt.onmessage=e=>{const t=JSON.parse(e.data),o=t[0];if(o===pt){const e=t[1];l(e)}else{if(o!==gt)throw`[ 2021-05-09 invalid connect msgType ${o} ]`;{const e=t[1],l=t[2];if(e===n&&jt[l])return void delete jt[l];Nt(t)}}},Bt.onerror=()=>{throw Rt(1),"[ 2021-05-15 onerror ]"},Bt.onclose=e=>{4e3===e.code||1001===e.code?Rt(4):Rt(1)}}))},requestReplayData:async function(e,t,n){const l={gameId:e,offset:t,size:n},o=await fetch(`/api/replay-data${Ve.asQueryArgs(l)}`);return await o.json()},disconnect:function(){Bt&&Bt.close(4e3),Ft=0,jt={}},sendClientEvent:function(e){Ft++,jt[Ft]=e,Vt([ht,Ft,jt[Ft]])},onServerChange:function(e){Nt=e;for(const t of Ot)Nt(t);Ot=[]},onConnectionStateChange:function(e){Gt=e;for(const t of Ut)Gt(t);Ut=[]},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},Wt=e({name:"connection-overlay",emits:{reconnect:null},props:{connectionState:Number},computed:{lostConnection(){return this.connectionState===Lt.CONN_STATE_DISCONNECTED},connecting(){return this.connectionState===Lt.CONN_STATE_CONNECTING},show(){return!(!this.lostConnection&&!this.connecting)}}});const qt={key:0,class:"overlay connection-lost"},Ht={key:0,class:"overlay-content"},Yt=n("div",null,"⁉️ LOST CONNECTION ⁉️",-1),Qt={key:1,class:"overlay-content"},Zt=n("div",null,"Connecting...",-1);Wt.render=function(e,l,i,s,r,d){return e.show?(a(),t("div",qt,[e.lostConnection?(a(),t("div",Ht,[Yt,n("span",{class:"btn",onClick:l[1]||(l[1]=t=>e.$emit("reconnect"))},"Reconnect")])):o("",!0),e.connecting?(a(),t("div",Qt,[Zt])):o("",!0)])):o("",!0)};var Kt=e({name:"help-overlay",emits:{bgclick:null}});const Jt=n("tr",null,[n("td",null,"⬆️ Move up:"),n("td",null,[n("div",null,[n("kbd",null,"W"),s("/"),n("kbd",null,"↑"),s("/πŸ–±οΈ")])])],-1),Xt=n("tr",null,[n("td",null,"⬇️ Move down:"),n("td",null,[n("div",null,[n("kbd",null,"S"),s("/"),n("kbd",null,"↓"),s("/πŸ–±οΈ")])])],-1),en=n("tr",null,[n("td",null,"⬅️ Move left:"),n("td",null,[n("div",null,[n("kbd",null,"A"),s("/"),n("kbd",null,"←"),s("/πŸ–±οΈ")])])],-1),tn=n("tr",null,[n("td",null,"➑️ Move right:"),n("td",null,[n("div",null,[n("kbd",null,"D"),s("/"),n("kbd",null,"β†’"),s("/πŸ–±οΈ")])])],-1),nn=n("tr",null,[n("td"),n("td",null,[n("div",null,[s("Move faster by holding "),n("kbd",null,"Shift")])])],-1),ln=n("tr",null,[n("td",null,"πŸ”+ Zoom in:"),n("td",null,[n("div",null,[n("kbd",null,"E"),s("/πŸ–±οΈ-Wheel")])])],-1),on=n("tr",null,[n("td",null,"πŸ”- Zoom out:"),n("td",null,[n("div",null,[n("kbd",null,"Q"),s("/πŸ–±οΈ-Wheel")])])],-1),an=n("tr",null,[n("td",null,"πŸ–ΌοΈ Toggle preview:"),n("td",null,[n("div",null,[n("kbd",null,"Space")])])],-1),sn=n("tr",null,[n("td",null,"πŸ§©βœ”οΈ Toggle fixed pieces:"),n("td",null,[n("div",null,[n("kbd",null,"F")])])],-1),rn=n("tr",null,[n("td",null,"πŸ§©β“ Toggle loose pieces:"),n("td",null,[n("div",null,[n("kbd",null,"G")])])],-1),dn=n("tr",null,[n("td",null,"πŸ”‰ Toggle sounds:"),n("td",null,[n("div",null,[n("kbd",null,"M")])])],-1);Kt.render=function(e,l,o,i,s,r){return a(),t("div",{class:"overlay transparent",onClick:l[2]||(l[2]=t=>e.$emit("bgclick"))},[n("table",{class:"overlay-content help",onClick:l[1]||(l[1]=u((()=>{}),["stop"]))},[Jt,Xt,en,tn,nn,ln,on,an,sn,rn,dn])])};var cn=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"/assets/click.550555f3.mp3"}),un=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAW0lEQVQ4je1RywrAIAxLxP//5exixRWlVgZelpOKeTQFfnDypgy3eLIkSLLL8mxGPoHsU2hPAgDHBLvRX6hZZw/fwT0BtlLSONqCbWAmEIqMZOCDDlaDR3N03gOyDCn+y4DWmAAAAABJRU5ErkJggg=="}),gn=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAARElEQVQ4jWNgGAU0Af+hmBCbgYGBgYERhwHEAEYGBgYGJtIdiApYyLAZBVDsAqoagC1ACQJyY4ERg0GCISh6KA4DigEAou8LC+LnIJoAAAAASUVORK5CYII="}),pn=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAcUlEQVQ4ja1TQQ7AIAgD///n7jCozA2Hbk00jbG1KIrcARszTugoBs49qioZj7r2kKACptkyAOCJsJuA+GzglwHjvMSSWFVaENWVASxh5eRLiq5fN/ASjI89VcP2K3hHpq1cEXNaMfnrL3TDZP2tDuoOA6MzCCXWr38AAAAASUVORK5CYII="}),hn=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAU0lEQVQ4jWNgoAH4D8X42HDARKlt5BoAd82AuQAOGLGIYQQUPv0wF5CiCQUge4EsQ5C9QI4BjMguwBYeBAElscCIy1ZivMKIwSDBEBQ9FCckigEAU3QOD7TGvY4AAAAASUVORK5CYII="});function mn(){let e=0,t=0,n=1;const l=(l,o)=>{e+=l/n,t+=o/n},o=e=>{const t=n+.05*n*("in"===e?1:-1);return Math.min(Math.max(t,.1),6)},i=l=>({x:l.x/n-e,y:l.y/n-t}),a=l=>({x:(l.x+e)*n,y:(l.y+t)*n}),s=e=>({w:e.w*n,h:e.h*n}),r=e=>({w:e.w/n,h:e.h/n});return{getCurrentZoom:()=>n,move:l,canZoom:e=>n!=o(e),zoom:(e,t)=>((e,t)=>{if(n==e)return!1;const o=1-n/e;return l(-t.x*o,-t.y*o),n=e,!0})(o(e),t),worldToViewport:e=>{const{x:t,y:n}=a(e);return{x:Math.round(t),y:Math.round(n)}},worldToViewportRaw:a,worldDimToViewport:e=>{const{w:t,h:n}=s(e);return{w:Math.round(t),h:Math.round(n)}},worldDimToViewportRaw:s,viewportToWorld:e=>{const{x:t,y:n}=i(e);return{x:Math.round(t),y:Math.round(n)}},viewportToWorldRaw:i,viewportDimToWorld:e=>{const{w:t,h:n}=r(e);return{w:Math.round(t),h:Math.round(n)}},viewportDimToWorldRaw:r}}function yn(e=0,t=0){const n=document.createElement("canvas");return n.width=e,n.height=t,n}var fn={createCanvas:yn,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 l=yn(t,n);return l.getContext("2d").drawImage(e,0,0,e.width,e.height,0,0,t,n),await createImageBitmap(l)},colorizedCanvas:function(e,t,n){const l=yn(e.width,e.height),o=l.getContext("2d");return o.save(),o.drawImage(t,0,0),o.fillStyle=n,o.globalCompositeOperation="source-in",o.fillRect(0,0,t.width,t.height),o.restore(),o.save(),o.globalCompositeOperation="destination-over",o.drawImage(e,0,0),o.restore(),l}};const wn=Re("Debug.js");let vn=0,bn=0;var xn=e=>{vn=performance.now(),bn=e},Cn=e=>{const t=performance.now(),n=t-vn;n>bn&&wn.log(e+": "+n),vn=t};function kn(e,t){const n=e.x-t.x,l=e.y-t.y;return Math.sqrt(n*n+l*l)}function An(e){return{x:e.x+e.w/2,y:e.y+e.h/2}}var Sn={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:kn,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:An,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 kn(An(e),An(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 zn=Re("PuzzleGraphics.js");function Pn(e,t){const n=Ve.coordByPieceIdx(e,t);return{x:n.x*e.tileSize,y:n.y*e.tileSize,w:e.tileSize,h:e.tileSize}}var In={loadPuzzleBitmaps:async function(e){const t=await fn.loadImageToBitmap(e.info.imageUrl),n=await fn.resizeBitmap(t,e.info.width,e.info.height);return await async function(e,t,n){zn.log("start createPuzzleTileBitmaps");const l=n.tileSize,o=n.tileMarginWidth,i=n.tileDrawSize,a=l/100,s=[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,i={x:o,y:o},r=Sn.pointAdd(i,{x:l,y:0}),c=Sn.pointAdd(r,{x:0,y:l}),u=Sn.pointSub(c,{x:l,y:0});if(n.moveTo(i.x,i.y),0!==e.top)for(let l=0;lVe.decodePiece(Tn[e].puzzle.tiles[t]),jn=(e,t)=>Fn(e,t).group,Ln=(e,t)=>{const n=Tn[e].puzzle.info,l={x:(n.table.width-n.width)/2,y:(n.table.height-n.height)/2},o=function(e,t){const n=Tn[e].puzzle.info,l=Ve.coordByPieceIdx(n,t),o=l.x*n.tileSize,i=l.y*n.tileSize;return{x:o,y:i}}(e,t);return Sn.pointAdd(l,o)},Wn=(e,t)=>Fn(e,t).pos,qn=e=>{const t=sl(e),n=rl(e),l=Math.round(t/4),o=Math.round(n/4);return{x:0-l,y:0-o,w:t+2*l,h:n+2*o}},Hn=(e,t)=>{const n=Kn(e),l=Fn(e,t);return{x:l.pos.x,y:l.pos.y,w:n,h:n}},Yn=(e,t)=>Fn(e,t).z,Qn=(e,t)=>{for(const n of Tn[e].puzzle.tiles){const e=Ve.decodePiece(n);if(e.owner===t)return e.idx}return-1},Zn=e=>Tn[e].puzzle.info.tileDrawSize,Kn=e=>Tn[e].puzzle.info.tileSize,Jn=e=>Tn[e].puzzle.data.maxGroup,Xn=e=>Tn[e].puzzle.data.maxZ;function el(e,t){const n=Tn[e].puzzle.info,l=Ve.coordByPieceIdx(n,t);return[l.y>0?t-n.tilesX:-1,l.x0?t-1:-1]}const tl=(e,t,n)=>{for(const l of t)Vn(e,l,{z:n})},nl=(e,t,n)=>{const l=Wn(e,t);Vn(e,t,{pos:Sn.pointAdd(l,n)})},ll=(e,t,n)=>{const l=Zn(e),o=qn(e),i=n;for(const a of t){const t=Fn(e,a);t.pos.x+n.xo.x+o.w&&(i.x=Math.min(o.x+o.w-t.pos.x+l,i.x)),t.pos.y+n.yo.y+o.h&&(i.y=Math.min(o.y+o.h-t.pos.y+l,i.y))}for(const a of t)nl(e,a,i)},ol=(e,t,n)=>{for(const l of t)Vn(e,l,{owner:n})};function il(e,t){const n=Tn[e].puzzle.tiles,l=Ve.decodePiece(n[t]),o=[];if(l.group)for(const i of n){const e=Ve.decodePiece(i);e.group===l.group&&o.push(e.idx)}else o.push(l.idx);return o}const al=(e,t)=>{const n=En(e,t);return n?n.points:0},sl=e=>Tn[e].puzzle.info.table.width,rl=e=>Tn[e].puzzle.info.table.height;var dl={setGame:function(e,t){Tn[e]=t},exists:function(e){return!!Tn[e]||!1},playerExists:Mn,getActivePlayers:function(e,t){const n=t-30*E;return Bn(e).filter((e=>e.ts>=n))},getIdlePlayers:function(e,t){const n=t-30*E;return Bn(e).filter((e=>e.ts0))},addPlayer:function(e,t,n){Mn(e,t)?$n(e,t,{ts:n}):Dn(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:Gn,getPieceCount:On,getImageUrl:function(e){return Tn[e].puzzle.info.imageUrl},setImageUrl:function(e,t){Tn[e].puzzle.info.imageUrl=t},get:function(e){return Tn[e]||null},getAllGames:function(){return Object.values(Tn).sort(((e,t)=>Un(e.id)===Un(t.id)?t.puzzle.data.started-e.puzzle.data.started:Un(e.id)?1:-1))},getPlayerBgColor:(e,t)=>{const n=En(e,t);return n?n.bgcolor:null},getPlayerColor:(e,t)=>{const n=En(e,t);return n?n.color:null},getPlayerName:(e,t)=>{const n=En(e,t);return n?n.name:null},getPlayerIndexById:_n,getPlayerIdByIndex:function(e,t){return Tn[e].players.length>t?Ve.decodePlayer(Tn[e].players[t]).id:null},changePlayer:$n,setPlayer:Dn,setPiece:function(e,t,n){Tn[e].puzzle.tiles[t]=Ve.encodePiece(n)},setPuzzleData:function(e,t){Tn[e].puzzle.data=t},getTableWidth:sl,getTableHeight:rl,getPuzzle:e=>Tn[e].puzzle,getRng:e=>Tn[e].rng.obj,getPuzzleWidth:e=>Tn[e].puzzle.info.width,getPuzzleHeight:e=>Tn[e].puzzle.info.height,getPiecesSortedByZIndex:function(e){return Tn[e].puzzle.tiles.map(Ve.decodePiece).sort(((e,t)=>e.z-t.z))},getFirstOwnedPiece:(e,t)=>{const n=Qn(e,t);return n<0?null:Tn[e].puzzle.tiles[n]},getPieceDrawOffset:e=>Tn[e].puzzle.info.tileDrawOffset,getPieceDrawSize:Zn,getFinalPiecePos:Ln,getStartTs:e=>Tn[e].puzzle.data.started,getFinishTs:e=>Tn[e].puzzle.data.finished,handleInput:function(e,t,n,l,o){const i=Tn[e].puzzle,a=function(e,t){return t in Tn[e].evtInfos?Tn[e].evtInfos[t]:{_last_mouse:null,_last_mouse_down:null}}(e,t),s=[],r=()=>{s.push([_t,i.data])},d=t=>{s.push([Et,Ve.encodePiece(Fn(e,t))])},c=e=>{for(const t of e)d(t)},u=()=>{const n=En(e,t);n&&s.push([Dt,Ve.encodePlayer(n)])},g=n[0];if(g===St){const o=n[1];$n(e,t,{bgcolor:o,ts:l}),u()}else if(g===zt){const o=n[1];$n(e,t,{color:o,ts:l}),u()}else if(g===Pt){const o=`${n[1]}`.substr(0,16);$n(e,t,{name:o,ts:l}),u()}else if(g===vt){const o=n[1],i=n[2],a=En(e,t);if(a){const n=a.x-o,s=a.y-i;$n(e,t,{ts:l,x:n,y:s}),u()}}else if(g===bt){const o={x:n[1],y:n[2]};$n(e,t,{d:1,ts:l}),u(),a._last_mouse_down=o;const i=((e,t)=>{const n=Tn[e].puzzle.info,l=Tn[e].puzzle.tiles;let o=-1,i=-1;for(let a=0;ao)&&(o=e.z,i=a)}return i})(e,o);if(i>=0){const n=Xn(e)+1;Rn(e,{maxZ:n}),r();const l=il(e,i);tl(e,l,Xn(e)),ol(e,l,t),c(l)}a._last_mouse=o}else if(g===Ct){const o=n[1],i=n[2],s={x:o,y:i};if(null===a._last_mouse_down)$n(e,t,{x:o,y:i,ts:l}),u();else{const n=Qn(e,t);if(n>=0){$n(e,t,{x:o,y:i,ts:l}),u();const r=il(e,n);let d=Sn.pointInBounds(s,qn(e))&&Sn.pointInBounds(a._last_mouse_down,qn(e));for(const t of r){const n=Hn(e,t);if(Sn.pointInBounds(s,n)){d=!0;break}}if(d){const t=o-a._last_mouse_down.x,n=i-a._last_mouse_down.y;ll(e,r,{x:t,y:n}),c(r)}}else $n(e,t,{ts:l}),u();a._last_mouse_down=s}a._last_mouse=s}else if(g===xt){const s={x:n[1],y:n[2]},g=0;a._last_mouse_down=null;const p=Qn(e,t);if(p>=0){const n=il(e,p);ol(e,n,0),c(n);const a=Wn(e,p),s=Ln(e,p);if(Sn.pointDistance(s,a){for(const n of t)Vn(e,n,{owner:-1,z:1})})(e,n),c(n);let d=al(e,t);Nn(e)===we.FINAL?d+=n.length:Nn(e)===we.ANY&&(d+=1),$n(e,t,{d:g,ts:l,points:d}),u(),Gn(e)===On(e)&&(Rn(e,{finished:l}),r()),o&&o(t)}else{const n=(e,t,n,l)=>{const o=Tn[e].puzzle.info;if(n<0)return!1;if(((e,t,n)=>{const l=jn(e,t),o=jn(e,n);return!(!l||l!==o)})(e,t,n))return!1;const i=Wn(e,t),a=Sn.pointAdd(Wn(e,n),{x:l[0]*o.tileSize,y:l[1]*o.tileSize});if(Sn.pointDistance(i,a){const l=Tn[e].puzzle.tiles,o=jn(e,t),i=jn(e,n);let a;const s=[];o&&s.push(o),i&&s.push(i),o?a=o:i?a=i:(Rn(e,{maxGroup:Jn(e)+1}),r(),a=Jn(e));if(Vn(e,t,{group:a}),d(t),Vn(e,n,{group:a}),d(n),s.length>0)for(const r of l){const t=Ve.decodePiece(r);s.includes(t.group)&&(Vn(e,t.idx,{group:a}),d(t.idx))}})(e,t,n),o=il(e,t);const s=((e,t)=>{let n=0;for(const l of t){const t=Yn(e,l);t>n&&(n=t)}return n})(e,o);return tl(e,o,s),c(o),!0}return!1};let i=!1;for(const t of il(e,p)){const l=el(e,t);if(n(e,t,l[0],[0,1])||n(e,t,l[1],[-1,0])||n(e,t,l[2],[0,-1])||n(e,t,l[3],[1,0])){i=!0;break}}if(i&&Nn(e)===we.ANY){const n=al(e,t)+1;$n(e,t,{d:g,ts:l,points:n}),u()}else $n(e,t,{d:g,ts:l}),u();i&&o&&o(t)}}else $n(e,t,{d:g,ts:l}),u();a._last_mouse=s}else if(g===kt){const o=n[1],i=n[2];$n(e,t,{x:o,y:i,ts:l}),u(),a._last_mouse={x:o,y:i}}else if(g===At){const o=n[1],i=n[2];$n(e,t,{x:o,y:i,ts:l}),u(),a._last_mouse={x:o,y:i}}else $n(e,t,{ts:l}),u();return function(e,t,n){Tn[e].evtInfos[t]=n}(e,t,a),s}};let cl=-10,ul=20,gl=2,pl=15;class hl{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=cl+Math.random()*ul,this.vy=-1*(gl+Math.random()*pl),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;gl=t/2,pl=t-gl;const n=1/4*this.canvas.width/(t/2);cl=-n,ul=2*n}resize(){this.setSpeedParams()}init(){this.readyBombs=[],this.explodedBombs=[],this.particles=[];for(let e=0;e<1;e++)this.readyBombs.push(new hl(this.rng))}update(){100*Math.random()<5&&this.readyBombs.push(new hl(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{const t=e.color+" "+e.d;if(!y[t]){const n=e.d?r:d;if(e.color){const l=e.d?c:u;y[t]=await createImageBitmap(fn.colorizedCanvas(n,l,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,xl=!0})),t}(o,fn.createCanvas()),v={final:!1,requesting:!0,log:[],logPointer:0,speeds:[.5,1,2,5,10,20,50,100,250,500],speedIdx:1,paused:!1,lastRealTs:0,lastGameTs:0,gameStartTs:0,dataOffset:0,dataSize:1e4};Lt.onConnectionStateChange((e=>{i.setConnectionState(e)}));const b=async e=>{v.requesting=!0;const t=await Lt.requestReplayData(e,v.dataOffset,v.dataSize);return v.dataOffset+=v.dataSize,v.requesting=!1,t};let x=()=>0;const C=async()=>{if("play"===l){const l=await Lt.connect(n,e,t),o=Ve.decodeGame(l);dl.setGame(o.id,o),x=()=>D()}else{if("replay"!==l)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=Ve.decodeGame(t.game);dl.setGame(n.id,n),v.requesting=!1,v.log=t.log,v.lastRealTs=D(),v.gameStartTs=parseInt(v.log[0][4],10),v.lastGameTs=v.gameStartTs,x=()=>v.lastGameTs}}xl=!0};await C();const k=dl.getPieceDrawOffset(e),A=dl.getPieceDrawSize(e),S=dl.getPuzzleWidth(e),z=dl.getPuzzleHeight(e),P=dl.getTableWidth(e),I=dl.getTableHeight(e),T={x:(P-S)/2,y:(I-z)/2},_={w:S,h:z},E={w:A,h:A},M=await In.loadPuzzleBitmaps(dl.getPuzzle(e)),B=new yl(w,dl.getRng(e));B.init();const O=w.getContext("2d");w.classList.add("loaded");const N=mn();N.move(-(P-w.width)/2,-(I-w.height)/2);const U=function(e,t,n){let l=[],o=!0,i=!1,a=!1,s=!1,r=!1,d=!1,c=!1,u=!1;const g=(e,t)=>{const l=n.viewportToWorld({x:e,y:t});return[l.x,l.y]},p=e=>g(e.offsetX,e.offsetY),h=()=>g(e.width/2,e.height/2),m=(e,t)=>{o&&("Shift"===t.key?u=e:"ArrowUp"===t.key||"w"===t.key||"W"===t.key?s=e:"ArrowDown"===t.key||"s"===t.key||"S"===t.key?r=e:"ArrowLeft"===t.key||"a"===t.key||"A"===t.key?i=e:"ArrowRight"===t.key||"d"===t.key||"D"===t.key?a=e:"q"===t.key?c=e:"e"===t.key&&(d=e))};let y=null;e.addEventListener("mousedown",(e=>{y=p(e),0===e.button&&f([bt,...y])})),e.addEventListener("mouseup",(e=>{y=p(e),0===e.button&&f([xt,...y])})),e.addEventListener("mousemove",(e=>{y=p(e),f([Ct,...y])})),e.addEventListener("wheel",(e=>{if(y=p(e),n.canZoom(e.deltaY<0?"in":"out")){const t=e.deltaY<0?kt:At;f([t,...y])}})),t.addEventListener("keydown",(e=>m(!0,e))),t.addEventListener("keyup",(e=>m(!1,e))),t.addEventListener("keypress",(e=>{o&&(" "===e.key&&f([It]),"F"!==e.key&&"f"!==e.key||(vl=!vl,xl=!0),"G"!==e.key&&"g"!==e.key||(bl=!bl,xl=!0),"M"!==e.key&&"m"!==e.key||f([Tt]))}));const f=e=>{l.push(e)};return{addEvent:f,consumeAll:()=>{if(0===l.length)return[];const e=l.slice();return l=[],e},createKeyEvents:()=>{const e=(i?1:0)-(a?1:0),t=(s?1:0)-(r?1:0);if(0!==e||0!==t){const l=(u?24:12)*Math.sqrt(n.getCurrentZoom()),o=n.viewportDimToWorld({w:e*l,h:t*l});f([vt,o.w,o.h]),y&&(y[0]-=o.w,y[1]-=o.h)}if(d&&c);else if(d){if(n.canZoom("in")){const e=y||h();f([kt,...e])}}else if(c&&n.canZoom("out")){const e=y||h();f([At,...e])}},setHotkeys:e=>{o=e}}}(w,window,N),G=dl.getImageUrl(e),$=()=>{const t=dl.getStartTs(e),n=dl.getFinishTs(e),l=x();i.setFinished(!!n),i.setDuration((n||l)-t)};$(),i.setPiecesDone(dl.getFinishedPiecesCount(e)),i.setPiecesTotal(dl.getPieceCount(e));const R=x();i.setActivePlayers(dl.getActivePlayers(e,R)),i.setIdlePlayers(dl.getIdlePlayers(e,R));const V=!!dl.getFinishTs(e);let F=V;const j=()=>F&&!V,L=()=>{const e=localStorage.getItem("sound_enabled");return null!==e&&"1"===e},W=()=>dl.getPlayerBgColor(e,t)||localStorage.getItem("bg_color")||"#222222",q=()=>dl.getPlayerColor(e,t)||localStorage.getItem("player_color")||"#ffffff";let H="",Y="",Q=!1;const Z=e=>{Q=e;const[t,n]=e?[H,"grab"]:[Y,"default"];w.style.cursor=`url('${t}') ${p} ${m}, ${n}`},K=e=>{H=fn.colorizedCanvas(r,c,e).toDataURL(),Y=fn.colorizedCanvas(d,u,e).toDataURL(),Z(Q)};K(q());const J=()=>{i.setReplaySpeed&&i.setReplaySpeed(v.speeds[v.speedIdx]),i.setReplayPaused&&i.setReplayPaused(v.paused)};if("play"===l?setInterval($,1e3):"replay"===l&&J(),"play"===l)Lt.onServerChange((n=>{n[0],n[1],n[2];const l=n[3];for(const[o,i]of l)switch(o){case Dt:{const n=Ve.decodePlayer(i);n.id!==t&&(dl.setPlayer(e,n.id,n),xl=!0)}break;case Et:{const t=Ve.decodePiece(i);dl.setPiece(e,t.idx,t),xl=!0}break;case _t:dl.setPuzzleData(e,i),xl=!0}F=!!dl.getFinishTs(e)}));else if("replay"===l){const t=setInterval((()=>{const n=D();if(v.requesting)return void(v.lastRealTs=n);if(v.logPointer+1>=v.log.length)return v.lastRealTs=n,void(async e=>{const t=await b(e);v.log=v.log.slice(v.logPointer),v.logPointer=0,v.log.push(...t.log),t.log.length=v.log.length){v.final&&clearInterval(t);break}const l=v.log[n],i=v.gameStartTs+l[l.length-1];if(i>o)break;const a=l.slice();if(a[0]===yt){const t=a[1];dl.addPlayer(e,t,i),xl=!0}else if(a[0]===ft){const t=dl.getPlayerIdByIndex(e,a[1]);if(!t)throw"[ 2021-05-17 player not found (update player) ]";dl.addPlayer(e,t,i),xl=!0}else if(a[0]===wt){const t=dl.getPlayerIdByIndex(e,a[1]);if(!t)throw"[ 2021-05-17 player not found (handle input) ]";const n=a[2];dl.handleInput(e,t,n,i),xl=!0}v.logPointer=n}v.lastRealTs=n,v.lastGameTs=o,$()}),50)}let X=null;return(e=>{const t=e.fps||60,n=e.slow||1,l=e.update,o=e.render,i=window.requestAnimationFrame,a=1/t,s=n*a;let r,d=0,c=window.performance.now();const u=()=>{for(r=window.performance.now(),d+=Math.min(1,(r-c)/1e3);d>s;)d-=s,l(a);o(d/n),c=r,i(u)};i(u)})({update:()=>{U.createKeyEvents();for(const n of U.consumeAll())if("play"===l){const l=n[0];if(l===vt){const e=n[1],t=n[2],l=N.worldDimToViewport({w:e,h:t});xl=!0,N.move(l.w,l.h)}else if(l===Ct){if(X&&!dl.getFirstOwnedPiece(e,t)){const e={x:n[1],y:n[2]},t=N.worldToViewport(e),l=Math.round(t.x-X.x),o=Math.round(t.y-X.y);xl=!0,N.move(l,o),X=t}}else if(l===zt)K(n[1]);else if(l===bt){const e={x:n[1],y:n[2]};X=N.worldToViewport(e),Z(!0)}else if(l===xt)X=null,Z(!1);else if(l===kt){const e={x:n[1],y:n[2]};xl=!0,N.zoom("in",N.worldToViewport(e))}else if(l===At){const e={x:n[1],y:n[2]};xl=!0,N.zoom("out",N.worldToViewport(e))}else l===It?i.togglePreview():l===Tt&&i.toggleSoundsEnabled();const o=x();dl.handleInput(e,t,n,o,(e=>{L()&&s.play()})).length>0&&(xl=!0),Lt.sendClientEvent(n)}else if("replay"===l){const e=n[0];if(e===vt){const e=n[1],t=n[2];xl=!0,N.move(e,t)}else if(e===Ct){if(X){const e={x:n[1],y:n[2]},t=N.worldToViewport(e),l=Math.round(t.x-X.x),o=Math.round(t.y-X.y);xl=!0,N.move(l,o),X=t}}else if(e===bt){const e={x:n[1],y:n[2]};X=N.worldToViewport(e)}else if(e===xt)X=null;else if(e===kt){const e={x:n[1],y:n[2]};xl=!0,N.zoom("in",N.worldToViewport(e))}else if(e===At){const e={x:n[1],y:n[2]};xl=!0,N.zoom("out",N.worldToViewport(e))}else e===It&&i.togglePreview()}F=!!dl.getFinishTs(e),j()&&(B.update(),xl=!0)},render:async()=>{if(!xl)return;const n=x();let o,a,s;window.DEBUG&&xn(0),O.fillStyle=W(),O.fillRect(0,0,w.width,w.height),window.DEBUG&&Cn("clear done"),o=N.worldToViewportRaw(T),a=N.worldDimToViewportRaw(_),O.fillStyle="rgba(255, 255, 255, .3)",O.fillRect(o.x,o.y,a.w,a.h),window.DEBUG&&Cn("board done");const r=dl.getPiecesSortedByZIndex(e);window.DEBUG&&Cn("get tiles done"),a=N.worldDimToViewportRaw(E);for(const e of r)(-1===e.owner?vl:bl)&&(s=M[e.idx],o=N.worldToViewportRaw({x:k+e.pos.x,y:k+e.pos.y}),O.drawImage(s,0,0,s.width,s.height,o.x,o.y,a.w,a.h));window.DEBUG&&Cn("tiles done");const d=[];for(const i of dl.getActivePlayers(e,n))c=i,("replay"===l||c.id!==t)&&(s=await f(i),o=N.worldToViewport(i),O.drawImage(s,o.x-p,o.y-m),d.push([`${i.name} (${i.points})`,o.x,o.y+h]));var c;O.fillStyle="white",O.textAlign="center";for(const[e,t,l]of d)O.fillText(e,t,l);window.DEBUG&&Cn("players done"),i.setActivePlayers(dl.getActivePlayers(e,n)),i.setIdlePlayers(dl.getIdlePlayers(e,n)),i.setPiecesDone(dl.getFinishedPiecesCount(e)),window.DEBUG&&Cn("HUD done"),j()&&B.render(),xl=!1}}),{setHotkeys:e=>{U.setHotkeys(e)},onBgChange:e=>{localStorage.setItem("bg_color",e),U.addEvent([St,e])},onColorChange:e=>{localStorage.setItem("player_color",e),U.addEvent([zt,e])},onNameChange:e=>{localStorage.setItem("player_name",e),U.addEvent([Pt,e])},onSoundsEnabledChange:e=>{localStorage.setItem("sound_enabled",e?"1":"0")},replayOnSpeedUp:()=>{v.speedIdx+1{v.speedIdx>=1&&(v.speedIdx--,J())},replayOnPauseToggle:()=>{v.paused=!v.paused,J()},previewImageUrl:G,player:{background:W(),color:q(),name:dl.getPlayerName(e,t)||localStorage.getItem("player_name")||"anon",soundsEnabled:L()},disconnect:Lt.disconnect,connect:C}}var kl=e({name:"game",components:{PuzzleStatus:lt,Scores:Je,SettingsOverlay:it,PreviewOverlay:ct,ConnectionOverlay:Wt,HelpOverlay:Kt},data:()=>({activePlayers:[],idlePlayers:[],finished:!1,duration:0,piecesDone:0,piecesTotal:0,overlay:"",connectionState:0,g:{player:{background:"",color:"",name:"",soundsEnabled:!1},previewImageUrl:"",setHotkeys:e=>{},onBgChange:e=>{},onColorChange:e=>{},onNameChange:e=>{},onSoundsEnabledChange:e=>{},disconnect:()=>{},connect:()=>{}}}),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.g=await Cl(`${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},setConnectionState:e=>{this.connectionState=e},togglePreview:()=>{this.toggle("preview",!1)},toggleSoundsEnabled:()=>{this.g.player.soundsEnabled=!this.g.player.soundsEnabled}}))},unmounted(){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 Al={id:"game"},Sl={class:"menu"},zl={class:"tabs"},Pl=s("🧩 Puzzles");kl.render=function(e,o,s,r,d,c){const u=i("settings-overlay"),p=i("preview-overlay"),h=i("help-overlay"),m=i("connection-overlay"),y=i("puzzle-status"),f=i("router-link"),w=i("scores");return a(),t("div",Al,[g(n(u,{onBgclick:o[1]||(o[1]=t=>e.toggle("settings",!0)),modelValue:e.g.player,"onUpdate:modelValue":o[2]||(o[2]=t=>e.g.player=t)},null,8,["modelValue"]),[[b,"settings"===e.overlay]]),g(n(p,{onBgclick:o[3]||(o[3]=t=>e.toggle("preview",!1)),img:e.g.previewImageUrl},null,8,["img"]),[[b,"preview"===e.overlay]]),g(n(h,{onBgclick:o[4]||(o[4]=t=>e.toggle("help",!0))},null,512),[[b,"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",Sl,[n("div",zl,[n(f,{class:"opener",to:{name:"index"},target:"_blank"},{default:l((()=>[Pl])),_:1}),n("div",{class:"opener",onClick:o[5]||(o[5]=t=>e.toggle("preview",!1))},"πŸ–ΌοΈ Preview"),n("div",{class:"opener",onClick:o[6]||(o[6]=t=>e.toggle("settings",!0))},"πŸ› οΈ Settings"),n("div",{class:"opener",onClick:o[7]||(o[7]=t=>e.toggle("help",!0))},"ℹ️ Help")])]),n(w,{activePlayers:e.activePlayers,idlePlayers:e.idlePlayers},null,8,["activePlayers","idlePlayers"])])};var Il=e({name:"replay",components:{PuzzleStatus:lt,Scores:Je,SettingsOverlay:it,PreviewOverlay:ct,HelpOverlay:Kt},data:()=>({activePlayers:[],idlePlayers:[],finished:!1,duration:0,piecesDone:0,piecesTotal:0,overlay:"",connectionState:0,g:{player:{background:"",color:"",name:"",soundsEnabled:!1},previewImageUrl:"",setHotkeys:e=>{},onBgChange:e=>{},onColorChange:e=>{},onNameChange:e=>{},onSoundsEnabledChange:e=>{},replayOnSpeedUp:()=>{},replayOnSpeedDown:()=>{},replayOnPauseToggle:()=>{},disconnect:()=>{}},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.g=await Cl(`${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}}))},unmounted(){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"},_l={class:"menu"},El={class:"tabs"},Dl=s("🧩 Puzzles");Il.render=function(e,o,s,d,c,u){const p=i("settings-overlay"),h=i("preview-overlay"),m=i("help-overlay"),y=i("puzzle-status"),f=i("router-link"),w=i("scores");return a(),t("div",Tl,[g(n(p,{onBgclick:o[1]||(o[1]=t=>e.toggle("settings",!0)),modelValue:e.g.player,"onUpdate:modelValue":o[2]||(o[2]=t=>e.g.player=t)},null,8,["modelValue"]),[[b,"settings"===e.overlay]]),g(n(h,{onBgclick:o[3]||(o[3]=t=>e.toggle("preview",!1)),img:e.g.previewImageUrl},null,8,["img"]),[[b,"preview"===e.overlay]]),g(n(m,{onBgclick:o[4]||(o[4]=t=>e.toggle("help",!0))},null,512),[[b,"help"===e.overlay]]),n(y,{finished:e.finished,duration:e.duration,piecesDone:e.piecesDone,piecesTotal:e.piecesTotal},{default:l((()=>[n("div",null,[n("div",null,r(e.replayText),1),n("button",{class:"btn",onClick:o[5]||(o[5]=t=>e.g.replayOnSpeedUp())},"⏫"),n("button",{class:"btn",onClick:o[6]||(o[6]=t=>e.g.replayOnSpeedDown())},"⏬"),n("button",{class:"btn",onClick:o[7]||(o[7]=t=>e.g.replayOnPauseToggle())},"⏸️")])])),_:1},8,["finished","duration","piecesDone","piecesTotal"]),n("div",_l,[n("div",El,[n(f,{class:"opener",to:{name:"index"},target:"_blank"},{default:l((()=>[Dl])),_:1}),n("div",{class:"opener",onClick:o[8]||(o[8]=t=>e.toggle("preview",!1))},"πŸ–ΌοΈ Preview"),n("div",{class:"opener",onClick:o[9]||(o[9]=t=>e.toggle("settings",!0))},"πŸ› οΈ Settings"),n("div",{class:"opener",onClick:o[10]||(o[10]=t=>e.toggle("help",!0))},"ℹ️ Help")])]),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=x({history:C(),routes:[{name:"index",path:"/",component:V},{name:"new-game",path:"/new-game",component:Fe},{name:"game",path:"/g/:id",component:kl},{name:"replay",path:"/replay/:id",component:Il}]});n.beforeEach(((e,t)=>{t.name&&document.documentElement.classList.remove(`view-${String(t.name)}`),document.documentElement.classList.add(`view-${String(e.name)}`)}));const l=k(A);l.config.globalProperties.$config=t,l.config.globalProperties.$clientId=function(){let e=localStorage.getItem("ID");return e||(e=Ve.uniqId(),localStorage.setItem("ID",e)),e}(),l.use(n),l.mount("#app")})(); diff --git a/build/public/assets/index.3ca85e0f.js b/build/public/assets/index.3ca85e0f.js deleted file mode 100644 index 9e662aa..0000000 --- a/build/public/assets/index.3ca85e0f.js +++ /dev/null @@ -1 +0,0 @@ -import{d as e,c as t,a as n,w as o,b as l,r as i,o as a,e as s,t as r,F as d,f as c,g as u,h as g,v as p,i as h,j as m,k as y,l as f,m as w,n as v,p as b,q as x,s as C,u as k}from"./vendor.18cd2d7e.js";var A=e({name:"app",computed:{showNav(){return!["game","replay"].includes(String(this.$route.name))}}});const S={id:"app"},z={key:0,class:"nav"},P=s("Index"),I=s("New game");A.render=function(e,s,r,d,c,u){const g=i("router-link"),p=i("router-view");return a(),t("div",S,[e.showNav?(a(),t("ul",z,[n("li",null,[n(g,{class:"btn",to:{name:"index"}},{default:o((()=>[P])),_:1})]),n("li",null,[n(g,{class:"btn",to:{name:"new-game"}},{default:o((()=>[I])),_:1})])])):l("",!0),n(p)])};const T=864e5,_=e=>{const t=Math.floor(e/T);e%=T;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 E=1e3,D=()=>{const e=new Date;return Date.UTC(e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate(),e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds(),e.getUTCMilliseconds())},B=(e,t)=>_(t-e),O=_,M=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||D();return`${n} ${B(o,l)}`}}});const U={class:"game-info-text"},N=n("br",null,null,-1),G=n("br",null,null,-1),$=n("br",null,null,-1),R=s(" β†ͺ️ Watch replay ");M.render=function(e,d,c,u,g,p){const h=i("router-link");return a(),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",U,[s(" 🧩 "+r(e.game.tilesFinished)+"/"+r(e.game.tilesTotal),1),N,s(" πŸ‘₯ "+r(e.game.players),1),G,s(" "+r(e.time(e.game.started,e.game.finished)),1),$])])),_:1},8,["to"]),e.game.hasReplay?(a(),t(h,{key:0,class:"game-replay",to:{name:"replay",params:{id:e.game.id}}},{default:o((()=>[R])),_:1},8,["to"])):l("",!0)],4)};var V=e({components:{GameTeaser:M},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 j=n("h1",null,"Running games",-1),F=n("h1",null,"Finished games",-1);V.render=function(e,o,l,s,r,u){const g=i("game-teaser");return a(),t("div",null,[j,(a(!0),t(d,null,c(e.gamesRunning,((e,o)=>(a(),t("div",{class:"game-teaser-wrap",key:o},[n(g,{game:e},null,8,["game"])])))),128)),F,(a(!0),t(d,null,c(e.gamesFinished,((e,o)=>(a(),t("div",{class:"game-teaser-wrap",key:o},[n(g,{game:e},null,8,["game"])])))),128))])};var L=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")}}});L.render=function(e,o,l,i,s,r){return a(),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 W=e({name:"image-library",components:{ImageTeaser:L},props:{images:{type:Array,required:!0}},emits:{imageClicked:null,imageEditClicked:null},methods:{imageClicked(e){this.$emit("imageClicked",e)},imageEditClicked(e){this.$emit("imageEditClicked",e)}}});W.render=function(e,n,o,l,s,r){const u=i("image-teaser");return a(),t("div",null,[(a(!0),t(d,null,c(e.images,((n,o)=>(a(),t(u,{image:n,onClick:t=>e.imageClicked(n),onEditClick:t=>e.imageEditClicked(n),key:o},null,8,["image","onClick","onEditClick"])))),128))])};const q={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}}}};q.render=function(e,n,o,l,i,s){return a(),t("div",{style:s.style,title:o.title},null,12,["title"])};var H=e({name:"tags-input",props:{modelValue:{type:Array,required:!0}},emits:{"update:modelValue":null},data:()=>({input:"",values:[]}),created(){this.values=this.modelValue},methods:{onKeyUp(e){if(","===e.key)return this.add(),e.stopPropagation(),!1},add(){const e=this.input.replace(/,/g,"").trim();e&&(this.values.includes(e)||this.values.push(e),this.input="",this.$emit("update:modelValue",this.values))},rm(e){this.values=this.values.filter((t=>t!==e)),this.$emit("update:modelValue",this.values)}}});const Q=m()(((e,o,l,i,s,u)=>(a(),t("div",null,[g(n("input",{class:"input",type:"text","onUpdate:modelValue":o[1]||(o[1]=t=>e.input=t),placeholder:"Plants, People",onKeydown:o[2]||(o[2]=h(((...t)=>e.add&&e.add(...t)),["enter"])),onKeyup:o[3]||(o[3]=(...t)=>e.onKeyUp&&e.onKeyUp(...t))},null,544),[[p,e.input]]),(a(!0),t(d,null,c(e.values,((n,o)=>(a(),t("span",{key:o,class:"bit",onClick:t=>e.rm(n)},r(n)+" βœ–",9,["onClick"])))),128))]))));H.render=Q,H.__scopeId="data-v-771460ae";var Y=e({name:"new-image-dialog",components:{ResponsiveImage:q,TagsInput:H},emits:{bgclick:null,setupGameClick:null,postToGalleryClick:null},data:()=>({previewUrl:"",file:null,title:"",tags:[]}),computed:{canPostToGallery(){return!(!this.previewUrl||!this.file)},canSetupGameClick(){return!(!this.previewUrl||!this.file)}},methods:{preview(e){const t=e.target;if(!t.files)return;const n=t.files[0];if(!n)return;const o=new FileReader;o.readAsDataURL(n),o.onload=e=>{this.previewUrl=e.target.result,this.file=n}},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})}}});const Z={key:0,class:"has-image"},K={key:1},J={class:"upload"},X=n("span",{class:"btn"},"Upload File",-1),ee={class:"area-settings"},te=n("td",null,[n("label",null,"Title")],-1),ne=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),oe=n("td",null,[n("label",null,"Tags")],-1),le={class:"area-buttons"},ie=s("🧩 Post to gallery "),ae=n("br",null,null,-1),se=s(" + set up game");Y.render=function(e,o,l,s,r,d){const c=i("responsive-image"),h=i("tags-input");return a(),t("div",{class:"overlay new-image-dialog",onClick:o[8]||(o[8]=t=>e.$emit("bgclick"))},[n("div",{class:"overlay-content",onClick:o[7]||(o[7]=u((()=>{}),["stop"]))},[n("div",{class:["area-image",{"has-image":!!e.previewUrl,"no-image":!e.previewUrl}]},[e.previewUrl?(a(),t("div",Z,[n("span",{class:"remove btn",onClick:o[1]||(o[1]=t=>e.previewUrl="")},"X"),n(c,{src:e.previewUrl},null,8,["src"])])):(a(),t("div",K,[n("label",J,[n("input",{type:"file",style:{display:"none"},onChange:o[2]||(o[2]=(...t)=>e.preview&&e.preview(...t)),accept:"image/*"},null,32),X])]))],2),n("div",ee,[n("table",null,[n("tr",null,[te,n("td",null,[g(n("input",{type:"text","onUpdate:modelValue":o[3]||(o[3]=t=>e.title=t),placeholder:"Flower by @artist"},null,512),[[p,e.title]])])]),ne,n("tr",null,[oe,n("td",null,[n(h,{modelValue:e.tags,"onUpdate:modelValue":o[4]||(o[4]=t=>e.tags=t)},null,8,["modelValue"])])])])]),n("div",le,[n("button",{class:"btn",disabled:!e.canPostToGallery,onClick:o[5]||(o[5]=(...t)=>e.postToGallery&&e.postToGallery(...t))},"πŸ–ΌοΈ Post to gallery",8,["disabled"]),n("button",{class:"btn",disabled:!e.canSetupGameClick,onClick:o[6]||(o[6]=(...t)=>e.setupGameClick&&e.setupGameClick(...t))},[ie,ae,se],8,["disabled"])])])])};var re=e({name:"edit-image-dialog",components:{ResponsiveImage:q,TagsInput:H},props:{image:{type:Object,required:!0}},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 de={class:"area-image"},ce={class:"has-image"},ue={class:"area-settings"},ge=n("td",null,[n("label",null,"Title")],-1),pe=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"};var ye,fe,we,ve;re.render=function(e,o,l,s,r,d){const c=i("responsive-image"),h=i("tags-input");return a(),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",de,[n("div",ce,[n(c,{src:e.image.url,title:e.image.title},null,8,["src","title"])])]),n("div",ue,[n("table",null,[n("tr",null,[ge,n("td",null,[g(n("input",{type:"text","onUpdate:modelValue":o[1]||(o[1]=t=>e.title=t),placeholder:"Flower by @artist"},null,512),[[p,e.title]])])]),pe,n("tr",null,[he,n("td",null,[n(h,{modelValue:e.tags,"onUpdate:modelValue":o[2]||(o[2]=t=>e.tags=t)},null,8,["modelValue"])])])])]),n("div",me,[n("button",{class:"btn",onClick:o[3]||(o[3]=(...t)=>e.saveImage&&e.saveImage(...t))},"πŸ–ΌοΈ Save image")])])])},(fe=ye||(ye={}))[fe.Flat=0]="Flat",fe[fe.Out=1]="Out",fe[fe.In=-1]="In",(ve=we||(we={}))[ve.FINAL=0]="FINAL",ve[ve.ANY=1]="ANY";var be=e({name:"new-game-dialog",components:{ResponsiveImage:q},props:{image:{type:Object,required:!0}},emits:{newGame:null,bgclick:null},data:()=>({tiles:1e3,scoreMode:we.ANY}),methods:{onNewGameClick(){this.$emit("newGame",{tiles:this.tilesInt,image:this.image,scoreMode:this.scoreModeInt})}},computed:{canStartNewGame(){return!!(this.tilesInt&&this.image&&this.image.url&&[0,1].includes(this.scoreModeInt))},scoreModeInt(){return parseInt(`${this.scoreMode}`,10)},tilesInt(){return parseInt(`${this.tiles}`,10)}}});const xe={class:"area-image"},Ce={class:"has-image"},ke={class:"area-settings"},Ae=n("td",null,[n("label",null,"Pieces")],-1),Se=n("td",null,[n("label",null,"Scoring: ")],-1),ze=s(" Any (Score when pieces are connected to each other or on final location)"),Pe=n("br",null,null,-1),Ie=s(" Final (Score when pieces are put to their final location)"),Te={class:"area-buttons"};be.render=function(e,o,l,s,r,d){const c=i("responsive-image");return a(),t("div",{class:"overlay new-game-dialog",onClick:o[6]||(o[6]=t=>e.$emit("bgclick"))},[n("div",{class:"overlay-content",onClick:o[5]||(o[5]=u((()=>{}),["stop"]))},[n("div",xe,[n("div",Ce,[n(c,{src:e.image.url,title:e.image.title},null,8,["src","title"])])]),n("div",ke,[n("table",null,[n("tr",null,[Ae,n("td",null,[g(n("input",{type:"text","onUpdate:modelValue":o[1]||(o[1]=t=>e.tiles=t)},null,512),[[p,e.tiles]])])]),n("tr",null,[Se,n("td",null,[n("label",null,[g(n("input",{type:"radio","onUpdate:modelValue":o[2]||(o[2]=t=>e.scoreMode=t),value:"1"},null,512),[[y,e.scoreMode]]),ze]),Pe,n("label",null,[g(n("input",{type:"radio","onUpdate:modelValue":o[3]||(o[3]=t=>e.scoreMode=t),value:"0"},null,512),[[y,e.scoreMode]]),Ie])])])])]),n("div",Te,[n("button",{class:"btn",disabled:!e.canStartNewGame,onClick:o[4]||(o[4]=(...t)=>e.onNewGameClick&&e.onNewGameClick(...t))}," 🧩 Generate Puzzle ",8,["disabled"])])])])};class _e{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 _e(0);return t.rand_high=e.rand_high,t.rand_low=e.rand_low,t}}const Ee=(e,t)=>{const n=`${e}`;return n.length>=t.length?n:t.substr(0,t.length-n.length)+n},De=(...e)=>{const t=t=>(...n)=>{const o=new Date,l=Ee(o.getHours(),"00"),i=Ee(o.getMinutes(),"00"),a=Ee(o.getSeconds(),"00");console[t](`${l}:${i}:${a}`,...e,...n)};return{log:t("log"),error:t("error"),info:t("info")}};var Be={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||"",_e.serialize(e.rng.obj),e.puzzle,e.players,e.evtInfos,e.scoreMode||we.FINAL]},decodeGame:function(e){return{id:e[0],rng:{type:e[1],obj:_e.unserialize(e[2])},puzzle:e[3],players:e[4],evtInfos:e[5],scoreMode:e[6]}},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("&")}`}},Oe=e({components:{ImageLibrary:W,NewImageDialog:Y,EditImageDialog:re,NewGameDialog:be},data:()=>({filters:{sort:"date_desc",tags:[]},images:[],tags:[],image:{id:0,filename:"",file:"",url:"",title:"",tags:[],created:0},dialog:""}),async created(){await this.loadImages()},methods:{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${Be.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){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 fetch("/api/upload",{method:"post",body:t});return 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){await this.uploadImage(e),this.dialog="",await this.loadImages()},async setupGameClick(e){const t=await this.uploadImage(e);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 Me={class:"upload-image-teaser"},Ue=n("div",{class:"hint"},"(The image you upload will be added to the public gallery.)",-1),Ne={key:0},Ge=s(" Tags: "),$e=s(" Sort by: "),Re=n("option",{value:"date_desc"},"Newest first",-1),Ve=n("option",{value:"date_asc"},"Oldest first",-1),je=n("option",{value:"alpha_asc"},"A-Z",-1),Fe=n("option",{value:"alpha_desc"},"Z-A",-1);Oe.render=function(e,o,s,u,p,h){const m=i("image-library"),y=i("new-image-dialog"),w=i("edit-image-dialog"),v=i("new-game-dialog");return a(),t("div",null,[n("div",Me,[n("div",{class:"btn btn-big",onClick:o[1]||(o[1]=t=>e.dialog="new-image")},"Upload your image"),Ue]),n("div",null,[e.tags.length>0?(a(),t("label",Ne,[Ge,(a(!0),t(d,null,c(e.tags,((n,o)=>(a(),t("span",{class:["bit",{on:e.filters.tags.includes(n.slug)}],key:o,onClick:t=>e.toggleTag(n)},r(n.title),11,["onClick"])))),128))])):l("",!0),n("label",null,[$e,g(n("select",{"onUpdate:modelValue":o[2]||(o[2]=t=>e.filters.sort=t),onChange:o[3]||(o[3]=(...t)=>e.filtersChanged&&e.filtersChanged(...t))},[Re,Ve,je,Fe],544),[[f,e.filters.sort]])])]),n(m,{images:e.images,onImageClicked:e.onImageClicked,onImageEditClicked:e.onImageEditClicked},null,8,["images","onImageClicked","onImageEditClicked"]),"new-image"===e.dialog?(a(),t(y,{key:0,onBgclick:o[4]||(o[4]=t=>e.dialog=""),onPostToGalleryClick:e.postToGalleryClick,onSetupGameClick:e.setupGameClick},null,8,["onPostToGalleryClick","onSetupGameClick"])):l("",!0),"edit-image"===e.dialog?(a(),t(w,{key:1,onBgclick:o[5]||(o[5]=t=>e.dialog=""),onSaveClick:e.onSaveImageClick,image:e.image},null,8,["onSaveClick","image"])):l("",!0),e.image&&"new-game"===e.dialog?(a(),t(v,{key:2,onBgclick:o[6]||(o[6]=t=>e.dialog=""),onNewGame:e.onNewGame,image:e.image},null,8,["onNewGame","image"])):l("",!0)])};var Le=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 We={class:"scores"},qe=n("div",null,"Scores",-1),He=n("td",null,"⚑",-1),Qe=n("td",null,"πŸ’€",-1);Le.render=function(e,o,l,i,s,u){return a(),t("div",We,[qe,n("table",null,[(a(!0),t(d,null,c(e.actives,((e,o)=>(a(),t("tr",{key:o,style:{color:e.color}},[He,n("td",null,r(e.name),1),n("td",null,r(e.points),1)],4)))),128)),(a(!0),t(d,null,c(e.idles,((e,o)=>(a(),t("tr",{key:o,style:{color:e.color}},[Qe,n("td",null,r(e.name),1),n("td",null,r(e.points),1)],4)))),128))])])};var Ye=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 O(this.duration)}}});const Ze={class:"timer"};Ye.render=function(e,o,l,i,s,d){return a(),t("div",Ze,[n("div",null," 🧩 "+r(e.piecesDone)+"/"+r(e.piecesTotal),1),n("div",null,r(e.icon)+" "+r(e.durationStr),1),w(e.$slots,"default")])};var Ke=e({name:"settings-overlay",emits:{bgclick:null,"update:modelValue":null},props:{modelValue:Object},created(){this.$watch("modelValue",(e=>{this.$emit("update:modelValue",e)}),{deep:!0})}});const Je=n("td",null,[n("label",null,"Background: ")],-1),Xe=n("td",null,[n("label",null,"Color: ")],-1),et=n("td",null,[n("label",null,"Name: ")],-1),tt=n("td",null,[n("label",null,"Sounds: ")],-1);Ke.render=function(e,o,l,i,s,r){return a(),t("div",{class:"overlay transparent",onClick:o[6]||(o[6]=t=>e.$emit("bgclick"))},[n("table",{class:"overlay-content settings",onClick:o[5]||(o[5]=u((()=>{}),["stop"]))},[n("tr",null,[Je,n("td",null,[g(n("input",{type:"color","onUpdate:modelValue":o[1]||(o[1]=t=>e.modelValue.background=t)},null,512),[[p,e.modelValue.background]])])]),n("tr",null,[Xe,n("td",null,[g(n("input",{type:"color","onUpdate:modelValue":o[2]||(o[2]=t=>e.modelValue.color=t)},null,512),[[p,e.modelValue.color]])])]),n("tr",null,[et,n("td",null,[g(n("input",{type:"text",maxLength:"16","onUpdate:modelValue":o[3]||(o[3]=t=>e.modelValue.name=t)},null,512),[[p,e.modelValue.name]])])]),n("tr",null,[tt,n("td",null,[g(n("input",{type:"checkbox","onUpdate:modelValue":o[4]||(o[4]=t=>e.modelValue.soundsEnabled=t)},null,512),[[v,e.modelValue.soundsEnabled]])])])])])};var nt=e({name:"preview-overlay",props:{img:String},emits:{bgclick:null},computed:{previewStyle(){return{backgroundImage:`url('${this.img}')`}}}});const ot={class:"preview"};nt.render=function(e,o,l,i,s,r){return a(),t("div",{class:"overlay",onClick:o[1]||(o[1]=t=>e.$emit("bgclick"))},[n("div",ot,[n("div",{class:"img",style:e.previewStyle},null,4)])])};var lt=1,it=4,at=2,st=3,rt=2,dt=4,ct=3,ut=9,gt=1,pt=2,ht=3,mt=4,yt=5,ft=6,wt=7,vt=8,bt=10,xt=11,Ct=1,kt=2,At=3;const St=De("Communication.js");let zt,Pt=[],It=e=>{Pt.push(e)},Tt=[],_t=e=>{Tt.push(e)};let Et=0;const Dt=e=>{Et!==e&&(Et=e,_t(e))};function Bt(e){if(2===Et)try{zt.send(JSON.stringify(e))}catch(t){St.info("unable to send message.. maybe because ws is invalid?")}}let Ot,Mt;var Ut={connect:function(e,t,n){return Ot=0,Mt={},Dt(3),new Promise((o=>{zt=new WebSocket(e,n+"|"+t),zt.onopen=()=>{Dt(2),Bt([st])},zt.onmessage=e=>{const t=JSON.parse(e.data),l=t[0];if(l===it){const e=t[1];o(e)}else{if(l!==lt)throw`[ 2021-05-09 invalid connect msgType ${l} ]`;{const e=t[1],o=t[2];if(e===n&&Mt[o])return void delete Mt[o];It(t)}}},zt.onerror=()=>{throw Dt(1),"[ 2021-05-15 onerror ]"},zt.onclose=e=>{4e3===e.code||1001===e.code?Dt(4):Dt(1)}}))},requestReplayData:async function(e,t,n){const o={gameId:e,offset:t,size:n},l=await fetch(`/api/replay-data${Be.asQueryArgs(o)}`);return await l.json()},disconnect:function(){zt&&zt.close(4e3),Ot=0,Mt={}},sendClientEvent:function(e){Ot++,Mt[Ot]=e,Bt([at,Ot,Mt[Ot]])},onServerChange:function(e){It=e;for(const t of Pt)It(t);Pt=[]},onConnectionStateChange:function(e){_t=e;for(const t of Tt)_t(t);Tt=[]},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},Nt=e({name:"connection-overlay",emits:{reconnect:null},props:{connectionState:Number},computed:{lostConnection(){return this.connectionState===Ut.CONN_STATE_DISCONNECTED},connecting(){return this.connectionState===Ut.CONN_STATE_CONNECTING},show(){return!(!this.lostConnection&&!this.connecting)}}});const Gt={key:0,class:"overlay connection-lost"},$t={key:0,class:"overlay-content"},Rt=n("div",null,"⁉️ LOST CONNECTION ⁉️",-1),Vt={key:1,class:"overlay-content"},jt=n("div",null,"Connecting...",-1);Nt.render=function(e,o,i,s,r,d){return e.show?(a(),t("div",Gt,[e.lostConnection?(a(),t("div",$t,[Rt,n("span",{class:"btn",onClick:o[1]||(o[1]=t=>e.$emit("reconnect"))},"Reconnect")])):l("",!0),e.connecting?(a(),t("div",Vt,[jt])):l("",!0)])):l("",!0)};var Ft=e({name:"help-overlay",emits:{bgclick:null}});const Lt=n("tr",null,[n("td",null,"⬆️ Move up:"),n("td",null,[n("div",null,[n("kbd",null,"W"),s("/"),n("kbd",null,"↑"),s("/πŸ–±οΈ")])])],-1),Wt=n("tr",null,[n("td",null,"⬇️ Move down:"),n("td",null,[n("div",null,[n("kbd",null,"S"),s("/"),n("kbd",null,"↓"),s("/πŸ–±οΈ")])])],-1),qt=n("tr",null,[n("td",null,"⬅️ Move left:"),n("td",null,[n("div",null,[n("kbd",null,"A"),s("/"),n("kbd",null,"←"),s("/πŸ–±οΈ")])])],-1),Ht=n("tr",null,[n("td",null,"➑️ Move right:"),n("td",null,[n("div",null,[n("kbd",null,"D"),s("/"),n("kbd",null,"β†’"),s("/πŸ–±οΈ")])])],-1),Qt=n("tr",null,[n("td"),n("td",null,[n("div",null,[s("Move faster by holding "),n("kbd",null,"Shift")])])],-1),Yt=n("tr",null,[n("td",null,"πŸ”+ Zoom in:"),n("td",null,[n("div",null,[n("kbd",null,"E"),s("/πŸ–±οΈ-Wheel")])])],-1),Zt=n("tr",null,[n("td",null,"πŸ”- Zoom out:"),n("td",null,[n("div",null,[n("kbd",null,"Q"),s("/πŸ–±οΈ-Wheel")])])],-1),Kt=n("tr",null,[n("td",null,"πŸ–ΌοΈ Toggle preview:"),n("td",null,[n("div",null,[n("kbd",null,"Space")])])],-1),Jt=n("tr",null,[n("td",null,"πŸ§©βœ”οΈ Toggle fixed pieces:"),n("td",null,[n("div",null,[n("kbd",null,"F")])])],-1),Xt=n("tr",null,[n("td",null,"πŸ§©β“ Toggle loose pieces:"),n("td",null,[n("div",null,[n("kbd",null,"G")])])],-1),en=n("tr",null,[n("td",null,"πŸ”‰ Toggle sounds:"),n("td",null,[n("div",null,[n("kbd",null,"M")])])],-1);Ft.render=function(e,o,l,i,s,r){return a(),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"]))},[Lt,Wt,qt,Ht,Qt,Yt,Zt,Kt,Jt,Xt,en])])};var tn=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"/assets/click.550555f3.mp3"}),nn=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAW0lEQVQ4je1RywrAIAxLxP//5exixRWlVgZelpOKeTQFfnDypgy3eLIkSLLL8mxGPoHsU2hPAgDHBLvRX6hZZw/fwT0BtlLSONqCbWAmEIqMZOCDDlaDR3N03gOyDCn+y4DWmAAAAABJRU5ErkJggg=="}),on=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAARElEQVQ4jWNgGAU0Af+hmBCbgYGBgYERhwHEAEYGBgYGJtIdiApYyLAZBVDsAqoagC1ACQJyY4ERg0GCISh6KA4DigEAou8LC+LnIJoAAAAASUVORK5CYII="}),ln=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAcUlEQVQ4ja1TQQ7AIAgD///n7jCozA2Hbk00jbG1KIrcARszTugoBs49qioZj7r2kKACptkyAOCJsJuA+GzglwHjvMSSWFVaENWVASxh5eRLiq5fN/ASjI89VcP2K3hHpq1cEXNaMfnrL3TDZP2tDuoOA6MzCCXWr38AAAAASUVORK5CYII="}),an=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAU0lEQVQ4jWNgoAH4D8X42HDARKlt5BoAd82AuQAOGLGIYQQUPv0wF5CiCQUge4EsQ5C9QI4BjMguwBYeBAElscCIy1ZivMKIwSDBEBQ9FCckigEAU3QOD7TGvY4AAAAASUVORK5CYII="});function sn(){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)},i=o=>({x:o.x/n-e,y:o.y/n-t}),a=o=>({x:(o.x+e)*n,y:(o.y+t)*n}),s=e=>({w:e.w*n,h:e.h*n}),r=e=>({w:e.w/n,h:e.h/n});return{getCurrentZoom:()=>n,move:o,canZoom:e=>n!=l(e),zoom:(e,t)=>((e,t)=>{if(n==e)return!1;const l=1-n/e;return o(-t.x*l,-t.y*l),n=e,!0})(l(e),t),worldToViewport:e=>{const{x:t,y:n}=a(e);return{x:Math.round(t),y:Math.round(n)}},worldToViewportRaw:a,worldDimToViewport:e=>{const{w:t,h:n}=s(e);return{w:Math.round(t),h:Math.round(n)}},worldDimToViewportRaw:s,viewportToWorld:e=>{const{x:t,y:n}=i(e);return{x:Math.round(t),y:Math.round(n)}},viewportToWorldRaw:i,viewportDimToWorld:e=>{const{w:t,h:n}=r(e);return{w:Math.round(t),h:Math.round(n)}},viewportDimToWorldRaw:r}}function rn(e=0,t=0){const n=document.createElement("canvas");return n.width=e,n.height=t,n}var dn={createCanvas:rn,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=rn(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=rn(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 cn=De("Debug.js");let un=0,gn=0;var pn=e=>{un=performance.now(),gn=e},hn=e=>{const t=performance.now(),n=t-un;n>gn&&cn.log(e+": "+n),un=t};function mn(e,t){const n=e.x-t.x,o=e.y-t.y;return Math.sqrt(n*n+o*o)}function yn(e){return{x:e.x+e.w/2,y:e.y+e.h/2}}var fn={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:mn,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:yn,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 mn(yn(e),yn(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 wn=De("PuzzleGraphics.js");function vn(e,t){const n=Be.coordByPieceIdx(e,t);return{x:n.x*e.tileSize,y:n.y*e.tileSize,w:e.tileSize,h:e.tileSize}}var bn={loadPuzzleBitmaps:async function(e){const t=await dn.loadImageToBitmap(e.info.imageUrl),n=await dn.resizeBitmap(t,e.info.width,e.info.height);return await async function(e,t,n){wn.log("start createPuzzleTileBitmaps");const o=n.tileSize,l=n.tileMarginWidth,i=n.tileDrawSize,a=o/100,s=[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,i={x:l,y:l},r=fn.pointAdd(i,{x:o,y:0}),c=fn.pointAdd(r,{x:0,y:o}),u=fn.pointSub(c,{x:o,y:0});if(n.moveTo(i.x,i.y),0!==e.top)for(let o=0;oBe.decodePiece(xn[e].puzzle.tiles[t]),Mn=(e,t)=>On(e,t).group,Un=(e,t)=>{const n=xn[e].puzzle.info,o={x:(n.table.width-n.width)/2,y:(n.table.height-n.height)/2},l=function(e,t){const n=xn[e].puzzle.info,o=Be.coordByPieceIdx(n,t),l=o.x*n.tileSize,i=o.y*n.tileSize;return{x:l,y:i}}(e,t);return fn.pointAdd(o,l)},Nn=(e,t)=>On(e,t).pos,Gn=e=>{const t=Xn(e),n=eo(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}},$n=(e,t)=>{const n=Fn(e),o=On(e,t);return{x:o.pos.x,y:o.pos.y,w:n,h:n}},Rn=(e,t)=>On(e,t).z,Vn=(e,t)=>{for(const n of xn[e].puzzle.tiles){const e=Be.decodePiece(n);if(e.owner===t)return e.idx}return-1},jn=e=>xn[e].puzzle.info.tileDrawSize,Fn=e=>xn[e].puzzle.info.tileSize,Ln=e=>xn[e].puzzle.data.maxGroup,Wn=e=>xn[e].puzzle.data.maxZ;function qn(e,t){const n=xn[e].puzzle.info,o=Be.coordByPieceIdx(n,t);return[o.y>0?t-n.tilesX:-1,o.x0?t-1:-1]}const Hn=(e,t,n)=>{for(const o of t)Bn(e,o,{z:n})},Qn=(e,t,n)=>{const o=Nn(e,t);Bn(e,t,{pos:fn.pointAdd(o,n)})},Yn=(e,t,n)=>{const o=jn(e),l=Gn(e),i=n;for(const a of t){const t=On(e,a);t.pos.x+n.xl.x+l.w&&(i.x=Math.min(l.x+l.w-t.pos.x+o,i.x)),t.pos.y+n.yl.y+l.h&&(i.y=Math.min(l.y+l.h-t.pos.y+o,i.y))}for(const a of t)Qn(e,a,i)},Zn=(e,t,n)=>{for(const o of t)Bn(e,o,{owner:n})};function Kn(e,t){const n=xn[e].puzzle.tiles,o=Be.decodePiece(n[t]),l=[];if(o.group)for(const i of n){const e=Be.decodePiece(i);e.group===o.group&&l.push(e.idx)}else l.push(o.idx);return l}const Jn=(e,t)=>{const n=kn(e,t);return n?n.points:0},Xn=e=>xn[e].puzzle.info.table.width,eo=e=>xn[e].puzzle.info.table.height;var to={setGame:function(e,t){xn[e]=t},exists:function(e){return!!xn[e]||!1},playerExists:Sn,getActivePlayers:function(e,t){const n=t-30*E;return zn(e).filter((e=>e.ts>=n))},getIdlePlayers:function(e,t){const n=t-30*E;return zn(e).filter((e=>e.ts0))},addPlayer:function(e,t,n){Sn(e,t)?En(e,t,{ts:n}):An(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:_n,getPieceCount:Pn,getImageUrl:function(e){return xn[e].puzzle.info.imageUrl},setImageUrl:function(e,t){xn[e].puzzle.info.imageUrl=t},get:function(e){return xn[e]||null},getAllGames:function(){return Object.values(xn).sort(((e,t)=>Tn(e.id)===Tn(t.id)?t.puzzle.data.started-e.puzzle.data.started:Tn(e.id)?1:-1))},getPlayerBgColor:(e,t)=>{const n=kn(e,t);return n?n.bgcolor:null},getPlayerColor:(e,t)=>{const n=kn(e,t);return n?n.color:null},getPlayerName:(e,t)=>{const n=kn(e,t);return n?n.name:null},getPlayerIndexById:Cn,getPlayerIdByIndex:function(e,t){return xn[e].players.length>t?Be.decodePlayer(xn[e].players[t]).id:null},changePlayer:En,setPlayer:An,setPiece:function(e,t,n){xn[e].puzzle.tiles[t]=Be.encodePiece(n)},setPuzzleData:function(e,t){xn[e].puzzle.data=t},getTableWidth:Xn,getTableHeight:eo,getPuzzle:e=>xn[e].puzzle,getRng:e=>xn[e].rng.obj,getPuzzleWidth:e=>xn[e].puzzle.info.width,getPuzzleHeight:e=>xn[e].puzzle.info.height,getPiecesSortedByZIndex:function(e){return xn[e].puzzle.tiles.map(Be.decodePiece).sort(((e,t)=>e.z-t.z))},getFirstOwnedPiece:(e,t)=>{const n=Vn(e,t);return n<0?null:xn[e].puzzle.tiles[n]},getPieceDrawOffset:e=>xn[e].puzzle.info.tileDrawOffset,getPieceDrawSize:jn,getFinalPiecePos:Un,getStartTs:e=>xn[e].puzzle.data.started,getFinishTs:e=>xn[e].puzzle.data.finished,handleInput:function(e,t,n,o,l){const i=xn[e].puzzle,a=function(e,t){return t in xn[e].evtInfos?xn[e].evtInfos[t]:{_last_mouse:null,_last_mouse_down:null}}(e,t),s=[],r=()=>{s.push([Ct,i.data])},d=t=>{s.push([kt,Be.encodePiece(On(e,t))])},c=e=>{for(const t of e)d(t)},u=()=>{const n=kn(e,t);n&&s.push([At,Be.encodePlayer(n)])},g=n[0];if(g===ft){const l=n[1];En(e,t,{bgcolor:l,ts:o}),u()}else if(g===wt){const l=n[1];En(e,t,{color:l,ts:o}),u()}else if(g===vt){const l=`${n[1]}`.substr(0,16);En(e,t,{name:l,ts:o}),u()}else if(g===ut){const l=n[1],i=n[2],a=kn(e,t);if(a){const n=a.x-l,s=a.y-i;En(e,t,{ts:o,x:n,y:s}),u()}}else if(g===gt){const l={x:n[1],y:n[2]};En(e,t,{d:1,ts:o}),u(),a._last_mouse_down=l;const i=((e,t)=>{const n=xn[e].puzzle.info,o=xn[e].puzzle.tiles;let l=-1,i=-1;for(let a=0;al)&&(l=e.z,i=a)}return i})(e,l);if(i>=0){const n=Wn(e)+1;Dn(e,{maxZ:n}),r();const o=Kn(e,i);Hn(e,o,Wn(e)),Zn(e,o,t),c(o)}a._last_mouse=l}else if(g===ht){const l=n[1],i=n[2],s={x:l,y:i};if(null===a._last_mouse_down)En(e,t,{x:l,y:i,ts:o}),u();else{const n=Vn(e,t);if(n>=0){En(e,t,{x:l,y:i,ts:o}),u();const r=Kn(e,n);let d=fn.pointInBounds(s,Gn(e))&&fn.pointInBounds(a._last_mouse_down,Gn(e));for(const t of r){const n=$n(e,t);if(fn.pointInBounds(s,n)){d=!0;break}}if(d){const t=l-a._last_mouse_down.x,n=i-a._last_mouse_down.y;Yn(e,r,{x:t,y:n}),c(r)}}else En(e,t,{ts:o}),u();a._last_mouse_down=s}a._last_mouse=s}else if(g===pt){const s={x:n[1],y:n[2]},g=0;a._last_mouse_down=null;const p=Vn(e,t);if(p>=0){const n=Kn(e,p);Zn(e,n,0),c(n);const a=Nn(e,p),s=Un(e,p);if(fn.pointDistance(s,a){for(const n of t)Bn(e,n,{owner:-1,z:1})})(e,n),c(n);let d=Jn(e,t);In(e)===we.FINAL?d+=n.length:In(e)===we.ANY&&(d+=1),En(e,t,{d:g,ts:o,points:d}),u(),_n(e)===Pn(e)&&(Dn(e,{finished:o}),r()),l&&l(t)}else{const n=(e,t,n,o)=>{const l=xn[e].puzzle.info;if(n<0)return!1;if(((e,t,n)=>{const o=Mn(e,t),l=Mn(e,n);return!(!o||o!==l)})(e,t,n))return!1;const i=Nn(e,t),a=fn.pointAdd(Nn(e,n),{x:o[0]*l.tileSize,y:o[1]*l.tileSize});if(fn.pointDistance(i,a){const o=xn[e].puzzle.tiles,l=Mn(e,t),i=Mn(e,n);let a;const s=[];l&&s.push(l),i&&s.push(i),l?a=l:i?a=i:(Dn(e,{maxGroup:Ln(e)+1}),r(),a=Ln(e));if(Bn(e,t,{group:a}),d(t),Bn(e,n,{group:a}),d(n),s.length>0)for(const r of o){const t=Be.decodePiece(r);s.includes(t.group)&&(Bn(e,t.idx,{group:a}),d(t.idx))}})(e,t,n),l=Kn(e,t);const s=((e,t)=>{let n=0;for(const o of t){const t=Rn(e,o);t>n&&(n=t)}return n})(e,l);return Hn(e,l,s),c(l),!0}return!1};let i=!1;for(const t of Kn(e,p)){const o=qn(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])){i=!0;break}}if(i&&In(e)===we.ANY){const n=Jn(e,t)+1;En(e,t,{d:g,ts:o,points:n}),u()}else En(e,t,{d:g,ts:o}),u();i&&l&&l(t)}}else En(e,t,{d:g,ts:o}),u();a._last_mouse=s}else if(g===mt){const l=n[1],i=n[2];En(e,t,{x:l,y:i,ts:o}),u(),a._last_mouse={x:l,y:i}}else if(g===yt){const l=n[1],i=n[2];En(e,t,{x:l,y:i,ts:o}),u(),a._last_mouse={x:l,y:i}}else En(e,t,{ts:o}),u();return function(e,t,n){xn[e].evtInfos[t]=n}(e,t,a),s}};let no=-10,oo=20,lo=2,io=15;class ao{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=no+Math.random()*oo,this.vy=-1*(lo+Math.random()*io),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;lo=t/2,io=t-lo;const n=1/4*this.canvas.width/(t/2);no=-n,oo=2*n}resize(){this.setSpeedParams()}init(){this.readyBombs=[],this.explodedBombs=[],this.particles=[];for(let e=0;e<1;e++)this.readyBombs.push(new ao(this.rng))}update(){100*Math.random()<5&&this.readyBombs.push(new ao(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{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(dn.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,ho=!0})),t}(l,dn.createCanvas()),v={final:!1,requesting:!0,log:[],logPointer:0,speeds:[.5,1,2,5,10,20,50,100,250,500],speedIdx:1,paused:!1,lastRealTs:0,lastGameTs:0,gameStartTs:0,dataOffset:0,dataSize:1e4};Ut.onConnectionStateChange((e=>{i.setConnectionState(e)}));const b=async e=>{v.requesting=!0;const t=await Ut.requestReplayData(e,v.dataOffset,v.dataSize);return v.dataOffset+=v.dataSize,v.requesting=!1,t};let x=()=>0;const C=async()=>{if("play"===o){const o=await Ut.connect(n,e,t),l=Be.decodeGame(o);to.setGame(l.id,l),x=()=>D()}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=Be.decodeGame(t.game);to.setGame(n.id,n),v.requesting=!1,v.log=t.log,v.lastRealTs=D(),v.gameStartTs=parseInt(v.log[0][4],10),v.lastGameTs=v.gameStartTs,x=()=>v.lastGameTs}}ho=!0};await C();const k=to.getPieceDrawOffset(e),A=to.getPieceDrawSize(e),S=to.getPuzzleWidth(e),z=to.getPuzzleHeight(e),P=to.getTableWidth(e),I=to.getTableHeight(e),T={x:(P-S)/2,y:(I-z)/2},_={w:S,h:z},E={w:A,h:A},B=await bn.loadPuzzleBitmaps(to.getPuzzle(e)),O=new ro(w,to.getRng(e));O.init();const M=w.getContext("2d");w.classList.add("loaded");const U=sn();U.move(-(P-w.width)/2,-(I-w.height)/2);const N=function(e,t,n){let o=[],l=!0,i=!1,a=!1,s=!1,r=!1,d=!1,c=!1,u=!1;const g=(e,t)=>{const o=n.viewportToWorld({x:e,y:t});return[o.x,o.y]},p=e=>g(e.offsetX,e.offsetY),h=()=>g(e.width/2,e.height/2),m=(e,t)=>{l&&("Shift"===t.key?u=e:"ArrowUp"===t.key||"w"===t.key||"W"===t.key?s=e:"ArrowDown"===t.key||"s"===t.key||"S"===t.key?r=e:"ArrowLeft"===t.key||"a"===t.key||"A"===t.key?i=e:"ArrowRight"===t.key||"d"===t.key||"D"===t.key?a=e:"q"===t.key?c=e:"e"===t.key&&(d=e))};let y=null;e.addEventListener("mousedown",(e=>{y=p(e),0===e.button&&f([gt,...y])})),e.addEventListener("mouseup",(e=>{y=p(e),0===e.button&&f([pt,...y])})),e.addEventListener("mousemove",(e=>{y=p(e),f([ht,...y])})),e.addEventListener("wheel",(e=>{if(y=p(e),n.canZoom(e.deltaY<0?"in":"out")){const t=e.deltaY<0?mt:yt;f([t,...y])}})),t.addEventListener("keydown",(e=>m(!0,e))),t.addEventListener("keyup",(e=>m(!1,e))),t.addEventListener("keypress",(e=>{l&&(" "===e.key&&f([bt]),"F"!==e.key&&"f"!==e.key||(go=!go,ho=!0),"G"!==e.key&&"g"!==e.key||(po=!po,ho=!0),"M"!==e.key&&"m"!==e.key||f([xt]))}));const f=e=>{o.push(e)};return{addEvent:f,consumeAll:()=>{if(0===o.length)return[];const e=o.slice();return o=[],e},createKeyEvents:()=>{const e=(i?1:0)-(a?1:0),t=(s?1:0)-(r?1:0);if(0!==e||0!==t){const o=(u?24:12)*Math.sqrt(n.getCurrentZoom()),l=n.viewportDimToWorld({w:e*o,h:t*o});f([ut,l.w,l.h]),y&&(y[0]-=l.w,y[1]-=l.h)}if(d&&c);else if(d){if(n.canZoom("in")){const e=y||h();f([mt,...e])}}else if(c&&n.canZoom("out")){const e=y||h();f([yt,...e])}},setHotkeys:e=>{l=e}}}(w,window,U),G=to.getImageUrl(e),$=()=>{const t=to.getStartTs(e),n=to.getFinishTs(e),o=x();i.setFinished(!!n),i.setDuration((n||o)-t)};$(),i.setPiecesDone(to.getFinishedPiecesCount(e)),i.setPiecesTotal(to.getPieceCount(e));const R=x();i.setActivePlayers(to.getActivePlayers(e,R)),i.setIdlePlayers(to.getIdlePlayers(e,R));const V=!!to.getFinishTs(e);let j=V;const F=()=>j&&!V,L=()=>{const e=localStorage.getItem("sound_enabled");return null!==e&&"1"===e},W=()=>to.getPlayerBgColor(e,t)||localStorage.getItem("bg_color")||"#222222",q=()=>to.getPlayerColor(e,t)||localStorage.getItem("player_color")||"#ffffff";let H="",Q="",Y=!1;const Z=e=>{Y=e;const[t,n]=e?[H,"grab"]:[Q,"default"];w.style.cursor=`url('${t}') ${p} ${m}, ${n}`},K=e=>{H=dn.colorizedCanvas(r,c,e).toDataURL(),Q=dn.colorizedCanvas(d,u,e).toDataURL(),Z(Y)};K(q());const J=()=>{i.setReplaySpeed&&i.setReplaySpeed(v.speeds[v.speedIdx]),i.setReplayPaused&&i.setReplayPaused(v.paused)};if("play"===o?setInterval($,1e3):"replay"===o&&J(),"play"===o)Ut.onServerChange((n=>{n[0],n[1],n[2];const o=n[3];for(const[l,i]of o)switch(l){case At:{const n=Be.decodePlayer(i);n.id!==t&&(to.setPlayer(e,n.id,n),ho=!0)}break;case kt:{const t=Be.decodePiece(i);to.setPiece(e,t.idx,t),ho=!0}break;case Ct:to.setPuzzleData(e,i),ho=!0}j=!!to.getFinishTs(e)}));else if("replay"===o){const t=setInterval((()=>{const n=D();if(v.requesting)return void(v.lastRealTs=n);if(v.logPointer+1>=v.log.length)return v.lastRealTs=n,void(async e=>{const t=await b(e);v.log=v.log.slice(v.logPointer),v.logPointer=0,v.log.push(...t.log),t.log.length=v.log.length){v.final&&clearInterval(t);break}const o=v.log[n],i=v.gameStartTs+o[o.length-1];if(i>l)break;const a=o.slice();if(a[0]===rt){const t=a[1];to.addPlayer(e,t,i),ho=!0}else if(a[0]===dt){const t=to.getPlayerIdByIndex(e,a[1]);if(!t)throw"[ 2021-05-17 player not found (update player) ]";to.addPlayer(e,t,i),ho=!0}else if(a[0]===ct){const t=to.getPlayerIdByIndex(e,a[1]);if(!t)throw"[ 2021-05-17 player not found (handle input) ]";const n=a[2];to.handleInput(e,t,n,i),ho=!0}v.logPointer=n}v.lastRealTs=n,v.lastGameTs=l,$()}),50)}let X=null;return(e=>{const t=e.fps||60,n=e.slow||1,o=e.update,l=e.render,i=window.requestAnimationFrame,a=1/t,s=n*a;let r,d=0,c=window.performance.now();const u=()=>{for(r=window.performance.now(),d+=Math.min(1,(r-c)/1e3);d>s;)d-=s,o(a);l(d/n),c=r,i(u)};i(u)})({update:()=>{N.createKeyEvents();for(const n of N.consumeAll())if("play"===o){const o=n[0];if(o===ut){const e=n[1],t=n[2],o=U.worldDimToViewport({w:e,h:t});ho=!0,U.move(o.w,o.h)}else if(o===ht){if(X&&!to.getFirstOwnedPiece(e,t)){const e={x:n[1],y:n[2]},t=U.worldToViewport(e),o=Math.round(t.x-X.x),l=Math.round(t.y-X.y);ho=!0,U.move(o,l),X=t}}else if(o===wt)K(n[1]);else if(o===gt){const e={x:n[1],y:n[2]};X=U.worldToViewport(e),Z(!0)}else if(o===pt)X=null,Z(!1);else if(o===mt){const e={x:n[1],y:n[2]};ho=!0,U.zoom("in",U.worldToViewport(e))}else if(o===yt){const e={x:n[1],y:n[2]};ho=!0,U.zoom("out",U.worldToViewport(e))}else o===bt?i.togglePreview():o===xt&&i.toggleSoundsEnabled();const l=x();to.handleInput(e,t,n,l,(e=>{L()&&s.play()})).length>0&&(ho=!0),Ut.sendClientEvent(n)}else if("replay"===o){const e=n[0];if(e===ut){const e=n[1],t=n[2];ho=!0,U.move(e,t)}else if(e===ht){if(X){const e={x:n[1],y:n[2]},t=U.worldToViewport(e),o=Math.round(t.x-X.x),l=Math.round(t.y-X.y);ho=!0,U.move(o,l),X=t}}else if(e===gt){const e={x:n[1],y:n[2]};X=U.worldToViewport(e)}else if(e===pt)X=null;else if(e===mt){const e={x:n[1],y:n[2]};ho=!0,U.zoom("in",U.worldToViewport(e))}else if(e===yt){const e={x:n[1],y:n[2]};ho=!0,U.zoom("out",U.worldToViewport(e))}else e===bt&&i.togglePreview()}j=!!to.getFinishTs(e),F()&&(O.update(),ho=!0)},render:async()=>{if(!ho)return;const n=x();let l,a,s;window.DEBUG&&pn(0),M.fillStyle=W(),M.fillRect(0,0,w.width,w.height),window.DEBUG&&hn("clear done"),l=U.worldToViewportRaw(T),a=U.worldDimToViewportRaw(_),M.fillStyle="rgba(255, 255, 255, .3)",M.fillRect(l.x,l.y,a.w,a.h),window.DEBUG&&hn("board done");const r=to.getPiecesSortedByZIndex(e);window.DEBUG&&hn("get tiles done"),a=U.worldDimToViewportRaw(E);for(const e of r)(-1===e.owner?go:po)&&(s=B[e.idx],l=U.worldToViewportRaw({x:k+e.pos.x,y:k+e.pos.y}),M.drawImage(s,0,0,s.width,s.height,l.x,l.y,a.w,a.h));window.DEBUG&&hn("tiles done");const d=[];for(const i of to.getActivePlayers(e,n))c=i,("replay"===o||c.id!==t)&&(s=await f(i),l=U.worldToViewport(i),M.drawImage(s,l.x-p,l.y-m),d.push([`${i.name} (${i.points})`,l.x,l.y+h]));var c;M.fillStyle="white",M.textAlign="center";for(const[e,t,o]of d)M.fillText(e,t,o);window.DEBUG&&hn("players done"),i.setActivePlayers(to.getActivePlayers(e,n)),i.setIdlePlayers(to.getIdlePlayers(e,n)),i.setPiecesDone(to.getFinishedPiecesCount(e)),window.DEBUG&&hn("HUD done"),F()&&O.render(),ho=!1}}),{setHotkeys:e=>{N.setHotkeys(e)},onBgChange:e=>{localStorage.setItem("bg_color",e),N.addEvent([ft,e])},onColorChange:e=>{localStorage.setItem("player_color",e),N.addEvent([wt,e])},onNameChange:e=>{localStorage.setItem("player_name",e),N.addEvent([vt,e])},onSoundsEnabledChange:e=>{localStorage.setItem("sound_enabled",e?"1":"0")},replayOnSpeedUp:()=>{v.speedIdx+1{v.speedIdx>=1&&(v.speedIdx--,J())},replayOnPauseToggle:()=>{v.paused=!v.paused,J()},previewImageUrl:G,player:{background:W(),color:q(),name:to.getPlayerName(e,t)||localStorage.getItem("player_name")||"anon",soundsEnabled:L()},disconnect:Ut.disconnect,connect:C}}var yo=e({name:"game",components:{PuzzleStatus:Ye,Scores:Le,SettingsOverlay:Ke,PreviewOverlay:nt,ConnectionOverlay:Nt,HelpOverlay:Ft},data:()=>({activePlayers:[],idlePlayers:[],finished:!1,duration:0,piecesDone:0,piecesTotal:0,overlay:"",connectionState:0,g:{player:{background:"",color:"",name:"",soundsEnabled:!1},previewImageUrl:"",setHotkeys:e=>{},onBgChange:e=>{},onColorChange:e=>{},onNameChange:e=>{},onSoundsEnabledChange:e=>{},disconnect:()=>{},connect:()=>{}}}),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.g=await mo(`${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},setConnectionState:e=>{this.connectionState=e},togglePreview:()=>{this.toggle("preview",!1)},toggleSoundsEnabled:()=>{this.g.player.soundsEnabled=!this.g.player.soundsEnabled}}))},unmounted(){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 fo={id:"game"},wo={class:"menu"},vo={class:"tabs"},bo=s("🧩 Puzzles");yo.render=function(e,l,s,r,d,c){const u=i("settings-overlay"),p=i("preview-overlay"),h=i("help-overlay"),m=i("connection-overlay"),y=i("puzzle-status"),f=i("router-link"),w=i("scores");return a(),t("div",fo,[g(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"]),[[b,"settings"===e.overlay]]),g(n(p,{onBgclick:l[3]||(l[3]=t=>e.toggle("preview",!1)),img:e.g.previewImageUrl},null,8,["img"]),[[b,"preview"===e.overlay]]),g(n(h,{onBgclick:l[4]||(l[4]=t=>e.toggle("help",!0))},null,512),[[b,"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",wo,[n("div",vo,[n(f,{class:"opener",to:{name:"index"},target:"_blank"},{default:o((()=>[bo])),_: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))},"ℹ️ Help")])]),n(w,{activePlayers:e.activePlayers,idlePlayers:e.idlePlayers},null,8,["activePlayers","idlePlayers"])])};var xo=e({name:"replay",components:{PuzzleStatus:Ye,Scores:Le,SettingsOverlay:Ke,PreviewOverlay:nt,HelpOverlay:Ft},data:()=>({activePlayers:[],idlePlayers:[],finished:!1,duration:0,piecesDone:0,piecesTotal:0,overlay:"",connectionState:0,g:{player:{background:"",color:"",name:"",soundsEnabled:!1},previewImageUrl:"",setHotkeys:e=>{},onBgChange:e=>{},onColorChange:e=>{},onNameChange:e=>{},onSoundsEnabledChange:e=>{},replayOnSpeedUp:()=>{},replayOnSpeedDown:()=>{},replayOnPauseToggle:()=>{},disconnect:()=>{}},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.g=await mo(`${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}}))},unmounted(){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 Co={id:"replay"},ko={class:"menu"},Ao={class:"tabs"},So=s("🧩 Puzzles");xo.render=function(e,l,s,d,c,u){const p=i("settings-overlay"),h=i("preview-overlay"),m=i("help-overlay"),y=i("puzzle-status"),f=i("router-link"),w=i("scores");return a(),t("div",Co,[g(n(p,{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"]),[[b,"settings"===e.overlay]]),g(n(h,{onBgclick:l[3]||(l[3]=t=>e.toggle("preview",!1)),img:e.g.previewImageUrl},null,8,["img"]),[[b,"preview"===e.overlay]]),g(n(m,{onBgclick:l[4]||(l[4]=t=>e.toggle("help",!0))},null,512),[[b,"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",ko,[n("div",Ao,[n(f,{class:"opener",to:{name:"index"},target:"_blank"},{default:o((()=>[So])),_: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))},"ℹ️ Help")])]),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=x({history:C(),routes:[{name:"index",path:"/",component:V},{name:"new-game",path:"/new-game",component:Oe},{name:"game",path:"/g/:id",component:yo},{name:"replay",path:"/replay/:id",component:xo}]});n.beforeEach(((e,t)=>{t.name&&document.documentElement.classList.remove(`view-${String(t.name)}`),document.documentElement.classList.add(`view-${String(e.name)}`)}));const o=k(A);o.config.globalProperties.$config=t,o.config.globalProperties.$clientId=function(){let e=localStorage.getItem("ID");return e||(e=Be.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 d8933e3..cfd52f7 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 116ee7d..0b37ab5 100644 --- a/build/server/main.js +++ b/build/server/main.js @@ -23,7 +23,13 @@ var ScoreMode; (function (ScoreMode) { ScoreMode[ScoreMode["FINAL"] = 0] = "FINAL"; ScoreMode[ScoreMode["ANY"] = 1] = "ANY"; -})(ScoreMode || (ScoreMode = {})); +})(ScoreMode || (ScoreMode = {})); +var ShapeMode; +(function (ShapeMode) { + ShapeMode[ShapeMode["NORMAL"] = 0] = "NORMAL"; + ShapeMode[ShapeMode["ANY"] = 1] = "ANY"; + ShapeMode[ShapeMode["FLAT"] = 2] = "FLAT"; +})(ShapeMode || (ShapeMode = {})); class Rng { constructor(seed) { @@ -1433,7 +1439,7 @@ var Images = { // cut size of each puzzle tile in the // final resized version of the puzzle image const TILE_SIZE = 64; -async function createPuzzle(rng, targetTiles, image, ts) { +async function createPuzzle(rng, targetTiles, image, ts, shapeMode) { const imagePath = image.file; const imageUrl = image.url; // determine puzzle information from the image dimensions @@ -1446,7 +1452,7 @@ async function createPuzzle(rng, targetTiles, image, ts) { for (let i = 0; i < rawPieces.length; i++) { rawPieces[i] = { idx: i }; } - const shapes = determinePuzzleTileShapes(rng, info); + const shapes = determinePuzzleTileShapes(rng, info, shapeMode); let positions = new Array(info.tiles); for (const piece of rawPieces) { const coord = Util.coordByPieceIdx(info, piece.idx); @@ -1555,8 +1561,19 @@ async function createPuzzle(rng, targetTiles, image, ts) { }, }; } -function determinePuzzleTileShapes(rng, info) { - const tabs = [-1, 1]; +function determineTabs(shapeMode) { + switch (shapeMode) { + case ShapeMode.ANY: + return [-1, 0, 1]; + case ShapeMode.FLAT: + return [0]; + case ShapeMode.NORMAL: + default: + return [-1, 1]; + } +} +function determinePuzzleTileShapes(rng, info, shapeMode) { + const tabs = determineTabs(shapeMode); const shapes = new Array(info.tiles); for (let i = 0; i < info.tiles; i++) { const coord = Util.coordByPieceIdx(info, i); @@ -1692,22 +1709,23 @@ var GameStorage = { setDirty, }; -async function createGameObject(gameId, targetTiles, image, ts, scoreMode) { +async function createGameObject(gameId, targetTiles, image, ts, scoreMode, shapeMode) { const seed = Util.hash(gameId + ' ' + ts); const rng = new Rng(seed); return { id: gameId, rng: { type: 'Rng', obj: rng }, - puzzle: await createPuzzle(rng, targetTiles, image, ts), + puzzle: await createPuzzle(rng, targetTiles, image, ts, shapeMode), players: [], evtInfos: {}, scoreMode, + shapeMode, }; } -async function createGame(gameId, targetTiles, image, ts, scoreMode) { - const gameObject = await createGameObject(gameId, targetTiles, image, ts, scoreMode); +async function createGame(gameId, targetTiles, image, ts, scoreMode, shapeMode) { + const gameObject = await createGameObject(gameId, targetTiles, image, ts, scoreMode, shapeMode); GameLog.create(gameId); - GameLog.log(gameId, Protocol.LOG_HEADER, 1, targetTiles, image, ts, scoreMode); + GameLog.log(gameId, Protocol.LOG_HEADER, 1, targetTiles, image, ts, scoreMode, shapeMode); GameCommon.setGame(gameObject.id, gameObject); GameStorage.setDirty(gameId); } @@ -1981,7 +1999,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); + game = await Game.createGameObject(gameId, log[0][2], log[0][3], log[0][4], log[0][5] || ScoreMode.FINAL, log[0][6] || ShapeMode.NORMAL); } res.send({ log, game: game ? Util.encodeGame(game) : null }); }); @@ -2069,7 +2087,7 @@ app.post('/api/newgame', express.json(), async (req, res) => { const gameId = Util.uniqId(); if (!GameCommon.exists(gameId)) { const ts = Time.timestamp(); - await Game.createGame(gameId, gameSettings.tiles, gameSettings.image, ts, gameSettings.scoreMode); + await Game.createGame(gameId, gameSettings.tiles, gameSettings.image, ts, gameSettings.scoreMode, gameSettings.shapeMode); } res.send({ id: gameId }); }); diff --git a/src/common/Types.ts b/src/common/Types.ts index edb4a1e..6f16971 100644 --- a/src/common/Types.ts +++ b/src/common/Types.ts @@ -73,6 +73,7 @@ export interface Game { puzzle: Puzzle evtInfos: Record scoreMode?: ScoreMode + shapeMode?: ShapeMode rng: GameRng } @@ -90,6 +91,7 @@ export interface GameSettings { tiles: number image: Image scoreMode: ScoreMode + shapeMode: ShapeMode } export interface Puzzle { @@ -198,3 +200,9 @@ export enum ScoreMode { FINAL = 0, ANY = 1, } + +export enum ShapeMode { + NORMAL = 0, + ANY = 1, + FLAT = 2, +} diff --git a/src/frontend/components/NewGameDialog.vue b/src/frontend/components/NewGameDialog.vue index 04a7eff..6bbd2b5 100644 --- a/src/frontend/components/NewGameDialog.vue +++ b/src/frontend/components/NewGameDialog.vue @@ -22,6 +22,16 @@ + + + + +
+ +
+ + + @@ -36,7 +46,7 @@