staminahandler.c 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058
  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. if ( m_StaminaConsumers && m_StaminaConsumers.Contains(consumer) )
  57. {
  58. StaminaConsumer sc = m_StaminaConsumers.Get(consumer);
  59. if ( consumer != EStaminaConsumers.SPRINT )
  60. {
  61. if ( (isDepleted || (curStamina < sc.GetDrainThreshold()/* && curStamina < cap*/)) )
  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. if ( m_StaminaConsumers && m_StaminaConsumers.Contains(consumer) )
  94. {
  95. StaminaConsumer sc = m_StaminaConsumers.Get(consumer);
  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_StaminaCap = CfgGameplayHandler.GetStaminaMax();
  255. m_StaminaDepletion = 0;
  256. m_StaminaDepletionMultiplier = 1;
  257. m_StaminaRecoveryMultiplier = 1;
  258. m_Time = 0;
  259. m_StaminaDepleted = false;
  260. m_IsInCooldown = false;
  261. m_HumanMoveSettings = m_Player.GetDayZPlayerType().CommandMoveSettingsW();
  262. RegisterStaminaConsumers();
  263. RegisterStaminaModifiers();
  264. m_TimerMap = new map<int,ref Timer>;
  265. //----------------- depletion --------------------
  266. m_RegisteredDepletionModifiers = new map<EStaminaMultiplierTypes, float>;
  267. m_ActiveDepletionModifiers = new set<EStaminaMultiplierTypes>;
  268. //----------------- recovery --------------------
  269. m_RegisteredRecoveryModifiers = new map<EStaminaMultiplierTypes, float>;
  270. m_ActiveRecoveryModifiers = new set<EStaminaMultiplierTypes>;
  271. Init();
  272. }
  273. void Init()
  274. {
  275. //----------------- depletion --------------------
  276. m_RegisteredDepletionModifiers.Insert(EStaminaMultiplierTypes.MASK, MaskMdfr.STAMINA_DEPLETION_MODIFIER);
  277. m_RegisteredDepletionModifiers.Insert(EStaminaMultiplierTypes.FATIGUE, FatigueMdfr.STAMINA_DEPLETION_MULTIPLIER);
  278. m_RegisteredDepletionModifiers.Insert(EStaminaMultiplierTypes.EPINEPHRINE, EpinephrineMdfr.STAMINA_DEPLETION_MULTIPLIER);
  279. m_RegisteredDepletionModifiers.Insert(EStaminaMultiplierTypes.VOMIT_EXHAUSTION, VomitSymptom.STAMINA_DEPLETION_MULTIPLIER);
  280. m_RegisteredDepletionModifiers.Insert(EStaminaMultiplierTypes.DISEASE_PNEUMONIA, PneumoniaMdfr.STAMINA_DEPLETION_MULTIPLIER);
  281. //----------------- recovery --------------------
  282. m_RegisteredRecoveryModifiers.Insert(EStaminaMultiplierTypes.MASK, MaskMdfr.STAMINA_RECOVERY_MODIFIER);
  283. m_RegisteredRecoveryModifiers.Insert(EStaminaMultiplierTypes.FATIGUE, FatigueMdfr.STAMINA_RECOVERY_MULTIPLIER);
  284. m_RegisteredRecoveryModifiers.Insert(EStaminaMultiplierTypes.DROWNING, DrowningMdfr.STAMINA_RECOVERY_MULTIPLIER);
  285. m_RegisteredRecoveryModifiers.Insert(EStaminaMultiplierTypes.VOMIT_EXHAUSTION, VomitSymptom.STAMINA_RECOVERY_MULTIPLIER);
  286. m_RegisteredRecoveryModifiers.Insert(EStaminaMultiplierTypes.DISEASE_PNEUMONIA, PneumoniaMdfr.STAMINA_RECOVERY_MULTIPLIER);
  287. }
  288. void ActivateDepletionModifier(EStaminaMultiplierTypes type)
  289. {
  290. if (m_RegisteredDepletionModifiers.Contains(type))
  291. {
  292. m_ActiveDepletionModifiers.Insert(type);
  293. RecalculateDepletionMultiplier();
  294. }
  295. else
  296. {
  297. Error("attempting to activate unregistered depletion modifier");
  298. }
  299. }
  300. void DeactivateDepletionModifier(EStaminaMultiplierTypes type)
  301. {
  302. int index = m_ActiveDepletionModifiers.Find(type);
  303. if (index != -1)
  304. {
  305. m_ActiveDepletionModifiers.Remove(index);
  306. RecalculateDepletionMultiplier();
  307. }
  308. }
  309. void RecalculateDepletionMultiplier()
  310. {
  311. float value = 1;
  312. foreach (int multiplier: m_ActiveDepletionModifiers)
  313. {
  314. value *= m_RegisteredDepletionModifiers.Get(multiplier);
  315. }
  316. if (value != m_StaminaDepletionMultiplier)
  317. SetDepletionMultiplier(value);
  318. }
  319. void ActivateRecoveryModifier(EStaminaMultiplierTypes type)
  320. {
  321. if (m_RegisteredRecoveryModifiers.Contains(type))
  322. {
  323. m_ActiveRecoveryModifiers.Insert(type);
  324. RecalculateRecoveryMultiplier();
  325. }
  326. else
  327. {
  328. Error("attempting to activate unregistered recovery modifier");
  329. }
  330. }
  331. void DeactivateRecoveryModifier(EStaminaMultiplierTypes type)
  332. {
  333. int index = m_ActiveRecoveryModifiers.Find(type);
  334. if (index != -1)
  335. {
  336. m_ActiveRecoveryModifiers.Remove(index);
  337. RecalculateRecoveryMultiplier();
  338. }
  339. }
  340. void RecalculateRecoveryMultiplier()
  341. {
  342. float value = 1;
  343. foreach (int multiplier: m_ActiveRecoveryModifiers)
  344. {
  345. value *= m_RegisteredRecoveryModifiers.Get(multiplier);
  346. }
  347. if (value != m_StaminaRecoveryMultiplier)
  348. {
  349. SetRecoveryMultiplier(value);
  350. }
  351. }
  352. void Update(float deltaT, int pCurrentCommandID)
  353. {
  354. #ifdef DIAG_DEVELOPER
  355. if (m_StaminaDisabled)
  356. return;
  357. #endif
  358. if (m_Player)
  359. {
  360. // Calculates actual max stamina based on player's load
  361. if (GetGame().IsServer() || !GetGame().IsMultiplayer())
  362. {
  363. //! gets the actual players load
  364. m_PlayerLoad = m_Player.GetWeightEx();
  365. //! StaminaCap calculation starts when PlayerLoad exceeds STAMINA_WEIGHT_LIMIT_THRESHOLD
  366. if (m_PlayerLoad >= CfgGameplayHandler.GetStaminaWeightLimitThreshold())
  367. {
  368. m_StaminaCap = Math.Max((CfgGameplayHandler.GetStaminaMax() - (((m_PlayerLoad - CfgGameplayHandler.GetStaminaWeightLimitThreshold())/GameConstants.STAMINA_KG_TO_GRAMS) * CfgGameplayHandler.GetStaminaKgToStaminaPercentPenalty())),CfgGameplayHandler.GetStaminaMinCap());
  369. }
  370. else
  371. {
  372. m_StaminaCap = CfgGameplayHandler.GetStaminaMax();
  373. }
  374. }
  375. // Calculates stamina gain/loss based on movement and load
  376. m_Player.GetMovementState(m_State);
  377. switch (m_State.m_CommandTypeId)
  378. {
  379. case DayZPlayerConstants.COMMANDID_MOVE:
  380. StaminaProcessor_Move(m_State);
  381. break;
  382. case DayZPlayerConstants.COMMANDID_LADDER:
  383. StaminaProcessor_Ladder(m_State);
  384. break;
  385. case DayZPlayerConstants.COMMANDID_SWIM:
  386. StaminaProcessor_Swimming(m_State);
  387. break;
  388. case DayZPlayerConstants.COMMANDID_FALL: //! processed on event
  389. case DayZPlayerConstants.COMMANDID_MELEE2: //! processed on event
  390. case DayZPlayerConstants.COMMANDID_CLIMB: //! processed on event
  391. break;
  392. default:
  393. if (!m_IsInCooldown)
  394. {
  395. m_StaminaDelta = GameConstants.STAMINA_GAIN_IDLE_PER_SEC;
  396. }
  397. break;
  398. }
  399. //Sets current stamina & stores + syncs data with client
  400. float temp = m_StaminaDelta * deltaT;
  401. if (temp < 0)
  402. {
  403. temp *= m_StaminaDepletionMultiplier;
  404. }
  405. else
  406. {
  407. temp *= m_StaminaRecoveryMultiplier;
  408. }
  409. m_Stamina = Math.Max(0, Math.Min((m_Stamina + temp), m_StaminaCap));
  410. m_Stamina = m_Stamina - m_StaminaDepletion;
  411. if (GetGame().IsServer() || !GetGame().IsMultiplayer())
  412. {
  413. m_Player.GetStatStamina().Set(m_Stamina);
  414. m_Time += deltaT;
  415. if (m_Time >= GameConstants.STAMINA_SYNC_RATE)
  416. {
  417. m_Time = 0;
  418. SetStamina(m_Stamina);
  419. }
  420. }
  421. #ifndef SERVER
  422. m_Player.SetStamina(m_StaminaSynced, m_StaminaCap);
  423. #endif
  424. ApplyExhaustion();
  425. CheckStaminaState();
  426. m_StaminaDelta = 0;
  427. m_StaminaDepletion = 0; // resets depletion modifier
  428. }
  429. }
  430. //! deprecated use, StaminaHandler uses SyncJunctures now
  431. void OnRPC(float stamina, float stamina_cap, bool cooldown)
  432. {
  433. }
  434. //! 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)
  435. void OnSyncJuncture(int pJunctureID, ParamsReadContext pCtx)
  436. {
  437. switch ( pJunctureID )
  438. {
  439. case DayZPlayerSyncJunctures.SJ_STAMINA:
  440. float stamina;
  441. float stamina_cap;
  442. bool cooldown;
  443. if (!pCtx.Read(stamina) || !pCtx.Read(stamina_cap) || !pCtx.Read(cooldown))
  444. {
  445. return;
  446. }
  447. m_Stamina = stamina; //?
  448. m_StaminaSynced = stamina;
  449. if (m_Player.GetInstanceType() != DayZPlayerInstanceType.INSTANCETYPE_CLIENT)
  450. {
  451. return;
  452. }
  453. if ( stamina_cap != m_StaminaCap )
  454. {
  455. m_StaminaCap = stamina_cap;
  456. }
  457. m_IsInCooldown = cooldown;
  458. m_Player.SetStamina(m_Stamina, m_StaminaCap);
  459. break;
  460. case DayZPlayerSyncJunctures.SJ_STAMINA_MISC:
  461. ReadAdditionalStaminaInfo(pCtx);
  462. break;
  463. }
  464. }
  465. protected void StaminaProcessor_Move(HumanMovementState pHumanMovementState)
  466. {
  467. switch (pHumanMovementState.m_iMovement)
  468. {
  469. case DayZPlayerConstants.MOVEMENTIDX_SPRINT: //sprint
  470. if (pHumanMovementState.m_iStanceIdx == DayZPlayerConstants.STANCEIDX_ERECT)
  471. {
  472. m_StaminaDelta = -GameConstants.STAMINA_DRAIN_STANDING_SPRINT_PER_SEC * CfgGameplayHandler.GetSprintStaminaModifierErc();
  473. SetCooldown(GameConstants.STAMINA_REGEN_COOLDOWN_DEPLETION);
  474. break;
  475. }
  476. else if ( pHumanMovementState.m_iStanceIdx == DayZPlayerConstants.STANCEIDX_CROUCH)
  477. {
  478. m_StaminaDelta = -GameConstants.STAMINA_DRAIN_CROUCHED_SPRINT_PER_SEC * CfgGameplayHandler.GetSprintStaminaModifierCro();
  479. SetCooldown(GameConstants.STAMINA_REGEN_COOLDOWN_DEPLETION);
  480. break;
  481. }
  482. m_StaminaDelta = GameConstants.STAMINA_GAIN_JOG_PER_SEC;
  483. break;
  484. case DayZPlayerConstants.MOVEMENTIDX_RUN: //jog
  485. if (m_Player.GetCurrentWaterLevel() >= m_HumanMoveSettings.m_fWaterLevelSpeedRectrictionHigh)
  486. {
  487. m_StaminaDelta = -GameConstants.STAMINA_DRAIN_STANDING_SPRINT_PER_SEC * CfgGameplayHandler.GetSprintStaminaModifierErc();
  488. break;
  489. }
  490. if (!m_IsInCooldown)
  491. {
  492. m_StaminaDelta = (GameConstants.STAMINA_GAIN_JOG_PER_SEC + CalcStaminaGainBonus());
  493. }
  494. break;
  495. case DayZPlayerConstants.MOVEMENTIDX_WALK: //walk
  496. if (!m_IsInCooldown)
  497. {
  498. m_StaminaDelta = (GameConstants.STAMINA_GAIN_WALK_PER_SEC + CalcStaminaGainBonus());
  499. }
  500. break;
  501. case DayZPlayerConstants.MOVEMENTIDX_IDLE: //idle
  502. if (m_Player.IsRolling())
  503. {
  504. m_StaminaDelta = GameConstants.STAMINA_GAIN_ROLL_PER_SEC;
  505. break;
  506. }
  507. if (!m_IsInCooldown)
  508. {
  509. m_StaminaDelta = (GameConstants.STAMINA_GAIN_IDLE_PER_SEC + CalcStaminaGainBonus());
  510. }
  511. break;
  512. default:
  513. if (!m_IsInCooldown)
  514. {
  515. m_StaminaDelta = GameConstants.STAMINA_GAIN_IDLE_PER_SEC;
  516. }
  517. break;
  518. }
  519. }
  520. protected void StaminaProcessor_Ladder(HumanMovementState pHumanMovementState)
  521. {
  522. switch (pHumanMovementState.m_iMovement)
  523. {
  524. case 2: //climb up (fast)
  525. m_StaminaDelta = -GameConstants.STAMINA_DRAIN_LADDER_FAST_PER_SEC * CfgGameplayHandler.GetSprintLadderStaminaModifier();
  526. SetCooldown(GameConstants.STAMINA_REGEN_COOLDOWN_DEPLETION);
  527. break;
  528. case 1: //climb up (slow)
  529. if (!m_IsInCooldown)
  530. {
  531. m_StaminaDelta = (GameConstants.STAMINA_GAIN_LADDER_PER_SEC + CalcStaminaGainBonus());
  532. }
  533. break;
  534. default:
  535. if (!m_IsInCooldown)
  536. {
  537. m_StaminaDelta = GameConstants.STAMINA_GAIN_IDLE_PER_SEC + CalcStaminaGainBonus();
  538. }
  539. break;
  540. }
  541. }
  542. protected void StaminaProcessor_Swimming(HumanMovementState pHumanMovementState)
  543. {
  544. switch (pHumanMovementState.m_iMovement)
  545. {
  546. case 3: //swim fast
  547. m_StaminaDelta = -GameConstants.STAMINA_DRAIN_SWIM_FAST_PER_SEC * CfgGameplayHandler.GetSprintSwimmingStaminaModifier();
  548. SetCooldown(GameConstants.STAMINA_REGEN_COOLDOWN_DEPLETION);
  549. break;
  550. case 2: //swim slow
  551. if (!m_IsInCooldown)
  552. {
  553. m_StaminaDelta = (GameConstants.STAMINA_GAIN_SWIM_PER_SEC + CalcStaminaGainBonus());
  554. }
  555. break;
  556. default:
  557. if (!m_IsInCooldown)
  558. {
  559. m_StaminaDelta = GameConstants.STAMINA_GAIN_IDLE_PER_SEC + CalcStaminaGainBonus();
  560. }
  561. break;
  562. }
  563. }
  564. //! stamina sync - server part
  565. protected void SyncStamina(float stamina, float stamina_cap, bool cooldown)
  566. {
  567. m_Player.GetStatStamina().Set(m_Stamina);
  568. ScriptJunctureData pCtx = new ScriptJunctureData();
  569. pCtx.Write(m_Stamina);
  570. pCtx.Write(m_StaminaCap);
  571. pCtx.Write(m_IsInCooldown);
  572. m_Player.SendSyncJuncture(DayZPlayerSyncJunctures.SJ_STAMINA,pCtx);
  573. }
  574. //! Method to sync more info for stamina manager. Template parameter means it is very extendable for further use
  575. protected void SyncAdditionalStaminaInfo(Param par)
  576. {
  577. Param2<float,float> p = Param2<float,float>.Cast(par);
  578. ScriptJunctureData pCtx = new ScriptJunctureData();
  579. pCtx.Write(p.param1);
  580. pCtx.Write(p.param2);
  581. m_Player.SendSyncJuncture(DayZPlayerSyncJunctures.SJ_STAMINA_MISC,pCtx);
  582. }
  583. //! Order of read parameters must match the order of writing above
  584. protected void ReadAdditionalStaminaInfo(ParamsReadContext pCtx)
  585. {
  586. float depletionMultiplier;
  587. float recoveryMultiplier;
  588. if (!pCtx.Read(depletionMultiplier) || !pCtx.Read(recoveryMultiplier))
  589. {
  590. return;
  591. }
  592. m_StaminaDepletionMultiplier = depletionMultiplier;
  593. m_StaminaRecoveryMultiplier = recoveryMultiplier;
  594. }
  595. protected void RegisterStaminaConsumers()
  596. {
  597. m_StaminaConsumers = new StaminaConsumers();
  598. m_StaminaConsumers.RegisterConsumer(
  599. EStaminaConsumers.HOLD_BREATH,
  600. GameConstants.STAMINA_HOLD_BREATH_THRESHOLD_ACTIVATE,
  601. GameConstants.STAMINA_HOLD_BREATH_THRESHOLD_DRAIN,
  602. );
  603. m_StaminaConsumers.RegisterConsumer(
  604. EStaminaConsumers.SPRINT,
  605. CfgGameplayHandler.GetStaminaMinCap() + 15,
  606. );
  607. m_StaminaConsumers.RegisterConsumer(
  608. EStaminaConsumers.JUMP,
  609. GameConstants.STAMINA_JUMP_THRESHOLD,
  610. );
  611. m_StaminaConsumers.RegisterConsumer(
  612. EStaminaConsumers.VAULT,
  613. GameConstants.STAMINA_VAULT_THRESHOLD,
  614. );
  615. m_StaminaConsumers.RegisterConsumer(
  616. EStaminaConsumers.CLIMB,
  617. GameConstants.STAMINA_CLIMB_THRESHOLD,
  618. );
  619. m_StaminaConsumers.RegisterConsumer(
  620. EStaminaConsumers.MELEE_HEAVY,
  621. GameConstants.STAMINA_MELEE_HEAVY_THRESHOLD,
  622. );
  623. m_StaminaConsumers.RegisterConsumer(
  624. EStaminaConsumers.MELEE_EVADE,
  625. GameConstants.STAMINA_MELEE_EVADE_THRESHOLD,
  626. );
  627. m_StaminaConsumers.RegisterConsumer(
  628. EStaminaConsumers.ROLL,
  629. GameConstants.STAMINA_ROLL_THRESHOLD,
  630. );
  631. m_StaminaConsumers.RegisterConsumer(EStaminaConsumers.DROWN, 0);
  632. m_StaminaConsumers.RegisterConsumer(EStaminaConsumers.PUSH, 0);
  633. }
  634. protected void RegisterStaminaModifiers()
  635. {
  636. m_StaminaModifiers = new StaminaModifiers();
  637. SMDataHoldBreath data = new SMDataHoldBreath();
  638. m_StaminaModifiers.RegisterExponentialEx(
  639. EStaminaModifiers.HOLD_BREATH,
  640. data,
  641. );
  642. m_StaminaModifiers.RegisterExponentialEx(
  643. EStaminaModifiers.PUSH_CAR,
  644. data,
  645. );
  646. m_StaminaModifiers.RegisterFixed(EStaminaModifiers.DROWN, 10);
  647. m_StaminaModifiers.RegisterFixed(
  648. EStaminaModifiers.JUMP,
  649. GameConstants.STAMINA_DRAIN_JUMP * CfgGameplayHandler.GetObstacleTraversalStaminaModifier(),
  650. );
  651. m_StaminaModifiers.RegisterFixed(
  652. EStaminaModifiers.VAULT,
  653. GameConstants.STAMINA_DRAIN_VAULT * CfgGameplayHandler.GetObstacleTraversalStaminaModifier(),
  654. );
  655. m_StaminaModifiers.RegisterFixed(
  656. EStaminaModifiers.CLIMB,
  657. GameConstants.STAMINA_DRAIN_CLIMB * CfgGameplayHandler.GetObstacleTraversalStaminaModifier(),
  658. );
  659. m_StaminaModifiers.RegisterFixed(
  660. EStaminaModifiers.MELEE_LIGHT,
  661. GameConstants.STAMINA_DRAIN_MELEE_LIGHT * CfgGameplayHandler.GetMeleeStaminaModifier(),
  662. );
  663. m_StaminaModifiers.RegisterFixed(
  664. EStaminaModifiers.MELEE_HEAVY,
  665. GameConstants.STAMINA_DRAIN_MELEE_HEAVY * CfgGameplayHandler.GetMeleeStaminaModifier(),
  666. );
  667. m_StaminaModifiers.RegisterFixed(
  668. EStaminaModifiers.OVERALL_DRAIN,
  669. CfgGameplayHandler.GetStaminaMax(),
  670. 5.0,
  671. );
  672. m_StaminaModifiers.RegisterRandomized(
  673. EStaminaModifiers.MELEE_EVADE,
  674. 3 * CfgGameplayHandler.GetMeleeStaminaModifier(),
  675. GameConstants.STAMINA_DRAIN_MELEE_EVADE * CfgGameplayHandler.GetMeleeStaminaModifier(),
  676. );
  677. m_StaminaModifiers.RegisterFixed(EStaminaModifiers.ROLL, GameConstants.STAMINA_DRAIN_ROLL);
  678. }
  679. //! Calulates stamina regain bonus coef based on current stamina cap and level
  680. protected float CalcStaminaGainBonus()
  681. {
  682. if (m_StaminaDepletion > 0)
  683. return 0;
  684. if (m_Stamina > 25)
  685. return Math.Min((m_Stamina/10),GameConstants.STAMINA_GAIN_BONUS_CAP); // exp version
  686. else
  687. return GameConstants.STAMINA_GAIN_BONUS_CAP; // linear version
  688. }
  689. protected void ApplyExhaustion()
  690. {
  691. //! sets exhaustion look of player based on stamina level
  692. HumanCommandAdditives ad = m_Player.GetCommandModifier_Additives();
  693. float exhaustion_value = 1;
  694. if (m_StaminaCap != 0)
  695. {
  696. exhaustion_value = 1 - ((m_Stamina / (m_StaminaCap * 0.01)) * 0.01);
  697. }
  698. exhaustion_value = Math.Min(1, exhaustion_value);
  699. if (ad)
  700. {
  701. // do not apply exhaustion on local client if player is in ADS/Optics (camera shakes)
  702. if (m_Player.GetInstanceType() == DayZPlayerInstanceType.INSTANCETYPE_CLIENT && (m_Player.IsInOptics() || m_Player.IsInIronsights()))
  703. {
  704. ad.SetExhaustion(0, true);
  705. }
  706. else
  707. {
  708. ad.SetExhaustion(exhaustion_value, true);
  709. }
  710. }
  711. }
  712. //! check if the stamina is completely depleted
  713. protected void CheckStaminaState()
  714. {
  715. if (m_Stamina <= 0)
  716. {
  717. m_StaminaDepleted = true;
  718. //! in case of complete depletion - start a cooldown timer before the regeneration cycle start
  719. if (!m_IsInCooldown)
  720. {
  721. // set this only once
  722. SetCooldown(GameConstants.STAMINA_REGEN_COOLDOWN_EXHAUSTION);
  723. }
  724. }
  725. else
  726. {
  727. m_StaminaDepleted = false;
  728. }
  729. }
  730. //! set cooldown timer between each consume of stamina
  731. protected void SetCooldown(float time, int modifier = -1)
  732. {
  733. if ( m_StaminaDepleted || m_Stamina <= 0.0 )
  734. {
  735. ResetCooldown(modifier);
  736. return;
  737. }
  738. m_IsInCooldown = true;
  739. Timer timer;
  740. if (m_TimerMap.Find(modifier, timer) && timer.IsRunning())
  741. {
  742. timer.Stop();
  743. }
  744. else
  745. {
  746. timer = new ref Timer;
  747. m_TimerMap.Set(modifier,timer);
  748. }
  749. timer.Run(time, this, "ResetCooldown", new Param1<int>( modifier ));
  750. //Print(m_TimerMap.Count());
  751. }
  752. protected void ResetCooldown(int modifier = -1)
  753. {
  754. StaminaModifier sm = m_StaminaModifiers.GetModifierData(modifier);
  755. if (sm)
  756. {
  757. //Print(modifier);
  758. //Error("Error: No StaminaModifier found! | StaminaHandler | ResetCooldown");
  759. sm.SetStartTime(-1);
  760. sm.ResetRunTime();
  761. sm.SetInUse(false);
  762. }
  763. m_IsInCooldown = false;
  764. }
  765. Timer GetCooldownTimer(int modifier)
  766. {
  767. }
  768. // ---------------------------------------------------
  769. bool HasEnoughStaminaFor(EStaminaConsumers consumer)
  770. {
  771. return m_StaminaConsumers.HasEnoughStaminaFor(consumer, m_Stamina, m_StaminaDepleted, m_StaminaCap);
  772. }
  773. bool HasEnoughStaminaToStart(EStaminaConsumers consumer)
  774. {
  775. return m_StaminaConsumers.HasEnoughStaminaToStart(consumer, m_Stamina, m_StaminaDepleted, m_StaminaCap);
  776. }
  777. void SetStamina(float stamina_value)
  778. {
  779. m_Stamina = Math.Clamp(stamina_value, 0, CfgGameplayHandler.GetStaminaMax());
  780. SyncStamina(m_Stamina, m_StaminaCap, m_IsInCooldown);
  781. }
  782. float GetStamina()
  783. {
  784. return m_Stamina;
  785. }
  786. float GetStaminaNormalized()
  787. {
  788. return m_Stamina / GetStaminaMax();
  789. }
  790. float GetSyncedStamina()
  791. {
  792. return m_StaminaSynced;
  793. }
  794. float GetSyncedStaminaNormalized()
  795. {
  796. return GetSyncedStamina() / GetStaminaMax();
  797. }
  798. float GetStaminaCap()
  799. {
  800. return m_StaminaCap;
  801. }
  802. float GetStaminaMax()
  803. {
  804. return CfgGameplayHandler.GetStaminaMax();
  805. }
  806. //obsolete, use ActivateDepletionModifier/DeactivateDepletionModifier instead
  807. void SetDepletionMultiplier(float val)
  808. {
  809. if (m_StaminaDepletionMultiplier < 0)
  810. m_StaminaDepletionMultiplier = 0;
  811. m_StaminaDepletionMultiplier = val;
  812. SyncAdditionalStaminaInfo(new Param2<float,float>(m_StaminaDepletionMultiplier,m_StaminaRecoveryMultiplier));
  813. }
  814. //obsolete, use ActivateRecoveryModifier/DeactivateRecoveryModifier instead
  815. void SetRecoveryMultiplier(float val)
  816. {
  817. if (m_StaminaRecoveryMultiplier < 0)
  818. m_StaminaRecoveryMultiplier = 0;
  819. m_StaminaRecoveryMultiplier = val;
  820. SyncAdditionalStaminaInfo(new Param2<float,float>(m_StaminaDepletionMultiplier,m_StaminaRecoveryMultiplier));
  821. }
  822. float GetDepletionMultiplier()
  823. {
  824. return m_StaminaDepletionMultiplier;
  825. }
  826. float GetRecoveryMultiplier()
  827. {
  828. return m_StaminaRecoveryMultiplier;
  829. }
  830. void DepleteStamina(EStaminaModifiers modifier, float dT = -1)
  831. {
  832. #ifdef DIAG_DEVELOPER
  833. if (m_StaminaDisabled)
  834. return;
  835. #endif
  836. float val = 0.0;
  837. float current_time = m_Player.GetSimulationTimeStamp();
  838. float valueProgress;
  839. StaminaModifier sm = m_StaminaModifiers.GetModifierData(modifier);
  840. // select by modifier type and drain stamina
  841. switch (sm.GetType())
  842. {
  843. case m_StaminaModifiers.FIXED:
  844. if (dT == -1)
  845. {
  846. dT = 1;
  847. }
  848. m_StaminaDepletion = m_StaminaDepletion + sm.GetMaxValue() * dT;
  849. break;
  850. case m_StaminaModifiers.RANDOMIZED:
  851. val = Math.RandomFloat(sm.GetMinValue(), sm.GetMaxValue());
  852. m_StaminaDepletion = m_StaminaDepletion + val;
  853. break;
  854. case m_StaminaModifiers.LINEAR:
  855. if (!sm.IsInUse())
  856. {
  857. sm.SetStartTime(current_time + sm.GetStartTimeAdjustment()/dT);
  858. sm.SetRunTimeTick(dT);
  859. sm.SetInUse(true);
  860. }
  861. valueProgress = Math.Clamp((current_time - sm.GetStartTime())/sm.GetDurationAdjusted(), 0, 1 );
  862. val = Math.Lerp(sm.GetMinValue(), sm.GetMaxValue(), valueProgress);
  863. m_StaminaDepletion = m_StaminaDepletion + val;
  864. break;
  865. case m_StaminaModifiers.EXPONENTIAL:
  866. StaminaModifierExponential smex;
  867. if (!Class.CastTo(smex,sm))
  868. {
  869. ErrorEx("StaminaModifierExponential not found for modifier type: " + sm.GetType());
  870. break;
  871. }
  872. if (!smex.IsInUse())
  873. {
  874. smex.SetStartTime(current_time + smex.GetStartTimeAdjustment()/dT);
  875. smex.SetRunTimeTick(dT);
  876. smex.SetInUse(true);
  877. }
  878. valueProgress = Math.Clamp((current_time - smex.GetStartTime())/smex.GetDurationAdjusted(), 0, 1 );
  879. float exp;
  880. if (Math.AbsFloat(smex.GetBaseValue()) < 1)
  881. {
  882. exp = 1 - Math.Lerp(0, smex.GetExponent(), valueProgress);
  883. val = Math.Pow(smex.GetBaseValue(),exp);
  884. }
  885. else
  886. {
  887. exp = Math.Lerp(Math.Min(0, smex.GetExponent()), Math.Max(0, smex.GetExponent()), valueProgress);
  888. val = Math.Pow(smex.GetBaseValue(),exp) + smex.GetBaseValue() - 1;
  889. }
  890. m_StaminaDepletion = m_StaminaDepletion + val;
  891. m_StaminaDepletion *= smex.GetMultiplier();
  892. break;
  893. }
  894. //! run cooldown right after depletion
  895. SetCooldown(sm.GetCooldown(),modifier);
  896. m_StaminaDepletion = Math.Clamp(m_StaminaDepletion, 0, CfgGameplayHandler.GetStaminaMax());
  897. m_StaminaDepletion = m_StaminaDepletion * m_StaminaDepletionMultiplier;
  898. }
  899. #ifdef DIAG_DEVELOPER
  900. void SetStaminaDisabled(bool value)
  901. {
  902. m_StaminaDisabled = value;
  903. }
  904. #endif
  905. }