From d49488615fcf6438c7c88b3d4f55e83622367bce Mon Sep 17 00:00:00 2001 From: peshomir Date: Tue, 8 Jul 2025 15:07:17 +0000 Subject: [PATCH] deploy: 23a272385cdaac54f10b26c1606463354f80b8e2 --- fx.bundle.js | 4 ++-- game.js | 26 +++++++------------------- index.html | 8 ++++---- sw.js | 2 +- 4 files changed, 14 insertions(+), 26 deletions(-) diff --git a/fx.bundle.js b/fx.bundle.js index 94e86b5..aa72a9c 100644 --- a/fx.bundle.js +++ b/fx.bundle.js @@ -1,2 +1,2 @@ -const buildTimestamp = "1751645086342"; const dictionary = {"gIsTeamGame":"j","game":"i","playerId":"gC","playerData":"a5","playerNames":"r3","gameState":"bw","fontSize":"fontSize","x":"bF","y":"bG","canvas":"ih","gHumans":"fd","playerStates":"ry","fontGeneratorFunction":"v.w.ae","rawPlayerNames":"lK","playerBalances":"s9","playerTerritories":"a6","gLobbyMaxJoin":"l9","data":"data","playerCount":"playerCount","gBots":"uO","strs":"vL","gIsSingleplayer":"kq","uiSizes":"ak","gap":"gap","gMaxPlayers":"df","i":"aA","MenuManager":"d0","getState":"d1"}; -(()=>{"use strict";const e=JSON.parse('{"rE":"0.6.9.1","_e":"Jul 3","Ao":["Updated FX Client to the latest game version"]}');function t(e){const t=document.createElement("p");t.innerText="Attack Percentage Keybinds";const n=document.createElement("div");n.className="arrayinput";const o=document.createElement("button");return o.innerText="Add",e.append(t,n,o),e.className="keybinds-input",this.container=n,this.keys=["key","type","value"],this.objectArray=[],this.addObject=function(){this.objectArray.push({key:"",type:"absolute",value:.8}),this.displayObjects(),o.scrollIntoView(!1)},this.update=function(e){this.objectArray=e.attackPercentageKeybinds,this.displayObjects()},o.addEventListener("click",this.addObject.bind(this)),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;eAbsolute',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))}"value"===n&&"absolute"===this.objectArray[e].type?o.value=100*this.objectArray[e][n]+"%":o.value=this.objectArray[e][n],t.appendChild(o)}),this);var n=document.createElement("button");n.textContent="Delete",n.addEventListener("click",this.deleteObject.bind(this,e)),t.appendChild(n),this.container.appendChild(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.displayObjects()}),{once:!0})},this.updateObject=function(e,t,n){if(e>=this.objectArray.length)return;const o="value"===t?"absolute"===this.objectArray[e].type?parseFloat(n.target.value)/100:parseFloat(n.target.value):"key"===t?n.key:n.target.value;this.objectArray[e][t]=o,"key"===t&&this.displayObjects()},this.deleteObject=function(e){this.objectArray.splice(e,1),this.displayObjects()},this}const n={count:0,removeWins:function(){confirm("Do you really want to reset your wins?")&&(n.count=0,localStorage.removeItem("fx_winCount"),alert("Successfully reset wins"))}};null!==localStorage.getItem("fx_winCount")&&(n.count=localStorage.getItem("fx_winCount"));const o=n;var a={};const i=document.getElementById("windowContainer");function s(e){a[e.name]=e,a[e.name].isOpen=!1}function l(e){!1!==a[e].isOpen&&(a[e].isOpen=!1,a[e].element.style.display="none",void 0!==a[e].onClose&&a[e].onClose())}function r(){Object.values(a).forEach((function(e){!1!==e.closable&&l(e.name)}))}document.addEventListener("mousedown",(e=>{i.contains(e.target)||r(),x().useFullscreenMode&&E()}),{passive:!0,capture:!0}),document.getElementById("canvasA").addEventListener("touchstart",r,{passive:!0}),document.addEventListener("keydown",(e=>{"Escape"===e.key&&r()}));const c={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",(()=>l(e.name))),n.textContent="Close",setTimeout((()=>t.appendChild(n)))}return i.appendChild(t),s(e),t},add:s,openWindow:function(e,...t){!0!==a[e].isOpen&&(void 0!==a[e].beforeOpen&&a[e].beforeOpen(...t),a[e].isOpen=!0,a[e].element.style.display=null)},closeWindow:l,closeAll:r},{Ao:d,rE:u}=e,p=c.create({name:"changelog",closeWithButton:!0}),h=document.createElement("h1");h.textContent="What's new";const m=document.createElement("p");m.textContent=`in FX Client v${u}`;const y=document.createElement("ul");function b(){c.openWindow("changelog")}d.forEach((e=>{const t=document.createElement("li");t.textContent=e,y.appendChild(t)})),p.append(h,m,y),window.__fx=window.__fx||{};const f=window.__fx;var g={displayWinCounter:!0,useFullscreenMode:!1,hoveringTooltip:!0,realisticNames:!1,showPlayerDensity:!0,coloredDensity:!0,densityDisplayStyle:"percentage",hideBotNames:!1,highlightClanSpawns:!1,detailedTeamPercentage:!1,openDonationHistoryFromLb:!0,customBackgroundUrl:"",keybindButtons:!1,attackPercentageKeybinds:[]};f.settings=g;const v=["hideAllLinks","fontName"];f.makeMainMenuTransparent=!1;const w=new function(){const n=[{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:o.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."},t,{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='Discord server |\n Github repository';const a=document.createElement("button");a.innerText="Changelog",a.addEventListener("click",b),t.append(n,o,a)}],a=document.querySelector(".settings .scrollable");var i={},s={},l=[];n.forEach((e=>{if("function"==typeof e){const t=document.createElement("div");return l.push(new e(t)),a.append(t)}const t=document.createElement("label");e.tooltip&&(t.title=e.tooltip);const n=e.type.endsWith("Input"),o=document.createElement(n||"checkbox"===e.type?"input":"selectMenu"===e.type?"select":"button");if("textInput"===e.type&&(o.type="text"),e.placeholder&&(o.placeholder=e.placeholder),(n||"selectMenu"===e.type)&&(i[e.for]=o),e.text&&(o.innerText=e.text),e.action&&o.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,o.append(t)})),t.append(o),"checkbox"===e.type){o.type="checkbox";const n=document.createElement("span");n.className="checkmark",t.className="checkbox",t.append(n),s[e.for]=o}else t.append(document.createElement("br"));a.append(t,document.createElement("br"))})),this.save=function(){Object.keys(i).forEach((function(e){g[e]=i[e].value.trim()})),Object.keys(s).forEach((function(e){g[e]=s[e].checked})),this.applySettings(),c.closeWindow("settings"),v.forEach((e=>delete g[e])),localStorage.setItem("fx_settings",JSON.stringify(g)),window.location.reload()};const r=document.createElement("input");function d(e){const t=e.target,n=t.files[0];if(!n)return;if(t.removeEventListener("change",d),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')&&(f.settings=g=e),localStorage.setItem("fx_settings",JSON.stringify(g)),window.location.reload()}catch(e){alert("Error\n"+e)}},o.readAsText(n)}r.type="file",this.importFromFile=function(){r.click(),r.addEventListener("change",d)},this.exportToFile=function(){var e,t,n;e=JSON.stringify(g),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=g[e]})),Object.keys(s).forEach((function(e){s[e].checked=g[e]})),l.forEach((e=>e.update?.(g)))},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(){""!==g.customBackgroundUrl&&(document.body.style.backgroundImage="url("+g.customBackgroundUrl+")",document.body.style.backgroundSize="cover",document.body.style.backgroundPosition="center"),f.makeMainMenuTransparent=""!==g.customBackgroundUrl},g.useFullscreenMode&&E()};function E(){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)}))}c.add({name:"settings",element:document.querySelector(".settings"),beforeOpen:function(){w.syncFields()}}),null!==localStorage.getItem("fx_settings")&&(f.settings=g={...g,...JSON.parse(localStorage.getItem("fx_settings"))}),w.applySettings();const k=w;function x(){return g}const L=["playerTerritories","playerBalances","rawPlayerNames"],T=["playerId","gIsTeamGame","gHumans","gLobbyMaxJoin","gameState","gIsSingleplayer"],I=e=>L.includes(e)?window[dictionary.playerData]?.[dictionary[e]]:T.includes(e)?window[dictionary.game]?.[dictionary[e]]:window[dictionary[e]],S=()=>Math.floor(window[dictionary.uiSizes]?.[dictionary.gap]??10),C=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,O.refresh()}},O=new function(){this.inOwnClan=new Array(512),this.inOwnClan.fill(!1),this.refresh=()=>{const e=I("gHumans"),t=C.parseClanFromPlayerName(I("rawPlayerNames")[I("playerId")]);null===t?this.inOwnClan.fill(!1):I("rawPlayerNames").forEach(((n,o)=>{this.inOwnClan[o]=o/g,">").replace(/"/g,""").replace(/'/g,"'")}c.add({name:"donationHistory",element:document.querySelector("#donationhistory"),beforeOpen:function(e){document.getElementById("donationhistory_note").style.display="none"},onClose:function(){A.openedWindowPlayerID=null}});const A=new function(){function e(e,t,n,o){const a=I("rawPlayerNames"),i=document.createElement("tr");o&&i.setAttribute("class","new");let s=`${t}. `;return n===e[1]?s+=`Received ${e[2]} resources from ${B(a[e[0]])}`:s+=`Sent ${e[2]} resources to ${B(a[e[1]])}`,s+="",i.innerHTML=s,i}this.openedWindowPlayerID=null,this.contentElement=document.querySelector("#donationhistory_content"),this.donationHistory=Array(512),this.getHistoryOf=function(e){return this.donationHistory[e].toReversed()},this.reset=function(){for(var e=0;e<512;e++)this.donationHistory[e]=[]},this.logDonation=function(t,n,o){const a=[t,n,o];if(this.donationHistory[n].push(a),this.donationHistory[t].push(a),this.openedWindowPlayerID===t||this.openedWindowPlayerID===n){const o=this.donationHistory[this.openedWindowPlayerID===t?t:n].length;this.contentElement.prepend(e(a,o,this.openedWindowPlayerID,!0))}},this.displayHistory=function(t,n=I("rawPlayerNames"),o=I("gIsSingleplayer")){var a=A.getHistoryOf(t);console.log("History for "+n[t]+":"),console.log(a),document.querySelector("#donationhistory h1").innerHTML="Donation history for "+B(n[t]),this.contentElement.innerHTML="",a.length>0?a.forEach(((n,o)=>{this.contentElement.appendChild(e(n,a.length-o,t))})):this.contentElement.innerText="Nothing to display",this.openedWindowPlayerID=t,c.openWindow("donationHistory",o)}},N=A,H=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")&&(c.closeWindow("playerList"),N.displayHistory(t))})),this.display=function(e){const t=I("gHumans"),n=I("gLobbyMaxJoin");let o=`

