• 🏆 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 Shot v0.02

  • Like
Reactions: Ironside
Credits in map.

Creates shadows of the caster itself. Each of these shadow warriors fires one arrow. The arrow travels towards the target location and over it. Each arrow deals damage to each enemy it hits.

Jep, this spell was a bit easy, but I think it can be pretty useful and I needed this spell for my arena.

Ahh jea, you can add things like Knockback, dot, slow ... in the function called HIT_FUNC, the function is located over the DAMAGE and AOE functions.

Last but not least: Please give credits if you use this spell.

v0.02: Fixed spell icnon and description. Fixed a liddle bug which canceled the loop to early. The arrows should now fade out when they passed the target point. When they are fading out, they deal lower damage.


JASS:
scope ShadowShot initializer init

//*************************************************************************************************************//
//                                                 Shadow Shot v0.02                                           //
//                                                        by                                                   //
//                                                      cedi                                                   //
//                                                                                                             //
//                                         needs: TimerUtils by Vexorian                                       //
//                                                Bound Sentinel by Vexorian                                   //
//                                                Dummy Model by                                               //
//                                                IsTerrainWalkable by Antiarf                                 //
//                                                                                                             //
//*************************************************************************************************************//

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

private keyword Main
private keyword Sub
private keyword Arrow

globals
    //ID of the spell
    private constant integer        SPELL_ID            = 'A000'
    //ID of your dummy
    private constant integer        DUMMY_ID            = 'h000'
    //Interval of the moves
    private constant real           TIMER_INTERVAL      = 0.035
    //Shooters
    //Color in rgb
    private constant integer        S_COLOR_RED         = 255
    private constant integer        S_COLOR_GREEN       = 255
    private constant integer        S_COLOR_BLUE        = 255
    //Alpha color reached befor shot.
    private constant integer        S_COLOR_ALPHA       = 125
    //Time the units need to appear.
    private constant real           SHOW_TIME           = 1.00
    //Time the units need to disappear.
    private constant real           HIDE_TIME           = 1.00
    //Time of the attack animation.
    private constant real           ANIMATION_TIME      = 0.70
    //Size of the units
    private constant real           SHOOTER_SIZE        = 1.00
    //Z-start of the missiles
    private constant real           SHOOTER_MISSILE_Z   = 60.00
    //Place a unit needs
    private constant real           SHOOTER_COL         = 100.00
    //Max angle the units can have
    private constant real           SHOOTER_ANGLE       = 45.00
    //Animation string
    private constant string         ANIMATION           = "attack"
    //Missile
    //Color in rgb
    private constant integer        M_COLOR_RED         = 255
    private constant integer        M_COLOR_GREEN       = 255
    private constant integer        M_COLOR_BLUE        = 255
    private constant integer        M_COLOR_ALPHA       = 125
    //Pick units in each
    private constant integer        PICK_EACH_X_TIMES   = 3
    //Speed of the arrows in wc3 units
    private constant real           SPEED               = 600.00
    //Size of the missile
    private constant real           MISSILE_SIZE        = 1.00
    //Model of the missile
    private constant string         MODEL               = "Abilities\\Weapons\\Arrow\\ArrowMissile.mdl"
    //SFX on damage
    private constant string         DAMAGE_SFX          = "Objects\\Spawnmodels\\Human\\HumanBlood\\HumanBloodFootman.mdl"
    //Additionall model
    private constant string         ADD_MODEL           = "Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdl"
    //Should the missile disappear on collision with doodads cliff ...
    private constant boolean        IS_COLLISION        = false
    //System
    private          Main           TEMPMAIN
    private          Sub            TEMPSUB
    private          Arrow          TEMPARROW
    private          group          TEMPGROUP           = CreateGroup()
    private          hashtable      HASH                = InitHashtable()
endglobals

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

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

//*************************************************************************************************************//
//                                                     !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 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 OuterControl takes nothing returns nothing
    set TEMPMAIN = GetTimerData( GetExpiredTimer() )
    call TEMPMAIN.control()
endfunction

