2 lines
27 KiB
JavaScript
2 lines
27 KiB
JavaScript
const buildTimestamp = "1753617327051"; const dictionary = {"game":"aD","gIsReplay":"gw","playerData":"ag","rawPlayerNames":"a0b","playerBalances":"gp","playerTerritories":"gb","gIsTeamGame":"hh","playerId":"eX","playerNames":"yQ","gameState":"zx","fontSize":"fontSize","x":"eu","y":"ew","canvas":"hp","gHumans":"kA","playerStates":"a3K","fontGeneratorFunction":"bA.qa.sO","gLobbyMaxJoin":"x2","data":"data","playerCount":"playerCount","gBots":"kW","strs":"aAg","gIsSingleplayer":"kU","uiSizes":"bc","gap":"gap","gMaxPlayers":"ek","i":"aB","MenuManager":"aa","getState":"a18"};
|
|
(()=>{"use strict";const e=JSON.parse('{"rE":"0.6.13","_e":"Jul 22","Ao":["Fix for game update 2.09.9","Fix to prevent the hovering tooltip from causing perspective switching during replays (a new feature was added to the base game where, in replays, you can switch to a different player/bot by right clicking on them)"]}'),t=e=>parseFloat(e.toFixed(12));function n(e){const n=document.createElement("p");n.innerText="Attack Percentage Keybinds";const o=document.createElement("div");o.className="arrayinput";const a=document.createElement("button");a.innerText="Add",e.append(n,o,a),e.className="keybinds-input",this.container=o,this.objectKeys=["key","type","value"],this.objectArray=[],this.addObject=function(){this.objectArray.push({key:"",type:"absolute",value:.8}),this.container.appendChild(i(this.objectArray.length-1)),a.scrollIntoView(!1)},a.addEventListener("click",this.addObject.bind(this)),this.update=function(e){this.objectArray=e.attackPercentageKeybinds,this.displayObjects()},this.displayObjects=function(){if(this.container.innerHTML="",0===this.objectArray.length)return this.container.innerText="No custom attack percentage keybinds added";for(var e=0;e<this.objectArray.length;e++)this.container.appendChild(i(e))};const i=e=>{var t=document.createElement("div");this.objectKeys.forEach((n=>{t.appendChild(this.createInputField(e,n))}));var n=document.createElement("button");return n.textContent="Delete",n.addEventListener("click",this.deleteObject.bind(this,e)),t.appendChild(n),t};return this.createInputField=function(e,n){let o=document.createElement("type"===n?"select":"input");if("type"===n)o.innerHTML='<option value="absolute">Absolute</option><option value="relative">Relative</option>',o.addEventListener("change",this.updateObject.bind(this,e,n));else if("key"===n)o.type="text",o.setAttribute("readonly",""),o.setAttribute("placeholder","No key set"),o.addEventListener("click",this.startKeyInput.bind(this,e,n));else{const t="absolute"===this.objectArray[e].type;o.type=t?"text":"number",t?o.addEventListener("click",this.convertIntoNumberInput.bind(this,e,n),{once:!0}):o.setAttribute("step","0.1"),o.addEventListener("input",this.updateObject.bind(this,e,n))}return"value"===n&&"absolute"===this.objectArray[e].type?o.value=t(100*this.objectArray[e][n])+"%":o.value=this.objectArray[e][n],o},this.recreateInputField=function(e,t){this.container.children[e].children[this.objectKeys.indexOf(t)].replaceWith(this.createInputField(e,t))},this.startKeyInput=function(e,t,n){n.target.value="Press any key";const o=this.updateObject.bind(this,e,t);n.target.addEventListener("keydown",o,{once:!0}),n.target.addEventListener("blur",(()=>{n.target.removeEventListener("keydown",o),n.target.value=this.objectArray[e][t]}),{once:!0})},this.convertIntoNumberInput=function(e,t,n){n.target.value=n.target.value.slice(0,-1),n.target.type="number",n.target.addEventListener("blur",(()=>{this.recreateInputField(e,t)}),{once:!0})},this.updateObject=function(e,n,o){if(e>=this.objectArray.length)return;const a="value"===n?"absolute"===this.objectArray[e].type?t(parseFloat(o.target.value)/100):parseFloat(o.target.value):"key"===n?o.key:o.target.value;this.objectArray[e][n]=a,"key"===n?this.recreateInputField(e,n):"type"===n&&this.recreateInputField(e,"value")},this.deleteObject=function(e){this.objectArray.splice(e,1),this.displayObjects()},this}const o={count:0,removeWins:function(){confirm("Do you really want to reset your wins?")&&(o.count=0,localStorage.removeItem("fx_winCount"),alert("Successfully reset wins"))}};null!==localStorage.getItem("fx_winCount")&&(o.count=localStorage.getItem("fx_winCount"));const a=o;var i={};const s=document.getElementById("windowContainer");function l(e){i[e.name]=e,i[e.name].isOpen=!1}function r(e){!1!==i[e].isOpen&&(i[e].isOpen=!1,i[e].element.style.display="none",void 0!==i[e].onClose&&i[e].onClose())}function c(){Object.values(i).forEach((function(e){!1!==e.closable&&r(e.name)}))}document.addEventListener("mousedown",(e=>{s.contains(e.target)||c(),T().useFullscreenMode&&x()}),{passive:!0,capture:!0}),document.getElementById("canvasA").addEventListener("touchstart",c,{passive:!0}),document.addEventListener("keydown",(e=>{"Escape"===e.key&&c()}));const d={create:function(e){const t=document.createElement("div");if(e.element=t,t.className="window"+(void 0!==e.classes?" "+e.classes:" scrollable selectable"),t.style.display="none",!0===e.closeWithButton){const n=document.createElement("button");n.addEventListener("click",(()=>r(e.name))),n.textContent="Close",setTimeout((()=>t.appendChild(n)))}return s.appendChild(t),l(e),t},add:l,openWindow:function(e,...t){!0!==i[e].isOpen&&(void 0!==i[e].beforeOpen&&i[e].beforeOpen(...t),i[e].isOpen=!0,i[e].element.style.display=null)},closeWindow:r,closeAll:c},{Ao:u,rE:p}=e,h=d.create({name:"changelog",closeWithButton:!0}),m=document.createElement("h1");m.textContent="What's new";const y=document.createElement("p");y.textContent=`in FX Client v${p}`;const b=document.createElement("ul");function f(){d.openWindow("changelog")}u.forEach((e=>{const t=document.createElement("li");t.textContent=e,b.appendChild(t)})),h.append(m,y,b),window.__fx=window.__fx||{};const g=window.__fx;var v={displayWinCounter:!0,useFullscreenMode:!1,hoveringTooltip:!0,realisticNames:!1,showPlayerDensity:!0,coloredDensity:!0,densityDisplayStyle:"absoluteQuotient",hideBotNames:!1,highlightClanSpawns:!1,detailedTeamPercentage:!1,openDonationHistoryFromLb:!0,customBackgroundUrl:"",keybindButtons:!1,attackPercentageKeybinds:[]};g.settings=v;const w=["hideAllLinks","fontName"];g.makeMainMenuTransparent=!1;const E=new function(){const t=[{for:"displayWinCounter",type:"checkbox",label:"Display win counter",note:"The win counter tracks multiplayer solo wins (not in team games)"},{type:"button",text:"Reset win counter",action:a.removeWins},{for:"useFullscreenMode",type:"checkbox",label:"Use fullscreen mode",note:"Note: fullscreen mode will trigger after you click anywhere on the page due to browser policy restrictions."},{for:"hoveringTooltip",type:"checkbox",label:"Hovering tooltip",note:"Display map territory info constantly (on mouse hover) instead of only when right clicking on the map"},{for:"realisticNames",type:"checkbox",label:"Realistic Bot Names"},{for:"showPlayerDensity",type:"checkbox",label:"Show player density"},{for:"coloredDensity",type:"checkbox",label:"Colored density",note:"Display the density with a color between red and green depending on the density value"},{for:"densityDisplayStyle",type:"selectMenu",label:"Density value display style:",tooltip:"Controls how the territorial density value should be rendered",options:[{value:"percentage",label:"Percentage"},{value:"absoluteQuotient",label:"Value from 0 to 150 (BetterTT style)"}]},{for:"hideBotNames",type:"checkbox",label:"Hide bot names"},{for:"highlightClanSpawns",type:"checkbox",label:"Highlight clan spawnpoints",note:"Increases the spawnpoint glow size for members of your clan"},{for:"detailedTeamPercentage",type:"checkbox",label:"Detailed team pie chart percentage",note:"For example: this would show 25.82% instead of 26% on the pie chart in team games"},{for:"openDonationHistoryFromLb",type:"checkbox",label:"Open donation history from the leaderboard",note:"Changes whether or not clicking on a player's name in the in-game leaderboard in team games will open their donation history"},{for:"customBackgroundUrl",type:"textInput",label:"Custom main menu background:",placeholder:"Enter an image URL here",tooltip:"A custom image to be shown as the main menu background instead of the currently selected map."},n,{for:"keybindButtons",type:"checkbox",label:"Keybind buttons",note:"Show keybind buttons above the troop selector (max 6)"},function(t){const n=document.createElement("p");n.innerText=`FX Client v${e.rE}`;const o=document.createElement("p");o.innerHTML='<a href="https://discord.gg/dyxcwdNKwK" target="_blank">Discord server</a> |\n <a href="https://github.com/fxclient/FXclient#readme">Github repository</a>';const a=document.createElement("button");a.innerText="Changelog",a.addEventListener("click",f),t.append(n,o,a)}],o=document.querySelector(".settings .scrollable");var i={},s={},l=[];t.forEach((e=>{if("function"==typeof e){const t=document.createElement("div");return l.push(new e(t)),o.append(t)}const t=document.createElement("label");e.tooltip&&(t.title=e.tooltip);const n=e.type.endsWith("Input"),a=document.createElement(n||"checkbox"===e.type?"input":"selectMenu"===e.type?"select":"button");if("textInput"===e.type&&(a.type="text"),e.placeholder&&(a.placeholder=e.placeholder),(n||"selectMenu"===e.type)&&(i[e.for]=a),e.text&&(a.innerText=e.text),e.action&&a.addEventListener("click",e.action),e.label&&t.append(e.label+" "),e.note){const n=document.createElement("small");n.innerText=e.note,t.append(document.createElement("br"),n)}if(e.options&&e.options.forEach((e=>{const t=document.createElement("option");t.setAttribute("value",e.value),t.innerText=e.label,a.append(t)})),t.append(a),"checkbox"===e.type){a.type="checkbox";const n=document.createElement("span");n.className="checkmark",t.className="checkbox",t.append(n),s[e.for]=a}else t.append(document.createElement("br"));o.append(t,document.createElement("br"))})),this.save=function(){Object.keys(i).forEach((function(e){v[e]=i[e].value.trim()})),Object.keys(s).forEach((function(e){v[e]=s[e].checked})),this.applySettings(),d.closeWindow("settings"),w.forEach((e=>delete v[e])),localStorage.setItem("fx_settings",JSON.stringify(v)),window.location.reload()};const r=document.createElement("input");function c(e){const t=e.target,n=t.files[0];if(!n)return;if(t.removeEventListener("change",c),t.value="",!n.name.endsWith(".json"))return alert("Invalid file format");const o=new FileReader;o.onload=function(){let e;try{e=JSON.parse(o.result),confirm('Warning: This will override all current settings, click "OK" to confirm')&&(g.settings=v=e),localStorage.setItem("fx_settings",JSON.stringify(v)),window.location.reload()}catch(e){alert("Error\n"+e)}},o.readAsText(n)}r.type="file",this.importFromFile=function(){r.click(),r.addEventListener("change",c)},this.exportToFile=function(){var e,t,n;e=JSON.stringify(v),t=document.createElement("a"),n=new Blob([e],{type:"application/json"}),t.href=URL.createObjectURL(n),t.download="FX_client_settings.json",t.click(),URL.revokeObjectURL(t.href)},this.syncFields=function(){Object.keys(i).forEach((function(e){i[e].value=v[e]})),Object.keys(s).forEach((function(e){s[e].checked=v[e]})),l.forEach((e=>e.update?.(v)))},this.resetAll=function(){confirm("Are you Really SURE you want to RESET ALL SETTINGS back to the default?")&&(localStorage.removeItem("fx_settings"),window.location.reload())},this.applySettings=function(){""!==v.customBackgroundUrl&&(document.body.style.backgroundImage="url("+v.customBackgroundUrl+")",document.body.style.backgroundSize="cover",document.body.style.backgroundPosition="center"),g.makeMainMenuTransparent=""!==v.customBackgroundUrl},v.useFullscreenMode&&x()};function x(){null===document.fullscreenElement&&document.fullscreenEnabled&&document.documentElement.requestFullscreen({navigationUI:"hide"}).then((()=>{console.log("Fullscreen mode activated")})).catch((e=>{console.warn("Could not enter fullscreen mode:",e)}))}d.add({name:"settings",element:document.querySelector(".settings"),beforeOpen:function(){E.syncFields()}}),null!==localStorage.getItem("fx_settings")&&(g.settings=v={...v,...JSON.parse(localStorage.getItem("fx_settings"))}),E.applySettings();const k=E;function T(){return v}const L=["playerTerritories","playerBalances","rawPlayerNames"],S=["playerId","gIsTeamGame","gHumans","gLobbyMaxJoin","gameState","gIsSingleplayer"],I=e=>L.includes(e)?window[dictionary.playerData]?.[dictionary[e]]:S.includes(e)?window[dictionary.game]?.[dictionary[e]]:window[dictionary[e]],C=()=>Math.floor(window[dictionary.uiSizes]?.[dictionary.gap]??10),O=new function(){this.playersToInclude=[],this.tabLabels=["ALL","CLAN"],this.filteredLeaderboard=[],this.tabBarOffset=0,this.windowWidth=0,this.verticalClickThreshold=1e3,this.hoveringOverTabs=!1,this.scrollToTop=()=>{},this.repaintLeaderboard=()=>{},this.setUpdateFlag=()=>{},this.parseClanFromPlayerName=()=>{console.warn("parse function not set")},this.selectedTab=0,this.tabHovering=-1,this.enabled=!1,this.drawTabs=function(e,t,n,o){e.textBaseline="middle",e.textAlign="center";const a=t/this.tabLabels.length,i=n+this.tabBarOffset/2;this.tabLabels.forEach(((t,s)=>{0!==s&&e.fillRect(a*s,n,1,this.tabBarOffset),this.selectedTab===s&&(e.fillStyle=o,e.fillRect(a*s,n,a,this.tabBarOffset),e.fillStyle="rgb(255,255,255)"),this.tabHovering===s&&(e.fillStyle="rgba(255,255,255,0.3)",e.fillRect(a*s,n,a,this.tabBarOffset),e.fillStyle="rgb(255,255,255)"),e.fillText(t,a*s+a/2,i)}))},this.setHovering=(e,t)=>{let n=!1;if(e){const e=Math.floor(t/(this.windowWidth/this.tabLabels.length));this.tabHovering!==e&&(this.tabHovering=e,n=!0)}return e!==this.hoveringOverTabs&&(this.hoveringOverTabs=e,!1===e&&(this.tabHovering=-1),e||(n=!0)),n&&this.repaintLeaderboard(),e},this.handleMouseDown=e=>{const t=Math.floor(e/(this.windowWidth/this.tabLabels.length));return this.selectedTab!==t&&(this.selectedTab=t,0===this.selectedTab?this.clearFilter():1===this.selectedTab&&(this.filterByOwnClan(),this.setUpdateFlag()),this.repaintLeaderboard()),!0},this.filterByOwnClan=()=>{this.playersToInclude=[];const e=I("playerId"),t=this.parseClanFromPlayerName(I("rawPlayerNames")[e]);I("rawPlayerNames").forEach(((n,o)=>{o!==e&&this.parseClanFromPlayerName(n)!==t||this.playersToInclude.push(o)})),this.enabled=!0,this.scrollToTop()},this.clearFilter=()=>{this.enabled=!1},this.reset=()=>{this.enabled=!1,this.selectedTab=0,A.refresh()}},A=new function(){this.inOwnClan=new Array(512),this.inOwnClan.fill(!1),this.refresh=()=>{const e=I("gHumans"),t=O.parseClanFromPlayerName(I("rawPlayerNames")[I("playerId")]);null===t?this.inOwnClan.fill(!1):I("rawPlayerNames").forEach(((n,o)=>{this.inOwnClan[o]=o<e&&O.parseClanFromPlayerName(n)===t}))}};function B(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}let F=null;d.add({name:"donationHistory",element:document.querySelector("#donationhistory"),beforeOpen:function(e){document.getElementById("donationhistory_note").style.display="none"},onClose:function(){N.openedWindowPlayerID=null}});const N=new function(){this.openedWindowPlayerID=null,this.contentElement=document.querySelector("#donationhistory_content"),this.donationHistory=Array(512);let e=!1;function t(e,t,n,o){const a=I("rawPlayerNames"),i=document.createElement("tr");o&&i.setAttribute("class","new");let s=`<td><span class="color-light-gray">${t}.</span> `;return n===e[1]?s+=`Received <span class="color-green">${e[2]}</span> resources from ${B(a[e[0]])}`:s+=`Sent <span class="color-red">${e[2]}</span> resources to ${B(a[e[1]])}`,s+="</td>",i.innerHTML=s,i}this.getHistoryOf=function(t){return function(e,t){try{return e()}catch(e){throw F=t,setTimeout((()=>{null!==F&&(F=null)})),e}}((()=>this.donationHistory[t].toReversed()),{playerID:t,resetCalled:e,type:typeof this.donationHistory[t],isArray:Array.isArray(this.donationHistory[t])})},this.reset=function(){e=!0;for(var t=0;t<512;t++)this.donationHistory[t]=[]},this.logDonation=function(e,n,o){const a=[e,n,o];if(this.donationHistory[n].push(a),this.donationHistory[e].push(a),this.openedWindowPlayerID===e||this.openedWindowPlayerID===n){const o=this.donationHistory[this.openedWindowPlayerID===e?e:n].length;this.contentElement.prepend(t(a,o,this.openedWindowPlayerID,!0))}},this.displayHistory=function(e,n=I("rawPlayerNames"),o=I("gIsSingleplayer")){var a=N.getHistoryOf(e);console.log("History for "+n[e]+":"),console.log(a),document.querySelector("#donationhistory h1").innerHTML="Donation history for "+B(n[e]),this.contentElement.innerHTML="",a.length>0?a.forEach(((n,o)=>{this.contentElement.appendChild(t(n,a.length-o,e))})):this.contentElement.innerText="Nothing to display",this.openedWindowPlayerID=e,d.openWindow("donationHistory",o)}},H=N,M=new function(){const e=document.createElement("img");e.setAttribute("src","assets/players_icon.png"),document.getElementById("playerlist_content").addEventListener("click",(e=>{const t=e.target.closest("tr[data-player-id]")?.getAttribute("data-player-id");t&&I("gIsTeamGame")&&(d.closeWindow("playerList"),H.displayHistory(t))})),this.display=function(e){const t=I("gHumans"),n=I("gLobbyMaxJoin");let o=`<h3>Players (${t})</h3>`;for(let a=0;a<n;a++)a===t&&(o+=`<h3>Bots (${n-t})</h3>`),o+=`<tr data-player-id="${a}"><td><span class="color-light-gray">${a+1}.</span> ${B(e[a])}</td></tr>`;document.getElementById("playerlist_content").innerHTML=o,document.getElementById("playerlist_content").setAttribute("class",I("gIsTeamGame")?"clickable":""),d.openWindow("playerList")},this.hoveringOverButton=!1,this.drawButton=(t,n,o,a)=>{t.fillRect(n,o,a,a),t.fillStyle=this.hoveringOverButton?"#aaaaaaaa":"#000000aa",t.clearRect(n+1,o+1,a-2,a-2),t.fillRect(n+1,o+1,a-2,a-2),t.fillStyle="#ffffff",t.imageSmoothingEnabled=!0,t.drawImage(e,n+2,o+2,a-4,a-4),t.imageSmoothingEnabled=!1}};d.add({name:"playerList",element:document.getElementById("playerlist")});const P=M,j={getMaxTroops:function(e,t){return(150*e[t]).toString()},getDensity:function(e,t=I("playerBalances"),n=I("playerTerritories")){return"percentage"===T().densityDisplayStyle?(t[e]/(150*(0===n[e]?1:n[e]))*100).toFixed(1)+"%":(t[e]/(0===n[e]?1:n[e])).toFixed(1)},isPointInRectangle:function(e,t,n,o,a,i){return e>=n&&e<=n+a&&t>=o&&t<=o+i},fillTextMultiline:function(e,t,n,o,a){const i=parseInt(e.font.split(" ").find((e=>e.endsWith("px"))).slice(0,-2));t.split("\n").forEach(((t,s)=>e.fillText(t,n,o+s*i,a)))},textStyleBasedOnDensity:function(e){const t=I("playerBalances"),n=I("playerTerritories");return`hsl(${t[e]/(1.5*n[e])}, 100%, 50%, 1)`}},W=new function(){let e=!1;function t(t){if(!T().hoveringTooltip||!I("gameState")||e)return;let n,o;if(t.type.includes("touch")){const{touches:e,changedTouches:a}=t.originalEvent??t,i=e[0]??a[0];n=i.pageX,o=i.pageY}else t.type.includes("mouse")&&(n=t.clientX,o=t.clientY);e=!0;try{this.active=!0,this.display(this.canvasPixelScale*n,this.canvasPixelScale*o),this.active=!1}catch(t){console.error(t)}setTimeout((()=>e=!1),100)}this.display=()=>{},this.active=!1,this.canvasPixelScale=1,document.getElementById("canvasA").addEventListener("mousemove",t.bind(this)),document.getElementById("canvasA").addEventListener("touchstart",t.bind(this))},D={setAbsolute:()=>{},setRelative:()=>{},repaintAttackPercentageBar:()=>{}};function _(e){"absolute"===e.type?D.setAbsolute(e.value):D.setRelative(e.value),D.repaintAttackPercentageBar()}let R,U=0,J=0;const K={setSize:(e,t,n)=>{if(!0!==T().keybindButtons)return;U=e,J=t,R=document.createElement("canvas"),R.width=e,R.height=t;const o=R.getContext("2d"),a=n.font.split("px ",2)[1];o.font="bold "+t/2+"px "+a,o.textAlign="center",o.textBaseline="middle";const i=T().attackPercentageKeybinds.slice(0,6),s=C()/4,l=(e-5*s)/6;i.forEach(((e,n)=>{o.fillStyle="rgba(0, 0, 0, 0.8)",o.fillRect(n*(l+s),0,l,t),o.fillStyle="white";const a="absolute"===e.type?(100*e.value).toFixed()+"%":"x "+Math.round(100*e.value)/100;o.fillText(a,(n+.5)*(l+s),t/2)}))},click:e=>{if(e<0||e>U)return!1;const t=T().attackPercentageKeybinds,n=Math.floor(e/U*6);return!(n>=t.length||(_(t[n]),0))},draw:(e,t,n)=>{e.drawImage(R,t,n-(J+C()/4))}};let $=!1,G="",X=()=>{},q=()=>{},V=(e,t)=>{};const z=new TextEncoder,Q=new TextDecoder;d.add({name:"lobbyJoinMenu",element:document.getElementById("customLobbyJoinMenu")}),d.create({name:"customLobbiesUnavailable",closeWithButton:!0}).innerHTML='<p>The latest version of FX Client doesn\'t support custom lobbies yet. Use the stable version at <a href="https://fxclient.github.io/custom-lobbies/">https://fxclient.github.io/custom-lobbies</a></p>';const Y=d.create({name:"customLobby",classes:"scrollable selectable flex-column text-align-center",closable:!1}),Z=document.createElement("h2");Z.textContent="Custom Lobby";const ee=document.createElement("div");ee.className="customlobby-main";const te=document.createElement("div"),ne=document.createElement("p");ne.textContent="0 Players";const oe=document.createElement("div");te.append(ne,oe);const ae=document.createElement("div");ae.className="text-align-left";const ie={mode:{label:"Mode:",type:"selectMenu",options:[{value:0,label:"2 Teams"},{value:1,label:"3 Teams"},{value:2,label:"4 Teams"},{value:3,label:"5 Teams"},{value:4,label:"6 Teams"},{value:5,label:"7 Teams"},{value:6,label:"8 Teams"},{value:7,label:"Battle Royale"},{value:10,label:"No Fullsend Battle Royale"},{value:9,label:"Zombie mode"}]},map:{label:"Map:",type:"selectMenu"},difficulty:{label:"Difficulty:",type:"selectMenu",options:[{value:0,label:"Very Easy (Default)"},{value:1,label:"Easy (1v1)"},{value:2,label:"Normal"},{value:3,label:"Hard"},{value:4,label:"Very Hard"},{value:5,label:"Impossible"}]},spawnSelection:{label:"Spawn selection",type:"checkbox"},botCount:{label:"Bot & player count:",type:"numberInput",attributes:{min:"1",max:"512"}}},se={},le={};function re(e,t){"checkbox"===ie[e].type?se[e].checked=0!==t:se[e].value=t.toString(),le[e]=t}function ce(e,t){fe("options",[e,parseInt(t.target.value)])}function de(e,t){fe("options",[e,t.target.checked?1:0])}Object.entries(ie).forEach((([e,t])=>{const n=document.createElement("label");t.tooltip&&(n.title=t.tooltip);const o=t.type.endsWith("Input"),a=document.createElement(o||"checkbox"===t.type?"input":"selectMenu"===t.type?"select":"button");if(se[e]=a,"textInput"===t.type&&(a.type="text"),"numberInput"===t.type&&(a.type="number"),t.placeholder&&(a.placeholder=t.placeholder),(o||"selectMenu"===t.type)&&a.addEventListener("change",ce.bind(void 0,e)),t.text&&(a.innerText=t.text),t.action&&a.addEventListener("click",t.action),t.label&&n.append(t.label+" "),t.note){const e=document.createElement("small");e.innerText=t.note,n.append(document.createElement("br"),e)}if(t.options&&be(t.options,a),t.attributes&&Object.entries(t.attributes).forEach((([e,t])=>a.setAttribute(e,t))),n.append(a),"checkbox"===t.type){a.type="checkbox";const t=document.createElement("span");t.className="checkmark",n.className="checkbox",n.append(t),a.addEventListener("change",de.bind(void 0,e))}else n.append(document.createElement("br"));ae.append(n)})),ee.append(te,ae);const ue=document.createElement("footer");function pe(e,t){const n=document.createElement("button");return n.textContent=e,n.addEventListener("click",t),n}ue.style.marginTop="10px";const he=pe("Start game",(function(){d.closeWindow("customLobby"),fe("startGame")})),me=pe("Leave lobby",(()=>q())),ye=pe("Copy link",(()=>{navigator.clipboard.writeText(`${window.location.href}#lobby=${G}`),ye.textContent="Copied!",setTimeout((()=>ye.textContent="Copy link"),1e3)}));function be(e,t){e.forEach((e=>{const n=document.createElement("option");n.setAttribute("value",e.value),n.textContent=e.label,t.append(n)}))}function fe(e,t){const n=void 0!==t?{t:e,d:t}:{t:e},o=z.encode(JSON.stringify(n)),a=new ArrayBuffer(o.length+1);new DataView(a).setUint8(0,120),new Uint8Array(a,1).set(o),V(1,a)}ue.append(he,me,ye),Y.append(Z,ee,ue),document.getElementById("lobbyCode").addEventListener("input",(({target:e})=>{5===e.value.length&&(G=e.value.toLowerCase(),e.value="",d.closeWindow("lobbyJoinMenu"),$=!0,X())})),document.getElementById("createLobbyButton").addEventListener("click",(()=>{G="",d.closeWindow("lobbyJoinMenu"),$=!0,X()}));let ge,ve=!1,we=[];function Ee(e,t){const n=document.createElement("span");return n.textContent=e,n.className=t?"":"d-none",n}function xe(e){const t=document.createElement("div");t.className="lobby-player",t.textContent=e.name;const n=document.createElement("button");n.textContent="Kick",n.className=ve&&!e.isHost?"":"d-none",n.addEventListener("click",ke);const o=Ee("Host",e.isHost),a=Ee("In Game",e.inGame);t.append(o,a,n),oe.append(t),we.push({element:t,hostBadge:o,inGameBadge:a,kickButton:n,isHost:e.isHost,inGame:e.inGame})}function ke(e){const t=e.target;for(let e=0;e<we.length;e++)if(we[e].kickButton===t){fe("kick",e);break}}function Te(){ne.textContent=`${we.length} Player${1===we.length?"":"s"}`}function Le(e){if(window.location.hash.startsWith("#lobby="))return d.openWindow("customLobbiesUnavailable")}window.addEventListener("hashchange",(()=>Le()));const Se={gameInfo:le,showJoinPrompt:function(){return d.openWindow("customLobbiesUnavailable")},isCustomMessage:function(e){if(120!==e[0])return!1;if(1===e.length)return!0;const t=new Uint8Array(e.buffer,1),n=JSON.parse(Q.decode(t)),{t:o,d:a}=n;if("lobby"===o)d.openWindow("customLobby"),Z.textContent="Custom Lobby "+a.code,G=a.code,ve=a.isHost,he.disabled=!ve,ve?ae.classList.remove("disabled"):ae.classList.add("disabled"),Object.entries(a.options).forEach((([e,t])=>re(e,t))),i=a.players,s=a.id,we=[],oe.innerHTML="",i.forEach(xe),ge=we[s],Te();else if("addPlayer"===o)xe({name:a.name,inGame:!1,isHost:!1}),Te();else if("removePlayer"===o){const e=a;we[e].element.remove(),we.splice(e,1),Te()}else if("inLobby"===o){const e=a;we[e].inGame=!1,we[e].inGameBadge.className="d-none"}else if("options"===o){const[e,t]=a;re(e,t)}else if("setHost"===o){const e=a;we[e].isHost=!0,we[e].hostBadge.className=""}else"host"===o?(ve=!0,he.disabled=!1,ae.classList.remove("disabled"),we.forEach((e=>{e.isHost||(e.kickButton.className="")}))):"serverMessage"===o&&alert(a);var i,s;return!0},getSocketURL:function(){return"wss://fx.peshomir.workers.dev/"+(""===G?"create":"join?"+G)},getPlayerId:function(){let e=0;for(let t=0;t<we.length;t++){const n=we[t];if(n===ge)return e;!1===n.inGame&&e++}},setJoinFunction:function(e){X=e,setTimeout(Le,0)},setLeaveFunction:function(e){q=e},setSendFunction:function(e){V=e},setMapInfo:function(e){setTimeout((()=>be(e.map(((e,t)=>({value:t.toString(),label:e.name}))),se.map)),0)},rejoinLobby:function(){X()},hideWindow:function(){d.closeWindow("customLobby")},isActive:()=>$,setActive:function(e){$=e,!1===e&&d.closeWindow("customLobby")}},{rE:Ie,_e:Ce}=e,Oe=localStorage.getItem("fx_version");Oe!==Ie&&(localStorage.setItem("fx_version",Ie),null!==Oe&&f()),window.__fx=window.__fx||{};const Ae=window.__fx;Ae.version=Ie+" "+Ce,Ae.settingsManager=k,Ae.leaderboardFilter=O,Ae.utils=j,Ae.WindowManager=d,Ae.keybindFunctions=D,Ae.keybindHandler=e=>{const t=T().attackPercentageKeybinds.find((t=>t.key===e));return void 0!==t&&(0!==I("gameState")&&_(t),!0)},Ae.mobileKeybinds=K,Ae.donationsTracker=H,Ae.reportError=function(e,t){function n(e){try{return I(e)}catch(e){return e.toString()}}t=e.filename+" "+e.lineno+" "+e.colno+" "+e.message+"\n"+t,fetch("https://fx.peshomir.workers.dev/stats/errors",{body:JSON.stringify({message:t,context:{debug:F,gameState:n("gameState"),singleplayer:n("gIsSingleplayer"),swState:navigator.serviceWorker?.controller?.state,location:window.location.toString(),userAgent:navigator.userAgent,dictionary:JSON.stringify(dictionary),buildTimestamp,scripts:Array.from(document.scripts).map((e=>e.src))}}),method:"POST"}).catch((e=>alert("Failed to report error: "+e)))},Ae.playerList=P,Ae.hoveringTooltip=W,Ae.clanFilter=A,Ae.wins=a,Ae.customLobby=Se,console.log("Successfully loaded FX Client")})(); |