dynamicmusicplayer.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901
  1. //#define DMP_DEBUG_PRINT
  2. //#define DMP_DEBUG_SETTINGS
  3. class DynamicMusicLocationTypes
  4. {
  5. const int NONE = -1;
  6. const int CONTAMINATED_ZONE = 0;
  7. const int UNDERGROUND = 1;
  8. }
  9. class DynamicMusicLocationDynamicData
  10. {
  11. int m_Type = DynamicMusicLocationTypes.NONE;
  12. //! rectangle coords (2d only)
  13. vector m_Min = vector.Zero;
  14. vector m_Max = vector.Zero;
  15. static array<vector> GetRectangularCoordsFromSize(vector origin, float size)
  16. {
  17. vector min = Vector(origin[0] - size, origin[1], origin[2] - size);
  18. vector max = Vector(origin[0] + size, origin[1], origin[2] + size);
  19. return {min, max};
  20. }
  21. }
  22. class DynamicMusicPlayerSettings
  23. {
  24. float m_MinWaitTimeSeconds = 3.0;
  25. float m_MaxWaitTimeSeconds = 5.0;
  26. float m_PreviousTrackFadeoutSeconds = 30;
  27. }
  28. class DynamicMusicTrackData
  29. {
  30. bool m_HasPriority = false;
  31. int m_TimeOfDay = -1;
  32. int m_LocationType = DynamicMusicLocationTypes.NONE;
  33. string m_SoundSet;
  34. EDynamicMusicPlayerCategory m_Category;
  35. ref array<ref array<vector>> locationBoundaries = new ref array<ref array<vector>>();
  36. void InsertLocation(vector min, vector max)
  37. {
  38. locationBoundaries.Insert({min, max});
  39. }
  40. }
  41. class DynamicMusicPlayerTrackHistoryLookupType
  42. {
  43. const int ANY = 0;
  44. const int BUFFER = 1;
  45. }
  46. class DynamicMusicPlayer
  47. {
  48. #ifdef DMP_DEBUG_SETTINGS
  49. protected const float TICK_TIME_OF_DATE_UPDATE_SECONDS = 10.0;
  50. protected const float TICK_LOCATION_CACHE_UPDATE_SECONDS = 10.0;
  51. protected const float TICK_LOCATION_UPDATE_SECONDS = 5.0;
  52. protected const float TICK_PRIORITY_LOCATION_UPDATE_SECONDS = 2.0;
  53. #else
  54. protected const float TICK_TIME_OF_DATE_UPDATE_SECONDS = 300.0;
  55. protected const float TICK_LOCATION_CACHE_UPDATE_SECONDS = 120.0;
  56. protected const float TICK_LOCATION_UPDATE_SECONDS = 120.0;
  57. protected const float TICK_PRIORITY_LOCATION_UPDATE_SECONDS = 30.0;
  58. #endif
  59. protected const float TICK_FADEOUT_PROCESSOR_SECONDS = 0.2;
  60. protected const int TRACKS_BUFFER_HISTORY_SIZE = 2;
  61. protected const float LOCATION_DISTANCE_MAX = 500;
  62. protected float m_TickTimeOfDateElapsed;
  63. protected float m_TickLocationCacheUpdateElapsed;
  64. protected float m_TickLocationUpdateElapsed;
  65. protected float m_TickPriorityLocationUpdateElapsed;
  66. protected float m_TickFadeOutProcessingElapsed;
  67. protected int m_ActualTimeOfDay
  68. protected EDynamicMusicPlayerCategory m_CategorySelected;
  69. protected DynamicMusicTrackData m_CurrentTrack;
  70. protected ref DynamicMusicPlayerRegistry m_DynamicMusicPlayerRegistry;
  71. protected ref map<int, ref DynamicMusicLocationDynamicData> m_LocationsDynamic //! map of dynamically registered locations during runtime
  72. private ref array<ref DynamicMusicTrackData> m_TracksLocationStaticCached; //! static + filtered by the distance between player and center of zone
  73. private ref array<ref DynamicMusicTrackData> m_TracksLocationStaticPrioritizedCached; //! static prio + filtered by the distance between player and center of zone
  74. protected ref array<ref DynamicMusicTrackData> m_TracksLocationMatchedPlayerInside;
  75. protected AbstractWave m_SoundPlaying;
  76. private bool m_WaitingForPlayback;
  77. private int m_RequestedPlaybackMode //! gets the playback mode as set in sounds menu; 0 - all; 1 - menu only
  78. private ref map<EDynamicMusicPlayerCategory, ref SimpleCircularBuffer<int>> m_LastPlayedTrackBufferPerCategory;
  79. private vector m_PlayerPosition;
  80. private float m_FadeoutTimeElapsed;
  81. private float m_FadeoutTimeRequested;
  82. private bool m_FadeoutInProgress;
  83. private bool m_Created;
  84. void DynamicMusicPlayer(DynamicMusicPlayerRegistry configuration)
  85. {
  86. m_DynamicMusicPlayerRegistry = configuration;
  87. m_RequestedPlaybackMode = g_Game.GetProfileOptionInt(EDayZProfilesOptions.AMBIENT_MUSIC_MODE);
  88. m_ActualTimeOfDay = DynamicMusicPlayerTimeOfDay.ANY;
  89. m_CategorySelected = EDynamicMusicPlayerCategory.NONE;
  90. m_LastPlayedTrackBufferPerCategory = new map<EDynamicMusicPlayerCategory, ref SimpleCircularBuffer<int>>;
  91. m_LastPlayedTrackBufferPerCategory[EDynamicMusicPlayerCategory.MENU] = new SimpleCircularBuffer<int>(TRACKS_BUFFER_HISTORY_SIZE, -1);
  92. m_LastPlayedTrackBufferPerCategory[EDynamicMusicPlayerCategory.TIME] = new SimpleCircularBuffer<int>(TRACKS_BUFFER_HISTORY_SIZE, -1);
  93. m_LastPlayedTrackBufferPerCategory[EDynamicMusicPlayerCategory.LOCATION_STATIC] = new SimpleCircularBuffer<int>(TRACKS_BUFFER_HISTORY_SIZE, -1);
  94. m_LastPlayedTrackBufferPerCategory[EDynamicMusicPlayerCategory.LOCATION_STATIC_PRIORITY] = new SimpleCircularBuffer<int>(TRACKS_BUFFER_HISTORY_SIZE, -1);
  95. m_LastPlayedTrackBufferPerCategory[EDynamicMusicPlayerCategory.LOCATION_DYNAMIC] = new SimpleCircularBuffer<int>(TRACKS_BUFFER_HISTORY_SIZE, -1);
  96. m_LocationsDynamic = new ref map<int, ref DynamicMusicLocationDynamicData>();
  97. m_TracksLocationStaticCached = new array<ref DynamicMusicTrackData>();
  98. m_TracksLocationStaticPrioritizedCached = new array<ref DynamicMusicTrackData>();
  99. m_TracksLocationMatchedPlayerInside = new array<ref DynamicMusicTrackData>();
  100. SetTimeOfDate();
  101. //! fadeout settings
  102. m_FadeoutTimeElapsed = 0.0;
  103. m_FadeoutTimeRequested = 0.0;
  104. g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(RefreshTracksCache, 5000);
  105. DayZProfilesOptions.m_OnIntOptionChanged.Insert(OnProfileOptionChanged);
  106. m_Created = true;
  107. }
  108. void OnUpdate(float timeslice)
  109. {
  110. if (m_DynamicMusicPlayerRegistry == null)
  111. return;
  112. m_TickTimeOfDateElapsed += timeslice;
  113. m_TickLocationCacheUpdateElapsed += timeslice;
  114. m_TickLocationUpdateElapsed += timeslice;
  115. m_TickPriorityLocationUpdateElapsed += timeslice;
  116. m_TickFadeOutProcessingElapsed += timeslice;
  117. //! handle fadeouts
  118. if (m_FadeoutInProgress && m_TickFadeOutProcessingElapsed >= TICK_FADEOUT_PROCESSOR_SECONDS)
  119. {
  120. m_FadeoutTimeElapsed += TICK_FADEOUT_PROCESSOR_SECONDS;
  121. m_TickFadeOutProcessingElapsed = 0.0;
  122. if (m_FadeoutTimeElapsed >= m_FadeoutTimeRequested)
  123. {
  124. m_FadeoutTimeElapsed = 0.0;
  125. m_FadeoutTimeRequested = 0.0;
  126. m_FadeoutInProgress = false;
  127. OnFadeoutFinished(m_CategorySelected);
  128. }
  129. else
  130. ProcessFadeOut();
  131. }
  132. else
  133. {
  134. if (m_CategorySelected != EDynamicMusicPlayerCategory.MENU)
  135. {
  136. //! caching of locations based on distance from player (<= LOCATION_DISTANCE_MAX)
  137. if (m_TickLocationCacheUpdateElapsed >= TICK_LOCATION_CACHE_UPDATE_SECONDS)
  138. {
  139. m_TickLocationCacheUpdateElapsed = 0.0;
  140. RefreshTracksCache();
  141. }
  142. if (m_TickPriorityLocationUpdateElapsed >= TICK_PRIORITY_LOCATION_UPDATE_SECONDS)
  143. {
  144. if (g_Game.GetPlayer())
  145. {
  146. m_PlayerPosition = g_Game.GetPlayer().GetPosition();
  147. m_PlayerPosition[1] = 0.0;
  148. }
  149. m_TickPriorityLocationUpdateElapsed = 0.0;
  150. //! no playback at all OR playback of non-prioritized category
  151. if (!IsPlaybackActive() || (IsPlaybackActive() && !IsPriotitizedCategorySelected()))
  152. {
  153. if (PlayerInsideOfLocationFilter(m_LocationsDynamic))
  154. OnLocationMatched(EDynamicMusicPlayerCategory.LOCATION_DYNAMIC, true);
  155. else if (PlayerInsideOfLocationFilter(m_TracksLocationStaticPrioritizedCached))
  156. OnLocationMatched(EDynamicMusicPlayerCategory.LOCATION_STATIC_PRIORITY, true);
  157. }
  158. }
  159. if (m_TickLocationUpdateElapsed >= TICK_LOCATION_UPDATE_SECONDS)
  160. {
  161. m_TickLocationUpdateElapsed = 0.0;
  162. if (!IsPlaybackActive())
  163. {
  164. if (PlayerInsideOfLocationFilter(m_TracksLocationStaticCached))
  165. OnLocationMatched(EDynamicMusicPlayerCategory.LOCATION_STATIC, false);
  166. }
  167. }
  168. if (m_TickTimeOfDateElapsed >= TICK_TIME_OF_DATE_UPDATE_SECONDS)
  169. {
  170. m_TickTimeOfDateElapsed = 0.0;
  171. SetTimeOfDate();
  172. //! works as default category selector
  173. if (!IsPlaybackActive() || !IsPriotitizedCategorySelected())
  174. SetCategory(EDynamicMusicPlayerCategory.TIME, false);
  175. }
  176. }
  177. else //! menu only
  178. {
  179. if (!IsPlaybackActive())
  180. DetermineTrackByCategory(m_CategorySelected);
  181. }
  182. }
  183. #ifdef DIAG_DEVELOPER
  184. if (DiagMenu.GetBool(DiagMenuIDs.SOUNDS_DYNAMIC_MUSIC_PLAYER_STATS))
  185. {
  186. DisplayDebugStats(true);
  187. DisplayStaticLocations(true);
  188. }
  189. #endif
  190. }
  191. void SetCategory(EDynamicMusicPlayerCategory category, bool forced)
  192. {
  193. if (m_DynamicMusicPlayerRegistry == null)
  194. return;
  195. m_CategorySelected = category;
  196. OnCategorySet(category, forced);
  197. }
  198. void RegisterDynamicLocation(notnull Entity caller, int locationType, float locationSize)
  199. {
  200. int id = caller.GetID();
  201. if (!m_LocationsDynamic.Contains(id))
  202. {
  203. array<vector> minMax = DynamicMusicLocationDynamicData.GetRectangularCoordsFromSize(caller.GetPosition(), locationSize);
  204. DynamicMusicLocationDynamicData location = new DynamicMusicLocationDynamicData();
  205. location.m_Type = locationType;
  206. location.m_Min = minMax[0];
  207. location.m_Max = minMax[1];
  208. m_LocationsDynamic.Insert(id, location);
  209. }
  210. }
  211. void UnregisterDynamicLocation(notnull Entity caller)
  212. {
  213. m_LocationsDynamic.Remove(caller.GetID());
  214. }
  215. void OnGameEvent(EventType eventTypeId, Param params)
  216. {
  217. if (eventTypeId == MPSessionPlayerReadyEventTypeID)
  218. {
  219. SetTimeOfDate();
  220. SetCategory(EDynamicMusicPlayerCategory.TIME, false);
  221. }
  222. }
  223. protected bool IsPriotitizedCategorySelected()
  224. {
  225. return m_CategorySelected == EDynamicMusicPlayerCategory.LOCATION_STATIC_PRIORITY || m_CategorySelected == EDynamicMusicPlayerCategory.LOCATION_DYNAMIC;
  226. }
  227. protected void DetermineTrackByCategory(EDynamicMusicPlayerCategory category)
  228. {
  229. if (m_CategorySelected != EDynamicMusicPlayerCategory.MENU && m_RequestedPlaybackMode == 1)
  230. return;
  231. if (IsPlaybackActive())
  232. return;
  233. switch (category)
  234. {
  235. case EDynamicMusicPlayerCategory.MENU:
  236. if (SetSelectedTrackFromCategory(category, m_DynamicMusicPlayerRegistry.m_TracksMenu, DynamicMusicPlayerTrackHistoryLookupType.BUFFER))
  237. break;
  238. g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(DetermineTrackByCategory, 5000, false, category);
  239. break;
  240. case EDynamicMusicPlayerCategory.TIME:
  241. if (SetSelectedTrackFromCategory(category, m_DynamicMusicPlayerRegistry.m_TracksTime, DynamicMusicPlayerTrackHistoryLookupType.BUFFER))
  242. break;
  243. g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(DetermineTrackByCategory, 5000, false, category);
  244. break;
  245. case EDynamicMusicPlayerCategory.LOCATION_STATIC:
  246. if (SetSelectedTrackFromCategory(category, m_TracksLocationMatchedPlayerInside))
  247. break;
  248. g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(DetermineTrackByCategory, 5000, false, category);
  249. break;
  250. case EDynamicMusicPlayerCategory.LOCATION_STATIC_PRIORITY:
  251. if (SetSelectedTrackFromCategory(category, m_TracksLocationMatchedPlayerInside))
  252. break;
  253. g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(DetermineTrackByCategory, 5000, false, category);
  254. break;
  255. case EDynamicMusicPlayerCategory.LOCATION_DYNAMIC:
  256. if (SetSelectedTrackFromCategory(category, m_DynamicMusicPlayerRegistry.m_TracksLocationDynamic))
  257. break;
  258. g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(DetermineTrackByCategory, 5000, false, category);
  259. break;
  260. }
  261. }
  262. protected bool IsPlaybackActive()
  263. {
  264. return m_SoundPlaying || m_WaitingForPlayback;
  265. }
  266. //! Events
  267. //! --------------------------------------------------------------------------------
  268. protected void OnProfileOptionChanged(EDayZProfilesOptions option, int value)
  269. {
  270. if (option == EDayZProfilesOptions.AMBIENT_MUSIC_MODE)
  271. {
  272. m_RequestedPlaybackMode = value;
  273. if (value == 1 && m_CategorySelected != EDynamicMusicPlayerCategory.MENU)
  274. {
  275. StopTrack();
  276. ResetWaitingQueue();
  277. }
  278. }
  279. }
  280. protected void OnCategorySet(EDynamicMusicPlayerCategory category, bool forced)
  281. {
  282. #ifdef DIAG_DEVELOPER
  283. DMPDebugPrint(string.Format(
  284. "OnCategorySet() - category: %1, forced: %2",
  285. EnumTools.EnumToString(EDynamicMusicPlayerCategory, category),
  286. forced),
  287. );
  288. #endif
  289. DetermineTrackByCategory(category);
  290. }
  291. protected void OnTrackEnded()
  292. {
  293. #ifdef DIAG_DEVELOPER
  294. if (m_CurrentTrack)
  295. DMPDebugPrint(string.Format("Track END - %1", m_CurrentTrack.m_SoundSet));
  296. #endif
  297. m_SoundPlaying = null;
  298. m_CurrentTrack = null;
  299. m_WaitingForPlayback = false;
  300. }
  301. protected void OnTrackStopped()
  302. {
  303. //! stopped only by fadeouts
  304. #ifdef DIAG_DEVELOPER
  305. if (m_CurrentTrack)
  306. DMPDebugPrint(string.Format("Track STOP - %1", m_CurrentTrack.m_SoundSet));
  307. #endif
  308. m_SoundPlaying = null;
  309. m_CurrentTrack = null;
  310. m_WaitingForPlayback = false;
  311. }
  312. protected void OnNextTrackSelected(DynamicMusicTrackData track, float waitTime)
  313. {
  314. m_WaitingForPlayback = true;
  315. m_CurrentTrack = track;
  316. if (m_Created)
  317. m_Created = false;
  318. #ifdef DIAG_DEVELOPER
  319. DMPDebugPrint(string.Format(
  320. "WaitTime set to %1s, deferring playback of \"%2\"",
  321. (int)waitTime,
  322. track.m_SoundSet),
  323. );
  324. m_DebugWaitTime = waitTime;
  325. #endif
  326. g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(PlayTrack, (int)waitTime * 1000, false, track);
  327. }
  328. protected void OnLocationMatched(EDynamicMusicPlayerCategory category, bool isPriorityLocation)
  329. {
  330. #ifdef DIAG_DEVELOPER
  331. string messagePriority;
  332. if (isPriorityLocation)
  333. messagePriority = "(with priority)";
  334. DMPDebugPrint(string.Format("Location matched %1", messagePriority));
  335. #endif
  336. if (isPriorityLocation)
  337. {
  338. if (!IsPriotitizedCategorySelected())
  339. {
  340. m_CategorySelected = category;
  341. FadeoutTrack(GetPreviousTrackFadeoutSeconds(category));
  342. }
  343. else
  344. SetCategory(category, true); //! play prio location track (no fadeout)
  345. }
  346. else
  347. SetCategory(category, false); //! play location track (no fadeout)
  348. }
  349. protected void OnFadeoutFinished(EDynamicMusicPlayerCategory category)
  350. {
  351. if (m_SoundPlaying)
  352. m_SoundPlaying.GetEvents().Event_OnSoundWaveEnded.Remove(OnTrackEnded);
  353. StopTrack();
  354. SetCategory(category, IsPriotitizedCategorySelected());
  355. }
  356. //! --------------------------------------------------------------------------------
  357. private void PlayTrack(DynamicMusicTrackData track)
  358. {
  359. SoundParams soundParams = new SoundParams(track.m_SoundSet);
  360. if (soundParams.IsValid())
  361. {
  362. SoundObjectBuilder soundBuilder = new SoundObjectBuilder(soundParams);
  363. SoundObject soundObject = soundBuilder.BuildSoundObject();
  364. soundObject.SetKind(WaveKind.WAVEMUSIC);
  365. m_SoundPlaying = GetGame().GetSoundScene().Play2D(soundObject, soundBuilder);
  366. if (m_SoundPlaying)
  367. {
  368. m_SoundPlaying.Loop(false);
  369. m_SoundPlaying.Play();
  370. //! register callbacks
  371. m_SoundPlaying.GetEvents().Event_OnSoundWaveEnded.Insert(OnTrackEnded);
  372. m_SoundPlaying.GetEvents().Event_OnSoundWaveStopped.Insert(OnTrackStopped);
  373. m_WaitingForPlayback = false;
  374. }
  375. }
  376. else //! invalid sound set is used
  377. {
  378. m_WaitingForPlayback = false;
  379. m_CurrentTrack = null;
  380. }
  381. }
  382. private void StopTrack()
  383. {
  384. if (m_SoundPlaying)
  385. m_SoundPlaying.Stop();
  386. }
  387. private void ResetWaitingQueue()
  388. {
  389. if (m_WaitingForPlayback)
  390. {
  391. g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).Remove(PlayTrack);
  392. m_WaitingForPlayback = false;
  393. m_CurrentTrack = null;
  394. }
  395. }
  396. private void FadeoutTrack(float fadeoutSeconds)
  397. {
  398. if (m_FadeoutInProgress)
  399. return;
  400. if (m_CurrentTrack && m_SoundPlaying)
  401. {
  402. #ifdef DIAG_DEVELOPER
  403. DMPDebugPrint(string.Format("Stopping currently played track %1", m_CurrentTrack.m_SoundSet));
  404. DMPDebugPrint(string.Format("-- Setting fadeout to %1", fadeoutSeconds));
  405. #endif
  406. m_FadeoutInProgress = true;
  407. m_FadeoutTimeRequested = fadeoutSeconds;
  408. }
  409. }
  410. private void ProcessFadeOut()
  411. {
  412. if (m_SoundPlaying)
  413. {
  414. float volume = 1 - (m_FadeoutTimeElapsed / m_FadeoutTimeRequested);
  415. m_SoundPlaying.SetFadeOutFactor(volume);
  416. }
  417. }
  418. private bool PlayerInsideOfLocationFilter(ref array<ref DynamicMusicTrackData> locations)
  419. {
  420. m_TracksLocationMatchedPlayerInside.Clear();
  421. if (locations.Count() > 0)
  422. {
  423. foreach (DynamicMusicTrackData track : locations)
  424. {
  425. foreach (int locationId, array<vector> bounds : track.locationBoundaries)
  426. {
  427. if (Math.IsPointInRectangle(bounds[0], bounds[1], m_PlayerPosition))
  428. {
  429. if (m_TracksLocationMatchedPlayerInside.Find(track) == INDEX_NOT_FOUND)
  430. m_TracksLocationMatchedPlayerInside.Insert(track);
  431. #ifdef DIAG_DEVELOPER
  432. DMPDebugPrint(string.Format("Player inside location <%1, %2>", bounds[0], bounds[1]));
  433. #endif
  434. }
  435. }
  436. }
  437. }
  438. return m_TracksLocationMatchedPlayerInside.Count() > 0;
  439. }
  440. private bool PlayerInsideOfLocationFilter(ref map<int, ref DynamicMusicLocationDynamicData> locations)
  441. {
  442. if (locations.Count() > 0)
  443. {
  444. foreach (int locationId, DynamicMusicLocationDynamicData location : locations)
  445. {
  446. if (Math.IsPointInRectangle(location.m_Min, location.m_Max, m_PlayerPosition))
  447. {
  448. #ifdef DIAG_DEVELOPER
  449. DMPDebugPrint(string.Format("Player inside location <%1, %2>", location.m_Min, location.m_Max));
  450. #endif
  451. return true;
  452. }
  453. }
  454. }
  455. return false;
  456. }
  457. private bool SetSelectedTrackFromCategory(EDynamicMusicPlayerCategory category, notnull ref array<ref DynamicMusicTrackData> tracklist, int historyLookupType = DynamicMusicPlayerTrackHistoryLookupType.ANY)
  458. {
  459. if (tracklist.Count() == 0)
  460. return true;
  461. array<ref DynamicMusicTrackData> filteredTracks = new array<ref DynamicMusicTrackData>();
  462. foreach (DynamicMusicTrackData filteredTrack : tracklist)
  463. {
  464. if (filteredTrack.m_TimeOfDay == m_ActualTimeOfDay || filteredTrack.m_TimeOfDay == DynamicMusicPlayerTimeOfDay.ANY)
  465. filteredTracks.Insert(filteredTrack);
  466. }
  467. float trackIndex;
  468. //! currently prioritize main menu track on DMP start
  469. if (m_Created && category == EDynamicMusicPlayerCategory.MENU)
  470. trackIndex = SelectRandomTrackIndexFromCategoryPriorityFlagFirst(category, filteredTracks);
  471. else
  472. trackIndex = SelectRandomTrackIndexFromCategory(category, historyLookupType, filteredTracks);
  473. if (trackIndex > INDEX_NOT_FOUND)
  474. {
  475. m_LastPlayedTrackBufferPerCategory[category].Add(trackIndex);
  476. OnNextTrackSelected(filteredTracks[trackIndex], GetWaitTimeForCategory(category));
  477. return true;
  478. }
  479. return false;
  480. }
  481. private int SelectRandomTrackIndexFromCategoryPriorityFlagFirst(EDynamicMusicPlayerCategory category, notnull array<ref DynamicMusicTrackData> tracks)
  482. {
  483. //! main menu priority tracks first
  484. if (category == EDynamicMusicPlayerCategory.MENU)
  485. {
  486. array<int> priorityFlagIndices = new array<int>();
  487. foreach (int i, DynamicMusicTrackData track : tracks)
  488. {
  489. if (!track.m_HasPriority)
  490. continue;
  491. priorityFlagIndices.Insert(i);
  492. }
  493. if (priorityFlagIndices.Count() > 0)
  494. return priorityFlagIndices[priorityFlagIndices.GetRandomIndex()];
  495. //! fallback in case there is no priority track
  496. return tracks.GetRandomIndex();
  497. }
  498. return INDEX_NOT_FOUND;
  499. }
  500. private int SelectRandomTrackIndexFromCategory(EDynamicMusicPlayerCategory category, int lookupType, notnull array<ref DynamicMusicTrackData> tracks)
  501. {
  502. int count = tracks.Count();
  503. if (count > 0)
  504. {
  505. int index = Math.RandomInt(0, count);
  506. switch (lookupType)
  507. {
  508. case DynamicMusicPlayerTrackHistoryLookupType.ANY:
  509. return index;
  510. case DynamicMusicPlayerTrackHistoryLookupType.BUFFER:
  511. // fallback - num of track is smaller than actual history size;
  512. if (count <= TRACKS_BUFFER_HISTORY_SIZE)
  513. return index;
  514. if (m_LastPlayedTrackBufferPerCategory[category].GetValues().Find(index) == INDEX_NOT_FOUND)
  515. return index;
  516. return INDEX_NOT_FOUND;
  517. }
  518. }
  519. return INDEX_NOT_FOUND;
  520. }
  521. private void SetTimeOfDate()
  522. {
  523. if (g_Game.GetMission())
  524. {
  525. m_ActualTimeOfDay = g_Game.GetMission().GetWorldData().GetDaytime();
  526. return;
  527. }
  528. m_ActualTimeOfDay = DynamicMusicPlayerTimeOfDay.DAY;
  529. }
  530. protected float GetWaitTimeForCategory(EDynamicMusicPlayerCategory category)
  531. {
  532. return Math.RandomFloatInclusive(GetMinWaitTimePerCategory(category), GetMaxWaitTimePerCategory(category));
  533. }
  534. private float GetMinWaitTimePerCategory(EDynamicMusicPlayerCategory category)
  535. {
  536. float waitTime = m_DynamicMusicPlayerRegistry.m_SettingsByCategory[category].m_MinWaitTimeSeconds;
  537. #ifdef DIAG_DEVELOPER
  538. if (FeatureTimeAccel.GetFeatureTimeAccelEnabled(ETimeAccelCategories.DYNAMIC_MUSIC_PLAYER))
  539. {
  540. float timeAccel = FeatureTimeAccel.GetFeatureTimeAccelValue();
  541. if (timeAccel > 0)
  542. return waitTime / FeatureTimeAccel.GetFeatureTimeAccelValue();
  543. }
  544. #endif
  545. return waitTime;
  546. }
  547. private float GetMaxWaitTimePerCategory(EDynamicMusicPlayerCategory category)
  548. {
  549. float waitTime = m_DynamicMusicPlayerRegistry.m_SettingsByCategory[category].m_MaxWaitTimeSeconds;
  550. #ifdef DIAG_DEVELOPER
  551. if (FeatureTimeAccel.GetFeatureTimeAccelEnabled(ETimeAccelCategories.DYNAMIC_MUSIC_PLAYER))
  552. {
  553. float timeAccel = FeatureTimeAccel.GetFeatureTimeAccelValue();
  554. if (timeAccel > 0)
  555. return waitTime / FeatureTimeAccel.GetFeatureTimeAccelValue();
  556. }
  557. #endif
  558. return waitTime;
  559. }
  560. private float GetPreviousTrackFadeoutSeconds(EDynamicMusicPlayerCategory category)
  561. {
  562. return m_DynamicMusicPlayerRegistry.m_SettingsByCategory[category].m_PreviousTrackFadeoutSeconds;
  563. }
  564. private void RefreshTracksCache()
  565. {
  566. if (m_DynamicMusicPlayerRegistry)
  567. {
  568. m_TracksLocationStaticCached.Clear();
  569. foreach (DynamicMusicTrackData track : m_DynamicMusicPlayerRegistry.m_TracksLocationStatic)
  570. {
  571. foreach (array<vector> bounds : track.locationBoundaries)
  572. {
  573. if (vector.Distance(m_PlayerPosition, Math.CenterOfRectangle(bounds[0], bounds[1])) > LOCATION_DISTANCE_MAX)
  574. continue;
  575. }
  576. m_TracksLocationStaticCached.Insert(track);
  577. }
  578. m_TracksLocationStaticPrioritizedCached.Clear();
  579. foreach (DynamicMusicTrackData trackPrio : m_DynamicMusicPlayerRegistry.m_TracksLocationStaticPrioritized)
  580. {
  581. foreach (array<vector> boundsPrio : trackPrio.locationBoundaries)
  582. {
  583. if (vector.Distance(m_PlayerPosition, Math.CenterOfRectangle(boundsPrio[0], boundsPrio[1])) > LOCATION_DISTANCE_MAX)
  584. continue;
  585. }
  586. m_TracksLocationStaticPrioritizedCached.Insert(trackPrio);
  587. }
  588. }
  589. }
  590. #ifdef DIAG_DEVELOPER
  591. private ref array<Shape> m_DebugShapesLocations = new array<Shape>();
  592. private float m_DebugWaitTime = 0;
  593. private void DisplayDebugStats(bool enabled)
  594. {
  595. int windowPosX = 10;
  596. int windowPosY = 200;
  597. DbgUI.Begin("DMP - Overall stats", windowPosX, windowPosY);
  598. if (enabled)
  599. {
  600. bool isPlaybackActive = m_SoundPlaying != null;
  601. DbgUI.Text(string.Format("Day/Night: %1", DynamicMusicPlayerTimeOfDay.ToString(m_ActualTimeOfDay)));
  602. DbgUI.Text("Playback:");
  603. DbgUI.Text(string.Format(" active: %1", isPlaybackActive.ToString()));
  604. DbgUI.Text(string.Format(" waiting: %1", m_WaitingForPlayback.ToString()));
  605. DbgUI.Text(string.Format("Selected Category: %1", EnumTools.EnumToString(EDynamicMusicPlayerCategory, m_CategorySelected)));
  606. if (m_CategorySelected != EDynamicMusicPlayerCategory.MENU)
  607. {
  608. DbgUI.Text("Update timers:");
  609. DbgUI.Text(string.Format(" TimeOfDay: %1(%2)", TICK_TIME_OF_DATE_UPDATE_SECONDS, TICK_TIME_OF_DATE_UPDATE_SECONDS - (int)m_TickTimeOfDateElapsed));
  610. DbgUI.Text(string.Format(" Location: %1(%2)", TICK_LOCATION_UPDATE_SECONDS, TICK_LOCATION_UPDATE_SECONDS - (int)m_TickLocationUpdateElapsed));
  611. DbgUI.Text(string.Format(" PriorityLocation: %1(%2)", TICK_PRIORITY_LOCATION_UPDATE_SECONDS, TICK_PRIORITY_LOCATION_UPDATE_SECONDS - (int)m_TickPriorityLocationUpdateElapsed));
  612. DbgUI.Text(string.Format(" Location Cache: %1(%2)", TICK_LOCATION_CACHE_UPDATE_SECONDS, TICK_LOCATION_CACHE_UPDATE_SECONDS - (int)m_TickLocationCacheUpdateElapsed));
  613. }
  614. if (m_CategorySelected != EDynamicMusicPlayerCategory.MENU)
  615. {
  616. DbgUI.Text("Player:");
  617. DbgUI.Text(string.Format(" position: %1", m_PlayerPosition.ToString()));
  618. DbgUI.Text(string.Format(" matched num tracks(location): %1", m_TracksLocationMatchedPlayerInside.Count()));
  619. }
  620. DbgUI.Text("Tracks counts:");
  621. if (m_CategorySelected == EDynamicMusicPlayerCategory.MENU)
  622. DbgUI.Text(string.Format(" Menu: %1", m_DynamicMusicPlayerRegistry.m_TracksMenu.Count()));
  623. else
  624. {
  625. DbgUI.Text(string.Format(" Time: %1", m_DynamicMusicPlayerRegistry.m_TracksTime.Count()));
  626. DbgUI.Text(string.Format(" Static[cache]: %1", m_TracksLocationStaticCached.Count()));
  627. DbgUI.Text(string.Format(" Static(prio)[cache]: %1", m_TracksLocationStaticPrioritizedCached.Count()));
  628. DbgUI.Text(string.Format(" Dynamic(prio): %1", m_DynamicMusicPlayerRegistry.m_TracksLocationDynamic.Count()));
  629. }
  630. }
  631. DbgUI.End();
  632. DbgUI.Begin("DMP - Current track", windowPosX, windowPosY+380);
  633. if (enabled && m_CurrentTrack)
  634. {
  635. string isPlaying = "waiting";
  636. if (m_SoundPlaying != null)
  637. isPlaying = "playing";
  638. DbgUI.Text(string.Format("State: %1", isPlaying));
  639. if (m_WaitingForPlayback)
  640. {
  641. DbgUI.Text(string.Format("Wait time: %1s (%2s)", (int)m_DebugWaitTime, (int)(g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).GetRemainingTime(PlayTrack) * 0.001)));
  642. }
  643. DbgUI.Text(string.Format("Sound set: %1", m_CurrentTrack.m_SoundSet));
  644. DbgUI.Text(string.Format("Category: %1", EnumTools.EnumToString(EDynamicMusicPlayerCategory, m_CurrentTrack.m_Category)));
  645. DbgUI.Text(string.Format("Time of day: %1", DynamicMusicPlayerTimeOfDay.ToString(m_CurrentTrack.m_TimeOfDay)));
  646. }
  647. DbgUI.End();
  648. DbgUI.Begin("DMP - Controls", windowPosX + 500, windowPosY);
  649. if (enabled)
  650. {
  651. if (DbgUI.Button("Stop"))
  652. StopTrack();
  653. DbgUI.Text("Set Category:\n");
  654. if (DbgUI.Button("Time"))
  655. SetCategory(EDynamicMusicPlayerCategory.TIME, false);
  656. if (DbgUI.Button("Location"))
  657. SetCategory(EDynamicMusicPlayerCategory.LOCATION_STATIC, false);
  658. DbgUI.Text("Reset Timers\n");
  659. if (DbgUI.Button("Timer ALL"))
  660. {
  661. m_TickTimeOfDateElapsed = TICK_TIME_OF_DATE_UPDATE_SECONDS - 1;
  662. m_TickLocationUpdateElapsed = TICK_LOCATION_UPDATE_SECONDS - 1;
  663. m_TickPriorityLocationUpdateElapsed = TICK_PRIORITY_LOCATION_UPDATE_SECONDS - 1;
  664. }
  665. if (DbgUI.Button("Timer Daytime"))
  666. m_TickTimeOfDateElapsed = TICK_TIME_OF_DATE_UPDATE_SECONDS - 1;
  667. if (DbgUI.Button("Timer Location"))
  668. m_TickLocationUpdateElapsed = TICK_LOCATION_UPDATE_SECONDS - 1;
  669. if (DbgUI.Button("Timer Location(prio)"))
  670. m_TickPriorityLocationUpdateElapsed = TICK_PRIORITY_LOCATION_UPDATE_SECONDS - 1;
  671. }
  672. DbgUI.End();
  673. }
  674. private void DisplayStaticLocations(bool enabled)
  675. {
  676. if (enabled)
  677. {
  678. vector locationMin;
  679. vector locationMax;
  680. vector position = g_Game.GetCurrentCameraPosition();
  681. foreach (DynamicMusicTrackData track : m_TracksLocationStaticCached)
  682. {
  683. foreach (array<vector> bounds : track.locationBoundaries)
  684. {
  685. locationMin = bounds[0];
  686. locationMax = bounds[1];
  687. if (vector.Distance(position, Math.CenterOfRectangle(locationMin, locationMax)) > 2000)
  688. continue;
  689. CleanupDebugShapes(m_DebugShapesLocations);
  690. locationMax[1] = locationMin[1] + 50.0; //! force the height of box for debug
  691. locationMin[1] = locationMin[1] - 50.0;
  692. m_DebugShapesLocations.Insert(Debug.DrawBoxEx(locationMin, locationMax, Colors.PURPLE, ShapeFlags.NOZWRITE|ShapeFlags.ONCE));
  693. }
  694. }
  695. foreach (DynamicMusicTrackData trackPrio : m_TracksLocationStaticPrioritizedCached)
  696. {
  697. foreach (array<vector> boundsPrio : trackPrio.locationBoundaries)
  698. {
  699. locationMin = boundsPrio[0];
  700. locationMax = boundsPrio[1];
  701. if (vector.Distance(position, Math.CenterOfRectangle(locationMin, locationMax)) > 2000)
  702. continue;
  703. CleanupDebugShapes(m_DebugShapesLocations);
  704. locationMax[1] = locationMin[1] + 50.0; //! force the height of box for debug
  705. locationMin[1] = locationMin[1] - 50.0;
  706. m_DebugShapesLocations.Insert(Debug.DrawBoxEx(locationMin, locationMax, Colors.RED, ShapeFlags.NOZWRITE|ShapeFlags.ONCE));
  707. }
  708. }
  709. foreach (DynamicMusicLocationDynamicData locationDynamic : m_LocationsDynamic)
  710. {
  711. locationMin = locationDynamic.m_Min;
  712. locationMax = locationDynamic.m_Max;
  713. if (vector.Distance(position, Math.CenterOfRectangle(locationMin, locationMax)) > 2000)
  714. continue;
  715. CleanupDebugShapes(m_DebugShapesLocations);
  716. locationMax[1] = locationMin[1] + 50.0; //! force the height of box for debug
  717. locationMin[1] = locationMin[1] - 50.0;
  718. m_DebugShapesLocations.Insert(Debug.DrawBoxEx(locationMin, locationMax, Colors.YELLOW, ShapeFlags.NOZWRITE|ShapeFlags.ONCE));
  719. }
  720. }
  721. else
  722. CleanupDebugShapes(m_DebugShapesLocations);
  723. }
  724. private void CleanupDebugShapes(array<Shape> shapesArr)
  725. {
  726. for ( int it = 0; it < shapesArr.Count(); ++it )
  727. {
  728. Debug.RemoveShape( shapesArr[it] );
  729. }
  730. shapesArr.Clear();
  731. }
  732. private void DMPDebugPrint(string message)
  733. {
  734. #ifdef DMP_DEBUG_PRINT
  735. Debug.Log(message);
  736. #endif
  737. }
  738. #endif
  739. }
  740. //! for backward compatibility
  741. class DynamicMusicPlayerTimeOfDay : WorldDataDaytime {}