• 🏆 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] spread shot skill

Status
Not open for further replies.
Level 4
Joined
Jan 13, 2013
Messages
92
is it possible to make a spread shot skill ? something like ashe first skill from League of legends
 
Level 4
Joined
Jan 13, 2013
Messages
92
i'm a bit newb in JASS but make it, i would like to learn JASS sometime :thumbs_up:

but anyway is there a way to make it by triggers?
 
Level 4
Joined
Jan 13, 2013
Messages
92
@doomlord ok thx, patience is a virtue :goblin_good_job:

@defskull the skill i'm talking about is a skill that shoot X arrow at once, but i want an active skill not an passive skill like barrage


well i'm going to practice some JASS right now
 
Level 16
Joined
Dec 15, 2011
Messages
1,423
Oh, firing arrows in a cone-shape degree ?
Let's say fires 5 arrows in 90 degree angle, each arrow is 18 degrees to one another, is that it ?

It is not exactly 90 degree but more like the center arrow takes the Atan2() angle and the other ones offset by an integer multiply of OffsetAngle() that goes from -2 to 2 :) I am terrible at explaning but I think you can understand me :D

EDIT: Spell made and attached. I made it a bit different from LoL's skill so that you can learn some JASS.

Also here is the code for reference.

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 "origin"
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

// Offset Angle
constant function V_OffsetAngle takes nothing returns real
    return 15.*bj_DEGTORAD
endfunction

// Filter valid units

function V_FilterUnit takes unit caster, unit u returns boolean
    return IsUnitEnemy(u, GetOwningPlayer(caster)) and GetWidgetLife(u) > 0.405 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

// 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), 'h000', 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

    // 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, 3)
        set level = V_SpellLvl(caster)
        set j = 0
        loop
            exitwhen j == V_ProjectileAmount(level)
            set dummyProjectile[j] = LoadUnitHandle(udg_V_Hashtable, ID, j + 9)
            set tempGroup[j] = LoadGroupHandle(udg_V_Hashtable, ID, j + 16)
            set j = j + 1
        endloop
        set dummyCaster = LoadUnitHandle(udg_V_Hashtable, ID, 7)
        set t = LoadReal(udg_V_Hashtable, ID, 5)

        if t >= V_Range(level) then
            set j = 0
            loop
                exitwhen j == V_ProjectileAmount(level)
                call DestroyEffect (AddSpecialEffect(V_Sfx(), GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j])))
                call KillUnit(dummyProjectile[j])
                call DestroyGroup(tempGroup[j])
                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) == LoadInteger(udg_V_Hashtable, 0, -1) + 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 == V_ProjectileAmount(level)
                    call DestroyEffect (AddSpecialEffect(V_Sfx(), GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j])))
                    call KillUnit(dummyProjectile[j])
                    call DestroyGroup(tempGroup[j])
                    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) == LoadInteger(udg_V_Hashtable, 0, -1) + 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()

                set j = 0
                loop
                    exitwhen j == V_ProjectileAmount(level)
                    set r = angle + (j - 3)*V_OffsetAngle()
                    call SetUnitX(dummyProjectile[j], GetUnitX(dummyProjectile[j]) + V_Speed(level)*Cos(r))
                    call SetUnitY(dummyProjectile[j], GetUnitY(dummyProjectile[j]) + V_Speed(level)*Sin(r))
                    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
                // Move the projectiles, dealing damage and slowing enemies around

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

        set caster = null

        set j = 0
        loop
            exitwhen j == V_ProjectileAmount(level)
            set dummyProjectile[j] = null
            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

    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)

        // 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 == V_ProjectileAmount(V_SpellLvl(caster))
            call SaveUnitHandle(udg_V_Hashtable, ID, i + 9, CreateUnit(owner, V_DummyID(), x1, y1, (angle + (i - 3)*V_OffsetAngle())*bj_RADTODEG))
            call SaveGroupHandle(udg_V_Hashtable, ID, i + 16, CreateGroup())
            set i = i + 1
        endloop

        call SaveReal(udg_V_Hashtable, ID, 3, angle)
        call SaveReal(udg_V_Hashtable, ID, 5, 0)
        call SaveUnitHandle(udg_V_Hashtable, ID, 7, CreateUnit(owner, V_DummycasterID(), x1, y1, angle))
        call UnitAddAbility (LoadUnitHandle(udg_V_Hashtable, ID, 7), 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
    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
 

Attachments

  • Volley.w3x
    28.6 KB · Views: 62
Last edited:
Level 20
Joined
Jun 27, 2011
Messages
1,864
It is not exactly 90 degree but more like the center arrow takes the Atan2() angle and the other ones offset by an integer multiply of OffsetAngle() that goes from -2 to 2 :) I am terrible at explaning but I think you can understand me :D

