#include "..\script_component.hpp" /* * Author: PabstMirror * * Makes a unit drop items * * Arguments: * 0: Caller (player) * 1: Target * 2: Classnames * 3: Do Not Drop Ammo (default: false) * * Return Value: * None * * Example: * [player, cursorTarget, ["ace_bandage"]] call ace_disarming_fnc_disarmDropItems * * Public: No */ #define TIME_MAX_WAIT 5 params ["_caller", "_target", "_listOfItemsToRemove", ["_doNotDropAmmo", false, [false]]]; //By default units drop all weapon mags when dropping a weapon private _fncSumArray = { private _return = 0; {_return = _return + _x;} count (_this select 0); _return }; //Sanity Checks if !([_target] call FUNC(canBeDisarmed)) exitWith { [_caller, _target, "Debug: Cannot disarm target"] call FUNC(eventTargetFinish); }; if (_doNotDropAmmo && {({_x in _listOfItemsToRemove} count (magazines _target)) > 0}) exitWith { [_caller, _target, "Debug: Trying to drop magazine with _doNotDropAmmo flag"] call FUNC(eventTargetFinish); }; private _holder = objNull; // if _target is in a vehicle, use vehicle inventory as container if (!isNull objectParent _target) then { _holder = objectParent _target; }; //If not dropping ammo, don't use an existing container if (!_doNotDropAmmo) then { { if ((_x getVariable [QGVAR(disarmUnit), objNull]) == _target) exitWith { _holder = _x; }; } forEach ((getpos _target) nearObjects [DISARM_CONTAINER, 3]); }; //Create a new weapon holder if (isNull _holder) then { private _dropPos = _target modelToWorld [0.4, 0.75, 0]; //offset someone unconscious isn't lying over it _dropPos set [2, ((getPosASL _target) select 2)]; _holder = createVehicle [DISARM_CONTAINER, _dropPos, [], 0, "CAN_COLLIDE"]; _holder setPosASL _dropPos; _holder setVariable [QGVAR(disarmUnit), _target, true]; }; //Verify holder created if (isNull _holder) exitWith { [_caller, _target, "Debug: Null Holder"] call FUNC(eventTargetFinish); }; //Make sure only one drop operation at a time (using PFEH system as a queue) if (_holder getVariable [QGVAR(holderInUse), false]) exitWith { [{ _this call FUNC(disarmDropItems); }, _this] call CBA_fnc_execNextFrame; }; _holder setVariable [QGVAR(holderInUse), true]; //Remove Magazines private _targetMagazinesStart = magazinesAmmo _target; private _holderMagazinesStart = magazinesAmmoCargo _holder; { _x params ["_xClassname", "_xAmmo"]; if ((_xClassname in _listOfItemsToRemove) && {(getNumber (configFile >> "CfgMagazines" >> _xClassname >> "ACE_isUnique")) == 0}) then { _holder addMagazineAmmoCargo [_xClassname, 1, _xAmmo]; _target removeMagazine _xClassname; }; } forEach _targetMagazinesStart; private _targetMagazinesEnd = magazinesAmmo _target; private _holderMagazinesEnd = magazinesAmmoCargo _holder; //Verify Mags dropped from unit: if (({((_x select 0) in _listOfItemsToRemove) && {(getNumber (configFile >> "CfgMagazines" >> (_x select 0) >> "ACE_isUnique")) == 0}} count _targetMagazinesEnd) != 0) exitWith { _holder setVariable [QGVAR(holderInUse), false]; [_caller, _target, "Debug: Didn't Remove Magazines"] call FUNC(eventTargetFinish); }; //Verify holder has mags unit had if !([_targetMagazinesStart, _targetMagazinesEnd, _holderMagazinesStart, _holderMagazinesEnd] call FUNC(verifyMagazinesMoved)) then { _holder setVariable [QGVAR(holderInUse), false]; [_caller, _target, "Debug: Crate Magazines not in holder"] call FUNC(eventTargetFinish); }; //Remove Items, Assigned Items and NVG private _holderItemsStart = getitemCargo _holder; private _targetItemsStart = (assignedItems _target) + (items _target) - (weapons _target); if ((headgear _target) != "") then {_targetItemsStart pushBack (headgear _target);}; if ((goggles _target) != "") then {_targetItemsStart pushBack (goggles _target);}; private _addToCrateClassnames = []; private _addToCrateCount = []; { if (_x in _listOfItemsToRemove) then { if (_x in (items _target)) then { _target removeItem _x; } else { _target unlinkItem _x; }; private _index = _addToCrateClassnames find _x; if (_index != -1) then { _addToCrateCount set [_index, ((_addToCrateCount select _index) + 1)]; } else { _addToCrateClassnames pushBack _x; _addToCrateCount pushBack 1; }; }; } forEach _targetItemsStart; //Add the items to the holder (combined to reduce addItemCargoGlobal calls) { _holder addItemCargoGlobal [(_addToCrateClassnames select _forEachIndex), (_addToCrateCount select _forEachIndex)]; } forEach _addToCrateClassnames; private _holderItemsEnd = getitemCargo _holder; private _targetItemsEnd = (assignedItems _target) + (items _target) - (weapons _target); if ((headgear _target) != "") then {_targetItemsEnd pushBack (headgear _target);}; if ((goggles _target) != "") then {_targetItemsEnd pushBack (goggles _target);}; //Verify Items Added if (((count _targetItemsStart) - (count _targetItemsEnd)) != ([_addToCrateCount] call _fncSumArray)) exitWith { _holder setVariable [QGVAR(holderInUse), false]; [_caller, _target, "Debug: Items Not Removed From Player"] call FUNC(eventTargetFinish); }; if ((([_holderItemsEnd select 1] call _fncSumArray) - ([_holderItemsStart select 1] call _fncSumArray)) != ([_addToCrateCount] call _fncSumArray)) exitWith { _holder setVariable [QGVAR(holderInUse), false]; [_caller, _target, "Debug: Items Not Added to Holder"] call FUNC(eventTargetFinish); }; //Script drop uniforms/vest if empty if (((uniform _target) != "") && {(uniform _target) in _listOfItemsToRemove} && {(uniformItems _target) isEqualTo []}) then { _holder addItemCargoGlobal [(uniform _target), 1]; removeUniform _target; }; if (((vest _target) != "") && {(vest _target) in _listOfItemsToRemove} && {(vestItems _target) isEqualTo []}) then { _holder addItemCargoGlobal [(vest _target), 1]; removeVest _target; }; //If holder is still empty, it will be 'garbage collected' while we wait for the drop 'action' to take place //So add a dummy item and just remove at the end private _holderIsEmpty = ([_holder] call FUNC(getAllGearContainer)) isEqualTo [[],[]]; if (_holderIsEmpty) then { TRACE_1("Debug: adding dummy item to holder",_holder); _holder addItemCargoGlobal [DUMMY_ITEM, 1]; }; //Start the PFEH to do the actions (which could take >1 frame) [{ params ["_args", "_pfID"]; _args params ["_caller", "_target", "_listOfItemsToRemove", "_holder", "_holderIsEmpty", "_maxWaitTime", "_doNotDropAmmo", "_startingMagazines"]; private _needToRemoveWeapon = ({_x in _listOfItemsToRemove} count (weapons _target)) > 0; private _needToRemoveMagazines = ({_x in _listOfItemsToRemove} count (magazines _target)) > 0; private _needToRemoveBackpack = ((backPack _target) != "") && {(backPack _target) in _listOfItemsToRemove}; private _needToRemoveVest = ((vest _target) != "") && {(vest _target) in _listOfItemsToRemove}; private _needToRemoveUniform = ((uniform _target) != "") && {(uniform _target) in _listOfItemsToRemove}; if ((CBA_missionTime < _maxWaitTime) && {[_target] call FUNC(canBeDisarmed)} && {_needToRemoveWeapon || _needToRemoveMagazines || _needToRemoveBackpack}) then { //action drop weapons (keeps loaded magazine and attachements) { if (_x in _listOfItemsToRemove) then { _target action ["DropWeapon", _holder, _x]; }; } forEach (weapons _target); //Drop magazine (keeps unique ID) { if (_x in _listOfItemsToRemove) then { _target action ["DropMagazine", _holder, _x]; }; } forEach (magazines _target); //Drop backpack (Keeps variables for ACRE/TFR) if (_needToRemoveBackpack) then {_target action ["DropBag", _holder, (backPack _target)];}; } else { [_pfID] call CBA_fnc_removePerFrameHandler; if (_doNotDropAmmo) then { private _error = false; private _magsToPickup = +_startingMagazines; { private _index = _magsToPickup find _x; if (_index == -1) exitWith {_error = true; ERROR("More mags than when we started?")}; _magsToPickup deleteAt _index; } forEach (magazinesAmmo _target); private _magazinesInHolder = magazinesAmmoCargo _holder; { private _index = _magazinesInHolder find _x; if (_index == -1) exitWith {_error = true; ERROR("Missing mag not in holder")}; _magazinesInHolder deleteAt _index; } forEach _magsToPickup; //No Error (all the ammo in the container is ammo we should have); if ((!_error) && {_magazinesInHolder isEqualTo []}) then { { _target addMagazine _x; } forEach (magazinesAmmoCargo _holder); clearMagazineCargoGlobal _holder; }; }; //If we added a dummy item, remove it now if (_holderIsEmpty && {(getItemCargo _holder) isNotEqualTo [[DUMMY_ITEM],[1]]}) exitWith { _holder setVariable [QGVAR(holderInUse), false]; [_caller, _target, "Debug: Holder should only have dummy item"] call FUNC(eventTargetFinish); }; if (_holderIsEmpty) then { TRACE_1("Debug: removing dummy item from holder",_holder); clearItemCargoGlobal _holder; }; //Verify we didn't timeout waiting on drop action if (CBA_missionTime >= _maxWaitTime) exitWith { _holder setVariable [QGVAR(holderInUse), false]; [_caller, _target, "Debug: Drop Actions Timeout"] call FUNC(eventTargetFinish); }; //If target lost disarm status: if !([_target] call FUNC(canBeDisarmed)) exitWith { _holder setVariable [QGVAR(holderInUse), false]; [_caller, _target, "Debug: Target cannot be disarmed"] call FUNC(eventTargetFinish); }; if (_needToRemoveVest && {(vestItems _target) isNotEqualTo []}) exitWith { _holder setVariable [QGVAR(holderInUse), false]; [_caller, _target, "Debug: Vest Not Empty"] call FUNC(eventTargetFinish); }; if (_needToRemoveVest) then { _holder addItemCargoGlobal [(vest _target), 1]; removeVest _target; }; if (_needToRemoveUniform && {(uniformItems _target) isNotEqualTo []}) exitWith { _holder setVariable [QGVAR(holderInUse), false]; [_caller, _target, "Debug: Uniform Not Empty"] call FUNC(eventTargetFinish); }; if (_needToRemoveUniform) then { _holder addItemCargoGlobal [(uniform _target), 1]; removeUniform _target; }; _holder setVariable [QGVAR(holderInUse), false]; [_caller, _target, ""] call FUNC(eventTargetFinish); }; }, 0.0, [_caller,_target, _listOfItemsToRemove, _holder, _holderIsEmpty, (CBA_missionTime + TIME_MAX_WAIT), _doNotDropAmmo, _targetMagazinesEnd]] call CBA_fnc_addPerFrameHandler;