From 70c8ab1e523e94ea06af0a28c197ebc220ac1d23 Mon Sep 17 00:00:00 2001 From: ulteq Date: Tue, 31 Oct 2017 21:47:08 +0100 Subject: [PATCH 1/5] Advanced Fatique - Various improvements * Fixes fatique calculation when walking across structures (bridges, ...) * Fixes the terrain gradient calculation * Fixes the 'wattsPerATP' constant * Implements the respiratory system * Prevents sprinting on very steep terrain * Adds more in-depth debug output * Closes #4492 --- addons/advanced_fatigue/XEH_postInit.sqf | 26 +++++++ .../functions/fnc_getAnimDuty.sqf | 2 +- .../functions/fnc_getMetabolicCosts.sqf | 47 ++++++++---- .../functions/fnc_handleEffects.sqf | 31 ++++---- .../functions/fnc_handlePlayerChanged.sqf | 16 +++- .../functions/fnc_mainLoop.sqf | 75 ++++++++++++------- addons/advanced_fatigue/script_component.hpp | 26 +++++-- 7 files changed, 154 insertions(+), 69 deletions(-) diff --git a/addons/advanced_fatigue/XEH_postInit.sqf b/addons/advanced_fatigue/XEH_postInit.sqf index 542360c0c36..2624d8d377b 100644 --- a/addons/advanced_fatigue/XEH_postInit.sqf +++ b/addons/advanced_fatigue/XEH_postInit.sqf @@ -1,6 +1,32 @@ #include "script_component.hpp" if (!hasInterface) exitWith {}; +#ifdef DEBUG_MODE_FULL +onEachFrame { + private _normal = surfaceNormal (getPosWorld ACE_player); + private _beg = (getPosWorld ACE_player) vectorAdd (_normal vectorMultiply 0.5); + private _end = _beg vectorAdd (_normal vectorMultiply 2.0); + drawLine3D [ASLToATL _beg, ASLToATL _end, [0,1,0,1]]; + + private _side = vectorNormalized (_normal vectorCrossProduct [0, 0, 1]); + private _end = _beg vectorAdd (_side vectorMultiply 2.0); + drawLine3D [ASLToATL _beg, ASLToATL _end, [0,0,1,1]]; + + private _up = vectorNormalized (_normal vectorCrossProduct _side); + private _end = _beg vectorAdd (_up vectorMultiply 2.0); + drawLine3D [ASLToATL _beg, ASLToATL _end, [1,0,0,1]]; + + private _movementVector = vectorNormalized (velocity ACE_player); + private _end = _beg vectorAdd (_movementVector vectorMultiply 2.0); + drawLine3D [ASLToATL _beg, ASLToATL _end, [1,1,0,1]]; + + private _sideVector = vectorNormalized (_movementVector vectorCrossProduct _normal); + _sideVector set[2, 0]; + private _end = _beg vectorAdd (_sideVector vectorMultiply 2.0); + drawLine3D [ASLToATL _beg, ASLToATL _end, [0,1,1,1]]; +}; +#endif + ["ace_settingsInitialized", { if (!GVAR(enabled)) exitWith {}; diff --git a/addons/advanced_fatigue/functions/fnc_getAnimDuty.sqf b/addons/advanced_fatigue/functions/fnc_getAnimDuty.sqf index 629152b6a10..2ddf01f045f 100644 --- a/addons/advanced_fatigue/functions/fnc_getAnimDuty.sqf +++ b/addons/advanced_fatigue/functions/fnc_getAnimDuty.sqf @@ -1,6 +1,6 @@ /* * Author: BaerMitUmlaut - * Calculates the duty of the current animation. + * Calculates the duty ('postureWeight') of the current animation. * * Arguments: * 0: Unit diff --git a/addons/advanced_fatigue/functions/fnc_getMetabolicCosts.sqf b/addons/advanced_fatigue/functions/fnc_getMetabolicCosts.sqf index c2c673fd95c..bd52b5ab829 100644 --- a/addons/advanced_fatigue/functions/fnc_getMetabolicCosts.sqf +++ b/addons/advanced_fatigue/functions/fnc_getMetabolicCosts.sqf @@ -6,24 +6,25 @@ * Arguments: * 0: Unit * 1: Speed + * 2: Forward Angle + * 3: Side Angle * * Return Value: * Metabolic cost * * Example: - * [player, 3.3] call ace_advanced_fatigue_fnc_getMetabolicCosts + * [player, 3.3, 0, 0] call ace_advanced_fatigue_fnc_getMetabolicCosts * * Public: No */ #include "script_component.hpp" -params ["_unit", "_velocity"]; +params ["_unit", "_speed", "_fwdAngle", "_sideAngle"]; private _gearMass = ((_unit getVariable [QEGVAR(movement,totalLoad), loadAbs _unit]) / 22.046) * GVAR(loadFactor); +private _terrainGradient = abs(_fwdAngle); +private _terrainFactor = 1; -private _terrainAngle = asin (1 - ((surfaceNormal getPosASL _unit) select 2)); -private _terrainGradient = (_terrainAngle / 45 min 1) * 5 * GVAR(terrainGradientFactor); private _duty = GVAR(animDuty); - { if (_x isEqualType 0) then { _duty = _duty * _x; @@ -32,20 +33,38 @@ private _duty = GVAR(animDuty); }; } forEach (GVAR(dutyList) select 1); -if (GVAR(isSwimming)) then { - _terrainGradient = 0; +if (!GVAR(isSwimming)) then { + if (_fwdAngle < 0) then { + _terrainGradient = 0.15 * _terrainGradient; + }; + if ((getPosATL _unit) select 2 < 0.01) then { + private _sideGradient = abs(_sideAngle / 45) min 1; + _terrainFactor = 1 + _sideGradient ^ 4; + }; }; -if (_velocity > 2) then { +if (_speed > 2) then { +#ifdef DEBUG_MODE_FULL + private _baseline = 2.10 * SIM_BODYMASS + 4 * (SIM_BODYMASS + _gearMass) * ((_gearMass / SIM_BODYMASS) ^ 2) + (SIM_BODYMASS + _gearMass) * 0.90 * (_speed ^ 2); + private _graded = 2.10 * SIM_BODYMASS + 4 * (SIM_BODYMASS + _gearMass) * ((_gearMass / SIM_BODYMASS) ^ 2) + _terrainFactor * (SIM_BODYMASS + _gearMass) * (0.90 * (_speed ^ 2) + 0.66 * _speed * _terrainGradient); + private _terrainImpact = abs((_graded / _baseline) - 1); + hintSilent format["FwdAngle: %1 | SideAngle: %2 \n TerrainFactor: %3 | TerrainGradient: %4 \n TerrainImpact: %5 \n Speed: %6 | CarriedLoad: %7 \n Duty: %8 | Work: %9", _fwdAngle toFixed 1, _sideAngle toFixed 1, _terrainFactor toFixed 2, _terrainGradient toFixed 1, _terrainImpact toFixed 2, _speed toFixed 2, _gearMass toFixed 1, _duty toFixed 2, round(_graded * BIOMECH_EFFICIENCY * _duty)]; +#endif ( 2.10 * SIM_BODYMASS - + 4 * (SIM_BODYMASS + _gearMass) * ((_gearMass / SIM_BODYMASS) ^ 2) - + (SIM_BODYMASS + _gearMass) * (0.90 * (_velocity ^ 2) + 0.66 * _velocity * _terrainGradient) - ) * 0.23 * _duty + + 4 * (SIM_BODYMASS + _gearMass) * (_gearMass / SIM_BODYMASS) ^ 2 + + _terrainFactor * (SIM_BODYMASS + _gearMass) * (0.90 * (_speed ^ 2) + 0.66 * _speed * _terrainGradient) + ) * BIOMECH_EFFICIENCY * _duty } else { +#ifdef DEBUG_MODE_FULL + private _baseline = 1.05 * SIM_BODYMASS + 2 * (SIM_BODYMASS + _gearMass) * ((_gearMass / SIM_BODYMASS) ^ 2) + (SIM_BODYMASS + _gearMass) * 1.15 * (_speed ^ 2); + private _graded = 1.05 * SIM_BODYMASS + 2 * (SIM_BODYMASS + _gearMass) * ((_gearMass / SIM_BODYMASS) ^ 2) + _terrainFactor * (SIM_BODYMASS + _gearMass) * (1.15 * (_speed ^ 2) + 0.66 * _speed * _terrainGradient); + private _terrainImpact = abs((_graded / _baseline) - 1); + hintSilent format["FwdAngle: %1 | SideAngle: %2 \n TerrainFactor: %3 | TerrainGradient: %4 \n TerrainImpact: %5 \n Speed: %6 | CarriedLoad: %7 \n Duty: %8 | Work: %9", _fwdAngle toFixed 1, _sideAngle toFixed 1, _terrainFactor toFixed 2, _terrainGradient toFixed 1, _terrainImpact toFixed 2, _speed toFixed 2, _gearMass toFixed 1, _duty toFixed 2, round(_graded * BIOMECH_EFFICIENCY * _duty)]; +#endif ( 1.05 * SIM_BODYMASS - + 4 * (SIM_BODYMASS + _gearMass) * ((_gearMass / SIM_BODYMASS) ^ 2) - + (SIM_BODYMASS + _gearMass) * (1.15 * (_velocity ^ 2) + 0.66 * _velocity * _terrainGradient) - ) * 0.23 * _duty + + 2 * (SIM_BODYMASS + _gearMass) * (_gearMass / SIM_BODYMASS) ^ 2 + + _terrainFactor * (SIM_BODYMASS + _gearMass) * (1.15 * (_speed ^ 2) + 0.66 * _speed * _terrainGradient) + ) * BIOMECH_EFFICIENCY * _duty }; diff --git a/addons/advanced_fatigue/functions/fnc_handleEffects.sqf b/addons/advanced_fatigue/functions/fnc_handleEffects.sqf index 111727c826e..8cb4c51b640 100644 --- a/addons/advanced_fatigue/functions/fnc_handleEffects.sqf +++ b/addons/advanced_fatigue/functions/fnc_handleEffects.sqf @@ -6,32 +6,29 @@ * 0: Unit * 1: Fatigue * 2: Speed - * 3: Overexhausted + * 3: Respiratory Rate + * 4: Forward Angle + * 5: Side Angle * * Return Value: * None * * Example: - * [_player, 0.5, 3.3, true] call ace_advanced_fatigue_fnc_handleEffects + * [_player, 0.5, 3.3, true, 0, 0] call ace_advanced_fatigue_fnc_handleEffects * * Public: No */ #include "script_component.hpp" -params ["_unit", "_fatigue", "_speed", "_overexhausted"]; - -#ifdef DEBUG_MODE_FULL - systemChat str _fatigue; - systemChat str vectorMagnitude velocity _unit; -#endif +params ["_unit", "_fatigue", "_speed", "_respiratoryRate", "_fwdAngle", "_sideAngle"]; // - Audible effects ---------------------------------------------------------- GVAR(lastBreath) = GVAR(lastBreath) + 1; -if (_fatigue > 0.4 && {GVAR(lastBreath) > (_fatigue * -10 + 9)} && {!underwater _unit}) then { +if (_respiratoryRate > 0.4 && {GVAR(lastBreath) > (_respiratoryRate * -10 + 9)} && {!underwater _unit}) then { switch (true) do { - case (_fatigue < 0.6): { + case (_respiratoryRate < 0.6): { playSound (QGVAR(breathLow) + str(floor random 6)); }; - case (_fatigue < 0.85): { + case (_respiratoryRate < 0.85): { playSound (QGVAR(breathMid) + str(floor random 6)); }; default { @@ -60,10 +57,10 @@ if (GVAR(ppeBlackoutLast) == 1) then { if (GVAR(isSwimming)) exitWith { _unit setAnimSpeedCoef linearConversion [0.7, 0.9, _fatigue, 1, 0.5, true]; - if ((isSprintAllowed _unit) && {_fatigue > 0.7}) then { + if ((isSprintAllowed _unit) && _fatigue > 0.7) then { [_unit, "blockSprint", QUOTE(ADDON), true] call EFUNC(common,statusEffect_set); } else { - if ((!isSprintAllowed _unit) && {_fatigue < 0.7}) then { + if ((!isSprintAllowed _unit) && _fatigue < 0.7) then { [_unit, "blockSprint", QUOTE(ADDON), false] call EFUNC(common,statusEffect_set); }; }; @@ -72,16 +69,16 @@ if ((getAnimSpeedCoef _unit) != 1) then { _unit setAnimSpeedCoef 1; }; -if (_overexhausted) then { +if (_fatigue >= 1) then { [_unit, "forceWalk", QUOTE(ADDON), true] call EFUNC(common,statusEffect_set); } else { - if (isForcedWalk _unit && {_fatigue < 0.7}) then { + if (isForcedWalk _unit && _fatigue < 0.8) then { [_unit, "forceWalk", QUOTE(ADDON), false] call EFUNC(common,statusEffect_set); } else { - if ((isSprintAllowed _unit) && {_fatigue > 0.7}) then { + if ((isSprintAllowed _unit) && (_fatigue > 0.7 || abs(_fwdAngle) > 20 || abs(_sideAngle) > 20)) then { [_unit, "blockSprint", QUOTE(ADDON), true] call EFUNC(common,statusEffect_set); } else { - if ((!isSprintAllowed _unit) && {_fatigue < 0.6}) then { + if ((!isSprintAllowed _unit) && _fatigue < 0.6 && abs(_fwdAngle) < 20 && abs(_sideAngle) < 20) then { [_unit, "blockSprint", QUOTE(ADDON), false] call EFUNC(common,statusEffect_set); }; }; diff --git a/addons/advanced_fatigue/functions/fnc_handlePlayerChanged.sqf b/addons/advanced_fatigue/functions/fnc_handlePlayerChanged.sqf index 0e31e896451..f30f12fa58f 100644 --- a/addons/advanced_fatigue/functions/fnc_handlePlayerChanged.sqf +++ b/addons/advanced_fatigue/functions/fnc_handlePlayerChanged.sqf @@ -29,6 +29,7 @@ if !(isNull _oldUnit) then { _oldUnit setVariable [QGVAR(anReserve), GVAR(anReserve)]; _oldUnit setVariable [QGVAR(anFatigue), GVAR(anFatigue)]; _oldUnit setVariable [QGVAR(muscleDamage), GVAR(muscleDamage)]; + _oldUnit setVariable [QGVAR(respiratoryRate), GVAR(respiratoryRate)]; }; _newUnit enableStamina false; @@ -47,18 +48,25 @@ GVAR(ae2Reserve) = _newUnit getVariable [QGVAR(ae2Reserve), AE2_MAXRESERVE] GVAR(anReserve) = _newUnit getVariable [QGVAR(anReserve), AN_MAXRESERVE]; GVAR(anFatigue) = _newUnit getVariable [QGVAR(anFatigue), 0]; GVAR(muscleDamage) = _newUnit getVariable [QGVAR(muscleDamage), 0]; +GVAR(respiratoryRate) = _newUnit getVariable [QGVAR(respiratoryRate), 0]; // Clean variables for respawning units { _newUnit setVariable [_x, nil]; -} forEach [QGVAR(ae1Reserve), QGVAR(ae2Reserve), QGVAR(anReserve), QGVAR(anFatigue), QGVAR(muscleDamage)]; +} forEach [QGVAR(ae1Reserve), QGVAR(ae2Reserve), QGVAR(anReserve), QGVAR(anFatigue), QGVAR(muscleDamage), QGVAR(respiratoryRate)]; GVAR(VO2Max) = 35 + 20 * (_newUnit getVariable [QGVAR(performanceFactor), GVAR(performanceFactor)]); -GVAR(VO2MaxPower) = GVAR(VO2Max) * SIM_BODYMASS * 0.23 * JOULES_PER_ML_O2 / 60; +GVAR(VO2MaxPower) = GVAR(VO2Max) * SIM_BODYMASS * BIOMECH_EFFICIENCY * JOULES_PER_ML_O2 / 60; GVAR(peakPower) = VO2MAX_STRENGTH * GVAR(VO2MaxPower); -GVAR(ae1PathwayPower) = GVAR(peakPower) / (13.3 + 16.7 + 113.3) * 13.3 * ANTPERCENT ^ 1.28 * 1.362; -GVAR(ae2PathwayPower) = GVAR(peakPower) / (13.3 + 16.7 + 113.3) * 16.7 * ANTPERCENT ^ 1.28 * 1.362; +GVAR(ae1PathwayPower) = GVAR(peakPower) / (AE1_ATP_RELEASE_RATE + AE2_ATP_RELEASE_RATE + AN_ATP_RELEASE_RATE) * AE1_ATP_RELEASE_RATE * ANTPERCENT ^ 1.28 * 1.362; +GVAR(ae2PathwayPower) = GVAR(peakPower) / (AE1_ATP_RELEASE_RATE + AE2_ATP_RELEASE_RATE + AN_ATP_RELEASE_RATE) * AE2_ATP_RELEASE_RATE * ANTPERCENT ^ 1.28 * 1.362; +GVAR(aePathwayPower) = GVAR(ae1PathwayPower) + GVAR(ae2PathwayPower); + +GVAR(wattsPerATP) = GVAR(ae1PathwayPower) / AE1_ATP_RELEASE_RATE; + +GVAR(respiratoryBufferDivisor) = (RESPIRATORY_BUFFER - 1) / RESPIRATORY_BUFFER; +GVAR(maxPowerFatigueRatio) = 0.057 / GVAR(peakPower); GVAR(ppeBlackoutLast) = 100; GVAR(lastBreath) = 0; diff --git a/addons/advanced_fatigue/functions/fnc_mainLoop.sqf b/addons/advanced_fatigue/functions/fnc_mainLoop.sqf index 4b23153dff9..4b566741f05 100644 --- a/addons/advanced_fatigue/functions/fnc_mainLoop.sqf +++ b/addons/advanced_fatigue/functions/fnc_mainLoop.sqf @@ -14,6 +14,7 @@ * Public: No */ #include "script_component.hpp" + if (!alive ACE_player) exitWith { // Dead people don't breath, Will also handle null (Map intros) [FUNC(mainLoop), [], 1] call CBA_fnc_waitAndExecute; private _staminaBarContainer = uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull]; @@ -21,6 +22,12 @@ if (!alive ACE_player) exitWith { // Dead people don't breath, Will also handle _staminaBarContainer ctrlCommit 1; }; +private _normal = surfaceNormal (getPosWorld ACE_player); +private _movementVector = vectorNormalized (velocity ACE_player); +private _sideVector = vectorNormalized (_movementVector vectorCrossProduct _normal); +private _fwdAngle = asin (_movementVector select 2) * GVAR(terrainGradientFactor); +private _sideAngle = asin (_sideVector select 2) * GVAR(terrainGradientFactor); + private _currentWork = REE; private _currentSpeed = (vectorMagnitude (velocity ACE_player)) min 6; @@ -30,49 +37,65 @@ if (GVAR(isProne)) then { }; if ((vehicle ACE_player == ACE_player) && {_currentSpeed > 0.1} && {isTouchingGround ACE_player || {underwater ACE_player}}) then { - _currentWork = [ACE_player, _currentSpeed] call FUNC(getMetabolicCosts); + _currentWork = [ACE_player, _currentSpeed, _fwdAngle, _sideAngle] call FUNC(getMetabolicCosts); _currentWork = _currentWork max REE; }; +// Oxygen calculation +private _oxygen = 1 - 0.126 * GVAR(respiratoryRate) ^ 2; + // Calculate muscle damage increase -// Note: Muscle damage recovery is ignored as it takes multiple days -GVAR(muscleDamage) = GVAR(muscleDamage) + (_currentWork / GVAR(peakPower)) ^ 3.2 * 0.00004; -private _muscleIntegritySqrt = sqrt (1 - GVAR(muscleDamage)); +GVAR(muscleDamage) = (GVAR(muscleDamage) + (_currentWork / GVAR(peakPower)) ^ 3.2 * MUSCLE_TEAR_RATE) min 1; +// Calculate muscle damage decrease +GVAR(muscleDamage) = 0 max (GVAR(muscleDamage) - MUSCLE_RECOVERY); +private _muscleIntegrity = 1 - GVAR(muscleDamage); +private _muscleFactor = sqrt (_muscleIntegrity); // Calculate available power -private _ae1PathwayPowerFatigued = GVAR(ae1PathwayPower) * sqrt (GVAR(ae1Reserve) / AE1_MAXRESERVE) * OXYGEN * _muscleIntegritySqrt; -private _ae2PathwayPowerFatigued = GVAR(ae2PathwayPower) * sqrt (GVAR(ae2Reserve) / AE2_MAXRESERVE) * OXYGEN * _muscleIntegritySqrt; +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; // Calculate how much power is consumed from each reserve private _ae1Power = _currentWork min _ae1PathwayPowerFatigued; -private _ae2Power = ((_currentWork - _ae1Power) max 0) min _ae2PathwayPowerFatigued; -private _anPower = (_currentWork - _ae1Power - _ae2Power) max 0; +private _ae2Power = (_currentWork - _ae1Power) min _ae2PathwayPowerFatigued; +private _anPower = 0 max (_currentWork - _ae1Power - _ae2Power); // Remove ATP from reserves for current work -GVAR(ae1Reserve) = GVAR(ae1Reserve) - _ae1Power / WATTSPERATP; -GVAR(ae2Reserve) = GVAR(ae2Reserve) - _ae2Power / WATTSPERATP; -GVAR(anReserve) = GVAR(anReserve) - _anPower / WATTSPERATP; -// Increase anearobic fatigue -GVAR(anFatigue) = GVAR(anFatigue) + _anPower * (0.057 / GVAR(peakPower)) * 1.1; +GVAR(ae1Reserve) = 0 max (GVAR(ae1Reserve) - _ae1Power / GVAR(wattsPerATP)); +GVAR(ae2Reserve) = 0 max (GVAR(ae2Reserve) - _ae2Power / GVAR(wattsPerATP)); +GVAR(anReserve) = 0 max (GVAR(anReserve) - _anPower / GVAR(wattsPerATP)); +#ifdef DEBUG_MODE_FULL +systemChat format["---- ae2: %1 ----", (GVAR(ae2Reserve) / AE2_MAXRESERVE) toFixed 2]; +systemChat format["---- an: %1 ----", (GVAR(anReserve) / AN_MAXRESERVE) toFixed 2]; +systemChat format["---- anFatigue: %1 ----", GVAR(anFatigue) toFixed 2]; +systemChat format["---- muscleDamage: %1 ----", GVAR(muscleDamage) toFixed 2]; +systemChat format["---- respiratoryRate: %1 ----", GVAR(respiratoryRate) toFixed 2]; +systemChat format["---- aePower: %1 ----", _aePathwayPowerFatigued toFixed 1]; +#endif + +// Acidosis accumulation +GVAR(anFatigue) = GVAR(anFatigue) + _anPower * GVAR(maxPowerFatigueRatio) * 1.1; // Aerobic ATP reserve recovery -GVAR(ae1Reserve) = ((GVAR(ae1Reserve) + OXYGEN * 6.60 * (GVAR(ae1PathwayPower) - _ae1Power) / GVAR(ae1PathwayPower) * GVAR(recoveryFactor)) min AE1_MAXRESERVE) max 0; -GVAR(ae2Reserve) = ((GVAR(ae2Reserve) + OXYGEN * 5.83 * (GVAR(ae2PathwayPower) - _ae2Power) / GVAR(ae2PathwayPower) * GVAR(recoveryFactor)) min AE2_MAXRESERVE) max 0; +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; -// Anaerobic ATP reserver and fatigue recovery -GVAR(anReserve) = ((GVAR(anReserve) - + (_ae1PathwayPowerFatigued + _ae2PathwayPowerFatigued - _ae1Power - _ae2Power) / GVAR(VO2MaxPower) * 56.7 * GVAR(anFatigue) ^ 2 * GVAR(recoveryFactor) -) min AN_MAXRESERVE) max 0; +private _aeSurplus = _ae1PathwayPowerFatigued + _ae2PathwayPowerFatigued - _ae1Power - _ae2Power; +// Anaerobic ATP reserve recovery +GVAR(anReserve) = (GVAR(anReserve) + _aeSurplus / GVAR(VO2MaxPower) * AN_ATP_RECOVERY * GVAR(recoveryFactor) * GVAR(anFatigue) ^ 2) min AN_MAXRESERVE; +// Acidosis recovery +GVAR(anFatigue) = 0 max (GVAR(anFatigue) - _aeSurplus * GVAR(maxPowerFatigueRatio) * GVAR(recoveryFactor) * GVAR(anFatigue) ^ 2) min 1; -GVAR(anFatigue) = ((GVAR(anFatigue) - - (_ae1PathwayPowerFatigued + _ae2PathwayPowerFatigued - _ae1Power - _ae2Power) * (0.057 / GVAR(peakPower)) * GVAR(anFatigue) ^ 2 * GVAR(recoveryFactor) -) min 1) max 0; +// Respiratory rate decrease +GVAR(respiratoryRate) = GVAR(respiratoryRate) * GVAR(respiratoryBufferDivisor); -private _aeReservePercentage = (GVAR(ae1Reserve) / AE1_MAXRESERVE + GVAR(ae2Reserve) / AE2_MAXRESERVE) / 2; -private _anReservePercentage = GVAR(anReserve) / AN_MAXRESERVE; -private _perceivedFatigue = 1 - (_anReservePercentage min _aeReservePercentage); +// Respiratory rate increase +private _aePowerRatio = (GVAR(aePathwayPower) / _aePathwayPowerFatigued) min 2; +private _respiratorySampleDivisor = 1 / (RESPIRATORY_BUFFER * 4.75 * GVAR(VO2Max)); +GVAR(respiratoryRate) = (GVAR(respiratoryRate) + _currentWork * _respiratorySampleDivisor * _aePowerRatio) min 1; -[ACE_player, _perceivedFatigue, _currentSpeed, GVAR(anReserve) == 0] call FUNC(handleEffects); +[ACE_player, GVAR(anFatigue), _currentSpeed, GVAR(respiratoryRate), _fwdAngle, _sideAngle] call FUNC(handleEffects); if (GVAR(enableStaminaBar)) then { [GVAR(anReserve) / AN_MAXRESERVE] call FUNC(handleStaminaBar); diff --git a/addons/advanced_fatigue/script_component.hpp b/addons/advanced_fatigue/script_component.hpp index fcc8abd3c06..09415e5af5b 100644 --- a/addons/advanced_fatigue/script_component.hpp +++ b/addons/advanced_fatigue/script_component.hpp @@ -2,7 +2,7 @@ #define COMPONENT_BEAUTIFIED Advanced Fatigue #include "\z\ace\addons\main\script_mod.hpp" -// #define DEBUG_MODE_FULL +#define DEBUG_MODE_FULL // #define DISABLE_COMPILE_CACHE // #define ENABLE_PERFORMANCE_COUNTERS @@ -20,10 +20,22 @@ #define SIM_BODYMASS 70 #define JOULES_PER_ML_O2 20.9 #define VO2MAX_STRENGTH 4.1 -#define REE 18.83 //((0.5617 * SIM_BODYMASS + 42.57) * 0.23) -#define OXYGEN 0.9 -#define WATTSPERATP 7 +#define BIOMECH_EFFICIENCY 0.23 +#define REE 18.83 //((0.5617 * SIM_BODYMASS + 42.57) * BIOMECH_EFFICIENCY) -#define AE1_MAXRESERVE 4000000 -#define AE2_MAXRESERVE 84000 -#define AN_MAXRESERVE 2300 +#define RESPIRATORY_BUFFER 60 + +#define MUSCLE_TEAR_RATE 0.004 +#define MUSCLE_RECOVERY 0.00000386 + +#define AE1_ATP_RELEASE_RATE 13.3 // mmol +#define AE2_ATP_RELEASE_RATE 16.7 // mmol +#define AN_ATP_RELEASE_RATE 113.3 // mmol + +#define AE1_ATP_RECOVERY 6.60 // mmol +#define AE2_ATP_RECOVERY 5.83 // mmol +#define AN_ATP_RECOVERY 56.70 // mmol + +#define AE1_MAXRESERVE 4000000 // mmol +#define AE2_MAXRESERVE 84000 // mmol +#define AN_MAXRESERVE 2300 // mmol From ea291715d5c382489f2040513cc07120c710d074 Mon Sep 17 00:00:00 2001 From: ulteq Date: Mon, 6 Nov 2017 11:03:09 +0100 Subject: [PATCH 2/5] Remove regular cloth weight from the carried load calculation. --- addons/advanced_fatigue/functions/fnc_getMetabolicCosts.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/advanced_fatigue/functions/fnc_getMetabolicCosts.sqf b/addons/advanced_fatigue/functions/fnc_getMetabolicCosts.sqf index bd52b5ab829..c1245b956d8 100644 --- a/addons/advanced_fatigue/functions/fnc_getMetabolicCosts.sqf +++ b/addons/advanced_fatigue/functions/fnc_getMetabolicCosts.sqf @@ -20,7 +20,7 @@ #include "script_component.hpp" params ["_unit", "_speed", "_fwdAngle", "_sideAngle"]; -private _gearMass = ((_unit getVariable [QEGVAR(movement,totalLoad), loadAbs _unit]) / 22.046) * GVAR(loadFactor); +private _gearMass = 0 max (((_unit getVariable [QEGVAR(movement,totalLoad), loadAbs _unit]) / 22.046 - 3.5) * GVAR(loadFactor)); private _terrainGradient = abs(_fwdAngle); private _terrainFactor = 1; From 82000cedec6298f025844f8998f86fa2fc1349db Mon Sep 17 00:00:00 2001 From: ulteq Date: Mon, 6 Nov 2017 19:04:06 +0100 Subject: [PATCH 3/5] Split 'wattsPerATP' calculation into two --- .../advanced_fatigue/functions/fnc_handlePlayerChanged.sqf | 6 ++++-- addons/advanced_fatigue/functions/fnc_mainLoop.sqf | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/addons/advanced_fatigue/functions/fnc_handlePlayerChanged.sqf b/addons/advanced_fatigue/functions/fnc_handlePlayerChanged.sqf index f30f12fa58f..aa3786b3071 100644 --- a/addons/advanced_fatigue/functions/fnc_handlePlayerChanged.sqf +++ b/addons/advanced_fatigue/functions/fnc_handlePlayerChanged.sqf @@ -62,8 +62,10 @@ GVAR(peakPower) = VO2MAX_STRENGTH * GVAR(VO2MaxPower); GVAR(ae1PathwayPower) = GVAR(peakPower) / (AE1_ATP_RELEASE_RATE + AE2_ATP_RELEASE_RATE + AN_ATP_RELEASE_RATE) * AE1_ATP_RELEASE_RATE * ANTPERCENT ^ 1.28 * 1.362; GVAR(ae2PathwayPower) = GVAR(peakPower) / (AE1_ATP_RELEASE_RATE + AE2_ATP_RELEASE_RATE + AN_ATP_RELEASE_RATE) * AE2_ATP_RELEASE_RATE * ANTPERCENT ^ 1.28 * 1.362; GVAR(aePathwayPower) = GVAR(ae1PathwayPower) + GVAR(ae2PathwayPower); - -GVAR(wattsPerATP) = GVAR(ae1PathwayPower) / AE1_ATP_RELEASE_RATE; +GVAR(anPathwayPower) = GVAR(peakPower) - GVAR(aePathwayPower); + +GVAR(aeWattsPerATP) = GVAR(ae1PathwayPower) / AE1_ATP_RELEASE_RATE; +GVAR(anWattsPerATP) = GVAR(anPathwayPower) / AN_ATP_RELEASE_RATE; GVAR(respiratoryBufferDivisor) = (RESPIRATORY_BUFFER - 1) / RESPIRATORY_BUFFER; GVAR(maxPowerFatigueRatio) = 0.057 / GVAR(peakPower); diff --git a/addons/advanced_fatigue/functions/fnc_mainLoop.sqf b/addons/advanced_fatigue/functions/fnc_mainLoop.sqf index 4b566741f05..70321d2f24b 100644 --- a/addons/advanced_fatigue/functions/fnc_mainLoop.sqf +++ b/addons/advanced_fatigue/functions/fnc_mainLoop.sqf @@ -62,9 +62,9 @@ private _ae2Power = (_currentWork - _ae1Power) min _ae2PathwayPowerFatigued; private _anPower = 0 max (_currentWork - _ae1Power - _ae2Power); // Remove ATP from reserves for current work -GVAR(ae1Reserve) = 0 max (GVAR(ae1Reserve) - _ae1Power / GVAR(wattsPerATP)); -GVAR(ae2Reserve) = 0 max (GVAR(ae2Reserve) - _ae2Power / GVAR(wattsPerATP)); -GVAR(anReserve) = 0 max (GVAR(anReserve) - _anPower / GVAR(wattsPerATP)); +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)); #ifdef DEBUG_MODE_FULL systemChat format["---- ae2: %1 ----", (GVAR(ae2Reserve) / AE2_MAXRESERVE) toFixed 2]; systemChat format["---- an: %1 ----", (GVAR(anReserve) / AN_MAXRESERVE) toFixed 2]; From 00305eb126c32c0cca985d42131974b8dd5d5ffa Mon Sep 17 00:00:00 2001 From: ulteq Date: Mon, 6 Nov 2017 19:19:59 +0100 Subject: [PATCH 4/5] Oxygen tweak --- addons/advanced_fatigue/functions/fnc_mainLoop.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/advanced_fatigue/functions/fnc_mainLoop.sqf b/addons/advanced_fatigue/functions/fnc_mainLoop.sqf index 70321d2f24b..88a7fc30cc9 100644 --- a/addons/advanced_fatigue/functions/fnc_mainLoop.sqf +++ b/addons/advanced_fatigue/functions/fnc_mainLoop.sqf @@ -42,7 +42,7 @@ if ((vehicle ACE_player == ACE_player) && {_currentSpeed > 0.1} && {isTouchingGr }; // Oxygen calculation -private _oxygen = 1 - 0.126 * GVAR(respiratoryRate) ^ 2; +private _oxygen = 1 - 0.1315 * GVAR(respiratoryRate) ^ 2; // Calculate muscle damage increase GVAR(muscleDamage) = (GVAR(muscleDamage) + (_currentWork / GVAR(peakPower)) ^ 3.2 * MUSCLE_TEAR_RATE) min 1; From 18661ebe02014d6f2c91350e341cf01eeddc8f0e Mon Sep 17 00:00:00 2001 From: ulteq Date: Tue, 7 Nov 2017 21:05:44 +0100 Subject: [PATCH 5/5] Experimental movement speed limiter --- addons/advanced_fatigue/XEH_PREP.hpp | 1 + .../functions/fnc_getMaximumSpeed.sqf | 93 +++++++++++++++++++ .../functions/fnc_handleEffects.sqf | 40 +++++--- .../functions/fnc_handlePlayerChanged.sqf | 1 + .../functions/fnc_mainLoop.sqf | 36 +++++-- 5 files changed, 150 insertions(+), 21 deletions(-) create mode 100644 addons/advanced_fatigue/functions/fnc_getMaximumSpeed.sqf diff --git a/addons/advanced_fatigue/XEH_PREP.hpp b/addons/advanced_fatigue/XEH_PREP.hpp index c6250516f5d..65558fb4e66 100644 --- a/addons/advanced_fatigue/XEH_PREP.hpp +++ b/addons/advanced_fatigue/XEH_PREP.hpp @@ -1,6 +1,7 @@ PREP(addDutyFactor); PREP(createStaminaBar); PREP(getAnimDuty); +PREP(getMaximumSpeed); PREP(getMetabolicCosts); PREP(handleEffects); PREP(handlePlayerChanged); diff --git a/addons/advanced_fatigue/functions/fnc_getMaximumSpeed.sqf b/addons/advanced_fatigue/functions/fnc_getMaximumSpeed.sqf new file mode 100644 index 00000000000..9531c7b9567 --- /dev/null +++ b/addons/advanced_fatigue/functions/fnc_getMaximumSpeed.sqf @@ -0,0 +1,93 @@ +/* + * Author: BaerMitUmlaut, Ruthberg + * Calculates the current maximum movement speed for a unit. + * Calculation is done according to the Pandolf/Wojtowicz formulas. + * + * Arguments: + * 0: Unit + * 1: Power + * 2: Forward Angle + * 3: Side Angle + * + * Return Value: + * Speed + * + * Example: + * [player, 840, 0, 0] call ace_advanced_fatigue_fnc_getMaximumSpeed + * + * Public: No + */ +#include "script_component.hpp" +params ["_unit", "_power", "_fwdAngle", "_sideAngle"]; + +private _gearMass = 0 max (((_unit getVariable [QEGVAR(movement,totalLoad), loadAbs _unit]) / 22.046 - 3.5) * GVAR(loadFactor)); +private _terrainGradient = abs(_fwdAngle); +private _terrainFactor = 1; + +private _duty = GVAR(animDuty); +{ + if (_x isEqualType 0) then { + _duty = _duty * _x; + } else { + _duty = _duty * (_unit call _x); + }; +} forEach (GVAR(dutyList) select 1); + +if (!GVAR(isSwimming)) then { + if (_fwdAngle < 0) then { + _terrainGradient = 0.15 * _terrainGradient; + }; + if ((getPosATL _unit) select 2 < 0.01) then { + private _sideGradient = abs(_sideAngle / 45) min 1; + _terrainFactor = 1 + _sideGradient ^ 4; + }; +}; + +private _fnc_getRunningSpeed = { + params ["_power", "_gearMass", "_terrainFactor", "_terrainGradient", "_duty"]; + private _p = _power / 0.23; + _p = _p - 2.10 * SIM_BODYMASS; + _p = _p - 4 * (SIM_BODYMASS + _gearMass) * (_gearMass / SIM_BODYMASS) ^ 2; + _p = _p / (_terrainFactor * (SIM_BODYMASS + _gearMass)); + (1 / 30 * (-11 * _terrainGradient + sqrt(121 * (_terrainGradient ^ 2) + 1000 * _p))) +}; + +private _fnc_getWalkingSpeed = { + params ["_power", "_gearMass", "_terrainFactor", "_terrainGradient", "_duty"]; + private _p = _power / 0.23; + _p = _p - 1.05 * SIM_BODYMASS; + _p = _p - 2 * (SIM_BODYMASS + _gearMass) * (_gearMass / SIM_BODYMASS) ^ 2; + _p = _p / (_terrainFactor * (SIM_BODYMASS + _gearMass)); + (1 / 115 * (-33 * _terrainGradient + sqrt(1089 * (_terrainGradient ^ 2) + 11500 * _p))) +}; + +/* +private _fnc_getRunningSpeed = { + params ["_power", "_gearMass", "_terrainFactor", "_terrainGradient", "_duty"]; + private _numerator = 3.6 * - (_power / (0.23 * _duty)); + _numerator = _numerator - 2.1 * SIM_BODYMASS; + _numerator = _numerator - 4 * (_gearMass + SIM_BODYMASS) * (_gearMass / SIM_BODYMASS) ^ 2; + private _denominator = _terrainFactor * (_gearMass + SIM_BODYMASS); + private _radicand = ((0.66 * _terrainGradient) ^ 2) - _numerator / _denominator; + private _v = -0.66 * _terrainGradient + sqrt(_radicand); + _v / 1.8 +}; + +private _fnc_getWalkingSpeed = { + params ["_power", "_gearMass", "_terrainFactor", "_terrainGradient", "_duty"]; + private _numerator = 4.6 * - (_power / (0.23 * _duty)); + _numerator = _numerator - 1.05 * SIM_BODYMASS; + _numerator = _numerator - 2 * (_gearMass + SIM_BODYMASS) * (_gearMass / SIM_BODYMASS) ^ 2; + private _denominator = _terrainFactor * (_gearMass + SIM_BODYMASS); + private _radicand = ((0.66 * _terrainGradient) ^ 2) - _numerator / _denominator; + private _v = -0.66 * _terrainGradient + sqrt(_radicand); + _v / 2.3 +}; +*/ + +private _speed = [_power, _gearMass, _terrainFactor, _terrainGradient, _duty] call _fnc_getRunningSpeed; +if (_speed < 2) then { + _speed = [_power, _gearMass, _terrainFactor, _terrainGradient, _duty] call _fnc_getWalkingSpeed; +}; + +_speed diff --git a/addons/advanced_fatigue/functions/fnc_handleEffects.sqf b/addons/advanced_fatigue/functions/fnc_handleEffects.sqf index 8cb4c51b640..fdcd9de6110 100644 --- a/addons/advanced_fatigue/functions/fnc_handleEffects.sqf +++ b/addons/advanced_fatigue/functions/fnc_handleEffects.sqf @@ -5,10 +5,12 @@ * Arguments: * 0: Unit * 1: Fatigue - * 2: Speed - * 3: Respiratory Rate - * 4: Forward Angle - * 5: Side Angle + * 2: Respiratory Rate + * 3: Current Speed + * 4: Max Run Speed + * 5: Max Sprint Speed + * 6: Forward Angle + * 7: Side Angle * * Return Value: * None @@ -19,7 +21,7 @@ * Public: No */ #include "script_component.hpp" -params ["_unit", "_fatigue", "_speed", "_respiratoryRate", "_fwdAngle", "_sideAngle"]; +params ["_unit", "_fatigue", "_respiratoryRate", "_currentSpeed", "_maxRunSpeed", "_maxSprintSpeed", "_fwdAngle", "_sideAngle"]; // - Audible effects ---------------------------------------------------------- GVAR(lastBreath) = GVAR(lastBreath) + 1; @@ -65,20 +67,36 @@ if (GVAR(isSwimming)) exitWith { }; }; }; -if ((getAnimSpeedCoef _unit) != 1) then { - _unit setAnimSpeedCoef 1; + +private _currentAnimCoef = getAnimSpeedCoef _unit; +if (_currentSpeed > 0.1) then { + if (_currentSpeed > 4 * _currentAnimCoef || _currentSpeed > GVAR(lastSpeed) + 0.8) then { + _unit setAnimSpeedCoef (0.70 max (_currentAnimCoef * ((_maxSprintSpeed / _currentSpeed) ^ 0.5)) min 1.0); + } else { + if (isForcedWalk _unit) then { + _unit setAnimSpeedCoef (0.80 max (_currentAnimCoef * ((_maxRunSpeed / _currentSpeed) ^ 0.5)) min 1.2); + } else { + _unit setAnimSpeedCoef (0.70 max (_currentAnimCoef * ((_maxRunSpeed / _currentSpeed) ^ 0.5)) min 1.0); + }; + }; + GVAR(lastSpeed) = _currentSpeed; }; -if (_fatigue >= 1) then { +if (!isForcedWalk _unit && {_fatigue >= 1 || (_maxRunSpeed < 2.4 && _currentSpeed < 2.5 && _currentAnimCoef < 0.75)}) then { + _unit setAnimSpeedCoef 1.2; [_unit, "forceWalk", QUOTE(ADDON), true] call EFUNC(common,statusEffect_set); } else { - if (isForcedWalk _unit && _fatigue < 0.8) then { + if (isForcedWalk _unit && {_fatigue < 0.90 && _maxRunSpeed > 2.5}) then { + if (!isWalking _unit) then { + _unit setAnimSpeedCoef 0.90; + }; [_unit, "forceWalk", QUOTE(ADDON), false] call EFUNC(common,statusEffect_set); } else { - if ((isSprintAllowed _unit) && (_fatigue > 0.7 || abs(_fwdAngle) > 20 || abs(_sideAngle) > 20)) then { + if (isSprintAllowed _unit && {abs(_fwdAngle) > 20 || abs(_sideAngle) > 20 || (_maxSprintSpeed < 3.5 && _currentAnimCoef < 0.85)}) then { + _unit setAnimSpeedCoef 1; [_unit, "blockSprint", QUOTE(ADDON), true] call EFUNC(common,statusEffect_set); } else { - if ((!isSprintAllowed _unit) && _fatigue < 0.6 && abs(_fwdAngle) < 20 && abs(_sideAngle) < 20) then { + if (!isSprintAllowed _unit && {_fatigue < 0.8 && {abs(_fwdAngle) < 20 && abs(_sideAngle) < 20 && _maxSprintSpeed > 4.5}}) then { [_unit, "blockSprint", QUOTE(ADDON), false] call EFUNC(common,statusEffect_set); }; }; diff --git a/addons/advanced_fatigue/functions/fnc_handlePlayerChanged.sqf b/addons/advanced_fatigue/functions/fnc_handlePlayerChanged.sqf index aa3786b3071..cba215f2607 100644 --- a/addons/advanced_fatigue/functions/fnc_handlePlayerChanged.sqf +++ b/addons/advanced_fatigue/functions/fnc_handlePlayerChanged.sqf @@ -69,6 +69,7 @@ GVAR(anWattsPerATP) = GVAR(anPathwayPower) / AN_ATP_RELEASE_RATE; GVAR(respiratoryBufferDivisor) = (RESPIRATORY_BUFFER - 1) / RESPIRATORY_BUFFER; GVAR(maxPowerFatigueRatio) = 0.057 / GVAR(peakPower); +GVAR(lastSpeed) = 0; GVAR(ppeBlackoutLast) = 100; GVAR(lastBreath) = 0; diff --git a/addons/advanced_fatigue/functions/fnc_mainLoop.sqf b/addons/advanced_fatigue/functions/fnc_mainLoop.sqf index 88a7fc30cc9..4dee02389fd 100644 --- a/addons/advanced_fatigue/functions/fnc_mainLoop.sqf +++ b/addons/advanced_fatigue/functions/fnc_mainLoop.sqf @@ -25,8 +25,8 @@ if (!alive ACE_player) exitWith { // Dead people don't breath, Will also handle private _normal = surfaceNormal (getPosWorld ACE_player); private _movementVector = vectorNormalized (velocity ACE_player); private _sideVector = vectorNormalized (_movementVector vectorCrossProduct _normal); -private _fwdAngle = asin (_movementVector select 2) * GVAR(terrainGradientFactor); -private _sideAngle = asin (_sideVector select 2) * GVAR(terrainGradientFactor); +private _fwdAngle = asin (_movementVector select 2); +private _sideAngle = asin (_sideVector select 2); private _currentWork = REE; private _currentSpeed = (vectorMagnitude (velocity ACE_player)) min 6; @@ -42,7 +42,7 @@ if ((vehicle ACE_player == ACE_player) && {_currentSpeed > 0.1} && {isTouchingGr }; // Oxygen calculation -private _oxygen = 1 - 0.1315 * GVAR(respiratoryRate) ^ 2; +private _oxygen = 1 - 0.131 * GVAR(respiratoryRate) ^ 2; // Calculate muscle damage increase GVAR(muscleDamage) = (GVAR(muscleDamage) + (_currentWork / GVAR(peakPower)) ^ 3.2 * MUSCLE_TEAR_RATE) min 1; @@ -55,7 +55,8 @@ private _muscleFactor = sqrt (_muscleIntegrity); 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; + // Calculate how much power is consumed from each reserve private _ae1Power = _currentWork min _ae1PathwayPowerFatigued; private _ae2Power = (_currentWork - _ae1Power) min _ae2PathwayPowerFatigued; @@ -69,9 +70,9 @@ GVAR(anReserve) = 0 max (GVAR(anReserve) - _anPower / GVAR(anWattsPerATP)); systemChat format["---- ae2: %1 ----", (GVAR(ae2Reserve) / AE2_MAXRESERVE) toFixed 2]; systemChat format["---- an: %1 ----", (GVAR(anReserve) / AN_MAXRESERVE) toFixed 2]; systemChat format["---- anFatigue: %1 ----", GVAR(anFatigue) toFixed 2]; -systemChat format["---- muscleDamage: %1 ----", GVAR(muscleDamage) toFixed 2]; -systemChat format["---- respiratoryRate: %1 ----", GVAR(respiratoryRate) toFixed 2]; -systemChat format["---- aePower: %1 ----", _aePathwayPowerFatigued toFixed 1]; +//systemChat format["---- muscleDamage: %1 ----", GVAR(muscleDamage) toFixed 2]; +//systemChat format["---- respiratoryRate: %1 ----", GVAR(respiratoryRate) toFixed 2]; +//systemChat format["---- aePower: %1 ----", _aePathwayPowerFatigued toFixed 1]; #endif // Acidosis accumulation @@ -83,19 +84,34 @@ GVAR(ae2Reserve) = (GVAR(ae2Reserve) + _oxygen * GVAR(recoveryFactor) * AE2_ATP_ private _aeSurplus = _ae1PathwayPowerFatigued + _ae2PathwayPowerFatigued - _ae1Power - _ae2Power; // Anaerobic ATP reserve recovery -GVAR(anReserve) = (GVAR(anReserve) + _aeSurplus / GVAR(VO2MaxPower) * AN_ATP_RECOVERY * GVAR(recoveryFactor) * GVAR(anFatigue) ^ 2) min AN_MAXRESERVE; +GVAR(anReserve) = 0 max (GVAR(anReserve) + _aeSurplus / GVAR(VO2MaxPower) * AN_ATP_RECOVERY * GVAR(recoveryFactor) * GVAR(anFatigue) ^ 2) min AN_MAXRESERVE; // Acidosis recovery GVAR(anFatigue) = 0 max (GVAR(anFatigue) - _aeSurplus * GVAR(maxPowerFatigueRatio) * GVAR(recoveryFactor) * GVAR(anFatigue) ^ 2) min 1; +// Anaerobic power calculation +_anPower = _anPathwayPowerFatigued * (1 - (GVAR(anFatigue) ^ 2)); + +// Peak fatigued power calculation +private _peakPowerFatigued = _aePathwayPowerFatigued + _anPower; + +// Movement rate limits +private _maxRunSpeed = ([ACE_player, _aePathwayPowerFatigued, _fwdAngle, _sideAngle] call FUNC(getMaximumSpeed)) - 0.1; +private _maxSprintSpeed = [ACE_player, _peakPowerFatigued, _fwdAngle, _sideAngle] call FUNC(getMaximumSpeed); +#ifdef DEBUG_MODE_FULL +systemChat format["---- animSpeedCoef: %1 ----", (getAnimSpeedCoef ACE_player) toFixed 2]; +systemChat format["---- maxRunSpeed: %1 ----", _maxRunSpeed toFixed 2]; +systemChat format["---- maxSprintSpeed: %1 ----", _maxSprintSpeed toFixed 2]; +#endif + // Respiratory rate decrease GVAR(respiratoryRate) = GVAR(respiratoryRate) * GVAR(respiratoryBufferDivisor); // Respiratory rate increase private _aePowerRatio = (GVAR(aePathwayPower) / _aePathwayPowerFatigued) min 2; -private _respiratorySampleDivisor = 1 / (RESPIRATORY_BUFFER * 4.75 * GVAR(VO2Max)); +private _respiratorySampleDivisor = 1 / (RESPIRATORY_BUFFER * 4.72 * GVAR(VO2Max)); GVAR(respiratoryRate) = (GVAR(respiratoryRate) + _currentWork * _respiratorySampleDivisor * _aePowerRatio) min 1; -[ACE_player, GVAR(anFatigue), _currentSpeed, GVAR(respiratoryRate), _fwdAngle, _sideAngle] call FUNC(handleEffects); +[ACE_player, GVAR(anFatigue), GVAR(respiratoryRate), _currentSpeed, _maxRunSpeed, _maxSprintSpeed, _fwdAngle, _sideAngle] call FUNC(handleEffects); if (GVAR(enableStaminaBar)) then { [GVAR(anReserve) / AN_MAXRESERVE] call FUNC(handleStaminaBar);