feat: combat-tracker-driven bleeding (HP loss per wound per round)

This commit is contained in:
2026-06-11 21:49:35 +02:00
parent 141d6048e0
commit 9b75fd4d96
3 changed files with 97 additions and 0 deletions
+90
View File
@@ -117,6 +117,9 @@ Hooks.once("ready", function () {
registerWorldCount("lethalFantasy")
}
// Saignement piloté par le combat tracker
_registerBleedingHooks()
_showUserGuide()
/**
@@ -129,6 +132,93 @@ Hooks.once("ready", function () {
}
})
/**
* Saignement piloté par le combat tracker.
* Chaque round = 1 seconde → les acteurs qui saignent perdent 1 HP/blessure.
* Hors combat, une notification prévient le MJ que des blessures saignent encore.
*/
function _registerBleedingHooks() {
if (!game.user.isGM) return
Hooks.on("combatRound", async (combat, previous, current) => {
if (previous === current) return
const processed = new Set()
for (const combatant of combat.combatants) {
const actor = combatant.actor
if (!actor || processed.has(actor.id)) continue
processed.add(actor.id)
await _applyBleedingTick(actor)
}
})
Hooks.on("combatEnd", async (combat) => {
const bleeding = _findBleedingActors()
if (bleeding.length) {
ui.notifications.warn(
`Saignement actif hors combat : ${bleeding.map(a => a.name).join(", ")}`
)
}
})
Hooks.on("combatStart", async (combat) => {
const bleeding = _findBleedingActors()
if (bleeding.length) {
ui.notifications.warn(
`Saignement toujours actif sur : ${bleeding.map(a => a.name).join(", ")}`
)
}
})
}
/**
* Appliquer 1 HP de dégât par blessure active, décrémenter la durée.
* @param {import("foundry/common/documents.mjs").Actor} actor
*/
async function _applyBleedingTick(actor) {
if (!actor?.system?.hp?.wounds) return
const wounds = foundry.utils.duplicate(actor.system.hp.wounds)
let hpLoss = 0
let changed = false
for (const wound of wounds) {
if (wound.duration > 0 && wound.value > 0) {
hpLoss += 1
wound.duration -= 1
if (wound.duration <= 0) {
wound.value = 0
wound.description = ""
}
changed = true
}
}
if (!changed) return
const currentHp = actor.system.hp.value ?? 0
await actor.update({
"system.hp.value": currentHp - hpLoss,
"system.hp.wounds": wounds,
})
}
/**
* Retourne les acteurs (monde + tokens) qui ont des blessures actives.
* @returns {import("foundry/common/documents.mjs").Actor[]}
*/
function _findBleedingActors() {
const actors = []
for (const actor of game.actors.values()) {
if (actor?.system?.hp?.wounds?.some(w => w.duration > 0 && w.value > 0)) {
actors.push(actor)
}
}
for (const token of canvas.tokens?.placeables ?? []) {
if (token.actor && !actors.includes(token.actor)) {
if (token.actor?.system?.hp?.wounds?.some(w => w.duration > 0 && w.value > 0)) {
actors.push(token.actor)
}
}
}
return actors
}
Hooks.on("renderChatMessageHTML", (message, html, data) => {
const typeMessage = data.message.flags.lethalFantasy?.typeMessage
// Message de demande de jet de dés