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/10/18 19:57] – adding notepad disclaimer Bobcw4:4rpl_tools [2025/06/30 20:12] (current) – [Pseudo Random Number Generator, based on sinus] kalli
Line 582: 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 601: 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 633: Line 637:
  "Explosion"    "Explosion"  
  "Explosion_1"    "Explosion_1"  
- "Explosion_10"   
- "Explosion_11"   
- "Explosion_12"   
  "Explosion_2"    "Explosion_2"  
  "Explosion_3"    "Explosion_3"  
Line 643: Line 644:
  "Explosion_8"    "Explosion_8"  
  "Explosion_9"    "Explosion_9"  
 + "Explosion_10"  
 + "Explosion_11"  
 + "Explosion_12"  
  "HoverOpen"    "HoverOpen"  
  "InfoCacheCollected"    "InfoCacheCollected"  
Line 715: Line 719:
  "Warning2"    "Warning2"  
  List ->soundList  List ->soundList
- +
  "AlarmClock"   "AlarmClock" 
  "GreenarRefinery"    "GreenarRefinery"  
Line 724: 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 738: 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 749: Line 752:
  "SoundLoop_ambience8"    "SoundLoop_ambience8"  
  "SoundLoop_ambience9"    "SoundLoop_ambience9"  
 + "SoundLoop_ambience10"  
  "SoundLoop_gun0"    "SoundLoop_gun0"  
  "SoundLoop_gun1"    "SoundLoop_gun1"  
Line 754: 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 764: 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 785: 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 797: 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 812: 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 824: 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 +
- false ->soundOn +
- Getpause ->pause +
-endOnce+
  
-if (GetMappedKeyDown("Custom1" false)) +SetUnitPosition(<-unitUID GetCameraPosition V3(0 1.5 0) +) 
- Mod(<-Index 1 +, <-soundCount) ->index+ 
 +if(GetMappedKey("Custom1" false) <-inputDelay eq0 &&) 
 + 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 863: 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 894: Line 900:
  "Explosion"    "Explosion"  
  "Explosion_1"    "Explosion_1"  
- "Explosion_10"   
- "Explosion_11"   
- "Explosion_12"   
  "Explosion_2"    "Explosion_2"  
  "Explosion_3"    "Explosion_3"  
Line 904: Line 907:
  "Explosion_8"    "Explosion_8"  
  "Explosion_9"    "Explosion_9"  
 + "Explosion_10"  
 + "Explosion_11"  
 + "Explosion_12"  
  "HoverOpen"    "HoverOpen"  
  "InfoCacheCollected"    "InfoCacheCollected"  
Line 914: Line 920:
  "MissionObjectiveFail"    "MissionObjectiveFail"  
  "MissionObjectiveRequiredComplete"    "MissionObjectiveRequiredComplete"  
- "MissionScan"     + "MissionScan"    "InhibitorFiring"   
- "InhibitorFiring"   +
  "MissionSpaceInitiateJump"    "MissionSpaceInitiateJump"  
  "MissionSpacePanelClose"    "MissionSpacePanelClose"  
Line 963: 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 988: 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 1002: 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 1013: Line 1015:
  "SoundLoop_ambience8"    "SoundLoop_ambience8"  
  "SoundLoop_ambience9"    "SoundLoop_ambience9"  
 + "SoundLoop_ambience10"  
  "SoundLoop_gun0"    "SoundLoop_gun0"  
  "SoundLoop_gun1"    "SoundLoop_gun1"  
Line 1018: 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 1028: 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 1049: 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 1061: 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 2209: Line 2212:
 #By Vertu #By Vertu
  
-$HEIGHT:100.0 #Float or Integer +$HEIGHT:3000.0 #Float or Integer 
-$RAND_MAX:8   #Integer +$RAND_MAX:15 #Integer 
-$RAND_MIN:-8  #Integer+$RAND_MIN:25 #Integer 
 +$STEP:1.0 #--How far something moves per incrament when using the script. 
 +$$BUFFER:2
  
-if(GetKey("RightArrow" false))+if(<-inputDelay gt0) <-inputDelay 1 - ->inputDelay endif 
 + 
 +if(GetKey("RightArrow" false) <-inputDelay eq0 &&) 
 + true ->doesBuffer
  GetSelectedUnits ->units  GetSelectedUnits ->units
  do(GetListCount(<-units) 0)  do(GetListCount(<-units) 0)
  GetUnitPosition(<-units[I]) ->unitPosPre  GetUnitPosition(<-units[I]) ->unitPosPre
- SetUnitPosition(<-units[I] V3(<-unitPosPre.x + <-unitPosPre.y <-unitPosPre.z))+ SetUnitPosition(<-units[I] V3(<-unitPosPre.x <-STEP + <-unitPosPre.y <-unitPosPre.z))
  GetTerrain(GetUnitCell(<-units[I])) ->terrainH  GetTerrain(GetUnitCell(<-units[I])) ->terrainH
  GetUnitPosition(<-units[I]) ->unitPos  GetUnitPosition(<-units[I]) ->unitPos
Line 2223: Line 2231:
  SetUnitOccupiesLand(<-units[I] true)  SetUnitOccupiesLand(<-units[I] true)
  else  else
- if(<-unitPos.y lt(<-terrainH)) SetUnitOccupiesLand(<-units[I] false) endif+ if(<-unitPos.y lt(<-terrainH)) endif #SetUnitOccupiesLand(<-units[I] false) endif
  endif  endif
  loop  loop
 endif endif
-if(GetKey("LeftArrow" false))+if(GetKey("LeftArrow" false) <-inputDelay eq0 &&) 
 + true ->doesBuffer
  GetSelectedUnits ->units  GetSelectedUnits ->units
  do(GetListCount(<-units) 0)  do(GetListCount(<-units) 0)
  GetUnitPosition(<-units[I]) ->unitPosPre  GetUnitPosition(<-units[I]) ->unitPosPre
