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

Dark Agility v0.02

Credits in map.

Description: The ranger charges towards the target point and releases a great amoun of arrows. Each arrow deals some damage to its target.

When I created this spell, I wanted first make only a normal slide but that was to lame for me, so I created a slide with random arrow shot on enemies witch are near the slide path.

The slide stops on collsion with buildings cliff ...

Have fun and give credits if you use.

v0.02: Fixed a bug with the pathabiliy check.


JASS:
scope DarkAgility initializer init

//*************************************************************************************************************//
//                                                Dark Agility v0.01                                           //
//                                                        by                                                   //
//                                                      cedi                                                   //
//                                                                                                             //
//                                         needs: TimerUtils by Vexorian                                       //
//                                                Bound Sentinel by Vexorian                                   //
//                                                Dummy Model by                                               //
//                                                IsTerrainWalkable by Antiarf                                 //
//                                                IsUnitNearLine by peq                                        //
//*************************************************************************************************************//

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

private keyword Main
private keyword Shadow
private keyword Arrow

globals
    //ID of the spell
    private constant integer        SPELL_ID            = 'A000'
    private constant integer        DUMMY_ID            = 'h000'
    private constant integer        HERO_DUMMY_ID       = 'h001'
    //Interval of the moves
    private constant real           TIMER_INTERVAL      = 0.01
    //Main
    private constant integer        COLOR_RED           = 125
    private constant integer        COLOR_GREEN         = 125
    private constant integer        COLOR_BLUE          = 125
    private constant integer        COLOR_ALPHA         = 255
    private constant integer        ANIMATION           = 6
    private constant real           SPEED               = 1000.00
    private constant real           ILLU_TIME           = 0.5
    private constant real           SHADOW_INTERVAL     = 0.035
    private constant real           PICK_INTERVAL       = 0.20
    private constant real           ANI_TIME            = 1.334
    private constant real           ANI_SPEED           = 15.00
    private constant real           ANI_SPEED_SHADOW    = 1.00
    private constant real           SHADOW_SIZE         = 1.00
    private constant real           Z_START             = 60.00
    private constant string         DAMAGE_SFX          = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl"
    //Arrow
    private constant integer        A_COLOR_RED         = 255
    private constant integer        A_COLOR_GREEN       = 255
    private constant integer        A_COLOR_BLUE        = 255
    private constant integer        A_COLOR_ALPHA       = 125
    private constant real           SHOOT_RANGE         = 600.00
    private constant real           SHOOT_SPEED         = 600.00
    private constant real           SHOOT_AOE           = 10.00
    private constant real           SHOOT_SIZE          = 1.00
    private constant string         SHOOT_MODEL         = "Abilities\\Weapons\\Arrow\\ArrowMissile.mdl"
    private constant string         SHOOT_ADD           = "Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdl"
    private constant string         SHOOT_DMG_SFX       = "Objects\\Spawnmodels\\Human\\HumanBlood\\HumanBloodFootman.mdl"
    //System
    private          Main           TEMPMAIN
    private          Arrow          TEMPARROW
    private          Shadow         TEMPSHADOW
    private          group          TEMPGROUP           = CreateGroup()
endglobals

private function HIT_FUNC takes Main m, unit target returns nothing
    //Do your uber crazy things, like knockback, dot, slow ...
endfunction

private function ARROW_HIT_FUNC takes Arrow a returns nothing
    //Do your uber crazy things, like knockback, dot, slow ... //Care arrow gets destroyed after call
endfunction

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

private function ARROW_DAMAGE takes integer level returns real
    return 100.00 + 50 * level
endfunction

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

private function CD takes integer level returns real
    return 0.10 - 0.01 * level
endfunction

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

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 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 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 A_OuterControl takes nothing returns nothing
    set TEMPARROW = GetTimerData( GetExpiredTimer() )
    call TEMPARROW.control()
endfunction

private function OuterControl takes nothing returns nothing
    set TEMPMAIN = GetTimerData( GetExpiredTimer() )
    call TEMPMAIN.control()
endfunction
    
private function RemoveShadow takes nothing returns nothing
    local timer t = GetExpiredTimer()
    set TEMPSHADOW = GetTimerData( t )
    set TEMPSHADOW.alpha = TEMPSHADOW.alpha - TEMPSHADOW.alphaneg
    call SetUnitVertexColor( TEMPSHADOW.u, COLOR_RED, COLOR_GREEN, COLOR_BLUE, R2I( TEMPSHADOW.alpha - 0.5 ) )
    if TEMPSHADOW.alpha <= 0.00 then
        call KillUnit( TEMPSHADOW.u )
        call RemoveUnit( TEMPSHADOW.u )
        call ReleaseTimer( t )
    endif
    set t = null
endfunction

private struct Shadow
    unit u          = null
    real alpha      = 0.00
    real alphaneg   = 0.00
    
    static method create takes unit u returns thistype
        local thistype this = thistype.allocate()
        set .u = u
        set .alpha = COLOR_ALPHA
        set .alphaneg = .alpha * TIMER_INTERVAL / ILLU_TIME
        return this
    endmethod
    
endstruct

