From 12a075520ddf685b3a2a48cfc09130e37dc84c35 Mon Sep 17 00:00:00 2001 From: Marsh Macy Date: Fri, 30 Oct 2026 15:03:37 -0700 Subject: [PATCH] Clarify this round's Unreleased changelog bullets after two cold reads Split the 2.0.1 replay bullet into a nested list that separates logs that raise from logs that play out differently, and fix the misreadings two fresh readers found in the new bullets. Claude-Session: https://claude.ai/code/session_01V7ErtnsmhaP7aLwcfmzohD --- CHANGELOG.md | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11f9f46..01328c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,10 +15,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), - [`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. -- [`BattleDeclaration`][osrlib.crawl.commands.BattleDeclaration] has a new field [`replacement_id`][osrlib.crawl.commands.BattleDeclaration.replacement_id], so a member can make a fighting withdrawal or a retreat alone (#171). It names the back-rank member who steps up into the front rank in the withdrawing member's place. The new event [`MemberWithdrewEvent`][osrlib.crawl.events.MemberWithdrewEvent] reports each swap, with the code `battle.member.withdrew` for a fighting withdrawal and `battle.member.retreated` for a retreat. Its `character_id` is the member who stepped back and its `replacement_id` is the member who stepped up, so a battle screen can redraw the ranks from it. The field defaults to `None`, so a saved command without it still parses and [`SCHEMA_VERSION`][osrlib.versioning.SCHEMA_VERSION] stays 4. +- [`BattleDeclaration`][osrlib.crawl.commands.BattleDeclaration] has a new field [`replacement_id`][osrlib.crawl.commands.BattleDeclaration.replacement_id], so a member can make a fighting withdrawal or a retreat alone (#171). A member who withdraws or retreats alone must set it. It names the back-rank member who steps up into the front rank in the withdrawing member's place. The new event [`MemberWithdrewEvent`][osrlib.crawl.events.MemberWithdrewEvent] reports each swap, with the code `battle.member.withdrew` for a fighting withdrawal and `battle.member.retreated` for a retreat. Its `character_id` is the member who stepped back and its `replacement_id` is the member who stepped up, so a battle screen can redraw the ranks from it. The field defaults to `None`, so a saved command without it still parses and [`SCHEMA_VERSION`][osrlib.versioning.SCHEMA_VERSION] stays 4. - Each attack routine has a new field, [`AttackRoutine.use`][osrlib.core.monsters.AttackRoutine.use], that gives when the monster uses the routine. Its value is a [`RoutineUse`][osrlib.core.monsters.RoutineUse]. The first routine is `PRIMARY`. A later routine is `ALTERNATIVE` when the OSE SRD lets the monster use it in place of the first in any round, like a thoul's weapon. It's `SITUATIONAL` when the SRD ties it to a situation osrlib doesn't model, like an elephant's trample or a giant's thrown boulder. It's `SPECIAL` when it's a breath, a gaze, or a spell. Every routine in the built-in monster and spell catalogs has its label. A routine with no label is `PRIMARY`, so in a monster template of your own, label each later routine, or the default policy picks it as often as the first. -- A policy of your own can choose which attack routine a monster uses, in the new field [`MonsterAction.routine`][osrlib.crawl.battle.MonsterAction.routine]. The field is an index into the template's `attacks`, and `None` uses the first routine. So your policy can have an elephant trample when your game decides it charges. Battle rolls the routine's attacks as melee attacks, and rolls nothing for a `SPECIAL` routine. An index the template doesn't have makes the action impossible, so the session skips it. -- [`BattleDeclaration`][osrlib.crawl.commands.BattleDeclaration] has a new field [`gaze_defense`][osrlib.crawl.commands.BattleDeclaration.gaze_defense], so a member can guard against a basilisk's or a medusa's petrifying gaze. Set it to `"avert_eyes"` or `"mirror"` with any action. The stance lasts the round it's declared in, whichever side acts first. A member who averts their eyes doesn't meet the gaze, but every attack roll they make that round takes a −4 penalty, and a basilisk or a medusa gains +2 to attack them. A member who fights by a mirror doesn't meet the gaze either, and every attack roll they make that round takes a −1 penalty. No monster gains a bonus against them. The member has to carry a `mirror`, or the session refuses the round with the new code `battle.declaration.no_mirror`. The penalties come from the basilisk's and the medusa's entries in the OSE SRD. A member who averts their eyes has the `averted_eyes` condition until the round ends. A stance makes no draw and emits no event. The basilisk's and the medusa's `averting_eyes` abilities are no longer `manual`. The field defaults to `None`, so a saved command without it still parses and [`SCHEMA_VERSION`][osrlib.versioning.SCHEMA_VERSION] stays 4. +- A policy of your own can choose which attack routine a monster uses, in the new field [`MonsterAction.routine`][osrlib.crawl.battle.MonsterAction.routine]. The field is an index into the template's `attacks`, and `None` uses the first routine. So your policy can have an elephant trample when your game decides it charges. Battle rolls the routine's attacks as melee attacks, and rolls nothing for a `SPECIAL` routine. An index the template doesn't have makes the action impossible, so the session skips it and the monster doesn't attack that turn. +- [`BattleDeclaration`][osrlib.crawl.commands.BattleDeclaration] has a new field [`gaze_defense`][osrlib.crawl.commands.BattleDeclaration.gaze_defense], so a member can guard against a basilisk's or a medusa's petrifying gaze. Set it on a member's declaration, alongside whatever action they declare, to `"avert_eyes"` or `"mirror"`. The defense lasts only the round it's declared in, and it protects the member whether the monsters or the party act first. A member who averts their eyes doesn't meet the gaze, but every attack roll they make that round takes a −4 penalty, and a basilisk or a medusa gains +2 to attack them. A member who fights by a mirror doesn't meet the gaze either, and every attack roll they make that round takes a −1 penalty. No monster gains a bonus against them. The member has to carry a `mirror`, or the session refuses the whole `ResolveBattleRound` command with the new code `battle.declaration.no_mirror`. The penalties come from the basilisk's and the medusa's entries in the OSE SRD. A member who averts their eyes has the `averted_eyes` condition until the round ends. The defense makes no draw and emits no event. The basilisk's and the medusa's `averting_eyes` abilities are no longer `manual`. The field defaults to `None`, so a saved command without it still parses and [`SCHEMA_VERSION`][osrlib.versioning.SCHEMA_VERSION] stays 4. - [`DeclarationFizzledEvent`][osrlib.core.events.DeclarationFizzledEvent] reports a wand, staff, or rod use or a turning that fizzled in a battle round (#145). Its code is `magic.device.fizzled` for a device and `magic.turning.fizzled` for a turning. `actor_id` is the member who declared it, `item_id` is the device's instance id (`None` for a turning), and `reason` is the rejection code of the check that failed. Tell the player the action failed. A device that fizzles keeps its charge. ### Changed @@ -32,24 +32,39 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), - A melee attack on a petrified creature hits automatically, as on a paralysed or sleeping one (#166). [`attack_roll`][osrlib.core.combat.attack_roll] reports `combat.attack.auto_hit` and draws no d20, so only the damage is rolled. A missile attack on a petrified creature is still rolled. - [`UseItem`][osrlib.crawl.commands.UseItem] with a potion and a `target_id` that names someone other than the member using it is now refused with `items.use.target_not_allowed`. Before, the session accepted the command and the member in `character_id` drank the potion, so a front end that expected the target to drink got no warning. A `target_id` of `None`, or the id of the member using the potion, drinks the potion as before, and a healing device still takes another member (#174). - A plain [`Rest`][osrlib.crawl.commands.Rest] in a dungeon now checks for wandering monsters at the level's full chance. Before, every rest took 1 off the chance, so a level with the usual 1 in 6 chance never rolled during a rest and resting was always safe. The session still adds 1 for noise and 1 for bright light, and a rest of a night or a day still checks once each turn and stops when monsters come. To rest quietly and take the 1 off, set the new `hidden` field. The `resting` keyword of [`wandering_check`][osrlib.crawl.exploration.wandering_check] still takes 1 off the chance. [`GameSession.advance_turns`][osrlib.crawl.session.GameSession.advance_turns] now sets it from `hidden`, so `advance_turns(resting=True)` alone no longer lowers the chance. It still counts the turns as rest. -- A party member can make a fighting withdrawal or a retreat alone, to pull a badly hurt character out of the front rank without the whole party leaving the fight (#171). Before, [`ResolveBattleRound`][osrlib.crawl.commands.ResolveBattleRound] refused a round in which only some of the members it expects to declare chose one of these moves, with `battle.declaration.formation_split`. That code is gone. A defensive move that every declarer makes still moves the whole party, as before. Any other is the declaring member's own. The member has to stand in the front rank, and steps back to trade places in the marching order with the member named in `replacement_id`. The party doesn't move and the battle goes on. The new order stays after the battle, until a [`ReorderParty`][osrlib.crawl.commands.ReorderParty] sets it again, and the session still refuses `ReorderParty` during a battle. Each rank stands 5 feet behind the one in front of it, and both members have to cover that distance. The withdrawing member covers it at up to half their encounter movement rate for a fighting withdrawal, or their full rate for a retreat. The replacement covers it at up to their full rate. *Haste* doubles either limit for the member who has it. The session refuses a member behind the front rank with `battle.declaration.not_in_front_rank`. It refuses a member who names no replacement, a replacement who can't step up, and a replacement too far back with three new codes: `battle.declaration.no_replacement`, `battle.declaration.replacement_unavailable`, and `battle.declaration.replacement_too_far`. The swap happens in the party's movement step, so monsters that win initiative strike at the ranks as they were declared. From the swap on, melee no longer reaches the member who stepped back. The replacement may declare a melee attack in the round they step up, and it resolves only if the swap happens. A member who retreats alone is easier to hit for the whole round: attackers get +2 against them and ignore their shield. A refused round costs no draw and no time, and a swap makes no draw. -- 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. [`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, isn't identified, and doesn't break the user's invisibility. 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 don't replay under this release. A log that contains a command this release refuses makes [`replay_game`][osrlib.persistence.replay_game] raise [`ContentValidationError`][osrlib.errors.ContentValidationError]. Two commands that 2.0.1 accepted are refused now, so a log that contains one of them raises: a potion `UseItem` aimed at another member, and a temple service that would do nothing. Other logs replay without an error but play out differently. That happens with *sleep* cast at a drained monster, a *confusion* re-save, a protection scroll, or the 5 Hit Dice rule met with a drained monster, a *death spell* cast at a group, a lair the party left and came back to, a rest in a dungeon, a rest that a wandering monster interrupted, a trap save with a `modifier`, or a chute trap where every member passed the save. In battle, it also happens with a monster that regenerates (a troll, a thoul, or a vampire), a monster whose attack has an effect (like a ghoul's paralysing claw or a wight's level drain), a basilisk's or a medusa's gaze, including a basilisk that surprises the party, a melee attack on a petrified creature, a monster that can choose between attack routines (a spitting cobra, a thoul, a mule, a minotaur, a wererat, or a monster of your own with more than one unlabelled routine), an attack declared with no `weapon_id` by a member who wields a weapon, or a device use (a wand, staff, or rod) or a turning that now fizzles. It also happens in 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. Each of these changes has an entry in this section or under Fixed. 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. +- A party member can make a fighting withdrawal or a retreat alone, to pull a badly hurt character out of the front rank without the whole party leaving the fight (#171). Before, [`ResolveBattleRound`][osrlib.crawl.commands.ResolveBattleRound] refused a round in which only some of the members it expects to declare chose one of these moves, with `battle.declaration.formation_split`. That code is gone, so a game that handled it should handle the three new codes instead: `battle.declaration.no_replacement`, `battle.declaration.replacement_unavailable`, and `battle.declaration.replacement_too_far`. A defensive move that every declarer makes still moves the whole party, as before. Any other fighting withdrawal or retreat is that member's alone. The member has to stand in the front rank, and steps back to trade places in the marching order with the member named in `replacement_id`. The party doesn't move and the battle goes on. The new order stays after the battle, until a [`ReorderParty`][osrlib.crawl.commands.ReorderParty] sets it again, and the session still refuses `ReorderParty` during a battle. Each rank stands 5 feet behind the one in front of it, and both members have to cover that distance. The withdrawing member covers it at up to half their encounter movement rate for a fighting withdrawal, or their full rate for a retreat. The replacement covers it at up to their full rate. *Haste* doubles either limit for the member who has it. The session refuses a member behind the front rank with `battle.declaration.not_in_front_rank`. It refuses a member who names no replacement, a replacement who can't step up, and a replacement too far back with three new codes: `battle.declaration.no_replacement`, `battle.declaration.replacement_unavailable`, and `battle.declaration.replacement_too_far`. The swap happens in the party's movement step, so monsters that win initiative strike at the ranks as they were declared. From the swap on, melee no longer reaches the member who stepped back. The replacement may declare a melee attack in the round they step up, and it resolves only if the swap happens. A member who retreats alone is easier to hit for the whole round: attackers get +2 against them and ignore their shield. A refused round costs no draw and no time, and a swap makes no draw. +- 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 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. + - A trap save with a `modifier`, or a chute trap where every member passed the save. + - A drained monster met by *sleep*, a *confusion* re-save, a protection scroll, or the 5 Hit Dice rule. + - A *death spell* cast at a group. + - A monster that regenerates (a troll, a thoul, or a vampire). + - A monster whose attack has an effect, like a ghoul's paralysing claw or a wight's level drain. + - A basilisk's or a medusa's gaze, including a basilisk that surprises the party. + - A melee attack on a petrified creature. + - A monster that can choose between attack routines: a spitting cobra, a thoul, a mule, a minotaur, a wererat, or a monster of your own with more than one unlabelled routine. + - 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. ### Fixed - A retreat by the whole party now makes its members easier to hit for the whole round, as the OSE SRD's retreat says: "This round: ... the opponent gains a +2 bonus to all attacks against the retreating combatant and ignores any AC bonus due to the combatant's shield". Before, the penalty started only when the party moved off, so monsters that won initiative and attacked first got no bonus and counted the shield. A confused monster's attack never got it. Now every attack the monster side makes on the party in that round gets +2 and ignores the shield, whichever side acts first. - A ring of regeneration no longer heals fire or acid damage, as the ring's entry in the OSE SRD says. Before, the ring healed every kind of damage at 1 hit point a round. Now [`deal_damage`][osrlib.core.combat.deal_damage] records fire and acid damage to the wearer in the new field [`Character.nonregen_damage`][osrlib.core.character.Character.nonregen_damage], and the ring heals the wearer only up to their maximum hit points less that damage. The ring still heals the rest, and a potion or a cure spell still heals all of it. [`DamageDealtEvent`][osrlib.core.events.DamageDealtEvent]`.non_regenerable` is now True when fire or acid hits a wearer. The new field defaults to 0, so a save from before this change still loads and [`SCHEMA_VERSION`][osrlib.versioning.SCHEMA_VERSION] stays 4. Saves and party documents now include the field, so the golden files that contain characters were regenerated to add it. - *Sleep* judges a drained monster by the Hit Dice it has left, because energy drain takes Hit Dice away. Before, both of the spell's modes read the monster's template. The single-creature mode takes only a creature of exactly 4+1 Hit Dice: 4 Hit Dice with a bonus, no more and no fewer. An ogre drained from 4+1 to 3+1 Hit Dice is no longer a 4+1 creature, so this mode now has no effect on it. Before, the mode put that ogre to sleep. The 2d8 Hit Dice mode takes creatures of 4 Hit Dice or fewer and leaves out 4+1 creatures. It now takes the drained ogre for 3 Hit Dice of its budget. Before, it left the ogre out as a 4+1 creature. This fix covers the 4+1 test in both modes. The [`effective_hd`][osrlib.core.combat.effective_hd] fix below covers the rest of the 2d8 mode: what each creature costs from the budget, and whether it's under the 4 Hit Dice limit. With that fix, a 5 Hit Dice grizzly bear drained to 4 is eligible for the 2d8 mode too. -- Three more rules judge a drained monster by the Hit Dice it has left, because energy drain takes Hit Dice away. Before, each used the monster's template. *Confusion*'s re-save each round uses the drained count, so a 3 Hit Dice boar drained to 2 Hit Dice is a subject of 2 Hit Dice and no longer saves. The Hit Dice bands of the protection scrolls use it too, so a 4 Hit Dice wraith drained to 3 Hit Dice counts in the 1 to 3 Hit Dice band of a scroll of protection from undead. Last, the optional rule that lets a monster of 5 Hit Dice or more harm a creature that only magic or silver can hurt uses it, so a grizzly drained from 5 to 4 Hit Dice no longer qualifies. This is [`MonsterInstance.hit_dice_count`][osrlib.core.monsters.MonsterInstance.hit_dice_count], the same count that sets its THAC0 and saves. The turning table and a monster's experience value still use the template, because the OSE SRD ties them to the kind of monster. -- *Death spell* cast at a group with `target_group_id` now takes only the creatures its 60' cube covers. Before, the cast took every living member of the group. The cast now uses the same footprint as any other area spell in [`BattleDeclaration`][osrlib.crawl.commands.BattleDeclaration]: the group's living members in spawn order up to the footprint's capacity, then the party's front rank when `aoe_friendly_fire` applies. A cast with named `targets` still uses those targets. *Sleep* has no area in the OSE SRD, so it still takes every living member of the group. +- Three more rules judge a drained monster by the Hit Dice it has left, because energy drain takes Hit Dice away. Before, each used the monster's template. *Confusion*'s re-save each round uses the drained count, so a 3 Hit Dice boar drained to 2 Hit Dice is a subject of 2 Hit Dice and no longer gets the re-save. The Hit Dice bands of the protection scrolls use it too, so a 4 Hit Dice wraith drained to 3 Hit Dice counts in the 1 to 3 Hit Dice band of a scroll of protection from undead. Last, the optional rule that lets a monster of 5 Hit Dice or more harm a creature that only magic or silver can hurt uses it, so a grizzly drained from 5 to 4 Hit Dice no longer qualifies. The count these rules use is [`MonsterInstance.hit_dice_count`][osrlib.core.monsters.MonsterInstance.hit_dice_count], the same count that sets the monster's THAC0 and saves. The turning table and a monster's experience value still use the template, because the OSE SRD ties them to the kind of monster. +- *Death spell* cast at a group with `target_group_id` now takes only the creatures its 60' cube covers. Before, the cast took every living member of the group. The cast now uses the same footprint as any other area spell in [`BattleDeclaration`][osrlib.crawl.commands.BattleDeclaration]: the group's living members in spawn order up to the footprint's capacity, then the party's front rank when the `aoe_friendly_fire` ruleset option is on, the group is within 5 feet, and the cube has room left. A cast with named `targets` still uses those targets. *Sleep* has no area in the OSE SRD, so it still takes every living member of the group. - When a wandering monster interrupts a rest, the full turns the party rested before the monsters came now count (#169). Before, a 48-turn night broken after four full turns cleared neither fatigue nor exhaustion. Now one full turn of rest clears fatigue. Each full turn also counts toward the three that clear exhaustion from a chase, and the next rest adds to that count. A rest interrupted during its first turn still clears nothing. An interrupted night still isn't sleep for preparing spells, and an interrupted day still heals nobody. The fix makes no new draws, and no golden file changed. - Trolls, thouls, and vampires regenerate in play (#167). Until this release a spawned one never healed, though its template had the ability. A monster whose template has the `regeneration` ability now starts to regenerate when its encounter opens. That's true whether the monster came from a [`SpawnMonsters`][osrlib.crawl.commands.SpawnMonsters] command, a keyed area, or a wandering-monster check. The command that opened the encounter reports an [`EffectAttachedEvent`][osrlib.core.events.EffectAttachedEvent] for each such monster. A hurt troll gets back 3 hit points a round, starting 3 rounds after it last took damage. It never gets back damage from fire or acid. A troll slain while its encounter is still running gets back up 2d6 rounds later and fights again. It stays dead if fire and acid alone did as much damage as its maximum hit points. The session rolls the revival delay on the effects stream. A thoul or a vampire heals every round while it lives. The session releases the effect when the encounter ends, so a troll that is still dead then stays dead. A game recorded under 2.0.1 that met one of these monsters replays differently under this release. No golden file changed. - Undead that a cleric turns now flee and attack no one, even when the cleric turns only some of a group (#168). Before, the turned members of a partly turned group kept closing and attacking, the cleric who turned them included. Now they break away as a new group, which runs from the battle the way a wholly turned group does. The session reports the split with a [`MonsterFledEvent`][osrlib.crawl.events.MonsterFledEvent] whose `group_id` is the new group's id. The members who weren't turned stay in the original group and fight on, and a policy you registered for that group still chooses their actions. The group's shared treasure stays with the members who fight on, and each turned member takes only the treasure it carries. A turned monster that can't move, such as one caught in a *web*, stays where it is and does nothing. The session skips any action a policy returns for a turned monster. A wholly turned group behaves as before, and no golden file changed. -- When a monster's attack hits in battle, the session applies the effects the attack lists (#167). A ghoul's or a thoul's claw paralyses for 2d4 turns unless the target saves versus paralysis. An elf isn't affected. A carcass crawler's tentacle and a gelatinous cube's touch paralyse the same way, and they affect elves too. A poisonous bite or sting kills unless the target saves versus death. The target dies at once, or when the poison's onset runs out if the monster's poison has one. The session records the death as a poison death, so *neutralize poison* can revive the character. A mummy's touch gives mummy rot, with no save. A wight's, a wraith's, a spectre's, or a vampire's touch drains levels, also with no save. A basilisk's bite and a cockatrice's beak turn the target to stone unless it saves versus petrification. The basilisk's and the medusa's petrifying gaze reaches each character in the party's front rank once a round while the gazer is in melee. The gaze takes no attack roll, and the session no longer rolls the basilisk's gaze as an attack. A character with the `averted_eyes` condition doesn't meet the gaze, and a member who declares the new `gaze_defense` field, described under Added, doesn't meet it either. The adaptations register lists the attack effects that the session leaves to the referee. In a battle against one of these monsters, the session now makes more draws from the combat, effects, and advancement streams. So the same seed and commands give a different battle than they gave under 2.0.1. No golden file changed. -- A basilisk that surprises the party now gazes at every member in its free round, as the OSE SRD's basilisk says: "Characters surprised by a basilisk meet its gaze." The gaze reaches the back rank too, at whatever distance the encounter opened, and each member saves versus paralysis, the save that also covers petrification, or turns to stone. The gaze takes the place of the round's front-rank gaze, so no member meets it twice. Before, the basilisk gazed in its free round only when it was already in melee, and then only at the front rank. A medusa's free round is unchanged, because her entry has no such rule. The basilisk's `surprise` ability is no longer `manual`, and its `params` have `meets_gaze` set to `true`. -- Battle uses every attack routine a monster has. Before, it used only the first routine in the monster's stat block, so a thoul never used a weapon, a mule never bit, and an elephant never trampled, and an effect in a later routine never applied. Now the default policy, [`ScriptedPolicy`][osrlib.crawl.battle.ScriptedPolicy], picks a routine for each melee attack, with equal chance among the first routine and the alternatives the OSE SRD offers. The spitting cobra spits or bites, and its bite poisons. The thoul claws or swings a weapon, the mule kicks or bites, the minotaur gores and bites or swings a weapon, and the wererat bites or uses a weapon. The policy never picks a situational routine, like an elephant's trample. A policy of your own can name one, and battle then rolls it as a melee attack. Battle never rolls a breath, a gaze, or a spell as an attack, even when a policy names one. The policy draws the pick from the monster-action stream, and only for a monster that has a choice. Every other monster draws as before. +- When a monster's attack hits in battle, the session applies the effects the attack lists (#167). A ghoul's or a thoul's claw paralyses for 2d4 turns unless the target saves versus paralysis. An elf isn't affected. A carcass crawler's tentacle and a gelatinous cube's touch paralyse the same way, and they affect elves too. A poisonous bite or sting kills unless the target saves versus death. The target dies at once, or when the poison's onset runs out if the monster's poison has one. The session records the death as a poison death, so *neutralize poison* can revive the character. A mummy's touch gives mummy rot, with no save. A wight's, a wraith's, a spectre's, or a vampire's touch drains levels, also with no save. A basilisk's bite and a cockatrice's beak turn the target to stone unless it saves versus petrification. The basilisk's and the medusa's petrifying gaze reaches each character in the party's front rank once a round while the gazer is in melee. The gaze takes no attack roll, and the session no longer rolls the basilisk's gaze as an attack. A character with the `averted_eyes` condition doesn't meet the gaze, and a member who sets the new [`BattleDeclaration.gaze_defense`][osrlib.crawl.commands.BattleDeclaration.gaze_defense] field doesn't meet it either. The adaptations register lists the attack effects that the session leaves to the referee. In a battle against one of these monsters, the session now makes more draws from the combat, effects, and advancement streams. So the same seed and commands give a different battle than they gave under 2.0.1. No golden file changed. +- A basilisk that surprises the party now gazes at every member in its free round, as the OSE SRD's basilisk says: "Characters surprised by a basilisk meet its gaze." The gaze reaches the back rank too, at whatever distance the encounter opened, and each member makes a saving throw versus paralysis, which in B/X also covers petrification, or turns to stone. The gaze takes the place of the round's front-rank gaze, so no member meets it twice. Before, the basilisk gazed in its free round only when it was already in melee, and then only at the front rank. A medusa's free round is unchanged, because her entry has no such rule. In the monster catalog, the basilisk's `surprise` ability is no longer `manual`, and its `params` include `meets_gaze: true`. You don't need to set either. +- Battle now uses a monster's other attack routines, not only the first. Before, it used only the first routine in the monster's stat block, so a thoul never used a weapon, a mule never bit, and an elephant never trampled, and an effect in a later routine never applied. Now the default policy, [`ScriptedPolicy`][osrlib.crawl.battle.ScriptedPolicy], picks a routine for each melee attack, with equal chance among the first routine and the alternatives the OSE SRD offers. Each round, the spitting cobra either spits or bites, and its bite poisons. The thoul either claws or swings a weapon, the mule kicks or bites, the minotaur gores and bites or swings a weapon, and the wererat bites or uses a weapon. The policy never picks a situational routine, like an elephant's trample. A policy of your own can name one, and battle then rolls it as a melee attack. Battle never rolls a breath, a gaze, or a spell as an attack, even when a policy names one. The policy draws the pick from the monster-action stream, and only for a monster that has a choice. Every other monster draws as before. - A trap's saving throw now adds the `modifier` of the trap's [`SaveSpec`][osrlib.core.spells.SaveSpec], as a spell's save does. Before, the session left the modifier out, so a trap you wrote with a bonus or a penalty on its save played as if it had none. The modifier changes whether the save passes, and the dice the session rolls are the same as before (#167). - Lighting the last oil flask in a pile on the floor with [`LightSource`][osrlib.crawl.commands.LightSource] now leaves the pile when gems, jewellery, or magic items are still in it. Before, the session removed the pile once it had no ordinary items and no coins, which deleted any gems, jewellery, or magic items still in it (#167). - When a breath weapon or *disintegrate* kills a party member, the magic items that make their saving throw now go to a drop pile on the party's cell in the same command. The party picks them up with [`TakeTreasure`][osrlib.crawl.commands.TakeTreasure] and the feature id `"pile"`. Before, the items stayed in the dead member's inventory, and the commands that hand over or drop an item refuse a dead member, so the party couldn't recover them. [`EquipmentDestroyedEvent`][osrlib.core.events.EquipmentDestroyedEvent]`.saved_items` still lists them, and the draws are unchanged (#167).