- Joined
- Nov 11, 2006
- Messages
- 7,649
Lua Object Generation
Disclaimer: This tutorial was written for patch 1.26, prior to Lua being introduced as a scripting language option in Reforged. If you would like to run a legacy Lua script and it is not working for your reforged map, it may be worth trying to run the script on a separate map (using an older patch/set of tools) and then importing the generated object data into your reforged map.
Description:
What is Lua?
Lua is a programming language that was made to be light, simple, and flexible. It was made to be embeddable into application programs to create a simple interface for scripting, and that is exactly what it will serve as for this tutorial.
How does it relate to Warcraft III?
With the introduction of jAPI, by xttocs, came a long line of hacks for Warcraft III. Thanks to PipeDream and PitzerMike, Jass NewGen Pack now has a pack of extensions integrated into its editor which enables several unique features for map making. The one we'll be looking at is the Object Merger.
What is the Object Merger?
The object merger is a plugin that allows you to manage object data between maps as well as create scripts that will automatically generate objects. By objects, I mean those available in the object editor; units, items, destructables, doodads, abilities, buffs, and upgrades. While standard object data editing can prove to be faster, the object merger can make implementation a breeze for systems which require custom object data, and it can allow mass production of objects very quickly.
Note that the GrimExt tools can only be used for editing. They are not able to be used during the game, only for ease of map making and implementation.
Requirements:
- Jass NewGen Pack
- I recommend that you download this hotfix for GrimExt that allows the ObjectMerger to work properly on certain fields that involve checkboxes. [Link]
- Basic knowledge of the Object Editor.
- Make sure you have the object editor hack enabled. (Grimoire -> Enable object editor hack)
Table of Contents:
- Part I - Setup
- Part II - The Object Merger
- Part III - Integrating Lua
- Part IV - Multiple Objects
- Part V - Advanced Lua
- Part VI - Lua Scripts
- Part VII - GetVarObject
- Part VIII - Writing Custom Lua Scripts
- FAQ
- Known Bugs
Part I - Setup
There may be many questions running through your head at this point. How do you access Lua scripting? Where do you put the code? How do you generate the data?
We will be doing all of our scripting in the Trigger Editor. First, I recommend you create a new trigger. Go to Edit -> Convert to Custom Text.

Erase all the code that appears. It should be completely blank.

Part II - The Object Merger
Now let's get started! First of all, what let's the compiler know that you are not scripting normal JASS anymore? This is it:
JASS:
//!
JASS:
//! external <TOOL_NAME> <ARGUMENTS>
For the object merger, "<TOOL_NAME>" will be replaced with "ObjectMerger".
JASS:
//! external ObjectMerger
Now we must input the arguments. As explained before, the object merger can be used to generate objects. However, the syntax is a little complicated. Open the object editor and you will notice that there are several object types: units, items, destructables, doodads, abilities, buffs, and upgrades. Each type has a rawcode that is associated with it:
- w3u Units
- w3t Items
- w3b Destructables
- w3d Doodads
- w3a Abilities
- w3h Buffs
- w3q Upgrades
This will tell the object merger what we are going to make. In this example, I will tell the compiler that I am going to make an ability.
JASS:
//! external ObjectMerger w3a
Each space represents the starting of a new argument. In this case, the first argument we put is the object type, w3a (abilities).
The next argument will be the rawcode of the base object and the rawcode of the object we are making. This is the basic syntax so far:
JASS:
//! external ObjectMerger <OBJECT TYPE> <BASE OBJECT ID> <CUSTOM OBJECT ID>
If the custom object ID is already used, then it will replace the object using that ID. For example, let's say we want to base an ability off of Aerial Shackles.

Our concern is for
'Amls'
, also known as the rawcode ID of the object. We will replace <BASE OBJECT ID> with this.
JASS:
//! external ObjectMerger w3a Amls <CUSTOM OBJECT ID>
Note that there is no ' ' denotation for these rawcodes. The next argument determines the custom object ID, which will be the rawcode ID of the object we will be creating. I recommend you use a unique one, but it can be anything you'd like. In this example, I'll use
'A000'
.
JASS:
//! external ObjectMerger w3a Amls A000
Next, open the object in the object editor and you'll notice it has several fields. These fields modify a certain aspect of an object, whether it be its scaling or its name.

The first part we want to pay attention to is the left column. You might notice that each field will have a 4 characters following the field name enclosed in parenthesis. This is the rawcode of the field.

When creating objects, we can modify fields by inputting the rawcode of the field and following it with its corresponding value. The syntax for the object creation so far will be:
JASS:
//! external ObjectMerger <OBJECT TYPE> <BASE OBJECT ID> <CUSTOM OBJECT ID> <FIELD ID 1> <FIELD VALUE 1> <FIELD ID 2> <FIELD VALUE 2> ...
The first thing you will probably want to modify is the name. The rawcode of this field is
'anam'
, so it would look like this:
JASS:
//! external ObjectMerger w3a Amls A000 anam
When we are entering the name, we will enclose it in quotations
""
. You will input most things in quotations, aside from integers and reals. Anyway, let's name our ability Fire.
JASS:
//! external ObjectMerger w3a Amls A000 anam "Fire"
Now it gets a bit more complicated. You may get some errors from saving for certain fields if you just follow that syntax. Remember how abilities have different levels? Well, for one level, the tooltip may be something, but for another, it may be something else. This external call requires us to input the level that we want to modify before modifying the field. This will apply to only certain fields. (the ones that gain extra fields when you change the amount of levels) Even if there is only one level, you must input a level to modify. The syntax is this:
JASS:
<FIELD ID> <LEVEL> <VALUE>
For example, if I wanted to modify the targets allowed:
JASS:
//! external ObjectMerger w3a Amls A000 anam "Fire" atar 1 "Air,Enemy,Organic,Neutral"
It modifies the field for level 1 of the ability to allow "Air", "Enemy", "Organic", and "Neutral" as targets. Notice how each value is separated by a comma. Now let's finish this spell off by making it a mana cost of 15.
JASS:
//! external ObjectMerger w3a Amls A000 anam "Fire" atar 1 "Air,Enemy,Organic,Neutral" amcs 1 15
This sets the mana cost of "Fire" (Level 1) to 15. To process this script, simply save the map and reopen it. (You do not have to close the entire editor, just the map) When you reopen it, you will notice the object is there. However, you may notice that the mana cost wasn't changed. For some odd reason, the object merger may neglect the final command, so it is best to add a
|
symbol at the end to let it know that you are done.
JASS:
//! external ObjectMerger w3a Amls A000 anam "Fire" atar 1 "Air,Enemy,Organic,Neutral" amcs 1 15 |
Now you can save, reopen, and the ability should be there! You can add more and more fields, but the more you add, the more lengthy it gets. Overall, this is an ugly and long method of modifying objects. Not only that, but it is very slow as well. That is why you will learn how to do this with Lua.
Part III - Integrating Lua
Lua will allow you to create the scripts shown above, but in a readable, efficient, and more flexible manner. Not only that, but this can allow you to utilize simple functions, such as loops and variables.
First of all, Lua is created within external block commands.
JASS:
//! externalblock extension=lua ObjectMerger $FILENAME$
//All the lua script goes within this block
//! endexternalblock
This will allow you to use Lua where the comment is. However, we will not simply use
//!
, we will have to use //! i
instead, as that denotes the start of a Lua call.Creating objects in Lua has its differences with the single-line external call, but for the most part, it is very similar. First off, let us define what object type we will be creating.
JASS:
//! externalblock extension=lua ObjectMerger $FILENAME$
//! i setobjecttype("abilities")
// You can also use "units", "items", "upgrades", "buffs", or "destructables"
//! endexternalblock
In the case above, I will be creating an ability. The function
//! i setobjecttype("")
will determine what object type the succeeding code will concern.With Lua, you have a function to modify an object and a function to create an object.
JASS:
//! i modifyobject("baseID")
//! i createobject("baseID","customID")
JASS:
//! i modifyobject("Acmg")
Creating an object is the same as we did with the single-line external call. It will create a new object based off of an existing one, and provide a custom ID. For the example we did for the single line, it would be:
JASS:
//! i createobject("Amls","A000")
'A000'
based off of Aerial Shackles (Amls).After that, it is just a matter of a single function:
JASS:
//! i makechange( object , field , value )
- current - Whenever you use "createobject" or "modifyobject", current will equal the object returned. So if you created an object based off of Aerial Shackles, current would now represent that object that was created. For modifying, it is the same thing.
- custom - This refers to all custom objects of a type in the map. The type that it chooses from is determined by
//! i setobjecttype("")
. - original - This refers to all original (non-custom) objects of a type in the map. The type that it chooses from is determined by setobjectype as well.
- modded - This refers to all original objects of a type in a map that were modified. The type that it chooses from is determined by setobjectype as well.
- unmodded - This refers to all original objects of a type in a map that were not modified. The type that it chooses from is determined by setobjectype as well.
That argument will not be enclosed in quotations.
The next argument determines the rawcode ID of the field it will change. The same rules apply as that of the ones in the external call. However, the rawcode will be placed in quotes. Example:
JASS:
//! i makechange(current,"anam",value)
The next field may or may not be the level. If it has different values depending on the level, then it will have the level as a field. Otherwise, you do not include that as an argument. For example:
JASS:
//! i makechange(current,"amcs","1","15")
The final field is the actual value itself. You can always enclose this within quotations, which makes it less of a hassle to know whether to use or not to use them.
JASS:
//! i makechange(current,"anam","Fire")
Now let's recreate the ability that we made for the external call.
JASS:
//! externalblock extension=lua ObjectMerger $FILENAME$
//! i setobjecttype("abilities")
//! i createobject("Amls","A000")
//! i makechange(current,"anam","Fire")
//! i makechange(current,"atar","1","Air,Enemy,Organic,Neutral")
//! i makechange(current,"amcs","1","15")
//! endexternalblock
Simple, right? For every field you want to change, you simply add another line with the appropriate values. After you have generated the object, closed the map and reopened it (assuming all went fine), you can feel free to remove the script. Once it is in the object editor, it is fine to remove the script, because otherwise it will regenerate it each time upon saving, which can increase save times by a great deal.
Now there are still some things to note. Let's say you wanted to change the icon of the ability:
JASS:
//! externalblock extension=lua ObjectMerger $FILENAME$
//! i setobjecttype("abilities")
//! i createobject("Amls","A000")
//! i makechange(current,"anam","Fire")
//! i makechange(current,"aart","ReplaceableTextures\CommandButtons\BTNAntiMagicShell.blp")
//! i makechange(current,"atar","1","Air,Enemy,Organic,Neutral")
//! i makechange(current,"amcs","1","15")
//! endexternalblock
If you try that, you'll notice that your ability now has no icon. The thing about entering strings is that "\" counts as a character escape sequence. (look up string literals) For the backslash to be properly input, we must put a double-backslash "\\" instead:
JASS:
//! externalblock extension=lua ObjectMerger $FILENAME$
//! i setobjecttype("abilities")
//! i createobject("Amls","A000")
//! i makechange(current,"anam","Fire")
//! i makechange(current,"aart","ReplaceableTextures\\CommandButtons\\BTNAntiMagicShell.blp")
//! i makechange(current,"atar","1","Air,Enemy,Organic,Neutral")
//! i makechange(current,"amcs","1","15")
//! endexternalblock
That should work properly.
Part IV - Multiple Objects
In case you were wondering, it is possible to create multiple objects in a single block. As I said before, the function
//! i sectobjecttype("")
will modify the type for all succeeding functions. However, calling it again will override the last one and change the type for all of its succeeding functions. For things like //! i createobject("","")
or //! i modifyobject("")
, the same thing applies. Here is an example that creates one unit and one item:
JASS:
//! externalblock extension=lua ObjectMerger $FILENAME$
//! i setobjecttype("units")
//sets the object type to units
//! i createobject("hfoo","h005")
//creates a unit with ID 'h005' based off of a footman
//! i makechange(current,"unam","Soldier")
//names it "Soldier"
//! i makechange(current,"ua1c","2.32")
//sets the attack cooldown to 2.32
//! i makechange(current,"umvs","360")
//sets the movement speed to 360
//! i setobjecttype("items")
//sets the object type to items
//! i createobject("rat9","I043")
//creates an item with ID 'I043' based off of claws of attack +9
//! i makechange(current,"unam","Armor +4")
//sets the name to "Armor +4"
//! i makechange(current,"iabi","AId4")
//sets the abilities as "Item Armor Bonus (+4)"
//! i makechange(current,"ides","Increases armor by 4.")
//sets the item description
//! endexternalblock
Part V - Advanced Lua
This all seems pretty simple, but one thing is that it is kind of long. It is easy to copy and paste over and over, but is there any way to make it become more compact?
Well, yes there is. Lua is a standalone language, so it shares many of the features that normal programming languages involve.
- Variables - Lua allows you to make variables wherever you want. Unlike JASS, you do not specify a type.
JASS://! externalblock extension=lua ObjectMerger $FILENAME$ //! i local someVar = 0 //! i local otherVar = "whee" //! i lalalulu = "this works too, this is a global variable though" //! endexternalblock
JASS://! externalblock extension=lua ObjectMerger $FILENAME$ //! i myArrayVariable = {} // = {} will make the variable an array //! i myArrayVariable[1] = 3 //! i myArrayVariable[2] = "this works as well" //! endexternalblock
- Looping - For object creating, sometimes it is nicer to make just a long loop to do things for you. Of course, there are both "for" and "while" loops.
JASS://! externalblock extension=lua ObjectMerger $FILENAME$ //! i local var = 5 //! i for i=1, 5 do //! i var = var * 2 //! i end //! i var = 0 //! i while (var < 5) do //! i var = var + 1 //! i end //! endexternalblock
- Functions - You can also make functions to call to simplify things. Many people seem to just use textmacros, but functions exist, so why not use it?
JASS://! externalblock extension=lua ObjectMerger $FILENAME$ //! i function whee( argument, argument2 ) //! i argument = argument + 1 //! i return argument //! i end //! i print(tostring(whee(5))) //! endexternalblock
- If/Then/Else Blocks - These are pretty much self explanatory.
JASS://! externalblock extension=lua ObjectMerger $FILENAME$ //! i local var = math.random(0,100) //! i if var < 10 then //! i print("var is less than 10.") //! i elseif var < 60 then //! i print ("var is less than 60 but greater than or equal to 10.") //! i else //! i print ("var is greater than or equal to 60.") //! i end //! endexternalblock
For more information on Lua itself, see Programming in Lua.
All of the above can be put to use for object creation.
JASS:
//! externalblock extension=lua ObjectMerger $FILENAME$
//! i function createmonsoon(name, damage)
//! i local rawcodes = {}
//! i setobjecttype("abilities")
//! i rawcodes[1] = "A000"
//! i rawcodes[2] = "A001"
//! i rawcodes[3] = "A002"
//! i for i=1, 3 do
//! i createobject("ACmo", rawcodes[i])
//! i makechange(current, "anam", name .. " [" .. i .. "]")
//! i makechange(current, "Esf1", "1", damage)
//! i end
//! i end
//! i createmonsoon("Monsoon", 5)
//! endexternalblock
This will create 3 monsoon abilities with names "Monsoon [1]", "Monsoon [2]", and "Monsoon[3]" all with 5 damage. It is a rather useless example, but it shows what is possible when doing this.
You might have noticed the
name .. " ["
. Unlike JASS, concatenation of strings follows the syntax:
JASS:
//! i string = "He" .. "ll" .. "o" .. " W" .. "or" .. "ld" .. "!"
Which would have a value of "Hello World!". Two periods between two strings will concatenate the two together into one.
Part VI - Lua Scripts
Nestharus has developed several scripts which can be used to create dynamic scripts, delete them, and execute them all with great ease! The installation is a breeze, and once it is installed, it can be used in any map. (for most scripts)
The first thing you will want to do is get LUA_FILE_HEADER. Follow the directions he posted at the top. Basically, change the FILE_NAME to whatever the name of your map is. For example, if my file name was Lua Fun Time.w3x, then it would be:
JASS:
//! i local FILENAME = "Lua_Fun_Time"
//! i function getfilename()
//! i return FILENAME
//! i end
JASS:
//! import "luajass.Lua_Fun_Time.j"
If you follow the instructions correctly, you then simply need to save for initialization, and then comment out the initialization and uncomment the import line once you have saved the map.
To make sure everything went correctly, check your jassnewgenpack5d -> grimext -> luadir folder. If done correctly, it should show something along the lines of FILE_NAME_dir as a folder. For the one I showed above, it should simply be Lua_Fun_Time_dir.
After that, keep the header in your map. Although it is not necessary, you will usually want this in your map so you can access/install external scripts when needed. If you have external JASS scripts/globals to use in your map, then the header must be in your map for everything to work properly.
How do you install a script? Well, it is very simple. I am going to use [Snippet] LUA_OBJECT_ID as an example.
JASS:
//GetObjectId 1.0.0.5
//! externalblock extension=lua FileExporter $FILENAME$
//! runtextmacro LUA_FILE_HEADER()
//! i writelua("GetObjectId", [[
//////////////////////////////////////////////////////////////////
//code
//! i function getobjectid(obj, objecttype)
//obj refers to the base object
//"hpea", "Amov", "Bphx", etc
//objectType refers to the type of object to create
//! i if (currentobjecttype() ~= objecttype) then
//! i setobjecttype(objecttype)
//! i end
//! i local object = generateid(obj)
//! i while (
//! i objectexists(object) or
//! i string.find(object, "'", 1, true) ~= nil or
//! i string.find(object, '\\', 1, true) ~= nil or
//! i string.find(object, ',', 1, true) ~= nil or
//! i string.find(object, '/', 1, true) ~= nil) do
//! i object = generateid(obj)
//! i end
//! i return object
//! i end
//end code
//////////////////////////////////////////////////////////////////
//! i ]])
//! endexternalblock
Just copy that text and paste it into an empty trigger. Make sure you have the LUA_FILE_HEADER in your map before installing. Afterward, just save and you can feel free to remove the code afterward! Yes, it is that simple. You can check if it is installed by going to the jassnewgenpack5d -> grimext -> luadir folder and seeing if it is installed there:

After that, simply proceed to install all the other scripts. Here I have a list of the Lua scripts posted on this site so far:
- [Snippet] LUA_FILE_HEADER
- [Snippet] LUA_OBJECT_ID
- [Snippet] LUA_GET_VAR_OBJECT
- [Snippet] LUA_TRIM
- [Snippet] LUA_STRING_EXPLODE
- [Snippet] LUA_FIND_LINE
- [Snippet] LUA_FILTER_LINE
- [Snippet] LUA_SERIALIZE_TABLE
- [Snippet] LUA_DUMMY_PHYSICAL_ABILITY
- [Snippet] LUA_NEW_BUFF
I recommend that you install all of those scripts if possible, because then you won't need to later on.
Part VII - GetVarObject
This script will be used a lot, so it is best to go over it to explain how it works.
[Snippet] LUA_GET_VAR_OBJECT
This library should be used for object generation, as it will ensure that all rawcode ID's used will be unique. This will prevent existing abilities from being modified by mistake, and can make object generation for systems become more fool-proof. Not only that, but it removes the need for any rawcode input for functions, which can simplify things even further.
JASS:
//! i function getvarobject(base, objtype, varname, import)
This function will return a unique ID. However, it has arguments that we must input values for, so I'll explain them:
- base - When creating a new object, you first must base it off of another object. That "base" object is the one referred to in the parameters, so we simply input the rawcode of it. For example:
//! i local myVar = getvarobject("hfoo", objtype, varname, import)
That first argument will specify that we will be basing the new object off of the one associated with the rawcode'hfoo'
(footman). - objtype - This is the object type to be created. This parameter essentially replaces
//! i setobjecttype("type")
, as the function will do it for you. For the example above, we would continue with the argument "units", since we are creating a new unit based off of the footman.
//! i local myVar = getvarobject("hfoo", "units", varname, import)
- varname - When creating an object, the rawcodes can become quite messy and annoying to look up. By inputting a value for varname, a new constant global integer will be made, set to the new rawcode. So if you input "UNITS_FOOTMAN", it will create a global with that name. Generally, you will want to use all uppercase, and follow the convention of TYPE_VARNAME. There is no difference, as it is all personal preference, but it should be that format when submitting lua scripts.
//! i local myVar = getvarobject("hfoo", "units", "UNITS_FOOTMAN", import)
- import - The final parameter will determine whether or not to import the global variable of the name you input for varname. "true" will have the global imported, while "false" will not.
//! i local myVar = getvarobject("hfoo", "units", "UNITS_FOOTMAN", "true")
//! i updateobjects()
is pretty important as well. After generation, you will want to be sure to update the objects, which will make sure that the globals are made and imported. Just call that function at the end of the generation to successfully make the globals.Part VIII - Writing Custom Lua Scripts
Writing custom Lua scripts are pretty easy as well. Let's make a simple script that includes a function that creates several destructables with different pitch/roll angles.
First, we'll start off with the preliminary setup for writing a script:
JASS:
//! externalblock extension=lua FileExporter $FILENAME$
//! runtextmacro LUA_FILE_HEADER()
//! i writelua("PitchRollDests", [[
//! i ]])
//! endexternalblock
"PitchRollDests" is the script name. Note that the extension block actually accesses the FileExporter, as it has a lighter overhead than the object merger. So let's make a simple function that creates a destructable based off of
'LTbx'
(Barrel) and allows you to modify the pitch/roll/name.However, let's say you want to have it automatically generate a unique rawcode. Luckily, the script GetVarObject exists. So how do we include it? We simply use dofile(""), like so:
JASS:
//! externalblock extension=lua FileExporter $FILENAME$
//! runtextmacro LUA_FILE_HEADER()
//! i writelua("PitchRollDests", [[
//! i dofile("GetVarObject")
//! i ]])
//! endexternalblock
This will allow us to use its contents when running the script. So let's finish up the function I just talked about:
JASS:
//! externalblock extension=lua FileExporter $FILENAME$
//! runtextmacro LUA_FILE_HEADER()
//! i writelua("PitchRollDests", [[
//! i dofile("GetVarObject")
//! i function barrelroll(pitch, roll, name)
//! i local dest = getvarobject("LTbx", "destructables", "DESTRUCTABLES_" .. name, false)
//! i createobject("LTbx", dest)
//! i makechange(current, "bnam", name)
//! i makechange(current, "bmap", pitch)
//! i makechange(current, "bmar", roll)
//! i end
//! i ]])
//! endexternalblock
Note the getvarobject use. It will automatically use "setobjecttype" to define what type we will be creating. It takes the base ID of the object we are basing our custom one off of, then it takes the category, then it takes the global name, and finally whether or not to import the globals. I figured that importing the globals for this would be unnecessary in this case, so I put "false" as the input for that field.
Now let's create a function that will automatically make several different objects of that destructable to have different pitch and roll values up to -6.28 (-2*pi), which is one full rotation all the way back to the default pitch/roll.
JASS:
//! externalblock extension=lua FileExporter $FILENAME$
//! runtextmacro LUA_FILE_HEADER()
//! i writelua("PitchRollDests", [[
//! i dofile("GetVarObject")
//! i function barrelroll(pitch, roll, name)
//! i local dest = getvarobject("LTbx", "destructables", "DESTRUCTABLES_" .. name, false)
//! i createobject("LTbx", dest)
//! i makechange(current, "bnam", name)
//! i makechange(current, "bmap", pitch)
//! i makechange(current, "bmar", roll)
//! i end
//! i function barrelgen(name, count)
//! i local factor = 6.28/(count-1)
//! i local angle = 0
//! i for i=1, count do
//! i barrelroll(angle, angle, name .. " [" .. i .. "]")
//! i angle = angle - factor
//! i end
//! i end
//! i ]])
//! endexternalblock
There we go, now we should be done. Simply save, and it should install the script. Afterward, just remove the text (or disable the trigger it is in) and then test it out. Note that the script above just provides functions for the object generation, it does not create the objects themselves. Thus, you have to make your own script which calls the function to actually generate objects. This is the script I have used, which creates 10 of the barrels with different pitch/roll angles:
JASS:
//! externalblock extension=lua ObjectMerger $FILENAME$
//! runtextmacro LUA_FILE_HEADER()
//! i dofile("PitchRollDests")
//! i barrelgen("BarrelRolls","10")
//! i updateobjects()
//! endexternalblock
This one now uses the ObjectMerger as the tool, instead of the file exporter. Once you have done this, simply save, close the map, and reopen. Delete the script for generation and then check if it is in the object editor. If everything went correctly, then this should be the result:

If it did work, then pat yourself on the back. If not, then do not worry. Simply debug the script until it works the way it should.
These dynamic scripts make it very easy to do things very quickly. In my example above, you can generate destructables with different pitch/rolls very easily, allowing for some cool animations if you link them together. If you have a spell that requires something along these lines, it is a pain for the user to have to copy and paste each individual object into their map. With Lua scripts, that becomes a thing of the past.
You may be wondering why the lua scripts have all uppercase titles. Before, the lua resources used to be all textmacros. Now that LUA_FILE_HEADER exists (which is still a textmacro), there was no longer a need for other textmacros. It is just a convention for the scripts, and recommended to be used if you are submitting a lua script.
FAQ:
- Can I create objects during the game by utilizing Lua scripts?
Sadly, no, that is not possible. Lua scripts are strictly for map editing purposes. It is not able to affect gameplay.
- I keep getting this error:
That is the generic error message for when the script does not follow proper syntax. The actual error log will appear in jassnewgenpack5d -> logs -> grimex.txt. However, the error messages are usually very generic, so you have to painfully debug and make sure that everything is written correctly.
- The file exists!
This error caused me a lot of distress, as well as about 2 other people total. I am not sure of what causes it to occur, but it has to do with improper Lua scripting. However, the fix is simple, so have no fear. When the lua scripts are made, they are made in the "temp" folder. (X:\Users\$USERNAME$\AppData\Local\Temp) Some file within that folder ends up causing the error. The fix is to simply delete that folder. It may take a while, as there are usually around ~70k files in there. Another fix could be to get a PC Clean tool, which usually will clean out your temp folder of unwanted things. That would probably be the best route, as sometimes you may get halfway through deleting until you see an error that says "This program is in use by another program", in which it will restore all the data you were trying to delete. In terms of safety, it should be fine to delete your temp folder. If you have any worries, just use a PC Clean tool, and it will do it for you.
- Do I have to keep Lua scripts in my map?
It is suggested to keep the file header in your map (or else syntax errors may occur), but for the most part, the rest can be deleted. It depends on the script, but normally things can be deleted after you have saved and reopened the map.
- How safe is using Lua?
Use it with caution. I recommend using it on blank test maps prior to using it on the actual map, and creating backups of the map before running lua. This is just a caution-measure in case an infinite loop is ran, which will destroy your map. An even better solution is to use a source code editor that supports Lua, such as NotePad ++.
- What are the other tools I can use for GrimExt?
Go to jassnewgenpack5d -> grimext and open up GrimExManual.html. It should explain some of the other tools to give a brief overview.
- Are there any more functions I can use for Lua?
As stated in the tutorial, Lua is a whole other programming language. Thus, it has a lot of features similar to other programming languages. See Programming in Lua for more information.
- Why doesn't my object editor show rawcodes next to the fields?
Make sure you have the map opened in NewGen, and have Grimoire -> Enable object editor hack checkmarked.
Known Bugs:
So far, the only bug I have encountered is the very bug WaterKnight mentioned in this thread: ObjectMerger Channelability
The ability "Channel" has checkmark boxes for its options. By default, it is invisible. You have to pull some strings to make the object editor consider it "visible", but sadly it does not work in game, even if it is clearly marked in the object editor as visible.
There is a hotfix included in that thread, linking to this post:
http://www.wc3c.net/showpost.php?p=1050064&postcount=7
That should allow you to create the channel abilities. That link is the same one listed at the top of the thread, in the section Requirements.
Credits:
- PipeDream - Grimoire.
- Azlier - He made a tutorial on the object merger, which is where I first learned of its abilities.
- PitzerMike - GrimExt and its documentation.
- Nestharus - Getting Lua to become more popular, assisting me in my weird problems, and for convincing me to write this tutorial.
- WaterKnight - ObjectMerger bug.
Last edited: