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);
|
font-size: calc(var(--font-size-standard) * 1.2);
|
||||||
text-shadow: 0 0 10px var(--color-shadow-primary);
|
text-shadow: 0 0 10px var(--color-shadow-primary);
|
||||||
}
|
}
|
||||||
.dice-roll .chat-actions {
|
.dice-roll .skill-progress-notice {
|
||||||
display: flex;
|
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;
|
flex-wrap: wrap;
|
||||||
gap: 0.375rem;
|
gap: 0.375rem;
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
@@ -3210,9 +3218,23 @@ i.fvtt-cthulhu-eternal {
|
|||||||
.dice-roll .chat-actions .nudge-roll,
|
.dice-roll .chat-actions .nudge-roll,
|
||||||
.dice-roll .chat-actions .damage-roll,
|
.dice-roll .chat-actions .damage-roll,
|
||||||
.dice-roll .chat-actions .healing-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;
|
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 {
|
.opposed-roll-result {
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
background: rgba(0, 0, 0, 0.05);
|
background: rgba(0, 0, 0, 0.05);
|
||||||
|
|||||||
+63
-24
@@ -159,11 +159,42 @@ Hooks.on('tokenActionHudCoreApiReady', async () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Hooks.on("renderChatMessageHTML", (message, html, data) => {
|
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
|
// Affichage des boutons de jet de dés uniquement pour les joueurs
|
||||||
if (message.author.id === game.user.id || game.user.isGM) {
|
if (message.author.id === game.user.id || game.user.isGM) {
|
||||||
$(html).find(".nudge-roll").each((i, btn) => {
|
$(html).find(".nudge-roll").each((i, btn) => {
|
||||||
btn.style.display = "inline"
|
btn.style.display = "inline"
|
||||||
})
|
})
|
||||||
|
$(html).find(".nudge-to-success").each((i, btn) => {
|
||||||
|
btn.style.display = "inline-flex"
|
||||||
|
})
|
||||||
$(html).find(".damage-roll").each((i, btn) => {
|
$(html).find(".damage-roll").each((i, btn) => {
|
||||||
btn.style.display = "inline"
|
btn.style.display = "inline"
|
||||||
})
|
})
|
||||||
@@ -178,47 +209,55 @@ Hooks.on("renderChatMessageHTML", (message, html, data) => {
|
|||||||
btn.style.display = "inline"
|
btn.style.display = "inline"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
$(html).find(".nudge-roll").click((event) => {
|
$(html).find(".nudge-roll").click(withActionLock(() => {
|
||||||
CthulhuEternalUtils.nudgeRoll(message)
|
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")
|
let formula = $(event.currentTarget).data("roll-value")
|
||||||
CthulhuEternalUtils.damageRoll(message, formula)
|
CthulhuEternalUtils.damageRoll(message, formula)
|
||||||
})
|
}))
|
||||||
$(html).find(".healing-roll").click((event) => {
|
$(html).find(".healing-roll").click(withActionLock(() => {
|
||||||
CthulhuEternalUtils.healingRoll(message)
|
CthulhuEternalUtils.healingRoll(message)
|
||||||
})
|
}))
|
||||||
$(html).find(".worn-weapon-check").click((event) => {
|
$(html).find(".worn-weapon-check").click(withActionLock(() => {
|
||||||
CthulhuEternalUtils.wornWeaponCheck(message)
|
CthulhuEternalUtils.wornWeaponCheck(message)
|
||||||
})
|
}))
|
||||||
$(html).find(".san-loose").click((event) => {
|
$(html).find(".san-loose").click(withActionLock((event) => {
|
||||||
CthulhuEternalUtils.applySANLoss(message, event)
|
CthulhuEternalUtils.applySANLoss(message, event)
|
||||||
})
|
}))
|
||||||
$(html).find(".san-type").click((event) => {
|
$(html).find(".san-type").click(withActionLock((event) => {
|
||||||
CthulhuEternalUtils.applySANType(message, event)
|
CthulhuEternalUtils.applySANType(message, event)
|
||||||
})
|
}))
|
||||||
$(html).find(".opposed-roll").click((event) => {
|
$(html).find(".opposed-roll").click(withActionLock((event) => {
|
||||||
CthulhuEternalUtils.opposedRollManagement(message, event)
|
CthulhuEternalUtils.opposedRollManagement(message, event)
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
if (game.user.isGM) {
|
if (game.user.isGM) {
|
||||||
$(html).find(".li-apply-wounds").each((i, btn) => {
|
$(html).find(".li-apply-wounds").each((i, btn) => {
|
||||||
btn.style.display = "block"
|
btn.style.display = "block"
|
||||||
})
|
})
|
||||||
$(html).find(".apply-wounds-btn").click((event) => {
|
$(html).find(".apply-wounds-btn").click(withActionLock((event) => {
|
||||||
CthulhuEternalUtils.applyWounds(message, event)
|
CthulhuEternalUtils.applyWounds(message, event)
|
||||||
})
|
}))
|
||||||
$(html).find(".apply-wounds-btn").hover(
|
$(html).find(".apply-wounds-btn").hover(
|
||||||
function (event) {
|
function (event) {
|
||||||
// Mouse enter - select the token
|
// Mouse enter - highlight the token on the canvas
|
||||||
let combatantId = $(this).data("combatant-id")
|
const tokenId = $(this).data("token-id")
|
||||||
if (combatantId && game.combat) {
|
if (tokenId) {
|
||||||
let combatant = game.combat.combatants.get(combatantId)
|
const token = canvas.tokens.get(tokenId)
|
||||||
if (combatant?.token) {
|
if (token) token.control({ releaseOthers: true })
|
||||||
let token = canvas.tokens.get(combatant.token.id)
|
return
|
||||||
if (token) {
|
|
||||||
token.control({ releaseOthers: true })
|
|
||||||
}
|
}
|
||||||
|
// Legacy: resolve via combatant
|
||||||
|
const combatantId = $(this).data("combatant-id")
|
||||||
|
if (combatantId && game.combat) {
|
||||||
|
const combatant = game.combat.combatants.get(combatantId)
|
||||||
|
if (combatant?.token) {
|
||||||
|
const token = canvas.tokens.get(combatant.token.id)
|
||||||
|
if (token) token.control({ releaseOthers: true })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
+7
-1
@@ -24,6 +24,9 @@
|
|||||||
"Settings": {
|
"Settings": {
|
||||||
"era": "Select the era of your game",
|
"era": "Select the era of your game",
|
||||||
"eraHint": "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",
|
"Common": "Common",
|
||||||
"Classical": "Classical",
|
"Classical": "Classical",
|
||||||
"Medieval": "Medieval",
|
"Medieval": "Medieval",
|
||||||
@@ -622,6 +625,7 @@
|
|||||||
"Weapon": "Weapon",
|
"Weapon": "Weapon",
|
||||||
"ZeroWP": "Zero WP : Automatic failure (ie 0%)",
|
"ZeroWP": "Zero WP : Automatic failure (ie 0%)",
|
||||||
"LowWP": "Low WP",
|
"LowWP": "Low WP",
|
||||||
|
"notEnoughWP": "Not enough WP to perform this action",
|
||||||
"Exhausted": "Exhausted",
|
"Exhausted": "Exhausted",
|
||||||
"wornWeaponWarning": "Worn weapon: Don't forget to make the luck roll after the attack!",
|
"wornWeaponWarning": "Worn weapon: Don't forget to make the luck roll after the attack!",
|
||||||
"creature": "Creature",
|
"creature": "Creature",
|
||||||
@@ -773,6 +777,7 @@
|
|||||||
"lethalityLethal": "Lethal !!",
|
"lethalityLethal": "Lethal !!",
|
||||||
"lethalityNotLethal": "Non-Lethal",
|
"lethalityNotLethal": "Non-Lethal",
|
||||||
"WPSpent": "WP Spent",
|
"WPSpent": "WP Spent",
|
||||||
|
"alreadyUsed": "Already used",
|
||||||
"targetMove": "Target Move",
|
"targetMove": "Target Move",
|
||||||
"attackerState": "Attacker State",
|
"attackerState": "Attacker State",
|
||||||
"targetSize": "Target Size",
|
"targetSize": "Target Size",
|
||||||
@@ -808,7 +813,8 @@
|
|||||||
"roll": "Roll",
|
"roll": "Roll",
|
||||||
"applyNudge": "Apply",
|
"applyNudge": "Apply",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"nudgeRoll": "Nudge Roll"
|
"nudgeRoll": "Nudge Roll",
|
||||||
|
"nudgeToSuccess": "Succeed at {score}% for {wp} WP"
|
||||||
},
|
},
|
||||||
"Tooltip": {
|
"Tooltip": {
|
||||||
"sanBP": ">5 SAN lost in one roll, temporary insanity. If SAN less reaches BP = a Disorder unconscious Breaking and AND reset BP.",
|
"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": {
|
"Settings": {
|
||||||
"era": "Sélectionnez l'époque de votre jeu",
|
"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.",
|
"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",
|
"Common": "Commun",
|
||||||
"Classical": "Classique",
|
"Classical": "Classique",
|
||||||
"Medieval": "Médiéval",
|
"Medieval": "Médiéval",
|
||||||
@@ -616,6 +619,7 @@
|
|||||||
"Weapon": "Arme",
|
"Weapon": "Arme",
|
||||||
"ZeroWP": "PVO à 0 : Echec automatique (ie 0%)",
|
"ZeroWP": "PVO à 0 : Echec automatique (ie 0%)",
|
||||||
"LowWP": "PVO faibles",
|
"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",
|
"Exhausted": "Epuisé", "wornWeaponWarning": "Arme usée : N'oubliez pas de faire le jet de chance après l'attaque !", "creature": "Créature",
|
||||||
"Rituals": "Rituels",
|
"Rituals": "Rituels",
|
||||||
"Tomes": "Ouvrages",
|
"Tomes": "Ouvrages",
|
||||||
@@ -764,6 +768,7 @@
|
|||||||
"lethalityLethal": "Létal !!",
|
"lethalityLethal": "Létal !!",
|
||||||
"lethalityNotLethal": "Non létal",
|
"lethalityNotLethal": "Non létal",
|
||||||
"WPSpent": "PVO dépensés",
|
"WPSpent": "PVO dépensés",
|
||||||
|
"alreadyUsed": "Déjà utilisé",
|
||||||
"targetMove": "Mouvement de la cible",
|
"targetMove": "Mouvement de la cible",
|
||||||
"attackerState": "Etat de l'attaquant",
|
"attackerState": "Etat de l'attaquant",
|
||||||
"targetSize": "Taille de la cible",
|
"targetSize": "Taille de la cible",
|
||||||
@@ -800,7 +805,8 @@
|
|||||||
"roll": "Jet",
|
"roll": "Jet",
|
||||||
"applyNudge": "Lancer",
|
"applyNudge": "Lancer",
|
||||||
"cancel": "Annuler",
|
"cancel": "Annuler",
|
||||||
"nudgeRoll": "Modifier le jet"
|
"nudgeRoll": "Modifier le jet",
|
||||||
|
"nudgeToSuccess": "Réussir à {score}% pour {wp} PVO"
|
||||||
},
|
},
|
||||||
"Tooltip": {
|
"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.",
|
"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) {
|
async _onDrop(event) {
|
||||||
if (!this.isEditable || !this.isEditMode) return
|
if (!this.isEditable || !this.isEditMode) return
|
||||||
const data = TextEditor.getDragEventData(event)
|
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
|
||||||
|
|
||||||
// Handle different data types
|
// Handle different data types
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
|
|||||||
@@ -264,7 +264,7 @@ export default class CthulhuEternalProtagonistSheet extends CthulhuEternalActorS
|
|||||||
|
|
||||||
async _onDrop(event) {
|
async _onDrop(event) {
|
||||||
if (!this.isEditable || !this.isEditMode) return
|
if (!this.isEditable || !this.isEditMode) return
|
||||||
const data = TextEditor.getDragEventData(event)
|
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
|
||||||
|
|
||||||
// Handle different data types
|
// Handle different data types
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
|
|||||||
@@ -81,8 +81,8 @@ export default class CthulhuEternalVehicleSheet extends CthulhuEternalActorSheet
|
|||||||
break
|
break
|
||||||
case "description":
|
case "description":
|
||||||
context.tab = context.tabs.description
|
context.tab = context.tabs.description
|
||||||
context.enrichedDescription = await TextEditor.enrichHTML(doc.system.description, { async: true })
|
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(doc.system.description, { async: true })
|
||||||
context.enrichedNotes = await TextEditor.enrichHTML(doc.system.notes, { async: true })
|
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(doc.system.notes, { async: true })
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return context
|
return context
|
||||||
@@ -104,7 +104,7 @@ export default class CthulhuEternalVehicleSheet extends CthulhuEternalActorSheet
|
|||||||
|
|
||||||
async _onDrop(event) {
|
async _onDrop(event) {
|
||||||
if (!this.isEditable || !this.isEditMode) return
|
if (!this.isEditable || !this.isEditMode) return
|
||||||
const data = TextEditor.getDragEventData(event)
|
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
|
||||||
|
|
||||||
// Handle different data types
|
// Handle different data types
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
|
|||||||
+45
-33
@@ -139,11 +139,19 @@ export default class CthulhuEternalRoll extends Roll {
|
|||||||
weapon.system.killRadius = choice.killRadius // Override kill radius
|
weapon.system.killRadius = choice.killRadius // Override kill radius
|
||||||
}
|
}
|
||||||
|
|
||||||
let combatants = []
|
let targets = []
|
||||||
if (game?.combat?.combatants) {
|
if (game?.combat?.combatants) {
|
||||||
for (let c of game.combat.combatants) {
|
for (let c of game.combat.combatants) {
|
||||||
if (c.actorid !== actor.id) {
|
if (c.actorId !== actor.id) {
|
||||||
combatants.push({ id: c.id, name: c.name })
|
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,
|
isLethal,
|
||||||
ammoUsed: weapon?.ammoUsed || 0,
|
ammoUsed: weapon?.ammoUsed || 0,
|
||||||
rollResult: lethalityRoll.total,
|
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({
|
let msg = await ChatMessage.create({
|
||||||
user: game.user.id,
|
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 }),
|
speaker: ChatMessage.getSpeaker({ actor: actor }),
|
||||||
}, { rollMode: options.rollMode, create: true })
|
}, { rollMode: options.rollMode, create: true })
|
||||||
await msg.setFlag("fvtt-cthulhu-eternal", "woundData", msgData)
|
await msg.setFlag("fvtt-cthulhu-eternal", "woundData", msgData)
|
||||||
@@ -191,12 +198,11 @@ export default class CthulhuEternalRoll extends Roll {
|
|||||||
formula,
|
formula,
|
||||||
ammoUsed: weapon?.ammoUsed || 0,
|
ammoUsed: weapon?.ammoUsed || 0,
|
||||||
rollResult: damageRoll.total,
|
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({
|
let msg = await ChatMessage.create({
|
||||||
user: game.user.id,
|
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 }),
|
speaker: ChatMessage.getSpeaker({ actor: actor }),
|
||||||
}, { rollMode: options.rollMode, create: true })
|
}, { rollMode: options.rollMode, create: true })
|
||||||
await msg.setFlag("fvtt-cthulhu-eternal", "woundData", msgData)
|
await msg.setFlag("fvtt-cthulhu-eternal", "woundData", msgData)
|
||||||
@@ -254,7 +260,8 @@ export default class CthulhuEternalRoll extends Roll {
|
|||||||
let formula = "1d100"
|
let formula = "1d100"
|
||||||
let hasModifier = true
|
let hasModifier = true
|
||||||
let hasMultiplier = false
|
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 actor = game.actors.get(options.actorId)
|
||||||
|
|
||||||
let target = CthulhuEternalUtils.getTarget()
|
let target = CthulhuEternalUtils.getTarget()
|
||||||
@@ -271,7 +278,7 @@ export default class CthulhuEternalRoll extends Roll {
|
|||||||
case "san":
|
case "san":
|
||||||
case "char":
|
case "char":
|
||||||
options.initialScore = options.rollItem.targetScore
|
options.initialScore = options.rollItem.targetScore
|
||||||
options.isNudge = (options.rollType !== "san")
|
options.isNudge = nudgeEnabled && (options.rollType !== "san")
|
||||||
break
|
break
|
||||||
case "resource":
|
case "resource":
|
||||||
hasModifier = false
|
hasModifier = false
|
||||||
@@ -322,7 +329,7 @@ export default class CthulhuEternalRoll extends Roll {
|
|||||||
break
|
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({
|
const fieldRollMode = new foundry.data.fields.StringField({
|
||||||
choices: rollModes,
|
choices: rollModes,
|
||||||
blank: false,
|
blank: false,
|
||||||
@@ -526,6 +533,16 @@ export default class CthulhuEternalRoll extends Roll {
|
|||||||
}
|
}
|
||||||
rollData.resultType = resultType
|
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.isLowWP = rollData.isLowWP
|
||||||
this.options.isZeroWP = rollData.isZeroWP
|
this.options.isZeroWP = rollData.isZeroWP
|
||||||
this.options.isExhausted = rollData.isExhausted
|
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.
|
* @param {boolean} [options.create=true] Whether to create the message.
|
||||||
* @returns {Promise} - A promise that resolves when the message is created.
|
* @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(
|
let rollMsg = await super.toMessage(
|
||||||
{
|
{
|
||||||
isFailure: this.resultType === "failure",
|
isFailure: this.resultType === "failure",
|
||||||
@@ -695,34 +724,17 @@ export default class CthulhuEternalRoll extends Roll {
|
|||||||
realDamage: this.realDamage,
|
realDamage: this.realDamage,
|
||||||
...messageData,
|
...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)
|
await rollMsg.setFlag("fvtt-cthulhu-eternal", "rollData", rollData)
|
||||||
|
|
||||||
// Manage the skill evolution if the roll is a failure
|
// Apply skill rollFailed flag after the message is created
|
||||||
if (rollData.resultType.includes("failure") && rollItem.type === "skill") {
|
if (skillMarkedForProgress) {
|
||||||
// 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)
|
const actor = game.actors.get(rollData.actorId)
|
||||||
await actor.updateEmbeddedDocuments("Item", [{
|
await actor.updateEmbeddedDocuments("Item", [{
|
||||||
_id: rollItem._id,
|
_id: rollItem._id,
|
||||||
"system.rollFailed": true
|
"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 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the roll is a SAN roll, we propose to select the SAN loss
|
// 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
|
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
|
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,
|
config: true,
|
||||||
onChange: _ => window.location.reload()
|
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", {
|
game.settings.register("fvtt-cthulhu-eternal", "roll-opposed-store", {
|
||||||
name: "Roll Opposed Store",
|
name: "Roll Opposed Store",
|
||||||
hint: "Whether to store opposed roll results for later use",
|
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) {
|
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 dialogContext = rollMessage.rolls[0]?.options
|
||||||
let actor = game.actors.get(dialogContext.actorId)
|
let actor = game.actors.get(dialogContext.actorId)
|
||||||
|
const rollTotal = rollMessage.rolls[0].total
|
||||||
dialogContext.wpValue = actor.system.wp.value
|
dialogContext.wpValue = actor.system.wp.value
|
||||||
dialogContext.rollResultIndex = rollMessage.rolls[0].total - 1
|
// Rule: 1 WP can decrease the roll by 1–5. Only decrease is allowed.
|
||||||
dialogContext.minValue = Math.max(rollMessage.rolls[0].total - (dialogContext.wpValue * 5), 1)
|
dialogContext.minValue = Math.max(rollTotal - (dialogContext.wpValue * 5), 1)
|
||||||
dialogContext.maxValue = Math.min(rollMessage.rolls[0].total + (dialogContext.wpValue * 5), 100)
|
dialogContext.maxValue = rollTotal
|
||||||
|
dialogContext.rollResultIndex = rollTotal - dialogContext.minValue // index of current value in nudgeOptions
|
||||||
dialogContext.wpCost = 0
|
dialogContext.wpCost = 0
|
||||||
|
dialogContext.nudgedValue = rollTotal // default: no change for the select: values from minValue to maxValue (current roll)
|
||||||
// Build options table for the select operator between minValue and maxValue
|
|
||||||
dialogContext.nudgeOptions = Array.from({ length: dialogContext.maxValue - dialogContext.minValue + 1 }, (_, i) => dialogContext.minValue + i)
|
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)
|
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
|
if (input.name) obj[input.name] = input.value
|
||||||
return obj
|
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
|
return output
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -579,7 +636,7 @@ export default class CthulhuEternalUtils {
|
|||||||
rejectClose: false, // Click on Close button will not launch an error
|
rejectClose: false, // Click on Close button will not launch an error
|
||||||
render: (event, dialog) => {
|
render: (event, dialog) => {
|
||||||
$(".nudged-score-select").change(event => {
|
$(".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)
|
dialogContext.wpCost = Math.ceil(Math.abs(rollMessage.rolls[0].total - dialogContext.nudgedValue) / 5)
|
||||||
$("#nudged-wp-cost").val(dialogContext.wpCost)
|
$("#nudged-wp-cost").val(dialogContext.wpCost)
|
||||||
})
|
})
|
||||||
@@ -591,6 +648,11 @@ export default class CthulhuEternalUtils {
|
|||||||
return
|
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))
|
const roll = new CthulhuEternalRoll(String(dialogContext.nudgedValue))
|
||||||
await roll.evaluate()
|
await roll.evaluate()
|
||||||
roll.options = dialogContext
|
roll.options = dialogContext
|
||||||
@@ -601,6 +663,13 @@ export default class CthulhuEternalUtils {
|
|||||||
|
|
||||||
actor.system.modifyWP(-dialogContext.wpCost)
|
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
|
// Delete the initial roll message
|
||||||
await rollMessage.delete()
|
await rollMessage.delete()
|
||||||
|
|
||||||
@@ -642,15 +711,29 @@ export default class CthulhuEternalUtils {
|
|||||||
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noActorFound"))
|
ui.notifications.error(game.i18n.localize("CTHULHUETERNAL.Notifications.noActorFound"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Remove the chat message
|
|
||||||
this.removeChatMessageId(message.id)
|
this.removeChatMessageId(message.id)
|
||||||
|
|
||||||
// Get the targetted actorId from the button's data attribute
|
// Resolve the target actor: prefer canvas token (works for both combat and scene tokens)
|
||||||
let targetCombatantId = event.currentTarget.dataset.combatantId
|
const targetTokenId = event.currentTarget.dataset.tokenId
|
||||||
let combatant = game.combat.combatants.get(targetCombatantId)
|
const targetActorId = event.currentTarget.dataset.actorId
|
||||||
let targetActor = combatant.token?.actor || game.actors.get(combatant.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) {
|
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
|
return
|
||||||
}
|
}
|
||||||
targetActor.applyWounds(woundData)
|
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/05/13-20:38:21.073323 7ff670fed6c0 Recovering log #304
|
||||||
2026/04/28-23:37:29.068330 7f57977fe6c0 Level-0 table #296: started
|
2026/05/13-20:38:21.083446 7ff670fed6c0 Delete type=3 #302
|
||||||
2026/04/28-23:37:29.068358 7f57977fe6c0 Level-0 table #296: 0 bytes OK
|
2026/05/13-20:38:21.083514 7ff670fed6c0 Delete type=0 #304
|
||||||
2026/04/28-23:37:29.074817 7f57977fe6c0 Delete type=0 #294
|
2026/05/13-21:29:49.379063 7ff6637fe6c0 Level-0 table #309: started
|
||||||
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/05/13-21:29:49.379189 7ff6637fe6c0 Level-0 table #309: 0 bytes OK
|
||||||
2026/04/28-23:37:29.106913 7f57977fe6c0 Compacting 1@0 + 0@1 files
|
2026/05/13-21:29:49.386620 7ff6637fe6c0 Delete type=0 #307
|
||||||
2026/04/28-23:37:29.111066 7f57977fe6c0 Generated table #297@0: 26 keys, 60964 bytes
|
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)
|
||||||
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)
|
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
2026/04/28-23:36:37.170404 7f5797fff6c0 Log #291: 0 ops saved to Table #292 OK
|
2026/05/13-13:05:01.465748 7ff6717ee6c0 Recovering log #300
|
||||||
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/05/13-13:05:01.518261 7ff6717ee6c0 Delete type=3 #298
|
||||||
2026/04/28-23:36:37.171708 7f5797fff6c0 Table #248: 26 entries OK
|
2026/05/13-13:05:01.518318 7ff6717ee6c0 Delete type=0 #300
|
||||||
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: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/05/13-20:38:21.045516 7ff6717ee6c0 Recovering log #473
|
||||||
2026/04/28-23:37:29.054894 7f57977fe6c0 Level-0 table #465: started
|
2026/05/13-20:38:21.056349 7ff6717ee6c0 Delete type=3 #471
|
||||||
2026/04/28-23:37:29.054961 7f57977fe6c0 Level-0 table #465: 0 bytes OK
|
2026/05/13-20:38:21.056423 7ff6717ee6c0 Delete type=0 #473
|
||||||
2026/04/28-23:37:29.061529 7f57977fe6c0 Delete type=0 #463
|
2026/05/13-21:29:49.443482 7ff6637fe6c0 Level-0 table #478: started
|
||||||
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/05/13-21:29:49.443606 7ff6637fe6c0 Level-0 table #478: 0 bytes OK
|
||||||
2026/04/28-23:37:29.081748 7f57977fe6c0 Compacting 1@0 + 0@1 files
|
2026/05/13-21:29:49.450275 7ff6637fe6c0 Delete type=0 #476
|
||||||
2026/04/28-23:37:29.088487 7f57977fe6c0 Generated table #466@0: 556 keys, 320457 bytes
|
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)
|
||||||
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)
|
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
2026/04/28-23:36:36.935963 7f57a57ee6c0 Log #460: 0 ops saved to Table #461 OK
|
2026/05/13-13:05:01.363329 7ff663fff6c0 Recovering log #469
|
||||||
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/05/13-13:05:01.408343 7ff663fff6c0 Delete type=3 #467
|
||||||
2026/04/28-23:36:36.942723 7f57a57ee6c0 Table #417: 556 entries OK
|
2026/05/13-13:05:01.408405 7ff663fff6c0 Delete type=0 #469
|
||||||
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: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/05/13-20:38:21.061357 7ff663fff6c0 Recovering log #119
|
||||||
2026/04/28-23:37:29.061681 7f57977fe6c0 Level-0 table #111: started
|
2026/05/13-20:38:21.070703 7ff663fff6c0 Delete type=3 #117
|
||||||
2026/04/28-23:37:29.061722 7f57977fe6c0 Level-0 table #111: 0 bytes OK
|
2026/05/13-20:38:21.070750 7ff663fff6c0 Delete type=0 #119
|
||||||
2026/04/28-23:37:29.068229 7f57977fe6c0 Delete type=0 #109
|
2026/05/13-21:29:49.386758 7ff6637fe6c0 Level-0 table #124: started
|
||||||
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/05/13-21:29:49.386971 7ff6637fe6c0 Level-0 table #124: 0 bytes OK
|
||||||
2026/04/28-23:37:29.095044 7f57977fe6c0 Compacting 1@0 + 0@1 files
|
2026/05/13-21:29:49.393349 7ff6637fe6c0 Delete type=0 #122
|
||||||
2026/04/28-23:37:29.100652 7f57977fe6c0 Generated table #112@0: 362 keys, 93592 bytes
|
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)
|
||||||
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)
|
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
2026/04/28-23:36:37.136506 7f57a4fed6c0 Log #106: 0 ops saved to Table #107 OK
|
2026/05/13-13:05:01.411955 7ff671fef6c0 Recovering log #115
|
||||||
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/05/13-13:05:01.463033 7ff671fef6c0 Delete type=3 #113
|
||||||
2026/04/28-23:36:37.141991 7f57a4fed6c0 Table #63: 362 entries OK
|
2026/05/13-13:05:01.463084 7ff671fef6c0 Delete type=0 #115
|
||||||
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: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}}
|
{{/if}}
|
||||||
|
|
||||||
<li class="li-apply-wounds">
|
<li class="li-apply-wounds">
|
||||||
<button type="button" class="apply-wounds">{{localize "CTHULHUETERNAL.Label.applyWounds"}}</button>
|
<div>{{localize "CTHULHUETERNAL.Label.applyWounds"}}</div>
|
||||||
<select name="combatant" class="roll-skill-modifier">
|
<div class="combatants-grid">
|
||||||
{{selectOptions combatants valueAttr="id" labelAttr="name"}}
|
{{#each combatants}}
|
||||||
</select>
|
<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>
|
</li>
|
||||||
|
|
||||||
{{#if isLethal}}
|
{{#if isLethal}}
|
||||||
|
|||||||
@@ -150,6 +150,13 @@
|
|||||||
</div>
|
</div>
|
||||||
{{/unless}}
|
{{/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 }}
|
{{! Zone d'actions regroupées }}
|
||||||
<div class="chat-actions">
|
<div class="chat-actions">
|
||||||
{{#if isSuccess}}
|
{{#if isSuccess}}
|
||||||
@@ -233,6 +240,15 @@
|
|||||||
>
|
>
|
||||||
<i class="fa-solid fa-circle-sort-down"></i>
|
<i class="fa-solid fa-circle-sort-down"></i>
|
||||||
</a>
|
</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}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
|||||||
@@ -39,8 +39,10 @@
|
|||||||
<div class="combatants-grid">
|
<div class="combatants-grid">
|
||||||
{{#each combatants}}
|
{{#each combatants}}
|
||||||
<button
|
<button
|
||||||
class="apply-wounds-btn"
|
class="apply-wounds-btn chat-action-button"
|
||||||
data-combatant-id="{{this.id}}"
|
data-combatant-id="{{this.id}}"
|
||||||
|
data-actor-id="{{this.actorId}}"
|
||||||
|
data-token-id="{{this.tokenId}}"
|
||||||
title="{{this.name}}"
|
title="{{this.name}}"
|
||||||
>
|
>
|
||||||
{{this.name}}
|
{{this.name}}
|
||||||
|
|||||||
@@ -8,19 +8,19 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<li class="san-loose-buttons">
|
<li class="san-loose-buttons">
|
||||||
<button class="san-loose" data-san-value="0">0</button>
|
<button class="san-loose chat-action-button" data-san-value="0">0</button>
|
||||||
<button class="san-loose" data-san-value="1">1</button>
|
<button class="san-loose chat-action-button" data-san-value="1">1</button>
|
||||||
<button class="san-loose" data-san-value="2">2</button>
|
<button class="san-loose chat-action-button" data-san-value="2">2</button>
|
||||||
<button class="san-loose" data-san-value="3">3</button>
|
<button class="san-loose chat-action-button" 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="4">4</button>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="san-loose-buttons">
|
<li class="san-loose-buttons">
|
||||||
<button class="san-loose" data-san-value="1d4">1d4</button>
|
<button class="san-loose chat-action-button" data-san-value="1d4">1d4</button>
|
||||||
<button class="san-loose" data-san-value="1d6">1d6</button>
|
<button class="san-loose chat-action-button" data-san-value="1d6">1d6</button>
|
||||||
<button class="san-loose" data-san-value="1d8">1d8</button>
|
<button class="san-loose chat-action-button" data-san-value="1d8">1d8</button>
|
||||||
<button class="san-loose" data-san-value="1d10">1d10</button>
|
<button class="san-loose chat-action-button" 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="1d12">1d12</button>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -5,12 +5,12 @@
|
|||||||
<li><strong>{{localize "CTHULHUETERNAL.Label.selectSANType"}}</strong></li>
|
<li><strong>{{localize "CTHULHUETERNAL.Label.selectSANType"}}</strong></li>
|
||||||
|
|
||||||
<li class="san-type-buttons">
|
<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 chat-action-button" 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="helplessness">{{localize "CTHULHUETERNAL.Label.Helplessness"}}</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="san-type-buttons">
|
<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 chat-action-button" 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="none">{{localize "CTHULHUETERNAL.Label.None"}}</button>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
</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