Making configurable actors in DECORATE
Guide written by inkoalawetrust, adapted for the Zandronum wiki from my original ZDoom wiki guide
So, let's say you want to make an actor such as a monster, but you want to be able to expose certain aspects of it to the level editor, so you or other mappers can change parts of the actor without creating multiple versions of it, how could you do this ? Well, to make an actor that can interface with the level editor in DECORATE, you need to use custom arguments.
Getting started
Every actor has an args[] field, this field is normally used to pass parameters to an action special given to the actor, similarly to how an action special is given to a linedef, like shown below:
However, in DECORATE, you can read the integer values passed to the args[] array, which allows you to use the array to make action functions that support expressions such as A_JumpIf only execute if one of the numbers/integers passed to a specific "part" (Member) of the args[] array equals, or is more or less than a given number, to use an easier to understand example, try making an actor that calls A_JumpIf, but only if args[0] equals 123:
A_JumpIf (args[0] == 123, "CoolStateName")
Or do the same, but make the state jump only occur if args[1] is less than 123:
A_JumpIf (args[1] < 123, "CoolStateName")
Before we continue, keep in mind that args[] is an array as was mentioned before, if you don't know what that is, then don't worry too much about it, just know that it's a container that stores multiple variables/values, and each variable/value in this container is called a "member".
Using the args[] array to make a configurable actor
Now that you have a basic understanding of how the args[] array works on an actor and how to access it, we can use it to make something like this custom Imp, whose angle is determined by the value given in its args[0] field, and will immediately die if the value set on its args[1] field is above 0. Try writing this yourself and messing with the lines that rely on the args[] array in different ways to get more comfortable with using custom arguments:
Actor CustomImp : DoomImp Replaces DoomImp { States { Spawn: TNT1 A 0 TNT1 A 0 A_SetAngle (args[0]) TNT1 A 0 A_JumpIf (args[1] > 0,"GetKilled") TROO AB 10}} A_Look Loop GetKilled: TNT1 A 0 A_Die } }
Now you can run this code, and if you wrote it correctly, we can now place this Imp replacement on a map, open the things' Edit Thing window, go to the Action/Tag/Misc tab and mess with the actors' custom arguments to get results, I'll give the first input box a value of 90, and add a value to the second box.
When we start up Zandronum, the Imp should be facing at an angle of 90 degrees and immediately die.
Integrating custom arguments to the editor
Now that you know how to use custom arguments to make configurable actors, you may have noticed something, the argument fields are totally unnamed and unmarked, so every time you use the custom arguments of a configurable actor (e.g. a rain spawner), you have to figure out where each custom argument is which out of the 5 available argument boxes. Yet, built-in ZDoom actors like dynamic lights, which also use custom arguments themselves, have each of their argument fields named in the editor, is there a way you could do that too ?
Yes there is, to give names to your custom arguments and even tooltips and drop down menus, you can use editor keys to further integrate your custom actor to the editor, and make using it more user friendly.
Note
Editor keys are placed in the same location actor properties and flags go to.
Naming your custom arguments
First things first, we need to be able to give a name to our custom arguments, to make it easier for people to use the actor, to name an argument, you can use the //$ArgN editor key, where N is the number of your argument field. So for example, say you've got a cowardly Pinky that uses its' arg[4] field to turn the Frightened flag on if the fields' value isn't 0. So to give that field a name, we would type the //$ArgN key like this on the actors' definition:
//$Arg4 Toggle cowardice
Adding tooltips to your custom arguments
Now that our flag switching argument is named, you may have noticed that it's not too clear what it does to people who haven't read or written the actors' code like you have, so to explain to people what the argument does, without making them flip through external documentation files. You can use the //$ArgNTooltip editor key to create tooltips. Which will create a tooltip that underlines the argument name and makes it blue, the tooltip will appear when you hover over the arguments' name.
//$Arg4Tooltip When turned on, makes the Pinky run away from its imminent demise.
But maybe your tooltip is a bit too long, and you want to break the tooltip box to multiple lines, to do that, you can type \n anywhere in the tooltips' description to break the text that comes after into a new line, like shown below, where the tooltip text from the above example is broken into two almost equally long lines.
//$Arg4Tooltip When turned on, makes the Pinky\nrun away from its imminent demise.
After adding these two editor keys for the custom flag changing argument, it should look like this in the editor:
Before we begin, to be able to use drop down menus for custom arguments, you'll first have to specify that the argument should be an enum and not just an integer/number, to do that you need to type a new line with the //$ArgNType key, then add the number 11 to it afterwards to specify that it's an enum, a list of all possible argument types that can be assigned by the //$ArgNType key can be found here.
Note: Enums are in essence a way to assign names to numbers/integers.
Since the Pinky's argument only turns a flag on or off, it only really needs to be a True or False switch, not a number input box. Thankfully, you can also use and make (Described below) drop down menus for your arguments, to use a drop down menu for an argument that is effectively just an on (1) or off (0) switch like the Pinky example, you can make use of the //$ArgNEnum editor key. A list of enums built-in to UDB can be found here.
With this basic info on what an enum is, how to use them, and what enums are built into Ultimate Doom Builder. You can use the //$ArgNEnum key, followed by the name of one of the built-in enums, in this case we'll pick the True/False switch enum, which is called falsetrue, so now your two keys should look like this:
//$Arg4Type 11 //$Arg4Enum falsetrue
And now the Pinkys' Cowardice toggle argument should start using a drop down menu that only has a True or False switch. Instead of a number input box.
While the built-in Boolean enum types are useful, and may well be all that's needed in most of your own use cases, you may eventually want to make your own enums, that have their own numbers/integers and corresponding names. To make a new enum from scratch, you need to use the //$ArgNEnum key like above, but instead of following the key with the name of pre-existing enum. You add { } brackets, and inside them use the following syntax:
//$Arg4Enum {number = "name"; number = "name"; number = "name";}
Where number is, well, a number that is then checked for by your code, and name is the name of that number, that will appear in the drop down menu. Every entry should be ended by a semicolon (;). And the entire enum block should be in the same line, doing this will not work and trigger startup errors:
//$Arg4Enum { 1 = "Option1"; 2 = "Option2"; 3 = "Option3"; }
Now, here is a real world example of a custom Cacodemon that uses a custom enum drop down menu to change its' behavior based on the exact named number value picked from the menu.
//$Arg0 Attack modes //$Arg0Tooltip "Toggles between the Cacodemons' different AI modes:\nStandard: Normal Cacodemon behaviour\nEvasive: Attempts to avoid being hit by its' target\nEvasive & Aggresive: Avoids being hit and attacks more fiercely" //$Arg0Type 11 //$Arg0Enum {0 = "Standard"; 1 = "Evasive"; 2 = "Evasive & Aggressive";}
Full example
Below is a link to a full, real world example of a custom Cacodemon that uses most of the information covered in this guide, it has 3 toggleable AI modes that change its' behavior through a custom enum, a customizable field of view, which also uses a user variable, and both custom arguments have names and tooltips explaining what they do included. It is also compatible with Zandronum, the actor can be used as a reference on how to make use of custom arguments, and of what a real world usage case of the information covered in this guide looks like.
https://drive.google.com/file/d/1tNmd7pkPPf8vOoNhJAGAeZ5iZFOcrSwN/view?usp=sharing
Note: The ACS code used by the actor isn't really relevant to the guide, only the main DECORATE file is.
Tips
- You can only have up to 5 custom arguments.
- Custom arguments only support numbers/integers. And yes, all the argument types available through //$ArgNType are still just integer numbers.
- Because action specials also use the args field, you cannot have an actor that both uses custom arguments and an action special. This applies to built-in actors that use the args[] array too, such as dynamic light actors.