- Joined
- Sep 26, 2009
- Messages
- 2,595
Warcraft 3 Trigger Format Specification (WTG!) - Reforged 1.36
For classic Warcraft's WTG file definition see Warcraft 3 Trigger Format Specification (WTG!)
|
---|
The changes I will write about are for Reforged version 1.36, however that is because I started investigating wtg files on this version. It is entirely possible that these changes were introduced in earlier versions. |
While I have noticed some odd structures, I don't have the resources to test hundreds/thousands of maps to ensure those are the only odd structures. As such, you may encounter some other odd structures that will not be covered in this post. |
Since March 2024, I tried to create a tool for migrating custom races between maps. As such, it was important for me to be able to visualize all object editor and trigger editor related data so that I may pick and choose what to migrate and what not. During my investigations, I've noticed that certain GUI trigger functions use odd structures. Browsing the web, I've found multiple tools/github repos, but I didn't see any of them addressing those odd structures. That lead me to share here what I've found.
Basics
Like other map files, the wtg file is a binary file. You should use a hex editor (like Hex Editor Neo) to investigate their content.You will need to adhere to the structures described later in the post to read and evaluate groups of bytes.
Bytes are displayed in hexadecimal numeral system, e.g.
0x10
is hexadecimal representation of number 16, and 0xff
of number 255.Structs contain a list of data types and their name/meaning. You may read here https://www.thehelper.net/threads/explanation-of-w3m-and-w3x-files.35292/ what data types are supported in WC3, but a tl;dr for WTG files is:
Data type | Size | Comment |
---|---|---|
integer | 4 bytes | These are stored using Little Endian order. |
integer[n] | n | n-times repeated integer |
boolean | 4 bytes | This is not a real type, it's more of a helper type for this document. It is basically an integer type with either value zero or one, matching boolean values false or true. |
string | variable length | These can be of any length, but they are always terminated by \0 (a 00 byte). It is possible to have an empty string, in which case the value consists only of terminator \0 |
char[n] | n bytes | Fixed size string. Since it is fixed size, it is not terminated by \0. |
struct | variable length | A struct can contain other struct(s). If the struct has square brackets like [n] , then it means that struct is repeated n-times. For example a GUI trigger struct will contain multiple GUI function structs. |
Terms
Let's agree on the following terms about trigger editor:- Object Tree View - The left side panel where you see all your various objects like GUI triggers, categories/folders, etc, and where you organize your triggers, scripts and variables.
- Object Detail View- The right-side panel which changes based on which object you have selected. From top to bottom, it has the following parts:
- Flags - checkboxes like if the trigger is enabled, if it is turned on, etc.
- Comment Section - Where you write comment
- Function Section - Where you write script or place GUI actions
- Based on object type, flags will be changed and some sections may not be avaiable
- Variables have completely different right-side panel
- Variable Editor - The classic way to create and manage variables, opened via
CTRL + B
keys. - ECA - a short name for Event/Condition/Action
Contents of WTG file
What WTG file contains:- Tree structure of folders, variables and other trigger editor objects (the tree view)
- Counts of each type of object
- Table of defined global variables, including their definition (name, type, size, etc.)
- Settings of each object type (i.e. whether a gui trigger is enabled or not)
- Content of GUI triggers
- Map header's comment section
- Map header's custom script section
- Content of 'Custom script' files
- Script content of GUI triggers that have been transformed to Custom Text via context menu
.wtg
file is stored in .wct
file instead.triggerdata.txt
The triggerdata.txt is an INI file, found in WC3's casc file under path.\war3.w3mod\ui\triggerdata.txt
.GUI triggers are tightly coupled with triggerdata.txt file: The wtg file uses an internal identifier for all its functions and these identifiers are referenced by the triggerdata.txt file. The reason for the tight coupling is because many GUI trigger functions have various amount of parameters. The WTG file does not contain information about how many parameters a function has. Instead, you need to use the identifier and search the triggerdata.txt file to determine parameter count.
Let's take the following GUI action as example:
-
Wait 2.00 seconds
TriggerSleepAction
. Looking into triggerdata.txt, we will find the following section:
Code:
TriggerSleepAction=0,real
_TriggerSleepAction_DisplayName="Wait"
_TriggerSleepAction_Parameters="Wait ",~Time," seconds"
_TriggerSleepAction_Defaults=2
_TriggerSleepAction_Limits=0,_
_TriggerSleepAction_Category=TC_WAIT
real
.Another example:
-
Unit - Cause (Triggering unit) to damage (Triggering unit), dealing 500.00 damage of attack type Spells and damage type Normal
UnitDamageTargetBJ
which is described in triggerdata.txt as:
Code:
UnitDamageTargetBJ=1,unit,unit,real,attacktype,damagetype
_UnitDamageTargetBJ_DisplayName="Damage Target"
_UnitDamageTargetBJ_Parameters="Cause ",~Unit," to damage ",~Target,", dealing ",~Amount," damage of attack type ",~AttackType," and damage type ",~DamageType
_UnitDamageTargetBJ_Defaults=GetTriggerUnit,GetTriggerUnit,500,AttackTypeNormal,DamageTypeNormal
_UnitDamageTargetBJ_Category=TC_UNIT
Important:
Note that functions without parameters may actually have a single special parameter defined in triggerdata.txt file:
nothing
. For example this action:
-
Do nothing
Code:
DoNothing=0,nothing
_DoNothing_DisplayName="Do Nothing"
_DoNothing_Parameters="Do nothing"
_DoNothing_Defaults=
_DoNothing_Category=TC_NOTHING
nothing
when counting parameters.Of special note is the line where the key starts with underscore
_
and ends with _Category
: These contain identifier to the category's icon and localized name. For example value of _UnitDamageTargetBJ_Category
is TC_UNIT
. Using that value as key in triggerdata.txt, we will find value WESTRING_TRIGCAT_UNIT,ReplaceableTextures\WorldEditUI\Actions-Unit
. Splitting that by comma ,
we get two values:- The first value is a key for localized string/name
- the second is path to category's icon.
WESTRING_TRIGCAT_UNIT
represents key for localized string and its value can be found in .\war3.w3mod\_locales\{locale like enus}.w3mod\ui\worldeditstrings.txt
.WTG structs
Struct fields are written in the order in which data are present in Wtg file, i.e. top-most field will be present first in the wtg.Root
This is the struct with which each and every wtg file starts.
Rich (BB code):
char[4] FileId // Always contains bytes '0x 57 54 47 21', representing ascii characters 'WTG!'
integer MagicNumber // Not clear on the meaning. Seems to contain bytes '0x 04 00 00 80'. Could be an identifier to determine Reforged version.
integer FileFormatVersion // 4 = Reign of Chaos, 7 = Frozen Throne. This seems to be obsolete for Reforged and will always be version 7.
struct TypeInfo MapHeaderInfo
struct TypeInfo LibraryInfo
struct TypeInfo CategoryInfo
struct TypeInfo TriggerInfo
struct TypeInfo CommentInfo
struct TypeInfo ScriptInfo
struct TypeInfo VariableInfo
integer Unknown #1 // Always seems to be zero
integer Unknown #2 // Always seems to be zero
integer TriggerDefinitionVersion
integer VariableDefinitionCount
struct VariableDefinition[n] VariableDefinitions // Number 'n' equals VariableDefinitionCount
integer TriggerObjectCount
struct TriggerObject[n] TriggerObjects // All trigger objects found in tree view. Number 'n' equals TriggerObjectCount
TypeInfo
This struct keeps track of number of existing trigger objects of the given type and how many were deleted. It also keeps track of all deleted objects' ids. Probably serves for reusing objectIds and generating new ones.
Rich (BB code):
integer Total // Total count of objects of the given type
integer DeletedCount // Number of deleted objects
integer[n] DeletedObjectIds // A list of deleted object ids of the given type
VariableDefinition
Definition of the variable, as set up in variable editor.
Rich (BB code):
string Name // Variable name
string Type // Variable type. They match values found in [TriggerTypes] section of triggerdata.txt
integer Category // Value seems to always be '1' that would match the 'Scripts/Initialization' category in variable editor
boolean IsArray // Whether variable is array
integer ArraySize // Size of the array. Non array variables always have value '1'
boolean IsInitialized // Whether this variable has an initial value or not
string InitialValue // If 'IsInitialized' is false, then this is empty string
integer ObjectId // Unique id of the variable, always ends with byte '0x06'
integer ParentId // Id of an object that this variable is child of (i.e. some category)
Raw bytes extracted from wtg:
Code:
0x:
44 69 61 67 00 64 69 61 6c 6f 67 00 01 00 00 00
00 00 00 00 01 00 00 00 00 00 00 00 00 01 00 00
06 01 00 00 02
Rich (BB code):
[44 69 61 67 00] // ascii characters for 'Diag' terminated by null \0
[64 69 61 6c 6f 67 00] // ascii characters for 'dialog' terminated by null \0
[01 00 00 00] // category is '1'
[00 00 00 00] // is array: false
[01 00 00 00] // array size: 1
[00 00 00 00] // is initialized: false
[00] // empty string for initial value
[01 00 00 06] // object id of the variable, ends with 0x06
[01 00 00 02] // parent id, points to category/folder
TriggerObject
This can be any object you can see in the tree view of the Trigger Editor. Each TriggerObject starts with its object type. Based on the type a different struct follows. Weirdly, one of the possible structs is a struct describing variables (although in this case, the variable struct is way simpler than VariableDefinition struct).
Rich (BB code):
integer ObjectType // Type of the object. Determines which struct follows. See table below for mapping.
struct ObjectDefinition // The actual struct matching ObjectType. See table below for mapping object types to their respective structs.
ObjectType value | Meaning | Struct name |
---|---|---|
1 | Map header | MapHeader |
2 | Library | No struct, it is unused as far as I know |
4 | Category / Folder | Category |
8 | Trigger | Trigger |
16 | Trigger comment | Trigger |
32 | Custom script | Trigger |
64 | Global variable | Variable |
As a side note, trigger objects are ordered in the order they appear in the tree view from top to bottom. Whether they are children of other objects or not does not play any role. Consider the following tree view. Numbers in the example denote in what order those objects are stored in wtg file:
Code:
#1 root.w3x
|- #2
| |- #3
| |- #4
|
|- #5
|- #6
| |- #7
|
|- #8
MapHeader
Represents the root node/map node in Tree View
Rich (BB code):
integer ObjectId // Unique id of the object. The id is always '0'
string Name // Name of the map
boolean IsComment // Whether Script block is hidden. Always seems to be false
boolean IsExpandable // Whether this is expandable (has the +/- symbol next to name). Always seems to be false.
integer ParentId // Id of an object that this object is child of. Since this is root node, ParentId is always '-1'
Category
Represents a category folder or a category comment.
Rich (BB code):
integer ObjectId // Unique id of the object. Always ends with byte '0x02'
string Name // Name of the category
boolean IsCategory // Whether this is a category folder or category comment (interchangeable via right-click context menu)
boolean IsExpandable // Whether this category has child objects or not (i.e. whether it can be expanded in tree view or not)
integer ParentId // Id of an object that this object is child of
Raw bytes extracted from wtg:
Code:
0x:
04 00 00 00 00 00 00 02 4d 79 46 6f 6c 64 65 72
4e 61 6d 65 00 00 00 00 00 01 00 00 00 00 00 00
00
Rich (BB code):
[04 00 00 00] // objectType is 'Category', so CATEGORY struct follows
[00 00 00 02] // objectId
[4d 79 46 6f 6c 64 65 72 4e 61 6d 65 00] // ascii characters for 'MyFolderName' terminated by null \0
[00 00 00 00] // isComment = false
[01 00 00 00] // isExpandable = true, so this folder has child nodes
[00 00 00 00] // parentId is all zeroes, so parent is MapHeader/root node
Variable
This is a simplified version of VariableDefinition struct. It contains only data needed by the tree view.
Rich (BB code):
integer ObjectId // Unique id of the object. Always ends with byte '0x06'
string Name // Name of the variable
integer ParentId // Id of an object that this object is child of
Raw bytes extracted from wtg:
Code:
0x:
40 00 00 00 02 00 00 06 6c 6f 63 31 00 00 00 00
02
Rich (BB code):
[40 00 00 00] // objectType is 'Global Variable', so VARIABLE struct follow
[02 00 00 06] // objectId
[6c 6f 63 31 00] // ascii characters for 'loc1' terminated by null \0
[00 00 00 02] // parentId, it ends with 0x02 so the parent is a category/folder
Trigger
This is a generic struct that is shared by trigger comments, GUI triggers, GUI triggers converted to custom text, and custom scripts (jass/lua).The Detail View offers the following Flags (checkboxes) based on the type of the object:
- GUI trigger: Enabled, Initially On
- GUI trigger converted to Custom Text: Enabled, Run on Map Initialization
- Script: Enabled
- Any flag that is not available to a given type of object is always set to false
Rich (BB code):
string Name // Name of the object
string CommentText // Text in the Comment Section. Default value is empty string
boolean IsComment // Whether Function Section in Detail View is hidden or not. Only Trigger Comments have value true
integer ObjectId // Unique id of the object
boolean IsEnabled // Represents the 'Enabled' flag
boolean IsCustomText // Whether this object uses GUI functions or text script
boolean IsInitiallyOff // Represents an inverse value of 'Initially On' flag
boolean RunOnMapIni // Represents the 'Run on Map Initialization' flag
integer ParentId // Id of an object that this object is child of
integer FunctionCount // Non-GUI triggers always have zero. Represents how many ECA functions this object contains
struct Function[n] Functions // ECA functions of the GUI trigger. Number 'n' matches 'FunctionCount'
Notes:
- IsComment is always true for Trigger Comment.
- ObjectId ends with different byte based on type of object:
- GUI trigger:
0x03
- Trigger Comment:
0x04
- Custom script:
0x05
- GUI trigger:
- IsCustomText will always be true for Custom Script and always false for Trigger Comment. GUI trigger will switch value based on whether it was or was not converted to Custom Text trigger.
Raw bytes extracted from wtg:
Code:
0x:
10 00 00 00 53 70 65 6c 6c 20 44 65 73 63 72 69
70 74 69 6f 6e 73 00 6c 6f 72 65 6d 20 69 70 73
75 6d 00 01 00 00 00 01 00 00 04 01 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00
00 00 00 20 00 00 00 54 65 73 74 20 53 63 72 69
70 74 00 00 00 00 00 00 04 00 00 05 00 00 00 00
01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02
00 00 00 00
Rich (BB code):
[10 00 00 00] // objectType is 'Trigger Comment', so TRIGGER struct follow
[53 70 65 6c 6c 20 44 65 73 63 72 69 70 74 69 6f 6e 73 00] // ascii characters for 'Spell Descriptions' terminated by null \0
[6c 6f 72 65 6d 20 69 70 73 75 6d 00] // ascii characters for 'lorem ipsum' terminated by null \0
[01 00 00 00] // isComment = true
[01 00 00 04] // objectId
[01 00 00 00] // isEnabled ... doesn't matter for trigger comment
[00 00 00 00] // isCustomText ... doesn't matter for trigger comment
[00 00 00 00] // isInitiallyOff ... doesn't matter for trigger comment
[00 00 00 00] // runOnMapIni ... doesn't matter for trigger comment
[00 00 00 02] // parentId, it ends with 0x02 so the parent is a category/folder
[00 00 00 00] // functionCount is 0 (and will always be zero for comments)
[20 00 00 00] // objectType is 'Custom Script', so TRIGGER struct follow
[54 65 73 74 20 53 63 72 69 70 74 00] // ascii characters for 'Test Script' terminated by null \0
[00] // this object has no comment written in it, since it is just the null value \0
[00 00 00 00] // isComment = false, meaning you can write jass/lua code inside
[04 00 00 05] // objectId
[00 00 00 00] // isEnabled = 0, so this custom script is disabled in the editor
[01 00 00 00] // isCustomText = 1, so you can write code instead of picking GUI functions
[00 00 00 00] // isInitiallyOff = 0, meaning this is enabled from map start
[00 00 00 00] // runOnMapIni = 0
[00 00 00 02] // parentId, it ends with 0x02 so the parent is a category/folder
[00 00 00 00] // functionCount is 0 (and will always be zero for custom script)
Important:
GUI triggers contain only top-level functions, they never contain nested functions. Take the
If / Then / Else, Multiple function
action as an example:-
Example Trigger
-
Events
-
Conditions
-
Actions
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
-
IsEnabled Equal to True
-
-
Then - Actions
-
Wait 5.22 seconds
-
-
Else - Actions
-
Skip remaining actions
-
-
-
-
If / Then / Else, Multiple function
. Although the function itself contains multiple nested functions, the Trigger object will have FunctionCount
equal 1
and describe only function If / Then / Else, Multiple function
. The struct of the top-level function will describe the nested functions it contains.(ECA) Function
Represents a single event, condition or action function.
Rich (BB code):
integer Type // Type of the function. See table below for mapping.
integer BlockId // For nested functions only; top-level functions do not have this! It is the id of the function block this function belongs to
string FunctionName // The internal identifier of the function
boolean IsEnabled // Whether this function is enabled or not. Disabled functions in GUI have greyed-out text and red cogwheel for icon
struct Parameter[n] Parameters // Parameters of the function. The number `n` can only be from triggerdata.txt
integer NestedFunctionCount // Number of nested functions
struct Function[n] NestedFunctions // Nested ECA functions of this function. Number `n` matches `NestedFunctionCount`
Type value | Name/meaning |
---|---|
0 | Event |
1 | Condition |
2 | Action |
Let's take a very simple example. The ECA function is
-
Events
-
Map initialization
-
Code:
0x:
00 00 00 00 4d 61 70 49 6e 69 74 69 61 6c 69 7a
61 74 69 6f 6e 45 76 65 6e 74 00 01 00 00 00 00
00 00 00
Rich (BB code):
[00 00 00 00] // type is 'event'
[] // this is a top level function, so blockId is missing
[4d 61 70 49 6e 69 74 69 61 6c 69 7a 61 74 69 6f 6e 45 76 65 6e 74 00] // ascii characters for 'MapInitializationEvent' terminated by \0
[01 00 00 00] // is enabled: true
[] // based on triggerdata.txt, this function has no parameters, so it is missing
[00 00 00 00] // nested function count: zero
[] // no nested functions, so it is missing
Note about function blocks
Before digging deeper, let's discuss function blocks first. GUI triggers and some ECA functions can contain functions. In GUI trigger editor, you will usually see the block as a node. Each GUI trigger has the following block nodes:-
Events
-
Conditions
-
Actions
-
Loop - Actions
-
If - Conditions
-
Then - Actions
Rich (BB code):
// Functions with a single 'Conditions' block
AndMultiple
OrMultiple
// Functions with a single 'Loop - Actions' block
ForLoopAMultiple
ForLoopBMultiple
ForLoopVarMultiple
EnumDestructablesInRectAllMultiple
EnumDestructablesInCircleBJMultiple
EnumItemsInRectBJMultiple
ForForceMultiple
ForGroupMultiple
// Function with three blocks: 'If - Conditions', 'Then - Actions' and 'Else - Actions'
IfThenElseMultiple
Also, blocks are stored in reverse order inside WTG files. So a GUI trigger will first contain list of Actions, then Conditions and finally Events. However ECA functions inside each block are stored in order we see them, from top to bottom. For example consider the following trigger:
-
Example Trigger
-
Events
-
Unit - A unit Dies
-
-
Conditions
-
(Unit-type of (Triggering unit)) Equal to Footman
-
(Owner of (Triggering unit)) Equal to Player 1 (Red)
-
-
Actions
-
Special Effect - Create a special effect at (Position of (Triggering unit)) using ...
-
Unit - Explode (Triggering unit).
-
-
-
Special Effect - Create a special effect at (Position of (Triggering unit)) using ...
-
Unit - Explode (Triggering unit).
-
(Unit-type of (Triggering unit)) Equal to Footman
-
(Owner of (Triggering unit)) Equal to Player 1 (Red)
-
Unit - A unit Dies
- block order: from bottom to top
- functions inside block: from top to bottom
This is not really important for GUI trigger events, condtions and actions, since you can easily determine that by their
Type
field, however it is good to know when it comes to If / Then / Else, Multiple function
function since it follows same rules: First are stored actions of Else - Actions block, then actions of the Then - Actions block and finally conditions of the If - Conditions block.You can use the
BlockId
field to determine the block, since these values are static:BlockId value | Block name |
---|---|
0 | If - Conditions |
1 | Then - Actions |
2 | Else - Actions |
For ECA functions that contain only a single block, like
Loop - Actions
block of Pick Every Unit In Unit Group And Do Multiple Actions
action, the BlockId will be present and its value will always be zero.Parameter
Represents a single parameter of an ECA function.
Rich (BB code):
integer Type // Type of the parameter. See table below for mapping.
string Value // Value of the parameter. It is always stored as string even in cases where the value is a number
boolean SupportsParameters // Only applicable to parameters of type 'function'. If value is 'true', check triggerdata.txt to determine parameters
struct Parameter[n] Parameters // Parameters of this parameter. Number 'n' can be determined from triggerdata.txt
boolean IsArray // Usable only for parameters of type 'variable', for others this is always false.
integer ArrayIndexValue // Only applicable if 'IsArray' is true. If 'IsArray' is false, this field will not be present in the struct
Type value | Name/meaning |
---|---|
0 | Preset |
1 | Variable |
2 | Function |
3 | String |
-1 | Invalid |
Let's look at the following action:
-
Actions
-
Set VariableSet x = y
-
Code:
0x:
02 00 00 00 53 65 74 56 61 72 69 61 62 6c 65 00
01 00 00 00 01 00 00 00 78 00 00 00 00 00 00 00
00 00 01 00 00 00 79 00 00 00 00 00 00 00 00 00
00 00 00 00
Rich (BB code):
[02 00 00 00] // function type is 'action'
[53 65 74 56 61 72 69 61 62 6c 65 00] // ascii characters for 'SetVariable' terminated by \0
[01 00 00 00] // is enabled: true
-- Start of Parameters: // triggerdata.txt shows two parameters
first parameter:
[01 00 00 00] // parameter type is 'variable'
[78 00] // ascii characters for 'x' terminated by \0
[00 00 00 00] // supports parameters: false
[] // this has no parameters, so that struct is missing
[00 00 00 00] // is array: false
[] // missing since this is not an array
second parameter:
[01 00 00 00] // parameter type is 'variable'
[79 00] // ascii characters for 'y' terminated by \0
[00 00 00 00] // supports parameters: false
[] // this has no parameters, so that struct is missing
[00 00 00 00] // is array: false
[] // missing since this is not an array
--End of parameters
[00 00 00 00] // nested function count: zero
[] // no nested functions, so it is missing
WrapperParameter
This is a special type of parameter. I haven't seen anyone addressing this, nor am I sure since which version was such structure present. It may have been an accident, or a never finished work-in-progress on Blizzard's side, however I have seen this come up consistently during my investigations.This stucture replaces the default
Parameter
structure whenever the type of parameter (as read from triggerdata.txt) is boolexpr
, boolcall
or code
.Simply put, it is a parameter with static values that wraps an ECA function.
Rich (BB code):
integer Type // Type of the parameter. Value is always '2' (Function)
string Value // For 'boolexpr' and 'boolcall', this is always empty string, for 'code' this is always 'DoNothing'
integer FunctionCount // This seems to always be '1'
struct Function[n] Functions // ECA functions. Number 'n' matches FunctionCount'
boolean IsArray // Is always 'false'
WrapperParameter
for Parameter
struct, since their format is very similar, however I believe thinking of this in terms of ECA Function struct makes more sense. A boolexpr
's Parameter would be of type 1
, which would be Variable
, but if we think of this as ECA Function, the type would match that of Condition
. Same for code
, where its parameter would be of type 2
(function), but in terms of ECA Function it would be of type Action
.Another reason is that any argument for parameter of boolexpr, boolcall and code can appear as a standalone ECA function, so it makes sense that the structure would be kept same even when such functions are inlined into different function.
We will look at the following action:
-
Actions
-
If (IsEnabled Equal to True) then do (Wait 5.22 seconds) else do (Skip remaining actions)
-
Code:
0x:
02 00 00 00 49 66 54 68 65 6e 45 6c 73 65 00 01
00 00 00 02 00 00 00 00 01 00 00 00 01 00 00 00
4f 70 65 72 61 74 6f 72 43 6f 6d 70 61 72 65 42
6f 6f 6c 65 61 6e 00 01 00 00 00 01 00 00 00 49
73 45 6e 61 62 6c 65 64 00 00 00 00 00 00 00 00
00 00 00 00 00 4f 70 65 72 61 74 6f 72 45 71 75
61 6c 45 4e 45 00 00 00 00 00 00 00 00 00 03 00
00 00 74 72 75 65 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 02 00 00 00 44 6f 4e 6f 74
68 69 6e 67 00 01 00 00 00 02 00 00 00 54 72 69
67 67 65 72 53 6c 65 65 70 41 63 74 69 6f 6e 00
01 00 00 00 03 00 00 00 35 2e 32 32 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00
00 44 6f 4e 6f 74 68 69 6e 67 00 01 00 00 00 02
00 00 00 52 65 74 75 72 6e 41 63 74 69 6f 6e 00
01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Rich (BB code):
[02 00 00 00] // type is 'action'
[] // this is a top level function, so blockId is missing
[49 66 54 68 65 6e 45 6c 73 65 00] // ascii characters for 'IfThenElse' terminated by \0
[01 00 00 00] // is enabled: true
--Start of Parameters: // from triggerdata.txt we know it has three params: boolexpr, code and code
first param: // param type is 'boolexpr', so WrapperParameter struct follows
[02 00 00 00] // type is 'function'
[00] // value is empty string
[01 00 00 00] // function count: one
--Start of wrapped ECA Function
[01 00 00 00] // type is 'condition'
[] // this is not a nested function, so blockId is missing
[4f 70 65 72 61 74 6f 72 43 6f 6d 70 61 72 65 42 6f 6f 6c 65 61 6e 00] // ascii characters for 'OperatorCompareBoolean' terminated by \0
[01 00 00 00] // is enabled: true
--Start of Parameters: // from triggerdata.txt we know that it has three params: boolean, EqualNotEqualOperator and boolean
first param:
[01 00 00 00] // type is 'variable'
[49 73 45 6e 61 62 6c 65 64 00] // ascii characters for 'IsEnabled' terminated by \0
[00 00 00 00] // supports parameters: false
[] // this has no parameters, so that struct is missing
[00 00 00 00] // is array: false
[] // missing since this is not an array
second param:
[00 00 00 00] // type is 'preset'
[4f 70 65 72 61 74 6f 72 45 71 75 61 6c 45 4e 45 00] // ascii characters for 'OperatorEqualENE' terminated by \0
[00 00 00 00] // supports parameters: false
[] // this has no parameters, so that struct is missing
[00 00 00 00] // is array: false
[] // missing since this is not an array
third param:
[03 00 00 00] // type is 'string'
[74 72 75 65 00] // ascii characters for 'true' terminated by \0
[00 00 00 00] // supports parameters: false
[] // this has no parameters, so that struct is missing
[00 00 00 00] // is array: false
[] // missing since this is not an array
--End of parameters
[00 00 00 00] // nested function count: zero
[] // no nested functions, so it is missing
--End of wrapped ECA Function
[00 00 00 00] // is array: false
second param: // param type is 'code', so WrapperParameter struct follows
[02 00 00 00] // type is 'function'
[44 6f 4e 6f 74 68 69 6e 67 00] // ascii characters for 'DoNothing' terminated by \0
[01 00 00 00] // function count: one
--Start of wrapped ECA Function
[02 00 00 00] // type is 'action'
[] // this is not a nested function, so blockId is missing
[54 72 69 67 67 65 72 53 6c 65 65 70 41 63 74 69 6f 6e 00] // ascii characters for 'TriggerSleepAction' terminated by \0
[01 00 00 00] // is enabled: true
--Start of Parameters: // from triggerdata.txt we know that 'TriggerSleepAction' has single param: real
first param:
[03 00 00 00] // type is 'string'
[35 2e 32 32 00] // ascii characters for '5.22' terminated by \0
[00 00 00 00] // supports parameters: false
[] // this has no parameters, so that struct is missing
[00 00 00 00] // is array: false
[] // missing since this is not an array
--End of parameters
[00 00 00 00] // nested function count: zero
[] // no nested functions, so it is missing
--End of wrapped ECA Function
[00 00 00 00] // is array: false
third param: // param type is 'code', so WrapperParameter struct follows
[02 00 00 00] // type is 'function'
[44 6f 4e 6f 74 68 69 6e 67 00] // ascii characters for 'DoNothing' terminated by \0
[01 00 00 00] // function count: one
--Start of wrapped ECA Function
[02 00 00 00] // type is 'action'
[] // this is not a nested function, so blockId is missing
[52 65 74 75 72 6e 41 63 74 69 6f 6e 00] // ascii characters for 'ReturnAction' terminated by \0
[01 00 00 00] // is enabled: true
[] // from triggerdata.txt we know that 'ReturnAction' has no params
[00 00 00 00] // nested function count: zero
--End of wrapped ECA Function
[00 00 00 00] // is array: false
--End of parameters
[00 00 00 00] // nested function count: zero
[] // no nested functions, so it is missing
NestedParameter
This may be a bug, or an attempt to enforce parameter encapsulation in round brackets when shown in Trigger Editor, e.g. encapsulation in this condition:
Code:
((Triggering unit) is A structure) Equal to True
function
, unless the WrapperParameter custom structure is used. The structure completely matches the Parameter
struct, but it is populated in the following way:- ParameterType: 2 (function)
- Value: name of the function
- SupportsParameters: always
true
- Parameters: this is always present and contains a single object - the actual parameter, but with a twist (see below).
- IsArray: always
false
- The parameter has correct
Type
andValue
, but never contains its own parameters as defined intriggerdata.txt
. - The nested parameter is always present, has exactly same
Value
, but is always of Type3
(string)! - Although the nested parameter is of Type
3
, it actually contains its own parameters, as defined intriggerdata.txt
!
Let's look at the following action:
-
Actions
-
Destructible - Pick every destructible in (Playable map area) and do (Destructible - Kill (Picked destructible))
-
Code:
0x:
02 00 00 00 45 6e 75 6d 44 65 73 74 72 75 63 74
61 62 6c 65 73 49 6e 52 65 63 74 41 6c 6c 00 01
00 00 00 02 00 00 00 47 65 74 50 6c 61 79 61 62
6c 65 4d 61 70 52 65 63 74 00 01 00 00 00 03 00
00 00 47 65 74 50 6c 61 79 61 62 6c 65 4d 61 70
52 65 63 74 00 01 00 00 00 00 00 00 00 00 00 00
00 02 00 00 00 44 6f 4e 6f 74 68 69 6e 67 00 01
00 00 00 02 00 00 00 4b 69 6c 6c 44 65 73 74 72
75 63 74 61 62 6c 65 00 01 00 00 00 02 00 00 00
47 65 74 45 6e 75 6d 44 65 73 74 72 75 63 74 61
62 6c 65 00 01 00 00 00 03 00 00 00 47 65 74 45
6e 75 6d 44 65 73 74 72 75 63 74 61 62 6c 65 00
01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
Rich (BB code):
[02 00 00 00] // type is 'action'
[] // this is a top level function, so blockId is missing
[45 6e 75 6d 44 65 73 74 72 75 63 74 61 62 6c 65 73 49 6e 52 65 63 74 41 6c 6c 00] // ascii characters for 'EnumDestructablesInRectAll' terminated by \0
[01 00 00 00] // is enabled: true
--Start of Parameters: // from triggerdata.txt we know it has two params: rect and code
first param:
[02 00 00 00] // type is 'function'
[47 65 74 50 6c 61 79 61 62 6c 65 4d 61 70 52 65 63 74 00] // ascii characters for 'GetPlayableMapRect' terminated by \0
[01 00 00 00] // supports parameters: true
--Start of Parameters: // since this is param of type 'function', the NestedParameter struct is used, it will have a single param
first param:
[03 00 00 00] // type is 'string'
[47 65 74 50 6c 61 79 61 62 6c 65 4d 61 70 52 65 63 74 00] // ascii characters for 'GetPlayableMapRect' terminated by \0
[01 00 00 00] // supports parameters: true
[] // based on triggerdata.txt, this has no parameters
[00 00 00 00] // is array: false
[] // missing since this is not an array
--End of parameters
[00 00 00 00] // is array: false
[] // missing since this is not an array
second param: // param type is 'code', so WrapperParameter struct follows
[02 00 00 00] // type is 'function'
[44 6f 4e 6f 74 68 69 6e 67 00] // ascii characters for 'DoNothing' terminated by \0
[01 00 00 00] // function count: one
--Start of wrapped ECA Function
[02 00 00 00] // type is 'action'
[] // this is not a nested function, so blockId is missing
[4b 69 6c 6c 44 65 73 74 72 75 63 74 61 62 6c 65 00] // ascii characters for 'KillDestructable' terminated by \0
[01 00 00 00] // is enabled: true
--Start of Parameters: // from triggerdata.txt we know it has single param: destructable
first param:
[02 00 00 00] // type is 'function'
[47 65 74 45 6e 75 6d 44 65 73 74 72 75 63 74 61 62 6c 65 00] // ascii characters for 'GetEnumDestructable' terminated by \0
[01 00 00 00] // supports parameters: true
--Start of Parameters: // since this is param of type 'function', the NestedParameter struct is used, it will have a single param
first param:
[03 00 00 00] // type is 'string'
[47 65 74 45 6e 75 6d 44 65 73 74 72 75 63 74 61 62 6c 65 00] // ascii characters for 'GetEnumDestructable' terminated by \0
[01 00 00 00] // supports parameters: true
[] // based on triggerdata.txt, this has no parameters
[00 00 00 00] // is array: false
[] // missing since this is not an array
--End of parameters
[00 00 00 00] // is array: false
[] // missing since this is not an array
--End of parameters
[00 00 00 00] // nested function count: zero
[] // no nested functions, so it is missing
--End of wrapped ECA Function
[00 00 00 00] // is array: false
[] // missing since this is not an array
--End of parameters
[00 00 00 00] // nested function count: zero
[] // no nested functions, so it is missing
Let's look at the following action:
-
Actions
-
Set VariableSet i = (i + 15)
-
Code:
0x:
02 00 00 00 53 65 74 56 61 72 69 61 62 6c 65 00
01 00 00 00 01 00 00 00 69 00 00 00 00 00 00 00
00 00 02 00 00 00 4f 70 65 72 61 74 6f 72 49 6e
74 00 01 00 00 00 03 00 00 00 4f 70 65 72 61 74
6f 72 49 6e 74 00 01 00 00 00 01 00 00 00 69 00
00 00 00 00 00 00 00 00 00 00 00 00 4f 70 65 72
61 74 6f 72 41 64 64 00 00 00 00 00 00 00 00 00
03 00 00 00 31 35 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00
Rich (BB code):
[02 00 00 00] // type is 'action'
[] // this is a top level function, so blockId is missing
[53 65 74 56 61 72 69 61 62 6c 65 00] // ascii characters for 'SetVariable' terminated by \0
[01 00 00 00] // is enabled
--Start of Parameters: // from triggerdata.txt we know it has two params: AnyGlobal and Null
first param:
[01 00 00 00] // type is 'variable'
[69 00] // ascii characters for 'i' terminated by \0
[00 00 00 00] // supports parameters: false
[] // params
[00 00 00 00] // is array: false
[] // missing since this is not an array
second param:
[02 00 00 00] // type is 'function'
[4f 70 65 72 61 74 6f 72 49 6e 74 00] // ascii characters for 'OperatorInt' terminated by \0
[01 00 00 00] // supports parameters: true
--Start of Parameters: // since this is param of type 'function', the NestedParameter struct is used, it will have a single param
first param:
[03 00 00 00] // type is 'string'
[4f 70 65 72 61 74 6f 72 49 6e 74 00] // ascii characters for 'OperatorInt' terminated by \0
[01 00 00 00] // supports parameters: true
--Start of Parameters: // from triggerdata.txt we know it has three params: integer, ArithmeticOperator and integer
first param:
[01 00 00 00] // type is 'variable'
[69 00] // ascii characters for 'i' terminated by \0
[00 00 00 00] // supports parameters: false
[] // params
[00 00 00 00] // is array: false
[] // missing since this is not an array
second param:
[00 00 00 00] // type is 'preset'
[4f 70 65 72 61 74 6f 72 41 64 64 00] // ascii characters for 'OperatorAdd' terminated by \0
[00 00 00 00] // supports parameters: false
[] // params
[00 00 00 00] // is array: false
[] // missing since this is not an array
third param:
[03 00 00 00] // type is 'string'
[31 35 00] // ascii characters for '15' terminated by \0
[00 00 00 00] // supports parameters: false
[] // params
[00 00 00 00] // is array: false
[] // missing since this is not an array
--End of Parameters
[00 00 00 00] // is array: false
[] // missing since this is not an array
--End of Parameters
[00 00 00 00] // is array: false
[] // missing since this is not an array
--End of Parameters
[00 00 00 00] // nested function count: zero
[] // no nested functions, so it is missing
Closing words
Hopefully this will be found helpful. If you see something incorrect or missing, let me know and I will try to update the post.
Last edited: