• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[JASS] Volley v1.0e

  • Like
Reactions: Lambdadelta
A spell based on Ashe the Frost Archer's Volley spell.

VOLLEY

Ashe fires 7 arrows in a cone, dealing physical damage to each target hit. Enemies that take an arrow to the knee are slowed temporarily. Each arrow can only damage a unit once.

Level 1 - 40 damage, 25% slow.
Level 2 - 50 damage, 30% slow.
Level 3 - 60 damage, 35% slow.
Level 4 - 70 damage, 40% slow.

Credit:
+ Riot Games for the spell idea.

Note: It is advised that you leave the arrow fly height at 100 and use the sentinel effect (i.e Huntress' missile) for arrow death since this one has a z-axis of 100 unit by default. Otherwise use a Special Effect z-axis vJASS library.


+ udg_V_Hashtable: Spell Hashtable


JASS:
// CONFIGURATION

// Constants

// Main spell raw code
constant function V_GetSpellID takes nothing returns integer
    return 'A005'
endfunction

// Slow spell raw code
constant function V_GetSlowID takes nothing returns integer
    return 'A000'
endfunction

// Main caster dummy raw code
constant function V_DummycasterID takes nothing returns integer
    return 'h000'
endfunction

// Main projectile dummy raw code
constant function V_DummyID takes nothing returns integer
    return 'h002'
endfunction

// Main projectile death effect
constant function V_Sfx takes nothing returns string
    return "Abilities\\Weapons\\SentinelMissile\\SentinelMissile.mdl"
endfunction

// Blood effect
constant function V_Sfx1 takes nothing returns string
    return "Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl"
endfunction

// Attachment point
constant function V_AttachPoint takes nothing returns string
    return "chest"
endfunction

// Order for the custom ability
constant function V_DummyOrder takes nothing returns string
    return "slow"
endfunction

// Movement interval
constant function V_Interval takes nothing returns real
    return 0.03125
endfunction

// Filter valid units

function V_FilterUnit takes unit caster, unit u returns boolean
    return IsUnitEnemy(u, GetOwningPlayer(caster)) and not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitAbilityLevel(u, 'Avul') == 0 and not IsUnitType (u, UNIT_TYPE_MAGIC_IMMUNE) and not IsUnitType (u, UNIT_TYPE_STRUCTURE)
endfunction

// Get Abilities Level

function V_SpellLvl takes unit caster returns integer
    return GetUnitAbilityLevel (caster, V_GetSpellID()) // Get the spell level of the caster
endfunction

// Level dependable values

// Main target damage
function V_Damage takes integer level returns real
    return 30 + level*10.
endfunction

// Area of Effect
function V_AoE takes integer level returns real
    return 50 + level*10.
endfunction

// Projectile Speed
function V_Speed takes integer level returns real
    return (1200. + level*200.)/(1/V_Interval())
endfunction

// Projectile Amount
function V_ProjectileAmount takes integer level returns integer
    return 7
endfunction

// Offset Angle
function V_OffsetAngle takes integer projectileAmount returns real
    if projectileAmount == 1 then
        return 0.
    elseif projectileAmount < 4 then
        return (30./(projectileAmount - 1))*bj_DEGTORAD
    endif

    return (60./(projectileAmount - 1))*bj_DEGTORAD
endfunction

// Max range allowed
function V_Range takes integer level returns real
    return 1200.
endfunction

// Attack - Damage - Weapon type

// Attack type
function V_AttackType takes nothing returns attacktype
    return ATTACK_TYPE_HERO
endfunction

// Damage type
function V_DamageType takes nothing returns damagetype
    return DAMAGE_TYPE_NORMAL
endfunction

// Weapon type
function V_WeaponType takes nothing returns weapontype
    return WEAPON_TYPE_METAL_MEDIUM_SLICE
endfunction

// Preload

function V_Preload takes nothing returns nothing
    local unit dummy  = CreateUnit(Player(0), V_DummycasterID(), 0, 0, 0.00)

    call UnitAddAbility(dummy, V_GetSpellID())
    call UnitAddAbility(dummy, V_GetSlowID())
    call RemoveUnit (dummy)
    set dummy = null
endfunction

// END CONFIGURATION

// SPELL FUNCTIONS

// This is the main function of the spell

function V_TimerLoop takes nothing returns nothing
    local integer ID
    local integer listmax = LoadInteger(udg_V_Hashtable, -1, 0 )
    local unit caster
    local unit dummyCaster
    local unit array dummyProjectile
    local integer i = 0
    local unit u
    local real angle
    local integer level
    local group enumGroup
    local group array tempGroup
    local real t
    local integer j
    local real r
    local integer k
    local real x
    local real y

    // Now we start looping through all the spell instances
    loop
        exitwhen i >= listmax

        // First we load the saved values of a spell instance
        set i = i + 1
        set ID = LoadInteger(udg_V_Hashtable, -1, i)
        set caster = LoadUnitHandle(udg_V_Hashtable, ID, 1)
        set angle = LoadReal(udg_V_Hashtable, ID, 2)
        set level = V_SpellLvl(caster)
        set k = V_ProjectileAmount(level)
        set j = 0
        loop
            exitwhen j == k
            set dummyProjectile[j] = LoadUnitHandle(udg_V_Hashtable, ID, j + 5)
            set tempGroup[j] = LoadGroupHandle(udg_V_Hashtable, ID, j + (5 + k))
            set j = j + 1
        endloop

        set t = LoadReal(udg_V_Hashtable, ID, 3)
        set dummyCaster = LoadUnitHandle(udg_V_Hashtable, ID, 4)

        if t >= V_Range(level) then
            set j = 0
            loop
                exitwhen j == k

                if dummyProjectile[j] != null then
                    call DestroyEffect (AddSpecialEffect(V_Sfx(), GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j])))
                    call KillUnit(dummyProjectile[j])
                    call DestroyGroup(tempGroup[j])
                endif

                set j = j + 1
            endloop

            call KillUnit (dummyCaster)

            if listmax != i then
                call SaveInteger(udg_V_Hashtable, LoadInteger(udg_V_Hashtable, -1, listmax), 0, i)
                call SaveInteger(udg_V_Hashtable, -1, i, LoadInteger(udg_V_Hashtable, -1, listmax))
                call RemoveSavedInteger(udg_V_Hashtable, -1, listmax)
                set i = i - 1
            endif

            set listmax = listmax - 1
            call SaveInteger(udg_V_Hashtable, -1, 0, listmax)

            if LoadInteger(udg_V_Hashtable, 0, 0) + 1 == LoadInteger(udg_V_Hashtable, 0, -1) then
                call FlushChildHashtable(udg_V_Hashtable, 0)
                call PauseTimer(LoadTimerHandle(udg_V_Hashtable, -2, 0))
            else
                call SaveInteger (udg_V_Hashtable, 0, 0, LoadInteger(udg_V_Hashtable, 0, 0) + 1)
                call SaveInteger(udg_V_Hashtable, 0, LoadInteger(udg_V_Hashtable, 0, 0), ID)
            endif

            call FlushChildHashtable(udg_V_Hashtable, ID)

        else

            if IsUnitType(caster, UNIT_TYPE_DEAD) then
                set j = 0
                loop
                    exitwhen j == k

                    if dummyProjectile[j] != null then
                        call DestroyEffect (AddSpecialEffect(V_Sfx(), GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j])))
                        call KillUnit(dummyProjectile[j])
                        call DestroyGroup(tempGroup[j])
                        set tempGroup[j] = null
                    endif

                    set j = j + 1
                endloop

                call KillUnit (dummyCaster)

                if listmax != i then
                    call SaveInteger(udg_V_Hashtable, LoadInteger(udg_V_Hashtable, -1 ,listmax), 0, i)
                    call SaveInteger(udg_V_Hashtable, -1, i, LoadInteger(udg_V_Hashtable, -1, listmax))
                    call RemoveSavedInteger(udg_V_Hashtable, -1, listmax)
                    set i = i - 1
                endif

                set listmax = listmax - 1
                call SaveInteger(udg_V_Hashtable, -1, 0, listmax)

                if LoadInteger(udg_V_Hashtable, 0, 0) + 1 == LoadInteger(udg_V_Hashtable, 0, -1) then
                    call FlushChildHashtable(udg_V_Hashtable, 0)
                    call PauseTimer(LoadTimerHandle(udg_V_Hashtable, -2, 0))
                else
                    call SaveInteger (udg_V_Hashtable, 0, 0, LoadInteger(udg_V_Hashtable, 0, 0) + 1)
                    call SaveInteger(udg_V_Hashtable, 0, LoadInteger(udg_V_Hashtable, 0, 0), ID)
                endif

                call FlushChildHashtable(udg_V_Hashtable, ID)
            else
                set enumGroup = CreateGroup()

                // Move the projectiles, dealing damage and slowing enemies
                set j = 0
                loop
                    exitwhen j == k
                    set r = angle + (j*1. - (k/2. - 0.5))*V_OffsetAngle(k)
                    set x = GetUnitX(dummyProjectile[j]) + V_Speed(level)*Cos(r)
                    set y = GetUnitY(dummyProjectile[j]) + V_Speed(level)*Sin(r)

                    if IsTerrainPathable (x, y, PATHING_TYPE_WALKABILITY) and dummyProjectile[j] != null then
                        call DestroyEffect (AddSpecialEffect(V_Sfx(), GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j])))
                        call KillUnit(dummyProjectile[j])
                        call DestroyGroup(tempGroup[j])
                        set tempGroup[j] = null
                    else
                        call SetUnitX(dummyProjectile[j], x)
                        call SetUnitY(dummyProjectile[j], y)
                        call GroupEnumUnitsInRange(enumGroup, GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j]), V_AoE(level), null)
                        loop
                            set u = FirstOfGroup(enumGroup)
                            exitwhen u == null

                            if V_FilterUnit(caster, u) and not IsUnitInGroup(u, tempGroup[j]) then
                                call UnitDamageTarget(caster, u, V_Damage(level), false, false, V_AttackType(), V_DamageType(), V_WeaponType())
                                call DestroyEffect (AddSpecialEffectTarget(V_Sfx(), u, V_AttachPoint()))
                                call DestroyEffect (AddSpecialEffectTarget(V_Sfx1(), u, V_AttachPoint()))
                                call SetUnitX(dummyCaster, GetUnitX(u))
                                call SetUnitY(dummyCaster, GetUnitY(u))
                                call SetUnitAbilityLevel(dummyCaster, V_GetSlowID(), level)
                                call IssueTargetOrder(dummyCaster, V_DummyOrder(), u)
                                call GroupAddUnit(tempGroup[j], u)
                            endif

                            call GroupRemoveUnit (enumGroup, u)
                        endloop
                    endif
                    set j = j + 1
                endloop

                call DestroyGroup(enumGroup)
                set enumGroup = null
                set t = t + V_Speed(level)
                call SaveReal(udg_V_Hashtable, ID, 3, t)
            endif
        endif

        set caster = null

        set j = 0
        loop
            exitwhen j == k
            set tempGroup[j] = null
            set j = j + 1
        endloop

        set dummyCaster = null
    endloop
