• 🏆 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 Leap v1.0.0.3

Shadow Leap
SLIC_zpsc957d84a.jpg
SLTT_zps6e1c69ef.jpg



JASS:
/******************************************************************************************************
*  Shadow Leap v1.0.0.3
*  by Maker
*  
*  ~--------------------------------------------------~ 
*  ~ The caster leaps to the targeted point and then  ~
*  ~ on nearby enemy land units several times. The    ~
*  ~ target is randomized. In the end the hero leaps  ~
*  ~ to the point where he casted the spell. The hero ~
*  ~ gains 100% evasion and spell immunity.           ~
*  ~--------------------------------------------------~
*
*  Importing
*  ------------
*
*  ¤ Copy Shadow Leap, Evasion, Spell immunity and the spell book abilities into your map
*  ¤ Make sure the Evasion and Spell immunity abilities are in the spell book
*  ¤ Copy the spell trigger and the resources folder into your map
*  ¤ Set the AID and SBID variables in this spell, in globals to match the raw codes of
*    the abilities in your map. You can check raw codes by pressing Ctrl + D in object editor
*
*  Requirements
*  ------------
*  ShadowTrail by Maker
*   - http://www.hiveworkshop.com/forums/spells-569/shadow-trail-system-v1-1-1-7-a-230421/
*  Jump by Maker
*   - http://www.hiveworkshop.com/forums/spells-569/jump-system-v-1-0-1-4-a-230294/
*  CTL by Nestharus
*   - http://www.hiveworkshop.com/forums/jass-resources-412/snippet-constant-timer-loop-32-a-201381/
*  Table by Bribe
*   - http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*  IsTerrainWalkable by anitarf and Vexorian
*   - http://pastebin.com/0zjeaBc7
*  SimError by Vexorian
*   - http://www.wc3c.net/showthread.php?t=101260
*
*  Optional
*  -----------
*  GetUnitCollision by Nestharus
*   - http://www.hiveworkshop.com/forums/jass-resources-412/snippet-getunitcollision-180495/
*  Knockback3D by Cokemonkey11
*   - http://www.hiveworkshop.com/forums/jass-resources-412/system-knockback3d-217056/
*  SpellEffectEvent by Bribe
*   - http://www.hiveworkshop.com/forums/jass-resources-412/snippet-spelleffectevent-187193/
*  RegisterPlayerUnitEvent by Magtheridon
*   - http://www.hiveworkshop.com/forums/jass-resources-412/snippet-registerplayerunitevent-203338/
*  IsDestructableTree by PitzerMike
*   - http://www.wc3c.net/showthread.php?t=103927
*
*  Other
*  -----------
*  Icon:    BTNThunderClap by Mr.Goblin
*  Effect:  Dirt Explosion by WILLTHEALMIGHTY
*
******************************************************************************************************/


scope ShadowLeap

    globals
        // Effect for landing
        private constant string     EFFECT              ="war3mapImported\\NewDirtEXNofire.mdx"
        // Error message for invalid location
        private constant string     ERROR               = "Can't leap there."
        // The Shadow Leap ability
        private constant integer    AID                 = 'A000'
        // The Shadow Leap Spell Book ability
        private constant integer    SBID                = 'A003'
        // Order id for the ability
        private constant integer    OID                 = 852121
        // How many times the unit leaps
        private constant integer    LEAPS_BASE          = 3
        // How many times the unit leaps, bonus per lvl
        private constant integer    LEAPS_BONUS         = 1
        // Leap animation index for the caster
        private constant integer    ANIM_INDEX          = 12
        // Leap animation index for the dummies
        private constant integer    ANIM_INDEX_DUMMY    = 6
        // How quickly the shadow trail fades
        private constant integer    FADE_RATE           = 5
        // How often a new shadow unit is created
        private constant real       SHADOW_INTERVAL     = 0.06250
        // How much gravity affects the unit when leaping
        private constant real       GRAVITY             = 100
        // How fast the leap moves
        private constant real       SPEED               = 500
        // Max height of the leap
        private constant real       MAX_HEIGHT          = 350
        // Area of effect when the leap lands
        private constant real       AOE                 = 200
        // Minimum duration of the leap in seconds
        private constant real       MIN_DUR             = 0.4
        // How much the leap distance affects the duration, a multiplier
        private constant real       DISTANCE_FACTOR     = 0.1
        // How far the leaper detects new targets
        private constant real       LEAP_RANGE          = 500
        // The base damage of the spell
        private constant real       DMG_BASE            = 50.
        // Bonus damage per lvl, not applied at lvl 1
        private constant real       DMG_BONUS           = 35.
        // Knockback velocity
        private constant real       KB_VEL              = 650.
        // Does the ability destroy trees
        private constant boolean    CUT_TREES           = true
        // Does the ability knock back units
        private constant boolean    KNOCKBACK           = true
        
        private constant attacktype ATY                 = ATTACK_TYPE_NORMAL
        private constant damagetype DTY                 = DAMAGE_TYPE_NORMAL
        private constant weapontype WTY                 = WEAPON_TYPE_ROCK_HEAVY_BASH
        
        private group ENU = CreateGroup()
        private rect REC = Rect(0, 0, 0, 0)
        private Table TAB
    endglobals

    struct JumpingStomp extends array
    
        private real x          // X of where spell was casted first
        private real y          // Y of where spell was caster first
        private unit u          // The caster
        private unit l          // Last targeted unit
        private real dmg        // How much damage the leap does
        private integer leaps   // How many leaps are left
        private ShadowTrail st  // The shadow trail attached to the caster
        
        private static unit array un
        private static integer array rn
        private static integer ic = 0
    
        static if LIBRARY_IsDestructableTree and CUT_TREES then
        private static method DestructableFilter takes nothing returns boolean
            if IsDestructableTree(GetFilterDestructable()) then
                call KillDestructable(GetFilterDestructable())
            endif
            return false
        endmethod
        endif
        
        private method destroy takes nothing returns nothing
            call UnitRemoveAbility(this.u, SBID)
            set this.u = null
            set this.l = null
            call TAB.remove(GetHandleId(this.u))
            set rn[this] = rn[0]
            set rn[0] = this
        endmethod
    
        private static method onFinish takes nothing returns boolean
            local real x0
            local real y0
            local real x1
            local real y1
            local real time
            local unit t
            local player p
            local integer i
            local integer k
            local Jump j
            local ShadowTrail st
            local thistype this = TAB[GetHandleId(EVENT_JUMP_UNIT)]
            
            call this.st.remove() // Destroys the shadow trail
            
            if not (IsUnitType(EVENT_JUMP_UNIT, UNIT_TYPE_DEAD) or GetUnitTypeId(EVENT_JUMP_UNIT) == 0) then
                set x0 = GetUnitX(EVENT_JUMP_UNIT)
                set y0 = GetUnitY(EVENT_JUMP_UNIT)
                call DestroyEffect(AddSpecialEffect(EFFECT, x0, y0))
                static if LIBRARY_IsDestructableTree and CUT_TREES then
                    call SetRect(REC, x0-AOE, y0-AOE, x0+AOE, y0+AOE)
                    call EnumDestructablesInRect(REC, function thistype.DestructableFilter, null)
                endif
                set p = GetOwningPlayer(this.u)
                call GroupEnumUnitsInRange(ENU, x0, y0, LEAP_RANGE, null)
                
                set i = -1  // Counts valid targets
                loop
                    set t = FirstOfGroup(ENU)
                    exitwhen t == null
                    if  not             (                                /*
                    */  IsUnitType      (t, UNIT_TYPE_STRUCTURE)    or   /*
                    */  IsUnitType      (t, UNIT_TYPE_DEAD)         or   /*
                    */  IsUnitType      (t, UNIT_TYPE_FLYING)       or   /*
                    */  IsUnitHidden    (t)                         )    /*
                    */  and IsUnitEnemy (t, p)                      then
                    
                        /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~/
                        /~  This block puts valid targets in to an array ~/
                        /~  and counts how many there are. This is done  ~/
                        /~  to randomize the next target                 */
                        if this.leaps > 0 then
                            set i = i + 1
                            set un[i] = t
                        endif
                        /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
                        
                        if IsUnitInRange(EVENT_JUMP_UNIT, t, AOE) then
                            call UnitDamageTarget(this.u, t, this.dmg, true, false, ATY, DTY, WTY)
                            static if LIBRARY_Knockback3D and KNOCKBACK then
                                call print("kb", 5)
                                call Knockback3D_add(t, KB_VEL, Atan2(GetUnitY(t)-y0, GetUnitX(t)-x0), 0)
                            endif
                        endif
                    endif
                    call GroupRemoveUnit(ENU, t)
                endloop
                
                if this.leaps > 0 then
                    set st = ShadowTrail.add(this.u, TRAIL_TYPE_STATIC, 0)
                    call st.setInterval(SHADOW_INTERVAL)
                    call st.setColor(150, 150, 150, 150)
                    call st.setAnimationIndex(ANIM_INDEX_DUMMY)
                    call st.setFadeRate(FADE_RATE, true)
                    call st.setPlayerColor(ConvertPlayerColor(15))
                    set this.st = st
                    
                    call SetUnitAnimationByIndex(this.u, ANIM_INDEX)
                    
                    set this.leaps = this.leaps - 1
                    
                    // Get new random target if there are leaps left
                    // and there is a valid target nearby
                    if this.leaps != 0 and i != -1 then
                        // Randomize a unit from the array of valid targets
                        set k = GetRandomInt(0, i)
                        // If the randomized target is the same unit that
                        // was targeted for the last jump and there is at
                        // least one other valid target, randomize new target
                        if un[k] == this.l and i > 0 then
                            set un[k] = un[i]
                            set this.l = un[GetRandomInt(0, i-1)]
                        else
                            set this.l = un[k]
                        endif
                        set x1 = GetUnitX(this.l)
                        set y1 = GetUnitY(this.l)
                    else
                        set x1 = this.x
                        set y1 = this.y
                        set this.leaps = 0
                    endif
                    
                    call SetUnitFacing(u, Atan2(y1-y0, x1-x0)*bj_RADTODEG)
                    set time = MIN_DUR + DISTANCE_FACTOR * SquareRoot( (x1-x0)*(x1-x0) + (y1-y0)*(y1-y0) ) / SPEED
                    set j = Jump.start(u, x1, y1, MAX_HEIGHT, time, GRAVITY)
                    call j.setUnitCollisionHeight(0)
                    call j.setDestructableCollisionDistance(0)
                else
                    call IssueImmediateOrderById(this.u, 851972) // Stop
                    call this.destroy()
                endif
            else
                call this.destroy()
            endif
            return false
        endmethod
        
        private static method create takes nothing returns thistype
            local Jump j
            local unit u = GetTriggerUnit()
            local real x0 = GetUnitX(u)
            local real y0 = GetUnitY(u)
            local real x1 = GetSpellTargetX()
            local real y1 = GetSpellTargetY()
            local real time = MIN_DUR + DISTANCE_FACTOR * SquareRoot( (x1-x0)*(x1-x0) + (y1-y0)*(y1-y0) ) / SPEED
            local ShadowTrail st
            
            local thistype this = rn[0]
            if this == 0 then
                set ic = ic + 1
                set this = ic
            else
                set rn[0] = rn[this]
            endif
            
            /* --------- Static Shadow Trail ---------- */ 
            set st = ShadowTrail.add(u, TRAIL_TYPE_STATIC, 0)
            call st.setInterval(SHADOW_INTERVAL)
            call st.setColor(150, 150, 150, 150)
            call st.setAnimationIndex(ANIM_INDEX_DUMMY)
            call st.setFadeRate(FADE_RATE, true)
            call st.setPlayerColor(ConvertPlayerColor(15))
            /* ---------------------------------------- */
            
            set this.u = u
            set this.x = x0
            set this.y = y0
            set this.st = st
            set this.leaps = (GetUnitAbilityLevel(u, AID) - 1)*LEAPS_BONUS + LEAPS_BASE
            set this.dmg = (GetUnitAbilityLevel(u, AID) - 1)*DMG_BONUS + DMG_BASE
            
            set TAB[GetHandleId(u)] = this
            call UnitAddAbility(u, SBID)
            set j = Jump.start(u, x1, y1, MAX_HEIGHT, time, GRAVITY)
            call j.setUnitCollisionHeight(0)
            call j.setDestructableCollisionDistance(0)
            
            set u = null
            return this
        endmethod
        
        private static method onOrder takes nothing returns boolean
            if GetIssuedOrderId() == OID and not IsPointJumpable(GetOrderPointX(), GetOrderPointY()) then
                call PauseUnit(GetTriggerUnit(), true)
                call IssueImmediateOrderById(GetTriggerUnit(), 851972)
                call PauseUnit(GetTriggerUnit(), false)
                call SimError(GetTriggerPlayer(), ERROR)
            endif
            return false
        endmethod
        
        static if not LIBRARY_SpellEffectEvent then
        private static method castCondition takes nothing returns boolean
            if GetSpellAbilityId() == AID then
                call thistype.create()
            endif
            return false
        endmethod
        endif
        
        private static method onInit takes nothing returns nothing
            local integer i
            local trigger t = CreateTrigger()
            call TriggerRegisterVariableEvent(t, "EVENT_JUMP_FINISH", EQUAL, 1)
            call TriggerAddCondition(t, function thistype.onFinish)
            
            static if LIBRARY_RegisterPlayerUnitEvent then
                call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function thistype.onOrder)
            else
                set t = CreateTrigger()
                call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
                call TriggerAddCondition(t, function thistype.onOrder)
            endif
            
            static if LIBRARY_SpellEffectEvent then
                call RegisterSpellEffectEvent(AID, function thistype.create)
            else
                set t = CreateTrigger()
                call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
                call TriggerAddCondition(t, function thistype.castCondition)
            endif
            
            set TAB = Table.create()
            
            set i = 0
            loop
                call SetPlayerAbilityAvailable(Player(i), SBID, false)
                exitwhen i == 15
                set i = i + 1
            endloop
        endmethod
    endstruct
