Created
March 24, 2020 05:30
-
-
Save badosu/99d1f1f95182a89ab39e46cfae764495 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const g_IsReplay = Engine.IsVisualReplay(); | |
const g_CivData = loadCivData(false, true); | |
const g_Ceasefire = prepareForDropdown(g_Settings && g_Settings.Ceasefire); | |
const g_MapSizes = prepareForDropdown(g_Settings && g_Settings.MapSizes); | |
const g_MapTypes = prepareForDropdown(g_Settings && g_Settings.MapTypes); | |
const g_PopulationCapacities = prepareForDropdown(g_Settings && g_Settings.PopulationCapacities); | |
const g_StartingResources = prepareForDropdown(g_Settings && g_Settings.StartingResources); | |
const g_VictoryDurations = prepareForDropdown(g_Settings && g_Settings.VictoryDurations); | |
const g_VictoryConditions = g_Settings && g_Settings.VictoryConditions; | |
var g_GameSpeeds; | |
var g_PassingArguments = {}; | |
g_IsSession = true; | |
/** | |
* Whether to display diplomacy colors (where players see self/ally/neutral/enemy each in different colors and | |
* observers see each team in a different color) or regular player colors. | |
*/ | |
var g_DiplomacyColorsToggle = false; | |
/** | |
* The array of displayed player colors (either the diplomacy color or regular color for each player). | |
*/ | |
var g_DisplayedPlayerColors; | |
/** | |
* Colors to flash when pop limit reached. | |
*/ | |
var g_DefaultPopulationColor = "white"; | |
var g_PopulationAlertColor = "255 128 0"; | |
/** | |
* Seen in the tooltip of the top panel. | |
*/ | |
var g_ResourceTitleFont = "sans-bold-16"; | |
/** | |
* A random file will be played. TODO: more variety | |
*/ | |
var g_Ambient = ["audio/ambient/dayscape/day_temperate_gen_03.ogg"]; | |
/** | |
* Map, player and match settings set in gamesetup. | |
*/ | |
const g_GameAttributes = deepfreeze(Engine.GetInitAttributes()); | |
/** | |
* True if this is a multiplayer game. | |
*/ | |
const g_IsNetworked = Engine.HasNetClient(); | |
/** | |
* Is this user in control of game settings (i.e. is a network server, or offline player). | |
*/ | |
var g_IsController = !g_IsNetworked || Engine.HasNetServer(); | |
/** | |
* Whether we have finished the synchronization and | |
* can start showing simulation related message boxes. | |
*/ | |
var g_IsNetworkedActive = false; | |
/** | |
* True if the connection to the server has been lost. | |
*/ | |
var g_Disconnected = false; | |
/** | |
* True if the current user has observer capabilities. | |
*/ | |
var g_IsObserver = false; | |
/** | |
* True if the current user has rejoined (or joined the game after it started). | |
*/ | |
var g_HasRejoined = false; | |
/** | |
* Shows a message box asking the user to leave if "won" or "defeated". | |
*/ | |
var g_ConfirmExit = false; | |
/** | |
* True if the current player has paused the game explicitly. | |
*/ | |
var g_Paused = false; | |
/** | |
* The list of GUIDs of players who have currently paused the game, if the game is networked. | |
*/ | |
var g_PausingClients = []; | |
/** | |
* Array with GUI page to switch to and parameters. | |
*/ | |
var g_PageOnLeaveSettings = []; | |
/** | |
* The playerID selected in the change perspective tool. | |
*/ | |
var g_ViewedPlayer = Engine.GetPlayerID(); | |
/** | |
* True if the camera should focus on attacks and player commands | |
* and select the affected units. | |
*/ | |
var g_FollowPlayer = false; | |
/** | |
* Cache the basic player data (name, civ, color). | |
*/ | |
var g_Players = []; | |
/** | |
* Last time when onTick was called(). | |
* Used for animating the main menu. | |
*/ | |
var g_LastTickTime = Date.now(); | |
/** | |
* Recalculate which units have their status bars shown with this frequency in milliseconds. | |
*/ | |
var g_StatusBarUpdate = 200; | |
/** | |
* For restoring selection, order and filters when returning to the replay menu | |
*/ | |
var g_ReplaySelectionData; | |
/** | |
* Remembers which clients are assigned to which player slots. | |
* The keys are guids or "local" in Singleplayer. | |
*/ | |
var g_PlayerAssignments; | |
/** | |
* Cache dev-mode settings that are frequently or widely used. | |
*/ | |
var g_DevSettings = { | |
"changePerspective": false, | |
"controlAll": false | |
}; | |
/** | |
* Whether the entire UI should be hidden (useful for promotional screenshots). | |
* Can be toggled with a hotkey. | |
*/ | |
var g_ShowGUI = true; | |
/** | |
* Whether status bars should be shown for all of the player's units. | |
*/ | |
var g_ShowAllStatusBars = false; | |
/** | |
* Blink the population counter if the player can't train more units. | |
*/ | |
var g_IsTrainingBlocked = false; | |
/** | |
* Cache of simulation state and template data (apart from TechnologyData, updated on every simulation update). | |
*/ | |
var g_SimState; | |
var g_EntityStates = {}; | |
var g_TemplateData = {}; | |
var g_TechnologyData = {}; | |
var g_ResourceData = new Resources(); | |
/** | |
* Top coordinate of the research list. | |
* Changes depending on the number of displayed counters. | |
*/ | |
var g_ResearchListTop = 4; | |
/** | |
* List of additional entities to highlight. | |
*/ | |
var g_ShowGuarding = false; | |
var g_ShowGuarded = false; | |
var g_AdditionalHighlight = []; | |
/** | |
* Display data of the current players entities shown in the top panel. | |
*/ | |
var g_PanelEntities = []; | |
/** | |
* Order in which the panel entities are shown. | |
*/ | |
var g_PanelEntityOrder = ["Hero", "Relic"]; | |
/** | |
* Unit classes to be checked for the idle-worker-hotkey. | |
*/ | |
var g_WorkerTypes = ["FemaleCitizen", "Trader", "FishingBoat", "CitizenSoldier"]; | |
/** | |
* Unit classes to be checked for the military-only-selection modifier and for the idle-warrior-hotkey. | |
*/ | |
var g_MilitaryTypes = ["Melee", "Ranged"]; | |
function GetSimState() | |
{ | |
if (!g_SimState) | |
g_SimState = deepfreeze(Engine.GuiInterfaceCall("GetSimulationState")); | |
return g_SimState; | |
} | |
function GetMultipleEntityStates(ents) | |
{ | |
if (!ents.length) | |
return null; | |
let entityStates = Engine.GuiInterfaceCall("GetMultipleEntityStates", ents); | |
for (let item of entityStates) | |
g_EntityStates[item.entId] = item.state && deepfreeze(item.state); | |
return entityStates; | |
} | |
function GetEntityState(entId) | |
{ | |
if (!g_EntityStates[entId]) | |
{ | |
let entityState = Engine.GuiInterfaceCall("GetEntityState", entId); | |
g_EntityStates[entId] = entityState && deepfreeze(entityState); | |
} | |
return g_EntityStates[entId]; | |
} | |
function GetTemplateData(templateName) | |
{ | |
if (!(templateName in g_TemplateData)) | |
{ | |
let template = Engine.GuiInterfaceCall("GetTemplateData", templateName); | |
translateObjectKeys(template, ["specific", "generic", "tooltip"]); | |
g_TemplateData[templateName] = deepfreeze(template); | |
} | |
return g_TemplateData[templateName]; | |
} | |
function GetTechnologyData(technologyName, civ) | |
{ | |
if (!g_TechnologyData[civ]) | |
g_TechnologyData[civ] = {}; | |
if (!(technologyName in g_TechnologyData[civ])) | |
{ | |
let template = GetTechnologyDataHelper(TechnologyTemplates.Get(technologyName), civ, g_ResourceData); | |
translateObjectKeys(template, ["specific", "generic", "description", "tooltip", "requirementsTooltip"]); | |
g_TechnologyData[civ][technologyName] = deepfreeze(template); | |
} | |
return g_TechnologyData[civ][technologyName]; | |
} | |
var g_ServerIP; | |
var g_ServerPort; | |
var g_UseSTUN; | |
var g_HostJID; | |
var g_PlayerName; | |
var g_ServerName = ""; | |
var g_MessageMenusSave = []; | |
var g_MessageMenusSavePerma = {}; | |
const g_MessageMenus = { | |
"main": { | |
"text": () => "Ally Message Menu [Ctrl+Z, Ctrl+Y]", | |
keys: [ | |
[ "1", "short" ], | |
[ "2", "need_quick" ], | |
[ "3", "need_res" ], | |
[ "4", "help1" ], | |
[ "5", "attack1" ], | |
[ "6", "attackColor" ], | |
[ "7", "change" ], | |
[ "8", "pos1" ], | |
[ "9", "longer_msgs" ], | |
[ "0", { text: "Exit" } ] | |
], | |
go: "" | |
}, | |
"short": { | |
"text": () => "Short ...", | |
"msg": () => g_MessageMenusSave[0] + ".", | |
keys: [ | |
[ "1", { text: "Yes", save: true } ], | |
[ "2", { text: "No", save: true } ], | |
[ "3", { text: "Back", save: true } ], | |
[ "4", { text: "Come help", save: true } ], | |
[ "5", { text: "Wait please", save: true } ], | |
[ "6", { text: "Okay?", save: true } ], | |
[ "7", { text: "Fast", save: true } ], | |
[ "8", { text: "Thanks", save: true } ], | |
[ "9", { text: "Coming", save: true } ], | |
[ "0", "main" ] | |
], | |
go: "msg" | |
}, | |
"longer_msgs": { | |
"text": () => "Random ...", | |
"msg": () => g_MessageMenusSave[0], | |
keys: [ | |
[ "1", { text: "Good work", save: true } ], | |
[ "2", { text: "Someone go for a civic center in the middle?", save: true } ], | |
[ "3", { text: "Can you build a civic center near me?", save: true, loadPerma: () => g_MessageMenusSavePerma["pos"] || "", loadPermaMsg: () => g_MessageMenus.team.msg() + " (At clock position " + g_MessageMenusSave[1] + ")" } ], | |
[ "4", { text: "Build up defence", save: true } ], | |
[ "5", { text: "What's your population?", save: true } ], | |
[ "6", { text: "I think we've lost", save: true } ], | |
[ "0", "main" ] | |
], | |
go: "msg" | |
}, | |
"change": { | |
"text": () => "Exchange Resources ...", | |
keys: [ | |
[ "1", { text: "Food", save: "food" } ], | |
[ "2", { text: "Wood", save: "wood" } ], | |
[ "3", { text: "Metal", save: "metal" } ], | |
[ "4", { text: "Stone", save: "stone" } ], | |
[ "0", "main" ] | |
], | |
go: "change_for" | |
}, | |
"change_for": { | |
"text": () => g_MessageMenus.change.text() + " " + g_MessageMenusSave[0], | |
"msg": () => "Exchange " + g_MessageMenusSave[0] + " for " + g_MessageMenusSave[1] + "?", | |
keys: [ | |
[ "1", { text: "For Food", save: "food" } ], | |
[ "2", { text: "For Wood", save: "wood" } ], | |
[ "3", { text: "For Metal", save: "metal" } ], | |
[ "4", { text: "For Stone", save: "stone" } ], | |
[ "0", "main" ] | |
], | |
go: "msg" | |
}, | |
"need_quick": { | |
"text": () => "Request ...", | |
"msg": () => "I need " + g_MessageMenusSave[0] + ".", | |
keys: [ | |
[ "1", { text: "Help at my position", save: "help", loadPerma: () => g_MessageMenusSavePerma["pos"] || "", loadPermaMsg: () => g_MessageMenus.need_quick.msg() + " (clock position " + g_MessageMenusSave[1] + ")" } ], | |
[ "2", { text: "Help (quick)", save: "quick help" } ], | |
[ "3", { text: "Attack", save: "attack help" } ], | |
[ "4", { text: "Food", save: "food" } ], | |
[ "5", { text: "Wood", save: "wood" } ], | |
[ "6", { text: "Stone", save: "stone" } ], | |
[ "7", { text: "Metal", save: "metal" } ], | |
[ "0", "main" ] | |
], | |
go: "msg" | |
}, | |
"need_res": { | |
"text": () => "Need Resource ...", | |
keys: [ | |
[ "1", { text: "Food", save: "food" } ], | |
[ "2", { text: "Wood", save: "wood" } ], | |
[ "3", { text: "Stone", save: "stone" } ], | |
[ "4", { text: "Metal", save: "metal" } ], | |
[ "0", "main" ] | |
], | |
go: "need_res_amount" | |
}, | |
"need_res_amount": { | |
"text": () => "Need Resource " + g_MessageMenusSave[0] + " amount ...", | |
"msg": () => "I need " + g_MessageMenusSave[1] + " " + g_MessageMenusSave[0] + ".", | |
keys: [ | |
[ "1", { text: "100", save: true } ], | |
[ "2", { text: "200", save: true } ], | |
[ "3", { text: "300", save: true } ], | |
[ "4", { text: "400", save: true } ], | |
[ "5", { text: "500", save: true } ], | |
[ "6", { text: "1000", save: true } ], | |
[ "0", "main" ] | |
], | |
go: "msg" | |
}, | |
"attack1": { | |
"text": () => "Attack At Map Clock Position ...", | |
"msg": () => "Attack at " + g_MessageMenusSave[0] + " (clock position).", | |
keys: [ | |
[ "1", { text: "1", save: "1" } ], | |
[ "2", { text: "2", save: "2" } ], | |
[ "3", { text: "3", save: "3" } ], | |
[ "4", { text: "4", save: "4" } ], | |
[ "5", { text: "5", save: "5" } ], | |
[ "6", { text: "6", save: "6" } ], | |
[ "7", { text: "7", save: "7" } ], | |
[ "8", { text: "8", save: "8" } ], | |
[ "9", "attack2" ], | |
[ "0", "main" ] | |
], | |
go: "msg" | |
}, | |
"attack2": { | |
"text": () => g_MessageMenus.attack1.text() + " (More)", | |
"msg": () => g_MessageMenus.attack1.msg(), | |
keys: [ | |
[ "1", { text: "9", save: "9" } ], | |
[ "2", { text: "10", save: "10" } ], | |
[ "3", { text: "11", save: "11" } ], | |
[ "4", { text: "12", save: "12" } ], | |
[ "9", "help1" ], | |
[ "0", "main" ] | |
], | |
go: "msg" | |
}, | |
"attackColor": { | |
"text": () => "Attack Player ...", | |
"msg": () => "Attack " + g_MessageMenusSave[0] + " player.", | |
keys: [ | |
[ "1", { text: "Blue", save: "blue" } ], | |
[ "2", { text: "Red", save: "red" } ], | |
[ "3", { text: "Green", save: "green" } ], | |
[ "4", { text: "Yellow", save: "yellow" } ], | |
[ "5", { text: "Cyan", save: "cyan" } ], | |
[ "6", { text: "Purple", save: "purple" } ], | |
[ "7", { text: "Orange", save: "orange" } ], | |
[ "8", { text: "Black", save: "black" } ], | |
[ "0", "main" ] | |
], | |
go: "msg" | |
}, | |
"help1": { | |
"text": () => "Help At Position ...", | |
"msg": () => "Need help at " + g_MessageMenusSave[0] + " (clock position).", | |
keys: [ | |
[ "1", { text: "1", save: "1" } ], | |
[ "2", { text: "2", save: "2" } ], | |
[ "3", { text: "3", save: "3" } ], | |
[ "4", { text: "4", save: "4" } ], | |
[ "5", { text: "5", save: "5" } ], | |
[ "6", { text: "6", save: "6" } ], | |
[ "7", { text: "7", save: "7" } ], | |
[ "8", { text: "8", save: "8" } ], | |
[ "9", "help2" ], | |
[ "0", "main" ] | |
], | |
go: "msg" | |
}, | |
"help2": { | |
"text": () => g_MessageMenus.help1.text() + " (More)", | |
"msg": () => g_MessageMenus.help1.msg(), | |
keys: [ | |
[ "1", { text: "9", save: "9" } ], | |
[ "2", { text: "10", save: "10" } ], | |
[ "3", { text: "11", save: "11" } ], | |
[ "4", { text: "12", save: "12" } ], | |
[ "9", "help1" ], | |
[ "0", "main" ] | |
], | |
go: "msg" | |
}, | |
"pos1": { | |
"text": () => "Tell Allies Your Map Position", | |
"msg": () => "Map position is " + g_MessageMenusSave[0] + ". (clock position)", | |
keys: [ | |
[ "1", { text: "1", save: true, savePerma: "pos" } ], | |
[ "2", { text: "2", save: true, savePerma: "pos" } ], | |
[ "3", { text: "3", save: true, savePerma: "pos" } ], | |
[ "4", { text: "4", save: true, savePerma: "pos" } ], | |
[ "5", { text: "5", save: true, savePerma: "pos" } ], | |
[ "6", { text: "6", save: true, savePerma: "pos" } ], | |
[ "7", { text: "7", save: true, savePerma: "pos" } ], | |
[ "8", { text: "8", save: true, savePerma: "pos" } ], | |
[ "9", "pos2" ], | |
[ "0", "main" ] | |
], | |
go: "msg" | |
}, | |
"pos2": { | |
"text": () => g_MessageMenus.pos1.text() + " (More)", | |
"msg": () => g_MessageMenus.pos1.msg(), | |
keys: [ | |
[ "1", { text: "9", save: true, savePerma: "pos" } ], | |
[ "2", { text: "10", save: true, savePerma: "pos" } ], | |
[ "3", { text: "11", save: true, savePerma: "pos" } ], | |
[ "4", { text: "12", save: true, savePerma: "pos" } ], | |
[ "9", "pos1" ], | |
[ "0", "main" ] | |
], | |
go: "msg" | |
} | |
} | |
var g_OpenedMessageMenu = ""; | |
function toggleMessageMenu() | |
{ | |
if (g_IsObserver) | |
return; | |
if (!g_OpenedMessageMenu) | |
{ | |
closeOpenDialogs(); | |
g_OpenedMessageMenu = "main"; | |
showMessageMenu(); | |
showDarkenOverlay(true); | |
} | |
else | |
{ | |
closeMessageMenu(); | |
} | |
} | |
function closeMessageMenu() | |
{ | |
g_OpenedMessageMenu = ""; | |
showMessageMenu(); | |
} | |
var g_MessageMenuSendMsgs = 0; | |
var g_MessageMenuSendTimer = 0; | |
function switchMenuKey(keyGo) | |
{ | |
let menu = g_MessageMenus[g_OpenedMessageMenu]; | |
for (let e of menu.keys) | |
{ | |
let [key, obj] = e; | |
if (key == keyGo) | |
{ | |
if (typeof obj === 'string') | |
g_OpenedMessageMenu = obj; | |
else if (typeof obj === 'object') | |
{ | |
if (!!obj.save) | |
{ | |
let save = ""; | |
if (typeof obj.save === 'boolean') | |
save = obj.text; | |
else | |
save = obj.save; | |
g_MessageMenusSave.push(save); | |
if (!!obj.savePerma) | |
g_MessageMenusSavePerma[obj.savePerma] = save; | |
if (!!obj.loadPerma && obj.loadPerma()) | |
g_MessageMenusSave.push(obj.loadPerma()); | |
} | |
if (menu.go == "msg") | |
{ | |
if (g_MessageMenuSendMsgs < 5) | |
{ | |
if (!!obj.loadPerma && obj.loadPerma()) | |
submitChatDirectly("/allies " + obj.loadPermaMsg()); | |
else | |
submitChatDirectly("/allies " + menu.msg()); | |
++g_MessageMenuSendMsgs; | |
} | |
else | |
{ | |
let clearSpamMessage = () => { | |
if (g_SpamMId > 0) | |
{ | |
if (g_SpamTimer) | |
clearTimeout(g_SpamTimer); | |
g_SpamTimer = 0; | |
g_ChatHistory.splice(g_SpamHId, 1); | |
g_ChatMessages.splice(g_SpamMId, 1); | |
g_SpamHId = 0; | |
g_SpamMId = 0; | |
Engine.GetGUIObjectByName("chatText").caption = g_ChatMessages.join("\n"); | |
updateChatHistory(); | |
} | |
} | |
clearSpamMessage(); | |
let msg = time => | |
{ return { | |
"type": "system", | |
"guid": 0, | |
"text": "Spam, wait: " + (time/1000).toFixed(1) + "s" | |
}; }; | |
addChatMessage(msg(2500)); | |
g_SpamMId = g_ChatMessages.length - 1; | |
g_SpamHId = g_ChatHistory.length - 1; | |
let updateSecondsSpamMessage = time => { | |
g_ChatMessages[g_SpamMId] = g_FormatChatMessage["system"](msg(time)); | |
Engine.GetGUIObjectByName("chatText").caption = g_ChatMessages.join("\n"); | |
updateChatHistory(); | |
if (time > 0) | |
g_SpamTimer = setTimeout(() => updateSecondsSpamMessage(time-100), 100); | |
else | |
clearSpamMessage(); | |
} | |
updateSecondsSpamMessage(2500); | |
} | |
let reduceMsgCountPerTime = () => { | |
if (g_MessageMenuSendMsgs <= 0) | |
{ | |
g_MessageMenuSendTimer = 0; | |
return; | |
} | |
--g_MessageMenuSendMsgs; | |
g_MessageMenuSendTimer = setTimeout(reduceMsgCountPerTime, 2500); | |
} | |
if (g_MessageMenuSendTimer) | |
clearTimeout(g_MessageMenuSendTimer); | |
g_MessageMenuSendTimer = setTimeout(reduceMsgCountPerTime, 2500); | |
g_MessageMenusSave = []; | |
g_OpenedMessageMenu = ""; | |
} | |
else | |
g_OpenedMessageMenu = g_MessageMenus[g_OpenedMessageMenu].go; | |
} | |
showMessageMenu(); | |
break; | |
} | |
} | |
} | |
var g_SpamHId = -1; | |
var g_SpamMId = -1; | |
var g_SpamTimer = 0; | |
function showMessageMenu() | |
{ | |
if (g_IsObserver) | |
return; | |
let cap = ""; | |
if (g_OpenedMessageMenu) | |
{ | |
let menu = g_MessageMenus[g_OpenedMessageMenu]; | |
cap = "" + escapeText(g_MessageMenus[g_OpenedMessageMenu].text())+"\n"; | |
for (let e of menu.keys) | |
{ | |
let [key, obj] = e; | |
let textAdd = setStringTags("" + key + "", g_HotkeyTags) + ". "; // colorizeHotkey("%(hotkey)s.", "selection.group.select." + key) + " "; | |
if (typeof obj === 'object') | |
textAdd += "" + escapeText(obj.text) + ""; | |
else if (typeof obj === 'string') | |
textAdd += escapeText(g_MessageMenus[obj].text()); | |
else | |
continue; | |
if (key == "0") | |
cap += "\n"; | |
cap += "\n" + textAdd; | |
} | |
} | |
else | |
showDarkenOverlay(false); | |
Engine.GetGUIObjectByName("messagesMenuText").caption = cap; | |
Engine.GetGUIObjectByName("messagesMenu").hidden = !cap; | |
} | |
function updateObserver() | |
{ | |
if (false && Engine.ConfigDB_GetValue("user", "session.showobservers") != "true") | |
{ | |
Engine.GetGUIObjectByName("observersList").hidden = true; | |
return; | |
} | |
let obs = []; | |
let off = [] | |
for (let player in g_Players) | |
if (!!g_Players[player].offline) | |
off.push(setStringTags(g_Players[player].name, { "color": "red"})); | |
for (let player in g_PlayerAssignments) | |
if (g_PlayerAssignments[player].player == -1) | |
obs.push(setStringTags(g_PlayerAssignments[player].name.substring(0,28), { "color": "white"})); | |
if (obs.length<=0 && off.length<=0) | |
{ | |
Engine.GetGUIObjectByName("observersList").hidden = true; | |
return; | |
} | |
Engine.GetGUIObjectByName("observersList").hidden = false; | |
Engine.GetGUIObjectByName("observersList").caption = (obs.length > 0 ? setStringTags("Observers (" + obs.length + "):\n" + obs.join(", ") + "\n" , | |
{ "color": "255 255 255 255", "font": "mono-10" }) : "") + | |
(off.length > 0 ? setStringTags("\nOfflines (" + off.length + "):\n" + off.join(", ") , | |
{ "color": "white", "font": "mono-10" }) : ""); | |
} | |
var g_TeamInfoState = 0; | |
var g_TeamInfoKeepBg = true; | |
function setTeamInfoState() | |
{ | |
let tp = (n, next) => "" | |
// + setStringTags(escapeText("[Click]"), g_HotkeyTags) | |
// + ", " | |
+ setStringTags(escapeText("[Shift+Tab]"), g_HotkeyTags) | |
+ " - Change player statistics overlay (options->In-Game->Player statistics overlay, disable for performance)."; | |
// //+ " - Cycle Mode to " + next + "\n(Or in options)\n" | |
// + " - Cycle Overlay Mode to Light-, Dark-Static (as Click-Through/No Tooltip) or to Off (Also Settable in Options)\n" | |
// // + "\nP: Phase\nPop: Population\nI: Infantry\nC: Cavalary\nSE: Sieges/Elephants\nTe: Techs\nUK: Units Killed"; | |
// + "P: Phase, Pop: Population, I: Infantry, C: Cavalary, SE: Sieges/Elephants, Te: Techs, UK: Units Killed"; | |
let teamInfoGUI = Engine.GetGUIObjectByName("teamInfo"); | |
switch (+g_TeamInfoState) | |
{ | |
case 1: | |
teamInfoGUI.sprite = "BackgroundTranslucent"; | |
teamInfoGUI.tooltip = tp("Dark Static", "Light Static"); | |
g_TeamInfoKeepBg = true; | |
teamInfoGUI.hidden = false; | |
teamInfoGUI.ghost = true; | |
break; | |
case 2: | |
teamInfoGUI.sprite = "BackgroundTranslucent2"; | |
teamInfoGUI.tooltip = tp("Light Static", "Off"); | |
g_TeamInfoKeepBg = true; | |
teamInfoGUI.hidden = false; | |
teamInfoGUI.ghost = true; | |
break; | |
case 3: | |
teamInfoGUI.hidden = true; | |
teamInfoGUI.ghost = true; | |
break; | |
default: | |
teamInfoGUI.sprite = "BackgroundTranslucent2"; | |
teamInfoGUI.tooltip = tp("Hoverable", "Dark Static"); | |
teamInfoGUI.ghost = false; | |
g_TeamInfoKeepBg = false; | |
teamInfoGUI.hidden = false; | |
break; | |
} | |
} | |
function setTeamInfoOverlay() | |
{ | |
g_TeamInfoState = +Engine.ConfigDB_GetValue("user", "gui.session.showstats") || 0; | |
setTeamInfoState(); | |
} | |
function cycleTeamInfoState() | |
{ | |
g_TeamInfoState = ++g_TeamInfoState > 3 ? 0 : g_TeamInfoState; | |
saveSettingAndWriteToUserConfig("gui.session.showstats", g_TeamInfoState); | |
setTeamInfoOverlay(); | |
} | |
let g_TeamInfoGUI = Engine.GetGUIObjectByName("teamInfo") || false; | |
var g_PlayerPos = -1; | |
function init(initData, hotloadData) | |
{ | |
if (!g_Settings) | |
{ | |
Engine.EndGame(); | |
Engine.SwitchGuiPage("page_pregame.xml"); | |
return; | |
} | |
// Object.keys(g_PlayerAssignments).find(pl => { if (g_PlayerAssignments[pl].name == g_PlayerName) { | |
// g_IsObserver = isPlayerObserver(Engine.GetPlayerID()) | |
// g_GameAttributes.settings.RevealMap | |
// warn(initData.attribs.settings.RevealMap) | |
// warn(Engine.ConfigDB_GetValue("user", "session.askposunrevealed")) | |
g_LobbyTextGui = Engine.GetGUIObjectByName("lobbyText"); | |
loadLobbyChatOverlay(); | |
g_TeamInfoGUI = Engine.GetGUIObjectByName("teamInfo") || false; | |
g_ServerIP = initData.serverIP; | |
g_ServerPort = initData.serverPort; | |
g_UseSTUN = initData.useSTUN; | |
g_HostJID = initData.hostJID; | |
g_ServerName = !!initData.attribs && !!initData.attribs.gameName && initData.attribs.gameName; | |
g_PlayerName = !!initData.playerName ? initData.playerName : ""; | |
g_PassingArguments = initData.passingArguments || {}; | |
// Fallback used by atlas | |
g_PlayerAssignments = initData ? initData.playerAssignments : { "local": { "player": 1 } }; | |
// Fallback used by atlas and autostart games | |
if (g_PlayerAssignments.local && !g_PlayerAssignments.local.name) | |
g_PlayerAssignments.local.name = singleplayerName(); | |
if (initData) | |
{ | |
g_ReplaySelectionData = initData.replaySelectionData; | |
g_HasRejoined = initData.isRejoining; | |
if (initData.savedGUIData) | |
restoreSavedGameData(initData.savedGUIData); | |
} | |
LoadModificationTemplates(); | |
updatePlayerData(); | |
initializeMusic(); // before changing the perspective | |
initGUIObjects(); | |
if (hotloadData) | |
g_Selection.selected = hotloadData.selection | |
sendLobbyPlayerlistUpdate(); | |
onSimulationUpdate(); | |
setTimeout(displayGamestateNotifications, 1000); | |
reduceBadPingsCountsStart(); | |
// Report the performance after 5 seconds (when we're still near | |
// the initial camera view) and a minute (when the profiler will | |
// have settled down if framerates as very low), to give some | |
// extremely rough indications of performance | |
// | |
// DISABLED: this information isn't currently useful for anything much, | |
// and it generates a massive amount of data to transmit and store | |
// setTimeout(function() { reportPerformance(5); }, 5000); | |
// setTimeout(function() { reportPerformance(60); }, 60000); | |
// Engine.GetGUIObjectByName("teamInfo").onPress = () => { warn("ok")}; | |
// Engine.GetGUIObjectByName("lobbyChatPanel").onmouseenter = () => { warn("ok" + this.style); }; | |
//Engine.GetGUIObjectByName("lobbyChatPanel").onMouseLeave = () => { warn("ok2"); }; | |
let observersListKeep = false; | |
Engine.GetGUIObjectByName("teamInfo").onmouseenter = () => { | |
// Engine.GetGUIObjectByName("teamInfo").sprite = "BackgroundTranslucent"; | |
if (!g_TeamInfoKeepBg) | |
Engine.GetGUIObjectByName("teamInfo").sprite = "BackgroundTranslucent"; | |
Engine.GetGUIObjectByName("teamInfoHide").hidden = false; | |
Engine.GetGUIObjectByName("teamInfoHide").onmouseenter = () => { | |
// Engine.GetGUIObjectByName("teamInfo").sprite = teamInfoKeep ? "BackgroundTranslucent" : "BackgroundTranslucent2"; | |
if (!g_TeamInfoKeepBg) | |
Engine.GetGUIObjectByName("teamInfo").sprite = "BackgroundTranslucent2"; | |
Engine.GetGUIObjectByName("observersList").sprite = observersListKeep ? "BackgroundTranslucent" : "BackgroundTranslucent2"; | |
Engine.GetGUIObjectByName("teamInfoHide").hidden = true; | |
}; | |
}; | |
Engine.GetGUIObjectByName("observersList").onmouseenter = () => { | |
Engine.GetGUIObjectByName("observersList").sprite = "BackgroundTranslucent"; | |
Engine.GetGUIObjectByName("teamInfoHide").hidden = false; | |
Engine.GetGUIObjectByName("teamInfoHide").onmouseenter = () => { | |
Engine.GetGUIObjectByName("observersList").sprite = observersListKeep ? "BackgroundTranslucent" : "BackgroundTranslucent2"; | |
Engine.GetGUIObjectByName("teamInfo").sprite = g_TeamInfoKeepBg ? "BackgroundTranslucent" : "BackgroundTranslucent2"; | |
Engine.GetGUIObjectByName("teamInfoHide").hidden = true; | |
}; | |
}; | |
// Engine.GetGUIObjectByName("teamInfo").onpress = cycleTeamInfoState; | |
setTeamInfoOverlay(); | |
Engine.GetGUIObjectByName("observersList").tooltip = "Infos: " + setStringTags(escapeText("[Click]"), g_HotkeyTags) + " - Darkened on/off." | |
Engine.GetGUIObjectByName("observersList").onpress = () => { observersListKeep = !observersListKeep; }; | |
//Engine.GetGUIObjectByName("teamInfo").sprite = "BackgroundTranslucent2"; }; | |
// warn(g_GameSpeeds.Speed.indexOf(Engine.GetSimRate())); | |
// warn(uneval(g_GameSpeeds)) | |
// let playerStates = Engine.GuiInterfaceCall("GetExtendedSimulationState").players[1]; | |
// let g = g_ViewedPlayer; | |
// Engine.SetViewedPlayer(1); | |
// let playerStates = Engine.GuiInterfaceCall("GetPlayerEntities"); | |
// Engine.SetViewedPlayer(g_ViewedPlayer); | |
// warn("ok "+uneval(playerStates)); | |
// g_ViewedPlayer = g; | |
// warn(uneval(Engine.GuiInterfaceCall("GetEntityState", playerStates[9]).identity.visibleClasses.forEach(o => switch(o) { | |
// case "Cavalry"))); | |
// warn(uneval([...playerStates.researchedTechs])); | |
// warn(playerStates[2].phase) | |
// let kill = playerStates[1]; | |
// for (let o in kill) | |
// warn(o) | |
// let ok = 0; | |
// for (let o in kill) | |
// warn(kill.total[kill.total.length-1]); //uneval(kill[o])); | |
// ok += kill[o].reduce((a, b) => a + b, 0); | |
// warn(ok); | |
// let playerStates = Engine.GuiInterfaceCall("GetSimulationState"); | |
// let researchStarted = Engine.GuiInterfaceCall("GetResearchedTechs", g_ViewedPlayer); | |
// warn(uneval(researchStarted)); //.players[0].researchedTechs)) | |
// warn(uneval(Object.keys(playerStates.players))); //.players[0].researchedTechs)) | |
// warn(uneval(playerStates.players[1].researchedTechs)) | |
// warn(uneval(playerStates.players[1].researchQueued)) | |
// warn(uneval(playerStates.players[1].researchStarted)) | |
// warn(playerStates.players[1].name); | |
// warn(uneval(Object.keys(playerStates.players[1].researchedTechs))) | |
// warn(uneval(Object.keys(playerStates.players[1].researchQueued))) | |
// warn(uneval(Object.keys(playerStates.players[1].researchStarted))) | |
// warn(uneval(Object.keys(playerState.players[1].sequences))) | |
// g_NetworkWarningTexts["client-timeout"]({lastReceivedTime: 1000}, "fpre3"); | |
// g_NetworkWarningTexts["client-timeout"]({lastReceivedTime: 5000}, "fpre3"); | |
// g_NetworkWarningTexts["client-latency"]({meanRTT: 512}, "fpre3"); | |
// g_NetworkWarningTexts["client-latency"]({meanRTT: 512}, "fpre3"); | |
// g_NetworkWarningTexts["client-latency"]({meanRTT: 512}, "fpre3"); | |
// g_NetworkWarningTexts["client-latency"]({meanRTT: 512}, "fpre3"); | |
// g_NetworkWarningTexts["client-latency"]({meanRTT: 512}, "fpre3"); | |
// warn(g_NetworkWarningTexts["client-timeout"]({lastReceivedTime: 2000}, "fpre3")); | |
// warn(g_NetworkWarningTexts["client-timeout"]({lastReceivedTime: 2000}, "fpre3")); | |
// warn(g_NetworkWarningTexts["client-timeout"]({lastReceivedTime: 2000}, "fpre3")); | |
// warn(g_NetworkWarningTexts["client-timeout"]({lastReceivedTime: 2000}, "fpre3")); | |
// warn(g_NetworkWarningTexts["client-timeout"]({lastReceivedTime: 2000}, "fpre3")); | |
// warn(uneval()) | |
// let pl = g_PlayerAssignments[Object.keys(g_PlayerAssignments)[0]]; | |
// warn(uneval(g_Players[pl.player].state | |
// )) | |
if (!g_IsObserver && Engine.ConfigDB_GetValue("user", "session.askposunrevealed") != "false" && !initData.attribs.settings.ExploreMap && !initData.attribs.settings.RevealMap && (!Object.keys(g_MessageMenusSavePerma).length || g_MessageMenusSavePerma[pos] == -1)) | |
{ | |
toggleMessageMenu(); | |
switchMenuKey(8); | |
} | |
Engine.GetGUIObjectByName("menuButton").tooltip = tooltipCaption("Toggle "+ colorizeHotkey("%(hotkey)s", "session.gui.menu.toggle")) ; | |
Engine.GetGUIObjectByName("manualButton").caption = translate("Manual") + " / " + setStringTags("FGod", { "color": "white" }); | |
Engine.GetGUIObjectByName("pauseButton").tooltip = tooltipCaption(colorizeHotkey("%(hotkey)s", "pause") + "\n" + colorizeHotkey("%(hotkey)s", "session.pingpauser") + " - Ping pausing player."); | |
Engine.GetGUIObjectByName("chatButton").tooltip = tooltipCaption([colorizeHotkey("%(hotkey)s", "chat"), colorizeHotkey("%(hotkey)s", "teamchat"), colorizeHotkey("%(hotkey)s", "privatechat")].join(", ")); | |
Engine.GetGUIObjectByName("summaryButton").tooltip = tooltipCaption(colorizeHotkey("%(hotkey)s", "summary")); | |
Engine.GetGUIObjectByName("manualButton").tooltip = tooltipCaption(colorizeHotkey("%(hotkey)s", "fgodmanual")); | |
Engine.GetGUIObjectByName("optionsButton").tooltip = tooltipCaption(colorizeHotkey("%(hotkey)s", "options")); | |
Engine.GetGUIObjectByName("lobbyButton").tooltip = tooltipCaption(colorizeHotkey("%(hotkey)s", "lobby")); | |
Engine.GetGUIObjectByName("lobbyMinimapButton").tooltip = tooltipCaption("Lobby ") + tooltipCaption(colorizeHotkey("%(hotkey)s", "lobby")) + "\n\n" + tooltipCaption(colorizeHotkey("%(hotkey)s", "chat")) + " - " + tooltipCaption("Game Chat") + ".\n" + (g_IsObserver ? "" : tooltipCaption(colorizeHotkey("%(hotkey)s", "session.messagemenu_en")) + ", " + tooltipCaption(colorizeHotkey("%(hotkey)s", "session.messagemenu")) + " - " + tooltipCaption("Ally Message Menu") + ".\n"); | |
Engine.GetGUIObjectByName("summaryMinimapButton").tooltip = tooltipCaption("Summary ") + tooltipCaption(colorizeHotkey("%(hotkey)s", "summary")) + "\n\n" + tooltipCaption(colorizeHotkey("%(hotkey)s", "session.playerstatsoverview")) + " - " + tooltipCaption("Mode Player Stats Overlay.") + "\n" + (g_IsObserver ? "" : tooltipCaption(colorizeHotkey("%(hotkey)s", "session.allyequalizeresources")) + " - " + tooltipCaption("Equalize Higher Resources to Team per portional by team member count and Request Lower Resources from Allies.")) + (g_IsObserver ? "" : + "\n" + tooltipCaption(colorizeHotkey("%(hotkey)s", "session.allyrequestresources")) + " - " + tooltipCaption("Request Higher Resources from Allies.")); | |
Engine.GetGUIObjectByName("menuExitButton").tooltip = tooltipCaption(colorizeHotkey("%(hotkey)s", "close")); | |
Engine.GetGUIObjectByName("diplomacyButton").tooltip = tooltipCaption(translate("Diplomacy") + " " + colorizeHotkey("%(hotkey)s", "session.gui.diplomacy.toggle")); | |
Engine.GetGUIObjectByName("tradeButton").tooltip = tooltipCaption(translate("Barter & Trade") + colorizeHotkey(" %(hotkey)s", "session.gui.barter.toggle")); | |
Engine.GetGUIObjectByName("objectivesButton").tooltip = tooltipCaption(translate("Objectives") + colorizeHotkey(" %(hotkey)s", "session.gui.objectives.toggle")); | |
Engine.GetGUIObjectByName("gameSpeedButton").tooltip = tooltipCaption(translate("Game Speed") + " " + | |
colorizeHotkey("%(hotkey)s" , "session.gui.gamespeed.toggle")); | |
Engine.GetGUIObjectByName("gameSpeed").tooltip = tooltipCaption(translate("Game Speed") + "\n") + tooltipCaption(colorizeHotkey("%(hotkey)s", "session.gui.gamespeed.faster")) + " - Faster Game Speed.\n" + tooltipCaption(colorizeHotkey("%(hotkey)s", "session.gui.gamespeed.slower")) + " - Slower Game Speed." ; | |
Engine.GetGUIObjectByName("viewPlayer").tooltip = "Toggle:\n" + | |
setStringTags("\\[Alt+0-9]", g_HotkeyTags) + sprintf(translate(" - Show View of Player Nr. 0-9.") + | |
"\n%(hotkeyPrev)s - Show View of previous Player." + | |
"\n%(hotkeySel)s - Show View of Player of selected unit.\n" + (!g_GameAttributes.settings.LockTeams ? "Diplomacy: + ally, = neutral, - enemy" : ""), | |
{ | |
"hotkeyPrev": colorizeHotkey("%(hotkey)s", "session.selectplayer.prev"), | |
"hotkeySel": colorizeHotkey("%(hotkey)s", "session.selectedunitplayerview") | |
}); | |
if (false && !!g_PassingArguments["chatText"]) | |
{ | |
updateChatAddressees(); | |
Engine.GetGUIObjectByName("chatInput").caption = g_PassingArguments["chatText"]; | |
openChatSolo(); | |
} | |
} | |
function tooltipCaption(name) | |
{ | |
return setStringTags(name, { "font": "sans-bold-stroke-14" }) | |
}; | |
function initGUIObjects() | |
{ | |
initMenu(); | |
updateGameSpeedControl(); | |
resizeDiplomacyDialog(); | |
resizeTradeDialog(); | |
initBarterButtons(); | |
initPanelEntities(); | |
initViewedPlayerDropdown(); | |
initChatWindow(); | |
Engine.SetBoundingBoxDebugOverlay(false); | |
updateEnabledRangeOverlayTypes(); | |
} | |
function updatePlayerData() | |
{ | |
let simState = GetSimState(); | |
if (!simState) | |
return; | |
let playerData = []; | |
for (let i = 0; i < simState.players.length; ++i) | |
{ | |
let playerState = simState.players[i]; | |
playerData.push({ | |
"name": playerState.name, | |
"civ": playerState.civ, | |
"color": { | |
"r": playerState.color.r * 255, | |
"g": playerState.color.g * 255, | |
"b": playerState.color.b * 255, | |
"a": playerState.color.a * 255 | |
}, | |
"team": playerState.team, | |
"teamsLocked": playerState.teamsLocked, | |
"cheatsEnabled": playerState.cheatsEnabled, | |
"state": playerState.state, | |
"isAlly": playerState.isAlly, | |
"isMutualAlly": playerState.isMutualAlly, | |
"isNeutral": playerState.isNeutral, | |
"isEnemy": playerState.isEnemy, | |
"guid": undefined, // network guid for players controlled by hosts | |
"offline": g_Players[i] && !!g_Players[i].offline | |
}); | |
} | |
for (let guid in g_PlayerAssignments) | |
{ | |
let playerID = g_PlayerAssignments[guid].player; | |
if (!playerData[playerID]) | |
continue; | |
playerData[playerID].guid = guid; | |
playerData[playerID].name = g_PlayerAssignments[guid].name; | |
} | |
g_Players = playerData; | |
} | |
function updateDiplomacyColorsButton() | |
{ | |
g_DiplomacyColorsToggle = !g_DiplomacyColorsToggle; | |
let diplomacyColorsButton = Engine.GetGUIObjectByName("diplomacyColorsButton"); | |
diplomacyColorsButton.sprite = g_DiplomacyColorsToggle ? | |
"stretched:session/minimap-diplomacy-on.png" : | |
"stretched:session/minimap-diplomacy-off.png"; | |
diplomacyColorsButton.sprite_over = g_DiplomacyColorsToggle ? | |
"stretched:session/minimap-diplomacy-on-highlight.png" : | |
"stretched:session/minimap-diplomacy-off-highlight.png"; | |
Engine.GetGUIObjectByName("diplomacyColorsWindowButtonIcon").sprite = g_DiplomacyColorsToggle ? | |
"stretched:session/icons/diplomacy-on.png" : | |
"stretched:session/icons/diplomacy.png"; | |
updateDisplayedPlayerColors(); | |
} | |
/** | |
* Updates the displayed colors of players in the simulation and GUI. | |
*/ | |
function updateDisplayedPlayerColors() | |
{ | |
if (g_DiplomacyColorsToggle) | |
{ | |
let getDiplomacyColor = stance => | |
guiToRgbColor(Engine.ConfigDB_GetValue("user", "gui.session.diplomacycolors." + stance)) || | |
guiToRgbColor(Engine.ConfigDB_GetValue("default", "gui.session.diplomacycolors." + stance)); | |
let teamRepresentatives = {}; | |
for (let i = 1; i < g_Players.length; ++i) | |
if (g_ViewedPlayer <= 0) | |
{ | |
// Observers and gaia see team colors | |
let team = g_Players[i].team; | |
g_DisplayedPlayerColors[i] = g_Players[teamRepresentatives[team] || i].color; | |
if (team != -1 && !teamRepresentatives[team]) | |
teamRepresentatives[team] = i; | |
} | |
else | |
// Players see colors depending on diplomacy | |
g_DisplayedPlayerColors[i] = | |
g_ViewedPlayer == i ? getDiplomacyColor("self") : | |
g_Players[g_ViewedPlayer].isAlly[i] ? getDiplomacyColor("ally") : | |
g_Players[g_ViewedPlayer].isNeutral[i] ? getDiplomacyColor("neutral") : | |
getDiplomacyColor("enemy"); | |
g_DisplayedPlayerColors[0] = g_Players[0].color; | |
} | |
else | |
g_DisplayedPlayerColors = g_Players.map(player => player.color); | |
Engine.GuiInterfaceCall("UpdateDisplayedPlayerColors", { | |
"displayedPlayerColors": g_DisplayedPlayerColors, | |
"displayDiplomacyColors": g_DiplomacyColorsToggle, | |
"showAllStatusBars": g_ShowAllStatusBars, | |
"selected": g_Selection.toList() | |
}); | |
updateGUIObjects(); | |
} | |
/** | |
* Depends on the current player (g_IsObserver). | |
*/ | |
function updateHotkeyTooltips() | |
{ | |
Engine.GetGUIObjectByName("chatInput").tooltip = | |
translateWithContext("chat input", "Type the message to send.") + "\n" + | |
colorizeAutocompleteHotkey() + | |
colorizeHotkey("\n" + translate("Press %(hotkey)s to open the public chat."), "chat") + | |
colorizeHotkey( | |
"\n" + (g_IsObserver ? | |
translate("Press %(hotkey)s to open the observer chat.") : | |
translate("Press %(hotkey)s to open the ally chat.")), | |
"teamchat") + | |
colorizeHotkey("\n" + translate("Press %(hotkey)s to open the previously selected private chat."), "privatechat"); | |
Engine.GetGUIObjectByName("idleWorkerButton").tooltip = | |
tooltipCaption(sprintf(translate("Find idle worker %(hotkey)s"), { "hotkey": colorizeHotkey("%(hotkey)s", "selection.idleworker") } )); | |
Engine.GetGUIObjectByName("diplomacyColorsButton").tooltip = | |
tooltipCaption(sprintf(translate("Toggle Diplomacy Colors %(hotkey)s") | |
, { "hotkey": colorizeHotkey("%(hotkey)s", "session.diplomacycolors") } )); | |
Engine.GetGUIObjectByName("diplomacyColorsWindowButton").tooltip = | |
colorizeHotkey("%(hotkey)s" + " ", "session.diplomacycolors") + | |
translate("Toggle Diplomacy Colors"); | |
Engine.GetGUIObjectByName("tradeHelp").tooltip = colorizeHotkey( | |
translate("Select one type of goods you want to modify by clicking on it, and then use the arrows of the other types to modify their shares. You can also press %(hotkey)s while selecting one type of goods to bring its share to 100%%."), | |
"session.fulltradeswap"); | |
Engine.GetGUIObjectByName("barterHelp").tooltip = sprintf( | |
translate("Start by selecting the resource you wish to sell from the upper row. For each time the lower buttons are pressed, %(quantity)s of the upper resource will be sold for the displayed quantity of the lower. Press and hold %(hotkey)s to temporarily multiply the traded amount by %(multiplier)s."), { | |
"quantity": g_BarterResourceSellQuantity, | |
"hotkey": colorizeHotkey("%(hotkey)s", "session.massbarter"), | |
"multiplier": g_BarterMultiplier | |
}); | |
} | |
function initPanelEntities() | |
{ | |
Engine.GetGUIObjectByName("panelEntityPanel").children.forEach((button, slot) => { | |
button.onPress = function() { | |
let panelEnt = g_PanelEntities.find(ent => ent.slot !== undefined && ent.slot == slot); | |
if (!panelEnt) | |
return; | |
if (!Engine.HotkeyIsPressed("selection.add")) | |
g_Selection.reset(); | |
g_Selection.addList([panelEnt.ent]); | |
}; | |
button.onDoublePress = function() { | |
let panelEnt = g_PanelEntities.find(ent => ent.slot !== undefined && ent.slot == slot); | |
if (panelEnt) | |
selectAndMoveTo(getEntityOrHolder(panelEnt.ent)); | |
}; | |
}); | |
} | |
/** | |
* Returns the entity itself except when garrisoned where it returns its garrisonHolder | |
*/ | |
function getEntityOrHolder(ent) | |
{ | |
let entState = GetEntityState(ent); | |
if (entState && !entState.position && entState.unitAI && entState.unitAI.orders.length && | |
entState.unitAI.orders[0].type == "Garrison") | |
return getEntityOrHolder(entState.unitAI.orders[0].data.target); | |
return ent; | |
} | |
function initializeMusic() | |
{ | |
initMusic(); | |
if (g_ViewedPlayer != -1 && g_CivData[g_Players[g_ViewedPlayer].civ].Music) | |
global.music.storeTracks(g_CivData[g_Players[g_ViewedPlayer].civ].Music); | |
global.music.setState(global.music.states.PEACE); | |
playAmbient(); | |
} | |
function initViewedPlayerDropdown() | |
{ | |
g_DisplayedPlayerColors = g_Players.map(player => player.color); | |
updateViewedPlayerDropdown(); | |
// Select "observer" in the view player dropdown when rejoining as a defeated player | |
let player = g_Players[Engine.GetPlayerID()]; | |
Engine.GetGUIObjectByName("viewPlayer").selected = player && player.state == "defeated" ? 0 : Engine.GetPlayerID() + 1; | |
} | |
function updateViewedPlayerDropdown() | |
{ | |
// warn(g_ViewedPlayer) | |
let viewPlayer = Engine.GetGUIObjectByName("viewPlayer"); | |
g_Players = g_Players.map((p, i) => { p.id = i; return p; }); //.map((p, i) => { if (id in p) p.id = i; }) | |
let playerList = g_Players.slice().sort((a, b) => a.team - b.team); | |
// playerList.forEach(player => warn(player.state + " " + player.name)); | |
// for (let i in g_Players[0]) warn(uneval(i)) | |
// for (let i in g_Players[0]) warn(uneval(g_Players[3].state)) | |
let viewedPlayer = g_Players[g_ViewedPlayer]; | |
viewPlayer.list_data = [-1].concat(playerList.map(player => player.id)); | |
viewPlayer.list = [translate("Overlord View")].concat(playerList.map( | |
(player, i) => setStringTags(sprintf("%(team)s%(name)s", { "team" : | |
(i >= 0 ? player.teamsLocked ? (player.team > -1 ? "T." + (player.team+1) + " - " : "") + setStringTags(i + ".", { "color": player.id == g_ViewedPlayer ? "140 140 255" : "210 210 210" }) + " " : | |
(g_ViewedPlayer >= 0 ? | |
( g_ViewedPlayer == player.id ? "" : player.isEnemy[g_ViewedPlayer] ? "- " : player.isNeutral[g_ViewedPlayer] ? "= " : viewedPlayer.isAlly[player.id] ? "+ " : "") + "" : | |
"") + i + " " : | |
""), | |
"name": colorizePlayernameHelper("■", player.id) + " " + setStringTags(player.name, { "color": player.id == g_ViewedPlayer ? "140 140 255" : "white" }) + | |
(player.state == "won" ? " (" + translate("won") + ")" : | |
player.state == "defeated" ? " (" + translate("defeated") + ")" : | |
player.state == "Offline" || player.offline ? " (" + translate("Offline") + ")" : "") | |
}), { | |
"color": | |
player.state == "defeated" ? "128 128 128" : | |
player.state == "won" ? "255 237 34" : | |
player.state == "Offline" || player.offline ? "255 0 0" : | |
player.id == g_ViewedPlayer ? "255 255 255" : "255 255 255" }) | |
)); | |
} | |
function changeViewPlayer(id) | |
{ | |
if (!(g_IsObserver || g_DevSettings.changePerspective)) | |
return; | |
let viewPlayer = Engine.GetGUIObjectByName("viewPlayer"); | |
if (id >= viewPlayer.list.length - 1) | |
return; | |
if (id + 1 == viewPlayer.selected) | |
id = -1; | |
viewPlayer.selected = id >= 0 ? id + 1 : 0; | |
// g_TeamInfoTimeLength = g_TeamInfoTimeUpdate; | |
teamInfo(); | |
} | |
function toggleChangePerspective(enabled) | |
{ | |
g_DevSettings.changePerspective = enabled; | |
selectViewPlayer(g_ViewedPlayer); | |
} | |
function showReplaceButton() | |
{ | |
// TODO: Offline player (g_Players[g_ViewedPlayer].offline) is at the moment not up-to-date for rejoining players | |
// so we can't show the replace button only for offline players. So for now just show replace button for any networked | |
// game player other than gaia. | |
Engine.GetGUIObjectByName("replaceButton").enabled = !g_IsController && !!g_PassingArguments.nameChangePossible; | |
if (!g_PassingArguments.nameChangePossible) | |
Engine.GetGUIObjectByName("replaceButtonImage").sprite = "iconResetDisabled"; | |
Engine.GetGUIObjectByName("replaceButton").hidden = !g_IsNetworked || g_ViewedPlayer < 1 || !g_IsObserver; | |
let selected = Engine.GetGUIObjectByName("viewPlayer").selected; | |
if (selected <= 1) | |
return; | |
let player = g_Players[Engine.GetGUIObjectByName("viewPlayer").selected -1 ]; | |
Engine.GetGUIObjectByName("replaceButton").tooltip = g_PassingArguments.nameChangePossible ? "Replace " + colorizePlayernameHelper(player.name, player.id) + " " + setStringTags("if he is offline", { color: "yellow"}) + "." //+ " and in the host " + setStringTags("lobby authentication isn't enabled", { color: "yellow"}) + " (try out)." | |
: g_IsController ? "Host cannot replace" : "Replace is disabled by the host." | |
} | |
var g_noClearSelection = false; | |
var g_LastViewPlayer = -1; | |
/** | |
* Change perspective tool. | |
* Shown to observers or when enabling the developers option. | |
*/ | |
function selectViewPlayer(playerID) | |
{ | |
if (playerID < -1 || playerID > g_Players.length - 1) | |
return; | |
if (g_ShowAllStatusBars) | |
recalculateStatusBarDisplay(true); | |
g_IsObserver = isPlayerObserver(Engine.GetPlayerID()); | |
if (g_IsObserver || g_DevSettings.changePerspective) | |
{ | |
if (!g_noClearSelection && g_ViewedPlayer != playerID) | |
clearSelection(); | |
g_LastViewPlayer = g_ViewedPlayer; | |
g_ViewedPlayer = playerID; | |
} | |
if (g_DevSettings.changePerspective) | |
{ | |
Engine.SetPlayerID(g_ViewedPlayer); | |
g_IsObserver = isPlayerObserver(g_ViewedPlayer); | |
} | |
Engine.SetViewedPlayer(g_ViewedPlayer); | |
updateDisplayedPlayerColors(); | |
updateTopPanel(); | |
teamInfo(); | |
updateChatAddressees(); | |
updateHotkeyTooltips(); | |
// Update GUI and clear player-dependent cache | |
g_TemplateData = {}; | |
Engine.GuiInterfaceCall("ResetTemplateModified"); | |
onSimulationUpdate(); | |
if (g_IsDiplomacyOpen) | |
openDiplomacy(); | |
if (g_IsTradeOpen) | |
openTrade(); | |
showReplaceButton(); | |
// updateViewedPlayerDropdown(); | |
} | |
/** | |
* Returns true if the player with that ID is in observermode. | |
*/ | |
function isPlayerObserver(playerID) | |
{ | |
let playerStates = GetSimState().players; | |
return !playerStates[playerID] || playerStates[playerID].state != "active"; | |
} | |
/** | |
* Returns true if the current user can issue commands for that player. | |
*/ | |
function controlsPlayer(playerID) | |
{ | |
let playerStates = GetSimState().players; | |
return playerStates[Engine.GetPlayerID()] && | |
playerStates[Engine.GetPlayerID()].controlsAll || | |
Engine.GetPlayerID() == playerID && | |
playerStates[playerID] && | |
playerStates[playerID].state != "defeated"; | |
} | |
/** | |
* Called when one or more players have won or were defeated. | |
* | |
* @param {array} - IDs of the players who have won or were defeated. | |
* @param {object} - a plural string stating the victory reason. | |
* @param {boolean} - whether these players have won or lost. | |
*/ | |
function playersFinished(players, victoryString, won) | |
{ | |
addChatMessage({ | |
"type": "defeat-victory", | |
"message": victoryString, | |
"players": players | |
}); | |
if (players.indexOf(Engine.GetPlayerID()) != -1) | |
reportGame(); | |
sendLobbyPlayerlistUpdate(); | |
updatePlayerData(); | |
updateChatAddressees(); | |
updateGameSpeedControl(); | |
updateViewedPlayerDropdown(); | |
if (players.indexOf(g_ViewedPlayer) == -1) | |
return; | |
// Select "observer" item on loss. On win enable observermode without changing perspective | |
Engine.GetGUIObjectByName("viewPlayer").selected = won ? g_ViewedPlayer + 1 : 0; | |
closeMessageMenu(); | |
if (players.indexOf(Engine.GetPlayerID()) == -1 || Engine.IsAtlasRunning()) | |
return; | |
global.music.setState( | |
won ? | |
global.music.states.VICTORY : | |
global.music.states.DEFEAT | |
); | |
g_ConfirmExit = won ? "won" : "defeated"; | |
} | |
/** | |
* Sets civ icon for the currently viewed player. | |
* Hides most gui objects for observers. | |
*/ | |
function updateTopPanel() | |
{ | |
let isPlayer = g_ViewedPlayer > 0; | |
let civIcon = Engine.GetGUIObjectByName("civIcon"); | |
civIcon.hidden = !isPlayer; | |
if (isPlayer) | |
{ | |
civIcon.sprite = "stretched:" + g_CivData[g_Players[g_ViewedPlayer].civ].Emblem; | |
Engine.GetGUIObjectByName("civIconOverlay").tooltip = | |
sprintf( | |
translate("%(civ)s\n%(hotkey_civinfo)s / %(hotkey_structree)s: View History / Structure Tree\nLast opened structure tree/history will be reopened."), { | |
"civ": setStringTags(g_CivData[g_Players[g_ViewedPlayer].civ].Name, { "font": "sans-bold-stroke-14" }), | |
"hotkey_civinfo": colorizeHotkey("%(hotkey)s", "civinfo"), | |
"hotkey_structree": colorizeHotkey("%(hotkey)s", "structree") | |
}); | |
} | |
// Following gaia can be interesting on scripted maps | |
Engine.GetGUIObjectByName("optionFollowPlayer").hidden = !g_IsObserver || g_ViewedPlayer == -1; | |
let viewPlayer = Engine.GetGUIObjectByName("viewPlayer"); | |
viewPlayer.hidden = !g_IsObserver && !g_DevSettings.changePerspective; | |
let followPlayerLabel = Engine.GetGUIObjectByName("followPlayerLabel"); | |
followPlayerLabel.hidden = Engine.GetTextWidth(followPlayerLabel.font, followPlayerLabel.caption + " ") + | |
followPlayerLabel.getComputedSize().left > viewPlayer.getComputedSize().left; | |
let resCodes = g_ResourceData.GetCodes(); | |
let r = 0; | |
for (let res of resCodes) | |
{ | |
if (!Engine.GetGUIObjectByName("resource[" + r + "]")) | |
{ | |
warn("Current GUI limits prevent displaying more than " + r + " resources in the top panel!"); | |
break; | |
} | |
Engine.GetGUIObjectByName("resource[" + r + "]_icon").sprite = "stretched:session/icons/resources/" + res + ".png"; | |
Engine.GetGUIObjectByName("resource[" + r + "]").hidden = g_ViewedPlayer == 0; | |
++r; | |
} | |
horizontallySpaceObjects("resourceCounts", 5); | |
hideRemaining("resourceCounts", r); | |
let resPop = Engine.GetGUIObjectByName("population"); | |
let resPopSize = resPop.size; | |
resPopSize.left = Engine.GetGUIObjectByName("resource[" + (r - 1) + "]").size.right; | |
resPop.size = resPopSize; | |
// Engine.GetGUIObjectByName("population").hidden = !isPlayer; | |
Engine.GetGUIObjectByName("population").hidden = g_ViewedPlayer == 0; | |
Engine.GetGUIObjectByName("diplomacyButton").hidden = !isPlayer; | |
Engine.GetGUIObjectByName("tradeButton").hidden = !isPlayer; | |
// Engine.GetGUIObjectByName("observerText").hidden = isPlayer; | |
Engine.GetGUIObjectByName("observerText").hidden = g_ViewedPlayer != 0; | |
let alphaLabel = Engine.GetGUIObjectByName("alphaLabel"); | |
// alphaLabel.hidden = isPlayer && !viewPlayer.hidden; | |
alphaLabel.hidden = g_ViewedPlayer != 0 && !viewPlayer.hidden; | |
alphaLabel.size = isPlayer ? "50%+44 0 100%-283 100%" : "155 0 85%-279 100%"; | |
Engine.GetGUIObjectByName("pauseButton").enabled = !g_IsObserver || !g_IsNetworked || g_IsController; | |
Engine.GetGUIObjectByName("menuResignButton").enabled = !g_IsObserver; | |
} | |
function reportPerformance(time) | |
{ | |
let settings = g_GameAttributes.settings; | |
Engine.SubmitUserReport("profile", 3, JSON.stringify({ | |
"time": time, | |
"map": settings.Name, | |
"seed": settings.Seed, // only defined for random maps | |
"size": settings.Size, // only defined for random maps | |
"profiler": Engine.GetProfilerState() | |
})); | |
} | |
function equalizeResourcesWithAllies() | |
{ | |
return; | |
let playerStates = GetSimState().players; | |
let viewedPlayerStates = playerStates[g_ViewedPlayer]; | |
let player = g_Players[g_ViewedPlayer]; | |
let players = g_Players.map((pl,i) => { pl.id = i; return pl; }).filter((pl, i) => { | |
return i != g_ViewedPlayer && pl.state != "defeated" && (pl.team == player.team || player.isMutualAlly[i]); | |
}); | |
playerStates = g_Players.map(pl => playerStates[pl.id]); | |
if (players.length) | |
{ | |
let amounts = {}; | |
let resCodes = g_ResourceData.GetCodes(); | |
let playersPerRes = []; | |
for (let res of resCodes) | |
{ | |
playersPerRes[res] = playerStates.filter(pl => playerStates[pl.id].resourceCounts[res] < viewedPlayerStates.resourceCounts[res]); | |
playersPerRes[res].forEach(pl => amounts[res] += playerStates[pl.id].resourceCounts[res]); | |
amounts[res] += viewedPlayerStates.resourceCounts[res]; | |
} | |
for (let res of resCodes) | |
amounts[res] = Math.floor(amounts[res]/(playersPerRes[res].length + 1)); | |
for (let res of resCodes) | |
playersPerRes[res].forEach(pl => { | |
let amountsSend = []; | |
amountsSend[res] = amounts[res] - playerStates[pl.id].resourceCounts[res]; | |
Engine.PostNetworkCommand({ "type": "tribute", "player": pl.id, "amounts": amountsSend }); | |
}); | |
} | |
} | |
/** | |
* Resign a player. | |
* @param leaveGameAfterResign If player is quitting after resignation. | |
*/ | |
function resignGame(leaveGameAfterResign) | |
{ | |
if (g_IsObserver || g_Disconnected) | |
return; | |
if (Engine.ConfigDB_GetValue("user", "session.sendresonresign") == "true") | |
{ | |
let player = g_Players[g_ViewedPlayer]; | |
let players = g_Players.map((pl,i) => { pl.id = i; return pl; }).filter((pl, i) => { | |
return i != g_ViewedPlayer && pl.state != "defeated" && (player.isMutualAlly[i]); | |
}) | |
if (players.length) | |
{ | |
let viewedPlayerStates = GetSimState().players[g_ViewedPlayer]; | |
let amounts = {}; | |
let resCodes = g_ResourceData.GetCodes(); | |
for (let res of resCodes) | |
amounts[res] = Math.floor(viewedPlayerStates.resourceCounts[res] / players.length); | |
players.forEach(pl => { Engine.PostNetworkCommand({ "type": "tribute", "player": pl.id, "amounts": amounts }); }); | |
} | |
} | |
Engine.PostNetworkCommand({ | |
"type": "resign" | |
}); | |
if (!leaveGameAfterResign) | |
resumeGame(true); | |
} | |
/** | |
* Leave the game | |
* @param willRejoin If player is going to be rejoining a networked game. | |
*/ | |
function leaveGame(willRejoin) | |
{ | |
if (!willRejoin && !g_IsObserver) | |
resignGame(true); | |
// Before ending the game | |
let replayDirectory = Engine.GetCurrentReplayDirectory(); | |
let simData = getReplayMetadata(); | |
let playerID = Engine.GetPlayerID(); | |
let summaryPageSettings = [ "page_summary.xml", { | |
"sim": simData, | |
"gui": { | |
"dialog": false, | |
"assignedPlayer": playerID, | |
"disconnected": g_Disconnected, | |
"isReplay": g_IsReplay, | |
"isEndGame": true, | |
"replayDirectory": !g_HasRejoined && replayDirectory, | |
"replaySelectionData": g_ReplaySelectionData | |
} | |
} ]; | |
Engine.EndGame(); | |
// After the replay file was closed in EndGame | |
// Done here to keep EndGame small | |
if (!g_IsReplay) | |
Engine.AddReplayToCache(replayDirectory); | |
if (g_IsController && Engine.HasXmppClient()) | |
Engine.SendUnregisterGame(); | |
if (g_IsController) | |
saveSettingAndWriteToUserConfig("multiplayerhosting.lobby", "false"); | |
g_PageOnLeaveSettings = g_PageOnLeaveSettings.length ? g_PageOnLeaveSettings : summaryPageSettings; | |
Engine.SwitchGuiPage(...g_PageOnLeaveSettings); | |
} | |
// Return some data that we'll use when hotloading this file after changes | |
function getHotloadData() | |
{ | |
return { "selection": g_Selection.selected }; | |
} | |
function getSavedGameData() | |
{ | |
return { | |
"groups": g_Groups.groups | |
}; | |
} | |
function restoreSavedGameData(data) | |
{ | |
// Restore camera if any | |
if (data.camera) | |
Engine.SetCameraData(data.camera.PosX, data.camera.PosY, data.camera.PosZ, | |
data.camera.RotX, data.camera.RotY, data.camera.Zoom); | |
// Clear selection when loading a game | |
g_Selection.reset(); | |
// Restore control groups | |
for (let groupNumber in data.groups) | |
{ | |
g_Groups.groups[groupNumber].groups = data.groups[groupNumber].groups; | |
g_Groups.groups[groupNumber].ents = data.groups[groupNumber].ents; | |
} | |
updateGroups(); | |
} | |
const g_TeamInfoTimeUpdate = 200; | |
// let g_TeamInfoTimeLength = g_TeamInfoTimeUpdate; | |
let g_TeamInfoTimeLength = Engine.ConfigDB_GetValue("user", "session.summaryautoupdate"); | |
var g_ExtendedSimState = { "state": {}, "lastUpdate": 0, "updateTime": 200 }; | |
function getExtendedSimState() | |
{ | |
if (Date.now() - g_ExtendedSimState.lastUpdate > g_ExtendedSimState.updateTime) | |
{ | |
g_ExtendedSimState.state = Engine.GuiInterfaceCall("GetExtendedSimulationState"); | |
g_ExtendedSimState.lastUpdate = Date.now(); | |
} | |
return g_ExtendedSimState.state; | |
} | |
var g_AutoEqualizeRequestResourcesToAllies = { "lastUpdate": 0, "updateTime": 5 }; | |
var now2 = 0; | |
var hitted = {}; | |
/** | |
* Called every frame. | |
*/ | |
function onTick() | |
{ | |
if (!g_Settings) | |
return; | |
if (!now2) | |
now2 = Date.now(); | |
let state = Math.ceil((Date.now() - now2)/1000); | |
if (false && g_IsController && !hitted[state] && ((state > 1 && state < 6) || | |
(state > 8 && state < 11))) | |
if (true) | |
{ | |
warn(state) | |
// g_NetworkWarningTexts["client-timeout"]({ lastReceivedTime: state * 1000 }, "fpre2", "fpre2"); | |
g_NetworkWarningTexts["client-latency"]({ meanRTT: 512 }, "fpre2", "fpre2"); | |
hitted[state] = true; | |
} | |
let now = Date.now(); | |
let tickLength = now - g_LastTickTime; | |
g_LastTickTime = now; | |
handleNetMessages(); | |
handleNetLobbyMessagesInBackground(); | |
updateCursorAndTooltip(); | |
if (g_Selection.dirty) | |
{ | |
g_Selection.dirty = false; | |
// When selection changed, get the entityStates of new entities | |
GetMultipleEntityStates(g_Selection.toList().filter(entId => !g_EntityStates[entId])); | |
updateGUIObjects(); | |
// Display rally points for selected buildings | |
if (Engine.GetPlayerID() != -1) | |
Engine.GuiInterfaceCall("DisplayRallyPoint", { "entities": g_Selection.toList() }); | |
} | |
else if (g_ShowAllStatusBars && now % g_StatusBarUpdate <= tickLength) | |
recalculateStatusBarDisplay(); | |
updateTimers(); | |
updateMenuPosition(tickLength); | |
// When training is blocked, flash population (alternates color every 500msec) | |
Engine.GetGUIObjectByName("resourcePop").textcolor = g_IsTrainingBlocked && now % 1000 < 500 ? g_PopulationAlertColor : g_DefaultPopulationColor; | |
Engine.GuiInterfaceCall("ClearRenamedEntities"); | |
updateObserver(); | |
if (g_TeamInfoGUI && !g_TeamInfoGUI.hidden) | |
{ | |
g_TeamInfoTimeLength += tickLength; | |
if (g_TeamInfoTimeLength >= g_TeamInfoTimeUpdate) | |
{ | |
g_TeamInfoTimeLength = 0; | |
teamInfo(); | |
} | |
} | |
else | |
g_TeamInfoTimeLength = g_TeamInfoTimeUpdate; | |
if (now - g_AutoEqualizeRequestResourcesToAllies.lastUpdate > g_AutoEqualizeRequestResourcesToAllies.updateTime * 1000) | |
{ | |
if (!g_AutoEqualizeRequestResourcesToAllies.autoEqualizeTriggered) | |
if (Engine.ConfigDB_GetValue("user", "session.autoequalizeallyresources") == "true") | |
{ | |
g_AutoEqualizeRequestResourcesToAllies.autoEqualizeTriggered = levelResWithAllies(false, true); | |
} | |
else if (!g_AutoEqualizeRequestResourcesToAllies.autoRequestTriggered && Engine.ConfigDB_GetValue("user", "session.autorequestallyresources") == "true") | |
{ | |
g_AutoEqualizeRequestResourcesToAllies.autoRequestTriggered = levelResWithAllies(true, true); | |
} | |
g_AutoEqualizeRequestResourcesToAllies.lastUpdate = now; | |
} | |
} | |
function onWindowResized() | |
{ | |
// Update followPlayerLabel | |
updateTopPanel(); | |
resizeChatWindow(); | |
} | |
function changeGameSpeed(speed) | |
{ | |
// if (!g_IsNetworked) | |
if (!g_IsNetworked || g_IsController) | |
Engine.SetSimRate(speed); | |
} | |
function updateIdleWorkerButton() | |
{ | |
Engine.GetGUIObjectByName("idleWorkerButton").enabled = Engine.GuiInterfaceCall("HasIdleUnits", { | |
"viewedPlayer": g_ViewedPlayer, | |
"idleClasses": g_WorkerTypes, | |
"excludeUnits": [] | |
}); | |
} | |
function onSimulationUpdate() | |
{ | |
// Templates change depending on technologies and auras, so they have to be reloaded after such a change. | |
// g_TechnologyData data never changes, so it shouldn't be deleted. | |
g_EntityStates = {}; | |
if (Engine.GuiInterfaceCall("IsTemplateModified")) | |
{ | |
g_TemplateData = {}; | |
Engine.GuiInterfaceCall("ResetTemplateModified"); | |
} | |
g_SimState = undefined; | |
if (!GetSimState()) | |
return; | |
GetMultipleEntityStates(g_Selection.toList()); | |
updateCinemaPath(); | |
handleNotifications(); | |
updateGUIObjects(); | |
if (g_ConfirmExit) | |
confirmExit(); | |
} | |
/** | |
* Don't show the message box before all playerstate changes are processed. | |
*/ | |
function confirmExit() | |
{ | |
if (g_IsNetworked && !g_IsNetworkedActive) | |
return; | |
closeOpenDialogs(); | |
// Don't ask for exit if other humans are still playing | |
let askExit = !Engine.HasNetServer() || g_Players.every((player, i) => | |
i == 0 || | |
player.state != "active" || | |
g_GameAttributes.settings.PlayerData[i].AI != ""); | |
let subject = g_PlayerStateMessages[g_ConfirmExit]; | |
if (askExit) | |
subject += "\n" + translate("Do you want to quit?"); | |
messageBox( | |
400, 200, | |
subject, | |
g_ConfirmExit == "won" ? | |
translate("VICTORIOUS!") : | |
translate("DEFEATED!"), | |
askExit ? [translate("No"), translate("Yes")] : [translate("OK")], | |
askExit ? [resumeGame, leaveGame] : [resumeGame] | |
); | |
g_ConfirmExit = false; | |
} | |
function toggleGUI() | |
{ | |
g_ShowGUI = !g_ShowGUI; | |
updateCinemaPath(); | |
} | |
function updateCinemaPath() | |
{ | |
let isPlayingCinemaPath = GetSimState().cinemaPlaying && !g_Disconnected; | |
Engine.GetGUIObjectByName("session").hidden = !g_ShowGUI || isPlayingCinemaPath; | |
Engine.Renderer_SetSilhouettesEnabled(!isPlayingCinemaPath && Engine.ConfigDB_GetValue("user", "silhouettes") == "true"); | |
} | |
function updateGUIObjects() | |
{ | |
g_Selection.update(); | |
if (g_ShowAllStatusBars) | |
recalculateStatusBarDisplay(); | |
if (g_ShowGuarding || g_ShowGuarded) | |
updateAdditionalHighlight(); | |
updatePanelEntities(); | |
displayPanelEntities(); | |
updateGroups(); | |
updateDebug(); | |
updatePlayerDisplay(); | |
updateResearchDisplay(); | |
updateSelectionDetails(); | |
updateBuildingPlacementPreview(); | |
updateTimeNotifications(); | |
updateIdleWorkerButton(); | |
if (g_IsTradeOpen) | |
{ | |
updateTraderTexts(); | |
updateBarterButtons(); | |
} | |
if (g_ViewedPlayer > 0) | |
{ | |
let playerState = GetSimState().players[g_ViewedPlayer]; | |
g_DevSettings.controlAll = playerState && playerState.controlsAll; | |
Engine.GetGUIObjectByName("devControlAll").checked = g_DevSettings.controlAll; | |
} | |
if (!g_IsObserver) | |
{ | |
// Update music state on basis of battle state. | |
let battleState = Engine.GuiInterfaceCall("GetBattleState", g_ViewedPlayer); | |
if (battleState) | |
global.music.setState(global.music.states[battleState]); | |
} | |
updateViewedPlayerDropdown(); | |
updateDiplomacy(false); | |
} | |
function onReplayFinished() | |
{ | |
closeOpenDialogs(); | |
pauseGame(); | |
messageBox(400, 200, | |
translateWithContext("replayFinished", "The replay has finished. Do you want to quit?"), | |
translateWithContext("replayFinished", "Confirmation"), | |
[translateWithContext("replayFinished", "No"), translateWithContext("replayFinished", "Yes")], | |
[resumeGame, leaveGame]); | |
} | |
/** | |
* updates a status bar on the GUI | |
* nameOfBar: name of the bar | |
* points: points to show | |
* maxPoints: max points | |
* direction: gets less from (right to left) 0; (top to bottom) 1; (left to right) 2; (bottom to top) 3; | |
*/ | |
function updateGUIStatusBar(nameOfBar, points, maxPoints, direction) | |
{ | |
// check, if optional direction parameter is valid. | |
if (!direction || !(direction >= 0 && direction < 4)) | |
direction = 0; | |
// get the bar and update it | |
let statusBar = Engine.GetGUIObjectByName(nameOfBar); | |
if (!statusBar) | |
return; | |
let healthSize = statusBar.size; | |
let value = 100 * Math.max(0, Math.min(1, points / maxPoints)); | |
// inverse bar | |
if (direction == 2 || direction == 3) | |
value = 100 - value; | |
if (direction == 0) | |
healthSize.rright = value; | |
else if (direction == 1) | |
healthSize.rbottom = value; | |
else if (direction == 2) | |
healthSize.rleft = value; | |
else if (direction == 3) | |
healthSize.rtop = value; | |
statusBar.size = healthSize; | |
} | |
function updatePanelEntities() | |
{ | |
let panelEnts = | |
g_ViewedPlayer == -1 ? | |
GetSimState().players.reduce((ents, pState) => ents.concat(pState.panelEntities), []) : | |
GetSimState().players[g_ViewedPlayer].panelEntities; | |
g_PanelEntities = g_PanelEntities.filter(panelEnt => panelEnts.find(ent => ent == panelEnt.ent)); | |
for (let ent of panelEnts) | |
{ | |
let panelEntState = GetEntityState(ent); | |
let template = GetTemplateData(panelEntState.template); | |
let panelEnt = g_PanelEntities.find(pEnt => ent == pEnt.ent); | |
if (!panelEnt) | |
{ | |
panelEnt = { | |
"ent": ent, | |
"tooltip": undefined, | |
"sprite": "stretched:session/portraits/" + template.icon, | |
"maxHitpoints": undefined, | |
"currentHitpoints": panelEntState.hitpoints, | |
"previousHitpoints": undefined | |
}; | |
g_PanelEntities.push(panelEnt); | |
} | |
panelEnt.tooltip = createPanelEntityTooltip(panelEntState, template); | |
panelEnt.previousHitpoints = panelEnt.currentHitpoints; | |
panelEnt.currentHitpoints = panelEntState.hitpoints; | |
panelEnt.maxHitpoints = panelEntState.maxHitpoints; | |
} | |
let panelEntIndex = ent => g_PanelEntityOrder.findIndex(entClass => | |
GetEntityState(ent).identity.classes.indexOf(entClass) != -1); | |
g_PanelEntities = g_PanelEntities.sort((panelEntA, panelEntB) => panelEntIndex(panelEntA.ent) - panelEntIndex(panelEntB.ent)); | |
} | |
function createPanelEntityTooltip(panelEntState, template) | |
{ | |
let getPanelEntNameTooltip = panelEntState => "[font=\"sans-bold-16\"]" + template.name.specific + "[/font]"; | |
return [ | |
getPanelEntNameTooltip, | |
getCurrentHealthTooltip, | |
getAttackTooltip, | |
getArmorTooltip, | |
getEntityTooltip, | |
getAurasTooltip | |
].map(tooltip => tooltip(panelEntState)).filter(tip => tip).join("\n"); | |
} | |
function displayPanelEntities() | |
{ | |
let buttons = Engine.GetGUIObjectByName("panelEntityPanel").children; | |
buttons.forEach((button, slot) => { | |
if (button.hidden || g_PanelEntities.some(ent => ent.slot !== undefined && ent.slot == slot)) | |
return; | |
button.hidden = true; | |
stopColorFade("panelEntityHitOverlay[" + slot + "]"); | |
}); | |
// The slot identifies the button, displayIndex determines its position. | |
for (let displayIndex = 0; displayIndex < Math.min(g_PanelEntities.length, buttons.length); ++displayIndex) | |
{ | |
let panelEnt = g_PanelEntities[displayIndex]; | |
// Find the first unused slot if new, otherwise reuse previous. | |
let slot = panelEnt.slot === undefined ? | |
buttons.findIndex(button => button.hidden) : | |
panelEnt.slot; | |
let panelEntButton = Engine.GetGUIObjectByName("panelEntityButton[" + slot + "]"); | |
panelEntButton.tooltip = panelEnt.tooltip; | |
updateGUIStatusBar("panelEntityHealthBar[" + slot + "]", panelEnt.currentHitpoints, panelEnt.maxHitpoints); | |
if (panelEnt.slot === undefined) | |
{ | |
let panelEntImage = Engine.GetGUIObjectByName("panelEntityImage[" + slot + "]"); | |
panelEntImage.sprite = panelEnt.sprite; | |
panelEntButton.hidden = false; | |
panelEnt.slot = slot; | |
} | |
// If the health of the panelEnt changed since the last update, trigger the animation. | |
if (panelEnt.previousHitpoints > panelEnt.currentHitpoints) | |
startColorFade("panelEntityHitOverlay[" + slot + "]", 100, 0, | |
colorFade_attackUnit, true, smoothColorFadeRestart_attackUnit); | |
// TODO: Instead of instant position changes, animate button movement. | |
setPanelObjectPosition(panelEntButton, displayIndex, buttons.length); | |
} | |
} | |
function updateGroups() | |
{ | |
g_Groups.update(); | |
// Determine the sum of the costs of a given template | |
let getCostSum = (ent) => { | |
let cost = GetTemplateData(GetEntityState(ent).template).cost; | |
return cost ? Object.keys(cost).map(key => cost[key]).reduce((sum, cur) => sum + cur) : 0; | |
}; | |
for (let i in Engine.GetGUIObjectByName("unitGroupPanel").children) | |
{ | |
Engine.GetGUIObjectByName("unitGroupLabel[" + i + "]").caption = i; | |
let button = Engine.GetGUIObjectByName("unitGroupButton[" + i + "]"); | |
button.hidden = g_Groups.groups[i].getTotalCount() == 0; | |
button.onpress = (function(i) { return function() { performGroup((Engine.HotkeyIsPressed("selection.add") ? "add" : "select"), i); }; })(i); | |
button.ondoublepress = (function(i) { return function() { performGroup("snap", i); }; })(i); | |
button.onpressright = (function(i) { return function() { performGroup("breakUp", i); }; })(i); | |
// Chose icon of the most common template (or the most costly if it's not unique) | |
if (g_Groups.groups[i].getTotalCount() > 0) | |
{ | |
let icon = GetTemplateData(GetEntityState(g_Groups.groups[i].getEntsGrouped().reduce((pre, cur) => { | |
if (pre.ents.length == cur.ents.length) | |
return getCostSum(pre.ents[0]) > getCostSum(cur.ents[0]) ? pre : cur; | |
return pre.ents.length > cur.ents.length ? pre : cur; | |
}).ents[0]).template).icon; | |
Engine.GetGUIObjectByName("unitGroupIcon[" + i + "]").sprite = | |
icon ? ("stretched:session/portraits/" + icon) : "groupsIcon"; | |
} | |
setPanelObjectPosition(button, i, 1); | |
} | |
} | |
function updateDebug() | |
{ | |
let debug = Engine.GetGUIObjectByName("debugEntityState"); | |
if (!Engine.GetGUIObjectByName("devDisplayState").checked) | |
{ | |
debug.hidden = true; | |
return; | |
} | |
debug.hidden = false; | |
let conciseSimState = clone(GetSimState()); | |
conciseSimState.players = "<<<omitted>>>"; | |
let text = "simulation: " + uneval(conciseSimState); | |
let selection = g_Selection.toList(); | |
if (selection.length) | |
{ | |
let entState = GetEntityState(selection[0]); | |
if (entState) | |
{ | |
let template = GetTemplateData(entState.template); | |
text += "\n\nentity: {\n"; | |
for (let k in entState) | |
text += " " + k + ":" + uneval(entState[k]) + "\n"; | |
text += "}\n\ntemplate: " + uneval(template); | |
} | |
} | |
debug.caption = text.replace(/\[/g, "\\["); | |
} | |
var g_DefaultTechs = {}; | |
function teamInfo() | |
{ | |
// if (!g_IsObserver) | |
// for (let player in playerStates) | |
// { | |
// let team = playerStates[player].teamsLocked ? g_Players[player].team + 1 : 0; | |
if (false && Engine.ConfigDB_GetValue("user", "session.showstats") != "true") | |
{ | |
Engine.GetGUIObjectByName("teamInfo").hidden = true; | |
return; | |
} | |
// let playerStates = GetSimState().players; | |
let playerStates = getExtendedSimState().players; | |
// print("\n\n" + uneval(playerStates[1].classCounts)); | |
// print("\n\n" + | |
// uneval(playerStates[2].classCounts.Cavalry) + | |
// uneval(playerStates[2].classCounts.Infantry) | |
// ); | |
if (!playerStates) | |
return; //warn(!playerStates[g_ViewedPlayer]) | |
// warn(g_ViewedPlayer) | |
if (g_TeamInfoState == 3) // || (!g_IsObserver || playerStates[g_ViewedPlayer] && (!playerStates[g_ViewedPlayer].hasSharedLos || g_Players[g_ViewedPlayer].isMutualAlly.reduce((a, b) => a + b, 0) <= 1))) | |
{ | |
Engine.GetGUIObjectByName("teamInfo").hidden = true; | |
return; | |
} | |
Engine.GetGUIObjectByName("teamInfo").hidden = false; | |
let tool = ""; | |
let highest, smallest, resAm = 0; | |
const resses = [ "phase", "pop", "inf", "cav", "siege", "food", "wood", "stone", "metal", "tech", "uk"]; | |
let plHighest = {}; | |
let plSmallest = {}; | |
let uk = {}; | |
let phase = { "city": 3, "village": 1, "town": 2}; | |
let units = []; | |
let unitClass = ["Cavalry", "Infantry", "Siege"]; | |
g_Players.forEach((o,pl) => { | |
units[pl] = {}; | |
for (let i of unitClass) | |
units[pl][i] = 0; | |
// Engine.SetViewedPlayer(pl); | |
// let playerStates = Engine.GuiInterfaceCall("GetPlayerEntitiesView", pl); | |
// units[pl] = Engine.GuiInterfaceCall("GetPlayerEntitiesView", pl); | |
// playerStates.forEach(e => { e.forEach(c => { | |
// for (let i of unitClass) | |
// if (c == i) | |
// ++units[pl][c]; | |
// })}); | |
// playerStates.forEach(e => Engine.GuiInterfaceCall("GetEntityState", e)); | |
// playerStates.forEach(e => Engine.GuiInterfaceCall("GetEntityState", e).identity.visibleClasses.forEach(c => { | |
// for (let i of unitClass) | |
// if (c == i) | |
// ++units[pl][c]; | |
// })); | |
}); | |
let filteredPlayerStates = playerStates.map((stat,pl) => { stat.pl = pl; | |
stat.sieges_eles = stat.classCounts && stat.classCounts.Siege ? stat.classCounts.Siege : 0; | |
stat.sieges_eles += stat.classCounts && stat.classCounts.Elephant ? stat.classCounts.Elephant : 0; | |
// print("\n\n" + (stat.classCounts && stat.classCounts.Elephant ? uneval(stat.classCounts.Elephant) : "")); | |
return stat; }).filter((stat, pl) => { | |
if (g_IsObserver || !g_Players[g_ViewedPlayer] || pl == g_ViewedPlayer) | |
return true; | |
if (!playerStates[g_ViewedPlayer].hasSharedLos || !g_Players[g_ViewedPlayer].isMutualAlly[pl]) | |
return false; | |
return true; | |
}); | |
// print(uneval(Object.keys(filteredPlayerStates[0]))) | |
for (let res of resses) | |
{ | |
plHighest[res] = {}; | |
highest = 0; | |
plSmallest[res] = {}; | |
smallest = 9999; | |
filteredPlayerStates.forEach((stat) => { | |
let pl = stat.pl; | |
uk[pl] = 0; | |
if (!g_IsObserver && (!g_Players[g_ViewedPlayer] || !g_Players[g_ViewedPlayer].isMutualAlly[pl])) | |
return; | |
if (pl > 0) | |
{ | |
// let kill = stat.sequences.enemyUnitsKilled.total; | |
// for (let o in kill) | |
uk[pl] = stat.sequences.enemyUnitsKilled.total[stat.sequences.enemyUnitsKilled.total.length-1]; //.reduce((a, b) => a + b, 0); | |
// warn(uneval(stat.sequences.unitsTrained.Infantry[stat.sequences.unitsTrained.Infantry.length-1] - stat.sequences.unitsLost.Infantry[stat.sequences.unitsLost.Infantry.length-1])) | |
// warn(uneval(Object.keys(stat.sequences))); //.unitsTrained.Infantry[stat.sequences.unitsTrained.Infantry.length-1] - stat.sequences.unitsLost.Infantry[stat.sequences.unitsLost.Infantry.length-1])) | |
} | |
// if (! pl in g_DefaultTechs) | |
// g_DefaultTechs[pl] = pl > 0 ? [...stat.researchedTechs].length : 0; | |
// print("\n\n" + uneval(stat.researchedTechs ? [...stat.researchedTechs] : "")); | |
if (res == "tech") | |
resAm = pl > 0 ? [...stat.researchedTechs].filter(t => t.indexOf("pair") == -1 && t.indexOf("bonus") == -1 && t.indexOf("phase") == -1).length: 0; | |
else if (res == "cav") | |
resAm = stat.classCounts && stat.classCounts.Cavalry ? stat.classCounts.Cavalry : 0; | |
else if (res == "inf") | |
resAm = stat.classCounts && stat.classCounts.Infantry ? stat.classCounts.Infantry : 0; | |
else if (res == "siege") | |
resAm = stat.sieges_eles; | |
else | |
resAm = (res == "pop") ? stat.popCount : (res == "uk") ? uk[pl] : (res == "phase") ? (!!stat.phase ? phase[stat.phase] : 0) : Math.round(stat.resourceCounts[res]); | |
if (pl != 0) { | |
if (resAm > highest) | |
{ | |
plHighest[res] = {}; | |
plHighest[res][pl] = true; | |
highest = resAm; | |
} | |
else if (resAm == highest) | |
{ | |
plHighest[res][pl] = true; | |
} | |
if (resAm < smallest) | |
{ | |
plSmallest[res] = {}; | |
plSmallest[res][pl] = true; | |
smallest = resAm; | |
} | |
else if (resAm == smallest) | |
{ | |
plSmallest[res][pl] = true; | |
} | |
} | |
stat.id = pl; | |
}); | |
} | |
// "values": resource == "pop" ? [ playerStates[player].popCount, playerStates[player].popLimit, playerStates[player].popMax ] : | |
// [ Math.round(playerStates[player].resourceCounts[resource]) ] | |
let f = (s,l) => { let a = l - s.length; for (let i=0; i < a; i++) s = " " + s; return s.substring(0,l); }; | |
let f2 = s => f(s,6); | |
//filter((stat,pl) => g_Players[pl].cheatsEnabled || g_IsObserver || | |
//g_Players[g_ViewedPlayer].isMutualAlly[pl] ). | |
let showTeamsNumbers = playerStates[0].teamsLocked; | |
let infoStringColor = "255 255 255"; | |
tool += filteredPlayerStates.map((stat) => { | |
let pl = stat.pl; | |
let playerColor = pl > -1 ? rgbToGuiColor(g_DisplayedPlayerColors[pl]) : "white"; | |
let add = "" | |
add += (pl == g_ViewedPlayer ? setStringTags("> ", { "color" : "white" || "120 120 255" }) : ""); | |
add += | |
setStringTags(g_Players[pl].name.substring(0,15 - (showTeamsNumbers ? 3 : 0)), { "color": pl == g_ViewedPlayer ? playerColor : g_Players[pl].state == "defeated" ? "255 255 255 128" : playerColor }) + " " + setStringTags("■", { "color": playerColor }) ; | |
add += setStringTags(" " + pl, { "color": infoStringColor }); | |
add += setStringTags(showTeamsNumbers ? " " + (g_Players[pl].team + 1) : "", { "color": infoStringColor }); | |
add += "" | |
// warn(uneval([...stat.researchedTechs].filter(t => ["pair", "bonus", "phase", "wooden_walls", "civpenalty"].every(s => t.indexOf(s) == -1 )))); | |
for (let res of resses) | |
{ | |
if (Object.keys(plHighest[res]).length == filteredPlayerStates.length) | |
{ | |
plHighest[res] = {}; | |
plSmallest[res] = {}; | |
} | |
if (res == "tech") | |
resAm = f(""+(pl > 0 ? [...stat.researchedTechs].filter(t => ["pair", "bonus", "phase", "wooden_walls", "civpenalty"].every(s => t.indexOf(s) == -1 )).length : 0),3); | |
else if (res == "cav") | |
resAm = f(""+(stat.classCounts && stat.classCounts.Cavalry ? stat.classCounts.Cavalry : 0),4); | |
else if (res == "inf") | |
resAm = f(""+(stat.classCounts && stat.classCounts.Infantry ? stat.classCounts.Infantry : 0),4); | |
else if (res == "siege") | |
resAm = f(""+(stat.sieges_eles),4); | |
else | |
resAm = (res == "pop") ? f(""+stat.popCount,4) : (res == "uk") ? f2(""+uk[pl]) : (res == "phase") ? " " + (!!stat.phase ? phase[stat.phase] : 0) : f2(""+Math.round(stat.resourceCounts[res])); | |
add += setStringTags(resAm, { "color": plHighest[res][pl] ? "255 255 0 255" : plSmallest[res][pl] ? "255 120 120 255" : "255 255 255 255" }) | |
} | |
// warn(uneval(pl > 0 ?stat.researchedTechs.forEach((a,b) => warn(a + " " +b)):0)); | |
return add + " "; | |
// + setStringTags(" 520", { "color": "255 250 40 255" }) | |
// + setStringTags(" 230", { "color": "255 255 180 255" }) | |
// + setStringTags(" 100", { "color": "255 255 180 255" }) | |
// + setStringTags(" 120", { "color": "255 255 180 255" }) | |
// + setStringTags(" 2300\n", { "color": "255 255 180 255" }); | |
}).join("\n"); | |
// playerStates.map((pl) => { | |
// tool += setStringTags("1.", { "color": "0 0 255 255" }) + " fpre" | |
// + setStringTags(" 100", { "color": "255 255 180 255" }) | |
// + setStringTags(" 520", { "color": "255 250 40 255" }) | |
// + setStringTags(" 230", { "color": "255 255 180 255" }) | |
// + setStringTags(" 100", { "color": "255 255 180 255" }) | |
// + setStringTags(" 120", { "color": "255 255 180 255" }) | |
// + setStringTags(" 2300\n", { "color": "255 255 180 255" }); | |
Engine.GetGUIObjectByName("teamInfo").caption = setStringTags(escapeText("") + | |
(g_ViewedPlayer == -1 ? setStringTags("", { "color" : "255 255 255" }) : "" ) + | |
setStringTags("Player ■", { "color" : g_ViewedPlayer == -1 ? "210 210 210" : "210 210 210" }) + | |
setStringTags(" # T P Pop Inf Cav Sie Food Wood Stone Metal Te Ki \n", { "color": "210 210 210", "font": "mono-10" }) + tool, { "color": "255 255 255 255", "font": "mono-10" }); | |
let x = (filteredPlayerStates.length+1)*12+7; | |
Engine.GetGUIObjectByName("teamInfo").size = "100%-448 100%-228-"+x+" 100% 100%-228"; | |
} | |
/** | |
* Create ally player stat tooltip. | |
* @param {string} resource - Resource type, on which values will be sorted. | |
* @param {object} playerStates - Playerstates from players whos stats are viewed in the tooltip. | |
* @param {number} sort - 0 no order, -1 descending, 1 ascending order. | |
* @returns {string} Tooltip string. | |
*/ | |
function getAllyStatTooltip(resource, playerStates, sort, color) | |
{ | |
let teamStats = {}; | |
for (let player in playerStates) | |
{ | |
let team = playerStates[player].teamsLocked ? g_Players[player].team + 1 : 0; | |
let playerStat = { | |
"playerId": player, | |
"playercolor": colorizePlayernameHelper("■", player), | |
"playername": colorizePlayernameHelper(g_Players[player].name, player), | |
"values": resource == "pop" ? [ playerStates[player].popCount, playerStates[player].popLimit, playerStates[player].popMax ] : | |
[ Math.round(playerStates[player].resourceCounts[resource]) ] | |
}; | |
if (!teamStats[team]) | |
teamStats[team] = { "number": team, "players": [], "sum": Array(playerStat.values.length).fill(0), "locked": playerStates[player].teamsLocked }; | |
teamStats[team].players.push(playerStat); | |
playerStat.values.forEach((value, i) => teamStats[team].sum[i] += value); | |
} | |
//playerValuesDesigned[0] = | |
let values= { | |
"highest": "notsetted", | |
"smallest": "notsetted", | |
"rank": {} | |
}; | |
let allNumbersEqual = true; | |
return "\n" + Object.keys(teamStats).map(team => { return teamStats[team]; }).sort((a, b) => sort * (b.sum[0] - a.sum[0])).map(team => | |
setStringTags((team.locked && (g_IsObserver || playerStates[g_ViewedPlayer].hasSharedLos) ? | |
(team.number != 0 ? | |
sprintf(translate("\n# %(teamSum)s %(team)s\n"), { | |
"team": setStringTags((g_IsObserver ? "Team " + team.number : "Team"), { "color": "180 180 180" }), | |
"teamSum": setStringTags(team.sum.map((o,i) => i==0 ? o : o).join(translateWithContext("seperator", "/")), { | |
"font": "sans-bold-14", "color": "180 180 180" }) | |
}) : | |
translate("No Team\n")) | |
: ""), { "color": "180 180 180 255" }) + | |
team.players.sort((a, b) => sort * (b.values[0] - a.values[0])).map( | |
(player, pl) => { | |
let stat = player.values[0]; | |
if (values.highest == "notsetted" || stat >= +values.highest) | |
{ | |
if (+values.highest < stat) | |
{ | |
allNumbersEqual = false; | |
if (+values.highest < +values.smallest || values.smallest == "notsetted") | |
{ | |
values.smallest = +values.highest; | |
} | |
} | |
values.highest = +stat; | |
} | |
else | |
if (values.smallest == "notsetted" || stat <= +values.smallest) | |
{ | |
allNumbersEqual = false; | |
values.smallest = +stat; | |
} | |
return player; | |
} | |
).map((player,pl) => { | |
// let playerValuesDesigned = player.values.slice().map((o,i) => i>0 ? setStringTags(o, { "font": "sans-12" }) : o); | |
return setStringTags((pl+1)+".", { "color": player.playerId == g_ViewedPlayer ? "255 255 0": "180 180 180" }) + " " + sprintf(translate("%(values)s %(playercolor)s %(playername)s" + setStringTags(" <", { "color": "255 255 0" + (player.playerId == g_ViewedPlayer ? " 0" : " 0") })), { | |
"playername": setStringTags(player.playername, { "font": "sans-bold-14" }), | |
"playercolor": player.playercolor, | |
"values": player.values.map((o,i) => setStringTags(o, { "font": "sans-bold-14", "color": i==0 ? "white" : "210 210 210" | |
//allNumbersEqual ? "255 255 255" : o == values.highest ? "white" : o == values.smallest ? "white" : "white" | |
})).join(translateWithContext("seperator", "/" )) | |
}); } | |
).join("\n") | |
).join("\n"); | |
} | |
function getViewablePlayerStates(allPlayerStates, includeViewedPlayer = true) | |
{ | |
let viewablePlayerStates = {}; | |
let alliedPlayerStates = g_ViewedPlayer > 0 ? [allPlayerStates[g_ViewedPlayer]] : | |
g_ViewedPlayer == -1 ? allPlayerStates.filter((stat, playerId) => playerId != 0) : []; | |
for (let player in allPlayerStates) | |
if (player != 0 && | |
g_Players[player].state != "defeated" && | |
(g_IsObserver || | |
alliedPlayerStates[0].hasSharedLos && (includeViewedPlayer || g_ViewedPlayer != player) && | |
g_Players[player].isMutualAlly[g_ViewedPlayer])) | |
viewablePlayerStates[player] = allPlayerStates[player]; | |
return viewablePlayerStates; | |
} | |
var g_LastShareMsgSend = {}; | |
function levelResWithAllies(onlyRequest = false, autoChecked = false) | |
{ | |
if (isPlayerObserver(Engine.GetPlayerID())) | |
return; | |
let allPlayerStates = GetSimState().players; | |
let viewedPlayer = allPlayerStates[g_ViewedPlayer]; | |
if (!viewedPlayer.hasSharedLos) | |
warn("You can use auto resource share among allies, if you have share allies vision technology (Cartography) researched."); | |
let alliedPlayerStates = getViewablePlayerStates(allPlayerStates, false); | |
let resCodes = g_ResourceData.GetCodes(); | |
// let g_LastShareMsgSendUpdated = Object.assign({}, g_LastShareMsgSend); | |
let sendPlayerRequest = {}; | |
let teamPlayerAmount = Object.keys(alliedPlayerStates).length + 1; | |
// warn(teamPlayerAmount) | |
// | |
// TODO: Sort resources by player and send after calculated all res for better view in chat | |
// | |
for (let res of resCodes) | |
{ | |
let resTotal = Math.floor(viewedPlayer.resourceCounts[res]); | |
let playersToSend = []; | |
for (let player in alliedPlayerStates) | |
{ | |
if (alliedPlayerStates[player].state == "defeated") | |
continue; | |
if (Math.floor(viewedPlayer.resourceCounts[res]) > Math.floor(alliedPlayerStates[player].resourceCounts[res])) | |
{ | |
playersToSend.push(player); | |
resTotal += Math.floor(alliedPlayerStates[player].resourceCounts[res]) | |
} | |
else if (g_IsNetworked && (!g_LastShareMsgSend[player] || (Date.now() - g_LastShareMsgSend[player] )/1000 >5)) | |
{ | |
// warn(viewedPlayer.resourceCounts[res] / alliedPlayerStates[player].resourceCounts[res]) | |
if (viewedPlayer.resourceCounts[res] / alliedPlayerStates[player].resourceCounts[res] < 1/teamPlayerAmount) | |
{ | |
if (!sendPlayerRequest[player]) | |
sendPlayerRequest[player] = []; | |
sendPlayerRequest[player].push(g_ResourceData.GetNames()[res] + ": " + (Math.floor((((alliedPlayerStates[player].resourceCounts[res] + viewedPlayer.resourceCounts[res]) / teamPlayerAmount) - viewedPlayer.resourceCounts[res])/100)*100)); | |
} | |
} | |
} | |
if (!onlyRequest) | |
for (let player of playersToSend) | |
{ | |
// let amount = Math.floor(resTotal / (playersToSend.length + 1)) - | |
// Math.floor(alliedPlayerStates[player].resourceCounts[res]); | |
// Math.floor(x / 100) * 100 round down to hundreds. | |
let amount = Math.floor((resTotal / (playersToSend.length + 1) - | |
alliedPlayerStates[player].resourceCounts[res]) / 100) * 100; | |
if (amount > 0) | |
{ | |
let equalizedResToAll = { [res]: amount }; | |
Engine.PostNetworkCommand({ "type": "tribute", "player": player, "amounts": equalizedResToAll }); | |
} | |
} | |
} | |
if (autoChecked && !Object.keys(sendPlayerRequest).length) | |
return false; | |
if (!Object.keys(sendPlayerRequest).length && !autoChecked) | |
warn("No Resources Requests from Team Members possible."); | |
for (let player in sendPlayerRequest) | |
{ | |
// warn("send name "+ alliedPlayerStates[player].name) | |
// Engine.SendNetworkChat("/msg " + alliedPlayerStates[player].name + " Request Ally Resource Equalize: " + sendPlayerRequest[player].join(", ") + ". (fgod: Alt+Shift+E"); | |
// Engine.SendNetworkChat("/msg " + alliedPlayerStates[player].name + " I'm more than " + ((1/teamPlayerAmount)*100) + "% lower in some resources than you! Can you use your Resources or equalize them to me? For team per player portional equalize send:\n" + sendPlayerRequest[player].join(", ") + ". ([Alt + Shift + E] - fgod ally-auto-resource-equalize-hotkey.)"); | |
Engine.SendNetworkChat("/msg " + alliedPlayerStates[player].name + " Request Resource Equalize (I have < " + ((1/teamPlayerAmount)*100) + "% of follow of yours). To Equalize send me: " + sendPlayerRequest[player].join(", ") + "."); | |
g_LastShareMsgSend[player] = Date.now(); | |
} | |
return true; | |
} | |
function updatePlayerDisplay() | |
{ | |
let allPlayerStates = GetSimState().players; | |
let viewedPlayerStates = g_ViewedPlayer > 0 ? [allPlayerStates[g_ViewedPlayer]] : | |
g_ViewedPlayer == -1 ? allPlayerStates.filter((stat, playerId) => playerId != 0) : []; | |
if (!viewedPlayerStates.length) | |
return; | |
let viewablePlayerStates = getViewablePlayerStates(allPlayerStates); | |
let tooltipSort = +Engine.ConfigDB_GetValue("user", "gui.session.respoptooltipsort"); | |
let orderHotkeyTooltip = Object.keys(viewablePlayerStates).length <= 1 ? "" : | |
"\n" + sprintf(translate("%(order)s: %(hotkey)s to change order."), { | |
"hotkey": setStringTags("\\[Click]", g_HotkeyTags), | |
"order": tooltipSort == 0 ? translate("Fixed") : tooltipSort == 1 ? translate("Descending") : translate("Ascending") | |
}); | |
let color = g_Res_bright_color; | |
g_DefaultPopulationColor = color["pop"]; | |
let resCodes = g_ResourceData.GetCodes(); | |
for (let r = 0; r < resCodes.length; ++r) | |
{ | |
let resourceObj = Engine.GetGUIObjectByName("resource[" + r + "]"); | |
if (!resourceObj) | |
break; | |
let res = resCodes[r]; | |
let tooltip = setStringTags('[font="' + g_ResourceTitleFont + '"]' + | |
resourceNameFirstWord(res) + '[/font]', { "color": color[res] || "255 255 128" }); // | |
let descr = g_ResourceData.GetResource(res).description; | |
if (descr) | |
tooltip += "\n" + translate(descr); | |
tooltip += orderHotkeyTooltip + getAllyStatTooltip(res, viewablePlayerStates, tooltipSort, color[res]); | |
resourceObj.tooltip = tooltip; | |
Engine.GetGUIObjectByName("resource[" + r + "]_count").caption = setStringTags(Math.floor( | |
viewedPlayerStates.map(playerState => playerState.resourceCounts[res]).reduce((total, resourceCount) => total + resourceCount, 0) | |
), { "color": color[res] || "white" }); | |
} | |
Engine.GetGUIObjectByName("resourcePop").caption = //setStringTags( | |
sprintf(translate( | |
"%(popCount)s/%(popLimit)s"), | |
viewedPlayerStates.reduce( | |
(sprintfData, playerState) => { | |
sprintfData.popCount += playerState.popCount; | |
sprintfData.popLimit += playerState.popLimit; | |
return sprintfData; | |
}, | |
{ "popCount": 0, "popLimit": 0 } | |
)); //, { "color": color["pop"] || "white" }); | |
let tooltip = setStringTags('[font="' + g_ResourceTitleFont + '"]'+ translate("Population") + '[/font]', { "color": color["pop"] || "255 255 128" }) + " (current/limit)" + "\n"; | |
Engine.GetGUIObjectByName("population").tooltip = tooltip + | |
sprintf(translate("Maximum population: %(popCap)s"), { | |
"popCap": viewedPlayerStates.map(playerState => playerState.popMax).reduce((totalPopMax, popMax) => totalPopMax + popMax, 0) | |
}) + | |
orderHotkeyTooltip + | |
getAllyStatTooltip("pop", viewablePlayerStates, tooltipSort, color["pop"]); | |
g_IsTrainingBlocked = g_ViewedPlayer > 0 && viewedPlayerStates[0].trainingBlocked; | |
} | |
function selectAndMoveTo(ent) | |
{ | |
let entState = GetEntityState(ent); | |
if (!entState || !entState.position) | |
return; | |
g_Selection.reset(); | |
g_Selection.addList([ent]); | |
let position = entState.position; | |
Engine.CameraMoveTo(position.x, position.z); | |
} | |
function updateResearchDisplay() | |
{ | |
let researchStarted = Engine.GuiInterfaceCall("GetStartedResearch", g_ViewedPlayer); | |
// Set up initial positioning. | |
let buttonSideLength = Engine.GetGUIObjectByName("researchStartedButton[0]").size.right; | |
for (let i = 0; i < 10; ++i) | |
{ | |
let button = Engine.GetGUIObjectByName("researchStartedButton[" + i + "]"); | |
let size = button.size; | |
size.top = g_ResearchListTop + (4 + buttonSideLength) * i; | |
size.bottom = size.top + buttonSideLength; | |
button.size = size; | |
} | |
let numButtons = 0; | |
for (let tech in researchStarted) | |
{ | |
// Show at most 10 in-progress techs. | |
if (numButtons >= 10) | |
break; | |
let template = GetTechnologyData(tech, g_Players[g_ViewedPlayer].civ); | |
let button = Engine.GetGUIObjectByName("researchStartedButton[" + numButtons + "]"); | |
button.hidden = false; | |
button.tooltip = getEntityNames(template); | |
button.onpress = (function(e) { return function() { selectAndMoveTo(e); }; })(researchStarted[tech].researcher); | |
let icon = "stretched:session/portraits/" + template.icon; | |
Engine.GetGUIObjectByName("researchStartedIcon[" + numButtons + "]").sprite = icon; | |
// Scale the progress indicator. | |
let size = Engine.GetGUIObjectByName("researchStartedProgressSlider[" + numButtons + "]").size; | |
// Buttons are assumed to be square, so left/right offsets can be used for top/bottom. | |
size.top = size.left + Math.round(researchStarted[tech].progress * (size.right - size.left)); | |
Engine.GetGUIObjectByName("researchStartedProgressSlider[" + numButtons + "]").size = size; | |
Engine.GetGUIObjectByName("researchStartedTimeRemaining[" + numButtons + "]").caption = | |
Engine.FormatMillisecondsIntoDateStringGMT(researchStarted[tech].timeRemaining, translateWithContext("countdown format", "m:ss")); | |
++numButtons; | |
} | |
// Hide unused buttons. | |
for (let i = numButtons; i < 10; ++i) | |
Engine.GetGUIObjectByName("researchStartedButton[" + i + "]").hidden = true; | |
} | |
/** | |
* Toggles the display of status bars for all of the player's entities. | |
* | |
* @param {Boolean} remove - Whether to hide all previously shown status bars. | |
*/ | |
function recalculateStatusBarDisplay(remove = false) | |
{ | |
let entities; | |
if (g_ShowAllStatusBars && !remove) | |
entities = g_ViewedPlayer == -1 ? | |
Engine.PickNonGaiaEntitiesOnScreen() : | |
Engine.PickPlayerEntitiesOnScreen(g_ViewedPlayer); | |
else | |
{ | |
let selected = g_Selection.toList(); | |
for (let ent in g_Selection.highlighted) | |
selected.push(g_Selection.highlighted[ent]); | |
// Remove selected entities from the 'all entities' array, | |
// to avoid disabling their status bars. | |
entities = Engine.GuiInterfaceCall( | |
g_ViewedPlayer == -1 ? "GetNonGaiaEntities" : "GetPlayerEntities", { | |
"viewedPlayer": g_ViewedPlayer | |
}).filter(idx => selected.indexOf(idx) == -1); | |
} | |
Engine.GuiInterfaceCall("SetStatusBars", { | |
"entities": entities, | |
"enabled": g_ShowAllStatusBars && !remove, | |
"showRank": Engine.ConfigDB_GetValue("user", "gui.session.rankabovestatusbar") == "true" | |
}); | |
} | |
/** | |
* Inverts the given configuration boolean and returns the current state. | |
* For example "silhouettes". | |
*/ | |
function toggleConfigBool(configName) | |
{ | |
let enabled = Engine.ConfigDB_GetValue("user", configName) != "true"; | |
saveSettingAndWriteToUserConfig(configName, String(enabled)); | |
return enabled; | |
} | |
/** | |
* Toggles the display of range overlays of selected entities for the given range type. | |
* @param {string} type - for example "Auras" | |
*/ | |
function toggleRangeOverlay(type) | |
{ | |
let enabled = toggleConfigBool("gui.session." + type.toLowerCase() + "range"); | |
Engine.GuiInterfaceCall("EnableVisualRangeOverlayType", { | |
"type": type, | |
"enabled": enabled | |
}); | |
let selected = g_Selection.toList(); | |
for (let ent in g_Selection.highlighted) | |
selected.push(g_Selection.highlighted[ent]); | |
Engine.GuiInterfaceCall("SetRangeOverlays", { | |
"entities": selected, | |
"enabled": enabled | |
}); | |
} | |
function updateEnabledRangeOverlayTypes() | |
{ | |
for (let type of ["Attack", "Auras", "Heal"]) | |
Engine.GuiInterfaceCall("EnableVisualRangeOverlayType", { | |
"type": type, | |
"enabled": Engine.ConfigDB_GetValue("user", "gui.session." + type.toLowerCase() + "range") == "true" | |
}); | |
} | |
// Update the additional list of entities to be highlighted. | |
function updateAdditionalHighlight() | |
{ | |
let entsAdd = []; // list of entities units to be highlighted | |
let entsRemove = []; | |
let highlighted = g_Selection.toList(); | |
for (let ent in g_Selection.highlighted) | |
highlighted.push(g_Selection.highlighted[ent]); | |
if (g_ShowGuarding) | |
// flag the guarding entities to add in this additional highlight | |
for (let sel in g_Selection.selected) | |
{ | |
let state = GetEntityState(g_Selection.selected[sel]); | |
if (!state.guard || !state.guard.entities.length) | |
continue; | |
for (let ent of state.guard.entities) | |
if (highlighted.indexOf(ent) == -1 && entsAdd.indexOf(ent) == -1) | |
entsAdd.push(ent); | |
} | |
if (g_ShowGuarded) | |
// flag the guarded entities to add in this additional highlight | |
for (let sel in g_Selection.selected) | |
{ | |
let state = GetEntityState(g_Selection.selected[sel]); | |
if (!state.unitAI || !state.unitAI.isGuarding) | |
continue; | |
let ent = state.unitAI.isGuarding; | |
if (highlighted.indexOf(ent) == -1 && entsAdd.indexOf(ent) == -1) | |
entsAdd.push(ent); | |
} | |
// flag the entities to remove (from the previously added) from this additional highlight | |
for (let ent of g_AdditionalHighlight) | |
if (highlighted.indexOf(ent) == -1 && entsAdd.indexOf(ent) == -1 && entsRemove.indexOf(ent) == -1) | |
entsRemove.push(ent); | |
_setHighlight(entsAdd, g_HighlightedAlpha, true); | |
_setHighlight(entsRemove, 0, false); | |
g_AdditionalHighlight = entsAdd; | |
} | |
function playAmbient() | |
{ | |
Engine.PlayAmbientSound(pickRandom(g_Ambient), true); | |
} | |
function getBuildString() | |
{ | |
return sprintf(translate("Build: %(buildDate)s (%(revision)s)"), { | |
"buildDate": Engine.GetBuildTimestamp(0), | |
"revision": Engine.GetBuildTimestamp(2) | |
}); | |
} | |
function toggleReplace() | |
{ | |
let selected = Engine.GetGUIObjectByName("viewPlayer").selected; | |
if (selected <= 1) | |
return; | |
let player = g_Players[Engine.GetGUIObjectByName("viewPlayer").selected -1 ]; | |
// TODO: Offline player (g_Players[g_ViewedPlayer].offline) is at the moment not up-to-date for rejoining players | |
// so we can't enable only replace for offline players. So for now just try replace for any networked game player. | |
if (!player) | |
return; | |
messageBox( | |
400, 200, | |
translate("Sure that you want to replace \"" + player.name + "\" in the game?"), | |
translate("Confirmation"), | |
[translate("No"), translate("Yes")], | |
[null, () => toggleReplaceReally(player.name)] | |
); | |
} | |
function toggleReplaceReally(playerName) | |
{ | |
if (playerName) | |
{ | |
submitChatDirectly("/me am going to replace " + escapeText(playerName) + ".\nThanks to fgod-mod. Search forums for fgod-mod (wildfiregames.com/forum)\nIf replace is working fine think about donate. Alt+Shift+F for details."); | |
g_PageOnLeaveSettings = [ "page_lobby.xml", { | |
"joinGame": { | |
"multiplayerGameType": "join", | |
"name": playerName, | |
"ip": g_ServerIP, | |
"port": g_ServerPort, | |
"useSTUN": g_UseSTUN, | |
"hostJID": g_HostJID} | |
} ]; | |
// exitMenuButton(); | |
leaveGame(); | |
} | |
} | |
function showTimeWarpMessageBox() | |
{ | |
messageBox( | |
500, 250, | |
translate("Note: time warp mode is a developer option, and not intended for use over long periods of time. Using it incorrectly may cause the game to run out of memory or crash."), | |
translate("Time warp mode") | |
); | |
} | |
/** | |
* Adds the ingame time and ceasefire counter to the global FPS and | |
* realtime counters shown in the top right corner. | |
*/ | |
function appendSessionCounters(counters) | |
{ | |
let simState = GetSimState(); | |
if (Engine.ConfigDB_GetValue("user", "gui.session.timeelapsedcounter") === "true") | |
{ | |
let currentSpeed = Engine.GetSimRate(); | |
if (currentSpeed != 1.0) | |
// Translation: The "x" means "times", with the mathematical meaning of multiplication. | |
counters.push(sprintf(translate("%(time)s (%(speed)sx)"), { | |
"time": timeToString(simState.timeElapsed), | |
"speed": Engine.FormatDecimalNumberIntoString(currentSpeed) | |
})); | |
else | |
counters.push(timeToString(simState.timeElapsed)); | |
} | |
if (simState.ceasefireActive && Engine.ConfigDB_GetValue("user", "gui.session.ceasefirecounter") === "true") | |
counters.push(timeToString(simState.ceasefireTimeRemaining)); | |
g_ResearchListTop = 4 + 14 * counters.length; | |
} | |
/** | |
* Send the current list of players, teams, AIs, observers and defeated/won and offline states to the lobby. | |
* The playerData format from g_GameAttributes is kept to reuse the GUI function presenting the data. | |
*/ | |
function sendLobbyPlayerlistUpdate() | |
{ | |
if (!g_IsController || !Engine.HasXmppClient()) | |
return; | |
// Extract the relevant player data and minimize packet load | |
let minPlayerData = []; | |
for (let playerID in g_GameAttributes.settings.PlayerData) | |
{ | |
if (+playerID == 0) | |
continue; | |
let pData = g_GameAttributes.settings.PlayerData[playerID]; | |
let minPData = { "Name": pData.Name, "Civ": pData.Civ }; | |
if (g_GameAttributes.settings.LockTeams) | |
minPData.Team = pData.Team; | |
if (pData.AI) | |
{ | |
minPData.AI = pData.AI; | |
minPData.AIDiff = pData.AIDiff; | |
minPData.AIBehavior = pData.AIBehavior; | |
} | |
if (g_Players[playerID].offline) | |
minPData.Offline = true; | |
// Whether the player has won or was defeated | |
let state = g_Players[playerID].state; | |
if (state != "active") | |
minPData.State = state; | |
minPlayerData.push(minPData); | |
} | |
// Add observers | |
let connectedPlayers = 0; | |
for (let guid in g_PlayerAssignments) | |
{ | |
let pData = g_GameAttributes.settings.PlayerData[g_PlayerAssignments[guid].player]; | |
if (pData) | |
++connectedPlayers; | |
else | |
minPlayerData.push({ | |
"Name": g_PlayerAssignments[guid].name, | |
"Team": "observer" | |
}); | |
} | |
Engine.SendChangeStateGame(connectedPlayers, playerDataToStringifiedTeamList(minPlayerData)); | |
} | |
/** | |
* Send a report on the gamestatus to the lobby. | |
*/ | |
function reportGame() | |
{ | |
// Only 1v1 games are rated (and Gaia is part of g_Players) | |
if (!Engine.HasXmppClient() || !Engine.IsRankedGame() || | |
g_Players.length != 3 || Engine.GetPlayerID() == -1) | |
return; | |
let extendedSimState = Engine.GuiInterfaceCall("GetExtendedSimulationState"); | |
let unitsClasses = [ | |
"total", | |
"Infantry", | |
"Worker", | |
"FemaleCitizen", | |
"Cavalry", | |
"Champion", | |
"Hero", | |
"Siege", | |
"Ship", | |
"Trader" | |
]; | |
let unitsCountersTypes = [ | |
"unitsTrained", | |
"unitsLost", | |
"enemyUnitsKilled" | |
]; | |
let buildingsClasses = [ | |
"total", | |
"CivCentre", | |
"House", | |
"Economic", | |
"Outpost", | |
"Military", | |
"Fortress", | |
"Wonder" | |
]; | |
let buildingsCountersTypes = [ | |
"buildingsConstructed", | |
"buildingsLost", | |
"enemyBuildingsDestroyed" | |
]; | |
let resourcesTypes = [ | |
"wood", | |
"food", | |
"stone", | |
"metal" | |
]; | |
let resourcesCounterTypes = [ | |
"resourcesGathered", | |
"resourcesUsed", | |
"resourcesSold", | |
"resourcesBought" | |
]; | |
let misc = [ | |
"tradeIncome", | |
"tributesSent", | |
"tributesReceived", | |
"treasuresCollected", | |
"lootCollected", | |
"percentMapExplored" | |
]; | |
let playerStatistics = {}; | |
// Unit Stats | |
for (let unitCounterType of unitsCountersTypes) | |
{ | |
if (!playerStatistics[unitCounterType]) | |
playerStatistics[unitCounterType] = { }; | |
for (let unitsClass of unitsClasses) | |
playerStatistics[unitCounterType][unitsClass] = ""; | |
} | |
playerStatistics.unitsLostValue = ""; | |
playerStatistics.unitsKilledValue = ""; | |
// Building stats | |
for (let buildingCounterType of buildingsCountersTypes) | |
{ | |
if (!playerStatistics[buildingCounterType]) | |
playerStatistics[buildingCounterType] = { }; | |
for (let buildingsClass of buildingsClasses) | |
playerStatistics[buildingCounterType][buildingsClass] = ""; | |
} | |
playerStatistics.buildingsLostValue = ""; | |
playerStatistics.enemyBuildingsDestroyedValue = ""; | |
// Resources | |
for (let resourcesCounterType of resourcesCounterTypes) | |
{ | |
if (!playerStatistics[resourcesCounterType]) | |
playerStatistics[resourcesCounterType] = { }; | |
for (let resourcesType of resourcesTypes) | |
playerStatistics[resourcesCounterType][resourcesType] = ""; | |
} | |
playerStatistics.resourcesGathered.vegetarianFood = ""; | |
for (let type of misc) | |
playerStatistics[type] = ""; | |
// Total | |
playerStatistics.economyScore = ""; | |
playerStatistics.militaryScore = ""; | |
playerStatistics.totalScore = ""; | |
let mapName = g_GameAttributes.settings.Name; | |
let playerStates = ""; | |
let playerCivs = ""; | |
let teams = ""; | |
let teamsLocked = true; | |
// Serialize the statistics for each player into a comma-separated list. | |
// Ignore gaia | |
for (let i = 1; i < extendedSimState.players.length; ++i) | |
{ | |
let player = extendedSimState.players[i]; | |
let maxIndex = player.sequences.time.length - 1; | |
playerStates += player.state + ","; | |
playerCivs += player.civ + ","; | |
teams += player.team + ","; | |
teamsLocked = teamsLocked && player.teamsLocked; | |
for (let resourcesCounterType of resourcesCounterTypes) | |
for (let resourcesType of resourcesTypes) | |
playerStatistics[resourcesCounterType][resourcesType] += player.sequences[resourcesCounterType][resourcesType][maxIndex] + ","; | |
playerStatistics.resourcesGathered.vegetarianFood += player.sequences.resourcesGathered.vegetarianFood[maxIndex] + ","; | |
for (let unitCounterType of unitsCountersTypes) | |
for (let unitsClass of unitsClasses) | |
playerStatistics[unitCounterType][unitsClass] += player.sequences[unitCounterType][unitsClass][maxIndex] + ","; | |
for (let buildingCounterType of buildingsCountersTypes) | |
for (let buildingsClass of buildingsClasses) | |
playerStatistics[buildingCounterType][buildingsClass] += player.sequences[buildingCounterType][buildingsClass][maxIndex] + ","; | |
let total = 0; | |
for (let type in player.sequences.resourcesGathered) | |
total += player.sequences.resourcesGathered[type][maxIndex]; | |
playerStatistics.economyScore += total + ","; | |
playerStatistics.militaryScore += Math.round((player.sequences.enemyUnitsKilledValue[maxIndex] + | |
player.sequences.enemyBuildingsDestroyedValue[maxIndex]) / 10) + ","; | |
playerStatistics.totalScore += (total + Math.round((player.sequences.enemyUnitsKilledValue[maxIndex] + | |
player.sequences.enemyBuildingsDestroyedValue[maxIndex]) / 10)) + ","; | |
for (let type of misc) | |
playerStatistics[type] += player.sequences[type][maxIndex] + ","; | |
} | |
// Send the report with serialized data | |
let reportObject = {}; | |
reportObject.timeElapsed = extendedSimState.timeElapsed; | |
reportObject.playerStates = playerStates; | |
reportObject.playerID = Engine.GetPlayerID(); | |
reportObject.matchID = g_GameAttributes.matchID; | |
reportObject.civs = playerCivs; | |
reportObject.teams = teams; | |
reportObject.teamsLocked = String(teamsLocked); | |
reportObject.ceasefireActive = String(extendedSimState.ceasefireActive); | |
reportObject.ceasefireTimeRemaining = String(extendedSimState.ceasefireTimeRemaining); | |
reportObject.mapName = mapName; | |
reportObject.economyScore = playerStatistics.economyScore; | |
reportObject.militaryScore = playerStatistics.militaryScore; | |
reportObject.totalScore = playerStatistics.totalScore; | |
for (let rct of resourcesCounterTypes) | |
for (let rt of resourcesTypes) | |
reportObject[rt + rct.substr(9)] = playerStatistics[rct][rt]; | |
// eg. rt = food rct.substr = Gathered rct = resourcesGathered | |
reportObject.vegetarianFoodGathered = playerStatistics.resourcesGathered.vegetarianFood; | |
for (let type of unitsClasses) | |
{ | |
// eg. type = Infantry (type.substr(0,1)).toLowerCase()+type.substr(1) = infantry | |
reportObject[(type.substr(0, 1)).toLowerCase() + type.substr(1) + "UnitsTrained"] = playerStatistics.unitsTrained[type]; | |
reportObject[(type.substr(0, 1)).toLowerCase() + type.substr(1) + "UnitsLost"] = playerStatistics.unitsLost[type]; | |
reportObject["enemy" + type + "UnitsKilled"] = playerStatistics.enemyUnitsKilled[type]; | |
} | |
for (let type of buildingsClasses) | |
{ | |
reportObject[(type.substr(0, 1)).toLowerCase() + type.substr(1) + "BuildingsConstructed"] = playerStatistics.buildingsConstructed[type]; | |
reportObject[(type.substr(0, 1)).toLowerCase() + type.substr(1) + "BuildingsLost"] = playerStatistics.buildingsLost[type]; | |
reportObject["enemy" + type + "BuildingsDestroyed"] = playerStatistics.enemyBuildingsDestroyed[type]; | |
} | |
for (let type of misc) | |
reportObject[type] = playerStatistics[type]; | |
Engine.SendGameReport(reportObject); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment