export default class DhRollTableSheet extends foundry.applications.sheets.RollTableSheet { static DEFAULT_OPTIONS = { ...super.DEFAULT_OPTIONS, actions: { changeMode: DhRollTableSheet.#onChangeMode, drawResult: DhRollTableSheet.#onDrawResult, resetResults: DhRollTableSheet.#onResetResults, addFormula: DhRollTableSheet.#addFormula, removeFormula: DhRollTableSheet.#removeFormula } }; static buildParts() { const { footer, header, sheet, results, ...parts } = super.PARTS; return { sheet: { ...sheet, template: 'systems/daggerheart/templates/sheets/rollTable/sheet.hbs' }, header: { template: 'systems/daggerheart/templates/sheets/rollTable/header.hbs' }, ...parts, results: { template: 'systems/daggerheart/templates/sheets/rollTable/results.hbs', templates: ['templates/sheets/roll-table/result-details.hbs'], scrollable: ['table[data-results] tbody'] }, summary: { template: 'systems/daggerheart/templates/sheets/rollTable/summary.hbs' }, footer }; } static PARTS = DhRollTableSheet.buildParts(); async _preRender(context, options) { await super._preRender(context, options); if (!options.internalRefresh) this.daggerheartFlag = new game.system.api.data.DhRollTable(this.document.flags.daggerheart); } /* root PART has a blank element on _attachPartListeners, so it cannot be used to set the eventListeners for the view mode */ async _onRender(context, options) { super._onRender(context, options); for (const element of this.element.querySelectorAll('.system-update-field')) element.addEventListener('change', this.updateSystemField.bind(this)); } async _preparePartContext(partId, context, options) { context = await super._preparePartContext(partId, context, options); switch (partId) { case 'sheet': context.altFormula = this.daggerheartFlag.altFormula; context.usesAltFormula = Object.keys(this.daggerheartFlag.altFormula).length > 0; context.altFormulaOptions = { '': { name: this.daggerheartFlag.formulaName }, ...this.daggerheartFlag.altFormula }; context.activeAltFormula = this.daggerheartFlag.activeAltFormula; context.selectedFormula = this.daggerheartFlag.getActiveFormula(this.document.formula); context.results = this.getExtendedResults(context.results); break; case 'header': context.altFormula = this.daggerheartFlag.altFormula; context.usesAltFormula = Object.keys(this.daggerheartFlag.altFormula).length > 0; context.altFormulaOptions = { '': { name: this.daggerheartFlag.formulaName }, ...this.daggerheartFlag.altFormula }; context.activeAltFormula = this.daggerheartFlag.activeAltFormula; break; case 'summary': context.systemFields = this.daggerheartFlag.schema.fields; context.altFormula = this.daggerheartFlag.altFormula; context.formulaName = this.daggerheartFlag.formulaName; break; case 'results': context.results = this.getExtendedResults(context.results); break; } return context; } getExtendedResults(results) { const bodyDarkMode = document.body.classList.contains('theme-dark'); const elementLightMode = this.element.classList.contains('theme-light'); const elementDarkMode = this.element.classList.contains('theme-dark'); const isDarkMode = elementDarkMode || (!elementLightMode && bodyDarkMode); return results.map(x => ({ ...x, displayImg: isDarkMode && x.img === 'icons/svg/d20-black.svg' ? 'icons/svg/d20.svg' : x.img })); } /* -------------------------------------------- */ /* Flag SystemData update methods */ /* -------------------------------------------- */ async updateSystemField(event) { const { dataset, value } = event.target; await this.daggerheartFlag.updateSource({ [dataset.path]: value }); this.render({ internalRefresh: true }); } getSystemFlagUpdate() { const deleteUpdate = Object.keys(this.document._source.flags.daggerheart?.altFormula ?? {}).reduce( (acc, formulaKey) => { if (!this.daggerheartFlag.altFormula[formulaKey]) acc.altFormula[formulaKey] = _del; return acc; }, { altFormula: {} } ); const flagData = this.daggerheartFlag.toObject(); return { ...flagData, altFormula: { ...flagData.altFormula, ...deleteUpdate.altFormula } }; } static async #addFormula() { await this.daggerheartFlag.updateSource({ [`altFormula.${foundry.utils.randomID()}`]: game.system.api.data.DhRollTable.getDefaultFormula() }); this.render({ internalRefresh: true }); } static async #removeFormula(_event, target) { await this.daggerheartFlag.updateSource({ [`altFormula.${target.dataset.key}`]: _del }); this.render({ internalRefresh: true }); } /* -------------------------------------------- */ /* Extended RollTable methods */ /* -------------------------------------------- */ /** * Alternate between view and edit modes. * @this {RollTableSheet} * @type {ApplicationClickAction} */ static async #onChangeMode() { this.mode = this.isEditMode ? 'view' : 'edit'; await this.document.update(this.getSystemFlagUpdate()); await this.render({ internalRefresh: true }); } /** @inheritdoc */ async _processSubmitData(event, form, submitData, options) { /* RollTable sends an empty dummy event when swapping from view/edit first time */ if (Object.keys(submitData).length) { if (!submitData.flags) submitData.flags = { daggerheart: {} }; submitData.flags.daggerheart = this.getSystemFlagUpdate(); } super._processSubmitData(event, form, submitData, options); } /** @inheritdoc */ static async #onResetResults() { await this.document.update(this.getSystemFlagUpdate()); await this.document.resetResults(); } /** * Roll and draw a TableResult. * @this {RollTableSheet} * @type {ApplicationClickAction} */ static async #onDrawResult(_event, button) { if (this.form) await this.submit({ operation: { render: false } }); button.disabled = true; const table = this.document; await this.document.update(this.getSystemFlagUpdate()); /* Sending in the currently selectd activeFormula to table.roll to use as the formula */ const selectedFormula = this.daggerheartFlag.getActiveFormula(this.document.formula); const tableRoll = await table.roll({ selectedFormula }); const draws = table.getResultsForRoll(tableRoll.roll.total); if (draws.length > 0) { if (game.settings.get('core', 'animateRollTable')) await this._animateRoll(draws); await table.draw(tableRoll); } // Reenable the button if drawing with replacement since the draw won't trigger a sheet re-render if (table.replacement) button.disabled = false; } }