import assets from '../assets.js'; import ModUtils from '../modUtils.js'; export default (/** @type {ModUtils} */ modUtils) => { // Disable built-in Territorial.io error reporting modUtils.insertCode( `window.removeEventListener("error", err); msg = e.lineno + " " + e.colno + "|" + getStack(e); /* here */`, `__fx.reportError(e, msg); return alert("Error:\\n" + e.filename + " " + e.lineno + " " + e.colno + " " + e.message);` ) // Render win count modUtils.insertCode(`var s = Math.floor((Device.a1.largeUIEnabled() ? 0.018 : 0.0137) * h___.hz); ctx.font = Util.qd.sS(0, Math.max(5, s)); Util.qd.textBaseline(ctx, 0); Util.qd.textAlign(ctx, 2); ctx.fillStyle = Colors.nl; ctx.fillText(clientInfo.gameVersion, h___.w, 0); /* here */`, `const text = "Win count: " + __fx.wins.count; const textLength = ctx.measureText(text).width; const size = Math.max(5, s); ctx.textAlign = "left"; ctx.textBaseline = "middle"; ctx.fillText(text, ctx.canvas.width - textLength - size / 2, size * 2);`) // Make the main canvas context have an alpha channel if a custom background is being used modUtils.insertCode(`mainCanvasElement = document.getElementById("canvasA"); if (Device.id === 2) { mainCanvasElement.style.webkitUserSelect = "none"; } mainCanvas = mainCanvasElement.getContext("2d", { alpha: /* here */ false });`, `__fx.makeMainMenuTransparent ? true :`) modUtils.waitForMinification(() => applyPatches(modUtils)) } //export const requiredVariables = ["game", "playerId", "playerData", "rawPlayerNames", "gIsSingleplayer", "playerTerritories"]; function applyPatches(/** @type {ModUtils} */ { replace, replaceOne, replaceRawCode, safeDictionary, matchOne, matchRawCode, escapeRegExp }) { // Constants for easy usage of otherwise long variable access expressions const dict = safeDictionary; const playerId = `${dict.game}.${dict.playerId}`; const rawPlayerNames = `${dict.playerData}.${dict.rawPlayerNames}`; const gIsSingleplayer = `${dict.game}.${dict.gIsSingleplayer}`; // Replace assets replaceOne(/(\(4,"crown",4,")[^"]+"\),/g, "$1" + assets.crownIcon + "\"),"); replaceOne(/(\(6,"territorial\.io",6,")[^"]+"\),/g, "$1" + assets.fxClientLogo + "\"),"); replaceOne(/(\(22,"logo",8,")[^"]+"\)/g, "$1" + assets.smallLogo + "\")"); // Add FX Client version info to the game version window replaceRawCode(`ar.oa(4,1,new s8(__L(),c.gameVersion+"
"+ah.aC5+"",`, `ar.oa(4,1,new s8(__L(),c.gameVersion+"
"+ah.aC5+"" + "

" + "FX Client v" + __fx.version + "
FX Client Discord server" + "
Github repository
",`); // Add update information replaceRawCode(`new k("🚀 New Game Update","The game was updated! Please reload the game.",!0,[`, `new k("🚀 New Game Update","The game was updated! Please reload the game." + "

FX Client is not yet compatible with the latest version of the game.

Updates should normally be available within a few hours.
You can still use FX to play in singleplayer mode.

",!0,[` ); // Max size for custom maps: from 4096x4096 to 8192x8192 // TODO: test this; it might cause issues with new boat mechanics? { // Add Troop Density and Maximum Troops in side panel const { valuesArray } = replaceRawCode(`,labels[5]=__L(0,"Interest"),labels[6]=__L(),labels[7]=__L(),(truncatedLabels=new Array(labels.length)).fill(""),(valuesArray=new Array(labels.length))[0]=game.io?`, `,labels[5]=__L(0,"Interest"),labels[6]=__L(),labels[7]=__L(), labels.push("Max Troops", "Density"), // add labels (truncatedLabels=new Array(labels.length)).fill(""),(valuesArray=new Array(labels.length))[0]=game.io?`); replaceOne(new RegExp(/(:(?\w+)<7\?\w+\.\w+\.\w+\(valuesArray\[\2\]\)):(\w+\.\w+\(valuesArray\[7\]\))}/ .source.replace(/valuesArray/g, valuesArray), "g"), '$1 : $ === 7 ? $3 ' + `: $ === 8 ? __fx.utils.getMaxTroops(${dict.playerData}.${dict.playerTerritories}, ${playerId}) ` + `: __fx.utils.getDensity(${playerId}) }`); // increase the size of the side panel by 25% to make the text easier to read replaceOne(/(this\.\w+=Math\.floor\(\(\w+\.\w+\.\w+\(\)\?\.1646:\.126\))\*(\w+\.\w+\),)/g, "$1 * 1.25 * $2"); } // Increment win counter on wins replaceRawCode(`=function(sE){a8.gD[sE]&&(o.ha(sE,2),b.h9<100?xD(0,__L([a8.jx[sE]]),3,sE,ad.gN,ad.kl,-1,!0):xD(0,__L([a8.jx[sE]]),3,sE,ad.gN,ad.kl,-1,!0))`, `=function(sE){ if (${playerId} === sE && !${gIsSingleplayer} && !${dict.game}.${dict.gIsReplay}) __fx.wins.count++, window.localStorage.setItem("fx_winCount", __fx.wins.count), xD(0,"Your Win Count is now " + __fx.wins.count,3,sE,ad.gN,ad.kl,-1,!0); a8.gD[sE]&&(o.ha(sE,2),b.h9<100?xD(0,__L([a8.jx[sE]]),3,sE,ad.gN,ad.kl,-1,!0):xD(0,__L([a8.jx[sE]]),3,sE,ad.gN,ad.kl,-1,!0))`); { // Add settings button and custom lobby button // add buttons replaceRawCode(`,new nQ("☰
"+__L(),function(){aD6(3)},aa.ks),new nQ("",function(){at.d5(12)},aa.kg,!1)]`, `,new nQ("☰
"+__L(),function(){aD6(3)},aa.ks),new nQ("",function(){at.d5(12)},aa.kg,!1), new nQ("FX Client settings", function() { __fx.WindowManager.openWindow("settings"); }, "rgba(0, 0, 20, 0.5)"), new nQ("Join/Create custom lobby", function() { __fx.customLobby.showJoinPrompt(); }, "rgba(20, 9, 77, 0.5)")]`) // set position replaceRawCode(`aZ.g5.vO(aD3[3].button,x+a0S+gap,a3X+h+gap,a0S,h);`, `aZ.g5.vO(aD3[3].button,x+a0S+gap,a3X+h+gap,a0S,h); aZ.g5.vO(aD3[5].button, x, a3X + h * 2 + gap * 2, a0S * 2 + gap, h / 3); aZ.g5.vO(aD3[6].button, x, a3X + h * 2.33 + gap * 3, a0S * 2 + gap, h / 3);`); } { // Keybinds // match required variables const { 0: match, groups: { attackBarObject, setRelative } } = matchOne(/:\w+\.\w+\(\w+,8\)\?(?\w+)\.(?\w+)\(32\/31\):/g); // create a setAbsolutePercentage function on the attack percentage bar object, // and also register the keybind handler functions replaceOne(/}(function \w+\((\w+)\){return!\(1<\2&&1===(?\w+)\|\|\(1<\2&&\2\*\3-\3<1\/1024\?\2=\(\3\+1\/1024\)\/\3:\2<1)/g, "} this.setAbsolutePercentage = function(newPercentage) { $ = newPercentage; }; " + "__fx.keybindFunctions.setAbsolute = this.setAbsolutePercentage; " + `__fx.keybindFunctions.setRelative = (arg1) => ${attackBarObject}.${setRelative}(arg1); $1`); // insert keybind handling code into the keyDown handler function replaceOne(new RegExp(/(function \w+\((?\w+)\){)([^}]+matched)/g.source.replace(/matched/g, escapeRegExp(match)), "g"), "$1 if (__fx.keybindHandler($.key)) return; $3"); } // Set the default font to Trebuchet MS replace(/sans-serif"/g, 'Trebuchet MS"'); // Realistic bot names setting // matches c4[i] = c4[i].replace(a6U[dx], a6V[dx]) replaceOne(/(((\w+)\[\w+\])=\2\.replace\(\w+(\[\w+\]),\w+\4\))/g, "$1; if (__fx.settings.realisticNames) $3 = realisticNames;") // Hide all links in main menu depending on settings //replaceOne(/(this\.\w+=function\(\){)((\w+\.\w+)\[2\]=\3\[3\]=\3\[4\]=(?!this\.\w+\.\w+),)/g, //"$1 if (settings.hideAllLinks) $3[0] = $3[1] = $; else $3[0] = $3[1] = true; $2") // Clear canvas background if a custom background is being used replaceRawCode(`,this.qk=function(){var a4n,a4m;aq.pd?(a4m=aL.gA/aq.eE,a4n=aL.gF/aq.eF,canvas.setTransform(a4m=a4n>1&1?(ctx.lineWidth=.05*fontSize,ctx.strokeStyle=a9U(fontSize,a9S%2),ctx.strokeText(i,x,y)):(1>1&1?(ctx.lineWidth=.05*fontSize,ctx.strokeStyle=a9U(fontSize,a9S%2),ctx.strokeText(i,x,y)):(1\w+),(?\w+),(?\w+),(?\w+),(?\w+)\){)(\6\.fillText\((?\w+)\.(?\w+)\[\2\],\4,\5\)),(\2<(?\w+)\.(?\w+)&&2!==\8\.(?\w+)\[[^}]+)}/g, `$1 var ___id = $2; var showName = $ < $.$ || !__fx.settings.hideBotNames; if (showName) $7, $10; ${placeBalanceAbove} && __fx.settings.showPlayerDensity && ( __fx.settings.coloredDensity && ($.fillStyle = __fx.utils.textStyleBasedOnDensity(___id)), $.fillText(__fx.utils.getDensity(___id), $, showName ? $ + $ : $) ); }`); } // Detailed team pie chart percentage replaceRawCode(`qr=Math.floor(100*f0+.5)+"%"`, `qr = (__fx.settings.detailedTeamPercentage ? (100*f0).toFixed(2) : Math.floor(100*f0+.5)) + "%"`) replaceRawCode(",fontSize=+dz*Math.min(f0,.37);", ",fontSize=(__fx.settings.detailedTeamPercentage ? 0.75 : 1)*dz*Math.min(f0,.37);") // Invalid hostname detection avoidance replaceRawCode(`,this.hostnameIsValid=0<=window.location.hostname.toLowerCase().indexOf("territorial.io"),`, `,this.hostnameIsValid=true,`) console.log('Removing ads...'); // Remove ads replace('//api.adinplay.com/libs/aiptag/pub/TRT/territorial.io/tag.min.js', ''); }