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 }