diff --git a/addons/medical/functions/fnc_adjustPainLevel.sqf b/addons/medical/functions/fnc_adjustPainLevel.sqf index 94b246bae39..dc80f4f7222 100644 --- a/addons/medical/functions/fnc_adjustPainLevel.sqf +++ b/addons/medical/functions/fnc_adjustPainLevel.sqf @@ -21,6 +21,8 @@ params ["_unit", "_addedPain"]; if (!local _unit) exitWith { ERROR_1("unit [%1] is not local",_unit); }; +if (!IS_MEDICAL_ACTIVITY(_unit)) then { [QGVAR(activateMedical), _unit] call CBA_fnc_localEvent; }; + private _pain = GET_PAIN(_unit); _pain = 0 max (_pain + _addedPain) min 1; diff --git a/addons/medical/functions/fnc_deserializeState.sqf b/addons/medical/functions/fnc_deserializeState.sqf index a9057a42b8e..54924498aaa 100644 --- a/addons/medical/functions/fnc_deserializeState.sqf +++ b/addons/medical/functions/fnc_deserializeState.sqf @@ -125,4 +125,7 @@ if (_currentState in ["Unconscious", "CardiacArrest"] && {_targetState in ["Defa [_unit, false] call EFUNC(medical_status,setUnconsciousState); }; +// Manually activate if non-defaults are present +[_unit] call EFUNC(medical_engine,checkForMedicalActivity); + _state call CBA_fnc_deleteNamespace; diff --git a/addons/medical/functions/fnc_fullHeal.sqf b/addons/medical/functions/fnc_fullHeal.sqf index 7c7a2cb13b8..be8b966e822 100644 --- a/addons/medical/functions/fnc_fullHeal.sqf +++ b/addons/medical/functions/fnc_fullHeal.sqf @@ -27,4 +27,6 @@ if (!alive _patient) exitWith { ERROR_2("fullHeal [medic %1][patient %2] Patient is dead or null",_medic,_patient); }; +if (!IS_MEDICAL_ACTIVITY(_patient)) then { [QGVAR(activateMedical), _patient, _patient] call CBA_fnc_targetEvent; }; + [_medic, _patient, "", "", objNull, "", false, _logMessage] call EFUNC(medical_treatment,fullHeal); diff --git a/addons/medical/functions/fnc_setUnconscious.sqf b/addons/medical/functions/fnc_setUnconscious.sqf index 050cfa0ce2d..2c0dbcaa71d 100644 --- a/addons/medical/functions/fnc_setUnconscious.sqf +++ b/addons/medical/functions/fnc_setUnconscious.sqf @@ -43,6 +43,8 @@ if (_knockOut isEqualTo IS_UNCONSCIOUS(_unit)) exitWith { false }; +if (!IS_MEDICAL_ACTIVITY(_unit)) then { [QGVAR(activateMedical), _unit] call CBA_fnc_localEvent; }; + if (currentWeapon _unit != primaryWeapon _unit) then { _unit selectWeapon primaryWeapon _unit; }; diff --git a/addons/medical_engine/XEH_PREP.hpp b/addons/medical_engine/XEH_PREP.hpp index ffc3543745d..97147c60ff0 100644 --- a/addons/medical_engine/XEH_PREP.hpp +++ b/addons/medical_engine/XEH_PREP.hpp @@ -1,4 +1,5 @@ PREP(applyAnimAfterRagdoll); +PREP(checkForMedicalActivity); PREP(damageBodyPart); PREP(disableThirdParty); PREP(getHitpointArmor); diff --git a/addons/medical_engine/XEH_postInit.sqf b/addons/medical_engine/XEH_postInit.sqf index 70a7489136c..e39d77ce8fb 100644 --- a/addons/medical_engine/XEH_postInit.sqf +++ b/addons/medical_engine/XEH_postInit.sqf @@ -8,6 +8,7 @@ ["CAManBase", "init", { params ["_unit"]; + [LINKFUNC(checkForMedicalActivity), _unit, 2] call CBA_fnc_waitAndExecute; if (unitIsUAV _unit) exitWith {TRACE_1("ignore UAV AI",typeOf _unit);}; if (getNumber (configOf _unit >> "isPlayableLogic") == 1) exitWith {TRACE_1("ignore logic unit",typeOf _unit);}; diff --git a/addons/medical_engine/XEH_preInit.sqf b/addons/medical_engine/XEH_preInit.sqf index 95b51d0411b..9922c128f67 100644 --- a/addons/medical_engine/XEH_preInit.sqf +++ b/addons/medical_engine/XEH_preInit.sqf @@ -20,6 +20,7 @@ if (isNil QUOTE(PAIN_FADE_TIME)) then {PAIN_FADE_TIME = PAIN_FADE_TIME_DEFAULT}; if (isNil QUOTE(LIMPING_DAMAGE_THRESHOLD)) then {LIMPING_DAMAGE_THRESHOLD = LIMPING_DAMAGE_THRESHOLD_DEFAULT}; if (isNil QUOTE(FRACTURE_DAMAGE_THRESHOLD)) then {FRACTURE_DAMAGE_THRESHOLD = FRACTURE_DAMAGE_THRESHOLD_DEFAULT}; if (isNil QUOTE(CARDIAC_OUTPUT_MIN)) then {CARDIAC_OUTPUT_MIN = CARDIAC_OUTPUT_MIN_DEFAULT}; +if (isNil QUOTE(MEDICAL_ACTIVITY)) then {MEDICAL_ACTIVITY = MEDICAL_ACTIVITY_DEFAULT}; // Derive the alternate fatal damage coefficients if (isNil QUOTE(FATAL_SUM_DAMAGE_WEIBULL_K) || isNil QUOTE(FATAL_SUM_DAMAGE_WEIBULL_L)) then { private _x1 = 0.5; diff --git a/addons/medical_engine/functions/fnc_checkForMedicalActivity.sqf b/addons/medical_engine/functions/fnc_checkForMedicalActivity.sqf new file mode 100644 index 00000000000..baa83e9ccf1 --- /dev/null +++ b/addons/medical_engine/functions/fnc_checkForMedicalActivity.sqf @@ -0,0 +1,48 @@ +#include "..\script_component.hpp" +/* + * Author: PabstMirror + * Checks for scripted medical activity on a unit + * + * Arguments: + * 0: Unit + * + * Return Value: + * None + * + * Example: + * [player] call ace_medical_engine_fnc_checkForMedicalActivity + * + * Public: No + */ +params ["_unit"]; +TRACE_1("checkForMedicalActivity",_unit); + +if (!alive _unit || {!local _unit}) exitWith {}; +if (IS_MEDICAL_ACTIVITY(_unit)) exitWith {}; + +{ + _x params ["_var", "_default"]; + if (_unit isNil _var) then { continue }; + private _current = _unit getVariable [_var, _default]; + if (_current isNotEqualTo _default) exitWith { + TRACE_2("unit has non-defaults",_unit,_var); + [QEGVAR(medical,activateMedical), _unit] call CBA_fnc_localEvent; + }; +} forEach [ + [VAR_BLOOD_VOL, DEFAULT_BLOOD_VOLUME], + [VAR_HEART_RATE, DEFAULT_HEART_RATE], + [VAR_BLOOD_PRESS, [80, 120]], + [VAR_HEMORRHAGE, 0], + [VAR_PAIN, 0], + [VAR_PAIN_SUPP, 0], + [VAR_OPEN_WOUNDS, createHashMap], + [VAR_BANDAGED_WOUNDS, createHashMap], + [VAR_STITCHED_WOUNDS, createHashMap], + [VAR_FRACTURES, DEFAULT_FRACTURE_VALUES], + [VAR_TOURNIQUET, DEFAULT_TOURNIQUET_VALUES], + [VAR_MEDICATIONS, []], + [QEGVAR(medical,occludedMedications), []], + [QEGVAR(medical,ivBags), []], + [VAR_BODYPART_DAMAGE, DEFAULT_BODYPART_DAMAGE_VALUES] +]; + diff --git a/addons/medical_engine/script_macros_medical.hpp b/addons/medical_engine/script_macros_medical.hpp index 1f7c2aee6bd..2b238d26ca0 100644 --- a/addons/medical_engine/script_macros_medical.hpp +++ b/addons/medical_engine/script_macros_medical.hpp @@ -223,3 +223,9 @@ // Ignore UAV/Drone AI Base Classes #define IGNORE_BASE_UAVPILOTS "B_UAV_AI", "O_UAV_AI", "UAV_AI_base_F" + +// Medical Activity (optimization for AI) +#define VAR_MEDICAL_ACTIVITY QEGVAR(medical,medicalActivity) +#define MEDICAL_ACTIVITY EGVAR(medical,const_medicalActivity) +#define MEDICAL_ACTIVITY_DEFAULT false +#define IS_MEDICAL_ACTIVITY(unit) (unit getVariable [VAR_MEDICAL_ACTIVITY, MEDICAL_ACTIVITY]) diff --git a/addons/medical_statemachine/XEH_postInit.sqf b/addons/medical_statemachine/XEH_postInit.sqf index a7c7740a390..6b6c601a731 100644 --- a/addons/medical_statemachine/XEH_postInit.sqf +++ b/addons/medical_statemachine/XEH_postInit.sqf @@ -12,3 +12,26 @@ [{_this enableSimulation true}, _unit, 2] call CBA_fnc_waitAndExecute; }; }] call CBA_fnc_addEventHandler; + + +[QEGVAR(medical,activateMedical), { // should be called where unit is local + params ["_unit"]; + _unit setVariable [VAR_MEDICAL_ACTIVITY, true, true]; + TRACE_2("activating medical",_unit,typeOf _unit); + // Because vitals loop has never run, set to 1 second ago to avoid large time delta (would hit max of 10 seconds) + if (_unit isNil QEGVAR(medical_vitals,lastTimeUpdated)) then { + _unit setVariable [QEGVAR(medical_vitals,lastTimeUpdated), CBA_missionTime - 1]; + }; +}] call CBA_fnc_addEventHandler; +[QEGVAR(medical,woundReceived), { + params ["_unit"]; + if (!IS_MEDICAL_ACTIVITY(_unit)) then { + [QEGVAR(medical,activateMedical), _unit] call CBA_fnc_localEvent; + }; +}] call CBA_fnc_addEventHandler; +["ace_treatmentStarted", { + params ["", "_patient"]; + if (!IS_MEDICAL_ACTIVITY(_patient)) then { + [QEGVAR(medical,activateMedical), _patient, _patient] call CBA_fnc_targetEvent; + }; +}] call CBA_fnc_addEventHandler; diff --git a/addons/medical_statemachine/functions/fnc_handleStateDefault.sqf b/addons/medical_statemachine/functions/fnc_handleStateDefault.sqf index 89e09b0d4cd..11ce7103a5c 100644 --- a/addons/medical_statemachine/functions/fnc_handleStateDefault.sqf +++ b/addons/medical_statemachine/functions/fnc_handleStateDefault.sqf @@ -19,6 +19,7 @@ params ["_unit"]; // If the unit died the loop is finished if (!alive _unit || {!local _unit}) exitWith {}; +if !((isPlayer _unit) || {IS_MEDICAL_ACTIVITY(_unit)}) exitWith {}; if ([_unit] call EFUNC(medical_vitals,handleUnitVitals)) then { // returns true when update ran private _painLevel = GET_PAIN_PERCEIVED(_unit); diff --git a/docs/wiki/framework/medical-framework.md b/docs/wiki/framework/medical-framework.md index 744334f048b..8038266f2f9 100644 --- a/docs/wiki/framework/medical-framework.md +++ b/docs/wiki/framework/medical-framework.md @@ -254,3 +254,14 @@ ACE will lock the seat of an unconscious or dead unit to prevent automatic unloa ace_medical_engine_disableSeatLocking = true; // disable on everything ace_medical_engine_disableSeatLocking = ["ship"]; // disable just on boats ``` + +### 5.2 Running vitals loop on untouched AI +For performance ACE will skip running vitals loop on AI that have never been wounded or treated. This can be disabled by setting: +```sqf +// always run all vitals calculations on all AI +ace_medical_const_medicalActivity = true; + +// specific AI +unit setVariable ["ace_medical_medicalActivity", true] +``` +Once medical activity has been enabled, you can't disable it. In other words: if you execute `unit setVariable ["ace_medical_medicalActivity", true]`, you can't disable medical activity for that specific AI anymore. If you run `ace_medical_const_medicalActivity = true;`, you can no longer set it to `false` and all AI will have their medical activity enabled.