Navigation:
INDEX | PREVIOUS CHAPTER

10. Working with Object Editor Data

I think most people will use GMSI to alter Object Editor data, like creating tooltips, setting the Hitpoints of creepwaves for Tower Defenses, enumerating the units in their point value, setting the button positions of buildings sorted by their cost and such stuff. In addition, there are some things you should know when working with such data.So I grant a whole section just for Object Editor Data.

I just want to remember you, that you have the option to load all objects or only the ones that contain changes (pink names in the Object Editor). By default all objects are loaded but you can use the extended loadMap function to just load changed objects. See: Loading and Saving Maps with wc3MapIO.gsl

If you don't load all objects, you CANNOT access details of not loaded objects. However, if your map only uses changed stuff, then you can do it, since you will hopefully never come into a situation where you have to read from (or write to) unchanged objects.

As you can see in the definitions there is:

array<Wc3obj> objects = null; //The World Editor objects of this map (unis, abilities,...)
in the map's definition.

This is an array that is restricted to the type Wc3Obj. This array contains all Object Editor objects. Lets see the definition for this type and its subtypes:

typedef Wc3obj extends array<string,float,int>; //A wc3 object editor object typedef Ability extends Wc3obj; typedef Unit extends Wc3obj; typedef Doodad extends Wc3obj; typedef Destructable extends Wc3obj; typedef Upgr extends Wc3obj; typedef Item extends Wc3obj; typedef Buff extends Wc3obj;
So as we can see a Wc3obj is just an array that is restricted to the types string, int and float, since these are the only three types that Object Editor fields can contain. Note that booleans in the Object Editor are just ints with 1 = true and 0 = false. Next thing we see is that there are 7 types derived from Wc3obj. These are the actual types of Object Editor objects. So a Unit in the OE is stored as type Unit in the array, an Ability in the OE is stored as type Ability in the array and so on.

For the index in the objects array the Object ID is used. This is the string that you see when using "show raw values" in your Object Editor. So, for example the peasant has the index "hpea". You can use struct and array syntax for adressing objects (like in all arrays with string keys). For iterating over objects using the foreach loop, you will have to use array syntax, in all other cases, struct syntax looks more natural, beautiful and is easier to understand (at least I think so).

Some examples:

Map map = loadMap(...); Wc3Object peasant = map.objects.hpea; //The peasant with struct syntax peasant = map.objects["hpea"]; //The peasant with array syntax. (ugly^^)

All attributes an Object Editor object has are stored in it with their raw names (the names they have when you use "show raw values" in your Object Editor). So, as an example we can access the name of our peasant by checking how this field is raw named in the OE. As we ca<n lookup there, it is "Name" (Warning: Case sensitive! Some OE values are written with a starting capital letter, others are not). So we can for example set the peasants name with:

Map map = loadMap(...); map.objects.hpea.Name = "Our peasant"; //The peasant with struct syntax map.objects["hpea"]["Name"] = "Our peasant"; //The peasant with array syntax. (ugly^^) map.objects["hpea"].name = "Our peasant"; //Of course you can mix struct and array syntax
I use the different syntaxes here again, that you get used to both, because you will see both somewhere and should know what they mean.

As said in the last chapter object attributes are accessed using the raw names. However, there are some attributes in the Object Editor, that appear twice. The most prominent Example is the Buttonposition. If you check the raw values for x and y Buttonposition, you will see that they re both named "Buttonpos". GMSI, has to know which one you mean if you access "Buttonpos", so it was made like this: The first value is just called as it is called in the Editor, so "Buttonpos" will refer to the X button position. All other appearances are numerated starting with 1, so "Buttonpos1" would be the Y buttonposition. Example:

Map map = loadMap(...); map.objects.hpea.Buttonpos = 1; //alter the x buttonpos of the peasant map.objects.hpea.Buttonpos1 = 1; //alter the y buttonpos of the peasant
I know that this syntax is very dissatisfying. Maybe I will code something new some day. However, if you are very familiar with the file format of these files, this notation is consistent. See this example, that you know what I mean: Check a three level upgrade in your Object Editor. As you can see the field "Requires" is enumerated like explained above. However, "Art" isn't enumerated like that. Instead, it is just three times the word "Art". So GMSI enumerates "Art" just like "Requires". This adds some consistency, where Blizzard has none.

Here is a (maybe incomplete) list of these "double attributes":

General:

  • xxxButtonpos (all Buttonpositions "Buttonpos", "UnButtonpos","Researchbuttonpos"...)
Units:
  • Missilearc
  • Missileart
  • MissileHoming
  • Missilespeed
Abilities:
  • Tip
  • Untip
  • Ubertip
  • Unubertip
Upgrades:
  • Tip
  • Ubertip
  • Art
  • Hotkey
  • Name
  • EditorSuffix

Since all objects are put into one array, you might want to know if the Wc3obj you have is a Unit or an Ability or whatever. This can be checked, since the objects are of different types, remember the type declaration:

typedef Ability extends Wc3obj; typedef Unit extends Wc3obj; typedef Doodad extends Wc3obj; typedef Destructable extends Wc3obj; typedef Upgr extends Wc3obj; typedef Item extends Wc3obj; typedef Buff extends Wc3obj;
So, we can use the instanceof operator to check if an object is of the type we want.

Example:

Map map = loadMap(...); Wc3Object peasant = map.objects.hpea; if(peasant instanceof Unit){ echo("The peasant is a unit"); //Will be displayed since the peasant is of type Unit. }

There is some information besides of type and attributes of an array, you might want to get or even alter. The library "objects.gsl" in the lib folder contains many functions for Object Editor data handling. Check the library for all functions, the comments above each function tell you exactly how to use it.

Here are some examples of functions in the library:

Getting object IDs:

string getObjectId(Wc3obj obj) //gets the ID of this object string getBaseId(Wc3obj obj) //gets the ID of the object this object is derived from

Checking object IDs:

bool isIdValid(string objId) //checks if an object ID is valid bool isIdPredefined(string objId) //checks if an object ID belongs to a predefined object bool isIdCustom(string objId) //checks if an object ID belongs to a user defined object

Creating and altering objects:

object setParentObject(Wc3obj toChange, Wc3obj newId, bool overwrite) //sets the object <toChange> is derived from Wc3obj createObject (Map mapHandle, string baseId) //creates an object, which is derived from <baseId> and returns it

Example: We want to check if an object is a user defined oject:

Wc3obj obj = ...; if(isIdCustom(getObjectId(obj))){ echo("This object is custom!"); }

Note: instead of using the function getObjectId(Wc3obj), you can also use the string you got from iterating. However, if you have only the object, you need getObjectId.

There are some other things you can do, without using the library:

Changing the ID of an object

You can change the ID of an object by just entering it into another entry in the map.objects array. However, you then have to unset the the old entry or the unit will be in the map twice. Example:

map.objects.h123 = map.objects.h001; //Sets the object with ID "h001" also as object with ID "h123" unset(map.objects.h001); //Removes the old link, now the object has only the new ID "h123"
Note that the getObjectId function will always get the ID of the object when it was loaded. So if you "rename" objects like that, you cannot use the getObjectId function for them anymore, because it will still return the old value.

Checking if an object contains any changes

Sometimes you want to know if an object is "changed", i.e. if it contains any attribute changes and is showed pink in the Object Editor. This is very simple. Just check the [ref=]size[/ref] of the object. If it is 0, then the object has no changes, if it is > 0 the object contains changes.

Example: checking if the peasant has changes:

if(size(map.objects.hpea)>0){ echo("The peaseant has changed attributes!"); }

Deleting an object

This is simple and works for everything, not only for objects, but also for triggers, rects,... . If you want to delete something from your map, just unset it. Then it is removed from the struct/array and not saved when the map is saved.

unset(map.objects.h001); //Deletes the unit with ID h001 from the map

The basis for the iteration over Object Editor attributes is of course the foreach loop, since it allows you to iterate over arrays like the "map.objects" array. Since you more or less never want to iterate over all objects of your map, you should use the information you gathered in the previous chapters to filter the objects returned by the foreach loop. You can do for example these restrictions:

  • Picking only Objects of one type (like Units) by using the instanceof operator.
  • Picking only user defined objects using functions like isIdCustom
  • Picking only objects where a current attribute matches a condition, like only units of the Undead race
  • ...

Example: Iterating over all Units, which are custom and of the undead race:

Map map = loadMap(...); for(string s: map.objects){ Wc3obj obj = map.objects[s]; //Retrieving the object if(!(obj instanceof Unit)) continue; //Object is no unit? then skip! if(isIdPredefined(s)) continue; //Object is not custom but predefined? then skip! if(obj.race != "undead") continue; //Race is not undead? then skip! //Do something, for example: echo("The object with ID " + s + " is a custom unit belonging to the undead race"); }

There are some links between objects in the Object Editor. For example a unit has an ability list or a trained units list. For many different purposes you want to access these. An example would be tooltip creation: You want to add all ability names to the tooltip of the unit that has them.

This is a very easy job. If you look at the raw value of these ability- / unit- / effect- / whatever- lists, you see that they just contain the IDs of the linked objects seperated by commas. As you should still remember you can iterate over these comma separated lists with the foreach loop just like iterating over the string keys of an array. So all you have to do then, is retrieving the object with that ID and using its values for whatever you need them.

Here is an example: We want to output the names of all abilities of the peasant:

Map map = loadMap(...); string list = map.objects.hpea.abilList; //Retrieving the abil list of the peasant for(string currentID: list){ //Iteration over all IDs in the list Wc3obj curAbil = map.objects[currentID]; //Retrieving the ability that belongs to the ID echo("Here is an ability of the peasant:" + curAbil.Name + "\n"); //Outputting the name }

Navigation:
INDEX | PREVIOUS CHAPTER