Jump to content
Sign in to follow this  
mrcurry

Discussion: Finding indoor housepositions

Recommended Posts

Indoor building positions...

It's one of those things that have been pretty elusive as far as arma scripting goes. I've been playing around with different ways of finding the positions but none of them have been satisfactory for a perfectionist like myself...

Hence I create this topic, as a sort of brainstorm for possible solutions, from the simple to the outright insane.

The essentials of the problem are:

Imagine a function such as...

fnc_findIndoorPositions = {
  //Stuff
};

The parameter of the function would be a building of any kind.

The return value must be an array of positions inside the building or an empty array.

One could pre-define the positions through some "down and dirty" relative-position collecting. The plus side of this approach would be that you could define custom positions not defined in the buildings config.

The drawback is the obvious that its massively time-consuming, especially for larger buildings which can have up to 50+ positions.

It is also very inflexible.

I've tried using commands like boundingBox to approximate the height of the building and have a rough "everything below rooftop level" selection, but buildings with several levels of roof kinda breaks it.

Post your ideas or workarounds that you have, even a straight-up solutions are welcome.

Oh, and leave the "it's impossible" attitude at the door. ;)

Cheers

/Curry

Edited by mrCurry
Typos and formatting

Share this post


Link to post
Share on other sites

There's at least one dude out there that did that massive amount of work and collected all (or almost all?) building positions for vanilla buildings. I just can't remember who that is right now, but I guess you'd find it pretty quickly if you search for the various house patrol/Garrison scripts on armaholic.

Edit: That's Jakerod's.

Edit again: Btw, you might wanna check its thread here, as Twirly's code (8th post) is what I'd use to find positions in buildings. Use Building pos in a loop that just stops when the returned position is [0,0,0].

Edited by BlackMamb

Share this post


Link to post
Share on other sites

Yes, I've seen his script and I feel that, while the work is solid and extensive, it is lacking in 1 important aspect. It has very little scale-ability.

It is using the sort of "down and dirty" approach I was referring to in the OP.

There is a lot work in making something like that (kudos to Jakerod) and adding more buildings to the script is... demanding to say the least even with his great comments, and it is exactly the kind of solution I am trying to avoid.

Instead of cutting down the tree with an axe, I want to design a chainsaw.

Hence I am looking for workarounds to the rather limited building configs and I'm trying to think outside the box (or the config if you will).

Things like checking building height against an average floor height, dropping balls on top of the positions and checking if they get close enough for it to be a rooftop, etc.

If you got any ideas, even somewhat crazy ones, do share!

P.S. The ball option is currently not very good for run-time application. :rolleyes:

Edited by mrCurry
Typos... again...

Share this post


Link to post
Share on other sites

The only way i know to get the house position is a while check:

_house = _this select 0;
_n_pos = 0;
while {format ["%1", _house buildingPos _n_pos] != "[0,0,0]" } do {_n_pos = _n_pos + 1};

Share this post


Link to post
Share on other sites

This was originally written for insurgency.

#define ILLEGALHOUSES		["Land_molo_beton","Land_vez","Land_A_Crane_02b","land_nav_pier_c2_end","Land_nav_pier_m_2","land_nav_pier_c_big","Land_A_Crane_02a","land_nav_pier_c2","Land_Nasypka","Land_Mil_hangar_EP1", "Land_Mil_ControlTower_EP1", "Land_Mil_Guardhouse_EP1", "Land_Mil_Repair_center_EP1","Land_Mil_Barracks_i_EP1","Land_A_Minaret_EP1","Land_Ind_Coltan_Main_EP1","land_most_blok"]

_findHouses = { 
private ["_buildings","_minPositions","_enterables","_alive"];
_buildings = nearestObjects [_this select 0, ["House"], _this select 1]; 
_minPositions = (_this select 2) - 1;
_alive = _this select 3;

_enterables = []; 	
{ 
	if (
		format["%1", _x buildingPos _minPositions] != "[0,0,0]" 
	&& (!(typeOf _x in ILLEGALHOUSES) && (alive _x || !_alive))
	) then { 
		_enterables set [count _enterables, _x]; 
	}; 
} forEach _buildings; 
_enterables
}; 
// called with : _buildingsArray = [position _something,400,2,true] call _findHouses;

