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

"evasion aura"

Status
Not open for further replies.
Hi everyone!
I want to make a smoke grenade-type spell that gives all friendly units in area of effect the ability to dodge x percent of enemy attacks. My idea was to give evasion to all those units within the AoE, and then somehow remove it when they get outside of it. I just can't come up with how to do the removing part.
Feel free to suggest other methods to achieve the same effect, as long as they don't involve a damage detection system, i have way too many units for that and they die way to fast for that to be reasonable.

Please note that giving miss chance to enemies will NOT have the same effect, the idea is that units inside the smoke would be able to dodge bullets even from units outside of it!

Thanks in advance!
 
Level 12
Joined
Mar 13, 2012
Messages
1,121
Okay, whats the problem about adding and removing an evasion ability?

Unit - Add Ability
Unit - Remove Ability

Please specify so I can help you better..
 
Level 37
Joined
Mar 6, 2006
Messages
9,240
You could create a dummy unit at the center of the smoke. Periodically (every 0.2 seconds or whatever) pick units in range. If the picked unit doesn't have the ability, give it that. Add the unit into a group x. Also pick all units in group x and if the distance between the dummy and picked units is too great, remove the ability.

The thing to keep in mind is that there can be more than one smokes, so if a unit that has the ability is not in range of any smoke, remove the ability.
 
Okay, whats the problem about adding and removing an evasion ability?

Unit - Add Ability
Unit - Remove Ability

Please specify so I can help you better..

Because i can't cycle through all units on the map to see if they are close to a smoke. Once i have added it, it should be removed once they get outside of the AoE again, and then i need to detect that.

@Maker, that sounds like a reasonable idea. I could have a global group for all affected units, and a linked list of structs for the dummies that i loop though periodically. I am gonna tinker a bit with this and see what i get, but please, come with more ideas if you got any on how to make it in the slimmest way possible.

EDIT: this is what i got after a couple of minutes:

JASS:
library SmokeGrenade requires ListModule

    globals
        private constant real    RANGE       = 300.
        private constant integer EVASION_ID  = 'A02O'
        private constant integer DUMMY_ID    = 'h013'
    endglobals
    
    private function filter takes nothing returns boolean
        return (IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) == false)
    endfunction
    
    private struct dummy
        static group units
        static group temp
        static timer tick
        unit u
        real x
        real y
        implement List //for ListModule
        
        private static method EnumGiveAbil takes nothing returns nothing
            call UnitAddAbility(GetEnumUnit(), EVASION_ID)
            call GroupAddUnit(.units, GetEnumUnit())
        endmethod
        
        private static method GiveAbil takes nothing returns nothing
            local thistype this = .first
            
            loop
                exitwhen this == 0
                
                call GroupEnumUnitsInRange(.temp, .x, .y, RANGE, Filter(function filter))
                call ForGroup(.temp, function thistype.EnumGiveAbil)
                
                if .count < 1 then
                    call PauseTimer(.tick)
                endif
                
                set this = .next
            endloop
        endmethod 
        
        private static method hasAbil takes nothing returns nothing
            local thistype this = .first //ListModule helps me browse through every instance of the struct
            local unit target = GetEnumUnit()
            local real x = GetUnitX(target)
            local real y = GetUnitY(target)
            local real dx
            local real dy
            
            loop
                exitwhen this == 0
                set dx = .x - x
                set dy = .y - y
                //Remove evasion from all units that aren't close to a dummy
                if SquareRoot(dx * dx + dy * dy) > RANGE then
                    call UnitRemoveAbility(target, EVASION_ID)
                    call GroupRemoveUnit(.units, target)
                endif
                
                set this = .next
            endloop
            
            call thistype.GiveAbil() //and then we add them again to those who are
        endmethod
        
        private static method onExpire takes nothing returns nothing
            call ForGroup(.units, function thistype.hasAbil) 
        endmethod
        
        static method Create takes real x, real y, player owner returns dummy
            local thistype this = thistype.allocate()
            set .u = CreateUnit(owner, DUMMY_ID, x, y, 0.)
            set .x = x
            set .y = y
            
            call listAdd()
            
            if .count == 1 then
                call TimerStart(.tick, 0.6, true, function thistype.onExpire)
            endif
            
            return this
        endmethod
        
        method onDestroy takes nothing returns nothing
            call listRemove()
        endmethod
        
        private static method onInit takes nothing returns nothing
            set .tick   = CreateTimer()
            set .units  = CreateGroup()
            set .temp   = CreateGroup()
        endmethod
        
    endstruct

endlibrary

I had to do it in a backwards kind of fashion since i would otherwise have problems with overlapping auras, but i hope you understand how it is supposed to work. How do you think this looks?
 
Last edited:
Level 12
Joined
Mar 13, 2012
Messages
1,121
Well the event 'Unit - Unit Within Range' and then adding this unit to the group x as Maker said and so on should be quite efficient, no?
 
I hade a code library that i added to my post above, check that out.

The way i made it is like this:

1. Browse through the global group and compare each units location to each dummy unit. If it is outside of range, remove evasion. This will fire when the unit is away from ANY dummy on the map, hence i have to do it before i add them again.

2. Browse through all dummies, pick all organic units near them, give them the ability and add them to the global group. It picks all units intentionally, i don't care about ownership.

EDIT: I just realized, my code doesn't work. Since i compare all units to all dummies, and remove evasion when the unit is away from at least ONE of all the dummies, it will always remove it unless the dummies are all infact in the same spot. Duh. Gonna do a quick fix for this...

