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);