import MGNEActorSheet from "./base-actor-sheet.mjs" const SYSTEM_ID = "fvtt-machine-gods-noxian-expanse" const PARTY_LOOT_TYPES = new Set(["weapon", "armor", "shield", "equipment", "resonance-core", "artifact"]) export default class MGNEPartySheet extends MGNEActorSheet { /** @override */ static DEFAULT_OPTIONS = { classes: ["party"], position: { width: 820, height: 640 }, actions: { openMember: MGNEPartySheet.#onOpenMember, removeMember: MGNEPartySheet.#onRemoveMember, moveMemberUp: MGNEPartySheet.#onMoveMemberUp, moveMemberDown: MGNEPartySheet.#onMoveMemberDown, adjustCredits: MGNEPartySheet.#onAdjustCredits, }, } /** @override */ static PARTS = { main: { template: `systems/${SYSTEM_ID}/templates/party-main.hbs` }, tabs: { template: `systems/${SYSTEM_ID}/templates/party-tabs.hbs` }, members: { template: `systems/${SYSTEM_ID}/templates/party-members.hbs` }, loot: { template: `systems/${SYSTEM_ID}/templates/party-loot.hbs` }, notes: { template: `systems/${SYSTEM_ID}/templates/party-notes.hbs` }, } tabGroups = { sheet: "members" } #getTabs() { const tabs = { members: { id: "members", group: "sheet", label: game.i18n.localize("MGNE.Tabs.members") }, loot: { id: "loot", group: "sheet", label: game.i18n.localize("MGNE.Tabs.loot") }, notes: { id: "notes", group: "sheet", label: game.i18n.localize("MGNE.Tabs.notes") }, } for (const tab of Object.values(tabs)) { tab.active = this.tabGroups[tab.group] === tab.id tab.cssClass = tab.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 "members": { context.tab = context.tabs.members // Build member list using actorId for moves; store refIdx (position in // memberRefs) so move actions always operate on the correct slot even // when some refs point to deleted actors. const refs = doc.system.memberRefs ?? [] const members = [] for (let refIdx = 0; refIdx < refs.length; refIdx++) { const actor = game.actors?.get(refs[refIdx].id) if (!actor) continue members.push({ id: actor.id, refIdx, name: actor.name, img: actor.img, type: actor.type, typeLabel: game.i18n.localize(`TYPES.Actor.${actor.type}`), hp: actor.system.hp ? `${actor.system.hp.value ?? "—"}/${actor.system.hp.max ?? "—"}` : "—", }) } // isFirst/isLast based on visible list, but swap uses refIdx for (let vi = 0; vi < members.length; vi++) { members[vi].isFirst = vi === 0 members[vi].isLast = vi === members.length - 1 } context.members = members break } case "loot": { context.tab = context.tabs.loot context.lootItems = doc.items.contents .filter(i => PARTY_LOOT_TYPES.has(i.type)) .map(i => ({ id: i.id, img: i.img, name: i.name, typeLabel: game.i18n.localize(`TYPES.Item.${i.type}`), })) break } case "notes": context.tab = context.tabs.notes break } return context } /** @override */ async _onDrop(event) { if (!this.isEditable) return const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event) if (data.type === "Actor") { const actor = await fromUuid(data.uuid) if (!actor || !["character", "companion"].includes(actor.type)) return const refs = foundry.utils.deepClone(this.document.system.memberRefs ?? []) if (refs.some(r => r.id === actor.id)) return refs.push({ id: actor.id }) return this.document.update({ "system.memberRefs": refs }) } if (data.type === "Item") { const item = await fromUuid(data.uuid) if (!item || !PARTY_LOOT_TYPES.has(item.type)) return return this.document.createEmbeddedDocuments("Item", [item.toObject()]) } } // ── 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 !== id) await this.document.update({ "system.memberRefs": refs }) } static async #onMoveMemberUp(_event, target) { const refIdx = parseInt(target.dataset.refIdx, 10) if (refIdx <= 0) return const refs = foundry.utils.deepClone(this.document.system.memberRefs ?? []); [refs[refIdx - 1], refs[refIdx]] = [refs[refIdx], refs[refIdx - 1]] await this.document.update({ "system.memberRefs": refs }) } static async #onMoveMemberDown(_event, target) { const refIdx = parseInt(target.dataset.refIdx, 10) const refs = foundry.utils.deepClone(this.document.system.memberRefs ?? []) if (refIdx >= refs.length - 1) return; [refs[refIdx], refs[refIdx + 1]] = [refs[refIdx + 1], refs[refIdx]] await this.document.update({ "system.memberRefs": refs }) } static async #onAdjustCredits(_event, target) { const delta = parseInt(target.dataset.delta, 10) const current = this.document.system.credits ?? 0 await this.document.update({ "system.credits": Math.max(0, current + delta) }) } }