library WrathOfZeus initializer init requires optional GroupUtils
globals
private constant integer ABILITY_ID = 'A000' //Raw code of the triggering ability
private constant integer DUMMY_ID = 'e001' //The dummy unit, please import dummy.mdl and make a proper dummy unit
private constant integer STATIC_LINKS = 32 //The links that are created when the orb crashes
private constant integer CHARGED_BOLTS_AMOUNT = 3 //The amount of orbiting missiles that are created around the caster when the ability is learned
private constant real CHARGED_BOLTS_SIZE = .88 //The scale value of the orbiting missiles
private constant real PRIMARY_BASE_DAMAGE = 130 //The base damage when the orb crashes
private constant real PRIMARY_INC_DAMAGE = 75 // The increment damage of the orb
private constant real SECONDARY_BASE_DAMAGE = 50 //The damage each static link deals
private constant real SECONDARY_INC_DAMAGE = 25 // The increment damage of each static link
private constant real BOLT_SCALE = 2.75 //The scale of the lightning bolt
private constant real NORMAL_OFFSET = 145 //The default rotating offset of the orbiting bolts
private constant real MAX_OFFSET = 425 //The maximum rotating offset the bolts can reach when overloading
private constant real ENUM_OFFSET = 75 //the range that units are checked from each static link. You should decrease this if you use a LOT of static links!
private constant string BALL_ART = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl" //The missiles that rotate around the cast
private constant string TAIL_LIGHTNING = "CLPB" //The lightning tail of the bolt
private constant string CHARGED_LIGHTNING = "CLSB" //The lightning tail of the secondary static links
private constant string ORBITAL_LIGHTNING = "DRAB" //The lightning that links all the orbital missiles
private constant string BOLT_LIGHTNING = "Abilities\\Weapons\\SpiritOfVengeanceMissile\\SpiritOfVengeanceMissile.mdl" //The lightning bolt art
private constant string PRIMARY_EFFECT = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl" //The effect that is created upon impact
private constant string SECONDARY_EFFECT = "Abilities\\Spells\\Orc\\LightningShield\\LightningShieldBuff.mdl" //The effect that is created on damaged units of secondary effect
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
///
private constant boolean USE_GROUPUTILS = true //Whether to use Group utils library, if the library isn't found, it will CreateGroup() instead
///
endglobals
////===============
globals
private location loc = Location(0, 0)
private location loc2 = Location(0, 0)
private integer instance = 0
private boolexpr BE
private constant group Group = CreateGroup()
private constant integer Cbolts = CHARGED_BOLTS_AMOUNT - 1
private real MINX
private real MINY
private real MAXX
private real MAXY
endglobals
/////=================
//Extra functions
//
private function SafeX takes real x returns real
local real rx=MINX+50
if(x<rx)then
return rx
endif
set rx=MAXX-50
if(x>rx)then
return rx
endif
return x
endfunction
private function SafeY takes real y returns real
local real ry=MINY+50
if(y<ry)then
return ry
endif
set ry=MAXY-50
if(y>ry)then
return ry
endif
return y
endfunction
/////////=======================================
private struct spark
unit u
lightning l
real ang
real sx
real sy
real x
real y
real time = 1.5
real time2 = 2
real dmg
group damaged
static integer tot = 0
static spark array ar
static timer stim = CreateTimer()
static method SecondaryEffects takes unit u, unit t, real x, real y returns nothing
local integer i = 1
local spark data
loop
exitwhen i > STATIC_LINKS
set data = spark.create()
set data.u = u
set data.sx = x
set data.sy = y
set data.x = x
set data.y = y
set data.ang = (360 / STATIC_LINKS) * i
static if USE_GROUPUTILS then
static if LIBRARY_GroupUtils then
set data.damaged = NewGroup()
else
set data.damaged = CreateGroup()
debug call BJDebugMsg(LIBRARY_PREFIX+": Could not find GroupUtils")
endif
else
set data.damaged = CreateGroup()
endif
if t != null then
call GroupAddUnit(data.damaged, t)
endif
set data.dmg = SECONDARY_BASE_DAMAGE + (SECONDARY_INC_DAMAGE * (GetUnitAbilityLevel(data.u, ABILITY_ID) - 1))
set data.l = AddLightning(CHARGED_LIGHTNING, true, x, y, x+5, y+5)
call SetLightningColor(data.l, .7, .7, 1, .7)
if spark.tot == 0 then
call TimerStart(spark.stim, .042, true, function spark.Loop)
endif
set spark.ar[spark.tot] = data
set spark.tot = spark.tot + 1
set i = i + 1
endloop
endmethod
static method Enum takes nothing returns boolean
local spark data = instance
local unit e = GetFilterUnit()
if IsUnitInGroup(e, data.damaged) then
set e = null
return false
else
if (not(IsUnitType(e, UNIT_TYPE_STRUCTURE))) and (GetWidgetLife(e)>.405) and (IsUnitEnemy(e, GetOwningPlayer(data.u))) then
call DestroyEffect(AddSpecialEffectTarget(SECONDARY_EFFECT, e, "origin"))
call UnitDamageTarget(data.u, e, data.dmg, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
call GroupAddUnit(data.damaged, e)
endif
endif
set e = null
return false
endmethod
static method Loop takes nothing returns nothing
local spark data
local integer i = 0
local real p
loop
exitwhen i >= spark.tot
set data = spark.ar[i]
if data.time > 0 then
set data.time = data.time - .042
set data.x = SafeX(data.x + 30 * Cos(data.ang * bj_DEGTORAD))
set data.y = SafeX(data.y + 30 * Sin(data.ang * bj_DEGTORAD))
set p = SquareRoot((data.x - data.sx) * (data.x - data.sx) + (data.y - data.sy) * (data.y - data.sy))
if p > 450 then
set data.sx = data.sx + 30 * Cos(data.ang * bj_DEGTORAD)
set data.sy = data.sy + 30 * Sin(data.ang * bj_DEGTORAD)
endif
set instance = data
call GroupEnumUnitsInRange(Group, data.x, data.y, ENUM_OFFSET, BE)
call MoveLocation(loc, data.sx, data.sy)
call MoveLocation(loc2, data.x, data.y)
call MoveLightningEx(data.l, true, data.sx, data.sy, GetLocationZ(loc), data.x, data.y, GetLocationZ(loc2))
else
if (SquareRoot((data.x - data.sx) * (data.x - data.sx) + (data.y - data.sy) * (data.y - data.sy)) > 50) and data.time2 > 0 then
set data.sx = data.sx + 30 * Cos(data.ang * bj_DEGTORAD)
set data.sy = data.sy + 30 * Sin(data.ang * bj_DEGTORAD)
call MoveLocation(loc, data.sx, data.sy)
call MoveLocation(loc2, data.x, data.y)
call MoveLightningEx(data.l, true, data.sx, data.sy, GetLocationZ(loc), data.x, data.y, GetLocationZ(loc2))
set instance = data
call GroupEnumUnitsInRange(Group, data.sx, data.sy, 125, BE)
set data.time2 = data.time2 - .04
else
call data.destroy()
set spark.tot = spark.tot - 1
set spark.ar[i] = spark.ar[spark.tot]
endif
endif
set i = i + 1
endloop
if spark.tot == 0 then
call PauseTimer(spark.stim)
endif
endmethod
method onDestroy takes nothing returns nothing
call DestroyLightning(.l)
static if USE_GROUPUTILS then
static if LIBRARY_GroupUtils then
call ReleaseGroup(.damaged)
else
call DestroyGroup(.damaged)
debug call BJDebugMsg(LIBRARY_PREFIX+": Could not find GroupUtils")
endif
else
call DestroyGroup(.damaged)
endif
endmethod
endstruct
private struct bolt
unit u
unit t
unit d
real damage
lightning l
effect art
static integer total = 0
static bolt array arr
static timer tim = CreateTimer()
static method create takes unit u, unit t returns bolt
local bolt data = bolt.allocate()
local real x
local real y
local real x2
local real y2
local sound snd
local string array s
local integer r = GetRandomInt(0, 1)
set s[0] = "Abilities\\Spells\\Orc\\LightningShield\\LightningShieldTarget.wav"
set s[1] = "Abilities\\Spells\\Orc\\LightningBolt\\LightningBolt.wav"
set data.u = u
set snd = CreateSound(s[r], false, false, true, 12700, 12700, "")
set x = GetUnitX(data.u) + 100 * Cos(GetUnitFacing(data.u) * bj_DEGTORAD)
set y = GetUnitY(data.u) + 100 * Sin(GetUnitFacing(data.u) * bj_DEGTORAD)
set data.d = CreateUnit(GetOwningPlayer(data.u), DUMMY_ID, x, y, 0)
call AttachSoundToUnit(snd, data.d)
set x2 = GetUnitX(data.d) + 200 * Cos((GetUnitFacing(data.u)-180) * bj_DEGTORAD)
set y2 = GetUnitY(data.d) + 200 * Sin((GetUnitFacing(data.u)-180) * bj_DEGTORAD)
set data.t = t
call UnitAddAbility(data.d, 'Amrf')
call UnitRemoveAbility(data.d, 'Amrf')
call SetUnitFlyHeight(data.d, 90, 550)
call SetUnitTimeScale(data.d, 0)
call SetUnitScale(data.d, BOLT_SCALE, BOLT_SCALE, BOLT_SCALE)
set data.l = AddLightning(TAIL_LIGHTNING, true, x2, y2, x, y)
call StartSound(snd)
call KillSoundWhenDone(snd)
set snd = null
set data.damage = PRIMARY_BASE_DAMAGE + (PRIMARY_INC_DAMAGE * (GetUnitAbilityLevel(data.u, ABILITY_ID)-1))
set data.art = AddSpecialEffectTarget(BOLT_LIGHTNING, data.d, "origin")
if bolt.total == 0 then
call TimerStart(bolt.tim, .0303, true, function bolt.Loop)
endif
set bolt.arr[bolt.total] = data
set bolt.total = bolt.total + 1
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Human\\StormBolt\\StormBoltMissile.mdl", data.u, "weapon,right"))
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl", data.d, "origin"))
return data
endmethod
static method Loop takes nothing returns nothing
local bolt data
local integer i = 0
local real p = 0
local real a=0
local real x=0
local real y=0
local real x2=0
local real y2=0
local real z=0
local real d=0
local real dx=0
local real dy=0
loop
exitwhen i >= bolt.total
set data = bolt.arr[i]
set x = GetUnitX(data.d)
set y = GetUnitY(data.d)
set x2 = GetUnitX(data.t)
set y2 = GetUnitY(data.t)
set a = bj_RADTODEG * Atan2(y2 - y, x2 - x)
call SetUnitFacing(data.d, a)
if IsUnitInRange(data.d, data.t, 50) then
call DestroyEffect(data.art)
call KillUnit(data.d)
call DestroyEffect(AddSpecialEffectTarget(PRIMARY_EFFECT, data.t, "origin"))
call UnitDamageTarget(data.u, data.t, data.damage, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
call spark.SecondaryEffects(data.u, data.t, x2, y2)
call DestroyLightning(data.l)
set data.d = CreateUnit(Player(12), DUMMY_ID, x2, y2, 0)
call SetUnitScale(data.d, 3.5, 3.5, 3.5)
call SetUnitVertexColor(data.d, 170, 170, 255, 255)
call DestroyEffect(AddSpecialEffectTarget(PRIMARY_EFFECT, data.d, "origin"))
call KillUnit(data.d)
call data.destroy()
set bolt.total = bolt.total - 1
set bolt.arr[i] = bolt.arr[bolt.total]
else
set p = SquareRoot((x - x2) * (x - x2) + (y - y2) * (y - y2))
set d = 17 + (p * .012)
///------------------------------------
set dx = SafeX(x + d * Cos(a * bj_DEGTORAD))
set dy = SafeY(y + d * Sin(a * bj_DEGTORAD))
call SetUnitX(data.d, dx)
call SetUnitY(data.d, dy)
////-----------------------------------
set x = GetUnitX(data.d)
set y = GetUnitY(data.d)
set a = (a-180)
set dx = x + 200 * Cos(a * bj_DEGTORAD)
set dy = y + 200 * Sin(a * bj_DEGTORAD)
call MoveLocation(loc, dx, dy)
call MoveLocation(loc2, x, y)
call MoveLightningEx(data.l, true, dx, dy, (GetLocationZ(loc)+90), x, y, (GetLocationZ(loc2)+GetUnitFlyHeight(data.d)))
endif
set i = i + 1
endloop
if bolt.total == 0 then
call PauseTimer(bolt.tim)
endif
endmethod
endstruct
// Charged bolts //
private struct Charged
unit u
unit array mis[CHARGED_BOLTS_AMOUNT]
real array fh[CHARGED_BOLTS_AMOUNT]
real array fi[CHARGED_BOLTS_AMOUNT]
real array x[CHARGED_BOLTS_AMOUNT]
lightning array l[CHARGED_BOLTS_AMOUNT]
lightning array l2[CHARGED_BOLTS_AMOUNT]
boolean array fb[CHARGED_BOLTS_AMOUNT]
effect array fx[CHARGED_BOLTS_AMOUNT]
boolean dead = false //If the flag is turned on, the charged bolts dissapear until the hero is alive again
real tick = 0
real xtick = .94 //Defines the duration of the lightning rotation
real rat = SquareRoot(NORMAL_OFFSET) / bj_PI
real limit = NORMAL_OFFSET
boolean charged = false
static thistype array arr
static integer total = 0
static timer tim = CreateTimer()
////
private static string str1 = "Abilities\\Spells\\Orc\\LightningShield\\LightningShieldBuff.mdl"
////
static method create takes unit u returns thistype
local thistype this = thistype.allocate()
local integer i = 0
local trigger t = CreateTrigger()
local real x
local real y
local real cx = GetUnitX(u)
local real cy = GetUnitY(u)
set this.u = u
loop
exitwhen i > Cbolts
set this.mis[i] = CreateUnit(GetOwningPlayer(u), 'e001', GetUnitX(u), GetUnitY(u), 0)
set this.fx[i] = AddSpecialEffectTarget(BALL_ART , this.mis[i], "origin")
set x = cx + 60 * Cos(( (360 / (Cbolts+1)) * (i+1) ) * bj_DEGTORAD)
set y = cy + 60 * Sin(( (360 / (Cbolts+1)) * (i+1) ) * bj_DEGTORAD)
call SetUnitX(this.mis[i], x)
call SetUnitY(this.mis[i], y)
call UnitAddAbility(this.mis[i], 'Amrf')
call SetUnitTimeScale(this.mis[i], 0)
call UnitRemoveAbility(this.mis[i], 'Amrf')
set this.fh[i] = 30 * i
call SetUnitFlyHeight(this.mis[i], this.fh[i], 0)
call SetUnitScale(this.mis[i], CHARGED_BOLTS_SIZE, CHARGED_BOLTS_SIZE, CHARGED_BOLTS_SIZE)
set this.fb[i] = true
set this.x[i] = bj_RADTODEG * Atan2(y - cy, x - cx)
set i = i + 1
endloop
call TriggerAddCondition(t, Condition(function Charged.ready))
call TriggerRegisterUnitEvent(t, u, EVENT_UNIT_SPELL_CAST)
if thistype.total == 0 then
call TimerStart(thistype.tim, .047, true, function Charged.fly)
endif
set thistype.total = thistype.total + 1
set thistype.arr[thistype.total - 1] = this
return this
endmethod
static method ready takes nothing returns boolean
local thistype this
local integer i = 0
local integer k = 0
local sound snd
if GetSpellAbilityId()!= ABILITY_ID then
return false
else
set this = thistype.GetInstance(GetTriggerUnit())
if this.charged then
return false
endif
set snd = CreateSound("Units\\Orc\\StasisTotem\\StasisTotem.wav", false, false, true, 12700, 12700, "")
call AttachSoundToUnit(snd, GetTriggerUnit())
set this.charged = true
set this.xtick = .8
set this.rat = this.rat + SquareRoot(MAX_OFFSET)
set this.limit = MAX_OFFSET
call StartSound(snd)
call KillSoundWhenDone(snd)
set snd = null
loop
exitwhen i>Cbolts
set k = i + 1
if k > Cbolts then
set k = 0
endif
set this.l[i] = AddLightning(CHARGED_LIGHTNING, true, GetUnitX(this.mis[i]), GetUnitY(this.mis[i]), GetUnitX(this.u), GetUnitY(this.u))
call SetLightningColor(this.l[i], 1, 1, 1, .6)
set this.l2[i] = AddLightning(ORBITAL_LIGHTNING, true, GetUnitX(this.mis[i]), GetUnitY(this.mis[i]), GetUnitX(this.mis[k]), GetUnitY(this.mis[k]))
call SetLightningColor(this.l2[i], .3, .1, 1, 1)
set i = i + 1
endloop
endif
return false
endmethod
static method GetInstance takes unit u returns integer
local thistype this
local integer i = 0
loop
exitwhen i >= thistype.total
set this = thistype.arr[i]
if IsUnit(u, this.u) then
return this
endif
set i = i + 1
endloop
return 0
endmethod
static method fly takes nothing returns nothing
local thistype this
local integer i = 0
local integer k = 0
local real x =0
local real y =0
local real cx
local real cy
local integer j = 0
loop
exitwhen i >= thistype.total
set this = thistype.arr[i]
set k = 0
set j = 0
set cx = GetUnitX(this.u)
set cy = GetUnitY(this.u)
loop
exitwhen k > Cbolts
set j = k + 1
if j > Cbolts then
set j = 0
endif
if (not(this.dead)) and this.mis[k] != null then
if this.fb[k] then
set this.fh[k] = this.fh[k] + 3
else
set this.fh[k] = this.fh[k] - 3
endif
if GetUnitFlyHeight(this.mis[k])<50 then
set this.fb[k] = true
elseif GetUnitFlyHeight(this.mis[k]) > 275 then
set this.fb[k] = false
endif
set this.tick = this.tick + .047
call SetUnitFlyHeight(this.mis[k], this.fh[k], 530)
set this.x[k] = this.x[k] + 4
if this.rat < this.limit then
set this.rat = this.rat + 2
elseif this.rat > this.limit then
set this.rat = this.rat - .5
endif
set x = cx + this.rat * Cos(this.x[k] * bj_DEGTORAD)
set y = cy + this.rat * Sin(this.x[k] * bj_DEGTORAD)
call SetUnitX(this.mis[k], x)
call SetUnitY(this.mis[k], y)
if this.charged then
if this.xtick > 0 then
set this.x[k] = this.x[k] + 6
call MoveLocation(loc, x, y)
call MoveLocation(loc2, GetUnitX(this.u), GetUnitY(this.u))
call MoveLightningEx(this.l[k], true, x, y, (GetLocationZ(loc)+GetUnitFlyHeight(this.mis[k])), cx, cy, (GetLocationZ(loc2)+GetUnitFlyHeight(this.u)+20))
call MoveLocation(loc2, GetUnitX(this.mis[j]), GetUnitY(this.mis[j]))
call MoveLightningEx(this.l2[k], true, x, y, (GetLocationZ(loc)+GetUnitFlyHeight(this.mis[k])), GetUnitX(this.mis[j]), GetUnitY(this.mis[j]), (GetLocationZ(loc2)+GetUnitFlyHeight(this.mis[j])))
else
call DestroyLightning(this.l[k])
call DestroyLightning(this.l2[k])
endif
endif
if this.tick > 1.75 then
set this.tick = 0
call DestroyEffect(AddSpecialEffectTarget(thistype.str1 , this.mis[k], "origin"))
endif
endif
set k = k + 1
endloop
if this.charged then
if this.xtick > 0 then
set this.xtick = this.xtick - .05
else
set this.charged = false
set this.limit = NORMAL_OFFSET
endif
endif
if GetWidgetLife(this.u)<.405 then
call this.hide()
set this.dead = true
elseif GetWidgetLife(this.u)>=.405 and this.dead then
set this.dead = false
call this.show()
endif
set i = i + 1
endloop
endmethod
method hide takes nothing returns nothing
local integer i = 0
loop
exitwhen i > Cbolts
if .mis[i] != null then
//call UnitRemoveAbility(.mis[i], 'Aloc')
call ShowUnit(.mis[i], false)
endif
set i = i + 1
endloop
endmethod
method show takes nothing returns nothing
local integer i = 0
loop
exitwhen i > Cbolts
if .mis[i] != null then
call ShowUnit(.mis[i], true)
call UnitAddAbility(.mis[i], 'Aloc')
endif
set i = i + 1
endloop
endmethod
endstruct
///////===================\\\\\\\
private function BoltStart takes nothing returns nothing
call bolt.create(GetTriggerUnit(), GetSpellTargetUnit())
endfunction
private function IsBolt takes nothing returns boolean
return GetSpellAbilityId() == ABILITY_ID
endfunction
private function BoltCharge takes nothing returns boolean
if GetLearnedSkill() == ABILITY_ID then
if GetUnitAbilityLevel(GetTriggerUnit(), ABILITY_ID)<2 then
call Charged.create(GetTriggerUnit())
endif
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 IsBolt))
call TriggerAddAction(t, function BoltStart)
set t = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_HERO_SKILL)
call TriggerAddCondition(t, Condition(function BoltCharge))
set t = null
set BE = Condition(function spark.Enum)
set MINX = GetRectMinX(bj_mapInitialPlayableArea)
set MINY = GetRectMinY(bj_mapInitialPlayableArea)
set MAXX = GetRectMaxX(bj_mapInitialPlayableArea)
set MAXY = GetRectMaxY(bj_mapInitialPlayableArea)
endfunction
endlibrary