Make custom lobbies persistent, add bot count and spawn selection options

Update v0.6.6.8
main
peshomir 2024-12-08 15:13:39 +02:00
parent 9b85d5d9b4
commit 8a85790caf
5 changed files with 67 additions and 28 deletions

View File

@ -145,6 +145,7 @@ const rawCodeSegments = [
"[0]=__L(),@strs[1]=@game.@gIsSingleplayer?__L():__L(),",
"?(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);`,
`var dt=@MenuManager.@getState();if(6===dt){if(4211===d)`
]
rawCodeSegments.forEach(code => {

View File

@ -318,11 +318,16 @@ canvas.font=aY.g0.g1(1,fontSize),canvas.fillStyle="rgba("+gR+","+tD+","+hj+",0.6
replaceRawCode("this.xY=function(){this.wg(),Sockets.kt.wf(3240),aN.setState(0),i___.j(5,5)}",
`this.xY=function(){this.wg(),Sockets.kt.wf(3240),__fx.customLobby.setActive(false),aN.setState(0),i___.j(5,5)},
__fx.customLobby.setLeaveFunction(() => this.xY())`)
// when a socket error occurs on the custom lobby socket
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);")
`this.wQ=function(wR,d){
wR===1 && __fx.customLobby.isActive() && ${dict.MenuManager}.${dict.getState}() !== 6 && __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)}")
`this.wl=function(zs){ __fx.customLobby.isActive() === false && ap.ky.zt(),
this.vH=0,bU.zu(),m.n.setState(0),zs||bJ.df.show(),aN.setState(0);
if (__fx.customLobby.isActive()) __fx.customLobby.rejoinLobby(); else 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
@ -332,11 +337,23 @@ 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(),")
// to not set custom lobby games as singleplayer
replaceRawCode(",a.b(c,d),1===players.length&&SingleplayerMenu.a9H(type),",
",a.b(c,d),1===players.length&&!__fx.customLobby.isActive()&&SingleplayerMenu.a9H(type),");
replaceRawCode(",this.vK=this.jS=players.length,this.gameIsSingleplayer=1===this.vK,",
",this.vK=this.jS=players.length,this.gameIsSingleplayer=1===this.vK&&!__fx.customLobby.isActive(),")
// 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;`
)
// spawn selection
replaceRawCode(",ax.jm&&!ax.jn.zi?this.zZ=this.gh=!1:this.zZ=this.gh=this.iS||this.jS<100,",
",ax.jm&&!ax.jn.zi?this.zZ=this.gh=!1 : __fx.customLobby.isActive() ? this.zZ=this.gh=__fx.customLobby.gameInfo.spawnSelection : this.zZ=this.gh=this.iS||this.jS<100,")
// bot count
replaceRawCode(",this.js?this.t1=aO.zj():this.t1=this.maxPlayers,this.jp=this.t1-this.jS,",
`,__fx.customLobby.isActive() ? this.t1 = Math.max(Math.min(__fx.customLobby.gameInfo.botCount, this.maxPlayers), this.jS)
: this.js?this.t1=aO.zj():this.t1=this.maxPlayers,this.jp=this.t1-this.jS,`)
}
// Invalid hostname detection avoidance

View File

