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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
- [`SpawnNpcParty`][osrlib.crawl.commands.SpawnNpcParty] takes an exact band size in the new `count_fixed` field, so a referee can place a band of four adventurers (#167). Before, the size had to be a dice expression, and `"4"` isn't one. Set at most one of `count_fixed` and `count_dice`. With neither, the session rolls the B/X number for the band's kind as before. A saved command without the field still parses.
- A game can end an encounter peacefully with the referee command [`EndEncounter`][osrlib.crawl.commands.EndEncounter], when the party pays a toll, talks its way past, or walks away (#167). The command closes the encounter, and the battle if one is open. Set `outcome` to `"parley"` when the party talks or pays its way past, and to `"departed"` when the party walks away from monsters that let it go. The command moves no coins, so to charge a toll, issue a [`RemoveCoins`][osrlib.crawl.commands.RemoveCoins] beside it. The session logs the command, so a saved game and a replay close the encounter too. Before, the only way was to call [`end_encounter`][osrlib.crawl.encounter.end_encounter] directly, and a replay of that game stayed in the encounter. Set `resolve_area` to `True` to mark the keyed area the monsters came from as resolved, so they aren't there the next time the party walks in. An adventure can use the command as a trigger's consequence too.
- A game can take items and coins away with logged, replayable referee commands, so it can run a shop that buys back armour, charge a price or a toll, or use up a quest item (#173). [`RemoveItem`][osrlib.crawl.commands.RemoveItem] takes units of a mundane item, or one magic item or valuable by its instance id. An equipped item comes off first, and any effect it attached ends. A revealed curse doesn't stop the removal. [`RemoveCoins`][osrlib.crawl.commands.RemoveCoins] takes exactly the denominations the command lists and makes no change from other coins. Both are legal in every mode, cost no draws and no time, and report an [`ItemsRemovedEvent`][osrlib.crawl.events.ItemsRemovedEvent] with the code `session.items.removed`. When the member in `character_id` doesn't have the item or the coins, the session refuses the command with `items.remove.not_carried` or `items.coins.insufficient`.
- A game can add a character to a running party with the referee command [`AddMember`][osrlib.crawl.commands.AddMember], so it can bring in a replacement for a character who died or an adventurer the party meets (#172). Build the character as you build a starting member and send it in the command. The command is legal in `town` and `exploring`. In every other mode the session refuses it with `session.command.wrong_mode`. The session adds a copy of the character at the back of the marching order and gives it the next `character-NNNN` id. It reports the new member in a player-visible [`MemberAddedEvent`][osrlib.crawl.events.MemberAddedEvent] with the code `session.member.added`. Read the id from the event and use it in later commands. The character must be new to this game. The session refuses a character who already has an id with `party.add.id_assigned`, one who has a condition or a stat modifier with `party.add.under_effects`, and one who carries a magic item or a valuable with `party.add.carries_instances`. The new member keeps their mundane gear and coins. Coins a new member brings into a dungeon earn no experience. A member who joins during a delve gets an equal share of the experience the party earns on that delve, including what it earned before they joined. The command makes no draws and takes no time, and the session logs it, so a saved game and a replay have the new member too.
- [`GrantItem`][osrlib.crawl.commands.GrantItem] can grant a magic item by its catalog id, so a game can sell potions or hand out a magic reward (#173). Each copy arrives unidentified, with a separate instance id in the [`ItemAcquiredEvent`][osrlib.crawl.events.ItemAcquiredEvent]. The session rolls whatever the item's entry in the OSE SRD leaves to chance, like a wand's charges, on the treasure stream, as it does for a magic item found in a cache. A mundane grant is unchanged.
- An adventure document can use `RemoveItem`, `RemoveCoins`, and a `GrantItem` of a magic item as trigger consequences and quest rewards (#173). A `RemoveItem` can name its member with [`CARRIER_SELECTOR`][osrlib.crawl.triggers.CARRIER_SELECTOR] (`"@carrier"`), which the interpreter replaces with the first living member in marching order who carries the item. When nobody does, the interpreter drops the command with a note. A quest reward written this way uses up the quest item when the quest completes. [`validate_adventure`][osrlib.crawl.adventure.validate_adventure] checks an authored `GrantItem`'s or `RemoveItem`'s item id against the equipment and magic item catalogs, and accepts `"@carrier"` only on a `RemoveItem`.
- [`Rest`][osrlib.crawl.commands.Rest] has a new field `hidden`, and [`GameSession.advance_turns`][osrlib.crawl.session.GameSession.advance_turns] takes a matching `hidden` parameter, so a game can let the party hide while it rests. The OSE SRD lowers the chance of wandering monsters when the party rests quietly in an out-of-the-way place, and a game sets `hidden` to say the party does. Each wandering-monster check during a hidden rest takes 1 off the chance in 6, after noise and bright light add theirs. On a level with the usual 1 in 6 chance, the session rolls no check during a hidden rest. The field defaults to `False`, so a saved command without it still parses as a plain rest. In town no checks run, so `hidden` changes nothing there.
Expand Down
1 change: 1 addition & 0 deletions docs/front-ends/llm-referees.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ Player commands let the model drive the party's turn. Referee commands let it *r
- [`EndEncounter`][osrlib.crawl.commands.EndEncounter] - close the encounter when the party talks its way past or walks away, and optionally mark a keyed area resolved so its monsters don't come back
- [`GrantItem`][osrlib.crawl.commands.GrantItem], [`GrantCoins`][osrlib.crawl.commands.GrantCoins], [`AwardXP`][osrlib.crawl.commands.AwardXP] - place rewards directly, magic items included
- [`RemoveItem`][osrlib.crawl.commands.RemoveItem], [`RemoveCoins`][osrlib.crawl.commands.RemoveCoins] - take items or coins away: a price, a toll, armour a shop buys back
- [`AddMember`][osrlib.crawl.commands.AddMember] - bring a new character into the party in town or while exploring, like a replacement for a fallen member. The new member joins at the back of the marching order, and the event gives their id
- [`SetDoorState`][osrlib.crawl.commands.SetDoorState] - rewrite any door's state anywhere: lock it, wedge it, reveal it
- [`PlaceParty`][osrlib.crawl.commands.PlaceParty] and [`AdvanceTime`][osrlib.crawl.commands.AdvanceTime] - teleport the party, advance the clock
- [`AddJournalEntry`][osrlib.crawl.commands.AddJournalEntry] and [`RecordNote`][osrlib.crawl.commands.RecordNote] - what the agent records in the game world: a journal entry is written for the players and appears in their view, and a note is for the referee only and stays out of the player view
Expand Down
2 changes: 1 addition & 1 deletion docs/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ An adventure can be won, and its content can be wired — the lever that opens t
- Rejected consequences are dropped and recorded as referee-visibility notes — no retry and no queue, because deferred consequences firing later would be undebuggable. (A triggered ambush can collide with a wandering encounter opened by the same movement; the spawn drops and the note says so.)
- Trigger-granted treasure follows the normal valuation rules at the moment it lands: granted mid-dungeon it counts in the next return delta, granted at town it earns no treasure XP. Authored XP belongs in `AwardXP`.
- A consequence addresses characters through party selectors, never a literal id: `@party` expands at issue time to one command per living member in marching order, `@first` to the first living member, `@carrier` (on `RemoveItem` only) to the first living member who carries the item, and validation rejects a document that names a character id, because character ids are allocated per session and no document can know one. The expansion happens as the commands issue, so the log stays concrete and replays exactly.
- The consequence surface is the referee command vocabulary minus three: the lifecycle commands (the interpreter's own vocabulary, never authored), `IdentifyItem` (its item ids are session-scoped instance ids, the same unknowability as character ids), and `RollDice` (a draw whose result no authored construct can read, so an authored roll would be a no-op that perturbs the adjudication stream).
- The consequence surface is the referee command vocabulary minus four: the lifecycle commands (the interpreter's own vocabulary, never authored), `IdentifyItem` (its item ids are session-scoped instance ids, the same unknowability as character ids), `RollDice` (a draw whose result no authored construct can read, so an authored roll would be a no-op that perturbs the adjudication stream), and `AddMember` (its payload is a whole character, and an adventure document carries no characters).
- Monster-defeat events emit at battle end, so "the portcullis opens mid-fight as the boss falls" is not authorable; stated so authors' expectations are set.

**The interpreter and the command log.** The interpreter observes events, decides, and acts exclusively by issuing commands — anything it merely remembered would be lost to a replay. Quest, trigger, and journal state live in the engine session, mutated only by a small family of lifecycle referee commands the interpreter issues (authors author quest and trigger specs, never these commands): `MarkTriggerFired` records fired-state before a trigger's consequences issue; `ActivateQuest`, `RevealObjective`, `CompleteObjective`, and `CompleteQuest` advance quest state, emit the player-visible events carrying the authored narrative, and append journal entries; `AddJournalEntry` gives one-off triggers a journal voice; `RecordNote` has no state effect and emits a referee-visibility event — the mechanism behind dropped-consequence and truncation records. These are ordinary logged, replayed commands. The base command model gains an optional `source` field, ignored by execution, which the interpreter stamps with the owning trigger or quest id — so "why did the party get 500 XP" is answerable from the log alone.
Expand Down
6 changes: 4 additions & 2 deletions src/osrlib/core/character.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,10 @@ class Character(BaseModel):
"""The character's id within a game, like `"character-0001"`, or `None` before a session assigns one.

[`GameSession.new`][osrlib.crawl.session.GameSession.new] assigns an id to each party member whose id is `None`,
from the session's [`IdAllocator`][osrlib.core.monsters.IdAllocator]. Commands refer to a character by this id. An
event about a character with no id uses the character's `name` instead.
from the session's [`IdAllocator`][osrlib.core.monsters.IdAllocator], and
[`AddMember`][osrlib.crawl.commands.AddMember] assigns one the same way to a character who joins during play.
Commands refer to a character by this id. An event about a character with no id uses the character's `name`
instead.
"""

name: str = Field(min_length=1)
Expand Down
Loading
Loading