User Tools

Site Tools


cw4:4rpl_tools

Differences

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

Link to this comparison view

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