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
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
- An `attack` in a [`BattleDeclaration`][osrlib.crawl.commands.BattleDeclaration] with no `weapon_id` now uses the wielded weapon that suits the attack (#162). Before, the member struck unarmed, even with a sword in hand. The group's distance when the session accepts the round decides the attack: melee at 5 feet or less, missile beyond. The session takes the first wielded weapon, in the order the member equipped them, that can make that attack. For a missile attack it prefers the first whose long range reaches the group. A real weapon comes before an improvised one, like a torch, so a member who lit a torch and then drew a sword swings the sword. The torch still counts when the member wields nothing else that suits the attack. The session passes over a splash weapon, like a flask of oil, and an item with no weapon stats, like a wand. A member with no suitable weapon still strikes unarmed. The round plays out as if you had named the chosen weapon, and the command log records the declaration as you sent it. The attack events name the weapon the member used. [`BattleDeclaration.weapon_id`][osrlib.crawl.commands.BattleDeclaration.weapon_id] describes the choice. A declaration that names a weapon works as before.
- A wand, staff, or rod use and a turning declared in [`ResolveBattleRound`][osrlib.crawl.commands.ResolveBattleRound] are checked again in the magic phase, just before they resolve, as a spell cast and a scroll read are (#145). Before, they resolved even when the round had changed something they depend on. For example, a staff of striking struck a group that had already run out of melee reach. Now a declaration that fails the second check fizzles, and the session reports it with a [`DeclarationFizzledEvent`][osrlib.core.events.DeclarationFizzledEvent]. A device that fizzles was never used, so it keeps its charge, the session doesn't identify it, and the member who declared it stays invisible if they were. A turning that fizzles rolls nothing. A device use or turning that passes the second check resolves with the same rolls as before.
- Some command logs recorded under 2.0.1 replay differently under this release. Loading a 2.0.1 save with [`load_game`][osrlib.persistence.load_game] isn't affected, because a load restores the saved state and runs no commands. The loaded game then plays on under this release's rules. Replaying a 2.0.1 log with [`replay_game`][osrlib.persistence.replay_game] can differ in two ways. Each change named below has an entry in this section or under Fixed.
- The replay raises [`ContentValidationError`][osrlib.errors.ContentValidationError] when the log contains a command this release refuses. Two commands that 2.0.1 accepted are refused now: a potion `UseItem` aimed at another member, and a temple service that would do nothing.
- The replay raises [`ContentValidationError`][osrlib.errors.ContentValidationError] when the log contains a command this release refuses. Three commands that 2.0.1 accepted are refused now: a potion `UseItem` aimed at another member, a temple service that would do nothing, and a battle round in which a member behind the front rank strikes with a staff of striking.
- The replay runs without an error but plays out differently when the log contains any of these:
- A lair the party left and came back to.
- A rest in a dungeon, or a rest that a wandering monster interrupted.
Expand All @@ -51,6 +51,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
- An attack declared with no `weapon_id` by a member who wields a weapon.
- A wand, staff, rod, or turning that now fizzles.
- A round in which the whole party retreats and a monster attacks a member before the party moves off, or while the monster is confused.
- A turning after a monster in the encounter died or its group fled the battle.
- A staff of striking used by a member who was to step up into the front rank but whose swap didn't happen.
- A level drain on a character, whose drain event now reports different `hp_lost` and `spawn_consequence` values. The drain itself takes the same levels and hit points.

### Fixed

Expand Down Expand Up @@ -79,6 +82,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
- The [`RollResult`][osrlib.core.dice.RollResult] that [`damage_roll`][osrlib.core.combat.damage_roll] returns obeys its documented rule, `total == (sum(rolls) + modifier) * multiplier`, before the total is raised to 1. `modifier` includes the Strength modifier, the magic weapon's bonus, spell bonuses, and a fixed amount. `multiplier` includes the doublings, like a back-stab or a braced spear. Before, both fields were always 0 and 1, so a front end that showed the breakdown showed the wrong numbers. Totals and draw sequences are unchanged (#166).
- A front end built on the player view can cast *sleep* at a group in battle (#162). When a [`BattleDeclaration`][osrlib.crawl.commands.BattleDeclaration] for an `hd_budget` mode has a `target_group_id` and no `targets`, the session now spends the spell's Hit Dice budget on that group's living members, weakest first. Before, the session refused it with `magic.cast.target_count`, and the player view shows no monster ids to name instead. It's the same cast as one that lists every living member of the group in spawn order, and it isn't limited to an area footprint. When both are set, the session still uses the named `targets`, so a saved command log replays as it did. No golden file changed.
- A front end can tell that a monster group has fled, and the session refuses every declaration aimed at it (#162). [`EncounterGroupView`][osrlib.crawl.views.EncounterGroupView] has a new `fled` field, which is true once the group has left the battle. A fled group stays in the player view's `groups`, in order. Before, the session refused an attack or an item aimed at a fled group but accepted a `close` move or an area spell. Now the session refuses each of these with `battle.declaration.unknown_group`, with the declarer's id as the `character` param. You can still declare against a group that is running away but hasn't left the battle. A spell aimed at a group that flees earlier in the same round fizzles in the magic phase, as a spell does when any of its checks fails there. The field defaults to `False`, so [`SCHEMA_VERSION`][osrlib.versioning.SCHEMA_VERSION] stays 4. No golden file changed.
- A turning reaches only the monsters still in the fight. Before, a `turn_undead` in [`ResolveBattleRound`][osrlib.crawl.commands.ResolveBattleRound] reached every monster in the encounter, the dead and the fled included. A skeleton shot dead in the missile phase was still read on the turning table, so the turning could report `magic.turning.turned` with no monster affected. Now it reaches the living monsters of the groups that haven't fled. When the magic phase finds none left, the turning fizzles with a [`DeclarationFizzledEvent`][osrlib.core.events.DeclarationFizzledEvent] at `magic.turning.fizzled`, whose `reason` is `battle.declaration.no_target`, and it draws nothing. [`TurnUndead`][osrlib.crawl.commands.TurnUndead] before a battle skips a monster killed earlier in the encounter in the same way.
- Only a member in the front rank can strike with a staff of striking, because the OSE SRD's entry says it's "Used as a magical melee weapon". Before, a member in the back rank could use one in [`ResolveBattleRound`][osrlib.crawl.commands.ResolveBattleRound]. Now the session refuses that use with `battle.declaration.not_in_front_rank`, as it refuses a melee attack. A member who steps up this round, named in [`replacement_id`][osrlib.crawl.commands.BattleDeclaration.replacement_id], may strike. If that swap doesn't happen, the use fizzles in the magic phase at `magic.device.fizzled`, and the staff keeps its charge. With the ruleset's `formation_width_limit` off, every member stands in the front rank and may strike.
- The `hp_lost` of a [`LevelDrainedEvent`][osrlib.core.events.LevelDrainedEvent] is how far the victim's maximum hit points fell. Before, it was the hit points rolled for the lost levels, which overstated the loss when the maximum stopped at 1. A level 2 dwarf with 3 maximum hit points who rolled a 7 for the lost level was reported to lose 7, and now is reported to lose 2. The `hp_lost` of [`DrainResult`][osrlib.core.classes.DrainResult] is still the hit points rolled.
- The `spawn_consequence` of a [`LevelDrainedEvent`][osrlib.core.events.LevelDrainedEvent] appears only on a `combat.drain.slain` event, because it says what a character drained of all levels becomes. Before, a drain the character survived carried it too, as if the character were about to rise as a wight.

## [2.0.1] - 2026-10-30

Expand Down
Loading
Loading