Skip to content

Commit

Permalink
Dragging - Corpse carrying and dragging - continued (#9273)
Browse files Browse the repository at this point in the history
Co-authored-by: Dystopian <sddex@ya.ru>
Co-authored-by: Grim <69561145+LinkIsGrim@users.noreply.github.com>
Co-authored-by: Joko <hoffman.jonas95@gmail.com>
Co-authored-by: PabstMirror <pabstmirror@gmail.com>
Co-authored-by: BaerMitUmlaut <baermitumlaut@users.noreply.github.com>
Co-authored-by: LinkIsGrim <salluci.lovi@gmail.com>
  • Loading branch information
7 people authored Sep 7, 2024
1 parent e560036 commit e2335c9
Show file tree
Hide file tree
Showing 21 changed files with 348 additions and 21 deletions.
3 changes: 3 additions & 0 deletions addons/dragging/CfgVehicles.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
class CBA_Extended_EventHandlers;

class CfgVehicles {
class C_man_1;
class GVAR(clone): C_man_1 {};

// Static weapons
class LandVehicle;
class StaticWeapon: LandVehicle {
Expand Down
2 changes: 2 additions & 0 deletions addons/dragging/XEH_PREP.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ PREP(canDrop_carry);
PREP(canRun_carry);
PREP(carryObject);
PREP(carryObjectPFH);
PREP(createClone);
PREP(deleteClone);
PREP(dragObject);
PREP(dragObjectPFH);
PREP(dropObject);
Expand Down
66 changes: 66 additions & 0 deletions addons/dragging/XEH_postInit.sqf
Original file line number Diff line number Diff line change
@@ -1,9 +1,62 @@
// by PabstMirror, commy2
#include "script_component.hpp"

[QGVAR(moveCorpse), {
params ["_corpse", "_dir", "_posATL"];

if (isNull _corpse) exitWith {};

// Check if the corpse is already close to the target
// If so, don't teleport
if ((getPosATL _corpse) distance _posATL > 0.25) then {
// Set direction before position
_corpse setDir _dir;

// Bring corpse back to clone's position
_corpse setPosATL _posATL;
};

// Sync the corpse with its position
[{
_this awake true;

[{
_this awake false;
}, _this] call CBA_fnc_execNextFrame;
}, _corpse] call CBA_fnc_execNextFrame;

// Allow the corpse to be synced for JIP players
if (isServer) exitWith {
GVAR(movedCorpses) pushBackUnique _corpse;
};
}] call CBA_fnc_addEventHandler;

if (isServer) then {
// Release object on disconnection. Function is identical to killed
addMissionEventHandler ["HandleDisconnect", LINKFUNC(handleKilled)];

GVAR(movedCorpses) = [];

["CAManBase", "Deleted", {
GVAR(movedCorpses) deleteAt (GVAR(movedCorpses) find (_this select 0));
}, true, [], true] call CBA_fnc_addClassEventHandler;

[QGVAR(disableSyncMovedCorpseOnJIP), {
params ["_corpse"];

GVAR(movedCorpses) deleteAt (GVAR(movedCorpses) find _corpse);
}] call CBA_fnc_addEventHandler;

// Sync position of dead corpse for JIP unit (prevents weird invisible hitboxes on corpses)
[QGVAR(requestSyncMovedCorpsesJIP), {
params ["_clientOwner"];

{
[QGVAR(moveCorpse), [_x, getDir _x, getPosATL _x], _clientOwner] call CBA_fnc_ownerEvent;
} forEach GVAR(movedCorpses);
}] call CBA_fnc_addEventHandler;
} else {
[QGVAR(requestSyncMovedCorpsesJIP), clientOwner] call CBA_fnc_serverEvent;
};

if (!hasInterface) exitWith {};
Expand All @@ -20,6 +73,11 @@ if (isNil QGVAR(maxWeightCarryRun)) then {
GVAR(maxWeightCarryRun) = 50;
};

// Extended EH doesn't fire for dead units, so add interactions manually
{
_x call FUNC(initPerson);
} forEach allDeadMen;

["isNotDragging", {!((_this select 0) getVariable [QGVAR(isDragging), false])}] call EFUNC(common,addCanInteractWithCondition);
["isNotCarrying", {!((_this select 0) getVariable [QGVAR(isCarrying), false])}] call EFUNC(common,addCanInteractWithCondition);

Expand Down Expand Up @@ -57,6 +115,14 @@ if (isNil QGVAR(maxWeightCarryRun)) then {
// Display event handler
["MouseZChanged", {(_this select 1) call FUNC(handleScrollWheel)}] call CBA_fnc_addDisplayHandler;

// Handle local effect commands for clones
[QGVAR(setCloneFace), {
params ["_clone", "_corpse"];

_clone setFace face _corpse;
_clone setMimic "unconscious";
}] call CBA_fnc_addEventHandler;

// Handle surrendering and handcuffing
["ace_captiveStatusChanged", {
params ["_unit", "_state"];
Expand Down
20 changes: 12 additions & 8 deletions addons/dragging/functions/fnc_canCarry.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,30 @@

params ["_unit", "_target"];

if !(alive _target && {_target getVariable [QGVAR(canCarry), false]} && {isNull objectParent _target}) exitWith {false};
private _alive = alive _target;
private _isPerson = _target isKindOf "CAManBase";

if !((_alive || _isPerson) && {_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};

// Units need to be unconscious or limping; Units also need to not be in ragdoll if alive, as that causes desync issues
if (_isPerson) exitWith {
((!_alive) && {missionNamespace getVariable [QGVAR(canMoveDead), true]}) ||
{(isAwake _target) && // not ragdolled if alive
{!(_target call EFUNC(common,isAwake)) ||
{_target getHitPointDamage "HitLegs" >= 0.5}}}
};

// Static weapons need to be empty for carrying (ignore UAV AI)
if (_target isKindOf "StaticWeapon") exitWith {
(crew _target) findIf {!unitIsUAV _x} == -1
};

// Units need to be unconscious or limping; Units also need to not be in ragdoll, as that causes desync issues
if (_target isKindOf "CAManBase") exitWith {
isAwake _target && // not ragdolled
{lifeState _target == "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
Expand Down
20 changes: 12 additions & 8 deletions addons/dragging/functions/fnc_canDrag.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,26 @@

params ["_unit", "_target"];

if !(alive _target && {_target getVariable [QGVAR(canDrag), false]} && {isNull objectParent _target}) exitWith {false};
private _alive = alive _target;
private _isPerson = _target isKindOf "CAManBase";

if !((_alive || _isPerson) && {_target getVariable [QGVAR(canDrag), false]} && {isNull objectParent _target}) exitWith {false};

if !([_unit, _target, ["isNotSwimming"]] call EFUNC(common,canInteractWith)) exitWith {false};

// Units need to be unconscious or limping; Units also need to not be in ragdoll if alive, as that causes desync issues
if (_isPerson) exitWith {
((!_alive) && {missionNamespace getVariable [QGVAR(canMoveDead), true]}) ||
{(isAwake _target) && // not ragdolled if alive
{!(_target call EFUNC(common,isAwake)) ||
{_target getHitPointDamage "HitLegs" >= 0.5}}}
};

// Static weapons need to be empty for dragging (ignore UAV AI)
if (_target isKindOf "StaticWeapon") exitWith {
(crew _target) findIf {!unitIsUAV _x} == -1
};

// Units need to be unconscious or limping; Units also need to not be in ragdoll, as that causes desync issues
if (_target isKindOf "CAManBase") exitWith {
isAwake _target && // not ragdolled
{lifeState _target == "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
Expand Down
101 changes: 101 additions & 0 deletions addons/dragging/functions/fnc_createClone.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#include "..\script_component.hpp"
/*
* Author: BaerMitUmlaut, johnb43
* Creates a draggable / carryable clone of a dead unit.
*
* Arguments:
* 0: Unit dragging/carrying <OBJECT>
* 1: Dead unit <OBJECT>
*
* Return Value:
* Cloned unit <OBJECT>
*
* Example:
* [player, cursorObject] call ace_dragging_fnc_createClone;
*
* Public: No
*/

params ["_unit", "_target"];

// Don't sync corpse when a player joins in progress until the corpse is in its proper position
[QGVAR(disableSyncMovedCorpseOnJIP), _target] call CBA_fnc_serverEvent;

private _posATL = getPosATL _target;

// Create clone
private _clone = createVehicle [[configOf _target >> QGVAR(cloneClass), "TEXT", QGVAR(clone)] call CBA_fnc_getConfigEntry, _posATL, [], 0, "CAN_COLLIDE"];

// Claim the clone
[_unit, _clone] call EFUNC(common,claim);

// Move unit -10 m below terrain in order to hide it and remove its inventory access
_posATL set [2, -10];

// Corpse is desynced, but it doesn't matter here
_target setPosATL _posATL;

// Hide unit until it can be moved below terrain
private _isObjectHidden = isObjectHidden _target;

if (!_isObjectHidden) then {
[QEGVAR(common,hideObjectGlobal), [_target, true]] call CBA_fnc_serverEvent;
};

// Prevents unit from falling when below terrain
private _simulationEnabled = simulationEnabled _target;

if (_simulationEnabled) then {
[QEGVAR(common,enableSimulationGlobal), [_target, false]] call CBA_fnc_serverEvent;
};

private _isInRemainsCollector = isInRemainsCollector _target;

// Make sure corpse isn't deleted by engine's garbage collector
if (_isInRemainsCollector) then {
removeFromRemainsCollector [_target];
};

// Make sure clone has the same wound textures as the corpse
_clone setDamage ((damage _target) min 0.99); // Don't kill the clone

{
_clone setHitPointDamage [_x, (_target getHitPointDamage _x) min 0.99];
} forEach ["HitHead", "HitBody", "HitHands", "HitLegs"]; // Relevant hitpoints

// Disable all damage
_clone allowDamage false;
_clone setVariable [QGVAR(original), [_target, _isInRemainsCollector, _isObjectHidden, _simulationEnabled], true];

[_clone, _target call CBA_fnc_getLoadout] call CBA_fnc_setLoadout;

// Sets the facial expression
[[QGVAR(setCloneFace), [_clone, _target]] call CBA_fnc_globalEventJIP, _clone] call CBA_fnc_removeGlobalEventJIP;

// API
[QGVAR(cloneCreated), [_clone, _target]] call CBA_fnc_localEvent;

[{
params ["_clone", "_target"];

// Remove clone from all zeuses
if (["ace_zeus"] call EFUNC(common,isModLoaded)) then {
[QEGVAR(zeus,removeObjects), [[_clone]]] call CBA_fnc_serverEvent;
};

// Release claim on corpse
[objNull, _target] call EFUNC(common,claim);
}, [_clone, _target], 0.25] call CBA_fnc_waitAndExecute;

// Save which curators had this object as editable
if (["ace_zeus"] call EFUNC(common,isModLoaded)) then {
private _objectCurators = objectCurators _target;

_target setVariable [QGVAR(objectCurators), _objectCurators, true];

if (_objectCurators isEqualTo []) exitWith {};

[QEGVAR(zeus,removeObjects), [[_target], _objectCurators]] call CBA_fnc_serverEvent;
};

_clone
80 changes: 80 additions & 0 deletions addons/dragging/functions/fnc_deleteClone.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#include "..\script_component.hpp"
/*
* Author: BaerMitUmlaut, johnb43
* Drops a draggable / carryable clone of a dead unit.
*
* Arguments:
* 0: Unit dragging / carrying <OBJECT>
* 1: Clone <OBJECT>
* 2: If unit is in building <BOOL>
*
* Return Value:
* Original unit <OBJECT>
*
* Example:
* [player, cursorObject, false] call ace_dragging_fnc_deleteClone;
*
* Public: No
*/

params ["_unit", "_clone", "_inBuilding"];

(_clone getVariable [QGVAR(original), []]) params [
["_target", objNull],
["_isInRemainsCollector", true],
["_isObjectHidden", false],
["_simulationEnabled", true]
];

// API
[QGVAR(cloneDeleted), [_clone, _target]] call CBA_fnc_localEvent;

// Check if unit was deleted
if (!isNull _target) then {
private _posATL = getPosATL _clone;

if (_inBuilding) then {
_posATL = _posATL vectorAdd [0, 0, 0.05];
};

// Make sure position isn't underground
if (_posATL select 2 < 0.05) then {
_posATL set [2, 0.05];
};

// Move the unit globally (important, as it desyncs the corpse position otherwise)
[QGVAR(moveCorpse), [_target, getDir _unit + 180, _posATL]] call CBA_fnc_globalEvent;

// Unhide unit
if (!_isObjectHidden) then {
[QEGVAR(common,hideObjectGlobal), [_target, false]] call CBA_fnc_serverEvent;
};

// Enable simulation again
if (_simulationEnabled) then {
[QEGVAR(common,enableSimulationGlobal), [_target, true]] call CBA_fnc_serverEvent;
};

// Detach first to prevent objNull in attachedObjects
detach _clone;
deleteVehicle _clone;

// Get which curators had this object as editable
if (["ace_zeus"] call EFUNC(common,isModLoaded)) then {
private _objectCurators = _target getVariable [QGVAR(objectCurators), []];

if (_objectCurators isEqualTo []) exitWith {};

[QEGVAR(zeus,addObjects), [[_target], _objectCurators]] call CBA_fnc_serverEvent;
};

if (_isInRemainsCollector) then {
addToRemainsCollector [_target];
};
} else {
// Detach first to prevent objNull in attachedObjects
detach _clone;
deleteVehicle _clone;
};

_target
5 changes: 5 additions & 0 deletions addons/dragging/functions/fnc_dragObjectPFH.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,8 @@ if (_target isKindOf "StaticWeapon" && {((crew _target) - (_target getVariable [

_idPFH call CBA_fnc_removePerFrameHandler;
};

// Clones can die of drowning if oxygen is under 0.5, so refill their oxygen from time to time
if (_target isKindOf QGVAR(clone) && {getOxygenRemaining _target < 0.8}) then {
_target setOxygenRemaining 1;
};
8 changes: 7 additions & 1 deletion addons/dragging/functions/fnc_dropObject.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ if (!GVAR(dragAndFire)) then {
};

private _inBuilding = _unit call FUNC(isObjectOnObject);
private _isClone = _target isKindOf QGVAR(clone);

// Drop cloned dead units
if (_isClone) then {
_target = [_unit, _target, _inBuilding] call FUNC(deleteClone);
};

// Play release animation
if (_unit call EFUNC(common,isAwake)) then {
Expand All @@ -57,7 +63,7 @@ _unit removeWeapon "ACE_FakePrimaryWeapon";
[_unit, "blockThrow", QUOTE(ADDON), false] call EFUNC(common,statusEffect_set);

// Prevent object from flipping inside buildings
if (_inBuilding) then {
if (_inBuilding && {!_isClone}) then {
_target setPosASL (getPosASL _target vectorAdd [0, 0, 0.05]);
TRACE_2("setPos",getPosASL _unit,getPosASL _target);
};
Expand Down
Loading

0 comments on commit e2335c9

Please sign in to comment.