undergroundhandlerclient.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. enum EUndergroundPresence
  2. {
  3. NONE,//player is not interacting with underground at any level
  4. OUTER,//player is on the outskirts of the underdound, some effects are already in effect, while others might not be
  5. TRANSITIONING,//player has entered underground and is in the process of screen darkening transition
  6. FULL//the player is now fully entered underground
  7. }
  8. class UndergroundHandlerClient
  9. {
  10. const float LIGHT_BLEND_SPEED_IN = 5;
  11. const float LIGHT_BLEND_SPEED_OUT = 1.75;
  12. const float MAX_RATIO = 0.9;//how much max ratio between 0..1 can a single breadcrumb occupy
  13. const float RATIO_CUTOFF = 0;//what's the minimum ratio a breadcrumb needs to have to be considered when calculatiing accommodation
  14. const float DISTANCE_CUTOFF = 5;//we ignore breadcrumbs further than this distance
  15. const float ACCO_MODIFIER = 1;//when we calculate eye accommodation between 0..1 based on the breadcrumbs values and distances, we multiply the result by this modifier to get the final eye accommodation value
  16. const float DEFAULT_INTERPOLATION_SPEED = 7;
  17. const string UNDERGROUND_LIGHTING = "dz\\data\\lighting\\lighting_underground.txt";
  18. protected ref AnimationTimer m_AnimTimerLightBlend;
  19. protected PlayerBase m_Player;
  20. protected PPERUndergroundAcco m_Requester;
  21. protected PPERequester_CameraNV m_NVRequester;
  22. protected ref set<UndergroundTrigger> m_InsideTriggers = new set<UndergroundTrigger>();
  23. protected float m_EyeAccoTarget = 1;
  24. protected float m_AccoInterpolationSpeed;
  25. protected float m_EyeAcco = 1;
  26. protected float m_LightingLerpTarget;
  27. protected float m_LightingLerp;
  28. protected EffectSound m_AmbientSound;
  29. protected UndergroundTrigger m_TransitionalTrigger;
  30. void UndergroundHandlerClient(PlayerBase player)
  31. {
  32. GetGame().GetWorld().LoadUserLightingCfg(UNDERGROUND_LIGHTING, "Underground");
  33. m_Player = player;
  34. m_NVRequester = PPERequester_CameraNV.Cast(PPERequesterBank.GetRequester( PPERequesterBank.REQ_CAMERANV));
  35. }
  36. void ~UndergroundHandlerClient()
  37. {
  38. if (GetGame())
  39. {
  40. GetGame().GetWorld().SetExplicitVolumeFactor_EnvSounds2D(1, 0.5);
  41. GetGame().GetWeather().SuppressLightningSimulation(false);
  42. GetGame().GetWorld().SetUserLightingLerp(0);
  43. if (m_AmbientSound)
  44. m_AmbientSound.Stop();
  45. }
  46. }
  47. protected PPERUndergroundAcco GetRequester()
  48. {
  49. if (!m_Requester)
  50. {
  51. m_Requester = PPERUndergroundAcco.Cast(PPERequesterBank.GetRequester( PPERequesterBank.REQ_UNDERGROUND));
  52. m_Requester.Start();
  53. }
  54. return m_Requester;
  55. }
  56. void OnTriggerEnter(UndergroundTrigger trigger)
  57. {
  58. m_InsideTriggers.Insert(trigger);
  59. OnTriggerInsiderUpdate();
  60. }
  61. void OnTriggerLeave(UndergroundTrigger trigger)
  62. {
  63. int index = m_InsideTriggers.Find(trigger);
  64. if (index != -1)
  65. {
  66. m_InsideTriggers.Remove(index);
  67. }
  68. OnTriggerInsiderUpdate();
  69. }
  70. protected void CalculateEyeAccoTarget()
  71. {
  72. if (!m_TransitionalTrigger)
  73. return;
  74. if (m_TransitionalTrigger.m_Data.UseLinePointFade && m_TransitionalTrigger.m_Data.Breadcrumbs.Count() >= 2)
  75. {
  76. CalculateLinePointFade();
  77. }
  78. else if (m_TransitionalTrigger.m_Data.Breadcrumbs.Count() >= 2)
  79. {
  80. CalculateBreadCrumbs();
  81. }
  82. }
  83. protected void CalculateBreadCrumbs()
  84. {
  85. float closestDist = float.MAX;
  86. array<float> distances = new array<float>();
  87. array<float> distancesInverted = new array<float>();
  88. int excludeMask = 0;
  89. foreach (int indx, auto crumb:m_TransitionalTrigger.m_Data.Breadcrumbs)
  90. {
  91. if (indx > 32)//error handling for exceeding this limit is handled elsewhere
  92. break;
  93. float dist = vector.Distance(m_Player.GetPosition(), crumb.GetPosition());
  94. float crumbRadius = m_TransitionalTrigger.m_Data.Breadcrumbs[indx].Radius;
  95. float maxRadiusAllowed = DISTANCE_CUTOFF;
  96. if (crumbRadius != -1)
  97. maxRadiusAllowed = crumbRadius;
  98. if (dist > maxRadiusAllowed)
  99. excludeMask = (excludeMask | (1 << indx));
  100. else if (m_TransitionalTrigger.m_Data.Breadcrumbs[indx].UseRaycast)
  101. {
  102. int idx = m_Player.GetBoneIndexByName("Head");
  103. vector rayStart = m_Player.GetBonePositionWS(idx);
  104. vector rayEnd = crumb.GetPosition();
  105. vector hitPos, hitNormal;
  106. float hitFraction;
  107. Object hitObj;
  108. if (DayZPhysics.RayCastBullet(rayStart, rayEnd,PhxInteractionLayers.TERRAIN | PhxInteractionLayers.ROADWAY| PhxInteractionLayers.BUILDING, null, hitObj, hitPos, hitNormal, hitFraction))
  109. {
  110. excludeMask = (excludeMask | (1 << indx));
  111. }
  112. }
  113. distances.Insert(dist);
  114. #ifdef DIAG_DEVELOPER
  115. if ( DiagMenu.GetBool(DiagMenuIDs.UNDERGROUND_SHOW_BREADCRUMB) )
  116. Debug.DrawSphere(crumb.GetPosition(),0.1, COLOR_RED, ShapeFlags.ONCE);
  117. #endif
  118. }
  119. float baseDst = distances[0];
  120. float sum = 0;
  121. foreach (float dst:distances)
  122. {
  123. if (dst == 0)
  124. dst = 0.1;
  125. float dstInv = (baseDst / dst) * baseDst;
  126. sum += dstInv;
  127. distancesInverted.Insert(dstInv);
  128. }
  129. float sumCheck = 0;
  130. float eyeAcco = 0;
  131. foreach (int i, float dstInvert:distancesInverted)
  132. {
  133. if ((1 << i) & excludeMask)
  134. continue;
  135. float ratio = dstInvert / sum;
  136. if (ratio > MAX_RATIO)
  137. ratio = MAX_RATIO;
  138. if (ratio > RATIO_CUTOFF)
  139. {
  140. #ifdef DIAG_DEVELOPER
  141. if (DiagMenu.GetBool(DiagMenuIDs.UNDERGROUND_SHOW_BREADCRUMB) )
  142. {
  143. float intensity = (1-ratio) * 255;
  144. Debug.DrawLine(GetGame().GetPlayer().GetPosition() + "0 1 0", m_TransitionalTrigger.m_Data.Breadcrumbs[i].GetPosition(),ARGB(0,255,intensity,intensity),ShapeFlags.ONCE);
  145. }
  146. #endif
  147. eyeAcco += ratio * m_TransitionalTrigger.m_Data.Breadcrumbs[i].EyeAccommodation;
  148. }
  149. }
  150. m_EyeAccoTarget = eyeAcco * ACCO_MODIFIER;
  151. }
  152. protected void CalculateLinePointFade()
  153. {
  154. array<ref JsonUndergroundAreaBreadcrumb> points = m_TransitionalTrigger.m_Data.Breadcrumbs;
  155. JsonUndergroundAreaBreadcrumb startPoint = points[0];
  156. JsonUndergroundAreaBreadcrumb endPoint = points[points.Count() - 1];
  157. vector playerPos = m_Player.GetPosition();
  158. bool forceAcco;
  159. float acco;
  160. float accoMax = m_TransitionalTrigger.m_Data.Breadcrumbs[0].EyeAccommodation;
  161. float accoMin = endPoint.EyeAccommodation;
  162. JsonUndergroundAreaBreadcrumb closestPoint;
  163. JsonUndergroundAreaBreadcrumb secondaryPoint;
  164. float distanceToClosest;
  165. float distanceToSecondary;
  166. float distanceBetweenPoints;
  167. foreach (JsonUndergroundAreaBreadcrumb point : points) //identify closest segment
  168. {
  169. float dist = vector.DistanceSq(playerPos, point.GetPosition());
  170. if (!closestPoint || dist < distanceToClosest)
  171. {
  172. if (closestPoint)
  173. {
  174. secondaryPoint = closestPoint;
  175. distanceToSecondary = distanceToClosest;
  176. }
  177. closestPoint = point;
  178. distanceToClosest = dist;
  179. }
  180. else if (!secondaryPoint || dist < secondaryPoint)
  181. {
  182. secondaryPoint = point;
  183. distanceToSecondary = dist;
  184. }
  185. #ifdef DIAG_DEVELOPER
  186. if (DiagMenu.GetBool(DiagMenuIDs.UNDERGROUND_SHOW_BREADCRUMB))
  187. Debug.DrawSphere(point.GetPosition(),0.1, COLOR_RED, ShapeFlags.ONCE);
  188. #endif
  189. }
  190. distanceBetweenPoints = vector.DistanceSq(closestPoint.GetPosition(), secondaryPoint.GetPosition());
  191. if (closestPoint == startPoint && secondaryPoint == points[1] && distanceToSecondary > distanceBetweenPoints) // before first point, do nothing
  192. acco = accoMax;
  193. else if (closestPoint == endPoint && secondaryPoint == points[points.Count() - 2] && distanceToSecondary > distanceBetweenPoints) // past second point, do nothing
  194. acco = accoMin;
  195. else // between the two points, lerp
  196. {
  197. if (closestPoint == endPoint)
  198. secondaryPoint = points[points.Count() - 2];
  199. else if (closestPoint == startPoint)
  200. secondaryPoint = points[1];
  201. else if (distanceBetweenPoints < distanceToClosest || distanceBetweenPoints < distanceToSecondary)
  202. {
  203. JsonUndergroundAreaBreadcrumb nexPoint = points[points.Find(closestPoint) + 1];
  204. if (vector.DistanceSq(playerPos, nexPoint.GetPosition()) < vector.DistanceSq(closestPoint.GetPosition(), nexPoint.GetPosition()))
  205. secondaryPoint = nexPoint;
  206. else
  207. {
  208. acco = closestPoint.EyeAccommodation;
  209. forceAcco = true;
  210. }
  211. }
  212. if (!forceAcco)
  213. {
  214. distanceToSecondary = vector.DistanceSq(playerPos, secondaryPoint.GetPosition());
  215. acco = distanceToSecondary / (distanceToClosest + distanceToSecondary); // progress
  216. acco = Math.Lerp(secondaryPoint.EyeAccommodation, closestPoint.EyeAccommodation, acco);
  217. if (points.Find(closestPoint) > points.Find(secondaryPoint))
  218. m_LightingLerpTarget = closestPoint.LightLerp;
  219. else
  220. m_LightingLerpTarget = secondaryPoint.LightLerp;
  221. }
  222. }
  223. m_EyeAccoTarget = acco * ACCO_MODIFIER;
  224. if (m_LightingLerpTarget != m_LightingLerp && (!m_AnimTimerLightBlend || !m_AnimTimerLightBlend.IsRunning()))
  225. {
  226. if (m_LightingLerpTarget == 1)
  227. {
  228. m_AnimTimerLightBlend = new AnimationTimer();
  229. m_AnimTimerLightBlend.Run(1, this, "OnUpdateTimerIn", "OnUpdateTimerEnd",0, false, LIGHT_BLEND_SPEED_IN);
  230. }
  231. else
  232. {
  233. m_AnimTimerLightBlend = new AnimationTimer();
  234. m_AnimTimerLightBlend.Run(0, this, "OnUpdateTimerOut", "OnUpdateTimerEnd",m_LightingLerp, false, LIGHT_BLEND_SPEED_OUT);
  235. }
  236. }
  237. #ifdef DIAG_DEVELOPER
  238. if (DiagMenu.GetBool(DiagMenuIDs.UNDERGROUND_SHOW_BREADCRUMB))
  239. {
  240. Debug.DrawLine(m_Player.GetPosition() + "0 1 0", closestPoint.GetPosition(), COLOR_YELLOW, ShapeFlags.ONCE);
  241. if (acco != accoMin && acco != accoMax)
  242. Debug.DrawLine(closestPoint.GetPosition(), secondaryPoint.GetPosition(), COLOR_RED, ShapeFlags.ONCE);
  243. DbgUI.Begin(String("Underground Areas"), 20, 20);
  244. DbgUI.Text(String("Closest point id: " + points.Find(closestPoint)));
  245. DbgUI.Text(String("Second closest id: " + points.Find(secondaryPoint)));
  246. DbgUI.End();
  247. }
  248. #endif
  249. }
  250. protected void ProcessEyeAcco(float timeSlice)
  251. {
  252. CalculateEyeAccoTarget();
  253. bool reachedTarget = CalculateEyeAcco(timeSlice);
  254. ApplyEyeAcco();
  255. if(reachedTarget && !m_Player.m_UndergroundPresence)
  256. {
  257. GetRequester().Stop();
  258. UpdateNVGRequester(1);
  259. //m_NVRequester.SetUndergroundExposureCoef(1.0);
  260. m_Player.KillUndergroundHandler();
  261. }
  262. }
  263. protected void ProcessLighting(float timeSlice)
  264. {
  265. #ifdef DEVELOPER
  266. if (!DiagMenu.GetBool(DiagMenuIDs.UNDERGROUND_DISABLE_DARKENING) )
  267. {
  268. GetGame().GetWorld().SetUserLightingLerp(m_LightingLerp);
  269. }
  270. else
  271. {
  272. GetGame().GetWorld().SetUserLightingLerp(0);
  273. }
  274. #else
  275. GetGame().GetWorld().SetUserLightingLerp(m_LightingLerp);
  276. #endif
  277. }
  278. protected void ProcessSound(float timeSlice)
  279. {
  280. GetGame().GetWorld().SetExplicitVolumeFactor_EnvSounds2D(m_EyeAcco, 0);
  281. if (m_AmbientSound)
  282. {
  283. m_AmbientSound.SetSoundVolume(1-m_EyeAcco);
  284. //Print(m_AmbientSound.GetSoundVolume());
  285. }
  286. else
  287. {
  288. m_Player.PlaySoundSetLoop(m_AmbientSound, "Underground_SoundSet",3,3);
  289. }
  290. }
  291. void Tick(float timeSlice)
  292. {
  293. if (!m_Player.IsAlive())
  294. return;
  295. ProcessEyeAcco(timeSlice);
  296. ProcessLighting(timeSlice);
  297. ProcessSound(timeSlice);
  298. #ifdef DIAG_DEVELOPER
  299. if ( DiagMenu.GetBool(DiagMenuIDs.UNDERGROUND_SHOW_BREADCRUMB) )
  300. {
  301. DisplayDebugInfo(GetGame().GetWorld().GetEyeAccom(), m_LightingLerp);
  302. }
  303. #endif
  304. }
  305. protected void ApplyEyeAcco()
  306. {
  307. #ifdef DIAG_DEVELOPER
  308. if (!DiagMenu.GetBool(DiagMenuIDs.UNDERGROUND_DISABLE_DARKENING) )
  309. {
  310. GetRequester().SetEyeAccommodation(m_EyeAcco);
  311. }
  312. else
  313. {
  314. GetRequester().SetEyeAccommodation(1);
  315. }
  316. #else
  317. GetRequester().SetEyeAccommodation(m_EyeAcco);
  318. #endif
  319. float undergrounNVExposureCoef = m_EyeAcco;
  320. if (m_LightingLerp >= 1.0 || GetDayZGame().GetWorld().IsNight())
  321. {
  322. undergrounNVExposureCoef = 1.0;
  323. }
  324. //m_NVRequester.SetUndergroundExposureCoef(undergrounNVExposureCoef);
  325. UpdateNVGRequester(undergrounNVExposureCoef);
  326. }
  327. protected void UpdateNVGRequester(float value)
  328. {
  329. m_NVRequester.SetUndergroundExposureCoef(value);
  330. }
  331. protected bool CalculateEyeAcco(float timeSlice)
  332. {
  333. if (m_TransitionalTrigger || !m_Player.m_UndergroundPresence || (m_EyeAccoTarget == 1))
  334. {
  335. float accoDiff = m_EyeAccoTarget - m_EyeAcco;
  336. float increase = accoDiff * m_AccoInterpolationSpeed * timeSlice;
  337. m_EyeAcco += increase;
  338. if (Math.AbsFloat(accoDiff) < 0.01)
  339. {
  340. m_EyeAcco = m_EyeAccoTarget;
  341. return true;
  342. }
  343. }
  344. else
  345. {
  346. m_EyeAcco = m_EyeAccoTarget;
  347. }
  348. return false;
  349. }
  350. protected void OnTriggerInsiderUpdate()
  351. {
  352. EUndergroundTriggerType bestType = EUndergroundTriggerType.UNDEFINED;
  353. m_TransitionalTrigger = null;
  354. UndergroundTrigger bestTrigger;
  355. m_EyeAccoTarget = 1;
  356. m_AccoInterpolationSpeed = DEFAULT_INTERPOLATION_SPEED;
  357. foreach (auto t:m_InsideTriggers)
  358. {
  359. if (t.m_Type > bestType)
  360. {
  361. bestTrigger = t;
  362. bestType = t.m_Type;
  363. }
  364. }
  365. //Print(m_InsideTriggers.Count());
  366. //Print(bestType);
  367. if (bestTrigger)
  368. {
  369. if (bestTrigger.m_Type == EUndergroundTriggerType.TRANSITIONING)
  370. {
  371. m_TransitionalTrigger = bestTrigger;
  372. }
  373. m_EyeAccoTarget = bestTrigger.m_Accommodation;
  374. if (bestTrigger.m_InterpolationSpeed != -1 && bestTrigger.m_InterpolationSpeed != 0)
  375. m_AccoInterpolationSpeed = bestTrigger.m_InterpolationSpeed;
  376. }
  377. SetUndergroundPresence(bestTrigger);
  378. }
  379. protected void SetUndergroundPresence(UndergroundTrigger trigger)
  380. {
  381. EUndergroundPresence newPresence = EUndergroundPresence.NONE;
  382. EUndergroundPresence oldPresence = m_Player.m_UndergroundPresence;
  383. if (trigger)
  384. {
  385. if (trigger.m_Type == EUndergroundTriggerType.OUTER)
  386. {
  387. newPresence = EUndergroundPresence.OUTER;
  388. }
  389. else if (trigger.m_Type == EUndergroundTriggerType.TRANSITIONING)
  390. {
  391. newPresence = EUndergroundPresence.TRANSITIONING;
  392. }
  393. else if (trigger.m_Type == EUndergroundTriggerType.INNER)
  394. {
  395. newPresence = EUndergroundPresence.FULL;
  396. }
  397. }
  398. if (newPresence != oldPresence)//was there a change ?
  399. {
  400. OnUndergroundPresenceUpdate(newPresence,oldPresence);
  401. m_Player.SetUnderground(newPresence);
  402. }
  403. }
  404. protected void EnableLights(bool enable)
  405. {
  406. foreach (ScriptedLightBase light:ScriptedLightBase.m_NightTimeOnlyLights)
  407. {
  408. light.SetVisibleDuringDaylight(enable);
  409. }
  410. }
  411. void OnUpdateTimerEnd();
  412. void OnUpdateTimerIn()
  413. {
  414. if (!m_AnimTimerLightBlend)
  415. return;
  416. float value01 = m_AnimTimerLightBlend.GetValue();
  417. float result = Easing.EaseInQuint(value01);
  418. m_LightingLerp = result;
  419. }
  420. void OnUpdateTimerOut()
  421. {
  422. if (!m_AnimTimerLightBlend)
  423. return;
  424. float value01 = m_AnimTimerLightBlend.GetValue();
  425. float result = Easing.EaseOutCubic(value01);
  426. m_LightingLerp = result;
  427. }
  428. protected void OnUndergroundPresenceUpdate(EUndergroundPresence newPresence, EUndergroundPresence oldPresence)
  429. {
  430. //Print("-----> On Undeground Presence update " + EnumTools.EnumToString(EUndergroundPresence, newPresence) + " " + EnumTools.EnumToString(EUndergroundPresence, oldPresence));
  431. if (newPresence > EUndergroundPresence.NONE)
  432. {
  433. if (oldPresence == EUndergroundPresence.NONE)
  434. {
  435. EnableLights(true);
  436. }
  437. if (newPresence > EUndergroundPresence.OUTER && oldPresence <= EUndergroundPresence.OUTER)
  438. {
  439. GetGame().GetWeather().SuppressLightningSimulation(true);
  440. m_Player.PlaySoundSetLoop(m_AmbientSound, "Underground_SoundSet",3,3);
  441. }
  442. if (newPresence == EUndergroundPresence.FULL)
  443. {
  444. m_AnimTimerLightBlend = new AnimationTimer();
  445. m_AnimTimerLightBlend.Run(1, this, "OnUpdateTimerIn", "OnUpdateTimerEnd",0, false, LIGHT_BLEND_SPEED_IN);
  446. }
  447. }
  448. if (newPresence < EUndergroundPresence.FULL && oldPresence == EUndergroundPresence.FULL)
  449. {
  450. m_AnimTimerLightBlend = new AnimationTimer();
  451. m_AnimTimerLightBlend.Run(0, this, "OnUpdateTimerOut", "OnUpdateTimerEnd",m_LightingLerp, false, LIGHT_BLEND_SPEED_OUT);
  452. }
  453. if (newPresence <= EUndergroundPresence.OUTER && oldPresence > EUndergroundPresence.OUTER)
  454. {
  455. GetGame().GetWeather().SuppressLightningSimulation(false);
  456. if (m_AmbientSound)
  457. m_Player.StopSoundSet(m_AmbientSound);
  458. }
  459. if (newPresence == EUndergroundPresence.NONE && oldPresence >= EUndergroundPresence.OUTER)
  460. {
  461. GetGame().GetWorld().SetUserLightingLerp(0);
  462. EnableLights(false);
  463. }
  464. }
  465. #ifdef DIAG_DEVELOPER
  466. protected void DisplayDebugInfo(float acco, float lighting)
  467. {
  468. if (acco < 0.0001)
  469. acco = 0;
  470. DbgUI.Begin(String("Underground Areas"), 20, 20);
  471. DbgUI.Text(String("Eye Accomodation: " + acco.ToString()));
  472. DbgUI.Text(String("Lighting lerp: " + lighting.ToString()));
  473. DbgUI.End();
  474. }
  475. #endif
  476. }