• 🏆 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 doesn't bounce to neutral units.

Status
Not open for further replies.
Level 14
Joined
Jul 19, 2007
Messages
772
Well I know almost nothing about JASS-triggering so I can't solve this myself. I have a JASS-triggered spell named "Elven Orb" in my map and it's meant to damage target enemy unit and gives it an armor reduction and if the enemy unit with the armor reduction is killed then the spell should bounce to a nearby enemy unit near the killed unit but it doesn't seems to bounce to neutral units and I dunno how to solve that. Pls help.

JASS:
library FelBeam requires Missiles, SpellEffectEvent, PluginSpellEffect, NewBonus, Utilities
    /* ----------------------- Fel Beam v1.4 by Chopinski ----------------------- */
    // Credits:
    //     BPower    - Missile Library
    //     Bribe     - SpellEffectEvent
    //     AZ        - Fel Beam model
    //     nGy       - Haunt model
    //     The Panda - ToxicBeam icon
    /* ----------------------------------- END ---------------------------------- */
    
    /* -------------------------------------------------------------------------- */
    /*                                Configuration                               */
    /* -------------------------------------------------------------------------- */
    globals
        // The raw code of the Rain of Fel Fire ability
        public constant integer     ABILITY       = 'A09I'
        // The beam inicial z offset
        private constant real       START_HEIGHT  = 60
        // The beam final z offset
        private constant real       END_HEIGHT    = 60
        // The landing time of the falling misisle
        private constant real       LANDING_TIME  = 1.5
        // The impact radius of the missile that will damage units.
        private constant real       IMPACT_RADIUS = 120.
        // The missile model
        private constant string     MISSILE_MODEL = "Abilities\\Spells\\Other\\HealingSpray\\HealBottleMissile.mdx"
        // The size of the fel beam
        private constant real       MISSILE_SCALE = 1.5
        // The beam missile speed
        private constant real       MISSILE_SPEED = 700
        // The Attack type of the damage dealt (Spell)
        private constant attacktype ATTACK_TYPE   = ATTACK_TYPE_NORMAL
        // The Damage type of the damage dealt 
        private constant damagetype DAMAGE_TYPE   = DAMAGE_TYPE_MAGIC
        // The curse model
        private constant string     CURSE_MODEL   = "Burning Rage Yellow.mdx"
        // The curse attachment point
        private constant string     CURSE_ATTACH  = "overhead"
    endglobals

    // The search range of units after a cursed unit dies
    private function GetSearchRange takes integer level returns real
        return 700. + 0.*level
    endfunction

    // The damage amount
    private function GetDamage takes integer level returns real
        return 50. *level
    endfunction

    // The amount of armor reduced
    public function GetArmorReduction takes integer level returns integer
        return level* 3
    endfunction

    // How long the curse lasts
    public function GetCurseDuration takes integer level returns real
        return 5. + 0.*level
    endfunction
    
    /* -------------------------------------------------------------------------- */
    /*                                   System                                   */
    /* -------------------------------------------------------------------------- */
    struct Curse
        static timer timer = CreateTimer()
        static integer array n
        static boolean array cursed
        //Dynamic Indexing
        static integer didx = -1
        static thistype array data

        unit    unit
        integer armor
        integer index
        real    ticks
        effect  effect

        method remove takes integer i returns integer
            call DestroyEffect(effect)
            call AddUnitBonus(unit, BONUS_ARMOR, armor)

            set data[i]       = data[didx]
            set didx          = didx - 1
            set cursed[index] = false
            set n[index]      = 0
            set unit          = null
            set effect        = null

            if didx == -1 then
                call PauseTimer(timer)
            endif

            call deallocate()

            return i - 1
        endmethod

        private static method onPeriod takes nothing returns nothing
            local integer  i = 0
            local thistype this

            loop
                exitwhen i > didx
                    set this = data[i]

                    if ticks <= 0 or not UnitAlive(unit) then
                        set i = remove(i)
                    endif
                    set ticks = ticks - 0.5
                set i = i + 1
            endloop
        endmethod

        static method create takes unit target, real duration, integer amount returns thistype
            local integer  idx = GetUnitUserData(target)
            local thistype this

            if n[idx] != 0 then
                set this       = n[idx]
            else
                set this       = thistype.allocate()
                set unit       = target
                set armor      = amount
                set effect     = AddSpecialEffectTarget(CURSE_MODEL, target, CURSE_ATTACH)
                set index      = idx
                set didx       = didx + 1
                set data[didx] = this
                set n[idx]     = this

                call AddUnitBonus(target, BONUS_ARMOR, -amount)
                if didx == 0 then
                    call TimerStart(timer, 0.5, true, function thistype.onPeriod)
                endif
            endif

            if duration >= 0.5 then
                set ticks = duration
            else
                set ticks = 0.
            endif

            return this
        endmethod
    endstruct

    struct Beam extends Missiles
        integer armor
        integer index
        real    curse_duration

        method onFinish takes nothing returns boolean
            if UnitAlive(target) then
                set Curse.cursed[index] = true
                if UnitDamageTarget(source, target, damage, false, false, ATTACK_TYPE, DAMAGE_TYPE, null) then
                    call Curse.create(target, curse_duration, armor)
                endif
            endif

            return true
        endmethod
    endstruct

    struct FelBeam
        static unit array source

        static method launch takes Beam beam, unit caster, unit target, integer level returns nothing
            set beam.source         = caster
            set beam.target         = target
            set beam.model          = MISSILE_MODEL
            set beam.scale          = MISSILE_SCALE
            set beam.speed          = MISSILE_SPEED
            set beam.damage         = GetDamage(level)
            set beam.owner          = GetOwningPlayer(caster)
            set beam.armor          = GetArmorReduction(level)
            set beam.curse_duration = GetCurseDuration(level)
            set beam.index          = GetUnitUserData(target)
            set source[beam.index]  = caster

            call beam.launch()
        endmethod

        private static method onDeath takes nothing returns nothing
            local unit    killed = GetTriggerUnit()
            local integer index  = GetUnitUserData(killed)
            local unit    caster = source[index]
            local integer level
            local real    x
            local real    y
            local real    z
            local group   g
            local unit    v
            local Beam    beam
        
            if Curse.cursed[index] then
                if source[index] == null then
                    set caster = GetKillingUnit()
                    set level  = 1
                else
                    set level = GetUnitAbilityLevel(caster, ABILITY)
                endif
        
                set x = GetUnitX(killed)
                set y = GetUnitY(killed)
                set z = GetUnitFlyHeight(killed) + START_HEIGHT
                set g = GetEnemyUnitsInRange(GetOwningPlayer(caster), x, y, GetSearchRange(level), false, false)
                if BlzGroupGetSize(g) > 0 then
                    set v    = GroupPickRandomUnit(g)
                    set beam = Beam.create(x, y, z, GetUnitX(v), GetUnitY(v), END_HEIGHT)
                    call launch(beam, caster, v, level)
                endif
                call DestroyGroup(g)
                set source[index]       = null
                set Curse.cursed[index] = false
            endif
        
            set g      = null
            set v      = null
            set killed = null
            set caster = null
        endmethod

        private static method onCast takes nothing returns nothing
            local Beam beam = Beam.create(Spell.source.x, Spell.source.y, START_HEIGHT, Spell.target.x, Spell.target.y, END_HEIGHT)
            
            call launch(beam, Spell.source.unit, Spell.target.unit, Spell.level)
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent(ABILITY, function thistype.onCast)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.onDeath)
        endmethod
    endstruct
endlibrary
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,544
Do you mean Neutral Hostile units? Should Neutral Passive be targeted as well?

Anyway, it looks like that code is relying on another "helper" system which is providing it with this function:
vJASS:
set g = GetEnemyUnitsInRange(GetOwningPlayer(caster), x, y, GetSearchRange(level), false, false)
GetEnemyUnitsInRange() puts nearby enemy units into a Unit Group. The problem is that it's excluding Neutral Hostile. Maybe those booleans in the function "false", "false" have something to do with it. I'd have to see the code that this function comes from to truly know.

If you look at the top of the FelBeam code you can see which libraries (scripts/systems) it relies on:
vJASS:
library FelBeam requires Missiles, SpellEffectEvent, PluginSpellEffect, NewBonus, Utilities
So it requires five different scripts. One of those scripts contains the GetEnemyUnitsInRange() function, which I image would be Utilities.
 
Level 14
Joined
Jul 19, 2007
Messages
772
Do you mean Neutral Hostile units? Should Neutral Passive be targeted as well?

Anyway, it looks like that code is relying on another "helper" system which is providing it with this function:
vJASS:
set g = GetEnemyUnitsInRange(GetOwningPlayer(caster), x, y, GetSearchRange(level), false, false)
GetEnemyUnitsInRange() puts nearby enemy units into a Unit Group. The problem is that it's excluding Neutral Hostile. Maybe those booleans in the function "false", "false" have something to do with it. I'd have to see the code that this function comes from to truly know.

If you look at the top of the FelBeam code you can see which libraries (scripts/systems) it relies on:
vJASS:
library FelBeam requires Missiles, SpellEffectEvent, PluginSpellEffect, NewBonus, Utilities
So it requires five different scripts. One of those scripts contains the GetEnemyUnitsInRange() function, which I image would be Utilities.
Yes I think you are right about Utilites, it should be in there somewhere but I found alot of "GetEnemyUnitsInRange" so I dunno which one to change how to change it :-/

