HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Little School of ABC

11-06-2007, 12:06 PM#1
cohadar
Welcome to the world of ABC.
You can get the latest version from here or here.

This is a thread for all you people who currently use and abuse ABC,
for all you who got sick of gamecache and nightmares with impossible to understand code,
for people who are new to vJass and for pros who always want to be better,
welcome to the world of clean, bug-free and easy to understand code.

I made this thread because I noticed people tend to send me PM's or emails
when they have a problem with stuff they make with ABC,
and I ended up answering to the same questions multiple times.
Private messages cannot be seen by public and cannot benefit many people.

From now on if you have a problem with anything ABC related,
you can post it in here, also feel free to give answers yourself.

In here you will soon find tutorials and demo spells,
also links to cool stuff people made with ABC.

And note that this is also advanced vJass thread,
in here you will find nothing but the top-quality vjass,
and there is no vJass question that will go unanswered.

Enjoy!


//----------------------------------------------------------------------
// LESSONS:
//----------------------------------------------------------------------

Lesson no. 1: Destroying Special Effects - example for using ABC
Lesson no. 2: Local Triggers - writing of a clean WE independent code
Lesson no. 3: Function Interfaces - Conditions vs Actions, Evaluate vs Execute

<lesson no. 3 is still in editing phase>
11-06-2007, 12:15 PM#2
cohadar
Destroying special effects

Well this is quite common operation and I got a few questions about that,
so this will be out first ABC lesson.

Problem:
You made a custom spell and it uses some special effect,
you want to destroy that effects at the end of spell to prevent memory leaks,
but the time when you want to destroy the effect is not always the same.
It depends on spell level and stuff like that, and you cannot possibly use waits because than it would not be MUI.

Easy Solution:

Lets make a function DestroyEffectTimed takes effect fx, real delay returns nothing
With this function we could do stuff like:
call DestroyEffectTimed(fx, 2.0) or call DestroyEffectTimed(fx, level *0.4)
So how exactly do we make a function like that?

Well for starters we will need a timer, that timer will after delay seconds call a function that will destroy our effect.

Unfortunately in jass timer functions don't have parameters so we will have to attach our effect to a timer, that is where ABC comes in play!

But wait ABC is struct attachment system? How do you attach effects with ABC?
You don't!

We will also need a "wrapper" struct around our effect to be able to pass it to timer.

Collapse JASS:
struct EffectWrapper
    effect fx
endstruct

Wait what! That is lame, if I used gamecache I could directly attach effect to timer!

Yes you could, and that is one of the reasons gamecache is evil,
direct handle attachments with gamecache cause infamous I2H bug.
Anything that uses direct handle attachments can be considered bugged.
Pro mappers who still use gamecache don't do direct attachments any more,
they also make wrapper structs.

Mmmkaaay, so lets continue,
where were we, we had a DestroyEffectTimed function a struct that is used as a wrapper for an effect, a timer and a timer function:
lets see how it all looks together:

Collapse JASS:
struct EffectWrapper
    effect fx
endstruct

//==============================================================================
function EffectDestroyer takes nothing returns nothing
    local timer t=GetExpiredTimer()
    local EffectWrapper ew=GetTimerStructA(t) // ABC
    call DestroyEffect(ew.fx)
    call ClearTimerStructA(t) // ABC    
    call ew.destroy()
    call DestroyTimer(t)
    set t = null
endfunction
        
//==============================================================================
function DestroyEffectTimed takes effect fx, real delay returns nothing
    local timer t=CreateTimer()
    local EffectWrapper e=EffectWrapper.create()
    set e.fx=fx
    call SetTimerStructA(t,e) // ABC
    call TimerStart(t,delay,false,function EffectDestroyer)
endfunction

So here it is, in DestroyEffectTimed we create a timer create wrapper struct,
insert our effect inside struct, attach the struct to timer with ABC
and call a timer.

Then when the timer expires it calls EffectDestroyer function
we get the timer, get the struct wrapper,
destroy the effect inside struct, remove the timer->struct link from ABC
destroy the struct and finally destroy and null the timer.

Pretty straightforward is it not?
Well it is simple and it works, but it has a couple of flows.
It can be optimized and made more encapsulated.

