User Tools

Site Tools


cw4:4rpl_tools

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
cw4:4rpl_tools [2023/09/29 12:06] – Random X-Z location on a radius was actually in the area of a circle. Vertucw4:4rpl_tools [2024/03/16 19:53] (current) – [Automated Tower Grid Building] Added a statement for clarity of how to use it. LiteralNoob
Line 10: Line 10:
 //Note: to get a default editor associated with the .4rpl filetype, please read about [[cw4:tutorials:file association]].// //Note: to get a default editor associated with the .4rpl filetype, please read about [[cw4:tutorials:file association]].//
  
-===== NotePad++ =====+===== Notepad++ =====  
 +<note>Make sure you download only from the official Notepad++ website to avoid malware. https://notepad-plus-plus.org/</note>
  
 ==== Syntax Highlighting ==== ==== Syntax Highlighting ====
Line 581: Line 582:
  
 $rmb:1 # right click button $rmb:1 # right click button
-$index:0 
  
-Once 
- @MakeSoundList 
-endOnce 
  
-if (GetMappedKeyDown("Custom1" false)) +if(<-inputDelay gt0) <-inputDelay 1 - ->inputDelay endif 
- Mod(<-Index 1 +, <-soundCount) ->index+ 
 +if (GetMappedKey("Custom1" false) <-inputDelay eq0 &&) 
 + 2 ->inputDelay 
 + Mod(<-index 1 +, <-soundCount) ->index
  <-soundList[<-index] ->soundName  <-soundList[<-index] ->soundName
  TraceAll ("This is sound #" <-index ", " DQ <-soundName DQ)  TraceAll ("This is sound #" <-index ", " DQ <-soundName DQ)
 endIf endIf
  
-if (GetMappedKeyDown("Custom2" false)) +if(GetMappedKey("Custom2" false) <-inputDelay eq0 &&) 
- Mod2(<-Index 1 -, <-soundCount) ->index+ 2 ->inputDelay 
 + Mod2(<-index 1 -, <-soundCount) ->index
  <-soundList[<-index] ->soundName  <-soundList[<-index] ->soundName
  TraceAll ("This is sound #" <-index ", " DQ <-soundName DQ)  TraceAll ("This is sound #" <-index ", " DQ <-soundName DQ)
Line 600: Line 601:
  
 if (GetMouseButtonDown(<-rmb true )) if (GetMouseButtonDown(<-rmb true ))
- PlaySound(<-soundName 8) + PlaySoundAtPosition(<-soundName 8 GetCameraPosition V3(0 0.1 0) +)
 endif endif
- + 
 +:Once 
 + -1 ->index #So we start at 0, not 1. 
 + @MakeSoundList 
 :MakeSoundList :MakeSoundList
- +
  "ADAMessage"    "ADAMessage"  
  "ADAMessagesClose"    "ADAMessagesClose"  
Line 632: Line 637:
  "Explosion"    "Explosion"  
  "Explosion_1"    "Explosion_1"  
- "Explosion_10"   
- "Explosion_11"   
- "Explosion_12"   
  "Explosion_2"    "Explosion_2"  
  "Explosion_3"    "Explosion_3"  
Line 642: Line 644:
  "Explosion_8"    "Explosion_8"  
  "Explosion_9"    "Explosion_9"  
 + "Explosion_10"  
 + "Explosion_11"  
 + "Explosion_12"  
  "HoverOpen"    "HoverOpen"  
  "InfoCacheCollected"    "InfoCacheCollected"  
Line 714: Line 719:
  "Warning2"    "Warning2"  
  List ->soundList  List ->soundList
- +
  "AlarmClock"   "AlarmClock" 
  "GreenarRefinery"    "GreenarRefinery"  
Line 723: Line 728:
  "SoundLoop_alarm0"    "SoundLoop_alarm0"  
  "SoundLoop_alarm1"    "SoundLoop_alarm1"  
