Compare commits
	
		
			No commits in common. "6259f52b3e13f652eefebbe2d51c2c3d1dfe51d1" and "b88129823195027dcd49eb63019c3c31eb315598" have entirely different histories. 
		
	
	
		
			6259f52b3e
			...
			b881298231
		
	
		
	
								
									
									
										
											2
										
									
									build.js
									
									
									
									
								
								
							
							
										
											2
										
									
									build.js
									
									
									
									
								| 
						 | 
					@ -115,7 +115,7 @@ const generateRegularExpression = (/** @type {string} */ code, /** @type {boolea
 | 
				
			||||||
		// if a substitution string for the "word" is specified in the nameMappings, use it
 | 
							// if a substitution string for the "word" is specified in the nameMappings, use it
 | 
				
			||||||
		if (nameMappings && nameMappings.hasOwnProperty(word)) return nameMappings[word];
 | 
							if (nameMappings && nameMappings.hasOwnProperty(word)) return nameMappings[word];
 | 
				
			||||||
		// if the "word" is a number or is one of these specific words, ingore it
 | 
							// if the "word" is a number or is one of these specific words, ingore it
 | 
				
			||||||
		if (/^\d/.test(word) || ["return", "this", "var", "function", "new", "Math", "WebSocket"].includes(word)) return word;
 | 
							if (/^\d/.test(word) || ["return", "this", "var", "function", "Math"].includes(word)) return word;
 | 
				
			||||||
		// for easy localizer function matching
 | 
							// for easy localizer function matching
 | 
				
			||||||
		else if (word === "___localizer") {
 | 
							else if (word === "___localizer") {
 | 
				
			||||||
			groups[word + (++localizerCount)] = groupNumberCounter++;
 | 
								groups[word + (++localizerCount)] = groupNumberCounter++;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
								
									
									
										
											46
										
									
									patches.js
									
									
									
									
								
								
							
							
										
											46
										
									
									patches.js
									
									
									
									
								| 
						 | 
					@ -50,17 +50,14 @@ export default ({ replace, replaceOne, replaceRawCode, dictionary, matchOne, mat
 | 
				
			||||||
		o.ha(sE,2),b.h9<100?xD(0,__L([a8.jx[sE]]),3,sE,ad.gN,ad.kl,-1,!0):xD(0,__L([a8.jx[sE]]),3,sE,ad.gN,ad.kl,-1,!0)`);
 | 
							o.ha(sE,2),b.h9<100?xD(0,__L([a8.jx[sE]]),3,sE,ad.gN,ad.kl,-1,!0):xD(0,__L([a8.jx[sE]]),3,sE,ad.gN,ad.kl,-1,!0)`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    { // Add settings button, custom lobby button and win count
 | 
					    { // Add settings button and win count
 | 
				
			||||||
        // add buttons
 | 
					        // add settings button
 | 
				
			||||||
        replaceRawCode(`,new nQ("☰<br>"+__L(),function(){aD6(3)},aa.ks),new nQ("",function(){at.d5(12)},aa.kg,!1)]`,
 | 
					        replaceRawCode(`,new nQ("☰<br>"+__L(),function(){aD6(3)},aa.ks),new nQ("",function(){at.d5(12)},aa.kg,!1)]`,
 | 
				
			||||||
            `,new nQ("☰<br>"+__L(),function(){aD6(3)},aa.ks),new nQ("",function(){at.d5(12)},aa.kg,!1),
 | 
					            `,new nQ("☰<br>"+__L(),function(){aD6(3)},aa.ks),new nQ("",function(){at.d5(12)},aa.kg,!1),
 | 
				
			||||||
            new nQ("FX Client settings", function() { __fx.WindowManager.openWindow("settings"); }, "rgba(0, 0, 20, 0.5)"),
 | 
					            new nQ("FX Client settings", function() { __fx.WindowManager.openWindow("settings"); }, "rgba(0, 0, 20, 0.5")]`)
 | 
				
			||||||
            new nQ("Join/Create custom lobby", function() { __fx.customLobby.showJoinPrompt(); }, "rgba(20, 9, 77, 0.5)")]`)
 | 
					        // set settings button position
 | 
				
			||||||
        // set position
 | 
					 | 
				
			||||||
        replaceRawCode(`aZ.g5.vO(aD3[3].button,x+a0S+gap,a3X+h+gap,a0S,h);`,
 | 
					        replaceRawCode(`aZ.g5.vO(aD3[3].button,x+a0S+gap,a3X+h+gap,a0S,h);`,
 | 
				
			||||||
            `aZ.g5.vO(aD3[3].button,x+a0S+gap,a3X+h+gap,a0S,h);
 | 
					            `aZ.g5.vO(aD3[3].button,x+a0S+gap,a3X+h+gap,a0S,h); aZ.g5.vO(aD3[5].button, x, a3X + h * 2 + gap * 2, a0S * 2 + gap, h / 3);`);
 | 
				
			||||||
            aZ.g5.vO(aD3[5].button, x, a3X + h * 2 + gap * 2, a0S * 2 + gap, h / 3);
 | 
					 | 
				
			||||||
            aZ.g5.vO(aD3[6].button, x, a3X + h * 2.33 + gap * 3, a0S * 2 + gap, h / 3);`);
 | 
					 | 
				
			||||||
        // render win count
 | 
					        // render win count
 | 
				
			||||||
        replaceRawCode(`if(_y.a4l(),_r.gI(),_m.gI(),aw.gI(),a0.g8()){ctx.imageSmoothingEnabled=!1;var iQ=a0.a4o("territorial.io"),kL=.84*aD4.gA/iQ.width;`,
 | 
					        replaceRawCode(`if(_y.a4l(),_r.gI(),_m.gI(),aw.gI(),a0.g8()){ctx.imageSmoothingEnabled=!1;var iQ=a0.a4o("territorial.io"),kL=.84*aD4.gA/iQ.width;`,
 | 
				
			||||||
            `if(_y.a4l(),_r.gI(),_m.gI(),aw.gI(),a0.g8()){
 | 
					            `if(_y.a4l(),_r.gI(),_m.gI(),aw.gI(),a0.g8()){
 | 
				
			||||||
| 
						 | 
					@ -140,7 +137,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`);
 | 
					        `, ${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
 | 
					    // 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(), ");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    { // Player list and leaderboard filter tabs
 | 
					    { // Player list and leaderboard filter tabs
 | 
				
			||||||
        // Draw player list button
 | 
					        // Draw player list button
 | 
				
			||||||
| 
						 | 
					@ -296,37 +293,6 @@ canvas.font=aY.g0.g1(1,fontSize),canvas.fillStyle="rgba("+gR+","+tD+","+hj+",0.6
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    { // Custom lobbies
 | 
					 | 
				
			||||||
        replaceRawCode("this.aHm=function(){i___.rX(),aM.a7U(bY.dZ.data[10].value),aM.init()}",
 | 
					 | 
				
			||||||
            `this.aHm=function(){i___.rX(),aM.a7U(bY.dZ.data[10].value),aM.init()},
 | 
					 | 
				
			||||||
            __fx.customLobby.setJoinFunction(() => { i___.rX(); aM.a7U(0); aM.init(); })`
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        replaceRawCode(`(socketId-aq.kt.a82)+"/",(socket=new WebSocket(url)`,
 | 
					 | 
				
			||||||
            `(socketId-aq.kt.a82)+"/",(socket=new WebSocket(__fx.customLobby.isActive() && socketId === 1 ? __fx.customLobby.getSocketURL() : url)`)
 | 
					 | 
				
			||||||
        replaceRawCode("this.send=function(socketId,data){aJE(socketId),aJ4[socketId].send(data)}",
 | 
					 | 
				
			||||||
            "this.send=function(socketId,data){aJE(socketId),aJ4[socketId].send(data)},__fx.customLobby.setSendFunction(this.send)")
 | 
					 | 
				
			||||||
        replaceRawCode("b7.dH(a0),0===b7.size?aq.kt.aJJ(wR,3205):",
 | 
					 | 
				
			||||||
            "b7.dH(a0),0===b7.size?aq.kt.aJJ(wR,3205):__fx.customLobby.isCustomMessage(a0)||")
 | 
					 | 
				
			||||||
        // set the custom lobby to inactive when clicking the "Back" button on the connection screen or leaving the lobby
 | 
					 | 
				
			||||||
        replaceRawCode("this.xZ=function(){Sockets.kt.wf(3260),i___.kt.we()}",
 | 
					 | 
				
			||||||
            "this.xZ=function(){Sockets.kt.wf(3260),__fx.customLobby.setActive(false),i___.kt.we()}")
 | 
					 | 
				
			||||||
        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())`)
 | 
					 | 
				
			||||||
        /* // if this is needed again, include a check for menu state === 6
 | 
					 | 
				
			||||||
        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);")*/
 | 
					 | 
				
			||||||
        // 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
 | 
					 | 
				
			||||||
        const errors = { 3249: "No servers found", 4705: "Lobby not found" };
 | 
					 | 
				
			||||||
        replaceRawCode(`i___.j(4,5,new k("⚠️ "+title,w3__,!0))`,
 | 
					 | 
				
			||||||
            `i___.j(4,5,new k("⚠️ "+title, ${JSON.stringify(errors)}[w3__] || w3__,!0))`)
 | 
					 | 
				
			||||||
        // 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(),")
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Invalid hostname detection avoidance
 | 
					    // Invalid hostname detection avoidance
 | 
				
			||||||
    replaceRawCode(`,hostnameIsValid=0<=window.location.hostname.toLowerCase().indexOf("territorial.io"),`,
 | 
					    replaceRawCode(`,hostnameIsValid=0<=window.location.hostname.toLowerCase().indexOf("territorial.io"),`,
 | 
				
			||||||
        `,hostnameIsValid=true,`)
 | 
					        `,hostnameIsValid=true,`)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
								
									
									
										
											17
										
									
									readme.md
									
									
									
									
								
								
							
							
										
											17
										
									
									readme.md
									
									
									
									
								| 
						 | 
					@ -25,18 +25,17 @@ FX Client is the first Territorial.io client, offering a better User Interface a
 | 
				
			||||||
4. Displays your troop density and maximum troops
 | 
					4. Displays your troop density and maximum troops
 | 
				
			||||||
5. Displays the density of players and bots
 | 
					5. Displays the density of players and bots
 | 
				
			||||||
6. Adds a "Clan" tab on the leaderboard, allowing you to easily see your clanmates
 | 
					6. Adds a "Clan" tab on the leaderboard, allowing you to easily see your clanmates
 | 
				
			||||||
7. Adds custom lobbies
 | 
					7. Hovering tooltip: makes the territory map information (normally visible on right click) be visible constantly (on hover)
 | 
				
			||||||
8. Hovering tooltip: makes the territory map information (normally visible on right click) be visible constantly (on hover)
 | 
					8. Adds a player list
 | 
				
			||||||
9. Adds a player list
 | 
					9. Adds the ability to view the history of who donated to a player during a team game by clicking on their name in the leaderboard or the player list
 | 
				
			||||||
10. Adds the ability to view the history of who donated to a player during a team game by clicking on their name in the leaderboard or the player list
 | 
					10. Adds a win counter
 | 
				
			||||||
11. Adds a win counter
 | 
					11. Can be installed as a PWA (progressive web app) ensuring maximum enjoyment on consoles, phones and even desktop devices
 | 
				
			||||||
12. Can be installed as a PWA (progressive web app) ensuring maximum enjoyment on consoles, phones and even desktop devices
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### The client has a settings menu, from which you can:
 | 
					#### The client has a settings menu, from which you can:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
13. Make fullscreen mode trigger automatically
 | 
					12. Make fullscreen mode trigger automatically
 | 
				
			||||||
14. Set a custom main menu background
 | 
					13. Set a custom main menu background
 | 
				
			||||||
15. Create custom attack percentage keybinds
 | 
					14. Create custom attack percentage keybinds
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Building Locally
 | 
					## Building Locally
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,181 +0,0 @@
 | 
				
			||||||
import WindowManager from "./windowManager.js";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//const socketURL = "ws://localhost:8080/";
 | 
					 | 
				
			||||||
const socketURL = "wss://fx.peshomir.workers.dev/";
 | 
					 | 
				
			||||||
const customMessageMarker = 120;
 | 
					 | 
				
			||||||
let isActive = false;
 | 
					 | 
				
			||||||
let currentCode = "";
 | 
					 | 
				
			||||||
let joinLobby = () => { };
 | 
					 | 
				
			||||||
let leaveLobby = () => { };
 | 
					 | 
				
			||||||
let sendRaw = (socketId, data) => { };
 | 
					 | 
				
			||||||
const textEncoder = new TextEncoder();
 | 
					 | 
				
			||||||
const textDecoder = new TextDecoder();
 | 
					 | 
				
			||||||
/*const gameInfo = {
 | 
					 | 
				
			||||||
    botCount: 512
 | 
					 | 
				
			||||||
}*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const windowElement = WindowManager.create({
 | 
					 | 
				
			||||||
    name: "customLobby",
 | 
					 | 
				
			||||||
    classes: "scrollable selectable flex-column text-align-center",
 | 
					 | 
				
			||||||
    closable: false
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const header = document.createElement("h2");
 | 
					 | 
				
			||||||
header.textContent = "Custom Lobby";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const main = document.createElement("div");
 | 
					 | 
				
			||||||
main.className = "customlobby-main";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const playerListContainer = document.createElement("div");
 | 
					 | 
				
			||||||
const playerCount = document.createElement("p");
 | 
					 | 
				
			||||||
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);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
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);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const footer = document.createElement("footer");
 | 
					 | 
				
			||||||
footer.style.marginTop = "10px";
 | 
					 | 
				
			||||||
const startButton = document.createElement("button");
 | 
					 | 
				
			||||||
const leaveButton = document.createElement("button");
 | 
					 | 
				
			||||||
startButton.textContent = "Start game";
 | 
					 | 
				
			||||||
leaveButton.textContent = "Leave lobby";
 | 
					 | 
				
			||||||
startButton.addEventListener("click", startGame);
 | 
					 | 
				
			||||||
leaveButton.addEventListener("click", () => leaveLobby());
 | 
					 | 
				
			||||||
footer.append(leaveButton, startButton);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
windowElement.append(header, main, footer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** @param {HTMLSelectElement} element  */
 | 
					 | 
				
			||||||
function setSelectMenuOptions(options, element) {
 | 
					 | 
				
			||||||
    options.forEach(data => {
 | 
					 | 
				
			||||||
        const option = document.createElement("option");
 | 
					 | 
				
			||||||
        option.setAttribute("value", data.value);
 | 
					 | 
				
			||||||
        option.textContent = data.label;
 | 
					 | 
				
			||||||
        element.append(option);
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function showJoinPrompt() {
 | 
					 | 
				
			||||||
    const code = prompt("Enter a lobby code or leave blank to create a new lobby");
 | 
					 | 
				
			||||||
    if (code === null) return;
 | 
					 | 
				
			||||||
    currentCode = code;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    //WindowManager.openWindow("customLobby");
 | 
					 | 
				
			||||||
    isActive = true;
 | 
					 | 
				
			||||||
    joinLobby();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
function sendMessage(type, data) {
 | 
					 | 
				
			||||||
    const message = data !== undefined ? { t: type, d: data } : { t: type }
 | 
					 | 
				
			||||||
    const originalArray = textEncoder.encode(JSON.stringify(message));
 | 
					 | 
				
			||||||
    const buffer = new ArrayBuffer(originalArray.length + 1);
 | 
					 | 
				
			||||||
    const view = new DataView(buffer);
 | 
					 | 
				
			||||||
    // Set the first byte to the custom message marker
 | 
					 | 
				
			||||||
    view.setUint8(0, customMessageMarker);
 | 
					 | 
				
			||||||
    // Copy the original array starting from the second byte
 | 
					 | 
				
			||||||
    const uint8ArrayView = new Uint8Array(buffer, 1);
 | 
					 | 
				
			||||||
    uint8ArrayView.set(originalArray);
 | 
					 | 
				
			||||||
    sendRaw(1, buffer);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
/** @param {Uint8Array} raw */
 | 
					 | 
				
			||||||
function isCustomMessage(raw) {
 | 
					 | 
				
			||||||
    if (raw[0] !== customMessageMarker) return false;
 | 
					 | 
				
			||||||
    if (raw.length === 1) return true; // ping
 | 
					 | 
				
			||||||
    const subArray = new Uint8Array(raw.buffer, 1);
 | 
					 | 
				
			||||||
    const message = JSON.parse(textDecoder.decode(subArray));
 | 
					 | 
				
			||||||
    const { t: type, d: data } = message;
 | 
					 | 
				
			||||||
    if (type === "lobby") {
 | 
					 | 
				
			||||||
        WindowManager.openWindow("customLobby");
 | 
					 | 
				
			||||||
        header.textContent = "Custom Lobby " + data.code;
 | 
					 | 
				
			||||||
        gameModeSelectMenu.value = data.options.mode.toString();
 | 
					 | 
				
			||||||
        mapSelectMenu.value = data.options.map.toString();
 | 
					 | 
				
			||||||
        displayPlayers(data.players);
 | 
					 | 
				
			||||||
    } else if (type === "addPlayer") addPlayer(data);
 | 
					 | 
				
			||||||
    else if (type === "removePlayer") {
 | 
					 | 
				
			||||||
        const index = data;
 | 
					 | 
				
			||||||
        playerElements[index].remove();
 | 
					 | 
				
			||||||
        playerElements.splice(index, 1);
 | 
					 | 
				
			||||||
        updatePlayerCount();
 | 
					 | 
				
			||||||
    } 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();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
/** @type {HTMLDivElement[]} */
 | 
					 | 
				
			||||||
let playerElements = [];
 | 
					 | 
				
			||||||
/** @param {{ name: string }} player */
 | 
					 | 
				
			||||||
function addPlayer(player) {
 | 
					 | 
				
			||||||
    const div = document.createElement("div");
 | 
					 | 
				
			||||||
    div.className = "lobby-player";
 | 
					 | 
				
			||||||
    div.textContent = player.name;
 | 
					 | 
				
			||||||
    playerList.append(div);
 | 
					 | 
				
			||||||
    playerElements.push(div);
 | 
					 | 
				
			||||||
    updatePlayerCount();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
/** @param {{ name: string }[]} players */
 | 
					 | 
				
			||||||
function displayPlayers(players) {
 | 
					 | 
				
			||||||
    playerElements = [];
 | 
					 | 
				
			||||||
    playerList.innerHTML = "";
 | 
					 | 
				
			||||||
    players.forEach(addPlayer);
 | 
					 | 
				
			||||||
    updatePlayerCount();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
function updatePlayerCount() {
 | 
					 | 
				
			||||||
    playerCount.textContent = `${playerElements.length} Player${playerElements.length === 1 ? "" : "s"}`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function getSocketURL() {
 | 
					 | 
				
			||||||
    return socketURL + (currentCode === "" ? "create" : "join?" + currentCode)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
function startGame() {
 | 
					 | 
				
			||||||
    WindowManager.closeWindow("customLobby");
 | 
					 | 
				
			||||||
    sendMessage("startGame" /*{ mode: gameModeSelectMenu.value, map: mapSelectMenu.value }*/);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function setJoinFunction(f) { joinLobby = f; }
 | 
					 | 
				
			||||||
function setLeaveFunction(f) { leaveLobby = f; }
 | 
					 | 
				
			||||||
function setSendFunction(f) { sendRaw = f; }
 | 
					 | 
				
			||||||
function setActive(active) {
 | 
					 | 
				
			||||||
    isActive = active;
 | 
					 | 
				
			||||||
    if (active === false) WindowManager.closeWindow("customLobby");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
const gameInterface = { showJoinPrompt, isCustomMessage, getSocketURL, setJoinFunction, setLeaveFunction, setSendFunction, setMapInfo, isActive: () => isActive, setActive }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const customLobby = gameInterface
 | 
					 | 
				
			||||||
export default customLobby
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
const playerDataProperties = ["playerTerritories", "playerBalances", "rawPlayerNames"];
 | 
					const playerDataProperties = ["playerTerritories", "playerBalances", "rawPlayerNames"];
 | 
				
			||||||
const gameObjectProperties = ["playerId", "gIsTeamGame", "gHumans", "gLobbyMaxJoin", "gameState", "gIsSingleplayer"];
 | 
					const gameObjectProperties = ["playerId", "gIsTeamGame", "gHumans", "gLobbyMaxJoin", "gameState", "gIsSingleplayer"];
 | 
				
			||||||
export const getVar = varName => {
 | 
					export const getVar = varName => {
 | 
				
			||||||
    if (playerDataProperties.includes(varName)) return window[dictionary.playerData]?.[dictionary[varName]];
 | 
					    if (playerDataProperties.includes(varName)) return window[dictionary.playerData][dictionary[varName]];
 | 
				
			||||||
    if (gameObjectProperties.includes(varName)) return window[dictionary.game]?.[dictionary[varName]];
 | 
					    if (gameObjectProperties.includes(varName)) return window[dictionary.game][dictionary[varName]];
 | 
				
			||||||
    return window[dictionary[varName]]
 | 
					    return window[dictionary[varName]]
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
const fx_version = '0.6.6'; // FX Client Version
 | 
					const fx_version = '0.6.5.6'; // FX Client Version
 | 
				
			||||||
const fx_update = 'Oct 14'; // FX Client Last Updated
 | 
					const fx_update = 'Oct 3'; // FX Client Last Updated
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import settingsManager from './settings.js';
 | 
					import settingsManager from './settings.js';
 | 
				
			||||||
import { clanFilter, leaderboardFilter } from "./clanFilters.js";
 | 
					import { clanFilter, leaderboardFilter } from "./clanFilters.js";
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,6 @@ import playerList from "./playerList.js";
 | 
				
			||||||
import gameScriptUtils from "./gameScriptUtils.js";
 | 
					import gameScriptUtils from "./gameScriptUtils.js";
 | 
				
			||||||
import hoveringTooltip from "./hoveringTooltip.js";
 | 
					import hoveringTooltip from "./hoveringTooltip.js";
 | 
				
			||||||
import { keybindFunctions, keybindHandler } from "./keybinds.js";
 | 
					import { keybindFunctions, keybindHandler } from "./keybinds.js";
 | 
				
			||||||
import customLobby from './customLobby.js';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.__fx = window.__fx || {};
 | 
					window.__fx = window.__fx || {};
 | 
				
			||||||
const __fx = window.__fx;
 | 
					const __fx = window.__fx;
 | 
				
			||||||
| 
						 | 
					@ -27,6 +26,5 @@ __fx.playerList = playerList;
 | 
				
			||||||
__fx.hoveringTooltip = hoveringTooltip;
 | 
					__fx.hoveringTooltip = hoveringTooltip;
 | 
				
			||||||
__fx.clanFilter = clanFilter;
 | 
					__fx.clanFilter = clanFilter;
 | 
				
			||||||
__fx.wins = winCounter;
 | 
					__fx.wins = winCounter;
 | 
				
			||||||
__fx.customLobby = customLobby;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
console.log('Successfully loaded FX Client');
 | 
					console.log('Successfully loaded FX Client');
 | 
				
			||||||
| 
						 | 
					@ -1,14 +1,4 @@
 | 
				
			||||||
var windows = {};
 | 
					var windows = {};
 | 
				
			||||||
const container = document.getElementById("windowContainer");
 | 
					 | 
				
			||||||
function create(info) {
 | 
					 | 
				
			||||||
    const window = document.createElement("div");
 | 
					 | 
				
			||||||
    info.element = window;
 | 
					 | 
				
			||||||
    window.className = "window" + (info.classes !== undefined ? " " + info.classes : " scrollable selectable");
 | 
					 | 
				
			||||||
    window.style.display = "none";
 | 
					 | 
				
			||||||
    container.appendChild(window);
 | 
					 | 
				
			||||||
    add(info);
 | 
					 | 
				
			||||||
    return window;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
function add(newWindow) {
 | 
					function add(newWindow) {
 | 
				
			||||||
    windows[newWindow.name] = newWindow;
 | 
					    windows[newWindow.name] = newWindow;
 | 
				
			||||||
    windows[newWindow.name].isOpen = false;
 | 
					    windows[newWindow.name].isOpen = false;
 | 
				
			||||||
| 
						 | 
					@ -27,11 +17,11 @@ function closeWindow(windowName) {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
function closeAll() {
 | 
					function closeAll() {
 | 
				
			||||||
    Object.values(windows).forEach(function (windowObj) {
 | 
					    Object.values(windows).forEach(function (windowObj) {
 | 
				
			||||||
        if (windowObj.closable !== false) closeWindow(windowObj.name);
 | 
					        closeWindow(windowObj.name);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
document.getElementById("canvasA").addEventListener("mousedown", closeAll);
 | 
					document.getElementById("canvasA").addEventListener("mousedown", closeAll);
 | 
				
			||||||
document.getElementById("canvasA").addEventListener("touchstart", closeAll, { passive: true });
 | 
					document.getElementById("canvasA").addEventListener("touchstart", closeAll, { passive: true });
 | 
				
			||||||
document.addEventListener("keydown", event => { if (event.key === "Escape") closeAll(); });
 | 
					document.addEventListener("keydown", event => { if (event.key === "Escape") closeAll(); });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default { create, add, openWindow, closeWindow, closeAll }
 | 
					export default { add, openWindow, closeWindow, closeAll }
 | 
				
			||||||
| 
						 | 
					@ -59,7 +59,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<body onload="aiCommand746(0);">
 | 
					<body onload="aiCommand746(0);">
 | 
				
			||||||
    <canvas id="canvasA" width="128" height="128"></canvas>
 | 
					    <canvas id="canvasA" width="128" height="128"></canvas>
 | 
				
			||||||
    <span id="windowContainer"><div class="window flex-column settings" style="display:none">
 | 
					    <span><div class="window flex settings" style="display:none">
 | 
				
			||||||
        <h1>Settings</h1>
 | 
					        <h1>Settings</h1>
 | 
				
			||||||
        <div class="scrollable"></div>
 | 
					        <div class="scrollable"></div>
 | 
				
			||||||
        <hr>
 | 
					        <hr>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,31 +34,11 @@
 | 
				
			||||||
	z-index         : 10;
 | 
						z-index         : 10;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.flex {
 | 
					.window.flex {
 | 
				
			||||||
	display: flex;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.flex-column {
 | 
					 | 
				
			||||||
	display       : flex;
 | 
						display       : flex;
 | 
				
			||||||
	flex-direction: column;
 | 
						flex-direction: column;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.customlobby-main {
 | 
					 | 
				
			||||||
	display: flex;
 | 
					 | 
				
			||||||
    justify-content: space-evenly;
 | 
					 | 
				
			||||||
    flex-wrap: wrap;
 | 
					 | 
				
			||||||
	gap: 10px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.lobby-player {
 | 
					 | 
				
			||||||
	margin: 5px;
 | 
					 | 
				
			||||||
	width: 15rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.text-align-center {
 | 
					 | 
				
			||||||
	text-align: center;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
hr {
 | 
					hr {
 | 
				
			||||||
	width: 100%;
 | 
						width: 100%;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -74,13 +54,6 @@ hr {
 | 
				
			||||||
	transition      : 0.2s;
 | 
						transition      : 0.2s;
 | 
				
			||||||
	border          : 1px solid #fff;
 | 
						border          : 1px solid #fff;
 | 
				
			||||||
	border-radius   : 5px;
 | 
						border-radius   : 5px;
 | 
				
			||||||
	margin          : 5px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.window.settings button,
 | 
					 | 
				
			||||||
.window.settings input,
 | 
					 | 
				
			||||||
.window.settings select {
 | 
					 | 
				
			||||||
	margin: 0px;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
h1 {
 | 
					h1 {
 | 
				
			||||||
| 
						 | 
					@ -123,16 +96,16 @@ td {
 | 
				
			||||||
#playerlist_content.clickable td:hover { background-color: #00ff0040; }
 | 
					#playerlist_content.clickable td:hover { background-color: #00ff0040; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
tr.new {
 | 
					tr.new {
 | 
				
			||||||
	animation: flashAnimation 0.4s ease-out;
 | 
					    animation: flashAnimation 0.4s ease-out;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@keyframes flashAnimation {
 | 
					@keyframes flashAnimation {
 | 
				
			||||||
	0% {
 | 
					    0% {
 | 
				
			||||||
		background-color: #ffffffaa;
 | 
					        background-color: #ffffffaa;
 | 
				
			||||||
	}
 | 
					    }
 | 
				
			||||||
	100% {
 | 
					    100% {
 | 
				
			||||||
		background-color: transparent;
 | 
					        background-color: transparent;
 | 
				
			||||||
	}
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
table {
 | 
					table {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue