diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..938abe7c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,34 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[BUG] - " +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Setup Information:** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Foundry Version [e.g. v13 b342] +- System Version [e.g. main-3593f44] + + +**Additional context** +Add any other context about the problem here. diff --git a/README.md b/README.md index 9c58fece..ed82da75 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,23 @@ # Daggerheart -This is a repo for a Foundry VTT implementation of daggerheart. It is not associated with Critical Role or Darrington Press. +## Table of Contents -## Setup +- [Overview](#overview) +- [User Install Guide](#user-install) +- [Developer Setup](#developer-setup) +- [Contribution Info](#contributing) + +## Overivew + +This is a community repo for a Foundry VTT implementation of Daggerheart. It is not associated with Critical Role or Darrington Press. + +## User Install + +1. **(Not Yet Supported - No Releases Yet)** Pasting `https://raw.githubusercontent.com/Foundryborne/daggerheart/refs/heads/main/system.json` into the Install System dialog on the Setup menu of the application. +2. **(Not Yet Supported - No Releases Yet)** Browsing the repository's Releases page, where you can copy any system.json link for use in the Install System dialog. +3. **(Not Yet Supported - No Releases Yet)** Downloading one of the .zip archives from the Releases page and extracting it into your foundry Data folder, under Data/systems/daggerheart. + +## Development Setup - Open a terminal in the directory with the repo `cd //` - NOTE: The repo should be placed in the system files are or somewhere else and a link (if on linux) is placed in the system directory @@ -23,8 +38,8 @@ This is a repo for a Foundry VTT implementation of daggerheart. It is not associ Now you should be able to build the app using `npm start` [Foundry VTT Website][1] -[1]: https://foundryvtt.com/ +[1]: https://foundryvtt.com/ -## Contributing +## Contributing -Looking to contribute to the project? Look no further, check out our [contributing guide](contributing.md), and keep the [Code of Conduct](coc.md) in mind when working on things. \ No newline at end of file +Looking to contribute to the project? Look no further, check out our [contributing guide](contributing.md), and keep the [Code of Conduct](coc.md) in mind when working on things. diff --git a/daggerheart.mjs b/daggerheart.mjs index 434824ef..b65bf1f3 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -62,7 +62,7 @@ Hooks.once('init', () => { CONFIG.Dice.rolls = [...CONFIG.Dice.rolls, ...[DHRoll, DualityRoll, D20Roll, DamageRoll]]; CONFIG.MeasuredTemplate.objectClass = DhMeasuredTemplate; - CONFIG.Item.documentClass = documents.DhpItem; + CONFIG.Item.documentClass = documents.DHItem; //Registering the Item DataModel CONFIG.Item.dataModels = models.items.config; @@ -323,7 +323,7 @@ const preloadHandlebarsTemplates = async function () { 'systems/daggerheart/templates/views/actionTypes/range-target.hbs', 'systems/daggerheart/templates/views/actionTypes/effect.hbs', 'systems/daggerheart/templates/settings/components/settings-item-line.hbs', - + 'systems/daggerheart/templates/chat/parts/target-chat.hbs' ]); }; diff --git a/lang/en.json b/lang/en.json index 619fb3dc..eb738315 100755 --- a/lang/en.json +++ b/lang/en.json @@ -100,6 +100,15 @@ "FIELDS": { "maxFear": { "label": "Max Fear" }, "traitArray": { "label": "Initial Trait Modifiers" } + }, + "Currency": { + "enabled": "Enable Overrides", + "title": "Currency Overrides", + "currencyName": "Currency Name", + "coinName": "Coin Name", + "handfullName": "Handfull Name", + "bagName": "Bag Name", + "chestName": "Chest Name" } }, "Resources": { @@ -1589,6 +1598,9 @@ }, "spellcast": { "name": "SpellCast" + }, + "diceSet": { + "name": "Dice Set" } } } diff --git a/module/applications/daggerheart-sheet.mjs b/module/applications/daggerheart-sheet.mjs deleted file mode 100644 index 32d5212e..00000000 --- a/module/applications/daggerheart-sheet.mjs +++ /dev/null @@ -1,48 +0,0 @@ -export default function DhpApplicationMixin(Base) { - return class DhpSheet extends Base { - static applicationType = 'sheets'; - static documentType = ''; - - static get defaultOptions() { - return Object.assign(super.defaultOptions, { - classes: ['daggerheart', 'sheet', this.documentType], - template: `systems/${SYSTEM.id}/templates/${this.applicationType}/${this.documentType}.hbs`, - height: 'auto', - submitOnChange: true, - submitOnClose: false, - width: 450 - }); - } - - /** @override */ - get title() { - const { documentName, type, name } = this.object; - // const typeLabel = game.i18n.localize(CONFIG[documentName].typeLabels[type]); - const typeLabel = documentName; - return `[${typeLabel}] ${name}`; - } - - // async _renderOuter() { - // const html = await super._renderOuter(); - // // const overlaySrc = "systems/amia/assets/ThePrimordial.png"; - // const overlay = `
` - // $(html).find('.window-header').prepend(overlay); - // return html; - // } - - activateListeners(html) { - super.activateListeners(html); - html.on('click', '[data-action]', this.#onClickAction.bind(this)); - } - - async #onClickAction(event) { - event.preventDefault(); - const button = event.currentTarget; - const action = button.dataset.action; - - return this._handleAction(action, event, button); - } - - async _handleAction(action, event, button) {} - }; -} diff --git a/module/applications/roll.mjs b/module/applications/roll.mjs index b5f53abc..eaf4747b 100644 --- a/module/applications/roll.mjs +++ b/module/applications/roll.mjs @@ -11,6 +11,10 @@ export class DHRoll extends Roll { super(formula, data, options); } + static messageType = 'adversaryRoll'; + + static DefaultDialog = D20RollDialog; + static async build(config = {}, message = {}) { const roll = await this.buildConfigure(config, message); if (!roll) return; @@ -28,19 +32,18 @@ export class DHRoll extends Roll { this.applyKeybindings(config); + let roll = new this(config.roll.formula, config.data, config); if (config.dialog.configure !== false) { // Open Roll Dialog const DialogClass = config.dialog?.class ?? this.DefaultDialog; - config = await DialogClass.configure(config, message); - if (!config) return; + const configDialog = await DialogClass.configure(roll, config, message); + if (!configDialog) return; } - let roll = new this(config.formula, config.data, config); for (const hook of config.hooks) { if (Hooks.call(`${SYSTEM.id}.post${hook.capitalize()}RollConfiguration`, roll, config, message) === false) return []; } - return roll; } @@ -62,7 +65,20 @@ export class DHRoll extends Roll { } } - static async postEvaluate(roll, config = {}) {} + static postEvaluate(roll, config = {}) { + if (!config.roll) config.roll = {}; + config.roll.total = roll.total; + config.roll.formula = roll.formula; + config.roll.dice = []; + roll.dice.forEach(d => { + config.roll.dice.push({ + dice: d.denomination, + total: d.total, + formula: d.formula, + results: d.results + }); + }); + } static async toMessage(roll, config) { const cls = getDocumentClass('ChatMessage'), @@ -77,15 +93,16 @@ export class DHRoll extends Roll { } static applyKeybindings(config) { - config.dialog.configure ??= true; + config.dialog.configure ??= !(config.event.shiftKey || config.event.altKey || config.event.ctrlKey); + } + + constructFormula(config) { + // const formula = Roll.replaceFormulaData(this.options.roll.formula, config.data); + this.terms = Roll.parse(this.options.roll.formula, config.data); + return (this._formula = this.constructor.getFormula(this.terms)); } } -// DHopeDie -// DFearDie -// DualityDie -// D20Die - export class DualityDie extends foundry.dice.terms.Die { constructor({ number = 1, faces = 12, ...args } = {}) { super({ number, faces, ...args }); @@ -95,10 +112,11 @@ export class DualityDie extends foundry.dice.terms.Die { export class D20Roll extends DHRoll { constructor(formula, data = {}, options = {}) { super(formula, data, options); - this.createBaseDice(); - this.configureModifiers(); + // this.createBaseDice(); + // this.configureModifiers(); - this._formula = this.resetFormula(); + // this._formula = this.resetFormula(); + this.constructFormula(); } static ADV_MODE = { @@ -165,7 +183,7 @@ export class D20Roll extends DHRoll { applyAdvantage() { this.d20.modifiers.findSplice(m => ['kh', 'kl'].includes(m)); - if (!this.hasAdvantage && !this.hasAdvantage) this.number = 1; + if (!this.hasAdvantage && !this.hasDisadvantage) this.number = 1; else { this.d20.number = 2; this.d20.modifiers.push(this.hasAdvantage ? 'kh' : 'kl'); @@ -175,62 +193,65 @@ export class D20Roll extends DHRoll { // Trait bonus != Adversary configureModifiers() { this.applyAdvantage(); - + // this.options.roll.modifiers = []; this.applyBaseBonus(); this.options.experiences?.forEach(m => { if (this.options.data.experiences?.[m]) this.options.roll.modifiers.push({ - label: this.options.data.experiences[m].description, - value: this.options.data.experiences[m].total + label: this.options.data.experiences[m].name, + value: this.options.data.experiences[m].total ?? this.options.data.experiences[m].value }); }); this.options.roll.modifiers?.forEach(m => { this.terms.push(...this.formatModifier(m.value)); }); - if (this.options.extraFormula) + if (this.options.extraFormula) { this.terms.push( new foundry.dice.terms.OperatorTerm({ operator: '+' }), ...this.constructor.parse(this.options.extraFormula, this.getRollData()) ); - + } // this.resetFormula(); } - applyBaseBonus() { - if (this.options.type === 'attack') - this.terms.push(...this.formatModifier(this.options.data.attack.roll.bonus)); + constructFormula(config) { + this.terms = []; + this.createBaseDice(); + this.configureModifiers(); + this.resetFormula(); + return this._formula; } - static async postEvaluate(roll, config = {}) { + applyBaseBonus() { + this.options.roll.modifiers = [ + { + label: 'Bonus to Hit', + value: Roll.replaceFormulaData('@attackBonus', this.data) + } + ]; + } + + static postEvaluate(roll, config = {}) { + super.postEvaluate(roll, config); if (config.targets?.length) { config.targets.forEach(target => { const difficulty = config.roll.difficulty ?? target.difficulty ?? target.evasion; target.hit = this.isCritical || roll.total >= difficulty; }); - } else if (config.roll.difficulty) config.roll.success = roll.isCritical || roll.total >= config.roll.difficulty; - config.roll.total = roll.total; - config.roll.formula = roll.formula; + } else if (config.roll.difficulty) + config.roll.success = roll.isCritical || roll.total >= config.roll.difficulty; config.roll.advantage = { type: config.advantage, dice: roll.dAdvantage?.denomination, value: roll.dAdvantage?.total }; - config.roll.modifierTotal = config.roll.modifiers.reduce((a, c) => a + c.value, 0); - config.roll.dice = []; - roll.dice.forEach(d => { - config.roll.dice.push({ - dice: d.denomination, - total: d.total, - formula: d.formula, - results: d.results - }); - }); + config.roll.modifierTotal = config.roll.modifiers.reduce((a, c) => a + Number(c.value), 0); } getRollData() { - return this.options.data(); + return this.options.data; } formatModifier(modifier) { @@ -332,7 +353,7 @@ export class DualityRoll extends D20Roll { bardRallyFaces = this.hasBarRally, advDie = new foundry.dice.terms.Die({ faces: dieFaces }); if (this.hasAdvantage || this.hasDisadvantage || bardRallyFaces) - this.terms.push(new foundry.dice.terms.OperatorTerm({ operator: '+' })); + this.terms.push(new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' })); if (bardRallyFaces) { const rallyDie = new foundry.dice.terms.Die({ faces: bardRallyFaces }); if (this.hasAdvantage) { @@ -349,15 +370,15 @@ export class DualityRoll extends D20Roll { } applyBaseBonus() { - if (!this.options.roll.modifiers) this.options.roll.modifiers = []; - if (this.options.roll?.trait) - this.options.roll.modifiers.push({ + this.options.roll.modifiers = [ + { label: `DAGGERHEART.Abilities.${this.options.roll.trait}.name`, - value: this.options.data.traits[this.options.roll.trait].total - }); + value: Roll.replaceFormulaData(`@traits.${this.options.roll.trait}.total`, this.data) + } + ]; } - static async postEvaluate(roll, config = {}) { + static postEvaluate(roll, config = {}) { super.postEvaluate(roll, config); config.roll.hope = { dice: roll.dHope.denomination, @@ -372,6 +393,7 @@ export class DualityRoll extends D20Roll { total: roll.dHope.total + roll.dFear.total, label: roll.totalLabel }; + console.log(roll, config); } } @@ -385,19 +407,7 @@ export class DamageRoll extends DHRoll { static DefaultDialog = DamageDialog; static async postEvaluate(roll, config = {}) { - config.roll = { - total: roll.total, - formula: roll.formula, - type: config.type - }; - config.roll.dice = []; - roll.dice.forEach(d => { - config.roll.dice.push({ - dice: d.denomination, - total: d.total, - formula: d.formula, - results: d.results - }); - }); + super.postEvaluate(roll, config); + config.roll.type = config.type; } } diff --git a/module/applications/settings/homebrewSettings.mjs b/module/applications/settings/homebrewSettings.mjs index d59bc35c..e67afb1b 100644 --- a/module/applications/settings/homebrewSettings.mjs +++ b/module/applications/settings/homebrewSettings.mjs @@ -24,14 +24,16 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli editItem: this.editItem, removeItem: this.removeItem, resetMoves: this.resetMoves, - save: this.save + save: this.save, + reset: this.reset }, form: { handler: this.updateData, submitOnChange: true } }; static PARTS = { main: { - template: 'systems/daggerheart/templates/settings/homebrew-settings.hbs' + template: 'systems/daggerheart/templates/settings/homebrew-settings.hbs', + scrollable: [''] } }; @@ -154,4 +156,27 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew, this.settings.toObject()); this.close(); } + + static async reset() { + const resetSettings = new DhHomebrew(); + let localizedSettings = this.localizeObject(resetSettings); + this.settings.updateSource(localizedSettings); + this.render(); + } + + localizeObject(obj) { + for (let key in obj) { + if (obj.hasOwnProperty(key)) { + const value = obj[key]; + if (typeof value === 'object' && value !== null) { + obj[key] = this.localizeObject(value); + } else { + if (typeof value === 'string' && value.startsWith('DAGGERHEART.')) { + obj[key] = game.i18n.localize(value); + } + } + } + } + return obj; + } } diff --git a/module/applications/sheets/api/base-item.mjs b/module/applications/sheets/api/base-item.mjs index f6607c44..1299b208 100644 --- a/module/applications/sheets/api/base-item.mjs +++ b/module/applications/sheets/api/base-item.mjs @@ -46,19 +46,18 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { switch (partId) { case 'description': - const value = foundry.utils.getProperty(this.document, "system.description") ?? ""; + const value = foundry.utils.getProperty(this.document, 'system.description') ?? ''; context.enrichedDescription = await TextEditor.enrichHTML(value, { relativeTo: this.item, rollData: this.item.getRollData(), secrets: this.item.isOwner - }) + }); break; } return context; } - /* -------------------------------------------- */ /* Application Clicks Actions */ /* -------------------------------------------- */ @@ -70,26 +69,18 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { */ static async selectActionType() { const content = await foundry.applications.handlebars.renderTemplate( - 'systems/daggerheart/templates/views/actionType.hbs', - { types: SYSTEM.ACTIONS.actionTypes } - ), - title = 'Select Action Type', //useless var - type = 'form', - data = {}; //useless var - //TODO: use DialogV2 - return Dialog.prompt({ - title, - label: title, + 'systems/daggerheart/templates/views/actionType.hbs', + { types: SYSTEM.ACTIONS.actionTypes } + ), + title = 'Select Action Type'; + + return foundry.applications.api.DialogV2.prompt({ + window: { title }, content, - type, //this prop is useless - callback: html => { - const form = html[0].querySelector('form'), - fd = new foundry.applications.ux.FormDataExtended(form); - foundry.utils.mergeObject(data, fd.object, { inplace: true }); - // if (!data.name?.trim()) data.name = game.i18n.localize(SYSTEM.ACTIONS.actionTypes[data.type].name); - return data; - }, - rejectClose: false + ok: { + label: title, + callback: (event, button, dialog) => button.form.elements.type.value + } }); } @@ -100,13 +91,14 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { */ static async #addAction(_event, _button) { const actionType = await DHBaseItemSheet.selectActionType(); + if (!actionType) return; try { - const cls = actionsTypes[actionType?.type] ?? actionsTypes.attack, + const cls = actionsTypes[actionType] ?? actionsTypes.attack, action = new cls( { _id: foundry.utils.randomID(), - type: actionType.type, - name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType.type].name), + type: actionType, + name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType].name), ...cls.getSourceConfig(this.document) }, { @@ -141,9 +133,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { event.stopPropagation(); const actionIndex = button.closest('[data-index]').dataset.index; await this.document.update({ - 'system.actions': this.document.system.actions.filter( - (_, index) => index !== Number.parseInt(actionIndex) - ) + 'system.actions': this.document.system.actions.filter((_, index) => index !== Number.parseInt(actionIndex)) }); } } diff --git a/module/applications/sheets/character.mjs b/module/applications/sheets/character.mjs index b2723346..44ded6f2 100644 --- a/module/applications/sheets/character.mjs +++ b/module/applications/sheets/character.mjs @@ -56,7 +56,6 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) { resizable: true }, form: { - handler: this.updateForm, submitOnChange: true, closeOnSubmit: false }, @@ -218,6 +217,15 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) { this._createContextMenues(); } + /** @inheritDoc */ + async _onRender(context, options) { + await super._onRender(context, options); + + this._createSearchFilter(); + } + + /* -------------------------------------------- */ + _createContextMenues() { const allOptions = { useItem: { @@ -332,12 +340,123 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) { return acc; }, {}); + context.inventory = { + currency: { + title: game.i18n.localize('DAGGERHEART.Sheets.PC.Gold.Title'), + coins: game.i18n.localize('DAGGERHEART.Sheets.PC.Gold.Coins'), + handfulls: game.i18n.localize('DAGGERHEART.Sheets.PC.Gold.Handfulls'), + bags: game.i18n.localize('DAGGERHEART.Sheets.PC.Gold.Bags'), + chests: game.i18n.localize('DAGGERHEART.Sheets.PC.Gold.Chests') + } + }; + + const homebrewCurrency = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew).currency; + if (homebrewCurrency.enabled) { + context.inventory.currency = homebrewCurrency; + } + + if (context.inventory.length === 0) { + context.inventory = Array(1).fill(Array(5).fill([])); + } + return context; } - static async updateForm(event, _, formData) { - await this.document.update(formData.object); - this.render(); + /* -------------------------------------------- */ + /* Search Filter */ + /* -------------------------------------------- */ + + /** + * The currently active search filter. + * @type {foundry.applications.ux.SearchFilter} + */ + #search = {}; + + /** + * Track which item IDs are currently displayed due to a search filter. + * @type {{ inventory: Set, loadout: Set }} + */ + #filteredItems = { + inventory: new Set(), + loadout: new Set() + }; + + /** + * Create and initialize search filter instances for the inventory and loadout sections. + * + * Sets up two {@link foundry.applications.ux.SearchFilter} instances: + * - One for the inventory, which filters items in the inventory grid. + * - One for the loadout, which filters items in the loadout/card grid. + * @private + */ + _createSearchFilter() { + //Filters could be a application option if needed + const filters = [ + { + key: 'inventory', + input: 'input[type="search"].search-inventory', + content: '[data-application-part="inventory"] .items-section', + callback: this._onSearchFilterInventory.bind(this) + }, + { + key: 'loadout', + input: 'input[type="search"].search-loadout', + content: '[data-application-part="loadout"] .items-section', + callback: this._onSearchFilterCard.bind(this) + } + ]; + + for (const { key, input, content, callback } of filters) { + const filter = new foundry.applications.ux.SearchFilter({ + inputSelector: input, + contentSelector: content, + callback + }); + filter.bind(this.element); + this.#search[key] = filter; + } + } + + /** + * Handle invetory items search and filtering. + * @param {KeyboardEvent} event The keyboard input event. + * @param {string} query The input search string. + * @param {RegExp} rgx The regular expression query that should be matched against. + * @param {HTMLElement} html The container to filter items from. + * @protected + */ + _onSearchFilterInventory(event, query, rgx, html) { + this.#filteredItems.inventory.clear(); + + for (const ul of html.querySelectorAll('.items-list')) { + for (const li of ul.querySelectorAll('.inventory-item')) { + const item = this.document.items.get(li.dataset.itemId); + const match = !query || foundry.applications.ux.SearchFilter.testQuery(rgx, item.name); + if (match) this.#filteredItems.inventory.add(item.id); + li.hidden = !match; + } + } + } + + /** + * Handle card items search and filtering. + * @param {KeyboardEvent} event The keyboard input event. + * @param {string} query The input search string. + * @param {RegExp} rgx The regular expression query that should be matched against. + * @param {HTMLElement} html The container to filter items from. + * @protected + */ + _onSearchFilterCard(event, query, rgx, html) { + this.#filteredItems.loadout.clear(); + + const elements = html.querySelectorAll('.items-list .inventory-item, .card-list .card-item'); + + for (const li of elements) { + const item = this.document.items.get(li.dataset.itemId); + const match = !query || foundry.applications.ux.SearchFilter.testQuery(rgx, item.name); + if (match) this.#filteredItems.loadout.add(item.id); + li.hidden = !match; + } } static async rollAttribute(event, button) { @@ -543,7 +662,7 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) { } static async makeDeathMove() { - if (this.document.system.resources.hitPoints.value === this.document.system.resources.hitPoints.max) { + if (this.document.system.resources.hitPoints.value >= this.document.system.resources.hitPoints.maxTotal) { await new DhpDeathMove(this.document).render(true); } } diff --git a/module/config/actionConfig.mjs b/module/config/actionConfig.mjs index a0af78eb..5dca28c2 100644 --- a/module/config/actionConfig.mjs +++ b/module/config/actionConfig.mjs @@ -76,4 +76,32 @@ export const damageOnSave = { label: 'Full damage', mod: 1 } -} +}; + +export const diceCompare = { + below: { + id: 'below', + label: 'Below', + operator: '<' + }, + belowEqual: { + id: 'belowEqual', + label: 'Below or Equal', + operator: '<=' + }, + equal: { + id: 'equal', + label: 'Equal', + operator: '=' + }, + aboveEqual: { + id: 'aboveEqual', + label: 'Above or Equal', + operator: '>=' + }, + above: { + id: 'above', + label: 'Above', + operator: '>' + } +}; diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index 3e1ef9d7..28cfc576 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -163,13 +163,15 @@ export const defaultRestOptions = { id: 'repairArmor', name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.RepairArmor.Name'), img: 'icons/skills/trades/smithing-anvil-silver-red.webp', - description: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.RepairArmor.Description') + description: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.RepairArmor.Description'), + actions: [] }, prepare: { id: 'prepare', name: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.Prepare.Name'), img: 'icons/skills/trades/academics-merchant-scribe.webp', - description: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.Prepare.Description') + description: game.i18n.localize('DAGGERHEART.Downtime.ShortRest.Prepare.Description'), + actions: [] } }), longRest: () => ({ @@ -177,31 +179,36 @@ export const defaultRestOptions = { id: 'tendToWounds', name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.TendToWounds.Name'), img: 'icons/magic/life/cross-worn-green.webp', - description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.TendToWounds.Description') + description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.TendToWounds.Description'), + actions: [] }, clearStress: { id: 'clearStress', name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.ClearStress.Name'), img: 'icons/magic/perception/eye-ringed-green.webp', - description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.ClearStress.Description') + description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.ClearStress.Description'), + actions: [] }, repairArmor: { id: 'repairArmor', name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.RepairArmor.Name'), img: 'icons/skills/trades/smithing-anvil-silver-red.webp', - description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.RepairArmor.Description') + description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.RepairArmor.Description'), + actions: [] }, prepare: { id: 'prepare', name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.Prepare.Name'), img: 'icons/skills/trades/academics-merchant-scribe.webp', - description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.Prepare.Description') + description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.Prepare.Description'), + actions: [] }, workOnAProject: { id: 'workOnAProject', name: game.i18n.localize('DAGGERHEART.Downtime.LongRest.WorkOnAProject.Name'), img: 'icons/skills/social/thumbsup-approval-like.webp', - description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.WorkOnAProject.Description') + description: game.i18n.localize('DAGGERHEART.Downtime.LongRest.WorkOnAProject.Description'), + actions: [] } }), custom: { @@ -264,8 +271,17 @@ export const diceTypes = { }; export const multiplierTypes = { - proficiency: 'Proficiency', - spellcast: 'Spellcast', + prof: 'Proficiency', + cast: 'Spellcast', + scale: 'Cost Scaling', + result: 'Roll Result', + flat: 'Flat' +}; + +export const diceSetNumbers = { + prof: 'Proficiency', + cast: 'Spellcast', + scale: 'Cost Scaling', flat: 'Flat' }; @@ -388,6 +404,10 @@ export const rollTypes = { ability: { id: 'ability', label: 'DAGGERHEART.RollTypes.ability.name' + }, + diceSet: { + id: 'diceSet', + label: 'DAGGERHEART.RollTypes.diceSet.name' } }; diff --git a/module/data/action/action.mjs b/module/data/action/action.mjs index 3beb92a9..aef15844 100644 --- a/module/data/action/action.mjs +++ b/module/data/action/action.mjs @@ -1,5 +1,5 @@ import CostSelectionDialog from '../../applications/costSelectionDialog.mjs'; -import { DHActionDiceData, DHDamageData, DHDamageField } from './actionDice.mjs'; +import { DHActionDiceData, DHActionRollData, DHDamageData, DHDamageField } from './actionDice.mjs'; import DhpActor from '../../documents/actor.mjs'; import D20RollDialog from '../../dialogs/d20RollDialog.mjs'; @@ -69,16 +69,14 @@ export class DHBaseAction extends foundry.abstract.DataModel { static defineExtraSchema() { const extraFields = { damage: new DHDamageField(), - roll: new fields.SchemaField({ - type: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.GENERAL.rollTypes }), - trait: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.ACTOR.abilities }), - difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }), - bonus: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }) - }), + roll: new fields.EmbeddedDataField(DHActionRollData), save: new fields.SchemaField({ trait: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.ACTOR.abilities }), difficulty: new fields.NumberField({ nullable: true, initial: 10, integer: true, min: 0 }), - damageMod: new fields.StringField({ initial: SYSTEM.ACTIONS.damageOnSave.none.id, choices: SYSTEM.ACTIONS.damageOnSave }) + damageMod: new fields.StringField({ + initial: SYSTEM.ACTIONS.damageOnSave.none.id, + choices: SYSTEM.ACTIONS.damageOnSave + }) }), target: new fields.SchemaField({ type: new fields.StringField({ @@ -103,9 +101,12 @@ export class DHBaseAction extends foundry.abstract.DataModel { initial: SYSTEM.GENERAL.healingTypes.hitPoints.id, label: 'Healing' }), - resultBased: new fields.BooleanField({ initial: false, label: "DAGGERHEART.Actions.Settings.ResultBased.label" }), + resultBased: new fields.BooleanField({ + initial: false, + label: 'DAGGERHEART.Actions.Settings.ResultBased.label' + }), value: new fields.EmbeddedDataField(DHActionDiceData), - valueAlt: new fields.EmbeddedDataField(DHActionDiceData), + valueAlt: new fields.EmbeddedDataField(DHActionDiceData) }) }, extraSchemas = {}; @@ -158,19 +159,23 @@ export class DHBaseAction extends foundry.abstract.DataModel { return updateSource; } - getRollData() { + getRollData(data = {}) { const actorData = this.actor.getRollData(false); // Remove when included directly in Actor getRollData - actorData.prof = actorData.proficiency?.value ?? 1, - actorData.cast = actorData.spellcast?.value ?? 1, - actorData.scale = this.cost.length - ? this.cost.reduce((a, c) => { + actorData.prof = actorData.proficiency?.value ?? 1; + actorData.cast = actorData.spellcast?.value ?? 1; + actorData.result = data.roll?.total ?? 1; + /* actorData.scale = data.costs?.length + ? data.costs.reduce((a, c) => { a[c.type] = c.value; return a; }, {}) - : 1, - actorData.roll = {} + : 1; */ + actorData.scale = data.costs?.length // Right now only return the first scalable cost. + ? (data.costs.find(c => c.scalable)?.total ?? 1) + : 1; + actorData.roll = {}; return actorData; } @@ -192,12 +197,14 @@ export class DHBaseAction extends foundry.abstract.DataModel { // Prepare Costs const costsConfig = this.prepareCost(); - if(isFastForward && !this.hasCost(costsConfig)) return ui.notifications.warn("You don't have the resources to use that action."); + if (isFastForward && !this.hasCost(costsConfig)) + return ui.notifications.warn("You don't have the resources to use that action."); // config = this.prepareUseCost(config) // Prepare Uses const usesConfig = this.prepareUse(); - if(isFastForward && !this.hasUses(usesConfig)) return ui.notifications.warn("That action doesn't have remaining uses."); + if (isFastForward && !this.hasUses(usesConfig)) + return ui.notifications.warn("That action doesn't have remaining uses."); // config = this.prepareUseCost(config) // Prepare Roll Data @@ -210,24 +217,24 @@ export class DHBaseAction extends foundry.abstract.DataModel { costs: costsConfig, uses: usesConfig, data: actorData - } - - if ( Hooks.call(`${SYSTEM.id}.preUseAction`, this, config) === false ) return; + }; + + if (Hooks.call(`${SYSTEM.id}.preUseAction`, this, config) === false) return; // Display configuration window if necessary - if ( config.dialog.configure && this.requireConfigurationDialog(config) ) { + if (config.dialog?.configure && this.requireConfigurationDialog(config)) { config = await D20RollDialog.configure(config); if (!config) return; } - if ( this.hasRoll ) { + if (this.hasRoll) { const rollConfig = this.prepareRoll(config); config.roll = rollConfig; config = await this.actor.diceRoll(config); if (!config) return; } - if( this.hasSave ) { + if (this.hasSave) { /* config.targets.forEach((t) => { if(t.hit) { const target = game.canvas.tokens.get(t.id), @@ -259,16 +266,16 @@ export class DHBaseAction extends foundry.abstract.DataModel { }) */ } - if ( this.doFollowUp() ) { - if(this.rollDamage) await this.rollDamage(event, config); - if(this.rollHealing) await this.rollHealing(event, config); - if(this.trigger) await this.trigger(event, config); + if (this.doFollowUp()) { + if (this.rollDamage) await this.rollDamage(event, config); + if (this.rollHealing) await this.rollHealing(event, config); + if (this.trigger) await this.trigger(event, config); } // Consume resources await this.consume(config); - - if ( Hooks.call(`${SYSTEM.id}.postUseAction`, this, config) === false ) return; + + if (Hooks.call(`${SYSTEM.id}.postUseAction`, this, config) === false) return; return config; } @@ -281,21 +288,18 @@ export class DHBaseAction extends foundry.abstract.DataModel { source: { item: this.item._id, action: this._id - // action: this - }, - dialog: { - configure: true }, + dialog: {}, type: this.type, hasDamage: !!this.damage?.parts?.length, hasHealing: !!this.healing, hasEffect: !!this.effects?.length, hasSave: this.hasSave - } + }; } requireConfigurationDialog(config) { - return !config.event.shiftkey && !this.hasRoll && (config.costs?.length || config.uses); + return !config.event.shiftKey && !this.hasRoll && (config.costs?.length || config.uses); } prepareCost() { @@ -321,7 +325,6 @@ export class DHBaseAction extends foundry.abstract.DataModel { } targets = targets.map(t => this.formatTarget(t)); return targets; - } prepareRange() { @@ -330,13 +333,16 @@ export class DHBaseAction extends foundry.abstract.DataModel { } prepareRoll() { - const roll = { + const roll = { modifiers: [], trait: this.roll?.trait, label: 'Attack', type: this.actionType, - difficulty: this.roll?.difficulty + difficulty: this.roll?.difficulty, + formula: this.roll.getFormula() }; + if (this.roll?.type === 'diceSet') roll.lite = true; + return roll; } @@ -345,11 +351,14 @@ export class DHBaseAction extends foundry.abstract.DataModel { } async consume(config) { - const resources = config.costs.filter(c => c.enabled !== false).map(c => { - return { type: c.type, value: c.total * -1 }; - }); + const resources = config.costs + .filter(c => c.enabled !== false) + .map(c => { + return { type: c.type, value: (c.total ?? c.value) * -1 }; + }); + await this.actor.modifyResource(resources); - if(config.uses?.enabled) { + if (config.uses?.enabled) { const newActions = foundry.utils.getProperty(this.item.system, this.systemPath).map(x => x.toObject()); newActions[this.index].uses.value++; await this.item.update({ [`system.${this.systemPath}`]: newActions }); @@ -388,13 +397,16 @@ export class DHBaseAction extends foundry.abstract.DataModel { hasCost(costs) { const realCosts = this.getRealCosts(costs); - return realCosts.reduce((a, c) => a && this.actor.system.resources[c.type]?.value >= (c.total ?? c.value), true); + return realCosts.reduce( + (a, c) => a && this.actor.system.resources[c.type]?.value >= (c.total ?? c.value), + true + ); } /* COST */ /* USES */ calcUses(uses) { - if(!uses) return null; + if (!uses) return null; return { ...uses, enabled: uses.hasOwnProperty('enabled') ? uses.enabled : true @@ -402,7 +414,7 @@ export class DHBaseAction extends foundry.abstract.DataModel { } hasUses(uses) { - if(!uses) return true; + if (!uses) return true; return (uses.hasOwnProperty('enabled') && !uses.enabled) || uses.value + 1 <= uses.max; } /* USES */ @@ -432,7 +444,7 @@ export class DHBaseAction extends foundry.abstract.DataModel { /* TARGET */ /* RANGE */ - + /* RANGE */ /* EFFECTS */ @@ -441,10 +453,10 @@ export class DHBaseAction extends foundry.abstract.DataModel { let effects = this.effects; data.system.targets.forEach(async token => { if (!token.hit && !force) return; - if(this.hasSave && token.saved.success === true) { - effects = this.effects.filter(e => e.onSave === true) + if (this.hasSave && token.saved.success === true) { + effects = this.effects.filter(e => e.onSave === true); } - if(!effects.length) return; + if (!effects.length) return; effects.forEach(async e => { const actor = canvas.tokens.get(token.id)?.actor, effect = this.item.effects.get(e._id); @@ -479,35 +491,42 @@ export class DHBaseAction extends foundry.abstract.DataModel { /* SAVE */ async rollSave(target, event, message) { - if(!target?.actor) return; - target.actor.diceRoll({ - event, - title: 'Roll Save', - roll: { - trait: this.save.trait, - difficulty: this.save.difficulty, - type: "reaction" - }, - data: target.actor.getRollData() - }).then(async (result) => { - this.updateChatMessage(message, target.id, {result: result.roll.total, success: result.roll.success}); - }) + if (!target?.actor) return; + target.actor + .diceRoll({ + event, + title: 'Roll Save', + roll: { + trait: this.save.trait, + difficulty: this.save.difficulty, + type: 'reaction' + }, + data: target.actor.getRollData() + }) + .then(async result => { + if (result) + this.updateChatMessage(message, target.id, { + result: result.roll.total, + success: result.roll.success + }); + }); } - async updateChatMessage(message, targetId, changes, chain=true) { + async updateChatMessage(message, targetId, changes, chain = true) { setTimeout(async () => { const chatMessage = ui.chat.collection.get(message._id), msgTargets = chatMessage.system.targets, msgTarget = msgTargets.find(mt => mt.id === targetId); msgTarget.saved = changes; - await chatMessage.update({'system.targets': msgTargets}); - },100); - if(chain) { - if(message.system.source.message) this.updateChatMessage(ui.chat.collection.get(message.system.source.message), targetId, changes, false); + await chatMessage.update({ 'system.targets': msgTargets }); + }, 100); + if (chain) { + if (message.system.source.message) + this.updateChatMessage(ui.chat.collection.get(message.system.source.message), targetId, changes, false); const relatedChatMessages = ui.chat.collection.filter(c => c.system.source.message === message._id); relatedChatMessages.forEach(c => { this.updateChatMessage(c, targetId, changes, false); - }) + }); } } /* SAVE */ @@ -525,7 +544,7 @@ export class DHDamageAction extends DHBaseAction { getFormulaValue(part, data) { let formulaValue = part.value; - if(this.hasRoll && part.resultBased && data.system.roll.result.duality === -1) return part.valueAlt; + if (this.hasRoll && part.resultBased && data.system.roll.result.duality === -1) return part.valueAlt; return formulaValue; } @@ -535,16 +554,19 @@ export class DHDamageAction extends DHBaseAction { if (!formula || formula == '') return; let roll = { formula: formula, total: formula }, bonusDamage = []; - + + if (isNaN(formula)) formula = Roll.replaceFormulaData(formula, this.getRollData(data.system ?? data)); + const config = { title: game.i18n.format('DAGGERHEART.Chat.DamageRoll.Title', { damage: this.name }), - formula, - targets: (data.system?.targets.filter(t => t.hit) ?? data.targets), + roll: { formula }, + targets: data.system?.targets.filter(t => t.hit) ?? data.targets, hasSave: this.hasSave, - source: data.system?.source + source: data.system?.source, + event }; - if(this.hasSave) config.onSave = this.save.damageMod; - if(data.system) { + if (this.hasSave) config.onSave = this.save.damageMod; + if (data.system) { config.source.message = data._id; config.directDamage = false; } @@ -575,7 +597,7 @@ export class DHAttackAction extends DHDamageAction { getParentDamage() { return { value: { - multiplier: 'proficiency', + multiplier: 'prof', dice: this.item?.system?.damage.value, bonus: this.item?.system?.damage.bonus ?? 0 }, @@ -594,7 +616,8 @@ export class DHHealingAction extends DHBaseAction { getFormulaValue(data) { let formulaValue = this.healing.value; - if(this.hasRoll && this.healing.resultBased && data.system.roll.result.duality === -1) return this.healing.valueAlt; + if (this.hasRoll && this.healing.resultBased && data.system.roll.result.duality === -1) + return this.healing.valueAlt; return formulaValue; } @@ -605,15 +628,16 @@ export class DHHealingAction extends DHBaseAction { if (!formula || formula == '') return; let roll = { formula: formula, total: formula }, bonusDamage = []; - + const config = { title: game.i18n.format('DAGGERHEART.Chat.HealingRoll.Title', { healing: game.i18n.localize(SYSTEM.GENERAL.healingTypes[this.healing.type].label) }), - formula, + roll: { formula }, targets: (data.system?.targets ?? data.targets).filter(t => t.hit), messageType: 'healing', - type: this.healing.type + type: this.healing.type, + event }; roll = CONFIG.Dice.daggerheart.DamageRoll.build(config); diff --git a/module/data/action/actionDice.mjs b/module/data/action/actionDice.mjs index 3bb51b50..8a6aa12a 100644 --- a/module/data/action/actionDice.mjs +++ b/module/data/action/actionDice.mjs @@ -2,13 +2,63 @@ import FormulaField from '../fields/formulaField.mjs'; const fields = foundry.data.fields; +/* Roll Field */ + +export class DHActionRollData extends foundry.abstract.DataModel { + /** @override */ + static defineSchema() { + return { + type: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.GENERAL.rollTypes }), + trait: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.ACTOR.abilities }), + difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }), + bonus: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }), + diceRolling: new fields.SchemaField({ + multiplier: new fields.StringField({ + choices: SYSTEM.GENERAL.diceSetNumbers, + initial: 'prof', + label: 'Dice Number' + }), + flatMultiplier: new fields.NumberField({ nullable: true, initial: 1, label: 'Flat Multiplier' }), + dice: new fields.StringField({ choices: SYSTEM.GENERAL.diceTypes, initial: 'd6', label: 'Dice Type' }), + compare: new fields.StringField({ + choices: SYSTEM.ACTIONS.diceCompare, + initial: 'above', + label: 'Should be' + }), + treshold: new fields.NumberField({ initial: 1, integer: true, min: 1, label: 'Treshold' }) + }) + }; + } + + getFormula() { + if (!this.type) return; + let formula = ''; + switch (this.type) { + case 'diceSet': + const multiplier = + this.diceRolling.multiplier === 'flat' + ? this.diceRolling.flatMultiplier + : `@${this.diceRolling.multiplier}`; + formula = `${multiplier}${this.diceRolling.dice}cs${SYSTEM.ACTIONS.diceCompare[this.diceRolling.compare].operator}${this.diceRolling.treshold}`; + break; + default: + // formula = `${(!!this.parent?.actor?.system?.attack ? `@attackBonus` : `@traits.${this.trait}.total`)}`; + formula = ''; + break; + } + return formula; + } +} + +/* Damage & Healing Field */ + export class DHActionDiceData extends foundry.abstract.DataModel { /** @override */ static defineSchema() { return { multiplier: new fields.StringField({ choices: SYSTEM.GENERAL.multiplierTypes, - initial: 'proficiency', + initial: 'prof', label: 'Multiplier' }), flatMultiplier: new fields.NumberField({ nullable: true, initial: 1, label: 'Flat Multiplier' }), @@ -22,10 +72,13 @@ export class DHActionDiceData extends foundry.abstract.DataModel { } getFormula(actor) { - const multiplier = this.multiplier === 'flat' ? this.flatMultiplier : actor.system[this.multiplier]?.total; + /* const multiplier = this.multiplier === 'flat' ? this.flatMultiplier : actor.system[this.multiplier]?.total; return this.custom.enabled ? this.custom.formula - : `${multiplier ?? 1}${this.dice}${this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : ''}`; + : `${multiplier ?? 1}${this.dice}${this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : ''}`; */ + const multiplier = this.multiplier === 'flat' ? this.flatMultiplier : `@${this.multiplier}`, + bonus = this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : ''; + return this.custom.enabled ? this.custom.formula : `${multiplier ?? 1}${this.dice}${bonus}`; } } @@ -53,9 +106,12 @@ export class DHDamageData extends foundry.abstract.DataModel { nullable: false, required: true }), - resultBased: new fields.BooleanField({ initial: false, label: "DAGGERHEART.Actions.Settings.ResultBased.label" }), + resultBased: new fields.BooleanField({ + initial: false, + label: 'DAGGERHEART.Actions.Settings.ResultBased.label' + }), value: new fields.EmbeddedDataField(DHActionDiceData), - valueAlt: new fields.EmbeddedDataField(DHActionDiceData), + valueAlt: new fields.EmbeddedDataField(DHActionDiceData) }; } } diff --git a/module/data/actor/adversary.mjs b/module/data/actor/adversary.mjs index 5ac80969..b1ed29ec 100644 --- a/module/data/actor/adversary.mjs +++ b/module/data/actor/adversary.mjs @@ -77,4 +77,8 @@ export default class DhpAdversary extends BaseDataActor { }) }; } + + get attackBonus() { + return this.attack.roll.bonus; + } } diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index 1a3c91ca..47eec324 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -62,7 +62,7 @@ export default class DhCharacter extends BaseDataActor { }), experiences: new fields.TypedObjectField( new fields.SchemaField({ - description: new fields.StringField({}), + name: new fields.StringField(), value: new fields.NumberField({ integer: true, initial: 0 }), bonus: new fields.NumberField({ integer: true, initial: 0 }) }) diff --git a/module/data/chat-message/dualityRoll.mjs b/module/data/chat-message/dualityRoll.mjs index 0f3f12de..1fdb537c 100644 --- a/module/data/chat-message/dualityRoll.mjs +++ b/module/data/chat-message/dualityRoll.mjs @@ -26,6 +26,7 @@ export default class DHDualityRoll extends foundry.abstract.TypeDataModel { }) }) ), + costs: new fields.ArrayField(new fields.ObjectField()), hasDamage: new fields.BooleanField({ initial: false }), hasHealing: new fields.BooleanField({ initial: false }), hasEffect: new fields.BooleanField({ initial: false }), diff --git a/module/data/item/armor.mjs b/module/data/item/armor.mjs index c7f5af0b..9c19fb80 100644 --- a/module/data/item/armor.mjs +++ b/module/data/item/armor.mjs @@ -9,7 +9,8 @@ export default class DHArmor extends BaseDataItem { label: 'TYPES.Item.armor', type: 'armor', hasDescription: true, - isQuantifiable: true + isQuantifiable: true, + isInventoryItem: true }); } diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs index 219b43aa..735c6588 100644 --- a/module/data/item/base.mjs +++ b/module/data/item/base.mjs @@ -7,6 +7,7 @@ import { actionsTypes } from '../action/_module.mjs'; * @property {string} type - The system type that this data model represents. * @property {boolean} hasDescription - Indicates whether items of this type have description field * @property {boolean} isQuantifiable - Indicates whether items of this type have quantity field + * @property {boolean} isInventoryItem- Indicates whether items of this type is a Inventory Item */ const fields = foundry.data.fields; @@ -18,7 +19,8 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { label: 'Base Item', type: 'base', hasDescription: false, - isQuantifiable: false + isQuantifiable: false, + isInventoryItem: false }; } @@ -52,9 +54,9 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { const data = { ...actorRollData, item: { ...this } }; return data; } - + async _preCreate(data, options, user) { - if(!this.constructor.metadata.hasInitialAction || !foundry.utils.isEmpty(this.actions)) return; + if (!this.constructor.metadata.hasInitialAction || !foundry.utils.isEmpty(this.actions)) return; const actionType = { weapon: 'attack' }[this.constructor.metadata.type], @@ -70,6 +72,6 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { parent: this.parent } ); - this.updateSource({actions: [action]}); + this.updateSource({ actions: [action] }); } } diff --git a/module/data/item/consumable.mjs b/module/data/item/consumable.mjs index 6c8df798..3e70f97a 100644 --- a/module/data/item/consumable.mjs +++ b/module/data/item/consumable.mjs @@ -8,7 +8,8 @@ export default class DHConsumable extends BaseDataItem { label: 'TYPES.Item.consumable', type: 'consumable', hasDescription: true, - isQuantifiable: true + isQuantifiable: true, + isInventoryItem: true }); } diff --git a/module/data/item/miscellaneous.mjs b/module/data/item/miscellaneous.mjs index d7687dc7..cad07f48 100644 --- a/module/data/item/miscellaneous.mjs +++ b/module/data/item/miscellaneous.mjs @@ -8,7 +8,8 @@ export default class DHMiscellaneous extends BaseDataItem { label: 'TYPES.Item.miscellaneous', type: 'miscellaneous', hasDescription: true, - isQuantifiable: true + isQuantifiable: true, + isInventoryItem: true }); } diff --git a/module/data/item/weapon.mjs b/module/data/item/weapon.mjs index e7551a21..9154eb31 100644 --- a/module/data/item/weapon.mjs +++ b/module/data/item/weapon.mjs @@ -2,7 +2,7 @@ import BaseDataItem from './base.mjs'; import FormulaField from '../fields/formulaField.mjs'; import ActionField from '../fields/actionField.mjs'; import { weaponFeatures } from '../../config/itemConfig.mjs'; -import { actionsTypes } from '../action/_module.mjs'; +import { actionsTypes } from '../action/_module.mjs'; export default class DHWeapon extends BaseDataItem { /** @inheritDoc */ @@ -12,9 +12,7 @@ export default class DHWeapon extends BaseDataItem { type: 'weapon', hasDescription: true, isQuantifiable: true, - embedded: { - feature: 'featureTest' - }, + isInventoryItem: true, hasInitialAction: true }); } diff --git a/module/data/settings/Homebrew.mjs b/module/data/settings/Homebrew.mjs index 3aef56d6..d26de253 100644 --- a/module/data/settings/Homebrew.mjs +++ b/module/data/settings/Homebrew.mjs @@ -16,6 +16,38 @@ export default class DhHomebrew extends foundry.abstract.DataModel { traitArray: new fields.ArrayField(new fields.NumberField({ required: true, integer: true }), { initial: () => [2, 1, 1, 0, 0, -1] }), + currency: new fields.SchemaField({ + enabled: new fields.BooleanField({ + required: true, + initial: false, + label: 'DAGGERHEART.Settings.Homebrew.Currency.enabled' + }), + title: new fields.StringField({ + required: true, + initial: 'Gold', + label: 'DAGGERHEART.Settings.Homebrew.Currency.currencyName' + }), + coins: new fields.StringField({ + required: true, + initial: 'Coins', + label: 'DAGGERHEART.Settings.Homebrew.Currency.coinName' + }), + handfulls: new fields.StringField({ + required: true, + initial: 'Handfulls', + label: 'DAGGERHEART.Settings.Homebrew.Currency.handfullName' + }), + bags: new fields.StringField({ + required: true, + initial: 'Bags', + label: 'DAGGERHEART.Settings.Homebrew.Currency.bagName' + }), + chests: new fields.StringField({ + required: true, + initial: 'Chests', + label: 'DAGGERHEART.Settings.Homebrew.Currency.chestName' + }) + }), restMoves: new fields.SchemaField({ longRest: new fields.SchemaField({ nrChoices: new fields.NumberField({ required: true, integer: true, min: 1, initial: 2 }), diff --git a/module/dialogs/d20RollDialog.mjs b/module/dialogs/d20RollDialog.mjs index 0b64615b..7c4fd06b 100644 --- a/module/dialogs/d20RollDialog.mjs +++ b/module/dialogs/d20RollDialog.mjs @@ -1,9 +1,10 @@ const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; export default class D20RollDialog extends HandlebarsApplicationMixin(ApplicationV2) { - constructor(config = {}, options = {}) { + constructor(roll, config = {}, options = {}) { super(options); + this.roll = roll; this.config = config; this.config.experiences = []; @@ -59,22 +60,26 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio context.advantage = this.config.advantage; /* context.diceOptions = this.diceOptions; */ context.canRoll = true; + context.isLite = this.config.roll?.lite; if (this.config.costs?.length) { const updatedCosts = this.action.calcCosts(this.config.costs); context.costs = updatedCosts; context.canRoll = this.action.hasCost(updatedCosts); + this.config.data.scale = this.config.costs[0].total; } if (this.config.uses?.max) { context.uses = this.action.calcUses(this.config.uses); context.canRoll = context.canRoll && this.action.hasUses(context.uses); } - console.log(context, _options) + context.formula = this.roll.constructFormula(this.config); return context; } static updateRollConfiguration(event, _, formData) { const { ...rest } = foundry.utils.expandObject(formData.object); - if (this.config.costs) this.config.costs = foundry.utils.mergeObject(this.config.costs, rest.costs); + if (this.config.costs) { + this.config.costs = foundry.utils.mergeObject(this.config.costs, rest.costs); + } if (this.config.uses) this.config.uses = foundry.utils.mergeObject(this.config.uses, rest.uses); this.render(); } @@ -86,11 +91,15 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio } static selectExperience(_, button) { - if (this.config.experiences.find(x => x === button.dataset.key)) { + /* if (this.config.experiences.find(x => x === button.dataset.key)) { this.config.experiences = this.config.experiences.filter(x => x !== button.dataset.key); } else { this.config.experiences = [...this.config.experiences, button.dataset.key]; - } + } */ + this.config.experiences = + this.config.experiences.indexOf(button.dataset.key) > -1 + ? this.config.experiences.filter(x => x !== button.dataset.key) + : [...this.config.experiences, button.dataset.key]; this.render(); } @@ -103,9 +112,9 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio if (!options.submitted) this.config = false; } - static async configure(config = {}, options={}) { + static async configure(roll, config = {}, options = {}) { return new Promise(resolve => { - const app = new this(config, options); + const app = new this(roll, config, options); app.addEventListener('close', () => resolve(app.config), { once: true }); app.render({ force: true }); }); diff --git a/module/dialogs/damageDialog.mjs b/module/dialogs/damageDialog.mjs index 5915f6e2..b94c2aab 100644 --- a/module/dialogs/damageDialog.mjs +++ b/module/dialogs/damageDialog.mjs @@ -1,9 +1,10 @@ const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; export default class DamageDialog extends HandlebarsApplicationMixin(ApplicationV2) { - constructor(config={}, options={}) { + constructor(roll, config = {}, options = {}) { super(options); + this.roll = roll; this.config = config; } @@ -36,24 +37,24 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application async _prepareContext(_options) { const context = await super._prepareContext(_options); context.title = this.config.title; - context.formula = this.config.formula; + context.formula = this.config.roll.formula; return context; } static async submitRoll() { - await this.close({ submitted: true }); + await this.close({ submitted: true }); } /** @override */ - _onClose(options={}) { - if ( !options.submitted ) this.config = false; + _onClose(options = {}) { + if (!options.submitted) this.config = false; } - static async configure(config={}) { + static async configure(roll, config = {}) { return new Promise(resolve => { - const app = new this(config); - app.addEventListener("close", () => resolve(app.config), { once: true }); + const app = new this(roll, config); + app.addEventListener('close', () => resolve(app.config), { once: true }); app.render({ force: true }); }); } -} \ No newline at end of file +} diff --git a/module/documents/_module.mjs b/module/documents/_module.mjs index 03237ee5..e6099009 100644 --- a/module/documents/_module.mjs +++ b/module/documents/_module.mjs @@ -1,4 +1,4 @@ export { default as DhpActor } from './actor.mjs'; -export { default as DhpItem } from './item.mjs'; +export { default as DHItem } from './item.mjs'; export { default as DhpCombat } from './combat.mjs'; export { default as DhActiveEffect } from './activeEffect.mjs'; diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index f13f81cd..c99aaa13 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -17,14 +17,6 @@ export default class DhpActor extends Actor { this.updateSource({ prototypeToken }); } - prepareData() { - super.prepareData(); - } - - async _preUpdate(changed, options, user) { - super._preUpdate(changed, options, user); - } - async updateLevel(newLevel) { if (this.type !== 'character' || newLevel === this.system.levelData.level.changed) return; @@ -274,9 +266,10 @@ export default class DhpActor extends Actor { * @param {object} [config.costs] */ async diceRoll(config) { - config.source = {...(config.source ?? {}), actor: this.uuid}; + config.source = { ...(config.source ?? {}), actor: this.uuid }; config.data = this.getRollData(); - return await this.rollClass.build(config); + const rollClass = config.roll.lite ? CONFIG.Dice.daggerheart['DHRoll'] : this.rollClass; + return await rollClass.build(config); } get rollClass() { @@ -429,13 +422,17 @@ export default class DhpActor extends Actor { break; default: updates.actor.resources[`system.resources.${r.type}.value`] = Math.max( - Math.min(this.system.resources[r.type].value + r.value, this.system.resources[r.type].max), + Math.min( + this.system.resources[r.type].value + r.value, + this.system.resources[r.type].maxTotal ?? this.system.resources[r.type].max + ), 0 ); break; } }); Object.values(updates).forEach(async u => { + console.log(updates, u); if (Object.keys(u.resources).length > 0) { if (game.user.isGM) { await u.target.update(u.resources); diff --git a/module/documents/item.mjs b/module/documents/item.mjs index 195b9c27..8e6dcdc3 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -1,14 +1,8 @@ -export default class DhpItem extends Item { - /** @inheritdoc */ - getEmbeddedDocument(embeddedName, id, { invalid = false, strict = false } = {}) { - const systemEmbeds = this.system.constructor.metadata.embedded ?? {}; - if (embeddedName in systemEmbeds) { - const path = `system.${systemEmbeds[embeddedName]}`; - return foundry.utils.getProperty(this, path).get(id) ?? null; - } - return super.getEmbeddedDocument(embeddedName, id, { invalid, strict }); - } - +/** + * Override and extend the basic Item implementation. + * @extends {foundry.documents.Item} + */ +export default class DHItem extends foundry.documents.Item { /** @inheritDoc */ prepareEmbeddedDocuments() { super.prepareEmbeddedDocuments(); @@ -35,67 +29,51 @@ export default class DhpItem extends Item { return data; } - isInventoryItem() { - return ['weapon', 'armor', 'miscellaneous', 'consumable'].includes(this.type); + /** + * Determine if this item is classified as an inventory item based on its metadata. + * @returns {boolean} Returns `true` if the item is an inventory item. + */ + get isInventoryItem() { + return this.system.constructor.metadata.isInventoryItem ?? false; } - static async createDialog(data = {}, { parent = null, pack = null, ...options } = {}) { - const documentName = this.metadata.name; - const types = game.documentTypes[documentName].filter(t => t !== CONST.BASE_DOCUMENT_TYPE); - let collection; - if (!parent) { - if (pack) collection = game.packs.get(pack); - else collection = game.collections.get(documentName); - } - const folders = collection?._formatFolderSelectOptions() ?? []; - const label = game.i18n.localize(this.metadata.label); - const title = game.i18n.format('DOCUMENT.Create', { type: label }); - const typeObjects = types.reduce((obj, t) => { - const label = CONFIG[documentName]?.typeLabels?.[t] ?? t; - obj[t] = { value: t, label: game.i18n.has(label) ? game.i18n.localize(label) : t }; - return obj; - }, {}); + /** @inheritdoc */ + static async createDialog(data = {}, createOptions = {}, options = {}) { + const { folders, types, template, context = {}, ...dialogOptions } = options; - // Render the document creation form - const html = await foundry.applications.handlebars.renderTemplate( - 'systems/daggerheart/templates/sidebar/documentCreate.hbs', - { - folders, - name: data.name || game.i18n.format('DOCUMENT.New', { type: label }), - folder: data.folder, - hasFolders: folders.length >= 1, - type: data.type || CONFIG[documentName]?.defaultType || typeObjects.armor, - types: { - Items: [typeObjects.armor, typeObjects.weapon, typeObjects.consumable, typeObjects.miscellaneous], - Character: [ - typeObjects.class, - typeObjects.subclass, - typeObjects.ancestry, - typeObjects.community, - typeObjects.feature, - typeObjects.domainCard - ] - }, - hasTypes: types.length > 1 + if (types?.length === 0) { + throw new Error('The array of sub-types to restrict to must not be empty.'); + } + + const documentTypes = this.TYPES.filter(type => type !== 'base' && (!types || types.includes(type))).map( + type => { + const labelKey = CONFIG.Item?.typeLabels?.[type]; + const label = labelKey && game.i18n.has(labelKey) ? game.i18n.localize(labelKey) : type; + + const isInventoryItem = CONFIG.Item.dataModels[type]?.metadata?.isInventoryItem; + const group = + isInventoryItem === true + ? 'Inventory Items' + : isInventoryItem === false + ? 'Character Items' + : 'Other'; + + return { value: type, label, group }; } ); - // Render the confirmation dialog window - return Dialog.prompt({ - title: title, - content: html, - label: title, - callback: html => { - const form = html[0].querySelector('form'); - const fd = new FormDataExtended(form); - foundry.utils.mergeObject(data, fd.object, { inplace: true }); - if (!data.folder) delete data.folder; - if (types.length === 1) data.type = types[0]; - if (!data.name?.trim()) data.name = this.defaultName(); - return this.create(data, { parent, pack, renderSheet: true }); - }, - rejectClose: false, - options + if (!documentTypes.length) { + throw new Error('No document types were permitted to be created.'); + } + + const sortedTypes = documentTypes.sort((a, b) => a.label.localeCompare(b.label, game.i18n.lang)); + + return await super.createDialog(data, createOptions, { + folders, + types, + template, + context: { types: sortedTypes, ...context }, + ...dialogOptions }); } @@ -104,20 +82,16 @@ export default class DhpItem extends Item { 'systems/daggerheart/templates/views/actionSelect.hbs', { actions: this.system.actions } ), - title = 'Select Action', - type = 'div', - data = {}; - return Dialog.prompt({ - title, - // label: title, + title = 'Select Action'; + + return foundry.applications.api.DialogV2.prompt({ + window: { title }, content, - type, - callback: html => { - const form = html[0].querySelector('form'), - fd = new foundry.applications.ux.FormDataExtended(form); - return this.system.actions.find(a => a._id === fd.object.actionId); - }, - rejectClose: false + ok: { + label: title, + callback: (event, button, dialog) => + this.system.actions.find(a => a._id === button.form.elements.actionId.value) + } }); } diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index af3ce16b..7816d0f4 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -225,11 +225,10 @@ export const getDeleteKeys = (property, innerProperty, innerPropertyDefaultValue // Fix on Foundry native formula replacement for DH const nativeReplaceFormulaData = Roll.replaceFormulaData; -Roll.replaceFormulaData = function (formula, data, { missing, warn = false } = {}) { - const terms = [ - { term: 'prof', default: 1 }, - { term: 'cast', default: 1 } - ]; +Roll.replaceFormulaData = function (formula, data = {}, { missing, warn = false } = {}) { + const terms = Object.keys(SYSTEM.GENERAL.multiplierTypes).map(type => { + return { term: type, default: 1 }; + }); formula = terms.reduce((a, c) => a.replaceAll(`@${c.term}`, data[c.term] ?? c.default), formula); return nativeReplaceFormulaData(formula, data, { missing, warn }); }; diff --git a/module/ui/chatLog.mjs b/module/ui/chatLog.mjs index 3f537adf..eeb515cf 100644 --- a/module/ui/chatLog.mjs +++ b/module/ui/chatLog.mjs @@ -154,15 +154,16 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo ? message.system.targets.map(target => game.canvas.tokens.get(target.id)) : Array.from(game.user.targets); - if(message.system.onSave && event.currentTarget.dataset.targetHit) { - console.log(message.system.targets) - const pendingingSaves = message.system.targets.filter(target => target.hit && target.saved.success === null); - if(pendingingSaves.length) { + if (message.system.onSave && event.currentTarget.dataset.targetHit) { + const pendingingSaves = message.system.targets.filter( + target => target.hit && target.saved.success === null + ); + if (pendingingSaves.length) { const confirm = await foundry.applications.api.DialogV2.confirm({ - window: {title: "Pending Reaction Rolls found"}, + window: { title: 'Pending Reaction Rolls found' }, content: `