- "SoundLoop_alarm10"   
- "SoundLoop_alarm11"   
- "SoundLoop_alarm12"   
- "SoundLoop_alarm13"   
- "SoundLoop_alarm14"   
- "SoundLoop_alarm15"   
  "SoundLoop_alarm2"    "SoundLoop_alarm2"  
  "SoundLoop_alarm3"    "SoundLoop_alarm3"  
Line 737: Line 736:
  "SoundLoop_alarm8"    "SoundLoop_alarm8"  
  "SoundLoop_alarm9"    "SoundLoop_alarm9"  
 + "SoundLoop_alarm10"  
 + "SoundLoop_alarm11"  
 + "SoundLoop_alarm12"  
 + "SoundLoop_alarm13"  
 + "SoundLoop_alarm14"  
 + "SoundLoop_alarm15"  
  "SoundLoop_ambience0"    "SoundLoop_ambience0"  
- "SoundLoop_ambience1"   + "SoundLoop_ambience1"   
- "SoundLoop_ambience10"  +
  "SoundLoop_ambience2"    "SoundLoop_ambience2"  
  "SoundLoop_ambience3"    "SoundLoop_ambience3"  
Line 748: Line 752:
  "SoundLoop_ambience8"    "SoundLoop_ambience8"  
  "SoundLoop_ambience9"    "SoundLoop_ambience9"  
 + "SoundLoop_ambience10"  
  "SoundLoop_gun0"    "SoundLoop_gun0"  
  "SoundLoop_gun1"    "SoundLoop_gun1"  
Line 753: Line 758:
  "SoundLoop_gun3"    "SoundLoop_gun3"  
  "SoundLoop_hum0"    "SoundLoop_hum0"  
- "SoundLoop_hum1"  + "SoundLoop_hum1"  
 + "SoundLoop_hum2"   
 + "SoundLoop_hum3"   
 + "SoundLoop_hum4"   
 + "SoundLoop_hum5"   
 + "SoundLoop_hum6"   
 + "SoundLoop_hum7"   
 + "SoundLoop_hum8"   
 + "SoundLoop_hum9"   
  "SoundLoop_hum10"    "SoundLoop_hum10"  
  "SoundLoop_hum11"    "SoundLoop_hum11"  
Line 763: Line 776:
  "SoundLoop_hum17"   "SoundLoop_hum17" 
  "SoundLoop_hum18"    "SoundLoop_hum18"  
- "SoundLoop_hum2"   
- "SoundLoop_hum3"   
- "SoundLoop_hum4"   
- "SoundLoop_hum5"   
- "SoundLoop_hum6"   
- "SoundLoop_hum7"   
- "SoundLoop_hum8"   
- "SoundLoop_hum9"   
  "SoundLoop_machine0"    "SoundLoop_machine0"  
  "SoundLoop_machine1"    "SoundLoop_machine1"  
- "SoundLoop_machine10"   
- "SoundLoop_machine11"   
- "SoundLoop_machine12"   
  "SoundLoop_machine2"    "SoundLoop_machine2"  
  "SoundLoop_machine3"    "SoundLoop_machine3"  
Line 784: Line 786:
  "SoundLoop_machine8"   "SoundLoop_machine8" 
  "SoundLoop_machine9"    "SoundLoop_machine9"  
 + "SoundLoop_machine10"  
 + "SoundLoop_machine11"  
 + "SoundLoop_machine12"  
  "SoundLoop_misc0"    "SoundLoop_misc0"  
  "SoundLoop_misc1"    "SoundLoop_misc1"  
Line 796: Line 801:
  "ambient"   "ambient" 
  List ->loopSoundList  List ->loopSoundList
- +
  GetListCount(<-soundList) ->soundCount  GetListCount(<-soundList) ->soundCount
