//===============================================================================\\
// \\
// Rune Ritual \\
// by \\
// TriggerHappy \\
// \\
// \\
//===============================================================================\\
// \\
// The caster channels his inner darkness, causing runes to circle around him. \\
// It drags the nearest four units towards the runes and they are struck with \\
// lightning periodicly. \\
// \\
//===============================================================================\\
// \\
// Requires: \\
// +TimerUtils \\
// +UnitStatus \\
// +SimError (optional) \\
// \\
//===============================================================================\\
scope RuneRitual initializer onInit //requires TimerUtils, UnitStatus optional SimError
globals
private constant integer SPELL_RAW_CODE = 'A000' // The spells raw code
private constant integer RUNE_DUMMY = 'h000' // The rune dummies raw code
private constant integer DMG_TICKS = 100 // How many timer intervals before the units get damaged
private constant real RUNE_DISTANCE = 300 // How far the units are made from the caster
private constant real TIMER_PERIOD = 0.03 // How far the units are made from the caster
private constant real SPIN_SPEED = 0.03 // how fast the runes spin
private constant boolean REQUIRE_UNITS = false // Displays an error if there are no enemy units around
private constant string DAMAGE_EFFECT = "Abilities\\Spells\\Other\\Monsoon\\MonsoonBoltTarget.mdl" // the effect when a unit is damaged
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
endglobals
private constant function AOE takes integer level returns real
return (level*400.0)-((level-1)*100.0)
endfunction
private constant function DURATION takes integer level returns real
return level*5.00-(level)
endfunction
private constant function DAMAGE takes integer level returns real
return level*50.00
endfunction
//===============================================================================\\
// DO NOT EDIT BELOW THIS LINE \\
//===============================================================================\\
private keyword data
globals
private group GLOBAL_GROUP = CreateGroup()
private player TEMP_PLAYER
private data TEMP_DATA
private hashtable HASH = InitHashtable()
endglobals
native UnitAlive takes unit id returns boolean
private struct data
unit array rune[4]
unit array picked[4]
unit caster
real x
real y
real spin = 0.00
integer level
integer in = 0
integer ticks = DMG_TICKS
real array runeX[4]
real array runeY[4]
timer t
static method callback takes nothing returns nothing
local data this = GetTimerData(GetExpiredTimer())
local integer i = 0
set this.ticks = this.ticks + 1
loop
exitwhen i > 4
set this.runeX[i] = this.x+RUNE_DISTANCE*Cos((i*90) * bj_DEGTORAD+this.spin)
set this.runeY[i] = this.y+RUNE_DISTANCE*Sin((i*90) * bj_DEGTORAD+this.spin)
call SetUnitX(this.rune[i], this.runeX[i])
call SetUnitY(this.rune[i], this.runeY[i])
if this.picked[i] != null then
call SetUnitX(this.picked[i], this.runeX[i])
call SetUnitY(this.picked[i], this.runeY[i])
endif
if this.ticks >= DMG_TICKS and UnitAlive(this.picked[i]) then
call DestroyEffect(AddSpecialEffectTarget(DAMAGE_EFFECT, this.picked[i], "origin"))
call UnitDamageTarget(this.caster, this.picked[i], DAMAGE(this.level), true, false, ATTACK_TYPE, DAMAGE_TYPE, null)
endif
set i = i + 1
endloop
if this.ticks >= DMG_TICKS then
set this.ticks = 0
endif
set this.spin = this.spin+SPIN_SPEED
endmethod
static method Enemies takes nothing returns boolean
local unit u = GetFilterUnit()
local boolean b = IsUnitEnemy(u, TEMP_PLAYER) and UnitAlive(u) and not LoadBoolean(HASH, GetHandleId(u), 0)
if TEMP_DATA.in < 4 and b then
set TEMP_DATA.picked[TEMP_DATA.in] = u
call StunUnit(u, true)
call SetUnitX(u, TEMP_DATA.runeX[TEMP_DATA.in])
call SetUnitY(u, TEMP_DATA.runeY[TEMP_DATA.in])
set TEMP_DATA.in = TEMP_DATA.in + 1
call SaveBoolean(HASH, GetHandleId(u), 0, true)
endif
set u = null
return b
endmethod
static method end takes nothing returns nothing
local timer t = GetExpiredTimer()
local data this = GetTimerData(t)
call ReleaseTimer(this.t)
set this.t = t
call this.destroy()
endmethod
static method create takes unit u returns data
local data this = data.allocate()
local integer i = 0
local real x
local real y
local real d
local timer t = NewTimer()
set this.caster = u
set this.y = GetUnitY(this.caster)
set this.x = GetUnitX(this.caster)
set this.level = GetUnitAbilityLevel(this.caster, SPELL_RAW_CODE)
set this.t = t
set d = DURATION(this.level)
call SaveBoolean(HASH, GetHandleId(u), 0, true)
set TEMP_DATA = this
set TEMP_PLAYER = GetOwningPlayer(this.caster)
call GroupEnumUnitsInRange(GLOBAL_GROUP, this.x, this.y, AOE(this.level), Filter(function data.Enemies))
static if REQUIRE_UNITS then
set bj_groupCountUnits = 0
call ForGroup(GLOBAL_GROUP, function CountUnitsInGroupEnum)
if bj_groupCountUnits == 0 then
static if LIBRARY_SimError then
call SimError(TEMP_PLAYER, "There are no enemy units around.")
else
call DisplayTimedTextToPlayer(TEMP_PLAYER, 0.52, 0.96, 2.00, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n|cffffcc00There are no enemy units around.|r")
endif
call this.destroy()
return 0
endif
endif
loop
exitwhen i > 3
set x = this.x + RUNE_DISTANCE * Cos((i*90) * bj_DEGTORAD)
set y = this.y + RUNE_DISTANCE * Sin((i*90)* bj_DEGTORAD)
set this.rune[i] = CreateUnit(TEMP_PLAYER, RUNE_DUMMY, x, y, 0)
set i = i + 1
endloop
call SetTimerData(t, this)
call TimerStart(t, TIMER_PERIOD, true, function data.callback)
set t = NewTimer()
call SetTimerData(t, this)
call TimerStart(t, DURATION(this.level), false, function data.end)
return this
endmethod
method onDestroy takes nothing returns nothing
local integer i = 0
call ReleaseTimer(this.t)
call SaveBoolean(HASH, GetHandleId(this.caster), 0, false)
loop
exitwhen i > 4
call RemoveUnit(this.rune[i])
if this.picked[i] != null then
call SaveBoolean(HASH, GetHandleId(this.picked[i]), 0, false)
call StunUnit(this.picked[i], false)
set this.picked[i] = null
endif
set i = i + 1
endloop
endmethod
endstruct
private function Actions takes nothing returns boolean
local unit u = GetTriggerUnit()
if GetSpellAbilityId() == SPELL_RAW_CODE then
if LoadBoolean(HASH, GetHandleId(u), 0) then
static if LIBRARY_SimError then
call SimError(GetOwningPlayer(u), "You already have a rune ritual running.")
else
call DisplayTimedTextToPlayer(GetOwningPlayer(u), 0.52, 0.96, 2.00, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n|cffffcc00You already have a rune ritual running.|r"
endif
set u = null
return false
else
call data.create(u)
endif
endif
set u = null
return false
endfunction
private function onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function Actions))
endfunction
endscope