World Editor FAQ: Custom Text
What is custom text?
A: Custom text (also known as JASS) is the programming language that all Warcraft 3 maps run on. All the triggers created using the graphical user interface are automatically translated into custom text when your map is saved. This includes triggers added by 3rd party programs. Creeps, regions, and sound variables (actually stings) are all translated into custom text as well.
There are 3 ways to use custom text.
  • Use the trigger action "Custom Script" to use a single line.
  • You can directly edit the text of a trigger using "Edit->Convert to Custom text".
  • Click on the very top entry in the list of triggers. There you can add your own global variables or functions.
  • Q:
    What can I do in custom text that I can't do with the graphical user interface?
    A: You can use local variables, call the same function from multiple places, and use all of the functions included in the game (you can use most of them without custom text). In addition custom text triggers will work with any editor. In general you use converted triggers for math then single custom script lines for local variables and functions not in the GUI.
    How do I learn to use custom text?
    A: You can convert existing triggers to custom text, you can find triggers written in custom text by other people, and you can extract blizzard.j and common.j from war3.mpq to use as documentation. You can find some resources here and here:
    How do I use local variables in the GUI?
    A: Create a local variable with the same name and type as a global variable. The GUI will allow you to select the global variable from the list as normal, but when the script compiles, it will actually refer to the local variable instead.

    To create the local variable use a single line of custom text at the start of your function like this:

    "Custom script: local integer udg_i"

    This would correspond to the global integer "i". Be careful with this, because the variable is local to the function and not the trigger. Triggers often contain multiple functions, especially since conditions are always seperate functions. To get around this you can assign the local variable to a second global variable before you enter the new function.

    You can only use one local variable this way per function. If you try to override with 2 locals at once they will both refer to the same variable. I have no idea why.
    How do I figure out what order string to give a unit?
    A: This trigger will give you the string of any order you give a unit:
    Display Order Strings
    Unit - A unit Is issued an order targeting an object
    Unit - A unit Is issued an order targeting a point
    Unit - A unit Is issued an order with no target
    Game - Display to (All players) the text: (String((Issued order)))
    How do I use a leaderboard?
  • Create a leaderboard. (don't do this step at map initialization)
  • Add players to the leaderboard.
  • Update the values you want displayed on the leaderboard.

    There are two ways to keep the values updated. If you are displaying something like gold or number of units in an area, then you can update the leaderboard every few seconds using the new gold count or number if units in area. If you are displaying something like number of rounds won or lives left, then you will need to keep track of the number separately. Use an array with one entry per player. Every time an event happens that would change the value, first update the array, then update the leaderboard with the current value of the array. For a "kills" leaderboard you can copy go here:
  • Q:
    How do I make a hero selection system, like in Aeon of Strife or Hero Arena maps?
    A: The easiest way to select heroes is a trigger like this (you only need the one trigger).
  • event: A unit is issued an order targetting an object.
  • condition: ((target unit of issued order) is a hero) equal to true
  • condition: (ordered unit) type is equal to
  • condition: owner of (target unit of issued order) is equal to (neutral passive)
  • action: create a unit of type (unit type of (target unit of issued order)) for player (owner of (ordered unit)) at ()
  • action: kill (ordered unit)
  • (optional) action: remove (target unit of issued order)
  • (optional) action: create item of type (ankh) and give it to (last created unit)
  • (optional) action: pan camera for (owner of (ordered unit)) to (location of unit (last created unit))

    Then place your heroes and change their owner to neutral passive. You will probably need to add a message at the start of your map telling your players how to choose a hero. You can add "Circle of Power" units under your heroes if you want but they are only there to look pretty.
  • Q:
    How do I choose a random region, sound, item type, or something similar?
    A: Make an array of each possible value, then use a random number as an index into that array. For example:
  • Set MySoundArray<1> = BoneyardWhat1
  • Set MySoundArray<2> = Credits
  • Set MySoundArray<3> = LadyVashjYes1
  • Sound - Play MySoundArray<(Random integer number between 1 and 3)>
  • Q:
    How can I make something happen, but only for a single player?
    A: Create a player variable named "LocalPlayer". Then run this trigger:
    Initialize LocalPlayer
            Map initialization
            Custom script:   set udg_LocalPlayer = GetLocalPlayer()
    Unlike all other variables in your map, this variable will be DIFFERENT for each person playing the game. This means that if you use this variable in the condition part on an "if" statement, you can make the game run differently for each player. If you create a unit, kill a unit, or otherwise change the game state for one person and not for another, then those players will disconnect from each other.

    If you play a sound, create a quest, or similar, the game state will not change and nobody will disconnect. For example:
    Play Sound On Death
            Unit - A unit Dies
            If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                If - Conditions
                    (Owner of (Dying unit)) Equal to LocalPlayer
                Then - Actions
                    Sound - Play L01Arthas07  at 100.00% volume, attached to (Dying unit)
                Else - Actions
    This will play a sound only for the owner of the dying unit.
    I want to run a function for each member of a group, and I want to have a wait statement in that function. How do I do that?
  • If you just use a "PickEveryX" function, then the wait statement will not work correctly. Every action after the wait will be skipped. This is because the actions inside the "pick" block are assigned to a seperate function and a pointer to that function is passed to the "pick" function. As a result the trigger will not be set up properly for sleeping within the block.

  • If you want the actions to happen at the same time for each memeber of the group, then put all the actions inside your "pick" block in a second trigger. Then inside the pick block, call your second trigger. "Picked Unit" and similar variables will be set properly.

  • If you do the member choosing yourself (pick a member from the group, run function, remove it, repeat), then the wait statement will work correctly. In this case it will wait to finish one member of the group before going on to the next.

    Not that none of this applies to "For Each Integer..." loops. There the wait will be executed just like any other action in the loop.
  • Q:
    How do I get the level of a unit for use in a trigger?
    A: There is no "level of unit" trigger function. The best work-around would be to use the Point-Value of Unit function instead, and set the point value for all relevant units via the Object Editor.
    How do I create creeps and have them drop items like in the orc campaign?
    A: Open up the orc campaign and see. Extract the Orc expansion campaign maps from war3xlocal.mpq (see the Import/Export section for how) and look at the triggers in the 2nd map. It has a system to spawn creeps and drop items when they die.
    How can I keep the camera centered on a unit, while still letting the player use the minimap?
    A: This trigger will force the camera to follow a unit around. The camera will slide to keep up with the unit, which looks a little nicer than the normal "lock camera to unit" trigger. Since the camera is not techincally locked, the player will be able to use the minimap normally, and can pull the camera away for a very short period of time.
    Center Camera
            Time - Every 0.30 seconds of game time
            Camera - Pan camera for Player 1 (Red) to (Position of Spell Breaker 0000 ) over 0.50 seconds
    Do regions have to be rectangles?
    A: No. "rect" variables have to be rectangles. "region" variables can be made up of multiple rects, so they can make any shape. The GUI sometimes uses regions and sometimes uses rects, but it calls both regions.

    So basically it would be really complicated to use but you could use the real "regions" if you wanted to. You can find the appropriate functions by extracting common.j
    Do I have to delete points and unit groups? What does "set bjWantDestroyGroup = true" mean?
    A: You should not use bjWantDestroyGroup, I'll explain why it exists at the end.

    Any time you create an object in a trigger it will exist until you destroy it or the map ends.

    You can think of them all as being similar to units. Imagine you have a trigger that creates a unit, then attaches a special effect to that unit. When your trigger ends, you still have a unit with a special effect attached. If you run this trigger many times, you will end up with many units with special effects attached. The same thing happens for a point. You create a random point in a region, then attach a special effect to that point. When the trigger ends you still have a point and a special effect. If you run this trigger many times you will end up with many points and many special effects. In both cases you will need to destroy the points, units, and special effects if you don't want them to build up.

    Variables are not objects, they are handles for objects. You assign a point to a variable the same way you would assign a unit.

    If you want to create a unit, use it for something, and then destroy it, then you will need to store that unit in a variable so you can access it later:

    1. create a unit
    2. store that unit in [variable]
    3. do something to unit in [variable]
    4. remove unit in [variable]

    If you do this every time you create a unit, then units won't build up over time. Points follow the same rules. Create your point, assign it to a variable, do something with it, and then destroy it. Otherwise they will build up over time.

    The problem is that the GUI functions skip steps 2 and 4. They create a group and use it, but without ever assigning it to a variable or destroying it.

    Blizzard people figured out the problem, but they couldn't find a good solution (I'm not making this up, Brett told us earlier). They could decompose every "each unit in group" function in every Blizzard map, but that would take forever. What they wanted to do was make the "each unit in group" function destroy the group that it was using. They couldn't do that exactly though, because the group may not be temporary! If you have a group variable chances are you don't want your group destroyed every time you use a for each function on it. What they did instead was sort of a cross between the two approaches. They modified the for each function to delete the group that was passed to it ONLY IF "bjWantDestroyGroup == true". Then all they had to do was set bjWantDestroyGroup before every "for each unit" function that did NOT use a group variable.

    If you are making a map from scratch then don't do this! Do exactly the same thing as for units and points. Make a unit group variable named "TempGroup", and do the 4 step process above.

    However if your map is short and your triggers don't run very often you probably don't care. If you only leak 1000 points during your entire map then it won't really matter.
    How do I make different background music play during my map?
    A: This is referring to the random music that plays throughout the map, there is already information on playing a specific song at a specific point earlier in this FAQ. Let's say you have a map where everyone is Human but you want to have some Orc and Undead music play as well, or you have a whole list of custom songs you want played randomly.

    You need to use the trigger action: Sound - Set Music List.

    Unfortunately, while this action is available in the GUI, it can only be used in custom text (JASS) because the paramater it takes, the list of music, doesn't have any way to specify it in the GUI. It's still pretty easy to do though, even if you don't know a thing about JASS or custom text. Here's what you do:

    1. Make a trigger called Setup Music. 2. Add an action to your Melee Initialization trigger to run the Setup Music trigger (that way the map will start out with one of your song choices right away). 3. Add the Sound - Set Music List command to the Setup Music trigger, but just leave the Music paramater as it is. 4. Convert the Setup Music trigger to custom text. 5. Look in the trigger for the part that says ("Music"). 6. Replace Music with a list of songs you want to play seperated by a semicolon (;).

    Each song built into the game (both TFT and ROC) is in the format of Sound\\Music\\mp3Music\\SongName.mp3. Imported songs are going to be war3mapImported\\SongName.mp3. So, an example of this command would look like:

    call SetMapMusicRandomBJ( "Sound\\Music\\mp3Music\\OrcX1.mp3;Sound\\Music\\mp3Music\\NightElfX1.mp3;Sound\\Music\\mp3Music\\ArthasTheme.mp3" )

    Now there's one problem with this. Even though you can make the list as long as you want in the editor, the string will be cut short in the actual game (I'm assuming it's at 255 characters but I haven't confirmed this). This means two things: first, that it will only actually play the first bunch of songs in the list, and second, that after a few songs it will stop playing music altogether for the rest of the map (this is because it attempts to play a song who's name was cut off halfway through it when the string was cut short). If you only want to play 3-5 songs it's probably not going to happen, but if you want to play more, you'll need an alternative.

    To get around this, add an Event to your Setup Music trigger that is Timer - Periodic Event every 200 seconds. Now in the trigger itself, make several of the random music commands like above each with a different set of 3-4 songs. Then, at the beginning of the actions assign a variable to a random number between 1 and the number of song sets you have. Then using if statements, have it run one of the song lists based on what random number was picked. This way every few minutes the map will change the set of random songs, so that all of your songs eventually have a random chance of being played.

    Don't worry, when the set of songs changes it doesn't stop playing the current song, it waits until the current song is finished before using the new song list. The transition will be smooth and unnoticable, you won't be able to tell the difference between doing it this way and having just one song list.
    How do I make a building cancel training a unit?
    A: Use the custom text function:
    call IssueImmediateOrderById( udg_buildingUnit, 0x000d0008 )
    If you have a global unit variable called buildingUnit, then the preceding line will cancel a unit being trained by buildingUnit. You can use the same technique to cancel upgrades or research.
    How do I kill all the units and share all the gold for a player who leaves the game?
    A: Use this trigger:
    Melee Initialization
            Player - Player 1 (Red) leaves the game
            Player - Player 2 (Blue) leaves the game
            Player - Player 3 (Teal) leaves the game
            Set tempPlayerGroup = (All allies of (Triggering player))
            Player Group - Remove (Triggering player) from tempPlayerGroup
            Set tempInteger = (Number of players in tempPlayerGroup)
            -------- We want to avoid dividing by zero if there are no allies left. --------
            If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                If - Conditions
                    tempInteger Greater than 0
                Then - Actions
                    Set tempInteger = (((Triggering player) Current gold) / tempInteger)
                    Player Group - Pick every player in tempPlayerGroup and do (Actions)
                        Loop - Actions
                            Player - Add tempInteger to (Picked player) Current gold
                Else - Actions
            -------- Destroy the player group once we are done with it. --------
            Custom script:   call DestroyForce( udg_tempPlayerGroup )
            -------- Now kill all the leaving player's units. --------
            Set tempUnitGroup = (Units owned by (Triggering player))
            Unit Group - Pick every unit in tempUnitGroup and do (Actions)
                Loop - Actions
                    Unit - Explode (Picked unit)
            -------- Destroy the unit group once we are done with it. --------
            Custom script:   call DestroyGroup( udg_tempUnitGroup )