package ClosureForGroups public interface ForGroupCallback function callback(unit u) /** Return true to continue iteration, false to stop. */ public interface ForGroupCallbackUntil function callback(unit u) returns bool /** This is used as dummy in the GroupEnum calls and should not be used */ constant DUMMY_GROUP = CreateGroup() // ====Callback stack for nested usage==== ForGroupCallback array tempCallbacks ForGroupCallbackUntil array tempCallbacksUntil int array callbackMode int array countLimits int array countIters bool array stopRequested var tempCallbacksCount = 0 constant filter = Filter(() -> filterCallback(GetFilterUnit())) constant MODE_NORMAL = 0 constant MODE_COUNTED = 1 constant MODE_UNTIL = 2 function filterCallback(unit filter) let idx = tempCallbacksCount - 1 let mode = callbackMode[idx] if mode == MODE_NORMAL currentCallback().callback(filter) else if mode == MODE_COUNTED if countIters[idx] < countLimits[idx] currentCallback().callback(filter) countIters[idx]++ else if not stopRequested[idx] if not currentCallbackUntil().callback(filter) stopRequested[idx] = true function pushCallback(ForGroupCallback c) let idx = tempCallbacksCount tempCallbacks[idx] = c callbackMode[idx] = MODE_NORMAL tempCallbacksCount++ function pushCallbackUntil(ForGroupCallbackUntil c) let idx = tempCallbacksCount tempCallbacksUntil[idx] = c callbackMode[idx] = MODE_UNTIL stopRequested[idx] = false tempCallbacksCount++ function pushCallbackCounted(ForGroupCallback c, int count) let idx = tempCallbacksCount tempCallbacks[idx] = c callbackMode[idx] = MODE_COUNTED countLimits[idx] = count countIters[idx] = 0 tempCallbacksCount++ function popCallback(bool destroyCallback) DUMMY_GROUP.clear() tempCallbacksCount-- let idx = tempCallbacksCount if destroyCallback if callbackMode[idx] == MODE_UNTIL destroy tempCallbacksUntil[idx] else destroy tempCallbacks[idx] function currentCallback() returns ForGroupCallback return tempCallbacks[tempCallbacksCount - 1] function currentCallbackUntil() returns ForGroupCallbackUntil return tempCallbacksUntil[tempCallbacksCount - 1] // ====Stack end==== /** Executes the given closure for every unit in this group, removing the units from the group as processed */ public function group.forEachFrom(ForGroupCallback cb) for u from this cb.callback(u) destroy cb /** Executes the given closure for every unit in this group, keeping all units in the group */ public function group.forEachIn(ForGroupCallback cb) for u in this cb.callback(u) destroy cb /** Executes the given closure for every unit of the given type. Remember that names of custom units are "custom_[typeId]" */ public function forUnitsOfType(string unitname, ForGroupCallback c) pushCallback(c) GroupEnumUnitsOfType(DUMMY_GROUP, unitname, filter) popCallback(true) /** Executes the given closure for every unit of the given player. Cancels itself after *count* iterations */ public function forUnitsOfTypeCounted(string unitname, int count, ForGroupCallback c) pushCallbackCounted(c, count) GroupEnumUnitsOfType(DUMMY_GROUP, unitname, filter) popCallback(true) /** Executes the given closure for every unit of the given player */ public function forUnitsOfPlayer(player p, ForGroupCallback c) pushCallback(c) GroupEnumUnitsOfPlayer(DUMMY_GROUP, p, filter) popCallback(true) /** Executes the given closure for every existing unit */ public function forUnitsAll(ForGroupCallback c) for i = 0 to bj_MAX_PLAYER_SLOTS - 1 pushCallback(c) GroupEnumUnitsOfPlayer(DUMMY_GROUP, players[i], filter) popCallback(false) destroy c /** Executes the given closure for every unit in the given rect */ public function forUnitsInRect(rect r, ForGroupCallback c) pushCallback(c) GroupEnumUnitsInRect(DUMMY_GROUP, r, filter) popCallback(true) /** Executes the given closure for every unit in the given rect. Cancels itself after *count* iterations */ public function forUnitsInRectCounted(rect r, int count, ForGroupCallback c) pushCallbackCounted(c, count) GroupEnumUnitsInRect(DUMMY_GROUP, r, filter) popCallback(true) /** Executes the given closure for every unit in range of the given position */ public function forUnitsInRange(vec2 pos, real radius, ForGroupCallback c) forUnitsInRange(pos, radius, false, c) /** Executes the given closure for every unit in range of the given position With collisionSizeFiltering true it will take the units' collision into account. */ public function forUnitsInRange(vec2 pos, real radius, bool collisionSizeFiltering, ForGroupCallback c) if collisionSizeFiltering pushCallback(c) GroupEnumUnitsInRange(ENUM_GROUP, pos.x, pos.y, radius + MAX_COLLISION_SIZE, null) for u from ENUM_GROUP if IsUnitInRangeXY(u, pos.x, pos.y, radius) c.callback(u) popCallback(true) else pushCallback(c) GroupEnumUnitsInRange(DUMMY_GROUP, pos.x, pos.y, radius, filter) popCallback(true) /** Executes the given closure for every unit in range of the given position Cancels itself after *count* iterations */ public function forUnitsInRangeCounted(vec2 pos, real radius, int count, ForGroupCallback c) pushCallbackCounted(c, count) GroupEnumUnitsInRange(DUMMY_GROUP, pos.x, pos.y, radius, filter) popCallback(true) /** Executes the given closure for every unit selected by the given player */ public function forUnitsSelected(player p, ForGroupCallback c) pushCallback(c) GroupEnumUnitsSelected(DUMMY_GROUP, p, filter) popCallback(true) /** Executes the given closure for the closest unit inside the given range of the given position, matching the provided filter. If there is no unit in range, the closure will be run with "null" */ public function forNearestUnit(vec2 pos, real range, filterfunc filter, ForGroupCallback c) pushCallback(c) GroupEnumUnitsInRange(ENUM_GROUP, pos.x, pos.y, range, filter) unit nearest = null var bestDist = REAL_MAX for u from ENUM_GROUP let distSq = pos.distanceToSq(u.getPos()) if distSq < bestDist nearest = u bestDist = distSq c.callback(nearest) popCallback(true) /** Executes the given closure for every unit in this group, removing the units from the group as processed. Return true to continue iteration, false to stop. */ public function group.forEachFromUntil(ForGroupCallbackUntil cb) while this.hasNext() if not cb.callback(this.next()) break destroy cb /** Executes the given closure for every unit in this group, keeping all units in the group. Return true to continue iteration, false to stop. */ public function group.forEachInUntil(ForGroupCallbackUntil cb) let itr = this.iterator() while itr.hasNext() if not cb.callback(itr.next()) break itr.close() destroy cb /** Executes the given closure for every unit of the given type. Return true to continue iteration, false to stop callback invocation. */ public function forUnitsOfTypeUntil(string unitname, ForGroupCallbackUntil c) pushCallbackUntil(c) GroupEnumUnitsOfType(DUMMY_GROUP, unitname, filter) popCallback(true) /** Executes the given closure for every unit of the given player. Return true to continue iteration, false to stop callback invocation. */ public function forUnitsOfPlayerUntil(player p, ForGroupCallbackUntil c) pushCallbackUntil(c) GroupEnumUnitsOfPlayer(DUMMY_GROUP, p, filter) popCallback(true) /** Executes the given closure for every existing unit. Return true to continue iteration, false to stop callback invocation. */ public function forUnitsAllUntil(ForGroupCallbackUntil c) pushCallbackUntil(c) for i = 0 to bj_MAX_PLAYER_SLOTS - 1 GroupEnumUnitsOfPlayer(DUMMY_GROUP, players[i], filter) if stopRequested[tempCallbacksCount - 1] break popCallback(true) /** Executes the given closure for every unit in the given rect. Return true to continue iteration, false to stop callback invocation. */ public function forUnitsInRectUntil(rect r, ForGroupCallbackUntil c) pushCallbackUntil(c) GroupEnumUnitsInRect(DUMMY_GROUP, r, filter) popCallback(true) /** Executes the given closure for every unit in range of the given position. Return true to continue iteration, false to stop callback invocation. */ public function forUnitsInRangeUntil(vec2 pos, real radius, ForGroupCallbackUntil c) pushCallbackUntil(c) GroupEnumUnitsInRange(DUMMY_GROUP, pos.x, pos.y, radius, filter) popCallback(true) /** Executes the given closure for every unit selected by the given player. Return true to continue iteration, false to stop callback invocation. */ public function forUnitsSelectedUntil(player p, ForGroupCallbackUntil c) pushCallbackUntil(c) GroupEnumUnitsSelected(DUMMY_GROUP, p, filter) popCallback(true) // Destructables // ====Callback stack for nested usage==== public interface ForGroupCallbackD function callback(destructable d) function filterD() returns boolean currentCallbackD().callback(GetEnumDestructable()) return false ForGroupCallbackD array tempCallbacksD int tempCallbacksDCount = 0 function pushCallbackD(ForGroupCallbackD c) tempCallbacksD[tempCallbacksDCount] = c tempCallbacksDCount++ function popCallbackD() tempCallbacksDCount-- destroy tempCallbacksD[tempCallbacksDCount] function currentCallbackD() returns ForGroupCallbackD return tempCallbacksD[tempCallbacksDCount - 1] // ====Stack end==== let enumRect = Rect(0,0,0,0) /** Executes the given closure for all destructables in a rect that incompasses the circle with the range radius at the given position. */ public function forDestructablesInRange(vec2 pos, real range, ForGroupCallbackD cb) forDestructablesInRange(pos, range, null, cb) /** Executes the given closure for all destructables in a rect that incompasses the circle with the range radius at the given position. */ public function forDestructablesInRange(vec2 pos, real range, boolexpr filter, ForGroupCallbackD cb) enumRect.resize(vec2(pos.x - range, pos.y - range), vec2(pos.x + range, pos.y + range)) forDestructablesInRect(enumRect, filter, cb) /** Executes the given closure for all destructables in the given rect */ public function forDestructablesInRect(rect r, ForGroupCallbackD cb) forDestructablesInRect(r, null, cb) /** Executes the given closure for all destructables in the given rect */ public function forDestructablesInRect(rect r, boolexpr filter, ForGroupCallbackD cb) pushCallbackD(cb) EnumDestructablesInRect(r, filter, () -> filterD()) popCallbackD() // Nearest Destructable helpers destructable nearestD = null var nearestDDist = REAL_MAX var gpos = ZERO2 /** Executes the given closure for the closes destructable in the given rect. If there is no destructable in range, the closure will be run with "null" */ public function forNearestDestructable(vec2 pos, real range, ForGroupCallbackD c) nearestD = null nearestDDist = range gpos = pos enumRect.resize(vec2(pos.x - range, pos.y - range), vec2(pos.x + range, pos.y + range)) EnumDestructablesInRect(enumRect, null) -> let dist = gpos.distanceTo(GetEnumDestructable().getPos()) if dist < nearestDDist nearestD = GetEnumDestructable() nearestDDist = dist c.callback(nearestD) destroy c