The 8-bit combo could be implemented with ControlTowers or SafeAreas or any combo of Online and Offline buildings. A game.txt Event can detect the correct pattern using combinations of BuildingOnline and BuildingOffline. But what to do about level resets? The cleanest approach would be to have a script initialize all the code buildings to Offline after each reset, but this can't be done. Instant engineers are problematic since they can only turn a building Offline if there is no competition from opposing team engineers... and they either have to be disposed of, and hidden, or their presence explained. If instant Red reset engineers are left around the CTs, then the player's engineers won't be able to bring the ControlTowers Online -- only the CT team will change, and then the CT's Online & team status will get out of sync, causing problems. So sometimes the only practical solution is to swap maps after each reset, and duplicate the game.txt Events for each map's group of building IDs. Nothing new here yet.
Swapping map files requires a "jump gate" level if the hack is to be transparent to the player. So if we are going to allow the player to reset the 8-bit code level maybe 5 times, we need to be able to update the jump level 5 times, once after each reset of the playable level. This can be done with stacked ScriptTriggers in another hidden level which only gets accessed once after each reset of the playable level. This is getting complicated, and I'll diagram it and give code listings below. But there is a BIG problem that bites when you have scripts jumping from level to level.
If a script (A) does EnterLocation into a level which has an Always ScriptTrigger, the Always script (B) can get executed before the calling EnterLocation script completely finishes. This causes all sorts of problems, but the biggest problem is if script B immediately does an ExitLocation. Then the mission file of the current location gets saved improperly. Any dynamic ScriptTriggers which have been used will NOT be retired & removed from the saved mission file. Also, instant units may not get instantiated in time to be saved in the mission file. They will be lost until the next reset, or forever if the timings are always unfavorable.
It doesn't matter if the EnterLocation is the very last command of script A -- script A is still "active" in some internal sense after the target level is first entered. It is the overlapping of scripts A & B which causes the problems. And since the dynamic ScriptTriggers are not getting retired as expected, it is easy to get trapped into infinite loops A <--> B which show up only as confusing infinite blackouts. You can find these loops by inserting a Wait 1.0 in each of your scripts.
The solution to this problem is to insert a short Wait before the ExitLocation in script B. This allows script A to fully complete inside the current location. The length of the Wait can be 0.01 for hidden levels which contain nothing but a stack of ScriptTriggers... up to 0.35 or more for levels that instantiate lots of units. For the setup I'm going to describe, the chained scripts are called only after level resets, so there will be a known amount of instant units, and the Wait can be tuned to get them all onboard and hopefully stay within the black fade-out / fade-in times. EDIT: see below (end) for a workaround to the camera delay.
The following setup shows 5 resets of the level called "target" (T). All versions of the target level share the common mission: mission_target. The target levels are hidden and only accessable through the level "jump" (J). The jump missions contain stacks of ScriptTriggers which jump to the appropriate versions of the target map. The target mission contains a single dynamic ScriptTrigger which fires only on first entry, and then retires until the next reset of a target level. This reset script jumps to the hidden level "serial" (S) which also contains a stack of ScriptTriggers. Unlike the jump stacks, the scripts here are all different. They set the mission for the jump level and then jump back to the appropriate target level. They are used up in serial order.
So the first level entry from the world map goes like this: J --> Tn --> S --> Tn, where n starts at 1 (or "a" in this example). Without a reset, subsequent level entries are more direct: J --> Tn. And the n is updated each time S is visited. It's hard to describe cleanly, but it works. Below is a listing of all the filenames involved, with the appropriate scripts grouped next to the mission files that call them. There are mission swaps for the jump map, and map swaps for the target mission, but the serial map & mission never change. Also listed are the relevant sections of game.txt, and the map & mission files, plus the script files.
Thanks to xander, TGF, trickfred, et al. or I never would have figured this out.
-brice
Code: Select all
# game.txt
Locations_StartDefinition
# Id Avail mapFile missionFile
# ==================================================================
70 1 map_jump.txt mission_jump2a.txt
80 0 map_serial.txt mission_serial.txt
90 0 map_target_a.txt mission_target.txt
91 0 map_target_b.txt mission_target.txt
92 0 map_target_c.txt mission_target.txt
93 0 map_target_d.txt mission_target.txt
94 0 map_target_d.txt mission_target.txt
Locations_EndDefinition
Code: Select all
# files grouped together by map, mission, and scripts called
map_jump.txt
mission_jump2a.txt
scripts/jump2target_a.txt
mission_jump2b.txt
scripts/jump2target_b.txt
mission_jump2c.txt
scripts/jump2target_c.txt
mission_jump2d.txt
scripts/jump2target_d.txt
mission_jump2e.txt
scripts/jump2target_e.txt
map_target_a.txt
map_target_b.txt
map_target_c.txt
map_target_d.txt
map_target_e.txt
mission_target.txt
scripts/jump2serial.txt
map_serial.txt
mission_serial.txt
scripts/setjump2a.txt
scripts/setjump2b.txt
scripts/setjump2c.txt
scripts/setjump2d.txt
scripts/setjump2e.txt
Code: Select all
# mission_jump2{a,b,c,d,e} call scripts jump2target_{a,b,c,d,e}.txt
Buildings_StartDefinition
# Type id x z tm rx rz isGlobal
# ====================================================================
ScriptTrigger 1 894.15 937.09 2 1.0 0.0 0 -1 100.00 jump2target_a.txt always
ScriptTrigger 2 894.15 937.09 2 1.0 0.0 0 -1 100.00 jump2target_a.txt always
ScriptTrigger 3 894.15 937.09 2 1.0 0.0 0 -1 100.00 jump2target_a.txt always
ScriptTrigger 4 894.15 937.09 2 1.0 0.0 0 -1 100.00 jump2target_a.txt always
ScriptTrigger 5 894.15 937.09 2 1.0 0.0 0 -1 100.00 jump2target_a.txt always
...
(ScriptTriggers stacked deep enough so player never uses them all up between resets)
Buildings_EndDefinition
Code: Select all
# script jump2target_a.txt -- scripts jump2target_{b,c,d,e} are similar.
ExitLocation
EnterLocation target_a
Code: Select all
# mission_serial has stacked ScriptTriggers, each one calling a different script, in serial order.
Buildings_StartDefinition
# Type id x z tm rx rz isGlobal
# ====================================================================
ScriptTrigger 0 1040.74 1048.42 2 1.00 0.00 0 -1 100.00 setjump2a.txt always
ScriptTrigger 1 1040.74 1048.42 2 1.00 0.00 0 -1 100.00 setjump2b.txt always
ScriptTrigger 2 1040.74 1048.42 2 1.00 0.00 0 -1 100.00 setjump2c.txt always
ScriptTrigger 3 1040.74 1048.42 2 1.00 0.00 0 -1 100.00 setjump2d.txt always
ScriptTrigger 4 1040.74 1048.42 2 1.00 0.00 0 -1 100.00 setjump2e.txt always
Buildings_EndDefinition
Code: Select all
# script setjump2a.txt -- scripts setjump2{b,c,d,e} are similar.
Wait 0.01 # !!CRITICAL!! WAIT BEFORE EXIT LOCATION
SetMission jump mission_jump2a.txt
ExitLocation
EnterLocation target_a
Code: Select all
# mission_target is shared by all map_target_{a,b,c,d,e}.
# ScriptTrigger fires only once and then is retired until level is reset.
Buildings_StartDefinition
# Type id x z tm rx rz isGlobal
# ====================================================================
ScriptTrigger 990 894.15 937.09 2 1.00 0.00 0 -1 100.00 jump2serial.txt always
Buildings_EndDefinition
Code: Select all
# script jump2serial.txt
Wait 0.35 # !!CRITICAL!! WAIT BEFORE EXIT LOCATION
# longer than setjumps so instant units get instantiated and saved.
# MUST allow the calling EnterLocation script to completely finish
# before exiting this location via an Always ScriptTrigger script --
# otherwise the used dynamic ScriptTriggers won't be retired for this
# location. The current mission will be saved without dynamic changes.
ExitLocation
EnterLocation serial
EDIT: If the Wait delay in script jump2serial.txt (above) has to be long, and the level fades in and out annoyingly during resets, there is a workaround that keeps the screen black until the target level is ready. Move the "start" camera in mission_target so that it points into black space. Then create a second camera at the desired "realstart" location. Next add a non-dynamic ScriptTrigger to each of the target_{a,b,c,d,e} map files. The building IDs must all be greater than the ID of the jump2serial.txt ScriptTrigger in the mission_target file. The IDs set their precedence, so the jump2serial.txt will occur first, and then when the serial level's script jumps back, the camcutrealstart.txt script will execute. Since this script is in the map file it never gets retired, and so it always cuts to the correct camera whether the level is being re-entered or reset. This way the jump2serial.txt Wait delay can be set long enough to avoid all mission & script problems without having to worry about the fade-in.
Code: Select all
# map_target_{a,b,c,d,e} where ScriptTrigger IDs are 995..999 respectively
Buildings_StartDefinition
# Type id x z tm rx rz isGlobal
# ====================================================================
ScriptTrigger 995 894.15 937.09 2 1.0 0.0 0 -1 10.00 camcutrealstart.txt always
Buildings_EndDefinition
Code: Select all
# script camcutrealstart.txt
CamCut realstart
CamReset