- Joined
- Nov 7, 2014
- Messages
- 571
I think this is an easy workaround for the problem discussed here, which has "bitten" at least few people before.
JASS:
library FirstOfGroup2 initializer init
// Notes:
// Iterating the native group type (which seems to be a doubly-linked list of units) using
// the FirstOfGroup native is faster than using ForGroup (which uses a callback function and GetEnumUnit())
// or at least its more convenient because one can see/use the local variables that are in scope.
// But it has a small caveat which is that when units get removed (either explicitly with the
// RemoveUnit native or implicitly when they die and after a while they decay) they persist in groups
// that they were added to as "ghosts" for which FirstOfGroup returns null but ForGroup skips over.
// What FirstOfGroup2 does is to detect when a "ghost" is returned by FirstOfGroup and then clears the
// group of all the "ghosts" using the GroupRefresh function. We can detect if FirstOfGroup returns a ghost
// or not by adding a non-hero unit to the group (with GroupAddUnit) and test if FirstOfGroup returns
// that unit or a "ghost", if it returns the unit that we added then we've reached the end of the group,
// otherwise if it returns null (a "ghost") then we GroupRefresh the group, remove the unit that we added
// and return FirstOfGroup.
// The reason we use a non-hero unit is because GroupAddUnit adds heros to the "head" of
// the doubly-linked list but it adds non-hero units to the "tail" of the list.
// It only makes sense to use FirstOfGroup2 instead of FirstOfGroup when iterating groups
// that could have "ghosts"; it doesn't make sense to use FirstOfGroup2 when we
// GroupEnumUnits* and then immediately iterate the group with FirstOfGroup because
// in doing so the group becomes empty.
// Credits:
// Captain Griffen (GroupRefresh): http://www.wc3c.net/showthread.php?t=101810
globals
private constant integer DUMMY_ID = 'hpea' // can't be a hero! =)
private constant real DUMMY_HOME_X = -2048.0
private constant real DUMMY_HOME_Y = 2048.0
endglobals
globals
private unit dummy
endglobals
private function init takes nothing returns nothing
set dummy = CreateUnit(Player(15), DUMMY_ID, DUMMY_HOME_X, DUMMY_HOME_Y, 270.0)
call UnitAddAbility(dummy, 'Aloc') // unselectable
call UnitAddAbility(dummy, 'Avul') // invulnerable
call PauseUnit(dummy, true)
call ShowUnit(dummy, false)
endfunction
globals
private boolean clear_ghosts
private group gg
endglobals
private function group_enum takes nothing returns nothing
if clear_ghosts then
call GroupClear(gg)
set clear_ghosts = false
endif
call GroupAddUnit(gg, GetEnumUnit())
endfunction
private function group_refresh takes group g returns nothing
set clear_ghosts = true
set gg = g
call ForGroup(gg, function group_enum)
if clear_ghosts then
// group had only ghosts
call GroupClear(g)
endif
endfunction
globals
private unit result
endglobals
function FirstOfGroup2 takes group g returns unit
set result = FirstOfGroup(g)
if result != null then
return result
endif
call GroupAddUnit(g, dummy)
if FirstOfGroup(g) == dummy then
call GroupRemoveUnit(g, dummy)
set result = null
return null
endif
call group_refresh(g)
call GroupRemoveUnit(g, dummy)
return FirstOfGroup(g)
endfunction
endlibrary