afterglow/sim/rooms_data.py¶

Part of Afterglow.

  1"""Authored room data for Afterglow: the full game, three worlds.
  2
  3Eighteen LARGE single-screen rooms (56 wide x 30 tall at our 8px tile; the camera
  4auto-frames the whole grid) that TEACH BY DESIGN, no text tutorials. Each room has a
  5real winding path with the exit set well away from the spawn (usually a vertical climb
  6or a gated crossing), so you cannot simply walk-and-hop straight at it. The difficulty
  7curve hands the player one new idea at a time and then asks them to combine ideas:
  8
  9  WORLD 1  'glade'   (6 rooms): pure movement, NO instant-death hazards (a miss just
 10      lands on a lower floor). run + variable-height jump -> a stepped climb -> a longer
 11      run-jump ascent -> the wall-jump (a short shaft, then a tall one) -> a graduation
 12      that adds a spring. No dash, crystals, orbs or gates.
 13  WORLD 2  'caverns' (6 rooms): the air-DASH + telegraphed hazards. the dash alone over
 14      a chasm, then a two-gap crossing, then resonance CRYSTALS (dash-through refills the
 15      dash), a 2-crystal chain, a rising up-dash constellation, and the long dash-chain.
 16  WORLD 3  'spire'   (6 rooms): GLOW ORBS + LIGHT-GATES (a gate is solid only while the
 17      Wisp glows) braided with dash-chains and hazards. introduced gently (fetch the
 18      light, cross the bridge), then under a 4s glow clock, then with the dash, then a
 19      vertical gate shaft, a timed dash-chain, and a climactic finale.
 20
 21Each room has exactly one spawn 'P', one exit 'E', and one hidden shard '*' tucked off
 22the main path as the expert-optional challenge. Every row is exactly 56 characters; see
 23tests/test_rooms.py for the structural gate and tests/test_solvable.py for the
 24reachability + naive-gating + scripted-solution proofs.
 25
 26After editing a room, run the level checker, which searches it with the real sim for an
 27unreachable exit, a soft-lock and an unreachable shard (seconds per room, so it is not in
 28the test suite), then re-record the room's stored solution in tests/_solver.py if it
 29changed:
 30
 31    uv run --package simvx-core python examples/demos/afterglow/tests/check_levels.py "Room Name"
 32
 33Grid legend (see sim.tiles): equal-length rows, y-DOWN, one char / 8px tile:
 34  '.' empty | '#' solid | '^' hazard | 'C' crystal | 'O' glow orb |
 35  'G' light-gate | 'S' spring | '*' hidden shard | '-' moving platform |
 36  'P' spawn | 'E' exit.
 37"""
 38
 39from __future__ import annotations
 40
 41from .room import RoomDef
 42
 43# ---------------------------------------------------------------------------
 44# WORLD 1 -- THE GLADE: pure movement fundamentals (no hazards).
 45# ---------------------------------------------------------------------------
 46
 47# 1.1 First Light: gentle opener. An ascending staircase of platforms climbs up-and-
 48# right from the low-left spawn to a high-right exit, growing gaps teaching the run and
 49# the variable-height jump. The exit is too high to walk to: you must climb. Shard on a
 50# low side ledge below the start (an optional drop-and-return off the ascent).
 51_GLADE_01 = RoomDef(
 52    name="First Light",
 53    palette="glade",
 54    grid=[
 55        "########################################################",
 56        "########################################################",
 57        "#..................................................#####",
 58        "#..............................................E...#####",
 59        "#...........................................########...#",
 60        "#......................................................#",
 61        "#......................................................#",
 62        "#.................................############.........#",
 63        "#......................................................#",
 64        "#......................................................#",
 65        "#......................................................#",
 66        "#......................############....................#",
 67        "#......................................................#",
 68        "#......................................................#",
 69        "#......................................................#",
 70        "#...........#####......................................#",
 71        "#......................................................#",
 72        "#......................................................#",
 73        "#...................*..................................#",
 74        "#.................#####................................#",
 75        "#......................................................#",
 76        "#......................................................#",
 77        "#......................................................#",
 78        "##########.............................................#",
 79        "#......................................................#",
 80        "#......................................................#",
 81        "#.P....................................................#",
 82        "########################################################",
 83        "########################################################",
 84        "########################################################",
 85    ],
 86)
 87
 88# 1.2 Over the Brook: a stepped ascent over a wide trough. Stair platforms rise to a
 89# high-right exit ledge; missing a step drops you onto the safe lower floor (a brook,
 90# no hazard in the glade), never a punishing pit. Shard on a high ledge above the exit.
 91_GLADE_02 = RoomDef(
 92    name="Over the Brook",
 93    palette="glade",
 94    grid=[
 95        "########################################################",
 96        "#......................................................#",
 97        "#......................................................#",
 98        "#......................................................#",
 99        "#.......................................E..............#",
100        "#......................................####............#",
101        "#......................................................#",
102        "#......................................................#",
103        "#......................................................#",
104        "#.............................................*........#",
105        "#...........................................########...#",
106        "#......................................................#",
107        "#.................................######...............#",
108        "#......................................................#",
109        "#.........................######.......................#",
110        "#......................................................#",
111        "#.................######...............................#",
112        "#......................................................#",
113        "#.........######.......................................#",
114        "#......................................................#",
115        "#......................................................#",
116        "#......................................................#",
117        "#......................................................#",
118        "#.P....................................................#",
119        "#########..............................................#",
120        "#......................................................#",
121        "#......................................................#",
122        "########################################################",
123        "########################################################",
124        "########################################################",
125    ],
126)
127
128# 1.3 Stepping Stones: a long run of platforms with widening gaps rising to a high
129# exit; commit to run-jumps and read the spacing. Spawn low-left, exit high-right.
130# Shard on a low side ledge below the start (drop off the main line and climb back).
131_GLADE_03 = RoomDef(
132    name="Stepping Stones",
133    palette="glade",
134    grid=[
135        "########################################################",
136        "########################################################",
137        "#................................................#######",
138        "#..............................................E.#######",
139        "#...........................................########...#",
140        "#......................................................#",
141        "#......................................................#",
142        "#...........................................#######....#",
143        "#.......................................###########....#",
144        "#......................................................#",
145        "#......................................................#",
146        "#.............................###############..........#",
147        "#......................................................#",
148        "#......................................................#",
149        "#......................................................#",
150        "#.................................#############........#",
151        "#......................................................#",
152        "#......................................................#",
153        "#.......................###############................#",
154        "#......................................................#",
155        "#......................................................#",
156        "#.............###############..........................#",
157        "#......................................................#",
158        "#.*....................................................#",
159        "#####.#############....................................#",
160        "#......................................................#",
161        "#...P..................................................#",
162        "########################################################",
163        "########################################################",
164        "########################################################",
165    ],
166)
167
168# 1.4 The Lean: introduce the wall-jump in a short, gentle 2-wide shaft. Spawn at the
169# base; alternate wall-jumps up the two faces to a ledge bearing the exit at the top.
170# Falling drops back to the shaft floor (no hazard). Shorter than the Chimney so the
171# idea lands before the full test. Shard on a ledge off the right wall.
172_GLADE_04 = RoomDef(
173    name="The Lean",
174    palette="glade",
175    grid=[
176        "########################################################",
177        "#......................................................#",
178        "#......................................................#",
179        "#................E.....................................#",
180        "#..............#####...................................#",
181        "####################..##################################",
182        "####################..##################################",
183        "####################..##################################",
184        "#################.....##################################",
185        "#################.*...##################################",
186        "####################..##################################",
187        "####################..##################################",
188        "####################..##################################",
189        "####################..##################################",
190        "####################..##################################",
191        "####################..##################################",
192        "####################..##################################",
193        "####################..##################################",
194        "####################..##################################",
195        "####################P.##################################",
196        "###################.##.#################################",
197        "###################.##.#################################",
198        "###################.##.#################################",
199        "###################.##.#################################",
200        "###################.##.#################################",
201        "###################.##.#################################",
202        "###################.##.#################################",
203        "########################################################",
204        "########################################################",
205        "########################################################",
206    ],
207)
208
209# 1.5 Chimney: the pure wall-jump test, a tall narrow 2-wide shaft climbed by
210# alternating wall-jumps; the top opens onto a ledge bearing the exit. Shard on a
211# ledge off the right pillar partway up (an optional dismount).
212_GLADE_05 = RoomDef(
213    name="Chimney",
214    palette="glade",
215    grid=[
216        "########################################################",
217        "#......................................................#",
218        "#...................E..................................#",
219        "#.................#####................................#",
220        "#.......................#..#...........................#",
221        "#.......................#..#...........................#",
222        "#.......................#..#...........................#",
223        "#.......................#..#...........................#",
224        "#.......................#.##...........................#",
225        "#.......................#..#...........................#",
226        "#.......................#..#...........................#",
227        "#.......................#..#...........................#",
228        "#.......................##.#*..........................#",
229        "#.......................#..##..........................#",
230        "#.......................#..#...........................#",
231        "#.......................#..#...........................#",
232        "#.......................#.##...........................#",
233        "#.......................#..#...........................#",
234        "#.......................#..#...........................#",
235        "#.......................#..#...........................#",
236        "#.......................##.#...........................#",
237        "#.......................#..#...........................#",
238        "#.......................#P.#...........................#",
239        "########################################################",
240        "#......................................................#",
241        "#......................................................#",
242        "#......................................................#",
243        "#......................................................#",
244        "########################################################",
245        "########################################################",
246    ],
247)
248
249# 1.6 Glade's End: the graduation. Run-jump up a flight of platforms, then a spring
250# launches you to the high exit ledge (too high to reach by jump alone), combining the
251# run, the variable jump and the spring. Shard on a side ledge near the spring's arc.
252_GLADE_06 = RoomDef(
253    name="Glade's End",
254    palette="glade",
255    grid=[
256        "########################################################",
257        "########################################################",
258        "#......................................#################",
259        "#....................................E.#################",
260        "#.........................##.....##############........#",
261        "#......................................................#",
262        "#......................................................#",
263        "#......................................................#",
264        "#......................................................#",
265        "#......................................................#",
266        "#......................................................#",
267        "#....................*.................................#",
268        "#..................#####......S........................#",
269        "#.......................#############..................#",
270        "#......................................................#",
271        "#......................................................#",
272        "#.....................#############....................#",
273        "#......................................................#",
274        "#.............#############............................#",
275        "#......................................................#",
276        "#......................................................#",
277        "#.......#############..................................#",
278        "#......................................................#",
279        "#......................................................#",
280        "#.###########..........................................#",
281        "#......................................................#",
282        "#..P...................................................#",
283        "########################################################",
284        "########################################################",
285        "########################################################",
286    ],
287)
288
289# ---------------------------------------------------------------------------
290# WORLD 2 -- THE CAVERNS: the air-dash, resonance crystals, dash-chains.
291# ---------------------------------------------------------------------------
292
293# 2.1 First Spark: a chasm too wide to jump; an air-dash carries you across. Spawn and
294# the far exit ledge sit at the same height (a clean horizontal dash) so the dash reads
295# in isolation. Hazard floor under the chasm (telegraphed). Shard on a lower-right ledge
296# (a downward dash off the line).
297_CAVERNS_01 = RoomDef(
298    name="First Spark",
299    palette="caverns",
300    grid=[
301        "########################################################",
302        "#......................................................#",
303        "#......................................................#",
304        "#......................................................#",
305        "#......................................................#",
306        "#......................................................#",
307        "#......................................................#",
308        "#......................................................#",
309        "#.P....................................................#",
310        "###############...............*........................#",
311        "###############.............##############.............#",
312        "#......................................................#",
313        "#......................................................#",
314        "#...........................................E..........#",
315        "#.........................................#####........#",
316        "#......................................................#",
317        "#......................................................#",
318        "#......................................................#",
319        "#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#",
320        "#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#",
321        "#......................................................#",
322        "#......................................................#",
323        "#......................................................#",
324        "#......................................................#",
325        "#......................................................#",
326        "#......................................................#",
327        "#......................................................#",
328        "#......................................................#",
329        "########################################################",
330        "########################################################",
331    ],
332)
333
334# 2.2 Crossing: two chasms in a row; the dash recharges on landing, so each gap is its
335# own dash, with a mid platform between and a stepping ledge climbing out of the second
336# one. Both gaps cross a hazard floor. Shard high over the mid platform (an up-aimed
337# dash).
338_CAVERNS_02 = RoomDef(
339    name="Crossing",
340    palette="caverns",
341    grid=[
342        "########################################################",
343        "#......................................................#",
344        "#......................................................#",
345        "#......................................................#",
346        "#.........................*......................E.....#",
347        "#.......................#####..................#####...#",
348        "#......................................................#",
349        "#......................................................#",
350        "#......................................................#",
351        "#....................................#####.............#",
352        "#......................................................#",
353        "#......................................................#",
354        "#......................................................#",
355        "#.....................########.........................#",
356        "#......................................................#",
357        "#......................................................#",
358        "#.P....................................................#",
359        "##########.............................................#",
360        "#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#",
361        "#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#",
362        "#......................................................#",
363        "#......................................................#",
364        "#......................................................#",
365        "#......................................................#",
366        "#......................................................#",
367        "#......................................................#",
368        "#......................................................#",
369        "#......................................................#",
370        "########################################################",
371        "########################################################",
372    ],
373)
374
375# 2.3 Resonance: one resonance crystal hangs mid-chasm. Dash into it to shatter and
376# refill the dash, then dash again to the far exit. The core dash-refill lesson over a
377# hazard chasm. Shard on a low ledge mid-chasm (an angled-down dash off the line).
378_CAVERNS_03 = RoomDef(
379    name="Resonance",
380    palette="caverns",
381    grid=[
382        "########################################################",
383        "#......................................................#",
384        "#......................................................#",
385        "#......................................................#",
386        "#......................................................#",
387        "#......................................................#",
388        "#......................................................#",
389        "#......................................................#",
390        "#.........................C............................#",
391        "#......................................................#",
392        "#.P....................................................#",
393        "#########..............................................#",
394        "#......................................................#",
395        "#...........................................E..........#",
396        "#.........................................#######......#",
397        "#......................................................#",
398        "#...................*..................................#",
399        "#.................####.................................#",
400        "#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#",
401        "#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#",
402        "#......................................................#",
403        "#......................................................#",
404        "#......................................................#",
405        "#......................................................#",
406        "#......................................................#",
407        "#......................................................#",
408        "#......................................................#",
409        "#......................................................#",
410        "########################################################",
411        "########################################################",
412    ],
413)
414
415# 2.4 Twin Lights: two crystals stagger across the chasm: dash, refill, dash, refill,
416# dash. A short constellation building the chaining rhythm over a hazard floor. Shard
417# high above the second crystal (aim the middle dash a touch upward).
418_CAVERNS_04 = RoomDef(
419    name="Twin Lights",
420    palette="caverns",
421    grid=[
422        "########################################################",
423        "########################################################",
424        "########################################################",
425        "#......................................................#",
426        "#......................................................#",
427        "#.......................*..............................#",
428        "#.....................#####............................#",
429        "#......................................................#",
430        "#......................................................#",
431        "#......................................................#",
432        "#......................................................#",
433        "#.......................................################",
434        "#.P...........C.........C.........E.....################",
435        "#########.......................#######................#",
436        "#......................................................#",
437        "#......................................................#",
438        "#......................................................#",
439        "#......................................................#",
440        "#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#",
441        "#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#",
442        "#......................................................#",
443        "#......................................................#",
444        "#......................................................#",
445        "#......................................................#",
446        "#......................................................#",
447        "#......................................................#",
448        "#......................................................#",
449        "########################################################",
450        "########################################################",
451        "########################################################",
452    ],
453)
454
455# 2.5 Constellation: a rising diagonal of crystals demands an upward dash-chain. Jump
456# off the spawn ledge, then up-right dash through each crystal so it refills the dash for
457# the next angled dash, climbing to the apex exit. Shard on a high-left ledge off the
458# chain.
459_CAVERNS_05 = RoomDef(
460    name="Constellation",
461    palette="caverns",
462    grid=[
463        "########################################################",
464        "#......................................................#",
465        "#......................................................#",
466        "#.......................................E..............#",
467        "#.....................................#####............#",
468        "#......................................................#",
469        "#................................C.....................#",
470        "#......................................................#",
471        "#..........................C...........................#",
472        "#......................................................#",
473        "#....................C.................................#",
474        "#.....*................................................#",
475        "#...####.......C.......................................#",
476        "#......................................................#",
477        "#......................................................#",
478        "#.P....................................................#",
479        "#########..............................................#",
480        "#......................................................#",
481        "#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#",
482        "#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#",
483        "#......................................................#",
484        "#......................................................#",
485        "#......................................................#",
486        "#......................................................#",
487        "#......................................................#",
488        "#......................................................#",
489        "#......................................................#",
490        "#......................................................#",
491        "########################################################",
492        "########################################################",
493    ],
494)
495
496# 2.6 The Long Dark: the caverns capstone and the longest dash-chain. Three crystals
497# strung across a very wide hazard chasm, each refilling the dash for the next, carrying
498# the Wisp all the way to the far exit. Shard tucked low past the mid-chain.
499_CAVERNS_06 = RoomDef(
500    name="The Long Dark",
501    palette="caverns",
502    grid=[
503        "########################################################",
504        "#......................................................#",
505        "#......................................................#",
506        "#......................................................#",
507        "#......................................................#",
508        "#......................................................#",
509        "#......................................................#",
510        "#......................................................#",
511        "#...............C...........C...........C..............#",
512        "#......................................................#",
513        "#.P....................................................#",
514        "########...............................................#",
515        "#................................................E.....#",
516        "#..............................................######..#",
517        "#......................................................#",
518        "#.................................*....................#",
519        "#...............................####...................#",
520        "#......................................................#",
521        "#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#",
522        "#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#",
523        "#......................................................#",
524        "#......................................................#",
525        "#......................................................#",
526        "#......................................................#",
527        "#......................................................#",
528        "#......................................................#",
529        "#......................................................#",
530        "#......................................................#",
531        "########################################################",
532        "########################################################",
533    ],
534)
535
536# ---------------------------------------------------------------------------
537# WORLD 3 -- THE AETHER SPIRE: glow orbs + light-gates, then the finale.
538# ---------------------------------------------------------------------------
539
540# 3.1 Kindling: the first orb + gate. The orb sits to the LEFT of spawn (away from the
541# exit): fetch the light, return, then cross the now-solid gate bridge to the exit on the
542# right. Walking straight at the exit steps onto an open (unglowing) gate and drops into
543# a deep but SAFE pit you cannot climb back out of toward the exit. Gentle, no hazards.
544# Shard above, behind a second gate the same glow opens.
545_SPIRE_01 = RoomDef(
546    name="Kindling",
547    palette="spire",
548    grid=[
549        "########################################################",
550        "#......................................................#",
551        "#......................................................#",
552        "#......................................................#",
553        "#......................................................#",
554        "#......................................................#",
555        "#......................................................#",
556        "#......................................................#",
557        "#.......P.....GGGGGGGGGGGGGGGGGGGGGGGG....E............#",
558        "###...########........................##########.......#",
559        "#......................................................#",
560        "#......................................................#",
561        "#......................................................#",
562        "#...O...........*......................................#",
563        "#..###.........###.....................................#",
564        "#......................................................#",
565        "#......................................................#",
566        "#......................................................#",
567        "#......................................................#",
568        "#......................................................#",
569        "#......................................................#",
570        "#......................................................#",
571        "#......................................................#",
572        "#......................................................#",
573        "#......................................................#",
574        "#......................................................#",
575        "########################################################",
576        "#......................................................#",
577        "########################################################",
578        "########################################################",
579    ],
580)
581
582# 3.2 Hold the Light: the glow is a 4s clock. Grab the orb, cross a long gate bridge
583# before it fades; now hazards lurk under the bridge. Spawn left, exit right. Shard past
584# a second, higher gate bridge.
585_SPIRE_02 = RoomDef(
586    name="Hold the Light",
587    palette="spire",
588    grid=[
589        "########################################################",
590        "#......................................................#",
591        "#......................................................#",
592        "#....O.................................................#",
593        "#......................................................#",
594        "#.......................................*..............#",
595        "#....C...........................GGGGGG.##.............#",
596        "#......................................................#",
597        "#......................................................#",
598        "#....C.................................................#",
599        "#......................................................#",
600        "#......................................................#",
601        "#.P..S.................................................#",
602        "########....GGGGGGGGGGGGGG..................E..........#",
603        "########..................................####.........#",
604        "#.......^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^............#",
605        "#......................................................#",
606        "#......................................................#",
607        "#......................................................#",
608        "#......................................................#",
609        "#......................................................#",
610        "#......................................................#",
611        "#......................................................#",
612        "#......................................................#",
613        "#......................................................#",
614        "#......................................................#",
615        "#......................................................#",
616        "#......................................................#",
617        "########################################################",
618        "########################################################",
619    ],
620)
621
622# 3.3 Quickening: combine the dash with the gate. Grab the orb, dash across a hazard gap
623# onto a gate bridge that only exists while you glow; the dash gets you there in time.
624# Shard high-left (an up-dash off a crystal). The orb is left of the dash line so a dash
625# without glowing falls.
626_SPIRE_03 = RoomDef(
627    name="Quickening",
628    palette="spire",
629    grid=[
630        "########################################################",
631        "#......................................................#",
632        "#......................................................#",
633        "#.........*............................................#",
634        "#.......####...........................................#",
635        "#......................................................#",
636        "#..........C...........................................#",
637        "#......................................................#",
638        "#......................................................#",
639        "#......................................................#",
640        "#...O...P..............................................#",
641        "###############.........GGGGGGGGGG......E..............#",
642        "###############...................########.............#",
643        "#......................................................#",
644        "#......................................................#",
645        "#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#",
646        "#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#",
647        "#......................................................#",
648        "#......................................................#",
649        "#......................................................#",
650        "#......................................................#",
651        "#......................................................#",
652        "#......................................................#",
653        "#......................................................#",
654        "#......................................................#",
655        "#......................................................#",
656        "#......................................................#",
657        "#......................................................#",
658        "########################################################",
659        "########################################################",
660    ],
661)
662
663# 3.4 Lantern Climb: a vertical gate shaft. Grab the orb at the base, then wall-jump up
664# the two gate pillars (solid only while glowing) to the exit at the top, racing the 4s
665# glow clock. Shard on a ledge off the shaft's right wall.
666_SPIRE_04 = RoomDef(
667    name="Lantern Climb",
668    palette="spire",
669    grid=[
670        "########################################################",
671        "#......................................................#",
672        "#......................................................#",
673        "#......................................................#",
674        "#......................................................#",
675        "#......................................................#",
676        "#......................................................#",
677        "#........E.............................................#",
678        "####################...................................#",
679        "#.....................G..G.............................#",
680        "#.....................G..G*............................#",
681        "#.....................G..G###..........................#",
682        "#.....................G..G.............................#",
683        "#.....................G..G.............................#",
684        "#.....................G..G.............................#",
685        "#.....................G..G.............................#",
686        "#.....................G..G.............................#",
687        "#.....................G..G.............................#",
688        "#.....................G..G...............O.............#",
689        "#.....................G..G.............................#",
690        "#.....................G..G.............................#",
691        "#.....................G................................#",
692        "#.....................G.P..............................#",
693        "########################################################",
694        "#......................................................#",
695        "#......................................................#",
696        "#......................................................#",
697        "#......................................................#",
698        "########################################################",
699        "########################################################",
700    ],
701)
702
703# 3.5 Beacon Run: the timed dash-chain across gates. Grab the orb, dash, a crystal
704# mid-chasm refills the dash, then a gate bridge, all under the 4s glow clock. The
705# hardest pre-finale room. Shard hidden low over the hazard (an angled-down dash off the
706# easy line).
707_SPIRE_05 = RoomDef(
708    name="Beacon Run",
709    palette="spire",
710    grid=[
711        "########################################################",
712        "#......................................................#",
713        "#......................................................#",
714        "#......................................................#",
715        "#......................................................#",
716        "#......................................................#",
717        "#......................................................#",
718        "#......................................................#",
719        "#......................................................#",
720        "#......................................................#",
721        "#...O...P..............................................#",
722        "###########.........C.......GGGGGGGGGG......E..........#",
723        "###########...........................########.........#",
724        "#......................................................#",
725        "#...............*......................................#",
726        "#.............####.....................................#",
727        "#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#",
728        "#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#",
729        "#......................................................#",
730        "#......................................................#",
731        "#......................................................#",
732        "#......................................................#",
733        "#......................................................#",
734        "#......................................................#",
735        "#......................................................#",
736        "#......................................................#",
737        "#......................................................#",
738        "#......................................................#",
739        "########################################################",
740        "########################################################",
741    ],
742)
743
744# 3.6 The Aether Spire: THE FINALE, braiding every mechanic. Grab the orb, dash through
745# a resonance crystal to refill, dash onto the timed gate bridge (solid only while
746# glowing), cross it, then ride a spring up to the summit exit. Climactic and fair.
747# Shard on a high ledge past the spring.
748_SPIRE_06 = RoomDef(
749    name="The Aether Spire",
750    palette="spire",
751    grid=[
752        "########################################################",
753        "#......................................................#",
754        "#...........................................E..........#",
755        "#.........................................######.......#",
756        "#......................................................#",
757        "#.................................................*....#",
758        "#...............................................####...#",
759        "#......................................................#",
760        "#......................................................#",
761        "#......................................................#",
762        "#......................................................#",
763        "#...O...P..............................................#",
764        "###########.........C.......GGGGGGGGGG......S..........#",
765        "###########...........................########.........#",
766        "#......................................................#",
767        "#......................................................#",
768        "#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#",
769        "#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#",
770        "#......................................................#",
771        "#......................................................#",
772        "#......................................................#",
773        "#......................................................#",
774        "#......................................................#",
775        "#......................................................#",
776        "#......................................................#",
777        "#......................................................#",
778        "#......................................................#",
779        "#......................................................#",
780        "########################################################",
781        "########################################################",
782    ],
783)
784
785
786WORLDS = [
787    {
788        "id": "glade",
789        "name": "The Glade",
790        "rooms": [_GLADE_01, _GLADE_02, _GLADE_03, _GLADE_04, _GLADE_05, _GLADE_06],
791    },
792    {
793        "id": "caverns",
794        "name": "The Caverns",
795        "rooms": [_CAVERNS_01, _CAVERNS_02, _CAVERNS_03, _CAVERNS_04, _CAVERNS_05, _CAVERNS_06],
796    },
797    {
798        "id": "spire",
799        "name": "The Aether Spire",
800        "rooms": [_SPIRE_01, _SPIRE_02, _SPIRE_03, _SPIRE_04, _SPIRE_05, _SPIRE_06],
801    },
802]