• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.
  • The Hive's 22nd Icon Contest: Creep Abilities is now concluded, time to vote for your favourite set of icons! Click here to vote!
  • ✅ The POLL for Hive's Texturing Contest #34 is OPEN! Vote for the TOP 3 SKINS! 🔗Click here to cast your vote!
  • ✅ The POLL for Hive's Techtree Contest #20 is OPEN! Vote for the TOP 3 FACTIONS! 🔗Click here to cast your vote!

[JASS] Very very basic question

Status
Not open for further replies.
Level 15
Joined
Nov 26, 2005
Messages
1,152
So, my first attemp Ever in Jass:

JASS:
function Trig_Net_Cast_Conditions takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == 'A013' ) ) then
        return false
    endif
    return true
endfunction

function Actions takes nothing returns nothing
    local unit NetCaster = GetSpellAbilityUnit()
    local unit NetTarget = GetSpellTargetUnit()
    local integer NetInt
    
    loop
        set NetInt = NetInt + 1
        call UnitDamageTargetBJ( NetCaster, NetTarget, ( GetHeroStatBJ(bj_HEROSTAT_AGI, NetCaster, true) * 1.00 ), ATTACK_TYPE_CHAOS, DAMAGE_TYPE_FORCE )
        call TriggerSleepAction(1.00)
        exitwhen NetInt>6
    endloop
    
    set NetCaster = null
    set NetTarget = null
endfunction

//===========================================================================
function InitTrig_Net_Cast takes nothing returns nothing
    set gg_trg_Net_Cast = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Net_Cast, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Net_Cast, Condition( function Trig_Net_Cast_Conditions ) )
    call TriggerAddAction( gg_trg_Net_Cast, function Actions )
endfunction

It does not work. Why? What is wrong.... I just have no idea.
 
The second one Codemonkey posted is the optimized version of yours (the first).

Basically, conditions are functions that return a boolean value. (Conditions are added via TriggerAddCondition() where you must input that function that returns the boolean value)

If the function ends up returning false, then the actions won't be executed. If the function ends up returning true, the actions will be executed.

So let's take a look at Blizzard's GUI converted JASS:
JASS:
function Trig_Net_Cast_Conditions takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == 'A013' ) ) then
        return false
    endif
    return true
endfunction

Ok, now the if ... then begins an "if/then/else" block. If blah blah condition, then perform blah blah actions. The block is ended by endif.

After the if, we must have something that will return a boolean value. In this case, we are checking if the ability being cast is equal to that ability which has 'A013' as its rawcode. However, notice that they added a not. The not is kind of like a negate tool, so it basically means:
If the ability's id is not equal to 'A013', then do these actions...

So if it isn't the id, then it will return false in the end, so the actions won't be executed. If it is equal to 'A013', then it will simply ignore the actions inside the if-block and it will return true, allowing the actions to be executed.

However, we can instead just use return <condition>. When we use that, it will return whether or not the condition is true. Since that is all we really need, it will return false when it is false and return true when it is true.

Thus, this:
JASS:
function Trig_Net_Cast_Conditions takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == 'A013' ) ) then
        return false
    endif
    return true
endfunction

Can be simplified to this:
JASS:
function Trig_Net_Cast_Conditions takes nothing returns boolean
    return GetSpellAbilityId()=='A013'
endfunction
 
I did not understand that.... The condition function was converted from GUI :?
What's the new one ?
It's just that the condition Cokemonkey11 provided is much more efficient than the GUI converted one.

This is irrelevant to your problem which has been solved, but you should use GetTriggerUnit() instead of GetSpellAbilityUnit() too. (I think it's a bit faster)
You could also inline most BJ's except for TriggerRegisterAnyUnitEventBJ.
Edit: Beaten by PurgeandFire111 who even provided a better explanation. XD
 
Last edited:
blizzard.j is what BJ stands for. They're the functions TESH represents in RED because blizzard.j is just a bunch of functions which call common.j (natives)

Some RED functions are okay to use, but generally you want to avoid them.

If you're curious how to get rid of them, open up the function list and read which natives the BJ calls.
 
What does this "bj" stand for anyway?

And why and how should it be avoided? (Some of these prese strings just have BJ in the end) :?

bj = blow job

ok, just kidding.

The purpose of many BJ functions is to reverse the order to make it readable for GUI (If my explaination was correct).

JNGP have a function list where you can view the list of fuction, you could use it to avoid bj.

bj are particularly an action that repeat the same action, just like an Do Nothing action which is an useless action that called another action that do particularly nothing.

Example of reverse the order are like this.

SetUnitAbilityLevelBJ (ability level, unit)

Without bj

SetUnitAbilityLevel(unit, ability level).
 
---unrelated---
sorry, but i am new to jass as well, does the shortening you guys showed here, only apply for condition functions?

Nope, it can apply to filters as well. =) (Or at least the whole "return bool" shortening method)

There are other ways to optimize other types of code though. However, inlining BJ's (or at least the unuseful/swapped ones) is good coding practice in general.
 
Ok, Septimus, Cokemonkey11, thank you very much for the explanations! :)

I've updated my spell trigger to this:

JASS:
function Trig_Net_Cast_Conditions takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == 'A013' ) ) then
        return false
    endif
    return true
endfunction

function Actions takes nothing returns nothing
    local unit NetCaster = GetSpellAbilityUnit()
    local unit NetTarget = GetSpellTargetUnit()
    local integer NetInt = 0
   
    if ( UnitHasBuffBJ(NetTarget, 'B008') == false ) then
        loop
            set NetInt = NetInt + 1
            call AddSpecialEffectTarget("Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl", NetTarget, "chest")
            call DestroyEffect( GetLastCreatedEffectBJ() )
            call UnitDamageTarget(NetCaster, NetTarget, ( GetHeroAgi(NetCaster, true) * 0.70), true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_FORCE, WEAPON_TYPE_AXE_MEDIUM_CHOP )
            call TriggerSleepAction(1.00)
            exitwhen NetInt>6
        endloop
    else
        call DisplayTextToForce( GetForceOfPlayer(GetOwningPlayer(NetCaster)), "Target already hit by a net gun!" )
    endif
    
    set NetCaster = null
    set NetTarget = null
endfunction

//===========================================================================
function InitTrig_Net_Cast takes nothing returns nothing
    set gg_trg_Net_Cast = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Net_Cast, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Net_Cast, Condition( function Trig_Net_Cast_Conditions ) )
    call TriggerAddAction( gg_trg_Net_Cast, function Actions )
endfunction


This does not work.... All I want is a if/then/else function to check whether the target hass buff (B008) and if not - run the loop, if yes - show a message.
What did I do wrong? (And how can I improve coding, too?)

P.S.
Btw, +Rep to both of u, too :)
 
You must use UnitHasAbility (or something like that) to check unit having buff.

EDIT: I optimised it a bit but i couldn't find why it doesn't work
JASS:
function Trig_Net_Cast_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A013'
endfunction

function Actions takes nothing returns nothing
    local unit NetCaster = GetTriggerUnit()
    local unit NetTarget = GetSpellTargetUnit()
    local integer NetInt = 0
   
    if ( GetUnitAbilityLevel(NetTarget, 'B008') <= 0) then
        loop
            set NetInt = NetInt + 1
            call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl", NetTarget, "chest"))
            call UnitDamageTarget(NetCaster, NetTarget, ( GetHeroAgi(NetCaster, true) * 0.70), true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_FORCE, WEAPON_TYPE_AXE_MEDIUM_CHOP )
            call TriggerSleepAction(1.00)
            exitwhen NetInt>6
        endloop
    else
        call DisplayTextToPlayer( GetOwningPlayer(NetCaster), 0, 0, "Target already hit by a net gun!" )
    endif
    
    set NetCaster = null
    set NetTarget = null
endfunction

//===========================================================================
function InitTrig_Net_Cast takes nothing returns nothing
    trigger t = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function Trig_Net_Cast_Conditions ) )
    call TriggerAddAction( t, function Actions )
endfunction
 
Last edited:
Here, it works for me:
JASS:
function Trig_Net_Cast_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'AHbn'
endfunction

