• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Apex Blizzard V1.00

  • Like
Reactions: StoPCampinGn00b
Zephyr Contest #15 entry - Apex Blizzard
Tooltip

Q - Apex Blizzard
Releases a torrent of ice from the target point which locks on to the first target hit dealing damage while engulfing them and all nearby units in ice, stunning them

Level 1 - 300 damage to primary target, 125 damage AOE, 3 second stun
Level 2 - 400 damage to primary target, 200 damage AOE, 4 second stun
Level 3 - 500 damage to primary target, 275 damage AOE, 5 second stun

Code

JASS:
////////////////////////////////////////////////////////////////////
//                       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                                                //
////////////////////////////////////////////////////////////////////

Spell Information

- 803 lines of pure JASS
- 42 Configurables reaching all aspects of the ability
- 1 ability, part skillshot part massive AOE obliteration
- Extensive Readme to help you configure the ability
- Doesn't interfere with game scorescreen
- Compatiable with Vanilla WE
- Functions for ground and flying heroes
- Visually works for both ground and flying targets
- Arcs are stable on uneven terrain
- Features can be adapted to your liking due to the configuration including complete removal
- Attractive yet functional
- Easy to reach target filtering (done through object data)
- Ignores magic immune enemies correctly
- Buffs for all status conditions
- Works well as a boss spell or a regular heroes ultimate (hope you don't miss)
- Swapping out models is easy to create one to match any element
- Makes you feel powerful
Tips and Tricks

- The seeking lines of the spell are consistent with every cast - use this knowledge to aim the ability to hit targets you want specifically
- If you cast the spell directly underneath your target it will activate the stun immediately
- Stuns can stack so you can keep refreezing an enemy you've caught (if the cooldown is short enough)
- The seeking lines work well to ward off enemies in a large AOE even if it is unlikely to hit them
Helpful Files

- Variable Creator: Copy and paste into your map, you now have all the variables needed for the spell to run
  • Variable Creator
    • Events
    • Conditions
    • Actions
      • Set APEX_AOE[0] = 0.00
      • Set APEX_AOEDamageHealth[0] = 0.00
      • Set APEX_AOEDamageMana[0] = 0.00
      • Set APEX_CurrentEffect[0] = APEX_CurrentEffect[0]
      • Set APEX_MapMaxX = 0.00
      • Set APEX_MapMaxY = 0.00
      • Set APEX_MapMinX = 0.00
      • Set APEX_MapMinY = 0.00
      • Set APEX_MaxSpeed[0] = 0
      • Set APEX_NO_PREV_STUN = False
      • Set APEX_NextNode[0] = 0
      • Set APEX_NodeNumber = 0
      • Set APEX_NotStunned[0] = False
      • Set APEX_OriginX[0] = 0.00
      • Set APEX_OriginY[0] = 0.00
      • Set APEX_OriginZ[0] = 0.00
      • Set APEX_Parent[0] = 0
      • Set APEX_Player[0] = APEX_Player[0]
      • Set APEX_PrevNode[0] = 0
      • Set APEX_ProjectileAcceleration[0] = 0.00
      • Set APEX_ProjectileAnimation[0] = 0
      • Set APEX_ProjectileCount[0] = 0
      • Set APEX_ProjectileDamageHealth[0] = 0.00
      • Set APEX_ProjectileDamageMana[0] = 0.00
      • Set APEX_ProjectileHeight[0] = 0.00
      • Set APEX_ProjectileHitBox[0] = 0.00
      • Set APEX_ProjectileLaunchPower[0] = 0.00
      • Set APEX_ProjectileScale[0] = 0.00
      • Set APEX_ProjectileTurnEfficiency[0] = 0.00
      • Set APEX_ProjectileTurnRate[0] = 0.00
      • Set APEX_ProjectileX[0] = 0.00
      • Set APEX_ProjectileY[0] = 0.00
      • Set APEX_Range[0] = 0.00
      • Set APEX_RecyclableNodes = 0
      • Set APEX_RecycleNodes[0] = 0
      • Set APEX_SpeedMultiplyer[0] = 0
      • Set APEX_StageID[0] = 0
      • Set APEX_StunDuration[0] = 0.00
      • Set APEX_StunDurationTimeout[0] = 0.00
      • Set APEX_StunStartMultiplyer[0] = 0.00
      • Set APEX_StunStartTimeout[0] = 0.00
      • Set APEX_Stunner = APEX_Stunner
      • Set APEX_Target[0] = APEX_Target[0]
      • Set APEX_TargetX[0] = 0.00
      • Set APEX_TargetY[0] = 0.00
      • Set APEX_TargetZ[0] = 0.00
      • Set APEX_TempGroup = APEX_TempGroup
      • Set APEX_TempReal[0] = 0.00
      • Set APEX_Timer = APEX_Timer
      • Set APEX_Unit[0] = APEX_Unit[0]
      • Set APEX_XVel[0] = 0.00
      • Set APEX_YVel[0] = 0.00
      • Set APEX_ZLoc = APEX_ZLoc
      • Set APEX_ZVel[0] = 0.00

WIP Links

- Zephyr Contest #15 - Model Based
- Zephyr Contest #15 - Model Based
Images

Tooltip

Levels

Showcases

GIFs


124031-1d60005599d7e80a1a7e430fbdfc2432.png

Level 1

Level 2

Level 3

Buff

124032-e0a7c3b6ef92f1bbc23872982d554d52.png
124033-a2fc770f39d71338860f47db79371cce.png
124034-8ec44299d42e1c2c35b31eb0537fe561.png
124039-0a0a63f3ab6eaa0c6805f20f4e34970f.png

Normal Cast

Frozen Units

Flyer Cast

Frozen Flyers

124035-7c7d0179f81a26cb7208c082f7593ce1.png
124036-d5d5b9c0d848295a1ac8398156997e71.png
124037-d07043af15b6c5849c54bba4a205acab.png
124038-658668c567aaf363a6fb0bcb4ef452ec.png

Basic Cast

Normal Usage

Hitting Flyers

124040-b481dbab0d2067e159ab16ccc49d4f33.gif
apex-blizzard-normal-cast-gif.264320
124041-2b4f630b8f4cccb25103ebc72861c48c.gif

[tr][/tr]
Contents

Apex Blizzard V1.00 (Map)

Reviews
KILLCIDE
Highest score in code for the contest, so not much to say here. I found your code and concept to be both the best out of everyone, hence why I voted for you. I read through IcemanBo's review for your entry, and I did not notice anything game breaking...
Level 37
Joined
Jul 22, 2015
Messages
3,485
Highest score in code for the contest, so not much to say here. I found your code and concept to be both the best out of everyone, hence why I voted for you. I read through IcemanBo's review for your entry, and I did not notice anything game breaking, so I have no reason not to approve this resource.

Needs Fixed

  • Nothing

Suggestions

  • Nothing

Status

Approved
 
Top