endfunction

// Initiate function

function Trig_V_Conditions takes nothing returns boolean
    local unit caster
    local integer ID
    local integer listmax
    local real x1
    local real y1
    local real x2
    local real y2
    local real angle
    local player owner
    local integer i
    local integer j
    local unit dummy

    if GetSpellAbilityId() == V_GetSpellID() then
        set caster = GetTriggerUnit()
        set x1 = GetUnitX(caster)
        set y1 = GetUnitY(caster)
        set x2 = GetSpellTargetX()
        set y2 = GetSpellTargetY()
        set angle = Atan2(y2-y1, x2-x1)
        set owner = GetTriggerPlayer()
        set ID = LoadInteger(udg_V_Hashtable, 0, 0)
        set j = V_ProjectileAmount(V_SpellLvl(caster))

        // Now we start indexing the spell
        if ID>0 then
            call SaveInteger(udg_V_Hashtable, 0, 0, ID - 1)
            set ID = LoadInteger (udg_V_Hashtable, 0, ID)
        else
            set ID = LoadInteger(udg_V_Hashtable, 0, -1) + 1
            call SaveInteger(udg_V_Hashtable,0 , -1, ID)
            if ID == 1 then
                call TimerStart (LoadTimerHandle(udg_V_Hashtable, -2, 0), V_Interval(), true, function V_TimerLoop)
            endif
        endif

        // Save the values related to this spell instance
        call SaveUnitHandle(udg_V_Hashtable, ID, 1, caster)

        set i = 0

        loop
            exitwhen i == j
            set dummy = CreateUnit(owner, V_DummyID(), x1, y1, (angle + (i*1. - (j/2. - 0.5))*V_OffsetAngle(j))*bj_RADTODEG)
            call UnitAddAbility (dummy, 'Arav')
            call UnitRemoveAbility (dummy, 'Arav')
            call SetUnitFlyHeight (dummy, 100, 0.00)
            call SaveUnitHandle(udg_V_Hashtable, ID, i + 5, dummy)
            call SaveGroupHandle(udg_V_Hashtable, ID, i + (5 + j), CreateGroup())
            set i = i + 1
        endloop

        call SaveReal(udg_V_Hashtable, ID, 2, angle)
        call SaveReal(udg_V_Hashtable, ID, 3, 0)
        call SaveUnitHandle(udg_V_Hashtable, ID, 4, CreateUnit(owner, V_DummycasterID(), x1, y1, angle))
        call UnitAddAbility (LoadUnitHandle(udg_V_Hashtable, ID, 4), V_GetSlowID())

        set listmax = LoadInteger (udg_V_Hashtable, -1, 0) + 1
        call SaveInteger (udg_V_Hashtable, -1, 0, listmax )
        call SaveInteger (udg_V_Hashtable, -1, listmax, ID)
        call SaveInteger (udg_V_Hashtable, ID, 0, listmax)

        set caster = null
        set owner = null
        set dummy = null
    endif
    return false
