Compare commits

...

2 Commits

Author SHA1 Message Date
peshomir cc7876639c Merge branch 'main' of https://github.com/fxclient/FXclient 2024-06-01 14:23:45 +03:00
peshomir 3db6d61262 Update v0.6.4.3 - Fixes for game versions ^1.94.6
Improved the build script code replacement errors: now the problematic raw code is shown too, previously it would just display a "no match" error for the regular expression;
Removed some old commented code from the build script;
In the build script, added constants for easy usage of otherwise long variable access expressions (like the playerId variable which is now a property of the "game" object, meaning you would have to reference the dictionary twice, which is long and repetitive; now you can just use the computed playerId constant)
2024-06-01 14:23:42 +03:00
2 changed files with 60 additions and 78 deletions

126
build.js
View File

@ -25,7 +25,7 @@ const escapeRegExp = (string) => string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&')
//const dictionary = { __dictionaryVersion: '1.90.0 4 Feb 2024', playerId: 'bB', playerNames: 'hA', playerBalances: 'bC', playerTerritories: 'bj', gIsSingleplayer: 'fc', gIsTeamGame: 'cH' };
//if (!script.includes(`"${dictionary.__dictionaryVersion}"`)) throw new Error("Dictionary is outdated.");
let dictionary = {};
const dictionary = {};
const matchDictionaryExpression = expression => {
result = expression.exec(script);
@ -43,7 +43,11 @@ const replaceRawCode = (/** @type {string} */ raw, /** @type {string} */ result,
return groups.hasOwnProperty(match) ? "$" + groups[match] : match;
});
//console.log(replacementString);
const expressionMatchResult = replaceOne(expression, replacementString);
let expressionMatchResult;
try { expressionMatchResult = replaceOne(expression, replacementString); }
catch (e) {
throw new Error("replaceRawCode match error:\n\n" + e + "\n\nRaw code: " + raw + "\n");
}
return Object.fromEntries(Object.entries(groups).map(([identifier, groupNumber]) => [identifier, expressionMatchResult[groupNumber]]));
}
const matchRawCode = (/** @type {string} */ raw, nameMappings) => {
@ -70,22 +74,18 @@ const generateRegularExpression = (/** @type {string} */ code, /** @type {boolea
}
[
///=(?<gIsSingleplayer>\w+)\?"Players":"Bots"/g,
/,(?<gIsTeamGame>\w+)=\(\w+=\w+\)<7\|\|9===\w+,/g,
/=function\((\w+),(\w+),\w+\){\1===(?<playerId>\w+)\?\w+\(175,\w+\.\w+\(18,\[(?<playerNames>\w+)\[\2\]\]\),1001,\2,\w+\(/g,
// this one broke in 1.91.3 /{\w+===(?<playerId>\w+)\?\w+\(175," Message to "/g,
/\w+\.\w+\((\w+)\)\?\w+\.\w+\(\1\)\?(\w+)=(\w+\.\w+)\(13,\[\2\]\):\(\w+=\w+\.\w+\(\1\),\2=\3\(14,\[(?<playerNames>\w+)\[(\w+)\],(\w+\.\w+\.\w+\()(?<playerBalances>\w+)\[\5\]\),\6(?<playerTerritories>\w+)\[\5\]\),\2\]\),\w+=!0\):\2=/g,
// this one also broke in 1.91.3 /,\w+="Player: "\+(?<playerNames>\w+)\[\w+\],\w+=\(\w\+=" Balance: "\+\w+\.\w+\((?<playerBalances>\w+)\[\w+\]\)\)\+\(" Territory: "\+\w+\.\w+\((?<playerTerritories>\w+)\[\w+\]\)\)\+\(" Coords: "/g,
///\((?<uiOffset>\w+)=Math\.floor\(\(\w+\?\.0114:\.01296\)\*\w+\)\)/g,
/(function \w+\((\w+),(\w+),(\w+),(\w+),(\w+)\){\6\.fillText\((?<playerNames>\w+)\[\2\],\4,\5\)),(\2<(?<gHumans>\w+)&&2!==(?<playerStates>\w+)\[)/g,
/,\w+=512,(?<gLobbyMaxJoin>\w+)=\w+,(?<gIsSingleplayer>\w+)&&\(\1=\w+\.\w+\(\)\),\w+=\1-\w+,\w+=0,/g,
/function \w+\(\)\{if\(2===(?<gameState>\w+)\)return 1;\w+\.\w+\(\),\1=2,\w+=\w+\}/g
/,this\.(?<gIsTeamGame>\w+)=this\.\w+<7\|\|9===this\.\w+,/g,
/=function\((\w+),(\w+),\w+\){\1===(?<game>\w+)\.(?<playerId>\w+)\?\w+\(175,\w+\.\w+\(18,\[(?<playerData>\w+)\.(?<playerNames>\w+)\[\2\]\]\),1001,\2,\w+\(/g,
/\w+\.\w+\((\w+)\)\?\w+\.\w+\(\1\)\?(\w+)=(\w+\.\w+)\(13,\[\2\]\):\(\w+=\w+\.\w+\(\1\),\2=\3\(14,\[\w+\.\w+\.\w+\((?<playerData>\w+)\.(?<rawPlayerNames>\w+)\[(\w+)\],\w+\.\w+\.\w+\(0,10\),150\),(\w+\.\w+\.\w+\()\4\.(?<playerBalances>\w+)\[\6\]\),\7\4\.(?<playerTerritories>\w+)\[\6\]\),\2\]\),\w+=!0\):\2=/g,
/,this\.(?<gLobbyMaxJoin>\w+)=this\.(?<gMaxPlayers>\w+),this\.(?<gIsSingleplayer>\w+)&&\(this\.\1=\w+\.\w+\(\)\),this\.\w+=this\.\1-this\.\w+,this\.\w+=0,/g,
/function \w+\(\)\{if\(2===(?<game>\w+)\.(?<gameState>\w+)\)return 1;\w+\.\w+\(\),\1\.\2=2,\1\.\w+=\1.\w+\}/g,
/(function \w+\((\w+),(?<fontSize>\w+),(?<x>\w+),(?<y>\w+),(?<canvas>\w+)\){)(\6\.fillText\((?<playerData>\w+)\.(?<playerNames>\w+)\[\2\],\4,\5\)),(\2<(?<game>\w+)\.(?<gHumans>\w+)&&2!==\8\.(?<playerStates>\w+)\[[^}]+)}/g
].forEach(matchDictionaryExpression);
const rawCodeSegments = [
"[0]=aV.nU[70],a0T[1]=@gIsSingleplayer?aV.nU[71]:aV.nU[72],",
"[0]=@Translations.@txt[70],@strs[1]=@game.@gIsSingleplayer?@Translations.txt[71]:@Translations.txt[72],",
"?(this.gB=Math.floor(.0536*aK.fw),g5=aK.g5-4*@uiSizes.@gap-this.gB):",
`for(a0L=new Array(@gMaxPlayers),a0A.font=a07,@i=gMaxPlayers-1;0<=i;i--)a0L[i]=i+1+".",@playerNames[i]=aY.qW.tm(@rawPlayerNames[i],a07,a0W),a0K[i]=Math.floor(a0A.measureText(playerNames[i]).width);`
`for(a0L=new Array(@game.@gMaxPlayers),a0A.font=a07,@i=game.gMaxPlayers-1;0<=i;i--)a0L[i]=i+1+".",@playerData.@playerNames[i]=aY.qW.tm(playerData.@rawPlayerNames[i],a07,a0W),a0K[i]=Math.floor(a0A.measureText(playerData.playerNames[i]).width);`,
]
rawCodeSegments.forEach(code => {
@ -96,20 +96,18 @@ rawCodeSegments.forEach(code => {
fs.writeFileSync("./build/fx_core.js", `const dictionary = ${JSON.stringify(dictionary)};\n` + fs.readFileSync("./build/fx_core.js").toString());
// Constants for easy usage of otherwise long variable access expressions
const dict = dictionary;
const playerId = `${dict.game}.${dict.playerId}`;
const rawPlayerNames = `${dict.playerData}.${dict.rawPlayerNames}`;
const gIsSingleplayer = `${dict.game}.${dict.gIsSingleplayer}`;
// Replace assets
const assets = require('./assets.js');
replaceOne(/(\(4,"crown",4,")[^"]+"\),/g, "$1" + assets.crownIcon + "\"),");
replaceOne(/(\(6,"territorial\.io",6,")[^"]+"\),/g, "$1" + assets.fxClientLogo + "\"),");
/*// Add FXClient menu item in "More" menu
// match },ug[0][5]={name:a79,id:5,mf:90,oU:0,e8:0},
replaceOne(/(},(\w+\[0\])\[\d+\]={(\w+):\w+,(\w+):\d+,(\w+):90,(\w+):0,(\w+):0},)/g,
'$1$2.push({$3:"FX Client v" + fx_version + " " + fx_update, $4: 20, $5: 0, $6: 0, $7: 70}),');
// Do not display hover effect on the last 2 items (territorial.io version and FX Client version) instead of only the last item
// match 0 === a9P ? ug[a9P].length - 1 : ug[a9P].length : 1,
replaceOne(/(0===(\w+)\?(\w+)\[\2\]\.length)-1:(\3\[\2\]\.length:1,)/g, "$1 - 2 : $4");*/
// Add FX Client version info to the game version window
//replaceRawCode(`ar.aAx("MenuGameVersion")||ar.aAz(new aB3(" "+aV.nU[84],gameVersion+"<br><a href='"`,
replaceRawCode(`ar.oa(4,1,new s8(" "+Translations.txt[84],gameVersion+"<br><a href='"+ah.aC5+"' target='_blank'>"+ah.aC5+"</a>",`,
`ar.oa(4,1,new s8(" "+Translations.txt[84],gameVersion+"<br><a href='"+ah.aC5+"' target='_blank'>"+ah.aC5+"</a>"
+ "<br><br><b>" + "FX Client v" + fx_version + " " + fx_update + "<br><a href='https://discord.gg/dyxcwdNKwK' target='_blank'>FX Client Discord server</a>"
@ -119,47 +117,30 @@ replaceRawCode(`ar.oa(4,1,new s8(" "+Translations.txt[84],gameVersion+"<br
// TODO: test this; it might cause issues with new boat mechanics?
{ // Add Troop Density and Maximum Troops in side panel
/*const { groups: { valuesArray } } = replaceOne(/(,(?<labelsArray>\w+)\[\d\]="Interest",\2\[\d\]="Income",\2\[\d\]="Time"),(\w+=\w+-\w+\(\w+,100\),\((?<valuesArray>\w+)=new Array\(\2\.length\)\)\[0\]=\w+)/g,
'$1, $<labelsArray>.push("Max Troops", "Density"), $3'); // add labels*/
const { valuesArray } = replaceRawCode(`,labels[5]=aV.nU[76],labels[6]=aV.nU[77],labels[7]=aV.nU[78],a0Z=tn-eT(tn,100),(valuesArray=new Array(labels.length))[0]=io?`,
const { valuesArray } = replaceRawCode(`,labels[5]=aV.nU[76],labels[6]=aV.nU[77],labels[7]=aV.nU[78],a0Z=game.tn-eT(game.tn,100),(valuesArray=new Array(labels.length))[0]=game.io?`,
`,labels[5]=aV.nU[76],labels[6]=aV.nU[77],labels[7]=aV.nU[78],
labels.push("Max Troops", "Density"), // add labels
a0Z=tn-eT(tn,100),(valuesArray=new Array(labels.length))[0]=io?`);
a0Z=game.tn-eT(game.tn,100),(valuesArray=new Array(labels.length))[0]=game.io?`);
replaceOne(new RegExp(/(:(?<valueIndex>\w+)<7\?\w+\.\w+\.\w+\(valuesArray\[\2\]\)):(\w+\.\w+\(valuesArray\[7\]\))}/
.source.replace(/valuesArray/g, valuesArray), "g"),
'$1 : $<valueIndex> === 7 ? $3 '
+ `: $<valueIndex> === 8 ? utils.getMaxTroops(${dictionary.playerTerritories}, ${dictionary.playerId}) `
+ `: utils.getDensity(${dictionary.playerId}) }`);
+ `: $<valueIndex> === 8 ? utils.getMaxTroops(${dict.playerData}.${dict.playerTerritories}, ${playerId}) `
+ `: 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
/*replaceOne(/(=function\((\w+)\){)([^}]+),((\w+\(0),\w+<100\?(\w+\.\w+)\(11,(\[\w+\[\w+\]\])\):\6\(12,\7\),(3,\2,[^()]+?\))),(?<end>[^}]+},)/g,
`$1 if (${dictionary.playerId} === $2) wins_counter++, window.localStorage.setItem("fx_winCount", wins_counter); ` +
`$3, $4, $5, "Your Current Win Count is Now " + wins_counter, $8, $<end>`);*/
replaceRawCode(`=function(rC){n.hQ(rC,2),vm(0,h2<100?aV.s9(4,[jm[rC]]):aV.s9(12,[jm[rC]]),3,rC,aZ.gG,aZ.ka,-1,!0),`,
`=function(rC){
if (${dictionary.playerId} === rC && !${dictionary.gIsSingleplayer})
replaceRawCode(`=function(sE){o.ha(sE,2),b.h9<100?xD(0,aZ.tG(4,[a8.jx[sE]]),3,sE,ad.gN,ad.kl,-1,!0):xD(0,aZ.tG(12,[a8.jx[sE]]),3,sE,ad.gN,ad.kl,-1,!0),`,
`=function(sE){
if (${playerId} === sE && !${gIsSingleplayer})
wins_counter++, window.localStorage.setItem("fx_winCount", wins_counter),
vm(0,"Your Win Count is now " + wins_counter,3,rC,aZ.gG,aZ.ka,-1,!0);
n.hQ(rC,2),vm(0,h2<100?aV.s9(4,[jm[rC]]):aV.s9(12,[jm[rC]]),3,rC,aZ.gG,aZ.ka,-1,!0),`);
xD(0,"Your Win Count is now " + wins_counter,3,sE,ad.gN,ad.kl,-1,!0);
o.ha(sE,2),b.h9<100?xD(0,aZ.tG(4,[a8.jx[sE]]),3,sE,ad.gN,ad.kl,-1,!0):xD(0,aZ.tG(12,[a8.jx[sE]]),3,sE,ad.gN,ad.kl,-1,!0),`);
{ // Add settings button and win count
// render gear icon and win count
/*// cV.textAlign=cX,cV.textBaseline=cW,a03(a9Y.gb,a9Y.gc,a9Y.m5,a9Y.tD,ug[a9P][0].mf,ug[a9P][0].oU,ug[a9P][0].e8,0===yk,ug[a9P][0].name),a9O))
// l(A.f3, A.f4, A.hw, A.nI, z[0].f7, z[0].mx, z[0].cm, 0 === t, z[0].name, .6);
// cH.drawImage(settingsGearIcon,A.f3-A.hw/2,A.f4,A.nI,A.nI);
// cH.font = bt + Math.floor(A.nI * 0.4) + bu;
// cH.fillText("Win count: " + wins_counter, Math.floor(A.f3 + A.hw / 2), Math.floor((A.f4 + A.nI / 2) * 2.1));
const { groups } = replaceOne(/((?<canvas>\w+)\.textAlign=\w+,\2\.textBaseline=\w+,\w+\((?<x>(?<coords>\w+).\w+),(?<y>\4.\w+),(?<w>\4.\w+),(?<h>\4.\w+),[^)]+\)),(?<end>(?<isMenuOpened>\w+)\)\))/g, '$1, ' +
'$<canvas>.imageSmoothingEnabled = true, ' +
'$<canvas>.drawImage(settingsGearIcon, $<x>-$<w>/2, $<y>, $<h>, $<h>), ' +
'$<canvas>.imageSmoothingEnabled = false, ' +
'$<canvas>.font = "bold " + Math.floor($<h> * 0.4) + "px " + settings.fontName, ' +
'(settings.displayWinCounter && !$<isMenuOpened> && $<canvas>.fillText("Win count: " + wins_counter, Math.floor($<x> + $<w> / 2), Math.floor(($<y> + $<h> / 2) * 2.1))), ' +
'$<end>');*/
replaceRawCode(`,fy=aV.nU[80],fontSize=.65*height,canvas.font=aY.g0.g1(1,fontSize),canvas.fillStyle="rgba("+gR+","+tD+","+hj+",0.6)",canvas.fillRect(x,y,width,height),`,
`,fy=aV.nU[80],fontSize=.65*height,
canvas.imageSmoothingEnabled = true,
@ -173,12 +154,6 @@ canvas.imageSmoothingEnabled = false,
canvas.font=aY.g0.g1(1,fontSize),canvas.fillStyle="rgba("+gR+","+tD+","+hj+",0.6)",canvas.fillRect(x,y,width,height),`);
// handle settings button click
/*replaceOne(/(this\.\w+=function\((?<mouseX>\w+),(?<mouseY>\w+)\){[^}]+?)if\((?<coordsGet>\w+=\w+\(\)),(?<isMenuOpened>\w+)\)(?<end>{for\([^}]+"Lobby ")/g,
'$1 $<coordsGet>; ' +
`var gearIconX = ${groups.x}-${groups.w}/2; ` +
// if (y > (C.f3-C.hw/2) && y < ((C.f3-C.hw/2)+C.nI) && A > C.f4 && A < (C.f4 + C.nI)) WindowManager.openWindow("settings");
`if ($<mouseX> > gearIconX && $<mouseX> < (gearIconX+${groups.h}) && $<mouseY> > ${groups.y} && $<mouseY> < (${groups.y}+${groups.h})) return WindowManager.openWindow("settings"); ` +
'if ($<isMenuOpened>) $<end>');*/
replaceRawCode(`(q6=Math.floor((b7.cv.fv()?.145:.09)*aK.fw),gap=Math.floor(.065*(b7.cv.fv()?.53:.36)*aK.fw),gap=aK.g5-q6-gap,jd=b0.gap,q6=Math.floor(.35*q6),gap<=mouseX&&mouseY<jd+q6&&ar.v2(1))`,
`(q6=Math.floor((b7.cv.fv()?.145:.09)*aK.fw),gap=Math.floor(.065*(b7.cv.fv()?.53:.36)*aK.fw),gap=aK.g5-q6-gap,jd=b0.gap,q6=Math.floor(.35*q6),
(gap <= mouseX && mouseY < jd + q6 && (ar.v2(1), true)) || (mouseX >= gap - q6 / 0.7 && mouseY < jd + q6 && WindowManager.openWindow("settings"))
@ -217,18 +192,18 @@ replaceOne(/(this\.\w+=function\(\){var (\w+),(\w+);)(\w+\.\w+\?\([^()]+setTrans
'$1 if (makeMainMenuTransparent) $<canvas>.clearRect($<wholeCanvas>); else $4')
// Track donations
replaceOne(/(this\.\w+=function\((\w+),(\w+)\)\{)(\2===\w+&&\(\w+\.\w+\((\w+\.\w+)\[0\],\5\[1\],\3\),this\.(\w+)\[12\]\+=\5\[1\],this\.\6\[16\]\+=\5\[0\]\),\3===\w+&&\()/g,
replaceOne(/(this\.\w+=function\((\w+),(\w+)\)\{)(\2===\w+\.\w+&&\(\w+\.\w+\((\w+\.\w+)\[0\],\5\[1\],\3\),this\.(\w+)\[12\]\+=\5\[1\],this\.\6\[16\]\+=\5\[0\]\),\3===\w+\.\w+&&\()/g,
"$1 donationsTracker.logDonation($2, $3, $5[0]); $4")
// Display donations for a player when clicking on them in the leaderboard
// and skip handling clicks when clicking on an empty space (see the isEmptySpace
// variable in the modified leaderboard click handler from the leaderboard filter)
// match , 0 !== dG[x]) && fq.hB(x, 800, false, 0),
replaceOne(/,(0!==\w+\[(\w+)\])(\)&&\w+\.\w+\(\2,800,!1,0\),)/g,
`, ${dictionary.gIsTeamGame} && donationsTracker.displayHistory($2, ${dictionary.rawPlayerNames}, ${dictionary.gIsSingleplayer}), $1 && !isEmptySpace $3`);
replaceOne(/,(0!==\w+\.\w+\[(\w+)\])(\)&&\w+\.\w+\(\2,800,!1,0\),)/g,
`, ${dict.game}.${dict.gIsTeamGame} && donationsTracker.displayHistory($2, ${rawPlayerNames}, ${gIsSingleplayer}), $1 && !isEmptySpace $3`);
// Reset donation history and leaderboard filter when a new game is started
replaceOne(new RegExp(`,${dictionary.playerBalances}=new Uint32Array\\(\\w+\\),`, "g"), "$& donationsTracker.reset(), leaderboardFilter.reset(), ");
replaceOne(new RegExp(`,this\\.${dictionary.playerBalances}=new Uint32Array\\(\\w+\\.\\w+\\),`, "g"), "$& donationsTracker.reset(), leaderboardFilter.reset(), ");
{ // Player list and leaderboard filter tabs
// Draw player list button
@ -239,7 +214,7 @@ replaceOne(new RegExp(`,${dictionary.playerBalances}=new Uint32Array\\(\\w+\\),`
// Handle player list button and leaderboard tabs mouseDown
// and create a function for scrolling the leaderboard to the top
replaceOne(/(this\.\w+=function\((?<x>\w+),(?<y>\w+)\){return!!\w+\(\2,\3\))&&(\(\w+=\w+\.\w+,[^}]+),!0\)/g,
`leaderboardFilter.scrollToTop = function(){position = 0;}, $1 && ((${buttonBoundsCheck} && playerList.display(${dictionary.rawPlayerNames}), true)
`leaderboardFilter.scrollToTop = function(){position = 0;}, $1 && ((${buttonBoundsCheck} && playerList.display(${rawPlayerNames}), true)
&& !($<y> - ${uiOffset} > leaderboardFilter.verticalClickThreshold && leaderboardFilter.handleMouseDown($<x> - ${uiOffset})) && $4),!0)`);
// Handle player list button and leaderboard tabs hover
// and create a function for repainting the leaderboard
@ -254,16 +229,17 @@ replaceOne(new RegExp(`,${dictionary.playerBalances}=new Uint32Array\\(\\w+\\),`
{ // Display density of other players
// Applies when the "Reverse Name/Balance" setting is off
const { groups: { settingsSwitchNameAndBalance } } = replaceOne(/(,(?<settingsSwitchNameAndBalance>\w+\.\w+\.\w+\[7\]\.\w+)\?(?<nameDrawingFunction>\w+)\(\w+,\w+,(?<x>\w+),(?<y>\w+)\+\.78\*(?<fontSize>\w+),(?<canvas>\w+)\)):(\7\.fillText\(\w+\.\w+\.\w+\(\w+\[(\w+)\]\),\4,\5\+\.78\*\6\))\)\)/g,
const { groups: { settingsSwitchNameAndBalance } } = replaceOne(/(,(?<settingsSwitchNameAndBalance>\w+\.\w+\.\w+\[7\]\.\w+)\?(?<nameDrawingFunction>\w+)\(\w+,\w+,(?<x>\w+),(?<y>\w+)\+\.78\*(?<fontSize>\w+),(?<canvas>\w+)\)):(\7\.fillText\(\w+\.\w+\.\w+\(\w+\.\w+\[(\w+)\]\),\4,\5\+\.78\*\6\))\)\)/g,
`$1 : ($8, settings.showPlayerDensity && (settings.coloredDensity && ($<canvas>.fillStyle = utils.textStyleBasedOnDensity($9)), $<canvas>.fillText(utils.getDensity($9), $<x>, $<y> + $<fontSize> * 1.5)) ) ) )`);
// Applies when the "Reverse Name/Balance" setting is on (default)
replaceOne(/(function \w+\((\w+),(?<fontSize>\w+),(?<x>\w+),(?<y>\w+),(?<canvas>\w+)\){)(\6\.fillText\((?<playerNames>\w+)\[\2\],\4,\5\)),(\2<(?<gHumans>\w+)&&2!==(?<playerStates>\w+)\[[^}]+)}/g,
`$1 var ___id = $2; $7, $9; ${settingsSwitchNameAndBalance} && settings.showPlayerDensity && (settings.coloredDensity && ($<canvas>.fillStyle = utils.textStyleBasedOnDensity(___id)), $<canvas>.fillText(utils.getDensity(___id), $<x>, $<y> + $<fontSize>)); }`);
replaceOne(/(function \w+\((\w+),(?<fontSize>\w+),(?<x>\w+),(?<y>\w+),(?<canvas>\w+)\){)(\6\.fillText\((?<playerData>\w+)\.(?<playerNames>\w+)\[\2\],\4,\5\)),(\2<(?<game>\w+)\.(?<gHumans>\w+)&&2!==\8\.(?<playerStates>\w+)\[[^}]+)}/g,
`$1 var ___id = $2; $7, $10; ${settingsSwitchNameAndBalance} && settings.showPlayerDensity && (settings.coloredDensity && ($<canvas>.fillStyle = utils.textStyleBasedOnDensity(___id)), $<canvas>.fillText(utils.getDensity(___id), $<x>, $<y> + $<fontSize>)); }`);
}
{ // Leaderboard filter
// for the leaderboard draw function:
replaceRawCode("a0A.clearRect(0,0,a04,y9),a0A.fillStyle=aZ.lE,a0A.fillRect(0,0,a04,a0F),a0A.fillStyle=aZ.kZ,a0A.fillRect(0,a0F,a04,y9-a0F),leaderboardPositionsById[playerId]>=position&&a0Z(leaderboardPositionsById[playerId]-position,aZ.kw),0!==leaderboardPositionsById[playerId]&&0===position&&a0Z(0,aZ.lJ),-1!==a0P&&a0Z(a0P,aZ.kd),a0A.fillStyle=aZ.gF,a0A.fillRect(0,a0F,a04,1),a0A.fillRect(0,0,a04,b0.ur),a0A.fillRect(0,0,b0.ur,y9),a0A.fillRect(a04-b0.ur,0,b0.ur,y9),a0A.fillRect(0,y9-b0.ur,a04,b0.ur),",
replaceRawCode("a0A.clearRect(0,0,a04,y9),a0A.fillStyle=aZ.lE,a0A.fillRect(0,0,a04,a0F),a0A.fillStyle=aZ.kZ,a0A.fillRect(0,a0F,a04,y9-a0F),leaderboardPositionsById[game.playerId]>=position&&a0Z(leaderboardPositionsById[game.playerId]-position,aZ.kw),0!==leaderboardPositionsById[game.playerId]&&0===position&&a0Z(0,aZ.lJ),-1!==a0P&&a0Z(a0P,aZ.kd),a0A.fillStyle=aZ.gF,a0A.fillRect(0,a0F,a04,1),a0A.fillRect(0,0,a04,b0.ur),a0A.fillRect(0,0,b0.ur,y9),a0A.fillRect(a04-b0.ur,0,b0.ur,y9),a0A.fillRect(0,y9-b0.ur,a04,b0.ur),",
`a0A.clearRect(0, 0, a04, y9),
a0A.fillStyle = aZ.lE,
a0A.fillRect(0, 0, a04, a0F),
@ -274,14 +250,14 @@ replaceOne(new RegExp(`,${dictionary.playerBalances}=new Uint32Array\\(\\w+\\),`
.map(id => leaderboardPositionsById[id]).sort((a, b) => a - b);
}
var playerPos = (leaderboardFilter.enabled
? leaderboardFilter.filteredLeaderboard.indexOf(leaderboardPositionsById[playerId])
: leaderboardPositionsById[playerId]
? leaderboardFilter.filteredLeaderboard.indexOf(leaderboardPositionsById[game.playerId])
: leaderboardPositionsById[game.playerId]
);
this.playerPos = playerPos;
if (leaderboardFilter.hoveringOverTabs) a0P = -1;
if (leaderboardFilter.enabled && a0P >= leaderboardFilter.filteredLeaderboard.length) a0P = -1;
playerPos >= position && a0Z(playerPos - position, aZ.kw),
0 !== leaderboardPositionsById[playerId] && 0 === position && a0Z(0, aZ.lJ),
0 !== leaderboardPositionsById[game.playerId] && 0 === position && a0Z(0, aZ.lJ),
-1 !== a0P && a0Z(a0P, aZ.kd),
a0A.fillStyle = aZ.kZ,
//console.log("drawing", a0P),
@ -295,7 +271,7 @@ replaceOne(new RegExp(`,${dictionary.playerBalances}=new Uint32Array\\(\\w+\\),`
a0A.fillRect(0, 0, b0.ur, y9),
a0A.fillRect(a04 - b0.ur, 0, b0.ur, y9),
a0A.fillRect(0, y9 - b0.ur, a04, b0.ur),`)
replaceRawCode("var hZ,eh=leaderboardPositionsById[playerId]<position+windowHeight-1?1:2;for(a0A.font=a07,aY.g0.textAlign(a0A,0),hZ=windowHeight-eh;0<=hZ;hZ--)a0a(leaderboardArray[hZ+position]),a0b(hZ,hZ+position,leaderboardArray[hZ+position]);for(aY.g0.textAlign(a0A,2),hZ=windowHeight-eh;0<=hZ;hZ--)a0a(leaderboardArray[hZ+position]),a0c(hZ,leaderboardArray[hZ+position]);",
replaceRawCode("var hZ,eh=leaderboardPositionsById[game.playerId]<position+windowHeight-1?1:2;for(a0A.font=a07,aY.g0.textAlign(a0A,0),hZ=windowHeight-eh;0<=hZ;hZ--)a0a(leaderboardArray[hZ+position]),a0b(hZ,hZ+position,leaderboardArray[hZ+position]);for(aY.g0.textAlign(a0A,2),hZ=windowHeight-eh;0<=hZ;hZ--)a0a(leaderboardArray[hZ+position]),a0c(hZ,leaderboardArray[hZ+position]);",
`var hZ, eh = playerPos < position + windowHeight - 1 ? 1 : 2;
if (leaderboardFilter.enabled) {
@ -320,23 +296,23 @@ replaceOne(new RegExp(`,${dictionary.playerBalances}=new Uint32Array\\(\\w+\\),`
a0a(leaderboardArray[hZ + position]), a0c(hZ, leaderboardArray[hZ + position]);
}`)
// in the leaderboard resize handler: make space for the tab buttons at the bottom of the leaderboard
replaceRawCode(",a0D=.025*a04,a06=.16*a04,a0E=0*a04,a0F=Math.floor(.45*a0D+a06),a0G=(y9-a06-2*a0D-a0E)/a08,a05=aY.g0.g1(1,Math.floor(.55*a06)),",
`,a0D=.025*a04,a06=.16*a04,a0E=0*a04,a0F=Math.floor(.45*a0D+a06),a0G=(y9-a06-2*a0D-a0E)/a08,
replaceRawCode(",a09.height=y9,a09_ctx=a09.getContext(\"2d\",{alpha:!0}),a0D=.025*a04,a06=.16*a04,a0E=0*a04,a0F=Math.floor(.45*a0D+a06),a0G=(y9-a06-2*a0D-a0E)/a08,a05=aY.g0.g1(1,Math.floor(.55*a06)),",
`,a09.height=y9,a09_ctx=a09.getContext("2d",{alpha:!0}),a0D=.025*a04,a06=.16*a04,a0E=0*a04,a0F=Math.floor(.45*a0D+a06),a0G=(y9-a06-2*a0D-a0E)/a08,
a09.height = y9 += a0G, leaderboardFilter.tabBarOffset = Math.floor(a0G * 1.3), leaderboardFilter.verticalClickThreshold = y9 - leaderboardFilter.tabBarOffset, leaderboardFilter.windowWidth = a04,
a05=aY.g0.g1(1,Math.floor(.55*a06)),`)
// handle clicking on a player in the leaderboard
replaceRawCode("var a0p=a0q(fJ);return ag.tQ()&&-1!==a0P&&(a0P=-1,a0Y(),b3.d1=!0),b3.dY-a0Q<350&&a0T===a0p&&-1!==(a0p=(a0p=yr(-1,a0p,windowHeight))!==windowHeight&&vU(x,y)?a0p:-1)&&(x=leaderboardArray[a0p+position],a0p===windowHeight-1&&leaderboardPositionsById[playerId]>=position+windowHeight-1&&(x=playerId),",
replaceRawCode("var a0p=a0q(fJ);return ag.tQ()&&-1!==a0P&&(a0P=-1,a0Y(),b3.d1=!0),b3.dY-a0Q<350&&a0T===a0p&&-1!==(a0p=(a0p=yr(-1,a0p,windowHeight))!==windowHeight&&vU(x,y)?a0p:-1)&&(x=leaderboardArray[a0p+position],a0p===windowHeight-1&&leaderboardPositionsById[game.playerId]>=position+windowHeight-1&&(x=game.playerId),",
`var a0p = a0q(fJ);
var isEmptySpace = false;
return ag.tQ() && -1 !== a0P && (a0P = -1, a0Y(), b3.d1 = !0), b3.dY - a0Q < 350 && a0T === a0p && -1 !== (a0p = (a0p = yr(-1, a0p, windowHeight)) !== windowHeight && vU(x, y) ? a0p : -1) && (x = (leaderboardFilter.enabled ? leaderboardArray[leaderboardFilter.filteredLeaderboard[a0p + position] ?? (isEmptySpace = true, leaderboardPositionsById[playerId])] : leaderboardArray[a0p + position]), a0p === windowHeight - 1 && (leaderboardFilter.enabled ? this.playerPos : leaderboardPositionsById[playerId]) >=
position + windowHeight - 1 && (x = playerId), !isEmptySpace && `);
return ag.tQ() && -1 !== a0P && (a0P = -1, a0Y(), b3.d1 = !0), b3.dY - a0Q < 350 && a0T === a0p && -1 !== (a0p = (a0p = yr(-1, a0p, windowHeight)) !== windowHeight && vU(x, y) ? a0p : -1) && (x = (leaderboardFilter.enabled ? leaderboardArray[leaderboardFilter.filteredLeaderboard[a0p + position] ?? (isEmptySpace = true, leaderboardPositionsById[game.playerId])] : leaderboardArray[a0p + position]), a0p === windowHeight - 1 && (leaderboardFilter.enabled ? this.playerPos : leaderboardPositionsById[game.playerId]) >=
position + windowHeight - 1 && (x = game.playerId), !isEmptySpace && `);
// Get clan parsing function
replaceRawCode(`this.uI=function(username){var uK,uJ=username.indexOf("[");return!(uJ<0)&&1<(uK=username.indexOf("]"))-uJ&&uK-uJ<=8?username.substring(uJ+1,uK).toUpperCase().trim():null}`,
`this.uI=function(username){var uK,uJ=username.indexOf("[");return!(uJ<0)&&1<(uK=username.indexOf("]"))-uJ&&uK-uJ<=8?username.substring(uJ+1,uK).toUpperCase().trim():null}, leaderboardFilter.parseClanFromPlayerName = this.uI;`);
}
{ // Hovering tooltip
replaceRawCode("this.click=function(g8,g9,tE){var fT=aj.fU(g8),fV=aj.fW(g9),fX=aj.fY(fT,fV),fZ=aj.fa(fX);return!(!aj.fb(fT,fV)||(fT=(b7.cv.fv()?.025:.0144)*aK.fw,fV=performance.now(),Math.abs(g8-uu)>fT)||Math.abs(g9-uv)>fT||dY+500<fV)&&(dY=fV,tE?(function(g8,g9,fZ){a2.eb(fZ)||-1===(g8=ak.ff.vR(g8,g9))?k.vQ(fZ):k.vS(g8)}(g8,g9,fZ),!1)",
replaceRawCode("this.click=function(g8,g9,tE){var fT=aj.fU(g8),fV=aj.fW(g9),fX=aj.fY(fT,fV),fZ=aj.fa(fX);return!(!aj.fb(fT,fV)||(fT=(b7.cv.fv()?.025:.0144)*aK.fw,fV=performance.now(),Math.abs(g8-uu)>fT)||Math.abs(g9-uv)>fT||dY+500<fV)&&(dY=fV,tE&&function(g8,g9,fZ){a2.eb(fZ)||-1===(g8=ak.ff.vR(g8,g9))?k.vQ(fZ):k.vS(g8)}(g8,g9,fZ),",
`hoveringTooltip.display = function(mouseX, mouseY) {
var coordX = aj.fU(mouseX), coordY = aj.fW(mouseY),
coord = aj.fY(coordX, coordY), point = aj.fa(coord);
@ -348,9 +324,9 @@ replaceOne(new RegExp(`,${dictionary.playerBalances}=new Uint32Array\\(\\w+\\),`
fV = aj.fW(g9),
fX = aj.fY(fT, fV),
fZ = aj.fa(fX);
return !(!aj.fb(fT, fV) || (fT = (b7.cv.fv() ? .025 : .0144) * aK.fw, fV = performance.now(), Math.abs(g8 - uu) > fT) || Math.abs(g9 - uv) > fT || dY + 500 < fV) && (dY = fV, tE ? (function(g8, g9, fZ) {
return !(!aj.fb(fT, fV) || (fT = (b7.cv.fv() ? .025 : .0144) * aK.fw, fV = performance.now(), Math.abs(g8 - uu) > fT) || Math.abs(g9 - uv) > fT || dY + 500 < fV) && (dY = fV, tE && function(g8, g9, fZ) {
a2.eb(fZ) || -1 === (g8 = ak.ff.vR(g8, g9)) ? k.vQ(fZ) : k.vS(g8)
}(g8, g9, fZ), false)`)
}(g8, g9, fZ),`)
replaceRawCode("aK.nH=(window.devicePixelRatio||1)*aEr,",
`aK.nH = (window.devicePixelRatio || 1) * aEr, hoveringTooltip.canvasPixelScale = aK.nH,`)
}

View File

@ -1,5 +1,5 @@
const fx_version = '0.6.4.2'; // FX Client Version
const fx_update = 'May 22'; // FX Client Last Updated
const fx_version = '0.6.4.3'; // FX Client Version
const fx_update = 'Jun 1'; // FX Client Last Updated
if (localStorage.getItem("fx_winCount") == undefined || localStorage.getItem("fx_winCount") == null) {
var wins_counter = 0;
@ -8,7 +8,13 @@ if (localStorage.getItem("fx_winCount") == undefined || localStorage.getItem("fx
var wins_counter = localStorage.getItem("fx_winCount");
}
const getVar = varName => window[dictionary[varName]];
const playerDataProperties = ["playerTerritories", "playerBalances", "rawPlayerNames"];
const gameObjectProperties = ["playerId", "gIsTeamGame", "gHumans", "gLobbyMaxJoin", "gameState", "gIsSingleplayer"];
const getVar = varName => {
if (playerDataProperties.includes(varName)) return window[dictionary.playerData][dictionary[varName]];
if (gameObjectProperties.includes(varName)) return window[dictionary.game][dictionary[varName]];
return window[dictionary[varName]]
};
// https://stackoverflow.com/a/6234804
function escapeHtml(unsafe) {