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

[JASS] Spell causing a huge lag

Status
Not open for further replies.
I've recreated a spell of mine and it is completly finished, but when casting it on units it creates a huge lag. It's supposed to create two missles, each flying on it's own path and damaging units once. If a unit gets struck by both, a special effect should happen, but that's not inserted fully at the moment.

However, if I cast it on three creeps the game lags like hell for this moment:
JASS:
scope ChillingStreams initializer InitStreams

    private keyword Streams

    globals
        private constant    integer SPELL_ID        = 'A000'
        private constant    integer STUN_ID         = 'A001'
        private constant    integer BUFF_ID         = 'B000'
        
        private constant    real    PERIOD          = 0.04
        
        private constant    string  SFX_PATH        = "Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl"
        private constant    string  SFX_ATTACH      = "chest"
        
        private constant    real    SPEED           = 400.
        
        private constant    real    SIDEDIST        = 100.
        private constant    real    COLLISION       = 100.
        
        private constant    integer MISSLE_COUNT    = 2
        
        private constant    real    STEP            = SPEED*PERIOD
        
        private             Streams TempStream
        private             integer Current
        
        private integer test = 0
    endglobals
    
    
    private constant function GetMaxDist takes integer lvl returns real
        return 500.+lvl*200.
    endfunction
    
    private constant function GetDamage takes integer lvl returns real
        return lvl*70.
    endfunction
    

    private struct Streams
        private unit            caster
        private player          owner
        
        private unit    array   missle[MISSLE_COUNT]
        private effect  array   sfx[MISSLE_COUNT]
        private real    array   vecx[MISSLE_COUNT]
        private real    array   vecy[MISSLE_COUNT]
        
        private group   array   hit[MISSLE_COUNT]
        
        private real            dist
        private real            maxdist
        
        private real            damage
        
        private timer           movetim
        
        
        method onDestroy takes nothing returns nothing
            local integer i = 0
            
            set .caster = null
            loop
                exitwhen i >= MISSLE_COUNT
                call ReleaseDummy(.missle[i])
                call DestroyEffect(.sfx[i])
                set .sfx[i] = null
                call ReleaseGroup(.hit[i])
                set .hit[i] = null
                set i = i+1
            endloop
            call ReleaseTimer(movetim)
        endmethod
        
        static method collisionCheck takes nothing returns boolean
            local thistype this = TempStream
            local unit u = GetFilterUnit()
            local unit dummy
            local boolean second = false
            
            local integer i = 0
            
            loop
                exitwhen i >= MISSLE_COUNT
                
                if IsUnitInGroup(u, .hit[i]) then
                    if i != Current then
                        set second = true
                    else
                        return false
                    endif
                else
                    set i = i+1
                endif
            endloop
            
            set test = test+1
            call BJDebugMsg(I2S(test))
            call BJDebugMsg(GetPlayerName(.owner))
            
            if GetWidgetLife(u) > 0.405 and IsUnitEnemy(u, .owner) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) then
                call UnitDamageTarget(.caster, u, .damage, false, true, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
                call GroupAddUnit(.hit[Current], u)
                call BJDebugMsg("Damage")
                if second then
                    set dummy = NewDummy(GetUnitX(u), GetUnitY(u), 0.)
                    call UnitAddAbility(dummy, STUN_ID)
                    call IssueTargetOrder(dummy, "thunderbolt", u)
                    call ReleaseDummy(dummy)
                endif
            endif
            
            return false
        endmethod 
        
        static method movement takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            
            local real x
            local real y
            
            local integer i = 0
            
            set TempStream = this
            
            loop
                exitwhen i >= MISSLE_COUNT
                
                set x = GetUnitX(.missle[i])+.vecx[i]
                set y = GetUnitY(.missle[i])+.vecy[i]
                
                call SetUnitX(.missle[i], x)
                call SetUnitY(.missle[i], y)
                
                set Current = i                
                call GroupEnumUnitsInArea(ENUM_GROUP, x, y, COLLISION, Condition(function thistype.collisionCheck))
                
                set i = i+1
            endloop
            
            set .dist = .dist+STEP
            if .dist >= .maxdist then
                call .destroy()
            endif
        endmethod
        
        static method createStreams takes nothing returns nothing
            local thistype this = thistype.allocate()
            
            local real spellx = GetSpellTargetX() 
            local real spelly = GetSpellTargetY()
            local real x
            local real y
            local real casterx
            local real castery
            local real angle
            local real facing
            
            local integer i = 0
            
            set .caster = GetTriggerUnit()
            set casterx = GetUnitX(.caster)
            set castery = GetUnitY(.caster)
            set facing  = Atan2(spelly-castery, spellx-casterx)
            set .owner  = GetOwningPlayer(.caster)
            
            loop
                exitwhen i >= MISSLE_COUNT
                
                set facing = facing+bj_PI*(i+1)/2
                set x = PolarX(casterx, facing, SIDEDIST)
                set y = PolarY(castery, facing, SIDEDIST)
                set angle = Atan2(spelly-y, spellx-x)
                
                set .missle[i] = NewDummy(x, y, angle)
                call AddSpecialEffectTarget(SFX_PATH, .missle[i], SFX_ATTACH)
                set .vecx[i] = PolarX(0, angle, STEP)
                set .vecy[i] = PolarY(0, angle, STEP)
                
                set .hit[i] = NewGroup()
                
                set i = i+1
            endloop
                
            set .dist = 0.
            set .maxdist = GetMaxDist(GetUnitAbilityLevel(.caster, SPELL_ID))
            
            set .damage = GetDamage(GetUnitAbilityLevel(.caster, SPELL_ID))
            
            set .movetim = NewTimer()
            call SetTimerData(.movetim, this)
            call TimerStart(.movetim, PERIOD, true, function thistype.movement)
        endmethod
    endstruct

    private function InitStreams takes nothing returns nothing
        call RegisterSpellAction(SPELL_ID, Streams.createStreams)
    endfunction
endscope
For debugging, "test" counts the picked units. After 5 spells this value is around 800, seems to be ok for 0.04 evaluations per second and picks each time. The owner is also correct, but damage is displayed only once for three creeps. I also used the blizzard pick-natives, same result.

Greets, Justify
 
Last edited:
At first glance I'd think it to be just in general due to loops within timers and the collision check, but I don't know if that is necessarily it. From what I can tell, you don't have any leaks other than maybe nulling your unit vars in collisionCheck, and as far as I know boolexprs don't need to be/shouldn't be destroyed.

I'd suggest attaching a test map, but otherwise you can try // commenting out the debug messages and special effects and see if that has an impact on it. This way you can tell whether it is your computer or the actual code itself.
 
The loop was the problem, but not just because it's a loop.
In case that the unit was hit once, the second hit was an endless loop.
I didn't think of this possibility because my computer normaly completly laggs and you can end warcraft because nothing works in such situations.
However, the code isn't lagging anymore, but units can be struck twice (they get stunned) but they take damage even more often. Why?
The current instance is safed in "current" and if the unit is already in the "current" group the emthod should return false and end.

JASS:
scope ChillingStreams initializer InitStreams

    private keyword Streams

    globals
        private constant    integer SPELL_ID        = 'A000'
        private constant    integer STUN_ID         = 'A001'
        private constant    integer BUFF_ID         = 'B000'
        
        private constant    real    PERIOD          = 0.04
        
        private constant    string  SFX_PATH        = "Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl"
        private constant    string  SFX_ATTACH      = "chest"
        
        private constant    real    SPEED           = 400.
        
        private constant    real    SIDEDIST        = 100.
        private constant    real    COLLISION       = 100.
        
        private constant    integer MISSLE_COUNT    = 2
        
        private constant    real    STEP            = SPEED*PERIOD
        
        private             Streams TempStream
        private             integer Current
        
        private integer test = 0
    endglobals
    
    
    private constant function GetMaxDist takes integer lvl returns real
        return 500.+lvl*200.
    endfunction
    
    private constant function GetDamage takes integer lvl returns real
        return lvl*70.
    endfunction
    

    private struct Streams
        private unit            caster
        private player          owner
        
        private unit    array   missle[MISSLE_COUNT]
        private effect  array   sfx[MISSLE_COUNT]
        private real    array   vecx[MISSLE_COUNT]
        private real    array   vecy[MISSLE_COUNT]
        
        private group   array   hit[MISSLE_COUNT]
        
        private real            dist
        private real            maxdist
        
        private real            damage
        
        private timer           movetim
        
        
        method onDestroy takes nothing returns nothing
            local integer i = 0
            
            set .caster = null
            loop
                exitwhen i >= MISSLE_COUNT
                call ReleaseDummy(.missle[i])
                call DestroyEffect(.sfx[i])
                set .sfx[i] = null
                call ReleaseGroup(.hit[i])
                set .hit[i] = null
                set i = i+1
            endloop
            call ReleaseTimer(movetim)
        endmethod
        
        static method collisionCheck takes nothing returns boolean
            local thistype this = TempStream
            local unit u = GetFilterUnit()
            local unit dummy
            local boolean second = false
            
            local integer i = 0
            
            loop
                exitwhen i >= MISSLE_COUNT
                
                if IsUnitInGroup(u, .hit[i]) then
                    if i != Current then
                        set second = true
                        set i = MISSLE_COUNT
                    else
                        return false
                    endif
                else
                    set i = i+1
                endif
            endloop
            
            set test = test+1
            
            if GetWidgetLife(u) > 0.405 and IsUnitEnemy(u, .owner) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) then
                call UnitDamageTarget(.caster, u, .damage, false, true, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
                call GroupAddUnit(.hit[Current], u)
                if second then
                    set dummy = NewDummy(GetUnitX(u), GetUnitY(u), 0.)
                    call UnitAddAbility(dummy, STUN_ID)
                    call IssueTargetOrder(dummy, "thunderbolt", u)
                    call ReleaseDummy(dummy)
                endif
            endif
            
            set u = null
            
            return false
        endmethod 
        
        static method movement takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            
            local real x
            local real y
            
            local integer i = 0
            
            set TempStream = this
            
            loop
                exitwhen i >= MISSLE_COUNT
                
                set x = GetUnitX(.missle[i])+.vecx[i]
                set y = GetUnitY(.missle[i])+.vecy[i]
                
                call SetUnitX(.missle[i], x)
                call SetUnitY(.missle[i], y)
                
                set Current = i                
                call GroupEnumUnitsInArea(ENUM_GROUP, x, y, COLLISION, Condition(function thistype.collisionCheck))
                
                set i = i+1
            endloop
            
            set .dist = .dist+STEP
            if .dist >= .maxdist then
                call .destroy()
            endif
        endmethod
        
        static method createStreams takes nothing returns nothing
            local thistype this = thistype.allocate()
            
            local real spellx = GetSpellTargetX() 
            local real spelly = GetSpellTargetY()
            local real x
            local real y
            local real casterx
            local real castery
            local real angle
            local real facing
            
            local integer i = 0
            
            set .caster = GetTriggerUnit()
            set casterx = GetUnitX(.caster)
            set castery = GetUnitY(.caster)
            set facing  = Atan2(spelly-castery, spellx-casterx)
            set .owner  = GetOwningPlayer(.caster)
            
            loop
                exitwhen i >= MISSLE_COUNT
                
                set facing = facing+bj_PI*(i+1)/2
                set x = PolarX(casterx, facing, SIDEDIST)
                set y = PolarY(castery, facing, SIDEDIST)
                set angle = Atan2(spelly-y, spellx-x)
                
                set .missle[i] = NewDummy(x, y, angle)
                call AddSpecialEffectTarget(SFX_PATH, .missle[i], SFX_ATTACH)
                set .vecx[i] = PolarX(0, angle, STEP)
                set .vecy[i] = PolarY(0, angle, STEP)
                
                set .hit[i] = NewGroup()
                
                set i = i+1
            endloop
                
            set .dist = 0.
            set .maxdist = GetMaxDist(GetUnitAbilityLevel(.caster, SPELL_ID))
            
            set .damage = GetDamage(GetUnitAbilityLevel(.caster, SPELL_ID))
            
            set .movetim = NewTimer()
            call SetTimerData(.movetim, this)
            call TimerStart(.movetim, PERIOD, true, function thistype.movement)
        endmethod
    endstruct

    private function InitStreams takes nothing returns nothing
        call RegisterSpellAction(SPELL_ID, Streams.createStreams)
    endfunction
endscope
 
JASS:
if GetWidgetLife(u) > 0.405 and IsUnitEnemy(u, .owner) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) then

should be

JASS:
if not IsUnitInGroup(u, .hit[i]) and GetWidgetLife(u) > 0.405 and IsUnitEnemy(u, .owner) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) then

based on other threads: you should use parentheses after not because it overwrites any other checker.... your trigger would work something like this one...

JASS:
if not (IsUnitInGroup(u, .hit[i]) and GetWidgetLife(u) > 0.405 and IsUnitEnemy(u, .owner) and not IsUnitType(u, UNIT_TYPE_STRUCTURE)) then

it should be

JASS:
if (not IsUnitInGroup(u, .hit[i])) and GetWidgetLife(u) > 0.405 and IsUnitEnemy(u, .owner) and (not IsUnitType(u, UNIT_TYPE_STRUCTURE)) then
 
Status
Not open for further replies.
Top