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

317 lines
10 KiB
JavaScript

const {HandlebarsApplicationMixin, DocumentSheetV2} = foundry.applications.api
const {ActorSheetV2} = foundry.applications.sheets
export class MerchantSheet extends HandlebarsApplicationMixin(ActorSheetV2) {
/** @inheritDoc */
static DEFAULT_OPTIONS = {
position: {width: 520, height: 480},
classes: ['dsa41', 'sheet', 'actor', 'merchant'],
tag: 'form',
dragDrop: [{
dropSelector: '.tab.inventory.active'
}],
form: {
submitOnChange: true,
closeOnSubmit: false,
handler: MerchantSheet.#onSubmitForm
},
window: {
resizable: true,
},
actions: {
buy: MerchantSheet.#buyWare,
editItem: MerchantSheet.#openEmbeddedDocument,
editImage: DocumentSheetV2.DEFAULT_OPTIONS.actions.editImage,
editServiceImage: MerchantSheet.#editServiceImage,
editNewServiceImage: MerchantSheet.#editNewServiceImage,
addNewService: MerchantSheet.#addNewService,
removeService: MerchantSheet.#removeService,
}
}
static TABS = {
sheet: {
tabs: [
{id: 'goods', group: 'sheet', label: 'Waren'},
{id: 'services', group: 'sheet', label: 'Dienstleistungen'},
// Meta is added via GM permission
],
initial: 'goods'
}
}
/** @inheritDoc */
static PARTS = {
form: {
template: `systems/DSA_4-1/templates/actor/merchant/main-sheet.hbs`
},
goods: {
template: `systems/DSA_4-1/templates/actor/merchant/tab-goods.hbs`
},
services: {
template: `systems/DSA_4-1/templates/actor/merchant/tab-services.hbs`
},
meta: {
template: `systems/DSA_4-1/templates/actor/merchant/tab-meta.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()
await this.document.update(formData.object) // Note: formData.object
}
static async #buyWare(event, target) {
const {itemId} = target.dataset
const item = this.document.items.get(itemId)
let selections = ''
game.actors.filter(p => p.isOwner && p.type === "Character").forEach(actor => {
selections += `<option value=${actor.id}>${actor.name}</option>`
})
const actorId = await foundry.applications.api.DialogV2.prompt({
window: {title: `${item.name} kaufen mit wem?`},
content: `<select name="actor">${selections}</select>`,
ok: {
label: `Kaufen`,
callback: (event, button, dialog) => button.form.elements.actor.value
}
});
if (actorId) { // ignore the following when dialog was cancelled
const actor = game.actors.get(actorId)
const canBuy = await actor.reduceWealth(item.system.price)
if (canBuy) { // returns false when the wealth cant be reduced sufficiently
actor.createEmbeddedDocuments('Item', [item]).then(documents => {
documents[0].update({'system.quantity': 1})
})
game.DSA41.socket.executeAsGM("buyFromLootTable", this.document.id, item.id)
ChatMessage.create({
user: game.user._id,
speaker: {actor},
content: `hat ${item.name} für ${game.DSA41.displayCurrency(item.system.price)} gekauft`,
type: CONST.CHAT_MESSAGE_TYPES.IC
})
} else {
ui.notifications.error(item.name + " ist zu teuer für " + actor.name)
}
}
}
/**
*
* @param {MouseEvent} event
*/
static #openEmbeddedDocument(event) {
let dataset = event.target.dataset
if (!dataset.itemId && !dataset.id) {
dataset = event.target.parentElement.dataset
}
const id = dataset.itemId ?? dataset.id
if (this.document.isOwner) { // only shop owner can change stock and price
this.document.items.get(id).sheet.render(true)
}
}
static async #removeService(event, target) {
const {rowId} = target.dataset;
const services = this.document.services
services.splice(rowId, 1)
this.document.update({"system.services": services})
}
static async #addNewService(event, target) {
event.preventDefault()
const fieldset = this.element.querySelector('details')
const image = fieldset.querySelector('img').src
const name = fieldset.querySelector('input[name="new_name"]').value
const price = fieldset.querySelector('input[name="new_price"]').value
const availability = fieldset.querySelector('input[name="new_availability"]').value
const description = fieldset.querySelector('prose-mirror').value
if (name && price) {
let services = this.document.system.services
services.push({
image,
name,
price,
availability,
description
})
this.document.update({"system.services": services}).then(e => {
this.element.reset()
})
}
return false
}
static async #editNewServiceImage(event, target) {
const field = target.dataset.field || "img"
const current = foundry.utils.getProperty(this.document, field)
const fp = new foundry.applications.apps.FilePicker({
type: "image",
current: current,
callback: (path) => {
target.src = path
}
})
fp.render(true)
}
static async #editServiceImage(event, target) {
const field = target.dataset.field || "img"
const current = foundry.utils.getProperty(this.document, field)
const fp = new foundry.applications.apps.FilePicker({
type: "image",
current: current,
callback: (path) => {
target.src = path
//foundry.utils.setProperty(this.document, field, path)
target.parentElement.querySelector(`input[name="${field}"][type="hidden"]`).value = path
this.element.submit()
}
})
fp.render(true)
}
/**
* Handle changing a Document's image.
* @param {MouseEvent} event The click event.
* @returns {Promise<FilePicker>}
* @protected
*/
/*
static _onEditImage(event) {
const attr = event.currentTarget.dataset.edit;
const current = foundry.utils.getProperty(this.object, attr);
const { img } = this.document.constructor.getDefaultArtwork?.(this.document.toObject()) ?? {};
const fp = new FilePicker.implementation({
current,
type: "image",
redirectToRoot: img ? [img] : [],
callback: path => {
event.target.src = path;
event.target.dataset.edit
this.document.update({'image': path})
},
top: this.position.top + 40,
left: this.position.left + 10
});
return fp.browse();
}*/
_getTabsConfig(group) {
const tabs = foundry.utils.deepClone(super._getTabsConfig(group))
// Modify tabs based on document properties
if (game.user.isGM) {
tabs.tabs.push({id: "meta", group: "sheet", label: "Meta"})
}
return tabs
}
/** @override */
async _prepareContext(options) {
const context = await super._prepareContext(options)
context.name = this.document.name
context.image = this.document.img
context.description = this.document.system.description
context.goods = this.document.itemTypes["Equipment"] ?? []
context.services = this.document.system.services
context.isOwner = this.document.isOwner
return context
}
/**
* Actions performed after any render of the Application.
* Post-render steps are not awaited by the render process.
* @param {ApplicationRenderContext} context Prepared context data
* @param {RenderOptions} options Provided render options
* @protected
*/
_onRender(context, options) {
new foundry.applications.ux.DragDrop.implementation({
dropSelector: ".window-content",
permissions: {
drop: this._canDragDrop.bind(this)
},
callbacks: {
drop: this._onDrop.bind(this)
}
}).bind(this.element);
}
_canDragDrop(event, options) {
return game.user.isGM
}
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);
if (document.type === "Equipment") {
// No duplication by moving items from one actor to another
if (document.parent && document.parent !== this.actor) {
document.parent.items.get(document._id).delete()
}
await this._onDropDocument(event, document);
}
}
}
}