156672d853
- 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>
296 lines
9.1 KiB
JavaScript
296 lines
9.1 KiB
JavaScript
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
|
|
|
import { MournbladeCYD2Utility } from "../../mournblade-cyd2-utility.js";
|
|
|
|
export default class MournbladeCYD2ItemSheetV2 extends HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2) {
|
|
|
|
constructor(options = {}) {
|
|
super(options);
|
|
this.#dragDrop = this.#createDragDropHandlers();
|
|
}
|
|
|
|
#dragDrop;
|
|
|
|
/** @override */
|
|
static DEFAULT_OPTIONS = {
|
|
classes: ["fvtt-mournblade-cyd-2-0", "item"],
|
|
position: {
|
|
width: 620,
|
|
height: 600,
|
|
},
|
|
form: {
|
|
submitOnChange: true,
|
|
},
|
|
window: {
|
|
resizable: true,
|
|
},
|
|
tabs: [
|
|
{
|
|
navSelector: 'nav[data-group="primary"]',
|
|
contentSelector: "section.sheet-body",
|
|
initial: "description",
|
|
},
|
|
],
|
|
dragDrop: [{ dragSelector: "[data-drag]", dropSelector: null }],
|
|
actions: {
|
|
editImage: MournbladeCYD2ItemSheetV2.#onEditImage,
|
|
postItem: MournbladeCYD2ItemSheetV2.#onPostItem,
|
|
addPredilection: MournbladeCYD2ItemSheetV2.#onAddPredilection,
|
|
deletePredilection: MournbladeCYD2ItemSheetV2.#onDeletePredilection,
|
|
addAutomation: MournbladeCYD2ItemSheetV2.#onAddAutomation,
|
|
deleteAutomation: MournbladeCYD2ItemSheetV2.#onDeleteAutomation,
|
|
// Actions pour les ActiveEffects
|
|
createEffect: MournbladeCYD2ItemSheetV2.#onCreateEffect,
|
|
editEffect: MournbladeCYD2ItemSheetV2.#onEditEffect,
|
|
deleteEffect: MournbladeCYD2ItemSheetV2.#onDeleteEffect,
|
|
toggleEffect: MournbladeCYD2ItemSheetV2.#onToggleEffect,
|
|
applyEffect: MournbladeCYD2ItemSheetV2.#onApplyEffect,
|
|
},
|
|
};
|
|
|
|
tabGroups = { primary: "description" };
|
|
|
|
/** @override */
|
|
async _prepareContext() {
|
|
return {
|
|
fields: this.document.schema.fields,
|
|
systemFields: this.document.system.schema.fields,
|
|
item: this.document,
|
|
system: this.document.system,
|
|
source: this.document.toObject(),
|
|
config: game.system.mournbladecyd2.config,
|
|
enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
|
this.document.system.description || "", { async: true }
|
|
),
|
|
isEditMode: true,
|
|
isEditable: this.isEditable,
|
|
isGM: game.user.isGM,
|
|
config: game.system.mournbladecyd2.config,
|
|
};
|
|
}
|
|
|
|
/** @override */
|
|
_onRender(context, options) {
|
|
super._onRender(context, options);
|
|
this.#dragDrop.forEach((d) => d.bind(this.element));
|
|
|
|
// Tab navigation
|
|
const nav = this.element.querySelector('nav.tabs[data-group]');
|
|
if (nav) {
|
|
const group = nav.dataset.group;
|
|
const activeTab = this.tabGroups[group] || "description";
|
|
nav.querySelectorAll('[data-tab]').forEach(link => {
|
|
const tab = link.dataset.tab;
|
|
link.classList.toggle('active', tab === activeTab);
|
|
link.addEventListener('click', (event) => {
|
|
event.preventDefault();
|
|
this.tabGroups[group] = tab;
|
|
this.render();
|
|
});
|
|
});
|
|
this.element.querySelectorAll(`[data-group="${group}"][data-tab]`).forEach(content => {
|
|
content.classList.toggle('active', content.dataset.tab === activeTab);
|
|
});
|
|
}
|
|
}
|
|
|
|
#createDragDropHandlers() {
|
|
return this.options.dragDrop.map(d => {
|
|
d.permissions = { dragstart: () => this.isEditable };
|
|
d.callbacks = { dragstart: this._onDragStart.bind(this) };
|
|
return new foundry.applications.ux.DragDrop.implementation(d);
|
|
});
|
|
}
|
|
|
|
_onDragStart(event) {}
|
|
|
|
// #region Actions
|
|
|
|
static async #onEditImage(event) {
|
|
const fp = new FilePicker({
|
|
type: "image",
|
|
current: this.document.img,
|
|
callback: (path) => this.document.update({ img: path })
|
|
});
|
|
fp.browse();
|
|
}
|
|
|
|
static async #onPostItem(event) {
|
|
event.preventDefault();
|
|
const item = this.document;
|
|
const chatData = foundry.utils.duplicate(item);
|
|
if (item.actor) {
|
|
chatData.actor = { id: item.actor.id };
|
|
}
|
|
if (chatData.img?.includes("/blank.png")) {
|
|
chatData.img = null;
|
|
}
|
|
chatData.jsondata = JSON.stringify({
|
|
compendium: "postedItem",
|
|
payload: chatData,
|
|
});
|
|
const html = await foundry.applications.handlebars.renderTemplate(
|
|
"systems/fvtt-mournblade-cyd-2-0/templates/post-item.hbs",
|
|
chatData
|
|
);
|
|
ChatMessage.create({ user: game.user.id, content: html });
|
|
}
|
|
|
|
static async #onAddPredilection(event) {
|
|
const preds = foundry.utils.duplicate(this.document.system.predilections || []);
|
|
preds.push({ id: foundry.utils.randomID(), name: "Nouvelle prédilection", description: "", acquise: false, maitrise: false, used: false });
|
|
await this.document.update({ "system.predilections": preds });
|
|
}
|
|
|
|
static async #onDeletePredilection(event, target) {
|
|
const idx = Number(target.dataset.predilectionIndex);
|
|
const preds = foundry.utils.duplicate(this.document.system.predilections || []);
|
|
preds.splice(idx, 1);
|
|
await this.document.update({ "system.predilections": preds });
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async #onAddAutomation(event) {
|
|
const automations = foundry.utils.duplicate(this.document.system.automations || []);
|
|
automations.push({
|
|
id: foundry.utils.randomID(),
|
|
eventtype: "on-drop",
|
|
name: "",
|
|
bonusname: "vigueur",
|
|
bonus: 0,
|
|
competence: "",
|
|
minLevel: 0,
|
|
baCost: 0
|
|
});
|
|
await this.document.update({
|
|
"system.automations": automations,
|
|
"system.isautomated": true
|
|
});
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
static async #onDeleteAutomation(event, target) {
|
|
const idx = Number(target.dataset.automationIndex);
|
|
const automations = foundry.utils.duplicate(this.document.system.automations || []);
|
|
automations.splice(idx, 1);
|
|
await this.document.update({ "system.automations": automations });
|
|
if (automations.length === 0) {
|
|
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
|
|
}
|