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

How to Count a UnitGroup?

Status
Not open for further replies.
Level 15
Joined
Nov 30, 2007
Messages
1,202
Pick units that are alive and of type X and an integer "count units in group" will let you know.

meh, it will be about 30 units to pick thorugh - will look horrible.

  • Actions
    • Set i = 0
    • Custom script: set bj_wantDestroyGroup = true
    • Unit Group - Pick every unit in (Units in (Playable map area)) and do (Actions)
      • Loop - Actions
        • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • ((Picked unit) is alive) Equal to True
            • (Unit-type of (Picked unit)) Equal to Granary
          • Then - Actions
            • Set i = (i + 1)
          • Else - Actions
    • Game - Display to (All players) the text: (String(i))
Like this? In most cases I only need to detect if its greater then 0.
 
"var" is your group of units.
JASS:
globals
    integer count = 0
    group var = CreateGroup()
    group iter = CreateGroup()
    group temp
endglobals

function CountUnitsIntGroup takes nothing returns integer
    local unit u
    set count = 0

    loop
        set u = FirstOfGroup(var)
        exitwhen u == null

        if ( IsUnitAlive(u) ) then
            set count = count + 1
        endif

        call GroupRemoveUnit(var, u)
        call GroupAddUnit(iter, u)
    endloop

    set temp = var
    set var = iter
    set iter = temp

    return count
endfunction
 
Last edited:
Level 15
Joined
Nov 30, 2007
Messages
1,202
Another thing these variables are stored 0 to 11 for each player. A dummy unit is created if the counted number of counted units is greater then 0. Is it dumb to use so many variables for this or ok?

pCityTech_c_Granary [] (unit)
pCityTech_c_Well [] (unit)
pCityTech_c_Market [] (unit)
pCityTech_c_House [] (unit)
pCityTech_c_2_House [] (unit)
pCityTech_c_3_House [] (unit)
pCityTech_c_4_House [] (unit)
pCityTech_c_Temple [] (unit)
pCityTech_c_Academy [] (unit)
pCityTech_c_Estate [] (unit)
pCityTech_m_Garrison [] (unit)
pCityTech_m_Blacksmith [] (unit)
pCityTech_m_Barracks [] (unit)
pCityTech_m_Archery [] (unit)
pCityTech_m_Stable [] (unit)

Basically i need to search TownGroup[CustomValueOf(GetTriggerUnit()] for all the above units in the CityTech list. and then create the dummy units if they find a match.

Event: Selection/Deselection: (Search thorugh entire list: then add/remove CityTech.
Event: Finnish Construction/Dies: Look for if tech of finnished unit is not on then create a dummy tech unit / remove a dummy tech unit if it doesn't match anymore.

Can one merge selection/deselection event into one trigger and detect which event was done? Same for Death/Finnish constructing.
 
First, it's not Banner but Bannar. Second, your approach with group as parameter fails, leaving group empty after procedure - and thats what we do not want to do.
In place of "IsUnitAlive" (which doesn't really exist) he should place any filter he wants :)
Sorry for "Banner". ^^ Why it fails if you work with an backup group? And yes, he wants to filter unitType, that's why integer parameter would make sense, it was just a suggestion for your example.
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
You could use a vector or table structure instead. Use 1D index as typeId and a 2D one as actual count.

Could u show me? Make two types: Peasant and footmen for two payers Player red and blue.

JASS:
function AddStructureTechDummy takes nothing returns nothing

endfunctin

function RemoveStructureTechDummy takes nothing returns nothing

endfunction

function Completed_Destroyed_Structure takes nothing returns boolean
    local unit u = GetTriggerUnit()
    local integer p_num = GetPlayerId(GetOwningPlayer(u))


    if (IsUnitType((u), UNIT_TYPE_STRUCTURE) == true) then

        set u = GetConstructedStructure()

        if (u != null) then // STRUCTURE COMPLETED

            if (IsUnitInGroup(u), udg_CityGroup[udg_BuilderCurCity[p_num]]) == true) then
                // SEARCH FOR TECH AND ADD IF NOT FOUND
            endif

        else // STRUCTURE DIED   
            set u = GetTriggerUnit()
            if (IsUnitInGroup(u), udg_CityGroup[udg_BuilderCurCity[p_num]]) == true) then
                // SEARCH FOR TECH AND REMOVE IF FOUND
            endif

        endif
    endif
    set u = null
return false
endfunction



//===========================================================================
function InitTrig_Completed_or_Destroyed_Structure takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH )
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_CONSTRUCT_FINISH)
    call TriggerAddCondition(t, Condition( function Completed_Destroyed_Structure))
    set t = null
