Add Advanced Fatigue component (#4321)

* Initial commit advanced fatigue

* Added animation changes

* Fixes, rewrites

* Broadcast 3den attribute and fix UBC

* Further animation tweaks

* Added Eden attribute and CBA settings

* Switch to CBA events

* Add beautified component name

* Rewrites, fixes, tweaks

* Move setVars to GVARs

* Add setting to disable

* Switch to status effect handler for sprint and jog blocking

* Add component name

* Failsafe for early unit player EH

* Removed raised weapon penalty for pistols

* Added respawn compatibility

* Tweaked side jog animation speed

* Added support for virtual load

* Added stamina bar

* Remove debug flags

* Added custom duty factor functions and values

* Added structural comments

* Disabled CBA settings, added ACE settings

* Fixed config

* Added readme

* Remove leftover debug diag_logs

* Improved stringtable
This commit is contained in:
BaerMitUmlaut 2016-09-04 21:33:07 +02:00 committed by Glowbal
parent 7314f863f6
commit cc414de8d1
29 changed files with 1026 additions and 0 deletions

View File

@ -0,0 +1 @@
z\ace\addons\advanced_fatigue

View File

@ -0,0 +1,45 @@
class ACE_Settings {
class GVAR(enabled) {
category = "Advanced Fatigue";
displayName = CSTRING(Enabled);
description = CSTRING(Enabled_Description);
typeName = "BOOL";
value = 1;
};
class GVAR(enableStaminaBar) {
category = "Advanced Fatigue";
displayName = CSTRING(EnableStaminaBar);
description = CSTRING(EnableStaminaBar_Description);
typeName = "BOOL";
value = 1;
isClientSettable = 1;
};
class GVAR(performanceFactor) {
category = "Advanced Fatigue";
displayName = CSTRING(PerformanceFactor);
description = CSTRING(PerformanceFactor_Description);
typeName = "SCALAR";
value = 1;
};
class GVAR(recoveryFactor) {
category = "Advanced Fatigue";
displayName = CSTRING(RecoveryFactor);
description = CSTRING(RecoveryFactor_Description);
typeName = "SCALAR";
value = 1;
};
class GVAR(loadFactor) {
category = "Advanced Fatigue";
displayName = CSTRING(LoadFactor);
description = CSTRING(LoadFactor_Description);
typeName = "SCALAR";
value = 1;
};
class GVAR(terrainGradientFactor) {
category = "Advanced Fatigue";
displayName = CSTRING(TerrainGradientFactor);
description = CSTRING(TerrainGradientFactor_Description);
typeName = "SCALAR";
value = 1;
};
};

View File

@ -0,0 +1,55 @@
class Cfg3DEN {
class Attributes {
class Slider;
class GVAR(slider): Slider {
attributeLoad = " \
params [""_ctrlGroup""]; \
private _slider = _ctrlGroup controlsGroupCtrl 100; \
private _edit = _ctrlGroup controlsGroupCtrl 101; \
_slider sliderSetPosition _value; \
_edit ctrlSetText ([_value, 1, 1] call CBA_fnc_formatNumber); \
";
attributeSave = " \
params [""_ctrlGroup""]; \
sliderPosition (_ctrlGroup controlsGroupCtrl 100); \
";
onLoad = " \
params [""_ctrlGroup""]; \
private _slider = _ctrlGroup controlsGroupCtrl 100; \
private _edit = _ctrlGroup controlsGroupCtrl 101; \
_slider sliderSetRange [0, 2]; \
_slider ctrlAddEventHandler [""SliderPosChanged"", { \
params [""_slider""]; \
private _edit = (ctrlParentControlsGroup _slider) controlsGroupCtrl 101; \
private _value = sliderPosition _slider; \
_edit ctrlSetText ([_value, 1, 1] call CBA_fnc_formatNumber); \
}]; \
_edit ctrlAddEventHandler [""KillFocus"", { \
params [""_edit""]; \
private _slider = (ctrlParentControlsGroup _edit) controlsGroupCtrl 100; \
private _value = ((parseNumber ctrlText _edit) min 2) max 0; \
_slider sliderSetPosition _value; \
_edit ctrlSetText str _value; \
}]; \
";
};
};
class Object {
class AttributeCategories {
class ace_attributes {
class Attributes {
class GVAR(performanceFactor) {
property = QGVAR(performanceFactor);
control = QGVAR(slider);
displayName = CSTRING(PerformanceFactor);
tooltip = CSTRING(PerformanceFactor_EdenDescription);
expression = QUOTE(_this setVariable [ARR_3(QQGVAR(performanceFactor),_value,true)]);
typeName = "NUMBER";
condition = "objectControllable";
defaultValue = 1;
};
};
};
};
};
};

View File

@ -0,0 +1,23 @@
class Extended_PreStart_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_FILE(XEH_preStart));
};
};
class Extended_PreInit_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_FILE(XEH_preInit));
};
};
class Extended_PostInit_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_FILE(XEH_postInit));
};
};
class Extended_DisplayLoad_EventHandlers {
class RscDisplayMission {
ADDON = QUOTE(call FUNC(createStaminaBar));
};
};