endscope



v1.0.0.0 uploaded @ 04.06.2013
v1.0.0.1 uploaded @ 05.06.2013
-Destroys the instance if the caster dies during the spell
v1.0.0.2 uploaded @ 08.06.2013
-Small code improvements
-The caster no longer keeps the ability order after finishing the spell
-Some previously required libraries are now optional
v1.0.0.3 uploaded @ 09.11.2013
-No longer checks unit type id of targets
-Knckback was not applied ever, fixed
-Added a boolean to enable/disable knockback


Keywords:
leap, jump, shadow, maker, stomp
Contents

Shadow Leap v1.0.0.3 (Map)

Reviews
20:58, 7th Jun 2013 PurgeandFire: All changes made. Approved! :) A great spell with wonderful effects. Plus, it makes all the necessary checks for playable maps!

Moderator

M

Moderator

20:58, 7th Jun 2013
PurgeandFire: All changes made. Approved! :) A great spell with wonderful effects. Plus, it makes all the necessary checks for playable maps!
 
Level 37
Joined
Mar 6, 2006
Messages
9,240
Hmmm, your spells just like Jass course, teach me the Jass....... >.<

Sorry, but I'm not available for teaching.

Hey maker really nice looking. Is it possible to edit it so he jumps only once and then come back?(how hard will be I dont know jass)

