Learning JASS (converting old GUI spell)

DrinkSlurm

Eat Bachelor Chow!
Reaction score
50
Beginner JASSer here.
I'm finally going to make a serious effort to learn JASS and I have a few basic questions:

(I read through most or all of the beginners' JASS tutorials on this site months ago, and I have just reread and/or will reread them each of them again.)

1. I was thinking that a good way for me to practice and learn as I go would be to take one of my old approved GUI spells and convert it into JASS and try to clean it up (get rid of BJs). Is this a good method to learn, overall?

2. I keep seeing these JASS terms mentioned: vJASS, handles, structs, libraries, scopes, etc. I figured I'd learn what each of those things are and how to use them one by one at a later time. Should I be trying to learn any of these things at the same time as learning the fundamentals of JASS?

3. I only have JassCraft at this time. Should I be using anything else at this time? NewGen sounds a bit too advanced for me right now; seems like something I should learn afterwards.

Anyhow, this is what I have so far:

from my first spellpack
I'm just working with the spell called Flame Fury

Code:
Flame Fury Cast
    Events
        Unit - A unit Starts the effect of an ability
    Conditions
        (Ability being cast) Equal to Flame Fury (Channel)
    Actions
        Set tempPoint1 = (Position of (Triggering unit))
        Unit - Create 1 Dummy Caster for (Owner of (Triggering unit)) at tempPoint1 facing Default building facing degrees
        Custom script:   call RemoveLocation (udg_tempPoint1)
        Unit - Add a 2.00 second Generic expiration timer to (Last created unit)
        Unit - Add Slow (for Flame Fury) to (Last created unit)
        Unit - Set level of Slow (for Flame Fury) for (Last created unit) to (Level of Flame Fury (Channel) for (Triggering unit))
        Unit - Order (Last created unit) to Human Sorceress - Slow (Triggering unit)
        Wait 0.10 game-time seconds
        Trigger - Turn on Flame Fury Periodic <gen>

Code:
Flame Fury Periodic
    Events
        Time - Every 0.10 seconds of game time
    Conditions
    Actions
        Set tempGroup = (Units in (Playable map area) matching (((Matching unit) has buff Flame Fury (Caster)) Equal to True))
        Unit Group - Pick every unit in tempGroup and do (Actions)
            Loop - Actions
                Set tempReal1 = (30.00 + (15.00 x (Real((Level of Flame Fury (Channel) for (Picked unit))))))
                Set tempPoint1 = (Position of (Picked unit))
                Set tempPoint2 = (tempPoint1 offset by 250.00 towards (Facing of (Picked unit)) degrees)
                Unit - Create 1 Dummy Caster for (Owner of (Picked unit)) at tempPoint1 facing (Facing of (Picked unit)) degrees
                Animation - Change (Last created unit)'s size to (tempReal1%, tempReal1%, tempReal1%) of its original size
                Unit - Add a 1.00 second Generic expiration timer to (Last created unit)
                Unit - Set level of Flame Fury (Breath of Fire) for (Last created unit) to (Level of Flame Fury (Channel) for (Picked unit))
                Unit - Order (Last created unit) to Neutral Pandaren Brewmaster - Breath Of Fire tempPoint2
                Custom script:   call RemoveLocation (udg_tempPoint1)
                Custom script:   call RemoveLocation (udg_tempPoint2)
        If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            If - Conditions
                (Number of units in tempGroup) Equal to 0
            Then - Actions
                Trigger - Turn off (This trigger)
            Else - Actions
        Custom script:   call DestroyGroup (udg_tempGroup)

After cleaning up all "BJs" and "Swappeds" (that I knew how to) this is what I have:

JASS:
function Trig_Flame_Fury_Cast_Conditions takes nothing returns boolean
    if GetSpellAbilityId() == &#039;A003&#039; then
        return true
    endif
    return false
endfunction

function Trig_Flame_Fury_Cast_Actions takes nothing returns nothing
    set udg_tempPoint1 = GetUnitLoc(GetTriggerUnit())
    call CreateNUnitsAtLoc( 1, &#039;h000&#039;, GetOwningPlayer(GetTriggerUnit()), udg_tempPoint1, 270 )
    call RemoveLocation (udg_tempPoint1)
    call UnitApplyTimedLife( GetLastCreatedUnit(), &#039;BTLF&#039;, 2.00 )
    call UnitAddAbility( GetLastCreatedUnit(), &#039;A009&#039; )
    call SetUnitAbilityLevel( GetLastCreatedUnit(), &#039;A009&#039;, GetUnitAbilityLevel(GetTriggerUnit(), &#039;A003&#039; ) )
    call IssueTargetOrder( GetLastCreatedUnit(), &quot;slow&quot;, GetTriggerUnit() )
    call PolledWait( 0.10 )
    call EnableTrigger( gg_trg_Flame_Fury_Periodic )
endfunction

//===========================================================================
function InitTrig_Flame_Fury_Cast takes nothing returns nothing
    set gg_trg_Flame_Fury_Cast = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Flame_Fury_Cast, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Flame_Fury_Cast, Condition( function Trig_Flame_Fury_Cast_Conditions ) )
    call TriggerAddAction( gg_trg_Flame_Fury_Cast, function Trig_Flame_Fury_Cast_Actions )
endfunction


JASS:
function Trig_Flame_Fury_Periodic_Func001002002 takes nothing returns boolean
    return ( UnitHasBuffBJ(GetFilterUnit(), &#039;B003&#039;) == true )
endfunction

function Trig_Flame_Fury_Periodic_Func002A takes nothing returns nothing
    set udg_tempPoint1 = GetUnitLoc(GetEnumUnit())
    set udg_tempPoint2 = PolarProjectionBJ(udg_tempPoint1, 250.00, GetUnitFacing(GetEnumUnit()))
    set udg_tempReal1 = ( 30.00 + ( 15.00 * I2R(GetUnitAbilityLevel(GetEnumUnit(), &#039;A003&#039; )) ) )
    call CreateNUnitsAtLoc( 1, &#039;h000&#039;, GetOwningPlayer(GetEnumUnit()), udg_tempPoint1, GetUnitFacing(GetEnumUnit()) )
    call SetUnitScalePercent( GetLastCreatedUnit(), udg_tempReal1, udg_tempReal1, udg_tempReal1 )
    call UnitApplyTimedLife( GetLastCreatedUnit(), &#039;BTLF&#039;, 1.00 )
    call SetUnitAbilityLevel( GetLastCreatedUnit(), &#039;A001&#039;, GetUnitAbilityLevel( GetEnumUnit(), &#039;A003&#039;) )
    call IssuePointOrderLoc( GetLastCreatedUnit(), &quot;breathoffire&quot;, udg_tempPoint2 )
    call RemoveLocation (udg_tempPoint1)
    call RemoveLocation (udg_tempPoint2)
endfunction

function Trig_Flame_Fury_Periodic_Actions takes nothing returns nothing
    set udg_tempGroup = GetUnitsInRectMatching(GetPlayableMapRect(), Condition(function Trig_Flame_Fury_Periodic_Func001002002))
    call ForGroupBJ( udg_tempGroup, function Trig_Flame_Fury_Periodic_Func002A )
    if CountUnitsInGroup(udg_tempGroup) == 0 then
        call DisableTrigger( GetTriggeringTrigger() )
    endif
    call DestroyGroup (udg_tempGroup)
endfunction

//===========================================================================
function InitTrig_Flame_Fury_Periodic takes nothing returns nothing
    set gg_trg_Flame_Fury_Periodic = CreateTrigger(  )
    call DisableTrigger( gg_trg_Flame_Fury_Periodic )
    call TriggerRegisterTimerEventPeriodic( gg_trg_Flame_Fury_Periodic, 0.10 )
    call TriggerAddAction( gg_trg_Flame_Fury_Periodic, function Trig_Flame_Fury_Periodic_Actions )
endfunction


I'm sure there is a lot more that can be done to simplify this, but I don't know how. There must be a way to combine the two triggers into one, right?

Also, when using JassCraft, I get an error for every global variable, even though it works just fine in WE and in game. Am I missing something? How do I declare global variables?

Thanks for any help.
 

SFilip

Gone but not forgotten
Reaction score
634
> Is this a good method to learn, overall?
Yes, but it's highly recommended that you also consult with someone more knowledgeable who can tell you what can be improved further in your code. Simply posting it in this forum is fine as well :)

> Should I be trying to learn any of these things at the same time as learning the fundamentals of JASS?
Some of them, yes.
You should know what handles are and that vJass is an improved Jass syntax possible thanks to NewGen. You can learn the rest as soon as you gather up some experience with Jass, most of vJass is quite straightforward.

> NewGen sounds a bit too advanced for me right now
Having NewGen is a good idea regardless of your knowledge - it allows syntax highlighting in the trigger editor, much better syntax checks when saving the map, a very good method of checking for memory leaks and much more.

> Am I missing something?
Nope, just ignore those errors, JassCraft can't guess what globals you have defined.

Anyway, after a quick look at the code:
JASS:
function Trig_Flame_Fury_Cast_Conditions takes nothing returns boolean
    if GetSpellAbilityId() == &#039;A003&#039; then
        return true
    endif
    return false
endfunction

can be
JASS:
function Trig_Flame_Fury_Cast_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == &#039;A003&#039;
endfunction


CreateNUnitsAtLoc is a BJ. You also should forget about Last Created <whatever> as they are deprecated in Jass, Jass natives return what they created.
 

DrinkSlurm

Eat Bachelor Chow!
Reaction score
50
Thanks for that. I downloaded NewGen and I'll dive into it a bit later. And I'll look into handles later too.

OK, I simplified the Condition like you said.

I want to make sure I simplified the "CreateNUnitsAtLoc" BJ correctly:

JASS:
    call CreateNUnitsAtLoc( 1, &#039;h000&#039;, GetOwningPlayer(GetTriggerUnit()), udg_tempPoint1, 270 )

...becomes...
JASS:
    call CreateUnitAtLocSaveLast(GetOwningPlayer(GetTriggerUnit()), &#039;h000&#039;, udg_tempPoint1, 270)

and
JASS:
    call CreateNUnitsAtLoc( 1, &#039;h000&#039;, GetOwningPlayer(GetEnumUnit()), udg_tempPoint1, GetUnitFacing(GetEnumUnit()) )

...becomes...
JASS:
    call CreateUnitAtLocSaveLast(GetOwningPlayer(GetEnumUnit()), &#039;h000&#039;, udg_tempPoint1, GetUnitFacing(GetEnumUnit()))

Is that right? (When I tried it with "CreateUnitAtLoc" instead of "CreateUnitAtLocSaveLast", the spell stopped working. I suspect it's because "CreateUnitAtLoc" doesn't allow me to refer to the last created unit for some reason.)
You also should forget about Last Created <whatever> as they are deprecated in Jass, Jass natives return what they created.

Sorry, I don't understand what you mean there. How do I do things to the last created unit, then? Can you give me an example?

Edit: One more question:
 

Arkan

Nobody rides for free
Reaction score
92
If you want to simplify and optimise the code, use coordinates and the native function: call CreateUnit()

so...

local unit u = CreateUnit(Player(0),'hfoo',x,y,0)

will create and store a unit into variable u.

About JassCraft, I don't think you can resize that window.
 

SFilip

Gone but not forgotten
Reaction score
634
> I suspect it's because "CreateUnitAtLoc" doesn't allow me to refer to the last created unit for some reason.
Exactly.
Functions and natives have the ability to return values.

Consider the following
Code:
function foo takes integer i [B]returns integer[/B]
    return i + 5
endfunction

function bar takes nothing returns nothing
    local integer test = [B]foo(10)[/B]
    call BJDebugMsg(I2S(test))
endfunction
Calling bar will display the message 15. Notice I'm assigning test to foo(10), this actually sets it to the value foo returns after running with the parameter 10.

Likewise:
JASS:
function test takes nothing returns nothing
    local unit u
    call CreateUnitAtLocSaveLast(Player(0), &#039;h000&#039;, udg_myloc, 0)
    set u = GetLastCreatedUnit()
endfunction

is the same as
JASS:
function test takes nothing returns nothing
    local unit u = CreateUnitAtLoc(Player(0), &#039;h000&#039;, udg_myloc, 0)
endfunction

but the second is more optimized and easier to use (once you get used to it, that is).

Another thing you might want to use are coordinates as opposed to locations (Points).
For instance:
JASS:
function test takes nothing returns nothing
    local location l = GetUnitLoc(someunit)
    call CreateUnitAtLoc(Player(0), &#039;h000&#039;, l, 0)
    call RemoveLocation(l)
    set l = null
endfunction

To prevent the memory leak there you have to remove l and set it to null (all local variables except for integers, reals, strings, booleans and code need to be set to null).
JASS:
function test takes nothing returns nothing
    local real lx = GetUnitX(someunit)
    local real ly = GetUnitY(someunit)
    call CreateUnit(Player(0), &#039;h000&#039;, lx, ly, 0)
endfunction

This one does the same thing, but uses coordinates (X and Y) which do not need to be removed or set to null - they simply don't leak. All Jass natives that use locations have a variation that uses X and Y instead (CreateUnitAtLoc - CreateUnit).
 

DrinkSlurm

Eat Bachelor Chow!
Reaction score
50
Awesome! Thanks to you both. Little by little, I think I'm getting the hang of it.

This is what I have now:
JASS:
function Trig_Flame_Fury_Cast_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == &#039;A003&#039;
endfunction

function Trig_Flame_Fury_Cast_Actions takes nothing returns nothing
    local real x = GetUnitX( GetTriggerUnit() )
    local real y = GetUnitY( GetTriggerUnit() )
    local unit u = CreateUnit (GetOwningPlayer(GetTriggerUnit()), &#039;h000&#039;, x, y, 0)
    call UnitApplyTimedLife( u, &#039;BTLF&#039;, 2.00 )
    call UnitAddAbility( u, &#039;A009&#039; )
    call SetUnitAbilityLevel( u, &#039;A009&#039;, GetUnitAbilityLevel(GetTriggerUnit(), &#039;A003&#039; ) )
    call IssueTargetOrder( u, &quot;slow&quot;, GetTriggerUnit() )
    set u = null
    call PolledWait( 0.10 )
    call EnableTrigger( gg_trg_Flame_Fury_Periodic )
endfunction

//===========================================================================
function InitTrig_Flame_Fury_Cast takes nothing returns nothing
    set gg_trg_Flame_Fury_Cast = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Flame_Fury_Cast, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Flame_Fury_Cast, Condition( function Trig_Flame_Fury_Cast_Conditions ) )
    call TriggerAddAction( gg_trg_Flame_Fury_Cast, function Trig_Flame_Fury_Cast_Actions )
endfunction


JASS:
function Trig_Flame_Fury_Periodic_Func001002002 takes nothing returns boolean
    return ( UnitHasBuffBJ(GetFilterUnit(), &#039;B003&#039;) == true )
endfunction

function Trig_Flame_Fury_Periodic_Func002A takes nothing returns nothing
    local real angle = GetUnitFacing(GetEnumUnit()) * 3.14159/180.00
    local real x1 = GetUnitX(GetEnumUnit())
    local real y1 = GetUnitY(GetEnumUnit())
    local real x2 = x1 + 250.00 * Cos(angle)
    local real y2 = y1 + 250.00 * Sin(angle)
    local real size = 30.00 + 15.00 * GetUnitAbilityLevel(GetEnumUnit(), &#039;A003&#039;)
    local unit u = CreateUnit(GetOwningPlayer(GetEnumUnit()), &#039;h000&#039;, x1, y1, angle)
    call SetUnitScalePercent( u, size, size, size )
    call UnitApplyTimedLife( u, &#039;BTLF&#039;, 1.00 )
    call SetUnitAbilityLevel( u, &#039;A001&#039;, GetUnitAbilityLevel( GetEnumUnit(), &#039;A003&#039;) )
    call IssuePointOrder( u, &quot;breathoffire&quot;, x2, y2 )
    set u = null
endfunction

function Trig_Flame_Fury_Periodic_Actions takes nothing returns nothing
    local group g = GetUnitsInRectMatching(GetPlayableMapRect(), Condition(function Trig_Flame_Fury_Periodic_Func001002002))
    call ForGroupBJ( g, function Trig_Flame_Fury_Periodic_Func002A )
    if CountUnitsInGroup(g) == 0 then
        call DisableTrigger( GetTriggeringTrigger() )
    endif
    set g = null
endfunction

//===========================================================================
function InitTrig_Flame_Fury_Periodic takes nothing returns nothing
    set gg_trg_Flame_Fury_Periodic = CreateTrigger(  )
    call DisableTrigger( gg_trg_Flame_Fury_Periodic )
    call TriggerRegisterTimerEventPeriodic( gg_trg_Flame_Fury_Periodic, 0.10 )
    call TriggerAddAction( gg_trg_Flame_Fury_Periodic, function Trig_Flame_Fury_Periodic_Actions )
endfunction


One more thing I want to learn is how to set it up so that you only need to set the rawcodes of the abilities, dummy casters, and buffs in one spot. I took a look at some other approved JASS spells and they have "constant function" and "private constant function." I guess those are part of some systems that I haven't learned yet. Do I need to learn a system before I can accomplish this?

Many thanks.
 

Arkan

Nobody rides for free
Reaction score
92
JASS:
return GetSpellAbilityId()==&#039;A000&#039;


Now in the actions, instead of using GetUnitAbilityLevel(u,'A000') you can use GetUnitAbilityLevel(u,GetSpellAbilityId()), so you only need to type 'A000' once.

Or you can put it in the globals

JASS:
globals
    constant integer AB_ID = &#039;A000&#039;
endglobals


then just use AB_ID whenever you need it.
 

DrinkSlurm

Eat Bachelor Chow!
Reaction score
50
OK, I hope I understood you correctly, because everything else I tried gave me errors. Basically, I used the old-fashion Variable Editor to define a bunch of global variables for all my abilities, dummies, and buffs. Then I used udg_[globvarname] in all the functions.

That way, when importing this spell to another map, all the user needs to do is make sure the global variables are pointing to the right objects by using the Variable Editor.

Thanks.
 

DrinkSlurm

Eat Bachelor Chow!
Reaction score
50
If anyone is so inclined, could you take a quick look at my final JASS trigger? I managed to merge it all into one trigger (including implementation instructions), and I'm using the NewGen features to specify globals for the rawcodes within this trigger.

JASS:
//===========================================================================
//============================= Flame Fury ==================================
//============================ Instructions =================================
//===========================================================================
// MUI; requires NewGen
//===========================================================================
// 1. Go to the Object Editor, and copy + paste the following:
//    Units
//       * Dummy Caster
//    Abilities
//       * Flame Fury: Add this ability to the Hero; make sure the Base Order ID
//         does not conflict with any other spell on the Hero.
//       * Slow (for Flame Fury): make sure that the buff is set to the custom
//         &quot;Flame Fury (Caster)&quot; buff. This is the buff that will show on the
//         Hero and that is used in this trigger.
//       * Breath of Fire (for Flame Fury)
//    Buffs
//       * Flame Fury (Caster)
// 2. Copy this trigger.
// 3. Other options:
//    Custom Gameplay Constants (this is what allows the Hero to move around and
//    aim the spell, albeit very slowly): 
//       * Movement - Unit Speed - Maximum: 522
//       * Movement - Unit Speed - Minimum: 1
//    Hero
//       * Movement - Turn Rate: 0.20 (this is part of the balance of the skill;
//         a slower turning rate makes it harder to aim)
// 4. To adjust the balance of the skill, you can change some of the following
//    values:
//    Abilities
//       * Flame Fury: Cooldown, Mana Cost
//       * Breath of Fire (for Flame Fury): Damages, Distances, Areas, etc.
//       * Slow (for Flame Fury): Attack Speed Factor, Movement Speed Factor
// 5. Make sure that the rawcodes specified here reference the to proper units,
//    abilities, or buffs.
globals
    constant integer FF_ab_Hero = &#039;A003&#039; //Ability ID of &quot;Flame Fury&quot;
    constant integer FF_un_Dmmy = &#039;h000&#039; //Unit ID of &quot;Dummy Caster&quot;
    constant integer FF_ab_Slow = &#039;A009&#039; //Ability ID of &quot;Slow (for Flame Fury)&quot;
    constant integer FF_ab_BoF = &#039;A001&#039; //Ability ID of &quot;Breath of Fire (for FF Dummy)&quot;
    constant integer FF_bf_FF = &#039;B003&#039; //Buff ID of &quot;Flame Fury (Caster)&quot;
// 6. Change the timer interval if desired but be careful of lag; default is 0.10 seconds
    constant real FF_int = 0.10
// 7. Change the attack distance if necessary; default is 250.00
    constant real FF_atkdist = 250.00
endglobals

//===========================================================================
function FF_Cond takes nothing returns boolean
    return GetSpellAbilityId() == FF_ab_Hero
endfunction

function FF_BuffCond takes nothing returns boolean
    return GetUnitAbilityLevel(GetFilterUnit(), FF_bf_FF) &gt; 0
endfunction

function FF_GroupActions takes nothing returns nothing
    local real face_rad = GetUnitFacing(GetEnumUnit()) * 0.01745
    local real X1 = GetUnitX(GetEnumUnit())
    local real Y1 = GetUnitY(GetEnumUnit())
    local real X2 = X1 + FF_atkdist * Cos(face_rad)
    local real Y2 = Y1 + FF_atkdist * Sin(face_rad)
    local real size = 0.30 + 0.15 * GetUnitAbilityLevel(GetEnumUnit(), FF_ab_Hero)
    local unit u = CreateUnit(GetOwningPlayer(GetEnumUnit()), FF_un_Dmmy, X1, Y1, face_rad)
    call SetUnitScale(u, size, size, size)
    call UnitApplyTimedLife(u, &#039;BTLF&#039;, 1.00)
    call UnitAddAbility(u, FF_ab_BoF)
    call SetUnitAbilityLevel(u, FF_ab_BoF, GetUnitAbilityLevel(GetEnumUnit(), FF_ab_Hero))
    call IssuePointOrder(u, &quot;breathoffire&quot;, X2, Y2)
    set u = null
endfunction

function FF_TimerActions takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local group g = CreateGroup()
    call GroupEnumUnitsInRect(g, bj_mapInitialPlayableArea, Condition(function FF_BuffCond))
    call DestroyBoolExpr(Condition(function FF_BuffCond))
    call ForGroup(g, function FF_GroupActions)
    if IsUnitGroupEmptyBJ(g) == true then
        call PauseTimer(t)
        call DestroyTimer(t)
    endif
    call DestroyGroup(g)
    set t = null
    set g = null
endfunction

function FF_Actions takes nothing returns nothing
    local unit hero = GetTriggerUnit()
    local real X = GetUnitX(hero)
    local real Y = GetUnitY(hero)
    local unit u = CreateUnit(GetOwningPlayer(hero), FF_un_Dmmy, X, Y, 0)
    local timer t = CreateTimer()
    call UnitApplyTimedLife(u, &#039;BTLF&#039;, 1.00)
    call UnitAddAbility(u, FF_ab_Slow)
    call SetUnitAbilityLevel(u, FF_ab_Slow, GetUnitAbilityLevel(hero, FF_ab_Hero))
    call IssueTargetOrder(u, &quot;slow&quot;, hero)
    call TriggerSleepAction(0.00)
    call TimerStart(t, FF_int, true, function FF_TimerActions)
    set hero = null
    set u = null
    set t = null
endfunction

//===========================================================================
function InitTrig_Flame_Fury takes nothing returns nothing
    set gg_trg_Flame_Fury = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(gg_trg_Flame_Fury, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(gg_trg_Flame_Fury, Condition(function FF_Cond))
    call TriggerAddAction(gg_trg_Flame_Fury, function FF_Actions)
endfunction


For a local group variable, are you supposed to destroy the group and null the variable too?

Also, apparently, you can't specify global variables with arrays in this manner:
JASS:
globals
constant integer unit_type_var[1] = whatever
constant integer unit_type_var[2] = whatever
constant integer unit_type_var[3] = whatever
endglobals

...so I had to use actual GUI-ish udg_ global variables. Any way around this?
 

Arkan

Nobody rides for free
Reaction score
92
Looks good overall, a few things:

1. consider using a recycling timer system such as CSSafety, then you don't need to destroy or null timers, which can have consequences.

2. use a global boolexpr instead of creating a new one every time, also destroying boolexprs are bad or so I've heard.

3. IsUnitGroupEmptyBJ(g) == true can simply be IsUnitGroupEmptyBJ(g). The only time you need to put ==true is when using IsUnitType(). I think "if CountUnitsInGroup(g)==0 then" is faster though.

About global arrays, you need to set the variables in initialization. I suggest you make a function on map init. where you set all variables.

And yes, you null the group after you destroy it.

Oh right, if you put the trigger in a scope you can make the globals private, that way you don't need the FF_ prefixes.
 

DrinkSlurm

Eat Bachelor Chow!
Reaction score
50
Thanks again.

2. use a global boolexpr instead of creating a new one every time
Can you show me how to do that? Everything I've tried gives me an error along the lines of "cannot convert boolexpr to boolean" or something.

Oh right, if you put the trigger in a scope you can make the globals private, that way you don't need the FF_ prefixes.
I didn't understand what scopes were for, but that clears up a lot!

1. consider using a recycling timer system such as CSSafety, then you don't need to destroy or null timers, which can have consequences.
Got a link or tutorial? My search on this forum didn't turn up anything.
 

Arkan

Nobody rides for free
Reaction score
92
JASS:
scope yourspell
globals
    boolexpr SafeB
endglobals

private function rSafe takes nothing returns nothing
    GetUnitAbilityLevel(GetFilterUnit(), FF_bf_FF) &gt; 0
endfunction

function InitTrig_Flame_Fury takes nothing returns nothing
    set gg_trg_Flame_Fury = CreateTrigger()
    set SafeB = Condition(function rSafe)
    call TriggerRegisterAnyUnitEventBJ(gg_trg_Flame_Fury, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(gg_trg_Flame_Fury, Condition(function FF_Cond))
    call TriggerAddAction(gg_trg_Flame_Fury, function FF_Actions)
endfunction
endscope

Think that will work.

CSSafety (by Vexorian):

JASS:
    globals
        private timer array T
        private integer N = 0
    endglobals

    //==========================================================================================
    function NewTimer takes nothing returns timer
        if (N==0) then
            return CreateTimer()
        endif
     set N=N-1
     return T[N]
    endfunction

    //==========================================================================================
    function ReleaseTimer takes timer t returns nothing
        call PauseTimer(t)
        if (N==8191) then
            debug call BJDebugMsg(&quot;Warning: Timer stack is full, destroying timer!!&quot;)

            //stack is full, the map already has much more troubles than the chance of bug
            call DestroyTimer(t)
        else
            set T[N]=t
            set N=N+1
        endif    
    endfunction

(put it in a library or something).
So you use NewTimer() instead of CreateTimer(), and you use ReleaseTimer() instead of pause/destroy timer.
 

DrinkSlurm

Eat Bachelor Chow!
Reaction score
50
Excellent! Thanks a lot.

Why, oh, why did I not start learning JASS earlier...?

(need to spread rep)
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top