View File

@ -0,0 +1,62 @@
class CfgMovesBasic;
class CfgMovesMaleSdr: CfgMovesBasic {
class States {
// Jog speed adjustment: 12km/h
class AmovPercMstpSlowWrflDnon;
class AmovPercMrunSlowWrflDf: AmovPercMstpSlowWrflDnon {
speed = 0.634570;
};
class AmovPercMrunSlowWrflDfl: AmovPercMrunSlowWrflDf {
speed = 0.634570;
};
class AmovPercMrunSlowWrflDl: AmovPercMrunSlowWrflDfl {
speed = 0.691626;
};
class AmovPercMrunSlowWrflDr: AmovPercMrunSlowWrflDfl {
speed = 0.727404;
};
// Jog + raised weapon speed adjustment: 13km/h
class AmovPercMstpSrasWrflDnon;
class AmovPercMrunSrasWrflDf: AmovPercMstpSrasWrflDnon {
speed = 0.677068;
};
// Tactical Jog animation replacement and speed adjustment: 10km/h
class AmovPercMwlkSlowWrflDf_ver2;
class AmovPercMwlkSlowWrflDfl_ver2;
class AmovPercMtacSlowWrflDf_ver2: AmovPercMwlkSlowWrflDf_ver2 {
speed = 0.528808;
file = "\A3\anims_f\Data\Anim\Sdr\Mov\Erc\run\low\Rfl\AmovPercMrunSlowWrflDf";
};
class AmovPercMtacSlowWrflDfl_ver2: AmovPercMwlkSlowWrflDfl_ver2 {
speed = 0.528808;
file = "\A3\anims_f\Data\Anim\Sdr\Mov\Erc\run\low\Rfl\AmovPercMrunSlowWrflDfl";
};
class AmovPercMtacSlowWrflDfr_ver2: AmovPercMtacSlowWrflDf_ver2 {
speed = 0.528808;
file = "\A3\anims_f\Data\Anim\Sdr\Mov\Erc\run\low\Rfl\AmovPercMrunSlowWrflDfr";
};
class AmovPercMtacSlowWrflDr_ver2: AmovPercMtacSlowWrflDf_ver2 {
speed = 0.528808;
file = "\A3\anims_f\Data\Anim\Sdr\Mov\Erc\run\low\Rfl\AmovPercMrunSlowWrflDr";
};
class AmovPercMtacSlowWrflDl_ver2: AmovPercMtacSlowWrflDf_ver2 {
speed = 0.528808;
file = "\A3\anims_f\Data\Anim\Sdr\Mov\Erc\run\low\Rfl\AmovPercMrunSlowWrflDl";
};
class AmovPercMtacSlowWrflDb_ver2: AmovPercMtacSlowWrflDf_ver2 {
speed = 0.684541;
file = "\A3\anims_f\Data\Anim\Sdr\Mov\Erc\run\low\Rfl\AmovPercMrunSlowWrflDb";
soundEdge[] = {0.25,0.5,0.75,1};
};
class AmovPercMtacSlowWrflDbl_ver2: AmovPercMtacSlowWrflDb_ver2 {
speed = 0.684541;
file = "\A3\anims_f\Data\Anim\Sdr\Mov\Erc\run\low\Rfl\AmovPercMrunSlowWrflDbl";
};
class AmovPercMtacSlowWrflDbr_ver2: AmovPercMtacSlowWrflDb_ver2 {
speed = 0.684541;
file = "\A3\anims_f\Data\Anim\Sdr\Mov\Erc\run\low\Rfl\AmovPercMrunSlowWrflDbr";
};
};
};

View File

