• 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.

Cross of Heaven 1.2

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
This spell casts a cross which will float down and rotate around the casting unit.
The cross itself is also surrounded by sheep which rotate around it.
The sheep deal damage to enemy units by throwing chain lightnings and heal allied units by throwing healing waves.

-Credits to DiscipleOfLife for his GetClosestUnitInRange() function

-Credits to mckill2009 for his Holy Cross spell which inspired me (http://www.hiveworkshop.com/forums/spells-569/holy-cross-v1-5-a-190143/?prev=search%3Dholy%2520cross%26d%3Dlist%26r%3D20)


JASS:
library HolyCross initializer Init requires GCU

//Configurables
globals
    //Following Ability-IDs have to be set:
    private constant integer SpellID = 'A000' //Triggering spell, should be targeting nothing
    
    //Following Dummie-IDs have to be set:
    private constant integer DummyCrossID = 'h002' // The Cross Dummy
    private constant integer DummyCasterID = 'h001' // The Sheep Dummy
    
    //This is the effect that is created on the hero, when casted:
    private constant string EffectModel = "Abilities\\Spells\\Human\\Resurrect\\ResurrectTarget.mdl"
    
    private constant string LightningHeal1 = "HWPB" // Healing Wave Effect
    private constant string LightningHeal2 = "HWSB" // Healing Wave Effect Variation 2
    private constant string LightningDamage1 = "CLPB" // Chain Lightning Effect
    private constant string LightningDamage2 = "CLSB" // Chain Lightning Effect Variation 2
    private constant string DamagingSoundPath = "Abilities\\Spells\\Orc\\LightningBolt\\LightningBolt.wav" // The Damaging Sound sound-path
    private constant string HealingSoundPath = "Abilities\\Spells\\Orc\\HealingWave\\HealingWave.wav" // The Healing Sound sound-path
    
    private constant real BaseHeight = 900 // This is the initial height from where the cross floats down and floats up after the spell is done
    private constant real HeightToGo = 175 // This is the heigh in which the cross will be floating while the spell is active
    private constant real Length = 20 // This is the spell length; DownRisingTime and UpRisingTime will be included
    private constant real DownRisingTime = 1.5 // This is the time that the cross will take to float down
    private constant real UpRisingTime = 1.5 // This is the time that the cross will take to float up, when it's finished    
    private constant real DummyCasterDistanceToCross = 200 // This is the distance in which the sheep will fly around the cross
    private constant real CrossRotatingSpeed = 2 // This is the speed of the cross rotating around the hero
    private constant real DummyCasterRotatingSpeed = 1.25 // This is the speed of the sheep rotating around the cross
    private constant real DummyCasterNukeCooldown = 2.25 // This is the cooldown of the damaging-/healing- lightnings
    private constant real DummyCasterNukeRange = 300 // This is the range of the sheep to be to trigger the damaging-/healing- lightnings
    private constant real DummyCasterAttackLength = 2. // This is the length of the lightning-attack/healing, the damage will be splited on it
    private constant boolean HealsCasterToo = false // true = heals spell casting unit
    private constant attacktype AttackType = ATTACK_TYPE_MAGIC // This is the attack type of the lightning-attack
    private constant damagetype DamageType = DAMAGE_TYPE_MAGIC // This is the damage type of the lightning-attack
endglobals


private function GetSheepNumb takes integer level returns integer
    return level // Returns the number of sheep in the given level. In this case it's the amount of levels
endfunction

private function GetHealing takes integer level returns real
    return 100. + (I2R(level)*50) // The formula of healing that is granted to a unit when healed by a sheep
endfunction

private function GetDamage takes integer level returns real
    return 100. + (I2R(level)*50) // The formula of damage that is granted to a unit when healed by a sheep
endfunction

// This is the unit filter. Don't change this is if you don't want any special units to be not attacked/healed by the sheep
private function IsUnitViableTarget takes nothing returns boolean
    if not HealsCasterToo and GetFilterUnit() == bj_lastCreatedUnit then
        return false
    endif
    return true
endfunction
//Endconfigurables

globals
    private integer Index = 0
    private boolean array IsActive
    private unit array Caster
    private unit array Cross
    private real array TimeLeft
    private real array FloatingDirection
    private boolean array FloatedUp
    private unit array      DummyCasters
    private integer array   DummyCastersSpellNumb
    private real array      DummyCasterFloatingDirection
    private integer         DummyCastersIndex = 0
    private real array      DummyCasterLeftCooldown
    private lightning array DummyCasterLightningAttack
    private real array      DummyCasterLightningRemainingDur
    private boolean array   DummyCasterDoesLightningAttack
    private unit array      DummyCasterTargetedUnit
    private boolean array   DummyCasterIsHealing
    private boolean array   DummyCasterLightningIsDestroyed
    
endglobals

private function Actions takes nothing returns boolean
    local real TempLocX
    local real TempLocY
    local real TempLoc2X
    local real TempLoc2Y
    local effect TempEffect
    local integer i = 1
    local real CasterDummyFacing
    
    if (GetSpellAbilityId() == SpellID) then
        set Index = Index + 1
        set IsActive[Index] = true
        set TimeLeft[Index] = Length
        set FloatingDirection[Index] = GetRandomInt(0, 360)
        set FloatedUp[Index] = false
        set Caster[Index] = GetTriggerUnit()
        set TempEffect = AddSpecialEffectTarget(EffectModel, Caster[Index], "origin")
        set TempLocX = GetUnitX(Caster[Index])
        set TempLocY = GetUnitY(Caster[Index])
        set Cross[Index] = CreateUnit(GetOwningPlayer(Caster[Index]), DummyCrossID, TempLocX, TempLocY, GetUnitFacing(Caster[Index]))
        call SetUnitFlyHeight(Cross[Index], BaseHeight, 0)
        call SetUnitFlyHeight(Cross[Index], HeightToGo, (BaseHeight - HeightToGo) / DownRisingTime)
        call DestroyEffect(TempEffect)
        
        loop
            set DummyCastersIndex = DummyCastersIndex + 1
            set DummyCasterLightningIsDestroyed[DummyCastersIndex] = false
            set DummyCasterLightningRemainingDur[DummyCastersIndex] = 0
            set DummyCasterDoesLightningAttack[DummyCastersIndex] = false
            set DummyCasterIsHealing[DummyCastersIndex] = false
            set DummyCasterLeftCooldown[DummyCastersIndex] = DummyCasterNukeCooldown
            set DummyCastersSpellNumb[DummyCastersIndex] = Index
            set DummyCasterFloatingDirection[DummyCastersIndex] = (360 / GetSheepNumb(GetUnitAbilityLevel(Caster[Index], SpellID))) * i
            set TempLoc2X = PolarProjectionX(TempLocX, DummyCasterDistanceToCross, DummyCasterFloatingDirection[DummyCastersIndex])
            set TempLoc2Y = PolarProjectionY(TempLocY, DummyCasterDistanceToCross, DummyCasterFloatingDirection[DummyCastersIndex])
            set CasterDummyFacing = AngleBetweenPointsXY(TempLoc2X, TempLoc2Y, TempLocX, TempLocY)
            set DummyCasters[DummyCastersIndex] = CreateUnit(GetOwningPlayer(Caster[Index]), DummyCasterID, TempLoc2X, TempLoc2Y, CasterDummyFacing)
            call SetUnitFlyHeight(DummyCasters[DummyCastersIndex], BaseHeight, 0)
            call SetUnitFlyHeight(DummyCasters[DummyCastersIndex], HeightToGo, (BaseHeight - HeightToGo) / DownRisingTime)
            set i = i + 1
            exitwhen i > GetSheepNumb(GetUnitAbilityLevel(Caster[Index], SpellID))
        endloop
    endif
    return false
endfunction

private function Loop takes nothing returns boolean
    local integer i = 1
    local integer j = 1
    local location TempLoc
    local location TempLoc2
    local real TempLocX
    local real TempLocY
    local real TempLoc2X
    local real TempLoc2Y
    local unit UnitTarget

    if (Index > 0) then
        loop
            if (IsActive[i]) then
                set TimeLeft[i] = TimeLeft[i] - 0.03
                
                // Set the position of cross
                if (FloatingDirection[i] < 360) then
                    set FloatingDirection[i] = FloatingDirection[i] + CrossRotatingSpeed
                else
                    set FloatingDirection[i] = FloatingDirection[i] - 360
                endif
                set TempLocX = PolarProjectionX(GetUnitX(Caster[i]), 200, FloatingDirection[i])
                set TempLocY = PolarProjectionY(GetUnitY(Caster[i]), 200, FloatingDirection[i])
                call SetUnitX(Cross[i], TempLocX)
                call SetUnitY(Cross[i], TempLocY)
                
                set j = 1
                loop
                    if (DummyCastersSpellNumb[j] == i) then
                        if (DummyCasterFloatingDirection[j] > 0) then
                            set DummyCasterFloatingDirection[j] = DummyCasterFloatingDirection[j] - DummyCasterRotatingSpeed
                        else
                            set DummyCasterFloatingDirection[j] = DummyCasterFloatingDirection[j] + 360
                        endif
                        set TempLoc2X = PolarProjectionX(TempLocX, DummyCasterDistanceToCross, DummyCasterFloatingDirection[j])
                        set TempLoc2Y = PolarProjectionY(TempLocY, DummyCasterDistanceToCross, DummyCasterFloatingDirection[j])
                        call SetUnitX(DummyCasters[j], TempLoc2X)
                        call SetUnitY(DummyCasters[j], TempLoc2Y)
                        call SetUnitFacing(DummyCasters[j], AngleBetweenPointsXY(TempLoc2X, TempLoc2Y, TempLocX, TempLocY))
                    endif
                    set j = j + 1
                    exitwhen j > DummyCastersIndex
                endloop
                
                if (TimeLeft[i] < (Length - DownRisingTime)) and (TimeLeft[i] > UpRisingTime) then // if cross is rised down
                    set j = 1
                    loop
                        if (DummyCastersSpellNumb[j] == i) then
                            set DummyCasterLeftCooldown[j] = DummyCasterLeftCooldown[j] - 0.03
                            if (DummyCasterLeftCooldown[j] <= 0) then
                                set bj_lastCreatedUnit = Caster[i]
                                set UnitTarget = GetClosestUnitInRange(GetUnitX(DummyCasters[j]), GetUnitY(DummyCasters[j]), DummyCasterNukeRange, Filter(function IsUnitViableTarget))
                                if (UnitTarget != null) then
                                    set DummyCasterLeftCooldown[j] = DummyCasterNukeCooldown
                                    if (IsUnitEnemy(UnitTarget, GetOwningPlayer(Caster[i]))) then
                                        if (GetRandomInt(0, 1) == 1) then
                                            set DummyCasterLightningAttack[j] = AddLightningEx(LightningDamage1, true, GetUnitX(DummyCasters[j]), GetUnitY(DummyCasters[j]), GetUnitFlyHeight(DummyCasters[j]), GetUnitX(UnitTarget), GetUnitY(UnitTarget), GetUnitFlyHeight(UnitTarget))
                                        else
                                            set DummyCasterLightningAttack[j] = AddLightningEx(LightningDamage2, true, GetUnitX(DummyCasters[j]), GetUnitY(DummyCasters[j]), GetUnitFlyHeight(DummyCasters[j]), GetUnitX(UnitTarget), GetUnitY(UnitTarget), GetUnitFlyHeight(UnitTarget))
                                        endif
                                        set DummyCasterLightningIsDestroyed[j] = false
                                        set DummyCasterLightningRemainingDur[j] = DummyCasterAttackLength
                                        set DummyCasterDoesLightningAttack[j] = true
                                        set DummyCasterTargetedUnit[j] = UnitTarget
                                        set DummyCasterIsHealing[j] = false
                                        call PlaySoundAtPoint(DamagingSoundPath, GetUnitX(UnitTarget), GetUnitY(UnitTarget))
                                    else
                                        if not IsUnitOwnedByPlayer(UnitTarget, Player(15)) then
                                            if (GetRandomInt(0, 1) == 1) then
                                                set DummyCasterLightningAttack[j] = AddLightningEx(LightningHeal1, true, GetUnitX(DummyCasters[j]), GetUnitY(DummyCasters[j]), GetUnitFlyHeight(DummyCasters[j]), GetUnitX(UnitTarget), GetUnitY(UnitTarget), GetUnitFlyHeight(UnitTarget))
                                            else
                                                set DummyCasterLightningAttack[j] = AddLightningEx(LightningHeal2, true, GetUnitX(DummyCasters[j]), GetUnitY(DummyCasters[j]), GetUnitFlyHeight(DummyCasters[j]), GetUnitX(UnitTarget), GetUnitY(UnitTarget), GetUnitFlyHeight(UnitTarget))
                                            endif
                                            set DummyCasterLightningIsDestroyed[j] = false
                                            set DummyCasterLightningRemainingDur[j] = DummyCasterAttackLength
                                            set DummyCasterDoesLightningAttack[j] = true
                                            set DummyCasterTargetedUnit[j] = UnitTarget
                                            set DummyCasterIsHealing[j] = true
                                            call PlaySoundAtPoint(HealingSoundPath, GetUnitX(UnitTarget), GetUnitY(UnitTarget))
                                        endif
                                    endif
                                    set UnitTarget = null
                                endif
                            endif
                            if (DummyCasterDoesLightningAttack[j]) then
                                if (IsUnitAlive(DummyCasterTargetedUnit[j])) then
                                    set DummyCasterLightningRemainingDur[j] = DummyCasterLightningRemainingDur[j] - 0.03
                                    if (DummyCasterLightningRemainingDur[j] <= 0) then
                                        set DummyCasterDoesLightningAttack[j] = false
                                        if not DummyCasterLightningIsDestroyed[j] then
                                            set DummyCasterLightningIsDestroyed[j] = true
                                            call DestroyLightning(DummyCasterLightningAttack[j])
                                        endif
                                        set DummyCasterTargetedUnit[j] = null
                                    else
                                        if not DummyCasterLightningIsDestroyed[j] then
                                            set TempLoc = GetUnitLoc(DummyCasters[j])
                                            set TempLoc2 = GetUnitLoc(DummyCasterTargetedUnit[j])
                                            call MoveLightningEx(DummyCasterLightningAttack[j], false, GetUnitX(DummyCasters[j]), GetUnitY(DummyCasters[j]), GetUnitFlyHeight(DummyCasters[j]) + GetLocationZ(TempLoc), GetUnitX(DummyCasterTargetedUnit[j]), GetUnitY(DummyCasterTargetedUnit[j]), GetUnitFlyHeight(DummyCasterTargetedUnit[j]) + GetLocationZ(TempLoc2))
                                            call RemoveLocation(TempLoc)
                                            call RemoveLocation(TempLoc2)
                                        endif
                                    endif
                                    
                                    if (DummyCasterIsHealing[j]) then
                                        if (GetWidgetLife(DummyCasterTargetedUnit[j]) / GetUnitState(DummyCasterTargetedUnit[j], UNIT_STATE_MAX_LIFE) * 100) != 100 then
                                            call SetWidgetLife(DummyCasterTargetedUnit[j], GetWidgetLife(DummyCasterTargetedUnit[j]) + ((GetHealing(GetUnitAbilityLevel(Caster[i], SpellID)) / DummyCasterAttackLength) * 0.03))
                                        endif
                                    else
                                        call UnitDamageTarget(Caster[i], DummyCasterTargetedUnit[j], (GetDamage(GetUnitAbilityLevel(Caster[i], SpellID)) / DummyCasterAttackLength) * 0.03, true, false, AttackType, DamageType, WEAPON_TYPE_WHOKNOWS)
                                    endif
                                else
                                    if not DummyCasterLightningIsDestroyed[j] then
                                        set DummyCasterLightningIsDestroyed[j] = true
                                        call DestroyLightning(DummyCasterLightningAttack[j])
                                    endif
                                    set DummyCasterLightningRemainingDur[j] = 0
                                    set DummyCasterTargetedUnit[j] = null
                                    set DummyCasterDoesLightningAttack[j] = false
                                endif
                            endif
                        endif
                        set j = j + 1
                        exitwhen j > DummyCastersIndex
                    endloop
                endif
                
                if (TimeLeft[i] < UpRisingTime and TimeLeft[i] > 0) then // if cross is rising up
                    if not FloatedUp[i] then
                        set FloatedUp[i] = true
                        call SetUnitFlyHeight(Cross[i], BaseHeight, (BaseHeight - HeightToGo) / UpRisingTime)
                        set j = 1
                        loop
                            if (DummyCastersSpellNumb[j] == i) then
                                if not DummyCasterLightningIsDestroyed[j] then
                                    set DummyCasterLightningIsDestroyed[j] = true
                                    call DestroyLightning(DummyCasterLightningAttack[j])
                                endif
                                set DummyCasterTargetedUnit[j] = null
                                call SetUnitFlyHeight(DummyCasters[j], BaseHeight, (BaseHeight - HeightToGo) / UpRisingTime)
                            endif
                            set j = j + 1
                            exitwhen j > DummyCastersIndex
                        endloop
                    endif
                endif
                
                if (TimeLeft[i] <= 0) then //Finish
                    set IsActive[i] = IsActive[Index]
                    set IsActive[Index] = false
                    call RemoveUnit(Cross[i])
                    set Cross[i] = Cross[Index]
                    set Cross[Index] = null
                    set Caster[i] = Caster[Index]
                    set Caster[Index] = null
                    set TimeLeft[i] = TimeLeft[Index]
                    set TimeLeft[Index] = 0
                    set FloatingDirection[i] = FloatingDirection[Index]
                    set FloatingDirection[Index] = 0
                    set FloatedUp[i] = FloatedUp[Index]
                    set FloatedUp[Index] = false

                    set j = 1
                    loop
                        if (DummyCastersSpellNumb[j] == Index) then
                            set DummyCastersSpellNumb[j] = i
                            set DummyCastersSpellNumb[DummyCastersIndex] = 0
                            if not DummyCasterLightningIsDestroyed[j] then
                                set DummyCasterLightningIsDestroyed[j] = true
                                call DestroyLightning(DummyCasterLightningAttack[j])
                            endif
                            call RemoveUnit(DummyCasters[j])
                            set DummyCasters[j] = DummyCasters[DummyCastersIndex]
                            set DummyCasters[DummyCastersIndex] = null
                            set DummyCasterTargetedUnit[j] = DummyCasterTargetedUnit[DummyCastersIndex]
                            set DummyCasterTargetedUnit[DummyCastersIndex] = null
                            set DummyCasterFloatingDirection[j] = DummyCasterFloatingDirection[DummyCastersIndex]
                            set DummyCasterFloatingDirection[DummyCastersIndex] = 0
                            set DummyCasterLeftCooldown[j] = DummyCasterLeftCooldown[DummyCastersIndex]
                            set DummyCasterLeftCooldown[DummyCastersIndex] = 0
                            set DummyCasterLightningAttack[j] = DummyCasterLightningAttack[DummyCastersIndex]
                            set DummyCasterLightningAttack[DummyCastersIndex] = null
                            set DummyCasterLightningRemainingDur[j] = DummyCasterLightningRemainingDur[DummyCastersIndex]
                            set DummyCasterLightningRemainingDur[DummyCastersIndex] = 0
                            set DummyCasterDoesLightningAttack[j] = DummyCasterDoesLightningAttack[DummyCastersIndex]
                            set DummyCasterDoesLightningAttack[DummyCastersIndex] = false
                            set DummyCasterTargetedUnit[j] = DummyCasterTargetedUnit[DummyCastersIndex]
                            set DummyCasterTargetedUnit[DummyCastersIndex] = null
                            set DummyCasterIsHealing[j] = DummyCasterIsHealing[DummyCastersIndex]
                            set DummyCasterIsHealing[DummyCastersIndex] = false
                            set DummyCasterLightningIsDestroyed[j] = DummyCasterLightningIsDestroyed[DummyCastersIndex]
                            set DummyCasterLightningIsDestroyed[DummyCastersIndex] = true 
                            
                            set DummyCastersIndex = DummyCastersIndex - 1
                            set j = j - 1
                        endif
                        set j = j + 1
                        exitwhen j > DummyCastersIndex
                    endloop
                    
                    set Index = Index - 1
                    set i = i - 1
                endif
            endif
            set i = i + 1
            exitwhen i > Index
        endloop
    endif
    return false
endfunction

private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    local trigger t2 = CreateTrigger()
    
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function Actions))
    
    call TriggerRegisterTimerEvent( t2, 0.03, true)
    call TriggerAddCondition( t2, Condition(function Loop))
    
    set t = null
    set t2 = null
