How to: Modify the World Editor's Trigger Editor

Tutorial By Sevion

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. This tutorial will not do everything for you. You will be required to manually create things. I.E. I'll give you the TriggerData portion, but you must create the TriggerStrings portion!

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:

[SPOILER](Image)[/SPOILER]

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. Another is Notepad++.

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:

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:

[SPOILER](Image)[/SPOILER]

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

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:

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.

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:

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,

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:

[SPOILER](Image)[/SPOILER]

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

RemoveLocation="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:

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:

[SPOILER](Image)[/SPOILER]

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:

OperatorCompareTrackable=1,trackable,EqualNotEqualOperator,trackable _OperatorCompareTrackable_Default=_,OperatorEqualENE,_ _OperatorCompareTrackable_Category=TC_CONDITON

Then in TriggerStrings.txt

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

trackable=1,1,1,WESTRING_TRIGTYPE_trackable

Good. in WorldEditStrings.txt find // Trigger Variable Types

Type in

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.

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:

// 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:

// 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:

// 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:

// Trackable events TriggerRegisterTrackableHitEvent=1,trackable _TriggerRegisterTrackableHitEvent_Defaults=_ _TriggerRegisterTrackableHitEvent_Category=TC_My_Functions

Now do the same for Track.

// 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:

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:

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:

native CreateTrackable      takes string trackableModelPath, real x, real y, real facing returns trackable


Ok. So we make,

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:

CreateTrackable=0,1,trackable,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:

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.

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
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.

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.

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.

Q: My function doesn't work! A: Check for misspellings, it's case-sensitive.

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.

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.

Other UI Modifications
Submitted by: StealthOfKing, sent to me by NationCreati.

480x480 Map Size
Under WorldEditData.txt in the [WorldEditMisc] section, find MaximumMapSize, and change it to 480. Grimoire is needed. Note: This lags a lot. It's not recommended for map making.

90° Terrain Slopes
Under MiscData.txt in the [Terrain] section, find MaxSlope and change it to 90. This allows you to create 90 degree slopes with Raise Terrain etc. Grimoire is not needed. Note: This is useful for creating better looking cliffs than Blizzard's ugly cliffs!

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.

~Sevion

Click here to comment on this tutorial.
 
 
Blizzard Entertainment, Inc.
Silkroad Online Forums
Team Griffonrawl Trains Muay Thai and MMA fighters in Ohio.
Apex Steel Pipe - Buys and sells Steel Pipe.