• 🏆 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!

[Spell] Passive - Remove CC after a delay [vJass]

Status
Not open for further replies.
Level 7
Joined
Feb 9, 2021
Messages
301
I am making a passive for a hero in a moba map, which removes Crowd Control (CC) from the hero x time later it was applied. On use, the passive knocks enemies back cause them damage and go into cooldown. For setting Crowd Control, I use [vJASS] - Buff System. The problem that I have is I do not know how to do a check on whether the applied/picked buff is of a type I want to remove. I thought about creating a boolean inside each buff struct. It will be set true if I want it to be removable by this spell, but I do not know to access it with the buff system API. Also, any feedback on my code will be great.

Here is my code and example of my CC:

JASS:
scope CCcounter
    
    private module SpellConfiguration
    
        static constant integer SPELL_ABILITY_ID    = '0000' //the rawcode of the spell
        static constant integer SPELL_SFX_ID         = '0000' //the rawcode of the spell
        static constant integer KB_SFX_ID            = '0000' //the rawcode of the spell
        static constant real SPEED                  = 1000
        
        static constant attacktype ATTACK_TYPE      = ATTACK_TYPE_MAGIC
        static constant damagetype DAMAGE_TYPE      = DAMAGE_TYPE_MAGIC
        static constant weapontype WEAPON_TYPE      = WEAPON_TYPE_WHOKNOWS
        
        static constant method CCRemovalDelay takes integer unitLevel returns real
            return 0.5 + 0.2 * unitLevel
        endmethod
        
        static constant method Cooldoown takes integer unitLevel returns real
            return 40.0 - 0.2 * unitLevel
        endmethod
            
       static method Damage takes unit caster, integer unitLevel returns real
            return (100. * unitLevel + 0.1 * GetHeroStr(caster, true)) * FPS
       endmethod
       
       static constant method KBRange takes integer unitLevel returns real
            return 200.0 + 0.2 * unitLevel
       endmethod
       
       static method Targets takes unit target, unit caster returns boolean
            return GetWidgetLife(target) > 0.405                    and/*
                */ IsUnitEnemy(target, GetOwningPlayer(caster))      
        endmethod
        
    endmodule
    
    private struct CCcounter extends array
        implement SpellConfiguration
        
        timer t
        unit target
        integer cdCounter
        
        private static method CooldoownTimer takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            
            if this.cdCounter == 0 then
                set this.t = null
                call ReleaseTimer(this.t)   
            else
                set this.cdCounter = this.cdCounter - 1
            endif
        
        endmethod
        
        private static method Action takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            local unit u
            local real tX  
            local real tY 
            local integer unit_level 
            local real dmg 
            local real aoe 
            local group enemies 
            local boolean isUnitStunned = false

            call Buff.pickBuffs(this.target)
            implement BuffListStart
                /*if Buff.picked.isBuffStun then
                    call Buff.picked.remove()
                    set isUnitStunned = true
                endif*/
            implement BuffListEnd

            if isUnitStunned then
                set enemies = CreateGroup()
                set tX = GetUnitX(this.target)
                set tY = GetUnitY(this.target)
                set unit_level = GetUnitLevel(this.target)
                set dmg = Damage(this.target, unit_level)
                set aoe = KBRange(unit_level)
                set u = null
                
                set this.cdCounter = Cooldoown(unit_level)
                
                call GroupEnumUnitsInRange(enemies, tX, tY, aoe, null)
                loop
                    set u = FirstOfGroup(enemies)
                    exitwhen u == null
                    if Targets(u, target) then
                        set udg_KB3D_Unit = u
                        set udg_KB3D_Range = KBRange(unit_level)
                        set udg_KB3D_Speed = SPEED
                        set udg_KB3D_Angle = GetAngleBetweenPoints(tX, GetUnitX(u), tY, GetUnitY(u))
                        set udg_KB3D_LoopDamage = dmg
                    endif
                    call GroupRemoveUnit(enemies, u)
                endloop
                
                
                call ReleaseTimer(this.t)
                set this.t = NewTimerEx(this)
                call TimerStart(this.t, 1.0 , true, function thistype.CooldoownTimer) 
                call DestroyGroup(enemies)
                set enemies = null
                
            else
                call ReleaseTimer(this.t)
            endif
            
            set target = null
            
        endmethod
    
        private static method AbilityCheck takes nothing returns nothing
            local thistype this
            if GetUnitAbilityLevel(BuffEvent.buff.target, SPELL_ABILITY_ID) > 0 and BuffEvent.buff.isBuffStun then
                set this = GetUnitId(BuffEvent.buff.target)
                if this.cdCounter == 0 then
                    set this.target = BuffEvent.buff.target
                    set this.t = NewTimerEx(this)
                    call TimerStart(this.t, CCRemovalDelay(GetUnitLevel(this.target)) , false, function thistype.Action) 
                endif
            endif
        endmethod
        
        private static method onInit takes nothing returns nothing
            call BuffEvent.create(function AbilityCheck)
        endmethod
    
    endstruct
    
