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

[JASS] Global Group Problem

Status
Not open for further replies.
Level 5
Joined
Aug 30, 2009
Messages
114
Hi there, I need some help understanding why is this causing lag when multiple enemy units are targeted...

JASS:
scope RainOfChaos initializer Init

private function Cond1 takes nothing returns boolean
        return(GetUnitState(GetFilterUnit(), UNIT_STATE_LIFE) > 0 and GetUnitAbilityLevel(GetFilterUnit(), 'Aloc') == 0 and GetUnitAbilityLevel(GetFilterUnit(), 'B012') > 0)
endfunction

private function Cond2 takes nothing returns boolean
        return(GetUnitState(GetFilterUnit(), UNIT_STATE_LIFE) > 0 and GetUnitAbilityLevel(GetFilterUnit(), 'Aloc') == 0 and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(GetEnumUnit())) and GetUnitAbilityLevel(GetFilterUnit(), 'A044') > 0)
endfunction

private function GroupAct takes nothing returns nothing
    local unit U = GetEnumUnit()
    local location L = GetUnitLoc(U)
    local group G = CreateGroup()
    local unit T
    local real Dmg = 30.00
    local effect Fx
    call GroupEnumUnitsInRangeOfLoc(G, L, 600.00, Condition(function Cond2))
    set T = FirstOfGroup(G)
    if T != null then
        set Dmg = Dmg * CountUnitsInGroup(G)
        set Fx = AddSpecialEffectTarget("Abilities\\Spells\\NightElf\\Immolation\\ImmolationDamage.mdl", U, "head")
        call UnitDamageTargetEx(T, U, Dmg, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_SPELL, false)
        call DestroyEffect(Fx)
    endif
    set T = null
    call DestroyGroup(G)
    call RemoveLocation(L)
    set U = null
endfunction

private function Actions takes nothing returns nothing
    local group Gr = CreateGroup()
    call GroupEnumUnitsInRect(Gr, GetPlayableMapRect(), Condition(function Cond1))
    call ForGroup(Gr, function GroupAct)
    call DestroyGroup(Gr)
endfunction

////////////////////////////////////////////////////////////////////////////////////////////

private function LearnAct takes nothing returns nothing
    local timer T = CreateTimer()
    call TimerStart(T, 1.00, true, function Actions)
    
    set T = null
    call DestroyTrigger(GetTriggeringTrigger())
endfunction

private function LearnCond takes nothing returns boolean
    return GetLearnedSkill() == 'A03H'
endfunction

private function Init takes nothing returns nothing
    local trigger T = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(T, EVENT_PLAYER_HERO_SKILL)
    call TriggerAddCondition(T, Condition(function LearnCond))
    call TriggerAddAction(T, function LearnAct)
    set T = null
endfunction

////////////////////////////////////////////////////////////////////////////////////////////
endscope

Everything works fine, Infernals with stacking Permanent Immolation aura, but when I come across many enemy units, it lags!!!

I've downloaded many maps that uses global grouping like I did, and none of them lag...

Can anyone tell me if I'm doing something wrong? Or just give me some solution? Please, don't give the solution to the spell itself, there are many different ways I could be using to create a stacking permanent immolation, but what I want is to UNDERSTAND why is it lagging and how can I fix it!!!

I heard about aura struct, that it could be able to target many units I want at once without lagging, is that right? And what is the difference between it and what I'm doing?

Thank you.
 
Level 33
Joined
Apr 24, 2012
Messages
5,113
First,you must null the group.
[jass=]set G = null
set GR = null[/code]
Second,All of the JASS types should be nulled,except reals,integers,strings etc.
So the SFX,Location,Group etc should be nulled.

Tips:
Using EnumUnitsInRangeofLoc is very bad,also for Group.Just use EnumUnitsInRange and use plane coordinates instead.
In vJASS,we have to use Coordinates instead of Locations because we are using JASS.(Coordinates(x,y) are much faster and leakless than locations)

Also,Your Script is somewhat like converted from GUI to JASS then changed those names of the functions and just added a scope.Are you a beginner?

Also use Library instead of Scope.And also use Struct
 
Level 5
Joined
Aug 30, 2009
Messages
114
Yes, I'm a beginner, I'm trying to understand and learn vJass.

How can I use library or struct instead of scope???

I've seen many maps created by scripters who use scope.

I have many snippets in my map, does it lag too?


**EDIT **

I did what you said, it have reduced the lag, but is still lagging every 1 sec when nearby many enemy units, do you have any other ideas?

Thank you for your help already, I'm very grateful
 
Last edited:
Leaks aren't problem,
1st of all code won't compile.
2nd GroupEnum native can't pick units with locust so just remove that condition.
3rd will be to replace ForGroup with FirstOfGroup
4th use X,Y coordinates over location

I don't understand what your spell do, but it seams when you learn some spell it will start timer and each 1 sec deal damage to all units around you eh?

Well there are many other different ways how to code this, I will now post your code and below mine idea:

JASS:
library RainOfChaos initializer Init

    globals
        private group G1 = CreateGroup()
        private group G2 = CreateGroup()
    endglobals

    private function Iterate takes nothing returns nothing
        local unit u1
        local unit u2
        local real x
        local real y
        /* We pick all units */
        call GroupEnumUnitsInRect(G1, bj_mapInitialPlayableArea, null)
        loop
            set u1 = FirstOfGroup(G1)
            exitwhen u1 == null
            call GroupRemoveUnit(G1,u1)
        
            /* Here we check conditions 1 */
            if GetUnitState(u1, UNIT_STATE_LIFE) > 0 and GetUnitAbilityLevel(u1, 'B012') > 0 then
                set x = GetUnitX(u1)
                set y = GetUnitY(u1)
                call GroupEnumUnitsInRange(G2, x, y, 600, null)
                loop
                    set u2 = FirstOfGroup(G2)
                    exitwhen u2 == null
                    call GroupRemoveUnit(G2,u2)
                    
                    /* Here we check conditions 2 */
                    if GetUnitState(u2, UNIT_STATE_LIFE) > 0 and IsUnitEnemy(u2, GetOwningPlayer(u1)) and GetUnitAbilityLevel(u2, 'A044') > 0 then
                        call UnitDamageTarget(u2, u1, 30.00, false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_MAGIC, null)
                        call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\NightElf\\Immolation\\ImmolationDamage.mdl", u2, "head"))
                    endif
                    
                endloop
            endif
        endloop
    endfunction

    private function Run takes nothing returns nothing
        if GetLearnedSkill() == 'A03H' then
           call TimerStart(CreateTimer(), 1.00, true, function Iterate)
        endif
    endfunction

    private function Init takes nothing returns nothing
        local trigger trig = CreateTrigger()
        local integer i = 0
        loop
            call TriggerRegisterPlayerUnitEvent(trig, Player(i), EVENT_PLAYER_HERO_SKILL, null)
            set i = i + 1
            exitwhen i == 12
        endloop
        call TriggerAddAction(trig, function Run)
        set trig = null
    endfunction

endlibrary

