• 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.
  • The Hive's 22nd Icon Contest: Creep Abilities is now concluded, time to vote for your favourite set of icons! Click here to vote!
  • ✅ The POLL for Hive's Texturing Contest #34 is OPEN! Vote for the TOP 3 SKINS! 🔗Click here to cast your vote!
  • ✅ The POLL for Hive's Techtree Contest #20 is OPEN! Vote for the TOP 3 FACTIONS! 🔗Click here to cast your vote!

Shadow Vortext v0.01

Credits in the code...

Here a next spell for the a dark hero...

hf and please give some credits when you use this...

Description: Absorbes shadow energy out of the air above the caster, after some time releases it in a nova.

JASS:
scope ShadowVortex initializer init

//*************************************************************************************************************//
//                                                 ShadowVortex v0.01                                          //
//                                                        by                                                   //
//                                                      cedi                                                   //
//                                                                                                             //
//                                         needs: TimerUtils by Vexorian                                       //
//                                                Bound Sentinel by Vexorian                                   //
//                                                Dummy Model by                                               //
//                                                Heights by cedi                                              //
//*************************************************************************************************************//

//For use, copy the trigger to your map, copy the dummy create a spell and adjust the values below.

private keyword Shadow
private keyword Main
private keyword Missile

globals
    //ID of the spell
    private constant integer        SPELL_ID            = 'A000'
    //ID of your dummy
    private constant integer        DUMMY_ID            = 'h000'
    //Amount of shadows created each time.
    private constant integer        SHADOW_COUNT        = 4
    //Amount of missiles created in the nova.
    private constant integer        MISSILE_COUNT       = 16
    //Interval of the moves
    private constant real           TIMER_INTERVAL      = 0.035
    //Time between the nova and the absorbing
    private constant real           SHADOW_NOVA_INT     = 1.00
    //Speed of the shadows. In wc3 ms.
    private constant real           SPEED               = 400.00
    //Interval of picking
    private constant real           PICK_INT            = 0.10
    //Floating height of the shadows.
    private constant real           SHADOW_HEIGHT       = 50.00
    //Speed of the rotating in angle per second.
    private constant real           ANGLE_CHANGE        = 120.00
    //Start Height
    private constant real           START_Z             = 500.00
    //Start distance
    private constant real           SHADOW_DISTANCE     = 550.00
    //Min Dist
    private constant real           SHADOW_MIN_DIST     = 5.00
    //Interval of the shadow creation.
    private constant real           SHADOW_INTERVAL     = 0.5
    //On Damage sfx
    private constant string         DAMAGE_SFX          = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl"
    //Model of the shadows.
    private constant string         SHADOW_MODEL        = "Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdl"
    
    //SYSTEM
    private          Shadow         SHADOW
    private          Main           MAIN
    private          Missile        MISSILE
    private          group          GROUP               = CreateGroup()
endglobals

private function DAMAGE takes integer level returns real
    return 100.00 + 50.00 * level
endfunction

private function AOE takes integer level returns real
    return 55.00 + 5.00 * level
endfunction

private function RANGE takes integer level returns real
    return 1000.00 + 200.00 * level
endfunction

private function NEEDED takes integer level returns integer
    return 8 + 4 * level
endfunction

private function HIT_FUNC takes Missile m, unit target returns nothing

endfunction

//*************************************************************************************************************//
//                                                     !SYSTEM!                                                //
//*************************************************************************************************************//

private function AngleBetweenCoordinates takes real x1, real x2, real y1, real y2 returns real
    return bj_RADTODEG * Atan2(y2 - y1, x2 - x1)
endfunction

private function DistanceBetweenCoordinates takes real x1, real x2, real y1, real y2 returns real
    local real dx = x2 - x1
    local real dy = y2 - y1
    return SquareRoot(dx * dx + dy * dy)
endfunction

private function AngleBetweenUnits takes unit u, unit u2 returns real
    return bj_RADTODEG * Atan2(GetUnitY( u2 ) - GetUnitY( u ), GetUnitX( u2 ) - GetUnitX( u ))
endfunction

private function IsAliveAndUnitAndNotMagicImmune takes nothing returns boolean
    return GetWidgetLife( GetFilterUnit() ) > 0.405 and IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false and IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == false
endfunction

private function ParabolaZ2 takes real y0, real y1, real h, real d, real x returns real
  local real A = (2*(y0+y1)-4*h)/(d*d)
  local real B = (y1-y0-A*d*d)/d
  return A*x*x + B*x + y0
endfunction

private function MissileControl takes nothing returns nothing
    set MISSILE = GetTimerData( GetExpiredTimer() )
    call MISSILE.control()
endfunction

private function ShadowControl takes nothing returns nothing
    set SHADOW = GetTimerData( GetExpiredTimer() )
    call SHADOW.control()
endfunction

private function MainControl takes nothing returns nothing
    set MAIN = GetTimerData( GetExpiredTimer() )
    call MAIN.control()
endfunction

