• 🏆 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!

Boss Ultimates Spellpack V1.02

[TD]Boss Ultimates Spellpack (or the BUS for short)[/TD]
What is this?

The BUS is a set of spells (and systems). They are powerful spells designed for bosses with ranging degrees of "flashiness". Each is designed to be able to be imported without the others (though all of them require the channel system) so you only need to take what you want.
Contains:

- Boss Channel (System) (and some default channels to get you started)
- Boss Knockback (System)
- Boss Soul Release (Spell)
- Boss Scathe (Spell)
- Boss Sheer Force (Spell)
- Boss Energy Spike (Spell)
Notes

- Every Ability/System comes with a variable creator specific to that ability/system
- If you want the knockback system but NOT the channel system (why on earth you would) then you'll need to move some of the initialisation for the channel system into the initialisation of the knockback system (Map Bounds variables, ZLoc)
- In the test map abilities can target buildings, this is for demonstration purposes ONLY as they intentionally cannot affect buildings
- All code created in JASS
- Channel System is GUI-Friendly
Boss Channel

What is this? - The boss channel system is designed to make your bosses look more intimidating and powerful when they cast their strong spells by introducing a "charge up" stage to them, it will work with target unit, target point and no target spells. All of the spells included in the BUS start by using this system, so for an idea of how this looks you can skip to the GIFs of each ability in their specific sections

JASS:
////////////////////////////////////////////////////////////////////
//             Boss Ultimate Spellpack Channel V1.02              //
//  Author: Tank-Commander                                        //
//  Purpose: Handles spell channelling within the spellpack       //
//  Used for: Soul Release, Scathe, Sheer Force, Energy Spike     //
//                                                                //
//  Notes:                                                        //
//    -  Read the readme before you try modifying the config      //
//    -  Use the "Helpful files" to help you import the system    //
//                                                                //
//  Credits:                                                      //
//    -  (Dummy.mdl) Vexorian                                     //
//                                                                //
//  If you have used this spellpack 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       //
//----------------------------------------------------------------//
//  CONFIGURABLE INFORMATION/HELP:                                //
//                                                                //
//  - Viewing data values: To see data values in the editor you   //
//  need to press Ctrl + D, to shift back to normal viewing       //
//  press it again                                                //
//                                                                //
//  - Effects: Pathnames for effects used in the spells should    //
//  have two "\"s throughout or the effect will not work (the     //
//  WE progress bar will not go away when saving, however if      //
//  fixed afterwards the save will still work, but the progress   //
//  bar will still remain until the WE is closed)                 //
//  e.g. "units\\human\\Footman\\Footman"                         //
//                                                                //
//  - Effect Scaling: Some effects have scale values below them   //
//  the scale determines the size of the effect and is expressed  //
//  as a real percentage (1.00 = 100%)                            //
//                                                                //
//  - Removing Effects: to remove an effect you don't want from   //
//  the ability, set the model path to that of Dummy.mdl          //
//                                                                //
//  - Timer: Some configurables have PerSecond values, the code   //
//  automatically accounts for changes to the timer as to         //
//  maintain consistency with what the user has chosen            //
//  All times in the system are expressions of seconds            //
//                                                                //
//----------------------------------------------------------------//
//  TimerSpeed: This is the amount of time in seconds between     //
//  each iteration of the channel loop function                   //
constant function BUSCR_TimerSpeed takes nothing returns real
    return 0.031250000
endfunction
//----------------------------------------------------------------//
//  DummyId: This is the raw data value of the dummy unit used    //
//  for creating the channeling effects it's advised this unit    //
//  uses Dummy.mdl for optimal usage, have death type set to      //
//  "can't raise does not decay" and a death time long enough     //
//  to play all effects (about 5.00 should be long enough) for    //
//  all effects)                                                  //
constant function BUSCR_DummyId takes nothing returns integer
    return 'u000'
endfunction
//----------------------------------------------------------------//
//  AttachmentPoint: This is the location on the dummy unit where //
//  effects will be placed                                        //
constant function BUSCR_AttachmentPoint takes nothing returns string
    return "origin"
endfunction
//----------------------------------------------------------------//
//  GroundMinAngle: This is the minimum angle off the floor that  //
//  all effects are made when the caster is a ground unit         //
constant function BUSCR_GroundMinAngle takes nothing returns real
    return 0.1
endfunction
//----------------------------------------------------------------//
//  DummyPlayer: This is the player that is given ownership of    //
//  all the dummy units used by the system                        //
constant function BUSCR_DummyPlayer takes nothing returns player
    return Player(14)
endfunction
//----------------------------------------------------------------//
//                      END OF CONFIGURATION                      //
//----------------------------------------------------------------//
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
//  RegisterChannel: This is the function used to register a new  //
//  type of channel (calling this function after setting up the   //
//  new channel will register it and allowit to be used)          //
////////////////////////////////////////////////////////////////////
function BUS_RegisterChannel takes integer target returns nothing
    local integer Id
  
    if (target == 0) then
        set udg_BUSCR_Counter = udg_BUSCR_Counter + 1
        set Id = udg_BUSCR_Counter
    else
        set Id = target
    endif
  
    set udg_BUSCR_Name[Id] = StringCase(udg_BUSCR_Name[0], true)
    set udg_BUSCR_Effect[Id] = udg_BUSCR_Effect[0]
    set udg_BUSCR_MinAOE[Id] = udg_BUSCR_MinAOE[0]
    set udg_BUSCR_MaxAOE[Id] = udg_BUSCR_MaxAOE[0]
    set udg_BUSCR_AbsorbAOE[Id] = udg_BUSCR_AbsorbAOE[0]
    set udg_BUSCR_MinSize[Id] = udg_BUSCR_MinSize[0]
    set udg_BUSCR_MaxSize[Id] = udg_BUSCR_MaxSize[0]
    set udg_BUSCR_SpawnRate[Id] = udg_BUSCR_SpawnRate[0]
    set udg_BUSCR_SpawnCount[Id] = udg_BUSCR_SpawnCount[0]
    set udg_BUSCR_Power[Id] = udg_BUSCR_Power[0]
    set udg_BUSCR_HueRed[Id] = udg_BUSCR_HueRed[0]
    set udg_BUSCR_HueGreen[Id] = udg_BUSCR_HueGreen[0]
    set udg_BUSCR_HueBlue[Id] = udg_BUSCR_HueBlue[0]
    set udg_BUSCR_HueAlpha[Id] = udg_BUSCR_HueAlpha[0]
    set udg_BUSCR_HueSpeed[Id] = udg_BUSCR_HueSpeed[0]
endfunction

////////////////////////////////////////////////////////////////////
//  GetChannelByName: This returns the Id number of the channel   //
//  which shares the name with the passed parameter, it can be    //
//  used to consistently refer to a channel without knowing the   //
//  order by which it was declared (allowing dynamic introduction //
//  of channels                                                   //
////////////////////////////////////////////////////////////////////
function BUS_GetChannelByName takes string name returns integer
    local integer iLoop = 0
  
    set name = StringCase(name, true)
  
    loop
        set iLoop = iLoop + 1
        exitwhen iLoop > udg_BUSCR_Counter
      
        if (name == udg_BUSCR_Name[iLoop]) then
            return iLoop
        endif
      
    endloop
  
    return 0

endfunction

////////////////////////////////////////////////////////////////////
//  GetZ: Used to get the Z height of a given location, used to   //
//  make sure that entities going over cliffs aren't incorrectly  //
//  moved/placed on the z plane                                   //
////////////////////////////////////////////////////////////////////
function BUS_GetZ takes real x, real y returns real

    call MoveLocation(udg_BUS_Loc, x, y)
    return GetLocationZ(udg_BUS_Loc)
  
endfunction

////////////////////////////////////////////////////////////////////

//  Function used to prevent two casts of the same channel being  //
//  done simultaneously                                           //
////////////////////////////////////////////////////////////////////
function BUS_CheckChannel takes unit u returns integer
    local integer Node = 0
  
    loop
        set Node = udg_BUSC_NextNode[Node]
        exitwhen (udg_BUSC_Unit[Node] == u)  or (Node == 0)
    endloop
  
    return Node

endfunction

////////////////////////////////////////////////////////////////////
//  ChannelLoop: The function used to control the channel this    //
//  includes creating entities and moving them toward the caster  //
//  checking if they are still casting the channel and activating //
//  the channel effects when it is completed                      //
////////////////////////////////////////////////////////////////////
function BUS_ChannelLoop takes nothing returns nothing
    local integer Node = 0
    local integer TempNode = 0
    local integer iLoop = 0
    local integer EffectCount = 0
    local real x
    local real x2
    local real x3
    local real y
    local real y2
    local real y3
    local real z
    local real z2
    local real dy
    local real dx
    local real Angle
    local real Angle2
    local real Distance
    local real TempReal
  
    loop
        exitwhen TempNode == 0
        set TempNode = udg_BUSC_PNextNode[TempNode]
    endloop
  
    loop
        set Node = udg_BUSC_NextNode[Node]
        exitwhen Node == 0
      
        set x = GetUnitX(udg_BUSC_Unit[Node])
        set y = GetUnitY(udg_BUSC_Unit[Node])
        set z = BUS_GetZ(x, y) + GetUnitFlyHeight(udg_BUSC_Unit[Node])
        set TempNode = 0

        if (udg_BUSC_CDuration[Node] > 0) then
            set udg_BUSC_CDuration[Node] = udg_BUSC_CDuration[Node] - BUSCR_TimerSpeed()
          
            if (udg_BUSC_CDelay[Node] <= BUSCR_TimerSpeed()) then
                set udg_BUSC_CDelay[Node] = udg_BUSC_Delay[Node]
              
                set iLoop = 0
                loop
                    set iLoop = iLoop + 1
                    exitwhen iLoop > udg_BUSCR_SpawnCount[udg_BUSC_ChannelId[Node]]
                      
                    if (udg_BUSC_PRecyclableNodes == 0) then
                        set udg_BUSC_PNodeNumber = udg_BUSC_PNodeNumber + 1
                        set TempNode = udg_BUSC_PNodeNumber
                    else
                        set udg_BUSC_PRecyclableNodes = udg_BUSC_PRecyclableNodes - 1
                        set TempNode = udg_BUSC_PRecycleNodes[udg_BUSC_PRecyclableNodes]
                    endif

                    set udg_BUSC_PNextNode[TempNode] = 0
                    set udg_BUSC_PNextNode[udg_BUSC_PPrevNode[0]] = TempNode
                    set udg_BUSC_PPrevNode[TempNode] = udg_BUSC_PPrevNode[0]
                    set udg_BUSC_PPrevNode[0] = TempNode

                    set Angle = GetRandomReal(0, bj_PI * 2)
                  
                    if (IsUnitType(udg_BUSC_Unit[Node], UNIT_TYPE_GROUND)) then
                        set Angle2 = GetRandomReal(BUSCR_GroundMinAngle(), bj_PI - BUSCR_GroundMinAngle())
                    else
                        set Angle2 = GetRandomReal(-bj_PI, bj_PI)
                    endif
                  
                    set TempReal = GetRandomReal(udg_BUSCR_MinAOE[udg_BUSC_ChannelId[Node]], udg_BUSCR_MaxAOE[udg_BUSC_ChannelId[Node]])
                    set Distance = Cos(Angle2) * TempReal
              
                    if Distance < 0 then
                        set Distance = Distance * -1
                    endif

                    set x2 = x + Distance * Cos(Angle)
                    set y2 = y + Distance * Sin(Angle)
                    set z2 = Sin(Angle2) * TempReal + z
                    set TempReal = GetRandomReal(udg_BUSCR_MinSize[udg_BUSC_ChannelId[Node]], udg_BUSCR_MaxSize[udg_BUSC_ChannelId[Node]])
              
                    set udg_BUSC_PCaster[TempNode] = udg_BUSC_Unit[Node]
                    set udg_BUSC_PXVelocity[TempNode] = 0
                    set udg_BUSC_PYVelocity[TempNode] = 0
                    set udg_BUSC_PZVelocity[TempNode] = 0
                    set udg_BUSC_PCurrentZ[TempNode] = z2
                  
                    set udg_BUSC_PUnit[TempNode] = CreateUnit(BUSCR_DummyPlayer(), BUSCR_DummyId(), x2, y2, Angle * bj_RADTODEG + 180)
                    if UnitAddAbility(udg_BUSC_PUnit[TempNode], 'Amrf') and UnitRemoveAbility(udg_BUSC_PUnit[TempNode], 'Amrf') then
                    endif
                    set udg_BUSC_PCurrentEffect[TempNode] = AddSpecialEffectTarget(udg_BUSCR_Effect[udg_BUSC_ChannelId[Node]], udg_BUSC_PUnit[TempNode], BUSCR_AttachmentPoint())
                    call SetUnitScale(udg_BUSC_PUnit[TempNode], TempReal, 0.00, 0.00)
                    call SetUnitFlyHeight(udg_BUSC_PUnit[TempNode], udg_BUSC_PCurrentZ[TempNode] - BUS_GetZ(x2,y2), 0.00) 
                endloop
              
            else
                set udg_BUSC_CDelay[Node] = udg_BUSC_CDelay[Node] - BUSCR_TimerSpeed()
            endif
      
            if (udg_BUSC_HueChange[Node]) then
                set udg_BUSC_CHueRed[Node] = udg_BUSC_CHueRed[Node] + udg_BUSC_HueChangeRed[Node]
                set udg_BUSC_CHueGreen[Node] = udg_BUSC_CHueGreen[Node] + udg_BUSC_HueChangeGreen[Node]
                set udg_BUSC_CHueBlue[Node] = udg_BUSC_CHueBlue[Node] + udg_BUSC_HueChangeBlue[Node]
                set udg_BUSC_CHueAlpha[Node] = udg_BUSC_CHueAlpha[Node] + udg_BUSC_HueChangeAlpha[Node]
              
                call SetUnitVertexColor(udg_BUSC_Unit[Node], udg_BUSC_CHueRed[Node], udg_BUSC_CHueGreen[Node], udg_BUSC_CHueBlue[Node], udg_BUSC_CHueAlpha[Node])
                if (udg_BUSC_CChangeHueDelay[Node] < 0) then
                    set udg_BUSC_CChangeHueDelay[Node] = udg_BUSCR_HueSpeed[udg_BUSC_ChannelId[Node]]
                    set udg_BUSC_HueChangeRed[Node] = udg_BUSC_HueChangeRed[Node] * -1
                    set udg_BUSC_HueChangeGreen[Node] = udg_BUSC_HueChangeGreen[Node] * -1
                    set udg_BUSC_HueChangeBlue[Node] = udg_BUSC_HueChangeBlue[Node] * -1
                    set udg_BUSC_HueChangeAlpha[Node] = udg_BUSC_HueChangeAlpha[Node] * - 1
                else
                    set udg_BUSC_CChangeHueDelay[Node] = udg_BUSC_CChangeHueDelay[Node] - BUSCR_TimerSpeed()
                endif
              
            endif
      
        elseif (udg_BUSC_Running[Node]) then
            set udg_BUS_Unit = udg_BUSC_Unit[Node]
            set udg_BUS_Target = udg_BUSC_Target[Node]
            set udg_BUS_X = udg_BUSC_TargetX[Node]
            set udg_BUS_Y = udg_BUSC_TargetY[Node]
            set udg_BUSC_Running[Node] = false
            set udg_BUSC_Event = 0
            set udg_BUSC_Event = udg_BUSC_Ability[Node]
        endif
          
        set TempNode = 0
        set EffectCount = 0
      
        loop
            set TempNode = udg_BUSC_PNextNode[TempNode]
            exitwhen TempNode == 0
          
            if (udg_BUSC_PCaster[TempNode] == udg_BUSC_Unit[Node]) then
                set x2 = GetUnitX(udg_BUSC_PUnit[TempNode])
                set y2 = GetUnitY(udg_BUSC_PUnit[TempNode])
                set z2 = BUS_GetZ(x2, y2) + GetUnitFlyHeight(udg_BUSC_PUnit[TempNode])
                set Distance = SquareRoot((x - x2) * (x - x2) + (y - y2) * (y - y2) + (z - z2) * (z - z2))
                if ((Distance < udg_BUSCR_AbsorbAOE[udg_BUSC_ChannelId[Node]]) or (not(GetUnitCurrentOrder(udg_BUSC_Unit[Node]) == udg_BUSC_Order[Node]) or udg_BUSC_Cancel[Node])) then
                    call DestroyEffect(udg_BUSC_PCurrentEffect[TempNode])
                    call KillUnit(udg_BUSC_PUnit[TempNode])
              
                    set udg_BUSC_PRecycleNodes[udg_BUSC_PRecyclableNodes] = TempNode
                    set udg_BUSC_PRecyclableNodes = udg_BUSC_PRecyclableNodes + 1
                    set udg_BUSC_PNextNode[udg_BUSC_PPrevNode[TempNode]] = udg_BUSC_PNextNode[TempNode]
                    set udg_BUSC_PPrevNode[udg_BUSC_PNextNode[TempNode]] = udg_BUSC_PPrevNode[TempNode]
              
                    if (udg_BUSC_NextNode[0] + udg_BUSC_NextNode[0] == 0) then
                        call PauseTimer(udg_BUSC_Timer)
                    endif
                else
                    set EffectCount = EffectCount + 1
                    set dy = y - y2
                    set dx = x - x2
                    set Angle = Atan2(dy, dx)
                    set Angle2 = Atan2(z - z2, SquareRoot((dx * dx) + (dy * dy)))
                    set TempReal = udg_BUSCR_Power[udg_BUSC_ChannelId[Node]] / Distance
                    set udg_BUSC_PZVelocity[TempNode] = udg_BUSC_PZVelocity[TempNode] + TempReal * Sin(Angle2)
                    set udg_BUSC_PXVelocity[TempNode] = udg_BUSC_PXVelocity[TempNode] + TempReal * Cos(Angle) * Cos(Angle2)
                    set udg_BUSC_PYVelocity[TempNode] = udg_BUSC_PYVelocity[TempNode] + TempReal * Sin(Angle) * Cos(Angle2)

                    set udg_BUSC_PCurrentZ[TempNode] = udg_BUSC_PCurrentZ[TempNode] + udg_BUSC_PZVelocity[TempNode]
                    set x3 = x2 + udg_BUSC_PXVelocity[TempNode]
                    set y3 = y2 + udg_BUSC_PYVelocity[TempNode]

                    if ((udg_BUS_MapMinX <= x3) and (x3 <= udg_BUS_MapMaxX) and (udg_BUS_MapMinY <= y3)and (y3 <= udg_BUS_MapMaxY)) then
                        call SetUnitX(udg_BUSC_PUnit[TempNode], x3)
                        call SetUnitY(udg_BUSC_PUnit[TempNode], y3)
                    endif
                  
                    call SetUnitFlyHeight(udg_BUSC_PUnit[TempNode], udg_BUSC_PCurrentZ[TempNode] - BUS_GetZ(x3,y3), 0.00) 
                    call SetUnitAnimationByIndex(udg_BUSC_PUnit[TempNode], R2I(Atan2(udg_BUSC_PZVelocity[TempNode], SquareRoot((udg_BUSC_PXVelocity[TempNode] * udg_BUSC_PXVelocity[TempNode]) + (udg_BUSC_PYVelocity[TempNode] * udg_BUSC_PYVelocity[TempNode]))) * bj_RADTODEG + 0.5) + 90)
                endif

            endif

        endloop
      
        if (EffectCount == 0) and ((udg_BUSC_CDuration[Node] <= 0) or not(GetUnitCurrentOrder(udg_BUSC_Unit[Node]) == udg_BUSC_Order[Node] or udg_BUSC_Cancel[Node])) then
          
            if (udg_BUSC_HueChange[Node]) then
                call SetUnitVertexColor(udg_BUSC_Unit[Node], 255, 255, 255, 255)
            endif
          
            set udg_BUSC_RecycleNodes[udg_BUSC_RecyclableNodes] = Node
            set udg_BUSC_RecyclableNodes = udg_BUSC_RecyclableNodes + 1
            set udg_BUSC_NextNode[udg_BUSC_PrevNode[Node]] = udg_BUSC_NextNode[Node]
            set udg_BUSC_PrevNode[udg_BUSC_NextNode[Node]] = udg_BUSC_PrevNode[Node]
          
            if (udg_BUSC_NextNode[0] + udg_BUSC_NextNode[0] == 0) then
                call PauseTimer(udg_BUSC_Timer)
            endif
          
        endif
          
    endloop
  
endfunction