endscope


JASS:
scope FrozenBuff
    /* -------------------------------------------------------------------------- */
    /*                                    Frozen                                  */
    /* -------------------------------------------------------------------------- */

    struct FrozenBuff extends Buff
        private static constant integer RAWCODE         = 'A00Z'
        private static constant integer DISPEL_TYPE     = BUFF_NEGATIVE
        private static constant integer STACK_TYPE      = BUFF_STACK_NONE
        private static constant string SFX_ID           = "Abilities\\Spells\\Undead\\FreezingBreath\\FreezingBreathTargetArt.mdl"
        
        effect sfx
        boolean isBuffStun
      
        method onRemove takes nothing returns nothing
            call Stun.stop(this.target)
            call SetUnitTimeScale(this.target, 100 * 0.01)
            call DestroyEffect(this.sfx)
        endmethod
      
        method onApply takes nothing returns nothing
            set isBuffStun = true
            call BJDebugMsg(R2S(this.duration))
            call Stun.apply(this.target, 99, false)
            call SetUnitTimeScale(this.target, 0 * 0.01)
            set this.sfx = AddSpecialEffectTarget(SFX_ID, this.target, "origin")
        endmethod
      
        implement BuffApply
    endstruct
endscope
 
Level 4
Joined
Jun 28, 2012
Messages
23
You simply want to do this :
vJASS:
local Buff b = Buff.get(target,source,StunBuff.typeid)
call b.remove()
Returns true if target has the buff, the Buff library uses the typeid of the struct you create.
And if you have multiple buff you will have multiple line like this for example

vJASS:
local Buff b = Buff.get(target,source,StunBuff.typeid)
call b.remove()
set b = Buff.get(target,source,FronzenBuff.typeid)
call b.remove()
set b = Buff.get(target,source,SilenceBuff.typeid)
call b.remove()
The FrozenBuff.typeid is the name of your struct :
Code:
 struct FrozenBuff extends Buff
 
Level 7
Joined
Feb 9, 2021
Messages
301
You simply want to do this :
vJASS:
Buff.get(target,source,StunBuff.typeid)
Returns true if target has the buff, the Buff library uses the typeid of the struct you create.
Well, that's a solution. The only things is I have 10+ CC buffs, but I guess I can put them into a function.
 
Level 4
Joined
Jun 28, 2012
Messages
23
The other solution a bit tricky is maybe do something like that

vJASS:
static constant boolean crowdControl
And when you loop through the list you check if crowdControl.
But then again you will have to implement static constant boolean crowdControl in every one your struct Buff



vJASS:
if this.cdCounter == 0 then
    set this.t = null
    call ReleaseTimer(this.t)  
else
    set this.cdCounter = this.cdCounter - 1
endif

And btw this won't work
 
Level 7
Joined
Feb 9, 2021
Messages
301
The other solution a bit tricky is maybe do something like that

vJASS:
static constant boolean crowdControl
And when you loop through the list you check if crowdControl.
But then again you will have to implement static constant boolean crowdControl in every one your struct Buff



vJASS:
if this.cdCounter == 0 then
    set this.t = null
    call ReleaseTimer(this.t) 
else
    set this.cdCounter = this.cdCounter - 1