private function A_OuterControl takes nothing returns nothing
    set TEMPARROW = GetTimerData( GetExpiredTimer() )
    call TEMPARROW.control()
endfunction
   
private struct Arrow
    unit u          = null
    unit caster     = null
    effect model    = null
    effect sfx      = null
    group g         = null
    real range      = 0.00
    real dist       = 0.00
    real vx         = 0.00
    real vy         = 0.00
    real alpha      = M_COLOR_ALPHA
    real alphalose  = 0.00
    integer level   = 1
    integer times   = 0
    timer t         = null
    boolean vanish  = false
    
    method onDestroy takes nothing returns nothing
        call ReleaseTimer( .t )
        set .t = null
        call DestroyEffect( .model )
        call DestroyEffect( .sfx )
        set .model = null
        set .sfx = null
        call KillUnit( .u )
        set .u = null
        call GroupClear( .g )
        call DestroyGroup( .g )
        set .g = null
    endmethod
    
    method dealDamage takes unit u returns nothing
        call DestroyEffect( AddSpecialEffectTarget( DAMAGE_SFX, u, "chest" ) )
        if .vanish then
            call UnitDamageTarget( .caster, u, DAMAGE( .level ) * .alpha / M_COLOR_ALPHA, true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_NORMAL, null )
        else
            call UnitDamageTarget( .caster, u, DAMAGE( .level ), true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_NORMAL, null )
        endif
        call HIT_FUNC( this, u )
    endmethod
    
    method control takes nothing returns nothing
        local real x = GetUnitX( .u ) + .vx
        local real y = GetUnitY( .u ) + .vy
        local unit u
        if GetWidgetLife( .u ) <= 0.405 then
            call .destroy()
        endif
        if not .vanish then
            set .dist = .dist - SPEED * TIMER_INTERVAL
            if .dist <= 0.00 then
                set .vanish = true
            endif
        endif
        if .vanish then
            set .alpha = .alpha - .alphalose
            call SetUnitVertexColor( .u, M_COLOR_RED, M_COLOR_GREEN, M_COLOR_GREEN, R2I( .alpha + 0.5 ) )
        endif
        set .range = .range - SPEED * TIMER_INTERVAL
        call SetUnitX( .u, x )
        call SetUnitY( .u, y )
        if IS_COLLISION then
            if not IsTerrainWalkable( x, y ) then
                call .destroy()
            endif
        endif
        set .times = .times + 1
        if .times >= PICK_EACH_X_TIMES then
            set .times = 0
            call GroupEnumUnitsInRange( TEMPGROUP, x, y, AOE( .level ), Condition( function IsAliveAndUnitAndNotMagicImmune ) )
            loop
                set u = FirstOfGroup( TEMPGROUP )
                exitwhen u == null
                if IsUnitEnemy( u, GetOwningPlayer( .caster ) ) then
                    if not IsUnitInGroup( u, .g ) then
                        call .dealDamage( u )
                        call GroupAddUnit( .g, u )
                    endif
                endif
                call GroupRemoveUnit( TEMPGROUP, u )
                set u = null
            endloop
        endif
        if .range <= 0.00 then
            call .destroy()
        endif
    endmethod
    
    static method create takes Main root, real x, real y, real angle returns thistype
        local thistype this = thistype.allocate()
        set .caster = root.caster
        set .g = CreateGroup()
        set .range = root.distance * 2.00
        set .level = root.level
        set .dist = root.distance
        set .vx = Cos( angle * bj_DEGTORAD ) * SPEED * TIMER_INTERVAL
        set .vy = Sin( angle * bj_DEGTORAD ) * SPEED * TIMER_INTERVAL
        set .u = CreateUnit( GetOwningPlayer( .caster ), DUMMY_ID, x, y, angle )
        set .model = AddSpecialEffectTarget( MODEL, .u, "origin" )
        set .sfx = AddSpecialEffectTarget( ADD_MODEL, .u, "origin" )
        set .alphalose = M_COLOR_ALPHA * TIMER_INTERVAL / ( .dist / ( SPEED * TIMER_INTERVAL ) )
        call SetUnitFlyHeight( .u, SHOOTER_MISSILE_Z, 0 )
        call SetUnitScale( .u, MISSILE_SIZE, MISSILE_SIZE, MISSILE_SIZE )
        call SetUnitVertexColor( .u, S_COLOR_RED, S_COLOR_GREEN, S_COLOR_BLUE, S_COLOR_ALPHA )
        set .t = NewTimer()
        call SetTimerData( .t, this )
        call TimerStart( .t, TIMER_INTERVAL, true, function A_OuterControl )
        return this
    endmethod
    
