wc3campaigns
WC3C Homepage - www.wc3c.netUser Control Panel (Requires Log-In)Engage in discussions with other users and join contests in the WC3C forums!Read one of our many tutorials, ranging in difficulty from beginner to advanced!Show off your artistic talents in the WC3C Gallery!Download quality models, textures, spells (vJASS/JASS), systems, and scripts!Download maps that have passed through our rigorous approval process!

Go Back   Wc3C.net > Tutorials > JASS/AI scripts tutorials
User Name
Password
Register Rules Get Hosted! Chat Pastebin FAQ and Rules Members List Calendar



Reply
 
Thread Tools Search this Thread
Old 03-09-2007, 04:32 AM   #1
wantok
User
 
Join Date: Jan 2006
Posts: 97

Submissions (1)

wantok is on a distinguished road (20)

Default Textmacros in vJass

To use textmacros you must have JassHelper (enabled by Grimoire or WEHelper). You can download it here.

If you haven't already read Vexorian's tutorial on struct usage, I recommend that you do so before reading this tutorial.

This tutorial will cover three main points:
I. What is a textmacro?
II. Using textmacros to reduce repetitive coding
III. Generic functions and textmacros



I. What is a textmacro?

Think of textmacros as instructions that the preprocessor uses to create a function or a segment of code.

For example:
Collapse JASS:
//! textmacro TEST
    call BJDebugMsg("this is only a test")
//! endtextmacro

//! runtextmacro TEST()

When you run this textmacro the preprocessor will replace //! runtextmacro TEST() with "call BJDebugMsg("this is only a test")" when it compiles your script.

Textmacros can also take arguments
Collapse JASS:
//! textmacro TEST takes NAME
    function $NAME$ takes nothing returns nothing
        call BJDebugMsg("This function's name is: $NAME$")
    endfunction
//! endtextmacro

Every instance of $NAME$ will be replaced by whatever you make NAME be when you run the textmacro.

Collapse JASS:
//! runtextmacro TEST("Test1")
//! runtextmacro TEST("Test2")
//! runtextmacro TEST("Test3")

When the preprocessor finds these lines it will create three functions and place them where you ran the textmacros.

Collapse JASS:
function Test1 takes nothing returns nothing
    call BJDebugMsg("This function's name is: Test1")
endfunction
function Test2 takes nothing returns nothing
    call BJDebugMsg("This function's name is: Test2")
endfunction
function Test3 takes nothing returns nothing
    call BJDebugMsg("This function's name is: Test3")
endfunction

It's as simple as that. You just created three different functions quickly and easily.

II. Using textmacros to reduce repetitive coding

Now to apply this to something useful. If you read Vexorian's tutorial on struct usage, you saw the method he used to create structs and loop through an array of them periodically performing actions. It involves creating three globals and also some repetitive moving of array values, starting and pausing timers, and setting global integer values.
It's not that big of a deal to do this for just one spell, but imagine if you have 20 different spells that also have periodic events. This is where textmacros come in handy.

First, look through the script and find the actions you will be doing for every spell.
Collapse JASS:
function OpticalFlare_Timer takes nothing returns nothing
 local integer i=0
 local flaredata dat
 local real nx
 local real ny
 local real a
 local real d

    loop
        exitwhen i==OpticalFlare_total
        set dat= OpticalFlare_Ar[i]
        if (dat.destroyplease) then
             //This is tricky.
             // We already have the ith flaredata referenced by dat, so we
             // will first remove it from the array, since the order does not matter
             // we can simply move the last flaredata to this position and decrease
             // the total

             set  OpticalFlare_Ar[i]= OpticalFlare_Ar[ OpticalFlare_total - 1]
             set OpticalFlare_total=OpticalFlare_total-1

             //now we are free to destroy the dat
             call dat.destroy() //another way to destroy struct objects.

             //If we don't substract i, it will skip the new object we just moved
             // to the ith position (we are increasing i later)
             set i=i-1

        else //Notice how this ressembles the original expire function
              set nx=GetUnitX(dat.b)
              set ny=GetUnitY(dat.b)
              set a=ModuloReal( Atan2(ny-dat.y , nx-dat.x ) , 2*bj_PI)
              set d= SquareRoot(Pow(dat.x-nx,2) +Pow(dat.y-ny,2))

              if ModuloReal(a+bj_PI/4,bj_PI*2)<=bj_PI then
                  set d=d*40
              endif
              call SetUnitPosition(dat.sh,nx+d*Cos(a),ny+d*Sin(a) )
              set dat.x=nx
              set dat.y=ny
              call SetUnitFacing(    dat.sh,GetUnitFacing( dat.b ))
              call SetUnitFlyHeight( dat.sh, GetUnitFlyHeight( dat.b )+120,0)
        endif
        set i=i+1
    endloop

    if (OpticalFlare_total==0) then
        // let's pause the timer, it is not needed anymore
        call PauseTimer(OpticalFlare_timer)
    endif