endif

And btw this won't work
Yes, i noticed it later. I will set it to null after the release. Thanks.

So, the second solution is to add this boolean in every buff struct I have and set it to false or true? Or can i add it only to CC Buffs and set it to true?
 
Level 4
Joined
Jun 28, 2012
Messages
23
vJASS:
 call Buff.pickBuffs(this.target)
            implement BuffListStart
                /*if Buff.picked.isBuffStun then
                    call Buff.picked.remove()
                    set isUnitStunned = true
                endif*/
            implement BuffListEnd
Since you use this every struct will need to have the boolean implemented in them, the second solution is creating a struct extending buff and then you use this struct to extend when you create a CCBuff

vJASS:
struct BuffCC extends Buff
.......................

struct StunBuff extends BuffCC

struct FrozenBuff extends BuffCC

And I believe you could do this thereafter

vJASS:
 call BuffCC.pickBuffs(this.target)
            implement BuffListStart
                /*if BuffCC.picked.isBuffStun then
                    call BuffCC.picked.remove()
                    set isUnitStunned = true
                endif*/
            implement BuffListEnd
But i'm not so sure if it will get only the buff extends BuffCC or all buff extends Buff



Edit : Another solution could be to use an array to store the typeid of the struct you want to be removeable and compare if the typeid of the buff you pick is in this array and remove it.
 
Level 7
Joined
Feb 9, 2021
Messages
301
vJASS:
 call Buff.pickBuffs(this.target)
            implement BuffListStart
                /*if Buff.picked.isBuffStun then
                    call Buff.picked.remove()
                    set isUnitStunned = true
                endif*/
            implement BuffListEnd
Since you use this every struct will need to have the boolean implemented in them, the second solution is creating a struct extending buff and then you use this struct to extend when you create a CCBuff

vJASS:
struct BuffCC extends Buff
.......................

struct StunBuff extends BuffCC

struct FrozenBuff extends BuffCC

And I believe you could do this thereafter

vJASS:
 call BuffCC.pickBuffs(this.target)
            implement BuffListStart
                /*if BuffCC.picked.isBuffStun then
                    call BuffCC.picked.remove()
                    set isUnitStunned = true
                endif*/
            implement BuffListEnd
But i'm not so sure if it will get only the buff extends BuffCC or all buff extends Buff



Edit : Another solution could be to use an array to store the typeid of the struct you want to be removeable and compare if the typeid of the buff you pick is in this array and remove it.
The last solution sounds similar to the first your proposed. These two seems to be the most optimal, as I need this group of buffs only for this spell. How would you go about storing structs' ids and comparing them?
 
Level 4
Joined
Jun 28, 2012
Messages
23
Since you have Table on your map you could do something like this

vJASS:
static table t
static method onInit takes nothing returns nothing
    set t = Table.create()
    set t[StunBuff.typeid] = StunBuff.typeid
    set t[StunBuff.typeid] = StunBuff.typeid
...............................
endmethod
And then

vJASS:
call Buff.pickBuffs(this.target)
implement BuffListStart
if t.has(Buff.picked.getType()) then
   call Buff.picked.remove()
   set isUnitStunned = true
endif
implement BuffListEnd
 
Level 7
Joined
Feb 9, 2021
Messages
301
Since you have Table on your map you could do something like this

vJASS:
static table t
static method onInit takes nothing returns nothing
    set t = Table.create()
    set t[StunBuff.typeid] = StunBuff.typeid
    set t[StunBuff.typeid] = StunBuff.typeid
...............................
endmethod
And then

vJASS:
call Buff.pickBuffs(this.target)
implement BuffListStart
if t.has(Buff.picked.getType()) then
   call Buff.picked.remove()
   set isUnitStunned = true
endif
implement BuffListEnd
Thanks for your help. After thinking about it, it seems like the first solution ended up being the least troublesome and the fastest. However, I will keep in mind other solutions for similar situations. Everything works now on this part. Weirdly, group selection does not work for some reason... but I will find the way to fix it.

Edit: Do you have code comments? (Optimisation and Leaks?)
 
Last edited:
Status
Not open for further replies.
Top