JASS:
library Utilities requires TimerUtils, Missiles
    /* ------------------------ Utilities functions v1.6 ------------------------ */
    // How to Import:
    // 1 - Copy this library into your map
    // 2 - Copy the Stun, Silence, Slow and Fear abilities and match them below and the Slow Buff
    // 3 - Copy the TimerUtils and Missiles libraries into your map and follow their import instructions
    /* ------------------------------ By Chopinski ------------------------------ */
    globals
        // The raw code of the ability used to silence an unit
        public  constant integer SILENCE   = 'A0CO'
        // The raw code of the ability used to stun an unit
        public  constant integer STUN      = 'A0GS'
        // The raw code of the ability used to slow an unit
        public  constant integer SLOW      = 'A0CN'
        // The dummy caster unit id 
        public  constant integer DUMMY     = 'dumi'
        // Update period
        private constant real    PERIOD    = 0.031250000
        // location z
        private location         LOCZ      = Location(0,0)
        // One hashtable to rule them all
        private hashtable        table     = InitHashtable()
        // Closest Unit
        private unit             bj_closestUnitGroup
    endglobals

    // Only one declaration per map required
    native UnitAlive takes unit id returns boolean

    // Returns the terrain Z value (Desync safe)
    function GetLocZ takes real x, real y returns real
        call MoveLocation(LOCZ, x, y)
        return GetLocationZ(LOCZ)
    endfunction
    
    // Similar to GetUnitX and GetUnitY but for Z axis
    function GetUnitZ takes unit u returns real
        return GetLocZ(GetUnitX(u), GetUnitY(u)) + GetUnitFlyHeight(u)
    endfunction
    
    // Similar to SetUnitX and SetUnitY but for Z axis
    function SetUnitZ takes unit u, real z returns nothing
        call SetUnitFlyHeight(u, z - GetLocZ(GetUnitX(u), GetUnitY(u)), 0)
    endfunction

    // Anlge between 2D points
    function AngleBetweenCoordinates takes real x, real y, real x2, real y2 returns real
        return Atan2(y2 - y, x2 - x)
    endfunction

    // Similar to AddSpecialEffect but scales the effect and considers z and return it
    function AddSpecialEffectEx takes string model, real x, real y, real z, real scale returns effect
        set bj_lastCreatedEffect = AddSpecialEffect(model, x, y)

        if z != 0 then
            call BlzSetSpecialEffectZ(bj_lastCreatedEffect, z + GetLocZ(x, y))
        endif
        call BlzSetSpecialEffectScale(bj_lastCreatedEffect, scale)
        
        return bj_lastCreatedEffect
    endfunction

    // Returns a group of enemy units of the specified player within the specified AOE of x and y
    function GetEnemyUnitsInRange takes player enemyOf, real x, real y, real aoe, boolean structures, boolean magicImmune returns group
        local group g = CreateGroup()
        local group h = CreateGroup()
        local unit  w
        
        call GroupEnumUnitsInRange(h, x, y, aoe, null)
        if structures and magicImmune then
            loop
                set w = FirstOfGroup(h)
                exitwhen w == null
                    if IsUnitEnemy(w, enemyOf) and UnitAlive(w) then
                        call GroupAddUnit(g, w)
                    endif
                call GroupRemoveUnit(h, w)
            endloop
        elseif structures and not magicImmune then
            loop
                set w = FirstOfGroup(h)
                exitwhen w == null
                    if IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE) then
                        call GroupAddUnit(g, w)
                    endif
                call GroupRemoveUnit(h, w)
            endloop
        elseif magicImmune and not structures then
            loop
                set w = FirstOfGroup(h)
                exitwhen w == null
                    if IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE) then
                        call GroupAddUnit(g, w)
                    endif
                call GroupRemoveUnit(h, w)
            endloop
        else
            loop
                set w = FirstOfGroup(h)
                exitwhen w == null
                    if IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE) then
                        call GroupAddUnit(g, w)
                    endif
                call GroupRemoveUnit(h, w)
            endloop
        endif
        call DestroyGroup(h)
    
        set    h = null
        return g
    endfunction

    // Returns the closest unit in a unit group with center at x and y
    function GetClosestUnitGroup takes real x, real y, group g returns unit
        local unit    u
        local real    dx
        local real    dy
        local real    md = 100000
        local integer i  = 0
        local integer size = BlzGroupGetSize(g)
        
        set bj_closestUnitGroup = null
        loop
            exitwhen i == size
                set u = BlzGroupUnitAt(g, i)
                if UnitAlive(u) then
                    set dx = GetUnitX(u) - x
                    set dy = GetUnitY(u) - y
                    
                    if (dx*dx + dy*dy)/100000 < md then
                        set bj_closestUnitGroup = u
                        set md = (dx*dx + dy*dy)/100000
                    endif
                endif
            set i = i + 1
        endloop
        
        return bj_closestUnitGroup
    endfunction

    /* -------------------------------------------------------------------------- */
    /*                           Reset Ability Cooldown                           */
    /* -------------------------------------------------------------------------- */
    private struct ResetCooldown
        timer timer
        unit unit
        integer ability

        private static method onExpire takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())

            call BlzEndUnitAbilityCooldown(unit, ability)
            call ReleaseTimer(timer)
            call deallocate()
            
            set unit = null
            set timer  = null
        endmethod

        static method reset takes unit u, integer id returns nothing
            local thistype this = thistype.allocate()

            set timer = NewTimerEx(this)
            set unit = u
            set ability = id

            call TimerStart(timer, 0.01, false, function thistype.onExpire)
        endmethod 
    endstruct
    
    /* -------------------------------------------------------------------------- */
    /*                                  Knockback                                 */
    /* -------------------------------------------------------------------------- */
    private struct Knockback extends Missiles
        private static integer array knocked
    
        boolean cliff
        boolean destructable
        boolean unit
        boolean isPaused
        integer key
        effect attachment
    
        method onPeriod takes nothing returns boolean
            if UnitAlive(source) then
                call SetUnitX(source, prevX)
                call SetUnitY(source, prevY)
                
                return false
            else
                return true
            endif
        endmethod
        
        method onHit takes unit u returns boolean
            if unit then
                if UnitAlive(u) then
                    return true
                else
                    return false
                endif
            else
                return false
            endif
        endmethod
        
        method onDestructable takes destructable d returns boolean
            if destructable then
                if GetDestructableLife(d) > 0 then
                    return true
                else
                    return false
                endif
            else
                return false
            endif
        endmethod
        
        method onCliff takes nothing returns boolean
            return cliff
        endmethod
        
        method onPause takes nothing returns boolean
            call pause(false)
            return false
        endmethod
        
        method onRemove takes nothing returns nothing
            call DestroyEffect(attachment)
            set knocked[key] = knocked[key] - 1
            
            if isPaused and knocked[key] == 0 then
                call BlzPauseUnitEx(source, false)
            endif
            
            set attachment = null
        endmethod
        
        static method isUnitKnocked takes unit u returns boolean
            return knocked[GetUnitUserData(u)] > 0
        endmethod
        
        static method start takes unit whichUnit, real angle, real distance, real duration, string model, string point, boolean onCliff, boolean onDestructable, boolean onUnit, boolean isPaused returns nothing
            local real x = GetUnitX(whichUnit)
            local real y = GetUnitY(whichUnit)
            local thistype this = thistype.create(x, y, 0, x + distance*Cos(angle), y + distance*Sin(angle), 0)

            set .source = whichUnit
            set .duration = duration
            set .collision = 2*BlzGetUnitCollisionSize(whichUnit)
            set .cliff = onCliff
            set .destructable = onDestructable
            set .unit = onUnit
            set .isPaused = isPaused
            set .key = GetUnitUserData(whichUnit)
            set knocked[key] = knocked[key] + 1
            
            if model != null and point != null then
                set .attachment = AddSpecialEffectTarget(model, whichUnit, point)
            endif
            
            if isPaused and knocked[key] == 1 then
                call BlzPauseUnitEx(whichUnit, true)
            endif
            
            call launch()
        endmethod
    endstruct

    /* -------------------------------------------------------------------------- */
    /*                                Timed Ability                               */
    /* -------------------------------------------------------------------------- */
    private struct TimedAbility
        static timer timer = CreateTimer()
        static integer key = -1
        static thistype array array

        unit unit
        integer ability
        real duration

        method remove takes integer i returns integer
            call UnitRemoveAbility(unit, ability)
            call RemoveSavedInteger(table, GetHandleId(unit), ability)

            set array[i] = array[key]
            set key = key - 1
            set unit = null

            if key == -1 then
                call PauseTimer(timer)
            endif

            call deallocate()

            return i - 1
        endmethod

        static method onPeriod takes nothing returns nothing
            local integer i = 0
            local thistype this

            loop
                exitwhen i > key
                    set this = array[i]

                    if duration <= 0 then
                        set i = remove(i)
                    endif
                    set duration = duration - 0.1
                set i = i + 1
            endloop
        endmethod


        static method add takes unit u, integer id, real duration, integer level, boolean hide returns nothing
            local thistype this = LoadInteger(table, GetHandleId(u), id)
            
            if this == 0 then
                set this = thistype.allocate()
                set unit = u
                set ability = id
                set key = key + 1
                set array[key] = this

                call SaveInteger(table, GetHandleId(unit), ability, this)

                if key == 0 then
                    call TimerStart(timer, 0.1, true, function thistype.onPeriod)
                endif
            endif

            if GetUnitAbilityLevel(unit, ability) != level then
                call UnitAddAbility(unit, ability)
                call SetUnitAbilityLevel(unit, ability, level)
                call UnitMakeAbilityPermanent(unit, true, ability)
                call BlzUnitHideAbility(unit, ability, hide)
            endif

            set .duration = duration
        endmethod
    endstruct

    /* -------------------------------------------------------------------------- */
    /*                                 Fear System                                */
    /* -------------------------------------------------------------------------- */
    private struct Fear
        static constant real PERIOD = 1./5.
        static constant integer DIRECTION_CHANGE = 5 
        static constant real MAX_CHANGE = 200.
        static integer key = -1
        static thistype array array
        static integer array struct
        static boolean array flag
        static real array x
        static real array y
        static timer timer = CreateTimer()

        unit unit
        effect effect
        integer id
        real duration
        integer change
        boolean selected

        static method feared takes unit target returns boolean
            return struct[GetUnitUserData(target)] != 0
        endmethod

        method remove takes integer i returns integer
            set flag[id] = true
            call IssueImmediateOrder(unit, "stop")
            call DestroyEffect(effect)
            call UnitRemoveAbility(unit, 'Abun')

            if selected then
                call SelectUnitAddForPlayer(unit, GetOwningPlayer(unit))
            endif

            set struct[id] = 0
            set unit = null
            set effect = null
            set array[i] = array[key]
            set key = key - 1

            call deallocate()

            if key == -1 then
                call PauseTimer(timer)
            endif

            return i - 1
        endmethod

        private static method onPeriod takes nothing returns nothing
            local integer i = 0
            local thistype this

            loop
                exitwhen i > key
                    set this = array[i]

                    if duration > 0 and UnitAlive(unit) then
                        set duration = duration - PERIOD
                        set change = change + 1

                        if change >= DIRECTION_CHANGE then
                            set change = 0
                            set flag[id] = true
                            set x[id] = GetRandomReal(GetUnitX(unit) - MAX_CHANGE, GetUnitX(unit) + MAX_CHANGE)
                            set y[id] = GetRandomReal(GetUnitY(unit) - MAX_CHANGE, GetUnitY(unit) + MAX_CHANGE)
                            call IssuePointOrder(unit, "move", x[id], y[id])
                        endif
                    else
                        set i = remove(i)
                    endif
                set i = i + 1
            endloop
        endmethod

        static method apply takes unit target, real duration, string fxpath, string attachment returns nothing
            local integer id = GetUnitUserData(target)
            local thistype this

            if struct[id] != 0 then
                set this = struct[id]
            else
                set this = thistype.allocate()
                set .id = id
                set unit = target
                set change = 0
                set selected = IsUnitSelected(target, GetOwningPlayer(target))
                set key = key + 1
                set array[key] = this
                set struct[id] = this

                call UnitAddAbility(target, 'Abun')

                if selected then
                    call SelectUnit(target, false)
                endif

                if fxpath != "" and attachment != "" then
                    set effect = AddSpecialEffectTarget(fxpath, target, attachment)
                endif

                if key == 0 then
                    call TimerStart(timer, PERIOD, true, function thistype.onPeriod)
                endif
            endif

            set .duration = duration
            set flag[id] = true
            set x[id] = GetRandomReal(GetUnitX(target) - MAX_CHANGE, GetUnitX(target) + MAX_CHANGE)
            set y[id] = GetRandomReal(GetUnitY(target) - MAX_CHANGE, GetUnitY(target) + MAX_CHANGE)
            call IssuePointOrder(target, "move", x[id], y[id])
        endmethod

        private static method onOrder takes nothing returns nothing
            local unit source = GetOrderedUnit()
            local integer id

            if feared(source) then
                set id = GetUnitUserData(source)

                if not flag[id] then
                    set flag[id] = true
                    call IssuePointOrder(source, "move", x[id], y[id])
                else
                    set flag[id] = false
                endif
            endif

            set source = null
        endmethod

        private static method onSelect takes nothing returns nothing
            local unit source = GetTriggerUnit()
        
            if feared(source) then
                if IsUnitSelected(source, GetOwningPlayer(source)) then
                    call SelectUnit(source, false)
                endif
            endif
            
            set source = null
        endmethod

        private static method onInit takes nothing returns nothing
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function thistype.onOrder)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function thistype.onOrder)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function thistype.onOrder)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER, function thistype.onOrder)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SELECTED, function thistype.onSelect)
        endmethod
    endstruct

    /* -------------------------------------------------------------------------- */
    /*                                 Effect Spam                                */
    /* -------------------------------------------------------------------------- */
    struct EffectSpam
        timer timer
        unit unit 
        integer i 
        string effect
        string point
        real scale
        real x
        real y
        real z

        private static method onPeriod takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())

            if i > 0 then
                if unit == null then
                    call DestroyEffect(AddSpecialEffectEx(effect, x, y, z, scale))
                else
                    call DestroyEffect(AddSpecialEffectTarget(effect, unit, point))
                endif
            else
                call ReleaseTimer(timer)
                call deallocate()
                set timer = null
                set unit = null
            endif
            set i = i - 1
        endmethod

        static method spam takes unit target, string model, string attach, real x, real y, real z, real scale, real interval, integer count returns nothing
            local thistype this = thistype.allocate()

            set timer = NewTimerEx(this)
            set unit = target
            set i = count
            set effect = model
            set .x = x
            set .y = y
            set .z = z
            set .scale = scale
            set point = attach

            call TimerStart(timer, interval, true, function thistype.onPeriod)
        endmethod
    endstruct

    /* -------------------------------------------------------------------------- */
    /*                               Chain Lightning                              */
    /* -------------------------------------------------------------------------- */
    struct ChainLightning
        timer      timer
        unit       unit
        unit       prev
        unit       self
        unit       next
        group      group
        group      damaged
        player     player
        real       damage
        real       range
        real       duration
        integer    bounces
        attacktype attacktype
        damagetype damagetype
        string     lightning
        string     effect
        string     attach
        boolean    rebounce

        private method destroy takes nothing returns nothing
            call DestroyGroup(group)
            call ReleaseTimer(timer)
            call DestroyGroup(damaged)

            set prev       = null
            set self       = null
            set next       = null 
            set unit       = null
            set group      = null
            set timer      = null
            set player     = null
            set damaged    = null
            set attacktype = null
            set damagetype = null

            call deallocate()
        endmethod

        private static method onPeriod takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            
            call DestroyGroup(group)
            if bounces > 0 then
                set group = GetEnemyUnitsInRange(player, GetUnitX(self), GetUnitY(self), range, false, false)
                call GroupRemoveUnit(group, self)
                
                if not rebounce then
                    call BlzGroupRemoveGroupFast(damaged, group)
                endif
                
                if BlzGroupGetSize(group) == 0 then
                    call destroy()
                else
                    set next = GetClosestUnitGroup(GetUnitX(self), GetUnitY(self), group)
                    
                    if next == prev and BlzGroupGetSize(group) > 1 then
                        call GroupRemoveUnit(group, prev)
                        set next = GetClosestUnitGroup(GetUnitX(self), GetUnitY(self), group)
                    endif
                    
                    if next != null then
                        call DestroyLightningTimed(AddLightningEx(lightning, true, GetUnitX(self), GetUnitY(self), GetUnitZ(self) + 60.0, GetUnitX(next), GetUnitY(next), GetUnitZ(next) + 60.0), duration)
                        call DestroyEffect(AddSpecialEffectTarget(effect, next, attach))
                        call GroupAddUnit(damaged, next)
                        call UnitDamageTarget(unit, next, damage, false, false, attacktype, damagetype, null)
                        call DestroyGroup(group)
                        set prev = self
                        set self = next
                        set next = null
                    else
                        call destroy()
                    endif
                endif
            else
                call destroy()
            endif
            set bounces = bounces - 1
        endmethod

        static method create takes unit source, unit target, real dmg, real aoe, real dur, real interval, integer bounceCount, attacktype attackType, damagetype damageType, string lightningType, string sfx, string attachPoint, boolean canRebounce returns thistype
            local group    g
            local thistype this

            set g = GetEnemyUnitsInRange(GetOwningPlayer(source), GetUnitX(target), GetUnitY(target), aoe, false, false)

            if BlzGroupGetSize(g) == 1 then
                call DestroyLightningTimed(AddLightningEx(lightningType, true, GetUnitX(source), GetUnitY(source), BlzGetUnitZ(source) + 60.0, GetUnitX(target), GetUnitY(target), BlzGetUnitZ(target) + 60.0), dur)
                call DestroyEffect(AddSpecialEffectTarget(sfx, target, attachPoint))
                call UnitDamageTarget(source, target, dmg, false, false, attackType, damageType, null)
            else
                set this       = thistype.allocate()
                set timer      = NewTimerEx(this)
                set prev       = null
                set self       = target
                set next       = null
                set unit       = source
                set player     = GetOwningPlayer(source)
                set damage     = dmg
                set range      = aoe
                set duration   = dur
                set bounces    = bounceCount
                set attacktype = attackType
                set damagetype = damageType
                set lightning  = lightningType
                set effect     = sfx
                set attach     = attachPoint
                set rebounce   = canRebounce
                set damaged    = CreateGroup()

                call GroupRemoveUnit(g, target)
                call GroupAddUnit(damaged, target)
                call DestroyEffect(AddSpecialEffectTarget(sfx, target, attachPoint))
                call UnitDamageTarget(source, target, damage, false, false, attacktype, damagetype, null)
                call TimerStart(timer, interval, true, function thistype.onPeriod)
            endif
            call DestroyGroup(g)
            set g = null

            return this
        endmethod
    endstruct

    /* -------------------------------------------------------------------------- */
    /*                                   Knockup                                  */
    /* -------------------------------------------------------------------------- */
    struct Knockup
        timer timer 
        unit  unit
        real  rate

        private static method onPeriod takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())

            call SetUnitFlyHeight(unit, GetUnitDefaultFlyHeight(unit), rate)
            call BlzPauseUnitEx(unit, false)
            call ReleaseTimer(timer)

            set timer = null
            set unit  = null

            call deallocate()
        endmethod

        static method create takes unit whichUnit, real airTime, real maxHeight returns thistype
            local thistype this = thistype.allocate()

            set timer = NewTimerEx(this)
            set unit  = whichUnit
            set rate  = maxHeight/airTime

            call UnitAddAbility(whichUnit, 'Amrf')
            call UnitRemoveAbility(whichUnit, 'Amrf')
            call BlzPauseUnitEx(whichUnit, true)
            call SetUnitFlyHeight(whichUnit, (GetUnitDefaultFlyHeight(whichUnit) + maxHeight), rate)
            
            call TimerStart(timer, airTime/2, false, function thistype.onPeriod)

            return this
        endmethod
    endstruct

    /* -------------------------------------------------------------------------- */
    /*                                 Dummy Pool                                 */
    /* -------------------------------------------------------------------------- */
    struct DummyPool
        private static player player = Player(PLAYER_NEUTRAL_PASSIVE)
        private static group  group  = CreateGroup()

        timer timer
        unit  unit

        static method recycle takes unit dummy returns nothing
            if GetUnitTypeId(dummy) != DUMMY then
                debug call BJDebugMsg("[DummyPool] Error: Trying to recycle a non dummy unit")
            else
                call GroupAddUnit(group, dummy)
                call SetUnitX(dummy, WorldBounds.maxX)
                call SetUnitY(dummy, WorldBounds.maxY)
                call SetUnitOwner(dummy, player, false)
                call ShowUnit(dummy, false)
                call BlzPauseUnitEx(dummy, true)
            endif
        endmethod

        static method retrieve takes player owner, real x, real y, real z, real face returns unit
            if BlzGroupGetSize(group) > 0 then
                set bj_lastCreatedUnit = FirstOfGroup(group)
                call BlzPauseUnitEx(bj_lastCreatedUnit, false)
                call ShowUnit(bj_lastCreatedUnit, true)
                call GroupRemoveUnit(group, bj_lastCreatedUnit)
                call SetUnitX(bj_lastCreatedUnit, x)
                call SetUnitY(bj_lastCreatedUnit, y)
                call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
                call BlzSetUnitFacingEx(bj_lastCreatedUnit, face*bj_RADTODEG)
                call SetUnitOwner(bj_lastCreatedUnit, owner, false)
            else
                set bj_lastCreatedUnit = CreateUnit(owner, DUMMY, x, y, face*bj_RADTODEG)
                call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
            endif

            return bj_lastCreatedUnit
        endmethod

        private static method onExpire takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())

            call recycle(unit)
            call ReleaseTimer(timer)
            
            set timer = null
            set unit  = null

            call deallocate()
        endmethod

        static method recycleTimed takes unit dummy, real delay returns nothing
            local thistype this

            if GetUnitTypeId(dummy) != DUMMY then
                debug call BJDebugMsg("[DummyPool] Error: Trying to recycle a non dummy unit")
            else
                set this = thistype.allocate()

                set timer = NewTimerEx(this)
                set unit  = dummy
                
                call TimerStart(timer, delay, false, function thistype.onExpire)
            endif
        endmethod

        private static method onInit takes nothing returns nothing
            local integer i = 0
            local unit    u

            loop
                exitwhen i == 20
                    set u = CreateUnit(player, DUMMY, WorldBounds.maxX, WorldBounds.maxY, 0)
                    call BlzPauseUnitEx(u, false)
                    call GroupAddUnit(group, u)
                set i = i + 1
            endloop

            set u = null
        endmethod
    endstruct

    /* -------------------------------------------------------------------------- */
    /*                                 Effect Link                                */
    /* -------------------------------------------------------------------------- */
    struct EffectLink
        static timer timer = CreateTimer()
        //Dynamic Indexing for buff and timed
        static integer didx = -1
        static thistype array data
        //Dynamic Indexing for items
        static integer ditem = -1
        static thistype array items

        unit    unit
        effect  effect
        item    item
        integer buff

        method remove takes integer i, boolean isItem returns integer
            call DestroyEffect(effect)

            if isItem then
                set  items[i] = items[ditem]
                set  ditem    = ditem - 1
            else
                set  data[i] = data[didx]
                set  didx    = didx - 1

                if didx == -1 then
                    call PauseTimer(timer)
                endif
            endif

            set unit   = null
            set item   = null
            set effect = null

            call deallocate()

            return i - 1
        endmethod

        static method onDrop takes nothing returns nothing
            local item     j = GetManipulatedItem()
            local integer  i = 0
            local thistype this

            loop
                exitwhen i > ditem
                    set this = items[i]

                    if item == j then
                        set i = remove(i, true)
                    endif
                set i = i + 1
            endloop

            set j = null
        endmethod

        static method onPeriod takes nothing returns nothing
            local integer i = 0
            local thistype this

            loop
                exitwhen i > didx
                    set this = data[i]

                    if GetUnitAbilityLevel(unit, buff) == 0 then
                        set i = remove(i, false)
                    endif
                set i = i + 1
            endloop
        endmethod

        static method BuffLink takes unit target, integer id, string model, string attach returns nothing
            local thistype this = thistype.allocate()

            set unit       = target
            set buff       = id
            set effect     = AddSpecialEffectTarget(model, target, attach)
            set didx       = didx + 1
            set data[didx] = this
            
            if didx == 0 then
                call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
            endif
        endmethod

        static method ItemLink takes unit target, item i, string model, string attach returns nothing
            local thistype this = thistype.allocate()

            set item         = i
            set effect       = AddSpecialEffectTarget(model, target, attach)
            set ditem        = ditem + 1
            set items[ditem] = this
        endmethod

        static method onInit takes nothing returns nothing
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DROP_ITEM, function thistype.onDrop)
        endmethod
    endstruct

    /* -------------------------------------------------------------------------- */
    /*                                   Disarm                                   */
    /* -------------------------------------------------------------------------- */
    struct Disarm
        static constant integer ability = 'Abun'
        static constant real    period  = 0.03125000
        static timer            timer   = CreateTimer()
        static integer          didx    = -1
        static thistype array   data
        static thistype array   n
        readonly static integer array count

        unit    unit
        integer index
        integer ticks

        static method disarmed takes unit target returns boolean
            return GetUnitAbilityLevel(target, ability) > 0
        endmethod

        private method remove takes integer i returns integer
            call apply(unit, false)

            set n[index] = 0
            set unit     = null
            set data[i]  = data[didx]
            set didx     = didx - 1

            if didx == -1 then
                call PauseTimer(timer)
            endif

            call deallocate()

            return i - 1
        endmethod

        private static method onPeriod takes nothing returns nothing
            local integer  i = 0
            local thistype this

            loop
                exitwhen i > didx
                    set this = data[i]

                    if ticks <= 0 then
                        set i = remove(i)
                    endif
                    set ticks = ticks - 1
                set i = i + 1
            endloop
        endmethod

        static method timed takes unit target, real duration returns nothing
            local integer  i = GetUnitUserData(target)
            local thistype this

            if n[i] != 0 then
                set this = n[i]
            else
                set this       = thistype.allocate()
                set unit       = target
                set index      = i
                set didx       = didx + 1
                set data[didx] = this
                set n[i]       = this

                call apply(target, true)

                if didx == 0 then
                    call TimerStart(timer, period, true, function thistype.onPeriod)
                endif
            endif

            set ticks = R2I(duration/period)
        endmethod

        static method apply takes unit target, boolean flag returns nothing
            local integer i = GetUnitUserData(target)
            
            if flag then
                set count[i] = count[i] + 1
                if count[i] > 0 then
                    call UnitAddAbility(target, ability)
                endif
            else
                set count[i] = count[i] - 1
                if count[i] <= 0 then
                    call UnitRemoveAbility(target, ability)
                endif
            endif
        endmethod
    endstruct

    /* -------------------------------------------------------------------------- */
    /*                           Start Ability Cooldown                           */
    /* -------------------------------------------------------------------------- */
    struct AbilityCooldown
        timer   timer
        unit    unit
        integer ability
        real    newCd

        private static method onExpire takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())

            call BlzStartUnitAbilityCooldown(unit, ability, newCd)
            call ReleaseTimer(timer)
            call deallocate()

            set timer = null
            set unit  = null
        endmethod

        static method start takes unit source, integer abilCode, real cooldown returns nothing
            local thistype this = thistype.allocate()

            set timer   = NewTimerEx(this)
            set unit    = source
            set ability = abilCode
            set newCd   = cooldown

            call TimerStart(timer, 0.01, false, function thistype.onExpire)
        endmethod
    endstruct

    /* -------------------------------------------------------------------------- */
    /*                          Remove Destructable Timed                         */
    /* -------------------------------------------------------------------------- */
    struct TimedDestructable
        private static constant real    period = 0.03125000
        private static timer            timer  = CreateTimer()
        private static integer          id    = -1
        private static thistype array   array

        destructable destructable
        real duration

        private method remove takes integer i returns integer
            call RemoveDestructable(destructable)

            set destructable = null
            set array[i] = array[id]
            set id = id - 1

            if id == -1 then
                call PauseTimer(timer)
            endif

            call deallocate()

            return i - 1
        endmethod

        private static method onPeriod takes nothing returns nothing
            local integer  i = 0
            local thistype this

            loop
                exitwhen i > id
                    set this = array[i]

                    if duration <= 0 then
                        set i = remove(i)
                    endif
                    set duration = duration - period
                set i = i + 1
            endloop
        endmethod

        static method create takes destructable dest, real timeout returns thistype
            local thistype this = thistype.allocate()

            set destructable = dest
            set duration     = timeout
            set id           = id + 1
            set array[id]    = this

            if id == 0 then
                call TimerStart(timer, period, true, function thistype.onPeriod)
            endif

            return this
        endmethod
    endstruct

    /* -------------------------------------------------------------------------- */
    /*                                 Timed Pause                                */
    /* -------------------------------------------------------------------------- */
    private struct TimedPause
        static integer array array

        timer timer
        unit unit
        integer key
        boolean flag

        static method onExpire takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())

            set array[key] = array[key] - 1
            if array[key] == 0 then
                call BlzPauseUnitEx(unit, not flag)
            endif
            call ReleaseTimer(timer)
            call deallocate()
            
            set timer = null
            set unit = null
        endmethod


        static method create takes unit u, real duration, boolean pause returns thistype
            local thistype this = thistype.allocate()

            set timer = NewTimerEx(this)
            set unit = u
            set flag = pause
            set key = GetUnitUserData(u)
            
            if array[key] == 0 then
                call BlzPauseUnitEx(u, pause)
            endif
            set array[key] = array[key] + 1
            
            call TimerStart(timer, duration, false, function thistype.onExpire)
            
            return this
        endmethod
    endstruct

    /* -------------------------------------------------------------------------- */
    /*                               Public JASS API                              */
    /* -------------------------------------------------------------------------- */

    // Removes a destructable after a period of time
    function RemoveDestructableTimed takes destructable dest, real timeout returns nothing
        call TimedDestructable.create(dest, timeout)
    endfunction

    // Returns true if a unit is disarmed
    function IsUnitDisarmed takes unit target returns boolean
        return Disarm.disarmed(target)
    endfunction

    // Disarms an unit for a duration
    function DisarmUnitTimed takes unit target, real duration returns nothing
        call Disarm.timed(target, duration)
    endfunction

    // Disarms an unit if flag is true
    function DisarmUnit takes unit target, boolean flag returns nothing
        call Disarm.apply(target, flag)
    endfunction

    // Link an effect to a unit buff or ability
    function LinkEffectToBuff takes unit target, integer buffId, string model, string attach returns nothing
        call EffectLink.BuffLink(target, buffId, model, attach)
    endfunction

    // Link an effect to an unit item.
    function LinkEffectToItem takes unit target, item i, string model, string attach returns nothing
        call EffectLink.ItemLink(target, i, model, attach)
    endfunction

    // Pretty obvious.
    function R2I2S takes real r returns string
        return I2S(R2I(r))
    endfunction

    // Spams the specified effect model at a location with the given interval for the number of times count
    function SpamEffect takes string model, real x, real y, real z, real scale, real interval, integer count returns nothing
        call EffectSpam.spam(null, model, "", x, y, z, scale, interval, count)
    endfunction

    // Spams the specified effect model attached to a unit for the given interval for the number of times count
    function SpamEffectUnit takes unit target, string model, string attach, real interval, integer count returns nothing
        call EffectSpam.spam(target, model, attach, 0, 0, 0, 0, interval, count)
    endfunction

    // Applys Fear to the specified unit for the given duration
    function UnitApplyFear takes unit whichUnit, real duration, string targetEffect, string attchPoint returns nothing
        call Fear.apply(whichUnit, duration, targetEffect, attchPoint)
    endfunction

    // Returns true if the specified unit is currently under the effect of fear effect
    function IsUnitFeared takes unit whichUnit returns boolean
        return Fear.feared(whichUnit)
    endfunction    

    // Add the specified ability to the specified unit for the given duration. Use hide to show or not the ability button.
    function UnitAddAbilityTimed takes unit whichUnit, integer abilityId, real duration, integer level, boolean hide returns nothing
        call TimedAbility.add(whichUnit, abilityId, duration, level, hide)
    endfunction

    // Resets the specified unit ability cooldown
    function ResetUnitAbilityCooldown takes unit whichUnit, integer abilCode returns nothing
        call ResetCooldown.reset(whichUnit, abilCode)
    endfunction 

    // Knockback the target unit given the angle (Rad) and a distance. Set model and point to attach an effect. Set onCliff to true to make the knockback stop when hitting a cliff, same for the others.
    function KnockbackUnit takes unit whichUnit, real angle, real distance, real duration, string model, string point, boolean onCliff, boolean onDestructable, boolean onUnit, boolean pause returns nothing
        call Knockback.start(whichUnit, angle, distance, duration, model, point, onCliff, onDestructable, onUnit, pause)
    endfunction
    
    // Returns true if a unit is currently being knocked back
    function IsUnitKnockedBack takes unit whichUnit returns boolean
        return Knockback.isUnitKnocked(whichUnit)
    endfunction

    // Returns the distance between 2 coordinates in Warcraft III units
    function DistanceBetweenCoordinates takes real x1, real y1, real x2, real y2 returns real
        local real dx = (x2 - x1)
        local real dy = (y2 - y1)
    
        return SquareRoot(dx*dx + dy*dy)
    endfunction

    // Makes the specified source damage an area respecting some basic unit filters
    function UnitDamageArea takes unit source, real x, real y, real aoe, real damage, attacktype atkType, damagetype dmgType, boolean structures, boolean magicImmune, boolean allies returns nothing
        local group  h       = CreateGroup()
        local player enemyOf = GetOwningPlayer(source)
        local unit   w
        
        call GroupEnumUnitsInRange(h, x, y, aoe, null)
        if allies then
            if structures and magicImmune then
                loop
                    set w = FirstOfGroup(h)
                    exitwhen w == null
                        if (UnitAlive(w) and w != source) then
                            call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
                        endif
                    call GroupRemoveUnit(h, w)
                endloop
            elseif structures and not magicImmune then
                loop
                    set w = FirstOfGroup(h)
                    exitwhen w == null
                        if (UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE) and w != source) then
                            call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
                        endif
                    call GroupRemoveUnit(h, w)
                endloop
            elseif magicImmune and not structures then
                loop
                    set w = FirstOfGroup(h)
                    exitwhen w == null
                        if (UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE) and w != source) then
                            call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
                        endif
                    call GroupRemoveUnit(h, w)
                endloop
            else
                loop
                    set w = FirstOfGroup(h)
                    exitwhen w == null
                        if (UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE) and w != source) then
                            call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
                        endif
                    call GroupRemoveUnit(h, w)
                endloop
            endif
        else
            if structures and magicImmune then
                loop
                    set w = FirstOfGroup(h)
                    exitwhen w == null
                        if (IsUnitEnemy(w, enemyOf) and UnitAlive(w)) then
                            call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
                        endif
                    call GroupRemoveUnit(h, w)
                endloop
            elseif structures and not magicImmune then
                loop
                    set w = FirstOfGroup(h)
                    exitwhen w == null
                        if (IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE)) then
                            call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
                        endif
                    call GroupRemoveUnit(h, w)
                endloop
            elseif magicImmune and not structures then
                loop
                    set w = FirstOfGroup(h)
                    exitwhen w == null
                        if (IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE)) then
                            call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
                        endif
                    call GroupRemoveUnit(h, w)
                endloop
            else
                loop
                    set w = FirstOfGroup(h)
                    exitwhen w == null
                        if (IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE)) then
                            call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
                        endif
                    call GroupRemoveUnit(h, w)
                endloop
            endif
        endif
        call DestroyGroup(h)
    
        set h       = null
        set enemyOf = null
    endfunction

    // Makes the specified source damage a group. Creates a special effect if specified
    function UnitDamageGroup takes unit u, group g, real damage, attacktype atk_type, damagetype dmg_type, string sfx, string atch_point, boolean destroy returns group
        local unit    v
        local integer i = 0
        local integer t = BlzGroupGetSize(g)

        loop
            exitwhen i == t
                set v = BlzGroupUnitAt(g, i)
                call UnitDamageTarget(u, v, damage, true, false, atk_type, dmg_type, null)

                if sfx != "" and atch_point != "" then
                    call DestroyEffect(AddSpecialEffectTarget(sfx, v, atch_point))
                endif
            set i = i + 1
        endloop

        if destroy then
            call DestroyGroup(g)
        endif

        return g
    endfunction

    // Returns a random range given a max value
    function GetRandomRange takes real radius returns real
        local real r = GetRandomReal(0, 1) + GetRandomReal(0, 1)

        if r > 1 then
            return (2 - r)*radius
        endif

        return r*radius
    endfunction

    // Returns a random value in the x/y coordinates depending on the value of boolean x
    function GetRandomCoordInRange takes real center, real radius, boolean x returns real
        local real theta = 2*bj_PI*GetRandomReal(0, 1)
        local real r

        if x then
            set r = center + radius*Cos(theta)
        else
            set r = center + radius*Sin(theta)
        endif

        return r
    endfunction

    // Clones the items in the source unit inventory to the target unit
    function CloneItems takes unit source, unit target returns nothing
        local integer i = 0
        local integer j
        local item k
        
        loop
            exitwhen i > bj_MAX_INVENTORY
                set k = UnitItemInSlot(source, i)
                set j = GetItemCharges(k)
                set k = CreateItem(GetItemTypeId(k), GetUnitX(target), GetUnitY(target))
                call SetItemCharges(k, j)
                call UnitAddItem(target, k)
            set i = i + 1
        endloop
        
        set k = null
    endfunction

    // Add the mount for he unit mana pool
    function AddUnitMana takes unit whichUnit, real amount returns nothing
        call SetUnitState(whichUnit, UNIT_STATE_MANA, (GetUnitState(whichUnit, UNIT_STATE_MANA) + amount))
    endfunction

    // Add the specified amounts to a hero str/agi/int base amount
    function UnitAddStat takes unit whichUnit, integer strength, integer agility, integer intelligence returns nothing
        if strength != 0 then
            call SetHeroStr(whichUnit, GetHeroStr(whichUnit, false) + strength, true)
        endif
    
        if agility != 0 then
            call SetHeroAgi(whichUnit, GetHeroAgi(whichUnit, false) + agility, true)
        endif
    
        if intelligence != 0 then
            call SetHeroInt(whichUnit, GetHeroInt(whichUnit, false) + intelligence, true)
        endif
    endfunction

    // Returns the closest unit from the x and y coordinates in the map
    function GetClosestUnit takes real x, real y, boolexpr e returns unit
        local real  md = 100000
        local group g = CreateGroup()
        local unit  u
        local real  dx
        local real  dy

        set bj_closestUnitGroup = null
        
        call GroupEnumUnitsInRect(g, bj_mapInitialPlayableArea, e)
        loop
            set u = FirstOfGroup(g)
            exitwhen u == null
                if UnitAlive(u) then
                    set dx = GetUnitX(u) - x
                    set dy = GetUnitY(u) - y
                    
                    if (dx*dx + dy*dy)/100000 < md then
                        set bj_closestUnitGroup = u
                        set md = (dx*dx + dy*dy)/100000
                    endif
                endif
            call GroupRemoveUnit(g, u)
        endloop
        call DestroyGroup(g)
        call DestroyBoolExpr(e)
        set g = null

        return bj_closestUnitGroup
    endfunction
    
    // Creates a chain lightning with the specified ligihtning effect with the amount of bounces
    function CreateChainLightning takes unit source, unit target, real damage, real aoe, real duration, real interval, integer bounceCount, attacktype attackType, damagetype damageType, string lightningType, string sfx, string attachPoint, boolean canRebounce returns nothing
        call ChainLightning.create(source, target, damage, aoe, duration, interval, bounceCount, attackType, damageType, lightningType, sfx, attachPoint, canRebounce)
    endfunction

    // Add the specified amount to the specified player gold amount
    function AddPlayerGold takes player whichPlayer, integer amount returns nothing
        call SetPlayerState(whichPlayer, PLAYER_STATE_RESOURCE_GOLD, GetPlayerState(whichPlayer, PLAYER_STATE_RESOURCE_GOLD) + amount)
    endfunction

    // Unit Knockup
    function KnockupUnit takes unit whichUnit, real airTime, real maxHeight returns nothing
        call Knockup.create(whichUnit, airTime, maxHeight)
    endfunction

    // Creates a text tag in an unit position for a duration
    function CreateTextOnUnit takes unit whichUnit, string text, real duration, integer red, integer green, integer blue, integer alpha returns nothing
        local texttag tx = CreateTextTag()
        
        call SetTextTagText(tx, text, 0.015)
        call SetTextTagPosUnit(tx, whichUnit, 0)
        call SetTextTagColor(tx, red, green, blue, alpha)
        call SetTextTagLifespan(tx, duration)
        call SetTextTagVelocity(tx, 0.0, 0.0355)
        call SetTextTagPermanent(tx, false)
        
        set tx = null
    endfunction

    // Add health regeneration to the unit base value
    function UnitAddHealthRegen takes unit whichUnit, real regen returns nothing
        call BlzSetUnitRealField(whichUnit, UNIT_RF_HIT_POINTS_REGENERATION_RATE, BlzGetUnitRealField(whichUnit, UNIT_RF_HIT_POINTS_REGENERATION_RATE) + regen)
    endfunction

    // Retrieves a dummy from the pool. Facing angle in radians
    function DummyRetrieve takes player owner, real x, real y, real z, real face returns unit
        return DummyPool.retrieve(owner, x, y, z, face)
    endfunction

    // Recycles a dummy unit type, putting it back into the pool.
    function DummyRecycle takes unit dummy returns nothing
        call DummyPool.recycle(dummy)
    endfunction

    // Recycles a dummy with a delay.
    function DummyRecycleTimed takes unit dummy, real delay returns nothing
        call DummyPool.recycleTimed(dummy, delay)
    endfunction

    // Casts an ability in the target unit. Must have no casting time
    function CastAbilityTarget takes unit target, integer id, string order, integer level returns nothing
        local unit dummy = DummyRetrieve(GetOwningPlayer(target), 0, 0, 0, 0)
        
        call UnitAddAbility(dummy, id)
        call SetUnitAbilityLevel(dummy, id, level)
        call IssueTargetOrder(dummy, order, target)
        call UnitRemoveAbility(dummy, id)
        call DummyRecycle(dummy)

        set dummy = null
    endfunction

    // Silences the specified unit for the given duration
    function SilenceUnit takes unit whichUnit, real duration returns nothing
        local unit dummy = DummyRetrieve(Player(PLAYER_NEUTRAL_PASSIVE), GetUnitX(whichUnit), GetUnitY(whichUnit), GetUnitFlyHeight(whichUnit), 0)
        local ability a

        
        call UnitAddAbility(dummy, SILENCE)
        set a = BlzGetUnitAbility(dummy, SILENCE)
        call BlzSetAbilityRealLevelField(a, ABILITY_RLF_DURATION_NORMAL, 0, duration)
        call BlzSetAbilityRealLevelField(a, ABILITY_RLF_DURATION_HERO, 0, duration)
        call IncUnitAbilityLevel(dummy, SILENCE)
        call DecUnitAbilityLevel(dummy, SILENCE)
        call IssueTargetOrder(dummy, "drunkenhaze", whichUnit)
        call UnitRemoveAbility(dummy, SILENCE)
        call DummyRecycle(dummy)

        set dummy = null
        set a = null
    endfunction

    // Silences a group of units for the given duration
    function SilenceGroup takes group whichGroup, real duration returns nothing
        local integer i = 0
        local integer size = BlzGroupGetSize(whichGroup)
        local unit u
        local unit dummy
        local ability a

        if size > 0 then
            set u = BlzGroupUnitAt(whichGroup, i)
            set dummy = DummyRetrieve(Player(PLAYER_NEUTRAL_PASSIVE), GetUnitX(u), GetUnitY(u), GetUnitFlyHeight(u), 0)
            
            call UnitAddAbility(dummy, SILENCE)
            set a = BlzGetUnitAbility(dummy, SILENCE)
            call BlzSetAbilityRealLevelField(a, ABILITY_RLF_DURATION_NORMAL, 0, duration)
            call BlzSetAbilityRealLevelField(a, ABILITY_RLF_DURATION_HERO, 0, duration)
            call IncUnitAbilityLevel(dummy, SILENCE)
            call DecUnitAbilityLevel(dummy, SILENCE)

            loop
                exitwhen i == size
                    set u = BlzGroupUnitAt(whichGroup, i)
                    if UnitAlive(u) then
                        call IssueTargetOrder(dummy, "drunkenhaze", u)
                    endif
                set i = i + 1
            endloop
            call UnitRemoveAbility(dummy, SILENCE)
            call DummyRecycle(dummy)
        endif

        set u = null
        set a = null
        set dummy = null
    endfunction

    // Silences all units within the specified AOE with the center at x and y for the given duration
    function SilenceArea takes real x, real y, real aoe, real duration returns nothing
        local group g = CreateGroup()

        call GroupEnumUnitsInRange(g, x, y, aoe, null)
        call SilenceGroup(g, duration)
        call DestroyGroup(g)

        set g = null
    endfunction

    // Stuns the specified unit for the given duration
    function StunUnit takes unit whichUnit, real duration returns nothing
        local unit dummy = DummyRetrieve(Player(PLAYER_NEUTRAL_PASSIVE), GetUnitX(whichUnit), GetUnitY(whichUnit), GetUnitFlyHeight(whichUnit), 0)
        local ability a

        call UnitAddAbility(dummy, STUN)
        set a = BlzGetUnitAbility(dummy, STUN)
        call BlzSetAbilityRealLevelField(a, ABILITY_RLF_DURATION_NORMAL, 0, duration)
        call BlzSetAbilityRealLevelField(a, ABILITY_RLF_DURATION_HERO, 0, duration)
        call IncUnitAbilityLevel(dummy, STUN)
        call DecUnitAbilityLevel(dummy, STUN)
        call IssueTargetOrder(dummy, "thunderbolt", whichUnit)
        call UnitRemoveAbility(dummy, STUN)
        call DummyRecycle(dummy)
        
        set dummy = null
        set a = null
    endfunction

    // Stuns a group of units for the given duration
    function StunGroup takes group whichGroup, real duration returns nothing
        local integer i = 0
        local integer size = BlzGroupGetSize(whichGroup)
        local unit u
        local unit dummy
        local ability a

        if size > 0 then
            set u = BlzGroupUnitAt(whichGroup, i)
            set dummy = DummyRetrieve(Player(PLAYER_NEUTRAL_PASSIVE), GetUnitX(u), GetUnitY(u), GetUnitFlyHeight(u), 0)
            
            call UnitAddAbility(dummy, STUN)
            set a = BlzGetUnitAbility(dummy, STUN)
            call BlzSetAbilityRealLevelField(a, ABILITY_RLF_DURATION_NORMAL, 0, duration)
            call BlzSetAbilityRealLevelField(a, ABILITY_RLF_DURATION_HERO, 0, duration)
            call IncUnitAbilityLevel(dummy, STUN)
            call DecUnitAbilityLevel(dummy, STUN)

            loop
                exitwhen i == size
                    set u = BlzGroupUnitAt(whichGroup, i)
                    if UnitAlive(u) then
                        call IssueTargetOrder(dummy, "thunderbolt", u)
                    endif
                set i = i + 1
            endloop
            call UnitRemoveAbility(dummy, STUN)
            call DummyRecycle(dummy)
        endif

        set u = null
        set a = null
        set dummy = null
    endfunction

    // Stuns all units within the specified AOE with the center at x and y for the given duration
    function StunArea takes real x, real y, real aoe, real duration returns nothing
        local group g = CreateGroup()

        call GroupEnumUnitsInRange(g, x, y, aoe, null)
        call StunGroup(g, duration)
        call DestroyGroup(g)

        set g = null
    endfunction

    // Slows the specified unit for the given duration
    function SlowUnit takes unit whichUnit, real amount, real duration returns nothing
        local unit dummy = DummyRetrieve(Player(PLAYER_NEUTRAL_PASSIVE), GetUnitX(whichUnit), GetUnitY(whichUnit), GetUnitFlyHeight(whichUnit), 0)
        local ability a

        call UnitAddAbility(dummy, SLOW)
        set a = BlzGetUnitAbility(dummy, SLOW)
        call BlzSetAbilityRealLevelField(a, ABILITY_RLF_DURATION_NORMAL, 0, duration)
        call BlzSetAbilityRealLevelField(a, ABILITY_RLF_DURATION_HERO, 0, duration)
        call BlzSetAbilityRealLevelField(a, ABILITY_RLF_MOVEMENT_SPEED_REDUCTION_PERCENT_CRI1, 0, amount)
        call IncUnitAbilityLevel(dummy, SLOW)
        call DecUnitAbilityLevel(dummy, SLOW)
        call IssueTargetOrder(dummy, "cripple", whichUnit)
        call UnitRemoveAbility(dummy, SLOW)
        call DummyRecycle(dummy)

        set dummy = null
        set a = null
    endfunction

    // Slows a group of units for the given duration
    function SlowGroup takes group whichGroup, real amount, real duration returns nothing
        local integer i = 0
        local integer size = BlzGroupGetSize(whichGroup)
        local unit u
        local unit dummy
        local ability a

        if size > 0 then
            set u = BlzGroupUnitAt(whichGroup, i)
            set dummy = DummyRetrieve(Player(PLAYER_NEUTRAL_PASSIVE), GetUnitX(u), GetUnitY(u), GetUnitFlyHeight(u), 0)
            
            call UnitAddAbility(dummy, SLOW)
            set a = BlzGetUnitAbility(dummy, SLOW)
            call BlzSetAbilityRealLevelField(a, ABILITY_RLF_DURATION_NORMAL, 0, duration)
            call BlzSetAbilityRealLevelField(a, ABILITY_RLF_DURATION_HERO, 0, duration)
            call BlzSetAbilityRealLevelField(a, ABILITY_RLF_MOVEMENT_SPEED_FACTOR_SLO1, 0, amount)
            call IncUnitAbilityLevel(dummy, SLOW)
            call DecUnitAbilityLevel(dummy, SLOW)

            loop
                exitwhen i == size
                    set u = BlzGroupUnitAt(whichGroup, i)
                    if UnitAlive(u) then
                        call IssueTargetOrder(dummy, "slow", u)
                    endif
                set i = i + 1
            endloop
            call UnitRemoveAbility(dummy, SLOW)
            call DummyRecycle(dummy)
        endif

        set u = null
        set a = null
        set dummy = null
    endfunction

    // Slows all units within the specified AOE with the center at x and y for the given duration
    function SlowArea takes real x, real y, real aoe, real amount, real duration returns nothing
        local group g = CreateGroup()

        call GroupEnumUnitsInRange(g, x, y, aoe, null)
        call SlowGroup(g, amount, duration)
        call DestroyGroup(g)

        set g = null
    endfunction

    // Returns a random unit within a group
    function GroupPickRandomUnitEx takes group g returns unit
        if BlzGroupGetSize(g) > 0 then
            return BlzGroupUnitAt(g, GetRandomInt(0, BlzGroupGetSize(g) - 1))
        else
            return null
        endif
    endfunction

    // Returns true if a unit is within a cone given a facing and fov angle in degrees (Less precise)
    function IsUnitInConeEx takes unit u, real x, real y, real face, real fov returns boolean
        return Acos(Cos((Atan2(GetUnitY(u) - y, GetUnitX(u) - x)) - face*bj_DEGTORAD)) < fov*bj_DEGTORAD/2
    endfunction

    // Returns true if a unit is within a cone given a facing, fov angle and a range in degrees (takes collision into consideration). Credits to AGD.
    function IsUnitInCone takes unit u, real x, real y, real range, real face, real fov returns boolean
        if IsUnitInRangeXY(u, x, y, range) then
            set x = GetUnitX(u) - x
            set y = GetUnitY(u) - y
            set range = x*x + y*y
    
            if range > 0. then
                set face = face*bj_DEGTORAD - Atan2(y, x)
                set fov = fov*bj_DEGTORAD/2 + Asin(BlzGetUnitCollisionSize(u)/SquareRoot(range))
    
                return RAbsBJ(face) <= fov or RAbsBJ(face - 2.00*bj_PI) <= fov
            endif
    
            return true
        endif
    
        return false
    endfunction

    // Makes the source unit damage enemy unit in a cone given a direction, foy and range
    function UnitDamageCone takes unit source, real x, real y, real face, real fov, real aoe, real damage, attacktype atkType, damagetype dmgType, boolean structures, boolean magicImmune, boolean allies returns nothing
        local group  h       = CreateGroup()
        local player enemyOf = GetOwningPlayer(source)
        local unit   w
        
        call GroupEnumUnitsInRange(h, x, y, aoe, null)
        if allies then
            if structures and magicImmune then
                loop
                    set w = FirstOfGroup(h)
                    exitwhen w == null
                        if (UnitAlive(w) and w != source and IsUnitInCone(w, x, y, aoe, face, fov)) then
                            call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
                        endif
                    call GroupRemoveUnit(h, w)
                endloop
            elseif structures and not magicImmune then
                loop
                    set w = FirstOfGroup(h)
                    exitwhen w == null
                        if (UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE) and w != source and IsUnitInCone(w, x, y, aoe, face, fov)) then
                            call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
                        endif
                    call GroupRemoveUnit(h, w)
                endloop
            elseif magicImmune and not structures then
                loop
                    set w = FirstOfGroup(h)
                    exitwhen w == null
                        if (UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE) and w != source and IsUnitInCone(w, x, y, aoe, face, fov)) then
                            call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
                        endif
                    call GroupRemoveUnit(h, w)
                endloop
            else
                loop
                    set w = FirstOfGroup(h)
                    exitwhen w == null
                        if (UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE) and w != source and IsUnitInCone(w, x, y, aoe, face, fov)) then
                            call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
                        endif
                    call GroupRemoveUnit(h, w)
                endloop
            endif
        else
            if structures and magicImmune then
                loop
                    set w = FirstOfGroup(h)
                    exitwhen w == null
                        if (IsUnitEnemy(w, enemyOf) and UnitAlive(w) and IsUnitInCone(w, x, y, aoe, face, fov)) then
                            call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
                        endif
                    call GroupRemoveUnit(h, w)
                endloop
            elseif structures and not magicImmune then
                loop
                    set w = FirstOfGroup(h)
                    exitwhen w == null
                        if (IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE) and IsUnitInCone(w, x, y, aoe, face, fov)) then
                            call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
                        endif
                    call GroupRemoveUnit(h, w)
                endloop
            elseif magicImmune and not structures then
                loop
                    set w = FirstOfGroup(h)
                    exitwhen w == null
                        if (IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE) and IsUnitInCone(w, x, y, aoe, face, fov)) then
                            call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
                        endif
                    call GroupRemoveUnit(h, w)
                endloop
            else
                loop
                    set w = FirstOfGroup(h)
                    exitwhen w == null
                        if (IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE) and IsUnitInCone(w, x, y, aoe, face, fov)) then
                            call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
                        endif
                    call GroupRemoveUnit(h, w)
                endloop
            endif
        endif
        call DestroyGroup(h)
    
        set h       = null
        set enemyOf = null
    endfunction

    // Heals all allied units of specified player in an area
    function HealArea takes player alliesOf, real x, real y, real aoe, real amount, string fxpath, string attchPoint returns nothing
        local group g = CreateGroup()
        local unit v
        
        call GroupEnumUnitsInRange(g, x, y, aoe, null)
        loop
            set v = FirstOfGroup(g)
            exitwhen v == null
                if IsUnitAlly(v, alliesOf) and UnitAlive(v) and not IsUnitType(v, UNIT_TYPE_STRUCTURE) then
                    call SetWidgetLife(v, GetWidgetLife(v) + amount)
                    if fxpath != "" then
                        if attchPoint != "" then
                            call DestroyEffect(AddSpecialEffectTarget(fxpath, v, attchPoint))
                        else
                            call DestroyEffect(AddSpecialEffect(fxpath, GetUnitX(v), GetUnitY(v)))
                        endif
                    endif
                endif
            call GroupRemoveUnit(g, v)
        endloop
        call DestroyGroup(g)
    
        set g = null
    endfunction

    // Returns an ability real level field as a string. Usefull for toolltip manipulation.
    function AbilityRealField takes unit u, integer abilityId, abilityreallevelfield field, integer level, integer multiplier, boolean asInteger returns string
        if asInteger then
            return R2I2S(BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, abilityId), field, level)*multiplier)
        else
            return R2SW(BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, abilityId), field, level)*multiplier,1,1)
        endif
    endfunction

    // Fix for camera pan desync. credits do Daffa
    function SmartCameraPanBJModified takes player whichPlayer, location loc, real duration returns nothing 
        local real tx = GetLocationX(loc)
        local real ty = GetLocationY(loc)
        local real dx = tx - GetCameraTargetPositionX()
        local real dy = ty - GetCameraTargetPositionY()
        local real dist = SquareRoot(dx * dx + dy * dy)

        if (GetLocalPlayer() == whichPlayer) then
            if (dist >= bj_SMARTPAN_TRESHOLD_SNAP) then
                call PanCameraToTimed(tx, ty, duration)
                // Far away = snap camera immediately to point
            elseif (dist >= bj_SMARTPAN_TRESHOLD_PAN) then
                call PanCameraToTimed(tx, ty, duration)
                // Moderately close = pan camera over duration
            else
                // User is close, don't move camera
            endif
        endif
    endfunction
    
    // Fix for camera pan desync. credits do Daffa
    function SmartCameraPanBJModifiedXY takes player whichPlayer, real x, real y, real duration returns nothing 
        local real dx = x - GetCameraTargetPositionX()
        local real dy = y - GetCameraTargetPositionY()
        local real dist = SquareRoot(dx * dx + dy * dy)

        if (GetLocalPlayer() == whichPlayer) then
            if (dist >= bj_SMARTPAN_TRESHOLD_SNAP) then
                call PanCameraToTimed(x, y, duration)
                // Far away = snap camera immediately to point
            elseif (dist >= bj_SMARTPAN_TRESHOLD_PAN) then
                call PanCameraToTimed(x, y, duration)
                // Moderately close = pan camera over duration
            else
                // User is close, don't move camera
            endif
        endif
    endfunction

    // Start the cooldown for the source unit unit the new value
    function StartUnitAbilityCooldown takes unit source, integer abilCode, real cooldown returns nothing
        call AbilityCooldown.start(source, abilCode, cooldown)
    endfunction
    
    // Pauses or Unpauses a unit after a delay. If flag = true than the unit will be paused and unpaused after the duration. If flag = false than the unit will be unpaused and paused after the duration.
    function PauseUnitTimed takes unit u, real duration, boolean flag returns nothing
        call TimedPause.create(u, duration, flag)
    endfunction
