feat: système de récupération des résistances
- Dialogue de récupération accessible depuis la feuille personnage - Coupe/Denier : test difficulté 7 (1x/jour), +1 si réussi - Épée : +1 manuel (repos/soins) ou test guérison rapide - Bâton : +1 manuel (appréciation MJ) - Suivi des dates de dernière récupération par symbole - Désactive le test si déjà fait aujourd'hui - Bouton d'accès intégré dans la section résistances (icône lit) - Réutilise HamalronTirageTarot.prompt pour les tests
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
import { SYSTEM } from "../config/system.mjs"
|
||||
|
||||
export default class HamalronResistanceRecovery {
|
||||
static SYMBOLE_LABELS = {
|
||||
coupe: "Coupe",
|
||||
epee: "Épée",
|
||||
denier: "Denier",
|
||||
baton: "Bâton",
|
||||
}
|
||||
|
||||
static async prompt(actor) {
|
||||
const resistances = actor.system.resistances
|
||||
const recovery = actor.system.resistanceRecovery || {}
|
||||
const today = new Date().toISOString().split("T")[0]
|
||||
const todayNum = Number(new Date().toISOString().split("T")[0].replace(/-/g, ""))
|
||||
|
||||
const symInfos = Object.entries(SYSTEM.TAROT_SYMBOLES).map(([key, val]) => {
|
||||
const r = resistances[key]
|
||||
const lastDate = recovery[key]
|
||||
const lastNum = lastDate ? Number(lastDate.replace(/-/g, "")) : 0
|
||||
const canRecover = key === "epee"
|
||||
? true
|
||||
: (lastNum < todayNum)
|
||||
const isDenierCoupe = key === "denier" || key === "coupe"
|
||||
return {
|
||||
id: key,
|
||||
label: val.label,
|
||||
value: r?.value ?? 0,
|
||||
max: r?.max ?? 0,
|
||||
canRecover,
|
||||
isDenierCoupe,
|
||||
lastRecovery: lastDate || "-",
|
||||
}
|
||||
})
|
||||
|
||||
const content = HamalronResistanceRecovery.#renderContent(actor, symInfos)
|
||||
|
||||
const result = await foundry.applications.api.DialogV2.wait({
|
||||
window: { title: game.i18n.localize("HAMALRON.Recovery.Title") },
|
||||
classes: ["fvtt-hamalron"],
|
||||
position: { width: 480 },
|
||||
content,
|
||||
buttons: [
|
||||
{
|
||||
action: "close",
|
||||
label: game.i18n.localize("Cancel"),
|
||||
default: true,
|
||||
},
|
||||
],
|
||||
actions: {
|
||||
recoverTest: async (event, button, dialog) => {
|
||||
const btn = event.currentTarget
|
||||
const symbole = btn.dataset.symbole
|
||||
if (!symbole) return
|
||||
const rollItem = { name: HamalronResistanceRecovery.SYMBOLE_LABELS[symbole] || symbole, value: 0 }
|
||||
await HamalronResistanceRecovery.#doRecoveryTest(actor, symbole, rollItem)
|
||||
HamalronResistanceRecovery.prompt(actor)
|
||||
},
|
||||
adjustResistance: async (event, button, dialog) => {
|
||||
const btn = event.currentTarget
|
||||
const symbole = btn.dataset.symbole
|
||||
const delta = Number(btn.dataset.delta) || 0
|
||||
if (!symbole) return
|
||||
const resistance = actor.system.resistances[symbole]
|
||||
if (!resistance) return
|
||||
const newValue = Math.max(0, Math.min(resistance.max, (resistance.value || 0) + delta))
|
||||
await actor.update({ [`system.resistances.${symbole}.value`]: newValue })
|
||||
HamalronResistanceRecovery.prompt(actor)
|
||||
},
|
||||
},
|
||||
rejectClose: false,
|
||||
})
|
||||
}
|
||||
|
||||
static #renderContent(actor, symInfos) {
|
||||
const today = new Date().toISOString().split("T")[0]
|
||||
let html = `<div class="fvtt-hamalron-recovery"><h3>${game.i18n.localize("HAMALRON.Recovery.Subtitle")}</h3>`
|
||||
html += `<p class="recovery-date">${game.i18n.format("HAMALRON.Recovery.Today", { date: today })}</p>`
|
||||
html += `<div class="recovery-grid">`
|
||||
|
||||
for (const info of symInfos) {
|
||||
const pct = info.max > 0 ? Math.round((info.value / info.max) * 100) : 0
|
||||
html += `<div class="recovery-card">
|
||||
<div class="recovery-header">
|
||||
<strong>${info.label}</strong>
|
||||
<span>${info.value}/${info.max}</span>
|
||||
</div>
|
||||
<div class="progress-bar-mini"><div class="progress-fill" style="width:${pct}%"></div></div>
|
||||
<div class="recovery-actions">`
|
||||
|
||||
if (info.isDenierCoupe) {
|
||||
if (info.canRecover) {
|
||||
html += `<button type="button" class="recovery-btn" data-action="recoverTest" data-symbole="${info.id}">
|
||||
<i class="fas fa-dice"></i> ${game.i18n.localize("HAMALRON.Recovery.Test")}</button>`
|
||||
} else {
|
||||
html += `<span class="recovery-done"><i class="fas fa-check-circle"></i> ${game.i18n.localize("HAMALRON.Recovery.Done")}</span>`
|
||||
}
|
||||
} else if (info.id === "epee") {
|
||||
html += `<button type="button" class="recovery-btn-sm" data-action="adjustResistance" data-symbole="epee" data-delta="1">
|
||||
<i class="fas fa-plus"></i> +1</button>`
|
||||
html += `<button type="button" class="recovery-btn-sm" data-action="recoverTest" data-symbole="epee">
|
||||
<i class="fas fa-dice"></i> ${game.i18n.localize("HAMALRON.Recovery.HealTest")}</button>`
|
||||
} else { // baton
|
||||
html += `<button type="button" class="recovery-btn-sm" data-action="adjustResistance" data-symbole="baton" data-delta="1">
|
||||
<i class="fas fa-plus"></i> +1</button>`
|
||||
}
|
||||
|
||||
html += `<button type="button" class="recovery-btn-sm" data-action="adjustResistance" data-symbole="${info.id}" data-delta="-1">
|
||||
<i class="fas fa-minus"></i></button>`
|
||||
|
||||
html += `</div><div class="recovery-last">${game.i18n.localize("HAMALRON.Recovery.Last")}: ${info.lastRecovery}</div>
|
||||
</div>`
|
||||
}
|
||||
|
||||
html += `</div></div>`
|
||||
return html
|
||||
}
|
||||
|
||||
static async #doRecoveryTest(actor, symbole, rollItem) {
|
||||
const niveauData = SYSTEM.NIVEAU_COMPETENCES.inexperimente
|
||||
const roll = await HamalronTirageTarot.prompt({
|
||||
rollType: "stat",
|
||||
rollItem,
|
||||
actorId: actor.id,
|
||||
actorName: actor.name,
|
||||
actorImage: actor.img,
|
||||
hasTarget: false,
|
||||
difficulty: "7",
|
||||
})
|
||||
if (!roll) return
|
||||
|
||||
const today = new Date().toISOString().split("T")[0]
|
||||
const recoveryData = foundry.utils.duplicate(actor.system.resistanceRecovery || {})
|
||||
recoveryData[symbole] = today
|
||||
await actor.update({ [`system.resistanceRecovery`]: recoveryData })
|
||||
|
||||
if (roll.total >= 7) {
|
||||
const resistance = actor.system.resistances[symbole]
|
||||
if (resistance && resistance.value < resistance.max) {
|
||||
const newValue = Math.min(resistance.max, (resistance.value || 0) + 1)
|
||||
await actor.update({ [`system.resistances.${symbole}.value`]: newValue })
|
||||
}
|
||||
ui.notifications.info(game.i18n.format("HAMALRON.Recovery.Success", { label: HamalronResistanceRecovery.SYMBOLE_LABELS[symbole] }))
|
||||
} else {
|
||||
ui.notifications.info(game.i18n.format("HAMALRON.Recovery.Failure", { label: HamalronResistanceRecovery.SYMBOLE_LABELS[symbole] }))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ export default class HamalronPersonnageSheet extends HamalronActorSheet {
|
||||
createArmor: HamalronPersonnageSheet.#onCreateArmor,
|
||||
createWeapon: HamalronPersonnageSheet.#onCreateWeapon,
|
||||
createSortilege: HamalronPersonnageSheet.#onCreateSortilege,
|
||||
openRecovery: HamalronPersonnageSheet.#onOpenRecovery,
|
||||
modifyResistance: HamalronPersonnageSheet.#onModifyResistance,
|
||||
discardCard: HamalronPersonnageSheet.#onDiscardCard,
|
||||
rollCompetence: HamalronPersonnageSheet.#onRollCompetence,
|
||||
@@ -173,6 +174,11 @@ export default class HamalronPersonnageSheet extends HamalronActorSheet {
|
||||
this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("HAMALRON.Label.newSortilege"), type: "sortilege" }])
|
||||
}
|
||||
|
||||
static async #onOpenRecovery(event, target) {
|
||||
const { default: HamalronResistanceRecovery } = await import("../resistance-recovery.mjs")
|
||||
await HamalronResistanceRecovery.prompt(this.document)
|
||||
}
|
||||
|
||||
static async #onModifyResistance(event, target) {
|
||||
const symbole = target.dataset.symbole
|
||||
const delta = Number.parseInt(target.dataset.delta)
|
||||
|
||||
Reference in New Issue
Block a user