Contributors to this page is encouraged to attribute their contributions((Invitation issued on November 11th, 2022)).
{{page>cw4:header&nofooter}}
{{page>4rpl:header&nofooter}}
====== Editors ======
//Note: to get a default editor associated with the .4rpl filetype, please read about [[cw4:tutorials:file association]].//
===== Notepad++ =====
Make sure you download only from the official Notepad++ website to avoid malware. https://notepad-plus-plus.org/
==== Syntax Highlighting ====
Some community members created syntax highlighters for Notepad++. More detailed descriptions can be found on the respective pages for each highlighter.
Please check out the [[cw4:nppguide|Notepad++ Guide]] on how to install highlighters if you don't know how.
*
[[4rpl:syntax_highlighter|Highlighter by Sanian.]]
Comes with basic spellcheck by underlining offending text in red. A Standard and Dark version are available.
*
[[4rpl:syntax_highlighter2|Highlighter by Alophox.]]
Does not include spellcheck. This highlighter can be used with multiple themes.
===== VSCode =====
[[https://marketplace.visualstudio.com/items?itemName=Up-Level.4rpl-lang|Language Extension by Up-Level.]]
Has syntax highlighting, hover information from the wiki, autocomplete and basic error detection.
====== Console Scripts ======
These scripts can be run from the [[cw4:4rpl_console|Console]] in the [[Mods]] section of the [[cw4:custom_maps#Mission Editor]]
----
===== Copy/Paste Utility =====
This script can be used to copy and paste terrain in a map.
New: now can copy arbitrary rectangle, use terrain overlay to show marked and copied areas. Also copy special terrain (breeder, mesh, etc.)
# Copy/Paste 2021-02-20
# ---------------------
# With grateful thanks to knucracker for his patient assistance
# ---------------------
# Also copies special terrain
$lmb:0 # left click button
$rmb:1 # right click button
$themeSlot:1 # theme overlay slot to use
$copy:1 # true for copy
$markActive:0 # false until area selection start
GetPointerTerrainCoords ->mouseZ ->mouseX
@BlitImage
if (GetMouseButtonDown(<-lmb false)) # start copy area selection
true ->markActive
--area # delete copy buffer
if (@IsMouseValid)
<-mouseZ ->areaStartZ
<-mouseX ->areaStartX
endIf
endIf
if (NOT(<-markActive)
AND (NOT(-?area)))
Return
endIf # don't run rest if not selecting terrain
if (GetMouseButton(<-lmb false)) # track copy area size amd mark terrain
if (@IsMouseValid)
<-mouseZ ->areaEndZ
<-mouseX ->areaEndX
MIN(<-areaStartZ <-areaEndZ) ->minZ
MAX(<-areaStartZ <-areaEndZ) ->maxZ
MIN(<-areaStartX <-areaEndX) ->minX
MAX(<-areaStartX <-areaEndX) ->maxX
FromCell(<-minX <-minZ) ->minV
FroMCell(<-maxX <-MaxZ) ->maxV
@MarkArea (V4(0 0 0 0) <-minVOld <-maxVOld) # Clear marker before painting new marker
@MarkArea (V4(.4 .4 .4 .4) <-minV <-maxV) # mark terrain
<-minV ->minVOld
<-maxV ->maxVOld
endIf
endif
if (GetMouseButtonUp(<-lmb false)) # copy terrain, create blit image
@MakeCopy
endIf
if (GetMouseButtonDown(<-rmb true)) # let's paste
If (-?area)
@Paste
else
TraceAllSp ("Copy buffer is empty!")
endIf
endIf
## mainline end - functiosn/routines follow
:MakeCopy
CreateList ->area
CreateList ->pasteImage
toCell(<-maxV) ->_maxCellZ ->_maxCellX
toCell(<-minV) ->_minCellZ ->_minCellX
<-_maxCellX <-_minCellX - ->blitWidth
<-_maxCellZ <-_minCellZ - ->blitHeight
# Iterate over marked area, copying both cell height and setting texture overlay
Do (<-_maxCellZ <-_minCellZ)
Do (<-_maxCellX <-_minCellX)
GetTerrain (I J ) ->_ter
GetTerrainSpecial(I J) ->_terS
V2(<-_ter <-_terS) ->_terData
<-_ter 20.0 / ->c
v4(<-c <-c <-c .6) ->_color
AppendToList(<-area <-_terData)
AppendToList(<-pasteImage <-_color)
loop
Loop
TraceAllSp ("Copied " GetListCount(<-area) "cells. From min/max" <-minV <-maxV)
If (GetListCount(<-area) eq0) --area endif # can't paste 0 zells
:BlitImage
If(-?PasteImage)
ClearThemeOverlay(<-themeSlot Vector0) # remove old mark
SetThemeOverlayPixels(<-themeSlot, <-mouseX, <-mouseZ, # set paste overlay
<-blitWidth <-blitHeight <-pasteImage)
EndIf
:MarkArea
toCell ->_maxCellZ ->_maxCellX
toCell ->_minCellZ ->_minCellX
->_tex
<-_maxCellX <-_minCellX - ->_width
<-_maxCellZ <-_minCellZ - ->_height
SetThemeOverlayRectPixels(<-themeSlot <-_minCellX <-_minCellZ <-_width <-_height <-_tex)
:Paste
if (@isMouseValid)
<-mouseZ ->areaStartZ
<-mouseX ->areaStartX
else
PlaySoundAtPosition("ADAMessage" 8 FromCell(<-mouseX <-mouseZ))
Return
endIf
if (not(-?area))
Trace ("Copy buffer empty!")
PlaySoundAtPosition("ADAMessage" 8 FromCell(<-mouseX <-mouseZ))
Return
endif
EditAddUndo(0)
->listItem (0)
toCell(<-maxV) ->_maxCellZ ->_maxCellX
toCell(<-minV) ->_minCellZ ->_minCellX
<-_maxCellZ <-_minCellZ - ->sizeZ
<-_maxCellX <-_minCellX - ->sizeX
Do (<-mouseZ <-sizeZ +, <-mouseZ)
Do (<-mouseX <-sizeX +, <-mouseX)
<-area[<-listItem] ->_terData
EV2(<-_terdata) ->_terS ->_ter
SetTerrain(I J <-_ter)
SetTerrainSpecial(I J <-_terS)
<-listItem 1 add ->listItem
loop
loop
Trace3 ("pasted " <-listitem "cells.")
:IsMouseValid
if (<-mouseX gte0
and(<-mouseZ gte0)
and(<-mouseX lt(<-mapSizeX))
and(<-mouseZ lt(<-mapSizeZ)))
true
else
false
endIf
:once
@Instructions
# null mouse positions
-1 ->mouseOldZ
-1 ->mouseOldX
GetMapSize ->mapSizeZ ->mapSizeX
CreateThemeOverlay(<-themeSlot <-mapSizeX <-mapSizeZ Vector0)
SetThemeOverlayEnabled(<-themeSlot true)
# SetThemeOverlayPointFilter(<-themeSlot true)
GetCameraTopDown ->initCamera # remember camera position
SetCameraTopDown(true)
# end Once
:Instructions
TraceAllSp ("Left Mouse to mark terrain,")
TraceAllSp ("right mouse to paste")
TraceAllSp ("Drag mouse to select area")
:Destroyed
SetCameraTopDown(<-initCamera)
SetThemeOverlayEnabled(<-themeSlot false) # delete
DestroyThemeOverlay(<-themeSlot) # stomp on it
\\
----
===== Terrain Export/Map Copy =====
Sometimes one might want to copy a terrain feature from one map to another.
That's not simple. This code will endeavor to assist, but it is a multi-step process.
* Download the ''**Map Export.4RPL***'' file and run it in the Console of the map that has the desired terrain feature. Note that every time you click the right mouse button, an export operation will be performed.
* The Export operation creates code in the file
%HOMEPATH%\Documents\My Games\creeperworld4\rpl.txt
If you close and open a new map in the game, the file will be erased, so make sure to save the contents before opening the next map.
*
There will be one or more (depending on how many exports you created)
4RPL programs in the file. All lines in the file will start with ''CONSOLE: '' that you need to remove in a text editor. A global change should suffice.
* Import these scripts into the Console and run them in the map where you desire to create the exported terrain feature.
# BigExport 2121-05-17
# exports a set of 4RPL instructions that can be used to replicate
# part of a map in another map.
# Exports Terrain and special terrain settings.
#
# A 4RPL script will be created in the rpl.txt file in the Creeper World 4 folder
# every time you copy terrai, another script will be appended to the file (in the same map editing session).
# You should save these scripts because next mission you open will overwrite the file.
# Edit the output file to remove the "Console:" prefix to every line.
# run the clean 4RPL script in the map where you want the terrain to be imported.
#
# With grateful thanks to knucracker who helped resolve my dumb blunders
# and provided the necessary APIs
$exports:0 # increment counter every export operation.
$lmb:0 # left click button
$rmb:1 # right click button
$themeSlot:1 # theme overlay slot to use
$markActive:0 # false until area selection start
GetPointerTerrainCoords ->mouseZ ->mouseX
if (GetKeyDown("A" false))
Trace("A Pressed")
GetMapSize ->maxCellZ ->maxCellX
0 ->minCellX
0 ->minCellZ
@export
endif
if (GetMouseButtonDown(<-lmb false)) # start copy area selection
if (@IsMouseValid)
<-mouseZ ->areaStartZ
<-mouseX ->areaStartX
true ->markActive
--area # delete old copy buffer
endIf
endIf
if (NOT(<-markActive)) # don't run rest if not selecting terrain
Return
endIf
if (GetMouseButton(<-lmb false)) # track copy area size and mark terrain
if (@IsMouseValid)
<-mouseZ ->areaEndZ
<-mouseX ->areaEndX
MIN(<-areaStartZ <-areaEndZ) ->minZ
MAX(<-areaStartZ <-areaEndZ) ->maxZ
MIN(<-areaStartX <-areaEndX) ->minX
MAX(<-areaStartX <-areaEndX) ->maxX
FromCell(<-minX <-minZ) ->minV
FroMCell(<-maxX <-MaxZ) ->maxV
@MarkArea (V4(0 0 0 0) <-minVOld <-maxVOld) # Clear marker before painting new marker
@MarkArea (V4(.4 .4 .4 .4) <-minV <-maxV) # mark terrain
<-minV ->minVOld
<-maxV ->maxVOld
endIf
endif
if (GetMouseButtonUp(<-lmb false)) # copy terrain, create blit image
@MarkArea (V4(0 0 0 0) <-minVOld <-maxVOld) # Clear marker
toCell(<-maxV) ->maxCellZ ->maxCellX
toCell(<-minV) ->minCellZ ->minCellX
<-maxCellX <-minCellX - ->blitWidth
<-maxCellZ <-minCellZ - ->blitHeight
@Export
endIf
# --- end mainline ---
:Export
<-exports 1 + ->exports
@PrintPreamble
@CopyTerrain
@PrintPostamble
TraceAll ("Exported " <-cells " terrain cells
in " <-rows " rows and " <-columns " columns.")
:CopyTerrain
0 ->cells
0 ->rows
PrintAll (":BuildListTerrain ")
# Iterate over marked area, copying cell height
# Copy Terrain
"" ->terData
Do (<-maxCellZ <-minCellZ)
<-rows 1 + ->rows
0 ->columns
Do (<-maxCellX <-minCellX)
<-columns 1 + ->columns
<-cells 1 + ->cells
GetTerrain (I J ) ->ter
if (<-ter 10 <) <-terData " " concat ->tmp endif
<-terData <-ter concat ->terData
<-terData " " concat ->terData
loop
PrintAll (<-terData)
"" ->terData
loop
PrintAll (" List ->pasteTerrain ")
#copy special terrain
PrintAll (":BuildListSpecial ")
"" ->terSData
Do (<-maxCellZ <-minCellZ)
Do (<-maxCellX <-minCellX)
GetTerrainSpecial(I J) ->terS
if (<-terS 10 <) <-terSData " " concat ->tmp endif
<-terSData <-terS concat ->terSData
<-terSData " " concat ->terSData
loop
PrintAll (<-terSData)
"" ->terSData
loop
PrintAll (" List ->pasteSpecial ")
TraceAllSp ("Copied " <-cells "cells. From min/max" <-minV <-maxV)
If (GetListCount(<-area) eq0) --area endif # can't paste 0 zells
:MarkArea
toCell ->maxCellZ ->maxCellX
toCell ->minCellZ ->minCellX
->_tex
<-maxCellX <-minCellX - ->_width
<-maxCellZ <-minCellZ - ->_height
SetThemeOverlayRectPixels(<-themeSlot <-minCellX <-minCellZ
<-_width <-_height <-_tex)
:IsMouseValid
if (<-mouseX gte0
and(<-mouseZ gte0)
and(<-mouseX lt(<-mapSizeX))
and(<-mouseZ lt(<-mapSizeZ)))
true
else
TraceAllSp ("Mouse not over terrain.")
PlaySoundAtPosition("ADAMessage" 8 FromCell(<-mouseX <-mouseZ))
false
endIf
:once
@Instructions
# null mouse positions
-1 ->mouseOldZ
-1 ->mouseOldX
GetMapSize ->mapSizeZ ->mapSizeX
CreateThemeOverlay(<-themeSlot <-mapSizeX <-mapSizeZ Vector0)
SetThemeOverlayEnabled(<-themeSlot true)
# SetThemeOverlayPointFilter(<-themeSlot true)
GetCameraTopDown ->initCamera # remember camera position
SetCameraTopDown(true)
GetPrintPrefixEnabled ->enabled
SetPrintPrefixEnabled(false)
# end Once
:Instructions
TraceAll ("Press " DQ "A" DQ " to export the entire map.")
TraceAllSp (" OR ")
TraceAllSp ("Left Mouse to mark terrain,")
TraceAllSp ("Drag mouse to select area")
TraceAllSp ("Mouse up to export selected area")
TraceAllSp
TraceAllSP ("Beeping sound means mouse is not over valid area.")
:Destroyed
TraceAllSP ("")
If(<-exports eq0)
TraceAllSp ("No map copy operations were performed.")
else
TraceAllSP ("Remember to save rpl.txt now!")
If (<-exports eq (1))
TraceAllSp ("One map export operation performed.")
else
TraceAllSp (<-exports "Map copy operations performed.")
endIf
endIf
TraceAllSP ("")
SetCameraTopDown(<-initCamera)
SetPrintPrefixEnabled(<-enabled)
SetThemeOverlayEnabled(<-themeSlot false) # delete
DestroyThemeOverlay(<-themeSlot) # stomp on it
:PrintPreamble
PrintAll ("# Terrain Import V 2.0. Copy Tick Timer: " GetGameTickCount )
PrintAll (" ")
PrintAll ("$lmb:0 # left click button ")
PrintAll ("$rmb:1 # right click button ")
PrintAll ("$themeSlot:1 # theme overlay slot to use ")
Printall (" ")
PrintAll ("Once ")
PrintAll ("TraceAllSp ( " DQ "Use right mouse button to paste." DQ ")" )
PrintAll (<-blitWidth " ->blitWidth ")
PrintAll (<-blitHeight " ->blitHeight ")
PrintAll (" GetMapSize ->mapSizeZ ->mapSizeX ")
PrintAll (" GetCameraTopDown ->initCamera # remember camera position ")
PrintAll (" SetCameraTopDown(true) ")
PrintAll (" @BuildListTerrain ")
PrintAll (" @BuildListSpecial ")
PrintAll (" @MakeBlitImage ")
PrintAll ("endOnce ")
PrintAll (" ")
PrintAll ("GetPointerTerrainCoords ->mouseZ ->mouseX ")
PrintAll ("@BlitImage ")
PrintAll (" ")
PrintAll ("if (GetMouseButtonDown(<-rmb true )) ")
PrintAll (" if (@isMouseValid) ")
printall (" @Import ")
PrintAll (" endIf ")
printall ("endif ")
PrintAll (" ")
PrintAll (":Import ")
PrintAll (" EditAddUndo (0) ")
PrintAll (" 0 ->rows ")
Printall (" 0 ->cell ")
Printall (" Do (<-mouseZ <-blitHeight + , <-mouseZ ) ")
Printall (" <-rows 1 + ->rows ")
PrintAll (" 0 ->columns ")
Printall (" Do (<-mouseX <-blitWidth +, <-mouseX ) ")
Printall (" SetTerrain (I J <-pasteTerrain[<-cell]) ")
Printall (" SetTerrainSpecial (I J <-pasteSpecial[<-cell]) ")
Printall (" <-cell 1 + ->cell ")
Printall (" <-columns 1 + ->columns ")
Printall (" loop ")
Printall (" loop ")
PrintAll (" TraceAll (" DQ "Imported " DQ " <-cell " DQ " terrain cells in " DQ " <-rows " DQ " rows and " DQ " <-columns " DQ " columns." DQ ") ")
PrintAll (" ")
PrintAll (":BlitImage ")
PrintAll (" ClearThemeOverlay(<-themeSlot Vector0) ") # remove old mark ")
PrintAll (" SetThemeOverlayPixels(<-themeSlot, <-mouseX, <-mouseZ, # set paste overlay ")
PrintAll (" <-blitWidth <-blitHeight <-blitMap) ")
Printall (" ")
PrintAll (":MakeBlitImage ")
pRINTaLL (" CreateList ->blitMap ")
pRINTaLL (" 0 ->i ")
pRINTaLL (" Do (<-blitHeight 0) ")
pRINTaLL (" Do (<-blitWidth 0 ) ")
pRINTaLL (" <-pasteTerrain[<-i] ->c ")
pRINTaLL (" <-c 20.0 / ->c ")
pRINTaLL (" v4(<-c <-c <-c .6) ->color ")
pRINTaLL (" AppendToList(<-blitMap <-color) ")
pRINTaLL (" <-i 1 + ->i ")
pRINTaLL (" loop ")
pRINTaLL (" Loop ")
pRINTaLL (" GetMapSize ->mapSizeZ ->mapSizeX ")
pRINTaLL (" CreateThemeOverlay(<-themeSlot <-mapSizeX <-mapSizeZ Vector0) ")
pRINTaLL (" SetThemeOverlayEnabled(<-themeSlot true) ")
pRINTaLL (" TraceAllSp (<-i " DQ " cells in blit map " DQ " ) ")
Printall (" ")
PrintAll (" ")
PrintAll (":IsMouseValid ")
PrintAll (" if (<-mouseX gte0 ")
PrintAll (" and(<-mouseZ gte0) ")
PrintAll (" and(<-mouseX lt(<-mapSizeX)) ")
PrintAll (" and(<-mouseZ lt(<-mapSizeZ))) ")
PrintAll (" true ")
PrintAll (" else ")
PrintAll (" false ")
PrintAll (" endIf ")
PrintAll (" ")
PrintAll (":Destroyed")
PrintAll (" SetCameraTopDown(<-initCamera) ")
PrintAll (" SetThemeOverlayEnabled(<-themeSlot false) # delete ")
PrintAll (" DestroyThemeOverlay(<-themeSlot) # stomp on it ")
PrintAll (" ")
PrintAll (" ")
:PrintPostamble
PrintAll ("### End ### ")
\\ \\
\\ \\ Change notes:
*
V3
*
now can export whole map with one key. Press
"a"
* message indicating how many operations were performed. For each uperation, there will be one program in RPL.txt.
* V2 Change notes:
* New version. Uses Terrain texture to show image
* V2.1 Change notes
* The` CONSOLE:` prefix is now suppressed, making it a bit easier to run.
----===== Sound Player =====
Looking for just that right sound for your next unit?
Here is a player that lets you evaluate all the available in-game sounds. You know the drill. Download, and run from the Console.
# Sounds
$rmb:1 # right click button
if(<-inputDelay gt0) <-inputDelay 1 - ->inputDelay endif
if (GetMappedKey("Custom1" false) <-inputDelay eq0 &&)
2 ->inputDelay
Mod(<-index 1 +, <-soundCount) ->index
<-soundList[<-index] ->soundName
TraceAll ("This is sound #" <-index ", " DQ <-soundName DQ)
endIf
if(GetMappedKey("Custom2" false) <-inputDelay eq0 &&)
2 ->inputDelay
Mod2(<-index 1 -, <-soundCount) ->index
<-soundList[<-index] ->soundName
TraceAll ("This is sound #" <-index ", " DQ <-soundName DQ)
endIf
if (GetMouseButtonDown(<-rmb true ))
PlaySoundAtPosition(<-soundName 8 GetCameraPosition V3(0 0.1 0) +)
endif
:Once
-1 ->index #So we start at 0, not 1.
@MakeSoundList
:MakeSoundList
"ADAMessage"
"ADAMessagesClose"
"ADAMessagesOpen"
"Achievement"
"Aircraft"
"Aircraft2"
"BaseLand"
"BerthaExplosion"
"BerthaFire"
"BombDrop"
"BombDropSlide"
"BombExplosion"
"BraveWarning"
"ButtonClick"
"Click 1"
"Click"
"Click2"
"CloseClick"
"Damper"
"DamperDone"
"Dematerialize"
"EggBrave"
"EggBurst"
"EggReleased"
"ErrorBuild"
"Explosion"
"Explosion_1"
"Explosion_2"
"Explosion_3"
"Explosion_4"
"Explosion_5"
"Explosion_7"
"Explosion_8"
"Explosion_9"
"Explosion_10"
"Explosion_11"
"Explosion_12"
"HoverOpen"
"InfoCacheCollected"
"Materialize"
"MinimapAlert"
"MissileExplosion"
"MissileLaunch"
"MissionFail"
"MissionObjective"
"MissionObjectiveFail"
"MissionObjectiveRequiredComplete"
"MissionScan" "InhibitorFiring"
"MissionSpaceInitiateJump"
"MissionSpacePanelClose"
"MissionSpacePanelOpen"
"MissionSpaceRegionNavSelect"
"MissionWin"
"MortarFire"
"MortarShellExplosion"
"NeutronBurst"
"Notify"
"PlanetView"
"PlanetView2"
"RainDrop"
"Rematerialize"
"RiftLabDepart"
"Sci Fi Explosion 01"
"Sci Fi Explosion 02"
"Sci Fi Explosion 03"
"Sci Fi Explosion 04"
"Sci Fi Explosion 05"
"Sci Fi Explosion 06"
"Sci Fi Explosion 07"
"Sci Fi Explosion 08"
"Sci Fi Explosion 09"
"Sci Fi Explosion 10"
"Sci Fi Explosion 11"
"Sci Fi Explosion 12"
"Sci Fi Explosion 13"
"Sci Fi Explosion 14"
"Sci Fi Explosion 15"
"Sci Fi Explosion 16"
"Sci Fi Explosion 17"
"Sci Fi Explosion 18"
"Sci Fi Explosion 19"
"Sci Fi Explosion 20"
"Sci Fi Explosion 21"
"Sci Fi Explosion 22"
"Sci Fi Explosion 23"
"Sci Fi Explosion 24"
"Shutdown"
"Singularity"
"SingularityDone"
"SingularityLaunch"
"SniperFire"
"SporeHit"
"SporeLaunch"
"SporePrelaunch"
"SprayerFire"
"Stun" "CannonFire"
"SurviveBaseOffline"
"SurviveBaseWarn"
"ThorGun"
"TotemExplosion" "UnitHoverLand"
"UnitBuild"
"UnitExplosion"
"UnitHoverTakeoff"
"UnitLand"
"UnitPlace"
"UnitTakeoff"
"Warning"
"Warning2"
List ->soundList
"AlarmClock"
"GreenarRefinery"
"InhibitorArming"
"Overloading"
"Pulsing Ambience"
"Rocket"
"SoundLoop_alarm0"
"SoundLoop_alarm1"
"SoundLoop_alarm2"
"SoundLoop_alarm3"
"SoundLoop_alarm4"
"SoundLoop_alarm5"
"SoundLoop_alarm6"
"SoundLoop_alarm7"
"SoundLoop_alarm8"
"SoundLoop_alarm9"
"SoundLoop_alarm10"
"SoundLoop_alarm11"
"SoundLoop_alarm12"
"SoundLoop_alarm13"
"SoundLoop_alarm14"
"SoundLoop_alarm15"
"SoundLoop_ambience0"
"SoundLoop_ambience1"
"SoundLoop_ambience2"
"SoundLoop_ambience3"
"SoundLoop_ambience4"
"SoundLoop_ambience5"
"SoundLoop_ambience6"
"SoundLoop_ambience7"
"SoundLoop_ambience8"
"SoundLoop_ambience9"
"SoundLoop_ambience10"
"SoundLoop_gun0"
"SoundLoop_gun1"
"SoundLoop_gun2"
"SoundLoop_gun3"
"SoundLoop_hum0"
"SoundLoop_hum1"
"SoundLoop_hum2"
"SoundLoop_hum3"
"SoundLoop_hum4"
"SoundLoop_hum5"
"SoundLoop_hum6"
"SoundLoop_hum7"
"SoundLoop_hum8"
"SoundLoop_hum9"
"SoundLoop_hum10"
"SoundLoop_hum11"
"SoundLoop_hum12"
"SoundLoop_hum13"
"SoundLoop_hum14"
"SoundLoop_hum15"
"SoundLoop_hum16"
"SoundLoop_hum17"
"SoundLoop_hum18"
"SoundLoop_machine0"
"SoundLoop_machine1"
"SoundLoop_machine2"
"SoundLoop_machine3"
"SoundLoop_machine4"
"SoundLoop_machine5"
"SoundLoop_machine6"
"SoundLoop_machine7"
"SoundLoop_machine8"
"SoundLoop_machine9"
"SoundLoop_machine10"
"SoundLoop_machine11"
"SoundLoop_machine12"
"SoundLoop_misc0"
"SoundLoop_misc1"
"SoundLoop_misc2"
"SoundLoop_misc3"
"SoundLoop_misc4"
"SoundLoop_misc5"
"SoundLoop_misc6"
"SoundLoop_misc7"
"SoundLoop_misc8"
"TotemFire"
"ambient"
List ->loopSoundList
GetListCount(<-soundList) ->soundCount
TraceAllSp("List of " <-soundCount "sounds available")
TraceAllSp("There are also " GetListCOunt(<-loopSoundList) "looping sounds that cannot be played in this module")
TraceAllSP("Press and hold KeyPad 1 / 2 to scroll through the sound list.")
TraceAllSp("Left-click mouse to listen to it.")
===== Looping Sound Player =====
Looking for just that right **looping** sound for your next unit?
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.
* Remember to unmute game music.
# SoundLoops
$rmb:1 # right click button
if(<-inputDelay gt0) <-inputDelay 1 - ->inputDelay endif
SetUnitPosition(<-unitUID GetCameraPosition V3(0 1.5 0) +)
if(GetMappedKey("Custom1" false) <-inputDelay eq0 &&)
2 ->inputDelay
Mod(<-index 1 +, <-soundCount) ->index
<-loopSoundList[<-index] ->soundName
TraceAll("This is sound #" <-index ", " DQ <-soundName DQ)
endIf
if(GetMappedKey("Custom2" false) <-inputDelay eq0 &&)
2 ->inputDelay
Mod2(<-index 1 -, <-soundCount) ->index
<-loopSoundList[<-index] ->soundName
TraceAll("This is sound #" <-index ", " DQ <-soundName DQ)
endIf
if(GetMouseButtonDown(<-rmb true))
If(<-soundOn)
StopSoundLoop(<-unitUID)
Not(<-soundOn) ->soundOn
SetPause(<-pause)
TraceAllSp("Stopping " <-soundName " from playing")
else
TraceAllSp("Can you hear " <-soundName " playing?")
SetPause (false)
PlaySoundLoop(<-soundName 5 <-unitUID)
Not(<-soundOn) ->soundOn
endif
endif
:Destroyed
TraceAllSp("Goodbye!")
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
"ADAMessage"
"ADAMessagesClose"
"ADAMessagesOpen"
"Achievement"
"Aircraft"
"Aircraft2"
"BaseLand"
"BerthaExplosion"
"BerthaFire"
"BombDrop"
"BombDropSlide"
"BombExplosion"
"BraveWarning"
"ButtonClick"
"Click 1"
"Click"
"Click2"
"CloseClick"
"Damper"
"DamperDone"
"Dematerialize"
"EggBrave"
"EggBurst"
"EggReleased"
"ErrorBuild"
"Explosion"
"Explosion_1"
"Explosion_2"
"Explosion_3"
"Explosion_4"
"Explosion_5"
"Explosion_7"
"Explosion_8"
"Explosion_9"
"Explosion_10"
"Explosion_11"
"Explosion_12"
"HoverOpen"
"InfoCacheCollected"
"Materialize"
"MinimapAlert"
"MissileExplosion"
"MissileLaunch"
"MissionFail"
"MissionObjective"
"MissionObjectiveFail"
"MissionObjectiveRequiredComplete"
"MissionScan" "InhibitorFiring"
"MissionSpaceInitiateJump"
"MissionSpacePanelClose"
"MissionSpacePanelOpen"
"MissionSpaceRegionNavSelect"
"MissionWin"
"MortarFire"
"MortarShellExplosion"
"NeutronBurst"
"Notify"
"PlanetView"
"PlanetView2"
"RainDrop"
"Rematerialize"
"RiftLabDepart"
"Sci Fi Explosion 01"
"Sci Fi Explosion 02"
"Sci Fi Explosion 03"
"Sci Fi Explosion 04"
"Sci Fi Explosion 05"
"Sci Fi Explosion 06"
"Sci Fi Explosion 07"
"Sci Fi Explosion 08"
"Sci Fi Explosion 09"
"Sci Fi Explosion 10"
"Sci Fi Explosion 11"
"Sci Fi Explosion 12"
"Sci Fi Explosion 13"
"Sci Fi Explosion 14"
"Sci Fi Explosion 15"
"Sci Fi Explosion 16"
"Sci Fi Explosion 17"
"Sci Fi Explosion 18"
"Sci Fi Explosion 19"
"Sci Fi Explosion 20"
"Sci Fi Explosion 21"
"Sci Fi Explosion 22"
"Sci Fi Explosion 23"
"Sci Fi Explosion 24"
"Shutdown"
"Singularity"
"SingularityDone"
"SingularityLaunch"
"SniperFire"
"SporeHit"
"SporeLaunch"
"SporePrelaunch"
"SprayerFire"
"Stun" "CannonFire"
"SurviveBaseOffline"
"SurviveBaseWarn"
"ThorGun"
"TotemExplosion" "UnitHoverLand"
"UnitBuild"
"UnitExplosion"
"UnitHoverTakeoff"
"UnitLand"
"UnitPlace"
"UnitTakeoff"
"Warning"
"Warning2"
List ->soundList
"AlarmClock"
"GreenarRefinery"
"InhibitorArming"
"Overloading"
"Pulsing Ambience"
"Rocket"
"SoundLoop_alarm0"
"SoundLoop_alarm1"
"SoundLoop_alarm2"
"SoundLoop_alarm3"
"SoundLoop_alarm4"
"SoundLoop_alarm5"
"SoundLoop_alarm6"
"SoundLoop_alarm7"
"SoundLoop_alarm8"
"SoundLoop_alarm9"
"SoundLoop_alarm10"
"SoundLoop_alarm11"
"SoundLoop_alarm12"
"SoundLoop_alarm13"
"SoundLoop_alarm14"
"SoundLoop_alarm15"
"SoundLoop_ambience0"
"SoundLoop_ambience1"
"SoundLoop_ambience2"
"SoundLoop_ambience3"
"SoundLoop_ambience4"
"SoundLoop_ambience5"
"SoundLoop_ambience6"
"SoundLoop_ambience7"
"SoundLoop_ambience8"
"SoundLoop_ambience9"
"SoundLoop_ambience10"
"SoundLoop_gun0"
"SoundLoop_gun1"
"SoundLoop_gun2"
"SoundLoop_gun3"
"SoundLoop_hum0"
"SoundLoop_hum1"
"SoundLoop_hum2"
"SoundLoop_hum3"
"SoundLoop_hum4"
"SoundLoop_hum5"
"SoundLoop_hum6"
"SoundLoop_hum7"
"SoundLoop_hum8"
"SoundLoop_hum9"
"SoundLoop_hum10"
"SoundLoop_hum11"
"SoundLoop_hum12"
"SoundLoop_hum13"
"SoundLoop_hum14"
"SoundLoop_hum15"
"SoundLoop_hum16"
"SoundLoop_hum17"
"SoundLoop_hum18"
"SoundLoop_machine0"
"SoundLoop_machine1"
"SoundLoop_machine2"
"SoundLoop_machine3"
"SoundLoop_machine4"
"SoundLoop_machine5"
"SoundLoop_machine6"
"SoundLoop_machine7"
"SoundLoop_machine8"
"SoundLoop_machine9"
"SoundLoop_machine10"
"SoundLoop_machine11"
"SoundLoop_machine12"
"SoundLoop_misc0"
"SoundLoop_misc1"
"SoundLoop_misc2"
"SoundLoop_misc3"
"SoundLoop_misc4"
"SoundLoop_misc5"
"SoundLoop_misc6"
"SoundLoop_misc7"
"SoundLoop_misc8"
"TotemFire"
"ambient"
List ->loopSoundList
GetListCount(<-loopSoundList) ->soundCount
TraceAllSp("List of " <-soundCount "looping sounds available")
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("Left-click to turn off again.")
TraceAllSp("Remember to mute music!")
----
===== Map Information =====
Looking for some general map information on a specific map, including how much of various resources can be produced? This script is primarily income related information, but there's a little bit of general information in there, too.
All values should be correct, but errors are possible. Below is a sample output of a chronum map, and the source itself that you can put in as a console script. There's some notes about the output in the top of the source code.
Ground Tiles: 12340
Resource Tiles: 620
Void Tiles: 904
Eco: 347 / 347
Potential Soylent Energy: 34.06
Eco Energy Value: 3.83
Resource Terrain could be
Energy Gen: 41 or
Bluite: 516.67 /min
Map resources per minute
Bluite: 0
Redon: 90
Greenar Crystals: 9.68
Liftic Produced: 116.2
#"Notes: You have to advance a frame for eco counts to be updated."
#"Values assume no ERNs"
#"Soylent value assumes flat terrain, and 100% efficiency."
GetMapSize ->sizeZ ->sizeX
0 ->tiles
0 ->void
0 ->resource
0 ->crystalsMin
0 ->blueMin
0 ->redMin
Createtable ->table
0 ->table{1}
<-sizeZ 0 do
<-sizeX 0 do
GetTerrain(i j) ->terrainHeight
if (<-terrainHeight gt0)
<-tiles 1 add ->tiles
GetTerrainSpecial(i j) ->specialTerrainSlot
<-table{<-specialTerrainSlot} 1 add ->table{<-specialTerrainSlot}
else
<-void 1 add ->void
endif
loop
loop
GetUnitsByType("resourcered" 1) ->units
<-units GetListCount 0 do
GetUnitSettings(<-units[i]) ->unitSettings
<-redMin 1800 <-unitSettings{"Interval"} div add ->redMin
loop
GetUnitsByType("resourceblue" 1) ->units
<-units GetListCount 0 do
GetUnitSettings(<-units[i]) ->unitSettings
<-blueMin 1800 <-unitSettings{"Interval"} div add ->blueMin
loop
GetUnitsByType("greenarmother" 1) ->units
<-units GetListCount 0 do
0 ->validTiles
GetUnitCell(<-units[i]) ->posZ ->posX
<-posZ 11 add <-posZ 10 sub do
<-posX 11 add <-posX 10 sub do
DistanceCell((<-posX) (<-posZ) i j) ->dist
if (<-dist 10.5 lte)
i ->x
j ->z
@TileOkay
if (<-okay gt0)
<-validTiles 1 add ->validTiles
endif
endif
loop
loop
<-validTiles 339.0 div ->pct
GetUnitSettings(<-units[i]) ->unitSettings
<-crystalsMin 1800 <-unitSettings{"Interval"} div <-pct mul add ->crystalsMin
loop
GetEcoCounts ->liveCount ->stumpCount
<-stumpCount <-liveCount add ->totalEco
" Ground Tiles:" <-tiles LF
"Resource Tiles:" <-table{1} LF
"Void Tiles:" <-void LF
"Eco:" <-liveCount "/" <-totalEco LF
LF
"Potential Soylent Energy:" <-tiles 0.00276 mul 2 round lf
"Eco Energy Value:" <-totalEco 0.01104 mul 2 round lf
lf
"Resource Terrain could be" lf
"Energy Gen:" <-table{1} 15 div "or" lf
"Bluite:" <-table{1} 1.2 div 2 round "/min" lf
LF
"Map resources per minute" LF
"Bluite:" <-blueMin 2 round lf
"Redon:" <-redMin 2 round lf
"Greenar Crystals:" <-crystalsMin 2 round lf
"Liftic Produced: " <-crystalsMin 12 mul 1 round lf
traceallsp
:TileOkay
1 ->okay
GetTerrain(<-x <-z) ->terrainHeight
if (<-terrainHeight lte0 GetCellOccupiedCount(<-x <-z) gt0 or)
0 ->okay
return
endif
<-z 2 add <-z 1 sub do
<-x 2 add <-x 1 sub do
if (GetTerrain(i j) <-terrainHeight neq )
0 ->okay
break
endif
loop
loop
----
===== Measure Execution Time =====
Optimizing code is important. This helps you quickly test how well different options run and choose the best one. Also can check for loose items on the stack if you want.
$avgNum:10 #The average, min, and max will be over this many invocations.
$showMin:1
$showAvg:1
$showMax:1
$checkStack:1
$iterations:1000 #How many times to execute the code every invocation.
ElapsedTime ->start
<-iterations 0 do
#CODE TO MEASURE GOES HERE
loop
@EndMeasure
#===============
:Once
<-avgNum CreateListStartingSize ->timeList
:EndMeasure
ElapsedTime <-start sub ->timeTaken
if(StackSize <-checkStack &&)
Trace(StackSize <-iterations div " items left on the stack!" concat)
endif
ClearStack
<-timeTaken ->timeList[InvocationCount <-avgNum mod]
9999999 ->min
0 ->max
0
InvocationCount 1 add <-avgNum min 0 do #Find the average
<-timeList[i]
dup <-min min ->min
dup <-max max ->max
add
loop
<-avgNum InvocationCount min div ->avg
<-timeTaken @Pad
if(<-showMin)
"min: " <-min @Pad
endif
if(<-showAvg)
"avg: " <-avg 1 round @Pad
endif
if(<-showMax)
"max: " <-max @Pad
endif
TraceAll
#Gets close, but non-monospace fonts (like the tracelog) aren't exact.
#Doesn't use a tab because sometimes that makes the issue worse.
:Pad
dup StringLength 8 swap sub 0 do
" "
loop
----
===== Map Symmetry/Transform Tool =====
Run this script in console to provide several functions that can copy half/quarter of the map and reflect/rotate and paste it (special terrain types are also copied, but units and creeper are not). Any operation may be undone with Ctrl-Z or redone with Ctrl-Y. Key binds are shown at the top of the script, and are also shown/described here for ease of use.
Be aware that some functions will not execute on non-square maps since symmetry cannot be maintained.
# Symmetrifier
# How to use: Paste script into console script file, run (not just once), then press one of the keys shown in the list below to perform the the described symmetry/map transform operation
# NOTE: Units are not affected by any of the below functions
# NOTE: On maps with odd-numbered side lengths, a 1-wide strip of terrain in the middle is unaffected
# NOTE: Script uses above-keyboard number keys and left-hand modifier keys (left alt, left shift and left control)
# =================================
# |1| = Left to Right Reflectional
# |2| = Right to Left Reflectional
# |3| = Bottom to Top Reflectional
# |4| = Top to Bottom Reflectional
# |5| = Bottom Left to Top Right Diagonal Reflectional (Requires Square Map)
# |6| = Top Right to Bottom Left Diagonal Reflectional (Requires Square Map)
# |7| = Bottom Right to Top Left Diagonal Reflectional (Requires Square Map)
# |8| = Top left to Bottom Right Diagonal Reflectional (Requires Square Map)
# |9| = Bottom Left Quadrilateral Reflectional
# |0| = Bottom Right Quadrilateral Reflectional
# |Shift+1| = Top Left Quadrilateral Reflectional
# |Shift+2| = Top Right Quadrilateral Reflectional
# =============================================
# |Shift+3| = Left Half Rotational
# |Shift+4| = Right Half Rotational
# |Shift+5| = Top Half Rotational
# |Shift+6| = Bottom Half Rotational
# |Shift+7| = Bottom Left Half Diagonal Rotational
# |Shift+8| = Bottom Right Half Diagonal Rotational
# |Shift+9| = Top Left Half Diagonal Rotational
# |Shift+0| = Top Right Half Diagonal Rotational
# |Alt+1| = Bottom Left Quarter Rotational (Requires Square Map)
# |Alt+2| = Bottom Right Quarter Rotational (Requires Square Map)
# |Alt+3| = Top Left Quarter Rotational (Requires Square Map)
# |Alt+4| = Top Right Quarter Rotational (Requires Square Map)
# |Alt+5| = Left Quarter Rotational (Requires Square Map)
# |Alt+6| = Right Quarter Rotational (Requires Square Map)
# |Alt+7| = Top Quarter Rotational (Requires Square Map)
# |Alt+8| = Bottom Quarter Rotational (Requires Square Map)
# ====================================
# |Ctrl+1| = Rotate Map 90 Clockwise (Requires Square Map)
# |Ctrl+2| = Rotate Map 90 Counterclockwise (Requires Square Map)
# |Ctrl+3| = Rotate Map 180
# |Ctrl+4| = Flip Map Left to Right
# |Ctrl+5| = Flip Map Top to Bottom
# |Ctrl+6| = Flip Map Bottom Left to Top Right Diagonal (Requires Square Map)
# |Ctrl+7| = Flip Map Bottom Right to Top Left Diagonal (Requires Square Map)
# =============================================
# |Ctrl+Arrow Key| = Scroll Map 1 tile in direction
# |Shift+Arrow Key| = Scroll Map 10 tiles in direction
once
GetMapSize ->mapHeight ->mapWidth
if(<-mapWidth 2 mod neq0)
<-mapWidth 1 sub 2 div ->halfWidth
else
<-mapWidth 2 div ->halfWidth
endif
if(<-mapHeight 2 mod neq0)
<-mapHeight 1 sub 2 div ->halfHeight
else
<-mapHeight 2 div ->halfHeight
endif
if(<-mapHeight <-mapWidth eq)
true ->squareMap
else
false ->squareMap
endif
CreateListStartingSize(<-mapHeight <-mapWidth mul) ->terrainBuffer #Only used in flipping/rotating/scrolling map
CreateListStartingSize(<-mapHeight <-mapWidth mul) ->specialBuffer #Ditto
endonce
if(GetKey("LeftShift" true))
if(GetKeyDown("Alpha1" true)) EditAddUndo(0) @TopLeftQuadrilateral endif
if(GetKeyDown("Alpha2" true)) EditAddUndo(0) @TopRightQuadrilateral endif
if(GetKeyDown("Alpha3" true)) EditAddUndo(0) @LeftHalfRotational endif
if(GetKeyDown("Alpha4" true)) EditAddUndo(0) @RightHalfRotational endif
if(GetKeyDown("Alpha5" true)) EditAddUndo(0) @TopHalfRotational endif
if(GetKeyDown("Alpha6" true)) EditAddUndo(0) @BottomHalfRotational endif
if(GetKeyDown("Alpha7" true)) EditAddUndo(0) @BottomLeftHalfRotational endif
if(GetKeyDown("Alpha8" true)) EditAddUndo(0) @BottomRightHalfRotational endif
if(GetKeyDown("Alpha9" true)) EditAddUndo(0) @TopLeftHalfRotational endif
if(GetKeyDown("Alpha0" true)) EditAddUndo(0) @TopRightHalfRotational endif
if(GetKeyDown("LeftArrow" true)) EditAddUndo(0) 10 ->scr @ScrollMapLeft endif
if(GetKeyDown("RightArrow" true)) EditAddUndo(0) 10 ->scr @ScrollMapRight endif
if(GetKeyDown("UpArrow" true)) EditAddUndo(0) 10 ->scr @ScrollMapUp endif
if(GetKeyDown("DownArrow" true)) EditAddUndo(0) 10 ->scr @ScrollMapDown endif
else
if(GetKey("LeftAlt" true))
if(GetKeyDown("Alpha1" true) true eq <-squareMap true eq and) EditAddUndo(0) @BottomLeftQuarterRotational endif
if(GetKeyDown("Alpha2" true) true eq <-squareMap true eq and) EditAddUndo(0) @BottomRightQuarterRotational endif
if(GetKeyDown("Alpha3" true) true eq <-squareMap true eq and) EditAddUndo(0) @TopLeftQuarterRotational endif
if(GetKeyDown("Alpha4" true) true eq <-squareMap true eq and) EditAddUndo(0) @TopRightQuarterRotational endif
if(GetKeyDown("Alpha5" true) true eq <-squareMap true eq and) EditAddUndo(0) @LeftQuarterRotational endif
if(GetKeyDown("Alpha6" true) true eq <-squareMap true eq and) EditAddUndo(0) @RightQuarterRotational endif
if(GetKeyDown("Alpha7" true) true eq <-squareMap true eq and) EditAddUndo(0) @TopQuarterRotational endif
if(GetKeyDown("Alpha8" true) true eq <-squareMap true eq and) EditAddUndo(0) @BottomQuarterRotational endif
else
if(GetKey("LeftControl" true))
if(GetKeyDown("Alpha1" true) true eq <-squareMap true eq and) EditAddUndo(0) @Rotate90Clockwise endif
if(GetKeyDown("Alpha2" true) true eq <-squareMap true eq and) EditAddUndo(0) @Rotate90Counterclockwise endif
if(GetKeyDown("Alpha3" true)) EditAddUndo(0) @Rotate180 endif
if(GetKeyDown("Alpha4" true)) EditAddUndo(0) @FlipLeftToRight endif
if(GetKeyDown("Alpha5" true)) EditAddUndo(0) @FlipTopToBottom endif
if(GetKeyDown("Alpha6" true) true eq <-squareMap true eq and) EditAddUndo(0) @FlipBottomLeftToTopRight endif
if(GetKeyDown("Alpha7" true) true eq <-squareMap true eq and) EditAddUndo(0) @FlipBottomRightToTopLeft endif
if(GetKeyDown("LeftArrow" true)) EditAddUndo(0) 1 ->scr @ScrollMapLeft endif
if(GetKeyDown("RightArrow" true)) EditAddUndo(0) 1 ->scr @ScrollMapRight endif
if(GetKeyDown("UpArrow" true)) EditAddUndo(0) 1 ->scr @ScrollMapUp endif
if(GetKeyDown("DownArrow" true)) EditAddUndo(0) 1 ->scr @ScrollMapDown endif
else
if(GetKeyDown("Alpha1" true)) EditAddUndo(0) @LeftToRight endif
if(GetKeyDown("Alpha2" true)) EditAddUndo(0) @RightToLeft endif
if(GetKeyDown("Alpha3" true)) EditAddUndo(0) @BottomToTop endif
if(GetKeyDown("Alpha4" true)) EditAddUndo(0) @TopToBottom endif
if(GetKeyDown("Alpha5" true) true eq <-squareMap true eq and) EditAddUndo(0) @BottomLeftToTopRight endif
if(GetKeyDown("Alpha6" true) true eq <-squareMap true eq and) EditAddUndo(0) @TopRightToBottomLeft endif
if(GetKeyDown("Alpha7" true) true eq <-squareMap true eq and) EditAddUndo(0) @BottomRightToTopLeft endif
if(GetKeyDown("Alpha8" true) true eq <-squareMap true eq and) EditAddUndo(0) @TopLeftToBottomRight endif
if(GetKeyDown("Alpha9" true)) EditAddUndo(0) @BottomLeftQuadrilateral endif
if(GetKeyDown("Alpha0" true)) EditAddUndo(0) @BottomRightQuadrilateral endif
endif
endif
endif
:LeftToRight #########################################################################################
do(<-mapHeight 0)
do(<-halfWidth 0)
SetTerrain(<-mapWidth 1 sub I sub J GetTerrain(I J))
SetTerrainSpecial(<-mapWidth 1 sub I sub J GetTerrainSpecial(I J))
loop
loop
:RightToLeft #########################################################################################
do(<-mapHeight 0)
do(<-halfWidth 0)
SetTerrain(I J GetTerrain(<-mapWidth 1 sub I sub J))
SetTerrainSpecial(I J GetTerrainSpecial(<-mapWidth 1 sub I sub J))
loop
loop
:BottomToTop #########################################################################################
do(<-halfHeight 0)
do(<-mapWidth 0)
SetTerrain(I <-mapHeight 1 sub J sub GetTerrain(I J))
SetTerrainSpecial(I <-mapheight 1 sub J sub GetTerrainSpecial(I J))
loop
loop
:TopToBottom #########################################################################################
do(<-halfHeight 0)
do(<-mapWidth 0)
SetTerrain(I J GetTerrain(I <-mapHeight 1 sub J sub))
SetTerrainSpecial(I J GetTerrainSpecial(I <-mapheight 1 sub J sub))
loop
loop
:BottomLeftToTopRight ################################################################################
do(<-mapHeight 0)
do(<-mapWidth 0)
if(I J add <-mapWidth lte)
SetTerrain(<-mapWidth 1 sub J sub <-mapHeight 1 sub I sub GetTerrain(I J))
SetTerrainSpecial(<-mapWidth 1 sub J sub <-mapHeight 1 sub I sub GetTerrainSpecial(I J))
endif
loop
loop
:TopRightToBottomLeft ################################################################################
do(<-mapHeight 0)
do(<-mapWidth 0)
if(I J add <-mapWidth lte)
SetTerrain(I J GetTerrain(<-mapWidth 1 sub J sub <-mapHeight 1 sub I sub))
SetTerrainSpecial(I J GetTerrainSpecial(<-mapWidth 1 sub J sub <-mapHeight 1 sub I sub))
endif
loop
loop
:BottomRightToTopLeft ################################################################################
do(<-mapHeight 0)
do(<-mapWidth 0)
if(<-mapWidth 1 sub I sub J add <-mapWidth lte)
SetTerrain(J I GetTerrain(I J))
SetTerrainSpecial(J I GetTerrainSpecial(I J))
endif
loop
loop
:TopLeftToBottomRight ################################################################################
do(<-mapHeight 0)
do(<-mapWidth 0)
if(<-mapWidth 1 sub I sub J add <-mapWidth lte)
SetTerrain(I J GetTerrain(J I))
SetTerrainSpecial(I J GetTerrainSpecial(J I))
endif
loop
loop
:BottomLeftQuadrilateral #############################################################################
@LeftToRight
@BottomToTop
:BottomRightQuadrilateral ############################################################################
@RightToLeft
@BottomToTop
:TopLeftQuadrilateral ################################################################################
@LeftToRight
@TopToBottom
:TopRightQuadrilateral ###############################################################################
@RightToLeft
@TopToBottom
:LeftHalfRotational ##################################################################################
do(<-mapHeight 0)
do(<-halfWidth 0)
SetTerrain(<-mapWidth 1 sub I sub <-mapHeight 1 sub J sub GetTerrain(I J))
SetTerrainSpecial(<-mapWidth 1 sub I sub <-mapHeight 1 sub J sub GetTerrainSpecial(I J))
loop
loop
:RightHalfRotational #################################################################################
do(<-mapHeight 0)
do(<-halfWidth 0)
SetTerrain(I J GetTerrain(<-mapWidth 1 sub I sub <-mapHeight 1 sub J sub))
SetTerrainSpecial(I J GetTerrainSpecial(<-mapWidth 1 sub I sub <-mapHeight 1 sub J sub))
loop
loop
:TopHalfRotational ###################################################################################
do(<-halfHeight 0)
do(<-mapWidth 0)
SetTerrain(I J GetTerrain(<-mapWidth 1 sub I sub <-mapHeight 1 sub J sub))
SetTerrainSpecial(I J GetTerrainSpecial(<-mapWidth 1 sub I sub <-mapHeight 1 sub J sub))
loop
loop
:BottomHalfRotational ################################################################################
do(<-halfHeight 0)
do(<-mapWidth 0)
SetTerrain(<-mapWidth 1 sub I sub <-mapHeight 1 sub J sub GetTerrain(I J))
SetTerrainSpecial(<-mapWidth 1 sub I sub <-mapHeight 1 sub J sub GetTerrainSpecial(I J))
loop
loop
:BottomLeftHalfRotational ############################################################################
do(<-mapHeight 0)
do(<-mapWidth 0)
if(asfloat(I) asfloat(<-mapWidth) div asfloat(J) asfloat(<-mapHeight) div add 1 lt)
SetTerrain(<-mapWidth 1 sub I sub <-mapHeight 1 sub J sub GetTerrain(I J))
SetTerrainSpecial(<-mapWidth 1 sub I sub <-mapHeight 1 sub J sub GetTerrainSpecial(I J))
endif
loop
loop
:TopRightHalfRotational ##############################################################################
do(<-mapHeight 0)
do(<-mapWidth 0)
if(asfloat(I) asfloat(<-mapWidth) div asfloat(J) asfloat(<-mapHeight) div add 1 lt)
SetTerrain(I J GetTerrain(<-mapWidth 1 sub I sub <-mapHeight 1 sub J sub))
SetTerrainSpecial(I J GetTerrainSpecial(<-mapWidth 1 sub I sub <-mapHeight 1 sub J sub))
endif
loop
loop
:BottomRightHalfRotational ###########################################################################
do(<-mapHeight 0)
do(<-mapWidth 0)
if(asfloat(<-mapWidth 1 sub I sub) asfloat(<-mapWidth) div asfloat(J) asfloat(<-mapHeight) div add 1 lt)
SetTerrain(<-mapWidth 1 sub I sub <-mapHeight 1 sub J sub GetTerrain(I J))
SetTerrainSpecial(<-mapWidth 1 sub I sub <-mapHeight 1 sub J sub GetTerrainSpecial(I J))
endif
loop
loop
:TopLeftHalfRotational ###############################################################################
do(<-mapHeight 0)
do(<-mapWidth 0)
if(asfloat(<-mapWidth 1 sub I sub) asfloat(<-mapWidth) div asfloat(J) asfloat(<-mapHeight) div add 1 lt)
SetTerrain(I J GetTerrain(<-mapWidth 1 sub I sub <-mapHeight 1 sub J sub ))
SetTerrainSpecial(I J GetTerrainSpecial(<-mapWidth 1 sub I sub <-mapHeight 1 sub J sub ))
endif
loop
loop
:BottomLeftQuarterRotational #########################################################################
do(<-halfHeight 0)
do(<-halfWidth 0)
SetTerrain(J <-mapHeight 1 sub I sub GetTerrain(I J))
SetTerrainSpecial(J <-mapHeight 1 sub I sub GetTerrainSpecial(I J))
loop
loop
@LeftHalfRotational
:TopLeftQuarterRotational ############################################################################
do(<-halfHeight 0)
do(<-halfWidth 0)
SetTerrain(I J GetTerrain(J <-mapHeight 1 sub I sub))
SetTerrainSpecial(I J GetTerrainSpecial(J <-mapHeight 1 sub I sub))
loop
loop
@LeftHalfRotational
:BottomRightQuarterRotational ########################################################################
do(<-halfHeight 0)
do(<-halfWidth 0)
SetTerrain(I J GetTerrain(<-mapWidth 1 sub J sub I))
SetTerrainSpecial(I J GetTerrainSpecial(<-mapWidth 1 sub J sub I))
loop
loop
@BottomHalfRotational
:TopRightQuarterRotational ###########################################################################
do(<-halfHeight 0)
do(<-halfWidth 0)
SetTerrain(<-mapWidth 1 sub I sub <-mapHeight 1 sub J sub GetTerrain(<-mapWidth 1 sub J sub I))
SetTerrainSpecial(<-mapWidth 1 sub I sub <-mapHeight 1 sub J sub GetTerrainSpecial(<-mapWidth 1 sub J sub I))
loop
loop
@RightHalfRotational
:LeftQuarterRotational ###############################################################################
do(<-mapHeight 0)
do(<-mapWidth 0)
if(asfloat(I) asfloat(<-mapWidth) div asfloat(J) asfloat(<-mapHeight) div add 1 lt asfloat(I) asfloat(<-mapWidth) div asfloat(<-mapHeight 1 sub J sub) asfloat(<-mapHeight) div add 1 lt and)
SetTerrain(J <-mapHeight 1 sub I sub GetTerrain(I J))
SetTerrainSpecial(J <-mapHeight 1 sub I sub GetTerrainSpecial(I J))
endif
loop
loop
@TopLeftHalfRotational
:RightQuarterRotational ##############################################################################
do(<-mapHeight 0)
do(<-mapWidth 0)
if(asfloat(I) asfloat(<-mapWidth) div asfloat(J) asfloat(<-mapHeight) div add 1 lt asfloat(I) asfloat(<-mapWidth) div asfloat(<-mapHeight 1 sub J sub) asfloat(<-mapHeight) div add 1 lt and)
SetTerrain(J <-mapHeight 1 sub I sub GetTerrain(<-mapWidth 1 sub I sub <-mapHeight 1 sub J sub))
SetTerrainSpecial(J <-mapHeight 1 sub I sub GetTerrainSpecial(<-mapWidth 1 sub I sub <-mapHeight 1 sub J sub))
endif
loop
loop
@TopRightHalfRotational
:TopQuarterRotational ################################################################################
do(<-mapHeight 0)
do(<-mapWidth 0)
if(asfloat(I) asfloat(<-mapWidth) div asfloat(J) asfloat(<-mapHeight) div add 1 lt asfloat(I) asfloat(<-mapWidth) div asfloat(<-mapHeight 1 sub J sub) asfloat(<-mapHeight) div add 1 lt and)
SetTerrain(I J GetTerrain(J <-mapHeight 1 sub I sub))
SetTerrainSpecial(I J GetTerrainSpecial(J <-mapHeight 1 sub I sub))
endif
loop
loop
@TopLeftHalfRotational
:BottomQuarterRotational #############################################################################
do(<-mapHeight 0)
do(<-mapWidth 0)
if(asfloat(I) asfloat(<-mapWidth) div asfloat(J) asfloat(<-mapHeight) div add 1 lt asfloat(I) asfloat(<-mapWidth) div asfloat(<-mapHeight 1 sub J sub) asfloat(<-mapHeight) div add 1 lt and)
SetTerrain(I J GetTerrain(<-mapWidth 1 sub J sub I))
SetTerrainSpecial(I J GetTerrainSpecial(<-mapWidth 1 sub J sub I))
endif
loop
loop
@BottomLeftHalfRotational
:Rotate90Clockwise ###################################################################################
do(<-mapHeight 0)
do(<-mapWidth 0)
I ->x J ->z GetTerrain(<-mapWidth 1 sub J sub I) ->h GetTerrainSpecial(<-mapWidth 1 sub J sub I) ->sp
@SetBuffersAt
loop
loop
@PrintBuffers
:Rotate90Counterclockwise ############################################################################
do(<-mapHeight 0)
do(<-mapWidth 0)
I ->x J ->z GetTerrain(J <-mapHeight 1 sub I sub) ->h GetTerrainSpecial(J <-mapheight 1 sub I sub) ->sp
@SetBuffersAt
loop
loop
@PrintBuffers
:Rotate180 ###########################################################################################
do(<-mapHeight 0)
do(<-mapWidth 0)
I ->x J ->z GetTerrain(<-mapWidth 1 sub I sub <-mapHeight 1 sub J sub) ->h GetTerrainSpecial(<-mapWidth 1 sub I sub <-mapHeight 1 sub J sub) ->sp
@SetBuffersAt
loop
loop
@PrintBuffers
:FlipLeftToRight #####################################################################################
do(<-mapHeight 0)
do(<-mapWidth 0)
I ->x J ->z GetTerrain(<-mapWidth 1 sub I sub J) ->h GetTerrainSpecial(<-mapWidth 1 sub I sub J) ->sp
@SetBuffersAt
loop
loop
@PrintBuffers
:FlipTopToBottom #####################################################################################
do(<-mapHeight 0)
do(<-mapWidth 0)
I ->x J ->z GetTerrain(I <-mapHeight 1 sub J sub) ->h GetTerrainSpecial(I <-mapHeight 1 sub J sub) ->sp
@SetBuffersAt
loop
loop
@PrintBuffers
:FlipBottomLeftToTopRight ############################################################################
do(<-mapHeight 0)
do(<-mapWidth 0)
I ->x J ->z GetTerrain(<-mapHeight 1 sub J sub <-mapWidth 1 sub I sub) ->h GetTerrainSpecial(<-mapHeight 1 sub J sub <-mapWidth 1 sub I sub) ->sp
@SetBuffersAt
loop
loop
@PrintBuffers
:FlipBottomRightToTopLeft ############################################################################
do(<-mapHeight 0)
do(<-mapWidth 0)
I ->x J ->z GetTerrain(J I) ->h GetTerrainSpecial(J I) ->sp
@SetBuffersAt
loop
loop
@PrintBuffers
:ScrollMapLeft #######################################################################################
do(<-mapHeight 0)
do(<-mapWidth 0)
I <-scr add ->x2
if(<-x2 <-mapWidth gte) <-x2 <-mapWidth sub ->x2 endif
I ->x J ->z GetTerrain(<-x2 J) ->h GetTerrainSpecial(<-x2 J) ->sp
@SetBuffersAt
loop
loop
@PrintBuffers
:ScrollMapRight ######################################################################################
do(<-mapHeight 0)
do(<-mapWidth 0)
I <-scr sub ->x2
if(<-x2 lt0) <-x2 <-mapWidth add ->x2 endif
I ->x J ->z GetTerrain(<-x2 J) ->h GetTerrainSpecial(<-x2 J) ->sp
@SetBuffersAt
loop
loop
@PrintBuffers
:ScrollMapUp #########################################################################################
do(<-mapHeight 0)
do(<-mapWidth 0)
J <-scr sub ->z2
if(<-z2 lt0) <-z2 <-mapHeight add ->z2 endif
I ->x J ->z GetTerrain(I <-z2) ->h GetTerrainSpecial(I <-z2) ->sp
@SetBuffersAt
loop
loop
@PrintBuffers
:ScrollMapDown #######################################################################################
do(<-mapHeight 0)
do(<-mapWidth 0)
J <-scr add ->z2
if(<-z2 <-mapHeight gte) <-z2 <-mapHeight sub ->z2 endif
I ->x J ->z GetTerrain(I <-z2) ->h GetTerrainSpecial(I <-z2) ->sp
@SetBuffersAt
loop
loop
@PrintBuffers
:SetBuffersAt ########################################################################################
SetListElement(<-terrainBuffer <-z <-mapWidth mul <-x add <-h)
SetListElement(<-specialBuffer <-z <-mapWidth mul <-x add <-sp)
:PrintBuffers ########################################################################################
do(GetListCount(<-terrainBuffer) 0)
SetTerrain(I asint(<-mapWidth) mod I asint(<-mapWidth) div GetListElement(<-terrainBuffer I))
SetTerrainSpecial(I asint(<-mapWidth) mod I asint(<-mapWidth) div GetListElement(<-specialBuffer I))
loop
# Made with luv by Argonwolf
Note: All reflections/rotations leave the original half/quarter intact when pasting a copy
1 - Reflect left half of map to right
2 - Reflect right half of map to left
3 - Reflect bottom half of map to top
4 - Reflect top half of map to bottom
5 - Reflect bottom-left half of map to top-right diagonally (Requires square map)
6 - Reflect top-right half of map to bottom-left diagonally (Requires square map)
7 - Reflect bottom-right half of map to top-left diagonally (Requires square map)
8 - Reflect top-left half of map to bottom-right diagonally (Requires square map)
9 - Reflect bottom-left quarter of map to all four corners
0 - Reflect bottom-right quarter of map to all four corners
Shift+1 - Reflect top-left quarter of map to all four corners
Shift+2 - Reflect top-right quarter of map to all four corners
Shift+3 - Rotate left half of map to right
Shift+4 - Rotate right half of map to left
Shift+5 - Rotate top half of map to bottom
Shift+6 - Rotate bottom half of map to top
Shift+7 - Rotate bottom-left half of map to top-right diagonally
Shift+8 - Rotate bottom-right half of map to top-left diagonally
Shift+9 - Rotate top-left half of map to bottom-right diagonally
Shift+0 - Rotate top-right half of map to bottom-left diagonally
Alt+1 - Rotate bottom-left quarter of map to all four corners (Requires square map)
Alt+2 - Rotate bottom-right quarter of map to all four corners (Requires square map)
Alt+3 - Rotate top-left quarter of map to all four corners (Requires square map)
Alt+4 - Rotate top-right quarter of map to all four corners (Requires square map)
Alt+5 - Rotate left triangular quarter of map to all four sides (Requires square map)
Alt+6 - Rotate right triangular quarter of map to all four sides (Requires square map)
Alt+7 - Rotate top triangular quarter of map to all four sides (Requires square map)
Alt+8 - Rotate bottom triangular quarter of map to all four sides (Requires square map)
Ctrl+1 - Rotate map 90 degrees clockwise (Requires square map)
Ctrl+2 - Rotate map 90 degrees counterclockwise (Requires square map)
Ctrl+3 - Rotate map 180 degrees
Ctrl+4 - Flip map left to right
Ctrl+5 - Flip map top to bottom
Ctrl+6 - Flip map bottom-left to top-right (Requires square map)
Ctrl+7 - Flip map bottom-right to top-left (Requires square map)
Ctrl+Arrow - Scroll map 1 tile in direction of arrow (off-map tiles wrap to other side)
Shift+Arrow - Scroll map 10 tiles in direction of arrow (off-map tiles wrap to other side)
----
===== Reset Mesh =====
# ResetMesh
# For mesh values, see: https://knucklecracker.com/wiki/doku.php?id=4rpl:commands:getmeshhealth
GetMapSize ->sizeZ ->sizeX
do(<-sizeZ 0)
do(<-sizeX 0)
if(GetMeshHealth(I J) neq0)
SetMeshHealth(I J -1000000)
endif
loop
loop
----
===== Locate all cells in a range =====
4RPL code that will locate cells in range of a midpoint and within a certain radius.
# CellinRange 2021/06/26
# Return a list of cells given a midpoint and a radius.
115 ->x
54 ->z
20 ->radius
CreateList ->cells # will contain list of cells in range of call
Do (<-x <-radius 1 + +, <-x <-radius -) #x #J
Do (<-z <-radius 1 + +, <-z <-radius -) #z #I
If (DistanceCell(<-x <-z J I) LTE (<-radius 0.66 +))
If (IsV2InMap (V2(J I)))
FC(J I) #push to stack
EndIf
EndIf
Loop
Loop
AppendStackToList (<-cells) # populate list
@CellOut #replace this with a routine to do what you desire.
#---------
:CellOut
# set up theme overlay
V4(1 1 0 1) List ->yellow
V4(0 1 0 1) list ->green
CreateThemeOverlay(0 GetMapSize Vector0)
SetThemeOverlayEnabled(0 true)
SetThemeOverlayPointFilter(0 true)
# do the real work
Do (GetListCount(<-cells) 0)
SetThemeOverlayPixels(0, UFC(<-cells[i]),, 1, 1, <-green)
Loop
----
===== Reset Special Terrain (Decayable) =====
In all versions up to and including V2.1.4, there is a bug in the editor that affects the Undo function for special terrain of type decayable (Shattered Terrain). If you place shattered terrain and then undo, the bug leaves terrain in a state where it still thinks it has the shattered terrain special though it looks like normal terrain. It will drop 1 elevation level when it first makes contact with creeper, but then doesn't change again after that happens.
The easiest fix is to floodfill special terrain to clear the hidden shattered land property, but if some cases that may not be appropriate, eg. when there are many and varied types of special terrain on a map. In that case this utility, written by Discord User Auri (GitHub @Durikkan) can be used to reset the terrain left in the wrong state.
GetMapSize ->sizeZ ->sizeX
<-sizeZ 0 do
<-sizeX 0 do
if (GetTerrainSpecial(i j) eq0)
SetTerrainSpecial(i j 0)
endif
loop
loop
----
===== Fully Build Selected Units =====
A simple piece of code that will build all selected units and give them full ammo.\\ This is very useful when making PAC((
Play As Creeper
)) maps so you don't need to have the map run to let the units get their ammo if that is of any concern, even as little as for sake of story or emergence. However units like the Missile Launcher which have a visual change from having ammo will still need a single frame to complete before they can apply this visual change in reaction to their new ammo amount.\\ The code is designed to work both when running once and when running constantly. When running constantly it will Build and Fully Stock every unit you select.
# Build selected units
#By Vertu.
GetSelectedUnits ->units
if(GetListCount(<-units) gt0)
do(GetListCount(<-units) 0)
ConstructUnit(<-units[I] 10000)
SetUnitAmmo(<-units[I] GetUnitMaxAmmo(<-units[I]))
SetUnitHealth(<-units[I] GetUnitMaxHealth(<-units[I]))
loop
ClearTraceLog
Trace3("Fully built " GetListCount(<-units) " selected units.")
else
if(GetUnitUpdateCount lt(2))
Trace("No units where selected for building.")
else
if(GetUnitUpdateCount 10 % eq0)
ClearTraceLog
if(<-count eq0)
Trace("Operating. |")
1 ->count
else
if(<-count eq(1))
Trace("Operating. /")
2 ->count
else
if(<-count eq(2))
Trace("Operating. -")
3 ->count
else
Trace("Operating. \")
0 ->count
endif
endif
endif
endif
endif
endif
:Destroyed
if(GetUnitUpdateCount gt(5)) ClearTraceLog endif
----
===== Mission Control Stats (mcs.dat) manipulation =====
There are two 4RPL commands added in Beta 2.3.4 that allows inspecting and altering the mcs.dat file.
Primarily, this allows for deletion/reset of the status of maps in Colonies. It is possible to detect and delete either completed or incomplete missions. It is highly advised that a backup of mcs.dat is taken before this code is executed.
The new version of this code has a GUI based on CW4 4RPL buttons that allows for either selective deletion of entries or mass-deletion of entries.
# MCSButton 2022/08/31
If (<-deleteSet GT0)
TraceAllSp ("DeleteSet is active")
<-deleteSet 1 - ->deleteSet
If(<-deleteSet eq0)
@DeleteList
endIf
endIf
:Awake
RegisterForMSG("ClickExit" "ButtonExit")
RegisterForMSG("ClickBackup" "ButtonBackup")
RegisterForMSG("ClickContinue" "ButtonContinue")
RegisterForMSG("ClickList" "ButtonList")
RegisterForMSG("ClickNext" "ButtonNext")
RegisterForMSG("ClickPrev" "ButtonPrev")
RegisterForMSG("ClickDelete" "ButtonDelete")
RegisterForMSG("ClickMode" "ButtonMode")
V4(0 1 0 1) ->green
V4(1 0 0 1) ->red
V4(1 1 0 1) ->yellow
V4(0 1 1 1) ->blue
V4(0 0 0 1) ->black
V4(0 0 0 0) ->invisible
V4(1 1 1 1) ->white
SetMSGButton(0 true LF " Exit" LF " " concat concat concat <-red "ClickExit" "X")
SetMSGButton(1 true LF " mcs.dat is not backed up" LF " " concat concat concat <-yellow "ClickBackup" "N")
SetMSGButton(2 FALSE " " " " " " " ")
SetMSGButton(3 False " " " " " " " ")
SetMSGButton(4 true LF " mcs.dat is backed up" LF " " concat concat concat <-green "ClickContinue" "Y")
:ButtonBackup
SetMSGButton(0 true LF " Exit" LF " " concat concat concat <-red "ClickExit" "X")
SetMSGButton(1 TRUE "Only use when you have backed up mcs.dat " <-white " " " ")
SetMSGButton(2 FALSE " " " " " " " ")
SetMSGButton(3 FALSE " " " " " " " ")
SetMSGButton(4 FALSE " " " " " " " ")
:ButtonExit
SetMSGButton(0 FALSE " " " " " " " ")
SetMSGButton(1 FALSE " " " " " " " ")
SetMSGButton(2 FALSE " " " " " " " ")
SetMSGButton(3 FALSE " " " " " " " ")
SetMSGButton(4 FALSE " " " " " " " ")
StopConsole
:ButtonContinue
@ParseMCS
SetMSGButton(0 true LF " Exit" LF " " concat concat concat <-red "ClickExit" "X")
SetMSGButton(1 FALSE " " " " " " " ")
SetMSGButton(2 FALSE " " " " " " " ")
SetMSGButton(3 true LF " List " <-completedCount " Completed maps" LF " " concat concat concat concat concat <-green "ClickList" "C")
SetMSGButton(4 true LF " List " <-incompleteCount " Incomplete maps" LF " " concat concat concat concat concat <-yellow "ClickList" "I")
:ButtonList
# CreateList ->processList
If (<-_DATA "C" EQ)
# Do (3 0) testing code
# AppendToList ( <-processList <-coloniesComplete[I])
# Loop
<-coloniesComplete ->processList
<-completedCount ->listCount
else
# Do (3 0) testing code
# AppendToList ( <-processList <-coloniesIncomplete[I])
# Loop
<-ColoniesIncomplete ->processList
<-incompleteCount ->listCount
endIf
0 ->listPos
SetMSGButton(0 true LF " Exit" LF " " concat concat concat <-red "ClickExit" "X")
SetMSGButton(1 FALSE " " " " " " " ")
SetMSGButton(2 FALSE " " " " " " " ")
SetMSGButton(3 true LF " Select maps to remove " LF " " concat concat concat <-green "ClickMode" "S")
SetMSGButton(4 true LF " Remove all maps " LF " " concat concat concat <-yellow "ClickMode" "A")
:ButtonMode
If (<-_DATA "S" EQ)
@ProcessButtons
else
@ScheduleDelete
endIf
:ButtonNext
If (<-listPos GetListCount(<-processList) 1 - LT)
<-listPos 1 + ->listPos
endIf
@ProcessButtons
:ButtonPrev
If (<-listPos GTE0)
<-listPos 1 - ->listPos
endIf
@ProcessButtons
:ButtonDelete
DeleteMCSEntry(<-Processlist[<-listPos][0])
RemoveListElement(<-Processlist <-listPos )
if (<-listPos gt0, <-listPos GetListCount(<-processList) gte, &&)
<-listPos 1 - ->listPos
endif
@ProcessButtons
:ScheduleDelete
SetMSGButton(0 FALSE " " " " " " " ")
SetMSGButton(1 TRUE " This may take some time. Please wait." <-white " " " ")
SetMSGButton(2 FALSE " " " " " " " ")
SetMSGButton(3 FALSE " " " " " " " ")
SetMSGButton(4 FALSE " " " " " " " ")
2 ->deleteSet
:DeleteList
0 ->deleteCount
do( getListCount(<-ProcessList) 0)
DeleteMCSEntry(<-ProcessList[I][0])
<-deleteCount 1 + ->deleteCount
Loop
SetMSGButton(0 true LF " Exit" LF " " concat concat concat <-red "ClickExit" "X")
SetMSGButton(1 TRUE <-deleteCount " Map entries deleted." Concat <-white " " " ")
SetMSGButton(2 FALSE " " " " " " " ")
SetMSGButton(3 FALSE " " " " " " " ")
SetMSGButton(4 FALSE " " " " " " " ")
TraceAllSp(<-deleteCount "Map entries deleted.")
:ProcessButtons
TraceAllSp ("List position:" <-listPos)
SetMSGButton(0 true LF " Exit" LF " " Concat Concat Concat <-red "ClickExit" "X")
SetMSGButton(4 TRUE "#" <-Processlist[<-listPos][0] ": " <-Processlist[<-listPos][1] Concat Concat Concat " " " " " ")
If (<-listPos LTE0)
setMSGButton(1 TRUE LF "Prev map" LF " " Concat Concat Concat <-black "ClickPrev" -1)
else
SetMSGButton(1 TRUE LF "Prev map" LF " " Concat Concat Concat <-blue "ClickPrev" -1)
endIf
If (<-listPos GetListCount(<-processList) 1 - GTE)
setMSGButton(2 TRUE LF "Next map" LF " " Concat Concat Concat <-black "ClickNext" -1)
else
SetMSGButton(2 TRUE LF "Next map" LF " " Concat Concat Concat <-blue "ClickNext" 1)
endIf
SetMSGButton(3 TRUE LF "Delete Map #" <-Processlist[<-listPos][0] LF " " Concat Concat Concat Concat <-red "ClickDelete" <-listPos)
TraceAllSP ("I have " GetListCount (<-ProcessList) "entries to process")
:ParseMCS
GetPrintPrefixEnabled ->enabled
SetPrintPrefixEnabled(FALSE)
ClearPrintLog
GetMCSEntries ->MCSList
TraceAllSp ("MCS contains:" GetListCount(<-MCSList) "entries")
CreateList ->coloniesComplete
CreateList ->ColoniesIncomplete
0 ->completedCount
0 ->incompleteCount
# Split completed Colonies maps from incomplete
do (GetListCount(<-MCSList) 0)
If (<-MCSList[I][0] NEQ (-1))
If (<-MCSList[I][2])
AppendToList( <-coloniesComplete <-MCSList[I])
<-completedCount 1 + ->completedCount
Else
AppendToList( <-coloniesIncomplete <-MCSList[I])
<-incompleteCount 1 + ->incompleteCount
endIf
endIf
Loop
TraceAllSp ("Colonies completed: " GetListCount(<-coloniesComplete) )
TraceAllSp ("Colonies incompleted:" GetListCount(<-ColoniesIncomplete) )
# Output all lists:
PrintAllSp ("Colonies Incomplete")
do( getListCount(<-coloniesIncomplete) 0)
PrintAllSP(<-coloniesIncomplete[I])
Loop
PrintAllSP(" ")
PrintAllSp ("Colonies Completed")
do( getListCount(<-coloniesComplete) 0)
PrintAllSP(<-coloniesComplete[I])
Loop
# @DeleteIncomplete ### USE AT OWN RISK! Make a Copy of MCS.Dat First! ###
SetPrintPrefixEnabled(<-enabled)
----
===== Manipulate Selected Unit Positions =====
This console script is designed to run constantly and manipulate the position of the selected units in 3 dimensions. Also it operates for as long as a key is pressed down. This can make precision somewhat annoying but far from impossible.\\
Key bindings:
* All arrow keys. (X and Z axis)
* Page Up & Page down (Y axis)
* Backspace (set Y axis position defined within the script, must edit the script to change this).
* / disperse the Y axis position of all selected units between two integers which include floats. The lower and upper bounds must be edited within the script.
#Maniuplate Selected Unit Positions
#By Vertu
$HEIGHT:3000.0 #Float or Integer
$RAND_MAX:15 #Integer
$RAND_MIN:25 #Integer
$STEP:1.0 #--How far something moves per incrament when using the script.
$$BUFFER:2
if(<-inputDelay gt0) <-inputDelay 1 - ->inputDelay endif
if(GetKey("RightArrow" false) <-inputDelay eq0 &&)
true ->doesBuffer
GetSelectedUnits ->units
do(GetListCount(<-units) 0)
GetUnitPosition(<-units[I]) ->unitPosPre
SetUnitPosition(<-units[I] V3(<-unitPosPre.x <-STEP + <-unitPosPre.y <-unitPosPre.z))
GetTerrain(GetUnitCell(<-units[I])) ->terrainH
GetUnitPosition(<-units[I]) ->unitPos
if(approximately(<-unitPos.y <-terrainH))
SetUnitOccupiesLand(<-units[I] true)
else
if(<-unitPos.y lt(<-terrainH)) endif #SetUnitOccupiesLand(<-units[I] false) endif
endif
loop
endif
if(GetKey("LeftArrow" false) <-inputDelay eq0 &&)
true ->doesBuffer
GetSelectedUnits ->units
do(GetListCount(<-units) 0)
GetUnitPosition(<-units[I]) ->unitPosPre
SetUnitPosition(<-units[I] V3(<-unitPosPre.x <-STEP - <-unitPosPre.y <-unitPosPre.z))
GetTerrain(GetUnitCell(<-units[I])) ->terrainH
GetUnitPosition(<-units[I]) ->unitPos
if(approximately(<-unitPos.y <-terrainH))
SetUnitOccupiesLand(<-units[I] true)
else
if(<-unitPos.y lt(<-terrainH)) endif #SetUnitOccupiesLand(<-units[I] false) endif
endif
loop
endif
if(GetKey("UpArrow" false) <-inputDelay eq0 &&)
true ->doesBuffer
GetSelectedUnits ->units
do(GetListCount(<-units) 0)
GetUnitPosition(<-units[I]) ->unitPosPre
SetUnitPosition(<-units[I] V3(<-unitPosPre.x <-unitPosPre.y <-unitPosPre.z <-STEP +))
GetTerrain(GetUnitCell(<-units[I])) ->terrainH
GetUnitPosition(<-units[I]) ->unitPos
if(approximately(<-unitPos.y <-terrainH))
SetUnitOccupiesLand(<-units[I] true)
else
if(<-unitPos.y lt(<-terrainH)) endif #SetUnitOccupiesLand(<-units[I] false) endif
endif
loop
endif
if(GetKey("DownArrow" false) <-inputDelay eq0 &&)
true ->doesBuffer
GetSelectedUnits ->units
do(GetListCount(<-units) 0)
GetUnitPosition(<-units[I]) ->unitPosPre
SetUnitPosition(<-units[I] V3(<-unitPosPre.x <-unitPosPre.y <-unitPosPre.z <-STEP -))
GetTerrain(GetUnitCell(<-units[I])) ->terrainH
GetUnitPosition(<-units[I]) ->unitPos
if(approximately(<-unitPos.y <-terrainH))
SetUnitOccupiesLand(<-units[I] true)
else
if(<-unitPos.y lt(<-terrainH)) endif #SetUnitOccupiesLand(<-units[I] false) endif
endif
loop
endif
if(GetKey("PageUp" false) <-inputDelay eq0 &&)
true ->doesBuffer
GetSelectedUnits ->units
do(GetListCount(<-units) 0)
GetUnitPosition(<-units[I]) ->unitPosPre
SetUnitPosition(<-units[I] V3(<-unitPosPre.x <-unitPosPre.y <-STEP + <-unitPosPre.z))
GetTerrain(GetUnitCell(<-units[I])) ->terrainH
GetUnitPosition(<-units[I]) ->unitPos
if(approximately(<-unitPos.y <-terrainH))
SetUnitOccupiesLand(<-units[I] true)
else
if(<-unitPos.y lt(<-terrainH)) endif #SetUnitOccupiesLand(<-units[I] false) endif
endif
loop
endif
if(GetKey("PageDown" false) <-inputDelay eq0 &&)
true ->doesBuffer
GetSelectedUnits ->units
do(GetListCount(<-units) 0)
GetUnitPosition(<-units[I]) ->unitPosPre
SetUnitPosition(<-units[I] V3(<-unitPosPre.x <-unitPosPre.y <-STEP - <-unitPosPre.z))
GetTerrain(GetUnitCell(<-units[I])) ->terrainH
GetUnitPosition(<-units[I]) ->unitPos
if(approximately(<-unitPos.y <-terrainH))
SetUnitOccupiesLand(<-units[I] true)
else
if(<-unitPos.y lt(<-terrainH)) endif #SetUnitOccupiesLand(<-units[I] false) endif
endif
loop
endif
if(GetKey("Backspace" false))
true ->doesBuffer
GetSelectedUnits ->units
do(GetListCount(<-units) 0)
GetUnitPosition(<-units[I]) ->unitPosPre
SetUnitPosition(<-units[I] V3(<-unitPosPre.x <-HEIGHT <-unitPosPre.z))
GetTerrain(GetUnitCell(<-units[I])) ->terrainH
GetUnitPosition(<-units[I]) ->unitPos
if(approximately(<-unitPos.y <-terrainH))
SetUnitOccupiesLand(<-units[I] true)
else
if(<-unitPos.y lt(<-terrainH)) endif #SetUnitOccupiesLand(<-units[I] false) endif
endif
loop
endif
if(GetKey("Slash" false) <-inputDelay eq0 &&)
true ->doesBuffer
GetSelectedUnits ->units
do(GetListCount(<-units) 0)
RandInt(<-RAND_MIN <-RAND_MAX 1 +) RandFloat + ->randHeight
GetUnitPosition(<-units[I]) ->unitPosPre
SetUnitPosition(<-units[I] V3(<-unitPosPre.x <-randHeight <-unitPosPre.z))
loop
endif
if(<-doesBuffer)
false ->doesBuffer
<-BUFFER ->inputDelay
endif
:Once
Trace("Left/Right Arrow = Left/Right by 1 cell.")
Trace("Up/Down Arrow = North/South by 1 cell.")
Trace("Page Up = Up by 1 terrain unit.")
Trace("Page Down = Down by 1 terrain unit.")
Trace2("Backspace = Set to height defined within this console script which is" <-HEIGHT)
Trace5("'/' = disperse selected with random height from " <-RAND_MIN ":" <-RAND_MAX " with RandFloat too.")
----
===== 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.
# 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
----
===== 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.
# 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
----
===== Set terrain higher than 20 =====
A console script that lets you place terrain higher than 20. Details below.
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 **BLINDING** 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.
# 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
----
===== Next Utility goes here =====
----
====== Unit Scripts ======
===== Custom CellOccupiedCount shape =====
Here's a copy-paste ready script for setting unit occupied cells in shapes other than the default box. This will allow you to create a unit that occupies a non-standard amount of cells, letting you have both filled areas and hollow areas that other units could occupy.
It should cover all of the obvious cases such as creating the unit, destroying it, moving it in editor, lifting off and landing (for a movable unit). Make sure the unit is set to not occupy land in unit settings. Recompiling scripts will cause leftovers but they are eliminated when the map is loaded, so fixing that is not worth the additional complexity.
To set up a custom shape, you only have to alter the SetCustomOccupiedLand function. The default example is set up for a 9x9 unit that only wants the outer edge of the unit to occupy space, with a hollow middle. You can change the algorithm to set/unset whichever cells you want around the unit's center cell, as long as you make sure to add 1 to each when SOL_bSet is TRUE, and subtract 1 from each when SOL_bSet is FALSE.
If you need help with this script or you want to report a bug please message me at Grabz#4707 on Discord, you can find me through the Knuckle Cracker discord server.
:awake
once
RegisterForMSG("MSG_PostUpdate" "PostUpdate")
endonce
:once
@awake
:destroyed
<-G_iCurrentlyOccupiedX <-G_iCurrentlyOccupiedZ FALSE @SetCustomOccupiedLand
:gameloaded
-?G_iCurrentlyOccupiedX if
<-G_iCurrentlyOccupiedX <-G_iCurrentlyOccupiedZ TRUE @SetCustomOccupiedLand
endif
:PostUpdate
Self GetUnitMoveCell ->POU_iMoveZ ->POU_iMoveX
Self GetUnitCell ->POU_iZ ->POU_iX
#Set the initial configuration of our unit
once
<-POU_iX <-POU_iZ TRUE @SetCustomOccupiedLand
<-POU_iX <-POU_iZ ->G_iCurrentlyOccupiedZ ->G_iCurrentlyOccupiedX
endonce
#If the unit has been ordered to move
<-POU_iMoveX <-POU_iLastMoveX neq <-POU_iMoveZ <-POU_iLastMoveZ neq or <-POU_iMoveX -1 neq and if
#We clear the last area the unit was set to occupy, and
#we set the occupied land at the new target location our unit is meant to land in
<-G_iCurrentlyOccupiedX <-G_iCurrentlyOccupiedZ FALSE @SetCustomOccupiedLand
<-POU_iMoveX <-POU_iMoveZ TRUE @SetCustomOccupiedLand
<-POU_iMoveX <-POU_iMoveZ ->G_iCurrentlyOccupiedZ ->G_iCurrentlyOccupiedX
#If the unit is stationary
else <-POU_iMoveX <-POU_iLastMoveX eq <-POU_iMoveZ <-POU_iLastMoveZ eq and <-POU_iMoveX -1 eq and if
#If the unit was moved in the editor, clear last position and set new position
<-POU_iLastX <-POU_iX neq <-POU_iLastZ <-POU_iZ neq or if
<-G_iCurrentlyOccupiedX <-G_iCurrentlyOccupiedZ FALSE @SetCustomOccupiedLand
<-POU_iX <-POU_iZ TRUE @SetCustomOccupiedLand
<-POU_iX <-POU_iZ ->G_iCurrentlyOccupiedZ ->G_iCurrentlyOccupiedX
endif
endif endif
<-POU_iMoveZ <-POU_iMoveX ->POU_iLastMoveX ->POU_iLastMoveZ
<-POU_iZ <-POU_iX ->POU_iLastX ->POU_iLastZ
#i1 i2 b3 -
#Uniformly set or unset a chunk of occupied land
#Arguments: CellX and CellY of the last position (if unsetting) or current position (if setting),
# boolean that determines if to set or unset
#Result: None
:SetCustomOccupiedLand
->SOL_bSet ->SOL_iCellZ ->SOL_iCellX
4 ->SOL_iRadius
<-SOL_iCellZ <-SOL_iRadius add 1 add <-SOL_iCellZ <-SOL_iRadius sub do
<-SOL_iCellX <-SOL_iRadius add 1 add <-SOL_iCellX <-SOL_iRadius sub do
<-SOL_iCellX <-SOL_iRadius sub I eq
<-SOL_iCellX <-SOL_iRadius add I eq or
<-SOL_iCellZ <-SOL_iRadius sub J eq or
<-SOL_iCellZ <-SOL_iRadius add J eq or if
I J GetCellOccupiedCount ->SOL_iCount
<-SOL_bSet if
I J <-SOL_iCount 1 add SetCellOccupiedCount
else
I J <-SOL_iCount 1 sub SetCellOccupiedCount
endif
endif
loop
loop
===== Make a non-moving unit only buildable in void =====
This code will make it so a unit can be only built in void and not on terrain. It only works properly for stationary units.
*
This is a global script, so you have to add it in Package Manager
-> Global Control -> Pre Update.
* **Tick the Run when paused box**, then in one of the UNITGUID fields paste the GUID of one or more void only units.
* The script does not work in editor, you can finalize the map, back out then open the finalized map and test there if it works.
* The unit must have the "Can build/land anywhere" toggle enabled.
$UnitGUID0:""
$UnitGUID1:""
$UnitGUID2:""
$UnitGUID3:""
$UnitGUID4:""
GetBuildUnit ->curBuildUnit
#Only do something if state has changed
<-curBuildUnit <-lastBuildUnit neq if
FALSE ->buildUnitSelected
FALSE ->buildUnitDeselected
5 0 do
"UnitGUID" I concat <-! ->val
<-val StringLength gt0 <-val <-curBuildUnit eq and if
TRUE ->buildUnitSelected
endif
<-val StringLength gt0 <-val <-lastBuildUnit eq and if
TRUE ->buildUnitDeselected
endif
loop
#If a void only unit was selected, and the last selection was not a void only unit
<-buildUnitSelected <-buildUnitDeselected not and if
FALSE SetAllLegalUnitCells
<-LEGAL_CELLS TRUE SetLegalUnitCells
TRUE UseLegalUnitCells
#If a non void only unit was selected, and the last selection was a void only unit
else <-buildUnitSelected not <-buildUnitDeselected and if
FALSE SetAllLegalUnitCells
FALSE UseLegalUnitCells
endif endif
endif
#Save current state
<-curBuildUnit ->lastBuildUnit
#Initialize
:once
GetBuildUnit ->lastBuildUnit
@RebuildVoidCellsList
:RebuildVoidCellsList
#Build list of void cells
CreateList ->LEGAL_CELLS
GetMapSize ->sizeZ ->sizeX
<-sizeZ 0 do
<-sizeX 0 do
#If terrain is void, add coordinates to list
I J GetTerrain eq0 if
<-LEGAL_CELLS I J V2 AppendToList
endif
loop
loop
----
===== Next Unit Script goes here =====
----
====== Useful Functions ======
===== GetUnitsInPolygon() and IsPointInPoly() =====
Need to find units in a polygon or determine if a specific point is inside a convex/concave polygon (without holes)? These slightly inefficient and imperfect functions are for you - at least until something equivalent is available natively in 4RPL. Just put the functions at the bottom of your 4RPL script.
# IsPointInPoly(point polyList)
:IsPointInPoly
->ipip_polyList
->ipip_point
false ->ipip_retval
GetListCount(<-ipip_polyList) 1 - ->ipip_prev
<-ipip_polyList 0 do
<-ipip_polyList[I] ->ipip_linept1
<-ipip_polyList[<-ipip_prev] ->ipip_linept2
if (<-ipip_linept1.y <-ipip_point.y gt <-ipip_linept2.y <-ipip_point.y gt neq)
<-ipip_linept2.x <-ipip_linept1.x - <-ipip_point.y <-ipip_linept1.y - * ->ipip_tempval
<-ipip_tempval <-ipip_linept2.y <-ipip_linept1.y - / ->ipip_tempval
<-ipip_tempval <-ipip_linept1.x + ->ipip_tempval
if (<-ipip_point.x <-ipip_tempval lt)
<-ipip_retval ! ->ipip_retval
endif
endif
I ->ipip_prev
loop
<-ipip_retval
# GetUnitsInPolygon(unitType polyList enemyState builtState imperviousState)
:GetUnitsInPolygon
->guip_imperviousState
->guip_builtState
->guip_enemyState
->guip_polyList
->guip_unitType
# Get all units on the map that match the non-position requirements.
GetUnits(<-guip_unitType V3(128 128 128) 512 false false false <-guip_enemyState <-guip_builtState <-guip_imperviousState) ->guip_units
CreateList ->guip_retval
<-guip_units 0 do
<-guip_units[I] ->guip_tempunit
if (@IsPointInPoly(V2(GetUnitCell(<-guip_tempunit)) <-guip_polyList))
AppendToList(<-guip_retval <-guip_tempunit)
endif
loop
<-guip_retval
Example usage:
# Creates a sparks effect on each friendly unit inside a triangle.
List(
V2(18 148)
V2(138 102)
V2(44 29)
) ->testPoly
@GetUnitsInPolygon("" <-testPoly 2 0 1) ->units
TraceAllSp("Num units " GetListCount(<-units))
<-units 0 do
<-units[I] ->tempunit
CreateEffect("sparks" GetUnitPosition(<-tempunit) V3(1 1 1))
loop
----
===== Bresenham Line Algorithm =====
Calculates the points (usually map cells) in a line between a starting and ending point.
@getLine(GetMapSize 0 0 ) ->LineList
EditAddUndo(0)
Do (GetListCount(<-linelist) 0)
SetTerrain(ToCell(<-linelist[I]) 15)
Loop
#TraceAllSp (<-LineList)
# Bresenham diagonal line
# http://ericw.ca/notes/bresenhams-line-algorithm-in-csharp.html
:getLine
->z1 ->x1 ->z0 ->x0
CreateList ->line
If (Abs(<-z1 <-z0 - ) Abs(<-x1 <-x0 -) GT )
->steep (true)
->t (<-x0) # swap x0 and z0
->x0 (<-z0)
->z0 (<-t)
->t (<-x1) # swap x1 and z1
->x1 (<-z1)
->z1 (<-t)
else
->steep (false)
endIf
if (<-x0 GT (<-x1))
->swap (true)
->t (<-x0) # swap x0 and x1
->x0 (<-x1)
->x1 (<-t)
->t (<-z0) # swap z0 and z1
->z0 (<-z1)
->z1 (<-t)
else
->swap (false)
endIf
->dx (<-x1 - (<-x0))
->dz (Abs(<-z1 - (<-z0)))
->error (<-dx / (2))
if (<-z0 <-z1 LT)
->zstep (1)
else
->zstep (-1)
endif
->z (<-z0)
Do(<-x1 <-x0)
If (<-swap)
if (<-steep)
PrependToList(<-line FromCell(<-z I))
else
PrependToList(<-line FromCell(I <-z))
endif
else
if (<-steep)
PushList(<-line FromCell(<-z I))
else
PushList(<-line FromCell(I <-z))
endif
endIf
->error (<-error - (<-dz))
if (<-error LT0)
->z (<-z + (<-zstep))
->error (<-error + (<-dx))
endIf
Loop
<-line #return
----
===== Random X-Z position within the area of a circle =====
This is a simple piece of trigonometry that is able to pick a random position within the area 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 choose a random location within the area of a circle with a definable radius.\\
Note: Floats will work for the radius.\\
\\
**Applying to a V3:**
V3(cos(RandFloat RandInt(0 7) +) <-RADIUS * <-cellX + <-Y_COORDINANT RandInt(-15 16) + sin(RandFloat RandInt(0 7) +) <-RADIUS * <-cellZ +) ->position
**Applying to a V2:**
V2(cos(RandFloat RandInt(0 7) +) <-RADIUS * <-cellX + sin(RandFloat RandInt(0 7) +) <-RADIUS * <-cellZ +) ->cellLocation
Example 1:\\
Descending down onto the map from an angle rather than only straight down.\\
(Remember this is just a snipit of what can use a random position on a circle, not an entire script).
$FALL_HEIGHT:300.0
GetUnitPosition(self) ->pos
if(<-pos.y lte(GetTerrain(<-pos.x <-pos.z)))
@deploy
else
SetUnitPosition(self MoveTowards(<-pos V3(<-placementPos.x 0 <-placementPos.z) <-FALL_SPEED))
endif
:Once
#When a unit begins operating, choose a random location on a circle to relocate the unit and remember the destination position it was originally placed at.
if(GetEditMode !)
GetUnitPosition(self) ->placementPos
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
----
===== Random X-Z position along the circumference of a circle =====
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.\\
\\
RandFloat RandInt(0 7) + ->angle
V3(cos(<-angle) <-RADIUS * <-cellX + <-Y_COORDINANT RandInt(-15 16) + sin(<-angle) <-RADIUS * <-cellZ +) ->position
----
===== MultiObjective =====
This script is designed to allow for victory conditions on objectives other than 'All Required' or 'Any'. By manipulating the weights, a much more varied set of victory conditions can be used in maps. The script uses the custom objective for this; setting the custom objective to the only required objective will allow for different victory conditions.\\
The script can also be used as an optional 'Complete All Objectives' timer for maps.
# MultiObjective 2023-03-16
# Grants the custom objective if the player has completed enough of the other objectives.
# Default use of this script is for the custom objective to be required, and all other objectives be optional.
# This will make the map be completed when a certain number of objectives are completed.
$ObjectivesRequired:2
$Nullify:1 # Set these to 0 to prevent them from counting
$Totems:1 # Unused objectives do not need to have their parameters set to 0.
$Reclaim:1
$Hold:1
$Collect:1
# Numbers other than 0 and 1 can be used to give different weights to the objectives.
# As an example, setting Nullify to 2 would have nullify count as 2 objectives when completed.
# If ObjectivesRequired is also set to 2 this would make the custom objective 'Nullify all or complete 2 objectives'
GetMissionObjectiveState(0) <-Nullify mul GetMissionObjectiveState(1) <-Totems mul add GetMissionObjectiveState(2) <-Reclaim mul add max(GetMissionObjectiveState(3) 0) <-Hold mul add GetMissionObjectiveState(4) <-Collect mul add ->Points
if(<-Points <-ObjectivesRequired gte)
once
AcquireMissionObjective(5 false)
endonce
endif
----
===== Next UseFul Function goes here =====
----