Zeus - Add medical menu module (#9367)

* open medical menu from Zeus

* fix function header

* Zeus can use every action, add room for more buttons in medical menu

* Update addons/medical_treatment/functions/fnc_treatment.sqf

* Added zeus treatment time coeff

* Update addons/medical_gui/functions/fnc_canOpenMenu.sqf

Co-authored-by: PabstMirror <pabstmirror@gmail.com>

* Update fnc_canOpenMenu.sqf

* Update initSettings.sqf

* Update fnc_moduleMedicalMenu.sqf

* Added check + documentation

* Update fnc_canTreat.sqf

---------

Authored-by: Brett <brett@mayson.io>
Co-authored-by: PabstMirror <pabstmirror@gmail.com>
This commit is contained in:
johnb432 2023-11-20 22:52:29 +01:00 committed by GitHub
parent 5d8978981a
commit ac9044f2d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 237 additions and 111 deletions

View File

@ -1,6 +1,6 @@
#include "..\script_component.hpp"
/*
* Author: Glowbal, mharis001
* Author: Glowbal, mharis001, johnb43
* Checks if the player can open the Medical Menu for the target.
*
* Arguments:
@ -18,8 +18,14 @@
params ["_player", "_target"];
alive _player
&& {!IS_UNCONSCIOUS(_player)}
&& {!isNull _target}
&& {_player distance _target < GVAR(maxDistance) || {vehicle _player == vehicle _target}}
&& {GVAR(enableMedicalMenu) == 1 || {GVAR(enableMedicalMenu) == 2 && {vehicle _player != _player || {vehicle _target != _target}}}}
// If in Zeus
if (!isNull findDisplay 312) exitWith {
!isNull _target &&
{missionNamespace getVariable [QGVAR(enableZeusModule), true]} &&
{GVAR(enableMedicalMenu) > 0}
};
_player call EFUNC(common,isAwake) &&
{!isNull _target} &&
{_player distance _target < GVAR(maxDistance) || {vehicle _player == vehicle _target}} &&
{GVAR(enableMedicalMenu) == 1 || {GVAR(enableMedicalMenu) == 2 && {vehicle _player != _player || {vehicle _target != _target}}}}

View File

@ -15,6 +15,9 @@
* Public: No
*/
// If in Zeus, ignore
if (!isNull findDisplay 312) exitWith {};
// Find new target to switch to
private _target = if (
GVAR(target) == ACE_player

View File

@ -16,7 +16,10 @@
*/
// Check if menu should stay open for target
if !([ACE_player, GVAR(target), ["isNotInside", "isNotSwimming"]] call EFUNC(common,canInteractWith) && {[ACE_player, GVAR(target)] call FUNC(canOpenMenu)}) then {
if !(
([ACE_player, GVAR(target), ["isNotInside", "isNotSwimming"]] call EFUNC(common,canInteractWith) || {!isNull findDisplay 312}) && // Allow player to look at himself when unconsious and in Zeus
{[ACE_player, GVAR(target)] call FUNC(canOpenMenu)}
) then {
closeDialog 0;
// Show hint if distance condition failed
if ((ACE_player distance GVAR(target) > GVAR(maxDistance)) && {vehicle ACE_player != vehicle GVAR(target)}) then {

View File

@ -10,7 +10,7 @@
* 3: Treatment <STRING>
*
* Return Value:
* Can Treat <BOOL>
* Can treat <BOOL>
*
* Example:
* [player, cursorObject, "Head", "SurgicalKit"] call ace_medical_treatment_fnc_canTreat
@ -22,17 +22,14 @@ params ["_medic", "_patient", "_bodyPart", "_classname"];
private _config = configFile >> QGVAR(actions) >> _classname;
isClass _config
&& {_patient isKindOf "CAManBase"}
&& {_medic != _patient || {GET_NUMBER_ENTRY(_config >> "allowSelfTreatment") == 1}}
&& {[_medic, GET_NUMBER_ENTRY(_config >> "medicRequired")] call FUNC(isMedic)}
&& {[_medic, _patient, _config] call FUNC(canTreat_holsterCheck)}
&& {
// Conditions that apply, regardless of curator status
(
isClass _config
) && {
_patient isKindOf "CAManBase"
} && {
private _selections = getArray (_config >> "allowedSelections") apply {toLower _x};
"all" in _selections || {_bodyPart in _selections}
} && {
private _items = getArray (_config >> "items");
_items isEqualTo [] || {[_medic, _patient, _items] call FUNC(hasItem)}
} && {
GET_FUNCTION(_condition,_config >> "condition");
@ -46,19 +43,34 @@ isClass _config
_condition
} && {
switch (GET_NUMBER_ENTRY(_config >> "treatmentLocations")) do {
case TREATMENT_LOCATIONS_ALL: {true};
case TREATMENT_LOCATIONS_VEHICLES: {
IN_MED_VEHICLE(_medic) || {IN_MED_VEHICLE(_patient)}
};
case TREATMENT_LOCATIONS_FACILITIES: {
IN_MED_FACILITY(_medic) || {IN_MED_FACILITY(_patient)}
};
case TREATMENT_LOCATIONS_VEHICLES_AND_FACILITIES: {
IN_MED_VEHICLE(_medic) || {IN_MED_VEHICLE(_patient)} || {IN_MED_FACILITY(_medic)} || {IN_MED_FACILITY(_patient)}
};
default {false};
};
} && {
((getNumber (_config >> "allowedUnderwater")) == 1) || {!([_medic] call ace_common_fnc_isSwimming)}
// If in Zeus, the rest of the condition checks can be omitted
(_medic isEqualTo player && {!isNull findDisplay 312}) || {
// Conditions that apply when not in Zeus
(
_medic != _patient || {GET_NUMBER_ENTRY(_config >> "allowSelfTreatment") == 1}
) && {
[_medic, GET_NUMBER_ENTRY(_config >> "medicRequired")] call FUNC(isMedic)
} && {
[_medic, _patient, _config] call FUNC(canTreat_holsterCheck)
} && {
private _items = getArray (_config >> "items");
_items isEqualTo [] || {[_medic, _patient, _items] call FUNC(hasItem)}
} && {
switch (GET_NUMBER_ENTRY(_config >> "treatmentLocations")) do {
case TREATMENT_LOCATIONS_ALL: {true};
case TREATMENT_LOCATIONS_VEHICLES: {
IN_MED_VEHICLE(_medic) || {IN_MED_VEHICLE(_patient)}
};
case TREATMENT_LOCATIONS_FACILITIES: {
IN_MED_FACILITY(_medic) || {IN_MED_FACILITY(_patient)}
};
case TREATMENT_LOCATIONS_VEHICLES_AND_FACILITIES: {
IN_MED_VEHICLE(_medic) || {IN_MED_VEHICLE(_patient)} || {IN_MED_FACILITY(_medic)} || {IN_MED_FACILITY(_patient)}
};
default {false};
};
} && {
!(_medic call EFUNC(common,isSwimming)) || {getNumber (_config >> "allowedUnderwater") == 1}
}
}
}

View File

@ -54,88 +54,98 @@ private _userAndItem = if (GET_NUMBER_ENTRY(_config >> "consumeItem") == 1) then
_userAndItem params ["_itemUser", "_usedItem"];
// Get treatment animation for the medic
private _medicAnim = if (_medic isEqualTo _patient) then {
getText (_config >> ["animationMedicSelf", "animationMedicSelfProne"] select (stance _medic == "PRONE"));
} else {
getText (_config >> ["animationMedic", "animationMedicProne"] select (stance _medic == "PRONE"));
};
private _isInZeus = !isNull findDisplay 312;
_medic setVariable [QGVAR(selectedWeaponOnTreatment), weaponState _medic];
if (_medic isNotEqualTo player || {!_isInZeus}) then {
// Get treatment animation for the medic
private _medicAnim = if (_medic isEqualTo _patient) then {
getText (_config >> ["animationMedicSelf", "animationMedicSelfProne"] select (stance _medic == "PRONE"));
} else {
getText (_config >> ["animationMedic", "animationMedicProne"] select (stance _medic == "PRONE"));
};
// Adjust animation based on the current weapon of the medic
private _wpn = ["non", "rfl", "lnr", "pst"] param [["", primaryWeapon _medic, secondaryWeapon _medic, handgunWeapon _medic] find currentWeapon _medic, "non"];
_medicAnim = [_medicAnim, "[wpn]", _wpn] call CBA_fnc_replace;
_medic setVariable [QGVAR(selectedWeaponOnTreatment), weaponState _medic];
// This animation is missing, use alternative
if (_medicAnim == "AinvPknlMstpSlayWlnrDnon_medic") then {
_medicAnim = "AinvPknlMstpSlayWlnrDnon_medicOther";
};
// Adjust animation based on the current weapon of the medic
private _wpn = ["non", "rfl", "lnr", "pst"] param [["", primaryWeapon _medic, secondaryWeapon _medic, handgunWeapon _medic] find currentWeapon _medic, "non"];
_medicAnim = [_medicAnim, "[wpn]", _wpn] call CBA_fnc_replace;
// Determine the animation length
private _animDuration = GVAR(animDurations) getVariable _medicAnim;
if (isNil "_animDuration") then {
WARNING_2("animation [%1] for [%2] has no duration defined",_medicAnim,_classname);
_animDuration = 10;
};
// This animation is missing, use alternative
if (_medicAnim == "AinvPknlMstpSlayWlnrDnon_medic") then {
_medicAnim = "AinvPknlMstpSlayWlnrDnon_medicOther";
};
// These animations have transitions that take a bit longer...
if (weaponLowered _medic) then {
_animDuration = _animDuration + 0.5;
// Determine the animation length
private _animDuration = GVAR(animDurations) getVariable _medicAnim;
if (isNil "_animDuration") then {
WARNING_2("animation [%1] for [%2] has no duration defined",_medicAnim,_classname);
_animDuration = 10;
};
// Fix problems with lowered weapon transitions by raising the weapon first
if (currentWeapon _medic != "" && {_medicAnim != ""}) then {
_medic action ["WeaponInHand", _medic];
// These animations have transitions that take a bit longer...
if (weaponLowered _medic) then {
_animDuration = _animDuration + 0.5;
// Fix problems with lowered weapon transitions by raising the weapon first
if (currentWeapon _medic != "" && {_medicAnim != ""}) then {
_medic action ["WeaponInHand", _medic];
};
};
if (binocular _medic != "" && {binocular _medic == currentWeapon _medic}) then {
_animDuration = _animDuration + 1;
};
// Play treatment animation for medic and determine the ending animation
if (vehicle _medic == _medic && {_medicAnim != ""}) then {
// Speed up animation based on treatment time (but cap max to prevent odd animiations/cam shake)
private _animRatio = _animDuration / _treatmentTime;
TRACE_3("setAnimSpeedCoef",_animRatio,_animDuration,_treatmentTime);
// Don't slow down animation too much to prevent it looking funny.
if (_animRatio < ANIMATION_SPEED_MIN_COEFFICIENT) then {
_animRatio = ANIMATION_SPEED_MIN_COEFFICIENT;
};
// Skip animation enitrely if progress bar too quick.
if (_animRatio > ANIMATION_SPEED_MAX_COEFFICIENT) exitWith {};
[QEGVAR(common,setAnimSpeedCoef), [_medic, _animRatio]] call CBA_fnc_globalEvent;
// Play animation
private _endInAnim = "AmovP[pos]MstpS[stn]W[wpn]Dnon";
private _pos = ["knl", "pne"] select (stance _medic == "PRONE");
private _stn = "non";
if (_wpn != "non") then {
_stn = ["ras", "low"] select (weaponLowered _medic);
};
_endInAnim = [_endInAnim, "[pos]", _pos] call CBA_fnc_replace;
_endInAnim = [_endInAnim, "[stn]", _stn] call CBA_fnc_replace;
_endInAnim = [_endInAnim, "[wpn]", _wpn] call CBA_fnc_replace;
[_medic, _medicAnim] call EFUNC(common,doAnimation);
[_medic, _endInAnim] call EFUNC(common,doAnimation);
_medic setVariable [QGVAR(endInAnim), _endInAnim];
if (!isNil QEGVAR(advanced_fatigue,setAnimExclusions)) then {
EGVAR(advanced_fatigue,setAnimExclusions) pushBack QUOTE(ADDON);
};
};
// Play a random treatment sound globally if defined
private _soundsConfig = _config >> "sounds";
if (isArray _soundsConfig) then {
(selectRandom (getArray _soundsConfig)) params ["_file", ["_volume", 1], ["_pitch", 1], ["_distance", 10]];
playSound3D [_file, objNull, false, getPosASL _medic, _volume, _pitch, _distance];
};
};
if (binocular _medic != "" && {binocular _medic == currentWeapon _medic}) then {
_animDuration = _animDuration + 1;
};
// Play treatment animation for medic and determine the ending animation
if (vehicle _medic == _medic && {_medicAnim != ""}) then {
// Speed up animation based on treatment time (but cap max to prevent odd animiations/cam shake)
private _animRatio = _animDuration / _treatmentTime;
TRACE_3("setAnimSpeedCoef",_animRatio,_animDuration,_treatmentTime);
// Don't slow down animation too much to prevent it looking funny.
if (_animRatio < ANIMATION_SPEED_MIN_COEFFICIENT) then {
_animRatio = ANIMATION_SPEED_MIN_COEFFICIENT;
};
// Skip animation enitrely if progress bar too quick.
if (_animRatio > ANIMATION_SPEED_MAX_COEFFICIENT) exitWith {};
[QEGVAR(common,setAnimSpeedCoef), [_medic, _animRatio]] call CBA_fnc_globalEvent;
// Play animation
private _endInAnim = "AmovP[pos]MstpS[stn]W[wpn]Dnon";
private _pos = ["knl", "pne"] select (stance _medic == "PRONE");
private _stn = "non";
if (_wpn != "non") then {
_stn = ["ras", "low"] select (weaponLowered _medic);
};
_endInAnim = [_endInAnim, "[pos]", _pos] call CBA_fnc_replace;
_endInAnim = [_endInAnim, "[stn]", _stn] call CBA_fnc_replace;
_endInAnim = [_endInAnim, "[wpn]", _wpn] call CBA_fnc_replace;
[_medic, _medicAnim] call EFUNC(common,doAnimation);
[_medic, _endInAnim] call EFUNC(common,doAnimation);
_medic setVariable [QGVAR(endInAnim), _endInAnim];
if (!isNil QEGVAR(advanced_fatigue,setAnimExclusions)) then {
EGVAR(advanced_fatigue,setAnimExclusions) pushBack QUOTE(ADDON);
};
};
// Play a random treatment sound globally if defined
if (isArray (_config >> "sounds")) then {
selectRandom getArray (_config >> "sounds") params ["_file", ["_volume", 1], ["_pitch", 1], ["_distance", 10]];
playSound3D [_file, objNull, false, getPosASL _medic, _volume, _pitch, _distance];
if (_isInZeus) then {
_treatmentTime = _treatmentTime * GVAR(treatmentTimeCoeffZeus);
};
GET_FUNCTION(_callbackStart,_config >> "callbackStart");
@ -156,7 +166,7 @@ if (_callbackProgress isEqualTo {}) then {
FUNC(treatmentFailure),
getText (_config >> "displayNameProgress"),
_callbackProgress,
["isNotInside", "isNotSwimming"]
["isNotInside", "isNotSwimming", "isNotInZeus"]
] call EFUNC(common,progressBar);
true

View File

@ -20,6 +20,10 @@
params ["_medic", "_patient", "_items"];
if (_medic isEqualTo player && {!isNull findDisplay 312}) exitWith {
[_medic, _items select 0]
};
scopeName "Main";
private _useOrder = [[_patient, _medic], [_medic, _patient], [_medic]] select GVAR(allowSharedEquipment);

View File

@ -305,6 +305,14 @@
true
] call CBA_fnc_addSetting;
[
QGVAR(treatmentTimeCoeffZeus),
"SLIDER",
[LSTRING(TreatmentTimeCoeffZeus_DisplayName), LSTRING(TreatmentTimeCoeffZeus_Description)],
[ELSTRING(medical,Category), LSTRING(SubCategory_Treatment)],
[0, 10, 1, 2]
] call CBA_fnc_addSetting;
[
QGVAR(allowBodyBagUnconscious),
"CHECKBOX",

View File

@ -4860,5 +4860,11 @@
<Japanese>医療品</Japanese>
<Spanish>Objetos médicos</Spanish>
</Key>
<Key ID="STR_ACE_Medical_Treatment_TreatmentTimeCoeffZeus_DisplayName">
<English>Zeus Treatment Time Coefficient</English>
</Key>
<Key ID="STR_ACE_Medical_Treatment_TreatmentTimeCoeffZeus_Description">
<English>Multiply all treatment times with this coefficient when in Zeus.</English>
</Key>
</Package>
</Project>

View File

@ -336,6 +336,13 @@ class CfgVehicles {
function = QFUNC(moduleBurn);
icon = QPATHTOF(ui\Icon_Module_Zeus_Burn_ca.paa);
};
class GVAR(moduleMedicalMenu): GVAR(moduleBase) {
curatorCanAttach = 1;
category = QGVAR(Medical);
displayName = CSTRING(ModuleMedicalMenu_DisplayName);
function = QFUNC(moduleMedicalMenu);
icon = QPATHTOF(UI\Icon_Module_Zeus_Medic_ca.paa);
};
class Man;
class CAManBase: Man {

View File

@ -8,11 +8,11 @@ PREP(bi_moduleRemoteControl);
PREP(canCreateModule);
PREP(getModuleDestination);
PREP(handleZeusUnitAssigned);
PREP(moduleAddArsenal);
PREP(moduleAddAceArsenal);
PREP(moduleAddArsenal);
PREP(moduleAddOrRemoveFRIES);
PREP(moduleAddSpareTrack);
PREP(moduleAddSpareWheel);
PREP(moduleAddOrRemoveFRIES);
PREP(moduleBurn);
PREP(moduleCaptive);
PREP(moduleCargoParadrop);
@ -23,13 +23,14 @@ PREP(moduleGroupSide);
PREP(moduleHeal);
PREP(moduleLayTrench);
PREP(moduleLoadIntoCargo);
PREP(moduleRemoveArsenal);
PREP(moduleMedicalMenu);
PREP(moduleRemoveAceArsenal);
PREP(moduleRemoveArsenal);
PREP(moduleSearchNearby);
PREP(moduleSetEngineer);
PREP(moduleSetMedic);
PREP(moduleSetMedicalVehicle);
PREP(moduleSetMedicalFacility);
PREP(moduleSetMedicalVehicle);
PREP(moduleSetRepairFacility);
PREP(moduleSetRepairVehicle);
PREP(moduleSimulation);

View File

@ -49,7 +49,8 @@ class CfgPatches {
QGVAR(moduleUnconscious),
QGVAR(moduleSetMedic),
QGVAR(moduleSetMedicalVehicle),
QGVAR(moduleSetMedicalFacility)
QGVAR(moduleSetMedicalFacility),
QGVAR(moduleMedicalMenu)
};
};
class GVAR(cargo): ADDON {

View File

@ -0,0 +1,41 @@
#include "..\script_component.hpp"
/*
* Author: Brett Mayson
* Opens the medical menu for the unit
*
* Arguments:
* 0: The module logic <OBJECT>
*
* Return Value:
* None
*
* Example:
* [LOGIC] call ace_zeus_fnc_moduleMedicalMenu
*
* Public: No
*/
params ["_logic"];
if !(local _logic) exitWith {};
private _unit = attachedTo _logic;
deleteVehicle _logic;
switch (false) do {
case !(isNull _unit): {
[LSTRING(NothingSelected)] call FUNC(showMessage);
};
case (_unit isKindOf "CAManBase"): {
[LSTRING(OnlyInfantry)] call FUNC(showMessage);
};
case (["ace_medical_gui"] call EFUNC(common,isModLoaded)): {
[LSTRING(RequiresAddon)] call FUNC(showMessage);
};
case ([objNull, _unit] call EFUNC(medical_gui,canOpenMenu)): {
[LSTRING(MedicalMenuDisabled)] call FUNC(showMessage);
};
default {
[_unit] call EFUNC(medical_gui,openMenu);
};
};

View File

@ -1934,6 +1934,25 @@
<Spanish>Quemar a unidad</Spanish>
<French>Brûler l'unité</French>
</Key>
<Key ID="STR_ACE_Zeus_ModuleMedicalMenu_DisplayName">
<English>Medical Menu</English>
<German>Sanitätsmenü</German>
<Polish>Menu medyczne</Polish>
<Portuguese>Menu Médico</Portuguese>
<Russian>Медицинское меню</Russian>
<Spanish>Menú médico</Spanish>
<Czech>Zdravotnická nabídka</Czech>
<Italian>Menù Medico</Italian>
<French>Menu médical</French>
<Japanese>治療メニュー</Japanese>
<Korean>의료 메뉴</Korean>
<Chinesesimp>医疗菜单</Chinesesimp>
<Chinese>醫療選單</Chinese>
<Turkish>Medikal Menü</Turkish>
</Key>
<Key ID="STR_ACE_Zeus_MedicalMenuDisabled">
<English>The medical menu is disabled</English>
</Key>
<Key ID="STR_ACE_Zeus_ModuleLayTrenchline_DisplayName">
<English>Lay Trenchline</English>
<Polish>Wykop Okop</Polish>

View File

@ -70,3 +70,8 @@ The object's rotation can also be modified, if necessary.
```sqf
ace_medical_treatment_graveRotation = 0; // rotation angle (will depend on model classname)
```
### 2.2 Zeus Medical Menu Module
If a mission maker wishes to disable Zeus access to the medical menu, they can set the variable below:
ace_medical_gui_enableZeusModule = false; // default is true