Jump to content
thecoolsideofthepillow

Placing Triggers Randomly with Random Size?

Recommended Posts

I want to create trigger zones, but I want to place them randomly when an event happens, and I want them to also come in random sizes as well as varying numbers of zones every time they are generated.

 

I am not sure about a couple of things though:

 

1. How do I even get/set a random map position that is still valid? It doesn't need to be around anything interesting, I just don't want it placed off in the unreachable parts of the map. Would it just be best to start at the player and pick a random value for the X and Y offsets?

 

2. How would I keep my randomly placed zones from overlapping?

 

3. Can I use the "do" command to execute something X number of times? This would help in generating the random number of markers to be placed.

Share this post


Link to post
Share on other sites

I would start by contructing some sort of for loop.

for "_i" from 1 to random(10) do
{
};

In order to  prevent overlappting, I'd choose a new position with https://community.bistudio.com/wiki/BIS_fnc_relPos. Simply use your trigger radius of the trigger you've just created and put it into the function.

 

About the placement of the zones. Since there are not really unreachable places I'd simply get the center of the map and then define an area with x and y.

 

I know that's very vague, but I think it could get you started.

Share this post


Link to post
Share on other sites

I would start by contructing some sort of for loop.

for "_i" from 1 to random(10) do
{
};

In order to  prevent overlappting, I'd choose a new position with https://community.bistudio.com/wiki/BIS_fnc_relPos. Simply use your trigger radius of the trigger you've just created and put it into the function.

 

About the placement of the zones. Since there are not really unreachable places I'd simply get the center of the map and then define an area with x and y.

 

I know that's very vague, but I think it could get you started.

 

This looks perfect. I can set the initial position from a marker placed in the center, then place additional points from the relative position of the last. So all I would need to do there is make the direction random, and the distance no less than the previous marker size combined with the new marker size.

 

However, I am wondering how I would do *that* since each item has to have a unique name, and using the loop would just execute the same exact command for however long the loop is. Can I make the command set the first name as "Marker1" then just add 1 to the name so the next would be "Marker2" and so on?

 

EDIT: Nevermind, I figured it out. Had to just remember what it was called (concatenation) to find what I needed. I think I got a perfectly working script now:

for "_i" from 1 to random(10) do
{
 _markername = Format ["marker_%1", _i];
 _rndDis = floor (random 500);
 _rndHead = floor (random 360);
 _rndX = [50,500] call bis_fnc_randomInt;
 _rndY = [50,500] call bis_fnc_randomInt;
 _mapPos = [player, _rndDis, _rndHead] call BIS_fnc_relPos;
 createMarker [_markername, _mapPos];
 _markername setMarkerSize [_rndX, _rndY];
};​

It works for markers, at least. I need to create triggers, though, and I am not sure if I can concatenate the triggers' variable names so I can set their activation and statements correctly. I mean, the trigger's name is set by saying "variable = createTrigger" so if I used "_markername = createTrigger" wouldn't it overwrite the previous setting of the variable "_markername?" Or would it create triggers named "Marker_any," "Marker_1," "Marker_2," and so on?

Share this post


Link to post
Share on other sites

I tried this:

_rndVal = [0,12] call bis_fnc_randomInt;
 
for "_i" from 3 to _rndVal do //Create as little as 3, but as many as 15 pockets of radiation.
{
 _rndDis = floor (random 5000); //Maximum distance from map center
 _rndHead = floor (random 360); //Compass bearing from map center
 _rndX = [50,500] call bis_fnc_randomInt; //X size of trigger area
 _rndY = [50,500] call bis_fnc_randomInt; //Y Size of trigger area
 _rndAngle = floor (random 360); // Angle of trigger area in the event of an elliptical zone
 _halfLife = random(0.075);
 _mapPos = [player, _rndDis, _rndHead] call BIS_fnc_relPos; //random map position to center trigger zone
 _trg = createTrigger ["EmptyDetector", _mapPos];
 _trg setTriggerArea [_rndX, _rndY, _rndAngle, false];
 _trg setTriggerActivation ["ANY", "PRESENT", true];
 _trg setTriggerStatements ["this", "{while (_trg) do {_x setDamage (damage _x - _halfLife);};} for each units in thislist;",""];
};
​

When I also combined that with a hint to show me all the "EmptyDetector" objects on the map, I just get a blank array ([]), so I am guessing I will actually need to give the trigger name (_trg) a unique name somehow. But I don't know how... I think I need to use "missionNameSpace," but I am confused about some of the details of how it would be used for my purposes.

 