endfunction

function OpticalFlare_Effect takes unit b, integer l returns nothing
 local real ac=GetUnitAcquireRange(b)
 local unit sh=CreateUnit( GetOwningPlayer(b), OpticalFlare_FakeSightUnit(), GetUnitX(b), GetUnitY(b), 0)
 local integer abi=OpticalFlareDetectDetector(b )
 local flaredata dat = flaredata.create()
    call SetUnitPathing(sh,false)
    call UnitRemoveAbility(b, abi )
    call UnitAddAbility(b, OpticalFlare_SightDestructorSpellId() )
    call UnitMakeAbilityPermanent(b,true,OpticalFlare_SightDestructorSpellId())
    call UnitMakeAbilityPermanent(b,true,OpticalFlare_BuffId())

    set dat.x=0. //struct fields aren't automatically initialized to 0, unlike handle vars
    set dat.y=0. // It is also possible to make them initialize to a default value inside the struct block
    set dat.b=b
    set dat.sh=sh

    if(OpticalFlare_total==0) then //There are no elements in the array so the timer is inactive
        call TimerStart(OpticalFlare_timer,0.01,true,function OpticalFlare_Timer) //restart it
    endif

    set OpticalFlare_total=OpticalFlare_total+1 //increase the number of elements
    set OpticalFlare_Ar[ OpticalFlare_total-1 ] = dat //add the flaredata to the array, notice that these are arrays
                                                      //that begin with the [0] index...
    loop
        exitwhen IsUnitDeadBJ(b)
        call TriggerSleepAction(0)
        exitwhen not UnitHasBuffBJ(b, OpticalFlare_BuffId() )
    endloop
    call UnitRemoveAbility(b, OpticalFlare_SightDestructorSpellId() )
    call UnitAddAbility(b, abi )

 call RemoveUnit(sh)
 set dat.destroyplease = true //Will send a signal to the timer function so it destroys this object...
 set sh=null
endfunction

The only things that will change in the parts I pulled out below are the variables.

Collapse JASS:
if(OpticalFlare_total==0) then 
    call TimerStart(OpticalFlare_timer,0.01,true,function OpticalFlare_Timer) 
endif
set OpticalFlare_total=OpticalFlare_total+1 
set OpticalFlare_Ar[ OpticalFlare_total-1 ] = dat
//
//
set OpticalFlare_Ar[i]= OpticalFlare_Ar[ OpticalFlare_total - 1]
set OpticalFlare_total=OpticalFlare_total-1
call dat.destroy()
//
//
if (OpticalFlare_total==0) then
    call PauseTimer(OpticalFlare_timer)
endif

The variables are:

The global integer used to count how many structs are active: OpticalFlare_total
The global struct array used to store the structs after you create them: OpticalFlare_Ar
The timer: OpticalFlare_timer
Also the timer callback function will change: OpticalFlare_Timer
And we need to pass the specific instance of the struct to the function so we need to add STRUCTNAME as an argument.


It appears then that our textmacro will need five arguments: TOTAL, STRUCTARRAY, TIMER, STRUCTNAME, and FUNCTION.

A textmacro can contain instructions for more than one function, which is good since we can't have all the actions in the same function. We'll need three different functions.

Collapse JASS:
//! textmacro PeriodicSpell takes TOTAL, STRUCTARRAY, TIMER, FUNCTION, STRUCTNAME
    function Add2Struct takes $STRUCTNAME$ dat, real period returns nothing// the integer dat is the newly created struct.
        if $TOTAL$ == 0 then
            call TimerStart($TIMER$, period, TRUE, function $FUNCTION$)
        endif
        set $TOTAL$ = $TOTAL$ + 1
        set $STRUCTARRAY$[$TOTAL$-1] = dat
    endfunction
    
    function RecycleStruct takes $STRUCTNAME$ dat, integer i returns nothing
        set $STRUCTARRAY$[i] = $STRUCTARRAY$[$TOTAL$-1]
        set $TOTAL$ = $TOTAL$ - 1
        call $STRUCTNAME$.destroy(dat)
    endfunction
    
    function StopTimer takes nothing returns nothing
        if $TOTAL$ == 0 then
            call PauseTimer($TIMER$)
        endif
    endfunction
