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

[JASS] Arcing Shurikens v1.0d

Well, another spell from my ninja fever :D This is a spell made upon a request by indomitable1319 with some changes.

ARCING SHURIKENS

Releases two magical shurikens that travel in an arc to the target location then back to the caster, damaging and maiming all enemies in its path. Area of Effect and projectile speed improves with level. Each shuriken can only damage a unit once.

Level 1 - 150 damage, 25% slow.
Level 2 - 200 damage, 40% slow.
Level 3 - 250 damage, 55% slow.
Level 4 - 300 damage, 70% slow.

Credit:
+ indomitable1319 for the original idea (i.e the request).
+ AlienAtSystem for his awesome formulae.
+ Dr Super Good for some help with the formulae.


+ udg_AS_Hashtable: Spell Hashtable


JASS:
// CONFIGURATION

// Constants

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

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

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

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

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

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

// Attachment point
constant function AS_AttachPoint takes nothing returns string
    return "origin"
endfunction

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

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

// Filter valid units

function AS_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 AS_SpellLvl takes unit caster returns integer
    return GetUnitAbilityLevel (caster, AS_GetSpellID()) // Get the spell level of the caster
endfunction

// Level dependable values

// Main target damage
function AS_Damage takes integer level returns real
    return 100 + level*50.
endfunction

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

// Distance travelled in 1 second
function AS_Speed takes integer level returns real
    return 750. + level*100.
endfunction

// Attack - Damage - Weapon type

// Attack type
function AS_AttackType takes nothing returns attacktype
    return ATTACK_TYPE_NORMAL
endfunction

// Damage type
function AS_DamageType takes nothing returns damagetype
    return DAMAGE_TYPE_MAGIC
endfunction

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

// Preload

function AS_Preload takes nothing returns nothing
    local unit dummy  = CreateUnit(Player(0), 'h000', 0, 0, 0.00)

    call UnitAddAbility(dummy, AS_GetSpellID())
    call UnitAddAbility(dummy, AS_GetSlowID())
    call RemoveUnit (dummy)
    set dummy = null
endfunction

// END CONFIGURATION

// SPELL FUNCTIONS


// This is the main function of the spell

function AS_TimerLoop takes nothing returns nothing
    local integer ID
    local integer listmax = LoadInteger(udg_AS_Hashtable, -1, 0 )
    local unit caster
    local unit dummyProjectile0
    local unit dummyProjectile1
    local unit dummyCaster
    local integer i = 0
    local real x1
    local real y1
    local real x2
    local real y2
    local unit u
    local integer level
    local group enumGroup
    local group tempGroup0
    local group tempGroup1
    local real t

    // 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_AS_Hashtable, -1, i)
        set caster = LoadUnitHandle(udg_AS_Hashtable, ID, 1)
        set dummyProjectile0 = LoadUnitHandle(udg_AS_Hashtable, ID, 2)
        set dummyProjectile1 = LoadUnitHandle(udg_AS_Hashtable, ID, 9)
        set dummyCaster = LoadUnitHandle(udg_AS_Hashtable, ID, 7)
        set t = LoadReal(udg_AS_Hashtable, ID, 5)
        set x1 = GetUnitX(caster)
        set y1 = GetUnitY(caster)
        set x2 = LoadReal (udg_AS_Hashtable, ID, 3)
        set y2 = LoadReal (udg_AS_Hashtable, ID, 4)
        set level = AS_SpellLvl(caster)
        set tempGroup0 = LoadGroupHandle(udg_AS_Hashtable, ID, 6)
        set tempGroup1 = LoadGroupHandle(udg_AS_Hashtable, ID, 10)

        if t >= bj_PI then
            call KillUnit (dummyProjectile0)
            call KillUnit (dummyProjectile1)
            call KillUnit (dummyCaster)
            call DestroyGroup(tempGroup0)
            call DestroyGroup(tempGroup1)
            call DestroyEffect (AddSpecialEffect(AS_Sfx(), x1, y1))

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

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

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

            call FlushChildHashtable(udg_AS_Hashtable, ID)

        else

            if IsUnitType(caster, UNIT_TYPE_DEAD) then
                call KillUnit (dummyProjectile0)
                call KillUnit (dummyProjectile1)
                call KillUnit (dummyCaster)
                call DestroyGroup(tempGroup0)
                call DestroyGroup(tempGroup1)
                call DestroyEffect (AddSpecialEffect(AS_Sfx(), x1, y1))

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

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

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

                call FlushChildHashtable(udg_AS_Hashtable, ID)

            else

                // Move the projectiles, dealing damage and slowing enemies around
                call SetUnitX(dummyProjectile0, (x2-x1)*Sin(t) + 0.25*(y2-y1)*Sin(2*t) + x1)
                call SetUnitY(dummyProjectile0, (y2-y1)*Sin(t) + 0.25*(x1-x2)*Sin(2*t) + y1)
                call SetUnitX(dummyProjectile1, (x2-x1)*Sin(t) - 0.25*(y2-y1)*Sin(2*t) + x1)
                call SetUnitY(dummyProjectile1, (y2-y1)*Sin(t) - 0.25*(x1-x2)*Sin(2*t) + y1)

                set enumGroup = CreateGroup()
                call GroupEnumUnitsInRange(enumGroup, GetUnitX(dummyProjectile0), GetUnitY(dummyProjectile0), AS_AoE(level), null)
                loop
                    set u = FirstOfGroup(enumGroup)
                    exitwhen u == null

                    if AS_FilterUnit(caster, u) and not IsUnitInGroup(u, tempGroup0) then
                        call UnitDamageTarget(caster, u, AS_Damage(level), false, false, AS_AttackType(), AS_DamageType(), AS_WeaponType())
                        call DestroyEffect (AddSpecialEffectTarget(AS_Sfx(), u, AS_AttachPoint()))
                        call DestroyEffect (AddSpecialEffectTarget(AS_Sfx1(), u, AS_AttachPoint()))
                        call SetUnitX(dummyCaster, GetUnitX(u))
                        call SetUnitY(dummyCaster, GetUnitY(u))
                        call SetUnitAbilityLevel(dummyCaster, AS_GetSlowID(), level)
                        call IssueTargetOrder(dummyCaster, AS_DummyOrder(), u)
                        call GroupAddUnit(tempGroup0, u)
                    endif

                    call GroupRemoveUnit (enumGroup, u)
                endloop

                call GroupEnumUnitsInRange(enumGroup, GetUnitX(dummyProjectile1), GetUnitY(dummyProjectile1), AS_AoE(level), null)
                loop
                    set u = FirstOfGroup(enumGroup)
                    exitwhen u == null

                    if AS_FilterUnit(caster, u) and not IsUnitInGroup(u, tempGroup1) then
                        call UnitDamageTarget(caster, u, AS_Damage(level), false, false, AS_AttackType(), AS_DamageType(), AS_WeaponType())
                        call DestroyEffect (AddSpecialEffectTarget(AS_Sfx(), u, AS_AttachPoint()))
                        call DestroyEffect (AddSpecialEffectTarget(AS_Sfx1(), u, AS_AttachPoint()))
                        call SetUnitX(dummyCaster, GetUnitX(u))
                        call SetUnitY(dummyCaster, GetUnitY(u))
                        call SetUnitAbilityLevel(dummyCaster, AS_GetSlowID(), level)
                        call IssueTargetOrder(dummyCaster, AS_DummyOrder(), u)
                        call GroupAddUnit(tempGroup1, u)
                    endif

                    call GroupRemoveUnit (enumGroup, u)
                endloop

                call DestroyGroup(enumGroup)
                set enumGroup = null
                set t = t + (AS_Speed(level)/((1/AS_Interval())*LoadReal(udg_AS_Hashtable, ID, 8)))*bj_PI
                call SaveReal(udg_AS_Hashtable, ID, 5, t)
            endif
        endif

        set caster = null
        set dummyProjectile0 = null
        set dummyProjectile1 = null
        set tempGroup0 = null
        set tempGroup1 = null
        set dummyCaster = null
    endloop
