/******************************************************************************************************
* 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