private struct Arrow
    unit u          = null
    unit caster     = null
    unit target     = null
    integer level   = 1
    effect model    = null
    effect add      = null
    timer t         = null
    
    method onDestroy takes nothing returns nothing
        call DestroyEffect( AddSpecialEffectTarget( SHOOT_DMG_SFX, .target, "chest" ) )
        call UnitDamageTarget( .caster, .target, ARROW_DAMAGE( .level ), true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_NORMAL, null )
        call ARROW_HIT_FUNC( this )
        call DestroyEffect( .model )
        call DestroyEffect( .add )
        set .model = null
        set .add = null
        call KillUnit( .u )
        set .u = null
        set .caster = null
        set .target = null
        call ReleaseTimer( .t )
        set .t = null
    endmethod
    
    method control takes nothing returns nothing
        local real angle = AngleBetweenUnits( .u, .target )
        local real x = GetUnitX( .u )
        local real y = GetUnitY( .u )
        local real z = GetUnitFlyHeight( .target ) - GetUnitFlyHeight( .u )
        local integer times = 0
        call SetUnitFacing( .u, angle )
        set angle = angle * bj_DEGTORAD
        set x = x + Cos( angle ) * SHOOT_SPEED * TIMER_INTERVAL
        set y = y + Sin( angle ) * SHOOT_SPEED * TIMER_INTERVAL
        set times = R2I( DistanceBetweenCoordinates( GetUnitX( .u ), x, GetUnitY( .u ), y ) / ( SHOOT_SPEED * TIMER_INTERVAL ) + 0.5 )
        call SetUnitX( .u, x )
        call SetUnitY( .u, y )
        if IsUnitInRange( .u, .target, SHOOT_AOE ) then
            call .destroy()
            return
        endif
        if z != 0.00 then
            call SetUnitFlyHeight( .u, GetUnitFlyHeight( .u ) + z / times, 0 )
        endif
    endmethod
    
    static method create takes unit caster, unit target, integer level returns thistype
        local thistype this = thistype.allocate()
        set .caster = caster
        set .target = target
        set .u = CreateUnit( GetOwningPlayer( caster ), DUMMY_ID, GetUnitX( caster ), GetUnitY( caster ), 0.00 )
        set .level = level
        set .model = AddSpecialEffectTarget( SHOOT_MODEL, .u, "origin" )
        set .add = AddSpecialEffectTarget( SHOOT_ADD, .u, "origin" )
        set .t = NewTimer()
        call SetUnitFlyHeight( .u, Z_START, 0 )
        call SetTimerData( .t, this )
        call TimerStart( .t, TIMER_INTERVAL, true, function A_OuterControl )
        call SetUnitScale( .u, SHOOT_SIZE, SHOOT_SIZE, SHOOT_SIZE )
        call SetUnitVertexColor( .u, A_COLOR_RED, A_COLOR_GREEN, A_COLOR_BLUE, A_COLOR_ALPHA )
        return this
    endmethod
    
endstruct