//! endtextmacro

//! runtextmacro PeriodicSpell("OpticalFlare_total","OpticalFlare_Ar","OpticalFlare_timer","OpticalFlare","OpticalFlare_Timer")

The preprocessor will create the following functions when it compiles:
Collapse JASS:
//textmacro instance: PeriodicSpell("OpticalFlare_total","OpticalFlare_Ar","OpticalFlare_timer","OpticalFlare_Timer","flaredata")
    function Add2Struct takes integer dat,real period returns nothing
        if OpticalFlare_total == 0 then
            call TimerStart(OpticalFlare_timer , period , TRUE , function OpticalFlare_Timer)
        endif
        set OpticalFlare_total = OpticalFlare_total + 1
        set OpticalFlare_Ar[OpticalFlare_total - 1]=dat
    endfunction
    
    function RecycleStruct takes integer dat,integer i returns nothing
        set OpticalFlare_Ar[i]=OpticalFlare_Ar[OpticalFlare_total - 1]
        set OpticalFlare_total = OpticalFlare_total - 1
        call s__flaredata_destroy(dat)
    endfunction
    
    function StopTimer takes nothing returns nothing
        if OpticalFlare_total == 0 then
            call PauseTimer(OpticalFlare_timer)
        endif
    endfunction
//end of: PeriodicSpell("OpticalFlare_total","OpticalFlare_Ar","OpticalFlare_timer","OpticalFlare_Timer","flaredata")

By changing the values of the textmacro arguments you can create functions that will work for all your other spells

Still, it's not very fun to have to type out all those different argument values. It would be nice to reduce the number of arguments necessary.

If you follow the same pattern of naming your variables you can reduce the arguments taken to just one. Also you can add the globals declaration to the textmacro to further reduce how much coding you need to do.

Collapse JASS:
//! textmacro PeriodicSpell_1 takes STRUCTNAME
globals
    $STRUCTNAME$ array $STRUCTNAME$_ar
    timer $STRUCTNAME$_timer = CreateTimer()
    integer $STRUCTNAME$_total = 0
endglobals

  
    function RecycleStruct takes $STRUCTNAME$ dat, integer i returns nothing
        set $STRUCTNAME$_ar[i] = $STRUCTNAME$_ar[$STRUCTNAME$_total-1]
        set $STRUCTNAME$_total = $STRUCTNAME$_total - 1
        call $STRUCTNAME$.destroy(dat)
    endfunction
    
    function StopTimer takes nothing returns nothing
        if $STRUCTNAME$_total == 0 then
            call PauseTimer($STRUCTNAME$_timer)
        endif
    endfunction   
//! endtextmacro

//! textmacro PeriodicSpell_2 takes STRUCTNAME
    function Add2Struct takes $STRUCTNAME$ dat, real period returns nothing// the integer dat is the newly created struct.
        if $STRUCTNAME$_total == 0 then
            call TimerStart($STRUCTNAME$_timer, period, TRUE, function $STRUCTNAME$_timer_callback)
        endif
        set $STRUCTNAME$_total = $STRUCTNAME$_total + 1
        set $STRUCTNAME$_ar[$STRUCTNAME$_total-1] = dat
    endfunction
//! endtextmacro

I split the textmacro into two because the Add2Struct function needs to be below the timer callback function (since that function calls the timer callback function) and the RecycleStruct and StopTimer functions must be above the timer callback function (since they are called in the timer callback function).

There is just one more thing to consider. You may have noticed that the function names are constant in the textmacros we created. This means that if you run the textmacro more than once you will create two functions with the same name. Clearly that is a problem. There are several solutions to the problem.
One method is to use a scope. All you need to do is put the entire set of functions for each spell inside its own scope and make the functions in the textmacro private.

First make a library for your textmacros.

