2024-08-11 03:14:20 -07:00
const dictionary = { "gIsTeamGame" : "hG" , "game" : "a4" , "playerId" : "en" , "playerData" : "aZ" , "playerNames" : "jo" , "rawPlayerNames" : "u4" , "playerBalances" : "eo" , "playerTerritories" : "fJ" , "gameState" : "tS" , "fontSize" : "fontSize" , "x" : "fc" , "y" : "fd" , "canvas" : "gJ" , "gHumans" : "hg" , "playerStates" : "wX" , "fontGeneratorFunction" : "b2.je.jj" , "gIsSingleplayer" : "ii" , "gLobbyMaxJoin" : "r3" , "SingleplayerMenu" : "aS" , "getSingleplayerPlayerCount" : "xY" , "gMaxPlayers" : "ew" , "gBots" : "ia" , "Translations" : "m" , "txt" : "n" , "strs" : "a3I" , "uiSizes" : "bQ" , "gap" : "gap" , "i" : "a2" } ;
2024-08-03 23:40:37 -07:00
const fx _version = '0.6.5.3' ; // FX Client Version
const fx _update = 'Aug 3' ; // FX Client Last Updated
2024-01-31 10:50:35 -08:00
if ( localStorage . getItem ( "fx_winCount" ) == undefined || localStorage . getItem ( "fx_winCount" ) == null ) {
var wins _counter = 0 ;
console . log ( 'Couldn\'t find a saved win data. creating one...' ) ;
} else if ( localStorage . getItem ( "fx_winCount" ) != undefined || localStorage . getItem ( "fx_winCount" ) != null ) {
var wins _counter = localStorage . getItem ( "fx_winCount" ) ;
}
2024-06-01 04:24:04 -07:00
const playerDataProperties = [ "playerTerritories" , "playerBalances" , "rawPlayerNames" ] ;
const gameObjectProperties = [ "playerId" , "gIsTeamGame" , "gHumans" , "gLobbyMaxJoin" , "gameState" , "gIsSingleplayer" ] ;
const getVar = varName => {
if ( playerDataProperties . includes ( varName ) ) return window [ dictionary . playerData ] [ dictionary [ varName ] ] ;
if ( gameObjectProperties . includes ( varName ) ) return window [ dictionary . game ] [ dictionary [ varName ] ] ;
return window [ dictionary [ varName ] ]
} ;
2024-03-07 05:56:42 -08:00
// https://stackoverflow.com/a/6234804
function escapeHtml ( unsafe ) {
return unsafe . replace ( /&/g , "&" ) . replace ( /</g , "<" ) . replace ( />/g , ">" ) . replace ( /"/g , """ ) . replace ( /'/g , "'" ) ;
}
2024-02-21 11:58:40 -08:00
function KeybindsInput ( containerElement ) {
2024-03-26 08:38:33 -07:00
const header = document . createElement ( "p" ) ;
header . innerText = "Attack Percentage Keybinds" ;
const keybindContainer = document . createElement ( "div" ) ;
keybindContainer . className = "arrayinput" ;
const keybindAddButton = document . createElement ( "button" ) ;
keybindAddButton . innerText = "Add" ;
containerElement . append ( header , keybindContainer , keybindAddButton ) ;
this . container = keybindContainer ;
2024-02-21 11:58:40 -08:00
this . keys = [ "key" , "type" , "value" ] ;
this . objectArray = [ ] ;
this . addObject = function ( ) {
2024-05-20 07:09:46 -07:00
this . objectArray . push ( { key : "" , type : "absolute" , value : 0.8 } ) ;
2024-02-21 11:58:40 -08:00
this . displayObjects ( ) ;
2024-05-20 07:09:46 -07:00
keybindAddButton . scrollIntoView ( false ) ;
2024-02-21 11:58:40 -08:00
} ;
2024-03-26 08:38:33 -07:00
this . update = function ( ) {
this . objectArray = settings . attackPercentageKeybinds ;
this . displayObjects ( ) ;
}
keybindAddButton . addEventListener ( "click" , this . addObject . bind ( this ) ) ;
2024-02-21 11:58:40 -08:00
this . displayObjects = function ( ) {
// Clear the content of the container
this . container . innerHTML = "" ;
2024-02-22 01:47:11 -08:00
if ( this . objectArray . length === 0 ) return this . container . innerText = "No custom attack percentage keybinds added" ;
2024-02-21 11:58:40 -08:00
// Loop through the array and display input fields for each object
for ( var i = 0 ; i < this . objectArray . length ; i ++ ) {
var objectDiv = document . createElement ( "div" ) ;
// Create input fields for each key
this . keys . forEach ( function ( key ) {
2024-02-22 01:47:11 -08:00
let inputField = document . createElement ( key === "type" ? "select" : "input" ) ;
2024-02-21 11:58:40 -08:00
if ( key === "type" ) {
2024-02-22 01:47:11 -08:00
inputField . innerHTML = '<option value="absolute">Absolute</option><option value="relative">Relative</option>' ;
inputField . addEventListener ( "change" , this . updateObject . bind ( this , i , key ) ) ;
} else if ( key === "key" ) {
inputField . type = "text" ;
inputField . setAttribute ( "readonly" , "" ) ;
inputField . setAttribute ( "placeholder" , "No key set" ) ;
inputField . addEventListener ( "click" , this . startKeyInput . bind ( this , i , key ) ) ;
} else { // key === "value"
2024-05-20 07:09:46 -07:00
const isAbsolute = this . objectArray [ i ] . type === "absolute" ;
inputField . type = isAbsolute ? "text" : "number" ;
if ( isAbsolute ) inputField . addEventListener ( "click" , this . convertIntoNumberInput . bind ( this , i , key ) , { once : true } ) ;
else inputField . setAttribute ( "step" , "0.1" ) ;
2024-02-22 01:47:11 -08:00
inputField . addEventListener ( "input" , this . updateObject . bind ( this , i , key ) ) ;
2024-02-21 11:58:40 -08:00
}
2024-05-20 07:09:46 -07:00
if ( key === "value" && this . objectArray [ i ] . type === "absolute" )
inputField . value = this . objectArray [ i ] [ key ] * 100 + "%" ;
else inputField . value = this . objectArray [ i ] [ key ] ;
2024-02-21 11:58:40 -08:00
// Append input field to the object div
objectDiv . appendChild ( inputField ) ;
} , this ) ;
// Button to delete the object
var deleteButton = document . createElement ( "button" ) ;
deleteButton . textContent = "Delete" ;
deleteButton . addEventListener ( "click" , this . deleteObject . bind ( this , i ) ) ;
// Append delete button to the object div
objectDiv . appendChild ( deleteButton ) ;
// Append the object div to the container
this . container . appendChild ( objectDiv ) ;
}
} ;
2024-02-22 01:47:11 -08:00
/** @param {PointerEvent} event */
2024-02-21 11:58:40 -08:00
this . startKeyInput = function ( index , property , event ) {
event . target . value = "Press any key" ;
2024-02-22 01:47:11 -08:00
const handler = this . updateObject . bind ( this , index , property ) ;
event . target . addEventListener ( 'keydown' , handler , { once : true } ) ;
event . target . addEventListener ( "blur" , ( ) => {
event . target . removeEventListener ( 'keydown' , handler , { once : true } ) ;
event . target . value = this . objectArray [ index ] [ property ] ;
//this.displayObjects();
} , { once : true } ) ;
2024-02-21 11:58:40 -08:00
} ;
2024-05-20 07:09:46 -07:00
/** @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 } ) ;
} ;
2024-02-21 11:58:40 -08:00
this . updateObject = function ( index , property , event ) {
if ( index >= this . objectArray . length ) return ;
// Update the corresponding property of the object in the array
2024-05-20 07:09:46 -07:00
const value = property === "value" ? (
this . objectArray [ index ] . type === "absolute" ? parseFloat ( event . target . value ) / 100 : parseFloat ( event . target . value )
) : property === "key" ? event . key : event . target . value ;
2024-02-21 11:58:40 -08:00
this . objectArray [ index ] [ property ] = value ;
if ( property === "key" ) this . displayObjects ( ) ;
} ;
this . deleteObject = function ( index ) {
// Remove the object from the array
this . objectArray . splice ( index , 1 ) ;
// Display the updated input fields for objects
this . displayObjects ( ) ;
} ;
return this ;
}
2024-01-31 10:50:35 -08:00
var settings = {
2024-03-26 08:38:33 -07:00
//"fontName": "Trebuchet MS",
//"showBotDonations": false,
2024-02-22 22:49:56 -08:00
"displayWinCounter" : true ,
"useFullscreenMode" : false ,
2024-05-20 07:09:46 -07:00
"hoveringTooltip" : true ,
2024-03-26 08:38:33 -07:00
//"hideAllLinks": false,
2024-01-31 10:50:35 -08:00
"realisticNames" : false ,
2024-03-08 03:08:38 -08:00
"showPlayerDensity" : true ,
2024-03-30 06:56:48 -07:00
"coloredDensity" : true ,
2024-02-27 23:21:14 -08:00
"densityDisplayStyle" : "percentage" ,
2024-07-12 10:15:39 -07:00
"highlightClanSpawns" : false ,
2024-01-31 10:50:35 -08:00
//"customMapFileBtn": true
2024-02-24 10:39:00 -08:00
"customBackgroundUrl" : "" ,
2024-02-21 11:58:40 -08:00
"attackPercentageKeybinds" : [ ] ,
2024-01-31 10:50:35 -08:00
} ;
2024-03-26 08:38:33 -07:00
const discontinuedSettings = [ "hideAllLinks" , "fontName" ] ;
2024-02-24 10:39:00 -08:00
let makeMainMenuTransparent = false ;
2024-01-31 10:50:35 -08:00
var settingsManager = new ( function ( ) {
2024-03-26 08:38:33 -07:00
const settingsStructure = [
2024-04-11 02:19:44 -07:00
{ for : "displayWinCounter" , type : "checkbox" , label : "Display win counter" ,
2024-07-12 10:15:39 -07:00
note : "The win counter tracks multiplayer solo wins (not in team games)" } ,
{ type : "button" , text : "Reset win counter" , action : removeWins } ,
2024-04-11 02:19:44 -07:00
{ 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." } ,
2024-05-20 07:09:46 -07:00
{ 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" } ,
2024-03-26 08:38:33 -07:00
//{ for: "hideAllLinks", type: "checkbox", label: "Hide Links option also hides app store links" },
{ for : "realisticNames" , type : "checkbox" , label : "Realistic Bot Names" } ,
{ for : "showPlayerDensity" , type : "checkbox" , label : "Show player density" } ,
2024-03-30 06:56:48 -07:00
{ for : "coloredDensity" , type : "checkbox" , label : "Colored density" , note : "Display the density with a color between red and green depending on the density value" } ,
2024-03-26 08:38:33 -07:00
{ for : "densityDisplayStyle" , type : "selectMenu" , label : "Density value display style:" , tooltip : "Controls how the territorial density value should be rendered" , options : [
{ value : "percentage" , label : "Percentage" } ,
{ value : "absoluteQuotient" , label : "Value from 0 to 150 (BetterTT style)" }
] } ,
2024-07-12 10:15:39 -07:00
{ for : "highlightClanSpawns" , type : "checkbox" , label : "Highlight clan spawnpoints" ,
note : "Increases the spawnpoint glow size for members of your clan" } ,
2024-03-26 08:38:33 -07:00
{ for : "customBackgroundUrl" , type : "textInput" , label : "Custom main menu background:" , placeholder : "Enter an image URL here" , tooltip : "A custom image to be shown as the main menu background instead of the currently selected map." } ,
KeybindsInput
] ;
const settingsContainer = document . querySelector ( ".settings .scrollable" ) ;
var inputFields = { } ; // (includes select menus)
var checkboxFields = { } ;
var customElements = [ ] ;
settingsStructure . forEach ( item => {
if ( typeof item === "function" ) {
const container = document . createElement ( "div" ) ;
customElements . push ( new item ( container ) ) ;
return settingsContainer . append ( container ) ;
}
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" ) ;
if ( item . type === "textInput" ) element . type = "text" ;
if ( item . placeholder ) element . placeholder = item . placeholder ;
if ( isValueInput || item . type === "selectMenu" ) inputFields [ item . for ] = element ;
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 ) item . options . forEach ( option => {
const optionElement = document . createElement ( "option" ) ;
optionElement . setAttribute ( "value" , option . value ) ;
optionElement . innerText = option . label ;
element . append ( optionElement ) ;
} ) ;
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" ) ) ;
settingsContainer . append ( label , document . createElement ( "br" ) ) ;
} ) ;
2024-01-31 10:50:35 -08:00
this . save = function ( ) {
Object . keys ( inputFields ) . forEach ( function ( key ) { settings [ key ] = inputFields [ key ] . value . trim ( ) ; } ) ;
Object . keys ( checkboxFields ) . forEach ( function ( key ) { settings [ key ] = checkboxFields [ key ] . checked ; } ) ;
this . applySettings ( ) ;
WindowManager . closeWindow ( "settings" ) ;
2024-03-26 08:38:33 -07:00
discontinuedSettings . forEach ( settingName => delete settings [ settingName ] ) ;
2024-01-31 10:50:35 -08:00
localStorage . setItem ( "fx_settings" , JSON . stringify ( settings ) ) ;
// should probably firgure out a way to do this without reloading - // You can't do it, localstorages REQUIRE you to reload
window . location . reload ( ) ;
} ;
2024-07-05 00:56:07 -07:00
const fileInput = document . createElement ( "input" ) ;
fileInput . type = "file" ;
function handleFileSelect ( event ) {
const input = event . target ;
/** @type {File} */
const selectedFile = input . files [ 0 ] ;
if ( ! selectedFile ) return ;
input . removeEventListener ( "change" , handleFileSelect ) ;
input . value = "" ;
if ( ! selectedFile . name . endsWith ( ".json" ) ) return alert ( "Invalid file format" ) ;
const fileReader = new FileReader ( ) ;
fileReader . onload = function ( ) {
let result ;
try {
result = JSON . parse ( fileReader . result ) ;
if ( confirm ( "Warning: This will override all current settings, click \"OK\" to confirm" ) ) settings = result ;
localStorage . setItem ( "fx_settings" , JSON . stringify ( settings ) ) ;
window . location . reload ( ) ;
} catch ( error ) {
alert ( "Error\n" + error )
}
}
fileReader . readAsText ( selectedFile ) ;
}
this . importFromFile = function ( ) {
fileInput . click ( ) ;
fileInput . addEventListener ( 'change' , handleFileSelect ) ;
} ;
// https://stackoverflow.com/a/34156339
function saveFile ( content , fileName , contentType ) {
var a = document . createElement ( "a" ) ;
var file = new Blob ( [ content ] , { type : contentType } ) ;
a . href = URL . createObjectURL ( file ) ;
a . download = fileName ;
a . click ( ) ;
URL . revokeObjectURL ( a . href ) ;
}
this . exportToFile = function ( ) {
saveFile ( JSON . stringify ( settings ) , 'FX_client_settings.json' , 'application/json' ) ;
} ;
2024-01-31 10:50:35 -08:00
this . syncFields = function ( ) {
Object . keys ( inputFields ) . forEach ( function ( key ) { inputFields [ key ] . value = settings [ key ] ; } ) ;
Object . keys ( checkboxFields ) . forEach ( function ( key ) { checkboxFields [ key ] . checked = settings [ key ] ; } ) ;
2024-03-26 08:38:33 -07:00
customElements . forEach ( element => element . update ( ) ) ;
2024-01-31 10:50:35 -08:00
} ;
this . resetAll = function ( ) {
if ( ! confirm ( "Are you Really SURE you want to RESET ALL SETTINGS back to the default?" ) ) return ;
localStorage . removeItem ( "fx_settings" ) ;
window . location . reload ( ) ;
} ;
this . applySettings = function ( ) {
//setVarByName("bu", "px " + settings.fontName);
2024-02-22 22:49:56 -08:00
if ( settings . useFullscreenMode && document . fullscreenEnabled ) {
function tryEnterFullscreen ( ) {
if ( document . fullscreenElement !== null ) return ;
document . documentElement . requestFullscreen ( { navigationUI : "hide" } )
. then ( ( ) => { console . log ( 'Fullscreen mode activated' ) ; } )
. catch ( ( error ) => { console . warn ( 'Could not enter fullscreen mode:' , error ) ; } ) ;
}
document . addEventListener ( 'mousedown' , tryEnterFullscreen , { once : true } ) ;
document . addEventListener ( 'click' , tryEnterFullscreen , { once : true } ) ;
}
2024-02-24 10:39:00 -08:00
if ( settings . customBackgroundUrl !== "" ) {
document . body . style . backgroundImage = "url(" + settings . customBackgroundUrl + ")" ;
document . body . style . backgroundSize = "cover" ;
document . body . style . backgroundPosition = "center" ;
}
makeMainMenuTransparent = settings . customBackgroundUrl !== "" ;
2024-01-31 10:50:35 -08:00
} ;
} ) ;
function removeWins ( ) {
var confirm1 = confirm ( 'Do you really want to reset your Wins?' ) ;
if ( confirm1 ) {
wins _counter = 0 ;
localStorage . removeItem ( 'fx_winCount' ) ;
alert ( "Successfully reset wins" ) ;
}
}
2024-02-24 10:39:00 -08:00
const openCustomBackgroundFilePicker = ( ) => {
const fileInput = document . getElementById ( "customBackgroundFileInput" ) ;
fileInput . click ( ) ;
fileInput . addEventListener ( 'change' , handleFileSelect ) ;
}
function handleFileSelect ( event ) {
const fileInput = event . target ;
const selectedFile = fileInput . files [ 0 ] ;
console . log ( fileInput . files ) ;
console . log ( fileInput . files [ 0 ] ) ;
if ( selectedFile ) {
const fileUrl = URL . createObjectURL ( selectedFile ) ;
console . log ( "File URL:" , fileUrl ) ;
fileInput . value = "" ;
fileInput . removeEventListener ( "change" , handleFileSelect ) ;
}
}
2024-01-31 10:50:35 -08:00
var WindowManager = new ( function ( ) {
var windows = { } ;
this . add = function ( newWindow ) {
windows [ newWindow . name ] = newWindow ;
windows [ newWindow . name ] . isOpen = false ;
} ;
this . openWindow = function ( windowName , ... args ) {
if ( windows [ windowName ] . isOpen === true ) return ;
if ( windows [ windowName ] . beforeOpen !== undefined ) windows [ windowName ] . beforeOpen ( ... args ) ;
windows [ windowName ] . isOpen = true ;
windows [ windowName ] . element . style . display = null ;
} ;
this . closeWindow = function ( windowName ) {
if ( windows [ windowName ] . isOpen === false ) return ;
windows [ windowName ] . isOpen = false ;
windows [ windowName ] . element . style . display = "none" ;
2024-03-10 11:59:15 -07:00
if ( windows [ windowName ] . onClose !== undefined ) windows [ windowName ] . onClose ( ) ;
2024-01-31 10:50:35 -08:00
} ;
this . closeAll = function ( ) {
Object . values ( windows ) . forEach ( function ( windowObj ) {
WindowManager . closeWindow ( windowObj . name ) ;
} ) ;
} ;
} ) ;
WindowManager . add ( {
name : "settings" ,
element : document . querySelector ( ".settings" ) ,
beforeOpen : function ( ) { settingsManager . syncFields ( ) ; }
} ) ;
WindowManager . add ( {
name : "donationHistory" ,
element : document . querySelector ( "#donationhistory" ) ,
beforeOpen : function ( isSingleplayer ) {
2024-02-01 06:55:06 -08:00
document . getElementById ( "donationhistory_note" ) . style . display = ( ( true || settings . showBotDonations || /*getVarByName("dt")*/ isSingleplayer ) ? "none" : "block" ) ;
2024-03-10 11:59:15 -07:00
} ,
onClose : function ( ) { donationsTracker . openedWindowPlayerID = null ; }
2024-01-31 10:50:35 -08:00
} ) ;
2024-03-05 12:29:32 -08:00
WindowManager . add ( {
name : "playerList" ,
element : document . getElementById ( "playerlist" ) ,
beforeOpen : function ( ) { }
} ) ;
2024-01-31 10:50:35 -08:00
document . getElementById ( "canvasA" ) . addEventListener ( "mousedown" , WindowManager . closeAll ) ;
2024-03-07 05:56:42 -08:00
document . getElementById ( "canvasA" ) . addEventListener ( "touchstart" , WindowManager . closeAll , { passive : true } ) ;
2024-02-29 23:56:30 -08:00
document . addEventListener ( "keydown" , event => { if ( event . key === "Escape" ) WindowManager . closeAll ( ) ; } ) ;
2024-01-31 10:50:35 -08:00
var settingsGearIcon = document . createElement ( 'img' ) ;
2024-03-05 12:29:32 -08:00
settingsGearIcon . setAttribute ( 'src' , 'assets/geari_white.png' ) ;
2024-01-31 10:50:35 -08:00
2024-03-05 12:29:32 -08:00
const playerList = new ( function ( ) {
const playersIcon = document . createElement ( 'img' ) ;
playersIcon . setAttribute ( 'src' , 'assets/players_icon.png' ) ;
2024-03-07 05:56:42 -08:00
document . getElementById ( "playerlist_content" ) . addEventListener ( "click" , event => {
const playerId = event . target . closest ( "tr[data-player-id]" ) ? . getAttribute ( "data-player-id" ) ;
if ( ! playerId ) return ;
2024-03-10 11:59:15 -07:00
if ( getVar ( "gIsTeamGame" ) ) WindowManager . closeWindow ( "playerList" ) , donationsTracker . displayHistory ( playerId ) ;
2024-03-07 05:56:42 -08:00
} ) ;
2024-03-05 12:29:32 -08:00
this . display = function displayPlayerList ( playerNames ) {
2024-03-10 11:59:15 -07:00
const gHumans = getVar ( "gHumans" ) ;
const gLobbyMaxJoin = getVar ( "gLobbyMaxJoin" ) ;
let listContent = ` <h3>Players ( ${ gHumans } )</h3> ` ;
for ( let i = 0 ; i < gLobbyMaxJoin ; i ++ ) {
if ( i === gHumans ) listContent += ` <h3>Bots ( ${ gLobbyMaxJoin - gHumans } )</h3> ` ;
2024-03-11 14:16:32 -07:00
listContent += ` <tr data-player-id=" ${ i } "><td><span class="color-light-gray"> ${ i + 1 } .</span> ${ escapeHtml ( playerNames [ i ] ) } </td></tr> `
2024-03-05 12:29:32 -08:00
}
2024-03-07 05:56:42 -08:00
document . getElementById ( "playerlist_content" ) . innerHTML = listContent ;
document . getElementById ( "playerlist_content" ) . setAttribute ( "class" , getVar ( "gIsTeamGame" ) ? "clickable" : "" ) ;
2024-03-05 12:29:32 -08:00
WindowManager . openWindow ( "playerList" ) ;
}
this . hoveringOverButton = false ;
this . drawButton = ( canvas , x , y , size ) => {
canvas . fillRect ( x , y , size , size ) ;
canvas . fillStyle = this . hoveringOverButton ? "#aaaaaaaa" : "#000000aa" ;
canvas . clearRect ( x + 1 , y + 1 , size - 2 , size - 2 ) ;
canvas . fillRect ( x + 1 , y + 1 , size - 2 , size - 2 ) ;
canvas . fillStyle = "#ffffff" ;
canvas . imageSmoothingEnabled = true ;
canvas . drawImage ( playersIcon , x + 2 , y + 2 , size - 4 , size - 4 ) ;
canvas . imageSmoothingEnabled = false ;
}
} ) ;
2024-05-20 07:09:46 -07:00
const leaderboardFilter = new ( function ( ) {
2024-06-03 10:16:59 -07:00
//this.playersToInclude = [0,1,8,20,24,30,32,42,50,69,200,400,500,510,511]; // for testing
this . playersToInclude = [ ] ;
2024-05-20 07:09:46 -07:00
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 = ( ) => { } ;
2024-06-05 00:47:33 -07:00
this . setUpdateFlag = ( ) => { } ;
2024-05-22 11:49:00 -07:00
this . parseClanFromPlayerName = ( ) => { console . warn ( "parse function not set" ) ; } ;
2024-05-20 07:09:46 -07:00
this . selectedTab = 0 ;
this . tabHovering = - 1 ;
2024-06-03 10:16:59 -07:00
this . enabled = false ;
//this.enabled = true;
2024-05-20 07:09:46 -07:00
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 ) => {
2024-06-03 09:06:31 -07:00
const tab = Math . floor ( xRelative / ( this . windowWidth / this . tabLabels . length ) ) ;
if ( this . selectedTab !== tab ) {
this . selectedTab = tab ;
2024-05-20 07:09:46 -07:00
if ( this . selectedTab === 0 ) this . clearFilter ( ) ;
2024-06-05 00:47:33 -07:00
else if ( this . selectedTab === 1 ) {
this . filterByOwnClan ( ) ;
this . setUpdateFlag ( ) ;
}
2024-05-20 07:09:46 -07:00
this . repaintLeaderboard ( ) ;
}
return true ;
} ;
this . filterByOwnClan = ( ) => {
this . playersToInclude = [ ] ;
2024-07-12 10:15:39 -07:00
const playerId = getVar ( "playerId" ) ;
const ownClan = this . parseClanFromPlayerName ( getVar ( "rawPlayerNames" ) [ playerId ] ) ;
2024-05-22 11:49:00 -07:00
getVar ( "rawPlayerNames" ) . forEach ( ( name , id ) => {
2024-07-12 10:15:39 -07:00
if ( id === playerId || this . parseClanFromPlayerName ( name ) === ownClan ) this . playersToInclude . push ( id ) ;
2024-05-20 07:09:46 -07:00
} ) ;
this . enabled = true ;
this . scrollToTop ( ) ;
} ;
this . clearFilter = ( ) => { this . enabled = false ; }
this . reset = ( ) => {
this . enabled = false ;
this . selectedTab = 0 ;
2024-07-12 10:15:39 -07:00
clanFilter . refresh ( ) ;
}
} ) ;
const clanFilter = new ( function ( ) {
this . inOwnClan = new Array ( 512 ) ;
this . inOwnClan . fill ( false ) ;
this . refresh = ( ) => {
const gHumans = getVar ( "gHumans" ) ;
const ownClan = leaderboardFilter . parseClanFromPlayerName ( getVar ( "rawPlayerNames" ) [ getVar ( "playerId" ) ] ) ;
if ( ownClan === null ) this . inOwnClan . fill ( false ) ;
else getVar ( "rawPlayerNames" ) . forEach ( ( name , id ) => {
this . inOwnClan [ id ] = id < gHumans && leaderboardFilter . parseClanFromPlayerName ( name ) === ownClan ;
} ) ;
2024-05-20 07:09:46 -07:00
}
} ) ;
const hoveringTooltip = new ( function ( ) {
2024-06-24 09:01:36 -07:00
let recentlyShown = false ;
2024-05-20 07:09:46 -07:00
this . display = ( ) => { } ; // this gets populated by the modified game script
2024-05-20 08:39:13 -07:00
this . canvasPixelScale = 1 ;
2024-08-03 23:40:37 -07:00
function handler ( e ) {
2024-06-24 09:01:36 -07:00
if ( ! settings . hoveringTooltip || ! getVar ( "gameState" ) || recentlyShown ) return ;
2024-08-03 23:52:09 -07:00
let x , y ;
// https://stackoverflow.com/a/61732450
if ( e . type . includes ( ` touch ` ) ) {
const { touches , changedTouches } = e . originalEvent ? ? e ;
const touch = touches [ 0 ] ? ? changedTouches [ 0 ] ;
x = touch . pageX ;
y = touch . pageY ;
} else if ( e . type . includes ( ` mouse ` ) ) {
x = e . clientX ;
y = e . clientY ;
}
2024-06-24 09:01:36 -07:00
recentlyShown = true ;
2024-05-20 07:09:46 -07:00
try {
2024-08-03 23:52:09 -07:00
this . display ( this . canvasPixelScale * x , this . canvasPixelScale * y ) ;
2024-05-20 07:09:46 -07:00
} catch ( e ) { console . error ( e ) }
2024-06-24 09:01:36 -07:00
// for better performance, reduce the tooltip display frequency to no more than once every 100 ms
setTimeout ( ( ) => recentlyShown = false , 100 ) ;
2024-08-03 23:40:37 -07:00
}
document . getElementById ( "canvasA" ) . addEventListener ( "mousemove" , handler . bind ( this ) ) ;
2024-08-03 23:52:09 -07:00
document . getElementById ( "canvasA" ) . addEventListener ( "touchstart" , handler . bind ( this ) ) ;
2024-05-20 07:09:46 -07:00
} ) ;
2024-01-31 10:50:35 -08:00
var donationsTracker = new ( function ( ) {
2024-03-10 11:59:15 -07:00
this . openedWindowPlayerID = null ;
this . contentElement = document . querySelector ( "#donationhistory_content" ) ;
2024-02-03 12:36:45 -08:00
this . donationHistory = Array ( 512 ) ;
2024-01-31 10:50:35 -08:00
// fill the array with empty arrays with length of 3
2024-02-03 12:36:45 -08:00
//for (var i = 0; i < 512; i++) this.donationHistory.push([]); // not needed as .reset is called on game start
2024-03-10 11:59:15 -07:00
this . getHistoryOf = function ( playerID ) {
return this . donationHistory [ playerID ] . toReversed ( ) ;
}
this . reset = function ( ) { for ( var i = 0 ; i < 512 ; i ++ ) this . donationHistory [ i ] = [ ] ; } ;
2024-01-31 10:50:35 -08:00
this . logDonation = function ( senderID , receiverID , amount ) {
2024-02-01 06:55:06 -08:00
const donationInfo = [ senderID , receiverID , amount ] ;
this . donationHistory [ receiverID ] . push ( donationInfo ) ;
this . donationHistory [ senderID ] . push ( donationInfo ) ;
2024-03-10 11:59:15 -07:00
if ( this . openedWindowPlayerID === senderID || this . openedWindowPlayerID === receiverID ) {
const indexOfNewItem = this . donationHistory [ this . openedWindowPlayerID === senderID ? senderID : receiverID ] . length ;
this . contentElement . prepend ( generateTableRowItem ( donationInfo , indexOfNewItem , this . openedWindowPlayerID , true ) ) ;
}
2024-01-31 10:50:35 -08:00
} ;
2024-03-10 11:59:15 -07:00
function generateTableRowItem ( historyItem , index , playerID , isNew ) {
2024-05-22 11:49:00 -07:00
const rawPlayerNames = getVar ( "rawPlayerNames" ) ;
2024-03-10 11:59:15 -07:00
const row = document . createElement ( "tr" ) ;
if ( isNew ) row . setAttribute ( "class" , "new" ) ;
let content = ` <td><span class="color-light-gray"> ${ index } .</span> ` ;
2024-02-06 09:30:02 -08:00
if ( playerID === historyItem [ 1 ] )
2024-05-22 11:49:00 -07:00
content += ` Received <span class="color-green"> ${ historyItem [ 2 ] } </span> resources from ${ escapeHtml ( rawPlayerNames [ historyItem [ 0 ] ] ) } ` ;
else content += ` Sent <span class="color-red"> ${ historyItem [ 2 ] } </span> resources to ${ escapeHtml ( rawPlayerNames [ historyItem [ 1 ] ] ) } ` ;
2024-03-10 11:59:15 -07:00
content += "</td>" ;
row . innerHTML = content ;
return row ;
}
2024-05-22 11:49:00 -07:00
this . displayHistory = function displayDonationsHistory ( playerID , playerNames = getVar ( "rawPlayerNames" ) , isSingleplayer = getVar ( "gIsSingleplayer" ) ) {
2024-03-10 11:59:15 -07:00
var history = donationsTracker . getHistoryOf ( playerID ) ;
console . log ( "History for " + playerNames [ playerID ] + ":" ) ;
console . log ( history ) ;
document . querySelector ( "#donationhistory h1" ) . innerHTML = "Donation history for " + escapeHtml ( playerNames [ playerID ] ) ;
this . contentElement . innerHTML = "" ;
if ( history . length > 0 ) history . forEach ( ( historyItem , index ) => {
this . contentElement . appendChild ( generateTableRowItem ( historyItem , history . length - index , playerID ) ) ;
} ) ;
else this . contentElement . innerText = "Nothing to display" ;
this . openedWindowPlayerID = playerID ;
WindowManager . openWindow ( "donationHistory" , isSingleplayer ) ;
}
} ) ;
2024-01-31 10:50:35 -08:00
var utils = new ( function ( ) {
2024-02-03 12:36:45 -08:00
this . getMaxTroops = function ( playerTerritories , playerID ) { return ( playerTerritories [ playerID ] * 150 ) . toString ( ) ; } ;
2024-03-08 03:08:38 -08:00
this . getDensity = function ( playerID , playerBalances = getVar ( "playerBalances" ) , playerTerritories = getVar ( "playerTerritories" ) ) {
2024-02-29 23:56:30 -08:00
if ( settings . densityDisplayStyle === "percentage" ) return ( ( ( playerBalances [ playerID ] / ( ( playerTerritories [ playerID ] === 0 ? 1 : playerTerritories [ playerID ] ) * 150 ) ) * 100 ) . toFixed ( 1 ) + "%" ) ;
else return ( playerBalances [ playerID ] / ( playerTerritories [ playerID ] === 0 ? 1 : playerTerritories [ playerID ] ) ) . toFixed ( 1 ) ;
2024-01-31 10:50:35 -08:00
} ;
2024-03-05 12:29:32 -08:00
this . isPointInRectangle = function ( x , y , rectangleStartX , rectangleStartY , width , height ) {
return x >= rectangleStartX && x <= rectangleStartX + width && y >= rectangleStartY && y <= rectangleStartY + height ;
2024-03-08 03:08:38 -08:00
} ;
/** @param {CanvasRenderingContext2D} canvas @param {string} text */
this . fillTextMultiline = function ( canvas , text , x , y , maxWidth ) {
const lineHeight = parseInt ( canvas . font . split ( " " ) . find ( part => part . endsWith ( "px" ) ) . slice ( 0 , - 2 ) ) ;
text . split ( "\n" ) . forEach ( ( line , index ) => canvas . fillText ( line , x , y + index * lineHeight , maxWidth ) ) ;
2024-03-05 12:29:32 -08:00
}
2024-03-30 06:56:48 -07:00
this . textStyleBasedOnDensity = function ( playerID ) {
const playerBalances = getVar ( "playerBalances" ) , playerTerritories = getVar ( "playerTerritories" ) ;
return ` hsl( ${ playerBalances [ playerID ] / ( playerTerritories [ playerID ] * 1.5 ) } , 100%, 50%, 1) ` ;
}
2024-01-31 10:50:35 -08:00
} ) ;
2024-02-21 11:58:40 -08:00
const keybindFunctions = { setAbsolute : ( ) => { } , setRelative : ( ) => { } } ;
const keybindHandler = key => {
const keybindData = settings . attackPercentageKeybinds . find ( keybind => keybind . key === key ) ;
if ( keybindData === undefined ) return false ;
if ( keybindData . type === "absolute" ) keybindFunctions . setAbsolute ( keybindData . value ) ;
else keybindFunctions . setRelative ( keybindData . value ) ;
return true ;
} ;
2024-01-31 10:50:35 -08:00
if ( localStorage . getItem ( "fx_settings" ) !== null ) {
settings = { ... settings , ... JSON . parse ( localStorage . getItem ( "fx_settings" ) ) } ;
}
2024-02-22 22:49:56 -08:00
settingsManager . applySettings ( ) ;
2024-01-31 10:50:35 -08:00
console . log ( 'Successfully loaded FX Client' ) ;