• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Lightning Bomb v 1.0.4

This is a spell that makes a semi circular Bomb made from lightning shooting out of the caster. It is an point target, instant damage type of spell but the lightning effect lasts for a little bit of time. I have it set to 5 seconds. Radius is set to 350.

You can change the lightning that u want to be displayed for the effect.

ALL OF MY SPELLS ARE GUI FRIENDLY

HOW TO IMPORT
I try to make my spells easy to import for both GUIers and Jassers.
Step 1) Copy the config copy trigger into your map.
Step 2) After copying that trigger u can rename it and get rid of the copy.
Step 3) Copy the cast trigger into your map.
Step 4) Go back to the config trigger and delete the variables after the line that says delete everything after this.

note: Make sure that the trigger name for the Jass is the same as the trigger name in the map.


  • LightningBomb Config
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- These are the base values that u can set. --------
      • -------- Here u set the max time for it to end. For every second u need 32. So 5 seconds is 160. --------
      • Set lightningBombTimeBase = 160
      • -------- --------
      • -------- This is the damage base. Every interval --------
      • Set lightningBombDamageBase = 500.00
      • -------- --------
      • -------- This is the radius base --------
      • Set lightningBombRadiusBase = 350.00
      • -------- --------
      • -------- ---------------------------------------------------------------------------------------------------- --------
      • -------- These are the values that get added to the base per lvl that u can set. --------
      • -------- Here u set the max time for it to end per lvl. For every second u need 32. So 5 seconds is 160. --------
      • Set lightningBombTimePerLvl = 0
      • -------- --------
      • -------- This is the damage per lvl. Every interval --------
      • Set lightningBombDamagePerLvl = 0.00
      • -------- --------
      • -------- This is the radius per lvl --------
      • Set lightningBombRadiusPerLvl = 0.00
      • -------- --------
      • -------- ---------------------------------------------------------------------------------------------------- --------
      • -------- These are the other values that u can change --------
      • -------- set the lightning that gets created when the lightning bomb gets casted --------
      • Set lightningBombLightningType = Magic Leash
      • -------- --------
      • -------- set the lightning that gets created from the caster to the target point when the lightning bomb gets casted --------
      • Set lightningBombLightningType2 = Finger of Death
      • -------- --------
      • -------- here u can change the attack type --------
      • Set lightningBombAttackType = Chaos
      • -------- --------
      • -------- here u can change the damage type --------
      • Set lightningBombDamageType = Normal
      • -------- --------
      • -------- Set the spell id here --------
      • Custom script: set udg_lightningBombSpellId = 'A000'
      • -------- --------
      • -------- You can change the time for the loop. Normally u cant change this. In this case it doesnt really matter. Just remember when u change this u need to change the time for the spell. --------
      • Custom script: set udg_lightningBombLoopTime = 0.031250000
      • -------- --------
      • -------- These values set the semi sphere shape for the lightning. I left them in custom script for accuracy. Dont change the array index. only change the real. --------
      • -------- Each variable is for the distance of the lightning. u can make it cylindrical, cone shaped. and a few others if u mess with it. --------
      • Custom script: set udg_lightningBombCircleReal[ 16] = 0.96592
      • Custom script: set udg_lightningBombCircleReal[ 31] = 0.86599
      • Custom script: set udg_lightningBombCircleReal[ 46] = 0.70698
      • Custom script: set udg_lightningBombCircleReal[ 61] = 0.50000
      • Custom script: set udg_lightningBombCircleReal[ 76] = 0.25869
      • -------- --------
      • -------- This is what you use to filter out the units that can take damage and get hit by the spell --------
      • Custom script: endfunction
      • Custom script: function LightningBombFilterUnits takes unit u, unit c returns boolean
      • Custom script: set udg_tempTarget = u
      • Custom script: set udg_tempCaster = c
      • -------- Use tempTarget as the targeted unit --------
      • -------- Use tempCaster as the casting unit --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
        • Then - Actions
          • Custom script: return true
        • Else - Actions
      • Custom script: return false
JASS:
// Lightning Bomb made by deathismyfriend
// version 1.0.4

