diff --git a/binaries/data/mods/public/gui/session/cinema/CinemaOverlay.js b/binaries/data/mods/public/gui/session/cinema/CinemaOverlay.js new file mode 100644 index 0000000000..8d11956adc --- /dev/null +++ b/binaries/data/mods/public/gui/session/cinema/CinemaOverlay.js @@ -0,0 +1,92 @@ +/** + * This class manages the cinematic overlay, which is responsible for showing black bars at the top and bottom while + * cinema paths are playing. Cinema paths are predefined camera animations, which block player input of any kind while + * playing; cutscenes, essentially. Whether one is playing is communicated through the simulation state. + */ +class CinemaOverlay +{ + constructor() + { + this.overlay = Engine.GetGUIObjectByName("cinemaOverlay"); + this.barTop = Engine.GetGUIObjectByName("cinemaOverlayBarTop"); + this.barBottom = Engine.GetGUIObjectByName("cinemaOverlayBarBottom"); + + // Objects to hide while showing the overlay. + this.primarySessionOverlays = Engine.GetGUIObjectByName("primaryOverlays"); + this.bandbox = Engine.GetGUIObjectByName("bandbox"); + this.hotkeys = Engine.GetGUIObjectByName("hotkeys"); + + this.overlay.onSimulationUpdate = this.onSimulationUpdate.bind(this); + this.overlay.onWindowResized = () => + { + this.recalculateBarSizes(); + }; + this.overlay.hidden = true; + this.isCutsceneModeEnabled = Engine.Renderer_GetCutsceneModeEnabled(); + + this.recalculateBarSizes(); + } + + /** + * Enable or disable cutscene mode and remember it in order to save unnecessary calls to the engine. + * This, however, assumes that the mode isn't modified anywhere else. + */ + setCutsceneModeEnabled(enabled) + { + if (this.isCutsceneModeEnabled == enabled) + return; + + Engine.Renderer_SetCutsceneModeEnabled(!!enabled); + this.isCutsceneModeEnabled = enabled; + } + + isInCutsceneMode() + { + return this.isCutsceneModeEnabled; + } + + onSimulationUpdate() + { + const cinemaPathPlaying = GetSimState().cinemaPathPlaying; + if (this.overlay.hidden && cinemaPathPlaying) + this.show(); + else if (!this.overlay.hidden && !cinemaPathPlaying) + this.hide(); + } + + show() + { + if (!this.overlay.hidden) + return; + + this.primarySessionOverlays.hidden = true; + this.bandbox.hidden = true; + this.hotkeys.hidden = true; + this.overlay.hidden = false; + + this.setCutsceneModeEnabled(true); + } + + hide() + { + if (this.overlay.hidden) + return; + + this.primarySessionOverlays.hidden = !g_ShowGUI; + this.hotkeys.hidden = false; + this.overlay.hidden = true; + + this.setCutsceneModeEnabled(false); + } + + recalculateBarSizes() + { + const minHeight = 115; + const width = this.overlay.getComputedSize().right; + const height = this.overlay.getComputedSize().bottom; + // The aspect ratio of 2.39:1 is typical in films and has a cinematic feel to it. + const barHeight = Math.max(minHeight, (height - width / 2.39) / 2); + this.barTop.size.bottom = barHeight; + this.barBottom.size.top = -barHeight; + } +} diff --git a/binaries/data/mods/public/gui/session/cinema/CinemaOverlay.xml b/binaries/data/mods/public/gui/session/cinema/CinemaOverlay.xml new file mode 100644 index 0000000000..d0afe5b825 --- /dev/null +++ b/binaries/data/mods/public/gui/session/cinema/CinemaOverlay.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/binaries/data/mods/public/gui/session/input.js b/binaries/data/mods/public/gui/session/input.js index a8f453faa5..2585b80c16 100644 --- a/binaries/data/mods/public/gui/session/input.js +++ b/binaries/data/mods/public/gui/session/input.js @@ -527,7 +527,7 @@ function getPreferredEntities(ents) function handleInputBeforeGui(ev, hoveredObject) { - if (GetSimState().cinemaPlaying) + if (g_CinemaOverlay.isInCutsceneMode()) return false; // Capture cursor position so we can use it for displaying cursors, @@ -843,7 +843,7 @@ function handleInputBeforeGui(ev, hoveredObject) } function handleInputAfterGui(ev) { - if (GetSimState().cinemaPlaying) + if (g_CinemaOverlay.isInCutsceneMode()) return false; if (ev.hotkey === undefined) ev.hotkey = null; diff --git a/binaries/data/mods/public/gui/session/messages.js b/binaries/data/mods/public/gui/session/messages.js index 34952dda32..4b9bf16e70 100644 --- a/binaries/data/mods/public/gui/session/messages.js +++ b/binaries/data/mods/public/gui/session/messages.js @@ -444,7 +444,6 @@ function handleNetStatusMessage(message) if (message.status == "disconnected") { g_Disconnected = true; - updateCinemaPath(); closeOpenDialogs(); } diff --git a/binaries/data/mods/public/gui/session/session.js b/binaries/data/mods/public/gui/session/session.js index 8bd672926e..c3b437ef4d 100644 --- a/binaries/data/mods/public/gui/session/session.js +++ b/binaries/data/mods/public/gui/session/session.js @@ -12,6 +12,7 @@ var g_Ambient; var g_AutoFormation; var g_Chat; var g_Cheats; +var g_CinemaOverlay; var g_DeveloperOverlay; var g_DiplomacyColors; var g_DiplomacyDialog; @@ -296,6 +297,7 @@ function init(initData, hotloadData) g_Ambient = new Ambient(); g_AutoFormation = new AutoFormation(); g_Chat = new Chat(g_PlayerViewControl, g_Cheats); + g_CinemaOverlay = new CinemaOverlay(); g_DeveloperOverlay = new DeveloperOverlay(g_PlayerViewControl, g_Selection); g_DiplomacyDialog = new DiplomacyDialog(g_PlayerViewControl, g_DiplomacyColors); g_GameSpeedControl = new GameSpeedControl(g_PlayerViewControl); @@ -631,6 +633,10 @@ function onTick() handleNetMessages(); updateCursorAndTooltip(); + updateTimers(); + + if (g_CinemaOverlay.isInCutsceneMode()) + return; if (g_Selection.dirty) { @@ -650,12 +656,7 @@ function onTick() else if (g_ShowAllStatusBars && now % g_StatusBarUpdate <= tickLength) recalculateStatusBarDisplay(); - updateTimers(); Engine.GuiInterfaceCall("ClearRenamedEntities"); - - const isPlayingCinemaPath = GetSimState().cinemaPlaying && !g_Disconnected; - if (isPlayingCinemaPath) - updateCinemaOverlay(); } function onSimulationUpdate() @@ -686,7 +687,6 @@ function onSimulationUpdate() handler(); // TODO: Move to handlers - updateCinemaPath(); handleNotifications(); updateGUIObjects(); } @@ -694,40 +694,8 @@ function onSimulationUpdate() function toggleGUI() { g_ShowGUI = !g_ShowGUI; - updateCinemaPath(); -} - -// TODO: The whole cinema UI should be handled by its own class. -var g_CutsceneModeEnabled = false; -function updateCinemaPath() -{ - const isPlayingCinemaPath = GetSimState().cinemaPlaying && !g_Disconnected; - - Engine.GetGUIObjectByName("session").hidden = !g_ShowGUI || isPlayingCinemaPath; - Engine.GetGUIObjectByName("cinemaOverlay").hidden = !isPlayingCinemaPath; - if (isPlayingCinemaPath && !g_CutsceneModeEnabled) - { - Engine.Renderer_SetCutsceneModeEnabled(true); - g_CutsceneModeEnabled = true; - } - else if (!isPlayingCinemaPath && g_CutsceneModeEnabled) - { - Engine.Renderer_SetCutsceneModeEnabled(false); - g_CutsceneModeEnabled = false; - } -} - -function updateCinemaOverlay() -{ - const cinemaOverlay = Engine.GetGUIObjectByName("cinemaOverlay"); - const width = cinemaOverlay.getComputedSize().right; - const height = cinemaOverlay.getComputedSize().bottom; - let barHeight = (height - width / 2.39) / 2; - if (barHeight < 0) - barHeight = 0; - - Engine.GetGUIObjectByName("cinemaBarTop").size.bottom = barHeight; - Engine.GetGUIObjectByName("cinemaBarBottom").size.top = -barHeight; + Engine.GetGUIObjectByName("primaryOverlays").hidden = !g_ShowGUI; + Engine.GetGUIObjectByName("supplementaryOverlays").hidden = !g_ShowGUI; } // TODO: Use event subscription onSimulationUpdate, onEntitySelectionChange, onPlayerViewChange, ... instead diff --git a/binaries/data/mods/public/gui/session/session.xml b/binaries/data/mods/public/gui/session/session.xml index 179b663213..e5bc790821 100644 --- a/binaries/data/mods/public/gui/session/session.xml +++ b/binaries/data/mods/public/gui/session/session.xml @@ -8,6 +8,7 @@