Skip to the text

A world can change the plan after the common roster has been built. Eighties replaces ordinary instructions; Dinosaur and Modern add independent actions; Steam installs board systems during a later setup pass. To understand the resulting battle, we need the schedule, the operation performed at each event, and the order in which those results reach the Board.

Draw endpoints, then interpolate

Several designers use the same helper. For a parameter with level bounds L0,L1L_0,L_1, it draws integers aa and bb from the inclusive low-level and high-level endpoint ranges, then computes

α(L)=clamp⁡(L−L0L1−L0,0,1),v(L)=trunc⁡(a+α(L)(b−a)).\begin{aligned} \alpha(L)&=\operatorname{clamp}\left(\frac{L-L_0}{L_1-L_0},0,1\right),\\ v(L)&=\operatorname{trunc}\bigl(a+\alpha(L)(b-a)\bigr). \end{aligned}

The native arithmetic uses float32 and fused multiply-add. A fixed endpoint still consumes its draw, and both endpoints are drawn even when the clamped fraction is 0 or 1. Thus a table of endpoint ranges defines a draw followed by interpolation, not a uniform draw between two interpolated limits.11Draw first, interpolate secondThe helper performs two inclusive integer draws, clamps the level fraction, uses float32 fused multiply-add to interpolate the drawn endpoints, and truncates to an integer. Both draws occur even at a fixed endpoint or outside the level range. Its random consumption must be retained when reconstructing a designer sequence.0x101ab6028

Eighties: schedule music and replace instructions

DangerRoomJamDesigner runs after ordinary filling, using the global RNG. It prepares music events in the wave properties before the battle creates those zombies.

First choose the music set

Below level 7, the candidates are Pop, Punk, Rap, and 8-bit. Metal joins at level 7. The desired number of distinct types interpolates from 2 at level 1 to 5 at level 15, capped by the eligible candidate count. The designer repeatedly draws a candidate until it has enough distinct types. A duplicate is discarded, but its random draw is still consumed.

Every event later draws from this completed subset. Consecutive events can choose the same music. Choosing the subset without duplicates does not make the event sequence nonrepeating. jam_ballad occurs in a broader music list but is absent from this Endless candidate list.

Then choose the event waves

The music schedule is independent of the flag spacing from Chapter 1. Let wjw_j be the zero-based wave index of event jj:

w0=Ifirst(L),wj+1=wj+Igap(L).w_0=I_{\mathrm{first}}(L),\qquad w_{j+1}=w_j+I_{\mathrm{gap}}(L).

Each II is a fresh endpoint-draw interpolation using level bounds 1–15. Events are added while wj<N(L)w_j<N(L).

ParameterLevel 1 endpointLevel 15+ endpoint
Number of distinct music types25
First event, zero-based wave index3–41–2
Later event spacing, in waves4–52–3

The first event is therefore on player-facing wave 4–5 at the low endpoint and wave 2–3 at the high endpoint. Event spacing is counted in waves, not seconds; each event’s next gap is drawn again.

What an event changes

For one event, the designer chooses one replacement type from this mapping. Rap makes one choice for the whole event, not one choice per replaced instruction.

Notification eventReplacement type
jam_popeighties_glitter
jam_punkeighties_punk
jam_rapeighties_mc or eighties_breakdancer
jam_8biteighties_arcade
jam_metaleighties_gargantuar_danger

The replacement count uses a separate level range, 1–50: its endpoint ranges are 1–2 and 5–7. The result is capped by the current instruction count. For each removal, the designer chooses a remaining index, saves that instruction’s level, moves the final instruction into its slot, and shortens the vector. It then appends new instructions of the selected replacement type using the saved levels.

Only the levels are carried forward. New instruction fields use defaults, including a false leader marker. Unselected instructions keep their fields, but swap-removal can change their order. The final count and level multiset are preserved; total cost and leader markings can change. No ordinary budget check is repeated, so Metal can inject giants even when their replacement costs exceed the original allowance.

The event name is appended to NotificationEvents. At runtime, the action creates and positions its batch, then emits those notifications. The replacement was already decided during generation; notification delivery is the later point where the music event reaches the Board.22Jam replaces instructions before creationDangerRoomJamDesigner edits the spawn-property vector through removal and appended replacement instructions. It selects one replacement type per event, preserving replaced instruction levels and total entry count, without rerunning the ordinary budget check. AdditionalPlantfood is unchanged. The WaveManager health sample runs after the resulting wave actions create their entities.0x101c79bec0x101ce76d8

The level’s waves, with the music events

12Metal345Rap67Metal89

Before the event

  1. eighties_armor2L6
  2. eightiesL5
  3. eighties_armor2L6
  4. eightiesL6
  5. eightiesL61
  6. eightiesL64
  7. eighties_armor2L6
  8. eighties_impL5
  9. eighties_arcadeL62
  10. eighties_mcL6
  11. eighties_armor2L6
  12. eightiesL63

After swap-removal

  1. eighties_armor2L6
  2. eightiesL5
  3. eighties_armor2L6
  4. eightiesL6
  5. eighties_mcL6
  6. eighties_armor2L6
  7. eighties_armor2L6
  8. eighties_impL5

After appending

  1. eighties_armor2L6
  2. eightiesL5
  3. eighties_armor2L6
  4. eightiesL6
  5. eighties_mcL6
  6. eighties_armor2L6
  7. eighties_armor2L6
  8. eighties_impL5
  9. eighties_mcL6
  10. eighties_mcL6
  11. eighties_mcL6
  12. eighties_mcL6
Plate 3.1Eighties at level 49, example seed 3: the designer chose 8-bit, Metal, Punk, Rap, Pop and placed 3 events on the 9-wave schedule. Event 2 plays Rap on wave 5, replacing 4 of its 12 instructions with eighties_mc: each removal moves the last instruction into the freed slot, and the new instructions keep the removed levels.

AdditionalPlantfood stays unchanged. Its carrier requests are assigned later from the modified order and types. General-loot assignment also runs afterward and sees replacement costs. Finally, the wave-health snapshot measures the resulting eligible entities after placement. These are three different downstream consequences of the same replacement pass.

The reported snapshot also has an integer limit. Replacement can change the actual eligible HP while a saturated report stays unchanged.

Dinosaur: append events alongside the roster

The candidate types are raptor, stego, ptero, tyranno, and ankylo. The designer uses repeated unique draws for its subset, then chooses one type for each dinosaur event. Its endpoint interpolation uses level bounds 1–20.

ParameterLevel 1 endpointLevel 20+ endpoint
Selected dinosaur types25
First event, zero-based wave index3–42–3
Later event spacing, in waves3–41–2
Dinosaur count in the event1–23–4

The first index and subsequent gaps define an event sequence just as above. At an event, the requested number of dinosaur actions is appended alongside ordinary zombie actions. Rows are drawn from a consumable list of 0–4, replenished when too few entries remain for the upcoming event. These choices consume the global RNG and do not spend the ordinary zombie budget.33Independent dinosaur actionsThe designer selects a type subset and adds dinosaur actions to the wave properties. Event count, first-wave index, and spacing use the shared endpoint-draw interpolation helper. One type is chosen for an event; rows are drawn from a consumable vector, replenished as needed. These actions are added outside ordinary zombie budget filling.0x10172986c

Modern: an event creates portals with their own queues

The fallback candidates are ten families: Eighties, Dinosaur, Lost City, Ice Age, Beach, Dark Ages, Future, West, Egypt, and Pirate. Their initial selection weights are all 1.0. The designer selects families without replacement, with subset size growing from 1 at level 1 to 4 at level 30. Each portal-event wave chooses a family from that subset.

Event parameterLevel 1 endpointLevel 30+ endpoint
First event, zero-based wave index42–3
Later event spacing, in waves42
Portals in one event13–4
Spawn column, zero-based85–6
Computed zombies-per-portal value1–23–4
Computed list start index00
Computed list end index03

Rows are sampled from 0–4 without replacement, with replenishment. Family and row choices use the supplied designer RNG; endpoint interpolation consumes the global RNG.

The last three computed values are not forwarded by the ordinary SpawnModernPortalsWaveAction. It forwards the portal family and position, leaving the runtime count/start/end overrides at their defaults. Those discarded computations still consumed their random draws.

The portal therefore copies its family’s full four-slot list and shuffles it through the imported C++ RNG machinery. Duplicate names remain separate slots. For Egypt, the source list is ra, explorer, explorer, pharaoh: two Explorer slots, not one. All ten families can be inspected in the view below.

Opening, emission, and closure

The first requested deadline is the creation time plus a random offset between zero and a draw from the 10–12-second interval. Emission also waits for opening to finish. Each later interval is added to the previous scheduled deadline, not simply to a delayed actual emission time.

Each emitted child receives wave marker −5-5. It is excluded from ordinary current-wave health and initial off-lawn rush, while still participating in the separate victory and loss predicates. Its requested-level bounds come from the shared DangerRoom curve, but its choice uses a global float draw rather than the ordinary instruction’s integer-residue comparison.

After emitting the last slot, the portal still schedules another due check. That later check sees an empty queue and starts closing; the close-animation callback then requests removal. Until removal, the portal itself remains a completion blocker, independently of its surviving children.44The portal action's actual payloadThe designer computes a count and list-range values, but the ordinary portal action does not forward them to the created grid item. Its post-create helper returns directly. Runtime initialization copies the full configured family list, retaining duplicate slots, then shuffles it. Emission creates children with wave marker -5; the default interval bounds are 10 and 12 game seconds.0x100739c580x101b044480x100120f800x100121a00

Each family’s four slots; choose one

  • EgyptRaExplorerExplorerPharaoh
  • Pirateseagullpirate_captainpirate_captainbarrelroller
  • Westponchochicken_farmerponchopiano
  • Futurefuture_jetpackfuture_protectorfuture_jetpackmech_cone
  • Dark Agesdark_jugglerdark_wizarddark_jugglerdark_armor3
  • Beachbeach_snorkelbeach_surferbeach_surferbeach_octopus
  • Ice Ageiceage_dodoiceage_weaselhoardericeage_dodoiceage_armor3
  • Lost Citylostcity_buglostcity_excavatorlostcity_buglostcity_crystalskull
  • Eightieseighties_8bit_armor1eighties_boomboxeighties_8bit_armor2eighties_boombox
  • Dinosaurdino_impdino_bullydino_impdino_armor3

The queue after the shuffle, with each entry’s original slot

  1. 1Explorerslot 3
  2. 2Raslot 1
  3. 3Explorerslot 2
  4. 4Pharaohslot 4
Scheduled deadlines
D1 · 3D2 · 14D3 · 25D4 · 36
The portal
openExplorer · 5Ra · 14Explorer · 25Pharaoh · 36empty: closing
Plate 3.2Egypt family: the portal’s list, shuffled from library position 2, gives the queue above. Its first deadline is 3 s, its opening finishes at 5 s, and each later deadline adds 11 s to the previous deadline, not to the emission: the children leave at 5, 14, 25, 36 s, and the due check at 47 s finds the queue empty and starts the closing.

Steam: fixed board systems and separate clocks

Steam uses the later setup hook. With α=clamp⁡((L−1)/99,0,1)\alpha=\operatorname{clamp}((L-1)/99,0,1), its counts are

nsmoke=⌊3α⌋,npipe=⌊5α⌋.n_{\mathrm{smoke}}=\lfloor3\alpha\rfloor,\qquad n_{\mathrm{pipe}}=\lfloor5\alpha\rfloor.

Smoke-hole counts first reach 1, 2, and 3 at levels 34, 67, and 100; pipeline counts reach 1–5 at 21, 41, 61, 81, and 100. Their positions are fixed: player-facing smoke column 6, starting at row 2; pipelines from column 7 to column 5, starting at row 1.

The first smoke-start parameter interpolates from 15 to 30 seconds. Both smoke damage and pipeline blockage damage interpolate from 30 to 100, using the pipeline-named damage fields. The smoke-start call uses the fields in the reverse order suggested by their Min/Max names.

Smoke waits for events

At the start call, the first alert deadline is t+3t+3, while the first roar deadline is independently t+StartTime⁡t+\operatorname{StartTime}. Roar-animation completion activates the smoke. While active, an accumulator strictly greater than 1 second causes one damage pass and subtraction of 1 second. The pass selects one plant target in each of the hole’s cell and eight neighbors; it does not independently damage every stacked layer.

