1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. The poll for Hive's 12th Concept Art Contest is up! Go cast your vote for your favourite genie!
    Dismiss Notice
  4. Travel to distant realms and encounter scenes unknown to the common folk. The Greatest of Adventures is upon us with the 8th Cinematic Contest. Join in on a fun ride.
    Dismiss Notice
  5. The 18th Icon Contest is ON! Choose any ingame unit and give him/her Hero abilities. Good luck to all.
    Dismiss Notice
  6. Contestants are to create a scene set in the Stone Age. Come and see what you can come up with. We wish you the best of luck!
    Dismiss Notice
  7. Colour outside the lines! Techtree Contest #13 is a go. The contest is optionally paired.
    Dismiss Notice
  8. Greetings cerebrates, our Swarm needs new spawners that will have numerous children. Join the HIVE's 31st Modeling Contest - Spawners and Spawned! The contest is optionally paired.
    Dismiss Notice
  9. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

[JASS/AI] Simple JASS tutorial - for beginners

Discussion in '"Graveyard"' started by BlueSaint, May 5, 2014.

  1. BlueSaint

    BlueSaint

    Joined:
    Jun 18, 2012
    Messages:
    2,777
    Resources:
    3
    Tools:
    1
    Spells:
    2
    Resources:
    3
    Last time updated : 21.10.2014


    [​IMG]

    [​IMG]
    Your guide, la
    trustworthy cat!
    Required :
    • Jass NewGen WE
    • Knowledge of GUI
    • Time
    [r]
    Recommended :
    • Experience with coding
    [r]
    Table of Contents :
    1. Basics of JASS I
    2. vJASS features
    3. Basics of JASS II
    4. Using JASS/vJASS
    5. Efficient JASS coding tips
    6. End of tutorial

    [​IMG] 1 : Basics of JASS I

    JASS is a scripting language for Warcraft III. It is rumored to stand for Just Another Scripting Syntax, although there's no validation for it. GUI stands for Graphical User Interface, which represents basically JASS but in more graphical way. People who have learned/mastered JASS might say: "don't use gui", and call it bad, but it's totally up to you whether you use GUI or not. Do you recognize these GUI lines?
    • Trigger
      • Events
        • Time - Elapsed game time is 0.00 seconds
      • Conditions
      • Actions
        • Custom script: //line
        • Custom script: //another line
        • Custom script: call RemoveUnit(CreateUnit('u000', Player(0), 0, 0, 270))
    Yes, custom scripts. Now you might be familiar with
    call RemoveLocation(udg_p)
    ,
    call DestroyGroup(udg_ug)
    and
    call DestroyForce(udg_pg)
    , especially if you're a spell maker. Now what these do is they release memory held by objects, for example call RemoveLocation() removes Location(x,y) from it, so you can't use it anymore as it doesn't know what you're referring to. This is essential for keeping your RAM free. If you don't do this, it's called leaking. Removing leaks is what mods require from you before you can get your spell approved.

    1.1 : Our first JASS trigger
    Here's a simple JASS code for you to create:
    Code (vJASS):
    function myCondition takes nothing returns boolean
        return false
    endfunction

    //===========================================================================
    function InitTrig_myTrigger takes nothing returns nothing
        local trigger t = CreateTrigger(  )
        call TriggerAddCondition( t, Condition(function myCondition) )
        set t = null
    endfunction
    Let's see what I did there. First I converted empty trigger called myTrigger to JASS code via Edit > Convert to Custom text.
    [​IMG]
    Then I removed everything before commented line, and cleared code in InitTrig_myTrigger.

    Now let's take a look at
    local trigger t = CreateTrigger()
    , we create local variable (not accessible outside the function) trigger t, and assign function CreateTrigger() to it. Why can we assign function to it? Because
    native CreateTrigger takes nothing returns trigger
    returns trigger. We don't need any arguments to this function because it says it takes nothing (only fill parentheses when the function asks for it). Simple, isn't it?

    Then comes the next line
    call TriggerAddCondition( t, function myCondition )
    , it's another function which does what it says: assigns a condition to a specific trigger. We tell it to assign function myCondition, which we have above, to trigger which we just created locally. Condition of course has to return a boolean, so we make myCondition return false. Why? Because if a condition (any of them) returns false, it'll not trigger the actions. And since we have no actions, we'll simply return false.

    As a side note, JASSers often put all their "Actions" into the condition function, instead of creating one function for a condition and one function for actions. It saves handles and is faster.

    You might wonder, why do we create trigger locally? The trigger isn't actually local, we can't decide (in vanilla JASS), whether trigger is local or not, it's just the variable which is local, trigger stays trigger. If you're confused, let's make it more simple:

    Let's imagine there's a variable type "door". We create new variable of door called handle. If we use
    set handle = null
    , we don't actually remove the door. The door still exists, but we just removed our way of using the door, by removing a handle for it. This is the reason why you need to use
    call RemoveLocation(udg_myLocation)
    , even in gui, because if you just assign myLocation to new location, you have a leak in your code.

    Hive user chobibo explained this further, expand this if you want to study the role of variables a bit more:
    Quote
    Variables are really just identifiers, so when you create a new variable, it really points to nothing [useful] (null on global handle types but undefined on local handle types), now when you create an "object" (an object is a data that we use, like regions, units, etc.) using a "function" (CreateUnit, etc..) you use data, that data can be "linked to" by a variable. e.g.:
    Code (vJASS):
    local location point // This is a declaration, it tells the compiler
                         // (warcraft) to create a variable. The variable
                         // is empty, it does not link to anything useful.
                     
    local location point = Location(0,0) // This time, the declaration
                                         // and use is done simultaeneously
                                         // The variable now links to
                                         // something useful.

    Now, that "point" uses memory, if were done with it (no longer need it) we must destroy it to free the memory space it uses. The first step is to tell warcraft that we no longer need it using a function Blizzard supplied.
    Code (vJASS):
    call RemoveLocation(point) // This function tells warcraft that the
                               // object linked to by "point" is no longer
                               // useful. This is the first step to remove
                               // this unuseful data.

    Now, the variable links to (or points to) something useless, to avoid errors, Blizzard made it so that when a variable still links to something in memory (the now useless data, subjective of course), that said memory will not be freed. The next, albeit important and last, step is to make our variable point to "null".
    Code (vJASS):
    set point = null // This tells warcraft to make the variable
                     // point link to the "null" value. This in
                     // effect allows warcraft to recycle the
                     // memory that the previously pointed object
                     // by the variable "point" is linking to
                     // (pointing to).


    Failing to do this crucial step will result in the memory being used by the object our variable links to (points to) unreachable, hence it will not be recycled by warcraft, *cue scary music ♫dum dum dumdam ♫* that is called a "memory leak". Too much memory leaks will cause your map to lag and eventually crash the game...

    Now that we've done that, warcraft will recycle that chunk of memory for us!

    Now, we've just freed a memory block thanks to that!

    So after we've done assigning condition to the trigger, we can nullify the variable, without touching the trigger itself. We do this by typing
    set t = null
    .

    1.2 : Functioning Trigger
    Now let's make the trigger actually do something:
    Code (vJASS):
    function myCondition takes nothing returns boolean
        call BJDebugMsg( "lol" )
        return false
    endfunction

    //===========================================================================
    function InitTrig_myTrigger takes nothing returns nothing
        local trigger t = CreateTrigger(  )
        call TriggerRegisterTimerEvent( t, 0, false )
        call TriggerAddCondition( t, Condition(function myCondition) )
        set t = null
    endfunction
    Let me clear the functions I added for you:

    call BJDebugMsg( "lol" )
    this will outprint message lol.

    call TriggerRegisterTimerEvent( t, 0, false )
    let me get the declaration for you:
    native TriggerRegisterTimerEvent takes trigger whichTrigger, real timeout, boolean periodic returns event
    we just connect things together by using values we want as arguments. We want it to affect trigger t, timeout at 0 seconds, and not loop itself. This is pretty self-evident.

    You may test the trigger in your map now. If it doesn't print out lol after 0 seconds of game-time, make sure the trigger name is myTrigger and repeat the steps if necessary.


    [​IMG] 2 : vJASS features

    vJASS stands for very-JASS. It was made by Vexorian, who is a god of vJASS who you'll never outperform. vJASS syntax adds additional features to JASS, this is why you'll need your own syntax checker for it. Make sure you have your JNGP (jasnewgenpack) working and ready before going further in this section. Remember because it uses its own syntax checker, you need to save your map before testing it.

    2.1 : How to get vJASS
    Download link for user made WE: http://blizzardmodding.info/4263/the-jass-newgen-pack-jngp-2-0/
    • Extract jassnewgenpack2XX.7z to somewhere on you PC
    • Start NewGen WE.exe, if it doesn't boot up, try to contact the program creator
    • (optional) Attach the New WE to your Start Menu for faster access

    2.2 : Scopes and private functions
    Hopefully you haven't removed myTrigger which we made in the last chapter, we're going to transform it to vJASS world :)
    Code (vJASS):
    scope myScope initializer init
        function myCondition takes nothing returns boolean
            call BJDebugMsg( "lol" )
            return false
        endfunction
       
        //===========================================================================
        function init takes nothing returns nothing
            local trigger t = CreateTrigger(  )
            call TriggerRegisterTimerEvent( t, 0, false )
            call TriggerAddCondition( t, function myCondition )
            set t = null
        endfunction
    endscope
    We added
    scope <name> initializer <function name>
    to the top row and
    endscope
    to the bottom row.

    Initializer is function that's ran in the loading screen, just like Map Initialization Event in GUI.

    Scope is something I recommend you to use when you're making a code which is not used elsewhere, a code which you want to work as an individual. Scopes can have private members, let's explore that feature now.

    If you save and test (Ctrl + S and Ctrl + F9) the map, it does exactly the same thing, outprints lol. Why? Because vJASS is just like JASS, the syntax is just a little bit more advanced. Here's a situation where in normal JASS would happen a collision: We create a new trigger myProof:
    Code (vJASS):
    scope myProof initializer init
        function myCondition takes nothing returns boolean
            call BJDebugMsg( "lol" )
            return false
        endfunction
       
        //===========================================================================
        function init takes nothing returns nothing
            local trigger t = CreateTrigger(  )
            call TriggerRegisterTimerEvent( t, 0, false )
            call TriggerAddCondition( t, function myCondition )
            set t = null
        endfunction
    endscope
    If we try to save it the syntax checker will nag at us about redeclaring and stuff like that. To prevent it, we make functions inside these two scopes local to them. Just make
    function myFunction
    -->
    private function myFunction
    , add private in front of each function. Now you can't use the function outside it. But what if we want to, but not to cause collisions?

    2.3 : Libraries and public functions
    Make the triggers look like this (the function lines):
    Code (vJASS):
    scope myScope initializer init
        public function myCondition takes nothing returns boolean
            call BJDebugMsg( "lol" )
            return false
        endfunction
       
        //===========================================================================
        private function init takes nothing returns nothing
            local trigger t = CreateTrigger(  )
            call TriggerRegisterTimerEvent( t, 0, false )
            call TriggerAddCondition( t, function myCondition )
            set t = null
        endfunction
    endscope

    Try to save it. It should not nag about functions, even when we made them public. Calling the functions outside them is not that simple, you need to do it in syntax of scopeName_functionName. Here's an example trigger:
    • asd
      • Events
        • Time - Elapsed game time is 0.00 seconds
      • Conditions
      • Actions
        • Custom script: call myScope_myCondition()
        • Custom script: call myProof_myCondition()

    Libraries are scopes which can require other libraries. Libraries are directly connected to the map header code, and requiring means simply this: If lib A requires lib B, computer puts lib B above lib A in the map header code. Let's make our first library:
    Code (vJASS):
    library myLib initializer init
        private function init takes nothing returns nothing
            call BJDebugMsg("hello!")
        endfunction
    endlibrary
    Save and test the map, you should see hello! , lol , lol , lol and lol printed on the screen.

    As libraries are inserted to the map header code and scopes' role is to be "individual", scopes can use any non-private function of any library in your map.

    For library to use other library's functions you need to use
    library <name> requires <name>
    , as explained before.

    2.4 : Globals and private variables
    Local variable is private to that function by default, but what if we want to use our variable in another function? You can use udg, which stands for user defined global. But this is slow and requires you to use GUI's interface. This is why I suggest you to use globals, variables which are defined like GUI's variables, at initialization. They are linked to your map via your trigger, so you don't need to remove them manually after you've quit using a system, unlike GUI's variables. Let's practice:
    Code (vJASS):
    scope myGlobals initializer init
        globals
            string MYSTRING = "asd"
        endglobals
        private function init takes nothing returns nothing
            call BJDebugMsg(MYSTRING)
        endfunction
    endscope
    Its name is written in caps lock for clarity. Your possible questions reaching your mind might be: 1. Where do I place them? 2. Do I have to assign them to something? 3. How to make array?
    1; You place globals on top of your trigger 2; You don't have to assign them to anything (you can just define MYSTRING like
    string MYSTRING
    ) 3; You make array like this:
    string array MYSTRING
    , but then you can't assign them at globals section.

    We talked about collisions between triggers in out previous segment. There can be collisions between globals too. To prevent this, type
    private
    in front of your global. Now your trigger should look something like this:
    Code (vJASS):
    scope myGlobals initializer init
        globals
            private string MYSTRING = "asd"
        endglobals
        private function init takes nothing returns nothing
            call BJDebugMsg(MYSTRING)
        endfunction
    endscope
    You can use non-private globals outside your trigger. This is because they're generated on top of map code:
    Code (vJASS):
    globals
        // User-defined
    string udg_MYSTRING

        // Generated
    string MYSTRING= "asd"

    endglobals
    Above you can see map code going through syntax checker. I have global variable MYSTRING in my scope, and GUI's variable MYSTRING in GUI. They're created at the same segment in reality, but in our editing environment they're placed elsewhere.

    2.5 (optional) : Structs
    Structs are inspired by object oriented programming (OOP). They can make your life a lot easier.

    Here's some concept explanations:
    struct
    is an object defined by coder
    instance
    is an individual object of struct made with
    allocate()

    member
    is a variable which is used by struct
    method
    is a function which belongs to struct
    static
    means same amongst all instances of struct
    allocate
    means giving instance its own index
    deallocate
    means destroying an instance
    extends
    is something I'm not gonna cover
    extends array
    is awesome

    Let's make trigger which does absolutely nothing in game:
    Code (vJASS):
    scope myScope initializer init
        struct myStruct
           
        endstruct
        private function init takes nothing returns nothing
            local myStruct A=myStruct.create()
            call A.destroy()
        endfunction
    endscope
    myStruct is the name of our struct type. In function init we create a pointer, and instantly issue it to point to newly created struct of type myStruct.
    <structtypename>.create() gives our struct a unique id. Structs use arrays, with maximum of 8191 values. This is actually 8190, index of 0 is used for null.
    You can use structs as globals, but you can't initialize them in globals section. You need to do this in init function.
    Let's create a member str for our struct:
    Code (vJASS):
    scope myScope initializer init
        struct myStruct
            string str
        endstruct
        private function init takes nothing returns nothing
            local myStruct A=myStruct.create()
            set A.str="lol"
            call BJDebugMsg(A.str)
            call A.destroy()
        endfunction
    endscope
    When I added
    string str
    in the struct, I added a member for it. Then we declared it in our init function. After that we called the member via BJDebugMsg using <pointername>.<membername>. Let's give our struct a function which runs on creation:
    Code (vJASS):
    scope myScope initializer init
        struct myStruct
            string str
            static method create takes nothing returns thistype
                local thistype returned = thistype.allocate()
                set returned.str = "lol"
                return returned
            endmethod
        endstruct
        private function init takes nothing returns nothing
            local myStruct A=myStruct.create()
            call BJDebugMsg(A.str)
            call A.destroy()
        endfunction
    endscope
    thistype
    = is the name of the struct. But you can use your struct's name (myStruct in this example).

    Static methods can't be called with <instance>.<method>(), they must be called with <structtype>.<method>().
    We can use keywords
    private
    and
    public
    in structs too. Try to add keyword public in front of static method create, it doesn't nag about it. But when we add private, we can't use the method outside of struct anymore. Let's modify the struct so that its str can be private:
    Code (vJASS):
    scope myScope initializer init
        struct myStruct
            private string str
            public static method create takes nothing returns thistype
                local thistype returned = thistype.allocate()
                set returned.str = "lol"
                return returned
            endmethod
            public method debugMsg takes nothing returns nothing
                call BJDebugMsg(str)
            endmethod
        endstruct
        private function init takes nothing returns nothing
            local myStruct A=myStruct.create()
            call A.debugMsg()
            call A.destroy()
        endfunction
    endscope
    I made a new method in our struct which is no static, so we can call it in out init function with <instancename>.<methodname>.
    Now, members can be static (non-indexed) too:
    Code (vJASS):
    scope myScope initializer init
        struct myStruct
            private static string str = "lol"
            public static method create takes nothing returns thistype
                local thistype returned = thistype.allocate()
                return returned
            endmethod
            public method debugMsg takes nothing returns nothing
                call BJDebugMsg(str)
            endmethod
        endstruct
        private function init takes nothing returns nothing
            local myStruct A=myStruct.create()
            call A.debugMsg()
            call A.destroy()
        endfunction
    endscope
    It doesn't nag about declaring static member in our create method, but it makes no sense to do so. So we simply declare it in the members section.
    If you want to go more in-depth about them, follow the link at the last chapter.

    2.6 (optional) : Textmacros
    //! textmacro
    is your personal CnP tool made possible by vJASS. Let's imagine you want to loop different texts many times:
    Code (vJASS):
    scope myScope initializer init
        //! textmacro LoopText takes STR
            call BJDebugMsg("$STR$")
            call BJDebugMsg("$STR$")
            call BJDebugMsg("$STR$")
        //! endtextmacro
       
        private function init takes nothing returns nothing
            //! runtextmacro LoopText("asd")
            //! runtextmacro LoopText("lol")
        endfunction
    endscope
    Textmacros are begun with
    //! textmacro <name> takes <parameter>, <parameter>
    , and ended with
    //! endtextmacro
    , the parameters are attached to the macro via $-marks. Textmacros are called with
    //! runtextmacro <name>(<parameters>)


    Attaching textmacros to your code is made first on progress of converting your vJASS code to something WC III can handle. Now, you can even create scopes with textmacros:
    Code (vJASS):
    //! textmacro NewScope takes NAME, STR
        scope $NAME$
            globals
                private string STR = "$STR$"
            endglobals
            public function loop10 takes nothing returns nothing
                local integer i = 0
                loop
                    call BJDebugMsg(STR)
                    exitwhen i == 10
                    set i = i + 1
                endloop
            endfunction
        endscope
    //! endtextmacro

    //! runtextmacro NewScope("asd","lol")
    scope myScope initializer init
       
        private function init takes nothing returns nothing
            call asd_loop10()
        endfunction
    endscope
    This loops "lol" 10 times for you. There's so much you can create with textmacros, explore the features yourself and you can become the next JASS god :p


    [​IMG] 3 : Basics of JASS II

    We've learned functions, scopes, libraries, locals, globals, privates and publics. That's a lot of coverage, but there's a lot more to be covered. We'll now learn a couple of new events and how to use them.

    3.1 : Periodic Loop
    Let's make a looping trigger which prints out "hi" every 2 seconds:
    Code (vJASS):
    function myFunc takes nothing returns boolean
        call BJDebugMsg("hi")
        return false
    endfunction

    //===========================================================================
    function InitTrig_myTrig takes nothing returns nothing
        local trigger t = CreateTrigger(  )
        call TriggerRegisterTimerEvent( t, 2, true )
        call TriggerAddCondition( t, Condition( function myFunc ) )
        set t = null
    endfunction
    You can recognize a function
    call TriggerRegisterTimerEvent( t, 2, true )
    which we used in part 1 also. Part 1's trigger didn't loop itself, however. Let's take a look at the declaration:
    native TriggerRegisterTimerEvent takes trigger whichTrigger, real timeout, boolean periodic returns event
    we changed timeout from 0 to 2 and boolean from false to true. This way we got ourselves a 2 second loop.

    3.2 : Timer Loop
    Do you know how you can create timers and timer windows in GUI? We can use those timers in JASS to trigger functions. Let's modify our trigger by adding globals, which we learned from previous chapter:
    Code (vJASS):
    globals
        timer TIMER = CreateTimer()
    endglobals

    function myFunc takes nothing returns nothing
        call BJDebugMsg("hi")
    endfunction

    //===========================================================================
    function InitTrig_myTrig takes nothing returns nothing
        call TimerStart( TIMER, 2, true, function myFunc )
    endfunction
    Timer can be stopped, so it's more advanced. You can stop the timer by calling
    call PauseTimer( TIMER )


    3.3 : Ability Event
    Just like in GUI, you can get event for ability cast. In GUI it'd be like this:
    • GUI
      • Events
        • Unit - A unit Starts the effect of an ability
      • Conditions
        • (Ability being cast) Equal to Thunder Clap
      • Actions
        • Custom script: call BJDebugMsg("lol")
    In JASS the equivalent of this would be:
    Code (vJASS):
    function myFunc takes nothing returns boolean
        if GetSpellAbilityId() == 'AHtc' then
            call BJDebugMsg("lol")
        endif
        return false
    endfunction

    //===========================================================================
    function InitTrig_JASS takes nothing returns nothing
        local trigger t = CreateTrigger(  )
        call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition( t, Condition( function myFunc ) )
        set t = null
    endfunction
    'AHtc'
    is a rawcode, I'll cover it up later in "4.5 : Unit IDs".

    There's a couple of new functions for me to cover:
    GetSpellAbilityId() == 'AHtc'
    this is a boolean, the real function here is;

    GetSpellAbilityId()
    , this is a native which gets the ID of an ability, which triggered the function.

    I'll talk about
    if then endif
    tags and 'AHtc' ID in the next chapter more.

    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    when there's a BJ at the end of a function, it means there's a script connected to it. For example the function here is actually the script below:
    Code (vJASS):
    function TriggerRegisterAnyUnitEventBJ takes trigger trig, playerunitevent whichEvent returns nothing
        local integer index

        set index = 0
        loop
            call TriggerRegisterPlayerUnitEvent(trig, Player(index), whichEvent, null)

            set index = index + 1
            exitwhen index == bj_MAX_PLAYER_SLOTS
        endloop
    endfunction
    This loops the event (unit owned by <player> casts a spell) for every player.

    Place a Mountain King on the map, give him thunder clap (set the level from 0 to 1 at the terrain editor) and save+run the map. It should outprint "lol" everytime you cast thunder clap. If it doesn't CnP my trigger to your map and make sure you have no other triggers on your map (after all we haven't made it private or anything.)

    3.4 : Scope transformation
    Let's transform the JASS trigger we made into vJASS:
    Code (vJASS):
    scope mySpell initializer init
        private function myFunc takes nothing returns boolean
            if GetSpellAbilityId() == 'AHtc' then
                call BJDebugMsg("lol")
            endif
            return false
        endfunction
       
        //===========================================================================
        private function init takes nothing returns nothing
            local trigger t = CreateTrigger(  )
            call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
            call TriggerAddCondition( t, Condition( function myFunc ) )
            set t = null
        endfunction
    endscope
    Now the trigger can't collide with other triggers, as it has private functions. But if you have two scopes called mySpell, you're in for a nagging by syntax checker. Save and test run, it should do exactly same thing as before.

    In the next chapter you'll learn advanced methods to use inside functions. Combine them with stuff learned in this section, and you'll able to create full JASS triggers :)


    [​IMG] 4 : Using JASS/vJASS


    You may now remove the triggers you've made before, they're not necessary anymore. Now we'll learn how to use JASS. I will show you multiple functions, which you can combine in different ways for awesome triggers :)

    4.1 : Unit Groups
    Create couple of footmen and couple of peasants on the map.

    In this code I trigger every unit pre-placed on the map editor to be colored blue. I've added comments for clarifying stuff.
    Code (vJASS):
    scope myScope initializer init
        private function groupAction takes nothing returns nothing
            call SetUnitColor(GetEnumUnit(), PLAYER_COLOR_BLUE) //we change the color for picked unit
        endfunction
        private function init takes nothing returns nothing
            local group g = CreateGroup() //create variable of type group, assign it to newly created group
            call GroupEnumUnitsInRect(g, bj_mapInitialPlayableArea, null) //assign units in playable map area to group g
            call ForGroup(g, function groupAction) //we assign every unit in the group through a function
            call DestroyGroup(g) //no use for the unit group anymore, destroy it
            set g = null //nullifying, I've covered this step in chapter 1
        endfunction
    endscope
    We get the picked unit (the unit which is currently going through groupAction) with
    GetEnumUnit()
    .

    Now do notice how I've placed the groupAction above init function. Because we need the groupAction function in init function, we have to place it above it.

    4.2 : Loops
    Loop is something that repeats given code until it can be concluded.
    We use
    loop exitwhen true endloop
    tags for this structure;
    Code (vJASS):
    function GetStrength takes unit u returns integer
        local integer i
        loop //loop, just like in gui
            set i = i + 1
            exitwhen i == GetUnitLevel(u) //exitwhen (true) quits the loop
        endloop //end the loop
        return i
    endfunction
    Don't use this function in your map, it's worthless :p Let's use the loop a bit differently.

    exitwhen <boolexpr>
    is necessary for the loop to conclude successfully. If you don't exit your loop, you'll reach OP (operation) limit and crash the thread (simply put a process).
    exitwhen true
    works fine if you want to exit the loop without expections.

    4.3 : Group Loop
    Hope you didn't remove the footmen and peasants, we'll now recolor them via different kind of method :)

    Code (vJASS):
    scope myScope initializer init
        private function init takes nothing returns nothing
            local group g = CreateGroup()
            local unit FoG //create handler for unit, don't assign it to anything
            call GroupEnumUnitsInRect(g, bj_mapInitialPlayableArea, null)
            loop //loop, just like in gui
                set FoG = FirstOfGroup(g) //assign FoG to first unit in g
                exitwhen FoG == null //exitwhen (true) quits the loop
                call SetUnitColor(FoG, PLAYER_COLOR_BLUE) //custom function, not necessary for FoG loop
                call GroupRemoveUnit(g, FoG) //remove FoG from g to not to cause infinite loop
            endloop //end the loop
            call DestroyGroup(g) //destroy g
            set g = null //nullify g
        endfunction
    endscope
    Save and test the map, you should see your units turn blue. If they did, good job, you're doing great! :)

    But be careful when using this kind of loop, while it has no problems on-demand filled groups (groups that are filled by enumerations within the local block where you perform the FoG iteration), it has issues with groups holding units for a period of time. See GroupUtils for more details.

    4.4 : If / Then / Else
    if then else elseif endif
    , these are the elements used for creating conditions inside functions. We'll have
    native GetRandomInt takes integer lowBound, integer highBound returns integer
    to do us the condition. Modify the loop part on your trigger to look something like this:
    Code (vJASS):
    scope myScope initializer init
        private function init takes nothing returns nothing
            local group g = CreateGroup()
            local unit FoG
            call GroupEnumUnitsInRect(g, bj_mapInitialPlayableArea, null)
            loop
                set FoG = FirstOfGroup(g)
                exitwhen FoG == null
                if GetRandomInt(0,1)==0 then //if (true) then
                    call SetUnitColor(FoG, PLAYER_COLOR_BLUE) //do this
                else //if not (true) then
                    call SetUnitColor(FoG, PLAYER_COLOR_CYAN) //do this instead
                endif //remember to always end your if:s!
                call GroupRemoveUnit(g, FoG)
            endloop
            call DestroyGroup(g)
            set g = null
        endfunction
    endscope
    Again save and test the map, you should see that some of the peasants turned teal instead of blue.

    4.4.1 : Elseif
    elseif
    is only checked at if the first
    if
    didn't trigger. Example:
    Code (vJASS):
    if myInt==3 then
        call BJDebugMsg("it's 3!")
    elseif myInt==1 then
        call BJDebugMsg("it's 1!")
    else
        call BJDebugMsg("it's alien!")
    endif

    This will come handy at some point of your code.​

    4.5 : Unit IDs
    Go to object editor, find footman. Press hotkey Ctrl + D, you should see the name of footman giving you a code "hfoo". This is actually integer. The format in JASS is
    'hfoo'
    . Now did I call it integer, it looks more like a string to us right?
    call BJDebugMsg(I2S('hfoo'))
    will print out 1751543663, so it is integer after all, it's just in disguise. Let's turn footmen blue, and peasants teal.
    Code (vJASS):
    scope myScope initializer init
        private function init takes nothing returns nothing
            local group g = CreateGroup()
            local unit FoG
            call GroupEnumUnitsInRect(g, bj_mapInitialPlayableArea, null)
            loop
                set FoG = FirstOfGroup(g)
                exitwhen FoG == null
                if GetUnitTypeId(FoG)=='hfoo' then //if the picked unit has this integer as id, it'll return true
                    call SetUnitColor(FoG, PLAYER_COLOR_BLUE)
                else
                    call SetUnitColor(FoG, PLAYER_COLOR_CYAN)
                endif
                call GroupRemoveUnit(g, FoG)
            endloop
            call DestroyGroup(g)
            set g = null
        endfunction
    endscope
    Save and test, if footmen turned blue and peasants teal, I'm damn proud of you :D

    4.6 : Group filters
    Let's fix the null in
    call GroupEnumUnitsInRect(g, bj_mapInitialPlayableArea, null)
    . The declaration for the native is
    native GroupEnumUnitsInRect takes group whichGroup, rect r, boolexpr filter returns nothing
    so you can see the null is boolexpr. It filters whether an unit can be added to the group. Let's make a simple filter which checks if unit is dead:
    Code (vJASS):
    private function groupFilter takes nothing returns boolean
        return not IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD)
    endfunction
    Naturally you need to change the line where you define your group to this -->
    call GroupEnumUnitsInRect(g, bj_mapInitialPlayableArea, Filter(function groupFilter))

    Save and test, it shouldn't affect the map in any way.

    I only scratched the surface of JASS features. Why? Because when you discover stuff by yourself, you get the most out of it. When editing JASS code in JNGP, you should see "Function List" button above your code. Click and explore it well, and you can become the next JASS god :p And I really recommend messing with FoG loop, it's very effective way of browsing through unit groups in a single function.


    [​IMG] 5 : Efficient JASS Coding Tips


    We all know our code can cause major performance impact on the map we work on. Sometimes the performance drop can be felt only after an hour of gameplay, due to thousands of leaks in your map.

    I don't want my dear student to make badly written maps, so here's some tips to avoid faulty scripts.

    5.1 : Always null abstract locals
    Certain non-nullied locals can cause performance impact. Abstract is something conceptual, for example an unit; we define unit by certain properties and unit is always a subject to change in-game.

    Something not abstract is integer, we are certain 3 is 3.
    To null local use
    set myLocal = null

    For further learning about which variable types should be nullied, open up this page. Null everything which extends widget or agent; these have abstract properties so you need to null them.

    5.2 : Avoid extra native calls
    Native functions are super useful and without them you basically couldn't do anything visual or physical in-game. But as these are often visual functions, they have their performance impact.

    By extra I mean like this:
    Code (vJASS):
    call BJDebugMsg(GetUnitName(GetTriggerUnit())+" just killed someone!")
    call BJDebugMsg("he's level "+I2S(GetUnitLevel(GetTriggerUnit())))

    You could just store the result gained from
    GetTriggerUnit()
    - native into a variable.

    For example, hashtables have more flexible structure than variable arrays or plain variables, but take more memory AND are slower on delivering data.
    Everytime you're planning to use only one dimension of hashtable, you should consider; could it be possible with only arrays?
    E.g.
    set myString = LoadStr(HASHTABLE, 0, i)
    -->
    set myString = STRING[i]

    Other example is hidden native calls. Most often BJs are functions like:
    Code (vJASS):
    function AttachSoundToUnitBJ takes sound soundHandle, unit whichUnit returns nothing
        call AttachSoundToUnit(soundHandle, whichUnit)
    endfunction
    Avoid hidden native calls to ensure efficient code.

    5.3 : Pre-calculate for loops
    Example right away:
    Code (vJASS):
    function loop
        ...
        set x = GetUnitX(missile)
        set y = GetUnitY(missile)
        set angle = GetUnitFacing(missile)
        set x = x + 5 * Cos(angle*bj_DEGTORAD)
        set y = y + 5 * Sin(angle*bj_DEGTORAD)
        call SetUnitX(missile, x)
        call SetUnitY(missile, y)
        ...
    endfunction
    function cast
        ...
        set missile = CreateUnit(...)
        ...
    endfunction
    -->
    Code (vJASS):
    function loop
        ...
        set x = x + 5 * Cos(angle)
        set y = y + 5 * Sin(angle)
        call SetUnitX(missile, x)
        call SetUnitY(missile, y)
        ...
    endfunction
    function cast
        ...
        set x = GetUnitX(caster)
        set y = GetUnitY(caster)
        set angle = GetUnitFacing(caster)*bj_DEGTORAD
        set missile = CreateUnit(...)
        ...
    endfunction
    It's good to reduce function calls for your periodic loops, it makes your spell run smoother and stuff like unit angle, which won't change, can be pre-calculated.

    5.4 : Use Proper Indexing
    Proper indexing is for example dynamic indexing. It's a bit hard to visualize, so I recommend reading Visualize: Dynamic Indexing by PurgeandFire.

    For alternative solution and ease of use, download TimerUtils made by Vexorian.
    A quick guide:
    local timer t = NewTimer()
    - creates a new timer
    call SetTimerData(t, myInteger)
    - defines a number you want to attach to a timer
    call GetTimerData(t)
    - obtains the number you attached before
    call ReleaseTimer(t)
    - destroys the timer
    Instead of looping through all of the instances via one loop - as you probably got used to in GUI world - use timers which function on their own times. This increases performance too.
    Remember to release the timer after you don't need it!

    Downside of TimerUtils is that it uses more memory. Downside of dynamic indexing is that it forces the computer to go through all of the instances at once.

    5.5 : Use variables efficiently
    Example:
    Code (vJASS):
    function myLoop takes nothing returns nothing
        ...
        local group g = CreateGroup()
        call GroupEnumUnits(g)
        loop
            set FoG = FirstOfGroup(g)
            exitwhen FoG == null
            call GroupRemoveUnit(g, FoG)
        endloop
        call DestroyGroup(g)
        set g = null
        ...
    endfunction
    This is a standard FoG loop, what's bad about it? The function name, it's a looping function, and looping functions gotta be as fast as possible.
    Avoid 2 native calls by using global group:
    Code (vJASS):
    globals
        private constant group loopGroup = CreateGroup()
    endglobals
    function myLoop takes nothing returns nothing
        ...
        call GroupEnumUnits(loopGroup)
        loop
            set FoG = FirstOfGroup(loopGroup)
            exitwhen FoG == null
            call GroupRemoveUnit(loopGroup, FoG)
        endloop
        ...
    endfunction
    By adding constant in front of the group, you ensure you won't null it in accident.
    As a side note, you don't have to null FoG, loop concludes only when the FoG is null so the loop takes care of it.

    5.6 : Avoid interfaces
    When you call a method of a struct which extends a certain interface, vJass compiler will convert it to
    TriggerExecute
    which is another native function, which isn't one of the faster ones.
    Avoid interface usage especially on looping functions which are supposed to be fast. I bet you can come up with a workaround, my dear JASS student!

    5.6.1 Avoid
    struct a extends b

    You can avoid a major mess by not using extends at all. I recommend you to do so.

    5.7 : Use Unit Indexer
    I recommend using Perfect Unit Indexing by Cohadar. It indexes only units you need, and is pretty lightweight. There's some other good indexers too.

    A quick guide:
    local integer index = GetUnitIndex(u)
    - gets unit's personal number, creates it if not created yet
    local unit u = GetIndexUnit(index)
    - gets a unit bound to a certain number, if there is any
    There's a native call behind GetUnitIndex, so I recommend indexing e.g. caster's index number instead of calling it in a looping function

    5.8 : Don't do stuff which is already done
    Here's libraries (beside PUI and TimerUtils) I often use and recommend you to also use:
    GeometryLibBase by moyack - Proper way of getting an angle from point to point
    Table by Bribe - One hashtable for your whole map, requires a bit of brainwork to get started
    Table by Vexorian - Alternative to the better Table
    IsDestructableTree by BPower - depends on your map whether you need it, but is certainly useful
    StructuredDD by Cokemonkey11 - Good for certain systems and spells, when you need "is attacked" event


    [​IMG] 6 : End of tutorial


    Hope I taught you something, just PM me if there's something you're having troubles with :) Just remember to complete your map before pumping it to maximum with cool systems, you never know when you're going to lose interest in your project :/ But that's not up to me to teach, go learn about map-making somewhere else ^^

    Sources
    Wikipedia
    Malhorne
    Own experience​

    For advanced tutorials, I recommend:
    • Hive user iAyanami has written a very good tutorial about structs
    • Hive user Earth-Fury has made a marvelous explanation of how computer checks your booleans
    • Hive user Silvenon has written very in-depth tutorial about knockbacks, check it out if you're planning on creating a kb system (requires knowledge of structs)

    Thanks to:
    • edo494, for fix suggestions
    • Malhorne, for helping to finish the tutorial and teaching me JASS
    • chobibo, for helping with tutorial and fix suggestions
    • Magtheridon96, who reviewed my first GUI spell and gave me motivation to continue learning GUI
    • PurgeandFire, for reviewing this tutorial
     

    Attached Files:

    Last edited: Oct 21, 2014
  2. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,855
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    1. Jass is scripting language, not programming

    2.
    TriggerAddCondition(trig, function T)
    is not valid Jass. Its valid if you have Vexorian's JassHelper(dont know if Cohadars allows this).

    This is, because if you pass only function T inside, it must return nothing. If it is meant to be used in TriggerAddCondition, it must be passed to
    Condition
    or
    Filter
    .

    This is implicitly done when your vJass compiles to Jass(you can look at the source, and you will see it will have Condition call)

    You could at least mention this, especially because you have it inside Basics of Jass, and this is illegal Jass code.

    3.
    This may sound for new users, as when the end of the function is reached, nothing else is ever checked. All conditions are checked, and if and only if they all return true, the body is checked. You could reformulate that a little bit maybe.

    4.
    While right, this kind of masks the real concept of the things. The variable is local, the object is global nevertheless.

    You could mention something like this in there(we also null the variable, not the handle, the handle is as local as the trigger object, not at all)

    5. You could maybe explain why we must null all local agents, not just say to do that

    6.
    This is very subjective. And since he wrote code that uses inheritance and stuff like that, as long as you dont use that, you outperform his code already. Yes, you dont outperform him directly, but you cant outperform someone who you dont know how good he is at writing stuff. And pretty much noone knows that, maybe someone that knows him IRL or worked with him on something

    7.
    //create handler for group, assign it to newly created group

    this is quite wrong. You dont really define handler for group, you define local variable, which points to the newly created group. Pointer != handler

    the same goes for
    //create handler for unit, assign it to nothing
    , you dont assign it to nothing(null), you dont assign it at all(again, not handler, but pointer)

    The handler, pointer thingy is just a play of a word and if you think that new Jassers will understand the jass better by reading it as handler, feel free to keep it there. We dont need to call things exactly for sake of calling it exactly.

    I ran out of time, but I dont really see anything that I could nitpick :D
     
  3. BlueSaint

    BlueSaint

    Joined:
    Jun 18, 2012
    Messages:
    2,777
    Resources:
    3
    Tools:
    1
    Spells:
    2
    Resources:
    3
    It says you need jass newgen we at beginning, sorry this is my first tutorial I'm kind of bad at explaining as of now...

    To be honest I don't know what a pointer is. However I've saw a list where wc3 engine defines handle trigger, handle unit, handle location etc. So that's why I thought they're handles, stupid me...

    Thanks A LOT for the feedback though, will fix the things you pointed out tomorrow :)
     
  4. Cokemonkey11

    Cokemonkey11

    Wurst Reviewer

    Joined:
    May 9, 2006
    Messages:
    3,240
    Resources:
    18
    Tools:
    1
    Maps:
    5
    Spells:
    3
    Tutorials:
    2
    JASS:
    7
    Resources:
    18
    You're wrong
     
  5. edo494

    edo494

    Joined:
    Apr 16, 2012
    Messages:
    3,855
    Resources:
    5
    Spells:
    1
    JASS:
    4
    Resources:
    5
    The fact that you have jngp doesnt mean you have JassHelper enabled.

    Trigger, unit and all this are handles, but local variable is not handler
     
  6. BlueSaint

    BlueSaint

    Joined:
    Jun 18, 2012
    Messages:
    2,777
    Resources:
    3
    Tools:
    1
    Spells:
    2
    Resources:
    3
    Oh, didn't know that :) thanks for clarifying!
     
  7. deathismyfriend

    deathismyfriend

    Joined:
    Oct 24, 2012
    Messages:
    6,532
    Resources:
    14
    Spells:
    12
    Tutorials:
    2
    Resources:
    14
    GUI is not fast at all when creating triggers.
    Most people on here type much faster in Jass than they can click everything needed in GUI. And the ones that can't will learn to very shortly.
    Map events in GUI are inefficient and should not be used when using Jass.
    The only thing that should ever be done in GUI is configurations. This means anything that is used in a spell or system that would make it easier for GUIers to understand.

    Actually you do not need libraries / structs / scopes at all when mixing GUI and Jass. Most of my systems are in Jass that mix with GUI to make it easy for GUIers and do not require vJass at all.

    Also if this tutorial is about Jass it should stay about Jass and not cover vJass at all.

    Just link to spells / jass/gui systems instead of making new ones. There are several out there. For Example: My spells are in Jass along with a vJass counterpart. Almost all of my systems are in Jass/GUI to allow for easy use of GUIers.

    If you read the rules it says do not post until you think the tutorial is complete. So please follow that next time.
     
  8. BlueSaint

    BlueSaint

    Joined:
    Jun 18, 2012
    Messages:
    2,777
    Resources:
    3
    Tools:
    1
    Spells:
    2
    Resources:
    3
    @edo494 Did the suggestions you posted, thanks for pointing them out :) Just say if I missed any! Also added couple of things.

    @deathismyfriend ;
    This is meant for GUI users who want to learn JASS. I don't want to say that they can't use GUI anymore, after all I enjoy using GUI for simple triggers in my project :)

    You're 100% right about this one, I fixed that sentence in the tutorial now :)

    But I hate vanilla JASS... D:

    No no, I want to be able to explain everything and use my own format of doing stuff. After all, the people who learn from this should be presented to JASS as a tool which makes things simple, I want to show it via simple system/spell or equivalent.

    I read that post, if you're referring to this. I thought it would be good to get feedback as the tutorial is in development, but okay won't do so again ^^
     
  9. deathismyfriend

    deathismyfriend

    Joined:
    Oct 24, 2012
    Messages:
    6,532
    Resources:
    14
    Spells:
    12
    Tutorials:
    2
    Resources:
    14
    You should write a vJass tutorial then not a Jass tutorial.
     
  10. BlueSaint

    BlueSaint

    Joined:
    Jun 18, 2012
    Messages:
    2,777
    Resources:
    3
    Tools:
    1
    Spells:
    2
    Resources:
    3
    Is it enough if I change the title to
    Simple JASS tutorial - for beginners --> Simple vJASS tutorial - for beginners
    xD
     
  11. deathismyfriend

    deathismyfriend

    Joined:
    Oct 24, 2012
    Messages:
    6,532
    Resources:
    14
    Spells:
    12
    Tutorials:
    2
    Resources:
    14
    I'm not sure that is up to the mods.
    But It is just too simple for explaining Jass. It also would need to explain vJass a lot more.
     
  12. BlueSaint

    BlueSaint

    Joined:
    Jun 18, 2012
    Messages:
    2,777
    Resources:
    3
    Tools:
    1
    Spells:
    2
    Resources:
    3
    That's why I have that disclaimer on top ^^ "NOT FINISHED YET"
     
  13. deathismyfriend

    deathismyfriend

    Joined:
    Oct 24, 2012
    Messages:
    6,532
    Resources:
    14
    Spells:
    12
    Tutorials:
    2
    Resources:
    14
    Yes and I said the above because of this statement you also made.
     
  14. BlueSaint

    BlueSaint

    Joined:
    Jun 18, 2012
    Messages:
    2,777
    Resources:
    3
    Tools:
    1
    Spells:
    2
    Resources:
    3
    Oh okay :D
    Thanks for feedback, will add more content tomorrow.
     
  15. chobibo

    chobibo

    Joined:
    Sep 24, 2005
    Messages:
    2,692
    Resources:
    0
    Resources:
    0
    • Section 4's mastering Jass should be changed to mastering vJass
    • Be careful when using FoG loops, while it has no problems on-demand filled groups (groups that are filled by enumerations within the local block where you perform the FoG iteration), it has issues with groups holding units for a period of time. See GroupUtils for details
    • Your description of native functions are a bit off (minor).
    • native GroupEnumUnitsInRect takes group whichGroup, rect r, boolexpr
      is the declaration of the native function.
    • call GroupEnumUnitsInRect(parameters)
      is the native function itself.
    Done! The tutorial is great, not much technical terms, newb friendly.
     
    Last edited: May 7, 2014
  16. Adiktuz

    Adiktuz

    Joined:
    Oct 16, 2008
    Messages:
    9,674
    Resources:
    23
    Models:
    2
    Packs:
    1
    Maps:
    1
    Spells:
    16
    Tutorials:
    1
    JASS:
    2
    Resources:
    23
    I'd say use Hidden tags or Tab tags to make navigating your tutorials easier..
     
  17. BlueSaint

    BlueSaint

    Joined:
    Jun 18, 2012
    Messages:
    2,777
    Resources:
    3
    Tools:
    1
    Spells:
    2
    Resources:
    3
    @chobibo thanks for review, will fix those later today

    @adiktuz I'm planning on adding pointer tags here and there ^^

    Edit: fixes applied, made the last chapter, added pointers
     
    Last edited: May 8, 2014
  18. _apPie_

    _apPie_

    Joined:
    Feb 28, 2013
    Messages:
    28
    Resources:
    0
    Resources:
    0
    this will help newbies in jass like me :)
     
  19. Garodar

    Garodar

    Joined:
    Dec 11, 2012
    Messages:
    390
    Resources:
    2
    Maps:
    2
    Resources:
    2
    I'll take a deeper look into the guide later but from what I see, you've put a lot of effort into it. Good job.
     
  20. BlueSaint

    BlueSaint

    Joined:
    Jun 18, 2012
    Messages:
    2,777
    Resources:
    3
    Tools:
    1
    Spells:
    2
    Resources:
    3
    Thanks, just tell me if you get lost on some section, so I can change or add info to it.