endfunction

If what you say works I should be able to hook it up with this.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
JASS:
//NOTE!: if you have JNGP, you can uncomment the following block
//globals
    //integer udg_countUnitsCounter = 0
    //integer udg_countUnitsTypeId = 0
//endglobals

//If you dont have JNGP, you should create global
//variable with name "countUnitsCounter", which should be of type Integer
//variable with anme "countUnitsTypeId", which should be of type Integer

function CountUnitsLooper134 takes nothing returns boolean
    local unit u = GetEnumUnit()
    if IsUnitType(u, UNIT_TYPE_ALIVE) and GetUnitTypeId(u) == udg_countUnitsTypeId then //I know there are better ways, but meh
        set udg_countUnitsCounter = udg_countUnitsCounter + 1
    endif
    set u = null
endfunction

function CountLivingUnits takes group g, integer ofType returns integer
    set udg_countUnitsCounter = 0
    set udg_countUnitsTypeId = ofType
    call ForGroup(g, function CountUnitsLooper134)
    return udg_countUnitsCounter
endfunction

and it wont modify the source group. Obviously this is in Jass so kind of hard to use for GUI users
 
Could u show me? Make two types: Peasant and footmen for two payers Player red and blue.

JASS:
function Completed_Destroyed_Structure takes nothing returns boolean
    local unit u = GetTriggerUnit()
    if (IsUnitType((u), UNIT_TYPE_STRUCTURE) == true) then

        set u = GetConstructedStructure()

        if (u != null) then // STRUCTURE COMPLETED
            //ADD UNIT TO TECH_LIST IF MATCHING TOWN IS CURRENTLY BEING SELECTED

        else // STRUCTURE DIED   
            //REMOVE UNIT TO TECH_LIST IF MATCHING TOWN IS CURRENTLY BEING SELECTED

        endif
    endif
    set u = null
return false
endfunction



//===========================================================================
function InitTrig_Completed_or_Destroyed_Structure takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH )
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_CONSTRUCT_FINISH)
    call TriggerAddCondition(t, Condition( function Completed_Destroyed_Structure))
    set t = null
endfunction

If what you say works I should be able to hook it up with this.


I suggest using the following instead
JASS:
if GetTriggerEventId() == EVENT_PLAYER_UNIT_DEATH then
//actions
else
//alternative actions
endif
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
My solution also doesn't modify group and, is much quicker than ForGroup because it doesn't start new threads, as you probably now.
However, it might not be as easy to use.

Will try it. But perhaps starting a new thread would make it look cleaner, why is it so bad to start a new thread?

JASS:
function StartCancelConstruction takes nothing returns boolean
    local unit u = GetTriggerUnit()
    local integer p_num = GetPlayerId(GetTriggerPlayer())
    local integer i = 0
    local integer owningCity
    local integer u_id
    local integer countLast
    local integer count

    if (IsUnitType(u, UNIT_TYPE_STRUCTURE) == true) then
        loop
            exitwhen udg_BuildingOrder[i] == null
            if (GetUnitTypeId(u) == udg_BuildingType[i]) then

                set u_id = GetUnitUserData(u)
                set owningCity = udg_City_BuildingOwner[u_id]

                // Count here: set countLast = Unit Group (udg_City_ConstructionGroup)

                if (GetTriggerEventId() == EVENT_PLAYER_UNIT_CONSTRUCT_START) then
                
                    set udg_City_BuildingOwner[u_id] = owningCity 
                    call GroupAddUnit(udg_City_Group[owningCity], u) 
                    call GroupAddUnit(udg_City_ConstructionGroup[owningCity], u) 
                 
                elseif (GetTriggerEventId() == EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL) then
                    call GroupRemoveUnit(udg_City_ConstructionGroup[udg_City_BuildingOwner[u_id]], u)

                elseif (GetTriggerEventId() == EVENT_PLAYER_UNIT_CONSTRUCT_FINISH) then
                    call GroupRemoveUnit(udg_City_ConstructionGroup[owningCity], u)

                elseif (GetTriggerEventId() == EVENT_PLAYER_UNIT_DEATH and IsUnitInGroup(u, udg_City_ConstructionGroup[owningCity]) == true ) then
                    call GroupRemoveUnit(udg_City_ConstructionGroup[owningCity], u)

                endif

                // Count here: set count = Unit Group (udg_City_ConstructionGroup)

                if (count >= udg_City_ConstructingLimit) then

                elseif (countLast >= 5 and count < 5) then
 
                endif

            endif
            set i = i + 1
        endloop
    endif
    set u = null
    return false
