Skip to content

osrlib.core.treasure

Treasure tables and generation: types A–V, gems, jewellery, and magic item rolls.

The treasure tables — treasure types A through V, the gem and jewellery value tables, the master Magic Item Type table, the room-contents stocking table, and the unguarded-treasure bands — compile from the OSE SRD's treasure pages into treasure.json and load as frozen models via load_treasure_tables. Generation starts at generate_treasure for a treasure type's full contents, at generate_unguarded_treasure for an unguarded cache, or at generate_magic_item for a single magic item rolled à la carte.

Every treasure-type entry parses on one fixed grammar: an optional NN%: presence gate, then a coin quantity (dice with an optional × K multiplier folded into the dice expression and a glued denomination suffix), a gem or jewellery count, or a structured magic-item clause. Magic clauses are never free text — MagicAllotment carries the any/category/pool kinds, exclusions, and fixed or diced counts.

Generation draws on the TREASURE_STREAM stream in printed-entry order: presence roll, then quantity dice, then per-item resolution depth-first, so results are reproducible from the stream alone.

TREASURE_STREAM module-attribute

TREASURE_STREAM = 'treasure'

Stream key convention for treasure-generation draws: presence, quantities, items.

CoinDenomination

Bases: StrEnum

The five coin denominations, as the treasure tables print them.

PP class-attribute instance-attribute

PP = 'pp'

GP class-attribute instance-attribute

GP = 'gp'

EP class-attribute instance-attribute

EP = 'ep'

SP class-attribute instance-attribute

SP = 'sp'

CP class-attribute instance-attribute

CP = 'cp'

CoinQuantity

Bases: BaseModel

A coin entry's quantity: the dice (multiplier folded in) and the denomination.

denomination instance-attribute

denomination: CoinDenomination

dice instance-attribute

dice: str

GemValueBand

Bases: BaseModel

One d20 band of the gem value table.

roll_min class-attribute instance-attribute

roll_min: int = Field(ge=1, le=20)

roll_max class-attribute instance-attribute

roll_max: int = Field(ge=1, le=20)

value_gp class-attribute instance-attribute

value_gp: int = Field(ge=1)

GemValueTable

Bases: BaseModel

The d20 gem value table plus the jewellery dice.

manual_notes keeps the referee-discretion prose (damaged jewellery −50%, the combining-values option): osrlib never reduces a jewellery value for damage, and combining values is a presentation choice left to the game.

bands class-attribute instance-attribute

bands: tuple[GemValueBand, ...] = Field(min_length=1)

jewellery_dice instance-attribute

jewellery_dice: str

manual_notes class-attribute instance-attribute

manual_notes: tuple[str, ...] = ()

value_for_roll

value_for_roll(roll: int) -> int

Return the gem value for a d20 roll.

Parameters:

Name Type Description Default
roll int

The d20 result, 1–20.

required

Returns:

Type Description
int

The gem value in gold pieces.

Raises:

Type Description
ValueError

If the roll is outside 1–20.

MagicAllotment

Bases: BaseModel

One structured magic-item clause of a treasure entry.

