2023-09-12 18:58:10 +00:00
#include "..\script_component.hpp"
2016-09-04 19:33:07 +00:00
/*
2024-06-22 17:53:08 +00:00
* Author: BaerMitUmlaut, ulteq
2016-09-23 21:40:52 +00:00
* Main looping function that updates fatigue values.
2016-09-04 19:33:07 +00:00
*
* Arguments:
* None
*
* Return Value:
* None
2017-06-08 13:31:51 +00:00
*
* Example:
* [] call ace_advanced_fatigue_fnc_mainLoop
*
* Public: No
2016-09-04 19:33:07 +00:00
*/
2022-03-09 03:41:56 +00:00
// Dead people don't breathe, will also handle null (map intros)
if (!alive ACE_player) exitWith {
2024-06-22 17:53:08 +00:00
[LINKFUNC(mainLoop), [], 1] call CBA_fnc_waitAndExecute;
2016-11-10 23:49:18 +00:00
private _staminaBarContainer = uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull];
_staminaBarContainer ctrlSetFade 1;
_staminaBarContainer ctrlCommit 1;
2016-09-23 21:40:52 +00:00
};
2016-09-04 19:33:07 +00:00
2024-06-22 17:53:08 +00:00
private _velocity = velocity ACE_player;
private _normal = surfaceNormal (getPosWorld ACE_player);
private _movementVector = vectorNormalized _velocity;
private _sideVector = vectorNormalized (_movementVector vectorCrossProduct _normal);
private _fwdAngle = asin (_movementVector select 2);
private _sideAngle = asin (_sideVector select 2);
2024-02-07 20:50:18 +00:00
2016-09-04 19:33:07 +00:00
private _currentWork = REE;
2024-06-22 17:53:08 +00:00
private _currentSpeed = (vectorMagnitude _velocity) min 6;
2016-10-06 08:59:21 +00:00
// fix #4481. Diving to the ground is recorded as PRONE stance with running speed velocity. Cap maximum speed to fix.
2017-03-15 15:12:07 +00:00
if (GVAR(isProne)) then {
2016-10-06 08:59:21 +00:00
_currentSpeed = _currentSpeed min 1.5;
};
2024-06-22 17:53:08 +00:00
// Get the current duty
private _duty = GVAR(animDuty);
{
if (_x isEqualType 0) then {
_duty = _duty * _x;
} else {
_duty = _duty * (ACE_player call _x);
};
} forEach (values GVAR(dutyList));
private _terrainGradient = abs _fwdAngle;
private _terrainFactor = 1;
private _gearMass = 0 max (((ACE_player getVariable [QEGVAR(movement,totalLoad), loadAbs ACE_player]) / 22.046 - UNDERWEAR_WEIGHT) * GVAR(loadFactor));
if (isNull objectParent ACE_player && {_currentSpeed > 0.1} && {isTouchingGround ACE_player || {underwater ACE_player}}) then {
if (!GVAR(isSwimming)) then {
// If the unit is going downhill, it's much less demanding
if (_fwdAngle < 0) then {
_terrainGradient = 0.15 * _terrainGradient;
};
// Used to simulate the unevenness/roughness of the terrain
if ((getPosATL ACE_player) select 2 < 0.01) then {
private _sideGradient = abs (_sideAngle / 45) min 1;
_terrainFactor = 1 + _sideGradient ^ 4;
};
};
_currentWork = [_duty, _gearMass, _terrainGradient * GVAR(terrainGradientFactor), _terrainFactor, _currentSpeed] call FUNC(getMetabolicCosts);
2016-09-04 19:33:07 +00:00
_currentWork = _currentWork max REE;
};
2024-06-22 17:53:08 +00:00
// Oxygen calculation
private _oxygen = if (GETEGVAR(medical,enabled,false) && {EGVAR(medical_vitals,simulateSpo2)}) then { // Defer to medical
(ACE_player getVariable [QEGVAR(medical,spo2), 97]) / 100
} else {
1 - 0.131 * GVAR(respiratoryRate) ^ 2 // Default AF oxygen saturation
};
2016-09-04 19:33:07 +00:00
// Calculate muscle damage increase
2024-06-22 17:53:08 +00:00
GVAR(muscleDamage) = GVAR(muscleDamage) + (_currentWork / GVAR(peakPower)) ^ 3.2 * MUSCLE_TEAR_RATE;
// Calculate muscle damage recovery
GVAR(muscleDamage) = 0 max (GVAR(muscleDamage) - MUSCLE_RECOVERY * GVAR(recoveryFactor)) min 1;
private _muscleIntegrity = 1 - GVAR(muscleDamage);
private _muscleFactor = sqrt _muscleIntegrity;
2016-09-04 19:33:07 +00:00
// Calculate available power
2024-06-22 17:53:08 +00:00
private _ae1PathwayPowerFatigued = GVAR(ae1PathwayPower) * sqrt (GVAR(ae1Reserve) / AE1_MAXRESERVE) * _oxygen * _muscleFactor;
private _ae2PathwayPowerFatigued = GVAR(ae2PathwayPower) * sqrt (GVAR(ae2Reserve) / AE2_MAXRESERVE) * _oxygen * _muscleFactor;
private _aePathwayPowerFatigued = _ae1PathwayPowerFatigued + _ae2PathwayPowerFatigued;
private _anPathwayPowerFatigued = GVAR(anPathwayPower) * sqrt (GVAR(anReserve) / AN_MAXRESERVE) * _oxygen * _muscleIntegrity;
2016-09-04 19:33:07 +00:00
// Calculate how much power is consumed from each reserve
private _ae1Power = _currentWork min _ae1PathwayPowerFatigued;
2024-06-22 17:53:08 +00:00
private _ae2Power = (_currentWork - _ae1Power) min _ae2PathwayPowerFatigued;
private _anPower = 0 max (_currentWork - _ae1Power - _ae2Power);
2016-09-04 19:33:07 +00:00
// Remove ATP from reserves for current work
2024-06-22 17:53:08 +00:00
GVAR(ae1Reserve) = 0 max (GVAR(ae1Reserve) - _ae1Power / GVAR(aeWattsPerATP));
GVAR(ae2Reserve) = 0 max (GVAR(ae2Reserve) - _ae2Power / GVAR(aeWattsPerATP));
GVAR(anReserve) = 0 max (GVAR(anReserve) - _anPower / GVAR(anWattsPerATP));
// Acidosis accumulation
GVAR(anFatigue) = GVAR(anFatigue) + _anPower * GVAR(maxPowerFatigueRatio) * 1.1;
2016-09-04 19:33:07 +00:00
// Aerobic ATP reserve recovery
2024-06-22 17:53:08 +00:00
GVAR(ae1Reserve) = (GVAR(ae1Reserve) + _oxygen * GVAR(recoveryFactor) * AE1_ATP_RECOVERY * (GVAR(ae1PathwayPower) - _ae1Power) / GVAR(ae1PathwayPower)) min AE1_MAXRESERVE;
GVAR(ae2Reserve) = (GVAR(ae2Reserve) + _oxygen * GVAR(recoveryFactor) * AE2_ATP_RECOVERY * (GVAR(ae2PathwayPower) - _ae2Power) / GVAR(ae2PathwayPower)) min AE2_MAXRESERVE;
private _aeSurplus = _ae1PathwayPowerFatigued + _ae2PathwayPowerFatigued - _ae1Power - _ae2Power;
2016-09-04 19:33:07 +00:00
2024-06-22 17:53:08 +00:00
// Anaerobic ATP reserve recovery
GVAR(anReserve) = 0 max (GVAR(anReserve) + _aeSurplus / GVAR(VO2MaxPower) * AN_ATP_RECOVERY * GVAR(recoveryFactor) * (GVAR(anFatigue) max linearConversion [AN_MAXRESERVE, 0, GVAR(anReserve), 0, 0.75, true]) ^ 2) min AN_MAXRESERVE; // max linearConversion ensures that if GVAR(anFatigue) is very low, it will still regenerate reserves
// Acidosis recovery
GVAR(anFatigue) = 0 max (GVAR(anFatigue) - _aeSurplus * GVAR(maxPowerFatigueRatio) * GVAR(recoveryFactor) * GVAR(anFatigue) ^ 2) min 1;
2016-09-04 19:33:07 +00:00
2024-06-22 17:53:08 +00:00
// Respiratory rate decrease
GVAR(respiratoryRate) = GVAR(respiratoryRate) * GVAR(respiratoryBufferDivisor);
2016-09-04 19:33:07 +00:00
2024-06-22 17:53:08 +00:00
// Respiratory rate increase
private _aePowerRatio = (GVAR(aePathwayPower) / _aePathwayPowerFatigued) min 2;
private _respiratorySampleDivisor = 1 / (RESPIRATORY_BUFFER * 4.72 * GVAR(VO2Max));
GVAR(respiratoryRate) = (GVAR(respiratoryRate) + _currentWork * _respiratorySampleDivisor * _aePowerRatio) min 1;
// Calculate a pseudo-perceived fatigue, which is used for effects
2024-02-07 20:50:18 +00:00
GVAR(aeReservePercentage) = (GVAR(ae1Reserve) / AE1_MAXRESERVE + GVAR(ae2Reserve) / AE2_MAXRESERVE) / 2;
GVAR(anReservePercentage) = GVAR(anReserve) / AN_MAXRESERVE;
private _perceivedFatigue = 1 - (GVAR(anReservePercentage) min GVAR(aeReservePercentage));
2016-09-04 19:33:07 +00:00
2024-06-22 17:53:08 +00:00
#ifdef DEBUG_MODE_FULL
systemChat format ["---- muscleDamage: %1 ----", GVAR(muscleDamage) toFixed 8];
systemChat format ["---- ae2: %1 - an: %2 ----", (GVAR(ae2Reserve) / AE2_MAXRESERVE) toFixed 2, (GVAR(anReserve) / AN_MAXRESERVE) toFixed 2];
systemChat format ["---- anFatigue: %1 - perceivedFatigue: %2 ----", GVAR(anFatigue) toFixed 2, _perceivedFatigue toFixed 2];
systemChat format ["---- velocity %1 - respiratoryRate: %2 ----", (vectorMagnitude _velocity) toFixed 2, GVAR(respiratoryRate) toFixed 2];
// systemChat format ["---- aePower: %1 ----", _aePathwayPowerFatigued toFixed 1];
#endif
[ACE_player, _perceivedFatigue, GVAR(anReserve) == 0, _fwdAngle, _sideAngle] call FUNC(handleEffects);
2016-09-04 19:33:07 +00:00
2024-08-22 17:13:54 +00:00
if (GVAR(enableStaminaBarRealized)) then {
2016-09-04 19:33:07 +00:00
[GVAR(anReserve) / AN_MAXRESERVE] call FUNC(handleStaminaBar);
};
2016-09-23 21:40:52 +00:00
2024-06-22 17:53:08 +00:00
[LINKFUNC(mainLoop), [], 1] call CBA_fnc_waitAndExecute;