EDIT2: this should work better:

JASS:
library SmokeGrenade requires ListModule

    globals
        private constant real    RANGE       = 300.
        private constant integer EVASION_ID  = 'A02O'
        private constant integer DUMMY_ID    = 'h013'
    endglobals
    
    private function filter takes nothing returns boolean
        return (IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) == false)
    endfunction
    
    private struct dummy
        static group units
        static group temp
        static timer tick
        unit u
        real x
        real y
        implement List //for ListModule
        
        private static method EnumGiveAbil takes nothing returns nothing
            call UnitAddAbility(GetEnumUnit(), EVASION_ID)
            call GroupAddUnit(.units, GetEnumUnit())
        endmethod
        
        private static method GiveAbil takes nothing returns nothing
            local thistype this = .first
            
            loop
                exitwhen this == 0
                
                call GroupEnumUnitsInRange(.temp, .x, .y, RANGE, Filter(function filter))
                call ForGroup(.temp, function thistype.EnumGiveAbil)
                
                if .count < 1 then
                    call PauseTimer(.tick)
                endif
                
                set this = .next
            endloop
        endmethod 
        
        private static method hasAbil takes nothing returns nothing
            local thistype this = .first
            local unit target = GetEnumUnit()
            local real x = GetUnitX(target)
            local real y = GetUnitY(target)
            local real dx
            local real dy
            local integer count = 0
            
            loop
                exitwhen this == 0
                set dx = .x - x
                set dy = .y - y
                
                if SquareRoot(dx * dx + dy * dy) < RANGE then
                    set count = count+1
                endif
                
                set this = .next
            endloop
            
            if count == 0 then
                call UnitRemoveAbility(target, EVASION_ID)
                call GroupRemoveUnit(.units, target)
            endif
            
            call thistype.GiveAbil()
        endmethod
        
        private static method onExpire takes nothing returns nothing
            call ForGroup(.units, function thistype.hasAbil) 
        endmethod
        
        static method Create takes real x, real y, player owner returns dummy
            local thistype this = thistype.allocate()
            set .u = CreateUnit(owner, DUMMY_ID, x, y, 0.)
            set .x = x
            set .y = y
            
            call listAdd()
            
            if .count == 1 then
                call TimerStart(.tick, 0.6, true, function thistype.onExpire)
            endif
            
            return this
        endmethod
        
        method onDestroy takes nothing returns nothing
            call listRemove()
        endmethod
        
        private static method onInit takes nothing returns nothing
            set .tick   = CreateTimer()
            set .units  = CreateGroup()
            set .temp   = CreateGroup()
        endmethod
        
    endstruct

endlibrary
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
I hade a code library that i added to my post above, check that out.

The way i made it is like this:

1. Browse through the global group and compare each units location to each dummy unit. If it is outside of range, remove evasion. This will fire when the unit is away from ANY dummy on the map, hence i have to do it before i add them again.

2. Browse through all dummies, pick all organic units near them, give them the ability and add them to the global group.

EDIT: I just realized, my code doesn't work. Since i compare all units to all dummies, and remove evasion when the unit is away from at least ONE of all the dummies, it will always remove it unless the dummies are all infact in the same spot. Duh. Gonna do a quick fix for this...

EDIT2: this should work better:

JASS:
library SmokeGrenade requires ListModule

    globals
        private constant real    RANGE       = 300.
        private constant integer EVASION_ID  = 'A02O'
        private constant integer DUMMY_ID    = 'h013'
    endglobals
    
    private function filter takes nothing returns boolean
        return (IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) == false)
    endfunction
    
    private struct dummy
        static group units
        static group temp
        static timer tick
        unit u
        real x
        real y
        implement List //for ListModule
        
        private static method EnumGiveAbil takes nothing returns nothing
            call UnitAddAbility(GetEnumUnit(), EVASION_ID)
            call GroupAddUnit(.units, GetEnumUnit())
        endmethod
        
        private static method GiveAbil takes nothing returns nothing
            local thistype this = .first
            
            loop
                exitwhen this == 0
                
                call GroupEnumUnitsInRange(.temp, .x, .y, RANGE, Filter(function filter))
                call ForGroup(.temp, function thistype.EnumGiveAbil)
                
                if .count < 1 then
                    call PauseTimer(.tick)
                endif
                
                set this = .next
            endloop
        endmethod 
        
        private static method hasAbil takes nothing returns nothing
            local thistype this = .first
            local unit target = GetEnumUnit()
            local real x = GetUnitX(target)
            local real y = GetUnitY(target)
            local real dx
            local real dy
            local integer count = 0
            
            loop
                exitwhen this == 0
                set dx = .x - x
                set dy = .y - y
                
                if SquareRoot(dx * dx + dy * dy) < RANGE then
                    set count = count+1
                endif
                
                set this = .next
            endloop
            
            if count == 0 then
                call UnitRemoveAbility(target, EVASION_ID)
                call GroupRemoveUnit(.units, target)
            endif
            
            call thistype.GiveAbil()
        endmethod
        
        private static method onExpire takes nothing returns nothing
            call ForGroup(.units, function thistype.hasAbil) 
        endmethod
        
        static method Create takes real x, real y, player owner returns dummy
            local thistype this = thistype.allocate()
            set .u = CreateUnit(owner, DUMMY_ID, x, y, 0.)
            set .x = x
            set .y = y
            
            call listAdd()
            
            if .count == 1 then
                call TimerStart(.tick, 0.6, true, function thistype.onExpire)
            endif
            
            return this
        endmethod
        
        method onDestroy takes nothing returns nothing
            call listRemove()
        endmethod
        
        private static method onInit takes nothing returns nothing
            set .tick   = CreateTimer()
            set .units  = CreateGroup()
            set .temp   = CreateGroup()
        endmethod
        
    endstruct