JASS:
library RainOfChaos initializer Init

    globals
        //Used to pick units around hero
        private group G = CreateGroup() 
        //Run each 1 sec until end of time
        private timer T = CreateTimer() 
        //Here we store units with skill
        private unit array U 
        //How many units we stored
        private integer Counter = 0 
    endglobals

    private function Iterate takes nothing returns nothing
        local unit u
        local real x
        local real y
        
        local integer i = 0
        loop
            //Loop from 0 to number of heroes we stored into array U
            exitwhen i == Counter 
            set i = i + 1
            call GroupEnumUnitsInRange(G, GetUnitX(U[i]), GetUnitY(U[i]), 600, null)
            loop
                //We pick random unit and link it with u
                set u = FirstOfGroup(G) 
                //If there is no unit to pick from group u will equal null and both group and unit variable leak is cleaned
                exitwhen u == null 
                //We remove unit from group so we can't pick it again
                call GroupRemoveUnit(G,u) 
                    
                // Your shit
                if GetUnitState(u, UNIT_STATE_LIFE) > 0 and IsUnitEnemy(u, GetOwningPlayer(U[i])) then
                    call UnitDamageTarget(U[i], u, 30.00, false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_MAGIC, null)
                    call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\NightElf\\Immolation\\ImmolationDamage.mdl", u, "head"))
                endif
    
            endloop
        endloop
    endfunction
    
    private function Run takes nothing returns nothing
        if GetLearnedSkill() == 'A03H' then
            //We set U[ number ] = unit
            set U[Counter] = GetTriggerUnit()
            //We increase number for example U[1] = Hero 1, U[2] = Hero 2 and so on
            set Counter = Counter + 1
            //This is cool way how to be sure to fire trigger just once, when first unit learn skill Counter will be 1 and timer will fire
            //When 2nd learn skill Counter will increase and this will be ignored until end of time :)
            if Counter == 1 then
                call TimerStart(T, 1.00, true, function Iterate)
            endif
        endif
    endfunction

    private function Init takes nothing returns nothing
        /* Basic shit */
        local trigger trig = CreateTrigger()
        local integer i = 0
        loop
            call TriggerRegisterPlayerUnitEvent(trig, Player(i), EVENT_PLAYER_HERO_SKILL, null)
            set i = i + 1
            exitwhen i == 12
        endloop
        call TriggerAddAction(trig, function Run)
        set trig = null
    endfunction

endlibrary

As you can see mine idea is little faster, you won't pick all units on whole map each 1 sec, instead you just do loop with max number of heroes with learned skill.
Try it it should work fine :)
 
Level 5
Joined
Aug 30, 2009
Messages
114
Thanks for you help, but I tested both codes, and now it lags even MORE when I'm in the middle of many units!!! I don't know what else to do!!! I'm trying to learn this, but it's hard!!! I mean, I'm not considering the spell itself, I just want to know a way to pick many units per second and do something with them without lagging too hard, in this case, I'm picking every units affected by an aura, and then looking for nearby aura sources and make the source damage the target!!

** EDIT **

I almost forgot to say, it ONLY lags when I'm in the middle of MANY enemy units, and with many, I mean 20, maybe 30 or more.
I can't just create a global variable that holds the aura source, because I want many units to have this aura, and I want them stacking!!! Also, the aura source in this case is an infernal, which can be summoned!! Don't tell me I can simply use "Permanent Immolation" ability, I'm doing this for LEARNING only, trying to find a way to create this spell.
 
Level 5
Joined
Aug 30, 2009
Messages
114
I used the first one, the ONLY modification I made was instead of using

UnitDamageTarget

I used

UnitDamageTargetEx
from IntuitiveDamageSystem.

I tested with 4 infernals against some grunts, witch doctors and headhunters.

** EDIT **

I also changed the

library RainOfChaos initializer Init

to

library RainOfChaos initializer Init requires IntuitiveDamageSystem

And the learning condition I kept mine

JASS:
////////////////////////////////////////////////////////////////////////////////////////////

private function LearnAct takes nothing returns nothing
    local timer T = CreateTimer()
    local trigger trg = CreateTrigger()
    call TimerStart(T, 1.00, true, function Actions)
    
    call TriggerAddAction(trg, function ResistanceActions)
    call TriggerAddCondition(trg, Condition(function ResistanceConditions))
    call TriggerRegisterDamageEvent(trg, 5)
    
    set T = null
    set trg = null
    call DestroyTrigger(GetTriggeringTrigger())
endfunction

private function LearnCond takes nothing returns boolean
    return GetLearnedSkill() == 'A03H'
endfunction

private function Init takes nothing returns nothing
    local trigger T = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(T, EVENT_PLAYER_HERO_SKILL)
    call TriggerAddCondition(T, Condition(function LearnCond))
    call TriggerAddAction(T, function LearnAct)
    set T = null
endfunction

////////////////////////////////////////////////////////////////////////////////////////////