@ -0,0 +1,76 @@
class CfgSounds {
class GVAR(breathLow0) {
sound[] = {"A3\sounds_f\characters\human-sfx\P04\Breath_Low_1", 0.6, 1, 7};
titles[] = {};
};
class GVAR(breathLow1) {
sound[] = {"A3\sounds_f\characters\human-sfx\P04\Breath_Low_2", 0.6, 1, 7};
titles[] = {};
};
class GVAR(breathLow2) {
sound[] = {"A3\sounds_f\characters\human-sfx\P04\Breath_Low_3", 0.6, 1, 7};
titles[] = {};
};
class GVAR(breathLow3) {
sound[] = {"A3\sounds_f\characters\human-sfx\P04\Breath_Low_4", 0.6, 1, 7};
titles[] = {};
};
class GVAR(breathLow4) {
sound[] = {"A3\sounds_f\characters\human-sfx\P04\Breath_Low_5", 0.6, 1, 7};
titles[] = {};
};
class GVAR(breathLow5) {
sound[] = {"A3\sounds_f\characters\human-sfx\P04\Breath_Low_6", 0.6, 1, 7};
titles[] = {};
};
class GVAR(breathMid0) {
sound[] = {"A3\sounds_f\characters\human-sfx\P04\Breath_Mid_1", 0.8, 1, 13};
titles[] = {};
};
class GVAR(breathMid1) {
sound[] = {"A3\sounds_f\characters\human-sfx\P04\Breath_Mid_2", 0.8, 1, 13};
titles[] = {};
};
class GVAR(breathMid2) {
sound[] = {"A3\sounds_f\characters\human-sfx\P04\Breath_Mid_3", 0.8, 1, 13};
titles[] = {};
};
class GVAR(breathMid3) {
sound[] = {"A3\sounds_f\characters\human-sfx\P04\Breath_Mid_4", 0.8, 1, 13};
titles[] = {};
};
class GVAR(breathMid4) {
sound[] = {"A3\sounds_f\characters\human-sfx\P04\Breath_Mid_5", 0.8, 1, 13};
titles[] = {};
};
class GVAR(breathMid5) {
sound[] = {"A3\sounds_f\characters\human-sfx\P04\Breath_Mid_6", 0.8, 1, 13};
titles[] = {};
};
class GVAR(breathMax0) {
sound[] = {"A3\sounds_f\characters\human-sfx\P04\Breath_Max_1", 1, 1, 17};
titles[] = {};
};
class GVAR(breathMax1) {
sound[] = {"A3\sounds_f\characters\human-sfx\P04\Breath_Max_2", 1, 1, 17};
titles[] = {};
};
class GVAR(breathMax2) {
sound[] = {"A3\sounds_f\characters\human-sfx\P04\Breath_Max_3", 1, 1, 17};
titles[] = {};
};
class GVAR(breathMax3) {
sound[] = {"A3\sounds_f\characters\human-sfx\P04\Breath_Max_4", 1, 1, 17};
titles[] = {};
};
class GVAR(breathMax4) {
sound[] = {"A3\sounds_f\characters\human-sfx\P04\Breath_Max_5", 1, 1, 17};
titles[] = {};
};
class GVAR(breathMax5) {
sound[] = {"A3\sounds_f\characters\human-sfx\P04\Breath_Max_6", 1, 1, 17};
titles[] = {};
};
};

View File

@ -0,0 +1,45 @@
class CfgVehicles {
class ACE_Module;
class GVAR(moduleSettings): ACE_Module {
author = ECSTRING(common,ACETeam);
category = "ACE";
displayName = "Advanced Fatigue";
function = QFUNC(moduleSettings);
scope = 2;
isGlobal = 1;
isTriggerActivated = 0;
icon = QPATHTOF(UI\Icon_Module.paa);
class Arguments {
class Enabled {
displayName = CSTRING(Enabled);
description = CSTRING(Enabled_Description);
typeName = "BOOL";
defaultValue = 1;
};
class PerformanceFactor {
displayName = CSTRING(PerformanceFactor);
description = CSTRING(PerformanceFactor_Description);
typeName = "NUMBER";
defaultValue = 1;
};
class RecoveryFactor {
displayName = CSTRING(RecoveryFactor);
description = CSTRING(RecoveryFactor_Description);
typeName = "NUMBER";
defaultValue = 1;
};
class LoadFactor {
displayName = CSTRING(LoadFactor);
description = CSTRING(LoadFactor_Description);
typeName = "NUMBER";
defaultValue = 1;
};
class TerrainGradientFactor {
displayName = CSTRING(TerrainGradientFactor);
description = CSTRING(TerrainGradientFactor_Description);
typeName = "NUMBER";
defaultValue = 1;
};
};
};
};

View File

@ -0,0 +1,20 @@
class RscControlsGroupNoScrollbars;
class RscPicture;
class GVAR(StaminaBarContainer): RscControlsGroupNoScrollbars {
x = "(profilenamespace getvariable [""IGUI_GRID_STAMINA_X"", ((safezoneX + safezoneW) - (10 * (((safezoneW / safezoneH) min 1.2) / 40)) - 4.3 * (((safezoneW / safezoneH) min 1.2) / 40))])";
y = "(profilenamespace getvariable [""IGUI_GRID_STAMINA_Y"", (safezoneY + 4.05 * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25))])";
w = "10 * (((safezoneW / safezoneH) min 1.2) / 40)";
h = "0.15 * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25)";
class Controls {
class StaminaBar: RscPicture {
idc = 10;
x = 0;
y = 0;
w = "10 * (((safezoneW / safezoneH) min 1.2) / 40)";
h = "0.15 * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25)";
text = "\A3\Ui_f\data\IGUI\RscIngameUI\RscUnitInfo\stamina_ca.paa";
};
};
};

View File