////////////////////////////////////////////////////////////////////
//  StartChannel: Used to start a channeling effect on a spell    //
//  Calling this function, passing the ability Id, the unit, any  //
//  target unit or point, the duration of the channel and the     //
//  duration of the channel (as well as if the channel will       //
//  include a hue change)                                         //
////////////////////////////////////////////////////////////////////
function BUS_StartChannel takes integer Id, unit u, unit Target, real x, real y, real Duration, real Event, boolean Hue returns nothing
    local integer Node = 0
    local real TempReal = 0
  
    set udg_BUSC_Cancel[BUS_CheckChannel(u)] = true

    if (udg_BUSC_RecyclableNodes == 0) then
        set udg_BUSC_NodeNumber = udg_BUSC_NodeNumber + 1
        set Node = udg_BUSC_NodeNumber
    else
        set udg_BUSC_RecyclableNodes = udg_BUSC_RecyclableNodes - 1
        set Node = udg_BUSC_RecycleNodes[udg_BUSC_RecyclableNodes]
    endif

    set udg_BUSC_NextNode[Node] = 0
    set udg_BUSC_NextNode[udg_BUSC_PrevNode[0]] = Node
    set udg_BUSC_PrevNode[Node] = udg_BUSC_PrevNode[0]
    set udg_BUSC_PrevNode[0] = Node
  
    set udg_BUSC_ChannelId[Node] = Id
    set udg_BUSC_Ability[Node] = Event
    set udg_BUSC_Unit[Node] = u
    set udg_BUSC_Target[Node] = Target
    set udg_BUSC_TargetX[Node] = x
    set udg_BUSC_TargetY[Node] = y
    set udg_BUSC_Order[Node] = GetUnitCurrentOrder(u)
    set udg_BUSC_Cancel[Node] = false
    set udg_BUSC_Running[Node] = true
    set udg_BUSC_CDuration[Node] = Duration
    set udg_BUSC_Delay[Node] = 1. / udg_BUSCR_SpawnRate[Id]
    set udg_BUSC_CDelay[Node] = udg_BUSC_Delay[Node]
    set udg_BUSC_HueChange[Node] = Hue
    set udg_BUSC_CHueRed[Node] = 255
    set udg_BUSC_CHueGreen[Node] = 255
    set udg_BUSC_CHueBlue[Node] = 255
    set udg_BUSC_CHueAlpha[Node] = 255
    set TempReal = (1 / udg_BUSCR_HueSpeed[Id]) * BUSCR_TimerSpeed()
    set udg_BUSC_HueChangeRed[Node] = R2I((udg_BUSCR_HueRed[Id] - udg_BUSC_CHueRed[Node]) * TempReal)
    set udg_BUSC_HueChangeGreen[Node] = R2I((udg_BUSCR_HueGreen[Id] - udg_BUSC_CHueGreen[Node]) * TempReal)
    set udg_BUSC_HueChangeBlue[Node] = R2I((udg_BUSCR_HueBlue[Id] - udg_BUSC_CHueBlue[Node]) * TempReal)
    set udg_BUSC_HueChangeAlpha[Node] = R2I((udg_BUSCR_HueAlpha[Id] - udg_BUSC_CHueAlpha[Node]) * TempReal)
    set udg_BUSC_CChangeHueDelay[Node] = udg_BUSCR_HueSpeed[Id]

    if (udg_BUSC_PrevNode[Node] == 0) then
        call TimerStart(udg_BUSC_Timer, BUSCR_TimerSpeed(), true, function BUS_ChannelLoop)
    endif

endfunction

////////////////////////////////////////////////////////////////////
//  InitTrig BUS Channel: Sets up the map bounds and the location //
//  used for getting Z locations and the timer used to run the    //
//  loop                                                          //
////////////////////////////////////////////////////////////////////
function InitTrig_BUS_Channel takes nothing returns nothing
    set udg_BUS_Loc = Location(0,0)

    set udg_BUSC_Timer = CreateTimer()

    set udg_BUS_MapMaxX = GetRectMaxX(bj_mapInitialPlayableArea)
    set udg_BUS_MapMinX = GetRectMinX(bj_mapInitialPlayableArea)
    set udg_BUS_MapMaxY = GetRectMaxY(bj_mapInitialPlayableArea)
    set udg_BUS_MapMinY = GetRectMinY(bj_mapInitialPlayableArea)
endfunction

////////////////////////////////////////////////////////////////////
//  End of the system                                             //
////////////////////////////////////////////////////////////////////
JASS:
This user guide explains how to use the BUS Channel system for your own abilities.

Instructions:

1) You will need to define some channel types, to do this follow the layout of "BUS Register Channels"
 - Set up all the variables which start with the acronym "BUSCR_" to the value you desire
 - Follow "Channel Register Explanation" for a more in-depth coverage of this
 - Do not set udg_BUSCR_HueSpeed[0] = 0 this will cause the channel to be unusable
 - Make sure that they all have 0 as their index number and use the function below at the end.
   call BUS_RegisterChannel(0)
 - This will register your channel and give it an index number for you to refer to it by
 - If you do not know its index number (they are assigned from 1+ in order of registering) use the below function
   call BUS_GetChannelByName("Your Channel Name")
   This is function is case insensitive
 
2) If you want to overwrite an existing channel or want to specify the index number specifically you can use these:
 - call Bus_RegisterChannel(i) Where i is the index number you wish to register it by (i > 0)
 - call BUS_RegisterChannel(BUS_GetChannelByName("Your Channel Name"))
 
3) If you wish to make an ability which uses the channel system and you have completed the above steps
 - You need to initialise a trigger which activates when the ability begins being channeled
 - You then need to initialise a trigger which activates when BUS_Event becomes equal to the ability event number
 - This second trigger will only activate if the channel is completed, so interrupting and cancelling work as normal
 - All abilities need a unique event number this can be any real number so long as only one ability uses it
 - A completed JASS example can be found at Example 1, A completed GUI example can be found aT Example 1 & 2
 - The Start channel function has many parameters that it can be passed, with quite a few of them being optional
 
call BUS_StartChannel(BUS_GetChannelByName("My Channel Type"), Caster, TargetUnit, TargetX., TargetY., ChannelDuration, EventNumber,  HaveHueChange)

 - It is not necessary to pass a Target unit, Target X or Target Y as some abilities may not have or use these things
 - If the channel type requested does not exist (the string inputted is wrong) then the last defined channel will
   be used by default, if you pass a number which does not correspond to any defined channel, there will be an error
   so it is safer to use BUS_GetChannelByName
 - Make sure the event number passed matches the event number defined to activate the right function
   example 1 uses 5.00 and example 2 uses 6.00 for instance, if they used the same value then both triggers would fire
 - While most parameters are self-explanatory the last one "HaveHueChange" is a bit less clear:
   It is a boolean value (true/false)
   It determines whether or not the caster has their hue (colour) changed while they are channeling the ability
   It assumes that the caster has a color of 255/255/255/255 (Red/Green/Blue/Alpha) so if your caster is not then
   you should always set this to false
 
4) Certain globals will have data which can be used by the event function (Example 1 first function, Example 3)
 - udg_BUS_Unit Will contain the caster unit, it must be used
 - udg_BUS_Target will contain the target unit, it should be null if not used
 - udg_BUS_X will contain the X co-ordinate of the target point, it should be 0 if not used
 - udg_BUS_Y will contain the Y co-ordinate of the target point, it should be 0 if not used
JASS:
This section covers the different variables when setting up a channel and what they do

Lets take a look at the first channel from "BUS Register Channel"
    set udg_BUSCR_Name[0] = "Arcane Charge"
    set udg_BUSCR_Effect[0] = "Abilities\\Spells\\Human\\ManaFlare\\ManaFlareTarget.mdl"
    set udg_BUSCR_MinAOE[0] = 200
    set udg_BUSCR_MaxAOE[0] = 300.
    set udg_BUSCR_AbsorbAOE[0] = 50.
    set udg_BUSCR_MinSize[0] = 0.1
    set udg_BUSCR_MaxSize[0] = 1.5
    set udg_BUSCR_SpawnRate[0] = 50
    set udg_BUSCR_SpawnCount[0] = 2
    set udg_BUSCR_Power[0] = 60
    set udg_BUSCR_HueRed[0] = 60
    set udg_BUSCR_HueGreen[0] = 60
    set udg_BUSCR_HueBlue[0] = 255
    set udg_BUSCR_HueAlpha[0] = 255
    set udg_BUSCR_HueSpeed[0] = 0.50
    call BUS_RegisterChannel(0)

set udg_BUSCR_Name[0] = "Arcane Charge"
 - This is the name the channel is given and used to locate it with BUS_GetChannelByName("Your Channel Name")
 - Names are case insensitive so it does not matter if this is upper, lower or any mix of the few
 - Remembering if there is spacing however is important
 - This attribute exists as names are significantly easier to remember than ID numbers
 