private struct Main
    unit caster      = null
    real targx       = 0.00
    real targy       = 0.00
    real x           = 0.00
    real y           = 0.00
    real vx          = 0.00
    real vy          = 0.00
    real angle       = 0.00
    real distance    = 0.00 //Distance moved
    real fdistance   = 0.00 //Full distance
    real pickint     = 0.00
    real shadowint   = 0.00
    real aniint      = 0.00
    real shotint     = 0.00
    integer level    = 1
    integer i        = 0
    timer t          = null
    group g          = null
    group targets    = null
    
    method shot takes nothing returns nothing
        local unit u = GroupPickRandomUnit( .targets )
        if u != null then
            call Arrow.create( .caster, u, .level )
        endif
        set u = null
    endmethod
    
    method createShadow takes nothing returns nothing
        local timer t = NewTimer()
        local unit u = CreateUnit( Player( 12 ), HERO_DUMMY_ID, .x, .y, .angle )
        set TEMPSHADOW = Shadow.create( u )
        call SetUnitTimeScale( u, ANI_SPEED_SHADOW )
        call SetUnitAnimationByIndex( u, ANIMATION )
        call SetUnitScale( u, SHADOW_SIZE, SHADOW_SIZE, SHADOW_SIZE )
        call SetUnitVertexColor( u, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_ALPHA )
        call SetTimerData( t, TEMPSHADOW )
        call TimerStart( t, TIMER_INTERVAL, true, function RemoveShadow )
    endmethod
    
    method pick takes nothing returns nothing
        local unit u = null
        call GroupEnumUnitsInRange( TEMPGROUP, .x, .y, AOE( .level ), Condition( function IsAliveAndUnitAndNotMagicImmune ) )
        loop
            set u = FirstOfGroup( TEMPGROUP )
            if IsUnitEnemy( u, GetOwningPlayer( .caster ) ) then
                if not IsUnitInGroup( u, .g ) then
                    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 )
                    call GroupAddUnit( .g, u )
                endif
            endif
            call GroupRemoveUnit( TEMPGROUP, u )
            set u = null
        endloop
    endmethod
    
    method control takes nothing returns nothing
        if .i == 3 then
            call PauseUnit( .caster, true )
        else
            set .i = .i + 1
        endif
        if .distance >= .fdistance then
            call .destroy()
        endif
        set .distance = .distance + SPEED * TIMER_INTERVAL
        set .x = .x + .vx
        set .y = .y + .vy
        call SetUnitX( .caster, .x )
        call SetUnitY( .caster, .y )
        
        set .shotint = .shotint + TIMER_INTERVAL
        if .shotint >= CD( .level ) then
            set .shotint = 0.00
            call .shot()
        endif
        
        set .pickint = .pickint + TIMER_INTERVAL
        if .pickint >= PICK_INTERVAL then
            set .pickint = 0.00
            call .pick()
        endif
        
        set .shadowint = .shadowint + TIMER_INTERVAL
        if .shadowint >= SHADOW_INTERVAL then
            set .shadowint = 0.00
            call .createShadow()
        endif
        
        set .aniint = .aniint + TIMER_INTERVAL
        if .aniint >= ANI_TIME / ANI_SPEED then
            set .aniint = 0.00
            call SetUnitAnimationByIndex( .caster, ANIMATION )
        endif
    endmethod
    
    method onDestroy takes nothing returns nothing
        call SetUnitAnimation( .caster, "stand" )
        call SetUnitTimeScale( .caster, 1.00 )
        call PauseUnit( .caster, false )
        call SetUnitInvulnerable( .caster, false )
        call SetUnitPathing( .caster, true )
        set .caster = null
        call GroupClear( .g )
        call DestroyGroup( .g )
        set .g = null
        call GroupClear( .targets )
        call DestroyGroup( .targets )
        set .targets = null
        call ReleaseTimer( .t )
        set .t = null
    endmethod
    
    static method create takes unit caster, real targx, real targy returns thistype
        local thistype this = thistype.allocate()
        local integer i
        local real x
        local real y 
        local unit u
        set .caster = caster
        set .targx = targx
        set .targy = targy
        set .x = GetUnitX( caster )
        set .y = GetUnitY( caster )
        set .level = GetUnitAbilityLevel( caster, SPELL_ID )
        set .t = NewTimer()
        call SetTimerData( .t, this )
        set .angle = AngleBetweenCoordinates( .x, targx, .y, targy )
        set .distance = DistanceBetweenCoordinates( .x, targx, .y, targy )
        set .distance = .distance / ( SPEED * TIMER_INTERVAL )
        set .vx = Cos( .angle * bj_DEGTORAD ) * SPEED * TIMER_INTERVAL
        set .vy = Sin( .angle * bj_DEGTORAD ) * SPEED * TIMER_INTERVAL
        set i = R2I( .distance + 0.5 )
        set x = .x
        set y = .y
        set .g = CreateGroup()
        set .targets = CreateGroup()
        call GroupAddUnitsNearLine( .targets, .x, .y, .targx, .targy, SHOOT_RANGE )
        call SetUnitInvulnerable( .caster, true )
        call SetUnitPathing( .caster, false )
        loop
            exitwhen i < 0
            set x = x + .vx
            set y = y + .vy
            if not IsTerrainWalkable( x, y ) then
                set .targx = x
                set .targy = y
            endif
            set i = i - 1
        endloop
        set .distance = 0.00
        set .fdistance = DistanceBetweenCoordinates( .x, .targx, .y, .targy )
        call GroupAddGroup( .targets, TEMPGROUP )
        loop
            set u = FirstOfGroup( TEMPGROUP )
            exitwhen u == null
            if GetUnitTypeId( u ) != DUMMY_ID and GetUnitTypeId( u ) != HERO_DUMMY_ID and IsUnitEnemy( u, GetOwningPlayer( .caster ) ) and GetWidgetLife( u ) > 0.405 and IsUnitType(u, UNIT_TYPE_STRUCTURE) == false and IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) == false then
                //good target
            else
                //not good
                call GroupRemoveUnit( .targets, u )
            endif
            call GroupRemoveUnit( TEMPGROUP, u )
            set u = null
        endloop
        call SetUnitTimeScale( .caster, ANI_SPEED )
        call SetUnitAnimationByIndex( .caster, ANIMATION )
        call TimerStart( .t, TIMER_INTERVAL, true, function OuterControl )
        return this
    endmethod
    
endstruct

private function IsSpell takes nothing returns nothing
    if GetSpellAbilityId() == SPELL_ID then
        call Main.create( GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY() )
    endif
endfunction

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

endscope

Keywords:
Slide, Move, Arrow, missile, shoot, dark, shadow,
Contents

Dark Agility v0.02 (Map)

Reviews
08:17, 14th Apr 2010 TriggerHappy: Coding seemed fine and the spell was nice.

Moderator

M

Moderator

08:17, 14th Apr 2010
TriggerHappy:

Coding seemed fine and the spell was nice.
 
Level 12
Joined
May 21, 2009
Messages
994
I don't see that you require the libraries (TimerUtils etc..) when creating the library? Have I missed something?
I am glad that you use TimerUtils and also post the code from start :).
Seriously I can't read the code with this strange new syntax on the hive.
 
Top