Pro Solution:
First of all directly using Create/Destroy timer functions is a noob way of dealing with timers.
Instead of those functions we will be using timer recycling library knows as CSSafety, this library is a part of Vexorian's Caster System.
You don't need to use the whole caster system (I for example use only CSSafety), so for convenience here is only the CSSafety trigger:
Expand JASS:

Ok lets go to the next step.
You noticed in easy solution how you create effect, create struct create timer, destroy effect, destroy struct destroy timer .... + ABC calls

Just too many create/destroy stuff there, can this be simplified?
Of course it can, ... how?

We will use create and onDestroy, two great features of structs,
to make our struct wrapper more easy to use.
Let's go straight to example:

Collapse JASS:
struct EffectWrapper
    private effect fx // note that fx is now private
    
    static method create takes effect fx returns EffectWrapper
        local EffectWrapper ret = EffectWrapper.allocate()
        set ret.fx = fx
        return ret
    endmethod
    
    method onDestroy takes nothing returns nothing
        call DestroyEffect(.fx)
        // set .fx = null // <--- You don't need to null struct variables
    endmethod

endstruct
So now we need to pass an fx directly to create when we make EffectWrapper structs, and our struct will automatically destroy effect inside it! Very nice.

Lets see how it affected other parts of the system:
Collapse JASS:
//==============================================================================
function EffectDestroyer takes nothing returns nothing
    local timer t=GetExpiredTimer()
    local EffectWrapper ew=GetTimerStructA(t) // ABC
    //call DestroyEffect(ew.fx)
    call ew.destroy()
    call ClearTimerStructA(t) // ABC    
    call ReleaseTimer(t) // CSSafety
    // set t = null // We are using CSSafety, no need to null timer variables any more!
endfunction
        
//==============================================================================
function DestroyEffectTimed takes effect fx, real delay returns nothing
    local timer t=NewTimer() // CSSafety
    local EffectWrapper e=EffectWrapper.create(fx)
    //local EffectWrapper e=EffectWrapper.create()
    //set e.fx=fx
    call SetTimerStructA(t,e) // ABC
    call TimerStart(t,delay,false,function EffectDestroyer)
endfunction
Commented out lines are the old code, so you can see how it changed,
you don't need to destroy effect directly any more,
and you don't need to null timer variable because of CSSafety.

On the other hand you did not need to null variables at all,
because unlike other systems ABC is resistant to leaks and not-nulling,
but that is not the issue here, lets continue...

Ok so it is still not good?
Cmon we used some heavy shit here what else can you possibly improve?
Well for starters here is a place for optimization:
This:
Collapse JASS:
//==============================================================================
function EffectDestroyer takes nothing returns nothing
    local timer t=GetExpiredTimer()
    local EffectWrapper ew=GetTimerStructA(t) // ABC
    call ew.destroy()
    call ClearTimerStructA(t) // ABC    
    call ReleaseTimer(t) // CSSafety
endfunction

Can actually be done like this:
Collapse JASS:
//==============================================================================
function EffectDestroyer takes nothing returns nothing
    local timer t=GetExpiredTimer()
    local EffectWrapper ew=ClearTimerStructA(t) // ABC
    call ew.destroy()
    call ReleaseTimer(t) // CSSafety
endfunction
Yes it is true, Clear functions also have a return values (unlike in any other system) so you can use them directly in one-shot timers.

Ok we are almost there, we made our little DestroyEffectTimed function and it is working just like we wanted it to work.
But now we want to use it for all our spells that need to destroy effects,
not just this one.

Time to put this all into a library:
Collapse JASS:
library EffectDestroyer uses ABC, CSSafety

//==============================================================================
private struct EffectWrapper
    private effect fx
    
    static method create takes effect fx returns EffectWrapper
        local EffectWrapper ret = EffectWrapper.allocate()
        set ret.fx = fx
        return ret
    endmethod
    
    method onDestroy takes nothing returns nothing
        call DestroyEffect(.fx)
    endmethod

endstruct


//==============================================================================
private function EffectDestroyer takes nothing returns nothing
    local timer t=GetExpiredTimer()
    local EffectWrapper ew=ClearTimerStructA(t) // ABC
    call ew.destroy()
    call ReleaseTimer(t) // CSSafety
endfunction
        
