feat: combat-tracker-driven bleeding (HP loss per wound per round)
This commit is contained in:
@@ -0,0 +1 @@
|
||||
packs/** filter=lfs diff=lfs merge=lfs -text
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"biodata",
|
||||
"LETHALFANTASY"
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user