Compare commits

..
3 Commits
Author SHA1 Message Date
uberwald a04f94b728 Sync compendiums 2026-08-19 00:14:38 +02:00
uberwald fc5d38deff fix: dead combatants reappearing as targets via scene token fallback
Release Creation / build (release) Successful in 54s
The combat loop correctly skipped defeated combatants, but their token
IDs were absent from existingTokenIds, so the scene token loop re-added
them. Now building a defeatedTokenIds set from defeated combatants and
checking it in the scene token loop. Also switched from
game.actors.get() to token.actor for the monster HP check so unlinked
monster tokens (synthetic actors) are covered.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-08-18 18:38:10 +02:00
uberwald f47f2906a4 fix: D30 special effects breaking combat comparison, map auto-pan, dead targets
Release Creation / build (release) Successful in 1m35s
- 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>
2026-08-17 20:12:12 +02:00
26 changed files with 88 additions and 89 deletions
+9 -2
View File
@@ -161,11 +161,15 @@ export default class LethalFantasyRoll extends Roll {
let isAttack = this.type === "weapon-attack" || this.type === "monster-attack" || this.type === "spell-attack" || this.type === "miracle-attack" let isAttack = this.type === "weapon-attack" || this.type === "monster-attack" || this.type === "spell-attack" || this.type === "miracle-attack"
if (this.rollData?.isDamage || isAttack) { if (this.rollData?.isDamage || isAttack) {
// D'abord, ajouter les combattants du combat actif // D'abord, ajouter les combattants du combat actif
const defeatedTokenIds = new Set()
if (game?.combat?.combatants) { if (game?.combat?.combatants) {
for (let c of game.combat.combatants) { for (let c of game.combat.combatants) {
if (c.actorId !== this.actorId) { if (c.actorId !== this.actorId && !c.isDefeated) {
combatants.push({ id: c.id, name: c.name, tokenId: c.token.id }) combatants.push({ id: c.id, name: c.name, tokenId: c.token.id })
} }
if (c.isDefeated && c.token?.id) {
defeatedTokenIds.add(c.token.id)
}
} }
} }
@@ -173,7 +177,10 @@ export default class LethalFantasyRoll extends Roll {
if (canvas?.scene?.tokens) { if (canvas?.scene?.tokens) {
const existingTokenIds = new Set(combatants.map(c => c.tokenId)) const existingTokenIds = new Set(combatants.map(c => c.tokenId))
for (let token of canvas.scene.tokens) { for (let token of canvas.scene.tokens) {
if (token.actorId !== this.actorId && !existingTokenIds.has(token.id)) { if (token.actorId !== this.actorId && !existingTokenIds.has(token.id) && !defeatedTokenIds.has(token.id)) {
// Skip dead monsters (0 HP). Use token.actor for unlinked tokens.
const tokenActor = token.actor ?? game.actors.get(token.actorId)
if (tokenActor?.type === "monster" && (Number(tokenActor?.system?.hp?.value) || 0) <= 0) continue
combatants.push({ combatants.push({
id: token.id, id: token.id,
name: token.name, name: token.name,
-26
View File
@@ -33,38 +33,12 @@ Hooks.on("renderChatMessageHTML", (message, html, data) => {
} }
for (const btn of html.querySelectorAll(".apply-wounds-btn")) { for (const btn of html.querySelectorAll(".apply-wounds-btn")) {
btn.addEventListener("mouseenter", () => {
const combatantId = btn.dataset.combatantId
if (combatantId && game.combat) {
const combatant = game.combat.combatants.get(combatantId)
if (combatant?.token) {
const token = canvas.tokens.get(combatant.token.id)
if (token) {
token.control({ releaseOthers: true })
canvas.animatePan(token.center)
}
}
}
})
btn.addEventListener("mouseleave", () => canvas.tokens.releaseAll())
btn.addEventListener("click", event => LethalFantasyUtils.applyDamage(message, event)) btn.addEventListener("click", event => LethalFantasyUtils.applyDamage(message, event))
} }
} }
// Gestion du survol et du clic sur les boutons de défense // Gestion du survol et du clic sur les boutons de défense
for (const btn of html.querySelectorAll(".request-defense-btn")) { for (const btn of html.querySelectorAll(".request-defense-btn")) {
btn.addEventListener("mouseenter", () => {
const tokenId = btn.dataset.tokenId
if (tokenId) {
const token = canvas.tokens.get(tokenId)
if (token) {
token.control({ releaseOthers: true })
canvas.animatePan(token.center)
}
}
})
btn.addEventListener("mouseleave", () => canvas.tokens.releaseAll())
// Gestionnaire pour les boutons de demande de défense // Gestionnaire pour les boutons de demande de défense
btn.addEventListener("click", async event => { btn.addEventListener("click", async event => {
event.preventDefault() event.preventDefault()
+10 -8
View File
@@ -108,20 +108,22 @@ export async function handleAttackBoosted(msg) {
const attacker = game.actors.get(attackerId) const attacker = game.actors.get(attackerId)
if (!defender) return if (!defender) return
// When the attacker is GM-owned (not a PC on another client), the createChatMessage // The createChatMessage hook is suppressed on this client in two cases:
// hook on the defender's client already handles everything: D30 bonus dice, defense // 1. Attacker is a PC on another client — the hook's defense reaction loop
// reaction dialog (grit/luck/shield/mulligan), and the comparison message. Running // and shouldCreateMessage are both skipped.
// handleAttackBoosted too would double-roll all dice and show duplicate dialogs. // 2. Attacker is GM-controlled with D30 — the hook skips everything and
// Only proceed when the hook is suppressed (attacker is a PC on another client). // waits for this socket handler.
const attackerHasNonGMOwner = attacker && game.users.some(u => u.active && !u.isGM && attacker.testUserPermission(u, "OWNER")) // In both cases, this handler must run to show the defense dialog and create
// the comparison message. The common condition: the current user is the
// defender's primary controller.
const _isPrimaryController = actor => { const _isPrimaryController = actor => {
if (!actor) return false if (!actor) return false
const activePlayerOwners = game.users.filter(u => u.active && !u.isGM && actor.testUserPermission(u, "OWNER")) const activePlayerOwners = game.users.filter(u => u.active && !u.isGM && actor.testUserPermission(u, "OWNER"))
if (activePlayerOwners.length > 0) return activePlayerOwners[0].id === game.user.id if (activePlayerOwners.length > 0) return activePlayerOwners[0].id === game.user.id
return game.user.isGM return game.user.isGM
} }
const attackerIsCrossClient = attackerHasNonGMOwner && !_isPrimaryController(attacker) const defenderIsMine = _isPrimaryController(defender)
if (!attackerIsCrossClient) return if (!defenderIsMine) return
let updatedDefenseRoll = defenseRoll let updatedDefenseRoll = defenseRoll
let shieldBlocked = false let shieldBlocked = false
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000693 MANIFEST-000697
+8
View File
@@ -0,0 +1,8 @@
2026/08/08-20:47:15.064143 7f4f41fee6c0 Recovering log #695
2026/08/08-20:47:15.122364 7f4f41fee6c0 Delete type=3 #693
2026/08/08-20:47:15.122421 7f4f41fee6c0 Delete type=0 #695
2026/08/08-20:47:51.243693 7f4d23fff6c0 Level-0 table #700: started
2026/08/08-20:47:51.243707 7f4d23fff6c0 Level-0 table #700: 0 bytes OK
2026/08/08-20:47:51.285979 7f4d23fff6c0 Delete type=0 #698
2026/08/08-20:47:51.322780 7f4d23fff6c0 Manual compaction at level-0 from '!folders!ATr9wZhg5uTVTksM' @ 72057594037927935 : 1 .. '!items!zw9RQocTdz3HRjZK' @ 0 : 0; will stop at (end)
2026/08/08-20:47:51.322816 7f4d23fff6c0 Manual compaction at level-1 from '!folders!ATr9wZhg5uTVTksM' @ 72057594037927935 : 1 .. '!items!zw9RQocTdz3HRjZK' @ 0 : 0; will stop at (end)
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000690 MANIFEST-000694
+8
View File
@@ -0,0 +1,8 @@
2026/08/08-20:47:15.125487 7f4f417ed6c0 Recovering log #692
2026/08/08-20:47:15.180561 7f4f417ed6c0 Delete type=3 #690
2026/08/08-20:47:15.180616 7f4f417ed6c0 Delete type=0 #692
2026/08/08-20:47:51.286089 7f4d23fff6c0 Level-0 table #697: started
2026/08/08-20:47:51.286104 7f4d23fff6c0 Level-0 table #697: 0 bytes OK
2026/08/08-20:47:51.322669 7f4d23fff6c0 Delete type=0 #695
2026/08/08-20:47:51.322784 7f4d23fff6c0 Manual compaction at level-0 from '!folders!yPWGvxHJbDNHVSnY' @ 72057594037927935 : 1 .. '!items!x5gLtqlW4sdDmHTd' @ 0 : 0; will stop at (end)
2026/08/08-20:47:51.322812 7f4d23fff6c0 Manual compaction at level-1 from '!folders!yPWGvxHJbDNHVSnY' @ 72057594037927935 : 1 .. '!items!x5gLtqlW4sdDmHTd' @ 0 : 0; will stop at (end)
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000695 MANIFEST-000699
+8 -8
View File
@@ -1,8 +1,8 @@
2026/07/07-17:14:18.710797 7f677f7fe6c0 Recovering log #693 2026/08/08-20:47:15.001445 7f4f40fec6c0 Recovering log #697
2026/07/07-17:14:18.721726 7f677f7fe6c0 Delete type=3 #691 2026/08/08-20:47:15.061666 7f4f40fec6c0 Delete type=3 #695
2026/07/07-17:14:18.721797 7f677f7fe6c0 Delete type=0 #693 2026/08/08-20:47:15.061713 7f4f40fec6c0 Delete type=0 #697
2026/07/07-18:13:39.509293 7f677ebff6c0 Level-0 table #698: started 2026/08/08-20:47:51.170774 7f4d23fff6c0 Level-0 table #702: started
2026/07/07-18:13:39.509377 7f677ebff6c0 Level-0 table #698: 0 bytes OK 2026/08/08-20:47:51.170810 7f4d23fff6c0 Level-0 table #702: 0 bytes OK
2026/07/07-18:13:39.515800 7f677ebff6c0 Delete type=0 #696 2026/08/08-20:47:51.206405 7f4d23fff6c0 Delete type=0 #700
2026/07/07-18:13:39.537358 7f677ebff6c0 Manual compaction at level-0 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end) 2026/08/08-20:47:51.322766 7f4d23fff6c0 Manual compaction at level-0 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end)
2026/07/07-18:13:39.537602 7f677ebff6c0 Manual compaction at level-1 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end) 2026/08/08-20:47:51.322936 7f4d23fff6c0 Manual compaction at level-1 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end)
+8 -8
View File
@@ -1,8 +1,8 @@
2026/07/07-08:29:43.889413 7f677f7fe6c0 Recovering log #689 2026/07/07-17:14:18.710797 7f677f7fe6c0 Recovering log #693
2026/07/07-08:29:43.943359 7f677f7fe6c0 Delete type=3 #687 2026/07/07-17:14:18.721726 7f677f7fe6c0 Delete type=3 #691
2026/07/07-08:29:43.943451 7f677f7fe6c0 Delete type=0 #689 2026/07/07-17:14:18.721797 7f677f7fe6c0 Delete type=0 #693
2026/07/07-08:30:08.186974 7f677ebff6c0 Level-0 table #694: started 2026/07/07-18:13:39.509293 7f677ebff6c0 Level-0 table #698: started
2026/07/07-08:30:08.187025 7f677ebff6c0 Level-0 table #694: 0 bytes OK 2026/07/07-18:13:39.509377 7f677ebff6c0 Level-0 table #698: 0 bytes OK
2026/07/07-08:30:08.193951 7f677ebff6c0 Delete type=0 #692 2026/07/07-18:13:39.515800 7f677ebff6c0 Delete type=0 #696
2026/07/07-08:30:08.207875 7f677ebff6c0 Manual compaction at level-0 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end) 2026/07/07-18:13:39.537358 7f677ebff6c0 Manual compaction at level-0 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end)
2026/07/07-08:30:08.208156 7f677ebff6c0 Manual compaction at level-1 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end) 2026/07/07-18:13:39.537602 7f677ebff6c0 Manual compaction at level-1 from '!folders!7j8H7DbmBb9Uza2X' @ 72057594037927935 : 1 .. '!items!zt8s7564ep1La4XQ' @ 0 : 0; will stop at (end)
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000390 MANIFEST-000394
+8 -8
View File
@@ -1,8 +1,8 @@
2026/07/07-17:14:18.764463 7f677f7fe6c0 Recovering log #388 2026/08/08-20:47:15.236767 7f4f40fec6c0 Recovering log #392
2026/07/07-17:14:18.774743 7f677f7fe6c0 Delete type=3 #386 2026/08/08-20:47:15.297325 7f4f40fec6c0 Delete type=3 #390
2026/07/07-17:14:18.774813 7f677f7fe6c0 Delete type=0 #388 2026/08/08-20:47:15.297382 7f4f40fec6c0 Delete type=0 #392
2026/07/07-18:13:39.530110 7f677ebff6c0 Level-0 table #393: started 2026/08/08-20:47:51.365096 7f4d23fff6c0 Level-0 table #397: started
2026/07/07-18:13:39.530142 7f677ebff6c0 Level-0 table #393: 0 bytes OK 2026/08/08-20:47:51.365121 7f4d23fff6c0 Level-0 table #397: 0 bytes OK
2026/07/07-18:13:39.537202 7f677ebff6c0 Delete type=0 #391 2026/08/08-20:47:51.401348 7f4d23fff6c0 Delete type=0 #395
2026/07/07-18:13:39.537544 7f677ebff6c0 Manual compaction at level-0 from '!folders!37mu4dxsSuftlnmP' @ 72057594037927935 : 1 .. '!items!zKOpU34oLziGJW6y' @ 0 : 0; will stop at (end) 2026/08/08-20:47:51.495415 7f4d23fff6c0 Manual compaction at level-0 from '!folders!37mu4dxsSuftlnmP' @ 72057594037927935 : 1 .. '!items!zKOpU34oLziGJW6y' @ 0 : 0; will stop at (end)
2026/07/07-18:13:39.537840 7f677ebff6c0 Manual compaction at level-1 from '!folders!37mu4dxsSuftlnmP' @ 72057594037927935 : 1 .. '!items!zKOpU34oLziGJW6y' @ 0 : 0; will stop at (end) 2026/08/08-20:47:51.495436 7f4d23fff6c0 Manual compaction at level-1 from '!folders!37mu4dxsSuftlnmP' @ 72057594037927935 : 1 .. '!items!zKOpU34oLziGJW6y' @ 0 : 0; will stop at (end)
+8 -8
View File
@@ -1,8 +1,8 @@
2026/07/07-08:29:44.131849 7f677f7fe6c0 Recovering log #384 2026/07/07-17:14:18.764463 7f677f7fe6c0 Recovering log #388
2026/07/07-08:29:44.183800 7f677f7fe6c0 Delete type=3 #382 2026/07/07-17:14:18.774743 7f677f7fe6c0 Delete type=3 #386
2026/07/07-08:29:44.183891 7f677f7fe6c0 Delete type=0 #384 2026/07/07-17:14:18.774813 7f677f7fe6c0 Delete type=0 #388
2026/07/07-08:30:08.227162 7f677ebff6c0 Level-0 table #389: started 2026/07/07-18:13:39.530110 7f677ebff6c0 Level-0 table #393: started
2026/07/07-08:30:08.227201 7f677ebff6c0 Level-0 table #389: 0 bytes OK 2026/07/07-18:13:39.530142 7f677ebff6c0 Level-0 table #393: 0 bytes OK
2026/07/07-08:30:08.234023 7f677ebff6c0 Delete type=0 #387 2026/07/07-18:13:39.537202 7f677ebff6c0 Delete type=0 #391
2026/07/07-08:30:08.242020 7f677ebff6c0 Manual compaction at level-0 from '!folders!37mu4dxsSuftlnmP' @ 72057594037927935 : 1 .. '!items!zKOpU34oLziGJW6y' @ 0 : 0; will stop at (end) 2026/07/07-18:13:39.537544 7f677ebff6c0 Manual compaction at level-0 from '!folders!37mu4dxsSuftlnmP' @ 72057594037927935 : 1 .. '!items!zKOpU34oLziGJW6y' @ 0 : 0; will stop at (end)
2026/07/07-08:30:08.254520 7f677ebff6c0 Manual compaction at level-1 from '!folders!37mu4dxsSuftlnmP' @ 72057594037927935 : 1 .. '!items!zKOpU34oLziGJW6y' @ 0 : 0; will stop at (end) 2026/07/07-18:13:39.537840 7f677ebff6c0 Manual compaction at level-1 from '!folders!37mu4dxsSuftlnmP' @ 72057594037927935 : 1 .. '!items!zKOpU34oLziGJW6y' @ 0 : 0; will stop at (end)
+1 -1
View File
@@ -1 +1 @@
MANIFEST-000689 MANIFEST-000693
+8 -8
View File
@@ -1,8 +1,8 @@
2026/07/07-17:14:18.752018 7f67ccbfe6c0 Recovering log #687 2026/08/08-20:47:15.182015 7f4f427ef6c0 Recovering log #691
2026/07/07-17:14:18.762282 7f67ccbfe6c0 Delete type=3 #685 2026/08/08-20:47:15.234871 7f4f427ef6c0 Delete type=3 #689
2026/07/07-17:14:18.762342 7f67ccbfe6c0 Delete type=0 #687 2026/08/08-20:47:15.234913 7f4f427ef6c0 Delete type=0 #691
2026/07/07-18:13:39.562602 7f677ebff6c0 Level-0 table #692: started 2026/08/08-20:47:51.206517 7f4d23fff6c0 Level-0 table #696: started
2026/07/07-18:13:39.562663 7f677ebff6c0 Level-0 table #692: 0 bytes OK 2026/08/08-20:47:51.206531 7f4d23fff6c0 Level-0 table #696: 0 bytes OK
2026/07/07-18:13:39.568947 7f677ebff6c0 Delete type=0 #690 2026/08/08-20:47:51.243593 7f4d23fff6c0 Delete type=0 #694
2026/07/07-18:13:39.569296 7f677ebff6c0 Manual compaction at level-0 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end) 2026/08/08-20:47:51.322773 7f4d23fff6c0 Manual compaction at level-0 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end)
2026/07/07-18:13:39.569322 7f677ebff6c0 Manual compaction at level-1 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end) 2026/08/08-20:47:51.322947 7f4d23fff6c0 Manual compaction at level-1 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end)
+8 -8
View File
@@ -1,8 +1,8 @@
2026/07/07-08:29:44.074796 7f677ffff6c0 Recovering log #683 2026/07/07-17:14:18.752018 7f67ccbfe6c0 Recovering log #687
2026/07/07-08:29:44.128067 7f677ffff6c0 Delete type=3 #681 2026/07/07-17:14:18.762282 7f67ccbfe6c0 Delete type=3 #685
2026/07/07-08:29:44.128136 7f677ffff6c0 Delete type=0 #683 2026/07/07-17:14:18.762342 7f67ccbfe6c0 Delete type=0 #687
2026/07/07-08:30:08.208175 7f677ebff6c0 Level-0 table #688: started 2026/07/07-18:13:39.562602 7f677ebff6c0 Level-0 table #692: started
2026/07/07-08:30:08.208206 7f677ebff6c0 Level-0 table #688: 0 bytes OK 2026/07/07-18:13:39.562663 7f677ebff6c0 Level-0 table #692: 0 bytes OK
2026/07/07-08:30:08.214632 7f677ebff6c0 Delete type=0 #686 2026/07/07-18:13:39.568947 7f677ebff6c0 Delete type=0 #690
2026/07/07-08:30:08.234270 7f677ebff6c0 Manual compaction at level-0 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end) 2026/07/07-18:13:39.569296 7f677ebff6c0 Manual compaction at level-0 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end)
2026/07/07-08:30:08.242040 7f677ebff6c0 Manual compaction at level-1 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end) 2026/07/07-18:13:39.569322 7f677ebff6c0 Manual compaction at level-1 from '!folders!mnO9OzE7BEE2KDfh' @ 72057594037927935 : 1 .. '!items!zkK6ixtCsCw3RH9X' @ 0 : 0; will stop at (end)