• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[Tutorial] Modifying the World Editor's Trigger Editor

Was this helpful to you?


  • Total voters
    12
Level 5
Joined
Dec 4, 2006
Messages
110
Modding the World Editor Trigger Editor
By: Sevion

This is for GUI users don't want to use JASS script in their coding.

Introduction

World Editor was created by Blizzard to be easily used and modified. Here I'll show you how to add different functions into the Trigger Editor for GUI. You will now be able to use JASS Natives in GUI! Instead of using, say, DestroyEffectBJ all the time, that's what it converts to when you change it to JASS, you can create a GUI function that is DestroyEffect! You cut down on a lot of function calls using this! You can now add in a GUI RemoveLocation function! No more Custom Scripting! Yay! Now, this may seem like it's better than JASS, but it isn't. JASS is still more powerful in all aspects no matter how you look at it. This tutorial is just going to make GUI a bit more powerful for you. If you like using GUI, but need a bit of JASS implemented constantly, this is the tutorial for you.

Getting Started

Now, first off you want to know where to edit, right? Well, create a new folder in your Warcraft III directory called UI. It should look like this picture:

UI.png


Now, you need these three files:

UI\TriggerData.txt
UI\TriggerStrings.txt
UI\WorldEditStrings.txt

A suggested text editor is Crimson Editor. Trust me, it will make life a whole lot easier. It can open all three text files in one window, each accessable by tabs.

These can be extracted from War3Patch.mpq or found attached to this post.

These are read only when WE is opened. So if you make any changes with it up, save, close, and restart WE for changes to take effect.

Format

Now, let's take a look at the file format, shall we?

In TriggerData.txt, you'll see a whole big huge titanic block of text. Above each large block is going to be some words in brackets, []. They are:

  • [TriggerCategories] - Categories are a way to organize actions and functions. They show up as a prefix followed by a hyphen, and when selecting an action, you can choose to show only actions from a particular category. For example, in "Unit - Kill Unit", the category is "Unit", and the action is "Kill Unit".
  • [TriggerTypes] - All of the Variable types are defined here. You may notice that there are very many more variable types listed here than you see in the Variables editor. This is because you can define types that the Trigger Editor can use internally, but would not be meaningful as regular (global) variables. More on that later.
  • [TriggerTypeDefaults] - Some Variable types are initialized to default values. For example, Integers are set to 0 by default. Some types must be initialized by a function (usually "Create"____) in order to work properly, so it's good practice to do this here. For example, timers are initialized to CreateTimer() and unit groups are initialized to CreateGroup().
  • [TriggerParams] - These are known as "Presets" in the Trigger Editor. These are defined values for a variable type that you can choose from a list. Some variable types have only these presets and no other purpose. For those of you with outside programming experience, these are basically "enumerated types". An example is the list of orders you can issue targetting a point ("Move", "Human Archmage - Blizzard", "Orc Tauren Chieftain - Shockwave", etc.).
  • [TriggerEvents] - All events are defined here.
  • [TriggerConditions] - All conditions are defined here.
  • [TriggerActions] - All actions are defined here.
  • [TriggerCalls] - These are known as "Functions" in the Trigger Editor, when you need to fill in a value as a parameter. Examples are "Math - Random Number" and "Region - Center of Region".
  • [DefaultTriggerCategories]
  • [DefaultTriggers] - Whenever a new map is created, trigger categories (for organization) and triggers can be created by default. The only things listed here are the familiar Initialization trigger category and Melee Initialization trigger. You might find occasion to add your own here, or you might remove the lines in this section if you're tired of having to delete Melee Initialization.
  • Whereas TriggerData.txt defines the functionality and structure of the Trigger Editor, the other two files, TriggerStrings.txt and WorldEditStrings.txt, provide all the text that will be displayed to you when creating and editing triggers. WorldEditStrings.txt is simply a list of strings, and is used by other parts of the World Editor as well. TriggerStrings.txt strings follow a special format, since you have to tell the Trigger Editor what parts of the string are editable parameters (underlined).
  • The sections in TriggerStrings.txt are as follows:
  • [TriggerEventStrings] - Strings for all events.
  • [TriggerConditionStrings] - Strings for all conditions.
  • [TriggerActionStrings] - Strings for all actions.
  • [TriggerCallStrings] - Strings for all functions.
  • [AIFunctionStrings] - These are used by the AI editor.
  • And WorldEditStrings.txt:
  • [WorldEditStrings] - Strings for categories, variable types, variable type defaults, and presets are in this file.