//==============================================================================
//  Exported function
//==============================================================================
function DestroyEffectTimed takes effect fx, real delay returns nothing
    local timer t=NewTimer() // CSSafety
    local EffectWrapper e=EffectWrapper.create(fx)
    call SetTimerStructA(t,e) // ABC
    call TimerStart(t,delay,false,function EffectDestroyer)
endfunction

endlibrary

Note that we made everything private except DestroyEffectTimed function,
that is the only thing other triggers will be able to use.
We did this because:

1. Noone really cares how DestroyEffectTimed function works inside.
2. Noone will be able to mess with private stuff so they cannot screw up anything even if they wanted to.

This method of hiding stuff from users of a library that they don't need to know/ should not use is in programming jargon called encapsulation.

That's all in this lesson folks!
11-06-2007, 01:50 PM#3
kentchow75
To be continue... Right?
11-06-2007, 01:56 PM#4
cohadar
Local triggers how and why

Ok so you are a jass moderate/pro coder and you just started using vJass
and you have seen people use that
local trigger trig = CreateTrigger()
but you don't really know why.

Have no fear, scopes are near.

Ok lets assume that you want to make custom jass-triggered animate dead spell, and you want your trigger to be started when units casts a standard animate dead spell.


You would naturally start from a trigger editor:
Trigger:
CustomAnimate
Collapse Events
Unit - A unit Starts the effect of an ability
Collapse Conditions
(Ability being cast) Equal to Animate Dead
Actions

Then you would convert it into text:
Collapse JASS:
function Trig_CustomAnimate_Conditions takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == 'AUan' ) ) then
        return false
    endif
    return true
endfunction

function Trig_CustomAnimate_Actions takes nothing returns nothing
endfunction

//===========================================================================
function InitTrig_CustomAnimate takes nothing returns nothing
    set gg_trg_CustomAnimate = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_CustomAnimate, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_CustomAnimate, Condition( function Trig_CustomAnimate_Conditions ) )
    call TriggerAddAction( gg_trg_CustomAnimate, function Trig_CustomAnimate_Actions )
endfunction

And now all you have to do is fill Trig_CustomAnimate_Actions with your crazzy ass custom code for that spell, right?

WRONG!

That is not the way of a vJass warriors and is not the way of JESP.
I mean just look at that code,
it has a bounch of gg_trg stuff it has unnecessary if statements it has raw codes 'AUan' lying around, it is simply asking for trouble.

And it is looking uglier that GUI goddamnit.
If you are to be called masters of vjass you shall write pretty code or vanish in oblivion.

Lets start by converting this:
Collapse JASS:
//===========================================================================
function InitTrig_CustomAnimate takes nothing returns nothing
    set gg_trg_CustomAnimate = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_CustomAnimate, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_CustomAnimate, Condition( function Trig_CustomAnimate_Conditions ) )
    call TriggerAddAction( gg_trg_CustomAnimate, function Trig_CustomAnimate_Actions )
endfunction

To this:
Collapse JASS:
//===========================================================================
function InitTrig_CustomAnimate takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( trig, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( trig, Condition( function Trig_CustomAnimate_Conditions ) )
    call TriggerAddAction( trig, function Trig_CustomAnimate_Actions )
endfunction

See it already looks prettier.

Ok so what did we do here?
we created a local trigger instead of default one, and replaced all those ugly gg_trg names with it.
But the benefit is not only the beauty of code,
the code has become less dependent on WE trigger editor (more on that later)

Next step:
Lets optimize this ugly biach:
Collapse JASS:
function Trig_CustomAnimate_Conditions takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == 'AUan' ) ) then
        return false
    endif
    return true
endfunction

like this:
Collapse JASS:
globals
    constant integer AID_ANIMATE_DEAD = 'AUan'
endglobals

function Trig_CustomAnimate_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == AID_ANIMATE_DEAD
endfunction

ahh globals, greatest feature of vJass, it is to wc3 mapping what wheel is for science.

It is quite obvious now what that condition is doing is it not?
Is someone casts animate dead it will return true and our trigger actions will run, straight on the spot.

And that global integer AID__ ??
Is it constant? What does that mean?

