scope ArrowStorm
//requires Bound Sountinel By Vexorian : [url]http://www.wc3c.net/showthread.php?t=102576[/url]
//Feel free to comment this native if you already have it in your map
native UnitAlive takes unit id returns boolean
//CONFIGURATION
globals
//The periodic check timer, if you don't know what to do let is as it is.
private constant real FPS = 0.0312500
//The id of the spell
private constant integer SPELL_ID = 'C000'
//The id of the primary missile
private constant integer ID_PRIMARY = 'l000'
//The id of the secondary missile
private constant integer ID_SECONDARY = 'l001'
//The speed of the primary missile
private constant real SPEED_PRIMARY = 950.
//The speed of the secondary missile
private constant real SPEED_SECONDARY = 650.
//The amount of units the missile should start from the caster
private constant real START_OFFSET = 150.
//The radius around the primary missile the units should get damaged
private constant real RADIUS_PRIMARY = 150.
//The radius around the secondary missile the units should get damaged
private constant real RADIUS_SECONDARY = 100.
//The attacktype of the damage
private constant attacktype A_TYPE =ATTACK_TYPE_MAGIC
//The damagetype of the damage
private constant damagetype D_TYPE = DAMAGE_TYPE_MAGIC
//The weapontype of the damage
private constant weapontype W_TYPE = null
//The path of the effect each time the primary missile create secondary missile
private constant string PATH = "Abilities\\Weapons\\ProcMissile\\ProcMissile.mdl"
//In radian
//The angle between each secondary missile upon creating
private constant real SECONDARY_OFFSET = bj_PI/4
//This will tell you how many secondary missiles it will create
//If you wan to change it usually you have to change the precedent constant.
//It works with a 2*pi reference.
//Dividing 2*pi by the SECONDARY_OFFSET
private constant integer NUMBER_SECONDARY = R2I(bj_PI*2/SECONDARY_OFFSET)
endglobals
//How many damage should the primary missile deal
private constant function DamagePrimary takes integer level returns real
return 55.*level
endfunction
//How many damage should the secondary missile deal
private constant function DamageSecondary takes integer level returns real
return 15.*level
endfunction
//The distance the primary missile should travel before the end
private constant function DistancePrimary takes integer level returns real
return 750.+50.*level
endfunction
//The distance the secondary missile should travel before the end
private constant function DistanceSecondary takes integer level returns real
return 300.+25.*level
endfunction
//The filter function
private function Unit_Filter takes player source, unit targ returns boolean
return (UnitAlive(targ)) and (not IsUnitType(targ, UNIT_TYPE_MAGIC_IMMUNE)) and IsUnitEnemy(targ, source)
endfunction
//END CONFIGURATION
//MAD CODING AFTER D: !
//Textmacro for allocating because I'm lazy ^^
//! textmacro AS_Alloc takes prefix
if thistype(0).prev == 0 then
set count = count + 1
set this$prefix$ = count
else
set this$prefix$ = thistype(0).prev
set thistype(0).prev = thistype(0).prev.prev
endif
if thistype(0).next == 0 then
call TimerStart(period, FPS, true, function thistype.periodic)
else
set thistype(0).next.prev = this$prefix$
endif
set this$prefix$.next = thistype(0).next
set thistype(0).next = this$prefix$
set this$prefix$.prev = thistype(0)
//! endtextmacro
private struct ArrowStorm extends array
unit caster
unit missile
real dmg
boolean head
integer lvl
real X
real Y
real steps
player owner
group already
thistype prev
thistype next
static integer count
static timer period
static group g
method destroy takes nothing returns nothing
if this.next != 0 then
set this.next.prev = this.prev
endif
set this.prev.next = this.next
set this.prev = thistype(0).prev
set thistype(0).prev = this
if thistype(0).next == 0 then
call PauseTimer(period)
endif
call DestroyGroup(this.already)
set this.caster = null
set this.missile = null
set this.already = null
set this.owner = null
endmethod
static method periodic takes nothing returns nothing
local thistype this = thistype(0).next
local thistype this2
local unit u
local integer i
local real x
local real y
local real angle
loop
exitwhen this == 0
set x = GetUnitX(this.missile)+this.X
set y = GetUnitY(this.missile)+this.Y
call SetUnitX(this.missile, x)
call SetUnitY(this.missile, y)
if this.head then
call GroupEnumUnitsInRange(g, x, y, RADIUS_PRIMARY, null)
else
call GroupEnumUnitsInRange(g, x, y, RADIUS_SECONDARY, null)
endif
loop
set u = FirstOfGroup(g)
exitwhen u == null
call GroupRemoveUnit(g,u)
if Unit_Filter(this.owner,u) and not IsUnitInGroup(u,this.already) then
call UnitDamageTarget(this.caster, u, this.dmg, true, false, A_TYPE, D_TYPE, W_TYPE)
call GroupAddUnit(this.already, u)
if this.head then
set i = 0
set x = GetUnitX(this.missile)
set y = GetUnitY(this.missile)
call DestroyEffect(AddSpecialEffect(PATH, x, y))
loop
exitwhen i>NUMBER_SECONDARY
//! runtextmacro AS_Alloc("2")
set this2.caster = this.caster
set this2.owner = this.owner
set this2.dmg = DamageSecondary(this.lvl)
set this2.head = false
set angle = SECONDARY_OFFSET*i
set this2.missile = CreateUnit(this2.owner, ID_SECONDARY, x + START_OFFSET*Cos(angle), y + START_OFFSET*Sin(angle), angle*bj_RADTODEG)
call UnitApplyTimedLife(this2.missile, 'BTLF', DistanceSecondary(this.lvl)/SPEED_SECONDARY)
set this2.X = SPEED_SECONDARY*FPS*Cos(angle)
set this2.Y = SPEED_SECONDARY*FPS*Sin(angle)
set this2.steps = R2I( DistanceSecondary(this.lvl)/SPEED_SECONDARY/FPS )
set this2.already = CreateGroup()
set i = i + 1
endloop
endif
endif
endloop
set this.steps = this.steps - 1
if this.steps == 0 then
call this.destroy()
endif
set this = this.next
endloop
endmethod
static method cond takes nothing returns boolean
local thistype this
local real x
local real y
local real tx
local real ty
local real angle
if GetSpellAbilityId() == SPELL_ID then
//! runtextmacro AS_Alloc("")
set this.caster = GetTriggerUnit()
set this.lvl = GetUnitAbilityLevel(this.caster, SPELL_ID)
set this.dmg = DamagePrimary(this.lvl)
set this.head = true
set x = GetUnitX(this.caster)
set y = GetUnitY(this.caster)
set tx = GetSpellTargetX()
set ty = GetSpellTargetY()
set angle = Atan2(ty-y, tx-x)
set this.owner = GetOwningPlayer(this.caster)
set this.missile = CreateUnit(this.owner, ID_PRIMARY, x+START_OFFSET*Cos(angle), y+START_OFFSET*Sin(angle), angle*bj_RADTODEG)
call UnitApplyTimedLife(this.missile, 'BTLF', DistancePrimary(this.lvl)/SPEED_PRIMARY)
set this.X = SPEED_PRIMARY*FPS*Cos(angle)
set this.Y = SPEED_PRIMARY*FPS*Sin(angle)
set this.steps = R2I(DistancePrimary(this.lvl)/SPEED_PRIMARY/FPS)
set this.already = CreateGroup()
endif
return false
endmethod
static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function thistype.cond))
set count = 0
set period = CreateTimer()
set g = CreateGroup()
call Preload(PATH)
set t = null
endmethod
endstruct
endscope