@ -0,0 +1,10 @@
ace_advanced_fatigue
==========
An in depth stamina and fatigue simulation.
## Maintainers
The people responsible for merging changes to this component or answering potential questions.
- [BaerMitUmlaut](https://github.com/BaerMitUmlaut)

Binary file not shown.

View File

@ -0,0 +1,10 @@
PREP(addDutyFactor);
PREP(createStaminaBar);
PREP(getAnimDuty);
PREP(getMetabolicCosts);
PREP(handleEffects);
PREP(handlePlayerChanged);
PREP(handleStaminaBar);
PREP(moduleSettings);
PREP(pfhMain);
PREP(removeDutyFactor);

View File

@ -0,0 +1,50 @@
#include "script_component.hpp"
if (!hasInterface) exitWith {};
["ace_settingsInitialized", {
if (!GVAR(enabled)) exitWith {};
// - Post process effect ------------------------------------------------------
GVAR(ppeBlackout) = ppEffectCreate ["ColorCorrections", 4220];
GVAR(ppeBlackout) ppEffectEnable true;
GVAR(ppeBlackout) ppEffectForceInNVG true;
GVAR(ppeBlackout) ppEffectAdjust [1,1,0,[0,0,0,1],[0,0,0,0],[1,1,1,1],[10,10,0,0,0,0.1,0.5]];
GVAR(ppeBlackout) ppEffectCommit 0.4;
// - GVAR updating and initialization -----------------------------------------
if !(isNull ACE_player) then {
[ACE_player, objNull] call FUNC(handlePlayerChanged);
};
["unit", FUNC(handlePlayerChanged)] call CBA_fnc_addPlayerEventHandler;
// - Duty factors -------------------------------------------------------------
[QEGVAR(medical,pain), {
1 + (((_this getVariable [QEGVAR(medical,pain), 0]) min 1) / 10)
}] call FUNC(addDutyFactor);
[QEGVAR(medical,bloodVolume), {
2 - (((_this getVariable [QEGVAR(medical,bloodVolume), 100]) min 100) / 100)
}] call FUNC(addDutyFactor);
[QEGVAR(dragging,isCarrying), {
if (_this getVariable [QEGVAR(dragging,isCarrying), false]) then {
3
} else {
1
};
}] call FUNC(addDutyFactor);
[QEGVAR(weather,temperature), {
(((missionNamespace getVariable [QEGVAR(weather,currentTemperature), 25]) - 35) / 10) max 2 min 1
}] call FUNC(addDutyFactor);
// - Add main PFH -------------------------------------------------------------
[FUNC(pfhMain), 1, []] call CBA_fnc_addPerFrameHandler;
}] call CBA_fnc_addEventHandler;
["ace_settingChanged", {
params ["_name", "_value"];
if (_name == QGVAR(enableStaminaBar) && {!_value}) then {
private _staminaBarContainer = uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull];
_staminaBarContainer ctrlSetFade 1;
_staminaBarContainer ctrlCommit 0;
};
}] call CBA_fnc_addEventHandler;

View File

@ -0,0 +1,11 @@
#include "script_component.hpp"
ADDON = false;
#include "XEH_PREP.hpp"
//#include "initSettings.sqf"
GVAR(staminaBarWidth) = 10 * (((safezoneW / safezoneH) min 1.2) / 40);
GVAR(dutyList) = [[], []];
ADDON = true;

View File

@ -0,0 +1,3 @@
#include "script_component.hpp"
#include "XEH_PREP.hpp"

View File

@ -0,0 +1,23 @@
#include "script_component.hpp"
class CfgPatches {
class ADDON {
name = COMPONENT_NAME;
units[] = {};
weapons[] = {};
requiredVersion = REQUIRED_VERSION;
requiredAddons[] = {"ace_common"};
author = CSTRING(ACETeam);
authors[] = {"BaerMitUmlaut"};
url = ECSTRING(main,URL);
VERSION_CONFIG;
};
};
#include "ACE_Settings.hpp"
#include "CfgEden.hpp"
#include "CfgEventHandlers.hpp"
#include "CfgMovesMaleSdr.hpp"
#include "CfgSounds.hpp"
#include "CfgVehicles.hpp"
#include "Dialog.hpp"

View File

@ -0,0 +1,18 @@
/*
* Author: BaerMitUmlaut
* Adds a duty factor.
*
* Arguments:
* 0: Factor ID <STRING>
* 1: Factor <NUMBER> or <CODE>
*
* Return Value:
* None
*/
#include "script_component.hpp"
params [["_id", "", [""]], ["_factor", 1, [0, {}]]];
if (_id == "" || {_factor isEqualTo 1}) exitWith {};
GVAR(dutyList) params ["_idList", "_factorList"];
_idList pushBack _id;
_factorList pushBack _factor,

View File

@ -0,0 +1,18 @@
/*
* Author: BaerMitUmlaut
* Creates the stamina bar.
*
* Arguments:
* 0: Display <DISPLAY>
*
* Return Value:
* None
*/
#include "script_component.hpp"
params ["_display"];
private _staminaBar = _display ctrlCreate [QGVAR(StaminaBarContainer), -1];
uiNamespace setVariable [QGVAR(staminaBarContainer), _staminaBar];
_staminaBar ctrlSetFade 1;
_staminaBar ctrlCommit 0;