EDIT: Spell made and attached. I made it a bit different from LoL's skill so that you can learn some JASS.

Also here is the code for reference.

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 "origin"
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

// Offset Angle
constant function V_OffsetAngle takes nothing returns real
    return 15.*bj_DEGTORAD
endfunction

// Filter valid units

function V_FilterUnit takes unit caster, unit u returns boolean
    return IsUnitEnemy(u, GetOwningPlayer(caster)) and GetWidgetLife(u) > 0.405 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

// 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), 'h000', 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

    // 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, 3)
        set level = V_SpellLvl(caster)
        set j = 0
        loop
            exitwhen j == V_ProjectileAmount(level)
            set dummyProjectile[j] = LoadUnitHandle(udg_V_Hashtable, ID, j + 9)
            set tempGroup[j] = LoadGroupHandle(udg_V_Hashtable, ID, j + 16)
            set j = j + 1
        endloop
        set dummyCaster = LoadUnitHandle(udg_V_Hashtable, ID, 7)
        set t = LoadReal(udg_V_Hashtable, ID, 5)

        if t >= V_Range(level) then
            set j = 0
            loop
                exitwhen j == V_ProjectileAmount(level)
                call DestroyEffect (AddSpecialEffect(V_Sfx(), GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j])))
                call KillUnit(dummyProjectile[j])
                call DestroyGroup(tempGroup[j])
                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) == LoadInteger(udg_V_Hashtable, 0, -1) + 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 == V_ProjectileAmount(level)
                    call DestroyEffect (AddSpecialEffect(V_Sfx(), GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j])))
                    call KillUnit(dummyProjectile[j])
                    call DestroyGroup(tempGroup[j])
                    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) == LoadInteger(udg_V_Hashtable, 0, -1) + 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()

                set j = 0
                loop
                    exitwhen j == V_ProjectileAmount(level)
                    set r = angle + (j - 3)*V_OffsetAngle()
                    call SetUnitX(dummyProjectile[j], GetUnitX(dummyProjectile[j]) + V_Speed(level)*Cos(r))
                    call SetUnitY(dummyProjectile[j], GetUnitY(dummyProjectile[j]) + V_Speed(level)*Sin(r))
                    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
                // Move the projectiles, dealing damage and slowing enemies around

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

        set caster = null

        set j = 0
        loop
            exitwhen j == V_ProjectileAmount(level)
            set dummyProjectile[j] = null
            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

    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)

        // 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 == V_ProjectileAmount(V_SpellLvl(caster))
            call SaveUnitHandle(udg_V_Hashtable, ID, i + 9, CreateUnit(owner, V_DummyID(), x1, y1, (angle + (i - 3)*V_OffsetAngle())*bj_RADTODEG))
            call SaveGroupHandle(udg_V_Hashtable, ID, i + 16, CreateGroup())
            set i = i + 1
        endloop

        call SaveReal(udg_V_Hashtable, ID, 3, angle)
        call SaveReal(udg_V_Hashtable, ID, 5, 0)
        call SaveUnitHandle(udg_V_Hashtable, ID, 7, CreateUnit(owner, V_DummycasterID(), x1, y1, angle))
        call UnitAddAbility (LoadUnitHandle(udg_V_Hashtable, ID, 7), 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
    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

It would be nice if the slow turns the affected unit blue (like in Halls of the Dead's Slow), and destroy the arrow if it reaches an enemy unit.
 
Level 16
Joined
Dec 15, 2011
Messages
1,423
It would be nice if the slow turns the affected unit blue (like in Halls of the Dead's Slow), and destroy the arrow if it reaches an enemy unit.

Well then I will upload that version later :D

EDIT: I am sorry but the custom slow requires a vJASS library, which is sth I can't do because I haven't learned vJASS yet. And I don't want to use Blizzard's Frost slow since it only allows little customization.

EDIT #2: Here is the code and map for those looking for an exact replica of Ashe's Volley (Each arrow will only hit one enemy, and each enemy will only be hit by one arrow).

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 "origin"
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

// Offset Angle
constant function V_OffsetAngle takes nothing returns real
    return 15.*bj_DEGTORAD
endfunction

// Filter valid units

function V_FilterUnit takes unit caster, unit u returns boolean
    return IsUnitEnemy(u, GetOwningPlayer(caster)) and GetWidgetLife(u) > 0.405 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

// 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), 'h000', 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 real t
    local integer j
    local real r
    local group tempGroup

    // 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 tempGroup = LoadGroupHandle(udg_V_Hashtable, ID, 12)
        set j = 0
        loop
            exitwhen j == V_ProjectileAmount(level)
            set dummyProjectile[j] = LoadUnitHandle(udg_V_Hashtable, ID, j + 5)
            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 == V_ProjectileAmount(level)
                if dummyProjectile[j] != null then
                    call DestroyEffect(AddSpecialEffect(V_Sfx(), GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j])))
                    call KillUnit(dummyProjectile[j])
                endif
                set j = j + 1
            endloop

            call KillUnit (dummyCaster)
            call DestroyGroup (tempGroup)

            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) == LoadInteger(udg_V_Hashtable, 0, -1) + 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 == V_ProjectileAmount(level)
                    if dummyProjectile[j] != null then
                        call DestroyEffect (AddSpecialEffect(V_Sfx(), GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j])))
                        call KillUnit(dummyProjectile[j])
                    endif
                    set j = j + 1
                endloop

                call KillUnit (dummyCaster)
                call DestroyGroup (tempGroup)

                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) == LoadInteger(udg_V_Hashtable, 0, -1) + 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 == V_ProjectileAmount(level)
                    set r = angle + (j - 3)*V_OffsetAngle()
                    call SetUnitX(dummyProjectile[j], GetUnitX(dummyProjectile[j]) + V_Speed(level)*Cos(r))
                    call SetUnitY(dummyProjectile[j], GetUnitY(dummyProjectile[j]) + V_Speed(level)*Sin(r))
                    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) 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 DestroyEffect (AddSpecialEffect(V_Sfx(), GetUnitX(dummyProjectile[j]), GetUnitY(dummyProjectile[j])))
                            call KillUnit(dummyProjectile[j])
                            call GroupAddUnit(tempGroup, 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 dummyCaster = null
        set tempGroup = null

        set j = 0
        loop
            exitwhen j == V_ProjectileAmount(level)
            set dummyProjectile[j] = null
            set j = j + 1
        endloop
    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

    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)

        // 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 == V_ProjectileAmount(V_SpellLvl(caster))
            call SaveUnitHandle(udg_V_Hashtable, ID, i + 5, CreateUnit(owner, V_DummyID(), x1, y1, (angle + (i - 3)*V_OffsetAngle())*bj_RADTODEG))
            set i = i + 1
        endloop

        call SaveReal(udg_V_Hashtable, ID, 2, angle)
        call SaveReal(udg_V_Hashtable, ID, 3, 0)
        call SaveGroupHandle(udg_V_Hashtable, ID, 12, CreateGroup())
        call SaveUnitHandle(udg_V_Hashtable, ID, 4, CreateUnit(owner, V_DummycasterID(), x1, y1, angle))
        call UnitAddAbility (LoadUnitHandle(udg_V_Hashtable, ID, 7), 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
    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

Map: http://www.hiveworkshop.com/forums/pastebin.php?id=nyfxl2

Btw I didn't care enough to fix the description so just bear with it and forgive me :p
 
Last edited:
Status
Not open for further replies.
Top