Jump to content
Sign in to follow this  
burdy

How to : Multiple scripts within single .sqf

Recommended Posts

Hello.

Currently I am trying to make a radio script. The player activates the song from the action menu and it plays within the helicopter. Currently the script has 3 parts :

Heli.sqf

//Defining Array
_House = False;
_Hill = False;
_Hunted = False;
_Rigby = False;
_Ramblin = False;
_HeyHey = False;
_Dress = False;
_Happy = False;
_Hunted = False;
_CCR = False;


//Helicopter Menu's Function
// addaction ["MENU NAME", "SQF LOC", "ARRAY"]

HeliRadio = 

{ _x addaction ["House of the Rising Sun", "True\_House.sqf", "", 0, false, true, "", ""] } foreach [h1,h2,h3,h4,h5,h6,h7,h8,h9,h10];
{ _x addaction ["Hill 137", "True\_Hill.sqf", "", 0, false, true, ""] } foreach [h1,h2,h3,h4,h5,h6,h7,h8,h9,h10];
{ _x addaction ["Hunted Down", "sound\_Hunted", "", 0, false, true, ""] } foreach [h1,h2,h3,h4,h5,h6,h7,h8,h9,h10];
};

to either hill or house.sqf (both consist of *name of array* = true)

to Radio.sqf

//Defines what Heli player music
// EXAMPLE : x = x [Who,What,How Far away you have to be]

_Music = nearestobjects [player,[h1,h2,h3,h4,h5,h6,h7,h8,h9],.5]; 


//Script run
///House of the Rising Sun
waitUntil {house;};
if (true) then {(vehicle player) say3d "house" foreach _Music;};
	while {house} do {
		if (house) then {hint "Radiowave 'House of the Rising Sun' taken"};
		sleep 248;
		hint "Radiowave 'House of the Rising Sun' open";
	house = false

};


};

///Hill 137

waitUntil {hill;};
if (true) then {(vehicle player) say3d "hill" foreach _Music;};
	while {hill} do {
		if (hill) then {hint "Radiowave 'Hill 137' taken"};
		sleep 248;
		hint "Radiowave 'Hill 137' open";
	hill = false

};

The problem is, when I run hill, it does not run. It presumably is waiting for "House" to trigger. So how do get around this (e.g going around the scripts in no order).

Also a few side questions-

1) Instead of writing all the h's out, how can I define the h's in a array and use them ? (E.g foreach _definedarray)

2) The conditions for the addaction isnt working well. How can I make the condition be met when the player is inside one of the defined helicopters?

Thanks!

Share this post


Link to post
Share on other sites

Make 3 separate scripts in 3 separate file

At the bottom of script 1 have a command: run script 2;

At the bottom of script 2 have a command: run script 3;

That way they would (probably) run in the correct sequence.

= = = = = = = = = = = = = = = = = = = = =

I believe there is a command something like: stop running for 20 seconds then continue

.

Share this post


Link to post
Share on other sites
Make 3 separate scripts in 3 separate file

At the bottom of script 1 have a command: run script 2;

At the bottom of script 2 have a command: run script 3;

That way they would (probably) run in the correct sequence.

= = = = = = = = = = = = = = = = = = = = =

I believe there is a command something like: stop running for 20 seconds then continue

.

Alright,

I went down the path of scrapping the 3rd set of script, and moving the say commands + sleep commands too the 2nd set of script. I also added the addactions too the INIT of the unit.

Everything seems to work good with that, now my latest dilemma is accidently running multiple songs at once. Would there be a way to hide the addaction commands after one is selected for the duration of the sleep command? Or would there be a way add a mute command that would end/mute the track?

Share this post


Link to post
Share on other sites