constant means that it can be optimized by wc3optimizer
It also means that you cannot change it's value, but you don't want that do anyway, it is a Id of your triggering spell, that should not be changed by code.

You should ALWAYS extract all 'XXXX' stuff from your triggers into constant integer global constants. - NO EXCEPTIONS.

It is the way to make stuff MUI, it is the way to make stuff JESP
it is the way to remove potential errors from your code.

Prefix naming convention
AID_ = Ability Id
UID_ = unit Id
and so on....

You don't have to use it, you can give your constants any names you like
but you shall make all your constant names in UPPERCASE latters or be called noob forever.

Lets see where we at now:
Collapse JASS:
globals
    constant integer AID_ANIMATE_DEAD = 'AUan'
endglobals

//===========================================================================
function Trig_CustomAnimate_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == AID_ANIMATE_DEAD
endfunction

//===========================================================================
function Trig_CustomAnimate_Actions takes nothing returns nothing
endfunction

//===========================================================================
function InitTrig_CustomAnimate takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( trig, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( trig, Condition( function Trig_CustomAnimate_Conditions ) )
    call TriggerAddAction( trig, function Trig_CustomAnimate_Actions )
endfunction


Ok it looks better than that idiotic GUI generated code,
time to add a final blow: scopes

Brace yourself:
Collapse JASS:
scope CustomAnimate

globals
    constant integer AID_ANIMATE_DEAD = 'AUan'
endglobals

//===========================================================================
private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == AID_ANIMATE_DEAD
endfunction

//===========================================================================
private function Actions takes nothing returns nothing
endfunction

//===========================================================================
public function InitTrig takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( trig, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( trig, Condition( function Conditions ) )
    call TriggerAddAction( trig, function Actions )
endfunction

endscope

Wait what! WTF just happened to the code?

scope , endscope, public, private ? geeez.

First of all notice that code looks fucking awesome,
just compare it to the code we started with!

Now to explain:
scope <TriggerName> should be the first line of code in your trigger (you can put comments before it if you want but nothing else!)

endscope should be the last line obviously.

Now come the 3 changes to InitTrig, to Conditions and to Actions:

this:
Collapse JASS:
//===========================================================================
function Trig_CustomAnimate_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == AID_ANIMATE_DEAD
endfunction

transformed into this:
Collapse JASS:
//===========================================================================
private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == AID_ANIMATE_DEAD
endfunction

Ok what does that mean?

First of all notice that second version does not have Trig_CustomAnimate inside the code, that means that your condition is now independent of in witch trigger it is in, it means that you can just copy it into another trigger and it will work straight away!

But it is called Conditions for god's sake!!!
What if I have two vJass triggers, would not 2 Conditions with same names create errors?

No they would not if they have private prefix !
Private stuff are always different.
Private means that that condition will be used ONLY inside that trigger,
it cannot go outside and mess with other conditions!

Same for Actions.

But InitTrig is public ?

Yes and it should be.
It is the entry point of your trigger, it is what all other triggers see.

InitTrig functions in vJass should always look like this:
public function InitTrig takes nothing returns nothing NO EXCEPTIONS!

And finally:
Hidden information:
Zoom (requires log in)


This lesson is more explaining how than why things should be done this way.

But that is intentional, for why about public and private stuff you should check the jasshelper documentation, no need to rewrite that in here.

For all that matters the biggest why here could simply be the astonishing
difference in look of GUI generated and vJass code.
Attached Images
File type: pngsame.PNG (14.2 KB)
11-06-2007, 03:19 PM#5
kentchow75
Hmm, in my way of doing these. I would just wrap everything into the struct and remain that public calling function.

Static method could be treat as a call back function.
Collapse JASS:
library EffectDestroyer uses ABC, CSSafety

//==============================================================================
private struct EffectWrapper
    private effect fx
    
    static method create takes effect fx returns EffectWrapper
        local EffectWrapper ret = EffectWrapper.allocate()
        set ret.fx = fx
        return ret
    endmethod
    
    static method EffectDestroyer takes nothing returns nothing
        local timer t=GetExpiredTimer()
        local EffectWrapper ew=ClearTimerStructA(t) // ABC
        call ew.destroy()
        call ReleaseTimer(t) // CSSafety
    endmethod

    method onDestroy takes nothing returns nothing
        call DestroyEffect(.fx)
    endmethod

endstruct

        
//==============================================================================
//  Exported function
//==============================================================================
function DestroyEffectTimed takes effect fx, real delay returns nothing
    local timer t=NewTimer() // CSSafety
    local EffectWrapper e=EffectWrapper.create(fx)
    call SetTimerStructA(t,e) // ABC
    call TimerStart(t,delay,false,function EffectDestroyer)
endfunction

endlibrary
11-06-2007, 03:52 PM#6
cohadar
Nice one ~Gals~

One objection thou:
Indent that static method properly, it looks ugly now.
EDIT:
See it looks better now :)
11-07-2007, 10:54 AM#7
kentchow75
>>InitTrig functions in vJass should always look like this:
Collapse JASS:
public function InitTrig takes nothing returns nothing
NO EXCEPTIONS!

No exceptions?
Actually....
Collapse JASS:
public function InitTrig takes nothing returns nothing
Collapse JASS:
function InitTrig takes nothing returns nothing
Works the same. Function that has no public prefix is treated as a public function.

>>Scope name and the trigger name MUST be the same or else your trigger won't work.
Hmm, is this true? I don't really see it anywhere on the manual

>>Indent that static method properly, it looks ugly now.
Sorry boss. I just did some cut-paste on your code. I didnt really have my JC2 opened and start teh editing.
The forum doesn't support TAB encap.

IMPORTANT NOTE
Quote:
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).
11-07-2007, 01:18 PM#8
cohadar
Quote:
Originally Posted by kentchow75
>>InitTrig functions in vJass should always look like this:
Collapse JASS:
public function InitTrig takes nothing returns nothing
NO EXCEPTIONS!