EDIT: Or I could just use my working method of using markers and then just use the bis_fnc_intrigger command, as it seems that will also work with markers (which I already have working).

​

Share this post


Link to post
Share on other sites

Just noticed that you need to change

_rndVal = [0,12] call bis_fnc_randomInt;

to

_rndVal = [3,12] call bis_fnc_randomInt;

and

for "_i" from 3 to _rndVal

to

for "_i" from 1 to _rndVal

Otherwise the for loop could look as follows

for "_i" from 3 to 0 

 --> Will probably produce an error

 

Starting from 3 makes not sense anyway.

Share this post


Link to post
Share on other sites
I have since corrected that (kept wondering why it would sometimes generate 10-12 zones, but other times it would be less than 3, even 0) when I saw a post describing what the for command was actually doing. I thought it was just doing X times between the first number and second. I was wrong. :P

 

Here's what I have so far:

 




DecayZones = [];
{deleteMarker _x;} forEach DecayZones;
 
_rndZones = [10,25] call BIS_fnc_randomInt;
for "_i" from 1 to _rndZones do
{
 _markername = Format ["marker_%1", _i]; //Concatenate the marker names to be unique
 _rndDis = floor (random 15000); //Set the radius to place the markers
 _rndHead = floor (random 360); //Set the compass bearing to create markers from center point
 _rndX = [50,750] call bis_fnc_randomInt; //Set the X size of the marker
 _rndY = [50,750] call bis_fnc_randomInt; //Set the Y size of the marker
 _rndDir = floor (random 360); //Set the marker's direction, separately from the original compass bearing used to place the marker.
 _mapPos = [getMarkerPos "MapCenter", _rndDis, _rndHead] call BIS_fnc_relPos; //Set the point from which to create the zones around.
 createMarker [_markername, _mapPos]; // Actually create the marker
 _markername setMarkerSize [_rndX, _rndY]; //Set the marker's size
 _markername setMarkerDir _rndDir; //Set the marker's direction in the event of oblong zones for more variety in shape.
/*
 
 You can uncomment the following lines to make the markers visible on the map for debuggging purposes.
 
 _markername setMarkerType "Empty";
 _markername setMarkerShape "ELLIPSE";
 _markername setMarkerBrush "Solid";
 _markername setMarkerColor "ColorRed";
*/
 DecayZones = DecayZones + [_markername]; //Add all the markers to an array to keep track of the buggers.
};
​


 

It works for creating the markers the way I want. What I am having difficulty with now is changing from markers to triggers or using the markers themselves as a trigger area.

 

Trying to keep what I have, I've tried

 



_inTrigger = [DecayZones, allUnits] call BIS_fnc_inTrigger;


 

which returns an error saying that "marker_2" is a string and not an array or scalar value (odd because it only says "marker_2" which means it has no issue parsing "marker_1"). But if I try

 



_inTrigger = ["DecayZones", allUnits] call BIS_fnc_inTrigger;


 

It gives an error about using the same position more than once. I would guess because it wants a position and returning the units name won't work, even though I *could* use player or an object's name as the position value.

 

Can I turn my markers into triggers quickly and painlessly without having to make a bunch of redundant lines for markers that might not even exist?

 

Again, I could also just try to make the markers triggers (which would make setting the effect the zones actually has *much* easier) but I don't know how to sequentially name the triggers so I can mess with their settings. Everything I search for comes back with various ways of using missionNameSpace, but they all use units/vehicles as their examples (like THIS), and while the examples work great for units, it doesn't seem to work with trigger names. I can easily make a bunch of units names "Unit1, Unit2, Unit3" etc with the missionNameSpace, but creating triggers that way does not do anything.

 

EDIT: I think I figured it out. Stupidly simple, too... Just place a trigger down for each marker, on each marker, the same shape size and direction as the marker. It actually worked to give me a number of triggers unlike trying to just generate the triggers in the for loop.

 

Just put this in the final line of the script:

 



{_trg = createTrigger ["EmptyDetector", getMarkerPos _x];} forEach DecayZones;​

Share this post


Link to post
Share on other sites
_inTrigger = ["DecayZones", allUnits] call BIS_fnc_inTrigger;

Problem here is not the marker, the problem is the second parameter allUnits. allUnits is an array which contains all units present in the mission. However, the function wants a position. Therefor I'd change the code to the following:

_inTrigger = ["DecayZones",getMarkerPos "DecayZones"] call BIS_fnc_inTrigger 

Presuming that your position is the marker's center.

Share this post


Link to post
Share on other sites