- SetUnitPosition(<-units[I] V3(<-unitPosPre.x - <-unitPosPre.y <-unitPosPre.z))+ SetUnitPosition(<-units[I] V3(<-unitPosPre.x <-STEP - <-unitPosPre.y <-unitPosPre.z))
  GetTerrain(GetUnitCell(<-units[I])) ->terrainH  GetTerrain(GetUnitCell(<-units[I])) ->terrainH
  GetUnitPosition(<-units[I]) ->unitPos  GetUnitPosition(<-units[I]) ->unitPos
Line 2237: Line 2246:
  SetUnitOccupiesLand(<-units[I] true)  SetUnitOccupiesLand(<-units[I] true)
  else  else
- if(<-unitPos.y lt(<-terrainH)) SetUnitOccupiesLand(<-units[I] false) endif+ if(<-unitPos.y lt(<-terrainH)) endif #SetUnitOccupiesLand(<-units[I] false) endif
  endif  endif
  loop  loop
 endif endif
-if(GetKey("UpArrow" false))+if(GetKey("UpArrow" false) <-inputDelay eq0 &&) 
 + true ->doesBuffer
  GetSelectedUnits ->units  GetSelectedUnits ->units
  do(GetListCount(<-units) 0)  do(GetListCount(<-units) 0)
  GetUnitPosition(<-units[I]) ->unitPosPre  GetUnitPosition(<-units[I]) ->unitPosPre
- SetUnitPosition(<-units[I] V3(<-unitPosPre.x <-unitPosPre.y <-unitPosPre.z +))+ SetUnitPosition(<-units[I] V3(<-unitPosPre.x <-unitPosPre.y <-unitPosPre.z <-STEP +))
  GetTerrain(GetUnitCell(<-units[I])) ->terrainH  GetTerrain(GetUnitCell(<-units[I])) ->terrainH
  GetUnitPosition(<-units[I]) ->unitPos  GetUnitPosition(<-units[I]) ->unitPos
Line 2251: Line 2261:
  SetUnitOccupiesLand(<-units[I] true)  SetUnitOccupiesLand(<-units[I] true)
  else  else
- if(<-unitPos.y lt(<-terrainH)) SetUnitOccupiesLand(<-units[I] false) endif+ if(<-unitPos.y lt(<-terrainH)) endif #SetUnitOccupiesLand(<-units[I] false) endif
  endif  endif
  loop  loop
 endif endif
-if(GetKey("DownArrow" false))+if(GetKey("DownArrow" false) <-inputDelay eq0 &&) 
 + true ->doesBuffer
  GetSelectedUnits ->units  GetSelectedUnits ->units
  do(GetListCount(<-units) 0)  do(GetListCount(<-units) 0)
  GetUnitPosition(<-units[I]) ->unitPosPre  GetUnitPosition(<-units[I]) ->unitPosPre
- SetUnitPosition(<-units[I] V3(<-unitPosPre.x <-unitPosPre.y <-unitPosPre.z -))+ SetUnitPosition(<-units[I] V3(<-unitPosPre.x <-unitPosPre.y <-unitPosPre.z <-STEP -))
  GetTerrain(GetUnitCell(<-units[I])) ->terrainH  GetTerrain(GetUnitCell(<-units[I])) ->terrainH
  GetUnitPosition(<-units[I]) ->unitPos  GetUnitPosition(<-units[I]) ->unitPos
Line 2265: Line 2276:
  SetUnitOccupiesLand(<-units[I] true)  SetUnitOccupiesLand(<-units[I] true)
  else  else
- if(<-unitPos.y lt(<-terrainH)) SetUnitOccupiesLand(<-units[I] false) endif+ if(<-unitPos.y lt(<-terrainH)) endif #SetUnitOccupiesLand(<-units[I] false) endif
  endif  endif
  loop  loop
 endif endif
-if(GetKey("PageUp" false))+if(GetKey("PageUp" false) <-inputDelay eq0 &&) 
 + true ->doesBuffer
  GetSelectedUnits ->units  GetSelectedUnits ->units
  do(GetListCount(<-units) 0)  do(GetListCount(<-units) 0)
  GetUnitPosition(<-units[I]) ->unitPosPre  GetUnitPosition(<-units[I]) ->unitPosPre
- SetUnitPosition(<-units[I] V3(<-unitPosPre.x <-unitPosPre.y + <-unitPosPre.z))+ SetUnitPosition(<-units[I] V3(<-unitPosPre.x <-unitPosPre.y <-STEP + <-unitPosPre.z))
  GetTerrain(GetUnitCell(<-units[I])) ->terrainH  GetTerrain(GetUnitCell(<-units[I])) ->terrainH
  GetUnitPosition(<-units[I]) ->unitPos  GetUnitPosition(<-units[I]) ->unitPos
Line 2279: Line 2291:
  SetUnitOccupiesLand(<-units[I] true)  SetUnitOccupiesLand(<-units[I] true)
  else  else
- if(<-unitPos.y lt(<-terrainH)) SetUnitOccupiesLand(<-units[I] false) endif+ if(<-unitPos.y lt(<-terrainH)) endif #SetUnitOccupiesLand(<-units[I] false) endif
  endif  endif
  loop  loop
 endif endif
-if(GetKey("PageDown" false))+if(GetKey("PageDown" false) <-inputDelay eq0 &&) 
 + true ->doesBuffer
  GetSelectedUnits ->units  GetSelectedUnits ->units
  do(GetListCount(<-units) 0)  do(GetListCount(<-units) 0)
  GetUnitPosition(<-units[I]) ->unitPosPre  GetUnitPosition(<-units[I]) ->unitPosPre
