From e116caa815c8ed8e7ad6ea49cb37a6a5c9c98ac4 Mon Sep 17 00:00:00 2001 From: A Man Date: Sat, 2 Apr 2022 12:22:56 +0200 Subject: [PATCH] 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. --- SQF/dayz_code/compile/fn_isInsideBuilding.sqf | 218 +++++++++++++----- SQF/dayz_code/init/compiles.sqf | 2 +- SQF/dayz_code/init/variables.sqf | 2 + .../{actions => old}/tow_AttachStraps.sqf | 0 .../{actions => old}/tow_DetachStraps.sqf | 0 5 files changed, 161 insertions(+), 61 deletions(-) rename SQF/dayz_code/{actions => old}/tow_AttachStraps.sqf (100%) rename SQF/dayz_code/{actions => old}/tow_DetachStraps.sqf (100%) diff --git a/SQF/dayz_code/compile/fn_isInsideBuilding.sqf b/SQF/dayz_code/compile/fn_isInsideBuilding.sqf index 863e94fed..0bb1c1a23 100644 --- a/SQF/dayz_code/compile/fn_isInsideBuilding.sqf +++ b/SQF/dayz_code/compile/fn_isInsideBuilding.sqf @@ -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 diff --git a/SQF/dayz_code/init/compiles.sqf b/SQF/dayz_code/init/compiles.sqf index b930ba15e..c4aed8c7d 100644 --- a/SQF/dayz_code/init/compiles.sqf +++ b/SQF/dayz_code/init/compiles.sqf @@ -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 diff --git a/SQF/dayz_code/init/variables.sqf b/SQF/dayz_code/init/variables.sqf index 3a463767c..2aaab0443 100644 --- a/SQF/dayz_code/init/variables.sqf +++ b/SQF/dayz_code/init/variables.sqf @@ -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) }; diff --git a/SQF/dayz_code/actions/tow_AttachStraps.sqf b/SQF/dayz_code/old/tow_AttachStraps.sqf similarity index 100% rename from SQF/dayz_code/actions/tow_AttachStraps.sqf rename to SQF/dayz_code/old/tow_AttachStraps.sqf diff --git a/SQF/dayz_code/actions/tow_DetachStraps.sqf b/SQF/dayz_code/old/tow_DetachStraps.sqf similarity index 100% rename from SQF/dayz_code/actions/tow_DetachStraps.sqf rename to SQF/dayz_code/old/tow_DetachStraps.sqf