endfunction

//===========================================================================
function InitTrig_Volley takes nothing returns nothing
    local trigger t = CreateTrigger()
    set udg_V_Hashtable = InitHashtable ()
    call V_Preload()
    call SaveTimerHandle (udg_V_Hashtable, -2, 0, CreateTimer())
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition( function Trig_V_Conditions ) )
    set t = null
endfunction


+ v1.0: Initial release.
+ v1.0a: Fix some minor stuffs.
+ v1.0b: Offset angle is now proportional to the number of projectiles. Arc width reduced from 90" to 60".
+ v1.0c: Fix some bugs.
+ v1.0d: Fixed an issue with the timer and indexing method.
+ v1.0e: Fixed most of the issues pointed out by Maker, except for one.


Keywords:
league of legends, ashe, archer, arrow, volley, spell, jass
Contents

Volley v1.0e (Map)

Reviews
Volley v1.0e | Reviewed by Maker | 17th Feb 2013 APPROVED Volley is a basic spell that can be useful. The code is ok and you won't see frame rate drops with this. However the idea isn't very original. Volley v1.0d | Reviewed by...

Moderator

M

Moderator


Volley v1.0e | Reviewed by Maker | 17th Feb 2013
APPROVED

Volley is a basic spell that can be useful.
The code is ok and you won't see frame rate drops with this.
However the idea isn't very original.
[td]
[/td]