- SetUnitPosition(<-units[I] V3(<-unitPosPre.x <-unitPosPre.y - <-unitPosPre.z))+ SetUnitPosition(<-units[I] V3(<-unitPosPre.x <-unitPosPre.y <-STEP - <-unitPosPre.z))
  GetTerrain(GetUnitCell(<-units[I])) ->terrainH  GetTerrain(GetUnitCell(<-units[I])) ->terrainH
  GetUnitPosition(<-units[I]) ->unitPos  GetUnitPosition(<-units[I]) ->unitPos
Line 2293: Line 2306:
  SetUnitOccupiesLand(<-units[I] true)  SetUnitOccupiesLand(<-units[I] true)
  else  else
- if(<-unitPos.y lt(<-terrainH)) SetUnitOccupiesLand(<-units[I] false) endif+ if(<-unitPos.y lt(<-terrainH)) endif #SetUnitOccupiesLand(<-units[I] false) endif
  endif  endif
  loop  loop
 endif endif
 if(GetKey("Backspace" false)) if(GetKey("Backspace" false))
 + true ->doesBuffer
  GetSelectedUnits ->units  GetSelectedUnits ->units
  do(GetListCount(<-units) 0)  do(GetListCount(<-units) 0)
Line 2307: Line 2321:
  SetUnitOccupiesLand(<-units[I] true)  SetUnitOccupiesLand(<-units[I] true)
  else  else
- if(<-unitPos.y lt(<-terrainH)) SetUnitOccupiesLand(<-units[I] false) endif+ if(<-unitPos.y lt(<-terrainH)) endif #SetUnitOccupiesLand(<-units[I] false) endif
  endif  endif
  loop  loop
 endif endif
  
-if(GetKey("Slash" false))+if(GetKey("Slash" false) <-inputDelay eq0 &&) 
 + true ->doesBuffer
  GetSelectedUnits ->units  GetSelectedUnits ->units
  do(GetListCount(<-units) 0)  do(GetListCount(<-units) 0)
Line 2319: Line 2334:
  SetUnitPosition(<-units[I] V3(<-unitPosPre.x <-randHeight <-unitPosPre.z))  SetUnitPosition(<-units[I] V3(<-unitPosPre.x <-randHeight <-unitPosPre.z))
  loop  loop
 +endif
 +
 +if(<-doesBuffer)
 + false ->doesBuffer
 + <-BUFFER ->inputDelay
 endif endif
  