No exceptions?
Actually....
Collapse JASS:
public function InitTrig takes nothing returns nothing
Collapse JASS:
function InitTrig takes nothing returns nothing
Works the same. Function that has no public prefix is treated as a public function.
No they are not treated the same.
public keyword in jass does not work the same like in java or C#


Quote:
Originally Posted by kentchow75
>>Scope name and the trigger name MUST be the same or else your trigger won't work.
Hmm, is this true? I don't really see it anywhere on the manual
library names do not have to have the same name as triggers they are in,
scopes do.


Quote:
Originally Posted by kentchow75
>>Indent that static method properly, it looks ugly now.
Sorry boss. I just did some cut-paste on your code. I didnt really have my JC2 opened and start teh editing.
The forum doesn't support TAB encap.
That is why I have jasscraft on my flash drive.

Please fix that, I promised nothing but top-quality code in here,
so when others post code I expect them to be on the level.
11-07-2007, 01:50 PM#9
kentchow75
>>No they are not treated the same.
public keyword in jass does not work the same like in java or C#

Sorry, I don't know any other programing language other than Jass. I know, I am an idiot. But what's the different? You still need to call it by <ScopeName>_<FunctionName>()

>>library names do not have to have the same name as triggers they are in,
scopes do.

True? Well, what if my trigger has multiple scope? I need to name them exactly as same as my trigger's name??
I don't mean nested scope. It is multiple scope.
A small test to prove. An item I made for someone else.

>>That is why I have jasscraft on my flash drive.
Please fix that, I promised nothing but top-quality code in here,
so when others post code I expect them to be on the level.

Please understand me, I am running on a 256 ram computer. It lags well (2-5 minutes) once I open my JassCraft. Lags more when I open my NewGen.
I know, I know. You would advise me to get some better performance, get me the budget.
Could'nt you just edit my post. xD
I know, just lazy.

Edit - Sorry, the link was not working. Something happened on FileFront. -.-
[Delu]Shuriken.w3x
Attached Files
File type: w3x[Delu]Shuriken.w3x (91.5 KB)
11-07-2007, 02:43 PM#10
Ammorth
For inits, you could do library with an initializer function.

Collapse JASS:
library SomeShit initializer InitSomeShit

// code

function InitSomeShit takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerAddActions...
    call RegisterPlayerEvent...
    set t = null
endfunction

endlibrary
11-07-2007, 03:39 PM#11
Captain Griffen
Quote:
welcome to the world of clean, bug-free and easy to understand code.

You're a liar or a retard.

