//TESH.scrollpos=0
//TESH.alwaysfold=0
Name | Type | is_array | initial_value |
APEX_AOE | real | Yes | |
APEX_AOEDamageHealth | real | Yes | |
APEX_AOEDamageMana | real | Yes | |
APEX_CurrentEffect | effect | Yes | |
APEX_MapMaxX | real | No | |
APEX_MapMaxY | real | No | |
APEX_MapMinX | real | No | |
APEX_MapMinY | real | No | |
APEX_MaxSpeed | integer | Yes | |
APEX_NextNode | integer | Yes | |
APEX_NO_PREV_STUN | boolean | No | |
APEX_NodeNumber | integer | No | |
APEX_NotStunned | boolean | Yes | |
APEX_OriginX | real | Yes | |
APEX_OriginY | real | Yes | |
APEX_OriginZ | real | Yes | |
APEX_Parent | integer | Yes | |
APEX_Player | player | Yes | |
APEX_PrevNode | integer | Yes | |
APEX_ProjectileAcceleration | real | Yes | |
APEX_ProjectileAnimation | integer | Yes | |
APEX_ProjectileCount | integer | Yes | |
APEX_ProjectileDamageHealth | real | Yes | |
APEX_ProjectileDamageMana | real | Yes | |
APEX_ProjectileHeight | real | Yes | |
APEX_ProjectileHitBox | real | Yes | |
APEX_ProjectileLaunchPower | real | Yes | |
APEX_ProjectileScale | real | Yes | |
APEX_ProjectileTurnEfficiency | real | Yes | |
APEX_ProjectileTurnRate | real | Yes | |
APEX_ProjectileX | real | Yes | |
APEX_ProjectileY | real | Yes | |
APEX_Range | real | Yes | |
APEX_RecyclableNodes | integer | No | |
APEX_RecycleNodes | integer | Yes | |
APEX_SpeedMultiplyer | integer | Yes | |
APEX_StageID | integer | Yes | |
APEX_StunDuration | real | Yes | |
APEX_StunDurationTimeout | real | Yes | |
APEX_Stunner | unit | No | |
APEX_StunStartMultiplyer | real | Yes | |
APEX_StunStartTimeout | real | Yes | |
APEX_Target | unit | Yes | |
APEX_TargetX | real | Yes | |
APEX_TargetY | real | Yes | |
APEX_TargetZ | real | Yes | |
APEX_TempGroup | group | No | |
APEX_TempReal | real | Yes | |
APEX_Timer | timer | No | |
APEX_Unit | unit | Yes | |
APEX_XVel | real | Yes | |
APEX_YVel | real | Yes | |
APEX_ZLoc | location | No | |
APEX_ZVel | real | Yes |
////////////////////////////////////////////////////////////////////
// APEX BLIZZARD V1.00 //
// Author: Tank-Commander //
// Purpose: Designed for General Frank's Icecrown Overlord //
// to blast away vast swathes of enemies in a //
// literally cool style //
// //
// Notes: //
// - Read the readme before you try modifying the config //
// - Use the "Helpful files" to help you import the system //
// //
// Credits: //
// - (Dummy.mdl) Vexorian //
// - (IceCube.mdl) Pyritie //
// - (FrostNova.mdl) JetFangInferno //
// - (FrozenShell.mdl) Daelin //
// - (FrozenOrb.mdl) Daelin //
// //
// If you have used this spell in your map, you are required //
// to give credits to Tank-Commander for the creation of it //
// If you would like to use snippets of code from this for //
// whatever, getting permission and crediting the source/linking //
// would be much appreciated. //
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// README: //
// Before modifying this spell a few things need to be //
// understood and read, this is one of those things, while //
// most modification can be considered intuitive, it still //
// helps to read through these intstructions, as they will //
// inform you about how to configure this spell to your //
// desire. //
//----------------------------------------------------------------//
// Initial importing: The variable creator trigger can be //
// imported first and if you have the correct settings (file, //
// preferences, General, automatically create unknown variables //
// checked, then when you paste in the variable creator it //
// will automatically give you all the variables you need for //
// this spell //
// //
// While the remaining object editor based data is not required //
// to function (provided they're replaced with equivelents) //
// it's recommended that they are also imported, if their data //
// value are not the same as listed in the configuration, those //
// configurables will need to be changed to work correctly //
//----------------------------------------------------------------//
// MISC //
//----------------------------------------------------------------//
// TimerSpeed: the time in seconds between each iteration of //
// the main loop function (default is 0.031250000) it's //
// recommended you leave it like that //
constant function APEX_TimerSpeed takes nothing returns real
return 0.031250000
endfunction
//----------------------------------------------------------------//
// AbilityID: This is the data value used for the base of the //
// ability, it's recommended you base this off channel //
// (use Ctrl + D to view raw data in the object editor, do it //
// again to return to the normal view) //
constant function APEX_AbilityID takes nothing returns integer
return 'A001'
endfunction
//----------------------------------------------------------------//
// BuffID: This is the data value of the ability used to apply //
// the buff signifying that a unit is currently the target of //
// the spell, recommended to base this off slow aura //
constant function APEX_BuffID takes nothing returns integer
return 'A000'
endfunction
//----------------------------------------------------------------//
// StunAbility: This is the data value of the ability used to //
// stun the units that are frozen by the ability recommended to //
// base this of war stomp //
constant function APEX_StunAbilityID takes nothing returns integer
return 'A003'
endfunction
//----------------------------------------------------------------//
// StunBuffID: This is the data value of the buff that //
// signifies that a unit has been stunned, it should be applied //
// by the StunAbility when it stuns a unit //
constant function APEX_StunBuffID takes nothing returns integer
return 'B001'
endfunction
//----------------------------------------------------------------//
// DummyID: This is the data value of the dummy unit used as a //
// base for all of the effects of the spell, it should use //
// dummy.mdl as its model //
constant function APEX_DummyID takes nothing returns integer
return 'u000'
endfunction
//----------------------------------------------------------------//
// DummyPlayer: This is the player that has ownership of all //
// the dummy units created by the spell, setting it to 14 //
// ensures that the scoreboard is not interfered with //
constant function APEX_DummyPlayer takes nothing returns player
return Player(14)
endfunction
//----------------------------------------------------------------//
// StunOrderID: This is the Order ID for the stun ability, for //
// war stomp this is 852127, it must match or units will not be //
// stunned by the spell //
constant function APEX_StunOrderID takes nothing returns integer
return 852127
endfunction
//----------------------------------------------------------------//
// DAMAGE //
//----------------------------------------------------------------//
// Range: This is how far the seeking projectiles will travel //
// from the spell origin before being destroyed //
function APEX_Range takes real level returns real
return 300 + (50 * level)
endfunction
//----------------------------------------------------------------//
// AOE: This is the area of effect of the damage and stun of //
// the spell, it will be centered around the target rather than //
// the origin of the spell //
function APEX_AOE takes real level returns real
return 300 + (50 * level)
endfunction
//----------------------------------------------------------------//
// AOEDamageHealth: This is the amount of damage dealt to all //
// units within the AOE (primary target also takes this damage) //
function APEX_AOEDamageHealth takes real level returns real
return 50 + (75 * level)
endfunction
//----------------------------------------------------------------//
// AOEDamageMana: This is the amount of mana lossed by all //
// units within the AOE (primary target also takes this loss) //
function APEX_AOEDamageMana takes real level returns real
return 0.
endfunction
//----------------------------------------------------------------//
// ProjectileDamageHealth: This is the total amount of damage //
// the projectiles will deal to the primary target's health //
function APEX_ProjectileDamageHealth takes real level returns real
return 200 + (100 * level)
endfunction
//----------------------------------------------------------------//
// ProjectileDamageMana: This is the total amount of damage //
// the projectiles will deal to the primary target's mana //
function APEX_ProjectileDamageMana takes real level returns real
return 0.
endfunction
//----------------------------------------------------------------//
// StunDuration: This is how long all units are stunned by the //
// ability in seconds //
function APEX_StunDuration takes real level returns real
return 2. + (1. * level)
endfunction
//----------------------------------------------------------------//
// StunStartMaxTimeout: This is how long in seconds it takes //
// for the furthert targets (targets at max AOE distance) to //
// become stunned - this create a more natural expanding feel //
// to the stun //
function APEX_StunStartMaxTimeout takes real level returns real
return 0.15
endfunction
//----------------------------------------------------------------//
// AttachkType: This is the attack type used by all damaging //
// aspects of the spell //
constant function APEX_AttackType takes nothing returns attacktype
return ATTACK_TYPE_NORMAL
endfunction
//----------------------------------------------------------------//
// AttachkType: This is the damage type used by all damaging //
// aspects of the spell //
constant function APEX_DamageType takes nothing returns damagetype
return DAMAGE_TYPE_NORMAL
endfunction
//----------------------------------------------------------------//
// AttachkType: This is the weapon type used by all damaging //
// aspects of the spell //
constant function APEX_WeaponType takes nothing returns weapontype
return null
endfunction
//----------------------------------------------------------------//
// BEHAVIOUR //
//----------------------------------------------------------------//
// ProjectileCountCircle: This is the amount of seeking lines //
// created by the spell (more makes it easier to hit targets) //
function APEX_ProjectileCountCircle takes real level returns integer
return 10
endfunction
//----------------------------------------------------------------//
// ProjectileCountArc: This is the amount of projectiles that //
// are launched down each seek line, (more makes the seek line //
// longer and thus easier to hit targets) mostly makes the //
// spell look better //
function APEX_ProjectileCountArc takes real level returns integer
return 0 + (1 * R2I(level))
endfunction
//----------------------------------------------------------------//
// ProjectileHitBox: This is how close to another unit a //
// seeking projectile has to get in order to hit it and set it //
// as the spell target //
function APEX_ProjectileHitBox takes real level returns real
return 80.
endfunction
//----------------------------------------------------------------//
// TargetHeight: This is how far off the ground the projectiles //
// will aim to be (visual effect only) //
constant function APEX_TargetHeight takes nothing returns real
return 70.
endfunction
//----------------------------------------------------------------//
// ProjectileTurnRate: This is the strength at which the //
// Projectiles are pulled towards the target - this does affect //
// the speed of the projectile and should almost never be a //
// a negative value as that may cause crashes //
constant function APEX_ProjectileTurnRate takes real level returns real
return 1.
endfunction
//----------------------------------------------------------------//
// ProjectileTurnEfficiency: This is the rate at which existing //
// momentum is converted to be toward the target unit (this //
// will cause the projectile to slow when turning) 1 converts //
// 50% each cycle, 0.5 converts 25% and so on //
constant function APEX_ProjectileTurnEfficiency takes real level returns real
return 0.08
endfunction
//----------------------------------------------------------------//
// ProjectileAcceleration: This is the percentage increase in //
// speed the projectile gains or loses each cycle (1.01 being //
// an increase of 1% speed) //
constant function APEX_ProjectileAcceleration takes real level returns real
return 1.01
endfunction
//----------------------------------------------------------------//
// ProjectileLaunchPower: This is the net momentum that //
// projectiles are launched out of the origin point of the //
// spell //
constant function APEX_ProjectileLaunchPower takes real level returns real
return 40.
endfunction
//----------------------------------------------------------------//
// SpeedMultiplyer: This is the number of movement steps //
// calculated when a target has been caught, increasing this //
// number takes up much more processing but increases the speed //
// that projectiles hit their target without altering their //
// behaviour otherwise, 2-4 are safe values //
constant function APEX_SpeedMultiplyer takes real level returns integer
return 2
endfunction
//----------------------------------------------------------------//
// Gravity: This is the strength at which unguided projectiles //
// are pulled towards the ground regardless of their own //
// intended direction //
constant function APEX_Gravity takes nothing returns real
return 0.3065
endfunction
//----------------------------------------------------------------//
// MODELS //
//----------------------------------------------------------------//
// ProjectileAttachmentPoint: This is where on the dummy unit //
// that the ProjectileModel will be attached //
constant function APEX_ProjectileAttachmentPoint takes nothing returns string
return "origin"
endfunction
//----------------------------------------------------------------//
// ProjectileModel: This is the path for the model used for the //
// projecitles, remember to place double slashes ("\\") instead //
// of single slashes ("\") as this will cause a compilation //
// error //
constant function APEX_ProjectileModel takes nothing returns string
return "war3mapImported\\FrozenOrb.mdl"
endfunction
//----------------------------------------------------------------//
// ProjectileScale: This is the size of the projectiles relative //
// to the base model's size (1. = 100% or normal size) //
function APEX_ProjectileScale takes real level returns real
return 0.8 + (0.1 * level)
endfunction
//----------------------------------------------------------------//
// ExplosionAttachmentPoint: This is where on the dummy unit //
// that the explosion (AOE effect) will be attached //
constant function APEX_ExplosionAttachmentPoint takes nothing returns string
return "origin"
endfunction
//----------------------------------------------------------------//
// ExplosionModel: This is the path for the model used for the //
// explosion (AOE effect) //
constant function APEX_ExplosionModel takes nothing returns string
return "war3mapImported\\FrostNova.mdx"
endfunction
//----------------------------------------------------------------//
// TargetAttachmentPoint: This is where on the target unit that //
// the target marker (identifies that this unit is being //
// targetted by the spell) will be attached //
constant function APEX_TargetAttachmentPoint takes nothing returns string
return "chest"
endfunction
//----------------------------------------------------------------//
// TargetModel: This is the path for the model used for the //
// target marker //
constant function APEX_TargetModel takes nothing returns string
return "war3mapImported\\FrozenShell.MDX"
endfunction
//----------------------------------------------------------------//
// FrozenAttachmentPoint: This is where on frozen units that //
// the frozen effect (identifies that this unit is stunned) //
// will be attached //
constant function APEX_FrozenAttachmentPoint takes nothing returns string
return "origin"
endfunction
//----------------------------------------------------------------//
// FrozenModel: This is the path for the model used for the //
// frozen effect //
constant function APEX_FrozenModel takes nothing returns string
return "war3mapImported\\ice cube.mdx"
endfunction
//----------------------------------------------------------------//
// StartEffectModel: This is the path for the effect used at //
// start of the spell that projectiles are launched from //
constant function APEX_StartEffectModel takes nothing returns string
return "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl"
endfunction
//----------------------------------------------------------------//
// READ ONLY //
//----------------------------------------------------------------//
// SpellCoreStageID: This is the ID given to spell cores to //
// make sure they run their aspects of the code only //
constant function APEX_SpellCoreStageID takes nothing returns integer
return 1
endfunction
//----------------------------------------------------------------//
// ProjectileStageID: This is the ID given to proejctiles to //
// make sure they run their aspects of the code only //
constant function APEX_ProjectileStageID takes nothing returns integer
return 2
endfunction
//----------------------------------------------------------------//
// StunnedUnitStageID: This is the ID given to stunned units to //
// make sure they run their aspects of the code only //
constant function APEX_StunnedUnitStageID takes nothing returns integer
return 3
endfunction
//----------------------------------------------------------------//
// END OF CONFIGURATION //
//----------------------------------------------------------------//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Target filter function used to differentiate between units //
// that can be hit by the spell and those that cannot //
////////////////////////////////////////////////////////////////////
function APEX_TargetFilter takes unit u, player pl returns boolean
return (not IsUnitType(u, UNIT_TYPE_STRUCTURE)) and (not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)) and (IsUnitEnemy(u, pl)) and (GetUnitTypeId(u) != APEX_DummyID()) and not(IsUnitType(u, UNIT_TYPE_DEAD) or GetUnitTypeId(u) == 0)
endfunction
////////////////////////////////////////////////////////////////////
// Function used to get the Z location height at a given point //
////////////////////////////////////////////////////////////////////
function APEX_GetZ takes real x, real y returns real
call MoveLocation(udg_APEX_ZLoc, x, y)
return GetLocationZ(udg_APEX_ZLoc)
endfunction
////////////////////////////////////////////////////////////////////
// Function used to make sure that the location is within the //
// map bounds so that units cannot be moved outside of it and //
// get permanently stuck //
////////////////////////////////////////////////////////////////////
function APEX_ValidateLocation takes real x, real y returns boolean
//Check if the point is within the map bounds
return (x < udg_APEX_MapMaxX) and (x > udg_APEX_MapMinX) and (y < udg_APEX_MapMaxY) and (y > udg_APEX_MapMinY)
endfunction
////////////////////////////////////////////////////////////////////
// Function used to get the distance between two points (3D) //
////////////////////////////////////////////////////////////////////
function APEX_Get3DDistance takes real x, real x2, real y, real y2, real z, real z2 returns real
return SquareRoot((x2 - x) * (x2 - x) + (y2 - y) * (y2 - y) + (z2 - z) * (z2 - z))
endfunction
////////////////////////////////////////////////////////////////////
// Function used to get the distance between two points (2D) //
////////////////////////////////////////////////////////////////////
function APEX_Get2DDistance takes real x, real x2, real y, real y2 returns real
return SquareRoot((x2 - x) * (x2 - x) + (y2 - y) * (y2 - y))
endfunction
////////////////////////////////////////////////////////////////////
// Function used to remove instances from the linked list once //
// they have expired //
////////////////////////////////////////////////////////////////////
function APEX_RecycleNode takes integer node returns nothing
set udg_APEX_RecycleNodes[udg_APEX_RecyclableNodes] = node
set udg_APEX_RecyclableNodes = udg_APEX_RecyclableNodes + 1
set udg_APEX_NextNode[udg_APEX_PrevNode[node]] = udg_APEX_NextNode[node]
set udg_APEX_PrevNode[udg_APEX_NextNode[node]] = udg_APEX_PrevNode[node]
endfunction
////////////////////////////////////////////////////////////////////
// Function used to add new nodes to the linked list used by the //
// spell //
////////////////////////////////////////////////////////////////////
function APEX_CreateNode takes nothing returns integer
local integer node
if (udg_APEX_RecyclableNodes == 0) then
set udg_APEX_NodeNumber = udg_APEX_NodeNumber + 1
set node = udg_APEX_NodeNumber
else
set udg_APEX_RecyclableNodes = udg_APEX_RecyclableNodes - 1
set node = udg_APEX_RecycleNodes[udg_APEX_RecyclableNodes]
endif
set udg_APEX_NextNode[node] = 0
set udg_APEX_NextNode[udg_APEX_PrevNode[0]] = node
set udg_APEX_PrevNode[node] = udg_APEX_PrevNode[0]
set udg_APEX_PrevNode[0] = node
return node
endfunction
////////////////////////////////////////////////////////////////////
// Function used to handle the behaviour of the projectiles //
// while in-flight and reports if the projectile has crashed //
////////////////////////////////////////////////////////////////////
function APEX_MoveProjectile takes integer node returns boolean
//Set up locals
local real dy = udg_APEX_TargetY[node] - udg_APEX_ProjectileY[node]
local real dx = udg_APEX_TargetX[node]- udg_APEX_ProjectileX[node]
local real x
local real y
local real z
local real Angle = Atan2(dy, dx)
local real Angle2 = Atan2((APEX_GetZ(udg_APEX_TargetX[node], udg_APEX_TargetY[node]) + udg_APEX_TargetZ[node]) - (APEX_GetZ(udg_APEX_ProjectileX[node], udg_APEX_ProjectileY[node]) + GetUnitFlyHeight(udg_APEX_Unit[node])), Pow(dx * dx + dy * dy, 0.5))
local real TempReal = Pow((udg_APEX_ZVel[node] * udg_APEX_ZVel[node]) + (udg_APEX_XVel[node] * udg_APEX_XVel[node]) + (udg_APEX_YVel[node] * udg_APEX_YVel[node]), 0.5) * udg_APEX_ProjectileTurnEfficiency[udg_APEX_Parent[node]]
local real TempReal2 = 1/(1 + udg_APEX_ProjectileTurnEfficiency[udg_APEX_Parent[node]])
local unit u
//Calculate new velocities
set udg_APEX_ZVel[node] = ((udg_APEX_ZVel[node] + (TempReal + udg_APEX_ProjectileTurnRate[udg_APEX_Parent[node]]) * Sin(Angle2)) * udg_APEX_ProjectileAcceleration[udg_APEX_Parent[node]]) * TempReal2
set udg_APEX_XVel[node] = ((udg_APEX_XVel[node] + (TempReal + udg_APEX_ProjectileTurnRate[udg_APEX_Parent[node]]) * Cos(Angle) * Cos(Angle2)) * udg_APEX_ProjectileAcceleration[udg_APEX_Parent[node]]) * TempReal2
set udg_APEX_YVel[node] = ((udg_APEX_YVel[node] + (TempReal + udg_APEX_ProjectileTurnRate[udg_APEX_Parent[node]]) * Sin(Angle) * Cos(Angle2)) * udg_APEX_ProjectileAcceleration[udg_APEX_Parent[node]]) * TempReal2
set udg_APEX_ProjectileHeight[node] = udg_APEX_ProjectileHeight[node] + udg_APEX_ZVel[node] - APEX_Gravity()
set x = udg_APEX_ProjectileX[node] + udg_APEX_XVel[node]
set y = udg_APEX_ProjectileY[node] + udg_APEX_YVel[node]
set z = udg_APEX_ProjectileHeight[node] - APEX_GetZ(x, y)
//Make sure the location is within the map bounds
if APEX_ValidateLocation(x, y) then
set udg_APEX_ProjectileX[node] = x
set udg_APEX_ProjectileY[node] = y
call SetUnitX(udg_APEX_Unit[node], x)
call SetUnitY(udg_APEX_Unit[node], y)
endif
//Apply visuals
call SetUnitFlyHeight(udg_APEX_Unit[node], z, 0.)
call SetUnitFacing(udg_APEX_Unit[node], bj_RADTODEG * Atan2(udg_APEX_YVel[node], udg_APEX_XVel[node]))
set udg_APEX_ProjectileAnimation[node] = R2I(Atan2((udg_APEX_ZVel[node]), Pow((udg_APEX_XVel[node] * udg_APEX_XVel[node]) + (udg_APEX_YVel[node] * udg_APEX_YVel[node]), 0.5) * bj_RADTODEG + 0.5)) + 70
call SetUnitAnimationByIndex(udg_APEX_Unit[node], udg_APEX_ProjectileAnimation[node])
//Update target unit information if applicable
if (udg_APEX_Target[udg_APEX_Parent[node]] == null) then
//Check range from origin point
if (APEX_Get3DDistance(x, udg_APEX_OriginX[udg_APEX_Parent[node]], y, udg_APEX_OriginY[udg_APEX_Parent[node]], udg_APEX_ProjectileHeight[node], udg_APEX_OriginZ[udg_APEX_Parent[node]]) >= udg_APEX_Range[udg_APEX_Parent[node]]) then
//Too far, destroy
set udg_APEX_ProjectileCount[udg_APEX_Parent[node]] = udg_APEX_ProjectileCount[udg_APEX_Parent[node]] - 1
call DestroyEffect(udg_APEX_CurrentEffect[node])
call KillUnit(udg_APEX_Unit[node])
call APEX_RecycleNode(node)
else
//Look for targets
call GroupEnumUnitsInRange(udg_APEX_TempGroup, x, y, udg_APEX_ProjectileHitBox[udg_APEX_Parent[node]], null)
loop
set u = FirstOfGroup(udg_APEX_TempGroup)
exitwhen (u == null or not(udg_APEX_Target[udg_APEX_Parent[node]] == null))
if (APEX_TargetFilter(u, udg_APEX_Player[udg_APEX_Parent[node]])) then
//Target found, set up data
set udg_APEX_ProjectileDamageHealth[udg_APEX_Parent[node]] = udg_APEX_ProjectileDamageHealth[udg_APEX_Parent[node]] / udg_APEX_ProjectileCount[udg_APEX_Parent[node]]
set udg_APEX_ProjectileDamageMana[udg_APEX_Parent[node]] = udg_APEX_ProjectileDamageMana[udg_APEX_Parent[node]] / udg_APEX_ProjectileCount[udg_APEX_Parent[node]]
set udg_APEX_Target[udg_APEX_Parent[node]] = u
set udg_APEX_SpeedMultiplyer[udg_APEX_Parent[node]] = udg_APEX_MaxSpeed[udg_APEX_Parent[node]]
set udg_APEX_CurrentEffect[udg_APEX_Parent[node]] = AddSpecialEffectTarget(APEX_TargetModel(), u, APEX_TargetAttachmentPoint())
call UnitAddAbility(u, APEX_BuffID())
endif
call GroupRemoveUnit(udg_APEX_TempGroup, u)
endloop
endif
//Check if target has been hit
elseif (APEX_Get3DDistance(x, udg_APEX_TargetX[node], y, udg_APEX_TargetY[node], z, udg_APEX_TargetZ[node]) <= udg_APEX_ProjectileHitBox[udg_APEX_Parent[node]]) then
return true
else
//Update target point
set udg_APEX_TargetX[node] = GetUnitX(udg_APEX_Target[udg_APEX_Parent[node]])
set udg_APEX_TargetY[node] = GetUnitY(udg_APEX_Target[udg_APEX_Parent[node]])
set udg_APEX_TargetZ[node] = GetUnitFlyHeight(udg_APEX_Target[udg_APEX_Parent[node]]) + APEX_TargetHeight()
endif
return false
endfunction
////////////////////////////////////////////////////////////////////
// Function used to detect if a unit is currently stunned by //
// the spell to permit spell overwrite //
////////////////////////////////////////////////////////////////////
function APEX_GetStunNode takes unit u returns integer
local integer node = 0
loop
set node = udg_APEX_NextNode[node]
exitwhen node == 0 or udg_APEX_Unit[node] == u
endloop
if (node == 0) then
set node = APEX_CreateNode()
set udg_APEX_NO_PREV_STUN = true
else
set udg_APEX_NO_PREV_STUN = false
endif
return node
endfunction
////////////////////////////////////////////////////////////////////
// Function used to set up new units being stunned and adding //
// them to the linked list //
////////////////////////////////////////////////////////////////////
function APEX_FreezeUnit takes unit u, integer parent, real x, real y returns nothing
local integer node = APEX_GetStunNode(u)
set udg_APEX_Unit[node] = u
set udg_APEX_StageID[node] = APEX_StunnedUnitStageID()
if (udg_APEX_NO_PREV_STUN) then
set udg_APEX_NotStunned[node] = true
set udg_APEX_StunStartTimeout[node] = APEX_Get2DDistance(x, GetUnitX(u), y, GetUnitY(u)) * udg_APEX_StunStartMultiplyer[parent]
set udg_APEX_StunDurationTimeout[node] = udg_APEX_StunDuration[parent]
elseif udg_APEX_StunDuration[parent] > udg_APEX_StunDurationTimeout[node] then
set udg_APEX_StunDurationTimeout[node] = udg_APEX_StunDuration[parent]
endif
endfunction
////////////////////////////////////////////////////////////////////
// Function used to run the main engine of the spell, handles //
// creation of new units, buff control and projectile control //
////////////////////////////////////////////////////////////////////
function APEX_Loop takes nothing returns nothing
//Set up locals
local integer node = 0
local integer i = 0
local unit u
local real x
local real y
loop
set node = udg_APEX_NextNode[node]
exitwhen node == 0
//Handle projectiles
if (udg_APEX_StageID[node] == APEX_ProjectileStageID()) then
set i = 0
//Handle speed boost
loop
set i = i + 1
exitwhen ((i > udg_APEX_SpeedMultiplyer[udg_APEX_Parent[node]]) or IsUnitType(udg_APEX_Unit[node], UNIT_TYPE_DEAD))
//Move projectiles
if (APEX_MoveProjectile(node)) then
//Target hit damage and destroy projectile
call UnitDamageTarget(udg_APEX_Unit[udg_APEX_Parent[node]], udg_APEX_Target[udg_APEX_Parent[node]], udg_APEX_ProjectileDamageHealth[udg_APEX_Parent[node]], false, false, APEX_AttackType(), APEX_DamageType(), APEX_WeaponType())
call SetUnitState(udg_APEX_Target[udg_APEX_Parent[node]], UNIT_STATE_MANA, GetUnitState(udg_APEX_Target[udg_APEX_Parent[node]], UNIT_STATE_MANA) - udg_APEX_ProjectileDamageMana[udg_APEX_Parent[node]])
call DestroyEffect(udg_APEX_CurrentEffect[node])
call KillUnit(udg_APEX_Unit[node])
call APEX_RecycleNode(node)
set udg_APEX_ProjectileCount[udg_APEX_Parent[node]] = udg_APEX_ProjectileCount[udg_APEX_Parent[node]] - 1
endif
endloop
//Handle frozen units
elseif (udg_APEX_StageID[node] == APEX_StunnedUnitStageID()) then
if (udg_APEX_NotStunned[node]) then
if (udg_APEX_StunStartTimeout[node] <= 0.) then
//Stun the unit
set udg_APEX_CurrentEffect[node] = AddSpecialEffectTarget(APEX_FrozenModel(), udg_APEX_Unit[node], APEX_FrozenAttachmentPoint())
call SetUnitX(udg_APEX_Stunner, GetUnitX(udg_APEX_Unit[node]))
call SetUnitY(udg_APEX_Stunner, GetUnitY(udg_APEX_Unit[node]))
call IssueImmediateOrderById(udg_APEX_Stunner, APEX_StunOrderID())
call SetUnitTimeScale(udg_APEX_Unit[node], 0.)
set udg_APEX_NotStunned[node] = false
endif
set udg_APEX_StunStartTimeout[node] = udg_APEX_StunStartTimeout[node] - APEX_TimerSpeed()
elseif (udg_APEX_StunDurationTimeout[node] <= 0.) or (IsUnitType(udg_APEX_Unit[node], UNIT_TYPE_DEAD)) then
//Remove the stun
call UnitRemoveAbility(udg_APEX_Unit[node], APEX_StunBuffID())
call SetUnitTimeScale(udg_APEX_Unit[node], 1.)
call DestroyEffect(udg_APEX_CurrentEffect[node])
call APEX_RecycleNode(node)
//Stops the timer if this is the only remaining Node
if (udg_APEX_PrevNode[0] == 0) then
call PauseTimer(udg_APEX_Timer)
endif
else
set udg_APEX_StunDurationTimeout[node] = udg_APEX_StunDurationTimeout[node] - APEX_TimerSpeed()
endif
//Handle spell core
elseif (udg_APEX_ProjectileCount[node] == 0) then
if not(udg_APEX_Target[node] == null) then
//Play ending effect
call UnitRemoveAbility(udg_APEX_Target[node], APEX_BuffID())
call DestroyEffect(udg_APEX_CurrentEffect[node])
call DestroyEffect(AddSpecialEffectTarget(APEX_ExplosionModel(), udg_APEX_Target[node], APEX_ExplosionAttachmentPoint()))
//Damage and freeze units
set x = GetUnitX(udg_APEX_Target[node])
set y = GetUnitY(udg_APEX_Target[node])
call GroupEnumUnitsInRange(udg_APEX_TempGroup, x, y, udg_APEX_AOE[node], null)
loop
set u = FirstOfGroup(udg_APEX_TempGroup)
exitwhen u == null
if (APEX_TargetFilter(u, udg_APEX_Player[node])) then
call UnitDamageTarget(udg_APEX_Unit[node], u, udg_APEX_AOEDamageHealth[node], false, false, APEX_AttackType(), APEX_DamageType(), APEX_WeaponType())
call SetUnitState(u, UNIT_STATE_MANA, GetUnitState(u, UNIT_STATE_MANA) - udg_APEX_AOEDamageMana[node])
call APEX_FreezeUnit(u, node, x, y)
endif
call GroupRemoveUnit(udg_APEX_TempGroup, u)
endloop
endif
call APEX_RecycleNode(node)
//Stops the timer if this is the only remaining Node
if (udg_APEX_PrevNode[0] == 0) then
call PauseTimer(udg_APEX_Timer)
endif
endif
endloop
endfunction
////////////////////////////////////////////////////////////////////
// Function used to create new projectiles //
////////////////////////////////////////////////////////////////////
function APEX_CreateProjectile takes integer parent, real x, real y, real angle, real angle2, real arc returns nothing
local integer node = APEX_CreateNode()
//set up projectile data
set udg_APEX_Parent[node] = parent
set udg_APEX_Unit[node] = CreateUnit(APEX_DummyPlayer(), APEX_DummyID(), udg_APEX_OriginX[udg_APEX_Parent[node]], udg_APEX_OriginY[udg_APEX_Parent[node]], angle)
set udg_APEX_StageID[node] = APEX_ProjectileStageID()
set udg_APEX_CurrentEffect[node] = AddSpecialEffectTarget(APEX_ProjectileModel(), udg_APEX_Unit[node], APEX_ProjectileAttachmentPoint())
call SetUnitScale(udg_APEX_Unit[node], udg_APEX_ProjectileScale[parent], 0., 0.)
if UnitAddAbility(udg_APEX_Unit[node], 'Amrf') and UnitRemoveAbility(udg_APEX_Unit[node], 'Amrf') then
endif
set udg_APEX_ProjectileX[node] = udg_APEX_OriginX[udg_APEX_Parent[node]]
set udg_APEX_ProjectileY[node] = udg_APEX_OriginY[udg_APEX_Parent[node]]
set udg_APEX_TargetX[node] = x
set udg_APEX_TargetY[node] = y
set udg_APEX_TargetZ[node] = APEX_TargetHeight()
set udg_APEX_ProjectileHeight[node] = udg_APEX_OriginZ[udg_APEX_Parent[node]]
call SetUnitFlyHeight(udg_APEX_Unit[node], udg_APEX_ProjectileHeight[node] - udg_APEX_TempReal[udg_APEX_Parent[node]], 0.)
set udg_APEX_XVel[node] = udg_APEX_ProjectileLaunchPower[udg_APEX_Parent[node]] * Cos(angle2) * Cos(arc)
set udg_APEX_YVel[node] = udg_APEX_ProjectileLaunchPower[udg_APEX_Parent[node]] * Sin(angle2) * Cos(arc)
set udg_APEX_ZVel[node] = udg_APEX_ProjectileLaunchPower[udg_APEX_Parent[node]] * Sin(arc)
endfunction
////////////////////////////////////////////////////////////////////
// Function used to start an instance of the spell //
////////////////////////////////////////////////////////////////////
function APEX_OnCast takes nothing returns boolean
//Set up locals
local integer node
local integer spellID = GetSpellAbilityId()
local integer projCountCircle
local integer projCountArc
local integer i = 0
local integer i2
local real level
local real angle = 0
local real radAngle
local real angleIncrement
local real arc = 0
local real arcIncrement
local real x
local real y
local real z
//Check if something was infected
if (spellID == APEX_AbilityID()) then
//Set up spell data
set node = APEX_CreateNode()
set udg_APEX_Unit[node] = GetTriggerUnit()
set level = GetUnitAbilityLevel(udg_APEX_Unit[node], APEX_AbilityID())
set projCountCircle = APEX_ProjectileCountCircle(level)
set projCountArc = APEX_ProjectileCountArc(level)
set udg_APEX_StageID[node] = APEX_SpellCoreStageID()
set udg_APEX_Parent[node] = node
set udg_APEX_Target[node] = null
set udg_APEX_Player[node] = GetTriggerPlayer()
set udg_APEX_TempReal[node] = APEX_GetZ(udg_APEX_OriginX[node], udg_APEX_OriginY[node])
set udg_APEX_OriginX[node] = GetSpellTargetX()
set udg_APEX_OriginY[node] = GetSpellTargetY()
set udg_APEX_OriginZ[node] = udg_APEX_TempReal[node] + APEX_TargetHeight()
set udg_APEX_ProjectileScale[node] = APEX_ProjectileScale(level)
set udg_APEX_AOE[node] = APEX_AOE(level)
set udg_APEX_AOEDamageHealth[node] = APEX_AOEDamageHealth(level)
set udg_APEX_AOEDamageMana[node] = APEX_AOEDamageMana(level)
set udg_APEX_Range[node] = APEX_Range(level)
set udg_APEX_StunDuration[node] = APEX_StunDuration(level)
set udg_APEX_StunStartMultiplyer[node] = APEX_StunStartMaxTimeout(level) / udg_APEX_AOE[node]
set udg_APEX_ProjectileCount[node] = projCountCircle * projCountArc
set udg_APEX_ProjectileDamageHealth[node] = APEX_ProjectileDamageHealth(level)
set udg_APEX_ProjectileDamageMana[node] = APEX_ProjectileDamageMana(level)
set udg_APEX_ProjectileHitBox[node] = APEX_ProjectileHitBox(level)
set udg_APEX_ProjectileTurnRate[node] = APEX_ProjectileTurnRate(level)
set udg_APEX_ProjectileTurnEfficiency[node] = APEX_ProjectileTurnEfficiency(level)
set udg_APEX_ProjectileAcceleration[node] = APEX_ProjectileAcceleration(level)
set udg_APEX_ProjectileLaunchPower[node] = APEX_ProjectileLaunchPower(level)
set udg_APEX_SpeedMultiplyer[node] = 1
set udg_APEX_MaxSpeed[node] = APEX_SpeedMultiplyer(level)
//Start Timer
if udg_APEX_PrevNode[node] == 0 then
call TimerStart(udg_APEX_Timer, APEX_TimerSpeed(), true, function APEX_Loop)
endif
call DestroyEffect(AddSpecialEffect(APEX_StartEffectModel(), udg_APEX_OriginX[node], udg_APEX_OriginY[node]))
//Create projectiles
set angleIncrement = 360. / projCountCircle
set arcIncrement = bj_PI / 2. / projCountArc
loop
set i = i + 1
exitwhen i > projCountCircle
set i2 = 0
set angle = angle + angleIncrement
set radAngle = bj_DEGTORAD * angle
set arc = 0
set x = udg_APEX_OriginX[node] + udg_APEX_Range[node] * Cos(radAngle)
set y = udg_APEX_OriginY[node] + udg_APEX_Range[node] * Sin(radAngle)
loop
set i2 = i2 + 1
exitwhen i2 > projCountArc
call APEX_CreateProjectile(node, x, y, angle, radAngle, arc)
set arc = arc + arcIncrement
endloop
endloop
endif
return false
endfunction
////////////////////////////////////////////////////////////////////
// Function used to register the trigger of the ability //
////////////////////////////////////////////////////////////////////
function InitTrig_Apex_Blizzard takes nothing returns nothing
//Set up local variables
local trigger t = CreateTrigger()
//Set up trigger
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function APEX_OnCast))
set udg_APEX_ZLoc = Location(0., 0.)
//Sets up the variables used to make sure a point is within the map area
set udg_APEX_MapMaxX = GetRectMaxX(bj_mapInitialPlayableArea)
set udg_APEX_MapMinX = GetRectMinX(bj_mapInitialPlayableArea)
set udg_APEX_MapMaxY = GetRectMaxY(bj_mapInitialPlayableArea)
set udg_APEX_MapMinY = GetRectMinY(bj_mapInitialPlayableArea)
set udg_APEX_Stunner = CreateUnit(APEX_DummyPlayer(), APEX_DummyID(), 0., 0., 0.)
call UnitAddAbility(udg_APEX_Stunner, APEX_StunAbilityID())
endfunction
////////////////////////////////////////////////////////////////////
//END OF THE SPELL //
////////////////////////////////////////////////////////////////////