Jump to content
Sign in to follow this  
NGagedFX

Returning Position of a Trigger

Recommended Posts

Just an example:

8073840a.jpg

BLUFOR, PRESENT, ONCE
Condition: this
OnAct: [this] exec "spawnAI.sqf";

spawnAI.sqf example

_spawnpos = getPos this;
_unit = group1 createUnit ["TK_GUE_Soldier_2_EP1", _spawnpos, [], 0, "FORM"]

As you can see, this script would be independent of trigger and marker names, which would make it very easy to have this single trigger spawn a single squad, and to occupy larger areas, just Ctrl+C Ctrl+V the trigger all over the area.

The only problem for me at this point, is that I have no idea of how to get the value of the position of the trigger, in the script. I've had problems with using [this] in combination with triggers, and I've been told that there is no "this", only "thislist". :(

So does anyone know a solution to get the position of the center of the trigger?

Edited by NGagedFX
Updated with a better explanation

Share this post


Link to post
Share on other sites

It would help to know what result you are trying to achieve, and why a nameless trigger?

However let me share a couple things.

- SQF needs to be called by execVM, not exec, otherwise you will cause errors and things may not work correctly

- if you DID name the trigger, let's call it 'triggy' and in the OnAct line put this:

_h = [thisList] execVM "script.sqf";

then group the trigger to a player group leader, change trigger Activation to "whole group", and in script.sqf put this:

_unitList = _this select 0;
player sideChat format["Position: %1", getPos triggy];

{
player sideChat format["trigger item: %1, position: %2", _x, (getPos _x)];
} forEach _unitList;

Then preview it and lead the group into the trigger area. You will get the position of the trigger, and the position of each unit by name in the trigger.

As I said, I have no idea what result you need, so I'm just throwing ideas at you.

Share this post


Link to post
Share on other sites

The application you suggest doesn't seem to require nameless trigger, like the others suggest.

Anyway, if you insist of using nameless triggers, if you know approx where it is to avoid having a large search area, then I use:

_trigger = nearestObject [_pos, "EmptyDetector"]; //[color="Red"]nearestObject[b]s[/b] doesn't seem to work with triggers.[/color]

Then build a list of these triggers. When list is complete, iterate through all triggers in that list and search for some search criteria of your choosing. I'm using triggerText.

Complete example in the spoiler.

My complete script, which is used in relation to deleting sound triggers if the object (i.e. a building) having a sound trigger have a killed eventhandler on it. To delete the sound triggers associated with that object, I call this from the EH:

XfSoundObjectRemover = {
private ["_killed","_pos","_i","_trigger","_objecttypes"];
_killed = _this select 0;
_pos = getPos _killed;
_objecttypes = ["RadarTop","RadarBottom","RadarGenerator","HQAntenna","HQRadio","FactoryContainer","FactoryGenerator","FactoryTank","AmbulWalkie","TowerWalkie"];
for "_i" from 0 to 4 do {
	_trigger = nearestObject [_pos, "EmptyDetector"]; //nearestObjects doesn't seem to work with triggers.
	if ((_pos distance _trigger < 40) && (triggerText _trigger in _objecttypes)) then {
		sleep 1.234;
		deleteVehicle _trigger;
	};
};
_objecttypes = nil;
};

Not relevant to the question per se, but here is how I place these nameless triggers:

XfSoundObjectPlacer = {
private ["_vec","_pos","_trigger"];
_create = {
	_vec = _this select 0;
	_pos = _this select 1;
	_sound = _this select 2;
	_text = _this select 3;
	_trigger = createTrigger ["EmptyDetector", _vec modelToWorld _pos];
	_trigger setTriggerStatements ["true", "", ""];
	_trigger setSoundEffect ["NoSound", "", "", _sound];
		_trigger setTriggerText _text;
	};
_vec = _this;
switch (typeOf _vec) do {
	case "US_WarfareBAntiAirRadar_EP1" : {
		[_vec, [-3.3,-6.5,5], "Factory07Sfx", "RadarTop"] call _create;
		[_vec, [-3.5,-7.7,2.5], "fx_humming", "RadarBottom"] call _create;
		[_vec, [3,2,2.5], "Factory01Sfx", "RadarGenerator"] call _create;
	};
	case "TK_WarfareBArtilleryRadar_EP1" : {
		[_vec, [-3,-7,5], "Factory07Sfx", "RadarTop"] call _create;
		[_vec, [-3.5,-7.7,2.5], "fx_humming", "RadarBottom"] call _create;
		[_vec,  [-9.6,1,2.5], "Factory01Sfx", "RadarGenerator"] call _create;
	};
	case "TK_WarfareBAntiAirRadar_EP1" : {
		[_vec, [-3.3,-6.5,5], "Factory07Sfx", "RadarTop"] call _create;
		[_vec, [-3.5,-7.7,2.5], "fx_humming", "RadarBottom"] call _create;
		[_vec,  [3,2,2.5], "Factory01Sfx", "RadarGenerator"] call _create;
	};
	case "BMP2_HQ_TK_unfolded_EP1" : {
		[_vec, [-0.5,3.9,1.5], "fx_humming", "HQAntenna"] call _create;
		[_vec, [0,0,2], "radio_walkie1_EP1", "HQRadio"] call _create;
	};
	case "TK_WarfareBHeavyFactory_EP1" : {
		[_vec, [3,9,2], "Crunch_EP1", "FactoryContainer"] call _create;
		[_vec, [7,2,2], "Factory01Sfx", "FactoryGenerator"] call _create;
	};
	case "TK_WarfareBLightFactory_EP1" : {
		[_vec, [4,-2.5,2], "Crunch_EP1", "FactoryContainer"] call _create;
		[_vec, [0.5,3.5,1.9], "Factory08Sfx", "FactoryTank"] call _create;
	};
	case "M113Ambul_TK_EP1" : {
		[_vec, [0,1,-1], "radio_walkie1_EP1", "AmbulWalkie"] call _create;
	};
	case "Land_Fort_Watchtower_EP1" : {
		[_vec, [-1,-3,1], "radio_walkie1_EP1", "TowerWalkie"] call _create;
	};
};
};

Btw, I advice against this method of creating sound. It works both in theory and practice, but anything dealing with cfgSFX (trigger sounds) are horribly unreliable. Examining it, you may find the trigger is there, it just doesn't produce any sound. The sound object within it seems to get destroyed somehow and we don't have any means of accessing it.

Note to self: XfSoundObjectRemover - for loop? Wtf was I thinking? It won't produce any errors, but it's bad practice nonetheless :p

Edited by CarlGustaffa

Share this post


Link to post
Share on other sites

That's funny, we all ask the same question about nameless trigger.

My example required naming the trigger. I like your nameless solution though, Carl. The only nameless idea I had was to look up the position in mission.sqm, then hardcode it in script, which is just not cool.

Share this post


Link to post
Share on other sites

I want a trigger to execute a script that spawns AI units at the position of the trigger that this script is executed from. I want a single setup with a single script that I can easily copy/paste in a mission without having to specify a name for every trigger.

I can't explain it any better now cause I'm on a mobile phone, but I'll take a look at your suggestions once I'm home. I'm thankful for your input :)

Edited by NGagedFX

Share this post


Link to post
Share on other sites

attachto the trigger to the object? Then you can just getpos the object instead.

---------- Post added at 11:41 AM ---------- Previous post was at 11:39 AM ----------

What fires the trigger? Or do you just do condition: true? Why do you need a trigger, why not just scripted solution?

Share this post


Link to post
Share on other sites

To explain myself a bit clearer, this would be an example of a trigger:

BLUFOR, PRESENT, ONCE
Condition: This
OnAct: this exec "spawnAI.sqf";

spawnAI.sqf example

_unit = group1 createUnit ["TK_GUE_Soldier_2_EP1", [b]positionofthetrigger[/b], [], 0, "FORM"]

As you can see, this script would be independent of trigger and marker names, which would make it very easy to have this single trigger spawn a single squad, and to occupy larger areas, just Ctrl+C Ctrl+V the trigger all over the area.

The only problem for me at this point, is that I have no idea of how to get the value of the position of the trigger, in the script. When you have a random TANK, and in the Init: [this] exec "sigh.sqf", then in the script, you can extract its position through _positiontank = getPos _this;

However, I've had problems with using [this] in combination with triggers, and I've been told that there is no "this", only "thislist". :(

Edited by NGagedFX

Share this post


Link to post
Share on other sites
To explain myself a bit clearer, this would be an example of a trigger:

...
OnAct: [b]this[/b] exec "spawnAI.sqf";

However, I've had problems with using [this] in combination with triggers, and I've been told that there is no "this", only "thislist". :(

So then, why are you calling with this in on activation? ;)

You could use my method, at least for non overlapping areas. If you send in thislist (instead of this) you have the position of the unit activating the trigger ([thislist], then _this select 0 select 0 in the script). Names have to be unique, but text fields doesn't. Search for "emptydetector" near this position, and verify that it's textfield is what it should be, and you have a reference to the trigger.

Yeah, I'm annoyed too that the trigger object itself isn't sent in with this parameter, would have made life easier.

Share this post


Link to post
Share on other sites

Tanks for your input Carl, but I'm not too much into scripting yet, so I'm not sure if I understand you correctly. My goal is to have units spawn on the flags once BLUFOR gets in the circle.

8073840a.jpg

If I'm correct, your solution is like this:

-A unit enters the trigger's area.

-The trigger executes the script through [thislist] exec "spawnAI.sqf"

-The script starts off with _unit = (_this select 0) select 0;

-[thislist] contains the BLUFOR units in the area, the 2nd select 0 selects the first unit in [thislist]

-With _trigger = nearestObject [_unit,"EmptyDetector"] the script then searches for an "EmptyDetector" (the trigger) near the previously selected unit

-_spawnpos = getPos _trigger would mark the position of the trigger and this position can then be used to spawn AI

The advantage would be that you could just copy/paste such a trigger for every squad you would like to spawn once BLUFOR gets close enough. However, the drawback would be, that in the screenshot I uploaded, there is a change that nearestObject would locate another trigger with a smaller circle that is closer than the trigger nearestObject was originally summoned from.

Share this post


Link to post
Share on other sites

Forget nearestObject. Even if nearestObjects doesn't see triggers, nearObjects does (forces me to do some own reprogramming today, lol). You could do additional filtering by checking if trigger is activated. Not perfect though. I'd say you are in a bit of trouble. Can't figure out a good workaround other than naming your triggers. Can't even name them after mission start with a script it seems. And without an object reference, for an object that doesn't have an init field, you can't assign variables to a trigger using setVariable either.

Share this post


Link to post
Share on other sites

You know that if you name one trigger, then copy & paste it several times, each one will have the same name with a _1, _2, _3, etc extension? It would be a minor detail to pass the name of the trigger in the call to your script.

_h = [trigger_3] execVM "script.sqf";

It would be quick to edit the mission.sqm with a find, then just replace the ending value so that it matches the name of the trigger it references.

Also, sqf files should be called with execVM, because exec is not 100% compatible:

http://community.bistudio.com/wiki/execVM

Share this post


Link to post
Share on other sites

Well, looks like it is no way possible then. I greatly thank you guys for you input, and I'll have a jump in the editor to see what works best. I'll let you know if and how it works. :D;)

Also, AZCoder, for some reason I'm unable to use execVM, that's why I've been using exec. When I try [thislist] execVM "script.sqf" in a trigger's OnAct-field, it give me an error and make me unable to close the options box until I remove the VM. Or is it only possible by using null = for instance?

Edited by NGagedFX

Share this post


Link to post
Share on other sites
Also, AZCoder, for some reason I'm unable to use execVM, that's why I've been using exec. When I try [thislist] execVM "script.sqf" in a trigger's OnAct-field, it give me an error and make me unable to close the options box until I remove the VM.

Well it does tell you why it is unable to close the options box. Here is a one solution:

0 = [thisList] execVM "script.sqf";

Share this post


Link to post
Share on other sites

You need to assign the script handle to something with execvm.

nul = [] execvm...

0 = [] execvm...

whatever = [] execvm...

Just don't assign it to nil, null or something useful.

Share this post


Link to post
Share on other sites
Also, AZCoder, for some reason I'm unable to use execVM, that's why I've been using exec. When I try [thislist] execVM "script.sqf" in a trigger's OnAct-field, it give me an error and make me unable to close the options box until I remove the VM. Or is it only possible by using null = for instance?

Please use -showScriptErrors, I'm sure there will be plenty if you try to execute sqf code from a exec call, as they are not compatible.

shk is correct though, you need to have a handle when called from a trigger, exec doesn't need this which is why it appears to work.

Share this post


Link to post
Share on other sites

I make it consistent to use a local var for the return handle from execVM. With the handle you can use the scriptDone command, or terminate the script. If you use a global var or null, then those commands will either do nothing, or act upon the last execVM call made which may not be the one you expect. If you're running multiple script threads with the same global return handle, it is a gamble as to which thread will be represented. Of course if you don't use scriptdone or the terminate commands, then it's not a problem.

Share this post


Link to post
Share on other sites

I'm pretty sure he doesn't use them, when he wasn't even aware that he needed to assign them. ;)

I use local as well in scripts, but I was under the impression you couldn't use local in trigger etc. Was that changed at some point?

Share this post


Link to post
Share on other sites

I use 0 = for almost anything. If I need to terminate script or check scriptDone (which is extremely rare for me), I use local or global depending on where I need to do it.

I'm still using _xhandle = [] execVM ... in my mission from trigger, so I don't think it's invalid, other than serving no particular reason :p

Share this post


Link to post
Share on other sites

That's a lot of abstract stuff for something I've never even got a clue about. Just to get things straight:

0 = nothing, the returned value just dissapears?
nul = nothing, the returned value just dissapears?
null = I have no idea
nil = destroys any given variables
name = a name for the executed script in global space
_name = a name for the executed script in local space

In a more practical manner:

-when I simply want to execute a script to add ammo to an object just once > 0 or null

-when I want to execute a script with a loop that's gotta be removed at some point > _name if I want to terminate it from withing the script, name if I want to terminate it through another script or trigger

Edited by NGagedFX

Share this post


Link to post
Share on other sites
Just don't assign it to nil, null or something useful.

nil is nothing, it's what undefined variables are. You shouldn't assign value to it (it's silly that the game engine even allows it).

null is also "nothing", it's what defined variables are when there's no value assigned to them. Don't assign script handles to it either.

0 or any other number, the handle is lost, because engine doesn't allow overwriting numbers obviously.

Any other variables name, local or global, can be used. With them you can use commands like scriptdone and terminate.

You shouldn't need terminate ever. Sometimes it can be handy, but for normal scripts with loops or not, it's not needed. Loops check a condition that can be used to stop them.

Share this post


Link to post
Share on other sites

I originally thought that triggers needed a global return handle, but I've been using locals for a few months without problem. I actually don't bother with the handle inside most of my scripts, unless I actually need it. Of course map editor objects like triggers require it.

Never used terminate myself. It was one of two commands that I know which *could* make use of the handle :)

I would think that using 0 (or whatever integer) should be a safe return handle, since you can't do anything with it later, the spawned thread can't be affected.

Share this post


Link to post
Share on other sites

I'm only guessing here, but what happens if you have a loop running or something time consuming, and you rely on a check within that script? It would have to finish before reaching the next check. I think terminate allows instant shutdown of the script, without any delay. Yeah, sounds dangerous, but I found quite a few uses of it in BIS own missions, like:

terminate _ppeffects; //onPlayerKilled

terminate _check; //onPlayerKilled

terminate _separateThread; //FirstToFight

{terminate _x} forEach _handles; //FirstToFight

{terminate _x} forEach _allSpawnedScriptsInScene; //destroy all running scripts, in scenes everywhere.

terminate _viewDistanceSpawn; //Shooting range

So I wouldn't completely ignore the command, especially on certain procedures.

Share this post


Link to post
Share on other sites

Thanks for clearing thing up a lot, this thread has been very helpful for me. :)

