const { HandlebarsApplicationMixin } = foundry.applications.api import { MournbladeUtility } from "../../mournblade-utility.js" export default class MournbladeActorSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) { /** * Different sheet modes. * @enum {number} */ static SHEET_MODES = { EDIT: 0, PLAY: 1 } constructor(options = {}) { super(options) this.#dragDrop = this.#createDragDropHandlers() this._sheetMode = this.constructor.SHEET_MODES.PLAY // Commencer en mode visualisation } #dragDrop /** @override */ static DEFAULT_OPTIONS = { classes: ["fvtt-mournblade", "sheet", "actor"], position: { width: 650, height: 720, }, 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: MournbladeActorSheet.#onEditImage, toggleSheet: MournbladeActorSheet.#onToggleSheet, editItem: MournbladeActorSheet.#onEditItem, deleteItem: MournbladeActorSheet.#onDeleteItem, createItem: MournbladeActorSheet.#onCreateItem, equipItem: MournbladeActorSheet.#onEquipItem, modifyQuantity: MournbladeActorSheet.#onModifyQuantity, modifySante: MournbladeActorSheet.#onModifySante, modifyAme: MournbladeActorSheet.#onModifyAme, rollAttribut: MournbladeActorSheet.#onRollAttribut, rollCompetence: MournbladeActorSheet.#onRollCompetence, rollRune: MournbladeActorSheet.#onRollRune, rollArmeOffensif: MournbladeActorSheet.#onRollArmeOffensif, rollArmeSpecial: MournbladeActorSheet.#onRollArmeSpecial, rollArmeDegats: MournbladeActorSheet.#onRollArmeDegats, rollAssommer: MournbladeActorSheet.#onRollAssommer, rollImmobiliser: MournbladeActorSheet.#onRollImmobiliser, rollFuir: MournbladeActorSheet.#onRollFuir, }, } /** * Is the sheet currently in 'Play' mode? * @type {boolean} */ get isPlayMode() { if (this._sheetMode === undefined) this._sheetMode = this.constructor.SHEET_MODES.PLAY return this._sheetMode === this.constructor.SHEET_MODES.PLAY } /** * Is the sheet currently in 'Edit' mode? * @type {boolean} */ get isEditMode() { if (this._sheetMode === undefined) this._sheetMode = this.constructor.SHEET_MODES.PLAY return this._sheetMode === this.constructor.SHEET_MODES.EDIT } /** * Tab groups state * @type {object} */ tabGroups = { primary: "stats" } /** @override */ async _prepareContext() { const actor = this.document const context = { actor: 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.mournblade.config, enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.description || "", { async: true }), } return context } /** @override */ _onRender(context, options) { super._onRender(context, options) this.#dragDrop.forEach((d) => d.bind(this.element)) // Handle edit-item-data changes 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 itemType = itemElement.dataset.itemType const itemField = target.dataset.itemField const dataType = target.dataset.dtype const value = target.value await this.document.editItemField(itemId, itemType, itemField, dataType, value) }) }) // Activate tab navigation manually const nav = this.element.querySelector('nav.tabs[data-group]') if (nav) { const group = nav.dataset.group // Activate the current tab 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() }) }) // Show/hide tab content this.element.querySelectorAll('[data-group="' + group + '"][data-tab]').forEach(content => { content.classList.toggle('active', content.dataset.tab === activeTab) }) } } // #region Drag-and-Drop Workflow /** * Create drag-and-drop workflow handlers for this Application */ #createDragDropHandlers() { return [] } // #region Actions /** @override */ static ACTIONS = { editImage: MournbladeActorSheet.#onEditImage, toggleSheet: MournbladeActorSheet.#onToggleSheet, editItem: MournbladeActorSheet.#onEditItem, deleteItem: MournbladeActorSheet.#onDeleteItem, createItem: MournbladeActorSheet.#onCreateItem, equipItem: MournbladeActorSheet.#onEquipItem, modifyQuantity: MournbladeActorSheet.#onModifyQuantity, rollAttribut: MournbladeActorSheet.#onRollAttribut, rollCompetence: MournbladeActorSheet.#onRollCompetence, rollArmeOffensif: MournbladeActorSheet.#onRollArmeOffensif, rollArmeDegats: MournbladeActorSheet.#onRollArmeDegats, rollAssommer: MournbladeActorSheet.#onRollAssommer, rollImmobiliser: MournbladeActorSheet.#onRollImmobiliser, rollFuir: MournbladeActorSheet.#onRollFuir, } /** * Handle editing the actor image * @param {Event} event - The triggering event */ static async #onEditImage(event) { event.preventDefault() const sheet = this const filePicker = new FilePicker({ type: "image", current: sheet.document.img, callback: (path) => { sheet.document.update({ img: path }) }, }) filePicker.browse() } /** * Handle toggling the sheet mode * @param {Event} event - The triggering event */ static async #onToggleSheet(event) { event.preventDefault() const sheet = this sheet._sheetMode = sheet._sheetMode === sheet.constructor.SHEET_MODES.PLAY ? sheet.constructor.SHEET_MODES.EDIT : sheet.constructor.SHEET_MODES.PLAY sheet.render() } /** * Handle editing an item * @param {Event} event - The triggering event * @param {HTMLElement} target - The target element */ static async #onEditItem(event, target) { const li = target.closest(".item") const itemId = li?.dataset.itemId if (!itemId) return const item = this.actor.items.get(itemId) if (item) item.sheet.render(true) } /** * Handle deleting an item * @param {Event} event - The triggering event * @param {HTMLElement} target - The target element */ static async #onDeleteItem(event, target) { const li = target.closest(".item") await MournbladeUtility.confirmDelete(this, li) } /** * Handle creating an item * @param {Event} event - The triggering event * @param {HTMLElement} target - The target element */ static async #onCreateItem(event, target) { const itemType = target.dataset.type await this.actor.createEmbeddedDocuments("Item", [{ name: `Nouveau ${itemType}`, type: itemType }], { renderSheet: true }) } /** * Handle equipping an item * @param {Event} event - The triggering event * @param {HTMLElement} target - The target element */ static async #onEquipItem(event, target) { const li = target.closest(".item") const itemId = li?.dataset.itemId if (!itemId) return const item = this.actor.items.get(itemId) if (item) { await item.update({ "system.equipped": !item.system.equipped }) } } /** * Handle modifying the quantity of an item * @param {Event} event - The triggering event * @param {HTMLElement} target - The target element */ static async #onModifyQuantity(event, target) { const li = target.closest('[data-item-id]') const itemId = li?.dataset.itemId const value = Number.parseInt(target.dataset.quantiteValue) const item = this.document.items.get(itemId) if (item) { const newQuantity = Math.max(0, (item.system.quantite || 0) + value) await item.update({ "system.quantite": newQuantity }) } } /** * Handle modifying santé (health) values * @param {Event} event - The triggering event * @param {HTMLElement} target - The target element */ static async #onModifySante(event, target) { const type = target.dataset.type const value = Number.parseInt(target.dataset.value) const actor = this.document const currentValue = actor.system.sante[type] || 0 const newValue = Math.max(0, currentValue + value) await actor.update({ [`system.sante.${type}`]: newValue }) } /** * Handle modifying âme (soul) value * @param {Event} event - The triggering event * @param {HTMLElement} target - The target element */ static async #onModifyAme(event, target) { const value = Number.parseInt(target.dataset.value) const actor = this.document const currentValue = actor.system.ame.value || 0 const newValue = Math.max(0, currentValue + value) await actor.update({ "system.ame.value": newValue }) } /** * Handle rolling an attribut * @param {Event} event - The triggering event */ static async #onRollAttribut(event, target) { event.preventDefault() const sheet = this const attrKey = target.dataset.attrKey const actor = sheet.document await actor.rollAttribut(attrKey) } /** * Handle rolling a competence * @param {Event} event - The triggering event */ static async #onRollCompetence(event, target) { event.preventDefault() const sheet = this const attrKey = target.dataset.attrKey const li = target.closest('[data-item-id]') const compId = li?.dataset.itemId const actor = sheet.document await actor.rollCompetence(attrKey, compId) } /** * Handle rolling a rune * @param {Event} event - The triggering event */ static async #onRollRune(event, target) { event.preventDefault() const sheet = this const li = target.closest('[data-item-id]') const runeId = li?.dataset.itemId const actor = sheet.document await actor.rollRune(runeId) } /** * Handle rolling an arme offensif * @param {Event} event - The triggering event */ static async #onRollArmeOffensif(event, target) { event.preventDefault() const sheet = this const armeId = target.dataset.armeId const actor = sheet.document await actor.rollArmeOffensif(armeId) } /** * Handle rolling an arme degats * @param {Event} event - The triggering event */ static async #onRollArmeDegats(event, target) { event.preventDefault() const sheet = this const armeId = target.dataset.armeId const actor = sheet.document await actor.rollArmeDegats(armeId) } /** * Handle rolling an arme special * @param {Event} event - The triggering event */ static async #onRollArmeSpecial(event, target) { event.preventDefault() const sheet = this const armeId = target.dataset.armeId const actor = sheet.document await actor.rollArmeSpecial(armeId) } /** * Handle rolling an assommer * @param {Event} event - The triggering event */ static async #onRollAssommer(event, target) { event.preventDefault() const sheet = this const actor = sheet.document await actor.rollAssomer() } /** * Handle rolling an immobiliser * @param {Event} event - The triggering event */ static async #onRollImmobiliser(event, target) { event.preventDefault() const sheet = this const actor = sheet.document await actor.rollImmobiliser() } /** * Handle rolling a fuir * @param {Event} event - The triggering event */ static async #onRollFuir(event, target) { event.preventDefault() const sheet = this const actor = sheet.document await actor.rollFuir() } }