function PolarProjectionXLightningBomb takes real x, real dist, real angle returns real // returns x
    return x + dist * Cos( angle)
endfunction

function PolarProjectionYLightningBomb takes real y, real dist, real angle returns real // returns y
    return y + dist * Sin( angle)
endfunction

function PolarProjectionZLightningBomb takes real y, real dist, real angle returns real // returns z
    return y + dist * Tan( angle)
endfunction


function LightningBombDamageLoop takes nothing returns nothing
    local integer L = 1
    local integer m = udg_lightningBombMaxIndex
    loop
        exitwhen L > m
        set udg_lightningBombTimeCounter[ L] = udg_lightningBombTimeCounter[ L] + 1
        if udg_lightningBombTimeCounter[ L] >= udg_lightningBombTime[ L] then
            if udg_lightningBombCaster[ L] != null then
                call SetUnitPropWindow( udg_lightningBombCaster[ L], udg_lightningBombPropWindow[ L])
            endif
            call DestroyLightning( udg_lightningBombLightning[ L])
            set udg_lightningBombCaster[ L] = udg_lightningBombCaster[ m]
            set udg_lightningBombCaster[ m] = null
            set udg_lightningBombTimeCounter[ L] = udg_lightningBombTimeCounter[ m]
            set udg_lightningBombPropWindow[ L] = udg_lightningBombPropWindow[ m]
            set udg_lightningBombLightning[ L] = udg_lightningBombLightning[ m]
            set udg_lightningBombLightning[ m] = null
            set udg_lightningBombTime[ L] = udg_lightningBombTime[ m]
            set m = m - 1
            set L = L - 1
        endif
        set L = L + 1
    endloop
    set udg_lightningBombMaxIndex = m
endfunction

function LightningBombSetup takes nothing returns nothing
    local unit c = GetTriggerUnit()
    local unit u
    local integer abiLvl = GetUnitAbilityLevel( c, udg_lightningBombSpellId)
    local integer lgth = udg_lightningBombTimeBase - ( abiLvl * udg_lightningBombTimePerLvl)
    local real r = udg_lightningBombRadiusBase - ( abiLvl * udg_lightningBombRadiusPerLvl)
    local real dam = udg_lightningBombDamageBase - ( abiLvl * udg_lightningBombDamagePerLvl)
    local real x = GetSpellTargetX()
    local real cx = GetUnitX( c)
    local real y = GetSpellTargetY()
    local real cy = GetUnitY( c)
    local real z
    local real x1
    local real y1
    local real z1
    local real L1 = 22.5
    local integer L = 1
    local real d = r
    local integer m = udg_lightningBombMaxIndex + 1
    call MoveLocation( udg_lightningBombLocation, x, y)
    set z = GetLocationZ( udg_lightningBombLocation) + 50.00
    set udg_lightningBombCaster[ m] = c
    set udg_lightningBombTimeCounter[ m] = 0
    set udg_lightningBombTime[ m] = lgth
    set udg_lightningBombLightning[ m] = AddLightning( udg_lightningBombLightningType2, true, cx, cy, x, y)
    set udg_lightningBombPropWindow[ m] = GetUnitPropWindow( c)
    if m == 1 then
        call TimerStart( udg_lightningBombTimer, udg_lightningBombLoopTime, true, function LightningBombDamageLoop)
    endif
    set udg_lightningBombTime[ m] = lgth
    loop
        exitwhen L > 90
        set z1 = PolarProjectionZLightningBomb( z, d, L)
        loop
            exitwhen L1 >= 360
            set x1 = PolarProjectionXLightningBomb( x, d, L1)
            set y1 = PolarProjectionYLightningBomb( y, d, L1)
            set m = m + 1
            set udg_lightningBombTimeCounter[ m] = 0
            set udg_lightningBombTime[ m] = lgth
            set udg_lightningBombLightning[ m] = AddLightningEx( udg_lightningBombLightningType, true, x1, y1, z1, x, y, z)
            set L1 = L1 + 45
        endloop
        if L1 >= 380 then // this rotates the lightning so its not straight up at every 45 degree angle.
            set L1 = 0
        else // 360
            set L1 = 22.5
        endif
        set L = L + 15
        set d = udg_lightningBombCircleReal[ L]*r // reduces the length but not equally.
    endloop
    

    call GroupEnumUnitsInRange( udg_lightningBombGroup , x, y, r, null)
    
    loop
        set u = FirstOfGroup( udg_lightningBombGroup )
        exitwhen u == null
        if u != c and LightningBombFilterUnits( u, c) and not IsUnitType( u, UNIT_TYPE_DEAD) and GetUnitTypeId( u) != 0 then
            call UnitDamageTarget( c, u, dam, false, false, udg_lightningBombAttackType, udg_lightningBombDamageType, null)
        endif
        call GroupRemoveUnit( udg_lightningBombGroup , u)
    endloop
    set udg_lightningBombMaxIndex = m
    set c = null
