Both sides previous revisionPrevious revisionNext revision | Previous revisionNext revisionBoth sides next revision |
cw4:4rpl_tools [2024/01/18 14:56] – Kalli | cw4:4rpl_tools [2024/03/16 19:53] – [Automated Tower Grid Building] Added a statement for clarity of how to use it. LiteralNoob |
---|
| |
$rmb:1 # right click button | $rmb:1 # right click button |
$index:0 | |
| |
Once | |
@MakeSoundList | |
endOnce | |
| |
if (GetMappedKey("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 (GetMappedKey("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) |
| |
if (GetMouseButtonDown(<-rmb true )) | if (GetMouseButtonDown(<-rmb true )) |
PlaySoundAtPosition(<-soundName 8 GetCameraPosition) | 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" |
"Explosion" | "Explosion" |
"Explosion_1" | "Explosion_1" |
"Explosion_10" | |
"Explosion_11" | |
"Explosion_12" | |
"Explosion_2" | "Explosion_2" |
"Explosion_3" | "Explosion_3" |
"Explosion_8" | "Explosion_8" |
"Explosion_9" | "Explosion_9" |
| "Explosion_10" |
| "Explosion_11" |
| "Explosion_12" |
"HoverOpen" | "HoverOpen" |
"InfoCacheCollected" | "InfoCacheCollected" |
"Warning2" | "Warning2" |
List ->soundList | List ->soundList |
| |
"AlarmClock" | "AlarmClock" |
"GreenarRefinery" | "GreenarRefinery" |
"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" |
"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" |
"SoundLoop_ambience8" | "SoundLoop_ambience8" |
"SoundLoop_ambience9" | "SoundLoop_ambience9" |
| "SoundLoop_ambience10" |
"SoundLoop_gun0" | "SoundLoop_gun0" |
"SoundLoop_gun1" | "SoundLoop_gun1" |
"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" |
"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" |
"SoundLoop_machine8" | "SoundLoop_machine8" |
"SoundLoop_machine9" | "SoundLoop_machine9" |
| "SoundLoop_machine10" |
| "SoundLoop_machine11" |
| "SoundLoop_machine12" |
"SoundLoop_misc0" | "SoundLoop_misc0" |
"SoundLoop_misc1" | "SoundLoop_misc1" |
"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> |
| |
| |
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. |
| |
| |
$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 | |
false ->soundOn | |
Getpause ->pause | |
endOnce | |
SetUnitPosition(<-unitUID GetCameraPosition V3(0 1.5 0) +) | SetUnitPosition(<-unitUID GetCameraPosition V3(0 1.5 0) +) |
| |
if (GetMappedKey("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 (GetMappedKey("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 5 <-unitUID) | PlaySoundLoop(<-soundName 5 <-unitUID) |
Not(<-soundOn) ->soundOn | Not(<-soundOn) ->soundOn |
endif | endif |
| |
endif | endif |
| |
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" |
"Explosion" | "Explosion" |
"Explosion_1" | "Explosion_1" |
"Explosion_10" | |
"Explosion_11" | |
"Explosion_12" | |
"Explosion_2" | "Explosion_2" |
"Explosion_3" | "Explosion_3" |
"Explosion_8" | "Explosion_8" |
"Explosion_9" | "Explosion_9" |
| "Explosion_10" |
| "Explosion_11" |
| "Explosion_12" |
"HoverOpen" | "HoverOpen" |
"InfoCacheCollected" | "InfoCacheCollected" |
"MissionObjectiveFail" | "MissionObjectiveFail" |
"MissionObjectiveRequiredComplete" | "MissionObjectiveRequiredComplete" |
"MissionScan" | "MissionScan" "InhibitorFiring" |
"InhibitorFiring" | |
"MissionSpaceInitiateJump" | "MissionSpaceInitiateJump" |
"MissionSpacePanelClose" | "MissionSpacePanelClose" |
"SporePrelaunch" | "SporePrelaunch" |
"SprayerFire" | "SprayerFire" |
"Stun" | "Stun" "CannonFire" |
"CannonFire" | |
"SurviveBaseOffline" | "SurviveBaseOffline" |
"SurviveBaseWarn" | "SurviveBaseWarn" |
"ThorGun" | "ThorGun" |
"TotemExplosion" | "TotemExplosion" "UnitHoverLand" |
"UnitHoverLand" | |
"UnitBuild" | "UnitBuild" |
"UnitExplosion" | "UnitExplosion" |
"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" |
"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" |
"SoundLoop_ambience8" | "SoundLoop_ambience8" |
"SoundLoop_ambience9" | "SoundLoop_ambience9" |
| "SoundLoop_ambience10" |
"SoundLoop_gun0" | "SoundLoop_gun0" |
"SoundLoop_gun1" | "SoundLoop_gun1" |
"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" |
"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" |
"SoundLoop_machine8" | "SoundLoop_machine8" |
"SoundLoop_machine9" | "SoundLoop_machine9" |
| "SoundLoop_machine10" |
| "SoundLoop_machine11" |
| "SoundLoop_machine12" |
"SoundLoop_misc0" | "SoundLoop_misc0" |
"SoundLoop_misc1" | "SoundLoop_misc1" |
"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> |
| |
---- | ---- |
| |
===== Next Utility goes here ===== | ===== 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> | <hidden click here for source code> |
| |
<code 4rpl file name.4rpl> | <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> | </code> |
| |
</hidden> | </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]] |
| |
---- | ---- |