goldbox.areas¶
The areas of Pool of Radiance, and the names to put on a map.
This module is the single source of truth for the area table. It replaces the
hand-written dictionary that used to live in automap/state.py, and the
thirty-row table in docs/118-debug-mode.md is the same data written out.
The key is the area id, not the map file. An area is a script – ECL<id>,
with the id in hex, so area 21 is ECL15 – and the game’s own transition
opcode NEWECL takes exactly that id. The map is a consequence of the script,
not the other way round, and the relation is not a bijection:
thirty scripts, twenty-nine maps.
ECL0Cdoes not exist at all, so id 12 is absent from the table;three areas have no map.
ECL08(Phlan City Hall),ECL0B(the training hall) andECL13(Cave of Diogenes) issue noLOADFILESand never put aGEOon the screen.ECL1Ewas believed a fourth until#260 (Area 30 is recorded as having no map, and ECL1E loads GEO12): it carriesLOADFILES 18, 2, 255at$9A54, four statements after it writes the party’s square, and file 18 isGEO12, Podol Plaza’s own map;three areas carry two maps each.
ECL10,ECL18andECL1Deach load two `GEO`s from the one script;three areas are outdoors. Areas 25-27 load a
SQRDATAas well as aGEO;LOADFILESpicks the file type from$49E6, not from the operand.
So Area.geos is a tuple, which is empty for the three mapless areas and holds
two entries for the three doubled ones, and areas_for_geo returns a tuple as
well – there is nothing in the format that stops two scripts naming one map,
and ECL07 already loads GEO03 on its way into area 5. ECL1E is a second
example: its GEO12 is also area 18’s, Podol Plaza’s.
Names come from docs/88-map-files.md and two write-ups since lost,
work/reports/world-map.md and work/reports/quest-flags.md; arrival squares were harvested from the
departing scripts’ SAVE <n>, mapX and the arriving scripts’ entry 4. Fourteen
areas have no known arrival square and say so with arrival = None. FastTraveling
into all fifteen that had none at the time and watching where the party ends up
P20, whose write-up work/reports/p20-arrivals.md is lost: it found area 21’s
square in ECL15’s own bytecode, and landing_square at the foot of this module is the fallback that
measurement put in place of “the first square with a passable edge”.
A name is a title, so it is title-cased, leading article included: “The Slums”, not “the Slums”. The table used to mix the two – proper names in capitals, descriptions in lower case – and the seam showed in the dropdown, which is a list of titles and nothing else. Short function words inside a name stay down (“Valjevo Castle, a Floor”, “Temple of Bane”).
## Titles
GEO15 exists in both Pool of Radiance and Curse of the Azure Bonds and
means a different place in each (docs/120-curse-testing.md), so a table keyed
by map file alone cannot help lying. GEO_NAMES is therefore keyed by game
title first, and area_name degrades an unrecognised title – or an
unrecognised map – to "area 21", read straight off the file’s own hex
digits, rather than to a confident wrong answer.
AREAS_SILVER_BLADES is the second table, built for
#20 (Build an area table for Silver Blades) and read off that title’s own
six sides by tools/areatable.py. It shares the Area shape and nothing else:
sparse ids, no names, disk sides 1-6, and five areas whose map is not their own
id. Its own comment carries what does not carry over, and every row is
PROBABLE.
AREAS_CURSE is the third, measured for #192 (Convert a Curse of the Azure
Bonds DOS save into a C64 one, which the importer refuses today) step 0b by
the same tool. It carries no name and no arrival square – a conversion needs
neither – and every row is UNKNOWN, because unlike Silver Blades no Curse
area has been entered by a driven fast travel yet, only walked by the
bytecode.
Enumerating maps by count or assuming a GEO00 is wrong for every Gold Box
title after this one: Curse’s ids are sparse and chapter-grouped, and Silver
Blades, Champions and Death Knights start at $10 or $20
(write-up lost, work/reports/goldbox-inventory.md; the per-title base
addresses are asserted in
tests/test_curse.py::test_the_addresses_are_the_ones_measured). Scan a
directory; never a range.
Module Attributes
Game titles this module knows about. |
|
|
|
|
Facing, as the game stores it in |
The table. |
|
Secret of the Silver Blades, read off its own six sides by |
|
twenty-five scripts, on six sides ( |
|
Game title -> that title's areas. |
|
|
Game title -> where a party that has not set out begins. |
Game title -> map file -> the name to show. |
Functions
|
The area with this id in Pool of Radiance, or None -- id 12 has no script. |
|
The area with this id in this title, or None. |
|
A name for a map that is never a lie about another game. |
|
Every area of this title that anybody has written down. |
|
Every area that loads this map. |
|
Every area a fast travel may offer, which is nothing for four of six. |
|
The map's squares grouped by what can walk to what. |
|
The place this map is, in this game -- or None if we cannot say. |
|
|
|
The maps this area loads: two, one, or none. |
|
Every map any area of this title loads. |
|
|
|
Where to drop a party on a map that has no arrival square of its own. |
|
The row |
|
Where a party of this title that has not set out begins. |
Classes
One |
|
The square the game itself puts the party on when it enters an area. |
|
|
Where a party that has not begun adventuring goes when it does. |
- goldbox.areas.AREAS: tuple[goldbox.areas.Area, ...] = (Area(id=0, name='New Phlan', disk=3, geos=('GEO00',), sqrdata=None, arrival=Arrival(x=15, y=1, facing=3), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=1, name='Buccaneer Base', disk=6, geos=('GEO01',), sqrdata=None, arrival=Arrival(x=8, y=0, facing=2), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=2, name='Cadorna Textile House', disk=4, geos=('GEO02',), sqrdata=None, arrival=Arrival(x=0, y=4, facing=3), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=3, name='Valjevo Castle, a Floor', disk=5, geos=('GEO03',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.PROBABLE: 'PROBABLE'>, geo_names={}, dynamic_geo=True, fasttravelable=True, side_name='POOL{}'), Area(id=4, name='Valjevo Castle, a Floor', disk=5, geos=('GEO04',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.PROBABLE: 'PROBABLE'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=5, name='Valjevo Castle, a Floor', disk=5, geos=('GEO05',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.PROBABLE: 'PROBABLE'>, geo_names={}, dynamic_geo=True, fasttravelable=True, side_name='POOL{}'), Area(id=6, name='Valjevo Castle, a Floor', disk=5, geos=('GEO06',), sqrdata=None, arrival=Arrival(x=4, y=15, facing=0), overland=None, confidence=<Confidence.PROBABLE: 'PROBABLE'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=7, name='Valjevo Castle, the Pool', disk=5, geos=('GEO07',), sqrdata=None, arrival=Arrival(x=5, y=7, facing=None), overland=None, confidence=<Confidence.PROBABLE: 'PROBABLE'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=8, name='Phlan City Hall', disk=3, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=9, name='Stojanow Gate', disk=2, geos=('GEO09',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=10, name='Valhingen Graveyard', disk=4, geos=('GEO0A',), sqrdata=None, arrival=Arrival(x=0, y=4, facing=3), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=11, name='The Training Hall', disk=3, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=13, name='The Kobold Caves', disk=8, geos=('GEO0D',), sqrdata=None, arrival=Arrival(x=6, y=15, facing=0), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=14, name='Kovel Mansion', disk=3, geos=('GEO0E',), sqrdata=None, arrival=Arrival(x=4, y=0, facing=2), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=15, name="Mendor's Library", disk=2, geos=('GEO0F',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=16, name='The Lizardman Keep', disk=8, geos=('GEO10', 'GEO1E'), sqrdata=None, arrival=Arrival(x=8, y=14, facing=0), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=17, name='The Nomad Camp', disk=7, geos=('GEO11',), sqrdata=None, arrival=Arrival(x=1, y=14, facing=1), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=18, name='Podol Plaza', disk=1, geos=('GEO12',), sqrdata=None, arrival=Arrival(x=0, y=4, facing=3), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=19, name='Cave of Diogenes', disk=6, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=20, name='The Slums', disk=2, geos=('GEO14',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=21, name='Sokol Keep', disk=4, geos=('GEO15',), sqrdata=None, arrival=Arrival(x=8, y=14, facing=0), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=22, name="Yarash's Pyramid", disk=7, geos=('GEO16',), sqrdata=None, arrival=Arrival(x=15, y=7, facing=1), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=23, name="Yarash's Pyramid, Lower", disk=7, geos=('GEO17',), sqrdata=None, arrival=Arrival(x=15, y=0, facing=2), overland=None, confidence=<Confidence.PROBABLE: 'PROBABLE'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=24, name='Temple of Bane', disk=1, geos=('GEO18', 'GEO1F'), sqrdata=None, arrival=Arrival(x=15, y=4, facing=3), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=25, name='Wilderness, West Window', disk=6, geos=('GEO19',), sqrdata='SQRDATA04', arrival=None, overland=(14, 29), confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=26, name='Wilderness, Middle Window', disk=7, geos=('GEO1A',), sqrdata='SQRDATA05', arrival=None, overland=(7, 29), confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=27, name='Wilderness, East Window', disk=8, geos=('GEO1B',), sqrdata='SQRDATA06', arrival=None, overland=(9, 29), confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=28, name='Zhentil Keep Outpost', disk=6, geos=('GEO1C',), sqrdata=None, arrival=Arrival(x=7, y=0, facing=2), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=29, name="Kuto's Well", disk=8, geos=('GEO1D', 'GEO20'), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names=mappingproxy({'GEO20': "Kuto's Well Catacombs"}), dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=30, name=None, disk=1, geos=('GEO12',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=False, side_name='POOL{}'))¶
The table. Ordered by id, with 12 absent because
ECL0Cis absent.The five POOL5 floors are a castle because
ECL07writes ledger flag 20 and prints the party leaving one; which floor is which is not known, which is why they are PROBABLE and why four of them share a name.
- goldbox.areas.AREAS_CURSE: tuple[goldbox.areas.Area, ...] = (Area(id=1, name=None, disk=2, geos=('GEO01',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=2, name=None, disk=2, geos=('GEO01',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=3, name=None, disk=2, geos=('GEO03',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=4, name=None, disk=2, geos=('GEO04',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=16, name=None, disk=3, geos=('GEO10',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=17, name=None, disk=3, geos=('GEO11',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=18, name=None, disk=3, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=21, name=None, disk=3, geos=('GEO15',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=30, name=None, disk=1, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=32, name=None, disk=4, geos=('GEO20',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=33, name=None, disk=4, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=34, name=None, disk=4, geos=('GEO21',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=35, name=None, disk=4, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=37, name=None, disk=4, geos=('GEO25',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=48, name=None, disk=5, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=49, name=None, disk=5, geos=('GEO32',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=50, name=None, disk=5, geos=('GEO32',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=51, name=None, disk=5, geos=('GEO33',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=53, name=None, disk=5, geos=('GEO35',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=64, name=None, disk=6, geos=('GEO40',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=66, name=None, disk=6, geos=('GEO42',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=67, name=None, disk=6, geos=('GEO43',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=69, name=None, disk=6, geos=('GEO45',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=80, name=None, disk=1, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=81, name=None, disk=1, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'))¶
twenty-five scripts, on six sides (
CURSE_A.D64-CURSE_F.D64),ECL64/ECL65excluded the same way Silver Blades excludes its two – they carry noLOADFILESand are not places. Measured for#192 (Convert a Curse of the Azure Bonds DOS save into a C64 one, which the importer refuses today)step 0b bytools/areatable.py curse-of-the-azure-bonds, which walks every script’s control flow from its five entryGOTO`s and reads the `LOADFILESoperands, corroborated by the loader’s own disk byte$7F12agreeing with the target script’s side on all 23 staticNEWECL`s it precedes, and re-checked directly off the disks by `tools/cursedisk.py --check-areason 2026-09-05 with 0 disagreements against this table.confidenceis UNKNOWN for every row, unlike Silver Blades’ table: no Curse area has been entered by a driven fast travel, only walked by the bytecode. Thegeoscolumn is the strongest part of it regardless –ECL02loadsGEO01,ECL22loadsGEO21andECL31/ECL32both loadGEO32, so it cannot be derived from the id the way a straight numbering could be.There is no area 0, and the table is not missing it. A DOS Curse save whose area word is 0 is one the player made from the party-formation menu before pressing
BEGIN ADVENTURING, and 0 is the initialiser’s value rather than a place: noECL00is on any of the six sides and noECLcontainer holds a block 0, and the same goes forGEO00. Adding a row would movegoldbox.dos’s refusal fromarea_into_resident_georather than remove it, and a C64 save naming area 0 sends the loader afterGEO00– measured,$B7/$BBat the disk prompt – which it can never find.#301 (A DOS Curse save standing in area 0 is refused by the import, because no row of the area table names area 0)anddocs/185-a-party-that-has-not-set-out.mdhave the measurements.- Type:
Curse of the Azure Bonds
- goldbox.areas.AREAS_SILVER_BLADES: tuple[goldbox.areas.Area, ...] = (Area(id=4, name=None, disk=1, geos=('GEO10',), sqrdata=None, arrival=Arrival(x=10, y=8, facing=1), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=16, name=None, disk=1, geos=('GEO10',), sqrdata=None, arrival=Arrival(x=15, y=8, facing=3), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=17, name=None, disk=1, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=32, name=None, disk=2, geos=('GEO20',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=33, name=None, disk=2, geos=('GEO21',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=34, name=None, disk=2, geos=('GEO22',), sqrdata=None, arrival=Arrival(x=14, y=14, facing=0), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=48, name=None, disk=3, geos=('GEO31', 'GEO30'), sqrdata=None, arrival=Arrival(x=3, y=3, facing=1), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=49, name=None, disk=3, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=True, fasttravelable=True, side_name='SILVER-{}'), Area(id=50, name=None, disk=3, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=True, fasttravelable=True, side_name='SILVER-{}'), Area(id=51, name=None, disk=3, geos=('GEO31',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=52, name=None, disk=3, geos=('GEO32',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=64, name=None, disk=4, geos=('GEO40',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=65, name=None, disk=4, geos=('GEO41',), sqrdata=None, arrival=Arrival(x=13, y=9, facing=1), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=66, name=None, disk=4, geos=('GEO42',), sqrdata=None, arrival=Arrival(x=12, y=13, facing=0), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=68, name=None, disk=4, geos=('GEO44',), sqrdata=None, arrival=Arrival(x=7, y=15, facing=0), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=80, name=None, disk=5, geos=('GEO50',), sqrdata=None, arrival=Arrival(x=1, y=11, facing=1), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=81, name=None, disk=5, geos=('GEO51',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=82, name=None, disk=5, geos=('GEO52',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=96, name=None, disk=6, geos=('GEO60',), sqrdata=None, arrival=Arrival(x=15, y=0, facing=3), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=97, name=None, disk=6, geos=('GEO61',), sqrdata=None, arrival=Arrival(x=15, y=0, facing=3), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=98, name=None, disk=6, geos=('GEO62',), sqrdata=None, arrival=Arrival(x=0, y=15, facing=1), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=99, name=None, disk=6, geos=('GEO62',), sqrdata=None, arrival=Arrival(x=0, y=0, facing=2), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'))¶
Secret of the Silver Blades, read off its own six sides by
tools/areatable.py(#20 (Build an area table for Silver Blades)). Twenty-two scripts, seventeen maps,ECL64andECL65excluded because their first four bytes do not decode as theGOTOan area script opens with – they are on every side and are the machine, not a place.Every row is CONFIRMED, and each one the same way: a party has been put in that area on a running machine and the landing measured. Twenty-one were entered by a trip through
automap.actions.FastTravel– 22 hops across nine driven sessions – and$11is where a loaded party starts, so it was read where it stood.#20 (Build an area table for Silver Blades),work/issue20/land1-land9.The map column is 21 of 21 exact, an unmasked 1024-byte compare of
$0400against the copy on the player’s own disk, no fingerprint and no filename. That includes all five rows where an area’s map is not its own id –$04and$10both loadedGEO10,$30and$33bothGEO31,$34GEO32,$63GEO62– and$11, where a sweep of all of RAM found none of the seventeen maps anywhere, which is what a row with nogeosclaims.The side column was confirmed by the game asking for it. This loader letters its sides:
INSERT SIDE AthroughINSERT SIDE Ffor sides 1 to 6. Six letters, six sides, each drawn when a party was sent to an area this table puts on that side, each answered by attaching it.`confidence` grades the id, the side and the map, and not `arrival`. Twelve arrivals into a row that carries a square have been measured, with
(1, 1, 2)– a square no row carries – written into$C04Bfirst, so that a table square read back afterwards could only be the arriving script’s own doing. Five reproduce the static reading, six land somewhere else, and one writes no square at all:area
came from
this table
where the party actually landed
$30$113,3 E
3,3 E
$41$6013,9 E
13,9 E
$44$607,15 N
7,15 N
$50$111,11 E
1,11 E
$62$110,15 E
0,15 E
$10$1115,8 W
3,3 S
$42$1112,13 N
0,7 W
$60$5015,0 W
0,7 E
$60$4215,0 W
15,9 W
$61$6315,0 W
0,15 E
$04$5210,8 E
9,8 W
$63$620,0 S
nothing written
Four rows with no
arrivalat all placed the party anyway, from the one came-from each was entered with:$20at 10,14 W from$21,$21at 15,9 N from$61,$40at 15,7 W from$44and$34at 4,0 S from$11. They are recorded here rather than in the rows, because one measurement from one came-from is exactly the evidence$60below shows to be insufficient. Four more wrote nothing and left the deliberately wrong square standing:$31,$32,$33and$51.$60twice, from two different areas, landing in two different places, is the proof rather than an argument: an arrival square here can depend on `$4BF2`, and a fast travel hands the arriving script a came-from it may have no branch for.ECL34andECL51were already known to branch that way when the table was built;ECL10,ECL42,ECL60andECL61do it too andtools/areatable.pyrecorded the constant on the branch it happened to walk.ECL63writes no square at all on this path: the deliberately wrong square survived it untouched.None of that makes the field harmful – the arriving script wins wherever it writes one – but it is not a promise about where a fast-travelled party ends up. The disk column is the strongest part of the table: the side is where the file sits in the directory, 29 of 29 static
SAVE <n>, [$7F12]before aNEWECLname that same side with 0 disagreeing, and the loader has now asked for six of them out loud.Four things do not carry over from Pool of Radiance, and each is a trap for anything that reads this table expecting the older shape.
The ids are sparse and blocked by side.
$04,$10-$11,$20-$22,$30-$34,$40-$42,$44,$50-$52,$60-$63. The high nibble is the side for twenty-one of the twenty-two, andECL04is the exception: it sits on side 1. Never enumerate by range.An area’s map is not its own id in the `$3x` block.
ECL30loadsGEO31,ECL33loadsGEO31as well,ECL34loadsGEO32,ECL04loadsGEO10andECL63loadsGEO62. Pool of Radiance’s rule – every script’sLOADFILESfirst operand is its own id – is false here in five of the twenty-two.The arriving script places the party, not the departing one. All twelve squares here come from the area’s own entry 4; exactly one
NEWECLin the whole title writes a square before it (ECL44sends the party to area$40at 7,15 N), and that one contradictsECL40’s own entry 4, so area$40gets no square. Pool of Radiance harvested most of its sixteen the other way round.A square is often computed rather than constant.
ECL21reads its throughGETTABLEindexed by a variable,ECL34andECL51branch on the came-from area$4BF2, andECL34adds 3 to whatever$C04Balready holds. Nine rows have noarrivalfor that reason, and writing one for them would be inventing it.
- class goldbox.areas.Area[source]¶
Bases:
objectOne
ECLscript, and whatever map or maps it loads.- __init__(id, name, disk, geos=(), sqrdata=None, arrival=None, overland=None, confidence=Confidence.UNKNOWN, geo_names=<factory>, dynamic_geo=False, fasttravelable=True, side_name='POOL{}')¶
- Parameters:
- Return type:
None
- arrival: goldbox.areas.Arrival | None = None¶
- confidence: goldbox.layout.Confidence = 'UNKNOWN'¶
What this grades is the `name`, and only the name. Every PROBABLE row is one whose name is a guess – the five Valjevo Castle floors and lower Yarash’s Pyramid – and the one UNKNOWN is area 30, which has no name because it is not a place: it is the attract-mode demo. A row’s other facts are graded where they are stated, in the comment beside it, which is why area 30 reads UNKNOWN here while its
ECL, its disk, itsGEOand its refusal to be fasttraveled are all CONFIRMED.Said here because
docs/118-debug-mode.md’s own table carries the same column and had graded area 30 CONFIRMED, against this file’s UNKNOWN, since before either said what the column was for. Settled 2026-09-07 at Donald’s asking: the two now agree, and both say what they are grading.
- disk: int¶
Which disk side carries the script – 1-8 in Pool of Radiance, 1-6 in Secret of the Silver Blades. This is what a fasttravel writes to the loader’s disk byte (
$6E12in Pool of Radiance,$7F12in Silver Blades) and what the loader will prompt for.
- dynamic_geo: bool = False¶
do not pick a square off
geos[0].** Two different mechanisms set it, one per title.Pool of Radiance areas 3 and 5 issue no static
LOADFILESat all and choose their map at run time withGETTABLE ..., mapDir; theirgeosentry is the doc’s inference from the id, not something the bytecode says. Silver Blades areas$31and$32issue none either, and their map is loaded byECL30, the script that sends the party to them –geosis empty there rather than a guess.And the inference is wrong for both. FastTraveled into, area 3 loaded
GEO05and area 5 loadedGEO04–$6E15and the bytes at$0400agreeing (write-up lost,work/reports/p20-arrivals.md). So a square chosen offgeos[0]is a square off a map the game was never going to show, and a caller with no arrival square should write none for these two and let the arriving script place the party, which is what it does.- Type:
**
geosmay be incomplete
- fasttravelable: bool = True¶
Whether a debug fasttravel may enter this area at all. False for
ECL1Ealone: it is the attract-mode demo, and fasttraveling there ends the session.$C04B-$C04Dread254, 127, 16, noGEOis resident, no status line and no command bar appear, and the program counter never returns toDUNGEON’s key-wait loop, so no later fasttravel can be started (write-up lost,work/reports/p20-arrivals.md).
- property geo: str | None¶
The one map, where there is exactly one. None for none and for two.
Callers that cannot cope with an area having two maps should be reading
geosand deciding what to do, not silently taking the first.
- geo_names: Mapping[str, str]¶
Names for individual maps of a two-map area, where the second map is a place in its own right – area 29’s
GEO20is the catacombs under Kuto’s Well, not Kuto’s Well.
- geos: tuple[str, ...] = ()¶
The
GEOfiles the script statically loads, in the order it loads them. Empty for the three mapless Pool of Radiance areas, and for the two Silver Blades areas whose map is loaded by the script that sends the party to them.
- name: str | None¶
None for
ECL1E, which has noNEWECLpointing at it and no name anybody has been able to attach to it, though it does loadGEO12(#260 (Area 30 is recorded as having no map, and ECL1E loads GEO12)).
- overland: tuple[int, int] | None = None¶
Where a fast travel puts the party on the travel grid, window-local (x, y), for areas 25-27 only – written to
$49C3/$49C4.arrivalis theGEOsquare in$C04B, and stays None for these three:$C04Bis notGDRIVE00’s square outdoors, so a caller must never write one there for a window.newecl_writesinautomap/actions.pywrites this field instead (#178 (Fast Travel to the wilderness leaves the party on whatever overland square it last stood on)).`confidence` below does not grade this field. It is one grade for the row and it was written for the area itself, so a row could read CONFIRMED while its
overlandwas still inferred. All three are now CONFIRMED in the running game – a party arrived on each square and walked off it – but the two gradings remain separate things, and the per-square evidence is in the comment beside the rows. Anything that ever shows a confidence to a user must not read this field’s trustworthiness offconfidence; raised in the code review of #178 on 2026-09-02, when nothing displayed it yet and the trap was one dropdown column away.
- class goldbox.areas.Arrival[source]¶
Bases:
objectThe square the game itself puts the party on when it enters an area.
facingis None where the departing script sets the square but not the direction – area 7 is the one such case.
- class goldbox.areas.Confidence[source]¶
Bases:
enum.EnumHow much we trust a field definition.
- CONFIRMED = 'CONFIRMED'¶
- GUESS = 'GUESS'¶
- PROBABLE = 'PROBABLE'¶
- UNKNOWN = 'UNKNOWN'¶
- goldbox.areas.GEO_NAMES: Mapping[str, Mapping[str, str]] = mappingproxy({'Pool of Radiance': mappingproxy({'GEO00': 'New Phlan', 'GEO01': 'Buccaneer Base', 'GEO02': 'Cadorna Textile House', 'GEO03': 'Valjevo Castle, a Floor', 'GEO04': 'Valjevo Castle, a Floor', 'GEO05': 'Valjevo Castle, a Floor', 'GEO06': 'Valjevo Castle, a Floor', 'GEO07': 'Valjevo Castle, the Pool', 'GEO09': 'Stojanow Gate', 'GEO0A': 'Valhingen Graveyard', 'GEO0D': 'The Kobold Caves', 'GEO0E': 'Kovel Mansion', 'GEO0F': "Mendor's Library", 'GEO10': 'The Lizardman Keep', 'GEO1E': 'The Lizardman Keep', 'GEO11': 'The Nomad Camp', 'GEO12': 'Podol Plaza', 'GEO14': 'The Slums', 'GEO15': 'Sokol Keep', 'GEO16': "Yarash's Pyramid", 'GEO17': "Yarash's Pyramid, Lower", 'GEO18': 'Temple of Bane', 'GEO1F': 'Temple of Bane', 'GEO19': 'Wilderness, West Window', 'GEO1A': 'Wilderness, Middle Window', 'GEO1B': 'Wilderness, East Window', 'GEO1C': 'Zhentil Keep Outpost', 'GEO1D': "Kuto's Well", 'GEO20': "Kuto's Well Catacombs"}), 'Curse of the Azure Bonds': mappingproxy({}), 'Secret of the Silver Blades': mappingproxy({})})¶
Game title -> map file -> the name to show. Curse and Silver Blades are present and empty: their maps are not named anywhere yet – Silver Blades’ twenty-two areas are decoded down to the map and the disk side and not one of them has a name – and an empty table degrades to
"area 21"where a missing title would degrade to the same thing. Listing them is the difference between “we know we do not know” and “we never looked”.
- goldbox.areas.MISSING_ID = 12¶
ECL0Cis not on any of the nine disks, so there is no area 12. Kept as a named constant because “the ids are 0-30 with one hole” is a fact about the game that a caller iterating a range would otherwise have to rediscover.
- goldbox.areas.POOL_OF_RADIANCE = 'Pool of Radiance'¶
Game titles this module knows about. Plain strings on purpose, and the seam with
goldbox/games.py: these areGame.title, so a caller holding a descriptor writesareas.area_name(geo, game.title)and neither module imports the other.tests/test_areas.pypins the two spellings together.
- goldbox.areas.TABLES: Mapping[str, tuple[goldbox.areas.Area, ...]] = mappingproxy({'Pool of Radiance': (Area(id=0, name='New Phlan', disk=3, geos=('GEO00',), sqrdata=None, arrival=Arrival(x=15, y=1, facing=3), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=1, name='Buccaneer Base', disk=6, geos=('GEO01',), sqrdata=None, arrival=Arrival(x=8, y=0, facing=2), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=2, name='Cadorna Textile House', disk=4, geos=('GEO02',), sqrdata=None, arrival=Arrival(x=0, y=4, facing=3), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=3, name='Valjevo Castle, a Floor', disk=5, geos=('GEO03',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.PROBABLE: 'PROBABLE'>, geo_names={}, dynamic_geo=True, fasttravelable=True, side_name='POOL{}'), Area(id=4, name='Valjevo Castle, a Floor', disk=5, geos=('GEO04',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.PROBABLE: 'PROBABLE'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=5, name='Valjevo Castle, a Floor', disk=5, geos=('GEO05',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.PROBABLE: 'PROBABLE'>, geo_names={}, dynamic_geo=True, fasttravelable=True, side_name='POOL{}'), Area(id=6, name='Valjevo Castle, a Floor', disk=5, geos=('GEO06',), sqrdata=None, arrival=Arrival(x=4, y=15, facing=0), overland=None, confidence=<Confidence.PROBABLE: 'PROBABLE'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=7, name='Valjevo Castle, the Pool', disk=5, geos=('GEO07',), sqrdata=None, arrival=Arrival(x=5, y=7, facing=None), overland=None, confidence=<Confidence.PROBABLE: 'PROBABLE'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=8, name='Phlan City Hall', disk=3, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=9, name='Stojanow Gate', disk=2, geos=('GEO09',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=10, name='Valhingen Graveyard', disk=4, geos=('GEO0A',), sqrdata=None, arrival=Arrival(x=0, y=4, facing=3), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=11, name='The Training Hall', disk=3, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=13, name='The Kobold Caves', disk=8, geos=('GEO0D',), sqrdata=None, arrival=Arrival(x=6, y=15, facing=0), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=14, name='Kovel Mansion', disk=3, geos=('GEO0E',), sqrdata=None, arrival=Arrival(x=4, y=0, facing=2), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=15, name="Mendor's Library", disk=2, geos=('GEO0F',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=16, name='The Lizardman Keep', disk=8, geos=('GEO10', 'GEO1E'), sqrdata=None, arrival=Arrival(x=8, y=14, facing=0), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=17, name='The Nomad Camp', disk=7, geos=('GEO11',), sqrdata=None, arrival=Arrival(x=1, y=14, facing=1), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=18, name='Podol Plaza', disk=1, geos=('GEO12',), sqrdata=None, arrival=Arrival(x=0, y=4, facing=3), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=19, name='Cave of Diogenes', disk=6, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=20, name='The Slums', disk=2, geos=('GEO14',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=21, name='Sokol Keep', disk=4, geos=('GEO15',), sqrdata=None, arrival=Arrival(x=8, y=14, facing=0), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=22, name="Yarash's Pyramid", disk=7, geos=('GEO16',), sqrdata=None, arrival=Arrival(x=15, y=7, facing=1), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=23, name="Yarash's Pyramid, Lower", disk=7, geos=('GEO17',), sqrdata=None, arrival=Arrival(x=15, y=0, facing=2), overland=None, confidence=<Confidence.PROBABLE: 'PROBABLE'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=24, name='Temple of Bane', disk=1, geos=('GEO18', 'GEO1F'), sqrdata=None, arrival=Arrival(x=15, y=4, facing=3), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=25, name='Wilderness, West Window', disk=6, geos=('GEO19',), sqrdata='SQRDATA04', arrival=None, overland=(14, 29), confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=26, name='Wilderness, Middle Window', disk=7, geos=('GEO1A',), sqrdata='SQRDATA05', arrival=None, overland=(7, 29), confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=27, name='Wilderness, East Window', disk=8, geos=('GEO1B',), sqrdata='SQRDATA06', arrival=None, overland=(9, 29), confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=28, name='Zhentil Keep Outpost', disk=6, geos=('GEO1C',), sqrdata=None, arrival=Arrival(x=7, y=0, facing=2), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=29, name="Kuto's Well", disk=8, geos=('GEO1D', 'GEO20'), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names=mappingproxy({'GEO20': "Kuto's Well Catacombs"}), dynamic_geo=False, fasttravelable=True, side_name='POOL{}'), Area(id=30, name=None, disk=1, geos=('GEO12',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=False, side_name='POOL{}')), 'Curse of the Azure Bonds': (Area(id=1, name=None, disk=2, geos=('GEO01',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=2, name=None, disk=2, geos=('GEO01',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=3, name=None, disk=2, geos=('GEO03',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=4, name=None, disk=2, geos=('GEO04',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=16, name=None, disk=3, geos=('GEO10',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=17, name=None, disk=3, geos=('GEO11',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=18, name=None, disk=3, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=21, name=None, disk=3, geos=('GEO15',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=30, name=None, disk=1, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=32, name=None, disk=4, geos=('GEO20',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=33, name=None, disk=4, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=34, name=None, disk=4, geos=('GEO21',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=35, name=None, disk=4, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=37, name=None, disk=4, geos=('GEO25',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=48, name=None, disk=5, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=49, name=None, disk=5, geos=('GEO32',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=50, name=None, disk=5, geos=('GEO32',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=51, name=None, disk=5, geos=('GEO33',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=53, name=None, disk=5, geos=('GEO35',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=64, name=None, disk=6, geos=('GEO40',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=66, name=None, disk=6, geos=('GEO42',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=67, name=None, disk=6, geos=('GEO43',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=69, name=None, disk=6, geos=('GEO45',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=80, name=None, disk=1, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}'), Area(id=81, name=None, disk=1, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.UNKNOWN: 'UNKNOWN'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='CURSE_{}')), 'Secret of the Silver Blades': (Area(id=4, name=None, disk=1, geos=('GEO10',), sqrdata=None, arrival=Arrival(x=10, y=8, facing=1), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=16, name=None, disk=1, geos=('GEO10',), sqrdata=None, arrival=Arrival(x=15, y=8, facing=3), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=17, name=None, disk=1, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=32, name=None, disk=2, geos=('GEO20',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=33, name=None, disk=2, geos=('GEO21',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=34, name=None, disk=2, geos=('GEO22',), sqrdata=None, arrival=Arrival(x=14, y=14, facing=0), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=48, name=None, disk=3, geos=('GEO31', 'GEO30'), sqrdata=None, arrival=Arrival(x=3, y=3, facing=1), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=49, name=None, disk=3, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=True, fasttravelable=True, side_name='SILVER-{}'), Area(id=50, name=None, disk=3, geos=(), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=True, fasttravelable=True, side_name='SILVER-{}'), Area(id=51, name=None, disk=3, geos=('GEO31',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=52, name=None, disk=3, geos=('GEO32',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=64, name=None, disk=4, geos=('GEO40',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=65, name=None, disk=4, geos=('GEO41',), sqrdata=None, arrival=Arrival(x=13, y=9, facing=1), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=66, name=None, disk=4, geos=('GEO42',), sqrdata=None, arrival=Arrival(x=12, y=13, facing=0), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=68, name=None, disk=4, geos=('GEO44',), sqrdata=None, arrival=Arrival(x=7, y=15, facing=0), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=80, name=None, disk=5, geos=('GEO50',), sqrdata=None, arrival=Arrival(x=1, y=11, facing=1), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=81, name=None, disk=5, geos=('GEO51',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=82, name=None, disk=5, geos=('GEO52',), sqrdata=None, arrival=None, overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=96, name=None, disk=6, geos=('GEO60',), sqrdata=None, arrival=Arrival(x=15, y=0, facing=3), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=97, name=None, disk=6, geos=('GEO61',), sqrdata=None, arrival=Arrival(x=15, y=0, facing=3), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=98, name=None, disk=6, geos=('GEO62',), sqrdata=None, arrival=Arrival(x=0, y=15, facing=1), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'), Area(id=99, name=None, disk=6, geos=('GEO62',), sqrdata=None, arrival=Arrival(x=0, y=0, facing=2), overland=None, confidence=<Confidence.CONFIRMED: 'CONFIRMED'>, geo_names={}, dynamic_geo=False, fasttravelable=True, side_name='SILVER-{}'))})¶
Game title -> that title’s areas.
- goldbox.areas.area(id)[source]¶
The area with this id in Pool of Radiance, or None – id 12 has no script.
- Parameters:
id (int)
- Return type:
goldbox.areas.Area | None
- goldbox.areas.area_in(id, title)[source]¶
The area with this id in this title, or None.
areaabove is Pool of Radiance’s, and an id means a different place in each game:$21is Sokol Keep there and a Silver Blades area on side 2 here. A caller holding a title must ask this one.- Parameters:
- Return type:
goldbox.areas.Area | None
- goldbox.areas.area_name(geo, title=POOL_OF_RADIANCE)[source]¶
A name for a map that is never a lie about another game.
GEO15is Sokol Keep in Pool of Radiance and somewhere else entirely in Curse of the Azure Bonds. With no title, or a title with no table, this falls back to the file’s own number –"area 21"– which says exactly as much as we know.
- goldbox.areas.areas_for(title)[source]¶
Every area of this title that anybody has written down.
This is the knowledge, not the permission. It answers with Silver Blades’ twenty-two rows;
areas_for_titlebelow is the one a fast travel asks, and it still refuses them. Use this for labelling a map, for a report, or for anything that only reads.- Parameters:
title (str | None)
- Return type:
- goldbox.areas.areas_for_geo(geo)[source]¶
Every area that loads this map. Usually one; never assume it.
- Parameters:
geo (str)
- Return type:
- goldbox.areas.areas_for_title(title)[source]¶
Every area a fast travel may offer, which is nothing for four of six.
`AREAS` is Pool of Radiance’s and only Pool of Radiance’s. Every row carries a
POOLdisk number and anECLid, and neither means anything in another title – Curse’s disks are not numbered like Pool of Radiance’s and itsECLids have never been decoded (docs/138-multiple-games.md§§2, 6). A caller that is about to write one of those numbers into a running machine must ask this rather than readingAREAS, and must offer nothing when it comes back empty. Falling back to Pool of Radiance’s list is the one answer that corrupts.Silver Blades is offered now, and it was not before. Two things had to be true and both are.
#15 (Fast Travel for more than one Gold Box title)moved the addresses off Pool of Radiance’s, soautomap/actions.pywrites$7F12,$7F1B,$4BF2, the wipe at$4C00, the sixth write at$4BFBand the tail$210Cwhen the title is this one. And a party has been fast-travelled into fourteen of these twenty-two areas on a running machine, each landing checked by an exact 1024-byte compare of$0400against the map on the player’s own disk, 15 hops and 15 matches (#20 (Build an area table for Silver Blades)). Offering rows nobody had driven was what the PROBABLE grade on the whole table was warning about, and that is what stopped being true.Curse is offered too, now that its table exists.
#192 (Convert a Curse of the Azure Bonds DOS save into a C64 one, which the importer refuses today)step 0b builtAREAS_CURSEoff the bytecode, andautomap/fasttravel.py’s addresses for it were already CONFIRMED by four driven warps (#19) before this table existed – so both ofautomap.actions.area_rows’s gates are open. Unlike Silver Blades, no individual Curse row has been landed on by a driven fast travel: every row’sconfidenceis UNKNOWN, and a caller reading that field before trusting a row – which nothing here does yet – would need to know that.areas_forabove is the accessor for everything that only reads, and answers for every title that has a table at all.- Parameters:
title (str | None)
- Return type:
- goldbox.areas.components(geo)[source]¶
The map’s squares grouped by what can walk to what.
A
GEOis not one room.GEO19breaks into 42 pieces,GEO0Dinto 21, and a square picked without regard to which piece it is in can be a place the party cannot walk out of.
- goldbox.areas.geo_name(geo, title=POOL_OF_RADIANCE)[source]¶
The place this map is, in this game – or None if we cannot say.
- goldbox.areas.geos_in(title)[source]¶
Every map any area of this title loads.
The set a
GEOnumber read out of a saved game is checked against, so that a number nothing could have loaded is refused rather than written into a converted save. It is the union of the rows and not a range:GEO0Cis in no row (areas_for_geo("GEO0C")is empty) and 12 is inside any range anybody would write.The set can be short of what a title’s disks hold, because two Pool of Radiance rows are
dynamic_geo– their script chooses its map at run time and theirgeosentry is the doc’s inference from the id, which is known to be wrong for both. If this ever refuses a save the game itself wrote, the row is what is incomplete.
- goldbox.areas.landing_square(geo)[source]¶
Where to drop a party on a map that has no arrival square of its own.
What `FastTravel` uses, in place of the old rule – the first square with any passable edge at all, which therefore took
(0, 0)on every one of the twenty-nine maps (write-up lost,work/reports/p20-arrivals.md; the pocket sizes are asserted intests/test_p20.py’sPOCKETS). That was legal in the narrow sense on most maps and wrong on four:(0, 0)is in a pocket of 32 squares inGEO05, 30 inGEO19, 16 inGEO1Aand 48 inGEO1B, cut off from the rest of the map, and a party fasttraveled there can walk but cannot get out.So: the largest connected component, and within it a square off the outer ring where one exists, facing a passable edge. Returns None for a map with no passable edge anywhere, which is also what an area with no map gets.
Two kinds of area the caller must not call this for at all, because the answer would be meaningless rather than merely imperfect: the three overland areas, where the position is
$49C3/$49C4, not aGEOsquare –Area.overlandcarries it andFastTravelwrites it directly (#178 (Fast Travel to the wilderness leaves the party on whatever overland square it last stood on); this used to say every arriving script writes[$4A18]/[$4A19], “the world-map cell” – those bytes are scratch, and the only writers copy an indoor square into$C04Bon the way into a window’s own cave, corrected while fixing #178); and the two `dynamic_geo` areas, which load a mapgeosdoes not name.