Problem here is not the marker, the problem is the second parameter allUnits. allUnits is an array which contains all units present in the mission. However, the function wants a position. Therefor I'd change the code to the following:

_inTrigger = ["DecayZones",getMarkerPos "DecayZones"] call BIS_fnc_inTrigger 

Presuming that your position is the marker's center.

 

 

This snippet produces the same error.

 

I should point out, DecayZones is the name of an array containing all the markers (named "Marker_1" through however many there are), not a marker itself. The biki says it can be an array, and I figure it would be doing this for everything in that array, which is what I want.

Share this post


Link to post
Share on other sites

Ok, my fault. I read it again and I'd say with array it means the a and b axis of the trigger. So you can either put in a trigger or it's axis.

{
     _inTrigger = [_x,getMarkerPos _x] call BIS_fnc_inTrigger 
} forEach DecayZones;

Maybe something like this works? I'm not sure if I understood what you want to do with BIS_fnc_inTrigger.

Share this post


Link to post
Share on other sites

I am trying to create randomly placed and sized areas that will cause any infantry unit to slowly take damage while inside them, unless certain other conditions are met.

 

I can create markers of random shapes and sizes around the map, but I haven't been able to do so with triggers. So I was trying to see if I could use the inTrigger function to turn the markers into triggers, basically.

 

When I put

{_trg = createTrigger ["EmptyDetector", getMarkerPos _x];} forEach DecayZones;

at the end of my script, I could use allMissionObjects "EmptyDetector" to find a number of arrays as I had markers set, but I could not change their area or other paramters. Their names (at least I think thats what they are) just show as a random string of numbers like 1779933, 179950, etc. When I try to use any of the triggerXXXX commands on the numbers, it just closes the debug console with no errors and outputs nothing.

 

EDIT: Interestingly enough, I have found that even just going by the example usage of BIS_fnc_inTrigger for checking if the player is within a marker produces the same "same position" error, but produces the right output if the player is in the marker or not.

Share this post


Link to post
Share on other sites

BIS_fnc_triggerToMarker will make your life easier.

 

{
    _trg = [ objNull, _x ] call BIS_fnc_triggerToMarker;
    _trg setTriggerActivation [ "BY", "TYPE", "REPEATING" ];
    _trg setTriggerStatements ["CONDITION", "ACTIVATION", "DEACTIVATION"];
}forEach DecayZones;
TBO if you dont need the markers i would just make the triggers straight away.

. I need to create triggers, though, and I am not sure if I can concatenate the triggers' variable names so I can set their activation and statements correctly.

There is no need to name them just used the returned trigger from the create command to apply settings.

_trg = createTrigger ["EmptyDetector", _mapPos];
_trg setTriggerArea [_rndX,_rndY, _rndDir, false];
_trg setTriggerActivation [ "BY", "TYPE", "REPEATING" ];
_trg setTriggerStatements ["CONDITION", "ACTIVATION", "DEACTIVATION"];
DecayZones pushback _trg;
Then push them into DecayZones array so you have a reference to the objects if you need to do something to them like deleting etc

Share this post


Link to post
Share on other sites

BIS_fnc_triggerToMarker will make your life easier.

 

{
    _trg = [ objNull, _x ] call BIS_fnc_triggerToMarker;
    _trg setTriggerActivation [ "BY", "TYPE", "REPEATING" ];
    _trg setTriggerStatements ["CONDITION", "ACTIVATION", "DEACTIVATION"];
}forEach DecayZones;
TBO if you dont need the markers i would just make the triggers straight away.

There is no need to name them just used the returned trigger from the create command to apply settings.

_trg = createTrigger ["EmptyDetector", _mapPos];
_trg setTriggerArea [_rndX,_rndY, _rndDir, false];
_trg setTriggerActivation [ "BY", "TYPE", "REPEATING" ];
_trg setTriggerStatements ["CONDITION", "ACTIVATION", "DEACTIVATION"];
DecayZones pushback _trg;
Then push them into DecayZones array so you have a reference to the objects if you need to do something to them like deleting etc

 

 

Well, I tried this based on your second suggestion:

DecayZones = [];
{deleteVehicle _x;} forEach DecayZones; 
 
