317 lines
10 KiB
JavaScript
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
} |