2023-09-12 18:58:10 +00:00
|
|
|
#include "..\script_component.hpp"
|
2016-01-27 00:01:01 +00:00
|
|
|
/*
|
|
|
|
* Author: PabstMirror, ViperMaul
|
2017-09-22 15:33:08 +00:00
|
|
|
* Find a safe place near a vehicle to unload something.
|
2023-11-17 23:07:28 +00:00
|
|
|
* Handles normal terrain, water or on buildings (Pier, StaticShip).
|
2016-01-27 00:01:01 +00:00
|
|
|
*
|
|
|
|
* Arguments:
|
2023-11-17 23:07:28 +00:00
|
|
|
* 0: Holder object (vehicle) <OBJECT>
|
|
|
|
* 1: Item to be unloaded <STRING> or <OBJECT>
|
|
|
|
* 2: Unit doing the unloading <OBJECT> (default: objNull)
|
|
|
|
* 3: Max distance (meters) <NUMBER> (default: 10)
|
|
|
|
* 4: Check if holder object is stable <BOOL> (default: true)
|
2016-01-27 00:01:01 +00:00
|
|
|
*
|
|
|
|
* Return Value:
|
2023-11-17 23:07:28 +00:00
|
|
|
* Unload PositionAGL ([] if no valid pos found) <ARRAY>
|
2016-01-27 00:01:01 +00:00
|
|
|
*
|
|
|
|
* Example:
|
2023-11-17 23:07:28 +00:00
|
|
|
* [cursorObject, "CAManBase", player, 10, true] call ace_common_fnc_findUnloadPosition
|
2016-01-27 00:01:01 +00:00
|
|
|
*
|
|
|
|
* Public: No
|
|
|
|
*/
|
|
|
|
|
2023-11-17 23:07:28 +00:00
|
|
|
// Number of tests run (effects performance in worst case scenario where nothing is found VERSUS reliably finding a pos)
|
2016-01-27 20:56:25 +00:00
|
|
|
#define MAX_TESTS 75
|
2016-01-27 00:01:01 +00:00
|
|
|
|
2023-11-17 23:07:28 +00:00
|
|
|
// Manual collision tests (count and radius)
|
2016-01-27 00:01:01 +00:00
|
|
|
#define COL_TEST_COUNT 12
|
|
|
|
|
2023-11-17 23:07:28 +00:00
|
|
|
params ["_vehicle", "_item", ["_unloader", objNull], ["_maxDistance", 10], ["_checkVehicleIsStable", true]];
|
|
|
|
TRACE_5("params",_vehicle,_item,_unloader,_maxDistance,_checkVehicleIsStable);
|
2016-01-27 00:01:01 +00:00
|
|
|
|
|
|
|
scopeName "main";
|
|
|
|
|
|
|
|
if (_checkVehicleIsStable) then {
|
|
|
|
if (((vectorMagnitude (velocity _vehicle)) > 1.5) || {(!(_vehicle isKindOf "Ship")) && {(!isTouchingGround _vehicle) && {((getPos _vehicle) select 2) > 1.5}}}) then {
|
|
|
|
TRACE_4("bad vehicle state",_vehicle,velocity _vehicle,isTouchingGround _vehicle,getPos _vehicle);
|
2023-11-17 23:07:28 +00:00
|
|
|
|
|
|
|
[] breakOut "main"
|
2016-01-27 00:01:01 +00:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2016-01-27 20:56:25 +00:00
|
|
|
private _radiusOfItem = 1;
|
2023-11-17 23:07:28 +00:00
|
|
|
|
|
|
|
if (_item isKindOf "CAManBase") then {
|
2016-01-27 20:56:25 +00:00
|
|
|
_radiusOfItem = 1.1;
|
|
|
|
} else {
|
2023-11-17 23:07:28 +00:00
|
|
|
// `sizeOf` is unreliable, and does not work with object types that don't exist on map, so estimate size based on cargo size
|
|
|
|
if (["ace_cargo"] call FUNC(isModLoaded)) then {
|
|
|
|
private _itemSize = _item call EFUNC(cargo,getSizeItem);
|
|
|
|
|
|
|
|
if (_itemSize > 0) then {
|
|
|
|
_radiusOfItem = (_itemSize ^ 0.35) max 0.75;
|
2018-06-01 03:48:19 +00:00
|
|
|
};
|
|
|
|
};
|
2016-01-27 00:01:01 +00:00
|
|
|
};
|
|
|
|
|
2023-11-22 22:38:52 +00:00
|
|
|
if (isNull _unloader || {_unloader in _vehicle}) then {
|
2023-11-17 23:07:28 +00:00
|
|
|
_unloader = _vehicle;
|
|
|
|
};
|
2016-01-27 00:01:01 +00:00
|
|
|
|
2023-11-17 23:07:28 +00:00
|
|
|
// Ideal unload pos is halfway between unloader and vehicle (at the unloader's height)
|
|
|
|
private _originASL = ((getPosASL _unloader) vectorAdd (getPosASL _vehicle)) vectorMultiply 0.5;
|
|
|
|
_originASL set [2, (getPosASL _unloader) select 2];
|
2016-01-27 00:01:01 +00:00
|
|
|
private _originAGL = ASLtoAGL _originASL;
|
|
|
|
|
2023-11-17 23:07:28 +00:00
|
|
|
// Do a manual search for empty pos (handles underwater, buildings or piers)
|
2016-01-27 00:01:01 +00:00
|
|
|
TRACE_2("Checking for unload",_originAGL,_radiusOfItem);
|
2016-01-27 20:56:25 +00:00
|
|
|
private _rangeToCheck = 0;
|
2023-11-17 23:07:28 +00:00
|
|
|
|
2016-01-27 20:56:25 +00:00
|
|
|
while {_rangeToCheck < _maxDistance} do {
|
|
|
|
private _roundDistance = random _rangeToCheck;
|
|
|
|
private _roundAngle = random 360;
|
|
|
|
private _roundAGL = _originAGL vectorAdd [(cos _roundAngle) * _roundDistance, (sin _roundAngle) * _roundDistance, 0];
|
2016-01-27 00:01:01 +00:00
|
|
|
|
2016-01-27 20:56:25 +00:00
|
|
|
private _roundPointIsValid = false;
|
2023-11-17 23:07:28 +00:00
|
|
|
|
2016-01-27 20:56:25 +00:00
|
|
|
if (((AGLtoASL _roundAGL) select 2) > 0) then {
|
2023-11-17 23:07:28 +00:00
|
|
|
// Shoot a ray down, and make sure we hit something solid like a building or the ground
|
|
|
|
private _belowRoundArray = lineIntersectsSurfaces [(AGLtoASL _roundAGL) vectorAdd [0, 0, 0.5], (AGLtoASL _roundAGL) vectorAdd [0, 0, -1]];
|
2016-01-27 20:56:25 +00:00
|
|
|
TRACE_4("Testing for solid",_roundDistance,_roundAngle,_roundAGL,_belowRoundArray);
|
2023-11-17 23:07:28 +00:00
|
|
|
|
2021-02-27 17:05:05 +00:00
|
|
|
if (_belowRoundArray isNotEqualTo []) then {
|
2016-01-27 20:56:25 +00:00
|
|
|
private _aboveBuilding = (_belowRoundArray select 0) select 2;
|
2023-11-17 23:07:28 +00:00
|
|
|
|
|
|
|
// Point is above something: Terrain (null) or Building
|
2016-01-27 20:56:25 +00:00
|
|
|
if ((isNull _aboveBuilding) || {_aboveBuilding isKindOf "Building"}) then {
|
2023-11-17 23:07:28 +00:00
|
|
|
// Get the real intersection point
|
2016-01-27 20:56:25 +00:00
|
|
|
_roundAGL = ASLtoAGL ((_belowRoundArray select 0) select 0);
|
2023-11-17 23:07:28 +00:00
|
|
|
|
2016-01-27 20:56:25 +00:00
|
|
|
_roundPointIsValid = true;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
} else {
|
2023-11-17 23:07:28 +00:00
|
|
|
// Underwater, just unload anywhere
|
2016-01-27 20:56:25 +00:00
|
|
|
TRACE_3("Under the sea",_roundDistance,_roundAngle,_roundAGL);
|
2023-11-17 23:07:28 +00:00
|
|
|
|
2016-01-27 20:56:25 +00:00
|
|
|
_roundPointIsValid = true;
|
|
|
|
};
|
2016-01-27 00:01:01 +00:00
|
|
|
|
2023-11-17 23:07:28 +00:00
|
|
|
// Make sure point is valid and do a fast check for people in the way (which sometimes aren't caught by line scanning)
|
2016-01-27 20:56:25 +00:00
|
|
|
if (_roundPointIsValid && {(_roundAGL nearEntities ["Man", _radiusOfItem]) isEqualTo []}) then {
|
|
|
|
for "_index" from 0 to (COL_TEST_COUNT -1) do {
|
2023-11-17 23:07:28 +00:00
|
|
|
// Scan for collisions with objects with lineIntersectsSurfaces
|
2016-01-27 20:56:25 +00:00
|
|
|
private _angle = _index * (360 / COL_TEST_COUNT);
|
|
|
|
private _point1ASL = (AGLtoASL _roundAGL) vectorAdd [_radiusOfItem * cos _angle, _radiusOfItem * sin _angle, 0.1];
|
2023-11-17 23:07:28 +00:00
|
|
|
private _point2ASL = (AGLtoASL _roundAGL) vectorAdd [-_radiusOfItem * cos _angle, -_radiusOfItem * sin _angle, _radiusOfItem + 0.5];
|
2016-01-27 20:56:25 +00:00
|
|
|
private _testIntersections = lineIntersectsSurfaces [_point1ASL, _point2ASL];
|
2023-11-17 23:07:28 +00:00
|
|
|
|
2016-04-13 18:05:32 +00:00
|
|
|
if (((count _testIntersections) == 1) && {isNull ((_testIntersections select 0) select 2)}) then {
|
|
|
|
private _hitGroundASL = (_testIntersections select 0) select 0;
|
|
|
|
private _hitHeightOffset = ((AGLtoASL _roundAGL) select 2) - (_hitGroundASL select 2);
|
|
|
|
private _hit2dOffset = _roundAGL distance2D _hitGroundASL;
|
|
|
|
private _slope = _hitHeightOffset atan2 _hit2dOffset;
|
2023-11-17 23:07:28 +00:00
|
|
|
|
|
|
|
// Ignore ground hit if slope is reasonable
|
|
|
|
if (_slope < 25) then {
|
2016-04-13 18:05:32 +00:00
|
|
|
_testIntersections = [];
|
|
|
|
};
|
|
|
|
};
|
2023-11-17 23:07:28 +00:00
|
|
|
|
2021-02-27 17:05:05 +00:00
|
|
|
if (_testIntersections isNotEqualTo []) exitWith {
|
2016-01-27 20:56:25 +00:00
|
|
|
TRACE_2("collision low/high",_roundAGL,_testIntersections);
|
2023-11-17 23:07:28 +00:00
|
|
|
|
2016-01-27 20:56:25 +00:00
|
|
|
_roundPointIsValid = false;
|
|
|
|
};
|
2023-11-17 23:07:28 +00:00
|
|
|
|
2016-01-27 20:56:25 +00:00
|
|
|
_point1ASL = (AGLtoASL _roundAGL) vectorAdd [_radiusOfItem * cos _angle, _radiusOfItem * sin _angle, 0.5];
|
|
|
|
_point2ASL = (AGLtoASL _roundAGL) vectorAdd [-_radiusOfItem * cos _angle, -_radiusOfItem * sin _angle, 1];
|
|
|
|
_testIntersections = lineIntersectsSurfaces [_point1ASL, _point2ASL];
|
2023-11-17 23:07:28 +00:00
|
|
|
|
2021-02-27 17:05:05 +00:00
|
|
|
if (_testIntersections isNotEqualTo []) exitWith {
|
2016-01-27 20:56:25 +00:00
|
|
|
TRACE_2("collision mid",_roundAGL,_testIntersections);
|
2023-11-17 23:07:28 +00:00
|
|
|
|
2016-01-27 20:56:25 +00:00
|
|
|
_roundPointIsValid = false;
|
2016-01-27 00:01:01 +00:00
|
|
|
};
|
|
|
|
};
|
2023-11-17 23:07:28 +00:00
|
|
|
|
2016-01-27 20:56:25 +00:00
|
|
|
if (_roundPointIsValid) then {
|
2023-11-17 23:07:28 +00:00
|
|
|
TRACE_3("Valid point found",_rangeToCheck,_roundAGL,_originAGL distance _roundAGL);
|
|
|
|
|
|
|
|
// Raise it slightly so we don't sink through the floor
|
|
|
|
(_roundAGL vectorAdd [0, 0, 0.05]) breakOut "main"; // return
|
2016-01-27 20:56:25 +00:00
|
|
|
};
|
2016-01-27 00:01:01 +00:00
|
|
|
};
|
2023-11-17 23:07:28 +00:00
|
|
|
|
2016-01-27 20:56:25 +00:00
|
|
|
_rangeToCheck = _rangeToCheck + (_maxDistance / MAX_TESTS);
|
2016-01-27 00:01:01 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
TRACE_1("no valid spots found",_rangeToCheck);
|
2023-11-17 23:07:28 +00:00
|
|
|
|
|
|
|
[] // return
|