Compare commits
No commits in common. "23a272385cdaac54f10b26c1606463354f80b8e2" and "05f59c31bda4de1747214d1b7fe8f806d4907946" have entirely different histories.
23a272385c
...
05f59c31bd
112
build.js
112
build.js
|
@ -15,7 +15,6 @@ fs.writeFileSync("./build/index.html", fs.readFileSync("./build/index.html").toS
|
||||||
fs.writeFileSync("./build/sw.js", fs.readFileSync("./build/sw.js").toString().replace("buildTimestamp", buildTimestamp));
|
fs.writeFileSync("./build/sw.js", fs.readFileSync("./build/sw.js").toString().replace("buildTimestamp", buildTimestamp));
|
||||||
|
|
||||||
const buildClientCode = () => /** @type {Promise<void>} */(new Promise((resolve, reject) => {
|
const buildClientCode = () => /** @type {Promise<void>} */(new Promise((resolve, reject) => {
|
||||||
console.log("Building client code...");
|
|
||||||
webpack({
|
webpack({
|
||||||
mode: 'production',
|
mode: 'production',
|
||||||
entry: { fxClient: "./src/main.js" },
|
entry: { fxClient: "./src/main.js" },
|
||||||
|
@ -28,97 +27,95 @@ const buildClientCode = () => /** @type {Promise<void>} */(new Promise((resolve,
|
||||||
if (err.details) console.error(err.details);
|
if (err.details) console.error(err.details);
|
||||||
return reject(err);
|
return reject(err);
|
||||||
}
|
}
|
||||||
const info = stats?.toJson();
|
const info = stats.toJson();
|
||||||
if (stats?.hasWarnings()) console.warn(info?.warnings);
|
if (stats.hasWarnings()) console.warn(info.warnings);
|
||||||
if (stats?.hasErrors()) {
|
if (stats.hasErrors()) {
|
||||||
console.error(info?.errors);
|
console.error(info.errors);
|
||||||
return reject("Webpack compilation error");
|
reject("Webpack compilation error");
|
||||||
}
|
}
|
||||||
fs.writeFileSync(
|
else resolve();
|
||||||
"./build/fx.bundle.js",
|
|
||||||
Buffer.concat([fs.readFileSync("./game/build_artefacts.js"), fs.readFileSync("./build/fx.bundle.js")])
|
|
||||||
);
|
|
||||||
resolve();
|
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
async function patchGameCode() {
|
let script = fs.readFileSync('./game/latest.js', { encoding: 'utf8' }).trim();
|
||||||
|
|
||||||
let script = fs.readFileSync('./game/latest.js', { encoding: 'utf8' }).trim();
|
const exposeVarsToGlobalScope = true;
|
||||||
|
// need to first remove the iife wrapper so the top-level functions aren't inlined
|
||||||
const exposeVarsToGlobalScope = true;
|
if (exposeVarsToGlobalScope && script.startsWith("\"use strict\"; (function () {") && script.endsWith("})();"))
|
||||||
// need to first remove the iife wrapper so the top-level functions aren't inlined
|
|
||||||
if (exposeVarsToGlobalScope && script.startsWith("\"use strict\"; (function () {") && script.endsWith("})();"))
|
|
||||||
script = script.slice("\"use strict\"; (function () {".length, -"})();".length);
|
script = script.slice("\"use strict\"; (function () {".length, -"})();".length);
|
||||||
if (exposeVarsToGlobalScope && script.startsWith("(function () {") && script.endsWith("})();"))
|
if (exposeVarsToGlobalScope && script.startsWith("(function () {") && script.endsWith("})();"))
|
||||||
script = script.slice("(function () {".length, -"})();".length);
|
script = script.slice("(function () {".length, -"})();".length);
|
||||||
|
|
||||||
// uncompress strings
|
// uncompress strings
|
||||||
// this will break if the sequence `"];` appears in one of the strings
|
// this will break if the sequence `"];` appears in one of the strings
|
||||||
const stringArrayRaw = script.match(/var S=(\[.+?"\]);/)?.[1];
|
const stringArrayRaw = script.match(/var S=(\[.+?"\]);/)?.[1];
|
||||||
if (stringArrayRaw === undefined) throw new Error("cannot find the string array");
|
if (stringArrayRaw === undefined) throw new Error("cannot find the string array");
|
||||||
const stringArray = JSON.parse(stringArrayRaw);
|
const stringArray = JSON.parse(stringArrayRaw);
|
||||||
script = script.replace(/\bS\[(\d+)\]/g, (_match, index) => `"${stringArray[index]}"`);
|
script = script.replace(/\bS\[(\d+)\]/g, (_match, index) => `"${stringArray[index]}"`);
|
||||||
|
|
||||||
const modUtils = new ModUtils(minifyCode(script));
|
const modUtils = new ModUtils(minifyCode(script));
|
||||||
|
|
||||||
const { default: applyPatches } = await import('./patches/main.js');
|
import applyPatches from './patches/main.js';
|
||||||
console.log("Applying patches...");
|
console.log("Applying patches...");
|
||||||
applyPatches(modUtils);
|
applyPatches(modUtils);
|
||||||
|
|
||||||
// for versions ^1.99.5.2
|
// for versions ^1.99.5.2
|
||||||
const minificationResult = UglifyJS.minify(modUtils.script, {
|
const minificationResult = UglifyJS.minify(modUtils.script, {
|
||||||
"compress": { "arrows": false },
|
"compress": { "arrows": false },
|
||||||
"mangle": false
|
"mangle": false
|
||||||
});
|
});
|
||||||
if (minificationResult.error) {
|
if (minificationResult.error) {
|
||||||
console.log("error while passing through UglifyJS, replaceCode replacements might have caused errors");
|
console.log("error while passing through UglifyJS, replaceCode replacements might have caused errors");
|
||||||
throw minificationResult.error;
|
throw minificationResult.error;
|
||||||
}
|
}
|
||||||
if (minificationResult.warnings) console.log(minificationResult.warnings);
|
if (minificationResult.warnings) console.log(minificationResult.warnings);
|
||||||
modUtils.script = minificationResult.code;
|
modUtils.script = minificationResult.code;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
matchDictionaryExpression,
|
matchDictionaryExpression,
|
||||||
generateRegularExpression
|
generateRegularExpression
|
||||||
} = modUtils;
|
} = modUtils;
|
||||||
const dictionary = modUtils.dictionary;
|
const dictionary = modUtils.dictionary;
|
||||||
|
|
||||||
[
|
[
|
||||||
/,this\.(?<gIsTeamGame>\w+)=this\.\w+<7\|\|9===this\.\w+,/g,
|
/,this\.(?<gIsTeamGame>\w+)=this\.\w+<7\|\|9===this\.\w+,/g,
|
||||||
/=function\((\w+),(\w+),\w+\){\1===(?<game>\w+)\.(?<playerId>\w+)\?\w+\(175," "\+\w+\(\d+,\[(?<playerData>\w+)\.(?<playerNames>\w+)\[\2\]\]\)\+": ",1001,\2,\w+\(/g,
|
/=function\((\w+),(\w+),\w+\){\1===(?<game>\w+)\.(?<playerId>\w+)\?\w+\(175," "\+\w+\(\d+,\[(?<playerData>\w+)\.(?<playerNames>\w+)\[\2\]\]\)\+": ",1001,\2,\w+\(/g,
|
||||||
/function \w+\(\)\{if\(2===(?<game>\w+)\.(?<gameState>\w+)\)return 1;\w+\.\w+\(\),\1\.\2=2,\1\.\w+=\1.\w+\}/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,
|
/(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,
|
||||||
/\w+\.font=(?<fontGeneratorFunction>\w+\.\w+\.\w+)\(1,\.39\*this\.\w+\),/g
|
/\w+\.font=(?<fontGeneratorFunction>\w+\.\w+\.\w+)\(1,\.39\*this\.\w+\),/g
|
||||||
].forEach(matchDictionaryExpression);
|
].forEach(matchDictionaryExpression);
|
||||||
|
|
||||||
const rawCodeSegments = [
|
const rawCodeSegments = [
|
||||||
`aQ.eI(e0)?aQ.eE(e0)?a38=__L([a38]):(player=aQ.eF(e0),oq=__L([b0.uS.zG(@playerData.@rawPlayerNames[player],b0.p9.qQ(0,10),150)])+" ",oq=(oq+=__L([b0.wx.a07(playerData.@playerBalances[player])])+" ")+__L([b0.wx.a07(playerData.@playerTerritories[player])])+" ",`,
|
`aQ.eI(e0)?aQ.eE(e0)?a38=__L([a38]):(player=aQ.eF(e0),oq=__L([b0.uS.zG(@playerData.@rawPlayerNames[player],b0.p9.qQ(0,10),150)])+" ",oq=(oq+=__L([b0.wx.a07(playerData.@playerBalances[player])])+" ")+__L([b0.wx.a07(playerData.@playerTerritories[player])])+" ",`,
|
||||||
"1===a.b?this.@gLobbyMaxJoin=this.@gHumans:this.gLobbyMaxJoin=this.@data.@playerCount,this.tZ=this.gLobbyMaxJoin,this.@gBots=this.gLobbyMaxJoin-this.gHumans,this.sg=0,",
|
"1===a.b?this.@gLobbyMaxJoin=this.@gHumans:this.gLobbyMaxJoin=this.@data.@playerCount,this.tZ=this.gLobbyMaxJoin,this.@gBots=this.gLobbyMaxJoin-this.gHumans,this.sg=0,",
|
||||||
"[0]=__L(),@strs[1]=@game.@gIsSingleplayer?__L():__L(),",
|
"[0]=__L(),@strs[1]=@game.@gIsSingleplayer?__L():__L(),",
|
||||||
"?(this.gB=Math.floor(.066*aK.fw),g5=aK.g5-4*@uiSizes.@gap-this.gB):",
|
"?(this.gB=Math.floor(.066*aK.fw),g5=aK.g5-4*@uiSizes.@gap-this.gB):",
|
||||||
`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);`,
|
`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);`,
|
||||||
`var dt=@MenuManager.@getState();if(6===dt){if(4211===d)`
|
`var dt=@MenuManager.@getState();if(6===dt){if(4211===d)`
|
||||||
]
|
]
|
||||||
|
|
||||||
rawCodeSegments.forEach(code => {
|
rawCodeSegments.forEach(code => {
|
||||||
const { expression } = generateRegularExpression(code, true);
|
const { expression } = generateRegularExpression(code, true);
|
||||||
//console.log(expression);
|
//console.log(expression);
|
||||||
matchDictionaryExpression(expression);
|
matchDictionaryExpression(expression);
|
||||||
});
|
});
|
||||||
|
|
||||||
modUtils.executePostMinifyHandlers();
|
modUtils.executePostMinifyHandlers();
|
||||||
script = modUtils.script;
|
script = modUtils.script;
|
||||||
|
|
||||||
// the dictionary should maybe get embedded into one of the files in the bundle
|
console.log("Building client code...")
|
||||||
fs.writeFileSync(
|
|
||||||
"./game/build_artefacts.js",
|
await buildClientCode();
|
||||||
|
// the dictionary should maybe get embedded into one of the files in the bundle
|
||||||
|
fs.writeFileSync(
|
||||||
|
"./build/fx.bundle.js",
|
||||||
`const buildTimestamp = "${buildTimestamp}"; const dictionary = ${JSON.stringify(dictionary)};\n`
|
`const buildTimestamp = "${buildTimestamp}"; const dictionary = ${JSON.stringify(dictionary)};\n`
|
||||||
);
|
+ fs.readFileSync("./build/fx.bundle.js").toString()
|
||||||
|
);
|
||||||
|
|
||||||
console.log("Formatting code...");
|
console.log("Formatting code...");
|
||||||
|
|
||||||
script = beautify(script, {
|
script = beautify(script, {
|
||||||
"indent_size": 1,
|
"indent_size": 1,
|
||||||
"indent_char": "\t",
|
"indent_char": "\t",
|
||||||
"max_preserve_newlines": 5,
|
"max_preserve_newlines": 5,
|
||||||
|
@ -137,13 +134,8 @@ async function patchGameCode() {
|
||||||
"comma_first": false,
|
"comma_first": false,
|
||||||
"e4x": false,
|
"e4x": false,
|
||||||
"indent_empty_lines": false
|
"indent_empty_lines": false
|
||||||
});
|
});
|
||||||
|
|
||||||
fs.writeFileSync("./build/game.js", script);
|
|
||||||
console.log("Wrote ./build/game.js");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!process.argv.includes("--skip-patching")) await patchGameCode();
|
|
||||||
if (!process.argv.includes("--patch-only")) await buildClientCode();
|
|
||||||
|
|
||||||
|
fs.writeFileSync("./build/game.js", script);
|
||||||
|
console.log("Wrote ./build/game.js");
|
||||||
console.log("Build done");
|
console.log("Build done");
|
|
@ -6,9 +6,7 @@
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "node index.js",
|
"build": "node index.js",
|
||||||
"build-only": "node build.js",
|
"build-only": "node build.js"
|
||||||
"build-client": "node build.js --skip-patching",
|
|
||||||
"patch": "node build.js --patch-only"
|
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
@ -2,15 +2,6 @@ import assets from '../assets.js';
|
||||||
import ModUtils from '../modUtils.js';
|
import ModUtils from '../modUtils.js';
|
||||||
|
|
||||||
export default (/** @type {ModUtils} */ modUtils) => {
|
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.utils.reportError(e, msg);
|
|
||||||
return alert("Error:\\n" + e.filename + " " + e.lineno + " " + e.colno + " " + e.message);`
|
|
||||||
)
|
|
||||||
|
|
||||||
modUtils.waitForMinification(() => applyPatches(modUtils))
|
modUtils.waitForMinification(() => applyPatches(modUtils))
|
||||||
}
|
}
|
||||||
//export const requiredVariables = ["game", "playerId", "playerData", "rawPlayerNames", "gIsSingleplayer", "playerTerritories"];
|
//export const requiredVariables = ["game", "playerId", "playerData", "rawPlayerNames", "gIsSingleplayer", "playerTerritories"];
|
||||||
|
@ -310,6 +301,10 @@ canvas.font=aY.g0.g1(1,fontSize),canvas.fillStyle="rgba("+gR+","+tD+","+hj+",0.6
|
||||||
replaceRawCode(`,this.hostnameIsValid=0<=window.location.hostname.toLowerCase().indexOf("territorial.io"),`,
|
replaceRawCode(`,this.hostnameIsValid=0<=window.location.hostname.toLowerCase().indexOf("territorial.io"),`,
|
||||||
`,this.hostnameIsValid=true,`)
|
`,this.hostnameIsValid=true,`)
|
||||||
|
|
||||||
|
// Disable built-in Territorial.io error reporting
|
||||||
|
replaceOne(/window\.addEventListener\("error",function (\w+)\((\w+)\){/g,
|
||||||
|
'$& window.removeEventListener("error", $1); return alert("Error:\\n" + $2.filename + " " + $2.lineno + " " + $2.colno + " " + $2.message);');
|
||||||
|
|
||||||
console.log('Removing ads...');
|
console.log('Removing ads...');
|
||||||
// Remove ads
|
// Remove ads
|
||||||
replace('//api.adinplay.com/libs/aiptag/pub/TRT/territorial.io/tag.min.js', '');
|
replace('//api.adinplay.com/libs/aiptag/pub/TRT/territorial.io/tag.min.js', '');
|
||||||
|
|
|
@ -1,43 +1,23 @@
|
||||||
import { getSettings } from "./settings.js";
|
import { getSettings } from "./settings.js";
|
||||||
import { getVar } from "./gameInterface.js";
|
import { getVar } from "./gameInterface.js";
|
||||||
|
|
||||||
// Example usage from game script: __fx.utils.getMaxTroops(...)
|
const utils = new (function() {
|
||||||
|
this.getMaxTroops = function(playerTerritories, playerID) { return (playerTerritories[playerID]*150).toString(); };
|
||||||
function getMaxTroops(playerTerritories, playerID) {
|
this.getDensity = function(playerID, playerBalances = getVar("playerBalances"), playerTerritories = getVar("playerTerritories")) {
|
||||||
return (playerTerritories[playerID] * 150).toString();
|
|
||||||
};
|
|
||||||
function getDensity(playerID, playerBalances = getVar("playerBalances"), playerTerritories = getVar("playerTerritories")) {
|
|
||||||
if (getSettings().densityDisplayStyle === "percentage") return (((playerBalances[playerID] / ((playerTerritories[playerID] === 0 ? 1 : playerTerritories[playerID]) * 150)) * 100).toFixed(1) + "%");
|
if (getSettings().densityDisplayStyle === "percentage") return (((playerBalances[playerID] / ((playerTerritories[playerID] === 0 ? 1 : playerTerritories[playerID]) * 150)) * 100).toFixed(1) + "%");
|
||||||
else return (playerBalances[playerID] / (playerTerritories[playerID] === 0 ? 1 : playerTerritories[playerID])).toFixed(1);
|
else return (playerBalances[playerID] / (playerTerritories[playerID] === 0 ? 1 : playerTerritories[playerID])).toFixed(1);
|
||||||
};
|
};
|
||||||
function isPointInRectangle(x, y, rectangleStartX, rectangleStartY, width, height) {
|
this.isPointInRectangle = function(x, y, rectangleStartX, rectangleStartY, width, height) {
|
||||||
return x >= rectangleStartX && x <= rectangleStartX + width && y >= rectangleStartY && y <= rectangleStartY + height;
|
return x >= rectangleStartX && x <= rectangleStartX + width && y >= rectangleStartY && y <= rectangleStartY + height;
|
||||||
};
|
};
|
||||||
/** @param {CanvasRenderingContext2D} canvas @param {string} text */
|
/** @param {CanvasRenderingContext2D} canvas @param {string} text */
|
||||||
function fillTextMultiline(canvas, text, x, y, maxWidth) {
|
this.fillTextMultiline = function(canvas, text, x, y, maxWidth) {
|
||||||
const lineHeight = parseInt(canvas.font.split(" ").find(part => part.endsWith("px")).slice(0, -2));
|
const lineHeight = parseInt(canvas.font.split(" ").find(part => part.endsWith("px")).slice(0, -2));
|
||||||
text.split("\n").forEach((line, index) => canvas.fillText(line, x, y + index * lineHeight, maxWidth));
|
text.split("\n").forEach((line, index) => canvas.fillText(line, x, y + index * lineHeight, maxWidth));
|
||||||
}
|
}
|
||||||
function textStyleBasedOnDensity(playerID) {
|
this.textStyleBasedOnDensity = function(playerID) {
|
||||||
const playerBalances = getVar("playerBalances"), playerTerritories = getVar("playerTerritories");
|
const playerBalances = getVar("playerBalances"), playerTerritories = getVar("playerTerritories");
|
||||||
return `hsl(${playerBalances[playerID] / (playerTerritories[playerID] * 1.5)}, 100%, 50%, 1)`;
|
return `hsl(${playerBalances[playerID] / (playerTerritories[playerID] * 1.5)}, 100%, 50%, 1)`;
|
||||||
}
|
|
||||||
function reportError(e, message) {
|
|
||||||
message = e.filename + " " + e.lineno + " " + e.colno + " " + e.message + "\n" + message;
|
|
||||||
fetch("https://fx.peshomir.workers.dev/stats/errors", {
|
|
||||||
body: JSON.stringify({
|
|
||||||
message,
|
|
||||||
context: {
|
|
||||||
swState: navigator.serviceWorker?.controller?.state,
|
|
||||||
location: window.location.toString(),
|
|
||||||
userAgent: navigator.userAgent,
|
|
||||||
dictionary,
|
|
||||||
buildTimestamp,
|
|
||||||
scripts: Array.from(document.scripts).map(s => s.src)
|
|
||||||
}
|
}
|
||||||
}),
|
});
|
||||||
method: "POST"
|
export default utils
|
||||||
}).catch(e => alert("Failed to report error: " + e));
|
|
||||||
}
|
|
||||||
|
|
||||||
export default { getMaxTroops, getDensity, isPointInRectangle, fillTextMultiline, textStyleBasedOnDensity, reportError }
|
|
|
@ -1,7 +1,4 @@
|
||||||
const truncate = n => parseFloat(n.toFixed(12));
|
|
||||||
|
|
||||||
export function KeybindsInput(/** @type {HTMLElement} */ containerElement) {
|
export function KeybindsInput(/** @type {HTMLElement} */ containerElement) {
|
||||||
|
|
||||||
const header = document.createElement("p");
|
const header = document.createElement("p");
|
||||||
header.innerText = "Attack Percentage Keybinds";
|
header.innerText = "Attack Percentage Keybinds";
|
||||||
const keybindContainer = document.createElement("div");
|
const keybindContainer = document.createElement("div");
|
||||||
|
@ -9,69 +6,60 @@ export function KeybindsInput(/** @type {HTMLElement} */ containerElement) {
|
||||||
const keybindAddButton = document.createElement("button");
|
const keybindAddButton = document.createElement("button");
|
||||||
keybindAddButton.innerText = "Add";
|
keybindAddButton.innerText = "Add";
|
||||||
containerElement.append(header, keybindContainer, keybindAddButton);
|
containerElement.append(header, keybindContainer, keybindAddButton);
|
||||||
|
|
||||||
containerElement.className = "keybinds-input";
|
containerElement.className = "keybinds-input";
|
||||||
this.container = keybindContainer;
|
this.container = keybindContainer;
|
||||||
this.objectKeys = ["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: 0.8 });
|
this.objectArray.push({ key: "", type: "absolute", value: 0.8 });
|
||||||
this.container.appendChild(createInputRow(this.objectArray.length - 1));
|
this.displayObjects();
|
||||||
keybindAddButton.scrollIntoView(false);
|
keybindAddButton.scrollIntoView(false);
|
||||||
};
|
};
|
||||||
keybindAddButton.addEventListener("click", this.addObject.bind(this));
|
|
||||||
this.update = function (settings) {
|
this.update = function (settings) {
|
||||||
this.objectArray = settings.attackPercentageKeybinds;
|
this.objectArray = settings.attackPercentageKeybinds;
|
||||||
this.displayObjects();
|
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 = "";
|
||||||
if (this.objectArray.length === 0) return this.container.innerText = "No custom attack percentage keybinds added";
|
if (this.objectArray.length === 0) return this.container.innerText = "No custom attack percentage keybinds added";
|
||||||
// Loop through the array and display input fields for each object
|
// Loop through the array and display input fields for each object
|
||||||
for (var i = 0; i < this.objectArray.length; i++) {
|
for (var i = 0; i < this.objectArray.length; i++) {
|
||||||
this.container.appendChild(createInputRow(i));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const createInputRow = (i) => {
|
|
||||||
var objectDiv = document.createElement("div");
|
var objectDiv = document.createElement("div");
|
||||||
// Create input fields for each modifiable parameter
|
// Create input fields for each key
|
||||||
this.objectKeys.forEach(key => {
|
this.keys.forEach(function (key) {
|
||||||
objectDiv.appendChild(this.createInputField(i, key))
|
let inputField = document.createElement(key === "type" ? "select" : "input");
|
||||||
});
|
if (key === "type") {
|
||||||
|
inputField.innerHTML = '<option value="absolute">Absolute</option><option value="relative">Relative</option>';
|
||||||
|
inputField.addEventListener("change", this.updateObject.bind(this, i, key));
|
||||||
|
} else if (key === "key") {
|
||||||
|
inputField.type = "text";
|
||||||
|
inputField.setAttribute("readonly", "");
|
||||||
|
inputField.setAttribute("placeholder", "No key set");
|
||||||
|
inputField.addEventListener("click", this.startKeyInput.bind(this, i, key));
|
||||||
|
} else { // key === "value"
|
||||||
|
const isAbsolute = this.objectArray[i].type === "absolute";
|
||||||
|
inputField.type = isAbsolute ? "text" : "number";
|
||||||
|
if (isAbsolute) inputField.addEventListener("click", this.convertIntoNumberInput.bind(this, i, key), { once: true });
|
||||||
|
else inputField.setAttribute("step", "0.1");
|
||||||
|
inputField.addEventListener("input", this.updateObject.bind(this, i, key));
|
||||||
|
}
|
||||||
|
if (key === "value" && this.objectArray[i].type === "absolute")
|
||||||
|
inputField.value = this.objectArray[i][key] * 100 + "%";
|
||||||
|
else inputField.value = this.objectArray[i][key];
|
||||||
|
// Append input field to the object div
|
||||||
|
objectDiv.appendChild(inputField);
|
||||||
|
}, this);
|
||||||
// Button to delete the object
|
// Button to delete the object
|
||||||
var deleteButton = document.createElement("button");
|
var deleteButton = document.createElement("button");
|
||||||
deleteButton.textContent = "Delete";
|
deleteButton.textContent = "Delete";
|
||||||
deleteButton.addEventListener("click", this.deleteObject.bind(this, i));
|
deleteButton.addEventListener("click", this.deleteObject.bind(this, i));
|
||||||
|
// Append delete button to the object div
|
||||||
objectDiv.appendChild(deleteButton);
|
objectDiv.appendChild(deleteButton);
|
||||||
|
// Append the object div to the container
|
||||||
return objectDiv;
|
this.container.appendChild(objectDiv);
|
||||||
}
|
}
|
||||||
this.createInputField = function (i, property) {
|
|
||||||
let inputField = document.createElement(property === "type" ? "select" : "input");
|
|
||||||
if (property === "type") {
|
|
||||||
inputField.innerHTML = '<option value="absolute">Absolute</option><option value="relative">Relative</option>';
|
|
||||||
inputField.addEventListener("change", this.updateObject.bind(this, i, property));
|
|
||||||
} else if (property === "key") {
|
|
||||||
inputField.type = "text";
|
|
||||||
inputField.setAttribute("readonly", "");
|
|
||||||
inputField.setAttribute("placeholder", "No key set");
|
|
||||||
inputField.addEventListener("click", this.startKeyInput.bind(this, i, property));
|
|
||||||
} else { // property === "value"
|
|
||||||
const isAbsolute = this.objectArray[i].type === "absolute";
|
|
||||||
inputField.type = isAbsolute ? "text" : "number";
|
|
||||||
if (isAbsolute) inputField.addEventListener("click", this.convertIntoNumberInput.bind(this, i, property), { once: true });
|
|
||||||
else inputField.setAttribute("step", "0.1");
|
|
||||||
inputField.addEventListener("input", this.updateObject.bind(this, i, property));
|
|
||||||
}
|
|
||||||
if (property === "value" && this.objectArray[i].type === "absolute")
|
|
||||||
inputField.value = truncate(this.objectArray[i][property] * 100) + "%";
|
|
||||||
else inputField.value = this.objectArray[i][property];
|
|
||||||
|
|
||||||
return inputField;
|
|
||||||
};
|
|
||||||
this.recreateInputField = function (index, property) {
|
|
||||||
this.container.children[index].children[this.objectKeys.indexOf(property)].replaceWith(this.createInputField(index, property))
|
|
||||||
};
|
};
|
||||||
this.startKeyInput = function (index, property, event) {
|
this.startKeyInput = function (index, property, event) {
|
||||||
event.target.value = "Press any key";
|
event.target.value = "Press any key";
|
||||||
|
@ -80,28 +68,25 @@ export function KeybindsInput(/** @type {HTMLElement} */ containerElement) {
|
||||||
event.target.addEventListener("blur", () => {
|
event.target.addEventListener("blur", () => {
|
||||||
event.target.removeEventListener('keydown', handler);
|
event.target.removeEventListener('keydown', handler);
|
||||||
event.target.value = this.objectArray[index][property];
|
event.target.value = this.objectArray[index][property];
|
||||||
|
//this.displayObjects();
|
||||||
}, { once: true });
|
}, { once: true });
|
||||||
};
|
};
|
||||||
this.convertIntoNumberInput = function (index, property, event) {
|
this.convertIntoNumberInput = function (index, property, event) {
|
||||||
event.target.value = event.target.value.slice(0, -1);
|
event.target.value = event.target.value.slice(0, -1);
|
||||||
event.target.type = "number";
|
event.target.type = "number";
|
||||||
event.target.addEventListener("blur", () => {
|
event.target.addEventListener("blur", () => {
|
||||||
this.recreateInputField(index, property);
|
//event.target.value = this.objectArray[index][property];
|
||||||
|
this.displayObjects();
|
||||||
}, { once: true });
|
}, { once: true });
|
||||||
};
|
};
|
||||||
this.updateObject = function (index, property, event) {
|
this.updateObject = function (index, property, event) {
|
||||||
if (index >= this.objectArray.length) return;
|
if (index >= this.objectArray.length) return;
|
||||||
// Update the corresponding property of the object in the array
|
// Update the corresponding property of the object in the array
|
||||||
const value = property === "value" ? (
|
const value = property === "value" ? (
|
||||||
this.objectArray[index].type === "absolute"
|
this.objectArray[index].type === "absolute" ? parseFloat(event.target.value) / 100 : parseFloat(event.target.value)
|
||||||
? truncate(parseFloat(event.target.value) / 100)
|
|
||||||
: parseFloat(event.target.value)
|
|
||||||
) : property === "key" ? event.key : event.target.value;
|
) : property === "key" ? event.key : event.target.value;
|
||||||
this.objectArray[index][property] = value;
|
this.objectArray[index][property] = value;
|
||||||
|
if (property === "key") this.displayObjects();
|
||||||
if (property === "key") this.recreateInputField(index, property);
|
|
||||||
// when the keybind's type (absolute or relative) is changed
|
|
||||||
else if (property === "type") this.recreateInputField(index, "value");
|
|
||||||
};
|
};
|
||||||
this.deleteObject = function (index) {
|
this.deleteObject = function (index) {
|
||||||
// Remove the object from the array
|
// Remove the object from the array
|
||||||
|
|
|
@ -17,7 +17,7 @@ var settings = {
|
||||||
realisticNames: false,
|
realisticNames: false,
|
||||||
showPlayerDensity: true,
|
showPlayerDensity: true,
|
||||||
coloredDensity: true,
|
coloredDensity: true,
|
||||||
densityDisplayStyle: "absoluteQuotient",
|
densityDisplayStyle: "percentage",
|
||||||
hideBotNames: false,
|
hideBotNames: false,
|
||||||
highlightClanSpawns: false,
|
highlightClanSpawns: false,
|
||||||
detailedTeamPercentage: false,
|
detailedTeamPercentage: false,
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
{
|
{
|
||||||
"version": "0.6.10",
|
"version": "0.6.9.1",
|
||||||
"lastUpdated": "Jul 8",
|
"lastUpdated": "Jul 3",
|
||||||
"changes": [
|
"changes": [
|
||||||
"Fixed floating point number imprecision when configuring keybinds",
|
"Updated FX Client to the latest game version"
|
||||||
"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."
|
|
||||||
]
|
]
|
||||||
}
|
}
|
Loading…
Reference in New Issue