kind="any" rolls the master type table per item (re-rolling excluded categories, draws consumed); kind="category" rolls one named sub-table; kind="pool" picks uniformly among categories before the sub-table roll (Type B's "magic sword, suit of armour, or weapon"). Exactly one of count and count_dice sizes the allotment.

kind instance-attribute

kind: Literal['any', 'category', 'pool']

categories class-attribute instance-attribute

categories: tuple[MagicItemType, ...] = ()

count class-attribute instance-attribute

count: int | None = None

count_dice class-attribute instance-attribute

count_dice: str | None = None

exclude class-attribute instance-attribute

exclude: tuple[MagicItemType, ...] = ()

MagicItemType

Bases: StrEnum

The eight magic item types of the master Magic Item Type table.

rod_staff_wand and scroll each cover one printed row ("Rod / Staff / Wand", "Scroll or Map") and one generation sub-table.

ARMOUR class-attribute instance-attribute

ARMOUR = 'armour'

MISC class-attribute instance-attribute

MISC = 'misc'

POTION class-attribute instance-attribute

POTION = 'potion'

RING class-attribute instance-attribute

RING = 'ring'

ROD_STAFF_WAND class-attribute instance-attribute

ROD_STAFF_WAND = 'rod_staff_wand'

SCROLL class-attribute instance-attribute

SCROLL = 'scroll'

SWORD class-attribute instance-attribute

SWORD = 'sword'

WEAPON class-attribute instance-attribute

WEAPON = 'weapon'

MagicItemTypeRow

Bases: BaseModel

One master-table row: the type and its printed d% bands, both tiers.

00 reads as 100, so the bands close at 100 exactly.

category instance-attribute

category: MagicItemType

basic_min class-attribute instance-attribute

basic_min: int = Field(ge=1, le=100)

basic_max class-attribute instance-attribute

basic_max: int = Field(ge=1, le=100)

expert_min class-attribute instance-attribute

expert_min: int = Field(ge=1, le=100)

expert_max class-attribute instance-attribute

expert_max: int = Field(ge=1, le=100)

MagicItemTypeTable

Bases: BaseModel

The master Magic Item Type table with its Basic and Expert d% columns.

rows class-attribute instance-attribute

rows: tuple[MagicItemTypeRow, ...] = Field(min_length=1)

category_for_roll

category_for_roll(roll: int, *, tier: str) -> MagicItemType

Return the item type a d% roll selects under one tier's column.

Parameters:

Name Type Description Default
roll int

The d% result, 1–100.

required
tier str

"basic" or "expert".

required

Returns:

Type Description
MagicItemType

The selected type.

Raises:

Type Description
ValueError

If the tier is unknown or the roll is outside 1–100.

RoomContentsResult

Bases: BaseModel

One à la carte stocking roll: the d6, its row, and the treasure-chance pair.

treasure_roll is None when the row's chance is 0 (None printed) — no die is consumed.

roll instance-attribute

roll: int

row instance-attribute

treasure_roll class-attribute instance-attribute

treasure_roll: int | None = None

treasure_present class-attribute instance-attribute

treasure_present: bool = False

StockingRow

Bases: BaseModel

One d6 row of the room-contents stocking table.

treasure_chance_in_six is 0 for the printed None.

roll_min class-attribute instance-attribute

roll_min: int = Field(ge=1, le=6)

roll_max class-attribute instance-attribute

roll_max: int = Field(ge=1, le=6)

contents instance-attribute

contents: Literal['empty', 'monster', 'special', 'trap']

treasure_chance_in_six class-attribute instance-attribute

treasure_chance_in_six: int = Field(ge=0, le=6)

StockingTable

Bases: BaseModel

The Random Dungeon Room Contents d6 table.

rows class-attribute instance-attribute

rows: tuple[StockingRow, ...] = Field(min_length=1)

row_for_roll

row_for_roll(roll: int) -> StockingRow

Return the stocking row a d6 roll selects.

Parameters:

Name Type Description Default
roll int

The d6 result, 1–6.

required

Returns:

Type Description
StockingRow

The selected row.

Raises:

Type Description
ValueError

If the roll is outside 1–6.

TreasureEntry

Bases: BaseModel

One printed entry (bullet) of a treasure type or unguarded-treasure band.

chance_pct is the presence gate (0 = always present). Exactly one payload is set: coins, gems_dice, jewellery_dice, or magic.

chance_pct class-attribute instance-attribute

chance_pct: int = Field(default=0, ge=0, le=100)

coins class-attribute instance-attribute

coins: CoinQuantity | None = None

gems_dice class-attribute instance-attribute

gems_dice: str | None = None

jewellery_dice class-attribute instance-attribute

jewellery_dice: str | None = None

magic class-attribute instance-attribute

magic: tuple[MagicAllotment, ...] = ()

TreasureRefPlan

Bases: BaseModel

A monster's TreasureRef, resolved to generation instructions.

Letters resolve each by its own section: hoard letters (A–O) are lair treasure, individual letters (P–T) generate once per monster, group letters (U–V) once per group. Parenthetical letters are lair treasure regardless of section (the Bandit's U (A): U carried by the group, A in the lair). extra_gp adds flat gp to the lair hoard; multiplier repeats the whole listed generation that many times (the Noble's V × 3). special labels and see_below generate nothing — they are content prose for a game's keyed areas to supply, as are the small-lair reduction and the referee's manual value adjustment.

lair class-attribute instance-attribute

lair: tuple[str, ...] = ()

individual class-attribute instance-attribute

individual: tuple[str, ...] = ()

group class-attribute instance-attribute

group: tuple[str, ...] = ()

extra_gp class-attribute instance-attribute

extra_gp: int = 0

multiplier class-attribute instance-attribute

multiplier: int = 1

TreasureSection

Bases: StrEnum

Which section of the treasure-type tables a letter belongs to.

The wire values are lowercase — they serialize into treasure.json; changing them is a schema_version bump. Hoards (A–O) are lair treasure, individual letters (P–T) generate once per monster, and group letters (U–V) once per group.

HOARD class-attribute instance-attribute

HOARD = 'hoard'

INDIVIDUAL class-attribute instance-attribute

INDIVIDUAL = 'individual'

GROUP class-attribute instance-attribute

GROUP = 'group'

TreasureTables

Bases: BaseModel

The loaded treasure tables, with treasure-type lookup by letter.

treasure_types instance-attribute

treasure_types: tuple[TreasureTypeTable, ...]

gems instance-attribute

magic_item_types instance-attribute

magic_item_types: MagicItemTypeTable

stocking instance-attribute

stocking: StockingTable

unguarded instance-attribute

treasure_type

treasure_type(letter: str) -> TreasureTypeTable

Return the treasure type for letter.

Parameters:

Name Type Description Default
letter str

The treasure-type letter, e.g. "A".

required

Returns:

Type Description
TreasureTypeTable

The treasure type table.

Raises:

Type Description
ValueError

If no type has that letter.

TreasureTypeTable

Bases: BaseModel

One treasure type (A–V): its section, printed average, and entries in order.

letter class-attribute instance-attribute

letter: str = Field(min_length=1, max_length=1)

kind instance-attribute

average_gp class-attribute instance-attribute

average_gp: float = Field(ge=0)

entries class-attribute instance-attribute

entries: tuple[TreasureEntry, ...] = Field(min_length=1)

UnguardedTreasureBand

Bases: BaseModel

One dungeon-level band of the unguarded-treasure table.

label class-attribute instance-attribute

label: str = Field(min_length=1)

min_level class-attribute instance-attribute

min_level: int = Field(ge=1)

max_level class-attribute instance-attribute

max_level: int = Field(ge=1)

entries class-attribute instance-attribute

entries: tuple[TreasureEntry, ...] = Field(min_length=1)

UnguardedTreasureTable

Bases: BaseModel

The unguarded-treasure bands, in level order.

Levels beyond the last printed band always clamp into it: levels 10+ use the 8–9 band.

bands class-attribute instance-attribute

bands: tuple[UnguardedTreasureBand, ...] = Field(min_length=1)

band_for_level

band_for_level(level: int) -> UnguardedTreasureBand

Return the band for a dungeon level, clamped into the printed bands.

Parameters:

Name Type Description Default
level int

The dungeon level number, 1-based.

required

Returns:

Type Description
UnguardedTreasureBand

The band; levels past the last band use the last band.

Raises:

Type Description
ValueError

If level is below 1.

generate_magic_item

generate_magic_item(
    category: MagicItemType | None,
    *,
    tier: str,
    stream: RngStream,
    allocator: Any,
    exclude: tuple[MagicItemType, ...] = ()
) -> list[MagicItemInstance]

Generate one magic item allotment: the type roll, the sub-table, the details.

When category is None, the master Magic Item Type table rolls under the tier's d% column, re-rolling excluded categories with draws consumed. tier selects the printed B or X probability columns (basic rolls the sub-table's small die, expert its d%). Instance details always resolve depth-first in this order: the Magic Armour Type d8 for generic armour outcomes, charges (rolled at creation, referee-only forever after — RAW "undiscoverable"), ammunition quantities, wish counts, scroll contents, the energy-drain sword's total, and sword sentience last.