endfunction

function LightningBombCreateLocation takes nothing returns nothing
    set udg_lightningBombLocation = Location( 0, 0)
endfunction

function LightningBombCastConditions takes nothing returns boolean
    if GetSpellAbilityId() == udg_lightningBombSpellId then
        call LightningBombSetup()
    endif
    return false
endfunction

//===========================================================================
function InitTrig_Lightning_Bomb_Cast takes nothing returns nothing
    local trigger t = CreateTrigger()
    set udg_lightningBombTimer = CreateTimer()
    call LightningBombCreateLocation()
    set udg_lightningBombGroup = CreateGroup()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_CAST)
    call TriggerAddCondition( t, Condition( function LightningBombCastConditions))
    set t = null
endfunction

JASS:
library LightningBomb

// Lightning Bomb made by deathismyfriend
// Alloc by Nestharus can be found here. http://www.hiveworkshop.com/forums/jass-resources-412/snippet-alloc-alternative-221493/
// version 1.0.4

    globals
        // These are the base values that u can set.
        // Here u set the max time for it to end. For every second u need 32. So 5 seconds is 160.
        private integer timeBase = 160
        //                                                                                                     
        // This is the damage base. Every interval
        private real damageBase = 50.00
        //                                                                                                     
        // This is the radius base
        private real radiusBase = 350.00
        //                                                                                                     
        // ----------------------------------------------------------------------------------------------------
        // These are the values that get added to the base per lvl that u can set.
        // Here u set the max time for it to end per lvl. For every second u need 32. So 5 seconds is 160.
        private integer timePerLvl = 0
        //                                                                                                     
        // This is the damage per lvl. Every interval
        private real damagePerLvl = 0.00
        //                                                                                                     
        // This is the radius per lvl
        private real radiusPerLvl = 0.00
        //                                                                                                     
        // ----------------------------------------------------------------------------------------------------
        // These are the other values that u can change
        // set the lightning that gets created when the lightning bomb gets casted
        private string lightningType = "LEAS"
        //                                             
        // set the lightning that gets created from the caster to the target point when the lightning bomb gets casted
        private string lightningType2 = "AFOD"
        //                                       
        // here u can change the attack type
        private attacktype attackType = ATTACK_TYPE_CHAOS
        //                                                                                                     
        // here u can change the damage type
        private damagetype damageType = DAMAGE_TYPE_NORMAL
        // 
        // You can change the time for the loop. Normally u cant change this. In this case it doesnt really matter. 
        // Just remember when u change this u need to change the time for the spell.
        private real loopTime = 0.031250000
        //                                             
        // Set the spell id here
        private integer spellId = 'A000'
        //                                                                                                     
        // ----------------------------------------------------------------------------------------------------
        // ----------------------------------------------------------------------------------------------------
        // Dont change the next couple variables. The other options u can change are under the globals block.
        private timer tmr
        private group grp
        private real array circleReal
        private location loc
        private integer maxIndex = 0
        private integer array structIndex
    endglobals

    //u can change the reals in here. This is for the semi spherical shape the lightning makes.
    //Each variable is for the distance of the lightning. u can make it cylindrical, cone shaped. and a few others if u mess with it.
    private function SetupCircle takes nothing returns nothing
        set circleReal[ 16] = 0.96592
        set circleReal[ 31] = 0.86599
        set circleReal[ 46] = 0.70698
        set circleReal[ 61] = 0.50000
        set circleReal[ 76] = 0.25869
    endfunction
    
    private function FilterUnits takes unit u, unit c returns boolean
        // u is the target
        // c is the caster
        // you can add the conditions you want the spell to follow here.
        if not IsUnitType( u, UNIT_TYPE_STRUCTURE) and GetWidgetLife( u) > .405 then
            return true
        endif
        return false
    endfunction
    
    //dont change anything under here

    private struct lightningBomb extends array
        implement Alloc

        private unit caster
        private integer time
        private integer timeCounter
        private lightning lghtning
        private real propWindow
        
        private static method pProjX takes real x, real dist, real angle returns real // returns x
            return x + dist * Cos( angle)
        endmethod

        private static method pProjY takes real y, real dist, real angle returns real // returns y
            return y + dist * Sin( angle)
        endmethod

        private static method pProjZ takes real y, real dist, real angle returns real // returns z
            return y + dist * Tan( angle)
        endmethod

        private static method create takes unit c, integer t, real prop returns thistype
            local thistype this = thistype.allocate()
            set this.timeCounter = 0
            set this.caster = c
            set this.time = t
            set this.propWindow = prop
            set maxIndex = maxIndex + 1
            return this
        endmethod
        
        private method destroy takes nothing returns nothing
            if this.caster != null then
                call SetUnitPropWindow( this.caster, this.propWindow)
                set caster = null
            endif
            call DestroyLightning( this.lghtning)
            set this.lghtning = null
            set maxIndex = maxIndex - 1
            call this.deallocate()
        endmethod

        private static method destroyLoop takes nothing returns nothing
            local integer L = 1
            local thistype this
            loop
                exitwhen L > maxIndex
                set this = structIndex[ L]
                set this.timeCounter = this.timeCounter + 1
                if this.timeCounter >= this.time then
                    set structIndex[ L] = structIndex[ maxIndex]
                    call this.destroy()
                    set L = L - 1
                endif
                set L = L + 1
            endloop
        endmethod

        private static method setup takes nothing returns nothing
            local thistype this
            local unit c = GetTriggerUnit()
            local unit u
            local integer abiLvl = GetUnitAbilityLevel( c, spellId)
            local integer t = timeBase - ( abiLvl * timePerLvl)
            local real r = radiusBase - ( abiLvl * radiusPerLvl)
            local real dam = damageBase - ( abiLvl * damagePerLvl)
            local real x = GetSpellTargetX()
            local real cx = GetUnitX( c)
            local real y = GetSpellTargetY()
            local real cy = GetUnitY( c)
            local real z
            local real x1
            local real y1
            local real z1
            local real L1 = 22.5
            local integer L = 1
            local real d = r
            call MoveLocation( loc, x, y)
            set z = GetLocationZ( loc) + 50.00
            set this = thistype.create( c, t, GetUnitPropWindow( c))
            set this.lghtning = AddLightning( lightningType2, true, cx, cy, x, y)
            set structIndex[ maxIndex] = this
            if maxIndex == 1 then
                call TimerStart( tmr, loopTime, true, function thistype.destroyLoop)
            endif
            loop
                exitwhen L > 90
                set z1 = pProjZ( z, d, L)
                loop
                    exitwhen L1 >= 360
                    set x1 = pProjX( x, d, L1)
                    set y1 = pProjY( y, d, L1)
                    set this = thistype.create( null, t, 0.00)
                    set structIndex[ maxIndex] = this
                    set this.lghtning = AddLightningEx( lightningType, true, x1, y1, z1, x, y, z)
                    set L1 = L1 + 45
                endloop
                if L1 >= 380 then // this rotates the lightning so its not straight up at every 45 degree angle.
                    set L1 = 0
                else // 360
                    set L1 = 22.5
                endif
                set L = L + 15
                set d = circleReal[ L]*r // reduces the length but not equally.
            endloop
            
            call GroupEnumUnitsInRange( grp, x, y, r, null)
            
            loop
                set u = FirstOfGroup( grp )
                exitwhen u == null
                if u != c and FilterUnits( u, c) and not IsUnitType( u, UNIT_TYPE_DEAD) and GetUnitTypeId( u) != 0 then
                    call UnitDamageTarget( c, u, dam, false, false, attackType, damageType, null)
                endif
                call GroupRemoveUnit( grp, u)
            endloop
            
            set c = null
        endmethod

        private static method createLocation takes nothing returns nothing
            set loc = Location( 0, 0)
        endmethod

        private static method conditions takes nothing returns boolean
            if GetSpellAbilityId() == spellId then
                call setup()
            endif
            return false
        endmethod

        //===========================================================================
        private static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            call createLocation()
            call SetupCircle()
            set tmr = CreateTimer()
            set grp = CreateGroup()
            call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_CAST)
            call TriggerAddCondition( t, Condition( function thistype.conditions))
            set t = null
        endmethod

    endstruct
        
