//TESH.scrollpos=0
//TESH.alwaysfold=0
//a
Name | Type | is_array | initial_value |
//TESH.scrollpos=0
//TESH.alwaysfold=0
library_once TimerUtils initializer init
//*********************************************************************
//* TimerUtils (Blue flavor for 1.23b or later)
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3campaigns.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Blue Flavor: Slower than the red flavor, it got a 408000 handle id
//* limit, which means that if more than 408000 handle ids
//* are used in your map, TimerUtils might fail, this
//* value is quite big and it is much bigger than the
//* timer limit in Red flavor.
//*
//********************************************************************
//==================================================================================================
globals
private hashtable hasht //I <3 blizz
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
call SaveInteger(hasht,0, GetHandleId(t), value)
endfunction
function GetTimerData takes timer t returns integer
return LoadInteger(hasht, 0, GetHandleId(t))
endfunction
//==========================================================================================
globals
private timer array tT
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
endglobals
//==========================================================================================
function NewTimer takes nothing returns timer
if (tN==0) then
set tT[0]=CreateTimer()
else
set tN=tN-1
endif
call SetTimerData(tT[tN],0)
return tT[tN]
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==8191) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
set hasht = InitHashtable()
endfunction
endlibrary
//TESH.scrollpos=3
//TESH.alwaysfold=0
//==============================================================================
// PUI -- Perfect Unit Indexing by Cohadar -- v5.1
//==============================================================================
//
// PURPOUSE:
// * Extending UnitUserData()
// * This is basically perfect hashing algorithm for units
//
// HOW TO USE:
// * You have only one function at your disposal GetUnitIndex(unit)
// It will return a unique index for each unit in range 1..8190
//
// * What you will do with that index is all up to you
// Of course using global arrays is the most obvious choice
// Advanced jassers will think of a couple of more clever ones ofc.
//
// * There are also 2 textmacros available at the end of library code
// They can be used for easier attaching to units
// PUI for structs
// PUI_PROPERTY for unit, integer, real, boolean and string variables
//
// PROS:
// * You can use any number of systems that previously could not work together
// because they all required exclusive access of UnitUserData()
//
// * You can also use this to attach spell data structs to casters
//
// * There are no SetUnitIndex() or ClearUnitIndex() functions here
// Each unit gets assigned one index that cannot be changed
// That index will be automatically recycled when unit is removed from the game.
//
// CONS:
// * This system uses UnitUserData() itself
// That means that if you want to use PUI you must recode
// any other system that uses UnitUserData() to use GetUnitIndex() instead
//
// * If you use UnitIndex for arrays of non-native types (timers, effects and similar)
// you must check if timer on that index already exists before you create a new one.
// This can happen if GetUnitIndex() assigns a recycled index (index of some dead and removed unit)
// to the newly created unit for which you intended to use timer for
//
// * All in all this is not a sys for newbies, it gives great power,
// but it requires knowledge and carefull handling
//
// DETAILS:
// * System is using unit array to keep track of all units with an index.
// Array is periodically checked for removed units,
// when removed unit is found, index is recycled.
// Indexes have "decay time" to prevent bugs
// caused by attaching to "Can't Raise, Does not decay" type units,
// or by using RemoveUnit() function
//
// SYSTEM COMMANDS: (debug mode only, red player only)
//
// * type -pui to display indexes of currently selected units
// * type -puistats to display some stats
//
// THANKS TO:
// * Vexorian - for his help with PUI textmacro
//
// HOW TO IMPORT:
// * Just create a trigger named PUI
// * convert it to text and replace the whole trigger text with this one
//
//==============================================================================
library PUI initializer Init
//==============================================================================
globals
//-----------------------------------------------
private constant real INDEX_DECAY_TIME = 5. // seconds
//-----------------------------------------------
private constant real PERIOD = 0.03125 // 32 fps
//-----------------------------------------------
private constant integer DECAY_TICKS = R2I(INDEX_DECAY_TIME/PERIOD)
//-----------------------------------------------
private integer array Indexz
private unit array Unitz
private integer array Decayz
private integer array Tickz
private integer maxindex = 0
private integer topindex = 0
private integer decayindex = 0
private integer checker = 0
private integer decayer = 0
private integer tick = 0
endglobals
//==============================================================================
private function PeriodicRecycler takes nothing returns boolean
local integer temp
set tick = tick + 1
if topindex > decayindex then
set checker = checker + 1
if checker > topindex then
set checker = decayindex + 1
endif
if (GetUnitUserData(Unitz[checker])==0) then
set decayindex = decayindex + 1
set Unitz[checker] = Unitz[decayindex]
// swap(checker, decayindex)
set temp = Indexz[checker]
set Indexz[checker] = Indexz[decayindex]
set Indexz[decayindex] = temp
set Decayz[decayindex] = DECAY_TICKS
set Tickz[decayindex] = tick
endif
endif
if decayindex > 0 then
set decayer = decayer + 1
if decayer > decayindex then
set decayer = 1
endif
set Decayz[decayer] = Decayz[decayer] - (tick-Tickz[decayer])
if Decayz[decayer] > 0 then
set Tickz[decayer] = tick
else
// swap(decayer, decayindex)
set temp = Indexz[decayer]
set Indexz[decayer] = Indexz[decayindex]
set Indexz[decayindex] = temp
set Unitz[decayindex] = Unitz[topindex]
// swap(decayindex, topindex)
set temp = Indexz[decayindex]
set Indexz[decayindex] = Indexz[topindex]
set Indexz[topindex] = temp
set decayindex = decayindex - 1
set topindex = topindex - 1
endif
endif
return false
endfunction
//==============================================================================
// Main and only function exported by this library
//==============================================================================
function GetUnitIndex takes unit whichUnit returns integer
local integer index
debug if whichUnit == null then
debug call BJDebugMsg("|c00FF0000ERROR: PUI - Index requested for null unit")
debug return 0
debug endif
set index = GetUnitUserData(whichUnit)
if index == 0 then
set topindex = topindex + 1
if topindex > maxindex then
set maxindex = topindex
set Indexz[topindex] = topindex
endif
set index = Indexz[topindex]
set Unitz[topindex] = whichUnit
call SetUnitUserData(whichUnit, index)
set index = GetUnitUserData(whichUnit)
// this happens when requesting unit index for removed unit
debug if index == 0 then
debug call BJDebugMsg("|c00FFCC00WARNING: PUI - Bad unit handle")
debug endif
//debug call BJDebugMsg("|c00FFCC00PUI: Index assigned #" + I2S(index))
endif
return index
endfunction
//==============================================================================
private function DisplayStats takes nothing returns nothing
call BJDebugMsg("=============================================")
call BJDebugMsg("Biggest index ever = " + I2S(maxindex))
call BJDebugMsg("Indexes in use = " + I2S(topindex-decayindex))
call BJDebugMsg("Decaying indexes = " + I2S(decayindex))
call BJDebugMsg("Released indexes = " + I2S(maxindex-topindex))
call BJDebugMsg("=============================================")
endfunction
//===========================================================================
private function DisplaySelectedEnum takes nothing returns nothing
call BJDebugMsg( "PUI(" + ( GetUnitName(GetEnumUnit()) + ( ") = " + I2S(GetUnitUserData(GetEnumUnit())) ) ) )
endfunction
//===========================================================================
private function DisplaySelected takes nothing returns nothing
local group g = CreateGroup()
call SyncSelections()
call GroupEnumUnitsSelected(g, Player(0), null)
call ForGroup(g, function DisplaySelectedEnum)
call DestroyGroup(g)
set g = null
endfunction
//==============================================================================
private function Init takes nothing returns nothing
local trigger trig
set trig = CreateTrigger()
call TriggerRegisterTimerEvent( trig, PERIOD, true )
call TriggerAddCondition( trig, Condition(function PeriodicRecycler) )
debug set trig = CreateTrigger()
debug call TriggerRegisterPlayerChatEvent( trig, Player(0), "-pui", true )
debug call TriggerAddAction( trig, function DisplaySelected )
debug set trig = CreateTrigger()
debug call TriggerRegisterPlayerChatEvent( trig, Player(0), "-puistats", true )
debug call TriggerAddAction( trig, function DisplayStats )
endfunction
endlibrary
//===========================================================================
// Allowed PUI_PROPERTY TYPES are: unit, integer, real, boolean, string
// Do NOT put handles that need to be destroyed here (timer, trigger, ...)
// Instead put them in a struct and use PUI textmacro
//===========================================================================
//! textmacro PUI_PROPERTY takes VISIBILITY, TYPE, NAME, DEFAULT
$VISIBILITY$ struct $NAME$
private static unit array pui_unit
private static $TYPE$ array pui_data
//-----------------------------------------------------------------------
// Returns default value when first time used
//-----------------------------------------------------------------------
static method operator[] takes unit whichUnit returns $TYPE$
local integer pui = GetUnitIndex(whichUnit)
if .pui_unit[pui] != whichUnit then
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = $DEFAULT$
endif
return .pui_data[pui]
endmethod
//-----------------------------------------------------------------------
static method operator[]= takes unit whichUnit, $TYPE$ whichData returns nothing
local integer pui = GetUnitIndex(whichUnit)
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = whichData
endmethod
endstruct
//! endtextmacro
//===========================================================================
// Never destroy PUI structs directly.
// Use .release() instead, will call .destroy()
//===========================================================================
//! textmacro PUI
private static unit array pui_unit
private static integer array pui_data
private static integer array pui_id
//-----------------------------------------------------------------------
// Returns zero if no struct is attached to unit
//-----------------------------------------------------------------------
static method operator[] takes unit whichUnit returns integer
local integer pui = GetUnitIndex(whichUnit)
if .pui_data[pui] != 0 then
if .pui_unit[pui] != whichUnit then
// recycled handle detected
call .destroy(.pui_data[pui])
set .pui_unit[pui] = null
set .pui_data[pui] = 0
endif
endif
return .pui_data[pui]
endmethod
//-----------------------------------------------------------------------
// This will overwrite already attached struct if any
//-----------------------------------------------------------------------
static method operator[]= takes unit whichUnit, integer whichData returns nothing
local integer pui = GetUnitIndex(whichUnit)
if .pui_data[pui] != 0 then
call .destroy(.pui_data[pui])
endif
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = whichData
set .pui_id[whichData] = pui
endmethod
//-----------------------------------------------------------------------
// If you do not call release struct will be destroyed when unit handle gets recycled
//-----------------------------------------------------------------------
method release takes nothing returns nothing
local integer pui= .pui_id[integer(this)]
call .destroy()
set .pui_unit[pui] = null
set .pui_data[pui] = 0
endmethod
//! endtextmacro
//TESH.scrollpos=0
//TESH.alwaysfold=0
library PolarProjection
//distance between X cord
function PolarProjectionX takes real x, real distance, real angle returns real
return x+distance*Cos(angle * bj_DEGTORAD)
endfunction
//distance between Y cord
function PolarProjectionY takes real y, real distance, real angle returns real
return y+distance*Sin(angle * bj_DEGTORAD)
endfunction
//distance between unit
function Unit_Distance takes unit A, unit B returns real
local real dx = GetUnitX(B) - GetUnitX(A)
local real dy = GetUnitY(B) - GetUnitY(A)
return SquareRoot(dx * dx + dy * dy)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library GroupUtils
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This library is a simple implementation of a stack for groups that need to
//* be in the user's control for greater than an instant of time. Additionally,
//* this library provides a single, global group variable for use with user-end
//* enumerations. It is important to note that users should not be calling
//* DestroyGroup() on the global group, since then it may not exist for when it
//* it is next needed.
//*
//* The group stack removes the need for destroying groups and replaces it with
//* a recycling method.
//* function NewGroup takes nothing returns group
//* function ReleaseGroup takes group g returns boolean
//* function GroupRefresh takes group g returns nothing
//*
//* NewGroup grabs a currently unused group from the stack or creates one if the
//* stack is empty. You can use this group however you'd like, but always
//* remember to call ReleaseGroup on it when you are done with it. If you don't
//* release it, it will 'leak' and your stack may eventually overflow if you
//* keep doing that.
//*
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hash table. If you remove a unit from the game who is a member of a unit
//* group, it will 'effectively' remove the unit from the group, but leave a
//* shadow in its place. Calling GroupRefresh on a group will clean up any
//* shadow references that may exist within it.
//*
globals
//* Group for use with all instant enumerations
group ENUM_GROUP = CreateGroup()
//* Temporary references for GroupRefresh
private boolean Flag = false
private group Refr = null
//* Assorted constants
private constant integer MAX_HANDLE_COUNT = 408000
private constant integer MIN_HANDLE_ID = 0x100000
//* Arrays and counter for the group stack
private group array Groups
private integer array Status[MAX_HANDLE_COUNT]
private integer Count = 0
endglobals
private function AddEx takes nothing returns nothing
if Flag then
call GroupClear(Refr)
set Flag = false
endif
call GroupAddUnit(Refr, GetEnumUnit())
endfunction
function GroupRefresh takes group g returns nothing
set Flag = true
set Refr = g
call ForGroup(Refr, function AddEx)
if Flag then
call GroupClear(g)
endif
endfunction
function NewGroup takes nothing returns group
if Count == 0 then
set Groups[0] = CreateGroup()
else
set Count = Count - 1
endif
set Status[GetHandleId(Groups[Count])-MIN_HANDLE_ID] = 1
return Groups[Count]
endfunction
function ReleaseGroup takes group g returns boolean
local integer stat = Status[GetHandleId(g)-MIN_HANDLE_ID]
local boolean b = true
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+" Error: Null groups cannot be released")
set b = false
elseif stat == 0 then
debug call BJDebugMsg(SCOPE_PREFIX+" Error: Group not part of stack")
set b = false
elseif stat == 2 then
debug call BJDebugMsg(SCOPE_PREFIX+" Error: Groups cannot be multiply released")
set b = false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+" Error: Max groups achieved, destroying group")
call DestroyGroup(g)
set b = false
else
call GroupClear(g)
set Groups[Count] = g
set Count = Count + 1
set Status[GetHandleId(g)-MIN_HANDLE_ID] = 2
endif
return b
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library MapTest initializer init
globals
private constant integer HERO_ID='Udre'
private unit Hero
endglobals
private function Actions takes nothing returns nothing
set Hero=CreateUnit(Player(0),HERO_ID,GetStartLocationX(0),GetStartLocationY(0),0)
call SetMapMusic("Sound\\Music\\mp3Music\\IllidansTheme.mp3",false,0)
call DisplayTimedTextToPlayer(Player(0),0,0,20.," |cff8b814c L I G H T N I N G I N V O C A T I O N|r")
call DisplayTimedTextToPlayer(Player(0),0,0,20.," |cffcdbe70 By: scorpion182 aka ranzi aka ada_aja")
call DisplayTimedTextToPlayer(Player(0),0,0,20.,"Type -test to create creeps.")
endfunction
private function Chat_Actions takes nothing returns nothing
local integer i=0
loop
exitwhen i>3
call CreateUnit(Player(2),'hfoo',GetUnitX(Hero)-20,GetUnitY(Hero)-20,0)
set i=i+1
endloop
endfunction
private function Chat_init takes nothing returns nothing
local trigger t = CreateTrigger( )
call TriggerRegisterPlayerChatEvent(t, Player(0), "-test", true )
call TriggerAddAction(t, function Chat_Actions )
endfunction
private function init takes nothing returns nothing
call Actions()
call Chat_init()
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//******************************************************************************
//* *
//* Lightning Invocation *
//* v1.30 *
//* *
//* By: scorpion182 aka ranzi aka ada_aja *
//* http://www.jade-wars.com *
//* *
//******************************************************************************
library LightningInvocation initializer init requires TimerUtils, PolarProjection, GroupUtils, PUI
//*************************************************************
//* Configuration Constants
globals
private constant integer DUMMY_ID='e000' //Dummy Caster Unit Rawcode
private constant integer SPELL_ID='A000' //Spell Rawcode
private constant integer LIGHTNING_NUM=6 //Number of Lightning
private constant real LIGHTNING_HEIGHT=500. //Lightning Height
private constant string LIGHTNING_ID="AFOD" //Lighting Rawcode
private constant string GROUND_FX="Abilities\\Weapons\\VengeanceMissile\\VengeanceMissile.mdl" //Effect attach to lightning
private constant string HEAL_FX="Abilities\\Weapons\\VengeanceMissile\\VengeanceMissile.mdl" //Effect attach to target heal unit
private constant string CASTER_FX="Abilities\\Spells\\Orc\\Voodoo\\VoodooAura.mdl" //Effect attach to caster
private constant string CASTER_ATTCH="origin" //caster attachment point
private constant string TARGT_H_ATTCH="origin" //target heal attachment point
private constant string ORDER_ID="starfall" //spell order id
private constant real INTERVAL=0.1 //timer interval
private constant boolean CLOCKWISE=false //rotate clockwise?
private constant real ANGLE_INCREMENT=.15 //angle movement
endglobals
//*****************************************************************
//* Configuration Functions
//* Spell Damage
private constant function HEAL_AMOUNT takes integer lvl returns real
return lvl*10.0*INTERVAL
endfunction
//* Spell Area of Effect Radius
private constant function RADIUS takes integer lvl returns real
return lvl*600.
endfunction
//* Configuration End
//*****************************************************************
globals
private integer array UnitData
private boolexpr b
private group CastersGroup
endglobals
//set unit's integer
private function AttachIntegerToUnit takes unit u, integer i returns nothing
set UnitData[GetUnitIndex(u)] = i
endfunction
//get unit's integer
private function GetIntegerFromUnit takes unit u returns integer
return UnitData[GetUnitIndex(u)]
endfunction
//spell filter function
private function Spell_Filter takes nothing returns boolean
return (IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE)==false) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE)==false) and (GetWidgetLife(GetFilterUnit()) > 0.405)
endfunction
//spell struct
private struct data
unit caster
unit dum
unit array missile[LIGHTNING_NUM]
lightning array lg[LIGHTNING_NUM]
real array angle[LIGHTNING_NUM]
effect fx
timer t
group g
integer lvl
static method create takes unit c, timer t returns data
local data d=data.allocate()
set d.g=NewGroup()
set d.caster=c
set d.t=t
set d.fx=AddSpecialEffectTarget(CASTER_FX, d.caster,CASTER_ATTCH)
set d.dum=CreateUnit(GetOwningPlayer(d.caster),DUMMY_ID,GetUnitX(d.caster),GetUnitY(d.caster),0)
set d.lvl=GetUnitAbilityLevel(d.caster,SPELL_ID)
call GroupAddUnit(CastersGroup,d.caster)
return d
endmethod
//update victim group
method Update_Group takes nothing returns nothing
local unit s
local group temp=NewGroup()
local integer lvl=GetUnitAbilityLevel(.caster,SPELL_ID)
local integer idx
call GroupEnumUnitsInRange(temp,GetUnitX(.caster),GetUnitY(.caster),RADIUS(lvl),b)
//convert owner, and heal allies
loop
set s=FirstOfGroup(temp)
exitwhen s==null
//convert owner
if (IsUnitEnemy(s,GetOwningPlayer(.caster))==true and IsUnitInGroup(s,.g)==false and IsUnitType(s,UNIT_TYPE_HERO)==false) then
call AttachIntegerToUnit(s,GetPlayerId(GetOwningPlayer(s)))
call SetUnitOwner(s,GetOwningPlayer(.caster),true)
call GroupAddUnit(.g,s)
//heal friendly unit
elseif (IsUnitAlly(s,GetOwningPlayer(.caster))==true and s!=.caster and IsUnitType(s,UNIT_TYPE_HERO)==true) then
call SetUnitState(s,UNIT_STATE_LIFE,GetUnitState(s,UNIT_STATE_LIFE)+HEAL_AMOUNT(lvl))
call DestroyEffect(AddSpecialEffectTarget(HEAL_FX, s,TARGT_H_ATTCH))
endif
call GroupRemoveUnit(temp,s)
endloop
//out of range?
call GroupAddGroup(.g,temp)
loop
set s=FirstOfGroup(temp)
exitwhen s==null
if (Unit_Distance(s,.caster)>RADIUS(lvl)) then
set idx=GetIntegerFromUnit(s)
call SetUnitOwner(s,Player(idx),true)
call GroupRemoveUnit(.g,s)
endif
call GroupRemoveUnit(temp,s)
endloop
call ReleaseGroup(temp)
set temp=null
endmethod
private method onDestroy takes nothing returns nothing
local integer i=0
local unit s
local integer idx
call ReleaseTimer(.t)
call DestroyEffect(.fx)
call KillUnit(.dum)
//revert creeps to original owner
loop
set s=FirstOfGroup(.g)
exitwhen s==null
set idx=GetIntegerFromUnit(s)
call SetUnitOwner(s,Player(idx),true)
call GroupRemoveUnit(.g,s)
endloop
call ReleaseGroup(.g)
//destroy lightning
loop
exitwhen i>LIGHTNING_NUM
call DestroyLightning(.lg[i])
call KillUnit(.missile[i])
set i=i+1
endloop
call GroupRemoveUnit(CastersGroup,.caster)
endmethod
endstruct
//rotate the lightning
private function Clock takes nothing returns nothing
local timer t = GetExpiredTimer()
local data d=data(GetTimerData(t))
local integer i=0
local real x1
local real y1
local real x2 = GetUnitX(d.dum)
local real y2 = GetUnitY(d.dum)
local real angle
local integer lvl=d.lvl
if (GetUnitCurrentOrder(d.caster)==OrderId(ORDER_ID)) then
call d.Update_Group()
call TimerStart(t,INTERVAL,false,function Clock)
loop
exitwhen i>LIGHTNING_NUM
set x1 = GetUnitX(d.missile[i])
set y1 = GetUnitY(d.missile[i])
//rotate counter clockwise
if CLOCKWISE==false then
set angle=d.angle[i]+ANGLE_INCREMENT
//rotate clockwise
else
set angle=d.angle[i]-ANGLE_INCREMENT
endif
//move lightning
call SetUnitPosition(d.missile[i], x2+RADIUS(lvl)*Cos(angle), y2+RADIUS(lvl)*Sin(angle))
call MoveLightningEx(d.lg[i],true,GetUnitX(d.dum),GetUnitY(d.dum),GetUnitFlyHeight(d.dum)+LIGHTNING_HEIGHT,GetUnitX(d.missile[i]),GetUnitY(d.missile[i]),GetUnitFlyHeight(d.missile[i]))
call DestroyEffect(AddSpecialEffectTarget(GROUND_FX, d.missile[i],"origin"))
set d.angle[i]=angle
set i=i+1
endloop
else
call d.destroy()
endif
set t=null
endfunction
//spell trigger actions
private function Actions takes nothing returns boolean
local timer t
local unit u=GetSpellAbilityUnit()
local data d
local integer i=0
local integer lvl=GetUnitAbilityLevel(u,SPELL_ID)
if (IsUnitInGroup(u,CastersGroup)==false and GetSpellAbilityId() == SPELL_ID) then
set t=NewTimer()
set d=data.create(u,t)
call SetTimerData(t,integer(d))
//create lightning
loop
exitwhen i>LIGHTNING_NUM
set d.missile[i]=CreateUnit(GetOwningPlayer(d.caster),DUMMY_ID, PolarProjectionX(GetUnitX(d.caster),RADIUS(lvl), 360/LIGHTNING_NUM*i), PolarProjectionY(GetUnitY(d.caster), RADIUS(lvl), 360/LIGHTNING_NUM*i), 0.00)
set d.lg[i]=AddLightningEx(LIGHTNING_ID,true,GetUnitX(d.dum),GetUnitY(d.dum),GetUnitFlyHeight(d.dum)+LIGHTNING_HEIGHT,GetUnitX(d.missile[i]),GetUnitY(d.missile[i]),GetUnitFlyHeight(d.missile[i]))
set d.angle[i]=bj_DEGTORAD*360/LIGHTNING_NUM*i
set i=i+1
endloop
call TimerStart(t,INTERVAL,false,function Clock)
set u=null
set t=null
return true
endif
set u=null
set t=null
return false
endfunction
private function init takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t,Condition(function Actions))
set b=Condition(function Spell_Filter)
set CastersGroup=NewGroup()
//Preload FX
call Preload(GROUND_FX)
call Preload(CASTER_FX)
call Preload(HEAL_FX)
endfunction
endlibrary