Collapse JASS:
library PeriodicSpellTextmacros
//! textmacro PeriodicSpell_1 takes STRUCTNAME
globals
    $STRUCTNAME$ array $STRUCTNAME$_ar
    timer $STRUCTNAME$_timer = CreateTimer()
    integer $STRUCTNAME$_total = 0
endglobals

  
    private function RecycleStruct takes $STRUCTNAME$ dat, integer i returns nothing
        set $STRUCTNAME$_ar[i] = $STRUCTNAME$_ar[$TOTAL$-1]
        set $STRUCTNAME$_total = $STRUCTNAME$_total - 1
        call $STRUCTNAME$.destroy(dat)
    endfunction
    
    private function StopTimer takes nothing returns nothing
        if $STRUCTNAME$_total == 0 then
            call PauseTimer($STRUCTNAME$_timer)
        endif
    endfunction   
//! endtextmacro

//! textmacro PeriodicSpell_2 takes STRUCTNAME
    private function Add2StructAr takes $STRUCTNAME$ dat, real period returns nothing// the integer dat is the newly created struct.
        if $STRUCTNAME$_total == 0 then
            call TimerStart($STRUCTNAME$_timer, period, TRUE, function $STRUCTNAME$_timer_callback)
        endif
        set $STRUCTNAME$_total = $STRUCTNAME$_total + 1
        set $STRUCTNAME$_ar[$STRUCTNAME$_total-1] = dat
    endfunction
//! endtextmacro

endlibrary

Now make your scope. Private functions inside a scope may only be called by other functions within the scope.

Collapse JASS:
//! scope OpticalFlare

struct OpticalFlare
    unit b  
    unit sh 
    real x 
    real y  
    
    boolean destroyplease = false
endstruct

//! runtextmacro PeriodicSpell_1("OpticalFlare")

function OpticalFlare_timer_callback takes nothing returns nothing
 local integer i=0
 local OpticalFlare dat
 
    loop
        exitwhen i==OpticalFlare_total
        set dat= OpticalFlare_ar[i]
        if (dat.destroyplease) then
             
             call RecycleStruct(dat, i)
             set i=i-1

        else 
              //do periodic actions
        endif
        set i=i+1
    endloop

    call StopTimer()

endfunction

//! runtextmacro PeriodicSpell_2("OpticalFlare")

function OpticalFlare_Effect takes unit b, integer l returns nothing
 local flaredata dat = flaredata.create()
    //set struct values

    call Add2StructAr(dat, .1)
    
endfunction

//! endscope

If you want to add another set of functions just put them in their own scope.
Collapse JASS:
//! scope other
struct other
    unit caster
    integer time
endstruct

//! runtextmacro PeriodicSpell_1("other")


function other_timer_callback takes nothing returns nothing
    local other dat
    local integer i
    
    loop
        exitwhen i == other_count
        set dat = other_ar[i]
        set dat.time = dat.time - 1
        if dat.time == 0 then
            
            call RecycleStruct(dat, i)
            
        else
            //do actions
            set i = i + 1
        endif
    endloop
    call StopTimer()
endfunction

//! runtextmacro PeriodicSpell_2("other")

function createother takes unit u returns nothing
    local other dat = other.create()
    set dat.caster = u
    call Add2StructAr(dat, 1)
endfunction

//! endscope

There is another way you could do this. Don't make functions inside the textmacros. Instead make four different textmacros. The first should have the globals. The last three should look like this:

Collapse JASS:
//! textmacro Recycle takes STRUCTNAME
        set $STRUCTNAME$_ar[i] = $STRUCTNAME$_ar[$STRUCTNAME$_total-1]
        set $STRUCTNAME$_total = $STRUCTNAME$_total - 1
        call $STRUCTNAME$.destroy(dat)
//! endtextmacro

//! textmacro Add takes STRUCTNAME
        if $STRUCTNAME$_total == 0 then
            call TimerStart($STRUCTNAME$_timer, period, TRUE, function $STRUCTNAME$_timer_callback)
        endif
        set $STRUCTNAME$_total = $STRUCTNAME$_total + 1
        set $STRUCTNAME$_ar[$STRUCTNAME$_total-1] = dat
//! endtextmacro

//! textmacro Stop takes STRUCTNAME
        if $STRUCTNAME$_total == 0 then
            call PauseTimer($STRUCTNAME$_timer)
        endif
//! endtextmacro

Now you can run the textmacros within the function:

Collapse JASS:
function other_timer_callback takes nothing returns nothing
    local other dat
    local integer i
    
    loop
        exitwhen i == other_count
        set dat = other_ar[i]
        set dat.time = dat.time - 1
        if dat.time == 0 then
            
            //! runtextmacro Recycle("other")
            
        else
            //do actions
            set i = i + 1
        endif
    endloop
    //! runtextmacro Stop("other")
endfunction

function createother takes unit u returns nothing
    local other dat = other.create()
    set dat.caster = u
    //! runtextmacro Add("other")
endfunction

You probably see other parts of the code that you could make into a textmacro as well.

On to the final section:

III. Generic functions and textmacros

Textmacros allow us to create many different useful functions from one generic function.

Let's say that for some reason you have decided to make a set of functions that handle basic arithmetic calculations for you. You create four functions (+,-,*,/) that take integer values and four that take reals.
Like so:
Collapse JASS:
function add_reals takes real a, real b returns real
    return (a + b)
endfunction

function sub_reals takes real a, real b returns real
    return (a-b)
endfunction

etc...

function add_integers takes integer a, integer b returns integer
    return (a+b)
endfunction

etc...

You can create these functions much faster using a textmacro
Collapse JASS:
//! textmacro arithmetic takes NAME, OPERATOR, TYPE
    function $NAME$$TYPE$s takes $TYPE$ a, $TYPE$ b returns $TYPE$
        return (a$OPERATOR$b)
    endfunction
//! endtextmacro

//! runtextmacro arithmetic("add","+","integer")
//! runtextmacro arithmetic("sub","-","real")
You created a generic function in the textmacro that allows you to use whatever type you specify when you run it.

Another example:

Collapse JASS:
//! textmacro compare takes TYPE
    function Compare_$TYPE$ takes $TYPE$ a, $TYPE$ b returns boolean
        return (a == b)
    endfunction
//! endtextmacro

//! runtextmacro compare("trigger")
//! runtextmacro compare("unit")
//! runtextmacro compare("handle")
//! runtextmacro compare("real")


Vexorian uses the following example in the JassHelper readme file. It shows how the same textmacro can quickly create all the local handle variable functions even though they all return a different type.
Collapse JASS:

//! 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")

If you use this textmacro with either a real or integer type there will be a problem, because null (which is returned in the GetHandle$TYPENAME$ function) is not an integer or a real.

To fix this you could either make another textmacro just for integers and reals (probably not worth the trouble), just code the integer and real functions manually (there are only two after all), or you could add another argument to the textmacro.

Collapse JASS:
//! textmacro GetSetHandle takes TYPE, TYPENAME, RETURN
    function GetHandle$TYPENAME$ takes handle h, string k returns $TYPE$
        return GetStoredInteger(udg_handlevars, I2S(H2I(h)), k)
        return $RETURN$
    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","null")
//! runtextmacro GetSetHandle("integer",Integer","0")

There you have it. Hopefully you see how powerful textmacros can be. You should now be able to make your own.

Be aware that using many textmacros (particularly those inside functions) can make your code much more difficult to read and understand, especially if you've forgotten what the textmacro was supposed to do.

Last edited by wantok : 06-03-2007 at 02:40 AM.
wantok is offline   Reply With Quote
Sponsored Links - Login to hide this ad!
Old 03-09-2007, 11:32 AM   #2
Toink
User
 
Join Date: Oct 2006
Posts: 1,086

Submissions (3)

Toink is on a distinguished road (17)

Send a message via Yahoo to Toink
Default

About time someone made a tut on textmacros.. I'll try to learn this when I can..
Toink is offline   Reply With Quote
Old 03-09-2007, 11:37 AM   #3
Vexorian
Free Software Terrorist
 
Vexorian's Avatar


Technical Director
 
Join Date: Apr 2003
Posts: 14,905

Submissions (37)

Vexorian has a reputation beyond repute (1060)Vexorian has a reputation beyond repute (1060)Vexorian has a reputation beyond repute (1060)Vexorian has a reputation beyond repute (1060)Vexorian has a reputation beyond repute (1060)Vexorian has a reputation beyond repute (1060)Vexorian has a reputation beyond repute (1060)

Hero Contest #3 - 2nd Place

Default

Probably lacks a section about generics

