• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Solved] Condition based on spell ID

Status
Not open for further replies.
Level 7
Joined
Feb 9, 2021
Messages
301
How can I catch the ID of the used spell?
I have a trigger that activates if any of the 4 spells had been casted. However, I want to have different effects, depending on the spell casted. Therefore, I need to catch the spell ID in my conditions for if/then. I tried to do it like this, but it didn't work:

JASS:
function Trig_Viper_Mirage_R_Conditions takes nothing returns boolean
    return  GetSpellAbilityId() == 'A00L' or GetSpellAbilityId() == 'A00O' or GetSpellAbilityId() == 'A00N' or GetSpellAbilityId() == 'A00P'
endfunction

function moveToTarget takes nothing returns nothing
    timer t = GetExpiredTimer()
    integer id = GetHandleId(t)
    unit caster = LoadUnitHandle(Hash, id, StringHash("caster"))
    unit dummy = LoadUnitHandle(Hash, id, StringHash("dummy"))
    unit target = LoadUnitHandle(Hash, id, StringHash("target"))
    real dummyX = GetUnitX(dummy)
    real dummyY = GetUnitY(dummy)
    real targetX = GetUnitX(target)
    real targetY = GetUnitY(target)
    real distance = GetDistanceBetweenCoordinates(dummyX, targetX, dummyY , targetY)
  
    real dummyExp = LoadReal(Hash, id, StringHash("dummyExp")) + 1
    call SaveReal(Hash, id, StringHash("dummyExp"), dummyExp)
  
    boolean isNeedDestroyFunction = false
    if dummyExp >= LoadReal(Hash, id, StringHash("MaxDummyLife"))/0.03125 then
        KillUnit(dummy)
        RemoveUnit(dummy)
      
        isNeedDestroyFunction = true
    elseif distance > 80 then
        real angle = GetAngleBetweenPoints(dummyX, targetX, dummyY, targetY)
        real xWithOffset = GetXWithOffset(dummyX, LoadReal(Hash, id, StringHash("speed")), angle)
        real yWithOffset = GetYWithOffset(dummyY, LoadReal(Hash, id, StringHash("speed")), angle)
        setUnitPosition(dummy, xWithOffset, yWithOffset)
    else
        KillUnit(dummy)
        UnitDamageTarget(caster, target, 50, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_PLANT, null)
        isNeedDestroyFunction = true
      
        if LoadInteger(Hash, id, StringHash("spellid")) == 'A00L' then
            BJDebugMsg("abilities")
        elseif LoadInteger(Hash, id, StringHash("spellid")) == 'A00N' then
            BJDebugMsg("move")
        elseif LoadInteger(Hash, id, StringHash("spellid")) == 'A00P' then
            BJDebugMsg("items")
          
        elseif LoadInteger(Hash, id, StringHash("spellid")) == 'A00O' then
            BJDebugMsg("attack")
          
        endif
      
      
    endif
    caster = null
    dummy = null
    target = null
    if isNeedDestroyFunction then
        PauseTimer(t)
        DestroyTimer(t)
        FlushChildHashtable(Hash, id)
    endif
    t = null
endfunction

function startMove takes unit caster, unit dummy, unit target returns nothing
    timer t = CreateTimer()
    integer id = GetHandleId(t)
  
    //Configurations
    real dummyExp = 0.0
    real MaxDummyLife = 4.0
    real speed = 10.0
    //
  
    integer spellid = LoadInteger(Hash, id, StringHash("spellid"))
  
    SaveInteger(Hash, id, StringHash("spellid"), spellid)
  
    SaveReal(Hash, id, StringHash("MaxDummyLife"), MaxDummyLife)
    SaveReal(Hash, id, StringHash("speed"), speed)
  
    SaveReal(Hash, id, StringHash("dummyExp"), dummyExp)
  
    SaveUnitHandle(Hash, id, StringHash("caster"), caster)
    SaveUnitHandle(Hash, id, StringHash("dummy"), dummy)
    SaveUnitHandle(Hash, id, StringHash("target"), target)

    TimerStart(t, 0.03125, true, function moveToTarget)
    caster = null
    dummy = null
    target = null
    t = null
endfunction

function prepareMassAttack takes nothing returns nothing
    timer t = GetExpiredTimer()
    integer id = GetHandleId(t)
    unit caster = LoadUnitHandle(Hash, id, StringHash("caster"))
    player p = GetOwningPlayer(caster)
    real x = GetUnitX(caster)
    real y = GetUnitY(caster)
    GroupEnumUnitsInRange(g, x, y, LoadReal(Hash, id, StringHash("aoe")), Base)
        loop
            E = FirstOfGroup(g)
            exitwhen E == null
             if Condition_Base(p, E) then
                real targetX = GetUnitX(E)
                real targetY = GetUnitY(E)
                real angle = GetAngleBetweenPoints(x, targetX, y, targetY)
                real xWithOffset = GetXWithOffset(x, LoadReal(Hash, id, StringHash("speed")), angle)
                real yWithOffset = GetYWithOffset(y, LoadReal(Hash, id, StringHash("speed")), angle)
                bj_lastCreatedUnit = CreateUnit(p, 'h004', xWithOffset,  yWithOffset, angle)
      
                call SetUnitPathing(bj_lastCreatedUnit, false)
                startMove(caster, bj_lastCreatedUnit, E)
            endif
            GroupRemoveUnit(g, E)      
        endloop
    caster = null
    p = null
    PauseTimer(t)
    DestroyTimer(t)
    FlushChildHashtable(Hash, id)
    t = null
endfunction

function Trig_Viper_Mirage_R_Actions takes nothing returns nothing
    timer t = CreateTimer()
    integer id = GetHandleId(t)
    unit caster = GetTriggerUnit()
    integer spellid = GetSpellAbilityId()

     //Configurations
    real aoe = 1000
  
    //
    SaveReal(Hash, id, StringHash("aoe"), aoe)
  
    SaveInteger(Hash, id, StringHash("spellid"), spellid)
    SaveUnitHandle(Hash, id, StringHash("caster"), caster)
  
  

    TimerStart(t, 0.1, false, function prepareMassAttack)
  

  
    caster = null
    t = null
endfunction

//===========================================================================
function InitTrig_Viper_Mirage_R takes nothing returns nothing
    local trigger t = CreateTrigger()
    local integer index = 0
    loop
        call TriggerRegisterPlayerUnitEvent(t, Player(index),  EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
        set index = index + 1
        exitwhen index == bj_MAX_PLAYER_SLOTS
    endloop
    call TriggerAddCondition( t, Condition( function Trig_Viper_Mirage_R_Conditions ) )
    call TriggerAddAction( t, function Trig_Viper_Mirage_R_Actions )
    set t = null
endfunction
 
Level 7
Joined
Feb 9, 2021
Messages
301
What doesn't work?
The conditions?
The actions?
The whole trigger?
Not enough info.
This part doesn't work:

JASS:
   if LoadInteger(Hash, id, StringHash("spellid")) == 'A00L' then
            BJDebugMsg("abilities")
   elseif LoadInteger(Hash, id, StringHash("spellid")) == 'A00N' then
            BJDebugMsg("move")
   elseif LoadInteger(Hash, id, StringHash("spellid")) == 'A00P' then
            BJDebugMsg("items")
            
   elseif LoadInteger(Hash, id, StringHash("spellid")) == 'A00O' then
            BJDebugMsg("attack")
      endif
 

Wrda

Spell Reviewer
Level 26
Joined
Nov 18, 2012
Messages
1,887
Rule number 1: Never null a function's parameters like you did in startMove function, also never change their values.
I still don't know what stops the trigger, perhaps StringHash isn't working, I don't know. Make more debug messages, see what it returns. I wouldn't even use it in first place.
 
Level 7
Joined
Feb 9, 2021
Messages
301
Rule number 1: Never null a function's parameters like you did in startMove function, also never change their values.
I still don't know what stops the trigger, perhaps StringHash isn't working, I don't know. Make more debug messages, see what it returns. I wouldn't even use it in first place.
Can you please elaborate on rule number 1? How do I avoid leaks then? What's wrong with nulling them there? I shouldn't null any of the parameters there? Even the timer?

SpellID get lost after prepareMassAttack function. I think the problem is that I do not transfer the id to the next function. However, I do not know how to go around it.
 

Wrda

Spell Reviewer
Level 26
Joined
Nov 18, 2012
Messages
1,887
These never leak, arguments don't leak. using local variables is another story, of course the timer t leaks, even if you local unit u = somerandomname it will leak unless you null.
It's easier than you think:
JASS:
    SaveInteger(Hash, id, StringHash("spellid"), spellid)

    SaveReal(Hash, id, StringHash("MaxDummyLife"), MaxDummyLife)
    SaveReal(Hash, id, StringHash("speed"), speed)

    SaveReal(Hash, id, StringHash("dummyExp"), dummyExp)

    SaveUnitHandle(Hash, id, StringHash("caster"), caster)
    SaveUnitHandle(Hash, id, StringHash("dummy"), dummy)
    SaveUnitHandle(Hash, id, StringHash("target"), target)
Replace StringHash with integers.
JASS:
    SaveInteger(Hash, id, 0, spellid)

    SaveReal(Hash, id, 1, MaxDummyLife)
    SaveReal(Hash, id, 2, speed)

    SaveReal(Hash, id, 3, dummyExp)

    SaveUnitHandle(Hash, id, 4, caster)
    SaveUnitHandle(Hash, id, 5, dummy)
    SaveUnitHandle(Hash, id, 6, target)
Edit: eh just posted after you posted :p
 
Level 7
Joined
Feb 9, 2021
Messages
301
These never leak, arguments don't leak. using local variables is another story, of course the timer t leaks, even if you local unit u = somerandomname it will leak unless you null.
It's easier than you think:
JASS:
    SaveInteger(Hash, id, StringHash("spellid"), spellid)

    SaveReal(Hash, id, StringHash("MaxDummyLife"), MaxDummyLife)
    SaveReal(Hash, id, StringHash("speed"), speed)

    SaveReal(Hash, id, StringHash("dummyExp"), dummyExp)

    SaveUnitHandle(Hash, id, StringHash("caster"), caster)
    SaveUnitHandle(Hash, id, StringHash("dummy"), dummy)
    SaveUnitHandle(Hash, id, StringHash("target"), target)
Replace StringHash with integers.
JASS:
    SaveInteger(Hash, id, 0, spellid)

    SaveReal(Hash, id, 1, MaxDummyLife)
    SaveReal(Hash, id, 2, speed)

    SaveReal(Hash, id, 3, dummyExp)

    SaveUnitHandle(Hash, id, 4, caster)
    SaveUnitHandle(Hash, id, 5, dummy)
    SaveUnitHandle(Hash, id, 6, target)
Edit: eh just posted after you posted :p
Stringhash had nothing to do with it. Thank you for your help.
 
Level 7
Joined
Feb 9, 2021
Messages
301
These never leak, arguments don't leak. using local variables is another story, of course the timer t leaks, even if you local unit u = somerandomname it will leak unless you null.
It's easier than you think:
JASS:
    SaveInteger(Hash, id, StringHash("spellid"), spellid)

    SaveReal(Hash, id, StringHash("MaxDummyLife"), MaxDummyLife)
    SaveReal(Hash, id, StringHash("speed"), speed)

    SaveReal(Hash, id, StringHash("dummyExp"), dummyExp)

    SaveUnitHandle(Hash, id, StringHash("caster"), caster)
    SaveUnitHandle(Hash, id, StringHash("dummy"), dummy)
    SaveUnitHandle(Hash, id, StringHash("target"), target)
Replace StringHash with integers.
JASS:
    SaveInteger(Hash, id, 0, spellid)

    SaveReal(Hash, id, 1, MaxDummyLife)
    SaveReal(Hash, id, 2, speed)

    SaveReal(Hash, id, 3, dummyExp)

    SaveUnitHandle(Hash, id, 4, caster)
    SaveUnitHandle(Hash, id, 5, dummy)
    SaveUnitHandle(Hash, id, 6, target)
Edit: eh just posted after you posted :p
Quick question: In this spell, I want to create a debuff based on the spell used. There are four debuffs (one for each spell). 1 - dmg unit every time it attacks; 2 - damage unit every time it moves; 3 damage unit every time it uses an item; 4 - damage unit every time it casts a spell. Is there a way to detect a condition for those? If not, how can I make these debuffs? For example, I thought of using DDS to track when the debuffed unit is attacking. However, I am not sure how to transfer the caster to cause damage, since it requires another trigger with an event.
 

Wrda

Spell Reviewer
Level 26
Joined
Nov 18, 2012
Messages
1,887
For the first one, you could simply load the caster as "0" from its HandleId "parent" on the Damage Event trigger.
Second one requires Is Unit Moving Event from Bribe, which is included in the DDS, as long with Unit Indexer.
Third and fourth are similar to the first case.
Note that this method wouldn't work if you have several units/heroes with this spell, specifically for these debuffs.
 
Level 7
Joined
Feb 9, 2021
Messages
301
For the first one, you could simply load the caster as "0" from its HandleId "parent" on the Damage Event trigger.
Second one requires Is Unit Moving Event from Bribe, which is included in the DDS, as long with Unit Indexer.
Third and fourth are similar to the first case.
Note that this method wouldn't work if you have several units/heroes with this spell, specifically for these debuffs.
Well, I have multiple units with this debuff, and the spell should be MUI. Here I made my attempt with "attack debuff". However, I don't know how to detect the caster and the damaged is caused multiple times for some reason.


JASS:
function Trig_VMR_Attack_Actions takes nothing returns nothing
    if UnitHasBuffBJ(udg_DamageEventSource, 'B00E') and not udg_IsDamageSpell then
        call UnitDamageTarget( udg_DamageEventSource, udg_DamageEventSource, 20.00, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_PLANT, null)
    endif
endfunction

//===========================================================================
function InitTrig_VMR_Attack takes nothing returns nothing
    set gg_trg_VMR_Attack = CreateTrigger(  )
    call TriggerRegisterVariableEvent( gg_trg_VMR_Attack, "udg_DamageEvent", EQUAL, 1.00 )
    call TriggerAddAction( gg_trg_VMR_Attack, function Trig_VMR_Attack_Actions )
endfunction
 
Last edited:

Wrda

Spell Reviewer
Level 26
Joined
Nov 18, 2012
Messages
1,887
Depends on how you gave the buff to the unit... but the way I see it here is
JASS:
LoadUnitHandle(Hash, id, StringHash("caster"))
You'd have to rearrange your code so you on't be loading a null unit, since i saw a few FlushChildHashtable. Bribe supposedly handles damage recursion, you should check it out on his DDS. But in your case I don't know if replacing "not udg_IsDamageSpell" with "not udg_IsDamageCode" works, worth a try
 
Level 7
Joined
Feb 9, 2021
Messages
301
Depends on how you gave the buff to the unit... but the way I see it here is
JASS:
LoadUnitHandle(Hash, id, StringHash("caster"))
You'd have to rearrange your code so you on't be loading a null unit, since i saw a few FlushChildHashtable. Bribe supposedly handles damage recursion, you should check it out on his DDS. But in your case I don't know if replacing "not udg_IsDamageSpell" with "not udg_IsDamageCode" works, worth a try
I can't transfer the caster without the id. I create a buff through a dummy unit, but I am planning to change it for a buff system. It should be easy to do, but I just don't get how to transfer the caster.
 
Last edited:
Status
Not open for further replies.
Top