View File

@ -0,0 +1,56 @@
/*
* Author: BaerMitUmlaut
* Calculates the duty of the current animation.
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Animation name <STRING>
*
* Return Value:
* Duty <NUMBER>
*
* Example:
* [player, "AidlPercMstpSlowWrflDnon_G05"] call ace_advanced_fatigue_fnc_getAnimDuty
*
* Public: No
*/
#include "script_component.hpp"
params ["_unit", "_animName"];
private _duty = 1;
private _animType = _animName select [1, 3];
GVAR(isSwimming) = false;
if (_animType in ["idl", "mov"]) then {
switch (_animName select [5, 3]) do {
case ("knl"): {
_duty = 1.5;
};
case ("pne"): {
_duty = 12;
};
default {
_duty = 1;
};
};
if (currentWeapon _unit != handgunWeapon _unit) then {
if (_animName select [13, 3] == "ras") then {
// low ready jog
_duty = _duty * 1.2;
if (_animName select [9, 3] == "tac") then {
// high ready jog/walk
_duty = _duty * 1.5;
};
};
};
} else {
// swimming and diving
if (_animType in ["swm", "ssw", "bsw", "dve", "sdv", "bdv"]) then {
_duty = 5;
GVAR(isSwimming) = true;
};
};
_duty

View File

@ -0,0 +1,61 @@
/*
* Author: BaerMitUmlaut
* Calculates the current metabolic costs for a unit.
* Calculation is done according to the Pandolf/Wojtowicz formulas.
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Speed <NUMBER>
*
* Return Value:
* Metabolic cost <NUMBER>
*
* Example:
* [player, 3.3] call ace_advanced_fatigue_fnc_getMetabolicCosts
*
* Public: No
*/
#include "script_component.hpp"
params ["_unit", "_velocity"];
private _virtualLoad = 0;
{
_virtualLoad = _virtualLoad + (_x getVariable [QEGVAR(movement,vLoad), 0]);
} forEach [
_unit,
uniformContainer _unit,
vestContainer _unit,
backpackContainer _unit
];
private _gearMass = ((loadAbs _unit + _virtualLoad) * 0.1 / 2.2046) * GVAR(loadFactor);
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;
} else {
_duty = _duty * (_unit call _x);
};
} forEach (GVAR(dutyList) select 1);
if (GVAR(isSwimming)) then {
_terrainGradient = 0;
};
if (_velocity > 2) then {
(
2.10 * SIM_BODYMASS
+ 4 * (SIM_BODYMASS + _gearMass) * ((_gearMass / SIM_BODYMASS) ^ 2)
+ _terrainFactor * (SIM_BODYMASS + _gearMass) * (0.90 * (_velocity ^ 2) + 0.66 * _velocity * _terrainGradient)
) * 0.23 * _duty
} else {
(
1.05 * SIM_BODYMASS
+ 4 * (SIM_BODYMASS + _gearMass) * ((_gearMass / SIM_BODYMASS) ^ 2)
+ _terrainFactor * (SIM_BODYMASS + _gearMass) * (1.15 * (_velocity ^ 2) + 0.66 * _velocity * _terrainGradient)
) * 0.23 * _duty
};

View File

