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