Jump to content
Sign in to follow this  
cornhelium

Passing arrays from a script with firedEH

Recommended Posts

Hi,

I'm having trouble with the final element of my wildlife sounds script. This is the part where you'd hear the sounds of birds/animals fleeing in panic when anyone fires in a trigger area:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

;fauna.sqs

;each ambient loop uses a set of two triggers and one logic, all with unique global names

;within fauna.sqs and all subsequent scripts they will be treated as local

_main = _this select 0

;a large repeating trigger anybody present

;emits the sound and is used by fauna.sqs to add firedEHs to units

_logic = _this select 1

;a logic that will be used as a distance reference and moved around

;various distance checks ensure that fauna.sqs cannot be run

;simultaneously by the same set

_innertrigger = _this select 2

; a smaller trigger anybody present, used to detect units in an inner warning call area

; when player is within 600m of _logic, _innertrigger execs fauna.sqs and passes the set to it

_type = _this select 3

; the type of critter specified in the activation field of _innertrigger

; points fauna.sqs to the relevant section

;===========================

?(_type == "nightjar_sing"):  goto "nightjar_sing_loop"

?(_type == "nightjar_hunt"):  goto "nightjar_hunt_loop"

?(_type == "thrushnightingale_sing_alarm"):  goto "thrushnightingale_sing_alarm_loop"

?(_type == "thrushnightingale_sing"):  goto "thrushnightingale_sing_loop"

;===========================

#nightjar_hunt_loop

_mainlist = list _main

_mainmemory = +_mainlist

_idx = {_x addEventHandler [{fired}, {[_this select 0,_main,_logic,_innertrigger,_type] exec "flyoff.sqs"}]} foreach _mainmemory

{_x addeventhandler ["fired", format [{(_this select 0) removeeventhandler [{fired}, %1]},_idx]]} foreach _mainmemory

;move _logic away so that script can't be re-triggered for this set

_logic setpos [0,0,0]

;check if any units are in the warning call area

_list1 = list _innertrigger

? "man" counttype _list1 > 0 : goto "nightjar_hunt_wait"

;if inner area clear, say song file and wait for it to finish

_main say "nightjar_eurasian_hunting"

~20

; has player left the area?

?(player distance _main > 600) and (_innertrigger distance _main < 50):goto "shutdown"

; is player in area, but flyoff.sqs has moved other elements to 0,0,0?

?(_innertrigger distance _main > 50): goto "flownoff"

;move _main away and back, to refresh its unit list

_main setpos [0,0,0]

~1

_repos = getpos _innertrigger

_main setpos _repos

goto "nightjar_hunt_loop"

#nightjar_hunt_wait

hint "nightjar hunt waiting while player is in trigger area"

;wait silently

;other critter types will sound a warning or alarm call

~15

;check again that player hasn't left area or flyoff.sqs hasn't been triggered

?(player distance _main > 600) and (_innertrigger distance _main < 50):goto "shutdown"

?(_innertrigger distance _main > 50): goto "flownoff"

;refresh _main unit list

_main setpos [0,0,0]

~1

_repos = getpos _innertrigger

_main setpos _repos

_list1 = list _innertrigger

;check if any units are still in warning call area

;if so, continue waiting

;if not, go back to main singing loop

? "man" counttype _list1 > 0 : goto "nightjar_hunt_wait"

goto "nightjar_hunt_loop"

;

;

;

;similar sections for different critter types

;

;

;

#shutdown

hint "player over 600m while sound is playing - exiting now"

~2

_repos = getpos _innertrigger

_logic setpos _repos

exit

#flownoff

hint "flyoff.sqs has been tripped while fauna.sqs is in loop. Exit fauna.sqs"

exit

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

;flyoff.sqs

;when a unit fires, _main should emit a panic call, then "fly away" for 3 minutes

;placeholder sound used for now

_shooter = _this select 0

_main = _this select 1

_logic = _this select 2

_innertrigger = _this select 3

_type = _this select 4

player globalchat "flyoff.sqs executed"

;===========================

?(_type == "nightjar_sing"):  goto "nightjar_sing_flyoff"

?(_type == "nightjar_hunt"):  goto "nightjar_hunt_flyoff"

?(_type == "thrushnightingale_sing_alarm"):  goto "thrushnightingale_sing_alarm_flyoff"

?(_type == "thrushnightingale_sing"):  goto "thrushnightingale_sing_flyoff"

;===========================

goto "shutdown"

#nightjar_hunt_flyoff

player globalchat "nightjar_hunt_flyoff"

_repos = getpos _innertrigger

_main setpos [_repos select 0, _repos select 1, +250]

_main say "bas_af_attack3"

~3

_logic setpos [0,0,0]

_main setpos [0,0,0]

~180

goto "shutdown"