endlibrary

just a question, why dont make a normal dummy aura(example endurance aura with 0 attack & move speed with different buff icon and description) and with damage detection system u check if DagameEventTarget got this aura buff then with random u make x% chance for Damage = 0 and write out miss :p

ok maker way is better if u want customize the eva chance, but if i remember Nest also made snip about aura things :) http://www.hiveworkshop.com/forums/jass-resources-412/snippet-auralevel-212146/
 
Because of the reason i already stated, i have too many units in my map that spawn and die too frequently to justify a damage detection system for all units. It requires you to create and destroy triggers everytime a unit dies and spawns, and the more often you do this the more likely it is to bug since destroying triggers on runtime is never very safe.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Actually, the easiest solution is this

http://www.hiveworkshop.com/forums/jass-resources-412/snippet-aurastruct-205061/

Create dummy, create aura for dummy. On enter range, add ability of level. On leave range, remove ability of level. Done.

Take you 5 minutes to code and it's efficient >.>.


The only problem is that all of your standard auras would then need to be custom auras as it does not work with wc3 standard auras : |. Wc3 standard auras break unit in range event ;p.
 
Level 19
Joined
Aug 8, 2007
Messages
2,765
You're overcomplicating this.

Make an aura, base it whatever and give it a buff.

Import DamageEvent (your choice) into your map

do something like this in dmg modify evnet

