staminahandler.c 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067
  1. enum EStaminaMultiplierTypes
  2. {
  3. MASK = 1,
  4. FATIGUE,
  5. EPINEPHRINE,
  6. DROWNING,
  7. VOMIT_EXHAUSTION,
  8. DISEASE_PNEUMONIA
  9. }
  10. /**@class Stamina Consumer
  11. * @brief Holds information about Stamina Consumer
  12. *
  13. * @param[in] threshold value needed to allow consume stamina
  14. * @param[in] state keeps state of the consumer non-depleted/depleted
  15. */
  16. class StaminaConsumer
  17. {
  18. protected float m_ActivationThreshold;
  19. protected float m_DrainThreshold
  20. protected bool m_State;
  21. void StaminaConsumer(float threshold, float threshold2, bool state)
  22. {
  23. m_ActivationThreshold = threshold; //can be activated if above this threshold
  24. m_DrainThreshold = threshold2; //can continually drain until it reaches this threshold
  25. m_State = state;
  26. }
  27. bool GetState() { return m_State; }
  28. void SetState(bool state) { m_State = state; }
  29. float GetActivationThreshold() { return m_ActivationThreshold; }
  30. void SetActivationThreshold(float threshold) { m_ActivationThreshold = threshold; }
  31. float GetDrainThreshold() { return m_DrainThreshold; }
  32. void SetDrainThreshold(float threshold) { m_DrainThreshold = threshold; }
  33. }
  34. class StaminaConsumers
  35. {
  36. protected ref map<EStaminaConsumers, ref StaminaConsumer> m_StaminaConsumers;
  37. void StaminaConsumers()
  38. {
  39. m_StaminaConsumers = new map<EStaminaConsumers, ref StaminaConsumer>;
  40. }
  41. void RegisterConsumer(EStaminaConsumers consumer, float threshold, float depletion_threshold = -1)
  42. {
  43. if (depletion_threshold == -1)
  44. {
  45. depletion_threshold = threshold;
  46. }
  47. if ( !m_StaminaConsumers.Contains(consumer) )
  48. {
  49. //! init of StaminaConsumer - threshold, state
  50. StaminaConsumer sc = new StaminaConsumer(threshold, depletion_threshold, true);
  51. m_StaminaConsumers.Set(consumer, sc);
  52. }
  53. }
  54. bool HasEnoughStaminaFor(EStaminaConsumers consumer, float curStamina, bool isDepleted, float cap)
  55. {
  56. StaminaConsumer sc;
  57. if (m_StaminaConsumers && m_StaminaConsumers.Find(consumer, sc))
  58. {
  59. if (consumer != EStaminaConsumers.SPRINT)
  60. {
  61. if (isDepleted || (curStamina < sc.GetDrainThreshold()))
  62. {
  63. sc.SetState(false);
  64. return false;
  65. }
  66. }
  67. else
  68. {
  69. if (!isDepleted)
  70. {
  71. if (sc.GetState())
  72. {
  73. sc.SetState(true);
  74. return true;
  75. }
  76. }
  77. else
  78. {
  79. sc.SetState(false);
  80. return false;
  81. }
  82. }
  83. if (curStamina > sc.GetDrainThreshold() || curStamina == cap) //Sometimes player can't go up to drain threshold
  84. {
  85. sc.SetState(true);
  86. return true;
  87. }
  88. }
  89. return false;
  90. }
  91. bool HasEnoughStaminaToStart(EStaminaConsumers consumer, float curStamina, bool isDepleted, float cap)
  92. {
  93. StaminaConsumer sc;
  94. if (m_StaminaConsumers && m_StaminaConsumers.Find(consumer, sc))
  95. {
  96. if ((isDepleted || (curStamina < sc.GetActivationThreshold() && curStamina < cap)))
  97. {
  98. sc.SetState(false);
  99. return false;
  100. }
  101. else
  102. {
  103. sc.SetState(true);
  104. return true;
  105. }
  106. }
  107. return false;
  108. }
  109. }
  110. /**@class Stamina Modifier
  111. * @brief Holds information about Stamina Modifier
  112. *
  113. * @param[in] type calculation method
  114. * @param[in] minValue min value substracted from stamina (if type > 0)
  115. * @param[in] maxValue max value substracted from stamina
  116. * @param[in] cooldown how many seconds will not be the stamina replenishing after depleting
  117. */
  118. class StaminaModifier
  119. {
  120. bool m_InUse = false;
  121. int m_Type;
  122. float m_MinValue, m_MaxValue, m_Multiplier, m_Cooldown, m_StartTime, m_StartTimeAdjustment, m_Duration, m_ProgressTime, m_Tick;
  123. void StaminaModifier(int type, float min, float max, float cooldown, float startTime = 0, float duration = 0)
  124. {
  125. m_Type = type;
  126. m_MinValue = min;
  127. m_MaxValue = max;
  128. m_Cooldown = cooldown;
  129. m_StartTimeAdjustment = startTime;
  130. m_Duration = duration;
  131. m_Tick = 1;
  132. }
  133. int GetType() { return m_Type; }
  134. float GetMinValue() { return m_MinValue; }
  135. void SetMinValue(float val) { m_MinValue = val; }
  136. float GetMaxValue() { return m_MaxValue; }
  137. void SetMaxValue(float val) { m_MaxValue = val; }
  138. float GetCooldown() { return m_Cooldown; }
  139. void SetCooldown(float val) { m_Cooldown = val; }
  140. float GetStartTime() { return m_StartTime; } //Actual game time (progressive modifiers only)
  141. void SetStartTime(float val) { m_StartTime = val; }
  142. float GetStartTimeAdjustment() {return m_StartTimeAdjustment;} //adjustment to current time (progressive modifiers only)
  143. float GetDuration() { return m_Duration; }
  144. float GetDurationAdjusted() { return m_Duration / m_Tick; }
  145. bool IsInUse() { return m_InUse; }
  146. void SetInUse(bool val) { m_InUse = val; }
  147. float GetRunTime() { return m_ProgressTime; }
  148. void AddRunTime(float val) { m_ProgressTime += val; }
  149. void SetRunTimeTick(float val) { m_Tick = val; }
  150. void ResetRunTime() { m_ProgressTime = 0 }
  151. }
  152. class StaminaModifierExponential : StaminaModifier
  153. {
  154. protected ref SMDataExponential m_SMDataEx;
  155. float GetBaseValue() { return m_SMDataEx.m_BaseValue; }
  156. float GetExponent() { return m_SMDataEx.m_Exponent; }
  157. float GetMultiplier() { return m_SMDataEx.m_Multiplier; }
  158. override float GetCooldown() { return m_SMDataEx.m_Cooldown; }
  159. override float GetStartTimeAdjustment() { return m_SMDataEx.m_StartTimeAdjustment; }
  160. override float GetDuration() { return m_SMDataEx.m_Duration; }
  161. override float GetDurationAdjusted() { return m_SMDataEx.m_Duration / m_Tick; }
  162. void SetData(SMDataExponential data) { m_SMDataEx = data; }
  163. }
  164. class StaminaModifiers
  165. {
  166. const int FIXED = 0;
  167. const int RANDOMIZED = 1;
  168. const int LINEAR = 2; //Useful ONLY for regular, over-time stamina drain
  169. const int EXPONENTIAL = 3; //Useful ONLY for regular, over-time stamina drain
  170. protected ref map<EStaminaModifiers, ref StaminaModifier> m_StaminaModifiers;
  171. void StaminaModifiers()
  172. {
  173. m_StaminaModifiers = new map<EStaminaModifiers, ref StaminaModifier>;
  174. }
  175. //! register single value modifier - depletes stamina for that value
  176. void RegisterFixed(EStaminaModifiers modifier, float value, float cooldown = GameConstants.STAMINA_REGEN_COOLDOWN_DEPLETION)
  177. {
  178. if ( !m_StaminaModifiers.Contains(modifier) )
  179. {
  180. //! init of StaminaModifier - type and min, max values (min is not relevant for that type)
  181. StaminaModifier sm = new StaminaModifier(FIXED, -1, value, cooldown);
  182. m_StaminaModifiers.Set(modifier, sm);
  183. }
  184. }
  185. //! register randomized modifier - stamina will be depleted by value between min and max value;
  186. void RegisterRandomized(EStaminaModifiers modifier, float minValue, float maxValue, float cooldown = GameConstants.STAMINA_REGEN_COOLDOWN_DEPLETION)
  187. {
  188. if ( !m_StaminaModifiers.Contains(modifier) )
  189. {
  190. //! init of StaminaModifier - type, min, max values
  191. StaminaModifier sm = new StaminaModifier(RANDOMIZED, minValue, maxValue, cooldown);
  192. m_StaminaModifiers.Set(modifier, sm);
  193. }
  194. }
  195. //! register lerped modifier - depletes stamina for startValue, and, after a startTime, lerps to endValue over duration
  196. void RegisterLinear(EStaminaModifiers modifier, float startValue, float endValue, float startTime, float duration, float cooldown = GameConstants.STAMINA_REGEN_COOLDOWN_DEPLETION)
  197. {
  198. StaminaModifier sm = new StaminaModifier(LINEAR, startValue, endValue, cooldown, startTime, duration);
  199. m_StaminaModifiers.Set(modifier, sm);
  200. }
  201. //! register exponential modifier - depletes stamina for startValue, and, after a startTime, lerps from 0 to exponent over duration
  202. void RegisterExponential(EStaminaModifiers modifier, float startValue, float exponent, float startTime, float duration, float cooldown = GameConstants.STAMINA_REGEN_COOLDOWN_DEPLETION)
  203. {
  204. StaminaModifier sm = new StaminaModifier(EXPONENTIAL, startValue, exponent, cooldown, startTime, duration);
  205. m_StaminaModifiers.Set(modifier, sm);
  206. }
  207. //! register exponential modifier, extended parameters
  208. void RegisterExponentialEx(EStaminaModifiers modifier, SMDataExponential data)
  209. {
  210. StaminaModifierExponential smex = new StaminaModifierExponential(EXPONENTIAL, data.m_BaseValue, data.m_Exponent, data.m_Cooldown, data.m_StartTimeAdjustment, data.m_Duration);
  211. smex.SetData(data);
  212. m_StaminaModifiers.Set(modifier, smex);
  213. }
  214. StaminaModifier GetModifierData(EStaminaModifiers modifier)
  215. {
  216. return m_StaminaModifiers.Get(modifier);
  217. }
  218. }
  219. class StaminaHandler
  220. {
  221. protected float m_PlayerLoad;
  222. protected float m_StaminaDelta;
  223. protected float m_Stamina;
  224. protected float m_StaminaSynced; //guaranteed to be identical on server and client
  225. protected float m_StaminaCap;
  226. protected float m_StaminaDepletion;
  227. protected float m_StaminaDepletionMultiplier; //controls depletion rate
  228. protected float m_StaminaRecoveryMultiplier; //controls recovery rate
  229. protected float m_Time;
  230. protected ref Param3<float,float,bool> m_StaminaParams;
  231. protected ref HumanMovementState m_State;
  232. protected SHumanCommandMoveSettings m_HumanMoveSettings;
  233. protected PlayerBase m_Player;
  234. protected bool m_Debug; //! DEPRECATED
  235. protected bool m_StaminaDepleted;
  236. protected ref map<int,ref Timer> m_TimerMap;
  237. ref map<EStaminaMultiplierTypes, float> m_RegisteredDepletionModifiers;
  238. ref set<EStaminaMultiplierTypes> m_ActiveDepletionModifiers;
  239. ref map<EStaminaMultiplierTypes, float> m_RegisteredRecoveryModifiers;
  240. ref set<EStaminaMultiplierTypes> m_ActiveRecoveryModifiers;
  241. protected bool m_IsInCooldown;
  242. protected ref StaminaConsumers m_StaminaConsumers;
  243. protected ref StaminaModifiers m_StaminaModifiers;
  244. #ifdef DIAG_DEVELOPER
  245. protected bool m_StaminaDisabled;
  246. #endif
  247. void StaminaHandler(PlayerBase player)
  248. {
  249. if (GetGame().IsServer() || !GetGame().IsMultiplayer())
  250. m_StaminaParams = new Param3<float,float,bool>(0, 0, false);
  251. m_State = new HumanMovementState();
  252. m_Player = player;
  253. m_Stamina = CfgGameplayHandler.GetStaminaMax();
  254. m_StaminaSynced = CfgGameplayHandler.GetStaminaMax();
  255. m_StaminaCap = CfgGameplayHandler.GetStaminaMax();
  256. m_StaminaDepletion = 0;
  257. m_StaminaDepletionMultiplier = 1;
  258. m_StaminaRecoveryMultiplier = 1;
  259. m_Time = 0;
  260. m_StaminaDepleted = false;
  261. m_IsInCooldown = false;
  262. m_HumanMoveSettings = m_Player.GetDayZPlayerType().CommandMoveSettingsW();
  263. RegisterStaminaConsumers();
  264. RegisterStaminaModifiers();
  265. m_TimerMap = new map<int,ref Timer>;
  266. //----------------- depletion --------------------
  267. m_RegisteredDepletionModifiers = new map<EStaminaMultiplierTypes, float>;
  268. m_ActiveDepletionModifiers = new set<EStaminaMultiplierTypes>;
  269. //----------------- recovery --------------------
  270. m_RegisteredRecoveryModifiers = new map<EStaminaMultiplierTypes, float>;
  271. m_ActiveRecoveryModifiers = new set<EStaminaMultiplierTypes>;
  272. Init();
  273. }
  274. void Init()
  275. {
  276. //----------------- depletion --------------------
  277. m_RegisteredDepletionModifiers.Insert(EStaminaMultiplierTypes.MASK, MaskMdfr.STAMINA_DEPLETION_MODIFIER);
  278. m_RegisteredDepletionModifiers.Insert(EStaminaMultiplierTypes.FATIGUE, FatigueMdfr.STAMINA_DEPLETION_MULTIPLIER);
  279. m_RegisteredDepletionModifiers.Insert(EStaminaMultiplierTypes.EPINEPHRINE, EpinephrineMdfr.STAMINA_DEPLETION_MULTIPLIER);
  280. m_RegisteredDepletionModifiers.Insert(EStaminaMultiplierTypes.VOMIT_EXHAUSTION, VomitSymptom.STAMINA_DEPLETION_MULTIPLIER);
  281. m_RegisteredDepletionModifiers.Insert(EStaminaMultiplierTypes.DISEASE_PNEUMONIA, PneumoniaMdfr.STAMINA_DEPLETION_MULTIPLIER);
  282. //----------------- recovery --------------------
  283. m_RegisteredRecoveryModifiers.Insert(EStaminaMultiplierTypes.MASK, MaskMdfr.STAMINA_RECOVERY_MODIFIER);
  284. m_RegisteredRecoveryModifiers.Insert(EStaminaMultiplierTypes.FATIGUE, FatigueMdfr.STAMINA_RECOVERY_MULTIPLIER);
  285. m_RegisteredRecoveryModifiers.Insert(EStaminaMultiplierTypes.DROWNING, DrowningMdfr.STAMINA_RECOVERY_MULTIPLIER);
  286. m_RegisteredRecoveryModifiers.Insert(EStaminaMultiplierTypes.VOMIT_EXHAUSTION, VomitSymptom.STAMINA_RECOVERY_MULTIPLIER);
  287. m_RegisteredRecoveryModifiers.Insert(EStaminaMultiplierTypes.DISEASE_PNEUMONIA, PneumoniaMdfr.STAMINA_RECOVERY_MULTIPLIER);
  288. }
  289. void ActivateDepletionModifier(EStaminaMultiplierTypes type)
  290. {
  291. if (m_RegisteredDepletionModifiers.Contains(type))
  292. {
  293. m_ActiveDepletionModifiers.Insert(type);
  294. RecalculateDepletionMultiplier();
  295. }
  296. else
  297. {
  298. Error("attempting to activate unregistered depletion modifier");
  299. }
  300. }
  301. void DeactivateDepletionModifier(EStaminaMultiplierTypes type)
  302. {
  303. int index = m_ActiveDepletionModifiers.Find(type);
  304. if (index != -1)
  305. {
  306. m_ActiveDepletionModifiers.Remove(index);
  307. RecalculateDepletionMultiplier();
  308. }
  309. }
  310. void RecalculateDepletionMultiplier()
  311. {
  312. float value = 1;
  313. foreach (int multiplier: m_ActiveDepletionModifiers)
  314. {
  315. value *= m_RegisteredDepletionModifiers.Get(multiplier);
  316. }
  317. if (value != m_StaminaDepletionMultiplier)
  318. SetDepletionMultiplier(value);
  319. }
  320. void ActivateRecoveryModifier(EStaminaMultiplierTypes type)
  321. {
  322. if (m_RegisteredRecoveryModifiers.Contains(type))
  323. {
  324. m_ActiveRecoveryModifiers.Insert(type);
  325. RecalculateRecoveryMultiplier();
  326. }
  327. else
  328. {
  329. Error("attempting to activate unregistered recovery modifier");
  330. }
  331. }
  332. void DeactivateRecoveryModifier(EStaminaMultiplierTypes type)
  333. {
  334. int index = m_ActiveRecoveryModifiers.Find(type);
  335. if (index != -1)
  336. {
  337. m_ActiveRecoveryModifiers.Remove(index);
  338. RecalculateRecoveryMultiplier();
  339. }
  340. }
  341. void RecalculateRecoveryMultiplier()
  342. {
  343. float value = 1;
  344. foreach (int multiplier: m_ActiveRecoveryModifiers)
  345. {
  346. value *= m_RegisteredRecoveryModifiers.Get(multiplier);
  347. }
  348. if (value != m_StaminaRecoveryMultiplier)
  349. {
  350. SetRecoveryMultiplier(value);
  351. }
  352. }
  353. void Update(float deltaT, int pCurrentCommandID)
  354. {
  355. #ifdef DIAG_DEVELOPER
  356. if (m_StaminaDisabled)
  357. return;
  358. #endif
  359. if (m_Player)
  360. {
  361. bool isServerOrSingleplayer = GetGame().IsServer() || !GetGame().IsMultiplayer();
  362. // Calculates actual max stamina based on player's load
  363. if (isServerOrSingleplayer)
  364. {
  365. //! gets the actual players load
  366. m_PlayerLoad = m_Player.GetWeightEx();
  367. //! StaminaCap calculation starts when PlayerLoad exceeds STAMINA_WEIGHT_LIMIT_THRESHOLD
  368. if (m_PlayerLoad >= CfgGameplayHandler.GetStaminaWeightLimitThreshold())
  369. {
  370. m_StaminaCap = Math.Max((CfgGameplayHandler.GetStaminaMax() - (((m_PlayerLoad - CfgGameplayHandler.GetStaminaWeightLimitThreshold())/GameConstants.STAMINA_KG_TO_GRAMS) * CfgGameplayHandler.GetStaminaKgToStaminaPercentPenalty())),CfgGameplayHandler.GetStaminaMinCap());
  371. }
  372. else
  373. {
  374. m_StaminaCap = CfgGameplayHandler.GetStaminaMax();
  375. }
  376. }
  377. // Calculates stamina gain/loss based on movement and load
  378. m_Player.GetMovementState(m_State);
  379. switch (m_State.m_CommandTypeId)
  380. {
  381. case DayZPlayerConstants.COMMANDID_MOVE:
  382. StaminaProcessor_Move(m_State);
  383. break;
  384. case DayZPlayerConstants.COMMANDID_LADDER:
  385. StaminaProcessor_Ladder(m_State);
  386. break;
  387. case DayZPlayerConstants.COMMANDID_SWIM:
  388. StaminaProcessor_Swimming(m_State);
  389. break;
  390. case DayZPlayerConstants.COMMANDID_FALL: //! processed on event
  391. case DayZPlayerConstants.COMMANDID_MELEE2: //! processed on event
  392. case DayZPlayerConstants.COMMANDID_CLIMB: //! processed on event
  393. break;
  394. default:
  395. if (!m_IsInCooldown)
  396. {
  397. m_StaminaDelta = GameConstants.STAMINA_GAIN_IDLE_PER_SEC;
  398. }
  399. break;
  400. }
  401. //Sets current stamina & stores + syncs data with client
  402. float temp = m_StaminaDelta * deltaT;
  403. if (temp < 0)
  404. {
  405. temp *= m_StaminaDepletionMultiplier;
  406. }
  407. else
  408. {
  409. temp *= m_StaminaRecoveryMultiplier;
  410. }
  411. m_Stamina = Math.Max(0, Math.Min((m_Stamina + temp), m_StaminaCap));
  412. m_Stamina = m_Stamina - m_StaminaDepletion;
  413. if (isServerOrSingleplayer)
  414. {
  415. m_Player.GetStatStamina().Set(m_Stamina);
  416. m_Time += deltaT;
  417. if (m_Time >= GameConstants.STAMINA_SYNC_RATE)
  418. {
  419. m_Time = 0;
  420. SetStamina(m_Stamina);
  421. }
  422. }
  423. #ifndef SERVER
  424. m_Player.SetStamina(m_StaminaSynced, m_StaminaCap);
  425. #endif
  426. ApplyExhaustion();
  427. CheckStaminaState();
  428. m_StaminaDelta = 0;
  429. m_StaminaDepletion = 0; // resets depletion modifier
  430. }
  431. }
  432. //! deprecated use, StaminaHandler uses SyncJunctures now
  433. void OnRPC(float stamina, float stamina_cap, bool cooldown)
  434. {
  435. }
  436. //! called from PlayerBase - syncs stamina values on server with client AND sets the value to match on server and client both (m_StaminaSynced guarantees identical values)
  437. void OnSyncJuncture(int pJunctureID, ParamsReadContext pCtx)
  438. {
  439. switch ( pJunctureID )
  440. {
  441. case DayZPlayerSyncJunctures.SJ_STAMINA:
  442. float stamina;
  443. float stamina_cap;
  444. bool cooldown;
  445. if (!pCtx.Read(stamina) || !pCtx.Read(stamina_cap) || !pCtx.Read(cooldown))
  446. {
  447. return;
  448. }
  449. m_Stamina = stamina; //?
  450. m_StaminaSynced = stamina;
  451. if (m_Player.GetInstanceType() != DayZPlayerInstanceType.INSTANCETYPE_CLIENT)
  452. {
  453. return;
  454. }
  455. if ( stamina_cap != m_StaminaCap )
  456. {
  457. m_StaminaCap = stamina_cap;
  458. }
  459. m_IsInCooldown = cooldown;
  460. m_Player.SetStamina(m_Stamina, m_StaminaCap);
  461. break;
  462. case DayZPlayerSyncJunctures.SJ_STAMINA_MISC:
  463. ReadAdditionalStaminaInfo(pCtx);
  464. break;
  465. }
  466. }
  467. protected void StaminaProcessor_Move(HumanMovementState pHumanMovementState)
  468. {
  469. switch (pHumanMovementState.m_iMovement)
  470. {
  471. case DayZPlayerConstants.MOVEMENTIDX_SPRINT: //sprint
  472. if (pHumanMovementState.m_iStanceIdx == DayZPlayerConstants.STANCEIDX_ERECT)
  473. {
  474. m_StaminaDelta = -GameConstants.STAMINA_DRAIN_STANDING_SPRINT_PER_SEC * CfgGameplayHandler.GetSprintStaminaModifierErc();
  475. SetCooldown(GameConstants.STAMINA_REGEN_COOLDOWN_DEPLETION);
  476. break;
  477. }
  478. else if ( pHumanMovementState.m_iStanceIdx == DayZPlayerConstants.STANCEIDX_CROUCH)
  479. {
  480. m_StaminaDelta = -GameConstants.STAMINA_DRAIN_CROUCHED_SPRINT_PER_SEC * CfgGameplayHandler.GetSprintStaminaModifierCro();
  481. SetCooldown(GameConstants.STAMINA_REGEN_COOLDOWN_DEPLETION);
  482. break;
  483. }
  484. m_StaminaDelta = GameConstants.STAMINA_GAIN_JOG_PER_SEC;
  485. break;
  486. case DayZPlayerConstants.MOVEMENTIDX_RUN: //jog
  487. if (m_Player.GetCurrentWaterLevel() >= m_HumanMoveSettings.m_fWaterLevelSpeedRectrictionHigh)
  488. {
  489. m_StaminaDelta = -GameConstants.STAMINA_DRAIN_STANDING_SPRINT_PER_SEC * CfgGameplayHandler.GetSprintStaminaModifierErc();
  490. break;
  491. }
  492. if (!m_IsInCooldown)
  493. {
  494. m_StaminaDelta = (GameConstants.STAMINA_GAIN_JOG_PER_SEC + CalcStaminaGainBonus());
  495. }
  496. break;
  497. case DayZPlayerConstants.MOVEMENTIDX_WALK: //walk
  498. if (!m_IsInCooldown)
  499. {
  500. m_StaminaDelta = (GameConstants.STAMINA_GAIN_WALK_PER_SEC + CalcStaminaGainBonus());
  501. }
  502. break;
  503. case DayZPlayerConstants.MOVEMENTIDX_IDLE: //idle
  504. if (m_Player.IsRolling())
  505. {
  506. m_StaminaDelta = GameConstants.STAMINA_GAIN_ROLL_PER_SEC;
  507. break;
  508. }
  509. if (!m_IsInCooldown)
  510. {
  511. m_StaminaDelta = (GameConstants.STAMINA_GAIN_IDLE_PER_SEC + CalcStaminaGainBonus());
  512. }
  513. break;
  514. default:
  515. if (!m_IsInCooldown)
  516. {
  517. m_StaminaDelta = GameConstants.STAMINA_GAIN_IDLE_PER_SEC;
  518. }
  519. break;
  520. }
  521. }
  522. protected void StaminaProcessor_Ladder(HumanMovementState pHumanMovementState)
  523. {
  524. switch (pHumanMovementState.m_iMovement)
  525. {
  526. case 2: //climb up (fast)
  527. m_StaminaDelta = -GameConstants.STAMINA_DRAIN_LADDER_FAST_PER_SEC * CfgGameplayHandler.GetSprintLadderStaminaModifier();
  528. SetCooldown(GameConstants.STAMINA_REGEN_COOLDOWN_DEPLETION);
  529. break;
  530. case 1: //climb up (slow)
  531. if (!m_IsInCooldown)
  532. {
  533. m_StaminaDelta = (GameConstants.STAMINA_GAIN_LADDER_PER_SEC + CalcStaminaGainBonus());
  534. }
  535. break;
  536. default:
  537. if (!m_IsInCooldown)
  538. {
  539. m_StaminaDelta = GameConstants.STAMINA_GAIN_IDLE_PER_SEC + CalcStaminaGainBonus();
  540. }
  541. break;
  542. }
  543. }
  544. protected void StaminaProcessor_Swimming(HumanMovementState pHumanMovementState)
  545. {
  546. switch (pHumanMovementState.m_iMovement)
  547. {
  548. case 3: //swim fast
  549. m_StaminaDelta = -GameConstants.STAMINA_DRAIN_SWIM_FAST_PER_SEC * CfgGameplayHandler.GetSprintSwimmingStaminaModifier();
  550. SetCooldown(GameConstants.STAMINA_REGEN_COOLDOWN_DEPLETION);
  551. break;
  552. case 2: //swim slow
  553. if (!m_IsInCooldown)
  554. {
  555. m_StaminaDelta = (GameConstants.STAMINA_GAIN_SWIM_PER_SEC + CalcStaminaGainBonus());
  556. }
  557. break;
  558. default:
  559. if (!m_IsInCooldown)
  560. {
  561. m_StaminaDelta = GameConstants.STAMINA_GAIN_IDLE_PER_SEC + CalcStaminaGainBonus();
  562. }
  563. break;
  564. }
  565. }
  566. //! stamina sync - server part
  567. protected void SyncStamina(float stamina, float stamina_cap, bool cooldown)
  568. {
  569. m_Player.GetStatStamina().Set(m_Stamina);
  570. ScriptJunctureData pCtx = new ScriptJunctureData();
  571. pCtx.Write(m_Stamina);
  572. pCtx.Write(m_StaminaCap);
  573. pCtx.Write(m_IsInCooldown);
  574. m_Player.SendSyncJuncture(DayZPlayerSyncJunctures.SJ_STAMINA,pCtx);
  575. }
  576. //! Method to sync more info for stamina manager. Template parameter means it is very extendable for further use
  577. protected void SyncAdditionalStaminaInfo(Param par)
  578. {
  579. Param2<float,float> p = Param2<float,float>.Cast(par);
  580. ScriptJunctureData pCtx = new ScriptJunctureData();
  581. pCtx.Write(p.param1);
  582. pCtx.Write(p.param2);
  583. m_Player.SendSyncJuncture(DayZPlayerSyncJunctures.SJ_STAMINA_MISC,pCtx);
  584. }
  585. //! Order of read parameters must match the order of writing above
  586. protected void ReadAdditionalStaminaInfo(ParamsReadContext pCtx)
  587. {
  588. float depletionMultiplier;
  589. float recoveryMultiplier;
  590. if (!pCtx.Read(depletionMultiplier) || !pCtx.Read(recoveryMultiplier))
  591. {
  592. return;
  593. }
  594. m_StaminaDepletionMultiplier = depletionMultiplier;
  595. m_StaminaRecoveryMultiplier = recoveryMultiplier;
  596. }
  597. protected void RegisterStaminaConsumers()
  598. {
  599. m_StaminaConsumers = new StaminaConsumers();
  600. m_StaminaConsumers.RegisterConsumer(
  601. EStaminaConsumers.HOLD_BREATH,
  602. GameConstants.STAMINA_HOLD_BREATH_THRESHOLD_ACTIVATE,
  603. GameConstants.STAMINA_HOLD_BREATH_THRESHOLD_DRAIN,
  604. );
  605. m_StaminaConsumers.RegisterConsumer(
  606. EStaminaConsumers.SPRINT,
  607. CfgGameplayHandler.GetStaminaMinCap() + 15,
  608. );
  609. m_StaminaConsumers.RegisterConsumer(
  610. EStaminaConsumers.JUMP,
  611. GameConstants.STAMINA_JUMP_THRESHOLD,
  612. );
  613. m_StaminaConsumers.RegisterConsumer(
  614. EStaminaConsumers.VAULT,
  615. GameConstants.STAMINA_VAULT_THRESHOLD,
  616. );
  617. m_StaminaConsumers.RegisterConsumer(
  618. EStaminaConsumers.CLIMB,
  619. GameConstants.STAMINA_CLIMB_THRESHOLD,
  620. );
  621. m_StaminaConsumers.RegisterConsumer(
  622. EStaminaConsumers.MELEE_HEAVY,
  623. GameConstants.STAMINA_MELEE_HEAVY_THRESHOLD,
  624. );
  625. m_StaminaConsumers.RegisterConsumer(
  626. EStaminaConsumers.MELEE_EVADE,
  627. GameConstants.STAMINA_MELEE_EVADE_THRESHOLD,
  628. );
  629. m_StaminaConsumers.RegisterConsumer(
  630. EStaminaConsumers.ROLL,
  631. GameConstants.STAMINA_ROLL_THRESHOLD,
  632. );
  633. m_StaminaConsumers.RegisterConsumer(EStaminaConsumers.DROWN, 0);
  634. m_StaminaConsumers.RegisterConsumer(EStaminaConsumers.PUSH, 0);
  635. }
  636. protected void RegisterStaminaModifiers()
  637. {
  638. m_StaminaModifiers = new StaminaModifiers();
  639. SMDataHoldBreath data = new SMDataHoldBreath();
  640. m_StaminaModifiers.RegisterExponentialEx(
  641. EStaminaModifiers.HOLD_BREATH,
  642. data,
  643. );
  644. m_StaminaModifiers.RegisterExponentialEx(
  645. EStaminaModifiers.PUSH_CAR,
  646. data,
  647. );
  648. m_StaminaModifiers.RegisterFixed(EStaminaModifiers.DROWN, 10);
  649. m_StaminaModifiers.RegisterFixed(
  650. EStaminaModifiers.JUMP,
  651. GameConstants.STAMINA_DRAIN_JUMP * CfgGameplayHandler.GetObstacleTraversalStaminaModifier(),
  652. );
  653. m_StaminaModifiers.RegisterFixed(
  654. EStaminaModifiers.VAULT,
  655. GameConstants.STAMINA_DRAIN_VAULT * CfgGameplayHandler.GetObstacleTraversalStaminaModifier(),
  656. );
  657. m_StaminaModifiers.RegisterFixed(
  658. EStaminaModifiers.CLIMB,
  659. GameConstants.STAMINA_DRAIN_CLIMB * CfgGameplayHandler.GetObstacleTraversalStaminaModifier(),
  660. );
  661. m_StaminaModifiers.RegisterFixed(
  662. EStaminaModifiers.MELEE_LIGHT,
  663. GameConstants.STAMINA_DRAIN_MELEE_LIGHT * CfgGameplayHandler.GetMeleeStaminaModifier(),
  664. );
  665. m_StaminaModifiers.RegisterFixed(
  666. EStaminaModifiers.MELEE_HEAVY,
  667. GameConstants.STAMINA_DRAIN_MELEE_HEAVY * CfgGameplayHandler.GetMeleeStaminaModifier(),
  668. );
  669. m_StaminaModifiers.RegisterFixed(
  670. EStaminaModifiers.OVERALL_DRAIN,
  671. CfgGameplayHandler.GetStaminaMax(),
  672. 5.0,
  673. );
  674. m_StaminaModifiers.RegisterRandomized(
  675. EStaminaModifiers.MELEE_EVADE,
  676. 3 * CfgGameplayHandler.GetMeleeStaminaModifier(),
  677. GameConstants.STAMINA_DRAIN_MELEE_EVADE * CfgGameplayHandler.GetMeleeStaminaModifier(),
  678. );
  679. m_StaminaModifiers.RegisterFixed(EStaminaModifiers.ROLL, GameConstants.STAMINA_DRAIN_ROLL);
  680. }
  681. //! Calulates stamina regain bonus coef based on current stamina cap and level
  682. protected float CalcStaminaGainBonus()
  683. {
  684. if (m_StaminaDepletion > 0)
  685. return 0;
  686. if (m_Stamina > 25)
  687. return Math.Min((m_Stamina/10),GameConstants.STAMINA_GAIN_BONUS_CAP); // exp version
  688. else
  689. return GameConstants.STAMINA_GAIN_BONUS_CAP; // linear version
  690. }
  691. protected void ApplyExhaustion()
  692. {
  693. //! sets exhaustion look of player based on stamina level
  694. HumanCommandAdditives ad = m_Player.GetCommandModifier_Additives();
  695. float exhaustion_value = 1;
  696. if (m_StaminaCap != 0)
  697. {
  698. exhaustion_value = 1 - ((m_Stamina / (m_StaminaCap * 0.01)) * 0.01);
  699. }
  700. exhaustion_value = Math.Min(1, exhaustion_value);
  701. if (ad)
  702. {
  703. // do not apply exhaustion on local client if player is in ADS/Optics (camera shakes)
  704. if (m_Player.GetInstanceType() == DayZPlayerInstanceType.INSTANCETYPE_CLIENT && (m_Player.IsInOptics() || m_Player.IsInIronsights()))
  705. {
  706. ad.SetExhaustion(0, true);
  707. }
  708. else
  709. {
  710. ad.SetExhaustion(exhaustion_value, true);
  711. }
  712. }
  713. }
  714. //! check if the stamina is completely depleted
  715. protected void CheckStaminaState()
  716. {
  717. if (m_Stamina <= 0)
  718. {
  719. m_StaminaDepleted = true;
  720. //! in case of complete depletion - start a cooldown timer before the regeneration cycle start
  721. if (!m_IsInCooldown)
  722. {
  723. // set this only once
  724. SetCooldown(GameConstants.STAMINA_REGEN_COOLDOWN_EXHAUSTION);
  725. }
  726. }
  727. else
  728. {
  729. m_StaminaDepleted = false;
  730. }
  731. }
  732. //! set cooldown timer between each consume of stamina
  733. protected void SetCooldown(float time, int modifier = -1)
  734. {
  735. if ( m_StaminaDepleted || m_Stamina <= 0.0 )
  736. {
  737. ResetCooldown(modifier);
  738. return;
  739. }
  740. m_IsInCooldown = true;
  741. Timer timer;
  742. if (m_TimerMap.Find(modifier, timer) && timer.IsRunning())
  743. {
  744. timer.Stop();
  745. }
  746. else
  747. {
  748. timer = new Timer;
  749. m_TimerMap.Set(modifier,timer);
  750. }
  751. timer.Run(time, this, "ResetCooldown", new Param1<int>( modifier ));
  752. //Print(m_TimerMap.Count());
  753. }
  754. protected void ResetCooldown(int modifier = -1)
  755. {
  756. StaminaModifier sm = m_StaminaModifiers.GetModifierData(modifier);
  757. if (sm)
  758. {
  759. //Print(modifier);
  760. //Error("Error: No StaminaModifier found! | StaminaHandler | ResetCooldown");
  761. sm.SetStartTime(-1);
  762. sm.ResetRunTime();
  763. sm.SetInUse(false);
  764. }
  765. m_IsInCooldown = false;
  766. }
  767. Timer GetCooldownTimer(int modifier)
  768. {
  769. }
  770. // ---------------------------------------------------
  771. bool HasEnoughStaminaFor(EStaminaConsumers consumer)
  772. {
  773. return m_StaminaConsumers.HasEnoughStaminaFor(consumer, m_StaminaSynced, m_StaminaDepleted, m_StaminaCap);
  774. }
  775. bool HasEnoughStaminaToStart(EStaminaConsumers consumer)
  776. {
  777. return m_StaminaConsumers.HasEnoughStaminaToStart(consumer, m_StaminaSynced, m_StaminaDepleted, m_StaminaCap);
  778. }
  779. void SetStamina(float stamina_value)
  780. {
  781. m_Stamina = Math.Clamp(stamina_value, 0, CfgGameplayHandler.GetStaminaMax());
  782. SyncStamina(m_Stamina, m_StaminaCap, m_IsInCooldown);
  783. }
  784. float GetStamina()
  785. {
  786. return m_Stamina;
  787. }
  788. float GetStaminaNormalized()
  789. {
  790. return m_Stamina / GetStaminaMax();
  791. }
  792. float GetSyncedStamina()
  793. {
  794. return m_StaminaSynced;
  795. }
  796. float GetSyncedStaminaNormalized()
  797. {
  798. return GetSyncedStamina() / GetStaminaMax();
  799. }
  800. float GetStaminaCap()
  801. {
  802. return m_StaminaCap;
  803. }
  804. float GetStaminaMax()
  805. {
  806. return CfgGameplayHandler.GetStaminaMax();
  807. }
  808. //obsolete, use ActivateDepletionModifier/DeactivateDepletionModifier instead
  809. void SetDepletionMultiplier(float val)
  810. {
  811. if (m_StaminaDepletionMultiplier < 0)
  812. m_StaminaDepletionMultiplier = 0;
  813. m_StaminaDepletionMultiplier = val;
  814. SyncAdditionalStaminaInfo(new Param2<float,float>(m_StaminaDepletionMultiplier,m_StaminaRecoveryMultiplier));
  815. }
  816. //obsolete, use ActivateRecoveryModifier/DeactivateRecoveryModifier instead
  817. void SetRecoveryMultiplier(float val)
  818. {
  819. if (m_StaminaRecoveryMultiplier < 0)
  820. m_StaminaRecoveryMultiplier = 0;
  821. m_StaminaRecoveryMultiplier = val;
  822. SyncAdditionalStaminaInfo(new Param2<float,float>(m_StaminaDepletionMultiplier,m_StaminaRecoveryMultiplier));
  823. }
  824. float GetDepletionMultiplier()
  825. {
  826. return m_StaminaDepletionMultiplier;
  827. }
  828. float GetRecoveryMultiplier()
  829. {
  830. return m_StaminaRecoveryMultiplier;
  831. }
  832. void DepleteStaminaEx(EStaminaModifiers modifier, float dT = -1, float coef = 1.0)
  833. {
  834. #ifdef DIAG_DEVELOPER
  835. if (m_StaminaDisabled)
  836. return;
  837. #endif
  838. float val = 0.0;
  839. float current_time = m_Player.GetSimulationTimeStamp();
  840. float valueProgress;
  841. StaminaModifier sm = m_StaminaModifiers.GetModifierData(modifier);
  842. // select by modifier type and drain stamina
  843. switch (sm.GetType())
  844. {
  845. case m_StaminaModifiers.FIXED:
  846. if (dT == -1)
  847. {
  848. dT = 1;
  849. }
  850. m_StaminaDepletion += sm.GetMaxValue() * dT * coef;
  851. break;
  852. case m_StaminaModifiers.RANDOMIZED:
  853. val = Math.RandomFloat(sm.GetMinValue(), sm.GetMaxValue());
  854. m_StaminaDepletion += val * coef;
  855. break;
  856. case m_StaminaModifiers.LINEAR:
  857. if (!sm.IsInUse())
  858. {
  859. sm.SetStartTime(current_time + sm.GetStartTimeAdjustment()/dT);
  860. sm.SetRunTimeTick(dT);
  861. sm.SetInUse(true);
  862. }
  863. valueProgress = Math.Clamp((current_time - sm.GetStartTime())/sm.GetDurationAdjusted(), 0, 1 );
  864. val = Math.Lerp(sm.GetMinValue(), sm.GetMaxValue(), valueProgress);
  865. m_StaminaDepletion += val * coef;
  866. break;
  867. case m_StaminaModifiers.EXPONENTIAL:
  868. StaminaModifierExponential smex;
  869. if (!Class.CastTo(smex,sm))
  870. {
  871. ErrorEx("StaminaModifierExponential not found for modifier type: " + sm.GetType());
  872. break;
  873. }
  874. if (!smex.IsInUse())
  875. {
  876. smex.SetStartTime(current_time + smex.GetStartTimeAdjustment()/dT);
  877. smex.SetRunTimeTick(dT);
  878. smex.SetInUse(true);
  879. }
  880. valueProgress = Math.Clamp((current_time - smex.GetStartTime())/smex.GetDurationAdjusted(), 0, 1 );
  881. float exp;
  882. if (Math.AbsFloat(smex.GetBaseValue()) < 1)
  883. {
  884. exp = 1 - Math.Lerp(0, smex.GetExponent(), valueProgress);
  885. val = Math.Pow(smex.GetBaseValue(),exp);
  886. }
  887. else
  888. {
  889. exp = Math.Lerp(Math.Min(0, smex.GetExponent()), Math.Max(0, smex.GetExponent()), valueProgress);
  890. val = Math.Pow(smex.GetBaseValue(),exp) + smex.GetBaseValue() - 1;
  891. }
  892. m_StaminaDepletion += val * smex.GetMultiplier() * coef;
  893. break;
  894. }
  895. //! run cooldown right after depletion
  896. SetCooldown(sm.GetCooldown(),modifier);
  897. m_StaminaDepletion *= m_StaminaDepletionMultiplier;
  898. m_StaminaDepletion = Math.Clamp(m_StaminaDepletion, 0, CfgGameplayHandler.GetStaminaMax());
  899. }
  900. #ifdef DIAG_DEVELOPER
  901. void SetStaminaDisabled(bool value)
  902. {
  903. m_StaminaDisabled = value;
  904. }
  905. #endif
  906. ////////////////////////////
  907. //Deprecated code playpen//
  908. //////////////////////////
  909. //! Deprecated
  910. void DepleteStamina(EStaminaModifiers modifier, float dT = -1)
  911. {
  912. DepleteStaminaEx(modifier,dT);
  913. }
  914. }