#shutdown

hint "repositioning main trigger"

_repos = getpos _innertrigger

_main setpos _repos

~5

hint "repositioning check logic"

_logic setpos _repos

exit

The problem seems to in passing the arguments to flyoff.sqs from within fauna.sqs:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

_idx = {_x addEventHandler ["fired", {[_this select 0,_main,_logic,_innertrigger,_type] exec "flyoff.sqs"}]} foreach _mainmemory

I get the "flyoff.sqs executed" message, so the script certainly execs, but it immediately goes to the shutdown section, which suggests that it isn't picking up any of the array elements which fauna.sqs passes to it.

Can someone tell me the right syntax to use here pls?

Secondly, when it comes to removing the EH, I get problems. Here's something I lifted from an old General Barron thread. To be honest, I'm completely out of my depth with it:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

{_x addeventhandler ["fired", format [{(_this select 0) removeeventhandler [{fired}, %1]},_idx]]} foreach _mainmemory

This gives an error message:

'(_this select 0) removeeventhandler [{fired}, scalar |#|bool array string 0xfcffffef]': Error Unknown operator bool

Can anyone advise pls? What I need is the same firedEH added to each unit in the trigger, then that same EH removed from those units as soon as one unit fires.

Using removeallEHs messes up the other EHs that the units may or may not have.

Thanks,

CH

Share this post


Link to post
Share on other sites

Tricky one!

I don't have access to my game right now, but let me see if I can somehow help!

First, I have a question...

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">_mainmemory = +_mainlist

What is the + there for? Is it a typo, or do I just not understand its purpose?

If it's a typo, that may be your problem right there, since your operating array of units gets funky.

Couldn't you also combine the two fired eventhandlers?

For example,

Instead of this:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">_idx = {_x addEventHandler [{fired}, {[_this select 0,_main,_logic,_innertrigger,_type] exec "flyoff.sqs"}]} foreach _mainmemory

{_x addeventhandler ["fired", format [{(_this select 0) removeeventhandler [{fired}, %1]},_idx]]} foreach _mainmemory

maybe this?

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">_idx = {_x addEventHandler [{fired}, format {[_this select 0,_main,_logic,_innertrigger,_type] exec "flyoff.sqs"; (_this select 0) removeeventhandler [{fired}, %1]}, _idx]} foreach _mainmemory

Don't know if that'll mess it up, just a thought!

Lastly, remember to privatize all your local variables within the script. This has given me many inexplicable problems before, and sometimes it's just that simple.

Do this by listing all your local variables at the beginning of each script like so:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">private ["_var1", "var2", "var3"] etc....

Hope this helps you out, looking forward to seeing this script in action!

Share this post


Link to post
Share on other sites

@cornhelium

Well, I can tell you, why your script doesn't work... that's quite simple. The solution to your problem... is not very simple.

Why it doesn't work: You can't pass local variables of a script to an eventhandler, check the following code:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

_somevar = 0

_unit addEventHandler["FIRED",{ hint format["%1",_somevar] }]

When _unit fires, you'll get good old "scalar bool...".

Good luck with this information!

Share this post


Link to post
Share on other sites

As Vektorboson said, you cant use local variables in this case.

This isn't an easy solution, but one way to do it is something like this:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">CT_PARAMS=[]

CT_PARAMS=CT_PARAMS+[[_Main,_Logic,_InnerTrigger,_Type]]

_Index=(Count CT_PARAMS)-1

_ParamStr=Format ["{[_This Select 0,(CT_PARAMS Select %1) Select 0,(CT_PARAMS Select %1) Select 1,(CT_PARAMS Select %1) Select 2,(CT_PARAMS Select %1) Select 3,(CT_PARAMS Select %1) Select 4] Exec {flyoff.sqs}}",_Index]

