Index: binaries/data/mods/public/gui/common/music.js =================================================================== --- binaries/data/mods/public/gui/common/music.js +++ binaries/data/mods/public/gui/common/music.js @@ -11,105 +11,106 @@ global.music = new Music(); } - // ============================================================================= // Music class for handling music states (requires onTick) // ============================================================================= -function Music() +class Music { - this.reference = this; + constructor() + { + this.reference = this; - this.RELATIVE_MUSIC_PATH = "audio/music/"; - this.MUSIC = { - "MENU": "menu", - "PEACE": "peace", - "BATTLE": "battle", - "VICTORY": "victory", - "DEFEAT": "defeat", - "CUSTOM": "custom" - }; + this.RELATIVE_MUSIC_PATH = "audio/music/"; + this.MUSIC = { + "MENU": "menu", + "PEACE": "peace", + "BATTLE": "battle", + "VICTORY": "victory", + "DEFEAT": "defeat", + "CUSTOM": "custom" + }; - this.resetTracks(); + this.resetTracks(); - this.states = { - "OFF": 0, - "MENU": 1, - "PEACE": 2, - "BATTLE": 3, - "VICTORY": 4, - "DEFEAT": 5, - "CUSTOM": 6 - }; + this.states = { + "OFF": 0, + "MENU": 1, + "PEACE": 2, + "BATTLE": 3, + "VICTORY": 4, + "DEFEAT": 5, + "CUSTOM": 6 + }; - this.musicGain = 0.3; + this.musicGain = +Engine.ConfigDB_GetValue("user", "sound.musicgain"); - this.locked = false; - this.currentState = 0; - this.oldState = 0; + this.locked = false; - // timer for delay between tracks - this.timer = []; - this.time = Date.now(); -} + // timer for delay between tracks + this.timer = []; + this.time = Date.now(); + } -Music.prototype.DEFAULT_MENU_TRACKS = ["Honor_Bound.ogg"].concat(shuffleArray([ - "An_old_Warhorse_goes_to_Pasture.ogg", - "Calm_Before_the_Storm.ogg", - "Juno_Protect_You.ogg" -])); + DEFAULT_MENU_TRACKS = ["Honor_Bound.ogg"].concat(shuffleArray([ + "An_old_Warhorse_goes_to_Pasture.ogg", + "Calm_Before_the_Storm.ogg", + "Juno_Protect_You.ogg" + ])); -Music.prototype.DEFAULT_PEACE_TRACKS = [ - "Tale_of_Warriors.ogg", - "Tavern_in_the_Mist.ogg", - "The_Road_Ahead.ogg" -]; + DEFAULT_PEACE_TRACKS = [ + "Tale_of_Warriors.ogg", + "Tavern_in_the_Mist.ogg", + "The_Road_Ahead.ogg" + ]; -Music.prototype.DEFAULT_BATTLE_TRACKS = [ - "Taiko_1.ogg", - "Taiko_2.ogg" -]; + DEFAULT_BATTLE_TRACKS = [ + "Taiko_1.ogg", + "Taiko_2.ogg" + ]; -Music.prototype.DEFAULT_VICTORY_TRACKS = [ - "You_are_Victorious!.ogg" -]; + DEFAULT_VICTORY_TRACKS = [ + "You_are_Victorious!.ogg" + ]; -Music.prototype.DEFAULT_DEFEAT_TRACKS = [ - "Dried_Tears.ogg" -]; + DEFAULT_DEFEAT_TRACKS = [ + "Dried_Tears.ogg" + ]; -Music.prototype.DEFAULT_CUSTOM_TRACKS = []; + DEFAULT_CUSTOM_TRACKS = []; -Music.prototype.completeTracks = function() -{ - for (const musicType of Object.keys(this.MUSIC)) - if (!this.tracks[musicType] || this.tracks[musicType].length === 0) + completeTracks = function() + { + for (const musicType of Object.keys(this.MUSIC)) + if (!this.tracks[musicType] || this.tracks[musicType].length === 0) + this.tracks[musicType] = this["DEFAULT_" + musicType + "_TRACKS"]; + }; + + resetTracks = function() + { + this.tracks = {}; + for (const musicType of Object.keys(this.MUSIC)) this.tracks[musicType] = this["DEFAULT_" + musicType + "_TRACKS"]; -}; + }; -Music.prototype.resetTracks = function() -{ - this.tracks = {}; - for (const musicType of Object.keys(this.MUSIC)) - this.tracks[musicType] = this["DEFAULT_" + musicType + "_TRACKS"]; -}; + // "reference" refers to this instance of Music (needed if called from the timer) + setState = function(state) + { + if (this.locked) + return; -// "reference" refers to this instance of Music (needed if called from the timer) -Music.prototype.setState = function(state) -{ - if (this.locked) - return; + // if string convert to int for updateState (e.g. "BATTLE" -> 3) + state = this.states[state] ? this.states[state] : state; - this.reference.currentState = state; - this.updateState(); -}; + if (Engine.GetMusicState() != state) + { + Engine.SaveMusicState(state) + this.updateState(state); + } + }; -Music.prototype.updateState = function() -{ - if (this.currentState != this.oldState) + updateState = function(state) { - this.oldState = this.currentState; - - switch (this.currentState) + switch (state) { case this.states.OFF: Engine.StopMusic(); @@ -140,67 +141,67 @@ break; default: - warn(sprintf("%(functionName)s: Unknown music state: %(state)s", { "functionName": "Music.updateState()", "state": this.currentState })); + warn(sprintf("%(functionName)s: Unknown music state: %(state)s", { "functionName": "Music.updateState()", "state": state })); break; } - } -}; + }; -Music.prototype.storeTracks = function(civMusic) -{ - for (const musicType of Object.keys(this.MUSIC)) - this.tracks[musicType] = []; + storeTracks = function(civMusic) + { + for (const musicType of Object.keys(this.MUSIC)) + this.tracks[musicType] = []; - for (let music of civMusic) - { - let type; - for (let i in this.MUSIC) - if (music.Type == this.MUSIC[i]) + for (let music of civMusic) + { + let type; + for (let i in this.MUSIC) + if (music.Type == this.MUSIC[i]) + { + type = i; + break; + } + + if (type === undefined) { - type = i; - break; + warn(sprintf("%(functionName)s: Unrecognized music type: %(musicType)s", { "functionName": "Music.storeTracks()", "musicType": music.Type })); + continue; } - if (type === undefined) - { - warn(sprintf("%(functionName)s: Unrecognized music type: %(musicType)s", { "functionName": "Music.storeTracks()", "musicType": music.Type })); - continue; + this.tracks[type].push(music.File); } - this.tracks[type].push(music.File); - } + this.completeTracks(); + }; - this.completeTracks(); -}; + startPlayList = function(tracks, fadeInPeriod, isLooping) + { + Engine.ClearPlaylist(); + for (let i in tracks) + Engine.AddPlaylistItem(this.RELATIVE_MUSIC_PATH + tracks[i]); -Music.prototype.startPlayList = function(tracks, fadeInPeriod, isLooping) -{ - Engine.ClearPlaylist(); - for (let i in tracks) - Engine.AddPlaylistItem(this.RELATIVE_MUSIC_PATH + tracks[i]); + Engine.StartPlaylist(isLooping); + }; - Engine.StartPlaylist(isLooping); -}; + isPlaying = function() + { + return Engine.MusicPlaying(); + }; -Music.prototype.isPlaying = function() -{ - return Engine.MusicPlaying(); -}; + start = function() + { + Engine.StartMusic(); + this.setState(this.states.PEACE); + }; -Music.prototype.start = function() -{ - Engine.StartMusic(); - this.setState(this.states.PEACE); -}; - -Music.prototype.stop = function() -{ - this.setState(this.states.OFF); -}; -/** -* Play the custom playlist when locked, otherwise plays the civ music according to the battle state. -*/ -Music.prototype.setLocked = function(locked) -{ - this.locked = locked; -}; + stop = function() + { + this.setState(this.states.OFF); + }; + /** + * Play the custom playlist when locked, otherwise plays the civ music according to the battle state. + */ + setLocked = function(locked) + { + this.locked = locked; + }; +} Index: binaries/data/mods/public/gui/session/PauseControl.js =================================================================== --- binaries/data/mods/public/gui/session/PauseControl.js +++ binaries/data/mods/public/gui/session/PauseControl.js @@ -81,6 +81,15 @@ // If explicit, send network message informing other clients Engine.SetPaused(this.explicitPause || pause || g_Disconnected, explicit); + if (pause) + Engine.SetMusicGain(global.music.musicGain / 2); + else + { + global.music.musicGain = +Engine.ConfigDB_GetValue("user", "sound.musicgain"); + Engine.SetMusicGain(global.music.musicGain); + } + + if (g_IsNetworked) this.setClientPauseState(Engine.GetPlayerGUID(), this.explicitPause); else Index: binaries/data/mods/public/gui/session/message_box/QuitConfirmation.js =================================================================== --- binaries/data/mods/public/gui/session/message_box/QuitConfirmation.js +++ binaries/data/mods/public/gui/session/message_box/QuitConfirmation.js @@ -17,12 +17,12 @@ { // Translation: Shown in the Dialog that shows up when the game finishes "caption": translate("Quit and View Summary"), - "onPress": () => { endGame(true); } + "onPress": () => { Engine.SetMusicGain(global.music.musicGain); endGame(true); } }, { // Translation: Shown in the Dialog that shows up when the game finishes "caption": translate("Quit"), - "onPress": () => { endGame(false); } + "onPress": () => { Engine.SetMusicGain(global.music.musicGain); endGame(false); } } ]; Index: binaries/data/mods/public/gui/session/messages.js =================================================================== --- binaries/data/mods/public/gui/session/messages.js +++ binaries/data/mods/public/gui/session/messages.js @@ -288,6 +288,10 @@ global.music.setLocked(notification.lock); }, + "set-music-state": function(notification, player) + { + global.music.setState(notification.state); + }, "map-flare": function(notification, player) { // Don't display for the player that did the flare because they will see it immediately Index: binaries/data/mods/public/gui/session/session.js =================================================================== --- binaries/data/mods/public/gui/session/session.js +++ binaries/data/mods/public/gui/session/session.js @@ -729,14 +729,6 @@ updateGroups(); updateSelectionDetails(); updateBuildingPlacementPreview(); - - 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 updateGroups() Index: binaries/data/mods/public/simulation/components/BattleDetection.js =================================================================== --- binaries/data/mods/public/simulation/components/BattleDetection.js +++ binaries/data/mods/public/simulation/components/BattleDetection.js @@ -47,8 +47,14 @@ this.state = state; var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); Engine.PostMessage(this.entity, MT_BattleStateChanged, { "player": cmpPlayer.GetPlayerID(), "to": this.state }); -}; + let cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); + cmpGuiInterface.PushNotification({ + "type": "set-music-state", + "players": [cmpPlayer.GetPlayerID()], + "state": this.state + });}; + BattleDetection.prototype.GetState = function() { return this.state; Index: source/soundmanager/ISoundManager.h =================================================================== --- source/soundmanager/ISoundManager.h +++ source/soundmanager/ISoundManager.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2023 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -42,6 +42,7 @@ virtual void PauseAmbient(bool pauseIt) = 0; virtual void PauseAction(bool pauseIt) = 0; + static int m_MusicState; virtual void SetMasterGain(float gain) = 0; virtual void SetMusicGain(float gain) = 0; virtual void SetAmbientGain(float gain) = 0; Index: source/soundmanager/SoundManager.cpp =================================================================== --- source/soundmanager/SoundManager.cpp +++ source/soundmanager/SoundManager.cpp @@ -482,6 +482,8 @@ } } +int ISoundManager::m_MusicState = 0; + void CSoundManager::SetMasterGain(float gain) { if (m_Enabled) Index: source/soundmanager/scripting/JSInterface_Sound.cpp =================================================================== --- source/soundmanager/scripting/JSInterface_Sound.cpp +++ source/soundmanager/scripting/JSInterface_Sound.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2023 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -32,6 +32,18 @@ { #if CONFIG2_AUDIO + void SaveMusicState(int state) + { + if (CSoundManager* sndManager = (CSoundManager*)g_SoundManager) + CSoundManager::m_MusicState = state; + } + + int GetMusicState() + { + if (CSoundManager* sndManager = (CSoundManager*)g_SoundManager) + return CSoundManager::m_MusicState; + } + void StartMusic() { if (CSoundManager* sndManager = (CSoundManager*)g_SoundManager) @@ -117,6 +129,8 @@ #else + void SaveMusicState(int UNUSED(state)) {} + int GetMusicState( ) { return 0; } bool MusicPlaying( ){ return false; } void PlayAmbientSound(const std::wstring& UNUSED(filename), bool UNUSED(looping) ){} void PlayUISound(const std::wstring& UNUSED(filename), bool UNUSED(looping) ) {} @@ -136,6 +150,8 @@ void RegisterScriptFunctions(const ScriptRequest& rq) { + ScriptFunction::Register<&SaveMusicState>(rq, "SaveMusicState"); + ScriptFunction::Register<&GetMusicState>(rq, "GetMusicState"); ScriptFunction::Register<&StartMusic>(rq, "StartMusic"); ScriptFunction::Register<&StopMusic>(rq, "StopMusic"); ScriptFunction::Register<&ClearPlaylist>(rq, "ClearPlaylist");