LurkerAspect
Now officially a Super Lurker
- Reaction score
- 118
PREVIEW SUMMARY: Curious about fixing lengthy loops containing many if statements so that they don't run over each other and produce weird things
Hi there
The below callback runs through a set of 10 units and compares and tests a lot of things and has multiple outcomes. However, when run, I think the loop is so slow that many loops are being handled at the same time, and it produces the same results on different units.
The most notable result is that when a unit captures a single node, 4-6 are "captured" by his faction as well as the one he actually did capture.
For those who want to look at it, the full scope is below.
My solution was to reduce the callback interval to 0.1 and address one unit at a time per callback, and use a counter to cycle through the currently checked unit.
If you have a moment to show a rookie coder how things are supposed to work, I'd be really grateful it might go a long way to fixing some of my broken and discarded maps!
Hi there
The below callback runs through a set of 10 units and compares and tests a lot of things and has multiple outcomes. However, when run, I think the loop is so slow that many loops are being handled at the same time, and it produces the same results on different units.
JASS:
// Everything in CAPS is a private scope global
private function callback takes nothing returns nothing
local integer i = 0
local boolean explorerNearby = false
local boolean demonNearby = false
local group g = NewGroup() // This jass function uses GroupUtils by Rising_Dusk
local boolexpr b = null
local unit exp = null
local unit dem = null
if TIME_COUNTER >= REWARD_INCREMENT_TIME*NODE_COUNT then // this incrementally increases the gold per second reward
set TIME_COUNTER = 0
set REWARD_LEVEL = REWARD_LEVEL+1
set CURRENT_GOLD_REWARD = CURRENT_GOLD_REWARD+GOLD_CONSTANT_REWARD_INCREMENT
call Debug("Reward level increased")
endif
loop // The loop in question!
exitwhen i > NODE_COUNT-1
set RELEVANT_NODE = NODE<i>
if NODE_OWNER<i> == 1 and NODE_STATE<i> == 1 then // Add a gold-per-second reward to the owning faction
call ForForce(EXPLORERS,function AddGoldConstantBonus)
elseif NODE_OWNER<i> == 2 and NODE_STATE<i> == 1 then
call ForForce(DEMONS,function AddGoldConstantBonus)
endif
if NODE_OWNER<i> == 0 and NODE_STATE<i> == 1 then // check if the node is on
set b = Filter(function CheckForExplorers)
call GroupEnumUnitsInRange(g,GetUnitX(NODE<i>),GetUnitY(NODE<i>),CHECK_RANGE,b) // look for members of faction 1
set exp = FirstOfGroup(g)
if exp != null then
set explorerNearby = true
endif
call DestroyBoolExpr(b)
call GroupClear(g)
set b = Filter(function CheckForDemons)
call GroupEnumUnitsInRange(g,GetUnitX(NODE<i>),GetUnitY(NODE<i>),CHECK_RANGE,b) // look for members of factions 2
set dem = FirstOfGroup(g)
if dem != null then
set demonNearby = true
endif
call DestroyBoolExpr(b)
call ReleaseGroup(g) // clear (I hope?)
if explorerNearby == true and demonNearby == false then // check the capture state depending on nearby units, then display a counter
set NODE_SCORE<i> = NODE_SCORE<i>+1
call SetUnitAnimation(NODE<i>,"stand work")
call TextTag_Unit(NODE<i>,I2S(NODE_SCORE<i>),"|cff00FF00") // this function uses TextTag by Cohadar
elseif explorerNearby == false and demonNearby == true then
set NODE_SCORE<i> = NODE_SCORE<i>-1
call SetUnitAnimation(NODE<i>,"stand work")
call TextTag_Unit(NODE<i>,I2S(NODE_SCORE<i>),"|cffFF0000")
elseif explorerNearby == false and demonNearby == false then
if NODE_SCORE<i> > 0 then
set NODE_SCORE<i> = NODE_SCORE<i>-1
call TextTag_Unit(NODE<i>,"+"+I2S(NODE_SCORE<i>),"|c00C0C0C0")
elseif NODE_SCORE<i> < 0 then
set NODE_SCORE<i> = NODE_SCORE<i>+1
call TextTag_Unit(NODE<i>,I2S(NODE_SCORE<i>),"|c00C0C0C0")
endif
call SetUnitAnimation(NODE<i>,"stand alternate")
endif
if NODE_SCORE<i> >= NODE_REQUIRED_SCORE then // check the resulting score and apply a final action
call SetUnitOwner(NODE<i>,GetOwningPlayer(exp),true)
call UnitRemoveAbility(NODE<i>,039;Avul039;)
call DestroyEffect(AddSpecialEffect(EXPLORER_SUCCESS_SFX,GetUnitX(NODE<i>),GetUnitY(NODE<i>)))
set NODE_OWNER<i> = 1
call ForForce(EXPLORERS,function AddInstantGoldBonus)
call DisplayTextToForce(bj_FORCE_ALL_PLAYERS,"|cff87ceebA Power Node has been captured by the Explorers!|r")
set NODE_SCORE<i> = 0
call SetUnitAnimation(NODE<i>,"stand alternate")
elseif NODE_SCORE<i> <= (-1)*NODE_REQUIRED_SCORE then
call SetUnitOwner(NODE<i>,GetOwningPlayer(dem),true)
call UnitRemoveAbility(NODE<i>,039;Avul039;)
call DestroyEffect(AddSpecialEffect(DEMON_SUCCESS_SFX,GetUnitX(NODE<i>),GetUnitY(NODE<i>)))
set NODE_OWNER<i> = 2
call ForForce(DEMONS,function AddInstantLumberBonus)
call DisplayTextToForce(bj_FORCE_ALL_PLAYERS,"|cff87ceebA Power Node has been captured by the Demon!|r")
set NODE_SCORE<i> = 0
call SetUnitAnimation(NODE<i>,"stand alternate")
endif
set exp = null
set dem = null
endif
set i = i+1
endloop
set NODE_CYCLE = NODE_CYCLE+1
endfunction</i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i>
The most notable result is that when a unit captures a single node, 4-6 are "captured" by his faction as well as the one he actually did capture.
For those who want to look at it, the full scope is below.
JASS:
scope PowerNode initializer init
globals
private constant real CHECK_RANGE = 300
private constant integer NODE_REQUIRED_SCORE = 15
private constant integer REWARD_INCREMENT_TIME = 300
private constant integer NODE_COUNT = 10
private constant integer GOLD_INSTANT_REWARD_BASE = 800
private constant integer GOLD_INSTANT_REWARD_INCREMENT = 200
private constant integer LUMBER_INSTANT_REWARD_BASE = 4
private constant integer LUMBER_INSTANT_REWARD_INCREMENT = 1
private constant integer GOLD_CONSTANT_REWARD_BASE = 0
private constant integer GOLD_CONSTANT_REWARD_INCREMENT = 5
private constant string EXPLORER_SUCCESS_SFX = "Abilities\\Spells\\Other\\Charm\\CharmTarget.mdl"
private constant string DEMON_SUCCESS_SFX = "Objects\\Spawnmodels\\Undead\\UCancelDeath\\UCancelDeath.mdl"
private constant string NEUTRALISE_SUCCESS_SFX = "Units\\NightElf\\Wisp\\WispExplode.mdl"
private constant string ACTIVATE_SFX = "Abilities\\Spells\\Items\\TomeOfRetraining\\TomeOfRetrainingCaster.mdl"
private unit array NODE[9]
private integer array NODE_OWNER[9] // 0 = neutral passive, 1 = explorers, 2 = demons
private integer array NODE_STATE[9] // 0 = off, 1 = on
private integer array NODE_SCORE[9] // determines the current capture state of the node. Postive value = explorer ownership, negative = demons
private integer CURRENT_NODE = -1
private timer TIMER = CreateTimer()
private integer TIME_COUNTER = 0
private integer REWARD_LEVEL = 0
private integer CURRENT_GOLD_REWARD = GOLD_CONSTANT_REWARD_BASE
private integer ACTIVE_NODES = 0
private unit RELEVANT_NODE = null
private integer DEBUG_COUNTER = 0
endglobals
private function AddGoldConstantBonus takes nothing returns nothing
call PlayerAddGold(GetEnumPlayer(),CURRENT_GOLD_REWARD)
call TextTag_GoldBounty(RELEVANT_NODE, I2S(CURRENT_GOLD_REWARD), GetEnumPlayer())
endfunction
private function AddInstantGoldBonus takes nothing returns nothing
call PlayerAddGold(GetEnumPlayer(),GOLD_INSTANT_REWARD_BASE+GOLD_INSTANT_REWARD_INCREMENT*REWARD_LEVEL)
call TextTag_GoldBounty(RELEVANT_NODE, I2S(GOLD_INSTANT_REWARD_BASE+GOLD_INSTANT_REWARD_INCREMENT*REWARD_LEVEL), GetEnumPlayer())
endfunction
private function AddInstantLumberBonus takes nothing returns nothing
call PlayerAddLumber(GetEnumPlayer(),LUMBER_INSTANT_REWARD_BASE+LUMBER_INSTANT_REWARD_INCREMENT*REWARD_LEVEL)
call TextTag_LumberBounty(RELEVANT_NODE, I2S(LUMBER_INSTANT_REWARD_BASE+LUMBER_INSTANT_REWARD_INCREMENT*REWARD_LEVEL), GetEnumPlayer())
endfunction
private function CheckForExplorers takes nothing returns boolean
return (GetWidgetLife(GetFilterUnit()) > 0 and IsUnitExplorer(GetFilterUnit()))
endfunction
private function CheckForDemons takes nothing returns boolean
return (GetWidgetLife(GetFilterUnit()) > 0 and IsUnitDemon(GetFilterUnit()))
endfunction
private function callback takes nothing returns nothing
local integer i = 0
local boolean explorerNearby = false
local boolean demonNearby = false
local group g = NewGroup() // This jass function uses GroupUtils by Rising_Dusk
local boolexpr b = null
local unit exp = null
local unit dem = null
if TIME_COUNTER >= REWARD_INCREMENT_TIME*NODE_COUNT then // this incrementally increases the gold per second reward
set TIME_COUNTER = 0
set REWARD_LEVEL = REWARD_LEVEL+1
set CURRENT_GOLD_REWARD = CURRENT_GOLD_REWARD+GOLD_CONSTANT_REWARD_INCREMENT
call Debug("Reward level increased")
endif
loop // The loop in question!
exitwhen i > NODE_COUNT-1
set RELEVANT_NODE = NODE<i>
if NODE_OWNER<i> == 1 and NODE_STATE<i> == 1 then // Add a gold-per-second reward to the owning faction
call ForForce(EXPLORERS,function AddGoldConstantBonus)
elseif NODE_OWNER<i> == 2 and NODE_STATE<i> == 1 then
call ForForce(DEMONS,function AddGoldConstantBonus)
endif
if NODE_OWNER<i> == 0 and NODE_STATE<i> == 1 then // check if the node is on
set b = Filter(function CheckForExplorers)
call GroupEnumUnitsInRange(g,GetUnitX(NODE<i>),GetUnitY(NODE<i>),CHECK_RANGE,b) // look for members of faction 1
set exp = FirstOfGroup(g)
if exp != null then
set explorerNearby = true
endif
call DestroyBoolExpr(b)
call GroupClear(g)
set b = Filter(function CheckForDemons)
call GroupEnumUnitsInRange(g,GetUnitX(NODE<i>),GetUnitY(NODE<i>),CHECK_RANGE,b) // look for members of factions 2
set dem = FirstOfGroup(g)
if dem != null then
set demonNearby = true
endif
call DestroyBoolExpr(b)
call ReleaseGroup(g) // clear (I hope?)
if explorerNearby == true and demonNearby == false then // check the capture state depending on nearby units, then display a counter
set NODE_SCORE<i> = NODE_SCORE<i>+1
call SetUnitAnimation(NODE<i>,"stand work")
call TextTag_Unit(NODE<i>,I2S(NODE_SCORE<i>),"|cff00FF00") // this function uses TextTag by Cohadar
elseif explorerNearby == false and demonNearby == true then
set NODE_SCORE<i> = NODE_SCORE<i>-1
call SetUnitAnimation(NODE<i>,"stand work")
call TextTag_Unit(NODE<i>,I2S(NODE_SCORE<i>),"|cffFF0000")
elseif explorerNearby == false and demonNearby == false then
if NODE_SCORE<i> > 0 then
set NODE_SCORE<i> = NODE_SCORE<i>-1
call TextTag_Unit(NODE<i>,"+"+I2S(NODE_SCORE<i>),"|c00C0C0C0")
elseif NODE_SCORE<i> < 0 then
set NODE_SCORE<i> = NODE_SCORE<i>+1
call TextTag_Unit(NODE<i>,I2S(NODE_SCORE<i>),"|c00C0C0C0")
endif
call SetUnitAnimation(NODE<i>,"stand alternate")
endif
if NODE_SCORE<i> >= NODE_REQUIRED_SCORE then // check the resulting score and apply a final action
call SetUnitOwner(NODE<i>,GetOwningPlayer(exp),true)
call UnitRemoveAbility(NODE<i>,039;Avul039;)
call DestroyEffect(AddSpecialEffect(EXPLORER_SUCCESS_SFX,GetUnitX(NODE<i>),GetUnitY(NODE<i>)))
set NODE_OWNER<i> = 1
call ForForce(EXPLORERS,function AddInstantGoldBonus)
call DisplayTextToForce(bj_FORCE_ALL_PLAYERS,"|cff87ceebA Power Node has been captured by the Explorers!|r")
set NODE_SCORE<i> = 0
call SetUnitAnimation(NODE<i>,"stand alternate")
elseif NODE_SCORE<i> <= (-1)*NODE_REQUIRED_SCORE then
call SetUnitOwner(NODE<i>,GetOwningPlayer(dem),true)
call UnitRemoveAbility(NODE<i>,039;Avul039;)
call DestroyEffect(AddSpecialEffect(DEMON_SUCCESS_SFX,GetUnitX(NODE<i>),GetUnitY(NODE<i>)))
set NODE_OWNER<i> = 2
call ForForce(DEMONS,function AddInstantLumberBonus)
call DisplayTextToForce(bj_FORCE_ALL_PLAYERS,"|cff87ceebA Power Node has been captured by the Demon!|r")
set NODE_SCORE<i> = 0
call SetUnitAnimation(NODE<i>,"stand alternate")
endif
set exp = null
set dem = null
endif
set i = i+1
endloop
endfunction
private function initForces takes nothing returns nothing
call ForceAddPlayer(EXPLORERS,Player(0))
call ForceAddPlayer(EXPLORERS,Player(1))
call ForceAddPlayer(EXPLORERS,Player(2))
call ForceAddPlayer(EXPLORERS,Player(3))
call ForceAddPlayer(EXPLORERS,Player(4))
call ForceAddPlayer(EXPLORERS,Player(5))
call ForceAddPlayer(EXPLORERS,Player(6))
call ForceAddPlayer(EXPLORERS,Player(7))
call ForceAddPlayer(EXPLORERS,Player(8))
call ForceAddPlayer(EXPLORERS,Player(9))
call ForceAddPlayer(EXPLORERS,Player(10))
call ForceAddPlayer(DEMONS,Player(11))
endfunction
private function initArrays takes nothing returns nothing
local integer i = 0
loop
exitwhen i > 9
set NODE_OWNER<i> = 0
set NODE_STATE<i> = 0
set NODE_SCORE<i> = 0
set i = i+1
endloop
set NODE[0] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),039;o006039;,GetRectCenterX(gg_rct_CommandPoint1),GetRectCenterY(gg_rct_CommandPoint1),bj_UNIT_FACING)
set NODE[1] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),039;o006039;,GetRectCenterX(gg_rct_CommandPoint2),GetRectCenterY(gg_rct_CommandPoint2),bj_UNIT_FACING)
set NODE[2] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),039;o006039;,GetRectCenterX(gg_rct_CommandPoint3),GetRectCenterY(gg_rct_CommandPoint3),bj_UNIT_FACING)
set NODE[3] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),039;o006039;,GetRectCenterX(gg_rct_CommandPoint4),GetRectCenterY(gg_rct_CommandPoint4),bj_UNIT_FACING)
set NODE[4] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),039;o006039;,GetRectCenterX(gg_rct_CommandPoint5),GetRectCenterY(gg_rct_CommandPoint5),bj_UNIT_FACING)
set NODE[5] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),039;o006039;,GetRectCenterX(gg_rct_CommandPoint6),GetRectCenterY(gg_rct_CommandPoint6),bj_UNIT_FACING)
set NODE[6] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),039;o006039;,GetRectCenterX(gg_rct_CommandPoint7),GetRectCenterY(gg_rct_CommandPoint7),bj_UNIT_FACING)
set NODE[7] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),039;o006039;,GetRectCenterX(gg_rct_CommandPoint8),GetRectCenterY(gg_rct_CommandPoint8),bj_UNIT_FACING)
set NODE[8] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),039;o006039;,GetRectCenterX(gg_rct_CommandPoint9),GetRectCenterY(gg_rct_CommandPoint9),bj_UNIT_FACING)
set NODE[9] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),039;o006039;,GetRectCenterX(gg_rct_CommandPoint10),GetRectCenterY(gg_rct_CommandPoint10),bj_UNIT_FACING)
//call Debug("Power node Arrays initialized")
endfunction
private function CreateNode takes integer i returns nothing
if i == 0 then
set NODE[0] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),039;o006039;,GetRectCenterX(gg_rct_CommandPoint1),GetRectCenterY(gg_rct_CommandPoint1),bj_UNIT_FACING)
elseif i == 1 then
set NODE[1] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),039;o006039;,GetRectCenterX(gg_rct_CommandPoint2),GetRectCenterY(gg_rct_CommandPoint2),bj_UNIT_FACING)
elseif i == 2 then
set NODE[2] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),039;o006039;,GetRectCenterX(gg_rct_CommandPoint3),GetRectCenterY(gg_rct_CommandPoint3),bj_UNIT_FACING)
elseif i == 3 then
set NODE[3] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),039;o006039;,GetRectCenterX(gg_rct_CommandPoint4),GetRectCenterY(gg_rct_CommandPoint4),bj_UNIT_FACING)
elseif i == 4 then
set NODE[4] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),039;o006039;,GetRectCenterX(gg_rct_CommandPoint5),GetRectCenterY(gg_rct_CommandPoint5),bj_UNIT_FACING)
elseif i == 5 then
set NODE[5] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),039;o006039;,GetRectCenterX(gg_rct_CommandPoint6),GetRectCenterY(gg_rct_CommandPoint6),bj_UNIT_FACING)
elseif i == 6 then
set NODE[6] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),039;o006039;,GetRectCenterX(gg_rct_CommandPoint7),GetRectCenterY(gg_rct_CommandPoint7),bj_UNIT_FACING)
elseif i == 7 then
set NODE[7] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),039;o006039;,GetRectCenterX(gg_rct_CommandPoint8),GetRectCenterY(gg_rct_CommandPoint8),bj_UNIT_FACING)
elseif i == 8 then
set NODE[8] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),039;o006039;,GetRectCenterX(gg_rct_CommandPoint9),GetRectCenterY(gg_rct_CommandPoint9),bj_UNIT_FACING)
elseif i == 9 then
set NODE[9] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),039;o006039;,GetRectCenterX(gg_rct_CommandPoint10),GetRectCenterY(gg_rct_CommandPoint10),bj_UNIT_FACING)
endif
endfunction
private function TestNode takes nothing returns nothing
set NODE_STATE[5] = 1
call TimerStart(TIMER,1,true,function callback)
endfunction
private function DeathActions takes nothing returns nothing
local integer i = 0
local boolean nodeRunning = false
if GetUnitTypeId(GetTriggerUnit()) == 039;o006039; then
loop
exitwhen i > NODE_COUNT-1
if GetTriggerUnit() == NODE<i> then
call RemoveUnit(NODE<i>)
call CreateNode(i)
set NODE_STATE<i> = 0
set NODE_OWNER<i> = 0
set ACTIVE_NODES = ACTIVE_NODES-1
//call Debug("loop tick = "+I2S(i))
call DisplayTextToForce(bj_FORCE_ALL_PLAYERS,"|cff87ceebA Power Node has been deactivated!|r")
call PingMinimapEx(GetUnitX(GetTriggerUnit()),GetUnitY(GetTriggerUnit()),5,0,128,255,true)
call DestroyEffect(AddSpecialEffect(NEUTRALISE_SUCCESS_SFX,GetUnitX(NODE<i>),GetUnitY(NODE<i>)))
//call UnitAddAbility(NODE<i>,'Avul')
endif
set i = i+1
endloop
if ACTIVE_NODES == 0 then
call PauseTimer(TIMER)
call Debug("The timer was paused")
endif
endif
endfunction
public function ActivateNode takes nothing returns nothing
local integer i = 0
local integer array rarray
local integer rcount = 0
local integer result = 0
local integer tempRandom = 0
//local string msg = "Array looks like this: "
loop
exitwhen i > NODE_COUNT-1
set rarray<i> = -1
set i=i+1
endloop
set i = 0
loop
exitwhen i > NODE_COUNT-1
if NODE_STATE<i> == 0 then
set rarray[rcount] = i
set rcount = rcount+1
endif
set i = i+1
endloop
set tempRandom = GetRandomInt(1,rcount)-1
//call Debug("tempRandom = "+I2S(tempRandom))
set result = rarray[tempRandom]
//call Debug("rcount = "+I2S(rcount))
//call Debug(msg)
if rcount == 0 then
call Debug("Can039;t activate any more nodes, they039;re all lit :/")
elseif result == -1 then
call Debug("Result was -1, your system is shit xD")
else
set NODE_STATE[result] = 1
set NODE_OWNER[result] = 0
//call Debug("Activated node: "+I2S(result))
set ACTIVE_NODES = ACTIVE_NODES+1
//call CreateNode(result)
call DisplayTextToForce(bj_FORCE_ALL_PLAYERS,"|cff87ceebA Power Node has been activated!|r")
call PingMinimapEx(GetUnitX(NODE[result]),GetUnitY(NODE[result]),5,0,128,255,false)
call DestroyEffect(AddSpecialEffect(NEUTRALISE_SUCCESS_SFX,GetUnitX(NODE[result]),GetUnitY(NODE[result])))
call PlaySoundBJ(gg_snd_Warning)
endif
if ACTIVE_NODES == 1 then
call TimerStart(TIMER,1/I2R(NODE_COUNT),true,function callback)
//call Debug("The timer was started!")
endif
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterPlayerChatEvent(t,Player(0),"-node",true)
call TriggerAddAction(t,function ActivateNode)
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_DEATH)
call TriggerAddAction(t,function DeathActions)
set t = null
call initArrays()
call initForces()
endfunction
endscope
</i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i>
My solution was to reduce the callback interval to 0.1 and address one unit at a time per callback, and use a counter to cycle through the currently checked unit.
If you have a moment to show a rookie coder how things are supposed to work, I'd be really grateful it might go a long way to fixing some of my broken and discarded maps!