Skip to content

A 'withdraw' battle declaration validates and then does nothing — the party stands still while declaring it #74

Description

@mmacy

Observed from osr-web while a party tried to escape a green slime: every member declared the client's "Fall back" (which sends move: "withdraw"), the round resolved with zero party movement, and the slime closed its 1 foot per round until the fight was hopeless. A declaration the validator accepts should move the game; this one is silently inert.

The defect

BattleDeclaration.move admits four values: close, withdraw, fighting_withdrawal, retreat. The validator (_validate_declaration, crawl/battle.py:650) accepts any non-None move, but _party_movement (crawl/battle.py:1202) branches only on all-retreat, all-fighting_withdrawal, and close — a withdraw declaration matches nothing and the formation moves zero feet. Three signs it is vestigial:

  • The OSE SRD (srd/Combat.md) defines exactly two defensive moves: fighting withdrawal (up to half encounter rate, clear space behind) and retreat (full encounter rate; no attack that round; +2 to be hit; shield bonus ignored). withdraw corresponds to no rule.
  • The adaptations register's battle entry names only all-retreat, all-fighting-withdrawal, and close.
  • No test exercises it beyond test_crawl_properties.py:174 sampling it into generated declarations — where it silently no-ops.

The _party_movement docstring compounds it: "every member withdrawing backs off at half encounter rate" describes the fighting_withdrawal branch while reading as if it covers withdraw.

What already works — this is a deletion, not a build-out

The flee and pursuit chain per the SRD is otherwise in place: all-retreat moves the formation at full encounter rate and ends the battle with battle.ended.fled; the retreating party takes the SRD's defense penalty (defender_retreating at crawl/battle.py:2065, +2 and shield ignored in core/combat.py:705/785); the conversion hands off to the encounter procedure's evasion-by-speed check, pursuit gap, treasure/food drops (3-in-6 by intelligence), and the 30-round exhaustion valve (PURSUIT_ROUND_CAP).

Proposed

  • Delete withdraw from the BattleDeclaration.move Literal and from the property-test sample list — no dead code, no vestigial remnants.
  • Reword the _party_movement docstring so "withdrawing" unambiguously means the fighting-withdrawal branch.
  • One best-effort question to settle while in here: the SRD's fighting withdrawal permits attacking while backing off, but under the formation adaptation a move declaration excludes attacking, making all-fighting-withdrawal a pure move. Either express the attack-while-withdrawing half (best-effort representation is the mantra) or pin the simplification explicitly in the adaptations register — today the register describes the movement without owning the no-attack consequence.

Downstream: osr-web's battle menu maps "Fall back" to withdraw today; deleting the value turns that into a hard rejection, so the client change should land with or before the pin bump (issue to follow in osr-web).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions