Skip to content

Commit

Permalink
Advanced Fatigue - Various improvements (continuation of #5723) (#9714)
Browse files Browse the repository at this point in the history
Co-authored-by: Grim <69561145+LinkIsGrim@users.noreply.github.com>
Co-authored-by: ulteq <ulteq@web.de>
Co-authored-by: LinkIsGrim <salluci.lovi@gmail.com>
  • Loading branch information
4 people authored Jun 22, 2024
1 parent cd66f49 commit f86e882
Show file tree
Hide file tree
Showing 11 changed files with 290 additions and 125 deletions.
1 change: 1 addition & 0 deletions addons/advanced_fatigue/XEH_PREP.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ PREP(handleStaminaBar);
PREP(mainLoop);
PREP(moduleSettings);
PREP(removeDutyFactor);
PREP(renderDebugLines);
18 changes: 10 additions & 8 deletions addons/advanced_fatigue/XEH_postInit.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

if (!hasInterface) exitWith {};

#ifdef DEBUG_MODE_FULL
call FUNC(renderDebugLines);
#endif

// recheck weapon inertia after weapon swap, change of attachments or switching unit
["weapon", {[ACE_player] call FUNC(getWeaponInertia)}, true] call CBA_fnc_addPlayerEventHandler;
["loadout", {[ACE_player] call FUNC(getWeaponInertia)}, true] call CBA_fnc_addPlayerEventHandler;
Expand Down Expand Up @@ -33,25 +37,23 @@ if (!hasInterface) exitWith {};
GVAR(ppeBlackout) ppEffectCommit 0.4;

// - GVAR updating and initialization -----------------------------------------
["unit", LINKFUNC(handlePlayerChanged), true] call CBA_fnc_addPlayerEventHandler;
["unit", LINKFUNC(handlePlayerChanged)] call CBA_fnc_addPlayerEventHandler;

["visibleMap", {
params ["", "_visibleMap"]; // command visibleMap is updated one frame later
private _staminaBarContainer = uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull];
_staminaBarContainer ctrlShow ((!_visibleMap) && {(vehicle ACE_player) == ACE_player});
(uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull]) ctrlShow (!_visibleMap && isNull objectParent ACE_player);
}, true] call CBA_fnc_addPlayerEventHandler;
["vehicle", {
private _staminaBarContainer = uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull];
_staminaBarContainer ctrlShow ((!visibleMap) && {(vehicle ACE_player) == ACE_player});
(uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull]) ctrlShow (!visibleMap && isNull objectParent ACE_player);
}, true] call CBA_fnc_addPlayerEventHandler;

// - Duty factors -------------------------------------------------------------
if (GETEGVAR(medical,enabled,false)) then {
[QEGVAR(medical,pain), { // 0->1.0, 0.5->1.05, 1->1.1
linearConversion [0, 1, (_this getVariable [QEGVAR(medical,pain), 0]), 1, 1.1, true];
linearConversion [0, 1, _this getVariable [QEGVAR(medical,pain), 0], 1, 1.1, true];
}] call FUNC(addDutyFactor);
[QEGVAR(medical,bloodVolume), { // 6->1.0, 5->1.167, 4->1.33
linearConversion [6, 0, (_this getVariable [QEGVAR(medical,bloodVolume), 6]), 1, 2, true];
linearConversion [6, 0, _this getVariable [QEGVAR(medical,bloodVolume), 6], 1, 2, true];
}] call FUNC(addDutyFactor);
};
if (["ace_dragging"] call EFUNC(common,isModLoaded)) then {
Expand All @@ -62,7 +64,7 @@ if (!hasInterface) exitWith {};
// Weather has an off switch, Dragging & Medical don't.
if (missionNamespace getVariable [QEGVAR(weather,enabled), false]) then {
[QEGVAR(weather,temperature), { // 35->1, 45->2
linearConversion [35, 45, (missionNamespace getVariable [QEGVAR(weather,currentTemperature), 25]), 1, 2, true];
linearConversion [35, 45, missionNamespace getVariable [QEGVAR(weather,currentTemperature), 25], 1, 2, true];
}] call FUNC(addDutyFactor);
};

Expand Down
2 changes: 1 addition & 1 deletion addons/advanced_fatigue/functions/fnc_getAnimDuty.sqf
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include "..\script_component.hpp"
/*
* Author: BaerMitUmlaut
* Calculates the duty of the current animation.
* Calculates the duty ('postureWeight') of the current animation.
*
* Arguments:
* 0: Unit <OBJECT>
Expand Down
78 changes: 49 additions & 29 deletions addons/advanced_fatigue/functions/fnc_getMetabolicCosts.sqf
Original file line number Diff line number Diff line change
@@ -1,54 +1,74 @@
#include "..\script_component.hpp"
/*
* Author: BaerMitUmlaut
* Calculates the current metabolic costs for a unit.
* Author: BaerMitUmlaut, ulteq
* Calculates the current metabolic costs.
* Calculation is done according to the Pandolf/Wojtowicz formulas.
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Speed <NUMBER>
* 0: Duty of animation
* 1: Mass of unit <NUMBER>
* 2: Terrain gradient <NUMBER>
* 3: Terrain factor <NUMBER>
* 4: Speed <NUMBER>
*
* Return Value:
* Metabolic cost <NUMBER>
*
* Example:
* [player, 3.3] call ace_advanced_fatigue_fnc_getMetabolicCosts
* [1, 840, 20, 1, 4] call ace_advanced_fatigue_fnc_getMetabolicCosts
*
* Public: No
*/
params ["_unit", "_velocity"];

private _gearMass = ((_unit getVariable [QEGVAR(movement,totalLoad), loadAbs _unit]) / 22.046) * GVAR(loadFactor);

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;
} else {
_duty = _duty * (_unit call _x);
};
} forEach (values GVAR(dutyList));