There is no automatic five-second expiry of active smoke. A clearing path such as blow_off or absorb must complete. Its removal event starts the cooldown: the next alert is clearing time + 5 seconds, and the next roar is another 3 seconds later, before extra delay. The inspected removal event is broadcast to registered hole handlers without an owning-hole argument.

Pipes retain the same entities

Entry requires a qualifying zombie in the entrance cell, X distance strictly below 0.5 from the entrance, runtime size small, and the shared capability/state checks including CanBeLaunchedByPlants. The Steam coal miner is mid, and its giant is big, so they fail this size gate.

The ordinary two-column distance produces a duration parameter of 2 seconds, but the entity deadlines are t+1t+1, t+2t+2, and t+3t+3. It starts using the exit position at the underground deadline and finishes only after the final deadline is strictly exceeded. Several zombies can be in flight together; this is not a single-server queue.

The blocking plant flattenedshroom can extend the underground and completion deadlines; its supplied BlockTime is 12 seconds. When transported references remain, damage is applied once per blocking plant per update as DamagePerSecond times game delta, not once per transported zombie. Transport retains the original level and wave marker, and state ZS_StuckInGround is not excluded by the base current-wave health query.55Steam's setup and runtime clocksThe later setup hook derives smoke-hole and pipeline counts from the clamped level fraction. Smoke uses a clearing-triggered cooldown and a damage update over neighboring cells. The pipeline's duration parameter and its state deadlines are distinct: the baseline parameter is 2, while deadlines are t+1, t+2, and t+3, with a strict comparison on the last transition. Blocking-plant damage is applied per plant per update.0x100b980c00x101ec8840

The lawn, columns and rows as the player counts them

1
pipeline, entrance at column 7, exit at column 5
2
smoke holepipeline, entrance at column 7, exit at column 5
3
smoke holepipeline, entrance at column 7, exit at column 5
4
5
A pipe journey
exit · 2undergrounddone · 3
Plate 3.3Steam at level 67, α = 0.667: 2 smoke holes in column 6 and 3 pipelines from column 7 to column 5; the first smoke start is 25 s, and smoke and blockage damage 76. Below, a small zombie entering a pipe at 0 s: underground from 1 s, at the exit at 2 s, finished once 3 s is exceeded.

Notes

The evidence behind each claim: the code path, and the native addresses where it was traced.

  1. 1

    Draw first, interpolate second

    The helper performs two inclusive integer draws, clamps the level fraction, uses float32 fused multiply-add to interpolate the drawn endpoints, and truncates to an integer. Both draws occur even at a fixed endpoint or outside the level range. Its random consumption must be retained when reconstructing a designer sequence.

    0x101ab6028
  2. 2

    Jam replaces instructions before creation

    DangerRoomJamDesigner edits the spawn-property vector through removal and appended replacement instructions. It selects one replacement type per event, preserving replaced instruction levels and total entry count, without rerunning the ordinary budget check. AdditionalPlantfood is unchanged. The WaveManager health sample runs after the resulting wave actions create their entities.

    0x101c79bec0x101ce76d8
  3. 3

    Independent dinosaur actions

    The designer selects a type subset and adds dinosaur actions to the wave properties. Event count, first-wave index, and spacing use the shared endpoint-draw interpolation helper. One type is chosen for an event; rows are drawn from a consumable vector, replenished as needed. These actions are added outside ordinary zombie budget filling.

    0x10172986c
  4. 4

    The portal action's actual payload

    The designer computes a count and list-range values, but the ordinary portal action does not forward them to the created grid item. Its post-create helper returns directly. Runtime initialization copies the full configured family list, retaining duplicate slots, then shuffles it. Emission creates children with wave marker -5; the default interval bounds are 10 and 12 game seconds.

    0x100739c580x101b044480x100120f800x100121a00
  5. 5

    Steam's setup and runtime clocks

    The later setup hook derives smoke-hole and pipeline counts from the clamped level fraction. Smoke uses a clearing-triggered cooldown and a damage update over neighboring cells. The pipeline's duration parameter and its state deadlines are distinct: the baseline parameter is 2, while deadlines are t+1, t+2, and t+3, with a strict comparison on the last transition. Blocking-plant damage is applied per plant per update.

    0x100b980c00x101ec8840