endlibrary


version 1.0.4
made some changes maker suggested​
version 1.0.3
made some small changes​
version 1.0.2
made some config changes​
version 1.0.1
changed it to a point target spell​
version 1.0.0
First version released​


Keywords:
GUI, GUI friendly, jass, vJass, deathismyfriend, bomb, lightning bomb, dimf
Contents

Template Map (Map)

Reviews
12:19, 3rd Aug 2013 Maker: Approved
nice way of generating the lightnings other than that its very lame

i might add an effect onto the units. I didnt want it to have to many effects since there is a lot of lightning being used already.

Wow, seems you've got a unique spell there, nice job.

P.S. I didn't test it yet, but I'll test maybe after.

thanks lol

Edit: Im going to change this a bit.

edit2: updated. Is now a point target ability
 
Last edited:
Looking good.

Would be a bit better to have those circleReal values set in a function at the top of the struct though (for config purposes).

In fact, you could move the lines that set them up into a module right under the globals block so all the configuration would be in one place :eek:

There are some other things that can be configurable as well such as:

  • The timeout (0.031250000)
  • The ATTACK_TYPE
  • The DAMAGE_TYPE

And you may want to move this condition:

if (u != c and u != UNIT_TYPE_STRUCTURE and GetWidgetLife( u) > .405) then

