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

[JASS] call CountUnitsInGroupEnum()

Status
Not open for further replies.
Level 10
Joined
Jan 21, 2007
Messages
576
Need some direction on this...

Ok so right now I'm just practicing using jass but I am kind of stuck first off here is the code I have:

JASS:
function check takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == 'A000' ) ) then
        return false
    endif
    return true
endfunction

function unitfilter takes nothing returns boolean
    if ( not ( IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(GetTriggerUnit())) == true ) ) then
        return false
    endif
    return true
endfunction

function action takes nothing returns nothing
        local group UIR
        local location loc  = GetUnitLoc(GetTriggerUnit())
        local integer i
        local real x
        local real y
        local real r        =300.0
        set x               =GetLocationX(loc)
        set y               =GetLocationY(loc)
        call GroupClear(UIR)
        call GroupEnumUnitsInRange(UIR, x, y, r, Condition(function unitfilter))
        call CountUnitsInGroupEnum()
        call DisplayTextToPlayer(Player(0), 0, 0, I2S(CountUnitsInGroup(UIR)))
    endfunction

function InitTrig takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t, Condition(function check))
        call TriggerAddAction(t, function action)
    endfunction

All I'm trying to do is when a unit casts a custom thunderclap (A000) have it count the number of enemy units and display it to player 1.. but as of right now nothing happens, and I am unsure of what to change. Anyone have any thought as to what I should do?
 
Last edited:
Level 10
Joined
Jan 21, 2007
Messages
576
Alright I changed up my code, creating the group. But something is wrong with my condition because it isn't even displaying the new text check I added (can someone tell me the thing to do the hude/unhide button I always forget that...) but heres my code now.

JASS:
function check takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == 'A000' ) ) then
        return false
    endif
    return true
endfunction

function unitfilter takes nothing returns boolean
    if ( not ( IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(GetTriggerUnit())) == true ) ) then
        return false
    endif
    return true
endfunction

function action takes nothing returns nothing
        local group UIR = CreateGroup()
        local location loc  = GetUnitLoc(GetTriggerUnit())
        local integer i
        local real x        =GetLocationX(loc)
        local real y        =GetLocationY(loc)
        local real r        =300.0
        call DisplayTextToPlayer(Player(0) 0, 0, "Spell Casted")
        call GroupEnumUnitsInRange(UIR, x, y, r, Condition(function unitfilter))
        call DisplayTextToPlayer(Player(0), 0, 0, I2S(CountUnitsInGroup(UIR)))
    endfunction

function InitTrig takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t, Condition(function check))
        call TriggerAddAction(t, function action)
    endfunction
 
Level 29
Joined
Jul 29, 2007
Messages
5,174
Can't find any problem, but you should improve the code.

JASS:
function check takes nothing returns boolean
    return GetSpellAbilityId() == 'A000'
endfunction

function unitfilter takes nothing returns boolean
    return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(GetTriggerUnit())) 
endfunction

function action takes nothing returns nothing
    local group UIR = CreateGroup()
    local integer i
    local unit u = GetTriggerUnit()
    local real x =GetUnitX(u )
    local real y =GetUnitY(u )
    local real r =300.0

    call DisplayTextToPlayer(Player(0) 0, 0, "Spell Casted")
    call GroupEnumUnitsInRange(UIR, x, y, r, Condition(function unitfilter))
    call DisplayTextToPlayer(Player(0), 0, 0, I2S(CountUnitsInGroup(UIR)))
        
    call DestroyGroup(UIR)
    set UIR = null
    set u = null
endfunction

function InitTrig takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t, Condition(function check))
        call TriggerAddAction(t, function action)
    endfunction
 
Level 10
Joined
Jan 21, 2007
Messages
576
Alright I solved the problem, the initilizer of the trigger was messed up. Here is my code now:

JASS:
function check takes nothing returns boolean
    return GetSpellAbilityId() == 'A000'
endfunction

function unitfilter takes nothing returns boolean
    return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(GetTriggerUnit()))
endfunction