Please note that that statement is not an exclusive or.
11-07-2007, 04:06 PM#12
cohadar
Quote:
Originally Posted by kentchow75
>>No they are not treated the same.
public keyword in jass does not work the same like in java or C#

Sorry, I don't know any other programing language other than Jass. I know, I am an idiot. But what's the different? You still need to call it by <ScopeName>_<FunctionName>()
functions without public in front of them do not get scope_name prefix.


Quote:
Originally Posted by kentchow75
>>library names do not have to have the same name as triggers they are in,
scopes do.

True? Well, what if my trigger has multiple scope? I need to name them exactly as same as my trigger's name??
I don't mean nested scope. It is multiple scope.
A small test to prove. An item I made for someone else.
Having multiple scopes in one trigger is a bad practise.
In java it is forbidden to do it altogether.

The reason that scope name has to be the same as trigger name is that once
you convert your InitTrig to this form:
public function InitTrig takes nothing returns nothing it only works if names match.
And the reason for this is that vjass does this:
public InitTrig -> InitTrig_ScopeName
for trigger initializers
unlike for functions where it does:
public FuncName -> ScopeName_InitTrig

And if scope name does not match trigger name inittrig will not be called on map initialization and your trigger will not be initialized.

So basically you "can" have 2 scopes in one trigger,
but only one inittrig function will work, this is not a problem by itself,
but it usually means that your designed is wrong.

Ok so why would anyone convert triggers to InitTrig form when trigger works fine after direct conversion from GUI?

Because it:
1. looks lot nicer
2. it is independent of WE generated trigger name.

Independence is extremely important here,
if you for example wanted to copy someones triggered spell into your map,
but want to change parameters and also the name of spell,
you would have to find ALL gg_trg uglies, and replace them with new ones,
and then find different Trig_name uglies for actions and conditions,
not to mention those uglies generated by GUI if statements.

On the other hand if you used the clean vJass form,
all you have to do after copying the trigger is to change scope name, nothing else :)
(It is the easiest way to make JESP spells)

@griffein:
In my case it is a correct AND statement there.
It is not my fault that you cannot write a code like that. :P

If I had written clean and optimized on the same line that would be a problem,
I don't suppose apologizing is your stile?
11-08-2007, 03:46 AM#13
kentchow75
I, might need an explanation of what interface is...
Don't tell me to read the manual, if I understand the manual well, I could be your teacher, Cohadar.
11-08-2007, 08:13 AM#14
Silvenon
Quote:
Collapse JASS:
//Forced by WE
function InitTrig_CSSafety takes nothing returns nothing
endfunction

Not anymore.......

So we should use local triggers just so it looks prettier? I still don't understand why.

Btw, nice job man, I'll enjoy this, +rep

One question for ~Gals~: I remember your thread on handle vars (on TheHelper), you know vJass, but you don't know how to use handle vars? Some guy you are..... :)
11-08-2007, 12:03 PM#15
cohadar
Quote:
Originally Posted by Silvenon
So we should use local triggers just so it looks prettier?

Well if you prefer code like this:
Collapse JASS:
function Trig_Untitled_Trigger_001_Actions takes nothing returns nothing
    if ( Trig_Untitled_Trigger_001_Func001C() ) then
        if ( Trig_Untitled_Trigger_001_Func001Func001C() ) then
            call ForGroupBJ( GetUnitsInRectAll(GetPlayableMapRect()), function Trig_Untitled_Trigger_001_Func001Func001Func003A )
        else
            call ForGroupBJ( GetUnitsInRectAll(GetPlayableMapRect()), function Trig_Untitled_Trigger_001_Func001Func001Func002A )
        endif
    else
    endif
endfunction

@~Gals~
struct interfaces are one of those features of vjass that we could do without,
my estimate is that there is no more than 20 people who use them.
I had to use them only once for example and for an extremely non-common problem.
I honestly think they are of no use for 95% of mappers.
(This does not mean that you should not try and learn them, after all they might be just what you needed for your xyz thing..)

function interfaces on the other hand are extremely useful,
they are a bit hard to understand at beginning but once you learn them,
you really gain some programming power you can use right away.

My next tutorial will be about function interfaces,
what is their purpose, how to use them and how they work "under the hood"

It will take me some time, so don't hold your breath.