Skip to content

Default an attack to the wielded weapon and re-check device uses - #193

Merged
mmacy merged 5 commits into
mainfrom
chunk/weapon-and-device
Oct 30, 2026
Merged

mmacy merged 5 commits into
mainfrom
chunk/weapon-and-device

Conversation

@mmacy

@mmacy mmacy commented Oct 30, 2026

Copy link
Copy Markdown
Owner

Two battle-round fixes for front ends that drive ResolveBattleRound.

An attack with no weapon_id uses the wielded weapon (#162 item 18)

A member holding a sword struck unarmed when the declaration left out weapon_id. Now the session picks the wielded weapon that suits the attack, when it accepts the round:

  • The group's distance sets the kind of attack: melee at 5 feet or less, missile beyond, as the OSE SRD's weapon qualities define them.
  • The session takes the first suitable weapon in equip order. For a missile attack it prefers the first whose long range reaches the group.
  • A real weapon comes before an improvised one, so a member who lit a torch and then drew a sword swings the sword. A reviewer found that case, and I added the tests for it.
  • Splash weapons and items with no weapon stats, like a wand, don't count. With nothing suitable, the member strikes unarmed as before.

The round plays out exactly as if the declaration had named the chosen weapon, and most tests check that equivalence. The command log records None, and replay makes the same choice.

Device uses and turnings are checked again in the magic phase (#145)

A wand, staff, or rod use and a turning now go through the same re-check as a spell cast and a scroll read, just before they resolve. One that fails fizzles and reports a new DeclarationFizzledEvent (magic.device.fizzled or magic.turning.fizzled). A fizzled device keeps its charge, isn't identified, and doesn't break invisibility. A fizzled turning draws nothing.

I added a new event instead of reusing SpellDisruptedEvent, because putting an item id or turn_undead in its spell_id would change what that field means, which schema_version 4 doesn't allow.

What to check

  • No golden file changed.
  • The fizzle message names the device by its instance id, as items.device.activated does, so it doesn't reveal an unidentified device.
  • The TUI example still has its own weapon-picking helper. I left it, because removing it could change the example's captured transcript.

Fixes #145.

https://claude.ai/code/session_01V7ErtnsmhaP7aLwcfmzohD

mmacy added 5 commits October 30, 2026 14:13
Contract stubs and acceptance tests for two owner decisions. The
engine's behavior doesn't change yet.

An attack with no weapon_id now means the wielded weapon that suits
the attack (#162, item 18). The group's distance when the round is
accepted decides the attack: melee at 5 feet or less, missile beyond.
The session takes the first wielded item, in equip order, with that
quality. It prefers a missile weapon whose long range reaches the
group, skips splash weapons and items with no weapon stats, and falls
back to bare hands. The choice holds for the whole round, so the round
plays out as if the chosen weapon had been named.

A device use and a turning are judged again in the magic phase, as a
cast and a scroll read are (#145). One that now fails fizzles with a
new event, DeclarationFizzledEvent. Its codes are
magic.device.fizzled and magic.turning.fizzled, and it carries
actor_id, item_id (the declared instance id, or None for a turning),
and reason (the re-check's first rejection code). SpellDisruptedEvent
stays for spells and scrolls, so its spell_id always names a spell. A
fizzled device keeps its charge and costs what a hold costs.

The new event is registered in KERNEL_EVENT_CLASSES and KernelEvent,
with a message template for each code and a registry sample in
test_events_kernel.py. These docstrings state the contract:
BattleDeclaration.weapon_id, _party_magic, and the two validators. The
acceptance tests in tests/test_weapon_default_and_device_recheck.py
are marked xfail for chunk weapon-and-device. The file's unmarked
classes pin behavior that stays the same.

Claude-Session: https://claude.ai/code/session_01V7ErtnsmhaP7aLwcfmzohD
An attack declared with no weapon_id now uses the wielded weapon that
suits the attack (#162, item 18). The session chooses it once, when it
accepts the round, from the group's distance: the first wielded item in
equip order with the melee or missile quality, preferring a missile
weapon whose long range reaches the group, skipping splash weapons and
items with no weapon stats, and falling back to bare hands. The chosen
id replaces None in the round's working declarations, so validation,
initiative, and every phase read it as if it had been named. The
command log keeps the declaration as sent.

A wand, staff, or rod use and a turning are judged again in the magic
phase, as a cast and a scroll read are (#145). One that now fails
fizzles with DeclarationFizzledEvent and the re-check's first rejection
code as its reason. A fizzled device spends no charge, isn't
identified, and breaks no invisibility. A fizzled turning draws
nothing. The re-check takes no draw, so a declaration that passes it
resolves with the same draws as before.

The ResolveBattleRound example relies on the default weapon instead of
a helper, and prints the same result. The adaptations register and the
Unreleased changelog describe both rules. No golden file changed.

Claude-Session: https://claude.ai/code/session_01V7ErtnsmhaP7aLwcfmzohD
A member who lights a torch and then draws a sword is the usual dungeon
setup, and the default attack should swing the sword.

Claude-Session: https://claude.ai/code/session_01V7ErtnsmhaP7aLwcfmzohD
A member who lights a torch and then draws a sword swung the torch,
because the torch was equipped first. Among the wielded items that suit
the attack, a real weapon now comes before an improvised one, a gear
item whose stats come from its combat facet, like the torch. Equip
order holds within each kind, and the torch still counts when nothing
else suits the attack. The weapon_id docstring, the adaptations
register, and the changelog bullet say the same. No golden file
changed.

Claude-Session: https://claude.ai/code/session_01V7ErtnsmhaP7aLwcfmzohD
@mmacy
mmacy merged commit 8991b5d into main Oct 30, 2026
5 checks passed
@mmacy
mmacy deleted the chunk/weapon-and-device branch October 30, 2026 21:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Judge a device use_item declaration again in the magic phase, as a cast and a scroll read are

1 participant