function Actions takes nothing returns nothing
    local unit NetCaster = GetTriggerUnit()
    local unit NetTarget = GetSpellTargetUnit()
    local integer NetInt = 0
   
    if ( UnitHasBuffBJ(NetTarget, 'BHbn') == false ) then
        loop
            exitwhen NetInt>6
            call TriggerSleepAction(1.00)
            call AddSpecialEffectTarget("Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl", NetTarget, "chest")
            call DestroyEffect( GetLastCreatedEffectBJ())
            call UnitDamageTarget(NetCaster, NetTarget, (GetHeroAgi(NetCaster, true) * 0.70), false, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_FIRE, WEAPON_TYPE_WHOKNOWS)
            set NetInt = NetInt + 1
        endloop
    else
        call DisplayTextToForce( GetForceOfPlayer(GetOwningPlayer(NetCaster)), "Target is already hit by a net gun!" )
    endif
    
    set NetCaster = null
    set NetTarget = null
endfunction

//===========================================================================
function InitTrig_Net_Cast takes nothing returns nothing
    local trigger t = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function Trig_Net_Cast_Conditions ) )
    call TriggerAddAction( t, function Actions )
endfunction
I used ATTACK_TYPE_MAGIC, because I was testing it with Banish (which turns the target unit into ethereal).
So, don't forget to change the BuffId and AbilityId.
 
10x, I'll try doing the optimizations right away! REp for u.

Ok 2 things:

1. Your way of useing "call" inside "call DestroyEffect" is an error for my editor...
2. AbilityLevel for target unit? Are u sure.... I mean, the target won't have the ability "Net Gun", and the ID B008 is a buff, not an ability, does it work?

-----------------------------------------------------
What does not work is: Even if the target has the buff, the other net effects also apply.


Edit:
@ Pharaoh, yes, my buff and spell ID are checked multiple times.... :S
 
Oh.... Damn it, the ability ID !

In the Editor when I press Ctrl+D the buff Called "Net" appears as: B008:Bhng ( Net)

I tried with that Bhng (or something like that, I don't remember exactly) and it worked, while with B008 it didn't

Why, what's the difference? O.o
 
1. Your way of useing "call" inside "call DestroyEffect" is an error for my editor...
2. AbilityLevel for target unit? Are u sure.... I mean, the target won't have the ability "Net Gun", and the ID B008 is a buff, not an ability, does it work?

-----------------------------------------------------
What does not work is: Even if the target has the buff, the other net effects also apply.

Yes, just remove that call inside DestroyEffect :p, that was wrong.
This is what UnitHasBuffBJ does:
JASS:
function UnitHasBuffBJ takes unit whichUnit, integer buffcode returns boolean
    return (GetUnitAbilityLevel(whichUnit, buffcode) > 0)
endfunction
So using GetUnitAbilityLevel is the same except that's it a bit faster.

Also, are you sure your dummy ability adds the buff to the target?
 
Yes Yes, everything is correct. The ability works now, though I must reorganize it.
It's based of ensnare now. I must base it on channel, and if the buff comparison returns "false" create a dummy that launches the net, otherwise when cast on the same unit, the net effect applies and the damage does not.

But everything I asked for now is clear. (Except what's the difference between the two ID's that show when I press Ctrl+D)

I'll try to improve my spell, and write here if there is a problem. Again. 10x :)
 
JASS:
function Trig_Net_Cast_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A015'
endfunction

function Actions takes nothing returns nothing
    local unit NetCaster = GetSpellAbilityUnit()
    local unit NetTarget = GetSpellTargetUnit()
    local location NetL = GetUnitLoc(NetCaster)
    local integer NetInt = 0
   
    if ( UnitHasBuffBJ(NetTarget, 'Beng') == false ) then
        call DisplayTextToForce( GetForceOfPlayer(GetOwningPlayer(NetCaster)), "Success!" )
        call CreateNUnitsAtLoc( 1, 'n001', GetOwningPlayer(NetCaster), NetL, 0.00 )
        call UnitAddAbility( GetLastCreatedUnit(), 'A013')
        call IssueTargetOrder( GetLastCreatedUnit(), "ensnare", NetTarget )
        call UnitApplyTimedLife( GetLastCreatedUnit(), 'BTLF', 1.00)
        loop
            set NetInt = NetInt + 1
            call AddSpecialEffectTarget("Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl", NetTarget, "chest")
            call DestroyEffect( GetLastCreatedEffectBJ() )
            call UnitDamageTarget(NetCaster, NetTarget, ( GetHeroAgi(NetCaster, true) * 0.70), true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_FORCE, WEAPON_TYPE_AXE_MEDIUM_CHOP )
            call TriggerSleepAction(1.00)
            exitwhen NetInt>6
        endloop
    else
        call IssueImmediateOrder(NetCaster, "stop")
        call DisplayTextToForce( GetForceOfPlayer(GetOwningPlayer(NetCaster)), "Target already hit by a net gun!" )
    endif
    
    set NetCaster = null
    set NetTarget = null
    set NetL = null
endfunction

//===========================================================================
function InitTrig_Net_Cast takes nothing returns nothing
    set gg_trg_Net_Cast = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Net_Cast, EVENT_PLAYER_UNIT_SPELL_CHANNEL )
    call TriggerAddCondition( gg_trg_Net_Cast, Condition( function Trig_Net_Cast_Conditions ) )
    call TriggerAddAction( gg_trg_Net_Cast, function Actions )
