Fix release numbering
This commit is contained in:
@@ -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);
|
||||
|
||||
+62
-23
@@ -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 })
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
+7
-1
@@ -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.",
|
||||
|
||||
+7
-1
@@ -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.",
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+50
-38
@@ -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: `<div class="cthulhu-eternal-roll"><p>${flavor}</p></div>`,
|
||||
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
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 })
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+95
-12
@@ -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)
|
||||
|
||||
@@ -1 +1 @@
|
||||
MANIFEST-000293
|
||||
MANIFEST-000306
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
MANIFEST-000462
|
||||
MANIFEST-000475
|
||||
|
||||
+7
-11
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
MANIFEST-000108
|
||||
MANIFEST-000121
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -22,10 +22,20 @@
|
||||
{{/if}}
|
||||
|
||||
<li class="li-apply-wounds">
|
||||
<button type="button" class="apply-wounds">{{localize "CTHULHUETERNAL.Label.applyWounds"}}</button>
|
||||
<select name="combatant" class="roll-skill-modifier">
|
||||
{{selectOptions combatants valueAttr="id" labelAttr="name"}}
|
||||
</select>
|
||||
<div>{{localize "CTHULHUETERNAL.Label.applyWounds"}}</div>
|
||||
<div class="combatants-grid">
|
||||
{{#each combatants}}
|
||||
<button
|
||||
class="apply-wounds-btn chat-action-button"
|
||||
data-combatant-id="{{this.id}}"
|
||||
data-actor-id="{{this.actorId}}"
|
||||
data-token-id="{{this.tokenId}}"
|
||||
title="{{this.name}}"
|
||||
>
|
||||
{{this.name}}
|
||||
</button>
|
||||
{{/each}}
|
||||
</div>
|
||||
</li>
|
||||
|
||||
{{#if isLethal}}
|
||||
|
||||
@@ -150,6 +150,13 @@
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
||||
{{#if skillMarkedForProgress}}
|
||||
<p class="skill-progress-notice">
|
||||
<i class="fa-solid fa-arrow-up-right-dots"></i>
|
||||
{{rollItem.name}} — {{localize "CTHULHUETERNAL.Label.skillFailed"}}
|
||||
</p>
|
||||
{{/if}}
|
||||
|
||||
{{! Zone d'actions regroupées }}
|
||||
<div class="chat-actions">
|
||||
{{#if isSuccess}}
|
||||
@@ -233,6 +240,15 @@
|
||||
>
|
||||
<i class="fa-solid fa-circle-sort-down"></i>
|
||||
</a>
|
||||
{{#if wpCostToSucceed}}
|
||||
<a
|
||||
class="nudge-to-success chat-action-button"
|
||||
data-tooltip="{{localize 'CTHULHUETERNAL.Roll.nudgeToSuccess' score=targetScore wp=wpCostToSucceed}}"
|
||||
>
|
||||
<i class="fa-solid fa-circle-check"></i>
|
||||
{{localize "CTHULHUETERNAL.Roll.nudgeToSuccess" score=targetScore wp=wpCostToSucceed}}
|
||||
</a>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
|
||||
@@ -39,8 +39,10 @@
|
||||
<div class="combatants-grid">
|
||||
{{#each combatants}}
|
||||
<button
|
||||
class="apply-wounds-btn"
|
||||
class="apply-wounds-btn chat-action-button"
|
||||
data-combatant-id="{{this.id}}"
|
||||
data-actor-id="{{this.actorId}}"
|
||||
data-token-id="{{this.tokenId}}"
|
||||
title="{{this.name}}"
|
||||
>
|
||||
{{this.name}}
|
||||
|
||||
@@ -8,19 +8,19 @@
|
||||
{{/if}}
|
||||
|
||||
<li class="san-loose-buttons">
|
||||
<button class="san-loose" data-san-value="0">0</button>
|
||||
<button class="san-loose" data-san-value="1">1</button>
|
||||
<button class="san-loose" data-san-value="2">2</button>
|
||||
<button class="san-loose" data-san-value="3">3</button>
|
||||
<button class="san-loose" data-san-value="4">4</button>
|
||||
<button class="san-loose chat-action-button" data-san-value="0">0</button>
|
||||
<button class="san-loose chat-action-button" data-san-value="1">1</button>
|
||||
<button class="san-loose chat-action-button" data-san-value="2">2</button>
|
||||
<button class="san-loose chat-action-button" data-san-value="3">3</button>
|
||||
<button class="san-loose chat-action-button" data-san-value="4">4</button>
|
||||
</li>
|
||||
|
||||
<li class="san-loose-buttons">
|
||||
<button class="san-loose" data-san-value="1d4">1d4</button>
|
||||
<button class="san-loose" data-san-value="1d6">1d6</button>
|
||||
<button class="san-loose" data-san-value="1d8">1d8</button>
|
||||
<button class="san-loose" data-san-value="1d10">1d10</button>
|
||||
<button class="san-loose" data-san-value="1d12">1d12</button>
|
||||
<button class="san-loose chat-action-button" data-san-value="1d4">1d4</button>
|
||||
<button class="san-loose chat-action-button" data-san-value="1d6">1d6</button>
|
||||
<button class="san-loose chat-action-button" data-san-value="1d8">1d8</button>
|
||||
<button class="san-loose chat-action-button" data-san-value="1d10">1d10</button>
|
||||
<button class="san-loose chat-action-button" data-san-value="1d12">1d12</button>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
<li><strong>{{localize "CTHULHUETERNAL.Label.selectSANType"}}</strong></li>
|
||||
|
||||
<li class="san-type-buttons">
|
||||
<button class="san-type" data-san-value="{{sanLoss}}" data-san-type="violence">{{localize "CTHULHUETERNAL.Label.Violence"}}</button>
|
||||
<button class="san-type" data-san-value="{{sanLoss}}" data-san-type="helplessness">{{localize "CTHULHUETERNAL.Label.Helplessness"}}</button>
|
||||
<button class="san-type chat-action-button" data-san-value="{{sanLoss}}" data-san-type="violence">{{localize "CTHULHUETERNAL.Label.Violence"}}</button>
|
||||
<button class="san-type chat-action-button" data-san-value="{{sanLoss}}" data-san-type="helplessness">{{localize "CTHULHUETERNAL.Label.Helplessness"}}</button>
|
||||
</li>
|
||||
<li class="san-type-buttons">
|
||||
<button class="san-type" data-san-value="{{sanLoss}}" data-san-type="unnatural">{{localize "CTHULHUETERNAL.Label.Unnatural"}}</button>
|
||||
<button class="san-type" data-san-value="{{sanLoss}}" data-san-type="none">{{localize "CTHULHUETERNAL.Label.None"}}</button>
|
||||
<button class="san-type chat-action-button" data-san-value="{{sanLoss}}" data-san-type="unnatural">{{localize "CTHULHUETERNAL.Label.Unnatural"}}</button>
|
||||
<button class="san-type chat-action-button" data-san-value="{{sanLoss}}" data-san-type="none">{{localize "CTHULHUETERNAL.Label.None"}}</button>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user