If (DamageEventTarget has buff YourAuraBuff and Random Integer between 1 and 100 is less than or equal to YourChance (15% would be 15) then
Set DamageAmount = 0
? - Create nessecary text tags to show "Dodged!"

e/ read the sentence about damage detection systems and i have to completely disagree with this. No map should have so many units that a damage detection system is not a choice
 
Well, i just want to achieve this without having to implement a shitload of code in the process.. DamageEvent requires me to get DamageModifiers and Table, while nestharus system requires me to get UnitEvent, UnitInRangeEvent, Tt, and RegisterPlayerUnitEvent (this being said, i already have Jesus4Lyf's Event library in the map!). That is thousands of extra lines of code just for a single shitty ability, and i don't feel like it is worth it.

EDIT: when i think of it, i might consider DamageEvent, just because i already have a damage detection system in my map reserved for just mechanicals, which is a bit more crude and skinned down than Anitarf's. It still kinda feels like building a nuclear powerplant to power a light bulb though.
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
EDIT: when i think of it, i might consider DamageEvent, just because i already have a damage detection system in my map reserved for just mechanicals, which is a bit more crude and skinned down than Anitarf's. It still kinda feels like building a nuclear powerplant to power a light bulb though.

i do similiar aura thing, just with channeling, i mean till a unit channel then in 600 range all friendly unit got x% damage reduction
basically mine a bit complicated maybe but if u need only for few aura then u can try it too this buff thing



Map header functions
JASS:
function RemoveUnitBuff takes integer id, integer index, boolean check returns nothing
    local integer cv = GetUnitUserData( udg_Buff_Unit[id] )
    local integer pl = GetPlayerId(GetOwningPlayer(udg_Buff_Unit[id])) + 1
    local integer buffid
    local real spd
    local integer amount
    local integer t
    local integer i = 1
    local integer times
    local unit u = udg_Buff_Unit[id]
    local integer diff
    if not check then
        set buffid = LoadInteger( udg_Buff_Index_Table, cv, index )
        set amount = LoadInteger( udg_Buff_Amount, cv, buffid ) * - 1
        set diff = amount
        set t = LoadInteger( udg_Buff_Index_Table, cv, buffid )
        if index != udg_Buff_Unit_Index[cv] then
            call SaveInteger( udg_Buff_Index_Table, cv, index, LoadInteger( udg_Buff_Index_Table, cv, udg_Buff_Unit_Index[cv] ) )
        endif
	
        set udg_Buff_Unit_Index[cv] = udg_Buff_Unit_Index[cv] - 1
        call DestroyEffect ( LoadEffectHandle(udg_Buff_EffectTable, cv, buffid))

        call DisplayTextToPlayer( Player(0), 0, 0, "|cff00000ff2nd Remove:|r" + GetUnitName(udg_Buff_Unit[id]) + " id" + I2S(id) + " index" + I2S(index))

        call SaveInteger( udg_Buff_Timer, cv, buffid, 0 )
        call SaveInteger( udg_Buff_Amount, cv, buffid, 0 )
        if buffid==1 then
            call AddHPMP( true, amount, udg_Buff_Unit[id] )
        elseif buffid==2 then
            call AddHPMP( false, amount, udg_Buff_Unit[id] )
        elseif buffid == 3 then
            set udg_B_Crit[cv] = udg_B_Crit[cv] + diff
        elseif buffid == 4 then
            set udg_B_CritDmg[cv] = udg_B_CritDmg[cv] + diff
        elseif buffid == 5 then
            set udg_B_Pdef[cv] = udg_B_Pdef[cv] + diff
        elseif buffid == 6 then
            set udg_B_Mdef[cv] = udg_B_Mdef[cv] + diff
        elseif buffid == 7 then
            set udg_B_AttLv[cv] = udg_B_AttLv[cv] + diff
        elseif buffid == 8 then
            set udg_B_DefLv[cv] = udg_B_DefLv[cv] + diff
        elseif buffid == 9 then
            set udg_B_Acc[cv] = udg_B_Acc[cv] + diff
        elseif buffid == 10 then
            set udg_B_Eva[cv] = udg_B_Eva[cv] + diff
        elseif buffid == 14 then
            call UnitRemoveAbility( u, 'A01R' )
        elseif buffid == 15 then
            set udg_Global_DmgRed[cv] = udg_Global_DmgRed[cv] + diff
        elseif buffid == 16 then
            set udg_Global_IncDmg[cv] = udg_Global_IncDmg[cv] + diff
        elseif buffid == 17 then
            set udg_B_Mdef[cv] = udg_B_Mdef[cv] + diff
        elseif buffid == 18 then
            set udg_B_Ref[cv] = udg_B_Ref[cv] + diff
        elseif buffid == 19 then
            set udg_Global_MattackAddon[cv] = udg_Global_MattackAddon[cv] + diff
        elseif buffid == 20 then
            set udg_Global_DmgInc[cv] = udg_Global_DmgInc[cv] + diff
        elseif buffid == 21 then
            set udg_Global_ArmorBreak[cv] = udg_Global_ArmorBreak[cv] + diff
        elseif buffid == 22 then
            set udg_Global_LifeSteal[cv] = udg_Global_LifeSteal[cv] + diff
        elseif buffid == 23 then
            set udg_B_Crit[cv] = udg_B_Crit[cv] + diff
        elseif buffid == 24 then
            set udg_B_CritDmg[cv] = udg_B_CritDmg[cv] + diff
        elseif buffid == 25 then
            set udg_B_Acc[cv] = udg_B_Acc[cv] + diff
        elseif buffid == 26 then
            set udg_B_Pdef[cv] = udg_B_Pdef[cv] + diff
        elseif buffid == 27 then
            set udg_B_Mdef[cv] = udg_B_Mdef[cv] + diff
        elseif buffid == 28 then
            set udg_Global_IncDmg[cv] = udg_Global_IncDmg[cv] + diff
        elseif buffid == 29 then
            set udg_Global_IncDmg[cv] = udg_Global_IncDmg[cv] + diff
        elseif buffid == 31 then
            set udg_Global_DmgInc[cv] = udg_Global_DmgInc[cv] + diff
        elseif buffid == 32 then
            set udg_Global_DmgInc[cv] = udg_Global_DmgInc[cv] + diff
        elseif buffid == 33 then
            set udg_Global_DmgRed[cv] = udg_Global_DmgRed[cv] + diff
        elseif buffid == 34 then
            set udg_Global_DmgRed[cv] = udg_Global_DmgRed[cv] + diff
        elseif buffid == 35 then
            set udg_Global_MattackAddon[cv] = udg_Global_MattackAddon[cv] + diff
        elseif buffid == 36 then
            set udg_B_LS[cv] = udg_B_LS[cv] + diff
        elseif buffid == 37 then
            set udg_B_LS[cv] = udg_B_LS[cv] + diff
        elseif buffid == 36 then
            set spd = GetUnitMoveSpeed( u )
            call SetUnitMoveSpeed( u, spd + diff )
        elseif buffid == 37 then
            set spd = GetUnitMoveSpeed( u )
            call SetUnitMoveSpeed( u, spd + diff )
        elseif buffid == 38 then
            set spd = GetUnitMoveSpeed( u )
            call SetUnitMoveSpeed( u, spd + diff )
        elseif buffid == 39 then
            set udg_B_Acc[cv] = udg_B_Acc[cv] + diff
        elseif buffid == 40 then
            set udg_Global_Shield[cv] = 0
        elseif buffid == 41 then
            set udg_Soulburn_Caster[cv] = null
            set udg_Soulburn_Damage[cv] = 0
        elseif buffid == 42 then
            set udg_Global_Exp_Buff[pl] = 0
        elseif buffid == 43 then
            set udg_Global_Exp_Red[pl] = udg_Global_Exp_Red[pl] + diff
        elseif buffid == 44 then
            set udg_B_Pdef[cv] = udg_B_Pdef[cv] + diff
        elseif buffid == 45 then
            set udg_B_Mdef[cv] = udg_B_Mdef[cv] + diff
        elseif buffid == 59 then
            set udg_Stat_AttackSpeed[pl] = udg_Stat_AttackSpeed[pl] + diff
            call AddDMG(false, u)
        endif

        call SaveInteger( udg_Buff_Amount, cv, buffid, 0 )

        if udg_Buff_Unit_Index[cv] == 0 then
            set udg_Buff_Unit_Id[cv] = 0
            if id != udg_Buff_Index then
                set udg_Buff_Unit[id] = udg_Buff_Unit[udg_Buff_Index]
                set udg_Buff_Unit_Id[GetUnitUserData(udg_Buff_Unit[id])] = id
                call RemoveUnitBuff (id, 0, true)
            else

            endif
            set udg_Buff_Index = udg_Buff_Index - 1
            call FlushChildHashtable(udg_Buff_Index_Table, cv)
            call FlushChildHashtable(udg_Buff_EffectTable, cv)
            call FlushChildHashtable(udg_Buff_Timer, cv)
            call FlushChildHashtable(udg_Buff_Amount, cv)
        endif
    else
        loop
            exitwhen id > udg_Buff_Index
            set cv = GetUnitUserData( udg_Buff_Unit[id] )
            loop
                exitwhen i > udg_Buff_Unit_Index[cv]
                set buffid = LoadInteger(udg_Buff_Index_Table, cv, i)
                set times = LoadInteger(udg_Buff_Timer, cv, buffid)
                call DisplayTextToPlayer( Player(0), 0, 0, "|cffffff00New unit:|r" + GetUnitName(udg_Buff_Unit[id]) + " index=" + I2S(id) + " buff id" + I2S(buffid) + " time" + I2S(times))

                if times == 0 then
                    call DisplayTextToPlayer( Player(0), 0, 0, "|cffff00ffRemove:|r" + GetUnitName(udg_Buff_Unit[id]) + " index" + I2S(i))

                    call RemoveUnitBuff (id, i, false)
                endif
                set i = i + 1
            endloop
            set id = id + 1
        endloop
    endif
//    call DisplayTextToPlayer( Player(0), 0, 0, "|cffffff00Remove buff a="+I2S(amount)+" array index" + I2S(udg_Buff_Index+1)+" C.Index" + I2S(id) +" buff index"+I2S(udg_Buff_Unit_Index[cv]) + " time" +I2S(t)+GetUnitName(u)+"|r")
    set u = null
endfunction

function ClearUnitBuff takes integer id returns nothing
    local unit u = udg_Buff_Unit[id]
    local integer e
    local integer v
    local integer i = 0
    local integer cv = GetUnitUserData(u)
    local integer buffid
    local integer po
    local integer amount
    local real spd

    loop
        exitwhen i > udg_Buff_Unit_Index[cv]
        set buffid = LoadInteger(udg_Buff_Index_Table, cv, i)
        call DisplayTextToPlayer( Player(0), 0, 0, "clearbuff from " + GetUnitName(u) + "unit index in unit array" + I2S(id) + " unit buff index" + I2S(i))
        call RemoveUnitBuff (id, i, false)
        set i = i + 1
    endloop
endfunction

function UnitDeBuff takes unit u, boolean debuff returns nothing
    local integer cv = GetUnitUserData(u)
    local integer index = udg_Buff_Unit_Id[cv]
    local integer i = 0
    local integer buffid
    local integer amount

    loop
        exitwhen i > udg_Buff_Unit_Index[cv]
        set buffid = LoadInteger(udg_Buff_Index_Table, cv, i)
        set amount = LoadInteger( udg_Buff_Amount, cv, buffid )
        if debuff and amount > 0 then
            call DestroyEffect(AddSpecialEffectTarget ("Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl", u, "overhead" ))
            call RemoveUnitBuff (index, i, false)
        elseif not debuff and amount < 0 then
            call DestroyEffect(AddSpecialEffectTarget ("Abilities\\Spells\\Human\\DispelMagic\\DispelMagicTarget.mdl", u, "overhead" ))
            call RemoveUnitBuff (index, i, false)
        endif
        set i = i + 1
    endloop
endfunction

function AddUnitBuff takes unit u, integer buffid, integer amount, integer dur, integer effectid, string apoint returns nothing
    local integer cv = GetUnitUserData(u)
    local integer times = LoadInteger( udg_Buff_Timer, cv, buffid )
    local integer oldv = LoadInteger( udg_Buff_Amount, cv, buffid )
    local integer diff = amount - oldv
    local integer pl = GetPlayerId(GetOwningPlayer(u)) + 1
    local real spd

    if udg_Buff_Unit_Id[cv] == 0 then
        set udg_Buff_Index = udg_Buff_Index + 1
        set udg_Buff_Unit[udg_Buff_Index] = u
        set udg_Buff_Unit_Id[cv] = udg_Buff_Index
    
    endif

    if times == 0 and LoadInteger( udg_Buff_Amount, cv, buffid ) == 0 then
        set udg_Buff_Unit_Index[cv] = udg_Buff_Unit_Index[cv] + 1
        call SaveInteger( udg_Buff_Index_Table, cv, udg_Buff_Unit_Index[cv], buffid )
		
        if effectid > - 1 then
            call SaveEffectHandle(udg_Buff_EffectTable, cv, buffid, AddSpecialEffectTarget( udg_Effects[effectid], u, apoint ))
        endif
    endif
	
    call SaveInteger( udg_Buff_Amount, cv, buffid, amount )
    call SaveInteger( udg_Buff_Timer, cv, buffid, dur )

//    call DisplayTextToPlayer( Player(0), 0, 0, "dur=" + I2S(dur) + " amount="+I2S(amount)+" unit index in unit array" + I2S(udg_Buff_Index) +" unit buff index"+I2S(udg_Buff_Unit_Index[cv]))

    if buffid == 1 and oldv == 0 then
        call AddHPMP(true, amount, u)
    elseif buffid == 2 and oldv == 0 then
        call AddHPMP(false, amount, u)
    elseif buffid == 3 then
        set udg_B_Crit[cv] = udg_B_Crit[cv] + diff
    elseif buffid == 4 then
        set udg_B_CritDmg[cv] = udg_B_CritDmg[cv] + diff
    elseif buffid == 5 then
        set udg_B_Pdef[cv] = udg_B_Pdef[cv] + diff
    elseif buffid == 6 then
        set udg_B_Mdef[cv] = udg_B_Mdef[cv] + diff
    elseif buffid == 7 then
        set udg_B_AttLv[cv] = udg_B_AttLv[cv] + diff
    elseif buffid == 8 then
        set udg_B_DefLv[cv] = udg_B_DefLv[cv] + diff
    elseif buffid == 9 then
        set udg_B_Acc[cv] = udg_B_Acc[cv] + diff
    elseif buffid == 10 then
        set udg_B_Eva[cv] = udg_B_Eva[cv] + diff
    elseif buffid == 14 then
        if oldv == 0 then
            call UnitAddAbility( u, 'A01R' )
        endif
    elseif buffid == 15 then
        set udg_Global_DmgRed[cv] = udg_Global_DmgRed[cv] + diff
    elseif buffid == 16 then
        set udg_Global_IncDmg[cv] = udg_Global_IncDmg[cv] + diff
    elseif buffid == 17 then
        set udg_B_Mdef[cv] = udg_B_Mdef[cv] + diff
    elseif buffid == 18 then
        set udg_B_Ref[cv] = udg_B_Ref[cv] + diff
    elseif buffid == 19 then
        set udg_Global_MattackAddon[cv] = udg_Global_MattackAddon[cv] + diff
    elseif buffid == 20 then
        set udg_Global_DmgInc[cv] = udg_Global_DmgInc[cv] + diff
    elseif buffid == 21 then
        set udg_Global_ArmorBreak[cv] = udg_Global_ArmorBreak[cv] + diff
    elseif buffid == 22 then
        set udg_Global_LifeSteal[cv] = udg_Global_LifeSteal[cv] + diff
    elseif buffid == 23 then
        set udg_B_Crit[cv] = udg_B_Crit[cv] + diff
    elseif buffid == 24 then
        set udg_B_CritDmg[cv] = udg_B_CritDmg[cv] + diff
    elseif buffid == 25 then
        set udg_B_Acc[cv] = udg_B_Acc[cv] + diff
    elseif buffid == 26 then
        set udg_B_Pdef[cv] = udg_B_Pdef[cv] + diff
    elseif buffid == 27 then
        set udg_B_Mdef[cv] = udg_B_Mdef[cv] + diff
    elseif buffid == 28 then
        set udg_Global_IncDmg[cv] = udg_Global_IncDmg[cv] + diff
    elseif buffid == 29 then
        set udg_Global_IncDmg[cv] = udg_Global_IncDmg[cv] + diff
    elseif buffid == 31 then
        set udg_Global_DmgInc[cv] = udg_Global_DmgInc[cv] + diff
    elseif buffid == 32 then
        set udg_Global_DmgInc[cv] = udg_Global_DmgInc[cv] + diff
    elseif buffid == 33 then
        set udg_Global_DmgRed[cv] = udg_Global_DmgRed[cv] + diff
    elseif buffid == 34 then
        set udg_Global_DmgRed[cv] = udg_Global_DmgRed[cv] + diff
    elseif buffid == 35 then
        set udg_Global_MattackAddon[cv] = udg_Global_MattackAddon[cv] + diff
    elseif buffid == 36 then
        set udg_B_LS[cv] = udg_B_LS[cv] + diff
    elseif buffid == 37 then
        set udg_B_LS[cv] = udg_B_LS[cv] + diff
    elseif buffid == 36 then
        set spd = GetUnitMoveSpeed( u )
        call SetUnitMoveSpeed( u, spd + diff )
    elseif buffid == 37 then
        set spd = GetUnitMoveSpeed( u )
        call SetUnitMoveSpeed( u, spd + diff )
    elseif buffid == 38 then
        set spd = GetUnitMoveSpeed( u )
        call SetUnitMoveSpeed( u, spd + diff )
    elseif buffid == 39 then
        set udg_B_Acc[cv] = udg_B_Acc[cv] + diff
    elseif buffid == 40 then
        //set udg_Global_Shield[cv] = 0
    elseif buffid == 41 then
        //set udg_Soulburn_Caster[cv] = null
        //set udg_Soulburn_Damage[cv] = 0
    elseif buffid == 42 then
        set udg_Global_Exp_Buff[pl] = udg_Global_Exp_Buff[pl] + diff
    elseif buffid == 43 then
        set udg_Global_Exp_Red[pl] = udg_Global_Exp_Red[pl] + diff
    elseif buffid == 44 then
        set udg_B_Pdef[cv] = udg_B_Pdef[cv] + diff
    elseif buffid == 45 then
        set udg_B_Mdef[cv] = udg_B_Mdef[cv] + diff
    elseif buffid == 59 then
        set udg_Stat_AttackSpeed[pl] = udg_Stat_AttackSpeed[pl] + diff
        call AddDMG(false, u)
    endif
    call DisplayTextToPlayer( Player(0), 0, 0, "|cffff0000b.id=" + I2S(buffid) + " diff=" + I2S(diff) + " array index" + I2S(udg_Buff_Index) + " b amount" + I2S(udg_Buff_Unit_Id[cv]) + "|r")

endfunction

all buff checking with 1 timer

JASS:
function Trig_Timer_Actions takes nothing returns nothing
    local integer i = 1
    local integer ii = 1
    local integer pv = 0
    local integer cc
    local integer po = 0
    local integer cv
    local integer buffid = 0
    local integer times = 0
    local integer vigor = 0
    loop
        exitwhen i > udg_Buff_Index
        set cv = GetUnitUserData(udg_Buff_Unit[i])
        if IsUnitType(udg_Buff_Unit[i], UNIT_TYPE_DEAD) or udg_Buff_Purge[cv] then
            call ClearUnitBuff (i)
        else
            loop
                exitwhen ii > udg_Buff_Unit_Index[cv]
                set buffid = LoadInteger(udg_Buff_Index_Table, cv, ii)
                set times = LoadInteger(udg_Buff_Timer, cv, buffid)
                call DisplayTextToPlayer( Player(0), 0, 0, GetUnitName(udg_Buff_Unit[i]) + " timer=" + I2S(times) + " unit index in unit array" + I2S(i) + " unit buff index" + I2S(ii))
                if times == 0 then
                    call RemoveUnitBuff (i, ii, false)
                else
                    call SaveInteger(udg_Buff_Timer, cv, buffid, times - 1)
                endif
                set ii = ii + 1
            endloop
            set ii = 1
            set udg_Buff_Purge[cv] = false
        endif
        set i = i + 1
    endloop
endfunction

healing rain with damage reduction

JASS:
function Trig_Tranquility_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A03M' 
endfunction


function TranquilityTimer takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetHandleId(t)
    local unit u = LoadUnitHandle(udg_Channel_Table, id, 1)
    local integer cv = GetUnitUserData(u)
    local unit pu
    local real x = udg_Channel_X[cv]
    local real y = udg_Channel_Y[cv]
    local real mana = GetUnitState(u, UNIT_STATE_MANA)
    local integer i = 0
   
    if mana >= udg_Channel_ManaCost[cv] and udg_Channel_Boolean[cv] then
        call SetUnitState (u, UNIT_STATE_MANA, (mana - udg_Channel_ManaCost[cv]))

            call GroupEnumUnitsInRange(udg_UG, x, y, 600.00, null)
            loop
                set pu = FirstOfGroup(udg_UG)
                exitwhen (pu==null)
if IsAlly(u, pu)  then 
                    call SetWidgetLife(pu, GetWidgetLife(pu)+udg_Channel_Dmg[cv])
//-------------------------------------------
// here i add friendly  unit to linked list
                    call AddUnitBuff (pu, 34, 33, 5, -1, "chest")
//-------------------------------------------
                    call DestroyEffect(AddSpecialEffect ("Abilities\\Spells\\Items\\AIil\\AIilTarget.mdl", GetUnitX(pu) , GetUnitY(pu)))
                endif
                call GroupRemoveUnit(udg_UG, pu)
           endloop
    
        call TimerStart(t, 5, false, function TranquilityTimer)
    else
        call PauseTimer(t)
        call DestroyTimer(t)
        call FlushChildHashtable(udg_Channel_Table, id)
        if udg_Channel_CasterSF[cv] != null then
            call IssueImmediateOrder( u, "stop" )
            call DestroyEffect(udg_Channel_CasterSF[cv])
        endif
    endif
   
    set t = null
    set u = null
endfunction

function Trig_Tranquility_Actions takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local real x = GetUnitX(u)
    local real y = GetUnitY(u)
    local real radius = 300.00
    local real dmg = GetUnitAbilityLevel(u, 'A03M') * 100 + udg_TotalSpellPower[GetPlayerId(GetTriggerPlayer())+1]
    local timer t = CreateTimer()
    local integer id = GetHandleId(t)
    local integer cv = GetUnitUserData(u)
    set udg_Channel_Boolean[cv] = true
    set udg_Channel_X[cv] = x
    set udg_Channel_Y[cv] = y
    set udg_Channel_ManaCost[cv] = GetUnitAbilityLevel(u, 'A03M') * 10
    set udg_Channel_Dmg[cv] = dmg
    if GetUnitState(u, UNIT_STATE_MANA) >= 10 then
        set udg_Channel_Count[cv] = 0
        set udg_Channel_Dur[cv] = 0
        call SaveUnitHandle(udg_Channel_Table, id, 1, u)
        call TimerStart(t, 0.5, false, function TranquilityTimer)
        set udg_Channel_CasterSF[cv] = AddSpecialEffectTarget("Abilities\\Spells\\NightElf\\Tranquility\\Tranquility.mdl", u, "origin")
    endif
    set t = null
    set u = null
endfunction

//===========================================================================
function InitTrig_Tranquility takes nothing returns nothing
    set gg_trg_Tranquility = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Tranquility, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Tranquility, Condition( function Trig_Tranquility_Conditions ) )
    call TriggerAddAction( gg_trg_Tranquility, function Trig_Tranquility_Actions )
endfunction


basically indifferent u do it with ability or with gui damage engine, then main thing is periodical add unit to a linked list for short time, that your choose u use a variable+damagedetectionsystem or u add passive ability with dummy hidden spellbook for a short time.

(when u add unit then u add ability or change variable amount[unit custom value] and if timer expired/unit die then simple remove that ability or decrease that variable)

i also use add passive ability for a time, this way i made buff from reincarnation :)
 
