library ScopeArrow uses Alloc, SpellEffectEvent, T32, WorldBounds
//===========================================================================
// CONFIGURABLES
//===========================================================================
globals
private constant integer ABIL_ID = 'ABSA' // raw code of base ability
private constant integer DUMMY_ID = 'dSCO' // raw code of unit "Scope Arrow Dummy"
private constant integer COUNT = 6 // number of arrows split, setting it 0 will result in catastrophic turn of events
private constant real COLLISION = 50. // collision size of arrow
private constant real SPEED = 1000. // distance travelled by arrow per second
private constant real SCALE_MAIN = 1.5 // scale size of main arrow
private constant real SCALE_SPLIT = 1.0 // scale size of split arrow
private constant real HEIGHT = 50. // flying height of arrow
private constant string FX = "Abilities\\Spells\\Other\\BlackArrow\\BlackArrowMissile.mdl" // effect upon impact
private constant string FX_AT = "chest" // FX attachment point
private constant real ENUM_RADIUS = 176. // max collision size of a unit in your map
private constant attacktype ATK = ATTACK_TYPE_NORMAL // attack type
private constant damagetype DMG = DAMAGE_TYPE_MAGIC // damage type
endglobals
// initial damage
private constant function GetDamage takes integer level returns real
return 50. * level
endfunction
// damage dealt per split arrow
private constant function GetDamageSplit takes integer level returns real
return 10. * level + 20.
endfunction
// distance travelled by arrow
private constant function GetDistanceMain takes integer level returns real
return 2000.
endfunction
private constant function GetDistanceSplit takes integer level returns real
return 1000.
endfunction
// filter for allowed targets
private constant function GetFilter takes unit u, unit caster returns boolean
return /*
*/ not IsUnitType(u, UNIT_TYPE_DEAD) /* // target is alive
*/ and IsUnitEnemy(u, GetOwningPlayer(caster)) /* // target is an enemy of caster
*/ and not IsUnitType(u, UNIT_TYPE_STRUCTURE) // target is not a structure
endfunction
//===========================================================================
// END CONFIGURABLES
//===========================================================================
globals
private constant real TRUE_SPEED = SPEED * T32_PERIOD
private constant real SPLIT_ANGLE = bj_PI * 2 / COUNT
private group G = bj_lastCreatedGroup
endglobals
private struct Split extends array
implement Alloc
private unit u
private unit dummy
private group g
private real cos
private real sin
private real dmg
private real dist
private method destroy takes nothing returns nothing
call KillUnit(this.dummy)
call DestroyGroup(this.g)
call this.stopPeriodic()
call this.deallocate()
endmethod
private method periodic takes nothing returns nothing
local unit u
local real x
local real y
if this.dist <= 0 then
call this.destroy()
else
set this.dist = this.dist - TRUE_SPEED
set x = GetUnitX(this.dummy) + TRUE_SPEED * this.cos
set y = GetUnitY(this.dummy) + TRUE_SPEED * this.sin
if x >= WorldBounds.minX and x <= WorldBounds.maxX and y >= WorldBounds.minY and y <= WorldBounds.maxY then
call SetUnitX(this.dummy, x)
call SetUnitY(this.dummy, y)
endif
call GroupEnumUnitsInRange(G, x, y, COLLISION + ENUM_RADIUS, null)
loop
set u = FirstOfGroup(G)
exitwhen u == null
call GroupRemoveUnit(G, u)
if GetFilter(u, this.u) and not IsUnitInGroup(u, this.g) and IsUnitInRangeXY(u, x, y, COLLISION) then
call UnitDamageTarget(this.u, u, this.dmg, true, false, ATK, DMG, null)
call GroupAddUnit(this.g, u)
endif
endloop
endif
endmethod
implement T32x
public static method create takes unit u, real dmg, real dist, real a, real x, real y returns thistype
local thistype this = thistype.allocate()
set this.u = u
set this.dummy = CreateUnit(GetOwningPlayer(this.u), DUMMY_ID, x, y, bj_RADTODEG * a)
set this.g = CreateGroup()
set this.cos = Cos(a)
set this.sin = Sin(a)
set this.dmg = dmg
set this.dist = dist
call SetUnitFlyHeight(this.dummy, HEIGHT, 0)
call SetUnitScale(this.dummy, SCALE_SPLIT, SCALE_SPLIT, SCALE_SPLIT)
call this.startPeriodic()
return this
endmethod
endstruct
private struct Data extends array
implement Alloc
private unit u
private unit dummy
private real a
private real cos
private real sin
private real dmg
private real splitDmg
private real dist
private real splitDist
private method destroy takes nothing returns nothing
call KillUnit(this.dummy)
call this.stopPeriodic()
call this.deallocate()
endmethod
private method periodic takes nothing returns nothing
local unit u
local integer i
local real x
local real y
local boolean b = true
if this.dist <= 0 then
call this.destroy()
else
set this.dist = this.dist - TRUE_SPEED
set x = GetUnitX(this.dummy)
set y = GetUnitY(this.dummy)
call GroupEnumUnitsInRange(G, x, y, COLLISION + ENUM_RADIUS, null)
loop
set u = FirstOfGroup(G)
exitwhen u == null
call GroupRemoveUnit(G, u)
if GetFilter(u, this.u) and IsUnitInRangeXY(u, x, y, COLLISION) then
set b = false
call UnitDamageTarget(this.u, u, this.dmg, true, false, ATK, DMG, null)
call DestroyEffect(AddSpecialEffectTarget(FX, u, FX_AT))
call GroupClear(G)
endif
endloop
if b then
set x = x + TRUE_SPEED * this.cos
set y = y + TRUE_SPEED * this.sin
if x >= WorldBounds.minX and x <= WorldBounds.maxX and y >= WorldBounds.minY and y <= WorldBounds.maxY then
call SetUnitX(this.dummy, x)
call SetUnitY(this.dummy, y)
endif
else
set this.a = SPLIT_ANGLE
set i = COUNT
loop
exitwhen i == 0
call Split.create(this.u, this.splitDmg, this.splitDist, this.a * i, x, y)
set i = i - 1
endloop
call this.destroy()
endif
endif
endmethod
implement T32x
private static method onCast takes nothing returns boolean
local thistype this = thistype.allocate()
local integer level
local real x
local real y
set this.u = GetTriggerUnit()
set level = GetUnitAbilityLevel(this.u, ABIL_ID)
set x = GetUnitX(this.u)
set y = GetUnitY(this.u)
set this.a = Atan2(GetSpellTargetY() - y, GetSpellTargetX()- x)
set this.cos = Cos(this.a)
set this.sin = Sin(this.a)
set this.dummy = CreateUnit(GetTriggerPlayer(), DUMMY_ID, x, y, bj_RADTODEG * this.a)
set this.dmg = GetDamage(level)
set this.splitDmg = GetDamageSplit(level)
set this.dist = GetDistanceMain(level)
set this.splitDist = GetDistanceSplit(level)
call SetUnitFlyHeight(this.dummy, HEIGHT, 0)
call SetUnitScale(this.dummy, SCALE_MAIN, SCALE_MAIN, SCALE_MAIN)
call this.startPeriodic()
return false
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(ABIL_ID, function thistype.onCast)
endmethod
endstruct
endlibrary