const { HandlebarsApplicationMixin } = foundry.applications.api import { WastelandUtility } from "../../wasteland-utility.js" export default class WastelandActorSheet 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 } #dragDrop /** @override */ static DEFAULT_OPTIONS = { classes: ["fvtt-wasteland", "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: WastelandActorSheet.#onEditImage, toggleSheet: WastelandActorSheet.#onToggleSheet, editItem: WastelandActorSheet.#onEditItem, deleteItem: WastelandActorSheet.#onDeleteItem, createItem: WastelandActorSheet.#onCreateItem, equipItem: WastelandActorSheet.#onEquipItem, modifyQuantity: WastelandActorSheet.#onModifyQuantity, incDecSante: WastelandActorSheet.#onIncDecSante, rollAttribut: WastelandActorSheet.#onRollAttribut, rollCompetence: WastelandActorSheet.#onRollCompetence, rollCharme: WastelandActorSheet.#onRollCharme, rollPouvoir: WastelandActorSheet.#onRollPouvoir, rollArmeOffensif: WastelandActorSheet.#onRollArmeOffensif, rollArmeDegats: WastelandActorSheet.#onRollArmeDegats, resetPredilections: WastelandActorSheet.#onResetPredilections, rollAssommer: WastelandActorSheet.#onRollAssommer, rollFuir: WastelandActorSheet.#onRollFuir, rollImmobiliser: WastelandActorSheet.#onRollImmobiliser, }, } /** * 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.wasteland.config, enrichedDescription: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.description || "", { async: true }), enrichedComportement: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.comportement || "", { async: true }), enrichedHabitat: await foundry.applications.ux.TextEditor.implementation.enrichHTML(actor.system.biodata?.habitat || "", { 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 /** * 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 WastelandUtility.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 await this.actor.equipItem(itemId) } /** * Handle modifying item quantity * @param {Event} event - The triggering event * @param {HTMLElement} target - The target element */ static async #onModifyQuantity(event, target) { const li = target.closest(".item") const itemId = li?.dataset.itemId if (!itemId) return const item = this.actor.items.get(itemId) if (!item) return const qty = parseInt(target.dataset.qty) || 0 const currentQty = item.system.quantite || 0 const newQty = Math.max(0, currentQty + qty) await item.update({ 'system.quantite': newQty }) } /** * Handle modifying santé/psyché * @param {Event} event - The triggering event * @param {HTMLElement} target - The target element */ static async #onIncDecSante(event, target) { const field = target.dataset.field const value = parseInt(target.dataset.value) || 0 if (field === 'psyche') { await this.actor.update({ 'system.psyche.value': this.actor.system.psyche.value + value }) } else if (field === 'nonletaux') { await this.actor.update({ 'system.sante.nonletaux': this.actor.system.sante.nonletaux + value }) } else if (field === 'letaux') { await this.actor.update({ 'system.sante.letaux': this.actor.system.sante.letaux + value }) } } /** * Handle rolling an attribute * @param {Event} event - The triggering event * @param {HTMLElement} target - The target element */ static async #onRollAttribut(event, target) { const attrKey = target.dataset.attrKey await this.actor.rollAttribut(attrKey) } /** * Handle rolling a competence * @param {Event} event - The triggering event * @param {HTMLElement} target - The target element */ static async #onRollCompetence(event, target) { const li = target.closest(".item") const itemId = li?.dataset.itemId const attrKey = target.dataset.attrKey if (!itemId) return await this.actor.rollCompetence(attrKey, itemId) } /** * Handle rolling a charme * @param {Event} event - The triggering event * @param {HTMLElement} target - The target element */ static async #onRollCharme(event, target) { const li = target.closest(".item") const itemId = li?.dataset.itemId if (!itemId) return await this.actor.rollCharme(itemId) } /** * Handle rolling a pouvoir * @param {Event} event - The triggering event * @param {HTMLElement} target - The target element */ static async #onRollPouvoir(event, target) { const li = target.closest(".item") const itemId = li?.dataset.itemId if (!itemId) return await this.actor.rollPouvoir(itemId) } /** * Handle rolling weapon attack * @param {Event} event - The triggering event * @param {HTMLElement} target - The target element */ static async #onRollArmeOffensif(event, target) { const li = target.closest(".item") const itemId = li?.dataset.itemId if (!itemId) return await this.actor.rollArmeOffensif(itemId) } /** * Handle rolling weapon damage * @param {Event} event - The triggering event * @param {HTMLElement} target - The target element */ static async #onRollArmeDegats(event, target) { const li = target.closest(".item") const itemId = li?.dataset.itemId if (!itemId) return await this.actor.rollArmeDegats(itemId) } /** * Handle resetting all predilections * @param {Event} event - The originating click event * @param {HTMLElement} target - The target element */ static async #onResetPredilections(event, target) { await this.actor.resetAllPredilections() } /** * Handle Assommer roll * @param {Event} event - The originating click event * @param {HTMLElement} target - The target element */ static async #onRollAssommer(event, target) { await this.actor.rollAssommer() } /** * Handle Fuir roll * @param {Event} event - The originating click event * @param {HTMLElement} target - The target element */ static async #onRollFuir(event, target) { await this.actor.rollFuir() } /** * Handle Immobiliser roll * @param {Event} event - The originating click event * @param {HTMLElement} target - The target element */ static async #onRollImmobiliser(event, target) { await this.actor.rollImmobiliser() } }