Thanks. Edit these
JASS:
        // How many times the unit leaps
        private constant integer    LEAPS_BASE          = 3
        // How many times the unit leaps, bonus per lvl
        private constant integer    LEAPS_BONUS         = 1

Set base to 1 and bonus to 0, then it should jump only once.

LOL. That looks awesome.

Thanks.
 
This has really awesome effects. :) Fun to cast too.

Review:
  • After the spell is over, the unit is still under the order of the ability and his animation is paused. It seems that you issue the stop order, but perhaps you should try issuing the order by it's ID instead. (851972)
  • SpellEffectEvent and RegisterPlayerUnitEvent should probably be optional requirements since there are already quite a few requirements. Just use static if LIBRARY_SpellEffectEvent then or whatever the syntax is.
  • In static method create: GetPlayerColor(Player(15)) is equivalent to ConvertPlayerColor(15). Same thing for static method onFinish.
  • In static method create: Use u in ShadowTrail.add instead of GetTriggerUnit()
  • In static method onFinish: You can inline local player p since you only use it once.
  • In static method onFinish: This:
    JASS:
    set x1 = GetUnitX(t)
    set y1 = GetUnitY(t)
    if (x1-x0)*(x1-x0) + (y1-y0)*(y1-y0) <= AOE2 then
    Can be reduced to:
    JASS:
    if IsUnitInRange(EVENT_JUMP_UNIT, t, AOE) then
    And you'll also be able to eliminate the variable AOE2.
    For the line with the knockback 3D, just change y1 and x1 to GetUnitX/Y(t)
  • In static method onFinish: You don't seem to use the variable f.