- TraceAllSp ("List of " <-soundCount "sounds available"+ TraceAllSp("List of " <-soundCount "sounds available"
- TraceAllSp ("There are also " GetListCOunt(<-loopSoundList) "looping sounds that cannot be played in this module"+ TraceAllSp("There are also " GetListCOunt(<-loopSoundList) "looping sounds that cannot be played in this module"
- TraceAllSP ("Press and hold user key 1/2 to scroll through the sound list." ) + TraceAllSP("Press and hold KeyPad 1 / 2 to scroll through the sound list."
- TraceAllSp ("Left-click mouse to listen to it.")+ TraceAllSp("Left-click mouse to listen to it.")
 </code> </code>
  
Line 811: Line 816:
  
 A few notes. A few notes.
- +  * The script will attempt to unpause the game, since looping sounds only play when not paused due to being attached to a unit.
-  * The Greenar Refinery sound is spatial and you have to be in freeview and right upon the unit in location (1,1) to hear it. +
-  * The script will attempt to unpause the game, since looping sounds only play when not paused.+
   * Remember to unmute game music.   * Remember to unmute game music.
  
Line 823: Line 826:
  
 $rmb:1 # right click button $rmb:1 # right click button
-$index:0 
  
-Once +if(<-inputDelay gt0<-inputDelay 1 - ->inputDelay endif 
- @MakeSoundList + 
- CreateUnitOnTerrain("infocache" 1 1 1) ->unitUID +SetUnitPosition(<-unitUID GetCameraPosition V3(0 1.5 0) +)
- false ->soundOn +
- Getpause ->pause +
-endOnce+
  
-if (GetMappedKeyDown("Custom1" false)) +if(GetMappedKey("Custom1" false) <-inputDelay eq0 &&) 
- Mod(<-Index 1 +, <-soundCount) ->index+ 2 ->inputDelay 
 + Mod(<-index 1 +, <-soundCount) ->index
  <-loopSoundList[<-index] ->soundName  <-loopSoundList[<-index] ->soundName
- TraceAll ("This is sound #" <-index ", " DQ <-soundName DQ)+ TraceAll("This is sound #" <-index ", " DQ <-soundName DQ)
 endIf endIf
  
-if (GetMappedKeyDown("Custom2" false)) +if(GetMappedKey("Custom2" false) <-inputDelay eq0 &&) 
- Mod2(<-Index 1 -, <-soundCount) ->index+ 2 ->inputDelay 
 + Mod2(<-index 1 -, <-soundCount) ->index
  <-loopSoundList[<-index] ->soundName  <-loopSoundList[<-index] ->soundName
- TraceAll ("This is sound #" <-index ", " DQ <-soundName DQ)+ TraceAll("This is sound #" <-index ", " DQ <-soundName DQ)
 endIf endIf
  
-if (GetMouseButtonDown(<-rmb true )) +if(GetMouseButtonDown(<-rmb true)) 
- If (<-soundOn)+ If(<-soundOn)
  StopSoundLoop(<-unitUID)  StopSoundLoop(<-unitUID)
  Not(<-soundOn) ->soundOn  Not(<-soundOn) ->soundOn
  SetPause(<-pause)  SetPause(<-pause)
- TraceAllSp ("Stopping " <-soundName " from playing")+ TraceAllSp("Stopping " <-soundName " from playing")
  else  else
- TraceAllSp ("Can you hear " <-soundName " playing?")+ TraceAllSp("Can you hear " <-soundName " playing?")
  SetPause (false)  SetPause (false)
- PlaySoundLoop(<-soundName <-unitUID) + PlaySoundLoop(<-soundName <-unitUID)
  Not(<-soundOn) ->soundOn  Not(<-soundOn) ->soundOn
  endif  endif
-  
 endif endif
  
Line 862: Line 862:
  TraceAllSp("Goodbye!")  TraceAllSp("Goodbye!")
  DestroyUnit(<-unitUID true true true)  DestroyUnit(<-unitUID true true true)
 + if(<-soundOn) SetPause(true) endif
 +
 +:Once
 + -1 ->index #So we start at 0, not 1.
 + @MakeSoundList
 + CreateUnitOnTerrain("infocache" 1 1 1) ->unitUID
 + false ->soundOn
 + Getpause ->pause
  
-  
 :MakeSoundList :MakeSoundList
- +
  "ADAMessage"    "ADAMessage"  
  "ADAMessagesClose"    "ADAMessagesClose"  
Line 893: Line 900:
  "Explosion"    "Explosion"  
  "Explosion_1"    "Explosion_1"  
- "Explosion_10"   
- "Explosion_11"   
- "Explosion_12"   
  "Explosion_2"    "Explosion_2"  
  "Explosion_3"    "Explosion_3"  
Line 903: Line 907:
  "Explosion_8"    "Explosion_8"  
  "Explosion_9"    "Explosion_9"  
 + "Explosion_10"  
 + "Explosion_11"  
 + "Explosion_12"  
  "HoverOpen"    "HoverOpen"  
  "InfoCacheCollected"    "InfoCacheCollected"  
Line 913: Line 920:
  "MissionObjectiveFail"    "MissionObjectiveFail"  
  "MissionObjectiveRequiredComplete"    "MissionObjectiveRequiredComplete"  
- "MissionScan"     + "MissionScan"    "InhibitorFiring"   
- "InhibitorFiring"   +
  "MissionSpaceInitiateJump"    "MissionSpaceInitiateJump"  
  "MissionSpacePanelClose"    "MissionSpacePanelClose"  
Line 962: Line 968:
  "SporePrelaunch"    "SporePrelaunch"  
  "SprayerFire"    "SprayerFire"  
- "Stun"     + "Stun"    "CannonFire"  
- "CannonFire"  +
  "SurviveBaseOffline"    "SurviveBaseOffline"  
  "SurviveBaseWarn"    "SurviveBaseWarn"  
  "ThorGun"    "ThorGun"  
- "TotemExplosion"    + "TotemExplosion"   "UnitHoverLand"  
- "UnitHoverLand"  +
  "UnitBuild"    "UnitBuild"  
  "UnitExplosion"    "UnitExplosion"  
Line 987: Line 991:
  "SoundLoop_alarm0"    "SoundLoop_alarm0"  
  "SoundLoop_alarm1"    "SoundLoop_alarm1"  
- "SoundLoop_alarm10"   
- "SoundLoop_alarm11"   
- "SoundLoop_alarm12"   
- "SoundLoop_alarm13"   
- "SoundLoop_alarm14"   
- "SoundLoop_alarm15"   
  "SoundLoop_alarm2"    "SoundLoop_alarm2"  
  "SoundLoop_alarm3"    "SoundLoop_alarm3"  
Line 1001: Line 999:
  "SoundLoop_alarm8"    "SoundLoop_alarm8"  
  "SoundLoop_alarm9"    "SoundLoop_alarm9"  
 + "SoundLoop_alarm10"  
 + "SoundLoop_alarm11"  
 + "SoundLoop_alarm12"  
 + "SoundLoop_alarm13"  
 + "SoundLoop_alarm14"  
 + "SoundLoop_alarm15"  
  "SoundLoop_ambience0"    "SoundLoop_ambience0"  
- "SoundLoop_ambience1"   + "SoundLoop_ambience1"   
- "SoundLoop_ambience10"  +
  "SoundLoop_ambience2"    "SoundLoop_ambience2"  
  "SoundLoop_ambience3"    "SoundLoop_ambience3"  
Line 1012: Line 1015:
  "SoundLoop_ambience8"    "SoundLoop_ambience8"  
  "SoundLoop_ambience9"    "SoundLoop_ambience9"  
 + "SoundLoop_ambience10"  
  "SoundLoop_gun0"    "SoundLoop_gun0"  
  "SoundLoop_gun1"    "SoundLoop_gun1"  
Line 1017: Line 1021:
  "SoundLoop_gun3"    "SoundLoop_gun3"  
  "SoundLoop_hum0"    "SoundLoop_hum0"  
- "SoundLoop_hum1"  + "SoundLoop_hum1"  
 + "SoundLoop_hum2"   
 + "SoundLoop_hum3"   
 + "SoundLoop_hum4"   
 + "SoundLoop_hum5"   
 + "SoundLoop_hum6"   
 + "SoundLoop_hum7"   
 + "SoundLoop_hum8"   
 + "SoundLoop_hum9"   
  "SoundLoop_hum10"    "SoundLoop_hum10"  
  "SoundLoop_hum11"    "SoundLoop_hum11"  
Line 1027: Line 1039:
  "SoundLoop_hum17"   "SoundLoop_hum17" 
  "SoundLoop_hum18"    "SoundLoop_hum18"  
- "SoundLoop_hum2"   
- "SoundLoop_hum3"   
- "SoundLoop_hum4"   
- "SoundLoop_hum5"   
- "SoundLoop_hum6"   
- "SoundLoop_hum7"   
- "SoundLoop_hum8"   
- "SoundLoop_hum9"   
  "SoundLoop_machine0"    "SoundLoop_machine0"  
  "SoundLoop_machine1"    "SoundLoop_machine1"  
- "SoundLoop_machine10"   
- "SoundLoop_machine11"   
- "SoundLoop_machine12"   
  "SoundLoop_machine2"    "SoundLoop_machine2"  
  "SoundLoop_machine3"    "SoundLoop_machine3"  
Line 1048: Line 1049:
  "SoundLoop_machine8"   "SoundLoop_machine8" 
  "SoundLoop_machine9"    "SoundLoop_machine9"  
 + "SoundLoop_machine10"  
 + "SoundLoop_machine11"  
 + "SoundLoop_machine12"  
  "SoundLoop_misc0"    "SoundLoop_misc0"  
  "SoundLoop_misc1"    "SoundLoop_misc1"  
Line 1060: Line 1064:
  "ambient"   "ambient" 
  List ->loopSoundList  List ->loopSoundList
- +
  GetListCount(<-loopSoundList) ->soundCount  GetListCount(<-loopSoundList) ->soundCount
- TraceAllSp ("List of " <-soundCount "looping sounds available"+ TraceAllSp("List of " <-soundCount "looping sounds available"
- TraceAllSP ("Press and hold user key 1/2 to scroll through the sound list." ) + TraceAllSP("Press and hold KeyPad 1 / 2 to scroll through the sound list."
- TraceAllSp ("Move mouse out of edit box. Left-click mouse to listen to a sound. " ) + TraceAllSp("Move mouse out of edit box. Left-click mouse to listen to a sound. ") 
- TraceAllSP ("Left-click to turn off again."+ TraceAllSP("Left-click to turn off again."
- TraceAllSp ("Remember to mute music!")+ TraceAllSp("Remember to mute music!")
 </code> </code>
  
Line 2328: Line 2332:
  Trace5("'/' = disperse selected with random height from " <-RAND_MIN ":" <-RAND_MAX " with RandFloat too.")  Trace5("'/' = disperse selected with random height from " <-RAND_MIN ":" <-RAND_MAX " with RandFloat too.")
 </code></hidden> </code></hidden>
 +
 +----
 +
 +===== Automated Tower Grid Building =====
 +
 +The below script originally existed as a map script that builds out the tower grid for/alongside the player. I've adapted it for console usage in the editor, by blocking out specicific code and instantly constructing all towers that it places.
 +
 +If you have a large map with varied terrain and you want to want to wire it up fast, then this script is worth a try.
 +
 +New towers will be constructed if they can connect to: existing riftlab, m-rift, pylons, energy pods and towers.
 +
 +To run the console script, simply let it run continuously.
 +
 +<hidden click here for source code>
 +
 +<code 4rpl file name.4rpl>
 +# 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
 +</code>
 +
 +</hidden>
 +
 +----
 +
 +===== Faster vanilla unit placement : Snapping Tool =====
 +
 +The snapping tool makes use of a unit for highlighting the nearest buildable cell, so it's a cpack instead of a script : [[https://knucklecracker.com/wiki/doku.php?id=cw4:cpack:docs:e58ef3f2-1468-42fa-ac84-c3561ddd9452|Kalli - SnappingTool]]
  
 ---- ----
cw4/4rpl_tools.1696003581.txt.gz · Last modified: 2023/09/29 12:06 by Vertu