Multiple Area Maps

Started by Lost in Nowhere, January 20, 2018, 02:51:02 AM

Previous topic - Next topic

Lost in Nowhere

Here's something interesting I've developed over the last few days: using CRPL to replace a map with another one! The prototype of this was Map #5315, if you need a better idea of what that means. Since I've heard of other people having some interest in doing this, I decided to clean up the script and post it.

Wait, so what does this actually do?
Basically, once you finish a map, this script clears off the map and fills the map space with new terrain, enemies, and so forth. Maybe this happens another time or three! It even allows you to replace your command nodes.
[close]

Anyways, how to use this in a map:

  • You need the data for each of the maps except the starting one. Although you could type it out, I'd recommend using the ReadMapData.crpl script on an already-created map. This script reads all data about the terrain levels, digitalis, presence of walls, and units. Note that it prints this to the game log.
  • Put the SwitchMaster.crpl script in the map that you want the map to change after completion.
  • In the same map, put as many copies of the mapheader.crpl script as you have alternate maps; put each of the maps' data into one of these scripts.
  • In the SwitchMaster.crpl script, in the :PrepareMapList function, put the name of each of the scripts from (3) in the order you want them to occur.
And then you should be good to go!

Some more in-depth explanations:
ReadMapData.crpl
This script relies heavily on the undocumented Debug keyword that writes the top of the CRPL stack to the game's log.
The result should be at the bottom of the log and look something like this:
STRING 0 0 Terrain: 2 5 4 7 -1 15 7 ...[lots of numbers later]... 4 5

(Filename: C:/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 64)

STRING 0 0 Digitalis: 0 4096

(Filename: C:/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 64)

STRING 0 0 Walls: 0 4096

(Filename: C:/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 64)

STRING 0 0 Units: 41 39 3600 2250 1 50000000 SPORETOWER 20 42 250 ENERGY RESOURCEPACK 20 42 SIPHON 35 40 NULLIFIER

(Filename: C:/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 64)

Of course, the exact output depends on the map.
The parts that we're interested in are the parts immediately following "Terrain:", "Digitalis:", "Walls:", and "Units:". The values can mostly be copy-pasted directly into the appropriate places in mapheader.crpl. There are two exceptions, both with units: everything that isn't a number on the Units line needs to be encased in quotes, and this script can't read what scripts are on other CRPL cores. The format for the latter is in the mapheader.crpl section.
[close]
mapheader.crpl
This script itself is just a template; unmodified, it ends up not doing anything, but with data added in, SwitchMaster.crpl can use it to generate a map. You need one copy of it for every additional map phase you want on the map.
The information from ReadMapData.crpl goes in the four functions in this script: GetUnits, GetDigitalis, GetWalls, and GetTerrainRow. For all but GetTerrainRow, just place the relevant information right after the function definition. For instance, GetUnits might look something like this:
:GetUnits
    41 39 3600 2250 1 50000000 "SPORETOWER"
    20 42 250 "ENERGY" "RESOURCEPACK"

GetTerrainRow has slightly more strict requirements than the other three functions. Particularly, the contents of this function must be between once and return endonce. Splitting this data into multiple once ... return endonce blocks will reduce the lag that occurs on any given frame as the new terrain is being generated. Due to CRPL being stack-based, the order of the blocks is reversed from when it's a single block. For instance:
once 7 14 -1 2 7 3 -1 3 4 6 7 5 4 3 7 10 return endonce
would become
once 3 4 6 7 5 4 3 7 10 return endonce
once 7 14 -1 2 7 3 -1 3 return endonce

If you split the blocks, be absolutely sure that every block has an even number of numbers inside of it. If not, the terrain will become very messed up.
Unit formats
For any single unit, the first two items are its x and y positions on the map. The last item is the unit type. A nullifier at coordinate (13, 70) would look like the following, for instance:
13 70 "NULLIFIER"
Depending on the unit, there may or may not be more items in between.
Emitters have three additional arguments: time until they start emitting (seconds), time between emitting (seconds), and amount of creeper emitted (units of creeper).
Spore towers have four additional arguments: build time (seconds), wave interval (seconds), number of spores, and spore payload (units of creeper).
Air exclustion towers have 1 additional argument: range (tiles).
Runner nests have 5 additional arguments: runner spawn interval (seconds), max runner population, runner move speed, runner health, and runner creeper payload (units of creeper).
Inhibitors have 4 additional arguments: time until they start emitting (seconds), time between emitting (seconds), amount of creeper emitted (units of creeper), and range of anti-AoO field.
Resource packs have 2 additional arguments: resource amount and resource type (string).
Artifacts of Odin have 1 additional argument: type (as a string).
Guppy pads have 1 additional argument: type (as a string).
Tech artifacts have 2 additional arguments: tech type (as a string), and count (-1 for unlimited).
CRPL cores are complicated, and can have arbitrarily many arguments. The last additional argument states how many scripts are attatched to the core.
The format for each script is as follows: the last item is the script name, the second-to-last item is the number of variables for that script, and the rest of the items are alternating variable names and variable values. The value comes after the name of the variable.
All other units have no additional arguments.
[close]
Terrain format
The terrain information is pairs of heights and how many of that terrain there is in a row. For instance, the numbers 7 13 describe a section of 13 tiles of height-7 terrain.
The major catch is that, because CRPL is stack-based, this information starts at the bottom-right of the map if you read the numbers from start to end. If you read it the direction the (stack-based) script does, it describes the map row-by-row, starting on the top left corner and moving right along the row. Once it reaches the end of a row, it begins at the left of the next row.
[close]
Digitalis and wall format
These are basically the same format as the terrain. Instead of heights, however, they use boolean values of whether there is digitalis/wall. 1 = digi/wall, 0 = no digi/wall.
[close]
[close]
SwitchMaster.crpl
The first two variables are for the images it shows while loading a map. LoadingBkgImg is stretched to cover the entire map, and LoadingMsgImg appears in front of it in the center of the screen.
The next two variables, as the names suggest, are for the map width and height. The only time you don't want these to be the same as the overall map size is if you're only replacing part of the map. I'm most likely going to post a map with an example of this soon.
Unless you're trying to modify its behavior, the only thing you'll probably be doing in this script is in the :PrepareMapList function.
For instance, if you had the scripts "map1.crpl" and "map2.crpl" describing the next two maps, :PrepareMapList would look something like this:

:PrepareMapList
   ClearStack
   #------------------------------
   "map1.crpl"
   "map2.crpl"
   #------------------------------
   CreateList dup ->pml_list AppendStackToList <-pml_list


Also worth noting is that if you have any other CRPL scripts that you want to be running the entire time, either put them on the same core as SwitchMaster.crpl is on, or make sure that the core they are attached to remains off of the affected area.
[close]
Don't die! :)

Lost in Nowhere

Reserved, since I might need it eventually.
Don't die! :)

Famous5000

Really nice work! I'll be using this quite a bit, since I know I'll be needing it.