scope RainOfArrow //version 1.04
/*---------------------------------------------------------------------------------------------------------
*
* Requires:
*
*-----------------------------------------------------------------------------------------------------------
*
* - CTL [By Nestharus]
* - WorldBounds [By Nestharus]
* - IsDestructableTree [By PitzerMike]
* - SpellEffectEvent [By Bribe]
* - RegisterPlayerUnitEvent [By Magtheridon9]
*
*-----------------------------------------------------------------------------------------------------------
*
* How to import:
*
*-----------------------------------------------------------------------------------------------------------
*
* - Copy Rain of Cold Arrows Ability from object editor ---> Abilities
* - Copy Rain of Cold Arrows (slow) Ability from objest editor ---> Abilities
* - Copy Rain of Cold Arrows (slow) Buff from object editor ---> Buffs/Effects
* - Copy Rain of Cold Arrows - Dummy 1 from objedt editor ---> Units
* - Copy Rain of Cold Arrows - Dummy 2 from object editor ---> Units
* - Copy Rain of Cold Arrows Trigger from Trigger editor
* - Copy Libraries folder from Trigger editor
*
*-----------------------------------------------------------------------------------------------------------
*
* Changelog:
*
*-----------------------------------------------------------------------------------------------------------
*
* v1.0
* - First release.
* v1.01
* - Moved Fire struct over Jump struct.
* - Removed onCast functions and inlined in to onRun function changing it's name to onCast.
* - Speed[this] now have value of global varaiable SPEED not 10 like it was before.
* - Removed unused libraries.
* v1.02
* - Added Z damage range dor now arrows can hit flying units.
* - Added dummy death effect.
* - Added SlowFilter function.
* - Now you can spawn more than arrows at once.
* - Removed GroupUtils and now using one group for damage
* - Privatized Fire struct
* v1.03
* - Removed some leaks
* - Now using one dummy to apply slow
* - Renamed some globals
* - Remove unused dummy
* v1.04
* - Changed some globals named
* - Now using global dmmy to apply debuffs
*
*-----------------------------------------------------------------------------------------------------------*/
/**************************************************************************************
* *
* CONFIGURATION *
* *
**************************************************************************************/
globals
/*Main ability rawcode*/
private constant integer SPELL_CODE = 'A000'
/*Slow ability rawcode*/
private constant integer SLOW_CODE = 'A001'
/*/Dummy - 1 rawcode*/
private constant integer DUMMY_CODE = 'h000'
/*Dummy - 2 rawcode*/
private constant integer DUMMY_CODE2 = 'h003'
/*Storm crow ability rawcode*/
private constant integer CROW_CODE = 'Arav'
/*Max jump height*/
private constant real MAX_HEIGHT = 225
/*Jump total distance traveled*/
private constant real DISTANCE = 250
/*Jump move speed*/
private constant real SPEED = 10
/*Arrows spawn in signle shot*/
private constant integer S_AMMOUT = 2
/*Distance at which archer will start shooting arrows*/
private constant real S_DISTANCE = 70
/*Jump move speed when only shooting*/
private constant real S_SPEED = 1.00
/*Shoting dummy height. It's caster's height + this value
to make arrows appear from bow not from feet*/
private constant real S_HEIGHT = 80
/*Arrows size 1 = 100%, 0.5 = 50%*/
private constant real S_SCALE = 1.10
/*Arrows will not spawn on caster*/
/*It will spawn on caster's location + this value*/
private constant real S_CREATE_OFF = 20
/*Caster's animation while shooting*/
private constant string S_ANIM = "attack"
/*Every X timer ticks (0.031250) player caster's animation*/
private constant integer S_ANIM_CD = 6
/*Every X timer ticks (0.031250) spawn arrow*/
private constant integer S_SPAWN_CD = 5
/*Arrows move speed*/
private constant real F_SPEED = 25
/*Arrows death animation when it land on the ground*/
private constant string F_ANIM = "death"
/*Arrows death effect size*/
private constant real F_D_SCALE = 1
/*Arrows death effect expiration time*/
private constant real F_D_LT = 1.2
/*Arrows death effect animation*/
private constant string F_D_ANIM = "death"
/*Arrows damage range*/
private constant real DAMAGE_RANGE = 50
/*Arrows Z damage range*/
private constant real Z_DAMAGE_RANGE = 25
/*Damage and attack type*/
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_DEATH
/*Should arrows kill trees?*/
private constant boolean DESTROY_TREE = true
/*Should arrows slow enemy targets?*/
private constant boolean SLOW = true
endglobals
/**************************************************************************************
* *
* FUNCTIONS CONFIGURATION *
* *
**************************************************************************************/
/*Damage dealt per arrow in DAMAGE_RANGE*/
private function GetDamage takes integer level returns real
return level * 15.00
endfunction
/*Arena of effect. Arrows will move from caster's location*/
/*to random point in target location based on this value*/
private function GetAoE takes integer level returns real
return level * 25.00 + 75.00
endfunction
/*Total ammout of arrows spawned*/
private function GetArrows takes integer level returns integer
return level * 5 + 15
endfunction
/*Damage filter function, you can set which units should not be damaged by spell*/
private function TargetFilter takes unit u, player p returns boolean
return IsUnitEnemy(u, p) and not IsUnitType(u, UNIT_TYPE_DEAD) and not IsUnitType(u, UNIT_TYPE_STRUCTURE)
endfunction
private function SlowFilter takes unit u, player p returns boolean
return IsUnitEnemy(u, p) and not IsUnitType(u, UNIT_TYPE_DEAD) and not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) and not IsUnitType(u, UNIT_TYPE_MECHANICAL)
endfunction
/**************************************************************************************
* *
* CONFIGURATION END *
* *
**************************************************************************************/
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 GetDist takes real x1, real y1, real x2, real y2 returns real
return SquareRoot(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)))
endfunction
private function Tree_Kill takes nothing returns boolean
local destructable dummy = GetFilterDestructable()
if IsDestructableTree(dummy) then
call KillDestructable(dummy)
endif
set dummy = null
return false
endfunction
/**************************************************************************************
* *
* ARROWS MOVEMENT *
* *
**************************************************************************************/
private struct Fire
private static unit array dummy /*Dummy*/
private static unit array sDummy /*Slow dummy*/
private static player array owner /*Dummy owner*/
private static real array targetx /*Spell Target X*/
private static real array targety /*Spell Target Y*/
private static real array distance /*Arrows Distance traveled*/
private static real array angle /*Arrows Move angle*/
private static real array speed /*Arrows Move speed*/
private static real array height /*Arrows height*/
private static real array heightDec /*Arrows height decrease*/
private static real array damage /*Arrows damage*/
private static rect array treeRect /*Rect for tree kill*/
private static group array damageGroup /*Group to pick units for damage*/
implement CTL
local unit t /*Damaged unit*/
local unit de /*Death effect dummy*/
local real tx /*Damaged unit X*/
local real ty /*Damaged unit Y*/
local real dx /*Arrow location X*/
local real dy /*Arrow location Y*/
local real mx /*Arrow move location X*/
local real my /*Arrow move location Y*/
local real th /*Target height*/
implement CTLExpire
/*Arrows move*/
set dx = GetUnitX(dummy[this])
set dy = GetUnitY(dummy[this])
set mx = dx + speed[this] * Cos(angle[this])
set my = dy + speed[this] * Sin(angle[this])
/*World bound check*/
if (mx < WorldBounds.maxX and mx > WorldBounds.minX and my < WorldBounds.maxY and my > WorldBounds.minY) then
call SetUnitX(dummy[this], mx)
call SetUnitY(dummy[this], my)
else
set distance[this] = 0
endif
/*Arrows height*/
set height[this] = height[this] - heightDec[this]
call SetUnitFlyHeight(dummy[this], height[this], 0)
set distance[this] = distance[this] - speed[this]
/*Damage*/
call GroupEnumUnitsInRange(damageGroup[this], mx, my, DAMAGE_RANGE, null)
loop
set t = FirstOfGroup(damageGroup[this])
exitwhen t == null
set th = GetUnitFlyHeight(t)
if TargetFilter(t, owner[this]) and height[this] > th - Z_DAMAGE_RANGE and height[this] < th + Z_DAMAGE_RANGE then
call UnitDamageTarget(dummy[this], t, damage[this], false, true, ATTACK_TYPE, DAMAGE_TYPE, null)
/*Slowing damaged units*/
static if SLOW then
if SlowFilter(t, owner[this]) then
call IssueTargetOrder(sDummy[this], "slow", t)
endif
endif
/*Ending arrow move*/
set distance[this] = 0
endif
call GroupRemoveUnit(damageGroup[this], t)
endloop
/*Tree Kill*/
static if DESTROY_TREE then
call SetRect(treeRect[this], mx - DAMAGE_RANGE, my - DAMAGE_RANGE, mx + DAMAGE_RANGE, my + DAMAGE_RANGE)
call EnumDestructablesInRect(treeRect[this],function Tree_Kill,null)
endif
/*Arrow move end*/
if distance[this] <= 0 then
call SetUnitAnimation(dummy[this], F_ANIM)
call UnitApplyTimedLife(dummy[this], 'BTLF', 0.75)
/*Arrows death effect*/
set de = CreateUnit(owner[this], DUMMY_CODE2, mx, my, angle[this] * bj_RADTODEG)
call SetUnitAnimation(de, F_D_ANIM)
call SetUnitScale(de, F_D_SCALE, 0, 0)
call UnitApplyTimedLife(de, 'BTLF', F_D_LT)
call SetUnitFlyHeight(de, height[this], 0)
call DestroyGroup(damageGroup[this])
call KillUnit(sDummy[this])
set de = null
set sDummy[this] = null
set dummy[this] = null
set owner[this] = null
set damageGroup[this] = null
call destroy()
endif
implement CTLNull
implement CTLEnd
static method onFire takes unit d, player p, integer lvl, real tx, real ty, real dHeight returns nothing
local thistype this = create()
local real AoE = GetAoE(lvl)
local real dx = GetUnitX(d)
local real dy = GetUnitY(d)
local real RT
set dummy[this] = d
set owner[this] = p
/*Arrow destination location*/
set targetx[this] = tx + AoE * Cos(GetRandomReal(0, 360))
set targety[this] = ty + AoE * Sin(GetRandomReal(0, 360))
set angle[this] = Atan2(targety[this] - dy, targetx[this] - dx)
set distance[this] = GetDist(dx, dy, targetx[this], targety[this])
set speed[this] = F_SPEED
set damage[this] = GetDamage(lvl)
set damageGroup[this] = CreateGroup()
/*Calcultaing time needed for arrows reach target location*/
set RT = distance[this] / speed[this]
set RT = RT * 0.031250
/*Calculating height decrease based on arrows time needed to reach target location*/
set height[this] = dHeight
set heightDec[this] = height[this] / (RT / 0.031250)
/*Tree kill rect*/
static if DESTROY_TREE then
set treeRect[this] = Rect(0, 0, 0, 0)
endif
/*Slow apply dummy*/
set sDummy[this] = CreateUnit(owner[this], DUMMY_CODE, dx, dy, 0)
call ShowUnit(sDummy[this], false)
call UnitAddAbility(sDummy[this], SLOW_CODE)
call SetUnitAbilityLevel(sDummy[this], SLOW_CODE, lvl)
endmethod
endstruct
/**************************************************************************************
* *
* ARCHER JUMP + ARROW SPAWN *
* *
**************************************************************************************/
private struct Jump extends array
private static unit array caster /*caster*/
private static player array owner /*caster's owner*/
private static real array cDistance /*Current distance for parabola function*/
private static real array mDistance /*Maximum distance for parabola funcrion*/
private static real array speed /*Caster move speed*/
private static real array angle /*Caster move angle*/
private static real array targetx /*Spell Target X*/
private static real array targety /*Spell Target Y*/
private static integer array spawnCD /*Arrows spawn cooldown*/
private static integer array spawnCCD /*Arrows spawn cooldown*/
private static integer array ammout /*Arrows total ammout*/
private static integer array count /*Arrows current ammout*/
private static integer array animCD /*Caster animation cooldown*/
private static integer array animCCD /*Caster animation cooldown*/
private static integer array level /*Caster ability level*/
implement CTL
local unit d /*Arrow dummy*/
local real cx /*Caster location X*/
local real cy /*Caster location Y*/
local real mx /*Caster move X*/
local real my /*Caster move Y*/
local real crx /*Arrow create X*/
local real cry /*Arrow create Y*/
local real height /*Caster height*/
local integer index = 0 /*For arrows spawn loop*/
implement CTLExpire
/*Caster movement*/
set cx = GetUnitX(caster[this])
set cy = GetUnitY(caster[this])
set mx = cx - speed[this] * Cos(angle[this])
set my = cy - speed[this] * Sin(angle[this])
/*World bound check*/
if (mx < WorldBounds.maxX and mx > WorldBounds.minX and my < WorldBounds.maxY and my > WorldBounds.minY) then
call SetUnitX(caster[this], mx)
call SetUnitY(caster[this], my)
endif
/*Caster height*/
set height = ParabolaZ2(0, 0, MAX_HEIGHT, mDistance[this], cDistance[this])
call SetUnitFlyHeight(caster[this], height, 0)
/*Distance traveled*/
set cDistance[this] = cDistance[this] + speed[this]
/*Shooting arrows*/
if cDistance[this] > S_DISTANCE and count[this] < ammout[this] then
/*Reducing move speed*/
set speed[this] = S_SPEED
set spawnCCD[this] = spawnCCD[this] + 1
set animCCD[this] = animCCD[this] + 1
/*Caster's animation*/
if animCCD[this] == animCD[this] then
call SetUnitAnimation(caster[this], S_ANIM)
set animCCD[this] = animCCD[this] - animCD[this]
endif
/*Arrows spawn*/
if spawnCCD[this] == spawnCD[this] then
loop
set crx = cx + S_CREATE_OFF * Cos(angle[this])
set cry = cy + S_CREATE_OFF * Sin(angle[this])
set d = CreateUnit(owner[this], DUMMY_CODE, crx, cry, angle[this] * bj_RADTODEG)
/*Arrow move struct call*/
call Fire.onFire(d, owner[this], level[this], targetx[this], targety[this], height + S_HEIGHT)
call SetUnitFlyHeight(d, height + S_HEIGHT, 0)
call SetUnitScale(d, S_SCALE, 0, 0)
/*Calculating ammout of arrows shooted*/
set count[this] = count[this] + 1
set index = index + 1
exitwhen index == S_AMMOUT or count[this] == ammout[this]
set d = null
endloop
set spawnCCD[this] = 0
endif
else
/*Changing speed to norma*/
set speed[this] = SPEED
endif
/*Jump end*/
if cDistance[this] >= mDistance[this] then
call SetUnitFlyHeight(caster[this], 0, 0)
call PauseUnit(caster[this], false)
set caster[this] = null
set owner[this] = null
call destroy()
endif
implement CTLNull
implement CTLEnd
private static method onCast takes nothing returns nothing
local thistype this = create()
local real cx
local real cy
set caster[this] = GetTriggerUnit()
set owner[this] = GetTriggerPlayer()
set level[this] = GetUnitAbilityLevel(caster[this], SPELL_CODE)
set cx = GetUnitX(caster[this])
set cy = GetUnitY(caster[this])
set targetx[this] = GetSpellTargetX()
set targety[this] = GetSpellTargetY()
set mDistance[this] = DISTANCE
set cDistance[this] = 0
set speed[this] = SPEED
set count[this] = 0
set ammout[this] = GetArrows(level[this])
set animCD[this] = S_ANIM_CD
set animCCD[this] = 0
set spawnCD[this] = S_SPAWN_CD
set spawnCCD[this] = 0
set angle[this] = Atan2(targety[this] - cy, targetx[this] - cx)
/*Crow ability for caster's fly*/
if UnitAddAbility(caster[this], CROW_CODE) then
call UnitRemoveAbility(caster[this], CROW_CODE)
endif
/*Caster pause - rly need it*/
call PauseUnit(caster[this], true)
endmethod
/*Init method*/
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_CODE,function thistype.onCast)
endmethod
endstruct
endscope