I actually replicated the required functionality in a test mission to most part when first considering answering this the other day but didn't complete/post it as I wasn't sure about how some of the functionality was intended (i.e should all helis play the tune together (in 3d, since you're using say3d) or would simply playing the sound to each player individually be acceptable (with you using say3d, possibly for the effect, I expected not); whether it's for multiplayer or singleplayer (I'd guess the former, in which case there should (probably?) be extra handling for syncing the availability of the actions/"playing-state" of the music)).

However, I decided to tinker the mission further and have made it accessible here (link). You will need to place tune1.ogg, tune2.ogg, tune3.ogg into the mus folder in there though (just copy & rename some ogg tunes you have; my upload is dial-up grade).

  • It features music loading in such a way that in case you want to add/remove music, you would only need to edit the description.ext and the mus folder's contents (ogg files). The former follows specific "wording" (you'll notice) to achieve it (the code in init.sqf and musicAction.sqf rely on that).
  • Similarly you can edit the mission (add/remove "h#"-named objects) without having to change the scripts/description.ext.
  • It uses a custom config-entry (myduration) to sleep exactly the length of the tune before enabling playing again.
  • It includes various approaches in the scripts (commented) - playing from single object vs from all objects; playing from one disables music playing options from all vs can be played from each independently.
  • By default it plays sound from all the objects (not actually using just helis there).

Also note that it isn't built with multiplayer in mind (doesn't handle anything related). The sleep-timers also don't compensate for time acceleration (the music doesn't automatically speed-up when using acceleration however sleep timers do and uisleep couldn't be used, since that would keep running when pausing the game, so the inputs/time acceleration status would need to be manually tracked/handled).

In any case, answering the original question - spawn or execVM might be (have been) what you're looking for? As for removing the tracks - removeAction or using the condition parameter of addAction would do that.

The provided example mission might give you some ideas/general guidelines though ;) (feel free to make direct use of it, should you feel like it)

Edited by geqqo
Added a point to the list (mission-editing independence)

Share this post


Link to post
Share on other sites
I actually replicated the required functionality in a test mission to most part when first considering answering this the other day but didn't complete/post it as I wasn't sure about how some of the functionality was intended (i.e should all helis play the tune together (in 3d, since you're using say3d) or would simply playing the sound to each player individually be acceptable (with you using say3d, possibly for the effect, I expected not); whether it's for multiplayer or singleplayer (I'd guess the former, in which case there should (probably?) be extra handling for syncing the availability of the actions/"playing-state" of the music)).

However, I decided to tinker the mission further and have made it accessible here (link). You will need to place tune1.ogg, tune2.ogg, tune3.ogg into the mus folder in there though (just copy & rename some ogg tunes you have; my upload is dial-up grade).

  • It features music loading in such a way that in case you want to add/remove music, you would only need to edit the description.ext and the mus folder's contents (ogg files). The former follows specific "wording" (you'll notice) to achieve it (the code in init.sqf and musicAction.sqf rely on that).
  • Similarly you can edit the mission (add/remove "h#"-named objects) without having to change the scripts/description.ext.
  • It uses a custom config-entry (myduration) to sleep exactly the length of the tune before enabling playing again.
  • It includes various approaches in the scripts (commented) - playing from single object vs from all objects; playing from one disables music playing options from all vs can be played from each independently.
  • By default it plays sound from all the objects (not actually using just helis there).

Also note that it isn't built with multiplayer in mind (doesn't handle anything related). The sleep-timers also don't compensate for time acceleration (the music doesn't automatically speed-up when using acceleration however sleep timers do and uisleep couldn't be used, since that would keep running when pausing the game, so the inputs/time acceleration status would need to be manually tracked/handled).

In any case, answering the original question - spawn or execVM might be (have been) what you're looking for? As for removing the tracks - removeAction or using the condition parameter of addAction would do that.

The provided example mission might give you some ideas/general guidelines though ;) (feel free to make direct use of it, should you feel like it)

Thanks for your help and guideline :D.

As im a very nooby coder.. How exactly would I make the condition hide the other add actions after I press one? And, how would I make a hint show to only one person? (Or is it already local).. As you can guess - this is all for MP

Share this post


Link to post
Share on other sites

If you look in the sample mission's init.sqf, line 31 (layed out here)

...
_heli addAction [
    (getArray (missionConfigFile >> "CfgSounds" >> _x >> "titles")) select 1,
    "musicAction.sqf",
    [_i,_x],
    0,
    false,
    true,
    "",
    "!musicActive"
];
...

