Advanced Triggering: Action & Function Definitions
So you've got a decent grasp of events, conditions, and actions. You're feeling pretty good about your triggering skills. But then you hear of a mystical object called an "Action Definition", and curiously look up this tutorial. Action defenitions will save you a lot of time (and effort!) once you learn them, and as you get better, they will completely change the way you build triggers. Let's go ahead and get started...
What is an action definition?
So what is an action definition? An action definition is when define (aka create) an action. Blizzard has already provided us with tons of actions- things like set variable, create unit, create dialog, or save bank are all pre-made actions. But if you create your own action, you can use it in all your triggers, just like any built-in action.
So how does that help me?
Let's take the map RuneCraft (you know, the really amazing one that you should totally go and play). To start up our AI, we use about 86 lines of triggers. Imagine if we had to use all 86 lines of that every time we wanted to turn the AI on or off for a certain player. Instead, we created an action that does it for us. Now, we can just turn on or off the AI with 1 line. You can also add parameters to your action, just like the built-in actions. This gives you a lot of flexibility and control over how you structure your triggers. I'll give you some actual examples later in the tutorial.
Alright, that sounds cool. But how do I make one?
Action definitions are very easy to make- just right click in the trigger list on the left side of the editor, and choose new >> action definition. For you editor pros, they hotkey is control + alt + R. This will bring up a panel on the right that looks remarkably similar to the trigger panel! The only difference is the presence of 6 little sections at the top.
So what do these little added areas at the top do? Well...
The Options section will let you set options for your actions. You don't really need to worry about this for the moment.
The Return Type section is used when you want to return something. Personally, I don't use return values in my actions. I use function detentions when I want to return something. In other words, ignore this for now.
The Parameters section is something you do need to use. What is a parameter, though? Parameters are those values that you insert into actions when you use them. For example, the action 'set variable' takes 2 parameters, First, it takes the variable you want to set. Second, it takes the value you want to set it to. These 2 values that you can set are called Parameters. In your actions, you can create your own parameters that can be set when you call (aka use) this action.
The Grammar Text section is used if you want to change the text displayed when you put this action into your triggers later. It's purely cosmetic and won't change the way your action works.
The Hint Text section allows you to give a 'hint' to the person using the action. This is helpful when you have hundreds of actions and you want to remember what one does. You should put a brief description of what the action does in here.
The Custom Script Code is another one of those sections you can just ignore; it allows you to use actual Galaxy script, but we don't want to do that, because we're triggering pros.
So really, parameters are the thing you need to worry about most right now. At the bottom of the list, you will see the familiar actions section. This should give you a warm and fuzzy feeling inside, because you use this exactly the same way as any other trigger. Just click 'new action' and proceed normally.
Okay, I get the idea. Let's actually do something.
Alright- now you should (hopefully) realize that these things aren't so scary after all. We're going to build an action that would be useful to us in a practical way. You can do some really complex things with actions, but we're going to start nice and slow. I'm going to use these to create an admin system for a map. I'm going to assume that if you are reading this, you understand variables, arrays, and loops already. If you don't, then this is not the tutorial for you
First things first, we're going to need some variables. Create the following 3 variables.
Name: Admin Keys. Type: String. Array size 15.
Name: Keys Created. Type: Integer. No Array.
Name: Is Admin. Type: Boolean. Array size 15.
Now go ahead and do what you've been itching to do- create a new action definition, and name it 'add key'.
Create a new parameter for this action by right clicking on parameter, and choosing new parameter
Name this parameter 'handle', and make it a string type
Notice that are set up very similarly to local variables
Create an action in the actions section, and use the 'modify variable' action. Set the variable to 'keys created' and set the number to +1.
Add another action, this time "set variable".
Make the variable 'admin keys', and set the array slot to the 'keys created' variable.
Here's the moment of truth: Set the value of this variable to your handle parameter! How do I do this, you ask? First, select the parameter menu in the window that pops up. Then just select your parameter, like you would with a variable, and hit ok.
Before you continue, take a look at my action dentition to make sure it looks the same as yours. Everything ok? Alright, then move on.
Now we're going to make the most important part of the system- the checking action. This one's going to a bit more complicated, but it's essentially more of the same.
Make another new Action def, name it 'check for admins'. This time we don't need any parameters, but we will need 3 local variables.
Make to integer variables called x and p. Make a string variable called handle.
Got those variables set up? Excellent. We're going to need 2 nested loops (aka 1 loop inside another loop).
Add a for each player loop, and set the loop variable to p.
Add a 'set variable' inside that loop, and set the variable h to the handle of player p.
Add a for each integer loop right after that set variable action, (still inside the original loop). Set the loop variable to x, and have it loop from 1 to 'keys created'. (that variable we had earlier).
Now add an if/then/else inside that loop.
Add the condition "if h = admin keys[x]"
Under "then", add the action set variable. Set IsAdmin[p] to true.
And ta-da! That's your next action, ready to go. Like before, check your action to make sure it's the same as mine.
Like I said earlier, I'm assuming you understand a good amount of triggering, so the actual code here should not be confusing, and I'm not going to explain the actual system in great detail. But basically we're just checking through each player, and then checking each admin handle to see if the player's handle matches an admin handle. But this isn't really about the admin system, it's more about the action definitions- so onward! We're oh so close!
Make a new trigger (yup, that's a regular old trigger, not an action def). Call it setup handles.
Right click and pick new action, like you always would. Next, choose "add key" from the list. That's right, your very own action is now inside your trigger!
If you recall, we had this add key action require 1 paramater- the player's handle. In this case, we're going to add in the player handle of the player we want to make an admin. We can use this action over and over again to add more than admin to our map (although since our keys variable is only array size 15, we would only be able to have 15 admins). Once you've added all the admins you want to add, just use the action "check admin handles". Thats it. You don't need t look at the ugly behind-the-scenes code ever again. You can now easily add, remove, or change admins by only messing with 1 little action.
Our final, super easy to use trigger:
This admin system was only a little example of how to use action defs with parameters. You can do some incredibly complex things with actions. For example, in Runecraft, our entire tech tree UI is created via action definitions. It automatically sets up all of our variables, all the upgrades, all the costs, buttons, icons, images, and tooltips. All we do is tell the engine where we'd like each button and what we want in it. If we coded each button manually, not only would that be something like 400 lines of code, but it would be nearly impossible for us to go back and change them. We've got another action detention that handles every single upgrade in every single button- meaning the entire purchasing system consists of 1 trigger, with 1 action. I hope you are as excited as I was when I first learned about these wonderful little things :P
You're not just the average shmoe- you are a pro mapper. And you want to go even further and learn what a function def is. While I'm not going to go into nearly as much detail, I will give a quick explanation of how to use functions.
What's a function?
A function is something you can plug into a parameter that 'returns' a value. For example, triggering player, player from player group, and attacking unit are all functions. They go and "get" a value for us, and then stick that into the trigger where we put them. For example, when we use the triggering player function, it runs some code to find the triggering player, and then "spits out" their number for us.
What's different about function defs in how I make them?
Functions need to return a value. This is the value that is put into the parameter we used the function in. For example, triggering unit would 'return' a specific unit. There's a handy action called "return" that you can use to return these values. Oh, and did I say that's an action you can use? I meant you must use. That's right. A function MUST return something 100% of the time. If it's at all possible for your function to not return a value, your map will prevent you from saving. Let's say you have this function (shown below).
If you were the poor soul who created this trigger, and you tried to save your map, you would be greeted with a nice popup that says "expected a return value". See, if the player in this trigger was 1, then the function would not return. And in the programming world, that's a big no-no. So how does one prevent that? When a function returns, it's over. No lines after that return are executed. So if your function gets to end, you know that something is wrong. Just put a return line at the very end, and have it return -1 or some other value. That means that your function will indeed return something no matter what.
So how can functions help me?
Functions can get values. First of all, you can set the return type in the top of the function (as discussed way back at the top). That means that you can have this function return anything you feel like. In runecraft, we have a function that goes and gets a player's rank. It's quite a large function, since we need to do a lot of calculations. But now we can just pop in the function to any parameter, and it will go find the player's rank for us.
Thanks for fighting your way through that tutorial. I know it was probably miserable to read, but the knowledge you now posses (well, hopefully posses) will seriously help you on your mapping adventure. As always, feel 100% free to Send me a PM if you have any questions, comments, or feedback. I'm always happy to help.
Hopefully this tutorial has helped you, and if it hasn't, no, you may not have that hour of your life back. It's mine now. You might want to check out this tutorial if you are interested in learning more.