To a unit filtering function at the top of the struct. (It would take unit/player arguments and return a boolean)

One last thing: The condition responsible for filtering out dead units is not exactly right. When units die, their HP can be practically anything. You should probably look into a combination of IsUnitType with the UNIT_TYPE_DEAD flag and a check to make sure that the unit is valid: GetUnitTypeId(u) != 0
 
Looking good.

Would be a bit better to have those circleReal values set in a function at the top of the struct though (for config purposes).

In fact, you could move the lines that set them up into a module right under the globals block so all the configuration would be in one place :eek:

I can make those configurable

There are some other things that can be configurable as well such as:

  • The timeout (0.031250000)
  • The ATTACK_TYPE
  • The DAMAGE_TYPE

timeout in this case i guess it can be configurable since i dont move units or detect anything.

And you may want to move this condition:

if (u != c and u != UNIT_TYPE_STRUCTURE and GetWidgetLife( u) > .405) then

To a unit filtering function at the top of the struct. (It would take unit/player arguments and return a boolean)

i dont have that in the group filter because it would mean using GetFilterUnit a bit and another function. What do u mean take unit/player arguments ?

One last thing: The condition responsible for filtering out dead units is not exactly right. When units die, their HP can be practically anything. You should probably look into a combination of IsUnitType with the UNIT_TYPE_DEAD flag and a check to make sure that the unit is valid: GetUnitTypeId(u) != 0

ooo i was always told to use a health check and a unit type check. i forgot the unit type check lol.

Edit: Updated
 
Last edited:
the z addition value should be configurable...

the "circle" setup is pretty hardcoded since inside the main code, you use things like
L = 1, L = L + 15 etc... and you stop it already at L = 90... So the user is kinda limited to the values that he can use... you should be able to modify the L + 45 part too...

doing that would allow the user to manipulate the amount of lightnings produced...

and I think you should clarify to the users that the circleReal values are for variability of the distance of lightnings...