Line 2329: Line 2349:
  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]]
 +
 +----
 +
 +===== Make Units Unselectable =====
 +
 +You can make friendly/enemy/all units (un)selectable and/or (un)destroyable with this script.
 +
 +The $ variables are now set so that friendly (=human) units are set unselectable and undestroyable, so that it's ready to use in a pac map. The variables are either 0 or 1.
 +
 +Enemy (=creeper) units are always undestroyable. Changing this setting with 4rpl does nothing, but also doesn't break anything.
 +
 +The script can be used from the console, or be added as a global script to a cpack. If used as a global script, then the script must run while paused. It will only apply to units that exist at map start.
 +
 +<hidden click here for source code>
 +
 +<code 4rpl MakeUnitsUnselectable.4rpl>
 +
 +# MakeUnitsUnselectable
 +
 +# The script can be used from the console, or be added as a global script to a cpack.
 +# If used as a global script, then the script must run while paused. It will only apply to units that exist at map start.
 +
 +$applyToFriendlyUnits:1
 +
 +$applyToEnemyUnits:0
 +
 +$applyToAllUnits:0
 +
 +$makeUnitsSelectable:0
 +$makeUnitsDestroyable:0
 +
 +:once
 + GetMapSize 2 div ->midPointZ 2 div ->midPointX 
 +
 + <-midPointX 0 <-midpointZ v3 ->pos
 +
 + <-applyToAllUnits <-applyToFriendlyUnits <-applyToEnemyUnits and or if
 + 0 ->enemyState
 + else
 + <-applyToFriendlyUnits if 2 else <-applyToEnemyUnits endif ->enemyState
 + endif
 +
 + # The position is the center of the map. The max possible range for units within the map boundaries is (512/2^2 + 128/2^2)^0.5 +1 = 265. To also catch units that have wandered just outside the map boundaries, I chose range 300.
 + "" <-pos 300 0 0 0 <-enemyState 0 0 getunits ->unitList
 +
 + <-unitList 0 do 
 + <-unitList[i] <-makeUnitsSelectable SetUnitSelectable
 + <-unitList[i] <-makeUnitsDestroyable SetUnitCanDestroy
 + loop
 +
 +</code>
 +
 +</hidden>
 +
 +----
 +
 +===== Set terrain higher than 20 =====
 +A console script that lets you place terrain higher than 20. Details below.
 +<hidden click here for source code>
 +Notes about terrain higher than 20:\\
 +  * There is a limit to how high terrain can be before it causes an overflow and crashes your game should the script try to make that terrain height. I have limited the script to a height of 100 to be safe, I have not tested when this crash occurs but I do know it does. Terrain higher than 100 gets excessive anyways.
 +  * The game was not built to account for terrain higher than 20, this means Creeper will not render at the top of the terrain but at a height of 20 (the maximum terrain height) should Creeper be on terrain higher than 20.
 +  * The build system does not detect terrain higher than 20, you have to point your mouse down as if it was a height of 20 in order to place units on that terrain. Obviously the best workaround to this is using top-down view.
 +  * The game doesn't fully render terrain higher than 20 graphically. The cliffs of terrain higher than 20 will have stripes of black and after a height of 50, the final black stripe will continue to "infinity" height.
 +  * The surfaces of various terrain higher than 20 will have **<fc #ff0000>BLINDING</fc>** bloom effects. If the surface isn't void-black, it is best you do not use that height as with bloom enabled, it **is** blinding. The effect is typically encountered when the terrain is at a height within a black stripe with the exception of height higher than 50.
 +  * I recommend only using terrain higher than 20 for visual effect rather than gameplay due to Creeper not rendering at the surface of the terrain. Best to make it impossible for Creeper to reach such areas.
 +<code 4rpl SetTerrainAbove20.4rpl>
 +# SetTerrainAbove20
 +#By Vertu.
 +
 +$HEIGHT:42 #--Integer, incramented by +-1 via hotkeys.
 +
 +$$BUFFER:2
 +
 +if(<-inputDelay gt0) <-inputDelay 1 - ->inputDelay endif
 +
 +if(<-phase2 ! GetKeyDown("Space" true) &&)
 + GetPointerTerrainCoords ->y1 ->x1
 + if(IsV2InMap(V2(<-x1 <-y1)))
 + true ->phase2
 + Trace4("Lower Coord: " <-x1 " " <-y1)
 + else
 + Trace("That wasn't in the map. Try again.")
 + endif
 +else
 +if(<-phase2 GetKeyDown("Space" true) &&)
 + GetPointerTerrainCoords ->y2 ->x2
 + if(IsV2InMap(V2(<-x2 <-y2)))
 + false ->phase2
 + Trace4("Upper Coord: " <-x2 " " <-y2)
 + else
 + Trace("That wasn't in the map. Try again.")
 + endif
 +endif
 +endif
 +if(GetKeyDown("Backspace" true) <-x1 neq(-1) && <-x2 neq(-1) &&)
 + <-y2 1 add <-y1 do
 +    <-x2 1 add <-x1 do
 + while I J GetTerrain <-setHeight neq repeat
 + SetTerrainInRange(I J <-setHeight dup 0 false 1)
 + endwhile
 +    loop
 + loop
 + ClearTraceLog
 + Trace4(<-keyBindText "
 +Height set to " <-setHeight ".")
 + EditAddUndo(0)
 +endif
 +
 +if(GetKeyDown("Keypad0" true))
 + ClearTraceLog
 + Trace4(<-keyBindText "
 +Height set to " <-setHeight ".")
 + GetPointerTerrainCoords ->posZ ->posX
 + FloodFillTerrain(<-posX <-posZ GetTerrain(<-posX <-posZ) GetTerrain(<-posX <-posZ) 1 + 10,000,000) ->cells
 + do(GetListCount(<-cells) 0)
 + EV2(<-cells[I]) ->cellyZ ->cellyX
 + while <-cellyX <-cellyZ GetTerrain <-setHeight neq repeat
 + SetTerrainInRange(<-cellyX <-cellyZ <-setHeight dup 0 false 1)
 + endwhile
 + loop
 + Trace3("Completed flood fill to height" <-setHeight ".")
 + EditAddUndo(0)
 +endif
 +
 +if(GetKey("PageUp" false) <-setHeight lt(100) && <-inputDelay eq0 &&)
 + <-setHeight 1 + ->setHeight
 + ClearTraceLog
 + Trace4(<-keyBindText "
 +Height set to " <-setHeight ".")
 + <-BUFFER ->inputDelay
 +endif
 +if(GetKey("PageDown" false) <-setHeight gt0 && <-inputDelay eq0 &&)
 + <-setHeight 1 - ->setHeight
 + ClearTraceLog
 + Trace4(<-keyBindText "
 +Height set to " <-setHeight ".")
 + <-BUFFER ->inputDelay
 +endif
 +
 +:Once
 + <-HEIGHT ->setHeight
 + "Key binds:
 +SpaceBar to specify coordinates, then backspace to set terrain as a rectangle.
 +
 +PageUp and PageDown to increase and decrease set terrain height.
 +
 +Pressing Keypad0 will conduct a flood-fill to set terrain to specified height." ->keyBindText
 + Trace(<-keyBindText)
 + Trace3("Height set to " <-HEIGHT ".")
 + -1 ->x1
 + -1 ->x2
 + -1 ->y1
 + -1 ->y2
 +</code>
 +
 +</hidden>
  
 ---- ----
Line 2695: Line 3463:
 <fs large>**Applying to a V3:**</fs> <fs large>**Applying to a V3:**</fs>
 <code> <code>
-V3(cos(RandFloat 2.0 / RandInt(0 7) +) <-RADIUS * <-cellX + <-Y_COORDINANT RandInt(-15 16) + sin(RandFloat 2.0 / RandInt(0 7) +) <-RADIUS * <-cellZ +) ->position+V3(cos(RandFloat RandInt(0 7) +) <-RADIUS * <-cellX + <-Y_COORDINANT RandInt(-15 16) + sin(RandFloat RandInt(0 7) +) <-RADIUS * <-cellZ +) ->position
 </code> </code>
 <fs large>**Applying to a V2:**</fs> <fs large>**Applying to a V2:**</fs>
 <code> <code>
-V2(cos(RandFloat 2.0 / RandInt(0 7) +) <-RADIUS * <-cellX + sin(RandFloat 2.0 / RandInt(0 7) +) <-RADIUS * <-cellZ +) ->cellLocation+V2(cos(RandFloat RandInt(0 7) +) <-RADIUS * <-cellX + sin(RandFloat RandInt(0 7) +) <-RADIUS * <-cellZ +) ->cellLocation
 </code> </code>
  
Line 2719: Line 3487:
  if(GetEditMode !)  if(GetEditMode !)
  GetUnitPosition(self) ->placementPos  GetUnitPosition(self) ->placementPos
- SetUnitPosition(self V3(cos(RandFloat 2.0 / RandInt(0 7) +) 45 * <-placementPos.x + <-FALL_HEIGHT RandInt(-15 16) + sin(RandFloat 2.0 / RandInt(0 7) +) 45 * <-placementPos.z +))+ SetUnitPosition(self V3(cos(RandFloat RandInt(0 7) +) 45 * <-placementPos.x + <-FALL_HEIGHT RandInt(-15 16) + sin(RandFloat RandInt(0 7) +) 45 * <-placementPos.z +))
  endif  endif
 </code> </code>
  
 +</hidden>
 +
 +
 +----
 +===== Random X-Z position along the circumference of a circle =====
 +
 +<hidden click here for source code>
 +This is a simple piece of trigonometry that is able to pick a random position along the circumference of a circle on the X-Z plain with a definable radius. This is useful in the event you wish to introduce circles into your 4RPL code's behavior.\\
 +Uses [[4rpl:commands:randint|RandInt]] and [[4rpl:commands:randfloat|RandFloat]] to determine the angle on the unit circle where the position along the circumference is.\\
 +Note: Floats will work for the radius.\\
 +\\
 +<code>
 +RandFloat RandInt(0 7) + ->angle
 +V3(cos(<-angle) <-RADIUS * <-cellX + <-Y_COORDINANT RandInt(-15 16) + sin(<-angle) <-RADIUS * <-cellZ +) ->position
 +</code>
 </hidden> </hidden>
  
Line 2761: Line 3544:
  endonce  endonce
 endif endif
 +</code>
 +
 +</hidden>
 +
 +----
 +
 +
 +===== Request Viable Build- or Move-Cell =====
 +
 +<hidden click here for source code>
 +
 +This script allows for other scripts to SEND A MESSAGE TO REQUEST A VIABLE BUILD- OR MOVE-CELL. 
 +
 +The script is a compromise between high performance and high fidelity. The reason that it's setup to run as a separate script and not as a function, is so that it can take move/build requests from multiple sources into account: by default it won't recheck cells that were recently rejected + new searches will continue where recent previous queries stopped after finding a solution. A very high performance version exists for 3x3 units in my SQUADS map.
 +
 +
 +How to use in a cpack:
 +Run the script as a global script in Pre. Do not run while paused.
 +
 +
 +Use example for building miners:
 +
 +
 +<code 4rpl example.4rpl>500 0 do
 + "requestWxWviableCell" 0 5 3 128 128 v2 256 0 0 0 0 1 0 1 1 0 14 listN sendmsg # Length > width = x direction.
 + <-*WxWviableCellFound if 
 + "miner" <-*WxWviableCell ev2 0 createunitonterrain dup 1 SetUnitOccupiesLand 999 constructunit 
 + # Unlike existing units that move, newly build units do not instantly occupy the cells that they were created on, so we force a refresh of that with SetUnitOccupiesLand.
 + endif
 +loop
 +
 +500 0 do
 + "requestWxWviableCell" 0 3 5 128 128 v2 256 0 0 0 0 1 0 1 1 0 14 listN sendmsg # Length > width = z direction. With Z-direction we also need to change the orientation below.
 + <-*WxWviableCellFound if 
 + "miner" <-*WxWviableCell ev2 0 createunitonterrain dup dup 1 setunitorientation 1 SetUnitOccupiesLand 999 constructunit
 + endif
 +loop</code>
 +
 +INPUT variables explained:
 +
 +<-moveUID: can be left 0, but it's needed if you want to allow a unit to check if it's current location is viable.
 +
 +<-unitLength: footprint length in X direction.
 +
 +<-unitWidth: footprint length in Z direction.
 +
 +<-cellV2: V2 vector. The cell where your requesting script wants to build/move the unit.
 +
 +<-searchRange: the cell distance around the requested cell location, where the function will look for a viable build/move cell.
 +
 +<-maxCreeperDepth: this will check the average creeper depth in a circle with range 4.
 +
 +<-noMesh: set to 1 to not allow solutions with ACTIVE mesh.
 +
 +<-likePlatform: the unit can be placed in the void.
 +
 +<-likeBeacon: the unit can be placed in the void and on uneven terrain.
 +
 +<-likeMiner: the unit can only be placed on resource special terrain that covers atleast 75% of the unit footprint.
 +
 +<-likeNullifier: the unit has to be placed within nullification range of an enemy creeper. The creeper enemey has to be known to a list inside the function script.
 +
 +<-ignoreCooldown: set to 1 to ignore time based computation optimizations. Best left at 0 when the function is called by an AI script (more optimized) and set to 1 when it's initiated by the player (feels more responsive).
 +
 +<-noSkip: set to 1 for max placement coverage, but slower computations. If left at 0, then the script will not check cells that are likely to not have a proper solution, but it will also mistakenly not check some cells that do have a proper solution. For spamming 3x3 units, "1" tends to be 40% slower, while only placing about 10% more units.
 +
 +<-resetSearch: if set to 1, then all previous search history will be forgotten. 
 +
 +
 +
 +<code 4rpl LxW-FindViableCell-NRP-Pre.4rpl>
 +# --LxW-FindViableCell-NRP-Pre--
 +
 +# INPUTS: "requestWxWviableCell" <-moveUID <-unitLength <-unitWidth <-cellV2 <-searchRange <-maxCreeperDepth <-noMesh <-likePlatform <-likeBeacon <-likeMiner <-likeNullifier <-ignoreCooldown <-noSkip <-resetSearch 14 listN 
 +# OUTPUT: global varialbles named <-*WxWviableCellFound <-*WxWviableCell
 +
 +# Contains snapping tool movecell requests, so that multiple units can request a viable cell from a single source, without double checking cells.
 +# BASED ON SNAPPING TOOL CODE, but without the Indicator unit or the Mouse commands, and only for SQUARE units.
 +
 +$radiusLandingSpot:4 # INTEGER. The creeper threshhold will be checked as an average over more than the unit landing location.
 +$cellsWithinLandingRange:69 # INTEGER, see previous radius. The creeper threshhold will be checked as an average over more than the unit landing location.
 +
 +$$defaultSearchRange:20 # INTEGER. The floodfill distance. If no searchRange is send in the message, this is the range that will be used.
 +
 +$groupDist:7 # Integer or float distance. Requests that are closer together than this distance, will be grouped together, so that later searches can continue on from previous searches.
 +$blockCellTimer:90 # INTEGER. Amount of frames under which queries will be grouped together & searches around cells that were found to be blocked, will not be done again.
 +$clearCellTimer:270 # INTEGER. Amount of frames after which the blocked cells will be forgotten. In between these 2 values, searches will only be done again if there are less units around than when the cell was last found to be blocked.
 +
 +$$contSlot:8 # VALUE EDITOR SETTING. Units cannot be placed on this terrain slot. 8 is the default contaminant slot.
 +$$resoSlot:1 # VALUE EDITOR SETTING. Miners can only be placed on resource terrain. 1 is the default resource slot.
 +$$meshSlot:6 # VALUE EDITOR SETTING. Units that can be placed in the void, cannot be placed on mesh inside the void. 6 is the default mesh slot.
 +
 +$$nullifyUnits:" emitter; airsaccauldron; blobnest; darktower; skimmerfactory; sporelauncher" # Enemy units that would be suppressed by building a nullifier near them.
 +
 +:requestWxWviableCellFunction
 + <-_DATA[0] 0 setunitoccupiesland # With this line, an existing unit's current cell will be a possible result from the search for a viable cell.
 + <-_DATA ListToStack @searchWxWviableCell ->*WxWviableCellFound ->*WxWviableCell
 + <-_DATA[0] 1 setunitoccupiesland
 +
 +:once
 +# listening channels:
 + "requestWxWviableCell" "requestWxWviableCellFunction" registerformsg
 + 1 ->resetSearch
 + 1 ->ignoreCooldown
 +
 + # When the loop was ran through fully without finding a resolution, remember that cell for 90 frames before trying the same cell again.
 + list ->blockedCellV2List 
 + list ->blockedCellTimeList
 + list ->nearbyUnitsCountList
 +
 + <-nullifyUnits removewhitespace ";" split ->suppressList
 +
 +:searchWxWviableCell # Output: viableCell as v2 + true/false
 +# See vanilla snapping tool cpack for full commments.
 +->resetSearch
 +->noSkip
 +->ignoreCooldown
 +->checkN
 +->checkR
 +->placeA
 +->placeV
 +->noMesh
 +->creDepth
 +->searchRange
 +->cell
 +->width
 +->length
 +pop # The UID has no further use.
 +
 + # The following switch checks if the search should start from scratch (=1), or if it should continue (=0) on from a previous search.
 + switch
 + <-resetSearch case 1 endcase
 + <-length <-prevLength neq case 1 endcase
 + <-width <-prevWidth neq case 1 endcase
 + <-checkN <-prevCheckN neq case 1 endcase
 + <-checkR <-prevCheckR neq case 1 endcase
 + <-placeA <-prevPlaceA neq case 1 endcase
 + <-placeV <-prevPlaceV neq case 1 endcase
 + <-cell ev2 <-prevSearchCell ev2 distancecell <-groupDist gt if 1 else 
 + <-prevSearchCell ->cell # Group the search query together with previous closeby queries.
 + <-ignoreCooldown if getgametickcount else getgameupdatecount endif dup <-prevSearchUpdateCount gt if
 + ->prevSearchUpdateCount
 + 1
 + else
 + pop 
 + 0
 + endif
 + endif
 + endswitch if # If the search starts from scratch, then the new settings have to be remembered for the next function call.
 + # Adjust the footprint array depending on the requested width in the function call.
 + <-width dup <-prevWidth neq if
 + dup ->prevWidth
 + 1 add 2.0 div asint dup ->rHz 1 sub ->rLz # For the footprint loop.
 + else pop endif
 + <-length dup <-prevLength neq if
 + dup ->prevLength
 + 1 add 2.0 div asint dup ->rHx 1 sub ->rLx # For the footprint loop.
 + else pop endif
 +
 + <-checkN ->prevCheckN
 + <-checkR ->prevCheckR
 + <-placeA ->prevPlaceA
 + <-placeV ->prevPlaceV
 + <-cell ->prevSearchCell
 +
 + # Recreate the area to search:
 + <-searchRange 1 sub dup dup mul swap add 2 mul 1 add ->floodRange
 + <-cell ev2 0 21 <-floodRange floodfillterrain dup getlistcount ->potentialCellCount ->potentialCells
 + 0 ->startLoop
 + 0 ->skip
 +
 + # If the search is being reset, then clear the blocked lists.
 + <-resetSearch If
 + 1 ->ignoreCooldown
 + <-blockedCellV2List clearlist
 + <-blockedCellTimeList clearlist
 + <-nearbyUnitsCountList clearlist
 + endif
 + else
 + # If the search continues on from before, but the search range was changed, then the potential cells will need to be adjusted, without adjusting the start of the loop.
 + <-searchRange dup <-prevSearchRange neq if 
 + dup ->prevSearchRange 1 sub dup dup mul swap add 2 mul 1 add ->floodRange
 + <-cell ev2 0 21 <-floodRange floodfillterrain dup getlistcount ->potentialCellCount ->potentialCells
 + else pop endif
 + endif
 +
 + # If the new searchRange is larger than the old one, then do not check if the cell was recently found to be blocked.
 + <-searchRange <-prevSearchRange gt <-ignoreCooldown OR not if
 + # Check if the cell is not on the list of blocked cells.
 + <-blockedCellV2List <-cell getlistindex dup -1 eq if pop else
 + ->index
 +
 + getgameupdatecount ->updateCount
 + # Try to purge a few old blocked cells from the lists, otherwise the list bloats.
 + -1 <-index 1 sub do
 + <-blockedCellTimeList[i] <-clearCellTimer add <-updateCount lt if
 + <-blockedCellV2List i removelistelement
 + <-blockedCellTimeList i removelistelement
 + <-nearbyUnitsCountList <-index removelistelement
 + <-index 1 sub ->index
 + endif
 + loop
 +
 + switch
 + # The last check on this spot was less than 3 seconds ago, so we're not checking it again now.
 + <-blockedCellTimeList[<-index] <-blockCellTimer add <-updateCount gt case
 + -1 -1 v2 false return
 + endcase
 + # The last check on this spot was longer than 9 seconds ago, so we can check it again now.
 + <-blockedCellTimeList[<-index] <-clearCellTimer add <-updateCount lt case
 + <-blockedCellV2List <-index removelistelement
 + <-blockedCellTimeList <-index removelistelement
 + <-nearbyUnitsCountList <-index removelistelement
 + endcase
 + # Check the cell again if there are now less units nearby than when it was blocked.
 + <-cell ev2 fromcell 13 0 0 0 0 0 0 getunitsinrange getlistcount <-nearbyUnitsCountList[<-index] lt case 
 + <-blockedCellV2List <-index removelistelement
 + <-blockedCellTimeList <-index removelistelement
 + <-nearbyUnitsCountList <-index removelistelement
 + endcase
 + # Too recent and still the same amount of units nearby, so don't check again:
 + -1 -1 v2 false return
 + endswitch
 + endif
 + endif
 +
 + <-potentialCellCount <-startLoop do
 + <-skip if
 + <-skip 1 sub ->skip
 + else
 + <-potentialCells[i] ev2 @checkCenterCell if
 + @checkFootPrint if
 + i <-noSkip if 1 else <-width endif add ->startLoop # Optimization for mass placement. When placing the next unit, skip the next few potential cells.
 + <-potentialCells[i] true return # LEFT ON STACK.
 + endif
 + else
 + <-noSkip if 0 else <-width 3 div asint 1 add endif ->skip # If the center cell was blocked, then also skip the next X potential cells.
 + endif
 + endif
 + loop
 + <-potentialCellCount ->startLoop
 + # If the loop was not interrupted by return, then all cells were blocked in some way.
 + <-blockedCellV2List <-cell appendtolist
 + <-blockedCellTimeList getgameupdatecount appendtolist
 + <-nearbyUnitsCountList <-cell ev2 fromcell 13 0 0 0 0 0 0 getunitsinrange getlistcount appendtolist
 + -1 -1 v2 false # LEFT ON STACK.
 +
 +:checkCenterCell # Inputs: x and z of cell.
 + dup2 <-radiusLandingSpot 0 1 0 getcreeperinrange <-cellsWithinLandingRange div <-creDepth gt if pop pop false return endif
 + dup2 getterrain dup ->height if
 + <-placeV if pop pop false return endif
 + 0 ->checkO
 + else 
 + <-placeV <-placeA OR if 0 else 1 endif ->checkO
 + endif
 + <-checkR if 0 ->minePot endif
 + # dup2 is still on the stack.
 + switch
 + dup2 getcelloccupiedcount <-checkO neq case pop pop false return endcase
 + # dup2 getterrain <-height neq case pop pop false return endcase # Useless on center cell.
 + # dup2 getcreeper <-creDepth gt case pop pop false return endcase
 + dup2 getmeshhealth gt0 <-noMesh AND case pop pop false return endcase
 + dup2 getterraformmarker gt0 case pop pop false return endcase
 + dup2 getterrainspecial dup ->special <-contSlot eq case pop pop false return endcase
 + <-special <-meshSlot eq <-height 0 eq AND case pop pop false return endcase # Units cannot be placed on mesh in the void.
 + ->cZ ->cX # Used in checkFootPrint.
 + <-checkR if <-special <-resoSlot eq if 
 + <-minePot 1 add ->minePot 
 + else
 + pop pop false return
 + endif endif
 + <-checkO if 
 + "platform" <-cX 0 <-cZ v3 2.9 0 0 0 2 1 0 getunits 0 getlistelement 
 + getunitcell <-cX <-cZ distancecell 2.828427 approximately not if pop pop false return endif
 + endif
 + <-checkN if 
 + <-suppressList <-cX 0 <-cZ v3 9 0 0 0 1 0 1 getunitsinrange 0 getlistelement getunittype listcontains not if pop pop false return endif 
 + endif
 + endswitch
 + true
 +
 +:checkFootPrint
 +# See vanilla snapping tool cpack for full commments.
 +
 + <-cZ <-rHz add <-cZ <-rLz sub do
 + <-cX <-rHx add <-cX <-rLx sub do
 + switch
 + i j
 + # dup2 <-cZ eq swap <-cX eq AND case pop pop endcase # The center cell was already checked and OK.
 + dup2 getcelloccupiedcount <-checkO neq case pop pop false return endcase
 + dup2 getterrain <-height neq case pop pop false return endcase
 + # dup2 getcreeper <-creDepth gt case pop pop false return endcase
 + dup2 getmeshhealth gt0 <-noMesh AND case pop pop false return endcase
 + dup2 getterraformmarker gt0 case pop pop false return endcase
 + dup2 getterrainspecial dup ->special <-contSlot eq case pop pop false return endcase
 + # dup2 getterrainspecial <-meshSlot eq <-height 0 eq AND case pop pop false return endcase # Prevents platforms from being placed on mesh in the void. Not relevant for this script.
 + <-special <-meshSlot eq <-height 0 eq AND case pop pop false return endcase # Units cannot be placed on mesh in the void.
 + <-placeA case pop pop endcase
 + pop pop
 + <-checkR if <-special <-resoSlot eq if <-minePot 1 add ->minePot endif endif
 + endswitch
 + loop
 + loop
 + <-checkR if <-minePot <-length <-width mul 0.75 mul lt if false return endif endif
 + true
 +
 +</code>
 +
 +</hidden>
 +
 +----
 +
 +
 +===== Pseudo Random Number Generator, based on sinus =====
 +
 +<hidden click here for source code>
 +
 +Several basic functions based on Grabz' rng lite function of "<-seed sinus 10000 mul dup floor sub".
 +
 +
 +Copy the functions directly into your script or run the below script in your cpack and have other scripts send messages to request a randfloat or randint.
 +
 +
 +Difference between the functions:
 +  * "seeded": provide a seed to the generator. The generated number will always be the same for the same seed.
 +  * "index": these seeds are sequenced. In the same map for the same index, the generated number 1,2,3,... will be the same after map restart.
 +  * "spiked": the generator is seeded with the elapsedtime of the gaming session, making the outcome uncontrollable.
 +  * No prefix: these are sequenced seeds and they use index 0. 
 +
 +Example code with messages:
 +<code example.4rpl>
 +12345 ->int
 +1 ->index
 +0 ->first
 +100 ->last # The last integer itself will be excluded.
 +"sinRandInt" <-first <-last 2 listN sendmsg
 +<-*sinRandInt trace
 +"sinRand01" 0 sendmsg
 +<-*sinRand01 trace
 +"indexSinRandInt" <-index <-first <-last 3 listN sendmsg
 +<-*sinRandInt trace
 +"indexSinRand01" <-index sendmsg
 +<-*sinRand01 trace
 +"spikedSinRandInt" <-first <-last 2 listN sendmsg
 +<-*sinRandInt trace
 +"spikedSinRand01" 0 sendmsg
 +<-*sinRand01 trace
 +"seededSinRandInt" <-seed <-first <-last 3 listN sendmsg
 +<-*sinRandInt trace
 +"seededSinRand01" <-seed sendmsg
 +<-*sinRand01 trace
 +</code>
 +
 +----
 +
 +<code SinusRNG.4rpl>
 +:once
 + # Creating lists and a starting mapconstant for the seed sequences.
 + createlist ->prevSeedList
 + createlist ->seedCountList
 + getmapsize 2 div swap 2 div swap dup2 getterrain 1 add dup 99999 floodfillterrain getlistcount ->mapConstant
 +
 + # Setting up the messages so that other scripts can request a random seed.
 + "sinRandInt" "sinRandInt_CALL" registerformsg
 + "sinRand01" "sinRand01_CALL" registerformsg
 + "indexSinRandInt" "indexSinRandInt_CALL" registerformsg
 + "indexSinRand01" "indexSinRand01_CALL" registerformsg
 + "spikedSinRandInt" "spikedSinRandInt_CALL" registerformsg
 + "spikedSinRand01" "spikedSinRand01_CALL" registerformsg
 + "seededSinRandInt" "seededSinRandInt_CALL" registerformsg
 + "seededSinRand01" "seededSinRand01_CALL" registerformsg
 +
 +:sinRandInt_CALL
 + <-_DATA listtostack @sinRandInt ->*sinRandInt
 +:sinRand01_CALL
 + @sinRand01 ->*sinRand01
 +:indexSinRandInt_CALL
 + <-_DATA listtostack @indexSinRandInt ->*sinRandInt
 +:indexSinRand01_CALL
 + <-_DATA @indexSinRand01 ->*sinRand01
 +:spikedSinRandInt_CALL
 + <-_DATA listtostack @spikedSinRandInt ->*sinRandInt
 +:spikedSinRand01_CALL
 + @spikedSinRand01 ->*sinRand01
 +:seededSinRandInt_CALL
 + <-_DATA listtostack @seededSinRandInt ->*sinRandInt
 +:seededSinRand01_CALL
 + <-_DATA @seededSinRand01 ->*sinRand01
 +
 +:sinRandInt # INPUT: integer first randInt + integer last randInt. OUTPUT: an integer in between the first and last randInt, excluding the last randInt.
 +->last
 +->first
 + 0 @indexSinRand01 <-last <-first sub mul <-first add asint
 +
 +:sinRand01 # INPUT: none.
 + 0 @indexSinRand01
 +
 +:indexSinRandInt # INPUT: the index of the seed sequence + integer first randInt + integer last randInt. OUTPUT: an integer in between the first and last randInt, excluding the last randInt.
 +->last
 +->first
 + @indexSinRand01 <-last <-first sub mul <-first add asint
 +
 +:indexSinRand01 # INPUT: the "index" of the seed sequence. OUTPUT: the next seed from that sequence.
 +# The random numbers will be generated with the previous seed that is stored under that index.
 +->i
 + <-prevSeedList[<-i] eq0 if
 + <-i <-mapConstant add 2357 mul @seededSinRand01 dup 10000000 mul 1 add asint ->prevSeedList[<-i]
 + 1 ->seedCountList[<-i]
 + else
 + <-prevSeedList[<-i] @seededSinRand01 dup 10000000 mul <-seedCountList[<-i] 1 add dup ->seedCountList[<-i] add asint ->prevSeedList[<-i]
 + endif
 +
 +:spikedSinRandInt # INPUT: integer first randInt + integer last randInt. OUTPUT: an integer in between the first and last randInt, excluding the last randInt.
 +->last
 +->first
 + elapsedtime asint @seededSinRand01 <-last <-first sub mul <-first add asint
 +
 +:spikedSinRand01
 + elapsedtime asint @seededSinRand01
 +
 +:seededSinRandInt # INPUT: integer seed + integer first randInt + integer last randInt. OUTPUT: an integer in between the first and last randInt, excluding the last randInt.
 +->last
 +->first
 + @seededSinRand01 <-last <-first sub mul <-first add asint
 +
 +:seededSinRand01
 + # abs <-power pow <-add add sin <-sinMul mul dup floor sub
 + 1.05 pow 99419 sub sin 619 mul dup floor sub
 </code> </code>
  
cw4/4rpl_tools.1697659027.txt.gz · Last modified: 2025/02/14 14:56 (external edit)