_idx = {_x addEventHandler [{fired},_ParamStr} foreach _mainmemory

CT_PARAMS is global and defined before you execute the script. The problem you have here though is, the index position of the parameters in the array cant change while you have the fired event assign to the objects. Otherwise it would end up pointing to the wrong set of parameters. But every time you call the script you will increase the size of the global array, if your doing 10 to 20 times in a mission it might not be so bad. In fact it would make life easier if you just keep adding to the array.

P.S I used the variable name CT_PARAMS because it's easier for this example. Best pick a better name.

But any more and you might want to add some code to detect parameters you don't use anymore and overwrite them. For example once you have finished with one set of params you could set _Main (Select 0) to ObjNull, then run this to get a new index position:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">_Count=0

_Index=-1

{If (_Index==-1) Then {If (IsNull _x) Then {_Index=_Count} Else {_Count=_Count+1}}} ForEach CT_PARAMS

If (_Index==-1) Then {goto "NewIndex"}

CT_PARAMS Set [_Index,[_Main,_Logic,_InnerTrigger,_Type]]

goto "EndIndex"

#NewIndex

CT_PARAMS=CT_PARAMS+[[_Main,_Logic,_InnerTrigger,_Type]]

_Index=(Count CT_PARAMS)-1

#EndIndex

So in the end your script would start with something like:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">;Initialise local variables

_Count=0

_Index=-1

;Look for an empty position in the array

{If (_Index==-1) Then {If (IsNull (_x Select 0)) Then {_Index=_Count} Else {_Count=_Count+1}}} ForEach CT_PARAMS

;If we didn't find one then create a new position

If (_Index==-1) Then {goto "NewIndex"}

;We found one so update that

CT_PARAMS Set [_Index,[_Main,_Logic,_InnerTrigger,_Type]]

goto "EndIndex"

#NewIndex

;Add a new array element

CT_PARAMS=CT_PARAMS+[[_Main,_Logic,_InnerTrigger,_Type]]

;Get the new index

_Index=(Count CT_PARAMS)-1

;End the indexing routines

#EndIndex

;Create the event paramater

_ParamStr=Format ["{[_This Select 0,(CT_PARAMS Select %1) Select 0,(CT_PARAMS Select %1) Select 1,(CT_PARAMS Select %1) Select 2,(CT_PARAMS Select %1) Select 3,(CT_PARAMS Select %1) Select 4] Exec {flyoff.sqs}}",_Index]

;Assign the event

_idx = {_x addEventHandler [{fired},_ParamStr} foreach _mainmemory

If you do want to reuse the indexes in your global array, you will have to decide for yourself, when to reset them.

To remove the eventhandlers, you would need another global array linking the unit with it's associated event index.

Share this post


Link to post
Share on other sites

Here is a bit easier solution for your problem. There exist two functions that I wrote a while back, inspired by General Barron's original functions on OFPEC.

fSetProperty.sqf

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">private["_obj","_att","_val","_atts","_vals","_i","_j"];

_obj = _this select 0;

_att = _this select 1;

_val = _this select 2;

if([F_OBJ_PROP_OBJ] call fIsNull)then{F_OBJ_PROP_OBJ=[];};

if([F_OBJ_PROP_ATT] call fIsNull)then{F_OBJ_PROP_ATT=[];};

if([F_OBJ_PROP_VAL] call fIsNull)then{F_OBJ_PROP_VAL=[];};

if(_obj in F_OBJ_PROP_OBJ)then

{

_i = [F_OBJ_PROP_OBJ, _obj] call fArrayFind;

}else{

_i = count F_OBJ_PROP_OBJ;

F_OBJ_PROP_OBJ set [_i,_obj];

F_OBJ_PROP_ATT set [_i, []];

F_OBJ_PROP_VAL set [_i, []];

};

_atts = F_OBJ_PROP_ATT select _i;

_vals = F_OBJ_PROP_VAL select _i;

if(_att in _atts)then

{

_j = [_atts,_att] call fArrayFind;

}else{

_j = count _atts;

_atts set [_j,_att];

};

_vals set [_j, _val];

publicVariable "F_OBJ_PROP_OBJ";

publicVariable "F_OBJ_PROP_ATT";

publicVariable "F_OBJ_PROP_VAL";

true

fGetProperty.sqf

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">private["_obj","_att","_atts","_vals","_r","_i","_j"];

_obj = _this select 0;

_att = _this select 1;

_r = nil;

if( !([F_OBJ_PROP_OBJ] call fIsNull) &&

!([F_OBJ_PROP_ATT] call fIsNull) &&

!([F_OBJ_PROP_VAL] call fIsNull) &&

_obj in F_OBJ_PROP_OBJ) then

{

_i = [F_OBJ_PROP_OBJ, _obj] call fArrayFind;

_atts = F_OBJ_PROP_ATT select _i;

_vals = F_OBJ_PROP_VAL select _i;

if(_att in _atts)then

{

_j = [_atts,_att] call fArrayFind;

_r = _vals select _j;

};

};

_r

+ 2 functions required by these two

fIsNull.sqf

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">(_this select 0) == (_this select 0)

fArrayFind.sqf

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">private["_c","_i","_r","_v","_a"];

_a=_this select 0;

_v=_this select 1;

_r=-1;

if(_v in _a)then{

_i=0; _c=count _a;

while "_i<_c && _r==(-1)" do {if call format["{%1}=={%2}",_v,_a select _i]then{_r=_i;}else{_i=_i+1;}};

};

_r

Just copy-paste them into files, the code doesn't really matter here. Now you only need to initialize them in your init.sqs:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">fSetProperty = loadFile "fSetProperty.sqf"

fGetProperty = loadFile "fGetProperty.sqf"

fIsNull = loadFile "fIsNull.sqf"

fArrayFind = loadFile "fArrayFind.sqf"

With those two functions, you can store attributes to objects. Now you can change the following line:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">_idx = {_x addEventHandler ["fired", {[_this select 0,_main,_logic,_innertrigger,_type] exec "flyoff.sqs"}]} foreach _mainmemory

to

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">_idx = {[_x, "main", _main] call fSetProperty; [_x, "logic", _logic] call fSetProperty; [_x, "innertrigger", _innertrigger] call fSetProperty; [_x, "type", _type] call fSetProperty; _x addEventHandler ["fired", {_this exec "flyoff.sqs"}]} foreach _mainmemory

In flyoff.sqs you can use fGetProperty to retrieve the values stored in the attributes "main", "logic", "innertrigger" and "type" of each object. Note that each object can have individual values on these attributes.

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">_object = _this select 0

_main = [_object, "main"] call fGetProperty

_logic = [_object, "logic"] call fGetProperty

_innertrigger = [_object, "innertrigger"] call fGetProperty

_type = [_object, "type"] call fGetProperty

PS: If the properties are the same for all objects, you can just use a logic to store and retrieve the values.

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">[mylogic, "attribute", value] call fSetProperty

_attribute = [mylogic, "attribute"] call fGetProperty

Share this post


Link to post
Share on other sites

Wow, thanks guys.

I'm gonna chew all this over for a while - will report back soon smile_o.gif

Cheers,

CH

Share this post


Link to post
Share on other sites

Just a couple of questions before I go any further.

The ideal solution for me would be to add the EH in the trigger activation field, where I can use the global names of each individual element. Eg. this works fine in the activation field of one of the many _main triggers on the map:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

{_x addEventHandler ["fired",{[nightjar_hunt1_main, nightjar_hunt1_l, nightjar_hunt1_t,"nightjar_hunt"] exec "flyoff.sqs"}]} forEach thislist

But the problem is that there seems no reliable way to remove that EH, without screwing up other EHs that the units may be using.

I can disable the EH by switching a global variable in the trigger:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

nightjar_hunt1 = true; {_x addEventHandler ["fired",{if (nightjar_hunt1) then {[nightjar_hunt1_main, nightjar_hunt1_l, nightjar_hunt1_t,"nightjar_hunt"] exec "flyoff.sqs",nightjar_hunt1 = false}}]} forEach thislist

This gives the effect I'm after (that is, the flyoff.sqs can only be triggered once every x minutes), but the EHs are never removed and I worry about them piling up.

I'm aiming to have around 50 of these ambient loops on the map, each running from a single fauna.sqs and flyoff.sqs. Worse, fauna.sqs refreshes the unit list within the _main trigger on every loop, adding EHs to any new unit that has wandered into the trigger and again to units that were previously in the trigger. (Only for "active" sets. That is, sets where the player is within around 550m.)

So, the EHs could potentially pile up into the thousands over the course of a mission, even though they only actually do anything 1% of the time. Should I abandon this approach, or is it OK to keep adding "disabled" EHs?

I've also tried using the _main trigger field to assign that trigger's EH a global name, so it can be removed either in the trigger or via some looping cleanup.sqs:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

nightjar_hunt1_flyoff = {_x addEventHandler ["fired",{[nightjar_hunt1_main, nightjar_hunt1_l, nightjar_hunt1_t,"nightjar_hunt"] exec "flyoff.sqs"; (_this select 0) removeeventhandler ["fired", nightjar_hunt1_flyoff]}]} forEach thislist

This would be the perfect solution I think, only it doesn't work wink_o.gif . I get error 'Type Nothing, expected any' within the editor.

The problem here seems to be assigning a global ID to an EH within the trigger. Is it only possible to do this from a script?

Apologies if this is all a bit confused and amateurish, but hey, I'm a confused amateur wink_o.gif

Thanks,

CH

Share this post


Link to post
Share on other sites

Wrong approach. You can only assign a global ID directly to the addEventHandler command, but not to the whole code snippet.

Thus instead of

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">global_id = {_x addEventHandler [...]; ...} forEach ...

it should rather be

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">{global_id = _x addEventHandler [...]; ...} forEach ...

The obvious problem is that you need a different global variable for each item that you assign an event handler to. This would lead to many many global variables, which is not desirable IMO.

Now you could again use the above listed functions fSetProperty and fGetProperty, which would reduce the number of global variables to three arrays, which would be of the same size as the number of items an event handler was assigned to.

Result:

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">{[_x, "flyoff_event_handler", _x addEventHandler ["fired", {...; (_this select 0) removeEventHandler ["fired", [_this select 0, "flyoff_event_handler"] call fGetProperty]}] call fSetProperty} forEach thislist

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  

×