Index: ps/trunk/binaries/data/config/default.cfg =================================================================== --- ps/trunk/binaries/data/config/default.cfg +++ ps/trunk/binaries/data/config/default.cfg @@ -169,6 +169,7 @@ realtime.toggle = "Alt+T" ; Toggle current display of computer time session.devcommands.toggle = "Alt+D" ; Toggle developer commands panel timeelapsedcounter.toggle = "F12" ; Toggle time elapsed counter +ceasefirecounter.toggle = unused ; Toggle ceasefire counter session.showstatusbars = Tab ; Toggle display of status bars session.highlightguarding = PgDn ; Toggle highlight of guarding units session.highlightguarded = PgUp ; Toggle highlight of guarded units Index: ps/trunk/binaries/data/mods/public/gui/common/OverlayCounter.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/common/OverlayCounter.js +++ ps/trunk/binaries/data/mods/public/gui/common/OverlayCounter.js @@ -0,0 +1,47 @@ +/** + * This is an abstract base class managing one counter shown. + * Classes implementing this class require a Config property and may have a Hotkey property. + */ +class OverlayCounter +{ + constructor(overlayCounterManager) + { + this.overlayCounterManager = overlayCounterManager; + this.updateEnabled(); + + registerConfigChangeHandler(this.onConfigChange.bind(this)); + + if (this.Hotkey) + Engine.SetGlobalHotkey(this.Hotkey, this.toggle.bind(this)); + } + + onConfigChange(changes) + { + if (changes.has(this.Config)) + this.updateEnabled(); + } + + isEnabled() + { + return Engine.ConfigDB_GetValue("user", this.Config) == "true"; + } + + updateEnabled() + { + this.overlayCounterManager.setCounterEnabled(this, this.isEnabled()); + } + + toggle() + { + Engine.ConfigDB_CreateValue("user", this.Config, String(!this.isEnabled())); + this.updateEnabled(); + } +} + +/** + * The properties of this prototype are defined in other files. Each of them is a class + * managing a counter shown on the current page and may extend the OverlayCounter class. + */ +class OverlayCounterTypes +{ +} Index: ps/trunk/binaries/data/mods/public/gui/common/OverlayCounterFPS.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/common/OverlayCounterFPS.js +++ ps/trunk/binaries/data/mods/public/gui/common/OverlayCounterFPS.js @@ -0,0 +1,32 @@ +/** + * This counter displays the current framerate in the screen corner. + */ +OverlayCounterTypes.prototype.FPS = class extends OverlayCounter +{ + constructor(overlayCounterManager) + { + super(overlayCounterManager); + + // Tiny performance improvement + this.caption = translate(this.Caption); + + // Minimize object construction + this.fpsObject = {}; + } + + /** + * This function is called frequently and thus minimized. + */ + get() + { + this.fpsObject.fps = Engine.GetFPS(); + return sprintf(this.caption, this.fpsObject); + } +}; + +// dennis-ignore: * +OverlayCounterTypes.prototype.FPS.prototype.Caption = markForTranslation("FPS: %(fps)4s"); + +OverlayCounterTypes.prototype.FPS.prototype.Config = "overlay.fps"; + +OverlayCounterTypes.prototype.FPS.prototype.Hotkey = "fps.toggle"; Index: ps/trunk/binaries/data/mods/public/gui/common/OverlayCounterManager.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/common/OverlayCounterManager.js +++ ps/trunk/binaries/data/mods/public/gui/common/OverlayCounterManager.js @@ -0,0 +1,119 @@ +/** + * Since every GUI page can display the FPS or realtime counter, + * this manager is initialized for every GUI page. + */ +var g_OverlayCounterManager; + +class OverlayCounterManager +{ + constructor(dataCounter) + { + this.dataCounter = dataCounter; + this.lineHeight = dataCounter.size.bottom - dataCounter.size.top; + this.counters = []; + this.enabledCounters = []; + this.lastTick = undefined; + this.lastLineCount = 0; + this.resizeHandlers = []; + + for (let name of this.availableCounterNames()) + { + let counter = new OverlayCounterTypes.prototype[name](this); + this.counters.push(counter); + counter.updateEnabled(); + } + + this.dataCounter.onTick = this.onTick.bind(this); + } + + /** + * Mods may overwrite this to change the order of the counters shown. + */ + availableCounterNames() + { + return Object.keys(OverlayCounterTypes.prototype); + } + + deleteCounter(counter) + { + let filter = count => count != counter; + this.counters = this.counters.filter(filter); + this.enabledCounters = this.enabledCounters.filter(filter); + } + + /** + * This function allows enabling and disabling of timers while preserving the counter order. + */ + setCounterEnabled(counter, enabled) + { + if (enabled) + this.enabledCounters = this.counters.filter(count => + this.enabledCounters.indexOf(count) != -1 || count == counter); + else + this.enabledCounters = this.enabledCounters.filter(count => count != counter); + + // Update instantly + this.lastTick = undefined; + this.onTick(); + } + + /** + * Handlers subscribed here will be informed then the dimension of the overlay changed. + * This allows placing the buttons below the counter. + */ + registerResizeHandler(handler) + { + this.resizeHandlers.push(handler); + } + + onTick() + { + // Don't rebuild the caption every frame + let now = Date.now(); + if (now < this.lastTick + this.Delay) + return; + + this.lastTick = now; + + let lineCount = 0; + let txt = ""; + + for (let counter of this.enabledCounters) + { + let newTxt = counter.get(); + if (!newTxt) + continue; + + ++lineCount; + txt += newTxt + "\n"; + } + + if (lineCount) + this.dataCounter.caption = txt; + + // The caption changes more often than not, + // but adding or removing lines happens rarely. + if (this.lastLineCount == lineCount) + return; + + let offset = this.lineHeight * lineCount; + + if (lineCount) + { + let size = this.dataCounter.size; + size.bottom = size.top + offset; + this.dataCounter.size = size; + } + + this.dataCounter.hidden = !lineCount; + + for (let handler of this.resizeHandlers) + handler(offset); + } +} + +/** + * To minimize the computation performed every frame, this duration + * in milliseconds determines how often the caption is rebuilt. + */ +OverlayCounterManager.prototype.Delay = 250; Index: ps/trunk/binaries/data/mods/public/gui/common/OverlayCounterRealtime.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/common/OverlayCounterRealtime.js +++ ps/trunk/binaries/data/mods/public/gui/common/OverlayCounterRealtime.js @@ -0,0 +1,21 @@ +/** + * Shows the current time to the player in their current timezone. + */ +OverlayCounterTypes.prototype.Realtime = class extends OverlayCounter +{ + constructor(overlayCounterManager) + { + super(overlayCounterManager); + this.date = new Date(); + } + + get() + { + this.date.setTime(Date.now()); + return this.date.toLocaleTimeString(); + } +}; + +OverlayCounterTypes.prototype.Realtime.prototype.Config = "overlay.realtime"; + +OverlayCounterTypes.prototype.Realtime.prototype.Hotkey = "realtime.toggle"; Index: ps/trunk/binaries/data/mods/public/gui/common/functions_global_object.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/common/functions_global_object.js +++ ps/trunk/binaries/data/mods/public/gui/common/functions_global_object.js @@ -1,29 +1,3 @@ -function updateCounters() -{ - let counters = []; - - if (Engine.ConfigDB_GetValue("user", "overlay.fps") === "true") - // dennis-ignore: * - counters.push(sprintf(translate("FPS: %(fps)4s"), { "fps": Engine.GetFPS() })); - - if (Engine.ConfigDB_GetValue("user", "overlay.realtime") === "true") - counters.push((new Date()).toLocaleTimeString()); - - // If game has been started - if (typeof appendSessionCounters != "undefined") - appendSessionCounters(counters); - - let dataCounter = Engine.GetGUIObjectByName("dataCounter"); - dataCounter.caption = counters.join("\n") + "\n"; - dataCounter.hidden = !counters.length; - dataCounter.size = sprintf("%(left)s %(top)s %(right)s %(bottom)s", { - "left": "100%%-100", - "top": "40", - "right": "100%%-5", - "bottom": 40 + 14 * counters.length - }); -} - /** * Update the overlay with the most recent network warning of each client. */ Index: ps/trunk/binaries/data/mods/public/gui/common/functions_utility.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/common/functions_utility.js +++ ps/trunk/binaries/data/mods/public/gui/common/functions_utility.js @@ -9,6 +9,27 @@ }; /** + * These events are fired when the user has closed the options page. + * The handlers are provided a Set storing which config values have changed. + * TODO: This should become a GUI event sent by the engine. + */ +var g_ConfigChangeHandlers = new Set(); + +function registerConfigChangeHandler(handler) +{ + g_ConfigChangeHandlers.add(handler); +} + +/** + * @param changes - a Set of config names + */ +function fireConfigChangeHandlers(changes) +{ + for (let handler of g_ConfigChangeHandlers) + handler(changes); +} + +/** * Returns translated history and gameplay data of all civs, optionally including a mock gaia civ. */ function loadCivData(selectableOnly, gaia) Index: ps/trunk/binaries/data/mods/public/gui/common/global.xml =================================================================== --- ps/trunk/binaries/data/mods/public/gui/common/global.xml +++ ps/trunk/binaries/data/mods/public/gui/common/global.xml @@ -39,43 +39,15 @@ type="text" ghost="true" z="199" - size="100%-90 40 100%-5 40" + size="100%-100 40 100%-5 54" font="mono-10" textcolor="white" text_align="right" text_valign="top" sprite="color: 0 0 0 100" - > - - updateCounters(); - - - - - - - - Index: ps/trunk/binaries/data/mods/public/gui/options/options.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/options/options.js +++ ps/trunk/binaries/data/mods/public/gui/options/options.js @@ -243,6 +243,7 @@ Engine.ConfigDB_SetChanges("user", true); g_ChangedKeys.add(option.config); + fireConfigChangeHandlers(new Set([option.config])); if (option.function) Engine[option.function](value); Index: ps/trunk/binaries/data/mods/public/gui/pregame/MainMenuItems.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/pregame/MainMenuItems.js +++ ps/trunk/binaries/data/mods/public/gui/pregame/MainMenuItems.js @@ -141,7 +141,10 @@ "caption": translate("Options"), "tooltip": translate("Adjust game settings."), "onPress": () => { - Engine.PushGuiPage("page_options.xml"); + Engine.PushGuiPage( + "page_options.xml", + {}, + fireConfigChangeHandlers); } }, { Index: ps/trunk/binaries/data/mods/public/gui/session/DiplomacyColors.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/DiplomacyColors.js +++ ps/trunk/binaries/data/mods/public/gui/session/DiplomacyColors.js @@ -17,6 +17,7 @@ registerPlayersInitHandler(this.onPlayersInit.bind(this)); registerConfigChangeHandler(this.onConfigChange.bind(this)); + registerCeasefireEndedHandler(this.onCeasefireEnded.bind(this)); } registerDiplomacyColorsChangeHandler(handler) Index: ps/trunk/binaries/data/mods/public/gui/session/OverlayCounterElapsedTime.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/OverlayCounterElapsedTime.js +++ ps/trunk/binaries/data/mods/public/gui/session/OverlayCounterElapsedTime.js @@ -0,0 +1,37 @@ +/** + * This class shows the simulated match time below the FPS counter. + */ +OverlayCounterTypes.prototype.ElapsedTime = class extends OverlayCounter +{ + constructor(overlayCounterManager) + { + super(overlayCounterManager); + + // Performance optimization + this.caption = translate(this.Caption); + this.sprintfData = {}; + } + + get() + { + if (!g_SimState) + return ""; + + let time = timeToString(g_SimState.timeElapsed); + + let speed = Engine.GetSimRate(); + if (speed == 1) + return time; + + this.sprintfData.time = time; + this.sprintfData.speed = Engine.FormatDecimalNumberIntoString(speed); + return sprintf(this.caption, this.sprintfData); + } +}; + +// Translation: The "x" means "times", with the mathematical meaning of multiplication. +OverlayCounterTypes.prototype.ElapsedTime.prototype.Caption = markForTranslation("%(time)s (%(speed)sx)"); + +OverlayCounterTypes.prototype.ElapsedTime.prototype.Config = "gui.session.timeelapsedcounter"; + +OverlayCounterTypes.prototype.ElapsedTime.prototype.Hotkey = "timeelapsedcounter.toggle"; Index: ps/trunk/binaries/data/mods/public/gui/session/OverlayCounterRemainingCeasefire.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/OverlayCounterRemainingCeasefire.js +++ ps/trunk/binaries/data/mods/public/gui/session/OverlayCounterRemainingCeasefire.js @@ -0,0 +1,28 @@ +/** + * Adds the ceasefire counter to the global FPS and + * realtime counters shown in the top right corner. + */ +OverlayCounterTypes.prototype.RemainingCeasefire = class extends OverlayCounter +{ + constructor(overlayCounterManager) + { + super(overlayCounterManager); + registerCeasefireEndedHandler(this.onCeasefireEnded.bind(this)); + } + + onCeasefireEnded() + { + this.overlayCounterManager.deleteCounter(this); + } + + get() + { + if (!g_SimState) + return ""; + return timeToString(g_SimState.ceasefireTimeRemaining); + } +}; + +OverlayCounterTypes.prototype.RemainingCeasefire.prototype.Config = "gui.session.ceasefirecounter"; + +OverlayCounterTypes.prototype.RemainingCeasefire.prototype.Hotkey = "ceasefirecounter.toggle"; Index: ps/trunk/binaries/data/mods/public/gui/session/messages.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/messages.js +++ ps/trunk/binaries/data/mods/public/gui/session/messages.js @@ -19,6 +19,11 @@ var g_PlayerAssignmentsChangeHandlers = new Set(); /** + * These handlers are called when the ceasefire time has run out. + */ +var g_CeasefireEndedHandlers = new Set(); + +/** * Handle all netmessage types that can occur. */ var g_NetMessageTypes = { @@ -151,7 +156,8 @@ "ceasefire-ended": function(notification, player) { updatePlayerData(); - g_DiplomacyColors.OnCeasefireEnded(); + for (let handler of g_CeasefireEndedHandlers) + handler(); }, "tutorial": function(notification, player) { @@ -291,6 +297,11 @@ g_PlayerAssignmentsChangeHandlers.add(handler); } +function registerCeasefireEndedHandler(handler) +{ + g_CeasefireEndedHandlers.add(handler); +} + /** * Loads all known cheat commands. */ Index: ps/trunk/binaries/data/mods/public/gui/session/session.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/session.js +++ ps/trunk/binaries/data/mods/public/gui/session/session.js @@ -160,12 +160,6 @@ var g_HotkeyChangeHandlers = new Set(); /** - * These events are fired when the user has closed the options page. - * The handlers are provided a Set storing which config values have changed. - */ -var g_ConfigChangeHandlers = new Set(); - -/** * List of additional entities to highlight. */ var g_ShowGuarding = false; @@ -353,20 +347,6 @@ g_HotkeyChangeHandlers.add(handler); } -function registerConfigChangeHandler(handler) -{ - g_ConfigChangeHandlers.add(handler); -} - -/** - * @param changes - a Set of config names - */ -function fireConfigChangeHandlers(changes) -{ - for (let handler of g_ConfigChangeHandlers) - handler(changes); -} - function updatePlayerData() { let simState = GetSimState(); @@ -870,30 +850,3 @@ { Engine.PlayAmbientSound(pickRandom(g_Ambient), true); } - -/** - * Adds the ingame time and ceasefire counter to the global FPS and - * realtime counters shown in the top right corner. - */ -function appendSessionCounters(counters) -{ - let simState = GetSimState(); - - if (Engine.ConfigDB_GetValue("user", "gui.session.timeelapsedcounter") === "true") - { - let currentSpeed = Engine.GetSimRate(); - if (currentSpeed != 1.0) - // Translation: The "x" means "times", with the mathematical meaning of multiplication. - counters.push(sprintf(translate("%(time)s (%(speed)sx)"), { - "time": timeToString(simState.timeElapsed), - "speed": Engine.FormatDecimalNumberIntoString(currentSpeed) - })); - else - counters.push(timeToString(simState.timeElapsed)); - } - - if (simState.ceasefireActive && Engine.ConfigDB_GetValue("user", "gui.session.ceasefirecounter") === "true") - counters.push(timeToString(simState.ceasefireTimeRemaining)); - - g_ResearchProgress.setTopOffset(14 * counters.length); -} Index: ps/trunk/binaries/data/mods/public/gui/session/session.xml =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/session.xml +++ ps/trunk/binaries/data/mods/public/gui/session/session.xml @@ -41,20 +41,6 @@ - - - - - -