- Fix handleAttackBoosted guard: replaced attackerIsCrossClient check
with defenderIsMine so the socket handler runs whenever the current
user controls the defender. Previously, when a GM-controlled monster
attacked with a D30 special result (damage multiplier, bleed, DR
multiplier, mulligan), the createChatMessage hook was suppressed
(d30PendingFromGM) but handleAttackBoosted returned early because
the attacker wasn't a cross-client PC, so nobody created the
comparison message.
- Remove map auto-pan on target/damage button hover (mouseenter/mouseleave
handlers on .request-defense-btn and .apply-wounds-btn).
- Filter defeated combatants and dead monster tokens from the target
selection list. Characters at 0 HP are still selectable (death saves).
- Restore 3D dice animation for damage rolls (lethal-fantasy.mjs,
character-sheet.mjs).
- Allow negative bonuses on weapon items (damaged weapons).
- Allow negative HP for characters (death saving throws).
- Fix D30Roll.convertToInternalType guard to warn on missing weapon
regardless of isRanged flag.
- Fix ranged weapon loading comparison (<= to <).
- Add isRangedAttack to rollTarget for consistent ranged detection.
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
Any exception from createReactionMessage or renderTemplate inside the
defense/attack reaction loops would silently abort the handler, preventing
combat result creation. The try/catch logs the error and continues safely.
Even with the partial correctly registered, a template rendering failure
should not abort the entire reroll handler. Without try/catch, the
exception propagates out of the do-while loop and prevents combat result
creation. The reroll value was applied but nothing else happened.
D30 bonus dice now rolled at defense request time, not in defense handler.
Sends boosted attackRoll + d30AttackEffects in socket/nextDefenseData
so both clients agree on the value. Adds stale flag for mulligan rerolls
to bypass stale pre-computed effects and process the new D30 fresh.
When monster (GM) rolls D30 natural 30 and applies bonus dice
(eg +D20), the boosted attackRollFinal was only applied on GM's
client. Defender (player) saw stale unboosted values — defense
dialog never appeared.
Add d30RequiresSocket flag (GM side): always send attackBoosted
socket when GM-owned attacker has D30 data, because the D30
choice/dice result is only known on GM's client.
Add d30PendingFromGM flag (player side): suppress hook-based
defense dialog and comparison message creation when D30 data
needs GM-side processing. Socket handler (handleAttackBoosted)
shows dialog with correct values.
Track d30ChangedAttack for same-client restart logic.