Fixes for game updates ^1.93.1; Update v0.6.3
Improvements to the build script: Regular expressions can now be generated and used automatically just by providing raw code segments of the original and the modified code; Dictionary expressions can be generated with a similar mechanism. Other changes: The settings controls are now programmatically generated; Removed the custom font setting because this was added to the base game; "Trebuchet MS" is now the default font for that setting instead of "sans-serif"; Removed the "hide all links" setting (the "Hide Links" option was removed from the base game), this could be replaced with a new "hide links" setting.archived
parent
0fed20b940
commit
f4e8debce8
116
build.js
116
build.js
|
@ -27,22 +27,67 @@ 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' };
|
//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.");
|
//if (!script.includes(`"${dictionary.__dictionaryVersion}"`)) throw new Error("Dictionary is outdated.");
|
||||||
let dictionary = {};
|
let dictionary = {};
|
||||||
|
|
||||||
|
const matchDictionaryExpression = expression => {
|
||||||
|
result = expression.exec(script);
|
||||||
|
if (result === null) throw new Error("no match for ") + expression;
|
||||||
|
if (expression.exec(script) !== null) throw new Error("more than one match for: ") + expression;
|
||||||
|
for (let [key, value] of Object.entries(result.groups)) dictionary[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return value example:
|
||||||
|
// When this function is called with "var1=var2+1;" as the code
|
||||||
|
// and this matches "a=b+1;", the returned value will be the object: { var1: "a", var2: "b" }
|
||||||
|
const replaceRawCode = (/** @type {string} */ raw, /** @type {string} */ result, nameMappings) => {
|
||||||
|
const { expression, groups } = generateRegularExpression(raw, false, nameMappings);
|
||||||
|
let replacementString = result.replaceAll("$", "$$").replace(/\w+/g, match => {
|
||||||
|
return groups.hasOwnProperty(match) ? "$" + groups[match] : match;
|
||||||
|
});
|
||||||
|
//console.log(replacementString);
|
||||||
|
const expressionMatchResult = replaceOne(expression, replacementString);
|
||||||
|
return Object.fromEntries(Object.entries(groups).map(([identifier, groupNumber]) => [identifier, expressionMatchResult[groupNumber]]));
|
||||||
|
}
|
||||||
|
const generateRegularExpression = (/** @type {string} */ code, /** @type {boolean} */ isForDictionary, nameMappings) => {
|
||||||
|
const groups = {};
|
||||||
|
let groupNumberCounter = 1;
|
||||||
|
let raw = escapeRegExp(code).replace(isForDictionary ? /(?:@@)*(@?)(\w+)/g : /()(\w+)/g, (_match, modifier, word) => {
|
||||||
|
// if a substitution string for the "word" is specified in the nameMappings, use it
|
||||||
|
if (nameMappings && nameMappings[word] !== undefined) return nameMappings[word];
|
||||||
|
// if the "word" is a number or is one of these specific words, ingore it
|
||||||
|
if (/^\d/.test(word) || ["return", "this", "var", "function", "Math"].includes(word)) return word;
|
||||||
|
else if (groups[word] !== undefined) return "\\" + groups[word]; // regex numeric reference to the group
|
||||||
|
else {
|
||||||
|
groups[word] = groupNumberCounter++;
|
||||||
|
return modifier === "@" ? `(?<${word}>\\w+)` : "(\\w+)";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let expression = new RegExp(isForDictionary ? raw.replaceAll("@@", "@") : raw, "g");
|
||||||
|
return { expression, groups };
|
||||||
|
}
|
||||||
|
|
||||||
[
|
[
|
||||||
/=(?<gIsSingleplayer>\w+)\?"Players":"Bots"/g,
|
///=(?<gIsSingleplayer>\w+)\?"Players":"Bots"/g,
|
||||||
/,(?<gIsTeamGame>\w+)=\(\w+=\w+\)<7\|\|9===\w+,/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,
|
/=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,
|
// 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,
|
/\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,
|
// 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,
|
///\((?<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,
|
/(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
|
/,\w+=512,(?<gLobbyMaxJoin>\w+)=\w+,(?<gIsSingleplayer>\w+)&&\(\1=\w+\.\w+\(\)\),\w+=\1-\w+,\w+=0,/g
|
||||||
].forEach(expression => {
|
].forEach(matchDictionaryExpression);
|
||||||
result = expression.exec(script);
|
|
||||||
if (result === null) throw new Error("no match for ") + expression;
|
const rawCodeSegments = [
|
||||||
if (expression.exec(script) !== null) throw new Error("more than one match for: ") + expression;
|
"[0]=aV.nU[70],a0T[1]=@gIsSingleplayer?aV.nU[71]:aV.nU[72],",
|
||||||
for (let [key, value] of Object.entries(result.groups)) dictionary[key] = value;
|
"?(this.gB=Math.floor(.0536*aK.fw),g5=aK.g5-4*@uiSizes.@gap-this.gB):"
|
||||||
|
]
|
||||||
|
|
||||||
|
rawCodeSegments.forEach(code => {
|
||||||
|
const { expression } = generateRegularExpression(code, true);
|
||||||
|
//console.log(expression);
|
||||||
|
matchDictionaryExpression(expression);
|
||||||
});
|
});
|
||||||
|
|
||||||
fs.writeFileSync("./build/fx_core.js", `const dictionary = ${JSON.stringify(dictionary)};\n` + fs.readFileSync("./build/fx_core.js").toString());
|
fs.writeFileSync("./build/fx_core.js", `const dictionary = ${JSON.stringify(dictionary)};\n` + fs.readFileSync("./build/fx_core.js").toString());
|
||||||
|
|
||||||
// Replace assets
|
// Replace assets
|
||||||
|
@ -50,28 +95,34 @@ const assets = require('./assets.js');
|
||||||
replaceOne(/(\(4,"crown",4,")[^"]+"\),/g, "$1" + assets.crownIcon + "\"),");
|
replaceOne(/(\(4,"crown",4,")[^"]+"\),/g, "$1" + assets.crownIcon + "\"),");
|
||||||
replaceOne(/(\(6,"territorial\.io",6,")[^"]+"\),/g, "$1" + assets.fxClientLogo + "\"),");
|
replaceOne(/(\(6,"territorial\.io",6,")[^"]+"\),/g, "$1" + assets.fxClientLogo + "\"),");
|
||||||
|
|
||||||
// Add FXClient menu item in "More" menu
|
/*// Add FXClient menu item in "More" menu
|
||||||
// match },ug[0][5]={name:a79,id:5,mf:90,oU:0,e8:0},
|
// 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,
|
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}),');
|
'$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
|
// 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,
|
// 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");
|
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='"`,
|
||||||
|
`ar.aAx("MenuGameVersion")||ar.aAz(new aB3("ℹ️ "+aV.nU[84],gameVersion + "<br><b>" + "FX Client v" + fx_version + " " + fx_update + "</b><br><a href='"`);
|
||||||
|
|
||||||
// Max size for custom maps: from 4096x4096 to 8192x8192
|
// Max size for custom maps: from 4096x4096 to 8192x8192
|
||||||
// TODO: test this; it might cause issues with new boat mechanics?
|
// TODO: test this; it might cause issues with new boat mechanics?
|
||||||
|
|
||||||
{ // Add Troop Density and Maximum Troops in side panel
|
{ // 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,
|
/*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
|
'$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?`,
|
||||||
|
`,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?`);
|
||||||
replaceOne(new RegExp(/(:(?<valueIndex>\w+)<7\?\w+\.\w+\.\w+\(valuesArray\[\2\]\)):(\w+\.\w+\(valuesArray\[7\]\))}/
|
replaceOne(new RegExp(/(:(?<valueIndex>\w+)<7\?\w+\.\w+\.\w+\(valuesArray\[\2\]\)):(\w+\.\w+\(valuesArray\[7\]\))}/
|
||||||
.source.replace(/valuesArray/g, valuesArray), "g"),
|
.source.replace(/valuesArray/g, valuesArray), "g"),
|
||||||
'$1 : $<valueIndex> === 7 ? $3 '
|
'$1 : $<valueIndex> === 7 ? $3 '
|
||||||
+ `: $<valueIndex> === 8 ? utils.getMaxTroops(${dictionary.playerTerritories}, ${dictionary.playerId}) `
|
+ `: $<valueIndex> === 8 ? utils.getMaxTroops(${dictionary.playerTerritories}, ${dictionary.playerId}) `
|
||||||
+ `: utils.getDensity(${dictionary.playerId}) }`);
|
+ `: utils.getDensity(${dictionary.playerId}) }`);
|
||||||
// increase the size of the side panel by 25% to make the text easier to read
|
// increase the size of the side panel by 25% to make the text easier to read
|
||||||
// match this.w = Math.floor((o ? .1646 : .126) * cZ),
|
replaceOne(/(this\.\w+=Math\.floor\(\(\w+\.\w+\.\w+\(\)\?\.1646:\.126\))\*(\w+\.\w+\),)/g, "$1 * 1.25 * $2");
|
||||||
replaceOne(/(this\.\w+=Math\.floor\(\(\w+\?\.1646:\.126\))\*(\w+\),)/g, "$1 * 1.25 * $2");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increment win counter on wins
|
// Increment win counter on wins
|
||||||
|
@ -82,7 +133,7 @@ replaceOne(/(=function\((\w+)\){)([^}]+),((\w+\(0),\w+<100\?(\w+\.\w+)\(11,(\[\w
|
||||||
|
|
||||||
{ // Add settings button and win count
|
{ // Add settings button and win count
|
||||||
// render gear icon 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))
|
/*// 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);
|
// 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.drawImage(settingsGearIcon,A.f3-A.hw/2,A.f4,A.nI,A.nI);
|
||||||
// cH.font = bt + Math.floor(A.nI * 0.4) + bu;
|
// cH.font = bt + Math.floor(A.nI * 0.4) + bu;
|
||||||
|
@ -93,14 +144,26 @@ const { groups } = replaceOne(/((?<canvas>\w+)\.textAlign=\w+,\2\.textBaseline=\
|
||||||
'$<canvas>.imageSmoothingEnabled = false, ' +
|
'$<canvas>.imageSmoothingEnabled = false, ' +
|
||||||
'$<canvas>.font = "bold " + Math.floor($<h> * 0.4) + "px " + settings.fontName, ' +
|
'$<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))), ' +
|
'(settings.displayWinCounter && !$<isMenuOpened> && $<canvas>.fillText("Win count: " + wins_counter, Math.floor($<x> + $<w> / 2), Math.floor(($<y> + $<h> / 2) * 2.1))), ' +
|
||||||
'$<end>');
|
'$<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,
|
||||||
|
canvas.drawImage(settingsGearIcon, x - width / 2, y, height, height),
|
||||||
|
canvas.imageSmoothingEnabled = false,
|
||||||
|
(settings.displayWinCounter && ( canvas.font = aY.g0.g1(1, Math.floor(height * 0.4)), canvas.fillText("Win count: " + wins_counter, Math.floor(x + width / 2), Math.floor((y + height / 2) * 2)) ) ),
|
||||||
|
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
|
// handle settings button click
|
||||||
replaceOne(/(this\.\w+=function\((?<mouseX>\w+),(?<mouseY>\w+)\){[^}]+?)if\((?<coordsGet>\w+=\w+\(\)),(?<isMenuOpened>\w+)\)(?<end>{for\([^}]+"Lobby ")/g,
|
/*replaceOne(/(this\.\w+=function\((?<mouseX>\w+),(?<mouseY>\w+)\){[^}]+?)if\((?<coordsGet>\w+=\w+\(\)),(?<isMenuOpened>\w+)\)(?<end>{for\([^}]+"Lobby ")/g,
|
||||||
'$1 $<coordsGet>; ' +
|
'$1 $<coordsGet>; ' +
|
||||||
`var gearIconX = ${groups.x}-${groups.w}/2; ` +
|
`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 (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 ($<mouseX> > gearIconX && $<mouseX> < (gearIconX+${groups.h}) && $<mouseY> > ${groups.y} && $<mouseY> < (${groups.y}+${groups.h})) return WindowManager.openWindow("settings"); ` +
|
||||||
'if ($<isMenuOpened>) $<end>');
|
'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())`,
|
||||||
|
`(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(), true)) || (mouseX >= gap - q6 / 0.7 && mouseY < jd + q6 && WindowManager.openWindow("settings"))
|
||||||
|
)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // Keybinds
|
{ // Keybinds
|
||||||
|
@ -117,21 +180,21 @@ replaceOne(/(this\.\w+=function\((?<mouseX>\w+),(?<mouseY>\w+)\){[^}]+?)if\((?<c
|
||||||
"$1 if (keybindHandler($<event>.key)) return; $3");
|
"$1 if (keybindHandler($<event>.key)) return; $3");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enforce custom font name
|
// Set the default font to Trebuchet MS
|
||||||
script = script.replace(/"px sans-serif"/g, '"px " + settings.fontName');
|
script = script.replace(/sans-serif"/g, 'Trebuchet MS"');
|
||||||
|
|
||||||
// Realistic bot names setting
|
// Realistic bot names setting
|
||||||
// matches c4[i] = c4[i].replace(a6U[dx], a6V[dx])
|
// matches c4[i] = c4[i].replace(a6U[dx], a6V[dx])
|
||||||
replaceOne(/(((\w+)\[\w+\])=\2\.replace\(\w+(\[\w+\]),\w+\4\))/g, "$1; if (settings.realisticNames) $3 = realisticNames;")
|
replaceOne(/(((\w+)\[\w+\])=\2\.replace\(\w+(\[\w+\]),\w+\4\))/g, "$1; if (settings.realisticNames) $3 = realisticNames;")
|
||||||
|
|
||||||
// Hide all links in main menu depending on settings
|
// Hide all links in main menu depending on settings
|
||||||
replaceOne(/(this\.\w+=function\(\){)((\w+\.\w+)\[2\]=\3\[3\]=\3\[4\]=(?<linksHidden>!this\.\w+\.\w+),)/g,
|
//replaceOne(/(this\.\w+=function\(\){)((\w+\.\w+)\[2\]=\3\[3\]=\3\[4\]=(?<linksHidden>!this\.\w+\.\w+),)/g,
|
||||||
"$1 if (settings.hideAllLinks) $3[0] = $3[1] = $<linksHidden>; else $3[0] = $3[1] = true; $2")
|
//"$1 if (settings.hideAllLinks) $3[0] = $3[1] = $<linksHidden>; else $3[0] = $3[1] = true; $2")
|
||||||
|
|
||||||
// Make the main canvas context have an alpha channel if a custom background is being used
|
// Make the main canvas context have an alpha channel if a custom background is being used
|
||||||
replaceOne(/(document\.getElementById\("canvasA"\),\(\w+=\w+\.getContext\("2d",){alpha:!1}/g, "$1 {alpha: makeMainMenuTransparent}")
|
replaceOne(/(document\.getElementById\("canvasA"\),\(\w+=\w+\.getContext\("2d",){alpha:!1}/g, "$1 {alpha: makeMainMenuTransparent}")
|
||||||
// Clear canvas background if a custom background is being used
|
// Clear canvas background if a custom background is being used
|
||||||
replaceOne(/(this\.\w+=function\(\){var (\w+),(\w+);)(\w+\.\w+\?\([^()]+setTransform\(\3=\2<\3\?\3:\2,0,0,\3,(?:Math\.floor\(\([^)]+\)\/2\)[,)]){2},(?:[^)]+\),){2}[^)]+\):(?<canvas>\w+)\.fillStyle=\w+\.\w+,\5\.fillRect\((?<wholeCanvas>0,0,\w+,\w+)\)}})/g,
|
replaceOne(/(this\.\w+=function\(\){var (\w+),(\w+);)(\w+\.\w+\?\([^()]+setTransform\(\3=\2<\3\?\3:\2,0,0,\3,(?:Math\.floor\(\([^)]+\)\/2\)[,)]){2},(?:[^)]+\),){2}[^)]+\):(?<canvas>\w+)\.fillStyle=\w+\.\w+,\5\.fillRect\((?<wholeCanvas>0,0,\w+\.\w+,\w+\.\w+)\)}})/g,
|
||||||
'$1 if (makeMainMenuTransparent) $<canvas>.clearRect($<wholeCanvas>); else $4')
|
'$1 if (makeMainMenuTransparent) $<canvas>.clearRect($<wholeCanvas>); else $4')
|
||||||
|
|
||||||
// Track donations
|
// Track donations
|
||||||
|
@ -148,9 +211,10 @@ replaceOne(new RegExp(`,${dictionary.playerBalances}=new Uint32Array\\(\\w+\\),`
|
||||||
|
|
||||||
{ // Player list
|
{ // Player list
|
||||||
// Draw player list button
|
// Draw player list button
|
||||||
const { groups: { drawFunction, topBarHeight } } = replaceOne(/(=1;function (?<drawFunction>\w+)\(\){[^}]+?(?<canvas>\w+)\.fillRect\(0,(?<topBarHeight>\w+),\w+,1\),(?:\3\.fillRect\([^()]+\),)+\3\.font=\w+,\3\.textBaseline=\w+,\3\.textAlign=\w+,\3\.fillText\(\w+,Math\.floor\()(\w+)\/2\),(Math\.floor\(\w+\+\w+\/2\)\));/g,
|
const uiOffset = dictionary.uiSizes + "." + dictionary.gap;
|
||||||
"$1($5 + $<topBarHeight> - 22) / 2), $6; playerList.drawButton($<canvas>, 12, 12, $<topBarHeight> - 22);");
|
const { groups: { drawFunction, topBarHeight } } = replaceOne(/(=1;function (?<drawFunction>\w+)\(\){[^}]+?(?<canvas>\w+)\.fillRect\(0,(?<topBarHeight>\w+),\w+,1\),(?:\3\.fillRect\([^()]+\),)+\3\.font=\w+,(\w+\.\w+)\.textBaseline\(\3,1\),\5\.textAlign\(\3,1\),\3\.fillText\(\w+\.\w+\[65\],Math\.floor\()(\w+)\/2\),(Math\.floor\(\w+\+\w+\/2\)\));/g,
|
||||||
const buttonBoundsCheck = `utils.isPointInRectangle($<x>, $<y>, ${dictionary.uiOffset} + 12, ${dictionary.uiOffset} + 12, ${topBarHeight} - 22, ${topBarHeight} - 22)`
|
"$1($6 + $<topBarHeight> - 22) / 2), $7; playerList.drawButton($<canvas>, 12, 12, $<topBarHeight> - 22);");
|
||||||
|
const buttonBoundsCheck = `utils.isPointInRectangle($<x>, $<y>, ${uiOffset} + 12, ${uiOffset} + 12, ${topBarHeight} - 22, ${topBarHeight} - 22)`
|
||||||
// Handle player list button mouseDown
|
// Handle player list button mouseDown
|
||||||
replaceOne(/(this\.\w+=function\((?<x>\w+),(?<y>\w+)\){return!!\w+\(\2,\3\))&&(\(\w+=\w+\.\w+,)/g,
|
replaceOne(/(this\.\w+=function\((?<x>\w+),(?<y>\w+)\){return!!\w+\(\2,\3\))&&(\(\w+=\w+\.\w+,)/g,
|
||||||
`$1 && (${buttonBoundsCheck} && playerList.display(${dictionary.playerNames}), true) && $4`);
|
`$1 && (${buttonBoundsCheck} && playerList.display(${dictionary.playerNames}), true) && $4`);
|
||||||
|
@ -162,7 +226,7 @@ replaceOne(new RegExp(`,${dictionary.playerBalances}=new Uint32Array\\(\\w+\\),`
|
||||||
|
|
||||||
{ // Display density of other players
|
{ // Display density of other players
|
||||||
// Applies when the "Reverse Name/Balance" setting is off
|
// Applies when the "Reverse Name/Balance" setting is off
|
||||||
const { groups: { settingsSwitchNameAndBalance } } = replaceOne(/(,(?<settingsSwitchNameAndBalance>\w+\.\w+\.\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+)\]\),\4,\5\+\.78\*\6\))\)\)/g,
|
||||||
`$1 : ($8, settings.showPlayerDensity && $<canvas>.fillText(utils.getDensity($9), $<x>, $<y> + $<fontSize> * 1.5)) ))`);
|
`$1 : ($8, settings.showPlayerDensity && $<canvas>.fillText(utils.getDensity($9), $<x>, $<y> + $<fontSize> * 1.5)) ))`);
|
||||||
// Applies when the "Reverse Name/Balance" setting is on (default)
|
// 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,
|
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,5 +1,5 @@
|
||||||
const fx_version = '0.6.2.1'; // FX Client Version
|
const fx_version = '0.6.3'; // FX Client Version
|
||||||
const fx_update = 'Mar 10'; // FX Client Last Updated
|
const fx_update = 'Mar 26'; // FX Client Last Updated
|
||||||
|
|
||||||
if (localStorage.getItem("fx_winCount") == undefined || localStorage.getItem("fx_winCount") == null) {
|
if (localStorage.getItem("fx_winCount") == undefined || localStorage.getItem("fx_winCount") == null) {
|
||||||
var wins_counter = 0;
|
var wins_counter = 0;
|
||||||
|
@ -16,14 +16,25 @@ function escapeHtml(unsafe) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function KeybindsInput(containerElement) {
|
function KeybindsInput(containerElement) {
|
||||||
this.container = containerElement;
|
const header = document.createElement("p");
|
||||||
|
header.innerText = "Attack Percentage Keybinds";
|
||||||
|
const keybindContainer = document.createElement("div");
|
||||||
|
keybindContainer.className = "arrayinput";
|
||||||
|
const keybindAddButton = document.createElement("button");
|
||||||
|
keybindAddButton.innerText = "Add";
|
||||||
|
containerElement.append(header, keybindContainer, keybindAddButton);
|
||||||
|
this.container = keybindContainer;
|
||||||
this.keys = [ "key", "type", "value" ];
|
this.keys = [ "key", "type", "value" ];
|
||||||
this.objectArray = [];
|
this.objectArray = [];
|
||||||
this.addObject = function () {
|
this.addObject = function () {
|
||||||
this.objectArray.push({ key: "", type: "absolute", value: 1 });
|
this.objectArray.push({ key: "", type: "absolute", value: 1 });
|
||||||
this.displayObjects();
|
this.displayObjects();
|
||||||
};
|
};
|
||||||
document.getElementById("keybindAddButton").addEventListener("click", this.addObject.bind(this));
|
this.update = function () {
|
||||||
|
this.objectArray = settings.attackPercentageKeybinds;
|
||||||
|
this.displayObjects();
|
||||||
|
}
|
||||||
|
keybindAddButton.addEventListener("click", this.addObject.bind(this));
|
||||||
this.displayObjects = function () {
|
this.displayObjects = function () {
|
||||||
// Clear the content of the container
|
// Clear the content of the container
|
||||||
this.container.innerHTML = "";
|
this.container.innerHTML = "";
|
||||||
|
@ -89,11 +100,11 @@ function KeybindsInput(containerElement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var settings = {
|
var settings = {
|
||||||
"fontName": "Trebuchet MS",
|
//"fontName": "Trebuchet MS",
|
||||||
"showBotDonations": false,
|
//"showBotDonations": false,
|
||||||
"displayWinCounter": true,
|
"displayWinCounter": true,
|
||||||
"useFullscreenMode": false,
|
"useFullscreenMode": false,
|
||||||
"hideAllLinks": false,
|
//"hideAllLinks": false,
|
||||||
"realisticNames": false,
|
"realisticNames": false,
|
||||||
"showPlayerDensity": true,
|
"showPlayerDensity": true,
|
||||||
"densityDisplayStyle": "percentage",
|
"densityDisplayStyle": "percentage",
|
||||||
|
@ -101,37 +112,80 @@ var settings = {
|
||||||
"customBackgroundUrl": "",
|
"customBackgroundUrl": "",
|
||||||
"attackPercentageKeybinds": [],
|
"attackPercentageKeybinds": [],
|
||||||
};
|
};
|
||||||
|
const discontinuedSettings = [ "hideAllLinks", "fontName" ];
|
||||||
let makeMainMenuTransparent = false;
|
let makeMainMenuTransparent = false;
|
||||||
var settingsManager = new (function() {
|
var settingsManager = new (function() {
|
||||||
var inputFields = { // (includes select menus)
|
const settingsStructure = [
|
||||||
fontName: document.getElementById("settings_fontname"),
|
//{ for: "fontName", type: "textInput", label: "Font name:", placeholder: "Enter font name", tooltip: "Name of the font to be used for rendering. For example: Arial, Georgia, sans-serif, serif, Comic Sans MS, ..."},
|
||||||
customBackgroundUrl: document.getElementById("settings_custombackgroundurl"),
|
{ type: "button", text: "Reset Wins Counter", action: removeWins },
|
||||||
densityDisplayStyle: document.getElementById("settings_densityDisplayStyle")
|
{ for: "displayWinCounter", type: "checkbox", label: "Display win counter" },
|
||||||
};
|
{ 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." },
|
||||||
var checkboxFields = {
|
//{ for: "hideAllLinks", type: "checkbox", label: "Hide Links option also hides app store links" },
|
||||||
//showBotDonations: document.getElementById("settings_donations_bots"),
|
{ for: "realisticNames", type: "checkbox", label: "Realistic Bot Names" },
|
||||||
hideAllLinks: document.getElementById("settings_hidealllinks"),
|
{ for: "showPlayerDensity", type: "checkbox", label: "Show player density" },
|
||||||
realisticNames: document.getElementById("settings_realisticnames"),
|
{ for: "densityDisplayStyle", type: "selectMenu", label: "Density value display style:", tooltip: "Controls how the territorial density value should be rendered", options: [
|
||||||
displayWinCounter: document.getElementById("settings_displaywincounter"),
|
{ value: "percentage", label: "Percentage" },
|
||||||
useFullscreenMode: document.getElementById("settings_usefullscreenmode"),
|
{ value: "absoluteQuotient", label: "Value from 0 to 150 (BetterTT style)" }
|
||||||
showPlayerDensity: document.getElementById("settings_showPlayerDensity"),
|
]},
|
||||||
//customMapFileBtn: document.getElementById("settings_custommapfileinput")
|
{ 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." },
|
||||||
};
|
KeybindsInput
|
||||||
|
];
|
||||||
|
const settingsContainer = document.querySelector(".settings .scrollable");
|
||||||
|
var inputFields = {}; // (includes select menus)
|
||||||
|
var checkboxFields = {};
|
||||||
|
var customElements = [];
|
||||||
|
settingsStructure.forEach(item => {
|
||||||
|
if (typeof item === "function") {
|
||||||
|
const container = document.createElement("div");
|
||||||
|
customElements.push(new item(container));
|
||||||
|
return settingsContainer.append(container);
|
||||||
|
}
|
||||||
|
const label = document.createElement("label");
|
||||||
|
if (item.tooltip) label.title = item.tooltip;
|
||||||
|
const isValueInput = item.type.endsWith("Input");
|
||||||
|
const element = document.createElement(isValueInput || item.type === "checkbox" ? "input" : item.type === "selectMenu" ? "select" : "button");
|
||||||
|
if (item.type === "textInput") element.type = "text";
|
||||||
|
if (item.placeholder) element.placeholder = item.placeholder;
|
||||||
|
if (isValueInput || item.type === "selectMenu") inputFields[item.for] = element;
|
||||||
|
if (item.text) element.innerText = item.text;
|
||||||
|
if (item.action) element.addEventListener("click", item.action);
|
||||||
|
if (item.label) label.append(item.label + " ");
|
||||||
|
if (item.note) {
|
||||||
|
const note = document.createElement("small");
|
||||||
|
note.innerText = item.note;
|
||||||
|
label.append(document.createElement("br"), note)
|
||||||
|
}
|
||||||
|
if (item.options) item.options.forEach(option => {
|
||||||
|
const optionElement = document.createElement("option");
|
||||||
|
optionElement.setAttribute("value", option.value);
|
||||||
|
optionElement.innerText = option.label;
|
||||||
|
element.append(optionElement);
|
||||||
|
});
|
||||||
|
label.append(element);
|
||||||
|
if (item.type === "checkbox") {
|
||||||
|
element.type = "checkbox";
|
||||||
|
const checkmark = document.createElement("span");
|
||||||
|
checkmark.className = "checkmark";
|
||||||
|
label.className = "checkbox";
|
||||||
|
label.append(checkmark);
|
||||||
|
checkboxFields[item.for] = element;
|
||||||
|
} else label.append(document.createElement("br"));
|
||||||
|
settingsContainer.append(label, document.createElement("br"));
|
||||||
|
});
|
||||||
this.save = function() {
|
this.save = function() {
|
||||||
Object.keys(inputFields).forEach(function(key) { settings[key] = inputFields[key].value.trim(); });
|
Object.keys(inputFields).forEach(function(key) { settings[key] = inputFields[key].value.trim(); });
|
||||||
Object.keys(checkboxFields).forEach(function(key) { settings[key] = checkboxFields[key].checked; });
|
Object.keys(checkboxFields).forEach(function(key) { settings[key] = checkboxFields[key].checked; });
|
||||||
this.applySettings();
|
this.applySettings();
|
||||||
WindowManager.closeWindow("settings");
|
WindowManager.closeWindow("settings");
|
||||||
|
discontinuedSettings.forEach(settingName => delete settings[settingName]);
|
||||||
localStorage.setItem("fx_settings", JSON.stringify(settings));
|
localStorage.setItem("fx_settings", JSON.stringify(settings));
|
||||||
// should probably firgure out a way to do this without reloading - // You can't do it, localstorages REQUIRE you to reload
|
// should probably firgure out a way to do this without reloading - // You can't do it, localstorages REQUIRE you to reload
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
};
|
};
|
||||||
let keybindsInput = new KeybindsInput(document.getElementById("keybinds"));
|
|
||||||
this.syncFields = function() {
|
this.syncFields = function() {
|
||||||
Object.keys(inputFields).forEach(function(key) { inputFields[key].value = settings[key]; });
|
Object.keys(inputFields).forEach(function(key) { inputFields[key].value = settings[key]; });
|
||||||
Object.keys(checkboxFields).forEach(function(key) { checkboxFields[key].checked = settings[key]; });
|
Object.keys(checkboxFields).forEach(function(key) { checkboxFields[key].checked = settings[key]; });
|
||||||
keybindsInput.objectArray = settings.attackPercentageKeybinds;
|
customElements.forEach(element => element.update());
|
||||||
keybindsInput.displayObjects();
|
|
||||||
};
|
};
|
||||||
this.resetAll = function() {
|
this.resetAll = function() {
|
||||||
if (!confirm("Are you Really SURE you want to RESET ALL SETTINGS back to the default?")) return;
|
if (!confirm("Are you Really SURE you want to RESET ALL SETTINGS back to the default?")) return;
|
||||||
|
|
|
@ -39,19 +39,21 @@
|
||||||
<style>
|
<style>
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background: rgb(0, 0, 0);
|
background: rgb(0, 0, 0);
|
||||||
touch-action: none;
|
color: rgb(255, 255, 255);
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-khtml-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: rgb(225, 225, 255);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body onload="aiCommand746(0);">
|
<body onload="aiCommand746(0);">
|
||||||
|
@ -59,13 +61,13 @@
|
||||||
<div class="window flex settings" style="display:none">
|
<div class="window flex settings" style="display:none">
|
||||||
<h1>Settings</h1>
|
<h1>Settings</h1>
|
||||||
<div class="scrollable">
|
<div class="scrollable">
|
||||||
<label title="Name of the font to be used for rendering. For example: Arial, Georgia, sans-serif, serif, Comic Sans MS, ...">
|
<!--<label title="Name of the font to be used for rendering. For example: Arial, Georgia, sans-serif, serif, Comic Sans MS, ...">
|
||||||
Font name: <input id="settings_fontname" placeholder="Enter font name" value="Arial"></label><br>
|
Font name: <input id="settings_fontname" placeholder="Enter font name" value="Arial"></label><br>
|
||||||
<br><button onclick="removeWins()">Reset Wins Counter</button><br><br>
|
<br><button onclick="removeWins()">Reset Wins Counter</button><br><br>
|
||||||
<!--<label for="settings_donations_bots" class="checkbox">
|
<!- -<label for="settings_donations_bots" class="checkbox">
|
||||||
Display donations from bots in donation history viewer (applies to multiplayer only)
|
Display donations from bots in donation history viewer (applies to multiplayer only)
|
||||||
<input type="checkbox" id="settings_donations_bots"><span class="checkmark"></span>
|
<input type="checkbox" id="settings_donations_bots"><span class="checkmark"></span>
|
||||||
</label><br>-->
|
</label><br>- ->
|
||||||
<label for="settings_displaywincounter" class="checkbox">
|
<label for="settings_displaywincounter" class="checkbox">
|
||||||
Display win counter
|
Display win counter
|
||||||
<input type="checkbox" id="settings_displaywincounter"><span class="checkmark"></span>
|
<input type="checkbox" id="settings_displaywincounter"><span class="checkmark"></span>
|
||||||
|
@ -94,15 +96,15 @@
|
||||||
</select></label><br><br>
|
</select></label><br><br>
|
||||||
<label title="A custom image to be shown in the main menu background instead of the currently selected map.">
|
<label title="A custom image to be shown in the main menu background instead of the currently selected map.">
|
||||||
Custom main menu background: <input id="settings_custombackgroundurl" placeholder="Enter an image URL here"></label>
|
Custom main menu background: <input id="settings_custombackgroundurl" placeholder="Enter an image URL here"></label>
|
||||||
<!--<input type="file" id="customBackgroundFileInput" style="display:none;">
|
<!- -<input type="file" id="customBackgroundFileInput" style="display:none;">
|
||||||
or <button onclick="openCustomBackgroundFilePicker()">Open a local file</button>--><br><br>
|
or <button onclick="openCustomBackgroundFilePicker()">Open a local file</button>- -><br><br>
|
||||||
<!--<label for="settings_custommapfileinput" class="checkbox">
|
<!- -<label for="settings_custommapfileinput" class="checkbox">
|
||||||
Bring back the custom map file button after the creator removed it in 1.83.0
|
Bring back the custom map file button after the creator removed it in 1.83.0
|
||||||
<input type="checkbox" id="settings_custommapfileinput"><span class="checkmark"></span>
|
<input type="checkbox" id="settings_custommapfileinput"><span class="checkmark"></span>
|
||||||
</label>-->
|
</label>- ->
|
||||||
<p>Attack Percentage Keybinds</p>
|
<p>Attack Percentage Keybinds</p>
|
||||||
<div id="keybinds" class="arrayinput"></div>
|
<div id="keybinds" class="arrayinput"></div>
|
||||||
<button id="keybindAddButton">Add</button>
|
<button id="keybindAddButton">Add</button>-->
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<footer>
|
<footer>
|
||||||
|
|
Loading…
Reference in New Issue