- I've used a global variable in the last parameter of addAction (condition; it checks whether the provided expression (in a string) is true, if so the action is shown, if not, it's not). The variable is initially set false (so the actions are shown, because I negate the value in the condition string with the ! operator) and I set it true (in musicActions.sqf in the mission) when playing any sound, so they're automatically hidden (the condition is constantly checked by the game). Since I'm using the same single variable for all addAction condition checks, it disables/enables all of them at the same time.

As far as multiplayer goes, I'm afaid my level of expertise is low in that "realm" but essentially, you would probaly need to use publicVariable (or publicVariableServer and publicVariableClient) and possibly also addPublicVariableEventHandler (not mandatory as publicVariable sets the variable for all other players automatically). I.e. whoever activates the action does publicVariable for the variable that's checked in the condition (musicActive) and every player (and the server) has a addPublicVariableEventHandler that will check the new value and if applicable, sleeps for the tune's duration. You could also check the current vehicle of the client to see if the tune was played on their vehicle (if you intend to have it work per-heli instead) etc. The server could also support JIP (by synchronizing the currently left sleep time, if any, with JIP players) by counting the sleep down one second at a time in a loop something like that (to be run on the server only):

/* Assumes you have the tune's duration readily available in _tuneLength
* and that currentCountdown is an exisiting global (for access by other
* functions, e.g. those that handle JIP (not included here))
*/
0 = _tuneLength spawn {
    currentCountdown = _this;
    while{currentCountdown > 0}do{
         sleep 1;
         currentCountdown = currentCountdown - 1;
    };
    musicActive = true;//Presuming usage of a variable called musicActive as the global var for the action's condition check
    publicVariable musicActive;
};

