From af1a0ca37ab2b2de985b81150f0098f933979f5e Mon Sep 17 00:00:00 2001 From: kaboissonneault Date: Sun, 11 Feb 2024 11:05:22 -0500 Subject: [PATCH 1/3] Fixed issue where Temple music would not update when moving between two different temples in the same city. Fixed issue where Fighter Trainers halls would throw an error in the SongManager (playing the Knight song like in classic instead) --- Assets/Scripts/Game/SongManager.cs | 50 +++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/Assets/Scripts/Game/SongManager.cs b/Assets/Scripts/Game/SongManager.cs index a5872962cb..9d1f43f010 100644 --- a/Assets/Scripts/Game/SongManager.cs +++ b/Assets/Scripts/Game/SongManager.cs @@ -40,6 +40,7 @@ public class SongManager : MonoBehaviour public SongFiles[] RainSongs = _rainSongs; public SongFiles[] SnowSongs = _snowSongs; public SongFiles[] TempleSongs = _templeSongs; + public SongFiles[] KnightSongs = _knightSongs; public SongFiles[] TavernSongs = _tavernSongs; public SongFiles[] NightSongs = _nightSongs; public SongFiles[] ShopSongs = _shopSongs; @@ -61,11 +62,13 @@ struct PlayerMusicContext public PlayerMusicEnvironment environment; public PlayerMusicWeather weather; public PlayerMusicTime time; + public uint factionID; public bool arrested; //minimize GC alloc of struct.Equals(object o) with this method instead public bool Equals(PlayerMusicContext pmc) { - return environment == pmc.environment + return environment == pmc.environment + && factionID == pmc.factionID && weather == pmc.weather && time == pmc.time && arrested == pmc.arrested; @@ -92,6 +95,7 @@ enum PlayerMusicEnvironment DungeonInterior, Graveyard, MagesGuild, + FighterTrainers, Interior, Palace, Shop, @@ -165,6 +169,7 @@ void Start() RainSongs = _weatherRainSongsFM; SnowSongs = _weatherSnowSongsFM; TempleSongs = _templeSongsFM; + KnightSongs = _knightSongsFM; TavernSongs = _tavernSongsFM; NightSongs = _nightSongsFM; ShopSongs = _shopSongsFM; @@ -197,6 +202,7 @@ void UpdateSong(bool forceChange = false) // Update current playlist if context changed if (!currentContext.Equals(lastContext) || (!songPlayer.IsPlaying && playSong) || forceChange) { + bool factionChanged = currentContext.factionID != lastContext.factionID; lastContext = currentContext; SongFiles[] lastPlaylist = currentPlaylist; @@ -204,7 +210,8 @@ void UpdateSong(bool forceChange = false) AssignPlaylist(); // If current playlist is different from last playlist, pick a song from the current playlist - if (currentPlaylist != lastPlaylist || forceChange) + // For the Temple songs playlist, changing factionID can change the song + if (currentPlaylist != lastPlaylist || (currentPlaylist == TempleSongs && factionChanged) || forceChange) { PlayAnotherSong(); return; @@ -282,6 +289,18 @@ public void PlayPreviousSong() #region Private Methods + readonly byte[] templeFactions = { 0x52, 0x54, 0x58, 0x5C, 0x5E, 0x62, 0x6A, 0x24 }; + readonly byte[] godFactions = { 0x15, 0x16, 0x18, 0x1A, 0x1B, 0x1D, 0x21, 0x23 }; + + // Returns -1 if not a template/god faction + int GetTempleIndex(uint factionId) + { + int index = Array.IndexOf(templeFactions, (byte)factionId); + if (index < 0) + index = Array.IndexOf(godFactions, (byte)factionId); + return index; + } + void SelectCurrentSong() { if (currentPlaylist == null || currentPlaylist.Length == 0) @@ -307,14 +326,10 @@ void SelectCurrentSong() index = (int)(random % SnowSongs.Length); else if (currentPlaylist == TempleSongs && playerEnterExit) { - byte[] templeFactions = { 0x52, 0x54, 0x58, 0x5C, 0x5E, 0x62, 0x6A, 0x24 }; uint factionOfPlayerEnvironment = playerEnterExit.FactionID; - index = Array.IndexOf(templeFactions, (byte)factionOfPlayerEnvironment); + index = GetTempleIndex(factionOfPlayerEnvironment); if (index < 0) - { - byte[] godFactions = { 0x15, 0x16, 0x18, 0x1A, 0x1B, 0x1D, 0x21, 0x23 }; - index = Array.IndexOf(godFactions, (byte)factionOfPlayerEnvironment); - } + index = (int)(random % TempleSongs.Length); // Fallback if someone ever creates a new Temple type } else if (currentPlaylist == TavernSongs) { @@ -457,7 +472,15 @@ void UpdatePlayerMusicEnvironment() currentContext.environment = PlayerMusicEnvironment.Palace; break; case DFLocation.BuildingTypes.Temple: - currentContext.environment = PlayerMusicEnvironment.Temple; + if (playerEnterExit.FactionID == (int)FactionFile.FactionIDs.The_Fighters_Guild) + { + currentContext.environment = PlayerMusicEnvironment.FighterTrainers; + } + else + { + currentContext.environment = PlayerMusicEnvironment.Temple; + currentContext.factionID = playerEnterExit.FactionID; // Faction ID changes temple music + } break; default: currentContext.environment = PlayerMusicEnvironment.Interior; @@ -569,6 +592,9 @@ void AssignPlaylist() case PlayerMusicEnvironment.MagesGuild: currentPlaylist = MagesGuildSongs; break; + case PlayerMusicEnvironment.FighterTrainers: + currentPlaylist = KnightSongs; + break; case PlayerMusicEnvironment.Interior: currentPlaylist = InteriorSongs; break; @@ -923,14 +949,14 @@ private void PlayerEnterExit_OnTransitionDungeonInterior(PlayerEnterExit.Transit SongFiles.song_23fm, }; - // Not used in classic. There is unused code to play it in knightly orders - static SongFiles[] _unusedKnightSong = new SongFiles[] + // Only used in Hammerfell "Temple" Fighter's Guild halls. There is unused code to play it in knightly orders + static SongFiles[] _knightSongs = new SongFiles[] { SongFiles.song_17, }; // FM version of above - static SongFiles[] _unusedKnightSongFM = new SongFiles[] + static SongFiles[] _knightSongsFM = new SongFiles[] { SongFiles.song_17fm, }; From 6ed13415511b17ba56610f61c492d475294d13e8 Mon Sep 17 00:00:00 2001 From: kaboissonneault Date: Mon, 12 Feb 2024 08:07:12 -0500 Subject: [PATCH 2/3] Simplified SongManager code to remove Dungeon workaround and be more consistent with how classic handles day transitions --- Assets/Scripts/Game/SongManager.cs | 231 ++++++++++++++++------------- 1 file changed, 129 insertions(+), 102 deletions(-) diff --git a/Assets/Scripts/Game/SongManager.cs b/Assets/Scripts/Game/SongManager.cs index 9d1f43f010..4c84226f97 100644 --- a/Assets/Scripts/Game/SongManager.cs +++ b/Assets/Scripts/Game/SongManager.cs @@ -39,7 +39,9 @@ public class SongManager : MonoBehaviour public SongFiles[] OvercastSongs = _overcastSongs; public SongFiles[] RainSongs = _rainSongs; public SongFiles[] SnowSongs = _snowSongs; - public SongFiles[] TempleSongs = _templeSongs; + public SongFiles[] TempleGoodSongs = _templeGoodSongs; + public SongFiles[] TempleNeutralSongs = _templeNeutralSongs; + public SongFiles[] TempleBadSongs = _templeBadSongs; public SongFiles[] KnightSongs = _knightSongs; public SongFiles[] TavernSongs = _tavernSongs; public SongFiles[] NightSongs = _nightSongs; @@ -62,15 +64,15 @@ struct PlayerMusicContext public PlayerMusicEnvironment environment; public PlayerMusicWeather weather; public PlayerMusicTime time; - public uint factionID; + public uint gameDays; // How many days since playthrough started. Each day gives a new song in a playlist public bool arrested; //minimize GC alloc of struct.Equals(object o) with this method instead public bool Equals(PlayerMusicContext pmc) { return environment == pmc.environment - && factionID == pmc.factionID && weather == pmc.weather && time == pmc.time + && gameDays == pmc.gameDays && arrested == pmc.arrested; } } @@ -100,7 +102,9 @@ enum PlayerMusicEnvironment Palace, Shop, Tavern, - Temple, + TempleGood, + TempleNeutral, + TempleBad, Wilderness, } @@ -168,7 +172,9 @@ void Start() OvercastSongs = _overcastSongsFM; RainSongs = _weatherRainSongsFM; SnowSongs = _weatherSnowSongsFM; - TempleSongs = _templeSongsFM; + TempleGoodSongs = _templeGoodSongsFM; + TempleNeutralSongs = _templeNeutralSongsFM; + TempleBadSongs = _templeBadSongsFM; KnightSongs = _knightSongsFM; TavernSongs = _tavernSongsFM; NightSongs = _nightSongsFM; @@ -180,16 +186,14 @@ void Start() CourtSongs = _courtSongsFM; SneakingSongs = _sneakingSongsFM; } - - PlayerEnterExit.OnTransitionDungeonInterior += PlayerEnterExit_OnTransitionDungeonInterior; } void Update() { - UpdateSong(false); + UpdateSong(); } - void UpdateSong(bool forceChange = false) + void UpdateSong() { if (!songPlayer) return; @@ -200,9 +204,9 @@ void UpdateSong(bool forceChange = false) PlayerMusicUpdateContext(); // Update current playlist if context changed - if (!currentContext.Equals(lastContext) || (!songPlayer.IsPlaying && playSong) || forceChange) + if (!currentContext.Equals(lastContext) || (!songPlayer.IsPlaying && playSong)) { - bool factionChanged = currentContext.factionID != lastContext.factionID; + bool dayChanged = currentContext.gameDays != lastContext.gameDays; lastContext = currentContext; SongFiles[] lastPlaylist = currentPlaylist; @@ -210,8 +214,8 @@ void UpdateSong(bool forceChange = false) AssignPlaylist(); // If current playlist is different from last playlist, pick a song from the current playlist - // For the Temple songs playlist, changing factionID can change the song - if (currentPlaylist != lastPlaylist || (currentPlaylist == TempleSongs && factionChanged) || forceChange) + // For many interiors, changing days will give you a new song + if (currentPlaylist != lastPlaylist || dayChanged) { PlayAnotherSong(); return; @@ -289,8 +293,26 @@ public void PlayPreviousSong() #region Private Methods + enum SongManagerGodAlignement + { + Good, + Neutral, + Bad, + } + readonly byte[] templeFactions = { 0x52, 0x54, 0x58, 0x5C, 0x5E, 0x62, 0x6A, 0x24 }; readonly byte[] godFactions = { 0x15, 0x16, 0x18, 0x1A, 0x1B, 0x1D, 0x21, 0x23 }; + readonly SongManagerGodAlignement[] templeAlignments = + { + SongManagerGodAlignement.Good, // Arkay + SongManagerGodAlignement.Bad, // Z'en + SongManagerGodAlignement.Good, // Mara + SongManagerGodAlignement.Neutral, // Akatosh + SongManagerGodAlignement.Bad, // Julianos + SongManagerGodAlignement.Good, // Dibella + SongManagerGodAlignement.Bad, // Stendarr + SongManagerGodAlignement.Neutral, // Kynareth + }; // Returns -1 if not a template/god faction int GetTempleIndex(uint factionId) @@ -309,32 +331,15 @@ void SelectCurrentSong() int index = 0; // General MIDI song selection { - uint gameMinutes = DaggerfallUnity.Instance.WorldTime.DaggerfallDateTime.ToClassicDaggerfallTime(); - DFRandom.srand(gameMinutes / 1440); - uint random = DFRandom.rand(); - if (currentPlaylist == NightSongs) - index = (int)(random % NightSongs.Length); - else if (currentPlaylist == SunnySongs) - index = (int)(random % SunnySongs.Length); - else if (currentPlaylist == CloudySongs) - index = (int)(random % CloudySongs.Length); - else if (currentPlaylist == OvercastSongs) - index = (int)(random % OvercastSongs.Length); - else if (currentPlaylist == RainSongs) - index = (int)(random % RainSongs.Length); - else if (currentPlaylist == SnowSongs) - index = (int)(random % SnowSongs.Length); - else if (currentPlaylist == TempleSongs && playerEnterExit) - { - uint factionOfPlayerEnvironment = playerEnterExit.FactionID; - index = GetTempleIndex(factionOfPlayerEnvironment); - if (index < 0) - index = (int)(random % TempleSongs.Length); // Fallback if someone ever creates a new Temple type - } - else if (currentPlaylist == TavernSongs) + uint gameDays = currentContext.gameDays; + + // All taverns share the same song for the day, and are played in sequence + // from one day to the next + if (currentPlaylist == TavernSongs) { - index = (int)(gameMinutes / 1440 % TavernSongs.Length); + index = (int)(gameDays % currentPlaylist.Length); } + // Dungeons use a special field for the song selection else if (currentPlaylist == DungeonInteriorSongs) { PlayerGPS gps = GameManager.Instance.PlayerGPS; @@ -346,13 +351,22 @@ void SelectCurrentSong() region = gps.CurrentRegionIndex; } DFRandom.srand(unknown2 ^ ((byte)region << 8)); - random = DFRandom.rand(); + uint random = DFRandom.rand(); index = (int)(random % DungeonInteriorSongs.Length); } + // Mages Guild uses a random track each time + // Sneaking is not used, but if it was, it would probably be random like this else if (currentPlaylist == SneakingSongs || currentPlaylist == MagesGuildSongs) { index = UnityEngine.Random.Range(0, currentPlaylist.Length); } + // Most other places use a random song for the day + else if (currentPlaylist.Length > 1) + { + DFRandom.srand(gameDays); + uint random = DFRandom.rand(); + index = (int)(random % currentPlaylist.Length); + } } currentSong = currentPlaylist[index]; currentSongIndex = index; @@ -478,8 +492,24 @@ void UpdatePlayerMusicEnvironment() } else { - currentContext.environment = PlayerMusicEnvironment.Temple; - currentContext.factionID = playerEnterExit.FactionID; // Faction ID changes temple music + int index = GetTempleIndex(playerEnterExit.FactionID); + if (index >= 0) + { + switch (templeAlignments[index]) + { + case SongManagerGodAlignement.Good: + currentContext.environment = PlayerMusicEnvironment.TempleGood; + break; + + case SongManagerGodAlignement.Neutral: + currentContext.environment = PlayerMusicEnvironment.TempleNeutral; + break; + + case SongManagerGodAlignement.Bad: + currentContext.environment = PlayerMusicEnvironment.TempleBad; + break; + } + } } break; default: @@ -520,6 +550,9 @@ void UpdatePlayerMusicWeather() void UpdatePlayerMusicTime() { + uint gameMinutes = DaggerfallUnity.Instance.WorldTime.DaggerfallDateTime.ToClassicDaggerfallTime(); + currentContext.gameDays = gameMinutes / 1440; + if (DaggerfallUnity.Instance.WorldTime.DaggerfallDateTime.IsDay) currentContext.time = PlayerMusicTime.Day; else @@ -540,43 +573,39 @@ void AssignPlaylist() return; } - // Weather music in cities and wilderness at day - if (!dfUnity.WorldTime.Now.IsNight && - (currentContext.environment == PlayerMusicEnvironment.City || currentContext.environment == PlayerMusicEnvironment.Wilderness)) - { - switch (currentContext.weather) - { - case PlayerMusicWeather.Sunny: - currentPlaylist = SunnySongs; - break; - case PlayerMusicWeather.Cloudy: - currentPlaylist = CloudySongs; - break; - case PlayerMusicWeather.Overcast: - currentPlaylist = OvercastSongs; - break; - case PlayerMusicWeather.Rain: - currentPlaylist = RainSongs; - break; - case PlayerMusicWeather.Snow: - currentPlaylist = SnowSongs; - break; - } - return; - } - - // Cities and wilderness - if (currentContext.environment == PlayerMusicEnvironment.City || currentContext.environment == PlayerMusicEnvironment.Wilderness) - { - // Night songs - if (dfUnity.WorldTime.Now.IsNight) - currentPlaylist = NightSongs; - return; - } - // Environment switch (currentContext.environment) { + case PlayerMusicEnvironment.City: + case PlayerMusicEnvironment.Wilderness: + if (currentContext.time == PlayerMusicTime.Day) + { + switch (currentContext.weather) + { + case PlayerMusicWeather.Sunny: + currentPlaylist = SunnySongs; + break; + case PlayerMusicWeather.Cloudy: + currentPlaylist = CloudySongs; + break; + case PlayerMusicWeather.Overcast: + currentPlaylist = OvercastSongs; + break; + case PlayerMusicWeather.Rain: + currentPlaylist = RainSongs; + break; + case PlayerMusicWeather.Snow: + currentPlaylist = SnowSongs; + break; + } + } + else + { + currentPlaylist = NightSongs; + } + break; + + case PlayerMusicEnvironment.Castle: currentPlaylist = CastleSongs; break; @@ -607,8 +636,14 @@ void AssignPlaylist() case PlayerMusicEnvironment.Tavern: currentPlaylist = TavernSongs; break; - case PlayerMusicEnvironment.Temple: - currentPlaylist = TempleSongs; + case PlayerMusicEnvironment.TempleGood: + currentPlaylist = TempleGoodSongs; + break; + case PlayerMusicEnvironment.TempleNeutral: + currentPlaylist = TempleNeutralSongs; + break; + case PlayerMusicEnvironment.TempleBad: + currentPlaylist = TempleBadSongs; break; } } @@ -617,20 +652,6 @@ void AssignPlaylist() #region Events - - private void PlayerEnterExit_OnTransitionDungeonInterior(PlayerEnterExit.TransitionEventArgs args) - { - if (!songPlayer) - return; - - // Dungeons have a dedicated SongManager and only one play list, so SongManager would never change song - if (currentContext.environment == PlayerMusicEnvironment.DungeonInterior) - { - // Immediately change song (instead of waiting for next Update()) to avoid transitory states - UpdateSong(true); - } - } - #endregion #region Song Playlists @@ -755,16 +776,19 @@ private void PlayerEnterExit_OnTransitionDungeonInterior(PlayerEnterExit.Transit }; // Temple - static SongFiles[] _templeSongs = new SongFiles[] + static SongFiles[] _templeGoodSongs = new SongFiles[] { SongFiles.song_ggood, - SongFiles.song_gbad, - SongFiles.song_ggood, + }; + + static SongFiles[] _templeNeutralSongs = new SongFiles[] + { SongFiles.song_gneut, + }; + + static SongFiles[] _templeBadSongs = new SongFiles[] + { SongFiles.song_gbad, - SongFiles.song_ggood, - SongFiles.song_gbad, - SongFiles.song_gneut, }; // Tavern @@ -853,16 +877,19 @@ private void PlayerEnterExit_OnTransitionDungeonInterior(PlayerEnterExit.Transit }; // Temple FM version - static SongFiles[] _templeSongsFM = new SongFiles[] + static SongFiles[] _templeGoodSongsFM = new SongFiles[] { SongFiles.song_fgood, - SongFiles.song_fbad, - SongFiles.song_fgood, + }; + + static SongFiles[] _templeNeutralSongsFM = new SongFiles[] + { SongFiles.song_fneut, + }; + + static SongFiles[] _templeBadSongsFM = new SongFiles[] + { SongFiles.song_fbad, - SongFiles.song_fgood, - SongFiles.song_fbad, - SongFiles.song_fneut, }; // Tavern FM version From 363d5e59040de9eef7e4e3d9c45a92249d13e7eb Mon Sep 17 00:00:00 2001 From: kaboissonneault Date: Mon, 12 Feb 2024 08:50:02 -0500 Subject: [PATCH 3/3] Store location index in SongManager music context in case someone really goes into two different dungeons in the same day --- Assets/Scripts/Game/SongManager.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Assets/Scripts/Game/SongManager.cs b/Assets/Scripts/Game/SongManager.cs index 4c84226f97..8a64c5e3ac 100644 --- a/Assets/Scripts/Game/SongManager.cs +++ b/Assets/Scripts/Game/SongManager.cs @@ -65,6 +65,7 @@ struct PlayerMusicContext public PlayerMusicWeather weather; public PlayerMusicTime time; public uint gameDays; // How many days since playthrough started. Each day gives a new song in a playlist + public int locationIndex; public bool arrested; //minimize GC alloc of struct.Equals(object o) with this method instead @@ -73,6 +74,7 @@ public bool Equals(PlayerMusicContext pmc) { && weather == pmc.weather && time == pmc.time && gameDays == pmc.gameDays + && locationIndex == pmc.locationIndex && arrested == pmc.arrested; } } @@ -207,6 +209,7 @@ void UpdateSong() if (!currentContext.Equals(lastContext) || (!songPlayer.IsPlaying && playSong)) { bool dayChanged = currentContext.gameDays != lastContext.gameDays; + bool locationChanged = currentContext.locationIndex != lastContext.locationIndex; lastContext = currentContext; SongFiles[] lastPlaylist = currentPlaylist; @@ -215,7 +218,8 @@ void UpdateSong() // If current playlist is different from last playlist, pick a song from the current playlist // For many interiors, changing days will give you a new song - if (currentPlaylist != lastPlaylist || dayChanged) + // For dungeons, changing locations will give you a new song + if (currentPlaylist != lastPlaylist || dayChanged || locationChanged) { PlayAnotherSong(); return; @@ -401,6 +405,8 @@ void UpdatePlayerMusicEnvironment() if (!playerEnterExit || !LocalPlayerGPS || !dfUnity) return; + currentContext.locationIndex = LocalPlayerGPS.CurrentLocationIndex; // -1 for "no location" + // Exteriors if (!playerEnterExit.IsPlayerInside) {