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

Divine Halo v2.1

Hey guys, this is one of my first JASS spells and i hope you guys will like it, comments and suggestions both positive and negative are welcome.

Divine Halo summons orbs that will begin to expand from the caster, stop at a distance and continue to rotate. After a short amount of the time the orbs will contract, damage all enemies that are vulnerable and heal all allies nearby.

This spell has many configurable values.

Create 1 timer array with size 1 called TimerUtils_timer
Create 1 integer called TimerUtils_int
Create 1 hashtable called TimerUtils_hash

Copy ALL of the following code into your maps header (the map icon above all triggers and folders)

NOTE: Do NOT give me credits for TimerUtils, i did not make it.
JASS:
//*************************************************
//*
//*   TimerUtils (Jass Version)
//*   v1.3.2.0
//*   By Magtheridon96
//*
//*   Original version by Vexorian.
//*   All the functions have O(1) complexity.
//*
//*   API:
//*       - function NewTimer takes nothing returns timer
//*           - Returns a new timer from the stack
//*       - function ReleaseTimer takes timer t returns nothing
//*           - Throws a timer back into the stack
//*       - function SetTimerData takes timer t, integer value returns nothing
//*           - Attaches a value to a timer
//*       - function GetTimerData takes timer t returns integer
//*           - Returns the attached value
//*
//*************************************************

    function SetTimerData takes timer t, integer value returns nothing
        call SaveInteger(udg_TimerUtils_hash,GetHandleId(t),0,value)
    endfunction

    function GetTimerData takes timer t returns integer
        return LoadInteger(udg_TimerUtils_hash,GetHandleId(t),0)
    endfunction

    function NewTimer takes nothing returns timer
        if 0==udg_TimerUtils_int then
            return CreateTimer()
        endif
        set udg_TimerUtils_int = udg_TimerUtils_int - 1
        return udg_TimerUtils_timer[udg_TimerUtils_int]
    endfunction

    function ReleaseTimer takes timer t returns nothing
        call PauseTimer(t)
        set udg_TimerUtils_timer[udg_TimerUtils_int] = t
        set udg_TimerUtils_int = udg_TimerUtils_int + 1
    endfunction

    function InitTrig_TimerUtils takes nothing returns nothing
        set udg_TimerUtils_hash = InitHashtable()
    endfunction
JASS:
//Divine Halo by Zaio
// ************************************* IMPORTING INSTRUCTIONS **********************************
// To import this spell, open up the test map and copy over the dummy, the halo ability, the holy bolt ability and buff
// Go to file then preferences and enable "automatically create unknown variables when pasting trigger data"
// Copy the code into your map
// Replace the ability and unit IDs below to your maps ability and unit ID
// Open up variables (Ctrl + B) and create 1 hashtable named DHash

function DHCDID takes nothing returns integer
    return 'h000' // Change the h000 to your dummy units ID (Hold ctrl and press D on the unit in object editor)
endfunction

function DHCAID takes nothing returns integer
    return 'A000' // Change the A000 to your spells ability ID (Hold ctrl and press D on the ability in object editor)
endfunction

// DONE!

// ****************************************** CONFIGURABLES **********************************************************

function DHDN takes nothing returns integer
    return 10 // This value is the amount of orbs in the halo.
endfunction

function DHFDUF takes nothing returns real
    return 100.00 // This value is the amount the spell will damage enemies for at the end of the spells duration.
endfunction

function DHFHUF takes nothing returns real
    return 100.00 // This value is the amount the spell will heal allies for at the end of the spells duration.
endfunction

function DHBE takes nothing returns string
    return "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl" // This is the holy bolt effect path, change this to alter the effect when the spell ends.
endfunction

function DHOS takes nothing returns real
    return 6.00 // This is the speed at which the orbs spin, supports positive and negative numbers (recommended to have on a value between -9.00 to 9.00)
endfunction

function DHDD takes nothing returns real
    return 300.00 // This is the max distance the orbs will travel over 1 second
endfunction

function DHDDI takes nothing returns real
    return 50.00 // This is the increment value per level (Example: if set to 50.00 and DHDD is set to 300, level 1 will travel 350, level 2 will travel 400 and level 3 will travel 450)
endfunction

function DHFD takes nothing returns real
    return 500.00 // This is the range units will be healed or damaged in when the spell ends
endfunction