Because it also starts a damage condition, but I have many of these and I'm pretty sure the problem is not there!! I got another units with this same ability to resist damage and it caused me no trouble.

I'm uploading the map so you can see it for yourself, and made the changes... The triggers might be a little confused, but look for Spells category, under the Warlock comment, Rain of Chaos.

Ignore the other heroes and the map, I'm just creating this to test my skills and learn new things.

If I made any mistakes, please tell me!!
 

Attachments

  • New Project.w3x
    774.6 KB · Views: 42
Try this, it works fine for me :)
JASS:
library RainOfChaos initializer Init uses IntuitiveDamageSystem

    globals
        private group G      = CreateGroup() 
        private group Caster = CreateGroup() 
        private timer T      = CreateTimer()
    endglobals
    
    private function Iterate takes nothing returns nothing
        local unit caster = GetEnumUnit()
        local unit u
        call GroupEnumUnitsInRange(G, GetUnitX(caster), GetUnitY(caster), 600, null)
        loop
            set u = FirstOfGroup(G) 
            exitwhen u == null 
            call GroupRemoveUnit(G,u) 
            if GetUnitState(caster, UNIT_STATE_LIFE) > 0 and GetUnitState(u, UNIT_STATE_LIFE) > 0 and IsUnitEnemy(u, GetOwningPlayer(caster)) then
                call UnitDamageTargetEx(caster, u, 30.00, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_SPELL, false)
                call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\NightElf\\Immolation\\ImmolationDamage.mdl", u, "head"))
            endif
        endloop
        set caster = null
    endfunction
    
    private function Run takes nothing returns nothing
        call ForGroup(Caster, function Iterate)
    endfunction
    
    private function RegisterUnit takes nothing returns nothing
        if GetUnitAbilityLevel(GetTriggerUnit(), 'A044') > 0 then
            call GroupAddUnit(Caster, GetTriggerUnit())
        endif
    endfunction
    
    private function FlushUnit takes nothing returns nothing
        if GetUnitAbilityLevel(GetTriggerUnit(), 'A044') > 0 then
            call GroupRemoveUnit(Caster, GetTriggerUnit())
        endif
    endfunction
    
    /***********************************************************************************/
    /*                               Useless code                                      */
    /*                   This below will register already created units                */
    /***********************************************************************************/
    /******/private function RemoveOnly_RegisterUnit takes nothing returns nothing/*****/
    /**********/if GetUnitAbilityLevel(GetEnumUnit(), 'A044') > 0 then/*****************/
    /**************/call GroupAddUnit(Caster, GetEnumUnit())/***************************/
    /**********/endif/******************************************************************/
    /******/endfunction/****************************************************************/
    /***********************************************************************************/
    
    private function Init takes nothing returns nothing
        local trigger trig = CreateTrigger()
        local region rectRegion = CreateRegion()
        local integer i = 0
        call RegionAddRect(rectRegion, bj_mapInitialPlayableArea)
        call TriggerRegisterEnterRegion(trig, rectRegion, null)
        call TriggerAddAction(trig, function RegisterUnit)
        set trig = CreateTrigger()
        loop
            call TriggerRegisterPlayerUnitEvent(trig, Player(i), EVENT_PLAYER_UNIT_DEATH, null)
            set i = i + 1
            exitwhen i == 12
        endloop
        call TriggerAddAction(trig, function FlushUnit)
        set trig = null
        call TimerStart(T, 1.00, true, function Run)
        
        /* 
        * This below will register already created units
        */
        call ForGroupBJ( GetUnitsInRectAll(GetPlayableMapRect()), function RemoveOnly_RegisterUnit )
        
    endfunction

endlibrary

Ofc just remove that part of useless code.
:)

EDIT:

Here is my final suggestion:
JASS:
library RainOfChaos initializer Init uses IntuitiveDamageSystem

    globals
        private group G      = CreateGroup() 
        private group Caster = CreateGroup() 
        private timer T      = CreateTimer()
        
        private boolean TurnedOff = true //Timer is disabled on start
    endglobals
    
    private function Iterate takes nothing returns nothing
        local unit caster = GetEnumUnit()
        local unit u
        call GroupEnumUnitsInRange(G, GetUnitX(caster), GetUnitY(caster), 600, null)
        loop
            set u = FirstOfGroup(G) 
            exitwhen u == null 
            call GroupRemoveUnit(G,u) 
            if GetUnitState(caster, UNIT_STATE_LIFE) > 0 and GetUnitState(u, UNIT_STATE_LIFE) > 0 and IsUnitEnemy(u, GetOwningPlayer(caster)) then
                call UnitDamageTargetEx(caster, u, 30.00, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_SPELL, false)
                call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\NightElf\\Immolation\\ImmolationDamage.mdl", u, "head"))
            endif
        endloop
        set caster = null
    endfunction
    
    private function Run takes nothing returns nothing
        call ForGroup(Caster, function Iterate)
    endfunction
    
    private function RegisterUnit takes nothing returns nothing
        if GetUnitAbilityLevel(GetTriggerUnit(), 'A044') > 0 then
            call GroupAddUnit(Caster, GetTriggerUnit())
            if TurnedOff then
                call TimerStart(T, 1.00, true, function Run)
                set TurnedOff = false
            endif
        endif
    endfunction
    
    private function FlushUnit takes nothing returns nothing
        local unit u
        if GetUnitAbilityLevel(GetTriggerUnit(), 'A044') > 0 then
            call GroupRemoveUnit(Caster, GetTriggerUnit())
            set u = FirstOfGroup(Caster)
            if u == null then
                set TurnedOff = true
                call PauseTimer(T)
            endif
        endif
        set u = null
    endfunction
    
    private function Init takes nothing returns nothing
        local trigger trig = CreateTrigger()
        local region rectRegion = CreateRegion()
        local integer i = 0
        call RegionAddRect(rectRegion, bj_mapInitialPlayableArea)
        call TriggerRegisterEnterRegion(trig, rectRegion, null)
        call TriggerAddAction(trig, function RegisterUnit)
        set trig = CreateTrigger()
        loop
            call TriggerRegisterPlayerUnitEvent(trig, Player(i), EVENT_PLAYER_UNIT_DEATH, null)
            set i = i + 1
            exitwhen i == 12
        endloop
        call TriggerAddAction(trig, function FlushUnit)
        set trig = null
    endfunction

endlibrary

Timer will fire only when there is at least 1 alive infernal unit.
 
I hope you learned more about this, I checked your other triggers, you more or less make same mistakes all around, but that's not big deal, I was like you as well.
Try to avoid locations completely, also Red code is bad in 99 cases, only trigger register shit is good (but even this can be improved)...
Pooled wait is big error.
Use timer for that :)

here is cool idea how to fix many things:
JASS:
globals
    private integer readInteger = 0
    private integer writeInteger = 0
    
    private unit array U
endglobals

function B takes nothing returns nothing
    set readInteger = readInteger + 1
    call KillUnit( U[readInteger] )
    if readInteger == writeInteger then
        set readInteger = 0
        set writeInteger = 0
    endif
    call DestroyTimer(GetExpiredTimer())
endfunction

function A takes nothing returns nothing
    set writeInteger = writeInteger  + 1
    set U[writeInteger] = GetTriggerUnit()
    call TimerStart(CreateTimer(), 4.00, false, function B )
endfunction
 
Last edited:
Level 14
Joined
Apr 20, 2009
Messages
1,543
JASS:
call TimerStart(CreateTimer(), 4.00, false, function A )

The callback is the same function instead of B.
That will probably cause some issues or confusions :p
 
Level 5
Joined
Aug 30, 2009
Messages
114
PolledWait leaks????

I only used the Red code sometimes for DistanceBetweenPoints, AngleBetweenPoints or PolarProjection, how can I improve this?

This Idea of yours is to replace PolledWait?

** EDIT **

Oh, and I also use red code for UnitHasItemOfTypeBJ
 
Status
Not open for further replies.
Top