function action takes nothing returns nothing
        local group UIR = CreateGroup()
        local location loc  = GetUnitLoc(GetTriggerUnit())
        local real x        =GetUnitX(u)
        local real y        =GetUnitY(u)
        local real r        =300.0
        local unit u        = GetTriggerUnit()
        call GroupEnumUnitsInRange(UIR, x, y, r, Condition(function unitfilter))
        call DisplayTextToPlayer(Player(0), 0, 0, "Number of enemy units within "+R2S(r)+" units = "+I2S(CountUnitsInGroup(UIR)))
        
        call DestroyGroup(UIR)
        set u = null
        set UIR = null
    endfunction

function InitTrig_Pulse takes nothing returns nothing
    set gg_trg_Pulse = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Pulse, EVENT_PLAYER_UNIT_SPELL_CAST )
    call TriggerAddCondition( gg_trg_Pulse, Condition( function check ) )
    call TriggerAddAction( gg_trg_Pulse, function action )
endfunction

Is that as optimized as it can be?
 
Level 14
Joined
Nov 23, 2008
Messages
187
1. Not at all, you can count enemy units directly in filter function.
2. Also, you don't need to create and destroy group every time trigger fires.
3. Locations should be removed after use. But you have no need to use locations in this code, I think.

JASS:
function check takes nothing returns boolean
  return GetSpellAbilityId() == 'A000'
endfunction

function unitfilter takes nothing returns boolean
  if IsUnitEnemy(GetFilterUnit(), udg_player) then
    set udg_count = udg_count + 1
  endif
  return false
endfunction

function action takes nothing returns nothing
  local unit u = GetTriggerUnit()
  local real x = GetUnitX(u)
  local real y = GetUnitY(u)
  local real r = 300.0
        
  set udg_count = 0
  set udg_player = GetOwningPlayer(u)
  call GroupEnumUnitsInRange(udg_group, x, y, r, Condition(function unitfilter))
  call DisplayTextToPlayer(Player(0), 0, 0, "Number of enemy units within "+R2S(r)+" = "+I2S(udg_count))

  set u = null
endfunction

function InitTrig_Pulse takes nothing returns nothing
  local trigger tr = CreateTrigger()
  call TriggerRegisterAnyUnitEventBJ(tr, EVENT_PLAYER_UNIT_SPELL_CAST)
  call TriggerAddCondition(tr, Condition(function check))
  call TriggerAddAction(tr, function action)

  set udg_group = CreateGroup()
endfunction

// udg_group is Group global variable
// udg_count is integer global variable

Now it looks quite optimized.
 
Level 11
Joined
Apr 6, 2008
Messages
760
if you have vJass, use scope or library with initializer instead of inittrig

JASS:
scope Name initializer Init

globals
    private constant integer Abil_id = 'A000'
    private integer count = 0
    private player player
endglobals

private function check takes nothing returns boolean
  return GetSpellAbilityId() == Abil_id
endfunction

private function unitfilter takes nothing returns boolean
  if IsUnitEnemy(GetFilterUnit(), player) then
    set count = count + 1
  endif
  return false
endfunction

private function action takes nothing returns nothing
  local unit u = GetTriggerUnit()
local real x = GetUnitX(u)
  local real y = GetUnitY(u)
  local real r = 300.0
  
  set Count = 0
  set player = GetOwningPlayer(u)
  call GroupEnumUnitsInRange(udg_group, x, y, r, Condition(function unitfilter))
  call DisplayTextToPlayer(Player(0), 0, 0, "Number of enemy units within "+R2S(r)+" = "+I2S(udg_count))

  set u = null
endfunction

private function Init takes nothing returns nothing
  local trigger tr = CreateTrigger()
  call TriggerRegisterAnyUnitEventBJ(tr, EVENT_PLAYER_UNIT_SPELL_CAST)
  call TriggerAddCondition(tr, Condition(function check))
  call TriggerAddAction(tr, function action)
endfunction

endscope
 
Level 10
Joined
Jan 21, 2007
Messages
576
EDIT: Alright I converted it to be more 'vjassy' but now my loop doesn't work.
JASS:
scope FrostNova initializer Init