Volley v1.0d | Reviewed by Maker | 17th Feb 2013
APPROVED

Volley is a basic spell that can be useful.
The code is ok and you won't see frame rate drops with this.
However the idea isn't very original and the execution needs improvement.

Suggested changes
  • Rename the dummy slow to something else so it is not confused
    with the default slow when looking at the ability list
  • GetWidgetLife(u) > 0.405 -> not IsUnitType(u, UNIT_TYPE_DEAD)
  • You could use only one dummy unit type, you can hide the projectile
    dummy when used for casting
  • The offset calculation when creating projectiles could be improved.
    If there are 1-3 projectiles, the function produces poor results.
  • The projectiles respond badly to terrain in terms of flying height.
    They can be spawned above the caster for example and when they
    go over cliffs it doesn't look good.
[td]
[/td]
 
Level 16
Joined
Dec 15, 2011
Messages
1,423
Fix'd.

Interesting, LoL spells are popping up. I can't find any glaring errors from the code (I didn't look too hard either) though, so good job?

Fixed :D Thanks for pointing that out and complimenting me.

Well about LoL spells I intend to make a few more since currently I am out of idea :p

EDIT: Meh, those needing an exact replica of LoL's spell can send me a PM to get it.
 
Last edited:
Level 16
Joined
Dec 15, 2011
Messages
1,423
Is it possible to make the arrows go through cliffs? If it's possible, What should I edit to accomplish that?