function DHFDI takes nothing returns real
    return 50.00 // This is the range increment per level for the filter (Example: if set to 50.00 and DHFD is set to 500.00, level 1 will heal/damage units in 550 range whereas level 2 will do for 600 range and level 3 will do for 650 range)
endfunction

function DHLO takes nothing returns boolean
    return true // Lightning effects on/off (true = on, false = off)
endfunction

function DHDH takes nothing returns real
    return 200.00 // The orbs flying height
endfunction

function DHLH takes nothing returns real
    return 200.00 // The lightnings flying height (If DHLO = true)
endfunction

// ******************************** DO NOT EDIT BELOW THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING! *****************************

function DHFilter takes nothing returns boolean
    local unit fu = GetFilterUnit()
    local real x = GetUnitX(fu)
    local real y = GetUnitY(fu)
    if not IsUnitType(fu, UNIT_TYPE_STRUCTURE) and not IsUnitType(fu, UNIT_TYPE_DEAD) then
      if IsUnitEnemy(fu, GetOwningPlayer(udg_DHEC)) and not (GetUnitAbilityLevel(fu, 'Bams') > 0) and not (GetUnitAbilityLevel(fu, 'BHds') > 0) then
        call DestroyEffect(AddSpecialEffect(DHBE(), x, y))
        call UnitDamageTarget(udg_DHEC, fu, DHFDUF(), false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
      else
        if not IsUnitEnemy(fu, GetOwningPlayer(udg_DHEC)) then
          call DestroyEffect(AddSpecialEffect(DHBE(), x, y))
          call SetWidgetLife(fu, (GetWidgetLife(fu) + DHFHUF()))
        endif
      endif
    endif
    set fu = null
    return false   
endfunction

function DHLoop takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetHandleId(t)
    local unit u = LoadUnitHandle(udg_DHash, id, 8190)
    local integer c
    local real x
    local real y
    local real x2
    local real y2
    local integer li = 0
    local unit lu
    local unit lu2
    local real dl
    local real d
    local real rn
    local real dps
    local real dis
    local integer lvl
    local real x3
    local real y3
    local lightning ll
    if not IsUnitType(u, UNIT_TYPE_DEAD) then
      set x = GetUnitX(u)
      set y = GetUnitY(u)
      set c = LoadInteger(udg_DHash, id, 8191)
      set d = LoadReal(udg_DHash, id, 8192)
      set rn = (360. / (I2R(DHDN())))
      set dps = LoadReal(udg_DHash, id, 8188)
      set dis = LoadReal(udg_DHash, id, 8189)
      set lvl = LoadInteger(udg_DHash, id, 8187)
      set c = (c - 1)
      call SaveInteger(udg_DHash, id, 8191, c)
      if c > 330 then
        set d = (d + dps)
        call SaveReal(udg_DHash, id, 8192, d)
        loop
          set li = li + 1
          set dl = ((I2R(c) * DHOS()) + (rn * I2R(li)))
          set x2 = x + d * Cos((dl) * bj_DEGTORAD)
          set y2 = y + d * Sin((dl) * bj_DEGTORAD)
          set lu = LoadUnitHandle(udg_DHash, id, li)
          call SetUnitX(lu, x2)
          call SetUnitY(lu, y2)
          if DHLO() == true then
            set ll = LoadLightningHandle(udg_DHash, id, (DHDN() + li))
            if li == DHDN() then
              set lu2 = LoadUnitHandle(udg_DHash, id, 1)
              set x3 = GetUnitX(lu2)
              set y3 = GetUnitY(lu2)
            else
              set lu2 = LoadUnitHandle(udg_DHash, id, (li +1))
              set x3 = GetUnitX(lu2)
              set y3 = GetUnitY(lu2)
            endif
            call MoveLightningEx(ll, true, x2, y2, DHLH(), x3, y3, DHLH())
          endif
          exitwhen li == DHDN()
        endloop
      elseif c < 34 and c > 0 then
        set d = (d - dps)
        call SaveReal(udg_DHash, id, 8192, d)
        loop
          set li = li + 1
          set dl = ((I2R(c) * DHOS()) + (rn * I2R(li)))
          set x2 = x + d * Cos((dl) * bj_DEGTORAD)
          set y2 = y + d * Sin((dl) * bj_DEGTORAD)
          set lu = LoadUnitHandle(udg_DHash, id, li)
          call SetUnitX(lu, x2)
          call SetUnitY(lu, y2)
          if DHLO() == true then
            set ll = LoadLightningHandle(udg_DHash, id, (DHDN() + li))
            if li == DHDN() then
              set lu2 = LoadUnitHandle(udg_DHash, id, 1)
              set x3 = GetUnitX(lu2)
              set y3 = GetUnitY(lu2)
            else
              set lu2 = LoadUnitHandle(udg_DHash, id, (li +1))
              set x3 = GetUnitX(lu2)
              set y3 = GetUnitY(lu2)
            endif
          call MoveLightningEx(ll, true, x2, y2, DHLH(), x3, y3, DHLH())
          endif
          exitwhen li == DHDN()
        endloop
      elseif c <= 0 then
        loop
          set li = li + 1
          set lu = LoadUnitHandle(udg_DHash, id, li)
          if DHLO() == true then
            set ll = LoadLightningHandle(udg_DHash, id, (DHDN() + li))
            call DestroyLightning(ll)
          endif
          call KillUnit(lu)
          exitwhen li == DHDN()
        endloop
        call ReleaseTimer(t)
        call FlushChildHashtable(udg_DHash, id)
        set udg_DHEC = u
        call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, (DHFD() + (DHFDI() * (I2R(lvl)))), Filter(function DHFilter))
      else
        loop
          set li = li + 1
          set dl = ((I2R(c) * DHOS()) + (rn * I2R(li)))
          set x2 = x + dis * Cos((dl) * bj_DEGTORAD)
          set y2 = y + dis * Sin((dl) * bj_DEGTORAD)
          set lu = LoadUnitHandle(udg_DHash, id, li)
          call SetUnitX(lu, x2)
          call SetUnitY(lu, y2)
            if DHLO() == true then
              set ll = LoadLightningHandle(udg_DHash, id, (DHDN() + li))
              if li == DHDN() then
                set lu2 = LoadUnitHandle(udg_DHash, id, 1)
                set x3 = GetUnitX(lu2)
                set y3 = GetUnitY(lu2)
              else
                set lu2 = LoadUnitHandle(udg_DHash, id, (li +1))
                set x3 = GetUnitX(lu2)
                set y3 = GetUnitY(lu2)
            endif
            call MoveLightningEx(ll, true, x2, y2, DHLH(), x3, y3, DHLH())
          endif
          exitwhen li == DHDN()
        endloop  
      endif
    else
      loop
        set li = li + 1
        set lu = LoadUnitHandle(udg_DHash, id, li)
        if DHLO() == true then
          set ll = LoadLightningHandle(udg_DHash, id, (DHDN() + li))
          call DestroyLightning(ll)
        endif
        call KillUnit(lu)
        exitwhen li == DHDN()
      endloop
      call ReleaseTimer(t)
      call FlushChildHashtable(udg_DHash, id)
    endif
    set lu = null
    set lu2 = null
    set ll = null
    set u = null
    set t = null