endfunction


Sooo, thank you all. Now everything works as it should.
I want one more thing:
If the unit dies while in net, that the loop for the unit stops. (so it doesn't show effect and damage a dead unit)
How can that be done?
 
Here it is, a bit more optimized with comments:
JASS:
function Trig_Net_Cast_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A015'
endfunction

function Actions takes nothing returns nothing
    local unit NetCaster = GetSpellAbilityUnit()
    local unit NetTarget = GetSpellTargetUnit()
    //local location NetL = GetUnitLoc(NetCaster) we can just inline it since we use it once
    //And we can use reals instead since they don't leak
    local integer NetInt = 0
    local unit tempDummy
    local real agi
   
    //if ( UnitHasBuffBJ(NetTarget, 'Beng') == false ) then
    if GetUnitAbilityLevel(NetTarget, 'Beng') <= 0 then //you can detect buffs via this method
                                                        //this is basically what the UnitHasBuffBJ does
                                                        //We put "<= 0" rather than ">0" since we want it to be == false
        set agi = GetHeroAgi(NetCaster,true)*0.7 //calculate it now rather than during the loop several times
        set tempDummy = CreateUnit(GetTriggerPlayer(),'n001',GetUnitX(NetCaster),GetUnitY(NetCaster),0)
        //We will use the native CreateUnit() instead of CreateNUnits(), which is a BJ
        //GetTriggerPlayer() returns the owner of the caster
        call UnitAddAbility(tempDummy, 'A013')
        call IssueTargetOrder(tempDummy, "ensnare", NetTarget)
        call UnitApplyTimedLife(tempDummy, 'BTLF', 1)
        loop
            set NetInt = NetInt + 1
            //call AddSpecialEffectTarget("Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl", NetTarget, "chest")
            //call DestroyEffect( GetLastCreatedEffectBJ() )
            //We can merge these two functions (btw, AddSpecialEffectTarget doesn't save the variable for the last created effect
            //thus, the effect isn't destroyed so it is leaking
            call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl", NetTarget, "chest"))
            call UnitDamageTarget(NetCaster, NetTarget, agi, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_FORCE, WEAPON_TYPE_AXE_MEDIUM_CHOP )
            call TriggerSleepAction(1)
            exitwhen NetInt==7 or GetWidgetLife(NetTarget) < 0.405
        endloop
    else
        call IssueImmediateOrder(NetCaster, "stop")
        call DisplayTextToPlayer(GetTriggerPlayer(),0,0,"Target already hit by a net gun!")
    endif
    
    set NetCaster = null
    set NetTarget = null
    set tempDummy = null
endfunction

//===========================================================================
function InitTrig_Net_Cast takes nothing returns nothing
    local trigger t = CreateTrigger()
    //set gg_trg_Net_Cast = CreateTrigger()
    //local triggers allow for renaming the trigger easily (however, sometimes they will auto-rename the global)
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function Trig_Net_Cast_Conditions))
    call TriggerAddAction(t, function Actions )
endfunction

Well, it isn't fully optimized (with a timer and all that) but does enough justice to the code. =)
 
Status
Not open for further replies.
Back
Top