_countPositions = { 
private ["_i","_house","_hPos"]; 
   _house = _this select 0; 
   _i 	 = _this select 1; 
_hPos	 = format["%1", _house buildingPos _i]; 
if (_hPos == "[0,0,0]") exitWith { _i; }; 
[_house, _i+1] call _countPositions; 
};
// called with _nr = [_house,0] call _countPositions;

And then I wrote this to position an ammobox inside the house correctly so it's not floating. It's not perfect (sometimes goes through the floor), playing around with the distances should help.

_box = createVehicle ["GuerillaCacheBox", (_house buildingPos (floor (random _nr))), [], 0, "CAN_COLLIDE"];
if (count (lineIntersectsWith [[getPosASL _box select 0,getPosASL _box select 1,(getPosASL _box select 2) + 0.1], [getPosASL _box select 0,getPosASL _box select 1,(getPosASL _box select 2)-0.5],_box, objNull]) == 0) then 
{
// it's in the air
for "_i" from 0 to 100 do
{
	if ((count (lineIntersectsWith [[getPosASL _box select 0,getPosASL _box select 1,(getPosASL _box select 2)], [getPosASL _box select 0,getPosASL _box select 1,(getPosASL _box select 2)-0.1],_box, objNull]) == 0)) then
	{
                     // lower it a little
		if (getPosATL _box select 2 > 0.1) then
		{
			_box setPosASL [getPosASL _box select 0,getPosASL _box select 1,(getPosASL _box select 2)-0.2];
		}else{_i = 100;}
	}else{_i = 100};
	sleep 0.01;
};
};

Share this post


Link to post
Share on other sites

I don't know if you've ever looked through the ALICE module, but here's part of the scripting which puts civilians in houses:

//--- Set values
if (_movein) then {
scopename "moveIn";

_marker = if (_debug) then {_house call BIS_fnc_boundingBoxMarker;} else {""};

//--- In da house
_posList = [];
for "_i" from 0 to 10 do {
	_point = _house selectionposition format ["AIspawnpos_%1",_i];
	if (_point distance [0,0,0] > 0.1) then {
		_posList = _posList + [_point];
		if (_debug) then {
			_pointPos = _house modeltoworld _point;
			_pointMarker = createmarker [format ["X%1",floor random 99999],_pointpos];
			_pointMarker setmarkertype "mil_dot";
			_pointMarker setmarkercolor "colorpink";
		};
	} else {breakto "moveIn"};
};
if (_debug) then {
	_color = if (count _posList > 0) then {"colorgreen"} else {"colorred"};
	_marker setmarkercolor _color;
};

if (count _posList == 0) then {_posList = [[0,0,0]]};

_spawnpos = _house modeltoworld (_posList call bis_fnc_selectrandom);
_unit setposatl _spawnpos;
_unit setskill (random 1);
_unit setvelocity [0,0,0];
};

This may give you another option.

Share this post


Link to post
Share on other sites

Thanks for the feedback, I managed to get a function up an running with does the job decently.

@cuel: Thanks for bringing my attention to the lineIntersectsWith command. It's an amazing addition to the SQF-library, so useful!

Description:

Using lineIntersectsWith you can trace the intersection between the model and lines going from positions outside the building you want to retrieve from. With that technique you filter the building positions into arch-types.

With that in mind I put together the following function, it is a rewrite of my existing function for retrieving positions and allows for a more general use as well as sorting positions according to type.

Input a building and it will get all positions in the building.

Input a building in an array and it will retrieve all the building positions sorted into 3 nested arrays.

In the first array you'll find all positions that are indoors.

In the second are all positions that are on balconies and porches and the like.

In the last array all outside positions are found, such as rooftops and ground-level positions.

The function:

/*
------------------------------------------------------------------------------------
Function: c_fnc_getBuildingPositions
Can be used in 2 ways, simple and advanced

Simple use:  		building call c_fnc_getBuildingPositions
Parameter(s): 		building - Object
Returns: 			All positions in a building

Advanced use:		[building (, offset)] call c_fnc_getBuildingPositions
Parameter(s): 		[
			building - Object ,
			offset -(optional) Number - offset height which balcony-positions are measured against - default: 1.5
		]
Returns: Array of 3 Arrays with positions sorted according to [ inside positions, balconies and porches, rooftops ]
Beware, each nested array may or may not be empty, depending on the building.
------------------------------------------------------------------------------------
*/
c_fnc_getBuildingPositions = {
private ["_fnc_checkLOS", "_b", "_offset", "_aslPos", "_bBox", "_bBoxWidth", "_bBoxDepth", "_bBoxHeight", "_searchW", "_searchH", "_notVisable", "_visFromSide", "_visFromAbove", "_i", "_bP", "_xPos", "_visIndex", "_p"];
_fnc_checkLOS = {
	private ["_b", "_pos", "_width", "_height", "_facing", "_return", "_aPos", "_blockingObjects", "_nrVis", "_numChecks", "_dirDif", "_i", "_c", "_xDir", "_sidePos"];
	_b 		= _this select 0;
	_pos 	= ATLtoASL (_this select 1);
	_width	= _this select 2;
	_height	= _this select 3;

	_facing = getDir _b;

	_return = 0;

	_aPos = [_pos select 0, _pos select 1, (_pos select 2) + _height];
	_blockingObjects = lineIntersectsWith [_pos, _aPos];

	if( !(_b in _blockingObjects) ) then {
		_return = 2;
	} else {
		_nrVis = 4;
		_numChecks = 12;
		_dirDif = 360/_numChecks;
		_i = 0;
		_c = 0;
		while {_i < _numChecks && _c < _nrVis} do {
			_xDir = _facing + _dirDif*_i;
			_sidePos = [(_pos select 0)+(_width*sin _xDir), (_pos select 1)+(_width*cos _xDir), _pos select 2];

			_blockingObjects = lineIntersectsWith [_pos, _sidePos];
			if (!(_b in _blockingObjects)) then {
				_c = _c + 1;
			} else {
				_c = 0;
			};

			_i = _i + 1;
		};

		if(_c == _nrVis) then {
			_return = 1;
		};
	};		
	_return
};

if(typeName _this == typeName []) then { 
	_b 			= _this select 0;
	_offset 	= if(count _this > 1) then { _this select 1 } else { 1.5 };

	_aslPos 	= getPosASL _b;
	_bBox 		= boundingBox _b;

	_bBoxWidth 	= ((_bBox select 1) select 0) - ((_bBox select 0) select 0);
	_bBoxDepth 	= ((_bBox select 1) select 1) - ((_bBox select 0) select 1);
	_bBoxHeight = ((_bBox select 1) select 2) - ((_bBox select 0) select 2);

	_searchW 	= if(_bBoxWidth > _bBoxDepth) then {_bBoxWidth} else {_bBoxDepth};
	_searchH	= _bBoxHeight;

	_notVisable 	= [];
	_visFromSide	= [];
	_visFromAbove	= [];

	_i  = 0;
	_bP = _b buildingPos _i;	
	while {{_x != 0} count _bP > 0} do {
		_xPos = [_bP select 0, _bP select 1, (_bP select 2) + _offset];
		_visIndex = [_b, _xPos, _searchW, 50] call _fnc_checkLOS;
		switch (_visIndex) do {
			case 0: {
				_notVisable set [count _notVisable, _bP];
			};
			case 1: {
				_visFromSide set [count _visFromSide, _bP];
			};
			case 2: {
				_visFromAbove set [count _visFromAbove, _bP];
			};
			default {
				diag_log text format ["c_fnc_getBuildingPositions - Invalid _visIndex: %1", _visIndex];
			};
		};
		_i = _i + 1;
		_bP = _b buildingPos _i;
	};
	//Return
	[_notVisable, _visFromSide, _visFromAbove]

} else {
	_b 	= _this;
	_p 	= [];
	_i  = 0;
	_bP = _b buildingPos _i;	
	while {{_x != 0} count _bP > 0} do {
		_p set [count _p, _bP];
		_i = _i + 1;
		_bP = _b buildingPos _i;
	};
	_p	
}
};

The nested function _fnc_checkLOS can be, with some modification, used separately to check if any position is in a certain building.

Known issues:

Due to lineIntersectsWith not detecting some thin surfaces, window glass and some rooftops are not detected when checking Line Of Sight. In some cases this may cause odd sorting.

Examples:

Here is the test script I wrote for this function. It shows the usage of c_fnc_getBuildingPositions and will allow you to preview buildings in game.

Usage:

Place player in editor, add above code to init.sqf and add a repeatable radio-trigger

Ingame you can trigger the test while looking at a building to show its positions and trigger it again to clean up.

Red: Indoors

Green: Balconies and porches

Blue: Rooftops and outdoor positions

Edited by mrCurry
bad format

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
Sign in to follow this  

×