@ -0,0 +1,90 @@
/*
* Author: BaerMitUmlaut
* Handles any audible, visual and physical effects of fatigue.
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Fatigue <NUMBER>
* 2: Speed <NUMBER>
* 3: Overexhausted <BOOL>
*
* Return Value:
* None
*
* Example:
* [_player, 0.5, 3.3, true] 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
// - Audible effects ----------------------------------------------------------
GVAR(lastBreath) = GVAR(lastBreath) + 1;
if (_fatigue > 0.4 && {GVAR(lastBreath) > (_fatigue * -10 + 9)} && {!underwater _unit}) then {
switch (true) do {
case (_fatigue < 0.6): {
playSound (QGVAR(breathLow) + str(floor random 6));
};
case (_fatigue < 0.85): {
playSound (QGVAR(breathMid) + str(floor random 6));
};
default {
playSound (QGVAR(breathMax) + str(floor random 6));
};
};
GVAR(lastBreath) = 0;
};
// - Visual effects -----------------------------------------------------------
GVAR(ppeBlackoutLast) = GVAR(ppeBlackoutLast) + 1;
if (GVAR(ppeBlackoutLast) == 1) then {
GVAR(ppeBlackout) ppEffectAdjust [1,1,0,[0,0,0,1],[0,0,0,0],[1,1,1,1],[10,10,0,0,0,0.1,0.5]];
GVAR(ppeBlackout) ppEffectCommit 1;
} else {
if (_fatigue > 0.85) then {
if (GVAR(ppeBlackoutLast) > (100 - _fatigue * 100) / 3) then {
GVAR(ppeBlackout) ppEffectAdjust [1,1,0,[0,0,0,1],[0,0,0,0],[1,1,1,1],[2,2,0,0,0,0.1,0.5]];
GVAR(ppeBlackout) ppEffectCommit 1;
GVAR(ppeBlackoutLast) = 0;
};
};
};
// - Physical effects ---------------------------------------------------------
if (GVAR(isSwimming)) exitWith {
_unit setAnimSpeedCoef (1 - _fatigue / 3);
};
if (_overexhausted) then {
[_unit, "forceWalk", QUOTE(ADDON), true] call EFUNC(common,statusEffect_set);
} else {
if (isForcedWalk _unit && {_fatigue < 0.7}) then {
[_unit, "forceWalk", QUOTE(ADDON), false] call EFUNC(common,statusEffect_set);
} else {
if ((isSprintAllowed _unit) && {_fatigue > 0.7}) then {
[_unit, "blockSprint", QUOTE(ADDON), true] call EFUNC(common,statusEffect_set);
} else {
if ((!isSprintAllowed _unit) && {_fatigue < 0.6}) then {
[_unit, "blockSprint", QUOTE(ADDON), false] call EFUNC(common,statusEffect_set);
};
};
};
};
switch (stance _unit) do {
case ("CROUCH"): {
_unit setCustomAimCoef (1.0 + _fatigue ^ 2 * 0.1);
};
case ("PRONE"): {
_unit setCustomAimCoef (1.0 + _fatigue ^ 2 * 2.0);
};
default {
_unit setCustomAimCoef (1.5 + _fatigue ^ 2 * 3.0);
};
};

View File

@ -0,0 +1,57 @@
/*
* Author: BaerMitUmlaut
* Handles switching units (once on init and afterwards via Zeus).
*
* Arguments:
* 0: New Unit <OBJECT>
* 1: Old Unit <OBJECT>
*
* Return Value:
* None
*/
#include "script_component.hpp"
params ["_newUnit", "_oldUnit"];
if !(isNull _oldUnit) then {
_oldUnit enableStamina true;
_oldUnit removeEventHandler ["AnimChanged", _oldUnit getVariable [QGVAR(animHandler), -1]];
_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)];
};
_newUnit enableStamina false;
// Don't add a new EH if the unit respawned
if (_newUnit getVariable [QGVAR(animHandler), -1] == -1) then {
private _animHandler = _newUnit addEventHandler ["AnimChanged", {
GVAR(animDuty) = _this call FUNC(getAnimDuty);
}];
_newUnit setVariable [QGVAR(animHandler), _animHandler];
};
GVAR(ae1Reserve) = _newUnit getVariable [QGVAR(ae1Reserve), AE1_MAXRESERVE];
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];
// Clean variables for respawning units
{
_newUnit setVariable [_x, nil];
} forEach [QGVAR(ae1Reserve), QGVAR(ae2Reserve), QGVAR(anReserve), QGVAR(anFatigue), QGVAR(muscleDamage)];
GVAR(VO2Max) = 35 + 20 * (_newUnit getVariable [QGVAR(performanceFactor), GVAR(performanceFactor)]);
GVAR(VO2MaxPower) = GVAR(VO2Max) * SIM_BODYMASS * 0.23 * 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(anPathwayPower) = GVAR(peakPower) - _ae1PathwayPower - _ae2PathwayPower;
GVAR(ppeBlackoutLast) = 100;
GVAR(lastBreath) = 0;
GVAR(animDuty) = [_newUnit, animationState _newUnit] call FUNC(getAnimDuty);

View File

@ -0,0 +1,44 @@
/*
* Author: BaerMitUmlaut
* Handles visual changes of the stamina bar.
*
* Arguments:
* Percent of stamina remaining <NUMBER>
*
* Return Value:
* None
*/
#include "script_component.hpp"
params ["_stamina"];
private _staminaBarContainer = uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull];
// - Size ---------------------------------------------------------------------
// Shrink the container to cut off the image (other wise it would just get stretched)
private _posAndSize = ctrlPosition _staminaBarContainer;
_posAndSize set [2, _stamina * GVAR(staminaBarWidth)];
_staminaBarContainer ctrlSetPosition _posAndSize;
// - Opacity ------------------------------------------------------------------
if (_stamina >= 0.8) then {
_staminaBarContainer ctrlSetFade (0.9 + 0.1 * (_stamina - 0.8) / 0.2);
} else {
_staminaBarContainer ctrlSetFade (0.9 * _stamina / 0.8);
};
// - Color --------------------------------------------------------------------
// 1.0 - 0.8: White
// 0.6 - 0.4: Orange
// 0.4 - 0.2: Red
private _color = [1, 1, 1];
if (_stamina < 0.6) then {
if (_stamina < 0.4) then {
_color = [1, 0, 0] vectorAdd ([0, 0.65, 0] vectorMultiply ((_stamina - 0.2) / 0.2));
} else {
_color = [1, 0.65, 0] vectorAdd ([0, 0.35, 1] vectorMultiply ((_stamina - 0.4) / 0.2));
};
};
_color pushBack 1;
(_staminaBarContainer controlsGroupCtrl 10) ctrlSetTextColor _color;
_staminaBarContainer ctrlCommit 1;