endfunction

function DHAID takes nothing returns boolean
    local unit u
    local timer t
    local integer id
    local integer li
    local real x
    local real y
    local real x2
    local real y2
    local unit lu
    local unit lu2
    local real rn
    local integer lvl
    local real dis
    local real dps
    local lightning ll
    local real x3
    local real y3
    if GetSpellAbilityId() == DHCAID() then
      set u = GetTriggerUnit()
      set t = NewTimer()
      set id = GetHandleId(t)
      set li = 0
      set x = GetUnitX(u)
      set y = GetUnitY(u)
      set rn = (360. / (I2R(DHDN())))
      set lvl = GetUnitAbilityLevel(u, DHCAID())
      set dis = (DHDD() + (DHDDI() * I2R(lvl)))
      set dps = (dis / 33.)
      call SaveInteger(udg_DHash, id, 8187, lvl)
      call SaveReal(udg_DHash, id, 8188, dps)
      call SaveReal(udg_DHash, id, 8189, dis)
      call SaveUnitHandle(udg_DHash, id, 8190, u)
      call SaveInteger(udg_DHash, id, 8191, 363)
      loop
        set li = li + 1
        set x2 = x + Cos((rn * li) * bj_DEGTORAD)
        set y2 = y + Sin((rn * li) * bj_DEGTORAD)
        set lu = CreateUnit(GetTriggerPlayer(), DHCDID(), x2, y2, 0.)
        call SetUnitFlyHeight(lu, DHDH(), 0.)
        call SaveUnitHandle(udg_DHash, id, li, lu)
        exitwhen li == DHDN()
      endloop
      set li = 0
      loop
        set li = li + 1
        set lu = LoadUnitHandle(udg_DHash, id, li)
        set x2 = GetUnitX(lu)
        set y2 = GetUnitY(lu)
        if DHLO() == true then
          if li == DHDN() then
            set lu2 = LoadUnitHandle(udg_DHash, id, 1)
            set x3 = GetUnitX(lu)
            set y3 = GetUnitY(lu)
          else
            set lu2 = LoadUnitHandle(udg_DHash, id, (li + 1))
            set x3 = GetUnitX(lu)
            set y3 = GetUnitY(lu)
          endif
          set ll = AddLightningEx("HWPB", true, x2, y2, DHLH(), x3, y3, DHLH())
          call SaveLightningHandle(udg_DHash, id, (DHDN() + li), ll)
        endif
        exitwhen li == DHDN()
      endloop
      call TimerStart(t, 0.03, true, function DHLoop)
      set u = null
      set t = null
      set ll = null
      set lu = null
      set lu2 = null
    endif
    return false
endfunction

function InitTrig_Divine_Halo takes nothing returns nothing
    local trigger t = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function DHAID ) )
    set udg_DHash = InitHashtable()
    set t = null
endfunction

Coding by me, Zaio
Basic idea from Skamigo's paladin in 'Skamigo's Hero Defense'


Changelog

2.1 - Efficiency update.
2.0 - Using TimerUtils
1.9 - Sorry for the extreme indenting fail, should be fixed and easily readable now.
1.8 - Minor code change, 2 new configurables for dummy and lightning height.
1.7 - Fixed a minor leak, correctly nulling locals (Thanks Deuterium!)
1.6 - Fixed what Pharaoh_ mentioned aswell as some other cody efficiency updates.
1.5 - Reinvented code quite a bit and added a configurable for lightning between each orb.
1.4 - Removed trigger action, added 4 more configurables, multiple level support and now the dummies can have sight radius back if wanted (fixed the previous error).
1.3 - Fixed everything Pharaoh_ mentioned as well as adding a configurable for orb speed.
1.2 - Removed one hashtable, made code more presentable.
1.1 - Updated importing instructions, changed heal to a set amount and changed checking of unit life to checking if unit is dead.

Keywords:
divine, halo, holy, heal, damage, mui, jass, orbs, pheonix, fire, bolt, lightning, orb, circle, radius, sphere, rotate.
Contents

Just another Warcraft III map (Map)

Reviews
18:32, 4th Sep 2011 Pharaoh_: Rename your variables into something readable. It's not GUI after all, placing the acronyms of your spell as a prefix on them. New review: http://www.hiveworkshop.com/forums/1999942-post9.html Approved; nice effects.
Level 38
Joined
Feb 27, 2007
Messages
4,951
There is an error with the height calculation for the lightning effects in this spell. MoveLightningEx() expects absolute Z height as its height arguments, which need to take the terrain height at the location of the lightning into account. This spell does not do that, and the creator never noticed because they tested the spell on a map with flat terrain that wasn't raised. On raised, uneven, or cliff level > 0 terrain the lightning effects will appear at an incorrect height causing a lot of visual confusion because lightning effects below ground are not hidden. I made a minor edit to this spell using a simple GetLocalZ library I wrote to fix this error. Paste the LocalZ library into your map's Custom Script section where the TimerUtils code is.
JASS:
library LocalZ
  //Can be substituted with any other library that does the same thing
  //or a Blz native if Blizzard adds something like that

  globals
    private location l = Location(0.,0.)
  endglobals

  function GetLocalZ takes real x, real y returns real
    call MoveLocation(l, x, y)
    return GetLocationZ(l)
  endfunction
endlibrary
JASS:
// Divine Halo by Zaio, minor edit for lightning clarity by Pyrogasm
// ************************************* IMPORTING INSTRUCTIONS **********************************
// To import this spell, open up the test map and copy over the dummy, the halo ability, the holy bolt ability and buff
// Go to file then preferences and enable "automatically create unknown variables when pasting trigger data"
// Copy the code into your map
// Replace the ability and unit IDs below to your maps ability and unit ID
// Open up variables (Ctrl + B) and create 1 hashtable named DHash

