diff --git a/build.js b/build.js index 851c524..9acd97f 100644 --- a/build.js +++ b/build.js @@ -56,6 +56,7 @@ script = script.replace(/\bS\[(\d+)\]/g, (_match, index) => `"${stringArray[inde const modUtils = new ModUtils(minifyCode(script)); import applyPatches from './patches/main.js'; +console.log("Applying patches..."); applyPatches(modUtils); // for versions ^1.99.5.2 diff --git a/patches/mobileKeybinds.js b/patches/mobileKeybinds.js new file mode 100644 index 0000000..b3bf245 --- /dev/null +++ b/patches/mobileKeybinds.js @@ -0,0 +1,56 @@ +export default (/** @type {import('../modUtils.js').default} */ { insertCode, replaceCode, matchCode }) => { + + const { mainCanvas, x, y } = insertCode(`this.te = function() { + if (!this.b()) { return; } + mainCanvas.drawImage(canvas, x, this.y); + /* here */ + }`, `if (__fx.settings.keybindButtons) __fx.mobileKeybinds.draw(mainCanvas, x, this.y);`) + + const { h, redraw } = insertCode(`a6k = Math.floor(3 * this.h / 2); + a4M = c.pZ.rN(1, Math.floor(0.5 * this.h)); + canvas = document.createElement("canvas"); + canvas.width = w; + /* here */ + canvas.height = this.h; + ctx = canvas.getContext("2d", { alpha: true }); + ctx.font = a4M; + c.pZ.textBaseline(ctx, 1); + c.pZ.textAlign(ctx, 1); + this.a6m(); + redraw(); + `, `__fx.mobileKeybinds.setSize(w, this.h, mainCanvas)`, { dictionary: { mainCanvas } }) + + const { ba, gap } = matchCode(`this.h = Math.floor(0.066 * h___.pb); w = h___.w - 4 * ba.gap - this.h;`); + + const { bd, requestRepaint } = insertCode(`this.gm = function(kt, ku) { + if (!this.b()) { return false; } + /* here */ + if (!a.a0n(kt, ku)) { return false; } + aR.mC = false; + if (a6w(this, kt, ku)) { bd.requestRepaint = true; } + return true; + };`, + `if (__fx.settings.keybindButtons && ku > this.y - Math.floor(ba.gap / 4) - this.h && ku < this.y - Math.floor(ba.gap / 4) && __fx.mobileKeybinds.click(kt - x)) return true;`, + { dictionary: { x, y, h, ba, gap } } + ) + + insertCode( + `var a6l = 11 / 12; /* here */`, + `__fx.keybindFunctions.repaintAttackPercentageBar = function() { redraw(); bd.requestRepaint = true; };`, + { dictionary: { redraw, bd, requestRepaint } } + ) + + // fix to correctly display peace vote menu and game messages (prevent overlap with keybind buttons) + replaceCode(`if (a.a4y(aM.a4u())) { + if (au.b) { return a.y - a.h - 2 * a4a; } + else { return a.y - a4a; } + }`, `if (a.a4y(aM.a4u())) { + if (au.b) { return __fx.settings.keybindButtons ? a.y - 2 * a.h - 3 * a4a : a.y - a.h - 2 * a4a; } + else { return __fx.settings.keybindButtons ? a.y - a.h - 2 * a4a : a.y - a4a; } + }`) + insertCode( + `if (a.a4y(aM.a4u())) { return /* here */ a.y - h - ba.gap; }`, + `__fx.settings.keybindButtons ? a.y - 2 * (h + ba.gap) : ` + ) + +} \ No newline at end of file diff --git a/src/gameInterface.js b/src/gameInterface.js index ddf8119..fba25da 100644 --- a/src/gameInterface.js +++ b/src/gameInterface.js @@ -1,7 +1,10 @@ const playerDataProperties = ["playerTerritories", "playerBalances", "rawPlayerNames"]; const gameObjectProperties = ["playerId", "gIsTeamGame", "gHumans", "gLobbyMaxJoin", "gameState", "gIsSingleplayer"]; + export 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]] -}; \ No newline at end of file +}; + +export const getUIGap = () => Math.floor(window[dictionary.uiSizes]?.[dictionary.gap] ?? 10); \ No newline at end of file diff --git a/src/keybinds.js b/src/keybinds.js index e896661..921c50e 100644 --- a/src/keybinds.js +++ b/src/keybinds.js @@ -1,10 +1,66 @@ +import { getUIGap } from "./gameInterface.js"; import { getSettings } from "./settings.js"; -export const keybindFunctions = { setAbsolute: () => {}, setRelative: () => {} }; +export const keybindFunctions = { + setAbsolute: () => {}, + setRelative: () => {}, + repaintAttackPercentageBar: () => {} +}; export const keybindHandler = key => { const keybindData = getSettings().attackPercentageKeybinds.find(keybind => keybind.key === key); if (keybindData === undefined) return false; - if (keybindData.type === "absolute") keybindFunctions.setAbsolute(keybindData.value); - else keybindFunctions.setRelative(keybindData.value); + executeKeybind(keybindData); return true; -}; \ No newline at end of file +}; +function executeKeybind(keybind) { + if (keybind.type === "absolute") keybindFunctions.setAbsolute(keybind.value); + else keybindFunctions.setRelative(keybind.value); + keybindFunctions.repaintAttackPercentageBar(); +} + +// mobile keybinds (keybind buttons) + +let canvas; +let width = 0; +let height = 0; +const maxCount = 6; + +export const mobileKeybinds = { + setSize: (w, h, mainCanvas) => { + if (getSettings().keybindButtons !== true) return; + width = w; + height = h; + + // redraw + canvas = document.createElement("canvas"); + canvas.width = w; + canvas.height = h; + const ctx = canvas.getContext("2d"); + const fontName = mainCanvas.font.split("px ", 2)[1]; + ctx.font = "bold " + h / 2 + "px " + fontName; + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + + const keybinds = getSettings().attackPercentageKeybinds.slice(0, maxCount); + const gap = getUIGap() / 4; + const buttonWidth = (w - gap * (maxCount - 1)) / maxCount; + keybinds.forEach((keybind, i) => { + ctx.fillStyle = "rgba(0, 0, 0, 0.8)"; + ctx.fillRect(i * (buttonWidth + gap), 0, buttonWidth, h); + ctx.fillStyle = "white"; + const label = keybind.type === "absolute" ? (keybind.value * 100).toFixed() + "%" : "x " + Math.round(keybind.value * 100) / 100; + ctx.fillText(label, (i + 0.5) * (buttonWidth + gap), h / 2); + }); + }, + click: (xRelative) => { + if (xRelative < 0 || xRelative > width) return false; + const keybinds = getSettings().attackPercentageKeybinds; + const index = Math.floor(xRelative / width * maxCount); + if (index >= keybinds.length) return false; + executeKeybind(keybinds[index]); + return true; + }, + draw: (mainCanvas, x, y) => { + mainCanvas.drawImage(canvas, x, y - (height + getUIGap() / 4)); + } +} \ No newline at end of file diff --git a/src/keybindsInput.js b/src/keybindsInput.js index 4ce33c9..6085470 100644 --- a/src/keybindsInput.js +++ b/src/keybindsInput.js @@ -1,4 +1,4 @@ -export function KeybindsInput(containerElement) { +export function KeybindsInput(/** @type {HTMLElement} */ containerElement) { const header = document.createElement("p"); header.innerText = "Attack Percentage Keybinds"; const keybindContainer = document.createElement("div"); @@ -6,6 +6,7 @@ export function KeybindsInput(containerElement) { const keybindAddButton = document.createElement("button"); keybindAddButton.innerText = "Add"; containerElement.append(header, keybindContainer, keybindAddButton); + containerElement.className = "keybinds-input"; this.container = keybindContainer; this.keys = [ "key", "type", "value" ]; this.objectArray = []; diff --git a/src/main.js b/src/main.js index 7195df2..5823fdc 100644 --- a/src/main.js +++ b/src/main.js @@ -1,5 +1,5 @@ -const fx_version = '0.6.7.1'; // FX Client Version -const fx_update = 'Feb 15'; // FX Client Last Updated +const fx_version = '0.6.7.2'; // FX Client Version +const fx_update = 'Mar 2'; // FX Client Last Updated if ("serviceWorker" in navigator) { navigator.serviceWorker.addEventListener("message", (e) => { @@ -20,7 +20,7 @@ import winCounter from "./winCounter.js"; import playerList from "./playerList.js"; import gameScriptUtils from "./gameScriptUtils.js"; import hoveringTooltip from "./hoveringTooltip.js"; -import { keybindFunctions, keybindHandler } from "./keybinds.js"; +import { keybindFunctions, keybindHandler, mobileKeybinds } from "./keybinds.js"; import customLobby from './customLobby.js'; window.__fx = window.__fx || {}; @@ -33,6 +33,7 @@ __fx.utils = gameScriptUtils; __fx.WindowManager = WindowManager; __fx.keybindFunctions = keybindFunctions; __fx.keybindHandler = keybindHandler; +__fx.mobileKeybinds = mobileKeybinds; __fx.donationsTracker = donationsTracker; __fx.playerList = playerList; __fx.hoveringTooltip = hoveringTooltip; diff --git a/src/settings.js b/src/settings.js index 52654fd..cfea7e3 100644 --- a/src/settings.js +++ b/src/settings.js @@ -21,6 +21,7 @@ var settings = { detailedTeamPercentage: false, //"customMapFileBtn": true customBackgroundUrl: "", + keybindButtons: false, attackPercentageKeybinds: [], }; __fx.settings = settings; @@ -102,6 +103,10 @@ const settingsManager = new (function () { "A custom image to be shown as the main menu background instead of the currently selected map.", }, KeybindsInput, + { + for: "keybindButtons", type: "checkbox", + label: "Keybind buttons", note: "Show keybind buttons above the troop selector (max 6)" + } ]; const settingsContainer = document.querySelector(".settings .scrollable"); var inputFields = {}; // (includes select menus) diff --git a/static/main.css b/static/main.css index 264a1c5..c64207a 100644 --- a/static/main.css +++ b/static/main.css @@ -28,7 +28,7 @@ border-width : 2px; border-width : calc(0.15 * (1vw + 1vh)); font-size : 20px; - font-size : calc(14px + ((0.5 * (1.1vw - 0.1vh)) + 0.14rem)); + font-size : calc(13px + ((0.5 * (1.1vw - 0.1vh)) + 0.14rem)); max-height : 90%; transition : 0.2s; z-index : 10; @@ -59,6 +59,13 @@ margin: 0px; } +.keybinds-input { + margin-bottom: 1em; +} +.keybinds-input input { + width: 10em; +} + .flex { display: flex; }