set structIndex[ maxIndex] = this -> can be placed at the create method

As for the counter, 32 = 1 second right? I think you should just make the timeCounter variable increase by the value of the timer interval instead of 1... that way, its more accurate and the user can now use 1 = 1 second...

and oh, you can create timers and groups at globals block I believe...
 
i dont know about making it completely up to the user. the z value can be changed i guess but i though + 50 looked good.

Since i use 90 degrees its still a semi circle because i use 90 in a 360 direction. So i go 360 then go up one lvl then go 360 again until i hit 90. when it hits 90 its straight up and down.
It will also be confusing if i add more flexibility as it can easily get messed up and look horrible.

As for this
Also, I haven't tested it yet, but shouldn't initial L be equal to the angle between the target point and the caster position? because theoretically, since initial L is always 1, then it will start at the same angle no matter where you cast the spell...
im not sure what u mean ?
 
Edited the post.... realized some mistakes... haha

of course after i post u do that lol.

i kept the 32 = 1 second to allow flexibility. Its how i do all my spells. That way it can be stopped at 5.5 seconds or anything to a value of 0.03125
It allows for much more flexibility in timing.

ya i shouldve put the thing in the create method lol. didnt notice that

groups and timers cant be created in globals. ive tested groups b4 and got told timers cant.

as for the circleReal variable i though i explained its to make the circle look like a circle lol.
 
shouldn't directly using 0.03125 (global loopTime) as addition to the countTime variable more flexible and accurate? also the user will only need to put 5.5 seconds in the setting instead of computing 5.5x32

JASS:
            set this.timeCounter = this.timeCounter + loopTime
                if this.timeCounter >= this.time then
                    set structIndex[ L] = structIndex[ maxIndex]
                    call this.destroy()
                    set L = L - 1
                endif

Also, you can use CTL or T32 so that you won't need to do the instance looping urself... XD
 
I just really don't see the point of having to multiply by 32, when you can use the real time right away... It's more practical and accurate if you ask me...

JASS:
             set this.timeCounter = this.timeCounter + loopTime
                if this.timeCounter >= this.time then
                    set structIndex[ L] = structIndex[ maxIndex]
                    call this.destroy()
                    set L = L - 1
                endif

Doing that is more accurate and you remove the need to have that 32 = 1 second thingy... So it will make things easier for the user too... And I don't see how it's less flexible than using the 32 count...

i kept the 32 = 1 second to allow flexibility. Its how i do all my spells. That way it can be stopped at 5.5 seconds or anything to a value of 0.03125
It allows for much more flexibility in timing.

the user can put 5.5 and it will stop at 5.5...
 
not sure it compiles and works right so i just left it lol.

Put some effort in object editor data. Tooltips and follow through time for example.

You are comparing unit with unit types:
u != UNIT_TYPE_STRUCTURE u != UNIT_TYPE_DEAD

You should describe what the values in SetupCircle do.

i did describe what the variables are for right above the function.
JASS:
//u can change the reals in here. This is for the semi spherical shape the lightning makes.
thats the only thing i hate about making spells lol.
will update shortly i hope
 
Level 37
Joined
Mar 6, 2006
Messages
9,243
Required
*Do something with the follow through time, there is no sense in having the unit channeling after there is nothing to cast or channel.
*Get rid of the unneeded degtorad/radtodeg conversions
*There is GetSpellTargetX/Y, no need for GetSpellTargetLoc

Optional
*Raise the red lightning above the ground
*Make the lightnings stretch over time, but very quickly
*Improve tooltip
 
Level 37
Joined
Mar 6, 2006
Messages
9,243
-The comments in SetupCircle are vague and do not explain how the value affect the spell
-You could use more descriptive variable names. For example, in FilterUnits, you use "u" and "c", then in comments you mention that u is the target and c is the caster. Why not use "caster" and "target" as the variable names
-You could remove createLocation method and create the location in globals block
-You don't need GetUnitTypeId( u) != 0 as it is impossible to pick units with no unit type
-The follow through time doesn't match the spell's duration
-The tooltip is lacking
-The effects disappear quite abruptly
 
Top