Thanks.

You will need a new version of this. As the pathing check is hardcoded into the spell, you will need to recode it to allow the arrows to move through cliffs.

If I can spare some time, I will rewrite it for you. Shouldn't take very long I reckon.
 
Level 16
Joined
Dec 15, 2011
Messages
1,423
JASS:
// CONFIGURATION

// Constants

// Main spell raw code
constant function V_GetSpellID takes nothing returns integer
    return 'A005'
endfunction

// Slow spell raw code
constant function V_GetSlowID takes nothing returns integer
    return 'A000'
endfunction

// Main caster dummy raw code
constant function V_DummycasterID takes nothing returns integer
    return 'h000'
endfunction

// Main projectile dummy raw code
constant function V_DummyID takes nothing returns integer
    return 'h002'
endfunction

// Main projectile death effect
constant function V_Sfx takes nothing returns string
    return "Abilities\\Weapons\\SentinelMissile\\SentinelMissile.mdl"
endfunction

// Blood effect
constant function V_Sfx1 takes nothing returns string
    return "Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl"
endfunction

// Attachment point
constant function V_AttachPoint takes nothing returns string
    return "chest"
endfunction

// Order for the custom ability
constant function V_DummyOrder takes nothing returns string
    return "slow"
endfunction

// Movement interval
constant function V_Interval takes nothing returns real
    return 0.03125
endfunction

// Filter valid units

function V_FilterUnit takes unit caster, unit u returns boolean
    return IsUnitEnemy(u, GetOwningPlayer(caster)) and not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitAbilityLevel(u, 'Avul') == 0 and not IsUnitType (u, UNIT_TYPE_MAGIC_IMMUNE) and not IsUnitType (u, UNIT_TYPE_STRUCTURE)
endfunction

// Get Abilities Level

function V_SpellLvl takes unit caster returns integer
    return GetUnitAbilityLevel (caster, V_GetSpellID()) // Get the spell level of the caster
endfunction

// Level dependable values

// Main target damage
function V_Damage takes integer level returns real
    return 30 + level*10.
endfunction

// Area of Effect
function V_AoE takes integer level returns real
    return 50 + level*10.
endfunction

// Projectile Speed
function V_Speed takes integer level returns real
    return (1200. + level*200.)/(1/V_Interval())
endfunction

