View Full Version : [EDITING] AddAction tutorial

29-12-2011, 11:04 PM
I'm still pretty new at scripting but for my latest mission I decided to get a bit fancy with AddAction. Since the wiki is pretty easily understood I'm just going to show you a quick and dirty example.

What it does
AddAction adds a new player action (ex. Get In on a car object) to an object in the game world, where you script what should happen when the action is run. An example might be, a laptop with an option to call for backup.

Wiki info

To begin with you need an object to add the action to and a player to call the action. In the action-object's initialization add

this addAction["Call for backup", "callBackup.sqf", nil, 6, True, True, "", "(_target distance _this) < 4"]
This adds an action to the object with the text Remove Object, calls removeObject.sqf when activated and is only shown when the player is within 4 meters of the object (last paramater). I'm skipping the other parameters for now but please, read about them in the wiki linked above.

Next, you need your backup script that actually does the grunt work.
Create a file called callBackup.sqf in your mission folder and open it. In it add

_radarObject = _this select 0;
_activatingPlayer = _this select 1;
_actionId = _this select 2;
The first two rows just gets the object with the action on it and the player using the action. The third row is the ID of the action on the object which we can use to manipulate the action. We will use this next.

At the end of the file add

hint Format["%1 has called for backup on %2", _activatingPlayer, _radarObject];
_radarObject removeAction _actionId;

This will hint out the name of the player calling the script and on what object he/she is calling it (if you have not given these objects names they will be given default ones by the system).
Lastly we use the ID of the action to remove it from the object, so that the player can't call for backup again.

A VERY important notice is that the action is only run on the client that does the action. So whatever you do in your script, if you want it to be done on all clients then you must handle this manually.

Differentiate player types
If you also want the action to only be visible to a certain player, change the addAction call in the object's initialization to

this addAction["Call for backup", "callBackup.sqf", nil, 6, True, True, "", "(_target distance _this) < 4 && _this == ThePlayer"]

Now only the player with the soldier name ThePlayer will be able to see the action.

Good luck!

29-12-2011, 11:51 PM
Let's assume you want to have any player who joins the mission be able to use an object to do something. That something should be doable only once, and will send a bit of information to all clients on the server. That information should also synch with clients who have joined the server after the action has been performed.

I'm assuming that the variable in question is a public variable (available to all clients instead of just the server or calling client), which is named PV_MyToggle and defaults to 0. When the action is performed, it should equal 1, then after a short delay equal 2. This might be useful for updating briefings/tasks or other clientside effects.

The example works on two objects called ActionBarrel and FakeBarrel. These can be pretty much any object you want from the editor. I used empty barrels, hence the names.

In your init.sqf place the following:

if (isNil "PV_MyToggle") then { //if the PublicVariable does not yet exist, then we'll create a local copy. But if it DOES, we won't.
PV_MyToggle = 0;
if (isServer) then {publicVariable "PV_MyToggle";}; // make the server propogate the value to all clients, clients should keep local copies until the propogation happens.

//player SideChat format ["PV_MyToggle = %1", PV_MyToggle]; // Debug, should return 0 during briefing *unless JIP*
//player SideChat format ["PV_Whatever = %1", PV_Whatever]; // Debug, should return ANY during briefing

// Requires: ActionBarrel (object to have action), FakeBarrel (object to replace Object after removal (edit toggle.sqf if undesirable)
if (isNil "myAct") then { //AddAction is local, therefore connecting clients should be the only ones to get the action but let's be paranoid
if (PV_MyToggle==0) then { // If action has not already been performed, then clients get the action added on connect
Fer_Delay=10; //delay between states
myAct = ActionBarrel addAction ["Send two states with delay between them", "toggle.sqf", [Fer_Delay,FakeBarrel,"PV_MyToggle"]]; // if using Norrin's revive, this should be called when player is revived too

In the same directory, create toggle.sqf:

private ["_obj","_caller","_id","_params","_FerDelay","_OtherObj","_PubVar"];

_obj = _this select 0; // E.g. ActionBarrel
_caller = _this select 1; // E.g. Player
_id = _this select 2; // ID of action
_params = _this select 3; // Params passed to this script.

_FerDelay = _params select 0; // Delay (seconds)
_OtherObj = _params select 1; // Dummy object to replace this object (object)
_PubVar = _params select 2; // name of Public Variable to update (string)

//_caller SideChat format ["PV_MyToggle = %1", PV_MyToggle]; // Debug (PV_MyToggle is directly referenced PublicVariable)
//_caller SideChat format ["Public Variable is called: %1", _PubVar]; // Debug (PV_MyToggle is directly referenced PublicVariable)

_OtherObj setPos (getPos _obj); //Replace object with dummy (so if player JIPs, they don't get to fire the event again; also prevents action from being called mulitple times whilst the wait is running)
_obj setPos [-200,-200]; //Move object to nether regions
_obj removeAction _id; //Remove the action from this client once it is activated (not useful; will only remove action from this client and we're moving the object away anyway)

call compile format ["%1 = 1;",_PubVar]; //Set value
publicVariable _PubVar; //Broadcast to all clients
//_caller SideChat format ["PV_MyToggle = %1", PV_MyToggle]; // Debug (PV_MyToggle is directly referenced PublicVariable)

sleep _FerDelay; //Wait

call compile format ["%1 = 2;",_PubVar]; //Set second value
publicVariable _PubVar; //Broadcast to all clients
//_caller SideChat format ["PV_MyToggle = %1", PV_MyToggle]; // Debug (PV_MyToggle is directly referenced PublicVariable)

Now, when a player gets within about 5 m of the object, they'll have the option to perform an action. When they do, the action will disappear, the object swapped with another object (pref. of the same type so they don't notice), and the script will update the public variables on all clients as previously outlined.

There's another way to do this that doesn't involve init.sqf and lets you specify how close the player must be. Create a trigger (note that as Fer_Delay was only defined in the init.sqf I have replaced it with a value here):

Condition: (player distance ActionBarrel ) <= 1.5
On Activation: myAct= ActionBarrel addAction ["Send two states with delay between them", "toggle.sqf", [10,FakeBarrel,"PV_MyToggle"]];
On Deactivation: ActionBarrel removeAction myAct;

Now the action only appears when the player is within 1.5 meters of the object instead of the usual 5 m. When the player is too far away, the action disappears again.

The disadvantage of not using the init.sqf approach is that there is no default value at the start of the mission; until the publicVariable "PV_MyToggle" is called by a script clients will return ANY instead of 0. This might be fine for your mission, if not then simply add the first four lines (first IF statement) of the init.sqf example to your own init.sqf.