_rndZones = [10,25] call BIS_fnc_randomInt;
for "_i" from 1 to _rndZones do
{
 _rndDis = floor (random 15000); //Set the radius to place the markers
 _rndHead = floor (random 360); //Set the compass bearing to create markers from center point
 _rndX = [50,750] call bis_fnc_randomInt; //Set the X size of the marker
 _rndY = [50,750] call bis_fnc_randomInt; //Set the Y size of the marker
 _rndDir = floor (random 360); //Set the marker's direction, separately from the original compass bearing used to place the marker.
 _toxicity = random 0.075; //Random amounts of radiation damage to receive without protection
 _mapPos = [getMarkerPos "MapCenter", _rndDis, _rndHead] call BIS_fnc_relPos; //Set the point from which to create the zones around.
 _trg = createTrigger ["EmptyDetector", _mapPos];
 _trg setTriggerArea [_rndX,_rndY, _rndDir, false];
 _trg setTriggerActivation [ "ANY", "PRESENT", true];
 _trg setTriggerStatements ["this", "{_x setDamage (damage _x + _toxcicity);} forEach units thislist;", ""];
 DecayZones pushback _trg;
​

It creates a random number of entries into the DecayZones array, just as I had gotten it to before, but it is not setting the trigger's effects. If I walk into or teleport myself into the triggers, I do not take any damage, but they do activate.

 

I can use the triggerXXXX commands if I set a variable first as an array's indice (_selection = DecayZones select 0) and then I see the zones are at least the proper area and statements and such.

 

I have the statements correct, right? I've never needed to make triggers from a script before, so I could be doing something wrong there. The triggers are activating correctly if a unit is inside, it just doesn't seem to be playing the "On Act" line of the triggerStatements I used to slowly drain their health for a random amount up to 0.075 damage per trigger tick.

Share this post


Link to post
Share on other sites
I did it!
 
//This script will spawn random zones of varying levels of radioactivity (slow death zones with varying degrees of how quickly they kill)
//It is designed to be used with a Blow Out script to re-create the changes of anomalies after such an event in STALKER,
//but can be used for other things as well.
//This particular example is set up for the size of Altis, creating between 10 to 25 zones ranging from 50m to 1.5km in size in a 15,000x15,000 square from the center of the map.
 
DecayZones = [];
{deleteVehicle _x;} forEach DecayZones; //"DecayZones" is a global variable defined at init. Either in the init.sqf or from an object's init field from the editor.
 
_rndZones = [10,25] call BIS_fnc_randomInt;
for "_i" from 1 to _rndZones do
{
 _rndDis = floor (random 15000); //Set the radius to place the markers
 _rndHead = floor (random 360); //Set the compass bearing to create markers from center point
 _rndX = [50,750] call bis_fnc_randomInt; //Set the X size of the marker
 _rndY = [50,750] call bis_fnc_randomInt; //Set the Y size of the marker
 _rndDir = floor (random 360); //Set the marker's direction, separately from the original compass bearing used to place the marker.
 _mapPos = [getMarkerPos "MapCenter", _rndDis, _rndHead] call BIS_fnc_relPos; //Set the point from which to create the zones around.
 _trg = createTrigger ["EmptyDetector", _mapPos];
 _trg setTriggerArea [_rndX,_rndY, _rndDir, false];
 _trg setTriggerActivation [ "ANY", "PRESENT", true];
 _trg setTriggerStatements ["call {if (count thislist > 0) then {{if (alive _x) then {_toxicity = random 0.075; _x setDamage (damage _x + _toxicity)}} forEach thislist};false}", "", ""];
 DecayZones pushback _trg;
};

This works not only for the units I expected, but also for the randomly generated animals the map spawns; a nice bonus for skilled STALKERS to notice the pockets without a Geiger counter.

 

It does not yet take into account any kind of radiation protection because I have not yet created those items. That's next. The long, and tedious task of moving a lot of the junk map objects into a list of usable inventory items.

 

EDIT: Although it occurs to me that the AI won't really do anything about the zones. Not a problem for those that spawn with protective suits, but all the others will just wander/spawn inside these zones and die (still kinda cool just for loot purposes I guess). I wonder if I could have them not be aware of them until a member of a group (or nearby group/all units with a radio, etc) takes X damage from the field then calls it in so all the other units who know about the field can avoid it.

Share this post


Link to post
Share on other sites

The only reason your first attempt in post #14 didn't work is because you tried to use _toxcicity. A triggers scripts run in thier own scope, so it would not know what _toxcicity is, as that was defined in your scripts scope.
 

_trg setTriggerStatements ["this", "
	{
		if ( alive _x ) then {
			_x setdamage (( damage _x ) + 0.075 );
		};
	}foreach thislist;
", ""];

Imagine your saying to the engine, "Here is a trigger, check it every 0.5seconds for this condition and run this code when it fires off, now take care of it for me".
Now this could happen at any time, 5 seconds, 10minutes or half hour later etc So at this point the trigger knows nothing about the script that created it all it has is some code it has been told to run.

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

×