diff --git a/lang/en.json b/lang/en.json index 1870fda..39deed3 100644 --- a/lang/en.json +++ b/lang/en.json @@ -382,7 +382,7 @@ "rollProgressionCount": "Roll progression count", "rollProgressionDice": "Roll progression/Lethargy dice", "earned": "Earned", - "divinityPoints": "Divinity points", + "divinityPoints": "Grace", "aetherPoints": "Aether points", "attacks": "Attacks", "attackMode": "Attack Mode", diff --git a/lethal-fantasy.mjs b/lethal-fantasy.mjs index fc10e46..bd0471b 100644 --- a/lethal-fantasy.mjs +++ b/lethal-fantasy.mjs @@ -828,6 +828,73 @@ Hooks.on("createChatMessage", async (message) => { } }) +// Hook: deduct aether when a spell-attack or spell-power roll is posted to chat +Hooks.on("createChatMessage", async (message) => { + if (!["spell-attack", "spell-power"].includes(message.rolls[0]?.options?.rollType)) return + + const actorId = message.rolls[0]?.options?.actorId + if (!actorId) return + const actor = game.actors.get(actorId) + if (!actor) return + + // Only the primary controller (player owner or GM) handles this + const activePlayerOwners = game.users.filter(u => u.active && !u.isGM && actor.testUserPermission(u, "OWNER")) + const isPrimary = activePlayerOwners.length > 0 + ? activePlayerOwners[0].id === game.user.id + : game.user.isGM + if (!isPrimary) return + + const rollTarget = message.rolls[0]?.options?.rollTarget + const spellId = rollTarget?.id || rollTarget?._id + const spell = spellId ? actor.items.get(spellId) : null + if (!spell || spell.type !== "spell") return + + const cost = Number(spell.system?.cost) || 0 + if (cost <= 0) return + + const currentAether = Number(actor.system.aetherPoints?.value) || 0 + const newAether = Math.max(0, currentAether - cost) + await actor.update({ "system.aetherPoints.value": newAether }) + + await ChatMessage.create({ + content: `

🔮 ${actor.name} casts ${spell.name} — spends ${cost} Aether (${currentAether} → ${newAether}).

`, + speaker: ChatMessage.getSpeaker({ actor }) + }) +}) + +// Hook: deduct grace when a miracle-attack or miracle-power roll is posted to chat +Hooks.on("createChatMessage", async (message) => { + if (!["miracle-attack", "miracle-power"].includes(message.rolls[0]?.options?.rollType)) return + + const actorId = message.rolls[0]?.options?.actorId + if (!actorId) return + const actor = game.actors.get(actorId) + if (!actor) return + + const activePlayerOwners = game.users.filter(u => u.active && !u.isGM && actor.testUserPermission(u, "OWNER")) + const isPrimary = activePlayerOwners.length > 0 + ? activePlayerOwners[0].id === game.user.id + : game.user.isGM + if (!isPrimary) return + + const rollTarget = message.rolls[0]?.options?.rollTarget + const miracleId = rollTarget?.id || rollTarget?._id + const miracle = miracleId ? actor.items.get(miracleId) : null + if (!miracle || miracle.type !== "miracle") return + + const cost = Number(miracle.system?.level) || 0 + if (cost <= 0) return + + const currentGrace = Number(actor.system.divinityPoints?.value) || 0 + const newGrace = Math.max(0, currentGrace - cost) + await actor.update({ "system.divinityPoints.value": newGrace }) + + await ChatMessage.create({ + content: `

✨ ${actor.name} invokes ${miracle.name} — spends ${cost} Grace (${currentGrace} → ${newGrace}).

`, + speaker: ChatMessage.getSpeaker({ actor }) + }) +}) + // Hook pour appliquer automatiquement les dégâts si une cible est définie Hooks.on("createChatMessage", async (message) => { // Vérifier si c'est un message de dégâts avec un defenderId diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 52e1090..880d65f 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -198,6 +198,22 @@ export default class LethalFantasyActor extends Actor { case "miracle-power": rollTarget = this.items.find((i) => (i.type === "miracle" || i.type == "spell") && i.id === rollKey) rollTarget.rollKey = rollKey + if (rollType === "spell-attack" || rollType === "spell-power") { + const cost = Number(rollTarget.system?.cost) || 0 + const currentAether = Number(this.system.aetherPoints?.value) || 0 + if (cost > currentAether) { + ui.notifications.warn(`${this.name} cannot cast ${rollTarget.name}: insufficient Aether (needs ${cost}, has ${currentAether}).`) + return + } + } + if (rollType === "miracle-attack" || rollType === "miracle-power") { + const cost = Number(rollTarget.system?.level) || 0 + const currentGrace = Number(this.system.divinityPoints?.value) || 0 + if (cost > currentGrace) { + ui.notifications.warn(`${this.name} cannot invoke ${rollTarget.name}: insufficient Grace (needs ${cost}, has ${currentGrace}).`) + return + } + } break case "shield-roll": { rollTarget = this.items.find((i) => i.type === "shield" && i.id === rollKey) diff --git a/module/documents/roll.mjs b/module/documents/roll.mjs index 78096d3..36f38cf 100644 --- a/module/documents/roll.mjs +++ b/module/documents/roll.mjs @@ -272,6 +272,7 @@ export default class LethalFantasyRoll extends Roll { hasChangeDice = false hasMaxValue = false hasExplode = false + hasFavor = true options.rollTarget.value = 0 } else if (options.rollType.includes("weapon-damage")) {