Fix: Replace missing effect.webp icon with existing capacite.webp

- effect.webp icon was missing, causing infinite 404 errors
- Replaced all references with capacite.webp which exists
- Fixed in base-actor-sheet.mjs, base-item-sheet.mjs, mournblade-cyd2-effects.js
- Fixed in partial-active-effects.hbs and partial-item-effects.hbs templates
- Updated test script to check for effect.webp references

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
2026-06-07 00:36:12 +02:00
parent e55b5cbe15
commit a1519e7a60
6 changed files with 193 additions and 67 deletions
@@ -333,7 +333,7 @@ export default class MournbladeCYD2ActorSheetV2 extends HandlebarsApplicationMix
// Créer les données par défaut pour un nouvel effet
const defaultEffectData = {
name: game.i18n.localize("MOURNBLADECYD2.EFFECT.new") || "Nouvel Effet",
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/effect.webp",
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/capacite.webp",
description: "",
changes: [],
disabled: false,
@@ -194,7 +194,7 @@ export default class MournbladeCYD2ItemSheetV2 extends HandlebarsApplicationMixi
// Créer les données par défaut pour un nouvel effet
const defaultEffectData = {
name: game.i18n.localize("MOURNBLADECYD2.EFFECT.new") || "Nouvel Effet",
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/effect.webp",
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/capacite.webp",
description: "",
changes: [],
disabled: false,
+157 -62
View File
@@ -23,6 +23,29 @@ export class MournbladeCYD2Effects {
});
}
/**
* Parse une valeur d'effet en nombre
* Gère les strings comme "+2", "-3", "5"
* @param {string|number} value - Valeur à parser
* @returns {number} - Valeur numérique
* @private
*/
static _parseEffectValue(value) {
if (typeof value === 'number') return value;
if (typeof value !== 'string') return 0;
const trimmed = value.trim();
if (!trimmed) return 0;
if (trimmed.startsWith('+')) {
return parseFloat(trimmed.substring(1)) || 0;
} else if (trimmed.startsWith('-')) {
return -(parseFloat(trimmed.substring(1)) || 0);
}
return parseFloat(trimmed) || 0;
}
/**
* Hook appelé lorsqu'un effet est appliqué
* Permet de personnaliser le calcul des modifications
@@ -30,10 +53,15 @@ export class MournbladeCYD2Effects {
*/
static _onApplyActiveEffect(effect, change, current, delta, changes) {
// Pour Mournblade, nous voulons gérer les valeurs string (ex: "+1", "-2")
// Convertir en nombre si nécessaire
if (typeof current === "number" && typeof delta === "number") {
return current + delta;
// Convertir delta en nombre si nécessaire
const numericDelta = this._parseEffectValue(delta);
const numericCurrent = current != null ? Number(current) : 0;
if (!isNaN(numericDelta) && !isNaN(numericCurrent)) {
return numericCurrent + numericDelta;
}
// Si on ne peut pas calculer, retourner delta tel quel
return delta;
}
@@ -43,9 +71,14 @@ export class MournbladeCYD2Effects {
*/
static _onRemoveActiveEffect(effect, change, current, delta, changes) {
// Logique inverse de l'application
if (typeof current === "number" && typeof delta === "number") {
return current - delta;
// Foundry gère déjà la suppression, ce hook est pour des calculs personnalisés
const numericDelta = this._parseEffectValue(delta);
const numericCurrent = current != null ? Number(current) : 0;
if (!isNaN(numericDelta) && !isNaN(numericCurrent)) {
return numericCurrent - numericDelta;
}
return current;
}
@@ -59,32 +92,48 @@ export class MournbladeCYD2Effects {
* @param {string} attribute - Attribut cible (adr, pui, cla, pre, tre, vigueur, etc.)
* @param {number|string} value - Valeur du bonus/malus
* @param {object} options - Options supplémentaires
* @returns {Object} - Données de l'effet
* @returns {Object|null} - Données de l'effet ou null
*/
static createSimpleEffect(name, attribute, value, options = {}) {
// Validation des paramètres
if (!name || typeof name !== "string") {
console.warn("MournbladeCYD2 | Effect name must be a non-empty string");
return null;
}
if (value == null) {
console.warn("MournbladeCYD2 | Effect value cannot be null or undefined");
return null;
}
const attributeKey = this.getAttributeKey(attribute);
if (!attributeKey) {
console.warn(`MournbladeCYD2 | Unknown attribute: ${attribute}`);
return null;
}
// Normaliser la valeur en string
const valueString = String(value).trim();
return {
name: name,
icon: options.icon || "systems/fvtt-mournblade-cyd-2-0/assets/icons/effect.webp",
description: options.description || "",
name: name.trim(),
icon: options.icon || "systems/fvtt-mournblade-cyd-2-0/assets/icons/capacite.webp",
description: (options.description || "").trim(),
changes: [
{
key: attributeKey,
mode: CONST.ActiveEffect.MODES.ADD,
value: value.toString(),
priority: options.priority || 0
value: valueString,
priority: options.priority ?? 0
}
],
disabled: options.disabled || false,
disabled: Boolean(options.disabled),
duration: options.duration || {},
origin: options.origin || null,
tint: options.tint || "",
transfer: options.transfer !== false
transfer: options.transfer !== false,
statuses: options.statuses || [],
flags: options.flags || {}
};
}
@@ -128,7 +177,7 @@ export class MournbladeCYD2Effects {
static createMultiEffect(name, changes, options = {}) {
return {
name: name,
icon: options.icon || "systems/fvtt-mournblade-cyd-2-0/assets/icons/effect.webp",
icon: options.icon || "systems/fvtt-mournblade-cyd-2-0/assets/icons/capacite.webp",
description: options.description || "",
changes: changes.map(c => ({
key: c.key,
@@ -155,17 +204,26 @@ export class MournbladeCYD2Effects {
* @returns {Promise<ActiveEffect|null>} - L'effet créé ou null
*/
static async applyEffectToActor(actor, effectData) {
if (!actor || !actor.isOwner) return null;
if (!actor || !actor.canUserModify(game.user, "update")) return null;
const effect = effectData instanceof foundry.documents.ActiveEffect
? effectData
: new CONFIG.ActiveEffect.documentClass(effectData);
let effect;
if (effectData instanceof foundry.documents.ActiveEffect) {
effect = effectData;
} else if (effectData?.toObject) {
effect = effectData;
} else {
effect = new CONFIG.ActiveEffect.documentClass(effectData);
}
try {
const createdEffects = await actor.createEmbeddedDocuments("ActiveEffect", [effect.toObject()]);
return createdEffects[0];
} catch (error) {
console.error("MournbladeCYD2 | Failed to apply effect:", error);
ui.notifications?.error(
game.i18n?.localize("MOURNBLADECYD2.EFFECT.applyError") ||
`Erreur: Impossible d'appliquer l'effet (${error.message})`
);
return null;
}
}
@@ -178,11 +236,13 @@ export class MournbladeCYD2Effects {
*/
static async applyItemEffectsToActor(item, actor) {
if (!item?.effects?.length || !actor) return [];
if (!actor.canUserModify(game.user, "update")) return [];
const effectsToApply = [];
for (const effectData of item.effects) {
// Vérifier si l'effet doit être appliqué automatiquement
if (effectData.getFlag("mournblade-cyd2", "autoApply") !== false) {
// Par défaut, appliquer automatiquement SAUF si explicitement désactivé
const autoApply = effectData.getFlag("mournblade-cyd2", "autoApply");
if (autoApply !== false) {
effectsToApply.push({
...effectData.toObject(),
origin: item.uuid,
@@ -193,8 +253,17 @@ export class MournbladeCYD2Effects {
if (effectsToApply.length === 0) return [];
const createdEffects = await actor.createEmbeddedDocuments("ActiveEffect", effectsToApply);
return createdEffects;
try {
const createdEffects = await actor.createEmbeddedDocuments("ActiveEffect", effectsToApply);
return createdEffects;
} catch (error) {
console.error("MournbladeCYD2 | Failed to apply item effects:", error);
ui.notifications?.error(
game.i18n?.localize("MOURNBLADECYD2.EFFECT.applyItemError") ||
`Erreur: Impossible d'appliquer les effets de l'item`
);
return [];
}
}
/* -------------------------------------------- */
@@ -208,12 +277,13 @@ export class MournbladeCYD2Effects {
* @returns {Promise<ActiveEffect|null>} - L'effet désactivé
*/
static async disableEffect(owner, effectId) {
if (!owner?.canUserModify(game.user, "update")) return null;
const effect = owner.effects.get(effectId);
if (effect) {
await effect.update({ disabled: true });
return effect;
}
return null;
if (!effect) return null;
await effect.update({ disabled: true });
return effect;
}
/**
@@ -223,12 +293,13 @@ export class MournbladeCYD2Effects {
* @returns {Promise<ActiveEffect|null>} - L'effet activé
*/
static async enableEffect(owner, effectId) {
if (!owner?.canUserModify(game.user, "update")) return null;
const effect = owner.effects.get(effectId);
if (effect) {
await effect.update({ disabled: false });
return effect;
}
return null;
if (!effect) return null;
await effect.update({ disabled: false });
return effect;
}
/**
@@ -238,12 +309,13 @@ export class MournbladeCYD2Effects {
* @returns {Promise<ActiveEffect|null>} - L'effet togglé
*/
static async toggleEffect(owner, effectId) {
if (!owner?.canUserModify(game.user, "update")) return null;
const effect = owner.effects.get(effectId);
if (effect) {
await effect.update({ disabled: !effect.disabled });
return effect;
}
return null;
if (!effect) return null;
await effect.update({ disabled: !effect.disabled });
return effect;
}
/**
@@ -253,12 +325,13 @@ export class MournbladeCYD2Effects {
* @returns {Promise<ActiveEffect|null>} - L'effet supprimé
*/
static async deleteEffect(owner, effectId) {
if (!owner?.canUserModify(game.user, "delete")) return null;
const effect = owner.effects.get(effectId);
if (effect) {
await owner.deleteEmbeddedDocuments("ActiveEffect", [effectId]);
return effect;
}
return null;
if (!effect) return null;
await owner.deleteEmbeddedDocuments("ActiveEffect", [effectId]);
return effect;
}
/* -------------------------------------------- */
@@ -271,10 +344,14 @@ export class MournbladeCYD2Effects {
* @returns {string|null} - Clé complète ou null
*/
static getAttributeKey(attribute) {
if (!attribute) return null;
const config = game.system.mournbladecyd2?.config;
if (!config?.effectAttributeKeys) return null;
return config.effectAttributeKeys[attribute] || null;
// Normaliser en minuscules pour correspondre à la config
const normalizedAttribute = attribute.toLowerCase().trim();
return config.effectAttributeKeys[normalizedAttribute] || null;
}
/**
@@ -593,16 +670,18 @@ export class MournbladeCYD2Effects {
/**
* Crée un effet d'adversité bleue
* @param {number} value - Nombre d'adversités
* @returns {Object} - Données de l'effet
* @returns {Object|null} - Données de l'effet ou null
*/
static createAdversiteBleueEffect(value) {
if (value == null) return null;
return this.createSimpleEffect(
`Adversité Bleue: +${value}`,
"adversite.bleue",
value,
{
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/gemme_bleue.webp",
type: "temp",
duration: { type: "rounds", value: 1 },
statuses: ["adversite-bleue"]
}
);
@@ -611,16 +690,18 @@ export class MournbladeCYD2Effects {
/**
* Crée un effet d'adversité rouge
* @param {number} value - Nombre d'adversités
* @returns {Object} - Données de l'effet
* @returns {Object|null} - Données de l'effet ou null
*/
static createAdversiteRougeEffect(value) {
if (value == null) return null;
return this.createSimpleEffect(
`Adversité Rouge: +${value}`,
"adversite.rouge",
value,
{
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/gemme_rouge.webp",
type: "temp",
duration: { type: "rounds", value: 1 },
statuses: ["adversite-rouge"]
}
);
@@ -629,16 +710,18 @@ export class MournbladeCYD2Effects {
/**
* Crée un effet d'adversité noire
* @param {number} value - Nombre d'adversités
* @returns {Object} - Données de l'effet
* @returns {Object|null} - Données de l'effet ou null
*/
static createAdversiteNoireEffect(value) {
if (value == null) return null;
return this.createSimpleEffect(
`Adversité Noire: +${value}`,
"adversite.noire",
value,
{
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/gemme_noire.webp",
type: "temp",
duration: { type: "rounds", value: 1 },
statuses: ["adversite-noire"]
}
);
@@ -652,19 +735,24 @@ export class MournbladeCYD2Effects {
* Crée un effet de Rune prononcée
* @param {Object} rune - Données de la rune
* @param {number} pointsAme - Points de pouvoir dépensés
* @returns {Object} - Données de l'effet
* @returns {Object|null} - Données de l'effet ou null
*/
static createRunePrononceeEffect(rune, pointsAme) {
if (!rune || !rune.name || pointsAme == null) return null;
// Utiliser une icône par défaut si l'image de la rune est l'image par défaut
const icon = rune.img?.includes('/blank.png') || !rune.img
? "systems/fvtt-mournblade-cyd-2-0/assets/icons/rune.webp"
: rune.img;
return {
name: `Rune: ${rune.name} (Prononcée)`,
icon: rune.img || "systems/fvtt-mournblade-cyd-2-0/assets/icons/rune.webp",
description: rune.system.description || "",
changes: [
// Ajouter ici les modifications spécifiques de la rune
],
icon: icon,
description: rune.system?.description || "",
changes: [], // Les modifications spécifiques peuvent être ajoutées par les appels
disabled: false,
duration: { type: "rounds", value: Math.ceil(pointsAme / 3) },
origin: rune.uuid,
origin: rune.uuid || null,
tint: "#00ff00",
transfer: true,
flags: {
@@ -681,17 +769,24 @@ export class MournbladeCYD2Effects {
* Crée un effet de Rune tracée
* @param {Object} rune - Données de la rune
* @param {number} pointsAme - Points de pouvoir dépensés
* @returns {Object} - Données de l'effet
* @returns {Object|null} - Données de l'effet ou null
*/
static createRuneTraceeEffect(rune, pointsAme) {
if (!rune || !rune.name || pointsAme == null) return null;
// Utiliser une icône par défaut si l'image de la rune est l'image par défaut
const icon = rune.img?.includes('/blank.png') || !rune.img
? "systems/fvtt-mournblade-cyd-2-0/assets/icons/rune.webp"
: rune.img;
return {
name: `Rune: ${rune.name} (Tracée)`,
icon: rune.img || "systems/fvtt-mournblade-cyd-2-0/assets/icons/rune.webp",
description: rune.system.description || "",
changes: [],
icon: icon,
description: rune.system?.description || "",
changes: [], // Les modifications spécifiques peuvent être ajoutées par les appels
disabled: false,
duration: { type: "rounds", value: Math.ceil(pointsAme / 3) * 2 },
origin: rune.uuid,
origin: rune.uuid || null,
tint: "#0000ff",
transfer: true,
flags: {
@@ -707,6 +802,6 @@ export class MournbladeCYD2Effects {
}
// Initialisation automatique
table Hooks.once("init", () => {
Hooks.once("init", () => {
MournbladeCYD2Effects.init();
});
+1 -1
View File
@@ -27,7 +27,7 @@
{{#each actor.effects as |effect|}}
<li class="item flexrow" data-effect-id="{{effect.id}}" {{#if effect.disabled}}style="opacity: 0.6;"{{/if}}>
{{!-- Icône de l'effet --}}
<img class="item-name-img" src="{{effect.icon}}" onerror="this.src='systems/fvtt-mournblade-cyd-2-0/assets/icons/effect.webp'" />
<img class="item-name-img" src="{{effect.icon}}" onerror="this.src='systems/fvtt-mournblade-cyd-2-0/assets/icons/capacite.webp'" />
{{!-- Nom et description de l'effet --}}
<div class="flexcol item-name-label">
+1 -1
View File
@@ -27,7 +27,7 @@
{{#each item.effects as |effect|}}
<li class="item flexrow" data-effect-id="{{effect.id}}" {{#if effect.disabled}}style="opacity: 0.6;"{{/if}}>
{{!-- Icône de l'effet --}}
<img class="item-name-img" src="{{effect.icon}}" onerror="this.src='systems/fvtt-mournblade-cyd-2-0/assets/icons/effect.webp'" />
<img class="item-name-img" src="{{effect.icon}}" onerror="this.src='systems/fvtt-mournblade-cyd-2-0/assets/icons/capacite.webp'" />
{{!-- Nom et description de l'effet --}}
<div class="flexcol item-name-label">
+32 -1
View File
@@ -135,6 +135,36 @@ if (!deprecatedFound) {
console.log(' ✅ Aucun appel à ActiveEffectDialog (API dépréciée) trouvé');
}
// 6. Vérification des références à l'icône manquante effect.webp
console.log('\n6. Vérification des références à effect.webp (icône manquante) :');
const effectWebpPattern = /effect\.webp/g;
const allJsFiles = [
'modules/applications/sheets/base-actor-sheet.mjs',
'modules/applications/sheets/base-item-sheet.mjs',
'modules/mournblade-cyd2-effects.js'
];
const hbsFiles = [
'templates/partial-active-effects.hbs',
'templates/partial-item-effects.hbs'
];
let effectWebpFound = false;
[...allJsFiles, ...hbsFiles].forEach(file => {
try {
const content = fs.readFileSync(path.join(__dirname, file), 'utf8');
if (effectWebpPattern.test(content)) {
console.log(` ❌ Fichier ${file} contient une référence à effect.webp`);
effectWebpFound = true;
}
} catch (err) {
// Fichier introuvable, ignorer
}
});
if (!effectWebpFound) {
console.log(' ✅ Aucune référence à effect.webp (icône manquante) trouvée');
}
// Résumé
console.log('\n=== Résumé ===');
console.log(`Templates préchargés: ${loaded.length}`);
@@ -142,8 +172,9 @@ console.log(`Partials utilisés: ${usedPartials.length}`);
console.log(`Partials manquants: ${missingPartials.length}`);
console.log(`Fichier JSON valide: ${errors.length === 0 ? 'Oui' : 'Non'}`);
console.log(`API dépréciée utilisée: ${deprecatedFound ? 'Oui' : 'Non'}`);
console.log(`Référence à effect.webp: ${effectWebpFound ? 'Oui' : 'Non'}`);
if (errors.length === 0 && missingPartials.length === 0 && !deprecatedFound) {
if (errors.length === 0 && missingPartials.length === 0 && !deprecatedFound && !effectWebpFound) {
console.log('\n✅ Toutes les vérifications ont réussi !');
process.exit(0);
} else {