endlibrary
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,544
Here it is.
vJASS:
 // Returns a group of enemy units of the specified player within the specified AOE of x and y
    function GetEnemyUnitsInRange takes player enemyOf, real x, real y, real aoe, boolean structures, boolean magicImmune returns group
        local group g = CreateGroup()
        local group h = CreateGroup()
        local unit  w
       
        call GroupEnumUnitsInRange(h, x, y, aoe, null)
        if structures and magicImmune then
            loop
                set w = FirstOfGroup(h)
                exitwhen w == null
                    if IsUnitEnemy(w, enemyOf) and UnitAlive(w) then
                        call GroupAddUnit(g, w)
                    endif
                call GroupRemoveUnit(h, w)
            endloop
        elseif structures and not magicImmune then
            loop
                set w = FirstOfGroup(h)
                exitwhen w == null
                    if IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE) then
                        call GroupAddUnit(g, w)
                    endif
                call GroupRemoveUnit(h, w)
            endloop
        elseif magicImmune and not structures then
            loop
                set w = FirstOfGroup(h)
                exitwhen w == null
                    if IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE) then
                        call GroupAddUnit(g, w)
                    endif
                call GroupRemoveUnit(h, w)
            endloop
        else
            loop
                set w = FirstOfGroup(h)
                exitwhen w == null
                    if IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE) then
                        call GroupAddUnit(g, w)
                    endif
                call GroupRemoveUnit(h, w)
            endloop
        endif
        call DestroyGroup(h)
   
        set    h = null
        return g
    endfunction

