Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Looting rewrite #120

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
1daae95
Refactor and reformat fn_recover.sqf
Tuupertunut Jun 19, 2024
6361a79
Fix looting progress bar running for a shorter time than the waiting …
Tuupertunut Jun 20, 2024
398607f
Optimize body search code when looting
Tuupertunut Jun 21, 2024
a0443fe
Loot every item on the ground, not just dropped weapons
Tuupertunut Jun 22, 2024
3292da7
Properly loot a body
Tuupertunut Jun 23, 2024
de89819
Replace vanilla medical items with ACE ones when looting.
Tuupertunut Jun 24, 2024
981bd9c
Fix some issues in new looting code
Tuupertunut Jun 25, 2024
59f6eb4
Small improvements to new looting code
Tuupertunut Jun 25, 2024
c5d19ef
Refactor recruit looting code and some fixes
Tuupertunut Jun 27, 2024
4eb06e1
Move new container management functions to separate files
Tuupertunut Jul 1, 2024
6364882
Add functions for checking if container has space
Tuupertunut Jul 6, 2024
8422490
Partial rewrite of the looter looting system
Tuupertunut Jul 6, 2024
718b87a
Loot weapons dropped just outside the looting range when using recove…
Tuupertunut Jul 6, 2024
f9229a1
Small improvements to fn_recover
Tuupertunut Jul 6, 2024
ce130ab
Remove obsolete OT_looted variable
Tuupertunut Jul 6, 2024
248bfa4
Make multiple looters not compete for the same bodies
Tuupertunut Jul 6, 2024
29d60c8
Raise required Arma 3 version to 2.12
Tuupertunut Jul 6, 2024
d70112e
Fix remaining errors in fn_orderLoot
Tuupertunut Jul 6, 2024
032c985
Fix looter not exiting vehicle
Tuupertunut Jul 7, 2024
785d81f
Use regular break instead of breakOut
Tuupertunut Jul 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions addons/overthrow_main/CfgFunctions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,12 @@ class CfgFunctions
class vehicleGetName {};
class vehicleGetPic {};
class getSearchStock {};
class dumpContainer {};
class dumpUnitLoadout {};
class dumpWeapon {};
class dumpItem {};
class canDumpUnitLoadout {};
class canDumpContainer {};
};