endfunction
endlibrary // PaladinHolyCross

Keywords:
holy, cross, paladin, arthas, sheep, flying, rotating, rotate, heal, thunder, wave, floating, float, heaven, hell
Contents

Untitled (Map)

Reviews
12th Dec 2015 IcemanBo: Too long as NeedsFix. Rejected. 15:17, 14th Jun 2014 BPower: review: http://www.hiveworkshop.com/forums/spells-569/cross-heaven-1-2-a-251720/#post2541444 note: cool concept

Moderator

M

Moderator

12th Dec 2015
IcemanBo: Too long as NeedsFix. Rejected.

15:17, 14th Jun 2014
BPower:
review:
http://www.hiveworkshop.com/forums/spells-569/cross-heaven-1-2-a-251720/#post2541444
note: cool concept
 
Never use location in jass / vJass unless you are trying to get the Z height at the location.
Don't use SetUnitLocation. Use SetUnitX/Y

Remove the BJs. ( the red function calls above.)

All local handles need to be nulled at end of function.

This does not check if unit is alive accurately.
IsUnitAliveBJ
You need to do this.
GetWidgetLife( u) > .405 and not IsUnitType( u, UNIT_TYPE_DEAD) and UnitTypeId( u) != 0

Never use TriggerAddAction. Use TriggerAddCondition and run everything from there.