if (GVAR(isSwimming)) then {
_terrainGradient = 0;
};
params ["_duty", "_gearMass", "_terrainGradient", "_terrainFactor", "_speed"];

// Metabolic cost for walking and running is different
if (_velocity > 2) then {
if (_speed > 2) then {
// Running
#ifdef DEBUG_MODE_FULL
private _baseline = 2.1 * SIM_BODYMASS + 4 * (SIM_BODYMASS + _gearMass) * ((_gearMass / SIM_BODYMASS) ^ 2) + (SIM_BODYMASS + _gearMass) * 0.9 * (_speed ^ 2);
private _graded = 2.1 * SIM_BODYMASS + 4 * (SIM_BODYMASS + _gearMass) * ((_gearMass / SIM_BODYMASS) ^ 2) + _terrainFactor * (SIM_BODYMASS + _gearMass) * (0.9 * (_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
2.1 * SIM_BODYMASS
+ 4 * (SIM_BODYMASS + _gearMass) * ((_gearMass / SIM_BODYMASS) ^ 2)
+ (SIM_BODYMASS + _gearMass) * (0.9 * (_velocity ^ 2) + 0.66 * _velocity * _terrainGradient)
) * 0.23 * _duty
+ _terrainFactor * (SIM_BODYMASS + _gearMass) * (0.9 * (_speed ^ 2) + 0.66 * _speed * _terrainGradient)
) * BIOMECH_EFFICIENCY * _duty
} else {
// Walking
#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
+ 2 * (SIM_BODYMASS + _gearMass) * ((_gearMass / SIM_BODYMASS) ^ 2)
+ (SIM_BODYMASS + _gearMass) * (1.15 * (_velocity ^ 2) + 0.66 * _velocity * _terrainGradient)
) * 0.23 * _duty
+ _terrainFactor * (SIM_BODYMASS + _gearMass) * (1.15 * (_speed ^ 2) + 0.66 * _speed * _terrainGradient)
) * BIOMECH_EFFICIENCY * _duty
};
50 changes: 27 additions & 23 deletions addons/advanced_fatigue/functions/fnc_handleEffects.sqf
Original file line number Diff line number Diff line change
@@ -1,44 +1,44 @@
#include "..\script_component.hpp"
/*
* Author: BaerMitUmlaut
* Author: BaerMitUmlaut, ulteq
* Handles any audible, visual and physical effects of fatigue.
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Fatigue <NUMBER>
* 2: Speed <NUMBER>
* 3: Overexhausted <BOOL>
* 2: Overexhausted <BOOL>
* 3: Forward Angle <NUMBER>
* 4: Side Angle <NUMBER>
*
* 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
*/
params ["_unit", "_fatigue", "_speed", "_overexhausted"];

#ifdef DEBUG_MODE_FULL
systemChat str _fatigue;
systemChat str vectorMagnitude velocity _unit;
#endif
params ["_unit", "_fatigue", "_overexhausted", "_fwdAngle", "_sideAngle"];

// - Audible effects ----------------------------------------------------------
GVAR(lastBreath) = GVAR(lastBreath) + 1;

if (_fatigue > 0.4 && {GVAR(lastBreath) > (_fatigue * -10 + 9)} && {!underwater _unit}) then {
if (!isGameFocused) exitWith {};

switch (true) do {
case (_fatigue < 0.6): {
playSound (QGVAR(breathLow) + str(floor random 6));
playSound (QGVAR(breathLow) + str (floor random 6));
};
case (_fatigue < 0.85): {
playSound (QGVAR(breathMid) + str(floor random 6));
playSound (QGVAR(breathMid) + str (floor random 6));
};
default {
playSound (QGVAR(breathMax) + str(floor random 6));
playSound (QGVAR(breathMax) + str (floor random 6));
};
};

GVAR(lastBreath) = 0;
};

Expand All @@ -62,31 +62,35 @@ if (GVAR(isSwimming)) exitWith {
if (GVAR(setAnimExclusions) isEqualTo []) then {
_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 { // small checks like these are faster without lazy eval
[_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);
};
};
};
if ((getAnimSpeedCoef _unit) != 1) then {
if (GVAR(setAnimExclusions) isEqualTo []) then {
TRACE_1("reset",getAnimSpeedCoef _unit);
_unit setAnimSpeedCoef 1;
};

// If other components are setting setAnimSpeedCoef, do not change animSpeedCoef
if (getAnimSpeedCoef _unit != 1 && {GVAR(setAnimExclusions) isEqualTo []}) then {
TRACE_1("reset",getAnimSpeedCoef _unit);
_unit setAnimSpeedCoef 1;
};

if (_overexhausted) then {
if (!isForcedWalk _unit && _fatigue >= 1) then { // small checks like these are faster without lazy eval
[_unit, "forceWalk", QUOTE(ADDON), true] call EFUNC(common,statusEffect_set);
[_unit, "blockSprint", QUOTE(ADDON), true] call EFUNC(common,statusEffect_set);
} else {
if (isForcedWalk _unit && {_fatigue < 0.7}) then {
if (isForcedWalk _unit && _fatigue < 0.7) then {
[_unit, "forceWalk", QUOTE(ADDON), false] call EFUNC(common,statusEffect_set);
[_unit, "blockSprint", QUOTE(ADDON), false] call EFUNC(common,statusEffect_set);
} else {
if ((isSprintAllowed _unit) && {_fatigue > 0.7}) then {
// Forward angle is the slope of the terrain, side angle simulates the unevenness/roughness of the terrain
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);
};
};
Expand Down
30 changes: 22 additions & 8 deletions addons/advanced_fatigue/functions/fnc_handlePlayerChanged.sqf
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include "..\script_component.hpp"
/*
* Author: BaerMitUmlaut
* Handles switching units (once on init and afterwards via Zeus).
* Author: BaerMitUmlaut, ulteq
* Handles switching units (once on init and afterwards via Zeus). Also handles CBA setting change for performance factor.
*
* Arguments:
* 0: New Unit <OBJECT>
Expand All @@ -15,20 +15,24 @@
*
* Public: No
*/

params ["_newUnit", "_oldUnit"];

TRACE_2("unit changed",_newUnit,_oldUnit);

if !(isNull _oldUnit) then {
if (!isNull _oldUnit) then {
TRACE_1("remove old",_oldUnit getVariable QGVAR(animHandler));

_oldUnit enableStamina true;
_oldUnit removeEventHandler ["AnimChanged", _oldUnit getVariable [QGVAR(animHandler), -1]];
_oldUnit setVariable [QGVAR(animHandler), nil];
TRACE_1("remove old",_oldUnit getVariable QGVAR(animHandler));

_oldUnit setVariable [QGVAR(ae1Reserve), GVAR(ae1Reserve)];
_oldUnit setVariable [QGVAR(ae2Reserve), GVAR(ae2Reserve)];
_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;
Expand All @@ -38,6 +42,7 @@ if (_newUnit getVariable [QGVAR(animHandler), -1] == -1) then {
private _animHandler = _newUnit addEventHandler ["AnimChanged", {
GVAR(animDuty) = _this call FUNC(getAnimDuty);
}];

TRACE_1("add new",_animHandler);
_newUnit setVariable [QGVAR(animHandler), _animHandler];
};
Expand All @@ -47,18 +52,27 @@ 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(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);

GVAR(ppeBlackoutLast) = 100;
GVAR(lastBreath) = 0;
Expand Down
Loading

0 comments on commit f86e882

Please sign in to comment.