diff --git a/addons/advanced_fatigue/$PBOPREFIX$ b/addons/advanced_fatigue/$PBOPREFIX$ new file mode 100644 index 0000000000..c99b9388c3 --- /dev/null +++ b/addons/advanced_fatigue/$PBOPREFIX$ @@ -0,0 +1 @@ +z\ace\addons\advanced_fatigue diff --git a/addons/advanced_fatigue/ACE_Settings.hpp b/addons/advanced_fatigue/ACE_Settings.hpp new file mode 100644 index 0000000000..8242e16111 --- /dev/null +++ b/addons/advanced_fatigue/ACE_Settings.hpp @@ -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; + }; +}; diff --git a/addons/advanced_fatigue/CfgEden.hpp b/addons/advanced_fatigue/CfgEden.hpp new file mode 100644 index 0000000000..dcabcc2ed6 --- /dev/null +++ b/addons/advanced_fatigue/CfgEden.hpp @@ -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; + }; + }; + }; + }; + }; +}; diff --git a/addons/advanced_fatigue/CfgEventHandlers.hpp b/addons/advanced_fatigue/CfgEventHandlers.hpp new file mode 100644 index 0000000000..0b5cb8edff --- /dev/null +++ b/addons/advanced_fatigue/CfgEventHandlers.hpp @@ -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)); + }; +}; diff --git a/addons/advanced_fatigue/CfgMovesMaleSdr.hpp b/addons/advanced_fatigue/CfgMovesMaleSdr.hpp new file mode 100644 index 0000000000..8d6dc57170 --- /dev/null +++ b/addons/advanced_fatigue/CfgMovesMaleSdr.hpp @@ -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"; + }; + }; +}; diff --git a/addons/advanced_fatigue/CfgSounds.hpp b/addons/advanced_fatigue/CfgSounds.hpp new file mode 100644 index 0000000000..a799dbb66f --- /dev/null +++ b/addons/advanced_fatigue/CfgSounds.hpp @@ -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[] = {}; + }; +}; diff --git a/addons/advanced_fatigue/CfgVehicles.hpp b/addons/advanced_fatigue/CfgVehicles.hpp new file mode 100644 index 0000000000..922322b588 --- /dev/null +++ b/addons/advanced_fatigue/CfgVehicles.hpp @@ -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; + }; + }; + }; +}; diff --git a/addons/advanced_fatigue/Dialog.hpp b/addons/advanced_fatigue/Dialog.hpp new file mode 100644 index 0000000000..df019f3800 --- /dev/null +++ b/addons/advanced_fatigue/Dialog.hpp @@ -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"; + }; + }; +}; diff --git a/addons/advanced_fatigue/README.md b/addons/advanced_fatigue/README.md new file mode 100644 index 0000000000..aec1cfd38b --- /dev/null +++ b/addons/advanced_fatigue/README.md @@ -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) diff --git a/addons/advanced_fatigue/UI/Icon_Module.paa b/addons/advanced_fatigue/UI/Icon_Module.paa new file mode 100644 index 0000000000..71579eff27 Binary files /dev/null and b/addons/advanced_fatigue/UI/Icon_Module.paa differ diff --git a/addons/advanced_fatigue/XEH_PREP.hpp b/addons/advanced_fatigue/XEH_PREP.hpp new file mode 100644 index 0000000000..1ec7705695 --- /dev/null +++ b/addons/advanced_fatigue/XEH_PREP.hpp @@ -0,0 +1,10 @@ +PREP(addDutyFactor); +PREP(createStaminaBar); +PREP(getAnimDuty); +PREP(getMetabolicCosts); +PREP(handleEffects); +PREP(handlePlayerChanged); +PREP(handleStaminaBar); +PREP(moduleSettings); +PREP(pfhMain); +PREP(removeDutyFactor); diff --git a/addons/advanced_fatigue/XEH_postInit.sqf b/addons/advanced_fatigue/XEH_postInit.sqf new file mode 100644 index 0000000000..2eff26abdf --- /dev/null +++ b/addons/advanced_fatigue/XEH_postInit.sqf @@ -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; diff --git a/addons/advanced_fatigue/XEH_preInit.sqf b/addons/advanced_fatigue/XEH_preInit.sqf new file mode 100644 index 0000000000..9b799a0816 --- /dev/null +++ b/addons/advanced_fatigue/XEH_preInit.sqf @@ -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; diff --git a/addons/advanced_fatigue/XEH_preStart.sqf b/addons/advanced_fatigue/XEH_preStart.sqf new file mode 100644 index 0000000000..022888575e --- /dev/null +++ b/addons/advanced_fatigue/XEH_preStart.sqf @@ -0,0 +1,3 @@ +#include "script_component.hpp" + +#include "XEH_PREP.hpp" diff --git a/addons/advanced_fatigue/config.cpp b/addons/advanced_fatigue/config.cpp new file mode 100644 index 0000000000..db947b5815 --- /dev/null +++ b/addons/advanced_fatigue/config.cpp @@ -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" diff --git a/addons/advanced_fatigue/functions/fnc_addDutyFactor.sqf b/addons/advanced_fatigue/functions/fnc_addDutyFactor.sqf new file mode 100644 index 0000000000..74a42d55cf --- /dev/null +++ b/addons/advanced_fatigue/functions/fnc_addDutyFactor.sqf @@ -0,0 +1,18 @@ +/* + * Author: BaerMitUmlaut + * Adds a duty factor. + * + * Arguments: + * 0: Factor ID + * 1: Factor or + * + * 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, diff --git a/addons/advanced_fatigue/functions/fnc_createStaminaBar.sqf b/addons/advanced_fatigue/functions/fnc_createStaminaBar.sqf new file mode 100644 index 0000000000..773e240494 --- /dev/null +++ b/addons/advanced_fatigue/functions/fnc_createStaminaBar.sqf @@ -0,0 +1,18 @@ +/* + * Author: BaerMitUmlaut + * Creates the stamina bar. + * + * Arguments: + * 0: 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; diff --git a/addons/advanced_fatigue/functions/fnc_getAnimDuty.sqf b/addons/advanced_fatigue/functions/fnc_getAnimDuty.sqf new file mode 100644 index 0000000000..b91e8a09bf --- /dev/null +++ b/addons/advanced_fatigue/functions/fnc_getAnimDuty.sqf @@ -0,0 +1,56 @@ +/* + * Author: BaerMitUmlaut + * Calculates the duty of the current animation. + * + * Arguments: + * 0: Unit + * 1: Animation name + * + * Return Value: + * Duty + * + * 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 diff --git a/addons/advanced_fatigue/functions/fnc_getMetabolicCosts.sqf b/addons/advanced_fatigue/functions/fnc_getMetabolicCosts.sqf new file mode 100644 index 0000000000..fd2234d1f2 --- /dev/null +++ b/addons/advanced_fatigue/functions/fnc_getMetabolicCosts.sqf @@ -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 + * 1: Speed + * + * Return Value: + * Metabolic cost + * + * 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 +}; diff --git a/addons/advanced_fatigue/functions/fnc_handleEffects.sqf b/addons/advanced_fatigue/functions/fnc_handleEffects.sqf new file mode 100644 index 0000000000..e23b9d86cd --- /dev/null +++ b/addons/advanced_fatigue/functions/fnc_handleEffects.sqf @@ -0,0 +1,90 @@ +/* + * Author: BaerMitUmlaut + * Handles any audible, visual and physical effects of fatigue. + * + * Arguments: + * 0: Unit + * 1: Fatigue + * 2: Speed + * 3: Overexhausted + * + * 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); + }; +}; diff --git a/addons/advanced_fatigue/functions/fnc_handlePlayerChanged.sqf b/addons/advanced_fatigue/functions/fnc_handlePlayerChanged.sqf new file mode 100644 index 0000000000..c275b91cc8 --- /dev/null +++ b/addons/advanced_fatigue/functions/fnc_handlePlayerChanged.sqf @@ -0,0 +1,57 @@ +/* + * Author: BaerMitUmlaut + * Handles switching units (once on init and afterwards via Zeus). + * + * Arguments: + * 0: New Unit + * 1: Old Unit + * + * 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); diff --git a/addons/advanced_fatigue/functions/fnc_handleStaminaBar.sqf b/addons/advanced_fatigue/functions/fnc_handleStaminaBar.sqf new file mode 100644 index 0000000000..2f865ac821 --- /dev/null +++ b/addons/advanced_fatigue/functions/fnc_handleStaminaBar.sqf @@ -0,0 +1,44 @@ +/* + * Author: BaerMitUmlaut + * Handles visual changes of the stamina bar. + * + * Arguments: + * Percent of stamina remaining + * + * 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; diff --git a/addons/advanced_fatigue/functions/fnc_moduleSettings.sqf b/addons/advanced_fatigue/functions/fnc_moduleSettings.sqf new file mode 100644 index 0000000000..275e4b7907 --- /dev/null +++ b/addons/advanced_fatigue/functions/fnc_moduleSettings.sqf @@ -0,0 +1,18 @@ +/* + * Author: BaerMitUmlaut + * Initializes the module settings. + * + * Arguments: + * 0: Module + * + * 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); diff --git a/addons/advanced_fatigue/functions/fnc_pfhMain.sqf b/addons/advanced_fatigue/functions/fnc_pfhMain.sqf new file mode 100644 index 0000000000..49db4646af --- /dev/null +++ b/addons/advanced_fatigue/functions/fnc_pfhMain.sqf @@ -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); +}; diff --git a/addons/advanced_fatigue/functions/fnc_removeDutyFactor.sqf b/addons/advanced_fatigue/functions/fnc_removeDutyFactor.sqf new file mode 100644 index 0000000000..0a67f318ac --- /dev/null +++ b/addons/advanced_fatigue/functions/fnc_removeDutyFactor.sqf @@ -0,0 +1,20 @@ +/* + * Author: BaerMitUmlaut + * Removes a duty factor. + * + * Arguments: + * 0: Factor ID + * + * 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; +}; diff --git a/addons/advanced_fatigue/functions/script_component.hpp b/addons/advanced_fatigue/functions/script_component.hpp new file mode 100644 index 0000000000..6f79ffc902 --- /dev/null +++ b/addons/advanced_fatigue/functions/script_component.hpp @@ -0,0 +1 @@ +#include "\z\ace\addons\advanced_fatigue\script_component.hpp" diff --git a/addons/advanced_fatigue/initSettings.sqf b/addons/advanced_fatigue/initSettings.sqf new file mode 100644 index 0000000000..d6c8acd0db --- /dev/null +++ b/addons/advanced_fatigue/initSettings.sqf @@ -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; diff --git a/addons/advanced_fatigue/script_component.hpp b/addons/advanced_fatigue/script_component.hpp new file mode 100644 index 0000000000..bed86c1be8 --- /dev/null +++ b/addons/advanced_fatigue/script_component.hpp @@ -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 diff --git a/addons/advanced_fatigue/stringtable.xml b/addons/advanced_fatigue/stringtable.xml new file mode 100644 index 0000000000..900a5a7b18 --- /dev/null +++ b/addons/advanced_fatigue/stringtable.xml @@ -0,0 +1,57 @@ + + + + + Performance Factor + Leistungsfaktor + + + Influences the overall performance of all players with no custom factor. Higher means better. + Beinflusst die Leistungsfähigkeit aller Spieler ohne eigenen Leistungsfaktor. Ein höherer Wert bedeutet bessere Leistung. + + + Influences the overall performance of this unit. Higher means better. + Beinflusst die Leistungsfähigkeit dieser Einheit. Ein höherer Wert bedeutet bessere Leistung. + + + Recovery Factor + Erholungsfaktor + + + Changes how fast the player recovers when resting. Higher is faster. + Ändert, wie schnell ein Spieler Ausdauer regeneriert. Ein höherer Wert bedeutet eine schnellere Regeneration. + + + Load Factor + Gewichtsfaktor + + + Increases or decreases how much weight influences the players performance. Zero means equipment weight has no performance influence. + Erhöht oder verringert, wie viel Einfluss das Ausrüstungsgewicht auf die Leistung hat. Null heißt, dass es keinen Einfluss hat. + + + Terrain Gradient Factor + Terrainsteigungsfaktor + + + Sets how much steep terrain increases stamina loss. Higher means higher stamina loss. + Beeinflusst, wie stark Steigungen den Ausdauerverbrauch erhöhen. Ein höherer Wert erhöht den Ausdauerverbrauch. + + + Enabled + Aktiv + + + Enables/disables Advanced Fatigue. + Aktiviert/deaktiviert Advanced Fatigue. + + + Show stamina bar + Zeige Ausdauerleiste + + + Shows the stamina bar. + Zeigt die Ausdauerleiste an. + + +