endstruct
   
private struct Sub
    unit u      = null
    real angle  = 0.00
    Main root   = 0
    
    method onDestroy takes nothing returns nothing
        call KillUnit( .u )
        call RemoveUnit( .u )
        set .u = null
    endmethod
    
    method shot takes nothing returns nothing
        call Arrow.create( .root, GetUnitX( .u ), GetUnitY( .u ), .angle )
    endmethod
    
    method attack takes nothing returns nothing
        call SetUnitAnimation( .u, ANIMATION )
    endmethod
    
    method adjustAlpha takes nothing returns nothing
        call SetUnitVertexColor( .u, S_COLOR_RED, S_COLOR_GREEN, S_COLOR_BLUE, R2I( .root.alpha + 0.5 ) )
    endmethod
    
    static method create takes Main root, real angle returns thistype
        local thistype this = thistype.allocate()
        local real x = root.x + Cos( angle * bj_DEGTORAD ) * root.distance
        local real y = root.y + Sin( angle * bj_DEGTORAD ) * root.distance
        set .root = root
        set .angle = angle + 180.00
        set .u = CreateUnit( Player( 12 ), GetUnitTypeId( root.caster ), x, y, .angle )
        call SetUnitColor( .u, GetPlayerColor( GetOwningPlayer( root.caster ) ) )
        call SetUnitScale( .u, SHOOTER_SIZE, SHOOTER_SIZE, SHOOTER_SIZE )
        call SetUnitVertexColor( .u, S_COLOR_RED, S_COLOR_GREEN, S_COLOR_BLUE, R2I( root.alpha ) )
        call SetUnitPathing( .u, false )
        call SetUnitX( .u, x )
        call SetUnitY( .u, y )
        call UnitAddAbility( .u, 'Aloc' )
        call PauseUnit( .u, true )
        return this
    endmethod
    
endstruct
    
