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

Shadow Vortext v0.01

  • Like
Reactions: nerovesper
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...

Moderator

M

Moderator

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
 
Level 15
Joined
Jul 6, 2009
Messages
889
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.
 
Level 10
Joined
May 19, 2008
Messages
176
@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 )
 
Level 10
Joined
May 19, 2008
Messages
176
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
 
Level 1
Joined
Apr 19, 2008
Messages
3
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:
Top