library MirrorWard initializer Init uses TNTI,TNTK
globals
private constant integer SID = 'MrWr'
//The rawcode of the Spell
private constant integer DID = 'mwdu'
//The rawcode of the Ward
private constant integer RING_ELEMENTS = 20
//The amount of special effects which form the circle
private constant string RING_SFX = "Abilities\\Spells\\Human\\MagicSentry\\MagicSentryCaster.mdl"
//The model path of the special effect for the circle
private constant string DAMAGE_SFX = "Abilities\\Spells\\Human\\ManaFlare\\ManaFlareMissile.mdl"
//The model path of the special effect which will showup upon damaged enemies
private constant string END_SFX = "Units\\NightElf\\Wisp\\WispExplode.mdl"
//The model path of the special effect which appears when the ward is destroyed
private constant string DAMAGE_BONE= "overhead"
//The bone name where the DAMAGE_SFX will appear
private constant boolean FLASH_SFX = false
//Turn this on TRUE and the RING_SFX will created peridically (not very efficient)
//Turn this on FALSE and the RING_SFX´s will only be created when the ward spawns
//and will be removed when the ward dies
private constant real EFFECT_INTERVALL = 0.2
//The recreation intervall for the RING_SFX. You don´t need this if you turnded
//FLASH_SFX on false
private constant real DAMAGE_INTERVALL = 0.4
//The intervall in which the enemies inside the circle get damad
private constant real MOVE_SPEED = 17.5
//The movespeed of the enemy unit when it´s knocked back to the center
private constant boolean COLLISION = true
//Determines if the enemy units have collision turned on or off when the are
//knocked back to the center
//Damage Settings - Should be selfexplaining
private constant attacktype ATTACK_TYPE = null
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_UNIVERSAL
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
private group TEMP_GROUP = CreateGroup()
//An enumaration group to detect enemies
private filterfunc ENUM_FILTER
//Enumaration filter for the enemies
endglobals
//The Area of Effct of the ward
private constant function GetAoERange takes real level returns real
return level * 0 + 400
endfunction
//The amount of damage dealt to the enemies per second
private constant function GetDamage takes real level returns real
return level * 5 + 2
endfunction
//How long should the ward stay ?
private constant function GetDuration takes real level returns real
return level * 10 + 5
endfunction
private struct Spell extends Indexable
static thistype temp
real runtime = 0.03
unit ward
player owner
effect array fx [RING_ELEMENTS]
real sfx_intervall = 0.0
real dmg_intervall = 0.0
real damage
real range
real x
real y
method onDestroy takes nothing returns nothing
local integer i = 0
static if not FLASH_SFX then
loop
exitwhen i == RING_ELEMENTS
call DestroyEffect(.fx[i])
set i = i +1
endloop
endif
call DestroyEffect(AddSpecialEffect(END_SFX,.x,.y))
endmethod
//The spell ends when the ward is dead or is being removed from the game
method onBreak takes nothing returns boolean
return IsUnitType(.ward,UNIT_TYPE_DEAD) or .ward == null
endmethod
//Will draw the circle effect. The way it works depends on FLASH_SFX
method DrawRing takes nothing returns nothing
local real step = (2*bj_PI)/RING_ELEMENTS
local real rad = 0.0
local integer i = 0
loop
static if FLASH_SFX then
exitwhen rad >= 2*bj_PI
call DestroyEffect(AddSpecialEffect(RING_SFX,.x+.range*Cos(rad),.y+.range*Sin(rad)))
else
exitwhen i == RING_ELEMENTS
set .fx[i] = AddSpecialEffect(RING_SFX,.x+.range*Cos(rad),.y+.range*Sin(rad))
set i = i+1
endif
set rad = rad + step
endloop
endmethod
//Does some timing stuff and the enumarations. And may updates the circle
method onLoop takes nothing returns nothing
static if FLASH_SFX then
if .sfx_intervall <= 0. then
call .DrawRing()
set .sfx_intervall = EFFECT_INTERVALL
else
set .sfx_intervall = .sfx_intervall - .runtime
endif
endif
if .dmg_intervall <= 0. then
set .dmg_intervall = DAMAGE_INTERVALL
else
set .dmg_intervall = .dmg_intervall - .runtime
endif
set thistype.temp = this
call GroupEnumUnitsInRange(TEMP_GROUP,.x,.y,.range*1.15, ENUM_FILTER)
//NOTE: .range*1.15 will generate a buffer zone.
endmethod
//Creator method
static method create takes unit c,real x, real y returns thistype
local thistype this = thistype.allocate()
local integer lv = GetUnitAbilityLevel(c,SID)
set .owner = GetOwningPlayer(c)
set .ward = CreateUnit(.owner,DID,x,y,0)
set .damage = GetDamage(lv) * DAMAGE_INTERVALL
set .range = GetAoERange(lv)
set .x = x
set .y = y
call UnitApplyTimedLife(.ward,'BTLF',GetDuration(lv))
static if not FLASH_SFX then
call .DrawRing()
endif
return this
endmethod
endstruct
//Enumaration function to detect the enemies
private function EnumFilterFunc takes nothing returns boolean
local unit u = GetFilterUnit()
local Spell this = Spell.temp
local TNTKnock knock
local real x = GetWidgetX(u) - this.x
local real y = GetWidgetY(u) - this.y
local real d = SquareRoot(x*x+y*y)
//If the unit is a valid target
if IsUnitEnemy(u,this.owner) and not( IsUnitType(u,UNIT_TYPE_DEAD) or IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE) or IsUnitType(u,UNIT_TYPE_STRUCTURE) or IsUnitType(u,UNIT_TYPE_ANCIENT) ) then
//and it´s time to deal damage
if this.dmg_intervall <= 0. then
call UnitDamageTarget(this.ward,u,this.damage,false,false,ATTACK_TYPE,DAMAGE_TYPE,WEAPON_TYPE)
call DestroyEffect(AddSpecialEffectTarget(DAMAGE_SFX,u,DAMAGE_BONE))
endif
//If the unit is about to leave the circle
if d > this.range*0.85 and not TNTKnock.IsInMotion(u) then
//NOTE: .range*0.85 will work like a buffer zone.
set knock = TNTKnock.create()
call knock.StartLinear(u,x+this.x,y+this.y,Atan2(y,x)+bj_PI,d,MOVE_SPEED)
set knock.collision = COLLISION
endif
endif
set u = null
return false
endfunction
//Will start the spell
private function onCast takes nothing returns nothing
call Spell.create(GetTriggerUnit(),GetSpellTargetX(),GetSpellTargetY())
endfunction
//Will check for the correct rawcode
private function onCheck takes nothing returns boolean
return GetSpellAbilityId() == SID
endfunction
//Initializer
private function Init takes nothing returns nothing
local trigger trig = CreateTrigger()
set ENUM_FILTER = Filter( function EnumFilterFunc)
//Preloading cosmetics
call Preload(RING_SFX)
call Preload(DAMAGE_SFX)
call Preload(END_SFX)
call PreloadStart()
//Trigger register stuff
call TriggerRegisterAnyUnitEventBJ(trig,EVENT_PLAYER_UNIT_SPELL_CAST)
call TriggerAddCondition(trig, Filter( function onCheck))
call TriggerAddAction(trig, function onCast)
set trig = null
endfunction
endlibrary