diff --git a/addons/common/XEH_PREP.hpp b/addons/common/XEH_PREP.hpp index 2706ca45e2..323f2c1d0d 100644 --- a/addons/common/XEH_PREP.hpp +++ b/addons/common/XEH_PREP.hpp @@ -105,6 +105,7 @@ PREP(handleModifierKey); PREP(handleModifierKeyUp); PREP(hasItem); PREP(hasMagazine); +PREP(hasZeusAccess); PREP(headBugFix); PREP(hideUnit); PREP(interpolateFromArray); diff --git a/addons/common/functions/fnc_codeToString.sqf b/addons/common/functions/fnc_codeToString.sqf index b938148220..8d746e9024 100644 --- a/addons/common/functions/fnc_codeToString.sqf +++ b/addons/common/functions/fnc_codeToString.sqf @@ -16,10 +16,7 @@ */ params ["_code"]; + if (_code isEqualType "") exitWith {_code}; -_code = str(_code); -_code = _code select [1, count _code - 2]; - -_code - +toString _code diff --git a/addons/common/functions/fnc_hasZeusAccess.sqf b/addons/common/functions/fnc_hasZeusAccess.sqf new file mode 100644 index 0000000000..057e4b1f0f --- /dev/null +++ b/addons/common/functions/fnc_hasZeusAccess.sqf @@ -0,0 +1,19 @@ +#include "script_component.hpp" +/* + * Author: LinkIsGrim + * Check if current player has Zeus access + * + * Arguments: + * None + * + * Return Value: + * Has Zeus + * + * Example: + * call ace_common_fnc_hasZeusAccess + * + * Public: Yes + */ + +// Use of player is intentional +!isNull getAssignedCuratorLogic player // return diff --git a/addons/dragging/CfgEventHandlers.hpp b/addons/dragging/CfgEventHandlers.hpp index d1f8a87636..8903f2ded8 100644 --- a/addons/dragging/CfgEventHandlers.hpp +++ b/addons/dragging/CfgEventHandlers.hpp @@ -40,6 +40,11 @@ class Extended_Init_EventHandlers { init = QUOTE(_this call DFUNC(initObject)); }; }; + class WeaponHolder { + class ADDON { + init = QUOTE(_this call DFUNC(initObject)); + }; + }; class Land_Camping_Light_F { class ADDON { init = QUOTE(_this call DFUNC(initObject)); diff --git a/addons/dragging/CfgVehicles.hpp b/addons/dragging/CfgVehicles.hpp index cea1675565..c972a6514f 100644 --- a/addons/dragging/CfgVehicles.hpp +++ b/addons/dragging/CfgVehicles.hpp @@ -226,6 +226,32 @@ class CfgVehicles { GVAR(canCarry) = 1; }; + // weapons dropped from dead body + class WeaponHolderSimulated: ThingX { + GVAR(canCarry) = 1; + GVAR(carryPosition[]) = {0,0.5,1.3}; + GVAR(carryDirection) = 0; + + // z-position floats from -1.2 to >0 + // it's OK for carrying but odd for dragging + // needs workaround to drag correctly. Disabled ATM + GVAR(canDrag) = 0; + GVAR(dragPosition[]) = {0,1,0}; + GVAR(dragDirection) = 0; + }; + + class ReammoBox; + // dropped weapons/gear + class WeaponHolder: ReammoBox { + GVAR(canCarry) = 1; + GVAR(carryPosition[]) = {0,0.5,1}; + GVAR(carryDirection) = 0; + + GVAR(canDrag) = 1; + GVAR(dragPosition[]) = {0,1,0}; + GVAR(dragDirection) = 0; + }; + class Lamps_base_F; class Land_PortableLight_single_F: Lamps_base_F { GVAR(canCarry) = 1; diff --git a/addons/dragging/functions/fnc_canCarry.sqf b/addons/dragging/functions/fnc_canCarry.sqf index 2483903eef..dac874f6c0 100644 --- a/addons/dragging/functions/fnc_canCarry.sqf +++ b/addons/dragging/functions/fnc_canCarry.sqf @@ -1,6 +1,6 @@ #include "script_component.hpp" /* - * Author: commy2 + * Author: commy2, Dystopian * Check if unit can carry the object. Doesn't check weight. * * Arguments: @@ -11,20 +11,35 @@ * Can the unit carry the object? * * Example: - * [player, cursorTarget] call ace_dragging_fnc_canCarry; + * [player, cursorTarget] call ace_dragging_fnc_canCarry * * Public: No */ params ["_unit", "_target"]; +if !(alive _target && {_target getVariable [QGVAR(canCarry), false]} && {isNull objectParent _target}) exitWith {false}; + if !([_unit, _target, []] call EFUNC(common,canInteractWith)) exitWith {false}; //#2644 - Units with injured legs cannot bear the extra weight of carrying an object //The fireman carry animation does not slow down for injured legs, so you could carry and run if ((_unit getHitPointDamage "HitLegs") >= 0.5) exitWith {false}; -// a static weapon has to be empty for dragging (ignore UAV AI) -if (((typeOf _target) isKindOf "StaticWeapon") && {{(getText (configOf _x >> "simulation")) != "UAVPilot"} count crew _target > 0}) exitWith {false}; +// Static weapons need to be empty for carrying (ignore UAV AI) +if (_target isKindOf "StaticWeapon") exitWith { + crew _target findIf {getText (configOf _x >> "simulation") != "UAVPilot"} == -1 +}; -alive _target && {vehicle _target isEqualto _target} && {_target getVariable [QGVAR(canCarry), false]} && {animationState _target in ["", "unconscious"] || (_target getVariable ["ACE_isUnconscious", false]) || (_target isKindOf "CAManBase" && {(_target getHitPointDamage "HitLegs") > 0.4})} +// Units need to be unconscious or limping +if (_target isKindOf "CAManBase") exitWith { + lifeState _target isEqualTo "INCAPACITATED" + || {_target getHitPointDamage "HitLegs" >= 0.5} +}; + +// Check max items for WeaponHolders +if (["WeaponHolder", "WeaponHolderSimulated"] findIf {_target isKindOf _x} != -1) exitWith { + (count (weaponCargo _target + magazineCargo _target + itemCargo _target)) <= MAX_DRAGGED_ITEMS +}; + +true // return diff --git a/addons/dragging/functions/fnc_canDrag.sqf b/addons/dragging/functions/fnc_canDrag.sqf index afc679e375..8336e20f2d 100644 --- a/addons/dragging/functions/fnc_canDrag.sqf +++ b/addons/dragging/functions/fnc_canDrag.sqf @@ -1,6 +1,6 @@ #include "script_component.hpp" /* - * Author: commy2 + * Author: commy2, Dystopian * Check if unit can drag the object. Doesn't check weight. * * Arguments: @@ -11,16 +11,31 @@ * Can the unit drag the object? * * Example: - * [player, cursorTarget] call ace_dragging_fnc_canDrag; + * [player, cursorTarget] call ace_dragging_fnc_canDrag * * Public: No */ params ["_unit", "_target"]; +if !(alive _target && {_target getVariable [QGVAR(canDrag), false]} && {isNull objectParent _target}) exitWith {false}; + if !([_unit, _target, ["isNotSwimming"]] call EFUNC(common,canInteractWith)) exitWith {false}; -// a static weapon has to be empty for dragging (ignore UAV AI) -if ((typeOf _target) isKindOf "StaticWeapon" && {{(getText (configOf _x >> "simulation")) != "UAVPilot"} count crew _target > 0}) exitWith {false}; +// Static weapons need to be empty for dragging (ignore UAV AI) +if (_target isKindOf "StaticWeapon") exitWith { + crew _target findIf {getText (configOf _x >> "simulation") != "UAVPilot"} == -1 +}; -alive _target && {vehicle _target isEqualto _target} && {_target getVariable [QGVAR(canDrag), false]} && {animationState _target in ["", "unconscious"] || (_target getVariable ["ACE_isUnconscious", false]) || (_target isKindOf "CAManBase" && {(_target getHitPointDamage "HitLegs") > 0.4})} +// Units need to be unconscious or limping +if (_target isKindOf "CAManBase") exitWith { + lifeState _target isEqualTo "INCAPACITATED" + || {_target getHitPointDamage "HitLegs" >= 0.5} +}; + +// Check max items for WeaponHolders +if (["WeaponHolder", "WeaponHolderSimulated"] findIf {_target isKindOf _x} != -1) exitWith { + (count (weaponCargo _target + magazineCargo _target + itemCargo _target)) <= MAX_DRAGGED_ITEMS +}; + +true // return diff --git a/addons/dragging/functions/fnc_carryObjectPFH.sqf b/addons/dragging/functions/fnc_carryObjectPFH.sqf index 93e35547ad..072b688b9d 100644 --- a/addons/dragging/functions/fnc_carryObjectPFH.sqf +++ b/addons/dragging/functions/fnc_carryObjectPFH.sqf @@ -33,8 +33,8 @@ if !(_unit getVariable [QGVAR(isCarrying), false]) exitWith { [_idPFH] call CBA_fnc_removePerFrameHandler; }; -// drop if the crate is destroyed OR (target moved away from carrier (weapon disasembled)) -if (!alive _target || {_unit distance _target > 10}) exitWith { +// drop if the crate is destroyed OR target moved away from carrier (weapon disassembled) OR carrier starts limping +if !(alive _target && {_unit distance _target <= 10} && {_unit getHitPointDamage "HitLegs" < 0.5}) exitWith { TRACE_2("dead/distance",_unit,_target); if ((_unit distance _target > 10) && {(CBA_missionTime - _startTime) < 1}) exitWith { //attachTo seems to have some kind of network delay and target can return an odd position during the first few frames, diff --git a/addons/dragging/script_component.hpp b/addons/dragging/script_component.hpp index 5befaede9c..ed1ad0fb37 100644 --- a/addons/dragging/script_component.hpp +++ b/addons/dragging/script_component.hpp @@ -20,3 +20,5 @@ #define DRAG_ANIMATIONS ["amovpercmstpslowwrfldnon_acinpknlmwlkslowwrfldb_2", "amovpercmstpsraswpstdnon_acinpknlmwlksnonwpstdb_2", "amovpercmstpsnonwnondnon_acinpknlmwlksnonwnondb_2", "acinpknlmstpsraswrfldnon", "acinpknlmstpsnonwpstdnon", "acinpknlmstpsnonwnondnon", "acinpknlmwlksraswrfldb", "acinpknlmwlksnonwnondb", "ace_dragging", "ace_dragging_static", "ace_dragging_drop"] #define CARRY_ANIMATIONS ["acinpercmstpsnonwnondnon", "acinpknlmstpsnonwnondnon_acinpercmrunsnonwnondnon"] + +#define MAX_DRAGGED_ITEMS 3 diff --git a/addons/field_rations/CfgWeapons.hpp b/addons/field_rations/CfgWeapons.hpp index 760d41290f..b100ba4e90 100644 --- a/addons/field_rations/CfgWeapons.hpp +++ b/addons/field_rations/CfgWeapons.hpp @@ -40,6 +40,7 @@ class CfgWeapons { XGVAR(replacementItem) = "ACE_WaterBottle_Half"; XGVAR(consumeAnims)[] = {QGVAR(drinkStand), QGVAR(drinkCrouch), QGVAR(drinkProne)}; XGVAR(consumeSounds)[] = {QGVAR(drink1), QGVAR(drink1), QGVAR(drink2)}; + ACE_isFieldRationItem = 1; }; class ACE_WaterBottle_Half: ACE_WaterBottle { @@ -87,6 +88,7 @@ class CfgWeapons { XGVAR(replacementItem) = "ACE_Canteen_Half"; XGVAR(consumeAnims)[] = {QGVAR(drinkStand), QGVAR(drinkCrouch), QGVAR(drinkProne)}; XGVAR(consumeSounds)[] = {QGVAR(drink1), QGVAR(drink1), QGVAR(drink2)}; + ACE_isFieldRationItem = 1; }; class ACE_Canteen_Half: ACE_Canteen { @@ -132,6 +134,7 @@ class CfgWeapons { XGVAR(consumeText) = CSTRING(DrinkingX); XGVAR(consumeAnims)[] = {QGVAR(drinkStandCan), QGVAR(drinkCrouchCan), QGVAR(drinkProneCan)}; XGVAR(consumeSounds)[] = {QGVAR(drinkCan1), QGVAR(drinkCan1), QGVAR(drinkCan2)}; + ACE_isFieldRationItem = 1; }; class ACE_Can_Franta: ACE_Can_Spirit { @@ -164,6 +167,7 @@ class CfgWeapons { XGVAR(consumeTime) = 10; XGVAR(hungerSatiated) = 20; XGVAR(consumeText) = CSTRING(EatingX); + ACE_isFieldRationItem = 1; }; class ACE_MRE_BeefStew: ACE_MRE_LambCurry { @@ -225,7 +229,7 @@ class CfgWeapons { model = QPATHTOF(data\mre_human.p3d); picture = QPATHTOF(ui\item_mre_human_co.paa); }; - + // - Misc Food ------------------------------------------------------------ class ACE_Sunflower_Seeds: ACE_ItemCore { author = ECSTRING(common,ACETeam); @@ -240,5 +244,6 @@ class CfgWeapons { XGVAR(consumeTime) = 10; XGVAR(hungerSatiated) = 10; XGVAR(consumeText) = CSTRING(EatingX); + ACE_isFieldRationItem = 1; }; }; diff --git a/addons/field_rations/XEH_PREP.hpp b/addons/field_rations/XEH_PREP.hpp index 68833a495b..2035345f47 100644 --- a/addons/field_rations/XEH_PREP.hpp +++ b/addons/field_rations/XEH_PREP.hpp @@ -14,5 +14,6 @@ ACEX_PREP(handleEffects); ACEX_PREP(handleHUD); ACEX_PREP(handleRespawn); ACEX_PREP(refillItem); +ACEX_PREP(scanFieldRations); ACEX_PREP(setRemainingWater); ACEX_PREP(update); diff --git a/addons/field_rations/XEH_preInit.sqf b/addons/field_rations/XEH_preInit.sqf index c4efb567ed..ede921483e 100644 --- a/addons/field_rations/XEH_preInit.sqf +++ b/addons/field_rations/XEH_preInit.sqf @@ -8,6 +8,8 @@ PREP_RECOMPILE_END; #include "initSettings.sqf" +#define ARSENAL_CATEGORY_ICON QPATHTOF(ui\icon_survival.paa) + // Init arrays of status modifiers GVAR(thirstModifiers) = []; GVAR(hungerModifiers) = []; @@ -18,4 +20,9 @@ GVAR(waterSourceP3Ds) = _cache select 0; // List of refill action offsets corresponding to the p3ds in the array above GVAR(waterSourceOffsets) = _cache select 1; +// Custom Arsenal Tab +if (["ace_arsenal"] call EFUNC(common,isModLoaded)) then { + [keys FIELD_RATIONS_ITEMS, LLSTRING(DisplayName), ARSENAL_CATEGORY_ICON] call EFUNC(arsenal,addRightPanelButton); +}; + ADDON = true; diff --git a/addons/field_rations/XEH_preStart.sqf b/addons/field_rations/XEH_preStart.sqf index eec59ff8f7..88109cf9ee 100644 --- a/addons/field_rations/XEH_preStart.sqf +++ b/addons/field_rations/XEH_preStart.sqf @@ -30,3 +30,5 @@ private _waterSourceOffsets = [ uiNamespace setVariable [QGVAR(cacheP3Ds), compileFinal str [_waterSourceP3Ds, _waterSourceOffsets]]; TRACE_1("compiled",count _waterSourceP3Ds); + +call FUNC(scanFieldRations); diff --git a/addons/field_rations/functions/fnc_scanFieldRations.sqf b/addons/field_rations/functions/fnc_scanFieldRations.sqf new file mode 100644 index 0000000000..395b3ba45b --- /dev/null +++ b/addons/field_rations/functions/fnc_scanFieldRations.sqf @@ -0,0 +1,34 @@ +#include "script_component.hpp" +/* + * Author: Salluci + * Caches all item classnames used as field rations, their thirst/hunger values, and whether they are treated as magazines + * + * Arguments: + * None + * + * Return Value: + * None + * + * Example: + * call acex_field_rations_fnc_scanFieldRations + * + * Public: No + */ + +private _list = createHashMap; +private _cfgWeapons = configFile >> "CfgWeapons"; +private _cfgMagazines = configFile >> "CfgMagazines"; + +private _fnc_isFieldRationItem = toString { + (getNumber (_x >> "ACE_isFieldRationItem") isEqualTo 1) || {(getNumber (_x >> QXGVAR(thirstQuenched))) > 0} || {(getNumber (_x >> QXGVAR(hungerSatiated))) > 0} || {(getText (_x >> QXGVAR(refillItem))) isNotEqualTo ""} +}; + +{ + _list set [configName _x, ""]; +} forEach (_fnc_isFieldRationItem configClasses _cfgWeapons); + +{ + _list set [configName _x, ""]; +} forEach (_fnc_isFieldRationItem configClasses _cfgMagazines); + +uiNamespace setVariable [QXGVAR(fieldRationItems), compileFinal str _list]; diff --git a/addons/field_rations/script_component.hpp b/addons/field_rations/script_component.hpp index 9811493029..bcde75fe9c 100644 --- a/addons/field_rations/script_component.hpp +++ b/addons/field_rations/script_component.hpp @@ -33,3 +33,5 @@ #define IDC_DRAINING_HUD_THIRST_ICON 7750 #define IDC_DRAINING_HUD_HUNGER_GROUP 7840 #define IDC_DRAINING_HUD_HUNGER_ICON 7850 + +#define FIELD_RATIONS_ITEMS (createHashMapFromArray (call (uiNamespace getVariable [QXGVAR(fieldRationItems), {createHashMap}]))) diff --git a/addons/gunbag/XEH_preInit.sqf b/addons/gunbag/XEH_preInit.sqf index 78c6741dbb..7829c58074 100644 --- a/addons/gunbag/XEH_preInit.sqf +++ b/addons/gunbag/XEH_preInit.sqf @@ -49,6 +49,11 @@ PREP_RECOMPILE_END; private _gunbagWeapon = _extendedInfo getOrDefault [QGVAR(gunbagWeapon), []]; if (_gunbagWeapon isNotEqualTo []) then { (backpackContainer _unit) setVariable [QGVAR(gunbagWeapon), _gunbagWeapon, true]; + + // Prevent the arsenal closed event from overwriting new info + if (!isNil QGVAR(arsenalCache)) then { + GVAR(arsenalCache) = _gunbagWeapon; + }; }; }] call CBA_fnc_addEventHandler; diff --git a/addons/interaction/CfgVehicles.hpp b/addons/interaction/CfgVehicles.hpp index c88e93c095..57753dfaff 100644 --- a/addons/interaction/CfgVehicles.hpp +++ b/addons/interaction/CfgVehicles.hpp @@ -162,6 +162,12 @@ class CfgVehicles { exceptions[] = {"isNotSwimming"}; icon = "\a3\ui_f\data\IGUI\Cfg\simpleTasks\types\getout_ca.paa"; }; + class GVAR(Gear) { + displayName = "$STR_ACTION_GEAR"; + condition = QUOTE(!(lifeState _target in [ARR_2('HEALTHY','INJURED')]) && {isNull objectParent _target}); + statement = QUOTE(_player action [ARR_2(QUOTE(QUOTE(Gear)),_target)]); + icon = "\A3\ui_f\data\igui\cfg\actions\gear_ca.paa"; + }; }; class ACE_Torso { @@ -693,6 +699,41 @@ class CfgVehicles { class ACE_SelfActions {}; }; + // weapons dropped from dead body + class WeaponHolderSimulated: ThingX { + class ACE_Actions { + class ACE_MainActions { + displayName = CSTRING(MainAction); + distance = 3; + position = QUOTE(_target worldToModel ASLToAGL getPosASL _target); + + class GVAR(Gear) { + displayName = "$STR_ACTION_GEAR"; + statement = QUOTE(_player action [ARR_2(QUOTE(QUOTE(Gear)),_target)]); + icon = "\A3\ui_f\data\igui\cfg\actions\gear_ca.paa"; + }; + }; + }; + }; + + class ReammoBox; + // dropped weapons/gear + class WeaponHolder: ReammoBox { + class ACE_Actions { + class ACE_MainActions { + displayName = CSTRING(MainAction); + distance = 3; + position = QUOTE(_target worldToModel ASLToAGL getPosASL _target); + + class GVAR(Gear) { + displayName = "$STR_ACTION_GEAR"; + statement = QUOTE(_player action [ARR_2(QUOTE(QUOTE(Gear)),_target)]); + icon = "\A3\ui_f\data\igui\cfg\actions\gear_ca.paa"; + }; + }; + }; + }; + class Lamps_base_F; class Land_PortableLight_single_F: Lamps_base_F { class EventHandlers { diff --git a/addons/interaction/XEH_postInit.sqf b/addons/interaction/XEH_postInit.sqf index 6d7d96713c..75e92c02c9 100644 --- a/addons/interaction/XEH_postInit.sqf +++ b/addons/interaction/XEH_postInit.sqf @@ -161,3 +161,21 @@ GVAR(isOpeningDoor) = false; [QGVAR(clearWeaponAttachmentsActionsCache)] call CBA_fnc_localEvent; }] call CBA_fnc_addPlayerEventHandler; } forEach ["loadout", "weapon"]; + + +// add "Take _weapon_" action to dropped weapons +private _action = [ + // action display name will be overwritten in modifier function + QGVAR(takeWeapon), "take", "\A3\ui_f\data\igui\cfg\actions\take_ca.paa", + {_player action ["TakeWeapon", _target, weaponCargo _target select 0]}, + {count weaponCargo _target == 1}, + nil, nil, nil, nil, nil, + { + params ["_target", "", "", "_actionData"]; + _actionData set [1, format [localize "STR_ACTION_TAKE_BAG", getText (configfile >> "CfgWeapons" >> weaponCargo _target select 0 >> "displayName")]]; + } +] call EFUNC(interact_menu,createAction); + +{ + [_x, 0, ["ACE_MainActions"], _action, true] call EFUNC(interact_menu,addActionToClass); +} forEach ["WeaponHolder", "WeaponHolderSimulated"]; diff --git a/addons/logistics_wirecutter/CfgWeapons.hpp b/addons/logistics_wirecutter/CfgWeapons.hpp index cfaa6c3259..5c66196f4a 100644 --- a/addons/logistics_wirecutter/CfgWeapons.hpp +++ b/addons/logistics_wirecutter/CfgWeapons.hpp @@ -8,6 +8,7 @@ class CfgWeapons { descriptionShort = CSTRING(wirecutterDescription); model = QPATHTOF(data\ace_wirecutter.p3d); picture = QPATHTOF(ui\item_wirecutter_ca.paa); + ACE_isWirecutter = 1; scope = 2; class ItemInfo: CBA_MiscItem_ItemInfo { mass = 25; diff --git a/addons/logistics_wirecutter/XEH_postInit.sqf b/addons/logistics_wirecutter/XEH_postInit.sqf index a22479e7e1..29dc506396 100644 --- a/addons/logistics_wirecutter/XEH_postInit.sqf +++ b/addons/logistics_wirecutter/XEH_postInit.sqf @@ -7,3 +7,5 @@ if (hasInterface) then { if (isServer) then { [QGVAR(destroyFence), {_this call FUNC(destroyFence)}] call CBA_fnc_addEventHandler; }; + +GVAR(possibleWirecutters) = call (uiNamespace getVariable [QGVAR(possibleWirecutters), {[]}]); diff --git a/addons/logistics_wirecutter/XEH_preStart.sqf b/addons/logistics_wirecutter/XEH_preStart.sqf index 022888575e..75567fe26e 100644 --- a/addons/logistics_wirecutter/XEH_preStart.sqf +++ b/addons/logistics_wirecutter/XEH_preStart.sqf @@ -1,3 +1,7 @@ #include "script_component.hpp" #include "XEH_PREP.hpp" + +// Cache wirecutter item classes, see XEH_postInit.sqf +private _possibleWirecutters = ("getNumber (_x >> 'ACE_isWirecutter') == 1" configClasses (configFile >> "CfgWeapons")) apply {configName _x}; +uiNamespace setVariable [QGVAR(possibleWirecutters), compileFinal str _possibleWirecutters]; diff --git a/addons/logistics_wirecutter/script_component.hpp b/addons/logistics_wirecutter/script_component.hpp index a499b6ae32..7335ef2e57 100644 --- a/addons/logistics_wirecutter/script_component.hpp +++ b/addons/logistics_wirecutter/script_component.hpp @@ -88,7 +88,7 @@ #define CUT_TIME_ENGINEER 7.5 #define HAS_WIRECUTTER(unit) (\ - "ACE_wirecutter" in (unit call EFUNC(common,uniqueItems)) \ + ((unit call EFUNC(common,uniqueItems)) arrayIntersect GVAR(possibleWirecutters)) isNotEqualTo []\ || {getNumber ((configOf (backpackContainer unit)) >> QGVAR(hasWirecutter)) == 1} \ || {getNumber (configFile >> "CfgWeapons" >> (vest unit) >> QGVAR(hasWirecutter)) == 1} \ ) diff --git a/addons/medical/functions/fnc_deserializeState.sqf b/addons/medical/functions/fnc_deserializeState.sqf index 2002abf59e..8f9d60b6e3 100644 --- a/addons/medical/functions/fnc_deserializeState.sqf +++ b/addons/medical/functions/fnc_deserializeState.sqf @@ -56,6 +56,13 @@ private _state = [_json] call CBA_fnc_parseJSON; _x params ["_var", "_default"]; private _value = _state getVariable _x; + // Handle wound hashmaps deserialized as CBA_namespaces + if (typeName _value == "LOCATION") then { + private _keys = allVariables _value; + private _values = _keys apply {_value getVariable _x}; + _value = _keys createHashMapFromArray _values; + }; + // Treat null as nil if (_value isEqualTo objNull) then { _value = _default; diff --git a/addons/medical/stringtable.xml b/addons/medical/stringtable.xml index c76c312f89..4be9a4029f 100644 --- a/addons/medical/stringtable.xml +++ b/addons/medical/stringtable.xml @@ -65,7 +65,7 @@ 에피네프린 사용 시 추가 회복 확률 - Increases how often spontaneous wake up checks happen when patient has Epinephrine in their system. + When an unconscious patient has Epinephrine in their system, the time between spontaneous wake up checks is divided by this value. 患者がアドレナリンを投与されると自発的に覚醒する確率を上昇させます。 增加因病患的循環系統裡面的腎上腺素自我甦醒的機率。 增加因病患的循环系统里面的肾上腺素自我苏醒的机率。 diff --git a/addons/medical_gui/functions/fnc_updateBodyImage.sqf b/addons/medical_gui/functions/fnc_updateBodyImage.sqf index 51a15945d3..7d3b6f03c5 100644 --- a/addons/medical_gui/functions/fnc_updateBodyImage.sqf +++ b/addons/medical_gui/functions/fnc_updateBodyImage.sqf @@ -22,6 +22,7 @@ params ["_ctrlGroup", "_target"]; private _tourniquets = GET_TOURNIQUETS(_target); private _fractures = GET_FRACTURES(_target); private _bodyPartDamage = _target getVariable [QEGVAR(medical,bodyPartDamage), [0, 0, 0, 0, 0, 0]]; +private _damageThreshold = GET_DAMAGE_THRESHOLD(_target); private _bodyPartBloodLoss = [0, 0, 0, 0, 0, 0]; { @@ -70,6 +71,21 @@ private _bodyPartBloodLoss = [0, 0, 0, 0, 0, 0]; [_bloodLoss] call FUNC(bloodLossToRGBA); } else { private _damage = _bodyPartDamage select _forEachIndex; + switch (true) do { // torso damage threshold doesn't need scaling + case (_forEachIndex > 3): { // legs: index 4 & 5 + _damageThreshold = LIMPING_DAMAGE_THRESHOLD * 4; + }; + case (_forEachIndex > 1): { // arms: index 2 & 3 + _damageThreshold = FRACTURE_DAMAGE_THRESHOLD * 4; + }; + case (_forEachIndex == 0): { // head: index 0 + _damageThreshold = _damageThreshold * 1.25; + }; + default { // torso: index 1 + _damageThreshold = _damageThreshold * 1.5 + }; + }; + _damage = (_damage / _damageThreshold) min 1; [_damage] call FUNC(damageToRGBA); }; diff --git a/addons/medical_gui/functions/fnc_updateInjuryList.sqf b/addons/medical_gui/functions/fnc_updateInjuryList.sqf index b0bf76fe45..d6f650f8a8 100644 --- a/addons/medical_gui/functions/fnc_updateInjuryList.sqf +++ b/addons/medical_gui/functions/fnc_updateInjuryList.sqf @@ -33,6 +33,43 @@ private _bodyPartName = [ _entries pushBack [localize _bodyPartName, [1, 1, 1, 1]]; +// Damage taken tooltip +if (GVAR(showDamageEntry)) then { + private _bodyPartDamage = (_target getVariable [QEGVAR(medical,bodyPartDamage), [0, 0, 0, 0, 0, 0]]) select _selectionN; + if (_bodyPartDamage > 0) then { + private _damageThreshold = GET_DAMAGE_THRESHOLD(_target); + switch (true) do { + case (_selectionN > 3): { // legs: index 4 & 5 + _damageThreshold = LIMPING_DAMAGE_THRESHOLD * 4; + }; + case (_selectionN > 1): { // arms: index 2 & 3 + _damageThreshold = FRACTURE_DAMAGE_THRESHOLD * 4; + }; + case (_selectionN == 0): { // head: index 0 + _damageThreshold = _damageThreshold * 1.25; + }; + default { // torso: index 1 + _damageThreshold = _damageThreshold * 1.5; + }; + }; + _bodyPartDamage = (_bodyPartDamage / _damageThreshold) min 1; + switch (true) do { + case (_bodyPartDamage isEqualTo 1): { + _entries pushBack [localize LSTRING(traumaSustained4), [_bodyPartDamage] call FUNC(damageToRGBA)]; + }; + case (_bodyPartDamage >= 0.75): { + _entries pushBack [localize LSTRING(traumaSustained3), [_bodyPartDamage] call FUNC(damageToRGBA)]; + }; + case (_bodyPartDamage >= 0.5): { + _entries pushBack [localize LSTRING(traumaSustained2), [_bodyPartDamage] call FUNC(damageToRGBA)]; + }; + case (_bodyPartDamage >= 0.25): { + _entries pushBack [localize LSTRING(traumaSustained1), [_bodyPartDamage] call FUNC(damageToRGBA)]; + }; + }; + }; +}; + // Indicate if unit is bleeding at all if (IS_BLEEDING(_target)) then { _entries pushBack [localize LSTRING(Status_Bleeding), [1, 0, 0, 1]]; @@ -78,10 +115,10 @@ if (_target call EFUNC(common,isAwake)) then { private _pain = GET_PAIN_PERCEIVED(_target); if (_pain > 0) then { private _painText = switch (true) do { - case (_pain > 0.5): { + case (_pain > PAIN_UNCONSCIOUS): { ELSTRING(medical_treatment,Status_SeverePain); }; - case (_pain > 0.1): { + case (_pain > (PAIN_UNCONSCIOUS / 5)): { ELSTRING(medical_treatment,Status_Pain); }; default { diff --git a/addons/medical_gui/initSettings.sqf b/addons/medical_gui/initSettings.sqf index eb6a36bac6..c1d02d7131 100644 --- a/addons/medical_gui/initSettings.sqf +++ b/addons/medical_gui/initSettings.sqf @@ -109,6 +109,15 @@ private _categoryColors = [ELSTRING(medical,Category), format ["| %1 |", LELSTRI ] call CBA_fnc_addSetting; } forEach _damageColors; +[ + QGVAR(showDamageEntry), + "CHECKBOX", + [LSTRING(showDamageEntry_DisplayName), LSTRING(showDamageEntry_Description)], + [ELSTRING(medical,Category), LSTRING(SubCategory)], + false, + true +] call CBA_fnc_addSetting; + [ QGVAR(showBloodlossEntry), "CHECKBOX", diff --git a/addons/medical_gui/stringtable.xml b/addons/medical_gui/stringtable.xml index e7cda25af9..afcd58b6e4 100644 --- a/addons/medical_gui/stringtable.xml +++ b/addons/medical_gui/stringtable.xml @@ -1250,5 +1250,29 @@ Показывать тяжесть кровопотери в списке ранений. Mostrar la pérdida de sangre cualitativa en la lista de heridas. + + Show Trauma Sustained + Mostrar Traumatismo Sofrido + + + Show trauma sustained in the injury list. + Mostrar traumatismo sofrido na lista de feridas. + + + Minor Trauma + Traumatismo Leve + + + Major Trauma + Traumatismo Significante + + + Severe Trauma + Traumatismo Severo + + + Chronic Trauma + Traumatismo Crônico + diff --git a/addons/medical_treatment/CfgWeapons.hpp b/addons/medical_treatment/CfgWeapons.hpp index fed3184a59..e285697cc1 100644 --- a/addons/medical_treatment/CfgWeapons.hpp +++ b/addons/medical_treatment/CfgWeapons.hpp @@ -8,12 +8,14 @@ class CfgWeapons { class FirstAidKit: ItemCore { type = 0; + ACE_isMedicalItem = 1; class ItemInfo: InventoryFirstAidKitItem_Base_F { mass = 4; }; }; class Medikit: ItemCore { type = 0; + ACE_isMedicalItem = 1; class ItemInfo: MedikitItem { mass = 60; }; @@ -27,6 +29,7 @@ class CfgWeapons { displayName = CSTRING(Bandage_Basic_Display); descriptionShort = CSTRING(Bandage_Basic_Desc_Short); descriptionUse = CSTRING(Bandage_Basic_Desc_Use); + ACE_isMedicalItem = 1; class ItemInfo: CBA_MiscItem_ItemInfo { mass = 1; }; @@ -39,6 +42,7 @@ class CfgWeapons { model = QPATHTOF(data\packingbandage.p3d); descriptionShort = CSTRING(Packing_Bandage_Desc_Short); descriptionUse = CSTRING(Packing_Bandage_Desc_Use); + ACE_isMedicalItem = 1; class ItemInfo: CBA_MiscItem_ItemInfo { mass = 1; }; @@ -51,6 +55,7 @@ class CfgWeapons { model = "\A3\Structures_F_EPA\Items\Medical\Bandage_F.p3d"; descriptionShort = CSTRING(Bandage_Elastic_Desc_Short); descriptionUse = CSTRING(Bandage_Elastic_Desc_Use); + ACE_isMedicalItem = 1; class ItemInfo: CBA_MiscItem_ItemInfo { mass = 1; }; @@ -63,6 +68,7 @@ class CfgWeapons { model = QPATHTOF(data\tourniquet.p3d); descriptionShort = CSTRING(Tourniquet_Desc_Short); descriptionUse = CSTRING(Tourniquet_Desc_Use); + ACE_isMedicalItem = 1; class ItemInfo: CBA_MiscItem_ItemInfo { mass = 1; }; @@ -74,6 +80,7 @@ class CfgWeapons { picture = QPATHTOF(ui\splint_ca.paa); model = QPATHTOF(data\splint.p3d); descriptionShort = CSTRING(splint_Desc_Short); + ACE_isMedicalItem = 1; class ItemInfo: CBA_MiscItem_ItemInfo { mass = 2; }; @@ -86,6 +93,7 @@ class CfgWeapons { model = QPATHTOF(data\morphine.p3d); descriptionShort = CSTRING(Morphine_Desc_Short); descriptionUse = CSTRING(Morphine_Desc_Use); + ACE_isMedicalItem = 1; class ItemInfo: CBA_MiscItem_ItemInfo { mass = 1; }; @@ -98,6 +106,7 @@ class CfgWeapons { model = QPATHTOF(data\adenosine.p3d); descriptionShort = CSTRING(adenosine_Desc_Short); descriptionUse = CSTRING(adenosine_Desc_Use); + ACE_isMedicalItem = 1; class ItemInfo: CBA_MiscItem_ItemInfo { mass = 1; }; @@ -110,6 +119,7 @@ class CfgWeapons { model = QPATHTOF(data\atropine.p3d); descriptionShort = CSTRING(Atropine_Desc_Short); descriptionUse = CSTRING(Atropine_Desc_Use); + ACE_isMedicalItem = 1; class ItemInfo: CBA_MiscItem_ItemInfo { mass = 1; }; @@ -122,6 +132,7 @@ class CfgWeapons { model = QPATHTOF(data\epinephrine.p3d); descriptionShort = CSTRING(Epinephrine_Desc_Short); descriptionUse = CSTRING(Epinephrine_Desc_Use); + ACE_isMedicalItem = 1; class ItemInfo: CBA_MiscItem_ItemInfo { mass = 1; }; @@ -137,6 +148,7 @@ class CfgWeapons { picture = QPATHTOF(ui\plasmaIV_ca.paa); descriptionShort = CSTRING(Plasma_IV_Desc_Short); descriptionUse = CSTRING(Plasma_IV_Desc_Use); + ACE_isMedicalItem = 1; class ItemInfo: CBA_MiscItem_ItemInfo { mass = 10; }; @@ -167,6 +179,7 @@ class CfgWeapons { hiddenSelectionsTextures[] = {QPATHTOF(data\IVBag_blood_1000ml_ca.paa)}; descriptionShort = CSTRING(Blood_IV_Desc_Short); descriptionUse = CSTRING(Blood_IV_Desc_Use); + ACE_isMedicalItem = 1; class ItemInfo: CBA_MiscItem_ItemInfo { mass = 10; }; @@ -197,6 +210,7 @@ class CfgWeapons { picture = QPATHTOF(ui\salineIV_ca.paa); descriptionShort = CSTRING(Saline_IV_Desc_Short); descriptionUse = CSTRING(Saline_IV_Desc_Use); + ACE_isMedicalItem = 1; class ItemInfo: CBA_MiscItem_ItemInfo { mass = 10; }; @@ -225,6 +239,7 @@ class CfgWeapons { picture = QPATHTOF(ui\quickclot_ca.paa); descriptionShort = CSTRING(QuikClot_Desc_Short); descriptionUse = CSTRING(QuikClot_Desc_Use); + ACE_isMedicalItem = 1; class ItemInfo: CBA_MiscItem_ItemInfo { mass = 1; }; @@ -236,6 +251,7 @@ class CfgWeapons { picture = QPATHTOF(ui\personal_aid_kit_ca.paa); descriptionShort = CSTRING(Aid_Kit_Desc_Short); descriptionUse = CSTRING(Aid_Kit_Desc_Use); + ACE_isMedicalItem = 1; class ItemInfo: CBA_MiscItem_ItemInfo { mass = 10; }; @@ -248,6 +264,7 @@ class CfgWeapons { picture = QPATHTOF(ui\surgicalKit_ca.paa); descriptionShort = CSTRING(SurgicalKit_Desc_Short); descriptionUse = CSTRING(SurgicalKit_Desc_Use); + ACE_isMedicalItem = 1; class ItemInfo: CBA_MiscItem_ItemInfo { mass = 15; }; @@ -260,6 +277,7 @@ class CfgWeapons { picture = QPATHTOF(ui\suture_ca.paa); descriptionShort = CSTRING(Suture_Desc_Short); descriptionUse = CSTRING(Suture_Desc_Use); + ACE_isMedicalItem = 1; class ItemInfo: CBA_MiscItem_ItemInfo { mass = 1; }; @@ -272,6 +290,7 @@ class CfgWeapons { picture = QPATHTOF(ui\bodybag_ca.paa); descriptionShort = CSTRING(Bodybag_Desc_Short); descriptionUse = CSTRING(Bodybag_Desc_Use); + ACE_isMedicalItem = 1; class ItemInfo: CBA_MiscItem_ItemInfo { mass = 7; }; diff --git a/addons/medical_treatment/XEH_PREP.hpp b/addons/medical_treatment/XEH_PREP.hpp index be64563857..ab128aa49c 100644 --- a/addons/medical_treatment/XEH_PREP.hpp +++ b/addons/medical_treatment/XEH_PREP.hpp @@ -48,6 +48,7 @@ PREP(medicationLocal); PREP(onMedicationUsage); PREP(placeInBodyBag); PREP(removeBody); +PREP(scanMedicalItems); PREP(setTriageStatus); PREP(splint); PREP(splintLocal); diff --git a/addons/medical_treatment/XEH_preInit.sqf b/addons/medical_treatment/XEH_preInit.sqf index dfb6bf6518..30bf875291 100644 --- a/addons/medical_treatment/XEH_preInit.sqf +++ b/addons/medical_treatment/XEH_preInit.sqf @@ -8,6 +8,8 @@ PREP_RECOMPILE_END; #include "initSettings.sqf" +#define ARSENAL_CATEGORY_ICON (["\A3\ui_f\data\igui\cfg\actions\heal_ca.paa", QPATHTOEF(medical_gui,data\categories\bandage_fracture.paa)] select (["ace_medical_gui"] call EFUNC(common,isModLoaded))) + // config to determine animation acceleration coefficient // adjusting these is trail and error // if the animation is cut of ingame, increase these values @@ -44,4 +46,9 @@ GVAR(facilityClasses) = []; } forEach getArray _x; } forEach configProperties [configFile >> QEGVAR(medical,facilities), "isArray _x"]; +// Custom Arsenal tab +if (["ace_arsenal"] call EFUNC(common,isModLoaded)) then { + [MEDICAL_TREATMENT_ITEMS, LELSTRING(medical,Category), ARSENAL_CATEGORY_ICON] call EFUNC(arsenal,addRightPanelButton); +}; + ADDON = true; diff --git a/addons/medical_treatment/XEH_preStart.sqf b/addons/medical_treatment/XEH_preStart.sqf index 022888575e..27f8f2d9c2 100644 --- a/addons/medical_treatment/XEH_preStart.sqf +++ b/addons/medical_treatment/XEH_preStart.sqf @@ -1,3 +1,5 @@ #include "script_component.hpp" #include "XEH_PREP.hpp" + +call FUNC(scanMedicalItems) diff --git a/addons/medical_treatment/functions/fnc_scanMedicalItems.sqf b/addons/medical_treatment/functions/fnc_scanMedicalItems.sqf new file mode 100644 index 0000000000..17b2456c42 --- /dev/null +++ b/addons/medical_treatment/functions/fnc_scanMedicalItems.sqf @@ -0,0 +1,34 @@ +#include "script_component.hpp" +/* + * Author: Salluci + * Caches all item classnames used in ACE_Medical_Treatment_Actions + * + * Arguments: + * None + * + * Return Value: + * None + * + * Example: + * call ace_medical_treatment_fnc_scanMedicalItems + * + * Public: No + */ + +private _list = []; +private _cfgActions = configFile >> QGVAR(actions); + +private _fnc_isMedicalItem = toString { + getNumber (_x >> "ACE_isMedicalItem") isEqualTo 1 +}; + +// get items in ACE_Medical_Treament_Actions, fallback for items without API config property +{ + _list append (getArray (_x >> "items")); +} forEach ("true" configClasses _cfgActions); + +{ + _list pushBack (configName _x); +} forEach (_fnc_isMedicalItem configClasses (configFile >> "CfgWeapons")); + +uiNamespace setVariable [QGVAR(treatmentItems), compileFinal str (_list arrayIntersect _list)] diff --git a/addons/medical_treatment/functions/fnc_surgicalKitProgress.sqf b/addons/medical_treatment/functions/fnc_surgicalKitProgress.sqf index 8fc4832f8f..f7ed35e412 100644 --- a/addons/medical_treatment/functions/fnc_surgicalKitProgress.sqf +++ b/addons/medical_treatment/functions/fnc_surgicalKitProgress.sqf @@ -83,9 +83,7 @@ if ( }; // Consume a suture for the next wound if one exists, stop stitching if none are left -if (GVAR(consumeSurgicalKit) == 2) then { - // Don't consume a suture if there are no more wounds to stitch - if (_bandagedWoundsOnPart isEqualTo []) exitWith {false}; +if (GVAR(consumeSurgicalKit) == 2 && {_bandagedWoundsOnPart isNotEqualTo []}) then { ([_medic, _patient, ["ACE_suture"]] call FUNC(useItem)) params ["_user"]; !isNull _user } else { diff --git a/addons/medical_treatment/script_component.hpp b/addons/medical_treatment/script_component.hpp index 682e8a471c..36173d4ac1 100644 --- a/addons/medical_treatment/script_component.hpp +++ b/addons/medical_treatment/script_component.hpp @@ -54,3 +54,5 @@ // Animations that would be played faster than this are instead skipped. (= Progress bar too quick for animation). #define ANIMATION_SPEED_MAX_COEFFICIENT 2.5 + +#define MEDICAL_TREATMENT_ITEMS (call (uiNamespace getVariable [QGVAR(treatmentItems), {[]}])) diff --git a/addons/repair/ACE_Repair.hpp b/addons/repair/ACE_Repair.hpp index ae436791ad..344c509b2f 100644 --- a/addons/repair/ACE_Repair.hpp +++ b/addons/repair/ACE_Repair.hpp @@ -3,10 +3,11 @@ class ACE_Repair { class ReplaceWheel { displayName = CSTRING(ReplaceWheel); displayNameProgress = CSTRING(ReplacingWheel); + forceDisplayName = 0; repairLocations[] = {"All"}; requiredEngineer = QGVAR(engineerSetting_Wheel); - repairingTime = 10; + repairingTime = QGVAR(wheelChangeTime); repairingTimeSelfCoef = 1; items = QGVAR(wheelRepairRequiredItems); condition = QFUNC(canReplaceWheel); @@ -21,6 +22,7 @@ class ACE_Repair { animationCallerProne = "Acts_carFixingWheel"; animationCallerSelf = "Acts_carFixingWheel"; animationCallerSelfProne = "Acts_carFixingWheel"; + loopAnimation = 0; litter[] = {}; }; class RemoveWheel: ReplaceWheel { @@ -46,7 +48,7 @@ class ACE_Repair { displayNameProgress = CSTRING(RepairingHitPoint); condition = QFUNC(canMiscRepair); requiredEngineer = QGVAR(engineerSetting_Repair); - repairingTime = 15; + repairingTime = QGVAR(miscRepairTime); callbackSuccess = QFUNC(doRepair); items = QGVAR(miscRepairRequiredItems); itemConsumed = QGVAR(consumeItem_ToolKit); @@ -80,11 +82,14 @@ class ACE_Repair { class FullRepair: MiscRepair { displayName = CSTRING(fullRepair); displayNameProgress = CSTRING(fullyRepairing); + forceDisplayName = 1; + loopAnimation = 1; requiredEngineer = QGVAR(engineerSetting_fullRepair); repairLocations[] = {QGVAR(fullRepairLocation)}; - repairingTime = 30; - condition = "-1 != ((getAllHitPointsDamage _target param [2,[]]) findIf {_x > 0})"; + repairingTime = QFUNC(getFullRepairTime); + condition = "((getAllHitPointsDamage _target) select 2) findIf {_x > 0} != -1"; callbackSuccess = QFUNC(doFullRepair); + callbackProgress = QFUNC(fullRepairProgress); items = QGVAR(fullRepairRequiredItems); itemConsumed = QGVAR(consumeItem_ToolKit); }; diff --git a/addons/repair/XEH_PREP.hpp b/addons/repair/XEH_PREP.hpp index 7c82632f87..ea9e40a4e7 100644 --- a/addons/repair/XEH_PREP.hpp +++ b/addons/repair/XEH_PREP.hpp @@ -17,8 +17,11 @@ PREP(doRepair); PREP(doRepairTrack); PREP(doReplaceTrack); PREP(doReplaceWheel); +PREP(fullRepairProgress); PREP(getClaimObjects); +PREP(getFullRepairTime); PREP(getHitPointString); +PREP(getHitPointsToIgnore); PREP(getPatchWheelTime); PREP(getPostRepairDamage); PREP(getRepairItems); diff --git a/addons/repair/functions/fnc_addRepairActions.sqf b/addons/repair/functions/fnc_addRepairActions.sqf index f78863985d..20607ca940 100644 --- a/addons/repair/functions/fnc_addRepairActions.sqf +++ b/addons/repair/functions/fnc_addRepairActions.sqf @@ -25,6 +25,9 @@ TRACE_2("addRepairActions", _vehicle,_type); private _initializedClasses = GETMVAR(GVAR(initializedClasses),[]); if (_type in _initializedClasses) exitWith {}; +// get hitPoints to ignore +private _hitPointsToIgnore = [_vehicle] call FUNC(getHitPointsToIgnore); + // get all hitpoints and selections (getAllHitPointsDamage _vehicle) params [["_hitPoints", []], ["_hitSelections", []]]; // Since 1.82 these are all lower case @@ -40,8 +43,6 @@ private _icon = ["a3\ui_f\data\igui\cfg\actions\repair_ca.paa", "#FFFFFF"]; private _vehCfg = configOf _vehicle; // Custom position can be defined via config for associated hitpoint private _hitpointPositions = getArray (_vehCfg >> QGVAR(hitpointPositions)); -// Associated hitpoints can be grouped via config to produce a single repair action -private _hitpointGroups = getArray (_vehCfg >> QGVAR(hitpointGroups)); // Get turret paths private _turretPaths = ((fullCrew [_vehicle, "gunner", true]) + (fullCrew [_vehicle, "commander", true])) apply {_x # 3}; @@ -88,59 +89,32 @@ private _turretPaths = ((fullCrew [_vehicle, "gunner", true]) + (fullCrew [_vehi _processedSelections pushBack _selection; } else { - // Empty hitpoints don't contain enough information - if (_hitpoint isEqualTo "") exitWith { TRACE_3("Skipping Empty Hit",_hitpoint,_forEachIndex,_selection); }; - // Ignore glass hitpoints - if ((_hitpoint find "glass") != -1) exitWith { TRACE_3("Skipping Glass",_hitpoint,_forEachIndex,_selection); }; - // Ignore hitpoints starting with # (seems to be lights) - if ((_hitpoint select [0,1]) == "#") exitWith { TRACE_3("Skipping # hit",_hitpoint,_forEachIndex,_selection); }; - // Ignore ERA/Slat armor (vanilla uses hitera_/hitslat_, pre-1.82 RHS uses era_) - // ToDo: see how community utilizes new armor system, could also check getText (_hitpointConfig >> "simulation") - if (((_hitpoint select [0,7]) == "hitera_") || {(_hitpoint select [0,8]) == "hitslat_"} || {(_hitpoint select [0,4]) == "era_"}) exitWith { TRACE_3("Skipping ERA/SLAT",_hitpoint,_forEachIndex,_selection); }; + // Skip ignored hitpoints + if (_hitpoint in _hitPointsToIgnore) exitWith { + TRACE_3("Skipping ignored hitpoint",_hitpoint,_forEachIndex,_selection); + }; // Some hitpoints do not have a selection but do have an armorComponent value (seems to mainly be RHS) // Ref https://community.bistudio.com/wiki/Arma_3_Damage_Enhancement // this code won't support identically named hitpoints (e.g. commander turret: Duplicate HitPoint name 'HitTurret') private _armorComponent = ""; if (_selection == "") then { - { - private _turretHitpointCfg = ([_vehCfg, _x] call CBA_fnc_getTurret) >> "HitPoints"; - private _hitpointsCfg = "configName _x == _hitpoint" configClasses _turretHitpointCfg; - if (_hitpointsCfg isNotEqualTo []) exitWith { - TRACE_2("turret hitpoint configFound",_hitpoint,_x); - // only do turret hitpoints or stuff linked to visuals for now or we apparently get some weird stuff - if ((_hitpoint in ["hitturret", "hitgun"]) || {(getNumber (_hitpointsCfg # 0 >> "isGun")) == 1} || {(getNumber (_hitpointsCfg # 0 >> "isTurret")) == 1} || {(getText (_hitpointsCfg # 0 >> "visual")) != ""}) then { + private _hitpointsCfg = "configName _x == _hitpoint" configClasses (_vehCfg >> "HitPoints"); + if (_hitpointsCfg isNotEqualTo []) then { + _armorComponent = getText (_hitpointsCfg # 0 >> "armorComponent"); + }; + if (_armorComponent == "") then { + { + private _turretHitpointCfg = ([_vehCfg, _x] call CBA_fnc_getTurret) >> "HitPoints"; + private _hitpointsCfg = "configName _x == _hitpoint" configClasses _turretHitpointCfg; + if (_hitpointsCfg isNotEqualTo []) exitWith { + TRACE_2("turret hitpoint configFound",_hitpoint,_x); _armorComponent = getText (_hitpointsCfg # 0 >> "armorComponent"); }; - }; - } forEach _turretPaths; - if (_armorComponent == "") then { - private _hitpointsCfg = "configName _x == _hitpoint" configClasses (_vehCfg >> "HitPoints"); - if ((getText (_hitpointsCfg # 0 >> "visual")) != "") then { - _armorComponent = getText (_hitpointsCfg # 0 >> "armorComponent"); - }; + } forEach _turretPaths; }; if (_armorComponent != "") then { INFO_3("%1: %2 no selection: using armorComponent %3",_type,_hitpoint,_armorComponent); }; }; - if ((_selection == "") && {_armorComponent == ""}) exitWith { TRACE_3("Skipping no selection OR armor component",_hitpoint,_forEachIndex,_selection); }; - - - //Depends hitpoints shouldn't be modified directly (will be normalized) - // Biki: Clearing 'depends' in case of inheritance cannot be an empty string (rpt warnings), but rather a "0" value. - if (!((getText (_vehCfg >> "HitPoints" >> _hitpoint >> "depends")) in ["", "0"])) exitWith { - TRACE_3("Skip Depends",_hitpoint,_forEachIndex,_selection); - }; - - private _childHitPoint = false; - { - { - if (_hitpoint == _x) exitWith { - _childHitPoint = true; - }; - } forEach (_x select 1); - } forEach _hitpointGroups; - // If the current selection is associated with a child hitpoint, then skip - if (_childHitPoint) exitWith { TRACE_3("childHitpoint",_hitpoint,_forEachIndex,_selection); }; // Find the action position private _position = compile format ["_target selectionPosition ['%1', 'HitPoints'];", _selection]; diff --git a/addons/repair/functions/fnc_doRepair.sqf b/addons/repair/functions/fnc_doRepair.sqf index 1b6f497d8e..2874eaf2f8 100644 --- a/addons/repair/functions/fnc_doRepair.sqf +++ b/addons/repair/functions/fnc_doRepair.sqf @@ -7,6 +7,7 @@ * 0: Unit that does the repairing * 1: Vehicle to repair * 2: Selected hitpointIndex + * 3: Repair action classname * * Return Value: * None @@ -17,10 +18,11 @@ * Public: No */ -params ["_unit", "_vehicle", "_hitPointIndex"]; -TRACE_3("params",_unit,_vehicle,_hitPointIndex); +params ["_unit", "_vehicle", "_hitPointIndex", "_action"]; +TRACE_4("params",_unit,_vehicle,_hitPointIndex,_action); -private _postRepairDamageMin = [_unit] call FUNC(getPostRepairDamage); +// override minimum damage if doing full repair +private _postRepairDamageMin = [_unit, _action isEqualTo "fullRepair"] call FUNC(getPostRepairDamage); (getAllHitPointsDamage _vehicle) params ["_allHitPoints"]; private _hitPointClassname = _allHitPoints select _hitPointIndex; diff --git a/addons/repair/functions/fnc_fullRepairProgress.sqf b/addons/repair/functions/fnc_fullRepairProgress.sqf new file mode 100644 index 0000000000..444bb750b3 --- /dev/null +++ b/addons/repair/functions/fnc_fullRepairProgress.sqf @@ -0,0 +1,50 @@ +#include "script_component.hpp" +/* + * Author: LinkIsGrim + * Handles full repair by periodically repairing damaged hitpoints. + * + * Arguments: + * 0: Arguments + * 0: Engineer + * 1: Vehicle + * 2: Hitpoint (unused) + * 3: Repair action classname + * 1: Elapsed Time + * 2: Total Time + * + * Return Value: + * Continue Repair + * + * Example: + * [[objNull, player], 5, 10] call ace_repair_fnc_fullRepairProgress + * + * Public: No + */ + +params ["_args", "_elapsedTime", "_totalTime"]; +_args params ["_engineer", "_vehicle", "", "_action"]; + +if !((alive _vehicle) && {(abs speed _vehicle) < 1}) exitWith {false}; // make sure vehicle doesn't drive off + +// Not enough time has elapsed to repair a hitpoint +if (_totalTime - _elapsedTime > ([_engineer, _vehicle] call FUNC(getFullRepairTime)) - (GVAR(miscRepairTime) * GVAR(timeCoefficientFullRepair))) exitWith {true}; + +private _allHitPointsDamage = getAllHitPointsDamage _vehicle; +_allHitPointsDamage params ["_hitPoints", "", "_damageValues"]; + +private _hitPointsToIgnore = [_vehicle] call FUNC(getHitPointsToIgnore); + +private _firstDamagedIndex = { + private _hitPoint = _hitPoints select _forEachIndex; + if (_x > 0 && {!(_hitPoint in _hitPointsToIgnore)}) exitWith {_forEachIndex}; + -1 +} forEach _damageValues; + +// Stop repairing if there are no more damaged hitpoints +// callBackSuccess to FUNC(doFullRepair) for ignored hitpoints +if (_firstDamagedIndex == -1) exitWith {true}; + +// Repair the first damaged hitpoint +[_engineer, _vehicle, _firstDamagedIndex, _action] call FUNC(doRepair); + +true diff --git a/addons/repair/functions/fnc_getFullRepairTime.sqf b/addons/repair/functions/fnc_getFullRepairTime.sqf new file mode 100644 index 0000000000..78f46472a5 --- /dev/null +++ b/addons/repair/functions/fnc_getFullRepairTime.sqf @@ -0,0 +1,42 @@ +#include "script_component.hpp" +/* + * Author: LinkIsGrim + * Calculates the Full Repair time based on the amount of hitpoints to repair + * + * Arguments: + * 0: Engineer + * 1: Vehicle + * + * Return Value: + * Repair Time + * + * Example: + * [player, vehicle] call ace_repair_fnc_getFullRepairTime + * + * Public: No + */ + +params ["_engineer", "_vehicle"]; + +private _allHitPointsDamage = getAllHitPointsDamage _vehicle; +_allHitPointsDamage params ["_hitPoints", "", "_damageValues"]; + +private _hitPointsToIgnore = [_vehicle] call FUNC(getHitPointsToIgnore); + +private _repairsNeeded = 0; +private _doExtraRepair = false; +{ + if (_x <= 0) then {continue}; // skip hitpoints that don't need repairs + private _hitPoint = _hitPoints select _forEachIndex; + if (_hitPoint in _hitPointsToIgnore) then { // only add extra repair for ignore hitpoints if they're damaged + _doExtraRepair = true; + continue + }; + _repairsNeeded = _repairsNeeded + ceil (_x / 0.5); // repair is capped at 0.5 in FUNC(doRepair) +} forEach _damageValues; + +if (_doExtraRepair) then { + _repairsNeeded = _repairsNeeded + 1; +}; + +_repairsNeeded * GVAR(miscRepairTime) * GVAR(timeCoefficientFullRepair) // return diff --git a/addons/repair/functions/fnc_getHitPointsToIgnore.sqf b/addons/repair/functions/fnc_getHitPointsToIgnore.sqf new file mode 100644 index 0000000000..24f2d9a207 --- /dev/null +++ b/addons/repair/functions/fnc_getHitPointsToIgnore.sqf @@ -0,0 +1,142 @@ +#include "script_component.hpp" +/* + * Author: commy2, kymckay, LinkIsGrim + * Get list of vehicle hitpoints to ignore + * + * Arguments: + * 0: Vehicle + * + * Return Value: + * HitPoints to ignore + * + * Example: + * [vehicle] call ace_repair_fnc_getHitpointsToIgnore + * + * Public: No + */ + +params ["_vehicle"]; + +private _type = typeOf _vehicle; +TRACE_2("getHitPointsToIgnore", _vehicle,_type); +private _initializedClasses = missionNamespace getVariable [QGVAR(hitPointsToIgnoreInitializedClasses), createHashMap]; +if (_type in _initializedClasses) exitWith {_initializedClasses get _type}; + +private _vehCfg = configOf _vehicle; +private _hitpointGroups = getArray (_vehCfg >> QGVAR(hitpointGroups)); +private _turretPaths = ((fullCrew [_vehicle, "gunner", true]) + (fullCrew [_vehicle, "commander", true])) apply {_x # 3}; + +(getAllHitPointsDamage _vehicle) params [["_hitPoints", []], ["_hitSelections", []]]; +// get hitpoints of wheels with their selections +([_vehicle] call FUNC(getWheelHitPointsWithSelections)) params ["_wheelHitPoints", "_wheelHitSelections"]; + +private _hitPointsToIgnore = [""]; // always ignore empty hitpoints +private _processedSelections = []; + +{ + private _selection = _x; + private _hitpoint = toLower (_hitPoints select _forEachIndex); + private _isWheelOrTrack = _selection in _wheelHitSelections || {_hitpoint in _wheelHitPoints} || {_hitpoint in TRACK_HITPOINTS}; + + if (_hitpoint isEqualTo "") then { // skip empty hitpoint + continue + }; + + if (_isWheelOrTrack && {_selection in _processedSelections || {_selection isEqualTo ""}}) then { // skip duplicate or empty selection wheel/track + TRACE_3("Skipping duplicate Wheel/Track or empty selection",_hitpoint,_forEachIndex,_selection); + /*#ifdef DEBUG_MODE_FULL + systemChat format ["Skipping duplicate wheel, hitpoint %1, index %2, selection %3", _hitpoint, _forEachIndex, _selection]; + #endif*/ + _hitPointsToIgnore pushBackUnique _hitpoint; + _processedSelections pushBack _selection; + continue + }; + + if ("glass" in _hitpoint) then { // skip glass + TRACE_3("Skipping glass",_hitpoint,_forEachIndex,_selection); + /*#ifdef DEBUG_MODE_FULL + systemChat format ["Skipping glass, hitpoint %1, index %2, selection %3", _hitpoint, _forEachIndex, _selection]; + #endif*/ + _hitPointsToIgnore pushBackUnique _hitpoint; + _processedSelections pushBack _selection; + continue + }; + + if (_hitpoint select [0,1] isEqualTo "#" || {_hitpoint select [0,9] isEqualTo "hit_light"}) then { // skip lights + TRACE_3("Skipping light",_hitpoint,_forEachIndex,_selection); + /*#ifdef DEBUG_MODE_FULL + systemChat format ["Skipping light, hitpoint %1, index %2, selection %3", _hitpoint, _forEachIndex, _selection]; + #endif*/ + _hitPointsToIgnore pushBackUnique _hitpoint; + _processedSelections pushBack _selection; + continue + }; + + if (_hitpoint select [0,7] isEqualTo "hitera_" || {_hitpoint select [0,8] isEqualTo "hitslat_"} || {_hitpoint select [0,4] isEqualTo "era_"}) then { // skip era/slat + TRACE_3("Skipping ERA/Slat HitPoint",_hitpoint,_forEachIndex,_selection); + /*#ifdef DEBUG_MODE_FULL + systemChat format ["Skipping ERA/SLAT, hitpoint %1, index %2, selection %3", _hitpoint, _forEachIndex, _selection]; + #endif*/ + _hitPointsToIgnore pushBackUnique _hitpoint; + _processedSelections pushBack _selection; + continue + }; + + private _armorComponent = ""; + if (_selection == "") then { // some hitpoints have empty selection but defined armor component (mostly RHS) + { + private _turretHitpointCfg = ([_vehCfg, _x] call CBA_fnc_getTurret) >> "HitPoints"; + private _hitpointsCfg = "configName _x == _hitpoint" configClasses _turretHitpointCfg; + if (_hitpointsCfg isNotEqualTo []) exitWith { + TRACE_2("turret hitpoint configFound",_hitpoint,_x); + // only do turret hitpoints and stuff linked to visuals + if ((_hitpoint in ["hitturret", "hitgun"]) || {(getNumber (_hitpointsCfg # 0 >> "isGun")) == 1} || {(getNumber (_hitpointsCfg # 0 >> "isTurret")) == 1} || {(getText (_hitpointsCfg # 0 >> "visual")) != ""}) then { + _armorComponent = getText (_hitpointsCfg # 0 >> "armorComponent"); + }; + }; + } forEach _turretPaths; + if (_armorComponent == "") then { + private _hitpointsCfg = "configName _x == _hitpoint" configClasses (_vehCfg >> "HitPoints"); + if (_hitpointsCfg isNotEqualTo [] && {(getText (_hitpointsCfg # 0 >> "visual")) != ""}) then { + _armorComponent = getText (_hitpointsCfg # 0 >> "armorComponent"); + }; + }; + }; + + if ((_selection == "") && {_armorComponent == ""}) then { + TRACE_3("Skipping no selection OR armor component",_hitpoint,_forEachIndex,_selection); + /*#ifdef DEBUG_MODE_FULL + systemChat format ["Skipping no selection OR armor component, hitpoint %1, index %2, selection %3", _hitpoint, _forEachIndex, _selection]; + #endif*/ + _hitPointsToIgnore pushBackUnique _hitpoint; + _processedSelections pushBack _selection; + continue + }; + + if (!(getText (_vehCfg >> "HitPoints" >> _hitpoint >> "depends") in ["", "0"])) then { // skip depends hitpoints, should be normalized by engine + TRACE_3("Skipping depends hitpoint",_hitpoint,_forEachIndex,_selection); + /*#ifdef DEBUG_MODE_FULL + systemChat format ["Skipping depends hitpoint, hitpoint %1, index %2, selection %3", _hitpoint, _forEachIndex, _selection]; + #endif*/ + _hitPointsToIgnore pushBackUnique _hitpoint; + _processedSelections pushBack _selection; + continue + }; + + if ((_hitpointGroups findIf {(_x select 1) == _hitpoint}) != -1) then { // skip child hitpoints + TRACE_3("Skipping child hitpoint",_hitpoint,_forEachIndex,_selection); + /*#ifdef DEBUG_MODE_FULL + systemChat format ["Skipping child hitpoint, hitpoint %1, index %2, selection %3", _hitpoint, _forEachIndex, _selection]; + #endif*/ + _hitPointsToIgnore pushBackUnique _hitpoint; + _processedSelections pushBack _selection; + continue + }; + + _processedSelections pushBack _selection; +} forEach _hitSelections; + +_initializedClasses set [_type, _hitPointsToIgnore]; +missionNamespace setVariable [QGVAR(hitPointsToIgnoreInitializedClasses), _initializedClasses]; + +_hitPointsToIgnore diff --git a/addons/repair/functions/fnc_getPostRepairDamage.sqf b/addons/repair/functions/fnc_getPostRepairDamage.sqf index 0eac151e45..141df15369 100644 --- a/addons/repair/functions/fnc_getPostRepairDamage.sqf +++ b/addons/repair/functions/fnc_getPostRepairDamage.sqf @@ -5,6 +5,7 @@ * * Arguments: * 0: Unit that does the repairing + * 1: Override for full repair (default: false) * * Return Value: * 0: Repair Damage Threshold @@ -15,11 +16,11 @@ * Public: No */ -params ["_unit"]; -TRACE_1("params",_unit); +params ["_unit", ["_override", false]]; +TRACE_2("params",_unit,_override); -//If in facility or near vehicle then complete repair of hitpoint: -if (([_unit] call FUNC(isInRepairFacility) || {[_unit] call FUNC(isNearRepairVehicle)})) exitWith {0}; +//If in facility, near vehicle, or doing full repair then complete repair of hitpoint: +if ((_override || {[_unit] call FUNC(isInRepairFacility)} || {[_unit] call FUNC(isNearRepairVehicle)})) exitWith {0}; private _class = _unit getVariable ["ACE_IsEngineer", getNumber (configOf _unit >> "engineer")]; //If advanced or more qualified than min, then use engineer threshold: diff --git a/addons/repair/functions/fnc_isInRepairFacility.sqf b/addons/repair/functions/fnc_isInRepairFacility.sqf index 1693297494..a0a1615cd9 100644 --- a/addons/repair/functions/fnc_isInRepairFacility.sqf +++ b/addons/repair/functions/fnc_isInRepairFacility.sqf @@ -15,28 +15,15 @@ * Public: Yes */ +#define CHECK_OBJECTS(var) ((var) findIf {(_x getVariable ["ACE_isRepairFacility", getNumber (configOf _x >> QGVAR(canRepair))] > 0) && {!(_x isKindOf "AllVehicles")} && {alive _x}} != -1) + params [["_object", objNull, [objNull]]]; TRACE_1("params",_object); -private _position = getPosASL _object; -private _isInBuilding = false; -private _checkObject = { - if ( - _x getVariable ["ACE_isRepairFacility", getNumber (configOf _x >> QGVAR(canRepair))] > 0 - && {!(_x isKindOf "AllVehicles")} // check if it's not repair vehicle - && {alive _x} - ) exitWith { - _isInBuilding = true; - }; +private _fnc_check = { + private _position = _object modelToWorldVisual [0, 0, eyePos _object select 2]; + CHECK_OBJECTS(lineIntersectsWith [ARR_3(_position, _position vectorAdd [ARR_3(0, 0, 10)], _object)]) || {CHECK_OBJECTS(_object nearObjects 7.5)} }; -private _objects = (lineIntersectsWith [_object modelToWorldVisual [0, 0, (_position select 2)], _object modelToWorldVisual [0, 0, (_position select 2) +10], _object]); -_checkObject forEach _objects; - -if (_isInBuilding) exitWith {true}; - -_objects = _object nearObjects 7.5; -_checkObject forEach _objects; - -_isInBuilding +[[], _fnc_check, _object, QGVAR(inRepairFacilityCache), IN_REPAIR_FACILITY_CACHE_EXPIRY] call EFUNC(common,cachedCall); diff --git a/addons/repair/functions/fnc_isNearRepairVehicle.sqf b/addons/repair/functions/fnc_isNearRepairVehicle.sqf index 1243c0f95b..3a894162ee 100644 --- a/addons/repair/functions/fnc_isNearRepairVehicle.sqf +++ b/addons/repair/functions/fnc_isNearRepairVehicle.sqf @@ -15,14 +15,14 @@ * Public: Yes */ +#define CHECK_OBJECTS(var) ((var) findIf {alive _x && {[_x] call FUNC(isRepairVehicle)}} != -1) + params ["_unit"]; TRACE_1("params",_unit); -private _nearObjects = nearestObjects [_unit, ["Air", "LandVehicle", "Slingload_base_F"], 20]; +private _fnc_check = { + private _nearObjects = nearestObjects [_unit, ["Air", "LandVehicle", "Slingload_base_F"], 20]; + CHECK_OBJECTS(_nearObjects) +}; -private _return = false; -{ - if (alive _x && {[_x] call FUNC(isRepairVehicle)}) exitWith {_return = true;}; -} forEach _nearObjects; - -_return; +[[], _fnc_check, _unit, QGVAR(nearRepairVehicleCache), NEAR_REPAIR_VEHICLE_CACHE_EXPIRY] call EFUNC(common,cachedCall); diff --git a/addons/repair/functions/fnc_repair.sqf b/addons/repair/functions/fnc_repair.sqf index 51fdea90fe..a0654d2555 100644 --- a/addons/repair/functions/fnc_repair.sqf +++ b/addons/repair/functions/fnc_repair.sqf @@ -148,6 +148,7 @@ if (_callbackProgress == "") then { // Player Animation private _callerAnim = [getText (_config >> "animationCaller"), getText (_config >> "animationCallerProne")] select (stance _caller == "PRONE"); +private _loopAnim = (getNumber (_config >> "loopAnimation")) isEqualTo 1; _caller setVariable [QGVAR(selectedWeaponOnrepair), currentWeapon _caller]; // Cannot use secondairy weapon for animation @@ -171,10 +172,27 @@ if (vehicle _caller == _caller && {_callerAnim != ""}) then { } else { _caller setVariable [QGVAR(repairPrevAnimCaller), animationState _caller]; }; + _caller setVariable [QGVAR(repairCurrentAnimCaller), toLower _callerAnim]; [_caller, _callerAnim] call EFUNC(common,doAnimation); }; }; +if (_loopAnim) then { + private _animDoneEh = _caller addEventHandler ["AnimDone", { + params ["_caller", "_anim"]; + if (_anim isEqualTo (_caller getVariable [QGVAR(repairCurrentAnimCaller), ""])) then { + [{ + params ["_caller", "_anim"]; + if !(isNil {_caller getVariable QGVAR(repairCurrentAnimCaller)}) then { + TRACE_2("loop",_caller,_anim); + _this call EFUNC(common,doAnimation) + }; + }, [_caller, _anim], 2.5] call CBA_fnc_waitAndExecute; + }; + }]; + _caller setVariable [QGVAR(repairLoopAnimEh), _animDoneEh]; +}; + private _soundPosition = _caller modelToWorldVisualWorld (_caller selectionPosition "RightHand"); ["Acts_carFixingWheel", _soundPosition, nil, 50] call EFUNC(common,playConfigSound3D); @@ -213,7 +231,10 @@ private _hitPointClassname = if (_hitPoint isEqualType "") then { }; private _processText = getText (_config >> "displayNameProgress"); private _backupText = format [localize LSTRING(RepairingHitPoint), _hitPointClassname]; -([_hitPointClassname, _processText, _backupText] call FUNC(getHitPointString)) params ["_text"]; +private _text = _processText; +if (getNumber (_config >> "forceDisplayName") isNotEqualTo 1) then { + _text = ([_hitPointClassname, _processText, _backupText] call FUNC(getHitPointString)) select 0; +}; TRACE_4("display",_hitPoint,_hitPointClassname,_processText,_text); diff --git a/addons/repair/functions/fnc_repair_failure.sqf b/addons/repair/functions/fnc_repair_failure.sqf index 83cce85e0d..5acd9564e7 100644 --- a/addons/repair/functions/fnc_repair_failure.sqf +++ b/addons/repair/functions/fnc_repair_failure.sqf @@ -29,9 +29,13 @@ TRACE_5("params",_caller,_target,_selectionName,_className,_usersOfItems); if (primaryWeapon _caller == "ACE_FakePrimaryWeapon") then { _caller removeWeapon "ACE_FakePrimaryWeapon"; }; + +_caller removeEventHandler ["AnimDone", _caller getVariable [QGVAR(repairLoopAnimEh), -1]]; +_caller setVariable [QGVAR(repairLoopAnimEh), nil]; if (vehicle _caller == _caller && {!(_caller call EFUNC(common,isSwimming))}) then { [_caller, _caller getVariable [QGVAR(repairPrevAnimCaller), ""], 2] call EFUNC(common,doAnimation); }; +_caller setVariable [QGVAR(repairCurrentAnimCaller), nil]; _caller setVariable [QGVAR(repairPrevAnimCaller), nil]; private _weaponSelect = (_caller getVariable [QGVAR(selectedWeaponOnrepair), ""]); diff --git a/addons/repair/functions/fnc_repair_success.sqf b/addons/repair/functions/fnc_repair_success.sqf index a86be84244..99778c37dd 100644 --- a/addons/repair/functions/fnc_repair_success.sqf +++ b/addons/repair/functions/fnc_repair_success.sqf @@ -29,9 +29,13 @@ TRACE_4("params",_caller,_target,_selectionName,_className); if (primaryWeapon _caller == "ACE_FakePrimaryWeapon") then { _caller removeWeapon "ACE_FakePrimaryWeapon"; }; + +_caller removeEventHandler ["AnimDone", _caller getVariable [QGVAR(repairLoopAnimEh), -1]]; +_caller setVariable [QGVAR(repairLoopAnimEh), nil]; if (vehicle _caller == _caller && {!(_caller call EFUNC(common,isSwimming))}) then { [_caller, _caller getVariable [QGVAR(repairPrevAnimCaller), ""], 2] call EFUNC(common,doAnimation); }; +_caller setVariable [QGVAR(repairCurrentAnimCaller), nil]; _caller setVariable [QGVAR(repairPrevAnimCaller), nil]; private _weaponSelect = (_caller getVariable [QGVAR(selectedWeaponOnrepair), ""]); diff --git a/addons/repair/initSettings.sqf b/addons/repair/initSettings.sqf index 82a411c91d..066e944a50 100644 --- a/addons/repair/initSettings.sqf +++ b/addons/repair/initSettings.sqf @@ -1,7 +1,9 @@ +private _category = format ["ACE %1", LLSTRING(Repair)]; + [ QGVAR(displayTextOnRepair), "CHECKBOX", [LSTRING(SettingDisplayTextName), LSTRING(SettingDisplayTextDesc)], - [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], + _category, true, // default value false, // isGlobal {[QGVAR(displayTextOnRepair), _this] call EFUNC(common,cbaSettings_settingChanged)} @@ -10,7 +12,7 @@ [ QGVAR(engineerSetting_repair), "LIST", [LSTRING(engineerSetting_Repair_name), LSTRING(engineerSetting_Repair_description)], - [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], + _category, [[0,1,2],[LSTRING(engineerSetting_anyone), LSTRING(engineerSetting_EngineerOnly), LSTRING(engineerSetting_AdvancedOnly)],1], // [values, titles, defaultIndex] true, // isGlobal {[QGVAR(engineerSetting_repair), _this] call EFUNC(common,cbaSettings_settingChanged)} @@ -19,7 +21,7 @@ [ QGVAR(engineerSetting_wheel), "LIST", [LSTRING(engineerSetting_Wheel_name), LSTRING(engineerSetting_Wheel_description)], - [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], + _category, [[0,1,2],[LSTRING(engineerSetting_anyone), LSTRING(engineerSetting_EngineerOnly), LSTRING(engineerSetting_AdvancedOnly)],0], // [values, titles, defaultIndex] true, // isGlobal {[QGVAR(engineerSetting_wheel), _this] call EFUNC(common,cbaSettings_settingChanged)} @@ -28,7 +30,7 @@ [ QGVAR(patchWheelEnabled), "LIST", [LSTRING(patchWheelEnabled_name), LSTRING(patchWheelEnabled_description)], - [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], + _category, [[-1,0,1,2],["str_player_none", LSTRING(engineerSetting_anyone), LSTRING(engineerSetting_EngineerOnly), LSTRING(engineerSetting_AdvancedOnly)],1], // default value true // isGlobal ] call CBA_fnc_addSetting; @@ -37,7 +39,7 @@ QGVAR(patchWheelRequiredItems), "LIST", [LSTRING(patchWheelRequiredItems_DisplayName), LSTRING(patchWheelRequiredItems_Description)], - [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], + _category, [[[], [ANY_TOOLKIT_FAKECLASS]], ["STR_A3_None", "STR_A3_CfgWeapons_Toolkit0"], 1], true ] call CBA_fnc_addSetting; @@ -46,7 +48,7 @@ QGVAR(patchWheelLocation), "LIST", [LSTRING(patchWheelLocation_DisplayName), LSTRING(patchWheelLocation_Description)], - [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], + _category, [[["ground", "vehicle"], ["vehicle"], ["ground"]], ["str_difficulty_any", LSTRING(patchWheelOnVehicle), LSTRING(patchWheelOnGround)], 0], true ] call CBA_fnc_addSetting; @@ -54,8 +56,8 @@ [ QGVAR(repairDamageThreshold), "SLIDER", [LSTRING(repairDamageThreshold_name), LSTRING(repairDamageThreshold_description)], - [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], - [0,1,0.6,2], // [min, max, default value, trailing decimals (-1 for whole numbers only)] + _category, + [0, 1, 0.6, 1, true], true, // isGlobal {[QGVAR(repairDamageThreshold), _this] call EFUNC(common,cbaSettings_settingChanged)} ] call CBA_fnc_addSetting; @@ -63,8 +65,8 @@ [ QGVAR(repairDamageThreshold_engineer), "SLIDER", [LSTRING(repairDamageThreshold_Engineer_name), LSTRING(repairDamageThreshold_Engineer_description)], - [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], - [0,1,0.4,2], // [min, max, default value, trailing decimals (-1 for whole numbers only)] + _category, + [0, 1, 0.4, 1, true], true, // isGlobal {[QGVAR(repairDamageThreshold_engineer), _this] call EFUNC(common,cbaSettings_settingChanged)} ] call CBA_fnc_addSetting; @@ -72,7 +74,7 @@ [ QGVAR(consumeItem_toolKit), "LIST", // fnc_repair expects number [LSTRING(consumeItem_ToolKit_name), LSTRING(consumeItem_ToolKit_description)], - [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], + _category, [[0,1],[ELSTRING(common,No), ELSTRING(common,Yes)],0], // [values, titles, defaultIndex] true, // isGlobal {[QGVAR(consumeItem_toolKit), _this] call EFUNC(common,cbaSettings_settingChanged)} @@ -82,7 +84,7 @@ QGVAR(locationsBoostTraining), "CHECKBOX", [ELSTRING(common,LocationsBoostTraining_DisplayName), LSTRING(LocationsBoostTraining_Description)], - [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], + _category, false, true ] call CBA_fnc_addSetting; @@ -90,7 +92,7 @@ [ QGVAR(fullRepairLocation), "LIST", [LSTRING(fullRepairLocation), LSTRING(fullRepairLocation_description)], - [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], + _category, [[0,1,2,3,4],[LSTRING(useAnywhere), LSTRING(repairVehicleOnly), LSTRING(repairFacilityOnly), LSTRING(vehicleAndFacility), ELSTRING(common,Disabled)],2], // [values, titles, defaultIndex] true, // isGlobal {[QGVAR(fullRepairLocation), _this] call EFUNC(common,cbaSettings_settingChanged)} @@ -99,16 +101,24 @@ [ QGVAR(engineerSetting_fullRepair), "LIST", [LSTRING(engineerSetting_fullRepair_name), LSTRING(engineerSetting_fullRepair_description)], - [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], + _category, [[0,1,2],[LSTRING(engineerSetting_anyone), LSTRING(engineerSetting_EngineerOnly), LSTRING(engineerSetting_AdvancedOnly)],2], // [values, titles, defaultIndex] true, // isGlobal {[QGVAR(engineerSetting_fullRepair), _this] call EFUNC(common,cbaSettings_settingChanged)} ] call CBA_fnc_addSetting; +[ + QGVAR(timeCoefficientFullRepair), "SLIDER", + [LSTRING(timeCoefficientFullRepair_name), LSTRING(timeCoefficientFullRepair_description)], + _category, + [0,3,1.5,2], + true +] call CBA_fnc_addSetting; + [ QGVAR(addSpareParts), "CHECKBOX", [LSTRING(addSpareParts_name), LSTRING(addSpareParts_description)], - [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], + _category, true, // default value true, // isGlobal {[QGVAR(addSpareParts), _this] call EFUNC(common,cbaSettings_settingChanged)}, @@ -119,7 +129,7 @@ QGVAR(wheelRepairRequiredItems), "LIST", [LSTRING(WheelRepairRequiredItems_DisplayName), LSTRING(WheelRepairRequiredItems_Description)], - [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], + _category, [[[], [ANY_TOOLKIT_FAKECLASS]], ["STR_A3_None", "STR_A3_CfgWeapons_Toolkit0"], 0], true ] call CBA_fnc_addSetting; @@ -128,7 +138,7 @@ QGVAR(miscRepairRequiredItems), "LIST", [LSTRING(MiscRepairRequiredItems_DisplayName), LSTRING(MiscRepairRequiredItems_Description)], - [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], + _category, [[[], [ANY_TOOLKIT_FAKECLASS]], ["STR_A3_None", "STR_A3_CfgWeapons_Toolkit0"], 1], true ] call CBA_fnc_addSetting; @@ -137,7 +147,7 @@ QGVAR(fullRepairRequiredItems), "LIST", [LSTRING(FullRepairRequiredItems_DisplayName), LSTRING(FullRepairRequiredItems_Description)], - [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], + _category, [[[], [ANY_TOOLKIT_FAKECLASS]], ["STR_A3_None", "STR_A3_CfgWeapons_Toolkit0"], 1], true ] call CBA_fnc_addSetting; @@ -145,18 +155,33 @@ [ QGVAR(autoShutOffEngineWhenStartingRepair), "CHECKBOX", [LSTRING(autoShutOffEngineWhenStartingRepair_name), LSTRING(autoShutOffEngineWhenStartingRepair_description)], - [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], + _category, false, // default value true, // isGlobal {[QGVAR(autoShutOffEngineWhenStartingRepair), _this] call EFUNC(common,cbaSettings_settingChanged)} ] call CBA_fnc_addSetting; +[ + QGVAR(miscRepairTime), "SLIDER", + [LSTRING(miscRepairTime_name), LSTRING(miscRepairTime_description)], + _category, + [0,60,15,-1], // [min, max, default value, trailing decimals (-1 for whole numbers only)] + true +] call CBA_fnc_addSetting; + +[ + QGVAR(wheelChangeTime), "SLIDER", + [LSTRING(wheelChangeTime_name), LSTRING(wheelChangeTime_description)], + _category, + [0,60,10,-1], // [min, max, default value, trailing decimals (-1 for whole numbers only)] + true +] call CBA_fnc_addSetting; [ QGVAR(patchWheelTime), "SLIDER", [LSTRING(patchWheelTime_DisplayName), LSTRING(patchWheelTime_Description)], - [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], + _category, [0.1, 60, 5, 1], true ] call CBA_fnc_addSetting; @@ -165,7 +190,7 @@ QGVAR(patchWheelMaximumRepair), "SLIDER", [LSTRING(patchWheelMaximumRepair_DisplayName), LSTRING(patchWheelMaximumRepair_Description)], - [localize ELSTRING(OptionsMenu,CategoryLogistics), localize "str_state_repair"], + _category, [0, 1, 0.3, 1, true], true ] call CBA_fnc_addSetting; diff --git a/addons/repair/script_component.hpp b/addons/repair/script_component.hpp index 2a6196c6fc..9fab647079 100644 --- a/addons/repair/script_component.hpp +++ b/addons/repair/script_component.hpp @@ -20,6 +20,10 @@ #define DAMAGE_COLOR_SCALE ["#FFFFFF", "#FFFF7E", "#FFEC4D", "#FFD52C", "#FCB121", "#FF9916", "#FF7D16", "#FF4400", "#FF0000"] +#define IN_REPAIR_FACILITY_CACHE_EXPIRY 1 + +#define NEAR_REPAIR_VEHICLE_CACHE_EXPIRY 1 + #define ANY_TOOLKIT_FAKECLASS QGVAR(anyToolKit) #define PATCH_WHEEL_STEP_TIME 0.05 diff --git a/addons/repair/stringtable.xml b/addons/repair/stringtable.xml index 3b650f13d5..4929e0f31a 100644 --- a/addons/repair/stringtable.xml +++ b/addons/repair/stringtable.xml @@ -266,6 +266,12 @@ 維修載具中... Tamir Ediliyor... + + Full Repair Time Coefficient + + + Modifies how long it takes to perform a Full Repair.\nThe repair time is based on based on the amount of repairs needed for each part, including those normally inaccessible. + Boost engineer training when in repair vehicles or facilities. Untrained becomes engineer, engineer becomes advanced engineer. Améliore les compétences en ingénierie des unités en fonction du lieu où elles se trouvent ; notamment dans les véhicules de réparation ou les ateliers.\nUn soldat non formé devient ingénieur, un ingénieur devient ingénieur avancé. @@ -1285,7 +1291,7 @@ 維修門檻 - What is the maximum damage that can be repaired with a toolkit? + Maximum damage to which a part can be repaired with a toolkit.\n0% means all damage can be repaired. Der maximale Schaden, der von einem Reperatursatz behoben werden kann? Jaki jest maksymalny poziom uszkodzeń jaki może zostać naprawiony przy pomocy narzędzi? Qual é o dano máximo que pode ser reparado com um kit de ferramentas? @@ -1315,7 +1321,7 @@ 維修門檻 (工兵) - What is the maximum damage that can be repaired by an engineer? + Maximum damage to which a part can be repaired by an engineer above the minimum level required for the repair.\n0% means all damage can be repaired. Der maximale Schaden, der von einem Pionier behoben werden kann? Jaki jest maksymalny poziom uszkodzeń jaki może zostać naprawiony przez mechanika? Qual é o dano máximo que pode ser reparado com um engenheiro? @@ -1860,7 +1866,7 @@ 選擇的備件數量 - Wheel Repair Requirements + Wheel Change Requirements Bedingungen für die Reifenreperatur Wym. naprawy kół Requisitos de reparación de ruedas @@ -2062,6 +2068,18 @@ Apagar el motor automáticamente al efectuar una reparación 수리 시 엔진을 자동으로 끕니다. + + Part Repair Time + + + Time in seconds to complete a repair. + + + Wheel Change Time + + + Time in seconds to remove or change a wheel. + Patch Wheel Radflicken @@ -2071,16 +2089,16 @@ Radflicken... - Patch wheel time + Wheel Patch Time Time it takes to patch a wheel by 5%. - Patch wheel maximum repair + Patch Wheel Threshold - Maximum level a wheel can be patched. + Maximum level to which a wheel can be patched. Wheel Patch Location diff --git a/docs/wiki/framework/field-rations-framework.md b/docs/wiki/framework/field-rations-framework.md index 54d2b22e90..bc433e7d52 100644 --- a/docs/wiki/framework/field-rations-framework.md +++ b/docs/wiki/framework/field-rations-framework.md @@ -28,6 +28,7 @@ Config Name | Type | Description `acex_field_rations_refillItem` | String | Makes an item refillable, class name of item added when refilled (OPTIONAL) `acex_field_rations_refillAmount` | Number | Amount of water required to refill item (OPTIONAL) `acex_field_rations_refillTime` | Number | Time required to refill item (in seconds) (OPTIONAL) +`ACE_isFieldRationItem` | Number | Force adds the item to the ACE Field Rations category in ACE Arsenal (OPTIONAL) _* Value range is 0 to 100 and can be modified by the corresponding coefficient setting._ diff --git a/docs/wiki/framework/medical-treatment-framework.md b/docs/wiki/framework/medical-treatment-framework.md index a3f5d04de1..adb9cd485d 100644 --- a/docs/wiki/framework/medical-treatment-framework.md +++ b/docs/wiki/framework/medical-treatment-framework.md @@ -38,3 +38,21 @@ class CfgVehicles { }; }; ``` +### 1.3 Treatment Items + +Items in `CfgWeapons` with `ACE_isMedicalItem` property will be added to the ACE Medical category in the ACE Arsenal. +```cpp +class CfgWeapons { + class MyMedicalItem { + ACE_isMedicalItem = 1; + }; +}; +``` +Required items in `ACE_Medical_Treatment_Actions` will also be added as a fallback. +```cpp +class ACE_Medical_Treatment_Actions { + class MyCustomTreatment { + items[] = {"MyMedicalItem"}; + }; +}; +``` diff --git a/docs/wiki/framework/wirecutter-framework.md b/docs/wiki/framework/wirecutter-framework.md new file mode 100644 index 0000000000..3f2a97c0b1 --- /dev/null +++ b/docs/wiki/framework/wirecutter-framework.md @@ -0,0 +1,41 @@ +--- +layout: wiki +title: Wirecutter Framework +description: Explains how to set-up items, backpacks and vests with ACE3 wirecutter system. +group: framework +order: 5 +parent: wiki +mod: ace +version: + major: 3 + minor: 16 + patch: 0 +--- + +## 1. Config Values + +The properties below control whether an item is considered a wirecutter. Worn items (vests and backpacks) can have "built-in" wirecutters with the `ace_logistics_wirecutter_hasWirecutter` property. +```cpp +class CfgWeapons { + class MyVest { + ace_logistics_wirecutter_hasWirecutter = 1; //1 for true, 0 for false. + }; + class MyItem { + ACE_isWirecutter = 1; //1 for true, 0 for false. + }; +}; +class CfgVehicles { + class MyBackpack { + ace_logistics_wirecutter_hasWirecutter = 1; //1 for true, 0 for false. + }; +}; +``` + + +## 2. Events + +### 2.1 Listenable + +Event Name | Description | Passed Parameter(s) | Locality +---------- | ----------- | ------------------- | -------- +`ace_wireCuttingStarted` | Wire cutting started | `[_unit, _fence]` | Global