// ° Mapmaking since 2006...
// °
// °
// °
// °°°ÛÜ
// °°X°ÛÛÛ ÛÛÛÛÛÛÛÛ ÿÜÛÛÛÛÛÛÛÛÜ
// °°XX° ÛÛÛ ßÛÛÛÛÛ ÛÛÛÛÛÛÛÛÛÛÛÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛ
// °XX ° ÛÛÛÛ ÛÛÛÛÜ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
// °XX ° ÛÛÛÛÛ ÛÛÛÛ ÛÛÛÛÛß ßÛÛÛÛÛÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
// °°XXX°° ÛÛÛÛÛÛÜ ÛÛÛÜ ÛÛÛÛÛ ßÛÛÛÛÛ ßÛÛÛÛ ÛÛÛÛÛÛ
// °°°°°°°° ÛÛÛÛÛÛÛ ÛÛÛÛ ÛÛÛÛ ÛÛÛÛ ÛÛÛÛÜ
// ÛÛÛÛÛÛÛÛ ÛÛÛÛ ÛÛÛÛ ÛÛÛÛÛÛÛ ÛÛÛÛÛÜ ÛÛÛÛ
// °°°°°°°° ÛÛÛÛÛÛÛÛÛ ÛÛÛÛÜ ÛÛÛÛÜ ÛÛÛÛÛÛÛÛ ÛÛÛÛÛÜ ÜÛÛÛÛÛÛ
// °°XXXXX° ÛÛÛÛ ÛÛÛÜ ÛÛÛÛÛ ÛÛÛÛÛÛÜ ßÛÛÛÛÛ ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
// °°XXXXX °° ÛÛÛÛ ÛÛÛÛÜ ÛÛÛÛ °°°ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ°°° ÛÛÛÛÛÛÛÛÛÛÛÛÛß
// ° XXXXX °°ÛÛÛÛÛ°°°°ÛÛÛÛÛÛÛÛ°°XX ÛÛÛÛÛÛÛÛÛÛÛÛÛÛ XXX°°ÛÛÛÛÛÛÛÛÛÛÛ ÛÛÛ
// °° XXXXX ÜÛÛÛÛÛÛ X ÛÛÛÛÛÛÛÛÛ XXX ÛÛÛÛÛÛÛÛÛÛÛ XXXXXX ÛÛÛÛÛÛÛ° ÛÛÛ
// °° XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX °°°°°° °°°°
// °° XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX °°°°°X°°
// °°° XXXXXXXXXX °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° XXXXXXXXXXXXXXXXX°°
// °°°°°°°°°°°°°°°° °°°°°°°° XXXXXXX °°°
// °°°°°° XX °°
// ...2009 and still moving on. °°°°°°°
// °°
// Screen Solution: 1280x1024 Visit www.ngo.clan.su!
//================================================================================
scope ThunderWave initializer Init
//=======================================================================
//========================= SETUP START =================================
//=======================================================================
globals
//Default: 'ThWa'
private constant integer AID = 'ThWa' //The rawcode from the ability "Thunder Wave"
//Default: 'eDUM'
private constant integer DUMMY = 'eDUM' //The rawcode from the unit "Dummy Missile"
//Default: 'TWst'
private constant integer STUN = 'TWst' //The rawcode from the ability "Thunder Wave (Stun)"
//Default: "FORK"
private constant string LGTYPE = "FORK" //The lightning type of the lightning being created
//Default: "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl"
private constant string IMPACTSPX = "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl" //The path of the impact effect
//Default: "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl"
private constant string MAINSPX = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl" //The path for the orb model
//Default: "Abilities\\Spells\\Orc\\Purge\\PurgeBuffTarget.mdl"
private constant string SECSPX = "Abilities\\Spells\\Orc\\Purge\\PurgeBuffTarget.mdl" //The path for the charging effect for the orb model
//Default: "Abilities\\Weapons\\Bolt\\BoltImpact.mdl"
private constant string HITSPX = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl" //The effect spawned on enemies when successfully hitting them
//Default: "spell slam"
private constant string ANIM = "spell slam" //While casting, the caster is paused and plays this animation
//Default: 1.5
private constant real SPELLDURA = 1.5 //The duration for the whole spell
///Default: 1.5
private constant real SIZE = 1.5 //The size for the orb models
//Defaul: 200.
private constant real DISTLEVEL = 200. //This variable defines the distance level for the position of the orbs (Has only graphical meaning)
//Default: 200.
private constant real HEIGHTLEVEL = 200. //This variable defines the height level for the flying height of the orbs (Has only graphical meaning)
//Default: 150.
private constant real DEBUGADD = 150. //If the lightning is created just like that, it will look odd since the lightning doesn't really "touch" the orbs. This value will increase the height of the lightning so it looks realistic.
endglobals
private constant function GetDamage takes integer level returns real
//Default: level * 15. + 15.
return level * 15. + 15.
endfunction
private constant function GetMaxDistance takes integer level returns real
//Default: level * 75. + 400.
return level * 75. + 400.
endfunction
private constant function GetStunChance takes integer level returns real
//Default: level * 1.5 + 2.5
return level * 1.5 + 2.5
endfunction
private constant function GetImpactRadius takes integer level returns real
//Default: level * 50. + 50.
return level * 50. + 50.
endfunction
//=======================================================================
//========================= SETUP END =================================
//=======================================================================
globals
private unit TempCaster = null
endglobals
private function A2PXY takes real x,real y,real xt,real yt returns real
return ModuloReal(bj_RADTODEG*Atan2(yt-y,xt-x),360.)
endfunction
private function UnitAllowFly takes unit u returns nothing
call UnitAddAbility(u, 'Arav')
call UnitRemoveAbility(u, 'Arav')
endfunction
private function Pick takes nothing returns boolean
return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(TempCaster)) and GetUnitState(GetFilterUnit(), UNIT_STATE_LIFE) > 0.405 and IsUnitType(GetFilterUnit(), UNIT_TYPE_GROUND)
endfunction
//================================================================================
private function DealDamage takes unit Caster, integer level, real x, real y returns nothing
local group Enemies = CreateGroup()
local unit f
local unit d
set TempCaster = Caster
call GroupEnumUnitsInRange(Enemies, x, y, GetImpactRadius(level), Condition(function Pick))
loop
set f = FirstOfGroup(Enemies)
exitwhen f == null
call UnitDamageTarget(Caster, f, GetDamage(level), true, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNKNOWN, WEAPON_TYPE_WHOKNOWS)
call AddSpecialEffect(HITSPX, GetUnitX(f), GetUnitY(f))
if GetRandomReal(1, 100) < GetStunChance(level) then
set d = CreateUnit(GetOwningPlayer(Caster), DUMMY, GetUnitX(f), GetUnitY(f), 0.)
call UnitAddAbility(d, STUN)
call SetUnitAbilityLevel(d, STUN, level)
call UnitApplyTimedLife(d, 'BTLK', 0.5)
call IssueTargetOrder(d, "thunderbolt", f)
endif
call GroupRemoveUnit(Enemies, f)
endloop
call DestroyGroup(Enemies)
set Enemies = null
endfunction
//================================================================================
private struct TWData
unit array Missiles [6]
unit caster
real CenterX
real CenterY
real Angle
timer t
integer level
lightning LocLg
real DistAdd = 0.
integer Counter
effect array spx [6]
effect array spx2 [6]
static method Execute takes nothing returns nothing
local TWData data = TWData(GetTimerData(GetExpiredTimer()))
local real x
local real y
local integer i = 1
if data.Counter <= 5 then
set data.Counter = data.Counter + 1
else
if data.spx2 [1] != null then
loop
exitwhen i == 6
call DestroyEffect(data.spx2 [i])
set i = i + 1
endloop
endif
if data.Counter < 11 then
if data.LocLg != null then
call DestroyLightning(data.LocLg)
endif
set data.DistAdd = data.DistAdd + (GetMaxDistance(data.level) / 5)
set x = data.CenterX + data.DistAdd * Cos(data.Angle * bj_DEGTORAD)
set y = data.CenterY + data.DistAdd * Sin(data.Angle * bj_DEGTORAD)
set data.LocLg = AddLightningEx(LGTYPE, false, GetUnitX(data.Missiles [data.Counter - 5]), GetUnitY(data.Missiles [data.Counter - 5]), GetUnitFlyHeight(data.Missiles [data.Counter - 5]) + DEBUGADD, x, y, 0.)
call DestroyEffect(AddSpecialEffect(IMPACTSPX, x, y))
call DealDamage(data.caster, data.level, x, y)
set data.Counter = data.Counter + 1
else
call PauseTimer(data.t)
call data.destroy()
endif
endif
endmethod
static method create takes unit Caster, real CenterX, real CenterY, real Angle returns TWData
local TWData data = TWData.allocate()
local real Ang = Angle - 90
local real x = CenterX + DISTLEVEL * Cos(Ang * bj_DEGTORAD)
local real y = CenterY + DISTLEVEL * Sin(Ang * bj_DEGTORAD)
set data.Angle = Angle
set data.caster = Caster
call SetUnitFacing(data.caster, data.Angle)
set data.CenterX = CenterX
set data.CenterY = CenterY
set data.Counter = 1
set data.level = GetUnitAbilityLevel(data.caster, AID)
set data.Missiles [1] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY, x, y, 0.)
call UnitAllowFly(data.Missiles [1])
call SetUnitFlyHeight(data.Missiles [1], GetUnitFlyHeight(Caster) + 50, 0.)
call SetUnitScale(data.Missiles [1], SIZE, SIZE, SIZE)
set data.spx [1] = AddSpecialEffectTarget(MAINSPX, data.Missiles [1], "chest")
set data.spx2 [1] = AddSpecialEffectTarget(SECSPX, data.Missiles [1], "origin")
set Ang = Angle + 90
set x = CenterX + DISTLEVEL * Cos(Ang * bj_DEGTORAD)
set y = CenterY + DISTLEVEL * Sin(Ang * bj_DEGTORAD)
set data.Missiles [2] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY, x, y, 0.)
call UnitAllowFly(data.Missiles [2])
call SetUnitFlyHeight(data.Missiles [2], GetUnitFlyHeight(Caster) + 50, 0.)
call SetUnitScale(data.Missiles [2], SIZE, SIZE, SIZE)
set data.spx [2] = AddSpecialEffectTarget(MAINSPX, data.Missiles [2], "chest")
set data.spx2 [2] = AddSpecialEffectTarget(SECSPX, data.Missiles [2], "origin")
set Ang = Angle - 90
set x = CenterX + (DISTLEVEL / 2) * Cos(Ang * bj_DEGTORAD)
set y = CenterY + (DISTLEVEL / 2) * Sin(Ang * bj_DEGTORAD)
set data.Missiles [3] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY, x, y, 0.)
call UnitAllowFly(data.Missiles [3])
call SetUnitFlyHeight(data.Missiles [3], GetUnitFlyHeight(Caster) + 50 + (HEIGHTLEVEL / 2), 0.)
call SetUnitScale(data.Missiles [3], SIZE, SIZE, SIZE)
set data.spx [3] = AddSpecialEffectTarget(MAINSPX, data.Missiles [3], "chest")
set data.spx2 [3] = AddSpecialEffectTarget(SECSPX, data.Missiles [3], "origin")
set Ang = Angle + 90
set x = CenterX + (DISTLEVEL / 2) * Cos(Ang * bj_DEGTORAD)
set y = CenterY + (DISTLEVEL / 2) * Sin(Ang * bj_DEGTORAD)
set data.Missiles [4] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY, x, y, 0.)
call UnitAllowFly(data.Missiles [4])
call SetUnitFlyHeight(data.Missiles [4], GetUnitFlyHeight(Caster) + 50 +(HEIGHTLEVEL / 2), 0.)
call SetUnitScale(data.Missiles [4], SIZE, SIZE, SIZE)
set data.spx [4] = AddSpecialEffectTarget(MAINSPX, data.Missiles [4], "chest")
set data.spx2 [4] = AddSpecialEffectTarget(SECSPX, data.Missiles [4], "origin")
set data.Missiles [5] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY, CenterX, CenterY, 0.)
call UnitAllowFly(data.Missiles [5])
call SetUnitFlyHeight(data.Missiles [5], GetUnitFlyHeight(Caster) + 50 + HEIGHTLEVEL, 0.)
call SetUnitScale(data.Missiles [5], SIZE, SIZE, SIZE)
set data.spx [5] = AddSpecialEffectTarget(MAINSPX, data.Missiles [5], "chest")
set data.spx2 [5] = AddSpecialEffectTarget(SECSPX, data.Missiles [5], "origin")
call PauseUnit(data.caster, true)
call SetUnitAnimation(data.caster, ANIM)
set data.t = NewTimer()
call SetTimerData(data.t, integer(data))
call TimerStart(data.t, SPELLDURA / 10, true, function TWData.Execute)
return data
endmethod
method onDestroy takes nothing returns nothing
local integer Index = 1
call ReleaseTimer(this.t)
call SetUnitAnimation(this.caster, "stand")
call PauseUnit(this.caster, false)
loop
exitwhen Index == 6
call DestroyEffect(this.spx [Index])
call UnitApplyTimedLife(this.Missiles [Index], 'BTLK', 0.5)
set this.Missiles [Index] = null
set this.spx [Index] = null
set this.spx2 [Index] = null
set Index = Index + 1
endloop
if this.LocLg != null then
call DestroyLightning(this.LocLg)
set this.LocLg = null
endif
endmethod
endstruct
//================================================================================
private function Conditions takes nothing returns boolean
return GetSpellAbilityId() == AID
endfunction
//================================================================================
private function Actions takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local location temploc = GetSpellTargetLoc()
local real TarX = GetLocationX(temploc)
local real TarY = GetLocationY(temploc)
call TWData.create(caster, GetUnitX(caster), GetUnitY(caster), A2PXY(GetUnitX(caster), GetUnitY(caster), TarX, TarY))
call RemoveLocation(temploc)
set temploc = null
set caster = null
endfunction
//================================================================================
private function Init takes nothing returns nothing
local trigger tri = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(tri, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(tri, Condition(function Conditions))
call TriggerAddAction(tri, function Actions)
// this will remove the first cast lag
call RemoveUnit(CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY, 0.00, 0.00, 0.00))
//Preloading strings to avoid leaks
call Preload(LGTYPE)
call Preload(IMPACTSPX)
call Preload(MAINSPX)
call Preload(SECSPX)
call Preload(ANIM)
endfunction
endscope