One those changes are made it will be approved. Great job with this, it is a great spell and I admire the conditions you check (whether it is flying, hidden, etc.) which make this very portable. :)
 
Level 37
Joined
Mar 6, 2006
Messages
9,240
Many thanks for the review, Purge. Updated.



  • After the spell is over, the unit is still under the order of the ability and his animation is paused. It seems that you issue the stop order, but perhaps you should try issuing the order by it's ID instead.
Ordering by id did the trick. I was so blind not seeing the order being still on because I did give the "stop" order, and thought that would do the trick.

  • SpellEffectEvent and RegisterPlayerUnitEvent should probably be optional requirements
Done, IsDestructableTree is now also optional.

  • In static method create: GetPlayerColor(Player(15)) is equivalent to ConvertPlayerColor(15).
  • In static method create: Use u in ShadowTrail.add instead of GetTriggerUnit()
  • if IsUnitInRange(EVENT_JUMP_UNIT, t, AOE) then
  • In static method onFinish: You don't seem to use the variable f
Done.

  • In static method onFinish: You can inline local player p since you only use it once.
Didn't change this, the p might be used multiple times, once per unit inside the AoE.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
In my mind, in your create method
local unit u = GetTriggerUnit() local real x0 = GetUnitX(u) local real y0 = GetUnitY(u)
could be directly assigned to this.u,x,y instead of
set this.u = u set this.x = x0 set this.y = y0

Ofc you have to move local real time calculation or do it here:
set j = Jump.start(u, x1, y1, MAX_HEIGHT, time, GRAVITY)
time is used once anyway.
Aswell as the ShadowTrail block, since it uses unit u --> this.u

Ofc these are just peanuts, or I'm missing something. :)

Independently, this spell is lovely :)
 
Level 37
Joined
Mar 6, 2006
Messages
9,240
In my mind, in your create method
local unit u = GetTriggerUnit() local real x0 = GetUnitX(u) local real y0 = GetUnitY(u)
could be directly assigned to this.u,x,y instead of
set this.u = u set this.x = x0 set this.y = y0

Ofc you have to move local real time calculation or do it here:
set j = Jump.start(u, x1, y1, MAX_HEIGHT, time, GRAVITY)
time is used once anyway.
Aswell as the ShadowTrail block, since it uses unit u --> this.u

Ofc these are just peanuts, or I'm missing something. :)

Independently, this spell is lovely :)

Thanks for the comment.

The local for unit is used so that i don't have to do this

local real x
set this.u = GetTriggerUnit()
set x = GetUnitX(this.u)

I can do this instead

local unit u = GetTriggerUnit()
local real x = GetUnitX(u)

The time variable is used so the Jump.start call stays cleaner.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
The time variable is used so the Jump.start call stays cleaner.
True that!

I was refering to this:
local unit u = GetTriggerUnit() local real x0 = GetUnitX(u) local real y0 = GetUnitY(u) //... some stuff between set this.u = u set this.x = x0 set this.y = y0
Setting this.u = GetTriggerUnit() this.x = GetUnitX(this.u) this.y = GetUnitY(this.u) would let you go without those 3 locals ( u, x0,y0) . Just local real time can't be set directly then.
That would be local real time
set time = MIN_DUR + DISTANCE_FACTOR * SquareRoot( (x1-this.x)*(x1-this.x) + (y1-this.y)*(y1-this.y) ) / SPEED

I don't want to argue about that, since it seems irrelevant to me, which way it is done.
I just wanted to share my point of view.
 

Deleted member 219079

D

Deleted member 219079

Nice spell, but it pushes hover-styled air units over pathing blockers and stuff, any solution to this?
 
Top