Parameters:

Name Type Description Default
category MagicItemType | None

The master-table type, or None to roll it.

required
tier str

"basic" or "expert" — the printed B or X columns.

required
stream RngStream

The treasure stream.

required
allocator Any

The id allocator (magic-item prefix).

required
exclude tuple[MagicItemType, ...]

Master-table types an unspecified roll re-rolls.

()

Returns:

Type Description
list[MagicItemInstance]

The generated instances — one, or two for a paired armour bundle.

Raises:

Type Description
ValueError

If tier is unknown or category is excluded.

generate_treasure

generate_treasure(treasure_type: str, *, tier: str, stream: RngStream, allocator: Any) -> GeneratedTreasure

Generate one treasure type's contents, à la carte.

Parameters:

Name Type Description Default
treasure_type str

The type letter, "A" through "V", from load_treasure_tables — see the treasure type index.

required
tier str

"basic" or "expert" — the printed B or X magic item columns; the crawl passes basic while the party's highest living level is 1–3 and expert at 4+, evaluated at generation time. À la carte callers choose explicitly.

required
stream RngStream

The treasure stream, conventionally TREASURE_STREAM.

required
allocator Any

The id allocator (valuable and magic-item prefixes).

required

Returns:

Type Description
GeneratedTreasure

The generated coins, valuables, and magic items.

