# AutoTowerGrid # by Kalli # Script is to run continuously in the console! $pacMode:0 # Makes all units built by the pilot unselectable and undeletable by the player # Quick guide to the variables: # 1) largeSkip makes the script less accurate, but it saves on computation time. # 2) optimalCellSelection will keep looking for the cell with the most connections. Or until optimalTowerTarget is met. # 3) Multiply optimalCellSelection with largeSkip to get an idea of what cellrange will be checked. Higher = more triangles even when square + more towers close to minimum distance. # 4) Good settings for a well connected tower grid with low average computation times: mintowerdistance=0; gosquare=1; optimalselection=3; largeSkip=8. $maxTowerDistance:12 # LT to exclude 12. Mostly max is 12, but sometimes 2 towers at an exact distance of 12 will not connect. And other units with a different connecting height, might not connect at all. $minTowerDistance:0 # Set to 0 to let the script calc the min distance as a function of the max distance. $disLOS:1 # Check LOS when checking for towers within the minimum distance. It makes it easier to climb walls or cliffs, but it becomes messier. $contaminentSlot:8 # Towers shouldn't build on contaminated terrain, but the mapmaker could move the slots around in the editor. $creeperRange:3 # Towers will be build up to this distance from creep. Closer creep will make it a "blocked" tower. $goSQUARE:1 # The script will either work angular or perpendicular. $optimalCellSelection:3 # after finding a suitable cell, the script will loop through x more cells to maybe find a better connected one. $largeSkip:8 # When a computation heavy check fails, skip a few cells. Works well with lowering optimalCellSelection. $smallSkip:1 # Only used for avoiding void and map limits atm. $optimalTowerTarget:2 # The loop will stop if the cell has this many towers at a good distance. Minimum 2. $$minTowerCellDistance:1 # Could lower execution time, but will make it harder to scale some cliffs. If this is increased, decrease the $optimalCellSelection variable. Test per map. $$maxTowerCellDistance:12 # LTE. something to play with. Cells further away than the maxTowerDistance are also automatically removed. $$connectionHeight:2.8 # 2.9 is very rarely too high for the LOS check along slope edges. $debugPrint:0 "Everything" @startChrono <-debugPrint if elapsedtime ->starttime endif # GetEnergyStore ->energyStored # GetEnergyUse GetEnergyGeneration 1 max div ->energyUsage GetTimer3 ->timer3 GetTimer0 ->timer0 switch # "Nodes" @startChrono case(<-timer0 eq0) 151 Settimer0 @findOtherNodes endcase # "Towers" @startChrono case(<-timer0 100 eq) @findOtherTowers endcase # "Cleaning" @startChrono case(<-timer0 50 eq) @cleanOldNodeList endcase # "Planned" @startChrono case(<-timer3 0 eq) 11 Settimer3 @buildPlannedTower @monitorTowers endcase # "Blocked" @startChrono case(<-timer3 10 eq) @buildBlockedTower @monitorTowers endcase # # "Supply" @startChrono # case(<-energystored eq0) # # The cases above don't need much energy, the cases below do. Only start large scale expansion when there is energy stored. # 0 @supplyNonEssentialInfrastructure # endcase # "Planned" @startChrono case(<-timer3 9 eq) @buildPlannedTower @monitorTowers endcase # "Blocked" @startChrono case(<-timer3 8 eq) @buildBlockedTower @monitorTowers endcase # case(<-energyUsage 1.0 gt <-energyStored 20 lt and) # # Try to prevent overbuilding again. # endcase # "Planned" @startChrono case(<-timer3 7 eq) @buildPlannedTower @monitorTowers endcase # "Blocked" @startChrono case(<-timer3 6 eq) @buildBlockedTower @monitorTowers endcase # "Planned" @startChrono case(<-timer3 5 eq) @buildPlannedTower @monitorTowers endcase # "Blocked" @startChrono case(<-timer3 4 eq) @buildBlockedTower @monitorTowers endcase # case(<-energyUsage 1.0 gt <-energyStored 40 lt or) # # Try to prevent overbuilding. # endcase # "PlannedS" @startChrono case(<-timer3 3 eq) @buildPlannedTower # 1 @supplyNonEssentialInfrastructure #turn on supply again for non essentials if there are enough supplies endcase # "Blocked" @startChrono case(<-timer3 2 eq) @buildBlockedTower @monitorTowers endcase # "Planned" @startChrono case(<-timer3 1 eq) @buildPlannedTower @monitorTowers endcase endswitch @stopChrono :startChrono # the ID is left on the stack, swap to send it. # <-debugPrint if "StartElapsedMSG" swap sendmsg endif "StartElapsedMSG" swap sendmsg :stopChrono # <-debugPrint if "EndElapsedMSG" "" sendmsg endif "EndElapsedMSG" "" sendmsg :Once createlist ->uid_BlockedTower # use indexBl to cycle through these between frames createlist ->dir_BlockedTower # not the coordinates, but the direction createlist ->uid_PlannedTower # use indexPl to cycle through these between frames createlist ->dir_PlannedTower # not the coordinates, but the direction createlist ->uid_MonitorTower # use indexMT to cycle through these between frames createlist ->dir_MonitorTower # not the coordinates, but the direction createlist ->uid_MonitoredTower # The actually build towers, whose eventual destruction has to be monitored. # Use existing or playerbuild nodes (microrifts, pylons, pods and towers) to plan towers around createlist ->oldNodeList createlist ->oldNodeList # riftlab, pylons, pods and microrifts. Gets purged overtime. # If PAC mode is enabled, make friendly pre existing units unselectable or vice versa do(9999 0) if(GetUnitCreeperDamages(I)) I @SetSelectable endif loop # The minimum distance is to block placing a tower within a perfect square of other towers. The absolute minimum ratio is roughly 7.25 / 12. if(<-minTowerDistance eq0) <-maxTowerDistance 7.25 mul 12 div ->minTowerDistance endif # The following 2 variables are used in finding other towers at an "optimal" range <-maxTowerDistance 1.0 mul ->maxOptimalRange <-maxTowerDistance 0.85 mul <-minTowerDistance max ->minOptimalRange # 4 DIRECTIONS createlist ->relativePositionList1 createlist ->relativePositionList2 createlist ->relativePositionList3 createlist ->relativePositionList4 # The below cell lists + list sorting were created in excel. <-goSQUARE if list( v2(12 0) v2(11 0) v2(11 1) v2(11 -1) v2(11 2) v2(11 -2) v2(11 3) v2(11 -3) v2(11 4) v2(11 -4) v2(10 0) v2(10 1) v2(10 -1) v2(10 2) v2(10 -2) v2(10 3) v2(10 -3) v2(10 4) v2(10 -4) v2(10 5) v2(10 -5) v2(10 6) v2(9 0) v2(9 1) v2(9 -1) v2(10 -6) v2(9 2) v2(9 -2) v2(9 3) v2(9 -3) v2(9 4) v2(9 -4) v2(9 5) v2(9 -5) v2(9 6) v2(9 -6) v2(9 7) v2(9 -7) v2(8 0) v2(8 1) v2(8 -1) v2(8 2) v2(8 -2) v2(8 3) v2(8 4) v2(8 -3) v2(8 5) v2(8 -4) v2(8 6) v2(8 -5) v2(8 7) v2(8 -6) v2(8 8) v2(8 -7) v2(7 7) v2(7 6) v2(7 5) v2(7 -6) v2(7 -5) v2(7 4) v2(7 -4) v2(7 3) v2(7 -3) v2(7 2) v2(7 -2) v2(7 1) v2(7 -1) v2(7 0) v2(6 6) v2(6 5) v2(6 -5) v2(6 4) v2(6 -4) v2(6 3) v2(6 -3) v2(6 2) v2(6 -2) v2(6 1) v2(6 -1) v2(6 0) v2(5 5) v2(5 4) v2(5 -4) v2(5 3) v2(5 -3) v2(5 2) v2(5 -2) v2(5 1) v2(5 -1) v2(5 0) v2(4 4) v2(4 3) v2(4 -3) v2(4 2) v2(4 -2) v2(4 1) v2(4 -1) v2(4 0) v2(3 3) v2(3 2) v2(3 -2) v2(3 1) v2(3 -1) v2(3 0) v2(2 2) v2(2 1) v2(2 -1) v2(2 0) v2(1 1) v2(1 0) ) ->relativePositionList1 # loop through this reversed list in normal order else list( v2(10 6) v2(6 10) v2(9 7) v2(7 9) v2(8 8) v2(11 4) v2(4 11) v2(10 5) v2(5 10) v2(9 6) v2(6 9) v2(8 7) v2(7 8) v2(11 3) v2(3 11) v2(10 4) v2(4 10) v2(12 0) v2(11 2) v2(9 5) v2(2 11) v2(5 9) v2(8 6) v2(6 8) v2(7 7) v2(10 3) v2(11 1) v2(3 10) v2(1 11) v2(9 4) v2(11 0) v2(4 9) v2(10 2) v2(2 10) v2(8 5) v2(5 8) v2(7 6) v2(6 7) v2(10 1) v2(9 3) v2(3 9) v2(1 10) v2(10 0) v2(8 4) v2(4 8) v2(9 2) v2(2 9) v2(7 5) v2(5 7) v2(6 6) v2(9 1) v2(8 3) v2(1 9) v2(3 8) v2(9 0) v2(7 4) v2(4 7) v2(8 2) v2(6 5) v2(2 8) v2(5 6) v2(8 1) v2(1 8) v2(7 3) v2(3 7) v2(8 0) v2(6 4) v2(4 6) v2(5 5) v2(7 2) v2(2 7) v2(7 1) v2(1 7) v2(6 3) v2(3 6) v2(7 0) v2(5 4) v2(4 5) v2(6 2) v2(2 6) v2(6 1) v2(5 3) v2(1 6) v2(3 5) v2(4 4) v2(6 0) v2(5 2) v2(2 5) v2(4 3) v2(3 4) v2(5 1) v2(1 5) v2(5 0) v2(4 2) v2(2 4) v2(3 3) v2(4 1) v2(1 4) v2(4 0) v2(3 2) v2(2 3) v2(3 1) v2(1 3) v2(3 0) v2(2 2) v2(2 1) v2(1 2) v2(2 0) v2(1 1) v2(1 0) ) ->relativePositionList1 # loop through this reversed list in normal order endif # throw out the tower positions that are too close or too far. <-maxTowerDistance <-maxTowerCellDistance lt if <-maxTowerDistance ->maxTowerCellDistance endif do(-1 <-relativePositionList1 getlistcount 1 sub) if (distancecell(0 0 <-relativePositionList1[i] ev2) dup <-minTowerCellDistance lt swap <-maxTowerCellDistance gte or) <-relativePositionList1 i removelistelement endif loop <-relativePositionList1 getlistcount ->relativePositionsCount # Find back roughly the center cell of the position list and add it to index [0], so that a pre-emptive tower check can be ran against it. <-goSQUARE if do(<-relativePositionsCount 0) <-relativePositionList1[i] ev2 eq0 if # ->x <-maxX max ->maxX else pop endif loop <-maxX 2 div 1 add floor ->centralX <-relativePositionList1 <-centralX 0 v2 prependtolist <-maxX 2 div 1 add ceil <-minTowerDistance max ->quickRangeCheck # the range of the pre-emptive tower check else do(<-relativePositionsCount 0) <-relativePositionList1[i] ev2 dup2 eq if # ->z ->x pop <-maxX max ->maxX else pop pop endif loop <-maxX 2 div 0.7 add floor dup ->centralX ->centralZ <-relativePositionList1 <-centralX <-centralZ v2 prependtolist <-maxX 2 div 0.7 add 2 sqrt mul ceil <-minTowerDistance max ->quickRangeCheck # the range of the pre-emptive tower check endif <-relativePositionsCount 1 add ->relativePositionsCount # Generate the 3 other relative position lists by rotating the 1 1 list. <-relativePositionsCount 0 do <-relativePositionList1[i] dup dup ev2 -1 mul swap v2 ->relativePositionList2[i] ev2 -1 mul swap -1 mul swap v2 ->relativePositionList3[i] ev2 swap -1 mul v2 ->relativePositionList4[i] loop # <-debugPrint if <-relativePositionList1 <-relativePositionList2 <-relativePositionList3 <-relativePositionList4 printallsp endif :SetSelectable <-pacMode ! SetUnitSelectable :supplyNonEssentialInfrastructure ->supplyON "porter" <-supplyON @supplyUnitType "runway" <-supplyON @supplyUnitType "greenarrefinery" <-supplyON @supplyUnitType "rocketpad" <-supplyON @constructUnitType :supplyUnitType ->supplyON ->unitType <-unitType 0 GetUnitsByType dup ->list getlistcount ->listCount <-supplyON if <-listCount 0 do <-list[i] dup getunitenabled not if 1 setunitenabled return endif loop else -1 <-listCount 1 sub do <-list[i] dup getunitenabled if 0 setunitenabled return endif loop endif :constructUnitType # the same functiion as supplyunittype, only aimed at infrastructure which does not consume energy once build. ->supplyON ->unitType <-supplyON if # supply should be turned on for all units of this type, since construction could have finished with the packets that were already underway. <-unitType 0 GetUnitsByType dup ->list getlistcount ->listCount else <-unitType 2 GetUnitsByType dup ->list getlistcount ->listCount endif <-supplyON if <-listCount 0 do <-list[i] dup getunitenabled not if 1 setunitenabled return endif loop else -1 <-listCount 1 sub do <-list[i] dup getunitenabled if 0 setunitenabled return endif loop endif :findOtherNodes "pylon" 1 getunitsbytype ->pylonList "microrift" 1 getunitsbytype ->microriftList "pod" 1 getunitsbytype ->podList do(<-podList 0) <-podList[i] ->UID # only add pods that are on the ground + containing energy <-UID GetUnitOccupiesLand if <-UID getunitsettings "Resource" gettableelement eq0 if <-oldNodeList <-UID listcontains not if <-UID @plan4Towers endif endif endif loop do(<-microriftList 0) <-microriftList[i] ->UID <-oldNodeList <-UID listcontains not if <-UID @plan4Towers endif loop do(<-pylonList 0) <-pylonList[i] ->UID <-oldNodeList <-UID listcontains not if <-UID @plan4Towers endif loop getriftlab ->UID @plan4Towers <-oldNodeList <-UID listcontains not if <-UID @plan4Towers endif :findOtherTowers # A part of the findOtherNodes script, but separated to run during a different frame "tower" 1 getunitsbytype ->*towerList do(<-*towerList 0) <-*towerList[i] ->UID <-oldNodeList <-UID listcontains not if <-UID @plan4Towers endif loop :findOtherTowers2 # Less effective, but better computation wise. Spam this when out of planned towers. # The below code requires less compute time, but is somehow less effective than the code above. How is getunitsbytype ordered? "tower" 1 getunitsbytype dup ->*towerList getlistcount ->listCount <-*towerList <-lastTower getlistindex 0 max ->listStart do(<-listCount <-listStart) <-*towerList[i] ->UID <-oldNodeList <-UID listcontains not if <-UID @plan4Towers endif loop <-*towerList[<-listCount 1 sub] ->lastTower :plan4Towers ->UID <-UID getunitcell pop eq0 if return endif clearstack <-UID dup dup2 <-uid_PlannedTower PrependStackToList 1 2 3 4 <-dir_PlannedTower PrependStackToList <-oldNodeList <-UID appendtolist :cleanOldNodeList # remove destroyed nodes from the list do(-1 <-oldNodeList 1 sub) <-oldNodeList[i] ->UID <-UID getunitcell pop eq0 if <-oldNodeList i removelistelement endif loop # surviving old nodes need to be removed from the old node list periodically, because if they are unknown in the monitor, then no towers will be rebuild around them. So a constant slow purge is needed. Only remove 1 per 3 cycles. if(<-cycleCounter eq0) 3 ->cycleCounter do(<-oldNodeList 0) <-uid_MonitorTower <-oldNodeList[i] listcontains not if <-oldNodeList i removelistelement return endif loop else <-cycleCounter 1 sub ->cycleCounter endif :buildPlannedTower # randint(0 GetListCount(<-uid_PlannedTower)) ->indexPl # Builds faster in multiple directions. <-uid_PlannedTower getlistcount 1 sub ->indexPl # Builds more systematic, but can leave a front waiting if it's expanding on multiple sides. <-indexPl -1 eq if @findOtherTowers2 else <-uid_PlannedTower[<-indexPl] ->UID <-dir_PlannedTower[<-indexPl] ->direction <-uid_PlannedTower <-indexPl RemoveListElement <-dir_PlannedTower <-indexPl RemoveListElement <-UID <-direction @buildOneTower endif :buildBlockedTower # randint(0 GetListCount(<-uid_BlockedTower)) ->indexBl <-uid_BlockedTower getlistcount 1 sub ->indexBl <-indexBl -1 eq if @buildPlannedTower else <-uid_BlockedTower[<-indexBl] ->UID <-dir_BlockedTower[<-indexBl] ->direction <-uid_BlockedTower <-indexBl RemoveListElement <-dir_BlockedTower <-indexBl RemoveListElement <-UID <-direction @buildOneTower endif :monitorTowers if(<-indexMT 0 lte) GetListCount(<-uid_MonitoredTower) 1 sub ->indexMT else <-indexMT 1 sub ->indexMT endif if(GetUnitCell(<-uid_MonitoredTower[<-indexMT]) pop eq0) if(GetUnitCell(<-uid_MonitorTower[<-indexMT]) pop neq0) <-uid_BlockedTower <-uid_MonitorTower[<-indexMT] PrependToList <-dir_BlockedTower <-dir_MonitorTower[<-indexMT] PrependToList endif <-uid_MonitoredTower <-indexMT RemoveListElement <-uid_MonitorTower <-indexMT RemoveListElement <-dir_MonitorTower <-indexMT RemoveListElement # else # # Some randomized tower building, which might unstuck tower building # <-uid_MonitorTower[<-indexMT] @buildOneRandomTower endif :buildOneRandomTower ->UID randint(1 5) ->rand1_4 <-UID <-rand1_4 @buildOneTower :buildOneTower ->direction ->UID if(GetUnitCell(<-UID) pop eq0) return endif GetUnitPosition(<-UID) ->UIDPos <-UIDPos.y <-connectionHeight add ->UIDPos.y GetUnitType(<-UID) ->unitType <-debugPrint if elapsedtime ->startTime <-UID " " <-direction concat3 " " <-UIDPos concat3 ->debugString endif switch case(<-direction 1 eq) <-relativePositionList1 ->relativeList endcase case(<-direction 2 eq) <-relativePositionList2 ->relativeList endcase case(<-direction 3 eq) <-relativePositionList3 ->relativeList endcase case(<-direction 4 eq) <-relativePositionList4 ->relativeList endcase endswitch # Do a pre-emptive tower check and abort if another tower is found in the middle of the positions list <-relativeList[0] ev2 <-UIDPos.z add ->testPos.z <-UIDPos.x add ->testPos.x GetTerrain(<-testPos.x <-testPos.z) ->testPos.y <-unitType "tower" eq if 1 ->check else 0 ->check endif GetUnits("tower" <-testPos <-quickRangeCheck 0 1 0 0 0 0) getlistcount <-check gt if return # <-debugPrint if <-debugString " " "QuickRangeCheck" concat3 print endif endif # Do a pre-emptive creeper check and classify it as blocked + abort if there is creeper in the middle of the positions list <-quickRangeCheck <-creeperRange sub <-creeperRange max ->checkRange GetCreeperInRange(<-testPos.x <-testPos.z <-checkRange 0 1 0) neq0 if <-uid_BlockedTower <-UID PrependToList <-dir_BlockedTower <-direction PrependToList return # <-debugPrint if <-debugString " " "CreeperRangeCheck" concat3 print endif endif # Check if the chosen direction is (partly) outside the map GetTerrain(<-testPos.x <-testPos.z) lt0 if return endif <-optimalCellSelection ->cellsToCheck -1 ->towersInRange # Even if no optimal towers are found nearby, construction still has to go ahead. 0 ->skipI do(<-relativePositionsCount 1) # Index 0 is used for pre-emptive checks. switch case(<-cellsToCheck lte0) <-debugPrint if <-debugString " " 1 concat3 ->debugString endif break endcase case(<-skipI) # The find units in range are the most taxing. If one of those conditions is true, skip the next few i indices. <-skipI 1 sub ->skipI <-debugPrint if <-debugString " " 2 concat3 ->debugString endif endcase if(<-towersInRange -1 gt) <-cellsToCheck 1 sub ->cellsToCheck endif <-relativeList[i] ev2 <-UIDPos.z add ->testPos.z <-UIDPos.x add ->testPos.x GetTerrain(<-testPos.x <-testPos.z) ->testPos.y case(<-testPos.y 0 lte) # cells outside the map will have a terrain height of -1 <-debugPrint if <-debugString " " 3 concat3 ->debugString endif # <-smallSkip ->skipI # Avoid using this, it degrades the grid. endcase case(GetCellOccupiedCount(<-testPos.x <-testPos.z)) <-debugPrint if <-debugString " " 4 concat3 ->debugString endif endcase # case(IsV3InMap(<-testPos) not) # <-debugPrint if <-debugString " " 5 concat3 ->debugString endif # endcase case(GetTerrainSpecial(<-testPos.x <-testPos.z) <-contaminentSlot eq) <-debugPrint if <-debugString " " 6 concat3 ->debugString endif endcase <-testPos.y <-connectionHeight add ->testPos.y # the next 3 checks are done slightly below the height of the connections. Slightly below because it was sometimes not getting terrainLOS right. distance(<-testPos <-UIDPos) ->testDist case(<-testDist <-maxTowerDistance gte) <-debugPrint if <-debugString " " 7 concat3 ->debugString endif # Do NOT use skipI here. # Sometimes 2 towers at an exact distance of 12 will not connect, so gte and not gt? This was a problem in version 1, also in 2? endcase case(<-testDist <-minTowerDistance lt <-disLOS not and) <-debugPrint if <-debugString " " 8 concat3 ->debugString endif <-largeSkip ->skipI <-cellsToCheck 1 sub ->cellsToCheck # be less picky if(<-towersInRange -1 gt) break endif endcase GetTerrainLOS(<-testPos <-UIDPos 0) ->losPos <-testPos.y <-UIDPos.y min ->connPosMinY <-testPos.y <-UIDPos.y max ->connPosMaxY case(<-losPos.y <-connPosMinY gte <-losPos.y <-connPosMaxY lte and) # <-cellsToCheck 1 sub ->cellsToCheck # be less picky if(<-towersInRange -1 gt) break endif <-debugPrint if <-debugString " " 9 concat3 ->debugString endif endcase <-disLOS if GetUnits("tower" <-testPos <-minTowerDistance 0 1 1 0 0 0) getlistcount ->amtCloseTowers <-testPos.y <-connectionHeight sub ->testPos.y # With LOS, check from an elevated position to get better viezing angles. else <-testPos.y <-connectionHeight sub ->testPos.y GetUnits("tower" <-testPos <-minTowerDistance 0 1 0 0 0 0) getlistcount ->amtCloseTowers endif case(<-amtCloseTowers) # are there other towers too close by. <-debugPrint if <-debugString " " 10 concat3 ->debugString endif <-largeSkip ->skipI <-cellsToCheck 1 sub ->cellsToCheck # be less picky if(<-towersInRange -1 gt) break endif endcase # check the next few cells to look for more towers at a near optimal distance, to form extra connections. case(<-cellsToCheck) <-debugPrint if <-debugString " " 11 concat3 ->debugString endif 0 ->goodTowersNearCell # gt(-1) atleast one suitable cell has been found, so the coordinates should be set. GetUnits("tower" <-testPos <-maxTowerDistance 0 1 0 0 0 0) getlistcount ->goodTowersNearCell if(<-goodTowersNearCell <-towersInRange lt) break endif if(<-goodTowersNearCell <-towersInRange gt) <-goodTowersNearCell ->towersInRange <-testPos.x ->optimalX <-testPos.z ->optimalZ endif if(<-goodTowersNearCell <-optimalTowerTarget gte) break endif <-largeSkip ->skipI endcase <-debugPrint if <-debugString " " 12 concat3 ->debugString endif break # break the loop and build (or plan) the optimal tower outside of the loop endswitch loop if(<-towersInRange -1 gt) switch case(GetMeshHealth(<-optimalX <-optimalZ) 0 gt) <-uid_BlockedTower <-UID PrependToList <-dir_BlockedTower <-direction PrependToList endcase case(GetCreeperInRange(<-optimalX <-optimalZ <-creeperRange 0 1 0) neq0) <-uid_BlockedTower <-UID PrependToList <-dir_BlockedTower <-direction PrependToList endcase CreateUnitOnTerrain("tower" <-optimalX <-optimalZ 0) ->newTower <-debugPrint if <-newTower <-UID "-" <-direction concat3 setunitdebugtext endif # Start monitoring of that one tower <-uid_MonitoredTower <-newTower PrependToList <-uid_MonitorTower <-UID PrependToList <-dir_MonitorTower <-direction PrependToList <-indexMT 1 add ->indexMT <-newTower 999 constructunit endswitch endif <-debugPrint if elapsedtime ->endTime <-endTime <-startTime sub ->spentTime if(<-spentTime 10 gt) getgameupdatecount " " <-spentTime concat3 " " <-debugString concat3 printallsp endif endif