// Projectile Amount
function V_ProjectileAmount takes integer level returns integer
    return 7
endfunction

// Offset Angle
function V_OffsetAngle takes integer projectileAmount returns real
    if projectileAmount == 1 then
        return 0.
    elseif projectileAmount < 4 then
        return (30./(projectileAmount - 1))*bj_DEGTORAD
    endif

    return (60./(projectileAmount - 1))*bj_DEGTORAD
endfunction

// Max range allowed
function V_Range takes integer level returns real
    return 1200.
endfunction

// Attack - Damage - Weapon type

// Attack type
function V_AttackType takes nothing returns attacktype
    return ATTACK_TYPE_HERO
endfunction

// Damage type
function V_DamageType takes nothing returns damagetype
    return DAMAGE_TYPE_NORMAL
endfunction

// Weapon type
function V_WeaponType takes nothing returns weapontype
    return WEAPON_TYPE_METAL_MEDIUM_SLICE
endfunction

// Preload

function V_Preload takes nothing returns nothing
    local unit dummy  = CreateUnit(Player(0), V_DummycasterID(), 0, 0, 0.00)

    call UnitAddAbility(dummy, V_GetSpellID())
    call UnitAddAbility(dummy, V_GetSlowID())
    call RemoveUnit (dummy)
    set dummy = null
endfunction

// END CONFIGURATION

// SPELL FUNCTIONS

// This is the main function of the spell