Players (${t})

`;for(let a=0;aBots (${n-t})`),o+=`${a+1}. ${B(e[a])}`;document.getElementById("playerlist_content").innerHTML=o,document.getElementById("playerlist_content").setAttribute("class",I("gIsTeamGame")?"clickable":""),c.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}};c.add({name:"playerList",element:document.getElementById("playerlist")});const M=H,P=new function(){this.getMaxTroops=function(e,t){return(150*e[t]).toString()},this.getDensity=function(e,t=I("playerBalances"),n=I("playerTerritories")){return"percentage"===x().densityDisplayStyle?(t[e]/(150*(0===n[e]?1:n[e]))*100).toFixed(1)+"%":(t[e]/(0===n[e]?1:n[e])).toFixed(1)},this.isPointInRectangle=function(e,t,n,o,a,i){return e>=n&&e<=n+a&&t>=o&&t<=o+i},this.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)))},this.textStyleBasedOnDensity=function(e){const t=I("playerBalances"),n=I("playerTerritories");return`hsl(${t[e]/(1.5*n[e])}, 100%, 50%, 1)`}},j=P,W=new function(){let e=!1;function t(t){if(!x().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.display(this.canvasPixelScale*n,this.canvasPixelScale*o)}catch(t){console.error(t)}setTimeout((()=>e=!1),100)}this.display=()=>{},this.canvasPixelScale=1,document.getElementById("canvasA").addEventListener("mousemove",t.bind(this)),document.getElementById("canvasA").addEventListener("touchstart",t.bind(this))},F={setAbsolute:()=>{},setRelative:()=>{},repaintAttackPercentageBar:()=>{}};function D(e){"absolute"===e.type?F.setAbsolute(e.value):F.setRelative(e.value),F.repaintAttackPercentageBar()}let _,R=0,U=0;const J={setSize:(e,t,n)=>{if(!0!==x().keybindButtons)return;R=e,U=t,_=document.createElement("canvas"),_.width=e,_.height=t;const o=_.getContext("2d"),a=n.font.split("px ",2)[1];o.font="bold "+t/2+"px "+a,o.textAlign="center",o.textBaseline="middle";const i=x().attackPercentageKeybinds.slice(0,6),s=S()/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>R)return!1;const t=x().attackPercentageKeybinds,n=Math.floor(e/R*6);return!(n>=t.length||(D(t[n]),0))},draw:(e,t,n)=>{e.drawImage(_,t,n-(U+S()/4))}};let $=!1,G="",K=()=>{},X=()=>{},q=(e,t)=>{};const V=new TextEncoder,z=new TextDecoder;c.add({name:"lobbyJoinMenu",element:document.getElementById("customLobbyJoinMenu")}),c.create({name:"customLobbiesUnavailable",closeWithButton:!0}).innerHTML='