Raises:

Type Description
ValueError

If the letter or tier is unknown.

Examples:

from osrlib.core.monsters import IdAllocator
from osrlib.core.rng import RngStreams
from osrlib.core.treasure import TREASURE_STREAM, generate_treasure

stream = RngStreams(master_seed=1).get(TREASURE_STREAM)
hoard = generate_treasure("A", tier="expert", stream=stream, allocator=IdAllocator())
assert hoard.coins.total_coins == 7000
assert len(hoard.valuables) == 19
assert len(hoard.magic_items) == 3
assert {item.template_id for item in hoard.magic_items} == {
    "sword_plus_1_plus_3_vs_dragons",
    "ring_of_protection",
    "potion_of_poison",
}

generate_treasure_entries

generate_treasure_entries(
    entries: Sequence[TreasureEntry], *, tier: str, stream: RngStream, allocator: Any
) -> GeneratedTreasure

Generate treasure from printed entries, in printed order — the core every generation entry point shares.

Per entry: the presence roll (when gated), then the quantity dice, then per-item resolution (each gem's value, each jewellery's value, each magic item fully depth-first), so results are reproducible from the stream alone. A pool allotment picks uniformly among its categories before the sub-table roll.

Parameters:

Name Type Description Default
entries Sequence[TreasureEntry]

The printed entries (a treasure type's, an unguarded band's, or a treasure map's recipe).

required
tier str

"basic" or "expert".

required
stream RngStream

The treasure stream.

required
allocator Any

The id allocator (valuable and magic-item prefixes).

required

Returns:

Type Description
GeneratedTreasure

The generated coins, valuables, and magic items.

generate_unguarded_treasure

generate_unguarded_treasure(dungeon_level: int, *, tier: str, stream: RngStream, allocator: Any) -> GeneratedTreasure

Generate an unguarded-treasure cache for a dungeon level.

The level always clamps into the printed bands: levels 10 and deeper use the 8–9 band.

Parameters:

Name Type Description Default
dungeon_level int

The dungeon level number, 1-based.

required
tier str

"basic" or "expert".

required
stream RngStream

The treasure stream.

required
allocator Any

The id allocator.

required

Returns:

Type Description
GeneratedTreasure

The generated coins, valuables, and magic items.

plan_treasure_ref

plan_treasure_ref(ref: TreasureRef) -> TreasureRefPlan

Resolve a stat block's treasure reference to its generation plan.

Parameters:

Name Type Description Default
ref TreasureRef

The monster template's treasure reference.

required

Returns:

Type Description
TreasureRefPlan

The resolved plan (see TreasureRefPlan).

Raises:

Type Description
ValueError

If a referenced letter is not a compiled treasure type.

roll_room_contents

roll_room_contents(stream: RngStream) -> RoomContentsResult

Roll the stocking d6 and its treasure-chance pair, à la carte.

Parameters:

Name Type Description Default
stream RngStream

The RNG stream, conventionally the treasure stream.

required

Returns:

Type Description
RoomContentsResult

The stocking roll, the row it selected, and the treasure-presence pair

RoomContentsResult

(no die is consumed for a printed None chance).