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
Stream key convention for treasure-generation draws: presence, quantities, items.
CoinDenomination
Bases: StrEnum
The five coin denominations, as the treasure tables print them.
CoinQuantity
GemValueBand
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.
value_for_roll
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.
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.
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.
MagicItemTypeTable
Bases: BaseModel
The master Magic Item Type table with its Basic and Expert d% columns.
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
|
|
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.
StockingRow
Bases: BaseModel
One d6 row of the room-contents stocking table.
treasure_chance_in_six is 0 for the printed None.
StockingTable
Bases: BaseModel
The Random Dungeon Room Contents d6 table.
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.
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.
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.
TreasureTables
Bases: BaseModel
The loaded treasure tables, with treasure-type lookup by letter.
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. |
required |
Returns:
| Type | Description |
|---|---|
TreasureTypeTable
|
The treasure type table. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If no type has that letter. |
TreasureTypeTable
UnguardedTreasureBand
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 |
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 |
required |
tier
|
str
|
|
required |
stream
|
RngStream
|
The treasure stream. |
required |
allocator
|
Any
|
The id allocator ( |
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 |
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, |
required |
tier
|
str
|
|
required |
stream
|
RngStream
|
The treasure stream, conventionally
|
required |
allocator
|
Any
|
The id allocator ( |
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
|
|
required |
stream
|
RngStream
|
The treasure stream. |
required |
allocator
|
Any
|
The id allocator ( |
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
|
|
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 |
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 |