So the first boolean determines whether or not it targets Structures and the second boolean determines whether or not it targets Magic Immune units. I don't see anything related to Neutral Hostile so are you sure that the unit being ignored isn't a Structure or Magic Immune?
 
Level 14
Joined
Jul 19, 2007
Messages
772
Here it is.
vJASS:
 // Returns a group of enemy units of the specified player within the specified AOE of x and y
    function GetEnemyUnitsInRange takes player enemyOf, real x, real y, real aoe, boolean structures, boolean magicImmune returns group
        local group g = CreateGroup()
        local group h = CreateGroup()
        local unit  w
       
        call GroupEnumUnitsInRange(h, x, y, aoe, null)
        if structures and magicImmune then
            loop
                set w = FirstOfGroup(h)
                exitwhen w == null
                    if IsUnitEnemy(w, enemyOf) and UnitAlive(w) then
                        call GroupAddUnit(g, w)
                    endif
                call GroupRemoveUnit(h, w)
            endloop
        elseif structures and not magicImmune then
            loop
                set w = FirstOfGroup(h)
                exitwhen w == null
                    if IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE) then
                        call GroupAddUnit(g, w)
                    endif
                call GroupRemoveUnit(h, w)
            endloop
        elseif magicImmune and not structures then
            loop
                set w = FirstOfGroup(h)
                exitwhen w == null
                    if IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE) then
                        call GroupAddUnit(g, w)
                    endif
                call GroupRemoveUnit(h, w)
            endloop
        else
            loop
                set w = FirstOfGroup(h)
                exitwhen w == null
                    if IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE) then
                        call GroupAddUnit(g, w)
                    endif
                call GroupRemoveUnit(h, w)
            endloop
        endif
        call DestroyGroup(h)
   
        set    h = null
        return g
    endfunction