I managed to finalize my system and make it work, although i noticed i'm getting some framerate drops in occasions, especially in the first few moments when i launch a bunch of smoke grenades into a large number of units. I am gessing this is as good as it is gonna get with this solution though, so i might indeed be better off getting a damage detection system after all.

Here is the final code for those who are interrested:

JASS:
library SmokeGrenade requires ListModule

    globals
        private constant real    RANGE       = 180.
        private constant integer EVASION_ID  = 'svsb'
        private constant integer DUMMY_ID    = 'smdu'
    endglobals
    
    private function filter takes nothing returns boolean
        return (IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) == false and GetUnitTypeId(GetFilterUnit()) != DUMMY_ID and GetUnitTypeId(GetFilterUnit()) != 'h013')
    endfunction
    
    struct Smoke
        static group units
        static group temp
        static timer tick
        unit u
        real x
        real y
        timer t
        implement List //for ListModule
        
        private static method EnumGiveAbil takes nothing returns nothing
            call UnitAddAbility(GetEnumUnit(), EVASION_ID)
            call GroupAddUnit(.units, GetEnumUnit())
        endmethod
        
        private static method GiveAbil takes nothing returns nothing
            local thistype this = .first
            
            loop
                exitwhen this == 0
                
                call GroupEnumUnitsInRange(.temp, .x, .y, RANGE, Filter(function filter))
                call ForGroup(.temp, function thistype.EnumGiveAbil)
                call GroupClear(.temp)
                
                set this = .next
            endloop
            
            if .count < 1 then
                call PauseTimer(.tick)
            endif
        endmethod 
        
        private static method hasAbil takes nothing returns nothing
            local thistype this = .first
            local unit target = GetEnumUnit()
            local real x = GetUnitX(target)
            local real y = GetUnitY(target)
            local real dx
            local real dy
            local integer count = 0
            
            loop
                exitwhen this == 0
                set dx = .x - x
                set dy = .y - y
                
                if SquareRoot(dx * dx + dy * dy) < RANGE then
                    set count = count+1
                endif
                
                set this = .next
            endloop
            
            if count == 0 then
                call UnitRemoveAbility(target, EVASION_ID)
                call GroupRemoveUnit(.units, target)
            endif
            
            set target = null
        endmethod
        
        private static method onExpire takes nothing returns nothing
            call ForGroup(.units, function thistype.hasAbil)
            call thistype.GiveAbil()
        endmethod
        
        private static method KillSmoke takes nothing returns nothing
            local thistype this = LoadInteger(Hashtbl, 2, GetHandleId(GetExpiredTimer()))
            call .destroy()
        endmethod
        
        static method create takes real x, real y, player owner returns Smoke
            local thistype this = thistype.allocate()
            set .u = CreateUnit(owner, DUMMY_ID, x, y, 0.)
            set .x = x
            set .y = y
            set .t = CreateTimer()
            
            call listAdd()
            
            if .count == 1 then
                call TimerStart(.tick, 1.2, true, function thistype.onExpire)
            endif
            
            call SaveInteger(Hashtbl, 2, GetHandleId(.t), this)
            call TimerStart(.t, 20., false, function thistype.KillSmoke)
            
            return this
        endmethod
        
        method onDestroy takes nothing returns nothing
            call listRemove()
            call RemoveSavedInteger(Hashtbl, 2, GetHandleId(.t))
            call KillUnit(.u)
            call DestroyTimer(.t)
        endmethod
        
        private static method onInit takes nothing returns nothing
            set .tick   = CreateTimer()
            set .units  = CreateGroup()
            set .temp   = CreateGroup()
        endmethod
        
    endstruct