Take a look at my tutorial Converting GUI to efficient JASS
It will show you how to make efficient Jass code.
 
Level 4
Joined
Dec 13, 2010
Messages
71
Nice to know those things, actually :)
Well, I will fix it tomorrow, I guess.
So I will use this function for checking if a unit is alive:
JASS:
function IsUnitAlive takes unit u returns boolean
    return (GetWidgetLife(u) > .405 and not IsUnitType( u, UNIT_TYPE_DEAD) and GetUnitTypeId(u) != 0)
endfunction
Is it correct?
 
This
JASS:
    if not HealsCasterToo then
        if (GetFilterUnit() == bj_lastCreatedUnit) then
            return false
        endif
    endif
Should be this.
JASS:
    if not HealsCasterToo and GetFilterUnit() == bj_lastCreatedUnit then
        return false
    endif
Get rid of this BJ TriggerRegisterTimerEventPeriodic
In you actions function you use this multiple times. GetTriggerUnit()
You already store it in a variable. The variable should be used. Along with any other instances that you did this.

You use an old form of indexing / de-indexing that needs to be changed as this is inefficient and will hit op-limit. The new method of indexed arrays is in my tutorial Thins you should know when using triggers / GUI. The chapter on how to index.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Most times spells should be a scope and not a library. This is such a case.

