Index: ps/trunk/binaries/data/mods/public/gui/session/session.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/session.js (revision 18779) +++ ps/trunk/binaries/data/mods/public/gui/session/session.js (revision 18780) @@ -1,1357 +1,1358 @@ const g_IsReplay = Engine.IsVisualReplay(); const g_Ceasefire = prepareForDropdown(g_Settings && g_Settings.Ceasefire); const g_GameSpeeds = prepareForDropdown(g_Settings && g_Settings.GameSpeeds.filter(speed => !speed.ReplayOnly || g_IsReplay)); 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_VictoryConditions = prepareForDropdown(g_Settings && g_Settings.VictoryConditions); const g_WonderDurations = prepareForDropdown(g_Settings && g_Settings.WonderDurations); /** * Colors to flash when pop limit reached. */ const g_DefaultPopulationColor = "white"; const g_PopulationAlertColor = "orange"; /** * A random file will be played. TODO: more variety */ const g_Ambient = [ "audio/ambient/dayscape/day_temperate_gen_03.ogg" ]; /** * Map, player and match settings set in gamesetup. */ const g_GameAttributes = Object.freeze(Engine.GetInitAttributes()); /** * Is this user in control of game settings (i.e. is a network server, or offline player). */ var g_IsController; /** * True if this is a multiplayer game. */ var g_IsNetworked = 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 = []; /** * 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 lastTickTime = new Date(); /** * Not constant as we add "gaia". */ var g_CivData = {}; /** * For restoring selection, order and filters when returning to the replay menu */ var g_ReplaySelectionData; var g_PlayerAssignments = { "local": { "name": singleplayerName(), "player": 1 } }; /** * Cache dev-mode settings that are frequently or widely used. */ var g_DevSettings = { "changePerspective": false, "controlAll": false }; /** * 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 simulation state (updated on every simulation update). */ var g_SimState; var g_EntityStates = {}; var g_TemplateData = {}; var g_TemplateDataWithoutLocalization = {}; var g_TechnologyData = {}; /** * 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 heroes. */ var g_Heroes = []; /** * Unit classes to be checked for the idle-worker-hotkey. */ var g_WorkerTypes = ["Female", "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"]; /** * Cache the idle worker status. */ var g_HasIdleWorker = false; function GetSimState() { if (!g_SimState) g_SimState = Engine.GuiInterfaceCall("GetSimulationState"); return g_SimState; } function GetEntityState(entId) { if (!g_EntityStates[entId]) g_EntityStates[entId] = Engine.GuiInterfaceCall("GetEntityState", entId); return g_EntityStates[entId]; } function GetExtendedEntityState(entId) { let entState = GetEntityState(entId); if (!entState || entState.extended) return entState; let extension = Engine.GuiInterfaceCall("GetExtendedEntityState", entId); for (let prop in extension) entState[prop] = extension[prop]; entState.extended = true; g_EntityStates[entId] = entState; return entState; } function GetTemplateData(templateName) { if (!(templateName in g_TemplateData)) { let template = Engine.GuiInterfaceCall("GetTemplateData", templateName); translateObjectKeys(template, ["specific", "generic", "tooltip"]); g_TemplateData[templateName] = template; } return g_TemplateData[templateName]; } function GetTemplateDataWithoutLocalization(templateName) { if (!(templateName in g_TemplateDataWithoutLocalization)) { let template = Engine.GuiInterfaceCall("GetTemplateData", templateName); g_TemplateDataWithoutLocalization[templateName] = template; } return g_TemplateDataWithoutLocalization[templateName]; } function GetTechnologyData(technologyName) { if (!(technologyName in g_TechnologyData)) { let template = Engine.GuiInterfaceCall("GetTechnologyData", technologyName); translateObjectKeys(template, ["specific", "generic", "description", "tooltip", "requirementsTooltip"]); g_TechnologyData[technologyName] = template; } return g_TechnologyData[technologyName]; } function init(initData, hotloadData) { if (!g_Settings) { Engine.EndGame(); Engine.SwitchGuiPage("page_pregame.xml"); return; } if (initData) { g_IsNetworked = initData.isNetworked; g_IsController = initData.isController; g_PlayerAssignments = initData.playerAssignments; g_ReplaySelectionData = initData.replaySelectionData; g_HasRejoined = initData.isRejoining; if (initData.savedGUIData) restoreSavedGameData(initData.savedGUIData); Engine.GetGUIObjectByName("gameSpeedButton").hidden = g_IsNetworked; } else // Needed for autostart loading option { if (g_IsReplay) g_PlayerAssignments.local.player = -1; } g_Players = getPlayerData(); g_CivData = loadCivData(); g_CivData.gaia = { "Code": "gaia", "Name": translate("Gaia") }; initializeMusic(); // before changing the perspective let gameSpeed = Engine.GetGUIObjectByName("gameSpeed"); gameSpeed.list = g_GameSpeeds.Title; gameSpeed.list_data = g_GameSpeeds.Speed; let gameSpeedIdx = g_GameSpeeds.Speed.indexOf(Engine.GetSimRate()); gameSpeed.selected = gameSpeedIdx != -1 ? gameSpeedIdx : g_GameSpeeds.Default; gameSpeed.onSelectionChange = function() { changeGameSpeed(+this.list_data[this.selected]); }; initMenuPosition(); for (let slot in Engine.GetGUIObjectByName("unitHeroPanel").children) initGUIHeroes(slot); // Populate player selection dropdown let playerNames = [translate("Observer")]; let playerIDs = [-1]; for (let player in g_Players) { playerIDs.push(player); playerNames.push(colorizePlayernameHelper("■", player) + " " + g_Players[player].name); } // Select "observer" item when rejoining as a defeated player let viewedPlayer = g_Players[Engine.GetPlayerID()]; let viewPlayerDropdown = Engine.GetGUIObjectByName("viewPlayer"); viewPlayerDropdown.list = playerNames; viewPlayerDropdown.list_data = playerIDs; viewPlayerDropdown.selected = viewedPlayer && viewedPlayer.state == "defeated" ? 0 : Engine.GetPlayerID() + 1; // If in Atlas editor, disable the exit button if (Engine.IsAtlasRunning()) Engine.GetGUIObjectByName("menuExitButton").enabled = false; if (hotloadData) g_Selection.selected = hotloadData.selection; initChatWindow(); sendLobbyPlayerlistUpdate(); onSimulationUpdate(); setTimeout(displayGamestateNotifications, 1000); // 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); } /** * 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"); Engine.GetGUIObjectByName("idleWorkerButton").tooltip = colorizeHotkey("%(hotkey)s" + " ", "selection.idleworker") + translate("Find idle worker"); Engine.GetGUIObjectByName("tradeHelp").tooltip = translate("Select one goods as origin of the changes, then use the arrows of the target goods to make the changes.") + colorizeHotkey( "\n" + translate("Using %(hotkey)s will put the selected resource to 100%%."), "session.fulltradeswap"); } function initGUIHeroes(slot) { let button = Engine.GetGUIObjectByName("unitHeroButton[" + slot + "]"); button.onPress = function() { let hero = g_Heroes.find(hero => hero.slot !== undefined && hero.slot == slot); if (!hero) return; if (!Engine.HotkeyIsPressed("selection.add")) g_Selection.reset(); g_Selection.addList([hero.ent]); }; button.onDoublePress = function() { let hero = g_Heroes.find(hero => hero.slot !== undefined && hero.slot == slot); if (hero) selectAndMoveTo(getEntityOrHolder(hero.ent)); }; } function initializeMusic() { initMusic(); if (g_ViewedPlayer != -1) global.music.storeTracks(g_CivData[g_Players[g_ViewedPlayer].civ].Music); global.music.setState(global.music.states.PEACE); playAmbient(); } function toggleChangePerspective(enabled) { g_DevSettings.changePerspective = enabled; selectViewPlayer(g_ViewedPlayer); } /** * 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) g_ViewedPlayer = playerID; if (g_DevSettings.changePerspective) { Engine.SetPlayerID(g_ViewedPlayer); g_IsObserver = isPlayerObserver(g_ViewedPlayer); } Engine.SetViewedPlayer(g_ViewedPlayer); updateTopPanel(); updateChatAddressees(); updateHotkeyTooltips(); // Update GUI and clear player-dependent cache onSimulationUpdate(); if (g_IsDiplomacyOpen) openDiplomacy(); if (g_IsTradeOpen) openTrade(); } /** * 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 a player has won or was defeated. */ function playerFinished(player, won) { if (player == Engine.GetPlayerID()) reportGame(); updateDiplomacy(); updateChatAddressees(); if (player != Engine.GetPlayerID() || Engine.IsAtlasRunning()) return; global.music.setState( won ? global.music.states.VICTORY : global.music.states.DEFEAT ); // Select "observer" item on loss. On win enable observermode without changing perspective Engine.GetGUIObjectByName("viewPlayer").selected = won ? g_ViewedPlayer + 1 : 0; 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 - Structure Tree"), { "civ": g_CivData[g_Players[g_ViewedPlayer].civ].Name }); } Engine.GetGUIObjectByName("optionFollowPlayer").hidden = !g_IsObserver || !isPlayer; let viewPlayer = Engine.GetGUIObjectByName("viewPlayer"); viewPlayer.hidden = !g_IsObserver && !g_DevSettings.changePerspective; Engine.GetGUIObjectByName("food").hidden = !isPlayer; Engine.GetGUIObjectByName("wood").hidden = !isPlayer; Engine.GetGUIObjectByName("stone").hidden = !isPlayer; Engine.GetGUIObjectByName("metal").hidden = !isPlayer; Engine.GetGUIObjectByName("population").hidden = !isPlayer; Engine.GetGUIObjectByName("diplomacyButton1").hidden = !isPlayer; Engine.GetGUIObjectByName("tradeButton1").hidden = !isPlayer; Engine.GetGUIObjectByName("observerText").hidden = isPlayer; let alphaLabel = Engine.GetGUIObjectByName("alphaLabel"); alphaLabel.hidden = isPlayer && !viewPlayer.hidden; alphaLabel.size = isPlayer ? "50%+20 0 100%-226 100%" : "200 0 100%-475 100%"; Engine.GetGUIObjectByName("pauseButton").enabled = !g_IsObserver || !g_IsNetworked; 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() })); } /** * Resign a player. * @param leaveGameAfterResign If player is quitting after resignation. */ function resignGame(leaveGameAfterResign) { if (g_IsObserver || g_Disconnected) return; Engine.PostNetworkCommand({ "type": "defeat-player", "playerId": Engine.GetPlayerID(), "resign": true }); 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(); Engine.EndGame(); if (g_IsController && Engine.HasXmppClient()) Engine.SendUnregisterGame(); Engine.SwitchGuiPage("page_summary.xml", { "sim": simData, "gui": { "assignedPlayer": Engine.GetPlayerID(), "disconnected": g_Disconnected, "isReplay": g_IsReplay, "replayDirectory": !g_HasRejoined && replayDirectory, "replaySelectionData": g_ReplaySelectionData } }); } // 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(); } /** * Called every frame. */ function onTick() { if (!g_Settings) return; let now = new Date(); let tickLength = new Date() - lastTickTime; lastTickTime = now; handleNetMessages(); updateCursorAndTooltip(); if (g_Selection.dirty) { g_Selection.dirty = false; updateGUIObjects(); // Display rally points for selected buildings if (Engine.GetPlayerID() != -1) Engine.GuiInterfaceCall("DisplayRallyPoint", { "entities": g_Selection.toList() }); } updateTimers(); updateMenuPosition(tickLength); // When training is blocked, flash population (alternates color every 500msec) Engine.GetGUIObjectByName("resourcePop").textcolor = g_IsTrainingBlocked && Date.now() % 1000 < 500 ? g_PopulationAlertColor : g_DefaultPopulationColor; Engine.GuiInterfaceCall("ClearRenamedEntities"); } function changeGameSpeed(speed) { if (!g_IsNetworked) Engine.SetSimRate(speed); } function hasIdleWorker() { return Engine.GuiInterfaceCall("HasIdleUnits", { "viewedPlayer": g_ViewedPlayer, "idleClasses": g_WorkerTypes, "excludeUnits": [] }); } function updateIdleWorkerButton() { g_HasIdleWorker = hasIdleWorker(); let idleWorkerButton = Engine.GetGUIObjectByName("idleOverlay"); let prefix = "stretched:session/"; if (!g_HasIdleWorker) idleWorkerButton.sprite = prefix + "minimap-idle-disabled.png"; else if (idleWorkerButton.sprite != prefix + "minimap-idle-highlight.png") idleWorkerButton.sprite = prefix + "minimap-idle.png"; } function onSimulationUpdate() { g_EntityStates = {}; g_TemplateData = {}; g_TechnologyData = {}; g_SimState = Engine.GuiInterfaceCall("GetSimulationState"); if (!g_SimState) return; handleNotifications(); updateGUIObjects(); if (g_ConfirmExit) confirmExit(); } /** * Don't show the message box before all playerstate changes are processed. */ function confirmExit() { closeOpenDialogs(); let subject = g_PlayerStateMessages[g_ConfirmExit] + "\n" + translate("Do you want to quit?"); if (g_IsNetworked && g_IsController) subject += "\n" + translate("Leaving will disconnect all other players."); messageBox( 400, 200, subject, g_ConfirmExit == "won" ? translate("VICTORIOUS!") : translate("DEFEATED!"), [translate("No"), translate("Yes")], [resumeGame, leaveGame] ); g_ConfirmExit = false; } function updateGUIObjects() { g_Selection.update(); if (g_ShowAllStatusBars) recalculateStatusBarDisplay(); if (g_ShowGuarding || g_ShowGuarded) updateAdditionalHighlight(); updateHeroes(); displayHeroes(); updateGroups(); updateDebug(); updatePlayerDisplay(); updateResearchDisplay(); updateSelectionDetails(); updateBuildingPlacementPreview(); updateTimeNotifications(); updateIdleWorkerButton(); 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]); } } 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 updateHeroes() { let playerState = GetSimState().players[g_ViewedPlayer]; let heroes = playerState ? playerState.heroes : []; g_Heroes = g_Heroes.filter(hero => heroes.find(ent => ent == hero.ent)); for (let ent of heroes) { let heroState = GetExtendedEntityState(ent); let template = GetTemplateData(heroState.template); let hero = g_Heroes.find(hero => ent == hero.ent); if (!hero) { hero = { "ent": ent, "tooltip": undefined, "sprite": "stretched:session/portraits/" + template.icon, "maxHitpoints": undefined, "currentHitpoints": heroState.hitpoints, "previousHitpoints": undefined }; g_Heroes.push(hero); } hero.tooltip = createHeroTooltip(heroState, template); hero.previousHitpoints = hero.currentHitpoints; hero.currentHitpoints = heroState.hitpoints; hero.maxHitpoints = heroState.maxHitpoints; } } function createHeroTooltip(heroState, template) { return [ "[font=\"sans-bold-16\"]" + template.name.specific + "[/font]" + "\n" + sprintf(translate("%(label)s %(current)s / %(max)s"), { "label": "[font=\"sans-bold-13\"]" + translate("Health:") + "[/font]", "current": Math.ceil(heroState.hitpoints), "max": Math.ceil(heroState.maxHitpoints) }), getAttackTooltip(heroState), getArmorTooltip(heroState), getEntityTooltip(heroState) ].filter(tip => tip).join("\n"); } function displayHeroes() { let buttons = Engine.GetGUIObjectByName("unitHeroPanel").children; buttons.forEach((button, slot) => { if (button.hidden || g_Heroes.some(hero => hero.slot !== undefined && hero.slot == slot)) return; button.hidden = true; stopColorFade("heroHitOverlay[" + slot + "]"); }); // The slot identifies the button, displayIndex determines its position. for (let displayIndex = 0; displayIndex < Math.min(g_Heroes.length, buttons.length); ++displayIndex) { let hero = g_Heroes[displayIndex]; // Find the first unused slot if new, otherwise reuse previous. let slot = hero.slot === undefined ? buttons.findIndex(button => button.hidden) : hero.slot; let heroButton = Engine.GetGUIObjectByName("unitHeroButton[" + slot + "]"); heroButton.tooltip = hero.tooltip; updateGUIStatusBar("heroHealthBar[" + slot + "]", hero.currentHitpoints, hero.maxHitpoints); if (hero.slot === undefined) { let heroImage = Engine.GetGUIObjectByName("unitHeroImage[" + slot + "]"); heroImage.sprite = hero.sprite; heroButton.hidden = false; hero.slot = slot; } // If the health of the hero changed since the last update, trigger the animation. if (hero.previousHitpoints > hero.currentHitpoints) startColorFade("heroHitOverlay[" + slot + "]", 100, 0, colorFade_attackUnit, true, smoothColorFadeRestart_attackUnit); // TODO: Instead of instant position changes, animate button movement. setPanelObjectPosition(heroButton, displayIndex, buttons.length); } } function updateGroups() { g_Groups.update(); // Determine the sum of the costs of a given template let getCostSum = (template) => { let cost = GetTemplateData(template).cost; return Object.keys(cost).map(key => cost[key]).reduce((sum, cur) => sum + cur); }; 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(g_Groups.groups[i].getEntsGrouped().reduce((pre, cur) => { if (pre.ents.length == cur.ents.length) return getCostSum(pre.template) > getCostSum(cur.template) ? pre : cur; return pre.ents.length > cur.ents.length ? pre : cur; }).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 = deepcopy(GetSimState()); conciseSimState.players = "<<>>"; let text = "simulation: " + uneval(conciseSimState); let selection = g_Selection.toList(); if (selection.length) { let entState = GetExtendedEntityState(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, "\\["); } function getAllyStatTooltip(resource) { let playersState = GetSimState().players; let ret = ""; for (let player in playersState) if (player != 0 && player != g_ViewedPlayer && (g_IsObserver || playersState[g_ViewedPlayer].hasSharedLos && g_Players[player].isMutualAlly[g_ViewedPlayer])) ret += "\n" + sprintf(translate("%(playername)s: %(statValue)s"),{ "playername": colorizePlayernameHelper("■", player) + " " + g_Players[player].name, "statValue": resource == "pop" ? sprintf(translate("%(popCount)s/%(popLimit)s/%(popMax)s"), playersState[player]) : Math.round(playersState[player].resourceCounts[resource]) }); return ret; } function updatePlayerDisplay() { let playerState = GetSimState().players[g_ViewedPlayer]; if (!playerState) return; for (let res of RESOURCES) { Engine.GetGUIObjectByName("resource_" + res).caption = Math.floor(playerState.resourceCounts[res]); Engine.GetGUIObjectByName(res).tooltip = getLocalizedResourceName(res, "firstWord") + getAllyStatTooltip(res); } Engine.GetGUIObjectByName("resourcePop").caption = sprintf(translate("%(popCount)s/%(popLimit)s"), playerState); Engine.GetGUIObjectByName("population").tooltip = translate("Population (current / limit)") + "\n" + sprintf(translate("Maximum population: %(popCap)s"), { "popCap": playerState.popMax }) + getAllyStatTooltip("pop"); g_IsTrainingBlocked = playerState.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); 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; ++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 }); } // 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(g_Ambient[Math.floor(Math.random() * g_Ambient.length)], true); } function getBuildString() { return sprintf(translate("Build: %(buildDate)s (%(revision)s)"), { "buildDate": Engine.GetBuildTimestamp(0), "revision": Engine.GetBuildTimestamp(2) }); } 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") ); } /** * 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", "Female", "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 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 = ""; playerStatistics.tradeIncome = ""; // Tribute playerStatistics.tributesSent = ""; playerStatistics.tributesReceived = ""; // Total playerStatistics.economyScore = ""; playerStatistics.militaryScore = ""; playerStatistics.totalScore = ""; // Various playerStatistics.treasuresCollected = ""; playerStatistics.lootCollected = ""; playerStatistics.feminisation = ""; playerStatistics.percentMapExplored = ""; 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]; 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.statistics[resourcesCounterType][resourcesType] + ","; playerStatistics.resourcesGathered.vegetarianFood += player.statistics.resourcesGathered.vegetarianFood + ","; for (let unitCounterType of unitsCountersTypes) for (let unitsClass of unitsClasses) playerStatistics[unitCounterType][unitsClass] += player.statistics[unitCounterType][unitsClass] + ","; for (let buildingCounterType of buildingsCountersTypes) for (let buildingsClass of buildingsClasses) playerStatistics[buildingCounterType][buildingsClass] += player.statistics[buildingCounterType][buildingsClass] + ","; let total = 0; for (let type in player.statistics.resourcesGathered) total += player.statistics.resourcesGathered[type]; playerStatistics.economyScore += total + ","; playerStatistics.militaryScore += Math.round((player.statistics.enemyUnitsKilledValue + player.statistics.enemyBuildingsDestroyedValue) / 10) + ","; playerStatistics.totalScore += (total + Math.round((player.statistics.enemyUnitsKilledValue + player.statistics.enemyBuildingsDestroyedValue) / 10)) + ","; playerStatistics.tradeIncome += player.statistics.tradeIncome + ","; playerStatistics.tributesSent += player.statistics.tributesSent + ","; playerStatistics.tributesReceived += player.statistics.tributesReceived + ","; playerStatistics.percentMapExplored += player.statistics.percentMapExplored + ","; playerStatistics.treasuresCollected += player.statistics.treasuresCollected + ","; playerStatistics.lootCollected += player.statistics.lootCollected + ","; } // 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]; } reportObject.tributesSent = playerStatistics.tributesSent; reportObject.tributesReceived = playerStatistics.tributesReceived; reportObject.percentMapExplored = playerStatistics.percentMapExplored; reportObject.treasuresCollected = playerStatistics.treasuresCollected; reportObject.lootCollected = playerStatistics.lootCollected; reportObject.tradeIncome = playerStatistics.tradeIncome; Engine.SendGameReport(reportObject); } Index: ps/trunk/binaries/data/mods/public/gui/summary/counters.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/summary/counters.js (revision 18779) +++ ps/trunk/binaries/data/mods/public/gui/summary/counters.js (revision 18780) @@ -1,463 +1,495 @@ var g_TeamHelperData = []; function resetDataHelpers() { g_TeamHelperData = []; } -function formatTrained(trained, lost, killed) +function formatTrained(trained, killed, lost) { return g_TrainedColor + trained + '[/color] / ' + - g_LostColor + lost + '[/color] / ' + - g_KilledColor + killed + '[/color]'; + g_KilledColor + killed + '[/color] / ' + + g_LostColor + lost + '[/color]'; } function formatCaptured(constructed, destroyed, captured, lost) { return g_TrainedColor + constructed + '[/color] / ' + g_KilledColor + destroyed + '[/color]\n' + g_CapturedColor + captured + '[/color] / ' + g_LostColor + lost + '[/color]\n'; } function formatIncome(income, outcome) { return g_IncomeColor + income + '[/color] / ' + g_OutcomeColor + outcome + '[/color]'; } function formatPercent(divident, divisor) { if (!divisor) return "0%"; return Math.floor(100 * divident / divisor) + "%"; } function formatRatio(divident, divisor) { if (!divident) return "0.00"; if (!divisor) return g_InfiniteSymbol; return Math.round(divident / divisor * 100) / 100; } /** * Remove color tags, whitespace, + and % to read numerical values from the GUI objects. * Remove \n only when removeLineFeed == true * TODO: access the data directly instead of this ugly hack. */ function cleanGUICaption(team, player, counter, removeLineFeed = true) { let caption = Engine.GetGUIObjectByName("valueDataTeam[" + team + "][" + player + "][" + counter + "]").caption; if (removeLineFeed) return caption.replace(/\[([\w\' \\\"\/\=]*)\]|\+|\%|\s/g, ""); else return caption.replace(/\[([\w\' \\\"\/\=]*)\]|[\t\r \f]/g, ""); } function updateCountersPlayer(playerState, counters, idGUI) { for (let w in counters) { let fn = counters[w].fn; Engine.GetGUIObjectByName(idGUI + "[" + w + "]").caption = fn && fn(playerState, w); } } // Updates g_TeamHelperData by appending some data from playerState function calculateTeamCounters(playerState) { if (!g_TeamHelperData[playerState.team]) g_TeamHelperData[playerState.team] = { "food": 0, "vegetarianFood": 0, "female": 0, "worker": 0, "enemyUnitsKilled": 0, "unitsLost": 0, "percentMapControlled": 0, "peakPercentMapControlled": 0, "percentMapExplored": 0, "totalBought": 0, "totalSold": 0 }; g_TeamHelperData[playerState.team].food += playerState.statistics.resourcesGathered.food; g_TeamHelperData[playerState.team].vegetarianFood += playerState.statistics.resourcesGathered.vegetarianFood; g_TeamHelperData[playerState.team].female += playerState.statistics.unitsTrained.Female; g_TeamHelperData[playerState.team].worker += playerState.statistics.unitsTrained.Worker; g_TeamHelperData[playerState.team].enemyUnitsKilled += playerState.statistics.enemyUnitsKilled.total; g_TeamHelperData[playerState.team].unitsLost += playerState.statistics.unitsLost.total; g_TeamHelperData[playerState.team].percentMapControlled = playerState.statistics.teamPercentMapControlled; g_TeamHelperData[playerState.team].peakPercentMapControlled = playerState.statistics.teamPeakPercentMapControlled; g_TeamHelperData[playerState.team].percentMapExplored = playerState.statistics.teamPercentMapExplored; for (let type in playerState.statistics.resourcesBought) g_TeamHelperData[playerState.team].totalBought += playerState.statistics.resourcesBought[type]; for (let type in playerState.statistics.resourcesSold) g_TeamHelperData[playerState.team].totalSold += playerState.statistics.resourcesSold[type]; } function calculateEconomyScore(playerState) { let total = 0; for (let type in playerState.statistics.resourcesGathered) total += playerState.statistics.resourcesGathered[type]; return Math.round(total / 10); } function calculateMilitaryScore(playerState) { return Math.round((playerState.statistics.enemyUnitsKilledValue + playerState.statistics.enemyBuildingsDestroyedValue + playerState.statistics.buildingsCapturedValue) / 10); } function calculateExplorationScore(playerState) { return playerState.statistics.percentMapExplored * 10; } function calculateScoreTotal(playerState) { return calculateEconomyScore(playerState) + calculateMilitaryScore(playerState) + calculateExplorationScore(playerState); } function calculateScoreTeam(counters) { for (let t in g_Teams) { if (t == -1) continue; let teamTotalScore = 0; for (let w in counters) { let total = 0; if (w == 2) // Team exploration score (not additive) total = g_TeamHelperData[t].percentMapExplored * 10; else for (let p = 0; p < g_Teams[t]; ++p) total += +Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + p + "][" + w + "]").caption; if (w < 3) { teamTotalScore += total; Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = total; } else Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = teamTotalScore; } } } function calculateBuildings(playerState, position) { let type = g_BuildingsTypes[position]; return formatCaptured( playerState.statistics.buildingsConstructed[type], playerState.statistics.enemyBuildingsDestroyed[type], playerState.statistics.buildingsCaptured[type], playerState.statistics.buildingsLost[type]); } function calculateBuildingsTeam(counters) { for (let t in g_Teams) { if (t == -1) continue; for (let w in counters) { let total = { "constructed" : 0, "destroyed" : 0, "captured" : 0, "lost" : 0 }; for (let p = 0; p < g_Teams[t]; ++p) { let splitCaption = cleanGUICaption(t, p, w, false).split("\n"); let first = splitCaption[0].split("/"); let second = splitCaption[1].split("/"); total.constructed += +first[0]; total.destroyed += +first[1]; total.captured += +second[0]; total.lost += +second[1]; } Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = formatCaptured(total.constructed, total.destroyed, total.captured, total.lost); } } } function calculateUnitsTeam(counters) { for (let t in g_Teams) { if (t == -1) continue; for (let w in counters) { - let total = { - "constructed": 0, - "lost": 0, - "destroyed": 0 + let total = + { + "trained": 0, + "killed": 0, + "captured" : 0, + "lost": 0 }; for (let p = 0; p < g_Teams[t]; ++p) { - let splitCaption = cleanGUICaption(t, p, w).split("/"); - - total.constructed += +splitCaption[0]; - total.lost += +splitCaption[1]; - total.destroyed += +splitCaption[2]; + if (w == 0 || w == 6) + { + let splitCaption = cleanGUICaption(t, p, w, false).split("\n"); + let first = splitCaption[0].split("/"); + let second = splitCaption[1].split("/"); + + total.trained += +first[0]; + total.killed += +first[1]; + total.captured += +second[0]; + total.lost += +second[1]; + } + else + { + let splitCaption = cleanGUICaption(t, p, w).split("/"); + total.trained += +splitCaption[0]; + total.killed += +splitCaption[1]; + total.lost += +splitCaption[2]; + } } - Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = - formatTrained(total.constructed, total.lost, total.destroyed); + let formattedCaption = ""; + + if (w == 0 || w == 6) + formattedCaption = formatCaptured(total.trained, total.killed, total.captured, total.lost); + else + formattedCaption = formatTrained(total.trained, total.killed, total.lost); + + Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = formattedCaption; } } } +function calculateUnitsWithCaptured(playerState, position) +{ + let type = g_UnitsTypes[position]; + + return formatCaptured( + playerState.statistics.unitsTrained[type], + playerState.statistics.enemyUnitsKilled[type], + playerState.statistics.unitsCaptured[type], + playerState.statistics.unitsLost[type]); +} + function calculateUnits(playerState, position) { let type = g_UnitsTypes[position]; return formatTrained( playerState.statistics.unitsTrained[type], - playerState.statistics.unitsLost[type], - playerState.statistics.enemyUnitsKilled[type]); + playerState.statistics.enemyUnitsKilled[type], + playerState.statistics.unitsLost[type]); } function calculateResources(playerState, position) { let type = g_ResourcesTypes[position]; return formatIncome( playerState.statistics.resourcesGathered[type], playerState.statistics.resourcesUsed[type] - playerState.statistics.resourcesSold[type]); } function calculateTotalResources(playerState) { let totalGathered = 0; let totalUsed = 0; for (let type of g_ResourcesTypes) { totalGathered += playerState.statistics.resourcesGathered[type]; totalUsed += playerState.statistics.resourcesUsed[type] - playerState.statistics.resourcesSold[type]; } return formatIncome(totalGathered, totalUsed); } function calculateTreasureCollected(playerState) { return playerState.statistics.treasuresCollected; } function calculateLootCollected(playerState) { return playerState.statistics.lootCollected; } function calculateTributeSent(playerState) { return formatIncome( playerState.statistics.tributesSent, playerState.statistics.tributesReceived); } function calculateResourcesTeam(counters) { for (let t in g_Teams) { if (t == -1) continue; for (let w in counters) { let total = { "income": 0, "outcome": 0 }; for (let p = 0; p < g_Teams[t]; ++p) { let caption = cleanGUICaption(t, p, w); if (w >= 6) total.income += +caption; else { let splitCaption = caption.split("/"); total.income += +splitCaption[0]; total.outcome += +splitCaption[1]; } } let teamTotal; if (w >= 6) teamTotal = total.income; else teamTotal = formatIncome(total.income, total.outcome); Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = teamTotal; } } } function calculateResourceExchanged(playerState, position) { let type = g_ResourcesTypes[position]; return formatIncome( playerState.statistics.resourcesBought[type], playerState.statistics.resourcesSold[type]); } function calculateBarterEfficiency(playerState) { let totalBought = 0; let totalSold = 0; for (let type in playerState.statistics.resourcesBought) totalBought += playerState.statistics.resourcesBought[type]; for (let type in playerState.statistics.resourcesSold) totalSold += playerState.statistics.resourcesSold[type]; return formatPercent(totalBought, totalSold); } function calculateTradeIncome(playerState) { return playerState.statistics.tradeIncome; } function calculateMarketTeam(counters) { for (let t in g_Teams) { if (t == -1) continue; for (let w in counters) { let total = { "income": 0, "outcome": 0 }; for (let p = 0; p < g_Teams[t]; ++p) { let caption = cleanGUICaption(t, p, w); if (w >= 4) total.income += +caption; else { let splitCaption = caption.split("/"); total.income += +splitCaption[0]; total.outcome += +splitCaption[1]; } } let teamTotal; if (w == 4) teamTotal = formatPercent(g_TeamHelperData[t].totalBought, g_TeamHelperData[t].totalSold); else if (w > 4) teamTotal = total.income; else teamTotal = formatIncome(total.income, total.outcome); Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = teamTotal; } } } function calculateVegetarianRatio(playerState) { return formatPercent( playerState.statistics.resourcesGathered.vegetarianFood, playerState.statistics.resourcesGathered.food); } function calculateFeminization(playerState) { return formatPercent( playerState.statistics.unitsTrained.Female, playerState.statistics.unitsTrained.Worker); } function calculateKillDeathRatio(playerState) { return formatRatio( playerState.statistics.enemyUnitsKilled.total, playerState.statistics.unitsLost.total); } function calculateMapExploration(playerState) { return playerState.statistics.percentMapExplored + "%"; } function calculateMapFinalControl(playerState) { return playerState.statistics.percentMapControlled + "%"; } function calculateMapPeakControl(playerState) { return playerState.statistics.peakPercentMapControlled + "%"; } function calculateMiscellaneous(counters) { for (let t in g_Teams) { if (t == -1) continue; for (let w in counters) { let teamTotal; if (w == 0) teamTotal = formatPercent(g_TeamHelperData[t].vegetarianFood, g_TeamHelperData[t].food); else if (w == 1) teamTotal = formatPercent(g_TeamHelperData[t].female, g_TeamHelperData[t].worker); else if (w == 2) teamTotal = formatRatio(g_TeamHelperData[t].enemyUnitsKilled, g_TeamHelperData[t].unitsLost); else if (w == 3) teamTotal = g_TeamHelperData[t].percentMapExplored + "%"; else if (w == 4) teamTotal = g_TeamHelperData[t].percentMapControlled + "%"; else if (w == 5) teamTotal = g_TeamHelperData[t].peakPercentMapControlled + "%"; Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]").caption = teamTotal; } } } Index: ps/trunk/binaries/data/mods/public/gui/summary/layout.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/summary/layout.js (revision 18779) +++ ps/trunk/binaries/data/mods/public/gui/summary/layout.js (revision 18780) @@ -1,339 +1,342 @@ var g_ScorePanelsData = { "score": { "headings": [ { "caption": translate("Player name"), "yStart": 26, "width": 200 }, { "caption": translate("Economy score"), "yStart": 16, "width": 100 }, { "caption": translate("Military score"), "yStart": 16, "width": 100 }, { "caption": translate("Exploration score"), "yStart": 16, "width": 100 }, { "caption": translate("Total score"), "yStart": 16, "width": 100 } ], "titleHeadings": [], "counters": [ { "width": 100, "fn": calculateEconomyScore, "verticalOffset": 12 }, { "width": 100, "fn": calculateMilitaryScore, "verticalOffset": 12 }, { "width": 100, "fn": calculateExplorationScore, "verticalOffset": 12 }, { "width": 100, "fn": calculateScoreTotal, "verticalOffset": 12 } ], "teamCounterFn": calculateScoreTeam }, "buildings": { "headings": [ { "caption": translate("Player name"), "yStart": 26, "width": 200 }, { "caption": translate("Total"), "yStart": 34, "width": 105 }, { "caption": translate("Houses"), "yStart": 34, "width": 85 }, { "caption": translate("Economic"), "yStart": 34, "width": 85 }, { "caption": translate("Outposts"), "yStart": 34, "width": 85 }, { "caption": translate("Military"), "yStart": 34, "width": 85 }, { "caption": translate("Fortresses"), "yStart": 34, "width": 85 }, { "caption": translate("Civ centers"), "yStart": 34, "width": 85 }, { "caption": translate("Wonders"), "yStart": 34, "width": 85 } ], "titleHeadings": [ { "caption": sprintf(translate("Buildings Statistics (%(constructed)s / %(destroyed)s / %(captured)s / %(lost)s)"), { "constructed": g_TrainedColor + translate("Constructed") + '[/color]', "destroyed": g_KilledColor + translate("Destroyed") + '[/color]', "captured": g_CapturedColor + translate("Captured") + '[/color]', "lost": g_LostColor + translate("Lost") + '[/color]' }), "yStart": 16, "width": (85 * 7 + 105) }, // width = 700 ], "counters": [ { "width": 105, "fn": calculateBuildings, "verticalOffset": 3 }, { "width": 85, "fn": calculateBuildings, "verticalOffset": 3 }, { "width": 85, "fn": calculateBuildings, "verticalOffset": 3 }, { "width": 85, "fn": calculateBuildings, "verticalOffset": 3 }, { "width": 85, "fn": calculateBuildings, "verticalOffset": 3 }, { "width": 85, "fn": calculateBuildings, "verticalOffset": 3 }, { "width": 85, "fn": calculateBuildings, "verticalOffset": 3 }, { "width": 85, "fn": calculateBuildings, "verticalOffset": 3 } ], "teamCounterFn": calculateBuildingsTeam }, "units": { "headings": [ { "caption": translate("Player name"), "yStart": 26, "width": 200 }, - { "caption": translate("Total"), "yStart": 34, "width": 120 }, - { "caption": translate("Infantry"), "yStart": 34, "width": 100 }, - { "caption": translate("Worker"), "yStart": 34, "width": 100 }, - { "caption": translate("Cavalry"), "yStart": 34, "width": 100 }, - { "caption": translate("Champion"), "yStart": 34, "width": 100 }, - { "caption": translate("Heroes"), "yStart": 34, "width": 100 }, - { "caption": translate("Navy"), "yStart": 34, "width": 100 }, - { "caption": translate("Traders"), "yStart": 34, "width": 100 } + { "caption": translate("Total"), "yStart": 34, "width": 105 }, + { "caption": translate("Infantry"), "yStart": 34, "width": 85 }, + { "caption": translate("Worker"), "yStart": 34, "width": 85 }, + { "caption": translate("Cavalry"), "yStart": 34, "width": 85 }, + { "caption": translate("Champion"), "yStart": 34, "width": 85 }, + { "caption": translate("Heroes"), "yStart": 34, "width": 85 }, + { "caption": translate("Siege"), "yStart": 34, "width": 85 }, + { "caption": translate("Navy"), "yStart": 34, "width": 85 }, + { "caption": translate("Traders"), "yStart": 34, "width": 85 } ], "titleHeadings": [ { - "caption": sprintf(translate("Units Statistics (%(trained)s / %(lost)s / %(killed)s)"), + "caption": sprintf(translate("Units Statistics (%(trained)s / %(killed)s / %(captured)s / %(lost)s)"), { "trained": g_TrainedColor + translate("Trained") + '[/color]', - "lost": g_LostColor + translate("Lost") + '[/color]', - "killed": g_KilledColor + translate("Killed") + '[/color]' + "killed": g_KilledColor + translate("Killed") + '[/color]', + "captured": g_CapturedColor + translate("Captured") + '[/color]', + "lost": g_LostColor + translate("Lost") + '[/color]' }), "yStart": 16, "width": (100 * 7 + 120) }, // width = 820 ], "counters": [ - { "width": 120, "fn": calculateUnits, "verticalOffset": 12 }, - { "width": 100, "fn": calculateUnits, "verticalOffset": 12 }, - { "width": 100, "fn": calculateUnits, "verticalOffset": 12 }, - { "width": 100, "fn": calculateUnits, "verticalOffset": 12 }, - { "width": 100, "fn": calculateUnits, "verticalOffset": 12 }, - { "width": 100, "fn": calculateUnits, "verticalOffset": 12 }, - { "width": 100, "fn": calculateUnits, "verticalOffset": 12 }, - { "width": 100, "fn": calculateUnits, "verticalOffset": 12 } + { "width": 105, "fn": calculateUnitsWithCaptured, "verticalOffset": 3 }, + { "width": 85, "fn": calculateUnits, "verticalOffset": 12 }, + { "width": 85, "fn": calculateUnits, "verticalOffset": 12 }, + { "width": 85, "fn": calculateUnits, "verticalOffset": 12 }, + { "width": 85, "fn": calculateUnits, "verticalOffset": 12 }, + { "width": 85, "fn": calculateUnits, "verticalOffset": 12 }, + { "width": 105, "fn": calculateUnitsWithCaptured, "verticalOffset": 3 }, + { "width": 85, "fn": calculateUnits, "verticalOffset": 12 }, + { "width": 85, "fn": calculateUnits, "verticalOffset": 12 } ], "teamCounterFn": calculateUnitsTeam }, "resources": { "headings": [ { "caption": translate("Player name"), "yStart": 26, "width": 200 }, { "caption": translate("Food"), "yStart": 34, "width": 100 }, { "caption": translate("Wood"), "yStart": 34, "width": 100 }, { "caption": translate("Stone"), "yStart": 34, "width": 100 }, { "caption": translate("Metal"), "yStart": 34, "width": 100 }, { "caption": translate("Total"), "yStart": 34, "width": 110 }, { "caption": sprintf(translate("Tributes \n(%(sent)s / %(received)s)"), { "sent": g_IncomeColor + translate("Sent") + '[/color]', "received": g_OutcomeColor + translate("Received") + '[/color]' }), "yStart": 16, "width": 121 }, { "caption": translate("Treasures collected"), "yStart": 16, "width": 100 }, { "caption": translate("Loot"), "yStart": 16, "width": 100 } ], "titleHeadings": [ { "caption": sprintf(translate("Resource Statistics (%(gathered)s / %(used)s)"), { "gathered": g_IncomeColor + translate("Gathered") + '[/color]', "used": g_OutcomeColor + translate("Used") + '[/color]' }), "yStart": 16, "width": (100 * 4 + 110) }, // width = 510 ], "counters": [ { "width": 100, "fn": calculateResources, "verticalOffset": 12 }, { "width": 100, "fn": calculateResources, "verticalOffset": 12 }, { "width": 100, "fn": calculateResources, "verticalOffset": 12 }, { "width": 100, "fn": calculateResources, "verticalOffset": 12 }, { "width": 110, "fn": calculateTotalResources, "verticalOffset": 12 }, { "width": 121, "fn": calculateTributeSent, "verticalOffset": 12 }, { "width": 100, "fn": calculateTreasureCollected, "verticalOffset": 12 }, { "width": 100, "fn": calculateLootCollected, "verticalOffset": 12 } ], "teamCounterFn": calculateResourcesTeam }, "market": { "headings": [ { "caption": translate("Player name"), "yStart": 26, "width": 200 }, { "caption": translate("Food exchanged"), "yStart": 16, "width": 100 }, { "caption": translate("Wood exchanged"), "yStart": 16, "width": 100 }, { "caption": translate("Stone exchanged"), "yStart": 16, "width": 100 }, { "caption": translate("Metal exchanged"), "yStart": 16, "width": 100 }, { "caption": translate("Barter efficiency"), "yStart": 16, "width": 100 }, { "caption": translate("Trade income"), "yStart": 16, "width": 100 } ], "titleHeadings": [], "counters": [ { "width": 100, "fn": calculateResourceExchanged, "verticalOffset": 12 }, { "width": 100, "fn": calculateResourceExchanged, "verticalOffset": 12 }, { "width": 100, "fn": calculateResourceExchanged, "verticalOffset": 12 }, { "width": 100, "fn": calculateResourceExchanged, "verticalOffset": 12 }, { "width": 100, "fn": calculateBarterEfficiency, "verticalOffset": 12 }, { "width": 100, "fn": calculateTradeIncome, "verticalOffset": 12 } ], "teamCounterFn": calculateMarketTeam }, "misc": { "headings": [ { "caption": translate("Player name"), "yStart": 26, "width": 200 }, { "caption": translate("Vegetarian\nratio"), "yStart": 16, "width": 100 }, { "caption": translate("Feminization"), "yStart": 16, "width": 100 }, { "caption": translate("Kill / Death\nratio"), "yStart": 16, "width": 100 }, { "caption": translate("Map\nexploration"), "yStart": 16, "width": 100 }, { "caption": translate("At peak"), "yStart": 34, "width": 100 }, { "caption": translate("At finish"), "yStart": 34, "width": 100 } ], "titleHeadings": [ { "caption": translate("Map control"), "xOffset": 400, "yStart": 16, "width": 200 } ], "counters": [ { "width": 100, "fn": calculateVegetarianRatio, "verticalOffset": 12 }, { "width": 100, "fn": calculateFeminization, "verticalOffset": 12 }, { "width": 100, "fn": calculateKillDeathRatio, "verticalOffset": 12 }, { "width": 100, "fn": calculateMapExploration, "verticalOffset": 12 }, { "width": 100, "fn": calculateMapPeakControl, "verticalOffset": 12 }, { "width": 100, "fn": calculateMapFinalControl, "verticalOffset": 12 } ], "teamCounterFn": calculateMiscellaneous } }; function resetGeneralPanel() { for (let h = 0; h < g_MaxHeadingTitle; ++h) { Engine.GetGUIObjectByName("titleHeading["+ h +"]").hidden = true; Engine.GetGUIObjectByName("Heading[" + h + "]").hidden = true; for (let p = 0; p < g_MaxPlayers; ++p) { Engine.GetGUIObjectByName("valueData[" + p + "][" + h + "]").hidden = true; for (let t = 0; t < g_MaxTeams; ++t) { Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + p + "][" + h + "]").hidden = true; Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + h + "]").hidden = true; } } } } function updateGeneralPanelHeadings(headings) { let left = 50; for (let h in headings) { let headerGUIName = "playerNameHeading"; if (h > 0) headerGUIName = "Heading[" + (h - 1) + "]"; let headerGUI = Engine.GetGUIObjectByName(headerGUIName); headerGUI.caption = headings[h].caption; headerGUI.size = left + " " + headings[h].yStart + " " + (left + headings[h].width) + " 100%"; headerGUI.hidden = false; if (headings[h].width < g_LongHeadingWidth) left += headings[h].width; } } function updateGeneralPanelTitles(titleHeadings) { let left = 250; for (let th in titleHeadings) { if (th >= g_MaxHeadingTitle) break; if (titleHeadings[th].xOffset) left += titleHeadings[th].xOffset; let headerGUI = Engine.GetGUIObjectByName("titleHeading["+ th +"]"); headerGUI.caption = titleHeadings[th].caption; headerGUI.size = left + " " + titleHeadings[th].yStart + " " + (left + titleHeadings[th].width) + " 100%"; headerGUI.hidden = false; if (titleHeadings[th].width < g_LongHeadingWidth) left += titleHeadings[th].width; } } function updateGeneralPanelCounter(counters) { let rowPlayerObjectWidth = 0; let left = 0; for (let p = 0; p < g_MaxPlayers; ++p) { left = 240; let counterObject; for (let w in counters) { counterObject = Engine.GetGUIObjectByName("valueData[" + p + "][" + w + "]"); counterObject.size = left + " " + counters[w].verticalOffset + " " + (left + counters[w].width) + " 100%"; counterObject.hidden = false; left += counters[w].width; } if (rowPlayerObjectWidth == 0) rowPlayerObjectWidth = left; let counterTotalObject; for (let t = 0; t < g_MaxTeams; ++t) { left = 240; for (let w in counters) { counterObject = Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + p + "][" + w + "]"); counterObject.size = left + " " + counters[w].verticalOffset + " " + (left + counters[w].width) + " 100%"; counterObject.hidden = false; if (g_Teams[t]) { let yStart = 25 + g_Teams[t] * (g_PlayerBoxYSize + g_PlayerBoxGap) + 3 + counters[w].verticalOffset; counterTotalObject = Engine.GetGUIObjectByName("valueDataTeam[" + t + "][" + w + "]"); counterTotalObject.size = (left + 20) + " " + yStart + " " + (left + counters[w].width) + " 100%"; counterTotalObject.hidden = false; } left += counters[w].width; } } } return rowPlayerObjectWidth; } function updateGeneralPanelTeams() { if (!g_Teams || g_WithoutTeam > 0) Engine.GetGUIObjectByName("noTeamsBox").hidden = false; if (!g_Teams) return; let yStart = g_TeamsBoxYStart + g_WithoutTeam * (g_PlayerBoxYSize + g_PlayerBoxGap); for (let i = 0; i < g_Teams.length; ++i) { if (!g_Teams[i]) continue; let teamBox = Engine.GetGUIObjectByName("teamBoxt["+i+"]"); teamBox.hidden = false; let teamBoxSize = teamBox.size; teamBoxSize.top = yStart; teamBox.size = teamBoxSize; yStart += 30 + g_Teams[i] * (g_PlayerBoxYSize + g_PlayerBoxGap) + 32; Engine.GetGUIObjectByName("teamNameHeadingt["+i+"]").caption = "Team "+(i+1); let teamHeading = Engine.GetGUIObjectByName("teamHeadingt["+i+"]"); let yStartTotal = 30 + g_Teams[i] * (g_PlayerBoxYSize + g_PlayerBoxGap) + 10; teamHeading.size = "50 " + yStartTotal + " 100% " + (yStartTotal + 20); teamHeading.caption = translate("Team total"); } // If there are no players without team, hide "player name" heading if (!g_WithoutTeam) Engine.GetGUIObjectByName("playerNameHeading").caption = ""; } function initPlayerBoxPositions() { for (let h = 0; h < g_MaxPlayers; ++h) { let playerBox = Engine.GetGUIObjectByName("playerBox[" + h + "]"); let boxSize = playerBox.size; boxSize.top += h * (g_PlayerBoxYSize + g_PlayerBoxGap); boxSize.bottom = boxSize.top + g_PlayerBoxYSize; playerBox.size = boxSize; for (let i = 0; i < g_MaxTeams; ++i) { let playerBoxt = Engine.GetGUIObjectByName("playerBoxt[" + i + "][" + h + "]"); boxSize = playerBoxt.size; boxSize.top += h * (g_PlayerBoxYSize + g_PlayerBoxGap); boxSize.bottom = boxSize.top + g_PlayerBoxYSize; playerBoxt.size = boxSize; } } } Index: ps/trunk/binaries/data/mods/public/gui/summary/summary.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/summary/summary.js (revision 18779) +++ ps/trunk/binaries/data/mods/public/gui/summary/summary.js (revision 18780) @@ -1,270 +1,270 @@ -const g_MaxHeadingTitle= 8; +const g_MaxHeadingTitle= 9; // const for filtering long collective headings const g_LongHeadingWidth = 250; const g_PlayerBoxYSize = 40; const g_PlayerBoxGap = 2; const g_PlayerBoxAlpha = " 50"; const g_PlayerColorBoxAlpha = " 255"; const g_TeamsBoxYStart = 40; // Colors used for units and buildings const g_TrainedColor = '[color="201 255 200"]'; const g_LostColor = '[color="255 213 213"]'; const g_KilledColor = '[color="196 198 255"]'; const g_CapturedColor = '[color="255 255 157"]'; const g_BuildingsTypes = [ "total", "House", "Economic", "Outpost", "Military", "Fortress", "CivCentre", "Wonder" ]; -const g_UnitsTypes = [ "total", "Infantry", "Worker", "Cavalry", "Champion", "Hero", "Ship", "Trader" ]; +const g_UnitsTypes = [ "total", "Infantry", "Worker", "Cavalry", "Champion", "Hero", "Siege", "Ship", "Trader" ]; const g_ResourcesTypes = [ "food", "wood", "stone", "metal" ]; // Colors used for gathered and traded resources const g_IncomeColor = '[color="201 255 200"]'; const g_OutcomeColor = '[color="255 213 213"]'; const g_InfiniteSymbol = "\u221E"; var g_CivData = loadCivData(); var g_Teams = []; // TODO set g_PlayerCount as playerCounters.length var g_PlayerCount = 0; // Count players without team (or all if teams are not displayed) var g_WithoutTeam = 0; var g_GameData; function selectPanel(panel) { // TODO: move panel buttons to a custom parent object for (let button of Engine.GetGUIObjectByName("summaryWindow").children) if (button.name.endsWith("PanelButton")) button.sprite = "BackgroundTab"; panel.sprite = "ForegroundTab"; adjustTabDividers(panel.size); updatePanelData(g_ScorePanelsData[panel.name.substr(0, panel.name.length - "PanelButton".length)]); } function adjustTabDividers(tabSize) { let leftSpacer = Engine.GetGUIObjectByName("tabDividerLeft"); let rightSpacer = Engine.GetGUIObjectByName("tabDividerRight"); leftSpacer.size = [ 20, leftSpacer.size.top, tabSize.left + 2, leftSpacer.size.bottom ].join(" "); rightSpacer.size = [ tabSize.right - 2, rightSpacer.size.top, "100%-20", rightSpacer.size.bottom ].join(" "); } function updatePanelData(panelInfo) { resetGeneralPanel(); resetDataHelpers(); updateGeneralPanelHeadings(panelInfo.headings); updateGeneralPanelTitles(panelInfo.titleHeadings); let rowPlayerObjectWidth = updateGeneralPanelCounter(panelInfo.counters); updateGeneralPanelTeams(); let playerBoxesCounts = [ ]; for (let i = 0; i < g_PlayerCount; ++i) { let playerState = g_GameData.sim.playerStates[i+1]; if (!playerBoxesCounts[playerState.team+1]) playerBoxesCounts[playerState.team+1] = 1; else playerBoxesCounts[playerState.team+1] += 1; let positionObject = playerBoxesCounts[playerState.team+1] - 1; let rowPlayer = "playerBox[" + positionObject + "]"; let playerOutcome = "playerOutcome[" + positionObject + "]"; let playerNameColumn = "playerName[" + positionObject + "]"; let playerCivicBoxColumn = "civIcon[" + positionObject + "]"; let playerCounterValue = "valueData[" + positionObject + "]"; if (playerState.team != -1) { rowPlayer = "playerBoxt[" + playerState.team + "][" + positionObject + "]"; playerOutcome = "playerOutcomet[" + playerState.team + "][" + positionObject + "]"; playerNameColumn = "playerNamet[" + playerState.team + "][" + positionObject + "]"; playerCivicBoxColumn = "civIcont[" + playerState.team + "][" + positionObject + "]"; playerCounterValue = "valueDataTeam[" + playerState.team + "][" + positionObject + "]"; } let colorString = "color: " + Math.floor(playerState.color.r * 255) + " " + Math.floor(playerState.color.g * 255) + " " + Math.floor(playerState.color.b * 255); let rowPlayerObject = Engine.GetGUIObjectByName(rowPlayer); rowPlayerObject.hidden = false; rowPlayerObject.sprite = colorString + g_PlayerBoxAlpha; let boxSize = rowPlayerObject.size; boxSize.right = rowPlayerObjectWidth; rowPlayerObject.size = boxSize; let outcome = Engine.GetGUIObjectByName(playerOutcome); if (playerState.state == "won") { outcome.sprite = "stretched:session/icons/stances/violent.png"; outcome.tooltip = translate("Victory"); } else if (playerState.state == "defeated") { outcome.sprite = "stretched:session/icons/stances/passive.png"; outcome.tooltip = translate("Defeated"); } Engine.GetGUIObjectByName(playerNameColumn).caption = g_GameData.sim.playerStates[i+1].name; let civIcon = Engine.GetGUIObjectByName(playerCivicBoxColumn); civIcon.sprite = "stretched:" + g_CivData[playerState.civ].Emblem; civIcon.tooltip = g_CivData[playerState.civ].Name; updateCountersPlayer(playerState, panelInfo.counters, playerCounterValue); calculateTeamCounters(playerState); } let teamCounterFn = panelInfo.teamCounterFn; if (g_Teams && teamCounterFn) teamCounterFn(panelInfo.counters); } function confirmStartReplay() { if (Engine.HasXmppClient()) messageBox( 400, 200, translate("Are you sure you want to quit the lobby?"), translate("Confirmation"), [translate("No"), translate("Yes")], [null, startReplay] ); else startReplay(); } function continueButton() { if (g_GameData.gui.isInGame) Engine.PopGuiPageCB(0); else if (g_GameData.gui.isReplay) Engine.SwitchGuiPage("page_replaymenu.xml", { "replaySelectionData": g_GameData.gui.replaySelectionData }); else if (Engine.HasXmppClient()) Engine.SwitchGuiPage("page_lobby.xml"); else Engine.SwitchGuiPage("page_pregame.xml"); } function startReplay() { if (Engine.HasXmppClient()) Engine.StopXmppClient(); Engine.StartVisualReplay(g_GameData.gui.replayDirectory); Engine.SwitchGuiPage("page_loading.xml", { "attribs": Engine.GetReplayAttributes(g_GameData.gui.replayDirectory), "isNetworked": false, "playerAssignments": { "local": { "name": singleplayerName(), "player": -1 } }, "savedGUIData": "", "isReplay": true, "replaySelectionData": g_GameData.gui.replaySelectionData }); } function init(data) { g_GameData = data; let assignedState = g_GameData.sim.playerStates[g_GameData.gui.assignedPlayer || -1]; Engine.GetGUIObjectByName("summaryText").caption = g_GameData.gui.isInGame ? translate("Current Scores") : g_GameData.gui.isReplay ? translate("Scores at the end of the game.") : g_GameData.gui.disconnected ? translate("You have been disconnected.") : !assignedState ? translate("You have left the game.") : assignedState.state == "won" ? translate("You have won the battle!") : assignedState.state == "defeated" ? translate("You have been defeated...") : translate("You have abandoned the game."); initPlayerBoxPositions(); Engine.GetGUIObjectByName("timeElapsed").caption = sprintf( translate("Game time elapsed: %(time)s"), { "time": timeToString(g_GameData.sim.timeElapsed) }); let mapType = g_Settings.MapTypes.find(mapType => mapType.Name == g_GameData.sim.mapSettings.mapType); let mapSize = g_Settings.MapSizes.find(size => size.Tiles == g_GameData.sim.mapSettings.Size || 0); Engine.GetGUIObjectByName("mapName").caption = sprintf( translate("%(mapName)s - %(mapType)s"), { "mapName": translate(g_GameData.sim.mapSettings.Name), "mapType": mapSize ? mapSize.LongName : (mapType ? mapType.Title : "") }); Engine.GetGUIObjectByName("replayButton").hidden = g_GameData.gui.isInGame || !g_GameData.gui.replayDirectory; // Panels g_PlayerCount = g_GameData.sim.playerStates.length - 1; if (g_GameData.sim.mapSettings.LockTeams) { // Count teams for (let t = 0; t < g_PlayerCount; ++t) { let playerTeam = g_GameData.sim.playerStates[t+1].team; g_Teams[playerTeam] = (g_Teams[playerTeam] || 0) + 1; } if (g_Teams.length == g_PlayerCount) g_Teams = false; // Each player has his own team. Displaying teams makes no sense. } else g_Teams = false; // Erase teams data if teams are not displayed if (!g_Teams) { for (let p = 0; p < g_PlayerCount; ++p) g_GameData.sim.playerStates[p+1].team = -1; } g_WithoutTeam = g_PlayerCount; if (g_Teams) { // Count players without team (or all if teams are not displayed) for (let i = 0; i < g_Teams.length; ++i) g_WithoutTeam -= g_Teams[i] ? g_Teams[i] : 0; } selectPanel(Engine.GetGUIObjectByName("scorePanelButton")); } Index: ps/trunk/binaries/data/mods/public/gui/summary/summary.xml =================================================================== --- ps/trunk/binaries/data/mods/public/gui/summary/summary.xml (revision 18779) +++ ps/trunk/binaries/data/mods/public/gui/summary/summary.xml (revision 18780) @@ -1,165 +1,165 @@