I wasn't going to ask it here, but now we're talking about terminating scripts:

I've got this IED script (thanks to shk and HitmanFF) that will create and explosion once a BLUFOR "CAR" gets near it. However, I want soldiers to be able to disable this IED through an action menu when close and pointing at the IED. Since the IED script has a loop that makes it check for a BLUFOR car, I think terminating the script is the only way to shut it down?

Here's the script:

//init: nul = [this, "size"] execVM "IED.sqf"
//size can be "small", "medium" or "large"

//private["_bomb", "_target", "_charge"];

_bomb = _this select 0;
_size = _this select 1;

_target = nearestObject [_bomb, "CAR"];
while {(side _target != west) or (_target distance _bomb >= 10)} do
{
 sleep 1;
 _target = nearestObject [_bomb, "CAR"];
};

switch (_size) do
{
   case "small":
   { 
       _charge1 = "B_30mmA10_AP" createVehicle (getPos _bomb);
       _charge2 = "B_30mmA10_AP" createVehicle (getPos _bomb);
   };

   case "medium":
   {
       _charge1 = "M_Sidewinder_AA" createVehicle (getPos _bomb);
       _charge2 = "M_Sidewinder_AA" createVehicle (getPos _bomb);
       _charge3 = "M_Sidewinder_AA" createVehicle (getPos _bomb);
   };

   case "large":
   {
       _charge1 = "Bo_GBU12_LGB" createVehicle (getPos _bomb);
   };
};

deleteVehicle _bomb;

So how would you guys use and action menu to "turn off" this script? :rolleyes:

Share this post


Link to post
Share on other sites

//Init phase:
bStillRunning = true;
//Check against it:
while {[b]([/b](side _target != west) || (_target distance _bomb >= 10)[b])[/b] && (bStillRunning)}

Then in the action script:

bStillRunning = false;

Should work, but termination might be good too, especially if it's in a "time consuming" script that takes a long while to repeat.

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  

×