DynamicMusicPlayer.c
class DynamicMusicLocationTypesclass DynamicMusicLocationShapeclass DynamicMusicLocationDynamicDataclass DynamicMusicPlayerSettingsclass DynamicMusicTrackDataclass DynamicMusicPlayerTrackHistoryLookupTypeclass DynamicMusicPlayerCategoryPlaybackDataclass DynamicMusicPlayerclass DynamicMusicPlayerTimeOfDay
//#define DMP_DEBUG_PRINT
//#define DMP_DEBUG_SETTINGS
class DynamicMusicLocationTypes
{
const int NONE = -1;
const int CONTAMINATED_ZONE = 0;
const int UNDERGROUND = 1;
}
class DynamicMusicLocationShape
{
const int BOX = 0;
const int POLYGON = 1;
}
class DynamicMusicLocationDynamicData
{
int m_Type = DynamicMusicLocationTypes.NONE;
//! rectangle coords (2d only)
vector m_Min = vector.Zero;
vector m_Max = vector.Zero;
static array<vector> GetRectangularCoordsFromSize(vector origin, float size)
{
vector min = Vector(origin[0] - size, origin[1], origin[2] - size);
vector max = Vector(origin[0] + size, origin[1], origin[2] + size);
return {min, max};
}
}
class DynamicMusicPlayerSettings
{
float m_MinWaitTimeSeconds = 3.0;
float m_MaxWaitTimeSeconds = 5.0;
float m_PreviousTrackFadeoutSeconds = 30;
}
class DynamicMusicTrackData
{
bool m_HasPriority = false;
int m_TimeOfDay = -1;
int m_LocationType = DynamicMusicLocationTypes.NONE;
int m_Shape = DynamicMusicLocationShape.BOX;
string m_SoundSet;
EDynamicMusicPlayerCategory m_Category;
ref array<ref array<vector>> locationBoundaries = new array<ref array<vector>>();
ref array<vector> vertices = new array<vector>();
void InsertLocation(vector min, vector max)
{
locationBoundaries.Insert({min, max});
}
}
class DynamicMusicPlayerTrackHistoryLookupType
{
const int ANY = 0;
const int BUFFER = 1;
}
/*
* \brief Structure for setting of Category playback
* @param m_Forced Force play of category (DEPRECATED)
* @param m_FadeOut If true, previously playing track will be faded out and stopped. Otherwise stopped only.
* @param m_Category Selected category that is going to be played
*/
class DynamicMusicPlayerCategoryPlaybackData
{
bool m_Forced = false;
bool m_FadeOut = false;
EDynamicMusicPlayerCategory m_Category = EDynamicMusicPlayerCategory.NONE;
}
class DynamicMusicPlayer
{
#ifdef DMP_DEBUG_SETTINGS
protected const float TICK_TIME_OF_DATE_UPDATE_SECONDS = 10.0;
protected const float TICK_LOCATION_CACHE_UPDATE_SECONDS = 10.0;
protected const float TICK_LOCATION_UPDATE_SECONDS = 5.0;
protected const float TICK_PRIORITY_LOCATION_UPDATE_SECONDS = 2.0;
#else
protected const float TICK_TIME_OF_DATE_UPDATE_SECONDS = 300.0;
protected const float TICK_LOCATION_CACHE_UPDATE_SECONDS = 120.0;
protected const float TICK_LOCATION_UPDATE_SECONDS = 120.0;
protected const float TICK_PRIORITY_LOCATION_UPDATE_SECONDS = 30.0;
#endif
protected const float TICK_FADEOUT_PROCESSOR_SECONDS = 0.2;
protected const int TRACKS_BUFFER_HISTORY_SIZE = 2;
protected const float LOCATION_DISTANCE_MAX = 500;
protected float m_TickTimeOfDateElapsed;
protected float m_TickLocationCacheUpdateElapsed;
protected float m_TickLocationUpdateElapsed;
protected float m_TickPriorityLocationUpdateElapsed;
protected float m_TickFadeOutProcessingElapsed;
protected int m_ActualTimeOfDay
protected EDynamicMusicPlayerCategory m_CategorySelected;
protected DynamicMusicTrackData m_CurrentTrack;
protected ref DynamicMusicPlayerRegistry m_DynamicMusicPlayerRegistry;
protected ref map<int, ref DynamicMusicLocationDynamicData> m_LocationsDynamic //! map of dynamically registered locations during runtime
private ref array<ref DynamicMusicTrackData> m_TracksLocationStaticCached; //! static + filtered by the distance between player and center of zone
private ref array<ref DynamicMusicTrackData> m_TracksLocationStaticPrioritizedCached; //! static prio + filtered by the distance between player and center of zone
protected ref array<ref DynamicMusicTrackData> m_TracksLocationMatchedPlayerInside;
protected AbstractWave m_SoundPlaying;
private bool m_WaitingForPlayback;
private int m_RequestedPlaybackMode //! gets the playback mode as set in sounds menu; 0 - all; 1 - menu only
private ref map<EDynamicMusicPlayerCategory, ref SimpleCircularBuffer<int>> m_LastPlayedTrackBufferPerCategory;
private vector m_PlayerPosition;
private float m_FadeoutTimeElapsed;
private float m_FadeoutTimeRequested;
private bool m_FadeoutInProgress;
private bool m_Created;
void DynamicMusicPlayer(DynamicMusicPlayerRegistry configuration)
{
m_DynamicMusicPlayerRegistry = configuration;
m_RequestedPlaybackMode = g_Game.GetProfileOptionInt(EDayZProfilesOptions.AMBIENT_MUSIC_MODE);
m_ActualTimeOfDay = DynamicMusicPlayerTimeOfDay.ANY;
m_CategorySelected = EDynamicMusicPlayerCategory.NONE;
m_LastPlayedTrackBufferPerCategory = new map<EDynamicMusicPlayerCategory, ref SimpleCircularBuffer<int>>;
m_LastPlayedTrackBufferPerCategory[EDynamicMusicPlayerCategory.MENU] = new SimpleCircularBuffer<int>(TRACKS_BUFFER_HISTORY_SIZE, -1);
m_LastPlayedTrackBufferPerCategory[EDynamicMusicPlayerCategory.CREDITS] = new SimpleCircularBuffer<int>(TRACKS_BUFFER_HISTORY_SIZE, -1);
m_LastPlayedTrackBufferPerCategory[EDynamicMusicPlayerCategory.TIME] = new SimpleCircularBuffer<int>(TRACKS_BUFFER_HISTORY_SIZE, -1);
m_LastPlayedTrackBufferPerCategory[EDynamicMusicPlayerCategory.LOCATION_STATIC] = new SimpleCircularBuffer<int>(TRACKS_BUFFER_HISTORY_SIZE, -1);
m_LastPlayedTrackBufferPerCategory[EDynamicMusicPlayerCategory.LOCATION_STATIC_PRIORITY] = new SimpleCircularBuffer<int>(TRACKS_BUFFER_HISTORY_SIZE, -1);
m_LastPlayedTrackBufferPerCategory[EDynamicMusicPlayerCategory.LOCATION_DYNAMIC] = new SimpleCircularBuffer<int>(TRACKS_BUFFER_HISTORY_SIZE, -1);
m_LocationsDynamic = new map<int, ref DynamicMusicLocationDynamicData>();
m_TracksLocationStaticCached = new array<ref DynamicMusicTrackData>();
m_TracksLocationStaticPrioritizedCached = new array<ref DynamicMusicTrackData>();
m_TracksLocationMatchedPlayerInside = new array<ref DynamicMusicTrackData>();
SetTimeOfDate();
//! fadeout settings
m_FadeoutTimeElapsed = 0.0;
m_FadeoutTimeRequested = 0.0;
g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(RefreshTracksCache, 5000);
DayZProfilesOptions.m_OnIntOptionChanged.Insert(OnProfileOptionChanged);
m_Created = true;
}
void OnUpdate(float timeslice)
{
if (m_DynamicMusicPlayerRegistry == null)
return;
m_TickTimeOfDateElapsed = Math.Clamp(m_TickTimeOfDateElapsed + timeslice, 0.0, TICK_TIME_OF_DATE_UPDATE_SECONDS);
m_TickLocationCacheUpdateElapsed = Math.Clamp(m_TickLocationCacheUpdateElapsed + timeslice, 0.0, TICK_LOCATION_CACHE_UPDATE_SECONDS);
m_TickLocationUpdateElapsed = Math.Clamp(m_TickLocationUpdateElapsed + timeslice, 0.0, TICK_LOCATION_UPDATE_SECONDS);
m_TickPriorityLocationUpdateElapsed = Math.Clamp(m_TickPriorityLocationUpdateElapsed + timeslice, 0.0, TICK_PRIORITY_LOCATION_UPDATE_SECONDS);
m_TickFadeOutProcessingElapsed = Math.Clamp(m_TickFadeOutProcessingElapsed + timeslice, 0.0, TICK_FADEOUT_PROCESSOR_SECONDS);
//! handle fadeouts
if (m_FadeoutInProgress && m_TickFadeOutProcessingElapsed >= TICK_FADEOUT_PROCESSOR_SECONDS)
{
m_FadeoutTimeElapsed += TICK_FADEOUT_PROCESSOR_SECONDS;
m_TickFadeOutProcessingElapsed = 0.0;
if (m_FadeoutTimeElapsed >= m_FadeoutTimeRequested)
{
m_FadeoutTimeElapsed = 0.0;
m_FadeoutTimeRequested = 0.0;
m_FadeoutInProgress = false;
OnFadeoutFinished(m_CategorySelected);
}
else
ProcessFadeOut();
}
else
{
if (m_CategorySelected != EDynamicMusicPlayerCategory.MENU && m_CategorySelected != EDynamicMusicPlayerCategory.CREDITS)
{
//! caching of locations based on distance from player (<= LOCATION_DISTANCE_MAX)
if (m_TickLocationCacheUpdateElapsed >= TICK_LOCATION_CACHE_UPDATE_SECONDS)
{
m_TickLocationCacheUpdateElapsed = 0.0;
RefreshTracksCache();
}
if (m_TickPriorityLocationUpdateElapsed >= TICK_PRIORITY_LOCATION_UPDATE_SECONDS)
{
if (g_Game.GetPlayer())
{
m_PlayerPosition = g_Game.GetPlayer().GetPosition();
m_PlayerPosition[1] = 0.0;
}
m_TickPriorityLocationUpdateElapsed = 0.0;
//! no playback at all OR playback of non-prioritized category
if ((IsPlaybackActive() && !IsPriotitizedCategorySelected()) || !IsPlaybackActive())
{
if (PlayerInsideOfLocationFilter(m_LocationsDynamic))
OnLocationMatched(EDynamicMusicPlayerCategory.LOCATION_DYNAMIC, true);
else if (PlayerInsideOfLocationFilter(m_TracksLocationStaticPrioritizedCached))
OnLocationMatched(EDynamicMusicPlayerCategory.LOCATION_STATIC_PRIORITY, true);
}
}
if (m_TickLocationUpdateElapsed >= TICK_LOCATION_UPDATE_SECONDS)
{
m_TickLocationUpdateElapsed = 0.0;
if (!IsPlaybackActive())
{
if (PlayerInsideOfLocationFilter(m_TracksLocationStaticCached))
OnLocationMatched(EDynamicMusicPlayerCategory.LOCATION_STATIC, false);
}
}
if (m_TickTimeOfDateElapsed >= TICK_TIME_OF_DATE_UPDATE_SECONDS)
{
m_TickTimeOfDateElapsed = 0.0;
SetTimeOfDate();
//! works as default category selector
if (!IsPlaybackActive() || !IsPriotitizedCategorySelected())
{
DynamicMusicPlayerCategoryPlaybackData playbackData = new DynamicMusicPlayerCategoryPlaybackData();
playbackData.m_Category = EDynamicMusicPlayerCategory.TIME;
SetCategory(playbackData);
}
}
}
else
{
if (!IsPlaybackActive())
DetermineTrackByCategory(m_CategorySelected);
}
}
#ifdef DIAG_DEVELOPER
if (DiagMenu.GetBool(DiagMenuIDs.SOUNDS_DYNAMIC_MUSIC_PLAYER_STATS))
{
DisplayDebugStats(true);
DisplayStaticLocations(true);
}
#endif
}
/*
* \brief Set playback category
* @param playbackData Structure holding information about category playback settings
*/
void SetCategory(DynamicMusicPlayerCategoryPlaybackData playbackData)
{
if (m_DynamicMusicPlayerRegistry == null)
return;
m_CategorySelected = playbackData.m_Category;
if (!playbackData.m_FadeOut)
{
OnCategorySet(playbackData.m_Category, playbackData.m_Forced);
return;
}
//! pass to fadeout handler
FadeoutTrack(GetPreviousTrackFadeoutSeconds(playbackData.m_Category));
}
void RegisterDynamicLocation(notnull Entity caller, int locationType, float locationSize)
{
int id = caller.GetID();
if (!m_LocationsDynamic.Contains(id))
{
array<vector> minMax = DynamicMusicLocationDynamicData.GetRectangularCoordsFromSize(caller.GetPosition(), locationSize);
DynamicMusicLocationDynamicData location = new DynamicMusicLocationDynamicData();
location.m_Type = locationType;
location.m_Min = minMax[0];
location.m_Max = minMax[1];
m_LocationsDynamic.Insert(id, location);
}
}
void UnregisterDynamicLocation(notnull Entity caller)
{
m_LocationsDynamic.Remove(caller.GetID());
}
void OnGameEvent(EventType eventTypeId, Param params)
{
if (eventTypeId == MPSessionPlayerReadyEventTypeID)
{
SetTimeOfDate();
DynamicMusicPlayerCategoryPlaybackData playbackData = new DynamicMusicPlayerCategoryPlaybackData();
playbackData.m_Category = EDynamicMusicPlayerCategory.TIME;
SetCategory(playbackData);
}
}
protected bool IsPriotitizedCategorySelected()
{
return m_CategorySelected == EDynamicMusicPlayerCategory.LOCATION_STATIC_PRIORITY || m_CategorySelected == EDynamicMusicPlayerCategory.LOCATION_DYNAMIC;
}
protected void DetermineTrackByCategory(EDynamicMusicPlayerCategory category)
{
if (m_CategorySelected != EDynamicMusicPlayerCategory.MENU && m_RequestedPlaybackMode == 1)
return;
if (IsPlaybackActive())
return;
switch (category)
{
case EDynamicMusicPlayerCategory.MENU:
if (SetSelectedTrackFromCategory(category, m_DynamicMusicPlayerRegistry.m_TracksMenu, DynamicMusicPlayerTrackHistoryLookupType.BUFFER))
break;
g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(DetermineTrackByCategory, 5000, false, category);
break;
case EDynamicMusicPlayerCategory.CREDITS:
if (SetSelectedTrackFromCategory(category, m_DynamicMusicPlayerRegistry.m_TracksCredits, DynamicMusicPlayerTrackHistoryLookupType.BUFFER))
break;
g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(DetermineTrackByCategory, 5000, false, category);
break;
case EDynamicMusicPlayerCategory.TIME:
if (SetSelectedTrackFromCategory(category, m_DynamicMusicPlayerRegistry.m_TracksTime, DynamicMusicPlayerTrackHistoryLookupType.BUFFER))
break;
g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(DetermineTrackByCategory, 5000, false, category);
break;
case EDynamicMusicPlayerCategory.LOCATION_STATIC:
if (SetSelectedTrackFromCategory(category, m_TracksLocationMatchedPlayerInside))
break;
g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(DetermineTrackByCategory, 5000, false, category);
break;
case EDynamicMusicPlayerCategory.LOCATION_STATIC_PRIORITY:
if (SetSelectedTrackFromCategory(category, m_TracksLocationMatchedPlayerInside))
break;
g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(DetermineTrackByCategory, 5000, false, category);
break;
case EDynamicMusicPlayerCategory.LOCATION_DYNAMIC:
if (SetSelectedTrackFromCategory(category, m_DynamicMusicPlayerRegistry.m_TracksLocationDynamic))
break;
g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(DetermineTrackByCategory, 5000, false, category);
break;
}
}
protected bool IsPlaybackActive()
{
return m_SoundPlaying || m_WaitingForPlayback;
}
//! Events
//! --------------------------------------------------------------------------------
protected void OnProfileOptionChanged(EDayZProfilesOptions option, int value)
{
if (option == EDayZProfilesOptions.AMBIENT_MUSIC_MODE)
{
m_RequestedPlaybackMode = value;
if (value == 1 && m_CategorySelected != EDynamicMusicPlayerCategory.MENU)
{
StopTrack();
ResetWaitingQueue();
}
}
}
protected void OnCategorySet(EDynamicMusicPlayerCategory category, bool forced)
{
#ifdef ENABLE_LOGGING
DMPDebugPrint(string.Format(
"OnCategorySet() - category: %1, forced: %2",
EnumTools.EnumToString(EDynamicMusicPlayerCategory, category),
forced),
);
#endif
DetermineTrackByCategory(category);
}
protected void OnTrackEnded()
{
#ifdef ENABLE_LOGGING
if (m_CurrentTrack)
DMPDebugPrint(string.Format("Track END - %1", m_CurrentTrack.m_SoundSet));
#endif
m_SoundPlaying = null;
m_CurrentTrack = null;
m_WaitingForPlayback = false;
}
protected void OnTrackStopped()
{
//! stopped only by fadeouts
#ifdef ENABLE_LOGGING
if (m_CurrentTrack)
DMPDebugPrint(string.Format("Track STOP - %1", m_CurrentTrack.m_SoundSet));
#endif
m_SoundPlaying = null;
m_CurrentTrack = null;
m_WaitingForPlayback = false;
}
protected void OnNextTrackSelected(DynamicMusicTrackData track, float waitTime)
{
m_WaitingForPlayback = true;
m_CurrentTrack = track;
if (m_Created)
m_Created = false;
#ifdef ENABLE_LOGGING
DMPDebugPrint(string.Format(
"WaitTime set to %1s, deferring playback of \"%2\"",
(int)waitTime,
track.m_SoundSet),
);
#endif
#ifdef DIAG_DEVELOPER
m_DebugWaitTime = waitTime;
#endif
g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(PlayTrack, (int)waitTime * 1000, false, track);
}
protected void OnLocationMatched(EDynamicMusicPlayerCategory category, bool isPriorityLocation)
{
#ifdef ENABLE_LOGGING
string messagePriority;
if (isPriorityLocation)
messagePriority = "(with priority)";
DMPDebugPrint(string.Format("Location matched %1", messagePriority));
#endif
DynamicMusicPlayerCategoryPlaybackData playbackData = new DynamicMusicPlayerCategoryPlaybackData();
playbackData.m_Category = category;
if (isPriorityLocation)
{
playbackData.m_Forced = isPriorityLocation;
if (!IsPriotitizedCategorySelected())
{
m_CategorySelected = category;
if (m_WaitingForPlayback)
ResetWaitingQueue();
if (m_SoundPlaying)
{
playbackData.m_FadeOut = true;
SetCategory(playbackData);
return;
}
SetCategory(playbackData);
}
else
SetCategory(playbackData); //! play prio location track (no fadeout)
}
else
SetCategory(playbackData); //! play location track (no fadeout)
}
protected void OnFadeoutFinished(EDynamicMusicPlayerCategory category)
{
if (m_SoundPlaying)
m_SoundPlaying.GetEvents().Event_OnSoundWaveEnded.Remove(OnTrackEnded);
StopTrack();
DynamicMusicPlayerCategoryPlaybackData playbackData = new DynamicMusicPlayerCategoryPlaybackData();
playbackData.m_Category = category;
playbackData.m_Forced = IsPriotitizedCategorySelected();
SetCategory(playbackData);
}
//! --------------------------------------------------------------------------------
private void PlayTrack(DynamicMusicTrackData track)
{
SoundParams soundParams = new SoundParams(track.m_SoundSet);
if (soundParams.IsValid())
{
SoundObjectBuilder soundBuilder = new SoundObjectBuilder(soundParams);
SoundObject soundObject = soundBuilder.BuildSoundObject();
soundObject.SetKind(WaveKind.WAVEMUSIC);
m_SoundPlaying = g_Game.GetSoundScene().Play2D(soundObject, soundBuilder);
if (m_SoundPlaying)
{
m_SoundPlaying.Loop(false);
m_SoundPlaying.Play();
//! register callbacks
m_SoundPlaying.GetEvents().Event_OnSoundWaveEnded.Insert(OnTrackEnded);
m_SoundPlaying.GetEvents().Event_OnSoundWaveStopped.Insert(OnTrackStopped);
m_WaitingForPlayback = false;
}
}
else //! invalid sound set is used
{
m_WaitingForPlayback = false;
m_CurrentTrack = null;
}
}
private void StopTrack()
{
if (m_SoundPlaying)
m_SoundPlaying.Stop();
}
private void ResetWaitingQueue()
{
if (m_WaitingForPlayback)
{
g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).Remove(PlayTrack);
m_WaitingForPlayback = false;
m_CurrentTrack = null;
}
}
private void FadeoutTrack(float fadeoutSeconds)
{
if (m_FadeoutInProgress)
return;
ResetWaitingQueue();
if (m_CurrentTrack && m_SoundPlaying)
{
#ifdef ENABLE_LOGGING
DMPDebugPrint(string.Format("Stopping currently played track %1", m_CurrentTrack.m_SoundSet));
DMPDebugPrint(string.Format("-- Setting fadeout to %1", fadeoutSeconds));
#endif
m_FadeoutInProgress = true;
m_FadeoutTimeRequested = fadeoutSeconds;
}
}
private void ProcessFadeOut()
{
if (m_SoundPlaying)
{
float volume = 1 - (m_FadeoutTimeElapsed / m_FadeoutTimeRequested);
m_SoundPlaying.SetFadeOutFactor(volume);
}
}
private bool PlayerInsideOfLocationFilter(array<ref DynamicMusicTrackData> locations)
{
m_TracksLocationMatchedPlayerInside.Clear();
if (locations.Count() > 0)
{
foreach (DynamicMusicTrackData track : locations)
{
switch (track.m_Shape)
{
case DynamicMusicLocationShape.BOX:
foreach (int locationId, array<vector> bounds : track.locationBoundaries)
{
if (Math.IsPointInRectangle(bounds[0], bounds[1], m_PlayerPosition))
{
if (m_TracksLocationMatchedPlayerInside.Find(track) == INDEX_NOT_FOUND)
m_TracksLocationMatchedPlayerInside.Insert(track);
#ifdef ENABLE_LOGGING
DMPDebugPrint(string.Format("Player inside location <%1, %2>", bounds[0], bounds[1]));
#endif
}
}
break;
case DynamicMusicLocationShape.POLYGON:
if (Math2D.IsPointInPolygonXZ(track.vertices, m_PlayerPosition))
{
if (m_TracksLocationMatchedPlayerInside.Find(track) == INDEX_NOT_FOUND)
m_TracksLocationMatchedPlayerInside.Insert(track);
#ifdef ENABLE_LOGGING
DMPDebugPrint(string.Format("Player inside polygon location at <%1>", m_PlayerPosition));
#endif
}
break;
}
}
}
return m_TracksLocationMatchedPlayerInside.Count() > 0;
}
private bool PlayerInsideOfLocationFilter(map<int, ref DynamicMusicLocationDynamicData> locations)
{
if (locations.Count() > 0)
{
foreach (int locationId, DynamicMusicLocationDynamicData location : locations)
{
if (Math.IsPointInRectangle(location.m_Min, location.m_Max, m_PlayerPosition))
{
#ifdef ENABLE_LOGGING
DMPDebugPrint(string.Format("Player inside location <%1, %2>", location.m_Min, location.m_Max));
#endif
return true;
}
}
}
return false;
}
private bool SetSelectedTrackFromCategory(EDynamicMusicPlayerCategory category, notnull array<ref DynamicMusicTrackData> tracklist, int historyLookupType = DynamicMusicPlayerTrackHistoryLookupType.ANY)
{
if (tracklist.Count() == 0)
return true;
array<ref DynamicMusicTrackData> filteredTracks = new array<ref DynamicMusicTrackData>();
foreach (DynamicMusicTrackData filteredTrack : tracklist)
{
if (filteredTrack.m_TimeOfDay == m_ActualTimeOfDay || filteredTrack.m_TimeOfDay == DynamicMusicPlayerTimeOfDay.ANY)
filteredTracks.Insert(filteredTrack);
}
float trackIndex;
//! currently prioritize main menu track on DMP start
if (m_Created && category == EDynamicMusicPlayerCategory.MENU)
trackIndex = SelectRandomTrackIndexFromCategoryPriorityFlagFirst(category, filteredTracks);
else
trackIndex = SelectRandomTrackIndexFromCategory(category, historyLookupType, filteredTracks);
if (trackIndex > INDEX_NOT_FOUND)
{
m_LastPlayedTrackBufferPerCategory[category].Add(trackIndex);
OnNextTrackSelected(filteredTracks[trackIndex], GetWaitTimeForCategory(category));
return true;
}
return false;
}
private int SelectRandomTrackIndexFromCategoryPriorityFlagFirst(EDynamicMusicPlayerCategory category, notnull array<ref DynamicMusicTrackData> tracks)
{
//! main menu priority tracks first
if (category == EDynamicMusicPlayerCategory.MENU)
{
array<int> priorityFlagIndices = new array<int>();
foreach (int i, DynamicMusicTrackData track : tracks)
{
if (!track.m_HasPriority)
continue;
priorityFlagIndices.Insert(i);
}
if (priorityFlagIndices.Count() > 0)
return priorityFlagIndices[priorityFlagIndices.GetRandomIndex()];
//! fallback in case there is no priority track
return tracks.GetRandomIndex();
}
return INDEX_NOT_FOUND;
}
private int SelectRandomTrackIndexFromCategory(EDynamicMusicPlayerCategory category, int lookupType, notnull array<ref DynamicMusicTrackData> tracks)
{
int count = tracks.Count();
if (count > 0)
{
int index = Math.RandomInt(0, count);
switch (lookupType)
{
case DynamicMusicPlayerTrackHistoryLookupType.ANY:
return index;
case DynamicMusicPlayerTrackHistoryLookupType.BUFFER:
// fallback - num of track is smaller than actual history size;
if (count <= TRACKS_BUFFER_HISTORY_SIZE)
return index;
if (m_LastPlayedTrackBufferPerCategory[category].GetValues().Find(index) == INDEX_NOT_FOUND)
return index;
return INDEX_NOT_FOUND;
}
}
return INDEX_NOT_FOUND;
}
private void SetTimeOfDate()
{
Mission mission = g_Game.GetMission();
if (mission)
{
m_ActualTimeOfDay = mission.GetWorldData().GetDaytime();
return;
}
m_ActualTimeOfDay = DynamicMusicPlayerTimeOfDay.DAY;
}
protected float GetWaitTimeForCategory(EDynamicMusicPlayerCategory category)
{
return Math.RandomFloatInclusive(GetMinWaitTimePerCategory(category), GetMaxWaitTimePerCategory(category));
}
private float GetMinWaitTimePerCategory(EDynamicMusicPlayerCategory category)
{
float waitTime = m_DynamicMusicPlayerRegistry.m_SettingsByCategory[category].m_MinWaitTimeSeconds;
#ifdef DIAG_DEVELOPER
if (FeatureTimeAccel.GetFeatureTimeAccelEnabled(ETimeAccelCategories.DYNAMIC_MUSIC_PLAYER))
{
float timeAccel = FeatureTimeAccel.GetFeatureTimeAccelValue();
if (timeAccel > 0)
return waitTime / FeatureTimeAccel.GetFeatureTimeAccelValue();
}
#endif
return waitTime;
}
private float GetMaxWaitTimePerCategory(EDynamicMusicPlayerCategory category)
{
float waitTime = m_DynamicMusicPlayerRegistry.m_SettingsByCategory[category].m_MaxWaitTimeSeconds;
#ifdef DIAG_DEVELOPER
if (FeatureTimeAccel.GetFeatureTimeAccelEnabled(ETimeAccelCategories.DYNAMIC_MUSIC_PLAYER))
{
float timeAccel = FeatureTimeAccel.GetFeatureTimeAccelValue();
if (timeAccel > 0)
return waitTime / FeatureTimeAccel.GetFeatureTimeAccelValue();
}
#endif
return waitTime;
}
private float GetPreviousTrackFadeoutSeconds(EDynamicMusicPlayerCategory category)
{
return m_DynamicMusicPlayerRegistry.m_SettingsByCategory[category].m_PreviousTrackFadeoutSeconds;
}
private void RefreshTracksCache()
{
if (m_DynamicMusicPlayerRegistry)
{
m_TracksLocationStaticCached.Clear();
foreach (DynamicMusicTrackData track : m_DynamicMusicPlayerRegistry.m_TracksLocationStatic)
{
if (track.m_Shape == DynamicMusicLocationShape.BOX)
{
foreach (array<vector> bounds : track.locationBoundaries)
{
if (vector.Distance(m_PlayerPosition, Math.CenterOfRectangle(bounds[0], bounds[1])) > LOCATION_DISTANCE_MAX)
continue;
}
}
m_TracksLocationStaticCached.Insert(track);
}
m_TracksLocationStaticPrioritizedCached.Clear();
foreach (DynamicMusicTrackData trackPrio : m_DynamicMusicPlayerRegistry.m_TracksLocationStaticPrioritized)
{
if (trackPrio.m_Shape == DynamicMusicLocationShape.BOX)
{
foreach (array<vector> boundsPrio : trackPrio.locationBoundaries)
{
if (vector.Distance(m_PlayerPosition, Math.CenterOfRectangle(boundsPrio[0], boundsPrio[1])) > LOCATION_DISTANCE_MAX)
continue;
}
}
m_TracksLocationStaticPrioritizedCached.Insert(trackPrio);
}
}
}
#ifdef DIAG_DEVELOPER
private ref array<Shape> m_DebugShapesLocations = new array<Shape>();
private ref array<Shape> m_DebugShapesLocationsVertices = new array<Shape>();
private float m_DebugWaitTime = 0;
private void DisplayDebugStats(bool enabled)
{
int windowPosX = 10;
int windowPosY = 200;
DbgUI.Begin("DMP - Overall stats", windowPosX, windowPosY);
if (enabled)
{
bool isPlaybackActive = m_SoundPlaying != null;
DbgUI.Text(string.Format("Day/Night: %1", DynamicMusicPlayerTimeOfDay.ToString(m_ActualTimeOfDay)));
DbgUI.Text("Playback:");
DbgUI.Text(string.Format(" active: %1", isPlaybackActive.ToString()));
DbgUI.Text(string.Format(" waiting: %1", m_WaitingForPlayback.ToString()));
DbgUI.Text(string.Format("Selected Category: %1", EnumTools.EnumToString(EDynamicMusicPlayerCategory, m_CategorySelected)));
if (m_CategorySelected != EDynamicMusicPlayerCategory.MENU && m_CategorySelected != EDynamicMusicPlayerCategory.CREDITS)
{
DbgUI.Text("Update timers:");
DbgUI.Text(string.Format(" TimeOfDay: %1(%2)", TICK_TIME_OF_DATE_UPDATE_SECONDS, TICK_TIME_OF_DATE_UPDATE_SECONDS - (int)m_TickTimeOfDateElapsed));
DbgUI.Text(string.Format(" Location: %1(%2)", TICK_LOCATION_UPDATE_SECONDS, TICK_LOCATION_UPDATE_SECONDS - (int)m_TickLocationUpdateElapsed));
DbgUI.Text(string.Format(" PriorityLocation: %1(%2)", TICK_PRIORITY_LOCATION_UPDATE_SECONDS, TICK_PRIORITY_LOCATION_UPDATE_SECONDS - (int)m_TickPriorityLocationUpdateElapsed));
DbgUI.Text(string.Format(" Location Cache: %1(%2)", TICK_LOCATION_CACHE_UPDATE_SECONDS, TICK_LOCATION_CACHE_UPDATE_SECONDS - (int)m_TickLocationCacheUpdateElapsed));
}
if (m_CategorySelected != EDynamicMusicPlayerCategory.MENU && m_CategorySelected != EDynamicMusicPlayerCategory.CREDITS)
{
DbgUI.Text("Player:");
DbgUI.Text(string.Format(" position: %1", m_PlayerPosition.ToString()));
DbgUI.Text(string.Format(" matched num tracks(location): %1", m_TracksLocationMatchedPlayerInside.Count()));
}
DbgUI.Text("Tracks counts:");
if (m_CategorySelected == EDynamicMusicPlayerCategory.MENU || m_CategorySelected == EDynamicMusicPlayerCategory.CREDITS)
{
DbgUI.Text(string.Format(" Menu: %1", m_DynamicMusicPlayerRegistry.m_TracksMenu.Count()));
DbgUI.Text(string.Format(" Credits: %1", m_DynamicMusicPlayerRegistry.m_TracksCredits.Count()));
}
else
{
DbgUI.Text(string.Format(" Time: %1", m_DynamicMusicPlayerRegistry.m_TracksTime.Count()));
DbgUI.Text(string.Format(" Static[cache]: %1", m_TracksLocationStaticCached.Count()));
DbgUI.Text(string.Format(" Static(prio)[cache]: %1", m_TracksLocationStaticPrioritizedCached.Count()));
DbgUI.Text(string.Format(" Dynamic(prio): %1", m_DynamicMusicPlayerRegistry.m_TracksLocationDynamic.Count()));
}
}
DbgUI.End();
DbgUI.Begin("DMP - Current track", windowPosX, windowPosY+380);
if (enabled && m_CurrentTrack)
{
string isPlaying = "waiting";
if (m_SoundPlaying != null)
isPlaying = "playing";
DbgUI.Text(string.Format("State: %1", isPlaying));
if (m_WaitingForPlayback)
{
DbgUI.Text(string.Format("Wait time: %1s (%2s)", (int)m_DebugWaitTime, (int)(g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).GetRemainingTime(PlayTrack) * 0.001)));
}
DbgUI.Text(string.Format("Sound set: %1", m_CurrentTrack.m_SoundSet));
DbgUI.Text(string.Format("Category: %1", EnumTools.EnumToString(EDynamicMusicPlayerCategory, m_CurrentTrack.m_Category)));
DbgUI.Text(string.Format("Time of day: %1", DynamicMusicPlayerTimeOfDay.ToString(m_CurrentTrack.m_TimeOfDay)));
}
DbgUI.End();
DbgUI.Begin("DMP - Controls", windowPosX + 500, windowPosY);
if (enabled)
{
if (DbgUI.Button("Stop"))
StopTrack();
if (DbgUI.Button("Reset Waiting"))
ResetWaitingQueue();
DynamicMusicPlayerCategoryPlaybackData playbackData = new DynamicMusicPlayerCategoryPlaybackData();
playbackData.m_Category = EDynamicMusicPlayerCategory.TIME;
DbgUI.Text("Set Category:\n");
if (DbgUI.Button("Time"))
SetCategory(playbackData);
if (DbgUI.Button("Location"))
{
playbackData.m_Category = EDynamicMusicPlayerCategory.LOCATION_STATIC;
SetCategory(playbackData);
}
if (DbgUI.Button("Menu"))
{
playbackData.m_Category = EDynamicMusicPlayerCategory.MENU;
SetCategory(playbackData);
}
if (DbgUI.Button("Credits"))
{
playbackData.m_Category = EDynamicMusicPlayerCategory.CREDITS;
SetCategory(playbackData);
}
DbgUI.Text("Reset Timers\n");
if (DbgUI.Button("Timer ALL"))
{
m_TickTimeOfDateElapsed = TICK_TIME_OF_DATE_UPDATE_SECONDS - 1.0;
m_TickLocationUpdateElapsed = TICK_LOCATION_UPDATE_SECONDS - 1.0;
m_TickPriorityLocationUpdateElapsed = TICK_PRIORITY_LOCATION_UPDATE_SECONDS - 1.0;
}
if (DbgUI.Button("Timer Daytime"))
m_TickTimeOfDateElapsed = TICK_TIME_OF_DATE_UPDATE_SECONDS - 1.0;
if (DbgUI.Button("Timer Location"))
m_TickLocationUpdateElapsed = TICK_LOCATION_UPDATE_SECONDS - 1.0;
if (DbgUI.Button("Timer Location(prio)"))
m_TickPriorityLocationUpdateElapsed = TICK_PRIORITY_LOCATION_UPDATE_SECONDS - 1.0;
}
DbgUI.End();
}
private void DisplayStaticLocations(bool enabled)
{
if (enabled)
{
vector locationMin;
vector locationMax;
vector position = g_Game.GetCurrentCameraPosition();
foreach (DynamicMusicTrackData track : m_TracksLocationStaticCached)
{
foreach (array<vector> bounds : track.locationBoundaries)
{
locationMin = bounds[0];
locationMax = bounds[1];
if (vector.Distance(position, Math.CenterOfRectangle(locationMin, locationMax)) > 2000)
continue;
Debug.CleanupDrawShapes(m_DebugShapesLocations);
locationMax[1] = locationMin[1] + 200.0; //! force the height of box for debug
locationMin[1] = locationMin[1] - 50.0;
m_DebugShapesLocations.Insert(Debug.DrawBoxEx(locationMin, locationMax, Colors.PURPLE, ShapeFlags.NOZWRITE|ShapeFlags.ONCE));
}
Debug.CleanupDrawShapes(m_DebugShapesLocationsVertices);
DrawPolygonLocation(track);
}
foreach (DynamicMusicTrackData trackPrio : m_TracksLocationStaticPrioritizedCached)
{
foreach (array<vector> boundsPrio : trackPrio.locationBoundaries)
{
locationMin = boundsPrio[0];
locationMax = boundsPrio[1];
if (vector.Distance(position, Math.CenterOfRectangle(locationMin, locationMax)) > 2000)
continue;
Debug.CleanupDrawShapes(m_DebugShapesLocations);
locationMax[1] = locationMin[1] + 200.0; //! force the height of box for debug
locationMin[1] = locationMin[1] - 50.0;
m_DebugShapesLocations.Insert(Debug.DrawBoxEx(locationMin, locationMax, Colors.RED, ShapeFlags.NOZWRITE|ShapeFlags.ONCE));
}
Debug.CleanupDrawShapes(m_DebugShapesLocationsVertices);
DrawPolygonLocation(trackPrio);
}
foreach (DynamicMusicLocationDynamicData locationDynamic : m_LocationsDynamic)
{
locationMin = locationDynamic.m_Min;
locationMax = locationDynamic.m_Max;
if (vector.Distance(position, Math.CenterOfRectangle(locationMin, locationMax)) > 2000)
continue;
Debug.CleanupDrawShapes(m_DebugShapesLocations);
locationMax[1] = locationMin[1] + 200.0; //! force the height of box for debug
locationMin[1] = locationMin[1] - 50.0;
m_DebugShapesLocations.Insert(Debug.DrawBoxEx(locationMin, locationMax, Colors.YELLOW, ShapeFlags.NOZWRITE|ShapeFlags.ONCE));
}
}
else
{
Debug.CleanupDrawShapes(m_DebugShapesLocations);
Debug.CleanupDrawShapes(m_DebugShapesLocationsVertices);
}
}
private void DrawPolygonLocation(notnull DynamicMusicTrackData track)
{
vector first, current, last;
int count = track.vertices.Count();
foreach (int i, vector vertexPos : track.vertices)
{
vertexPos[1] = vertexPos[1] + 0.5;
current = vertexPos;
if (i == 0)
first = vertexPos;
else
m_DebugShapesLocationsVertices.Insert(Debug.DrawLine(last, current, COLOR_WHITE, ShapeFlags.TRANSP|ShapeFlags.NOZWRITE|ShapeFlags.ONCE));
last = current;
}
m_DebugShapesLocationsVertices.Insert(Debug.DrawLine(current, first, COLOR_WHITE, ShapeFlags.TRANSP|ShapeFlags.NOZWRITE|ShapeFlags.ONCE));
}
#endif
#ifdef ENABLE_LOGGING
private void DMPDebugPrint(string message)
{
#ifdef DMP_DEBUG_PRINT
Debug.Log(message);
#endif
}
#endif
//!DEPRECATED
private void CleanupDebugShapes(array<Shape> shapesArr)
{
Debug.CleanupDrawShapes(shapesArr);
}
void SetCategory(EDynamicMusicPlayerCategory category, bool forced)
{
DynamicMusicPlayerCategoryPlaybackData playbackData = new DynamicMusicPlayerCategoryPlaybackData();
playbackData.m_Category = category;
playbackData.m_Forced = forced;
SetCategory(playbackData);
}
}
//! for backward compatibility
class DynamicMusicPlayerTimeOfDay : WorldDataDaytime {}