private struct Main
    unit caster     = null
    integer shooter = 0
    real alpha      = 0
    integer level   = 1
    real time       = 0.00
    real x          = 0.00
    real y          = 0.00
    real distance   = 0.00
    timer t         = null
    boolean show    = true
    boolean shot    = false
    boolean hide    = false
    
    method control takes nothing returns nothing
        local real r
        local integer i
        //SHOW
        if .show then
            set r = S_COLOR_ALPHA * TIMER_INTERVAL / SHOW_TIME
            set .alpha = .alpha + r
            set .time = .time - TIMER_INTERVAL
            set i = 0
            loop
                exitwhen i > .shooter
                set TEMPSUB = LoadInteger( HASH, this, i )
                call TEMPSUB.adjustAlpha()
                set i = i + 1
            endloop
            if .time <= 0.00 then
                set .show = false
                set .shot = true
                set .time = ANIMATION_TIME
                set i = 0
                loop
                    exitwhen i > .shooter
                    set TEMPSUB = LoadInteger( HASH, this, i )
                    call TEMPSUB.attack()
                    set i = i + 1
                endloop
            endif
            return
        endif
        //ATTACK
        if .shot then
            set .time = .time - TIMER_INTERVAL
            if .time <= 0.00 then
                set i = 0
                set .shot = false
                set .hide = true
                set .time = HIDE_TIME
                loop
                    exitwhen i > .shooter
                    set TEMPSUB = LoadInteger( HASH, this, i )
                    call TEMPSUB.shot()
                    set i = i + 1
                endloop
            endif
            return
        endif
        //HIDE
        if .hide then
            set r = S_COLOR_ALPHA * TIMER_INTERVAL / SHOW_TIME
            set .alpha = .alpha - r
            set .time = .time - TIMER_INTERVAL
            set i = 0
            loop
                exitwhen i > .shooter
                set TEMPSUB = LoadInteger( HASH, this, i )
                call TEMPSUB.adjustAlpha()
                set i = i + 1
            endloop
            if .time <= 0.00 then
                call .destroy()
            endif
        endif
    endmethod
    
    method onDestroy takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen i > .shooter
            set TEMPSUB = LoadInteger( HASH, this, i )
            call TEMPSUB.destroy()
            set i = i + 1
        endloop
        call FlushChildHashtable( HASH, this )
        set .caster = null
        call ReleaseTimer( .t )
        set .t = null
    endmethod
    
    static method create takes unit caster, real x, real y returns thistype
        local thistype this = thistype.allocate()
        local real distance = DistanceBetweenCoordinates( GetUnitX( caster ), x, GetUnitY( caster ), y )
        local real bogen = 2 * bj_PI * distance * ( SHOOTER_ANGLE * 2.00 ) / 360.00
        local integer count = R2I( bogen / SHOOTER_COL + 0.5 )
        local real angle = AngleBetweenCoordinates( GetUnitX( caster ), x, GetUnitY( caster ), y )
        local real r = 0.00
        local integer i = 0
        set bogen = SHOOTER_ANGLE * 2 / count
        
        set .caster = caster
        set .shooter = count
        set .level = GetUnitAbilityLevel( caster, SPELL_ID )
        set .time = SHOW_TIME
        set .alpha = 0
        set .x = x
        set .y = y
        set .t = NewTimer()
        set .distance = distance 
        call SetTimerData( .t, this )
        set r = ( angle + 180.00 ) - SHOOTER_ANGLE
        
        loop
            exitwhen i > count
            set TEMPSUB = Sub.create( this, r )
            call SaveInteger( HASH, this, i, TEMPSUB )
            set r = r + bogen
            set i = i + 1
        endloop
        
        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:
Shadow, shot, arrow, missile, ranger, archer, illusion, dark, black
Contents

Shadow Shot v0.02 (Map)

Reviews
08:21, 14th Apr 2010 TriggerHappy: Coding was fine, but it was very similar to your other spell.

Moderator

M

Moderator

08:21, 14th Apr 2010
TriggerHappy:

Coding was fine, but it was very similar to your other spell.
 
Level 5
Joined
Dec 8, 2008
Messages
102
REALLY EPIC USE OF EFFECTS <3<3<3

one little thing... 3 structs for 1 spell?
i tried to make this spell by myself and used 1 struct without problems (besides that it was 30% shorter code)

but +rep, you inspired me of implementing this into my footmen frenzy

EDIT: is it still necessary to use timerutils? now there is a "GetHandleId( handle h )" function which is better than GetTimerData(), and i think the ghost-timer-bug doesnt exist anymore
 
Level 1
Joined
Nov 30, 2008
Messages
3
I'm ind of a noob to triggered spells, and especially ones made with JASS, so can someone tell me how to import this spell onto my map? I changed the dummy and spell ID in the very beginning, but what else am I supposed to change?
 
Level 12
Joined
Apr 4, 2010
Messages
862
wtf..... headache...again... the picture looks cool....but headache its just too much.. dammit...i dont prefer JASS dude... can u make it the trigger form?

I'm ind of a noob to triggered spells, and especially ones made with JASS, so can someone tell me how to import this spell onto my map? I changed the dummy and spell ID in the very beginning, but what else am I supposed to change?

errr go download winrar then you will know..

errr go download winrar then you will know..

I am not wrong am i?? LOL not really sure...i extract the spells using winrar and import into my WE and well.... sometimes nothing happen while sometimes it works perfectly well..LOL
 
Last edited by a moderator:
Level 1
Joined
Nov 30, 2008
Messages
3
I am not wrong am i?? LOL not really sure...i extract the spells using winrar and import into my WE and well.... sometimes nothing happen while sometimes it works perfectly well..LOL

yeah i'm pretty sure you're wrong, i don't see how winrar has anything to do with importing it
 
Top