Compare commits
	
		
			No commits in common. "ad6f0e2f05127b87946fe58bff8b6f4f182230ed" and "ce2e2468fde2980a7bcf58780a321ec7145a78d6" have entirely different histories. 
		
	
	
		
			ad6f0e2f05
			...
			ce2e2468fd
		
	
		
	
								
									
									
										
											125
										
									
									build.js
									
									
									
									
								
								
							
							
										
											125
										
									
									build.js
									
									
									
									
								| 
						 | 
					@ -78,8 +78,7 @@ const generateRegularExpression = (/** @type {string} */ code, /** @type {boolea
 | 
				
			||||||
	// this one also broke in 1.91.3 /,\w+="Player: "\+(?<playerNames>\w+)\[\w+\],\w+=\(\w\+="   Balance: "\+\w+\.\w+\((?<playerBalances>\w+)\[\w+\]\)\)\+\("   Territory: "\+\w+\.\w+\((?<playerTerritories>\w+)\[\w+\]\)\)\+\("   Coords: "/g,
 | 
						// this one also broke in 1.91.3 /,\w+="Player: "\+(?<playerNames>\w+)\[\w+\],\w+=\(\w\+="   Balance: "\+\w+\.\w+\((?<playerBalances>\w+)\[\w+\]\)\)\+\("   Territory: "\+\w+\.\w+\((?<playerTerritories>\w+)\[\w+\]\)\)\+\("   Coords: "/g,
 | 
				
			||||||
	///\((?<uiOffset>\w+)=Math\.floor\(\(\w+\?\.0114:\.01296\)\*\w+\)\)/g,
 | 
						///\((?<uiOffset>\w+)=Math\.floor\(\(\w+\?\.0114:\.01296\)\*\w+\)\)/g,
 | 
				
			||||||
	/(function \w+\((\w+),(\w+),(\w+),(\w+),(\w+)\){\6\.fillText\((?<playerNames>\w+)\[\2\],\4,\5\)),(\2<(?<gHumans>\w+)&&2!==(?<playerStates>\w+)\[)/g,
 | 
						/(function \w+\((\w+),(\w+),(\w+),(\w+),(\w+)\){\6\.fillText\((?<playerNames>\w+)\[\2\],\4,\5\)),(\2<(?<gHumans>\w+)&&2!==(?<playerStates>\w+)\[)/g,
 | 
				
			||||||
	/,\w+=512,(?<gLobbyMaxJoin>\w+)=\w+,(?<gIsSingleplayer>\w+)&&\(\1=\w+\.\w+\(\)\),\w+=\1-\w+,\w+=0,/g,
 | 
						/,\w+=512,(?<gLobbyMaxJoin>\w+)=\w+,(?<gIsSingleplayer>\w+)&&\(\1=\w+\.\w+\(\)\),\w+=\1-\w+,\w+=0,/g
 | 
				
			||||||
	/function \w+\(\)\{if\(2===(?<gameState>\w+)\)return 1;\w+\.\w+\(\),\1=2,\w+=\w+\}/g
 | 
					 | 
				
			||||||
].forEach(matchDictionaryExpression);
 | 
					].forEach(matchDictionaryExpression);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const rawCodeSegments = [
 | 
					const rawCodeSegments = [
 | 
				
			||||||
| 
						 | 
					@ -220,35 +219,26 @@ replaceOne(/(this\.\w+=function\((\w+),(\w+)\)\{)(\2===\w+&&\(\w+\.\w+\((\w+\.\w
 | 
				
			||||||
"$1 donationsTracker.logDonation($2, $3, $5[0]); $4")
 | 
					"$1 donationsTracker.logDonation($2, $3, $5[0]); $4")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Display donations for a player when clicking on them in the leaderboard
 | 
					// Display donations for a player when clicking on them in the leaderboard
 | 
				
			||||||
// and skip handling clicks when clicking on an empty space (see the isEmptySpace
 | 
					 | 
				
			||||||
// variable in the modified leaderboard click handler from the leaderboard filter)
 | 
					 | 
				
			||||||
// match , 0 !== dG[x]) && fq.hB(x, 800, false, 0),
 | 
					// match , 0 !== dG[x]) && fq.hB(x, 800, false, 0),
 | 
				
			||||||
replaceOne(/,(0!==\w+\[(\w+)\])(\)&&\w+\.\w+\(\2,800,!1,0\),)/g,
 | 
					replaceOne(/,(0!==\w+\[(\w+)\]\)&&\w+\.\w+\(\2,800,!1,0\),)/g,
 | 
				
			||||||
	`, ${dictionary.gIsTeamGame} && donationsTracker.displayHistory($2, ${dictionary.playerNames}, ${dictionary.gIsSingleplayer}), $1 && !isEmptySpace $3`);
 | 
						`, ${dictionary.gIsTeamGame} && donationsTracker.displayHistory($2, ${dictionary.playerNames}, ${dictionary.gIsSingleplayer}), $1`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Reset donation history and leaderboard filter when a new game is started
 | 
					// Reset donation history when a new game is started
 | 
				
			||||||
replaceOne(new RegExp(`,${dictionary.playerBalances}=new Uint32Array\\(\\w+\\),`, "g"), "$& donationsTracker.reset(), leaderboardFilter.reset(), ");
 | 
					replaceOne(new RegExp(`,${dictionary.playerBalances}=new Uint32Array\\(\\w+\\),`, "g"), "$& donationsTracker.reset(), ");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{ // Player list and leaderboard filter tabs
 | 
					{ // Player list
 | 
				
			||||||
	// Draw player list button
 | 
						// Draw player list button
 | 
				
			||||||
	const uiOffset = dictionary.uiSizes + "." + dictionary.gap;
 | 
						const uiOffset = dictionary.uiSizes + "." + dictionary.gap;
 | 
				
			||||||
	const { groups: { drawFunction, topBarHeight } } = replaceOne(/(=1;function (?<drawFunction>\w+)\(\){[^}]+?(?<canvas>\w+)\.fillRect\(0,(?<topBarHeight>\w+),\w+,1\),(?:\3\.fillRect\([^()]+\),)+\3\.font=\w+,(\w+\.\w+)\.textBaseline\(\3,1\),\5\.textAlign\(\3,1\),\3\.fillText\(\w+\.\w+\[65\],Math\.floor\()(\w+)\/2\),(Math\.floor\(\w+\+\w+\/2\)\));/g,
 | 
						const { groups: { drawFunction, topBarHeight } } = replaceOne(/(=1;function (?<drawFunction>\w+)\(\){[^}]+?(?<canvas>\w+)\.fillRect\(0,(?<topBarHeight>\w+),\w+,1\),(?:\3\.fillRect\([^()]+\),)+\3\.font=\w+,(\w+\.\w+)\.textBaseline\(\3,1\),\5\.textAlign\(\3,1\),\3\.fillText\(\w+\.\w+\[65\],Math\.floor\()(\w+)\/2\),(Math\.floor\(\w+\+\w+\/2\)\));/g,
 | 
				
			||||||
	"$1($6 + $<topBarHeight> - 22) / 2), $7; playerList.drawButton($<canvas>, 12, 12, $<topBarHeight> - 22);");
 | 
						"$1($6 + $<topBarHeight> - 22) / 2), $7; playerList.drawButton($<canvas>, 12, 12, $<topBarHeight> - 22);");
 | 
				
			||||||
	const buttonBoundsCheck = `utils.isPointInRectangle($<x>, $<y>, ${uiOffset} + 12, ${uiOffset} + 12, ${topBarHeight} - 22, ${topBarHeight} - 22)`
 | 
						const buttonBoundsCheck = `utils.isPointInRectangle($<x>, $<y>, ${uiOffset} + 12, ${uiOffset} + 12, ${topBarHeight} - 22, ${topBarHeight} - 22)`
 | 
				
			||||||
	// Handle player list button and leaderboard tabs mouseDown
 | 
						// Handle player list button mouseDown
 | 
				
			||||||
	// and create a function for scrolling the leaderboard to the top
 | 
						replaceOne(/(this\.\w+=function\((?<x>\w+),(?<y>\w+)\){return!!\w+\(\2,\3\))&&(\(\w+=\w+\.\w+,)/g,
 | 
				
			||||||
	replaceOne(/(this\.\w+=function\((?<x>\w+),(?<y>\w+)\){return!!\w+\(\2,\3\))&&(\(\w+=\w+\.\w+,[^}]+),!0\)/g,
 | 
						`$1 && (${buttonBoundsCheck} && playerList.display(${dictionary.playerNames}), true) && $4`);
 | 
				
			||||||
	`leaderboardFilter.scrollToTop = function(){position = 0;}, $1 && ((${buttonBoundsCheck} && playerList.display(${dictionary.playerNames}), true)
 | 
						// Handle player list button hover
 | 
				
			||||||
		&& !($<y> - ${uiOffset} > leaderboardFilter.verticalClickThreshold && leaderboardFilter.handleMouseDown($<x> - ${uiOffset})) && $4),!0)`);
 | 
					 | 
				
			||||||
	// Handle player list button and leaderboard tabs hover
 | 
					 | 
				
			||||||
	// and create a function for repainting the leaderboard
 | 
					 | 
				
			||||||
	replaceOne(/(this\.\w+=function\((?<x>\w+),(?<y>\w+)\){)(var \w+,\w+=\w+\(\3\);return \w+\?\(\w+=(\w+),\(\5=\w+\(0,\5\+=(?:[^}]+,(?<setRepaintNeeded>\w+\.\w+=!0)){2})/g,
 | 
						replaceOne(/(this\.\w+=function\((?<x>\w+),(?<y>\w+)\){)(var \w+,\w+=\w+\(\3\);return \w+\?\(\w+=(\w+),\(\5=\w+\(0,\5\+=(?:[^}]+,(?<setRepaintNeeded>\w+\.\w+=!0)){2})/g,
 | 
				
			||||||
	`leaderboardFilter.repaintLeaderboard = function() { ${drawFunction}(), $<setRepaintNeeded>; },
 | 
						`$1 if (${buttonBoundsCheck}) { playerList.hoveringOverButton === false && (playerList.hoveringOverButton = true, ${drawFunction}(), $<setRepaintNeeded>); } `
 | 
				
			||||||
	$1 if (${buttonBoundsCheck}) { playerList.hoveringOverButton === false && (playerList.hoveringOverButton = true, ${drawFunction}(), $<setRepaintNeeded>); }
 | 
						+ ` else { playerList.hoveringOverButton === true && (playerList.hoveringOverButton = false, ${drawFunction}(), $<setRepaintNeeded>); } $4`);
 | 
				
			||||||
	else { playerList.hoveringOverButton === true && (playerList.hoveringOverButton = false, ${drawFunction}(), $<setRepaintNeeded>); }
 | 
					 | 
				
			||||||
	if (leaderboardFilter.setHovering(
 | 
					 | 
				
			||||||
		utils.isPointInRectangle($<x>, $<y>, ${uiOffset}, ${uiOffset} + leaderboardFilter.verticalClickThreshold, leaderboardFilter.windowWidth, leaderboardFilter.tabBarOffset), $<x> - ${uiOffset}
 | 
					 | 
				
			||||||
	)) return; $4`);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{ // Display density of other players
 | 
					{ // Display density of other players
 | 
				
			||||||
| 
						 | 
					@ -260,97 +250,6 @@ replaceOne(new RegExp(`,${dictionary.playerBalances}=new Uint32Array\\(\\w+\\),`
 | 
				
			||||||
	`$1 var ___id = $2; $7, $9; ${settingsSwitchNameAndBalance} && settings.showPlayerDensity && (settings.coloredDensity && ($<canvas>.fillStyle = utils.textStyleBasedOnDensity(___id)), $<canvas>.fillText(utils.getDensity(___id), $<x>, $<y> + $<fontSize>)); }`);
 | 
						`$1 var ___id = $2; $7, $9; ${settingsSwitchNameAndBalance} && settings.showPlayerDensity && (settings.coloredDensity && ($<canvas>.fillStyle = utils.textStyleBasedOnDensity(___id)), $<canvas>.fillText(utils.getDensity(___id), $<x>, $<y> + $<fontSize>)); }`);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{ // Leaderboard filter
 | 
					 | 
				
			||||||
	// for the leaderboard draw function:
 | 
					 | 
				
			||||||
	replaceRawCode("a0A.clearRect(0,0,a04,y9),a0A.fillStyle=aZ.lE,a0A.fillRect(0,0,a04,a0F),a0A.fillStyle=aZ.kZ,a0A.fillRect(0,a0F,a04,y9-a0F),leaderboardPositionsById[playerId]>=position&&a0Z(leaderboardPositionsById[playerId]-position,aZ.kw),0!==leaderboardPositionsById[playerId]&&0===position&&a0Z(0,aZ.lJ),-1!==a0P&&a0Z(a0P,aZ.kd),a0A.fillStyle=aZ.gF,a0A.fillRect(0,a0F,a04,1),a0A.fillRect(0,0,a04,b0.ur),a0A.fillRect(0,0,b0.ur,y9),a0A.fillRect(a04-b0.ur,0,b0.ur,y9),a0A.fillRect(0,y9-b0.ur,a04,b0.ur),",
 | 
					 | 
				
			||||||
		`a0A.clearRect(0, 0, a04, y9),
 | 
					 | 
				
			||||||
		a0A.fillStyle = aZ.lE,
 | 
					 | 
				
			||||||
		a0A.fillRect(0, 0, a04, a0F),
 | 
					 | 
				
			||||||
		a0A.fillStyle = aZ.kZ,
 | 
					 | 
				
			||||||
		a0A.fillRect(0, a0F, a04, y9 - a0F);
 | 
					 | 
				
			||||||
		if (leaderboardFilter.enabled) {
 | 
					 | 
				
			||||||
			leaderboardFilter.filteredLeaderboard = leaderboardFilter.playersToInclude
 | 
					 | 
				
			||||||
				.map(id => leaderboardPositionsById[id]).sort((a, b) => a - b);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		var playerPos = (leaderboardFilter.enabled
 | 
					 | 
				
			||||||
			? leaderboardFilter.filteredLeaderboard.indexOf(leaderboardPositionsById[playerId])
 | 
					 | 
				
			||||||
			: leaderboardPositionsById[playerId]
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		this.playerPos = playerPos;
 | 
					 | 
				
			||||||
		if (leaderboardFilter.hoveringOverTabs) a0P = -1;
 | 
					 | 
				
			||||||
		if (leaderboardFilter.enabled && a0P >= leaderboardFilter.filteredLeaderboard.length) a0P = -1;
 | 
					 | 
				
			||||||
		playerPos >= position && a0Z(playerPos - position, aZ.kw),
 | 
					 | 
				
			||||||
		0 !== leaderboardPositionsById[playerId] && 0 === position && a0Z(0, aZ.lJ),
 | 
					 | 
				
			||||||
		-1 !== a0P && a0Z(a0P, aZ.kd),
 | 
					 | 
				
			||||||
		a0A.fillStyle = aZ.kZ,
 | 
					 | 
				
			||||||
		//console.log("drawing", a0P),
 | 
					 | 
				
			||||||
		a0A.clearRect(0, y9 - leaderboardFilter.tabBarOffset, a04, leaderboardFilter.tabBarOffset);
 | 
					 | 
				
			||||||
		a0A.fillRect(0, y9 - leaderboardFilter.tabBarOffset, a04, leaderboardFilter.tabBarOffset);
 | 
					 | 
				
			||||||
		a0A.fillStyle = aZ.gF,
 | 
					 | 
				
			||||||
		a0A.fillRect(0, a0F, a04, 1),
 | 
					 | 
				
			||||||
		a0A.fillRect(0, y9 - leaderboardFilter.tabBarOffset, a04, 1),
 | 
					 | 
				
			||||||
		leaderboardFilter.drawTabs(a0A, a04, y9 - leaderboardFilter.tabBarOffset, aZ.kw),
 | 
					 | 
				
			||||||
		a0A.fillRect(0, 0, a04, b0.ur),
 | 
					 | 
				
			||||||
		a0A.fillRect(0, 0, b0.ur, y9),
 | 
					 | 
				
			||||||
		a0A.fillRect(a04 - b0.ur, 0, b0.ur, y9),
 | 
					 | 
				
			||||||
		a0A.fillRect(0, y9 - b0.ur, a04, b0.ur),`)
 | 
					 | 
				
			||||||
	replaceRawCode("var hZ,eh=leaderboardPositionsById[playerId]<position+windowHeight-1?1:2;for(a0A.font=a07,aY.g0.textAlign(a0A,0),hZ=windowHeight-eh;0<=hZ;hZ--)a0a(leaderboardArray[hZ+position]),a0b(hZ,hZ+position,leaderboardArray[hZ+position]);for(aY.g0.textAlign(a0A,2),hZ=windowHeight-eh;0<=hZ;hZ--)a0a(leaderboardArray[hZ+position]),a0c(hZ,leaderboardArray[hZ+position]);",
 | 
					 | 
				
			||||||
		`var hZ, eh = playerPos < position + windowHeight - 1 ? 1 : 2;
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		if (leaderboardFilter.enabled) {
 | 
					 | 
				
			||||||
			let result = leaderboardFilter.filteredLeaderboard;
 | 
					 | 
				
			||||||
			if (position !== 0 && position >= result.length - windowHeight)
 | 
					 | 
				
			||||||
				position = (result.length > windowHeight ? result.length : windowHeight) - windowHeight;
 | 
					 | 
				
			||||||
			//if (position >= result.length) position = result.length - 1;
 | 
					 | 
				
			||||||
			for (a0A.font = a07, aY.g0.textAlign(a0A, 0), hZ = windowHeight - eh; 0 <= hZ; hZ--) {
 | 
					 | 
				
			||||||
				const pos = result[hZ + position];
 | 
					 | 
				
			||||||
				if (pos !== undefined)
 | 
					 | 
				
			||||||
					a0a(leaderboardArray[pos]), a0b(hZ, pos, leaderboardArray[pos]);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			for (aY.g0.textAlign(a0A, 2), hZ = windowHeight - eh; 0 <= hZ; hZ--) {
 | 
					 | 
				
			||||||
				const pos = result[hZ + position];
 | 
					 | 
				
			||||||
				if (pos !== undefined)
 | 
					 | 
				
			||||||
					a0a(leaderboardArray[pos]), a0c(hZ, leaderboardArray[pos]);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			for (a0A.font = a07, aY.g0.textAlign(a0A, 0), hZ = windowHeight - eh; 0 <= hZ; hZ--)
 | 
					 | 
				
			||||||
				a0a(leaderboardArray[hZ + position]), a0b(hZ, hZ + position, leaderboardArray[hZ + position]);
 | 
					 | 
				
			||||||
			for (aY.g0.textAlign(a0A, 2), hZ = windowHeight - eh; 0 <= hZ; hZ--)
 | 
					 | 
				
			||||||
				a0a(leaderboardArray[hZ + position]), a0c(hZ, leaderboardArray[hZ + position]);
 | 
					 | 
				
			||||||
		}`)
 | 
					 | 
				
			||||||
	// in the leaderboard resize handler: make space for the tab buttons at the bottom of the leaderboard
 | 
					 | 
				
			||||||
	replaceRawCode(",a0D=.025*a04,a06=.16*a04,a0E=0*a04,a0F=Math.floor(.45*a0D+a06),a0G=(y9-a06-2*a0D-a0E)/a08,a05=aY.g0.g1(1,Math.floor(.55*a06)),",
 | 
					 | 
				
			||||||
		`,a0D=.025*a04,a06=.16*a04,a0E=0*a04,a0F=Math.floor(.45*a0D+a06),a0G=(y9-a06-2*a0D-a0E)/a08,
 | 
					 | 
				
			||||||
		a09.height = y9 += a0G, leaderboardFilter.tabBarOffset = Math.floor(a0G * 1.3), leaderboardFilter.verticalClickThreshold = y9 - leaderboardFilter.tabBarOffset, leaderboardFilter.windowWidth = a04,
 | 
					 | 
				
			||||||
		a05=aY.g0.g1(1,Math.floor(.55*a06)),`)
 | 
					 | 
				
			||||||
	// handle clicking on a player in the leaderboard
 | 
					 | 
				
			||||||
	replaceRawCode("var a0p=a0q(fJ);return ag.tQ()&&-1!==a0P&&(a0P=-1,a0Y(),b3.d1=!0),b3.dY-a0Q<350&&a0T===a0p&&-1!==(a0p=(a0p=yr(-1,a0p,windowHeight))!==windowHeight&&vU(x,y)?a0p:-1)&&(x=leaderboardArray[a0p+position],a0p===windowHeight-1&&leaderboardPositionsById[playerId]>=position+windowHeight-1&&(x=playerId),",
 | 
					 | 
				
			||||||
		`var a0p = a0q(fJ);
 | 
					 | 
				
			||||||
		var isEmptySpace = false;
 | 
					 | 
				
			||||||
		return ag.tQ() && -1 !== a0P && (a0P = -1, a0Y(), b3.d1 = !0), b3.dY - a0Q < 350 && a0T === a0p && -1 !== (a0p = (a0p = yr(-1, a0p, windowHeight)) !== windowHeight && vU(x, y) ? a0p : -1) && (x = (leaderboardFilter.enabled ? leaderboardArray[leaderboardFilter.filteredLeaderboard[a0p + position] ?? (isEmptySpace = true, leaderboardPositionsById[playerId])] : leaderboardArray[a0p + position]), a0p === windowHeight - 1 && (leaderboardFilter.enabled ? this.playerPos : leaderboardPositionsById[playerId]) >=
 | 
					 | 
				
			||||||
			position + windowHeight - 1 && (x = playerId), !isEmptySpace && `);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{ // Hovering tooltip
 | 
					 | 
				
			||||||
	replaceRawCode("this.click=function(g8,g9,tE){var fT=aj.fU(g8),fV=aj.fW(g9),fX=aj.fY(fT,fV),fZ=aj.fa(fX);return!(!aj.fb(fT,fV)||(fT=(b7.cv.fv()?.025:.0144)*aK.fw,fV=performance.now(),Math.abs(g8-uu)>fT)||Math.abs(g9-uv)>fT||dY+500<fV)&&(dY=fV,tE?(function(g8,g9,fZ){a2.eb(fZ)||-1===(g8=ak.ff.vR(g8,g9))?k.vQ(fZ):k.vS(g8)}(g8,g9,fZ),!1)",
 | 
					 | 
				
			||||||
		`hoveringTooltip.display = function(mouseX, mouseY) {
 | 
					 | 
				
			||||||
			var coordX = aj.fU(mouseX), coordY = aj.fW(mouseY),
 | 
					 | 
				
			||||||
				coord = aj.fY(coordX, coordY), point = aj.fa(coord);
 | 
					 | 
				
			||||||
			if (coordX < 0 || coordY < 0) return;
 | 
					 | 
				
			||||||
			k.vQ(point);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		this.click = function(g8, g9, tE) {
 | 
					 | 
				
			||||||
			var fT = aj.fU(g8),
 | 
					 | 
				
			||||||
				fV = aj.fW(g9),
 | 
					 | 
				
			||||||
				fX = aj.fY(fT, fV),
 | 
					 | 
				
			||||||
				fZ = aj.fa(fX);
 | 
					 | 
				
			||||||
			return !(!aj.fb(fT, fV) || (fT = (b7.cv.fv() ? .025 : .0144) * aK.fw, fV = performance.now(), Math.abs(g8 - uu) > fT) || Math.abs(g9 - uv) > fT || dY + 500 < fV) && (dY = fV, tE ? (function(g8, g9, fZ) {
 | 
					 | 
				
			||||||
				a2.eb(fZ) || -1 === (g8 = ak.ff.vR(g8, g9)) ? k.vQ(fZ) : k.vS(g8)
 | 
					 | 
				
			||||||
			}(g8, g9, fZ), false)`)
 | 
					 | 
				
			||||||
	replaceRawCode("aK.nH=(window.devicePixelRatio||1)*aEr,",
 | 
					 | 
				
			||||||
		`aK.nH = (window.devicePixelRatio || 1) * aEr, hoveringTooltip.canvasPixelScale = aK.nH,`)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Disable built-in Territorial.io error reporting
 | 
					// Disable built-in Territorial.io error reporting
 | 
				
			||||||
replaceOne(/window\.addEventListener\("error",function (\w+)\((\w+)\){/g,
 | 
					replaceOne(/window\.addEventListener\("error",function (\w+)\((\w+)\){/g,
 | 
				
			||||||
	'$& window.removeEventListener("error", $1); return alert("Error:\\n" + $2.filename + " " + $2.lineno + " " + $2.colno + " " + $2.message);');
 | 
						'$& window.removeEventListener("error", $1); return alert("Error:\\n" + $2.filename + " " + $2.lineno + " " + $2.colno + " " + $2.message);');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
								
									
									
										
											19
										
									
									readme.md
									
									
									
									
								
								
							
							
										
											19
										
									
									readme.md
									
									
									
									
								| 
						 | 
					@ -20,22 +20,21 @@ FX Client is the first Territorial.io client, targeting better User Interface an
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Features:
 | 
					## Features:
 | 
				
			||||||
1. It's 100% free and open source on Github
 | 
					1. It's 100% free and open source on Github
 | 
				
			||||||
2. It's ad-free and removes game's default ads.
 | 
					2. It's Ad free and removes game's default ads.
 | 
				
			||||||
3. It makes game look cooler, by replacing default assets with new ones.
 | 
					3. It makes game look cooler, by replacing default assets with new ones.
 | 
				
			||||||
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 win counter
 | 
				
			||||||
7. Hovering tooltip: makes the territory map information (normally visible on right click) be visible constantly (on hover)
 | 
					7. Adds a player list
 | 
				
			||||||
8. Adds a player list
 | 
					8. Adds the ability to view the history of who donated to a player during the game by clicking on their name in the leaderboard or the 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
 | 
					9. Can be installed as a PWA (progressive web app) ensuring maximum enjoyment on consoles, phones and even desktop devices
 | 
				
			||||||
10. Adds a win counter
 | 
					 | 
				
			||||||
11. 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:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
12. Make fullscreen mode trigger automatically
 | 
					10. Change the game font
 | 
				
			||||||
13. Set a custom main menu background
 | 
					11. Make fullscreen mode trigger automatically
 | 
				
			||||||
14. Create custom attack percentage keybinds
 | 
					12. Set a custom main menu background
 | 
				
			||||||
 | 
					13. Create custom attack percentage keybinds
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Building Locally
 | 
					## Building Locally
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
								
									
									
										
											134
										
									
									src/fx_core.js
									
									
									
									
								
								
							
							
										
											134
										
									
									src/fx_core.js
									
									
									
									
								| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
const fx_version = '0.6.4.1'; // FX Client Version
 | 
					const fx_version = '0.6.3.3'; // FX Client Version
 | 
				
			||||||
const fx_update = 'May 20'; // FX Client Last Updated
 | 
					const fx_update = 'Apr 11'; // FX Client Last Updated
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if (localStorage.getItem("fx_winCount") == undefined || localStorage.getItem("fx_winCount") == null) {
 | 
					if (localStorage.getItem("fx_winCount") == undefined || localStorage.getItem("fx_winCount") == null) {
 | 
				
			||||||
    var wins_counter = 0;
 | 
					    var wins_counter = 0;
 | 
				
			||||||
| 
						 | 
					@ -27,9 +27,8 @@ function KeybindsInput(containerElement) {
 | 
				
			||||||
    this.keys = [ "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: 1 });
 | 
				
			||||||
        this.displayObjects();
 | 
					        this.displayObjects();
 | 
				
			||||||
        keybindAddButton.scrollIntoView(false);
 | 
					 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    this.update = function () {
 | 
					    this.update = function () {
 | 
				
			||||||
        this.objectArray = settings.attackPercentageKeybinds;
 | 
					        this.objectArray = settings.attackPercentageKeybinds;
 | 
				
			||||||
| 
						 | 
					@ -55,15 +54,11 @@ function KeybindsInput(containerElement) {
 | 
				
			||||||
                    inputField.setAttribute("placeholder", "No key set");
 | 
					                    inputField.setAttribute("placeholder", "No key set");
 | 
				
			||||||
                    inputField.addEventListener("click", this.startKeyInput.bind(this, i, key));
 | 
					                    inputField.addEventListener("click", this.startKeyInput.bind(this, i, key));
 | 
				
			||||||
                } else { // key === "value"
 | 
					                } else { // key === "value"
 | 
				
			||||||
                    const isAbsolute = this.objectArray[i].type === "absolute";
 | 
					                    inputField.type = "number";
 | 
				
			||||||
                    inputField.type = isAbsolute ? "text" : "number";
 | 
					                    inputField.setAttribute("step", "0.1");
 | 
				
			||||||
                    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));
 | 
					                    inputField.addEventListener("input", this.updateObject.bind(this, i, key));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if (key === "value" && this.objectArray[i].type === "absolute")
 | 
					                inputField.value = this.objectArray[i][key];
 | 
				
			||||||
                    inputField.value = this.objectArray[i][key] * 100 + "%";
 | 
					 | 
				
			||||||
                else inputField.value = this.objectArray[i][key];
 | 
					 | 
				
			||||||
                // Append input field to the object div
 | 
					                // Append input field to the object div
 | 
				
			||||||
                objectDiv.appendChild(inputField);
 | 
					                objectDiv.appendChild(inputField);
 | 
				
			||||||
            }, this);
 | 
					            }, this);
 | 
				
			||||||
| 
						 | 
					@ -88,21 +83,10 @@ function KeybindsInput(containerElement) {
 | 
				
			||||||
            //this.displayObjects();
 | 
					            //this.displayObjects();
 | 
				
			||||||
        }, { once: true });
 | 
					        }, { once: true });
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    /** @param {PointerEvent} event */
 | 
					 | 
				
			||||||
    this.convertIntoNumberInput = function (index, property, event) {
 | 
					 | 
				
			||||||
        event.target.value = event.target.value.slice(0, -1);
 | 
					 | 
				
			||||||
        event.target.type = "number";
 | 
					 | 
				
			||||||
        event.target.addEventListener("blur", () => {
 | 
					 | 
				
			||||||
            //event.target.value = this.objectArray[index][property];
 | 
					 | 
				
			||||||
            this.displayObjects();
 | 
					 | 
				
			||||||
        }, { 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" ? parseFloat(event.target.value) : property === "key" ? event.key : event.target.value;
 | 
				
			||||||
            this.objectArray[index].type === "absolute" ? parseFloat(event.target.value) / 100 : parseFloat(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.displayObjects();
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
| 
						 | 
					@ -120,7 +104,6 @@ var settings = {
 | 
				
			||||||
    //"showBotDonations": false,
 | 
					    //"showBotDonations": false,
 | 
				
			||||||
    "displayWinCounter": true,
 | 
					    "displayWinCounter": true,
 | 
				
			||||||
    "useFullscreenMode": false,
 | 
					    "useFullscreenMode": false,
 | 
				
			||||||
    "hoveringTooltip": true,
 | 
					 | 
				
			||||||
    //"hideAllLinks": false,
 | 
					    //"hideAllLinks": false,
 | 
				
			||||||
    "realisticNames": false,
 | 
					    "realisticNames": false,
 | 
				
			||||||
    "showPlayerDensity": true,
 | 
					    "showPlayerDensity": true,
 | 
				
			||||||
| 
						 | 
					@ -140,8 +123,6 @@ var settingsManager = new (function() {
 | 
				
			||||||
        note: "The win counter tracks multiplayer solo wins (not in team games)" },
 | 
					        note: "The win counter tracks multiplayer solo wins (not in team games)" },
 | 
				
			||||||
        { for: "useFullscreenMode", type: "checkbox", label: "Use fullscreen mode",
 | 
					        { for: "useFullscreenMode", type: "checkbox", label: "Use fullscreen mode",
 | 
				
			||||||
        note: "Note: fullscreen mode will trigger after you click anywhere on the page due to browser policy restrictions." },
 | 
					        note: "Note: fullscreen mode will trigger after you click anywhere on the page due to browser policy restrictions." },
 | 
				
			||||||
        { for: "hoveringTooltip", type: "checkbox", label: "Hovering tooltip",
 | 
					 | 
				
			||||||
        note: "Display map territory info constantly (on mouse hover) instead of only when right clicking on the map" },
 | 
					 | 
				
			||||||
        //{ for: "hideAllLinks", type: "checkbox", label: "Hide Links option also hides app store links" },
 | 
					        //{ for: "hideAllLinks", type: "checkbox", label: "Hide Links option also hides app store links" },
 | 
				
			||||||
        { for: "realisticNames", type: "checkbox", label: "Realistic Bot Names" },
 | 
					        { for: "realisticNames", type: "checkbox", label: "Realistic Bot Names" },
 | 
				
			||||||
        { for: "showPlayerDensity", type: "checkbox", label: "Show player density" },
 | 
					        { for: "showPlayerDensity", type: "checkbox", label: "Show player density" },
 | 
				
			||||||
| 
						 | 
					@ -341,107 +322,6 @@ const playerList = new (function () {
 | 
				
			||||||
        canvas.imageSmoothingEnabled = false;
 | 
					        canvas.imageSmoothingEnabled = false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					 | 
				
			||||||
/** @param {string} name */
 | 
					 | 
				
			||||||
function parseClanFromPlayerName(name) {
 | 
					 | 
				
			||||||
    const startIndex = name.indexOf("[");
 | 
					 | 
				
			||||||
    // this is probably how the algorithm works, since a player with
 | 
					 | 
				
			||||||
    // the name "][a]" will count as not being in a clan in the base game
 | 
					 | 
				
			||||||
    return startIndex === -1 ? "" : name.slice(startIndex + 1, name.indexOf("]")).toUpperCase();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const leaderboardFilter = new (function() {
 | 
					 | 
				
			||||||
    this.playersToInclude = [0,1,8,20,24,30,32,42,50,69,200,400,500,510,511]; // for testing
 | 
					 | 
				
			||||||
    //this.playersToInclude = [];
 | 
					 | 
				
			||||||
    this.tabLabels = ["ALL", "CLAN"];
 | 
					 | 
				
			||||||
    // these get populated by the modified game code
 | 
					 | 
				
			||||||
    this.filteredLeaderboard = [];
 | 
					 | 
				
			||||||
    this.tabBarOffset = 0;
 | 
					 | 
				
			||||||
    this.windowWidth = 0;
 | 
					 | 
				
			||||||
    this.verticalClickThreshold = 1000;
 | 
					 | 
				
			||||||
    this.hoveringOverTabs = false;
 | 
					 | 
				
			||||||
    this.scrollToTop = () => {};
 | 
					 | 
				
			||||||
    this.repaintLeaderboard = () => {};
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    this.selectedTab = 0;
 | 
					 | 
				
			||||||
    this.tabHovering = -1;
 | 
					 | 
				
			||||||
    //this.enabled = false;
 | 
					 | 
				
			||||||
    this.enabled = true;
 | 
					 | 
				
			||||||
    this.drawTabs = function(canvas, totalWidth, verticalOffset, colorForSelectedTab) {
 | 
					 | 
				
			||||||
        canvas.textBaseline = "middle";
 | 
					 | 
				
			||||||
        canvas.textAlign = "center";
 | 
					 | 
				
			||||||
        const tabWidth = totalWidth / this.tabLabels.length;
 | 
					 | 
				
			||||||
        const textOffsetY = verticalOffset + this.tabBarOffset / 2;
 | 
					 | 
				
			||||||
        //console.log(verticalOffset, this.tabBarOffset, textOffsetY);
 | 
					 | 
				
			||||||
        this.tabLabels.forEach((label, index) => {
 | 
					 | 
				
			||||||
            if (index !== 0) canvas.fillRect(tabWidth * index, verticalOffset, 1, this.tabBarOffset);
 | 
					 | 
				
			||||||
            if (this.selectedTab === index) {
 | 
					 | 
				
			||||||
                canvas.fillStyle = colorForSelectedTab;
 | 
					 | 
				
			||||||
                canvas.fillRect(tabWidth * index, verticalOffset, tabWidth, this.tabBarOffset);
 | 
					 | 
				
			||||||
                canvas.fillStyle = "rgb(255,255,255)";
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (this.tabHovering === index) {
 | 
					 | 
				
			||||||
                canvas.fillStyle = "rgba(255,255,255,0.3)";
 | 
					 | 
				
			||||||
                canvas.fillRect(tabWidth * index, verticalOffset, tabWidth, this.tabBarOffset);
 | 
					 | 
				
			||||||
                canvas.fillStyle = "rgb(255,255,255)";
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            canvas.fillText(label, tabWidth * index + tabWidth / 2, textOffsetY);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    this.setHovering = (isHovering, xRelative) => {
 | 
					 | 
				
			||||||
        let repaintNeeded = false;
 | 
					 | 
				
			||||||
        if (isHovering) {
 | 
					 | 
				
			||||||
            const tab = Math.floor(xRelative / (this.windowWidth / this.tabLabels.length));
 | 
					 | 
				
			||||||
            if (this.tabHovering !== tab) {
 | 
					 | 
				
			||||||
                this.tabHovering = tab;
 | 
					 | 
				
			||||||
                repaintNeeded = true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (isHovering !== this.hoveringOverTabs) {
 | 
					 | 
				
			||||||
            this.hoveringOverTabs = isHovering;
 | 
					 | 
				
			||||||
            if (isHovering === false) this.tabHovering = -1;
 | 
					 | 
				
			||||||
            if (!isHovering) repaintNeeded = true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (repaintNeeded) this.repaintLeaderboard();
 | 
					 | 
				
			||||||
        return isHovering;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    this.handleMouseDown = (xRelative) => {
 | 
					 | 
				
			||||||
        //console.log("click; x: ", xRelative);
 | 
					 | 
				
			||||||
        if (this.tabHovering !== this.selectedTab) {
 | 
					 | 
				
			||||||
            this.selectedTab = this.tabHovering;
 | 
					 | 
				
			||||||
            if (this.selectedTab === 0) this.clearFilter();
 | 
					 | 
				
			||||||
            else if (this.selectedTab === 1) this.filterByOwnClan();
 | 
					 | 
				
			||||||
            this.repaintLeaderboard();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return true;
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    this.filterByOwnClan = () => {
 | 
					 | 
				
			||||||
        this.playersToInclude = [];
 | 
					 | 
				
			||||||
        const ownClan = parseClanFromPlayerName(getVar("playerNames")[getVar("playerId")]);
 | 
					 | 
				
			||||||
        getVar("playerNames").forEach((name, id) => {
 | 
					 | 
				
			||||||
            if (parseClanFromPlayerName(name) === ownClan) this.playersToInclude.push(id);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
        this.enabled = true;
 | 
					 | 
				
			||||||
        this.scrollToTop();
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    this.clearFilter = () => { this.enabled = false; }
 | 
					 | 
				
			||||||
    this.reset = () => {
 | 
					 | 
				
			||||||
        this.enabled = false;
 | 
					 | 
				
			||||||
        this.selectedTab = 0;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const hoveringTooltip = new (function() {
 | 
					 | 
				
			||||||
    this.display = () => {}; // this gets populated by the modified game script
 | 
					 | 
				
			||||||
    this.canvasPixelScale = 1;
 | 
					 | 
				
			||||||
    document.getElementById("canvasA").addEventListener("mousemove", e => {
 | 
					 | 
				
			||||||
        if (!settings.hoveringTooltip || !getVar("gameState")) return;
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            this.display(this.canvasPixelScale * e.clientX, this.canvasPixelScale * e.clientY);
 | 
					 | 
				
			||||||
        } catch (e) { console.error(e) }
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var donationsTracker = new (function(){
 | 
					var donationsTracker = new (function(){
 | 
				
			||||||
    this.openedWindowPlayerID = null;
 | 
					    this.openedWindowPlayerID = null;
 | 
				
			||||||
    this.contentElement = document.querySelector("#donationhistory_content");
 | 
					    this.contentElement = document.querySelector("#donationhistory_content");
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue