1. Music Contest #10 - Results are finally published! Drop by to check some retro songs and congratulate the winners!
    Dismiss Notice
  2. Join Terraining Contest #19 and witness the aftermath!
    Dismiss Notice
  3. The 3rd Melee Mapping Contest is ON! Join in on a ride of a 4v4 melee experience!
    Dismiss Notice
  4. The 30th edition of the Modeling Contest is finally up! The Portable Buildings need your attention, so come along and have a blast!
    Dismiss Notice

Lua Object Generation

Discussion in 'JASS/AI Scripts Tutorials' started by PurgeandFire, Mar 17, 2011.

  1. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,399
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Lua Object Generation

    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

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

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

    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:
    Code (vJASS):
    //!

    This is the command to access the external preprocessor. It will allow us to execute scripts using specific tools/extensions that we specify. To access a specific tool or extension, we use this syntax:
    Code (vJASS):
    //! external <TOOL_NAME> <ARGUMENTS>


    For the object merger, "<TOOL_NAME>" will be replaced with "ObjectMerger".
    Code (vJASS):
    //! 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.
    Code (vJASS):
    //! 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:
    Code (vJASS):
    //! 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.
    aerialshackles.png

    Our concern is for
    'Amls'
    , also known as the rawcode ID of the object. We will replace <BASE OBJECT ID> with this.
    Code (vJASS):
    //! 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'
    .
    Code (vJASS):
    //! 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.
    objEdfields.png

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

    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:
    Code (vJASS):
    //! 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:
    Code (vJASS):
    //! external ObjectMerger w3a Amls A000 anam

    However, that is missing the corresponding field value.

    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.
    Code (vJASS):
    //! 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:
    Code (vJASS):
    <FIELD ID> <LEVEL> <VALUE>


    For example, if I wanted to modify the targets allowed:
    Code (vJASS):
    //! 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.
    Code (vJASS):
    //! 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.
    Code (vJASS):
    //! 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.
    Code (vJASS):
    //! 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.
    Code (vJASS):
    //! 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.
    Code (vJASS):
    //! i modifyobject("baseID")
    //! i createobject("baseID","customID")

    When modifying an object, you simply need to provide the rawcode ID of the object to modify. For example, if you wanted to modify Control Magic, you would use:
    Code (vJASS):
    //! 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:
    Code (vJASS):
    //! i createobject("Amls","A000")

    That will create an ability with the rawcode
    'A000'
    based off of Aerial Shackles (Amls).

    After that, it is just a matter of a single function:
    Code (vJASS):
    //! i makechange( object , field , value )

    The object in that function is special. You can have several different input for that:
    • 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:
    Code (vJASS):
    //! 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:
    Code (vJASS):
    //! i makechange(current,"amcs","1","15")

    That will change the mana cost of level 1 of the ability to 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.
    Code (vJASS):
    //! i makechange(current,"anam","Fire")


    Now let's recreate the ability that we made for the external call.
    Code (vJASS):
    //! 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:
    Code (vJASS):
    //! 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:
    Code (vJASS):
    //! 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:
    Code (vJASS):
    //! 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.
      Code (vJASS):
      //! 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

      In Lua, you can also make arrays.
      Code (vJASS):
      //! 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.
      Code (vJASS):
      //! 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

      One thing to note is that if you run an infinite loop with Lua, your map will be dead. Always keep a backup before executing Lua scripts, especially when it comes to your own scripts. For safety, you may want to stick with "for" loops unless you need to do a "while" loop, because the latter is a bit more prone to infinite loops compared to the first one.
    • 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?
      Code (vJASS):
      //! 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

      The arguments are enclosed within parenthesis () and have commas as separators for individual arguments. To execute a function, just call its name and input the appropriate arguments.
    • If/Then/Else Blocks - These are pretty much self explanatory.
      Code (vJASS):
      //! 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.
    Code (vJASS):
    //! 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:
    Code (vJASS):
    //! 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:
    Code (vJASS):
        //! i local FILENAME = "Lua_Fun_Time"
           
        //! i function getfilename()
            //! i return FILENAME
        //! i end

    And this line would be:
    Code (vJASS):
    //! 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 http://www.hiveworkshop.com/forums/jass-functions-413/snippet-lua_object_id-176943/ as an example.
    Code (vJASS):
    //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:
    GetObjectId.png

    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:

    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.

    Code (vJASS):
    //! 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:
    Code (vJASS):
    //! 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:
    Code (vJASS):
    //! 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:
    Code (vJASS):
    //! 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.

    Code (vJASS):
    //! 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:
    Code (vJASS):
    //! 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:
    BarrelRolls.png

    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:
      ExtError.png

      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!
      thefileexists.png

      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: Aug 16, 2013
  2. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Wow, it looks awesome Oo

    You should submit this to TH and wc3c as well ;o

    edit
    Except this
    //! i function getfilename()


    That returns a variable, you don't have to edit what it returns. Just change the variable's value : P


    This portion
    Code (vJASS):

    //! i local FILENAME = "Lua_Fun_Time"
       
    //! i function getfilename()
        //! i return "Lua_Fun_Time"
    //! i end
     


    Should just be this
    //! i local FILENAME = "Lua_Fun_Time"



    It says that in the little mini tut at the top of LUA_FILE_HEADER as well ; D

    edit
    You should note that Lua scripts are shared across all maps and JASS scripts are local to a map. Check the mini tut on the LUA_FILE_HEADER to get full scope specifics = P.

    Also, you should never ever remove the LUA_FILE_HEADER from the map as that imports generated JASS scripts.

    ->
    //! import "luajass.Lua_Fun_Time.j"


    edit
    Also, please don't link to graveyarded Lua resources. There is a reason they were graveyarded. For example, the IO script shouldn't be used and the JASS globals thing shouldn't be used either.

    That's incorrect. That's not what FileExporter does. Most Lua installation scripts are written using FileExporter because it has a lighter overhead than the ObjectMerger. If you save the map and compare ObjectMerger speed to FileExporter speed, FileExporter will be faster.

    That's not how you run object generating scripts. See the mini tut on LUA_FILE_HEADER to see how to properly run an object editor script.

    An even better option is to get something like Notepad++ and link up an Lua interpreter with it so that you can run Lua scripts. I don't recommend developing Lua scripts inside of WE, that's just a pain. I know I don't do that =p.
     
  3. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,399
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Doh, I'll fix that. :)

    I'll add that as well.

    Oh, could've sworn I removed those. Anyway, I'll remove them asap.

    Fixed.

    Well, the thing is that the script just has functions for object gen, kind of like LUA_NEW_BUFF. It doesn't actually create the objects unless the user executes the functions. I added clarification though, so that the reader knows he has to execute the script.

    Thanks for the feedback. ;D
     
    Last edited: Mar 17, 2011
  4. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Added more feedback ;o
     
  5. -Kobas-

    -Kobas-

    Joined:
    Jan 17, 2010
    Messages:
    5,911
    Resources:
    28
    Icons:
    1
    Tools:
    2
    Maps:
    10
    Spells:
    4
    Template:
    5
    Tutorials:
    6
    Resources:
    28
    Can you provide test map or some resource related to this :)
     
  6. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,399
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Good idea. I'll whip something up tomorrow. :)
     
  7. Raven0

    Raven0

    Joined:
    Oct 16, 2010
    Messages:
    878
    Resources:
    2
    Maps:
    2
    Resources:
    2
    epic tut

    I haven't finished muddling through it all yet, but this should make mass item generation for my socketing system alot less tedious.
     
  8. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Oh, you forgot the updateobjects() when using getvarobject. You shouldn't use it in the function, but rather place it at the end of the script that calls the function.

    Code (vJASS):

    //! externalblock extension=lua ObjectMerger $FILENAME$
        //! runtextmacro LUA_FILE_HEADER()
        //! i dofile("PitchRollDests")
       
        //! i barrelgen("BarrelRolls","10")
    //! endexternalblock
     


    updateobjects is rather critical ;P

    You should go over the API of LUA_GET_VAR_OBJECT since that script will be used a lot.
    Code (vJASS):

    //function getvarobject(base, objtype, varname, import)
        //base: base id of object
            //"hpea", "Amov", "Bphx", etc
           
        //objtype: type of object
            //"units", "abilities", "items", etc
           
        //varname: name assigned to variable
            //OBJECTTYPE_NAME
            //"UNITS_MY_UNIT", "ABILITIES_RAIN_OF_CHAOS", etc
           
        //import: should the variable be imported into the map as a global?
            //true, false, nil
           
    //function getvarobjectname(value)
        //retrieve name given value ("hpea", etc)
       
    //function getvarobjectvalue(objectname)
        //retrieve value given name ("UNITS_MY_UNIT", etc)
       
    //function updateobjects()
        //call at end of script
     


    Also, you should talk about why Lua scripts are in all uppercase. It seems like a nutty convention now, especially considering that the Lua scripts themselves are not all uppercase, but back when Lua resources were first really starting up, there was a reason for the uppercase ;P. Remember that before LUA_FILE_HEADER, everything was a macro. LUA_FILE_HEADER is still a macro and it's the only macro you have to run.

    You should also go over the other object generating scripts and things used for generating objects, like the dummy physical ability one and the object id one.


    Also, this hasn't yet been update to use the script you wrote
    ->http://www.hiveworkshop.com/forums/submissions-414/snippet-lua_new_buff-180920/

    My suggestion is just to submit your script linking back to the original and link to your updated script here =).


    You should let people know that Lua scripts that don't run through LUA_FILE_HEADER and object generating scripts that don't use LUA_GET_VAR_OBJECT are no longer approved at THW. You can look through Submissions and Graveyard to see plenty of scripts that were unapproved and etc because of those 2 probs, some of mine included (I have yet to update them >.<).
     
    Last edited: Mar 19, 2011
  9. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,399
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Whoops, I'll fix that. Thanks.

    Will do.

    Okay, I'll add some of those fun facts. xP

    Will do.

    ------

    I'll get around to updating this tomorrow or the day after (or Monday), I've got some work to do this weekend. Again, thanks everyone for the feedback. :)

    Thanks. :D You can feel free to ask me any questions if you get stuck. They might help me clarify some things in the tutorial.
     
  10. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,712
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    I built my first LUA script over the weekend thanks to this tutorial, really covers some useful ground.

    I was having trouble finding the four-character names for the object editor data fields because I had "Display as Raw Data" turned on and that strangely hides the four-character field names. Thank you for writing this.
     
  11. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,399
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Good to hear, and thanks for the feedback. :D

    ----

    Updated.
     
  12. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,712
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    You going to let yourself approve this any time soon?
     
  13. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,399
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Yeah, I sometimes forget about this tutorial. lol.

    I suppose I got enough positive replies. If anyone has any objections or anything I need to fix, PM me or post in this thread.
     
  14. mckill2009

    mckill2009

    Joined:
    Mar 10, 2009
    Messages:
    4,696
    Resources:
    34
    Maps:
    5
    Spells:
    27
    JASS:
    2
    Resources:
    34
    I like this, very useful indeed...wanna learn moreeeee...

    EDIT:
    I just have one question...when you're done making generating object or make the script, where do you put it?...is it accessible to all scipts like libraries?...
     
  15. watermelon_1234

    watermelon_1234

    Joined:
    Nov 18, 2007
    Messages:
    1,066
    Resources:
    10
    Spells:
    9
    JASS:
    1
    Resources:
    10
    This was a really good, informative tutorial. It definitely helped me understand Lua (and the Object Merger) much better. =D

    I only don't understand one little thing:
    Do you mean quotes or something else?
     
  16. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,399
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Sorry, I meant to say quotes. I don't know why I said parentheses, thanks for pointing that out, lol. Fixed.
     
  17. a2581701

    a2581701

    Joined:
    Dec 21, 2010
    Messages:
    1
    Resources:
    0
    Resources:
    0
    i'm Taiwnaness , so my English not very well


    Can it pick the value out?

    As GMSL out as able to use some functions of the value we want to

    Example: local str = A000.unam

    Will be able to know the value of str
     
  18. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,399
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    As far as I know, Lua doesn't offer that. I am not sure though, I'll check soon. However, I don't think it offers that kind of retrieval.
     
  19. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    It doesn't offer that kind of retrieval.


    However, what you can do is store it into a global via Lua and then read the global.
     
  20. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,712
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    It would be helpful to note that in "external ObjectMerger" commands, unlike in "lua ObjectMerger" commands, you do not have to escape the backslash.