mirror of
https://github.com/acemod/ACE3.git
synced 2024-08-30 18:23:18 +00:00
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>
This commit is contained in:
parent
cd66f495ad
commit
f86e882b18
@ -9,3 +9,4 @@ PREP(handleStaminaBar);
|
||||
PREP(mainLoop);
|
||||
PREP(moduleSettings);
|
||||
PREP(removeDutyFactor);
|
||||
PREP(renderDebugLines);
|
||||
|
@ -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;
|
||||
@ -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 {
|
||||
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
@ -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);
|
||||
};
|
||||
};
|
||||
|
@ -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>
|
||||
@ -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;
|
||||
@ -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];
|
||||
};
|
||||
@ -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;
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include "..\script_component.hpp"
|
||||
/*
|
||||
* Author: BaerMitUmlaut
|
||||
* Author: BaerMitUmlaut, ulteq
|
||||
* Main looping function that updates fatigue values.
|
||||
*
|
||||
* Arguments:
|
||||
@ -17,73 +17,131 @@
|
||||
|
||||
// Dead people don't breathe, will also handle null (map intros)
|
||||
if (!alive ACE_player) exitWith {
|
||||
[FUNC(mainLoop), [], 1] call CBA_fnc_waitAndExecute;
|
||||
[LINKFUNC(mainLoop), [], 1] call CBA_fnc_waitAndExecute;
|
||||
|
||||
private _staminaBarContainer = uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull];
|
||||
_staminaBarContainer ctrlSetFade 1;
|
||||
_staminaBarContainer ctrlCommit 1;
|
||||
};
|
||||
|
||||
|
||||
private _oxygen = 0.9; // Default AF oxygen saturation
|
||||
if (GETEGVAR(medical,enabled,false) && {EGVAR(medical_vitals,simulateSpo2)}) then {
|
||||
_oxygen = (ACE_player getVariable [QEGVAR(medical,spo2), 97]) / 100;
|
||||
};
|
||||
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);
|
||||
|
||||
private _currentWork = REE;
|
||||
private _currentSpeed = (vectorMagnitude (velocity ACE_player)) min 6;
|
||||
private _currentSpeed = (vectorMagnitude _velocity) min 6;
|
||||
|
||||
// fix #4481. Diving to the ground is recorded as PRONE stance with running speed velocity. Cap maximum speed to fix.
|
||||
if (GVAR(isProne)) then {
|
||||
_currentSpeed = _currentSpeed min 1.5;
|
||||
};
|
||||
|
||||
if ((vehicle ACE_player == ACE_player) && {_currentSpeed > 0.1} && {isTouchingGround ACE_player || {underwater ACE_player}}) then {
|
||||
_currentWork = [ACE_player, _currentSpeed] call FUNC(getMetabolicCosts);
|
||||
// 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);
|
||||
_currentWork = _currentWork max REE;
|
||||
};
|
||||
|
||||
// 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
|
||||
};
|
||||
// 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) min 1;
|
||||
private _muscleIntegritySqrt = sqrt (1 - GVAR(muscleDamage));
|
||||
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;
|
||||
|
||||
// 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;
|
||||
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) 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(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;
|
||||
|
||||
// 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;
|
||||
|
||||
GVAR(anFatigue) = ((GVAR(anFatigue)
|
||||
- (_ae1PathwayPowerFatigued + _ae2PathwayPowerFatigued - _ae1Power - _ae2Power) * (0.057 / GVAR(peakPower)) * GVAR(anFatigue) ^ 2 * GVAR(recoveryFactor)
|
||||
) min 1) max 0;
|
||||
// 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;
|
||||
|
||||
// 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.72 * GVAR(VO2Max));
|
||||
GVAR(respiratoryRate) = (GVAR(respiratoryRate) + _currentWork * _respiratorySampleDivisor * _aePowerRatio) min 1;
|
||||
|
||||
// Calculate a pseudo-perceived fatigue, which is used for effects
|
||||
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));
|
||||
|
||||
[ACE_player, _perceivedFatigue, _currentSpeed, GVAR(anReserve) == 0] call FUNC(handleEffects);
|
||||
#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);
|
||||
|
||||
if (GVAR(enableStaminaBar)) then {
|
||||
[GVAR(anReserve) / AN_MAXRESERVE] call FUNC(handleStaminaBar);
|
||||
};
|
||||
|
||||
[FUNC(mainLoop), [], 1] call CBA_fnc_waitAndExecute;
|
||||
[LINKFUNC(mainLoop), [], 1] call CBA_fnc_waitAndExecute;
|
||||
|
40
addons/advanced_fatigue/functions/fnc_renderDebugLines.sqf
Normal file
40
addons/advanced_fatigue/functions/fnc_renderDebugLines.sqf
Normal file
@ -0,0 +1,40 @@
|
||||
#include "..\script_component.hpp"
|
||||
/*
|
||||
* Author: ulteq
|
||||
* Draw lines for debugging.
|
||||
*
|
||||
* Arguments:
|
||||
* None
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Example:
|
||||
* call ace_advanced_fatigue_fnc_renderDebugLines
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
|
||||
addMissionEventHandler ["Draw3D", {
|
||||
private _normal = surfaceNormal (getPosWorld ACE_player);
|
||||
private _beg = (getPosWorld ACE_player) vectorAdd (_normal vectorMultiply 0.5);
|
||||
private _end = _beg vectorAdd (_normal vectorMultiply 2);
|
||||
drawLine3D [ASLToATL _beg, ASLToATL _end, [0, 1, 0, 1]];
|
||||
|
||||
private _side = vectorNormalized (_normal vectorCrossProduct [0, 0, 1]);
|
||||
private _end = _beg vectorAdd (_side vectorMultiply 2);
|
||||
drawLine3D [ASLToATL _beg, ASLToATL _end, [0, 0, 1, 1]];
|
||||
|
||||
private _up = vectorNormalized (_normal vectorCrossProduct _side);
|
||||
private _end = _beg vectorAdd (_up vectorMultiply 2);
|
||||
drawLine3D [ASLToATL _beg, ASLToATL _end, [1, 0, 0, 1]];
|
||||
|
||||
private _movementVector = vectorNormalized (velocity ACE_player);
|
||||
private _end = _beg vectorAdd (_movementVector vectorMultiply 2);
|
||||
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);
|
||||
drawLine3D [ASLToATL _beg, ASLToATL _end, [0, 1, 1, 1]];
|
||||
}];
|
@ -4,12 +4,14 @@
|
||||
[LSTRING(Enabled), LSTRING(Enabled_Description)],
|
||||
LSTRING(DisplayName),
|
||||
true,
|
||||
true, {
|
||||
1,
|
||||
{
|
||||
if (!_this) then {
|
||||
private _staminaBarContainer = uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull];
|
||||
_staminaBarContainer ctrlSetFade 1;
|
||||
_staminaBarContainer ctrlCommit 0;
|
||||
};
|
||||
|
||||
[QGVAR(enabled), _this] call EFUNC(common,cbaSettings_settingChanged)
|
||||
},
|
||||
true // Needs mission restart
|
||||
@ -21,7 +23,8 @@
|
||||
[LSTRING(EnableStaminaBar), LSTRING(EnableStaminaBar_Description)],
|
||||
LSTRING(DisplayName),
|
||||
true,
|
||||
true, {
|
||||
1,
|
||||
{
|
||||
if (!_this) then {
|
||||
private _staminaBarContainer = uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull];
|
||||
_staminaBarContainer ctrlSetFade 1;
|
||||
@ -36,7 +39,8 @@
|
||||
[LSTRING(FadeStaminaBar), LSTRING(FadeStaminaBar_Description)],
|
||||
LSTRING(DisplayName),
|
||||
true,
|
||||
false, {
|
||||
0,
|
||||
{
|
||||
if (!_this && GVAR(enabled) && GVAR(enableStaminaBar)) then {
|
||||
private _staminaBarContainer = uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull];
|
||||
_staminaBarContainer ctrlSetFade 0;
|
||||
@ -50,8 +54,14 @@
|
||||
"SLIDER",
|
||||
[LSTRING(PerformanceFactor), LSTRING(PerformanceFactor_Description)],
|
||||
LSTRING(DisplayName),
|
||||
[0, 5, 1, 1],
|
||||
true
|
||||
[0, 10, 1, 2],
|
||||
1,
|
||||
{
|
||||
// Recalculate values if the setting is changed mid-mission
|
||||
if (GVAR(enabled) && hasInterface && !isNull ACE_player) then {
|
||||
[ACE_player, ACE_player] call FUNC(handlePlayerChanged);
|
||||
};
|
||||
}
|
||||
] call CBA_fnc_addSetting;
|
||||
|
||||
[
|
||||
@ -59,8 +69,8 @@
|
||||
"SLIDER",
|
||||
[LSTRING(RecoveryFactor), LSTRING(RecoveryFactor_Description)],
|
||||
LSTRING(DisplayName),
|
||||
[0, 5, 1, 1],
|
||||
true
|
||||
[0, 10, 1, 2],
|
||||
1
|
||||
] call CBA_fnc_addSetting;
|
||||
|
||||
[
|
||||
@ -68,8 +78,8 @@
|
||||
"SLIDER",
|
||||
[LSTRING(LoadFactor), LSTRING(LoadFactor_Description)],
|
||||
LSTRING(DisplayName),
|
||||
[0, 5, 1, 1],
|
||||
true
|
||||
[0, 5, 1, 2],
|
||||
1
|
||||
] call CBA_fnc_addSetting;
|
||||
|
||||
[
|
||||
@ -77,6 +87,6 @@
|
||||
"SLIDER",
|
||||
[LSTRING(TerrainGradientFactor), LSTRING(TerrainGradientFactor_Description)],
|
||||
LSTRING(DisplayName),
|
||||
[0, 5, 1, 1],
|
||||
true
|
||||
[0, 5, 1, 2],
|
||||
1
|
||||
] call CBA_fnc_addSetting;
|
||||
|
@ -16,14 +16,28 @@
|
||||
|
||||
#include "\z\ace\addons\main\script_macros.hpp"
|
||||
|
||||
#define UNDERWEAR_WEIGHT 3.5
|
||||
|
||||
#define ANTPERCENT 0.8
|
||||
#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.00004
|
||||
#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
|
||||
|
@ -13,16 +13,18 @@ version:
|
||||
|
||||
## 1. Global Settings
|
||||
|
||||
ACE provides four settings to tweak Advanced Fatigue. Adjust these factors depending on how hard you want your experience to be.
|
||||
ACE provides multiple settings to tweak Advanced Fatigue. Adjust these settings depending on how hard you want your experience to be.
|
||||
|
||||
- **Enabled:** This allow you to toggle Advanced Fatigue on or off. Mission needs to be restarted if this setting is changed.
|
||||
- **Show stamina bar:** Shows/hides the stamina bar.
|
||||
- **Fade Stamina bar automatically:** Adjusts transparency of the bar based on stamina status.
|
||||
- **Performance factor:** This influences the overall performance of players, malnourished or super soldiers, everything is possible.
|
||||
- **Recovery factor:** Do you like looking at the landscape or think breaks are boring? Whatever the case, this influences the length of your stamina breaks.
|
||||
- **Load factor:** If you believe a Javelin is the perfect companion for your .50 BMG sniper rifle you probably should tweak this setting.
|
||||
- **Terrain factor**: Not everyone is used to mountainous terrain. Tweak this until you feel more at home.
|
||||
- **Sway factor**: Influences the amount of weapon sway. Higher means more sway.
|
||||
|
||||
Note that while there currently is no restriction on the value of these settings, it's generally recommended to keep them between 0 and 2.
|
||||
|
||||
- **Terrain factor:** Not everyone is used to mountainous terrain. Tweak this until you feel more at home.
|
||||
- **Sway factor:** Influences the amount of weapon sway. Higher means more sway.
|
||||
- **Rested sway factor:** Influences the amount of weapon sway while rested is deployed. Higher means more sway.
|
||||
- **Deployed sway factor:** Influences the amount of weapon sway while weapon is deployed. Higher means more sway.
|
||||
|
||||
## 2. Unit Specific Settings
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user