Name | Type | is_array | initial_value |
CreepLocs | location | Yes | |
i | integer | No |
//TESH.scrollpos=0
//TESH.alwaysfold=0
function Trig_Revive_Conditions takes nothing returns boolean
return GetOwningPlayer(GetTriggerUnit()) == Player(PLAYER_NEUTRAL_AGGRESSIVE)
endfunction
function Trig_Revive_Actions takes nothing returns nothing
local unit u = GetTriggerUnit()
local integer i = GetUnitUserData(u)
local integer id = GetUnitTypeId(u)
local real x = GetLocationX(udg_CreepLocs[i])
local real y = GetLocationY(udg_CreepLocs[i])
call TriggerSleepAction(30)
call CreateUnit(Player(PLAYER_NEUTRAL_AGGRESSIVE), id, x, y, GetRandomReal(0, 360))
call SetUnitUserData(u, i)
set u = null
endfunction
//===========================================================================
function InitTrig_Revive takes nothing returns nothing
set gg_trg_Revive = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( gg_trg_Revive, EVENT_PLAYER_UNIT_DEATH )
call TriggerAddCondition( gg_trg_Revive, Condition( function Trig_Revive_Conditions ) )
call TriggerAddAction( gg_trg_Revive, function Trig_Revive_Actions )
endfunction
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+)
//* ----------
//*
//* 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.wc3c.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.
//*
//* Multi-flavor:
//* Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************
//================================================================
globals
//How to tweak timer utils:
// USE_HASH_TABLE = true (new blue)
// * SAFEST
// * SLOWEST (though hash tables are kind of fast)
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true (orange)
// * kinda safe (except there is a limit in the number of timers)
// * ALMOST FAST
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
// * THE FASTEST (though is only faster than the previous method
// after using the optimizer on the map)
// * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
// work)
//
private constant boolean USE_HASH_TABLE = true
private constant boolean USE_FLEXIBLE_OFFSET = false
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 256
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
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
//If this happens then the QUANTITY rule has already been broken, try to fix the
// issue, else fail.
debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
static if( not USE_HASH_TABLE) then
debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
set tT[0]=CreateTimer()
static if( USE_FLEXIBLE_OFFSET) then
if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
else
if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
endif
endif
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==ARRAY_SIZE) 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
local integer i=0
local integer o=-1
local boolean oops = false
static if( USE_HASH_TABLE ) then
set ht = InitHashtable()
loop
exitwhen(i==QUANTITY)
set tT[i]=CreateTimer()
call SetTimerData(tT[i], HELD)
set i=i+1
endloop
set tN = QUANTITY
else
loop
set i=0
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set VOFFSET = GetHandleId(tT[i])
static if(USE_FLEXIBLE_OFFSET) then
set o=VOFFSET
else
set o=OFFSET
endif
endif
if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
exitwhen true
endif
if (GetHandleId(tT[i])-o>=0) then
set i=i+1
endif
endloop
set tN = i
exitwhen(tN == QUANTITY)
set oops = true
exitwhen not USE_FLEXIBLE_OFFSET
debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")
endloop
if(oops) then
static if ( USE_FLEXIBLE_OFFSET) then
debug call BJDebugMsg("The problem has been fixed.")
//If this message doesn't appear then there is so much
//handle id fragmentation that it was impossible to preload
//so many timers and the thread crashed! Therefore this
//debug message is useful.
elseif(DEBUG_MODE) then
call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
endif
endif
endif
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library GroupUtils initializer Init requires optional xebasic
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This library is a combination of several features relevant to groups. First
//* and foremost, it contains a group stack that you can access dynamic groups
//* from. It also provides means to refresh groups and clear any shadow
//* references within them. The included boolexprs are there for backwards
//* compatibility with maps that happen to use them. Since the 1.24c patch,
//* null boolexprs used in GroupEnumUnits* calls no longer leak, so there is no
//* performance gain to using the BOOLEXPR_TRUE constant.
//*
//* Instead of creating/destroying groups, we have moved on to recycling them.
//* NewGroup pulls a group from the stack and ReleaseGroup adds it back. Always
//* remember to call ReleaseGroup on a group when you are done using it. If you
//* fail to do so enough times, the stack will overflow and no longer work.
//*
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hashtable. 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. It is only worth doing this on
//* groups that you plan to have around for awhile.
//*
//* Constants that can be used from the library:
//* [group] ENUM_GROUP As you might expect, this group is good for
//* when you need a group just for enumeration.
//* [boolexpr] BOOLEXPR_TRUE This is a true boolexpr, which is important
//* because a 'null' boolexpr in enumeration
//* calls results in a leak. Use this instead.
//* [boolexpr] BOOLEXPR_FALSE This exists mostly for completeness.
//*
//* This library also includes a simple implementation of a group enumeration
//* call that factors collision of units in a given area of effect. This is
//* particularly useful because GroupEnumUnitsInRange doesn't factor collision.
//*
//* In your map, you can just replace all instances of GroupEnumUnitsInRange
//* with GroupEnumUnitsInArea with identical arguments and your spells will
//* consider all units colliding with the area of effect. After calling this
//* function as you would normally call GroupEnumUnitsInRange, you are free to
//* do anything with the group that you would normally do.
//*
//* If you don't use xebasic in your map, you may edit the MAX_COLLISION_SIZE
//* variable below and the library will use that as the added radius to check.
//* If you use xebasic, however, the script will automatically use xe's
//* collision size variable.
//*
//* You are also able to use GroupUnitsInArea. This function returns all units
//* within the area, no matter what they are, which can be convenient for those
//* instances where you actually want that.
//*
//* Example usage:
//* local group MyGroup = NewGroup()
//* call GroupRefresh(MyGroup)
//* call ReleaseGroup(MyGroup)
//* call GroupEnumUnitsInArea(ENUM_GROUP, x, y, 350., BOOLEXPR_TRUE)
//* call GroupUnitsInArea(ENUM_GROUP, x, y, 350.)
//*
globals
//If you don't have xebasic in your map, this value will be used instead.
//This value corresponds to the max collision size of a unit in your map.
private constant real MAX_COLLISION_SIZE = 197.
//If you are insane and don't care about any of the protection involved in
//this library, but want this script to be really fast, set this to true.
private constant boolean LESS_SAFETY = false
endglobals
globals
//* Constants that are available to the user
group ENUM_GROUP = CreateGroup()
boolexpr BOOLEXPR_TRUE = null
boolexpr BOOLEXPR_FALSE = null
endglobals
globals
//* Hashtable for debug purposes
private hashtable ht = InitHashtable()
//* Temporary references for GroupRefresh
private boolean Flag = false
private group Refr = null
//* Arrays and counter for the group stack
private group array Groups
private integer Count = 0
//* Variables for use with the GroupUnitsInArea function
private real X = 0.
private real Y = 0.
private real R = 0.
private hashtable H = InitHashtable()
endglobals
private function HookDestroyGroup takes group g returns nothing
if g == ENUM_GROUP then
call BJDebugMsg(SCOPE_PREFIX+"Warning: ENUM_GROUP destroyed")
endif
endfunction
debug hook DestroyGroup HookDestroyGroup
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
static if not LESS_SAFETY then
call SaveInteger(ht, 0, GetHandleId(Groups[Count]), 1)
endif
return Groups[Count]
endfunction
function ReleaseGroup takes group g returns boolean
local integer id = GetHandleId(g)
static if LESS_SAFETY then
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
else
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif not HaveSavedInteger(ht, 0, id) then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Group not part of stack")
return false
elseif LoadInteger(ht, 0, id) == 2 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Groups cannot be multiply released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
call SaveInteger(ht, 0, id, 2)
endif
call GroupClear(g)
set Groups[Count] = g
set Count = Count + 1
return true
endfunction
private function Filter takes nothing returns boolean
return IsUnitInRangeXY(GetFilterUnit(), X, Y, R)
endfunction
private function HookDestroyBoolExpr takes boolexpr b returns nothing
local integer bid = GetHandleId(b)
if HaveSavedHandle(H, 0, bid) then
//Clear the saved boolexpr
call DestroyBoolExpr(LoadBooleanExprHandle(H, 0, bid))
call RemoveSavedHandle(H, 0, bid)
endif
endfunction
hook DestroyBoolExpr HookDestroyBoolExpr
private constant function GetRadius takes real radius returns real
static if LIBRARY_xebasic then
return radius+XE_MAX_COLLISION_SIZE
else
return radius+MAX_COLLISION_SIZE
endif
endfunction
function GroupEnumUnitsInArea takes group whichGroup, real x, real y, real radius, boolexpr filter returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
local integer bid = 0
//Set variables to new values
set X = x
set Y = y
set R = radius
if filter == null then
//Adjusts for null boolexprs passed to the function
set filter = Condition(function Filter)
else
//Check for a saved boolexpr
set bid = GetHandleId(filter)
if HaveSavedHandle(H, 0, bid) then
//Set the filter to use to the saved one
set filter = LoadBooleanExprHandle(H, 0, bid)
else
//Create a new And() boolexpr for this filter
set filter = And(Condition(function Filter), filter)
call SaveBooleanExprHandle(H, 0, bid, filter)
endif
endif
//Enumerate, if they want to use the boolexpr, this lets them
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), filter)
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
function GroupUnitsInArea takes group whichGroup, real x, real y, real radius returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
//Set variables to new values
set X = x
set Y = y
set R = radius
//Enumerate
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), Condition(function Filter))
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
private function True takes nothing returns boolean
return true
endfunction
private function False takes nothing returns boolean
return false
endfunction
private function Init takes nothing returns nothing
set BOOLEXPR_TRUE = Condition(function True)
set BOOLEXPR_FALSE = Condition(function False)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//**************************************************************//
// //
// Chilling Streams //
// by //
// Justify //
// //
//**************************************************************//
//**************************************************************//
// //
// What you need to import: //
// Buff: Frozen //
// Spell: Chilling Streams //
// Spell: Chilling_Freeze //
// Unit: Frostorb //
// Unit: FreezeDummy //
// Trigger: Chilling Streams //
// Trigger: GroupUtils //
// Trigger: TimerUtils //
// //
//Don't forget to add "Frozen" as the buff to "Chilling_Freeze" //
// //
//**************************************************************//
//**************************************************************//
// //
// Dont forget to give credits to Pitzermike for his TreeFilter //
// //
// Have fun! :) //
// //
//**************************************************************//
//**************************************************************//
//TESH.scrollpos=264
//TESH.alwaysfold=0
scope ChillingStreams initializer InitStreams
private keyword Streams
globals
//ID of the spell "Chilling Streams"
private constant integer SpellId = 'A000'
//ID of the unit "FrostOrb"
private constant integer MissleId = 'h000'
//ID of the unit "Freezedummy"
private constant integer DummyId = 'h001'
//ID of the spell "Chilling_Freeze"
private constant integer FreezeId = 'A001'
//Period of the timer. Minimum should be 0.04, what means 25 moves per second
private constant real Period = 0.025
//Amount of the orbs
private constant integer Missles = 2
//How many different orbs need to hit a unit until it gets frozen
private constant integer FreezeHits = 2
//Distance of the orbs to the caster on creation
private constant real SideDist = 100.
//The startangle allows you to place the first orb: Casterfacing-StartAngle = Orbangle
private constant real StartAngle = 90.
//The flyingspeed of the orbs
private constant real Speed = 500.
//The distance the orbs fly
private constant real MaxDist = 800.
//How far the units may be away from the orb
private constant real HitRange = 100.
//Damage at level 1
private constant real StartDamage = 100.
//Damage added per level
private constant real AddDamage = 50.
//Animation that is shown while hitting a unit
private constant string HitAnimation = "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl"
//DON'T CHANGE ANYTHING UNDER THIS LINE UNLESS YOU WANT TO CHANGE THE SPELL ITSELF
//--------------------------------------------------------------------------------
private Streams array StreamData
private timer MoveTim
private integer StructCount = 0
private group TempGroup
private player TempPlayer
private rect TreeRect = Rect(0, 0, 0, 0)
private boolexpr Trees = null
private boolexpr Cond = null
endglobals
private struct Streams
unit caster
unit array missles [Missles]
integer level
player owner
real array step [Missles]
real array dist [Missles]
real array cos [Missles]
real array sin [Missles]
group array hit [Missles]
integer remaining
static method IsEnemy takes nothing returns boolean
return not IsUnitInGroup(GetFilterUnit(), TempGroup) and IsUnitEnemy(GetFilterUnit(), TempPlayer) and GetWidgetLife(GetFilterUnit()) > 0.405
endmethod
static method TreeFilter takes nothing returns boolean
local destructable d = GetFilterDestructable()
local boolean i = IsDestructableInvulnerable(d)
local unit u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DummyId,GetWidgetX(d), GetWidgetY(d), 0)
local boolean result = false
call UnitAddAbility(u, 'Ahrl')
if i then
call SetDestructableInvulnerable(d, false)
endif
set result = IssueTargetOrder(u, "harvest", d)
call RemoveUnit(u)
if i then
call SetDestructableInvulnerable(d, true)
endif
return result
endmethod
static method KillTrees takes nothing returns nothing
call KillDestructable(GetFilterDestructable())
endmethod
static method Move takes nothing returns nothing
local Streams data
local unit u
local unit dummy
local real x
local real y
local integer i = 0
local integer j = 0
local integer k = 0
local integer l = 0
local integer level
//loop through the structs
loop
exitwhen i >= StructCount
set data = StreamData[i]
//loop through the missles
loop
exitwhen j >= Missles
//I also could reorder the missles, effiecency should be the same
if data.missles[j] != null then
set x = GetUnitX(data.missles[j])+data.step[j]*data.cos[j]
set y = GetUnitY(data.missles[j])+data.step[j]*data.sin[j]
call SetUnitX(data.missles[j], x)
call SetUnitY(data.missles[j], y)
//Treefilter
call MoveRectTo(TreeRect, x, y)
call SetRect(TreeRect, x-HitRange, y-HitRange, x+HitRange, y+HitRange)
call EnumDestructablesInRect(TreeRect, Trees, function Streams.KillTrees)
set TempPlayer = data.owner
//We don't want a single unit to be hit twice
set TempGroup = data.hit[j]
call GroupEnumUnitsInRange(ENUM_GROUP, x, y, HitRange, Cond)
loop
set u = FirstOfGroup(ENUM_GROUP)
exitwhen u == null
call GroupAddUnit(data.hit[j], u)
call DestroyEffect(AddSpecialEffect(HitAnimation, x, y))
call UnitDamageTarget(data.caster, u, StartDamage+(data.level-1)*AddDamage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, WEAPON_TYPE_WHOKNOWS)
//l is the number of groups they're in, k is just the index
loop
exitwhen k >= Missles
if IsUnitInGroup(u, data.hit[k]) then
set l = l+1
endif
set k = k+1
endloop
if l >= FreezeHits then
set dummy = CreateUnit(data.owner, DummyId, x, y, 0)
call UnitAddAbility(dummy, FreezeId)
call IssueTargetOrder(dummy, "thunderbolt", u)
call UnitApplyTimedLife(dummy, 'BTLF', 1.0)
set dummy = null
endif
set l = 0
set k = 0
call GroupRemoveUnit(ENUM_GROUP, u)
endloop
set data.dist[j] = data.dist[j]+data.step[j]
if data.dist[j] >= MaxDist then
call KillUnit(data.missles[j])
set data.missles[j] = null
set data.remaining = data.remaining-1
if data.remaining <= 0 then
call data.destroy()
set StructCount = StructCount-1
set StreamData[i] = StreamData[StructCount]
if StructCount == 0 then
call ReleaseTimer(MoveTim)
endif
endif
endif
endif
set j = j+1
endloop
set j = 0
set i = i+1
endloop
endmethod
static method Create takes nothing returns boolean
local Streams data
local location target
local player p
local integer i = 0
local real x
local real y
local real mx
local real my
local real tx
local real ty
local real angle
local real face
if GetSpellAbilityId() == SpellId then
set data = Streams.allocate()
set tx = GetSpellTargetX()
set ty = GetSpellTargetY()
set data.caster = GetTriggerUnit()
set x = GetUnitX(data.caster)
set y = GetUnitY(data.caster)
//The facing isn't exact enough. You can see that the caster starts casting even before he faces the enemy
//Calculate the angle
set face = Atan2(ty-y, tx-x)*bj_RADTODEG
set data.owner = GetOwningPlayer(data.caster)
set data.level = GetUnitAbilityLevel(data.caster, SpellId)
set data.remaining = Missles
loop
exitwhen i >= Missles
set angle = (face-StartAngle+i*(360/Missles))*bj_DEGTORAD
set mx = x+SideDist*Cos(angle)
set my = y+SideDist*Sin(angle)
//This is just importent for THIS model! The trail of the orbs looks weird if its "looking" in another direction
//then it's moving
set angle = Atan2(ty-my, tx-mx)
set data.missles[i] = CreateUnit(data.owner, MissleId, mx, my, angle*bj_RADTODEG)
set data.cos[i] = Cos(angle)
set data.sin[i] = Sin(angle)
set data.step[i] = Speed*Period
set data.dist [i] = 0
set data.hit[i] = NewGroup()
set i = i+1
endloop
set StreamData[StructCount] = data
set StructCount = StructCount+1
if StructCount == 1 then
set MoveTim = NewTimer()
call TimerStart(MoveTim, Period, true, function Streams.Move)
endif
endif
return false
endmethod
method destroy takes nothing returns nothing
local integer i = 0
set .caster = null
loop
exitwhen i >= Missles
call ReleaseGroup(.hit[i])
set i = i+1
endloop
call .deallocate()
endmethod
endstruct
private function InitStreams takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
local unit u
call RemoveUnit(CreateUnit(Player(15), MissleId, 0, 0, 0))
set u = CreateUnit(Player(15), DummyId, 0, 0, 0)
call UnitAddAbility(u, FreezeId)
call RemoveUnit(u)
set u = null
loop
exitwhen i >= bj_MAX_PLAYER_SLOTS
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
set i = i+1
endloop
call TriggerAddCondition(t, Condition(function Streams.Create))
set Cond = Condition(function Streams.IsEnemy)
set Trees = Filter(function Streams.TreeFilter)
endfunction
endscope