private struct Missile
    unit caster     = null
    unit u          = null
    integer level   = 1
    real vx         = 0.00
    real vy         = 0.00
    real range      = 0.00
    real maxrange   = 0.00
    real x          = 0.00
    real y          = 0.00
    real interval   = 0.00
    effect model    = null
    timer t         = null
    group g         = null
    
    private method pick takes nothing returns nothing
        local unit u = null
        call GroupEnumUnitsInRange( GROUP, .x, .y, AOE( .level ), Condition( function IsAliveAndUnitAndNotMagicImmune ) )
        loop
            set u = FirstOfGroup( GROUP )
            exitwhen u == null
            if IsUnitEnemy( u, GetOwningPlayer( .caster ) ) then
                if not IsUnitInGroup( u, .g ) then
                    call GroupAddUnit( .g, u )
                    call DestroyEffect( AddSpecialEffectTarget( DAMAGE_SFX, u, "chest" ) )
                    call UnitDamageTarget( .caster, u, DAMAGE( .level ), true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_NORMAL, null )
                    call HIT_FUNC( this, u )
                endif
            endif
            call GroupRemoveUnit( GROUP, u )
            set u = null
        endloop
    endmethod
    
    method onDestroy takes nothing returns nothing
        set .caster = null
        call DestroyEffect( .model )
        set .model = null
        call KillUnit( .u )
        set .u = null
        call ReleaseTimer( .t )
        set .t = null
        call GroupClear( .g )
        call DestroyGroup( .g )
        set .g = null
    endmethod
    
    method control takes nothing returns nothing
        if GetWidgetLife( .u ) <= 0.405 then
            call .destroy()
            return
        endif
        set .range = .range - SPEED * TIMER_INTERVAL
        if .range <= 0.00 then
            call .destroy()
            return
        endif
        set .x = .x + .vx
        set .y = .y + .vy
        call SetUnitX( .u, .x )
        call SetUnitY( .u, .y )
        set .interval = .interval + TIMER_INTERVAL
        if .interval >= PICK_INT then
            set .interval = 0.00
            call .pick()
        endif
    endmethod
    
    static method create takes Main m, real angle returns thistype
        local thistype this = thistype.allocate()
        set angle = angle * bj_DEGTORAD
        set .caster = m.caster
        set .level = m.level
        set .vx = Cos( angle ) * SPEED * TIMER_INTERVAL
        set .vy = Sin( angle ) * SPEED * TIMER_INTERVAL
        set .x = GetUnitX( .caster ) + .vx
        set .y = GetUnitY( .caster ) + .vy
        set .range = RANGE( .level )
        set .maxrange = .range
        set .u = CreateUnit( GetOwningPlayer( .caster ), DUMMY_ID, .x, .y, angle * bj_RADTODEG )
        set .model = AddSpecialEffectTarget( SHADOW_MODEL, .u, "origin" )
        set .t = NewTimer()
        set .g = CreateGroup()
        call SetUnitZ( .u, SHADOW_HEIGHT )
        call SetTimerData( .t, this )
        call TimerStart( .t, TIMER_INTERVAL, true, function MissileControl )
        return this
    endmethod
endstruct

private struct Shadow
    Main main       = 0
    unit caster     = null
    unit u          = null
    real distance   = 0.00
    real angle      = 0.00
    real z          = 0.00
    effect model    = null
    timer t         = null
    
    method control takes nothing returns nothing
        local real x
        local real y
        local real z
        if GetWidgetLife( .caster ) <= 0.405 then
            call .destroy()
            return
        endif
        set .angle = .angle + ANGLE_CHANGE * TIMER_INTERVAL
        set .distance = .distance - SPEED * TIMER_INTERVAL
        if .distance <= 0.00 then
            call .destroy()
            return
        endif
        set x = GetUnitX( .caster ) + Cos( .angle * bj_DEGTORAD ) * .distance
        set y = GetUnitY( .caster ) + Sin( .angle * bj_DEGTORAD ) * .distance
        set z = ParabolaZ2( GetUnitZ( .caster ), .z, START_Z / 2.00, SHADOW_DISTANCE, .distance )
        call SetUnitX( .u, x )
        call SetUnitY( .u, y )
        call SetUnitZ( .u, z )
        if IsUnitInRange( .u, .main.caster, SHADOW_MIN_DIST ) then
            call .main.addCharge()
            call .destroy()
        endif
    endmethod
    
    method onDestroy takes nothing returns nothing
        call DestroyEffect( .model )
        set .model = null
        call ReleaseTimer( .t )
        set .t = null
        call KillUnit( .u )
        set .u = null
        set .caster = null
    endmethod
    
    static method create takes Main m, real angle returns thistype
        local thistype this = thistype.allocate()
        local real x = GetUnitX( m.caster )
        local real y = GetUnitY( m.caster )
        set .main = m
        set .distance = SHADOW_DISTANCE
        set .angle = angle
        set .t = NewTimer()
        set x = x + Cos( angle * bj_DEGTORAD ) * .distance
        set y = y + Sin( angle * bj_DEGTORAD ) * .distance
        set .u = CreateUnit( GetOwningPlayer( m.caster ), DUMMY_ID, x, y, 0.00 )
        call SetUnitZ( .u, START_Z )
        set .model = AddSpecialEffectTarget( SHADOW_MODEL, .u, "origin" )
        set .caster = m.caster
        set .z = GetUnitZ( .u )
        call SetTimerData( .t, this )
        call TimerStart( .t, TIMER_INTERVAL, true, function ShadowControl )
        return this
    endmethod
    
