Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 72 additions & 51 deletions src/osrlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,61 +7,82 @@
the same seed and the same commands replay the same game.

Every name has one import home and the package root re-exports nothing, so you import from
the module that defines the symbol. The modules fall into three layers.
the module that defines the symbol. The modules fall into three layers, and each table below
lists one layer's modules in the order you meet them.

## The core kernel

The kernel, under `osrlib.core`, is the rules on their own: no session, no dungeon, no
adventure. Use it directly to roll a character, resolve an attack, or price a sword with no
game running.

- [`osrlib.core.rng`][osrlib.core.rng]: a master seed in, one named stream per subsystem out.
- [`osrlib.core.ruleset`][osrlib.core.ruleset]: the optional rules you switch on, in one model the kernel reads.
- [`osrlib.core.dice`][osrlib.core.dice]: a dice expression in, a parsed expression or a roll and its own dice out.
- [`osrlib.core.alignment`][osrlib.core.alignment]: the three alignments, shared by characters and monsters.
- [`osrlib.core.abilities`][osrlib.core.abilities]: an ability score in, the modifier or chance the tables grant it out.
- [`osrlib.core.classes`][osrlib.core.classes]: a class definition and a character in, titles, XP, advancement out.
- [`osrlib.core.character`][osrlib.core.character]: creation choices and a stream in, a character or refusals out.
- [`osrlib.core.items`][osrlib.core.items]: templates and an inventory in, purchases, equipment, and encumbrance out.
- [`osrlib.core.spells`][osrlib.core.spells]: a caster and a spell in, memorization, casting, and turning undead out.
- [`osrlib.core.monsters`][osrlib.core.monsters]: a monster template in, a spawned instance with its own hit points out.
- [`osrlib.core.combat`][osrlib.core.combat]: combatants and a stream in, initiative, attacks, damage, and saves out.
- [`osrlib.core.effects`][osrlib.core.effects]: a condition or effect in, a ledger that ticks and expires it out.
- [`osrlib.core.treasure`][osrlib.core.treasure]: a treasure type and a stream in, coins, valuables, magic items out.
- [`osrlib.core.tables`][osrlib.core.tables]: hit dice or an armour class in, the printed row for it out.
- [`osrlib.core.npc`][osrlib.core.npc]: a party level and a stream in, a generated NPC adventuring party out.
- [`osrlib.core.clock`][osrlib.core.clock]: rounds in, turns and days out, with the boundaries each crossing reports.
- [`osrlib.core.events`][osrlib.core.events]: the base class every event inherits, and the contract its code follows.
- [`osrlib.core.validation`][osrlib.core.validation]: the refusal value a rules check hands back instead of raising.
adventure. You hand a function the inputs a rule needs, plus a seeded stream and a ruleset,
and it returns the outcome and the events that describe it. Call it directly to roll a
character, resolve an attack, or price a sword with no game running. Kernel modules never
import from `osrlib.crawl`, so what you build on the kernel keeps working whatever the crawl
framework does above it.

| Module | What it's for |
| --- | --- |
| [`osrlib.core.rng`][] | A master seed in, one named stream per subsystem out. |
| [`osrlib.core.ruleset`][] | The optional rules you switch on, in one model the kernel reads. |
| [`osrlib.core.dice`][] | A dice expression in, a parsed expression or a roll and its own dice out. |
| [`osrlib.core.alignment`][] | The three alignments, shared by characters and monsters. |
| [`osrlib.core.abilities`][] | An ability score in, the modifier or chance the tables grant it out. |
| [`osrlib.core.classes`][] | A class definition and a character in, titles, XP, and advancement out. |
| [`osrlib.core.character`][] | Creation choices and a stream in, a character or refusals out. |
| [`osrlib.core.items`][] | Templates and an inventory in, purchases, equipment, and encumbrance out. |
| [`osrlib.core.spells`][] | A caster and a spell in, memorization, casting, and turning undead out. |
| [`osrlib.core.monsters`][] | A monster template in, a spawned instance with its own hit points out. |
| [`osrlib.core.combat`][] | Combatants and a stream in, initiative, attacks, damage, and saves out. |
| [`osrlib.core.effects`][] | A condition or effect in, a ledger that ticks and expires it out. |
| [`osrlib.core.treasure`][] | A treasure type and a stream in, coins, valuables, and magic items out. |
| [`osrlib.core.tables`][] | Hit dice or an armour class in, the printed row for it out. |
| [`osrlib.core.npc`][] | A party level and a stream in, a generated NPC adventuring party out. |
| [`osrlib.core.clock`][] | Rounds in, turns and days out, with the boundaries each crossing reports. |
| [`osrlib.core.events`][] | The base class every event inherits, and the contract its code follows. |
| [`osrlib.core.validation`][] | The refusal value a rules check returns instead of raising. |