HolyCrossFunctions is not listed as requirement in the holycross library.

In actions this:
JASS:
        set TempEffect = AddSpecialEffectTarget(EffectModel, Caster[Index], "origin")
//......
        call DestroyEffect(TempEffect)
should be call DestroyEffect(AddSpecialEffectTarget(EffectModel, Caster[Index], "origin")) In function actions: Store the ability level and the TriggerPlayer(Owner) into a local integer variable. In function loop: Either use one global location to determine the z value or you have to null the two local locations used. You have needless GetUnitX/Y function calls since the value is already stored into TempLoc2X/Y Handles of all types should be nulled once they are not used anymore. This also includes lightnings. [code=jass]function IsUnitAlive takes unit u returns boolean return (GetWidgetLife(u) > .405 and not IsUnitType( u, UNIT_TYPE_DEAD) and GetUnitTypeId(u) != 0) endfunction[/code] ---> [code=jass]function IsUnitAlive takes unit u returns boolean return not IsUnitType( u, UNIT_TYPE_DEAD) and GetUnitTypeId(u) != 0) endfunction[/code] or declare [icode=jass]native UnitAlive takes unit id returns boolean

The loop is running even though there is no active spell instance. Please ensure the timer is stopped once there is no active instance running.

Sometimes the lightinings are not created properly.
 
Top