mirror of
https://github.com/EpochModTeam/DayZ-Epoch.git
synced 2025-12-14 12:12:34 +03:00
Update fnc_isInsideBuilding
Made by @Victor-the-Cleaner - If the player is inside a building but near a large open doorway or full height windows, or out on a balcony, they may be considered outside. - The UI visual stealth icon will update accordingly, so the player will know if they need to step back from open doors or windows to regain stealth. - dayz_inside global variable will now only affect player temperature, stealth vs zombies, and blizzard effects. - The new dayz_insideBuilding global variable stores the building name the player is currently inside of, or null if player is outside. This may be used for modding purposes.
This commit is contained in:
@@ -1,77 +1,175 @@
|
||||
/*
|
||||
Created exclusively for ArmA2:OA - DayZMod.
|
||||
Please request permission to use/alter/distribute from project leader (R4Z0R49) AND the author (facoptere@gmail.com)
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// fn_isInsideBuilding.sqf
|
||||
//
|
||||
// Author: Victor the Cleaner
|
||||
// Date: April 2022
|
||||
//
|
||||
// [_unit] call fnc_isInsideBuilding;
|
||||
//
|
||||
// Called continuously from player_checkStealth.
|
||||
// Used for temperature, stealth vs zombies, and blizzard effects.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
local _unit = _this select 0; // player
|
||||
local _inside = false;
|
||||
local _scan = 3; // horizontal radius around player (in meters)
|
||||
local _zenith = 50; // scan height above and below player
|
||||
local _posASL = aimPos _unit; // center of mass (ASL)
|
||||
local _posLowASL = getPosASL _unit; // foot of player (ASL)
|
||||
_posLowASL set [2, (_posLowASL select 2) + 0.3]; // shin level, below most windows
|
||||
|
||||
// check if arg#0 is inside or on the roof of a building
|
||||
// second argument is optional:
|
||||
// - arg#1 is an object: check whether arg#0 is inside (bounding box of) arg#1
|
||||
// - missing arg#1: check whether arg#0 is inside (bounding box of) the nearest enterable building
|
||||
// - arg#1 is a boolean: check also whether arg#0 is inside (bounding box of) some non-enterable buildings around. Can be used to check if a player or an installed item is on a building roof.
|
||||
// - arg#0 is posATL, arg#1 should be a building
|
||||
local _posX = _posASL select 0;
|
||||
local _posY = _posASL select 1;
|
||||
local _posZ = _posASL select 2;
|
||||
local _posLowZ = _posLowASL select 2;
|
||||
|
||||
private ["_check","_unit","_inside","_building","_type","_option"];
|
||||
local _insideBox = objNull; // object the player is inside of
|
||||
local _type = ""; // class name
|
||||
local _roofAbove = false; // is there geometry above
|
||||
local _intersect = false; // for raycast
|
||||
local _hit = []; // array to record hits and misses
|
||||
local _idx = 0; // initialize
|
||||
local _truth = []; // used with the _hit array
|
||||
|
||||
_check = {
|
||||
private ["_building", "_pos", "_inside", "_offset", "_relPos", "_boundingBox", "_min", "_max", "_myX", "_myY", "_myZ"];
|
||||
local _cowsheds = ["Land_Farm_Cowshed_a","Land_Farm_Cowshed_b","Land_Farm_Cowshed_c"];
|
||||
local _isCowshed = false; // special case objects
|
||||
|
||||
_building = _this select 0;
|
||||
_inside = false;
|
||||
if (isNull _building) exitWith {_inside};
|
||||
_pos = _this select 1;
|
||||
_offset = 1; // shrink building boundingbox by this length.
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Functions
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
local _checkBox = {
|
||||
local _object = _this select 0; // object
|
||||
_type = typeOf _object; // class name
|
||||
|
||||
_relPos = _building worldToModel _pos;
|
||||
_boundingBox = boundingBox _building;
|
||||
if (_type isKindOf "House" || {_type isKindOf "Church" || {_type in DZE_insideExceptions}}) then {
|
||||
|
||||
_min = _boundingBox select 0;
|
||||
_max = _boundingBox select 1;
|
||||
_myX = _relPos select 0;
|
||||
_myY = _relPos select 1;
|
||||
_myZ = _relPos select 2;
|
||||
local _pos = _object worldToModel (ASLToATL _posASL);
|
||||
local _max = (boundingBox _object) select 1;
|
||||
_insideBox = _object;
|
||||
|
||||
if ((_myX > (_min select 0)+_offset) and {(_myX < (_max select 0)-_offset)}) then {
|
||||
if ((_myY > (_min select 1)+_offset) and {(_myY < (_max select 1)-_offset)}) then {
|
||||
if ((_myZ > (_min select 2)) and {(_myZ < (_max select 2))}) then {
|
||||
_inside = true;
|
||||
};
|
||||
for "_i" from 0 to 2 do {
|
||||
if (abs (_pos select _i) > (_max select _i)) exitWith {_insideBox = objNull;};
|
||||
};
|
||||
};
|
||||
//diag_log(format["fnc_isInsideBuilding: building:%1 typeOf:%2 bbox:%3 relpos:%4 result:%5", _building, typeOf(_building), _boundingBox, _relPos, _inside ]);
|
||||
};
|
||||
local _scanUp = {
|
||||
local _pos = [_posX, _posY, _posZ + _zenith];
|
||||
local _arr = lineIntersectsWith [_posASL, _pos, _unit, objNull, true]; // sorted (nearest last)
|
||||
|
||||
_inside
|
||||
for "_i" from (count _arr - 1) to 0 step -1 do { // count backwards
|
||||
[_arr select _i] call _checkBox; // validate object
|
||||
if (!isNull _insideBox) exitWith {_roofAbove = true;}; // player is within bounds of a candidate object
|
||||
};
|
||||
};
|
||||
local _scanDown = {
|
||||
local _pos = [_posX, _posY, _posZ - _zenith];
|
||||
local _arr = lineIntersectsWith [_posASL, _pos, _unit, objNull, true]; // sorted (nearest last)
|
||||
|
||||
for "_i" from (count _arr - 1) to 0 step -1 do { // count backwards
|
||||
[_arr select _i] call _checkBox; // validate object
|
||||
if (!isNull _insideBox) exitWith {}; // player is within bounds of a candidate object
|
||||
};
|
||||
};
|
||||
local _scanNear = {
|
||||
local _north = [_posX, _posY + _scan, _posZ];
|
||||
local _east = [_posX + _scan, _posY, _posZ];
|
||||
local _south = [_posX, _posY - _scan, _posZ];
|
||||
local _west = [_posX - _scan, _posY, _posZ];
|
||||
local _cp = [_north, _east, _south, _west, _north]; // compass points
|
||||
|
||||
scopeName "near";
|
||||
for "_i" from 0 to 3 do {
|
||||
local _arr = lineIntersectsWith [_cp select _i, _cp select (_i + 1)]; // unsorted
|
||||
{
|
||||
[_x] call _checkBox; // validate object
|
||||
if (!isNull _insideBox) then {breakTo "near";}; // player is within bounds of a candidate object
|
||||
} count _arr;
|
||||
};
|
||||
};
|
||||
|
||||
_unit = _this select 0;
|
||||
_inside = false;
|
||||
|
||||
// [object] call fnc_isInsideBuilding;
|
||||
// This option is called continuously from player_checkStealth.
|
||||
if (count _this == 1) exitWith {
|
||||
//_building = nearestObject [_unit, "Building"];
|
||||
_building = nearestBuilding _unit; // Not sure if this command is faster.
|
||||
_inside = [_building,(getPosATL _unit)] call _check;
|
||||
_inside
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Cowsheds are arranged from 3 separate classes. Therefore, we need to allow adjacent
|
||||
// cowshed class names as substitutes for the object we're scanning.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
local _cowshedCheck = {
|
||||
local _array = _this select 0;
|
||||
if (_isCowshed) then {
|
||||
{
|
||||
if (typeOf _x in _cowsheds) exitWith { // is object of similar type?
|
||||
_intersect = true; // override radial scan
|
||||
_truth set [0,49]; // force hit detection
|
||||
};
|
||||
} forEach _array;
|
||||
};
|
||||
};
|
||||
local _checkWalls = {
|
||||
// known problem buildings
|
||||
if (_type == (_cowsheds select 2) && _idx in [3]) exitWith {_hit set [_idx, 49];}; // simulate wall at East sector
|
||||
if (_type == (_cowsheds select 1) && _idx in [3,11]) exitWith {_hit set [_idx, 49];}; // simulate walls at East and West sectors
|
||||
if (_type == (_cowsheds select 0) && _idx in [11]) exitWith {_hit set [_idx, 49];}; // simulate wall at West sector
|
||||
};
|
||||
|
||||
_option = _this select 1;
|
||||
// [object,building] call fnc_isInsideBuilding;
|
||||
if (typeName _option == "OBJECT") then {
|
||||
// optional argument is a specific building
|
||||
_inside = [_option,(getPosATL _unit)] call _check;
|
||||
} else {
|
||||
// [object,boolean] call fnc_isInsideBuilding; This is used in fn_niceSpot.
|
||||
{
|
||||
_building = _x;
|
||||
_type = typeOf _building;
|
||||
if (!(_type in DayZ_SafeObjects) // not installable objects
|
||||
&& {!(_type isKindOf "ReammoBox")} // not lootpiles (weaponholders and ammoboxes)
|
||||
&& {((sizeOf typeOf _unit) + (sizeOf _type)) > (_unit distance _building)} // objects might colliding
|
||||
&& {[_building, _unit] call _check}) exitWith { // perform the check. exitWith works only in non-nested "if"
|
||||
_inside = true;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Initial scan to determine if player is above, below, or near a building
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
call _scanUp;
|
||||
if (isNull _insideBox) then { // no detectable roof
|
||||
call _scanDown;
|
||||
if (isNull _insideBox) then { // no detectable floor
|
||||
call _scanNear;
|
||||
};
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// If player is inside a bounding box, perform radial scan and determine the outcome
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if (!isNull _insideBox) then { // bounding box detected
|
||||
local _dir = getDir _insideBox; // direction of object on map
|
||||
local _rad = sizeOf (typeOf _insideBox); // scan radius
|
||||
local _seg = 16; // radial scan density (must be evenly divisible by 4)
|
||||
local _arc = 360 / _seg; // radial scan delta
|
||||
local _miss = "00000"; // must be (_seg / 4 + 1) characters in length -- the number of consecutive misses before player is determined to be outside
|
||||
_isCowshed = _type in _cowsheds; // special case for known problem buildings
|
||||
|
||||
for "_n" from _arc to 360 step _arc do { // perform radial scan
|
||||
local _angle = (_dir + _n) % 360; // normalize from 0 to 360
|
||||
local _a = (sin _angle) * _rad; // X offset
|
||||
local _b = (cos _angle) * _rad; // Y offset
|
||||
local _v = [_posX + _a, _posY + _b, _posZ]; // radial vector
|
||||
_truth = [48,49]; // [miss,hit]
|
||||
|
||||
local _arr = lineIntersectsWith [_posASL, _v, _unit]; // raycast
|
||||
_intersect = (_insideBox in _arr); // did an intersect occur?
|
||||
[_arr] call _cowshedCheck; // check known problem buildings
|
||||
|
||||
if (!_intersect && _roofAbove) then { // if no hit at chest level, check lower. This eliminates most normal windows.
|
||||
_v = [_posX + _a, _posY + _b, _posLowZ]; // radial vector Low
|
||||
_arr = lineIntersectsWith [_posLowASL, _v, _unit]; // raycast
|
||||
[_arr] call _cowshedCheck; // re-check known problem buildings
|
||||
};
|
||||
} forEach (nearestObjects [_unit, ["Building"], 50]);
|
||||
};
|
||||
//diag_log ("fnc_isInsideBuilding Check: " + str(_inside)+ " last building:"+str(_building));
|
||||
_hit set [_idx, _truth select (_insideBox in _arr)]; // record hit or miss
|
||||
call _checkWalls; // simulate walls for known problem buildings, and override scan
|
||||
_idx = _idx + 1;
|
||||
};
|
||||
|
||||
for "_i" from 0 to 3 do {
|
||||
_hit set [_seg + _i, _hit select _i]; // loop (_seg / 4) times to allow wrap-around search
|
||||
};
|
||||
if (!_roofAbove) then {_miss = "0000";}; // if player is on a roof or in an open area, reduce the consecutive miss criteria by one arc
|
||||
|
||||
if !([_miss, toString _hit] call fnc_inString) then { // if there are no sufficient consecutive misses, then player is deemed to be inside
|
||||
_inside = true;
|
||||
};
|
||||
};
|
||||
dayz_insideBuilding = [objNull, _insideBox] select _inside;
|
||||
_inside
|
||||
|
||||
@@ -767,6 +767,7 @@ if (!isDedicated) then {
|
||||
|
||||
DZ_KeyDown_EH = compile preprocessFileLineNumbers "\z\addons\dayz_code\compile\keyboard.sqf";
|
||||
dayz_EjectPlayer = compile preprocessFileLineNumbers "\z\addons\dayz_code\compile\dze_ejectPlayer.sqf";
|
||||
fnc_isInsideBuilding = compile preprocessFileLineNumbers "\z\addons\dayz_code\compile\fn_isInsideBuilding.sqf"; //_isInside = [_unit,_building] call fnc_isInsideBuilding;
|
||||
};
|
||||
|
||||
//Both
|
||||
@@ -789,7 +790,6 @@ fnc_veh_handleKilled = compile preprocessFileLineNumbers "\z\addons\dayz_code\co
|
||||
fnc_veh_handleRepair = compile preprocessFileLineNumbers "\z\addons\dayz_code\compile\veh_handleRepair.sqf"; //process the hit as a NORMAL damage (useful for persistent vehicles)
|
||||
fnc_veh_ResetEH = compile preprocessFileLineNumbers "\z\addons\dayz_code\init\veh_ResetEH.sqf"; //Initialize vehicle
|
||||
fnc_inString = compile preprocessFileLineNumbers "\z\addons\dayz_code\compile\fn_inString.sqf";
|
||||
fnc_isInsideBuilding = compile preprocessFileLineNumbers "\z\addons\dayz_code\compile\fn_isInsideBuilding.sqf"; //_isInside = [_unit,_building] call fnc_isInsideBuilding;
|
||||
dayz_zombieSpeak = compile preprocessFileLineNumbers "\z\addons\dayz_code\compile\object_speak.sqf"; //Used to generate random speech for a unit
|
||||
vehicle_getHitpoints = compile preprocessFileLineNumbers "\z\addons\dayz_code\compile\vehicle_getHitpoints.sqf";
|
||||
local_gutObject = compile preprocessFileLineNumbers "\z\addons\dayz_code\compile\local_gutObject.sqf"; //Generated on the server (or local to unit) when gutting an object
|
||||
|
||||
@@ -491,4 +491,6 @@ if (!isDedicated) then {
|
||||
["RightFoot","LeftFoot"],
|
||||
["neck","pilot"]
|
||||
];
|
||||
dayz_insideBuilding = objNull; // building name the player is currently inside of, or objNull if player is outside
|
||||
DZE_insideExceptions = ["Garage_Green_DZ","Garage_White_DZ","Garage_Brown_DZ","Garage_Grey_DZ","Wooden_shed_DZ","Wooden_shed2_DZ","WoodShack_DZ","WoodShack2_DZ","StorageShed_DZ","StorageShed2_DZ","Concrete_Bunker_DZ","Concrete_Bunker_Locked_DZ","SandNestLarge_DZ"]; // list of base-building objects that allow checking if player is inside (fnc_isInsideBuilding)
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user