scope LightningBlink initializer Init
globals
//--------------------------------------------------SETUP--------------------------------------------------
//This ability is based on blink ability to ease the triggering part. To change the maximum/minimum distance, go to Object Editor, find the ablility there, and change it.
private constant integer SpellID = 'A003' //Raw code of the spell.
private constant real ElevAngle = bj_DEGTORAD * 50. //The elevation angle between the starting point of the parabola and its maximum height in degrees (converted to radians with the constant).
private constant real DummySize = 3. //The size of the lightning orb.
private constant real OrbSpeed = 22. //Distance traveled by the lightning orb every 0.04 seconds.
private constant real Damage = 50. //Amount of damage done when the lightning orb explodes.
private constant real DamageInc = 30. //Damage increment per level.
private constant real DamageAoE = 100. //Area affected by the lightning ball's explotion.
private constant real DamageAoEInc = 50. //Area increment per level.
private constant integer JumpTotal = 1 //Total times the lightning orb can jump.
private constant integer JumpTotInc = 1 //Increase in the amount of times the orb can jump per level.
private constant real JumpAoE = 500. //The maximum range to where the orb can jump.
private constant real JumpMinD = 300. //Minimum distance between the target point and new target for the orb to jump.
private constant string DummyPath = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl" //Effect path of the dummy (lightning orb, by default).
private constant string DeathEffect = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl" //Effect created when the orb explodes.
private constant attacktype AttackT = ATTACK_TYPE_NORMAL //Attack type of the spell.
private constant damagetype DamageT = DAMAGE_TYPE_NORMAL //Damage type of the spell.
private constant weapontype WeaponT = WEAPON_TYPE_WHOKNOWS //Weapon type of the spell (never knew what it was, so I always set it to null, hehe).
private constant boolean MagicImmune = false //Wether the spell affects magic immune units.
private constant boolean Building = false //Wether the spell affects buildings.
private constant boolean Flying = false //Wether the spell affects flying units.
//------------------------------------------------SETUP END------------------------------------------------
private timer OrbTimer = CreateTimer()
private integer OrbActiveTotal = 0
private integer array OrbStructID
private unit TempU
private integer TempI
endglobals
private function DistBetweenPointsXY takes real X1, real Y1, real X2, real Y2 returns real
return SquareRoot((X2 - X1)*(X2 - X1) + (Y2 - Y1)*(Y2 - Y1))
endfunction
//--------Credits to moyack and Spec for this function!--------
function ParabolaZ takes real h, real d, real x returns real
return (4 * h / d) * (d - x) * (x / d)
endfunction
//-------------------------------------------------------------
private function RemoveUnits takes nothing returns nothing
call GroupRemoveUnit(ENUM_GROUP, GetEnumUnit())
endfunction
private function EnumFilter takes nothing returns boolean
local unit Temp = GetFilterUnit()
local boolean B1
local boolean B2
local boolean B3
if IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(TempU)) and GetWidgetLife(GetFilterUnit()) > 0.405 then
set B1 = IsUnitType(Temp, UNIT_TYPE_MAGIC_IMMUNE) == false
set B2 = IsUnitType(Temp, UNIT_TYPE_STRUCTURE) == false
set B3 = IsUnitType(Temp, UNIT_TYPE_FLYING) == false
static if MagicImmune then
set B1 = true
endif
static if Building then
set B2 = true
endif
static if Flying then
set B3 = true
endif
if B1 and B2 and B3 then
return true
endif
endif
return false
endfunction
private function DamageTargets takes nothing returns boolean
local unit Temp = GetFilterUnit()
local boolean B1
local boolean B2
local boolean B3
if IsUnitEnemy(Temp, GetOwningPlayer(TempU)) and GetWidgetLife(Temp) > 0.405 then
set B1 = IsUnitType(Temp, UNIT_TYPE_MAGIC_IMMUNE) == false
set B2 = IsUnitType(Temp, UNIT_TYPE_STRUCTURE) == false
set B3 = IsUnitType(Temp, UNIT_TYPE_FLYING) == false
static if MagicImmune then
set B1 = true
endif
static if Building then
set B2 = true
endif
static if Flying then
set B3 = true
endif
if B1 and B2 and B3 then
call UnitDamageTarget(TempU, Temp, Damage + DamageInc * (TempI - 1), true, false, AttackT, DamageT, WeaponT)
endif
endif
set Temp = null
return false
endfunction
private struct L_Orb
private unit Caster
private unit Target
private xefx OrbDummy
private group Affected
private integer Jumps = 0
private integer JumpTotal
private integer AbilityLevel
private real Distance = 0.
private real CenterX
private real CenterY
private real Cosine
private real Sine
private real MaxD
static method create takes unit caster, location targetloc returns L_Orb
local L_Orb o = L_Orb.allocate()
local real TargetX = GetLocationX(targetloc)
local real TargetY = GetLocationY(targetloc)
set o.Affected = NewGroup()
set o.Caster = caster
set o.CenterX = GetUnitX(o.Caster)
set o.CenterY = GetUnitY(o.Caster)
set o.OrbDummy = xefx.create(o.CenterX, o.CenterY, 0.)
set o.OrbDummy.fxpath = DummyPath
set o.OrbDummy.scale = DummySize
set o.AbilityLevel = GetUnitAbilityLevel(o.Caster, SpellID)
set o.Cosine = Atan2(TargetY - o.CenterY, TargetX - o.CenterX)
set o.Sine = Sin(o.Cosine)
set o.Cosine = Cos(o.Cosine)
set o.MaxD = DistBetweenPointsXY(o.CenterX, o.CenterY, TargetX, TargetY)
set o.JumpTotal = o.JumpTotal + (JumpTotInc * o.AbilityLevel - JumpTotInc)
if OrbActiveTotal == 0 then
call TimerStart(OrbTimer, 0.04, true, function L_Orb.Move)
endif
set OrbStructID[OrbActiveTotal] = o
set OrbActiveTotal = OrbActiveTotal + 1
return o
endmethod
private static method Move takes nothing returns nothing
local L_Orb TempStruct
local integer Count = 0
local real TempR
local real PointX
local real PointY
local unit Temp
loop
exitwhen Count >= OrbActiveTotal
set TempStruct = OrbStructID[Count]
set TempStruct.Distance = TempStruct.Distance + OrbSpeed
set PointX = TempStruct.CenterX + TempStruct.Distance * TempStruct.Cosine
set PointY = TempStruct.CenterY + TempStruct.Distance * TempStruct.Sine
set TempR = DistBetweenPointsXY(TempStruct.CenterX, TempStruct.CenterY, PointX, PointY)
set TempStruct.OrbDummy.x = PointX
set TempStruct.OrbDummy.y = PointY
set TempStruct.OrbDummy.z = ParabolaZ(Tan(ElevAngle) * TempStruct.MaxD/2., TempStruct.MaxD, TempR)
if TempR >= TempStruct.MaxD then
call TempStruct.Explode(TempStruct.Caster, PointX, PointY)
if TempStruct.Jumps >= TempStruct.JumpTotal then
call TempStruct.OrbDummy.destroy()
set OrbActiveTotal = OrbActiveTotal - 1
set OrbStructID[Count] = OrbStructID[OrbActiveTotal]
if OrbActiveTotal == 0 then
call PauseTimer(OrbTimer)
endif
set Count = Count - 1
call TempStruct.destroy()
else
set TempU = TempStruct.Caster
call GroupEnumUnitsInRange(ENUM_GROUP, PointX, PointY, JumpAoE, Condition(function EnumFilter))
set TempU = null
call GroupRemoveUnit(ENUM_GROUP, TempStruct.Target)
call GroupRefresh(TempStruct.Affected)
call ForGroup(TempStruct.Affected, function RemoveUnits)
loop
set Temp = FirstOfGroup(ENUM_GROUP)
if Temp == null then
call TempStruct.OrbDummy.destroy()
set OrbActiveTotal = OrbActiveTotal - 1
set OrbStructID[Count] = OrbStructID[OrbActiveTotal]
if OrbActiveTotal == 0 then
call PauseTimer(OrbTimer)
endif
set Count = Count - 1
call TempStruct.destroy()
exitwhen true
elseif DistBetweenPointsXY(TempStruct.OrbDummy.x, TempStruct.OrbDummy.y, GetUnitX(Temp), GetUnitY(Temp)) >= JumpMinD then
call GroupAddUnit(TempStruct.Affected, Temp)
set TempStruct.CenterX = TempStruct.OrbDummy.x
set TempStruct.CenterY = TempStruct.OrbDummy.y
set TempStruct.Distance = 0.
set TempStruct.Cosine = Atan2(GetUnitY(Temp) - TempStruct.CenterY, GetUnitX(Temp) - TempStruct.CenterX)
set TempStruct.Sine = Sin(TempStruct.Cosine)
set TempStruct.Cosine = Cos(TempStruct.Cosine)
set TempStruct.MaxD = DistBetweenPointsXY(TempStruct.CenterX, TempStruct.CenterY, GetUnitX(Temp), GetUnitY(Temp))
set TempStruct.Jumps = TempStruct.Jumps + 1
exitwhen true
else
call GroupRemoveUnit(ENUM_GROUP, Temp)
endif
endloop
endif
endif
set Count = Count + 1
endloop
endmethod
private method Explode takes unit caster, real locx, real locy returns nothing
local unit Temp
local real PointX
local real PointY
local real Distance = 0
local integer Count = 0
local integer Count2 = 0
call DestroyEffect(AddSpecialEffect(DeathEffect, locx, locy))
loop
set Count = Count + 1
set Distance = Distance + 70.
loop
set Count2 = Count2 + 1
set PointX = locx + Distance * Cos(Count2 * bj_PI/4.)
set PointY = locy + Distance * Sin(Count2 * bj_PI/4.)
call DestroyEffect(AddSpecialEffect(DeathEffect, PointX, PointY))
exitwhen Count2 == 8
endloop
set Count2 = 0
exitwhen Count == 3
endloop
set TempU = this.Caster
set TempI = this.AbilityLevel
call GroupEnumUnitsInRange(ENUM_GROUP, locx, locy, DamageAoE + (DamageAoEInc * this.AbilityLevel - DamageAoEInc), Condition(function DamageTargets))
set TempU = null
endmethod
private method onDestroy takes nothing returns nothing
set .Caster = null
set .Target = null
call ReleaseGroup(.Affected)
endmethod
endstruct
private function ConditionsActions takes nothing returns nothing
if GetSpellAbilityId() == SpellID then
call L_Orb.create(GetTriggerUnit(), GetSpellTargetLoc())
endif
endfunction
private function Init takes nothing returns nothing
local trigger L_OrbTrig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(L_OrbTrig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddAction(L_OrbTrig, function ConditionsActions)
call XE_PreloadAbility(SpellID)
call Preload(DeathEffect)
set L_OrbTrig = null
endfunction
endscope