//============================================================================================
//Black Parade v1.51 by blanc_dummy aka scorpion182
//2009
//
//Requires :
//- vJASS compiler
//- TimerUtils, by Vexorian
//- PUI by Cohadar
//- GroupUtils by Rising_Dusk
//
//
//============================================================================================
scope BLACKPARADE initializer INIT
//====Config Option===========================================================================
globals
private constant integer DUMMY_ID='e000'
private constant integer SPELL_ID='A000' //rawcode of Black Parade Ability
private constant real EXPIRATION_TIMER=45.0 //skeleton minion life time
private constant real ANIMATE_D_TIME=45.0 //animate dead unit life time
private constant integer BUFF_TIME_LIFE='Brai' //minion's buff time life
private constant integer ANIMATE_DEAD_BUFF='BUan' //buff for animate dead unit
private constant integer LEVEL=3 //how many level?
private constant string FX_IMPACT="Abilities\\Weapons\\MeatwagonMissile\\MeatwagonMissile.mdl" //effect on target when hit
private constant string FX_ANIMATE_D="Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl" //animate dead effect
private constant string FX_MISSILE="Abilities\\Weapons\\ZigguratMissile\\ZigguratMissile.mdl" //healer missile effect
private constant string FX_HEAL="Abilities\\Spells\\Demon\\DarkPortal\\DarkPortalTarget.mdl" //effect on heal
private constant real AOE=800.0 //spell AOE
private constant real RADIUS=75. //wave radius
private constant real INTERVAL=0.03125 //soul interval
private constant integer MINION=2 //how many minions create per second
private constant attacktype AT=ATTACK_TYPE_MAGIC //attack type
private constant damagetype DT=DAMAGE_TYPE_UNIVERSAL //damage type
private constant weapontype WT=WEAPON_TYPE_WHOKNOWS //weapon type
private constant string ORDER_ID="stampede" //spell order id
private integer array SKELL_ID[LEVEL]
//for damage filter
private boolexpr b
private integer array ABIL_LVL
endglobals
private function SKELETON takes nothing returns nothing
set SKELL_ID[0]='h001' //skeleton lvl 1
set SKELL_ID[1]='h002' //skeleton lvl 2
set SKELL_ID[2]='h003' //skeleton lvl 3
endfunction
private constant function damage takes integer lvl returns real
return 50.0*lvl //deal 50/100/150 damage per hit
endfunction
private constant function heal takes integer lvl returns real
return 25.0+lvl*0.0 //recover hp and mp by 25 per soul
endfunction
//======end of config=========================================================================
//=====Don't touch anything below unless you konw what are you doing!=========================
globals
private integer array UnitData
endglobals
private function AttachIntegerToUnit takes unit u, integer i returns nothing
set UnitData[GetUnitIndex(u)] = i
endfunction
private function GetIntegerFromUnit takes unit u returns integer
return UnitData[GetUnitIndex(u)]
endfunction
//struct #1 for spell wave
private struct data
unit caster
timer t
real angle=0.
group fading
real dur=0.
static method create takes unit c, timer t returns data
local data d=data.allocate()
set d.caster=c
set d.t=t
set d.fading=NewGroup()
call AttachIntegerToUnit(d.caster,GetUnitIndex(d.caster))
set ABIL_LVL[GetIntegerFromUnit(d.caster)]=GetUnitAbilityLevel(d.caster,SPELL_ID)
return d
endmethod
private method onDestroy takes nothing returns nothing
local unit f
//kill minions
loop
set f=FirstOfGroup(.fading)
exitwhen f==null
call KillUnit(f)
call GroupRemoveUnit(.fading,f)
endloop
call ReleaseTimer(.t)
call ReleaseGroup(.fading)
set f=null
endmethod
endstruct
//spell filter damage
private function filter takes nothing returns boolean
return (IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE)==false) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE)==false) and (GetWidgetLife(GetFilterUnit()) > 0.405)
endfunction
//spell casting condition
private function Conditions takes nothing returns boolean
return(GetSpellAbilityId() == SPELL_ID)
endfunction
//Function CopyGroup made by Blade.dk;BJ variables are NOT evil as many people think, they are actually faster. Only some BJ functions are evil :D
private function CopyGroup takes group g returns group
set bj_groupAddGroupDest = NewGroup()
call ForGroup(g, function GroupAddGroupEnum)
return bj_groupAddGroupDest
endfunction
//create & move skeleton minions per interval
private function Create_Skel takes nothing returns nothing
local timer t=GetExpiredTimer()
local data d=data(GetTimerData(t))
local unit f
local unit targ
local unit fnew
local real x = GetUnitX(d.caster) + GetRandomReal(-1.0*AOE, AOE) * Cos((d.angle+90.) * bj_DEGTORAD)
local real y = GetUnitY(d.caster) + GetRandomReal(-1.0*AOE, AOE) * Sin((d.angle+90.) * bj_DEGTORAD)
local group g=NewGroup()
local group victim
local group temp
local integer id=GetIntegerFromUnit(d.caster)
if (GetUnitCurrentOrder(d.caster)==OrderId(ORDER_ID)) then
//create skeleton
if(d.dur>=1.0/MINION) then
set d.dur=0.
set f=CreateUnit(GetOwningPlayer(d.caster),SKELL_ID[0],x,y,d.angle)
call UnitAddAbility(f,'Aloc')
call UnitAddType(f, UNIT_TYPE_PEON)
call UnitApplyTimedLife(f,BUFF_TIME_LIFE,6.0)
call SetUnitColor(f,PLAYER_COLOR_PURPLE)
call SetUnitAnimationByIndex(f,6)
call GroupAddUnit(d.fading,f)
endif
set g=CopyGroup(d.fading)
//move skeleton
loop
set f=FirstOfGroup(g)
exitwhen f==null
if (GetUnitState(f,UNIT_STATE_LIFE)>0) then
set x = GetUnitX(f) + 12.0 * Cos(d.angle * bj_DEGTORAD)
set y = GetUnitY(f) + 12.0 * Sin(d.angle * bj_DEGTORAD)
call SetUnitPosition(f,x,y)
call SetUnitFacingTimed(f,d.angle,0)
//check & damage
set victim=NewGroup()
set temp=NewGroup()
call GroupEnumUnitsInRange(temp,GetUnitX(f),GetUnitY(f),RADIUS,b)
loop
set targ=FirstOfGroup(temp)
exitwhen targ==null
if (IsUnitEnemy(targ,GetOwningPlayer(d.caster))) then
call GroupAddUnit(victim,targ)
endif
call GroupRemoveUnit(temp,targ)
endloop
set targ=FirstOfGroup(victim)
//minion crashed enemy
if (targ!=null) then
call DestroyEffect(AddSpecialEffectTarget(FX_IMPACT,targ,"origin"))
call UnitDamageTarget(d.caster,targ,damage(ABIL_LVL[id]),true, false, AT, DT, WT) //damage the target
call GroupRemoveUnit(d.fading,f)
set fnew=CreateUnit(GetOwningPlayer(d.caster),SKELL_ID[ABIL_LVL[id]-1],GetUnitX(f),GetUnitY(f),GetUnitFacing(f))
call AttachIntegerToUnit(fnew,id)
call RemoveUnit(f)
call UnitApplyTimedLife(fnew,BUFF_TIME_LIFE,EXPIRATION_TIMER)
endif
call ReleaseGroup(temp)
call ReleaseGroup(victim)
else
call GroupRemoveUnit(d.fading,f)
call RemoveUnit(f)
endif
call GroupRemoveUnit(g,f)
endloop
set d.dur=d.dur+0.03
call TimerStart(t,0.03,false,function Create_Skel)
else
call d.destroy()
endif
call ReleaseGroup(g)
set fnew=null
set temp=null
set targ=null
set victim=null
set g=null
set f=null
set t=null
endfunction
//spell casting actions
private function Actions takes nothing returns nothing
local timer t=NewTimer()
local data d=data.create(GetSpellAbilityUnit(),t)
local location l=GetSpellTargetLoc()
local real angle
set angle=bj_RADTODEG * Atan2(GetLocationY(l) - GetUnitY(d.caster), GetLocationX(l) - GetUnitX(d.caster))
set d.angle=angle
call SetTimerData(t,integer(d))
call TimerStart(t,0.03,false,function Create_Skel)
call RemoveLocation(l)
set l=null
set t=null
endfunction
//skeleton kill enemy cond
private function SkellD_Conditions takes nothing returns boolean
local integer lvl=GetIntegerFromUnit(GetKillingUnit())
return( GetUnitTypeId(GetKillingUnit()) == SKELL_ID[ABIL_LVL[lvl]-1] )
endfunction
//check player's hero
private function Hero_Cond takes nothing returns boolean
return IsUnitType(GetFilterUnit(), UNIT_TYPE_HERO) == true and GetWidgetLife(GetFilterUnit()) > 0.405 and GetUnitAbilityLevel(GetFilterUnit(),SPELL_ID)>0
endfunction
//struct #2 for recover HP & mana
private struct heals
unit hero
unit healer
timer t
effect fx
static method create takes unit h, unit healer, timer t returns heals
local heals d=heals.allocate()
set d.hero=h
set d.healer=healer
set d.t=t
set d.fx=AddSpecialEffectTarget(FX_MISSILE,d.healer,"origin")
call UnitAddAbility(d.healer,'Aloc')
call UnitAddAbility(d.healer,'Avul')
return d
endmethod
private method onDestroy takes nothing returns nothing
call KillUnit(.healer)
call DestroyEffect(.fx)
endmethod
endstruct
//move the souls
private function Healing takes nothing returns nothing
local timer t=GetExpiredTimer()
local heals h=heals(GetTimerData(t))
local real x1 = GetUnitX(h.healer)
local real y1 = GetUnitY(h.healer)
local real x2 = GetUnitX(h.hero)
local real y2 = GetUnitY(h.hero)
local real ang = bj_RADTODEG * Atan2(y2 - y1, x2 - x1)
local real newx = x1 + 15 * Cos(ang * bj_DEGTORAD)
local real newy = y1 + 15 * Sin(ang * bj_DEGTORAD)
local real dx=GetUnitX(h.hero) - GetUnitX(h.healer)
local real dy=GetUnitY(h.hero) - GetUnitY(h.healer)
local real dist=SquareRoot(dx * dx + dy * dy)
if GetUnitState(h.hero,UNIT_STATE_LIFE)>0 then
if (dist>80) then
//move souls
call SetUnitX(h.healer, newx)
call SetUnitY(h.healer, newy)
call SetUnitFacing(h.healer,ang)
call TimerStart(t,INTERVAL,false,function Healing)
else
//recover hp & mana
call SetUnitState(h.hero,UNIT_STATE_LIFE,GetUnitState(h.hero,UNIT_STATE_LIFE)+heal(GetUnitAbilityLevel(h.hero,SPELL_ID)))
call SetUnitState(h.hero,UNIT_STATE_MANA,GetUnitState(h.hero,UNIT_STATE_MANA)+heal(GetUnitAbilityLevel(h.hero,SPELL_ID)))
call DestroyEffect(AddSpecialEffectTarget(FX_HEAL,h.hero,"origin"))
call h.destroy()
endif
else
call h.destroy()
endif
set t=null
endfunction
//skeleton kill enemy actions
private function SkellD_Actions takes nothing returns nothing
local unit k=GetKillingUnit()
local unit f=GetDyingUnit()
local unit d
local unit healer
local group g
local group temp
local boolexpr bol
local timer t
local heals h
local integer id=GetIntegerFromUnit(k)
if (IsUnitEnemy(f,GetOwningPlayer(k)) and IsUnitType(f,UNIT_TYPE_HERO)==false and IsUnitType(f,UNIT_TYPE_STRUCTURE)==false) then
//animate death
set d=CreateUnit(GetOwningPlayer(k),GetUnitTypeId(f),GetUnitX(f),GetUnitY(f),GetUnitFacing(f))
call DestroyEffect(AddSpecialEffectTarget(FX_ANIMATE_D,d,"origin"))
call UnitApplyTimedLife(d,ANIMATE_DEAD_BUFF,ANIMATE_D_TIME)
set healer=CreateUnit(GetOwningPlayer(k),DUMMY_ID,GetUnitX(f),GetUnitY(f),bj_UNIT_FACING)
//filter group
set temp=NewGroup()
set g=NewGroup()
set bol=Condition(function Hero_Cond)
call GroupEnumUnitsOfPlayer(temp, GetOwningPlayer(d), bol)
loop
set d=FirstOfGroup(temp)
exitwhen d==null
//who the owner of the skeleton
if (GetIntegerFromUnit(d)==id) then
call GroupAddUnit(g,d)
endif
call GroupRemoveUnit(temp,d)
endloop
set d=FirstOfGroup(g)
if (d!=null) then
set t=NewTimer()
set h=heals.create(d,healer,t)
call SetTimerData(t,integer(h))
//move the souls
call TimerStart(t,INTERVAL,false,function Healing)
else
call RemoveUnit(healer)
endif
call DestroyBoolExpr(bol)
call ReleaseGroup(g)
call ReleaseGroup(temp)
endif
set temp=null
set k=null
set healer=null
set t=null
set bol=null
set g=null
set d=null
set f=null
endfunction
//init function scope, hoorayy!!
private function INIT takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition(t, Condition( function Conditions ) )
call TriggerAddAction(t, function Actions )
set t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH )
call TriggerAddCondition(t, Condition( function SkellD_Conditions ) )
call TriggerAddAction(t, function SkellD_Actions )
//init globals
set b=Condition(function filter)
call SKELETON()
set t=null
endfunction
endscope