Some Tokens still need to roll their Reaction Roll.

Are you sure you want to continue ?

Undone reaction rolls will be considered as failed

` }); - if ( !confirm ) return; + if (!confirm) return; } } @@ -170,7 +171,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected')); for (let target of targets) { let damage = message.system.roll.total; - if(message.system.onSave && message.system.targets.find(t => t.id === target.id)?.saved?.success === true) damage = Math.ceil(damage * (SYSTEM.ACTIONS.damageOnSave[message.system.onSave]?.mod ?? 1)); + if (message.system.onSave && message.system.targets.find(t => t.id === target.id)?.saved?.success === true) + damage = Math.ceil(damage * (SYSTEM.ACTIONS.damageOnSave[message.system.onSave]?.mod ?? 1)); + await target.actor.takeDamage(damage, message.system.roll.type); } }; @@ -181,7 +184,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo if (targets.length === 0) ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected')); - + for (var target of targets) { await target.actor.takeHealing([{ value: message.system.roll.total, type: message.system.roll.type }]); } diff --git a/pull_request_template.md b/pull_request_template.md new file mode 100644 index 00000000..194ce9f4 --- /dev/null +++ b/pull_request_template.md @@ -0,0 +1,48 @@ +## Description + +Please include a summary of the change and which issue is fixed (if applicable). Also include relevant context or motivation for the change. + +- Fixes #(issue) +- Closes #(issue) + +## Type of Change + +Please check the relevant options: + +- [ ] Bug fix +- [ ] New feature +- [ ] Code cleanup/refactor +- [ ] Documentation update +- [ ] Test coverage +- [ ] Dependency update +- [ ] Configuration change +- [ ] Other (please describe): + +## How Has This Been Tested? + +Please describe the tests you ran to verify your changes: + +- [ ] Manual testing +- [ ] Other: + +## Screenshots (if applicable) + +Include screenshots or GIFs to help explain your changes visually. + +## Checklist + +- [ ] My code follows the project style guidelines +- [ ] I have performed a self-review of my code +- [ ] I have commented my code where necessary +- [ ] I have made corresponding changes to the documentation +- [ ] My changes generate no new warnings or errors +- [ ] I have added tests that prove my fix or feature works +- [ ] New and existing tests pass locally with my changes + +## Additional Comments + +Add any other context or questions here. + +--- + +> Thank you for your contribution! 🎉 diff --git a/styles/characterCreation.less b/styles/characterCreation.less index 49f7e74a..e6548d21 100644 --- a/styles/characterCreation.less +++ b/styles/characterCreation.less @@ -1,6 +1,7 @@ +@import './less/utils/colors.less'; + .theme-light .daggerheart.dh-style.dialog.character-creation { .tab-navigation nav a .descriptor { - background: red; background-image: url('../assets/parchments/dh-parchment-dark.png'); } .main-selections-container { @@ -15,6 +16,10 @@ .daggerheart.dh-style.dialog.character-creation { .window-content { gap: 16px; + + .tab { + overflow-y: auto; + } } .tab-navigation { diff --git a/styles/daggerheart.css b/styles/daggerheart.css index 0478ee9a..e3128feb 100755 --- a/styles/daggerheart.css +++ b/styles/daggerheart.css @@ -2570,7 +2570,6 @@ div.daggerheart.views.multiclass { opacity: 1; } .theme-light .daggerheart.dh-style.dialog.character-creation .tab-navigation nav a .descriptor { - background: red; background-image: url('../assets/parchments/dh-parchment-dark.png'); } .theme-light .daggerheart.dh-style.dialog.character-creation .main-selections-container .traits-container .suggested-traits-container .suggested-trait-container, @@ -2581,6 +2580,9 @@ div.daggerheart.views.multiclass { .daggerheart.dh-style.dialog.character-creation .window-content { gap: 16px; } +.daggerheart.dh-style.dialog.character-creation .window-content .tab { + overflow-y: auto; +} .daggerheart.dh-style.dialog.character-creation .tab-navigation nav { flex: 1; } @@ -4088,6 +4090,10 @@ div.daggerheart.views.multiclass { .application.sheet.daggerheart.actor.dh-style.character .tab.inventory .search-section .search-bar input:placeholder { color: light-dark(#18162e50, #efe6d850); } +.application.sheet.daggerheart.actor.dh-style.character .tab.inventory .search-section .search-bar input::-webkit-search-cancel-button { + -webkit-appearance: none; + display: none; +} .application.sheet.daggerheart.actor.dh-style.character .tab.inventory .search-section .search-bar .icon { align-content: center; height: 32px; diff --git a/styles/less/actors/character/inventory.less b/styles/less/actors/character/inventory.less index a6caf22b..516b01b0 100644 --- a/styles/less/actors/character/inventory.less +++ b/styles/less/actors/character/inventory.less @@ -30,6 +30,11 @@ &:placeholder { color: light-dark(@dark-blue-50, @beige-50); } + + &::-webkit-search-cancel-button { + -webkit-appearance: none; + display: none; + } } .icon { diff --git a/templates/settings/homebrew-settings.hbs b/templates/settings/homebrew-settings.hbs index 2e6ddb10..e32830fe 100644 --- a/templates/settings/homebrew-settings.hbs +++ b/templates/settings/homebrew-settings.hbs @@ -1,4 +1,4 @@ -
+
{{formGroup settingFields.schema.fields.maxFear value=settingFields._source.maxFear localize=true}}

{{localize "DAGGERHEART.Settings.Homebrew.FIELDS.traitArray.label"}}

@@ -9,7 +9,20 @@
{{/each}} -
+ + +
+ + {{localize "DAGGERHEART.Settings.Homebrew.Currency.title"}} + + {{formGroup settingFields.schema.fields.currency.fields.enabled value=settingFields._source.currency.enabled localize=true}} + {{formGroup settingFields.schema.fields.currency.fields.title value=settingFields._source.currency.title localize=true}} + {{formGroup settingFields.schema.fields.currency.fields.coins value=settingFields._source.currency.coins localize=true}} + {{formGroup settingFields.schema.fields.currency.fields.handfulls value=settingFields._source.currency.handfulls localize=true}} + {{formGroup settingFields.schema.fields.currency.fields.bags value=settingFields._source.currency.bags localize=true}} + {{formGroup settingFields.schema.fields.currency.fields.chests value=settingFields._source.currency.chests localize=true}} + +
{{localize "DAGGERHEART.Settings.Homebrew.DowntimeMoves"}} diff --git a/templates/sheets/actors/character/inventory.hbs b/templates/sheets/actors/character/inventory.hbs index 0d9f312a..3f4b98be 100644 --- a/templates/sheets/actors/character/inventory.hbs +++ b/templates/sheets/actors/character/inventory.hbs @@ -8,11 +8,11 @@
- + - +
{{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'TYPES.Item.weapon') type='weapon' isGlassy=true}} {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'TYPES.Item.armor') type='armor' isGlassy=true}} @@ -20,23 +20,23 @@ {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-fieldset-items.hbs' title=(localize 'TYPES.Item.miscellaneous') type='miscellaneous' isGlassy=true}}
-
+
- {{localize "DAGGERHEART.Sheets.PC.Gold.Coins"}} + {{localize this.inventory.currency.coins}} {{formInput systemFields.gold.fields.coins value=source.system.gold.coins enriched=source.system.gold.coins localize=true toggled=true}}
- {{localize "DAGGERHEART.Sheets.PC.Gold.Handfulls"}} + {{localize this.inventory.currency.handfulls}} {{formInput systemFields.gold.fields.handfulls value=source.system.gold.handfulls enriched=source.system.gold.handfulls localize=true toggled=true}}
- {{localize "DAGGERHEART.Sheets.PC.Gold.Bags"}} + {{localize this.inventory.currency.bags}} {{formInput systemFields.gold.fields.bags value=source.system.gold.bags enriched=source.system.gold.bags localize=true toggled=true}}
- {{localize "DAGGERHEART.Sheets.PC.Gold.Chests"}} + {{localize this.inventory.currency.chests}} {{formInput systemFields.gold.fields.chests value=source.system.gold.chests enriched=source.system.gold.chests localize=true toggled=true}}
+
- \ No newline at end of file diff --git a/templates/sheets/actors/character/loadout.hbs b/templates/sheets/actors/character/loadout.hbs index e80aaa80..de63323c 100644 --- a/templates/sheets/actors/character/loadout.hbs +++ b/templates/sheets/actors/character/loadout.hbs @@ -8,7 +8,7 @@
- +
+ {{/unless}} +
+ +
{{!-- {{#if (not isNpc)}} --}} {{!--