I'd like to mention that I really hate textmacros. Think of them as a necessary evil. People should use them as the last resort, of course if you really want to you are free to abuse them like hell.
__________________
Zoom (requires log in)Wc3 map optimizer 5.0
Someone should fix .wav sound in this thing.
Zoom (requires log in)JassHelper 0.A.2.A
Turns your simple code into something that is complicated enough to work.
Faster != more useful
Vexorian is offline   Reply With Quote
Old 03-09-2007, 09:42 PM   #4
wantok
User
 
Join Date: Jan 2006
Posts: 97

Submissions (1)

wantok is on a distinguished road (20)

Default

When you are talking about generics, am I correct in assuming that you are referring to something like the SetGetHandle textmacro in the Jasshelper readme file? I was going to discuss that but it slipped my mind. I will add a section about using textmacros in that way.

Edit: Updated the tutorial.

Last edited by wantok : 03-10-2007 at 06:47 PM.
wantok is offline   Reply With Quote
Old 03-11-2007, 09:20 PM   #5
PitzerMike
Alcopops
 
PitzerMike's Avatar


Tools & Tutorials Moderator
 
Join Date: Jan 2003
Posts: 2,791

Submissions (12)

PitzerMike is a splendid one to behold (643)PitzerMike is a splendid one to behold (643)PitzerMike is a splendid one to behold (643)PitzerMike is a splendid one to behold (643)

Approved Map: Pitzer's Minesweeper

Default

Cool, approved.
__________________
Zoom (requires log in)
PitzerMike is offline   Reply With Quote
Old 05-25-2007, 07:59 PM   #6
Silvenon
User
 
Silvenon's Avatar
 
Join Date: May 2007
Posts: 492

Submissions (1)

Silvenon will become famous soon enough (63)Silvenon will become famous soon enough (63)Silvenon will become famous soon enough (63)

Send a message via MSN to Silvenon
Default

Nice tutorial, very useful. It's too bad that I don't know what to test it on........

About this part:

Collapse JASS:
//! textmacro compare takes $TYPE$
    function Compare_$TYPE$ takes $TYPE$ a, $TYPE$ b returns boolean
        return (a == b)
    endfunction
//! endtextmacro

//! runtextmacro compare("trigger")
//! runtextmacro compare("unit")
//! runtextmacro compare("handle")
//! runtextmacro compare("real")

in the first line, shouldn't it be TYPE instead of $TYPE$?

Last edited by Silvenon : 05-25-2007 at 08:00 PM.
Silvenon is offline   Reply With Quote
Old 06-02-2007, 09:32 PM   #7
blu_da_noob
Nonchalant
 
blu_da_noob's Avatar


Respected User
 
Join Date: Mar 2006
Posts: 1,933

Submissions (2)

blu_da_noob is just really nice (398)blu_da_noob is just really nice (398)blu_da_noob is just really nice (398)blu_da_noob is just really nice (398)blu_da_noob is just really nice (398)blu_da_noob is just really nice (398)

[Quicksilver #2] - 2nd Place[Quicksilver#1] 1st place

Send a message via MSN to blu_da_noob
Default

That is correct.
__________________
blu_da_noob is offline   Reply With Quote
Old 06-03-2007, 02:42 AM   #8
wantok
User
 
Join Date: Jan 2006
Posts: 97

Submissions (1)

wantok is on a distinguished road (20)

Default

Quote:
Originally Posted by Silvenon
Nice tutorial, very useful. It's too bad that I don't know what to test it on........

About this part:

Collapse JASS:
//! textmacro compare takes $TYPE$
    function Compare_$TYPE$ takes $TYPE$ a, $TYPE$ b returns boolean
        return (a == b)
    endfunction
//! endtextmacro

//! runtextmacro compare("trigger")
//! runtextmacro compare("unit")
//! runtextmacro compare("handle")
//! runtextmacro compare("real")

in the first line, shouldn't it be TYPE instead of $TYPE$?

Whoops, all fixed. Thanks for pointing that out.
wantok is offline   Reply With Quote
Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off


All times are GMT. The time now is 12:13 AM.


Donate

Affiliates
The Hubb http://bylur.com - Warcraft, StarCraft, Diablo and DotA Blog & Forums The JASS Vault Clan WEnW Campaign Creations Clan CBS GamesModding Flixreel Videos

Powered by vBulletin (Copyright ©2000 - 2014, Jelsoft Enterprises Ltd).
Hosted by www.OICcam.com
IT Support and Services provided by Executive IT Services