const { HandlebarsApplicationMixin } = foundry.applications.api; import { MournbladeCYD2Utility } from "../../mournblade-cyd2-utility.js"; export default class MournbladeCYD2ActorSheetV2 extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) { static SHEET_MODES = { EDIT: 0, PLAY: 1 }; constructor(options = {}) { super(options); this.#dragDrop = this.#createDragDropHandlers(); this._sheetMode = this.constructor.SHEET_MODES.PLAY; } #dragDrop; /** @override */ static DEFAULT_OPTIONS = { classes: ["fvtt-mournblade-cyd-2-0", "sheet", "actor"], position: { width: 750, height: 820, }, form: { submitOnChange: true, closeOnSubmit: false, }, window: { resizable: true, }, tabs: [ { navSelector: 'nav[data-group="primary"]', contentSelector: "section.sheet-body", initial: "stats", }, ], dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }], actions: { editImage: MournbladeCYD2ActorSheetV2.#onEditImage, toggleSheet: MournbladeCYD2ActorSheetV2.#onToggleSheet, editItem: MournbladeCYD2ActorSheetV2.#onEditItem, deleteItem: MournbladeCYD2ActorSheetV2.#onDeleteItem, createItem: MournbladeCYD2ActorSheetV2.#onCreateItem, equipItem: MournbladeCYD2ActorSheetV2.#onEquipItem, modifyQuantity: MournbladeCYD2ActorSheetV2.#onModifyQuantity, modifyAdversite: MournbladeCYD2ActorSheetV2.#onModifyAdversite, modifySante: MournbladeCYD2ActorSheetV2.#onModifySante, modifyAme: MournbladeCYD2ActorSheetV2.#onModifyAme, rollAttribut: MournbladeCYD2ActorSheetV2.#onRollAttribut, rollCompetence: MournbladeCYD2ActorSheetV2.#onRollCompetence, rollRune: MournbladeCYD2ActorSheetV2.#onRollRune, rollArmeOffensif: MournbladeCYD2ActorSheetV2.#onRollArmeOffensif, rollArmeSpecial: MournbladeCYD2ActorSheetV2.#onRollArmeSpecial, rollArmeDegats: MournbladeCYD2ActorSheetV2.#onRollArmeDegats, rollAssommer: MournbladeCYD2ActorSheetV2.#onRollAssommer, rollCoupBas: MournbladeCYD2ActorSheetV2.#onRollCoupBas, rollImmobiliser: MournbladeCYD2ActorSheetV2.#onRollImmobiliser, rollRepousser: MournbladeCYD2ActorSheetV2.#onRollRepousser, rollDesengager: MournbladeCYD2ActorSheetV2.#onRollDesengager, rollInitiative: MournbladeCYD2ActorSheetV2.#onRollInitiative, rollFuir: MournbladeCYD2ActorSheetV2.#onRollFuir, }, }; get isPlayMode() { if (this._sheetMode === undefined) this._sheetMode = this.constructor.SHEET_MODES.PLAY; return this._sheetMode === this.constructor.SHEET_MODES.PLAY; } get isEditMode() { if (this._sheetMode === undefined) this._sheetMode = this.constructor.SHEET_MODES.PLAY; return this._sheetMode === this.constructor.SHEET_MODES.EDIT; } tabGroups = { primary: "stats" }; /** @override */ async _prepareContext() { const actor = this.document; return { actor, system: actor.system, source: actor.toObject(), fields: actor.schema.fields, systemFields: actor.system.schema.fields, isEditable: this.isEditable, isEditMode: this.isEditMode, isPlayMode: this.isPlayMode, isGM: game.user.isGM, config: game.system.mournbladecyd2.config, enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML( actor.system.biodata?.description || "", { async: true } ), enrichedHabitat: await foundry.applications.ux.TextEditor.implementation.enrichHTML( actor.system.biodata?.habitat || "", { async: true } ), }; } /** @override */ _onRender(context, options) { super._onRender(context, options); this.#dragDrop.forEach((d) => d.bind(this.element)); this.element.querySelectorAll('.edit-item-data').forEach(element => { element.addEventListener('change', async (event) => { const target = event.currentTarget; const itemElement = target.closest('[data-item-id]'); if (!itemElement) return; const itemId = itemElement.dataset.itemId; const itemField = target.dataset.itemField; const dataType = target.dataset.dtype; const value = dataType === "Number" ? Number(target.value) : target.value; const item = this.document.items.get(itemId); if (item) await item.update({ [itemField]: value }); }); }); // Tab navigation const nav = this.element.querySelector('nav.tabs[data-group]'); if (nav) { const group = nav.dataset.group; const activeTab = this.tabGroups[group] || "stats"; 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._canDragStart.bind(this), drop: this._canDragDrop.bind(this) }; d.callbacks = { dragstart: this._onDragStart.bind(this), dragover: this._onDragOver.bind(this), drop: this._onDrop.bind(this) }; return new foundry.applications.ux.DragDrop.implementation(d); }); } _canDragStart(selector) { return this.isEditable; } _canDragDrop(selector) { return this.isEditable; } _onDragStart(event) { const li = event.currentTarget.closest("[data-item-id]"); if (!li) return; const item = this.document.items.get(li.dataset.itemId); if (!item) return; event.dataTransfer.setData("text/plain", JSON.stringify(item.toDragData())); } _onDragOver(event) { event.preventDefault(); } async _onDrop(event) { const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); if (data?.type === "Item") return this._onDropItem(event, data); if (data?.type === "Actor") return this._onDropActor(event, data); } async _onDropItem(event, data) { if (!this.document.isOwner) return; const item = await Item.fromDropData(data); if (!item) return; if (this.document.uuid === item.parent?.uuid) return; return this.document.createEmbeddedDocuments("Item", [item.toObject()]); } async _onDropActor(event, data) {} // #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 #onToggleSheet(event) { this._sheetMode = this.isEditMode ? this.constructor.SHEET_MODES.PLAY : this.constructor.SHEET_MODES.EDIT; this.render(); } static async #onEditItem(event, target) { const li = target.closest(".item"); const item = this.document.items.get(li?.dataset.itemId); item?.sheet.render(true); } static async #onDeleteItem(event, target) { const li = target.closest(".item"); await MournbladeCYD2Utility.confirmDelete(this, li); } static async #onCreateItem(event, target) { const itemType = target.dataset.type; await this.document.createEmbeddedDocuments("Item", [{ name: `Nouveau ${itemType}`, type: itemType }], { renderSheet: true }); } static async #onEquipItem(event, target) { const li = target.closest(".item"); const item = this.document.items.get(li?.dataset.itemId); if (item) await item.update({ "system.equipped": !item.system.equipped }); } static async #onModifyQuantity(event, target) { const li = target.closest('[data-item-id]'); const item = this.document.items.get(li?.dataset.itemId); const value = Number.parseInt(target.dataset.quantiteValue); if (item) { const newQty = Math.max(0, (item.system.quantite || 0) + value); await item.update({ "system.quantite": newQty }); } } static async #onModifyAdversite(event, target) { const li = target.closest('[data-adversite]'); const adversiteKey = li?.dataset.adversite; if (!adversiteKey) return; const value = Number.parseInt(target.dataset.adversiteValue); const current = this.document.system.adversite[adversiteKey] || 0; await this.document.update({ [`system.adversite.${adversiteKey}`]: Math.max(0, current + value) }); } static async #onModifySante(event, target) { const type = target.dataset.type; const value = Number.parseInt(target.dataset.value); const current = this.document.system.sante[type] || 0; await this.document.update({ [`system.sante.${type}`]: Math.max(0, current + value) }); } static async #onModifyAme(event, target) { const value = Number.parseInt(target.dataset.value); const current = this.document.system.ame.nbame || 0; await this.document.update({ "system.ame.nbame": Math.max(0, current + value) }); } static async #onRollAttribut(event, target) { await this.document.rollAttribut(target.dataset.attrKey); } static async #onRollCompetence(event, target) { const li = target.closest('[data-item-id]'); await this.document.rollCompetence(target.dataset.attrKey, li?.dataset.itemId); } static async #onRollRune(event, target) { const li = target.closest('[data-item-id]'); await this.document.rollRune(li?.dataset.itemId); } static async #onRollArmeOffensif(event, target) { const li = target.closest('[data-item-id]'); await this.document.rollArmeOffensif(target.dataset.armeId ?? li?.dataset.itemId); } static async #onRollArmeSpecial(event, target) { const li = target.closest('[data-item-id]'); await this.document.rollArmeSpecial(target.dataset.armeId ?? li?.dataset.itemId); } static async #onRollArmeDegats(event, target) { const li = target.closest('[data-item-id]'); await this.document.rollArmeDegats(target.dataset.armeId ?? li?.dataset.itemId); } static async #onRollAssommer(event, target) { await this.document.rollAssomer(); } static async #onRollCoupBas(event, target) { await this.document.rollCoupBas(); } static async #onRollImmobiliser(event, target) { await this.document.rollImmobiliser(); } static async #onRollRepousser(event, target) { await this.document.rollRepousser(); } static async #onRollDesengager(event, target) { await this.document.rollDesengager(); } static async #onRollInitiative(event, target) { await this.document.rollAttribut("adr", true); } static async #onRollFuir(event, target) { await this.document.rollFuir(); } }