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

Random Boss Spellpack v2.1

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
Hello everybody. These are the first spells I actually submit, and therefore my first experience with vJass. These are basically just some very simplistic spells that work with structs, methods, and timers.

The ideas from this spellpack are taken (most of them) from my map, Frostbite Caverns. They are spells that bosses you encounter in the map perform. I might update this spellpack with some more of those spells.

This spellpack requires Jass NewGen Pack to work.
Also, 4 libraries are used:

-XE and its modules (except xedamage) by Vexorian
-GroupUtils by Rising_Dusk
-Knockback by Berb
-DoT by Dynasti

So far, the spellpack contains 8 spells:

Clock: Summons 14 light orbs which spin around the hero in the form of a clock, damaging units that they contact and healing the hero over time.

Shock Blast: Creates 8 charged lightning orbs in a circle around the hero which travel outwards and explode upon contact with enemy units or upon reaching a certain distance.

Lightning Blink: The hero teleports to the target location. Energy flow is unstabilized as the hero travels at such a high speed, and a lightning ball is formed. It travels in an arc and lands violently on the targeted location. The orb jumps to nearby units (cannot be closer than 300 from the landing point and cannot hit the same unit twice) for X amount of times.

Shadow Circle: The hero focuses his shadow energies into the target area. A spiral is formed at the location, and is followed by a circle of pure, dark energy. Units inside the circle are damaged. Lasts X seconds.

Thunderstorm: The hero summons several lightning bolts that fall from the sky. Enemy units hit by these lightning bolts are damaged and knocked back.

Starfall Frenzy: Barrages a target area with falling stars, which cause damage over time for the units inside the affected region.

Shadow Call: The hero summons X missiles from the shadows that, upon reaching a certain distance and exploding, spawn minions that aid the hero in combat.

[RAINBOW]Elemental Discharge:[/RAINBOW] Summons several rings of pure fire, lightning, or nature energy that travel outwards from the hero and damage enemy units on their path. Each ring has a unique effect against units it hits (be it allies or enemies), depending on the element.


JASS:
scope Clock initializer Init
    
    globals
//--------------------------------------------------SETUP--------------------------------------------------
        private constant integer SpellID = 'A001'   //Raw code of the spell.
        private constant real Arrow1Damage = 1000.    //Damage the first (fast) arrow deals per second to units in full contact.
        private constant real Arrow1DamageInc = 200.     //Damage amount of the first arrow increased per level of the ability.
        private constant real Arrow2Damage = 350.    //Damage the second (slow) arrow deals every second upon contact with enemies. NOTE: This arrow moves very slowly, making the damage output big even though the number is small.
        private constant real Arrow2DamageInc = 100.     //Damage amount of the first arrow increased per level of the ability.
        private constant real DamageAoE = 80.    //Range from arrow dummies in which enemy units get damaged.
        private constant boolean AttachClock = true     //Wether to move the clock effect with the caster or not. (true - move, false - don't move).
        private constant real HealBase = 200.   //Amount healed over 8 secs for the duration of the spell at level 1. Affected by level increase below.
        private constant real HealIncrease = 100.   //Heal amount increased per level of the ability.
        private constant real RotationStart = 90.   //Starting rotation (degrees) of the arrows. 90 is most logical since it is the starting position of real clock arrows (doh).
        private constant real RotationTotal = 360.  //The total degrees the slow arrow rotates before finishing effect. (360 - default: 1 complete circle)
        private constant real TotalTime = 8.    //The total duration of the spell in seconds.
        private constant real Interval = 0.04   //Period of time in which the arrow dummies are moved.
        private constant real Separation = 120.     //Separation distance between each light orb.
        private constant integer DummyCount1 = 7      //Number of light orb dummies created for arrow 1 (fast arrow).
        private constant integer DummyCount2 = 7      //Number of light orb dummies created for arrow 2 (slow arrow).
        private constant real DummyHeight = 100.    //Flying height of the light orb dummies.
        private constant real DummySize = 3.5    //Size of the light orb dummies in each arrow.
        private constant string DummyPath = "Abilities\\Weapons\\FaerieDragonMissile\\FaerieDragonMissile.mdl"   //Effect that composes the clock's arrows.
        private constant string SpawnEffect = "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl"      //Effect created when the dummies are spawned.
        private constant string DeathEffect = "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl"      //Effect created when the dummies are destroyed.
        private constant attacktype AttackT = ATTACK_TYPE_NORMAL    //Attack type of the spell.
        private constant damagetype DamageT = DAMAGE_TYPE_NORMAL    //Damage type of the spell.
        private constant weapontype WeaponT = WEAPON_TYPE_WHOKNOWS     //Weapon type of the spell (never knew what it was, so I always set it to null, hehe).
        
        private constant boolean MagicImmune = false    //Wether the spell affects magic immune units.
        private constant boolean Building = false    //Wether the spell affects buildings.
        private constant boolean Flying = true    //Wether the spell affects flying units.
//------------------------------------------------SETUP END------------------------------------------------
        
        private timer ClockTimer = CreateTimer()
        private integer ClockActiveTotal = 0   
        private integer array ClockStructID
        private unit TempU
        private real TempR
        private constant real TotalExecutions = TotalTime / Interval
        private constant real Rotation2Interval = RotationTotal / TotalExecutions * bj_DEGTORAD
        private constant real Rotation1Interval = Rotation2Interval * 12.
    endglobals
    
    private function DamageTargets takes nothing returns boolean
        local unit Temp = GetFilterUnit()
        local boolean B1
        local boolean B2
        local boolean B3
        if IsUnitEnemy(Temp, GetOwningPlayer(TempU)) and GetWidgetLife(Temp) > 0.405 then
            set B1 = IsUnitType(Temp, UNIT_TYPE_MAGIC_IMMUNE) == false
            set B2 = IsUnitType(Temp, UNIT_TYPE_STRUCTURE) == false
            set B3 = IsUnitType(Temp, UNIT_TYPE_FLYING) == false
            static if MagicImmune then
                set B1 = true
            endif
            static if Building then
                set B2 = true
            endif
            static if Flying then
                set B3 = true
            endif
            if B1 and B2 and B3 then
                call UnitDamageTarget(TempU, Temp, TempR, true, false, AttackT, DamageT, WeaponT)
            endif
        endif
        set Temp = null
        return false
    endfunction
    
    private struct Clock
        private unit Caster
        private xefx array Arrow1Dummy[DummyCount1]
        private xefx array Arrow2Dummy[DummyCount2]
        private integer ArrowMoveCount = 0
        private integer AbilityLevel
        private real CenterX
        private real CenterY
        private real Arrow1Rotation
        private real Arrow2Rotation
        private real PeriodicDmg1
        private real PeriodicDmg2
    
        static method create takes unit source returns Clock
            local Clock c = Clock.allocate()
            local real PointX
            local real PointY
            local integer Count = 0
            
            set c.Caster = source
            set c.Arrow1Rotation = RotationStart * bj_DEGTORAD
            set c.Arrow2Rotation = RotationStart * bj_DEGTORAD
            set c.AbilityLevel = GetUnitAbilityLevel(source, SpellID)
            set c.CenterX = GetUnitX(c.Caster)
            set c.CenterY = GetUnitY(c.Caster)
            set c.PeriodicDmg1 = (Arrow1Damage + Arrow1DamageInc * (c.AbilityLevel - 1))/TotalExecutions
            set c.PeriodicDmg2 = (Arrow2Damage + Arrow2DamageInc * (c.AbilityLevel - 1))/TotalExecutions
            loop
                set Count = Count + 1
                set PointX = c.CenterX + Separation * (Count - 1) * Cos(c.Arrow1Rotation)
                set PointY = c.CenterY + Separation * (Count - 1) * Sin(c.Arrow1Rotation)
                set c.Arrow1Dummy[Count] = xefx.create(PointX, PointY, 0.)
                set c.Arrow1Dummy[Count].fxpath = DummyPath
                set c.Arrow1Dummy[Count].z = DummyHeight
                set c.Arrow1Dummy[Count].scale = 3.5
                call DestroyEffect(AddSpecialEffect(SpawnEffect, PointX, PointY))
                exitwhen Count == DummyCount1
            endloop
            set Count = 0
            loop
                set Count = Count + 1
                set PointX = c.CenterX + Separation * (Count - 1) * Cos(c.Arrow1Rotation)
                set PointY = c.CenterY + Separation * (Count - 1) * Sin(c.Arrow1Rotation)
                set c.Arrow2Dummy[Count] = xefx.create(PointX, PointY, 0.)
                set c.Arrow2Dummy[Count].fxpath = "Abilities\\Weapons\\FaerieDragonMissile\\FaerieDragonMissile.mdl"
                set c.Arrow2Dummy[Count].z = DummyHeight
                set c.Arrow2Dummy[Count].scale = 3.5
                call DestroyEffect(AddSpecialEffect(SpawnEffect, PointX, PointY))
                exitwhen Count == DummyCount2
            endloop
            if ClockActiveTotal == 0 then
                call TimerStart(ClockTimer, Interval, true, function Clock.Move)
            endif
            set ClockStructID[ClockActiveTotal] = c
            set ClockActiveTotal = ClockActiveTotal + 1
            return c
        endmethod
    
        private static method Move takes nothing returns nothing
            local Clock TempStruct
            local real PointX
            local real PointY
            local integer Count = 0
            local integer Count2 = 0
            local unit Temp
            
            loop
                exitwhen Count >= ClockActiveTotal
                set TempStruct = ClockStructID[Count]
                if AttachClock == true then
                    set TempStruct.CenterX = GetUnitX(TempStruct.Caster)
                    set TempStruct.CenterY = GetUnitY(TempStruct.Caster)
                endif
                call SetWidgetLife(TempStruct.Caster, GetWidgetLife(TempStruct.Caster) + (HealBase + (HealIncrease * TempStruct.AbilityLevel - HealIncrease))/TotalExecutions)
                set TempStruct.Arrow1Rotation = TempStruct.Arrow1Rotation - Rotation1Interval
                set TempStruct.Arrow2Rotation = TempStruct.Arrow2Rotation - Rotation2Interval
                set TempStruct.ArrowMoveCount = TempStruct.ArrowMoveCount + 1
                loop
                    set Count2 = Count2 + 1
                    set PointX = TempStruct.CenterX + Separation * (Count2 - 1) * Cos(TempStruct.Arrow1Rotation)
                    set PointY = TempStruct.CenterY + Separation * (Count2 - 1) * Sin(TempStruct.Arrow1Rotation)
                    set TempStruct.Arrow1Dummy[Count2].x = PointX
                    set TempStruct.Arrow1Dummy[Count2].y = PointY
                    set TempU = TempStruct.Caster
                    set TempR = TempStruct.PeriodicDmg1
                    call GroupEnumUnitsInRange(ENUM_GROUP, PointX, PointY, DamageAoE, Condition(function DamageTargets))
                    set TempU = null
                    exitwhen Count2 == DummyCount1
                endloop
                set Count2 = 0
                loop
                    set Count2 = Count2 + 1
                    set PointX = TempStruct.CenterX + Separation * (Count2 - 1) * Cos(TempStruct.Arrow2Rotation)
                    set PointY = TempStruct.CenterY + Separation * (Count2 - 1) * Sin(TempStruct.Arrow2Rotation)
                    set TempStruct.Arrow2Dummy[Count2].x = PointX
                    set TempStruct.Arrow2Dummy[Count2].y = PointY
                    set TempU = TempStruct.Caster
                    set TempR = TempStruct.PeriodicDmg2
                    call GroupEnumUnitsInRange(ENUM_GROUP, PointX, PointY, DamageAoE, Condition(function DamageTargets))
                    set TempU = null
                    exitwhen Count2 == DummyCount2
                endloop
                set Count2 = 0
                if TempStruct.ArrowMoveCount == TotalExecutions or GetWidgetLife(TempStruct.Caster) <= 0.405 then
                    call DestroyEffect(AddSpecialEffect(DeathEffect, TempStruct.CenterX, TempStruct.CenterY))
                    loop
                        set Count2 = Count2 + 1
                        set PointX = TempStruct.CenterX + Separation * Count2 * Cos(bj_PI/2.)
                        set PointY = TempStruct.CenterY + Separation * Count2 * Sin(bj_PI/2.)
                        call DestroyEffect(AddSpecialEffect(DeathEffect, PointX, PointY))
                        exitwhen Count2 == 6
                    endloop
                    set Count2 = 0
                    loop
                        set Count2 = Count2 + 1
                        call TempStruct.Arrow1Dummy[Count2].destroy()
                        exitwhen Count2 == DummyCount1
                    endloop
                    set Count2 = 0
                    loop
                        set Count2 = Count2 + 1
                        call TempStruct.Arrow2Dummy[Count2].destroy()
                        exitwhen Count2 == DummyCount2
                    endloop
                    set ClockActiveTotal = ClockActiveTotal - 1
                    set ClockStructID[Count] = ClockStructID[ClockActiveTotal]
                    if ClockActiveTotal == 0 then
                        call PauseTimer(ClockTimer)
                    endif
                    call TempStruct.destroy()
                elseif TempStruct.ArrowMoveCount > TotalExecutions then
                    call DestroyEffect(AddSpecialEffect(DeathEffect, TempStruct.CenterX, TempStruct.CenterY))
                    loop
                        set Count2 = Count2 + 1
                        set PointX = TempStruct.CenterX + Separation * Count2 * Cos(bj_PI/2.)
                        set PointY = TempStruct.CenterY + Separation * Count2 * Sin(bj_PI/2.)
                        call DestroyEffect(AddSpecialEffect(DeathEffect, PointX, PointY))
                        exitwhen Count2 == 6
                    endloop
                    set Count2 = 0
                    loop
                        set Count2 = Count2 + 1
                        call TempStruct.Arrow1Dummy[Count2].destroy()
                        exitwhen Count2 == DummyCount1
                    endloop
                    set Count2 = 0
                    loop
                        set Count2 = Count2 + 1
                        call TempStruct.Arrow2Dummy[Count2].destroy()
                        exitwhen Count2 == DummyCount2
                    endloop
                    set ClockActiveTotal = ClockActiveTotal - 1
                    set ClockStructID[Count] = ClockStructID[ClockActiveTotal]
                    if ClockActiveTotal == 0 then
                        call PauseTimer(ClockTimer)
                    endif
                    set Count = Count - 1
                    call TempStruct.destroy()
                endif
                set Count = Count + 1
            endloop
        endmethod
        
        private method onDestroy takes nothing returns nothing
            set .Caster = null
        endmethod
    endstruct
    
    private function ConditionsActions takes nothing returns nothing
        if GetSpellAbilityId() == SpellID then
            call Clock.create(GetTriggerUnit())
        endif
    endfunction   
        
    private function Init takes nothing returns nothing
        local trigger ClockTrig = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(ClockTrig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddAction(ClockTrig, function ConditionsActions)
        call XE_PreloadAbility(SpellID)
        call Preload(SpawnEffect)
        call Preload(DeathEffect)
        set ClockTrig = null
    endfunction
    
endscope
JASS:
scope ShockBlast initializer Init
    
    globals
//--------------------------------------------------SETUP--------------------------------------------------
        private constant integer SpellID = 'A002'    //Raw code of the spell.
        private constant real OrbDamage = 100.   //Area damage dealt when an orb explodes
        private constant real OrbDamageIncrease = 30.   //Damage increased per level when an orb explodes.
        private constant real MaxDistance = 400.    //Distance the orbs travel before exploding.
        private constant real MaxDistIncrease = 100.    //Distance that the orbs travel before exploding increased per level.
        private constant real ExplodeRadius = 50.   //Area in which an orb explodes upon contact with enemies and damages them.
        private constant real ERadIncrease = 10.   //AoE explosion increment per level.
        private constant real ExplodeRadiusFinal = 100.     //Area damaged when an orb explodes because of reaching maximum distance.
        private constant real ERadFinalIncrease = 50.      //Increase of AoE on orb explode because of max distance.
        private constant real OrbSpeed = 13.    //Distance the orbs travel per interval (0.05 seconds).
        private constant integer DummyCount = 8     //Amount of lightning orbs created by the spell.
        private constant real DummyHeight = 100.     //Height of the lightning orbs.
        private constant real DummySize = 3.    //Size of the lightning orbs.
        private constant string DummyPath = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl"    //Effect path of the dummies (lightning orbs, by default).
        private constant string DeathEffect = "Abilities\\Spells\\Human\\ThunderClap\\ThunderClapCaster.mdl"    //Effect created when the lightning orbs are destroyed.
        private constant attacktype AttackT = ATTACK_TYPE_NORMAL    //Attack type of the spell.
        private constant damagetype DamageT = DAMAGE_TYPE_NORMAL    //Damage type of the spell.
        private constant weapontype WeaponT = WEAPON_TYPE_WHOKNOWS     //Weapon type of the spell (never knew what it was, so I always set it to null, hehe).
        
        private constant boolean MagicImmune = false    //Wether the spell affects magic immune units.
        private constant boolean Building = true    //Wether the spell affects buildings.
        private constant boolean Flying = false    //Wether the spell affects flying units.
//------------------------------------------------SETUP END------------------------------------------------
        
        private timer ShockTimer = CreateTimer()
        private integer ShockActiveTotal = 0
        private integer array ShockStructID
        private unit TempU
        private real TempI
    endglobals
    
    private function EnumFilter takes nothing returns boolean
        return GetWidgetLife(GetFilterUnit()) > 0.405 and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(TempU))
    endfunction
    
    private function DamageTargets takes nothing returns boolean
        local unit Temp = GetFilterUnit()
        local boolean B1
        local boolean B2
        local boolean B3
        if GetWidgetLife(Temp) > 0.405 and IsUnitEnemy(Temp, GetOwningPlayer(TempU)) then
            set B1 = IsUnitType(Temp, UNIT_TYPE_MAGIC_IMMUNE) == false
            set B2 = IsUnitType(Temp, UNIT_TYPE_STRUCTURE) == false
            set B3 = IsUnitType(Temp, UNIT_TYPE_FLYING) == false
            static if MagicImmune then
                set B1 = true
            endif
            static if Building then
                set B2 = true
            endif
            static if Flying then
                set B3 = true
            endif
            if B1 and B2 and B3 then
                call UnitDamageTarget(TempU, Temp, OrbDamage + OrbDamageIncrease * (TempI - 1), true, false, AttackT, DamageT, WeaponT)
            endif
        endif
        set Temp = null
        return false
    endfunction
    
    private struct ShockBlast
        private unit Caster
        private xefx array ShockDummy[DummyCount]
        private integer DeadOrbs = 0
        private integer AbilityLevel
        private real Distance = 0.
        private real SourceX
        private real SourceY
        
        static method create takes unit source returns ShockBlast
            local ShockBlast shock = ShockBlast.allocate()
            local integer Count = 0
            
            set shock.Caster = source
            set shock.SourceX = GetUnitX(shock.Caster)
            set shock.SourceY = GetUnitY(shock.Caster)
            set shock.AbilityLevel = GetUnitAbilityLevel(shock.Caster, SpellID)
            loop
                set Count = Count + 1
                set shock.ShockDummy[Count] = xefx.create(shock.SourceX, shock.SourceY, 0.)
                set shock.ShockDummy[Count].fxpath = DummyPath
                set shock.ShockDummy[Count].z = DummyHeight
                set shock.ShockDummy[Count].scale = DummySize
                exitwhen Count >= DummyCount
            endloop
            if ShockActiveTotal == 0 then
                call TimerStart(ShockTimer, .05, true, function ShockBlast.Move)
            endif
            set ShockStructID[ShockActiveTotal] = shock
            set ShockActiveTotal = ShockActiveTotal + 1
            return shock
        endmethod
        
        private static method Move takes nothing returns nothing
            local ShockBlast TempStruct
            local real PointX
            local real PointY
            local unit Temp
            local unit Temp2
            local integer Count = 0
            local integer Count2 = 0
            
            loop
                exitwhen Count >= ShockActiveTotal
                set TempStruct = ShockStructID[Count]
                set TempStruct.Distance = TempStruct.Distance + OrbSpeed
                loop
                    set Count2 = Count2 + 1
                    if TempStruct.ShockDummy[Count2] != 0 then
                        set PointX = TempStruct.SourceX + TempStruct.Distance * Cos(Count2 * bj_PI * 2./DummyCount)
                        set PointY = TempStruct.SourceY + TempStruct.Distance * Sin(Count2 * bj_PI * 2./DummyCount)
                        set TempStruct.ShockDummy[Count2].x = PointX
                        set TempStruct.ShockDummy[Count2].y = PointY
                        set TempU = TempStruct.Caster
                        call GroupEnumUnitsInRange(ENUM_GROUP, PointX, PointY, ExplodeRadius + (ERadIncrease * TempStruct.AbilityLevel - ERadIncrease), Condition(function EnumFilter))
                        set TempU = null
                        set Temp = FirstOfGroup(ENUM_GROUP)
                        if Temp != null then
                            call TempStruct.ShockDummy[Count2].destroy()
                            set TempStruct.ShockDummy[Count2] = 0
                            call DestroyEffect(AddSpecialEffect(DeathEffect, PointX, PointY))
                            call GroupClear(ENUM_GROUP)
                            set TempU = TempStruct.Caster
                            set TempI = TempStruct.AbilityLevel
                            call GroupEnumUnitsInRange(ENUM_GROUP, PointX, PointY, ExplodeRadius + (ERadIncrease * TempStruct.AbilityLevel - ERadIncrease), Condition(function DamageTargets))
                            set TempU = null
                            set TempStruct.DeadOrbs = TempStruct.DeadOrbs + 1
                            if TempStruct.DeadOrbs >= 8 then
                                set ShockActiveTotal = ShockActiveTotal - 1
                                set ShockStructID[Count] = ShockStructID[ShockActiveTotal]
                                if ShockActiveTotal == 0 then
                                    call PauseTimer(ShockTimer)
                                endif
                                set Count = Count - 1
                                call TempStruct.destroy()
                            endif
                        endif
                    endif
                    exitwhen Count2 == DummyCount
                endloop
                set Count2 = 0
                if TempStruct.Distance >= MaxDistance + (MaxDistIncrease * TempStruct.AbilityLevel - MaxDistIncrease) then
                    loop
                        set Count2 = Count2 + 1
                        if TempStruct.ShockDummy[Count2] != 0 then
                            call DestroyEffect(AddSpecialEffect(DeathEffect, TempStruct.ShockDummy[Count2].x, TempStruct.ShockDummy[Count2].y))
                            set TempU = TempStruct.Caster
                            set TempI = TempStruct.AbilityLevel
                            call GroupEnumUnitsInRange(ENUM_GROUP, TempStruct.ShockDummy[Count2].x, TempStruct.ShockDummy[Count2].y, ExplodeRadiusFinal + (ERadFinalIncrease * TempStruct.AbilityLevel - ERadFinalIncrease), Condition(function DamageTargets))
                            set TempU = null
                            call TempStruct.ShockDummy[Count2].destroy()
                            set TempStruct.ShockDummy[Count2] = 0
                        endif
                        exitwhen Count2 == DummyCount
                    endloop
                    set ShockActiveTotal = ShockActiveTotal - 1
                    set ShockStructID[Count] = ShockStructID[ShockActiveTotal]
                    if ShockActiveTotal == 0 then
                        call PauseTimer(ShockTimer)
                    endif
                    set Count = Count - 1
                    call TempStruct.destroy()
                endif
                set Count = Count + 1
            endloop
            
        endmethod
        
        private method onDestroy takes nothing returns nothing
            local integer Count = 0
            set .Caster = null
        endmethod
    endstruct
    
    private function ConditionsActions takes nothing returns nothing
        if GetSpellAbilityId() == SpellID then
            call ShockBlast.create(GetTriggerUnit())
        endif
    endfunction   
        
    private function Init takes nothing returns nothing
        local trigger ShockTrig = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(ShockTrig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddAction(ShockTrig, function ConditionsActions)
        call XE_PreloadAbility(SpellID)
        call Preload(DeathEffect)
        set ShockTrig = null
    endfunction
    
endscope
JASS:
scope LightningBlink initializer Init

    globals
//--------------------------------------------------SETUP--------------------------------------------------
        //This ability is based on blink ability to ease the triggering part. To change the maximum/minimum distance, go to Object Editor, find the ablility there, and change it.
        private constant integer SpellID = 'A003'    //Raw code of the spell.
        private constant real ElevAngle = bj_DEGTORAD * 50.   //The elevation angle between the starting point of the parabola and its maximum height in degrees (converted to radians with the constant).
        private constant real DummySize = 3.   //The size of the lightning orb.
        private constant real OrbSpeed = 22.   //Distance traveled by the lightning orb every 0.04 seconds.
        private constant real Damage = 50.   //Amount of damage done when the lightning orb explodes.
        private constant real DamageInc = 30.     //Damage increment per level.
        private constant real DamageAoE = 100.     //Area affected by the lightning ball's explotion.
        private constant real DamageAoEInc = 50.      //Area increment per level.
        private constant integer JumpTotal = 1      //Total times the lightning orb can jump.
        private constant integer JumpTotInc = 1      //Increase in the amount of times the orb can jump per level.
        private constant real JumpAoE = 500.       //The maximum range to where the orb can jump.
        private constant real JumpMinD = 300.      //Minimum distance between the target point and new target for the orb to jump.
        private constant string DummyPath = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl"    //Effect path of the dummy (lightning orb, by default).
        private constant string DeathEffect = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl"    //Effect created when the orb explodes.
        private constant attacktype AttackT = ATTACK_TYPE_NORMAL    //Attack type of the spell.
        private constant damagetype DamageT = DAMAGE_TYPE_NORMAL    //Damage type of the spell.
        private constant weapontype WeaponT = WEAPON_TYPE_WHOKNOWS     //Weapon type of the spell (never knew what it was, so I always set it to null, hehe).
        
        private constant boolean MagicImmune = false    //Wether the spell affects magic immune units.
        private constant boolean Building = false    //Wether the spell affects buildings.
        private constant boolean Flying = false    //Wether the spell affects flying units.
//------------------------------------------------SETUP END------------------------------------------------

        private timer OrbTimer = CreateTimer()
        private integer OrbActiveTotal = 0
        private integer array OrbStructID
        private unit TempU
        private integer TempI
    endglobals
    
    private function DistBetweenPointsXY takes real X1, real Y1, real X2, real Y2 returns real
        return SquareRoot((X2 - X1)*(X2 - X1) + (Y2 - Y1)*(Y2 - Y1))
    endfunction
    
    //--------Credits to moyack and Spec for this function!--------
    function ParabolaZ takes real h, real d, real x returns real
      return (4 * h / d) * (d - x) * (x / d)
    endfunction
    //-------------------------------------------------------------
    
    private function RemoveUnits takes nothing returns nothing
        call GroupRemoveUnit(ENUM_GROUP, GetEnumUnit())
    endfunction
    
    private function EnumFilter takes nothing returns boolean
        local unit Temp = GetFilterUnit()
        local boolean B1
        local boolean B2
        local boolean B3
        if IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(TempU)) and GetWidgetLife(GetFilterUnit()) > 0.405 then
            set B1 = IsUnitType(Temp, UNIT_TYPE_MAGIC_IMMUNE) == false
            set B2 = IsUnitType(Temp, UNIT_TYPE_STRUCTURE) == false
            set B3 = IsUnitType(Temp, UNIT_TYPE_FLYING) == false
            static if MagicImmune then
                set B1 = true
            endif
            static if Building then
                set B2 = true
            endif
            static if Flying then
                set B3 = true
            endif
            if B1 and B2 and B3 then
                return true
            endif
        endif
        return false
    endfunction
    
    private function DamageTargets takes nothing returns boolean
        local unit Temp = GetFilterUnit()
        local boolean B1
        local boolean B2
        local boolean B3
        if IsUnitEnemy(Temp, GetOwningPlayer(TempU)) and GetWidgetLife(Temp) > 0.405 then
            set B1 = IsUnitType(Temp, UNIT_TYPE_MAGIC_IMMUNE) == false
            set B2 = IsUnitType(Temp, UNIT_TYPE_STRUCTURE) == false
            set B3 = IsUnitType(Temp, UNIT_TYPE_FLYING) == false
            static if MagicImmune then
                set B1 = true
            endif
            static if Building then
                set B2 = true
            endif
            static if Flying then
                set B3 = true
            endif
            if B1 and B2 and B3 then
                call UnitDamageTarget(TempU, Temp, Damage + DamageInc * (TempI - 1), true, false, AttackT, DamageT, WeaponT)
            endif
        endif
        set Temp = null
        return false
    endfunction
    
    private struct L_Orb
        private unit Caster
        private unit Target
        private xefx OrbDummy
        private group Affected
        private integer Jumps = 0
        private integer JumpTotal
        private integer AbilityLevel
        private real Distance = 0.
        private real CenterX
        private real CenterY
        private real Cosine
        private real Sine
        private real MaxD
        
        static method create takes unit caster, location targetloc returns L_Orb
            local L_Orb o = L_Orb.allocate()
            local real TargetX = GetLocationX(targetloc)
            local real TargetY = GetLocationY(targetloc)
            
            set o.Affected = NewGroup()
            set o.Caster = caster
            set o.CenterX = GetUnitX(o.Caster)
            set o.CenterY = GetUnitY(o.Caster)
            set o.OrbDummy = xefx.create(o.CenterX, o.CenterY, 0.)
            set o.OrbDummy.fxpath = DummyPath
            set o.OrbDummy.scale = DummySize
            set o.AbilityLevel = GetUnitAbilityLevel(o.Caster, SpellID)
            set o.Cosine = Atan2(TargetY - o.CenterY, TargetX - o.CenterX)
            set o.Sine = Sin(o.Cosine)
            set o.Cosine = Cos(o.Cosine)
            set o.MaxD = DistBetweenPointsXY(o.CenterX, o.CenterY, TargetX, TargetY)
            set o.JumpTotal = o.JumpTotal + (JumpTotInc * o.AbilityLevel - JumpTotInc)
            if OrbActiveTotal == 0 then
                call TimerStart(OrbTimer, 0.04, true, function L_Orb.Move)
            endif
            set OrbStructID[OrbActiveTotal] = o
            set OrbActiveTotal = OrbActiveTotal + 1
            return o
        endmethod
        
        private static method Move takes nothing returns nothing
            local L_Orb TempStruct
            local integer Count = 0
            local real TempR
            local real PointX
            local real PointY
            local unit Temp
            
            loop
                exitwhen Count >= OrbActiveTotal
                set TempStruct = OrbStructID[Count]
                set TempStruct.Distance = TempStruct.Distance + OrbSpeed
                set PointX = TempStruct.CenterX + TempStruct.Distance * TempStruct.Cosine                
                set PointY = TempStruct.CenterY + TempStruct.Distance * TempStruct.Sine
                set TempR = DistBetweenPointsXY(TempStruct.CenterX, TempStruct.CenterY, PointX, PointY)
                set TempStruct.OrbDummy.x = PointX
                set TempStruct.OrbDummy.y = PointY
                set TempStruct.OrbDummy.z = ParabolaZ(Tan(ElevAngle) * TempStruct.MaxD/2., TempStruct.MaxD, TempR)
                if TempR >= TempStruct.MaxD then
                    call TempStruct.Explode(TempStruct.Caster, PointX, PointY)
                    if TempStruct.Jumps >= TempStruct.JumpTotal then
                        call TempStruct.OrbDummy.destroy()
                        set OrbActiveTotal = OrbActiveTotal - 1
                        set OrbStructID[Count] = OrbStructID[OrbActiveTotal]
                        if OrbActiveTotal == 0 then
                            call PauseTimer(OrbTimer)
                        endif
                        set Count = Count - 1
                        call TempStruct.destroy()
                    else
                        set TempU = TempStruct.Caster
                        call GroupEnumUnitsInRange(ENUM_GROUP, PointX, PointY, JumpAoE, Condition(function EnumFilter))
                        set TempU = null
                        call GroupRemoveUnit(ENUM_GROUP, TempStruct.Target)
                        call GroupRefresh(TempStruct.Affected)
                        call ForGroup(TempStruct.Affected, function RemoveUnits)
                        loop
                            set Temp = FirstOfGroup(ENUM_GROUP)
                            if Temp == null then
                                call TempStruct.OrbDummy.destroy()
                                set OrbActiveTotal = OrbActiveTotal - 1
                                set OrbStructID[Count] = OrbStructID[OrbActiveTotal]
                                if OrbActiveTotal == 0 then
                                    call PauseTimer(OrbTimer)
                                endif
                                set Count = Count - 1
                                call TempStruct.destroy()
                                exitwhen true
                            elseif DistBetweenPointsXY(TempStruct.OrbDummy.x, TempStruct.OrbDummy.y, GetUnitX(Temp), GetUnitY(Temp)) >= JumpMinD then
                                call GroupAddUnit(TempStruct.Affected, Temp)
                                set TempStruct.CenterX = TempStruct.OrbDummy.x
                                set TempStruct.CenterY = TempStruct.OrbDummy.y
                                set TempStruct.Distance = 0.
                                set TempStruct.Cosine = Atan2(GetUnitY(Temp) - TempStruct.CenterY, GetUnitX(Temp) - TempStruct.CenterX)
                                set TempStruct.Sine = Sin(TempStruct.Cosine)
                                set TempStruct.Cosine = Cos(TempStruct.Cosine)
                                set TempStruct.MaxD = DistBetweenPointsXY(TempStruct.CenterX, TempStruct.CenterY, GetUnitX(Temp), GetUnitY(Temp))
                                set TempStruct.Jumps = TempStruct.Jumps + 1
                                exitwhen true
                            else
                                call GroupRemoveUnit(ENUM_GROUP, Temp)
                            endif
                        endloop
                    endif
                endif
                set Count = Count + 1
            endloop
        endmethod
        
        private method Explode takes unit caster, real locx, real locy returns nothing
            local unit Temp
            local real PointX
            local real PointY
            local real Distance = 0
            local integer Count = 0
            local integer Count2 = 0
            
            call DestroyEffect(AddSpecialEffect(DeathEffect, locx, locy))
            loop
                set Count = Count + 1
                set Distance = Distance + 70.
                loop
                    set Count2 = Count2 + 1
                    set PointX = locx + Distance * Cos(Count2 * bj_PI/4.)
                    set PointY = locy + Distance * Sin(Count2 * bj_PI/4.)
                    call DestroyEffect(AddSpecialEffect(DeathEffect, PointX, PointY))
                    exitwhen Count2 == 8
                endloop
                set Count2 = 0
                exitwhen Count == 3
            endloop
            set TempU = this.Caster
            set TempI = this.AbilityLevel
            call GroupEnumUnitsInRange(ENUM_GROUP, locx, locy, DamageAoE + (DamageAoEInc * this.AbilityLevel - DamageAoEInc), Condition(function DamageTargets))
            set TempU = null
        endmethod
        
        private method onDestroy takes nothing returns nothing
            set .Caster = null
            set .Target = null
            call ReleaseGroup(.Affected)
        endmethod
    endstruct
    
    private function ConditionsActions takes nothing returns nothing
        if GetSpellAbilityId() == SpellID then
            call L_Orb.create(GetTriggerUnit(), GetSpellTargetLoc())
        endif
    endfunction
    
    private function Init takes nothing returns nothing
        local trigger L_OrbTrig = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(L_OrbTrig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddAction(L_OrbTrig, function ConditionsActions)
        call XE_PreloadAbility(SpellID)
        call Preload(DeathEffect)
        set L_OrbTrig = null
    endfunction
    
endscope
JASS:
scope ShadowCircle initializer Init
    
    globals
//--------------------------------------------------SETUP--------------------------------------------------
        private constant integer SpellID = 'A005'     //Raw code of the spell
        private constant real AoE = 300.    //Area affected by the shadow circle. (Remember to change this in Object Editor for aesthetic purposes).
        private constant real AoEInc = 100.     //Area increment per level. ---------------^
        private constant real EffectAoE = 150.     //Area affected by the shadow effect thingies (or whatever effect you choose).
        private constant real EffectDamage = 8.      //Damage done by the spiral's shadow effects every 0.05 seconds. :O
        private constant real TotalDamage = 200.      //Damage dealt to units inside the circle over the spell's duration.
        private constant real TotDamageInc = 100.      //Damage increase per level.
        private constant real Duration = 5.      //Duration of the spell in seconds after the circle is spawned.
        private constant real DurationInc = 1.5     //Duration increased per level.
        private constant real SpiralInterval = 0.05     //Period of time in which spiral effects are spawned.
        private constant real CircleInterval = 0.2     //Period of time in which circle effects are spawned.
        private constant string Effect = "Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl"     //Effects of the spiral and the circle.
        private constant attacktype AttackT = ATTACK_TYPE_NORMAL    //Attack type of the spell.
        private constant damagetype DamageT = DAMAGE_TYPE_NORMAL    //Damage type of the spell.
        private constant weapontype WeaponT = WEAPON_TYPE_WHOKNOWS     //Weapon type of the spell (never knew what it was, so I always set it to null, hehe).
        
        private constant boolean MagicImmune = false    //Wether the spell affects magic immune units.
        private constant boolean Building = false    //Wether the spell affects buildings.
        private constant boolean Flying = true    //Wether the spell affects flying units.
//------------------------------------------------SETUP END------------------------------------------------

        private timer ShadowSpiralTimer = CreateTimer()
        private timer ShadowExeTimer = CreateTimer()
        private integer ShadowActiveTotal = 0
        private integer array ShadowStructID
        private unit TempU
        private real TempR
        private constant real PeriodicDist = 200. * SpiralInterval
    endglobals
    
    private function DamageTargets takes nothing returns boolean
        local unit Temp = GetFilterUnit()
        local boolean B1
        local boolean B2
        local boolean B3
        if IsUnitEnemy(Temp, GetOwningPlayer(TempU)) and GetWidgetLife(Temp) > 0.405 then
            set B1 = IsUnitType(Temp, UNIT_TYPE_MAGIC_IMMUNE) == false
            set B2 = IsUnitType(Temp, UNIT_TYPE_STRUCTURE) == false
            set B3 = IsUnitType(Temp, UNIT_TYPE_FLYING) == false
            static if MagicImmune then
                set B1 = true
            endif
            static if Building then
                set B2 = true
            endif
            static if Flying then
                set B3 = true
            endif
            if B1 and B2 and B3 then
                call UnitDamageTarget(TempU, Temp, TempR, true, false, AttackT, DamageT, WeaponT)
            endif
        endif
        set Temp = null
        return false
    endfunction
    
    private struct Shadow
        private unit Caster
        private integer Executions = 0
        private integer AbilityLevel
        private boolean Spiral = true
        private real Distance = 0.
        private real Rotation1 = 0.
        private real CenterX
        private real CenterY
        private real Rotation2
        private real Rotation3
        private real Rotation4
        private real Rotation5
        private real TotalExe
        private real PeriodicDamage
        
        static method create takes unit caster, location targetloc returns Shadow
            local Shadow s = Shadow.allocate()
            
            set s.Caster = caster
            set s.CenterX = GetLocationX(targetloc)
            set s.CenterY = GetLocationY(targetloc)
            set s.AbilityLevel = GetUnitAbilityLevel(caster, SpellID)
            set s.TotalExe = (Duration + (DurationInc * s.AbilityLevel - DurationInc))/.2
            set s.PeriodicDamage = (TotalDamage + (DurationInc * s.AbilityLevel - TotDamageInc))/s.TotalExe
            if ShadowActiveTotal == 0 then
                call TimerStart(ShadowSpiralTimer, SpiralInterval, true, function Shadow.InitSpiral)
                call TimerStart(ShadowExeTimer, CircleInterval, true, function Shadow.Periodic)
            endif
            set ShadowStructID[ShadowActiveTotal] = s
            set ShadowActiveTotal = ShadowActiveTotal + 1
            return s
        endmethod
        
        private static method InitSpiral takes nothing returns nothing
            local Shadow TempStruct
            local real PointX
            local real PointY
            local integer Count = 0
            local unit Temp
            
            loop
                exitwhen Count >= ShadowActiveTotal
                set TempStruct = ShadowStructID[Count]
                if TempStruct.Spiral then
                    set TempStruct.Rotation1 = TempStruct.Rotation1 + 15. * bj_DEGTORAD
                    set TempStruct.Distance = TempStruct.Distance + PeriodicDist
                    set PointX = TempStruct.CenterX + TempStruct.Distance * Cos(TempStruct.Rotation1)
                    set PointY = TempStruct.CenterY + TempStruct.Distance * Sin(TempStruct.Rotation1)
                    call DestroyEffect(AddSpecialEffect(Effect, PointX, PointY))
                    set TempU = TempStruct.Caster
                    set TempR = EffectDamage
                    call GroupEnumUnitsInRange(ENUM_GROUP, PointX, PointY, EffectAoE, Condition(function DamageTargets))
                    set TempU = null
                    if TempStruct.Distance >= AoE + (AoEInc * TempStruct.AbilityLevel - AoEInc) then
                        set TempStruct.Spiral = false
                        set TempStruct.Rotation1 = 72. * bj_DEGTORAD
                        set TempStruct.Rotation2 = 144. * bj_DEGTORAD
                        set TempStruct.Rotation3 = 216. * bj_DEGTORAD
                        set TempStruct.Rotation4 = 288. * bj_DEGTORAD
                        set TempStruct.Rotation5 = 360. * bj_DEGTORAD
                    endif
                endif
                set Count = Count + 1
            endloop
        endmethod
        
//! textmacro ShadowEffects takes INTEGER
    set TempStruct.Rotation$INTEGER$ = TempStruct.Rotation$INTEGER$ + 10. * bj_DEGTORAD
    set PointX = TempStruct.CenterX + (AoE + (AoEInc * TempStruct.AbilityLevel - AoEInc)) * Cos(TempStruct.Rotation$INTEGER$)
    set PointY = TempStruct.CenterY + (AoE + (AoEInc * TempStruct.AbilityLevel - AoEInc)) * Sin(TempStruct.Rotation$INTEGER$)
    call DestroyEffect(AddSpecialEffect(Effect, PointX, PointY))
//! endtextmacro
        
        private static method Periodic takes nothing returns nothing
            local Shadow TempStruct
            local real PointX
            local real PointY
            local integer Count = 0
            local integer Count2 = 0
            local unit Temp
            
            loop
                exitwhen Count >= ShadowActiveTotal
                set TempStruct = ShadowStructID[Count]
                if TempStruct.Spiral == false then
                    set TempStruct.Executions = TempStruct.Executions + 1
                    //! runtextmacro ShadowEffects("1")
                    //! runtextmacro ShadowEffects("2")
                    //! runtextmacro ShadowEffects("3")
                    //! runtextmacro ShadowEffects("4")
                    //! runtextmacro ShadowEffects("5")
                    set TempU = TempStruct.Caster
                    set TempR = TempStruct.PeriodicDamage
                    call GroupEnumUnitsInRange(ENUM_GROUP, TempStruct.CenterX, TempStruct.CenterY, AoE + (AoEInc * TempStruct.AbilityLevel - AoEInc), Condition(function DamageTargets))
                    set TempU = null
                    if TempStruct.Executions >= TempStruct.TotalExe then
                        set ShadowActiveTotal = ShadowActiveTotal - 1
                        set ShadowStructID[Count] = ShadowStructID[ShadowActiveTotal]
                        if ShadowActiveTotal == 0 then
                            call PauseTimer(ShadowSpiralTimer)
                            call PauseTimer(ShadowExeTimer)
                        endif
                        set Count = Count - 1
                        call TempStruct.destroy()
                    endif
                endif
                set Count = Count + 1
            endloop
        endmethod
        
        method onDestroy takes nothing returns nothing
            set .Caster = null
        endmethod
    endstruct
    
    private function ConditionsActions takes nothing returns nothing
        if GetSpellAbilityId() == SpellID then
            call Shadow.create(GetTriggerUnit(), GetSpellTargetLoc())
        endif
    endfunction
    
    private function Init takes nothing returns nothing
        local trigger ShadowTrig = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(ShadowTrig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddAction(ShadowTrig, function ConditionsActions)
        call XE_PreloadAbility(SpellID)
        call Preload(Effect)
        set ShadowTrig = null
    endfunction
    
endscope
JASS:
scope Thunderstorm initializer Init
    
    globals
//--------------------------------------------------SETUP--------------------------------------------------
        private constant integer SpellID = 'A000'    //Raw code of the spell.
        private constant real SpellDamage = 50.    //Damage dealt by each lightning bolt.
        private constant real DamageInc = 50.    //Damage increased per level of the spell.
        private constant real MaxRad = 650.   //Maximum range at which lightning bolts spawn.
        private constant real MaxRadInc = 50.   //Increase of maximum radius per level.
        private constant real DmgRadius = 200.    //Radius affected by each lightning bolt (damage and knockback area).
        private constant real DmgRadInc = 0.    //Radius increase per level of the spell.
        //=============================== Knockback section =================================
        //Credits to Berb for his knockback library!
        private constant boolean DoKnockback = true     //Wether to knockback units hit by lightning bolts or not (if false, values in knockback section don't have any effect).
        
        private constant real KnockDist = 150.   //The distance units are knockbacked.
        private constant real KnDistInc = 50.   //Knockback distance increased per level.
        private constant real KnockTime = .7   //Time it takes to complete the knockback on each unit.
        private constant string KnockEffect1 = "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"    //Individual effect spawn on knockback start.
        private constant string KnockEffectP = ""   //Effect spawned every 8 (or whatever the frequency is) executions of the periodic function, which runs every 0.04 seconds.
        private constant integer EffectFrequency = 8    //Modifies the frequency in which the periodic effect spawns.
        //===================================================================================
        private constant integer MinBolts = 4     //Minimum number of lightning bolts spawned.
        private constant integer MinBoltsInc = 1      //Minimum number of lightning bolts increased per level.
        private constant integer MaxBolts = 8     //Maximum number of lightning bolts spawned.
        private constant integer MaxBoltsInc = 1    //Maximum number of lightning bolts increased per level.
        private constant real SpawnInterval = .1    //Time amount between the creation of each lightning bolt in seconds.
        private constant real Pause = 1.    //Time amount between the warning effects placement and the actual spawning of lightning bolts in seconds.
        private constant string PreEffect = "Abilities\\Spells\\NightElf\\TrueshotAura\\TrueshotAura.mdl"    //"Warning" effect placed before the lightning hits.
        private constant string Effect1 = "Abilities\\Spells\\Other\\Monsoon\\MonsoonBoltTarget.mdl"    //Effect spawned when lightning bolt hits (this is the actual lightning bolt).
        private constant string Effect2 = "Abilities\\Spells\\Human\\ThunderClap\\ThunderClapCaster.mdl"     //Extra effect spawned when lightning bolt hits.
        private constant attacktype AtkType = ATTACK_TYPE_NORMAL    //Attack type of the spell's damage.
        private constant damagetype DmgType = DAMAGE_TYPE_NORMAL    //Damage type of the spell's damage.
        private constant weapontype WeaponT = WEAPON_TYPE_WHOKNOWS     //Weapon type of the spell (never knew what it was, so I always set it to null, hehe).
        
        private constant boolean MagicImmune = false    //Wether the spell affects magic immune units.
        private constant boolean Building = true    //Wether the spell affects buildings.
        private constant boolean Flying = true  //Wether the spell affects flying units.
//------------------------------------------------SETUP END------------------------------------------------
        
        private unit TempU
        private integer TempI
        private real TempR1
        private real TempR2
    endglobals
    
    private function DamageTargets takes nothing returns boolean
        local unit Temp = GetFilterUnit()
        local boolean B1
        local boolean B2
        local boolean B3
        if GetWidgetLife(Temp) > .405 and IsUnitEnemy(Temp, GetOwningPlayer(TempU)) then
            set B1 = IsUnitType(Temp, UNIT_TYPE_MAGIC_IMMUNE) == false
            set B2 = IsUnitType(Temp, UNIT_TYPE_STRUCTURE) == false
            set B3 = IsUnitType(Temp, UNIT_TYPE_FLYING) == false
            static if MagicImmune then
                set B1 = true
            endif
            static if Building then
                set B2 = true
            endif
            static if Flying then
                set B3 = true
            endif
            if B1 and B2 and B3 then
                call UnitDamageTarget(TempU, Temp, SpellDamage + DamageInc * TempI, true, false, AtkType, DmgType, WeaponT)
                if DoKnockback and (IsUnitType(Temp, UNIT_TYPE_STRUCTURE)) == false then
                    call Knockback.create(Temp, Atan2(GetUnitY(Temp) - TempR2, GetUnitX(Temp) - TempR1), KnockDist, KnockTime, KnockEffect1, KnockEffectP, EffectFrequency)
                endif
            endif
        endif
        set Temp = null
        return false
    endfunction
    
    private function Actions takes nothing returns nothing
        local unit Caster = GetTriggerUnit()
        local xefx array TempE
        local integer AbiLevel = GetUnitAbilityLevel(Caster, SpellID)
        local integer LightningNumber = GetRandomInt(MinBolts + MinBoltsInc * (AbiLevel - 1), MaxBolts + MaxBoltsInc * (AbiLevel - 1))
        local integer Count = 0
        local real TempA
        local real TempD
        local real PointX
        local real PointY
        
        loop
            set Count = Count + 1
            set TempA = GetRandomReal(0., 2. * bj_PI)
            set TempD = SquareRoot(GetRandomReal(0., 1.)) * (MaxRad + MaxRadInc * (AbiLevel - 1))
            set PointX = GetUnitX(Caster) + TempD * Cos(TempA)
            set PointY = GetUnitY(Caster) + TempD * Sin(TempA)
            set TempE[Count] = xefx.create(PointX, PointY, 0.)
            set TempE[Count].fxpath = PreEffect
            exitwhen Count == LightningNumber
            if SpawnInterval != 0. then
                call TriggerSleepAction(SpawnInterval)          //I know what you're thinking - "Ughh, waits? What the @#$! is wrong with you?"  Hey... at least it's not GUI, so it's still MUI.
            endif
        endloop
        set Count = 0
        call TriggerSleepAction(Pause)
        loop
            set Count = Count + 1
            set PointX = TempE[Count].x
            set PointY = TempE[Count].y
            call DestroyEffect(AddSpecialEffect(Effect1, PointX, PointY))
            call DestroyEffect(AddSpecialEffect(Effect2, PointX, PointY))
            set TempU = Caster
            set TempI = AbiLevel - 1
            static if DoKnockback then
                set TempR1 = PointX
                set TempR2 = PointY
            endif
            call GroupEnumUnitsInRange(ENUM_GROUP, PointX, PointY, DmgRadius + DmgRadInc * (AbiLevel - 1), Condition(function DamageTargets))
            set TempU = null
            call TempE[Count].destroy()
            exitwhen Count == LightningNumber
            if SpawnInterval != 0. then
                call TriggerSleepAction(SpawnInterval)
            endif
        endloop
        set Caster = null
    endfunction
    
    private function Conditions takes nothing returns boolean
        if GetSpellAbilityId() == SpellID then
            return true
        endif
        return false
    endfunction
    
    private function Init takes nothing returns nothing
        local trigger StormTrig = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(StormTrig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddAction(StormTrig, function Actions)
        call TriggerAddCondition(StormTrig, Condition(function Conditions))
        call XE_PreloadAbility(SpellID)
        call Preload(KnockEffect1)
        call Preload(KnockEffectP)
        call Preload(PreEffect)
        call Preload(Effect1)
        call Preload(Effect2)
        set StormTrig = null
    endfunction
    
endscope
JASS:
scope StarfallFrenzy initializer Init
    
    globals
//--------------------------------------------------SETUP--------------------------------------------------
        private constant integer SpellID = 'A004'   //Raw code of the spell.
        private constant real TotalDmg = 150.     //Total damage dealt by the spell.
        private constant real TotalDmgInc = 100.    //Total damage increase per level.
        private constant real Rad = 250.    //Radius of the circular region affected by the spell.
        private constant real RadInc = 50.   //Radius increment per level.
        private constant real Duration = 3.   //Duration of the spell.
        private constant real DurationInc = .5   //Duration of the spell increased per level.
        private constant real DamageDelay = .7    //Delay in seconds before the spell starts damaging units (set so the spell damages units only after starfall effects hit the ground).
        private constant string Effect = "Abilities\\Spells\\NightElf\\Starfall\\StarfallTarget.mdl"   //Effect of the spell.
        private constant attacktype AtkType = ATTACK_TYPE_NORMAL    //Attack type of the spell's damage.
        private constant damagetype DmgType = DAMAGE_TYPE_NORMAL    //Damage type of the spell's damage.
        private constant weapontype WeaponT = WEAPON_TYPE_WHOKNOWS     //Weapon type of the spell (never knew what it was, so I always set it to null, hehe).
        
        private constant boolean MagicImmune = false    //Wether the spell affects magic immune units.
        private constant boolean Building = false   //Wether the spell affects buildings.
        private constant boolean Flying = true  //Wether the spell affects flying units.
//------------------------------------------------SETUP END------------------------------------------------
        
        private timer StarTimer = CreateTimer()
        private integer StarActiveTotal = 0
        private integer array StarStructID
        private unit TempU
        private real TempR
    endglobals
    
    private function DamageTargets takes nothing returns boolean
        local unit Temp = GetFilterUnit()
        local boolean B1
        local boolean B2
        local boolean B3
        if GetWidgetLife(Temp) > .405 and IsUnitEnemy(Temp, GetOwningPlayer(TempU)) then
            set B1 = IsUnitType(Temp, UNIT_TYPE_MAGIC_IMMUNE) == false
            set B2 = IsUnitType(Temp, UNIT_TYPE_STRUCTURE) == false
            set B3 = IsUnitType(Temp, UNIT_TYPE_FLYING) == false
            static if MagicImmune then
                set B1 = true
            endif
            static if Building then
                set B2 = true
            endif
            static if Flying then
                set B3 = true
            endif
            if B1 and B2 and B3 then
                call UnitDamageTarget(TempU, Temp, TempR, true, false, AtkType, DmgType, WeaponT)
            endif
        endif
        set Temp = null
        return false
    endfunction
    
    private struct Starfall
        private unit Caster
        private integer AbiLevel
        private boolean Damage
        private real ElapsedTime = 0.
        private real CenterX
        private real CenterY
        private real PeriodicDmg
        private real Radius
        private real TotalTime
        
        static method create takes unit caster, location targetloc returns Starfall
            local Starfall s = Starfall.allocate()
            set s.Caster = caster
            set s.CenterX = GetLocationX(targetloc)
            set s.CenterY = GetLocationY(targetloc)
            set s.AbiLevel = GetUnitAbilityLevel(s.Caster, SpellID)
            set s.Radius = Rad + RadInc * (s.AbiLevel - 1)
            set s.TotalTime = Duration + DurationInc * (s.AbiLevel - 1)
            set s.Damage = false
            set s.PeriodicDmg = (TotalDmg + TotalDmgInc * (s.AbiLevel - 1))/(s.TotalTime/.04)
            if StarActiveTotal == 0 then
                call TimerStart(StarTimer, .04, true, function Starfall.Periodic)
            endif
            set StarStructID[StarActiveTotal] = s
            set StarActiveTotal = StarActiveTotal + 1
            return s
        endmethod
        
        private static method Periodic takes nothing returns nothing
            local Starfall TempStruct
            local real TempA
            local real TempD
            local real PointX
            local real PointY
            local integer Count = 0
            
            loop
                exitwhen Count >= StarActiveTotal
                set TempStruct = StarStructID[Count]
                set TempStruct.ElapsedTime = TempStruct.ElapsedTime + .04
                if TempStruct.ElapsedTime >= DamageDelay then
                    set TempStruct.Damage = true
                endif
                if TempStruct.Damage then
                    set TempU = TempStruct.Caster
                    set TempR = TempStruct.PeriodicDmg
                    call GroupEnumUnitsInRange(ENUM_GROUP, TempStruct.CenterX, TempStruct.CenterY, TempStruct.Radius, Condition(function DamageTargets))
                endif
                set TempA = GetRandomReal(0., 2. * bj_PI)
                set TempD = SquareRoot(GetRandomReal(0., 1.)) * TempStruct.Radius
                set PointX = TempStruct.CenterX + TempD * Cos(TempA)
                set PointY = TempStruct.CenterY + TempD * Sin(TempA)
                call DestroyEffect(AddSpecialEffect(Effect, PointX, PointY))
                if TempStruct.ElapsedTime >= TempStruct.TotalTime then
                    set StarActiveTotal = StarActiveTotal - 1
                    set StarStructID[Count] = StarStructID[StarActiveTotal]
                    if StarActiveTotal == 0 then
                        call PauseTimer(StarTimer)
                    endif
                    set Count = Count - 1
                    call TempStruct.destroy()
                endif
                set Count = Count + 1
            endloop
        endmethod
        
        private method onDestroy takes nothing returns nothing
            set .Caster = null
        endmethod
        
    endstruct
    
    private function ConditionsActions takes nothing returns nothing
        if GetSpellAbilityId() == SpellID then
            call Starfall.create(GetTriggerUnit(), GetSpellTargetLoc())
        endif
    endfunction
    
    private function Init takes nothing returns nothing
        local trigger StarTrig = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(StarTrig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddAction(StarTrig, function ConditionsActions)
        call XE_PreloadAbility(SpellID)
        call Preload(Effect)
        set StarTrig = null
    endfunction
    
endscope
JASS:
scope ShadowCall initializer Init
    
    globals
//--------------------------------------------------SETUP--------------------------------------------------
        private constant integer SpellID = 'A007'   //Raw code of the spell.
        private constant integer SummonID = 'h003'   //Raw code of the unit to be summoned by the spell.
        private constant integer SummonQty = 2    //Amount of units summoned at level 1.
        private constant integer QtyIncrease = 1    //Amount of units summoned increased per level.
        private constant real Radius = 600.   //Radius around the hero of the circular area in which units can be spawned.
        private constant real Collision = 50.   //The distance the missile has to be at from the target point in order to explode.
        private constant real MissileSpeed = 400.   //Distance traveled per second by the missiles.
        private constant real SpawnInterval = .1   //Amount of time between the spawning of each missile.
        private constant real MissileHeight = 64.   //Height at which missiles fly.
        private constant string MissileModel = "Abilities\\Weapons\\ZigguratMissile\\ZigguratMissile.mdl"    //Model used for the missiles.
        private constant string FinalEffect = "Objects\\Spawnmodels\\Undead\\UCancelDeath\\UCancelDeath.mdl"    //Effect created once the missile explodes.
//------------------------------------------------SETUP END------------------------------------------------
        
        private timer MissileTimer = CreateTimer()
        private integer MActiveTotal = 0
        private integer array MStructID
    endglobals
    
    private struct Missile extends xecollider
        private player Owner
        
        static method create takes real x, real y, real dir, player owner returns Missile
            local Missile m = Missile.allocate(x, y, dir)
            set m.Owner = owner
            return m
        endmethod
        
        private method loopControl takes nothing returns nothing
            if SquareRoot((.x - .targetPointX)*(.x - .targetPointX) + (.y - .targetPointY)*(.y - .targetPointY)) <= Collision then
                call .terminate()
            endif
        endmethod
        
        private method onDestroy takes nothing returns nothing
            call CreateUnit(.Owner, SummonID, .x, .y, GetRandomReal(0., 360.))
            call DestroyEffect(AddSpecialEffect(FinalEffect, .x, .y))
        endmethod
    endstruct
    
    private function Actions takes nothing returns nothing
        local unit Caster = GetTriggerUnit()
        local Missile TempStruct
        local integer AbiLevel = GetUnitAbilityLevel(Caster, SpellID)
        local integer Count = 0
        local real TempA
        local real TempD
        local real PointX
        local real PointY
        
        loop
            set TempA = GetRandomReal(0., 2. * bj_PI)
            set TempD = SquareRoot(GetRandomReal(0., 1.)) * Radius
            set PointX = GetUnitX(Caster) + TempD * Cos(TempA)
            set PointY = GetUnitY(Caster) + TempD * Sin(TempA)
            set TempStruct = Missile.create(GetUnitX(Caster), GetUnitY(Caster), TempA, GetOwningPlayer(Caster))
            set TempStruct.fxpath = MissileModel
            set TempStruct.z = MissileHeight
            set TempStruct.speed = MissileSpeed
            set TempStruct.angleSpeed = 90.
            call TempStruct.setTargetPoint(PointX, PointY)
            exitwhen Count + 1 == SummonQty + QtyIncrease * (AbiLevel - 1)
            set Count = Count + 1
            if SpawnInterval != 0. then
                call TriggerSleepAction(SpawnInterval)
            endif
        endloop
    endfunction
    
    private function Conditions takes nothing returns boolean
        if GetSpellAbilityId() == SpellID then
            return true
        endif
        return false
    endfunction
    
    private function Init takes nothing returns nothing
        local trigger ShadowCallTrig = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(ShadowCallTrig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(ShadowCallTrig, Condition(function Conditions))
        call TriggerAddAction(ShadowCallTrig, function Actions)
        call XE_PreloadAbility(SpellID)
        call Preload(MissileModel)
        call Preload(FinalEffect)
        set ShadowCallTrig = null
    endfunction
    
endscope
JASS:
scope ElementalDischarge initializer Init
    
    globals
//--------------------------------------------------SETUP--------------------------------------------------
        private constant integer SpellID = 'A006'   //Raw code of the spell.
        private constant integer DummyID = 'h000'   //Raw code of the dummy unit.
        //The dummy Purge and Entangling Roots spells can be configured in the object editor.
        //The damage, duration, and other stats set in the Object Editor in each level are 
        //    the values applied when the effects are used on a unit.
        private constant integer PurgeID = 'A008'   //Raw code of the "Dummy Purge" spell.
        private constant integer RootsID = 'A009'   //Raw code of the "Dummy Roots" spell.
        private constant real FireChance      = 33.34   //Percent chance to summon a fire ring.
        private constant real LightningChance = 33.33   //Percent chance to summon a lightning ring.
        private constant real NatureChance    = 33.33   //Percent chance to summon a nature ring.
        //---------------------------------->  100.00   //The sum of these three values must be equal to 100. If not, one of the values will be adjusted.  
        private constant real EffectDmgRad = 75.   //Radius in which the effects that compose the rings deal damage.
        private constant real FireDamage = 10.   //Instant damage dealt by each little fire effect.
        private constant real FireDmgInc = 5.   //Instant fire damage increase per level.
        private constant real FireDoT = 30.   //Damage over time dealt by each fire effect.
        private constant real FireDoTInc = 5.   //Fire damage over time increase per level.
        private constant real FireDoTDur = 5.   //Duration of the damage over time in seconds.
        private constant real LightningDamage = 20.   //Instant damage dealy by each lightning effect.
        private constant real LightningDmgInc = 5.   //Instant lightning damage increase per level.
        private constant real NatureHealing = 30.   //Instant healing done by each nature effect.
        private constant real NatureHealInc = 5.   //Instant nature healing increase per level.
        private constant real RingSeparation = 100.   //Distance between each elemental ring.
        private constant integer RingAmount = 4   //Amount of rings spawned by the spell.
        private constant integer RingInc = 1    //Increase in ring amount per level.
        private constant integer RingEffects = 6   //Amount of effects that compose the innermost ring.
        private constant integer RingEffInc = 4    //Increase in amount of effects per ring (the first ring has 16 effects, the second has 20 effects, etc).
        private constant string FireEffect = "Abilities\\Spells\\Human\\MarkOfChaos\\MarkOfChaosTarget.mdl"    //Effects that compose the fire rings.
        private constant string FireDoTEffect = "Abilities\\Spells\\Other\\BreathOfFire\\BreathOfFireDamage.mdl"    //Damage attached to units affected by fire damage over time.
        private constant string LightningEffect = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl"    //Effects that compose the lightning rings.
        private constant string NatureEffect = "Objects\\Spawnmodels\\NightElf\\EntBirthTarget\\EntBirthTarget.mdl"    //Effects that compoose the nature rings.
        private constant attacktype AttackT = ATTACK_TYPE_NORMAL    //Attack type of the spell.
        private constant damagetype DamageT = DAMAGE_TYPE_NORMAL    //Damage type of the spell.
        private constant weapontype WeaponT = WEAPON_TYPE_WHOKNOWS     //Weapon type of the spell (never knew what it was, so I always set it to null, hehe).
        
        private constant boolean MagicImmune = false    //Wether the spell affects magic immune units.
        private constant boolean Building = false    //Wether the spell affects buildings.
        private constant boolean Flying = false    //Wether the spell affects flying units.
//------------------------------------------------SETUP END------------------------------------------------
        
        private constant boolean PercentNotExact = (FireChance + LightningChance + NatureChance) != 100.
        private unit TempU
        private integer TempI1
        private integer TempI2
        private real TempR1
        private real TempR2
        private real PercentBound1 = FireChance
        private real PercentBound2 = FireChance + LightningChance
    endglobals
    
    private function AffectTargets takes nothing returns boolean
        local unit Temp = GetFilterUnit()
        local unit DummyUnit
        local boolean B1
        local boolean B2
        local boolean B3
        if GetWidgetLife(Temp) > 0.405 then
            set B1 = IsUnitType(Temp, UNIT_TYPE_MAGIC_IMMUNE) == false
            set B2 = IsUnitType(Temp, UNIT_TYPE_STRUCTURE) == false
            set B3 = IsUnitType(Temp, UNIT_TYPE_FLYING) == false
            static if MagicImmune then
                set B1 = true
            endif
            static if Building then
                set B2 = true
            endif
            static if Flying then
                set B3 = true
            endif
            if B1 and B2 and B3 then
                if IsUnitEnemy(Temp, GetOwningPlayer(TempU)) then
                    if TempI1 < 2 then
                        call UnitDamageTarget(TempU, Temp, TempR1, true, false, AttackT, DamageT, WeaponT)
                        if TempI1 == 0 then
                            call DamageOverTimeEx(TempU, Temp, TempR2, FireDoTDur, AttackT, DamageT, FireDoTEffect, "origin")
                        else
                            set DummyUnit = CreateUnit(GetOwningPlayer(TempU), DummyID, GetUnitX(TempU), GetUnitY(TempU), 0.)
                            call UnitApplyTimedLife(DummyUnit, 'BTLF', 1.)
                            call UnitAddAbility(DummyUnit, PurgeID)
                            call SetUnitAbilityLevel(DummyUnit, PurgeID, TempI2)
                            call IssueTargetOrder(DummyUnit, "purge", Temp)
                            set DummyUnit = null
                        endif
                    else
                        set DummyUnit = CreateUnit(GetOwningPlayer(TempU), DummyID, GetUnitX(TempU), GetUnitY(TempU), 0.)
                        call UnitApplyTimedLife(DummyUnit, 'BTLF', 1.)
                        call UnitAddAbility(DummyUnit, RootsID)
                        call SetUnitAbilityLevel(DummyUnit, RootsID, TempI2)
                        call IssueTargetOrder(DummyUnit, "entanglingroots", Temp)
                        set DummyUnit = null
                    endif
                elseif TempI1 == 2 and IsUnitAlly(Temp, GetOwningPlayer(TempU)) then
                    call SetWidgetLife(Temp, GetWidgetLife(Temp) + TempR1)
                endif
            endif
        endif
        set Temp = null
        return false
    endfunction
    
    private function Actions takes nothing returns nothing
        local unit Caster = GetTriggerUnit()
        local real SourceX = GetUnitX(Caster)
        local real SourceY = GetUnitY(Caster)
        local integer AbiLevel = GetUnitAbilityLevel(Caster, SpellID)
        local integer Count = 1
        local integer Count2 = 1
        local real Randomizer
        local real PointX
        local real PointY
        
        loop
            set Randomizer = GetRandomReal(0., 100.)
            loop
                set PointX = SourceX + Count * RingSeparation * Cos(bj_PI*2./(RingEffects + RingEffInc * (Count - 1)) * Count2)
                set PointY = SourceY + Count * RingSeparation * Sin(bj_PI*2./(RingEffects + RingEffInc * (Count - 1)) * Count2)
                if Randomizer <= PercentBound1 then
                    set TempU = Caster
                    set TempI1 = 0
                    set TempR1 = FireDamage + FireDmgInc * (AbiLevel - 1)
                    set TempR2 = FireDoT + FireDoTInc * (AbiLevel - 1)
                    call GroupEnumUnitsInRange(ENUM_GROUP, PointX, PointY, EffectDmgRad, Condition(function AffectTargets))
                    set TempU = null
                    call DestroyEffect(AddSpecialEffect(FireEffect, PointX, PointY))
                elseif Randomizer <= PercentBound2 then
                    set TempU = Caster
                    set TempI1 = 1
                    set TempI2 = AbiLevel
                    set TempR1 = LightningDamage + LightningDmgInc * (AbiLevel - 1)
                    call GroupEnumUnitsInRange(ENUM_GROUP, PointX, PointY, EffectDmgRad, Condition(function AffectTargets))
                    set TempU = null
                    call DestroyEffect(AddSpecialEffect(LightningEffect, PointX, PointY))
                else
                    set TempU = Caster
                    set TempI1 = 2
                    set TempI2 = AbiLevel
                    set TempR1 = NatureHealing + NatureHealInc * (AbiLevel - 1)
                    call GroupEnumUnitsInRange(ENUM_GROUP, PointX, PointY, EffectDmgRad, Condition(function AffectTargets))
                    set TempU = null
                    call DestroyEffect(AddSpecialEffect(NatureEffect, PointX, PointY))
                endif
                exitwhen Count2 >= RingEffects + RingEffInc * (Count - 1)
                set Count2 = Count2 + 1
            endloop
            exitwhen Count >= RingAmount + RingInc * (AbiLevel - 1)
            set Count = Count + 1
            set Count2 = 0
            call TriggerSleepAction(.1)
        endloop
        set Caster = null
    endfunction
    
    private function Conditions takes nothing returns boolean
        if GetSpellAbilityId() == SpellID then
            return true
        endif
        return false
    endfunction
    
    private function Init takes nothing returns nothing
        local trigger DischargeTrig = CreateTrigger()
        local integer Randomizer
        call TriggerRegisterAnyUnitEventBJ(DischargeTrig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(DischargeTrig, Condition(function Conditions))
        call TriggerAddAction(DischargeTrig, function Actions)
        static if PercentNotExact then
            set Randomizer = GetRandomInt(1, 3)
            if Randomizer == 1 then
                set PercentBound1 = 100. - LightningChance - NatureChance
                set PercentBound2 = PercentBound1 + LightningChance
            elseif Randomizer == 2 then
                set PercentBound2 = FireChance + 100. - FireChance - NatureChance
            endif
        endif
        call XE_PreloadAbility(SpellID)
        call Preload(FireEffect)
        call Preload(FireDoTEffect)
        call Preload(LightningEffect)
        call Preload(NatureEffect)
        set DischargeTrig = null
    endfunction
    
endscope

-First release.
-Fixed what was requested.
-Changed bits of the code, such as changing "if" to "static if" multiple times.
-Fixed issue where magic immune units, structures, and flying units were still being damaged by the spells.
-Added 4 spells: Thunderstorm, Starfall Frenzy, Shadow Call, and Elemental Discharge
-Implemented many new libraries for increased efficiency, such as XE, GroupUtils, Knockback, and DoT
-Many major/minor changes in code for more efficiency
-Other minor changes/changes I don't remember
-Don't read this and go test the spells!
-Made the boolexprs global variables
-Fixed errors that I made when importing and implementing the XE library
-Passed the triggers' actions into their conditions instead, and used "return false"
-Made the test map cooler :)


Please, feel free to criticize my coding all you want... I am just learning vJass. All suggestions or comments are appreciated and encouraged. :)

Keywords:
lightning, spellpack, random, boss, frostbite, caverns, blink, shock, blast, spell, clock, holy, shadow, circle, thunder, storm, thunderstorm, starfal
Contents

Random (Boss) Spellpack (Map)

Reviews
12.12 IcemanBo: For long time as NeedsFix. Rejected. Bribe set B1 = IsUnitType(Temp, UNIT_TYPE_MAGIC_IMMUNE) == false set B2 = IsUnitType(Temp, UNIT_TYPE_STRUCTURE) == false set B3 = IsUnitType(Temp...

Moderator

M

Moderator

12.12
IcemanBo: For long time as NeedsFix. Rejected.

Bribe

JASS:
            set B1 = IsUnitType(Temp, UNIT_TYPE_MAGIC_IMMUNE) == false
            set B2 = IsUnitType(Temp, UNIT_TYPE_STRUCTURE) == false
            set B3 = IsUnitType(Temp, UNIT_TYPE_FLYING) == false
            static if MagicImmune then
                set B1 = true
            endif
            static if Building then
                set B2 = true
            endif
            static if Flying then
                set B3 = true
            endif

Would be much better as:

JASS:
            static if MAGIC_IMMUNE then
                set b1 = true
            else
                set b1 = IsUnitType(Temp, UNIT_TYPE_MAGIC_IMMUNE)
            endif
            static if BUILDING then
                set b2 = true
            else
                set b2 = IsUnitType(Temp, UNIT_TYPE_STRUCTURE)
            endif
            static if FLYING then
                set b3 = true
            else
                set b3 = not IsUnitType(Temp, UNIT_TYPE_FLYING)
            endif

Remember, constants should be capitalized. This helps people reading the code to know what things are constant and what things aren't.

This is a recurring thing throughout all your scripts, should be changed.

GetWidgetLife(Temp) > 0.405 is not as reliable as "not IsUnitType(temp, UNIT_TYPE_DEAD)"

onDestroy is useless since one of the recent JassHelper patches allows you to override destroy and call .deallocate() from it. Just make sure the "destroy" method is situated above any method that might call it.

In LightningBlink, you leak a location every time the spell is cast. Use GetSpellTargetX/Y (added in 1.24) to avoid this.

Same leak in ShadowCircle.

private constant boolean DoKnockback = true

All references to this should be confinded to static - if's.

I suggest running a search/replace through each of these texts if only to convert them all to capital letters. It's difficult to understand which booleans are dynamic in your code and which ones could be optimized into static if's.

StarfallFrenzy has two location leaks every time it's cast. Use GetSpellTargetX/Y instead of GetLocationX/Y(GetSpellTargetLoc()).

## Needs fix ##
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
Short Code Skim

In General

  • You definitely should learn how to use loops and arrays. You don't need those text macros if you use loops.
  • Never use locations unless you're trying to find GetLocationZ. If you do, use a global location.
  • Don't need to null player variables
  • You should avoid using FirstOfGroup loops.
  • Why did you use SetUnitState for setting a unit's life? Use SetWidgetLife. Same with GetUnitState and GetWidgetLife.
  • You could convert to radians directly.
Lightning Blink
  • Could support ParabolicFunction instead of defining your own.
  • Don't destroy groups.
 
Level 5
Joined
Oct 24, 2007
Messages
90
@Klingo: Saw that coming, hehe. Trying to do it that way.

Short Code Skim

In General

* You definitely should learn how to use loops and arrays. You don't need those text macros if you use loops.
* Never use locations unless you're trying to find GetLocationZ. If you do, use a global location.
* Don't need to null player variables
* You should avoid using FirstOfGroup loops.
* Why did you use SetUnitState for setting a unit's life? Use SetWidgetLife. Same with GetUnitState and GetWidgetLife.
* You could convert to radians directly.

Lightning Blink

* Could support ParabolicFunction instead of defining your own.
* Don't destroy groups.

Thank you, that's exactly what I was looking for. Couple questions:

-How can I use arrays and loops to save unit dummies, for example, to struct instance members when the members can't be arrays themselves (as far as I know)?

-So, instead of FirstOfGroup, I should use ForGroup? I'm guessing yes, since its the only other "for each unit in group" function I know of.

-On Lightning Blink, why shouldn't I destroy the group? I thought it leaked if I didn't?

+Rep for the help, I'll fix everything. Much appreciated.
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
-How can I use arrays and loops to save unit dummies, for example, to struct instance members when the members can't be arrays themselves (as far as I know)?
You can have array members in structs. You just need to define the size if it's not static.
Example
JASS:
integer array ThisIntegerArray[10]

-So, instead of FirstOfGroup, I should use ForGroup? I'm guessing yes, since its the only other "for each unit in group" function I know of.
Yes or do it directly in the filter. Either works.

-On Lightning Blink, why shouldn't I destroy the group? I thought it leaked if I didn't?
It's better to recycle/reuse groups.
I heard there's some weird leak if you use DestroyGroup on something that enumerates.
 
Level 5
Joined
Oct 24, 2007
Messages
90
First time I hear of array members. It wasn't even in the Jasshelper manual...
Running the for group actions inside the filter is also a good idea and new to me, I'll look into that.

Once again, thanks for your help, you've teached me something valuable. :)
Now I'm going to get rid of these textmacros and update it...
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
It's better to recycle/reuse groups.
I heard there's some weird leak if you use DestroyGroup on something that enumerates.

I think it leaves a small shadow memory leak which can't be removed, that's why you recycle.

About the spells.

Attack, damage and weapon type should be configurable in the global constants.

Shouldn't dummies be created for player(15)? it's 11 now, is that player brown? why?

Instead of GetLocationX/Y use GetUnitX/Y (as watermelon stated about location usage)

Dunno if this is true but isn't SetUnitX/Y much more smoother than SetUnitPos?

I think you should remove the dummies after killed or just remove as they will trigger event Unit dies.

I just looked at the code, didn't test in-game, looks good :D
 
Level 5
Joined
Oct 24, 2007
Messages
90
Shouldn't dummies be created for player(15)? it's 11 now, is that player brown? why?

Yeah oops, in my boss map they are all created to player 11, which is the "computer." I was going to set it to create them for the player casting the spell, but I guess 15 is a better idea since they don't trigger player-owned unit death events.

Instead of GetLocationX/Y use GetUnitX/Y (as watermelon stated about location usage)

Dunno if this is true but isn't SetUnitX/Y much more smoother than SetUnitPos?

Didn't even know these functions existed, hehe. Thanks, I will use them instead of getting the unit location every time.

I think you should remove the dummies after killed or just remove as they will trigger event Unit dies.

Only problem here is that if I just remove the dummies, the death effect for the dummies wouldn't show and therefore the overall spell effect would be less cool-looking (like the lightning orbs).

Thanks for all the suggestions!
 
Level 5
Joined
Oct 24, 2007
Messages
90
Clock used in Restricted Complex 601. I will enjoy a version with TimerUtils, GroupUtils and AutoIndex to make this spell perfect. (If I rework it, I will post the new version).

Good work anyway.

That's awesome, thanks for using the spell! :)

I liked this spells but when i casted Lightning Blink rapidly i got a bug.

Thanks for downloading and testing the spells. I will take a look and see what's wrong (uh oh). :eekani:

EDIT: Please keep on posting comments or suggestions, I like receiving feedback and responding to it :)
 
Last edited:
Level 2
Joined
Nov 2, 2010
Messages
20
Hello, your spells are ready perfect and useful.
I know your new version has uesd " static if "

But my jasshelper has got a error with " static if "
my jasshelper's version is ready update to 0.A.2.B
How can i fix this error?
 

Attachments

  • 1.JPG
    1.JPG
    15.9 KB · Views: 177
  • 2.jpg
    2.jpg
    57.5 KB · Views: 192
Level 5
Joined
Oct 24, 2007
Messages
90
Hello, your spells are ready perfect and useful.
I know your new version has uesd " static if "

But my jasshelper has got a error with " static if "
my jasshelper's version is ready update to 0.A.2.B
How can i fix this error?

Hey, thanks for testing the spells! :)

It's wierd that you are getting an error with that static if, considering the line giving you the error is part of the XE Preload library and not my actual spell code. You have the latest version of JassHelper, too... I used to have that problem, but then again, my version was outdated. Don't know what might be wrong, I'm sorry = /
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
local variables need to be camelCased, starting with a lowercase letter, not an uppercase. This is not a mandatory change but it certainly would help me understand where the variables are coming from.

Locations should not be a requirement for the spell-target area in LightningBlink. Not only, but you didn't remove the location. Use GetSpellTargetX, Y instead.

There are a lot of spells here and you should link them together via something like GTrigger, SpellEvent or the new SpellEffectEvent I developed, so that when a spell is fired only one condition is evaluated instead of ALL the conditions. Something like that is going to have to be a requirement for someone releasing a spell pack.
 
Top