endlibrary
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
i can ask something? why u use for loop and create group each time instead loop?

u can declare group like global
JASS:
            call GroupEnumUnitsInRange(udg_UG, x, y, RANGE, null)
            loop
                set pu = FirstOfGroup(udg_UG)
                exitwhen (pu==null)
                    if not IsUnitType(pu, UNIT_TYPE_MECHANICAL) and GetUnitTypeId(pu) != DUMMY_ID and GetUnitTypeId(pu) != 'h013'  then 
                 //do action
                    endif
                call GroupRemoveUnit(udg_UG, pu)
           endloop

anyway linked list not faster than timer for each unit?

(just a notice about damage detection engine, between enable-disableing floating text = really visible difference when alot unit attack same time, atleast on my pc drastically droped the fps after i put 700 paladin on same part of the map and all attacked eachother and still i use floating text too, without it really better)
 
Last edited:
I don't create groups every loop, and all my groups are alreaady global (static struct members are the same as private globals!). Groups are created on initialization and never destroyed. Also, in your suloution, you pick ALL nearby untis first and then do the check, it is much more efficient to only pick the ones you want using a filter and then loop through them. As for why i use ForGroup... i could ofcourse go with your method just to get less function calls, but i just thought this was more readable since i would otherwise be forced to put a loop inside a loop.
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
I don't create groups every loop, and all my groups are alreaady global (static struct members are the same as private globals!). Groups are created on initialization and never destroyed. Also, in your suloution, you pick ALL nearby untis first and then do the check, it is much more efficient to only pick the ones you want using a filter and then loop through them. As for why i use ForGroup... i could ofcourse go with your method just to get less function calls, but i just thought this was more readable since i would otherwise be forced to put a loop inside a loop.

ah i see, i never use structs because i allways used normal we. then seems ok.

something i dont got, ofc u filter with forloop but that is also a loop, then another loop when u do action, correct me if i am wrong but forloop also check every unit, just remove the group what dosnt fit with condition.
 
Well, that is pretty similar to what i already have, except instead of a buff i simply group all units within range of the aura unit and add them to a group (since there is no event for when a unit retrieves a buff), and then when i refresh, i loop through all units in the group and check if they are still in range of a buff unit, and if not, i remove the effect from them.

Or maybe you mean, that instead of doing the whole range check thing, i just check if the unit has the buff? cause that could ofcourse also be effective...
 
Status
Not open for further replies.
Top