So the first boolean determines whether or not it targets Structures and the second boolean determines whether or not targets Magic Immune units. I don't see anything related to Neutral Hostile so are you sure that the unit being ignored isn't a Structure or Magic Immune?
Yes I'm 100% sure. This is really strange...
 
Level 19
Joined
Feb 27, 2019
Messages
590
Can you post the function RegisterPlayerUnitEvent from your maps code? Also which version are you making the map on?
JASS:
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.onDeath)
 
Level 14
Joined
Jul 19, 2007
Messages
772
Can you post the function RegisterPlayerUnitEvent from your maps code? Also which version are you making the map on?
JASS:
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.onDeath)
I'm using version 1.35 right now.

JASS:
/**************************************************************
*
*   RegisterPlayerUnitEvent
*   v5.1.0.1
*   By Magtheridon96
*
*   I would like to give a special thanks to Bribe, azlier
*   and BBQ for improving this library. For modularity, it only 
*   supports player unit events.
*
*   Functions passed to RegisterPlayerUnitEvent must either
*   return a boolean (false) or nothing. (Which is a Pro)
*
*   Warning:
*   --------
*
*       - Don't use TriggerSleepAction inside registered code.
*       - Don't destroy a trigger unless you really know what you're doing.
*
*   API:
*   ----
*
*       - function RegisterPlayerUnitEvent takes playerunitevent whichEvent, code whichFunction returns nothing
*           - Registers code that will execute when an event fires.
*       - function RegisterPlayerUnitEventForPlayer takes playerunitevent whichEvent, code whichFunction, player whichPlayer returns nothing
*           - Registers code that will execute when an event fires for a certain player.
*       - function GetPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
*           - Returns the trigger corresponding to ALL functions of a playerunitevent.
*
**************************************************************/
library RegisterPlayerUnitEvent // Special Thanks to Bribe and azlier
    globals
        private trigger array t
    endglobals
    
    function RegisterPlayerUnitEvent takes playerunitevent p, code c returns nothing
        local integer i = GetHandleId(p)
        local integer k = 15
        if t[i] == null then
            set t[i] = CreateTrigger()
            loop
                call TriggerRegisterPlayerUnitEvent(t[i], Player(k), p, null)
                exitwhen k == 0
                set k = k - 1
            endloop
        endif
        call TriggerAddCondition(t[i], Filter(c))
    endfunction
    
    function RegisterPlayerUnitEventForPlayer takes playerunitevent p, code c, player pl returns nothing
        local integer i = 16 * GetHandleId(p) + GetPlayerId(pl)
        if t[i] == null then
            set t[i] = CreateTrigger()
            call TriggerRegisterPlayerUnitEvent(t[i], pl, p, null)
        endif
        call TriggerAddCondition(t[i], Filter(c))
    endfunction
    
    function GetPlayerUnitEventTrigger takes playerunitevent p returns trigger
        return t[GetHandleId(p)]
    endfunction
