fix: prevent duplicate cross-client defense dialog, clear bleed on heal
- Only send attackBoosted socket when attackerHandledBonus || attackerHasNonGMOwner (GM→player: hook handles it, no socket needed; PC→PC: socket needed) - Clear bleeding wounds when HP restored via token HUD heal buttons
This commit is contained in:
+31
-21
@@ -1003,31 +1003,41 @@ Hooks.on("createChatMessage", async (message) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Cross-client coordination: delegate the remaining reaction + message
|
||||
// to the defender's controller via socket. Only the attacker's owning
|
||||
// client sends — preventing duplicate emissions from other clients.
|
||||
// Cross-client coordination: only delegate to the defender's client
|
||||
// when the attacker boosted past the defense. When no attacker boost
|
||||
// occurred, the defender's client already processed the defense via
|
||||
// the createChatMessage hook and will create the correct comparison.
|
||||
// Sending attackBoosted with stale (unboosted) values would cause
|
||||
// the defender to see a duplicate dialog and overwrite the result.
|
||||
if (defender && isPrimaryController(attacker)) {
|
||||
const defenderOwner = game.users.find(u => u.active && !u.isGM && defender.testUserPermission(u, "OWNER"))
|
||||
|| game.users.find(u => u.active && u.isGM)
|
||||
if (defenderOwner && defenderOwner.id !== game.user.id) {
|
||||
const sData = LethalFantasyUtils.getShieldReactionData(defender)
|
||||
game.socket.emit(`system.${SYSTEM.id}`, {
|
||||
type: "attackBoosted",
|
||||
userId: defenderOwner.id,
|
||||
attackerName, attackerId, defenderName, defenderId, defenderTokenId,
|
||||
attackerHandledBonus, attackRollFinal, defenseRoll, attackWeaponId, attackRollType, attackRollKey,
|
||||
shieldDamageReduction: shieldBlocked ? shieldReaction?.damageReduction ?? 0 : 0,
|
||||
d30Bleed: d30Bleed ? "true" : "",
|
||||
d30DamageMultiplier, d30DrMultiplier,
|
||||
damageTier: damageTier || "standard",
|
||||
attackD30message,
|
||||
defenseD30message,
|
||||
hasShield: !!sData,
|
||||
shieldLabel: sData?.label || "",
|
||||
shieldFormula: sData?.formula || "",
|
||||
shieldDr: sData?.damageReduction || 0,
|
||||
canAdHocShield: !sData,
|
||||
})
|
||||
// Send attackBoosted when the attacker actually boosted (so defender
|
||||
// can respond to the new numbers), OR when the attacker has an active
|
||||
// non-GM owner (PC-vs-PC cross-client) — the defender's hook-based
|
||||
// processing is suppressed by attackerIsCrossClient, so the socket
|
||||
// handler must show the defense dialog instead.
|
||||
if (attackerHandledBonus || attackerHasNonGMOwner) {
|
||||
const sData = LethalFantasyUtils.getShieldReactionData(defender)
|
||||
game.socket.emit(`system.${SYSTEM.id}`, {
|
||||
type: "attackBoosted",
|
||||
userId: defenderOwner.id,
|
||||
attackerName, attackerId, defenderName, defenderId, defenderTokenId,
|
||||
attackerHandledBonus, attackRollFinal, defenseRoll, attackWeaponId, attackRollType, attackRollKey,
|
||||
shieldDamageReduction: shieldBlocked ? shieldReaction?.damageReduction ?? 0 : 0,
|
||||
d30Bleed: d30Bleed ? "true" : "",
|
||||
d30DamageMultiplier, d30DrMultiplier,
|
||||
damageTier: damageTier || "standard",
|
||||
attackD30message,
|
||||
defenseD30message,
|
||||
hasShield: !!sData,
|
||||
shieldLabel: sData?.label || "",
|
||||
shieldFormula: sData?.formula || "",
|
||||
shieldDr: sData?.damageReduction || 0,
|
||||
canAdHocShield: !sData,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
// Same client: restart for defender loop if attacker boosted past defense
|
||||
|
||||
Reference in New Issue
Block a user