@ -34,6 +34,7 @@ const playerList = document.createElement("div");
playerListContainer.append(playerCount, playerList);
const optionsContainer = document.createElement("div");
optionsContainer.className = "text-align-left";
const optionsStructure = {
mode: {
@ -51,26 +52,32 @@ const optionsStructure = {
]
},
map: { label: "Map:", type: "selectMenu" },
difficulty: { label: "Difficulty", type: "selectMenu", options: [
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" }
]}
]},
spawnSelection: { label: "Spawn selection", type: "checkbox" },
botCount: { label: "Bot & player count:", type: "numberInput", attributes: { min: "1", max: "512" } }
}
const optionsElements = {};
const optionsValues = {};
function updateOption(option, value) {
value = value.toString();
optionsElements[option].value = value.toString();
if (optionsStructure[option].type === "checkbox")
optionsElements[option].checked = (value === 0 ? false : true);
else optionsElements[option].value = value.toString();
optionsValues[option] = value;
}
function inputUpdateHandler(key, e) {
sendMessage("options", [key, parseInt(e.target.value)])
}
function checkboxUpdateHandler(key, e) {
sendMessage("options", [key, e.target.checked ? 1 : 0])
}
Object.entries(optionsStructure).forEach(([key, item]) => {
const label = document.createElement("label");
if (item.tooltip) label.title = item.tooltip;
@ -82,6 +89,7 @@ Object.entries(optionsStructure).forEach(([key, item]) => {
);
optionsElements[key] = element;
if (item.type === "textInput") element.type = "text";
if (item.type === "numberInput") element.type = "number";
if (item.placeholder) element.placeholder = item.placeholder;
if (isValueInput || item.type === "selectMenu")
element.addEventListener("change", inputUpdateHandler.bind(undefined, key))
@ -94,6 +102,9 @@ Object.entries(optionsStructure).forEach(([key, item]) => {
label.append(document.createElement("br"), note);
}
if (item.options) setSelectMenuOptions(item.options, element);
if (item.attributes) Object.entries(item.attributes).forEach(
([name, value]) => element.setAttribute(name, value)
);
label.append(element);
if (item.type === "checkbox") {
element.type = "checkbox";
@ -102,18 +113,14 @@ Object.entries(optionsStructure).forEach(([key, item]) => {
label.className = "checkbox";
label.append(checkmark);
//checkboxFields[item.for] = element;
}// else label.append(document.createElement("br"));
optionsContainer.append(label, document.createElement("br"));
element.addEventListener("change", checkboxUpdateHandler.bind(undefined, key))
} 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);
}
/*const botCountInput = document.createElement("input");
botCountInput.setAttribute("type", "number");
botCountInput.setAttribute("min", "0");
botCountInput.setAttribute("max", "512");
botCountInput.value = "512";*/
main.append(playerListContainer, optionsContainer);
@ -183,7 +190,8 @@ function isCustomMessage(raw) {
currentCode = data.code;
playerIsHost = data.isHost;
startButton.disabled = !playerIsHost;
optionsContainer.className = playerIsHost ? "" : "disabled";
if (playerIsHost) optionsContainer.classList.remove("disabled");
else optionsContainer.classList.add("disabled");
Object.entries(data.options).forEach(([option, value]) => updateOption(option, value));
displayPlayers(data.players);
} else if (type === "addPlayer") {
@ -194,8 +202,10 @@ function isCustomMessage(raw) {
playerElements[index].element.remove();
playerElements.splice(index, 1);
updatePlayerCount();
} else if (type === "inLobby") {
const index = data;
playerElements[index].inGameBadge.className = "d-none";
} else if (type === "options") {
console.log(data);
const [option, value] = data;
updateOption(option, value);
} else if (type === "setHost") {
@ -205,14 +215,21 @@ function isCustomMessage(raw) {
} else if (type === "host") {
playerIsHost = true;
startButton.disabled = false;
optionsContainer.className = "";
optionsContainer.classList.remove("disabled");
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 }[]} */
/** @type {{ element: HTMLDivElement, hostBadge: HTMLSpanElement, inGameBadge: HTMLSpanElement, kickButton: HTMLButtonElement, isHost: boolean }[]} */
let playerElements = [];
/** @param {{ name: string, isHost: boolean }} player */
function createBadge(text, visible) {
const badge = document.createElement("span");
badge.textContent = text;
badge.className = visible ? "" : "d-none";
return badge;
}
/** @typedef {{ name: string, isHost?: boolean, inGame?: boolean }} PlayerInfo */
/** @param {PlayerInfo} player */
function addPlayer(player) {
const div = document.createElement("div");
div.className = "lobby-player";
@ -221,12 +238,11 @@ function addPlayer(player) {
kickButton.textContent = "Kick";
kickButton.className = playerIsHost && !player.isHost ? "" : "d-none";
kickButton.addEventListener("click", kickButtonHandler);
const badge = document.createElement("span");
badge.textContent = "Host";
badge.className = player.isHost ? "" : "d-none";
div.append(badge, kickButton);
const hostBadge = createBadge("Host", player.isHost);
const inGameBadge = createBadge("In Game", player.inGame);
div.append(hostBadge, inGameBadge, kickButton);
playerList.append(div);
playerElements.push({ element: div, hostBadge: badge, kickButton, isHost: player.isHost });
playerElements.push({ element: div, hostBadge, inGameBadge, kickButton, isHost: player.isHost });
}
function kickButtonHandler(event) {
const button = event.target;
@ -237,7 +253,7 @@ function kickButtonHandler(event) {
}
}
}
/** @param {{ name: string, isHost: boolean }[]} players */
/** @param {PlayerInfo[]} players */
function displayPlayers(players) {
playerElements = [];
playerList.innerHTML = "";
@ -255,7 +271,9 @@ function startGame() {
WindowManager.closeWindow("customLobby");
sendMessage("startGame");
}
function rejoinLobby() {
joinLobby();
}
function setJoinFunction(f) { joinLobby = f; }
function setLeaveFunction(f) { leaveLobby = f; }
@ -267,7 +285,7 @@ function setActive(active) {
function hideWindow() {
WindowManager.closeWindow("customLobby");
}
const gameInterface = { gameInfo: optionsValues, showJoinPrompt, isCustomMessage, getSocketURL, setJoinFunction, setLeaveFunction, setSendFunction, setMapInfo, hideWindow, isActive: () => isActive, setActive }
const gameInterface = { gameInfo: optionsValues, showJoinPrompt, isCustomMessage, getSocketURL, setJoinFunction, setLeaveFunction, setSendFunction, setMapInfo, rejoinLobby, hideWindow, isActive: () => isActive, setActive }
const customLobby = gameInterface
export default customLobby

View File

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

View File

@ -110,6 +110,9 @@
.text-align-center {
text-align: center;
}
.text-align-left {
text-align: left;
}
hr {
width: 100%;