osrlib.crawl.content_pack
Content packs: portable, geometry-free keyed content an editor can carry between adventures.
A ContentPack is finished room content
with the geometry left behind: sections of entries that mirror
AreaSpec minus its cells, each section
optionally carrying a level's WanderingSpec,
plus the bundled MonsterTemplates the
entries' encounters and the sections' wandering tables reference — the pack's
closure. Packs are how an authoring
tool moves stocked rooms from one adventure to another: the consumer writes an
entry's content into a target area it already has, so a pack never places cells,
transitions, or any other geometry.
Identity is the contract consumers stand on: section ids, entry ids (pack-wide),
and bundled monster ids are each unique, enforced at construction — a pack that
breaks them refuses to load rather than lingering as a diagnostic. Dangling
references are legal by the same posture as
Adventure: an encounter may name a template
neither the pack nor the shipped catalog carries, and
validate_content_pack
reports such gaps as structured
PackFindings instead of raising — the
first consumer is a panel that lists findings, not a gate.
Packs serialize as stamped "content_pack" documents
(CONTENT_PACK_KIND), the
longest-lived artifacts in the document family, and own their acceptance rules:
a document stamped by an older schema version is accepted on load, one stamped
by a newer version fails with SaveVersionError,
and any write re-stamps at the current schema and engine versions — a loaded
older pack saves as a current one.
from osrlib.crawl.content_pack import ContentPack, ContentPackEntry, PackSection, validate_content_pack
from osrlib.crawl.dungeon import KeyedEncounter, KeyedMonster
from osrlib.data import load_equipment, load_monsters
pack = ContentPack(
name="The gnawing dark",
sections=(
PackSection(
id="level-1",
label="Level 1",
entries=(
ContentPackEntry(
id="guard-post",
name="Guard post",
encounter=KeyedEncounter(monsters=(KeyedMonster(template_id="orc", count_fixed=4),)),
),
),
),
),
)
document = pack.to_document()
assert document["kind"] == "content_pack"
assert ContentPack.from_document(document) == pack
assert validate_content_pack(pack, load_monsters(), load_equipment()) == ()
CONTENT_PACK_KIND
module-attribute
The stamped-document kind for serialized content packs.
ContentPack
Bases: BaseModel
A content pack: sections of geometry-free entries plus their monster closure.
monsters bundles the MonsterTemplates
the pack's encounters and wandering tables reference beyond the shipped
catalog. id defaults empty: a pack derived on the fly takes its identity
from its source, and only a persisted pack mints an id of its own.
to_document
from_document
classmethod
from_document(document: Mapping[str, object]) -> ContentPack
Load a content pack from a stamped document.
A schema_version older than the current one is accepted — pack payload
changes are additive within a version, so an older document validates
directly. Unknown payload fields are ignored, per the additive-schema
contract.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
document
|
Mapping[str, object]
|
A document produced by
|
required |
Returns:
| Type | Description |
|---|---|
ContentPack
|
The reconstructed pack. |
Raises:
| Type | Description |
|---|---|
ContentValidationError
|
If the envelope or payload is malformed or of the wrong kind. |
SaveVersionError
|
If the document's schema version is newer than this library understands. |
ContentPackEntry
Bases: BaseModel
One portable room: AreaSpec minus its geometry.
The entry carries the content slots an area has — prose, encounter, trap,
treasure, features — and nothing that binds to a grid: there are no cells,
and a consumer writes the carried slots into a target area it already has.
An entry trap must be a room trap, the same rule AreaSpec enforces; the
treasure-trap coupling needs no restatement because it lives on
FeatureSpec itself.
PackFinding
PackSection
Bases: BaseModel
A pack's level grouping: entries plus the level's optional wandering table.
Sections are structural because three consumers operate on a level grouping — a panel's level rows, wandering's level scope, and a level-scoped capture. Entry ids are unique pack-wide, so consumers address entries by id alone; the section contributes presentation grouping and the wandering slot.
validate_content_pack
validate_content_pack(
pack: ContentPack, monsters: MonsterCatalog, equipment: EquipmentCatalog
) -> tuple[PackFinding, ...]
Report a pack's self-containment gaps — references its closure does not cover.
Checks every monster reference (keyed-encounter lines and wandering-table
rows) against the union of the shipped catalog and the pack's bundled
monsters, every feature's item_ids against the equipment catalog, and every
feature's magic_item_ids against the shipped magic-item catalog
(load_magic_items — packs bundle no magic
items, so the check loads it itself).
Findings are data, not errors: a dangling reference is legal in a pack
exactly as it is while editing an adventure, and a caller wanting a gate
checks for a non-empty result.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pack
|
ContentPack
|
The pack to check. |
required |
monsters
|
MonsterCatalog
|
The base monster catalog — the check unions it with
|
required |
equipment
|
EquipmentCatalog
|
The equipment catalog feature contents resolve against. |
required |
Returns:
| Type | Description |
|---|---|
PackFinding
|
One finding per gap, in section and entry order; empty means the pack is |
...
|
self-contained. |