endlibrary
 
Level 19
Joined
Feb 27, 2019
Messages
590
I'm using version 1.35 right now.

JASS:
/**************************************************************
*
*   RegisterPlayerUnitEvent
*   v5.1.0.1
*   By Magtheridon96
*
*   I would like to give a special thanks to Bribe, azlier
*   and BBQ for improving this library. For modularity, it only
*   supports player unit events.
*
*   Functions passed to RegisterPlayerUnitEvent must either
*   return a boolean (false) or nothing. (Which is a Pro)
*
*   Warning:
*   --------
*
*       - Don't use TriggerSleepAction inside registered code.
*       - Don't destroy a trigger unless you really know what you're doing.
*
*   API:
*   ----
*
*       - function RegisterPlayerUnitEvent takes playerunitevent whichEvent, code whichFunction returns nothing
*           - Registers code that will execute when an event fires.
*       - function RegisterPlayerUnitEventForPlayer takes playerunitevent whichEvent, code whichFunction, player whichPlayer returns nothing
*           - Registers code that will execute when an event fires for a certain player.
*       - function GetPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
*           - Returns the trigger corresponding to ALL functions of a playerunitevent.
*
**************************************************************/
library RegisterPlayerUnitEvent // Special Thanks to Bribe and azlier
    globals
        private trigger array t
    endglobals
  
    function RegisterPlayerUnitEvent takes playerunitevent p, code c returns nothing
        local integer i = GetHandleId(p)
        local integer k = 15
        if t[i] == null then
            set t[i] = CreateTrigger()
            loop
                call TriggerRegisterPlayerUnitEvent(t[i], Player(k), p, null)
                exitwhen k == 0
                set k = k - 1
            endloop
        endif
        call TriggerAddCondition(t[i], Filter(c))
    endfunction
  
    function RegisterPlayerUnitEventForPlayer takes playerunitevent p, code c, player pl returns nothing
        local integer i = 16 * GetHandleId(p) + GetPlayerId(pl)
        if t[i] == null then
            set t[i] = CreateTrigger()
            call TriggerRegisterPlayerUnitEvent(t[i], pl, p, null)
        endif
        call TriggerAddCondition(t[i], Filter(c))
    endfunction
  
    function GetPlayerUnitEventTrigger takes playerunitevent p returns trigger
        return t[GetHandleId(p)]
    endfunction
endlibrary
In jass, player 0 is equal to GUI player 1 (red). For a long time there were 12 player slots (0-11 in jass) and a few extra slots for the neutral players (12-15). Then 12 new player slots were added, meaning there was a total of 28 players if you include the neutral players. The neutral players slot positions were moved from 12-15 to 24-27. Try doing what pred1980 says here [Snippet] RegisterPlayerUnitEvent and see if that fixes the problem.
 
Last edited:
Level 14
Joined
Jul 19, 2007
Messages
772
In jass, player 0 is equal to GUI player 1 (red). For a long time there were 12 player slots (0-11 in jass) and a few extra slots for the neutral players (12-15). Then 12 new player slots were added, meaning there was a total of 28 players if you include the neutral players. The neutral players slot positions were moved from 12-15 to 24-27. Try doing what pred1980 says here [Snippet] RegisterPlayerUnitEvent and see if that fixes the problem.
Yes it fixed the problem, it will now bounce on neutrals too, thank you!!!
 
Status
Not open for further replies.
Top