Let's Take A Break Shall We? 5 Mintues Is Good. Give Your Eyes A Rest.

Alright, back on task.

Adding A Function

Now, you're probably like, "Well, where do I type and what do I type?"

Well, if you look, it's broken up into sections, or categories, if you will. They're all stated up above. First we're going to add an Action. Well, since we need a RemoveLocation function in GUI, let's add that.

In TriggerData.txt, look for the part with all the actions, [TriggerActions]. Run a Find and search for, say, DestroyEffect because they're basically exactly the same.

You should find:

Code:
DestroyEffectBJ=0,effect
_DestroyEffectBJ_Defaults=GetLastCreatedEffectBJ
_DestroyEffectBJ_Category=TC_SPECIALEFFECT
DestroyEffectBJ is the name of the function (Later we'll optimize this function by creating a Native action for this.).

The first value, "0", states whether it's compatible with just TFT or ROC and TFT. "0" states that it works for both.

The "," separates each value. Simple.

"effect" is what it takes. When you're destroying an effect, you need to take an effect to destroy, correct?

_DestroyEffectBJ_Defaults=GetLastCreatedEffectBJ. That just states what it's default value is when you create that action. We don't need to add this for our location because there is no "GetLastCreatedLocation" function.

_DestroyEffectBJ_Category=TC_SPECIALEFFECT. That means it will show up under Special Effects. It will say Special Effects - Destroy Effect. Like in the picture:

UI2.png


So what does that mean? Well, since our function is RemoveLocation, we do this:

Code:
RemoveLocation=0,location
_RemoveLocation_Defaults=_
_RemoveLocation_Category=TC_My_Functions
We're done, right?

WAIT! The category TC_My_Functions doesn't exist! What do we do? Create it! So, where do we find Categories? In TriggerData.txt, near the top you'll see a big block of text of which each line starts with "TC_" What did we type for our RemoveLocation Category? "TC_My_Functions" So, find:

Code:
TC_AI=WESTRING_TRIGCAT_AI,ReplaceableTextures\WorldEditUI\Actions-AI
This is near the top. It's the first category under actions. We want ours ABOVE it so it's at the top of the Actions list for easy accessability. So if that's what the syntax looks like let's get started.

Code:
TC_My_Functions=WESTRING_TRIGCAT_My_Functions,ReplaceableTextures\WorldEditUI\Actions-SetVariables
Now, I just chose a nice looking icon. You can use whatever icon you want that you see under the Trigger Categories, TC.

So, now we have the function and the category finished. What's next? The strings that are displayed! So open TriggerStrings.txt.

Look for "[TriggerActionStrings]" Now, you'll see a bunch of actions. Find DestroyEffectBJ again.

It will say:

Code:
DestroyEffectBJ="Destroy Special Effect"
DestroyEffectBJ="Destroy ",~Special Effect
DestroyEffectBJHint=
Well what the hell does that mean? Simple.

DestroyEffectBJ="Destroy Special Effect" is what it shows after the category, Special Effect -, so it will read "Special Effect - Destroy Special Effect"

Now it says DestroyEffectBJ="Destroy ",~Special Effect

Well what's that mean? It's what shows up in the box underneath the drop-down box. It will say "Destroy (Last Created Special Effect)" Well, I see "Destroy ". Except it stops there. So where does (Last Created Special Effect) come from? ,~Special Effect. The "," is like the Concatenate String thing for text. It mashes two things together. For example "Destroy ", "yourself." would display as "Destroy yourself." The ~ prefix states that it's going to be a variable part. So ~Special Effect. It's going to be a variable Special Effect.

Ok. Next is DestroyEffectBJHint=. What does it equal? Nothing. It's the gray text that says what it does. Well, it can't get any simpler than Destroy Special Effect so let's find an example that does have a hint like,

Code:
DisableTrigger="Turn Off"
DisableTrigger="Turn off ",~Trigger
DisableTriggerHint="Does not interrupt existing executions of the trigger, but prevents future executions."
Now look at it in the Trigger Editor:

UI3.png


Well, RemoveLocation is like DestroyEffect. Doesn't need any more description. So leave it blank.

Code:
RemoveLocatoin="Remove Location"
RemoveLocation="Remove "~Location
RemoveLocationHint=
Add it anywhere you want under [TriggerActionStrings]

Ok. So far we've learned how to implement an action from JASS and create new categories. What's in store next? Events and Conditions!

Alright. Take five!

Back already? Alright. After a good rest, you should be ready to learn how to make Conditions. They're really simple!

Look for [TriggerConditions]

A condition looks like:

Code:
OperatorCompareAbilityId=1,abilcode,EqualNotEqualOperator,abilcode
_OperatorCompareAbilityId_Defaults=GetSpellAbilityId,OperatorEqualENE,AUan
_OperatorCompareAbilityId_Category=TC_CONDITION
Well, since Location comparisons are already in GUI, we'll make a Trackable conmparison! Now, look at the code.

OperatorCopmareAbilityId=1,abilcode,EqualNotEqualOperator,abilcode. These are the parameters, or arguments, that the comparison takes. In the Trigger Editor it looks like:

UI4.png


Anyone will look and know that the first and third are abilities, and that abilcode means the ability's raw code. EqualNotEqualOperator is the Equal To/Not Equal To comparison operator.

So, to create our comparison we do:

Code:
OperatorCompareTrackable=1,trackable,EqualNotEqualOperator,trackable
_OperatorCompareTrackable_Default=_,OperatorEqualENE,_
_OperatorCompareTrackable_Category=TC_CONDITON
Then in TriggerStrings.txt

Code:
OperatorCompareTrackable="Trackable Comparison"
OperatorCompareTrackable=~trackable," ",~Operator," ",~trackable
OperatorCompareTrackableHint=
Now, anyone with knowledge of the Variables, we'll know that there are no Trackable variables in GUI. So we create some.

In TriggerData.txt are the variable types.

Find // Trigger Variable Types

Now, underneath where all the other variables are defined, type

Code:
trackable=1,1,1,WESTRING_TRIGTYPE_trackable
Good. in WorldEditStrings.txt find // Trigger Variable Types

Type in

Code:
WESTRING_TRIGTYPE_trackable="Trackable"
Now we have Trackable Variables! Good job!

Well, then. What's left? Events! Alright. So let's make an event for Trackables in GUI. There are two events for Trackables.

Code:
native TriggerRegisterTrackableHitEvent takes trigger whichTrigger, trackable t returns event
native TriggerRegisterTrackableTrackEvent takes trigger whichTrigger, trackable t returns event
Hit is when it's clicked, Track is when you mouse over it. Now, what to do?

Alright, if you look at other events, they're almost exactly like actions! So:

Code:
// Destructible events
TriggerRegisterDeathEvent=0,destructable
_TriggerRegisterDeathEvent_Defaults=_
_TriggerRegisterDeathEvent_Category=TC_DESTRUCT
First line is a comment. It tells what it is, Destructable events. So, next line. TriggerRegisterDeathEvent=0,destructable.

"0" means it's ROC compatible. Destructable is what it's firing for. So TriggerRegisterDeathEvent registers when a destructable dies. Simple.

So we get:

Code:
// Trackable events
TriggerRegisterTrackableHitEvent=1,trackable
_TriggerRegisterDeathEvent_Defaults=_ that's the default values. It's a _. What does that mean? It means nothing. No destructable. Null. So. After modifying, we get:

Code:
// Trackable events
TriggerRegisterTrackableHitEvent=1,trackable
_TriggerRegisterTrackableHitEvent_Defaults=_
Now _TriggerRegisterDeathEvent_Category=TC_DESTRUCT. We already know this is what category it goes under. So we have:

Code:
// Trackable events
TriggerRegisterTrackableHitEvent=1,trackable
_TriggerRegisterTrackableHitEvent_Defaults=_
_TriggerRegisterTrackableHitEvent_Category=TC_My_Functions
Now do the same for Track.

Code:
// Trackable events
TriggerRegisterTrackableHitEvent=1,trackable
_TriggerRegisterTrackableHitEvent_Defaults=_
_TriggerRegisterTrackableHitEvent_Category=TC_My_Functions
Now, let's take another break. This is a lot to soak in :)

Now, we've learned Actions, Conditions, Variables, and Events. Wait! We need to get things like Triggering Trackable! Because there is no function for GetTriggeringTrackable in GUI! So, we need to figure out how we're going to set our new variable to the Trackable that's clicked. We can't do it without a bit of JASS unless we create a new function! Let's get started.

For anyone who doesn't know, there's a function called Triggering Unit, correct? So, we find _GetTriggeringUnit

We get:

Code:
GetTriggerUnit=0,0,unit
_GetTriggerUnit_Defaults=
_GetTriggerUnit_Category=TC_EVENTRESPONSE
So, GetTriggerUnit=0,0,unit

It's syntax is: Function=0/1 Compatibility,0/1 Event Usable,Return Type

So, GetTriggerUnit is the JASS function, 0 means it works for ROC, 0 means it can't be used in Events, and unit is the type it returns.

So ours goes:

Code:
GetTriggerTrackable=1,0,trackable
_GetTriggerTrackable_Defaults=
_GetTriggerTrackable_Category=TC_EVENTRESPONSE
And we've already leanred about Defaults and Categories. So, we have when the trackable is clicked or moused over and the function to get that trackable. WAIT! We need trackables in order for this to fire! So what do we do? We add an action to create trackables!

So we remember what actions look like?

These are the parameters it takes in JASS:

Code:
native CreateTrackable      [B]takes string trackableModelPath, real x, real y, real facing returns trackable[/B]
Ok. So we make,

Code:
CreateTrackable=1,modelfile,real,real,real
_CreateTrackable_Defaults=_,_,_,_,
_CreateTrackable_Category=TC_TRACKABLE
But wait! We can't do Set My_Var = (Last Created Trackable). There's no such function! So we implement a bit of JASS thinking. Set My_Var = CreateTrackable().

Now we remember that we need to move it to [TriggerCalls] because we can't set a variable to an action. We need to set it to a function call. So move it to [TriggerCalls] Now, if you look the syntax is slightly different. So we modify it a bit to match syntax and we get:

Code:
CreateTrackable=1,modelfile,real,real,real
_CreateTrackable_Defaults="Abilities\Spells\Other\TalkToMe\TalkToMe.mdl",0,0,RealUnitFacing
_CreateTrackable_Limits=_,_,_,_,_,_,0,360
_CreateTrackable_Category=TC_TRACKABLE
Before I forget, DON'T FORGET TO ADD TRIGGERSTRINGS! IT WON'T SHOW IF YOU DON'T HAVE THE RIGHT STRINGS TO DISPLAY!!!

ScriptName

There's one more useful field provided for Actions (and only Actions, unfortunately): ScriptName. This lets you separate the name of the JASS function from the name of the GUI action. This is actually quite powerful for several reasons.

One advantage is that you can give multiple GUI names to the same function. Maybe you want one function to work for both "integer" and "unitcode". In JASS, of course this would work out, since both types are really integers. In the GUI, you need two separate entries, but by using the ScriptName field, you could have two names that point to the same function.

The bigger advantage with this field is that it separates the GUI implementation from the JASS implementation. Maybe the GUI thinks that you are using one function, but when you save the map, a different JASS function is really used. It is important to know some of what goes on when you save a map in the World Editor to appreciate how this works. When you save a map in the WE, all of the triggers and your triggers' structure are saved in a triggers file inside the map. This file serves no purpose in Warcraft III, and is only used by the WE. (This is why almost all map protector programs destroy this file.) Additionally, all of your triggers are converted to JASS and are placed inside a script file inside the map. This file is the one that is actually used by Warcraft III, and includes other information as well, such as pre-placed units and regions, variable initializations, and player start locations.

When you use the ScriptName field, you change the JASS part, the script file that is created when you save, but not the GUI part, the triggers file created when you save. This means that changes to the ScriptName field will change how the map compiles in your editor, without preventing the map from being opened in other versions of the editor.

Here is a prime example. Let's revisit our first example of an Action: DestroyEffectBJ. The entry looks like this:

Code:
DestroyEffectBJ=0,effect
_DestroyEffectBJ_Defaults=GetLastCreatedEffectBJ
_DestroyEffectBJ_Category=TC_SPECIALEFFECT
Now, we know that DestroyEffectBJ is redundant and inefficient to use, and that DestroyEffect is preferred. We can use the ScriptName field to replace all instances of DestroyEffectBJ with DestroyEffect, without having to edit anything in any map! The WE will still think you are using DestroyEffectBJ just as before, but when you convert a trigger to JASS, or save the map, DestroyEffect will be used.

Code:
DestroyEffectBJ=0,effect
_DestroyEffectBJ_Defaults=GetLastCreatedEffectBJ
_DestroyEffectBJ_Category=TC_SPECIALEFFECT
_DestroyEffectBJ_ScriptName=DestroyEffect
This method is greatly preferred to using two separate actions (as we did in the first example) in a situation like this, because you don't need to change any maps using the GUI, and when you use this function, your map will still open in other editors. (It's also less work, you don't need to do anything in TriggerStrings.txt when you do this.) Unfortunately, you will find that this nifty technique is quite limited, since the parameters list of the two functions must be identical. The purpose of many BJ functions is to reverse the order of parameters (for natural language purposes), so these can't be simply changed by using the ScriptName field.

Troubleshooting/FAQ
Code:
Q: I get errors saying "Missing XXX.XXX"
A: Did you save the files I told you into X:/Program Files/Warcraft III/UI?
    Did you extract the files from War3Patch.mpq? War3.mpq doesn't seem to work.
Code:
Q: When I open the MPQ to extract the files, I get a bunch of gibberish saying "Unknown.xxx"
A: Download the latest listfile for Warcraft. It can be found attached at the bottom of the tutorial.
Code:
Q: When I try it it says "Missing String 'WESTRING_TRIGCAT_XXX'"
A: You didn't add the category.
    You spelled the category wrong somewhere.
Code:
Q: My function doesn't work!
A: Check for misspellings, it's case-sensitive.
Code:
Q: What does this do?
A: You can now implement more efficient code into GUI or add new functions like trackables or your own custom functions.
Code:
Q: Why would we need this?
A: It makes your GUI coding more efficient and gives you more power.
Exercises

Here are some ways you might consider extending your Trigger Editor:

Add the "region" variable type (as opposed to rect) and all its functionality.
Add the native functions that take real (coordinate) parameters, as opposed to location parameters.
Find other examples of "dummy" BJ functions that can be replaced with their native counterparts just by using the ScriptName field (and let me know about them!)
Add a system like KaTTaNa's Handle Variables or Vexorian's CSCache to the GUI.
Browse the Jass Vault (www.wc3jass.com), and implement whatever functions you think would be useful to you.
If you are working on a large system that relies on JASS, add the most important "high level" functions to the GUI.


Well, that's all folks. I hope you see how easy it is to customize the Trigger Editor to your needs and, if you don't see it yet, I hope you will one day see how silly the debate between GUI and JASS really is.

Please let me know if there are any errors.

[This has only been submitted to the following websites: www.thehelper.net, www.wc3jass.com, and www.hiveworkshop.com If you find this on any other website please contact me and the forum administrator.]

~Sevion
 

Attachments

  • listfile.zip
    1.3 MB · Views: 154
  • UI.zip
    829.3 KB · Views: 164
Last edited:
Level 40
Joined
Dec 14, 2005
Messages
10,532
Well that was quite a read, very nice though. Maybe abuse BBCode to add things like a
  • when you're listing the various bracketed fields in TriggerData.txt, etc?

    Anyways, I'll approve this in a bit (as I'm not sure if you can edit posts in the approved tutorial sections as a normal user), after you confirm whether or not you're making the changes or if you don't reply.
 
Level 4
Joined
Dec 4, 2007
Messages
76
I followed this tutorial word for word THREE TIMES and every time I tried I had the error "[Trigger Actions] - "Create Trackable" in the Data section and text section have different parameters 3,0" or something to that extent...how do I fix? I really need this!
 
Top