globals
    private constant integer Abil_Id = 'A000'
    private integer count = 0
    private player p
endglobals

function check takes nothing returns boolean
    return GetSpellAbilityId() == Abil_Id
endfunction

private function unitfilter takes nothing returns boolean
  if IsUnitEnemy(GetFilterUnit(), p) then
    set count = count + 1
  endif
  return false
endfunction

function action takes nothing returns nothing
        local unit u        =GetTriggerUnit()
        local group g       =CreateGroup()
        local real x        =GetUnitX(u)
        local real y        =GetUnitY(u)
        local real r        =200.000
        local unit t        =GetSpellTargetUnit()
        local real d        =60.000 * I2R(GetUnitAbilityLevelSwapped('A000', GetTriggerUnit()))
        set count           =0
        call UnitDamageTarget( u, t, d, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
        set d               =30.000 * I2R(GetUnitAbilityLevelSwapped('A000', GetTriggerUnit())) 
        call GroupEnumUnitsInRange(g, x, y, r, Condition(function unitfilter))
loop
    exitwhen (count<=0)
        set t = FirstOfGroup(g)
        call UnitDamageTarget( u, t, d, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null) 
        set count=count-1
        call GroupRemoveUnit(g, t)
    endloop
        set t = null
    endfunction

private function Init takes nothing returns nothing
  local trigger tr = CreateTrigger()
  call TriggerRegisterAnyUnitEventBJ(tr, EVENT_PLAYER_UNIT_SPELL_EFFECT)
  call TriggerAddCondition(tr, Condition(function check))
  call TriggerAddAction(tr, function action)
endfunction

endscope
 
Last edited:
Level 10
Joined
Jan 21, 2007
Messages
576
Your logic is the secks Eleandor lol. (Editing. . .)

EDIT: For some reason when I change this (The loop):
JASS:
scope FrostNova initializer Init

globals
    private constant integer Abil_Id = 'A000'
    private integer count = 0
    private player p
endglobals

function check takes nothing returns boolean
    return GetSpellAbilityId() == Abil_Id
endfunction

function unitfilter takes nothing returns boolean
    return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(GetTriggerUnit()))
endfunction

function action takes nothing returns nothing
        local unit u        =GetTriggerUnit()
        local unit t        =GetSpellTargetUnit()
        local group g       =CreateGroup()
        local real x        =GetUnitX(t)
        local real y        =GetUnitY(t)
        local real r        =200.000
        local real d        =60.000 * I2R(GetUnitAbilityLevelSwapped('A000', GetTriggerUnit()))
        set p               = GetOwningPlayer(GetTriggerUnit())
        call UnitDamageTarget( u, t, d, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
        set d               =30.000 * I2R(GetUnitAbilityLevelSwapped('A000', GetTriggerUnit())) 
        call GroupEnumUnitsInRange(g, x, y, r, Condition(function unitfilter))
        set count           =CountUnitsInGroup(g)

loop
    exitwhen (count<=0)
        set t = FirstOfGroup(g)
        call UnitDamageTarget( u, t, d, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null) 
        set count=count-1
        call GroupRemoveUnit(g, t)
    endloop
        set t = null
    endfunction

private function Init takes nothing returns nothing
  local trigger tr = CreateTrigger()
  call TriggerRegisterAnyUnitEventBJ(tr, EVENT_PLAYER_UNIT_SPELL_EFFECT)
  call TriggerAddCondition(tr, Condition(function check))
  call TriggerAddAction(tr, function action)
endfunction

endscope

To this:
JASS:
scope FrostNova initializer Init

globals
    private constant integer Abil_Id = 'A000'
    private integer count = 0
    private player p
endglobals

function check takes nothing returns boolean
    return GetSpellAbilityId() == Abil_Id
endfunction

function unitfilter takes nothing returns boolean
    return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(GetTriggerUnit()))
endfunction

