//TESH.scrollpos=0
//TESH.alwaysfold=0
Name | Type | is_array | initial_value |
//TESH.scrollpos=43
//TESH.alwaysfold=0
[center]
[COLOR="Yellow"][SIZE="4"]DESCRIPTION:[/SIZE][/COLOR]
[IMG]http://www.hiveworkshop.com/forums/members/167625-albums4356-picture92285.jpg[/IMG]
[COLOR="Yellow"][SIZE="4"]PREVIEW:[/SIZE][/COLOR]
[img]http://i1288.photobucket.com/albums/b488/mckill2009/FireEscort%20-%20Anim_zps129sapn2.gif[/img][/center]
[jass]
/*
=====Spell Name: Fire Escort v1.2
=====Created by: Mckill2009
REQUIRES:
- JassNewGenPack by Vexorian
- SpellEffectEvent by Bribe
OPTIONAL REQUIREMENTS:
- BoundSentinel by Vexorian
HOW TO INSTALL:
- Copy ALL the custom units/abilities/buff from the object editor to your map.
- This has custom buff to remove the stun buff from Firebolt spell.
- Copy ALL that is inside the folder 'FireEscort' to your trigger editor.
- If you want to make your own units/abilities/buff, make sure you change the rawID indicated in the code.
- If you change the DUMMY_SPELL_ID rawID, make sure it will be casted by ORDER_ID.
- To view rawID, press CTRL+D in the object editor.
- Save and Done!
*/
library FireEscort uses SpellEffectEvent optional BoundSentinel
globals
/**************************************************************
* RAW IDs: change the IDs according to your object editor
* The DUMMY_SPELL_ID and ORDER_ID must match!
***************************************************************/
private constant integer SPELL_ID = 'A002' //Howl of Terror custom spell (rawID)
private constant integer DUMMY_SPELL_ID = 'A000' //Firebolt custom spell (rawID)
private constant integer ORDER_ID = 852231 //Firebolt (orderID)
private constant integer DUMMY_ID = 'h000' //rawID
private constant integer BOMB_ID = 'h001' //rawID
/**************************************************************
* CONFIGURABLE GLOBALS
***************************************************************/
//It is recommended not to exceed 2, you may use negative values
private constant real ROTATION_SPEED = 0.05
//Fireball move speed towards the caster or target
private constant real FOLLOW_SPEED = 7
//Fireballs MAX offset from dummy's center
private constant real EXPAND_RANGE = 300
//Fireballs move speed away from hero to EXPAND_RANGE
private constant real EXPAND_SPEED = 1
//Sets how many seconds the dummy will search for new target
private constant real TARGET_SEARCH_INTERVAL = 5
//Cast interval of the Fireballs
private constant real MISSILE_CAST_INTERVAL = 1
//The FireballLifeDuration will start here, when fireballs at MAX height
private constant real MAX_FLY_HEIGHT = 400
//Fly speed from ground to MAX_FLY_HEIGHT
private constant real FLY_SPEED = 2
/**************************************************************
* NON-CONFIGURABLE GLOBALS
***************************************************************/
private constant real INTERVAL = 0.03125
private group g = CreateGroup()
private integer count = 0
private unit TempU = null
endglobals
/**************************************************************
CONFIGURABLES
***************************************************************/
private function FilterEnemies takes unit u returns boolean
return not (IsUnitType(u, UNIT_TYPE_STRUCTURE) or IsUnitType(u, UNIT_TYPE_MECHANICAL) or IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE))
endfunction
private function FireballLifeDuration takes integer level returns real
return 5. * level + 5 //10/15/20/25/30
endfunction
//Range of Fireball casting Firebolt
private function FireballCastRange takes integer level returns real
return 150. * level + 250 //400/550/700/850/1000
endfunction
//How many fireballs created
private function FireballCount takes integer level returns integer
return 2 * level + 4 //6/8/10/12/14
endfunction
//Fireballs search for nearby enemies
private function SearchEnemyRange takes integer level returns real
return 200. * level + 600 //800/1000/1200/1400/1600
endfunction
/**************************************************************
NON-CONFIGURABLES
***************************************************************/
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and u!=null
endfunction
private function GetDistance takes real x1, real y1, real x2, real y2 returns real
return (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)
endfunction
private function FilterThem takes unit caster, unit target returns boolean
return UnitAlive(target) and IsUnitEnemy(caster, GetOwningPlayer(target)) and FilterEnemies(target)
endfunction
private function FilterTarget takes nothing returns boolean
return FilterThem(TempU, GetFilterUnit())
endfunction
private function EnumUnits takes nothing returns nothing
set count = count+1
endfunction
private function SetUnitXY takes unit u, real x, real y, real angle returns nothing
call SetUnitX(u, x + FOLLOW_SPEED * Cos(angle))
call SetUnitY(u, y + FOLLOW_SPEED * Sin(angle))
endfunction
private function GetRandomUnitInArea takes real x, real y, real aoe, boolexpr b returns unit
local integer ran
call GroupEnumUnitsInRange(g, x, y, aoe, b)
set count = 0
call ForGroup(g, function EnumUnits)
set ran = GetRandomInt(1, count)
set count = 0
loop
set TempU = FirstOfGroup(g)
exitwhen TempU==null
set count = count + 1
if count==ran then
exitwhen true
endif
call GroupRemoveUnit(g, TempU)
endloop
call GroupClear(g)
return TempU
endfunction
private struct EscortRotation
unit leader
unit escort
real angle
real aoe
real expand
real height
real duration
real castInterval
static timer t = CreateTimer()
static integer index = 0
static integer array indexAR
static method periodic takes nothing returns nothing
local thistype this
local integer i = 0
local unit tar
local real xEscort
local real yEscort
local real xLeader
local real yLeader
loop
set i = i + 1
set this = indexAR[i]
if .duration > 0 then
set .angle = .angle + ROTATION_SPEED
set xLeader = GetUnitX(.leader)
set yLeader = GetUnitY(.leader)
if EXPAND_RANGE > .expand then
set .expand = .expand + EXPAND_SPEED
endif
if MAX_FLY_HEIGHT > .height then
set .height = .height + FLY_SPEED
call SetUnitFlyHeight(.escort, .height, 0)
elseif .height >= MAX_FLY_HEIGHT then
set .duration = .duration - INTERVAL
set .castInterval = .castInterval + INTERVAL
if .castInterval > MISSILE_CAST_INTERVAL then
set .castInterval = 0
set xEscort = GetUnitX(.escort)
set yEscort = GetUnitY(.escort)
set TempU = .escort
set tar = GetRandomUnitInArea(xEscort, yEscort, .aoe, Filter(function FilterTarget))
if tar!=null then
call IssueTargetOrderById(.escort, ORDER_ID, tar)
set tar = null
endif
endif
endif
call SetUnitX(.escort, xLeader + .expand * Cos(.angle))
call SetUnitY(.escort, yLeader + .expand * Sin(.angle))
else
if UnitAlive(.leader) then
call KillUnit(.leader)
endif
call KillUnit(.escort)
set .leader = null
set .escort = null
call .destroy()
set indexAR[i] = indexAR[index]
set indexAR[index] = this
set i = i - 1
set index = index - 1
if index==0 then
call PauseTimer(t)
endif
endif
exitwhen i==index
endloop
endmethod
static method register takes unit leader, unit escort, real angle, real duration, real aoe returns nothing
local thistype this = allocate()
set .leader = leader
set .escort = escort
set .angle = angle*bj_DEGTORAD
set .expand = 0
set .castInterval = 0
set .height = 0
set .duration = duration
set .aoe = aoe
if index==0 then
call TimerStart(t, INTERVAL, true, function thistype.periodic)
endif
set index = index + 1
set indexAR[index] = this
endmethod
endstruct
private struct Cast
unit caster
unit target
unit dummy
real search
real aoe
real goBack
real height
static timer t = CreateTimer()
static integer index = 0
static integer array indexAR
static method periodic takes nothing returns nothing
local thistype this
local integer i = 0
local unit first
local real xCaster
local real yCaster
local real xDum
local real yDum
loop
set i = i+1
set this = indexAR[i]
if UnitAlive(.caster) and UnitAlive(.dummy) then
set xCaster = GetUnitX(.caster)
set yCaster = GetUnitY(.caster)
set xDum = GetUnitX(.dummy)
set yDum = GetUnitY(.dummy)
if .target==null then
//search for enemies from dummy's aoe
set .target = GetRandomUnitInArea(xCaster, yCaster, .aoe, Filter(function FilterTarget))
if .target==null then //back to caster
call SetUnitXY(.dummy, xDum, yDum, Atan2(yCaster - yDum, xCaster - xDum))
endif
else
//this is not the height of the fireballs
set .height = .height + FLY_SPEED
if .height >= MAX_FLY_HEIGHT then
if GetDistance(xCaster, yCaster, xDum, yDum) > .goBack then
call SetUnitXY(.dummy, xDum, yDum, Atan2(yCaster - yDum, xCaster - xDum))
else
//goes targets if caster is also near the target's aoe
call SetUnitXY(.dummy, xDum, yDum, Atan2(GetUnitY(.target) - yDum, GetUnitX(.target) - xDum))
endif
else
call SetUnitXY(.dummy, xDum, yDum, Atan2(yCaster - yDum, xCaster - xDum))
endif
//This refreshes/nulls the targets to search for a new one
set .search = .search + INTERVAL
if .search > TARGET_SEARCH_INTERVAL then
set .search = 0
set .target = null
endif
endif
else //End the Spell
if UnitAlive(.dummy) then
call KillUnit(.dummy)
endif
set .caster = null
set .target = null
set .dummy = null
call .destroy()
set indexAR[i] = indexAR[index]
set indexAR[index] = this
set i = i - 1
set index = index - 1
if index==0 then
call PauseTimer(t)
endif
endif
exitwhen i==index
endloop
endmethod
static method onCast takes nothing returns nothing
local thistype this = allocate()
local integer level
local integer i = 0
local integer countEscorts
local real g = 0
local real gap
local real fireRange
local unit bomb
local real duration
set .caster = GetTriggerUnit()
set level = GetUnitAbilityLevel(.caster, SPELL_ID)
set .target = null
set .dummy = CreateUnit(GetTriggerPlayer(), DUMMY_ID, GetUnitX(.caster), GetUnitY(.caster), 0)
set .search = 0
set .aoe = SearchEnemyRange(level)
set .goBack = .aoe*.aoe
set .height = 0
set countEscorts = FireballCount(level)
set duration = 120. //FireballLifeDuration(level)
set fireRange = FireballCastRange(level)
set gap = (360/countEscorts)
loop
exitwhen i==countEscorts
set bomb = CreateUnit(GetTriggerPlayer(), BOMB_ID, 0, 0, 0)
call SetUnitFlyHeight(bomb, 0, 0)
call UnitAddAbility(bomb, DUMMY_SPELL_ID)
call SetUnitAbilityLevel(bomb, DUMMY_SPELL_ID, level)
call EscortRotation.register(.dummy, bomb, g, duration, fireRange)
set g = g + gap
set i = i + 1
endloop
if index==0 then
call TimerStart(t, INTERVAL, true, function thistype.periodic)
endif
set index = index + 1
set indexAR[index] = this
set bomb = null
endmethod
static method onInit takes nothing returns nothing
local unit u = CreateUnit(Player(15), DUMMY_ID,0,0,0)
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
call UnitAddAbility(u, DUMMY_SPELL_ID)
call KillUnit(u)
set u = null
endmethod
endstruct
endlibrary
[/jass]
[hidden=Changelogs]
v1.2
- Improved random units
v1.1a
- Fixed Documentation and big bug
v1.1
- Description changed to random units
- Added BoundSentinel
- Stun buff removed
- More configurables and explained
[/hidden]
[hidden=Credits]
- JassNewGenPack by Vexorian
- SpellEffectEvent by Bribe
- BoundSentinel by Vexorian
[/hidden]
//TESH.scrollpos=0
//TESH.alwaysfold=0
/**************************************************************
*
* RegisterPlayerUnitEvent
* v5.1.0.1
* By Magtheridon96
*
* I would like to give a special thanks to Bribe, azlier
* and BBQ for improving this library. For modularity, it only
* supports player unit events.
*
* Functions passed to RegisterPlayerUnitEvent must either
* return a boolean (false) or nothing. (Which is a Pro)
*
* Warning:
* --------
*
* - Don't use TriggerSleepAction inside registered code.
* - Don't destroy a trigger unless you really know what you're doing.
*
* API:
* ----
*
* - function RegisterPlayerUnitEvent takes playerunitevent whichEvent, code whichFunction returns nothing
* - Registers code that will execute when an event fires.
* - function RegisterPlayerUnitEventForPlayer takes playerunitevent whichEvent, code whichFunction, player whichPlayer returns nothing
* - Registers code that will execute when an event fires for a certain player.
* - function GetPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
* - Returns the trigger corresponding to ALL functions of a playerunitevent.
*
**************************************************************/
library RegisterPlayerUnitEvent // Special Thanks to Bribe and azlier
globals
private trigger array t
endglobals
function RegisterPlayerUnitEvent takes playerunitevent p, code c returns nothing
local integer i = GetHandleId(p)
local integer k = 15
if t[i] == null then
set t[i] = CreateTrigger()
loop
call TriggerRegisterPlayerUnitEvent(t[i], Player(k), p, null)
exitwhen k == 0
set k = k - 1
endloop
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function RegisterPlayerUnitEventForPlayer takes playerunitevent p, code c, player pl returns nothing
local integer i = 16 * GetHandleId(p) + GetPlayerId(pl)
if t[i] == null then
set t[i] = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(t[i], pl, p, null)
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function GetPlayerUnitEventTrigger takes playerunitevent p returns trigger
return t[GetHandleId(p)]
endfunction
endlibrary
//TESH.scrollpos=15
//TESH.alwaysfold=0
//============================================================================
// SpellEffectEvent
// - Version 1.1.0.0
//
// API
// ---
// RegisterSpellEffectEvent(integer abil, code onCast)
//
// Requires
// --------
// RegisterPlayerUnitEvent: hiveworkshop.com/forums/showthread.php?t=203338
//
// Optional
// --------
// Table: hiveworkshop.com/forums/showthread.php?t=188084
//
library SpellEffectEvent requires RegisterPlayerUnitEvent, optional Table
//============================================================================
private module M
static if LIBRARY_Table then
static Table tb
else
static hashtable ht = InitHashtable()
endif
static method onCast takes nothing returns nothing
static if LIBRARY_Table then
call TriggerEvaluate(.tb.trigger[GetSpellAbilityId()])
else
call TriggerEvaluate(LoadTriggerHandle(.ht, 0, GetSpellAbilityId()))
endif
endmethod
private static method onInit takes nothing returns nothing
static if LIBRARY_Table then
set .tb = Table.create()
endif
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
endmethod
endmodule
//============================================================================
private struct S extends array
implement M
endstruct
//============================================================================
function RegisterSpellEffectEvent takes integer abil, code onCast returns nothing
static if LIBRARY_Table then
if not S.tb.handle.has(abil) then
set S.tb.trigger[abil] = CreateTrigger()
endif
call TriggerAddCondition(S.tb.trigger[abil], Filter(onCast))
else
if not HaveSavedHandle(S.ht, 0, abil) then
call SaveTriggerHandle(S.ht, 0, abil, CreateTrigger())
endif
call TriggerAddCondition(LoadTriggerHandle(S.ht, 0, abil), Filter(onCast))
endif
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library BoundSentinel initializer init
//*************************************************
//* BoundSentinel
//* -------------
//* Don't leave your units unsupervised, naughty
//* them may try to get out of the map bounds and
//* crash your game.
//*
//* To implement, just get a vJass compiler and
//* copy this library/trigger to your map.
//*
//*************************************************
//==================================================
globals
// High enough so the unit is no longer visible, low enough so the
// game doesn't crash...
//
// I think you need 0.0 or soemthing negative prior to patch 1.22
//
private constant real EXTRA = 500.0
endglobals
//=========================================================================================
globals
private real maxx
private real maxy
private real minx
private real miny
endglobals
//=======================================================================
private function dis takes nothing returns nothing
local unit u=GetTriggerUnit()
local real x=GetUnitX(u)
local real y=GetUnitY(u)
if(x>maxx) then
set x=maxx
elseif(x<minx) then
set x=minx
endif
if(y>maxy) then
set y=maxy
elseif(y<miny) then
set y=miny
endif
call SetUnitX(u,x)
call SetUnitY(u,y)
set u=null
endfunction
private function init takes nothing returns nothing
local trigger t=CreateTrigger()
local region r=CreateRegion()
local rect rc
set minx=GetCameraBoundMinX() - EXTRA
set miny=GetCameraBoundMinY() - EXTRA
set maxx=GetCameraBoundMaxX() + EXTRA
set maxy=GetCameraBoundMaxY() + EXTRA
set rc=Rect(minx,miny,maxx,maxy)
call RegionAddRect(r, rc)
call RemoveRect(rc)
call TriggerRegisterLeaveRegion(t,r, null)
call TriggerAddAction(t, function dis)
//this is not necessary but I'll do it anyway:
set t=null
set r=null
set rc=null
endfunction
endlibrary
//TESH.scrollpos=33
//TESH.alwaysfold=0
/*
=====Spell Name: Fire Escort v1.2
=====Created by: Mckill2009
REQUIRES:
- JassNewGenPack by Vexorian
- SpellEffectEvent by Bribe
OPTIONAL REQUIREMENTS:
- BoundSentinel by Vexorian
HOW TO INSTALL:
- Copy ALL the custom units/abilities/buff from the object editor to your map.
- This has custom buff to remove the stun buff from Firebolt spell.
- Copy ALL that is inside the folder 'FireEscort' to your trigger editor.
- If you want to make your own units/abilities/buff, make sure you change the rawID indicated in the code.
- If you change the DUMMY_SPELL_ID rawID, make sure it will be casted by ORDER_ID.
- To view rawID, press CTRL+D in the object editor.
- Save and Done!
*/
library FireEscort uses SpellEffectEvent optional BoundSentinel
globals
/**************************************************************
* RAW IDs: change the IDs according to your object editor
* The DUMMY_SPELL_ID and ORDER_ID must match!
***************************************************************/
private constant integer SPELL_ID = 'A002' //Howl of Terror custom spell (rawID)
private constant integer DUMMY_SPELL_ID = 'A000' //Firebolt custom spell (rawID)
private constant integer ORDER_ID = 852231 //Firebolt (orderID)
private constant integer DUMMY_ID = 'h000' //rawID
private constant integer BOMB_ID = 'h001' //rawID
/**************************************************************
* CONFIGURABLE GLOBALS
***************************************************************/
//It is recommended not to exceed 2, you may use negative values
private constant real ROTATION_SPEED = 0.05
//Fireball move speed towards the caster or target
private constant real FOLLOW_SPEED = 7
//Fireballs MAX offset from dummy's center
private constant real EXPAND_RANGE = 300
//Fireballs move speed away from hero to EXPAND_RANGE
private constant real EXPAND_SPEED = 1
//Sets how many seconds the dummy will search for new target
private constant real TARGET_SEARCH_INTERVAL = 5
//Cast interval of the Fireballs
private constant real MISSILE_CAST_INTERVAL = 1
//The FireballLifeDuration will start here, when fireballs at MAX height
private constant real MAX_FLY_HEIGHT = 400
//Fly speed from ground to MAX_FLY_HEIGHT
private constant real FLY_SPEED = 2
/**************************************************************
* NON-CONFIGURABLE GLOBALS
***************************************************************/
private constant real INTERVAL = 0.03125
private group g = CreateGroup()
private integer count = 0
private unit TempU = null
endglobals
/**************************************************************
CONFIGURABLES
***************************************************************/
private function FilterEnemies takes unit u returns boolean
return not (IsUnitType(u, UNIT_TYPE_STRUCTURE) or IsUnitType(u, UNIT_TYPE_MECHANICAL) or IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE))
endfunction
private function FireballLifeDuration takes integer level returns real
return 5. * level + 5 //10/15/20/25/30
endfunction
//Range of Fireball casting Firebolt
private function FireballCastRange takes integer level returns real
return 150. * level + 250 //400/550/700/850/1000
endfunction
//How many fireballs created
private function FireballCount takes integer level returns integer
return 2 * level + 4 //6/8/10/12/14
endfunction
//Fireballs search for nearby enemies
private function SearchEnemyRange takes integer level returns real
return 200. * level + 600 //800/1000/1200/1400/1600
endfunction
/**************************************************************
NON-CONFIGURABLES
***************************************************************/
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and u!=null
endfunction
private function GetDistance takes real x1, real y1, real x2, real y2 returns real
return (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)
endfunction
private function FilterThem takes unit caster, unit target returns boolean
return UnitAlive(target) and IsUnitEnemy(caster, GetOwningPlayer(target)) and FilterEnemies(target)
endfunction
private function FilterTarget takes nothing returns boolean
return FilterThem(TempU, GetFilterUnit())
endfunction
private function EnumUnits takes nothing returns nothing
set count = count+1
endfunction
private function SetUnitXY takes unit u, real x, real y, real angle returns nothing
call SetUnitX(u, x + FOLLOW_SPEED * Cos(angle))
call SetUnitY(u, y + FOLLOW_SPEED * Sin(angle))
endfunction
private function GetRandomUnitInArea takes real x, real y, real aoe, boolexpr b returns unit
local integer ran
call GroupEnumUnitsInRange(g, x, y, aoe, b)
set count = 0
call ForGroup(g, function EnumUnits)
set ran = GetRandomInt(1, count)
set count = 0
loop
set TempU = FirstOfGroup(g)
exitwhen TempU==null
set count = count + 1
if count==ran then
exitwhen true
endif
call GroupRemoveUnit(g, TempU)
endloop
call GroupClear(g)
return TempU
endfunction
private struct EscortRotation
unit leader
unit escort
real angle
real aoe
real expand
real height
real duration
real castInterval
static timer t = CreateTimer()
static integer index = 0
static integer array indexAR
static method periodic takes nothing returns nothing
local thistype this
local integer i = 0
local unit tar
local real xEscort
local real yEscort
local real xLeader
local real yLeader
loop
set i = i + 1
set this = indexAR[i]
if .duration > 0 then
set .angle = .angle + ROTATION_SPEED
set xLeader = GetUnitX(.leader)
set yLeader = GetUnitY(.leader)
if EXPAND_RANGE > .expand then
set .expand = .expand + EXPAND_SPEED
endif
if MAX_FLY_HEIGHT > .height then
set .height = .height + FLY_SPEED
call SetUnitFlyHeight(.escort, .height, 0)
elseif .height >= MAX_FLY_HEIGHT then
set .duration = .duration - INTERVAL
set .castInterval = .castInterval + INTERVAL
if .castInterval > MISSILE_CAST_INTERVAL then
set .castInterval = 0
set xEscort = GetUnitX(.escort)
set yEscort = GetUnitY(.escort)
set TempU = .escort
set tar = GetRandomUnitInArea(xEscort, yEscort, .aoe, Filter(function FilterTarget))
if tar!=null then
call IssueTargetOrderById(.escort, ORDER_ID, tar)
set tar = null
endif
endif
endif
call SetUnitX(.escort, xLeader + .expand * Cos(.angle))
call SetUnitY(.escort, yLeader + .expand * Sin(.angle))
else
if UnitAlive(.leader) then
call KillUnit(.leader)
endif
call KillUnit(.escort)
set .leader = null
set .escort = null
call .destroy()
set indexAR[i] = indexAR[index]
set indexAR[index] = this
set i = i - 1
set index = index - 1
if index==0 then
call PauseTimer(t)
endif
endif
exitwhen i==index
endloop
endmethod
static method register takes unit leader, unit escort, real angle, real duration, real aoe returns nothing
local thistype this = allocate()
set .leader = leader
set .escort = escort
set .angle = angle*bj_DEGTORAD
set .expand = 0
set .castInterval = 0
set .height = 0
set .duration = duration
set .aoe = aoe
if index==0 then
call TimerStart(t, INTERVAL, true, function thistype.periodic)
endif
set index = index + 1
set indexAR[index] = this
endmethod
endstruct
private struct Cast
unit caster
unit target
unit dummy
real search
real aoe
real goBack
real height
static timer t = CreateTimer()
static integer index = 0
static integer array indexAR
static method periodic takes nothing returns nothing
local thistype this
local integer i = 0
local unit first
local real xCaster
local real yCaster
local real xDum
local real yDum
loop
set i = i+1
set this = indexAR[i]
if UnitAlive(.caster) and UnitAlive(.dummy) then
set xCaster = GetUnitX(.caster)
set yCaster = GetUnitY(.caster)
set xDum = GetUnitX(.dummy)
set yDum = GetUnitY(.dummy)
if .target==null then
//search for enemies from dummy's aoe
set .target = GetRandomUnitInArea(xCaster, yCaster, .aoe, Filter(function FilterTarget))
if .target==null then //back to caster
call SetUnitXY(.dummy, xDum, yDum, Atan2(yCaster - yDum, xCaster - xDum))
endif
else
//this is not the height of the fireballs
set .height = .height + FLY_SPEED
if .height >= MAX_FLY_HEIGHT then
if GetDistance(xCaster, yCaster, xDum, yDum) > .goBack then
call SetUnitXY(.dummy, xDum, yDum, Atan2(yCaster - yDum, xCaster - xDum))
else
//goes targets if caster is also near the target's aoe
call SetUnitXY(.dummy, xDum, yDum, Atan2(GetUnitY(.target) - yDum, GetUnitX(.target) - xDum))
endif
else
call SetUnitXY(.dummy, xDum, yDum, Atan2(yCaster - yDum, xCaster - xDum))
endif
//This refreshes/nulls the targets to search for a new one
set .search = .search + INTERVAL
if .search > TARGET_SEARCH_INTERVAL then
set .search = 0
set .target = null
endif
endif
else //End the Spell
if UnitAlive(.dummy) then
call KillUnit(.dummy)
endif
set .caster = null
set .target = null
set .dummy = null
call .destroy()
set indexAR[i] = indexAR[index]
set indexAR[index] = this
set i = i - 1
set index = index - 1
if index==0 then
call PauseTimer(t)
endif
endif
exitwhen i==index
endloop
endmethod
static method onCast takes nothing returns nothing
local thistype this = allocate()
local integer level
local integer i = 0
local integer countEscorts
local real g = 0
local real gap
local real fireRange
local unit bomb
local real duration
set .caster = GetTriggerUnit()
set level = GetUnitAbilityLevel(.caster, SPELL_ID)
set .target = null
set .dummy = CreateUnit(GetTriggerPlayer(), DUMMY_ID, GetUnitX(.caster), GetUnitY(.caster), 0)
set .search = 0
set .aoe = SearchEnemyRange(level)
set .goBack = .aoe*.aoe
set .height = 0
set countEscorts = FireballCount(level)
set duration = 120. //FireballLifeDuration(level)
set fireRange = FireballCastRange(level)
set gap = (360/countEscorts)
loop
exitwhen i==countEscorts
set bomb = CreateUnit(GetTriggerPlayer(), BOMB_ID, 0, 0, 0)
call SetUnitFlyHeight(bomb, 0, 0)
call UnitAddAbility(bomb, DUMMY_SPELL_ID)
call SetUnitAbilityLevel(bomb, DUMMY_SPELL_ID, level)
call EscortRotation.register(.dummy, bomb, g, duration, fireRange)
set g = g + gap
set i = i + 1
endloop
if index==0 then
call TimerStart(t, INTERVAL, true, function thistype.periodic)
endif
set index = index + 1
set indexAR[index] = this
set bomb = null
endmethod
static method onInit takes nothing returns nothing
local unit u = CreateUnit(Player(15), DUMMY_ID,0,0,0)
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
call UnitAddAbility(u, DUMMY_SPELL_ID)
call KillUnit(u)
set u = null
endmethod
endstruct
endlibrary