const dictionary = {"gIsTeamGame":"iN","game":"a1","playerId":"e2","playerData":"aV","playerNames":"a1G","gameState":"v9","fontSize":"fontSize","x":"eP","y":"eR","canvas":"h1","gHumans":"jN","playerStates":"yb","fontGeneratorFunction":"b2.ow.q5","rawPlayerNames":"w2","playerBalances":"gK","playerTerritories":"g6","gIsSingleplayer":"jn","gLobbyMaxJoin":"st","SingleplayerMenu":"aO","getSingleplayerPlayerCount":"zd","gMaxPlayers":"eF","gBots":"jk","strs":"a5M","uiSizes":"bR","gap":"gap","i":"z"}; (()=>{"use strict";function e(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),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 t={count:0,removeWins:function(){confirm("Do you really want to reset your wins?")&&(t.count=0,localStorage.removeItem("fx_winCount"),alert("Successfully reset wins"))}};null!==localStorage.getItem("fx_winCount")&&(t.count=localStorage.getItem("fx_winCount"));const n=t;var o={};function i(e){!1!==o[e].isOpen&&(o[e].isOpen=!1,o[e].element.style.display="none",void 0!==o[e].onClose&&o[e].onClose())}function a(){Object.values(o).forEach((function(e){i(e.name)}))}document.getElementById("canvasA").addEventListener("mousedown",a),document.getElementById("canvasA").addEventListener("touchstart",a,{passive:!0}),document.addEventListener("keydown",(e=>{"Escape"===e.key&&a()}));const s={add:function(e){o[e.name]=e,o[e.name].isOpen=!1},openWindow:function(e,...t){!0!==o[e].isOpen&&(void 0!==o[e].beforeOpen&&o[e].beforeOpen(...t),o[e].isOpen=!0,o[e].element.style.display=null)},closeWindow:i,closeAll:a};window.__fx=window.__fx||{};const l=window.__fx;var r={displayWinCounter:!0,useFullscreenMode:!1,hoveringTooltip:!0,realisticNames:!1,showPlayerDensity:!0,coloredDensity:!0,densityDisplayStyle:"percentage",highlightClanSpawns:!1,customBackgroundUrl:"",attackPercentageKeybinds:[]};l.settings=r;const c=["hideAllLinks","fontName"];l.makeMainMenuTransparent=!1;const d=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:n.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:"highlightClanSpawns",type:"checkbox",label:"Highlight clan spawnpoints",note:"Increases the spawnpoint glow size for members of your clan"},{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."},e],o=document.querySelector(".settings .scrollable");var i={},a={},d=[];t.forEach((e=>{if("function"==typeof e){const t=document.createElement("div");return d.push(new e(t)),o.append(t)}const t=document.createElement("label");e.tooltip&&(t.title=e.tooltip);const n=e.type.endsWith("Input"),s=document.createElement(n||"checkbox"===e.type?"input":"selectMenu"===e.type?"select":"button");if("textInput"===e.type&&(s.type="text"),e.placeholder&&(s.placeholder=e.placeholder),(n||"selectMenu"===e.type)&&(i[e.for]=s),e.text&&(s.innerText=e.text),e.action&&s.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,s.append(t)})),t.append(s),"checkbox"===e.type){s.type="checkbox";const n=document.createElement("span");n.className="checkmark",t.className="checkbox",t.append(n),a[e.for]=s}else t.append(document.createElement("br"));o.append(t,document.createElement("br"))})),this.save=function(){Object.keys(i).forEach((function(e){r[e]=i[e].value.trim()})),Object.keys(a).forEach((function(e){r[e]=a[e].checked})),this.applySettings(),s.closeWindow("settings"),c.forEach((e=>delete r[e])),localStorage.setItem("fx_settings",JSON.stringify(r)),window.location.reload()};const u=document.createElement("input");function h(e){const t=e.target,n=t.files[0];if(!n)return;if(t.removeEventListener("change",h),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')&&(l.settings=r=e),localStorage.setItem("fx_settings",JSON.stringify(r)),window.location.reload()}catch(e){alert("Error\n"+e)}},o.readAsText(n)}u.type="file",this.importFromFile=function(){u.click(),u.addEventListener("change",h)},this.exportToFile=function(){var e,t,n;e=JSON.stringify(r),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=r[e]})),Object.keys(a).forEach((function(e){a[e].checked=r[e]})),d.forEach((e=>e.update(r)))},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(){if(r.useFullscreenMode&&document.fullscreenEnabled){function e(){null===document.fullscreenElement&&document.documentElement.requestFullscreen({navigationUI:"hide"}).then((()=>{console.log("Fullscreen mode activated")})).catch((e=>{console.warn("Could not enter fullscreen mode:",e)}))}document.addEventListener("mousedown",e,{once:!0}),document.addEventListener("click",e,{once:!0})}""!==r.customBackgroundUrl&&(document.body.style.backgroundImage="url("+r.customBackgroundUrl+")",document.body.style.backgroundSize="cover",document.body.style.backgroundPosition="center"),l.makeMainMenuTransparent=""!==r.customBackgroundUrl}};s.add({name:"settings",element:document.querySelector(".settings"),beforeOpen:function(){d.syncFields()}}),null!==localStorage.getItem("fx_settings")&&(l.settings=r={...r,...JSON.parse(localStorage.getItem("fx_settings"))}),d.applySettings();const u=d;function h(){return r}const y=["playerTerritories","playerBalances","rawPlayerNames"],p=["playerId","gIsTeamGame","gHumans","gLobbyMaxJoin","gameState","gIsSingleplayer"],m=e=>y.includes(e)?window[dictionary.playerData][dictionary[e]]:p.includes(e)?window[dictionary.game][dictionary[e]]:window[dictionary[e]],f=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 i=t/this.tabLabels.length,a=n+this.tabBarOffset/2;this.tabLabels.forEach(((t,s)=>{0!==s&&e.fillRect(i*s,n,1,this.tabBarOffset),this.selectedTab===s&&(e.fillStyle=o,e.fillRect(i*s,n,i,this.tabBarOffset),e.fillStyle="rgb(255,255,255)"),this.tabHovering===s&&(e.fillStyle="rgba(255,255,255,0.3)",e.fillRect(i*s,n,i,this.tabBarOffset),e.fillStyle="rgb(255,255,255)"),e.fillText(t,i*s+i/2,a)}))},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=m("playerId"),t=this.parseClanFromPlayerName(m("rawPlayerNames")[e]);m("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,g.refresh()}},g=new function(){this.inOwnClan=new Array(512),this.inOwnClan.fill(!1),this.refresh=()=>{const e=m("gHumans"),t=f.parseClanFromPlayerName(m("rawPlayerNames")[m("playerId")]);null===t?this.inOwnClan.fill(!1):m("rawPlayerNames").forEach(((n,o)=>{this.inOwnClan[o]=o/g,">").replace(/"/g,""").replace(/'/g,"'")}s.add({name:"donationHistory",element:document.querySelector("#donationhistory"),beforeOpen:function(e){document.getElementById("donationhistory_note").style.display="none"},onClose:function(){v.openedWindowPlayerID=null}});const v=new function(){function e(e,t,n,o){const i=m("rawPlayerNames"),a=document.createElement("tr");o&&a.setAttribute("class","new");let s=`${t}. `;return n===e[1]?s+=`Received ${e[2]} resources from ${b(i[e[0]])}`:s+=`Sent ${e[2]} resources to ${b(i[e[1]])}`,s+="",a.innerHTML=s,a}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 i=[t,n,o];if(this.donationHistory[n].push(i),this.donationHistory[t].push(i),this.openedWindowPlayerID===t||this.openedWindowPlayerID===n){const o=this.donationHistory[this.openedWindowPlayerID===t?t:n].length;this.contentElement.prepend(e(i,o,this.openedWindowPlayerID,!0))}},this.displayHistory=function(t,n=m("rawPlayerNames"),o=m("gIsSingleplayer")){var i=v.getHistoryOf(t);console.log("History for "+n[t]+":"),console.log(i),document.querySelector("#donationhistory h1").innerHTML="Donation history for "+b(n[t]),this.contentElement.innerHTML="",i.length>0?i.forEach(((n,o)=>{this.contentElement.appendChild(e(n,i.length-o,t))})):this.contentElement.innerText="Nothing to display",this.openedWindowPlayerID=t,s.openWindow("donationHistory",o)}},w=v,E=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&&m("gIsTeamGame")&&(s.closeWindow("playerList"),w.displayHistory(t))})),this.display=function(e){const t=m("gHumans"),n=m("gLobbyMaxJoin");let o=`

Players (${t})

`;for(let i=0;iBots (${n-t})`),o+=`${i+1}. ${b(e[i])}`;document.getElementById("playerlist_content").innerHTML=o,document.getElementById("playerlist_content").setAttribute("class",m("gIsTeamGame")?"clickable":""),s.openWindow("playerList")},this.hoveringOverButton=!1,this.drawButton=(t,n,o,i)=>{t.fillRect(n,o,i,i),t.fillStyle=this.hoveringOverButton?"#aaaaaaaa":"#000000aa",t.clearRect(n+1,o+1,i-2,i-2),t.fillRect(n+1,o+1,i-2,i-2),t.fillStyle="#ffffff",t.imageSmoothingEnabled=!0,t.drawImage(e,n+2,o+2,i-4,i-4),t.imageSmoothingEnabled=!1}};s.add({name:"playerList",element:document.getElementById("playerlist")});const k=E,T=new function(){this.getMaxTroops=function(e,t){return(150*e[t]).toString()},this.getDensity=function(e,t=m("playerBalances"),n=m("playerTerritories")){return"percentage"===h().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,i,a){return e>=n&&e<=n+i&&t>=o&&t<=o+a},this.fillTextMultiline=function(e,t,n,o,i){const a=parseInt(e.font.split(" ").find((e=>e.endsWith("px"))).slice(0,-2));t.split("\n").forEach(((t,s)=>e.fillText(t,n,o+s*a,i)))},this.textStyleBasedOnDensity=function(e){const t=m("playerBalances"),n=m("playerTerritories");return`hsl(${t[e]/(1.5*n[e])}, 100%, 50%, 1)`}},x=new function(){let e=!1;function t(t){if(!h().hoveringTooltip||!m("gameState")||e)return;let n,o;if(t.type.includes("touch")){const{touches:e,changedTouches:i}=t.originalEvent??t,a=e[0]??i[0];n=a.pageX,o=a.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))},O={setAbsolute:()=>{},setRelative:()=>{}};window.__fx=window.__fx||{};const I=window.__fx;I.version="0.6.5.6 Oct 3",I.settingsManager=u,I.leaderboardFilter=f,I.utils=T,I.WindowManager=s,I.keybindFunctions=O,I.keybindHandler=e=>{const t=h().attackPercentageKeybinds.find((t=>t.key===e));return void 0!==t&&("absolute"===t.type?O.setAbsolute(t.value):O.setRelative(t.value),!0)},I.donationsTracker=w,I.playerList=k,I.hoveringTooltip=x,I.clanFilter=g,I.wins=n,console.log("Successfully loaded FX Client")})();