foundry-dsa41-game/src/module/sheets/groupSheet.mjs

328 lines
11 KiB
JavaScript

const {HandlebarsApplicationMixin, DocumentSheetV2} = foundry.applications.api
const {ActorSheetV2} = foundry.applications.sheets
export class GroupSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
/** @inheritDoc */
static DEFAULT_OPTIONS = {
position: {width: 520, height: 480},
classes: ['dsa41', 'sheet', 'actor', 'group'],
tag: 'form',
dragDrop: [{
dropSelector: '.tab.inventory.active'
}],
form: {
submitOnChange: true,
closeOnSubmit: false,
handler: GroupSheet.#onSubmitForm
},
window: {
resizable: true,
},
actions: {
roll: GroupSheet.#dieRoll,
editImage: DocumentSheetV2.DEFAULT_OPTIONS.actions.editImage,
openEmbeddedDocument: GroupSheet.#openEmbeddedDocument,
openActorDocument: GroupSheet.#openActorDocument,
}
}
static TABS = {
sheet: {
tabs: [
{id: 'members', group: 'sheet', label: 'Gruppenmitglieder'},
{id: 'inventory', group: 'sheet', label: 'Gruppeninventar'},
],
initial: 'members'
}
}
/** @inheritDoc */
static PARTS = {
form: {
template: `systems/DSA_4-1/templates/actor/group/main-sheet.hbs`
},
members: {
template: `systems/DSA_4-1/templates/actor/group/tab-members.hbs`
},
inventory: {
template: `systems/DSA_4-1/templates/actor/group/tab-inventory.hbs`
},
settings: {
template: `systems/DSA_4-1/templates/actor/group/tab-settings.hbs`
}
}
constructor(options = {}) {
super(options);
}
_configureRenderOptions(options) {
super._configureRenderOptions(options)
if (options.window) {
options.window.title = this.document.name
}
return options
}
/**
* Handle form submission
* @this {AdvantageSheet}
* @param {SubmitEvent} event
* @param {HTMLFormElement} form
* @param {FormDataExtended} formData
*/
static async #onSubmitForm(event, form, formData) {
event.preventDefault()
if (formData.object.name) {
await (game.folders.get(this.document.system.groupId)).update({name: formData.object.name})
}
await this.document.update(formData.object) // Note: formData.object
}
#stringToKeyFieldName(s) {
return s
}
static #openEmbeddedDocument(event) {
const dataset = event.target.parentElement.dataset
const id = dataset.itemId ?? dataset.id
this.document.items.get(id).sheet.render(true)
}
static async #openActorDocument(event) {
const dataset = event.target.parentElement.dataset
const id = dataset.itemId ?? dataset.id
game.actors.get(id).sheet.render(true)
}
static async #dieRoll(evt) {
}
async #onUpdateCharacterSettings(data) {
if (data.type === "Character") {
// update group
let settings = {...this.document.system.settings}
data.items.filter((i) => i.type === "Advantage").forEach((advantage) => {
if (!settings[this.#stringToKeyFieldName(advantage.name)]) {
settings[this.#stringToKeyFieldName(advantage.name)] = false
}
})
data.items.filter((i) => i.type === "Skill").forEach((skill) => {
if (!settings[this.#stringToKeyFieldName(skill.name)]) {
settings[this.#stringToKeyFieldName(skill.name)] = false
}
})
await this.document.update({"system.settings": settings})
}
}
_getTabsConfig(group) {
const tabs = foundry.utils.deepClone(super._getTabsConfig(group))
// Modify tabs based on document properties
if (game.user.isGM) {
tabs.tabs.push({id: "settings", group: "sheet", label: "Einstellungen"})
}
return tabs
}
/** @override */
async _prepareContext(options) {
const context = await super._prepareContext(options)
const groupData = context.document
context.system = groupData.system
context.flags = groupData.flags
context.characters = []
context.isGM = game.user.isGM
context.name = groupData.name
context.img = groupData.img
context.fields = [];
const hiddenFields = Object.entries(groupData.system.settings)
.sort(([key, value], [otherKey, otherValue]) => key.localeCompare(otherKey))
.filter(([key, value]) => value === true)
.map(([key, value]) => key)
context.fields = {}
context.fields["head"] = {}
for (const field of hiddenFields) {
context.fields[field] = {}
for (const characterId of groupData.system.characters) {
const character = await game.actors.get(characterId)
context.fields[field][character.name] = "-"
}
}
// TODO hook on changes in the given folder
const characters = await game.folders.get(groupData.system.groupId).contents
for (const character of characters) {
character.items.filter((i) => i.type === "Advantage").filter((i) => hiddenFields.includes(this.#stringToKeyFieldName(i.name))).map((advantage) => {
const n = this.#stringToKeyFieldName(advantage.name)
if (!context.fields[n]) {
context.fields[n] = {}
}
context.fields[n][character.name] = advantage.system.value ?? "Ja" // TODO: Allow GM roll
})
character.items.filter((i) => i.type === "Skill").filter((i) => hiddenFields.includes(this.#stringToKeyFieldName(i.name))).map((skill) => {
const n = this.#stringToKeyFieldName(skill.name)
if (!context.fields[n]) {
context.fields[n] = {}
}
context.fields[n][character.name] = {
taw: skill.system.taw ?? "0",
name: skill.name,
actor: character._id,
}
?? 0
})
context.fields.head[character.name] =
{
img: character.img,
name: character.name,
id: character._id,
attributes: [
{name: "MU", value: character.system.attribute.mu.aktuell},
{name: "KL", value: character.system.attribute.kl.aktuell},
{name: "IN", value: character.system.attribute.in.aktuell},
{name: "CH", value: character.system.attribute.ch.aktuell},
{name: "FF", value: character.system.attribute.ff.aktuell},
{name: "GE", value: character.system.attribute.ge.aktuell},
{name: "KO", value: character.system.attribute.ko.aktuell},
{name: "KK", value: character.system.attribute.kk.aktuell},
],
isLimited: character.isOwner || !character.limited,
isVisible: character.isOwner || character.visible,
isOwner: character.isOwner
}
}
context.inventoryItems = [];
const actorData = context.document;
actorData.items.forEach((item, index) => {
if (item.type === "Equipment") {
context.inventoryItems.push({
index: index,
id: item._id,
quantity: item.system.quantity,
name: item.name,
icon: item.img
})
}
})
context.settings = Object.fromEntries(Object.entries(groupData.system.settings)
.sort(([key, value], [otherKey, otherValue]) => key.localeCompare(otherKey)))
return await context;
}
_onRender(context, options) {
/*ContextMenu.implementation.create(this, this.element, ".equipment", [
{
name: "Aus dem Gruppeninventar entfernen",
icon: '<i class="fa-solid fa-trash"></i>',
callback: (event) => {
this.object.deleteEmbeddedDocuments('Item', [event[0].dataset.id])
},
condition: () => true
}
], {
jQuery: false
});*/
// Drag-drop
new foundry.applications.ux.DragDrop.implementation({
dragSelector: ".inventory-table .equipment",
dropSelector: ".inventory",
permissions: {
dragstart: this._canDragStart.bind(this),
drop: this._canDragDrop.bind(this)
},
callbacks: {
dragstart: this._onDragStart.bind(this),
drop: this._onDrop.bind(this)
}
}).bind(this.element);
// Update Group Members when either an Actor was moved into the linked Folder or removed from the linked Folder
Hooks.on('updateActor', (data) => {
if (data._id !== this.document._id) { // dont update yourself when you update yourself... baka!
if (data.type === "Character" && data.folder?._id === this.document.system.groupId) {
this.#onUpdateCharacterSettings(data)
this.render()
} else if (data.type === "Character") {
this.render()
}
}
});
}
/**
* An event that occurs when a drag workflow begins for a draggable item on the sheet.
* @param {DragEvent} event The initiating drag start event
* @returns {Promise<void>}
* @protected
*/
async _onDragStart(event) {
const target = event.currentTarget;
if ("link" in event.target.dataset) return;
let dragData;
// Owned Items
if (target.dataset.itemId) {
const item = this.actor.items.get(target.dataset.itemId);
dragData = item.toDragData();
}
// Active Effect
if (target.dataset.effectId) {
const effect = this.actor.effects.get(target.dataset.effectId);
dragData = effect.toDragData();
}
// Set data transfer
if (!dragData) return;
event.dataTransfer.setData("text/plain", JSON.stringify(dragData));
}
// TODO needs to be fixed once Character Sheet is migrated to ActorSheetV2
async _onDrop(event) {
const data = TextEditor.implementation.getDragEventData(event);
const actor = this.actor;
const allowed = Hooks.call("dropActorSheetData", actor, this, data);
if (allowed === false) return;
// Dropped Documents
const documentClass = foundry.utils.getDocumentClass(data.type);
if (documentClass) {
const document = await documentClass.fromDropData(data);
await this._onDropDocument(event, document);
// No duplication by moving items from one actor to another
if (document.parent) {
document.parent.items.get(document._id).delete()
}
}
}
}