|
|
|
|
|
|
|
World Editor FAQ: Custom Text |
|
Q:
| 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. |
Q:
| 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.
|
Q:
| 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
Events
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
Conditions
Actions
Game - Display to (All players) the text: (String((Issued order)))
|
Q:
| How do I use a leaderboard?
|
A: |
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: http://www.elilz.com/demomaps.html
|
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
Events
Map initialization
Conditions
Actions
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
Events
Unit - A unit Dies
Conditions
Actions
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.
|
Q:
| 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?
|
A: |
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. |
Q:
| 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. |
Q:
| 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
Events
Time - Every 0.30 seconds of game time
Actions
Camera - Pan camera for Player 1 (Red) to (Position of Spell Breaker 0000 ) over 0.50 seconds
|
Q:
| 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 |
Q:
| 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.
|
Q:
| 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.
|
Q:
| 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.
|
Q:
| 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
Events
Player - Player 1 (Red) leaves the game
Player - Player 2 (Blue) leaves the game
Player - Player 3 (Teal) leaves the game
...
Conditions
Actions
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 )
|
|
|
|
|
|
|
|
|
|