function DHCDID takes nothing returns integer
    return 'h000' // Change the h000 to your dummy units ID (Hold ctrl and press D on the unit in object editor)
endfunction

function DHCAID takes nothing returns integer
    return 'A000' // Change the A000 to your spells ability ID (Hold ctrl and press D on the ability in object editor)
endfunction

// DONE!

// ****************************************** CONFIGURABLES **********************************************************

function DHDN takes nothing returns integer
    return 10 // This value is the amount of orbs in the halo.
endfunction

function DHFDUF takes nothing returns real
    return 100.00 // This value is the amount the spell will damage enemies for at the end of the spells duration.
endfunction

function DHFHUF takes nothing returns real
    return 100.00 // This value is the amount the spell will heal allies for at the end of the spells duration.
endfunction

function DHBE takes nothing returns string
    return "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl" // This is the holy bolt effect path, change this to alter the effect when the spell ends.
endfunction

function DHOS takes nothing returns real
    return 6.00 // This is the speed at which the orbs spin, supports positive and negative numbers (recommended to have on a value between -9.00 to 9.00)
endfunction

function DHDD takes nothing returns real
    return 300.00 // This is the max distance the orbs will travel over 1 second
endfunction

function DHDDI takes nothing returns real
    return 50.00 // This is the increment value per level (Example: if set to 50.00 and DHDD is set to 300, level 1 will travel 350, level 2 will travel 400 and level 3 will travel 450)
endfunction

function DHFD takes nothing returns real
    return 500.00 // This is the range units will be healed or damaged in when the spell ends
endfunction

function DHFDI takes nothing returns real
    return 50.00 // This is the range increment per level for the filter (Example: if set to 50.00 and DHFD is set to 500.00, level 1 will heal/damage units in 550 range whereas level 2 will do for 600 range and level 3 will do for 650 range)
endfunction

function DHLO takes nothing returns boolean
    return true // Lightning effects on/off (true = on, false = off)
endfunction

function DHDH takes nothing returns real
    return 200.00 // The orbs flying height
endfunction

function DHLH takes nothing returns real
    return 200.00 // The lightnings flying height (If DHLO = true)
endfunction

// ******************************** DO NOT EDIT BELOW THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING! *****************************

function DHFilter takes nothing returns boolean
    local unit fu = GetFilterUnit()
    local real x = GetUnitX(fu)
    local real y = GetUnitY(fu)
    if not IsUnitType(fu, UNIT_TYPE_STRUCTURE) and not IsUnitType(fu, UNIT_TYPE_DEAD) then
      if IsUnitEnemy(fu, GetOwningPlayer(udg_DHEC)) and not (GetUnitAbilityLevel(fu, 'Bams') > 0) and not (GetUnitAbilityLevel(fu, 'BHds') > 0) then
        call DestroyEffect(AddSpecialEffect(DHBE(), x, y))
        call UnitDamageTarget(udg_DHEC, fu, DHFDUF(), false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
      else
        if not IsUnitEnemy(fu, GetOwningPlayer(udg_DHEC)) then
          call DestroyEffect(AddSpecialEffect(DHBE(), x, y))
          call SetWidgetLife(fu, (GetWidgetLife(fu) + DHFHUF()))
        endif
      endif
    endif
    set fu = null
    return false
endfunction

function DHLoop takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetHandleId(t)
    local unit u = LoadUnitHandle(udg_DHash, id, 8190)
    local integer c
    local real x
    local real y
    local real x2
    local real y2
    local integer li = 0
    local unit lu
    local unit lu2
    local real dl
    local real d
    local real rn
    local real dps
    local real dis
    local integer lvl
    local real x3
    local real y3
    local lightning ll

    if not IsUnitType(u, UNIT_TYPE_DEAD) then
      set x = GetUnitX(u)
      set y = GetUnitY(u)
      set c = LoadInteger(udg_DHash, id, 8191)
      set d = LoadReal(udg_DHash, id, 8192)
      set rn = (360. / (I2R(DHDN())))
      set dps = LoadReal(udg_DHash, id, 8188)
      set dis = LoadReal(udg_DHash, id, 8189)
      set lvl = LoadInteger(udg_DHash, id, 8187)
      set c = (c - 1)
      call SaveInteger(udg_DHash, id, 8191, c)
      if c > 330 then
        set d = (d + dps)
        call SaveReal(udg_DHash, id, 8192, d)
        loop
          set li = li + 1
          set dl = ((I2R(c) * DHOS()) + (rn * I2R(li)))
          set x2 = x + d * Cos((dl) * bj_DEGTORAD)
          set y2 = y + d * Sin((dl) * bj_DEGTORAD)
          set lu = LoadUnitHandle(udg_DHash, id, li)
          call SetUnitX(lu, x2)
          call SetUnitY(lu, y2)
          if DHLO() == true then
            set ll = LoadLightningHandle(udg_DHash, id, (DHDN() + li))
            if li == DHDN() then
              set lu2 = LoadUnitHandle(udg_DHash, id, 1)
              set x3 = GetUnitX(lu2)
              set y3 = GetUnitY(lu2)
            else
              set lu2 = LoadUnitHandle(udg_DHash, id, (li +1))
              set x3 = GetUnitX(lu2)
              set y3 = GetUnitY(lu2)
            endif
            call MoveLightningEx(ll, true, x2, y2, GetLocalZ(x2,y2)+DHLH(), x3, y3, GetLocalZ(x3,y3)+DHLH())
          endif
          exitwhen li == DHDN()
        endloop
      elseif c < 34 and c > 0 then
        set d = (d - dps)
        call SaveReal(udg_DHash, id, 8192, d)
        loop
          set li = li + 1
          set dl = ((I2R(c) * DHOS()) + (rn * I2R(li)))
          set x2 = x + d * Cos((dl) * bj_DEGTORAD)
          set y2 = y + d * Sin((dl) * bj_DEGTORAD)
          set lu = LoadUnitHandle(udg_DHash, id, li)
          call SetUnitX(lu, x2)
          call SetUnitY(lu, y2)
          if DHLO() == true then
            set ll = LoadLightningHandle(udg_DHash, id, (DHDN() + li))
            if li == DHDN() then
              set lu2 = LoadUnitHandle(udg_DHash, id, 1)
              set x3 = GetUnitX(lu2)
              set y3 = GetUnitY(lu2)
            else
              set lu2 = LoadUnitHandle(udg_DHash, id, (li +1))
              set x3 = GetUnitX(lu2)
              set y3 = GetUnitY(lu2)
            endif
          call MoveLightningEx(ll, true, x2, y2, GetLocalZ(x2,y2)+DHLH(), x3, y3, GetLocalZ(x3,y3)+DHLH())
          endif
          exitwhen li == DHDN()
        endloop
      elseif c <= 0 then
        loop
          set li = li + 1
          set lu = LoadUnitHandle(udg_DHash, id, li)
          if DHLO() == true then
            set ll = LoadLightningHandle(udg_DHash, id, (DHDN() + li))
            call DestroyLightning(ll)
          endif
          call KillUnit(lu)
          exitwhen li == DHDN()
        endloop
        call ReleaseTimer(t)
        call FlushChildHashtable(udg_DHash, id)
        set udg_DHEC = u
        call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, (DHFD() + (DHFDI() * (I2R(lvl)))), Filter(function DHFilter))
      else
        loop
          set li = li + 1
          set dl = ((I2R(c) * DHOS()) + (rn * I2R(li)))
          set x2 = x + dis * Cos((dl) * bj_DEGTORAD)
          set y2 = y + dis * Sin((dl) * bj_DEGTORAD)
          set lu = LoadUnitHandle(udg_DHash, id, li)
          call SetUnitX(lu, x2)
          call SetUnitY(lu, y2)
            if DHLO() == true then
              set ll = LoadLightningHandle(udg_DHash, id, (DHDN() + li))
              if li == DHDN() then
                set lu2 = LoadUnitHandle(udg_DHash, id, 1)
                set x3 = GetUnitX(lu2)
                set y3 = GetUnitY(lu2)
              else
                set lu2 = LoadUnitHandle(udg_DHash, id, (li +1))
                set x3 = GetUnitX(lu2)
                set y3 = GetUnitY(lu2)
            endif
            call MoveLightningEx(ll, true, x2, y2, GetLocalZ(x2,y2)+DHLH(), x3, y3, GetLocalZ(x3,y3)+DHLH())
          endif
          exitwhen li == DHDN()
        endloop
      endif
    else
      loop
        set li = li + 1
        set lu = LoadUnitHandle(udg_DHash, id, li)
        if DHLO() == true then
          set ll = LoadLightningHandle(udg_DHash, id, (DHDN() + li))
          call DestroyLightning(ll)
        endif
        call KillUnit(lu)
        exitwhen li == DHDN()
      endloop
      call ReleaseTimer(t)
      call FlushChildHashtable(udg_DHash, id)
    endif
    set lu = null
    set lu2 = null
    set ll = null
    set u = null
    set t = null
