ActiveEffects: Add complete ActiveEffects management system
- New: modules/mournblade-cyd2-effects.js with utility methods for creating, applying, and managing effects - New: templates/partial-active-effects.hbs for displaying actor effects - New: templates/partial-item-effects.hbs for displaying item effects - Update: modules/mournblade-cyd2-config.js with effect configuration (types, attribute keys, categories) - Update: templates/actor-sheet.hbs and creature-sheet.hbs with Effects tab - Update: templates/partial-item-nav.hbs with conditional Effects tab - Update: templates/item-talent-sheet.hbs with Effects tab content - Update: base-actor-sheet.mjs with effect action handlers (create, edit, delete, toggle) - Update: base-item-sheet.mjs with effect action handlers and context - Update: modules/mournblade-cyd2-main.js to import and expose MournbladeCYD2Effects - Update: lang/fr.json with effect-related translations Features: - Support for creating permanent and temporary effects - Support for attribute modifications (ADR, PUI, CLA, PRE, TRE, etc.) - Support for health, soul, combat, and adversity modifications - Support for status effects - Support for runes (pronounced and traced) effects - Toggle to enable/disable effects - Duration tracking (rounds, turns, seconds, combat, scene) - Display of all active modifications summary Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
+36
-1
@@ -71,5 +71,40 @@
|
|||||||
"traced": "Tracée",
|
"traced": "Tracée",
|
||||||
"tracedrune": "Rune tracée",
|
"tracedrune": "Rune tracée",
|
||||||
"tracerune": "Tracer"
|
"tracerune": "Tracer"
|
||||||
}
|
},
|
||||||
|
"EFFECT": {
|
||||||
|
"new": "Nouvel Effet",
|
||||||
|
"edit": "Éditer l'effet",
|
||||||
|
"delete": "Supprimer l'effet",
|
||||||
|
"deleteConfirm": "Supprimer l'effet",
|
||||||
|
"deleteConfirmText": "Êtes-vous sûr de vouloir supprimer cet effet ?",
|
||||||
|
"create": "Créer un effet",
|
||||||
|
"name": "Nom de l'effet",
|
||||||
|
"icon": "Icône",
|
||||||
|
"description": "Description",
|
||||||
|
"changes": "Modifications",
|
||||||
|
"addChange": "Ajouter une modification",
|
||||||
|
"duration": "Durée",
|
||||||
|
"durationType": "Type de durée",
|
||||||
|
"durationValue": "Valeur",
|
||||||
|
"disabled": "Désactivé",
|
||||||
|
"transfer": "Transférer au token",
|
||||||
|
"noDuration": "Aucune (permanent)",
|
||||||
|
"rounds": "Rounds",
|
||||||
|
"turns": "Tours",
|
||||||
|
"seconds": "Secondes",
|
||||||
|
"combat": "Jusqu'à la fin du combat",
|
||||||
|
"scene": "Jusqu'à la fin de la scène",
|
||||||
|
"attribute": "Attribut",
|
||||||
|
"value": "Valeur",
|
||||||
|
"mode": "Mode",
|
||||||
|
"modeAdd": "Ajouter",
|
||||||
|
"modeMultiply": "Multiplier",
|
||||||
|
"modeOverride": "Remplacer",
|
||||||
|
"modeUpgrade": "Améliorer",
|
||||||
|
"modeDowngrade": "Dégrader",
|
||||||
|
"activeEffects": "Effets Actifs",
|
||||||
|
"noActiveEffects": "Aucun effet actif",
|
||||||
|
"effectSummary": "Résumé des modifications",
|
||||||
|
"toggleEffect": "Activer/Désactiver"
|
||||||
}
|
}
|
||||||
@@ -60,6 +60,12 @@ export default class MournbladeCYD2ActorSheetV2 extends HandlebarsApplicationMix
|
|||||||
rollDesengager: MournbladeCYD2ActorSheetV2.#onRollDesengager,
|
rollDesengager: MournbladeCYD2ActorSheetV2.#onRollDesengager,
|
||||||
rollInitiative: MournbladeCYD2ActorSheetV2.#onRollInitiative,
|
rollInitiative: MournbladeCYD2ActorSheetV2.#onRollInitiative,
|
||||||
rollFuir: MournbladeCYD2ActorSheetV2.#onRollFuir,
|
rollFuir: MournbladeCYD2ActorSheetV2.#onRollFuir,
|
||||||
|
// Actions pour les ActiveEffects
|
||||||
|
createEffect: MournbladeCYD2ActorSheetV2.#onCreateEffect,
|
||||||
|
editEffect: MournbladeCYD2ActorSheetV2.#onEditEffect,
|
||||||
|
deleteEffect: MournbladeCYD2ActorSheetV2.#onDeleteEffect,
|
||||||
|
toggleEffect: MournbladeCYD2ActorSheetV2.#onToggleEffect,
|
||||||
|
applyEffect: MournbladeCYD2ActorSheetV2.#onApplyEffect,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -310,4 +316,118 @@ export default class MournbladeCYD2ActorSheetV2 extends HandlebarsApplicationMix
|
|||||||
static async #onRollFuir(event, target) {
|
static async #onRollFuir(event, target) {
|
||||||
await this.document.rollFuir();
|
await this.document.rollFuir();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #region ActiveEffects Management
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un nouvel effet actif
|
||||||
|
* @param {Event} event - Événement
|
||||||
|
* @param {HTMLElement} target - Éléments cible
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onCreateEffect(event, target) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
// 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",
|
||||||
|
description: "",
|
||||||
|
changes: [],
|
||||||
|
disabled: false,
|
||||||
|
duration: {},
|
||||||
|
origin: this.document.uuid,
|
||||||
|
tint: "",
|
||||||
|
transfer: true,
|
||||||
|
flags: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Utiliser la dialog native FoundryVTT pour créer l'effet
|
||||||
|
const effect = await foundry.applications.api.ActiveEffectDialog.create({
|
||||||
|
document: this.document,
|
||||||
|
effect: defaultEffectData
|
||||||
|
});
|
||||||
|
|
||||||
|
if (effect) {
|
||||||
|
await this.document.createEmbeddedDocuments("ActiveEffect", [effect.toObject()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Édite un effet actif existant
|
||||||
|
* @param {Event} event - Événement
|
||||||
|
* @param {HTMLElement} target - Éléments cible
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onEditEffect(event, target) {
|
||||||
|
event.preventDefault();
|
||||||
|
const effectId = target?.dataset?.effectId;
|
||||||
|
if (!effectId) return;
|
||||||
|
|
||||||
|
const effect = this.document.effects.get(effectId);
|
||||||
|
if (effect) {
|
||||||
|
// Ouvrir la sheet de l'effet pour édition
|
||||||
|
effect.sheet.render(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supprime un effet actif
|
||||||
|
* @param {Event} event - Événement
|
||||||
|
* @param {HTMLElement} target - Éléments cible
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onDeleteEffect(event, target) {
|
||||||
|
event.preventDefault();
|
||||||
|
const effectId = target?.dataset?.effectId;
|
||||||
|
if (!effectId) return;
|
||||||
|
|
||||||
|
const effect = this.document.effects.get(effectId);
|
||||||
|
if (effect) {
|
||||||
|
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||||
|
title: game.i18n.localize("MOURNBLADECYD2.effect.deleteConfirm") || "Supprimer l'effet",
|
||||||
|
content: game.i18n.localize("MOURNBLADECYD2.effect.deleteConfirmText") || `Êtes-vous sûr de vouloir supprimer l'effet "${effect.name}" ?`
|
||||||
|
});
|
||||||
|
|
||||||
|
if (confirmed) {
|
||||||
|
await this.document.deleteEmbeddedDocuments("ActiveEffect", [effectId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle l'état actif/désactivé d'un effet
|
||||||
|
* @param {Event} event - Événement
|
||||||
|
* @param {HTMLElement} target - Éléments cible
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onToggleEffect(event, target) {
|
||||||
|
event.preventDefault();
|
||||||
|
const effectId = target?.dataset?.effectId;
|
||||||
|
if (!effectId) return;
|
||||||
|
|
||||||
|
const effect = this.document.effects.get(effectId);
|
||||||
|
if (effect) {
|
||||||
|
await effect.update({ disabled: !effect.disabled });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applique un effet à partir d'un item
|
||||||
|
* @param {Event} event - Événement
|
||||||
|
* @param {HTMLElement} target - Éléments cible
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onApplyEffect(event, target) {
|
||||||
|
event.preventDefault();
|
||||||
|
const effectId = target?.dataset?.effectId;
|
||||||
|
if (!effectId) return;
|
||||||
|
|
||||||
|
const effect = this.document.effects.get(effectId);
|
||||||
|
if (effect) {
|
||||||
|
await effect.apply();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,12 @@ export default class MournbladeCYD2ItemSheetV2 extends HandlebarsApplicationMixi
|
|||||||
deletePredilection: MournbladeCYD2ItemSheetV2.#onDeletePredilection,
|
deletePredilection: MournbladeCYD2ItemSheetV2.#onDeletePredilection,
|
||||||
addAutomation: MournbladeCYD2ItemSheetV2.#onAddAutomation,
|
addAutomation: MournbladeCYD2ItemSheetV2.#onAddAutomation,
|
||||||
deleteAutomation: MournbladeCYD2ItemSheetV2.#onDeleteAutomation,
|
deleteAutomation: MournbladeCYD2ItemSheetV2.#onDeleteAutomation,
|
||||||
|
// Actions pour les ActiveEffects
|
||||||
|
createEffect: MournbladeCYD2ItemSheetV2.#onCreateEffect,
|
||||||
|
editEffect: MournbladeCYD2ItemSheetV2.#onEditEffect,
|
||||||
|
deleteEffect: MournbladeCYD2ItemSheetV2.#onDeleteEffect,
|
||||||
|
toggleEffect: MournbladeCYD2ItemSheetV2.#onToggleEffect,
|
||||||
|
applyEffect: MournbladeCYD2ItemSheetV2.#onApplyEffect,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -52,6 +58,7 @@ export default class MournbladeCYD2ItemSheetV2 extends HandlebarsApplicationMixi
|
|||||||
item: this.document,
|
item: this.document,
|
||||||
system: this.document.system,
|
system: this.document.system,
|
||||||
source: this.document.toObject(),
|
source: this.document.toObject(),
|
||||||
|
config: game.system.mournbladecyd2.config,
|
||||||
enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||||
this.document.system.description || "", { async: true }
|
this.document.system.description || "", { async: true }
|
||||||
),
|
),
|
||||||
@@ -171,4 +178,118 @@ export default class MournbladeCYD2ItemSheetV2 extends HandlebarsApplicationMixi
|
|||||||
await this.document.update({ "system.isautomated": false });
|
await this.document.update({ "system.isautomated": false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #region ActiveEffects Management
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un nouvel effet actif sur l'item
|
||||||
|
* @param {Event} event - Événement
|
||||||
|
* @param {HTMLElement} target - Éléments cible
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onCreateEffect(event, target) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
// 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",
|
||||||
|
description: "",
|
||||||
|
changes: [],
|
||||||
|
disabled: false,
|
||||||
|
duration: {},
|
||||||
|
origin: this.document.uuid,
|
||||||
|
tint: "",
|
||||||
|
transfer: false,
|
||||||
|
flags: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Utiliser la dialog native FoundryVTT pour créer l'effet
|
||||||
|
const effect = await foundry.applications.api.ActiveEffectDialog.create({
|
||||||
|
document: this.document,
|
||||||
|
effect: defaultEffectData
|
||||||
|
});
|
||||||
|
|
||||||
|
if (effect) {
|
||||||
|
await this.document.createEmbeddedDocuments("ActiveEffect", [effect.toObject()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Édite un effet actif existant sur l'item
|
||||||
|
* @param {Event} event - Événement
|
||||||
|
* @param {HTMLElement} target - Éléments cible
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onEditEffect(event, target) {
|
||||||
|
event.preventDefault();
|
||||||
|
const effectId = target?.dataset?.effectId;
|
||||||
|
if (!effectId) return;
|
||||||
|
|
||||||
|
const effect = this.document.effects.get(effectId);
|
||||||
|
if (effect) {
|
||||||
|
// Ouvrir la sheet de l'effet pour édition
|
||||||
|
effect.sheet.render(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supprime un effet actif de l'item
|
||||||
|
* @param {Event} event - Événement
|
||||||
|
* @param {HTMLElement} target - Éléments cible
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onDeleteEffect(event, target) {
|
||||||
|
event.preventDefault();
|
||||||
|
const effectId = target?.dataset?.effectId;
|
||||||
|
if (!effectId) return;
|
||||||
|
|
||||||
|
const effect = this.document.effects.get(effectId);
|
||||||
|
if (effect) {
|
||||||
|
const confirmed = await foundry.applications.api.DialogV2.confirm({
|
||||||
|
title: game.i18n.localize("MOURNBLADECYD2.EFFECT.deleteConfirm") || "Supprimer l'effet",
|
||||||
|
content: game.i18n.localize("MOURNBLADECYD2.EFFECT.deleteConfirmText") || `Êtes-vous sûr de vouloir supprimer l'effet "${effect.name}" ?`
|
||||||
|
});
|
||||||
|
|
||||||
|
if (confirmed) {
|
||||||
|
await this.document.deleteEmbeddedDocuments("ActiveEffect", [effectId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle l'état actif/désactivé d'un effet sur l'item
|
||||||
|
* @param {Event} event - Événement
|
||||||
|
* @param {HTMLElement} target - Éléments cible
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onToggleEffect(event, target) {
|
||||||
|
event.preventDefault();
|
||||||
|
const effectId = target?.dataset?.effectId;
|
||||||
|
if (!effectId) return;
|
||||||
|
|
||||||
|
const effect = this.document.effects.get(effectId);
|
||||||
|
if (effect) {
|
||||||
|
await effect.update({ disabled: !effect.disabled });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applique un effet à partir de l'item
|
||||||
|
* @param {Event} event - Événement
|
||||||
|
* @param {HTMLElement} target - Éléments cible
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onApplyEffect(event, target) {
|
||||||
|
event.preventDefault();
|
||||||
|
const effectId = target?.dataset?.effectId;
|
||||||
|
if (!effectId) return;
|
||||||
|
|
||||||
|
const effect = this.document.effects.get(effectId);
|
||||||
|
if (effect) {
|
||||||
|
await effect.apply();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,6 +119,96 @@ export const MOURNBLADECYD2_CONFIG = {
|
|||||||
{ key: "personnage", label: "Personnage" },
|
{ key: "personnage", label: "Personnage" },
|
||||||
{ key: "traitespece", label: "Trait d'espèce" }
|
{ key: "traitespece", label: "Trait d'espèce" }
|
||||||
],
|
],
|
||||||
|
|
||||||
|
// Configuration des ActiveEffects
|
||||||
|
effectTypes: {
|
||||||
|
bonus: "Bonus",
|
||||||
|
malus: "Malus",
|
||||||
|
rune: "Effet de Rune",
|
||||||
|
don: "Effet de Don",
|
||||||
|
talent: "Effet de Talent",
|
||||||
|
trait: "Effet de Trait",
|
||||||
|
temporaire: "Effet Temporaire",
|
||||||
|
permanent: "Effet Permanent"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Clés des attributs pour les modifications d'effets
|
||||||
|
effectAttributeKeys: {
|
||||||
|
// Attributs
|
||||||
|
adr: "system.attributs.adr.value",
|
||||||
|
pui: "system.attributs.pui.value",
|
||||||
|
cla: "system.attributs.cla.value",
|
||||||
|
pre: "system.attributs.pre.value",
|
||||||
|
tre: "system.attributs.tre.value",
|
||||||
|
|
||||||
|
// Santé
|
||||||
|
vigueur: "system.sante.vigueur",
|
||||||
|
etat: "system.sante.etat",
|
||||||
|
nbcombativite: "system.sante.nbcombativite",
|
||||||
|
|
||||||
|
// Âme
|
||||||
|
nbame: "system.ame.nbame",
|
||||||
|
seuilpouvoir: "system.ame.seuilpouvoir",
|
||||||
|
etatAme: "system.ame.etat",
|
||||||
|
|
||||||
|
// Bonne Aventure
|
||||||
|
bonneaventure: "system.bonneaventure.base",
|
||||||
|
bonneaventureActuelle: "system.bonneaventure.actuelle",
|
||||||
|
eclat: "system.eclat.value",
|
||||||
|
|
||||||
|
// Combat
|
||||||
|
initiative: "system.combat.inittotal",
|
||||||
|
defense: "system.combat.defensetotal",
|
||||||
|
protection: "system.combat.protectiontotal",
|
||||||
|
|
||||||
|
// Adversités
|
||||||
|
adversiteBleue: "system.adversite.bleue",
|
||||||
|
adversiteRouge: "system.adversite.rouge",
|
||||||
|
adversiteNoire: "system.adversite.noire",
|
||||||
|
|
||||||
|
// Balance
|
||||||
|
loi: "system.balance.loi",
|
||||||
|
chaos: "system.balance.chaos",
|
||||||
|
aspect: "system.balance.aspect",
|
||||||
|
|
||||||
|
// Ressources
|
||||||
|
ressources: "system.ressources.value",
|
||||||
|
|
||||||
|
// Vitesse
|
||||||
|
vitesse: "system.vitesse.value"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Types de bonus/malus supportés (groupés par catégorie)
|
||||||
|
effectTypesConfig: {
|
||||||
|
attribut: {
|
||||||
|
label: "Attribut",
|
||||||
|
keys: ["adr", "pui", "cla", "pre", "tre"]
|
||||||
|
},
|
||||||
|
sante: {
|
||||||
|
label: "Santé",
|
||||||
|
keys: ["vigueur", "etat", "nbcombativite"]
|
||||||
|
},
|
||||||
|
ame: {
|
||||||
|
label: "Âme",
|
||||||
|
keys: ["nbame", "seuilpouvoir", "etatAme"]
|
||||||
|
},
|
||||||
|
combat: {
|
||||||
|
label: "Combat",
|
||||||
|
keys: ["initiative", "defense", "protection"]
|
||||||
|
},
|
||||||
|
bonneAventure: {
|
||||||
|
label: "Bonne Aventure",
|
||||||
|
keys: ["bonneaventure", "bonneaventureActuelle", "eclat"]
|
||||||
|
},
|
||||||
|
adversite: {
|
||||||
|
label: "Adversité",
|
||||||
|
keys: ["adversiteBleue", "adversiteRouge", "adversiteNoire"]
|
||||||
|
},
|
||||||
|
balance: {
|
||||||
|
label: "Balance",
|
||||||
|
keys: ["loi", "chaos", "aspect"]
|
||||||
|
}
|
||||||
|
},
|
||||||
optionsUseTalent: [
|
optionsUseTalent: [
|
||||||
{ key: "permanent", label: "Permanent" },
|
{ key: "permanent", label: "Permanent" },
|
||||||
{ key: "sceance", label: "Une fois par scéance" },
|
{ key: "sceance", label: "Une fois par scéance" },
|
||||||
|
|||||||
@@ -0,0 +1,712 @@
|
|||||||
|
/**
|
||||||
|
* Gestion des ActiveEffects pour Mournblade CYD 2.0
|
||||||
|
* Ce module fournit des utilitaires pour créer, appliquer et gérer les effets actifs
|
||||||
|
* sur les Acteurs et les Items.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class MournbladeCYD2Effects {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise le système de gestion des effets
|
||||||
|
*/
|
||||||
|
static init() {
|
||||||
|
console.log("MournbladeCYD2 | Initializing ActiveEffects management");
|
||||||
|
|
||||||
|
// Hook pour appliquer les modifications des effets
|
||||||
|
Hooks.on("applyActiveEffect", (effect, change, current, delta, changes) => {
|
||||||
|
return this._onApplyActiveEffect(effect, change, current, delta, changes);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hook pour supprimer les modifications des effets
|
||||||
|
Hooks.on("removeActiveEffect", (effect, change, current, delta, changes) => {
|
||||||
|
return this._onRemoveActiveEffect(effect, change, current, delta, changes);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook appelé lorsqu'un effet est appliqué
|
||||||
|
* Permet de personnaliser le calcul des modifications
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
return delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook appelé lorsqu'un effet est supprimé
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static _onRemoveActiveEffect(effect, change, current, delta, changes) {
|
||||||
|
// Logique inverse de l'application
|
||||||
|
if (typeof current === "number" && typeof delta === "number") {
|
||||||
|
return current - delta;
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Méthodes de création d'effets */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet simple de bonus/malus à un attribut
|
||||||
|
* @param {string} name - Nom de l'effet
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
static createSimpleEffect(name, attribute, value, options = {}) {
|
||||||
|
const attributeKey = this.getAttributeKey(attribute);
|
||||||
|
if (!attributeKey) {
|
||||||
|
console.warn(`MournbladeCYD2 | Unknown attribute: ${attribute}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: name,
|
||||||
|
icon: options.icon || "systems/fvtt-mournblade-cyd-2-0/assets/icons/effect.webp",
|
||||||
|
description: options.description || "",
|
||||||
|
changes: [
|
||||||
|
{
|
||||||
|
key: attributeKey,
|
||||||
|
mode: CONST.ActiveEffect.MODES.ADD,
|
||||||
|
value: value.toString(),
|
||||||
|
priority: options.priority || 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
disabled: options.disabled || false,
|
||||||
|
duration: options.duration || {},
|
||||||
|
origin: options.origin || null,
|
||||||
|
tint: options.tint || "",
|
||||||
|
transfer: options.transfer !== false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet de bonus permanent
|
||||||
|
* @param {string} name - Nom de l'effet
|
||||||
|
* @param {string} attribute - Attribut cible
|
||||||
|
* @param {number|string} value - Valeur du bonus
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createPermanentEffect(name, attribute, value) {
|
||||||
|
return this.createSimpleEffect(name, attribute, value, {
|
||||||
|
duration: {},
|
||||||
|
type: "base"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet temporaire (rounds, turns, etc.)
|
||||||
|
* @param {string} name - Nom de l'effet
|
||||||
|
* @param {string} attribute - Attribut cible
|
||||||
|
* @param {number|string} value - Valeur du bonus/malus
|
||||||
|
* @param {string} durationType - Type de durée (rounds, turns, seconds, combat)
|
||||||
|
* @param {number} durationValue - Valeur de la durée
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createTemporaryEffect(name, attribute, value, durationType, durationValue) {
|
||||||
|
return this.createSimpleEffect(name, attribute, value, {
|
||||||
|
duration: { type: durationType, value: durationValue },
|
||||||
|
type: "temp"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet avec plusieurs modifications
|
||||||
|
* @param {string} name - Nom de l'effet
|
||||||
|
* @param {Array} changes - Array de modifications {key, mode, value}
|
||||||
|
* @param {object} options - Options supplémentaires
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createMultiEffect(name, changes, options = {}) {
|
||||||
|
return {
|
||||||
|
name: name,
|
||||||
|
icon: options.icon || "systems/fvtt-mournblade-cyd-2-0/assets/icons/effect.webp",
|
||||||
|
description: options.description || "",
|
||||||
|
changes: changes.map(c => ({
|
||||||
|
key: c.key,
|
||||||
|
mode: c.mode || CONST.ActiveEffect.MODES.ADD,
|
||||||
|
value: c.value.toString(),
|
||||||
|
priority: c.priority || 0
|
||||||
|
})),
|
||||||
|
disabled: options.disabled || false,
|
||||||
|
duration: options.duration || {},
|
||||||
|
origin: options.origin || null,
|
||||||
|
tint: options.tint || "",
|
||||||
|
transfer: options.transfer !== false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Méthodes d'application d'effets */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applique un effet à un acteur
|
||||||
|
* @param {Actor} actor - L'acteur cible
|
||||||
|
* @param {Object|ActiveEffect} effectData - Données de l'effet ou instance ActiveEffect
|
||||||
|
* @returns {Promise<ActiveEffect|null>} - L'effet créé ou null
|
||||||
|
*/
|
||||||
|
static async applyEffectToActor(actor, effectData) {
|
||||||
|
if (!actor || !actor.isOwner) return null;
|
||||||
|
|
||||||
|
const effect = effectData instanceof foundry.documents.ActiveEffect
|
||||||
|
? effectData
|
||||||
|
: 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);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applique les effets d'un item à un acteur
|
||||||
|
* @param {Item} item - L'item source
|
||||||
|
* @param {Actor} actor - L'acteur cible
|
||||||
|
* @returns {Promise<Array<ActiveEffect>>} - Liste des effets appliqués
|
||||||
|
*/
|
||||||
|
static async applyItemEffectsToActor(item, actor) {
|
||||||
|
if (!item?.effects?.length || !actor) 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) {
|
||||||
|
effectsToApply.push({
|
||||||
|
...effectData.toObject(),
|
||||||
|
origin: item.uuid,
|
||||||
|
name: `${item.name}: ${effectData.name}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (effectsToApply.length === 0) return [];
|
||||||
|
|
||||||
|
const createdEffects = await actor.createEmbeddedDocuments("ActiveEffect", effectsToApply);
|
||||||
|
return createdEffects;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Méthodes de gestion d'effets */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Désactive un effet
|
||||||
|
* @param {Actor|Item} owner - Le propriétaire de l'effet
|
||||||
|
* @param {string} effectId - ID de l'effet
|
||||||
|
* @returns {Promise<ActiveEffect|null>} - L'effet désactivé
|
||||||
|
*/
|
||||||
|
static async disableEffect(owner, effectId) {
|
||||||
|
const effect = owner.effects.get(effectId);
|
||||||
|
if (effect) {
|
||||||
|
await effect.update({ disabled: true });
|
||||||
|
return effect;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Active un effet
|
||||||
|
* @param {Actor|Item} owner - Le propriétaire de l'effet
|
||||||
|
* @param {string} effectId - ID de l'effet
|
||||||
|
* @returns {Promise<ActiveEffect|null>} - L'effet activé
|
||||||
|
*/
|
||||||
|
static async enableEffect(owner, effectId) {
|
||||||
|
const effect = owner.effects.get(effectId);
|
||||||
|
if (effect) {
|
||||||
|
await effect.update({ disabled: false });
|
||||||
|
return effect;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle l'état d'un effet (actif/désactivé)
|
||||||
|
* @param {Actor|Item} owner - Le propriétaire de l'effet
|
||||||
|
* @param {string} effectId - ID de l'effet
|
||||||
|
* @returns {Promise<ActiveEffect|null>} - L'effet togglé
|
||||||
|
*/
|
||||||
|
static async toggleEffect(owner, effectId) {
|
||||||
|
const effect = owner.effects.get(effectId);
|
||||||
|
if (effect) {
|
||||||
|
await effect.update({ disabled: !effect.disabled });
|
||||||
|
return effect;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supprime un effet
|
||||||
|
* @param {Actor|Item} owner - Le propriétaire de l'effet
|
||||||
|
* @param {string} effectId - ID de l'effet
|
||||||
|
* @returns {Promise<ActiveEffect|null>} - L'effet supprimé
|
||||||
|
*/
|
||||||
|
static async deleteEffect(owner, effectId) {
|
||||||
|
const effect = owner.effects.get(effectId);
|
||||||
|
if (effect) {
|
||||||
|
await owner.deleteEmbeddedDocuments("ActiveEffect", [effectId]);
|
||||||
|
return effect;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Méthodes utilitaires */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtient la clé complète pour un attribut
|
||||||
|
* @param {string} attribute - Attribut court (adr, pui, cla, pre, tre, vigueur, etc.)
|
||||||
|
* @returns {string|null} - Clé complète ou null
|
||||||
|
*/
|
||||||
|
static getAttributeKey(attribute) {
|
||||||
|
const config = game.system.mournbladecyd2?.config;
|
||||||
|
if (!config?.effectAttributeKeys) return null;
|
||||||
|
|
||||||
|
return config.effectAttributeKeys[attribute] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtient tous les attributs modifiables
|
||||||
|
* @returns {Object} - Map des attributs courts vers les clés complètes
|
||||||
|
*/
|
||||||
|
static getAllAttributeKeys() {
|
||||||
|
const config = game.system.mournbladecyd2?.config;
|
||||||
|
return config?.effectAttributeKeys || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtient les effets actifs d'un acteur
|
||||||
|
* @param {Actor} actor - L'acteur
|
||||||
|
* @returns {Array<ActiveEffect>} - Liste des effets actifs (non désactivés)
|
||||||
|
*/
|
||||||
|
static getActiveEffects(actor) {
|
||||||
|
if (!actor?.effects) return [];
|
||||||
|
return actor.effects.filter(e => !e.disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtient les effets désactivés d'un acteur
|
||||||
|
* @param {Actor} actor - L'acteur
|
||||||
|
* @returns {Array<ActiveEffect>} - Liste des effets désactivés
|
||||||
|
*/
|
||||||
|
static getDisabledEffects(actor) {
|
||||||
|
if (!actor?.effects) return [];
|
||||||
|
return actor.effects.filter(e => e.disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtient les effets par origine
|
||||||
|
* @param {Actor} actor - L'acteur
|
||||||
|
* @param {string} originUuid - UUID de l'origine
|
||||||
|
* @returns {Array<ActiveEffect>} - Liste des effets de cette origine
|
||||||
|
*/
|
||||||
|
static getEffectsByOrigin(actor, originUuid) {
|
||||||
|
if (!actor?.effects) return [];
|
||||||
|
return actor.effects.filter(e => e.origin === originUuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtient les effets temporaires en cours
|
||||||
|
* @param {Actor} actor - L'acteur
|
||||||
|
* @returns {Array<ActiveEffect>} - Liste des effets temporaires actifs
|
||||||
|
*/
|
||||||
|
static getActiveTemporaryEffects(actor) {
|
||||||
|
if (!actor?.effects) return [];
|
||||||
|
return actor.effects.filter(e => !e.disabled && e.duration?.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calcule la valeur totale des modifications pour une clé donnée
|
||||||
|
* @param {Actor} actor - L'acteur
|
||||||
|
* @param {string} key - Clé à vérifier
|
||||||
|
* @returns {number} - Somme des modifications
|
||||||
|
*/
|
||||||
|
static getTotalModificationForKey(actor, key) {
|
||||||
|
if (!actor?.effects) return 0;
|
||||||
|
|
||||||
|
let total = 0;
|
||||||
|
for (const effect of actor.effects) {
|
||||||
|
if (effect.disabled) continue;
|
||||||
|
|
||||||
|
for (const change of effect.changes || []) {
|
||||||
|
if (change.key === key && change.mode === CONST.ActiveEffect.MODES.ADD) {
|
||||||
|
total += Number(change.value) || 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtient toutes les modifications actives groupées par clé
|
||||||
|
* @param {Actor} actor - L'acteur
|
||||||
|
* @returns {Object} - Objet avec les clés et les valeurs totales
|
||||||
|
*/
|
||||||
|
static getAllActiveModifications(actor) {
|
||||||
|
if (!actor?.effects) return {};
|
||||||
|
|
||||||
|
const modifications = {};
|
||||||
|
|
||||||
|
for (const effect of actor.effects) {
|
||||||
|
if (effect.disabled) continue;
|
||||||
|
|
||||||
|
for (const change of effect.changes || []) {
|
||||||
|
if (!modifications[change.key]) {
|
||||||
|
modifications[change.key] = {
|
||||||
|
value: 0,
|
||||||
|
effects: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Appliquer selon le mode
|
||||||
|
const numericValue = Number(change.value) || 0;
|
||||||
|
switch (change.mode) {
|
||||||
|
case CONST.ActiveEffect.MODES.ADD:
|
||||||
|
modifications[change.key].value += numericValue;
|
||||||
|
break;
|
||||||
|
case CONST.ActiveEffect.MODES.OVERRIDE:
|
||||||
|
modifications[change.key].value = numericValue;
|
||||||
|
modifications[change.key].overridden = true;
|
||||||
|
break;
|
||||||
|
case CONST.ActiveEffect.MODES.MULTIPLY:
|
||||||
|
// Ne peut pas être additionné, stocké séparément
|
||||||
|
if (!modifications[change.key].multipliers) {
|
||||||
|
modifications[change.key].multipliers = [];
|
||||||
|
}
|
||||||
|
modifications[change.key].multipliers.push(numericValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
modifications[change.key].effects.push(effect.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return modifications;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Méthodes de création d'effets prédéfinis */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet de bonus d'attribut
|
||||||
|
* @param {string} attribute - Attribut (adr, pui, cla, pre, tre)
|
||||||
|
* @param {number} value - Valeur du bonus
|
||||||
|
* @param {string} source - Source de l'effet
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createAttributeBonusEffect(attribute, value, source = "Effet") {
|
||||||
|
const attrNames = {
|
||||||
|
adr: "Adresse",
|
||||||
|
pui: "Puissance",
|
||||||
|
cla: "Clairvoyance",
|
||||||
|
pre: "Présence",
|
||||||
|
tre: "Trempe"
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.createSimpleEffect(
|
||||||
|
`${source}: Bonus de ${attrNames[attribute] || attribute}`,
|
||||||
|
attribute,
|
||||||
|
`+${value}`,
|
||||||
|
{
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/attributs.webp",
|
||||||
|
type: "base"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet de malus d'attribut
|
||||||
|
* @param {string} attribute - Attribut
|
||||||
|
* @param {number} value - Valeur du malus (positif)
|
||||||
|
* @param {string} source - Source de l'effet
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createAttributeMalusEffect(attribute, value, source = "Effet") {
|
||||||
|
const attrNames = {
|
||||||
|
adr: "Adresse",
|
||||||
|
pui: "Puissance",
|
||||||
|
cla: "Clairvoyance",
|
||||||
|
pre: "Présence",
|
||||||
|
tre: "Trempe"
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.createSimpleEffect(
|
||||||
|
`${source}: Malus de ${attrNames[attribute] || attribute}`,
|
||||||
|
attribute,
|
||||||
|
`-${value}`,
|
||||||
|
{
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/malus.webp",
|
||||||
|
type: "base"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet de bonus à la Vigueur
|
||||||
|
* @param {number} value - Valeur du bonus
|
||||||
|
* @param {string} source - Source de l'effet
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createVigueurBonusEffect(value, source = "Effet") {
|
||||||
|
return this.createSimpleEffect(
|
||||||
|
`${source}: Bonus de Vigueur`,
|
||||||
|
"vigueur",
|
||||||
|
`+${value}`,
|
||||||
|
{
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/vigueur.webp",
|
||||||
|
type: "base"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet de bonus au Seuil de Pouvoir
|
||||||
|
* @param {number} value - Valeur du bonus
|
||||||
|
* @param {string} source - Source de l'effet
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createSeuilPouvoirBonusEffect(value, source = "Effet") {
|
||||||
|
return this.createSimpleEffect(
|
||||||
|
`${source}: Bonus au Seuil de Pouvoir`,
|
||||||
|
"seuilPouvoir",
|
||||||
|
`+${value}`,
|
||||||
|
{
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/ame.webp",
|
||||||
|
type: "base"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet de bonus à la Bonne Aventure
|
||||||
|
* @param {number} value - Valeur du bonus
|
||||||
|
* @param {string} source - Source de l'effet
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createBonneAventureBonusEffect(value, source = "Effet") {
|
||||||
|
return this.createSimpleEffect(
|
||||||
|
`${source}: Bonus de Bonne Aventure`,
|
||||||
|
"bonneAventure",
|
||||||
|
`+${value}`,
|
||||||
|
{
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/bonneaventure.webp",
|
||||||
|
type: "base"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet de bonus à l'Initiative
|
||||||
|
* @param {number} value - Valeur du bonus
|
||||||
|
* @param {string} source - Source de l'effet
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createInitiativeBonusEffect(value, source = "Effet") {
|
||||||
|
return this.createSimpleEffect(
|
||||||
|
`${source}: Bonus d'Initiative`,
|
||||||
|
"initiative",
|
||||||
|
`+${value}`,
|
||||||
|
{
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/initiative.webp",
|
||||||
|
type: "temp",
|
||||||
|
duration: { type: "rounds", value: 1 }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet de bonus à la Défense
|
||||||
|
* @param {number} value - Valeur du bonus
|
||||||
|
* @param {string} source - Source de l'effet
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createDefenseBonusEffect(value, source = "Effet") {
|
||||||
|
return this.createSimpleEffect(
|
||||||
|
`${source}: Bonus de Défense`,
|
||||||
|
"defense",
|
||||||
|
`+${value}`,
|
||||||
|
{
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/defense.webp",
|
||||||
|
type: "temp",
|
||||||
|
duration: { type: "rounds", value: 1 }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet de bonus à la Protection
|
||||||
|
* @param {number} value - Valeur du bonus
|
||||||
|
* @param {string} source - Source de l'effet
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createProtectionBonusEffect(value, source = "Effet") {
|
||||||
|
return this.createSimpleEffect(
|
||||||
|
`${source}: Bonus de Protection`,
|
||||||
|
"protection",
|
||||||
|
`+${value}`,
|
||||||
|
{
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/protection.webp",
|
||||||
|
type: "base"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Méthodes de gestion des statuts */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet qui applique un statut
|
||||||
|
* @param {string} status - Nom du statut
|
||||||
|
* @param {string} source - Source de l'effet
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createStatusEffect(status, source = "Effet") {
|
||||||
|
return {
|
||||||
|
name: `${source}: ${status}`,
|
||||||
|
icon: `systems/fvtt-mournblade-cyd-2-0/assets/icons/status_${status.toLowerCase()}.webp`,
|
||||||
|
description: `Applique le statut ${status}`,
|
||||||
|
changes: [],
|
||||||
|
statuses: [status],
|
||||||
|
disabled: false,
|
||||||
|
duration: {},
|
||||||
|
origin: null,
|
||||||
|
tint: "",
|
||||||
|
transfer: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet d'adversité bleue
|
||||||
|
* @param {number} value - Nombre d'adversités
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createAdversiteBleueEffect(value) {
|
||||||
|
return this.createSimpleEffect(
|
||||||
|
`Adversité Bleue: +${value}`,
|
||||||
|
"adversite.bleue",
|
||||||
|
value,
|
||||||
|
{
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/gemme_bleue.webp",
|
||||||
|
type: "temp",
|
||||||
|
statuses: ["adversite-bleue"]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet d'adversité rouge
|
||||||
|
* @param {number} value - Nombre d'adversités
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createAdversiteRougeEffect(value) {
|
||||||
|
return this.createSimpleEffect(
|
||||||
|
`Adversité Rouge: +${value}`,
|
||||||
|
"adversite.rouge",
|
||||||
|
value,
|
||||||
|
{
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/gemme_rouge.webp",
|
||||||
|
type: "temp",
|
||||||
|
statuses: ["adversite-rouge"]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un effet d'adversité noire
|
||||||
|
* @param {number} value - Nombre d'adversités
|
||||||
|
* @returns {Object} - Données de l'effet
|
||||||
|
*/
|
||||||
|
static createAdversiteNoireEffect(value) {
|
||||||
|
return this.createSimpleEffect(
|
||||||
|
`Adversité Noire: +${value}`,
|
||||||
|
"adversite.noire",
|
||||||
|
value,
|
||||||
|
{
|
||||||
|
icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/gemme_noire.webp",
|
||||||
|
type: "temp",
|
||||||
|
statuses: ["adversite-noire"]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Méthodes pour les Runes */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
static createRunePrononceeEffect(rune, pointsAme) {
|
||||||
|
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
|
||||||
|
],
|
||||||
|
disabled: false,
|
||||||
|
duration: { type: "rounds", value: Math.ceil(pointsAme / 3) },
|
||||||
|
origin: rune.uuid,
|
||||||
|
tint: "#00ff00",
|
||||||
|
transfer: true,
|
||||||
|
flags: {
|
||||||
|
"mournblade-cyd2": {
|
||||||
|
runeId: rune._id,
|
||||||
|
runeType: "prononcee",
|
||||||
|
pointsAme: pointsAme
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
static createRuneTraceeEffect(rune, pointsAme) {
|
||||||
|
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: [],
|
||||||
|
disabled: false,
|
||||||
|
duration: { type: "rounds", value: Math.ceil(pointsAme / 3) * 2 },
|
||||||
|
origin: rune.uuid,
|
||||||
|
tint: "#0000ff",
|
||||||
|
transfer: true,
|
||||||
|
flags: {
|
||||||
|
"mournblade-cyd2": {
|
||||||
|
runeId: rune._id,
|
||||||
|
runeType: "tracee",
|
||||||
|
pointsAme: pointsAme
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialisation automatique
|
||||||
|
table Hooks.once("init", () => {
|
||||||
|
MournbladeCYD2Effects.init();
|
||||||
|
});
|
||||||
@@ -16,6 +16,7 @@ import { MournbladeCYD2Item } from "./mournblade-cyd2-item.js";
|
|||||||
import { MournbladeCYD2Automation } from "./mournblade-cyd2-automation.js";
|
import { MournbladeCYD2Automation } from "./mournblade-cyd2-automation.js";
|
||||||
import { MournbladeCYD2TokenHud } from "./mournblade-cyd2-hud.js";
|
import { MournbladeCYD2TokenHud } from "./mournblade-cyd2-hud.js";
|
||||||
import { MOURNBLADECYD2_CONFIG } from "./mournblade-cyd2-config.js";
|
import { MOURNBLADECYD2_CONFIG } from "./mournblade-cyd2-config.js";
|
||||||
|
import { MournbladeCYD2Effects } from "./mournblade-cyd2-effects.js";
|
||||||
|
|
||||||
// Import DataModels
|
// Import DataModels
|
||||||
import * as models from "./models/index.mjs";
|
import * as models from "./models/index.mjs";
|
||||||
@@ -77,6 +78,7 @@ Hooks.once("init", async function () {
|
|||||||
game.system.mournbladecyd2 = {
|
game.system.mournbladecyd2 = {
|
||||||
MournbladeCYD2Utility,
|
MournbladeCYD2Utility,
|
||||||
MournbladeCYD2Automation,
|
MournbladeCYD2Automation,
|
||||||
|
MournbladeCYD2Effects,
|
||||||
config: MOURNBLADECYD2_CONFIG
|
config: MOURNBLADECYD2_CONFIG
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -115,6 +115,7 @@
|
|||||||
<a class="item" data-tab="dons">Dons & Pactes</a>
|
<a class="item" data-tab="dons">Dons & Pactes</a>
|
||||||
<a class="item" data-tab="combat">Combat</a>
|
<a class="item" data-tab="combat">Combat</a>
|
||||||
<a class="item" data-tab="equipement">{{localize "MNBL.equipment"}}</a>
|
<a class="item" data-tab="equipement">{{localize "MNBL.equipment"}}</a>
|
||||||
|
<a class="item" data-tab="effects">Effets</a>
|
||||||
<a class="item" data-tab="biodata">Bio&Notes</a>
|
<a class="item" data-tab="biodata">Bio&Notes</a>
|
||||||
</nav>
|
</nav>
|
||||||
<hr>
|
<hr>
|
||||||
@@ -690,6 +691,10 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{!-- Effects Tab --}}
|
||||||
|
<div class="tab effects" data-group="primary" data-tab="effects">
|
||||||
|
{{> systems/fvtt-mournblade-cyd-2-0/templates/partial-active-effects.hbs}}
|
||||||
|
</div>
|
||||||
|
|
||||||
{{!-- Biography Tab --}}
|
{{!-- Biography Tab --}}
|
||||||
<div class="tab biodata" data-group="primary" data-tab="biodata">
|
<div class="tab biodata" data-group="primary" data-tab="biodata">
|
||||||
|
|||||||
@@ -92,6 +92,7 @@
|
|||||||
<a class="item" data-tab="competences">Compétences</a>
|
<a class="item" data-tab="competences">Compétences</a>
|
||||||
<a class="item" data-tab="talents">Talents</a>
|
<a class="item" data-tab="talents">Talents</a>
|
||||||
<a class="item" data-tab="armes">Armes</a>
|
<a class="item" data-tab="armes">Armes</a>
|
||||||
|
<a class="item" data-tab="effects">Effets</a>
|
||||||
<a class="item" data-tab="biodata">Bio&Notes</a>
|
<a class="item" data-tab="biodata">Bio&Notes</a>
|
||||||
</nav>
|
</nav>
|
||||||
<hr>
|
<hr>
|
||||||
@@ -422,6 +423,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{!-- Effects Tab --}}
|
||||||
|
<div class="tab effects" data-group="primary" data-tab="effects">
|
||||||
|
{{> systems/fvtt-mournblade-cyd-2-0/templates/partial-active-effects.hbs}}
|
||||||
|
</div>
|
||||||
|
|
||||||
{{!-- Bio&Notes Tab --}}
|
{{!-- Bio&Notes Tab --}}
|
||||||
<div class="tab biodata" data-group="primary" data-tab="biodata">
|
<div class="tab biodata" data-group="primary" data-tab="biodata">
|
||||||
<div class="sheet-box color-bg-archetype">
|
<div class="sheet-box color-bg-archetype">
|
||||||
|
|||||||
@@ -54,5 +54,12 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{!-- Effects Tab --}}
|
||||||
|
{{#if item.effects.length}}
|
||||||
|
<div class="tab effects" data-group="primary" data-tab="effects">
|
||||||
|
{{> systems/fvtt-mournblade-cyd-2-0/templates/partial-item-effects.hbs}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
{{!-- Partial pour l'affichage des ActiveEffects --}}
|
||||||
|
|
||||||
|
<div class="sheet-box color-bg-archetype">
|
||||||
|
<ul class="item-list alternate-list">
|
||||||
|
|
||||||
|
{{!-- En-tête --}}
|
||||||
|
<li class="item flexrow list-item items-title-bg">
|
||||||
|
<span class="item-name-label-header">
|
||||||
|
<h3><label class="items-title-text">Effets Actifs</label></h3>
|
||||||
|
</span>
|
||||||
|
<div class="item-filler"> </div>
|
||||||
|
<div class="item-controls item-controls-fixed">
|
||||||
|
<a class="item-control" data-action="createEffect" title="Ajouter un effet">
|
||||||
|
<i class="fas fa-plus"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
{{!-- Affiche un message si aucun effet --}}
|
||||||
|
{{#if (not actor.effects.length)}}
|
||||||
|
<li class="item flexrow">
|
||||||
|
<span class="item-name-label competence-name" style="color: #888; font-style: italic;">Aucun effet actif</span>
|
||||||
|
</li>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{!-- Liste des effets --}}
|
||||||
|
{{#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'" />
|
||||||
|
|
||||||
|
{{!-- Nom et description de l'effet --}}
|
||||||
|
<div class="flexcol item-name-label">
|
||||||
|
<span class="item-name-label competence-name">
|
||||||
|
{{effect.name}}
|
||||||
|
{{#if effect.disabled}}<i class="fas fa-ban" style="color: #ff5555; margin-left: 5px;" title="Désactivé"></i>{{/if}}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{{!-- Affichage compact des modifications --}}
|
||||||
|
{{#if effect.changes.length}}
|
||||||
|
<span class="predilection-text" style="font-size: 0.85em; color: #aaa;">
|
||||||
|
{{#each effect.changes as |change index|}}
|
||||||
|
{{#if (eq change.mode 0)}}(+{{/if}}{{#if (eq change.mode 1)}}(*{{/if}}{{#if (eq change.mode 2)}}={{/if}}{{change.value}}{{#if (eq change.mode 0)}}){{/if}}{{#if (eq change.mode 1)}}){{/if}}
|
||||||
|
{{#unless (eq index (subtract effect.changes.length 1))}}, {{/unless}}
|
||||||
|
{{/each}}
|
||||||
|
{{#each effect.statuses as |status|}}
|
||||||
|
{{#if (and (ne status "") (not (eq index 0)))}}, {{/if}}
|
||||||
|
<i class="fas fa-exclamation-triangle" style="color: #ffaa00;" title="{{status}}"></i>
|
||||||
|
{{/each}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{!-- Affichage de la durée --}}
|
||||||
|
{{#if effect.duration.type}}
|
||||||
|
<span class="item-field-label-short" style="font-size: 0.85em;">
|
||||||
|
{{#if (eq effect.duration.type "rounds")}}🔄{{/if}}
|
||||||
|
{{#if (eq effect.duration.type "turns")}}🎭{{/if}}
|
||||||
|
{{#if (eq effect.duration.type "seconds")}}⏱️{{/if}}
|
||||||
|
{{#if (eq effect.duration.type "combat")}}⚔️{{/if}}
|
||||||
|
{{#if (eq effect.duration.type "scene")}}📜{{/if}}
|
||||||
|
{{effect.duration.value}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{!-- Contrôles --}}
|
||||||
|
<div class="item-controls item-controls-fixed">
|
||||||
|
<a class="item-control" data-action="editEffect" data-effect-id="{{effect.id}}" title="Éditer">
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
</a>
|
||||||
|
<a class="item-control" data-action="toggleEffect" data-effect-id="{{effect.id}}" title="{{#if effect.disabled}}Activer{{else}}Désactiver{{/if}}">
|
||||||
|
<i class="fas fa-{{#if effect.disabled}}check{{else}}times{{/if}}"></i>
|
||||||
|
</a>
|
||||||
|
<a class="item-control" data-action="deleteEffect" data-effect-id="{{effect.id}}" title="Supprimer">
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{!-- Affichage détaillé des effets actifs --}}
|
||||||
|
{{#if actor.effects.length}}
|
||||||
|
<div class="sheet-box color-bg-archetype effect-summary">
|
||||||
|
<h4 class="section-title">Résumé des modifications</h4>
|
||||||
|
<div class="effect-modifications">
|
||||||
|
{{#each actor.effects as |effect|}}
|
||||||
|
{{#if (not effect.disabled)}}
|
||||||
|
{{#if effect.changes.length}}
|
||||||
|
<div class="effect-category">
|
||||||
|
<strong>{{effect.name}}:</strong>
|
||||||
|
{{#each effect.changes as |change|}}
|
||||||
|
<span class="effect-modification">
|
||||||
|
{{change.key}}:
|
||||||
|
{{#if (eq change.mode 0)}}+{{/if}}
|
||||||
|
{{#if (eq change.mode 1)}}*{{/if}}
|
||||||
|
{{#if (eq change.mode 2)}}={{/if}}
|
||||||
|
{{change.value}}
|
||||||
|
{{#if (eq change.mode 0)}}{{/if}}
|
||||||
|
{{#if (eq change.mode 1)}}{{/if}}
|
||||||
|
</span>{{#unless @last}}, {{/unless}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
{{!-- Partial pour l'affichage des effets sur les items --}}
|
||||||
|
|
||||||
|
<div class="sheet-box color-bg-archetype">
|
||||||
|
<ul class="item-list alternate-list">
|
||||||
|
|
||||||
|
{{!-- En-tête --}}
|
||||||
|
<li class="item flexrow list-item items-title-bg">
|
||||||
|
<span class="item-name-label-header">
|
||||||
|
<h3><label class="items-title-text">Effets Actifs</label></h3>
|
||||||
|
</span>
|
||||||
|
<div class="item-filler"> </div>
|
||||||
|
<div class="item-controls item-controls-fixed">
|
||||||
|
<a class="item-control" data-action="createEffect" title="Ajouter un effet">
|
||||||
|
<i class="fas fa-plus"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
{{!-- Affiche un message si aucun effet --}}
|
||||||
|
{{#if (not item.effects.length)}}
|
||||||
|
<li class="item flexrow">
|
||||||
|
<span class="item-name-label competence-name" style="color: #888; font-style: italic;">Aucun effet sur cet item</span>
|
||||||
|
</li>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{!-- Liste des effets --}}
|
||||||
|
{{#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'" />
|
||||||
|
|
||||||
|
{{!-- Nom et description de l'effet --}}
|
||||||
|
<div class="flexcol item-name-label">
|
||||||
|
<span class="item-name-label competence-name">
|
||||||
|
{{effect.name}}
|
||||||
|
{{#if effect.disabled}}<i class="fas fa-ban" style="color: #ff5555; margin-left: 5px;" title="Désactivé"></i>{{/if}}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{{!-- Affichage compact des modifications --}}
|
||||||
|
{{#if effect.changes.length}}
|
||||||
|
<span class="predilection-text" style="font-size: 0.85em; color: #aaa;">
|
||||||
|
{{#each effect.changes as |change index|}}
|
||||||
|
{{change.key}}: {{change.value}}
|
||||||
|
{{#unless (eq index (subtract effect.changes.length 1))}}, {{/unless}}
|
||||||
|
{{/each}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{!-- Affichage de la durée --}}
|
||||||
|
{{#if effect.duration.type}}
|
||||||
|
<span class="item-field-label-short" style="font-size: 0.85em;">
|
||||||
|
{{#if (eq effect.duration.type "rounds")}}🔄{{/if}}
|
||||||
|
{{#if (eq effect.duration.type "turns")}}🎭{{/if}}
|
||||||
|
{{#if (eq effect.duration.type "seconds")}}⏱️{{/if}}
|
||||||
|
{{#if (eq effect.duration.type "combat")}}⚔️{{/if}}
|
||||||
|
{{effect.duration.value}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{!-- Contrôles --}}
|
||||||
|
<div class="item-controls item-controls-fixed">
|
||||||
|
<a class="item-control" data-action="editEffect" data-effect-id="{{effect.id}}" title="Éditer">
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
</a>
|
||||||
|
<a class="item-control" data-action="toggleEffect" data-effect-id="{{effect.id}}" title="{{#if effect.disabled}}Activer{{else}}Désactiver{{/if}}">
|
||||||
|
<i class="fas fa-{{#if effect.disabled}}check{{else}}times{{/if}}"></i>
|
||||||
|
</a>
|
||||||
|
<a class="item-control" data-action="deleteEffect" data-effect-id="{{effect.id}}" title="Supprimer">
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
@@ -3,5 +3,8 @@
|
|||||||
<nav class="sheet-tabs tabs" data-group="primary">
|
<nav class="sheet-tabs tabs" data-group="primary">
|
||||||
<a class="item" data-tab="description">Description</a>
|
<a class="item" data-tab="description">Description</a>
|
||||||
<a class="item" data-tab="details">Details</a>
|
<a class="item" data-tab="details">Details</a>
|
||||||
|
{{#if item.effects.length}}
|
||||||
|
<a class="item" data-tab="effects">Effets</a>
|
||||||
|
{{/if}}
|
||||||
</nav>
|
</nav>
|
||||||
<hr />
|
<hr />
|
||||||
|
|||||||
Reference in New Issue
Block a user