## The crawl framework

The crawl framework, under `osrlib.crawl`, is the game around those rules: a party in a
mapped dungeon, driven by commands. Start at the session and work outwards.

- [`osrlib.crawl.dungeon`][osrlib.crawl.dungeon]: cells, edges, doors, areas, and traps in, a mapped dungeon out.
- [`osrlib.crawl.adventure`][osrlib.crawl.adventure]: dungeons and a town in, one adventure a session can play out.
- [`osrlib.crawl.party`][osrlib.crawl.party]: characters in, marching order, group movement, and combat ranks out.
- [`osrlib.crawl.session`][osrlib.crawl.session]: a party, an adventure, and a seed in, a game taking commands out.
- [`osrlib.crawl.commands`][osrlib.crawl.commands]: every command you can execute, each with the modes it is legal in.
- [`osrlib.crawl.events`][osrlib.crawl.events]: every event a command can emit, and the parser that reads one back.
- [`osrlib.crawl.views`][osrlib.crawl.views]: a session in, what a player may see or what a referee may see out.
- [`osrlib.crawl.exploration`][osrlib.crawl.exploration]: movement, doors, searching, light, rest, and wandering checks.
- [`osrlib.crawl.encounter`][osrlib.crawl.encounter]: a meeting in, surprise, distance, reaction, evasion, pursuit out.
- [`osrlib.crawl.battle`][osrlib.crawl.battle]: an encounter that came to blows in, a round-by-round battle out.
- [`osrlib.crawl.stocking`][osrlib.crawl.stocking]: an empty area and a stream in, its monsters and treasure out.
- [`osrlib.crawl.gates`][osrlib.crawl.gates]: a condition and a session in, whether the way opens out.
- [`osrlib.crawl.triggers`][osrlib.crawl.triggers]: an event pattern in, a match against what just happened out.
- [`osrlib.crawl.quests`][osrlib.crawl.quests]: objectives and the clauses that complete them, as authored content.
- [`osrlib.crawl.narrative`][osrlib.crawl.narrative]: the authored text on a mechanical object, one block per audience.
- [`osrlib.crawl.interpreter`][osrlib.crawl.interpreter]: a listener you register in, an adventure playing itself out.
- [`osrlib.crawl.content_pack`][osrlib.crawl.content_pack]: keyed room content out of one adventure and into another.

The shared services sit at the top level and serve both layers.

- [`osrlib.data`][osrlib.data]: a content id in, the frozen rules entry behind it out.
- [`osrlib.errors`][osrlib.errors]: the exceptions the library raises, and which failure each one stands for.
- [`osrlib.messages`][osrlib.messages]: an event in, a line of default English out.
- [`osrlib.persistence`][osrlib.persistence]: a session in, a save document out, and back again by loading or replaying.
- [`osrlib.versioning`][osrlib.versioning]: the two version stamps on every document, and the envelope for them.

The quickstart below runs the whole loop: characters, party, adventure, session, commands,
mapped dungeon, driven by commands. You author the content, start a session, and hand it one
command per player action. The session runs the exploration, encounter, and battle procedures
for you and returns the events. Start at the session and work outwards.

