scope BouncingGlaives initializer init
/*************************************************************************************
* BouncingGlaves v1.0.5
* Discription: the caster hurls several glaives from his position. When the glaive hits an obstacle
* it will bounce to other direction. By default, each glaive can bounce five times if it
* keeps hitting obstacles. However, if an enemey is hit by the glaive, the glaive will
* damage the enemy before its vanishment.
*
*************************************************************************************
*
* Requires IsTerrainWalkable
*
*************************************************************************************
*
* Credits
*
* To Anitarf and Maker for makeing the IsTerrainWalkable library
* To Malhorne for making the vJass spell tutorial
* To PurgeandFire for answering all my questions
* To Vexorian for making vJass
*
************************************************************************************/
globals
//Configurations===============================================================
//The dummy ID, please do match this ID to your dummy ID.
private constant integer DUM_ID = 'h000'
//The ability ID, please match this ID to your ability ID.
private constant integer ABID = 'A000'
//The maximum number of glaives that can be hurled by the Hero. Do not set this more than 50.
private constant integer MAX_NUM_OF_GLAIVE = 30
//The number of glaive that will be hurled by the Hero at level one.
private constant integer NUM_OF_GLAIVE = 3
//How many more glaives when you level up (for each level).
private constant integer LEVEL_UP = 3
//The bouncing times of each glaive.
private constant integer BOUNCE = 5
//Whether the bouncing angle is random or not. If false, do remember to set the ANGLE bellow.
private constant boolean RANDOM_ANGLE = true
//The bouncing angle. Only works when the above RANDOM_ANGLE is false.
private constant real ANGLE = 180
//How fast the glaive moves.
private constant real SPEED = 25
//How far away the glaives will appear from the Hero.
private constant real DISTANCE = 300
//The attack type of the damage done to the enemy.
private constant attacktype AT = ATTACK_TYPE_CHAOS
//The damage type of the damage done to the enemy.
private constant damagetype DT = DAMAGE_TYPE_DEMOLITION
//The detection AOE of the glaive. If the distance between the enemy and the glaive exceeds this range, the glaive will not damage it.
private constant real RANGE = 128
//The effect when the glaive hits an enemy.
private constant string FX = "Abilities\\Spells\\Undead\\FrostArmor\\FrostArmorDamage.mdl"
//The attach point of the effect
private constant string ATTACH = "origin"
endglobals
//The damage amount upon hiting an enemy.
private constant function GetDamage takes integer level returns real
return 35. + (10*level)
endfunction
//How many glaives for each level
private constant function GetNumberofGlaives takes integer level returns integer
return NUM_OF_GLAIVE + (level -1 ) * LEVEL_UP
endfunction
//Filter out which units should be damaged.
private function FilterUnits takes unit u, player p returns boolean
return UnitAlive(u) and IsUnitEnemy(u, p)
endfunction
//Configuration Ends====================================================================
globals
private integer deindex = -1
private timer t = CreateTimer()
private group g = CreateGroup()
endglobals
native UnitAlive takes unit id returns boolean
//Uses this struct to store data.
private struct Data
integer array bounce[50] //bouncing times of a glaive
unit array dum[50] //glaive dmummy
real array angle[50] //bouncing angle
real dmg //damage when hits an enemy
real X //x coordinate of the spell casting unit
real Y //y coordinate of the spell casting unit
integer num //the number of glaives
player p
method destroy takes nothing returns nothing
if deindex == -1 then
call PauseTimer(t)
endif
set this.p = null
call this.deallocate()
endmethod
endstruct
globals
private Data array data
endglobals
private function Loop takes nothing returns nothing
local integer i = 0
local integer ii = 0
local integer iii = 0
local Data this
local real x
local real y
local boolean bb = false
local unit u
//Loop through all the instances
loop
exitwhen i > deindex
set this = data[i]
loop
exitwhen ii > this.num
//Calculate the new coordinates for the glaive
set x = GetUnitX(this.dum[ii]) + SPEED * Cos(this.angle[ii] * bj_DEGTORAD)
set y = GetUnitY(this.dum[ii]) + SPEED * Sin(this.angle[ii] * bj_DEGTORAD)
//Moves the glaive if it hasn't reach it maximum bouncing limite and the terrain is walkable
if this.bounce[ii] > 0 and IsTerrainWalkable(x,y) then
call SetUnitX(this.dum[ii],x)
call SetUnitY(this.dum[ii],y)
//Check if there are enemys near the glaive
call GroupEnumUnitsInRange(g,x,y,RANGE,null)
loop
set u = FirstOfGroup(g)
exitwhen u == null
call GroupRemoveUnit(g,u)
//If the glaive hits an enemy, does damage and remove the glaive
if FilterUnits(u,this.p) then
call UnitDamageTarget(this.dum[ii],u,this.dmg,true,false,AT,DT,null)
call DestroyEffect(AddSpecialEffectTarget(FX,u,ATTACH))
set this.bounce[ii] = 0
set u = null
//Only needs the loop to operate once, so ends it here.
exitwhen true
endif
endloop
else
//If the glaive hits an obstacle, it bounces.
static if RANDOM_ANGLE then
set this.angle[ii] = this.angle[ii] - GetRandomReal(90,180)
else
set this.angle[ii] = this.angle[ii] - ANGLE
endif
//Setting new locations for the bouncing glaive
set x = GetUnitX(this.dum[ii]) + SPEED * Cos(this.angle[ii] * bj_DEGTORAD)
set y = GetUnitY(this.dum[ii]) + SPEED * Sin(this.angle[ii] * bj_DEGTORAD)
call SetUnitX(this.dum[ii],x)
call SetUnitY(this.dum[ii],y)
//Reduce its remaining bouncing times
set this.bounce[ii] = this.bounce[ii] - 1
//If the glaive has reached its maximum bouncing times, it will be removed.
if this.bounce[ii] <= 0 then
call RemoveUnit(this.dum[ii])
set this.dum[ii] = null
//Checks if all glaives are removed
loop
exitwhen iii > this.num
if UnitAlive(this.dum[iii]) then
set bb = true
endif
set iii = iii + 1
endloop
set iii = 0
//If all glaives are removed then deallocates the instance
if not bb then
set data[i] = data[deindex]
set i = i - 1
set deindex = deindex - 1
//Ends the loop
set ii = this.num + 10
call this.destroy()
set bb = false
endif
endif
endif
set ii = ii + 1
endloop
set ii = 0
set i = i + 1
endloop
endfunction
private function onCast takes nothing returns boolean
local unit u = GetTriggerUnit()
local integer i = 0
local real angle = 0
local real newX
local real newY
local integer n
local integer lvl = GetUnitAbilityLevel(u,ABID)
local Data this = Data.create()
//Stores data in the struct
set this.p = GetTriggerPlayer()
set this.X = GetUnitX(u)
set this.Y = GetUnitY(u)
set this.num = GetNumberofGlaives(lvl) - 1
set this.dmg = GetDamage(lvl)
set n = GetNumberofGlaives(lvl)
if this.num >= MAX_NUM_OF_GLAIVE then
set this.num = MAX_NUM_OF_GLAIVE - 1
set n = MAX_NUM_OF_GLAIVE
endif
//Creates glaives=======================================
loop
exitwhen i > this.num
set newX = this.X + DISTANCE * Cos(angle * bj_DEGTORAD)
set newY = this.Y + DISTANCE * Sin(angle * bj_DEGTORAD)
set this.dum[i] = CreateUnit(this.p,DUM_ID,newX,newY,angle)
set this.bounce[i] = BOUNCE + 1
set this.angle[i] = angle
set angle = angle + 360/n
set i = i + 1
endloop
//=======================================================
set deindex = deindex + 1
set data[deindex] = this
if deindex == 0 then
call TimerStart(t,0.0312500,true,function Loop)
endif
set u = null
return false
endfunction
private function run takes nothing returns boolean
if GetSpellAbilityId() == ABID then
call onCast()
endif
return false
endfunction
//===========================================================================
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition(t, Condition(function run))
set t = null //I don't think it needs to be nulled here, but nulling it won't hurt.
endfunction
endscope