function action takes nothing returns nothing
        local unit u        =GetTriggerUnit()
        local unit t        =GetSpellTargetUnit()
        local group g       =CreateGroup()
        local real x        =GetUnitX(t)
        local real y        =GetUnitY(t)
        local real r        =200.000
        local real d        =60.000 * I2R(GetUnitAbilityLevelSwapped('A000', GetTriggerUnit()))
        set p               = GetOwningPlayer(GetTriggerUnit())
        call UnitDamageTarget( u, t, d, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
        set d               =30.000 * I2R(GetUnitAbilityLevelSwapped('A000', GetTriggerUnit())) 
        call GroupEnumUnitsInRange(g, x, y, r, Condition(function unitfilter))
        set count           =CountUnitsInGroup(g)

loop
    exitwhen t == null
    set t = FirstOfGroup(g)
    call GroupRemoveUnit(g, t)
    call UnitDamateTarget( u, t, d, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
endloop

private function Init takes nothing returns nothing
  local trigger tr = CreateTrigger()
  call TriggerRegisterAnyUnitEventBJ(tr, EVENT_PLAYER_UNIT_SPELL_EFFECT)
  call TriggerAddCondition(tr, Condition(function check))
  call TriggerAddAction(tr, function action)
endfunction

endscope

This line gets a syntex error:
JASS:
private function Init takes nothing returns nothing
Even though I'm not touching it....
 
Level 29
Joined
Jul 29, 2007
Messages
5,174
It's not like you HAVE to add useless things...

And by the way, you simply forgot a endfunction.

JASS:
scope FrostNova initializer Init

function check takes nothing returns boolean
    return GetSpellAbilityId() ==  'A000'
endfunction

function action takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local unit t = GetSpellTargetUnit()
    local group g = CreateGroup()
    local real x = GetUnitX(t)
    local real y = GetUnitY(t)
    local real r = 200
    local real d = 60 * GetUnitAbilityLevel(u,'A000')
    local player p = GetOwningPlayer(u)
        
    call UnitDamageTarget(u, t, d, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
    set d = d/2
    call GroupEnumUnitsInRange(g,x,y r,null)

    loop  
        set t = FirstOfGroup(g)
        exitwhen t == null
        call GroupRemoveUnit(g, t)
    
        if IsUnitEnemy(t,p) then
            call UnitDamageTarget( u, t, d, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
        endif
    endloop

    call DestroyGroup(g)
    set g = null
    set u = null
    // should p be nulled?
endfunction

private function Init takes nothing returns nothing
  local trigger tr = CreateTrigger()
  call TriggerRegisterAnyUnitEventBJ(tr, EVENT_PLAYER_UNIT_SPELL_EFFECT)
  call TriggerAddCondition(tr, Condition(function check))
  call TriggerAddAction(tr, function action)
endfunction

endscope
 
Level 10
Joined
Jan 21, 2007
Messages
576
Ghostwolf, alright that seems to be optimized to its fullest and the cleanest (I am also unaware of if you have to null p or not >.>). It had 2 syntex errors but I fixed those. To those interested in the finished product:
JASS:
scope FrostNova initializer Init

function check takes nothing returns boolean
    return GetSpellAbilityId() ==  'A000'
endfunction

function action takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local unit t = GetSpellTargetUnit()
    local group g = CreateGroup()
    local real x = GetUnitX(t)
    local real y = GetUnitY(t)
    local real r = 200
    local real d = 60 * GetUnitAbilityLevel(u,'A000')
    local player p = GetOwningPlayer(u)

    call UnitDamageTarget(u, t, d, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
    set d = d/2
    call GroupEnumUnitsInRange(g,x,y,r,null)

    loop
        exitwhen t == null
        set t = FirstOfGroup(g)
        call GroupRemoveUnit(g, t)

        if IsUnitEnemy(t,p) then
            call UnitDamageTarget( u, t, d, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
        endif
    endloop

    call DestroyGroup(g)
    set g = null
    set u = null
    // should p be nulled?
endfunction

private function Init takes nothing returns nothing
  local trigger tr = CreateTrigger()
  call TriggerRegisterAnyUnitEventBJ(tr, EVENT_PLAYER_UNIT_SPELL_EFFECT)
  call TriggerAddCondition(tr, Condition(function check))
  call TriggerAddAction(tr, function action)
endfunction

endscope

Thanks to everyone who helped me, I know it is annoying to have to deal with people trying to learn something and I'm glad you all stuck around and we finished this. I'm really happy with what I came out with ;]. Rep all around!
 
Level 21
Joined
Aug 21, 2005
Messages
3,699
JASS:
loop
        exitwhen t == null
        set t = FirstOfGroup(g)
        call GroupRemoveUnit(g, t)

        if IsUnitEnemy(t,p) then
            call UnitDamageTarget( u, t, d, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
        endif
    endloop

You need to swap "exitwhen" and "set t = FirstOfGroup()"...

JASS:
loop
        set t = FirstOfGroup(g)
        exitwhen t == null
        call GroupRemoveUnit(g, t)

        if IsUnitEnemy(t,p) then
            call UnitDamageTarget( u, t, d, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
        endif
    endloop
 
Level 14
Joined
Nov 23, 2008
Messages
187
I think, picking method is not effective. Loop should be slower than using ForGroup, well, ForGroup in fact is slower than GroupEnum.

swdn said:
Also, you don't need to create and destroy group every time trigger fires.
I'd recommend to make non-trigger objects "static" (i.e. created once).

swdn said:
you can count enemy units directly in filter function.
You can do anything (or almost anything) in filters, what you usually do in loops or ForGroup's:

JASS:
scope FrostNove initializer Init

globals
  group g = CreateGroup()
  boolexpr bx = null
  // . . .
endglobals

private function unitfilter takes nothing returns boolean
  if IsUnitEnemy(GetFilterUnit(), p) then
    call UnitDamageTarget(u, t, d, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
    set count = count + 1
    // >> something else what you need to do...
    // . . .
  endif
  return false
endfunction
// u, t, d, p, count are globals

function action takes nothing returns nothing
// . . .
  // >> setting u, t, d, p, count here
  call GroupEnumUnitsInRange(g, x, y, r, bx)
// . . .
endfunction

private function Init takes nothing returns nothing
  // . . .
  set bx = Filter(function unitfilter)
endfunction

endscope

should p be nulled?
No, players are cached in memory.
 
Level 29
Joined
Jul 29, 2007
Messages
5,174
You can do anything (or almost anything) in filters, what you usually do in loops or ForGroup's

Not at all. The only two values you can have in them is the enum unit and triggering unit, which isn't enough for many things.
Never liked the idea of adding another useless function anyway.

No, players are cached in memory.

Yes but it's another pointer (or is it?).
 
Level 21
Joined
Aug 21, 2005
Messages
3,699
Yes but it's another pointer (or is it?).

Pointers *never* have to be nulled.
Handles in theory don't have to be nulled either, but since there's a reference bug in jass handles, you do need to null them.
One of the only exceptions there are players: as there are never any players created or destroyed you don't care about broken references, so you "don't have to" null player handles. But I think it's a good habit to simply null all handles.
 
Level 14
Joined
Nov 23, 2008
Messages
187
GhostWolf said:
Not at all. The only two values you can have in them is the enum unit and triggering unit, which isn't enough for many things.

Never liked the idea of adding another useless function anyway.

1. There's an opinion, that you haven't understood me. I meant that what you can do with picked (filter, enum) unit in loop, the same you can do in filter function. E.g. damaging, attaching effects, moving or whatever.

2. Do you think the less functions you have, the better you code is? Yes, it would be slightly faster, but not readable. Some functions should, some not, some cannot be inlined. Each jasser decides for himself, what functions to inline.
 
Level 29
Joined
Jul 29, 2007
Messages
5,174
Pointers *never* have to be nulled.

Excuse me good sir, but are you not nulling your units and location variables?

Do you think the less functions you have, the better you code is? Yes, it would be slightly faster, but not readable. Some functions should, some not, some cannot be inlined. Each jasser decides for himself, what functions to inline.

I said -useless- functions.
 
Status
Not open for further replies.
Top