import OathHammerActorSheet from "./base-actor-sheet.mjs" const ALLOWED_LOOT_TYPES = new Set(["weapon", "armor", "ammunition", "equipment", "magic-item"]) export default class OathHammerPartySheet extends OathHammerActorSheet { /** @override */ static DEFAULT_OPTIONS = { classes: ["party"], position: { width: 780, height: 600 }, window: { contentClasses: ["party-content"] }, actions: { openMember: OathHammerPartySheet.#onOpenMember, removeMember: OathHammerPartySheet.#onRemoveMember, moveMemberUp: OathHammerPartySheet.#onMoveMemberUp, moveMemberDown: OathHammerPartySheet.#onMoveMemberDown, adjustCurrency: OathHammerPartySheet.#onAdjustCurrency, adjustQty: OathHammerPartySheet.#onAdjustQty, }, } /** @override */ static PARTS = { main: { template: "systems/fvtt-oath-hammer/templates/actor/party-sheet.hbs" }, tabs: { template: "templates/generic/tab-navigation.hbs" }, members: { template: "systems/fvtt-oath-hammer/templates/actor/party-members.hbs" }, loot: { template: "systems/fvtt-oath-hammer/templates/actor/party-loot.hbs" }, notes: { template: "systems/fvtt-oath-hammer/templates/actor/party-notes.hbs" }, } tabGroups = { sheet: "members" } #getTabs() { const tabs = { members: { id: "members", group: "sheet", icon: "fa-solid fa-users", label: "OATHHAMMER.Tab.Members" }, loot: { id: "loot", group: "sheet", icon: "fa-solid fa-treasure-chest", label: "OATHHAMMER.Tab.Loot" }, notes: { id: "notes", group: "sheet", icon: "fa-solid fa-book", label: "OATHHAMMER.Tab.Notes" }, } for (const v of Object.values(tabs)) { v.active = this.tabGroups[v.group] === v.id v.cssClass = v.active ? "active" : "" } return tabs } /** @override */ async _prepareContext() { const context = await super._prepareContext() context.tabs = this.#getTabs() return context } /** @override */ async _preparePartContext(partId, context) { const doc = this.document switch (partId) { case "main": break case "members": { context.tab = context.tabs.members const refs = doc.system.memberRefs ?? [] context.members = refs.map((id, idx) => { const actor = game.actors?.get(id) if (!actor) return null const sys = actor.system const classItem = actor.items?.find(i => i.type === "class") return { id: actor.id, name: actor.name, img: actor.img, idx, position: idx + 1, isFirst: idx === 0, isLast: idx === refs.length - 1, classLabel: classItem?.name ?? "—", level: sys.level ?? "—", grit: sys.grit ? `${sys.grit.value}/${sys.grit.max}` : "—", } }).filter(Boolean) break } case "loot": { context.tab = context.tabs.loot const allItems = doc.items.contents.filter(i => ALLOWED_LOOT_TYPES.has(i.type)) context.lootItems = allItems.map(i => ({ id: i.id, uuid: i.uuid, img: i.img, name: i.name, type: i.type, typeLabel: game.i18n.localize(`TYPES.Item.${i.type}`), system: i.system, })) break } case "notes": context.tab = context.tabs.notes context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML( doc.system.notes ?? "", { async: true } ) break } return context } /** @override */ async _onDrop(event) { if (!this.isEditable || !this.isEditMode) return const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event) if (data.type === "Actor") { const actor = await fromUuid(data.uuid) if (!actor || actor.type !== "character") return const refs = foundry.utils.deepClone(this.document.system.memberRefs ?? []) if (refs.includes(actor.id)) return refs.push(actor.id) return this.document.update({ "system.memberRefs": refs }) } if (data.type === "Item") { const item = await fromUuid(data.uuid) if (!item || !ALLOWED_LOOT_TYPES.has(item.type)) return return this._onDropItem(item) } } // ── Actions ───────────────────────────────────────────────────────────────── static async #onOpenMember(event, target) { const actor = game.actors?.get(target.dataset.actorId) if (actor) actor.sheet.render(true) } static async #onRemoveMember(event, target) { const id = target.dataset.actorId const refs = (this.document.system.memberRefs ?? []).filter(r => r !== id) await this.document.update({ "system.memberRefs": refs }) } static async #onMoveMemberUp(event, target) { const idx = parseInt(target.dataset.idx, 10) if (idx <= 0) return const refs = foundry.utils.deepClone(this.document.system.memberRefs ?? []); [refs[idx - 1], refs[idx]] = [refs[idx], refs[idx - 1]] await this.document.update({ "system.memberRefs": refs }) } static async #onMoveMemberDown(event, target) { const idx = parseInt(target.dataset.idx, 10) const refs = foundry.utils.deepClone(this.document.system.memberRefs ?? []) if (idx >= refs.length - 1) return; [refs[idx], refs[idx + 1]] = [refs[idx + 1], refs[idx]] await this.document.update({ "system.memberRefs": refs }) } static async #onAdjustCurrency(event, target) { const field = target.dataset.field const delta = parseInt(target.dataset.delta, 10) const cur = foundry.utils.getProperty(this.document, field) ?? 0 await this.document.update({ [field]: Math.max(0, cur + delta) }) } static async #onAdjustQty(event, target) { const item = this.document.items.get(target.dataset.itemId) const delta = parseInt(target.dataset.delta, 10) if (!item) return const qty = (item.system.quantity ?? 1) + delta if (qty <= 0) return item.delete() await item.update({ "system.quantity": qty }) } }