endfunction

function DHAID takes nothing returns boolean
    local unit u
    local timer t
    local integer id
    local integer li
    local real x
    local real y
    local real x2
    local real y2
    local unit lu
    local unit lu2
    local real rn
    local integer lvl
    local real dis
    local real dps
    local lightning ll
    local real x3
    local real y3
    if GetSpellAbilityId() == DHCAID() then
      set u = GetTriggerUnit()
      set t = NewTimer()
      set id = GetHandleId(t)
      set li = 0
      set x = GetUnitX(u)
      set y = GetUnitY(u)
      set rn = (360. / (I2R(DHDN())))
      set lvl = GetUnitAbilityLevel(u, DHCAID())
      set dis = (DHDD() + (DHDDI() * I2R(lvl)))
      set dps = (dis / 33.)
      call SaveInteger(udg_DHash, id, 8187, lvl)
      call SaveReal(udg_DHash, id, 8188, dps)
      call SaveReal(udg_DHash, id, 8189, dis)
      call SaveUnitHandle(udg_DHash, id, 8190, u)
      call SaveInteger(udg_DHash, id, 8191, 363)
      loop
        set li = li + 1
        set x2 = x + Cos((rn * li) * bj_DEGTORAD)
        set y2 = y + Sin((rn * li) * bj_DEGTORAD)
        set lu = CreateUnit(GetTriggerPlayer(), DHCDID(), x2, y2, 0.)
        call SetUnitFlyHeight(lu, DHDH(), 0.)
        call SaveUnitHandle(udg_DHash, id, li, lu)
        exitwhen li == DHDN()
      endloop
      set li = 0
      loop
        set li = li + 1
        set lu = LoadUnitHandle(udg_DHash, id, li)
        set x2 = GetUnitX(lu)
        set y2 = GetUnitY(lu)
        if DHLO() == true then
          if li == DHDN() then
            set lu2 = LoadUnitHandle(udg_DHash, id, 1)
            set x3 = GetUnitX(lu)
            set y3 = GetUnitY(lu)
          else
            set lu2 = LoadUnitHandle(udg_DHash, id, (li + 1))
            set x3 = GetUnitX(lu)
            set y3 = GetUnitY(lu)
          endif
          set ll = AddLightningEx("HWPB", true, x2, y2, GetLocalZ(x2,y2)+DHLH(), x3, y3, GetLocalZ(x3,y3)+DHLH())
          call SaveLightningHandle(udg_DHash, id, (DHDN() + li), ll)
        endif
        exitwhen li == DHDN()
      endloop
      call TimerStart(t, 0.03, true, function DHLoop)
      set u = null
      set t = null
      set ll = null
      set lu = null
      set lu2 = null
    endif
    return false
endfunction

function InitTrig_Divine_Halo takes nothing returns nothing
    local trigger t = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function DHAID ) )
    set udg_DHash = InitHashtable()
    set t = null
endfunction
However, this is not the only thing 'wrong' with the spell. It has additional deficiencies (as a result of it being old vanilla JASS from the JESP era) that are worth updating by someone else who cares:
  • The dummy unit does not have proper cast backswing/cast points to facilitate instant casting.
  • It should be encapsulated in a library or scope, removing the InitTrig and giving it an initializer
  • TimerUtils should be updated to a library version to remove the dependence on manually created global variables
  • It is not particularly optimized, and often calls functions multiple times for the same output
  • It uses checks like UNIT_TYPE_DEAD, hardcoded checks for magic immunity, etc.
  • It does not aggro creeps properly (they run from the damage source even if they are ranged and can attack it)
  • It could use a struct instead of a bunch of reading from a hashtable
But at that point you might as well rewrite the spell entirely, which is why I didn't do it.
 
Last edited:
Top