set udg_BUSCR_Effect[0] = "Abilities\\Spells\\Human\\ManaFlare\\ManaFlareTarget.mdl"
 - This is the filepath used for the effect of the channel - the particles that appear and are drawn toward the caster
 - The double "\"s are important and if you are getting a string from the object editor it will only have one, be sure
   to add the other ones in before you save (this may cause the progress bar to appear stuck, though correcting and
   attempting to save again will work, even if it remains bugged
 - Having an incorrect path may bug the system, as well as make no effect appear when you are channeling
 
set udg_BUSCR_MinAOE[0] = 200
set udg_BUSCR_MaxAOE[0] = 300.
 - These both are very similar - they determine how far away from the caster effects may spawn
 - The gap between these two values is the effective range (all effects will spawn in this range)
 - Technically these can take negative values, but it is not recommended as the facing angle of effects will be wrong
 
set udg_BUSCR_AbsorbAOE[0] = 50.
 - This is the distance from the caster that the effects must have (or be closer than) before they are destroyed
 - Ideally this is close to melee range, and should be much lower then the MinAOE (effects may spawn which are
   instantly removed which is essentially pointless, if they are the same)
 
set udg_BUSCR_MinSize[0] = 0.1
set udg_BUSCR_MaxSize[0] = 1.5
 - These are scaling controls for the effects - determining the upper and lower bounds of their sizes
 - They are done in terms of percentages (0.1 = 10%, 1.5 = 150%)
 - Most models scale differently so you are unlikely to get the desired sizes first try if you estimate
 - setting these values to negatives will invert the models (which may be desirable depending on the effect)
 
set udg_BUSCR_SpawnRate[0] = 50
 - The spawn rate is a measure of how many sets of effects are made per second
 - This is inherently limited by the timer speed used by the system (default 0.03125)
 - The maximum amount of spawns per second is 32, so setting this value to 32 should reach these maximum but
   due to inaccuracies in floating point numbers, it is safer to use a higher number to ensure all 32 sets are made

set udg_BUSCR_SpawnCount[0] = 2
 - The spawn count is a measure of how many effects are created within each set
 - Unlike the spawn rate this is not limited by the timer but rather by the Warcraft 3 engine with an approximate
   maximum of 400 effects being able to be created at any given time, though to do this would cause massive amounts
   of lag and lead to freezing, particularly if the spawn rate is also high
 - The total amount of effects spawned can be calculated via (ChannelDuration * SpawnRate * SpawnCount) where the
   duration is in seconds, so be mindful of this when setting up your channels
  
set udg_BUSCR_Power[0] = 60
 - The power of a channel refers to how strongly the effects are pulled into the caster, the higher this value
   the more visually powerful the channel looks (particularly if accompanied by a high spawn rate) conversely a
   lower value leads to a more aura-like channel effect

set udg_BUSCR_HueRed[0] = 60
set udg_BUSCR_HueGreen[0] = 60
set udg_BUSCR_HueBlue[0] = 255
set udg_BUSCR_HueAlpha[0] = 255
 - These values determine the Red/Green/Blue/Alpha extremes of a hue (colour) change respectively assuming the
   instance of the channel is using them
 - The system assumes that the default values are 255/255/255/255 respectively for any given caster, these are
   the values that it will periodically oscillate to (go to and from) during a channel
 - Alpha refers to the transparency of a unit, 0 leads to a completely invisible unit

set udg_BUSCR_HueSpeed[0] = 0.50
 - This determines the time it takes for one oscillation to complete (go from default colours to the above hue values
 - A complete oscillation is therefore double this time
 - When creating a spell it is good to consider what the colour will be when the channel is over, in this case so long
   as the channel duration is in complete seconds (1.00, 2.00, 3.00 etc.) then the caster will be at their original
   colours when the channel is complete, conversely if ending at a half second (0.50, 1.50, 2.50, etc.) then they
   will be at the extreme end of the colour change, however when a channel completes the unit is automatically
   restored to default colouring, so this is only important to consider if you wish to avoid a sudden colour change
   (this will still happen if the channel is cancelled)

You have reached the end of this explanation
JASS:
//Channel Finished Event
function ExampleEvent takes nothing returns boolean
    return false
endfunction

//Start Channel
function ExampleStart takes nothing returns boolean
    local unit u
  
    if (GetSpellAbilityId() == Example_Ability) then
        set u = GetTriggerUnit()
        call BUS_StartChannel(BUS_GetChannelByName("My Channel Type"), u, TargetUnit, TargetX., TargetY., ChannelDuration, 5.00,  HaveHueChange)
        set u = null
    endif

    return false
endfunction

//Init Trig
function InitTrig_Example_1 takes nothing returns nothing
    local trigger t = CreateTrigger()
    local integer index = 0
  
    loop
        call TriggerRegisterPlayerUnitEvent(t, Player(index),EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
        set index = index + 1
        exitwhen index >= bj_MAX_PLAYER_SLOTS
    endloop
  
    call TriggerAddCondition(t, Condition(function ExampleStart))
  
    set t = CreateTrigger()
    call TriggerRegisterVariableEvent(t, "udg_BUSC_Event", EQUAL, 5.00)
    call TriggerAddCondition(t, Condition(function ExampleEvent))
  
endfunction
  
endfunction
  • Example 2
    • Events
      • Unit - A unit Begins channeling an ability
    • Conditions
      • (Ability being cast) Equal to Channel
    • Actions
      • Custom script: set udg_BUSC_Cancel[BUS_CheckChannel(Caster)] = true
      • Custom script: call BUS_StartChannel(BUS_GetChannelByName("My Channel Type"), Caster, TargetUnit, TargetX., TargetY., ChannelDuration, 6.00, HaveHueChange)
  • Example 3
    • Events
      • Game - BUSC_Event becomes Equal to 6.00
    • Conditions
    • Actions
      • -------- Do whatever you wish here --------
Boss Knockback

What is this? - The boss knockback system is used for the boss abilities that need it, rather it is not designed to be used by other abilities (and thus does not contain extra functionality) although it can be. The knockback is optionally 3D, works on flying units (in 3D) has different decays for units in the air and ground units (ground units that are presently in the air will be treated like air units) and destroys trees for ground units (when they're on the ground) but can optionally instead attempt to simply prevent them from being pushed into trees rather than kill trees they are pushed into.

JASS:
////////////////////////////////////////////////////////////////////
//             Boss Ultimate Spellpack Knockback V1.01            //
//  Author: Tank-Commander                                        //
//  Purpose: Handles physics simulation within the spellpack      //
//  Used for: Scathe, Sheer Force                                 //
//                                                                //
//  Notes:                                                        //
//    -  Read the readme before you try modifying the config      //
//    -  Use the "Helpful files" to help you import the system    //
//                                                                //
//  Credits:                                                      //
//    -  (Dummy.mdl) Vexorian                                     //
//                                                                //
//  If you have used this spellpack 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       //
//----------------------------------------------------------------//
//  CONFIGURABLE INFORMATION/HELP:                                //
//                                                                //
//  - Viewing data values: To see data values in the editor you   //
//  need to press Ctrl + D, to shift back to normal viewing       //
//  press it again                                                //
//                                                                //
//  - Effects: Pathnames for effects used in the spells should    //
//  have two "\"s throughout or the effect will not work (the     //
//  WE progress bar will not go away when saving, however if      //
//  fixed afterwards the save will still work, but the progress   //
//  bar will still remain until the WE is closed)                 //
//  e.g. "units\\human\\Footman\\Footman"                         //
//                                                                //
//  - Removing Effects: to remove an effect you don't want from   //
//  the ability, set the model path to that of Dummy.mdl          //
//                                                                //
//  - Timer: Some configurables have PerSecond values, the code   //
//  automatically accounts for changes to the timer as to         //
//  maintain consistency with what the user has chosen            //
//  All times in the system are expressions of seconds            //
//                                                                //
//----------------------------------------------------------------//
//  TimerSpeed: This is the amount of time in seconds between     //
//  each iteration of the Knockback Loop function                 //
constant function BUSKR_TimerSpeed takes nothing returns real
    return 0.031250000
endfunction
//----------------------------------------------------------------//
//  TreeCheckerId: This is the raw data Id of the unit used for   //
//  locating and potentially killing trees, it is advised this    //
//  unit uses dummy.mdl for its model for optimal usage and must  //
//  be able to harvest trees                                      //
constant function BUSKR_TreeCheckerId takes nothing returns integer
    return 'u001'
endfunction
//----------------------------------------------------------------//
//  TreeCheckerId: This is the order Id of the ability used to    //
//  stun units that have been knocked back, by default this is    //
//  the order ID for storm bolt (use the one in the object editor //
//  which has been placed onto the TreeChecker dummy unit there)  //
constant function BUSKR_StunOrderId takes nothing returns integer
    return 852095
endfunction
//----------------------------------------------------------------//
//  AttachmentPoint: This is the point on affected units that the //
//  effects will be placed on while they are being knocked back   //
constant function BUSKR_AttachmentPoint takes nothing returns string
    return "origin"
endfunction
//----------------------------------------------------------------//
//  HeightResetRate: This is the speed at which units return to   //
//  their original heights (both flying units and ground units)   //
constant function BUSKR_HeightResetRate takes nothing returns real
    return 18.5
endfunction
//----------------------------------------------------------------//
//  ForceAirDecay: This is the ratio at which momentum decays     //
//  while a unit is in the Air (Current Force / ForceDecay )      //
constant function BUSKR_ForceAirDecay takes nothing returns real
    return 1.10
endfunction
//----------------------------------------------------------------//
//  ForceGroundDecay: This is the ratio at which momentum decays  //
//  while a unit is on the ground (Current Force / Force Decay)   //
constant function BUSKR_ForceGroundDecay takes nothing returns real
    return 2.
endfunction
//----------------------------------------------------------------//
//  DragLet: This is a "let" value for when units will be reset   //
//  (if the units current total knockback is less than this value //
//  it will be treated as its knockback value is 0, this also     //
//  applies to fly heights and should not be 0 or less)           //
constant function BUSKR_DragLet takes nothing returns real
    return 2.
endfunction
//----------------------------------------------------------------//
//  Use3DKnockback: This only applies to ground units, the value  //
//  determines whether or not ground units which are hit by a     //
//  force will be knocked into the air, this includes "bouncing"  //
//  off the ground when hit by a force coming from above (this    //
//  only occurs if the unit is currently on the ground)           //
constant function BUSKR_Use3DKnockback takes nothing returns boolean
    return true
endfunction
//----------------------------------------------------------------//
//  StunGround: This determines whether or not ground units hit   //
//  into the air will be stunned for that time (so they do not    //
//  alter the arc)                                                //
constant function BUSKR_StunGround takes nothing returns boolean
    return true
endfunction
//----------------------------------------------------------------//
//  KnockbackEffect: This is the path of the effect that will be  //
//  attached to units that are currently being knocked back       //
constant function BUSKR_KnockbackEffect takes nothing returns string
    return "Abilities\\Spells\\Other\\Tornado\\Tornado_Target.mdl"
endfunction
//----------------------------------------------------------------//
//  GroundedEffect: This is the path of the effect that will be   //
//  created when a flying unit is knocked into the ground (fly    //
//  height of 0)                                                  //
constant function BUSKR_GroundedEffect takes nothing returns string
    return "Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl"
endfunction
//----------------------------------------------------------------//
//  InstantKillGround: This determines whether or not grounded    //
//  flyers will be instantly killed upon impact with the ground   //
constant function BUSKR_InstantKillGround takes nothing returns boolean
    return true
endfunction
//----------------------------------------------------------------//
//  KillTrees; This determines whether or not trees will be       //
//  killed when a unit (on the ground) is pushed through them, if //
//  set to false the system will attempt to prevent the unit from //
//  moving into trees (but cannot be garunteed as 100% effective  //
//  due to knockback values potentially "jumping" the unit into   //
//  a clearing in the trees - TreeKillRadius can supplement this  //
//  issue                                                         //
constant function BUSKR_KillTrees takes nothing returns boolean
    return false
endfunction
//----------------------------------------------------------------//
//  TreeKillRadius: This determines the size of the area checked  //
//  when looking for trees to kill, but also functions as the     //
//  "let" radius for preventing units from being pushed through   //
//  trees, only applies to grounded units                         //
constant function BUSKR_TreeKillRadius takes nothing returns real
    return 250.00
endfunction
//----------------------------------------------------------------//
//  PreventUnpathable: This determines whether or not the system  //
//  will prevent units being knocked back into positions that     //
//  units cannot move in (it will assume ground units are not     //
//  able to move in deep water)                                   //
constant function BUSKR_PreventUnpathable takes nothing returns boolean
    return false
endfunction
//----------------------------------------------------------------//
//                      END OF CONFIGURATION                      //
//----------------------------------------------------------------//
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
//  TreeDestroy: Function used to count trees and kill them if    //
//  the configuration has been set up to do so                    //
////////////////////////////////////////////////////////////////////
function BUS_TreeDestroy takes nothing returns nothing
    local destructable Tree = GetEnumDestructable()
  
    set udg_BUSK_TreeCounter = udg_BUSK_TreeCounter + 1
  
    call IssueTargetOrderById(udg_BUS_TreeChecker, 852018, Tree)
  
    if (GetUnitCurrentOrder(udg_BUS_TreeChecker) == 852018) and (BUSKR_KillTrees()) then
        call KillDestructable(Tree)
    endif

    call IssueImmediateOrderById(udg_BUS_TreeChecker, 851972)
  
    set Tree = null
endfunction

////////////////////////////////////////////////////////////////////
//  GetTrees: Part of the system used to locate trees and pass    //
//  them on to the TreeDestroy function                           //
////////////////////////////////////////////////////////////////////
function BUS_GetTrees takes real x, real y returns nothing
    //Move Rect and find trees
    call MoveRectTo(udg_BUS_Rect, x, y)
    set udg_BUSK_TreeCounter = 0
    call MoveLocation(udg_BUS_Loc, x, y)
    set bj_enumDestructableCenter = udg_BUS_Loc
    set bj_enumDestructableRadius = BUSKR_TreeKillRadius()
    call EnumDestructablesInRect(udg_BUS_Rect, filterEnumDestructablesInCircleBJ, function BUS_TreeDestroy)
endfunction

////////////////////////////////////////////////////////////////////
//  KnockbackLoop: function used to handle moving and changing    //
//  the heights knocked back units as well as slowing down their  //
//  speeds to 0                                                   //
////////////////////////////////////////////////////////////////////
function BUS_KnockbackLoop takes nothing returns nothing
    local integer Node = 0
    local real x
    local real x2
    local real y
    local real y2
    local real TempReal

    loop
        set Node = udg_BUSK_NextNode[Node]
        exitwhen Node == 0
      
        set x2 = GetUnitX(udg_BUSK_Unit[Node])
        set y2 = GetUnitY(udg_BUSK_Unit[Node])
        set x = x2 + udg_BUSK_KnockbackX[Node]
        set y = y2 + udg_BUSK_KnockbackY[Node]
        set TempReal = BUS_GetZ(x, y)
      
        if ((udg_BUS_MapMinX <= x) and (x <= udg_BUS_MapMaxX) and (udg_BUS_MapMinY <= y)and (y <= udg_BUS_MapMaxY)) then
          
            if not((BUSKR_PreventUnpathable()) and not(IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY))) or not(udg_BUSK_UnitType[Node]) then
          
                if (udg_BUSK_UnitType[Node] and udg_BUSK_CurrentHeight[Node] - TempReal <= 0) then
                    call BUS_GetTrees(x, y)
                  
                    if (udg_BUSK_TreeCounter == 0) or (BUSKR_KillTrees()) then
                        call SetUnitX(udg_BUSK_Unit[Node], x)
                        call SetUnitY(udg_BUSK_Unit[Node], y)
                    endif
                else
                    call SetUnitX(udg_BUSK_Unit[Node], x)
                    call SetUnitY(udg_BUSK_Unit[Node], y)
                endif
              
            endif
          
        endif
      
        if BUSKR_StunGround() then
            call IssueTargetOrderById(udg_BUS_Stunner, BUSKR_StunOrderId(), udg_BUSK_Unit[Node])
        endif
      
        set udg_BUSK_CurrentHeight[Node] = udg_BUSK_CurrentHeight[Node] + udg_BUSK_KnockbackZ[Node] + (BUS_GetZ(x2, y2) - TempReal)
      
        if ((udg_BUSK_CurrentHeight[Node] - TempReal) > 0) then
            set udg_BUSK_KnockbackX[Node] = udg_BUSK_KnockbackX[Node] / BUSKR_ForceAirDecay()
            set udg_BUSK_KnockbackY[Node] = udg_BUSK_KnockbackY[Node] / BUSKR_ForceAirDecay()
            set udg_BUSK_KnockbackZ[Node] = udg_BUSK_KnockbackZ[Node] / BUSKR_ForceAirDecay()
        else
            set udg_BUSK_KnockbackX[Node] = udg_BUSK_KnockbackX[Node] / BUSKR_ForceGroundDecay()
            set udg_BUSK_KnockbackY[Node] = udg_BUSK_KnockbackY[Node] / BUSKR_ForceGroundDecay()
            set udg_BUSK_KnockbackZ[Node] = 0
          
            if not(udg_BUSK_UnitType[Node]) then
                call DestroyEffect(AddSpecialEffectTarget(BUSKR_GroundedEffect(), udg_BUSK_Unit[Node], BUSKR_AttachmentPoint()))
              
                if (BUSKR_InstantKillGround()) then
                    call KillUnit(udg_BUSK_Unit[Node])
                    call DestroyEffect(udg_BUSK_CurrentEffect[Node])
                      
                    set udg_BUSK_RecycleNodes[udg_BUSK_RecyclableNodes] = Node
                    set udg_BUSK_RecyclableNodes = udg_BUSK_RecyclableNodes + 1
                    set udg_BUSK_NextNode[udg_BUSK_PrevNode[Node]] = udg_BUSK_NextNode[Node]
                    set udg_BUSK_PrevNode[udg_BUSK_NextNode[Node]] = udg_BUSK_PrevNode[Node]
                  
                    if (udg_BUSK_PrevNode[0] == 0) then
                        call PauseTimer(udg_BUSK_Timer)
                    endif
                  
                endif
              
            endif
          
        endif
      
        call SetUnitFlyHeight(udg_BUSK_Unit[Node], udg_BUSK_CurrentHeight[Node] - TempReal, 0.00)
      
        if (udg_BUSK_UnitType[Node]) then
          
            if(udg_BUSK_KnockbackX[Node] + udg_BUSK_KnockbackY[Node] + udg_BUSK_KnockbackZ[Node] < BUSKR_DragLet()) and (udg_BUSK_CurrentHeight[Node] - TempReal <= 0) then
                call SetUnitFlyHeight(udg_BUSK_Unit[Node], 0, 0.00)
                call DestroyEffect(udg_BUSK_CurrentEffect[Node])
              
                set udg_BUSK_RecycleNodes[udg_BUSK_RecyclableNodes] = Node
                set udg_BUSK_RecyclableNodes = udg_BUSK_RecyclableNodes + 1
                set udg_BUSK_NextNode[udg_BUSK_PrevNode[Node]] = udg_BUSK_NextNode[Node]
                set udg_BUSK_PrevNode[udg_BUSK_NextNode[Node]] = udg_BUSK_PrevNode[Node]

                if (udg_BUSK_PrevNode[0] == 0) then
                    call PauseTimer(udg_BUSK_Timer)
                endif
              
            endif
          
            set udg_BUSK_CurrentHeight[Node] = udg_BUSK_CurrentHeight[Node] - BUSKR_HeightResetRate()
        else
      
            set TempReal = udg_BUSK_ReturnHeight[Node] - (udg_BUSK_CurrentHeight[Node] - TempReal)
          
            if (TempReal * TempReal <= BUSKR_HeightResetRate() * BUSKR_HeightResetRate()) and (udg_BUSK_KnockbackZ[Node]  < BUSKR_DragLet()) then
                call SetUnitFlyHeight(udg_BUSK_Unit[Node], udg_BUSK_ReturnHeight[Node], 0.00)
                call DestroyEffect(udg_BUSK_CurrentEffect[Node])
              
                if (udg_BUSK_KnockbackX[Node] + udg_BUSK_KnockbackY[Node] + udg_BUSK_KnockbackZ[Node] < BUSKR_DragLet()) then
                      
                    set udg_BUSK_RecycleNodes[udg_BUSK_RecyclableNodes] = Node
                    set udg_BUSK_RecyclableNodes = udg_BUSK_RecyclableNodes + 1
                    set udg_BUSK_NextNode[udg_BUSK_PrevNode[Node]] = udg_BUSK_NextNode[Node]
                    set udg_BUSK_PrevNode[udg_BUSK_NextNode[Node]] = udg_BUSK_PrevNode[Node]
                  
                    if (udg_BUSK_PrevNode[0] == 0) then
                        call PauseTimer(udg_BUSK_Timer)
                    endif
              
                endif
              
            elseif (udg_BUSK_ReturnHeight[Node] > (udg_BUSK_CurrentHeight[Node] - TempReal)) then
                set udg_BUSK_CurrentHeight[Node] = udg_BUSK_CurrentHeight[Node] + BUSKR_HeightResetRate()
            else
                set udg_BUSK_CurrentHeight[Node] = udg_BUSK_CurrentHeight[Node] - BUSKR_HeightResetRate()
            endif
          
        endif
    endloop
  
endfunction

////////////////////////////////////////////////////////////////////
//  StartKnockback: Function used to start a unit's knockback     //
//  and adds it into the loop to be knocked back, it also         //
//  prevents a unit having two iterations within the loop         //
////////////////////////////////////////////////////////////////////
function BUS_StartKnockback takes unit u, real Force, real x, real x2, real y, real y2, real z, real z2 returns nothing
    local boolean TempBoolean = true
    local integer Node = 0
    local real dx
    local real dy
    local real Angle
    local real Angle2
  
    loop
        set Node = udg_BUSK_NextNode[Node]

        if (udg_BUSK_Unit[Node] == u) then
            set TempBoolean = false
        endif

        exitwhen (Node == 0) or (udg_BUSK_Unit[Node] == u)
    endloop
  
    if (TempBoolean) then
  
        if (udg_BUSK_RecyclableNodes == 0) then
            set udg_BUSK_NodeNumber = udg_BUSK_NodeNumber + 1
            set Node = udg_BUSK_NodeNumber
        else
            set udg_BUSK_RecyclableNodes = udg_BUSK_RecyclableNodes - 1
            set Node = udg_BUSK_RecycleNodes[udg_BUSK_RecyclableNodes]
        endif
      
        set udg_BUSK_NextNode[Node] = 0
        set udg_BUSK_NextNode[udg_BUSK_PrevNode[0]] = Node
        set udg_BUSK_PrevNode[Node] = udg_BUSK_PrevNode[0]
        set udg_BUSK_PrevNode[0] = Node
      
        set udg_BUSK_Unit[Node] = u
        set udg_BUSK_ReturnHeight[Node] = GetUnitFlyHeight(u)
        set udg_BUSK_CurrentHeight[Node] = udg_BUSK_ReturnHeight[Node] + BUS_GetZ(x, y)
        set udg_BUSK_CurrentEffect[Node] = AddSpecialEffectTarget(BUSKR_KnockbackEffect(), u, BUSKR_AttachmentPoint())
        set udg_BUSK_UnitType[Node] = IsUnitType(u, UNIT_TYPE_GROUND)
        set udg_BUSK_KnockbackX[Node] = 0.
        set udg_BUSK_KnockbackY[Node] = 0.
        set udg_BUSK_KnockbackZ[Node] = 0.
      
        if UnitAddAbility(udg_BUSK_Unit[Node], 'Amrf') and UnitRemoveAbility(udg_BUSK_Unit[Node], 'Amrf') then
        endif
      
        if (udg_BUSK_PrevNode[Node] == 0) then
             call TimerStart(udg_BUSK_Timer, BUSKR_TimerSpeed(), true, function BUS_KnockbackLoop)
        endif
      
    endif
  
    set dy = y - y2
    set dx = x - x2
  
    if (udg_BUSK_UnitType[Node]) and (udg_BUSK_CurrentHeight[Node] - BUS_GetZ(x, y) <= BUSKR_DragLet()) then
        set Angle2 = Atan2((z2 - z), SquareRoot((dy * dy) + (dx * dx)))
      
        if(BUSKR_Use3DKnockback()) then
          
            if ((z2 - z) < 0) then
                set Angle2 = Atan2((z - z2), SquareRoot((dy * dy) + (dx * dx)))
            endif
          
            set udg_BUSK_KnockbackZ[Node] = udg_BUSK_KnockbackZ[Node] + Sin(Angle2) * Force
          
        endif
      
    else
        set Angle2 = Atan2((z - z2), SquareRoot((dy * dy) + (dx * dx)))
        set udg_BUSK_KnockbackZ[Node] = udg_BUSK_KnockbackZ[Node] + Sin(Angle2) * Force
    endif

    set Angle = Atan2(dy, dx)
    set udg_BUSK_KnockbackX[Node] = udg_BUSK_KnockbackX[Node] + Cos(Angle) * Cos(Angle2) * Force
    set udg_BUSK_KnockbackY[Node] = udg_BUSK_KnockbackY[Node] + Sin(Angle) * Cos(Angle2) * Force
endfunction

////////////////////////////////////////////////////////////////////
//  InitTrig BUS Knockback: function used to set up the timer     //
//  used by the system and the tree checking unit                 //
////////////////////////////////////////////////////////////////////
function InitTrig_BUS_Knockback takes nothing returns nothing
    set udg_BUSK_Timer = CreateTimer()
    set udg_BUS_TreeChecker = CreateUnit(BUSCR_DummyPlayer(), BUSKR_TreeCheckerId(), 0., 0. , 0.)
    set udg_BUS_Stunner = CreateUnit(BUSCR_DummyPlayer(), BUSKR_TreeCheckerId(), 0., 0., 0.)
    set udg_BUS_Rect = Rect(0, 0, BUSKR_TreeKillRadius(), BUSKR_TreeKillRadius())
endfunction

////////////////////////////////////////////////////////////////////
//  End of the system                                             //
////////////////////////////////////////////////////////////////////

JASS:
The knockback system used in the spellpack is fairly simplistic and easy to use though is not a main feature

Attributes of the system:
 - Ground units can be "bounced" off the ground if a force pushes them down
 - Flying units can be knocked back in 3D and will return to their original heights
 - Flying units can be killed instantly if they are pushed into the floor
 - Ground units (on the ground) can optionally kill or attempt to prevent going through, trees
 - Ground knockback decay different from air knockback decay (flying ground units use the air knockback decay)
 
Using the system:

call BUS_StartKnockback(Target, Force, TargetX, OriginX, TargetY, OriginY, TargetZ, OriginZ)
 - This is the only function required the run the knockback from a user perspective
 - Target = Unit to knock back
 - Force = The force to push the unit
 - TargetX = The x co-ordinate of the Target
 - OriginX = The x co-ordinate of the origin point of the knockback (used for angle calculation)
 - TargetY = The y co-ordinate of the Target
 - OriginY = The y co-ordinate of the origin point of the knockback (used for angle calculation)
 - TargetZ = The z co-ordinate of the Target
 - OriginZ = The z co-ordinate of the origin point of the knockback (used for angle calculation)
 
When calculating Target and Origin Z values, you should use the BUS_GetZ(x,y) function if you want greater accuracy
though this is optional, to use this effectively  use the following formula

 set TargetZ = GetUnitFlyHeight(Target) + BUS_GetZ(TargetX,TargetY) + OffsetMod
 set OriginZ = GetUnitFlyHeight(Origin) + BUS_GetZ(OriginX,OriginY) + OffsetMod
 
 - If there is no Origin Unit then simply leave out getting its fly height to achieve the same effect
 - Offset modifiers can be used to achieve certain effects (assuming they are different from eachother)
   such as causing the knockback angle to assume the Target is above the origin point or other similar scenarios
Boss Soul Release

What is this? - Soul Release is an instant kill attack for your bosses - after targetting a unit the boss will begin to channel, once completed it checks if the unit is still within range, if yes then the unit is killed instantly (as well as doing a small AOE of instant death) though it is not a "true" death so gold rewards and the "kill" are not recorded

JASS:
////////////////////////////////////////////////////////////////////
//          Boss Ultimate Spellpack Soul Release V1.00            //
//  Author: Tank-Commander                                        //
//  Purpose: Boss Ultimate Instant Kill                           //
//  Requires: Dummy.mdl, BUS Channel                              //
//                                                                //
//  Notes:                                                        //
//    -  Read the readme before you try modifying the config      //
//    -  Use the "Helpful files" to help you import the system    //
//                                                                //
//  Credits:                                                      //
//    -  (Dummy.mdl) Vexorian                                     //
//    -  (LightningWrath.mdl) Callahan                            //
//                                                                //
//  Optimum User: Any (Large)                                     //
//                                                                //
//  If you have used this spellpack 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       //
//----------------------------------------------------------------//
//  CONFIGURABLE INFORMATION/HELP:                                //
//                                                                //
//  - Viewing data values: To see data values in the editor you   //
//  need to press Ctrl + D, to shift back to normal viewing       //
//  press it again                                                //
//                                                                //
//  - Effects: Pathnames for effects used in the spells should    //
//  have two "\"s throughout or the effect will not work (the     //
//  WE progress bar will not go away when saving, however if      //
//  fixed afterwards the save will still work, but the progress   //
//  bar will still remain until the WE is closed)                 //
//  e.g. "units\\human\\Footman\\Footman"                         //
//                                                                //
//  - Effect Scaling: Some effects have scale values below them   //
//  the scale determines the size of the effect and is expressed  //
//  as a real percentage (1.00 = 100%)                            //
//                                                                //
//  - Removing Effects: to remove an effect you don't want from   //
//  the ability, set the model path to that of Dummy.mdl          //
//                                                                //
//  - Base and Per Values: Most configurables have a base and per //
//  value, Base values are what a value is set to regardless of   //
//  other factors. Per values are what a value is set to based on //
//  what the per value is the formula for calculating the result  //
//  is as follows:                                                //
//    - BaseValue + (Factor * PerValue)                           //
//                                                                //
//  - Timer: Some configurables have PerSecond values, the code   //
//  automatically accounts for changes to the timer as to         //
//  maintain consistency with what the user has chosen            //
//  All times in the system are expressions of seconds            //
//                                                                //
//  - AttackTypes: This should match the Damage Type of the       //
//  ability, though it can be something else if you wish          //
//                                                                //
//  - DamageTypes: This changes the damage multiplyer vs. enemy   //
//  armour types, note that by default the damage filters         //
//  exclude magic immune units so changing this from MAGIC        //
//  damage will not make them take damage                         //
//                                                                //
//  - WeaponTypes: Generally don't need to be used, should only   //
//  not be null if you particularly wish or need to use them      //
//                                                                //
//----------------------------------------------------------------//
//  TimerSpeed: This is the amount of time in seconds between     //
//  each iteration of the Soul Release Loop function              //
constant function BUSRR_TimerSpeed takes nothing returns real
    return 0.031250000
endfunction
//----------------------------------------------------------------//
//  Ability: This is the AbilityId that is used as the dummy      //
//  ability used for the soul release spell                       //
constant function BUSRR_Ability takes nothing returns integer
    return 'A000'
endfunction
//----------------------------------------------------------------//
//  InvulAbility: This is the AbilityId that is used to make the  //
//  spirits immune, this should be the ability "invulnerability"  //
//  (which one it is doesn't matter)                              //
constant function BUSRR_InvulAbility takes nothing returns integer
    return 'Avul'
endfunction
//----------------------------------------------------------------//
//  DummyId: This is the UnitId that is used to create effects    //
//  It is recommended this unit use Dummy.mdl for its model for   //
//  optimal usage                                                 //
constant function BUSRR_DummyId takes nothing returns integer
    return 'u000'
endfunction
//----------------------------------------------------------------//
//  Event: This is the value of BUS_Event that it will be set to  //
//  when the channeling ends, this should be unique to each       //
//  ability which uses the channel system                         //
constant function BUSRR_Event takes nothing returns integer
    return 1
endfunction
//----------------------------------------------------------------//
//  AttachmentPoint: This is the point on units that effects will //
//  be attached to                                                //
constant function BUSRR_AttachmentPoint takes nothing returns string
    return "origin"
endfunction
//----------------------------------------------------------------//
//  HaveHue: This determines whether or not a unit will have its  //
//  hue altered during the channeling stage                       //
constant function BUSRR_HaveHue takes nothing returns boolean
    return true
endfunction
//----------------------------------------------------------------//
//  ChannelType: This determines the type of channel that is used //
//  This should match the name of channel type used               //
constant function BUSRR_ChannelType takes nothing returns string
    return "Holy Storm"
endfunction
//----------------------------------------------------------------//
//  ChannelDurationBase: This determines how long the ability     //
//  will use the channel system before starting its next stage    //
constant function BUSRR_ChannelDurationBase takes nothing returns real
    return 6.
endfunction
//----------------------------------------------------------------//
//  ChannelDurationPerLevel: The per level counterpart of         //
//  ChannelDurationBase                                           //
constant function BUSRR_ChannelDurationPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  HitDistanceBase: This is the range used to check if units     //
//  are valid targets for the ability once the channeling is      //
//  completed                                                     //
constant function BUSRR_HitDistanceBase takes nothing returns real
    return 700.
endfunction
//----------------------------------------------------------------//
//  HitDistancePerLevel: The per level counterpart of             //
//  HitDistanceBase                                               //
constant function BUSRR_HitDistancePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  MainAOEBase: This is the AOE of the central bolt of the       //
//  ability which will turn units into spirits                    //
constant function BUSRR_MainAOEBase takes nothing returns real
    return 150.
endfunction
//----------------------------------------------------------------//
//  MainAoePerLevel: This is the per level counterpart of         //
//  MainAOEBase                                                   //
constant function BUSRR_MainAOEPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  MiniAOEBase: This is the AOE of the smaller bolts of the      //
//  ability which appear around the central bolt                  //
constant function BUSRR_MiniAOEBase takes nothing returns real
    return 75.
endfunction
//----------------------------------------------------------------//
//  MiniAOEPerLevel: This is the per level counterpart of         //
//  MiniAOEBase                                                   //
constant function BUSRR_MiniAOEPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  MiniCountBase: This is the amount of smaller bolts that       //
//  appear around the central bolt                                //
constant function BUSRR_MiniCountBase takes nothing returns integer
    return 8
endfunction
//----------------------------------------------------------------//
//  MiniCountPerLevel: This is the per level counterpart of       //
//  MiniCountBase                                                 //
constant function BUSRR_MiniCountPerLevel takes nothing returns integer
    return 0
endfunction
//----------------------------------------------------------------//
//  MainScaleBase: This is the scaling size of the main bolt      //
constant function BUSRR_MainScaleBase takes nothing returns real
    return 3.
endfunction
//----------------------------------------------------------------//
//  MainScalePerLevel: This is the per level counterpart of       //
//  MainScaleBase                                                 //
constant function BUSRR_MainScalePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  MiniScaleBase: This is the scaling size of the smaller bolts  //
//  that appear around the central bolt                           //
constant function BUSRR_MiniScaleBase takes nothing returns real
    return 0.5
endfunction
//----------------------------------------------------------------//
//  MiniScalePerLevel: This is the per level counterpart of       //
//  MiniScaleBase                                                 //
constant function BUSRR_MiniScalePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  MainEffect: This is the pathname of the model used for the    //
//  central bolt                                                  //
constant function BUSRR_MainEffect takes nothing returns string
    return "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl"
endfunction
//----------------------------------------------------------------//
//  MiniEffect: This is the pathname of the model used for the    //
//  smaller bolts                                                 //
constant function BUSRR_MiniEffect takes nothing returns string
    return "war3mapImported\\LightningWrath.mdx"
endfunction
//----------------------------------------------------------------//
//  FailEffect: This is the pathname of the model used when the   //
//  Ability fails (target moved too far away)                     //
constant function BUSRR_FailEffect takes nothing returns string
    return "Abilities\\Spells\\Human\\Invisibility\\InvisibilityTarget.mdl"
endfunction
//----------------------------------------------------------------//
//  SpiritRed: This is the red hue of spirits created by the      //
//  ability                                                       //
constant function BUSRR_SpiritRed takes nothing returns integer
    return 255
endfunction
//----------------------------------------------------------------//
//  SpiritGreen: This is the green hue of spirits created by the  //
//  ability                                                       //
constant function BUSRR_SpiritGreen takes nothing returns integer
    return 255
endfunction
//----------------------------------------------------------------//
//  SpiritBlue: This is the blue hue of spirits created by the    //
//  ability                                                       //
constant function BUSRR_SpiritBlue takes nothing returns integer
    return 125
endfunction
//----------------------------------------------------------------//
//  SpiritAlpha: This is the alpha of spirits created by the      //
//  ability                                                       //
constant function BUSRR_SpiritAlpha takes nothing returns integer
    return 125
endfunction
//----------------------------------------------------------------//
//  AscentionBase: This is the speed at which spirits travel      //
//  upwards                                                       //
constant function BUSRR_AscentionBase takes nothing returns real
    return 5.
endfunction
//----------------------------------------------------------------//
//  AscentionPerLevel: This is the per level counterpart of       //
//  AscentionBase                                                 //
constant function BUSRR_AscentionPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  AscentionStart: This is the starting height of spirits when   //
//  they are created                                              //
constant function BUSRR_AscentionStart takes nothing returns real
    return 200.
endfunction
//----------------------------------------------------------------//
//  AscentionTimeBase: This is how long a spirit ascends          //
constant function BUSRR_AscentionTimeBase takes nothing returns real
    return 1.
endfunction
//----------------------------------------------------------------//
//  AscentionTimePerLevel: This is the per level counterpart of   //
//  AscentionTimeBase                                             //
constant function BUSRR_AscentionTimePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  EffectTime: This is how long after the effects are completed  //
//  that they are completely removed from the lists               //
constant function BUSRR_EffectTime takes nothing returns real
    return 4.0
endfunction
//----------------------------------------------------------------//
//  TargetFilter: This is the target filter for units that can    //
//  be turned into spirits, configure this as desired (requires   //
//  some coding knowledge, u is the target unit, pl is the player //
//  who owns the caster)                                          //
function BUS_RTargetFilter takes unit u, player pl returns boolean
    return (not(IsUnitType(u, UNIT_TYPE_DEAD)) and not(IsUnitType(u, UNIT_TYPE_STRUCTURE)) and (IsPlayerEnemy(GetOwningPlayer(u), pl)))
endfunction
//----------------------------------------------------------------//
//                      END OF CONFIGURATION                      //
//----------------------------------------------------------------//
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
//  SoulReleaseLoop: Function used to move and handle spirits and //
//  effects that are no longer in use                             //
////////////////////////////////////////////////////////////////////
function BUS_SoulReleaseLoop takes nothing returns nothing
    local integer Node = 0
  
    loop
        set Node = udg_BUSR_PNextNode[Node]
        exitwhen Node == 0
      
        if (udg_BUSR_PCurrentAlpha[Node] <= 0) then
            call RemoveUnit(udg_BUSR_PUnit[Node])
          
            set udg_BUSR_PRecycleNodes[udg_BUSR_PRecyclableNodes] = Node
            set udg_BUSR_PRecyclableNodes = udg_BUSR_PRecyclableNodes + 1
            set udg_BUSR_PNextNode[udg_BUSR_PPrevNode[Node]] = udg_BUSR_PNextNode[Node]
            set udg_BUSR_PPrevNode[udg_BUSR_PNextNode[Node]] = udg_BUSR_PPrevNode[Node]
          
            if (udg_BUSR_NextNode[0] + udg_BUSR_PNextNode[0] == 0) then
                call PauseTimer(udg_BUSR_Timer)
            endif
        else
            set udg_BUSR_PCurrentAlpha[Node] = udg_BUSR_PCurrentAlpha[Node] - udg_BUSR_PAlphaReduction[Node]
            call SetUnitVertexColor(udg_BUSR_PUnit[Node], BUSRR_SpiritRed(), BUSRR_SpiritGreen(), BUSRR_SpiritBlue(), udg_BUSR_PCurrentAlpha[Node])
            call SetUnitFlyHeight(udg_BUSR_PUnit[Node], GetUnitFlyHeight(udg_BUSR_PUnit[Node]) + udg_BUSR_PSpeed[Node], 0.)
        endif
      
    endloop
  
    set Node = 0
    loop
        set Node = udg_BUSR_NextNode[Node]
        exitwhen Node == 0
      
        if (udg_BUSR_Time[Node] < 0) then
            call RemoveUnit(udg_BUSR_Unit[Node])
          
            set udg_BUSR_RecycleNodes[udg_BUSR_RecyclableNodes] = Node
            set udg_BUSR_RecyclableNodes = udg_BUSR_RecyclableNodes + 1
            set udg_BUSR_NextNode[udg_BUSR_PrevNode[Node]] = udg_BUSR_NextNode[Node]
            set udg_BUSR_PrevNode[udg_BUSR_NextNode[Node]] = udg_BUSR_PrevNode[Node]
          
            if (udg_BUSR_NextNode[0] + udg_BUSR_PNextNode[0] == 0) then
                call PauseTimer(udg_BUSR_Timer)
            endif
        else
            set udg_BUSR_Time[Node] = udg_BUSR_Time[Node] - BUSRR_TimerSpeed()
        endif
    endloop
  
endfunction

////////////////////////////////////////////////////////////////////
//  SoulReleaseSpawn: The main function, used to kill units,      //
//  create spirits and effects and set up the loop                //
////////////////////////////////////////////////////////////////////
function BUS_SoulReleaseSpawn takes real x, real y, real AOE, real Scale, real Speed, real Time, player pl, string m returns nothing
    local integer Node
    local unit u
  
    if (udg_BUSR_RecyclableNodes == 0) then
        set udg_BUSR_NodeNumber = udg_BUSR_NodeNumber + 1
        set Node = udg_BUSR_NodeNumber
    else
        set udg_BUSR_RecyclableNodes = udg_BUSR_RecyclableNodes - 1
        set Node = udg_BUSR_RecycleNodes[udg_BUSR_RecyclableNodes]
    endif

    set udg_BUSR_NextNode[Node] = 0
    set udg_BUSR_NextNode[udg_BUSR_PrevNode[0]] = Node
    set udg_BUSR_PrevNode[Node] = udg_BUSR_PrevNode[0]
    set udg_BUSR_PrevNode[0] = Node
  
    if (udg_BUSR_PrevNode[Node] + udg_BUSR_PPrevNode[0] == 0) then
        call TimerStart(udg_BUSR_Timer, BUSRR_TimerSpeed(), true, function BUS_SoulReleaseLoop)
    endif
  
    set udg_BUSR_Unit[Node] = CreateUnit(BUSCR_DummyPlayer(), BUSRR_DummyId(), x, y, 0.)
    call SetUnitScale(udg_BUSR_Unit[Node], Scale, 0., 0.)
    call DestroyEffect(AddSpecialEffectTarget(m, udg_BUSR_Unit[Node], BUSRR_AttachmentPoint()))
    set udg_BUSR_Time[Node] = BUSRR_EffectTime()
  
    call GroupEnumUnitsInRange(udg_BUSR_UnitGroup, x, y, AOE, null)
  
    loop
        set u = FirstOfGroup(udg_BUSR_UnitGroup)
        exitwhen u == null
        call GroupRemoveUnit(udg_BUSR_UnitGroup, u)
      
        if (BUS_RTargetFilter(u, pl)) then          
            if (udg_BUSR_PRecyclableNodes == 0) then
                set udg_BUSR_PNodeNumber = udg_BUSR_PNodeNumber + 1
                set Node = udg_BUSR_PNodeNumber
            else
                set udg_BUSR_PRecyclableNodes = udg_BUSR_PRecyclableNodes - 1
                set Node = udg_BUSR_PRecycleNodes[udg_BUSR_PRecyclableNodes]
            endif

            set udg_BUSR_PNextNode[Node] = 0
            set udg_BUSR_PNextNode[udg_BUSR_PPrevNode[0]] = Node
            set udg_BUSR_PPrevNode[Node] = udg_BUSR_PPrevNode[0]
            set udg_BUSR_PPrevNode[0] = Node
          
            call KillUnit(u)
          
            set x = GetUnitX(u)
            set y = GetUnitY(u)
            set udg_BUSR_PUnit[Node] = CreateUnit(Player(15), GetUnitTypeId(u), x, y, GetUnitFacing(u))
            call SetUnitColor(udg_BUSR_PUnit[Node], GetPlayerColor(GetOwningPlayer(u)))
            call SetUnitVertexColor(udg_BUSR_PUnit[Node], BUSRR_SpiritRed(), BUSRR_SpiritGreen(), BUSRR_SpiritBlue(), BUSRR_SpiritAlpha())
            call SetUnitPathing(udg_BUSR_PUnit[Node], false)
            if UnitAddAbility(udg_BUSR_PUnit[Node], 'Amrf') and UnitRemoveAbility(udg_BUSR_PUnit[Node], 'Amrf') then
            endif
            call PauseUnit(udg_BUSR_PUnit[Node], true)
            call UnitAddAbility(udg_BUSR_PUnit[Node], BUSRR_InvulAbility())
            call SetUnitFlyHeight(udg_BUSR_PUnit[Node], BUSRR_AscentionStart(), 0.)
            set udg_BUSR_PSpeed[Node] = Speed
            set udg_BUSR_PCurrentAlpha[Node] = BUSRR_SpiritAlpha()
            set udg_BUSR_PAlphaReduction[Node] = R2I((I2R(BUSRR_SpiritAlpha()) / Time) * BUSRR_TimerSpeed())
        endif
      
    endloop
  
endfunction

////////////////////////////////////////////////////////////////////
//  SoulReleaseEvent: Function used to determine the points of    //
//  impact. Runs when the channeling is finished and handles      //
//  all calculations                                              //
////////////////////////////////////////////////////////////////////
function BUS_SoulReleaseEvent takes nothing returns boolean
    local real rLevel = GetUnitAbilityLevel(udg_BUS_Unit, BUSRR_Ability())
    local real x = GetUnitX(udg_BUS_Unit)
    local real y = GetUnitY(udg_BUS_Unit)
    local real x2 = GetUnitX(udg_BUS_Target)
    local real y2 = GetUnitY(udg_BUS_Target)
    local real TempReal
    local real TempReal2
    local real TempReal3
    local real TempReal4
    local real TempReal5
    local real TempReal6
    local real Angle
    local integer iLoop
    local integer TempInt
    local player pl
  
    if (SquareRoot((x2 - x) * (x2 - x) + (y2 - y) * (y2 - y)) <= BUSRR_HitDistanceBase() + (rLevel * BUSRR_HitDistancePerLevel())) then
        set pl = GetOwningPlayer(udg_BUS_Unit)
        set TempReal = BUSRR_MainAOEBase() + (rLevel * BUSRR_MainAOEPerLevel())
        set TempReal2 = BUSRR_MainScaleBase() + (rLevel * BUSRR_MainScalePerLevel())
        set TempReal3 = BUSRR_AscentionBase() + (rLevel * BUSRR_AscentionPerLevel())
        set TempReal4 = BUSRR_AscentionTimeBase() + (rLevel * BUSRR_AscentionTimePerLevel())
        call BUS_SoulReleaseSpawn(x2, y2, TempReal, TempReal2, TempReal3, TempReal4, pl, BUSRR_MainEffect())
        set TempReal2 = BUSRR_MiniAOEBase() + (rLevel * BUSRR_MiniAOEPerLevel())
        set TempReal6 = TempReal + TempReal2
        set TempReal = BUSRR_MiniScaleBase() + (rLevel *BUSRR_MiniScalePerLevel())
        set TempInt = BUSRR_MiniCountBase() + (R2I(rLevel) * BUSRR_MiniCountPerLevel())
        set TempReal5 = 360 / I2R(TempInt)
        set Angle = 0
        set iLoop = 0
      
        loop
            set iLoop = iLoop + 1
            exitwhen iLoop > TempInt
            set Angle = Angle + TempReal5
            set x = x2 + TempReal6 * Cos(Angle * bj_DEGTORAD)
            set y = y2 + TempReal6 * Sin(Angle * bj_DEGTORAD)
            call BUS_SoulReleaseSpawn(x, y, TempReal2, TempReal, TempReal3, TempReal4, pl, BUSRR_MiniEffect())
        endloop
  
        set pl = null
    else
        call DestroyEffect(AddSpecialEffectTarget(BUSRR_FailEffect(), udg_BUS_Unit, BUSRR_AttachmentPoint()))
    endif
  
    return false
endfunction

////////////////////////////////////////////////////////////////////
//  SoulReleaseStart: Function used to set up the channeling      //
//  for the ability                                               //
////////////////////////////////////////////////////////////////////
function BUS_SoulReleaseStart takes nothing returns boolean
    local unit u
  
    if (GetSpellAbilityId() == BUSRR_Ability()) then
        set u = GetTriggerUnit()
        call BUS_StartChannel(BUS_GetChannelByName(BUSRR_ChannelType()), u, GetSpellTargetUnit(), 0., 0., BUSRR_ChannelDurationBase() + (GetUnitAbilityLevel(u, BUSRR_Ability()) * BUSRR_ChannelDurationPerLevel()), BUSRR_Event(), BUSRR_HaveHue())
        set u = null
    endif

    return false
endfunction

////////////////////////////////////////////////////////////////////
//  InitTrig BUS Soul Release: Function used to set up the        //
//  event and starting functions of the ability as well as the    //
//  timer for the loop                                            //
////////////////////////////////////////////////////////////////////
function InitTrig_BUS_Soul_Release takes nothing returns nothing
    local trigger t = CreateTrigger()
    local integer index = 0
  
    loop
        call TriggerRegisterPlayerUnitEvent(t, Player(index),EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
        set index = index + 1
        exitwhen index >= bj_MAX_PLAYER_SLOTS
    endloop
  
    call TriggerAddCondition(t, Condition(function BUS_SoulReleaseStart))
  
    set t = CreateTrigger()
    call TriggerRegisterVariableEvent(t, "udg_BUSC_Event", EQUAL, BUSRR_Event())
    call TriggerAddCondition(t, Condition(function BUS_SoulReleaseEvent))
  
    set udg_BUSS_Timer = CreateTimer()
  
endfunction

////////////////////////////////////////////////////////////////////
//  End of the spell                                              //
////////////////////////////////////////////////////////////////////

The caster absorbs powerful magic lingering in the air and focuses it, fashioning it into an almighty strike. If the target does not flee in terror they and all enemies near them are struck down, extracting their souls leaving the lifeless corpses behind


GIF
172730-albums6263-picture98400.gif
Boss Scathe

What is this? - Scathe is a satisfying laser ability for bosses to blast away their enemies, after selecting a target the boss begins channeling, once completed the boss aims his laser at the target - after a brief delay (making the ability dodgeable) the laser fires and devestates all caught in the ray (3D Collision)

JASS:
////////////////////////////////////////////////////////////////////
//              Boss Ultimate Spellpack Scathe V1.00              //
//  Author: Tank-Commander                                        //
//  Purpose: Boss Strong Knockback/High Damage Laser              //
//  Requires: Dummy.mdl, BUS Channel, BUS Knockback               //
//                                                                //
//  Notes:                                                        //
//    -  Read the readme before you try modifying the config      //
//    -  Use the "Helpful files" to help you import the system    //
//                                                                //
//  Credits:                                                      //
//    -  (Dummy.mdl) Vexorian                                     //
//    -  (OrbitalRay.mdl) Callahan                                //
//                                                                //
//  Optimum User: Any                                             //
//                                                                //
//  If you have used this spellpack 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       //
//----------------------------------------------------------------//
//  CONFIGURABLE INFORMATION/HELP:                                //
//                                                                //
//  - Viewing data values: To see data values in the editor you   //
//  need to press Ctrl + D, to shift back to normal viewing       //
//  press it again                                                //
//                                                                //
//  - Effects: Pathnames for effects used in the spells should    //
//  have two "\"s throughout or the effect will not work (the     //
//  WE progress bar will not go away when saving, however if      //
//  fixed afterwards the save will still work, but the progress   //
//  bar will still remain until the WE is closed)                 //
//  e.g. "units\\human\\Footman\\Footman"                         //
//                                                                //
//  - Effect Scaling: Some effects have scale values below them   //
//  the scale determines the size of the effect and is expressed  //
//  as a real percentage (1.00 = 100%)                            //
//                                                                //
//  - Removing Effects: to remove an effect you don't want from   //
//  the ability, set the model path to that of Dummy.mdl          //
//                                                                //
//  - Base and Per Values: Most configurables have a base and per //
//  value, Base values are what a value is set to regardless of   //
//  other factors. Per values are what a value is set to based on //
//  what the per value is the formula for calculating the result  //
//  is as follows:                                                //
//    - BaseValue + (Factor * PerValue)                           //
//                                                                //
//  - Timer: Some configurables have PerSecond values, the code   //
//  automatically accounts for changes to the timer as to         //
//  maintain consistency with what the user has chosen            //
//  All times in the system are expressions of seconds            //
//                                                                //
//  - AttackTypes: This should match the Damage Type of the       //
//  ability, though it can be something else if you wish          //
//                                                                //
//  - DamageTypes: This changes the damage multiplyer vs. enemy   //
//  armour types, note that by default the damage filters         //
//  exclude magic immune units so changing this from MAGIC        //
//  damage will not make them take damage                         //
//                                                                //
//  - WeaponTypes: Generally don't need to be used, should only   //
//  not be null if you particularly wish or need to use them      //
//                                                                //
//----------------------------------------------------------------//
//  TimerSpeed: This is the amount of time in seconds between     //
//  each iteration of the Scathe Loop function                    //
constant function BUSSR_TimerSpeed takes nothing returns real
    return 0.031250000
endfunction
//----------------------------------------------------------------//
//  Ability: This is the AbilityId that is used as the dummy      //
//  ability used for the scathe spell                             //
constant function BUSSR_Ability takes nothing returns integer
    return 'A002'
endfunction
//----------------------------------------------------------------//
//  DummyId: This is the UnitId that is used to create effects    //
//  It is recommended this unit use Dummy.mdl for its model for   //
//  optimal usage                                                 //
constant function BUSSR_DummyId takes nothing returns integer
    return 'u000'
endfunction
//----------------------------------------------------------------//
//  Event: This is the value of BUS_Event that it will be set to  //
//  when the channeling ends, this should be unique to each       //
//  ability which uses the channel system                         //
constant function BUSSR_Event takes nothing returns integer
    return 2
endfunction
//----------------------------------------------------------------//
//  Order: This is the order ID of the ability used to make sure  //
//  that the unit is still channeling the ability                 //
constant function BUSSR_Order takes nothing returns integer
    return 852187
endfunction
//----------------------------------------------------------------//
//  AttachmentPoint: This is the point that effects are attached  //
//  to on units and dummy units                                   //
constant function BUSSR_AttachmentPoint takes nothing returns string
    return "origin"
endfunction
//----------------------------------------------------------------//
//  HaveHue; This determines whether or not during channeling     //
//  the hue of the caster changes throughout                      //
constant function BUSSR_HaveHue takes nothing returns boolean
    return true
endfunction
//----------------------------------------------------------------//
//  ChannelType: This is the name of the channel type used by     //
//  this ability (it should match the name of the channel type)   //
constant function BUSSR_ChannelType takes nothing returns string
    return "Arcane charge"
endfunction
//----------------------------------------------------------------//

//  HealthDamageBase: This is the damage dealt by the laser to    //
//  enemy units                                                   //
constant function BUSSR_HealthDamageBase takes nothing returns real
    return 900.0
endfunction
//----------------------------------------------------------------//
//  HealthDamagePerLevel: This is the per level counterpart of    //
//  HealthDamageBase                                              //
constant function BUSSR_HealthDamagePerLevel takes nothing returns real
    return 0.00
endfunction
//----------------------------------------------------------------//
//  ManaDamageBase: This is the mana damage dealt by the laser    //
//  to enemy units                                                //
constant function BUSSR_ManaDamageBase takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  ManaDamagePerLevel: This is the per level counterpart of      //
//  ManaDamageBase                                                //
constant function BUSSR_ManaDamagePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  ChannelDurationBase: This is the duration of the channeling   //
//  used by the spell                                             //
constant function BUSSR_ChannelDurationBase takes nothing returns real
    return 2.5
endfunction
//----------------------------------------------------------------//
//  ChannelDurationPerLevel: This is the per level counterpart of //
//  ChannelDurationBase                                           //
constant function BUSSR_ChannelDurationPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  AOEBase: This is the AOE of the laser (width of the laser)    //
constant function BUSSR_AOEBase takes nothing returns real
    return 250.
endfunction
//----------------------------------------------------------------//
//  AOEPerLevel: This is the per level counterpart of AOEBASE     //
constant function BUSSR_AOEPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  RangeBase: This is the maximum distance of the laser          //
constant function BUSSR_RangeBase takes nothing returns real
    return 1000.
endfunction
//----------------------------------------------------------------//
//  RangePerLevel: This is the per level counterpart of RangeBase //
constant function BUSSR_RangePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  ForceBase: This is the amount of force the laser exerts on    //
//  units which are hit by it                                     //
constant function BUSSR_ForceBase takes nothing returns real
    return 90.
endfunction
//----------------------------------------------------------------//
//  ForcePerLevel: This is the per level counterpart of ForceBase //
constant function BUSSR_ForcePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  EndChannelTimeBase: This is the time after channeling is      //
//  completed that the laser is created (this value + the Angle   //
//  delay == how long after channeling the laser fires)           //
constant function BUSSR_EndChannelTimeBase takes nothing returns real
    return 0.75
endfunction
//----------------------------------------------------------------//
//  EndChannelTimePerLevel: This is the per level counterpart     //
//  EndChannelTimeBase                                            //
constant function BUSSR_EndChannelTimePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  EndChannelAngleDelay: This is the time after being created    //
//  that the laser fires                                          //
constant function BUSSR_EndChannelAngleDelay takes nothing returns real
    return 0.50
endfunction
//----------------------------------------------------------------//
//  EffectRemove: This is how long after an effect has been       //
//  completed that it is removed                                  //
constant function BUSSR_EffectRemove takes nothing returns real
    return 4.00
endfunction
//----------------------------------------------------------------//
//  PointerScaleBase: This is the scale of the effect which       //
//  denotes the angle of fire of the laser                        //
constant function BUSSR_PointerScaleBase takes nothing returns real
    return 3.
endfunction
//----------------------------------------------------------------//
//  PointerScalePerLevel: This is the per level counterpart       //
//  PointerScaleBase                                              //
constant function BUSSR_PointerScalePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  LaserScaleBase: This is the scaling size of the laser         //
constant function BUSSR_LaserScaleBase takes nothing returns real
    return 2.25
endfunction
//----------------------------------------------------------------//
//  LaserScalePerLevel: This is the per level counterpart of      //
//  LaserScaleBase                                                //
constant function BUSSR_LaserScalePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  OffsetBase: This is the offset from the casting unit that all //
//  laser effects are created at                                  //
constant function BUSSR_OffsetBase takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  OffsetPerLevel: This is the per level counterpart of          //
//  OffsetBase                                                    //
constant function BUSSR_OffsetPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  GroundHeightOffset: This is the z offset lasers are created   //
//  at when the spell is cast by a ground unit                    //
constant function BUSSR_GroundHeightOffset takes nothing returns real
    return 200.
endfunction
//----------------------------------------------------------------//
//  PointerEffect: This is the pathname of the effect used to     //
//  denote the direction of the laser                             //
constant function BUSSR_PointerEffect takes nothing returns string
    return "Abilities\\Spells\\Orc\\LightningShield\\LightningShieldTarget.mdl"
endfunction
//----------------------------------------------------------------//
//  LaserEffect: This is the pathname of the effect used to       //
//  denote the laser                                              //
constant function BUSSR_LaserEffect takes nothing returns string
    return "war3mapImported\\OrbitalRay.mdx"
endfunction
//----------------------------------------------------------------//
//  UnitDamageEffect: This is the pathname of the effect used to  //
//  denote a unit being damaged                                   //
constant function BUSSR_UnitDamageEffect takes nothing returns string
    return "Objects\\Spawnmodels\\NightElf\\NEDeathSmall\\NEDeathSmall.mdl"
endfunction
//----------------------------------------------------------------//
//  GroundDamageEffect: This is the pathname of the effect used   //
//  to mark where the laser hits the ground                       //
constant function BUSSR_GroundDamageEffect takes nothing returns string
    return "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl"
endfunction
//----------------------------------------------------------------//
//  AttackType: This is the attacktype used by the spell          //
constant function BUSSR_AttackType takes nothing returns attacktype
    return ATTACK_TYPE_MAGIC
endfunction
//----------------------------------------------------------------//
//  DamageType: This is the damagetype used by the spell          //
constant function BUSSR_DamageType takes nothing returns damagetype
    return DAMAGE_TYPE_MAGIC
endfunction
//----------------------------------------------------------------//
//  WeaponType: This is the weapontype used by the spell          //
constant function BUSSR_WeaponType takes nothing returns weapontype
    return null
endfunction
//----------------------------------------------------------------//
//  TargetFilter: This is the target filter for units that can    //
//  be turned into spirits, configure this as desired (requires   //
//  some coding knowledge, u is the target unit, Node is the      //
//  index of the casting unit                                     //
function BUS_STargetFilter takes unit u, integer Node returns boolean
    return (not(IsUnitType(u, UNIT_TYPE_DEAD)) and not(IsUnitType(u, UNIT_TYPE_STRUCTURE)) and (IsPlayerEnemy(GetOwningPlayer(u), udg_BUSS_Owner[Node])))
endfunction
//----------------------------------------------------------------//
//                      END OF CONFIGURATION                      //
//----------------------------------------------------------------//
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////

//  Function used to prevent two casts of the same channel being  //
//  done simultaneously                                           //
////////////////////////////////////////////////////////////////////
function BUS_CheckScathe takes unit u returns integer
    local integer Node = 0
  
    loop
        set Node = udg_BUSS_NextNode[Node]
        exitwhen (udg_BUSS_Unit[Node] == u)  or (Node == 0)
    endloop
  
    return Node
endfunction

////////////////////////////////////////////////////////////////////
//  ScatheLoop: The main function used to make sure the caster is //
//  still channeling the ability, fire the laser and handle all   //
//  effects used by the ability                                   //
////////////////////////////////////////////////////////////////////
function BUS_ScatheLoop takes nothing returns nothing
    local integer Node = 0
    local integer TempNode = 0
    local real TempRealX
    local real TempRealY
    local real TempRealZ
    local real TempRealMax
    local real x
    local real y
    local real z
    local real dx
    local real dy
    local real Angle
    local real Angle2
    local unit TempUnit
    local boolean TempBoolean
  
    loop
        set Node = udg_BUSS_NextNode[Node]
        exitwhen Node == 0
      
        if (GetUnitCurrentOrder(udg_BUSS_Unit[Node]) == BUSSR_Order()) and not(udg_BUSS_Cancel[Node]) then
          
            if (udg_BUSS_EndChannelTime[Node] < 0) and (udg_BUSS_Activate[Node]) then
                set udg_BUSS_Activate[Node] = false
                set udg_BUSS_EndDelay[Node] = BUSSR_EndChannelAngleDelay()
                set udg_BUSS_EffectMade[Node] = true
              
                if (udg_BUSS_PRecyclableNodes == 0) then
                    set udg_BUSS_PNodeNumber = udg_BUSS_PNodeNumber + 1
                    set TempNode = udg_BUSS_PNodeNumber
                else
                    set udg_BUSS_PRecyclableNodes = udg_BUSS_PRecyclableNodes - 1
                    set TempNode = udg_BUSS_PRecycleNodes[udg_BUSS_PRecyclableNodes]
                endif

                set udg_BUSS_PNextNode[TempNode] = 0
                set udg_BUSS_PNextNode[udg_BUSS_PPrevNode[0]] = TempNode
                set udg_BUSS_PPrevNode[TempNode] = udg_BUSS_PPrevNode[0]
                set udg_BUSS_PPrevNode[0] = TempNode

                set udg_BUSS_PRemove[TempNode] = false
                set udg_BUSS_PCaster[TempNode] = udg_BUSS_Unit[Node]
                set udg_BUSS_PUnit[TempNode] = CreateUnit(BUSCR_DummyPlayer(), BUSSR_DummyId(),udg_BUSS_PX[Node],udg_BUSS_PY[Node], udg_BUSS_Angle[Node])
                if UnitAddAbility(udg_BUSS_PUnit[TempNode], 'Amrf') and UnitRemoveAbility(udg_BUSS_PUnit[TempNode], 'Amrf') then
                endif
                call SetUnitScale(udg_BUSS_PUnit[TempNode], udg_BUSS_LaserScale[Node], 0.00, 0.00)
                call SetUnitAnimationByIndex(udg_BUSS_PUnit[TempNode], udg_BUSS_PAnimationIndex[Node])
                set udg_BUSS_PCurrentEffect[TempNode] = null
                call SetUnitFlyHeight(udg_BUSS_PUnit[TempNode], udg_BUSS_PZ[Node] - BUS_GetZ(udg_BUSS_PX[Node], udg_BUSS_PY[Node]), 0.00)
            else
                set udg_BUSS_EndChannelTime[Node] = udg_BUSS_EndChannelTime[Node] - BUSSR_TimerSpeed()
            endif
          
            if (udg_BUSS_EndDelay[Node] < 0) and (not(udg_BUSS_Activate[Node])) and (udg_BUSS_EffectMade[Node]) then
                set TempNode = 0
              
                loop
                    set TempNode = udg_BUSS_PNextNode[TempNode]
                    exitwhen TempNode == 0
                  
                    if (udg_BUSS_PCurrentEffect[TempNode] == null) and (udg_BUSS_PCaster[TempNode] == udg_BUSS_Unit[Node]) then
                        set udg_BUSS_PCurrentEffect[TempNode] = AddSpecialEffectTarget(BUSSR_LaserEffect(), udg_BUSS_PUnit[TempNode], BUSSR_AttachmentPoint())
                        set udg_BUSS_EffectMade[Node] = false
                    endif
                  
                endloop
              
                set TempRealX = udg_BUSS_PX[Node]
                set TempRealY = udg_BUSS_PY[Node]
                set TempRealZ = udg_BUSS_PZ[Node]
                set TempRealMax = 0.
              
                loop
                    call GroupEnumUnitsInRange(udg_BUSS_UnitGroup, TempRealX, TempRealY, udg_BUSS_AOE[Node], null)
                  
                    loop
                        set TempUnit = FirstOfGroup(udg_BUSS_UnitGroup)
                        exitwhen TempUnit == null
                        set x = GetUnitX(TempUnit)
                        set y = GetUnitY(TempUnit)
                        set z = GetUnitFlyHeight(TempUnit) + BUS_GetZ(x,y)
                      
                        if (BUS_STargetFilter(TempUnit, Node)) and (SquareRoot((TempRealX - x) * (TempRealX - x) + (TempRealY - y) * (TempRealY - y) + (TempRealZ - z) * (TempRealZ - z)) < udg_BUSS_AOE[Node]) then
                            call UnitDamageTarget(udg_BUSS_Unit[Node], TempUnit, udg_BUSS_HealthDamage[Node], false, false, BUSSR_AttackType(), BUSSR_DamageType(), BUSSR_WeaponType())
                            call SetUnitState(TempUnit, UNIT_STATE_MANA, GetUnitState(TempUnit, UNIT_STATE_MANA) - udg_BUSS_ManaDamage[Node])
                            call DestroyEffect(AddSpecialEffectTarget(BUSSR_UnitDamageEffect(), TempUnit, BUSSR_AttachmentPoint()))
                          
                            call BUS_StartKnockback(TempUnit, udg_BUSS_Force[Node], x, udg_BUSS_PX[Node], y, udg_BUSS_PY[Node], z, udg_BUSS_PZ[Node])
                        endif
                      
                        call GroupRemoveUnit(udg_BUSS_UnitGroup, TempUnit)
                    endloop
                  
                    exitwhen ((TempRealMax > udg_BUSS_Range[Node]) or (TempRealZ < BUS_GetZ(TempRealX, TempRealY)))
                    set TempRealX = TempRealX + (udg_BUSS_DX[Node])
                    set TempRealY = TempRealY + (udg_BUSS_DY[Node])
                    set TempRealZ = TempRealZ + (udg_BUSS_DZ[Node])
                    set TempRealMax = TempRealMax + udg_BUSS_AOE[Node]
                endloop
              
                if (TempRealZ < BUS_GetZ(TempRealX, TempRealY)) then
                    call DestroyEffect(AddSpecialEffect(BUSSR_GroundDamageEffect(), TempRealX, TempRealY))
                endif
              
            else
                set udg_BUSS_EndDelay[Node] = udg_BUSS_EndDelay[Node] - BUSSR_TimerSpeed()
            endif
          
        else
            set TempNode = 0
          
            loop
                set TempNode = udg_BUSS_PNextNode[TempNode]
                exitwhen TempNode == 0

              
                if (udg_BUSS_PCaster[TempNode] == udg_BUSS_Unit[Node]) then
              
                    if not(udg_BUSS_PCurrentEffect[TempNode] == null) then
                        call DestroyEffect(udg_BUSS_PCurrentEffect[TempNode])
                    endif
                     
                    set udg_BUSS_PRemove[TempNode] = true
                    set udg_BUSS_PTimer[TempNode] = BUSSR_EffectRemove()
                endif
              
            endloop
          
            set udg_BUSS_RecycleNodes[udg_BUSS_RecyclableNodes] = Node
            set udg_BUSS_RecyclableNodes = udg_BUSS_RecyclableNodes + 1
            set udg_BUSS_NextNode[udg_BUSS_PrevNode[Node]] = udg_BUSS_NextNode[Node]
            set udg_BUSS_PrevNode[udg_BUSS_NextNode[Node]] = udg_BUSS_PrevNode[Node]
          
            if (udg_BUSS_NextNode[0] + udg_BUSS_PNextNode[0] == 0) then
                call PauseTimer(udg_BUSS_Timer)
            endif
          
        endif
          
    endloop
  
    set TempNode = 0
  
    loop
        set TempNode = udg_BUSS_PNextNode[TempNode]
        exitwhen TempNode == 0
      
        if (udg_BUSS_PRemove[TempNode]) then
      
            if (udg_BUSS_PTimer[TempNode] < 0) then
                call RemoveUnit(udg_BUSS_PUnit[TempNode])
              
                set udg_BUSS_PRecycleNodes[udg_BUSS_PRecyclableNodes] = TempNode
                set udg_BUSS_PRecyclableNodes = udg_BUSS_PRecyclableNodes + 1
                set udg_BUSS_PNextNode[udg_BUSS_PPrevNode[TempNode]] = udg_BUSS_PNextNode[TempNode]
                set udg_BUSS_PPrevNode[udg_BUSS_PNextNode[TempNode]] = udg_BUSS_PPrevNode[TempNode]
              
                if (udg_BUSS_NextNode[0] + udg_BUSS_PNextNode[0] == 0) then
                    call PauseTimer(udg_BUSS_Timer)
                endif
            else
                set udg_BUSS_PTimer[TempNode] = udg_BUSS_PTimer[TempNode] - BUSSR_TimerSpeed()
            endif
          
        endif
      
    endloop
  
endfunction

////////////////////////////////////////////////////////////////////
//  ScatheEvent: Function used to set up all the data and effects //
//  that are used in the loop. Activates when the channeling is   //
//  completed                                                     //
////////////////////////////////////////////////////////////////////
function BUS_ScatheEvent takes nothing returns boolean
    local real rLevel = GetUnitAbilityLevel(udg_BUS_Unit, BUSSR_Ability())
    local integer Node
    local integer TempNode
    local real x = GetUnitX(udg_BUS_Unit)
    local real y = GetUnitY(udg_BUS_Unit)
    local real x2 = GetUnitX(udg_BUS_Target)
    local real y2 = GetUnitY(udg_BUS_Target)
    local real z = BUS_GetZ(x,y) + GetUnitFlyHeight(udg_BUS_Unit)
    local real z2 = BUS_GetZ(x2, y2) + GetUnitFlyHeight(udg_BUS_Target)
    local real Angle
    local real TempReal = BUSSR_OffsetBase() + (rLevel * BUSSR_OffsetPerLevel())
  
    if (IsUnitType(udg_BUS_Unit, UNIT_TYPE_GROUND)) then
        set z = z + BUSSR_GroundHeightOffset()
    endif
  
    if (udg_BUSS_RecyclableNodes == 0) then
        set udg_BUSS_NodeNumber = udg_BUSS_NodeNumber + 1
        set Node = udg_BUSS_NodeNumber
    else
        set udg_BUSS_RecyclableNodes = udg_BUSS_RecyclableNodes - 1
        set Node = udg_BUSS_RecycleNodes[udg_BUSS_RecyclableNodes]
    endif

    set udg_BUSS_NextNode[Node] = 0
    set udg_BUSS_NextNode[udg_BUSS_PrevNode[0]] = Node
    set udg_BUSS_PrevNode[Node] = udg_BUSS_PrevNode[0]
    set udg_BUSS_PrevNode[0] = Node
  
    set udg_BUSS_Unit[Node] = udg_BUS_Unit
    set udg_BUSS_Owner[Node] = GetOwningPlayer(udg_BUSS_Unit[Node])
    set udg_BUSS_HealthDamage[Node] = BUSSR_HealthDamageBase() + (rLevel * BUSSR_HealthDamagePerLevel())
    set udg_BUSS_ManaDamage[Node] = BUSSR_ManaDamageBase() + (rLevel * BUSSR_ManaDamagePerLevel())
    set udg_BUSS_AOE[Node] = BUSSR_AOEBase() + (rLevel * BUSSR_AOEPerLevel())
    set udg_BUSS_Range[Node] = BUSSR_RangeBase() + (rLevel * BUSSR_RangePerLevel())
    set udg_BUSS_Force[Node] = BUSSR_ForceBase() + (rLevel * BUSSR_ForcePerLevel())
    set udg_BUSS_EndChannelTime[Node] = BUSSR_EndChannelTimeBase() + (rLevel * BUSSR_EndChannelTimePerLevel())
    set udg_BUSS_Activate[Node] = true
    set udg_BUSS_Cancel[Node] = false

    set udg_BUSS_DY[Node] = y2 - y
    set udg_BUSS_DX[Node] = x2 - x
    set Angle = Atan2(udg_BUSS_DY[Node], udg_BUSS_DX[Node])
    set udg_BUSS_DZ[Node] = Atan2(z2 - z, SquareRoot((udg_BUSS_DX[Node] * udg_BUSS_DX[Node]) + (udg_BUSS_DY[Node] * udg_BUSS_DY[Node])))
    set udg_BUSS_PZ[Node] = z + (TempReal * Sin(udg_BUSS_DZ[Node]))
    set udg_BUSS_PX[Node] = x + (TempReal * Cos(Angle) * Cos(udg_BUSS_DZ[Node]))
    set udg_BUSS_PY[Node] = y + (TempReal * Sin(Angle) * Cos(udg_BUSS_DZ[Node]))
  
    if (udg_BUSS_PrevNode[Node] + udg_BUSS_PPrevNode[0] == 0) then
        call TimerStart(udg_BUSS_Timer, BUSSR_TimerSpeed(), true, function BUS_ScatheLoop)
    endif
  
    if (udg_BUSS_PRecyclableNodes == 0) then
        set udg_BUSS_PNodeNumber = udg_BUSS_PNodeNumber + 1
        set TempNode = udg_BUSS_PNodeNumber
    else
        set udg_BUSS_PRecyclableNodes = udg_BUSS_PRecyclableNodes - 1
        set TempNode = udg_BUSS_PRecycleNodes[udg_BUSS_PRecyclableNodes]
    endif

    set udg_BUSS_PNextNode[TempNode] = 0
    set udg_BUSS_PNextNode[udg_BUSS_PPrevNode[0]] = TempNode
    set udg_BUSS_PPrevNode[TempNode] = udg_BUSS_PPrevNode[0]
    set udg_BUSS_PPrevNode[0] = TempNode
  
    set udg_BUSS_Angle[Node] = Angle * bj_RADTODEG
    set udg_BUSS_PRemove[TempNode] = false
  
    set udg_BUSS_PCaster[TempNode] = udg_BUS_Unit
    set udg_BUSS_PUnit[TempNode] = CreateUnit(BUSCR_DummyPlayer(), BUSSR_DummyId(),udg_BUSS_PX[Node],udg_BUSS_PY[Node], udg_BUSS_Angle[Node])
    if UnitAddAbility(udg_BUSS_PUnit[TempNode], 'Amrf') and UnitRemoveAbility(udg_BUSS_PUnit[TempNode], 'Amrf') then
    endif
    set TempReal = z2 - z
    if (TempReal < 0) then
        set udg_BUSS_PScale[Node] = -1 * (BUSSR_PointerScaleBase() + (rLevel * BUSSR_PointerScalePerLevel()))
        set udg_BUSS_LaserScale[Node] = -1 * (BUSSR_LaserScaleBase() + (rLevel * BUSSR_LaserScalePerLevel()))
        set udg_BUSS_PAnimationIndex[Node] = R2I(Atan2((TempReal), SquareRoot((udg_BUSS_DX[Node] * udg_BUSS_DX[Node]) + (udg_BUSS_DY[Node] * udg_BUSS_DY[Node]))) * bj_RADTODEG + 0.5) + 180
    else
        set udg_BUSS_PScale[Node] = (BUSSR_PointerScaleBase() + (rLevel * BUSSR_PointerScalePerLevel()))
        set udg_BUSS_LaserScale[Node] = (BUSSR_LaserScaleBase() + (rLevel * BUSSR_LaserScalePerLevel()))
        set udg_BUSS_PAnimationIndex[Node] = R2I(Atan2((TempReal), SquareRoot((udg_BUSS_DX[Node] * udg_BUSS_DX[Node]) + (udg_BUSS_DY[Node] * udg_BUSS_DY[Node]))) * bj_RADTODEG + 0.5)
    endif
    call SetUnitScale(udg_BUSS_PUnit[TempNode], udg_BUSS_PScale[Node], 0.00, 0.00)
    call SetUnitAnimationByIndex(udg_BUSS_PUnit[TempNode], udg_BUSS_PAnimationIndex[Node])
    set udg_BUSS_PCurrentEffect[TempNode] = AddSpecialEffectTarget(BUSSR_PointerEffect(), udg_BUSS_PUnit[TempNode], BUSSR_AttachmentPoint())
    call SetUnitFlyHeight(udg_BUSS_PUnit[TempNode], udg_BUSS_PZ[Node] - BUS_GetZ(udg_BUSS_PX[Node], udg_BUSS_PY[Node]), 0.00) 
    set udg_BUSS_DX[Node] = Cos(Angle) * Cos(udg_BUSS_DZ[Node]) * udg_BUSS_AOE[Node]
    set udg_BUSS_DY[Node] = Sin(Angle) * Cos(udg_BUSS_DZ[Node]) * udg_BUSS_AOE[Node]
    set udg_BUSS_DZ[Node] = Sin(udg_BUSS_DZ[Node]) * udg_BUSS_AOE[Node]
  
    return false
endfunction

////////////////////////////////////////////////////////////////////
//  ScatheStart: Function used to set up the channeling for the   //
//  ability                                                       //
////////////////////////////////////////////////////////////////////
function BUS_ScatheStart takes nothing returns boolean
    local unit u
  
    if (GetSpellAbilityId() == BUSSR_Ability()) then
        set u = GetTriggerUnit()
        set udg_BUSS_Cancel[BUS_CheckScathe(u)] = true
        call BUS_StartChannel(BUS_GetChannelByName(BUSSR_ChannelType()), u, GetSpellTargetUnit(), 0., 0., BUSSR_ChannelDurationBase() + (GetUnitAbilityLevel(u, BUSSR_Ability()) * BUSSR_ChannelDurationPerLevel()), BUSSR_Event(), BUSSR_HaveHue())
        set u = null
    endif

    return false
endfunction

////////////////////////////////////////////////////////////////////
//  InitTrig BUS Scathe: Function used to set up the event and    //
//  starting functions of the ability as well as the timer for    //
//  the loop                                                      //
////////////////////////////////////////////////////////////////////
function InitTrig_BUS_Scathe takes nothing returns nothing
    local trigger t = CreateTrigger()
    local integer index = 0
  
    loop
        call TriggerRegisterPlayerUnitEvent(t, Player(index),EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
        set index = index + 1
        exitwhen index >= bj_MAX_PLAYER_SLOTS
    endloop
  
    call TriggerAddCondition(t, Condition(function BUS_ScatheStart))
  
    set t = CreateTrigger()
    call TriggerRegisterVariableEvent(t, "udg_BUSC_Event", EQUAL, BUSSR_Event())
    call TriggerAddCondition(t, Condition(function BUS_ScatheEvent))
  
    set udg_BUSS_Timer = CreateTimer()
  
endfunction

////////////////////////////////////////////////////////////////////
//  End of the spell                                              //
////////////////////////////////////////////////////////////////////

The caster absorbs powerful magic lingering in the air and focuses it, fashioning it into a powerful laser to blast back its target dealing 900 damage to all enemies caught in the light and throwing them away from the caster


GIF
172730-albums6263-picture98399.gif
Boss Sheer Force

What is this? - Sheer Force is a gravity based ability using the force of it to both damage and push enemies away from the caster, it has a fast charge up and use time being the most simple spell in the pack, the ability is instant-cast

JASS:
////////////////////////////////////////////////////////////////////
//            Boss Ultimate Spellpack Sheer Force V1.00           //
//  Author: Tank-Commander                                        //
//  Purpose: Boss Strong Knockback (ring out)                     //
//  Requires: Dummy.mdl, BUS Channel, BUS Knockback               //
//                                                                //
//  Notes:                                                        //
//    -  Read the readme before you try modifying the config      //
//    -  Use the "Helpful files" to help you import the system    //
//                                                                //
//  Credits:                                                      //
//    -  (Dummy.mdl) Vexorian                                     //
//    -  (ForceField.mdl) Fingolfin                               //
//                                                                //
//  Optimum User: Flyer (any)                                     //
//                                                                //
//  If you have used this spellpack 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       //
//----------------------------------------------------------------//
//  CONFIGURABLE INFORMATION/HELP:                                //
//                                                                //
//  - Viewing data values: To see data values in the editor you   //
//  need to press Ctrl + D, to shift back to normal viewing       //
//  press it again                                                //
//                                                                //
//  - Effects: Pathnames for effects used in the spells should    //
//  have two "\"s throughout or the effect will not work (the     //
//  WE progress bar will not go away when saving, however if      //
//  fixed afterwards the save will still work, but the progress   //
//  bar will still remain until the WE is closed)                 //
//  e.g. "units\\human\\Footman\\Footman"                         //
//                                                                //
//  - Effect Scaling: Some effects have scale values below them   //
//  the scale determines the size of the effect and is expressed  //
//  as a real percentage (1.00 = 100%)                            //
//                                                                //
//  - Removing Effects: to remove an effect you don't want from   //
//  the ability, set the model path to that of Dummy.mdl          //
//                                                                //
//  - Base and Per Values: Most configurables have a base and per //
//  value, Base values are what a value is set to regardless of   //
//  other factors. Per values are what a value is set to based on //
//  what the per value is the formula for calculating the result  //
//  is as follows:                                                //
//    - BaseValue + (Factor * PerValue)                           //
//                                                                //
//  - Timer: Some configurables have PerSecond values, the code   //
//  automatically accounts for changes to the timer as to         //
//  maintain consistency with what the user has chosen            //
//  All times in the system are expressions of seconds            //
//                                                                //
//  - AttackTypes: This should match the Damage Type of the       //
//  ability, though it can be something else if you wish          //
//                                                                //
//  - DamageTypes: This changes the damage multiplyer vs. enemy   //
//  armour types, note that by default the damage filters         //
//  exclude magic immune units so changing this from MAGIC        //
//  damage will not make them take damage                         //
//                                                                //
//  - WeaponTypes: Generally don't need to be used, should only   //
//  not be null if you particularly wish or need to use them      //
//                                                                //
//----------------------------------------------------------------//
//  TimerSpeed: This is the amount of time in seconds between     //
//  each iteration of the Sheer Force Loop function               //
constant function BUSFR_TimerSpeed takes nothing returns real
    return 0.031250000
endfunction
//----------------------------------------------------------------//
//  Ability: This is the AbilityId that is used as the dummy      //
//  ability used for the Sheer Force spell                        //
constant function BUSFR_Ability takes nothing returns integer
    return 'A003'
endfunction
//----------------------------------------------------------------//
//  DummyId: This is the UnitId that is used to create effects    //
//  It is recommended this unit use Dummy.mdl for its model for   //
//  optimal usage                                                 //
constant function BUSFR_DummyId takes nothing returns integer
    return 'u000'
endfunction
//----------------------------------------------------------------//
//  Event: This is the value of BUS_Event that it will be set to  //
//  when the channeling ends, this should be unique to each       //
//  ability which uses the channel system                         //
constant function BUSFR_Event takes nothing returns integer
    return 3
endfunction
//----------------------------------------------------------------//
//  AttachmentPoint: This is the point that effects are attached  //
//  to on units and dummy units                                   //
constant function BUSFR_AttachmentPoint takes nothing returns string
    return "origin"
endfunction
//----------------------------------------------------------------//
//  HaveHue; This determines whether or not during channeling     //
//  the hue of the caster changes throughout                      //
constant function BUSFR_HaveHue takes nothing returns boolean
    return true
endfunction
//----------------------------------------------------------------//
//  ChannelType: This is the name of the channel type used by     //
//  this ability (it should match the name of the channel type)   //
constant function BUSFR_ChannelType takes nothing returns string
    return "gravity"
endfunction
//----------------------------------------------------------------//
//  ChannelDurationBase: This is the duration of the channeling   //
//  used by the spell                                             //
constant function BUSFR_ChannelDurationBase takes nothing returns real
    return 2.
endfunction
//----------------------------------------------------------------//
//  ChannelDurationPerLevel: This is the per level counterpart of //
//  ChannelDurationBase                                           //
constant function BUSFR_ChannelDurationPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  AOEBase: This is the AOE of the ability                       //
constant function BUSFR_AOEBase takes nothing returns real
    return 800.
endfunction
//----------------------------------------------------------------//
//  AOEPerLevel: This is the per level counterpart of AOEBase     //
constant function BUSFR_AOEPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  ForceBase: This is the amount of force this ability exerts    //
//  on the units that are affected                                //
constant function BUSFR_ForceBase takes nothing returns real
    return 4000.
endfunction
//----------------------------------------------------------------//
//  ForcePerLevel: This is the per level counterpart of ForceBase //
constant function BUSFR_ForcePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  ForceDegradeBase: This is the change of force over distance   //
//  the lower the value the faster the force degrades             //
constant function BUSFR_ForceDegradeBase takes nothing returns real
    return 10.
endfunction
//----------------------------------------------------------------//
//  ForceDegradePerLevel: This is the per level counterpart of    //
//  ForceDegradeBase                                              //
constant function BUSFR_ForceDegradePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  ForceDamageMultiplyerBase: This is the multiplyer of damage   //
//  in terms of Force, Force * Multiplyer = Damage                //
constant function BUSFR_ForceDamageMultiplyerBase takes nothing returns real
    return .33
endfunction
//----------------------------------------------------------------//
//  ForceDamageMultiplyerPerLevel: This is the per level          //
//  counterpart of ForceDamageMultiplyerBase                      //
constant function BUSFR_ForceDamageMultiplyerPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  DamageMana: This determines whether or not the same damage    //
//  is dealt to the units mana                                    //
constant function BUSFR_DamageMana takes nothing returns boolean
    return false
endfunction
//----------------------------------------------------------------//
//  OrbStartSizeBase: This determines the start size of the orb   //
//  effect which marks the boundaries of the effect               //
constant function BUSFR_OrbStartSizeBase takes nothing returns real
    return 0.01
endfunction
//----------------------------------------------------------------//
//  OrbStartSizePerLevel: This is the per level counterpart of    //
//  OrbStartSizeBase                                              //
constant function BUSFR_OrbStartSizePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  OrbEndSizeBase: This is the final size of the Orb effect      //
constant function BUSFR_OrbEndSizeBase takes nothing returns real
    return 1.
endfunction
//----------------------------------------------------------------//
//  OrbEndSizePerLevel: This is the per level counterpart of      //
//  OrbEndSizeBase                                                //
constant function BUSFR_OrbEndSizePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  OrbTimeBase: This is how long it takes for the Orb to reach   //
//  its maximum size                                              //
constant function BUSFR_OrbTimeBase takes nothing returns real
    return 0.25
endfunction
//----------------------------------------------------------------//
//  OrbTimePerLevel: This is the per level counterpart of         //
//  OrbTimeBase                                                   //
constant function BUSFR_OrbTimePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  OrbEffect: This is the pathname of the effect used to denote  //
//  the boarder of the ability                                    //
constant function BUSFR_OrbEffect takes nothing returns string
    return "war3mapImported\\ForceField.mdx"
endfunction
//----------------------------------------------------------------//
//  AttackType: This is the attacktype used by the spell          //
constant function BUSFR_AttackType takes nothing returns attacktype
    return ATTACK_TYPE_MAGIC
endfunction
//----------------------------------------------------------------//
//  DamageType: This is the damagetype used by the spell          //
constant function BUSFR_DamageType takes nothing returns damagetype
    return DAMAGE_TYPE_MAGIC
endfunction
//----------------------------------------------------------------//
//  WeaponType: This is the weapontype used by the spell          //
constant function BUSFR_WeaponType takes nothing returns weapontype
    return null
endfunction
//----------------------------------------------------------------//
//  EffectRemove: This is how long after an effect has been       //
//  completed that it is removed                                  //
constant function BUSFR_EffectRemove takes nothing returns real
    return 2.00
endfunction
//----------------------------------------------------------------//
//  TargetFilter: This is the target filter for units that can    //
//  be turned into spirits, configure this as desired (requires   //
//  some coding knowledge, u is the target unit, Node is the      //
//  index of the casting unit                                     //
function BUS_FTargetFilter takes unit u, player pl returns boolean
    return (not(IsUnitType(u, UNIT_TYPE_DEAD)) and not(IsUnitType(u, UNIT_TYPE_STRUCTURE)) and (IsPlayerEnemy(GetOwningPlayer(u), pl)))
endfunction
//----------------------------------------------------------------//
//                      END OF CONFIGURATION                      //
//----------------------------------------------------------------//
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
//  SheerForceLoop: Function used to handle the effects of the    //
//  ability and recycle the ones that have been completed         //
////////////////////////////////////////////////////////////////////
function BUS_SheerForceLoop takes nothing returns nothing
    local integer Node = 0
  
    loop
        set Node = udg_BUSF_NextNode[Node]
        exitwhen Node == 0

        if (udg_BUSF_Remove[Node]) then
            if (udg_BUSF_RemoveTimer[Node] <= 0) then
                call RemoveUnit(udg_BUSF_Unit[Node])
              
                set udg_BUSF_RecycleNodes[udg_BUSF_RecyclableNodes] = Node
                set udg_BUSF_RecyclableNodes = udg_BUSF_RecyclableNodes + 1
                set udg_BUSF_NextNode[udg_BUSF_PrevNode[Node]] = udg_BUSF_NextNode[Node]
                set udg_BUSF_PrevNode[udg_BUSF_NextNode[Node]] = udg_BUSF_PrevNode[Node]
              
                if (udg_BUSF_PrevNode[0] == 0) then
                    call PauseTimer(udg_BUSF_Timer)
                endif
              
            else
                set udg_BUSF_RemoveTimer[Node] = udg_BUSF_RemoveTimer[Node] - BUSFR_TimerSpeed()
            endif
        else
            set udg_BUSF_CurrentSize[Node] = udg_BUSF_CurrentSize[Node] + udg_BUSF_ScaleSpeed[Node]
            set udg_BUSF_GrowthTime[Node] = udg_BUSF_GrowthTime[Node] - BUSFR_TimerSpeed()
            call SetUnitScale(udg_BUSF_Unit[Node], udg_BUSF_CurrentSize[Node], 0., 0.)
          
            if (udg_BUSF_GrowthTime[Node] <= 0) then
                set udg_BUSF_Remove[Node] = true
                call DestroyEffect(udg_BUSF_CurrentEffect[Node])
            endif
        endif
      
    endloop
  
endfunction

////////////////////////////////////////////////////////////////////
//  SheerForceEvent: The main function, used to start the         //
//  knockback of units as well as start the effects used in the   //
//  loop of the ability                                           //
////////////////////////////////////////////////////////////////////
function BUS_SheerForceEvent takes nothing returns boolean
    local real rLevel = GetUnitAbilityLevel(udg_BUS_Unit, BUSFR_Ability())
    local real x = GetUnitX(udg_BUS_Unit)
    local real y = GetUnitY(udg_BUS_Unit)
    local real z = GetUnitFlyHeight(udg_BUS_Unit) + BUS_GetZ(x, y)
    local real x2
    local real y2
    local real z2
    local real TempReal = BUSFR_AOEBase() + (rLevel * BUSFR_AOEPerLevel())
    local real TempReal2 = BUSFR_ForceBase() + (rLevel * BUSFR_ForcePerLevel())
    local real TempReal3 = BUSFR_ForceDegradeBase() + (rLevel * BUSFR_ForceDegradePerLevel())
    local real TempReal4 = BUSFR_ForceDamageMultiplyerBase() + (rLevel * BUSFR_ForceDamageMultiplyerPerLevel())
    local real TempReal5
    local real TempReal6
    local unit u
    local player pl = GetOwningPlayer(udg_BUS_Unit)
    local integer Node

    if (udg_BUSF_RecyclableNodes == 0) then
        set udg_BUSF_NodeNumber = udg_BUSF_NodeNumber + 1
        set Node = udg_BUSF_NodeNumber
    else
        set udg_BUSF_RecyclableNodes = udg_BUSF_RecyclableNodes - 1
        set Node = udg_BUSF_RecycleNodes[udg_BUSF_RecyclableNodes]
    endif

    set udg_BUSF_NextNode[Node] = 0
    set udg_BUSF_NextNode[udg_BUSF_PrevNode[0]] = Node
    set udg_BUSF_PrevNode[Node] = udg_BUSF_PrevNode[0]
    set udg_BUSF_PrevNode[0] = Node
  
    set udg_BUSF_Unit[Node] = CreateUnit(BUSCR_DummyPlayer(), BUSFR_DummyId(), x, y, 0.)
    set udg_BUSF_CurrentEffect[Node] = AddSpecialEffectTarget(BUSFR_OrbEffect(), udg_BUSF_Unit[Node], BUSFR_AttachmentPoint())
    set udg_BUSF_CurrentSize[Node] = BUSFR_OrbStartSizeBase() + (rLevel * BUSFR_OrbStartSizePerLevel())
    set udg_BUSF_GrowthTime[Node] = BUSFR_OrbTimeBase() + (rLevel * BUSFR_OrbTimePerLevel())
    set udg_BUSF_ScaleSpeed[Node] = (((BUSFR_OrbEndSizeBase() + (rLevel * BUSFR_OrbEndSizePerLevel())) - udg_BUSF_CurrentSize[Node]) / udg_BUSF_GrowthTime[Node]) * BUSFR_TimerSpeed()
    set udg_BUSF_RemoveTimer[Node] = BUSFR_EffectRemove()
    set udg_BUSF_Remove[Node] = false
    call SetUnitScale(udg_BUSF_Unit[Node], udg_BUSF_CurrentSize[Node], 0.,0.)
    if UnitAddAbility(udg_BUSF_Unit[Node], 'Amrf') and UnitRemoveAbility(udg_BUSF_Unit[Node], 'Amrf') then
    endif
    call SetUnitFlyHeight(udg_BUSF_Unit[Node], z, 0.)
  
    if (udg_BUSF_PrevNode[Node] == 0) then
        call TimerStart(udg_BUSF_Timer, BUSFR_TimerSpeed(), true, function BUS_SheerForceLoop)
    endif
  
    call GroupEnumUnitsInRange(udg_BUSF_UnitGroup, x, y, TempReal, null)
  
    loop
        set u = FirstOfGroup(udg_BUSF_UnitGroup)
        exitwhen u == null
        call GroupRemoveUnit(udg_BUSF_UnitGroup, u)
      
        if (BUS_FTargetFilter(u, pl)) then
            set x2 = GetUnitX(u)
            set y2 = GetUnitY(u)
            set z2 = GetUnitFlyHeight(u) + BUS_GetZ(x2, y2)
            set TempReal5 = SquareRoot((x2 - x) * (x2 - x) + (y2 - y) * (y2 - y) + (z2 - z) * (z2 - z))
          
            if (TempReal5 < TempReal3) then
                set TempReal5 = TempReal3
            endif
          
            if (TempReal5 <= TempReal) then
                set TempReal6 = (TempReal2) / (TempReal5 / TempReal3)
                call UnitDamageTarget(udg_BUS_Unit, u, TempReal6 * TempReal3, false, false, BUSFR_AttackType(), BUSFR_DamageType(), BUSFR_WeaponType())
              
                if (BUSFR_DamageMana()) then
                    call SetUnitState(u, UNIT_STATE_MANA, GetUnitState(u, UNIT_STATE_MANA) - (TempReal6 * TempReal3))
                endif
              
                call BUS_StartKnockback(u, TempReal6, x2, x, y2, y, z2, z)
            endif
          
        endif
      
    endloop
  
    set pl = null
  
    return false
endfunction

////////////////////////////////////////////////////////////////////
//  SheerForceStart: Function used to set up the channeling for   //
//  the ability                                                   //
////////////////////////////////////////////////////////////////////
function BUS_SheerForceStart takes nothing returns boolean
    local unit u
  
    if (GetSpellAbilityId() == BUSFR_Ability()) then
        set u = GetTriggerUnit()
        call BUS_StartChannel(BUS_GetChannelByName(BUSFR_ChannelType()), u, null, 0., 0., BUSFR_ChannelDurationBase() + (GetUnitAbilityLevel(u, BUSFR_Ability()) * BUSFR_ChannelDurationPerLevel()), BUSFR_Event(), BUSFR_HaveHue())
        set u = null
    endif

    return false
endfunction

////////////////////////////////////////////////////////////////////
//  InitTrig BUS Sheer Force: Function used to set up the event   //
//  and starting functions of the ability as well as the timer    //
//  for the loop                                                  //
////////////////////////////////////////////////////////////////////
function InitTrig_BUS_Sheer_Force takes nothing returns nothing
    local trigger t = CreateTrigger()
    local integer index = 0
  
    loop
        call TriggerRegisterPlayerUnitEvent(t, Player(index),EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
        set index = index + 1
        exitwhen index >= bj_MAX_PLAYER_SLOTS
    endloop
  
    call TriggerAddCondition(t, Condition(function BUS_SheerForceStart))
  
    set t = CreateTrigger()
    call TriggerRegisterVariableEvent(t, "udg_BUSC_Event", EQUAL, BUSFR_Event())
    call TriggerAddCondition(t, Condition(function BUS_SheerForceEvent))
  
    set udg_BUSF_Timer = CreateTimer()
  
endfunction

////////////////////////////////////////////////////////////////////
//  End of the spell                                              //
////////////////////////////////////////////////////////////////////

The caster absorbs powerful magic lingering in the air and focuses it, fashioning it into an overwhelming blast, blowing enemies away from the caster dealing damage to enemies based on the amount of force pushing them


GIF
172730-albums6263-picture98402.gif
Boss Energy Spike

What is this? - Energy spike is an instant-cast high AOE damage ability with some strong effects to go with it being the most effect-intensive of the pack, after channeling the caster creates multiple giant swords to strike down its enemies, each sword has 3D collision and they slowly pan downward, strongly recommended to be used by flyers or large ground units as too many units being next to the centre of the swords causes lag (and lots and lots of damage) though lowering the amount of swords would help this.

JASS:
////////////////////////////////////////////////////////////////////
//           Boss Ultimate Spellpack Energy Spike V1.00           //
//  Author: Tank-Commander                                        //
//  Purpose: Boss Huge Damage AOE strike                          //
//  Requires: Dummy.mdl, BUS Channel                              //
//                                                                //
//  Notes:                                                        //
//    -  Read the readme before you try modifying the config      //
//    -  Use the "Helpful files" to help you import the system    //
//                                                                //
//  Credits:                                                      //
//    -  (Dummy.mdl) Vexorian                                     //
//    -  (DragonBlade.mdl) HappyTauren                            //
//                                                                //
//  Optimum User: Flyer (large)                                   //
//                                                                //
//  If you have used this spellpack 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       //
//----------------------------------------------------------------//
//  CONFIGURABLE INFORMATION/HELP:                                //
//                                                                //
//  - Viewing data values: To see data values in the editor you   //
//  need to press Ctrl + D, to shift back to normal viewing       //
//  press it again                                                //
//                                                                //
//  - Effects: Pathnames for effects used in the spells should    //
//  have two "\"s throughout or the effect will not work (the     //
//  WE progress bar will not go away when saving, however if      //
//  fixed afterwards the save will still work, but the progress   //
//  bar will still remain until the WE is closed)                 //
//  e.g. "units\\human\\Footman\\Footman"                         //
//                                                                //
//  - Effect Scaling: Some effects have scale values below them   //
//  the scale determines the size of the effect and is expressed  //
//  as a real percentage (1.00 = 100%)                            //
//                                                                //
//  - Removing Effects: to remove an effect you don't want from   //
//  the ability, set the model path to that of Dummy.mdl          //
//                                                                //
//  - Base and Per Values: Most configurables have a base and per //
//  value, Base values are what a value is set to regardless of   //
//  other factors. Per values are what a value is set to based on //
//  what the per value is the formula for calculating the result  //
//  is as follows:                                                //
//    - BaseValue + (Factor * PerValue)                           //
//                                                                //
//  - Timer: Some configurables have PerSecond values, the code   //
//  automatically accounts for changes to the timer as to         //
//  maintain consistency with what the user has chosen            //
//  All times in the system are expressions of seconds            //
//                                                                //
//  - AttackTypes: This should match the Damage Type of the       //
//  ability, though it can be something else if you wish          //
//                                                                //
//  - DamageTypes: This changes the damage multiplyer vs. enemy   //
//  armour types, note that by default the damage filters         //
//  exclude magic immune units so changing this from MAGIC        //
//  damage will not make them take damage                         //
//                                                                //
//  - WeaponTypes: Generally don't need to be used, should only   //
//  not be null if you particularly wish or need to use them      //
//                                                                //
//----------------------------------------------------------------//
//  TimerSpeed: This is the amount of time in seconds between     //
//  each iteration of the Energy Spike Loop function              //
constant function BUSER_TimerSpeed takes nothing returns real
    return 0.031250000
endfunction
//----------------------------------------------------------------//
//  Ability: This is the AbilityId that is used as the dummy      //
//  ability used for the Energy Spike spell                       //
constant function BUSER_Ability takes nothing returns integer
    return 'A001'
endfunction
//----------------------------------------------------------------//
//  DummyId: This is the UnitId that is used to create effects    //
//  It is recommended this unit use Dummy.mdl for its model for   //
//  optimal usage                                                 //
constant function BUSER_DummyId takes nothing returns integer
    return 'u000'
endfunction
//----------------------------------------------------------------//
//  Event: This is the value of BUS_Event that it will be set to  //
//  when the channeling ends, this should be unique to each       //
//  ability which uses the channel system                         //
constant function BUSER_Event takes nothing returns integer
    return 4
endfunction
//----------------------------------------------------------------//
//  Order: This is the order ID of the ability used to make sure  //
//  that the unit is still channeling the ability                 //
constant function BUSER_Order takes nothing returns integer
    return 852089
endfunction
//----------------------------------------------------------------//
//  AttachmentPoint: This is the point that effects are attached  //
//  to on units and dummy units                                   //
constant function BUSER_AttachmentPoint takes nothing returns string
    return "origin"
endfunction
//----------------------------------------------------------------//
//  HaveHue; This determines whether or not during channeling     //
//  the hue of the caster changes throughout                      //
constant function BUSER_HaveHue takes nothing returns boolean
    return true
endfunction
//----------------------------------------------------------------//
//  ChannelType: This is the name of the channel type used by     //
//  this ability (it should match the name of the channel type)   //
constant function BUSER_ChannelType takes nothing returns string
    return "burning fury"
endfunction
//----------------------------------------------------------------//
//  HealthDamageBase: This is the amount of damage the swords     //
//  will do to a unit per second                                  //
constant function BUSER_HealthDamageBase takes nothing returns real
    return 600.0
endfunction
//----------------------------------------------------------------//
//  HealthDamagePerLevel: This is the per level counterpart of    //
//  HealthDamageBase                                              //
constant function BUSER_HealthDamagePerLevel takes nothing returns real
    return 0.00
endfunction
//----------------------------------------------------------------//
//  ManaDamageBase: This is the amount of damage th swords will   //
//  to a unit's mana per second                                   //
constant function BUSER_ManaDamageBase takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  ManaDamagePerLevel: This is the per level counterpart of      //
//  ManaDamageBase                                                //
constant function BUSER_ManaDamagePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  ChannelDurationBase: This is the duration of the channeling   //
//  used by the spell                                             //
constant function BUSER_ChannelDurationBase takes nothing returns real
    return 3.
endfunction
//----------------------------------------------------------------//
//  ChannelDurationPerLevel: This is the per level counterpart of //
//  ChannelDurationBase                                           //
constant function BUSER_ChannelDurationPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  AOEBase: This is the Width of the swords used to damage units //
constant function BUSER_AOEBase takes nothing returns real
    return 125.
endfunction
//----------------------------------------------------------------//
//  AOEPerLevel: This is the per level counterpart of AOEBase     //
constant function BUSER_AOEPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  RangeBase: This is the length of the swords used to damage    //
//  units                                                         //
constant function BUSER_RangeBase takes nothing returns real
    return 500.
endfunction
//----------------------------------------------------------------//
//  RangePerLevel: This is the per level counterpart of RangeBase //
constant function BUSER_RangePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  OrbStartSizeBase: This is the starting size of the orb effect //
//  used around the swords                                        //
constant function BUSER_OrbStartSizeBase takes nothing returns real
    return 1.
endfunction
//----------------------------------------------------------------//
//  OrbStartSizePerLevel: This is the per level counterpart of    //
//  OrbStartSizeBase                                              //
constant function BUSER_OrbStartSizePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  OrbEndSizeBase: This is the ending size of the orb effect     //
//  used around the swords                                        //
constant function BUSER_OrbEndSizeBase takes nothing returns real
    return 5.
endfunction
//----------------------------------------------------------------//
//  OrbEndSizePerLevel: This is the per level counterpart of      //
//  OrbEndSizeBase                                                //
constant function BUSER_OrbEndSizePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  OrbTimeBase: This is the length of time it takes for the Orb  //
//  to become full sized                                          //
constant function BUSER_OrbTimeBase takes nothing returns real
    return 0.09
endfunction
//----------------------------------------------------------------//
//  OrbTimePerLevel: This is the per level counterpart of         //
//  OrbTimeBase                                                   //
constant function BUSER_OrbTimePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  LaserStartSizeBase: This is the starting size of the          //
//  swords/lasers                                                 //
constant function BUSER_LaserStartSizeBase takes nothing returns real
    return 0.01
endfunction
//----------------------------------------------------------------//
//  LaserStartSizePerLevel: This is the per level counterpart of  //
//  LaserStartSizeBase                                            //
constant function BUSER_LaserStartSizePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  LaserEndSizeBase: This is the ending size of the              //
//  swords/lasers                                                 //
constant function BUSER_LaserEndSizeBase takes nothing returns real
    return 7.
endfunction
//----------------------------------------------------------------//
//  LaserEndSizePerLevel: This is the per level counterpart of    //
//  LaserEndSizeBase                                              //
constant function BUSER_LaserEndSizePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  LaserTimeBase: This is how long it takes for the              //
//  swords/lasers to become full sized                            //
constant function BUSER_LaserTimeBase takes nothing returns real
    return 0.25
endfunction
//----------------------------------------------------------------//
//  LaserTimePerLevel: This is the per level counterpart of       //
//  LaserTimeBase                                                 //
constant function BUSER_LaserTimePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  LaserXYCountBase: This is the amount of laser sets that will  //
//  be made on the X and Y axis                                   //
constant function BUSER_LaserXYCountBase takes nothing returns integer
    return 8
endfunction
//----------------------------------------------------------------//
//  LaserXYCountPerLevel: This is the per level counterpart of    //
//  LaserXYCountBase                                              //
constant function BUSER_LaserXYCountPerLevel takes nothing returns integer
    return 0
endfunction
//----------------------------------------------------------------//
//  LaserZCountBase: This is the amount of lasers that are in a   //
//  single laser set                                              //
constant function BUSER_LaserZCountBase takes nothing returns integer
    return 5
endfunction
//----------------------------------------------------------------//
//  LaserZCountPerLevel: This is the per level counterpart of     //
//  LaserZCountBase                                               //
constant function BUSER_LaserZCountPerLevel takes nothing returns integer
    return 0
endfunction
//----------------------------------------------------------------//
//  LaserEdgeOffset: This is the ZAngle offset that the lasers    //
//  have (0 will mean one set points directly up and one down)    //
constant function BUSER_LaserEdgeOffset takes nothing returns real
    return 12.
endfunction
//----------------------------------------------------------------//
//  PanningTimeBase: This is the amount of time after becoming    //
//  full sized that the swords/lasers begin panning               //
constant function BUSER_PanningTimeBase takes nothing returns real
    return 1.
endfunction
//----------------------------------------------------------------//
//  PanningTimePerLevel: This is the per level counterpard of     //
//  PanningTimeBase                                               //
constant function BUSER_PanningTimePerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  PanningBase: This is how fast the swords/lasers pan downwards //
//  positive is a pan downward, negative is a pan upwards         //
constant function BUSER_PanningBase takes nothing returns real
    return 1.
endfunction
//----------------------------------------------------------------//
//  PanningPerLevel: This is the per level counterpard of         //
//  PanningBase                                                   //
constant function BUSER_PanningPerLevel takes nothing returns real
    return 0.
endfunction
//----------------------------------------------------------------//
//  OrbEffect: This is the filepath of the effect used to denote  //
//  the orb around the swords/lasers                              //
constant function BUSER_OrbEffect takes nothing returns string
    return "Abilities\\Spells\\Other\\ImmolationRed\\ImmolationRedTarget.mdl"
endfunction
//----------------------------------------------------------------//
//  DamageEffect: This is the filepath of the effect used to      //
//  denote damage being dealt to units by the swords/lasers       //
constant function BUSER_DamageEffect takes nothing returns string
    return "Abilities\\Spells\\Other\\Incinerate\\IncinerateBuff.mdl"
endfunction
//----------------------------------------------------------------//
//  LaserEffect: This is the filepath of the effect used to       //
//  denote the swords/lasers                                      //
constant function BUSER_LaserEffect takes nothing returns string
    return "war3mapImported\\DragonBladePlusDeathAndBirth.mdx"
endfunction
//----------------------------------------------------------------//
//  EndEffect: This is the filepath of the effect used to denote  //
//  the spell ending                                              //
constant function BUSER_EndEffect takes nothing returns string
    return "Abilities\\Spells\\Human\\Resurrect\\ResurrectTarget.mdl"
endfunction
//----------------------------------------------------------------//
//  EffectRemove: This is how long after an effect has been       //
//  completed that it is removed  
constant function BUSER_EffectRemove takes nothing returns real
    return 2.00
endfunction
//----------------------------------------------------------------//
//  AttackType: This is the attacktype used by the spell          //
constant function BUSER_AttackType takes nothing returns attacktype
    return ATTACK_TYPE_MAGIC
endfunction
//----------------------------------------------------------------//
//  DamageType: This is the damagetype used by the spell          //
constant function BUSER_DamageType takes nothing returns damagetype
    return DAMAGE_TYPE_MAGIC
endfunction
//----------------------------------------------------------------//
//  WeaponType: This is the weapontype used by the spell          //                           //
constant function BUSER_WeaponType takes nothing returns weapontype
    return null
endfunction
//----------------------------------------------------------------//
//  TargetFilter: This is the target filter for units that can    //
//  be turned into spirits, configure this as desired (requires   //
//  some coding knowledge, u is the target unit, Node is the      //
//  index of the casting unit                                     //
function BUS_ETargetFilter takes unit u, integer Node returns boolean
    return (not(IsUnitType(u, UNIT_TYPE_DEAD)) and not(IsUnitType(u, UNIT_TYPE_STRUCTURE)) and (IsPlayerEnemy(GetOwningPlayer(u), udg_BUSE_POwner[Node])))
endfunction
//----------------------------------------------------------------//
//                      END OF CONFIGURATION                      //
//----------------------------------------------------------------//
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////

//  Function used to prevent two casts of the same channel being  //
//  done simultaneously                                           //
////////////////////////////////////////////////////////////////////
function BUS_CheckEnergySpike takes unit u returns integer
    local integer Node = 0
  
    loop
        set Node = udg_BUSE_NextNode[Node]
        exitwhen (udg_BUSE_Unit[Node] == u)  or (Node == 0)
    endloop
  
    return Node
endfunction

////////////////////////////////////////////////////////////////////
//  EnergySpikeLoop: The main function, used to control the angle //
//  of the swords/lasers and damage units, as well as recycle     //
//  completed effects and make sure that the caster is still      //
//  channeling the ability                                        //
////////////////////////////////////////////////////////////////////
function BUS_EnergySpikeLoop takes nothing returns nothing
    local integer Node = 0
    local integer TempNode = 0
    local real TempReal
    local real x
    local real y
    local real z
    local real x2
    local real y2
    local real z2
    local unit TempUnit
  
    loop
        set Node = udg_BUSE_NextNode[Node]
        exitwhen Node == 0
      
        if not(GetUnitCurrentOrder(udg_BUSE_Unit[Node]) == BUSER_Order()) or (udg_BUSE_Cancel[Node]) then
            set TempNode = 0

            loop
                set TempNode = udg_BUSE_PNextNode[TempNode]
                exitwhen TempNode == 0
              
                if (udg_BUSE_PCaster[TempNode] == udg_BUSE_Unit[Node]) then
                    call DestroyEffect(udg_BUSE_PCurrentEffect[TempNode])
                    set udg_BUSE_PRemove[TempNode] = true
                  
                    if not(udg_BUSE_PDoesPan[TempNode]) then
                        call DestroyEffect(AddSpecialEffectTarget(BUSER_EndEffect(), udg_BUSE_PUnit[TempNode], BUSER_AttachmentPoint()))
                    endif
                  
                endif
              
            endloop
          
            set udg_BUSE_RecycleNodes[udg_BUSE_RecyclableNodes] = Node
            set udg_BUSE_RecyclableNodes = udg_BUSE_RecyclableNodes + 1
            set udg_BUSE_NextNode[udg_BUSE_PrevNode[Node]] = udg_BUSE_NextNode[Node]
            set udg_BUSE_PrevNode[udg_BUSE_NextNode[Node]] = udg_BUSE_PrevNode[Node]
          
            if (udg_BUSE_PPrevNode[0] + udg_BUSE_PrevNode[0] == 0) then
                call PauseTimer(udg_BUSE_Timer)
            endif
          
        endif
      
    endloop
  
    set Node = 0
  
    loop
        set Node = udg_BUSE_PNextNode[Node]
        exitwhen Node == 0
      
        if (udg_BUSE_PRemove[Node]) then
            if (udg_BUSE_PTimer[Node] <= 0) then
                call RemoveUnit(udg_BUSE_PUnit[Node])
              
                set udg_BUSE_PRecycleNodes[udg_BUSE_PRecyclableNodes] = Node
                set udg_BUSE_PRecyclableNodes = udg_BUSE_PRecyclableNodes + 1
                set udg_BUSE_PNextNode[udg_BUSE_PPrevNode[Node]] = udg_BUSE_PNextNode[Node]
                set udg_BUSE_PPrevNode[udg_BUSE_PNextNode[Node]] = udg_BUSE_PPrevNode[Node]
              
                if (udg_BUSE_PPrevNode[0] + udg_BUSE_PrevNode[0] == 0) then
                    call PauseTimer(udg_BUSE_Timer)
                endif
              
            else
                set udg_BUSE_PTimer[Node] = udg_BUSE_PTimer[Node] - BUSER_TimerSpeed()
            endif
        elseif (udg_BUSE_PGrowing[Node]) then
            set udg_BUSE_PCurrentSize[Node] = udg_BUSE_PCurrentSize[Node] + udg_BUSE_PScaleSpeed[Node]
            set udg_BUSE_PGrowthTime[Node] = udg_BUSE_PGrowthTime[Node] - BUSER_TimerSpeed()
            call SetUnitScale(udg_BUSE_PUnit[Node], udg_BUSE_PCurrentSize[Node], 0., 0.)
          
            if (udg_BUSE_PGrowthTime[Node] <= 0) then
                set udg_BUSE_PGrowing[Node] = false
            endif
      
        else
            if (udg_BUSE_PDoesPan[Node]) then
              
                if (udg_BUSE_PPanningTime[Node] <= 0) then
                    set udg_BUSE_PDZ[Node] = udg_BUSE_PDZ[Node] - udg_BUSE_PPan[Node]
                    call SetUnitAnimationByIndex(udg_BUSE_PUnit[Node], R2I(Atan2(udg_BUSE_PDZ[Node], SquareRoot((udg_BUSE_PDX[Node] * udg_BUSE_PDX[Node]) + (udg_BUSE_PDY[Node] * udg_BUSE_PDY[Node]))) * bj_RADTODEG + 0.5) + 90)
                else
                    set udg_BUSE_PPanningTime[Node] = udg_BUSE_PPanningTime[Node] - BUSER_TimerSpeed()
                endif
              
                set x = GetUnitX(udg_BUSE_PUnit[Node])
                set y = GetUnitY(udg_BUSE_PUnit[Node])
                set z = GetUnitFlyHeight(udg_BUSE_PUnit[Node]) + BUS_GetZ(x,y)
                set TempReal = 0
              
                loop
                    set x = x + udg_BUSE_PDX[Node]
                    set y = y + udg_BUSE_PDY[Node]
                    set z = z + udg_BUSE_PDZ[Node]
                    exitwhen (z < BUS_GetZ(x, y)) or (TempReal > udg_BUSE_PRange[Node])
                    call GroupEnumUnitsInRange(udg_BUSE_UnitGroup, x, y,udg_BUSE_PAOE[Node], null)
                    loop
                        set TempUnit = FirstOfGroup(udg_BUSE_UnitGroup)
                        exitwhen (TempUnit == null)
                        call GroupRemoveUnit(udg_BUSE_UnitGroup, TempUnit)
                      
                        set x2 = GetUnitX(TempUnit)
                        set y2 = GetUnitY(TempUnit)
                        set z2 = GetUnitFlyHeight(TempUnit) + BUS_GetZ(x2,y2)
                      
                        if (BUS_ETargetFilter(TempUnit, Node)) and (SquareRoot((x2 - x) * (x2 - x) + (y2 - y) * (y2 - y) + (z2 - z) * (z2 - z)) <= udg_BUSE_PAOE[Node]) then
                            call UnitDamageTarget(udg_BUSE_PCaster[Node], TempUnit, udg_BUSE_PHealthDamage[Node], false, false, BUSER_AttackType(), BUSER_DamageType(), BUSER_WeaponType())
                            call SetUnitState(TempUnit, UNIT_STATE_MANA, GetUnitState(TempUnit, UNIT_STATE_MANA) - udg_BUSE_PManaDamage[Node])
                            call DestroyEffect(AddSpecialEffectTarget(BUSER_DamageEffect(), TempUnit, BUSER_AttachmentPoint()))
                        endif
                      
                    endloop
                  
                    set TempReal = TempReal + udg_BUSE_PAOE[Node]
                endloop
              
            endif
          
        endif
      
    endloop
  
endfunction

////////////////////////////////////////////////////////////////////
//  EnergySpikeEvent: Function used to set up all the data and    //
//  effects that are used in the loop. Activates when the         //
//  channeling is completed                                       //
////////////////////////////////////////////////////////////////////
function BUS_EnergySpikeEvent takes nothing returns boolean
    local integer iLevel = GetUnitAbilityLevel(udg_BUS_Unit, BUSER_Ability())
    local real rLevel = I2R(iLevel)
    local integer Node = 0
    local integer TempNode = 0
    local integer iLoop = 0
    local integer iLoop2
    local integer TempInt
    local integer TempInt2
    local real x
    local real y
    local real z
    local real TempSin
    local real SinIncrement
    local real TempAngle = GetUnitFacing(udg_BUS_Unit)
    local real AngleIncrement
    local real TempAngle2
    local real TempAngle3
  
    if (udg_BUSE_RecyclableNodes == 0) then
        set udg_BUSE_NodeNumber = udg_BUSE_NodeNumber + 1
        set Node = udg_BUSE_NodeNumber
    else
        set udg_BUSE_RecyclableNodes = udg_BUSE_RecyclableNodes - 1
        set Node = udg_BUSE_RecycleNodes[udg_BUSE_RecyclableNodes]
    endif

    set udg_BUSE_NextNode[Node] = 0
    set udg_BUSE_NextNode[udg_BUSE_PrevNode[0]] = Node
    set udg_BUSE_PrevNode[Node] = udg_BUSE_PrevNode[0]
    set udg_BUSE_PrevNode[0] = Node
  
    set udg_BUSE_Unit[Node] = udg_BUS_Unit
    set udg_BUSE_Cancel[Node] = false
    set x = GetUnitX(udg_BUSE_Unit[Node])
    set y = GetUnitY(udg_BUSE_Unit[Node])
    set z = GetUnitFlyHeight(udg_BUSE_Unit[Node])
  
    if (udg_BUSE_PrevNode[Node] + udg_BUSE_PPrevNode[0] == 0) then
        call TimerStart(udg_BUSE_Timer, BUSER_TimerSpeed(), true, function BUS_EnergySpikeLoop)
    endif
  
    if (udg_BUSE_PRecyclableNodes == 0) then
        set udg_BUSE_PNodeNumber = udg_BUSE_PNodeNumber + 1
        set TempNode = udg_BUSE_PNodeNumber
    else
        set udg_BUSE_PRecyclableNodes = udg_BUSE_PRecyclableNodes - 1
        set TempNode = udg_BUSE_PRecycleNodes[udg_BUSE_PRecyclableNodes]
    endif

    set udg_BUSE_PNextNode[TempNode] = 0
    set udg_BUSE_PNextNode[udg_BUSE_PPrevNode[0]] = TempNode
    set udg_BUSE_PPrevNode[TempNode] = udg_BUSE_PPrevNode[0]
    set udg_BUSE_PPrevNode[0] = TempNode
  
    set udg_BUSE_PUnit[TempNode] = CreateUnit(BUSCR_DummyPlayer(), BUSER_DummyId(), x, y, 0.)
    set udg_BUSE_PCaster[TempNode] = udg_BUSE_Unit[Node]
    set udg_BUSE_PCurrentEffect[TempNode] = AddSpecialEffectTarget(BUSER_OrbEffect(), udg_BUSE_PUnit[TempNode], BUSER_AttachmentPoint())
    set udg_BUSE_PCurrentSize[TempNode] = BUSER_OrbStartSizeBase() + (rLevel * BUSER_OrbStartSizePerLevel())
    set udg_BUSE_PGrowthTime[TempNode] = BUSER_OrbTimeBase() + (rLevel * BUSER_OrbTimePerLevel())
    set udg_BUSE_PScaleSpeed[TempNode] = (((BUSER_OrbEndSizeBase() + (rLevel * BUSER_OrbEndSizePerLevel())) - udg_BUSE_PCurrentSize[TempNode]) / udg_BUSE_PGrowthTime[TempNode]) * BUSER_TimerSpeed()
    set udg_BUSE_PGrowing[TempNode] = true
    set udg_BUSE_PDoesPan[TempNode] = false
    set udg_BUSE_PTimer[TempNode] = BUSER_EffectRemove()
    set udg_BUSE_PRemove[TempNode] = false
    call SetUnitScale(udg_BUSE_PUnit[TempNode], udg_BUSE_PCurrentSize[TempNode], 0.,0.)
    if UnitAddAbility(udg_BUSE_PUnit[TempNode], 'Amrf') and UnitRemoveAbility(udg_BUSE_PUnit[TempNode], 'Amrf') then
    endif
    call SetUnitFlyHeight(udg_BUSE_PUnit[TempNode], z, 0.)
  
    set TempInt = BUSER_LaserXYCountBase() + (iLevel * BUSER_LaserXYCountPerLevel())
    set TempInt2 = BUSER_LaserZCountBase() + (iLevel * BUSER_LaserZCountPerLevel())
    set SinIncrement = (180 - (2 * BUSER_LaserEdgeOffset())) / TempInt2
    set AngleIncrement = 360 / TempInt
  
    loop
        set iLoop = iLoop + 1
        exitwhen iLoop > TempInt
        set iLoop2 = 0
        set TempAngle = TempAngle + AngleIncrement
        if (IsUnitType(udg_BUSE_Unit[Node], UNIT_TYPE_GROUND)) then
            set TempSin = BUSER_LaserEdgeOffset()
        else
            set TempSin = -90 + BUSER_LaserEdgeOffset()
        endif
      
        set TempAngle2 = bj_DEGTORAD * TempAngle
      
        loop
            set iLoop2 = iLoop2 + 1
            exitwhen iLoop2 > TempInt2
            set TempAngle3 = bj_DEGTORAD * TempSin
          
            if (udg_BUSE_PRecyclableNodes == 0) then
                set udg_BUSE_PNodeNumber = udg_BUSE_PNodeNumber + 1
                set TempNode = udg_BUSE_PNodeNumber
            else
                set udg_BUSE_PRecyclableNodes = udg_BUSE_PRecyclableNodes - 1
                set TempNode = udg_BUSE_PRecycleNodes[udg_BUSE_PRecyclableNodes]
            endif

            set udg_BUSE_PNextNode[TempNode] = 0
            set udg_BUSE_PNextNode[udg_BUSE_PPrevNode[0]] = TempNode
            set udg_BUSE_PPrevNode[TempNode] = udg_BUSE_PPrevNode[0]
            set udg_BUSE_PPrevNode[0] = TempNode

            set udg_BUSE_PAOE[TempNode] = BUSER_AOEBase() + (rLevel * BUSER_AOEPerLevel())
            set udg_BUSE_PUnit[TempNode] = CreateUnit(Player(14), BUSER_DummyId(), x, y, TempAngle)
            set udg_BUSE_PCaster[TempNode] = udg_BUSE_Unit[Node]
            set udg_BUSE_POwner[TempNode] = GetOwningPlayer(udg_BUS_Unit)
            set udg_BUSE_PHealthDamage[TempNode] = (BUSER_HealthDamageBase() + (rLevel * BUSER_HealthDamagePerLevel())) * BUSER_TimerSpeed()
            set udg_BUSE_PManaDamage[TempNode] = (BUSER_ManaDamageBase() + (rLevel * BUSER_ManaDamagePerLevel())) * BUSER_TimerSpeed()
            set udg_BUSE_PCurrentEffect[TempNode] = AddSpecialEffectTarget(BUSER_LaserEffect(), udg_BUSE_PUnit[TempNode], BUSER_AttachmentPoint())
            set udg_BUSE_PCurrentSize[TempNode] = BUSER_LaserStartSizeBase() + (rLevel * BUSER_LaserStartSizePerLevel())
            set udg_BUSE_PGrowthTime[TempNode] = BUSER_LaserTimeBase() + (rLevel * BUSER_LaserTimePerLevel())
            set udg_BUSE_PScaleSpeed[TempNode] = (((BUSER_LaserEndSizeBase() + (rLevel * BUSER_LaserEndSizePerLevel())) - udg_BUSE_PCurrentSize[TempNode]) / udg_BUSE_PGrowthTime[TempNode]) * BUSER_TimerSpeed()
            call SetUnitScale(udg_BUSE_PUnit[TempNode], udg_BUSE_PCurrentSize[TempNode], 0.,0.)
            set udg_BUSE_PDX[TempNode] = Cos(TempAngle2) * Cos(TempAngle3) * udg_BUSE_PAOE[TempNode]
            set udg_BUSE_PDY[TempNode] = Sin(TempAngle2) * Cos(TempAngle3) * udg_BUSE_PAOE[TempNode]
            set udg_BUSE_PDZ[TempNode] = Sin(TempAngle3) * udg_BUSE_PAOE[TempNode]
            set udg_BUSE_PPanningTime[TempNode] = BUSER_PanningTimeBase() + (rLevel * BUSER_PanningTimePerLevel())
            set udg_BUSE_PPan[TempNode] = BUSER_PanningBase() + (rLevel * BUSER_PanningPerLevel())
            set udg_BUSE_PRange[TempNode] = BUSER_RangeBase() + (rLevel * BUSER_RangePerLevel())
            set udg_BUSE_PGrowing[TempNode] = true
            set udg_BUSE_PDoesPan[TempNode] = true
            set udg_BUSE_PTimer[TempNode] = BUSER_EffectRemove()
            set udg_BUSE_PRemove[TempNode] = false
            if UnitAddAbility(udg_BUSE_PUnit[TempNode], 'Amrf') and UnitRemoveAbility(udg_BUSE_PUnit[TempNode], 'Amrf') then
            endif
            call SetUnitAnimationByIndex(udg_BUSE_PUnit[TempNode], R2I(Atan2(udg_BUSE_PDZ[TempNode], SquareRoot((udg_BUSE_PDX[TempNode] * udg_BUSE_PDX[TempNode]) + (udg_BUSE_PDY[TempNode] * udg_BUSE_PDY[TempNode]))) * bj_RADTODEG + 0.5) + 90)
            call SetUnitFlyHeight(udg_BUSE_PUnit[TempNode], z, 0.)
            set TempSin = TempSin + SinIncrement
        endloop
      
    endloop
  
    return false
endfunction

////////////////////////////////////////////////////////////////////
//  EnergySpikeStart: Function used to set up the channeling      //
//  for the ability                                               //
////////////////////////////////////////////////////////////////////
function BUS_EnergySpikeStart takes nothing returns boolean
    local unit u
  
    if (GetSpellAbilityId() == BUSER_Ability()) then
        set u = GetTriggerUnit()
        set udg_BUSE_Cancel[BUS_CheckEnergySpike(u)] = true
        call BUS_StartChannel(BUS_GetChannelByName(BUSER_ChannelType()), u, null, 0., 0., BUSER_ChannelDurationBase() + (GetUnitAbilityLevel(u, BUSER_Ability()) * BUSER_ChannelDurationPerLevel()), BUSER_Event(), BUSER_HaveHue())
        set u = null
    endif

    return false
endfunction

////////////////////////////////////////////////////////////////////
//  InitTrig BUS Energy Spike: Function used to set up the        //
//  event and starting functions of the ability as well as the    //
//  timer for the loop                                            //
////////////////////////////////////////////////////////////////////
function InitTrig_BUS_Energy_Spike takes nothing returns nothing
    local trigger t = CreateTrigger()
    local integer index = 0
  
    loop
        call TriggerRegisterPlayerUnitEvent(t, Player(index),EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
        set index = index + 1
        exitwhen index >= bj_MAX_PLAYER_SLOTS
    endloop
  
    call TriggerAddCondition(t, Condition(function BUS_EnergySpikeStart))
  
    set t = CreateTrigger()
    call TriggerRegisterVariableEvent(t, "udg_BUSC_Event", EQUAL, BUSER_Event())
    call TriggerAddCondition(t, Condition(function BUS_EnergySpikeEvent))
  
    set udg_BUSE_Timer = CreateTimer()
  
endfunction

////////////////////////////////////////////////////////////////////
//  End of the spell                                              //
////////////////////////////////////////////////////////////////////

The caster absorbs powerful magic lingering in the air and focuses it, fashioning it into 40 large burning blades, dealing 600 damage per second to enemies that touch them, Casters on the ground have 24 blades and increased damage


GIF
Energy%20Spike%20GIF%20updated.jpg

-=V1.02=-
- Channel made slightly more efficient
- Restructure of Linked list to reduce variable usage and up efficiency
-=No Version Change=-
- Changed the effect of swords in BUS Energy Spike to HappyTauren's awesome updated one with birth/death animations, making the spell appear much smoother
-=V1.01=-
- Updates to Boss Channel (V1.01b)
- Now prevents multiple channels at once (original cast is cancelled)
- Dummy player now a configurable
- Same changes made to abilities where applicable
- Increased speed of dummy removal (and efficiency of it)
- Updates to Boss Knockback
- Now stuns instead of pausing units
- Added option to prevent units being pushed into unpathable positions (errors may occur with knockbacks of unreasonable power)
- Slight efficiency improvements when handling knockbacks (removed use of Pow() functions)
- Uses a global rect for tree desruction instead of a local (V1.01b)
-=V1.00=-
- Initial Upload
[tr]

Keywords:
Boss, Ultimate, Channel, System, Spell, Pack, Lasers, Shock, Awe, Explosions, Fire, Holy, Light, Gravity, Knockback, Push, Force, Scathe, Swords of de[/tr]
Contents

BUS V1.02 (Map)

Reviews
Boss Ultimate Spellpack v1.0 | Reviewed by BPower | 15.06.2015 Concept[/COLOR]] BUS Channel helps to set up the mood for a real boss fight. It spices up the atmosphere until something really nasty happens. I can see this beeing used in a lot...

Moderator

M

Moderator


Boss Ultimate Spellpack v1.0 | Reviewed by BPower | 15.06.2015

[COLOR="gray"

[COLOR="gray"

[COLOR="gray"

[COLOR="gray"

[COLOR="gray"

[COLOR="gray"

Concept[/COLOR]]
126248-albums6177-picture66521.png
BUS Channel helps to set up the mood for a real boss fight.
It spices up the atmosphere until something really nasty happens.
I can see this beeing used in a lot of maps or taken as template for
creating an own, map-specific channel system.

BUS Knockback offers a nice push and drag system including
an optional 3D knockback toogle.

The spellpack is rounded up by 4 independant spells, which are
using BUS Channel and eventually BUS Knockback.
Code[/COLOR]]
126248-albums6177-picture66521.png
  • The code is MUI, leakless and working.
  • It is very well done.
126248-albums6177-picture66523.png
  • I didn't find anything wrong and I scanned it carefully.
  • Overall coding is very nice, read-able and appears quite profesional.
  • I couldn't ask for a better documentation.
Objects[/COLOR]]
126248-albums6177-picture66523.png
  • Object data is ok.
Effects[/COLOR]]
126248-albums6177-picture66521.png
  • Beautiful and very creative effects in all spells.
  • Personally I like "Boss Scathe" channeling effects the most.
Rating[/COLOR]]
CONCEPTCODEOBJECTSEFFECTSRATINGSTATUS
5/5
5/5
5/5
5/5
5/5
APPROVED
Extra[/COLOR]]
126248-albums6177-picture66524.png
If you are unhappy with the overall rating,
feel free to contact me directly.
DC were never given out in the Spell Section,
except in a few cases. ( DDS, Save&Load, ..)
I might kick off a discussion with the other moderators in charge.

[COLOR="gray"

[/TD]
 
Hi, I still have some notes/suggestions:

  • There should be a safety check to also abort the channeling if unit tries to channel the same ability again.
  • Knockback can push units also to unwanted positions, for example deep water. (they will stuck)
  • onKnockBack units are paused, maybe stunning or something similar would be superior here.
Others (minor important)
  • Some little performance boosts would be possible. For example Pow function does not need to be used, RAD could be used more often over DEG+additional converting.
  • To destroy trees there could be one global rect instead of local construction/destruction.
  • Is there any "HowToImport/Instructions" trigger, text?
Greetings.
 
Some minor code notices that might be considered next time:

  • For enum trees in rect, there can be used one global rect that is never destroyed.
    If there is need to know if function BUS_GetTrees takes real x, real y returns nothing
    does destroy and tree or not, then it should handle it internaly and just return a boolean true or false.
  • Owner of dummy units can be a constant function.
  • GetOwningPlayer(unit) -> GetTriggerPlayer()
    ^This works for all PlayerUnit events.

Honestly I have problems to understand the channeling structure. Why there are multiple instances for only one channeling unit?
When the unit cancels channeling, it gets registrated and the node will be recycled,
but at same time there are still active nodes for the PCount so the loop will continue a while before it stops.
I think some code documentation would be helpful here and there.

We have talked about it in chat, but now I have a more clear opinion on multiple channeling.
Multiple instances for channeling for the same unit should not be allowed.
As I'm not fully aware atm how the channeling is structured, I can't provide an easy solution atm,
but I feel like the old channeling instance should be interrupted and the new one should be the only valid.
 
Make sure you have
JASS:
function EnableScriptUsage takes nothing returns nothing
endfunction
in your map header or the system won't work like all JASS programs which have function calls outside of their own trigger, I also noticed that 4 variables were missing off the variable creators due to updates (which I have now rectified, though this applies only to the vanilla WE as JNGP would still create the missing ones) so copy and pasting now should work as intended
 
Holy shit, I never imagined my sword model would end up used as a spell effect :) Great job on the spell pack.

So here's an updated model that'll work better with your spell, I've added a birth and death animation and scaled the particles to the correct size to be used with the spell :)
 

Attachments

  • DragonBladePlusDeathAndBirth.mdx
    19.4 KB · Views: 61
Last edited:
Level 2
Joined
Sep 24, 2016
Messages
15
A few things I found:
Scathe pushes units through custom made map boundaries, or into them, getting these units stuck there.
The “PreventUnpathable” option fixes this, but causes units to Not be knocked back across raised or lowered terrain (which is walkable)
- why have the knockback if units are, more or less, just being knocked into the air on the spot? Maps with raised/lowered ground customizations all around (of which I can imagine there are many) would have no use of it.

Another thing that would benefit this resource: The option of choosing which unit types will Not be knocked back, but still be damaged by the spells.
Certain maps have special units/bosses that bug if knocked back in certain directions.

All the best!
 
feel free to made the necessary modifications yourself - the prevent unpathable option is expressly for stopping units from potentially landing in unpalatable locations, thus categorically must stop units traveling over cliffs generated by the raise/lower cliff tools as it's possible for a unit to land in one and become stuck - it should function find on terrain generated via the raise and lower tools that create hills rather than cliffs

Knocking units directly into the air above them is certainly an option, though not one that visually makes sense for the effect Scathe set out to create, to find the knockback call just use find "call BUS_StartKnockback" and it'll take you straight there; so that code can be easily switched out with any other knockback system if you'd rather use on of those (such as a knockup system rather than knockback), similarly adding a filter for the unit types should be quite simple in the same way (if my_filter(u) then call BUS_StartKnockback(...) endif) where myfilter takes either a unit or unittype of the affected unit. I won't be coding it into the spell by default however as it's not a feature that I could meaningfully be put in by default

Abilities with knockback are almost all pretty much abuse-able or can break maps not designed to account for them, hence why I personally think in most maps they're suited to be limited to enemy NPC units (bosses and the such) since they're much more easily limited and controlled then (e.g. putting an "out of bounds" death clause in a bossfight, so units flung out of the boss arena simply die if pushed out)

Thanks for the feedback
 
Level 2
Joined
Sep 24, 2016
Messages
15
Ok. I figured out a way without knowing anything about jass. Just through recognition of patterns.
If anyone could check it for bad coding I’d appreciate it.
Look in the “BUS Scathe” trigger, just above “END OF CONFIGURATION” and below the “call GroupEnumUnitsInRange” code.
I made the first loop Not call the knockback, and added a second loop after it that Does call the knockback for the chosen units, I think.

The spellpack deserves a wider audience (those out there who would like to have this option for their maps and don’t know jass)
which is why I’m making this effort.

Best regards
 

Attachments

  • BUS V1.02x.w3x
    397.9 KB · Views: 54
ok these are cool, no problems essentially, although a little thing i found, when on higher terrain and going up to higher, mountain style, the force field is not properly adjusting, as when im up at the top, the force is quite a way above my head, as i go down the forcefield makes its way back down, now if i keep going lower which i havent tried, but seems plausible going down further in terrain lvl, would cause the force field to spawn way underneath inside the ground lol i would imagine since its doing that going up probably be in reverse going down as coming back to main ground.

so im not sure where i would go to fix that or change or whatever, not so skilled in jass here, so what can be done about that?
a campaign map is no campaign map on straight flat land you know what im saying lol
 
Top