endfunction

// Initiate function

function Trig_AS_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

    if GetSpellAbilityId() == AS_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)*bj_RADTODEG
        set owner = GetTriggerPlayer()
        set ID = LoadInteger(udg_AS_Hashtable, 0, 0)

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

        // Save the values related to this spell instance
        call SaveUnitHandle(udg_AS_Hashtable, ID, 1, caster)
        call SaveUnitHandle(udg_AS_Hashtable, ID, 2, CreateUnit(owner, AS_DummyID(), x1, y1, angle))
        call SaveUnitHandle(udg_AS_Hashtable, ID, 9, CreateUnit(owner, AS_DummyID(), x1, y1, angle))
        call SaveReal(udg_AS_Hashtable, ID, 3, x2)
        call SaveReal(udg_AS_Hashtable, ID, 4, y2)
        call SaveReal(udg_AS_Hashtable, ID, 5, 0)
        call SaveGroupHandle(udg_AS_Hashtable, ID, 6, CreateGroup())
        call SaveGroupHandle(udg_AS_Hashtable, ID, 10, CreateGroup())
        call SaveUnitHandle(udg_AS_Hashtable, ID, 7, CreateUnit(owner, AS_DummycasterID(), x1, y1, angle))
        call UnitAddAbility (LoadUnitHandle(udg_AS_Hashtable, ID, 7), AS_GetSlowID())

        if (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) == 0 then
            call SaveReal(udg_AS_Hashtable, ID, 8, 1)
        else
            call SaveReal(udg_AS_Hashtable, ID, 8, SquareRoot((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)))
        endif

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

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

//===========================================================================
function InitTrig_Arcing_Shuriken takes nothing returns nothing
    local trigger t = CreateTrigger()
    set udg_AS_Hashtable = InitHashtable ()
    call AS_Preload()
    call SaveTimerHandle (udg_AS_Hashtable, -2, 0, CreateTimer())
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition( function Trig_AS_Conditions ) )
    set t = null
endfunction


+ v1.0: Initial release.
+ v1.0a : Posted the actual code and modified the spell a bit for more awesomeness :p
+ v1.0b : Code improved.
+ v1.0c: Another code improvement following Mag's review.
+ v1.0d: Fixed an issue with the timer and indexing method.


Keywords:
ninja, ninjutsu, shuriken, crescent, arc, mcninja, batman, spell, jass
Contents

Arcing Shurikens v1.0d (Map)

Reviews
00:48, 23rd Jan 2013 Magtheridon96: Approved. Good job. The spell is decently configurable, and efficient enough.

Moderator

M

Moderator

00:48, 23rd Jan 2013
Magtheridon96: Approved. Good job.
The spell is decently configurable, and efficient enough.
 
Level 16
Joined
Dec 15, 2011
Messages
1,423
He requires your help in learning how to create spells awesome as yours.

Oh thanks for the compliment. About the spells I always need the help of awesome guys around here (my long credit list is evident enough) and thus it seems to be really challenging for me to teach him.

My only advise atm is hanging in chat a lot and talk to people there. They will help you improve your skills gradually.
 
Top