function V_TimerLoop takes nothing returns nothing
    local integer ID
    local integer listmax = LoadInteger(udg_V_Hashtable, -1, 0 )
    local unit caster
    local unit dummyCaster
    local unit array dummyProjectile
    local integer i = 0
    local unit u
    local real angle
    local integer level
    local group enumGroup
    local group array tempGroup
    local real t
    local integer j
    local real r
    local integer k
    local real x
    local real y

    // Now we start looping through all the spell instances
    loop
        exitwhen i >= listmax

        // First we load the saved values of a spell instance
        set i = i + 1
        set ID = LoadInteger(udg_V_Hashtable, -1, i)
        set caster = LoadUnitHandle(udg_V_Hashtable, ID, 1)
        set angle = LoadReal(udg_V_Hashtable, ID, 2)
        set level = V_SpellLvl(caster)
        set k = V_ProjectileAmount(level)
        set j = 0
        loop
            exitwhen j == k
            set dummyProjectile[j] = LoadUnitHandle(udg_V_Hashtable, ID, j + 5)
            set tempGroup[j] = LoadGroupHandle(udg_V_Hashtable, ID, j + (5 + k))
            set j = j + 1
        endloop

        set t = LoadReal(udg_V_Hashtable, ID, 3)
        set dummyCaster = LoadUnitHandle(udg_V_Hashtable, ID, 4)

        if t >= V_Range(level) then
            set j = 0
            loop
                exitwhen j == k

                if dummyProjectile[j] != null then
                    call DestroyEffect (AddSpecialEffect(V_Sfx(), GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j])))
                    call KillUnit(dummyProjectile[j])
                    call DestroyGroup(tempGroup[j])
                endif

                set j = j + 1
            endloop

            call KillUnit (dummyCaster)

            if listmax != i then
                call SaveInteger(udg_V_Hashtable, LoadInteger(udg_V_Hashtable, -1, listmax), 0, i)
                call SaveInteger(udg_V_Hashtable, -1, i, LoadInteger(udg_V_Hashtable, -1, listmax))
                call RemoveSavedInteger(udg_V_Hashtable, -1, listmax)
                set i = i - 1
            endif

            set listmax = listmax - 1
            call SaveInteger(udg_V_Hashtable, -1, 0, listmax)

            if LoadInteger(udg_V_Hashtable, 0, 0) + 1 == LoadInteger(udg_V_Hashtable, 0, -1) then
                call FlushChildHashtable(udg_V_Hashtable, 0)
                call PauseTimer(LoadTimerHandle(udg_V_Hashtable, -2, 0))
            else
                call SaveInteger (udg_V_Hashtable, 0, 0, LoadInteger(udg_V_Hashtable, 0, 0) + 1)
                call SaveInteger(udg_V_Hashtable, 0, LoadInteger(udg_V_Hashtable, 0, 0), ID)
            endif

            call FlushChildHashtable(udg_V_Hashtable, ID)

        else

            if IsUnitType(caster, UNIT_TYPE_DEAD) then
                set j = 0
                loop
                    exitwhen j == k

                    if dummyProjectile[j] != null then
                        call DestroyEffect (AddSpecialEffect(V_Sfx(), GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j])))
                        call KillUnit(dummyProjectile[j])
                        call DestroyGroup(tempGroup[j])
                        set tempGroup[j] = null
                    endif

                    set j = j + 1
                endloop

                call KillUnit (dummyCaster)

                if listmax != i then
                    call SaveInteger(udg_V_Hashtable, LoadInteger(udg_V_Hashtable, -1 ,listmax), 0, i)
                    call SaveInteger(udg_V_Hashtable, -1, i, LoadInteger(udg_V_Hashtable, -1, listmax))
                    call RemoveSavedInteger(udg_V_Hashtable, -1, listmax)
                    set i = i - 1
                endif

                set listmax = listmax - 1
                call SaveInteger(udg_V_Hashtable, -1, 0, listmax)

                if LoadInteger(udg_V_Hashtable, 0, 0) + 1 == LoadInteger(udg_V_Hashtable, 0, -1) then
                    call FlushChildHashtable(udg_V_Hashtable, 0)
                    call PauseTimer(LoadTimerHandle(udg_V_Hashtable, -2, 0))
                else
                    call SaveInteger (udg_V_Hashtable, 0, 0, LoadInteger(udg_V_Hashtable, 0, 0) + 1)
                    call SaveInteger(udg_V_Hashtable, 0, LoadInteger(udg_V_Hashtable, 0, 0), ID)
                endif

                call FlushChildHashtable(udg_V_Hashtable, ID)
            else
                set enumGroup = CreateGroup()

                // Move the projectiles, dealing damage and slowing enemies
                set j = 0
                loop
                    exitwhen j == k
                    set r = angle + (j*1. - (k/2. - 0.5))*V_OffsetAngle(k)
                    set x = GetUnitX(dummyProjectile[j]) + V_Speed(level)*Cos(r)
                    set y = GetUnitY(dummyProjectile[j]) + V_Speed(level)*Sin(r)

                    call SetUnitX(dummyProjectile[j], x)
                    call SetUnitY(dummyProjectile[j], y)
                    call GroupEnumUnitsInRange(enumGroup, GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j]), V_AoE(level), null)
                    loop
                        set u = FirstOfGroup(enumGroup)
                        exitwhen u == null

                        if V_FilterUnit(caster, u) and not IsUnitInGroup(u, tempGroup[j]) then
                            call UnitDamageTarget(caster, u, V_Damage(level), false, false, V_AttackType(), V_DamageType(), V_WeaponType())
                            call DestroyEffect (AddSpecialEffectTarget(V_Sfx(), u, V_AttachPoint()))
                            call DestroyEffect (AddSpecialEffectTarget(V_Sfx1(), u, V_AttachPoint()))
                            call SetUnitX(dummyCaster, GetUnitX(u))
                            call SetUnitY(dummyCaster, GetUnitY(u))
                            call SetUnitAbilityLevel(dummyCaster, V_GetSlowID(), level)
                            call IssueTargetOrder(dummyCaster, V_DummyOrder(), u)
                            call GroupAddUnit(tempGroup[j], u)
                        endif

                        call GroupRemoveUnit (enumGroup, u)
                    endloop
                    
                    set j = j + 1
                endloop

                call DestroyGroup(enumGroup)
                set enumGroup = null
                set t = t + V_Speed(level)
                call SaveReal(udg_V_Hashtable, ID, 3, t)
            endif
        endif

        set caster = null

        set j = 0
        loop
            exitwhen j == k
            set tempGroup[j] = null
            set j = j + 1
        endloop

        set dummyCaster = null
    endloop