| Module | What it's for |
| --- | --- |
| [`osrlib.crawl.dungeon`][] | Cells, edges, doors, areas, and traps in, a mapped dungeon out. |
| [`osrlib.crawl.adventure`][] | Dungeons and a town in, one adventure a session can play out. |
| [`osrlib.crawl.party`][] | Characters in, marching order, group movement, and combat ranks out. |
| [`osrlib.crawl.session`][] | A party, an adventure, and a seed in, a game taking commands out. |
| [`osrlib.crawl.commands`][] | Every command you can execute, each with the modes it's legal in. |
| [`osrlib.crawl.events`][] | Every event a command can emit, and the parser that reads one back. |
| [`osrlib.crawl.views`][] | A session in, what a player may see or what a referee may see out. |
| [`osrlib.crawl.exploration`][] | Movement, doors, searching, light, rest, and wandering checks. |
| [`osrlib.crawl.encounter`][] | A meeting in, surprise, distance, reaction, evasion, and pursuit out. |
| [`osrlib.crawl.battle`][] | An encounter that came to blows in, a round-by-round battle out. |
| [`osrlib.crawl.stocking`][] | An empty area and a stream in, its monsters and treasure out. |
| [`osrlib.crawl.gates`][] | A gate's condition and a session in, whether the gate opens out. |
| [`osrlib.crawl.triggers`][] | An event pattern in, a match against what just happened out. |
| [`osrlib.crawl.quests`][] | Objectives and the clauses that complete them, as authored content. |
| [`osrlib.crawl.narrative`][] | The authored text on a mechanical object, one block per audience. |
| [`osrlib.crawl.interpreter`][] | The listener you register on a session to play the adventure's triggers and quests. |
| [`osrlib.crawl.content_pack`][] | Keyed room content out of one adventure and into another. |

## Shared services

The shared services sit at the top level and serve both layers: the compiled rules content,
the exceptions, the message formatter, and the save and version documents.

| Module | What it's for |
| --- | --- |
| [`osrlib.data`][] | A content id in, the frozen rules entry behind it out. |
| [`osrlib.errors`][] | The exceptions the library raises, and which failure each one stands for. |
| [`osrlib.messages`][] | An event in, a line of default English out. |
| [`osrlib.persistence`][] | A session in, a save document out, and back again by loading or replaying. |
| [`osrlib.versioning`][] | The two version stamps on every document, and the envelope for them. |

## Quickstart

The quickstart runs the whole loop: characters, party, adventure, session, commands,
events, save, and load. For the documentation, including a stepwise walk through this
example, see https://mmacy.github.io/osrlib-python/

Expand Down
18 changes: 18 additions & 0 deletions tests/test_docs_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,3 +212,21 @@ def test_the_stream_page_names_every_stream_constant(self):
if name.endswith("_STREAM") and f"`{name}`" not in page:
missing.append(f"{info.name}.{name}")
assert not missing, f"stream constants absent from docs/reference/rng-streams.md: {missing}"

def test_the_api_overview_tables_every_exporting_module_once(self):
import importlib
import pkgutil

import osrlib

overview = osrlib.__doc__ or ""
rows = re.findall(r"^\| \[`(osrlib\.[\w.]+)`\]\[\] \|", overview, re.M)
exporting = sorted(
info.name
for info in pkgutil.walk_packages(osrlib.__path__, "osrlib.")
if getattr(importlib.import_module(info.name), "__all__", None)
)
assert sorted(rows) == exporting, (
f"missing from the package docstring tables: {sorted(set(exporting) - set(rows))}; "
f"tabled but not exporting or duplicated: {sorted(set(rows) - set(exporting))}"
)
16 changes: 7 additions & 9 deletions tools/docs/gen_api_reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,15 @@ def _layer(name: str) -> str:
for symbol in exported:
page.write(f" - {symbol}\n")

# The overview page is the package docstring: it introduces each layer and tables its
# modules in the order a reader meets them, with a link to every module page.
with mkdocs_gen_files.open("reference/api/index.md", "w") as index:
index.write("# API reference\n\n")
index.write("::: osrlib\n options:\n members: false\n\n")
index.write("One page per module, each rendering that module's public (importable) surface:\n\n")
for layer_title, _ in _LAYERS:
index.write(f"\n## {layer_title}\n\n")
for name, exported in modules:
if _layer(name) == layer_title:
path = name.replace(".", "/") + ".md"
summary = importlib.import_module(name).__doc__.strip().splitlines()[0].rstrip(".")
index.write(f"- [`{name}`]({path}) — {summary} ({len(exported)} symbols)\n")
index.write("::: osrlib\n")
index.write(" options:\n")
index.write(" members: false\n")
index.write(" heading_level: 1\n")
index.write(" show_root_toc_entry: false\n")

with mkdocs_gen_files.open("reference/api/SUMMARY.md", "w") as summary:
summary.write("\n".join(summary_lines) + "\n")
Loading