The latest version of FX Client doesn\'t support custom lobbies yet. Use the stable version at https://fxclient.github.io/custom-lobbies

';const Y=c.create({name:"customLobby",classes:"scrollable selectable flex-column text-align-center",closable:!1}),Q=document.createElement("h2");Q.textContent="Custom Lobby";const Z=document.createElement("div");Z.className="customlobby-main";const ee=document.createElement("div"),te=document.createElement("p");te.textContent="0 Players";const ne=document.createElement("div");ee.append(te,ne);const oe=document.createElement("div");oe.className="text-align-left";const ae={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"}}},ie={},se={};function le(e,t){"checkbox"===ae[e].type?ie[e].checked=0!==t:ie[e].value=t.toString(),se[e]=t}function re(e,t){be("options",[e,parseInt(t.target.value)])}function ce(e,t){be("options",[e,t.target.checked?1:0])}Object.entries(ae).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(ie[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",re.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&&ye(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",ce.bind(void 0,e))}else n.append(document.createElement("br"));oe.append(n)})),Z.append(ee,oe);const de=document.createElement("footer");function ue(e,t){const n=document.createElement("button");return n.textContent=e,n.addEventListener("click",t),n}de.style.marginTop="10px";const pe=ue("Start game",(function(){c.closeWindow("customLobby"),be("startGame")})),he=ue("Leave lobby",(()=>X())),me=ue("Copy link",(()=>{navigator.clipboard.writeText(`${window.location.href}#lobby=${G}`),me.textContent="Copied!",setTimeout((()=>me.textContent="Copy link"),1e3)}));function ye(e,t){e.forEach((e=>{const n=document.createElement("option");n.setAttribute("value",e.value),n.textContent=e.label,t.append(n)}))}function be(e,t){const n=void 0!==t?{t:e,d:t}:{t:e},o=V.encode(JSON.stringify(n)),a=new ArrayBuffer(o.length+1);new DataView(a).setUint8(0,120),new Uint8Array(a,1).set(o),q(1,a)}de.append(pe,he,me),Y.append(Q,Z,de),document.getElementById("lobbyCode").addEventListener("input",(({target:e})=>{5===e.value.length&&(G=e.value.toLowerCase(),e.value="",c.closeWindow("lobbyJoinMenu"),$=!0,K())})),document.getElementById("createLobbyButton").addEventListener("click",(()=>{G="",c.closeWindow("lobbyJoinMenu"),$=!0,K()}));let fe,ge=!1,ve=[];function we(e,t){const n=document.createElement("span");return n.textContent=e,n.className=t?"":"d-none",n}function Ee(e){const t=document.createElement("div");t.className="lobby-player",t.textContent=e.name;const n=document.createElement("button");n.textContent="Kick",n.className=ge&&!e.isHost?"":"d-none",n.addEventListener("click",ke);const o=we("Host",e.isHost),a=we("In Game",e.inGame);t.append(o,a,n),ne.append(t),ve.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;eLe()));const Te={gameInfo:se,showJoinPrompt:function(){return c.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(z.decode(t)),{t:o,d:a}=n;if("lobby"===o)c.openWindow("customLobby"),Q.textContent="Custom Lobby "+a.code,G=a.code,ge=a.isHost,pe.disabled=!ge,ge?oe.classList.remove("disabled"):oe.classList.add("disabled"),Object.entries(a.options).forEach((([e,t])=>le(e,t))),i=a.players,s=a.id,ve=[],ne.innerHTML="",i.forEach(Ee),fe=ve[s],xe();else if("addPlayer"===o)Ee({name:a.name,inGame:!1,isHost:!1}),xe();else if("removePlayer"===o){const e=a;ve[e].element.remove(),ve.splice(e,1),xe()}else if("inLobby"===o){const e=a;ve[e].inGame=!1,ve[e].inGameBadge.className="d-none"}else if("options"===o){const[e,t]=a;le(e,t)}else if("setHost"===o){const e=a;ve[e].isHost=!0,ve[e].hostBadge.className=""}else"host"===o?(ge=!0,pe.disabled=!1,oe.classList.remove("disabled"),ve.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;tye(e.map(((e,t)=>({value:t.toString(),label:e.name}))),ie.map)),0)},rejoinLobby:function(){K()},hideWindow:function(){c.closeWindow("customLobby")},isActive:()=>$,setActive:function(e){$=e,!1===e&&c.closeWindow("customLobby")}},{rE:Ie,_e:Se}=e;"serviceWorker"in navigator&&(navigator.serviceWorker.addEventListener("message",(e=>{const t=e.data;"activate"===t.event&&buildTimestamp!==t.version&&(document.getElementById("updateNotification").style.display="block")})),navigator.serviceWorker.register("./sw.js"));const Ce=localStorage.getItem("fx_version");Ce!==Ie&&(localStorage.setItem("fx_version",Ie),null!==Ce&&b()),window.__fx=window.__fx||{};const Oe=window.__fx;Oe.version=Ie+" "+Se,Oe.settingsManager=k,Oe.leaderboardFilter=C,Oe.utils=j,Oe.WindowManager=c,Oe.keybindFunctions=F,Oe.keybindHandler=e=>{const t=x().attackPercentageKeybinds.find((t=>t.key===e));return void 0!==t&&(0!==I("gameState")&&D(t),!0)},Oe.mobileKeybinds=J,Oe.donationsTracker=N,Oe.playerList=M,Oe.hoveringTooltip=W,Oe.clanFilter=O,Oe.wins=o,Oe.customLobby=Te,console.log("Successfully loaded FX Client")})(); \ No newline at end of file +const buildTimestamp = "1751987229663"; const dictionary = {"gIsTeamGame":"j","game":"i","playerId":"gC","playerData":"a5","playerNames":"r3","gameState":"bw","fontSize":"fontSize","x":"bF","y":"bG","canvas":"ih","gHumans":"fd","playerStates":"ry","fontGeneratorFunction":"v.w.ae","rawPlayerNames":"lK","playerBalances":"s9","playerTerritories":"a6","gLobbyMaxJoin":"l9","data":"data","playerCount":"playerCount","gBots":"uO","strs":"vL","gIsSingleplayer":"kq","uiSizes":"ak","gap":"gap","gMaxPlayers":"df","i":"aA","MenuManager":"d0","getState":"d1"}; +(()=>{"use strict";const e=JSON.parse('{"rE":"0.6.10","_e":"Jul 8","Ao":["Fixed floating point number imprecision when configuring keybinds","Minor improvements to the keybinds settings menu","Added automatic error reporting to simplify bug identification and removal","The BetterTT density style (a value from 0 to 150) is now the default way that density is displayed. The percentage style will remain as is it for players who have already used FX Client, but you can change it in the settings at any time."]}'),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{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='',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&&k()}),{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='Discord server |\n Github repository';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&&k()};function k(){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 x=E;function T(){return v}const L=["playerTerritories","playerBalances","rawPlayerNames"],I=["playerId","gIsTeamGame","gHumans","gLobbyMaxJoin","gameState","gIsSingleplayer"],S=e=>L.includes(e)?window[dictionary.playerData]?.[dictionary[e]]:I.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=S("playerId"),t=this.parseClanFromPlayerName(S("rawPlayerNames")[e]);S("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,B.refresh()}},B=new function(){this.inOwnClan=new Array(512),this.inOwnClan.fill(!1),this.refresh=()=>{const e=S("gHumans"),t=O.parseClanFromPlayerName(S("rawPlayerNames")[S("playerId")]);null===t?this.inOwnClan.fill(!1):S("rawPlayerNames").forEach(((n,o)=>{this.inOwnClan[o]=o/g,">").replace(/"/g,""").replace(/'/g,"'")}d.add({name:"donationHistory",element:document.querySelector("#donationhistory"),beforeOpen:function(e){document.getElementById("donationhistory_note").style.display="none"},onClose:function(){F.openedWindowPlayerID=null}});const F=new function(){function e(e,t,n,o){const a=S("rawPlayerNames"),i=document.createElement("tr");o&&i.setAttribute("class","new");let s=`${t}. `;return n===e[1]?s+=`Received ${e[2]} resources from ${A(a[e[0]])}`:s+=`Sent ${e[2]} resources to ${A(a[e[1]])}`,s+="",i.innerHTML=s,i}this.openedWindowPlayerID=null,this.contentElement=document.querySelector("#donationhistory_content"),this.donationHistory=Array(512),this.getHistoryOf=function(e){return this.donationHistory[e].toReversed()},this.reset=function(){for(var e=0;e<512;e++)this.donationHistory[e]=[]},this.logDonation=function(t,n,o){const a=[t,n,o];if(this.donationHistory[n].push(a),this.donationHistory[t].push(a),this.openedWindowPlayerID===t||this.openedWindowPlayerID===n){const o=this.donationHistory[this.openedWindowPlayerID===t?t:n].length;this.contentElement.prepend(e(a,o,this.openedWindowPlayerID,!0))}},this.displayHistory=function(t,n=S("rawPlayerNames"),o=S("gIsSingleplayer")){var a=F.getHistoryOf(t);console.log("History for "+n[t]+":"),console.log(a),document.querySelector("#donationhistory h1").innerHTML="Donation history for "+A(n[t]),this.contentElement.innerHTML="",a.length>0?a.forEach(((n,o)=>{this.contentElement.appendChild(e(n,a.length-o,t))})):this.contentElement.innerText="Nothing to display",this.openedWindowPlayerID=t,d.openWindow("donationHistory",o)}},N=F,H=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&&S("gIsTeamGame")&&(d.closeWindow("playerList"),N.displayHistory(t))})),this.display=function(e){const t=S("gHumans"),n=S("gLobbyMaxJoin");let o=`

Players (${t})

`;for(let a=0;aBots (${n-t})`),o+=`${a+1}. ${A(e[a])}`;document.getElementById("playerlist_content").innerHTML=o,document.getElementById("playerlist_content").setAttribute("class",S("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 M=H,P={getMaxTroops:function(e,t){return(150*e[t]).toString()},getDensity:function(e,t=S("playerBalances"),n=S("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=S("playerBalances"),n=S("playerTerritories");return`hsl(${t[e]/(1.5*n[e])}, 100%, 50%, 1)`},reportError:function(e,t){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:{swState:navigator.serviceWorker?.controller?.state,location:window.location.toString(),userAgent:navigator.userAgent,dictionary,buildTimestamp,scripts:Array.from(document.scripts).map((e=>e.src))}}),method:"POST"}).catch((e=>alert("Failed to report error: "+e)))}},W=new function(){let e=!1;function t(t){if(!T().hoveringTooltip||!S("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.display(this.canvasPixelScale*n,this.canvasPixelScale*o)}catch(t){console.error(t)}setTimeout((()=>e=!1),100)}this.display=()=>{},this.canvasPixelScale=1,document.getElementById("canvasA").addEventListener("mousemove",t.bind(this)),document.getElementById("canvasA").addEventListener("touchstart",t.bind(this))},j={setAbsolute:()=>{},setRelative:()=>{},repaintAttackPercentageBar:()=>{}};function D(e){"absolute"===e.type?j.setAbsolute(e.value):j.setRelative(e.value),j.repaintAttackPercentageBar()}let _,R=0,U=0;const J={setSize:(e,t,n)=>{if(!0!==T().keybindButtons)return;R=e,U=t,_=document.createElement("canvas"),_.width=e,_.height=t;const o=_.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>R)return!1;const t=T().attackPercentageKeybinds,n=Math.floor(e/R*6);return!(n>=t.length||(D(t[n]),0))},draw:(e,t,n)=>{e.drawImage(_,t,n-(U+C()/4))}};let K=!1,$="",G=()=>{},X=()=>{},q=(e,t)=>{};const V=new TextEncoder,z=new TextDecoder;d.add({name:"lobbyJoinMenu",element:document.getElementById("customLobbyJoinMenu")}),d.create({name:"customLobbiesUnavailable",closeWithButton:!0}).innerHTML='

The latest version of FX Client doesn\'t support custom lobbies yet. Use the stable version at https://fxclient.github.io/custom-lobbies

';const Q=d.create({name:"customLobby",classes:"scrollable selectable flex-column text-align-center",closable:!1}),Y=document.createElement("h2");Y.textContent="Custom Lobby";const Z=document.createElement("div");Z.className="customlobby-main";const ee=document.createElement("div"),te=document.createElement("p");te.textContent="0 Players";const ne=document.createElement("div");ee.append(te,ne);const oe=document.createElement("div");oe.className="text-align-left";const ae={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"}}},ie={},se={};function le(e,t){"checkbox"===ae[e].type?ie[e].checked=0!==t:ie[e].value=t.toString(),se[e]=t}function re(e,t){be("options",[e,parseInt(t.target.value)])}function ce(e,t){be("options",[e,t.target.checked?1:0])}Object.entries(ae).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(ie[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",re.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&&ye(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",ce.bind(void 0,e))}else n.append(document.createElement("br"));oe.append(n)})),Z.append(ee,oe);const de=document.createElement("footer");function ue(e,t){const n=document.createElement("button");return n.textContent=e,n.addEventListener("click",t),n}de.style.marginTop="10px";const pe=ue("Start game",(function(){d.closeWindow("customLobby"),be("startGame")})),he=ue("Leave lobby",(()=>X())),me=ue("Copy link",(()=>{navigator.clipboard.writeText(`${window.location.href}#lobby=${$}`),me.textContent="Copied!",setTimeout((()=>me.textContent="Copy link"),1e3)}));function ye(e,t){e.forEach((e=>{const n=document.createElement("option");n.setAttribute("value",e.value),n.textContent=e.label,t.append(n)}))}function be(e,t){const n=void 0!==t?{t:e,d:t}:{t:e},o=V.encode(JSON.stringify(n)),a=new ArrayBuffer(o.length+1);new DataView(a).setUint8(0,120),new Uint8Array(a,1).set(o),q(1,a)}de.append(pe,he,me),Q.append(Y,Z,de),document.getElementById("lobbyCode").addEventListener("input",(({target:e})=>{5===e.value.length&&($=e.value.toLowerCase(),e.value="",d.closeWindow("lobbyJoinMenu"),K=!0,G())})),document.getElementById("createLobbyButton").addEventListener("click",(()=>{$="",d.closeWindow("lobbyJoinMenu"),K=!0,G()}));let fe,ge=!1,ve=[];function we(e,t){const n=document.createElement("span");return n.textContent=e,n.className=t?"":"d-none",n}function Ee(e){const t=document.createElement("div");t.className="lobby-player",t.textContent=e.name;const n=document.createElement("button");n.textContent="Kick",n.className=ge&&!e.isHost?"":"d-none",n.addEventListener("click",ke);const o=we("Host",e.isHost),a=we("In Game",e.inGame);t.append(o,a,n),ne.append(t),ve.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;eTe()));const Le={gameInfo:se,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(z.decode(t)),{t:o,d:a}=n;if("lobby"===o)d.openWindow("customLobby"),Y.textContent="Custom Lobby "+a.code,$=a.code,ge=a.isHost,pe.disabled=!ge,ge?oe.classList.remove("disabled"):oe.classList.add("disabled"),Object.entries(a.options).forEach((([e,t])=>le(e,t))),i=a.players,s=a.id,ve=[],ne.innerHTML="",i.forEach(Ee),fe=ve[s],xe();else if("addPlayer"===o)Ee({name:a.name,inGame:!1,isHost:!1}),xe();else if("removePlayer"===o){const e=a;ve[e].element.remove(),ve.splice(e,1),xe()}else if("inLobby"===o){const e=a;ve[e].inGame=!1,ve[e].inGameBadge.className="d-none"}else if("options"===o){const[e,t]=a;le(e,t)}else if("setHost"===o){const e=a;ve[e].isHost=!0,ve[e].hostBadge.className=""}else"host"===o?(ge=!0,pe.disabled=!1,oe.classList.remove("disabled"),ve.forEach((e=>{e.isHost||(e.kickButton.className="")}))):"serverMessage"===o&&alert(a);var i,s;return!0},getSocketURL:function(){return"wss://fx.peshomir.workers.dev/"+(""===$?"create":"join?"+$)},getPlayerId:function(){let e=0;for(let t=0;tye(e.map(((e,t)=>({value:t.toString(),label:e.name}))),ie.map)),0)},rejoinLobby:function(){G()},hideWindow:function(){d.closeWindow("customLobby")},isActive:()=>K,setActive:function(e){K=e,!1===e&&d.closeWindow("customLobby")}},{rE:Ie,_e:Se}=e;"serviceWorker"in navigator&&(navigator.serviceWorker.addEventListener("message",(e=>{const t=e.data;"activate"===t.event&&buildTimestamp!==t.version&&(document.getElementById("updateNotification").style.display="block")})),navigator.serviceWorker.register("./sw.js"));const Ce=localStorage.getItem("fx_version");Ce!==Ie&&(localStorage.setItem("fx_version",Ie),null!==Ce&&f()),window.__fx=window.__fx||{};const Oe=window.__fx;Oe.version=Ie+" "+Se,Oe.settingsManager=x,Oe.leaderboardFilter=O,Oe.utils=P,Oe.WindowManager=d,Oe.keybindFunctions=j,Oe.keybindHandler=e=>{const t=T().attackPercentageKeybinds.find((t=>t.key===e));return void 0!==t&&(0!==S("gameState")&&D(t),!0)},Oe.mobileKeybinds=J,Oe.donationsTracker=N,Oe.playerList=M,Oe.hoveringTooltip=W,Oe.clanFilter=B,Oe.wins=a,Oe.customLobby=Le,console.log("Successfully loaded FX Client")})(); \ No newline at end of file diff --git a/game.js b/game.js index 1b71a82..faa4426 100644 --- a/game.js +++ b/game.js @@ -434,27 +434,15 @@ function f2() { f3 && (f3.onclose = null, f3.onopen = null, f3 = null) } window.addEventListener("error", function f5(f6) { - window.removeEventListener("error", f5); - return alert("Error:\n" + f6.filename + " " + f6.lineno + " " + f6.colno + " " + f6.message); f4 = ""; try { - var bW; - if (window.removeEventListener("error", f5), f4 = f6.lineno + " " + f6.colno + "|" + function(f6) { - if (!f6.error) return "NoStack"; - var stack = f6.error.stack; - if (!stack || !stack.length) return "NoStack"; - for (var match, fM = new RegExp(":([0-9]+):([0-9]+)", "g"), result = []; null !== (match = fM.exec(stack));) result.push(parseInt(match[1], 10)), result.push(parseInt(match[2], 10)); - return result.length ? result.join(" ") : "NoStack" - }(f6), performance.memory && ((bW = []).push(Math.floor(performance.memory.jsHeapSizeLimit / 1e5)), bW.push(Math.floor(performance.memory.totalJSHeapSize / 1e5)), bW.push(Math.floor(performance.memory.usedJSHeapSize / 1e5)), - f4 = f4 + "|" + bW.join(" ")), f4 = (f4 = r ? f4 + "|R" + r.d + "," + r.an + "," + r.f8.toFixed(2) : f4) + "|" + f6.message, f9 && 1 === f9.fA) { - if (f4 = f9.fB + "|" + fC.fD.join(",") + "|" + f4, f6.lineno < 43) return void fE(); - var e8 = "What happened? Please send us a detailed email to davidtschacher@gmail.com so we can fix this error!"; - e8 += "
Error Message: " + f4, fC.fF(), d2.da(4, 5, new fG("🤖 Beep Boop! An error occurred.", e8, !0, [new dF("Close", function() { - d2.fH(), d2.d4.fI() - }), new dF("Reload", function() { - n.o.fJ() - })])) - } + return window.removeEventListener("error", f5), f4 = f6.lineno + " " + f6.colno + "|" + function(f6) { + if (!f6.error) return "NoStack"; + var stack = f6.error.stack; + if (!stack || !stack.length) return "NoStack"; + for (var match, fM = new RegExp(":([0-9]+):([0-9]+)", "g"), result = []; null !== (match = fM.exec(stack));) result.push(parseInt(match[1], 10)), result.push(parseInt(match[2], 10)); + return result.length ? result.join(" ") : "NoStack" + }(f6), __fx.utils.reportError(f6, f4), alert("Error:\n" + f6.filename + " " + f6.lineno + " " + f6.colno + " " + f6.message) } catch (f6) { f4 = "SE|" + f4 + "|" + f6, console.log(f4), alert(f4) } diff --git a/index.html b/index.html index a0db3c3..8b522a1 100644 --- a/index.html +++ b/index.html @@ -35,7 +35,7 @@ - +