View File

@ -0,0 +1,18 @@
/*
* Author: BaerMitUmlaut
* Initializes the module settings.
*
* Arguments:
* 0: Module <OBJECT>
*
* Return Value:
* None
*/
#include "script_component.hpp"
params ["_logic"];
[_logic, QGVAR(enabled), "Enabled"] call EFUNC(common,readSettingFromModule);
[_logic, QGVAR(performanceFactor), "PerformanceFactor"] call EFUNC(common,readSettingFromModule);
[_logic, QGVAR(recoveryFactor), "RecoveryFactor"] call EFUNC(common,readSettingFromModule);
[_logic, QGVAR(loadFactor), "LoadFactor"] call EFUNC(common,readSettingFromModule);
[_logic, QGVAR(terrainGradientFactor), "TerrainGradientFactor"] call EFUNC(common,readSettingFromModule);

View File

@ -0,0 +1,63 @@
/*
* Author: BaerMitUmlaut
* Main perFrameHandler that updates fatigue values.
*
* Arguments:
* None
*
* Return Value:
* None
*/
#include "script_component.hpp"
if (isNull ACE_player) exitWith {}; // Map intros
private _currentWork = REE;
private _currentSpeed = (vectorMagnitude (velocity ACE_player)) min 6;
if ((vehicle ACE_player == ACE_player) && {_currentSpeed > 0.1} && {isTouchingGround ACE_player || {underwater ACE_player}}) then {
_currentWork = [ACE_player, _currentSpeed] call FUNC(getMetabolicCosts);
_currentWork = _currentWork max REE;
};
// 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 _muscleIntegrity = 1 - GVAR(muscleDamage);
// Calculate available power
private _ae1PathwayPowerFatigued = GVAR(ae1PathwayPower) * sqrt (GVAR(ae1Reserve) / AE1_MAXRESERVE) * OXYGEN * sqrt _muscleIntegrity;
private _ae2PathwayPowerFatigued = GVAR(ae2PathwayPower) * sqrt (GVAR(ae2Reserve) / AE2_MAXRESERVE) * OXYGEN * sqrt _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;
// 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;
// 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;
// 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;
GVAR(anFatigue) = ((GVAR(anFatigue)
- (_ae1PathwayPowerFatigued + _ae2PathwayPowerFatigued - _ae1Power - _ae2Power) * (0.057 / GVAR(peakPower)) * GVAR(anFatigue) ^ 2 * GVAR(recoveryFactor)
) min 1) max 0;
private _aeReservePercentage = (GVAR(ae1Reserve) / AE1_MAXRESERVE + GVAR(ae2Reserve) / AE2_MAXRESERVE) / 2;
private _anReservePercentage = GVAR(anReserve) / AN_MAXRESERVE;
private _perceivedFatigue = 1 - (_anReservePercentage min _aeReservePercentage);
[ACE_player, _perceivedFatigue, _currentSpeed, GVAR(anReserve) == 0] call FUNC(handleEffects);
if (GVAR(enableStaminaBar)) then {
[GVAR(anReserve) / AN_MAXRESERVE] call FUNC(handleStaminaBar);
};

View File

@ -0,0 +1,20 @@
/*
* Author: BaerMitUmlaut
* Removes a duty factor.
*
* Arguments:
* 0: Factor ID <STRING>
*
* Return Value:
* None
*/
#include "script_component.hpp"
params [["_id", "", [""]]];
GVAR(dutyList) params ["_idList", "_factorList"];
private _index = _idList find _id;
if (_index != -1) then {
_idList deleteAt _index;
_factorList deleteAt _index;
};

View File

@ -0,0 +1 @@
#include "\z\ace\addons\advanced_fatigue\script_component.hpp"

View File

@ -0,0 +1,59 @@
[
QGVAR(enabled),
"CHECKBOX",
[LSTRING(Enabled), LSTRING(Enabled_Description)],
"ACE3 Advanced Fatigue",
true,
true
] call CBA_Settings_fnc_init;
[
QGVAR(enableStaminaBar),
"CHECKBOX",
[LSTRING(EnableStaminaBar), LSTRING(EnableStaminaBar_Description)],
"ACE3 Advanced Fatigue",
true,
true, {
if (!_this) then {
private _staminaBarContainer = uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull];
_staminaBarContainer ctrlSetFade 1;
_staminaBarContainer ctrlCommit 0;
};
}
] call CBA_Settings_fnc_init;
[
QGVAR(performanceFactor),
"SLIDER",
[LSTRING(PerformanceFactor), LSTRING(PerformanceFactor_Description)],
"ACE3 Advanced Fatigue",
[0, 2, 1, 1],
true
] call CBA_Settings_fnc_init;
[
QGVAR(recoveryFactor),
"SLIDER",
[LSTRING(RecoveryFactor), LSTRING(RecoveryFactor_Description)],
"ACE3 Advanced Fatigue",
[0, 2, 1, 1],
true
] call CBA_Settings_fnc_init;
[
QGVAR(loadFactor),
"SLIDER",
[LSTRING(LoadFactor), LSTRING(LoadFactor_Description)],
"ACE3 Advanced Fatigue",
[0, 2, 1, 1],
true
] call CBA_Settings_fnc_init;
[
QGVAR(terrainGradientFactor),
"SLIDER",
[LSTRING(TerrainGradientFactor), LSTRING(TerrainGradientFactor_Description)],
"ACE3 Advanced Fatigue",
[0, 2, 1, 1],
true
] call CBA_Settings_fnc_init;

