Skip to content

Commit

Permalink
Experimental movement speed limiter
Browse files Browse the repository at this point in the history
  • Loading branch information
ulteq committed Nov 22, 2017
1 parent 00305eb commit 18661eb
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 21 deletions.
1 change: 1 addition & 0 deletions addons/advanced_fatigue/XEH_PREP.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
PREP(addDutyFactor);
PREP(createStaminaBar);
PREP(getAnimDuty);
PREP(getMaximumSpeed);
PREP(getMetabolicCosts);
PREP(handleEffects);
PREP(handlePlayerChanged);
Expand Down
93 changes: 93 additions & 0 deletions addons/advanced_fatigue/functions/fnc_getMaximumSpeed.sqf
Original file line number Diff line number Diff line change
@@ -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 <OBJECT>
* 1: Power <NUMBER>
* 2: Forward Angle <NUMBER>
* 3: Side Angle <NUMBER>
*
* Return Value:
* Speed <NUMBER>
*
* 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
40 changes: 29 additions & 11 deletions addons/advanced_fatigue/functions/fnc_handleEffects.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
* Arguments:
* 0: Unit <OBJECT>
* 1: Fatigue <NUMBER>
* 2: Speed <NUMBER>
* 3: Respiratory Rate <NUMBER>
* 4: Forward Angle <NUMBER>
* 5: Side Angle <NUMBER>
* 2: Respiratory Rate <NUMBER>
* 3: Current Speed <NUMBER>
* 4: Max Run Speed <NUMBER>
* 5: Max Sprint Speed <NUMBER>
* 6: Forward Angle <NUMBER>
* 7: Side Angle <NUMBER>
*
* Return Value:
* None
Expand All @@ -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;
Expand Down Expand Up @@ -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);
};
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
36 changes: 26 additions & 10 deletions addons/advanced_fatigue/functions/fnc_mainLoop.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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
Expand All @@ -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);
Expand Down

0 comments on commit 18661eb

Please sign in to comment.