dynamicmusicplayer.c 30 KB

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