JassHelper 0.9.H.1

Although World Editor's Jass compiler was finally replaced by PJass using WEHelper , there were a couple of other annoyances that still needed fixing, that's the reason this project begand.

Later I felt like going further and restarted the idea of extending Jass to OOP thus JassHelper is a compiler for the vJass language extension which includes structs, libraries, textmacros and more.

Although this is not really OOP the syntax is powerful enough I hope, there is no inheritance and that's the reason I am not calling the objects classes but structs. There are however, interfaces which allow polymorphism and since they can declare attributes you can have some kind of pseudo inheritance. Pseudo inheritance can be accomplished in many ways.

The design of vJass ought to stay static eventually so I stop adding syntax construct cause that' the right thing to do. After version 1.0.0 there should not be any addition, so if you have requests hurry up cause it would not be healthy to change the syntax after 1.0.0.

Table of contents

I. vJass reference:

  • Jass syntax mods
  • Libraries and Scopes
  • TextMacros
  • Structs and other custom types
  • Storage space enhancements
  • Jass syntax extensions
  • inject
  • Import external script files
  • No vJass!
  • Loading structs from SLK files
  • Script optimization
  • External tools
  • Linebreak fixer
  • II. Installation

    III. Usage

    I. vJass reference:

    If you want actual instructions on how to install and use JassHelper: skip to the usage area, remember to come back here once you have it installed so you can actually take advantage of this solution.

    Global declaration Freedom

    Warcraft III world editor always made everything harder for us, inluding declaring globals, you needed to use that dialog and it was really difficult to recreate global variables from a map or another and you were forced to use GUI for that. It was also impossible to declare globals of some types without modding world editor and then make your map completelly unopenable by normal world editor.

    Global declaration freedom simply allows you to write globals blocks wherever you want, for example in the custom script section or in a 'trigger', and because of this you can even use the constant prefix which can even make finalizers able to inline constants and stuff.

    The JassHelper preprocessor will simply merge all the global blocks found around the map script and move them to the top of the map, all merged in a single globals block.

        function something takes nothing returns nothing
            set somearray[SOMETHING_INDEX]=4
        endfunction
    
        globals
            constant integer SOMETHING_INDEX = 45
            integer array somearray
        endglobals

    Will now work without any error, there is one limitation though, you can't use functions or non-constant values in the default value, for example you can make a global start on null, 1 , 19923 , 0xFFF, true, false, "Hi" , etc. But you can't make them initialize with any function call or with a native (although it is possible to use a native, most natives tend to crash the thread when used in globals declaration). You can't also assign to another global variable, because there isn't really a way to control to what position of the map would a global declaration go.

    Notes:

    Debug preprocessor

    Jass includes a debug keyword which compiles correctly but makes the rest of the code be ignored. It seemed this keyword was used for debugging purposes like a switch in a debug build of warcraft III that enabled those calls.

    We can now take advantage of this hidden Jass feature. Saving a map in debug mode using JassHelper will simply remove the debug keyword so the calls are enabled, if debug mode is not enabled, JassHelper will remove the lines that begin with debug.

       function Something takes integer a returns nothing
           debug call BJDebugMsg("a is "+I2S(a))
           call KillNUnits(a)
       endfunction

    If we use this function in a map saved with debug mode, we will see "a is value" each time this function is called, otherwise it will only call KillNUnits silently.

    Libraries

    Yet another issue with World editor and Jass was that it is impossible to control the order of triggers in the map's script when saved. The custom script partially solved the problem but it is really problematic to ask users to paste things there, it is also anti-modular programming to keep it full of unrelated stuff.

    The library preprocessor allows you to keep your top functions in the top and being able to control where each one goes. It also has an smart requirement support so it will sort the function packs for you.

    The syntax is simple, you use library LIBRARYNAME or library LIBRARYNAME requires ONEREQUIREMENT or library LIBRARYNAME requires REQ1, REQ2 ...

    Remember to mark the end of a library using the endlibrary keyword .

        library B
            function Bfun takes nothing returns nothing
            endfunction
        endlibrary
    
        library A
            function Afun takes nothing returns nothing
            endfunction
        endlibrary

    If JassHelper finds this command, it will make sure to move the Afun and Bfun functions to the top of the map's script, so the rest of the map's script can freely call Afun() or Bfun().

    Notice that it would be uncertain to know what would happen if Afun() was called from a function inside the B library. The command is to move libraries to the top, but we wouldn't know if B went before or after A.

    If a function inside library B needed to call a function inside library A we should let JassHelper know that A must be added before B. That's the reason the 'requires' keyword exists:

        library B requires A
    
            function Bfun takes nothing returns nothing
               call Afun()
            endfunction
    
        endlibrary
    
        library A
    
            function Afun takes nothing returns nothing
            endfunction
    
        endlibrary

    Note: For senseless reasons: requires, needs and uses all work correctly and have the same function in the library syntax.

    It will move Afun to the top of the map and it will place Bfun after it, Bfun can now freely call Afun()

    A library can have multiple requirements, just separate them by commas:

        library C needs A, B, D
            function Cfun takes nothing returns nothing
                call Afun()
                call Bfun()
                call Dfun()
            endfunction
        endlibrary
    
        library D
            function Dfun takes nothing returns nothing
            endfunction
        endlibrary
    
        library B uses A
            function Bfun takes nothing returns nothing
                call Afun()
            endfunction
        endlibrary
    
        library A
            function Afun takes nothing returns nothing
            endfunction
        endlibrary

    The result in the top of the map would be:

        function Afun takes nothing returns nothing
        endfunction
        function Dfun takes nothing returns nothing
        endfunction
        function Bfun takes nothing returns nothing
            call Afun()
        endfunction
        function Cfun takes nothing returns nothing
            call Afun()
            call Bfun()
            call Dfun()
        endfunction

    or maybe:

        function Dfun takes nothing returns nothing
        endfunction
        function Afun takes nothing returns nothing
        endfunction
        function Bfun takes nothing returns nothing
            call Afun()
        endfunction
        function Cfun takes nothing returns nothing
            call Afun()
            call Bfun()
            call Dfun()
        endfunction

    or :

        function Afun takes nothing returns nothing
        endfunction
        function Bfun takes nothing returns nothing
            call Afun()
        endfunction
        function Dfun takes nothing returns nothing
        endfunction
        function Cfun takes nothing returns nothing
            call Afun()
            call Bfun()
            call Dfun()
        endfunction

    It would depend on the order WE saves the scripts in the input file, but notice that since the requirements were set accordingly to the usage of functions by each library none of the 3 possible ways would cause any compile error.

    Make sure to remember that:

    It is also difficult to control what code is executed first, that's the reason libraries also have an initializer keyword, you can add initializer FUNCTION_NAME after the name of a library and it will make it be executed with priority using ExecuteFunc , ExecuteFunc is forced so it uses another thread, most libraries require heavy operations on init so we better prevent the init thread from crashing. After the initializer keyword the 'needs' statement might be used as well.

    The initializers are added to the script in the same order libraries are added. So if library A needs B and both have initializers then B's inititalizer will be called before A's.

    Notice the initializer is a function that takes nothing .

        library A initializer InitA requires B
    
    
        function InitA takes nothing returns nothing
           call StoreInteger(B_gamecache , "a_rect" , Rect(-100.0 , 100.0 , -100.0 , 100  ) )
        endfunction
    
        endlibrary
    
        library B initializer InitB
        globals
            gamecache B_gamecache
        endglobals
    
        function InitB takes nothing returns nothing
            set B_gamecache=InitGameCache("B")
        endfunction
    
        endlibrary

    B's initializer will be called on init before A's initializer.

    Hints:

    Private members

    With the adition of libraries it was a good idea to add some scope control, private members are a great way of protecting users from themselves and to avoid collisions.

    library privatetest
        globals
            private integer N=0
        endglobals
        private function x takes nothing returns nothing
            set N=N+1
        endfunction
    
        function privatetest takes nothing returns nothing
            call x()
            call x()
        endfunction
    endlibrary
    
    library otherprivatetest
        globals
            private integer N=5
        endglobals
        private function x takes nothing returns nothing
            set N=N+1
        endfunction
    
        function otherprivatetest takes nothing returns nothing
            call x()
            call x()
        endfunction
    endlibrary
    

    Notice how both libraries have private globals and functions with the same names, this wouldn't cause any syntax errors since the private preprocessor will make sure that private members are only available for that scope and don't conflict with things named the same present in other scopes. In this case private members are only to be used by the libraries in which they are declared.

    Sometimes, you don't want the code to go to the top of your script (it is not really a function library) yet you' still want to use the private keyword for a group of globals and functions. This is the reason we defined the scope keyword

    The scope keyword has this syntax: scope NAME [...script block...] endscope

    So, functions and other declarations inside an scope can freely use the private members of the scope, but code outside won't be able to. (Notice that a library is to be considered to have an internal scope with its name)

    There are many applications for this feature:

    scope GetUnitDebugStr
    
        private function H2I takes handle h returns integer
            return h
            return 0
        endfunction
    
        function GetUnitDebugStr takes unit u returns string
            return GetUnitName(u)+"_"+I2S(H2I(u))
        endfunction
    endscope
    

    In this case, the function uses H2I, but H2I is a very common function name, so there could be conflicts with other scripts that might declare it as well, you could add a whole preffix to the H2I function yourself, or make the function a library that requires another library H2I, but that can be sometimes too complicated, by using an scope and private you can freely use H2I in that function without worrying. It doesn' matter if another H2I is declared elsewhere and it is not a private function, private also makes the scope keep a priority for its members.

    It is more important for globals because if you make a function pack you might want to disallow direct access to globals but just allow access to some functions, to keep a sense of encapsullation, for example.

    The way private work is actually by automatically prefixing scopename(random digit)__ to the identifier names of the private members. The random digit is a way to let it be truly private so people can not even use them by adding the preffix themselves. A double _ is used because we decided that it is the way to recognize preprocessor-generated variables/functions, so you should avoid to use double __ in your human-declarated identifier names. Being able to recognize preprocessor-generated identifiers is useful when reading the output file (for example when PJass returns syntax errors).

    In order to use private members ExecuteFunc or real value change events you have to use SCOPE_PRIVATE (see bellow)

    Hint:Scopes support initializer just like libraries, there is a difference in implementation and it is that they use a normal call rather than an ExecuteFunc call, if you need a heavy process to init a scope, better use a library initializer or call a subfunction using ExecuteFunc from the scope initializer.

    Note:In a similar way to libraries, scopes used to have a syntax that required //! , that old syntax is deprecated and will cause a syntax error.

    Public members

    Public members are closely related to private members in that they do mostly the same, the difference is that public members don't get their names randomized, and can be used outside the scope. For a variable/function declared as public in an scope called SCP you can just use the declared function/variable name inside the scope, but to use it outside of the scope you call it with an SCP_ preffix.

    An example should be easier to understand:

        library cookiesystem
            public function ko takes nothing returns nothing
                call BJDebugMsg("a")
            endfunction
    
            function thisisnotpublicnorprivate takes nothing returns nothing
                 call ko()
                 call cookiesystem_ko() //cookiesystem_ preffix is optional
            endfunction
        endlibrary
    
        function outside takes nothing returns nothing
             call cookiesystem_ko() //cookiesystem_ preffix is required
        endfunction
    
    

    Public function members can be used by ExecuteFunc or real variable value events, but they always need the scope prefix when used as string:

        library cookiesystem
            public function ko takes nothing returns nothing
                call BJDebugMsg("a")
            endfunction
    
            function thisisnotpublicnorprivate takes nothing returns nothing
                 call ExecuteFunc("cookiesystem_ko") //Needs the prefix no matter it is inside the scope
    
                 call ExecuteFunc("ko") //This will most likely crash the game.
                 call cookiesystem_ko() //Does not need the prefix but can use it.
                 call ko() //since it doesn't need the prefix, the line works correctly.
            endfunction
        endlibrary
    

    Alternatively, you may use SCOPE_PREFIX (see bellow)

    Note: If you use public on a function called InitTrig, it is handled in an special way, instead of becoming ScopeName_InitTrig it will become InitTrig_ScopeName, so you could have an scope/library in a trigger with the same scope name and use this public function instead of manually making InitTrig_Correctname.

    Nested scopes

    Scopes can be nested, don't confuse this statement with `libraries can be nested´, in fact, you cannot even place a library inside an scope definition. You can however, have scope inside either library or scope definitions.

    An scope inside another scope is considered a child scope. A child scope is considered to be a public member of the parent scope (?).

    A child scope cannot declare members that were previously declared as private or global by a parent scope.

    A child scope behaves in relation to its parent in the same way as a normal scope behaves in relation to the whole map script.

    Since child scopes are always public members, you can access a child scope' public members froum outside the parent scope, but it needs a prefix for the parent and a prefix for the child.

    An example :

        library nestedtest
            scope A
              globals
                private integer N=4
              endglobals
    
              public function display takes nothing returns nothing
                call BJDebugMsg(I2S(N))
              endfunction
            endscope
    
            scope B
                globals
                    public integer N=5
                endglobals
    
                public function display takes nothing returns nothing
                    call BJDebugMsg(I2S(N))
                endfunction
            endscope
    
            function nestedDoTest takes nothing returns nothing
                call B_display()
                call A_display()
            endfunction
    
        endlibrary
    
        public function outside takes nothing returns nothing
            set nestedtest_B_N= -4
            call nestedDoTest()
            call nestedtest_A_display()
    
        endfunction
    
    

    The next example will cause a syntax error:

        library nestedtest
            globals
                private integer N=3
            endglobals
    
            scope A
              globals
                private integer N=4 //Error: 'N' redeclared
              endglobals
            endscope
    
        endlibrary
    
    

    It is actually caused by a limitation in the parser, there is a conflict caused by using N for the parent and then declaring it for the child. However this version does not cause syntax errors:

        library nestedtest
            scope A
              globals
                private integer N=4
              endglobals
            endscope
    
            globals
                private integer N=3
            endglobals
    
        endlibrary
    
    
    

    It does kind of the same thing, but since the child's N was declared before the parent's the parser no longer gets in a confusion.

    Another thing to keep in mind is that unlike normal global variables, private/public global variables cannot be used before declared, otherwise JassHelper will think they are just normal variables.

    Scopes cannot be redeclared, there cannot be 2 scopes with the same name. But 2 child scopes might have the same name if they are children of different scopes, the reason is that they actually don't have the same name, they have different names given by their public child scope situation.

        library nestedtest
          scope A
            function kkk takes nothing returns nothing
                set N=N+5  // By the time JassHelper gets to this line, it did not see the private integer N
                           // declaration yet, so it assumes N is an attempt to use a global variable and does not
                           // do any replacement
            endfunction
          endscope
    
       endlibrary
    
       scope X
           scope A
              //Declaring scope A again does not cause any problem, it is because the scope is actually X_A , the
              // previous declaration of A was actually nestedtest_A
              function DoSomething takes nothing returns nothing
              endfunction
    
           endscope
        endscope

    There is no nesting limit, but notice that resulting variable and function names of private/public members get big and bigger depending of the depth of the scope nesting. Bigger variable names may affect performance. Not a lot but they do, and this efficiency issue is to be prevented by an obfuscator/finalizer that renames every identifier in the map script a.k.a the map optimizer's shortest names possible method.

    SCOPE_PREFIX and SCOPE_PRIVATE

    Whenever you are inside an scope/library declaration, SCOPE_PREFIX and SCOPE_PRIVATE are enabled string constants that you could use.

    SCOPE_PREFIX will return the name (as a Jass string) of the current scope concatenated with an underscode. (The prefix added for public memebers)

    SCOPE_PRIVATE will return the name (as a Jass string) of the current prefix for private members.

    scope test
    
        private function kol takes nothing returns nothing
            call BJDebugMsg("...")
        endfunction
    
        function lala takes nothing returns nothing
             call ExecuteFunc(SCOPE_PRIVATE+"kol")
        endfunction
    
    endscope
    
    

    In the example, we are allowing lala() to call the private function kol via ExecuteFunc.

    keyword

    The keyword statement allows you to declare a replacement directive for an scope without declaring an actual function/variable/etc. It is useful for many reasons, the most important of the reasons being that you cannot use a private/public member in an scope before it is declared, in most cases this limitation is no more than an annoyance requiring you to change the position of declarations, in other situations though this is a limitator of other features.

    For example two mutually recursive functions may use .evaluate (keep reading this readme) to call each other, but if you also want the functions to be private it is impossible to do it without using keyword:

    scope myScope
    
       private keyword B //we were able to declare B as private without having to actually
                         //include the statement of the function which would cause conflicts.
    
       private function A takes integer i returns nothing
           if(i!=0) then
               return B.evaluate(i-1)*2 //we can safely use B since it was already
                                        //declared as a private member of the scope
           endif
           return 0
       endfunction
    
       private function B takes integer i returns nothing
           if(i!=0) then
               return A(i-1)*3
           endif
           return 0
       endfunction
    
    endscope
    

    Text Macros

    Let's accept it, sometimes we want very complex things added to Jass but other times, the only thing we actually need is an automatic text copy+paste+replace, textmacros were added because they can be really useful in a lot of very different cases

    The syntax is simply, //! textmacro NAME [takes argument1, argument2, ..., argument n] then //! endtextmacro to finish. And just a runtextmacro to run. It is easier to understand after an example:

        //! textmacro Increase takes TYPEWORD
        function IncreaseStored$TYPEWORD$ takes gamecache g, string m, string l returns nothing
            call Store$TYPEWORD$(g,m,l,GetStored$TYPEWORD$(g,m,l)+1)
        endfunction
        //! endtextmacro
    
        //! runtextmacro Increase("Integer")
        //! runtextmacro Increase("Real")

    The result of the example is:

        function IncreaseStoredInteger takes gamecache g, string m, string l returns nothing
            call StoreInteger(g,m,l,GetStoredInteger(g,m,l)+1)
        endfunction
        function IncreaseStoredReal takes gamecache g, string m, string l returns nothing
            call StoreReal(g,m,l,GetStoredReal(g,m,l)+1)
        endfunction

    The $$ delimiters are required because the replace tokens could require to be together to other symbols.

    Notice that strings and comments are not protected from the text replacement. So if there is a match for any of the arguments delimited by $$ it will always get replaced.

    textmacros don't need arguments, in that case you simply remove the takes keyword.

        //! textmacro bye
        call BJDebugMsg("1")
        call BJDebugMsg("2")
        call BJDebugMsg("3")
        //! endtextmacro
    
        function test takes nothing returns nothing
            //! runtextmacro bye()
            //! runtextmacro bye()
        endfunction

    Textmacros add just a lot of fake dynamism to the language, the next is the typical attach/handle vars call for handles:

        //! textmacro GetSetHandle takes TYPE, TYPENAME
            function GetHandle$TYPENAME$ takes handle h, string k returns $TYPE$
                return GetStoredInteger(udg_handlevars, I2S(H2I(h)), k)
                return null
            endfunction
            function SetHandle$TYPENAME$ takes handle h, string k, $TYPE$ v returns nothing
                call StoredInteger(udg_handlevars,I2S(H2I(h)),k, H2I(v))
            endfunction
        //! endtextmacro
    
        //! runtextmacro GetSetHandle("unit","Unit")
        //! runtextmacro GetSetHandle("location","Loc")
        //! runtextmacro GetSetHandle("item","Item")

    The development time has beed reduced significantly

    Textmacros and scopes/libraries can become best friends by allowing some kind of static object orientedness:

        //! textmacro STACK takes NAME, TYPE, TYPE2STRING
            scope $NAME$
                globals
                    private $TYPE$ array V
                    private integer N=0
                endglobals
                public function push takes $TYPE$ val returns nothing
                    set V[N]=val
                    set N=N+1
                endfunction
    
                public function pop takes nothing returns $TYPE$
                    set N=N-1
                    return V[N]
                endfunction
    
                public function print takes nothing returns nothing
                 local integer a=N-1
                    call BJDebugMsg("Contents of $TYPE$ stack $NAME$:")
                    loop
                        exitwhen a<0
                        call BJDebugMsg(" "+$TYPE2STRING$(V[a]))
                        set a=a-1
                    endloop
                endfunction
            endscope
        //! endtextmacro
    
        //! runtextmacro STACK("StackA","integer","I2S")
        //! runtextmacro STACK("StackB","integer","I2S")
        //! runtextmacro STACK("StackC","string","")
        function Test takes nothing returns nothing
            call StackA_push(4)
            call StackA_push(5)
            call StackB_push(StackA_pop())
            call StackA_push(7)
            call StackA_print()
            call StackB_print()
            call StackC_push("A")
            call StackC_push("B")
            call StackC_push("C")
            call StackC_print()
        endfunction
    
    

    Hint: You can use textmacro_once in a similar way to library_once.

    Structs

    Structs introduce Jass to the object oriented programming paradigm.

    I am unable to explain them without making an example first:

    struct pair
        integer x
        integer y
    endstruct
    
    function testpairs takes nothing returns nothing
     local pair A=pair.create()
        set A.x=5
        set A.x=8
    
        call BJDebugMsg(I2S(A.x)+" : "+I2S(A.y))
    
        call pair.destroy(A)
    
    endfunction

    As you can see, you can store multiple values in a single struct, then you can just use the struct as if it was another Jass type, notice the . syntax which is used for members on most common languages.

    Declaring structs

    Before using an struct you need to declare it, duh. The syntax is simply using the struct <name> and endstruct keyword, notice how similar they are to global blocks

    To declare a member you simply use <type> <name> [= initial value]

    In the above example we are declaring an struct type named pair, which has 2 members: x and y, they do not have an initial value set.

    It is usually a good idea to assign initial values to the members, so that you don't have to manually initialize them after creating an object of the struct type, the usual default values are the null ones, but depending on the problem you want to solve you could need any other value.

    Creating and destroying structs

    Structs are pseudo-dynamic, you would often need to create and destroy structs, and you should create an struct an assign it to a variable before using it.

    The syntax to create an struct (It is actually to get a unique id) is : structtypename.create()

    In the case of the above struct you would have to use pair.create() to get a new struct.

    JassHelper is just a preprocessor not a hack so whatever adition to Jass we add is still limited by Jass' own limitations, in this case, structs use arrays which have a 8191 values limit, and we cannot use index 0 which is null for structs, so there is an 8190 instances limit. This limit is for instances of each type, so you may have 8190 objects of a pair struct type and still are able to have many other instances of other types.

    It means that if you keep creating many structs of a type without destroying them you would eventually reach the limit. So keep in mind this: IN the case the instances limit of a type is reached structtype.create() WILL RETURN 0.

    The limit is not usually a worrying issue, 8190 is in practice a huge number, unless you want to make linked lists or things like that, but those should be solved by a lower level approach.

    For example, if you are only using structs for spell instance data, it is even impossible to get more than 9 instances. And many other practical applications would never need more than 2000 instances.

    UNLESS, of course some structs that are not used anymore are not getting destroyed. In that case the limit is reached by a bug in the usage of structs that should be fixed (In the case of structs, unlike handles, not destroying them does not increase the memory usage, but the risk of reaching the limit)

    If you make calculations, if you create an struct per second and always forget to remove it, the map would need 2 hour, 16 minutes in order to reach the limit.

    Either way if you are making this for usage by other people and are unsure about the possibility of reaching the limit you can always use a comparission with 0 after calling create() and block the process somehow in case the error is found.

    In case the limit is reached and you don't have a way to catch it, struct 0 would be used and assigned and probably cause some conflicts later, the strenght of the conflicts could be null or huge depending on the way you are using the structs.

    If debug mode is on when compiling the script, create() will show a warning message whenever the limit is reached.

    To destroy an struct you simply use the destroy method which can work as an instance method or as a class method, in the above example call pair.destroy(a) is used to destroy the instance, but you could also use call a.destroy().

    In the case you attempt to destroy the zero struct destroy will do nothing or would show a warning message if debug mode is on.

    Struct usage

    Just declare struct values the way you declare variables/functions/arguments of normal types.

    Once an struct is declared and you create it and you want to access it, you have to access its members, the way is often (struct value).(member name) , in the case of pairs you use pair.x to access the x member and pair.y to access the y member.

    Once a member is accessed the usage is quite similar to the usage of a variable. You can use it in set statements or as a value inside expressions.

    struct pair
        integer x=1
        integer y=2
    endstruct
    
    function pair_sum takes pair A, pair B returns pair
     local pair C=pair.create()
        set C.x=A.x+B.x
        set C.y=A.y+B.y
     return C
    endfunction
    
    function testpairs takes nothing returns nothing
     local pair A=pair.create()
     local pair B=pair_sum(A, A)
     local pair C=pair_sum(A,B)
    
        call BJDebugMsg(I2S(C.x)+" : "+I2S(C.y))
    
        //Dont forget, if you are not using an struct instance anymore, you destroy it
        call B.destroy()
        call C.destroy()
        call pair.destroy(A)
    
    endfunction

    It would display "3 : 6"

    Instance members

    So, you can declare struct members of any type, even struct types. You cannot however declare array members, this limitation should be removed in later versions.

    struct pairpair
        pair x=0 //you cannot use pair.create() and should actually only use constants for default initial values.
        pair y=0
    endstruct
    
    function testpairs takes nothing returns nothing
     local pairpair A=pairpair.create()
     local pair x
    
        set A.x=pair.create()
        set A.y=pair.create()
    
        set x=A.y //notice we are saving A.y in a backup variable so we can destroy it.
        set A.y= pair_sum(A.x,A.y) //this replaces A.y that's the reason we saved it
    
        call BJDebugMsg(I2S(  A.y.x )+" : "+I2S( A.y.y )) //notice the nesting of the . operator
    
        call A.x.destroy()
        call A.y.destroy()
        call A.destroy()
        call x.destroy()
    endfunction

    Globals of struct types

    You can have globals of struct types. Because of Jass' limitations you cannot initialize them directly.

    globals
         pair globalpair=0 //legal
         pair globalpair2= pair.create() //not legal
    endglobals

    You would have to assign them in an init function instead.

    Static members

    An static member would just behave like a global variable inside struct syntax, just add the static keyword before the member syntax. There can be static arrays as well.

    They might be useful in conjunction to methods.

    public/private Structs

    You can declare scope's public or private structs and struct variables with all freedom

    scope cool
        public struct a
            integer x
        endstruct
    
        globals
            a x
            public a b
        endglobals
    
    
        public function test takes nothing returns nothing
            set b = a.create()
            set b.x = 3
            call b.destroy()
        endfunction
    
    endscope
    
    function test takes nothing returns nothing
     local cool_a x=cool_a.create()
        set a.x=6
        call a.destroy()
    endfunction
    

    Methods

    Methods are just like functions, the difference is that they are associated with the class, also [normal] methods are associated with an instance (in this case 'this')

    Once again an example is needed

        struct point
            real x=0.0
            real y=0.0
    
            method move takes real tx, real ty returns nothing
                set this.x=tx
                set this.y=ty
            endmethod
    
        endstruct
    
        function testpoint takes nothing returns nothing
         local point p=point.create()
            call p.move(56,89)
    
            call BJDebugMsg(R2S(p.x))
        endfunction
    

    this : A keyword that denotes a pointer to current instance, [normal] methods are instance methods which means that they are called from an already assigned variable/value of that type, and this will point to it. In the above example we use this inside the method to assign x and y, when calling p.move() it ends up modiffying x and y attributes for the struct pointed by p.

    method syntax : You might notice that method syntax is really similar to function syntax.

    this is optional : You can use a single . instead of this. when you are inside an instance method. (For example set .member = value)

    Methods are different to normal functions in that they can be called from any place (except global declarations) and that you are not necessarily able to use waits, sync natives or GetTriggeringTrigger() inside them (but you may use any other event response), you might be able to use them but it depends on various factors, it is not recommended to use them at all. In next versions the compiler might even raise a syntax error when it finds them.

    Encapsullation

    Encapsullation is an object oriented programming concept in which you only give access to things that need to have access, in other words it is private and public for structs

        struct encap
            real a=0.0
            private real b=0.0
            public real c=4.5
    
            method randomize takes nothing returns nothing
                // All legal:
                set this.a= GetRandomReal(0,45.0)
                set this.b= GetRandomReal(0,45.0)
                set this.c= GetRandomReal(0,45.0)
            endmethod
    
        endstruct
    
        function test takes nothing returns nothing
         local encap e=encap.create()
    
             call BJDebugMsg(R2S(e.a)) //legal
             call BJDebugMsg(R2S(e.c)) //legal
             call BJDebugMsg(R2S(e.b)) //syntax error
    
        endfunction
    

    private members can only be used inside the struct declaration. Public and private are options for both variables and methods.

    All struct members are public by default, so the public keyword is not necessary, on the other hand you must specify private. Something to point out is the existance of the readonly keyword, it allows code outside the struct to read the variable but not to assign it. It is a nonstandard at the moment, so you shouldn't use it on public releases.

    Static methods

    Static methods or class methods do not use an instance, they are actually like functions but since they are declared inside the struct they can use private members.

        struct encap
            real a=0.0
            private real b=0.0
            public real c=4.5
    
            private method dosomething takes nothing returns nothing
                if (this.a==5) then
                    set this.a=56
                endif
            endmethod
    
            static method altcreate takes real a, real b, real c returns encap
             local encap r=encap.create()
                set r.a=a
                set r.b=b
                set r.c=c
                call r.dosomething() //even though it is private you can use
                                     //it since we are inside the struct declaration
             return r
            endmethod
    
            method randomize takes nothing returns nothing
                // All legal:
                set this.a= GetRandomReal(0,45.0)
                set this.b= GetRandomReal(0,45.0)
                set this.c= GetRandomReal(0,45.0)
            endmethod
    
        endstruct
    
        function test takes nothing returns nothing
         local encap e=encap.altcreate(5,12.4,78.0)
             call BJDebugMsg(R2S(e.a)+" , "+R2S(e.c))
        endfunction
    

    You might notice that the usual create() syntax works like an static method, and destroy() can work as static or instance method

    You can override the static method create by declaring your own one, once you do it, you might require another method just to allocate a unique id for the struct, this is the allocate() static method which is added by default to all structs, it is a private method. When a struct does not have an specific create method declared, jasshelper will use allocate directly when .create is called.

    struct vec
       real x
       real y
       real z
    
       // static method create must return a value of the struct's type
       // create may have arguments.
       static method create takes real ax, real ay, real az returns vec
         local vec r= vec.allocate() //allocate() is private and
                                     //it gets a unique id for the struct
             set r.x=ax
             set r.y=ay
             set r.z=az
    
         return r
       endmethod
    
    endstruct
    
    function test takes nothing returns nothing
     local vec v= vec.create(1.0 , 0.0 , -1.0 )
    
        call BJDebugMsg( R2S(v.z) )
    
        call v.destroy()
    endfunction
    

    Static methods that take nothing can also be used as code values

        struct something
            static method bb takes nothing returns nothing
                call BJDebugMsg("!!")
            endmethod
        endstruct
    
        function atest takes nothing returns nothing
         local trigger t=CreateTrigger()
             call TriggerAddAction(t, function something.bb)
             call TriggerExecute(t)
        endfunction
    

    The onDestroy method

    There is no actual syntax for destructors, but there is a rule and it is that if the struct has a method called onDestroy, it is always automatically called when .destroy() is issued on an instance.

    It is useful to have onDestroy when an instance of the type may hold things that have to be correctly cleaned, it saves time and even makes things safer.

        struct sta
            real a
            real b
        endstruct
    
        struct stb
            sta H=0
            sta K=0
    
            method onDestroy takes nothing returns nothing
                if (H!=0) then
                    call sta.destroy(H)
                endif
                if (K!=0) then
                    call sta.destroy(K)
                endif
            endmethod
        endstruct
    
    

    In the above example, it is only needed to destroy the object of type stb and it would automatically destroy the attached objects of type sta if present.

    The onInit method

    It is usual to need some initialization to be done to an struct's static members during map initialization, you can use an static onInit method to make code execute during map initialization.

    Notice struct initializations are executed before any library initializer, if you require a library initializer to be executed before your initialization, use a library initializer instead. The relative order between different struct initializers is undefined.

         struct A
            static integer