Action assigned ACS scripts
-
Please make sure to read Modding prerequisites page in order to use new ACS properties listed below
-
Also please make sure to read How to make your mod feel lagless in multiplayer on how Q-Zandronum compensates network latency
In Q-Zandronum it is possible to execute ACS scripts by pressing general action buttons like Move forward
, Jump
or Fire
. These scripts will be executed every tic as long as the action button is pressed.
To do so you first need to create a script. We will be creating kind of a jetpack that thrusts the player upward if he holds the jump key:
Script "Example_Jetpack" (int predicting, int justPressed, int justReleased, int buttons)
{
// The script also executes when the button was released.
// For this example, we don't want to do anything when the button is not pressed.
if (!justReleased)
{
// Thrust the player upward
ThrustThingZ(0, 10, 0, 1);
}
}
Then, we need to assign that script to the jump
action:
In Decorate:
Player.ActionScript "jump" "Example_Jetpack"
Or in ACS:
// SetActionScript(int tid, int action_button, str script_name)
SetActionScript(0, BT_JUMP, "Example_Jetpack");
And that's it!
All possible actions are below:
Decorate | ACS | Description |
---|---|---|
"attack" | BT_ATTACK | Script will execute when the player holds the attack button |
"altattack" | BT_ALTATTACK | Script will execute when the player holds the alt attack button |
"reload" | BT_RELOAD | Script will execute when the player holds the reload button |
"use" | BT_USE | Script will execute when the player holds the use button |
"speed" | BT_SPEED | Script will execute when the player is walking. In Q-Zandronum speed is always considered pressed when the player is walking and unpressed when the player is running, no matter whether he has cl_run ON or OFF and whether he presses the run button or not. |
"jump" | BT_JUMP | Script will execute when the player holds the jump button |
"crouch" | BT_CROUCH | Script will execute when the player holds the crouch button |
"forward" | BT_FORWARD | Script will execute when the player holds the move forward button |
"back" | BT_BACK | Script will execute when the player holds the move backward button |
"moveleft" | BT_MOVELEFT | Script will execute when the player holds the strafe left button |
"moveright" | BT_MOVERIFHT | Script will execute when the player holds the strafe right button |
"moveup" | BT_MOVEUP | Script will execute when the player holds the move up button |
"movedown" | BT_MOVEDOWN | Script will execute when the player holds the move down button |
"strafe" | BT_STRAFE | Script will execute when the player holds the strafe button |
"turn180" | BT_TURN180 | Script will execute when the player holds the turn 180 button |
"zoom" | BT_ZOOM | Script will execute when the player holds the zoom button |
"left" | BT_LEFT | Script will execute when the player holds the look left button |
"right" | BT_RIGHT | Script will execute when the player holds the look right button |
"lookup" | BT_LOOKUP | Script will execute when the player holds the look up button |
"lookdown" | BT_LOOKDOWN | Script will execute when the player holds the look down button |
"showscores" | BT_SHOWSCORES | Script will execute when the player holds the show scores button |
"user1" | BT_USER1 | Script will execute when the player holds the user1 button |
"user2" | BT_USER2 | Script will execute when the player holds the user2 button |
"user3" | BT_USER3 | Script will execute when the player holds the user3 button |
"user4" | BT_USER4 | Script will execute when the player holds the user4 button |
"always" | 0 | Script will execute all the time, no matter what buttons the player presses |
-
NOTE: Make sure to not mark the action script as
NET
orCLIENTSIDE
, cause that will break prediction. - NOTE: Keep in mind that action scripts are executed AFTER the engine calculated player movement.
-
NOTE: Keep in mind that action scripts are executed in the same order as above, aka
attack
, thenaltattack
, thenreload
and so on.
The main reason the above exists is to give modders a way to create unlagged custom movement, i.e. movement that doesn't depend on player's ping. You might have noticed the (int predicting, int justPressed, int justReleased, int buttons)
parameters in the script example. We'll get to that later, but first we need to explain how does the engine compensate ping for built-in movement.
Zandronum (and Q-Zandronum) has a feature called client prediction
. Please make sure to read How to make your mod feel lagless in multiplayer about that.
Client predictable action assigned ACS scripts bring that to ACS. Now, let's say that we want our jetpack to consume fuel and, if the fuel is out, it will not work.
First, we want to refill our jetpack, for example, by picking up an item, and the jetpack can work for up to 5 seconds, e.g. for 175 tics. And we also want the engine to remember the fuel amount and tell us how much fuel is left during client prediction.
We will need these two functions:
SetPredictableValue(int tid, int val_index, int value)
- set a value for client prediction
GetPredictableValue(int tid, int val_index)
- read the client predictable value
In other words, you set a value via the SetPredictableValue
and the engine will remember the value for that specific tick. Then, during actual prediction, the GetPredictableValue
will return the value you set before for that tick
So we need to execute this function on item pickup:
// This will tell the engine to write 175 into predictable variable 1 for the activator (tid 0)
SetPredictableValue(0, 1, 175);
-
NOTE: You can only store up to 20 predictable values, aka
0 <= value index <= 19
Now we need to update our jetpack script to use our fuel. The engine also tells us whether it is currently predicting or not. When it is predicting we only need to thrust the player, otherwise we need to thrust and lower fuel amount:
Script "Example_Jetpack" (int predicting, int justPressed, int justReleased, int buttons)
{
// The script also executes when the button was released.
// For this example, we don't want to do anything when the button is not pressed.
if (value1 > 0 && !justReleased)
{
// Thrust the player upward
ThrustThingZ(0, 10, 0, 1);
// "predicting" variable is 0 when not predicting and 1 when predicting
if (predicting == 0)
{
// Read the amount of fuel we currently have
int fuel = GetPredictableValue(0, 1);
// Reduce the fuel amount by 1 and write it back
SetPredictableValue(0, 1, fuel - 1);
}
}
}
And now you have a jetpack script that doesn't lag and doesn't cause jitter in multiplayer!
Avoid using the delay()
function inside the predictable part of the script. The reason is that when you use the delay()
function, the engine will pause it and put it in queue for future ticks. This will take the script out of client prediction.