hint is local (in the sense that if you call it on any client, it will only trigger for that client; but you still need to make sure you call the code only on the intended player's computer/players' computers). To have the message show to only the troops in the specific heli where the music was activated from, you could use the public variable event handler as mentioned above (by checking the vehicle of the client); however per heli would require using a somewhat different approach (cannot use a single bool (true/false) check), which in the sample mission is commented out (however the setVariable/getVariable used in the sample, will not do the trick alone*, if applied in multiplayer, since they appear to be local, so you would need to use a global array or a set of variables (globals) for each heli that would be publicVariable'd when appropriate).

* or ... it might since apparently publicVariable supports type "object", however I'm not sure if that applies to vehicles and if the vehicle's variable space would also be transferred.

But yeah, more experienced multiplayer scripters can probably tell with more certainty what the best approaches would be.

Edited by geqqo
Layed out the code-example from init.sqf for better readability; no more nil = ...

Share this post


Link to post
Share on other sites

Okay, I have came up with a way to achieve my goal, but have one small problem (end of post)

So, the script begins with this in the line of a vehicle I would like to have the radio option

this addaction ["House of the Rising Sun", "Variables\playhouse.sqf", "", 0, false, true, "", "driver  _target == _this"];  

The code then runs what is defined above (Variable\playhouse.sqf)

playhouse = 1;
publicVariable "playhouse";       // new playXXXX variable value sent to other non-local machines, to trigger eventhandler
[playhouse] execVM "True\House.sqf" //for client (activator) to play script.

Which is activated through the init

//House of the Rising Sun Public Variable
playhouse = 0;
"playhouse" addPublicVariableEventHandler {[_this select 1] execVM "True\House.sqf";};

and then finally activated through this (True\House.sqf)

if (_this select 0 == 1) then //_this select 1 is new playXXXX value. check to run only if value is 1
{
vehicle player say3d "house";


	while {true} do {
		if (true) then {hint "Radiowave 'House of the Rising Sun' taken"};
		sleep 248;
		if (true) exitwith {hint "Radiowave 'House of the Rising Sun' open"};
		playhouse = 0;

};
};

Now, everything works fine (MP everyone can hear the music, hints are local, etc etc) However, as the final script says "vehicle player" - the game takes it as play the music through every vehicle every player is in at the given moment. How can I limit it to just the helicopter the script was activated from?

So close to getting it all good :yay:

Share this post


Link to post
Share on other sites

Here's one way I can figure this could be done -

I'm thinking of making playhouse (and any other such music play-state variable) hold the particular heli's name instead of a number. Note, that any one tune could be played only from one heli at a time like this though (i.e. once the house tune is played from heli 1, it couldn't be played from other helis, however other tunes still could be, if you are still using multiple tunes).

If you're running the first code fragment that you posted in each vehicle's init, as it would seem, then you could go like this:

this addaction ["House of the Rising Sun", "Variables\playhouse.sqf", "h0", 0, false, true, "", "driver  _target == _this"];

Note the "h0" (arguments parameter) after the script parameter, that string will be available in playhouse.sqf as _this select 3. It stands for the heli object (use the particular heli's name in the init line of each; in this example I expect them to be h0,h1,...).

In playhouse.sqf change playhouse = 1; into playhouse = _this select 3;.

In init.sqf code, change the playhouse = 0; into playhouse = "";.

Finally, in House.sqf (I believe the previously present while do loop and the if(true) checks to be unnecessary, if you know otherwise, feel free to reintroduce them) -

_thisheli = (missionNamespace getVariable (_this select 0));
if (_thisheli == vehicle player) then
{
    _thisheli say3d "house";
    hint "Radiowave 'House of the Rising Sun' taken";
    sleep 248;
    hint "Radiowave 'House of the Rising Sun' open";
    playhouse = "";
};

References - getVariable, setVariable, missionNamespace.

Share this post


Link to post
Share on other sites

Hmmm.

Your route appears to have the music not being heard at all in a MP Environment (e.g only the one who runs the addaction can hear it). Ontop of that - the sleep command is ignored now, with the user able to run the song multiple times.

Also, how could I have the action removed from every clients screen and then brought back while the song is playing? I was thinking of adding "player removaction _actionname" after the hint but nothing happens.

Take note, however, that I am currently running the songs on a total of 30 possible helicopters, and there are a total of 10 possible songs that the player can select (all activated in the same way as "House") - so I am not sure If I would have to edit the file at all.

Edited by Burdy

Share this post


Link to post
Share on other sites

I intended to test this myself but I cannot seem to connect two clients to a dedicated server on the same computer (with kickduplicate=0; and reportingIP="<>";).

Anyway, does the rpt say anything? (if you have tested multiple clients on your computer, and thus have access to the rpt of the client that should play the sound but doesn't)

Also, how could I have the action removed from every clients screen and then brought back while the song is playing?

If you want to hide it also for the driver (as it currently seems to display only for the driver), you could check like this -

"playhouse != """" && driver _target == _this"

That is, if you're using the suggested code; or if you're using the number approach, then you would use playhouse == 0 && ...

As for showing again while the song is playing, you could just shorten the sleep. Or if you want to show it in case a condition is met, you could use a global variable that you set true at the appropriate time, for either all tracks or per each track (i.e. "houseavailable && driver _target == _this").

Share this post


Link to post
Share on other sites

Been testing MP with some buds and the music is dodgy at best. I believe if the track is played without another client in the vehicle with you , the other client will not be able to hear the track. Also, running your version of the script, the "sleep" command appears to be ignored (to an extent), as in the "Radiowave" hints go off as timed, but I can run the script as many times as I want (so I could in theory loop it x amount of times).

The most stable version of the script was the one I made a few posts up. Is there anyway to keep that completely intact while addressing the issue of vehicle player being ran on every vehicle a player is in (so essentially my original question) ?

Share this post


Link to post
Share on other sites
I believe if the track is played without another client in the vehicle with you , the other client will not be able to hear the track.

Ahhh. There's the key to solution (banging my head) :D

I specifically made it play only for people in the heli absolutely disregarding the possibility that you would want others (outside) as well to be able to hear it (which of course makes sense, with the say3d and all...).

So the solution is to just remove the if (_thisheli == vehicle player) check altogether in the proposed House.sqf (so it plays for all players, but only from the specific heli, where triggered from) -

(missionNamespace getVariable (_this select 0)) say3d "house";
hint "Radiowave 'House of the Rising Sun' taken";
sleep 248;
hint "Radiowave 'House of the Rising Sun' open";
playhouse = "";

Also, if you do not want to have the action removed from players for some reason but don't want the specific* track to trigger when chosen, then in playhouse.sqf (as per my solution), add an if check as follows -

*(disabling all would be done similarly but using a global variable like explained further below but applied to the context of this particular example; see the comment in the code)

if(playhouse == "")then{//if(!musicactive)then{
    playhouse = _this select 3;
    ...
};

Or...

If you do want to have the action (only the particular one) hide itself then the previously suggested "playhouse != """" && driver _target == _this" would still be the way to do it (with the appropriate variable for each case).

(else) If you want to have all the actions hidden in case of any tune played then a global variable would do the trick. E.g. -

Action's condition - "(!musicactive) && driver _target == _this"

To init.sqf - musicactive = false;

To House.sqf (and all other serving the same purpose) -

Before the sleep: musicactive = true;

After the sleep: musicactive = false;

What surprises me though, is that you could not repeatedly trigger the track with your solution (using 0/1 for the variable, etc.) since there really don't seem to be any measures against that it in the shown pieces of code.

(You may want to read the last sentence of this "chapter" before the contents ;) )

What's started to bug me, is a theoretical "edge-case" of someone being able to potentially execute the action after somebody else has already just done that, but the option was still available to the first party due to delay. If you only ever intend the music options to be available to the driver then that solves it in itself (since there's no "second party") but otherwise a "synchronization" with the server might be required (e.g. instead of publicVariable, the client would run publicVariableServer and then the server-end handler would check if the variable was already set and only run publicVariable if not (however it wouldn't be that straightforward, as publicVariableServer (like publicVariable) changes the variable directly, so you would need to keep a "backup" variable or approach a bit different and send a variable name/value-pair (an array) to a dedicated variable and so forth)). I may be overthinking this and maybe there's some counter-mechanism for that on the game's end, so you may disregard this unless you get cases of people actually doing something like that.

Edited by geqqo

Share this post


Link to post
Share on other sites

Seems music doesn't not play for any client now (minus those who run it). Dont understand whats going on

Share this post


Link to post
Share on other sites

What exactly do the related parts in the files look like now (for example for the house track again)? Also, nothing related in the rpt?

Share this post


Link to post
Share on other sites

Couldn't find anything in the RPT.

So the code goes from here (INIT of vehicle)

this addaction ["House of the Rising Sun", "Variables\playhouse.sqf","h1", 0, false, true, "", "driver _target == _this"];

To the INIT.sqf

//House of the Rising Sun Public Variable
playhouse = "";
"playhouse" addPublicVariableEventHandler {[_this select 1] execVM "True\House.sqf";}; //event handler ran on all machines to trigger when playXXXX is changed

to Variables/playhouse.sqf

playhouse =  _this select 3;
publicVariable "playhouse";       // new playXXXX variable value sent to other non-local machines, to trigger eventhandler
[playhouse] execVM "True\House.sqf" //for client (activator) to play script.

to True/House.sqf

(missionNamespace getVariable (_this select 0)) say3d "house"; 
hint "Radiowave 'House of the Rising Sun' taken"; 
sleep 248; 
hint "Radiowave 'House of the Rising Sun' open"; 
playhouse = "";  

Keep in mind there are multiple songs and vehicles - and for those its essentially the same process just different names.

Edited by Burdy

Share this post


Link to post
Share on other sites

The part in init.sqf is run for all (not enclosed in a isServer check or similar), right? Doubt, there's any problem with that, just to eliminate. Other than that I'm quite lost... as far as I can tell, there don't seem to be any reasons as to why this wouldn't work. So, perhaps you can include some debugging lines to visualize the state of things (especially with the other clients in mind (for whom it doesn't run)) and see where stuff goes off track (only for the single case of house just for testing; make backups of the originals, this functionality is just to trace the problem).

Debug setup -

init.sqf (will start to hintSilent the debug string every second and set it up to reset every time the EH triggers (so it can be repeatedly tested in a single game; the driver of h1 gets special handling, in that it resets when triggering the action); it'll also add an action that's avilable to all players that outputs the debugstring to the rpt for easier copying of the data (copyToClipboard is said to be unavailable on multiplayer)):

//House of the Rising Sun Public Variable
debugstring = "";
playhouse = "";
"playhouse" addPublicVariableEventHandler {//event handler ran on all machines to trigger when playXXXX is changed
    debugstring = format["pvEH _this:\n%1\n\npvEH playhouse:\n%2",_this,playhouse];
    [_this select 1] execVM "True\House.sqf";
};
0 = 0 spawn {
    player addAction ["debugstring to RPT","debug.sqf",nil,1.5,false,true,"","_this==_target"];
    while{true}do{hintSilent debugstring; sleep 1;};
};

debug.sqf:

diag_log text debugstring;

Variables/playhouse.sqf:

if(player == driver h1)then{debugstring = ""};
debugstring = (debugstring + format["\n\nactionscript _this/3:\n%1\n\nactionscript playhouse 1:\n%2",_this select 3,playhouse]);
playhouse =  _this select 3;
debugstring = (debugstring + format["\n\nactionscript playhouse 2:\n%1",playhouse]);
publicVariable "playhouse"; // new playXXXX variable value sent to other non-local machines, to trigger eventhandler
[playhouse] execVM "True\House.sqf"; //for client (activator) to play script.
debugstring = (debugstring + "\n\nactionscript OK");

True/House.sqf:

debugstring = (debugstring + format["\n\nsay3dscript-exvm _this:\n%1\n\nsay3dscript-exvm getV _this/0:\n%2\n\nsay3dscript-exvm h1:\n%3",_this,missionNamespace getVariable (_this select 0),if(isNil "h1")then{"nil"}else{h1}]);
(missionNamespace getVariable (_this select 0)) say3d "house";
debugstring = (debugstring + "\n\nsay3dscript (execVM) say3d OK");
hint "Radiowave 'House of the Rising Sun' taken";
sleep 248;
hint "Radiowave 'House of the Rising Sun' open";
playhouse = "";

I expect the testing to be done on the heli that's h1. If you can't make anything critical out of it, a screenshot of or text copy of the contents of the hint a few seconds after the publicVariable has commenced (if it does at all) on the client for whom the sound isn't working, might help.

Edited by geqqo
Typo (not in a script) .. and in script (never assign anything to nil ...)

Share this post


Link to post
Share on other sites

When I run the mission I get "WARNING : NIL VARIABLE OVERRIDDEN : RESTART MISSION OR CHECK ADDONS" and my game freezes up.

Share this post


Link to post
Share on other sites

Wow... must be the nil = 0 spawn { that I've been using in editor for testing (it doesn't like any value returned so I've been returning into nil but I guess it doesn't work like void in C or similar (got to fix some codes on my end now that I'm aware of this; never encountered this warning before, curiously, probably due to my singleplayer background)). Just replace it with 0 = 0 spawn{ .... Sorry about that :|

Share this post


Link to post
Share on other sites

It may have been because Nil is one of the commands. You would want to avoid using any command (i.e., like "time") as a variable because if the command occurs later it can affect the variable.

https://community.bistudio.com/wiki/nil

EDIT: Or, apparently, affect the command.

Edited by OpusFmSPol
Just read the note at the bottom of the linked page.... didn't notice when I first pulled the page.

Share this post


Link to post
Share on other sites

Well, yes, it will start acting as a global variable, once assigned. While naturally any keywords/commands shouldn't be used as variable names, I just expected for some reason for it to have special handling as a "data sink" (can't quite tell where I picked this up). Quite coincidentally, I had just recently started using nil to undefine variables and started getting "anomalous" results that I hadn't yet looked into very specifically, so when Burdy mentioned the warning, the problem became clear.

Share this post


Link to post
Share on other sites
Well, yes, it will start acting as a global variable, once assigned. While naturally any keywords/commands shouldn't be used as variable names, I just expected for some reason for it to have special handling as a "data sink" (can't quite tell where I picked this up). Quite coincidentally, I had just recently started using nil to undefine variables and started getting "anomalous" results that I hadn't yet looked into very specifically, so when Burdy mentioned the warning, the problem became clear.

Alright Got it working. Turns out on the INIT that some commands were before the Music variables and the INIT didn't like that.. Anyway, thanks a ton for everything!

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  

×