spookyareamisc.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. //-------------------------------------------------------------
  2. class SpookyEventWind : SpookyEventBase
  3. {
  4. override protected void Init()
  5. {
  6. SetCoolDown(65);
  7. m_SoundSet = "SpookyArea_WhistlingWind_SoundSet";
  8. }
  9. override protected bool CanDo(PlayerBase player, TStringVectorMap surfaceTypes)
  10. {
  11. return player.IsSoundInsideBuilding();
  12. }
  13. override protected void Do(PlayerBase player)
  14. {
  15. //put additional code here
  16. }
  17. }
  18. //-------------------------------------------------------------
  19. class SpookyEventWhisper : SpookyEventBase
  20. {
  21. override protected void Init()
  22. {
  23. SetCoolDown(80);
  24. m_SoundSet = "SpookyArea_Whispering_SoundSet";
  25. }
  26. override protected void Do(PlayerBase player)
  27. {
  28. //put additional code here
  29. }
  30. }
  31. //-------------------------------------------------------------
  32. class SpookyEventSteps : SpookyEventBase
  33. {
  34. override protected void Init()
  35. {
  36. SetCoolDown(40);
  37. m_SoundSet = "SpookyArea_RunOnConcrete_SoundSet";
  38. m_Surfaces = {"stone", "gravel", "concrete", "wood", "asphalt", "tiles", "textile"};
  39. }
  40. override protected void Do(PlayerBase player)
  41. {
  42. //put additional code here
  43. }
  44. }
  45. //-------------------------------------------------------------
  46. class SpookyEventRustle : SpookyEventBase
  47. {
  48. override protected void Init()
  49. {
  50. SetCoolDown(60);
  51. m_SoundSet = "SpookyArea_IntenseFoliageRustle_SoundSet";
  52. m_Surfaces = {"grass", "dirt", "forest", "soil"};
  53. }
  54. override protected bool CanDo(PlayerBase player, TStringVectorMap surfaceTypes)
  55. {
  56. return !player.IsSoundInsideBuilding();
  57. }
  58. override protected void Do(PlayerBase player)
  59. {
  60. vector soundPos = GetSoundPos(player);
  61. string secondarySoundSet = "SpookyArea_Hedgehog_SoundSet";
  62. if (Math.RandomBool())
  63. {
  64. secondarySoundSet = "SpookyArea_Badger_Voice_SoundSet";
  65. }
  66. EffectSound sound = SEffectManager.PlaySound(secondarySoundSet, soundPos);
  67. sound.SetAutodestroy( true );
  68. }
  69. }
  70. //-------------------------------------------------------------
  71. //-------------------------------------------------------------
  72. //-------------------------------------------------------------
  73. class SpookyEventBase
  74. {
  75. //internal params, do not set manually
  76. protected float m_PerformedTimestamp;//internal, marks the time this event was last performed so that it can guruantee the cooldown period before playing it again
  77. protected int m_Cooldown;//how much time needs to elapse between this event can be performed again in seconds
  78. //the params bellow can be set in the 'Init' method
  79. protected string m_SoundSet;//if set, will play this soundset when performing the event
  80. protected ref TStringArray m_Surfaces;//if set, the player needs to detect this surface for the event to be considered valid
  81. protected vector m_MatchingSurfacePos;//position of the first matching surface
  82. void SpookyEventBase()
  83. {
  84. Init();
  85. }
  86. protected void Init();
  87. protected vector GetMatchingSurfacePos(TStringArray surfaces, TStringVectorMap gatheredSurfaces)
  88. {
  89. for (int i = 0; i < gatheredSurfaces.Count(); i++)
  90. {
  91. string currSurface = gatheredSurfaces.GetKey(i);
  92. foreach (string s:surfaces)
  93. {
  94. if (currSurface.Contains(s))
  95. return gatheredSurfaces.Get(currSurface);
  96. }
  97. }
  98. return vector.Zero;
  99. }
  100. protected void SetCoolDown(float secs)
  101. {
  102. m_Cooldown = secs * 1000;
  103. }
  104. protected bool HasSurfaces()
  105. {
  106. return (m_Surfaces && m_Surfaces.Count() > 0);
  107. }
  108. protected bool CanDo(PlayerBase player, TStringVectorMap surfaceTypes)
  109. {
  110. return true;
  111. }
  112. protected void Do(PlayerBase player);
  113. //internal, do not override, use 'CanDo' instead
  114. bool CanPerform(PlayerBase player, float currentTime, TStringVectorMap surfaceTypes)
  115. {
  116. bool surfaceCheckResult = 1;
  117. if (HasSurfaces())
  118. {
  119. m_MatchingSurfacePos = GetMatchingSurfacePos(m_Surfaces, surfaceTypes);
  120. surfaceCheckResult = m_MatchingSurfacePos != vector.Zero;
  121. }
  122. return ( currentTime > (m_PerformedTimestamp + m_Cooldown) && surfaceCheckResult && CanDo(player, surfaceTypes) );
  123. }
  124. //internal, do not override, use 'Do' instead
  125. void Perform(PlayerBase player, float currentTime, TStringVectorMap gatheredSurfaces)
  126. {
  127. #ifdef DIAG_DEVELOPER
  128. if (DiagMenu.GetBool(DiagMenuIDs.TRIGGER_DEBUG))
  129. Print("Performing " + this);
  130. #endif
  131. m_PerformedTimestamp = currentTime;
  132. if (m_SoundSet)
  133. {
  134. vector soundPos = GetSoundPos(player);
  135. EffectSound sound = SEffectManager.PlaySound(m_SoundSet, soundPos);
  136. sound.SetAutodestroy( true );
  137. #ifdef DIAG_DEVELOPER
  138. if (DiagMenu.GetBool(DiagMenuIDs.TRIGGER_DEBUG))
  139. Debug.DrawSphere(soundPos , 0.15,Colors.YELLOW, ShapeFlags.NOZBUFFER);
  140. #endif
  141. }
  142. Do(player);
  143. }
  144. protected vector GetSoundPos(PlayerBase player)
  145. {
  146. vector soundPos;
  147. if (HasSurfaces())
  148. {
  149. soundPos = m_MatchingSurfacePos;
  150. }
  151. else
  152. {
  153. float distance = Math.RandomFloatInclusive(5,15);
  154. vector randomDir = vector.RandomDir2D() * distance;
  155. vector playerPos = player.GetPosition();
  156. soundPos = playerPos + randomDir;
  157. }
  158. return soundPos;
  159. }
  160. }
  161. //----------------------------------------------------------------------------------------------------------
  162. class SpookyTriggerEventsHandler
  163. {
  164. protected ref array<ref SpookyEventBase> m_SoundEvents;
  165. protected PlayerBase m_Player;
  166. protected float m_TimeAccu;
  167. protected const float CONSECUTIVE_EVENTS_COOLDOWN = 20;//min delay in seconds before two events
  168. protected const float EVENT_CHECK_FREQUENCY = 2;//when not in cooldown, the rate at which we query the events
  169. protected const float FIRST_EVENT_CHECK_DELAY = 15;//the delay between the first event check when we first enter the contaminated area
  170. protected const float SURFACE_CHECK_POINT_DISTANCE = 2;//additional checks for surface are performed at this distance from the player
  171. protected float m_NextEventCheck = FIRST_EVENT_CHECK_DELAY;
  172. void SpookyTriggerEventsHandler(notnull PlayerBase player)
  173. {
  174. if (!m_SoundEvents)
  175. {
  176. m_SoundEvents = new array<ref SpookyEventBase>();
  177. RegisterEvents();
  178. }
  179. m_Player = player;
  180. }
  181. void ~SpookyTriggerEventsHandler()
  182. {
  183. m_SoundEvents = null;
  184. }
  185. protected void RegisterEvents()
  186. {
  187. m_SoundEvents.Insert(new SpookyEventWind());
  188. m_SoundEvents.Insert(new SpookyEventWhisper());
  189. m_SoundEvents.Insert(new SpookyEventSteps());
  190. m_SoundEvents.Insert(new SpookyEventRustle());
  191. }
  192. void Update(float deltaTime)
  193. {
  194. m_TimeAccu += deltaTime;
  195. if (m_TimeAccu > m_NextEventCheck)
  196. {
  197. if (SelectEvent())
  198. m_NextEventCheck = CONSECUTIVE_EVENTS_COOLDOWN;
  199. else
  200. m_NextEventCheck = EVENT_CHECK_FREQUENCY;
  201. m_TimeAccu = 0;
  202. }
  203. }
  204. protected void GatherSurfaces(notnull TStringVectorMap gatheredGurfaces)
  205. {
  206. gatheredGurfaces.Clear();
  207. vector playerPos = m_Player.GetPosition();
  208. TVectorArray positions = new TVectorArray();
  209. positions.Insert(playerPos);
  210. positions.Insert(playerPos + ("0 0 1" * SURFACE_CHECK_POINT_DISTANCE));
  211. positions.Insert(playerPos + ("0 0 -1" * SURFACE_CHECK_POINT_DISTANCE));
  212. positions.Insert(playerPos + ("1 0 0" * SURFACE_CHECK_POINT_DISTANCE));
  213. positions.Insert(playerPos + ("-1 0 0" * SURFACE_CHECK_POINT_DISTANCE));
  214. foreach (vector pos : positions)
  215. {
  216. string surfaceType;
  217. GetGame().SurfaceGetType3D(pos[0],pos[1], pos[2], surfaceType);
  218. if (!gatheredGurfaces.Contains(surfaceType))
  219. gatheredGurfaces.Insert(surfaceType, pos);
  220. }
  221. #ifdef DIAG_DEVELOPER
  222. if (DiagMenu.GetBool(DiagMenuIDs.TRIGGER_DEBUG))
  223. {
  224. foreach (vector p:positions)
  225. Debug.DrawLine(p, p + "0 10 0", COLOR_BLUE,ShapeFlags.NOZBUFFER);
  226. }
  227. #endif
  228. }
  229. protected bool SelectEvent()
  230. {
  231. TStringVectorMap gatheredSurfaces = new TStringVectorMap();
  232. GatherSurfaces(gatheredSurfaces);//this can be optimized by calling this on demand from an event, as none events might be eligible at this point, so we might be doing this in vain
  233. #ifdef DIAG_DEVELOPER
  234. if (DiagMenu.GetBool(DiagMenuIDs.TRIGGER_DEBUG))
  235. {
  236. for(int i = 0; i < gatheredSurfaces.Count(); i++)
  237. {
  238. Print(gatheredSurfaces.GetKey(i));
  239. Print(gatheredSurfaces.Get(gatheredSurfaces.GetKey(i)));
  240. }
  241. Print("--------------------------------------------------------------------");
  242. }
  243. #endif
  244. array<ref SpookyEventBase> validEvents = new array<ref SpookyEventBase>();
  245. float currentTime = GetGame().GetTime();
  246. foreach (SpookyEventBase spookyEvent:m_SoundEvents)
  247. {
  248. if (spookyEvent.CanPerform(m_Player, currentTime, gatheredSurfaces))
  249. validEvents.Insert(spookyEvent);
  250. }
  251. //validEvents.Debug();
  252. SpookyEventBase selectedEvent;
  253. if (validEvents.Count() > 0)
  254. {
  255. int randIndex = Math.RandomIntInclusive(0, validEvents.Count() - 1);
  256. selectedEvent = validEvents[randIndex];
  257. }
  258. if (selectedEvent)
  259. {
  260. selectedEvent.Perform(m_Player, currentTime, gatheredSurfaces);
  261. return true;
  262. }
  263. return false;
  264. }
  265. }
  266. //!this entity gets attached to each player while present in the spooky area
  267. class SpookyPlayerStalker : ScriptedEntity
  268. {
  269. protected ref UniversalTemperatureSource m_UTSource;
  270. protected ref UniversalTemperatureSourceSettings m_UTSSettings;
  271. protected ref UniversalTemperatureSourceLambdaConstant m_UTSLConstant;
  272. override void EEInit()
  273. {
  274. super.EEInit();
  275. if (GetGame().IsServer() || !GetGame().IsMultiplayer())
  276. {
  277. m_UTSSettings = new UniversalTemperatureSourceSettings();
  278. m_UTSSettings.m_Updateable = true;
  279. m_UTSSettings.m_UpdateInterval = 3;
  280. m_UTSSettings.m_TemperatureItemCap = -20;
  281. m_UTSSettings.m_TemperatureCap = -20;
  282. m_UTSSettings.m_RangeFull = 2;
  283. m_UTSSettings.m_RangeMax = 2;
  284. m_UTSSettings.m_ManualUpdate = false;
  285. m_UTSSettings.m_IsWorldOverriden = false;
  286. m_UTSLConstant = new UniversalTemperatureSourceLambdaConstant();
  287. m_UTSource = new UniversalTemperatureSource(this, m_UTSSettings, m_UTSLConstant);
  288. m_UTSource.SetActive(true);
  289. }
  290. }
  291. }