PageRenderTime 36ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/SimsVille/HIT/HITVM.cs

https://gitlab.com/simscolony/FreeSims
C# | 324 lines | 297 code | 26 blank | 1 comment | 12 complexity | cc73d38ad4e9a1408ad505460997e33e MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using TSO.HIT.model;
  6. using FSO.Content;
  7. using System.IO;
  8. using FSO.Files.HIT;
  9. using FSO.HIT.Events;
  10. namespace TSO.HIT
  11. {
  12. public class HITVM
  13. {
  14. private static HITVM INSTANCE;
  15. public static HITVM Get()
  16. {
  17. return INSTANCE; //there can be only one!
  18. }
  19. public static void Init()
  20. {
  21. INSTANCE = new HITVM();
  22. }
  23. //non static stuff
  24. private HITResourceGroup newmain;
  25. private HITResourceGroup relationships;
  26. private HITResourceGroup tsoep5;
  27. private HITResourceGroup tsov2;
  28. private HITResourceGroup tsov3;
  29. private HITResourceGroup turkey;
  30. private HITTVOn MusicEvent;
  31. private HITTVOn NextMusic;
  32. private Dictionary<string, HITSound> ActiveEvents; //events that are active are reused for all objects calling that event.
  33. private List<HITSound> Threads;
  34. private int[] Globals; //SimSpeed 0x64 to CampfireSize 0x87.
  35. private List<FSCPlayer> FSCPlayers;
  36. private Dictionary<string, HITEventRegistration> Events;
  37. private float[] GroupMasterVolumes = new float[]
  38. {
  39. 1.0f, 1.0f, 1.0f, 1.0f
  40. };
  41. public HITVM()
  42. {
  43. var content = FSO.Content.Content.Get();
  44. Events = new Dictionary<string, HITEventRegistration>();
  45. newmain = LoadHitGroup(content.GetPath("sounddata/newmain.hit"), content.GetPath("sounddata/eventlist.txt"), content.GetPath("sounddata/newmain.hsm"));
  46. relationships = LoadHitGroup(content.GetPath("sounddata/relationships.hit"), content.GetPath("sounddata/relationships.evt"), content.GetPath("sounddata/relationships.hsm"));
  47. tsoep5 = LoadHitGroup(content.GetPath("sounddata/tsoep5.hit"), content.GetPath("sounddata/tsoep5.evt"), content.GetPath("sounddata/tsoep5.hsm"));
  48. tsov2 = LoadHitGroup(content.GetPath("sounddata/tsov2.hit"), content.GetPath("sounddata/tsov2.evt"), null); //tsov2 has no hsm file
  49. tsov3 = LoadHitGroup(content.GetPath("sounddata/tsov3.hit"), content.GetPath("sounddata/tsov3.evt"), content.GetPath("sounddata/tsov3.hsm"));
  50. turkey = LoadHitGroup(content.GetPath("sounddata/turkey.hit"), content.GetPath("sounddata/turkey.evt"), content.GetPath("sounddata/turkey.hsm"));
  51. RegisterEvents(newmain);
  52. RegisterEvents(relationships);
  53. RegisterEvents(tsoep5);
  54. RegisterEvents(tsov2);
  55. RegisterEvents(tsov3);
  56. RegisterEvents(turkey);
  57. Globals = new int[36];
  58. Threads = new List<HITSound>();
  59. ActiveEvents = new Dictionary<string, HITSound>();
  60. FSCPlayers = new List<FSCPlayer>();
  61. }
  62. public void WriteGlobal(int num, int value)
  63. {
  64. Globals[num] = value;
  65. }
  66. public int ReadGlobal(int num)
  67. {
  68. return Globals[num];
  69. }
  70. private void RegisterEvents(HITResourceGroup group)
  71. {
  72. var events = group.evt;
  73. for (int i = 0; i < events.Entries.Count; i++)
  74. {
  75. var entry = events.Entries[i];
  76. if (!Events.ContainsKey(entry.Name))
  77. {
  78. Events.Add(entry.Name, new HITEventRegistration()
  79. {
  80. Name = entry.Name,
  81. EventType = (FSO.Files.HIT.HITEvents)entry.EventType,
  82. TrackID = entry.TrackID,
  83. ResGroup = group
  84. });
  85. }
  86. }
  87. }
  88. private HITResourceGroup LoadHitGroup(string HITPath, string EVTPath, string HSMPath)
  89. {
  90. var events = new EVT(EVTPath);
  91. var hitfile = new HITFile(HITPath);
  92. HSM hsmfile = null;
  93. if (HSMPath != null) hsmfile = new HSM(HSMPath);
  94. return new HITResourceGroup()
  95. {
  96. evt = events,
  97. hit = hitfile,
  98. hsm = hsmfile
  99. };
  100. }
  101. public void Tick()
  102. {
  103. for (int i = 0; i < Threads.Count; i++)
  104. {
  105. if (!Threads[i].Tick()) Threads.RemoveAt(i--);
  106. }
  107. for (int i = 0; i < FSCPlayers.Count; i++)
  108. {
  109. FSCPlayers[i].Tick(1/60f);
  110. }
  111. if (NextMusic != null)
  112. {
  113. if (MusicEvent == null || MusicEvent.Dead)
  114. {
  115. MusicEvent = NextMusic;
  116. Threads.Add(NextMusic);
  117. NextMusic = null;
  118. }
  119. }
  120. }
  121. public void StopFSC(FSCPlayer input)
  122. {
  123. FSCPlayers.Remove(input);
  124. }
  125. public FSCPlayer PlayFSC(string path)
  126. {
  127. var dir = Path.GetDirectoryName(path)+"\\";
  128. FSC fsc = new FSC(path);
  129. var player = new FSCPlayer(fsc, dir);
  130. FSCPlayers.Add(player);
  131. return player;
  132. }
  133. public HITSound PlaySoundEvent(string evt)
  134. {
  135. evt = evt.ToLower();
  136. HITThread InterruptBlocker = null; //the thread we have to wait for to finish before we begin.
  137. if (ActiveEvents.ContainsKey(evt))
  138. {
  139. var aevt = ActiveEvents[evt];
  140. if (aevt.Dead) ActiveEvents.Remove(evt); //if the last event is dead, remove and make a new one
  141. else
  142. {
  143. if ((aevt as HITThread)?.InterruptBlocker != null)
  144. {
  145. //we can stop this thread - steal its waiter
  146. (aevt as HITThread).Dead = true;
  147. InterruptBlocker = (aevt as HITThread).InterruptBlocker;
  148. }
  149. else if ((aevt as HITThread)?.Interruptable == true)
  150. {
  151. InterruptBlocker = (aevt as HITThread);
  152. }
  153. else return aevt; //an event of this type is already alive - here, take it.
  154. }
  155. }
  156. var content = FSO.Content.Content.Get();
  157. if (Events.ContainsKey(evt))
  158. {
  159. var evtent = Events[evt];
  160. if (evt.Equals("piano_play", StringComparison.InvariantCultureIgnoreCase))
  161. {
  162. evt = "playpiano";
  163. if (ActiveEvents.ContainsKey(evt))
  164. {
  165. if (ActiveEvents[evt].Dead) ActiveEvents.Remove(evt); //if the last event is dead, remove and make a new one
  166. else return ActiveEvents[evt]; //an event of this type is already alive - here, take it.
  167. }
  168. }
  169. uint TrackID = 0;
  170. uint SubroutinePointer = 0;
  171. if (evtent.ResGroup.hsm != null)
  172. {
  173. var c = evtent.ResGroup.hsm.Constants;
  174. if (c.ContainsKey(evt)) SubroutinePointer = (uint)c[evt];
  175. var trackIdName = "guid_tkd_" + evt;
  176. if (c.ContainsKey(trackIdName)) TrackID = (uint)c[trackIdName];
  177. else TrackID = evtent.TrackID;
  178. }
  179. else
  180. { //no hsm, fallback to eent and event track ids (tsov2)
  181. var entPoints = evtent.ResGroup.hit.EntryPointByTrackID;
  182. TrackID = evtent.TrackID;
  183. if (entPoints.ContainsKey(evtent.TrackID)) SubroutinePointer = entPoints[evtent.TrackID];
  184. }
  185. if (evtent.EventType == HITEvents.kTurnOnTV)
  186. {
  187. var thread = new HITTVOn(evtent.TrackID, this);
  188. thread.VolGroup = HITVolumeGroup.FX;
  189. Threads.Add(thread);
  190. ActiveEvents.Add(evt, thread);
  191. return thread;
  192. }
  193. else if (evtent.EventType == HITEvents.kSetMusicMode)
  194. {
  195. var thread = new HITTVOn(evtent.TrackID, this, true);
  196. thread.VolGroup = HITVolumeGroup.MUSIC;
  197. ActiveEvents.Add(evt, thread);
  198. if (NextMusic != null) NextMusic.Kill();
  199. if (MusicEvent != null) MusicEvent.Fade();
  200. NextMusic = thread;
  201. return thread;
  202. }
  203. else if (SubroutinePointer != 0)
  204. {
  205. var thread = new HITThread(evtent.ResGroup.hit, this);
  206. thread.PC = SubroutinePointer;
  207. if (TrackID != 0) thread.SetTrack(TrackID);
  208. Threads.Add(thread);
  209. if (!ActiveEvents.ContainsKey(evt))
  210. ActiveEvents.Add(evt, thread);
  211. if (InterruptBlocker != null)
  212. {
  213. InterruptBlocker.Interrupt(thread);
  214. InterruptBlocker.KillVocals();
  215. }
  216. return thread;
  217. }
  218. else if (TrackID != 0 && content.Audio.TracksById.ContainsKey(TrackID))
  219. {
  220. var thread = new HITThread(TrackID);
  221. Threads.Add(thread);
  222. ActiveEvents.Add(evt, thread);
  223. if (InterruptBlocker != null)
  224. {
  225. InterruptBlocker.Interrupt(thread);
  226. InterruptBlocker.KillVocals();
  227. }
  228. return thread;
  229. }
  230. }
  231. return null;
  232. }
  233. public float GetMasterVolume(HITVolumeGroup group)
  234. {
  235. return GroupMasterVolumes[(int)group];
  236. }
  237. /// <summary>
  238. /// Ducks all sounds with a priority lower than the one passed.
  239. /// </summary>
  240. /// <param name="DuckPri">The ducking priority under which to duck other sounds.</param>
  241. public void Duck(HITDuckingPriorities DuckPri)
  242. {
  243. for (int i = 0; i < Threads.Count; i++)
  244. {
  245. //0 means least importance, so it gets ducked.
  246. if (Threads[i].DuckPriority < DuckPri)
  247. {
  248. switch (DuckPri)
  249. {
  250. case HITDuckingPriorities.duckpri_low:
  251. Threads[i].SetVolume(0.15f, Threads[i].Pan);
  252. break;
  253. case HITDuckingPriorities.duckpri_normal:
  254. Threads[i].SetVolume(0.25f, Threads[i].Pan);
  255. break;
  256. case HITDuckingPriorities.duckpri_high:
  257. Threads[i].SetVolume(0.45f, Threads[i].Pan);
  258. break;
  259. case HITDuckingPriorities.duckpri_higher:
  260. Threads[i].SetVolume(0.65f, Threads[i].Pan);
  261. break;
  262. case HITDuckingPriorities.duckpri_evenhigher:
  263. //Threads[i].SetVolume(0.85f, Threads[i].Pan);
  264. //If ducking priority is duckpri_never, it shouldn't be ducked!
  265. break;
  266. }
  267. }
  268. }
  269. }
  270. /// <summary>
  271. /// Unducks all threads, I.E sets their volume back to what it was before Duck() was called.
  272. /// </summary>
  273. public void Unduck()
  274. {
  275. for (int i = 0; i < Threads.Count; i++)
  276. Threads[i].SetVolume(Threads[i].PreviousVolume, Threads[i].Pan);
  277. }
  278. }
  279. }