/** * Donjon & Cie - Systeme FoundryVTT * * Donjon & Cie est un jeu de role edite par John Doe. * Ce systeme FoundryVTT est une implementation independante et n'est pas * affilie a John Doe. * * @author LeRatierBretonnien * @copyright 2025–2026 LeRatierBretonnien * @license CC BY-NC-SA 4.0 – https://creativecommons.org/licenses/by-nc-sa/4.0/ */ const { HandlebarsApplicationMixin } = foundry.applications.api; export default class DonjonEtCieActorSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) { static DEFAULT_OPTIONS = { classes: ["fvtt-donjon-et-cie", "sheet", "actor"], position: { width: 920, height: 820 }, form: { submitOnChange: true, closeOnSubmit: false }, window: { resizable: true }, dragDrop: [{ dragSelector: ".item-list .item", dropSelector: ".item-dropzone" }], actions: { editImage: DonjonEtCieActorSheet.#onEditImage, setTab: DonjonEtCieActorSheet.#onSetTab, createItem: DonjonEtCieActorSheet.#onCreateItem, editItem: DonjonEtCieActorSheet.#onEditItem, deleteItem: DonjonEtCieActorSheet.#onDeleteItem, rollHitDice: DonjonEtCieActorSheet.#onRollHitDice, rollInitiative: DonjonEtCieActorSheet.#onRollInitiative, rollCharacteristic: DonjonEtCieActorSheet.#onRollCharacteristic, rollWeapon: DonjonEtCieActorSheet.#onRollWeapon, rollDamage: DonjonEtCieActorSheet.#onRollDamage, rollSpell: DonjonEtCieActorSheet.#onRollSpell, rollUsage: DonjonEtCieActorSheet.#onRollUsage, useFavorService: DonjonEtCieActorSheet.#onUseFavorService, postItem: DonjonEtCieActorSheet.#onPostItem, adjustCounter: DonjonEtCieActorSheet.#onAdjustCounter } }; async _prepareContext() { const actor = this.document; return { actor, system: actor.system, source: actor.toObject(), config: game.system.donjonEtCie.config, characteristics: actor.getCharacteristicEntries(), sections: actor.getSectionData(), fields: actor.schema.fields, systemFields: actor.system.schema.fields, activeTab: this._activeTab ?? "combat" }; } _onRender(context, options) { super._onRender(context, options); this.#fixWindowShell(); this._applyActiveTab(); } #fixWindowShell() { const app = this.element?.matches?.(".application") ? this.element : this.element?.closest(".application"); const content = app?.querySelector(":scope > .window-content") ?? app?.querySelector(".window-content"); const header = app?.querySelector(".window-header"); if (app) { app.style.display = "flex"; app.style.flexDirection = "column"; app.style.paddingTop = "0"; app.style.overflow = "hidden"; } if (header) { header.style.width = "100%"; header.style.flex = "0 0 auto"; header.style.position = "relative"; header.style.zIndex = "3"; } if (content) { content.style.width = "100%"; content.style.flex = "1 1 auto"; content.style.minHeight = "0"; content.style.overflowY = "auto"; content.style.overflowX = "hidden"; } } _canDragStart() { return this.isEditable; } _canDragDrop() { return this.isEditable; } _onDragStart(event) { const itemElement = event.currentTarget.closest(".item"); if (!itemElement) return; const itemId = itemElement.dataset.itemId; const item = this.document.items.get(itemId); if (!item) return; event.dataTransfer.setData("text/plain", JSON.stringify({ type: "Item", uuid: item.uuid })); } _onDragOver(event) { const dropTarget = event.target.closest(".item-section"); this.#setDropTarget(dropTarget); } async _onDrop(event) { this.#setDropTarget(null); return super._onDrop(event); } #setDropTarget(target) { this.element.querySelectorAll(".item-section.is-dragover").forEach((section) => section.classList.remove("is-dragover")); if (target instanceof HTMLElement) { target.classList.add("is-dragover"); } } _applyActiveTab() { const activeTab = this._activeTab ?? "combat"; this.element.querySelectorAll("[data-tab-button]").forEach((button) => { const isActive = button.dataset.tab === activeTab; button.classList.toggle("active", isActive); button.setAttribute("aria-pressed", isActive ? "true" : "false"); }); this.element.querySelectorAll("[data-tab-panel]").forEach((panel) => { const isActive = panel.dataset.tabPanel === activeTab; panel.classList.toggle("active", isActive); panel.toggleAttribute("hidden", !isActive); }); } static async #onEditImage(event) { event.preventDefault(); const picker = new FilePicker({ type: "image", current: this.document.img, callback: (path) => this.document.update({ img: path }) }); return picker.browse(); } static async #onCreateItem(event, target) { event.preventDefault(); const type = target.dataset.type; if (!type) return; return this.document.createEmbeddedDocuments("Item", [{ name: `Nouveau ${type}`, type }], { renderSheet: true }); } static async #onSetTab(event, target) { event.preventDefault(); const tab = target.dataset.tab; if (!tab) return; this._activeTab = tab; this._applyActiveTab(); } static async #onEditItem(event, target) { event.preventDefault(); const item = this.document.items.get(target.closest("[data-item-id]")?.dataset.itemId); return item?.sheet.render(true); } static async #onDeleteItem(event, target) { event.preventDefault(); const itemId = target.closest("[data-item-id]")?.dataset.itemId; if (!itemId) return; return this.document.deleteEmbeddedDocuments("Item", [itemId]); } static async #onRollCharacteristic(event, target) { event.preventDefault(); return this.document.rollCharacteristic(target.dataset.characteristic); } static async #onRollInitiative(event) { event.preventDefault(); return this.document.rollInitiative(); } static async #onRollHitDice(event) { event.preventDefault(); return this.document.rollHitDice(); } static async #onRollWeapon(event, target) { event.preventDefault(); return this.document.rollWeapon(target.closest("[data-item-id]")?.dataset.itemId); } static async #onRollDamage(event, target) { event.preventDefault(); return this.document.rollDamage(target.closest("[data-item-id]")?.dataset.itemId); } static async #onRollSpell(event, target) { event.preventDefault(); return this.document.rollSpell(target.closest("[data-item-id]")?.dataset.itemId); } static async #onRollUsage(event, target) { event.preventDefault(); return this.document.rollUsage(target.closest("[data-item-id]")?.dataset.itemId); } static async #onUseFavorService(event, target) { event.preventDefault(); return this.document.useFavorService(target.dataset.department); } static async #onPostItem(event, target) { event.preventDefault(); const item = this.document.items.get(target.closest("[data-item-id]")?.dataset.itemId); return item?.postToChat(); } static async #onAdjustCounter(event, target) { event.preventDefault(); const path = target.dataset.path; const delta = Number(target.dataset.delta ?? 0); if (!path || Number.isNaN(delta)) return; return this.document.adjustNumericField(path, delta); } }