Index: ps/trunk/binaries/data/mods/public/gui/common/tab_buttons.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/common/tab_buttons.js (revision 23788) +++ ps/trunk/binaries/data/mods/public/gui/common/tab_buttons.js (revision 23789) @@ -1,79 +1,103 @@ /** * Number of categories. */ var g_TabCategoryCount; /** + * Align the buttons horizontally or vertically. + */ +var g_TabHorizontal; + +/** * Index of the currently visible tab, set first tab as default. */ var g_TabCategorySelected = 0; /** * Function to be executed when selecting a tab. The new category index is passed. */ var g_OnSelectTab; /** * Create tab buttons. * * @param {Array} categoriesData - Arrays of objects containing for every tab a (translated) label and tooltip. - * @param {number} buttonHeight - Vertical distance between the top and bottom of a button. - * @param {number} spacing - Vertical distance between two buttons. + * @param {boolean} horizontal - Have the tabs horizontally or vertically aligned. + * @param {number} buttonSize - Size of a button in the specified direction. + * @param {number} spacing - Distance between two buttons in the specified direction. * @param {function} onPress - Function to be executed when a button is pressed, it gets the new category index passed. * @param {function} onSelect - Function to be executed whenever the selection changes (so also for scrolling), it gets the new category index passed. */ -function placeTabButtons(categoriesData, buttonHeight, spacing, onPress, onSelect) +function placeTabButtons(categoriesData, horizontal, buttonSize, spacing, onPress, onSelect) { - g_OnSelectTab = onSelect; g_TabCategoryCount = categoriesData.length; + g_TabHorizontal = horizontal; + g_OnSelectTab = onSelect; for (let category in categoriesData) { let button = Engine.GetGUIObjectByName("tabButton[" + category + "]"); if (!button) { warn("Too few tab-buttons!"); break; } + button.style = "ModernTabButton" + (horizontal ? "Horizontal" : "Vertical"); button.hidden = false; let size = button.size; - size.top = category * (buttonHeight + spacing) + spacing / 2; - size.bottom = size.top + buttonHeight; + if (horizontal) + { + size.left = category * (buttonSize + spacing) + spacing / 2; + size.right = size.left + buttonSize; + size.rright = 0; + } + else + { + size.top = category * (buttonSize + spacing) + spacing / 2; + size.bottom = size.top + buttonSize; + size.rbottom = 0; + } button.size = size; button.tooltip = categoriesData[category].tooltip || ""; let categoryNum = +category; button.onPress = () => { onPress(categoryNum); }; Engine.GetGUIObjectByName("tabButtonText[" + category + "]").caption = categoriesData[category].label; } selectPanel(g_TabCategorySelected); } /** * Show next/previous panel. * @param direction - +1/-1 for forward/backward. */ function selectNextTab(direction) { if (g_TabCategoryCount) selectPanel(g_TabCategorySelected === undefined ? direction > 0 ? 0 : g_TabCategoryCount - 1 : (g_TabCategorySelected + direction + g_TabCategoryCount) % g_TabCategoryCount); } function selectPanel(category) { g_TabCategorySelected = category; Engine.GetGUIObjectByName("tabButtons").children.forEach((button, j) => { - button.sprite = category == j ? "ModernTabVerticalForeground" : "ModernTabVerticalBackground"; + button.sprite = g_TabHorizontal ? + category == j ? + "ModernTabHorizontalForeground" : + "ModernTabHorizontalBackground" : + category == j ? + "ModernTabVerticalForeground" : + "ModernTabVerticalBackground"; }); if (g_OnSelectTab) g_OnSelectTab(category); } Index: ps/trunk/binaries/data/mods/public/gui/common/tab_buttons.xml =================================================================== --- ps/trunk/binaries/data/mods/public/gui/common/tab_buttons.xml (revision 23788) +++ ps/trunk/binaries/data/mods/public/gui/common/tab_buttons.xml (revision 23789) @@ -1,20 +1,20 @@ selectNextTab(1); selectNextTab(-1); - Index: ps/trunk/binaries/data/mods/public/gui/credits/credits.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/credits/credits.js (revision 23788) +++ ps/trunk/binaries/data/mods/public/gui/credits/credits.js (revision 23789) @@ -1,98 +1,99 @@ /** * Order in which the tabs should show up. */ var g_OrderTabNames = [ "special", "programming", "art", "history", "balancing", "community", "translators", "donators" ]; /** * Array of Objects containg all relevant data per tab. */ var g_PanelData = []; /** * Vertical size of a tab button. */ var g_TabButtonHeight = 35; /** * Vertical space between two tab buttons. */ var g_TabButtonDist = 5; function init() { // Load credits list from the disk and parse them for (let category of g_OrderTabNames) { let json = Engine.ReadJSONFile("gui/credits/texts/" + category + ".json"); if (!json || !json.Content) { error("Could not load credits for " + category + "!"); continue; } translateObjectKeys(json, ["Title", "Subtitle"]); g_PanelData.push({ "label": json.Title || category, "content": parseHelper(json.Content) }); } placeTabButtons( g_PanelData, + false, g_TabButtonHeight, g_TabButtonDist, selectPanel, category => { Engine.GetGUIObjectByName("creditsText").caption = g_PanelData[category].content; }); } // Run through a "Content" list and parse elements for formatting and translation function parseHelper(list) { let result = ""; for (let object of list) { if (object.LangName) result += setStringTags(object.LangName + "\n", { "font": "sans-bold-stroke-14" }); if (object.Title) result += setStringTags(object.Title + "\n", { "font": "sans-bold-stroke-14" }); if (object.Subtitle) result += setStringTags(object.Subtitle + "\n", { "font": "sans-bold-14" }); if (object.List) { for (let element of object.List) { let credit; if (element.nick && element.name) credit = sprintf(translate("%(nick)s - %(name)s"), { "nick": element.nick, "name": element.name }); else if (element.nick) credit = element.nick; else if (element.name) credit = element.name; if (credit) result += setStringTags(credit + "\n", { "font": "sans-14" }); } result += "\n"; } if (object.Content) result += "\n" + parseHelper(object.Content) + "\n"; } return result; } Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/GameSettingsTabs.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/GameSettingsTabs.js (revision 23788) +++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/GameSettingsTabs.js (revision 23789) @@ -1,97 +1,98 @@ class GameSettingTabs { constructor(setupWindow, lobbyButton) { this.lobbyButton = lobbyButton; this.tabSelectHandlers = new Set(); this.tabsResizeHandlers = new Set(); this.settingsTabButtonsFrame = Engine.GetGUIObjectByName("settingTabButtonsFrame"); for (let tab in g_GameSettingsLayout) g_GameSettingsLayout[tab].tooltip = sprintf(this.ToggleTooltip, { "name": g_GameSettingsLayout[tab].label }) + colorizeHotkey("\n" + this.HotkeyNextTooltip, this.ConfigNameHotkeyNext) + colorizeHotkey("\n" + this.HotkeyPreviousTooltip, this.ConfigNameHotkeyPrevious); setupWindow.registerLoadHandler(this.onLoad.bind(this)); Engine.SetGlobalHotkey("cancel", "Press", selectPanel); } registerTabsResizeHandler(handler) { this.tabsResizeHandlers.add(handler); } registerTabSelectHandler(handler) { this.tabSelectHandlers.add(handler); } onLoad() { placeTabButtons( g_GameSettingsLayout, + false, this.TabButtonHeight, this.TabButtonMargin, this.onTabPress.bind(this), this.onTabSelect.bind(this)); this.resize(); if (!g_IsController) selectPanel(); } resize() { let size = this.settingsTabButtonsFrame.size; size.bottom = size.top + g_GameSettingsLayout.length * (this.TabButtonHeight + this.TabButtonMargin); if (!this.lobbyButton.lobbyButton.hidden) { let lobbyButtonSize = this.lobbyButton.lobbyButton.parent.size; size.right -= lobbyButtonSize.right - lobbyButtonSize.left + this.LobbyButtonMargin; } this.settingsTabButtonsFrame.size = size; for (let handler of this.tabsResizeHandlers) handler(this.settingsTabButtonsFrame); } onTabPress(category) { selectPanel(category == g_TabCategorySelected ? undefined : category); } onTabSelect() { for (let handler of this.tabSelectHandlers) handler(); } } GameSettingTabs.prototype.ToggleTooltip = translate("Click to toggle the %(name)s settings tab."); GameSettingTabs.prototype.HotkeyNextTooltip = translate("Press %(hotkey)s to move to the next settings tab."); GameSettingTabs.prototype.HotkeyPreviousTooltip = translate("Press %(hotkey)s to move to the previous settings tab."); GameSettingTabs.prototype.ConfigNameHotkeyNext = "tab.next"; GameSettingTabs.prototype.ConfigNameHotkeyPrevious = "tab.prev"; GameSettingTabs.prototype.TabButtonHeight = 30; GameSettingTabs.prototype.TabButtonMargin = 4; /** * Horizontal space between tab buttons and lobby button. */ GameSettingTabs.prototype.LobbyButtonMargin = 8; Index: ps/trunk/binaries/data/mods/public/gui/options/options.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/options/options.js (revision 23788) +++ ps/trunk/binaries/data/mods/public/gui/options/options.js (revision 23789) @@ -1,387 +1,388 @@ /** * Translated JSON file contents. */ var g_Options; /** * Names of config keys that have changed, value returned when closing the page. */ var g_ChangedKeys; /** * Vertical size of a tab button. */ var g_TabButtonHeight = 30; /** * Vertical space between two tab buttons. */ var g_TabButtonDist = 5; /** * Vertical distance between the top of the page and the first option. */ var g_OptionControlOffset = 5; /** * Vertical size of each option control. */ var g_OptionControlHeight = 26; /** * Vertical distance between two consecutive options. */ var g_OptionControlDist = 2; /** * Horizontal indentation to distinguish options that depend on another option. */ var g_DependentLabelIndentation = 25; /** * Color used to indicate that the string entered by the player isn't a sane color. */ var g_InsaneColor = "255 0 255"; /** * Defines the parsing of config strings and GUI control interaction for the different option types. * * @property configToValue - parses a string from the user config to a value of the declared type. * @property valueToGui - sets the GUI control to display the given value. * @property guiToValue - returns the value of the GUI control. * @property guiSetter - event name that should be considered a value change of the GUI control. * @property initGUI - sets properties of the GUI control that are independent of the current value. * @property sanitizeValue - Displays a visual clue if the entered value is invalid and returns a sane value. * @property tooltip - appends a custom tooltip to the given option description depending on the current value. */ var g_OptionType = { "boolean": { "configToValue": config => config == "true", "valueToGui": (value, control) => { control.checked = value; }, "guiToValue": control => control.checked, "guiSetter": "onPress" }, "string": { "configToValue": value => value, "valueToGui": (value, control) => { control.caption = value; }, "guiToValue": control => control.caption, "guiSetter": "onTextEdit" }, "color": { "configToValue": value => value, "valueToGui": (value, control) => { control.caption = value; }, "guiToValue": control => control.caption, "guiSetter": "onTextEdit", "sanitizeValue": (value, control, option) => { let color = guiToRgbColor(value); let sanitized = rgbToGuiColor(color); if (control) { control.sprite = sanitized == value ? "ModernDarkBoxWhite" : "ModernDarkBoxWhiteInvalid"; control.children[1].sprite = sanitized == value ? "color:" + value : "color:" + g_InsaneColor; } return sanitized; }, "tooltip": (value, option) => sprintf(translate("Default: %(value)s"), { "value": Engine.ConfigDB_GetValue("default", option.config) }) }, "number": { "configToValue": value => value, "valueToGui": (value, control) => { control.caption = value; }, "guiToValue": control => control.caption, "guiSetter": "onTextEdit", "sanitizeValue": (value, control, option) => { let sanitized = Math.min(option.max !== undefined ? option.max : +Infinity, Math.max(option.min !== undefined ? option.min : -Infinity, isNaN(+value) ? 0 : value)); if (control) control.sprite = sanitized == value ? "ModernDarkBoxWhite" : "ModernDarkBoxWhiteInvalid"; return sanitized; }, "tooltip": (value, option) => sprintf( option.min !== undefined && option.max !== undefined ? translateWithContext("option number", "Min: %(min)s, Max: %(max)s") : option.min !== undefined && option.max === undefined ? translateWithContext("option number", "Min: %(min)s") : option.min === undefined && option.max !== undefined ? translateWithContext("option number", "Max: %(max)s") : "", { "min": option.min, "max": option.max }) }, "dropdown": { "configToValue": value => value, "valueToGui": (value, control) => { control.selected = control.list_data.indexOf(value); }, "guiToValue": control => control.list_data[control.selected], "guiSetter": "onSelectionChange", "initGUI": (option, control) => { control.list = option.list.map(e => e.label); control.list_data = option.list.map(e => e.value); control.onHoverChange = () => { let item = option.list[control.hovered]; control.tooltip = item && item.tooltip || option.tooltip; }; } }, "slider": { "configToValue": value => +value, "valueToGui": (value, control) => { control.value = +value; }, "guiToValue": control => control.value, "guiSetter": "onValueChange", "initGUI": (option, control) => { control.max_value = option.max; control.min_value = option.min; }, "tooltip": (value, option) => sprintf(translateWithContext("slider number", "Value: %(val)s (min: %(min)s, max: %(max)s)"), { "val": value.toFixed(2), "min": option.min.toFixed(2), "max": option.max.toFixed(2) }) } }; function init(data, hotloadData) { g_ChangedKeys = hotloadData ? hotloadData.changedKeys : new Set(); g_TabCategorySelected = hotloadData ? hotloadData.tabCategorySelected : 0; g_Options = Engine.ReadJSONFile("gui/options/options.json"); translateObjectKeys(g_Options, ["label", "tooltip"]); deepfreeze(g_Options); placeTabButtons( g_Options, + false, g_TabButtonHeight, g_TabButtonDist, selectPanel, displayOptions); } function getHotloadData() { return { "tabCategorySelected": g_TabCategorySelected, "changedKeys": g_ChangedKeys }; } /** * Sets up labels and controls of all options of the currently selected category. */ function displayOptions() { // Hide all controls for (let body of Engine.GetGUIObjectByName("option_controls").children) { body.hidden = true; for (let control of body.children) control.hidden = true; } // Initialize label and control of each option for this category for (let i = 0; i < g_Options[g_TabCategorySelected].options.length; ++i) { // Position vertically let body = Engine.GetGUIObjectByName("option_control[" + i + "]"); let bodySize = body.size; bodySize.top = g_OptionControlOffset + i * (g_OptionControlHeight + g_OptionControlDist); bodySize.bottom = bodySize.top + g_OptionControlHeight; body.size = bodySize; body.hidden = false; // Load option data let option = g_Options[g_TabCategorySelected].options[i]; let optionType = g_OptionType[option.type]; let value = optionType.configToValue(Engine.ConfigDB_GetValue("user", option.config)); // Setup control let control = Engine.GetGUIObjectByName("option_control_" + option.type + "[" + i + "]"); control.tooltip = option.tooltip + (optionType.tooltip ? "\n" + optionType.tooltip(value, option) : ""); control.hidden = false; if (optionType.initGUI) optionType.initGUI(option, control); control[optionType.guiSetter] = function() {}; optionType.valueToGui(value, control); if (optionType.sanitizeValue) optionType.sanitizeValue(value, control, option); control[optionType.guiSetter] = function() { let value = optionType.guiToValue(control); if (optionType.sanitizeValue) optionType.sanitizeValue(value, control, option); control.tooltip = option.tooltip + (optionType.tooltip ? "\n" + optionType.tooltip(value, option) : ""); Engine.ConfigDB_CreateValue("user", option.config, String(value)); Engine.ConfigDB_SetChanges("user", true); g_ChangedKeys.add(option.config); fireConfigChangeHandlers(new Set([option.config])); if (option.function) Engine[option.function](value); enableButtons(); }; // Setup label let label = Engine.GetGUIObjectByName("option_label[" + i + "]"); label.caption = option.label; label.tooltip = option.tooltip; label.hidden = false; let labelSize = label.size; labelSize.left = option.dependencies ? g_DependentLabelIndentation : 0; labelSize.rright = control.size.rleft; label.size = labelSize; } enableButtons(); } /** * Enable exactly the buttons whose dependencies are met. */ function enableButtons() { g_Options[g_TabCategorySelected].options.forEach((option, i) => { let enabled = !option.dependencies || option.dependencies.every(config => Engine.ConfigDB_GetValue("user", config) == "true"); Engine.GetGUIObjectByName("option_label[" + i + "]").enabled = enabled; Engine.GetGUIObjectByName("option_control_" + option.type + "[" + i + "]").enabled = enabled; }); let hasChanges = Engine.ConfigDB_HasChanges("user"); Engine.GetGUIObjectByName("revertChanges").enabled = hasChanges; Engine.GetGUIObjectByName("saveChanges").enabled = hasChanges; } function setDefaults() { messageBox( 500, 200, translate("Resetting the options will erase your saved settings. Do you want to continue?"), translate("Warning"), [translate("No"), translate("Yes")], [null, reallySetDefaults] ); } function reallySetDefaults() { for (let category in g_Options) for (let option of g_Options[category].options) { Engine.ConfigDB_RemoveValue("user", option.config); g_ChangedKeys.add(option.config); } Engine.ConfigDB_WriteFile("user", "config/user.cfg"); revertChanges(); } function revertChanges() { Engine.ConfigDB_Reload("user"); Engine.ConfigDB_SetChanges("user", false); for (let category in g_Options) for (let option of g_Options[category].options) if (option.function) Engine[option.function]( g_OptionType[option.type].configToValue( Engine.ConfigDB_GetValue("user", option.config))); displayOptions(); } function saveChanges() { for (let category in g_Options) for (let i = 0; i < g_Options[category].options.length; ++i) { let option = g_Options[category].options[i]; let optionType = g_OptionType[option.type]; if (!optionType.sanitizeValue) continue; let value = optionType.configToValue(Engine.ConfigDB_GetValue("user", option.config)); if (value == optionType.sanitizeValue(value, undefined, option)) continue; selectPanel(category); messageBox( 500, 200, translate("Some setting values are invalid! Are you sure to save them?"), translate("Warning"), [translate("No"), translate("Yes")], [null, reallySaveChanges] ); return; } reallySaveChanges(); } function reallySaveChanges() { Engine.ConfigDB_WriteFile("user", "config/user.cfg"); Engine.ConfigDB_SetChanges("user", false); enableButtons(); } /** * Close GUI page and inform the parent GUI page which options changed. **/ function closePage() { if (Engine.ConfigDB_HasChanges("user")) messageBox( 500, 200, translate("You have unsaved changes, do you want to close this window?"), translate("Warning"), [translate("No"), translate("Yes")], [null, closePageWithoutConfirmation]); else closePageWithoutConfirmation(); } function closePageWithoutConfirmation() { Engine.PopGuiPage(g_ChangedKeys); } Index: ps/trunk/binaries/data/mods/public/gui/summary/layout.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/summary/layout.js (revision 23788) +++ ps/trunk/binaries/data/mods/public/gui/summary/layout.js (revision 23789) @@ -1,384 +1,400 @@ -var getScorePanelsData = () => ({ - "score": { - "caption": translate("Score"), +/** + * Horizontal size of a tab button. + */ +var g_TabButtonWidth = 118; + +/** + * Horizontal space between two tab buttons. + */ +var g_TabButtonDist = 6; + +var getScorePanelsData = () => [ + { + "label": translate("Score"), "headings": [ { "identifier": "playername", "caption": translate("Player name"), "yStart": 26, "width": 200 }, { "identifier": "totalScore", "caption": translate("Total score"), "yStart": 16, "width": 100 }, { "identifier": "economyScore", "caption": translate("Economy score"), "yStart": 16, "width": 100 }, { "identifier": "militaryScore", "caption": translate("Military score"), "yStart": 16, "width": 100 }, { "identifier": "explorationScore", "caption": translate("Exploration score"), "yStart": 16, "width": 100 } ], "titleHeadings": [], "counters": [ { "width": 100, "fn": calculateScoreTotal, "verticalOffset": 12 }, { "width": 100, "fn": calculateEconomyScore, "verticalOffset": 12 }, { "width": 100, "fn": calculateMilitaryScore, "verticalOffset": 12 }, { "width": 100, "fn": calculateExplorationScore, "verticalOffset": 12 } ], "teamCounterFn": calculateScoreTeam }, - "buildings": { - "caption": translate("Structures"), + { + "label": translate("Structures"), "headings": [ { "identifier": "playername", "caption": translate("Player name"), "yStart": 26, "width": 200 }, { "identifier": "total", "caption": translate("Total"), "yStart": 34, "width": 105 }, { "identifier": "House", "caption": translate("Houses"), "yStart": 34, "width": 85 }, { "identifier": "Economic", "caption": translate("Economic"), "yStart": 34, "width": 85 }, { "identifier": "Outpost", "caption": translate("Outposts"), "yStart": 34, "width": 85 }, { "identifier": "Military", "caption": translate("Military"), "yStart": 34, "width": 85 }, { "identifier": "Fortress", "caption": translate("Fortresses"), "yStart": 34, "width": 85 }, { "identifier": "CivCentre", "caption": translate("Civ centers"), "yStart": 34, "width": 85 }, { "identifier": "Wonder", "caption": translate("Wonders"), "yStart": 34, "width": 85 } ], "titleHeadings": [ { "caption": sprintf(translate("Structure Statistics (%(constructed)s / %(destroyed)s / %(captured)s / %(lost)s)"), { "constructed": getColoredTypeTranslation("constructed"), "destroyed": getColoredTypeTranslation("destroyed"), "captured": getColoredTypeTranslation("captured"), "lost": getColoredTypeTranslation("lost") }), "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": { - "caption": translate("Units"), + { + "label": translate("Units"), "headings": [ { "identifier": "playername", "caption": translate("Player name"), "yStart": 26, "width": 200 }, { "identifier": "total", "caption": translate("Total"), "yStart": 34, "width": 105 }, { "identifier": "Infantry", "caption": translate("Infantry"), "yStart": 34, "width": 85 }, { "identifier": "Worker", "caption": translate("Worker"), "yStart": 34, "width": 85 }, { "identifier": "Cavalry", "caption": translate("Cavalry"), "yStart": 34, "width": 85 }, { "identifier": "Champion", "caption": translate("Champion"), "yStart": 34, "width": 85 }, { "identifier": "Hero", "caption": translate("Heroes"), "yStart": 34, "width": 85 }, { "identifier": "Siege", "caption": translate("Siege"), "yStart": 34, "width": 85 }, { "identifier": "Ship", "caption": translate("Navy"), "yStart": 34, "width": 85 }, { "identifier": "Trader", "caption": translate("Traders"), "yStart": 34, "width": 85 } ], "titleHeadings": [ { "caption": sprintf(translate("Unit Statistics (%(trained)s / %(killed)s / %(captured)s / %(lost)s)"), { "trained": getColoredTypeTranslation("trained"), "killed": getColoredTypeTranslation("killed"), "captured": getColoredTypeTranslation("captured"), "lost": getColoredTypeTranslation("lost") }), "yStart": 16, "width": 85 * 8 + 105 }, // width = 785 ], "counters": [ { "width": 105, "fn": calculateUnitsWithCaptured, "verticalOffset": 3 }, { "width": 85, "fn": calculateUnits, "verticalOffset": 3 }, { "width": 85, "fn": calculateUnits, "verticalOffset": 3 }, { "width": 85, "fn": calculateUnits, "verticalOffset": 3 }, { "width": 85, "fn": calculateUnits, "verticalOffset": 3 }, { "width": 85, "fn": calculateUnits, "verticalOffset": 3 }, { "width": 85, "fn": calculateUnitsWithCaptured, "verticalOffset": 3 }, { "width": 85, "fn": calculateUnits, "verticalOffset": 3 }, { "width": 85, "fn": calculateUnits, "verticalOffset": 3 } ], "teamCounterFn": calculateUnitsTeam }, - "resources": { - "caption": translate("Resources"), + { + "label": translate("Resources"), "headings": [ { "identifier": "playername", "caption": translate("Player name"), "yStart": 26, "width": 200 }, { "identifier": "total", "caption": translate("Total"), "yStart": 34, "width": 110 }, ...g_ResourceData.GetResources().map(res => ({ "identifier": res.code, "caption": resourceNameFirstWord(res.code), "yStart": 34, "width": 100 })), { "identifier": "tributes", "caption": translate("Tributes"), "headerCaption": sprintf(translate("Tributes \n(%(sent)s / %(received)s)"), { "sent": getColoredTypeTranslation("sent"), "received": getColoredTypeTranslation("received") }), "yStart": 16, "width": 121 }, { "identifier": "treasuresCollected", "caption": translate("Treasures collected"), "yStart": 16, "width": 85 }, { "identifier": "loot", "caption": translate("Loot"), "yStart": 16, "width": 85 }, { "identifier": "livestock", "caption": translate("Livestock bred"), "yStart": 16, "width": 85 } ], "titleHeadings": [ { "caption": sprintf(translate("Resource Statistics (%(gathered)s / %(used)s)"), { "gathered": getColoredTypeTranslation("gathered"), "used": getColoredTypeTranslation("used") }), "yStart": 16, "width": 100 * g_ResourceData.GetCodes().length + 110 }, ], "counters": [ { "width": 110, "fn": calculateTotalResources, "verticalOffset": 12 }, ...g_ResourceData.GetCodes().map(code => ({ "fn": calculateResources, "verticalOffset": 12, "width": 100 })), { "width": 121, "fn": calculateTributeSent, "verticalOffset": 12 }, { "width": 85, "fn": calculateTreasureCollected, "verticalOffset": 12 }, { "width": 85, "fn": calculateLootCollected, "verticalOffset": 12 }, { "width": 85, "fn": calculateLivestockTrained, "verticalOffset": 12 } ], "teamCounterFn": calculateResourcesTeam }, - "market": { - "caption": translate("Market"), + { + "label": translate("Market"), "headings": [ { "identifier": "playername", "caption": translate("Player name"), "yStart": 26, "width": 200 }, { "identifier": "tradeIncome", "caption": translate("Trade income"), "yStart": 16, "width": 100 }, { "identifier": "barterEfficency", "caption": translate("Barter efficiency"), "yStart": 16, "width": 100, "format": "PERCENTAGE" }, ...g_ResourceData.GetResources().map(res => { return { "identifier": res.code, "caption": // Translation: use %(resourceWithinSentence)s if needed sprintf(translate("%(resourceFirstWord)s exchanged"), { "resourceFirstWord": resourceNameFirstWord(res.code), "resourceWithinSentence": resourceNameWithinSentence(res.code) }), "yStart": 16, "width": 100 }; }) ], "titleHeadings": [], "counters": [ { "width": 100, "fn": calculateTradeIncome, "verticalOffset": 12 }, { "width": 100, "fn": calculateBarterEfficiency, "verticalOffset": 12 }, ...g_ResourceData.GetCodes().map(code => ({ "width": 100, "fn": calculateResourceExchanged, "verticalOffset": 12 })) ], "teamCounterFn": calculateMarketTeam }, - "misc": { - "caption": translate("Miscellaneous"), + { + "label": translate("Miscellaneous"), "headings": [ { "identifier": "playername", "caption": translate("Player name"), "yStart": 26, "width": 200 }, { "identifier": "killDeath", "caption": translate("Kill / Death ratio"), "yStart": 16, "width": 100, "format": "DECIMAL2" }, { "identifier": "mapControlPeak", "caption": translate("Map control (peak)"), "yStart": 16, "width": 100, "format": "PERCENTAGE" }, { "identifier": "mapControl", "caption": translate("Map control (finish)"), "yStart": 16, "width": 100, "format": "PERCENTAGE" }, { "identifier": "mapExploration", "caption": translate("Map exploration"), "yStart": 16, "width": 100, "format": "PERCENTAGE" }, { "identifier": "vegetarianRatio", "caption": translate("Vegetarian ratio"), "yStart": 16, "width": 100, "format": "PERCENTAGE" }, { "identifier": "feminization", "caption": translate("Feminization"), "yStart": 16, "width": 100, "format": "PERCENTAGE" }, { "identifier": "bribes", "caption": translate("Bribes"), "headerCaption": sprintf(translate("Bribes\n(%(succeeded)s / %(failed)s)"), { "succeeded": getColoredTypeTranslation("succeeded"), "failed": getColoredTypeTranslation("failed") }), "yStart": 16, "width": 139 } ], "titleHeadings": [], "counters": [ { "width": 100, "fn": calculateKillDeathRatio, "verticalOffset": 12 }, { "width": 100, "fn": calculateMapPeakControl, "verticalOffset": 12 }, { "width": 100, "fn": calculateMapFinalControl, "verticalOffset": 12 }, { "width": 100, "fn": calculateMapExploration, "verticalOffset": 12 }, { "width": 100, "fn": calculateVegetarianRatio, "verticalOffset": 12 }, { "width": 100, "fn": calculateFeminization, "verticalOffset": 12 }, { "width": 139, "fn": calculateBribes, "verticalOffset": 12 } ], "teamCounterFn": calculateMiscellaneousTeam } -}); +]; + +var g_ChartPanelsData = [ + { + "label": translate("Charts") + } +]; function getColoredTypeTranslation(type) { return g_SummaryTypes[type].color ? coloredText(g_SummaryTypes[type].caption, g_SummaryTypes[type].color) : g_SummaryTypes[type].caption; } 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].headerCaption || 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].length * (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() { let withoutTeam = !g_Teams[-1] ? 0 : g_Teams[-1].length; if (!g_Teams || withoutTeam > 0) Engine.GetGUIObjectByName("noTeamsBox").hidden = false; if (!g_Teams) return; let yStart = g_TeamsBoxYStart + withoutTeam * (g_PlayerBoxYSize + g_PlayerBoxGap) + (withoutTeam ? 30 : 0); for (let i in g_Teams) { if (i == -1) 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].length * (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].length * (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 (!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 23788) +++ ps/trunk/binaries/data/mods/public/gui/summary/summary.js (revision 23789) @@ -1,567 +1,539 @@ const g_CivData = loadCivData(false, false); var g_ScorePanelsData; var g_MaxHeadingTitle = 9; var g_LongHeadingWidth = 250; var g_PlayerBoxYSize = 40; var g_PlayerBoxGap = 2; var g_PlayerBoxAlpha = 50; var g_TeamsBoxYStart = 40; var g_TypeColors = { "blue": "196 198 255", "green": "201 255 200", "red": "255 213 213", "yellow": "255 255 157" }; /** * Colors, captions and format used for units, structures, etc. types */ var g_SummaryTypes = { "percent": { "color": "", "caption": "%", "postfix": "%" }, "trained": { "color": g_TypeColors.green, "caption": translate("Trained"), "postfix": " / " }, "constructed": { "color": g_TypeColors.green, "caption": translate("Constructed"), "postfix": " / " }, "gathered": { "color": g_TypeColors.green, "caption": translate("Gathered"), "postfix": " / " }, "sent": { "color": g_TypeColors.green, "caption": translate("Sent"), "postfix": " / " }, "bought": { "color": g_TypeColors.green, "caption": translate("Bought"), "postfix": " / " }, "income": { "color": g_TypeColors.green, "caption": translate("Income"), "postfix": " / " }, "captured": { "color": g_TypeColors.yellow, "caption": translate("Captured"), "postfix": " / " }, "succeeded": { "color": g_TypeColors.green, "caption": translate("Succeeded"), "postfix": " / " }, "destroyed": { "color": g_TypeColors.blue, "caption": translate("Destroyed"), "postfix": "\n" }, "killed": { "color": g_TypeColors.blue, "caption": translate("Killed"), "postfix": "\n" }, "lost": { "color": g_TypeColors.red, "caption": translate("Lost"), "postfix": "" }, "used": { "color": g_TypeColors.red, "caption": translate("Used"), "postfix": "" }, "received": { "color": g_TypeColors.red, "caption": translate("Received"), "postfix": "" }, "sold": { "color": g_TypeColors.red, "caption": translate("Sold"), "postfix": "" }, "outcome": { "color": g_TypeColors.red, "caption": translate("Outcome"), "postfix": "" }, "failed": { "color": g_TypeColors.red, "caption": translate("Failed"), "postfix": "" } }; // Translation: Unicode encoded infinity symbol indicating a division by zero in the summary screen. var g_InfinitySymbol = translate("\u221E"); var g_Teams = []; var g_PlayerCount; var g_GameData; var g_ResourceData = new Resources(); /** * Selected chart indexes. */ var g_SelectedChart = { "category": [0, 0], "value": [0, 1], "type": [0, 0] }; -/** - * Array of the panel button names. - */ -var g_PanelButtons = []; - -/** - * Remember the name of the currently opened view panel. - */ -var g_SelectedPanel; - function init(data) { initSummaryData(data); initGUISummary(); } function initSummaryData(data) { g_GameData = data; g_ScorePanelsData = getScorePanelsData(); - g_PanelButtons = Object.keys(g_ScorePanelsData).concat(["charts"]).map(panel => panel + "PanelButton"); - g_SelectedPanel = g_PanelButtons[0]; if (data && data.gui && data.gui.summarySelection) { - g_SelectedPanel = data.gui.summarySelection.panel; + g_TabCategorySelected = data.gui.summarySelection.panel; g_SelectedChart = data.gui.summarySelection.charts; } initTeamData(); calculateTeamCounterDataHelper(); } function initGUISummary() { initGUIWindow(); initPlayerBoxPositions(); initGUICharts(); initGUILabels(); initGUIButtons(); - - selectPanel(Engine.GetGUIObjectByName(g_SelectedPanel)); - for (let button of g_PanelButtons) - { - let tab = Engine.GetGUIObjectByName(button); - tab.onMouseWheelUp = () => selectNextTab(1); - tab.onMouseWheelDown = () => selectNextTab(-1); - } } /** * Sets the style and title of the page. */ function initGUIWindow() { let summaryWindow = Engine.GetGUIObjectByName("summaryWindow"); summaryWindow.sprite = g_GameData.gui.dialog ? "ModernDialog" : "ModernWindow"; summaryWindow.size = g_GameData.gui.dialog ? "16 24 100%-16 100%-24" : "0 0 100% 100%"; Engine.GetGUIObjectByName("summaryWindowTitle").size = g_GameData.gui.dialog ? "50%-128 -16 50%+128 16" : "50%-128 4 50%+128 36"; } -/** - * Show next/previous panel. - * @param direction - 1/-1 forward, backward panel. - */ -function selectNextTab(direction) +function selectPanelGUI(panel) { - selectPanel(Engine.GetGUIObjectByName(g_PanelButtons[ - (g_PanelButtons.indexOf(g_SelectedPanel) + direction + g_PanelButtons.length) % g_PanelButtons.length])); -} - -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 = "ModernTabHorizontalBackground"; - - panel.sprite = "ModernTabHorizontalForeground"; - - adjustTabDividers(panel.size); + adjustTabDividers(Engine.GetGUIObjectByName("tabButton[" + panel + "]").size); let generalPanel = Engine.GetGUIObjectByName("generalPanel"); let chartsPanel = Engine.GetGUIObjectByName("chartsPanel"); - let chartsHidden = panel.name != "chartsPanelButton"; + + // We assume all scorePanels come before the charts. + let chartsHidden = panel < g_ScorePanelsData.length; generalPanel.hidden = !chartsHidden; chartsPanel.hidden = chartsHidden; if (chartsHidden) - updatePanelData(g_ScorePanelsData[panel.name.substr(0, panel.name.length - "PanelButton".length)]); + updatePanelData(g_ScorePanelsData[panel]); else [0, 1].forEach(updateCategoryDropdown); - - g_SelectedPanel = panel.name; } function initGUICharts() { let player_colors = []; for (let i = 1; i <= g_PlayerCount; ++i) { let playerState = g_GameData.sim.playerStates[i]; player_colors.push( Math.floor(playerState.color.r * 255) + " " + Math.floor(playerState.color.g * 255) + " " + Math.floor(playerState.color.b * 255) ); } for (let i = 0; i < 2; ++i) Engine.GetGUIObjectByName("chart[" + i + "]").series_color = player_colors; let chartLegend = Engine.GetGUIObjectByName("chartLegend"); chartLegend.caption = g_GameData.sim.playerStates.slice(1).map( (state, index) => coloredText("■", player_colors[index]) + " " + state.name ).join(" "); let chart1Part = Engine.GetGUIObjectByName("chart[1]Part"); let chart1PartSize = chart1Part.size; chart1PartSize.rright += 50; chart1PartSize.rleft += 50; chart1PartSize.right -= 5; chart1PartSize.left -= 5; chart1Part.size = chart1PartSize; } function resizeDropdown(dropdown) { let size = dropdown.size; size.bottom = dropdown.size.top + (Engine.GetTextWidth(dropdown.font, dropdown.list[dropdown.selected]) > dropdown.size.right - dropdown.size.left - 32 ? 42 : 27); dropdown.size = size; } function updateCategoryDropdown(number) { let chartCategory = Engine.GetGUIObjectByName("chart[" + number + "]CategorySelection"); - chartCategory.list_data = Object.keys(g_ScorePanelsData); - chartCategory.list = Object.keys(g_ScorePanelsData).map(panel => g_ScorePanelsData[panel].caption); + chartCategory.list_data = g_ScorePanelsData.map((panel, idx) => idx); + chartCategory.list = g_ScorePanelsData.map(panel => panel.label); chartCategory.onSelectionChange = function() { if (!this.list_data[this.selected]) return; if (g_SelectedChart.category[number] != this.selected) { g_SelectedChart.category[number] = this.selected; g_SelectedChart.value[number] = 0; g_SelectedChart.type[number] = 0; } resizeDropdown(this); updateValueDropdown(number, this.list_data[this.selected]); }; chartCategory.selected = g_SelectedChart.category[number]; } function updateValueDropdown(number, category) { let chartValue = Engine.GetGUIObjectByName("chart[" + number + "]ValueSelection"); let list = g_ScorePanelsData[category].headings.map(heading => heading.caption); list.shift(); chartValue.list = list; let list_data = g_ScorePanelsData[category].headings.map(heading => heading.identifier); list_data.shift(); chartValue.list_data = list_data; chartValue.onSelectionChange = function() { if (!this.list_data[this.selected]) return; if (g_SelectedChart.value[number] != this.selected) { g_SelectedChart.value[number] = this.selected; g_SelectedChart.type[number] = 0; } resizeDropdown(this); updateTypeDropdown(number, category, this.list_data[this.selected], this.selected); }; chartValue.selected = g_SelectedChart.value[number]; } function updateTypeDropdown(number, category, item, itemNumber) { let testValue = g_ScorePanelsData[category].counters[itemNumber].fn(g_GameData.sim.playerStates[1], 0, item); let hide = !g_ScorePanelsData[category].counters[itemNumber].fn || typeof testValue != "object" || Object.keys(testValue).length < 2; Engine.GetGUIObjectByName("chart[" + number + "]TypeLabel").hidden = hide; let chartType = Engine.GetGUIObjectByName("chart[" + number + "]TypeSelection"); chartType.hidden = hide; if (hide) { updateChart(number, category, item, itemNumber, Object.keys(testValue)[0] || undefined); return; } chartType.list = Object.keys(testValue).map(type => g_SummaryTypes[type].caption); chartType.list_data = Object.keys(testValue); chartType.onSelectionChange = function() { if (!this.list_data[this.selected]) return; g_SelectedChart.type[number] = this.selected; resizeDropdown(this); updateChart(number, category, item, itemNumber, this.list_data[this.selected]); }; chartType.selected = g_SelectedChart.type[number]; } function updateChart(number, category, item, itemNumber, type) { if (!g_ScorePanelsData[category].counters[itemNumber].fn) return; let chart = Engine.GetGUIObjectByName("chart[" + number + "]"); chart.format_y = g_ScorePanelsData[category].headings[itemNumber + 1].format || "INTEGER"; Engine.GetGUIObjectByName("chart[" + number + "]XAxisLabel").caption = translate("Time elapsed"); let series = []; for (let j = 1; j <= g_PlayerCount; ++j) { let playerState = g_GameData.sim.playerStates[j]; let data = []; for (let index in playerState.sequences.time) { let value = g_ScorePanelsData[category].counters[itemNumber].fn(playerState, index, item); if (type) value = value[type]; data.push([playerState.sequences.time[index], value]); } series.push(data); } chart.series = series; } function adjustTabDividers(tabSize) { + let tabButtonsLeft = Engine.GetGUIObjectByName("tabButtonsFrame").size.left; + let leftSpacer = Engine.GetGUIObjectByName("tabDividerLeft"); - let rightSpacer = Engine.GetGUIObjectByName("tabDividerRight"); + let leftSpacerSize = leftSpacer.size; + leftSpacerSize.right = tabSize.left + tabButtonsLeft + 2; + leftSpacer.size = leftSpacerSize; - 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(" "); + let rightSpacer = Engine.GetGUIObjectByName("tabDividerRight"); + let rightSpacerSize = rightSpacer.size; + rightSpacerSize.left = tabSize.right + tabButtonsLeft - 2; + rightSpacer.size = rightSpacerSize; } function updatePanelData(panelInfo) { resetGeneralPanel(); updateGeneralPanelHeadings(panelInfo.headings); updateGeneralPanelTitles(panelInfo.titleHeadings); let rowPlayerObjectWidth = updateGeneralPanelCounter(panelInfo.counters); updateGeneralPanelTeams(); let index = g_GameData.sim.playerStates[1].sequences.time.length - 1; 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; setOutcomeIcon(playerState.state, Engine.GetGUIObjectByName(playerOutcome)); playerNameColumn = Engine.GetGUIObjectByName(playerNameColumn); playerNameColumn.caption = g_GameData.sim.playerStates[i + 1].name; playerNameColumn.tooltip = translateAISettings(g_GameData.sim.mapSettings.PlayerData[i + 1]); let civIcon = Engine.GetGUIObjectByName(playerCivicBoxColumn); civIcon.sprite = "stretched:" + g_CivData[playerState.civ].Emblem; civIcon.tooltip = g_CivData[playerState.civ].Name; updateCountersPlayer(playerState, panelInfo.counters, panelInfo.headings, playerCounterValue, index); } let teamCounterFn = panelInfo.teamCounterFn; if (g_Teams && teamCounterFn) updateCountersTeam(teamCounterFn, panelInfo.counters, panelInfo.headings, index); } function continueButton() { let summarySelection = { - "panel": g_SelectedPanel, + "panel": g_TabCategorySelected, "charts": g_SelectedChart }; if (g_GameData.gui.isInGame) Engine.PopGuiPage({ "summarySelection": summarySelection }); else if (g_GameData.gui.dialog) Engine.PopGuiPage(); else if (Engine.HasXmppClient()) Engine.SwitchGuiPage("page_lobby.xml", { "dialog": false }); else if (g_GameData.gui.isReplay) Engine.SwitchGuiPage("page_replaymenu.xml", { "replaySelectionData": g_GameData.gui.replaySelectionData, "summarySelection": summarySelection }); else Engine.SwitchGuiPage("page_pregame.xml"); } function startReplay() { if (!Engine.StartVisualReplay(g_GameData.gui.replayDirectory)) { warn("Replay file not found!"); return; } Engine.SwitchGuiPage("page_loading.xml", { "attribs": Engine.GetReplayAttributes(g_GameData.gui.replayDirectory), "playerAssignments": { "local": { "name": singleplayerName(), "player": -1 } }, "savedGUIData": "", "isReplay": true, "replaySelectionData": g_GameData.gui.replaySelectionData }); } function initGUILabels() { 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."); Engine.GetGUIObjectByName("timeElapsed").caption = sprintf( translate("Game time elapsed: %(time)s"), { "time": timeToString(g_GameData.sim.timeElapsed) }); let mapType = g_Settings.MapTypes.find(type => type.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.Name : (mapType ? mapType.Title : "") }); } function initGUIButtons() { let replayButton = Engine.GetGUIObjectByName("replayButton"); replayButton.hidden = g_GameData.gui.isInGame || !g_GameData.gui.replayDirectory; let lobbyButton = Engine.GetGUIObjectByName("lobbyButton"); lobbyButton.tooltip = colorizeHotkey(translate("%(hotkey)s: Toggle the multiplayer lobby in a dialog window."), "lobby"); lobbyButton.hidden = g_GameData.gui.isInGame || !Engine.HasXmppClient(); // Right-align lobby button let lobbyButtonSize = lobbyButton.size; let lobbyButtonWidth = lobbyButtonSize.right - lobbyButtonSize.left; lobbyButtonSize.right = (replayButton.hidden ? Engine.GetGUIObjectByName("continueButton").size.left : replayButton.size.left) - 10; lobbyButtonSize.left = lobbyButtonSize.right - lobbyButtonWidth; lobbyButton.size = lobbyButtonSize; + + let allPanelsData = g_ScorePanelsData.concat(g_ChartPanelsData); + for (let tab in allPanelsData) + allPanelsData[tab].tooltip = + sprintf(translate("Toggle the %(name)s summary tab."), { "name": allPanelsData[tab].label }) + + colorizeHotkey("\n" + translate("Use %(hotkey)s to move a summary tab right."), "tab.next") + + colorizeHotkey("\n" + translate("Use %(hotkey)s to move a summary tab left."), "tab.prev"); + + placeTabButtons( + allPanelsData, + true, + g_TabButtonWidth, + g_TabButtonDist, + selectPanel, + selectPanelGUI); } function initTeamData() { // Panels g_PlayerCount = g_GameData.sim.playerStates.length - 1; if (g_GameData.sim.mapSettings.LockTeams) { // Count teams for (let player = 1; player <= g_PlayerCount; ++player) { let playerTeam = g_GameData.sim.playerStates[player].team; if (!g_Teams[playerTeam]) g_Teams[playerTeam] = []; g_Teams[playerTeam].push(player); } if (g_Teams.every(team => team && team.length < 2)) 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; } Index: ps/trunk/binaries/data/mods/public/gui/summary/summary.xml =================================================================== --- ps/trunk/binaries/data/mods/public/gui/summary/summary.xml (revision 23788) +++ ps/trunk/binaries/data/mods/public/gui/summary/summary.xml (revision 23789) @@ -1,236 +1,183 @@