endfunction



//===========================================================================
function InitTrig_CC_Start_Cancel_Finnish_Death takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL)
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_CONSTRUCT_START)
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_CONSTRUCT_FINISH)
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
    call TriggerAddCondition( t, Condition( function StartCancelConstruction) )
    set t = null
endfunction

If there is a boolean for unit is being built I could change the groups to a integer and just ++ and -- at the same place where they are added/removed from group or keep the group and add a integer as well.
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
Because starting threads have overhead ops, which makes code slower to execute. Simply put, it's faster if you don't need to open another one, aside from your main thread.
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
"var" is your group of units.
JASS:
globals
    integer count = 0
    group var = CreateGroup()
    group iter = CreateGroup()
    group temp
endglobals

function CountUnitsIntGroup takes nothing returns integer
    local unit u
    set count = 0

    loop
        set u = FirstOfGroup(var)
        exitwhen u == null

        if ( IsUnitAlive(u) ) then
            set count = count + 1
        endif

        call GroupRemoveUnit(var, u)
        call GroupAddUnit(iter, u)
    endloop

    set temp = var
    set var = iter
    set iter = temp

    return count
endfunction

Don't fully understand what you did. Here is a test setup:
JASS:
library Setup initializer init 

    globals 
        group array CityBuildingGroup
        group array CityGroup_being_built 
        integer array CityBuilding_type
        private timer t
    endglobals

    private function run takes nothing returns nothing
        local integer i = 0
        call PauseTimer(t)
        call DestroyTimer(t)
        set t = null
        set CityBuildingGroup[0] = CreateGroup()
        call GroupAddGroup( GetUnitsInRectAll(GetPlayableMapRect()), CityBuildingGroup[0])
        set CityBuilding_type[0] = 'hhou' // farm
        set CityBuilding_type[1] = 'halt' // altar
        set CityBuilding_type[2] = 'hbar' // Baracks
        set CityBuilding_type[3] = 'hlum' // lumber mill
        
        loop
            exitwhen CityBuilding_type[i] == null 
            call BJDebugMsg(UnitId2StringBJ(CityBuilding_type[i]) + " = " + I2S(CountBuildings(0,i)))    
            set i = i + 1
        endloop
endfunction
    
    private function init takes nothing returns nothing
        set t = CreateTimer() 
        call TimerStart(t, 0.1, false, function run)
    endfunction
endlibrary

JASS:
library CountBuildings initializer init 
    function CountBuildings takes integer city_num, integer b_type_num returns integer 
        local unit u
        local integer count = 0 
        local group g1 = CreateGroup()
        set g1 = CityBuildingGroup[city_num]
        loop
            set u = FirstOfGroup(g1)
            exitwhen u == null
            if (GetUnitTypeId(u) == CityBuilding_type[b_type_num] and IsUnitInGroup(u, CityGroup_being_built[city_num]) == false) then 
                set count = count + 1
            endif
            call GroupRemoveUnit(g1, u) 
        endloop
        call DestroyGroup(g1)
        set u = null 
        return count 
    endfunction

    private function init takes nothing returns nothing 
    endfunction
endlibrary

It only counts the first one...

When I test it pritns:
farm 3
altar 0
barracks 0
lumbermill 0

it should print:
farm 3
altar 1
barracks 1
lumbermill 0

it counts the first run. The other times CityBuildingGroup[0] has become empty for some reason; can someone explain to me why?

*EDIT:

Works now, coppied you exactly no idea what var = temp, temp = iter, iter = var does though but something good...

JASS:
library CountBuildings initializer init 
    function CountBuildings takes integer city_num, integer b_type_num returns integer 
        local unit u
        local integer count = 0 
        local group iter = CreateGroup()
        local group temp = CreateGroup()
        
        loop
            set u = FirstOfGroup(CityBuildingGroup[city_num])
            exitwhen u == null
            if (GetUnitTypeId(u) == CityBuilding_type[b_type_num] and IsUnitInGroup(u, CityGroup_being_built[city_num]) == false) then 
                set count = count + 1
            endif
            
            call GroupRemoveUnit(CityBuildingGroup[city_num], u) 
            call GroupAddUnit(iter, u)
        endloop
        
        set temp = CityBuildingGroup[city_num]
        set CityBuildingGroup[city_num] = iter
        set iter = temp
        
        return count 
    endfunction

    private function init takes nothing returns nothing 
    endfunction
endlibrary
Should I change anything?
 
Last edited:
Status
Not open for further replies.
Top