/*
Expand Down
300 changes: 184 additions & 116 deletions addons/overthrow_main/functions/AI/orders/fn_orderLoot.sqf
Original file line number Diff line number Diff line change
@@ -1,131 +1,199 @@
private _sorted = [];
private _myunits = groupSelectedUnits player;

private _selectedUnits = groupSelectedUnits player;
{
player groupSelectUnit [_x, false];
} forEach (groupSelectedUnits player);

_myunits params ["_tt"];
if(!isNull objectParent _tt) then {
_sorted = [objectParent _tt];
}else{
private _objects = _tt nearEntities [["Car","ReammoBox_F","Air","Ship"],20];
if(count _objects isEqualTo 0) exitWith {
"Cannot find any containers or vehicles within 20m of first selected unit" call OT_fnc_notifyMinor;
};
_sorted = [_objects,[],{_x distance _tt},"ASCEND"] call BIS_fnc_SortBy;
} forEach (_selectedUnits);

// If at least one selected unit is a driver of a recovery truck, do truck recovery instead
private _unitInRecoveryTruck = _selectedUnits findIf {objectParent _x isKindOf "OT_I_Truck_recovery" && driver objectParent _x isEqualTo _x};
if (_unitInRecoveryTruck > -1) exitWith {
[_selectedUnits # _unitInRecoveryTruck] spawn OT_fnc_recover;
};

if(count _sorted isEqualTo 0) exitWith {};
private _target = _sorted select 0;
private _sortedTargets = nearestObjects [_selectedUnits # 0, ["Car", "ReammoBox_F", "Air", "Ship"], 20] select {alive _x};
if (count _sortedTargets isEqualTo 0) exitWith {
"Cannot find any containers or vehicles within 20m of first selected unit" call OT_fnc_notifyMinor;
};
private _target = _sortedTargets # 0;

{
if ((typeOf vehicle _x) == "OT_I_Truck_recovery" && (driver vehicle _x) == _x) exitWith {
[_x] spawn OT_fnc_recover;
};
[_x,_target] spawn {
private _active = true;
private _wasincar = false;
private _car = objNull;
[_x, _target] spawn {
params ["_looter", "_target"];

private _unit = _this select 0;
private _range = 100;

_unit setVariable ["NOAI",true,true];
_unit setBehaviour "SAFE";
[_unit, ""] remoteExec ["switchMove", 0, false];
_looter globalChat format["Looting bodies and item piles within %1m into the %2", _range, (typeOf _target) call OT_fnc_vehicleGetName];

if(!isNull objectParent _unit) then {
_car = (objectParent _unit);
doGetOut _unit;
_wasincar = true;
};
_looter setBehaviour "SAFE";
if (!isNull objectParent _looter) then {
doGetOut _looter;
waitUntil {sleep 1; (isNull objectParent _looter) || (!alive _looter)};
};

_t = _this select 1;
_looter doMove ASLtoAGL (getPosASL _target);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to experiment with using moveTo rather than doMove. I believe that with moveTo the unit will attempt to return to formation automatically after moving (or failing to move), whereas with doMove the unit must be explicitly ordered to return. Read notes on the biki page for doMove for more info.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not my code so I don't know the original idea, but is the looter supposed to return to formation after moving? Now it stays wherever it was ordered to go in "ready" state. Both results make sense.


_unit globalchat format["Looting bodies within 100m into the %1",(typeof _t) call OT_fnc_vehicleGetName];
// Wait until looter reaches the target container
private _timeout = time + 30;
waitUntil {sleep 1; (_looter distance _target < 12) || (!alive _looter) || (!alive _target) || (_timeout < time)};
if ((!alive _looter) || (!alive _target) || (_timeout < time)) exitWith {
if (alive _looter) then {_looter globalChat format ["Can't get to the %1, cancelling loot order", (typeOf _target) call OT_fnc_vehicleGetName]};
};

private _istruck = true;
if(count _this isEqualTo 2) then {
_istruck = (_t isKindOf "Truck_F") || (_t isKindOf "ReammoBox_F");
// Looter has reached the target container. Dump his loadout to it.
if !([_looter, _target] call OT_fnc_canDumpUnitLoadout) exitWith {
_looter globalChat "This vehicle is full, cancelling loot order";
};

_unit doMove ASLtoAGL (getPosASL _t);

_timeout = time + 30;
waitUntil {sleep 1; (!alive _unit) || (isNull _t) || (_unit distance _t < 10) || (_timeOut < time) || (unitReady _unit)};
if(!alive _unit || (isNull _t) || (_timeOut < time)) exitWith {};

if !([_unit,_t] call OT_fnc_dumpStuff) then {
_unit globalchat "This vehicle is full, cancelling loot order";
_active = false;
};
private _weapons = (_t nearObjects ["WeaponHolder", 100]) + (_t nearEntities ["WeaponHolderSimulated", 100]);
_unit globalchat format["Looting %1 weapons",count _weapons];
{
_weapon = _x;
_s = (weaponsItems _weapon) select 0;
if(!isNil {_s}) then {
_cls = (_s select 0);
_i = _s select 1;
if(_i != "") then {_t addItemCargoGlobal [_i,1]};
_i = _s select 2;
if(_i != "") then {_t addItemCargoGlobal [_i,1]};
_i = _s select 3;
if(_i != "") then {_t addItemCargoGlobal [_i,1]};

if (!(_t canAdd (_cls call BIS_fnc_baseWeapon)) && !_istruck) exitWith {
_unit globalchat "This vehicle is full, cancelling loot order";
_active = false;
};
_t addWeaponCargoGlobal [_cls call BIS_fnc_baseWeapon,1];
deleteVehicle _weapon;
private _looterOwnUniform = uniform _looter;

[_looter, _target] call OT_fnc_dumpUnitLoadout;
_looter setUnitLoadout [[], [], [], [_looterOwnUniform, []], [], [], "", "", [], ["", "", "", "", "", ""]];

while {true} do {
private _sortedBodies = [];
{
// Some bodies are inside vehicles, so we search through the crew of every vehicle
// we find. Luckily every man is crew of itself so the same code also works for
// bodies on the ground.
private _vehicleOrMan = _x;
{
private _body = _x;
if (!alive _body && !(_body getVariable ["OT_looterReserved", false])) then {
_sortedBodies pushBack _x;
};
} forEach crew _vehicleOrMan;
} forEach (nearestObjects [_target, ["AllVehicles"], _range]);

if (_sortedBodies isNotEqualTo []) then {
// There are bodies to be looted. Loot the nearest body.

_looter globalChat format ["%1 bodies to loot", count _sortedBodies];
private _body = _sortedBodies # 0;

_body setVariable ["OT_looterReserved", true, false];
_looter doMove ASLtoAGL (getPosASL _body);
[_looter, 1] call OT_fnc_experience;

// Wait until looter reaches the body
_timeout = time + 30;
waitUntil {sleep 1; (_looter distance2D _body < 12) || (isNull _body) || (!alive _looter) || (!alive _target) || (_timeout < time)};
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moveToCompleted could be used here to avoid waiting the full timeout if the unit fails to reach the destination.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable. The timeout may not be needed if moveToCompleted always returns true when moving fails.

if ((!alive _looter) || (!alive _target) || (_timeout < time)) then {
if (alive _looter) then {_looter globalChat "Can't get to a body, cancelling loot order"};
_body setVariable ["OT_looterReserved", false, false];
break;
};
if (isNull _body) then {
_looter globalChat "Body has vanished, skipping";
continue;
};

// Looter has reached the body. Transfer its loadout to the looter.
private _lootedLoadout = getUnitLoadout _body;

// If the looter has his own uniform, keep it.
if (_looterOwnUniform isNotEqualTo "") then {
private _bodyUniformContent = _lootedLoadout # 3 param [1, []];
_lootedLoadout set [3, [_looterOwnUniform, _bodyUniformContent]];
};

// Also take the dropped weapons belonging to the body. The body may have dropped
// its weapons outside of the search range and they would get deleted when the body
// is deleted, so search for dropped weapons 10m further than bodies. It is still
// possible that the weapon has flown more than 10m outside the range, in which case
// it is lost. This code can be simplified in Arma 3 version 2.18 with the new
// command getCorpseWeaponholders.
// https://community.bistudio.com/wiki/getCorpseWeaponholders
private _droppedWeaponHolders = (_target nearEntities ["WeaponHolderSimulated", (_range + 10)]);
{
if (getCorpse _x isEqualTo _body) then {
private _weapon = weaponsItemsCargo _x # 0;
if (_weapon # 0 isKindOf ["Launcher", configFile >> "CfgWeapons"]) then {
_lootedLoadout set [1, _weapon];
} else {
_lootedLoadout set [0, _weapon];
};
};
} forEach _droppedWeaponHolders;

_looter setUnitLoadout _lootedLoadout;
[_body] call OT_fnc_cleanupUnit;

sleep 2;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why wait 2 seconds here, the unit could already start running. In my opinion the looter should be almost constantly moving, small stops are ok but 2 seconds is too long in my opinion.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not my code so I don't know the original idea, but I think it's meant to simulate the looter spending time to pick up the items.

_looter doMove ASLtoAGL (getPosASL _target);

// Wait until looter reaches the target container
_timeout = time + 30;
waitUntil {sleep 1; (_looter distance _target < 12) || (!alive _looter) || (!alive _target) || (_timeout < time)};
if ((!alive _looter) || (!alive _target) || (_timeout < time)) then {
if (alive _looter) then {_looter globalChat format ["Can't get back to the %1, cancelling loot order", (typeOf _target) call OT_fnc_vehicleGetName]};
break;
};

// Looter has reached the target container. Dump his loadout to it.
if !([_looter, _target] call OT_fnc_canDumpUnitLoadout) then {
_looter globalChat "This vehicle is full, cancelling loot order";
break;
};

[_looter, _target] call OT_fnc_dumpUnitLoadout;
_looter setUnitLoadout [[], [], [], [_looterOwnUniform, []], [], [], "", "", [], ["", "", "", "", "", ""]];
} else {
// There are no longer any bodies to loot. Loot the nearest item pile.

private _sortedWeaponHolders = nearestObjects [_target, ["WeaponHolder", "WeaponHolderSimulated"], _range] select {!(_x getVariable ["OT_looterReserved", false])};

if (_sortedWeaponHolders isEqualTo []) then {
_looter globalChat "All done!";
break;
};

_looter globalChat format ["%1 item piles to loot", count _sortedWeaponHolders];
private _weaponHolder = _sortedWeaponHolders # 0;

_weaponHolder setVariable ["OT_looterReserved", true, false];
_looter doMove ASLtoAGL (getPosASL _weaponHolder);
[_looter, 1] call OT_fnc_experience;

// Wait until looter reaches the item pile
_timeout = time + 30;
waitUntil {sleep 1; (_looter distance2D _weaponHolder < 12) || (isNull _weaponHolder) || (!alive _looter) || (!alive _target) || (_timeout < time)};
if ((!alive _looter) || (!alive _target) || (_timeout < time)) then {
if (alive _looter) then {_looter globalChat "Can't get to an item pile, cancelling loot order"};
_weaponHolder setVariable ["OT_looterReserved", false, false];
break;
};

// Looter has reached the item pile. Its contents may not fit in the looter's
// inventory, so fake the looting trip by running back empty handed and dumping the
// contents directly to the target container once there.

sleep 2;
_looter doMove ASLtoAGL (getPosASL _target);

// Wait until looter reaches the target container
_timeout = time + 30;
waitUntil {sleep 1; (_looter distance _target < 12) || (isNull _weaponHolder) || (!alive _looter) || (!alive _target) || (_timeout < time)};
if ((!alive _looter) || (!alive _target) || (_timeout < time)) then {
if (alive _looter) then {_looter globalChat format ["Can't get back to the %1, cancelling loot order", (typeOf _target) call OT_fnc_vehicleGetName]};
_weaponHolder setVariable ["OT_looterReserved", false, false];
break;
};
if (isNull _weaponHolder) then {
_looter globalChat "Item pile has vanished, skipping";
continue;
};

// Looter has reached the target container.
if !([_weaponHolder, _target] call OT_fnc_canDumpContainer) then {
_looter globalChat "This vehicle is full, cancelling loot order";
_weaponHolder setVariable ["OT_looterReserved", false, false];
break;
};

[_weaponHolder, _target] call OT_fnc_dumpContainer;
deleteVehicle _weaponHolder;
};
}foreach(_weapons);

while {_active} do {
_deadguys = [];
{
if !(_x isKindOf "CAManBase") then {continue};
if (_x distance _t < 100) then {
_deadguys pushback _x;
};
} forEach allDeadMen;

if(count _deadguys isEqualTo 0) exitWith {_unit globalchat "All done!"};
_unit globalchat format["%1 bodies to loot",count _deadguys];
_sorted = [_deadguys,[],{_x distance _t},"ASCEND"] call BIS_fnc_SortBy;

_timeout = time + 30;
_deadguy = _sorted select 0;
_deadguy setVariable ["OT_looted",true,true];
_deadguy setvariable ["OT_lootedAt",time,true];

_unit doMove ASLtoAGL (getPosASL _deadguy);
[_unit,1] call OT_fnc_experience;

waitUntil {sleep 1; (!alive _unit) || (isNull _t) || (_unit distance2D _deadguy < 12) || (_timeOut < time)};
if((!alive _unit) || (_timeOut < time)) exitWith {_unit globalchat "Cant get to a body, cancelling loot order"};

[_deadguy,_unit] call OT_fnc_takeStuff;
sleep 2;
[_deadguy] call OT_fnc_cleanupUnit;
_timeout = time + 30;
_unit doMove ASLtoAGL (getPosASL _t);
waitUntil {sleep 1; (!alive _unit) || (isNull _t) || (_unit distance _t < 12) || (_timeOut < time)};
if((!alive _unit) || (_timeOut < time)) exitWith {};

if !([_unit,_t] call OT_fnc_dumpStuff) exitWith {
_unit globalchat "This vehicle is full, cancelling loot order";
_active = false;
};

sleep 1;
};

_unit setVariable ["NOAI",true,true];
if(_wasincar) then {
_unit assignAsCargo _car;
[_unit] orderGetIn true;
};
};
}foreach(_myunits);

sleep 1;
};
};
} forEach (_selectedUnits);
Loading