endfunction

// Initiate function

function Trig_V_Conditions takes nothing returns boolean
    local unit caster
    local integer ID
    local integer listmax
    local real x1
    local real y1
    local real x2
    local real y2
    local real angle
    local player owner
    local integer i
    local integer j
    local unit dummy

    if GetSpellAbilityId() == V_GetSpellID() then
        set caster = GetTriggerUnit()
        set x1 = GetUnitX(caster)
        set y1 = GetUnitY(caster)
        set x2 = GetSpellTargetX()
        set y2 = GetSpellTargetY()
        set angle = Atan2(y2-y1, x2-x1)
        set owner = GetTriggerPlayer()
        set ID = LoadInteger(udg_V_Hashtable, 0, 0)
        set j = V_ProjectileAmount(V_SpellLvl(caster))

        // Now we start indexing the spell
        if ID>0 then
            call SaveInteger(udg_V_Hashtable, 0, 0, ID - 1)
            set ID = LoadInteger (udg_V_Hashtable, 0, ID)
        else
            set ID = LoadInteger(udg_V_Hashtable, 0, -1) + 1
            call SaveInteger(udg_V_Hashtable,0 , -1, ID)
            if ID == 1 then
                call TimerStart (LoadTimerHandle(udg_V_Hashtable, -2, 0), V_Interval(), true, function V_TimerLoop)
            endif
        endif

        // Save the values related to this spell instance
        call SaveUnitHandle(udg_V_Hashtable, ID, 1, caster)

        set i = 0

        loop
            exitwhen i == j
            set dummy = CreateUnit(owner, V_DummyID(), x1, y1, (angle + (i*1. - (j/2. - 0.5))*V_OffsetAngle(j))*bj_RADTODEG)
            call UnitAddAbility (dummy, 'Arav')
            call UnitRemoveAbility (dummy, 'Arav')
            call SetUnitFlyHeight (dummy, 100, 0.00)
            call SaveUnitHandle(udg_V_Hashtable, ID, i + 5, dummy)
            call SaveGroupHandle(udg_V_Hashtable, ID, i + (5 + j), CreateGroup())
            set i = i + 1
        endloop

        call SaveReal(udg_V_Hashtable, ID, 2, angle)
        call SaveReal(udg_V_Hashtable, ID, 3, 0)
        call SaveUnitHandle(udg_V_Hashtable, ID, 4, CreateUnit(owner, V_DummycasterID(), x1, y1, angle))
        call UnitAddAbility (LoadUnitHandle(udg_V_Hashtable, ID, 4), V_GetSlowID())

        set listmax = LoadInteger (udg_V_Hashtable, -1, 0) + 1
        call SaveInteger (udg_V_Hashtable, -1, 0, listmax )
        call SaveInteger (udg_V_Hashtable, -1, listmax, ID)
        call SaveInteger (udg_V_Hashtable, ID, 0, listmax)

        set caster = null
        set owner = null
        set dummy = null
    endif
    return false
endfunction

//===========================================================================
function InitTrig_Volley takes nothing returns nothing
    local trigger t = CreateTrigger()
    set udg_V_Hashtable = InitHashtable ()
    call V_Preload()
    call SaveTimerHandle (udg_V_Hashtable, -2, 0, CreateTimer())
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition( function Trig_V_Conditions ) )
    set t = null
endfunction

Replaced the current code with this one. I did a freehand edit of the code so there may be something that I missed. If that happens, just drop me a line and I will fix it.

Thank you for using my resource :)
 
Top