From 3e83730c342f1981c9165cf24aadcdcbf897c49d Mon Sep 17 00:00:00 2001 From: Chris Ryan <73275196+chrisryan10@users.noreply.github.com> Date: Sat, 9 Aug 2025 21:39:19 +1000 Subject: [PATCH 1/7] Implementation of Dice So Nice settings preview #710 (#732) * Fix for #695 * Implements DiceSoNice preview in config settings #710 * Remove incorrect class * Fix Preview button position * Remove extra line * Fix formatting --------- Co-authored-by: Chris Ryan --- .../settings/appearanceSettings.mjs | 20 ++++++++++++++++++- module/config/generalConfig.mjs | 15 +++++++------- styles/less/global/elements.less | 10 ++++++++++ templates/settings/appearance-settings.hbs | 8 +++++++- 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/module/applications/settings/appearanceSettings.mjs b/module/applications/settings/appearanceSettings.mjs index 491c9799..f0310477 100644 --- a/module/applications/settings/appearanceSettings.mjs +++ b/module/applications/settings/appearanceSettings.mjs @@ -1,4 +1,5 @@ import DhAppearance from '../../data/settings/Appearance.mjs'; +import { getDiceSoNicePreset } from '../../config/generalConfig.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; @@ -25,7 +26,8 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App }, actions: { reset: this.reset, - save: this.save + save: this.save, + preview: this.preview }, form: { handler: this.updateData, submitOnChange: true } }; @@ -89,6 +91,22 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App this.render(); } + static async preview() { + const source = this.settings._source.diceSoNice[this.tabGroups.diceSoNice]; + let faces = 'd12'; + switch (this.tabGroups.diceSoNice) { + case 'advantage': + case 'disadvantage': + faces = 'd6'; + } + const preset = await getDiceSoNicePreset(source, faces); + const diceSoNiceRoll = await new Roll(`1${faces}`).evaluate(); + diceSoNiceRoll.dice[0].options.appearance = preset.appearance; + diceSoNiceRoll.dice[0].options.modelFile = preset.modelFile; + + await game.dice3d.showForRoll(diceSoNiceRoll, game.user, false); + } + static async reset() { this.settings = new DhAppearance(); this.render(); diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index 7785b6a6..bd6ef44e 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -494,9 +494,7 @@ export const diceSetNumbers = { flat: 'Flat' }; -export const getDiceSoNicePresets = async (hopeFaces, fearFaces, advantageFaces = 'd6', disadvantageFaces = 'd6') => { - const { diceSoNice } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance); - const getPreset = async (type, faces) => { +export const getDiceSoNicePreset = async (type, faces) => { const system = game.dice3d.DiceFactory.systems.get(type.system).dice.get(faces); if (!system) { ui.notifications.error( @@ -523,11 +521,14 @@ export const getDiceSoNicePresets = async (hopeFaces, fearFaces, advantageFaces }; }; +export const getDiceSoNicePresets = async (hopeFaces, fearFaces, advantageFaces = 'd6', disadvantageFaces = 'd6') => { + const { diceSoNice } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance); + return { - hope: await getPreset(diceSoNice.hope, hopeFaces), - fear: await getPreset(diceSoNice.fear, fearFaces), - advantage: await getPreset(diceSoNice.advantage, advantageFaces), - disadvantage: await getPreset(diceSoNice.disadvantage, disadvantageFaces) + hope: await getDiceSoNicePreset(diceSoNice.hope, hopeFaces), + fear: await getDiceSoNicePreset(diceSoNice.fear, fearFaces), + advantage: await getDiceSoNicePreset(diceSoNice.advantage, advantageFaces), + disadvantage: await getDiceSoNicePreset(diceSoNice.disadvantage, disadvantageFaces) }; }; diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less index 7f8cdd94..688df165 100755 --- a/styles/less/global/elements.less +++ b/styles/less/global/elements.less @@ -571,6 +571,16 @@ white-space: nowrap; } } + + .button-container { + display: grid; + grid-template-columns: 1fr; + gap: 10px; + text-align: center; + display: flex; + justify-content: center; + width: 100% + } } footer { diff --git a/templates/settings/appearance-settings.hbs b/templates/settings/appearance-settings.hbs index e7c1d757..ab30eefd 100644 --- a/templates/settings/appearance-settings.hbs +++ b/templates/settings/appearance-settings.hbs @@ -69,7 +69,13 @@ {{selectOptions diceSoNiceMaterials selected=diceTab.source.material valueAttr="key" labelAttr="name" localize=true}} - +
+ +
+ {{/if}} From 7f3a83564b96a191a9deb563e93de1637c6a7655 Mon Sep 17 00:00:00 2001 From: Murilo Brito <91566541+moliloo@users.noreply.github.com> Date: Sat, 9 Aug 2025 08:39:56 -0300 Subject: [PATCH 2/7] remove very far field from variant rules range section (#730) --- templates/settings/variant-rules.hbs | 1 - 1 file changed, 1 deletion(-) diff --git a/templates/settings/variant-rules.hbs b/templates/settings/variant-rules.hbs index 03027f69..5cc5c90c 100644 --- a/templates/settings/variant-rules.hbs +++ b/templates/settings/variant-rules.hbs @@ -11,7 +11,6 @@ {{formGroup settingFields.schema.fields.rangeMeasurement.fields.veryClose value=settingFields._source.rangeMeasurement.veryClose localize=true}} {{formGroup settingFields.schema.fields.rangeMeasurement.fields.close value=settingFields._source.rangeMeasurement.close localize=true}} {{formGroup settingFields.schema.fields.rangeMeasurement.fields.far value=settingFields._source.rangeMeasurement.far localize=true}} - {{formGroup settingFields.schema.fields.rangeMeasurement.fields.veryFar value=settingFields._source.rangeMeasurement.veryFar localize=true}}
From 1c000c7cfe912c8b30dcdbc42d120eb8b564d137 Mon Sep 17 00:00:00 2001 From: Murilo Brito <91566541+moliloo@users.noreply.github.com> Date: Sat, 9 Aug 2025 08:40:20 -0300 Subject: [PATCH 3/7] fix item list columns and pretier itemBrowser.mjs (#729) --- module/applications/ui/itemBrowser.mjs | 82 +++++++++---------- styles/less/ui/item-browser/item-browser.less | 6 +- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/module/applications/ui/itemBrowser.mjs b/module/applications/ui/itemBrowser.mjs index 69de5249..3763bd36 100644 --- a/module/applications/ui/itemBrowser.mjs +++ b/module/applications/ui/itemBrowser.mjs @@ -17,7 +17,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { this.config = CONFIG.DH.ITEMBROWSER.compendiumConfig; this.presets = options.presets; - if(this.presets?.compendium && this.presets?.folder) + if (this.presets?.compendium && this.presets?.folder) ItemBrowser.selectFolder.call(this, null, null, this.presets.compendium, this.presets.folder); } @@ -26,7 +26,6 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { id: 'itemBrowser', classes: ['daggerheart', 'dh-style', 'dialog', 'compendium-browser'], tag: 'div', - // title: 'Item Browser', window: { frame: true, title: 'Compendium Browser', @@ -41,9 +40,8 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { sortList: this.sortList }, position: { - top: 330, - left: 120, - width: 800, + left: 100, + width: 850, height: 600 } }; @@ -88,16 +86,14 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { /** @inheritDoc */ async _preFirstRender(context, options) { - if(context.presets?.render?.noFolder || context.presets?.render?.lite) - options.position.width = 600; - + if (context.presets?.render?.noFolder || context.presets?.render?.lite) options.position.width = 600; + await super._preFirstRender(context, options); } /** @inheritDoc */ async _preRender(context, options) { - - if(context.presets?.render?.noFolder || context.presets?.render?.lite) + if (context.presets?.render?.noFolder || context.presets?.render?.lite) options.parts.splice(options.parts.indexOf('sidebar'), 1); await super._preRender(context, options); @@ -110,18 +106,17 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { this._createSearchFilter(); this._createFilterInputs(); this._createDragProcess(); - - if(context.presets?.render?.lite) - this.element.classList.add('lite'); - - if(context.presets?.render?.noFolder) - this.element.classList.add('no-folder'); - - if(context.presets?.render?.noFilter) - this.element.classList.add('no-filter'); - if(this.presets?.filter) { - Object.entries(this.presets.filter).forEach(([k,v]) => this.fieldFilter.find(c => c.name === k).value = v.value); + if (context.presets?.render?.lite) this.element.classList.add('lite'); + + if (context.presets?.render?.noFolder) this.element.classList.add('no-folder'); + + if (context.presets?.render?.noFilter) this.element.classList.add('no-filter'); + + if (this.presets?.filter) { + Object.entries(this.presets.filter).forEach( + ([k, v]) => (this.fieldFilter.find(c => c.name === k).value = v.value) + ); await this._onInputFilterBrowser(); } } @@ -198,6 +193,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { formatLabel(item, field) { const property = foundry.utils.getProperty(item, field.key); + if (Array.isArray(property)) property.join(', '); if (typeof field.format !== 'function') return property ?? '-'; return field.format(property); } @@ -315,19 +311,18 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { async _onInputFilterBrowser(event) { this.#filteredItems.browser.input.clear(); - if(event) this.fieldFilter.find(f => f.name === event.target.name).value = event.target.value; + if (event) this.fieldFilter.find(f => f.name === event.target.name).value = event.target.value; for (const li of this.element.querySelectorAll('.item-container')) { const itemUUID = li.dataset.itemUuid, item = this.items.find(i => i.uuid === itemUUID); - - if(!item) continue; + + if (!item) continue; const matchesMenu = this.fieldFilter.length === 0 || - this.fieldFilter.every(f => ( - !f.value && f.value !== false) || - ItemBrowser.evaluateFilter(item, this.createFilterData(f)) + this.fieldFilter.every( + f => (!f.value && f.value !== false) || ItemBrowser.evaluateFilter(item, this.createFilterData(f)) ); if (matchesMenu) this.#filteredItems.browser.input.add(item.id); @@ -335,21 +330,21 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { li.hidden = !(search.has(item.id) && matchesMenu); } } - + /** * Foundry evaluateFilter doesn't allow you to match if filter values are included into item data - * @param {*} obj - * @param {*} filter + * @param {*} obj + * @param {*} filter */ static evaluateFilter(obj, filter) { let docValue = foundry.utils.getProperty(obj, filter.field); let filterValue = filter.value; switch (filter.operator) { - case "contains2": + case 'contains2': filterValue = Array.isArray(filterValue) ? filterValue : [filterValue]; docValue = Array.isArray(docValue) ? docValue : [docValue]; return docValue.some(dv => filterValue.includes(dv)); - case "contains3": + case 'contains3': return docValue.some(f => f.value === filterValue); default: return foundry.applications.ux.SearchFilter.evaluateFilter(obj, filter); @@ -373,30 +368,33 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { this.render({ force: true }); } - static getFolderConfig(folder, property = "columns") { - if(!folder) return []; + static getFolderConfig(folder, property = 'columns') { + if (!folder) return []; return folder[property] ?? CONFIG.DH.ITEMBROWSER.typeConfig[folder.listType]?.[property] ?? []; } static sortList(_, target) { const key = target.dataset.sortKey, - type = !target.dataset.sortType || target.dataset.sortType === "DESC" ? "ASC" : "DESC", - itemListContainer = target.closest(".compendium-results").querySelector(".item-list"), - itemList = itemListContainer.querySelectorAll(".item-container"); + type = !target.dataset.sortType || target.dataset.sortType === 'DESC' ? 'ASC' : 'DESC', + itemListContainer = target.closest('.compendium-results').querySelector('.item-list'), + itemList = itemListContainer.querySelectorAll('.item-container'); - target.closest(".item-list-header").querySelectorAll('[data-sort-key]').forEach(b => b.dataset.sortType = ""); + target + .closest('.item-list-header') + .querySelectorAll('[data-sort-key]') + .forEach(b => (b.dataset.sortType = '')); target.dataset.sortType = type; - + const newOrder = [...itemList].reverse().sort((a, b) => { const aProp = a.querySelector(`[data-item-key="${key}"]`), - bProp = b.querySelector(`[data-item-key="${key}"]`) - if(type === "DESC") { + bProp = b.querySelector(`[data-item-key="${key}"]`); + if (type === 'DESC') { return aProp.innerText < bProp.innerText ? 1 : -1; } else { return aProp.innerText > bProp.innerText ? 1 : -1; } }); - + itemListContainer.replaceChildren(...newOrder); } diff --git a/styles/less/ui/item-browser/item-browser.less b/styles/less/ui/item-browser/item-browser.less index 3b13056b..e7ff3b12 100644 --- a/styles/less/ui/item-browser/item-browser.less +++ b/styles/less/ui/item-browser/item-browser.less @@ -227,14 +227,16 @@ display: flex; > * { - flex: 1; + flex: 2.5; + text-align: center; } .item-list-img { width: 40px; flex: unset; } .item-list-name { - flex-grow: 3 !important; + flex-grow: 3; + text-align: start; } } From 585601c13493ccda4a5e0e1b6189171c0f0c6cdc Mon Sep 17 00:00:00 2001 From: Dapoulp <74197441+Dapoulp@users.noreply.github.com> Date: Sun, 10 Aug 2025 01:20:24 +0200 Subject: [PATCH 4/7] Feature/683 damage dialog options (#735) * Temp solution for specific weapon feature * Add Serrated & Self-Correcting * Remove comments --- module/applications/dialogs/damageDialog.mjs | 2 + .../sheets/api/application-mixin.mjs | 21 +--- module/config/itemConfig.mjs | 39 +----- module/data/actor/character.mjs | 12 -- module/dice/damageRoll.mjs | 111 +++++++++++++++++- module/dice/dhRoll.mjs | 6 + module/dice/dualityRoll.mjs | 2 +- module/helpers/utils.mjs | 20 ++++ ...n_Advanced_Longsword_9xkB3MWXahrsVP4N.json | 2 +- ...eapon_Advanced_Spear_pK6dsNABKKp1CIGN.json | 14 +-- ...n_Improved_Longsword_QyBZ5NxM8F9nCL9s.json | 2 +- ...eapon_Improved_Spear_j5Pt1thLfcvopBij.json | 14 +-- ..._Legendary_Longsword_14abPqQcROJfDChR.json | 2 +- ...apon_Legendary_Spear_4e5pWxi2qohuGsWh.json | 14 +-- .../weapon_Longsword_Iv8BZM1R24QMT72M.json | 2 +- .../weapon_Spear_TF85tKJetUjLwh54.json | 14 +-- styles/less/global/dialog.less | 2 +- .../dialogs/dice-roll/damageSelection.hbs | 16 +++ 18 files changed, 177 insertions(+), 118 deletions(-) diff --git a/module/applications/dialogs/damageDialog.mjs b/module/applications/dialogs/damageDialog.mjs index 2d372725..fbc584e4 100644 --- a/module/applications/dialogs/damageDialog.mjs +++ b/module/applications/dialogs/damageDialog.mjs @@ -56,12 +56,14 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application label, icon })); + context.modifiers = this.config.modifiers; return context; } static updateRollConfiguration(_event, _, formData) { const { ...rest } = foundry.utils.expandObject(formData.object); foundry.utils.mergeObject(this.config.roll, rest.roll); + foundry.utils.mergeObject(this.config.modifiers, rest.modifiers); this.config.selectedRollMode = rest.selectedRollMode; this.render(); diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 7f646460..f35ebb9f 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -333,7 +333,7 @@ export default function DHApplicationMixin(Base) { } ]; - if (usable) + if (usable) { options.unshift({ name: 'DAGGERHEART.GENERAL.damage', icon: 'fa-solid fa-explosion', @@ -353,24 +353,11 @@ export default function DHApplicationMixin(Base) { icon: 'fa-solid fa-burst', condition: target => { const doc = getDocFromElementSync(target); - return doc?.system?.attack?.damage.parts.length || doc?.damage?.parts.length; + return doc && !(doc.type === 'domainCard' && doc.system.inVault); }, - callback: async (target, event) => { - const doc = await getDocFromElement(target), - action = doc?.system?.attack ?? doc; - return action && action.use(event, { byPassRoll: true }); - } + callback: async (target, event) => (await getDocFromElement(target)).use(event) }); - - options.unshift({ - name: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem', - icon: 'fa-solid fa-burst', - condition: target => { - const doc = getDocFromElementSync(target); - return doc && !(doc.type === 'domainCard' && doc.system.inVault); - }, - callback: async (target, event) => (await getDocFromElement(target)).use(event) - }); + } if (toChat) options.push({ diff --git a/module/config/itemConfig.mjs b/module/config/itemConfig.mjs index e9d8de4c..296dc927 100644 --- a/module/config/itemConfig.mjs +++ b/module/config/itemConfig.mjs @@ -1040,16 +1040,6 @@ export const weaponFeatures = { key: 'system.evasion', mode: 2, value: '-1' - }, - { - key: 'system.bonuses.damage.primaryWeapon.extraDice', - mode: 2, - value: '1' - }, - { - key: 'system.rules.weapon.dropLowestDamageDice', - mode: 5, - value: '1' } ] } @@ -1166,18 +1156,7 @@ export const weaponFeatures = { name: 'DAGGERHEART.CONFIG.WeaponFeature.powerful.effects.powerful.name', description: 'DAGGERHEART.CONFIG.WeaponFeature.powerful.effects.powerful.description', img: 'icons/magic/control/buff-flight-wings-runes-red-yellow.webp', - changes: [ - { - key: 'system.bonuses.damage.primaryWeapon.extraDice', - mode: 2, - value: '1' - }, - { - key: 'system.rules.weapon.dropLowestDamageDice', - mode: 5, - value: '1' - } - ] + changes: [] } ] }, @@ -1301,13 +1280,7 @@ export const weaponFeatures = { name: 'DAGGERHEART.CONFIG.WeaponFeature.selfCorrecting.effects.selfCorrecting.name', description: 'DAGGERHEART.CONFIG.WeaponFeature.selfCorrecting.effects.selfCorrecting.description', img: 'icons/weapons/ammunition/arrow-broadhead-glowing-orange.webp', - changes: [ - { - key: 'system.rules.damage.flipMinDiceValue', - mode: 5, - value: 1 - } - ] + changes: [] } ] }, @@ -1319,13 +1292,7 @@ export const weaponFeatures = { name: 'DAGGERHEART.CONFIG.WeaponFeature.serrated.effects.serrated.name', description: 'DAGGERHEART.CONFIG.WeaponFeature.serrated.effects.serrated.description', img: 'icons/weapons/ammunition/arrow-broadhead-glowing-orange.webp', - changes: [ - { - key: 'system.rules.damage.flipMinDiceValue', - mode: 5, - value: 1 - } - ] + changes: [] } ] }, diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index d4544d2c..c40e7e5d 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -287,18 +287,6 @@ export default class DhCharacter extends BaseDataActor { }) }) }), - weapon: new fields.SchemaField({ - /* Unimplemented - -> Should remove the lowest damage dice from weapon damage - -> Reflect this in the chat message somehow so players get feedback that their choice is helping them. - */ - dropLowestDamageDice: new fields.BooleanField({ initial: false }), - /* Unimplemented - -> Should flip any lowest possible dice rolls for weapon damage to highest - -> Reflect this in the chat message somehow so players get feedback that their choice is helping them. - */ - flipMinDiceValue: new fields.BooleanField({ intial: false }) - }), runeWard: new fields.BooleanField({ initial: false }), burden: new fields.SchemaField({ ignore: new fields.BooleanField() diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs index 31458516..34973108 100644 --- a/module/dice/damageRoll.mjs +++ b/module/dice/damageRoll.mjs @@ -102,14 +102,14 @@ export default class DamageRoll extends DHRoll { } constructFormula(config) { - this.options.roll.forEach(part => { + this.options.roll.forEach((part, index) => { part.roll = new Roll(Roll.replaceFormulaData(part.formula, config.data)); - this.constructFormulaPart(config, part); + this.constructFormulaPart(config, part, index); }); return this.options.roll; } - constructFormulaPart(config, part) { + constructFormulaPart(config, part, index) { part.roll.terms = Roll.parse(part.roll.formula, config.data); if (part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) { @@ -120,6 +120,15 @@ export default class DamageRoll extends DHRoll { }); } + /* To Remove When Reaction System */ + if(index === 0 && part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) { + for(const mod in config.modifiers) { + const modifier = config.modifiers[mod]; + if(modifier.beforeCrit === true && (modifier.enabled || modifier.value)) + modifier.callback(part); + } + } + if (part.extraFormula) { part.roll.terms.push( new foundry.dice.terms.OperatorTerm({ operator: '+' }), @@ -132,6 +141,102 @@ export default class DamageRoll extends DHRoll { criticalBonus = tmpRoll.total - this.constructor.calculateTotalModifiers(tmpRoll); part.roll.terms.push(...this.formatModifier(criticalBonus)); } + + /* To Remove When Reaction System */ + if(index === 0 && part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) { + for(const mod in config.modifiers) { + const modifier = config.modifiers[mod]; + if(!modifier.beforeCrit && (modifier.enabled || modifier.value)) + modifier.callback(part); + } + } + return (part.roll._formula = this.constructor.getFormula(part.roll.terms)); } + + /* To Remove When Reaction System */ + static temporaryModifierBuilder(config) { + const mods = {}; + if(config.data?.parent) { + if(config.data.parent.appliedEffects) { + // Bardic Rally + mods.rally = { + label: "DAGGERHEART.CLASS.Feature.rallyDice", + values: config.data?.parent?.appliedEffects.reduce((a, c) => { + const change = c.changes.find(ch => ch.key === 'system.bonuses.rally'); + if (change) a.push({ value: c.id, label: change.value }); + return a; + }, []), + value: null, + beforeCrit: true, + callback: (part) => { + const rallyFaces = config.modifiers.rally.values.find(r => r.value === config.modifiers.rally.value)?.label; + part.roll.terms.push( + new foundry.dice.terms.OperatorTerm({ operator: '+' }), + ...this.parse(`1${rallyFaces}`) + ); + } + }; + } + + const item = config.data.parent.items?.get(config.source.item); + if(item) { + // Massive (Weapon Feature) + if(item.system.itemFeatures.find(f => f.value === "massive")) + mods.massive = { + label: CONFIG.DH.ITEM.weaponFeatures.massive.label, + enabled: true, + callback: (part) => { + part.roll.terms[0].modifiers.push(`kh${part.roll.terms[0].number}`); + part.roll.terms[0].number += 1; + } + }; + + // Powerful (Weapon Feature) + if(item.system.itemFeatures.find(f => f.value === "powerful")) + mods.powerful = { + label: CONFIG.DH.ITEM.weaponFeatures.powerful.label, + enabled: true, + callback: (part) => { + part.roll.terms[0].modifiers.push(`kh${part.roll.terms[0].number}`); + part.roll.terms[0].number += 1; + } + }; + + // Brutal (Weapon Feature) + if(item.system.itemFeatures.find(f => f.value === "brutal")) + mods.brutal = { + label: CONFIG.DH.ITEM.weaponFeatures.brutal.label, + enabled: true, + beforeCrit: true, + callback: (part) => { + part.roll.terms[0].modifiers.push(`x${part.roll.terms[0].faces}`); + } + }; + + // Serrated (Weapon Feature) + if(item.system.itemFeatures.find(f => f.value === "serrated")) + mods.serrated = { + label: CONFIG.DH.ITEM.weaponFeatures.serrated.label, + enabled: true, + callback: (part) => { + part.roll.terms[0].modifiers.push(`sc8`); + } + }; + + // Self-Correcting (Weapon Feature) + if(item.system.itemFeatures.find(f => f.value === "selfCorrecting")) + mods.selfCorrecting = { + label: CONFIG.DH.ITEM.weaponFeatures.selfCorrecting.label, + enabled: true, + callback: (part) => { + part.roll.terms[0].modifiers.push(`sc6`); + } + }; + } + } + + config.modifiers = mods; + return mods; + } } diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index 889b24b3..a785e508 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -36,6 +36,8 @@ export default class DHRoll extends Roll { this.applyKeybindings(config); + this.temporaryModifierBuilder(config); + let roll = new this(config.roll.formula, config.data, config); if (config.dialog.configure !== false) { // Open Roll Dialog @@ -207,6 +209,10 @@ export default class DHRoll extends Roll { } return modifierTotal; } + + static temporaryModifierBuilder(config) { + return {}; + } } export const registerRollDiceHooks = () => { diff --git a/module/dice/dualityRoll.mjs b/module/dice/dualityRoll.mjs index 030b4df2..35bae725 100644 --- a/module/dice/dualityRoll.mjs +++ b/module/dice/dualityRoll.mjs @@ -149,7 +149,7 @@ export default class DualityRoll extends D20Roll { } if (this.rallyFaces) this.terms.push( - new foundry.dice.terms.OperatorTerm({ operator: '+' }), + new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }), new foundry.dice.terms.Die({ faces: this.rallyFaces }) ); } diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 7b588fc7..f1483e31 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -172,6 +172,26 @@ Roll.replaceFormulaData = function (formula, data = {}, { missing, warn = false return nativeReplaceFormulaData(formula, data, { missing, warn }); }; +foundry.dice.terms.Die.MODIFIERS.sc = "selfCorrecting"; + +/** + * Return the configured value as result if 1 is rolled + * Example: 6d6sc6 Roll 6d6, each result of 1 will be changed into 6 + * @param {string} modifier The matched modifier query + */ +foundry.dice.terms.Die.prototype.selfCorrecting = function(modifier) { + const rgx = /(?:sc)([0-9]+)/i; + const match = modifier.match(rgx); + if ( !match ) return false; + let [target] = match.slice(1); + target = parseInt(target); + for ( const r of this.results ) { + if ( r.result === 1 ) { + r.result = target; + } + } +} + export const getDamageKey = damage => { return ['none', 'minor', 'major', 'severe'][damage]; }; diff --git a/src/packs/items/weapons/weapon_Advanced_Longsword_9xkB3MWXahrsVP4N.json b/src/packs/items/weapons/weapon_Advanced_Longsword_9xkB3MWXahrsVP4N.json index 79ce8416..8b44c759 100644 --- a/src/packs/items/weapons/weapon_Advanced_Longsword_9xkB3MWXahrsVP4N.json +++ b/src/packs/items/weapons/weapon_Advanced_Longsword_9xkB3MWXahrsVP4N.json @@ -43,7 +43,7 @@ "parts": [ { "value": { - "dice": "d8", + "dice": "d10", "bonus": 9, "multiplier": "prof", "flatMultiplier": 1, diff --git a/src/packs/items/weapons/weapon_Advanced_Spear_pK6dsNABKKp1CIGN.json b/src/packs/items/weapons/weapon_Advanced_Spear_pK6dsNABKKp1CIGN.json index 433533e3..0bfb4a8f 100644 --- a/src/packs/items/weapons/weapon_Advanced_Spear_pK6dsNABKKp1CIGN.json +++ b/src/packs/items/weapons/weapon_Advanced_Spear_pK6dsNABKKp1CIGN.json @@ -12,15 +12,7 @@ "equipped": false, "secondary": false, "burden": "twoHanded", - "weaponFeatures": [ - { - "value": "cumbersome", - "effectIds": [ - "hl0S2LrBY5Mg69q6" - ], - "actionIds": [] - } - ], + "weaponFeatures": [], "attack": { "name": "Attack", "img": "icons/skills/melee/blood-slash-foam-red.webp", @@ -51,8 +43,8 @@ "parts": [ { "value": { - "dice": "d10", - "bonus": 8, + "dice": "d8", + "bonus": 9, "multiplier": "prof", "flatMultiplier": 1, "custom": { diff --git a/src/packs/items/weapons/weapon_Improved_Longsword_QyBZ5NxM8F9nCL9s.json b/src/packs/items/weapons/weapon_Improved_Longsword_QyBZ5NxM8F9nCL9s.json index 867a563f..b064b1c2 100644 --- a/src/packs/items/weapons/weapon_Improved_Longsword_QyBZ5NxM8F9nCL9s.json +++ b/src/packs/items/weapons/weapon_Improved_Longsword_QyBZ5NxM8F9nCL9s.json @@ -43,7 +43,7 @@ "parts": [ { "value": { - "dice": "d8", + "dice": "d10", "bonus": 6, "multiplier": "prof", "flatMultiplier": 1, diff --git a/src/packs/items/weapons/weapon_Improved_Spear_j5Pt1thLfcvopBij.json b/src/packs/items/weapons/weapon_Improved_Spear_j5Pt1thLfcvopBij.json index beb295d6..367f80ad 100644 --- a/src/packs/items/weapons/weapon_Improved_Spear_j5Pt1thLfcvopBij.json +++ b/src/packs/items/weapons/weapon_Improved_Spear_j5Pt1thLfcvopBij.json @@ -12,15 +12,7 @@ "equipped": false, "secondary": false, "burden": "twoHanded", - "weaponFeatures": [ - { - "value": "cumbersome", - "effectIds": [ - "8twXPJELZpvFWA5K" - ], - "actionIds": [] - } - ], + "weaponFeatures": [], "attack": { "name": "Attack", "img": "icons/skills/melee/blood-slash-foam-red.webp", @@ -51,8 +43,8 @@ "parts": [ { "value": { - "dice": "d10", - "bonus": 5, + "dice": "d8", + "bonus": 6, "multiplier": "prof", "flatMultiplier": 1, "custom": { diff --git a/src/packs/items/weapons/weapon_Legendary_Longsword_14abPqQcROJfDChR.json b/src/packs/items/weapons/weapon_Legendary_Longsword_14abPqQcROJfDChR.json index 186cd1c1..636dd7a5 100644 --- a/src/packs/items/weapons/weapon_Legendary_Longsword_14abPqQcROJfDChR.json +++ b/src/packs/items/weapons/weapon_Legendary_Longsword_14abPqQcROJfDChR.json @@ -43,7 +43,7 @@ "parts": [ { "value": { - "dice": "d8", + "dice": "d10", "bonus": 12, "multiplier": "prof", "flatMultiplier": 1, diff --git a/src/packs/items/weapons/weapon_Legendary_Spear_4e5pWxi2qohuGsWh.json b/src/packs/items/weapons/weapon_Legendary_Spear_4e5pWxi2qohuGsWh.json index c1322de0..bfdcd4eb 100644 --- a/src/packs/items/weapons/weapon_Legendary_Spear_4e5pWxi2qohuGsWh.json +++ b/src/packs/items/weapons/weapon_Legendary_Spear_4e5pWxi2qohuGsWh.json @@ -12,15 +12,7 @@ "equipped": false, "secondary": false, "burden": "twoHanded", - "weaponFeatures": [ - { - "value": "cumbersome", - "effectIds": [ - "f44KWDgCQeKYfccr" - ], - "actionIds": [] - } - ], + "weaponFeatures": [], "attack": { "name": "Attack", "img": "icons/skills/melee/blood-slash-foam-red.webp", @@ -51,8 +43,8 @@ "parts": [ { "value": { - "dice": "d10", - "bonus": 11, + "dice": "d8", + "bonus": 12, "multiplier": "prof", "flatMultiplier": 1, "custom": { diff --git a/src/packs/items/weapons/weapon_Longsword_Iv8BZM1R24QMT72M.json b/src/packs/items/weapons/weapon_Longsword_Iv8BZM1R24QMT72M.json index 54d18b78..c5d9070b 100644 --- a/src/packs/items/weapons/weapon_Longsword_Iv8BZM1R24QMT72M.json +++ b/src/packs/items/weapons/weapon_Longsword_Iv8BZM1R24QMT72M.json @@ -43,7 +43,7 @@ "parts": [ { "value": { - "dice": "d8", + "dice": "d10", "bonus": 3, "multiplier": "prof", "flatMultiplier": 1, diff --git a/src/packs/items/weapons/weapon_Spear_TF85tKJetUjLwh54.json b/src/packs/items/weapons/weapon_Spear_TF85tKJetUjLwh54.json index 6aa8fe8c..77ba9a93 100644 --- a/src/packs/items/weapons/weapon_Spear_TF85tKJetUjLwh54.json +++ b/src/packs/items/weapons/weapon_Spear_TF85tKJetUjLwh54.json @@ -12,15 +12,7 @@ "equipped": false, "secondary": false, "burden": "twoHanded", - "weaponFeatures": [ - { - "value": "cumbersome", - "effectIds": [ - "Z5MnVI8EOOgzRdXC" - ], - "actionIds": [] - } - ], + "weaponFeatures": [], "attack": { "name": "Attack", "img": "icons/skills/melee/blood-slash-foam-red.webp", @@ -51,8 +43,8 @@ "parts": [ { "value": { - "dice": "d10", - "bonus": 2, + "dice": "d8", + "bonus": 3, "multiplier": "prof", "flatMultiplier": 1, "custom": { diff --git a/styles/less/global/dialog.less b/styles/less/global/dialog.less index aaa5c812..701d5025 100644 --- a/styles/less/global/dialog.less +++ b/styles/less/global/dialog.less @@ -53,7 +53,7 @@ font-weight: 500; font-size: 14px; line-height: 17px; - + white-space: nowrap; color: light-dark(@dark, @beige); } diff --git a/templates/dialogs/dice-roll/damageSelection.hbs b/templates/dialogs/dice-roll/damageSelection.hbs index 2967b675..be49906b 100644 --- a/templates/dialogs/dice-roll/damageSelection.hbs +++ b/templates/dialogs/dice-roll/damageSelection.hbs @@ -24,6 +24,22 @@
{{/each}} + {{#if @root.modifiers}} +
+ {{localize "DAGGERHEART.GENERAL.Modifier.plural"}} + {{#each @root.modifiers}} + {{ localize label }} + {{#if (hasProperty this "values")}} + + {{/if}} + {{#if (hasProperty this "enabled")}} + + {{/if}} + {{/each}} +
+ {{/if}}
{{#if directDamage}} + + {{this.selectedResults}}/{{this.maxSelected}} Selected + + +
+ {{#each this.results}} +
+ {{this.result}} + {{#if this.active}} + + + + + {{/if}} +
+ {{/each}} +
+ + {{/each}} +
+ {{/each}} + {{/each}} + \ No newline at end of file diff --git a/templates/dialogs/rerollDialog/footer.hbs b/templates/dialogs/rerollDialog/footer.hbs new file mode 100644 index 00000000..4aff2823 --- /dev/null +++ b/templates/dialogs/rerollDialog/footer.hbs @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/templates/dialogs/rerollDialog/main.hbs b/templates/dialogs/rerollDialog/main.hbs new file mode 100644 index 00000000..6f10ce33 --- /dev/null +++ b/templates/dialogs/rerollDialog/main.hbs @@ -0,0 +1,35 @@ +
+ {{#each damage}} +

{{localize (concat 'DAGGERHEART.CONFIG.HealingType.' @key '.name')}}

+ {{#each this}} +
+ {{#each this}} +
+ + + + {{this.selectedResults}}/{{this.results.length}} Selected + + +
+ {{#each this.results}} +
+ {{this.result}} + {{#if this.active}} + + + + + {{/if}} +
+ {{/each}} +
+
+ {{/each}} +
+ {{/each}} + {{/each}} +
\ No newline at end of file diff --git a/templates/ui/chat/parts/damage-part.hbs b/templates/ui/chat/parts/damage-part.hbs index 7ec8bfd8..232b1303 100644 --- a/templates/ui/chat/parts/damage-part.hbs +++ b/templates/ui/chat/parts/damage-part.hbs @@ -29,7 +29,13 @@ {{#each results}} {{#unless discarded}}
-
{{result}}
+
+ {{#if hasRerolls}}{{/if}} + {{result}} +
{{/unless}} {{/each}}