328 lines
11 KiB
JavaScript
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()
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|