endstruct

private struct Main
    unit caster     = null
    integer level   = 1
    integer needed  = 0
    real interval   = 0.00
    timer t         = null
    boolean enough  = false
    
    method addCharge takes nothing returns nothing
        set .needed = .needed - 1
        if .needed <= 0 then
            set .enough = true
            set .interval = SHADOW_NOVA_INT
        endif
    endmethod
    
    method newShadows takes nothing returns nothing
        local integer i = 0
        local real r = 360.00 / SHADOW_COUNT
        loop
            exitwhen i > SHADOW_COUNT
            call Shadow.create( this, r * i )
            set i = i + 1
        endloop
    endmethod
    
    method nova takes nothing returns nothing
        local integer i = 0
        local real r = 360.00 / MISSILE_COUNT
        loop
            exitwhen i > MISSILE_COUNT
            call Missile.create( this, r * i )
            set i = i + 1
        endloop
    endmethod
    
    method control takes nothing returns nothing
        if GetWidgetLife( .caster ) <= 0.405 then
            call .destroy()
        endif
        set .interval = .interval - TIMER_INTERVAL
        if not .enough then
            if .interval <= 0.00 then
                call .newShadows()
                set .interval = SHADOW_INTERVAL
            endif
        else
            if .interval <= 0.00 then
                call .nova()
                call .destroy()
            endif
        endif
    endmethod
    
    method onDestroy takes nothing returns nothing
        call ReleaseTimer( .t )
        set .t = null
    endmethod
    
    static method create takes unit caster returns thistype
        local thistype this = thistype.allocate()
        set .caster = caster
        set .level = GetUnitAbilityLevel( caster, SPELL_ID )
        set .needed = NEEDED( .level )
        set .t = NewTimer()
        set .interval = SHADOW_INTERVAL
        call SetTimerData( .t, this )
        call TimerStart( .t, TIMER_INTERVAL, true, function MainControl )
        return this
    endmethod
    
endstruct

private function IsSpell takes nothing returns boolean
    if GetSpellAbilityId() == SPELL_ID then
        call Main.create( GetTriggerUnit() )
    endif
    return false
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function IsSpell ) )
    
    set t = null
endfunction

endscope

Keywords:
Shadow, darkness, dark, missile, vortex, nova
Contents

Shadow Vortex v0.01 (Map)

Reviews
16:20, 12th Mar 2010 The_Reborn_Devil: The code looks ok, but you use a rather inefficient way to update all the instances. You should just have three timers (the best would be one though :D) and use them to loop through all instead of having one...
16:20, 12th Mar 2010
The_Reborn_Devil:

The code looks ok, but you use a rather inefficient way to update all the instances. You should just have three timers (the best would be one though :D) and use them to loop through all instead of having one timer per instance.


Status: Approved
Rating: Useful
 
No need to null a trigger that stays forever as you do in init function.

The:
JASS:
    //SYSTEM
    private          Shadow         SHADOW
    private          Main           MAIN
    private          Missile        MISSILE
    private          group          GROUP               = CreateGroup()
endglobals

Should also be declared in a second globals block under the giant comment warning line.

And nice spell you've got, but you should add options for channeling and one active instance per unit option.
 
@war_Golum, did you test it? Ahh jea, please show me one which is the same. HF searching.

Okey, I could a Channeling and add some other things. I was just happy that I finished this one. ( Had a bug and couldn't find it, until I saw that I forgott a exitwhen in the pick loop .... n/c )
 
Its vjass, so you need jngp or jasshelper.

Then copy the spell trigger and the others in the folder. ( TimerUtils, BoundSentinel, Heights )

Then import the dummy.mdx.

Create a dummy with that model. Movetype flying and with the ability locust.

Now create a dummy spell, just like mine.

Then hit ctrl + d, now you should see the rawcode of the units / spells. Write them down into these globals:

//ID of the spell
private constant integer SPELL_ID = 'A000'
//ID of your dummy
private constant integer DUMMY_ID = 'h000'

Then you're finished.

cedi
 
I tried to cope it without jass but it has some errors.
I cant found where i can download the jasshelper and i dont know how to use it :/

I notices that the //ID of the spell is A001, what should i do?
 
Last edited by a moderator:
Both of the them doesn't work to my pc becuase it cant find a file :sad:
The ID of the dummy is ok. :grin:
 
Back
Top