View File

@ -0,0 +1,30 @@
#define COMPONENT advanced_fatigue
#define COMPONENT_BEAUTIFIED Advanced Fatigue
#include "\z\ace\addons\main\script_mod.hpp"
// #define DEBUG_MODE_FULL
// #define DISABLE_COMPILE_CACHE
// #define CBA_DEBUG_SYNCHRONOUS
// #define ENABLE_PERFORMANCE_COUNTERS
#ifdef DEBUG_ENABLED_ADVANCED_FATIGUE
#define DEBUG_MODE_FULL
#endif
#ifdef DEBUG_SETTINGS_ADVANCED_FATIGUE
#define DEBUG_SETTINGS DEBUG_SETTINGS_ADVANCED_FATIGUE
#endif
#include "\z\ace\addons\main\script_macros.hpp"
#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 AE1_MAXRESERVE 4000000
#define AE2_MAXRESERVE 84000
#define AN_MAXRESERVE 2300

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<Project name="ACE">
<Package name="Advanced_Fatigue">
<Key ID="STR_ACE_Advanced_Fatigue_PerformanceFactor">
<English>Performance Factor</English>
<German>Leistungsfaktor</German>
</Key>
<Key ID="STR_ACE_Advanced_Fatigue_PerformanceFactor_Description">
<English>Influences the overall performance of all players with no custom factor. Higher means better.</English>
<German>Beinflusst die Leistungsfähigkeit aller Spieler ohne eigenen Leistungsfaktor. Ein höherer Wert bedeutet bessere Leistung.</German>
</Key>
<Key ID="STR_ACE_Advanced_Fatigue_PerformanceFactor_EdenDescription">
<English>Influences the overall performance of this unit. Higher means better.</English>
<German>Beinflusst die Leistungsfähigkeit dieser Einheit. Ein höherer Wert bedeutet bessere Leistung.</German>
</Key>
<Key ID="STR_ACE_Advanced_Fatigue_RecoveryFactor">
<English>Recovery Factor</English>
<German>Erholungsfaktor</German>
</Key>
<Key ID="STR_ACE_Advanced_Fatigue_RecoveryFactor_Description">
<English>Changes how fast the player recovers when resting. Higher is faster.</English>
<German>Ändert, wie schnell ein Spieler Ausdauer regeneriert. Ein höherer Wert bedeutet eine schnellere Regeneration.</German>
</Key>
<Key ID="STR_ACE_Advanced_Fatigue_LoadFactor">
<English>Load Factor</English>
<German>Gewichtsfaktor</German>
</Key>
<Key ID="STR_ACE_Advanced_Fatigue_LoadFactor_Description">
<English>Increases or decreases how much weight influences the players performance. Zero means equipment weight has no performance influence.</English>
<German>Erhöht oder verringert, wie viel Einfluss das Ausrüstungsgewicht auf die Leistung hat. Null heißt, dass es keinen Einfluss hat.</German>
</Key>
<Key ID="STR_ACE_Advanced_Fatigue_TerrainGradientFactor">
<English>Terrain Gradient Factor</English>
<German>Terrainsteigungsfaktor</German>
</Key>
<Key ID="STR_ACE_Advanced_Fatigue_TerrainGradientFactor_Description">
<English>Sets how much steep terrain increases stamina loss. Higher means higher stamina loss.</English>
<German>Beeinflusst, wie stark Steigungen den Ausdauerverbrauch erhöhen. Ein höherer Wert erhöht den Ausdauerverbrauch.</German>
</Key>
<Key ID="STR_ACE_Advanced_Fatigue_Enabled">
<English>Enabled</English>
<German>Aktiv</German>
</Key>
<Key ID="STR_ACE_Advanced_Fatigue_Enabled_Description">
<English>Enables/disables Advanced Fatigue.</English>
<German>Aktiviert/deaktiviert Advanced Fatigue.</German>
</Key>
<Key ID="STR_ACE_Advanced_Fatigue_EnableStaminaBar">
<English>Show stamina bar</English>
<German>Zeige Ausdauerleiste</German>
</Key>
<Key ID="STR_ACE_Advanced_Fatigue_EnableStaminaBar_Description">
<English>Shows the stamina bar.</English>
<German>Zeigt die Ausdauerleiste an.</German>
</Key>
</Package>
</Project>