From e43487b72732c3fb3acd1ea24a562e4149f70864 Mon Sep 17 00:00:00 2001 From: LeRatierBretonnier Date: Sun, 14 Jun 2026 23:25:24 +0200 Subject: [PATCH] Fix release numbering --- css/fvtt-cthulhu-eternal.css | 28 ++++- cthulhu-eternal.mjs | 85 ++++++++++---- lang/en.json | 8 +- lang/fr.json | 8 +- module/applications/sheets/creature-sheet.mjs | 2 +- .../applications/sheets/protagonist-sheet.mjs | 2 +- module/applications/sheets/vehicle-sheet.mjs | 6 +- module/documents/roll.mjs | 88 +++++++------- module/models/creature.mjs | 2 +- module/models/protagonist.mjs | 2 +- module/utils.mjs | 107 ++++++++++++++++-- .../rituals/{000295.log => 000308.log} | 0 packs-system/rituals/CURRENT | 2 +- packs-system/rituals/LOG | 18 ++- packs-system/rituals/LOG.old | 11 +- packs-system/rituals/MANIFEST-000293 | Bin 266 -> 0 bytes packs-system/rituals/MANIFEST-000306 | Bin 0 -> 174 bytes .../skills/{000464.log => 000477.log} | 0 packs-system/skills/CURRENT | 2 +- packs-system/skills/LOG | 18 ++- packs-system/skills/LOG.old | 11 +- packs-system/skills/MANIFEST-000462 | Bin 273 -> 0 bytes packs-system/skills/MANIFEST-000475 | Bin 0 -> 178 bytes .../weapons/{000110.log => 000123.log} | 0 packs-system/weapons/CURRENT | 2 +- packs-system/weapons/LOG | 18 ++- packs-system/weapons/LOG.old | 11 +- packs-system/weapons/MANIFEST-000108 | Bin 264 -> 0 bytes packs-system/weapons/MANIFEST-000121 | Bin 0 -> 173 bytes templates/chat-lethal-damage.hbs | 18 ++- templates/chat-message.hbs | 16 +++ templates/chat-regular-damage.hbs | 4 +- templates/chat-san-request.hbs | 20 ++-- templates/chat-san-type-request.hbs | 8 +- utf8_checker.py | 46 ++++++++ 35 files changed, 391 insertions(+), 152 deletions(-) rename packs-system/rituals/{000295.log => 000308.log} (100%) delete mode 100644 packs-system/rituals/MANIFEST-000293 create mode 100644 packs-system/rituals/MANIFEST-000306 rename packs-system/skills/{000464.log => 000477.log} (100%) delete mode 100644 packs-system/skills/MANIFEST-000462 create mode 100644 packs-system/skills/MANIFEST-000475 rename packs-system/weapons/{000110.log => 000123.log} (100%) delete mode 100644 packs-system/weapons/MANIFEST-000108 create mode 100644 packs-system/weapons/MANIFEST-000121 create mode 100644 utf8_checker.py diff --git a/css/fvtt-cthulhu-eternal.css b/css/fvtt-cthulhu-eternal.css index 199c097..334fccb 100644 --- a/css/fvtt-cthulhu-eternal.css +++ b/css/fvtt-cthulhu-eternal.css @@ -3167,8 +3167,16 @@ i.fvtt-cthulhu-eternal { font-size: calc(var(--font-size-standard) * 1.2); text-shadow: 0 0 10px var(--color-shadow-primary); } -.dice-roll .chat-actions { - display: flex; +.dice-roll .skill-progress-notice { + margin: 0.3rem 0 0; + padding: 0.25rem 0.5rem; + font-size: 0.8rem; + font-style: italic; + color: var(--color-level-warning); + border-left: 3px solid var(--color-level-warning); + background: rgba(var(--color-level-warning-rgb, 180, 130, 0), 0.08); +} +.dice-roll .chat-actions { display: flex; flex-wrap: wrap; gap: 0.375rem; padding: 0.5rem; @@ -3210,9 +3218,23 @@ i.fvtt-cthulhu-eternal { .dice-roll .chat-actions .nudge-roll, .dice-roll .chat-actions .damage-roll, .dice-roll .chat-actions .healing-roll, -.dice-roll .chat-actions .opposed-roll { +.dice-roll .chat-actions .opposed-roll, +.dice-roll .chat-actions .nudge-to-success { display: none; } +.dice-roll .chat-actions .nudge-to-success { + width: auto; + padding: 0 0.5rem !important; + gap: 0.3rem; + font-size: 0.75rem; + font-weight: bold; + background: var(--color-level-success); + color: var(--color-light-1); + border-color: var(--color-level-success); +} +.dice-roll .chat-actions .nudge-to-success:hover { + filter: brightness(1.15); +} .opposed-roll-result { padding: 0.5rem; background: rgba(0, 0, 0, 0.05); diff --git a/cthulhu-eternal.mjs b/cthulhu-eternal.mjs index e89c61b..04c728d 100644 --- a/cthulhu-eternal.mjs +++ b/cthulhu-eternal.mjs @@ -159,11 +159,42 @@ Hooks.on('tokenActionHudCoreApiReady', async () => { }) Hooks.on("renderChatMessageHTML", (message, html, data) => { + const alreadyUsedLabel = game.i18n.localize("CTHULHUETERNAL.Label.alreadyUsed") + + // Helper: disable all action buttons in the message + function disableChatActions(container) { + $(container).find(".chat-action-button").each((i, btn) => { + btn.setAttribute("disabled", "disabled") + btn.setAttribute("data-tooltip", alreadyUsedLabel) + btn.style.opacity = "0.5" + btn.style.cursor = "not-allowed" + btn.style.pointerEvents = "none" + }) + } + + // If an action was already used on this message, disable all buttons immediately + if (message.getFlag("fvtt-cthulhu-eternal", "actionUsed")) { + disableChatActions(html) + } + + // Wrapper: click any action button → disable all + persist flag + function withActionLock(handler) { + return async (event) => { + const container = $(event.currentTarget).closest(".chat-message, .message-content, div[class]")[0] || html + disableChatActions(container) + message.setFlag("fvtt-cthulhu-eternal", "actionUsed", true).catch(() => { }) + await handler(event) + } + } + // Affichage des boutons de jet de dés uniquement pour les joueurs if (message.author.id === game.user.id || game.user.isGM) { $(html).find(".nudge-roll").each((i, btn) => { btn.style.display = "inline" }) + $(html).find(".nudge-to-success").each((i, btn) => { + btn.style.display = "inline-flex" + }) $(html).find(".damage-roll").each((i, btn) => { btn.style.display = "inline" }) @@ -178,47 +209,55 @@ Hooks.on("renderChatMessageHTML", (message, html, data) => { btn.style.display = "inline" }) } - $(html).find(".nudge-roll").click((event) => { + $(html).find(".nudge-roll").click(withActionLock(() => { CthulhuEternalUtils.nudgeRoll(message) - }) - $(html).find(".damage-roll").click((event) => { + })) + $(html).find(".nudge-to-success").click(withActionLock(() => { + CthulhuEternalUtils.nudgeToSuccess(message) + })) + $(html).find(".damage-roll").click(withActionLock((event) => { let formula = $(event.currentTarget).data("roll-value") CthulhuEternalUtils.damageRoll(message, formula) - }) - $(html).find(".healing-roll").click((event) => { + })) + $(html).find(".healing-roll").click(withActionLock(() => { CthulhuEternalUtils.healingRoll(message) - }) - $(html).find(".worn-weapon-check").click((event) => { + })) + $(html).find(".worn-weapon-check").click(withActionLock(() => { CthulhuEternalUtils.wornWeaponCheck(message) - }) - $(html).find(".san-loose").click((event) => { + })) + $(html).find(".san-loose").click(withActionLock((event) => { CthulhuEternalUtils.applySANLoss(message, event) - }) - $(html).find(".san-type").click((event) => { + })) + $(html).find(".san-type").click(withActionLock((event) => { CthulhuEternalUtils.applySANType(message, event) - }) - $(html).find(".opposed-roll").click((event) => { + })) + $(html).find(".opposed-roll").click(withActionLock((event) => { CthulhuEternalUtils.opposedRollManagement(message, event) - }) + })) } if (game.user.isGM) { $(html).find(".li-apply-wounds").each((i, btn) => { btn.style.display = "block" }) - $(html).find(".apply-wounds-btn").click((event) => { + $(html).find(".apply-wounds-btn").click(withActionLock((event) => { CthulhuEternalUtils.applyWounds(message, event) - }) + })) $(html).find(".apply-wounds-btn").hover( function (event) { - // Mouse enter - select the token - let combatantId = $(this).data("combatant-id") + // Mouse enter - highlight the token on the canvas + const tokenId = $(this).data("token-id") + if (tokenId) { + const token = canvas.tokens.get(tokenId) + if (token) token.control({ releaseOthers: true }) + return + } + // Legacy: resolve via combatant + const combatantId = $(this).data("combatant-id") if (combatantId && game.combat) { - let combatant = game.combat.combatants.get(combatantId) + const combatant = game.combat.combatants.get(combatantId) if (combatant?.token) { - let token = canvas.tokens.get(combatant.token.id) - if (token) { - token.control({ releaseOthers: true }) - } + const token = canvas.tokens.get(combatant.token.id) + if (token) token.control({ releaseOthers: true }) } } }, diff --git a/lang/en.json b/lang/en.json index 0f84a18..b169b8d 100644 --- a/lang/en.json +++ b/lang/en.json @@ -24,6 +24,9 @@ "Settings": { "era": "Select the era of your game", "eraHint": "Select the era of your game", + "nudge": "Allow roll modification (WP spend)", + "nudgeHint": "When enabled, players can spend Willpower Points to modify failed rolls. Disable to remove this optional rule.", + "nudgeDisabled": "Roll modification via Willpower Points is currently disabled.", "Common": "Common", "Classical": "Classical", "Medieval": "Medieval", @@ -622,6 +625,7 @@ "Weapon": "Weapon", "ZeroWP": "Zero WP : Automatic failure (ie 0%)", "LowWP": "Low WP", + "notEnoughWP": "Not enough WP to perform this action", "Exhausted": "Exhausted", "wornWeaponWarning": "Worn weapon: Don't forget to make the luck roll after the attack!", "creature": "Creature", @@ -773,6 +777,7 @@ "lethalityLethal": "Lethal !!", "lethalityNotLethal": "Non-Lethal", "WPSpent": "WP Spent", + "alreadyUsed": "Already used", "targetMove": "Target Move", "attackerState": "Attacker State", "targetSize": "Target Size", @@ -808,7 +813,8 @@ "roll": "Roll", "applyNudge": "Apply", "cancel": "Cancel", - "nudgeRoll": "Nudge Roll" + "nudgeRoll": "Nudge Roll", + "nudgeToSuccess": "Succeed at {score}% for {wp} WP" }, "Tooltip": { "sanBP": ">5 SAN lost in one roll, temporary insanity. If SAN less reaches BP = a Disorder unconscious Breaking and AND reset BP.", diff --git a/lang/fr.json b/lang/fr.json index 2e7941b..5161f87 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -24,6 +24,9 @@ "Settings": { "era": "Sélectionnez l'époque de votre jeu", "eraHint": "L'époque détermine les compétences, les armes et les équipements disponibles pour votre protagoniste.", + "nudge": "Autoriser la modification des jets (dépense de PVO)", + "nudgeHint": "Si activé, les joueurs peuvent dépenser des Points de Volonté pour modifier leurs jets ratés. Désactivez pour supprimer cette règle optionnelle.", + "nudgeDisabled": "La modification des jets via les Points de Volonté est actuellement désactivée.", "Common": "Commun", "Classical": "Classique", "Medieval": "Médiéval", @@ -616,6 +619,7 @@ "Weapon": "Arme", "ZeroWP": "PVO à 0 : Echec automatique (ie 0%)", "LowWP": "PVO faibles", + "notEnoughWP": "PVO insuffisants pour effectuer cette action", "Exhausted": "Epuisé", "wornWeaponWarning": "Arme usée : N'oubliez pas de faire le jet de chance après l'attaque !", "creature": "Créature", "Rituals": "Rituels", "Tomes": "Ouvrages", @@ -764,6 +768,7 @@ "lethalityLethal": "Létal !!", "lethalityNotLethal": "Non létal", "WPSpent": "PVO dépensés", + "alreadyUsed": "Déjà utilisé", "targetMove": "Mouvement de la cible", "attackerState": "Etat de l'attaquant", "targetSize": "Taille de la cible", @@ -800,7 +805,8 @@ "roll": "Jet", "applyNudge": "Lancer", "cancel": "Annuler", - "nudgeRoll": "Modifier le jet" + "nudgeRoll": "Modifier le jet", + "nudgeToSuccess": "Réussir à {score}% pour {wp} PVO" }, "Tooltip": { "sanBP": "Perte de 5+ SAN en 1 jet : folie temporaire. SI la SAN atteint le PR : trouble mental, perte de conscience et reset du PR.", diff --git a/module/applications/sheets/creature-sheet.mjs b/module/applications/sheets/creature-sheet.mjs index a2cd980..e961817 100644 --- a/module/applications/sheets/creature-sheet.mjs +++ b/module/applications/sheets/creature-sheet.mjs @@ -162,7 +162,7 @@ export default class CthulhuEternalCreatureSheet extends CthulhuEternalActorShee async _onDrop(event) { if (!this.isEditable || !this.isEditMode) return - const data = TextEditor.getDragEventData(event) + const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event) // Handle different data types switch (data.type) { diff --git a/module/applications/sheets/protagonist-sheet.mjs b/module/applications/sheets/protagonist-sheet.mjs index eb548cc..10297c2 100644 --- a/module/applications/sheets/protagonist-sheet.mjs +++ b/module/applications/sheets/protagonist-sheet.mjs @@ -264,7 +264,7 @@ export default class CthulhuEternalProtagonistSheet extends CthulhuEternalActorS async _onDrop(event) { if (!this.isEditable || !this.isEditMode) return - const data = TextEditor.getDragEventData(event) + const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event) // Handle different data types switch (data.type) { diff --git a/module/applications/sheets/vehicle-sheet.mjs b/module/applications/sheets/vehicle-sheet.mjs index 3183e15..1cf010b 100644 --- a/module/applications/sheets/vehicle-sheet.mjs +++ b/module/applications/sheets/vehicle-sheet.mjs @@ -81,8 +81,8 @@ export default class CthulhuEternalVehicleSheet extends CthulhuEternalActorSheet break case "description": context.tab = context.tabs.description - context.enrichedDescription = await TextEditor.enrichHTML(doc.system.description, { async: true }) - context.enrichedNotes = await TextEditor.enrichHTML(doc.system.notes, { async: true }) + context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(doc.system.description, { async: true }) + context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(doc.system.notes, { async: true }) break } return context @@ -104,7 +104,7 @@ export default class CthulhuEternalVehicleSheet extends CthulhuEternalActorSheet async _onDrop(event) { if (!this.isEditable || !this.isEditMode) return - const data = TextEditor.getDragEventData(event) + const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event) // Handle different data types switch (data.type) { diff --git a/module/documents/roll.mjs b/module/documents/roll.mjs index 34ab54c..648e4c2 100644 --- a/module/documents/roll.mjs +++ b/module/documents/roll.mjs @@ -139,11 +139,19 @@ export default class CthulhuEternalRoll extends Roll { weapon.system.killRadius = choice.killRadius // Override kill radius } - let combatants = [] + let targets = [] if (game?.combat?.combatants) { for (let c of game.combat.combatants) { - if (c.actorid !== actor.id) { - combatants.push({ id: c.id, name: c.name }) + if (c.actorId !== actor.id) { + targets.push({ id: c.id, name: c.name, actorId: c.actorId, tokenId: c.token?.id }) + } + } + } + // Fall back to scene tokens if not in combat + if (targets.length === 0 && canvas?.scene) { + for (let tokenDoc of canvas.scene.tokens) { + if (tokenDoc.actorId !== actor.id) { + targets.push({ name: tokenDoc.name, actorId: tokenDoc.actorId, tokenId: tokenDoc.id }) } } } @@ -162,12 +170,11 @@ export default class CthulhuEternalRoll extends Roll { isLethal, ammoUsed: weapon?.ammoUsed || 0, rollResult: lethalityRoll.total, - combatants: combatants + combatants: targets } - let flavor = await foundry.applications.handlebars.renderTemplate("systems/fvtt-cthulhu-eternal/templates/chat-lethal-damage.hbs", msgData) let msg = await ChatMessage.create({ user: game.user.id, - content: flavor, + content: await foundry.applications.handlebars.renderTemplate("systems/fvtt-cthulhu-eternal/templates/chat-lethal-damage.hbs", msgData), speaker: ChatMessage.getSpeaker({ actor: actor }), }, { rollMode: options.rollMode, create: true }) await msg.setFlag("fvtt-cthulhu-eternal", "woundData", msgData) @@ -191,12 +198,11 @@ export default class CthulhuEternalRoll extends Roll { formula, ammoUsed: weapon?.ammoUsed || 0, rollResult: damageRoll.total, - combatants: combatants + combatants: targets } - let flavor = await foundry.applications.handlebars.renderTemplate("systems/fvtt-cthulhu-eternal/templates/chat-regular-damage.hbs", msgData) let msg = await ChatMessage.create({ user: game.user.id, - content: flavor, + content: await foundry.applications.handlebars.renderTemplate("systems/fvtt-cthulhu-eternal/templates/chat-regular-damage.hbs", msgData), speaker: ChatMessage.getSpeaker({ actor: actor }), }, { rollMode: options.rollMode, create: true }) await msg.setFlag("fvtt-cthulhu-eternal", "woundData", msgData) @@ -254,7 +260,8 @@ export default class CthulhuEternalRoll extends Roll { let formula = "1d100" let hasModifier = true let hasMultiplier = false - options.isNudge = true + const nudgeEnabled = game.settings.get("fvtt-cthulhu-eternal", "settings-nudge") + options.isNudge = nudgeEnabled let actor = game.actors.get(options.actorId) let target = CthulhuEternalUtils.getTarget() @@ -271,7 +278,7 @@ export default class CthulhuEternalRoll extends Roll { case "san": case "char": options.initialScore = options.rollItem.targetScore - options.isNudge = (options.rollType !== "san") + options.isNudge = nudgeEnabled && (options.rollType !== "san") break case "resource": hasModifier = false @@ -322,7 +329,7 @@ export default class CthulhuEternalRoll extends Roll { break } - const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes); //Object.fromEntries(Object.entries(CONFIG.Dice.rollModes).map(([key, value]) => [key, game.i18n.localize(value)])) + const rollModes = foundry.utils.duplicate(CONFIG.ChatMessage.modes ?? CONFIG.Dice.rollModes) const fieldRollMode = new foundry.data.fields.StringField({ choices: rollModes, blank: false, @@ -526,6 +533,16 @@ export default class CthulhuEternalRoll extends Roll { } rollData.resultType = resultType + // Compute WP cost to nudge a failure into a success (minimum roll needed = targetScore) + if (this.options.isFailure && options.isNudge && !this.options.isNudgedRoll && rollData.targetScore > 0 && this.total > rollData.targetScore) { + const actor = game.actors.get(options.actorId) + const wpAvailable = actor?.system?.wp?.value ?? 0 + const wpCostToSucceed = Math.ceil((this.total - rollData.targetScore) / 5) + this.options.wpCostToSucceed = (wpCostToSucceed <= wpAvailable) ? wpCostToSucceed : 0 + } else { + this.options.wpCostToSucceed = 0 + } + this.options.isLowWP = rollData.isLowWP this.options.isZeroWP = rollData.isZeroWP this.options.isExhausted = rollData.isExhausted @@ -685,7 +702,19 @@ export default class CthulhuEternalRoll extends Roll { * @param {boolean} [options.create=true] Whether to create the message. * @returns {Promise} - A promise that resolves when the message is created. */ - async toMessage(messageData = {}, { rollMode, create = true } = {}) { + async toMessage(messageData = {}, { rollMode, messageMode, create = true } = {}) { + let rollData = this.options.rollData || this.options + let rollItem = this.options.rollItem + + // Determine skill progression before calling super.toMessage so _getChatCardData picks it up + let skillMarkedForProgress = false + if (rollData.resultType?.includes("failure") && rollItem?.type === "skill") { + if (rollItem.system.diceEvolved && !rollItem.system.rollFailed && !rollItem.system.isAdversary) { + skillMarkedForProgress = true + } + } + this.options.skillMarkedForProgress = skillMarkedForProgress + let rollMsg = await super.toMessage( { isFailure: this.resultType === "failure", @@ -695,34 +724,17 @@ export default class CthulhuEternalRoll extends Roll { realDamage: this.realDamage, ...messageData, }, - { rollMode: rollMode }, + { messageMode: messageMode ?? rollMode }, ) - let rollData = this.options.rollData || this.options - let rollItem = this.options.rollItem await rollMsg.setFlag("fvtt-cthulhu-eternal", "rollData", rollData) - // Manage the skill evolution if the roll is a failure - if (rollData.resultType.includes("failure") && rollItem.type === "skill") { - // Is the skill able to progress - if (rollItem.system.diceEvolved && !rollItem.system.rollFailed) { - // If the skill is not adversary, we can evolve it - if (!rollItem.system.isAdversary) { - rollItem.system.rollFailed = true - // Get the actor and update the skill - const actor = game.actors.get(rollData.actorId) - await actor.updateEmbeddedDocuments("Item", [{ - _id: rollItem._id, - "system.rollFailed": true - }]) - // Create a chat message to inform the user - const flavor = `${rollItem.name} - ${game.i18n.localize("CTHULHUETERNAL.Label.skillFailed")}` - await ChatMessage.create({ - user: game.user.id, - content: `

${flavor}

`, - speaker: ChatMessage.getSpeaker({ actor: rollData.actor }), - }, { rollMode: rollData.rollMode, create: true }) - } - } + // Apply skill rollFailed flag after the message is created + if (skillMarkedForProgress) { + const actor = game.actors.get(rollData.actorId) + await actor.updateEmbeddedDocuments("Item", [{ + _id: rollItem._id, + "system.rollFailed": true + }]) } // If the roll is a SAN roll, we propose to select the SAN loss diff --git a/module/models/creature.mjs b/module/models/creature.mjs index 836d77d..c61c686 100644 --- a/module/models/creature.mjs +++ b/module/models/creature.mjs @@ -74,7 +74,7 @@ export default class CthulhuEternalCreature extends foundry.abstract.TypeDataMod }) if (!roll) return null - await roll.toMessage({}, { rollMode: roll.options.rollMode }) + await roll.toMessage({}, { messageMode: roll.options.rollMode }) } } diff --git a/module/models/protagonist.mjs b/module/models/protagonist.mjs index df76af8..346f696 100644 --- a/module/models/protagonist.mjs +++ b/module/models/protagonist.mjs @@ -401,7 +401,7 @@ export default class CthulhuEternalProtagonist extends foundry.abstract.TypeData }) if (!roll) return null - await roll.toMessage({}, { rollMode: roll.options.rollMode }) + await roll.toMessage({}, { messageMode: roll.options.rollMode }) } } diff --git a/module/utils.mjs b/module/utils.mjs index b84a8b4..d2a9141 100644 --- a/module/utils.mjs +++ b/module/utils.mjs @@ -16,6 +16,14 @@ export default class CthulhuEternalUtils { config: true, onChange: _ => window.location.reload() }); + game.settings.register("fvtt-cthulhu-eternal", "settings-nudge", { + name: game.i18n.localize("CTHULHUETERNAL.Settings.nudge"), + hint: game.i18n.localize("CTHULHUETERNAL.Settings.nudgeHint"), + default: true, + scope: "world", + type: Boolean, + config: true + }); game.settings.register("fvtt-cthulhu-eternal", "roll-opposed-store", { name: "Roll Opposed Store", hint: "Whether to store opposed roll results for later use", @@ -536,17 +544,63 @@ export default class CthulhuEternalUtils { }) } + static async nudgeToSuccess(rollMessage) { + if (!game.settings.get("fvtt-cthulhu-eternal", "settings-nudge")) { + ui.notifications.warn(game.i18n.localize("CTHULHUETERNAL.Settings.nudgeDisabled")) + return + } + const rollOptions = rollMessage.rolls[0]?.options + if (!rollOptions) return + const actor = game.actors.get(rollOptions.actorId) + if (!actor) return + const rollTotal = rollMessage.rolls[0].total + const targetScore = rollOptions.rollData?.targetScore ?? rollOptions.targetScore + const wpCostToSucceed = Math.ceil((rollTotal - targetScore) / 5) + + if (actor.system.wp.value < wpCostToSucceed) { + ui.notifications.warn(game.i18n.localize("CTHULHUETERNAL.Label.notEnoughWP")) + return + } + + const dialogContext = foundry.utils.duplicate(rollOptions) + dialogContext.nudgedValue = targetScore + dialogContext.wpCost = wpCostToSucceed + dialogContext.isNudgedRoll = true + dialogContext.isNudge = false + + const roll = new CthulhuEternalRoll(String(targetScore)) + await roll.evaluate() + roll.options = dialogContext + roll.displayRollResult(roll, dialogContext, dialogContext.rollData) + roll.toMessage() + + actor.system.modifyWP(-wpCostToSucceed) + + // Original roll was a failure with skill progression — nudge succeeded, so unmark progression + if (rollOptions.skillMarkedForProgress && rollOptions.rollItem?._id) { + const skillItem = actor.items.get(rollOptions.rollItem._id) + if (skillItem) await skillItem.update({ "system.rollFailed": false }) + } + + await rollMessage.delete() + } + static async nudgeRoll(rollMessage) { + if (!game.settings.get("fvtt-cthulhu-eternal", "settings-nudge")) { + ui.notifications.warn(game.i18n.localize("CTHULHUETERNAL.Settings.nudgeDisabled")) + return + } let dialogContext = rollMessage.rolls[0]?.options let actor = game.actors.get(dialogContext.actorId) + const rollTotal = rollMessage.rolls[0].total dialogContext.wpValue = actor.system.wp.value - dialogContext.rollResultIndex = rollMessage.rolls[0].total - 1 - dialogContext.minValue = Math.max(rollMessage.rolls[0].total - (dialogContext.wpValue * 5), 1) - dialogContext.maxValue = Math.min(rollMessage.rolls[0].total + (dialogContext.wpValue * 5), 100) + // Rule: 1 WP can decrease the roll by 1–5. Only decrease is allowed. + dialogContext.minValue = Math.max(rollTotal - (dialogContext.wpValue * 5), 1) + dialogContext.maxValue = rollTotal + dialogContext.rollResultIndex = rollTotal - dialogContext.minValue // index of current value in nudgeOptions dialogContext.wpCost = 0 - - // Build options table for the select operator between minValue and maxValue + dialogContext.nudgedValue = rollTotal // default: no change for the select: values from minValue to maxValue (current roll) dialogContext.nudgeOptions = Array.from({ length: dialogContext.maxValue - dialogContext.minValue + 1 }, (_, i) => dialogContext.minValue + i) const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-cthulhu-eternal/templates/nudge-dialog.hbs", dialogContext) @@ -565,6 +619,9 @@ export default class CthulhuEternalUtils { if (input.name) obj[input.name] = input.value return obj }, {}) + // Compute nudgedValue from the selected index (selectOptions uses 0-based indices for arrays) + dialogContext.nudgedValue = dialogContext.minValue + Number(output.modifiedValue) + dialogContext.wpCost = Math.ceil(Math.abs(rollTotal - dialogContext.nudgedValue) / 5) return output }, }, @@ -579,7 +636,7 @@ export default class CthulhuEternalUtils { rejectClose: false, // Click on Close button will not launch an error render: (event, dialog) => { $(".nudged-score-select").change(event => { - dialogContext.nudgedValue = Number(event.target.value) + 1 + dialogContext.nudgedValue = dialogContext.minValue + Number(event.target.value) dialogContext.wpCost = Math.ceil(Math.abs(rollMessage.rolls[0].total - dialogContext.nudgedValue) / 5) $("#nudged-wp-cost").val(dialogContext.wpCost) }) @@ -591,6 +648,11 @@ export default class CthulhuEternalUtils { return } + if (actor.system.wp.value < dialogContext.wpCost) { + ui.notifications.warn(game.i18n.localize("CTHULHUETERNAL.Label.notEnoughWP")) + return + } + const roll = new CthulhuEternalRoll(String(dialogContext.nudgedValue)) await roll.evaluate() roll.options = dialogContext @@ -601,6 +663,13 @@ export default class CthulhuEternalUtils { actor.system.modifyWP(-dialogContext.wpCost) + // If original roll had skill progression and nudge converts it to success, unmark progression + const nudgedTargetScore = dialogContext.rollData?.targetScore ?? dialogContext.targetScore + if (dialogContext.skillMarkedForProgress && dialogContext.nudgedValue <= nudgedTargetScore && dialogContext.rollItem?._id) { + const skillItem = actor.items.get(dialogContext.rollItem._id) + if (skillItem) await skillItem.update({ "system.rollFailed": false }) + } + // Delete the initial roll message await rollMessage.delete() @@ -642,15 +711,29 @@ export default class CthulhuEternalUtils { ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noActorFound")) return } - // Remove the chat message this.removeChatMessageId(message.id) - // Get the targetted actorId from the button's data attribute - let targetCombatantId = event.currentTarget.dataset.combatantId - let combatant = game.combat.combatants.get(targetCombatantId) - let targetActor = combatant.token?.actor || game.actors.get(combatant.actorId) + // Resolve the target actor: prefer canvas token (works for both combat and scene tokens) + const targetTokenId = event.currentTarget.dataset.tokenId + const targetActorId = event.currentTarget.dataset.actorId + let targetActor + if (targetTokenId) { + const token = canvas.tokens.get(targetTokenId) + targetActor = token?.actor + } + if (!targetActor && targetActorId) { + targetActor = game.actors.get(targetActorId) + } + // Legacy fallback: combatant lookup if (!targetActor) { - ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noTargetActorFound") + targetCombatantId) + const combatantId = event.currentTarget.dataset.combatantId + if (combatantId && game.combat) { + const combatant = game.combat.combatants.get(combatantId) + targetActor = combatant?.token?.actor || game.actors.get(combatant?.actorId) + } + } + if (!targetActor) { + ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noTargetActorFound")) return } targetActor.applyWounds(woundData) diff --git a/packs-system/rituals/000295.log b/packs-system/rituals/000308.log similarity index 100% rename from packs-system/rituals/000295.log rename to packs-system/rituals/000308.log diff --git a/packs-system/rituals/CURRENT b/packs-system/rituals/CURRENT index 7fe694c..759a159 100644 --- a/packs-system/rituals/CURRENT +++ b/packs-system/rituals/CURRENT @@ -1 +1 @@ -MANIFEST-000293 +MANIFEST-000306 diff --git a/packs-system/rituals/LOG b/packs-system/rituals/LOG index 84f4f93..5c6bf14 100644 --- a/packs-system/rituals/LOG +++ b/packs-system/rituals/LOG @@ -1,11 +1,7 @@ -2026/04/28-23:36:37.192093 7f5797fff6c0 Delete type=3 #1 -2026/04/28-23:37:29.068330 7f57977fe6c0 Level-0 table #296: started -2026/04/28-23:37:29.068358 7f57977fe6c0 Level-0 table #296: 0 bytes OK -2026/04/28-23:37:29.074817 7f57977fe6c0 Delete type=0 #294 -2026/04/28-23:37:29.106904 7f57977fe6c0 Manual compaction at level-0 from '!items!4oyPRBWPBWAChrJP' @ 72057594037927935 : 1 .. '!items!zVFfp3o0G0Zg3Ia4' @ 0 : 0; will stop at '!items!zVFfp3o0G0Zg3Ia4' @ 52 : 1 -2026/04/28-23:37:29.106913 7f57977fe6c0 Compacting 1@0 + 0@1 files -2026/04/28-23:37:29.111066 7f57977fe6c0 Generated table #297@0: 26 keys, 60964 bytes -2026/04/28-23:37:29.111082 7f57977fe6c0 Compacted 1@0 + 0@1 files => 60964 bytes -2026/04/28-23:37:29.117095 7f57977fe6c0 compacted to: files[ 0 1 0 0 0 0 0 ] -2026/04/28-23:37:29.117212 7f57977fe6c0 Delete type=2 #248 -2026/04/28-23:37:29.117383 7f57977fe6c0 Manual compaction at level-0 from '!items!zVFfp3o0G0Zg3Ia4' @ 52 : 1 .. '!items!zVFfp3o0G0Zg3Ia4' @ 0 : 0; will stop at (end) +2026/05/13-20:38:21.073323 7ff670fed6c0 Recovering log #304 +2026/05/13-20:38:21.083446 7ff670fed6c0 Delete type=3 #302 +2026/05/13-20:38:21.083514 7ff670fed6c0 Delete type=0 #304 +2026/05/13-21:29:49.379063 7ff6637fe6c0 Level-0 table #309: started +2026/05/13-21:29:49.379189 7ff6637fe6c0 Level-0 table #309: 0 bytes OK +2026/05/13-21:29:49.386620 7ff6637fe6c0 Delete type=0 #307 +2026/05/13-21:29:49.393683 7ff6637fe6c0 Manual compaction at level-0 from '!items!4oyPRBWPBWAChrJP' @ 72057594037927935 : 1 .. '!items!zVFfp3o0G0Zg3Ia4' @ 0 : 0; will stop at (end) diff --git a/packs-system/rituals/LOG.old b/packs-system/rituals/LOG.old index 740ac98..aa269f8 100644 --- a/packs-system/rituals/LOG.old +++ b/packs-system/rituals/LOG.old @@ -1,4 +1,7 @@ -2026/04/28-23:36:37.170404 7f5797fff6c0 Log #291: 0 ops saved to Table #292 OK -2026/04/28-23:36:37.170516 7f5797fff6c0 Archiving /home/morr/foundry/foundrydata-dev/Data/systems/fvtt-cthulhu-eternal/packs-system/rituals/000291.log: OK -2026/04/28-23:36:37.171708 7f5797fff6c0 Table #248: 26 entries OK -2026/04/28-23:36:37.175280 7f5797fff6c0 **** Repaired leveldb /home/morr/foundry/foundrydata-dev/Data/systems/fvtt-cthulhu-eternal/packs-system/rituals; recovered 1 files; 60964 bytes. Some data may have been lost. **** +2026/05/13-13:05:01.465748 7ff6717ee6c0 Recovering log #300 +2026/05/13-13:05:01.518261 7ff6717ee6c0 Delete type=3 #298 +2026/05/13-13:05:01.518318 7ff6717ee6c0 Delete type=0 #300 +2026/05/13-13:57:22.815510 7ff6637fe6c0 Level-0 table #305: started +2026/05/13-13:57:22.815533 7ff6637fe6c0 Level-0 table #305: 0 bytes OK +2026/05/13-13:57:22.821566 7ff6637fe6c0 Delete type=0 #303 +2026/05/13-13:57:22.821711 7ff6637fe6c0 Manual compaction at level-0 from '!items!4oyPRBWPBWAChrJP' @ 72057594037927935 : 1 .. '!items!zVFfp3o0G0Zg3Ia4' @ 0 : 0; will stop at (end) diff --git a/packs-system/rituals/MANIFEST-000293 b/packs-system/rituals/MANIFEST-000293 deleted file mode 100644 index 5a6db063d1c29d7f41874da8beff4f5d277c97f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 266 zcma#MqtTkoz{n_-lUkOVlai$8R9TW*o>`pgoS$2eSd>_jU&PMvgK^0nW_iWTlGNN{ zMU(u>fFP&v0H<(A=Zqq+07hvBFo4Ndg}J2_80QqHG6;ObcfsmnWK)10ou4JNu`vBoFML7Tf diff --git a/packs-system/rituals/MANIFEST-000306 b/packs-system/rituals/MANIFEST-000306 new file mode 100644 index 0000000000000000000000000000000000000000..43b294dd48d501ab3a2d97be2b7ca4a8fe8f10f1 GIT binary patch literal 174 zcmeyqC*e#h10$nUPHI_dPD+xVQ)NkNd1i5{bAE0?Vo_pAei18!ykcfaYHqP&RhU~^ zfpNZpyFpaCv1g(QqX`2Tursb?T5^XOrpzS2G9bt)Jisa3(K(~YD}YfNq6{XF%a~gN geSTaFj7*!EI2o9?FtM0CJ#au0B(Mb}uni~x02{_HKmY&$ literal 0 HcmV?d00001 diff --git a/packs-system/skills/000464.log b/packs-system/skills/000477.log similarity index 100% rename from packs-system/skills/000464.log rename to packs-system/skills/000477.log diff --git a/packs-system/skills/CURRENT b/packs-system/skills/CURRENT index dd4dce9..b79af71 100644 --- a/packs-system/skills/CURRENT +++ b/packs-system/skills/CURRENT @@ -1 +1 @@ -MANIFEST-000462 +MANIFEST-000475 diff --git a/packs-system/skills/LOG b/packs-system/skills/LOG index a88f6a4..5e7bcea 100644 --- a/packs-system/skills/LOG +++ b/packs-system/skills/LOG @@ -1,11 +1,7 @@ -2026/04/28-23:36:37.131149 7f57a57ee6c0 Delete type=3 #1 -2026/04/28-23:37:29.054894 7f57977fe6c0 Level-0 table #465: started -2026/04/28-23:37:29.054961 7f57977fe6c0 Level-0 table #465: 0 bytes OK -2026/04/28-23:37:29.061529 7f57977fe6c0 Delete type=0 #463 -2026/04/28-23:37:29.081738 7f57977fe6c0 Manual compaction at level-0 from '!folders!5PrT9QmN1cFPzDFP' @ 72057594037927935 : 1 .. '!items!zvoUByzWSWZ87fxA' @ 0 : 0; will stop at '!items!zvoUByzWSWZ87fxA' @ 1281 : 1 -2026/04/28-23:37:29.081748 7f57977fe6c0 Compacting 1@0 + 0@1 files -2026/04/28-23:37:29.088487 7f57977fe6c0 Generated table #466@0: 556 keys, 320457 bytes -2026/04/28-23:37:29.088505 7f57977fe6c0 Compacted 1@0 + 0@1 files => 320457 bytes -2026/04/28-23:37:29.094678 7f57977fe6c0 compacted to: files[ 0 1 0 0 0 0 0 ] -2026/04/28-23:37:29.094831 7f57977fe6c0 Delete type=2 #417 -2026/04/28-23:37:29.117360 7f57977fe6c0 Manual compaction at level-0 from '!items!zvoUByzWSWZ87fxA' @ 1281 : 1 .. '!items!zvoUByzWSWZ87fxA' @ 0 : 0; will stop at (end) +2026/05/13-20:38:21.045516 7ff6717ee6c0 Recovering log #473 +2026/05/13-20:38:21.056349 7ff6717ee6c0 Delete type=3 #471 +2026/05/13-20:38:21.056423 7ff6717ee6c0 Delete type=0 #473 +2026/05/13-21:29:49.443482 7ff6637fe6c0 Level-0 table #478: started +2026/05/13-21:29:49.443606 7ff6637fe6c0 Level-0 table #478: 0 bytes OK +2026/05/13-21:29:49.450275 7ff6637fe6c0 Delete type=0 #476 +2026/05/13-21:29:49.450420 7ff6637fe6c0 Manual compaction at level-0 from '!folders!5PrT9QmN1cFPzDFP' @ 72057594037927935 : 1 .. '!items!zvoUByzWSWZ87fxA' @ 0 : 0; will stop at (end) diff --git a/packs-system/skills/LOG.old b/packs-system/skills/LOG.old index 2d8ad3c..6bb4978 100644 --- a/packs-system/skills/LOG.old +++ b/packs-system/skills/LOG.old @@ -1,4 +1,7 @@ -2026/04/28-23:36:36.935963 7f57a57ee6c0 Log #460: 0 ops saved to Table #461 OK -2026/04/28-23:36:36.936025 7f57a57ee6c0 Archiving /home/morr/foundry/foundrydata-dev/Data/systems/fvtt-cthulhu-eternal/packs-system/skills/000460.log: OK -2026/04/28-23:36:36.942723 7f57a57ee6c0 Table #417: 556 entries OK -2026/04/28-23:36:36.946588 7f57a57ee6c0 **** Repaired leveldb /home/morr/foundry/foundrydata-dev/Data/systems/fvtt-cthulhu-eternal/packs-system/skills; recovered 1 files; 320457 bytes. Some data may have been lost. **** +2026/05/13-13:05:01.363329 7ff663fff6c0 Recovering log #469 +2026/05/13-13:05:01.408343 7ff663fff6c0 Delete type=3 #467 +2026/05/13-13:05:01.408405 7ff663fff6c0 Delete type=0 #469 +2026/05/13-13:57:22.802421 7ff6637fe6c0 Level-0 table #474: started +2026/05/13-13:57:22.802455 7ff6637fe6c0 Level-0 table #474: 0 bytes OK +2026/05/13-13:57:22.809135 7ff6637fe6c0 Delete type=0 #472 +2026/05/13-13:57:22.821691 7ff6637fe6c0 Manual compaction at level-0 from '!folders!5PrT9QmN1cFPzDFP' @ 72057594037927935 : 1 .. '!items!zvoUByzWSWZ87fxA' @ 0 : 0; will stop at (end) diff --git a/packs-system/skills/MANIFEST-000462 b/packs-system/skills/MANIFEST-000462 deleted file mode 100644 index ca3e25cff0f5f43ad56c153212aad14a6806600b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 273 zcma!RR(O-jz{n_-lUkOVlai$8R9TW*o>`pgoS$2eSd>_jU&PL^kon|sVMWEX{G623 zqGCnUfT9q~z+6AWWVe7S7q=sbv;ugTz p#spSK$RL3iTQ74nFf!d?=44>L%gnNedu5rAG)UwwNaQ|91OV8bGA954 literal 0 HcmV?d00001 diff --git a/packs-system/weapons/000110.log b/packs-system/weapons/000123.log similarity index 100% rename from packs-system/weapons/000110.log rename to packs-system/weapons/000123.log diff --git a/packs-system/weapons/CURRENT b/packs-system/weapons/CURRENT index db6fa61..2b465ed 100644 --- a/packs-system/weapons/CURRENT +++ b/packs-system/weapons/CURRENT @@ -1 +1 @@ -MANIFEST-000108 +MANIFEST-000121 diff --git a/packs-system/weapons/LOG b/packs-system/weapons/LOG index f62f6d5..bf270b5 100644 --- a/packs-system/weapons/LOG +++ b/packs-system/weapons/LOG @@ -1,11 +1,7 @@ -2026/04/28-23:36:37.166060 7f57a4fed6c0 Delete type=3 #1 -2026/04/28-23:37:29.061681 7f57977fe6c0 Level-0 table #111: started -2026/04/28-23:37:29.061722 7f57977fe6c0 Level-0 table #111: 0 bytes OK -2026/04/28-23:37:29.068229 7f57977fe6c0 Delete type=0 #109 -2026/04/28-23:37:29.095030 7f57977fe6c0 Manual compaction at level-0 from '!folders!0DI3T2jve3nsmsfZ' @ 72057594037927935 : 1 .. '!items!zyxA9DhO36t5OBDv' @ 0 : 0; will stop at '!items!zyxA9DhO36t5OBDv' @ 55 : 1 -2026/04/28-23:37:29.095044 7f57977fe6c0 Compacting 1@0 + 0@1 files -2026/04/28-23:37:29.100652 7f57977fe6c0 Generated table #112@0: 362 keys, 93592 bytes -2026/04/28-23:37:29.100675 7f57977fe6c0 Compacted 1@0 + 0@1 files => 93592 bytes -2026/04/28-23:37:29.106685 7f57977fe6c0 compacted to: files[ 0 1 0 0 0 0 0 ] -2026/04/28-23:37:29.106785 7f57977fe6c0 Delete type=2 #63 -2026/04/28-23:37:29.117373 7f57977fe6c0 Manual compaction at level-0 from '!items!zyxA9DhO36t5OBDv' @ 55 : 1 .. '!items!zyxA9DhO36t5OBDv' @ 0 : 0; will stop at (end) +2026/05/13-20:38:21.061357 7ff663fff6c0 Recovering log #119 +2026/05/13-20:38:21.070703 7ff663fff6c0 Delete type=3 #117 +2026/05/13-20:38:21.070750 7ff663fff6c0 Delete type=0 #119 +2026/05/13-21:29:49.386758 7ff6637fe6c0 Level-0 table #124: started +2026/05/13-21:29:49.386971 7ff6637fe6c0 Level-0 table #124: 0 bytes OK +2026/05/13-21:29:49.393349 7ff6637fe6c0 Delete type=0 #122 +2026/05/13-21:29:49.393700 7ff6637fe6c0 Manual compaction at level-0 from '!folders!0DI3T2jve3nsmsfZ' @ 72057594037927935 : 1 .. '!items!zyxA9DhO36t5OBDv' @ 0 : 0; will stop at (end) diff --git a/packs-system/weapons/LOG.old b/packs-system/weapons/LOG.old index 5a90874..12177b0 100644 --- a/packs-system/weapons/LOG.old +++ b/packs-system/weapons/LOG.old @@ -1,4 +1,7 @@ -2026/04/28-23:36:37.136506 7f57a4fed6c0 Log #106: 0 ops saved to Table #107 OK -2026/04/28-23:36:37.136625 7f57a4fed6c0 Archiving /home/morr/foundry/foundrydata-dev/Data/systems/fvtt-cthulhu-eternal/packs-system/weapons/000106.log: OK -2026/04/28-23:36:37.141991 7f57a4fed6c0 Table #63: 362 entries OK -2026/04/28-23:36:37.145196 7f57a4fed6c0 **** Repaired leveldb /home/morr/foundry/foundrydata-dev/Data/systems/fvtt-cthulhu-eternal/packs-system/weapons; recovered 1 files; 93592 bytes. Some data may have been lost. **** +2026/05/13-13:05:01.411955 7ff671fef6c0 Recovering log #115 +2026/05/13-13:05:01.463033 7ff671fef6c0 Delete type=3 #113 +2026/05/13-13:05:01.463084 7ff671fef6c0 Delete type=0 #115 +2026/05/13-13:57:22.809226 7ff6637fe6c0 Level-0 table #120: started +2026/05/13-13:57:22.809250 7ff6637fe6c0 Level-0 table #120: 0 bytes OK +2026/05/13-13:57:22.815441 7ff6637fe6c0 Delete type=0 #118 +2026/05/13-13:57:22.821701 7ff6637fe6c0 Manual compaction at level-0 from '!folders!0DI3T2jve3nsmsfZ' @ 72057594037927935 : 1 .. '!items!zyxA9DhO36t5OBDv' @ 0 : 0; will stop at (end) diff --git a/packs-system/weapons/MANIFEST-000108 b/packs-system/weapons/MANIFEST-000108 deleted file mode 100644 index 372c91e66975d16bc3aa48a275d59b86377183a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 264 zcmeBpnjD?Nz{n_-lUkOVlai$8R9TW*o>`pgoS$2eSd>_jU&PK}KjSv5qGDQpPD*M~ zv7&*Cr*Vi;R#~cXUU6=5S`_0#1~8CU%q&UGEmo|mtZ=k+$?!KeD>3zVaw%gphsX!I zcP!*&U}VbWWMIx?xyvl<&LIrq=K=WzK>nY$!ss$6zYxf0WguiC8-qPNV*ynh2LRjk BMn3=m diff --git a/packs-system/weapons/MANIFEST-000121 b/packs-system/weapons/MANIFEST-000121 new file mode 100644 index 0000000000000000000000000000000000000000..9498b0ddeacc68aedb8ee85709af8c2133e0a41f GIT binary patch literal 173 zcmWf1JiD}wfss)vC$%g!CnZVGsj?)sJhM2}IX|}`u_&=5zlfDVUNN&IHMdx?sEO(h#^DpHC@vDLSS|A?)kH{|b literal 0 HcmV?d00001 diff --git a/templates/chat-lethal-damage.hbs b/templates/chat-lethal-damage.hbs index 24a436c..4f99c53 100644 --- a/templates/chat-lethal-damage.hbs +++ b/templates/chat-lethal-damage.hbs @@ -22,10 +22,20 @@ {{/if}}
  • - - +
    {{localize "CTHULHUETERNAL.Label.applyWounds"}}
    +
    + {{#each combatants}} + + {{/each}} +
  • {{#if isLethal}} diff --git a/templates/chat-message.hbs b/templates/chat-message.hbs index 8e89c8d..2cd5496 100644 --- a/templates/chat-message.hbs +++ b/templates/chat-message.hbs @@ -150,6 +150,13 @@ {{/unless}} + {{#if skillMarkedForProgress}} +

    + + {{rollItem.name}} — {{localize "CTHULHUETERNAL.Label.skillFailed"}} +

    + {{/if}} + {{! Zone d'actions regroupées }}
    {{#if isSuccess}} @@ -233,6 +240,15 @@ > + {{#if wpCostToSucceed}} + + + {{localize "CTHULHUETERNAL.Roll.nudgeToSuccess" score=targetScore wp=wpCostToSucceed}} + + {{/if}} {{/if}} {{/if}} diff --git a/templates/chat-regular-damage.hbs b/templates/chat-regular-damage.hbs index 8f38121..d670ac2 100644 --- a/templates/chat-regular-damage.hbs +++ b/templates/chat-regular-damage.hbs @@ -39,8 +39,10 @@
    {{#each combatants}} - - - - + + + + +
  • - - - - - + + + + +
  • diff --git a/templates/chat-san-type-request.hbs b/templates/chat-san-type-request.hbs index b7d33ae..c527d17 100644 --- a/templates/chat-san-type-request.hbs +++ b/templates/chat-san-type-request.hbs @@ -5,12 +5,12 @@
  • {{localize "CTHULHUETERNAL.Label.selectSANType"}}
  • - - + +
  • - - + +
  • diff --git a/utf8_checker.py b/utf8_checker.py new file mode 100644 index 0000000..ba339f6 --- /dev/null +++ b/utf8_checker.py @@ -0,0 +1,46 @@ +import os + +def check_utf8_encoding(filenames): + """ + Checks a list of filenames to detect potential UTF-8 encoding errors. + + Args: + filenames (list): A list of strings representing filenames to check. + + Returns: + dict: A dictionary where keys are filenames with errors and values are the error messages. + """ + error_report = {} + print("--- Starting UTF-8 Encoding Check ---") + + for filename in filenames: + try: + # Attempt to decode the filename as UTF-8 + filename.encode('utf-8') + # If encode succeeds, it's likely valid UTF-8 + print(f"SUCCESS: {filename}") + except UnicodeEncodeError as e: + # If encoding fails, it indicates invalid UTF-8 sequences + error_report[filename] = str(e) + print(f"ERROR: {filename} - Decoding failed: {e}") + + print("--- Check Complete ---") + if error_report: + print("\n--- Summary of Errors ---") + for filename, error in error_report.items(): + print(f"File: {filename}\n Error: {error}\n") + else: + print("\nAll checked filenames appear to be valid UTF-8.") + + return error_report + +if __name__ == "__main__": + # Example list of filenames to check. Replace these with your actual directory contents. + files_to_check = [ + "valid_file.txt", + "file_with_bad_byte\x80.bin", # Example of a potentially bad filename + "another_valid_one.md", + "file_with_another_error\x99.dat" + ] + + check_utf8_encoding(files_to_check) \ No newline at end of file