Custom lobby difficulty option, hide bot names setting

Custom lobby options are now automatically generated from a specified structure, similar to the settings menu
Update v0.6.6.7
main
peshomir 2024-11-25 17:27:09 +02:00
parent 407716b11b
commit 9b85d5d9b4
4 changed files with 110 additions and 45 deletions

View File

@ -140,7 +140,7 @@ canvas.font=aY.g0.g1(1,fontSize),canvas.fillStyle="rgba("+gR+","+tD+","+hj+",0.6
`, ${dict.game}.${dict.gIsTeamGame} && __fx.donationsTracker.displayHistory($2, ${rawPlayerNames}, ${gIsSingleplayer}), $1 && !isEmptySpace $3`);
// Reset donation history and leaderboard filter when a new game is started
replaceOne(new RegExp(`,this\\.${dictionary.playerBalances}.fill\\(0\\),`, "g"), "$& __fx.donationsTracker.reset(), __fx.leaderboardFilter.reset(), __fx.customLobby.isActive() && __fx.customLobby.setActive(false), ");
replaceOne(new RegExp(`,this\\.${dictionary.playerBalances}.fill\\(0\\),`, "g"), "$& __fx.donationsTracker.reset(), __fx.leaderboardFilter.reset(), __fx.customLobby.isActive() && __fx.customLobby.hideWindow(), ");
{ // Player list and leaderboard filter tabs
// Draw player list button
@ -164,19 +164,24 @@ canvas.font=aY.g0.g1(1,fontSize),canvas.fillStyle="rgba("+gR+","+tD+","+hj+",0.6
)) return; $4`);
}
{ // Display density of other players
{ // Name rendering patches - Display density of other players & Hide bot names features
const r = matchRawCode(`bD.dO.data[7].value?a9W(i,jm,jk,jl,ctx):a9V(ctx,i,jm,jk,jl,a9S)))`);
const settingsSwitchNameAndBalance = `${r.bD}.${r.dO}.${r.data}[7].${r.value}`;
//console.log(settingsSwitchNameAndBalance);
// Applies when the "Reverse Name/Balance" setting is off
// Balance rendering; Renders density when the "Reverse Name/Balance" setting is off
replaceRawCode("function a9V(ctx,i,fontSize,x,y,a9S){i=ac.jv.formatNumber(playerData.playerBalances[i]);a9S>>1&1?(ctx.lineWidth=.05*fontSize,ctx.strokeStyle=a9U(fontSize,a9S%2),ctx.strokeText(i,x,y)):(1<a9S&&(ctx.lineWidth=.12*fontSize,ctx.strokeStyle=a9U(fontSize,a9S),ctx.strokeText(i,x,y)),ctx.fillText(i,x,y))}",
`function a9V(ctx,i,fontSize,x,y,a9S){
var ___id = i;
i=ac.jv.formatNumber(playerData.playerBalances[i]);a9S>>1&1?(ctx.lineWidth=.05*fontSize,ctx.strokeStyle=a9U(fontSize,a9S%2),ctx.strokeText(i,x,y)):(1<a9S&&(ctx.lineWidth=.12*fontSize,ctx.strokeStyle=a9U(fontSize,a9S),ctx.strokeText(i,x,y)),ctx.fillText(i,x,y));
${settingsSwitchNameAndBalance} || __fx.settings.showPlayerDensity && (__fx.settings.coloredDensity && (ctx.fillStyle = __fx.utils.textStyleBasedOnDensity(___id)), ctx.fillText(__fx.utils.getDensity(___id), x, y + fontSize))}`)
// Applies when the "Reverse Name/Balance" setting is on (default)
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} && __fx.settings.showPlayerDensity && (__fx.settings.coloredDensity && ($<canvas>.fillStyle = __fx.utils.textStyleBasedOnDensity(___id)), $<canvas>.fillText(__fx.utils.getDensity(___id), $<x>, $<y> + $<fontSize>)); }`);
// Name rendering; Renders density when the "Reverse Name/Balance" setting is on (default)
replaceOne(/(function \w+\((?<i>\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;
var showName = $<i> < $<game>.$<gHumans> || !__fx.settings.hideBotNames;
if (showName) $7, $10;
${settingsSwitchNameAndBalance} && __fx.settings.showPlayerDensity && (
__fx.settings.coloredDensity && ($<canvas>.fillStyle = __fx.utils.textStyleBasedOnDensity(___id)),
$<canvas>.fillText(__fx.utils.getDensity(___id), $<x>, showName ? $<y> + $<fontSize> : $<y>)
); }`);
}
{ // Leaderboard filter
@ -315,6 +320,9 @@ canvas.font=aY.g0.g1(1,fontSize),canvas.fillStyle="rgba("+gR+","+tD+","+hj+",0.6
__fx.customLobby.setLeaveFunction(() => this.xY())`)
replaceRawCode("this.wQ=function(wR,d){if(8===i.pz&&0===wR)if(4211===d)wS(d);",
"this.wQ=function(wR,d){ wR===1&&__fx.customLobby.isActive()&&__fx.customLobby.setActive(false); if(8===i.pz&&0===wR)if(4211===d)wS(d);")
// when leaving a game
replaceRawCode("this.wl=function(zs){ap.ky.zt(),this.vH=0,bU.zu(),m.n.setState(0),zs||bJ.df.show(),aN.setState(0),i.j(5,5)}",
"this.wl=function(zs){__fx.customLobby.setActive(false); ap.ky.zt(),this.vH=0,bU.zu(),m.n.setState(0),zs||bJ.df.show(),aN.setState(0),i.j(5,5)}")
// if the server is unreachable
replaceRawCode("0===a7Q?g.wc(3249):", "0===a7Q?g.wc(3249):1===a7Q&&__fx.customLobby.isActive()?(g.wc(3249),__fx.customLobby.setActive(false)):")
// error descriptions
@ -324,6 +332,11 @@ canvas.font=aY.g0.g1(1,fontSize),canvas.fillStyle="rgba("+gR+","+tD+","+hj+",0.6
// map info (for the map selection menu)
replaceRawCode("this.info=new Array(Maps.totalMapCount+1),this.info[0]={name:__L(),",
"this.info=new Array(Maps.totalMapCount+1),__fx.customLobby.setMapInfo(this.info),this.info[0]={name:__L(),")
// custom difficulty
replaceRawCode("if(ax.jm){if(ax.jn.jo)for(i=game.jp-1;0<=i;i--)this.strength[i+jl]=ax.jn.jo[i+1]}else if(9===game.jq)this.jr();",
`if(ax.jm){if(ax.jn.jo)for(i=game.jp-1;0<=i;i--)this.strength[i+jl]=ax.jn.jo[i+1]}else if(9===game.jq)this.jr();
else if (__fx.customLobby.isActive()) for(i=game.jp-1;0<=i;i--) this.strength[i+jl] = __fx.customLobby.gameInfo.difficulty;`
)
}
// Invalid hostname detection avoidance

View File

@ -10,9 +10,6 @@ let leaveLobby = () => { };
let sendRaw = (socketId, data) => { };
const textEncoder = new TextEncoder();
const textDecoder = new TextDecoder();
/*const gameInfo = {
botCount: 512
}*/
WindowManager.add({
name: "lobbyJoinMenu",
@ -36,38 +33,87 @@ playerCount.textContent = "0 Players";
const playerList = document.createElement("div");
playerListContainer.append(playerCount, playerList);
// todo: convert the options into something similar to the automatic settings generator
const optionsContainer = document.createElement("div");
const gameModeSelectMenu = document.createElement("select");
setSelectMenuOptions([
{ value: 0, label: "2 Teams" },
{ value: 1, label: "3 Teams" },
{ value: 2, label: "4 Teams" },
{ value: 3, label: "5 Teams" },
{ value: 4, label: "6 Teams" },
{ value: 5, label: "7 Teams" },
{ value: 6, label: "8 Teams" },
{ value: 7, label: "Battle Royale" },
{ value: 10, label: "No Fullsend Battle Royale" },
{ value: 9, label: "Zombie mode" }
], gameModeSelectMenu);
gameModeSelectMenu.value = "7";
const mapSelectMenu = document.createElement("select");
function setMapInfo(maps) {
setTimeout(() => setSelectMenuOptions(maps.map((info, index) => ({ value: index.toString(), label: info.name })), mapSelectMenu), 0);
const optionsStructure = {
mode: {
label: "Mode:", type: "selectMenu", options: [
{ value: 0, label: "2 Teams" },
{ value: 1, label: "3 Teams" },
{ value: 2, label: "4 Teams" },
{ value: 3, label: "5 Teams" },
{ value: 4, label: "6 Teams" },
{ value: 5, label: "7 Teams" },
{ value: 6, label: "8 Teams" },
{ value: 7, label: "Battle Royale" },
{ value: 10, label: "No Fullsend Battle Royale" },
{ value: 9, label: "Zombie mode" }
]
},
map: { label: "Map:", type: "selectMenu" },
difficulty: { label: "Difficulty", type: "selectMenu", options: [
{ value: 0, label: "Very Easy (Default)" },
{ value: 1, label: "Easy (1v1)" },
{ value: 2, label: "Normal" },
{ value: 3, label: "Hard" },
{ value: 4, label: "Very Hard" },
{ value: 5, label: "Impossible" }
]}
}
const optionsElements = {};
const optionsValues = {};
function updateOption(option, value) {
value = value.toString();
optionsElements[option].value = value.toString();
optionsValues[option] = value;
}
function inputUpdateHandler(key, e) {
sendMessage("options", [key, parseInt(e.target.value)])
}
Object.entries(optionsStructure).forEach(([key, item]) => {
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"
);
optionsElements[key] = element;
if (item.type === "textInput") element.type = "text";
if (item.placeholder) element.placeholder = item.placeholder;
if (isValueInput || item.type === "selectMenu")
element.addEventListener("change", inputUpdateHandler.bind(undefined, key))
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) setSelectMenuOptions(item.options, element);
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"));
optionsContainer.append(label, document.createElement("br"));
});
function setMapInfo(maps) {
setTimeout(() => setSelectMenuOptions(maps.map((info, index) => ({ value: index.toString(), label: info.name })), optionsElements["map"]), 0);
}
gameModeSelectMenu.addEventListener("change", e => sendMessage("options", ["mode", parseInt(e.target.value)]));
mapSelectMenu.addEventListener("change", e => sendMessage("options", ["map", parseInt(e.target.value)]));
/*const botCountInput = document.createElement("input");
botCountInput.setAttribute("type", "number");
botCountInput.setAttribute("min", "0");
botCountInput.setAttribute("max", "512");
botCountInput.value = "512";*/
optionsContainer.append(
"Mode: ", gameModeSelectMenu, document.createElement("br"),
"Map: ", mapSelectMenu, document.createElement("br"),
//"Bot count: "
);
main.append(playerListContainer, optionsContainer);
@ -138,8 +184,7 @@ function isCustomMessage(raw) {
playerIsHost = data.isHost;
startButton.disabled = !playerIsHost;
optionsContainer.className = playerIsHost ? "" : "disabled";
gameModeSelectMenu.value = data.options.mode.toString();
mapSelectMenu.value = data.options.map.toString();
Object.entries(data.options).forEach(([option, value]) => updateOption(option, value));
displayPlayers(data.players);
} else if (type === "addPlayer") {
addPlayer(data);
@ -152,8 +197,7 @@ function isCustomMessage(raw) {
} else if (type === "options") {
console.log(data);
const [option, value] = data;
if (option === "mode") gameModeSelectMenu.value = value.toString();
else if (option === "map") mapSelectMenu.value = value.toString();
updateOption(option, value);
} else if (type === "setHost") {
const index = data;
playerElements[index].isHost = true;
@ -163,7 +207,7 @@ function isCustomMessage(raw) {
startButton.disabled = false;
optionsContainer.className = "";
playerElements.forEach(p => { if (!p.isHost) p.kickButton.className = "" });
}
} else if (type === "serverMessage") alert(data);
return true;
}
/** @type {{ element: HTMLDivElement, hostBadge: HTMLSpanElement, kickButton: HTMLButtonElement, isHost: boolean }[]} */
@ -187,7 +231,10 @@ function addPlayer(player) {
function kickButtonHandler(event) {
const button = event.target;
for (let index = 0; index < playerElements.length; index++) {
if (playerElements[index].kickButton === button) sendMessage("kick", index);
if (playerElements[index].kickButton === button) {
sendMessage("kick", index);
break;
}
}
}
/** @param {{ name: string, isHost: boolean }[]} players */
@ -206,7 +253,7 @@ function getSocketURL() {
}
function startGame() {
WindowManager.closeWindow("customLobby");
sendMessage("startGame" /*{ mode: gameModeSelectMenu.value, map: mapSelectMenu.value }*/);
sendMessage("startGame");
}
@ -217,7 +264,10 @@ function setActive(active) {
isActive = active;
if (active === false) WindowManager.closeWindow("customLobby");
}
const gameInterface = { showJoinPrompt, isCustomMessage, getSocketURL, setJoinFunction, setLeaveFunction, setSendFunction, setMapInfo, isActive: () => isActive, setActive }
function hideWindow() {
WindowManager.closeWindow("customLobby");
}
const gameInterface = { gameInfo: optionsValues, showJoinPrompt, isCustomMessage, getSocketURL, setJoinFunction, setLeaveFunction, setSendFunction, setMapInfo, hideWindow, isActive: () => isActive, setActive }
const customLobby = gameInterface
export default customLobby

View File

@ -1,5 +1,5 @@
const fx_version = '0.6.6.6'; // FX Client Version
const fx_update = 'Nov 14'; // FX Client Last Updated
const fx_version = '0.6.6.7'; // FX Client Version
const fx_update = 'Nov 25'; // FX Client Last Updated
import settingsManager from './settings.js';
import { clanFilter, leaderboardFilter } from "./clanFilters.js";

View File

@ -16,6 +16,7 @@ var settings = {
showPlayerDensity: true,
coloredDensity: true,
densityDisplayStyle: "percentage",
hideBotNames: false,
highlightClanSpawns: false,
//"customMapFileBtn": true
customBackgroundUrl: "",
@ -79,6 +80,7 @@ const settingsManager = new (function () {
},
],
},
{ for: "hideBotNames", type: "checkbox", label: "Hide bot names" },
{
for: "highlightClanSpawns",
type: "checkbox",