goldbox.geo

GEO map geometry for Pool of Radiance (C64).

A GEO file is four 256-byte planes over a 16x16 grid, not an array of per-square records. Every square is indexed x + (y << 4) in each plane – row-major, y increasing southward, origin top-left.

$000 high nibble = wall art north, low nibble = wall art east $100 high nibble = wall art south, low nibble = wall art west $200 square attributes; bit 7 = roofed / indoor $300 passability, two bits per direction: N=0-1, E=2-3, S=4-5, W=6-7

A wall and a barrier are two independent fields. Five readings of GEO failed because they conflated them. Wall art says what to draw; the two-bit field says whether you can walk through, and the game consults it only when there is wall art on that edge – an edge with no art is passable whatever its bits say.

Confirmed against simeonpilgrim/coab, a C# reimplementation of Curse of the Azure Bonds reversed from the DOS overlays, and verified on our own 29 files: adjacent squares agree about their shared edge 13793 times in 13920 (0.991). reciprocity() recomputes that, so a mis-parsed file fails loudly.

On disk these are PRG files loading at $0400, so the payload is 1024 bytes after the two-byte load address.

Functions

load_geo_files(disk)

Every GEO file on one game disk, keyed by its PETSCII name.

Classes

Geo

One GEO file: a 16x16 grid of walls, doors and square attributes.

Exceptions

GeoError

A GEO payload that is not 1024 bytes.

class goldbox.geo.Geo[source]

Bases: object

One GEO file: a 16x16 grid of walls, doors and square attributes.

__init__(payload)[source]
Parameters:

payload (bytes | bytearray)

attributes(x, y)[source]
Parameters:
Return type:

int

barrier(x, y, direction)[source]

The raw two-bit field. Meaningless where there is no wall art.

Parameters:
Return type:

int

door(x, y, direction)[source]

PASSABLE, LOCKED or WIZARD_LOCKED for a door; None otherwise.

Parameters:
Return type:

int | None

classmethod from_bytes(data)[source]

Accept either the bare 1024 bytes or the PRG with its load address.

Parameters:

data (bytes | bytearray)

Return type:

goldbox.geo.Geo

classmethod from_disk(disk, name)[source]
Parameters:
Return type:

goldbox.geo.Geo

is_indoor(x, y)[source]
Parameters:
Return type:

bool

is_passable(x, y, direction)[source]

Can the party step this way?

No wall art means yes, whatever the bits hold – that is the engine’s own order of tests, and getting it backwards is what sank the earlier readings.

Parameters:
Return type:

bool

reciprocity()[source]

(agreements, edges) on the raw barrier field between neighbours.

Needs no ground truth: the east edge of a square is the west edge of its neighbour. Across all 29 files a correct parse scores 13793/13920 = 0.991; the readings that were tried and abandoned scored about 0.3. Run it before trusting anything else a file says.

It is deliberately the raw field and not is_passable. Wall art is only 0.960 reciprocal, so the two sides of an edge genuinely disagree about whether a wall is drawn there – and that drags passability to 0.958. Those are one-way edges, not parse errors, and folding them in would blunt the diagnostic.

Return type:

tuple[int, int]

script_id(x, y, mask=SCRIPT_ID)[source]

Which entry of the area’s ECL jump table this square runs, if any.

0 means no script. Pass the area’s own mask – DUNGEON_FLOOR_MASK for the dungeon-floor family, whose scripts use the two freed bits for encounter control instead.

Parameters:
Return type:

int

to_bytes()[source]
Return type:

bytes

to_text(mark=None)[source]

An ASCII floor plan.

--- a wall, -.- an opening, -+- a locked door, -*- wizard-locked; the same characters singly on vertical edges. mark puts a character in the middle of named squares, for the party or a route.

Parameters:

mark (dict[tuple[int, int], str] | None)

Return type:

str

walkable_route(squares)[source]

True if every consecutive pair in squares is one legal step.

Return type:

bool

wall(x, y, direction)[source]

The wall-art nibble on one edge. 0 means no wall.

Parameters:
Return type:

int

wallset(x, y, direction)[source]

(wallset, slice) into WALLDEF, or None where there is no wall.

Parameters:
Return type:

tuple[int, int] | None

exception goldbox.geo.GeoError[source]

Bases: ValueError

A GEO payload that is not 1024 bytes.

goldbox.geo.load_geo_files(disk)[source]

Every GEO file on one game disk, keyed by its PETSCII name.

Parameters:

disk (goldbox.d64.D64 | str)

Return type:

dict[str, goldbox.geo.Geo]