daggerheart/module/applications/dialogs/beastformDialog.mjs
2025-07-18 15:05:12 +02:00

201 lines
6.5 KiB
JavaScript

const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
export default class BeastformDialog extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(configData) {
super();
this.configData = configData;
this.selected = null;
this.evolved = { form: null };
this.hybrid = null;
this._dragDrop = this._createDragDropHandlers();
}
static DEFAULT_OPTIONS = {
tag: 'form',
classes: ['daggerheart', 'views', 'dh-style', 'beastform-selection'],
position: {
width: 'auto',
height: 'auto'
},
actions: {
selectBeastform: this.selectBeastform,
submitBeastform: this.submitBeastform
},
form: {
handler: this.updateBeastform,
submitOnChange: true,
submitOnClose: false
},
dragDrop: [{ dragSelector: '.beastform-container', dropSelector: '.advanced-form-container' }]
};
get title() {
return game.i18n.localize('DAGGERHEART.ITEMS.Beastform.dialogTitle');
}
/** @override */
static PARTS = {
beastform: {
template: 'systems/daggerheart/templates/dialogs/beastformDialog.hbs'
}
};
_createDragDropHandlers() {
return this.options.dragDrop.map(d => {
d.callbacks = {
dragstart: this._onDragStart.bind(this),
drop: this._onDrop.bind(this)
};
return new foundry.applications.ux.DragDrop.implementation(d);
});
}
_attachPartListeners(partId, htmlElement, options) {
super._attachPartListeners(partId, htmlElement, options);
this._dragDrop.forEach(d => d.bind(htmlElement));
}
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
context.selected = this.selected;
context.selectedBeastformEffect = this.selected?.effects?.find?.(x => x.type === 'beastform');
context.evolved = this.evolved;
context.hybrid = this.hybrid;
const maximumDragTier = Math.max(
this.selected?.system?.evolved?.maximumTier ?? 0,
this.selected?.system?.hybrid?.maximumTier ?? 0
);
const compendiumBeastforms = await game.packs.get(`daggerheart.beastforms`)?.getDocuments();
context.beastformTiers = [...(compendiumBeastforms ? compendiumBeastforms : []), ...game.items].reduce(
(acc, x) => {
const tier = CONFIG.DH.GENERAL.tiersAlternate[x.system.tier];
if (x.type !== 'beastform' || tier.id > this.configData.tierLimit) return acc;
if (!acc[tier.id]) acc[tier.id] = { label: game.i18n.localize(tier.label), values: {} };
acc[tier.id].values[x.uuid] = {
selected: this.selected?.uuid == x.uuid,
value: x,
draggable: maximumDragTier ? x.system.tier <= maximumDragTier : false
};
return acc;
},
{}
); // Also get from compendium when added
context.canSubmit = this.canSubmit();
return context;
}
canSubmit() {
if (this.selected) {
switch (this.selected.system.beastformType) {
case 'normal':
return true;
case 'evolved':
return this.evolved.form;
}
}
return false;
}
static updateBeastform(event, _, formData) {
this.selected = foundry.utils.mergeObject(this.selected, formData.object);
this.render();
}
static async selectBeastform(_, target) {
this.element.querySelectorAll('.beastform-container ').forEach(element => {
if (element.dataset.uuid === target.dataset.uuid && this.selected?.uuid !== target.dataset.uuid) {
element.classList.remove('inactive');
} else {
element.classList.add('inactive');
}
});
const uuid = this.selected?.uuid === target.dataset.uuid ? null : target.dataset.uuid;
this.selected = uuid ? await foundry.utils.fromUuid(uuid) : null;
if (this.selected && this.selected.system.beastformType !== CONFIG.DH.ITEM.beastformTypes.normal.id) {
this.element.querySelector('.advanced-container').classList.add('expanded');
} else {
this.element.querySelector('.advanced-container').classList.remove('expanded');
}
if (this.selected) {
if (this.selected.system.beastformType !== 'evolved') this.evolved.form = null;
if (this.selected.system.beastformType !== 'hybrid') this.hybrid = null;
}
this.render();
}
static async submitBeastform() {
await this.close({ submitted: true });
}
/** @override */
_onClose(options = {}) {
if (!options.submitted) this.selected = null;
}
static async configure(configData) {
return new Promise(resolve => {
const app = new this(configData);
app.addEventListener(
'close',
() => resolve({ selected: app.selected, evolved: app.evolved, hybrid: app.hybrid }),
{ once: true }
);
app.render({ force: true });
});
}
async _onDragStart(event) {
const target = event.currentTarget;
if (!this.selected) {
event.preventDefault();
return;
}
const draggedForm = await foundry.utils.fromUuid(target.dataset.uuid);
if (this.selected.system.beastformType === 'evolved') {
if (draggedForm.system.tier > this.selected.system.evolved.maximumTier) {
event.preventDefault();
return;
}
}
if (this.selected.system.beastformType === 'hybrid') {
if (draggedForm.system.tier > this.selected.system.hybrid.maximumTier) {
event.preventDefault();
return;
}
}
event.dataTransfer.setData('text/plain', JSON.stringify(target.dataset));
event.dataTransfer.setDragImage(target, 60, 0);
}
async _onDrop(event) {
event.stopPropagation();
const data = foundry.applications.ux.TextEditor.getDragEventData(event);
const item = await fromUuid(data.uuid);
if (event.target.closest('.advanced-form-container.evolved')) {
this.evolved.form = item;
}
this.render();
}
}