From c5308617e1880eba81d76b8e932fac169ac0bce6 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sat, 27 Jun 2026 22:31:36 +0200 Subject: [PATCH 01/30] Moved v13 countdown migration over to a migrateData --- module/data/countdowns.mjs | 33 ++++++++++++++++++++ module/systemRegistration/migrations.mjs | 38 ------------------------ 2 files changed, 33 insertions(+), 38 deletions(-) diff --git a/module/data/countdowns.mjs b/module/data/countdowns.mjs index 8e55ed31..ffe4d26b 100644 --- a/module/data/countdowns.mjs +++ b/module/data/countdowns.mjs @@ -28,6 +28,39 @@ export default class DhCountdowns extends foundry.abstract.DataModel { for (const countdownKey of changedCountdowns) foundry.ui.countdowns.changedCountdownsForAnimation.add(countdownKey); } + + static migrateData(source) { + const migrateOldCountdowns = (data, type) => { + for (const key of Object.keys(data.countdowns)) { + const countdown = data.countdowns[key]; + source.countdowns[key] = { + ...countdown, + type: type, + ownership: Object.keys(countdown.ownership.players).reduce((acc, key) => { + acc[key] = + countdown.ownership.players[key].type === 1 ? 2 : countdown.ownership.players[key].type; + return acc; + }, {}), + progress: { + ...countdown.progress, + type: countdown.progress.type.value + } + }; + } + + source[type] = null; + }; + + if (source.narrative) { + migrateOldCountdowns(source.narrative, 'narrative'); + } + + if (source.encounter) { + migrateOldCountdowns(source.encounter, 'encounter'); + } + + return super.migrateData(source); + } } export class DhCountdown extends foundry.abstract.DataModel { diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs index ec546c92..a77c6d8b 100644 --- a/module/systemRegistration/migrations.mjs +++ b/module/systemRegistration/migrations.mjs @@ -1,5 +1,4 @@ import { defaultRestOptions } from '../config/generalConfig.mjs'; -import { RefreshType, socketEvent } from './socket.mjs'; export async function runMigrations() { let lastMigrationVersion = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion); @@ -153,43 +152,6 @@ export async function runMigrations() { await pack.configure({ locked: true }); } - /* Migrate old countdown structure */ - const countdownSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); - const getCountdowns = (data, type) => { - return Object.keys(data.countdowns).reduce((acc, key) => { - const countdown = data.countdowns[key]; - acc[key] = { - ...countdown, - type: type, - ownership: Object.keys(countdown.ownership.players).reduce((acc, key) => { - acc[key] = - countdown.ownership.players[key].type === 1 ? 2 : countdown.ownership.players[key].type; - return acc; - }, {}), - progress: { - ...countdown.progress, - type: countdown.progress.type.value - } - }; - - return acc; - }, {}); - }; - - await countdownSettings.updateSource({ - countdowns: { - ...getCountdowns(countdownSettings.narrative, 'narrative'), - ...getCountdowns(countdownSettings.encounter, 'encounter') - } - }); - await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, countdownSettings); - - game.socket.emit(`system.${CONFIG.DH.id}`, { - action: socketEvent.Refresh, - data: { refreshType: RefreshType.Countdown } - }); - Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Countdown }); - lastMigrationVersion = '1.2.0'; } From f02e97f0cdf6483c63e33aeb11ef915e69b55c72 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sat, 27 Jun 2026 23:10:33 +0200 Subject: [PATCH 02/30] Revert "Moved v13 countdown migration over to a migrateData" This reverts commit c5308617e1880eba81d76b8e932fac169ac0bce6. --- module/data/countdowns.mjs | 33 -------------------- module/systemRegistration/migrations.mjs | 38 ++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 33 deletions(-) diff --git a/module/data/countdowns.mjs b/module/data/countdowns.mjs index ffe4d26b..8e55ed31 100644 --- a/module/data/countdowns.mjs +++ b/module/data/countdowns.mjs @@ -28,39 +28,6 @@ export default class DhCountdowns extends foundry.abstract.DataModel { for (const countdownKey of changedCountdowns) foundry.ui.countdowns.changedCountdownsForAnimation.add(countdownKey); } - - static migrateData(source) { - const migrateOldCountdowns = (data, type) => { - for (const key of Object.keys(data.countdowns)) { - const countdown = data.countdowns[key]; - source.countdowns[key] = { - ...countdown, - type: type, - ownership: Object.keys(countdown.ownership.players).reduce((acc, key) => { - acc[key] = - countdown.ownership.players[key].type === 1 ? 2 : countdown.ownership.players[key].type; - return acc; - }, {}), - progress: { - ...countdown.progress, - type: countdown.progress.type.value - } - }; - } - - source[type] = null; - }; - - if (source.narrative) { - migrateOldCountdowns(source.narrative, 'narrative'); - } - - if (source.encounter) { - migrateOldCountdowns(source.encounter, 'encounter'); - } - - return super.migrateData(source); - } } export class DhCountdown extends foundry.abstract.DataModel { diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs index a77c6d8b..ec546c92 100644 --- a/module/systemRegistration/migrations.mjs +++ b/module/systemRegistration/migrations.mjs @@ -1,4 +1,5 @@ import { defaultRestOptions } from '../config/generalConfig.mjs'; +import { RefreshType, socketEvent } from './socket.mjs'; export async function runMigrations() { let lastMigrationVersion = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion); @@ -152,6 +153,43 @@ export async function runMigrations() { await pack.configure({ locked: true }); } + /* Migrate old countdown structure */ + const countdownSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); + const getCountdowns = (data, type) => { + return Object.keys(data.countdowns).reduce((acc, key) => { + const countdown = data.countdowns[key]; + acc[key] = { + ...countdown, + type: type, + ownership: Object.keys(countdown.ownership.players).reduce((acc, key) => { + acc[key] = + countdown.ownership.players[key].type === 1 ? 2 : countdown.ownership.players[key].type; + return acc; + }, {}), + progress: { + ...countdown.progress, + type: countdown.progress.type.value + } + }; + + return acc; + }, {}); + }; + + await countdownSettings.updateSource({ + countdowns: { + ...getCountdowns(countdownSettings.narrative, 'narrative'), + ...getCountdowns(countdownSettings.encounter, 'encounter') + } + }); + await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, countdownSettings); + + game.socket.emit(`system.${CONFIG.DH.id}`, { + action: socketEvent.Refresh, + data: { refreshType: RefreshType.Countdown } + }); + Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Countdown }); + lastMigrationVersion = '1.2.0'; } From 7409a577f6ab2c3b8bf64380f8548ded1fdab961 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Fri, 3 Jul 2026 00:58:48 -0400 Subject: [PATCH 03/30] Remove certain fieldsets and adjust scrollsbars --- .../applications/sheets/actors/adversary.mjs | 2 +- styles/less/global/elements.less | 2 +- styles/less/global/sheet.less | 2 +- .../sheets/actors/actor-sheet-shared.less | 17 +- .../less/sheets/actors/adversary/effects.less | 5 +- .../sheets/actors/adversary/features.less | 8 +- .../less/sheets/actors/adversary/sheet.less | 2 +- .../sheets/actors/character/biography.less | 11 +- .../less/sheets/actors/character/effects.less | 5 +- .../sheets/actors/character/features.less | 5 +- .../less/sheets/actors/character/header.less | 19 +- .../less/sheets/actors/character/index.less | 2 +- .../sheets/actors/character/inventory.less | 8 +- .../less/sheets/actors/character/loadout.less | 7 +- .../less/sheets/actors/character/sheet.less | 2 +- .../less/sheets/actors/companion/details.less | 5 +- .../less/sheets/actors/companion/effects.less | 5 +- .../sheets/actors/environment/features.less | 8 +- .../less/sheets/actors/environment/index.less | 2 +- .../environment/potentialAdversaries.less | 5 +- .../less/sheets/actors/environment/sheet.less | 2 - .../sheets/actors/party/party-members.less | 3 +- styles/less/sheets/actors/party/sheet.less | 13 +- styles/less/utils/mixin.less | 10 +- .../sheets/actors/adversary/features.hbs | 21 +- .../sheets/actors/character/features.hbs | 50 +-- templates/sheets/actors/character/header.hbs | 326 +++++++++--------- .../sheets/actors/environment/features.hbs | 21 +- 28 files changed, 283 insertions(+), 285 deletions(-) diff --git a/module/applications/sheets/actors/adversary.mjs b/module/applications/sheets/actors/adversary.mjs index bcfe3cbb..b6e17ddd 100644 --- a/module/applications/sheets/actors/adversary.mjs +++ b/module/applications/sheets/actors/adversary.mjs @@ -7,7 +7,7 @@ export default class AdversarySheet extends DHBaseActorSheet { /** @inheritDoc */ static DEFAULT_OPTIONS = { classes: ['adversary'], - position: { width: 645, height: 760 }, + position: { width: 645, height: 750 }, window: { resizable: true }, actions: { toggleHitPoints: AdversarySheet.#toggleHitPoints, diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less index f7934b71..d570c08c 100755 --- a/styles/less/global/elements.less +++ b/styles/less/global/elements.less @@ -261,7 +261,7 @@ fieldset { align-items: center; - margin-top: 5px; + margin: 5px 0 0 0; border-radius: 6px; border-color: @color-fieldset-border; padding-inline: 0.625rem; diff --git a/styles/less/global/sheet.less b/styles/less/global/sheet.less index e3072da1..8381c7c3 100755 --- a/styles/less/global/sheet.less +++ b/styles/less/global/sheet.less @@ -54,7 +54,7 @@ body.game:is(.performance-low, .noblur) { position: relative; min-height: -webkit-fill-available; transition: opacity 0.3s ease; - padding-bottom: 20px; + padding-bottom: 16px; .tab { padding: 0 10px; diff --git a/styles/less/sheets/actors/actor-sheet-shared.less b/styles/less/sheets/actors/actor-sheet-shared.less index 5eb5b43c..37e8579f 100644 --- a/styles/less/sheets/actors/actor-sheet-shared.less +++ b/styles/less/sheets/actors/actor-sheet-shared.less @@ -39,6 +39,20 @@ .window-header > .attribution-header-label { margin-right: var(--spacer-4); + pointer-events: none; + } + + .tab-navigation { + margin-bottom: 0; + } + + .tab { + flex: 1; + padding: 0; + overflow: hidden; + .search-section { + padding: 12px 14px var(--spacer-8) 12px; + } } .tab.inventory { @@ -46,7 +60,7 @@ display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 10px; - padding: 10px 10px 0; + padding: var(--spacer-8) 16px var(--spacer-8) 16px; .input { color: light-dark(@dark, @beige); @@ -57,7 +71,6 @@ .tab.notes.active { padding: 0; margin: 0; - margin-top: -10px; // will be removed once tab-navigation bottom margin is removed on all actor sheets scrollbar-gutter: unset; // Add padding around top level level prosemirrors used for note tabs diff --git a/styles/less/sheets/actors/adversary/effects.less b/styles/less/sheets/actors/adversary/effects.less index 4aa44e51..f489bee6 100644 --- a/styles/less/sheets/actors/adversary/effects.less +++ b/styles/less/sheets/actors/adversary/effects.less @@ -7,9 +7,8 @@ display: flex; flex-direction: column; gap: 10px; - overflow-y: auto; - padding-bottom: 20px; - .with-scroll-shadows(); + padding: 7px calc(12px - var(--scrollbar-width)) 4px 12px; + .stable-scroll-container(); } } } diff --git a/styles/less/sheets/actors/adversary/features.less b/styles/less/sheets/actors/adversary/features.less index 447d050e..49289d36 100644 --- a/styles/less/sheets/actors/adversary/features.less +++ b/styles/less/sheets/actors/adversary/features.less @@ -5,12 +5,8 @@ .application.sheet.daggerheart.actor.dh-style.adversary { .tab.features { .feature-section { - display: flex; - flex-direction: column; - gap: 10px; - overflow-y: auto; - padding-bottom: 20px; - .with-scroll-shadows(); + padding: 16px calc(16px - var(--scrollbar-width)) 4px 16px; + .stable-scroll-container(); } } } diff --git a/styles/less/sheets/actors/adversary/sheet.less b/styles/less/sheets/actors/adversary/sheet.less index 639af98b..1eb7d423 100644 --- a/styles/less/sheets/actors/adversary/sheet.less +++ b/styles/less/sheets/actors/adversary/sheet.less @@ -30,9 +30,9 @@ grid-row: 2; grid-column: 2; &.active { - overflow: hidden; display: flex; flex-direction: column; + margin: 0 0 10px 0; } } } \ No newline at end of file diff --git a/styles/less/sheets/actors/character/biography.less b/styles/less/sheets/actors/character/biography.less index 8548a2fb..9782a588 100644 --- a/styles/less/sheets/actors/character/biography.less +++ b/styles/less/sheets/actors/character/biography.less @@ -8,17 +8,14 @@ display: flex; flex-direction: column; gap: 10px; - height: 100%; - overflow-y: auto; - padding-top: 8px; - padding-bottom: 20px; - height: 100%; - .with-scroll-shadows(); + height: 100%; + padding: 12px calc(12px - var(--scrollbar-width)) 4px 12px; + .stable-scroll-container(); } .characteristics-section { gap: 20px; - padding: 0 10px; + padding: 0 4px; } .biography-section { diff --git a/styles/less/sheets/actors/character/effects.less b/styles/less/sheets/actors/character/effects.less index 0ab1007d..8fac301c 100644 --- a/styles/less/sheets/actors/character/effects.less +++ b/styles/less/sheets/actors/character/effects.less @@ -8,9 +8,8 @@ display: flex; flex-direction: column; gap: 10px; - overflow-y: auto; - padding-bottom: 20px; - .with-scroll-shadows(); + padding: 7px calc(12px - var(--scrollbar-width)) 4px 12px; + .stable-scroll-container(); } } } diff --git a/styles/less/sheets/actors/character/features.less b/styles/less/sheets/actors/character/features.less index 52b41826..fcde3e38 100644 --- a/styles/less/sheets/actors/character/features.less +++ b/styles/less/sheets/actors/character/features.less @@ -8,9 +8,8 @@ display: flex; flex-direction: column; gap: 10px; - overflow-y: auto; - padding-bottom: 20px; - .with-scroll-shadows(); + padding: 7px calc(12px - var(--scrollbar-width)) 4px 12px; + .stable-scroll-container(); } } } diff --git a/styles/less/sheets/actors/character/header.less b/styles/less/sheets/actors/character/header.less index 91b3545a..81345715 100644 --- a/styles/less/sheets/actors/character/header.less +++ b/styles/less/sheets/actors/character/header.less @@ -19,16 +19,19 @@ .application.sheet.daggerheart.actor.dh-style.character { .character-header-sheet { - padding: 0 15px; padding-top: var(--header-height); width: 100%; + > *:not(line-div, .tab-navigation) { + padding-left: 15px; + padding-right: 15px; + } + .name-row { display: flex; gap: 6px; align-items: start; justify-content: space-between; - padding: 0; padding-top: 5px; flex: 1; @@ -100,8 +103,8 @@ .character-details { display: flex; justify-content: space-between; - padding: 5px 0; - margin-bottom: 8px; + margin-top: 5px; + margin-bottom: 10px; font-size: var(--font-size-12); color: @color-text-emphatic; @@ -130,7 +133,6 @@ .character-row { display: flex; align-items: center; - padding: 0; margin-bottom: 12px; .resource-section { @@ -218,12 +220,11 @@ .character-traits { display: flex; - padding: 0; margin-bottom: 15px; justify-content: space-between; max-width: 38.5rem; gap: 0.5rem; - padding-left: 0.5rem; + margin-left: 0.5rem; .trait { cursor: pointer; @@ -325,5 +326,9 @@ } } } + + .tab-navigation button[data-action="openSettings"] { + margin-right: 12px; + } } } diff --git a/styles/less/sheets/actors/character/index.less b/styles/less/sheets/actors/character/index.less index edefe0a1..f196d5bf 100644 --- a/styles/less/sheets/actors/character/index.less +++ b/styles/less/sheets/actors/character/index.less @@ -1,8 +1,8 @@ +@import './sheet.less'; @import './biography.less'; @import './effects.less'; @import './features.less'; @import './header.less'; @import './inventory.less'; @import './loadout.less'; -@import './sheet.less'; @import './sidebar.less'; diff --git a/styles/less/sheets/actors/character/inventory.less b/styles/less/sheets/actors/character/inventory.less index fcfbbee9..ce7a8cdb 100644 --- a/styles/less/sheets/actors/character/inventory.less +++ b/styles/less/sheets/actors/character/inventory.less @@ -7,11 +7,9 @@ .items-section { display: flex; flex-direction: column; - gap: 10px; - overflow-y: auto; - margin-top: 20px; - padding-bottom: 20px; - .with-scroll-shadows(); + gap: 10px; + padding: 8px calc(12px - var(--scrollbar-width)) 4px 12px; + .stable-scroll-container(); } } } diff --git a/styles/less/sheets/actors/character/loadout.less b/styles/less/sheets/actors/character/loadout.less index fa3e0176..cb1baaa6 100644 --- a/styles/less/sheets/actors/character/loadout.less +++ b/styles/less/sheets/actors/character/loadout.less @@ -50,11 +50,8 @@ display: flex; flex-direction: column; gap: 10px; - height: 100%; - overflow-y: auto; - margin-top: 20px; - padding-bottom: 20px; - .with-scroll-shadows(); + padding: 8px calc(12px - var(--scrollbar-width)) 4px 12px; + .stable-scroll-container(); } } } diff --git a/styles/less/sheets/actors/character/sheet.less b/styles/less/sheets/actors/character/sheet.less index 68792c99..43401b19 100644 --- a/styles/less/sheets/actors/character/sheet.less +++ b/styles/less/sheets/actors/character/sheet.less @@ -30,7 +30,7 @@ &.active { display: flex; flex-direction: column; - overflow: hidden; + margin: 0 0 10px 0; } } } diff --git a/styles/less/sheets/actors/companion/details.less b/styles/less/sheets/actors/companion/details.less index 2e43cac4..e10e7680 100644 --- a/styles/less/sheets/actors/companion/details.less +++ b/styles/less/sheets/actors/companion/details.less @@ -1,7 +1,10 @@ @import '../../../utils/colors.less'; @import '../../../utils/fonts.less'; -.application.sheet.daggerheart.actor.dh-style.companion { +.application.sheet.daggerheart.actor.dh-style.companion .tab.details.active { + padding: 12px calc(12px - var(--scrollbar-width)) 4px 12px; + .stable-scroll-container(); + .partner-section, .attack-section, .experience-list { diff --git a/styles/less/sheets/actors/companion/effects.less b/styles/less/sheets/actors/companion/effects.less index c0cac669..ffe66da8 100644 --- a/styles/less/sheets/actors/companion/effects.less +++ b/styles/less/sheets/actors/companion/effects.less @@ -6,9 +6,8 @@ display: flex; flex-direction: column; gap: 10px; - overflow-y: auto; - padding-bottom: 20px; - .with-scroll-shadows(); + padding: 7px calc(12px - var(--scrollbar-width)) 4px 12px; + .stable-scroll-container(); } } } diff --git a/styles/less/sheets/actors/environment/features.less b/styles/less/sheets/actors/environment/features.less index 84cf26f8..c6fe3750 100644 --- a/styles/less/sheets/actors/environment/features.less +++ b/styles/less/sheets/actors/environment/features.less @@ -5,12 +5,8 @@ .application.sheet.daggerheart.actor.dh-style.environment { .tab.features { .feature-section { - display: flex; - flex-direction: column; - gap: 10px; - overflow-y: auto; - padding-bottom: 4px; - .with-scroll-shadows(); + padding: 16px calc(16px - var(--scrollbar-width)) 4px 16px; + .stable-scroll-container(); } } } diff --git a/styles/less/sheets/actors/environment/index.less b/styles/less/sheets/actors/environment/index.less index 211c8e60..df7e6fc2 100644 --- a/styles/less/sheets/actors/environment/index.less +++ b/styles/less/sheets/actors/environment/index.less @@ -1,4 +1,4 @@ +@import './sheet.less'; @import './features.less'; @import './header.less'; @import './potentialAdversaries.less'; -@import './sheet.less'; diff --git a/styles/less/sheets/actors/environment/potentialAdversaries.less b/styles/less/sheets/actors/environment/potentialAdversaries.less index f112c0d2..07dc5f92 100644 --- a/styles/less/sheets/actors/environment/potentialAdversaries.less +++ b/styles/less/sheets/actors/environment/potentialAdversaries.less @@ -6,9 +6,8 @@ display: flex; flex-direction: column; gap: 10px; - overflow-y: auto; - padding-bottom: 4px; - .with-scroll-shadows(); + padding: 7px calc(12px - var(--scrollbar-width)) 4px 12px; + .stable-scroll-container(); } } } diff --git a/styles/less/sheets/actors/environment/sheet.less b/styles/less/sheets/actors/environment/sheet.less index 2d9cc188..ef9d542a 100644 --- a/styles/less/sheets/actors/environment/sheet.less +++ b/styles/less/sheets/actors/environment/sheet.less @@ -14,9 +14,7 @@ .application.sheet.daggerheart.actor.dh-style.environment { .tab { - flex: 1; overflow-y: auto; - &.active { overflow: hidden; display: flex; diff --git a/styles/less/sheets/actors/party/party-members.less b/styles/less/sheets/actors/party/party-members.less index 3d882345..ca384322 100644 --- a/styles/less/sheets/actors/party/party-members.less +++ b/styles/less/sheets/actors/party/party-members.less @@ -3,7 +3,8 @@ @import '../../../utils/mixin.less'; .application.sheet.daggerheart.actor.dh-style.party .tab.partyMembers { - overflow: auto; + padding: 12px calc(12px - var(--scrollbar-width)) 4px 12px; + .stable-scroll-container(); .actions-section { display: flex; diff --git a/styles/less/sheets/actors/party/sheet.less b/styles/less/sheets/actors/party/sheet.less index d24c712c..bf589696 100644 --- a/styles/less/sheets/actors/party/sheet.less +++ b/styles/less/sheets/actors/party/sheet.less @@ -17,15 +17,8 @@ }); .application.sheet.daggerheart.actor.dh-style.party { - .tab { - flex: 1; - overflow-y: auto; - scrollbar-gutter: stable; - - &.active { - overflow: auto; - display: flex; - flex-direction: column; - } + .tab.active { + display: flex; + flex-direction: column; } } diff --git a/styles/less/utils/mixin.less b/styles/less/utils/mixin.less index e2ef85ef..2ce85166 100644 --- a/styles/less/utils/mixin.less +++ b/styles/less/utils/mixin.less @@ -174,10 +174,10 @@ --fade-start: 0; } 10%, 100% { - --fade-start: 12px; + --fade-start: 14px; } 0%, 90% { - --fade-end: 12px; + --fade-end: 14px; } 100% { --fade-end: 0; @@ -198,3 +198,9 @@ transparent 100% ); } + +.stable-scroll-container() { + overflow-y: auto; + scrollbar-gutter: stable; + .with-scroll-shadows(); +} \ No newline at end of file diff --git a/templates/sheets/actors/adversary/features.hbs b/templates/sheets/actors/adversary/features.hbs index 3b495e74..05e95737 100644 --- a/templates/sheets/actors/adversary/features.hbs +++ b/templates/sheets/actors/adversary/features.hbs @@ -1,14 +1,15 @@
-
- {{> 'daggerheart.inventory-items' - title=tabs.features.label - type='feature' - collection=@root.features - hideContextMenu=true - hideModifyControls=true - canCreate=@root.editable - showActions=@root.editable - }} +
+ {{#each @root.features as |item|}} + {{> "daggerheart.inventory-item" + item=item + type="feature" + actorType=@root.document.type + hideContextMenu=true + hideModifyControls=true + showActions=@root.editable + }} + {{/each}}
\ No newline at end of file diff --git a/templates/sheets/actors/character/features.hbs b/templates/sheets/actors/character/features.hbs index b2760900..c96ab623 100644 --- a/templates/sheets/actors/character/features.hbs +++ b/templates/sheets/actors/character/features.hbs @@ -1,26 +1,26 @@ -
-
- {{#each document.system.sheetLists as |category|}} - {{#if (eq category.type 'feature' )}} - {{> 'daggerheart.inventory-items' - title=category.title - type='feature' - actorType='character' - collection=category.values - canCreate=@root.editable - showActions=@root.editable - }} - {{else if category.values}} - {{> 'daggerheart.inventory-items' - title=category.title - type='feature' - actorType='character' - collection=category.values - canCreate=false - showActions=@root.editable - }} - {{/if}} - {{/each}} -
+
+
+ {{#each document.system.sheetLists as |category|}} + {{#if (eq category.type 'feature' )}} + {{> 'daggerheart.inventory-items' + title=category.title + type='feature' + actorType='character' + collection=category.values + canCreate=@root.editable + showActions=@root.editable + }} + {{else if category.values}} + {{> 'daggerheart.inventory-items' + title=category.title + type='feature' + actorType='character' + collection=category.values + canCreate=false + showActions=@root.editable + }} + {{/if}} + {{/each}} +
\ No newline at end of file diff --git a/templates/sheets/actors/character/header.hbs b/templates/sheets/actors/character/header.hbs index 459911af..a40c336a 100644 --- a/templates/sheets/actors/character/header.hbs +++ b/templates/sheets/actors/character/header.hbs @@ -1,165 +1,163 @@ -
- -
-

{{source.name}}

-
-

- {{#if @root.editable}} - {{#if document.system.needsCharacterSetup}} - - {{else if document.system.levelData.canLevelUp}} - - {{/if}} - {{/if}} - {{#unless document.system.needsCharacterSetup}} - {{localize 'DAGGERHEART.GENERAL.level'}} - - {{/unless}} -

-
-
-
-
- {{#if document.system.class.value}} - {{document.system.class.value.name}} - {{else}} - {{localize 'TYPES.Item.class'}} - {{/if}} - - {{#if document.system.class.subclass}} - {{document.system.class.subclass.name}} - {{else}} - {{localize 'TYPES.Item.subclass'}} - {{/if}} - - {{#if document.system.community}} - {{document.system.community.name}} - {{else}} - {{localize 'TYPES.Item.community'}} - {{/if}} - - {{#if document.system.ancestry}} - {{document.system.ancestry.name}} - {{else}} - {{localize 'TYPES.Item.ancestry'}} - {{/if}} -
- - {{#if (or document.system.multiclass.value document.system.multiclass.subclass)}} -
- {{#if document.system.multiclass.value}} - {{document.system.multiclass.value.name}} - {{else}} - {{localize 'DAGGERHEART.GENERAL.multiclass'}} - {{/if}} - - {{#if document.system.multiclass.subclass}} - {{document.system.multiclass.subclass.name}} - {{else}} - {{localize 'TYPES.Item.subclass'}} - {{/if}} -
- {{/if}} - - -
- -
-
-
-

{{localize "DAGGERHEART.GENERAL.hope"}}

- {{#times document.system.resources.hope.max}} - - {{#if (gte ../document.system.resources.hope.value (add this 1))}} - - {{else}} - - {{/if}} - - {{/times}} - {{#times document.system.scars}} - - - - {{/times}} - {{#if hasExtraResources}}{{/if}} -
-
- {{#if document.system.class.value}} -
- {{#each document.system.domainData as |data|}} -
- -
- {{/each}} -
- {{/if}} -
- {{#if document.parties.size}} - - {{/if}} - {{#if @root.editable}} - - - {{/if}} -
-
- -
- {{#each this.attributes as |attribute key|}} -
-
-
- {{attribute.label}} -
-
- - - - -
- {{#if (gt attribute.value 0)}} - +{{attribute.value}} - {{else}} - {{attribute.value}} - {{/if}} -
- {{#if isSpellcasting}} -
- -
- {{/if}} -
-
- {{/each}} -
- - {{#> 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }} - - {{/'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs'}} +
+ +
+

{{source.name}}

+
+

+ {{#if @root.editable}} + {{#if document.system.needsCharacterSetup}} + + {{else if document.system.levelData.canLevelUp}} + + {{/if}} + {{/if}} + {{#unless document.system.needsCharacterSetup}} + {{localize 'DAGGERHEART.GENERAL.level'}} + + {{/unless}} +

+
+
+
+
+ {{#if document.system.class.value}} + {{document.system.class.value.name}} + {{else}} + {{localize 'TYPES.Item.class'}} + {{/if}} + + {{#if document.system.class.subclass}} + {{document.system.class.subclass.name}} + {{else}} + {{localize 'TYPES.Item.subclass'}} + {{/if}} + + {{#if document.system.community}} + {{document.system.community.name}} + {{else}} + {{localize 'TYPES.Item.community'}} + {{/if}} + + {{#if document.system.ancestry}} + {{document.system.ancestry.name}} + {{else}} + {{localize 'TYPES.Item.ancestry'}} + {{/if}} +
+ + {{#if (or document.system.multiclass.value document.system.multiclass.subclass)}} +
+ {{#if document.system.multiclass.value}} + {{document.system.multiclass.value.name}} + {{else}} + {{localize 'DAGGERHEART.GENERAL.multiclass'}} + {{/if}} + + {{#if document.system.multiclass.subclass}} + {{document.system.multiclass.subclass.name}} + {{else}} + {{localize 'TYPES.Item.subclass'}} + {{/if}} +
+ {{/if}} +
+ +
+
+
+

{{localize "DAGGERHEART.GENERAL.hope"}}

+ {{#times document.system.resources.hope.max}} + + {{#if (gte ../document.system.resources.hope.value (add this 1))}} + + {{else}} + + {{/if}} + + {{/times}} + {{#times document.system.scars}} + + + + {{/times}} + {{#if hasExtraResources}}{{/if}} +
+
+ {{#if document.system.class.value}} +
+ {{#each document.system.domainData as |data|}} +
+ +
+ {{/each}} +
+ {{/if}} +
+ {{#if document.parties.size}} + + {{/if}} + {{#if @root.editable}} + + + {{/if}} +
+
+ +
+ {{#each this.attributes as |attribute key|}} +
+
+
+ {{attribute.label}} +
+
+ + + + +
+ {{#if (gt attribute.value 0)}} + +{{attribute.value}} + {{else}} + {{attribute.value}} + {{/if}} +
+ {{#if isSpellcasting}} +
+ +
+ {{/if}} +
+
+ {{/each}} +
+ + {{#> 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }} + + {{/'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs'}}
\ No newline at end of file diff --git a/templates/sheets/actors/environment/features.hbs b/templates/sheets/actors/environment/features.hbs index 35fcb038..85fa7066 100644 --- a/templates/sheets/actors/environment/features.hbs +++ b/templates/sheets/actors/environment/features.hbs @@ -3,15 +3,16 @@ data-tab='{{tabs.features.id}}' data-group='{{tabs.features.group}}' > -
- {{> 'daggerheart.inventory-items' - title=tabs.features.label - type='feature' - collection=@root.features - hideContextMenu=true - hideModifyControls=true - canCreate=@root.editable - showActions=@root.editable - }} +
+ {{#each @root.features as |item|}} + {{> "daggerheart.inventory-item" + item=item + type="feature" + actorType=@root.document.type + hideContextMenu=true + hideModifyControls=true + showActions=@root.editable + }} + {{/each}}
\ No newline at end of file From b73c70aeb842ab2bdc03a12d21451cb9f287d5df Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Fri, 3 Jul 2026 01:42:34 -0400 Subject: [PATCH 04/30] Also remove fieldset for npc features and fix padding issue in full screen notes --- .../sheets/actors/actor-sheet-shared.less | 3 ++- styles/less/sheets/actors/npc/features.less | 8 ++----- templates/sheets/actors/npc/features.hbs | 21 ++++++++++--------- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/styles/less/sheets/actors/actor-sheet-shared.less b/styles/less/sheets/actors/actor-sheet-shared.less index 37e8579f..3e233013 100644 --- a/styles/less/sheets/actors/actor-sheet-shared.less +++ b/styles/less/sheets/actors/actor-sheet-shared.less @@ -79,6 +79,7 @@ .editor-content { scrollbar-gutter: stable; padding-right: @right-padding; + padding-bottom: 4px; } &.inactive { button.toggle { @@ -89,7 +90,7 @@ } } &.active { - padding: 8px 0 4px 16px; + padding: 8px 0 0 16px; } } diff --git a/styles/less/sheets/actors/npc/features.less b/styles/less/sheets/actors/npc/features.less index a579d9f8..f68df8a8 100644 --- a/styles/less/sheets/actors/npc/features.less +++ b/styles/less/sheets/actors/npc/features.less @@ -7,12 +7,8 @@ } .feature-section { - display: flex; - flex-direction: column; - gap: 10px; - overflow-y: auto; - padding-bottom: 4px; - .with-scroll-shadows(); + padding: 16px calc(16px - var(--scrollbar-width)) 4px 16px; + .stable-scroll-container(); } } } diff --git a/templates/sheets/actors/npc/features.hbs b/templates/sheets/actors/npc/features.hbs index 3b495e74..05e95737 100644 --- a/templates/sheets/actors/npc/features.hbs +++ b/templates/sheets/actors/npc/features.hbs @@ -1,14 +1,15 @@
-
- {{> 'daggerheart.inventory-items' - title=tabs.features.label - type='feature' - collection=@root.features - hideContextMenu=true - hideModifyControls=true - canCreate=@root.editable - showActions=@root.editable - }} +
+ {{#each @root.features as |item|}} + {{> "daggerheart.inventory-item" + item=item + type="feature" + actorType=@root.document.type + hideContextMenu=true + hideModifyControls=true + showActions=@root.editable + }} + {{/each}}
\ No newline at end of file From 0fbfe388b0cd32f77894f38ab57f12f54c6e51c2 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Fri, 10 Jul 2026 19:52:50 -0400 Subject: [PATCH 05/30] Remove postEvaluate static method (#2073) --- module/dice/d20Roll.mjs | 7 +++--- module/dice/damageRoll.mjs | 36 ++++++++++++++++------------ module/dice/dhRoll.mjs | 47 ++++++++++++++++++++----------------- module/dice/dualityRoll.mjs | 35 ++++++++++++--------------- module/dice/fateRoll.mjs | 22 ++++++++--------- 5 files changed, 76 insertions(+), 71 deletions(-) diff --git a/module/dice/d20Roll.mjs b/module/dice/d20Roll.mjs index e9c447e4..7dc06741 100644 --- a/module/dice/d20Roll.mjs +++ b/module/dice/d20Roll.mjs @@ -185,8 +185,10 @@ export default class D20Roll extends DHRoll { return changeKeys; } - static postEvaluate(roll, config = {}) { - const data = super.postEvaluate(roll, config); + static async buildEvaluate(roll, config = {}, message = {}) { + await super.buildEvaluate(roll, config, message); + + const data = config.roll; data.type = config.actionType; data.difficulty = config.roll.difficulty; if (config.targets?.length) { @@ -222,7 +224,6 @@ export default class D20Roll extends DHRoll { }; }); data.modifierTotal = roll.modifierTotal; - return data; } resetFormula() { diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs index ef810ed7..3f2f79e0 100644 --- a/module/dice/damageRoll.mjs +++ b/module/dice/damageRoll.mjs @@ -13,32 +13,38 @@ export default class DamageRoll extends DHRoll { static DefaultDialog = DamageDialog; + /** @inheritdoc */ static async buildEvaluate(roll, config = {}, message = {}) { if (config.dialog.configure === false) roll.constructFormula(config); - if (config.evaluate !== false) for (const roll of config.roll) await roll.roll.evaluate(); + for (const roll of config.roll) await roll.roll.evaluate(); roll._evaluated = true; const parts = []; - for (const roll of config.roll) { - parts.push(this.postEvaluate(roll)); - roll.roll = JSON.stringify(roll.roll.toJSON()); + for (const rollData of config.roll) { + const roll = rollData.roll; + parts.push({ + ...rollData, + ...roll.options.roll, + total: roll.total, + formula: roll.formula, + dice: roll.dice.map(d => ({ + dice: d.denomination, + total: d.total, + formula: d.formula, + results: d.results + })), + damageTypes: [...(rollData.damageTypes ?? [])], + roll, + type: config.type, + modifierTotal: this.calculateTotalModifiers(roll) + }); + rollData.roll = JSON.stringify(roll.toJSON()); } config.damage = this.unifyDamageRoll(parts); } - static postEvaluate(roll, config = {}) { - return { - ...roll, - ...super.postEvaluate(roll.roll, config), - damageTypes: [...(roll.damageTypes ?? [])], - roll: roll.roll, - type: config.type, - modifierTotal: this.calculateTotalModifiers(roll.roll) - }; - } - static async buildPost(roll, config, message) { const chatMessage = config.source?.message ? ui.chat.collection.get(config.source.message) diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index 1c6ec829..16472fea 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -33,7 +33,9 @@ export default class DHRoll extends Roll { if (config.skips?.createMessage) config.messageRoll = roll; - await this.buildEvaluate(roll, config, (message = {})); + if (config.evaluate !== false) { + await this.buildEvaluate(roll, config, (message = {})); + } await this.buildPost(roll, config, (message = {})); return config; } @@ -72,27 +74,14 @@ export default class DHRoll extends Roll { return roll; } + /** + * Evaluates the roll and assigns roll data into the config. + * This is only called if config.evaluate is not set to false + * @protected + */ static async buildEvaluate(roll, config = {}, message = {}) { - if (config.evaluate !== false) { - await roll.evaluate(); - config.roll = this.postEvaluate(roll, config); - } - } - - static async buildPost(roll, config, message) { - for (const hook of config.hooks) { - if (Hooks.call(`${CONFIG.DH.id}.postRoll${hook.capitalize()}`, config, message) === false) return null; - } - - if (config.skips?.createMessage) { - await triggerChatRollFx([roll]); - } else if (!config.source?.message) { - config.message = await this.toMessage(roll, config); - } - } - - static postEvaluate(roll, config = {}) { - return { + await roll.evaluate(); + config.roll = { ...roll.options.roll, total: roll.total, formula: roll.formula, @@ -105,6 +94,22 @@ export default class DHRoll extends Roll { }; } + /** + * Runs any post configuration events that need to happen towards the end, such as hooks and dice so nice + * @protected + */ + static async buildPost(roll, config, message) { + for (const hook of config.hooks) { + if (Hooks.call(`${CONFIG.DH.id}.postRoll${hook.capitalize()}`, config, message) === false) return null; + } + + if (config.skips?.createMessage) { + await triggerChatRollFx([roll]); + } else if (!config.source?.message) { + config.message = await this.toMessage(roll, config); + } + } + static async toMessage(roll, config) { const item = config.data.parent?.items?.get?.(config.source.item) ?? null; const action = item ? item.system.actions.get(config.source.action) : null; diff --git a/module/dice/dualityRoll.mjs b/module/dice/dualityRoll.mjs index e2d967a3..38d9315f 100644 --- a/module/dice/dualityRoll.mjs +++ b/module/dice/dualityRoll.mjs @@ -232,22 +232,10 @@ export default class DualityRoll extends D20Roll { return changeKeys; } + /** @inheritdoc */ static async buildEvaluate(roll, config = {}, message = {}) { await super.buildEvaluate(roll, config, message); - - await setDiceSoNiceForDualityRoll( - roll, - config.roll.advantage.type, - config.roll.hope.dice, - config.roll.fear.dice, - config.roll.advantage.dice - ); - } - - static postEvaluate(roll, config = {}) { - const data = super.postEvaluate(roll, config); - - data.hope = { + config.roll.hope = { dice: roll.dHope.denomination, value: this.guaranteedCritical ? 0 : roll.dHope.total, rerolled: { @@ -255,7 +243,7 @@ export default class DualityRoll extends D20Roll { rerolls: roll.dHope.results.filter(x => x.rerolled) } }; - data.fear = { + config.roll.fear = { dice: roll.dFear.denomination, value: this.guaranteedCritical ? 0 : roll.dFear.total, rerolled: { @@ -263,11 +251,11 @@ export default class DualityRoll extends D20Roll { rerolls: roll.dFear.results.filter(x => x.rerolled) } }; - data.rally = { + config.roll.rally = { dice: roll.dRally?.denomination, value: roll.dRally?.total }; - data.result = { + config.roll.result = { duality: roll.withHope ? 1 : roll.withFear ? -1 : 0, total: this.guaranteedCritical ? 0 : roll.dHope.total + roll.dFear.total, label: roll.totalLabel @@ -275,11 +263,18 @@ export default class DualityRoll extends D20Roll { if (roll._rallyIndex && roll.data?.parent) roll.data.parent.deleteEmbeddedDocuments('ActiveEffect', [roll._rallyIndex]); - - return data; } - + + /** @inheritdoc */ static async buildPost(roll, config, message) { + await setDiceSoNiceForDualityRoll( + roll, + config.roll.advantage.type, + config.roll.hope.dice, + config.roll.fear.dice, + config.roll.advantage.dice + ); + await super.buildPost(roll, config, message); await DualityRoll.dualityUpdate(config); diff --git a/module/dice/fateRoll.mjs b/module/dice/fateRoll.mjs index 114fad59..f8276ce1 100644 --- a/module/dice/fateRoll.mjs +++ b/module/dice/fateRoll.mjs @@ -75,25 +75,23 @@ export default class FateRoll extends D20Roll { this.terms[0] = new foundry.dice.terms.Die({ faces: 12 }); } + /** @inheritdoc */ static async buildEvaluate(roll, config = {}, message = {}) { await super.buildEvaluate(roll, config, message); + config.roll.fate = { + dice: roll.fateDie === 'Hope' ? roll.dHope.denomination : roll.dFear.denomination, + value: roll.fateDie === 'Hope' ? roll.dHope.total : roll.dFear.total, + fateDie: roll.fateDie + }; + } + /** @inheritdoc */ + static async buildPost(roll, config, message) { if (roll.fateDie === 'Hope') { await setDiceSoNiceForHopeFateRoll(roll, config.roll.fate.dice); } else { await setDiceSoNiceForFearFateRoll(roll, config.roll.fate.dice); } - } - - static postEvaluate(roll, config = {}) { - const data = super.postEvaluate(roll, config); - - data.fate = { - dice: roll.fateDie === 'Hope' ? roll.dHope.denomination : roll.dFear.denomination, - value: roll.fateDie === 'Hope' ? roll.dHope.total : roll.dFear.total, - fateDie: roll.fateDie - }; - - return data; + return super.buildPost(roll, config, message); } } From 780aaf216c8bcef1ea79af11273cdbfd69bf5a56 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sat, 11 Jul 2026 21:05:15 +0200 Subject: [PATCH 06/30] Fixed a typo in getTemplateShape and added stopPropagaton when clicking enriched buttons (#2074) --- module/enrichers/TemplateEnricher.mjs | 2 +- module/enrichers/_module.mjs | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/module/enrichers/TemplateEnricher.mjs b/module/enrichers/TemplateEnricher.mjs index 4478ea14..b951f41e 100644 --- a/module/enrichers/TemplateEnricher.mjs +++ b/module/enrichers/TemplateEnricher.mjs @@ -58,7 +58,7 @@ export const renderMeasuredTemplate = async event => { if (!type || !range || !game.canvas.scene) return; const shapeData = CONFIG.Canvas.layers.regions.layerClass.getTemplateShape({ - shapetype: type, + shapeType: type, angle, range, direction, diff --git a/module/enrichers/_module.mjs b/module/enrichers/_module.mjs index b80f166c..02002884 100644 --- a/module/enrichers/_module.mjs +++ b/module/enrichers/_module.mjs @@ -35,23 +35,24 @@ export const enricherConfig = [ ]; export const enricherRenderSetup = element => { + const clickWrapper = func => event => { + event.stopPropagation(); + func(event); + }; + element .querySelectorAll('.enriched-damage-button') - .forEach(element => element.addEventListener('click', renderDamageButton)); + .forEach(element => element.addEventListener('click', clickWrapper(renderDamageButton))); element .querySelectorAll('.duality-roll-button') - .forEach(element => element.addEventListener('click', renderDualityButton)); + .forEach(element => element.addEventListener('click', clickWrapper(renderDualityButton))); element .querySelectorAll('.fate-roll-button') - .forEach(element => element.addEventListener('click', renderFateButton)); + .forEach(element => element.addEventListener('click', clickWrapper(renderFateButton))); element .querySelectorAll('.measured-template-button') - .forEach(element => element.addEventListener('click', renderMeasuredTemplate)); - - // element - // .querySelectorAll('.enriched-effect') - // .forEach(element => element.addEventListener('dragstart', dragEnrichedEffect)); + .forEach(element => element.addEventListener('click', clickWrapper(renderMeasuredTemplate))); }; From 2c980708369c9c14fd4424c98848a885d6622d38 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sat, 11 Jul 2026 22:27:38 +0200 Subject: [PATCH 07/30] Fixed so that the v13/v14 change from mode to type is fixed in SRD (#2075) --- ...ersary_Adult_Flickerfly_G7jiltRjgvVhZewm.json | 2 +- .../adversary_Archer_Guard_JRhrrEg5UroURiAD.json | 2 +- ...rsary_Assassin_Poisoner_h5RuhzGL17dW5FBT.json | 2 +- .../adversary_Chaos_Skull_jDmHqGvzg5wjgmxE.json | 4 ++-- .../adversary_Construct_uOP5oT9QzXPlnf3p.json | 2 +- ...ersary_Demon_of_Avarice_pnyjIGxxvurcWmTv.json | 2 +- ...ersary_Demon_of_Despair_kE4dfhqmIQpNd44e.json | 2 +- ...rsary_Failed_Experiment_ChwwVqowFw8hJQwT.json | 4 ++-- .../adversary_Giant_Eagle_OMQ0v6PE8s1mSU0K.json | 2 +- ...ersary_Giant_Mosquitoes_IIWV4ysJPFPnTP7W.json | 2 +- ...Greater_Earth_Elemental_dsfB3YhoL5SudvS2.json | 2 +- ...Greater_Water_Elemental_xIICT6tEdnA7dKDV.json | 2 +- ...versary_Hallowed_Archer_kabueAo6BALApWqp.json | 2 +- ...ary_Jagged_Knife_Shadow_XF4tYTq9nPJAy2ox.json | 2 +- ...ary_Juvenile_Flickerfly_MYXmTx2FHcIjdfYZ.json | 2 +- ...ary_Knight_of_the_Realm_7ai2opemrclQe3VF.json | 4 ++-- ...y_Minor_Chaos_Elemental_sRn4bqerfARvhgSV.json | 2 +- ...uter_Realms_Abomination_A0SeeDzwjvqOsyof.json | 2 +- ...versary_Skeleton_Archer_7X5q7a6ueeHs5oA9.json | 2 +- ...ersary_Skeleton_Warrior_10YIQl0lvCJXZLfX.json | 2 +- ...versary_Spectral_Archer_5tCkhnBByUIN5UdG.json | 2 +- ...ersary_Spectral_Captain_65cSO3EQEh6ZH6Xk.json | 2 +- ...rsary_Spectral_Guardian_UFVGl1osOsJTneLf.json | 2 +- ...Vault_Guardian_Sentinel_FVgYb28fhxlVcGwA.json | 2 +- ...y_Vault_Guardian_Turret_c5hGdvY5UnSjlHws.json | 2 +- ...ic_Dragon__Ashen_Tyrant_pMuXGCSOQaxpi5tb.json | 2 +- ...agon__Obsidian_Predator_ladm7wykhZczYzrQ.json | 4 ++-- ...adversary_Zombie_Legion_YhJrP7rTBiRdX5Fp.json | 2 +- ...eature_Celestial_Trance_TfolXWFG2W2hx6sK.json | 4 ++-- .../feature_Dread_Visage_i92lYjDhVB0LyPid.json | 2 +- .../feature_Efficient_2xlqKOkDxWHbuj4t.json | 4 ++-- .../feature_Endurance_tXWEMdLXafUSZTbK.json | 2 +- .../feature_High_Stamina_HMXNJZ7ynzajR2KT.json | 2 +- ...feature_Natural_Climber_soQvPL0MrTLLcc31.json | 2 +- .../feature_Nimble_3lNqft3LmOlEIEkw.json | 2 +- .../feature_Scales_u8ZhV962rNmUlzkp.json | 2 +- .../feature_Shell_A6a87OWA3tx16g9V.json | 4 ++-- .../feature_Thick_Skin_S0Ww7pYOSREt8qKg.json | 2 +- .../feature_Tusks_YhxD1ujZpftPu19w.json | 4 ++-- .../feature_Wings_WquAjoOcso8lwySW.json | 2 +- .../beastform_Agile_Scout_a9UoCwtrbgKk02mK.json | 8 ++++---- ...stform_Aquatic_Predator_ItBVeCl2u5uetgy7.json | 10 +++++----- ...beastform_Aquatic_Scout_qqzdFCxyYupWZK23.json | 8 ++++---- ...eastform_Armored_Sentry_8pUHJv3BYdjA4Qdf.json | 10 +++++----- ...form_Epic_Aquatic_Beast_wT4xbF99I55yjKZV.json | 10 +++++----- ...eastform_Great_Predator_afbMt4Ld6nY3mw0N.json | 10 +++++----- ...form_Great_Winged_Beast_b4BMnTbJ3iPPidSb.json | 10 +++++----- ...stform_Household_Friend_iDmOtiHJJ80AIAVT.json | 8 ++++---- ...astform_Legendary_Beast_mqP6z4Wg4K3oDAom.json | 4 ++-- ...stform_Legendary_Hybrid_rRUtgcUjimlpPhnn.json | 10 +++++----- ...stform_Massive_Behemoth_qjwMzPn33aKZACkv.json | 10 +++++----- ...beastform_Mighty_Lizard_94tvcC3D5Kp4lzuN.json | 10 +++++----- ...eastform_Mighty_Strider_zRLjqKx4Rn2TjivL.json | 10 +++++----- ...rm_Mythic_Aerial_Hunter_jV6EuEacyQlHW4SN.json | 10 +++++----- .../beastform_Mythic_Beast_kObobka52JdpWBSu.json | 6 +++--- ...beastform_Mythic_Hybrid_WAbxCf2An8qmxyJ1.json | 10 +++++----- ...beastform_Nimble_Grazer_CItO8yX6amQaqyk7.json | 8 ++++---- ...beastform_Pack_Predator_YLisKYYhAGca50WM.json | 10 +++++----- ...tform_Pouncing_Predator_33oFSZ1PwFqInHPe.json | 10 +++++----- ...eastform_Powerful_Beast_m8BVTuJI1wCvzTcf.json | 10 +++++----- ...tform_Stalking_Arachnid_A4TVRY0D5r9EiVwA.json | 10 +++++----- ...stform_Striking_Serpent_1XrZWGDttBAAUxR1.json | 10 +++++----- ...astform_Terrible_Lizard_5BABxRe2XVrYTj8N.json | 10 +++++----- .../beastform_Winged_Beast_mZ4Wlqtss2FlNNvL.json | 10 +++++----- .../feature_Armored_Shell_nDQZdIF2epKlhauX.json | 4 ++-- .../feature_Hollow_Bones_xVgmXhj2YgeqS1KK.json | 4 ++-- ...eature_Physical_Defense_StabkQ3BzWRZa8Tz.json | 4 ++-- .../feature_Rampage_8upqfcZvi7b5hRLE.json | 2 +- .../feature_Takedown_0ey4kM9ssj2otHvb.json | 2 +- .../feature_Thick_Hide_ZYbdXaWVj2zdcmaK.json | 4 ++-- .../feature_Undaunted_ODudjX88Te4vDP57.json | 4 ++-- ...feature_Combat_Training_eoSmuAJmgHUyULtp.json | 4 ++-- .../classes/feature_Rally_PydiMnNCKpd44SGS.json | 2 +- .../feature_Sneak_Attack_5QqpEwmwkPfZHpMW.json | 4 ++-- .../feature_Unstoppable_PnD2UCgzIlwX6cY3.json | 10 +++++----- .../feature_Lightfoot_TQ1AIQjndC4mYmmU.json | 2 +- .../feature_Privilege_C7NR6qRatawZusmg.json | 6 +++--- .../feature_Scoundrel_ZmEuBdL0JrvuA8le.json | 6 +++--- .../feature_Steady_DYmmr5CknLtHnwuj.json | 6 +++--- .../feature_Well_Read_JBZJmywisJg5X3tH.json | 2 +- ...mainCard_Arcana_Touched_5PvMQKCjrgSxzstn.json | 2 +- ...omainCard_Blade_Touched_Gb5bqpFSBiuBxUix.json | 4 ++-- .../domainCard_Body_Basher_aQz8jKkCd8M9aKMA.json | 4 ++-- ...omainCard_Bold_Presence_tdsL00yTSLNgZWs6.json | 2 +- ...domainCard_Bone_Touched_ON5bvnoQBy0SYc9Y.json | 2 +- .../domainCard_Brace_QXs4vssSqNGQu5b8.json | 2 +- ...omainCard_Codex_Touched_7Pu83ABdMukTxu3e.json | 2 +- ...ainCard_Conjured_Steeds_Jkp6cMDiHHaBZQRS.json | 6 +++--- ...ainCard_Cruel_Precision_bap1eCWryPNowbyo.json | 8 ++++---- ...domainCard_Deadly_Focus_xxZOXC4tiZQ6kg1e.json | 2 +- ...omainCard_Deft_Deceiver_38znCh6kHTkaPwYi.json | 2 +- ...mainCard_Deft_Maneuvers_dc4rAXlv95srZUct.json | 2 +- .../domainCard_Forager_06UapZuaA5S6fAKl.json | 2 +- ...ainCard_Force_of_Nature_LzVpMkD5I4QeaIHf.json | 6 +++--- ...mainCard_Forest_Sprites_JrkUMTzaFmQNBHVm.json | 2 +- ...ainCard_Fortified_Armor_oVa49lI107eZILZr.json | 4 ++-- .../domainCard_Get_Back_Up_BFWN2cObMdlk9uVz.json | 2 +- ...mainCard_Gifted_Tracker_VZ2b4zfRzV73XTuT.json | 2 +- ...domainCard_Goad_Them_On_HufF5KzuNfEb9RTi.json | 2 +- .../domainCard_Inevitable_XTT8c8uJ4D7fvtbL.json | 2 +- ...omainCard_Mass_Disguise_dT95m0Jam8sWbeuC.json | 2 +- ...inCard_Natural_Familiar_Tag303LoRNC5zGgl.json | 4 ++-- ...ainCard_Nature_s_Tongue_atWLorlCOxcrq8WB.json | 2 +- ...mainCard_Never_Upstaged_McdncxmO9K1YNP7Y.json | 16 ++++++++-------- ...domainCard_On_the_Brink_zbxPl81kbWEegKQN.json | 2 +- ...omainCard_Pick_and_Pull_HdgZUfWd7Hyj7nBW.json | 6 +++--- .../domainCard_Recovery_gsiQFT6q3WOgqerJ.json | 8 ++++---- .../domainCard_Rise_Up_oDIZoC4l19Nli0Fj.json | 2 +- .../domainCard_Safe_Haven_lmBLMPuR8qLbuzNf.json | 4 ++-- ...domainCard_Sage_Touched_VOSFaQHZbmhMyXwi.json | 6 +++--- ...domainCard_Shadowhunter_A0XzD6MmBXYdk7Ps.json | 2 +- .../domainCard_Shield_Aura_rfIv6lln40Fh6EIl.json | 2 +- ...domainCard_Shrug_It_Off_JwfhtgmmuRxg4zhI.json | 2 +- ...ard_Specter_of_the_Dark_iQhgqmLwhcSTYnvr.json | 2 +- ...inCard_Splendor_Touched_JT5dM3gVL6chDBYU.json | 2 +- ...inCard_Uncanny_Disguise_TV56wSysbU5xAlOa.json | 2 +- .../domainCard_Untouchable_9QElncQUDSakuSdR.json | 2 +- .../domainCard_Vitality_sWUlSPOJEaXyQLCj.json | 8 ++++---- ...ainCard_Voice_of_Reason_t3RRGH6mMYYJJCcF.json | 6 +++--- ...environment_Cult_Ritual_QAXXiOKBDmCTauHD.json | 6 +++--- ...nment_Divine_Usurpation_4DLYez7VbMCFDAuZ.json | 8 ++++---- ...nvironment_Haunted_City_OzYbizKraK92FDiI.json | 2 +- ...dvanced_Chainmail_Armor_LzLOJ9EVaHWAjoq9.json | 2 +- ...vanced_Full_Plate_Armor_crIbCb9NZ4K0VpoU.json | 4 ++-- ...Advanced_Gambeson_Armor_epkAmlZVk7HOfUUT.json | 2 +- ...mor_Bellamoi_Fine_Armor_WuoVwZA53XRAIt6d.json | 2 +- .../armor_Bladefare_Armor_mNN6pvcsS10ChrWF.json | 2 +- .../armor_Chainmail_Armor_haULhuEg37zUUvhb.json | 2 +- .../armor_Channeling_Armor_vMJxEWz1srfwMsoj.json | 2 +- ...r_Elundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json | 2 +- ...or_Full_Fortified_Armor_7emTSt6nhZuTlvt5.json | 2 +- .../armor_Full_Plate_Armor_UdUJNa31WxFW2noa.json | 4 ++-- .../armor_Gambeson_Armor_yJFp1bfpecDcStVK.json | 2 +- ...mproved_Chainmail_Armor_K5WkjS0NGqHYmhU3.json | 2 +- ...proved_Full_Plate_Armor_9f7RozpPTqrzJS1m.json | 4 ++-- ...Improved_Gambeson_Armor_jphnMZjnS2FkOH3s.json | 2 +- ...ntree_Breastplate_Armor_tzZntboNtHL5C6VM.json | 4 ++-- ...gendary_Chainmail_Armor_EsIN5OLKe9ZYFNXZ.json | 2 +- ...endary_Full_Plate_Armor_SXWjUR2aUR6bYvdl.json | 4 ++-- ...egendary_Gambeson_Armor_c6tMXz4rPf9ioQrf.json | 2 +- .../armor_Monett_s_Cloak_AQzU2RsqS5V5bd1v.json | 2 +- .../armor_Savior_Chainmail_8X16lJQ3xltTwynm.json | 14 +++++++------- ...rmor_Spiked_Plate_Armor_QjwsIhXKqnlvRBMv.json | 4 ++-- ...onsumable_Attune_Potion_JGD3M9hBHtVAA8XP.json | 2 +- ...nsumable_Bolster_Potion_FOPQNqXbiVO0ilYL.json | 2 +- ...consumable_Charm_Potion_CVBbFfOY75YwyQsp.json | 2 +- ...nsumable_Control_Potion_eeBhZSGLjuNZuJuI.json | 2 +- ...umable_Enlighten_Potion_aWHSO2AqDufi7nL4.json | 2 +- ...able_Grindletooth_Venom_8WkhvSzeOmLdnoLJ.json | 2 +- ...oved_Grindletooth_Venom_BqBWXXe9T07AMV4u.json | 2 +- ...ble_Major_Attune_Potion_CCPFm5iXXwvyYYwR.json | 2 +- ...le_Major_Bolster_Potion_mnyQDRtngWWQeRXF.json | 2 +- ...able_Major_Charm_Potion_IJLAUlQymbSjzsri.json | 2 +- ...le_Major_Control_Potion_80s1FLmTLtohZ5GH.json | 2 +- ..._Major_Enlighten_Potion_SDdv1G2veMLKrxcJ.json | 2 +- .../consumable_Mythic_Dust_Zsh2AvZr8EkGtLyw.json | 2 +- ...ble_Potion_of_Stability_dvL8oaxpEF6jKvYN.json | 4 ++-- ...sumable_Redthorn_Saliva_s2Exl2XFuoOhtIov.json | 2 +- ...onsumable_Stride_Potion_lNtcrkgFGOJNaroE.json | 2 +- .../loot/loot_Arcane_Prism_Mn1eo2Mdtu1kzyxB.json | 2 +- .../loot/loot_Attune_Relic_vK6bKyQTT3m8WvMh.json | 2 +- .../loot_Bolster_Relic_m3EpxlDgxn2tCDDR.json | 2 +- .../loot_Charging_Quiver_gsUDP90d4SRtLEUn.json | 4 ++-- .../loot/loot_Charm_Relic_9P9jqGSlxVCbTdLe.json | 2 +- .../loot_Control_Relic_QPGBDItjrRhXU6iJ.json | 2 +- .../loot_Enlighten_Relic_vSGx1f9SYUiA29L3.json | 2 +- .../loot_Piercing_Arrows_I63LTFD6GXHgyGpR.json | 4 ++-- ...loot_Ring_of_Resistance_aUqRifqR5JXXa1dN.json | 4 ++-- .../loot/loot_Stride_Relic_FfJISMzYATaPQPLc.json | 2 +- .../weapon_Aantari_Bow_ijodu5yNBoMxpkHV.json | 2 +- ...Arcane_Frame_Wheelchair_la3sAWgnvadc4NvP.json | 2 +- ...pon_Advanced_Broadsword_WtQAGz0TUgz8Xg70.json | 2 +- ...pon_Advanced_Greatsword_MAC6YWTo4lzSotQc.json | 2 +- ...weapon_Advanced_Halberd_C8gQn7onAc9wsrCs.json | 2 +- ..._Heavy_Frame_Wheelchair_eT2Qwb0RdrLX2hH1.json | 2 +- ...weapon_Advanced_Longbow_M5CywMAyPKGgebsJ.json | 2 +- ...pon_Advanced_Shortsword_p3nz5CaGUoyuGVg0.json | 2 +- ...n_Advanced_Small_Dagger_0thN0BpN05KT8Avx.json | 2 +- ...apon_Advanced_Warhammer_8Lipw3RRKDgBVP0p.json | 2 +- ...Arcane_Frame_Wheelchair_XRChepscgr75Uug7.json | 2 +- .../weapon_Bravesword_QZrWAkprA2tL2MOI.json | 4 ++-- .../weapon_Broadsword_1cwWNt4sqlgA8gCT.json | 2 +- .../weapons/weapon_Buckler_EmFTp9wzT6MHSaNz.json | 2 +- .../weapon_Curved_Dagger_Fk69R40svV0kanZD.json | 2 +- .../weapon_Finehair_Bow_ykF3jouxHZ6YR8Bg.json | 2 +- ...weapon_Flickerfly_Blade_xLJ5RRpUoTRmAC3G.json | 2 +- .../weapon_Fusion_Gloves_uK1RhtYAsDeoPNGx.json | 2 +- .../weapon_Gilded_Bow_ctTgFfMbM3YtmsYU.json | 2 +- .../weapon_Greatsword_70ysaFJDREwTgvZa.json | 2 +- .../weapons/weapon_Halberd_qT7FfmauAumOjJoq.json | 2 +- ..._Heavy_Frame_Wheelchair_XjPQjhRCH08VUIbr.json | 2 +- ...Arcane_Frame_Wheelchair_N9P695V5KKlJbAY5.json | 2 +- ...pon_Improved_Broadsword_OcKeLJxvmdT81VBc.json | 2 +- ...pon_Improved_Greatsword_FPX4ouDrxXiQ5MDf.json | 2 +- ...weapon_Improved_Halberd_F9PETfCQGwczBPif.json | 2 +- ..._Heavy_Frame_Wheelchair_L5KeCtrs768PmYWW.json | 2 +- ...weapon_Improved_Longbow_NacNonjbzyoVMNhI.json | 2 +- ...pon_Improved_Shortsword_rSyBNRwemBVuTo3H.json | 2 +- ...n_Improved_Small_Dagger_nMuF8ZDZ2aXZVTg6.json | 2 +- ...apon_Improved_Warhammer_pxaN4ZK4eqKrjtWj.json | 2 +- .../weapon_Keeper_s_Staff_q382JqMkqLaaFLIr.json | 2 +- ...Arcane_Frame_Wheelchair_gA2tiET9VHGhwMoO.json | 2 +- ...on_Legendary_Broadsword_y3hfTPfZhMognyaJ.json | 2 +- ...on_Legendary_Greatsword_zMZ46F9VR7zdTxb9.json | 2 +- ...eapon_Legendary_Halberd_1AuMNiJz96Ez9fur.json | 2 +- ..._Heavy_Frame_Wheelchair_S6nB0CNlzdU05o5U.json | 2 +- ...eapon_Legendary_Longbow_Utt1GpoH1fhaTOtN.json | 2 +- ...on_Legendary_Shortsword_dEumq3BIZBk5xYTk.json | 2 +- ..._Legendary_Small_Dagger_Px3Rh3kIvAqyISxJ.json | 2 +- ...pon_Legendary_Warhammer_W9ymfEDck2icfvla.json | 2 +- .../weapons/weapon_Longbow_YfVs6Se903az4Yet.json | 2 +- .../weapon_Midas_Scythe_BdLfy5i488VZgkjP.json | 2 +- ...weapon_Powered_Gauntlet_bW3xw5S9DbaLCN3E.json | 2 +- .../weapon_Shortsword_cjGZpXCoshEqi1FI.json | 2 +- .../weapon_Sledge_Axe_OxsEmffWriiQmqJK.json | 2 +- .../weapon_Small_Dagger_wKklDxs5nkzILNp4.json | 2 +- .../weapon_Thistlebow_I1nDGpulg29GpWOW.json | 2 +- ...on_Wand_of_Enthrallment_tP6vmnrmTq2h5sj7.json | 2 +- .../weapon_War_Scythe_z6yEdFYQJ5IzgTX3.json | 2 +- .../weapon_Warhammer_ZXh1GQahBiODfSTC.json | 2 +- .../feature_Adrenaline_uByM34yQlw38yf1V.json | 4 ++-- ...ature_Advanced_Training_uGcs785h94RMtueH.json | 2 +- .../feature_Arcane_Charge_yA4MKQ1tbKFiJoDB.json | 2 +- .../feature_Ascendant_fefLgx6kcYWusjBb.json | 2 +- .../feature_At_Ease_xPWFvGvtUjIcqgJq.json | 2 +- .../feature_Battlemage_Y9eGMewnFZgPvX0M.json | 2 +- .../feature_Conjure_Shield_oirsCnN66GOlK3Fa.json | 2 +- ...ture_Elemental_Dominion_EFUJHrkTuyv8uA9l.json | 4 ++-- ...e_Elemental_Incarnation_f37TTgCc0Q3Ih1A1.json | 6 +++--- .../feature_Elementalist_dPcqKN5NeDkjB1HW.json | 6 +++--- .../feature_Epic_Poetry_eCoEWkWuZPMZ9C6a.json | 2 +- ...feature_Ethereal_Visage_tyGB6wRKjYdIBK1i.json | 2 +- ...feature_Expert_Training_iCXtOWBKv1FdKdWz.json | 2 +- ...feature_Fleeting_Shadow_EY7Eo6hNGppVL3dR.json | 2 +- .../feature_Iron_Will_7AVRNyBcd1Nffjtn.json | 2 +- ...ature_Ruthless_Predator_Qny2J3R35bvC0Cey.json | 2 +- .../feature_Transcendence_th6HZwEFnVBjUtqm.json | 2 +- .../feature_Undaunted_866b2jjyzXP8nPRQ.json | 4 ++-- .../feature_Unrelenting_4qP7bNyxVHBmr4Rb.json | 4 ++-- .../feature_Unwavering_WBiFZaYNoQNhysmN.json | 4 ++-- .../feature_Wings_of_Light_KkQH0tYhagIqe2MT.json | 4 ++-- 241 files changed, 416 insertions(+), 416 deletions(-) diff --git a/src/packs/adversaries/adversary_Adult_Flickerfly_G7jiltRjgvVhZewm.json b/src/packs/adversaries/adversary_Adult_Flickerfly_G7jiltRjgvVhZewm.json index 9fef39f0..ad558d43 100644 --- a/src/packs/adversaries/adversary_Adult_Flickerfly_G7jiltRjgvVhZewm.json +++ b/src/packs/adversaries/adversary_Adult_Flickerfly_G7jiltRjgvVhZewm.json @@ -325,7 +325,7 @@ "changes": [ { "key": "system.evasion", - "mode": 5, + "type": "override", "value": "@system.evasion / 2", "priority": 10 } diff --git a/src/packs/adversaries/adversary_Archer_Guard_JRhrrEg5UroURiAD.json b/src/packs/adversaries/adversary_Archer_Guard_JRhrrEg5UroURiAD.json index 46dc3777..d9399007 100644 --- a/src/packs/adversaries/adversary_Archer_Guard_JRhrrEg5UroURiAD.json +++ b/src/packs/adversaries/adversary_Archer_Guard_JRhrrEg5UroURiAD.json @@ -328,7 +328,7 @@ "changes": [ { "key": "system.disadvantageSources", - "mode": 2, + "type": "add", "value": "Agility Rolls", "priority": null } diff --git a/src/packs/adversaries/adversary_Assassin_Poisoner_h5RuhzGL17dW5FBT.json b/src/packs/adversaries/adversary_Assassin_Poisoner_h5RuhzGL17dW5FBT.json index f156174d..51eb7273 100644 --- a/src/packs/adversaries/adversary_Assassin_Poisoner_h5RuhzGL17dW5FBT.json +++ b/src/packs/adversaries/adversary_Assassin_Poisoner_h5RuhzGL17dW5FBT.json @@ -338,7 +338,7 @@ "changes": [ { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "On attacks if they are Hidden.", "priority": null } diff --git a/src/packs/adversaries/adversary_Chaos_Skull_jDmHqGvzg5wjgmxE.json b/src/packs/adversaries/adversary_Chaos_Skull_jDmHqGvzg5wjgmxE.json index 8739fb2e..3db27edd 100644 --- a/src/packs/adversaries/adversary_Chaos_Skull_jDmHqGvzg5wjgmxE.json +++ b/src/packs/adversaries/adversary_Chaos_Skull_jDmHqGvzg5wjgmxE.json @@ -235,7 +235,7 @@ "changes": [ { "key": "system.rules.conditionImmunities.restrained", - "mode": 5, + "type": "override", "value": "1", "priority": null } @@ -298,7 +298,7 @@ "changes": [ { "key": "system.resistance.magical.resistance", - "mode": 5, + "type": "override", "value": "1", "priority": null } diff --git a/src/packs/adversaries/adversary_Construct_uOP5oT9QzXPlnf3p.json b/src/packs/adversaries/adversary_Construct_uOP5oT9QzXPlnf3p.json index 840f030b..02f6d587 100644 --- a/src/packs/adversaries/adversary_Construct_uOP5oT9QzXPlnf3p.json +++ b/src/packs/adversaries/adversary_Construct_uOP5oT9QzXPlnf3p.json @@ -462,7 +462,7 @@ "changes": [ { "key": "system.bonuses.damage.physical.bonus", - "mode": 2, + "type": "add", "value": "10", "priority": null } diff --git a/src/packs/adversaries/adversary_Demon_of_Avarice_pnyjIGxxvurcWmTv.json b/src/packs/adversaries/adversary_Demon_of_Avarice_pnyjIGxxvurcWmTv.json index e80c91d3..ebd41544 100644 --- a/src/packs/adversaries/adversary_Demon_of_Avarice_pnyjIGxxvurcWmTv.json +++ b/src/packs/adversaries/adversary_Demon_of_Avarice_pnyjIGxxvurcWmTv.json @@ -273,7 +273,7 @@ "changes": [ { "key": "system.bonuses.roll.attack.bonus", - "mode": 2, + "type": "add", "value": "ITEM.@system.resource.value", "priority": 21 } diff --git a/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json b/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json index 6ac0279c..219136ff 100644 --- a/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json +++ b/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json @@ -241,7 +241,7 @@ "changes": [ { "key": "system.rules.attack.damage.hpDamageMultiplier", - "mode": 5, + "type": "override", "value": "2", "priority": null } diff --git a/src/packs/adversaries/adversary_Failed_Experiment_ChwwVqowFw8hJQwT.json b/src/packs/adversaries/adversary_Failed_Experiment_ChwwVqowFw8hJQwT.json index 325530b0..fa963fce 100644 --- a/src/packs/adversaries/adversary_Failed_Experiment_ChwwVqowFw8hJQwT.json +++ b/src/packs/adversaries/adversary_Failed_Experiment_ChwwVqowFw8hJQwT.json @@ -243,7 +243,7 @@ "changes": [ { "key": "system.resistance.physical.resistance", - "mode": 5, + "type": "override", "value": "1", "priority": null } @@ -306,7 +306,7 @@ "changes": [ { "key": "system.rules.attack.damage.hpDamageMultiplier", - "mode": 5, + "type": "override", "value": "2", "priority": null } diff --git a/src/packs/adversaries/adversary_Giant_Eagle_OMQ0v6PE8s1mSU0K.json b/src/packs/adversaries/adversary_Giant_Eagle_OMQ0v6PE8s1mSU0K.json index dd10483a..044d51a0 100644 --- a/src/packs/adversaries/adversary_Giant_Eagle_OMQ0v6PE8s1mSU0K.json +++ b/src/packs/adversaries/adversary_Giant_Eagle_OMQ0v6PE8s1mSU0K.json @@ -268,7 +268,7 @@ "changes": [ { "key": "system.difficulty", - "mode": 2, + "type": "add", "value": "3", "priority": null } diff --git a/src/packs/adversaries/adversary_Giant_Mosquitoes_IIWV4ysJPFPnTP7W.json b/src/packs/adversaries/adversary_Giant_Mosquitoes_IIWV4ysJPFPnTP7W.json index 74fd0b04..fef1d858 100644 --- a/src/packs/adversaries/adversary_Giant_Mosquitoes_IIWV4ysJPFPnTP7W.json +++ b/src/packs/adversaries/adversary_Giant_Mosquitoes_IIWV4ysJPFPnTP7W.json @@ -272,7 +272,7 @@ "changes": [ { "key": "system.difficulty", - "mode": 2, + "type": "add", "value": "2", "priority": null } diff --git a/src/packs/adversaries/adversary_Greater_Earth_Elemental_dsfB3YhoL5SudvS2.json b/src/packs/adversaries/adversary_Greater_Earth_Elemental_dsfB3YhoL5SudvS2.json index a3ae5de3..2fe67f24 100644 --- a/src/packs/adversaries/adversary_Greater_Earth_Elemental_dsfB3YhoL5SudvS2.json +++ b/src/packs/adversaries/adversary_Greater_Earth_Elemental_dsfB3YhoL5SudvS2.json @@ -343,7 +343,7 @@ "changes": [ { "key": "system.resistance.physical.reduction", - "mode": 2, + "type": "add", "value": "7", "priority": null } diff --git a/src/packs/adversaries/adversary_Greater_Water_Elemental_xIICT6tEdnA7dKDV.json b/src/packs/adversaries/adversary_Greater_Water_Elemental_xIICT6tEdnA7dKDV.json index f0f68c11..ef2cf6d5 100644 --- a/src/packs/adversaries/adversary_Greater_Water_Elemental_xIICT6tEdnA7dKDV.json +++ b/src/packs/adversaries/adversary_Greater_Water_Elemental_xIICT6tEdnA7dKDV.json @@ -326,7 +326,7 @@ "changes": [ { "key": "system.disadvantageSources", - "mode": 2, + "type": "add", "value": "Your next action", "priority": null } diff --git a/src/packs/adversaries/adversary_Hallowed_Archer_kabueAo6BALApWqp.json b/src/packs/adversaries/adversary_Hallowed_Archer_kabueAo6BALApWqp.json index 34e40c70..64a4dfb6 100644 --- a/src/packs/adversaries/adversary_Hallowed_Archer_kabueAo6BALApWqp.json +++ b/src/packs/adversaries/adversary_Hallowed_Archer_kabueAo6BALApWqp.json @@ -237,7 +237,7 @@ "changes": [ { "key": "system.rules.attack.damage.hpDamageMultiplier", - "mode": 5, + "type": "override", "value": "2", "priority": null } diff --git a/src/packs/adversaries/adversary_Jagged_Knife_Shadow_XF4tYTq9nPJAy2ox.json b/src/packs/adversaries/adversary_Jagged_Knife_Shadow_XF4tYTq9nPJAy2ox.json index a8600966..6b005769 100644 --- a/src/packs/adversaries/adversary_Jagged_Knife_Shadow_XF4tYTq9nPJAy2ox.json +++ b/src/packs/adversaries/adversary_Jagged_Knife_Shadow_XF4tYTq9nPJAy2ox.json @@ -378,7 +378,7 @@ "changes": [ { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "Attacks made while Hidden", "priority": null } diff --git a/src/packs/adversaries/adversary_Juvenile_Flickerfly_MYXmTx2FHcIjdfYZ.json b/src/packs/adversaries/adversary_Juvenile_Flickerfly_MYXmTx2FHcIjdfYZ.json index c9fbb78a..72f4626d 100644 --- a/src/packs/adversaries/adversary_Juvenile_Flickerfly_MYXmTx2FHcIjdfYZ.json +++ b/src/packs/adversaries/adversary_Juvenile_Flickerfly_MYXmTx2FHcIjdfYZ.json @@ -349,7 +349,7 @@ "changes": [ { "key": "system.evasion", - "mode": 5, + "type": "override", "value": "@system.evasion / 2", "priority": 10 } diff --git a/src/packs/adversaries/adversary_Knight_of_the_Realm_7ai2opemrclQe3VF.json b/src/packs/adversaries/adversary_Knight_of_the_Realm_7ai2opemrclQe3VF.json index da767edc..f86717fc 100644 --- a/src/packs/adversaries/adversary_Knight_of_the_Realm_7ai2opemrclQe3VF.json +++ b/src/packs/adversaries/adversary_Knight_of_the_Realm_7ai2opemrclQe3VF.json @@ -253,7 +253,7 @@ "changes": [ { "key": "system.difficulty", - "mode": 2, + "type": "add", "value": "2", "priority": null } @@ -316,7 +316,7 @@ "changes": [ { "key": "system.resistance.physical.reduction", - "mode": 2, + "type": "add", "value": "3", "priority": null } diff --git a/src/packs/adversaries/adversary_Minor_Chaos_Elemental_sRn4bqerfARvhgSV.json b/src/packs/adversaries/adversary_Minor_Chaos_Elemental_sRn4bqerfARvhgSV.json index b22bbe51..7c4bbdba 100644 --- a/src/packs/adversaries/adversary_Minor_Chaos_Elemental_sRn4bqerfARvhgSV.json +++ b/src/packs/adversaries/adversary_Minor_Chaos_Elemental_sRn4bqerfARvhgSV.json @@ -238,7 +238,7 @@ "changes": [ { "key": "system.resistance.magical.resistance", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/adversaries/adversary_Outer_Realms_Abomination_A0SeeDzwjvqOsyof.json b/src/packs/adversaries/adversary_Outer_Realms_Abomination_A0SeeDzwjvqOsyof.json index bb5b99fe..e9a0b5f7 100644 --- a/src/packs/adversaries/adversary_Outer_Realms_Abomination_A0SeeDzwjvqOsyof.json +++ b/src/packs/adversaries/adversary_Outer_Realms_Abomination_A0SeeDzwjvqOsyof.json @@ -316,7 +316,7 @@ "changes": [ { "key": "system.disadvantageSources", - "mode": 2, + "type": "add", "value": "On your next action roll.", "priority": null } diff --git a/src/packs/adversaries/adversary_Skeleton_Archer_7X5q7a6ueeHs5oA9.json b/src/packs/adversaries/adversary_Skeleton_Archer_7X5q7a6ueeHs5oA9.json index e9cfde91..5e2e8200 100644 --- a/src/packs/adversaries/adversary_Skeleton_Archer_7X5q7a6ueeHs5oA9.json +++ b/src/packs/adversaries/adversary_Skeleton_Archer_7X5q7a6ueeHs5oA9.json @@ -238,7 +238,7 @@ "changes": [ { "key": "system.rules.attack.damage.hpDamageMultiplier", - "mode": 5, + "type": "override", "value": "2", "priority": null } diff --git a/src/packs/adversaries/adversary_Skeleton_Warrior_10YIQl0lvCJXZLfX.json b/src/packs/adversaries/adversary_Skeleton_Warrior_10YIQl0lvCJXZLfX.json index 288089a5..4cf72b0b 100644 --- a/src/packs/adversaries/adversary_Skeleton_Warrior_10YIQl0lvCJXZLfX.json +++ b/src/packs/adversaries/adversary_Skeleton_Warrior_10YIQl0lvCJXZLfX.json @@ -240,7 +240,7 @@ "changes": [ { "key": "system.resistance.physical.resistance", - "mode": 5, + "type": "override", "value": "1", "priority": null } diff --git a/src/packs/adversaries/adversary_Spectral_Archer_5tCkhnBByUIN5UdG.json b/src/packs/adversaries/adversary_Spectral_Archer_5tCkhnBByUIN5UdG.json index 6acbcf61..bda82248 100644 --- a/src/packs/adversaries/adversary_Spectral_Archer_5tCkhnBByUIN5UdG.json +++ b/src/packs/adversaries/adversary_Spectral_Archer_5tCkhnBByUIN5UdG.json @@ -273,7 +273,7 @@ "changes": [ { "key": "system.resistance.physical.resistance", - "mode": 5, + "type": "override", "value": "1", "priority": null } diff --git a/src/packs/adversaries/adversary_Spectral_Captain_65cSO3EQEh6ZH6Xk.json b/src/packs/adversaries/adversary_Spectral_Captain_65cSO3EQEh6ZH6Xk.json index 73d06ee6..20668427 100644 --- a/src/packs/adversaries/adversary_Spectral_Captain_65cSO3EQEh6ZH6Xk.json +++ b/src/packs/adversaries/adversary_Spectral_Captain_65cSO3EQEh6ZH6Xk.json @@ -273,7 +273,7 @@ "changes": [ { "key": "system.resistance.physical.resistance", - "mode": 5, + "type": "override", "value": "1", "priority": null } diff --git a/src/packs/adversaries/adversary_Spectral_Guardian_UFVGl1osOsJTneLf.json b/src/packs/adversaries/adversary_Spectral_Guardian_UFVGl1osOsJTneLf.json index dcb4e57e..e9792485 100644 --- a/src/packs/adversaries/adversary_Spectral_Guardian_UFVGl1osOsJTneLf.json +++ b/src/packs/adversaries/adversary_Spectral_Guardian_UFVGl1osOsJTneLf.json @@ -273,7 +273,7 @@ "changes": [ { "key": "system.resistance.physical.resistance", - "mode": 5, + "type": "override", "value": "1", "priority": null } diff --git a/src/packs/adversaries/adversary_Vault_Guardian_Sentinel_FVgYb28fhxlVcGwA.json b/src/packs/adversaries/adversary_Vault_Guardian_Sentinel_FVgYb28fhxlVcGwA.json index 4d70cf28..8afe5f9e 100644 --- a/src/packs/adversaries/adversary_Vault_Guardian_Sentinel_FVgYb28fhxlVcGwA.json +++ b/src/packs/adversaries/adversary_Vault_Guardian_Sentinel_FVgYb28fhxlVcGwA.json @@ -300,7 +300,7 @@ "changes": [ { "key": "system.disadvantageSources", - "mode": 2, + "type": "add", "value": "On attack rolls while you're within Very Close range of the Sentinel", "priority": null } diff --git a/src/packs/adversaries/adversary_Vault_Guardian_Turret_c5hGdvY5UnSjlHws.json b/src/packs/adversaries/adversary_Vault_Guardian_Turret_c5hGdvY5UnSjlHws.json index 9ecd43fc..906e05ff 100644 --- a/src/packs/adversaries/adversary_Vault_Guardian_Turret_c5hGdvY5UnSjlHws.json +++ b/src/packs/adversaries/adversary_Vault_Guardian_Turret_c5hGdvY5UnSjlHws.json @@ -305,7 +305,7 @@ "changes": [ { "key": "system.evasion", - "mode": 5, + "type": "override", "value": "@system.evasion / 2", "priority": 10 } diff --git a/src/packs/adversaries/adversary_Volcanic_Dragon__Ashen_Tyrant_pMuXGCSOQaxpi5tb.json b/src/packs/adversaries/adversary_Volcanic_Dragon__Ashen_Tyrant_pMuXGCSOQaxpi5tb.json index 9052287b..04663981 100644 --- a/src/packs/adversaries/adversary_Volcanic_Dragon__Ashen_Tyrant_pMuXGCSOQaxpi5tb.json +++ b/src/packs/adversaries/adversary_Volcanic_Dragon__Ashen_Tyrant_pMuXGCSOQaxpi5tb.json @@ -379,7 +379,7 @@ "changes": [ { "key": "system.difficulty", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/adversaries/adversary_Volcanic_Dragon__Obsidian_Predator_ladm7wykhZczYzrQ.json b/src/packs/adversaries/adversary_Volcanic_Dragon__Obsidian_Predator_ladm7wykhZczYzrQ.json index 2959c66f..29117035 100644 --- a/src/packs/adversaries/adversary_Volcanic_Dragon__Obsidian_Predator_ladm7wykhZczYzrQ.json +++ b/src/packs/adversaries/adversary_Volcanic_Dragon__Obsidian_Predator_ladm7wykhZczYzrQ.json @@ -324,7 +324,7 @@ "changes": [ { "key": "system.difficulty", - "mode": 2, + "type": "add", "value": "3", "priority": null } @@ -387,7 +387,7 @@ "changes": [ { "key": "system.resistance.physical.resistance", - "mode": 5, + "type": "override", "value": "1", "priority": null } diff --git a/src/packs/adversaries/adversary_Zombie_Legion_YhJrP7rTBiRdX5Fp.json b/src/packs/adversaries/adversary_Zombie_Legion_YhJrP7rTBiRdX5Fp.json index 322b23af..ee324e16 100644 --- a/src/packs/adversaries/adversary_Zombie_Legion_YhJrP7rTBiRdX5Fp.json +++ b/src/packs/adversaries/adversary_Zombie_Legion_YhJrP7rTBiRdX5Fp.json @@ -262,7 +262,7 @@ "changes": [ { "key": "system.resistance.physical.resistance", - "mode": 5, + "type": "override", "value": "1", "priority": null } diff --git a/src/packs/ancestries/feature_Celestial_Trance_TfolXWFG2W2hx6sK.json b/src/packs/ancestries/feature_Celestial_Trance_TfolXWFG2W2hx6sK.json index b1022f1f..0321223a 100644 --- a/src/packs/ancestries/feature_Celestial_Trance_TfolXWFG2W2hx6sK.json +++ b/src/packs/ancestries/feature_Celestial_Trance_TfolXWFG2W2hx6sK.json @@ -27,13 +27,13 @@ "changes": [ { "key": "system.bonuses.rest.shortRest.shortMoves", - "mode": 2, + "type": "add", "value": "1", "priority": null }, { "key": "system.bonuses.rest.longRest.longMoves", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/ancestries/feature_Dread_Visage_i92lYjDhVB0LyPid.json b/src/packs/ancestries/feature_Dread_Visage_i92lYjDhVB0LyPid.json index 0b38aebc..5d72e579 100644 --- a/src/packs/ancestries/feature_Dread_Visage_i92lYjDhVB0LyPid.json +++ b/src/packs/ancestries/feature_Dread_Visage_i92lYjDhVB0LyPid.json @@ -27,7 +27,7 @@ "changes": [ { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "Dread Visage: Rolls to intimidate hostile creatures", "priority": null } diff --git a/src/packs/ancestries/feature_Efficient_2xlqKOkDxWHbuj4t.json b/src/packs/ancestries/feature_Efficient_2xlqKOkDxWHbuj4t.json index fad97bdf..5a567695 100644 --- a/src/packs/ancestries/feature_Efficient_2xlqKOkDxWHbuj4t.json +++ b/src/packs/ancestries/feature_Efficient_2xlqKOkDxWHbuj4t.json @@ -27,13 +27,13 @@ "changes": [ { "key": "system.bonuses.rest.shortRest.longMoves", - "mode": 2, + "type": "add", "value": "1", "priority": null }, { "key": "system.bonuses.rest.shortRest.shortMoves", - "mode": 2, + "type": "add", "value": "-1", "priority": null } diff --git a/src/packs/ancestries/feature_Endurance_tXWEMdLXafUSZTbK.json b/src/packs/ancestries/feature_Endurance_tXWEMdLXafUSZTbK.json index 9d4710ab..ecf3d148 100644 --- a/src/packs/ancestries/feature_Endurance_tXWEMdLXafUSZTbK.json +++ b/src/packs/ancestries/feature_Endurance_tXWEMdLXafUSZTbK.json @@ -27,7 +27,7 @@ "changes": [ { "key": "system.resources.hitPoints.max", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/ancestries/feature_High_Stamina_HMXNJZ7ynzajR2KT.json b/src/packs/ancestries/feature_High_Stamina_HMXNJZ7ynzajR2KT.json index b93562e1..c653d13b 100644 --- a/src/packs/ancestries/feature_High_Stamina_HMXNJZ7ynzajR2KT.json +++ b/src/packs/ancestries/feature_High_Stamina_HMXNJZ7ynzajR2KT.json @@ -27,7 +27,7 @@ "changes": [ { "key": "system.resources.stress.max", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/ancestries/feature_Natural_Climber_soQvPL0MrTLLcc31.json b/src/packs/ancestries/feature_Natural_Climber_soQvPL0MrTLLcc31.json index 8b33dc70..3817b5bd 100644 --- a/src/packs/ancestries/feature_Natural_Climber_soQvPL0MrTLLcc31.json +++ b/src/packs/ancestries/feature_Natural_Climber_soQvPL0MrTLLcc31.json @@ -27,7 +27,7 @@ "changes": [ { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "Agility Rolls that involve balancing and climbing", "priority": null } diff --git a/src/packs/ancestries/feature_Nimble_3lNqft3LmOlEIEkw.json b/src/packs/ancestries/feature_Nimble_3lNqft3LmOlEIEkw.json index 07d894e3..d7bf9dd4 100644 --- a/src/packs/ancestries/feature_Nimble_3lNqft3LmOlEIEkw.json +++ b/src/packs/ancestries/feature_Nimble_3lNqft3LmOlEIEkw.json @@ -27,7 +27,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/ancestries/feature_Scales_u8ZhV962rNmUlzkp.json b/src/packs/ancestries/feature_Scales_u8ZhV962rNmUlzkp.json index 6bf571ab..956d4aa3 100644 --- a/src/packs/ancestries/feature_Scales_u8ZhV962rNmUlzkp.json +++ b/src/packs/ancestries/feature_Scales_u8ZhV962rNmUlzkp.json @@ -27,7 +27,7 @@ "changes": [ { "key": "system.rules.damageReduction.stressDamageReduction.severe.cost", - "mode": 5, + "type": "override", "value": "1", "priority": null } diff --git a/src/packs/ancestries/feature_Shell_A6a87OWA3tx16g9V.json b/src/packs/ancestries/feature_Shell_A6a87OWA3tx16g9V.json index 4dbfbc34..3063966e 100644 --- a/src/packs/ancestries/feature_Shell_A6a87OWA3tx16g9V.json +++ b/src/packs/ancestries/feature_Shell_A6a87OWA3tx16g9V.json @@ -27,13 +27,13 @@ "changes": [ { "key": "system.damageThresholds.major", - "mode": 2, + "type": "add", "value": "@prof", "priority": 21 }, { "key": "system.damageThresholds.severe", - "mode": 2, + "type": "add", "value": "@prof", "priority": 21 } diff --git a/src/packs/ancestries/feature_Thick_Skin_S0Ww7pYOSREt8qKg.json b/src/packs/ancestries/feature_Thick_Skin_S0Ww7pYOSREt8qKg.json index d9fc92de..e3712cd5 100644 --- a/src/packs/ancestries/feature_Thick_Skin_S0Ww7pYOSREt8qKg.json +++ b/src/packs/ancestries/feature_Thick_Skin_S0Ww7pYOSREt8qKg.json @@ -27,7 +27,7 @@ "changes": [ { "key": "system.rules.damageReduction.stressDamageReduction.minor.cost", - "mode": 5, + "type": "override", "value": "2", "priority": null } diff --git a/src/packs/ancestries/feature_Tusks_YhxD1ujZpftPu19w.json b/src/packs/ancestries/feature_Tusks_YhxD1ujZpftPu19w.json index 3581bb7f..53c3de82 100644 --- a/src/packs/ancestries/feature_Tusks_YhxD1ujZpftPu19w.json +++ b/src/packs/ancestries/feature_Tusks_YhxD1ujZpftPu19w.json @@ -90,13 +90,13 @@ "changes": [ { "key": "system.bonuses.damage.physical.dice", - "mode": 2, + "type": "add", "value": "1d6", "priority": null }, { "key": "system.bonuses.damage.magical.dice", - "mode": 2, + "type": "add", "value": "1d6", "priority": null } diff --git a/src/packs/ancestries/feature_Wings_WquAjoOcso8lwySW.json b/src/packs/ancestries/feature_Wings_WquAjoOcso8lwySW.json index 0db94fb2..b90b6076 100644 --- a/src/packs/ancestries/feature_Wings_WquAjoOcso8lwySW.json +++ b/src/packs/ancestries/feature_Wings_WquAjoOcso8lwySW.json @@ -57,7 +57,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "2", "priority": null } diff --git a/src/packs/beastforms/beastform_Agile_Scout_a9UoCwtrbgKk02mK.json b/src/packs/beastforms/beastform_Agile_Scout_a9UoCwtrbgKk02mK.json index bd9bfffb..0fc7a23d 100644 --- a/src/packs/beastforms/beastform_Agile_Scout_a9UoCwtrbgKk02mK.json +++ b/src/packs/beastforms/beastform_Agile_Scout_a9UoCwtrbgKk02mK.json @@ -62,25 +62,25 @@ "changes": [ { "key": "system.traits.agility.value", - "mode": 2, + "type": "add", "value": "1", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "2", "priority": null }, { "key": "system.rules.attack.damage.diceIndex", - "mode": 5, + "type": "override", "value": "0", "priority": null }, { "key": "system.rules.attack.roll.trait", - "mode": 5, + "type": "override", "value": "agility", "priority": null } diff --git a/src/packs/beastforms/beastform_Aquatic_Predator_ItBVeCl2u5uetgy7.json b/src/packs/beastforms/beastform_Aquatic_Predator_ItBVeCl2u5uetgy7.json index 5287de84..9cf209e2 100644 --- a/src/packs/beastforms/beastform_Aquatic_Predator_ItBVeCl2u5uetgy7.json +++ b/src/packs/beastforms/beastform_Aquatic_Predator_ItBVeCl2u5uetgy7.json @@ -62,31 +62,31 @@ "changes": [ { "key": "system.traits.agility.value", - "mode": 2, + "type": "add", "value": "2", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "4", "priority": null }, { "key": "system.rules.attack.damage.diceIndex", - "mode": 5, + "type": "override", "value": "3", "priority": null }, { "key": "system.rules.attack.damage.bonus", - "mode": 5, + "type": "override", "value": "6", "priority": null }, { "key": "system.rules.attack.roll.trait", - "mode": 5, + "type": "override", "value": "agility", "priority": null } diff --git a/src/packs/beastforms/beastform_Aquatic_Scout_qqzdFCxyYupWZK23.json b/src/packs/beastforms/beastform_Aquatic_Scout_qqzdFCxyYupWZK23.json index 95bea914..7816fd93 100644 --- a/src/packs/beastforms/beastform_Aquatic_Scout_qqzdFCxyYupWZK23.json +++ b/src/packs/beastforms/beastform_Aquatic_Scout_qqzdFCxyYupWZK23.json @@ -62,25 +62,25 @@ "changes": [ { "key": "system.traits.agility.value", - "mode": 2, + "type": "add", "value": "1", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "2", "priority": null }, { "key": "system.rules.attack.damage.diceIndex", - "mode": 5, + "type": "override", "value": "0", "priority": null }, { "key": "system.rules.attack.roll.trait", - "mode": 5, + "type": "override", "value": "agility", "priority": null } diff --git a/src/packs/beastforms/beastform_Armored_Sentry_8pUHJv3BYdjA4Qdf.json b/src/packs/beastforms/beastform_Armored_Sentry_8pUHJv3BYdjA4Qdf.json index ba18c05f..bc9187f5 100644 --- a/src/packs/beastforms/beastform_Armored_Sentry_8pUHJv3BYdjA4Qdf.json +++ b/src/packs/beastforms/beastform_Armored_Sentry_8pUHJv3BYdjA4Qdf.json @@ -62,31 +62,31 @@ "changes": [ { "key": "system.traits.strength.value", - "mode": 2, + "type": "add", "value": "1", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "1", "priority": null }, { "key": "system.rules.attack.damage.diceIndex", - "mode": 5, + "type": "override", "value": "2", "priority": null }, { "key": "system.rules.attack.damage.bonus", - "mode": 5, + "type": "override", "value": "2", "priority": null }, { "key": "system.rules.attack.roll.trait", - "mode": 5, + "type": "override", "value": "strength", "priority": null } diff --git a/src/packs/beastforms/beastform_Epic_Aquatic_Beast_wT4xbF99I55yjKZV.json b/src/packs/beastforms/beastform_Epic_Aquatic_Beast_wT4xbF99I55yjKZV.json index 0dfe9c20..5131fd69 100644 --- a/src/packs/beastforms/beastform_Epic_Aquatic_Beast_wT4xbF99I55yjKZV.json +++ b/src/packs/beastforms/beastform_Epic_Aquatic_Beast_wT4xbF99I55yjKZV.json @@ -65,31 +65,31 @@ "changes": [ { "key": "system.traits.agility.value", - "mode": 2, + "type": "add", "value": "3", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "3", "priority": null }, { "key": "system.rules.attack.damage.diceIndex", - "mode": 5, + "type": "override", "value": "3", "priority": null }, { "key": "system.rules.attack.damage.bonus", - "mode": 5, + "type": "override", "value": "10", "priority": null }, { "key": "system.rules.attack.roll.trait", - "mode": 5, + "type": "override", "value": "agility", "priority": null } diff --git a/src/packs/beastforms/beastform_Great_Predator_afbMt4Ld6nY3mw0N.json b/src/packs/beastforms/beastform_Great_Predator_afbMt4Ld6nY3mw0N.json index 450a1312..d8d41890 100644 --- a/src/packs/beastforms/beastform_Great_Predator_afbMt4Ld6nY3mw0N.json +++ b/src/packs/beastforms/beastform_Great_Predator_afbMt4Ld6nY3mw0N.json @@ -62,31 +62,31 @@ "changes": [ { "key": "system.traits.strength.value", - "mode": 2, + "type": "add", "value": "2", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "2", "priority": null }, { "key": "system.rules.attack.damage.diceIndex", - "mode": 5, + "type": "override", "value": "4", "priority": null }, { "key": "system.rules.attack.damage.bonus", - "mode": 5, + "type": "override", "value": "8", "priority": null }, { "key": "system.rules.attack.roll.trait", - "mode": 5, + "type": "override", "value": "strength", "priority": null } diff --git a/src/packs/beastforms/beastform_Great_Winged_Beast_b4BMnTbJ3iPPidSb.json b/src/packs/beastforms/beastform_Great_Winged_Beast_b4BMnTbJ3iPPidSb.json index c04b2182..e3842c7e 100644 --- a/src/packs/beastforms/beastform_Great_Winged_Beast_b4BMnTbJ3iPPidSb.json +++ b/src/packs/beastforms/beastform_Great_Winged_Beast_b4BMnTbJ3iPPidSb.json @@ -62,31 +62,31 @@ "changes": [ { "key": "system.traits.finesse.value", - "mode": 2, + "type": "add", "value": "2", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "3", "priority": null }, { "key": "system.rules.attack.damage.diceIndex", - "mode": 5, + "type": "override", "value": "2", "priority": null }, { "key": "system.rules.attack.damage.bonus", - "mode": 5, + "type": "override", "value": "6", "priority": null }, { "key": "system.rules.attack.roll.trait", - "mode": 5, + "type": "override", "value": "finesse", "priority": null } diff --git a/src/packs/beastforms/beastform_Household_Friend_iDmOtiHJJ80AIAVT.json b/src/packs/beastforms/beastform_Household_Friend_iDmOtiHJJ80AIAVT.json index cfb6aea7..ff7daa91 100644 --- a/src/packs/beastforms/beastform_Household_Friend_iDmOtiHJJ80AIAVT.json +++ b/src/packs/beastforms/beastform_Household_Friend_iDmOtiHJJ80AIAVT.json @@ -62,25 +62,25 @@ "changes": [ { "key": "system.traits.instinct.value", - "mode": 2, + "type": "add", "value": "1", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "2", "priority": null }, { "key": "system.rules.attack.damage.diceIndex", - "mode": 5, + "type": "override", "value": "1", "priority": null }, { "key": "system.rules.attack.roll.trait", - "mode": 5, + "type": "override", "value": "instinct", "priority": null } diff --git a/src/packs/beastforms/beastform_Legendary_Beast_mqP6z4Wg4K3oDAom.json b/src/packs/beastforms/beastform_Legendary_Beast_mqP6z4Wg4K3oDAom.json index 60ba7cd0..dd35735b 100644 --- a/src/packs/beastforms/beastform_Legendary_Beast_mqP6z4Wg4K3oDAom.json +++ b/src/packs/beastforms/beastform_Legendary_Beast_mqP6z4Wg4K3oDAom.json @@ -49,13 +49,13 @@ "changes": [ { "key": "system.bonuses.damage.physical.bonus", - "mode": 2, + "type": "add", "value": "6", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "2", "priority": null } diff --git a/src/packs/beastforms/beastform_Legendary_Hybrid_rRUtgcUjimlpPhnn.json b/src/packs/beastforms/beastform_Legendary_Hybrid_rRUtgcUjimlpPhnn.json index 4575820e..58edfabc 100644 --- a/src/packs/beastforms/beastform_Legendary_Hybrid_rRUtgcUjimlpPhnn.json +++ b/src/packs/beastforms/beastform_Legendary_Hybrid_rRUtgcUjimlpPhnn.json @@ -49,31 +49,31 @@ "changes": [ { "key": "system.traits.strength.value", - "mode": 2, + "type": "add", "value": "2", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "3", "priority": null }, { "key": "system.rules.attack.damage.diceIndex", - "mode": 5, + "type": "override", "value": "3", "priority": null }, { "key": "system.rules.attack.damage.bonus", - "mode": 5, + "type": "override", "value": "8", "priority": null }, { "key": "system.rules.attack.roll.trait", - "mode": 5, + "type": "override", "value": "strength", "priority": null } diff --git a/src/packs/beastforms/beastform_Massive_Behemoth_qjwMzPn33aKZACkv.json b/src/packs/beastforms/beastform_Massive_Behemoth_qjwMzPn33aKZACkv.json index 35715056..1b34daf9 100644 --- a/src/packs/beastforms/beastform_Massive_Behemoth_qjwMzPn33aKZACkv.json +++ b/src/packs/beastforms/beastform_Massive_Behemoth_qjwMzPn33aKZACkv.json @@ -66,31 +66,31 @@ "changes": [ { "key": "system.traits.strength.value", - "mode": 2, + "type": "add", "value": "3", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "1", "priority": null }, { "key": "system.rules.attack.damage.diceIndex", - "mode": 5, + "type": "override", "value": "4", "priority": null }, { "key": "system.rules.attack.damage.bonus", - "mode": 5, + "type": "override", "value": "12", "priority": null }, { "key": "system.rules.attack.roll.trait", - "mode": 5, + "type": "override", "value": "strength", "priority": null } diff --git a/src/packs/beastforms/beastform_Mighty_Lizard_94tvcC3D5Kp4lzuN.json b/src/packs/beastforms/beastform_Mighty_Lizard_94tvcC3D5Kp4lzuN.json index 390bf054..8f76f675 100644 --- a/src/packs/beastforms/beastform_Mighty_Lizard_94tvcC3D5Kp4lzuN.json +++ b/src/packs/beastforms/beastform_Mighty_Lizard_94tvcC3D5Kp4lzuN.json @@ -62,31 +62,31 @@ "changes": [ { "key": "system.traits.instinct.value", - "mode": 2, + "type": "add", "value": "2", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "1", "priority": null }, { "key": "system.rules.attack.damage.diceIndex", - "mode": 5, + "type": "override", "value": "3", "priority": null }, { "key": "system.rules.attack.damage.bonus", - "mode": 5, + "type": "override", "value": "7", "priority": null }, { "key": "system.rules.attack.roll.trait", - "mode": 5, + "type": "override", "value": "instinct", "priority": null } diff --git a/src/packs/beastforms/beastform_Mighty_Strider_zRLjqKx4Rn2TjivL.json b/src/packs/beastforms/beastform_Mighty_Strider_zRLjqKx4Rn2TjivL.json index adb9627b..5393c188 100644 --- a/src/packs/beastforms/beastform_Mighty_Strider_zRLjqKx4Rn2TjivL.json +++ b/src/packs/beastforms/beastform_Mighty_Strider_zRLjqKx4Rn2TjivL.json @@ -62,31 +62,31 @@ "changes": [ { "key": "system.traits.agility.value", - "mode": 2, + "type": "add", "value": "1", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "2", "priority": null }, { "key": "system.rules.attack.damage.diceIndex", - "mode": 5, + "type": "override", "value": "2", "priority": null }, { "key": "system.rules.attack.damage.bonus", - "mode": 5, + "type": "override", "value": "1", "priority": null }, { "key": "system.rules.attack.roll.trait", - "mode": 5, + "type": "override", "value": "agility", "priority": null } diff --git a/src/packs/beastforms/beastform_Mythic_Aerial_Hunter_jV6EuEacyQlHW4SN.json b/src/packs/beastforms/beastform_Mythic_Aerial_Hunter_jV6EuEacyQlHW4SN.json index dc373c27..80571c43 100644 --- a/src/packs/beastforms/beastform_Mythic_Aerial_Hunter_jV6EuEacyQlHW4SN.json +++ b/src/packs/beastforms/beastform_Mythic_Aerial_Hunter_jV6EuEacyQlHW4SN.json @@ -65,31 +65,31 @@ "changes": [ { "key": "system.traits.finesse.value", - "mode": 2, + "type": "add", "value": "3", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "4", "priority": null }, { "key": "system.rules.attack.damage.diceIndex", - "mode": 5, + "type": "override", "value": "3", "priority": null }, { "key": "system.rules.attack.damage.bonus", - "mode": 5, + "type": "override", "value": "11", "priority": null }, { "key": "system.rules.attack.roll.trait", - "mode": 5, + "type": "override", "value": "finesse", "priority": null } diff --git a/src/packs/beastforms/beastform_Mythic_Beast_kObobka52JdpWBSu.json b/src/packs/beastforms/beastform_Mythic_Beast_kObobka52JdpWBSu.json index cd879ac0..84dfb32b 100644 --- a/src/packs/beastforms/beastform_Mythic_Beast_kObobka52JdpWBSu.json +++ b/src/packs/beastforms/beastform_Mythic_Beast_kObobka52JdpWBSu.json @@ -49,19 +49,19 @@ "changes": [ { "key": "system.bonuses.damage.physical.bonus", - "mode": 2, + "type": "add", "value": "9", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "3", "priority": null }, { "key": "system.rules.attack.damage.diceIndex", - "mode": 2, + "type": "add", "value": "1", "priority": 60 } diff --git a/src/packs/beastforms/beastform_Mythic_Hybrid_WAbxCf2An8qmxyJ1.json b/src/packs/beastforms/beastform_Mythic_Hybrid_WAbxCf2An8qmxyJ1.json index fa35eaac..691d9972 100644 --- a/src/packs/beastforms/beastform_Mythic_Hybrid_WAbxCf2An8qmxyJ1.json +++ b/src/packs/beastforms/beastform_Mythic_Hybrid_WAbxCf2An8qmxyJ1.json @@ -49,31 +49,31 @@ "changes": [ { "key": "system.traits.strength.value", - "mode": 2, + "type": "add", "value": "3", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "2", "priority": null }, { "key": "system.rules.attack.damage.diceIndex", - "mode": 5, + "type": "override", "value": "4", "priority": null }, { "key": "system.rules.attack.damage.bonus", - "mode": 5, + "type": "override", "value": "10", "priority": null }, { "key": "system.rules.attack.roll.trait", - "mode": 5, + "type": "override", "value": "strength", "priority": null } diff --git a/src/packs/beastforms/beastform_Nimble_Grazer_CItO8yX6amQaqyk7.json b/src/packs/beastforms/beastform_Nimble_Grazer_CItO8yX6amQaqyk7.json index 183ad150..30523e41 100644 --- a/src/packs/beastforms/beastform_Nimble_Grazer_CItO8yX6amQaqyk7.json +++ b/src/packs/beastforms/beastform_Nimble_Grazer_CItO8yX6amQaqyk7.json @@ -62,25 +62,25 @@ "changes": [ { "key": "system.traits.agility.value", - "mode": 2, + "type": "add", "value": "1", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "3", "priority": null }, { "key": "system.rules.attack.damage.diceIndex", - "mode": 5, + "type": "override", "value": "1", "priority": null }, { "key": "system.rules.attack.roll.trait", - "mode": 5, + "type": "override", "value": "agility", "priority": null } diff --git a/src/packs/beastforms/beastform_Pack_Predator_YLisKYYhAGca50WM.json b/src/packs/beastforms/beastform_Pack_Predator_YLisKYYhAGca50WM.json index 834493bb..82a0dded 100644 --- a/src/packs/beastforms/beastform_Pack_Predator_YLisKYYhAGca50WM.json +++ b/src/packs/beastforms/beastform_Pack_Predator_YLisKYYhAGca50WM.json @@ -62,31 +62,31 @@ "changes": [ { "key": "system.traits.strength.value", - "mode": 2, + "type": "add", "value": "2", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "1", "priority": null }, { "key": "system.rules.attack.damage.diceIndex", - "mode": 5, + "type": "override", "value": "2", "priority": null }, { "key": "system.rules.attack.damage.bonus", - "mode": 5, + "type": "override", "value": "2", "priority": null }, { "key": "system.rules.attack.roll.trait", - "mode": 5, + "type": "override", "value": "strength", "priority": null } diff --git a/src/packs/beastforms/beastform_Pouncing_Predator_33oFSZ1PwFqInHPe.json b/src/packs/beastforms/beastform_Pouncing_Predator_33oFSZ1PwFqInHPe.json index d172d8f3..f058d649 100644 --- a/src/packs/beastforms/beastform_Pouncing_Predator_33oFSZ1PwFqInHPe.json +++ b/src/packs/beastforms/beastform_Pouncing_Predator_33oFSZ1PwFqInHPe.json @@ -62,31 +62,31 @@ "changes": [ { "key": "system.traits.instinct.value", - "mode": 2, + "type": "add", "value": "1", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "3", "priority": null }, { "key": "system.rules.attack.damage.diceIndex", - "mode": 5, + "type": "override", "value": "2", "priority": null }, { "key": "system.rules.attack.damage.bonus", - "mode": 5, + "type": "override", "value": "6", "priority": null }, { "key": "system.rules.attack.roll.trait", - "mode": 5, + "type": "override", "value": "instinct", "priority": null } diff --git a/src/packs/beastforms/beastform_Powerful_Beast_m8BVTuJI1wCvzTcf.json b/src/packs/beastforms/beastform_Powerful_Beast_m8BVTuJI1wCvzTcf.json index 7fa832e6..6ad91eed 100644 --- a/src/packs/beastforms/beastform_Powerful_Beast_m8BVTuJI1wCvzTcf.json +++ b/src/packs/beastforms/beastform_Powerful_Beast_m8BVTuJI1wCvzTcf.json @@ -62,31 +62,31 @@ "changes": [ { "key": "system.traits.strength.value", - "mode": 2, + "type": "add", "value": "3", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "1", "priority": null }, { "key": "system.rules.attack.damage.diceIndex", - "mode": 5, + "type": "override", "value": "3", "priority": null }, { "key": "system.rules.attack.damage.bonus", - "mode": 5, + "type": "override", "value": "4", "priority": null }, { "key": "system.rules.attack.roll.trait", - "mode": 5, + "type": "override", "value": "strength", "priority": null } diff --git a/src/packs/beastforms/beastform_Stalking_Arachnid_A4TVRY0D5r9EiVwA.json b/src/packs/beastforms/beastform_Stalking_Arachnid_A4TVRY0D5r9EiVwA.json index 16520a9c..cf6f49f2 100644 --- a/src/packs/beastforms/beastform_Stalking_Arachnid_A4TVRY0D5r9EiVwA.json +++ b/src/packs/beastforms/beastform_Stalking_Arachnid_A4TVRY0D5r9EiVwA.json @@ -62,31 +62,31 @@ "changes": [ { "key": "system.traits.finesse.value", - "mode": 2, + "type": "add", "value": "1", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "2", "priority": null }, { "key": "system.rules.attack.damage.diceIndex", - "mode": 5, + "type": "override", "value": "1", "priority": null }, { "key": "system.rules.attack.damage.bonus", - "mode": 5, + "type": "override", "value": "1", "priority": null }, { "key": "system.rules.attack.roll.trait", - "mode": 5, + "type": "override", "value": "finesse", "priority": null } diff --git a/src/packs/beastforms/beastform_Striking_Serpent_1XrZWGDttBAAUxR1.json b/src/packs/beastforms/beastform_Striking_Serpent_1XrZWGDttBAAUxR1.json index f78500c9..16111887 100644 --- a/src/packs/beastforms/beastform_Striking_Serpent_1XrZWGDttBAAUxR1.json +++ b/src/packs/beastforms/beastform_Striking_Serpent_1XrZWGDttBAAUxR1.json @@ -62,31 +62,31 @@ "changes": [ { "key": "system.traits.finesse.value", - "mode": 2, + "type": "add", "value": "1", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "2", "priority": null }, { "key": "system.rules.attack.damage.diceIndex", - "mode": 5, + "type": "override", "value": "2", "priority": null }, { "key": "system.rules.attack.damage.bonus", - "mode": 5, + "type": "override", "value": "4", "priority": null }, { "key": "system.rules.attack.roll.trait", - "mode": 5, + "type": "override", "value": "finesse", "priority": null } diff --git a/src/packs/beastforms/beastform_Terrible_Lizard_5BABxRe2XVrYTj8N.json b/src/packs/beastforms/beastform_Terrible_Lizard_5BABxRe2XVrYTj8N.json index 49818b74..e9993526 100644 --- a/src/packs/beastforms/beastform_Terrible_Lizard_5BABxRe2XVrYTj8N.json +++ b/src/packs/beastforms/beastform_Terrible_Lizard_5BABxRe2XVrYTj8N.json @@ -65,31 +65,31 @@ "changes": [ { "key": "system.traits.strength.value", - "mode": 2, + "type": "add", "value": "3", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "2", "priority": null }, { "key": "system.rules.attack.damage.diceIndex", - "mode": 5, + "type": "override", "value": "4", "priority": null }, { "key": "system.rules.attack.damage.bonus", - "mode": 5, + "type": "override", "value": "10", "priority": null }, { "key": "system.rules.attack.roll.trait", - "mode": 5, + "type": "override", "value": "strength", "priority": null } diff --git a/src/packs/beastforms/beastform_Winged_Beast_mZ4Wlqtss2FlNNvL.json b/src/packs/beastforms/beastform_Winged_Beast_mZ4Wlqtss2FlNNvL.json index 4ca44471..ccb67283 100644 --- a/src/packs/beastforms/beastform_Winged_Beast_mZ4Wlqtss2FlNNvL.json +++ b/src/packs/beastforms/beastform_Winged_Beast_mZ4Wlqtss2FlNNvL.json @@ -62,31 +62,31 @@ "changes": [ { "key": "system.traits.finesse.value", - "mode": 2, + "type": "add", "value": "1", "priority": null }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "3", "priority": null }, { "key": "system.rules.attack.damage.diceIndex", - "mode": 5, + "type": "override", "value": "0", "priority": null }, { "key": "system.rules.attack.damage.bonus", - "mode": 5, + "type": "override", "value": "2", "priority": null }, { "key": "system.rules.attack.roll.trait", - "mode": 5, + "type": "override", "value": "finesse", "priority": null } diff --git a/src/packs/beastforms/feature_Armored_Shell_nDQZdIF2epKlhauX.json b/src/packs/beastforms/feature_Armored_Shell_nDQZdIF2epKlhauX.json index 7aaefef8..395a1d7d 100644 --- a/src/packs/beastforms/feature_Armored_Shell_nDQZdIF2epKlhauX.json +++ b/src/packs/beastforms/feature_Armored_Shell_nDQZdIF2epKlhauX.json @@ -60,7 +60,7 @@ "changes": [ { "key": "system.resistance.physical.resistance", - "mode": 5, + "type": "override", "value": "1", "priority": null } @@ -98,7 +98,7 @@ "changes": [ { "key": "system.resistance.physical.reduction", - "mode": 2, + "type": "add", "value": "@system.armorScore", "priority": 21 } diff --git a/src/packs/beastforms/feature_Hollow_Bones_xVgmXhj2YgeqS1KK.json b/src/packs/beastforms/feature_Hollow_Bones_xVgmXhj2YgeqS1KK.json index d047a501..23b2317f 100644 --- a/src/packs/beastforms/feature_Hollow_Bones_xVgmXhj2YgeqS1KK.json +++ b/src/packs/beastforms/feature_Hollow_Bones_xVgmXhj2YgeqS1KK.json @@ -25,13 +25,13 @@ "changes": [ { "key": "system.damageThresholds.major", - "mode": 2, + "type": "add", "value": "-2", "priority": null }, { "key": "system.damageThresholds.severe", - "mode": 2, + "type": "add", "value": "-2", "priority": null } diff --git a/src/packs/beastforms/feature_Physical_Defense_StabkQ3BzWRZa8Tz.json b/src/packs/beastforms/feature_Physical_Defense_StabkQ3BzWRZa8Tz.json index f4c86b8c..6edc64da 100644 --- a/src/packs/beastforms/feature_Physical_Defense_StabkQ3BzWRZa8Tz.json +++ b/src/packs/beastforms/feature_Physical_Defense_StabkQ3BzWRZa8Tz.json @@ -25,13 +25,13 @@ "changes": [ { "key": "system.damageThresholds.major", - "mode": 2, + "type": "add", "value": "3", "priority": null }, { "key": "system.damageThresholds.severe", - "mode": 2, + "type": "add", "value": "3", "priority": null } diff --git a/src/packs/beastforms/feature_Rampage_8upqfcZvi7b5hRLE.json b/src/packs/beastforms/feature_Rampage_8upqfcZvi7b5hRLE.json index 03c3b2c7..8c86458b 100644 --- a/src/packs/beastforms/feature_Rampage_8upqfcZvi7b5hRLE.json +++ b/src/packs/beastforms/feature_Rampage_8upqfcZvi7b5hRLE.json @@ -62,7 +62,7 @@ "changes": [ { "key": "system.proficiency", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/beastforms/feature_Takedown_0ey4kM9ssj2otHvb.json b/src/packs/beastforms/feature_Takedown_0ey4kM9ssj2otHvb.json index 531b30ea..591f4857 100644 --- a/src/packs/beastforms/feature_Takedown_0ey4kM9ssj2otHvb.json +++ b/src/packs/beastforms/feature_Takedown_0ey4kM9ssj2otHvb.json @@ -108,7 +108,7 @@ "changes": [ { "key": "system.proficiency", - "mode": 2, + "type": "add", "value": "2", "priority": null } diff --git a/src/packs/beastforms/feature_Thick_Hide_ZYbdXaWVj2zdcmaK.json b/src/packs/beastforms/feature_Thick_Hide_ZYbdXaWVj2zdcmaK.json index 6eba9342..932749bf 100644 --- a/src/packs/beastforms/feature_Thick_Hide_ZYbdXaWVj2zdcmaK.json +++ b/src/packs/beastforms/feature_Thick_Hide_ZYbdXaWVj2zdcmaK.json @@ -25,13 +25,13 @@ "changes": [ { "key": "system.damageThresholds.major", - "mode": 2, + "type": "add", "value": "2", "priority": null }, { "key": "system.damageThresholds.severe", - "mode": 2, + "type": "add", "value": "2", "priority": null } diff --git a/src/packs/beastforms/feature_Undaunted_ODudjX88Te4vDP57.json b/src/packs/beastforms/feature_Undaunted_ODudjX88Te4vDP57.json index 092efe51..d13c8a1c 100644 --- a/src/packs/beastforms/feature_Undaunted_ODudjX88Te4vDP57.json +++ b/src/packs/beastforms/feature_Undaunted_ODudjX88Te4vDP57.json @@ -25,13 +25,13 @@ "changes": [ { "key": "system.damageThresholds.major", - "mode": 2, + "type": "add", "value": "2", "priority": null }, { "key": "system.damageThresholds.severe", - "mode": 2, + "type": "add", "value": "2", "priority": null } diff --git a/src/packs/classes/feature_Combat_Training_eoSmuAJmgHUyULtp.json b/src/packs/classes/feature_Combat_Training_eoSmuAJmgHUyULtp.json index 8b4ad82e..34f242ed 100644 --- a/src/packs/classes/feature_Combat_Training_eoSmuAJmgHUyULtp.json +++ b/src/packs/classes/feature_Combat_Training_eoSmuAJmgHUyULtp.json @@ -24,13 +24,13 @@ "changes": [ { "key": "system.bonuses.damage.physical.bonus", - "mode": 2, + "type": "add", "value": "@system.levelData.level.current", "priority": null }, { "key": "system.rules.burden.ignore", - "mode": 5, + "type": "override", "value": "1", "priority": null } diff --git a/src/packs/classes/feature_Rally_PydiMnNCKpd44SGS.json b/src/packs/classes/feature_Rally_PydiMnNCKpd44SGS.json index e4f84a9f..9ecb926b 100644 --- a/src/packs/classes/feature_Rally_PydiMnNCKpd44SGS.json +++ b/src/packs/classes/feature_Rally_PydiMnNCKpd44SGS.json @@ -55,7 +55,7 @@ "changes": [ { "key": "system.bonuses.rally", - "mode": 2, + "type": "add", "value": "6 + min((floor(@system.levelData.level.current / 5)*2), 2)", "priority": null } diff --git a/src/packs/classes/feature_Sneak_Attack_5QqpEwmwkPfZHpMW.json b/src/packs/classes/feature_Sneak_Attack_5QqpEwmwkPfZHpMW.json index 4b5ce9bd..83db7d06 100644 --- a/src/packs/classes/feature_Sneak_Attack_5QqpEwmwkPfZHpMW.json +++ b/src/packs/classes/feature_Sneak_Attack_5QqpEwmwkPfZHpMW.json @@ -28,13 +28,13 @@ "changes": [ { "key": "system.bonuses.damage.physical.dice", - "mode": 2, + "type": "add", "value": "@tierd6", "priority": null }, { "key": "system.bonuses.damage.magical.dice", - "mode": 2, + "type": "add", "value": "@tierd6", "priority": null } diff --git a/src/packs/classes/feature_Unstoppable_PnD2UCgzIlwX6cY3.json b/src/packs/classes/feature_Unstoppable_PnD2UCgzIlwX6cY3.json index 56362824..5477f2c4 100644 --- a/src/packs/classes/feature_Unstoppable_PnD2UCgzIlwX6cY3.json +++ b/src/packs/classes/feature_Unstoppable_PnD2UCgzIlwX6cY3.json @@ -65,31 +65,31 @@ "changes": [ { "key": "system.bonuses.damage.physical.bonus", - "mode": 2, + "type": "add", "value": "ORIGIN.@item.resource.value", "priority": null }, { "key": "system.bonuses.damage.magical.bonus", - "mode": 2, + "type": "add", "value": "ORIGIN.@item.resource.value", "priority": null }, { "key": "system.rules.damageReduction.reduceSeverity.physical", - "mode": 2, + "type": "add", "value": "1", "priority": null }, { "key": "system.rules.conditionImmunities.vulnerable", - "mode": 5, + "type": "override", "value": "1", "priority": null }, { "key": "system.rules.conditionImmunities.restrained", - "mode": 5, + "type": "override", "value": "1", "priority": null } diff --git a/src/packs/communities/feature_Lightfoot_TQ1AIQjndC4mYmmU.json b/src/packs/communities/feature_Lightfoot_TQ1AIQjndC4mYmmU.json index 91c771fc..84134bbb 100644 --- a/src/packs/communities/feature_Lightfoot_TQ1AIQjndC4mYmmU.json +++ b/src/packs/communities/feature_Lightfoot_TQ1AIQjndC4mYmmU.json @@ -27,7 +27,7 @@ "changes": [ { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "Move without being heard.", "priority": null } diff --git a/src/packs/communities/feature_Privilege_C7NR6qRatawZusmg.json b/src/packs/communities/feature_Privilege_C7NR6qRatawZusmg.json index caae7322..85ef1ec3 100644 --- a/src/packs/communities/feature_Privilege_C7NR6qRatawZusmg.json +++ b/src/packs/communities/feature_Privilege_C7NR6qRatawZusmg.json @@ -27,19 +27,19 @@ "changes": [ { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "Consort with nobles", "priority": null }, { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "Negotiate prices", "priority": null }, { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "Leverage your reputation to get what you want", "priority": null } diff --git a/src/packs/communities/feature_Scoundrel_ZmEuBdL0JrvuA8le.json b/src/packs/communities/feature_Scoundrel_ZmEuBdL0JrvuA8le.json index 55605a00..7f7a34ae 100644 --- a/src/packs/communities/feature_Scoundrel_ZmEuBdL0JrvuA8le.json +++ b/src/packs/communities/feature_Scoundrel_ZmEuBdL0JrvuA8le.json @@ -27,19 +27,19 @@ "changes": [ { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "Negotiate with criminals", "priority": null }, { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "Detect lies", "priority": null }, { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "Find a safe place to hide", "priority": null } diff --git a/src/packs/communities/feature_Steady_DYmmr5CknLtHnwuj.json b/src/packs/communities/feature_Steady_DYmmr5CknLtHnwuj.json index ee9c683c..292fd773 100644 --- a/src/packs/communities/feature_Steady_DYmmr5CknLtHnwuj.json +++ b/src/packs/communities/feature_Steady_DYmmr5CknLtHnwuj.json @@ -27,19 +27,19 @@ "changes": [ { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "Traverse dangerous cliffs and ledges", "priority": null }, { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "Navigate harsh environment", "priority": null }, { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "Use your survival knowledge", "priority": null } diff --git a/src/packs/communities/feature_Well_Read_JBZJmywisJg5X3tH.json b/src/packs/communities/feature_Well_Read_JBZJmywisJg5X3tH.json index 3ce8fd16..28d4c962 100644 --- a/src/packs/communities/feature_Well_Read_JBZJmywisJg5X3tH.json +++ b/src/packs/communities/feature_Well_Read_JBZJmywisJg5X3tH.json @@ -27,7 +27,7 @@ "changes": [ { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "History, culture, or politics of a prominent person or place", "priority": null } diff --git a/src/packs/domains/domainCard_Arcana_Touched_5PvMQKCjrgSxzstn.json b/src/packs/domains/domainCard_Arcana_Touched_5PvMQKCjrgSxzstn.json index 70e2d23c..361558d3 100644 --- a/src/packs/domains/domainCard_Arcana_Touched_5PvMQKCjrgSxzstn.json +++ b/src/packs/domains/domainCard_Arcana_Touched_5PvMQKCjrgSxzstn.json @@ -68,7 +68,7 @@ "changes": [ { "key": "system.bonuses.roll.spellcast.bonus", - "mode": 2, + "type": "add", "value": "+1", "priority": null } diff --git a/src/packs/domains/domainCard_Blade_Touched_Gb5bqpFSBiuBxUix.json b/src/packs/domains/domainCard_Blade_Touched_Gb5bqpFSBiuBxUix.json index 8cc9834e..ba8c56a9 100644 --- a/src/packs/domains/domainCard_Blade_Touched_Gb5bqpFSBiuBxUix.json +++ b/src/packs/domains/domainCard_Blade_Touched_Gb5bqpFSBiuBxUix.json @@ -27,13 +27,13 @@ "changes": [ { "key": "system.bonuses.roll.attack.bonus", - "mode": 2, + "type": "add", "value": "2", "priority": null }, { "key": "system.damageThresholds.severe", - "mode": 2, + "type": "add", "value": "4", "priority": null } diff --git a/src/packs/domains/domainCard_Body_Basher_aQz8jKkCd8M9aKMA.json b/src/packs/domains/domainCard_Body_Basher_aQz8jKkCd8M9aKMA.json index 7c5a7ef1..fc49b640 100644 --- a/src/packs/domains/domainCard_Body_Basher_aQz8jKkCd8M9aKMA.json +++ b/src/packs/domains/domainCard_Body_Basher_aQz8jKkCd8M9aKMA.json @@ -35,13 +35,13 @@ "changes": [ { "key": "system.bonuses.damage.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "@system.traits.strength.value", "priority": 21 }, { "key": "system.bonuses.damage.secondaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "@system.traits.strength.value", "priority": 21 } diff --git a/src/packs/domains/domainCard_Bold_Presence_tdsL00yTSLNgZWs6.json b/src/packs/domains/domainCard_Bold_Presence_tdsL00yTSLNgZWs6.json index 95b73ff5..3d7a26e2 100644 --- a/src/packs/domains/domainCard_Bold_Presence_tdsL00yTSLNgZWs6.json +++ b/src/packs/domains/domainCard_Bold_Presence_tdsL00yTSLNgZWs6.json @@ -88,7 +88,7 @@ "changes": [ { "key": "system.traits.presence.value", - "mode": 2, + "type": "add", "value": "@system.traits.strength.value", "priority": null } diff --git a/src/packs/domains/domainCard_Bone_Touched_ON5bvnoQBy0SYc9Y.json b/src/packs/domains/domainCard_Bone_Touched_ON5bvnoQBy0SYc9Y.json index 35f8a4d0..3fc45037 100644 --- a/src/packs/domains/domainCard_Bone_Touched_ON5bvnoQBy0SYc9Y.json +++ b/src/packs/domains/domainCard_Bone_Touched_ON5bvnoQBy0SYc9Y.json @@ -60,7 +60,7 @@ "changes": [ { "key": "system.traits.agility.value", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/domains/domainCard_Brace_QXs4vssSqNGQu5b8.json b/src/packs/domains/domainCard_Brace_QXs4vssSqNGQu5b8.json index 23b15eba..321de80a 100644 --- a/src/packs/domains/domainCard_Brace_QXs4vssSqNGQu5b8.json +++ b/src/packs/domains/domainCard_Brace_QXs4vssSqNGQu5b8.json @@ -26,7 +26,7 @@ "changes": [ { "key": "system.rules.damageReduction.maxArmorMarked.stressExtra", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/domains/domainCard_Codex_Touched_7Pu83ABdMukTxu3e.json b/src/packs/domains/domainCard_Codex_Touched_7Pu83ABdMukTxu3e.json index f07e89be..3eb4da35 100644 --- a/src/packs/domains/domainCard_Codex_Touched_7Pu83ABdMukTxu3e.json +++ b/src/packs/domains/domainCard_Codex_Touched_7Pu83ABdMukTxu3e.json @@ -89,7 +89,7 @@ "changes": [ { "key": "system.bonuses.roll.spellcast.bonus", - "mode": 2, + "type": "add", "value": "@system.proficiency", "priority": null } diff --git a/src/packs/domains/domainCard_Conjured_Steeds_Jkp6cMDiHHaBZQRS.json b/src/packs/domains/domainCard_Conjured_Steeds_Jkp6cMDiHHaBZQRS.json index 62f42054..0b581925 100644 --- a/src/packs/domains/domainCard_Conjured_Steeds_Jkp6cMDiHHaBZQRS.json +++ b/src/packs/domains/domainCard_Conjured_Steeds_Jkp6cMDiHHaBZQRS.json @@ -66,19 +66,19 @@ "changes": [ { "key": "system.bonuses.roll.attack.bonus", - "mode": 2, + "type": "add", "value": "-2", "priority": null }, { "key": "system.bonuses.damage.magical.dice", - "mode": 2, + "type": "add", "value": "+2", "priority": null }, { "key": "system.bonuses.damage.physical.bonus", - "mode": 2, + "type": "add", "value": "+2", "priority": null } diff --git a/src/packs/domains/domainCard_Cruel_Precision_bap1eCWryPNowbyo.json b/src/packs/domains/domainCard_Cruel_Precision_bap1eCWryPNowbyo.json index c71dfa04..fbe1d7c7 100644 --- a/src/packs/domains/domainCard_Cruel_Precision_bap1eCWryPNowbyo.json +++ b/src/packs/domains/domainCard_Cruel_Precision_bap1eCWryPNowbyo.json @@ -26,13 +26,13 @@ "changes": [ { "key": "system.bonuses.damage.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "@system.traits.agility.value", "priority": 21 }, { "key": "system.bonuses.damage.secondaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "@system.traits.agility.value", "priority": 21 } @@ -69,13 +69,13 @@ "changes": [ { "key": "system.bonuses.damage.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "@system.traits.finesse.value", "priority": null }, { "key": "system.bonuses.damage.secondaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "@system.traits.finesse.value", "priority": null } diff --git a/src/packs/domains/domainCard_Deadly_Focus_xxZOXC4tiZQ6kg1e.json b/src/packs/domains/domainCard_Deadly_Focus_xxZOXC4tiZQ6kg1e.json index 938f2daf..e794241f 100644 --- a/src/packs/domains/domainCard_Deadly_Focus_xxZOXC4tiZQ6kg1e.json +++ b/src/packs/domains/domainCard_Deadly_Focus_xxZOXC4tiZQ6kg1e.json @@ -60,7 +60,7 @@ "changes": [ { "key": "system.proficiency", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/domains/domainCard_Deft_Deceiver_38znCh6kHTkaPwYi.json b/src/packs/domains/domainCard_Deft_Deceiver_38znCh6kHTkaPwYi.json index dac804e2..f8696f52 100644 --- a/src/packs/domains/domainCard_Deft_Deceiver_38znCh6kHTkaPwYi.json +++ b/src/packs/domains/domainCard_Deft_Deceiver_38znCh6kHTkaPwYi.json @@ -66,7 +66,7 @@ "changes": [ { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "Deceive or trick someone into believing a lie you told them", "priority": null } diff --git a/src/packs/domains/domainCard_Deft_Maneuvers_dc4rAXlv95srZUct.json b/src/packs/domains/domainCard_Deft_Maneuvers_dc4rAXlv95srZUct.json index 6cb59b2c..671d1233 100644 --- a/src/packs/domains/domainCard_Deft_Maneuvers_dc4rAXlv95srZUct.json +++ b/src/packs/domains/domainCard_Deft_Maneuvers_dc4rAXlv95srZUct.json @@ -68,7 +68,7 @@ "changes": [ { "key": "system.bonuses.roll.attack.bonus", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/domains/domainCard_Forager_06UapZuaA5S6fAKl.json b/src/packs/domains/domainCard_Forager_06UapZuaA5S6fAKl.json index cfa183d1..00786add 100644 --- a/src/packs/domains/domainCard_Forager_06UapZuaA5S6fAKl.json +++ b/src/packs/domains/domainCard_Forager_06UapZuaA5S6fAKl.json @@ -260,7 +260,7 @@ "changes": [ { "key": "system.bonuses.roll.spellcast.bonus", - "mode": 2, + "type": "add", "value": "2", "priority": null } diff --git a/src/packs/domains/domainCard_Force_of_Nature_LzVpMkD5I4QeaIHf.json b/src/packs/domains/domainCard_Force_of_Nature_LzVpMkD5I4QeaIHf.json index 9db4016c..913de9e0 100644 --- a/src/packs/domains/domainCard_Force_of_Nature_LzVpMkD5I4QeaIHf.json +++ b/src/packs/domains/domainCard_Force_of_Nature_LzVpMkD5I4QeaIHf.json @@ -95,19 +95,19 @@ "changes": [ { "key": "system.bonuses.damage.physical.bonus", - "mode": 2, + "type": "add", "value": "10", "priority": null }, { "key": "system.bonuses.damage.magical.bonus", - "mode": 2, + "type": "add", "value": "10", "priority": null }, { "key": "system.rules.conditionImmunities.restrained", - "mode": 5, + "type": "override", "value": "1", "priority": null } diff --git a/src/packs/domains/domainCard_Forest_Sprites_JrkUMTzaFmQNBHVm.json b/src/packs/domains/domainCard_Forest_Sprites_JrkUMTzaFmQNBHVm.json index f345262a..164b3a71 100644 --- a/src/packs/domains/domainCard_Forest_Sprites_JrkUMTzaFmQNBHVm.json +++ b/src/packs/domains/domainCard_Forest_Sprites_JrkUMTzaFmQNBHVm.json @@ -91,7 +91,7 @@ "changes": [ { "key": "system.bonuses.roll.attack.bonus", - "mode": 2, + "type": "add", "value": "+3", "priority": null } diff --git a/src/packs/domains/domainCard_Fortified_Armor_oVa49lI107eZILZr.json b/src/packs/domains/domainCard_Fortified_Armor_oVa49lI107eZILZr.json index 7162c664..90ed8d89 100644 --- a/src/packs/domains/domainCard_Fortified_Armor_oVa49lI107eZILZr.json +++ b/src/packs/domains/domainCard_Fortified_Armor_oVa49lI107eZILZr.json @@ -26,13 +26,13 @@ "changes": [ { "key": "system.damageThresholds.major", - "mode": 2, + "type": "add", "value": "2", "priority": null }, { "key": "system.damageThresholds.severe", - "mode": 2, + "type": "add", "value": "2", "priority": null } diff --git a/src/packs/domains/domainCard_Get_Back_Up_BFWN2cObMdlk9uVz.json b/src/packs/domains/domainCard_Get_Back_Up_BFWN2cObMdlk9uVz.json index f4cc2cbf..fde3f546 100644 --- a/src/packs/domains/domainCard_Get_Back_Up_BFWN2cObMdlk9uVz.json +++ b/src/packs/domains/domainCard_Get_Back_Up_BFWN2cObMdlk9uVz.json @@ -27,7 +27,7 @@ "changes": [ { "key": "system.rules.damageReduction.stressDamageReduction.severe.cost", - "mode": 5, + "type": "override", "value": "1", "priority": null } diff --git a/src/packs/domains/domainCard_Gifted_Tracker_VZ2b4zfRzV73XTuT.json b/src/packs/domains/domainCard_Gifted_Tracker_VZ2b4zfRzV73XTuT.json index 7998b6c4..ad1c1580 100644 --- a/src/packs/domains/domainCard_Gifted_Tracker_VZ2b4zfRzV73XTuT.json +++ b/src/packs/domains/domainCard_Gifted_Tracker_VZ2b4zfRzV73XTuT.json @@ -66,7 +66,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "+1", "priority": null } diff --git a/src/packs/domains/domainCard_Goad_Them_On_HufF5KzuNfEb9RTi.json b/src/packs/domains/domainCard_Goad_Them_On_HufF5KzuNfEb9RTi.json index 11610aac..884324f7 100644 --- a/src/packs/domains/domainCard_Goad_Them_On_HufF5KzuNfEb9RTi.json +++ b/src/packs/domains/domainCard_Goad_Them_On_HufF5KzuNfEb9RTi.json @@ -109,7 +109,7 @@ "changes": [ { "key": "system.disadvantageSources", - "mode": 2, + "type": "add", "value": "Attacking the goading creature", "priority": null } diff --git a/src/packs/domains/domainCard_Inevitable_XTT8c8uJ4D7fvtbL.json b/src/packs/domains/domainCard_Inevitable_XTT8c8uJ4D7fvtbL.json index 790acf08..d7f1d82d 100644 --- a/src/packs/domains/domainCard_Inevitable_XTT8c8uJ4D7fvtbL.json +++ b/src/packs/domains/domainCard_Inevitable_XTT8c8uJ4D7fvtbL.json @@ -26,7 +26,7 @@ "changes": [ { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "If you failed your previous action roll", "priority": null } diff --git a/src/packs/domains/domainCard_Mass_Disguise_dT95m0Jam8sWbeuC.json b/src/packs/domains/domainCard_Mass_Disguise_dT95m0Jam8sWbeuC.json index 8c0122f4..4941eddd 100644 --- a/src/packs/domains/domainCard_Mass_Disguise_dT95m0Jam8sWbeuC.json +++ b/src/packs/domains/domainCard_Mass_Disguise_dT95m0Jam8sWbeuC.json @@ -96,7 +96,7 @@ "changes": [ { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "Presence Rolls to avoid scrutiny.", "priority": null } diff --git a/src/packs/domains/domainCard_Natural_Familiar_Tag303LoRNC5zGgl.json b/src/packs/domains/domainCard_Natural_Familiar_Tag303LoRNC5zGgl.json index f561e0be..68ed1c54 100644 --- a/src/packs/domains/domainCard_Natural_Familiar_Tag303LoRNC5zGgl.json +++ b/src/packs/domains/domainCard_Natural_Familiar_Tag303LoRNC5zGgl.json @@ -142,13 +142,13 @@ "changes": [ { "key": "system.bonuses.damage.magical.dice", - "mode": 2, + "type": "add", "value": "d6", "priority": null }, { "key": "system.bonuses.damage.physical.dice", - "mode": 2, + "type": "add", "value": "d6", "priority": null } diff --git a/src/packs/domains/domainCard_Nature_s_Tongue_atWLorlCOxcrq8WB.json b/src/packs/domains/domainCard_Nature_s_Tongue_atWLorlCOxcrq8WB.json index 81c0734e..42734531 100644 --- a/src/packs/domains/domainCard_Nature_s_Tongue_atWLorlCOxcrq8WB.json +++ b/src/packs/domains/domainCard_Nature_s_Tongue_atWLorlCOxcrq8WB.json @@ -112,7 +112,7 @@ "changes": [ { "key": "system.bonuses.roll.spellcast.bonus", - "mode": 2, + "type": "add", "value": "+2", "priority": null } diff --git a/src/packs/domains/domainCard_Never_Upstaged_McdncxmO9K1YNP7Y.json b/src/packs/domains/domainCard_Never_Upstaged_McdncxmO9K1YNP7Y.json index 4662a882..396b7153 100644 --- a/src/packs/domains/domainCard_Never_Upstaged_McdncxmO9K1YNP7Y.json +++ b/src/packs/domains/domainCard_Never_Upstaged_McdncxmO9K1YNP7Y.json @@ -214,13 +214,13 @@ "changes": [ { "key": "system.bonuses.damage.physical.bonus", - "mode": 2, + "type": "add", "value": "+5", "priority": null }, { "key": "system.bonuses.damage.magical.bonus", - "mode": 2, + "type": "add", "value": "+5", "priority": null } @@ -257,13 +257,13 @@ "changes": [ { "key": "system.bonuses.damage.magical.bonus", - "mode": 2, + "type": "add", "value": "+10", "priority": null }, { "key": "system.bonuses.damage.physical.bonus", - "mode": 2, + "type": "add", "value": "+10", "priority": null } @@ -300,13 +300,13 @@ "changes": [ { "key": "system.bonuses.damage.magical.bonus", - "mode": 2, + "type": "add", "value": "+15", "priority": null }, { "key": "system.bonuses.damage.physical.bonus", - "mode": 2, + "type": "add", "value": "+15", "priority": null } @@ -343,13 +343,13 @@ "changes": [ { "key": "system.bonuses.damage.magical.bonus", - "mode": 2, + "type": "add", "value": "+20", "priority": null }, { "key": "system.bonuses.damage.physical.bonus", - "mode": 2, + "type": "add", "value": "+20", "priority": null } diff --git a/src/packs/domains/domainCard_On_the_Brink_zbxPl81kbWEegKQN.json b/src/packs/domains/domainCard_On_the_Brink_zbxPl81kbWEegKQN.json index c4c89d17..2dc7b95e 100644 --- a/src/packs/domains/domainCard_On_the_Brink_zbxPl81kbWEegKQN.json +++ b/src/packs/domains/domainCard_On_the_Brink_zbxPl81kbWEegKQN.json @@ -26,7 +26,7 @@ "changes": [ { "key": "system.rules.damageReduction.thresholdImmunities.minor", - "mode": 5, + "type": "override", "value": "1", "priority": null } diff --git a/src/packs/domains/domainCard_Pick_and_Pull_HdgZUfWd7Hyj7nBW.json b/src/packs/domains/domainCard_Pick_and_Pull_HdgZUfWd7Hyj7nBW.json index 250bc539..1a5e40df 100644 --- a/src/packs/domains/domainCard_Pick_and_Pull_HdgZUfWd7Hyj7nBW.json +++ b/src/packs/domains/domainCard_Pick_and_Pull_HdgZUfWd7Hyj7nBW.json @@ -26,19 +26,19 @@ "changes": [ { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "Pick nonmagical locks", "priority": null }, { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "Disarm nonmagical traps", "priority": null }, { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "Steal items from a target (either through stealth or by force)", "priority": null } diff --git a/src/packs/domains/domainCard_Recovery_gsiQFT6q3WOgqerJ.json b/src/packs/domains/domainCard_Recovery_gsiQFT6q3WOgqerJ.json index 83e3a22c..bf6ffbf3 100644 --- a/src/packs/domains/domainCard_Recovery_gsiQFT6q3WOgqerJ.json +++ b/src/packs/domains/domainCard_Recovery_gsiQFT6q3WOgqerJ.json @@ -60,13 +60,13 @@ "changes": [ { "key": "system.bonuses.rest.shortRest.longMoves", - "mode": 2, + "type": "add", "value": "1", "priority": null }, { "key": "system.bonuses.rest.shortRest.shortMoves", - "mode": 2, + "type": "add", "value": "-1", "priority": null } @@ -99,13 +99,13 @@ "changes": [ { "key": "system.bonuses.rest.shortRest.longMoves", - "mode": 2, + "type": "add", "value": "1", "priority": null }, { "key": "system.bonuses.rest.shortRest.shortMoves", - "mode": 2, + "type": "add", "value": "-1", "priority": null } diff --git a/src/packs/domains/domainCard_Rise_Up_oDIZoC4l19Nli0Fj.json b/src/packs/domains/domainCard_Rise_Up_oDIZoC4l19Nli0Fj.json index 74f6293f..09c82b6e 100644 --- a/src/packs/domains/domainCard_Rise_Up_oDIZoC4l19Nli0Fj.json +++ b/src/packs/domains/domainCard_Rise_Up_oDIZoC4l19Nli0Fj.json @@ -95,7 +95,7 @@ "changes": [ { "key": "system.damageThresholds.severe", - "mode": 2, + "type": "add", "value": "@system.proficiency", "priority": 21 } diff --git a/src/packs/domains/domainCard_Safe_Haven_lmBLMPuR8qLbuzNf.json b/src/packs/domains/domainCard_Safe_Haven_lmBLMPuR8qLbuzNf.json index de4872a2..14c79df8 100644 --- a/src/packs/domains/domainCard_Safe_Haven_lmBLMPuR8qLbuzNf.json +++ b/src/packs/domains/domainCard_Safe_Haven_lmBLMPuR8qLbuzNf.json @@ -66,13 +66,13 @@ "changes": [ { "key": "system.bonuses.rest.shortRest.shortMoves", - "mode": 2, + "type": "add", "value": "1", "priority": null }, { "key": "system.bonuses.rest.longRest.longMoves", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/domains/domainCard_Sage_Touched_VOSFaQHZbmhMyXwi.json b/src/packs/domains/domainCard_Sage_Touched_VOSFaQHZbmhMyXwi.json index 082f8620..a289fd30 100644 --- a/src/packs/domains/domainCard_Sage_Touched_VOSFaQHZbmhMyXwi.json +++ b/src/packs/domains/domainCard_Sage_Touched_VOSFaQHZbmhMyXwi.json @@ -108,7 +108,7 @@ "changes": [ { "key": "system.bonuses.roll.spellcast.bonus", - "mode": 2, + "type": "add", "value": "+2", "priority": null } @@ -149,7 +149,7 @@ "changes": [ { "key": "system.traits.agility.value", - "mode": 1, + "type": "multiply", "value": "2", "priority": null } @@ -186,7 +186,7 @@ "changes": [ { "key": "system.traits.instinct.value", - "mode": 1, + "type": "multiply", "value": "2", "priority": null } diff --git a/src/packs/domains/domainCard_Shadowhunter_A0XzD6MmBXYdk7Ps.json b/src/packs/domains/domainCard_Shadowhunter_A0XzD6MmBXYdk7Ps.json index 9c9a31c7..b942cc20 100644 --- a/src/packs/domains/domainCard_Shadowhunter_A0XzD6MmBXYdk7Ps.json +++ b/src/packs/domains/domainCard_Shadowhunter_A0XzD6MmBXYdk7Ps.json @@ -57,7 +57,7 @@ "changes": [ { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "Attack rolls while shrouded in low light or darkness.", "priority": null } diff --git a/src/packs/domains/domainCard_Shield_Aura_rfIv6lln40Fh6EIl.json b/src/packs/domains/domainCard_Shield_Aura_rfIv6lln40Fh6EIl.json index 6a2b565c..03155bf1 100644 --- a/src/packs/domains/domainCard_Shield_Aura_rfIv6lln40Fh6EIl.json +++ b/src/packs/domains/domainCard_Shield_Aura_rfIv6lln40Fh6EIl.json @@ -68,7 +68,7 @@ "changes": [ { "key": "system.rules.damageReduction.increasePerArmorMark", - "mode": 2, + "type": "add", "value": "+1", "priority": null } diff --git a/src/packs/domains/domainCard_Shrug_It_Off_JwfhtgmmuRxg4zhI.json b/src/packs/domains/domainCard_Shrug_It_Off_JwfhtgmmuRxg4zhI.json index b6461215..f73deba0 100644 --- a/src/packs/domains/domainCard_Shrug_It_Off_JwfhtgmmuRxg4zhI.json +++ b/src/packs/domains/domainCard_Shrug_It_Off_JwfhtgmmuRxg4zhI.json @@ -82,7 +82,7 @@ "changes": [ { "key": "system.rules.damageReduction.stressDamageReduction.any.cost", - "mode": 5, + "type": "override", "value": "1", "priority": null } diff --git a/src/packs/domains/domainCard_Specter_of_the_Dark_iQhgqmLwhcSTYnvr.json b/src/packs/domains/domainCard_Specter_of_the_Dark_iQhgqmLwhcSTYnvr.json index 0ee15cb9..5291c31c 100644 --- a/src/packs/domains/domainCard_Specter_of_the_Dark_iQhgqmLwhcSTYnvr.json +++ b/src/packs/domains/domainCard_Specter_of_the_Dark_iQhgqmLwhcSTYnvr.json @@ -66,7 +66,7 @@ "changes": [ { "key": "system.resistance.physical.immunity", - "mode": 5, + "type": "override", "value": "true", "priority": null } diff --git a/src/packs/domains/domainCard_Splendor_Touched_JT5dM3gVL6chDBYU.json b/src/packs/domains/domainCard_Splendor_Touched_JT5dM3gVL6chDBYU.json index a7609b62..730ac290 100644 --- a/src/packs/domains/domainCard_Splendor_Touched_JT5dM3gVL6chDBYU.json +++ b/src/packs/domains/domainCard_Splendor_Touched_JT5dM3gVL6chDBYU.json @@ -27,7 +27,7 @@ "changes": [ { "key": "system.damageThresholds.severe", - "mode": 2, + "type": "add", "value": "+3", "priority": null } diff --git a/src/packs/domains/domainCard_Uncanny_Disguise_TV56wSysbU5xAlOa.json b/src/packs/domains/domainCard_Uncanny_Disguise_TV56wSysbU5xAlOa.json index 6088f1dc..e0a2d403 100644 --- a/src/packs/domains/domainCard_Uncanny_Disguise_TV56wSysbU5xAlOa.json +++ b/src/packs/domains/domainCard_Uncanny_Disguise_TV56wSysbU5xAlOa.json @@ -100,7 +100,7 @@ "changes": [ { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "Presence Rolls to avoid scrutiny", "priority": null } diff --git a/src/packs/domains/domainCard_Untouchable_9QElncQUDSakuSdR.json b/src/packs/domains/domainCard_Untouchable_9QElncQUDSakuSdR.json index a5326403..9693b6bf 100644 --- a/src/packs/domains/domainCard_Untouchable_9QElncQUDSakuSdR.json +++ b/src/packs/domains/domainCard_Untouchable_9QElncQUDSakuSdR.json @@ -26,7 +26,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "ceil(@system.traits.agility.value / 2)", "priority": 21 } diff --git a/src/packs/domains/domainCard_Vitality_sWUlSPOJEaXyQLCj.json b/src/packs/domains/domainCard_Vitality_sWUlSPOJEaXyQLCj.json index 2d42ef9f..6b4f1740 100644 --- a/src/packs/domains/domainCard_Vitality_sWUlSPOJEaXyQLCj.json +++ b/src/packs/domains/domainCard_Vitality_sWUlSPOJEaXyQLCj.json @@ -69,7 +69,7 @@ "changes": [ { "key": "system.resources.hitPoints.max", - "mode": 2, + "type": "add", "value": "1", "priority": null } @@ -106,7 +106,7 @@ "changes": [ { "key": "system.resources.stress.max", - "mode": 2, + "type": "add", "value": "1", "priority": null } @@ -143,13 +143,13 @@ "changes": [ { "key": "system.damageThresholds.major", - "mode": 2, + "type": "add", "value": "2", "priority": null }, { "key": "system.damageThresholds.severe", - "mode": 2, + "type": "add", "value": "2", "priority": null } diff --git a/src/packs/domains/domainCard_Voice_of_Reason_t3RRGH6mMYYJJCcF.json b/src/packs/domains/domainCard_Voice_of_Reason_t3RRGH6mMYYJJCcF.json index 5b6c21ca..86305f00 100644 --- a/src/packs/domains/domainCard_Voice_of_Reason_t3RRGH6mMYYJJCcF.json +++ b/src/packs/domains/domainCard_Voice_of_Reason_t3RRGH6mMYYJJCcF.json @@ -26,13 +26,13 @@ "changes": [ { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "De-escalate violent situations.", "priority": null }, { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "Convince someone to follow your lead.", "priority": null } @@ -69,7 +69,7 @@ "changes": [ { "key": "system.proficiency", - "mode": 2, + "type": "add", "value": "+1", "priority": null } diff --git a/src/packs/environments/environment_Cult_Ritual_QAXXiOKBDmCTauHD.json b/src/packs/environments/environment_Cult_Ritual_QAXXiOKBDmCTauHD.json index f3845f23..d6809cd1 100644 --- a/src/packs/environments/environment_Cult_Ritual_QAXXiOKBDmCTauHD.json +++ b/src/packs/environments/environment_Cult_Ritual_QAXXiOKBDmCTauHD.json @@ -186,7 +186,7 @@ "changes": [ { "key": "system.rules.dualityRoll.defaultHopeDice", - "mode": 5, + "type": "override", "value": "d10", "priority": null } @@ -312,13 +312,13 @@ "changes": [ { "key": "system.bonuses.damage.physical.dice", - "mode": 2, + "type": "add", "value": "1d10", "priority": null }, { "key": "system.bonuses.damage.magical.dice", - "mode": 2, + "type": "add", "value": "1d10", "priority": null } diff --git a/src/packs/environments/environment_Divine_Usurpation_4DLYez7VbMCFDAuZ.json b/src/packs/environments/environment_Divine_Usurpation_4DLYez7VbMCFDAuZ.json index e7ca7832..339ac5cd 100644 --- a/src/packs/environments/environment_Divine_Usurpation_4DLYez7VbMCFDAuZ.json +++ b/src/packs/environments/environment_Divine_Usurpation_4DLYez7VbMCFDAuZ.json @@ -425,25 +425,25 @@ "changes": [ { "key": "system.difficulty", - "mode": 2, + "type": "add", "value": "0", "priority": null }, { "key": "system.bonuses.damage.physical.bonus", - "mode": 2, + "type": "add", "value": "0", "priority": null }, { "key": "system.bonuses.damage.magical.bonus", - "mode": 2, + "type": "add", "value": "0", "priority": null }, { "key": "system.bonuses.roll.attack.bonus", - "mode": 2, + "type": "add", "value": "0", "priority": null } diff --git a/src/packs/environments/environment_Haunted_City_OzYbizKraK92FDiI.json b/src/packs/environments/environment_Haunted_City_OzYbizKraK92FDiI.json index f2da690b..564612cb 100644 --- a/src/packs/environments/environment_Haunted_City_OzYbizKraK92FDiI.json +++ b/src/packs/environments/environment_Haunted_City_OzYbizKraK92FDiI.json @@ -206,7 +206,7 @@ "changes": [ { "key": "system.resistance.physical.resistance", - "mode": 5, + "type": "override", "value": "1", "priority": null } diff --git a/src/packs/items/armors/armor_Advanced_Chainmail_Armor_LzLOJ9EVaHWAjoq9.json b/src/packs/items/armors/armor_Advanced_Chainmail_Armor_LzLOJ9EVaHWAjoq9.json index 4566396a..53f9bf79 100644 --- a/src/packs/items/armors/armor_Advanced_Chainmail_Armor_LzLOJ9EVaHWAjoq9.json +++ b/src/packs/items/armors/armor_Advanced_Chainmail_Armor_LzLOJ9EVaHWAjoq9.json @@ -45,7 +45,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/armors/armor_Advanced_Full_Plate_Armor_crIbCb9NZ4K0VpoU.json b/src/packs/items/armors/armor_Advanced_Full_Plate_Armor_crIbCb9NZ4K0VpoU.json index 52adc7aa..5fde7416 100644 --- a/src/packs/items/armors/armor_Advanced_Full_Plate_Armor_crIbCb9NZ4K0VpoU.json +++ b/src/packs/items/armors/armor_Advanced_Full_Plate_Armor_crIbCb9NZ4K0VpoU.json @@ -45,12 +45,12 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "-2" }, { "key": "system.traits.agility.value", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/armors/armor_Advanced_Gambeson_Armor_epkAmlZVk7HOfUUT.json b/src/packs/items/armors/armor_Advanced_Gambeson_Armor_epkAmlZVk7HOfUUT.json index 36edec39..fe6a96fb 100644 --- a/src/packs/items/armors/armor_Advanced_Gambeson_Armor_epkAmlZVk7HOfUUT.json +++ b/src/packs/items/armors/armor_Advanced_Gambeson_Armor_epkAmlZVk7HOfUUT.json @@ -45,7 +45,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "1" } ], diff --git a/src/packs/items/armors/armor_Bellamoi_Fine_Armor_WuoVwZA53XRAIt6d.json b/src/packs/items/armors/armor_Bellamoi_Fine_Armor_WuoVwZA53XRAIt6d.json index c470de87..8df5162f 100644 --- a/src/packs/items/armors/armor_Bellamoi_Fine_Armor_WuoVwZA53XRAIt6d.json +++ b/src/packs/items/armors/armor_Bellamoi_Fine_Armor_WuoVwZA53XRAIt6d.json @@ -45,7 +45,7 @@ "changes": [ { "key": "system.traits.presence.value", - "mode": 2, + "type": "add", "value": "1" } ], diff --git a/src/packs/items/armors/armor_Bladefare_Armor_mNN6pvcsS10ChrWF.json b/src/packs/items/armors/armor_Bladefare_Armor_mNN6pvcsS10ChrWF.json index 4ee73939..70a8b229 100644 --- a/src/packs/items/armors/armor_Bladefare_Armor_mNN6pvcsS10ChrWF.json +++ b/src/packs/items/armors/armor_Bladefare_Armor_mNN6pvcsS10ChrWF.json @@ -45,7 +45,7 @@ "changes": [ { "key": "system.rules.damageReduction.physical", - "mode": 5, + "type": "override", "value": "1" } ], diff --git a/src/packs/items/armors/armor_Chainmail_Armor_haULhuEg37zUUvhb.json b/src/packs/items/armors/armor_Chainmail_Armor_haULhuEg37zUUvhb.json index 4f0719a7..c41978b9 100644 --- a/src/packs/items/armors/armor_Chainmail_Armor_haULhuEg37zUUvhb.json +++ b/src/packs/items/armors/armor_Chainmail_Armor_haULhuEg37zUUvhb.json @@ -45,7 +45,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/armors/armor_Channeling_Armor_vMJxEWz1srfwMsoj.json b/src/packs/items/armors/armor_Channeling_Armor_vMJxEWz1srfwMsoj.json index e805d5d1..9f24ee70 100644 --- a/src/packs/items/armors/armor_Channeling_Armor_vMJxEWz1srfwMsoj.json +++ b/src/packs/items/armors/armor_Channeling_Armor_vMJxEWz1srfwMsoj.json @@ -45,7 +45,7 @@ "changes": [ { "key": "system.bonuses.roll.spellcast", - "mode": 2, + "type": "add", "value": "1" } ], diff --git a/src/packs/items/armors/armor_Elundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json b/src/packs/items/armors/armor_Elundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json index 1cf74e2e..d8de4497 100644 --- a/src/packs/items/armors/armor_Elundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json +++ b/src/packs/items/armors/armor_Elundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json @@ -45,7 +45,7 @@ "changes": [ { "key": "system.resistance.magical.reduction", - "mode": 2, + "type": "add", "value": "@system.armorScore", "priority": 21 } diff --git a/src/packs/items/armors/armor_Full_Fortified_Armor_7emTSt6nhZuTlvt5.json b/src/packs/items/armors/armor_Full_Fortified_Armor_7emTSt6nhZuTlvt5.json index 9f2d7ece..acc8c125 100644 --- a/src/packs/items/armors/armor_Full_Fortified_Armor_7emTSt6nhZuTlvt5.json +++ b/src/packs/items/armors/armor_Full_Fortified_Armor_7emTSt6nhZuTlvt5.json @@ -45,7 +45,7 @@ "changes": [ { "key": "system.rules.damageReduction.increasePerArmorMark", - "mode": 5, + "type": "override", "value": "2" } ], diff --git a/src/packs/items/armors/armor_Full_Plate_Armor_UdUJNa31WxFW2noa.json b/src/packs/items/armors/armor_Full_Plate_Armor_UdUJNa31WxFW2noa.json index 7701d063..7d08e57e 100644 --- a/src/packs/items/armors/armor_Full_Plate_Armor_UdUJNa31WxFW2noa.json +++ b/src/packs/items/armors/armor_Full_Plate_Armor_UdUJNa31WxFW2noa.json @@ -45,12 +45,12 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "-2" }, { "key": "system.traits.agility.value", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/armors/armor_Gambeson_Armor_yJFp1bfpecDcStVK.json b/src/packs/items/armors/armor_Gambeson_Armor_yJFp1bfpecDcStVK.json index 0ede5b60..0ab7203b 100644 --- a/src/packs/items/armors/armor_Gambeson_Armor_yJFp1bfpecDcStVK.json +++ b/src/packs/items/armors/armor_Gambeson_Armor_yJFp1bfpecDcStVK.json @@ -45,7 +45,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "1" } ], diff --git a/src/packs/items/armors/armor_Improved_Chainmail_Armor_K5WkjS0NGqHYmhU3.json b/src/packs/items/armors/armor_Improved_Chainmail_Armor_K5WkjS0NGqHYmhU3.json index ef93ecdd..c87b1f51 100644 --- a/src/packs/items/armors/armor_Improved_Chainmail_Armor_K5WkjS0NGqHYmhU3.json +++ b/src/packs/items/armors/armor_Improved_Chainmail_Armor_K5WkjS0NGqHYmhU3.json @@ -45,7 +45,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/armors/armor_Improved_Full_Plate_Armor_9f7RozpPTqrzJS1m.json b/src/packs/items/armors/armor_Improved_Full_Plate_Armor_9f7RozpPTqrzJS1m.json index 1723c53a..85b4ed88 100644 --- a/src/packs/items/armors/armor_Improved_Full_Plate_Armor_9f7RozpPTqrzJS1m.json +++ b/src/packs/items/armors/armor_Improved_Full_Plate_Armor_9f7RozpPTqrzJS1m.json @@ -45,12 +45,12 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "-2" }, { "key": "system.traits.agility.value", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/armors/armor_Improved_Gambeson_Armor_jphnMZjnS2FkOH3s.json b/src/packs/items/armors/armor_Improved_Gambeson_Armor_jphnMZjnS2FkOH3s.json index a2ff6554..4f2f3c2b 100644 --- a/src/packs/items/armors/armor_Improved_Gambeson_Armor_jphnMZjnS2FkOH3s.json +++ b/src/packs/items/armors/armor_Improved_Gambeson_Armor_jphnMZjnS2FkOH3s.json @@ -45,7 +45,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "1" } ], diff --git a/src/packs/items/armors/armor_Irontree_Breastplate_Armor_tzZntboNtHL5C6VM.json b/src/packs/items/armors/armor_Irontree_Breastplate_Armor_tzZntboNtHL5C6VM.json index a664ad9c..d73d5e45 100644 --- a/src/packs/items/armors/armor_Irontree_Breastplate_Armor_tzZntboNtHL5C6VM.json +++ b/src/packs/items/armors/armor_Irontree_Breastplate_Armor_tzZntboNtHL5C6VM.json @@ -45,13 +45,13 @@ "changes": [ { "key": "system.damageThresholds.major", - "mode": 2, + "type": "add", "value": "2", "priority": null }, { "key": "system.damageThresholds.severe", - "mode": 2, + "type": "add", "value": "2", "priority": null } diff --git a/src/packs/items/armors/armor_Legendary_Chainmail_Armor_EsIN5OLKe9ZYFNXZ.json b/src/packs/items/armors/armor_Legendary_Chainmail_Armor_EsIN5OLKe9ZYFNXZ.json index 6c93cbe4..472b243c 100644 --- a/src/packs/items/armors/armor_Legendary_Chainmail_Armor_EsIN5OLKe9ZYFNXZ.json +++ b/src/packs/items/armors/armor_Legendary_Chainmail_Armor_EsIN5OLKe9ZYFNXZ.json @@ -45,7 +45,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/armors/armor_Legendary_Full_Plate_Armor_SXWjUR2aUR6bYvdl.json b/src/packs/items/armors/armor_Legendary_Full_Plate_Armor_SXWjUR2aUR6bYvdl.json index f66e4c38..52f52882 100644 --- a/src/packs/items/armors/armor_Legendary_Full_Plate_Armor_SXWjUR2aUR6bYvdl.json +++ b/src/packs/items/armors/armor_Legendary_Full_Plate_Armor_SXWjUR2aUR6bYvdl.json @@ -45,12 +45,12 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "-2" }, { "key": "system.traits.agility.value", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/armors/armor_Legendary_Gambeson_Armor_c6tMXz4rPf9ioQrf.json b/src/packs/items/armors/armor_Legendary_Gambeson_Armor_c6tMXz4rPf9ioQrf.json index 4cf1c856..a703c7c7 100644 --- a/src/packs/items/armors/armor_Legendary_Gambeson_Armor_c6tMXz4rPf9ioQrf.json +++ b/src/packs/items/armors/armor_Legendary_Gambeson_Armor_c6tMXz4rPf9ioQrf.json @@ -45,7 +45,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "1" } ], diff --git a/src/packs/items/armors/armor_Monett_s_Cloak_AQzU2RsqS5V5bd1v.json b/src/packs/items/armors/armor_Monett_s_Cloak_AQzU2RsqS5V5bd1v.json index 6bb479a4..a160aeee 100644 --- a/src/packs/items/armors/armor_Monett_s_Cloak_AQzU2RsqS5V5bd1v.json +++ b/src/packs/items/armors/armor_Monett_s_Cloak_AQzU2RsqS5V5bd1v.json @@ -45,7 +45,7 @@ "changes": [ { "key": "system.rules.damageReduction.magical", - "mode": 5, + "type": "override", "value": "1" } ], diff --git a/src/packs/items/armors/armor_Savior_Chainmail_8X16lJQ3xltTwynm.json b/src/packs/items/armors/armor_Savior_Chainmail_8X16lJQ3xltTwynm.json index 6826254a..0a7f55ba 100644 --- a/src/packs/items/armors/armor_Savior_Chainmail_8X16lJQ3xltTwynm.json +++ b/src/packs/items/armors/armor_Savior_Chainmail_8X16lJQ3xltTwynm.json @@ -45,37 +45,37 @@ "changes": [ { "key": "system.traits.agility.value", - "mode": 2, + "type": "add", "value": "-1" }, { "key": "system.traits.strength.value", - "mode": 2, + "type": "add", "value": "-1" }, { "key": "system.traits.finesse.value", - "mode": 2, + "type": "add", "value": "-1" }, { "key": "system.traits.instinct.value", - "mode": 2, + "type": "add", "value": "-1" }, { "key": "system.traits.presence.value", - "mode": 2, + "type": "add", "value": "-1" }, { "key": "system.traits.knowledge.value", - "mode": 2, + "type": "add", "value": "-1" }, { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/armors/armor_Spiked_Plate_Armor_QjwsIhXKqnlvRBMv.json b/src/packs/items/armors/armor_Spiked_Plate_Armor_QjwsIhXKqnlvRBMv.json index ac9115a2..8f91f92b 100644 --- a/src/packs/items/armors/armor_Spiked_Plate_Armor_QjwsIhXKqnlvRBMv.json +++ b/src/packs/items/armors/armor_Spiked_Plate_Armor_QjwsIhXKqnlvRBMv.json @@ -45,12 +45,12 @@ "changes": [ { "key": "system.bonuses.damage.primaryWeapon.dice", - "mode": 2, + "type": "add", "value": "1d4" }, { "key": "system.bonuses.damage.secondaryWeapon.dice", - "mode": 2, + "type": "add", "value": "1d4" } ], diff --git a/src/packs/items/consumables/consumable_Attune_Potion_JGD3M9hBHtVAA8XP.json b/src/packs/items/consumables/consumable_Attune_Potion_JGD3M9hBHtVAA8XP.json index e034976a..d17ae550 100644 --- a/src/packs/items/consumables/consumable_Attune_Potion_JGD3M9hBHtVAA8XP.json +++ b/src/packs/items/consumables/consumable_Attune_Potion_JGD3M9hBHtVAA8XP.json @@ -63,7 +63,7 @@ "changes": [ { "key": "system.traits.instinct.value", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/items/consumables/consumable_Bolster_Potion_FOPQNqXbiVO0ilYL.json b/src/packs/items/consumables/consumable_Bolster_Potion_FOPQNqXbiVO0ilYL.json index 421acdc3..d200d825 100644 --- a/src/packs/items/consumables/consumable_Bolster_Potion_FOPQNqXbiVO0ilYL.json +++ b/src/packs/items/consumables/consumable_Bolster_Potion_FOPQNqXbiVO0ilYL.json @@ -63,7 +63,7 @@ "changes": [ { "key": "system.traits.strength.value", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/items/consumables/consumable_Charm_Potion_CVBbFfOY75YwyQsp.json b/src/packs/items/consumables/consumable_Charm_Potion_CVBbFfOY75YwyQsp.json index f1d7b058..015f334e 100644 --- a/src/packs/items/consumables/consumable_Charm_Potion_CVBbFfOY75YwyQsp.json +++ b/src/packs/items/consumables/consumable_Charm_Potion_CVBbFfOY75YwyQsp.json @@ -63,7 +63,7 @@ "changes": [ { "key": "system.traits.presence.value", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/items/consumables/consumable_Control_Potion_eeBhZSGLjuNZuJuI.json b/src/packs/items/consumables/consumable_Control_Potion_eeBhZSGLjuNZuJuI.json index 2c6b9a93..dbb6b796 100644 --- a/src/packs/items/consumables/consumable_Control_Potion_eeBhZSGLjuNZuJuI.json +++ b/src/packs/items/consumables/consumable_Control_Potion_eeBhZSGLjuNZuJuI.json @@ -63,7 +63,7 @@ "changes": [ { "key": "system.traits.finesse.value", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/items/consumables/consumable_Enlighten_Potion_aWHSO2AqDufi7nL4.json b/src/packs/items/consumables/consumable_Enlighten_Potion_aWHSO2AqDufi7nL4.json index bff70126..5d9712b1 100644 --- a/src/packs/items/consumables/consumable_Enlighten_Potion_aWHSO2AqDufi7nL4.json +++ b/src/packs/items/consumables/consumable_Enlighten_Potion_aWHSO2AqDufi7nL4.json @@ -63,7 +63,7 @@ "changes": [ { "key": "system.traits.knowledge.value", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/items/consumables/consumable_Grindletooth_Venom_8WkhvSzeOmLdnoLJ.json b/src/packs/items/consumables/consumable_Grindletooth_Venom_8WkhvSzeOmLdnoLJ.json index 1472c00a..22cc7723 100644 --- a/src/packs/items/consumables/consumable_Grindletooth_Venom_8WkhvSzeOmLdnoLJ.json +++ b/src/packs/items/consumables/consumable_Grindletooth_Venom_8WkhvSzeOmLdnoLJ.json @@ -63,7 +63,7 @@ "changes": [ { "key": "system.bonuses.damage.physical.dice", - "mode": 2, + "type": "add", "value": "1d6", "priority": null } diff --git a/src/packs/items/consumables/consumable_Improved_Grindletooth_Venom_BqBWXXe9T07AMV4u.json b/src/packs/items/consumables/consumable_Improved_Grindletooth_Venom_BqBWXXe9T07AMV4u.json index ed8ca562..3e5907d8 100644 --- a/src/packs/items/consumables/consumable_Improved_Grindletooth_Venom_BqBWXXe9T07AMV4u.json +++ b/src/packs/items/consumables/consumable_Improved_Grindletooth_Venom_BqBWXXe9T07AMV4u.json @@ -63,7 +63,7 @@ "changes": [ { "key": "system.bonuses.damage.physical.dice", - "mode": 2, + "type": "add", "value": "1d8", "priority": null } diff --git a/src/packs/items/consumables/consumable_Major_Attune_Potion_CCPFm5iXXwvyYYwR.json b/src/packs/items/consumables/consumable_Major_Attune_Potion_CCPFm5iXXwvyYYwR.json index b27fee91..784ed534 100644 --- a/src/packs/items/consumables/consumable_Major_Attune_Potion_CCPFm5iXXwvyYYwR.json +++ b/src/packs/items/consumables/consumable_Major_Attune_Potion_CCPFm5iXXwvyYYwR.json @@ -63,7 +63,7 @@ "changes": [ { "key": "system.traits.instinct.value", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/items/consumables/consumable_Major_Bolster_Potion_mnyQDRtngWWQeRXF.json b/src/packs/items/consumables/consumable_Major_Bolster_Potion_mnyQDRtngWWQeRXF.json index 95cd6c92..fc35ef93 100644 --- a/src/packs/items/consumables/consumable_Major_Bolster_Potion_mnyQDRtngWWQeRXF.json +++ b/src/packs/items/consumables/consumable_Major_Bolster_Potion_mnyQDRtngWWQeRXF.json @@ -63,7 +63,7 @@ "changes": [ { "key": "system.traits.strength.value", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/items/consumables/consumable_Major_Charm_Potion_IJLAUlQymbSjzsri.json b/src/packs/items/consumables/consumable_Major_Charm_Potion_IJLAUlQymbSjzsri.json index c7e22aeb..c70ff759 100644 --- a/src/packs/items/consumables/consumable_Major_Charm_Potion_IJLAUlQymbSjzsri.json +++ b/src/packs/items/consumables/consumable_Major_Charm_Potion_IJLAUlQymbSjzsri.json @@ -63,7 +63,7 @@ "changes": [ { "key": "system.traits.presence.value", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/items/consumables/consumable_Major_Control_Potion_80s1FLmTLtohZ5GH.json b/src/packs/items/consumables/consumable_Major_Control_Potion_80s1FLmTLtohZ5GH.json index 1dabf6c6..3f8786d2 100644 --- a/src/packs/items/consumables/consumable_Major_Control_Potion_80s1FLmTLtohZ5GH.json +++ b/src/packs/items/consumables/consumable_Major_Control_Potion_80s1FLmTLtohZ5GH.json @@ -63,7 +63,7 @@ "changes": [ { "key": "system.traits.finesse.value", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/items/consumables/consumable_Major_Enlighten_Potion_SDdv1G2veMLKrxcJ.json b/src/packs/items/consumables/consumable_Major_Enlighten_Potion_SDdv1G2veMLKrxcJ.json index 5a9a2d28..ea3e4ffc 100644 --- a/src/packs/items/consumables/consumable_Major_Enlighten_Potion_SDdv1G2veMLKrxcJ.json +++ b/src/packs/items/consumables/consumable_Major_Enlighten_Potion_SDdv1G2veMLKrxcJ.json @@ -63,7 +63,7 @@ "changes": [ { "key": "system.traits.knowledge.value", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/items/consumables/consumable_Mythic_Dust_Zsh2AvZr8EkGtLyw.json b/src/packs/items/consumables/consumable_Mythic_Dust_Zsh2AvZr8EkGtLyw.json index 62d9e6bf..596b5ac8 100644 --- a/src/packs/items/consumables/consumable_Mythic_Dust_Zsh2AvZr8EkGtLyw.json +++ b/src/packs/items/consumables/consumable_Mythic_Dust_Zsh2AvZr8EkGtLyw.json @@ -63,7 +63,7 @@ "changes": [ { "key": "system.bonuses.damage.magical.dice", - "mode": 2, + "type": "add", "value": "1d12", "priority": null } diff --git a/src/packs/items/consumables/consumable_Potion_of_Stability_dvL8oaxpEF6jKvYN.json b/src/packs/items/consumables/consumable_Potion_of_Stability_dvL8oaxpEF6jKvYN.json index ddff33f0..ecfc34ea 100644 --- a/src/packs/items/consumables/consumable_Potion_of_Stability_dvL8oaxpEF6jKvYN.json +++ b/src/packs/items/consumables/consumable_Potion_of_Stability_dvL8oaxpEF6jKvYN.json @@ -63,13 +63,13 @@ "changes": [ { "key": "system.bonuses.rest.shortRest.shortMoves", - "mode": 2, + "type": "add", "value": "1", "priority": null }, { "key": "system.bonuses.rest.longRest.longMoves", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/items/consumables/consumable_Redthorn_Saliva_s2Exl2XFuoOhtIov.json b/src/packs/items/consumables/consumable_Redthorn_Saliva_s2Exl2XFuoOhtIov.json index 0a2b6469..ec8e6d5a 100644 --- a/src/packs/items/consumables/consumable_Redthorn_Saliva_s2Exl2XFuoOhtIov.json +++ b/src/packs/items/consumables/consumable_Redthorn_Saliva_s2Exl2XFuoOhtIov.json @@ -63,7 +63,7 @@ "changes": [ { "key": "system.bonuses.damage.physical.dice", - "mode": 2, + "type": "add", "value": "1d12", "priority": null } diff --git a/src/packs/items/consumables/consumable_Stride_Potion_lNtcrkgFGOJNaroE.json b/src/packs/items/consumables/consumable_Stride_Potion_lNtcrkgFGOJNaroE.json index 76d43d33..c13af610 100644 --- a/src/packs/items/consumables/consumable_Stride_Potion_lNtcrkgFGOJNaroE.json +++ b/src/packs/items/consumables/consumable_Stride_Potion_lNtcrkgFGOJNaroE.json @@ -63,7 +63,7 @@ "changes": [ { "key": "system.traits.agility.value", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/items/loot/loot_Arcane_Prism_Mn1eo2Mdtu1kzyxB.json b/src/packs/items/loot/loot_Arcane_Prism_Mn1eo2Mdtu1kzyxB.json index 016c7262..2a6729d2 100644 --- a/src/packs/items/loot/loot_Arcane_Prism_Mn1eo2Mdtu1kzyxB.json +++ b/src/packs/items/loot/loot_Arcane_Prism_Mn1eo2Mdtu1kzyxB.json @@ -53,7 +53,7 @@ "changes": [ { "key": "system.bonuses.roll.spellcast.bonus", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/items/loot/loot_Attune_Relic_vK6bKyQTT3m8WvMh.json b/src/packs/items/loot/loot_Attune_Relic_vK6bKyQTT3m8WvMh.json index abed8a55..60afc84c 100644 --- a/src/packs/items/loot/loot_Attune_Relic_vK6bKyQTT3m8WvMh.json +++ b/src/packs/items/loot/loot_Attune_Relic_vK6bKyQTT3m8WvMh.json @@ -23,7 +23,7 @@ "changes": [ { "key": "system.traits.instinct.value", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/items/loot/loot_Bolster_Relic_m3EpxlDgxn2tCDDR.json b/src/packs/items/loot/loot_Bolster_Relic_m3EpxlDgxn2tCDDR.json index d45af6dc..2d7c360e 100644 --- a/src/packs/items/loot/loot_Bolster_Relic_m3EpxlDgxn2tCDDR.json +++ b/src/packs/items/loot/loot_Bolster_Relic_m3EpxlDgxn2tCDDR.json @@ -23,7 +23,7 @@ "changes": [ { "key": "system.traits.strength.value", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/items/loot/loot_Charging_Quiver_gsUDP90d4SRtLEUn.json b/src/packs/items/loot/loot_Charging_Quiver_gsUDP90d4SRtLEUn.json index 8f9a5904..a31a4e93 100644 --- a/src/packs/items/loot/loot_Charging_Quiver_gsUDP90d4SRtLEUn.json +++ b/src/packs/items/loot/loot_Charging_Quiver_gsUDP90d4SRtLEUn.json @@ -23,13 +23,13 @@ "changes": [ { "key": "system.bonuses.damage.physical.bonus", - "mode": 2, + "type": "add", "value": "@system.tier", "priority": null }, { "key": "system.bonuses.damage.magical.bonus", - "mode": 2, + "type": "add", "value": "@system.tier", "priority": null } diff --git a/src/packs/items/loot/loot_Charm_Relic_9P9jqGSlxVCbTdLe.json b/src/packs/items/loot/loot_Charm_Relic_9P9jqGSlxVCbTdLe.json index f3313941..514df12b 100644 --- a/src/packs/items/loot/loot_Charm_Relic_9P9jqGSlxVCbTdLe.json +++ b/src/packs/items/loot/loot_Charm_Relic_9P9jqGSlxVCbTdLe.json @@ -23,7 +23,7 @@ "changes": [ { "key": "system.traits.presence.value", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/items/loot/loot_Control_Relic_QPGBDItjrRhXU6iJ.json b/src/packs/items/loot/loot_Control_Relic_QPGBDItjrRhXU6iJ.json index 254f4017..5df7b228 100644 --- a/src/packs/items/loot/loot_Control_Relic_QPGBDItjrRhXU6iJ.json +++ b/src/packs/items/loot/loot_Control_Relic_QPGBDItjrRhXU6iJ.json @@ -23,7 +23,7 @@ "changes": [ { "key": "system.traits.finesse.value", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/items/loot/loot_Enlighten_Relic_vSGx1f9SYUiA29L3.json b/src/packs/items/loot/loot_Enlighten_Relic_vSGx1f9SYUiA29L3.json index a6ca361e..a99e6b9b 100644 --- a/src/packs/items/loot/loot_Enlighten_Relic_vSGx1f9SYUiA29L3.json +++ b/src/packs/items/loot/loot_Enlighten_Relic_vSGx1f9SYUiA29L3.json @@ -23,7 +23,7 @@ "changes": [ { "key": "system.traits.knowledge.value", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/items/loot/loot_Piercing_Arrows_I63LTFD6GXHgyGpR.json b/src/packs/items/loot/loot_Piercing_Arrows_I63LTFD6GXHgyGpR.json index 1dcf503c..3d0493c4 100644 --- a/src/packs/items/loot/loot_Piercing_Arrows_I63LTFD6GXHgyGpR.json +++ b/src/packs/items/loot/loot_Piercing_Arrows_I63LTFD6GXHgyGpR.json @@ -46,13 +46,13 @@ "changes": [ { "key": "system.bonuses.damage.physical.bonus", - "mode": 2, + "type": "add", "value": "@system.proficiency", "priority": null }, { "key": "system.bonuses.damage.magical.bonus", - "mode": 2, + "type": "add", "value": "@system.proficiency", "priority": null } diff --git a/src/packs/items/loot/loot_Ring_of_Resistance_aUqRifqR5JXXa1dN.json b/src/packs/items/loot/loot_Ring_of_Resistance_aUqRifqR5JXXa1dN.json index 47f38431..6db909b2 100644 --- a/src/packs/items/loot/loot_Ring_of_Resistance_aUqRifqR5JXXa1dN.json +++ b/src/packs/items/loot/loot_Ring_of_Resistance_aUqRifqR5JXXa1dN.json @@ -53,13 +53,13 @@ "changes": [ { "key": "system.resistance.magical.resistance", - "mode": 5, + "type": "override", "value": "1", "priority": null }, { "key": "system.resistance.physical.resistance", - "mode": 5, + "type": "override", "value": "1", "priority": null } diff --git a/src/packs/items/loot/loot_Stride_Relic_FfJISMzYATaPQPLc.json b/src/packs/items/loot/loot_Stride_Relic_FfJISMzYATaPQPLc.json index 703d8439..50917309 100644 --- a/src/packs/items/loot/loot_Stride_Relic_FfJISMzYATaPQPLc.json +++ b/src/packs/items/loot/loot_Stride_Relic_FfJISMzYATaPQPLc.json @@ -23,7 +23,7 @@ "changes": [ { "key": "system.traits.agility.value", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/items/weapons/weapon_Aantari_Bow_ijodu5yNBoMxpkHV.json b/src/packs/items/weapons/weapon_Aantari_Bow_ijodu5yNBoMxpkHV.json index 975b2489..195d3ef9 100644 --- a/src/packs/items/weapons/weapon_Aantari_Bow_ijodu5yNBoMxpkHV.json +++ b/src/packs/items/weapons/weapon_Aantari_Bow_ijodu5yNBoMxpkHV.json @@ -119,7 +119,7 @@ "changes": [ { "key": "system.bonuses.roll.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "1" } ] diff --git a/src/packs/items/weapons/weapon_Advanced_Arcane_Frame_Wheelchair_la3sAWgnvadc4NvP.json b/src/packs/items/weapons/weapon_Advanced_Arcane_Frame_Wheelchair_la3sAWgnvadc4NvP.json index 8910a1a4..042d922a 100644 --- a/src/packs/items/weapons/weapon_Advanced_Arcane_Frame_Wheelchair_la3sAWgnvadc4NvP.json +++ b/src/packs/items/weapons/weapon_Advanced_Arcane_Frame_Wheelchair_la3sAWgnvadc4NvP.json @@ -118,7 +118,7 @@ "changes": [ { "key": "system.bonuses.roll.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "1" } ] diff --git a/src/packs/items/weapons/weapon_Advanced_Broadsword_WtQAGz0TUgz8Xg70.json b/src/packs/items/weapons/weapon_Advanced_Broadsword_WtQAGz0TUgz8Xg70.json index 23c78cc8..f7e1f872 100644 --- a/src/packs/items/weapons/weapon_Advanced_Broadsword_WtQAGz0TUgz8Xg70.json +++ b/src/packs/items/weapons/weapon_Advanced_Broadsword_WtQAGz0TUgz8Xg70.json @@ -119,7 +119,7 @@ "changes": [ { "key": "system.bonuses.roll.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "1" } ] diff --git a/src/packs/items/weapons/weapon_Advanced_Greatsword_MAC6YWTo4lzSotQc.json b/src/packs/items/weapons/weapon_Advanced_Greatsword_MAC6YWTo4lzSotQc.json index 9e04bf7a..39d1b6f2 100644 --- a/src/packs/items/weapons/weapon_Advanced_Greatsword_MAC6YWTo4lzSotQc.json +++ b/src/packs/items/weapons/weapon_Advanced_Greatsword_MAC6YWTo4lzSotQc.json @@ -116,7 +116,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/weapons/weapon_Advanced_Halberd_C8gQn7onAc9wsrCs.json b/src/packs/items/weapons/weapon_Advanced_Halberd_C8gQn7onAc9wsrCs.json index 6c11724c..92841972 100644 --- a/src/packs/items/weapons/weapon_Advanced_Halberd_C8gQn7onAc9wsrCs.json +++ b/src/packs/items/weapons/weapon_Advanced_Halberd_C8gQn7onAc9wsrCs.json @@ -116,7 +116,7 @@ "changes": [ { "key": "system.traits.finesse.value", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/weapons/weapon_Advanced_Heavy_Frame_Wheelchair_eT2Qwb0RdrLX2hH1.json b/src/packs/items/weapons/weapon_Advanced_Heavy_Frame_Wheelchair_eT2Qwb0RdrLX2hH1.json index 7f5bb9c7..1e9a6441 100644 --- a/src/packs/items/weapons/weapon_Advanced_Heavy_Frame_Wheelchair_eT2Qwb0RdrLX2hH1.json +++ b/src/packs/items/weapons/weapon_Advanced_Heavy_Frame_Wheelchair_eT2Qwb0RdrLX2hH1.json @@ -115,7 +115,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/weapons/weapon_Advanced_Longbow_M5CywMAyPKGgebsJ.json b/src/packs/items/weapons/weapon_Advanced_Longbow_M5CywMAyPKGgebsJ.json index 14327a8c..4bf1375d 100644 --- a/src/packs/items/weapons/weapon_Advanced_Longbow_M5CywMAyPKGgebsJ.json +++ b/src/packs/items/weapons/weapon_Advanced_Longbow_M5CywMAyPKGgebsJ.json @@ -116,7 +116,7 @@ "changes": [ { "key": "system.traits.finesse.value", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/weapons/weapon_Advanced_Shortsword_p3nz5CaGUoyuGVg0.json b/src/packs/items/weapons/weapon_Advanced_Shortsword_p3nz5CaGUoyuGVg0.json index 64337b2b..2a24d0cc 100644 --- a/src/packs/items/weapons/weapon_Advanced_Shortsword_p3nz5CaGUoyuGVg0.json +++ b/src/packs/items/weapons/weapon_Advanced_Shortsword_p3nz5CaGUoyuGVg0.json @@ -123,7 +123,7 @@ "changes": [ { "key": "system.bonuses.damage.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "ITEM.@system.tier + 1" } ] diff --git a/src/packs/items/weapons/weapon_Advanced_Small_Dagger_0thN0BpN05KT8Avx.json b/src/packs/items/weapons/weapon_Advanced_Small_Dagger_0thN0BpN05KT8Avx.json index 55bcc11f..2e313ace 100644 --- a/src/packs/items/weapons/weapon_Advanced_Small_Dagger_0thN0BpN05KT8Avx.json +++ b/src/packs/items/weapons/weapon_Advanced_Small_Dagger_0thN0BpN05KT8Avx.json @@ -123,7 +123,7 @@ "changes": [ { "key": "system.bonuses.damage.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "ITEM.@system.tier + 1" } ] diff --git a/src/packs/items/weapons/weapon_Advanced_Warhammer_8Lipw3RRKDgBVP0p.json b/src/packs/items/weapons/weapon_Advanced_Warhammer_8Lipw3RRKDgBVP0p.json index bb142281..8770ef57 100644 --- a/src/packs/items/weapons/weapon_Advanced_Warhammer_8Lipw3RRKDgBVP0p.json +++ b/src/packs/items/weapons/weapon_Advanced_Warhammer_8Lipw3RRKDgBVP0p.json @@ -116,7 +116,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/weapons/weapon_Arcane_Frame_Wheelchair_XRChepscgr75Uug7.json b/src/packs/items/weapons/weapon_Arcane_Frame_Wheelchair_XRChepscgr75Uug7.json index 4ee53b92..cdb26ca6 100644 --- a/src/packs/items/weapons/weapon_Arcane_Frame_Wheelchair_XRChepscgr75Uug7.json +++ b/src/packs/items/weapons/weapon_Arcane_Frame_Wheelchair_XRChepscgr75Uug7.json @@ -119,7 +119,7 @@ "changes": [ { "key": "system.bonuses.roll.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "1" } ] diff --git a/src/packs/items/weapons/weapon_Bravesword_QZrWAkprA2tL2MOI.json b/src/packs/items/weapons/weapon_Bravesword_QZrWAkprA2tL2MOI.json index 412a7083..281ec542 100644 --- a/src/packs/items/weapons/weapon_Bravesword_QZrWAkprA2tL2MOI.json +++ b/src/packs/items/weapons/weapon_Bravesword_QZrWAkprA2tL2MOI.json @@ -116,12 +116,12 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "-1" }, { "key": "system.damageThresholds.severe", - "mode": 2, + "type": "add", "value": "ITEM.@system.tier" } ], diff --git a/src/packs/items/weapons/weapon_Broadsword_1cwWNt4sqlgA8gCT.json b/src/packs/items/weapons/weapon_Broadsword_1cwWNt4sqlgA8gCT.json index 3e7662da..1bed724d 100644 --- a/src/packs/items/weapons/weapon_Broadsword_1cwWNt4sqlgA8gCT.json +++ b/src/packs/items/weapons/weapon_Broadsword_1cwWNt4sqlgA8gCT.json @@ -119,7 +119,7 @@ "changes": [ { "key": "system.bonuses.roll.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "1" } ] diff --git a/src/packs/items/weapons/weapon_Buckler_EmFTp9wzT6MHSaNz.json b/src/packs/items/weapons/weapon_Buckler_EmFTp9wzT6MHSaNz.json index be147888..911dd4c3 100644 --- a/src/packs/items/weapons/weapon_Buckler_EmFTp9wzT6MHSaNz.json +++ b/src/packs/items/weapons/weapon_Buckler_EmFTp9wzT6MHSaNz.json @@ -154,7 +154,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "@system.armorScore", "priority": 21 } diff --git a/src/packs/items/weapons/weapon_Curved_Dagger_Fk69R40svV0kanZD.json b/src/packs/items/weapons/weapon_Curved_Dagger_Fk69R40svV0kanZD.json index 1af2c372..ddaa12d0 100644 --- a/src/packs/items/weapons/weapon_Curved_Dagger_Fk69R40svV0kanZD.json +++ b/src/packs/items/weapons/weapon_Curved_Dagger_Fk69R40svV0kanZD.json @@ -116,7 +116,7 @@ "changes": [ { "key": "system.rules.damage.flipMinDiceValue", - "mode": 5, + "type": "override", "value": "1" } ], diff --git a/src/packs/items/weapons/weapon_Finehair_Bow_ykF3jouxHZ6YR8Bg.json b/src/packs/items/weapons/weapon_Finehair_Bow_ykF3jouxHZ6YR8Bg.json index d1bd58b5..4523ad41 100644 --- a/src/packs/items/weapons/weapon_Finehair_Bow_ykF3jouxHZ6YR8Bg.json +++ b/src/packs/items/weapons/weapon_Finehair_Bow_ykF3jouxHZ6YR8Bg.json @@ -116,7 +116,7 @@ "changes": [ { "key": "system.bonuses.roll.primaryWeapon.attack", - "mode": 2, + "type": "add", "value": "1" } ], diff --git a/src/packs/items/weapons/weapon_Flickerfly_Blade_xLJ5RRpUoTRmAC3G.json b/src/packs/items/weapons/weapon_Flickerfly_Blade_xLJ5RRpUoTRmAC3G.json index acf0cfd6..ffd6531f 100644 --- a/src/packs/items/weapons/weapon_Flickerfly_Blade_xLJ5RRpUoTRmAC3G.json +++ b/src/packs/items/weapons/weapon_Flickerfly_Blade_xLJ5RRpUoTRmAC3G.json @@ -116,7 +116,7 @@ "changes": [ { "key": "system.bonuses.damage.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "@system.traits.agility.value", "priority": 21 } diff --git a/src/packs/items/weapons/weapon_Fusion_Gloves_uK1RhtYAsDeoPNGx.json b/src/packs/items/weapons/weapon_Fusion_Gloves_uK1RhtYAsDeoPNGx.json index 034ad5cf..e2e61ec7 100644 --- a/src/packs/items/weapons/weapon_Fusion_Gloves_uK1RhtYAsDeoPNGx.json +++ b/src/packs/items/weapons/weapon_Fusion_Gloves_uK1RhtYAsDeoPNGx.json @@ -116,7 +116,7 @@ "changes": [ { "key": "system.bonuses.damage.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "@system.levelData.level.current" } ], diff --git a/src/packs/items/weapons/weapon_Gilded_Bow_ctTgFfMbM3YtmsYU.json b/src/packs/items/weapons/weapon_Gilded_Bow_ctTgFfMbM3YtmsYU.json index 0147cfdb..0f5b5c71 100644 --- a/src/packs/items/weapons/weapon_Gilded_Bow_ctTgFfMbM3YtmsYU.json +++ b/src/packs/items/weapons/weapon_Gilded_Bow_ctTgFfMbM3YtmsYU.json @@ -116,7 +116,7 @@ "changes": [ { "key": "system.rules.damage.flipMinDiceValue", - "mode": 5, + "type": "override", "value": "1" } ], diff --git a/src/packs/items/weapons/weapon_Greatsword_70ysaFJDREwTgvZa.json b/src/packs/items/weapons/weapon_Greatsword_70ysaFJDREwTgvZa.json index f0e450a4..74aa3cf3 100644 --- a/src/packs/items/weapons/weapon_Greatsword_70ysaFJDREwTgvZa.json +++ b/src/packs/items/weapons/weapon_Greatsword_70ysaFJDREwTgvZa.json @@ -116,7 +116,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/weapons/weapon_Halberd_qT7FfmauAumOjJoq.json b/src/packs/items/weapons/weapon_Halberd_qT7FfmauAumOjJoq.json index 5a990da3..8d58b29b 100644 --- a/src/packs/items/weapons/weapon_Halberd_qT7FfmauAumOjJoq.json +++ b/src/packs/items/weapons/weapon_Halberd_qT7FfmauAumOjJoq.json @@ -116,7 +116,7 @@ "changes": [ { "key": "system.traits.finesse.value", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/weapons/weapon_Heavy_Frame_Wheelchair_XjPQjhRCH08VUIbr.json b/src/packs/items/weapons/weapon_Heavy_Frame_Wheelchair_XjPQjhRCH08VUIbr.json index e74ff4aa..a8187c25 100644 --- a/src/packs/items/weapons/weapon_Heavy_Frame_Wheelchair_XjPQjhRCH08VUIbr.json +++ b/src/packs/items/weapons/weapon_Heavy_Frame_Wheelchair_XjPQjhRCH08VUIbr.json @@ -116,7 +116,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/weapons/weapon_Improved_Arcane_Frame_Wheelchair_N9P695V5KKlJbAY5.json b/src/packs/items/weapons/weapon_Improved_Arcane_Frame_Wheelchair_N9P695V5KKlJbAY5.json index 71f03625..f7d1a9d3 100644 --- a/src/packs/items/weapons/weapon_Improved_Arcane_Frame_Wheelchair_N9P695V5KKlJbAY5.json +++ b/src/packs/items/weapons/weapon_Improved_Arcane_Frame_Wheelchair_N9P695V5KKlJbAY5.json @@ -118,7 +118,7 @@ "changes": [ { "key": "system.bonuses.roll.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "1" } ] diff --git a/src/packs/items/weapons/weapon_Improved_Broadsword_OcKeLJxvmdT81VBc.json b/src/packs/items/weapons/weapon_Improved_Broadsword_OcKeLJxvmdT81VBc.json index c0b2d460..cd7f62d4 100644 --- a/src/packs/items/weapons/weapon_Improved_Broadsword_OcKeLJxvmdT81VBc.json +++ b/src/packs/items/weapons/weapon_Improved_Broadsword_OcKeLJxvmdT81VBc.json @@ -119,7 +119,7 @@ "changes": [ { "key": "system.bonuses.roll.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "1" } ] diff --git a/src/packs/items/weapons/weapon_Improved_Greatsword_FPX4ouDrxXiQ5MDf.json b/src/packs/items/weapons/weapon_Improved_Greatsword_FPX4ouDrxXiQ5MDf.json index 60e5dd53..ec618f0b 100644 --- a/src/packs/items/weapons/weapon_Improved_Greatsword_FPX4ouDrxXiQ5MDf.json +++ b/src/packs/items/weapons/weapon_Improved_Greatsword_FPX4ouDrxXiQ5MDf.json @@ -116,7 +116,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/weapons/weapon_Improved_Halberd_F9PETfCQGwczBPif.json b/src/packs/items/weapons/weapon_Improved_Halberd_F9PETfCQGwczBPif.json index fb5a1dcc..79c3a31b 100644 --- a/src/packs/items/weapons/weapon_Improved_Halberd_F9PETfCQGwczBPif.json +++ b/src/packs/items/weapons/weapon_Improved_Halberd_F9PETfCQGwczBPif.json @@ -116,7 +116,7 @@ "changes": [ { "key": "system.traits.finesse.value", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/weapons/weapon_Improved_Heavy_Frame_Wheelchair_L5KeCtrs768PmYWW.json b/src/packs/items/weapons/weapon_Improved_Heavy_Frame_Wheelchair_L5KeCtrs768PmYWW.json index e65cc221..1443e76f 100644 --- a/src/packs/items/weapons/weapon_Improved_Heavy_Frame_Wheelchair_L5KeCtrs768PmYWW.json +++ b/src/packs/items/weapons/weapon_Improved_Heavy_Frame_Wheelchair_L5KeCtrs768PmYWW.json @@ -115,7 +115,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/weapons/weapon_Improved_Longbow_NacNonjbzyoVMNhI.json b/src/packs/items/weapons/weapon_Improved_Longbow_NacNonjbzyoVMNhI.json index c197f726..cc083a33 100644 --- a/src/packs/items/weapons/weapon_Improved_Longbow_NacNonjbzyoVMNhI.json +++ b/src/packs/items/weapons/weapon_Improved_Longbow_NacNonjbzyoVMNhI.json @@ -116,7 +116,7 @@ "changes": [ { "key": "system.traits.finesse.value", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/weapons/weapon_Improved_Shortsword_rSyBNRwemBVuTo3H.json b/src/packs/items/weapons/weapon_Improved_Shortsword_rSyBNRwemBVuTo3H.json index 070e2374..498b5dfb 100644 --- a/src/packs/items/weapons/weapon_Improved_Shortsword_rSyBNRwemBVuTo3H.json +++ b/src/packs/items/weapons/weapon_Improved_Shortsword_rSyBNRwemBVuTo3H.json @@ -123,7 +123,7 @@ "changes": [ { "key": "system.bonuses.damage.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "ITEM.@system.tier + 1" } ] diff --git a/src/packs/items/weapons/weapon_Improved_Small_Dagger_nMuF8ZDZ2aXZVTg6.json b/src/packs/items/weapons/weapon_Improved_Small_Dagger_nMuF8ZDZ2aXZVTg6.json index 5b0282f1..01f3fc8e 100644 --- a/src/packs/items/weapons/weapon_Improved_Small_Dagger_nMuF8ZDZ2aXZVTg6.json +++ b/src/packs/items/weapons/weapon_Improved_Small_Dagger_nMuF8ZDZ2aXZVTg6.json @@ -123,7 +123,7 @@ "changes": [ { "key": "system.bonuses.damage.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "ITEM.@system.tier + 1" } ] diff --git a/src/packs/items/weapons/weapon_Improved_Warhammer_pxaN4ZK4eqKrjtWj.json b/src/packs/items/weapons/weapon_Improved_Warhammer_pxaN4ZK4eqKrjtWj.json index aa24c4ad..47b553a6 100644 --- a/src/packs/items/weapons/weapon_Improved_Warhammer_pxaN4ZK4eqKrjtWj.json +++ b/src/packs/items/weapons/weapon_Improved_Warhammer_pxaN4ZK4eqKrjtWj.json @@ -116,7 +116,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/weapons/weapon_Keeper_s_Staff_q382JqMkqLaaFLIr.json b/src/packs/items/weapons/weapon_Keeper_s_Staff_q382JqMkqLaaFLIr.json index f511af8b..e72607cb 100644 --- a/src/packs/items/weapons/weapon_Keeper_s_Staff_q382JqMkqLaaFLIr.json +++ b/src/packs/items/weapons/weapon_Keeper_s_Staff_q382JqMkqLaaFLIr.json @@ -119,7 +119,7 @@ "changes": [ { "key": "system.bonuses.roll.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "1" } ] diff --git a/src/packs/items/weapons/weapon_Legendary_Arcane_Frame_Wheelchair_gA2tiET9VHGhwMoO.json b/src/packs/items/weapons/weapon_Legendary_Arcane_Frame_Wheelchair_gA2tiET9VHGhwMoO.json index 6d022c8a..935584a8 100644 --- a/src/packs/items/weapons/weapon_Legendary_Arcane_Frame_Wheelchair_gA2tiET9VHGhwMoO.json +++ b/src/packs/items/weapons/weapon_Legendary_Arcane_Frame_Wheelchair_gA2tiET9VHGhwMoO.json @@ -118,7 +118,7 @@ "changes": [ { "key": "system.bonuses.roll.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "1" } ] diff --git a/src/packs/items/weapons/weapon_Legendary_Broadsword_y3hfTPfZhMognyaJ.json b/src/packs/items/weapons/weapon_Legendary_Broadsword_y3hfTPfZhMognyaJ.json index 3a09f8e4..4570fa8b 100644 --- a/src/packs/items/weapons/weapon_Legendary_Broadsword_y3hfTPfZhMognyaJ.json +++ b/src/packs/items/weapons/weapon_Legendary_Broadsword_y3hfTPfZhMognyaJ.json @@ -119,7 +119,7 @@ "changes": [ { "key": "system.bonuses.roll.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "1" } ] diff --git a/src/packs/items/weapons/weapon_Legendary_Greatsword_zMZ46F9VR7zdTxb9.json b/src/packs/items/weapons/weapon_Legendary_Greatsword_zMZ46F9VR7zdTxb9.json index aa9d2ef0..97464c6b 100644 --- a/src/packs/items/weapons/weapon_Legendary_Greatsword_zMZ46F9VR7zdTxb9.json +++ b/src/packs/items/weapons/weapon_Legendary_Greatsword_zMZ46F9VR7zdTxb9.json @@ -116,7 +116,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/weapons/weapon_Legendary_Halberd_1AuMNiJz96Ez9fur.json b/src/packs/items/weapons/weapon_Legendary_Halberd_1AuMNiJz96Ez9fur.json index af6b2a07..2b411f11 100644 --- a/src/packs/items/weapons/weapon_Legendary_Halberd_1AuMNiJz96Ez9fur.json +++ b/src/packs/items/weapons/weapon_Legendary_Halberd_1AuMNiJz96Ez9fur.json @@ -116,7 +116,7 @@ "changes": [ { "key": "system.traits.finesse.value", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/weapons/weapon_Legendary_Heavy_Frame_Wheelchair_S6nB0CNlzdU05o5U.json b/src/packs/items/weapons/weapon_Legendary_Heavy_Frame_Wheelchair_S6nB0CNlzdU05o5U.json index d8816fc9..9139a7b0 100644 --- a/src/packs/items/weapons/weapon_Legendary_Heavy_Frame_Wheelchair_S6nB0CNlzdU05o5U.json +++ b/src/packs/items/weapons/weapon_Legendary_Heavy_Frame_Wheelchair_S6nB0CNlzdU05o5U.json @@ -115,7 +115,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/weapons/weapon_Legendary_Longbow_Utt1GpoH1fhaTOtN.json b/src/packs/items/weapons/weapon_Legendary_Longbow_Utt1GpoH1fhaTOtN.json index bd5ab13b..e9e95967 100644 --- a/src/packs/items/weapons/weapon_Legendary_Longbow_Utt1GpoH1fhaTOtN.json +++ b/src/packs/items/weapons/weapon_Legendary_Longbow_Utt1GpoH1fhaTOtN.json @@ -116,7 +116,7 @@ "changes": [ { "key": "system.traits.finesse.value", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/weapons/weapon_Legendary_Shortsword_dEumq3BIZBk5xYTk.json b/src/packs/items/weapons/weapon_Legendary_Shortsword_dEumq3BIZBk5xYTk.json index cfc648de..c01a6d5e 100644 --- a/src/packs/items/weapons/weapon_Legendary_Shortsword_dEumq3BIZBk5xYTk.json +++ b/src/packs/items/weapons/weapon_Legendary_Shortsword_dEumq3BIZBk5xYTk.json @@ -123,7 +123,7 @@ "changes": [ { "key": "system.bonuses.damage.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "ITEM.@system.tier + 1" } ] diff --git a/src/packs/items/weapons/weapon_Legendary_Small_Dagger_Px3Rh3kIvAqyISxJ.json b/src/packs/items/weapons/weapon_Legendary_Small_Dagger_Px3Rh3kIvAqyISxJ.json index 33859bf4..42a977f6 100644 --- a/src/packs/items/weapons/weapon_Legendary_Small_Dagger_Px3Rh3kIvAqyISxJ.json +++ b/src/packs/items/weapons/weapon_Legendary_Small_Dagger_Px3Rh3kIvAqyISxJ.json @@ -123,7 +123,7 @@ "changes": [ { "key": "system.bonuses.damage.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "ITEM.@system.tier + 1" } ] diff --git a/src/packs/items/weapons/weapon_Legendary_Warhammer_W9ymfEDck2icfvla.json b/src/packs/items/weapons/weapon_Legendary_Warhammer_W9ymfEDck2icfvla.json index 562122d2..91d26e71 100644 --- a/src/packs/items/weapons/weapon_Legendary_Warhammer_W9ymfEDck2icfvla.json +++ b/src/packs/items/weapons/weapon_Legendary_Warhammer_W9ymfEDck2icfvla.json @@ -116,7 +116,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/weapons/weapon_Longbow_YfVs6Se903az4Yet.json b/src/packs/items/weapons/weapon_Longbow_YfVs6Se903az4Yet.json index 43af46ab..f2995cb5 100644 --- a/src/packs/items/weapons/weapon_Longbow_YfVs6Se903az4Yet.json +++ b/src/packs/items/weapons/weapon_Longbow_YfVs6Se903az4Yet.json @@ -116,7 +116,7 @@ "changes": [ { "key": "system.traits.finesse.value", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/weapons/weapon_Midas_Scythe_BdLfy5i488VZgkjP.json b/src/packs/items/weapons/weapon_Midas_Scythe_BdLfy5i488VZgkjP.json index 57fe2367..143d4910 100644 --- a/src/packs/items/weapons/weapon_Midas_Scythe_BdLfy5i488VZgkjP.json +++ b/src/packs/items/weapons/weapon_Midas_Scythe_BdLfy5i488VZgkjP.json @@ -145,7 +145,7 @@ "changes": [ { "key": "system.proficiency", - "mode": 2, + "type": "add", "value": "1" } ], diff --git a/src/packs/items/weapons/weapon_Powered_Gauntlet_bW3xw5S9DbaLCN3E.json b/src/packs/items/weapons/weapon_Powered_Gauntlet_bW3xw5S9DbaLCN3E.json index 3c4ebd1c..9ba6f1b1 100644 --- a/src/packs/items/weapons/weapon_Powered_Gauntlet_bW3xw5S9DbaLCN3E.json +++ b/src/packs/items/weapons/weapon_Powered_Gauntlet_bW3xw5S9DbaLCN3E.json @@ -152,7 +152,7 @@ "changes": [ { "key": "system.proficiency", - "mode": 2, + "type": "add", "value": "1" } ], diff --git a/src/packs/items/weapons/weapon_Shortsword_cjGZpXCoshEqi1FI.json b/src/packs/items/weapons/weapon_Shortsword_cjGZpXCoshEqi1FI.json index 61f91b58..e8d49d15 100644 --- a/src/packs/items/weapons/weapon_Shortsword_cjGZpXCoshEqi1FI.json +++ b/src/packs/items/weapons/weapon_Shortsword_cjGZpXCoshEqi1FI.json @@ -123,7 +123,7 @@ "changes": [ { "key": "system.bonuses.damage.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "ITEM.@system.tier + 1", "priority": null } diff --git a/src/packs/items/weapons/weapon_Sledge_Axe_OxsEmffWriiQmqJK.json b/src/packs/items/weapons/weapon_Sledge_Axe_OxsEmffWriiQmqJK.json index d0230362..3e43c85a 100644 --- a/src/packs/items/weapons/weapon_Sledge_Axe_OxsEmffWriiQmqJK.json +++ b/src/packs/items/weapons/weapon_Sledge_Axe_OxsEmffWriiQmqJK.json @@ -171,7 +171,7 @@ "changes": [ { "key": "system.traits.agility.value", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/items/weapons/weapon_Small_Dagger_wKklDxs5nkzILNp4.json b/src/packs/items/weapons/weapon_Small_Dagger_wKklDxs5nkzILNp4.json index ddaa312f..184b1ff0 100644 --- a/src/packs/items/weapons/weapon_Small_Dagger_wKklDxs5nkzILNp4.json +++ b/src/packs/items/weapons/weapon_Small_Dagger_wKklDxs5nkzILNp4.json @@ -123,7 +123,7 @@ "changes": [ { "key": "system.bonuses.damage.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "ITEM.@system.tier + 1" } ] diff --git a/src/packs/items/weapons/weapon_Thistlebow_I1nDGpulg29GpWOW.json b/src/packs/items/weapons/weapon_Thistlebow_I1nDGpulg29GpWOW.json index 3821342f..4cd9ad55 100644 --- a/src/packs/items/weapons/weapon_Thistlebow_I1nDGpulg29GpWOW.json +++ b/src/packs/items/weapons/weapon_Thistlebow_I1nDGpulg29GpWOW.json @@ -119,7 +119,7 @@ "changes": [ { "key": "system.bonuses.roll.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "1" } ] diff --git a/src/packs/items/weapons/weapon_Wand_of_Enthrallment_tP6vmnrmTq2h5sj7.json b/src/packs/items/weapons/weapon_Wand_of_Enthrallment_tP6vmnrmTq2h5sj7.json index 84c7b3f2..ea13be12 100644 --- a/src/packs/items/weapons/weapon_Wand_of_Enthrallment_tP6vmnrmTq2h5sj7.json +++ b/src/packs/items/weapons/weapon_Wand_of_Enthrallment_tP6vmnrmTq2h5sj7.json @@ -152,7 +152,7 @@ "changes": [ { "key": "system.traits.presence.value", - "mode": 2, + "type": "add", "value": "2" } ], diff --git a/src/packs/items/weapons/weapon_War_Scythe_z6yEdFYQJ5IzgTX3.json b/src/packs/items/weapons/weapon_War_Scythe_z6yEdFYQJ5IzgTX3.json index df9186ac..fc893901 100644 --- a/src/packs/items/weapons/weapon_War_Scythe_z6yEdFYQJ5IzgTX3.json +++ b/src/packs/items/weapons/weapon_War_Scythe_z6yEdFYQJ5IzgTX3.json @@ -119,7 +119,7 @@ "changes": [ { "key": "system.bonuses.roll.primaryWeapon.bonus", - "mode": 2, + "type": "add", "value": "1" } ] diff --git a/src/packs/items/weapons/weapon_Warhammer_ZXh1GQahBiODfSTC.json b/src/packs/items/weapons/weapon_Warhammer_ZXh1GQahBiODfSTC.json index a94950c7..17724919 100644 --- a/src/packs/items/weapons/weapon_Warhammer_ZXh1GQahBiODfSTC.json +++ b/src/packs/items/weapons/weapon_Warhammer_ZXh1GQahBiODfSTC.json @@ -116,7 +116,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "-1" } ], diff --git a/src/packs/subclasses/feature_Adrenaline_uByM34yQlw38yf1V.json b/src/packs/subclasses/feature_Adrenaline_uByM34yQlw38yf1V.json index 764dd92a..95cb2263 100644 --- a/src/packs/subclasses/feature_Adrenaline_uByM34yQlw38yf1V.json +++ b/src/packs/subclasses/feature_Adrenaline_uByM34yQlw38yf1V.json @@ -28,13 +28,13 @@ "changes": [ { "key": "system.bonuses.damage.physical.bonus", - "mode": 2, + "type": "add", "value": "@system.levelData.level.current", "priority": null }, { "key": "system.bonuses.damage.magical.bonus", - "mode": 2, + "type": "add", "value": "@system.levelData.level.current", "priority": null } diff --git a/src/packs/subclasses/feature_Advanced_Training_uGcs785h94RMtueH.json b/src/packs/subclasses/feature_Advanced_Training_uGcs785h94RMtueH.json index f15d1efa..b551ef50 100644 --- a/src/packs/subclasses/feature_Advanced_Training_uGcs785h94RMtueH.json +++ b/src/packs/subclasses/feature_Advanced_Training_uGcs785h94RMtueH.json @@ -24,7 +24,7 @@ "changes": [ { "key": "system.companionData.levelupChoices", - "mode": 2, + "type": "add", "value": "2", "priority": null } diff --git a/src/packs/subclasses/feature_Arcane_Charge_yA4MKQ1tbKFiJoDB.json b/src/packs/subclasses/feature_Arcane_Charge_yA4MKQ1tbKFiJoDB.json index c884bc6f..e563668d 100644 --- a/src/packs/subclasses/feature_Arcane_Charge_yA4MKQ1tbKFiJoDB.json +++ b/src/packs/subclasses/feature_Arcane_Charge_yA4MKQ1tbKFiJoDB.json @@ -32,7 +32,7 @@ "changes": [ { "key": "system.bonuses.damage.magical.bonus", - "mode": 2, + "type": "add", "value": "+10", "priority": null } diff --git a/src/packs/subclasses/feature_Ascendant_fefLgx6kcYWusjBb.json b/src/packs/subclasses/feature_Ascendant_fefLgx6kcYWusjBb.json index 845e287d..abdbe271 100644 --- a/src/packs/subclasses/feature_Ascendant_fefLgx6kcYWusjBb.json +++ b/src/packs/subclasses/feature_Ascendant_fefLgx6kcYWusjBb.json @@ -24,7 +24,7 @@ "changes": [ { "key": "system.damageThresholds.severe", - "mode": 2, + "type": "add", "value": "+4", "priority": null } diff --git a/src/packs/subclasses/feature_At_Ease_xPWFvGvtUjIcqgJq.json b/src/packs/subclasses/feature_At_Ease_xPWFvGvtUjIcqgJq.json index abc5fbfc..f63b0e42 100644 --- a/src/packs/subclasses/feature_At_Ease_xPWFvGvtUjIcqgJq.json +++ b/src/packs/subclasses/feature_At_Ease_xPWFvGvtUjIcqgJq.json @@ -24,7 +24,7 @@ "changes": [ { "key": "system.resources.stress.max", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/subclasses/feature_Battlemage_Y9eGMewnFZgPvX0M.json b/src/packs/subclasses/feature_Battlemage_Y9eGMewnFZgPvX0M.json index c9b54d71..36706909 100644 --- a/src/packs/subclasses/feature_Battlemage_Y9eGMewnFZgPvX0M.json +++ b/src/packs/subclasses/feature_Battlemage_Y9eGMewnFZgPvX0M.json @@ -24,7 +24,7 @@ "changes": [ { "key": "system.resources.hitPoints.max", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/subclasses/feature_Conjure_Shield_oirsCnN66GOlK3Fa.json b/src/packs/subclasses/feature_Conjure_Shield_oirsCnN66GOlK3Fa.json index 0455de3d..8f542544 100644 --- a/src/packs/subclasses/feature_Conjure_Shield_oirsCnN66GOlK3Fa.json +++ b/src/packs/subclasses/feature_Conjure_Shield_oirsCnN66GOlK3Fa.json @@ -24,7 +24,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "@system.proficiency", "priority": 21 } diff --git a/src/packs/subclasses/feature_Elemental_Dominion_EFUJHrkTuyv8uA9l.json b/src/packs/subclasses/feature_Elemental_Dominion_EFUJHrkTuyv8uA9l.json index 5035393f..0adda762 100644 --- a/src/packs/subclasses/feature_Elemental_Dominion_EFUJHrkTuyv8uA9l.json +++ b/src/packs/subclasses/feature_Elemental_Dominion_EFUJHrkTuyv8uA9l.json @@ -144,7 +144,7 @@ "changes": [ { "key": "system.proficiency", - "mode": 2, + "type": "add", "value": "1", "priority": null } @@ -223,7 +223,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/subclasses/feature_Elemental_Incarnation_f37TTgCc0Q3Ih1A1.json b/src/packs/subclasses/feature_Elemental_Incarnation_f37TTgCc0Q3Ih1A1.json index e7e282b4..87150aa0 100644 --- a/src/packs/subclasses/feature_Elemental_Incarnation_f37TTgCc0Q3Ih1A1.json +++ b/src/packs/subclasses/feature_Elemental_Incarnation_f37TTgCc0Q3Ih1A1.json @@ -246,13 +246,13 @@ "changes": [ { "key": "system.damageThresholds.major", - "mode": 2, + "type": "add", "value": "@system.proficiency", "priority": 21 }, { "key": "system.damageThresholds.severe", - "mode": 2, + "type": "add", "value": "@system.proficiency", "priority": 21 } @@ -289,7 +289,7 @@ "changes": [ { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "Advantage on Agility Rolls", "priority": null } diff --git a/src/packs/subclasses/feature_Elementalist_dPcqKN5NeDkjB1HW.json b/src/packs/subclasses/feature_Elementalist_dPcqKN5NeDkjB1HW.json index 8ab51263..dd989364 100644 --- a/src/packs/subclasses/feature_Elementalist_dPcqKN5NeDkjB1HW.json +++ b/src/packs/subclasses/feature_Elementalist_dPcqKN5NeDkjB1HW.json @@ -101,7 +101,7 @@ "changes": [ { "key": "system.bonuses.roll.action.bonus", - "mode": 2, + "type": "add", "value": "+2", "priority": null } @@ -138,13 +138,13 @@ "changes": [ { "key": "system.bonuses.damage.magical.bonus", - "mode": 2, + "type": "add", "value": "+3", "priority": null }, { "key": "system.bonuses.damage.physical.bonus", - "mode": 2, + "type": "add", "value": "+3", "priority": null } diff --git a/src/packs/subclasses/feature_Epic_Poetry_eCoEWkWuZPMZ9C6a.json b/src/packs/subclasses/feature_Epic_Poetry_eCoEWkWuZPMZ9C6a.json index c5814cdc..2367f220 100644 --- a/src/packs/subclasses/feature_Epic_Poetry_eCoEWkWuZPMZ9C6a.json +++ b/src/packs/subclasses/feature_Epic_Poetry_eCoEWkWuZPMZ9C6a.json @@ -23,7 +23,7 @@ "changes": [ { "key": "system.bonuses.rally", - "mode": 5, + "type": "override", "value": "d10", "priority": null } diff --git a/src/packs/subclasses/feature_Ethereal_Visage_tyGB6wRKjYdIBK1i.json b/src/packs/subclasses/feature_Ethereal_Visage_tyGB6wRKjYdIBK1i.json index 59164f27..15871e9f 100644 --- a/src/packs/subclasses/feature_Ethereal_Visage_tyGB6wRKjYdIBK1i.json +++ b/src/packs/subclasses/feature_Ethereal_Visage_tyGB6wRKjYdIBK1i.json @@ -24,7 +24,7 @@ "changes": [ { "key": "system.advantageSources", - "mode": 2, + "type": "add", "value": "Presence rolls while flying", "priority": null } diff --git a/src/packs/subclasses/feature_Expert_Training_iCXtOWBKv1FdKdWz.json b/src/packs/subclasses/feature_Expert_Training_iCXtOWBKv1FdKdWz.json index e356c3a9..0ae1a60d 100644 --- a/src/packs/subclasses/feature_Expert_Training_iCXtOWBKv1FdKdWz.json +++ b/src/packs/subclasses/feature_Expert_Training_iCXtOWBKv1FdKdWz.json @@ -24,7 +24,7 @@ "changes": [ { "key": "system.companionData.levelupChoices", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/subclasses/feature_Fleeting_Shadow_EY7Eo6hNGppVL3dR.json b/src/packs/subclasses/feature_Fleeting_Shadow_EY7Eo6hNGppVL3dR.json index 2b338bb3..fe3f252f 100644 --- a/src/packs/subclasses/feature_Fleeting_Shadow_EY7Eo6hNGppVL3dR.json +++ b/src/packs/subclasses/feature_Fleeting_Shadow_EY7Eo6hNGppVL3dR.json @@ -24,7 +24,7 @@ "changes": [ { "key": "system.evasion", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/subclasses/feature_Iron_Will_7AVRNyBcd1Nffjtn.json b/src/packs/subclasses/feature_Iron_Will_7AVRNyBcd1Nffjtn.json index e45f91e0..fa8516ca 100644 --- a/src/packs/subclasses/feature_Iron_Will_7AVRNyBcd1Nffjtn.json +++ b/src/packs/subclasses/feature_Iron_Will_7AVRNyBcd1Nffjtn.json @@ -24,7 +24,7 @@ "changes": [ { "key": "system.rules.damageReduction.maxArmorMarked.value", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/subclasses/feature_Ruthless_Predator_Qny2J3R35bvC0Cey.json b/src/packs/subclasses/feature_Ruthless_Predator_Qny2J3R35bvC0Cey.json index 64bde511..ba92472f 100644 --- a/src/packs/subclasses/feature_Ruthless_Predator_Qny2J3R35bvC0Cey.json +++ b/src/packs/subclasses/feature_Ruthless_Predator_Qny2J3R35bvC0Cey.json @@ -63,7 +63,7 @@ "changes": [ { "key": "system.proficiency", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/subclasses/feature_Transcendence_th6HZwEFnVBjUtqm.json b/src/packs/subclasses/feature_Transcendence_th6HZwEFnVBjUtqm.json index 6d43ed30..32d8181c 100644 --- a/src/packs/subclasses/feature_Transcendence_th6HZwEFnVBjUtqm.json +++ b/src/packs/subclasses/feature_Transcendence_th6HZwEFnVBjUtqm.json @@ -133,7 +133,7 @@ "changes": [ { "key": "system.proficiency", - "mode": 2, + "type": "add", "value": "+1", "priority": null } diff --git a/src/packs/subclasses/feature_Undaunted_866b2jjyzXP8nPRQ.json b/src/packs/subclasses/feature_Undaunted_866b2jjyzXP8nPRQ.json index 79c51474..792dfd3c 100644 --- a/src/packs/subclasses/feature_Undaunted_866b2jjyzXP8nPRQ.json +++ b/src/packs/subclasses/feature_Undaunted_866b2jjyzXP8nPRQ.json @@ -24,13 +24,13 @@ "changes": [ { "key": "system.damageThresholds.severe", - "mode": 2, + "type": "add", "value": "3", "priority": null }, { "key": "system.damageThresholds.major", - "mode": 2, + "type": "add", "value": "3", "priority": null } diff --git a/src/packs/subclasses/feature_Unrelenting_4qP7bNyxVHBmr4Rb.json b/src/packs/subclasses/feature_Unrelenting_4qP7bNyxVHBmr4Rb.json index 0b5e89f6..287a54c1 100644 --- a/src/packs/subclasses/feature_Unrelenting_4qP7bNyxVHBmr4Rb.json +++ b/src/packs/subclasses/feature_Unrelenting_4qP7bNyxVHBmr4Rb.json @@ -24,13 +24,13 @@ "changes": [ { "key": "system.damageThresholds.major", - "mode": 2, + "type": "add", "value": "2", "priority": null }, { "key": "system.damageThresholds.severe", - "mode": 2, + "type": "add", "value": "2", "priority": null } diff --git a/src/packs/subclasses/feature_Unwavering_WBiFZaYNoQNhysmN.json b/src/packs/subclasses/feature_Unwavering_WBiFZaYNoQNhysmN.json index bd7172ea..dc183879 100644 --- a/src/packs/subclasses/feature_Unwavering_WBiFZaYNoQNhysmN.json +++ b/src/packs/subclasses/feature_Unwavering_WBiFZaYNoQNhysmN.json @@ -24,13 +24,13 @@ "changes": [ { "key": "system.damageThresholds.major", - "mode": 2, + "type": "add", "value": "1", "priority": null }, { "key": "system.damageThresholds.severe", - "mode": 2, + "type": "add", "value": "1", "priority": null } diff --git a/src/packs/subclasses/feature_Wings_of_Light_KkQH0tYhagIqe2MT.json b/src/packs/subclasses/feature_Wings_of_Light_KkQH0tYhagIqe2MT.json index 51a203fc..b7d8af3f 100644 --- a/src/packs/subclasses/feature_Wings_of_Light_KkQH0tYhagIqe2MT.json +++ b/src/packs/subclasses/feature_Wings_of_Light_KkQH0tYhagIqe2MT.json @@ -97,13 +97,13 @@ "changes": [ { "key": "system.bonuses.damage.physical.dice", - "mode": 2, + "type": "add", "value": "+1d8", "priority": null }, { "key": "system.bonuses.damage.magical.dice", - "mode": 2, + "type": "add", "value": "+1d8", "priority": null } From c6411ef0fea48994eab7ef20e209672c122b461b Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sat, 11 Jul 2026 22:51:44 +0200 Subject: [PATCH 08/30] Cleaned up uses of 'mode' for activeEffects in configs (#2076) --- .../applications/dialogs/beastformDialog.mjs | 6 +- module/applications/dialogs/deathMove.mjs | 16 +- module/config/encounterConfig.mjs | 26 +- module/config/itemConfig.mjs | 527 ++++++++++-------- 4 files changed, 315 insertions(+), 260 deletions(-) diff --git a/module/applications/dialogs/beastformDialog.mjs b/module/applications/dialogs/beastformDialog.mjs index 8ae6d5fe..8aca1844 100644 --- a/module/applications/dialogs/beastformDialog.mjs +++ b/module/applications/dialogs/beastformDialog.mjs @@ -315,15 +315,15 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat const beastformEffect = selected.effects.find(x => x.type === 'beastform'); for (const traitBonus of app.modifications.traitBonuses) { - const existingChange = beastformEffect.changes.find( + const existingChange = beastformEffect.system.changes.find( x => x.key === `system.traits.${traitBonus.trait}.value` ); if (existingChange) { existingChange.value = Number.parseInt(existingChange.value) + traitBonus.bonus; } else { - beastformEffect.changes.push({ + beastformEffect.system.changes.push({ key: `system.traits.${traitBonus.trait}.value`, - mode: 2, + type: 'add', priority: null, value: traitBonus.bonus }); diff --git a/module/applications/dialogs/deathMove.mjs b/module/applications/dialogs/deathMove.mjs index 4a949b99..8e0ed6af 100644 --- a/module/applications/dialogs/deathMove.mjs +++ b/module/applications/dialogs/deathMove.mjs @@ -139,13 +139,15 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV name: game.i18n.localize('DAGGERHEART.CONFIG.DeathMoves.blazeOfGlory.name'), description: game.i18n.localize('DAGGERHEART.CONFIG.DeathMoves.blazeOfGlory.description'), img: CONFIG.DH.GENERAL.deathMoves.blazeOfGlory.img, - changes: [ - { - key: 'system.rules.roll.guaranteedCritical', - mode: 2, - value: 'true' - } - ] + system: { + changes: [ + { + key: 'system.rules.roll.guaranteedCritical', + type: 'add', + value: 'true' + } + ] + } } ]); diff --git a/module/config/encounterConfig.mjs b/module/config/encounterConfig.mjs index 4e0f8a6e..6ea03fcf 100644 --- a/module/config/encounterConfig.mjs +++ b/module/config/encounterConfig.mjs @@ -90,18 +90,20 @@ export const BPModifiers = { name: 'DAGGERHEART.CONFIG.BPModifiers.increaseDamage.effect.name', description: 'DAGGERHEART.CONFIG.BPModifiers.increaseDamage.effect.description', img: 'icons/magic/control/buff-flight-wings-red.webp', - changes: [ - { - key: 'system.bonuses.damage.physical.dice', - mode: 2, - value: '1d4' - }, - { - key: 'system.bonuses.damage.magical.dice', - mode: 2, - value: '1d4' - } - ] + system: { + changes: [ + { + key: 'system.bonuses.damage.physical.dice', + type: 'add', + value: '1d4' + }, + { + key: 'system.bonuses.damage.magical.dice', + type: 'add', + value: '1d4' + } + ] + } } ] } diff --git a/module/config/itemConfig.mjs b/module/config/itemConfig.mjs index 9ebfd1e2..61ed3703 100644 --- a/module/config/itemConfig.mjs +++ b/module/config/itemConfig.mjs @@ -37,13 +37,15 @@ export const armorFeatures = { name: 'DAGGERHEART.CONFIG.ArmorFeature.channeling.effects.channeling.name', description: 'DAGGERHEART.CONFIG.ArmorFeature.channeling.effects.channeling.description', img: 'icons/magic/symbols/rune-sigil-horned-blue.webp', - changes: [ - { - key: 'system.bonuses.roll.spellcast', - mode: 2, - value: '1' - } - ] + system: { + changes: [ + { + key: 'system.bonuses.roll.spellcast', + type: 'add', + value: '1' + } + ] + } } ] }, @@ -55,43 +57,45 @@ export const armorFeatures = { name: 'DAGGERHEART.CONFIG.ArmorFeature.difficult.effects.difficult.name', description: 'DAGGERHEART.CONFIG.ArmorFeature.difficult.effects.difficult.description', img: 'icons/magic/control/buff-flight-wings-red.webp', - changes: [ - { - key: 'system.traits.agility.value', - mode: 2, - value: '-1' - }, - { - key: 'system.traits.strength.value', - mode: 2, - value: '-1' - }, - { - key: 'system.traits.finesse.value', - mode: 2, - value: '-1' - }, - { - key: 'system.traits.instinct.value', - mode: 2, - value: '-1' - }, - { - key: 'system.traits.presence.value', - mode: 2, - value: '-1' - }, - { - key: 'system.traits.knowledge.value', - mode: 2, - value: '-1' - }, - { - key: 'system.evasion', - mode: 2, - value: '-1' - } - ] + system: { + changes: [ + { + key: 'system.traits.agility.value', + type: 'add', + value: '-1' + }, + { + key: 'system.traits.strength.value', + type: 'add', + value: '-1' + }, + { + key: 'system.traits.finesse.value', + type: 'add', + value: '-1' + }, + { + key: 'system.traits.instinct.value', + type: 'add', + value: '-1' + }, + { + key: 'system.traits.presence.value', + type: 'add', + value: '-1' + }, + { + key: 'system.traits.knowledge.value', + type: 'add', + value: '-1' + }, + { + key: 'system.evasion', + type: 'add', + value: '-1' + } + ] + } } ] }, @@ -103,13 +107,15 @@ export const armorFeatures = { name: 'DAGGERHEART.CONFIG.ArmorFeature.flexible.effects.flexible.name', description: 'DAGGERHEART.CONFIG.ArmorFeature.flexible.effects.flexible.description', img: 'icons/magic/movement/abstract-ribbons-red-orange.webp', - changes: [ - { - key: 'system.evasion', - mode: 2, - value: '1' - } - ] + system: { + changes: [ + { + key: 'system.evasion', + type: 'add', + value: '1' + } + ] + } } ] }, @@ -121,13 +127,15 @@ export const armorFeatures = { name: 'DAGGERHEART.CONFIG.ArmorFeature.fortified.effects.fortified.name', description: 'DAGGERHEART.CONFIG.ArmorFeature.fortified.effects.fortified.description', img: 'icons/magic/defensive/shield-barrier-glowing-blue.webp', - changes: [ - { - key: 'system.rules.damageReduction.increasePerArmorMark', - mode: 5, - value: '2' - } - ] + system: { + changes: [ + { + key: 'system.rules.damageReduction.increasePerArmorMark', + type: 'override', + value: '2' + } + ] + } } ] }, @@ -139,13 +147,15 @@ export const armorFeatures = { name: 'DAGGERHEART.CONFIG.ArmorFeature.gilded.effects.gilded.name', description: 'DAGGERHEART.CONFIG.ArmorFeature.gilded.effects.gilded.description', img: 'icons/magic/control/control-influence-crown-gold.webp', - changes: [ - { - key: 'system.traits.presence.value', - mode: 2, - value: '1' - } - ] + system: { + changes: [ + { + key: 'system.traits.presence.value', + type: 'add', + value: '1' + } + ] + } } ] }, @@ -157,13 +167,15 @@ export const armorFeatures = { name: 'DAGGERHEART.CONFIG.ArmorFeature.heavy.effects.heavy.name', description: 'DAGGERHEART.CONFIG.ArmorFeature.heavy.effects.heavy.description', img: 'icons/commodities/metal/ingot-worn-iron.webp', - changes: [ - { - key: 'system.evasion', - mode: 2, - value: '-1' - } - ] + system: { + changes: [ + { + key: 'system.evasion', + type: 'add', + value: '-1' + } + ] + } } ] }, @@ -212,13 +224,15 @@ export const armorFeatures = { name: 'DAGGERHEART.CONFIG.ArmorFeature.magical.effects.magical.name', description: 'DAGGERHEART.CONFIG.ArmorFeature.magical.effects.magical.description', img: 'icons/magic/defensive/barrier-shield-dome-blue-purple.webp', - changes: [ - { - key: 'system.rules.damageReduction.magical', - mode: 5, - value: 1 - } - ] + system: { + changes: [ + { + key: 'system.rules.damageReduction.magical', + type: 'override', + value: 1 + } + ] + } } ] }, @@ -249,13 +263,15 @@ export const armorFeatures = { name: 'DAGGERHEART.CONFIG.ArmorFeature.physical.effects.physical.name', description: 'DAGGERHEART.CONFIG.ArmorFeature.physical.effects.physical.description', img: 'icons/commodities/stone/ore-pile-tan.webp', - changes: [ - { - key: 'system.rules.damageReduction.physical', - mode: 5, - value: 1 - } - ] + system: { + changes: [ + { + key: 'system.rules.damageReduction.physical', + type: 'override', + value: 1 + } + ] + } } ] }, @@ -280,18 +296,20 @@ export const armorFeatures = { name: 'DAGGERHEART.CONFIG.ArmorFeature.reinforced.effects.reinforced.name', description: 'DAGGERHEART.CONFIG.ArmorFeature.reinforced.effects.reinforced.description', img: 'icons/magic/defensive/shield-barrier-glowing-triangle-green.webp', - changes: [ - { - key: 'system.bunuses.damageThresholds.major', - mode: 2, - value: '2' - }, - { - key: 'system.bunuses.damageThresholds.severe', - mode: 2, - value: '2' - } - ] + system: { + changes: [ + { + key: 'system.bunuses.damageThresholds.major', + type: 'add', + value: '2' + }, + { + key: 'system.bunuses.damageThresholds.severe', + type: 'add', + value: '2' + } + ] + } } ] }, @@ -326,18 +344,20 @@ export const armorFeatures = { name: 'DAGGERHEART.CONFIG.ArmorFeature.sharp.effects.sharp.name', description: 'DAGGERHEART.CONFIG.ArmorFeature.sharp.effects.sharp.description', img: 'icons/magic/defensive/shield-barrier-glowing-triangle-green.webp', - changes: [ - { - key: 'system.bonuses.damage.primaryWeapon.dice', - mode: 2, - value: '1d4' - }, - { - key: 'system.bonuses.damage.secondaryWeapon.dice', - mode: 2, - value: '1d4' - } - ] + system: { + changes: [ + { + key: 'system.bonuses.damage.primaryWeapon.dice', + type: 'add', + value: '1d4' + }, + { + key: 'system.bonuses.damage.secondaryWeapon.dice', + type: 'add', + value: '1d4' + } + ] + } } ] }, @@ -408,18 +428,20 @@ export const armorFeatures = { name: 'DAGGERHEART.CONFIG.ArmorFeature.veryHeavy.effects.veryHeavy.name', description: 'DAGGERHEART.CONFIG.ArmorFeature.veryHeavy.effects.veryHeavy.description', img: 'icons/commodities/metal/ingot-stamped-steel.webp', - changes: [ - { - key: 'system.evasion', - mode: 2, - value: '-2' - }, - { - key: 'system.traits.agility.value', - mode: 2, - value: '-1' - } - ] + system: { + changes: [ + { + key: 'system.evasion', + type: 'add', + value: '-2' + }, + { + key: 'system.traits.agility.value', + type: 'add', + value: '-1' + } + ] + } } ] }, @@ -431,14 +453,16 @@ export const armorFeatures = { name: 'DAGGERHEART.CONFIG.ArmorFeature.warded.effects.warded.name', description: 'DAGGERHEART.CONFIG.ArmorFeature.warded.effects.warded.description', img: 'icons/magic/defensive/barrier-shield-dome-pink.webp', - changes: [ - { - key: 'system.resistance.magical.reduction', - mode: 2, - value: '@system.armorScore', - priority: 21 - } - ] + system: { + changes: [ + { + key: 'system.resistance.magical.reduction', + type: 'add', + value: '@system.armorScore', + priority: 21 + } + ] + } } ] } @@ -488,21 +512,23 @@ export const weaponFeatures = { name: 'DAGGERHEART.CONFIG.WeaponFeature.barrier.effects.barrier.name', description: 'DAGGERHEART.CONFIG.WeaponFeature.barrier.effects.barrier.description', img: 'icons/skills/melee/shield-block-bash-blue.webp', - changes: [ - { - key: 'system.evasion', - mode: 2, - value: '-1' - }, - { - key: 'Armor', - type: 'armor', - typeData: { + system: { + changes: [ + { + key: 'system.evasion', + type: 'add', + value: '-1' + }, + { + key: 'Armor', type: 'armor', - max: 'ITEM.@system.tier + 1' + typeData: { + type: 'armor', + max: 'ITEM.@system.tier + 1' + } } - } - ] + ] + } } ] }, @@ -514,13 +540,15 @@ export const weaponFeatures = { name: 'DAGGERHEART.CONFIG.WeaponFeature.bonded.effects.damage.name', description: 'DAGGERHEART.CONFIG.WeaponFeature.bonded.effects.damage.description', img: 'icons/magic/symbols/chevron-elipse-circle-blue.webp', - changes: [ - { - key: 'system.bonuses.damage.primaryWeapon.bonus', - mode: 2, - value: '@system.levelData.level.current' - } - ] + system: { + changes: [ + { + key: 'system.bonuses.damage.primaryWeapon.bonus', + type: 'add', + value: '@system.levelData.level.current' + } + ] + } } ] }, @@ -553,18 +581,20 @@ export const weaponFeatures = { name: 'DAGGERHEART.CONFIG.WeaponFeature.brave.effects.brave.name', description: 'DAGGERHEART.CONFIG.WeaponFeature.brave.effects.brave.description', img: 'icons/magic/life/heart-cross-strong-flame-purple-orange.webp', - changes: [ - { - key: 'system.evasion', - mode: 2, - value: '-1' - }, - { - key: 'system.damageThresholds.severe', - mode: 2, - value: 'ITEM.@system.tier' - } - ] + system: { + changes: [ + { + key: 'system.evasion', + type: 'add', + value: '-1' + }, + { + key: 'system.damageThresholds.severe', + type: 'add', + value: 'ITEM.@system.tier' + } + ] + } } ] }, @@ -618,13 +648,15 @@ export const weaponFeatures = { name: 'DAGGERHEART.CONFIG.WeaponFeature.charged.name', description: 'DAGGERHEART.CONFIG.WeaponFeature.charged.description', img: 'icons/magic/lightning/claws-unarmed-strike-teal.webp', - changes: [ - { - key: 'system.proficiency', - mode: 2, - value: '1' - } - ] + system: { + changes: [ + { + key: 'system.proficiency', + type: 'add', + value: '1' + } + ] + } } ] } @@ -660,13 +692,15 @@ export const weaponFeatures = { name: 'DAGGERHEART.CONFIG.WeaponFeature.cumbersome.effects.cumbersome.name', description: 'DAGGERHEART.CONFIG.WeaponFeature.cumbersome.effects.cumbersome.description', img: 'icons/commodities/metal/mail-plate-steel.webp', - changes: [ - { - key: 'system.traits.finesse.value', - mode: 2, - value: '-1' - } - ] + system: { + changes: [ + { + key: 'system.traits.finesse.value', + type: 'add', + value: '-1' + } + ] + } } ] }, @@ -707,14 +741,16 @@ export const weaponFeatures = { name: 'DAGGERHEART.CONFIG.WeaponFeature.deflecting.effects.deflecting.name', description: 'DAGGERHEART.CONFIG.WeaponFeature.deflecting.effects.deflecting.description', img: 'icons/skills/melee/hand-grip-sword-strike-orange.webp', - changes: [ - { - key: 'system.evasion', - mode: 2, - value: '@system.armorScore', - priority: 21 - } - ] + system: { + changes: [ + { + key: 'system.evasion', + type: 'add', + value: '@system.armorScore', + priority: 21 + } + ] + } } ] } @@ -754,13 +790,15 @@ export const weaponFeatures = { name: 'DAGGERHEART.CONFIG.WeaponFeature.destructive.name', description: 'DAGGERHEART.CONFIG.WeaponFeature.destructive.effects.agility', img: 'icons/skills/melee/strike-flail-spiked-pink.webp', - changes: [ - { - key: 'system.traits.agility.value', - mode: 2, - value: '-1' - } - ] + system: { + changes: [ + { + key: 'system.traits.agility.value', + type: 'add', + value: '-1' + } + ] + } } ] }, @@ -795,7 +833,7 @@ export const weaponFeatures = { changes: [ { key: 'system.bonuses.damage.primaryWeapon.bonus', - mode: 2, + type: 'add', value: '1' } ], @@ -902,13 +940,15 @@ export const weaponFeatures = { name: 'DAGGERHEART.CONFIG.WeaponFeature.greedy.actions.greed.name', description: 'DAGGERHEART.CONFIG.WeaponFeature.greedy.actions.greed.description', img: 'icons/commodities/currency/coins-crown-stack-gold.webp', - changes: [ - { - key: 'system.proficiency', - mode: 2, - value: '1' - } - ] + system: { + changes: [ + { + key: 'system.proficiency', + type: 'add', + value: '1' + } + ] + } } ] } @@ -951,13 +991,15 @@ export const weaponFeatures = { name: 'DAGGERHEART.CONFIG.WeaponFeature.heavy.effects.heavy.name', description: 'DAGGERHEART.CONFIG.WeaponFeature.heavy.effects.heavy.description', img: 'icons/commodities/metal/ingot-worn-iron.webp', - changes: [ - { - key: 'system.evasion', - mode: 2, - value: '-1' - } - ] + system: { + changes: [ + { + key: 'system.evasion', + type: 'add', + value: '-1' + } + ] + } } ] }, @@ -1066,13 +1108,15 @@ export const weaponFeatures = { name: 'DAGGERHEART.CONFIG.WeaponFeature.massive.effects.massive.name', description: 'DAGGERHEART.CONFIG.WeaponFeature.massive.effects.massive.description', img: 'icons/skills/melee/strike-flail-destructive-yellow.webp', - changes: [ - { - key: 'system.evasion', - mode: 2, - value: '-1' - } - ] + system: { + changes: [ + { + key: 'system.evasion', + type: 'add', + value: '-1' + } + ] + } } ] }, @@ -1107,7 +1151,7 @@ export const weaponFeatures = { changes: [ { key: 'system.bonuses.damage.primaryWeapon.bonus', - mode: 2, + type: 'add', value: 'ITEM.@system.tier + 1' } ], @@ -1158,13 +1202,15 @@ export const weaponFeatures = { name: 'DAGGERHEART.CONFIG.WeaponFeature.persuasive.effects.persuasive.name', description: 'DAGGERHEART.CONFIG.WeaponFeature.persuasive.effects.persuasive.description', img: 'icons/magic/control/hypnosis-mesmerism-eye.webp', - changes: [ - { - key: 'system.traits.presence.value', - mode: 2, - value: '2' - } - ] + system: { + changes: [ + { + key: 'system.traits.presence.value', + type: 'add', + value: '2' + } + ] + } } ] } @@ -1203,17 +1249,20 @@ export const weaponFeatures = { name: 'DAGGERHEART.CONFIG.WeaponFeature.protective.effects.protective.name', description: 'DAGGERHEART.CONFIG.WeaponFeature.protective.effects.protective.description', img: 'icons/skills/melee/shield-block-gray-orange.webp', - changes: [ - { - key: 'Armor', - type: 'armor', - value: 0, - typeData: { + system: { + changes: [ + { + key: 'Armor', type: 'armor', - max: 'ITEM.@system.tier' + value: 0, + typeData: { + type: 'armor', + max: 'ITEM.@system.tier' + } } - } - ] + ] + } + } ] }, @@ -1244,13 +1293,15 @@ export const weaponFeatures = { name: 'DAGGERHEART.CONFIG.WeaponFeature.reliable.effects.reliable.name', description: 'DAGGERHEART.CONFIG.WeaponFeature.reliable.effects.reliable.description', img: 'icons/skills/melee/strike-sword-slashing-red.webp', - changes: [ - { - key: 'system.bonuses.roll.primaryWeapon.bonus', - mode: 2, - value: 1 - } - ] + system: { + changes: [ + { + key: 'system.bonuses.roll.primaryWeapon.bonus', + type: 'add', + value: 1 + } + ] + } } ] }, @@ -1341,7 +1392,7 @@ export const weaponFeatures = { changes: [ { key: 'system.bonuses.damage.primaryWeapon.bonus', - mode: 2, + type: 'add', value: '@system.traits.agility.value', priority: 21 } From 76ee7779851e25b818c25f184ca6f9f7208a70b7 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 12 Jul 2026 00:37:02 +0200 Subject: [PATCH 09/30] [Fix] ActiveEffect Mode OneTime Migration (#2078) * Migrations for ActiveEffect Mode * Start on migration handlers --------- Co-authored-by: Carlos Fernandez --- .../migration-handlers/2_5_2.mjs | 38 +++++++++ .../migration-handlers/base.mjs | 77 +++++++++++++++++++ module/systemRegistration/migrations.mjs | 15 +++- 3 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 module/systemRegistration/migration-handlers/2_5_2.mjs create mode 100644 module/systemRegistration/migration-handlers/base.mjs diff --git a/module/systemRegistration/migration-handlers/2_5_2.mjs b/module/systemRegistration/migration-handlers/2_5_2.mjs new file mode 100644 index 00000000..f6d9ea77 --- /dev/null +++ b/module/systemRegistration/migration-handlers/2_5_2.mjs @@ -0,0 +1,38 @@ +import { MigrationHandlerBase } from './base.mjs'; + +export class Migration_2_5_2 extends MigrationHandlerBase { + version = '2.5.2'; + + /** @inheritdoc */ + async updateActiveEffectSource(effectSource, item) { + let shouldUpdate = false; + const newChanges = []; + const srdItem = item?._stats.compendiumSource ? + await foundry.utils.fromUuid(item?._stats.compendiumSource) : + null; + for (let i = 0; i < effectSource.system.changes.length; i++) { + const change = effectSource.system.changes[i]; + const srdEffect = srdItem?.effects.find(x => x.name === effectSource.name); + if (change.type === 'custom') { + const srdChange = srdEffect ? srdEffect.system.changes[i] : null; + if ( + change.key === srdChange.key && + change.value === srdChange.value && + change.type !== srdChange.type + ) { + shouldUpdate = true; + newChanges.push(srdChange); + } + } else { + newChanges.push(change); + } + } + + if (shouldUpdate) { + return { + _id: effectSource._id, + system: { changes: newChanges } + } + } + } +} \ No newline at end of file diff --git a/module/systemRegistration/migration-handlers/base.mjs b/module/systemRegistration/migration-handlers/base.mjs new file mode 100644 index 00000000..7426570d --- /dev/null +++ b/module/systemRegistration/migration-handlers/base.mjs @@ -0,0 +1,77 @@ +/** + * @import DHItem from "../../documents/item.mjs"; + */ + +/** + * The base class of an async migration. + * These are generally run between versions for things that require compendiums or must be done in post. + * The migrate() functions calls the various updateXSource() functions. + * Generally a subclass will override the version and the updateXSource() functions. + */ +export class MigrationHandlerBase { + version = null; + + /** + * Gets change data for an active effect's source, or null if no changes + * @param {object} effectSource + * @param {DHItem} item + * @returns {Promise} + * @protected + */ + async updateActiveEffectSource(effectSource, item) { + return null; + } + + async migrate() { + // todo: handle more than just migrating effects. Right now this can only migrate effects + // NOTE: the preload is hardcoded, we should not hardcode it + + const numActors = game.actors.size; + const numItems = game.items.size; + const finalUpdateProgress = 5; + const DhProgress = game.system.api.applications.ui.DhProgress; + const preRunProgress = game.packs.size; + + const progress = DhProgress.createMigrationProgress( + preRunProgress + numActors + numItems + finalUpdateProgress + ); + + // Preload. Avoid hardcoding in the future + for (const pack of game.packs) { + await pack.getDocuments(); + progress.advance(); + } + + const batch = []; + + const updateItem = async item => { + const itemUpdates = []; + for (const effect of item.effects) { + const changes = await this.updateActiveEffectSource(effect.toObject(), item); + if (changes) itemUpdates.push(changes); + } + if (itemUpdates.length) { + batch.push({ + action: 'update', + documentName: 'ActiveEffect', + updates: itemUpdates, + parent: item + }); + } + }; + + for (const actor of game.actors) { + for (const item of actor.items) { + await updateItem(item); + } + progress.advance(); + } + for (const item of game.items) { + await updateItem(item); + progress.advance(); + } + + await foundry.documents.modifyBatch(batch); + progress.advance({ by: finalUpdateProgress }); + } +} \ No newline at end of file diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs index 6971c34c..fef97b8f 100644 --- a/module/systemRegistration/migrations.mjs +++ b/module/systemRegistration/migrations.mjs @@ -1,4 +1,5 @@ import { defaultRestOptions } from '../config/generalConfig.mjs'; +import { Migration_2_5_2 } from './migration-handlers/2_5_2.mjs'; export async function runMigrations() { let lastMigrationVersion = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion); @@ -320,7 +321,19 @@ export async function runMigrations() { lastMigrationVersion = '2.1.0'; } - //#endregion await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, lastMigrationVersion); + + /* -------------------------------------------- */ + /* New Style migrations below this point */ + /* -------------------------------------------- */ + + const migrations = [ + new Migration_2_5_2() + ].filter(m => m.version && foundry.utils.isNewerVersion(m.version, lastMigrationVersion)); + + for (const handler of migrations) { + await handler.migrate(); + await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, handler.version); + } } From 783505da0ace8667dd134bf5d6c961ebf6aea03d Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sun, 12 Jul 2026 00:37:37 +0200 Subject: [PATCH 10/30] Raised verison --- system.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system.json b/system.json index 3f1b49a6..43e06254 100644 --- a/system.json +++ b/system.json @@ -2,7 +2,7 @@ "id": "daggerheart", "title": "Daggerheart", "description": "An unofficial implementation of the Daggerheart system", - "version": "2.5.1", + "version": "2.5.2", "compatibility": { "minimum": "14.364", "verified": "14.364", @@ -10,7 +10,7 @@ }, "url": "https://github.com/Foundryborne/daggerheart", "manifest": "https://raw.githubusercontent.com/Foundryborne/daggerheart/v14/system.json", - "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.5.1/system.zip", + "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.5.2/system.zip", "authors": [ { "name": "WBHarry" From 3faf588e6c361555d6985b2e94574b3ea89db557 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Sun, 12 Jul 2026 20:43:56 -0400 Subject: [PATCH 11/30] Fix chat messages with list items or weapon/armor features (#2081) --- module/data/item/base.mjs | 2 +- module/documents/item.mjs | 5 +--- styles/less/global/inventory-item.less | 32 +---------------------- styles/less/global/prose-mirror.less | 31 +--------------------- styles/less/ui/chat/ability-use.less | 4 ++- styles/less/utils/mixin.less | 36 +++++++++++++++++++++++++- 6 files changed, 42 insertions(+), 68 deletions(-) diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs index ba114fda..131ef10f 100644 --- a/module/data/item/base.mjs +++ b/module/data/item/base.mjs @@ -143,7 +143,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { /** * Gets the enriched and augmented description for the item. * @param {object} [options] - Options that modify the styling of the rendered template. { headerStyle: undefined|'none'|'large' } - * @returns {string} + * @returns {Promise} */ async getEnrichedDescription() { if (!this.metadata.hasDescription) return ''; diff --git a/module/documents/item.mjs b/module/documents/item.mjs index 14717538..8112e99f 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -208,10 +208,7 @@ export default class DHItem extends foundry.documents.Item { tags: this._getTags() }, actions: item.system.actionsList, - description: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.system.description, { - relativeTo: this.parent, - rollData: this.parent?.getRollData() ?? {} - }) + description: await this.system.getEnrichedDescription() }; const msg = { diff --git a/styles/less/global/inventory-item.less b/styles/less/global/inventory-item.less index 2e6cc863..d942133c 100644 --- a/styles/less/global/inventory-item.less +++ b/styles/less/global/inventory-item.less @@ -163,37 +163,7 @@ } .inventory-description { overflow: hidden; - - h1 { - font-size: var(--font-size-32); - } - h2 { - font-size: var(--font-size-28); - font-weight: 600; - } - h3 { - font-size: var(--font-size-20); - font-weight: 600; - } - h4 { - font-size: var(--font-size-16); - color: @beige; - font-weight: 600; - } - - ul, - ol { - margin: 1rem 0; - padding: 0 0 0 1.25rem; - - li { - margin-bottom: 0.25rem; - } - } - - ul { - list-style: disc; - } + .typography(); } } .item-resources { diff --git a/styles/less/global/prose-mirror.less b/styles/less/global/prose-mirror.less index 27048ddf..430ca79d 100644 --- a/styles/less/global/prose-mirror.less +++ b/styles/less/global/prose-mirror.less @@ -14,36 +14,7 @@ } .editor-content { .with-scroll-shadows(); - h1 { - font-size: var(--font-size-32); - } - h2 { - font-size: var(--font-size-28); - font-weight: 600; - } - h3 { - font-size: var(--font-size-20); - font-weight: 600; - } - h4 { - font-size: var(--font-size-16); - color: light-dark(@dark, @beige); - font-weight: 600; - } - - ul, - ol { - margin: 1rem 0; - padding: 0 0 0 1.25rem; - - li { - margin-bottom: 0.25rem; - } - } - - ul { - list-style: disc; - } + .typography(); } // Fixes centering and makes it not render over scrollbar &:hover button.toggle:enabled { diff --git a/styles/less/ui/chat/ability-use.less b/styles/less/ui/chat/ability-use.less index d028fa7a..c31136ad 100644 --- a/styles/less/ui/chat/ability-use.less +++ b/styles/less/ui/chat/ability-use.less @@ -117,7 +117,9 @@ } .description { - padding: 8px; + padding: 0; + margin: 8px; + .typography(); } .ability-card-footer { diff --git a/styles/less/utils/mixin.less b/styles/less/utils/mixin.less index 2ce85166..fb70d0a3 100644 --- a/styles/less/utils/mixin.less +++ b/styles/less/utils/mixin.less @@ -203,4 +203,38 @@ overflow-y: auto; scrollbar-gutter: stable; .with-scroll-shadows(); -} \ No newline at end of file +} + +/** Typography stylings for most longform text, usually item descriptions */ +.typography() { + h1 { + font-size: var(--font-size-32); + } + h2 { + font-size: var(--font-size-28); + font-weight: 600; + } + h3 { + font-size: var(--font-size-20); + font-weight: 600; + } + h4 { + font-size: var(--font-size-16); + color: light-dark(@dark, @beige); + font-weight: 600; + } + + ul, + ol { + margin: 1rem 0; + padding: 0 0 0 1.25rem; + + li { + margin-bottom: 0.25rem; + } + } + + ul { + list-style: disc; + } +} From 3de9c2f9095620d0f6f32445e77934eb90cb633c Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Mon, 13 Jul 2026 08:56:49 -0400 Subject: [PATCH 12/30] Remove duplicate action descriptions in environments (#2083) --- ...ment_Abandoned_Grove_pGEdzdLkqYtBhxnG.json | 2 +- ...nvironment_Ambushers_uXZpebPR77YQ1oXI.json | 10 ++--- ...ronment_Castle_Siege_1eZ32Esq7rfZOjlu.json | 2 +- ...ent_Cliffside_Ascent_LPpfdlNKqiZIl04w.json | 12 +++--- ...ironment_Cult_Ritual_QAXXiOKBDmCTauHD.json | 38 +++++++++---------- ...ment_Hallowed_Temple_dsA6j69AnaJhUyqH.json | 12 +++--- ...ronment_Haunted_City_OzYbizKraK92FDiI.json | 10 ++--- ...nment_Imperial_Court_jr1xAoXzVwVblzxI.json | 10 ++--- ...ecromancer_s_Ossuary_h3KyRL7AshhLAmcH.json | 6 +-- ...nment_Pitched_Battle_EWD3ZsLoK6VMVOf7.json | 2 +- 10 files changed, 46 insertions(+), 58 deletions(-) diff --git a/src/packs/environments/environment_Abandoned_Grove_pGEdzdLkqYtBhxnG.json b/src/packs/environments/environment_Abandoned_Grove_pGEdzdLkqYtBhxnG.json index 23c1d966..039eafcf 100644 --- a/src/packs/environments/environment_Abandoned_Grove_pGEdzdLkqYtBhxnG.json +++ b/src/packs/environments/environment_Abandoned_Grove_pGEdzdLkqYtBhxnG.json @@ -391,7 +391,7 @@ "type": "effect", "_id": "p6V4k4yMwJ1UPZMz", "systemPath": "actions", - "description": "

Spend a Fear to summon a @UUID[Compendium.daggerheart.adversaries.Actor.sRn4bqerfARvhgSV]{Minor Chaos Elemental} drawn to the echoes of violence and discord. They appear within Far range of a chosen PC and immediately take the spotlight.

What color does the grass turn as the elemental appears? How does the chaos warp insects and small wildlife within the grove?

", + "description": "", "chatDisplay": true, "actionType": "action", "cost": [], diff --git a/src/packs/environments/environment_Ambushers_uXZpebPR77YQ1oXI.json b/src/packs/environments/environment_Ambushers_uXZpebPR77YQ1oXI.json index e8ba889a..582e7ec0 100644 --- a/src/packs/environments/environment_Ambushers_uXZpebPR77YQ1oXI.json +++ b/src/packs/environments/environment_Ambushers_uXZpebPR77YQ1oXI.json @@ -35,12 +35,9 @@ "src": "systems/daggerheart/assets/icons/documents/actors/forest.svg", "anchorX": 0.5, "anchorY": 0.5, - "offsetX": 0, - "offsetY": 0, "fit": "contain", "scaleX": 1, "scaleY": 1, - "rotation": 0, "tint": "#ffffff", "alphaThreshold": 0.75 }, @@ -91,7 +88,7 @@ "saturation": 0, "contrast": 0 }, - "detectionModes": [], + "detectionModes": {}, "occludable": { "radius": 0 }, @@ -117,7 +114,8 @@ "flags": {}, "randomImg": false, "appendNumber": false, - "prependAdjective": false + "prependAdjective": false, + "depth": 1 }, "items": [ { @@ -156,7 +154,7 @@ "type": "effect", "_id": "6DKa1Pm605HpChPd", "systemPath": "actions", - "description": "

When a PC starts the ambush on unsuspecting adversaries, you lose 2 Fear and the first attack roll a PC makes has advantage.

What are the adversaries in the middle of doing when the ambush starts? How does this impact their approach to the fight?

", + "description": "", "chatDisplay": true, "actionType": "action", "cost": [ diff --git a/src/packs/environments/environment_Castle_Siege_1eZ32Esq7rfZOjlu.json b/src/packs/environments/environment_Castle_Siege_1eZ32Esq7rfZOjlu.json index 190d78b1..0df61deb 100644 --- a/src/packs/environments/environment_Castle_Siege_1eZ32Esq7rfZOjlu.json +++ b/src/packs/environments/environment_Castle_Siege_1eZ32Esq7rfZOjlu.json @@ -330,7 +330,7 @@ "type": "attack", "_id": "r5JN5oFYL5DC6Qqw", "systemPath": "actions", - "description": "

When an adversary is defeated, you can spend a Fear to have a stray attack from a siege weapon hit a point on the battlefield. All targets within Very Close range of that point must make an Agility Reaction Roll.

  • Targets who fail take 3d8+3 physical or magic damage and must mark a Stress.

  • Targets who succeed must mark a Stress.

What debris is scattered by the attack? What is broken by the strike that can’t be easily mended?

", + "description": "", "chatDisplay": true, "actionType": "action", "cost": [], diff --git a/src/packs/environments/environment_Cliffside_Ascent_LPpfdlNKqiZIl04w.json b/src/packs/environments/environment_Cliffside_Ascent_LPpfdlNKqiZIl04w.json index ef367d67..c65ede29 100644 --- a/src/packs/environments/environment_Cliffside_Ascent_LPpfdlNKqiZIl04w.json +++ b/src/packs/environments/environment_Cliffside_Ascent_LPpfdlNKqiZIl04w.json @@ -45,12 +45,9 @@ "src": "systems/daggerheart/assets/icons/documents/actors/forest.svg", "anchorX": 0.5, "anchorY": 0.5, - "offsetX": 0, - "offsetY": 0, "fit": "contain", "scaleX": 1, "scaleY": 1, - "rotation": 0, "tint": "#ffffff", "alphaThreshold": 0.75 }, @@ -101,7 +98,7 @@ "saturation": 0, "contrast": 0 }, - "detectionModes": [], + "detectionModes": {}, "occludable": { "radius": 0 }, @@ -127,7 +124,8 @@ "flags": {}, "randomImg": false, "appendNumber": false, - "prependAdjective": false + "prependAdjective": false, + "depth": 1 }, "items": [ { @@ -204,7 +202,7 @@ "type": "damage", "_id": "p1UiGEiGyl6r7PrA", "systemPath": "actions", - "description": "

Previous climbers left behind large metal rods that climbers can use to aid their ascent. If a PC using the pitons fails an action roll to climb, they can mark a Stress instead of ticking the countdown up.

What do the shape and material of these pitons tell you about the previous climbers? How far apart are they from one another?

", + "description": "", "chatDisplay": true, "actionType": "action", "cost": [], @@ -435,7 +433,7 @@ "type": "effect", "_id": "M8MfD2qBfYCwNKvH", "systemPath": "actions", - "description": "

Spend a Fear to have a PC’s handhold fail, plummeting them toward the ground. If they aren’t saved on the next action, they hit the ground and tick up the countdown by 2. The PC takes 1d12 physical damage if the countdown is between 8 and 12, 2d12 between 4 and 7, and 3d12 at 3 or lower.

How can you tell many others have fallen here before? What lives in these walls that might try to scare adventurers into falling for an easy meal?

", + "description": "", "chatDisplay": true, "actionType": "action", "cost": [ diff --git a/src/packs/environments/environment_Cult_Ritual_QAXXiOKBDmCTauHD.json b/src/packs/environments/environment_Cult_Ritual_QAXXiOKBDmCTauHD.json index d6809cd1..e3e90c26 100644 --- a/src/packs/environments/environment_Cult_Ritual_QAXXiOKBDmCTauHD.json +++ b/src/packs/environments/environment_Cult_Ritual_QAXXiOKBDmCTauHD.json @@ -45,12 +45,9 @@ "src": "systems/daggerheart/assets/icons/documents/actors/forest.svg", "anchorX": 0.5, "anchorY": 0.5, - "offsetX": 0, - "offsetY": 0, "fit": "contain", "scaleX": 1, "scaleY": 1, - "rotation": 0, "tint": "#ffffff", "alphaThreshold": 0.75 }, @@ -101,7 +98,7 @@ "saturation": 0, "contrast": 0 }, - "detectionModes": [], + "detectionModes": {}, "occludable": { "radius": 0 }, @@ -127,7 +124,8 @@ "flags": {}, "randomImg": false, "appendNumber": false, - "prependAdjective": false + "prependAdjective": false, + "depth": 1 }, "items": [ { @@ -236,7 +234,7 @@ "type": "effect", "_id": "EATw4ZkcuGeDfgLZ", "systemPath": "actions", - "description": "

A portion of the ritual’s power is diverted into a cult member to fight off interlopers. Choose one adversary to become Imbued with terrible magic until the scene ends or they’re defeated. An Imbued adversary immediately takes the spotlight and gains one of the following benefits, or all three if you spend a Fear:

  • They gain advantage on all attacks.

  • They deal an extra 1d10 damage on a successful attack.

  • They gain the following feature: Relentless (2) - Passive. This adversary can be spotlighted up to two times per GM turn. Spend Fear as usual to spotlight them.

How does the enemy change in appearance? What fears do their blows bring to the surface?

", + "description": "", "chatDisplay": true, "actionType": "action", "cost": [], @@ -283,13 +281,10 @@ }, "disabled": false, "duration": { - "startTime": null, - "combat": null, - "seconds": null, - "rounds": null, - "turns": null, - "startRound": null, - "startTurn": null + "value": null, + "units": "seconds", + "expiry": null, + "expired": false }, "description": "

An Imbued adversary immediately takes the spotlight and gains one of the following benefits, or all three if you spend a Fear:

  • They gain advantage on all attacks.

  • They deal an extra 1d10 damage on a successful attack.

  • They gain the following feature: Relentless (2) - Passive. This adversary can be spotlighted up to two times per GM turn. Spend Fear as usual to spotlight them.

How does the enemy change in appearance? What fears do their blows bring to the surface?

", "tint": "#ffffff", @@ -299,6 +294,9 @@ "_stats": { "compendiumSource": null }, + "start": null, + "showIcon": 1, + "folder": null, "_key": "!actors.items.effects!QAXXiOKBDmCTauHD.0Rgqw1kUPeJ11ldd.dYQBQq1xIysM0qLo" }, { @@ -326,13 +324,10 @@ }, "disabled": true, "duration": { - "startTime": null, - "combat": null, - "seconds": null, - "rounds": null, - "turns": null, - "startRound": null, - "startTurn": null + "value": null, + "units": "seconds", + "expiry": null, + "expired": false }, "description": "", "tint": "#ffffff", @@ -342,6 +337,9 @@ "_stats": { "compendiumSource": null }, + "start": null, + "showIcon": 1, + "folder": null, "_key": "!actors.items.effects!QAXXiOKBDmCTauHD.0Rgqw1kUPeJ11ldd.Hxw5lXE77bGzuaOu" } ], diff --git a/src/packs/environments/environment_Hallowed_Temple_dsA6j69AnaJhUyqH.json b/src/packs/environments/environment_Hallowed_Temple_dsA6j69AnaJhUyqH.json index c510a87f..d8b04d22 100644 --- a/src/packs/environments/environment_Hallowed_Temple_dsA6j69AnaJhUyqH.json +++ b/src/packs/environments/environment_Hallowed_Temple_dsA6j69AnaJhUyqH.json @@ -44,12 +44,9 @@ "src": "systems/daggerheart/assets/icons/documents/actors/forest.svg", "anchorX": 0.5, "anchorY": 0.5, - "offsetX": 0, - "offsetY": 0, "fit": "contain", "scaleX": 1, "scaleY": 1, - "rotation": 0, "tint": "#ffffff", "alphaThreshold": 0.75 }, @@ -100,7 +97,7 @@ "saturation": 0, "contrast": 0 }, - "detectionModes": [], + "detectionModes": {}, "occludable": { "radius": 0 }, @@ -126,7 +123,8 @@ "flags": {}, "randomImg": false, "appendNumber": false, - "prependAdjective": false + "prependAdjective": false, + "depth": 1 }, "items": [ { @@ -140,7 +138,7 @@ "type": "healing", "_id": "uLCoTKa7Jn2HaRqR", "systemPath": "actions", - "description": "

A PC who takes a rest in the Hallowed Temple automatically clears all HP.

What does the incense smell like? What kinds of songs do the acolytes sing?

", + "description": "", "chatDisplay": true, "actionType": "action", "cost": [], @@ -337,7 +335,7 @@ "type": "effect", "_id": "pJVipg7CbA9CB0Um", "systemPath": "actions", - "description": "

When the PCs have trespassed, blasphemed, or offended the clergy, you can spend a Fear to summon a @UUID[Compendium.daggerheart.adversaries.Actor.r1mbfSSwKWdcFdAU]{High Seraph} and [[/r 1d4]] @UUID[Compendium.daggerheart.adversaries.Actor.B4LZcGuBAHzyVdzy]{Bladed Guard} within Close range of the senior priest to reinforce their will.

What symbols or icons do they bear that signal they are anointed agents of the divinity? Who leads the group and what led them to this calling?

", + "description": "", "chatDisplay": true, "actionType": "action", "cost": [ diff --git a/src/packs/environments/environment_Haunted_City_OzYbizKraK92FDiI.json b/src/packs/environments/environment_Haunted_City_OzYbizKraK92FDiI.json index 564612cb..bbc3ef0a 100644 --- a/src/packs/environments/environment_Haunted_City_OzYbizKraK92FDiI.json +++ b/src/packs/environments/environment_Haunted_City_OzYbizKraK92FDiI.json @@ -44,12 +44,9 @@ "src": "systems/daggerheart/assets/icons/documents/actors/forest.svg", "anchorX": 0.5, "anchorY": 0.5, - "offsetX": 0, - "offsetY": 0, "fit": "contain", "scaleX": 1, "scaleY": 1, - "rotation": 0, "tint": "#ffffff", "alphaThreshold": 0.75 }, @@ -100,7 +97,7 @@ "saturation": 0, "contrast": 0 }, - "detectionModes": [], + "detectionModes": {}, "occludable": { "radius": 0 }, @@ -126,7 +123,8 @@ "flags": {}, "randomImg": false, "appendNumber": false, - "prependAdjective": false + "prependAdjective": false, + "depth": 1 }, "items": [ { @@ -282,7 +280,7 @@ "type": "countdown", "_id": "VhqZKDA4032i8zY3", "systemPath": "actions", - "description": "

Spend a Fear to manifest the echo of a past disaster that ravaged the city. Activate a Progress Countdown (5) as the disaster replays around the PCs. To complete the countdown and escape the catastrophe, the PCs must overcome threats such as rampaging fires, stampeding civilians, collapsing buildings, or crumbling streets, while recalling history and finding clues to escape the inevitable.

Is this the disaster that led the city to be abandoned? What is known about this disaster and how could that help the PCs escape?

", + "description": "", "chatDisplay": true, "originItem": { "type": "itemCollection" diff --git a/src/packs/environments/environment_Imperial_Court_jr1xAoXzVwVblzxI.json b/src/packs/environments/environment_Imperial_Court_jr1xAoXzVwVblzxI.json index 5807d43c..93851e3c 100644 --- a/src/packs/environments/environment_Imperial_Court_jr1xAoXzVwVblzxI.json +++ b/src/packs/environments/environment_Imperial_Court_jr1xAoXzVwVblzxI.json @@ -46,12 +46,9 @@ "src": "systems/daggerheart/assets/icons/documents/actors/forest.svg", "anchorX": 0.5, "anchorY": 0.5, - "offsetX": 0, - "offsetY": 0, "fit": "contain", "scaleX": 1, "scaleY": 1, - "rotation": 0, "tint": "#ffffff", "alphaThreshold": 0.75 }, @@ -102,7 +99,7 @@ "saturation": 0, "contrast": 0 }, - "detectionModes": [], + "detectionModes": {}, "occludable": { "radius": 0 }, @@ -128,7 +125,8 @@ "flags": {}, "randomImg": false, "appendNumber": false, - "prependAdjective": false + "prependAdjective": false, + "depth": 1 }, "items": [ { @@ -297,7 +295,7 @@ "type": "attack", "_id": "9ipckCFMz9DVw8ab", "systemPath": "actions", - "description": "

Spend a Fear to tick down a long-term countdown related to the empire’s agenda by [[/r 1d4]]. If this triggers the countdown, a proclamation related to the agenda is announced at court as the plan is executed.

What display of power or transfer of wealth was needed to expedite this plan? Whose lives were disrupted or upended to make this happen?

", + "description": "", "chatDisplay": true, "actionType": "action", "cost": [], diff --git a/src/packs/environments/environment_Necromancer_s_Ossuary_h3KyRL7AshhLAmcH.json b/src/packs/environments/environment_Necromancer_s_Ossuary_h3KyRL7AshhLAmcH.json index 299e8729..3d87930c 100644 --- a/src/packs/environments/environment_Necromancer_s_Ossuary_h3KyRL7AshhLAmcH.json +++ b/src/packs/environments/environment_Necromancer_s_Ossuary_h3KyRL7AshhLAmcH.json @@ -137,7 +137,7 @@ "type": "damage", "_id": "jVY198vniaTSlgsX", "systemPath": "actions", - "description": "

A feature or action that clears HP requires spending a Hope to use. If it already costs Hope, a PC must spend an additional Hope.

What does it feel like to try to heal in a place so antithetical to life?

", + "description": "", "chatDisplay": true, "actionType": "action", "cost": [], @@ -240,7 +240,7 @@ "type": "attack", "_id": "M1mOwi4Limw2hRwL", "systemPath": "actions", - "description": "

All targets within Close range of a point you choose in this environment must succeed on an Agility Reaction Roll or take 4d8+8 physical damage from skeletal shrapnel as part of the ossuary detonates around them.

What ancient skeletal architecture is destroyed? What bones stick in your armor?

", + "description": "", "chatDisplay": true, "actionType": "action", "cost": [], @@ -421,7 +421,7 @@ "type": "effect", "_id": "hFeTdiHWeCYkb8Hg", "systemPath": "actions", - "description": "

Spend a Fear to summon [[/r 1d6]] @UUID[Compendium.daggerheart.adversaries.Actor.gP3fWTLzSFnpA8EJ]{Rotted Zombie}, two @UUID[Compendium.daggerheart.adversaries.Actor.CP6iRfHdyFWniTHY]{Perfected Zombie}, or a @UUID[Compendium.daggerheart.adversaries.Actor.YhJrP7rTBiRdX5Fp]{Zombie Legion}, who appear at Close range of a chosen PC.

Who were these people before they became the necromancer’s pawns? What vestiges of those lives remain for the heroes to see?

", + "description": "", "chatDisplay": true, "actionType": "action", "cost": [ diff --git a/src/packs/environments/environment_Pitched_Battle_EWD3ZsLoK6VMVOf7.json b/src/packs/environments/environment_Pitched_Battle_EWD3ZsLoK6VMVOf7.json index 42fbd8f9..120b92a0 100644 --- a/src/packs/environments/environment_Pitched_Battle_EWD3ZsLoK6VMVOf7.json +++ b/src/packs/environments/environment_Pitched_Battle_EWD3ZsLoK6VMVOf7.json @@ -199,7 +199,7 @@ "type": "attack", "_id": "1giAFbu3tGqXwi8g", "systemPath": "actions", - "description": "

Spend a Fear as a mage from one side uses large-scale destructive magic. Pick a point on the battlefield within Very Far range of the mage. All targets within Close range of that point must make an Agility Reaction Roll. Targets who fail take 3d12+8 magic damage and must mark a Stress.

What form does the attack take—fireball raining acid a storm of blades? What tactical objective is this attack meant to accomplish and what comes next?

", + "description": "", "chatDisplay": true, "actionType": "action", "cost": [ From 6e0d0b4e2cfb1fd7b4ff8bb15b1fe2fd73b139d0 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Mon, 13 Jul 2026 08:57:55 -0400 Subject: [PATCH 13/30] Adjust styling of secret blocks (#2080) * Adjust styling of secret blocks * Only show button when hovering over the secret section --- styles/less/global/elements.less | 38 ++++++++++++++++++++++++++++++++ styles/less/global/sheet.less | 4 ++-- styles/less/utils/colors.less | 4 ++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less index e35f527a..edc02f9a 100755 --- a/styles/less/global/elements.less +++ b/styles/less/global/elements.less @@ -597,6 +597,44 @@ font-size: var(--font-size-12); padding-left: 3px; } + + secret-block { + position: relative; + section.secret { + background-color: @red-10; + padding: 0; + margin-top: 0.375rem; + &.revealed { + background-color: @green-10; + } + p { + margin: 0.5rem 0; + } + } + button.reveal { + --button-size: 0.875rem; + position: absolute; + margin: auto; + left: 0; + right: 0; + width: min-content; + padding: 1px 8px 0 8px; + bottom: calc(100% - 0.4375rem - 2px); + + background-color: var(--dh-window-button-color-bg); // todo: find a better var name + border-color: var(--color-secret-border); + color: var(--dh-window-button-color-text); + font-size: var(--font-size-10); + user-select: none; + text-transform: uppercase; + + visibility: hidden; + } + + &:hover button.reveal { + visibility: visible; + } + } } .system-daggerheart { diff --git a/styles/less/global/sheet.less b/styles/less/global/sheet.less index 8381c7c3..d7be1a84 100755 --- a/styles/less/global/sheet.less +++ b/styles/less/global/sheet.less @@ -36,8 +36,8 @@ body.game:is(.performance-low, .noblur) { } button { - background: light-dark(#e8e6e3, @deep-black); - color: light-dark(@dark-blue, @beige); + background: var(--dh-window-button-color-bg); + color: var(--dh-window-button-color-text); border: 1px solid light-dark(@dark-blue, transparent); padding: 0; diff --git a/styles/less/utils/colors.less b/styles/less/utils/colors.less index bb219ebb..d9358112 100755 --- a/styles/less/utils/colors.less +++ b/styles/less/utils/colors.less @@ -107,6 +107,8 @@ --dh-input-color-text: @dark; --dh-trait-color-bg: #b1afb6; --dh-trait-color-border: #8e8d96; + --dh-window-button-color-bg: #e8e6e3; + --dh-window-button-color-text: @dark-blue; } } @scope (.theme-dark) to (.themed) { @@ -124,6 +126,8 @@ --dh-input-color-text: @beige; --dh-trait-color-bg: #50433F; --dh-trait-color-border: #927952; + --dh-window-button-color-bg: @deep-black; + --dh-window-button-color-text: @beige; } } From 7b35feb36dd71b548dbb004a3c149044cc47e7a5 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Mon, 13 Jul 2026 15:01:03 +0200 Subject: [PATCH 14/30] Corrected translation for damageReductionOnlyMagical (#2084) --- lang/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/en.json b/lang/en.json index 7863fb67..99b6baeb 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2245,7 +2245,7 @@ "hint": "A used armor slot normally reduces damage by one step. This value increases the number of steps damage is reduced by." }, "magical": { - "label": "Daamge Reduction: Only Magical", + "label": "Damage Reduction: Only Magical", "hint": "Armor can only be used to reduce magical damage" }, "maxArmorMarkedBonus": "Max Armor Used", From d3d9ddfb4188a55fa5d13ccb0d90e8c8f1e5c12d Mon Sep 17 00:00:00 2001 From: WBHarry Date: Mon, 13 Jul 2026 17:02:26 +0200 Subject: [PATCH 15/30] Fixed an issue with the 2.5.2 migration --- module/systemRegistration/migration-handlers/2_5_2.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/systemRegistration/migration-handlers/2_5_2.mjs b/module/systemRegistration/migration-handlers/2_5_2.mjs index f6d9ea77..944f0eec 100644 --- a/module/systemRegistration/migration-handlers/2_5_2.mjs +++ b/module/systemRegistration/migration-handlers/2_5_2.mjs @@ -15,7 +15,7 @@ export class Migration_2_5_2 extends MigrationHandlerBase { const srdEffect = srdItem?.effects.find(x => x.name === effectSource.name); if (change.type === 'custom') { const srdChange = srdEffect ? srdEffect.system.changes[i] : null; - if ( + if (srdChange && change.key === srdChange.key && change.value === srdChange.value && change.type !== srdChange.type From 81e264a4779fc86e7c7b2593b7d8c5e76d30d381 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Mon, 13 Jul 2026 17:04:27 +0200 Subject: [PATCH 16/30] Raised version --- system.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system.json b/system.json index 43e06254..37242137 100644 --- a/system.json +++ b/system.json @@ -2,7 +2,7 @@ "id": "daggerheart", "title": "Daggerheart", "description": "An unofficial implementation of the Daggerheart system", - "version": "2.5.2", + "version": "2.5.3", "compatibility": { "minimum": "14.364", "verified": "14.364", @@ -10,7 +10,7 @@ }, "url": "https://github.com/Foundryborne/daggerheart", "manifest": "https://raw.githubusercontent.com/Foundryborne/daggerheart/v14/system.json", - "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.5.2/system.zip", + "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.5.3/system.zip", "authors": [ { "name": "WBHarry" From 450287e4d05d0f3dd3f3d5f3e29558d8462e4c9c Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Mon, 13 Jul 2026 22:57:43 +0200 Subject: [PATCH 17/30] [Fix] Summon Wildcard Handling (#2086) --- module/data/action/baseAction.mjs | 3 ++- module/data/fields/action/summonField.mjs | 18 +++++++++-------- module/data/fields/actionField.mjs | 4 ++-- module/documents/tokenManager.mjs | 24 +++++++++++++---------- templates/ui/chat/action.hbs | 6 +++--- 5 files changed, 31 insertions(+), 24 deletions(-) diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index be7224cd..5b871c9a 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -256,7 +256,8 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel if (Hooks.call(`${CONFIG.DH.id}.postUseAction`, this, config) === false) return; - if (this.chatDisplay && !config.skips.createMessage && !config.actionChatMessageHandled) await this.toChat(); + if (this.chatDisplay && !config.skips.createMessage && !config.actionChatMessageHandled) + await this.toChat(null, config); return config; } diff --git a/module/data/fields/action/summonField.mjs b/module/data/fields/action/summonField.mjs index 6845d2ba..fef6625a 100644 --- a/module/data/fields/action/summonField.mjs +++ b/module/data/fields/action/summonField.mjs @@ -23,7 +23,7 @@ export default class DHSummonField extends fields.ArrayField { super(summonFields, options, context); } - static async execute() { + static async execute(config) { if (!canvas.scene) { ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.summon.error')); return; @@ -36,6 +36,7 @@ export default class DHSummonField extends fields.ArrayField { const rolls = []; const summonData = []; + const chatMessageData = []; for (const summon of this.summon) { const roll = new Roll(itemAbleRollParse(summon.count, this.actor, this.item)); await roll.evaluate(); @@ -54,17 +55,18 @@ export default class DHSummonField extends fields.ArrayField { tokenPreviewName: `${actor.prototypeToken.name}${remaining > 1 ? ` (${remaining}x)` : ''}` }); } + + chatMessageData.push({ + data: actor, + quantity: countNumber + }); } if (rolls.length) await triggerChatRollFx(rolls); this.actor.sheet?.minimize(); - DHSummonField.handleSummon(summonData, this.actor); - } - - static async handleSummon(summonData, actionActor) { - await CONFIG.ux.TokenManager.createTokensWithPreview(summonData, { elevation: actionActor.token?.elevation }); - - return actionActor.sheet?.maximize(); + await CONFIG.ux.TokenManager.createTokensWithPreview(summonData, { elevation: this.actor.token?.elevation }); + this.actor.sheet?.maximize(); + config.summonData = chatMessageData; } } diff --git a/module/data/fields/actionField.mjs b/module/data/fields/actionField.mjs index 83672c8e..af8f338e 100644 --- a/module/data/fields/actionField.mjs +++ b/module/data/fields/actionField.mjs @@ -269,7 +269,7 @@ export function ActionMixin(Base) { return this.delete(); } - async toChat(origin) { + async toChat(origin, config) { const autoExpandDescription = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance) .expandRollMessage?.desc; @@ -282,7 +282,7 @@ export function ActionMixin(Base) { img: this.baseAction ? this.parent.parent.img : this.img, tags: this.tags ? this.tags : ['Spell', 'Arcana', 'Lv 10'], areas: this.areas, - summon: this.summon + summon: config?.summonData }, source: { actor: this.actor.uuid, diff --git a/module/documents/tokenManager.mjs b/module/documents/tokenManager.mjs index 7678d2c7..062a22ee 100644 --- a/module/documents/tokenManager.mjs +++ b/module/documents/tokenManager.mjs @@ -19,10 +19,10 @@ export default class DhTokenManager { } } - return await canvas.tokens.placeTokens( + const placedData = await canvas.tokens.placeTokens( [ { - ...actor.prototypeToken.toObject(), + ...(await actor.getTokenDocument()).toObject(), actorId: actor.id, displayName: 50, ...tokenData @@ -30,6 +30,8 @@ export default class DhTokenManager { ], { create: false } ); + + return placedData[0] ?? null; } /** @@ -46,22 +48,24 @@ export default class DhTokenManager { const createElevation = elevation ?? level.elevation.bottom; for (const tokenData of tokensData) { - const previewTokens = await this.createPreview(tokenData.actor, { + const previewToken = await this.createPreview(tokenData.actor, { name: tokenData.tokenPreviewName, level: game.user.viewedLevel, elevation: createElevation, flags: { daggerheart: { createPlacement: true } } }); - if (!previewTokens?.length) return null; + if (!previewToken) return null; + + const finalTokenData = { + ...previewToken.toObject(), + name: tokenData.actor.prototypeToken.name, + displayName: tokenData.actor.prototypeToken.displayName, + flags: tokenData.actor.prototypeToken.flags + }; await canvas.scene.createEmbeddedDocuments( 'Token', - previewTokens.map(x => ({ - ...x.toObject(), - name: tokenData.actor.prototypeToken.name, - displayName: tokenData.actor.prototypeToken.displayName, - flags: tokenData.actor.prototypeToken.flags - })), + [finalTokenData], { controlObject: true, parent: canvas.scene } ); } diff --git a/templates/ui/chat/action.hbs b/templates/ui/chat/action.hbs index 51840363..d9ceb417 100644 --- a/templates/ui/chat/action.hbs +++ b/templates/ui/chat/action.hbs @@ -16,10 +16,10 @@ {{#each action.summon}}
- - + +
- # {{this.rolledCount}} + # {{this.quantity}}
{{/each}} From 02a73d774a916f20b6816590fbc8680dcd60b032 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Mon, 13 Jul 2026 18:31:42 -0400 Subject: [PATCH 18/30] Add guard for null placedData (#2087) --- module/documents/tokenManager.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/documents/tokenManager.mjs b/module/documents/tokenManager.mjs index 062a22ee..b21b56a0 100644 --- a/module/documents/tokenManager.mjs +++ b/module/documents/tokenManager.mjs @@ -31,7 +31,7 @@ export default class DhTokenManager { { create: false } ); - return placedData[0] ?? null; + return placedData?.[0] ?? null; } /** From 3a5529f1dc8730c217f5fa3cf033974775de556f Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Mon, 13 Jul 2026 18:56:34 -0400 Subject: [PATCH 19/30] Cleanup secret block styling (#2088) --- styles/less/global/elements.less | 39 ++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less index edc02f9a..1b7ed072 100755 --- a/styles/less/global/elements.less +++ b/styles/less/global/elements.less @@ -599,27 +599,23 @@ } secret-block { - position: relative; - section.secret { - background-color: @red-10; - padding: 0; - margin-top: 0.375rem; - &.revealed { - background-color: @green-10; - } - p { - margin: 0.5rem 0; - } - } + display: block; + + /** A buffer to make the hover behavior work a bit better. The bottom in the button needs to compensate */ + @buffer: 8px; + margin-top: -@buffer; + padding-top: @buffer; + button.reveal { - --button-size: 0.875rem; + --button-size: 1rem; + height: var(--button-size); position: absolute; margin: auto; left: 0; right: 0; width: min-content; padding: 1px 8px 0 8px; - bottom: calc(100% - 0.4375rem - 2px); + bottom: calc(100% - 0.4375rem - 1px); background-color: var(--dh-window-button-color-bg); // todo: find a better var name border-color: var(--color-secret-border); @@ -627,6 +623,7 @@ font-size: var(--font-size-10); user-select: none; text-transform: uppercase; + white-space: nowrap; visibility: hidden; } @@ -635,6 +632,20 @@ visibility: visible; } } + + /** + * The element inside a secret-block. + * This is separate since during prosemirror editing, the secret-block container does not exist. + */ + section.secret { + --color-secret-bg: @red-10; + --color-revealed-bg: @green-10; + position: relative; + padding: 0; + p { + margin: 0.5rem 0; + } + } } .system-daggerheart { From 4974df16d071f67667df972d9fd27e0c4fee34e1 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Tue, 14 Jul 2026 08:35:02 -0400 Subject: [PATCH 20/30] Preserve description expand state on re-render (#2089) --- module/applications/dialogs/deathMove.mjs | 3 -- module/applications/dialogs/downtime.mjs | 6 +--- module/data/fields/actionField.mjs | 5 +-- module/documents/chatMessage.mjs | 42 ++++++++++++++--------- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/module/applications/dialogs/deathMove.mjs b/module/applications/dialogs/deathMove.mjs index 8e0ed6af..cfd7687b 100644 --- a/module/applications/dialogs/deathMove.mjs +++ b/module/applications/dialogs/deathMove.mjs @@ -185,8 +185,6 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV if (result === undefined) return; - const autoExpandDescription = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance) - .expandRollMessage?.desc; const cls = getDocumentClass('ChatMessage'); const msg = { @@ -202,7 +200,6 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV img: this.selectedMove.img, description: game.i18n.localize(this.selectedMove.description), result: result, - open: autoExpandDescription ? 'open' : '', showRiskItAllButton: this.showRiskItAllButton, riskItAllButtonLabel: this.riskItAllButtonLabel, riskItAllHope: this.riskItAllHope diff --git a/module/applications/dialogs/downtime.mjs b/module/applications/dialogs/downtime.mjs index e209cc3b..5ba8e48e 100644 --- a/module/applications/dialogs/downtime.mjs +++ b/module/applications/dialogs/downtime.mjs @@ -196,9 +196,6 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV .filter(x => x.testUserPermission(game.user, 'LIMITED')) .filter(x => x.uuid !== this.actor.uuid); - const autoExpandDescription = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance) - .expandRollMessage?.desc; - const cls = getDocumentClass('ChatMessage'); const msg = { user: game.user.id, @@ -219,8 +216,7 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV actor: { name: this.actor.name, img: this.actor.img }, moves: moves, characters: characters, - selfId: this.actor.uuid, - open: autoExpandDescription ? 'open' : '' + selfId: this.actor.uuid } ), flags: { diff --git a/module/data/fields/actionField.mjs b/module/data/fields/actionField.mjs index af8f338e..ba2fa37e 100644 --- a/module/data/fields/actionField.mjs +++ b/module/data/fields/actionField.mjs @@ -270,9 +270,6 @@ export function ActionMixin(Base) { } async toChat(origin, config) { - const autoExpandDescription = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance) - .expandRollMessage?.desc; - const cls = getDocumentClass('ChatMessage'); const systemData = { title: game.i18n.localize('DAGGERHEART.CONFIG.FeatureForm.action'), @@ -307,7 +304,7 @@ export function ActionMixin(Base) { system: systemData, content: await foundry.applications.handlebars.renderTemplate( 'systems/daggerheart/templates/ui/chat/action.hbs', - { ...systemData, open: autoExpandDescription ? 'open' : '' } + systemData ), flags: { daggerheart: { diff --git a/module/documents/chatMessage.mjs b/module/documents/chatMessage.mjs index fd68997c..d53a76bd 100644 --- a/module/documents/chatMessage.mjs +++ b/module/documents/chatMessage.mjs @@ -3,6 +3,13 @@ import { emitGMUpdate, emitGMCreate, GMUpdateEvent } from '../systemRegistration export default class DhpChatMessage extends foundry.documents.ChatMessage { targetHook = null; + static #EXPAND_SECTIONS = [ + { selector: 'roll-section [data-action="expandRoll"]', key: 'roll' }, + { selector: 'damage-section', key: 'damage' }, + { selector: 'target-section', key: 'target' }, + { selector: 'description-section', key: 'desc' } + ]; + async renderHTML() { const actor = game.actors.get(this.speaker.actor); const actorData = @@ -89,23 +96,26 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { } } + // Check registered selectors and the main item section for expanding + // Preserving during re-render is handled by core foundry on anything with [data-action=expandRoll] const autoExpandRoll = game.settings.get( - CONFIG.DH.id, - CONFIG.DH.SETTINGS.gameSettings.appearance - ).expandRollMessage, - rollSections = html.querySelectorAll('.roll-part'), - itemDesc = html.querySelector('.domain-card-move'); - rollSections.forEach(s => { - if (s.classList.contains('roll-section')) { - const toExpand = s.querySelector('[data-action="expandRoll"]'); - toExpand.classList.toggle('expanded', autoExpandRoll.roll); - } else if (s.classList.contains('damage-section')) - s.classList.toggle('expanded', autoExpandRoll.damage); - else if (s.classList.contains('target-section')) s.classList.toggle('expanded', autoExpandRoll.target); - else if (s.classList.contains('description-section')) - s.classList.toggle('expanded', autoExpandRoll.desc); - }); - if (itemDesc && autoExpandRoll.desc) itemDesc.setAttribute('open', ''); + CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance + ).expandRollMessage; + for (const { selector, key } of DhpChatMessage.#EXPAND_SECTIONS) { + const elements = html.querySelectorAll(selector); + for (const element of elements) { + element.classList.toggle('expanded', autoExpandRoll[key]); + } + } + + // Auto expand the item description. These are not preserved by foundry during re-renders + const itemDesc = html.querySelector('details'); + if (itemDesc) { + const existing = document.querySelector(`.chat-message[data-message-id="${this.id}"] details`); + if (existing?.hasAttribute('open') ?? autoExpandRoll.desc) { + itemDesc.setAttribute('open', ''); + } + } } if (!this.isAuthor && !this.speakerActor?.isOwner) { From 0c2d25787182f6ae9145ae0f8e4ba7592320e793 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 14 Jul 2026 14:36:56 +0200 Subject: [PATCH 21/30] Raised version --- system.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system.json b/system.json index 37242137..93b1dea7 100644 --- a/system.json +++ b/system.json @@ -2,7 +2,7 @@ "id": "daggerheart", "title": "Daggerheart", "description": "An unofficial implementation of the Daggerheart system", - "version": "2.5.3", + "version": "2.5.4", "compatibility": { "minimum": "14.364", "verified": "14.364", @@ -10,7 +10,7 @@ }, "url": "https://github.com/Foundryborne/daggerheart", "manifest": "https://raw.githubusercontent.com/Foundryborne/daggerheart/v14/system.json", - "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.5.3/system.zip", + "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.5.4/system.zip", "authors": [ { "name": "WBHarry" From 79d652261459eeed358ff597154e36c7e8fa9cd6 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Tue, 14 Jul 2026 08:39:53 -0400 Subject: [PATCH 22/30] [Feature] Add support for GM Notes (#2082) * Add support for GM Notes * Localize GM Notes header label * Fix active editor height and menu auto sizing * Add tooltip to add gm note button --- daggerheart.mjs | 4 + lang/en.json | 6 ++ module/applications/sheets/api/base-item.mjs | 52 ++++++++++++- module/data/item/base.mjs | 23 ++++-- styles/less/global/elements.less | 36 ++++++++- styles/less/global/feature-section.less | 2 +- styles/less/global/global.less | 4 + styles/less/global/item-header.less | 35 +++++---- styles/less/global/tab-description.less | 77 ++++++++++++++++++- .../sheets/actors/actor-sheet-shared.less | 4 - styles/less/sheets/items/beastform.less | 9 +++ styles/less/sheets/items/feature.less | 10 +-- styles/less/sheets/items/index.less | 4 +- .../less/sheets/items/item-sheet-shared.less | 20 ++++- styles/less/utils/mixin.less | 2 +- system.json | 20 ++--- .../sheets/global/tabs/tab-description.hbs | 34 +++++--- 17 files changed, 278 insertions(+), 64 deletions(-) diff --git a/daggerheart.mjs b/daggerheart.mjs index 63127aa4..f91eedbe 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -267,6 +267,10 @@ Hooks.on('i18nInit', () => { }); Hooks.on('setup', () => { + if (game.user.isGM) { + document.body.dataset.gm = true; + } + CONFIG.statusEffects = [ ...CONFIG.statusEffects.filter(x => !['dead', 'unconscious'].includes(x.id)), ...Object.values(SYSTEM.GENERAL.conditions()).map(x => ({ diff --git a/lang/en.json b/lang/en.json index 99b6baeb..508a771d 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2550,6 +2550,9 @@ }, "identifier": { "label": "Identifier" + }, + "gmNotes": { + "label": "GM Notes" } }, "Ancestry": { @@ -2565,6 +2568,9 @@ "severe": "Severe Threshold" } }, + "Base": { + "addGMNote": "Add GM Note" + }, "Beastform": { "FIELDS": { "beastformType": { "label": "Beastform Type" }, diff --git a/module/applications/sheets/api/base-item.mjs b/module/applications/sheets/api/base-item.mjs index 1e08fc05..70a6bcc6 100644 --- a/module/applications/sheets/api/base-item.mjs +++ b/module/applications/sheets/api/base-item.mjs @@ -30,7 +30,8 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { addFeature: DHBaseItemSheet.#addFeature, deleteFeature: DHBaseItemSheet.#deleteFeature, addResource: DHBaseItemSheet.#addResource, - removeResource: DHBaseItemSheet.#removeResource + removeResource: DHBaseItemSheet.#removeResource, + editGMNote: DHBaseItemSheet.#onEditGMNote }, dragDrop: [ { dragSelector: null, dropSelector: '.drop-section' }, @@ -76,10 +77,16 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { /**@inheritdoc */ async _preparePartContext(partId, context, options) { await super._preparePartContext(partId, context, options); + const TextEditor = foundry.applications.ux.TextEditor.implementation; switch (partId) { case 'description': - context.enrichedDescription = await this.document.system.getEnrichedDescription(); + context.enrichedDescription = await this.document.system.getEnrichedDescription({ gmNotes: false }); + context.enrichedGMNotes = await TextEditor.implementation.enrichHTML(this.item.system.gmNotes, { + relativeTo: this.item, + rollData: this.item.getRollData(), + secrets: this.item.isOwner + }) break; case 'effects': await this._prepareEffectsContext(context, options); @@ -331,4 +338,45 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { } } } + + /** + * Handles the Add GM Note button being pressed. This is only used when an item has no GM notes. + * Later edits to a GM note instead go through the normal editor toggle workflow. + * @this DHBaseItemSheet + */ + static #onEditGMNote() { + // Open the editor, which might be hidden. We remove the css class to hide temporarily + // so that menu auto resizing functions properly. + const editor = this.element.querySelector('prose-mirror[name="system.gmNotes"]'); + const wasHidden = editor.classList.contains('hide-if-inactive'); + editor.classList.remove('hide-if-inactive'); + editor.open = true; + window.setTimeout(() => { + if (wasHidden) editor.classList.add('hide-if-inactive'); + }, 0); + } + + /** @inheritdoc */ + async _onRender(context, options) { + await super._onRender(context, options); + + // Render an add gmnotes button if there are no set GM notes. + // We need to re-render on close since its possible to prosemirror to close *without* triggering a full re-render + if (game.user.isGM && !this.item.system.gmNotes) { + const description = this.element.querySelector('[name="system.description"]'); + const addButton = () => { + if (description.querySelector('[data-action=editGMNote]')) return; + + const button = document.createElement('button'); + button.type = 'button'; + button.classList.add('icon', 'toggle', 'fa-regular', 'fa-note-medical'); + button.dataset.action = 'editGMNote'; + button.dataset.tooltip = 'DAGGERHEART.ITEMS.Base.addGMNote'; + description.appendChild(button); + } + + addButton(); + description.addEventListener('close', () => addButton()); + } + } } diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs index 131ef10f..095ba8f2 100644 --- a/module/data/item/base.mjs +++ b/module/data/item/base.mjs @@ -49,7 +49,10 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { }) }; - if (this.metadata.hasDescription) schema.description = new fields.HTMLField({ required: true, nullable: true }); + if (this.metadata.hasDescription) { + schema.description = new fields.HTMLField({ required: true, nullable: true }); + schema.gmNotes = new fields.HTMLField({ required: true, nullable: true }); + } if (this.metadata.hasResource) { schema.resource = new fields.SchemaField( @@ -134,7 +137,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { /** * Augments the description for the item with type specific info to display. Implemented in applicable item subtypes. * @param {object} [options] - Options that modify the styling of the rendered template. { headerStyle: undefined|'none'|'large' } - * @returns {string} + * @returns {Promise<{ prefix: string | null; value: string | null; suffix: string | null }>} */ async getDescriptionData(_options) { return { prefix: null, value: this.description, suffix: null }; @@ -145,14 +148,24 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { * @param {object} [options] - Options that modify the styling of the rendered template. { headerStyle: undefined|'none'|'large' } * @returns {Promise} */ - async getEnrichedDescription() { + async getEnrichedDescription({ gmNotes = true } = {}) { if (!this.metadata.hasDescription) return ''; const { prefix, value, suffix } = await this.getDescriptionData(); - const fullDescription = [prefix, value, suffix].filter(p => !!p).join('\n
\n'); + let fullDescription = [prefix, value, suffix].filter(p => !!p).join('\n
\n'); + if (this.gmNotes && gmNotes) { + const gmNotesElement = document.createElement('section'); + gmNotesElement.classList.add('gm-notes-section'); + gmNotesElement.dataset.visibility = 'gm'; + const header = document.createElement('header'); + header.classList.add('gm-notes'); + header.textContent = _loc('DAGGERHEART.ITEMS.FIELDS.gmNotes.label'); + gmNotesElement.innerHTML = header.outerHTML + this.gmNotes; + fullDescription += gmNotesElement.outerHTML; + } return await foundry.applications.ux.TextEditor.implementation.enrichHTML(fullDescription, { - relativeTo: this, + relativeTo: this.parent, rollData: this.getRollData(), secrets: this.parent.isOwner }); diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less index 1b7ed072..d31e09b9 100755 --- a/styles/less/global/elements.less +++ b/styles/less/global/elements.less @@ -595,7 +595,37 @@ margin-top: 4px; color: light-dark(#14142599, #efe6d850); font-size: var(--font-size-12); - padding-left: 3px; + padding-left: 16px; + } + + section.gm-notes-section { + padding-bottom: var(--spacer-4); + header.gm-notes + p { + margin-top: 0; + } + } + + header.gm-notes { + position: relative; + display: flex; + gap: 6px; + align-items: center; + &::before, + &::after { + content: " "; + flex: 1; + border-bottom: 1px solid var(--color-dark-6); + } + &::before { + mask-image: linear-gradient(270deg, black 0%, black calc(100% - 10px), transparent 100%); + } + &::after { + mask-image: linear-gradient(270deg, transparent 0%, black 10px, black 100%); + } + margin-top: var(--spacer-8); + margin-bottom: var(--spacer-4); + font-size: var(--font-size-11); + text-transform: uppercase; } secret-block { @@ -866,4 +896,8 @@ right: 2px; } } + + .gm-notes { + font-style: italic; + } } diff --git a/styles/less/global/feature-section.less b/styles/less/global/feature-section.less index 2fd4e20f..ecfb4ff6 100644 --- a/styles/less/global/feature-section.less +++ b/styles/less/global/feature-section.less @@ -3,7 +3,7 @@ .sheet.daggerheart.dh-style.item { .tab.features { - padding: 0 10px; + padding: 7px 10px; overflow-y: auto; .feature-list { display: flex; diff --git a/styles/less/global/global.less b/styles/less/global/global.less index 19a9e519..2c44c94e 100644 --- a/styles/less/global/global.less +++ b/styles/less/global/global.less @@ -111,3 +111,7 @@ body.theme-light, .themed.theme-light { color-scheme: light; } + +body:not([data-gm=true]) [data-visibility="gm"] { + display: none; +} \ No newline at end of file diff --git a/styles/less/global/item-header.less b/styles/less/global/item-header.less index f47ca7dc..1a8d7fce 100755 --- a/styles/less/global/item-header.less +++ b/styles/less/global/item-header.less @@ -12,12 +12,14 @@ }); .application.sheet.daggerheart.dh-style { + --portrait-size: 150px; + .item-sheet-header { display: flex; .profile { - height: 150px; - width: 150px; + height: var(--portrait-size); + width: var(--portrait-size); object-fit: cover; border-right: 1px solid light-dark(@dark-blue, @golden); border-bottom: 1px solid light-dark(@dark-blue, @golden); @@ -34,19 +36,24 @@ text-align: center; width: 80%; - .item-name input[type='text'] { - font-size: var(--font-size-32); - height: 42px; - text-align: center; - width: 90%; - transition: all 0.3s ease; - outline: 2px solid transparent; - border: 1px solid transparent; + .item-name { + display: flex; + flex-direction: column; + margin: 10px 10px 0 10px; + input[type='text'] { + font-size: var(--font-size-30); + text-align: center; + width: 100%; + transition: all 0.3s ease; + outline: 2px solid transparent; + border: 1px solid transparent; + text-overflow: ellipsis; - &:hover[type='text'], - &:focus[type='text'] { - box-shadow: none; - outline: 2px solid light-dark(@dark-blue, @golden); + &:hover[type='text'], + &:focus[type='text'] { + box-shadow: none; + outline: 2px solid light-dark(@dark-blue, @golden); + } } } diff --git a/styles/less/global/tab-description.less b/styles/less/global/tab-description.less index 5c18e02b..e2869723 100644 --- a/styles/less/global/tab-description.less +++ b/styles/less/global/tab-description.less @@ -6,11 +6,80 @@ display: flex; flex-direction: column; flex: 1; - overflow-y: hidden !important; - padding-top: 10px; + overflow: hidden; + padding: 0; + margin: 0; - prose-mirror.active + .artist-attribution { - display: none; + .description-section { + flex: 1; + display: flex; + flex-direction: column; + overflow: auto; + padding: 12px 16px 4px 16px; + .with-scroll-shadows(); + prose-mirror { + button.toggle { + top: 0px; + right: 0; + } + button[data-action=editGMNote] { + right: calc(var(--button-size) + 4px); + } + &.inactive { + height: unset!important; + overflow: unset; + .editor-content { + position: relative; + overflow: unset; + + // Allows content links to peek out + margin-top: -4px; + padding: 4px 0 0 0; + } + } + &.active { + --min-height: 250px; + padding: 8px 0 0 16px; + button[data-action=editGMNote] { + display: none; + } + .editor-content { + padding-right: 16px; + padding-bottom: 4px; + } + } + } + /** Hide editors that are empty when inactive if we need them to be */ + prose-mirror.inactive.hide-if-inactive { + display: none; + } + &:has(prose-mirror.active) { + padding: 0; + } + /** Description should fill available room (with overriden exceptions) */ + prose-mirror[name="system.description"] { + flex: 1 0; + } + &:has(prose-mirror[name="system.gmNotes"]:not(.hide-if-inactive)) { + prose-mirror.inactive { + --min-height: 3rem; + &[name="system.description"] { + flex: 0 0; + } + &[name="system.gmNotes"] { + flex: 1 0; + } + } + } + } + + /** Hide other elements if an editor is open */ + &:has(prose-mirror.active) { + prose-mirror.inactive, + header.gm-notes, + .artist-attribution { + display: none; + } } } } diff --git a/styles/less/sheets/actors/actor-sheet-shared.less b/styles/less/sheets/actors/actor-sheet-shared.less index 3e233013..a464d7a1 100644 --- a/styles/less/sheets/actors/actor-sheet-shared.less +++ b/styles/less/sheets/actors/actor-sheet-shared.less @@ -93,10 +93,6 @@ padding: 8px 0 0 16px; } } - - .artist-attribution { - padding-left: 16px; - } } .search-section { diff --git a/styles/less/sheets/items/beastform.less b/styles/less/sheets/items/beastform.less index 100b024a..017c4ef0 100644 --- a/styles/less/sheets/items/beastform.less +++ b/styles/less/sheets/items/beastform.less @@ -1,4 +1,6 @@ .application.sheet.daggerheart.dh-style.beastform { + --portrait-size: 130px; + .settings.tab { .advantage-on-section { display: flex; @@ -9,4 +11,11 @@ font-style: italic; } } + .tab.features.active { + display: flex; + flex-direction: column; + gap: 10px; + padding: 8px calc(12px - var(--scrollbar-width)) 4px 12px; + .stable-scroll-container(); + } } diff --git a/styles/less/sheets/items/feature.less b/styles/less/sheets/items/feature.less index f3c7cd49..9166fac1 100644 --- a/styles/less/sheets/items/feature.less +++ b/styles/less/sheets/items/feature.less @@ -2,17 +2,9 @@ @import '../../utils/fonts.less'; .application.sheet.daggerheart.dh-style.feature { - .item-sheet-header { - display: flex; - - .profile { - height: 130px; - width: 130px; - } - } + --portrait-size: 130px; section.tab { - height: 400px; overflow-y: auto; } } diff --git a/styles/less/sheets/items/index.less b/styles/less/sheets/items/index.less index 7c40a2e3..7f9bb684 100644 --- a/styles/less/sheets/items/index.less +++ b/styles/less/sheets/items/index.less @@ -1,6 +1,6 @@ +@import './item-sheet-shared.less'; @import './beastform.less'; @import './class.less'; @import './domain-card.less'; @import './feature.less'; -@import './heritage.less'; -@import './item-sheet-shared.less'; \ No newline at end of file +@import './heritage.less'; \ No newline at end of file diff --git a/styles/less/sheets/items/item-sheet-shared.less b/styles/less/sheets/items/item-sheet-shared.less index 5155ad70..63846b8e 100644 --- a/styles/less/sheets/items/item-sheet-shared.less +++ b/styles/less/sheets/items/item-sheet-shared.less @@ -1,4 +1,4 @@ -.application.sheet.daggerheart.dh-style.item { +.item.daggerheart.dh-style:where(.application.sheet) { &.minimized { .attribution-header-label { display: none; @@ -14,4 +14,22 @@ button.plain.inline-control { flex: 0 0 auto; } + + .tab-navigation { + margin-bottom: 0; + } + + /** Default tab stylings */ + .tab.active { + padding-top: 8px; + .with-scroll-shadows(); + + &.effects { + display: flex; + flex-direction: column; + gap: 10px; + padding: 8px calc(12px - var(--scrollbar-width)) 4px 12px; + .stable-scroll-container(); + } + } } diff --git a/styles/less/utils/mixin.less b/styles/less/utils/mixin.less index fb70d0a3..429fb3ef 100644 --- a/styles/less/utils/mixin.less +++ b/styles/less/utils/mixin.less @@ -226,7 +226,7 @@ ul, ol { - margin: 1rem 0; + margin: 0.5rem 0; padding: 0 0 0 1.25rem; li { diff --git a/system.json b/system.json index 93b1dea7..214ab2ee 100644 --- a/system.json +++ b/system.json @@ -256,34 +256,34 @@ }, "Item": { "ancestry": { - "htmlFields": ["description"] + "htmlFields": ["description", "gmNotes"] }, "community": { - "htmlFields": ["description"] + "htmlFields": ["description", "gmNotes"] }, "class": { - "htmlFields": ["description"] + "htmlFields": ["description", "gmNotes"] }, "subclass": { - "htmlFields": ["description"] + "htmlFields": ["description", "gmNotes"] }, "feature": { - "htmlFields": ["description"] + "htmlFields": ["description", "gmNotes"] }, "domainCard": { - "htmlFields": ["description"] + "htmlFields": ["description", "gmNotes"] }, "loot": { - "htmlFields": ["description"] + "htmlFields": ["description", "gmNotes"] }, "consumable": { - "htmlFields": ["description"] + "htmlFields": ["description", "gmNotes"] }, "weapon": { - "htmlFields": ["description"] + "htmlFields": ["description", "gmNotes"] }, "armor": { - "htmlFields": ["description"] + "htmlFields": ["description", "gmNotes"] }, "beastform": {} }, diff --git a/templates/sheets/global/tabs/tab-description.hbs b/templates/sheets/global/tabs/tab-description.hbs index 71995a51..3fdf1a93 100755 --- a/templates/sheets/global/tabs/tab-description.hbs +++ b/templates/sheets/global/tabs/tab-description.hbs @@ -1,11 +1,25 @@ -
- {{formInput systemFields.description value=document.system.description enriched=enrichedDescription toggled=true}} - - {{#if (and showAttribution document.system.attribution.artist)}} - - {{/if}} +
+
+ {{formInput systemFields.description value=document.system.description enriched=enrichedDescription toggled=true}} + {{#if (and systemFields.gmNotes @root.user.isGM)}} +
+ {{#if enrichedGMNotes}} +
{{localize "DAGGERHEART.ITEMS.FIELDS.gmNotes.label"}}
+ {{/if}} + {{{enrichedGMNotes}}} +
+ {{/if}} +
+ {{#if (and showAttribution document.system.attribution.artist)}} + + {{/if}}
\ No newline at end of file From d76b4bb707fbfad3412c3a14882eeb7f4c4bcf64 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Sat, 18 Jul 2026 05:42:50 -0400 Subject: [PATCH 23/30] Fix chat being jumpy as timestamps get longer (#2095) --- styles/less/global/chat.less | 87 +++++++++++++++++++----------- templates/ui/chat/chat-message.hbs | 41 +++++++------- 2 files changed, 79 insertions(+), 49 deletions(-) diff --git a/styles/less/global/chat.less b/styles/less/global/chat.less index b9478ea4..4e29cfff 100644 --- a/styles/less/global/chat.less +++ b/styles/less/global/chat.less @@ -7,12 +7,12 @@ .chat-log .chat-message { background-image: url('../assets/parchments/dh-parchment-light.png'); - .message-header .message-header-metadata .message-metadata, - .message-header .message-header-main .message-sub-header-container { + .message-header .message-header-main .message-metadata, + .message-header .message-header-main .name { color: @dark; } - .message-header .message-header-main .message-sub-header-container h4 { + .message-header .message-header-main h4 { color: @dark-blue; } @@ -42,27 +42,12 @@ .message-header { display: flex; - gap: 4px; + gap: 8px; padding: 8px; + align-items: center; - .message-header-metadata { - flex: none; - display: flex; - flex-direction: column; - - .message-metadata { - font-family: @font-body; - color: @beige; - } - } - - .message-header-main { - display: flex; - align-items: center; - gap: 8px; - flex: 1; - overflow: hidden; - + .portrait { + flex: 0 0 auto; .actor-img { border-radius: 50%; width: 40px; @@ -70,20 +55,60 @@ object-fit: cover; object-position: top center; } + } - .message-sub-header-container { + .message-header-main { + display: flex; + align-items: center; + column-gap: 4px; + row-gap: 2px; + flex: 1; + overflow: hidden; + + display: grid; + grid-template: + "title metadata" + "subtitle subtitle"; + + h4 { + font-size: var(--font-size-16); + font-weight: bold; + margin-bottom: 0; + font-family: @font-subtitle; + color: @golden; + white-space: nowrap; + text-overflow: ellipsis; + flex: 1; + overflow: hidden; + align-self: flex-end; + } + + .message-metadata { + font-family: @font-body; + color: @beige; + white-space: nowrap; + align-items: baseline; + .message-timestamp { + font-size: var(--font-size-11); + } + } + + .subtitle { + grid-area: subtitle; flex: 1; display: flex; - flex-direction: column; justify-content: space-between; color: @beige; - - h4 { - font-size: var(--font-size-16); - font-weight: bold; - margin-bottom: 0; - font-family: @font-subtitle; - color: @golden; + gap: 4px; + align-items: baseline; + line-height: 1; + .name { + flex: 1; + } + .whisper-to { + color: @color-text-subtle; + flex: 0; + white-space: nowrap; } } } diff --git a/templates/ui/chat/chat-message.hbs b/templates/ui/chat/chat-message.hbs index 87ecce39..92490cec 100644 --- a/templates/ui/chat/chat-message.hbs +++ b/templates/ui/chat/chat-message.hbs @@ -1,23 +1,18 @@
  • -
    +
    -
    - {{#if message.title}} -

    {{message.title}}

    -
    {{alias}} {{#if author.isGM}}(GM){{/if}}
    - {{else}} - {{#unless actor.name}} -

    {{author.name}}

    - {{else}} -

    {{alias}}

    -
    {{author.name}}
    - {{/unless}} - {{/if}} -
    -
    From c181a47cc2b5ff421ff0758fc450e3274fbb4f1e Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Sat, 18 Jul 2026 12:28:42 -0400 Subject: [PATCH 24/30] Swap item macro tooltip with system item tooltips (#2092) --- module/documents/tooltipManager.mjs | 369 +++++++++++++++------------- 1 file changed, 202 insertions(+), 167 deletions(-) diff --git a/module/documents/tooltipManager.mjs b/module/documents/tooltipManager.mjs index 3e3f4a16..6e794b6a 100644 --- a/module/documents/tooltipManager.mjs +++ b/module/documents/tooltipManager.mjs @@ -4,199 +4,234 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti #wide = false; #bordered = false; + /** @inheritdoc */ async activate(element, options = {}) { - const { TextEditor } = foundry.applications.ux; + this.#wide = false; + this.#bordered = false; - let html = options.html; - if (element.dataset.tooltip?.startsWith('#battlepoints#')) { - this.#wide = true; - this.#bordered = true; - - html = await this.getBattlepointHTML(element.dataset.combatId); - options.direction = this._determineItemTooltipDirection(element); - super.activate(element, { ...options, html: html }); - - const lockedTooltip = this.lockTooltip(); - lockedTooltip.querySelectorAll('.battlepoint-toggle-container input').forEach(element => { - element.addEventListener('input', this.toggleModifier.bind(this)); - }); - return; - } else { - this.#wide = false; - this.#bordered = false; + const isMacro = document.getElementById('action-bar').contains(element); + const macro = isMacro ? game.macros.get(game.user.hotbar[Number(element.dataset.slot)] ?? null) : null; + const macroItemUuid = macro?.type === 'script' ? macro.command.match(/await game\.system\.api\.applications\.ui\.DhHotbar\.useItem\("([^"]+)"\);/)?.[1] : null; + if (macroItemUuid && await fromUuid(macroItemUuid, { strict: false })) { + element.dataset.tooltip = `#item#${macroItemUuid}`; + options.direction = this.constructor.TOOLTIP_DIRECTIONS.UP; } - if (element.dataset.tooltip === '#effect-display#') { - this.#bordered = true; - let effect = {}; - if (element.dataset.uuid) { - const effectItem = await foundry.utils.fromUuid(element.dataset.uuid); - const effectData = effectItem.toObject(); + let html = options.html; + const key = element.dataset.tooltip?.match(/^#([\w-]+)#/)?.[1]; + switch (key) { + case 'battlepoints': + return this.#activateBattlepoints(element, options); + case 'effect-display': + html = await this.#activateEffectDisplay(element, options); + break; + case 'item': + html = await this.#activateItem(element, options); + break; + case 'attack': + html = await this.#activateAttack(element, options); + break; + case 'shortRest': + case 'longRest': + html = await this.#activateRest(element, options); + break; + case 'advantage': + case 'disadvantage': + html = await this.#activateAdvantageDisadvantage(element, options); + break; + case 'deathMove': + html = await this.#activateDeathMove(element, options); + break; + } - effect = { - ...effectData, - name: game.i18n.localize(effectData.name) + this.noOffset = options.noOffset; + super.activate(element, { ...options, html }); + } + + async #activateBattlepoints(element, options) { + this.#wide = true; + this.#bordered = true; + + const html = await this.getBattlepointHTML(element.dataset.combatId); + options.direction = this._determineItemTooltipDirection(element); + super.activate(element, { ...options, html: html }); + + const lockedTooltip = this.lockTooltip(); + lockedTooltip.querySelectorAll('.battlepoint-toggle-container input').forEach(element => { + element.addEventListener('input', this.toggleModifier.bind(this)); + }); + } + + async #activateEffectDisplay(element, options) { + this.#bordered = true; + let effect = {}; + if (element.dataset.uuid) { + const effectItem = await foundry.utils.fromUuid(element.dataset.uuid); + const effectData = effectItem.toObject(); + + effect = { + ...effectData, + name: game.i18n.localize(effectData.name) + }; + + if (effectData.type === 'beastform') { + const beastformData = { + features: [], + advantageOn: effectData.system.advantageOn, + beastformAttackData: game.system.api.data.items.DHBeastform.getBeastformAttackData(effectItem) }; - if (effectData.type === 'beastform') { - const beastformData = { - features: [], - advantageOn: effectData.system.advantageOn, - beastformAttackData: game.system.api.data.items.DHBeastform.getBeastformAttackData(effectItem) - }; - - const features = effectItem.parent.items.filter(x => effectItem.system.featureIds.includes(x.id)); - for (const feature of features) { - const featureData = feature.toObject(); - featureData.enrichedDescription = await feature.system.getEnrichedDescription(); - beastformData.features.push(featureData); - } - - effect.description = await foundry.applications.handlebars.renderTemplate( - 'systems/daggerheart/templates/ui/tooltip/parts/beastformData.hbs', - { - item: { system: beastformData } - } - ); - } else { - effect.description = game.i18n.localize( - effectData.description ?? effectData.parent.system.description - ); + const features = effectItem.parent.items.filter(x => effectItem.system.featureIds.includes(x.id)); + for (const feature of features) { + const featureData = feature.toObject(); + featureData.enrichedDescription = await feature.system.getEnrichedDescription(); + beastformData.features.push(featureData); } - } else { - const conditions = CONFIG.DH.GENERAL.conditions(); - const condition = conditions[element.dataset.condition]; - effect = { - ...condition, - name: game.i18n.localize(condition.name), - description: game.i18n.localize(condition.description), - appliedBy: element.dataset.appliedBy, - isLockedCondition: true - }; - } - html = await foundry.applications.handlebars.renderTemplate( - `systems/daggerheart/templates/ui/tooltip/effect-display.hbs`, + effect.description = await foundry.applications.handlebars.renderTemplate( + 'systems/daggerheart/templates/ui/tooltip/parts/beastformData.hbs', + { + item: { system: beastformData } + } + ); + } else { + effect.description = game.i18n.localize( + effectData.description ?? effectData.parent.system.description + ); + } + } else { + const conditions = CONFIG.DH.GENERAL.conditions(); + const condition = conditions[element.dataset.condition]; + effect = { + ...condition, + name: game.i18n.localize(condition.name), + description: game.i18n.localize(condition.description), + appliedBy: element.dataset.appliedBy, + isLockedCondition: true + }; + } + + const html = await foundry.applications.handlebars.renderTemplate( + `systems/daggerheart/templates/ui/tooltip/effect-display.hbs`, + { + effect + } + ); + + this.tooltip.innerHTML = html; + options.direction = this._determineItemTooltipDirection(element); + + return html; + } + + async #activateItem(element, options) { + const itemUuid = element.dataset.tooltip.slice(6); + const item = await foundry.utils.fromUuid(itemUuid); + if (item) { + const isAction = item instanceof game.system.api.models.actions.actionsTypes.base; + const isEffect = item instanceof ActiveEffect; + await this.enrichText(item); + + const type = isAction ? 'action' : isEffect ? 'effect' : item.type; + const html = await foundry.applications.handlebars.renderTemplate( + `systems/daggerheart/templates/ui/tooltip/${type}.hbs`, { - effect + item: item, + description: item.system?.enrichedDescription ?? item.enrichedDescription, + config: CONFIG.DH, + allDomains: CONFIG.DH.DOMAIN.allDomains() } ); this.tooltip.innerHTML = html; - options.direction = this._determineItemTooltipDirection(element); - } else { - this.#bordered = false; + options.direction ??= this._determineItemTooltipDirection(element); + return html; } - if (element.dataset.tooltip?.startsWith('#item#')) { - const itemUuid = element.dataset.tooltip.slice(6); - const item = await foundry.utils.fromUuid(itemUuid); - if (item) { - const isAction = item instanceof game.system.api.models.actions.actionsTypes.base; - const isEffect = item instanceof ActiveEffect; - await this.enrichText(item); + return null; + } - const type = isAction ? 'action' : isEffect ? 'effect' : item.type; - html = await foundry.applications.handlebars.renderTemplate( - `systems/daggerheart/templates/ui/tooltip/${type}.hbs`, - { - item: item, - description: item.system?.enrichedDescription ?? item.enrichedDescription, - config: CONFIG.DH, - allDomains: CONFIG.DH.DOMAIN.allDomains() - } - ); + async #activateAttack(element, options) { + const actorUuid = element.dataset.tooltip.slice(8); + const actor = await foundry.utils.fromUuid(actorUuid); + const attack = actor.system.attack; - this.tooltip.innerHTML = html; - options.direction = this._determineItemTooltipDirection(element); + const description = await foundry.applications.ux.TextEditor.enrichHTML(attack.description); + const html = await foundry.applications.handlebars.renderTemplate( + `systems/daggerheart/templates/ui/tooltip/attack.hbs`, + { + attack: attack, + description: description, + parent: actor, + config: CONFIG.DH } - } else { - const attack = element.dataset.tooltip?.startsWith('#attack#'); - if (attack) { - const actorUuid = element.dataset.tooltip.slice(8); - const actor = await foundry.utils.fromUuid(actorUuid); - const attack = actor.system.attack; + ); - const description = await TextEditor.enrichHTML(attack.description); - html = await foundry.applications.handlebars.renderTemplate( - `systems/daggerheart/templates/ui/tooltip/attack.hbs`, - { - attack: attack, - description: description, - parent: actor, - config: CONFIG.DH - } - ); + this.tooltip.innerHTML = html; + return html; + } - this.tooltip.innerHTML = html; - } + async #activateAdvantageDisadvantage(element, options) { + const isAdvantage = element.dataset.tooltip?.startsWith('#advantage#'); + const actorUuid = element.dataset.tooltip.slice(isAdvantage ? 11 : 14); + const actor = await foundry.utils.fromUuid(actorUuid); - const shortRest = element.dataset.tooltip?.startsWith('#shortRest#'); - const longRest = element.dataset.tooltip?.startsWith('#longRest#'); - if (shortRest || longRest) { - const key = element.dataset.tooltip.slice(shortRest ? 11 : 10); - - const moves = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).restMoves[ - element.dataset.restType - ].moves; - const move = moves[key]; - const description = await TextEditor.enrichHTML(move.description); - html = await foundry.applications.handlebars.renderTemplate( - `systems/daggerheart/templates/ui/tooltip/downtime.hbs`, - { - move: move, - description: description - } - ); - - this.tooltip.innerHTML = html; - options.direction = this._determineItemTooltipDirection( - element, - this.constructor.TOOLTIP_DIRECTIONS.RIGHT - ); - } - - const isAdvantage = element.dataset.tooltip?.startsWith('#advantage#'); - const isDisadvantage = element.dataset.tooltip?.startsWith('#disadvantage#'); - if (isAdvantage || isDisadvantage) { - const actorUuid = element.dataset.tooltip.slice(isAdvantage ? 11 : 14); - const actor = await foundry.utils.fromUuid(actorUuid); - - if (actor) { - html = await foundry.applications.handlebars.renderTemplate( - `systems/daggerheart/templates/ui/tooltip/advantage.hbs`, - { - sources: isAdvantage ? actor.system.advantageSources : actor.system.disadvantageSources - } - ); - - this.tooltip.innerHTML = html; + if (actor) { + const html = await foundry.applications.handlebars.renderTemplate( + `systems/daggerheart/templates/ui/tooltip/advantage.hbs`, + { + sources: isAdvantage ? actor.system.advantageSources : actor.system.disadvantageSources } - } + ); - const deathMove = element.dataset.tooltip?.startsWith('#deathMove#'); - if (deathMove) { - const name = element.dataset.deathName; - const img = element.dataset.deathImg; - const description = element.dataset.deathDescription; - - html = await foundry.applications.handlebars.renderTemplate( - `systems/daggerheart/templates/ui/tooltip/death-move.hbs`, - { - move: { name: name, img: img, description: description } - } - ); - - this.tooltip.innerHTML = html; - options.direction = this._determineItemTooltipDirection( - element, - this.constructor.TOOLTIP_DIRECTIONS.RIGHT - ); - } + this.tooltip.innerHTML = html; + return html; } + return null; + } - this.noOffset = options.noOffset; - super.activate(element, { ...options, html: html }); + async #activateDeathMove(element, options) { + const name = element.dataset.deathName; + const img = element.dataset.deathImg; + const description = element.dataset.deathDescription; + + const html = await foundry.applications.handlebars.renderTemplate( + `systems/daggerheart/templates/ui/tooltip/death-move.hbs`, + { + move: { name: name, img: img, description: description } + } + ); + + this.tooltip.innerHTML = html; + options.direction = this._determineItemTooltipDirection( + element, + this.constructor.TOOLTIP_DIRECTIONS.RIGHT + ); + return html; + } + + async #activateRest(element, options) { + const isShortRest = element.dataset.tooltip?.startsWith('#shortRest#'); + const key = element.dataset.tooltip.slice(isShortRest ? 11 : 10); + const moves = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).restMoves[ + element.dataset.restType + ].moves; + const move = moves[key]; + const description = await foundry.applications.ux.TextEditor.enrichHTML(move.description); + const html = await foundry.applications.handlebars.renderTemplate( + `systems/daggerheart/templates/ui/tooltip/downtime.hbs`, + { + move: move, + description: description + } + ); + + this.tooltip.innerHTML = html; + options.direction = this._determineItemTooltipDirection( + element, + this.constructor.TOOLTIP_DIRECTIONS.RIGHT + ); + return html; } _setAnchor(direction) { From 4b651836ffb2ee6436258b6e12e33bf26b831de3 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Sat, 18 Jul 2026 12:33:19 -0400 Subject: [PATCH 25/30] Start supporting flat damage without custom formulas (#2077) --- module/data/fields/action/damageField.mjs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs index 9b21d3ba..2c6c7b30 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -275,10 +275,18 @@ export class DHActionDiceData extends foundry.abstract.DataModel { }; } + /** + * @returns {string} the formula associated with this damage field + */ getFormula() { - 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}`; + if (this.custom.enabled) return this.custom.formula; + + const multiplier = this.multiplier === 'flat' ? this.flatMultiplier : `@${this.multiplier}`; + if (!multiplier) return String(this.bonus || 0); + + const dice = `${multiplier ?? 1}${this.dice}`; + const sign = this.bonus < 0 ? ' - ' : ' + '; + return this.bonus ? `${dice} ${sign} ${Math.abs(this.bonus)}` : dice; } } From effab8db0cc1815ca552a3eafb3247c785c7e037 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 19 Jul 2026 03:02:57 +0200 Subject: [PATCH 26/30] [Rework] ChatMessage Damage (#2079) * Initial rework * Removed unneeded method * Removed outcommented code * Added migration * Fixed DamageActions * Semi-corrected reroll of one of multiple results on a die * Code improvement * Added aseDie.rerollResult method to specifically reroll a specific result die in a grouping * TagTeamDialog somewhat working * Added migration for TagTeamData * Fix for mean tagTeamDialog.finish * . * Improved migration * Internalised ChatDamageData.prepareRolls to its own constructor * Moved DamageTypes to roll.options * TagTeamDialog fixes to accomodate new flat damage.types structure * Changed so all default rolls become BaseRolls to access convenience functions and getters * . * Fixed so that party.tagTeam now uses a declared damageRollData(ChatDamageData) field. rollData.options.damage is retired. * Critical damage fixes * Corrected TagTeamDialog rerender logic when selecting a roll * Fixed so that DamageTypes are retained and joined together throughout TagTeamDialog * Removed some remaining types.<>.roll references * [Rework] Damage and Damage Resource split (#2094) * Changed the label of ActionSheet damage.resources to Mark Resources * Fix assigning includeBase * Localize add resource * Maybe simplify API * Fix onRollSimple * Handle erroring action update differently --------- Co-authored-by: Carlos Fernandez --- daggerheart.mjs | 5 +- lang/en.json | 5 +- module/applications/dialogs/damageDialog.mjs | 12 +- module/applications/dialogs/tagTeamDialog.mjs | 219 ++++++++-------- .../sheets-configs/action-base-config.mjs | 99 ++++--- .../sheets-configs/adversary-settings.mjs | 35 ++- module/applications/ui/chatLog.mjs | 58 ++--- module/config/generalConfig.mjs | 2 +- module/data/action/attackAction.mjs | 21 +- module/data/action/baseAction.mjs | 34 ++- module/data/action/damageAction.mjs | 7 +- module/data/actor/adversary.mjs | 12 +- module/data/actor/character.mjs | 18 +- module/data/actor/tierAdjustment.mjs | 14 +- module/data/chat-message/actorRoll.mjs | 69 +++-- module/data/chat-message/chatDamageData.mjs | 58 +++++ module/data/fields/action/damageField.mjs | 78 +++--- module/data/fields/actionField.mjs | 1 + module/data/item/weapon.mjs | 23 +- module/data/tagTeamData.mjs | 3 + module/dice/baseRoll.mjs | 4 + module/dice/damageRoll.mjs | 242 ++++++------------ module/dice/dhRoll.mjs | 9 +- module/dice/die/_module.mjs | 1 + module/dice/die/advantageDie.mjs | 4 +- module/dice/die/baseDie.mjs | 12 + module/dice/die/disadvantageDie.mjs | 4 +- module/dice/die/dualityDie.mjs | 3 +- module/dice/dualityRoll.mjs | 4 - module/documents/actor.mjs | 130 +++++----- module/helpers/handlebarsHelper.mjs | 5 +- module/helpers/utils.mjs | 13 - .../migration-handlers/2_5_2.mjs | 3 +- .../migration-handlers/2_6_0.mjs | 20 ++ .../migration-handlers/base.mjs | 44 +++- module/systemRegistration/migrations.mjs | 4 +- .../less/dialog/damage-selection/sheet.less | 7 + templates/actionTypes/cost.hbs | 2 +- templates/actionTypes/damage.hbs | 194 +++++++------- templates/actionTypes/resource.hbs | 2 +- .../dialogs/dice-roll/damageSelection.hbs | 69 +++-- .../parts/tagTeamDamageParts.hbs | 51 ++-- templates/dialogs/tagTeamDialog/result.hbs | 8 +- .../dialogs/tagTeamDialog/tagTeamMember.hbs | 16 +- .../action-settings/effect.hbs | 2 +- .../adversary-settings/attack.hbs | 2 +- templates/sheets/items/weapon/settings.hbs | 40 +-- templates/ui/chat/parts/button-part.hbs | 8 +- templates/ui/chat/parts/damage-part.hbs | 115 +++++---- templates/ui/tooltip/attack.hbs | 2 +- templates/ui/tooltip/weapon.hbs | 2 +- 51 files changed, 1007 insertions(+), 788 deletions(-) create mode 100644 module/data/chat-message/chatDamageData.mjs create mode 100644 module/dice/die/baseDie.mjs create mode 100644 module/systemRegistration/migration-handlers/2_6_0.mjs diff --git a/daggerheart.mjs b/daggerheart.mjs index f91eedbe..2c51c1f6 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -6,6 +6,7 @@ import * as documents from './module/documents/_module.mjs'; import { macros } from './module/_module.mjs'; import * as collections from './module/documents/collections/_module.mjs'; import * as dice from './module/dice/_module.mjs'; +import * as die from './module/dice/die/_module.mjs'; import * as fields from './module/data/fields/_module.mjs'; import RegisterHandlebarsHelpers from './module/helpers/handlebarsHelper.mjs'; import { enricherConfig, enricherRenderSetup } from './module/enrichers/_module.mjs'; @@ -23,7 +24,7 @@ import TokenManager from './module/documents/tokenManager.mjs'; CONFIG.DH = SYSTEM; CONFIG.TextEditor.enrichers.push(...enricherConfig); -CONFIG.Dice.rolls = [BaseRoll, DHRoll, DualityRoll, D20Roll, DamageRoll, FateRoll]; +CONFIG.Dice.rolls = [Roll = BaseRoll, DHRoll, DualityRoll, D20Roll, DamageRoll, FateRoll]; CONFIG.Dice.daggerheart = { DHRoll: DHRoll, DualityRoll: DualityRoll, @@ -38,6 +39,8 @@ CONFIG.RegionBehavior.dataModels = { }; Object.assign(CONFIG.Dice.termTypes, dice.diceTypes); +CONFIG.Dice.terms.d = die.BaseDie; +CONFIG.Dice.types = [die.BaseDie, CONFIG.Dice.terms.f]; CONFIG.Actor.documentClass = documents.DhpActor; CONFIG.Actor.dataModels = models.actors.config; diff --git a/lang/en.json b/lang/en.json index 508a771d..b65cade7 100755 --- a/lang/en.json +++ b/lang/en.json @@ -104,8 +104,10 @@ "startCountdown": "Start Countdown" }, "damage": { + "addResource": "Add Resource", "multiplier": "Multiplier", - "flatMultiplier": "Flat Multiplier" + "flatMultiplier": "Flat Multiplier", + "markResources": "Mark Resources" }, "general": { "customFormula": "Custom Formula", @@ -2483,7 +2485,6 @@ "reroll": "Reroll", "rerolled": "Rerolled", "rerollThing": "Reroll {thing}", - "resource": "Resource", "result": { "single": "Result", "plural": "Results" diff --git a/module/applications/dialogs/damageDialog.mjs b/module/applications/dialogs/damageDialog.mjs index 46d3d41f..ce613ade 100644 --- a/module/applications/dialogs/damageDialog.mjs +++ b/module/applications/dialogs/damageDialog.mjs @@ -51,7 +51,11 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application const context = await super._prepareContext(_options); context.config = CONFIG.DH; context.title = this.config.title ?? this.title; - context.formula = this.roll.constructFormula(this.config); + + const { damageFormula, resourceFormulas } = this.roll.constructFormulas(this.config); + context.damageFormula = damageFormula; + context.resourceFormulas = resourceFormulas; + context.hasHealing = this.config.hasHealing; context.directDamage = this.config.directDamage; context.selectedMessageMode = this.config.selectedMessageMode; @@ -73,7 +77,11 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application static updateRollConfiguration(_event, _, formData) { const data = foundry.utils.expandObject(formData.object); - foundry.utils.mergeObject(this.config.roll, data.roll); + + if (this.config.damageFormula) + foundry.utils.mergeObject(this.config.damageFormula, data.damageFormula); + + foundry.utils.mergeObject(this.config.resourceFormulas, data.resourceFormulas); foundry.utils.mergeObject(this.config.modifiers, data.modifiers); this.config.selectedMessageMode = data.selectedMessageMode; diff --git a/module/applications/dialogs/tagTeamDialog.mjs b/module/applications/dialogs/tagTeamDialog.mjs index b33541a3..4c6c5339 100644 --- a/module/applications/dialogs/tagTeamDialog.mjs +++ b/module/applications/dialogs/tagTeamDialog.mjs @@ -1,4 +1,5 @@ import { ResourceUpdateMap } from '../../data/action/baseAction.mjs'; +import { ChatDamageData } from '../../data/chat-message/chatDamageData.mjs'; import { MemberData } from '../../data/tagTeamData.mjs'; import { getCritDamageBonus, shouldUseHopeFearAutomation } from '../../helpers/utils.mjs'; import { emitGMUpdate, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; @@ -140,7 +141,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio const hasRolled = Boolean(data.rollData); if (!hasRolled) return false; - return !data.rollData.options.hasDamage || Boolean(data.rollData.options.damage); + return !data.rollData.options.hasDamage || data.damageRollData.active; }); return context; @@ -181,7 +182,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio const selectedRoll = Object.values(this.party.system.tagTeam.members).find(member => member.selected); const critSelected = !selectedRoll ? undefined - : (selectedRoll?.rollData?.options?.roll?.isCritical ?? false); + : (selectedRoll?.roll?.isCritical ?? false); partContext.hintText = await this.getInfoTexts(this.party.system.tagTeam.members); partContext.joinedRoll = await this.getJoinedRoll({ @@ -235,8 +236,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio } const selectedRoll = Object.values(this.party.system.tagTeam.members).find(member => member.selected); - const critSelected = !selectedRoll ? undefined : (selectedRoll?.rollData?.options?.roll?.isCritical ?? false); - const damage = data.rollData?.options?.damage; + const critSelected = !selectedRoll ? undefined : (selectedRoll?.roll?.isCritical ?? false); return { ...data, @@ -247,9 +247,9 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio hasRolled: Boolean(data.rollData), rollOptions, damageRollOptions, - damage: damage, - critDamage: await this.getCriticalDamage(damage), - useCritDamage: critSelected || (critSelected === undefined && data.rollData?.options?.roll?.isCritical) + damage: data.damageRollData, + critDamage: await this.getCriticalDamage(data.damageRollData), + useCritDamage: critSelected || (critSelected === undefined && data.roll?.isCritical) }; } @@ -379,7 +379,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio let rollIsSelected = false; for (const member of Object.values(members)) { const rollFinished = Boolean(member.rollData); - const damageFinished = member.rollData?.options?.hasDamage ? Boolean(member.rollData.options.damage) : true; + const damageFinished = member.rollData?.options?.hasDamage ? member.damageRollData.active : true; rollsAreFinished = rollsAreFinished && rollFinished && damageFinished; rollIsSelected = rollIsSelected || member.selected; @@ -540,17 +540,10 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio await action.workflow.get('damage').execute(config, null, true); if (!config.damage) return; - - const current = this.party.system.tagTeam.members[memberKey].rollData; + await this.updatePartyData( { - [`system.tagTeam.members.${memberKey}.rollData`]: { - ...current, - options: { - ...current.options, - damage: config.damage - } - } + [`system.tagTeam.members.${memberKey}.damageRollData`]: config.damage }, this.getUpdatingParts(button) ); @@ -558,15 +551,11 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio static async #removeDamageRoll(_, button) { const { memberKey } = button.dataset; - const current = this.party.system.tagTeam.members[memberKey].rollData; this.updatePartyData( { - [`system.tagTeam.members.${memberKey}.rollData`]: { - ...current, - options: { - ...current.options, - damage: null - } + [`system.tagTeam.members.${memberKey}.damageRollData`]: { + main: null, + resources: _replace({}) } }, this.getUpdatingParts(button) @@ -574,89 +563,44 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio } static async #rerollDamageDice(_, button) { - const { memberKey, damageKey, part, dice } = button.dataset; + const { isResource, memberKey, damageKey, diceIndex, resultIndex } = button.dataset; const memberData = this.party.system.tagTeam.members[memberKey]; - const partData = memberData.rollData.options.damage[damageKey].parts[part]; - const activeDiceResultKey = Object.keys(partData.dice[dice].results).find( - index => partData.dice[dice].results[index].active - ); - const { parsedRoll, rerolledDice } = await game.system.api.dice.DamageRoll.reroll( - partData, - dice, - activeDiceResultKey - ); - - const rollData = this.party.system.tagTeam.members[memberKey].rollData; - rollData.options.damage[damageKey].parts = rollData.options.damage[damageKey].parts.map((damagePart, index) => { - if (index !== Number.parseInt(part)) return damagePart; - - return { - ...damagePart, - total: parsedRoll.total, - dice: rerolledDice - }; - }); - rollData.options.damage[damageKey].total = rollData.options.damage[damageKey].parts.reduce((acc, part) => { - acc += part.total; - return acc; - }, 0); + await memberData.damageRollData.rerollDamageDie(isResource, damageKey, diceIndex, resultIndex); + const basePath = `system.tagTeam.members.${memberKey}.damageRollData`; + const updatePath = isResource ? `${basePath}.resources.${damageKey}` : `${basePath}.main`; + const updateValue = isResource ? + memberData.damageRollData.resources[damageKey] : memberData.damageRollData.main; this.updatePartyData( { - [`system.tagTeam.members.${memberKey}.rollData`]: rollData + [updatePath]: updateValue.toJSON() }, this.getUpdatingParts(button) ); } - async getCriticalDamage(damage) { - const newDamage = foundry.utils.deepClone(damage); - for (let key in newDamage) { - var damage = newDamage[key]; - damage.formula = ''; - damage.total = 0; - - for (let part of damage.parts) { - const criticalDamage = await getCritDamageBonus(part.formula); - if (criticalDamage) { - part.modifierTotal += criticalDamage; - part.total += criticalDamage; - part.formula = `${part.dice.map(x => x.formula).join(' + ')} + ${part.modifierTotal}`; - part.roll = new Roll(part.formula); - } - - damage.formula = [damage.formula, part.formula].filter(x => x).join(' + '); - damage.total += part.total; + async getCriticalDamage(origDamage) { + const newDamage = origDamage ? ChatDamageData.fromJSON(JSON.stringify(origDamage)) : null; + if (newDamage?.main) { + const criticalDamage = await getCritDamageBonus(newDamage.main.formula); + if (criticalDamage) { + const criticalTerm = new foundry.dice.terms.NumericTerm({ number: criticalDamage, evaluated: true }); + criticalTerm.evaluate(); + newDamage.main = await Roll.fromTerms([ + ...origDamage.main.terms, + new foundry.dice.terms.OperatorTerm({ operator: '+' }), + criticalTerm + ]); + newDamage.main.options = foundry.utils.deepClone(origDamage.main.options); } - } - - return newDamage; - } - - async getNonCriticalDamage(config) { - const newDamage = foundry.utils.deepClone(config.damage); - for (let key in newDamage) { - var damage = newDamage[key]; - damage.formula = ''; - damage.total = 0; - - for (let part of damage.parts) { - const critDamageBonus = await getCritDamageBonus(part.formula); - part.modifierTotal -= critDamageBonus; - part.total -= critDamageBonus; - part.formula = `${part.dice.map(x => x.formula).join(' + ')} + ${part.modifierTotal}`; - part.roll = new Roll(part.formula); - - damage.formula = [damage.formula, part.formula].filter(x => x).join(' + '); - damage.total += part.total; - } - } + } return newDamage; } static async #selectRoll(_, button) { const { memberKey } = button.dataset; + this.updatePartyData( { [`system.tagTeam.members`]: Object.entries(this.party.system.tagTeam.members).reduce( @@ -667,7 +611,12 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio {} ) }, - this.getUpdatingParts(button) + /* Selecting a roll must update all member sections hbs to display the correct damage information incase of a critical */ + [ + ...Object.keys(this.party.system.tagTeam.members), + this.constructor.PARTS.rollSelection.id, + this.constructor.PARTS.result.id + ] ); } @@ -685,29 +634,65 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio if (!baseMainRoll?.rollData || !baseSecondaryRoll) return null; const mainRoll = new MemberData(baseMainRoll.toObject()); - const secondaryRollData = new MemberData(baseSecondaryRoll.toObject()).rollData; - const systemData = mainRoll.rollData.options; - const isCritical = overrideIsCritical ?? systemData.roll.isCritical; - if (isCritical) systemData.damage = await this.getCriticalDamage(systemData.damage); + mainRoll.damageRollData = baseMainRoll.damageRollData ? + ChatDamageData.fromJSON(JSON.stringify(baseMainRoll.damageRollData)) : null; + const secondaryRoll = new MemberData(baseSecondaryRoll.toObject()); + secondaryRoll.damageRollData = baseSecondaryRoll.damageRollData ? + ChatDamageData.fromJSON(JSON.stringify(baseSecondaryRoll.damageRollData)) : null; - if (secondaryRollData?.options.hasDamage) { + const isCritical = overrideIsCritical ?? mainRoll.roll.isCritical; + if (isCritical) mainRoll.damageRollData = await this.getCriticalDamage(mainRoll.damageRollData); + + if (secondaryRoll.damageRollData) { const secondaryDamage = (displayVersion ? overrideIsCritical : isCritical) - ? await this.getCriticalDamage(secondaryRollData.options.damage) - : secondaryRollData.options.damage; - if (systemData.damage) { - for (const [key, damage] of Object.entries(secondaryDamage ?? {})) { - if (key in systemData.damage) { - systemData.damage[key].formula = [systemData.damage[key]?.formula, damage.formula] - .filter(x => x) - .join(' + '); - systemData.damage[key].total += damage.total; - systemData.damage[key].parts.push(...damage.parts); + ? await this.getCriticalDamage(secondaryRoll.damageRollData) + : secondaryRoll.damageRollData; + if (mainRoll.damageRollData) { + if (secondaryDamage.main) { + if (mainRoll.damageRollData.main) { + mainRoll.damageRollData.main = Roll.fromTerms([ + ...baseMainRoll.damageRollData.main.terms, + new foundry.dice.terms.OperatorTerm({ operator: '+' }), + ...baseSecondaryRoll.damageRollData.main.terms + ]); + + /* Joining the roll.options of both rolls */ + const joinedDamageTypes = new Set([ + ...baseMainRoll.damageRollData.main.options.damageTypes, + ...baseSecondaryRoll.damageRollData.main.options.damageTypes + ]); + mainRoll.damageRollData.main.options = { + ...baseMainRoll.damageRollData.main.options, + damageTypes: [...joinedDamageTypes] + }; } else { - systemData.damage[key] = damage; + mainRoll.damageRollData.main = secondaryDamage.main; + } + } + + for (const [key, damage] of Object.entries(secondaryDamage.resources ?? {})) { + if (key in mainRoll.damageRollData.resources) { + mainRoll.damageRollData.resources[key] = Roll.fromTerms([ + ...baseMainRoll.damageRollData.resources[key].terms, + new foundry.dice.terms.OperatorTerm({ operator: '+' }), + ...baseSecondaryRoll.damageRollData.resources[key].terms + ]); + + /* Joining the roll.options of both rolls */ + const joinedDamageTypes = new Set([ + ...baseMainRoll.damageRollData.resources[key].options.damageTypes, + ...baseSecondaryRoll.damageRollData.resources[key].options.damageTypes + ]); + mainRoll.damageRollData.resources[key].options = { + ...baseMainRoll.damageRollData.resources[key].options, + damageTypes: [...joinedDamageTypes] + }; + } else { + mainRoll.damageRollData.resources[key] = damage; } } } else { - systemData.damage = secondaryDamage; + mainRoll.damageRollData = secondaryDamage; } } @@ -762,13 +747,27 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio const mainActor = this.party.system.partyMembers.find(x => x.uuid === mainRoll.options.source.actor); mainRoll.options.title = game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.chatMessageRollTitle'); + + /* This could assumably be done better. For some reason rolls don't get correctly done through rollData.toJSON */ + const systemData = { + ...mainRoll.options, + damage: joinedRoll.damageRollData?.toJSON() + }; + + if (joinedRoll.damageRollData.main) { + systemData.damage.main = joinedRoll.damageRollData.toJSON(); + } + for (const type of Object.keys(joinedRoll.damageRollData?.resources ?? {})) { + systemData.damage.resources[type] = joinedRoll.damageRollData.resources[type].toJSON(); + } + const cls = getDocumentClass('ChatMessage'), msgData = { type: 'dualityRoll', user: game.user.id, title: game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.title'), speaker: cls.getSpeaker({ actor: mainActor }), - system: mainRoll.options, + system: systemData, rolls: [JSON.stringify(joinedRoll.roll)], sound: null, flags: { core: { RollTable: true } } diff --git a/module/applications/sheets-configs/action-base-config.mjs b/module/applications/sheets-configs/action-base-config.mjs index b65e1cdf..e675fb3f 100644 --- a/module/applications/sheets-configs/action-base-config.mjs +++ b/module/applications/sheets-configs/action-base-config.mjs @@ -1,4 +1,4 @@ -import { getUnusedDamageTypes } from '../../helpers/utils.mjs'; +import { DHDamageData } from '../../data/fields/action/damageField.mjs'; import DaggerheartSheet from '../sheets/daggerheart-sheet.mjs'; const { ApplicationV2 } = foundry.applications.api; @@ -31,8 +31,10 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) removeElement: this.removeElement, removeTransformActor: this.removeTransformActor, editEffect: this.editEffect, - addDamage: this.addDamage, - removeDamage: this.removeDamage, + addDamage: this.#onAddDamage, + removeDamage: this.#onRemoveDamage, + addDamageResource: this.#onAddDamageResource, + removeDamageResource: this.#onRemoveDamageResource, editDoc: this.editDoc, addTrigger: this.addTrigger, removeTrigger: this.removeTrigger, @@ -157,9 +159,9 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) context.tabs = this._getTabs(this.constructor.TABS); context.config = CONFIG.DH; if (this.action.damage) { - context.allDamageTypesUsed = !getUnusedDamageTypes(this.action.damage.parts).length; - - if (this.action.damage.hasOwnProperty('includeBase') && this.action.type === 'attack') + const allKeys = Object.keys(CONFIG.DH.GENERAL.healingTypes); + context.allDamageTypesUsed = allKeys.every(k => k in this.action._source.damage.resources); + if (this.action.damage?.main?.hasOwnProperty('includeBase') && this.action.type === 'attack') context.hasBaseDamage = !!this.action.parent.attack; } @@ -253,7 +255,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) const submitData = this._prepareSubmitData(event, formData); const data = foundry.utils.mergeObject(this.action.toObject(), submitData); - this.action = await this.action.update(data); + this.action = (await this.action.update(data)) ?? this.action; this.sheetUpdate?.(this.action); this.render(); @@ -299,53 +301,70 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); } - static addDamage(_event) { - if (!this.action.damage.parts) return; + /** @this DHActionBaseConfig */ + static #onAddDamage() { + if (!this.action.damage || this.action.damage?.main) return; - const choices = getUnusedDamageTypes(this.action._source.damage.parts); + const data = this.action.toObject(); + data.damage.main = { + ...DHDamageData.schema.getInitialValue(), + applyTo: 'hitPoints', + type: 'physical' + }; + this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); + } + + /** @this DHActionBaseConfig */ + static #onRemoveDamage() { + if (!this.action.damage?.main) return; + const data = this.action.toObject(); + data.damage.main = null; + this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); + } + + /** @this DHActionBaseConfig */ + static #onAddDamageResource(_event) { + if (!this.action.damage) return; + + const allKeys = Object.keys(CONFIG.DH.GENERAL.healingTypes); + const unused = allKeys.filter(k => !(k in this.action._source.damage.resources)); + const choices = unused.map(k => ({ value: k, label: _loc(CONFIG.DH.GENERAL.healingTypes[k].label) })); const content = new foundry.data.fields.StringField({ - label: game.i18n.localize('Damage Type'), + label: _loc('DAGGERHEART.GENERAL.Resource.single'), choices, required: true - }).toFormGroup( - {}, - { - name: 'type', - localize: true, - nameAttr: 'value', - labelAttr: 'label' - } - ).outerHTML; + }).toFormGroup({}, { + name: 'type', + localize: true, + nameAttr: 'value', + labelAttr: 'label' + }).outerHTML; const callback = (_, button) => { const data = this.action.toObject(); const type = choices[button.form.elements.type.value].value; - const part = this.action.schema.fields.damage.fields.parts.element.getInitialValue(); - part.applyTo = type; - if (type === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) - part.type = this.action.schema.fields.damage.fields.parts.element.fields.type.element.initial; - - data.damage.parts[type] = part; + data.damage.resources[type] = { + ...this.action.schema.fields.damage.fields.resources.element.getInitialValue(), + applyTo: type + }; this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); }; const typeDialog = new foundry.applications.api.DialogV2({ buttons: [ - foundry.utils.mergeObject( - { - action: 'ok', - label: 'Confirm', - icon: 'fas fa-check', - default: true - }, - { callback: callback } - ) + { + action: 'ok', + label: 'Confirm', + icon: 'fas fa-check', + default: true, + callback + } ], content: content, rejectClose: false, modal: false, window: { - title: game.i18n.localize('Add Damage') + title: _loc('DAGGERHEART.ACTIONS.Config.damage.addResource') }, position: { width: 300 } }); @@ -353,12 +372,12 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) typeDialog.render(true); } - static removeDamage(_event, button) { - if (!this.action.damage.parts) return; + /** @this DHActionBaseConfig */ + static #onRemoveDamageResource(_event, button) { + if (!this.action.damage?.resources) return; const data = this.action.toObject(); const key = button.dataset.key; - delete data.damage.parts[key]; - data.damage.parts[`${key}`] = _del; + data.damage.resources[key] = _del; this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); } diff --git a/module/applications/sheets-configs/adversary-settings.mjs b/module/applications/sheets-configs/adversary-settings.mjs index ff3f3039..0bf18ee6 100644 --- a/module/applications/sheets-configs/adversary-settings.mjs +++ b/module/applications/sheets-configs/adversary-settings.mjs @@ -1,3 +1,4 @@ +import { DHDamageData } from '../../data/fields/action/damageField.mjs'; import DHBaseActorSettings from '../sheets/api/actor-setting.mjs'; /**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */ @@ -8,8 +9,10 @@ export default class DHAdversarySettings extends DHBaseActorSettings { classes: ['adversary-settings'], position: { width: 455, height: 'auto' }, actions: { - addExperience: DHAdversarySettings.#addExperience, - removeExperience: DHAdversarySettings.#removeExperience + addExperience: this.#onAddExperience, + removeExperience: this.#onRemoveExperience, + addDamage: this.#onAddDamage, + removeDamage: this.#onRemoveDamage } }; @@ -71,7 +74,7 @@ export default class DHAdversarySettings extends DHBaseActorSettings { * Adds a new experience entry to the actor. * @type {ApplicationClickAction} */ - static async #addExperience() { + static async #onAddExperience() { const newExperience = { name: 'Experience', modifier: 0 @@ -83,7 +86,7 @@ export default class DHAdversarySettings extends DHBaseActorSettings { * Removes an experience entry from the actor. * @type {ApplicationClickAction} */ - static async #removeExperience(_, target) { + static async #onRemoveExperience(_, target) { const experience = this.actor.system.experiences[target.dataset.experience]; const confirmed = await foundry.applications.api.DialogV2.confirm({ window: { @@ -98,4 +101,28 @@ export default class DHAdversarySettings extends DHBaseActorSettings { await this.actor.update({ [`system.experiences.${target.dataset.experience}`]: _del }); } + + /** + * @this DHAdversarySettings + * @type {ApplicationClickAction} + */ + static #onAddDamage() { + this.actor.update({ + 'system.attack.damage.main': { + ...DHDamageData.schema.getInitialValue(), + applyTo: 'hitPoints', + type: 'physical' + } + }); + } + + /** + * @this DHAdversarySettings + * @type {ApplicationClickAction} + */ + static #onRemoveDamage() { + this.actor.update({ + 'system.attack.damage.main': null + }); + } } diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index 199ee87d..c457a6a6 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -110,7 +110,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo const message = game.messages.get(li.dataset.messageId); return message.system.hasRoll && (game.user.isGM || message.isAuthor); }, - callback: async li => { + onClick: async (_event, li) => { const message = game.messages.get(li.dataset.messageId); const reroll = await message.rolls[0].reroll({ liveRoll: true }); message.update({ rolls: [reroll] }); @@ -126,7 +126,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo : false; return (game.user.isGM || message.isAuthor) && hasRolledDamage; }, - callback: async li => { + onClick: async (_event, li) => { const message = game.messages.get(li.dataset.messageId); const update = await message.system.getRerolledDamage(); message.update(update); @@ -179,28 +179,15 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo } async onRollSimple(event, message) { - const buttonType = event.target.dataset.type ?? 'damage', - total = message.rolls.reduce((a, c) => a + Roll.fromJSON(c).total, 0), - damages = { - hitPoints: { - parts: [ - { - applyTo: 'hitPoints', - damageTypes: [], - total - } - ] - } - }, - targets = Array.from(game.user.targets); - + const buttonType = event.target.dataset.type ?? 'damage'; + const total = message.rolls.reduce((a, c) => a + Roll.fromJSON(c).total, 0); + const targets = Array.from(game.user.targets); if (targets.length === 0) return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected')); - - targets.forEach(target => { - if (buttonType === 'healing') target.actor.takeHealing(damages); - else target.actor.takeDamage(damages); - }); + for (const target of targets) { + if (buttonType === 'healing') target.actor.takeHealing({ hitPoints: total }); + else target.actor.takeDamage({ total }); + } } async abilityUseButton(event, message) { @@ -256,26 +243,17 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo } const message = game.messages.get(messageData._id); - const target = event.target.closest('[data-die-index]'); + const target = event.target.closest('[data-result]'); if (target.dataset.type === 'damage') { - const { damageType, part, dice, result } = target.dataset; - const damagePart = message.system.damage[damageType].parts[part]; - const { parsedRoll, rerolledDice } = await game.system.api.dice.DamageRoll.reroll(damagePart, dice, result); - const damageParts = message.system.damage[damageType].parts.map((damagePart, index) => { - if (index !== Number(part)) return damagePart; - return { - ...damagePart, - total: parsedRoll.total, - dice: rerolledDice - }; - }); - const updateMessage = game.messages.get(message._id); - await updateMessage.update({ - [`system.damage.${damageType}`]: { - total: parsedRoll.total, - parts: damageParts - } + const { isResource, damageType, dice, result } = target.dataset; + await message.system.damage.rerollDamageDie(isResource, damageType, dice, result); + + const updatePath = isResource ? `system.damage.resources.${damageType}` : 'system.damage.main'; + const updateValue = isResource ? + message.system.damage.resources[damageType] : message.system.damage.main; + await message.update({ + [updatePath]: updateValue.toJSON() }); } else { const rerollDice = message.system.roll.dice[target.dataset.dieIndex]; diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index 188efafb..802f5907 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -825,7 +825,7 @@ export const refreshTypes = { export const itemAbilityCosts = { resource: { id: 'resource', - label: 'DAGGERHEART.GENERAL.resource', + label: 'DAGGERHEART.GENERAL.Resource.single', group: 'Global' }, quantity: { diff --git a/module/data/action/attackAction.mjs b/module/data/action/attackAction.mjs index 1988b1d8..dadc85f0 100644 --- a/module/data/action/attackAction.mjs +++ b/module/data/action/attackAction.mjs @@ -13,18 +13,19 @@ export default class DHAttackAction extends DHDamageAction { if (this.damage.includeBase) { const baseDamage = this.getParentHitPointDamage(); if (baseDamage) { - if (!this.damage.parts.hitPoints) { - this.damage.parts.hitPoints = baseDamage; + if (!this.damage.main) { + this.damage.main = baseDamage; } else { - for (const type of baseDamage.type) this.damage.parts.hitPoints.type.add(type); + for (const type of baseDamage.type) this.damage.main.type.add(type); - this.damage.parts.hitPoints.value.custom = { + this.damage.main.value.custom = { enabled: true, - formula: `${baseDamage.value.getFormula()} + ${this.damage.parts.hitPoints.value.getFormula()}` + formula: `${baseDamage.value.getFormula()} + ${this.damage.main.value.getFormula()}` }; } } } + if (this.roll.useDefault) { this.roll.trait = this.item.system.attack.roll.trait; this.roll.type = 'attack'; @@ -33,18 +34,18 @@ export default class DHAttackAction extends DHDamageAction { } getParentHitPointDamage() { - return this.item?.system?.attack.damage.parts.hitPoints; + return this.item?.system?.attack.damage.main; } get damageFormula() { - const hitPointsPart = this.damage.parts.hitPoints; + const hitPointsPart = this.damage.main; if (!hitPointsPart) return '0'; return hitPointsPart.value.getFormula(); } get altDamageFormula() { - const hitPointsPart = this.damage.parts.hitPoints; + const hitPointsPart = this.damage.main; if (!hitPointsPart) return '0'; return hitPointsPart.valueAlt.getFormula(); @@ -73,7 +74,7 @@ export default class DHAttackAction extends DHDamageAction { if (range) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Range.${range}.short`)); const useAltDamage = this.actor?.effects?.find(x => x.type === 'horde')?.active; - for (const { value, valueAlt, type } of damage.parts) { + for (const { value, valueAlt, type } of [damage.main, ...damage.resources].filter(d => !!d)) { const usedValue = useAltDamage ? valueAlt : value; const damageString = Roll.replaceFormulaData(usedValue.getFormula(), this.actor?.getRollData() ?? {}); const str = damageString @@ -82,7 +83,7 @@ export default class DHAttackAction extends DHDamageAction { x: game.i18n.localize('DAGGERHEART.GENERAL.damage') }); - const icons = Array.from(type) + const icons = Array.from(type ?? []) .map(t => CONFIG.DH.GENERAL.damageTypes[t]?.icon) .filter(Boolean); diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 5b871c9a..05890335 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -289,7 +289,6 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel hasEffect: this.hasEffect, hasSave: this.hasSave, onSave: this.save?.damageMod, - isDirect: !!this.damage?.direct, selectedMessageMode: game.settings.get('core', 'messageMode'), data: this.getRollData(), evaluate: this.hasRoll, @@ -307,20 +306,20 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel }; if (this.damage) { - config.isDirect = this.damage.direct; + config.isDirect = !!this.damage.main?.direct; - const groupAttackTokens = this.damage.groupAttack + const groupAttackTokens = this.damage.main?.groupAttack ? game.system.api.fields.ActionFields.DamageField.getGroupAttackTokens( this.actor.id, - this.damage.groupAttack + this.damage.main.groupAttack ) : null; config.damageOptions = { - groupAttack: this.damage.groupAttack + groupAttack: this.damage.main?.groupAttack ? { numAttackers: Math.max(groupAttackTokens.length, 1), - range: this.damage.groupAttack + range: this.damage.main.groupAttack } : null }; @@ -430,11 +429,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel } get hasDamage() { - return Boolean(Object.keys(this.damage?.parts ?? {}).length) && this.type !== 'healing'; + return this.type !== 'healing' && (Boolean(this.damage.main) || !foundry.utils.isEmpty(this.damage.resources)); } get hasHealing() { - return Boolean(Object.keys(this.damage?.parts ?? {}).length) && this.type === 'healing'; + return this.type === 'healing' && !foundry.utils.isEmpty(this.damage.resources); } get hasSave() { @@ -470,6 +469,25 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel return acc; }, {}); } + + if (source.damage?.parts && !source.damage.resources && !source.damage.main) { + source.damage.main = null; + source.damage.resources = {}; + for (const [partKey, part] of Object.entries(source.damage.parts)) { + if (partKey === 'hitPoints' && source.type !== 'healing') { + source.damage.main = { + ...part, + includeBase: source.damage.includeBase, + direct: source.damage.direct, + groupAttack: source.damage.groupAttack + }; + } else { + source.damage.resources[partKey] = part; + } + } + + delete source.damage.parts; + } } } diff --git a/module/data/action/damageAction.mjs b/module/data/action/damageAction.mjs index 51735543..15135e0d 100644 --- a/module/data/action/damageAction.mjs +++ b/module/data/action/damageAction.mjs @@ -8,11 +8,8 @@ export default class DHDamageAction extends DHBaseAction { * @returns Formula string */ getDamageFormula() { - const strings = []; - for (const { value } of this.damage.parts) { - strings.push(Roll.replaceFormulaData(value.getFormula(), this.actor?.getRollData() ?? {})); - } + if (!this.damage.main) return ''; - return strings.join(' + '); + return Roll.replaceFormulaData(this.damage.main.value.getFormula(), this.actor?.getRollData() ?? {}); } } diff --git a/module/data/actor/adversary.mjs b/module/data/actor/adversary.mjs index ae17c128..2640211f 100644 --- a/module/data/actor/adversary.mjs +++ b/module/data/actor/adversary.mjs @@ -84,13 +84,11 @@ export default class DhpAdversary extends DhCreature { type: 'attack' }, damage: { - parts: { - hitPoints: { - type: ['physical'], - applyTo: 'hitPoints', - value: { - multiplier: 'flat' - } + main: { + type: ['physical'], + applyTo: 'hitPoints', + value: { + multiplier: 'flat' } } } diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index b39c64aa..8ff50a5d 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -104,15 +104,13 @@ export default class DhCharacter extends DhCreature { trait: 'strength' }, damage: { - parts: { - hitPoints: { - type: ['physical'], - applyTo: 'hitPoints', - value: { - custom: { - enabled: true, - formula: '@profd4' - } + main: { + type: ['physical'], + applyTo: 'hitPoints', + value: { + custom: { + enabled: true, + formula: '@profd4' } } } @@ -838,7 +836,7 @@ export default class DhCharacter extends DhCreature { isReversed: true }; - this.attack.damage.parts.hitPoints.value.custom.formula = `@prof${this.basicAttackDamageDice}${this.rules.attack.damage.bonus ? ` + ${this.rules.attack.damage.bonus}` : ''}`; + this.attack.damage.main.value.custom.formula = `@prof${this.basicAttackDamageDice}${this.rules.attack.damage.bonus ? ` + ${this.rules.attack.damage.bonus}` : ''}`; // Clamp resources (must be done last to ensure all updates occur) this.resources.clamp(); diff --git a/module/data/actor/tierAdjustment.mjs b/module/data/actor/tierAdjustment.mjs index 8b9e5bdc..4bf74a45 100644 --- a/module/data/actor/tierAdjustment.mjs +++ b/module/data/actor/tierAdjustment.mjs @@ -40,17 +40,17 @@ export function getTierAdjustedAdversary(source, tier) { // Store initial attack damage for abilities that have you deal a "standard attack" const initialAttack = { - type: source.system.attack.damage?.parts.hitPoints?.type?.toSorted(), - value: getFormula(source.system.attack.damage?.parts.hitPoints?.value) + type: source.system.attack.damage?.main?.type?.toSorted(), + value: getFormula(source.system.attack.damage?.main?.value) }; // Update damage of base attack. try { const damage = source.system.attack.damage; - if (!damage?.parts.hitPoints) throw new Error('Unexpected missing attack in adversary'); + if (!damage?.main) throw new Error('Unexpected missing damage in adversary'); for (const property of ['value', 'valueAlt']) { - const data = damage.parts.hitPoints[property]; + const data = damage.main[property]; const previousFormula = getFormula(data); const value = calculateAdjustedDamage(previousFormula, 'attack', damageMeta); applyAdjustedDamage(data, value); @@ -82,12 +82,12 @@ export function getTierAdjustedAdversary(source, tier) { // Update damage in item actions and convert all formula matches in the descriptions to the new damage for (const action of Object.values(item.system.actions)) { - if (!action.damage?.parts.hitPoints) continue; + if (!action.damage?.main) continue; try { // Apply conversions and save a record. If it matches attack damage *and* Its not in the description, use attack conversion instead const result = []; for (const property of ['value', 'valueAlt']) { - const { [property]: data, type: damageType } = action.damage.parts.hitPoints; + const { [property]: data, type: damageType } = action.damage.main; const previousFormula = getFormula(data); const isActuallyAttack = previousFormula === initialAttack.value && @@ -199,7 +199,7 @@ function calculateAdjustedDamage(formula, type, { currentDamageRange, newDamageR } /** - * Get formula from either damage parts *or* a simple formula object. + * Get formula from either damage data *or* a simple formula object. * @returns {string} the new formula data */ function getFormula(data) { diff --git a/module/data/chat-message/actorRoll.mjs b/module/data/chat-message/actorRoll.mjs index ccfe25ea..dd81952b 100644 --- a/module/data/chat-message/actorRoll.mjs +++ b/module/data/chat-message/actorRoll.mjs @@ -1,4 +1,5 @@ import { triggerChatRollFx } from '../../helpers/utils.mjs'; +import { ChatDamageData } from './chatDamageData.mjs'; const fields = foundry.data.fields; @@ -49,7 +50,7 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { originItem: originItemField(), action: new fields.StringField() }), - damage: new fields.ObjectField(), + damage: new fields.EmbeddedDataField(ChatDamageData), damageOptions: new fields.ObjectField(), costs: new fields.ArrayField(new fields.ObjectField()), successConsumed: new fields.BooleanField({ initial: false }) @@ -132,28 +133,21 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { }); } - /* TODO: Change how damage data is stored somehow to enable better rerolling */ async getRerolledDamage() { - if (!this.damage) return; + if (!this.damage.active) return; const rerolls = []; - const update = { system: { damage: {} } }; - for (const partKey in this.damage) { - const part = this.damage[partKey]; - const testRoll = Roll.fromData(part.parts[0].roll); - const rerolled = await testRoll.reroll(); - rerolls.push(rerolled); + const update = { system: { damage: { main: null, resources: _replace({}) } } }; + if (this.damage.main) { + const reroll = await this.damage.main.reroll(); + rerolls.push(reroll); + update.system.damage.main = reroll.toJSON(); + } - if (!update.system.damage[partKey]) update.system.damage[partKey] = { parts: [part.parts[0]] }; - const partData = update.system.damage[partKey].parts[0]; - update.system.damage[partKey].total = rerolled.total; - partData.modifierTotal = rerolled.terms.reduce((acc, x) => { - if (x.isDeterministic && !x.operator) acc += x.total; - return acc; - }, 0); - partData.dice = rerolled.dice.map(d => ({ ...d.toJSON(), dice: d.denomination })); - partData.total = rerolled.total; - partData.roll = rerolled.toJSON(); + for (const key of Object.keys(this.damage.resources)) { + const reroll = await this.damage.resources[key].reroll(); + rerolls.push(reroll); + update.system.damage.resources[key] = reroll.toJSON(); } await triggerChatRollFx(rerolls); @@ -198,6 +192,43 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { this.isGM = game.user.isGM; //temp } + static migrateData(source) { + const { main, resources, ...flatDamageKeys } = source.damage ?? {}; + if (source.damage && !main && !resources) { + source.damage.main = null; + source.damage.resources = {}; + + const getRoll = key => { + const damageData = source.damage[key]; + const oldRoll = damageData.parts[0]?.roll; + return oldRoll ? JSON.stringify({ + ...oldRoll, + class: 'BaseRoll', + options: { + ...oldRoll.options, + damageTypes: damageData.parts[0].damageTypes ?? [] + } + }) : null; + }; + + for (const key of Object.keys(flatDamageKeys)) { + if (key === 'hitPoints' && source.hasDamage && !source.hasHealing) { + source.damage.main = getRoll('hitPoints'); + } + else { + source.damage.resources[key] = getRoll(key); + } + } + } + + for (const key of Object.keys(flatDamageKeys)) { + delete source.damage[key]; + } + + return source; + } + + getTargetList() { const targets = this.targetMode && this.parent.isAuthor diff --git a/module/data/chat-message/chatDamageData.mjs b/module/data/chat-message/chatDamageData.mjs new file mode 100644 index 00000000..404cda43 --- /dev/null +++ b/module/data/chat-message/chatDamageData.mjs @@ -0,0 +1,58 @@ +import { triggerChatRollFx } from '../../helpers/utils.mjs'; + +export class ChatDamageData extends foundry.abstract.DataModel { + constructor(data = {}, options = {}) { + super(data, options); + + this._prepareRolls(); + } + + static defineSchema() { + const fields = foundry.data.fields; + + return { + main: new fields.JSONField({ nullable: true, validate: ChatDamageData.#validateRoll}), + resources: new fields.TypedObjectField(new fields.JSONField({validate: ChatDamageData.#validateRoll})) + }; + } + + get active() { + return !!this.main || Boolean(Object.keys(this.resources).length); + } + + static #validateRoll(rollJSON) { + if (rollJSON) { + const roll = JSON.parse(rollJSON); + if (!roll.evaluated) throw new Error('Roll objects added to ChatMessage documents must be evaluated'); + } + } + + _prepareRolls() { + this.main &&= Roll.fromData(this.main); + for (const key of Object.keys(this.resources)) { + this.resources[key] = Roll.fromData(this.resources[key]); + } + } + + async rerollDamageDie(isResource, damageType, dice, resultIndex) { + const reroll = isResource ? this.resources[damageType] : this.main; + const rerollDice = reroll.dice[dice]; + await rerollDice.rerollResult(resultIndex); + await reroll._evaluate(); + + const rerolledResult = rerollDice.results[rerollDice.results.length - 1]; + if (rerolledResult) { + const fakeRoll = { + _evaluated: true, + dice: [new foundry.dice.terms.Die({ + ...rerollDice, + results: [rerolledResult], + total: rerolledResult.value, + faces: rerollDice.faces + })], + options: { appearance: {} } + }; + await triggerChatRollFx([fakeRoll]); + } + } +} \ No newline at end of file diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs index 2c6c7b30..25b3ba97 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -12,20 +12,10 @@ export default class DamageField extends fields.SchemaField { /** @inheritDoc */ constructor(options, context = {}) { - const damageFields = { - parts: new IterableTypedObjectField(DHDamageData), - includeBase: new fields.BooleanField({ - initial: false, - label: 'DAGGERHEART.ACTIONS.Settings.includeBase.label' - }), - direct: new fields.BooleanField({ initial: false, label: 'DAGGERHEART.CONFIG.DamageType.direct.name' }), - groupAttack: new fields.StringField({ - choices: CONFIG.DH.GENERAL.groupAttackRange, - blank: true, - label: 'DAGGERHEART.ACTIONS.Settings.groupAttack.label' - }) - }; - super(damageFields, options, context); + super({ + main: new fields.EmbeddedDataField(DHDamageData, { nullable: true }), + resources: new IterableTypedObjectField(DHResourceData) + }, options, context); } /** @@ -41,25 +31,23 @@ export default class DamageField extends fields.SchemaField { this.hasRoll && DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.never.id && !force - ) + ) { return; + } - let formulas = this.damage.parts.map(p => ({ - formula: DamageField.getFormulaValue.call(this, p, config).getFormula(this.actor), - damageTypes: p.applyTo === 'hitPoints' && !p.type.size ? new Set(['physical']) : p.type, - applyTo: p.applyTo - })); + const damageFormula = this.damage.main ? + DamageField.formatFormulas.call(this, [this.damage.main], config)[0] : null; + const resourceFormulas = DamageField.formatFormulas.call(this, this.damage.resources, config); - if (!formulas.length) return false; - - formulas = DamageField.formatFormulas.call(this, formulas, config); + if (!damageFormula && !resourceFormulas.length) return false; messageId = config.message?._id ?? messageId; const message = game.messages.get(messageId); const damageConfig = { dialog: {}, ...config, - roll: formulas, + damageFormula, + resourceFormulas, data: this.getRollData(), isCritical: Boolean(message?.system.roll?.isCritical) }; @@ -92,7 +80,7 @@ export default class DamageField extends fields.SchemaField { const targetDamage = []; const damagePromises = []; - for (let target of targets) { + for (const target of targets) { const actor = foundry.utils.fromUuidSync(target.actorId); if (!actor) continue; if (!config.hasHealing && config.onSave && target.saved?.success === true) { @@ -114,13 +102,12 @@ export default class DamageField extends fields.SchemaField { actor.takeHealing(config.damage).then(updates => targetDamage.push({ token, updates })) ); else { - const configDamage = foundry.utils.deepClone(config.damage); - const hpDamageMultiplier = config.actionActor?.system.rules?.attack?.damage?.hpDamageMultiplier ?? 1; - const hpDamageTakenMultiplier = actor.system.rules?.attack?.damage?.hpDamageTakenMultiplier; - if (configDamage.hitPoints) { - for (const part of configDamage.hitPoints.parts) { - part.total = Math.ceil(part.total * hpDamageMultiplier * hpDamageTakenMultiplier); - } + const configDamage = config.damage.clone(); + configDamage.main &&= configDamage.main.toJSON(); + if (configDamage.main) { + const multiplier = config.actionActor?.system.rules?.attack?.damage?.hpDamageMultiplier ?? 1; + const takenMultiplier = actor.system.rules?.attack?.damage?.hpDamageTakenMultiplier; + configDamage.main.total = Math.ceil(configDamage.main.total * multiplier * takenMultiplier); } damagePromises.push( @@ -183,13 +170,19 @@ export default class DamageField extends fields.SchemaField { /** * Prepare formulas for Damage Roll * Must be called within Action context or similar. - * @param {object[]} formulas Array of formatted formulas object + * @param {DHResourceData[]} damageData Array of DHResourceData * @param {object} data Action getRollData * @returns */ - static formatFormulas(formulas, data) { + static formatFormulas(damageData, data) { + const formulas = damageData.map(x => ({ + formula: DamageField.getFormulaValue.call(this, x, data).getFormula(this.actor), + damageTypes: x.type ?? new Set(), + applyTo: x.applyTo + })); + const formattedFormulas = []; - formulas.forEach(formula => { + for (const formula of formulas) { if (isNaN(formula.formula)) formula.formula = Roll.replaceFormulaData(formula.formula, this.getRollData(data)); const same = formattedFormulas.find( @@ -197,7 +190,8 @@ export default class DamageField extends fields.SchemaField { ); if (same) same.formula += ` + ${formula.formula}`; else formattedFormulas.push(formula); - }); + } + return formattedFormulas; } @@ -294,6 +288,7 @@ export class DHResourceData extends foundry.abstract.DataModel { /** @override */ static defineSchema() { return { + base: new fields.BooleanField({ initial: false, readonly: true, label: 'Base' }), applyTo: new fields.StringField({ choices: CONFIG.DH.GENERAL.healingTypes, required: true, @@ -316,7 +311,16 @@ export class DHDamageData extends DHResourceData { static defineSchema() { return { ...super.defineSchema(), - base: new fields.BooleanField({ initial: false, readonly: true, label: 'Base' }), + includeBase: new fields.BooleanField({ + initial: false, + label: 'DAGGERHEART.ACTIONS.Settings.includeBase.label' + }), + direct: new fields.BooleanField({ initial: false, label: 'DAGGERHEART.CONFIG.DamageType.direct.name' }), + groupAttack: new fields.StringField({ + choices: CONFIG.DH.GENERAL.groupAttackRange, + blank: true, + label: 'DAGGERHEART.ACTIONS.Settings.groupAttack.label' + }), type: new fields.SetField( new fields.StringField({ choices: CONFIG.DH.GENERAL.damageTypes, diff --git a/module/data/fields/actionField.mjs b/module/data/fields/actionField.mjs index ba2fa37e..f3bdce8a 100644 --- a/module/data/fields/actionField.mjs +++ b/module/data/fields/actionField.mjs @@ -238,6 +238,7 @@ export function ActionMixin(Base) { result = this.parent; } else { result = await this.item.update({ [path]: updates }, options); + if (!result) return result; } return this.inCollection diff --git a/module/data/item/weapon.mjs b/module/data/item/weapon.mjs index 42326f93..5ff2d8d1 100644 --- a/module/data/item/weapon.mjs +++ b/module/data/item/weapon.mjs @@ -67,13 +67,11 @@ export default class DHWeapon extends AttachableItem { type: 'attack' }, damage: { - parts: { - hitPoints: { - type: ['physical'], - value: { - multiplier: 'prof', - dice: 'd8' - } + main: { + type: ['physical'], + value: { + multiplier: 'prof', + dice: 'd8' } } } @@ -230,11 +228,12 @@ export default class DHWeapon extends AttachableItem { game.i18n.localize(`DAGGERHEART.CONFIG.Burden.${burden}`) ]; - for (const { value, type } of attack.damage.parts) { + if (attack.damage.main) { + const { value, type } = attack.damage.main; const parts = value.custom.enabled ? [game.i18n.localize('DAGGERHEART.GENERAL.custom')] : [value.dice]; if (!value.custom.enabled && value.bonus) parts.push(value.bonus.signedString()); - if (type.size > 0) { + if (type?.size) { const typeTags = Array.from(type) .map(t => game.i18n.localize(`DAGGERHEART.CONFIG.DamageType.${t}.abbreviation`)) .join(' | '); @@ -243,7 +242,7 @@ export default class DHWeapon extends AttachableItem { tags.push(parts.join('')); } - + return tags; } @@ -258,10 +257,10 @@ export default class DHWeapon extends AttachableItem { if (roll.trait) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Traits.${roll.trait}.short`)); if (range) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Range.${range}.short`)); - for (const { value, type } of damage.parts) { + for (const { value, type } of [damage.main, ...damage.resources].filter(d => !!d)) { const str = Roll.replaceFormulaData(value.getFormula(), this.actor?.getRollData() ?? {}); - const icons = Array.from(type) + const icons = Array.from(type ?? []) .map(t => CONFIG.DH.GENERAL.damageTypes[t]?.icon) .filter(Boolean); diff --git a/module/data/tagTeamData.mjs b/module/data/tagTeamData.mjs index 640c2f6c..ef6536f1 100644 --- a/module/data/tagTeamData.mjs +++ b/module/data/tagTeamData.mjs @@ -1,3 +1,5 @@ +import { ChatDamageData } from './chat-message/chatDamageData.mjs'; + export default class TagTeamData extends foundry.abstract.DataModel { static defineSchema() { const fields = foundry.data.fields; @@ -37,6 +39,7 @@ export class MemberData extends foundry.abstract.DataModel { }), rollChoice: new fields.StringField({ nullable: true, initial: null }), rollData: new fields.JSONField({ nullable: true, initial: null }), + damageRollData: new fields.EmbeddedDataField(ChatDamageData), selected: new fields.BooleanField({ initial: false }) }; } diff --git a/module/dice/baseRoll.mjs b/module/dice/baseRoll.mjs index 4d065fff..2c42d38a 100644 --- a/module/dice/baseRoll.mjs +++ b/module/dice/baseRoll.mjs @@ -4,4 +4,8 @@ export default class BaseRoll extends Roll { /** @inheritdoc */ static TOOLTIP_TEMPLATE = 'systems/daggerheart/templates/ui/chat/foundryRollTooltip.hbs'; + + get modifierTotal() { + return this.total - this.dice.reduce((acc, dice) => acc + dice.total, 0); + } } diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs index 3f2f79e0..683d14d8 100644 --- a/module/dice/damageRoll.mjs +++ b/module/dice/damageRoll.mjs @@ -13,36 +13,34 @@ export default class DamageRoll extends DHRoll { static DefaultDialog = DamageDialog; + static createRollInstance(config) { + return new this(undefined, config.data, config); + } + /** @inheritdoc */ - static async buildEvaluate(roll, config = {}, message = {}) { - if (config.dialog.configure === false) roll.constructFormula(config); - for (const roll of config.roll) await roll.roll.evaluate(); - - roll._evaluated = true; - - const parts = []; - for (const rollData of config.roll) { - const roll = rollData.roll; - parts.push({ - ...rollData, - ...roll.options.roll, - total: roll.total, - formula: roll.formula, - dice: roll.dice.map(d => ({ - dice: d.denomination, - total: d.total, - formula: d.formula, - results: d.results - })), - damageTypes: [...(rollData.damageTypes ?? [])], - roll, - type: config.type, - modifierTotal: this.calculateTotalModifiers(roll) - }); - rollData.roll = JSON.stringify(roll.toJSON()); + static async buildEvaluate(roll, config = {}) { + if (config.dialog.configure === false) roll.constructFormulas(config); + + const evaluateRoll = async roll => { + await roll.roll.evaluate(); + roll.roll.options = { damageTypes: roll.damageTypes ? [...roll.damageTypes] : [] }; + return roll.roll; } - config.damage = this.unifyDamageRoll(parts); + if (!config.damage) config.damage = { main: null, resources: {} }; + + if (config.damageFormula) { + config.damage.main = await evaluateRoll(config.damageFormula); + config.damage.main.options = { damageTypes: + config.damageFormula.damageTypes ? [...config.damageFormula.damageTypes] : [] + }; + } + + for (const roll of config.resourceFormulas) { + config.damage.resources[roll.applyTo] = await evaluateRoll(roll); + } + + roll._evaluated = true; } static async buildPost(roll, config, message) { @@ -53,9 +51,10 @@ export default class DamageRoll extends DHRoll { const diceRolls = []; if (game.modules.get('dice-so-nice')?.active) { config.mute = true; - const pool = foundry.dice.terms.PoolTerm.fromRolls( - Object.values(config.damage).flatMap(r => r.parts.map(p => p.roll)) - ); + const pool = foundry.dice.terms.PoolTerm.fromRolls([ + ...(config.damage.main ? [config.damage.main] : []), + ...Object.values(config.damage.resources) + ]); diceRolls.push(Roll.fromTerms([pool])); } @@ -66,22 +65,14 @@ export default class DamageRoll extends DHRoll { await super.buildPost(roll, config, message); if (config.source?.message) { - chatMessage.update({ 'system.damage': config.damage }); + chatMessage.update({ 'system.damage': { + ...config.damage.toObject(), + main: config.damage.main, + resources: config.damage.resources + }}); } } - static unifyDamageRoll(rolls) { - const unified = {}; - rolls.forEach(r => { - const resource = unified[r.applyTo] ?? { formula: '', total: 0, parts: [] }; - resource.formula += `${resource.formula !== '' ? ' + ' : ''}${r.formula}`; - resource.total += r.total; - resource.parts.push(r); - unified[r.applyTo] = resource; - }); - return unified; - } - static formatGlobal(rolls) { let formula, total; const applyTo = new Set(rolls.flatMap(r => r.applyTo)); @@ -130,12 +121,10 @@ export default class DamageRoll extends DHRoll { const type = this.options.messageType ?? (this.options.hasHealing ? 'healing' : 'damage'); const changeKeys = []; - for (const roll of this.options.roll) { - for (const damageType of roll.damageTypes?.values?.() ?? []) { - changeKeys.push(`system.bonuses.${type}.${damageType}`); - } + for (const damageType of this.options.damageFormula?.damageTypes?.values?.() ?? []) { + changeKeys.push(`system.bonuses.${type}.${damageType}`); } - + const item = this.data.parent?.items?.get(this.options.source.item); if (item) { switch (item.type) { @@ -151,62 +140,69 @@ export default class DamageRoll extends DHRoll { return changeKeys; } - constructFormula(config) { + constructFormulas(config) { + return { + damageFormula: this.constructFormula(this.options.damageFormula, config, true), + resourceFormulas: this.options.resourceFormulas.map(x => this.constructFormula(x, config)) + }; + } + + constructFormula(formulaData, config, isDamage) { + if (!formulaData) return null; this.options.isCritical = config.isCritical; - for (const [index, part] of this.options.roll.entries()) { - const isHitpointPart = part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id; - part.roll = new Roll(Roll.replaceFormulaData(part.formula, config.data)); - part.roll.terms = Roll.parse(part.roll.formula, config.data); - if (part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) { - part.modifiers = this.applyBaseBonus(part); - this.addModifiers(part); - part.modifiers?.forEach(m => { - part.roll.terms.push(...this.formatModifier(m.value)); - }); + + formulaData.roll = new Roll(Roll.replaceFormulaData(formulaData.formula, config.data)); + formulaData.roll.terms = Roll.parse(formulaData.roll.formula, config.data); + + if (formulaData.extraFormula) { + formulaData.roll.terms.push( + new foundry.dice.terms.OperatorTerm({ operator: '+' }), + ...this.constructor.parse(formulaData.extraFormula, this.options.data) + ); + } + + if (isDamage && formulaData.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) { + formulaData.modifiers = this.applyBaseBonus(formulaData); + this.addModifiers(formulaData); + formulaData.modifiers?.forEach(m => { + formulaData.roll.terms.push(...this.formatModifier(m.value)); + }); + + /* To Remove When Reaction System */ + for (const mod in config.modifiers) { + const modifier = config.modifiers[mod]; + if ( + modifier.beforeCrit === true && + (modifier.enabled || modifier.value) + ) modifier.callback(formulaData); } /* 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); - } + for (const mod in config.modifiers) { + const modifier = config.modifiers[mod]; + if (!modifier.beforeCrit && (modifier.enabled || modifier.value)) modifier.callback(formulaData); } - if (part.extraFormula) { - part.roll.terms.push( - new foundry.dice.terms.OperatorTerm({ operator: '+' }), - ...this.constructor.parse(part.extraFormula, this.options.data) - ); - } - - if (config.damageOptions.groupAttack?.numAttackers > 1 && isHitpointPart) { + if (config.damageOptions.groupAttack?.numAttackers > 1) { const damageTypes = [foundry.dice.terms.Die, foundry.dice.terms.NumericTerm]; - for (const term of part.roll.terms) { + for (const term of formulaData.roll.terms) { if (damageTypes.some(type => term instanceof type)) { term.number *= config.damageOptions.groupAttack.numAttackers; } } } - if (config.isCritical && isHitpointPart) { - const total = part.roll.dice.reduce((acc, term) => acc + term._faces * term._number, 0); + if (config.isCritical) { + const total = formulaData.roll.dice.reduce((acc, term) => acc + term._faces * term._number, 0); if (total > 0) { - part.roll.terms.push(...this.formatModifier(total)); + formulaData.roll.terms.push(...this.formatModifier(total)); } } - - /* 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); - } - } - - part.roll._formula = this.constructor.getFormula(part.roll.terms); } - return this.options.roll; + + formulaData.roll._formula = this.constructor.getFormula(formulaData.roll.terms); + + return formulaData; } /* To Remove When Reaction System */ @@ -299,76 +295,4 @@ export default class DamageRoll extends DHRoll { config.modifiers = mods; return mods; } - - static async reroll(rollPart, dice, result) { - let diceIndex = 0; - let parsedRoll = game.system.api.dice.DamageRoll.fromData({ - ...rollPart.roll, - terms: rollPart.roll.terms.map(term => { - const isDie = term.class === 'Die'; - const fixedTerm = { - ...term, - ...(isDie ? { results: rollPart.dice[diceIndex].results } : {}) - }; - - if (isDie) diceIndex++; - return fixedTerm; - }), - class: 'DamageRoll', - evaluated: false - }); - - const parsedDiceTerms = Object.keys(parsedRoll.terms).reduce((acc, key) => { - const term = parsedRoll.terms[key]; - if (term instanceof CONFIG.Dice.termTypes.DiceTerm) acc[Object.keys(acc).length] = term; - return acc; - }, {}); - const term = parsedDiceTerms[dice]; - const termResult = parsedDiceTerms[dice].results[result]; - - const newIndex = parsedDiceTerms[dice].results.length; - await term.reroll(`/r1=${termResult.result}`); - - const diceRolls = []; - if (game.modules.get('dice-so-nice')?.active) { - const newResult = parsedDiceTerms[dice].results[newIndex]; - diceRolls.push({ - _evaluated: true, - dice: [ - new foundry.dice.terms.Die({ - ...term, - total: newResult.result, - faces: term._faces, - results: [newResult] - }) - ], - options: { appearance: {} } - }); - } - - await triggerChatRollFx(diceRolls); - await parsedRoll.evaluate(); - - const results = parsedRoll.dice[dice].results.map(result => ({ - ...result, - discarded: !result.active - })); - const newResult = results.splice(results.length - 1, 1); - results.splice(Number(result) + 1, 0, newResult[0]); - - const rerolledDice = parsedRoll.dice.map((x, index) => { - const isRerollDice = index === Number(dice); - if (!isRerollDice) return { ...x, dice: x.denomination }; - return { - dice: parsedRoll.dice[dice].denomination, - total: parsedRoll.dice[dice].total, - results: results.map(result => ({ - ...result, - hasRerolls: result.hasRerolls || isRerollDice - })) - }; - }); - - return { parsedRoll, rerolledDice }; - } } diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index 16472fea..c78caa4f 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -1,7 +1,8 @@ import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs'; import { triggerChatRollFx } from '../helpers/utils.mjs'; +import BaseRoll from './baseRoll.mjs'; -export default class DHRoll extends Roll { +export default class DHRoll extends BaseRoll { baseTerms = []; constructor(formula, data = {}, options = {}) { super(formula, data, foundry.utils.mergeObject(options, { roll: [] }, { overwrite: false })); @@ -40,6 +41,10 @@ export default class DHRoll extends Roll { return config; } + static createRollInstance(config) { + return new this(config.roll.formula, config.data, config); + } + /** * @param {Partial} config * @returns {Promise} @@ -57,7 +62,7 @@ export default class DHRoll extends Roll { this.temporaryModifierBuilder(config); - let roll = new this(config.roll.formula, config.data, config); + let roll = this.createRollInstance(config); if (config.dialog.configure !== false) { // Open Roll Dialog const DialogClass = config.dialog?.class ?? this.DefaultDialog; diff --git a/module/dice/die/_module.mjs b/module/dice/die/_module.mjs index 19ca951a..b84cef86 100644 --- a/module/dice/die/_module.mjs +++ b/module/dice/die/_module.mjs @@ -3,6 +3,7 @@ import HopeDie from './hopeDie.mjs'; import FearDie from './fearDie.mjs'; import AdvantageDie from './advantageDie.mjs'; import DisadvantageDie from './disadvantageDie.mjs'; +export { default as BaseDie } from './baseDie.mjs'; export const diceTypes = { DualityDie, diff --git a/module/dice/die/advantageDie.mjs b/module/dice/die/advantageDie.mjs index 9c2f0b03..66b6f124 100644 --- a/module/dice/die/advantageDie.mjs +++ b/module/dice/die/advantageDie.mjs @@ -1,4 +1,6 @@ -export default class AdvantageDie extends foundry.dice.terms.Die { +import BaseDie from './baseDie.mjs'; + +export default class AdvantageDie extends BaseDie { constructor(options) { super(options); diff --git a/module/dice/die/baseDie.mjs b/module/dice/die/baseDie.mjs new file mode 100644 index 00000000..cc88f753 --- /dev/null +++ b/module/dice/die/baseDie.mjs @@ -0,0 +1,12 @@ +export default class BaseDie extends foundry.dice.terms.Die { + async rerollResult(resultIndex) { + const result = this.results[resultIndex]; + result.rerolled = true; + result.active = false; + await this.roll({ reroll: true }); + + const rerolledResult = this.results[this.results.length - 1]; + this.results.splice(this.results.length - 1, 1); + this.results.splice(resultIndex, 0, rerolledResult); + } +} \ No newline at end of file diff --git a/module/dice/die/disadvantageDie.mjs b/module/dice/die/disadvantageDie.mjs index f56ebe96..e79845f3 100644 --- a/module/dice/die/disadvantageDie.mjs +++ b/module/dice/die/disadvantageDie.mjs @@ -1,4 +1,6 @@ -export default class DisadvantageDie extends foundry.dice.terms.Die { +import BaseDie from './baseDie.mjs'; + +export default class DisadvantageDie extends BaseDie { constructor(options) { super(options); diff --git a/module/dice/die/dualityDie.mjs b/module/dice/die/dualityDie.mjs index cc7ee75e..2f4dae77 100644 --- a/module/dice/die/dualityDie.mjs +++ b/module/dice/die/dualityDie.mjs @@ -1,6 +1,7 @@ +import BaseDie from './baseDie.mjs'; import { updateResourcesForDualityReroll } from '../helpers.mjs'; -export default class DualityDie extends foundry.dice.terms.Die { +export default class DualityDie extends BaseDie { constructor(options) { super(options); diff --git a/module/dice/dualityRoll.mjs b/module/dice/dualityRoll.mjs index 38d9315f..6503efb1 100644 --- a/module/dice/dualityRoll.mjs +++ b/module/dice/dualityRoll.mjs @@ -409,8 +409,4 @@ export default class DualityRoll extends D20Roll { return rerolled; } - - fromJSON(json) { - return super.fromJSON(json); - } } diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index f8880597..666d9ac1 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -622,6 +622,11 @@ export default class DhpActor extends Actor { return rollData; } + /** + * Checks to see if damage can be reduced in one way or another. + * @param {number} hpDamage the amount of marked hp that will be marked + * @param {Set} types a list of damage types + */ #canReduceDamage(hpDamage, types) { const { stressDamageReduction, disabledArmor, reduceSeverity, thresholdImmunities } = this.system.rules.damageReduction; @@ -648,35 +653,32 @@ export default class DhpActor extends Actor { return canUseArmor || canUseStress || hasReduceSeverity || hasThresholdImmunity; } - async takeDamage(damages, isDirect = false) { - if (Hooks.call(`${CONFIG.DH.id}.preTakeDamage`, this, damages) === false) return null; + async takeDamage(args, isDirect = false) { + args = this.#parseDamageArgs(args); + if (Hooks.call(`${CONFIG.DH.id}.preTakeDamage`, this, args) === false) return null; if (this.type === 'companion') { await this.modifyResource([{ value: 1, key: 'stress' }]); return; } - const updates = []; + const updates = args.resourceUpdates; + if (args.main) { + // todo: avoid side effects, but hook currently requires it + args.main.value = this.calculateDamage(args.main.value, args.main.damageTypes); + } - Object.entries(damages).forEach(([key, damage]) => { - damage.parts.forEach(part => { - if (part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) - part.total = this.calculateDamage(part.total, part.damageTypes); - const update = updates.find(u => u.key === key); - if (update) { - update.value += part.total; - update.damageTypes.add(...new Set(part.damageTypes)); - } else updates.push({ value: part.total, key, damageTypes: new Set(part.damageTypes) }); - }); - }); + if (Hooks.call(`${CONFIG.DH.id}.postCalculateDamage`, this, args) === false) return null; - if (Hooks.call(`${CONFIG.DH.id}.postCalculateDamage`, this, damages) === false) return null; + // Convert deducted resources to a record of updates. Return if nothing to do. + if (!updates.some(u => u.value) && !args.main) return; - if (!updates.length) return; - - const hpDamage = updates.find(u => u.key === CONFIG.DH.GENERAL.healingTypes.hitPoints.id); - if (hpDamage?.value) { - hpDamage.value = this.convertDamageToThreshold(hpDamage.value); + if (args.main) { + const hpDamage = { + value: this.convertDamageToThreshold(args.main.value), + damageTypes: new Set(args.main.damageTypes), + key: CONFIG.DH.GENERAL.healingTypes.hitPoints.id + }; if (this.type === 'character' && !isDirect && this.#canReduceDamage(hpDamage.value, hpDamage.damageTypes)) { const armorSlotResult = await this.owner.query( 'armorSlot', @@ -691,7 +693,7 @@ export default class DhpActor extends Actor { ); if (armorSlotResult) { const { modifiedDamage, armorChanges, stressSpent } = armorSlotResult; - updates.find(u => u.key === 'hitPoints').value = modifiedDamage; + hpDamage.value = modifiedDamage; for (const armorChange of armorChanges) { updates.push({ value: armorChange.amount, key: 'armor', uuid: armorChange.uuid }); } @@ -701,20 +703,24 @@ export default class DhpActor extends Actor { else updates.push({ value: stressSpent, key: 'stress' }); } } - } - if (this.type === 'adversary') { + } else if (this.type === 'adversary') { const reducedSeverity = hpDamage.damageTypes.reduce((value, curr) => { return Math.max(this.system.rules.damageReduction.reduceSeverity[curr], value); }, 0); hpDamage.value = Math.max(hpDamage.value - reducedSeverity, 0); - - if ( - hpDamage.value && - this.system.rules.damageReduction.thresholdImmunities[getDamageKey(hpDamage.value)] - ) { - hpDamage.value -= 1; + if (this.system.rules.damageReduction.thresholdImmunities[getDamageKey(hpDamage.value)]) { + hpDamage.value = Math.max(0, hpDamage.value - 1); } } + + // Merge existing hitPoint deduction with finalised damage deduction + const existing = updates.find(u => u.key === CONFIG.DH.GENERAL.healingTypes.hitPoints.id); + if (existing) { + existing.value += hpDamage.value; + existing.damageTypes = hpDamage.damageTypes; + } else { + updates.push(hpDamage); + } } const results = await game.system.registeredTriggers.runTrigger( @@ -729,12 +735,10 @@ export default class DhpActor extends Actor { for (var result of results) resourceMap.addResources(result.updates); resourceMap.updateResources(); } - - updates.forEach( - u => - (u.value = - u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false ? u.value * -1 : u.value) - ); + + for (const u of updates) { + u.value = u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false ? u.value * -1 : u.value; + } await this.modifyResource(updates); @@ -743,6 +747,36 @@ export default class DhpActor extends Actor { return updates; } + async takeHealing(args) { + args = this.#parseDamageArgs({ resources: 'resources' in args ? args.resources : args }); + if (Hooks.call(`${CONFIG.DH.id}.preTakeHealing`, this, args) === false) return null; + + const updates = args.resourceUpdates; + for (const u of updates) { + const shouldFlip = !(u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false); + u.value = shouldFlip ? u.value * -1 : u.value; + } + await this.modifyResource(updates); + + if (Hooks.call(`${CONFIG.DH.id}.postTakeHealing`, this, updates) === false) return null; + + return updates; + } + + /** Parse damage args that may be coming from takeHealing or takeDamage. Used to simplify macro usage and roll vs non-roll usage */ + #parseDamageArgs(args = {}) { + const damageRoll = 'total' in args ? args : (args.main ?? args.damage); + const damageValue = typeof damageRoll === 'number' ? damageRoll : damageRoll?.total; + const damageTypes = Array.from(damageRoll?.options?.damageTypes ?? damageRoll?.damageTypes ?? []); + const main = typeof damageValue === 'number' ? { key: 'damage', value: damageValue, damageTypes } : null; + const resourceUpdates = Object.entries(args.resources ?? {}).map(([key, damage]) => ({ + key, + value: typeof damage === 'number' ? damage : damage?.total ?? 0 + })); + + return { main, resourceUpdates }; + } + calculateDamage(baseDamage, type) { if (this.canResist(type, 'immunity')) return 0; if (this.canResist(type, 'resistance')) baseDamage = Math.ceil(baseDamage / 2); @@ -767,32 +801,6 @@ export default class DhpActor extends Actor { return reduction === Infinity ? 0 : reduction; } - async takeHealing(healings) { - if (Hooks.call(`${CONFIG.DH.id}.preTakeHealing`, this, healings) === false) return null; - - const updates = []; - Object.entries(healings).forEach(([key, healing]) => { - healing.parts.forEach(part => { - const update = updates.find(u => u.key === key); - if (update) update.value += part.total; - else updates.push({ value: part.total, key }); - }); - }); - - updates.forEach( - u => - (u.value = !(u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false) - ? u.value * -1 - : u.value) - ); - - await this.modifyResource(updates); - - if (Hooks.call(`${CONFIG.DH.id}.postTakeHealing`, this, updates) === false) return null; - - return updates; - } - /** * Resources are modified asynchronously, so be careful not to update the same resource in * quick succession. diff --git a/module/helpers/handlebarsHelper.mjs b/module/helpers/handlebarsHelper.mjs index 7f30d970..dbcc50dc 100644 --- a/module/helpers/handlebarsHelper.mjs +++ b/module/helpers/handlebarsHelper.mjs @@ -48,9 +48,8 @@ export default class RegisterHandlebarsHelpers { return formula; } - static damageSymbols(damageParts) { - const allTypes = [...new Set([...damageParts].flatMap(x => Array.from(x.type)))]; - const symbols = allTypes.map(p => CONFIG.DH.GENERAL.damageTypes[p].icon); + static damageSymbols(damageData) { + const symbols = damageData.type.map(p => CONFIG.DH.GENERAL.damageTypes[p].icon); return new Handlebars.SafeString(Array.from(symbols).map(symbol => ``)); } diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 84bcacf2..f600eae6 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -700,19 +700,6 @@ export async function RefreshFeatures( return refreshedActors; } -export function getUnusedDamageTypes(parts) { - const usedKeys = Object.keys(parts); - return Object.keys(CONFIG.DH.GENERAL.healingTypes).reduce((acc, key) => { - if (!usedKeys.includes(key)) - acc.push({ - value: key, - label: game.i18n.localize(CONFIG.DH.GENERAL.healingTypes[key].label) - }); - - return acc; - }, []); -} - /** Returns resolved armor sources ordered by application order */ export function getArmorSources(actor) { const rawArmorSources = Array.from(actor.allApplicableEffects()).filter(x => x.system.armorData); diff --git a/module/systemRegistration/migration-handlers/2_5_2.mjs b/module/systemRegistration/migration-handlers/2_5_2.mjs index 944f0eec..096f4bb7 100644 --- a/module/systemRegistration/migration-handlers/2_5_2.mjs +++ b/module/systemRegistration/migration-handlers/2_5_2.mjs @@ -7,7 +7,8 @@ export class Migration_2_5_2 extends MigrationHandlerBase { async updateActiveEffectSource(effectSource, item) { let shouldUpdate = false; const newChanges = []; - const srdItem = item?._stats.compendiumSource ? + + const srdItem = item?._stats?.compendiumSource ? await foundry.utils.fromUuid(item?._stats.compendiumSource) : null; for (let i = 0; i < effectSource.system.changes.length; i++) { diff --git a/module/systemRegistration/migration-handlers/2_6_0.mjs b/module/systemRegistration/migration-handlers/2_6_0.mjs new file mode 100644 index 00000000..9edda9a2 --- /dev/null +++ b/module/systemRegistration/migration-handlers/2_6_0.mjs @@ -0,0 +1,20 @@ +import { MigrationHandlerBase } from './base.mjs'; + +export class Migration_2_6_0 extends MigrationHandlerBase { + version = '2.6.0'; + + /** @inheritdoc */ + async updateActorSource(actor) { + if (actor.type === 'party' && Object.keys(actor.system.tagTeam.members).length) { + return { + _id: actor._id, + system: { + tagTeam: { + initiator: null, + members: _replace({}) + } + } + }; + } + } +} \ No newline at end of file diff --git a/module/systemRegistration/migration-handlers/base.mjs b/module/systemRegistration/migration-handlers/base.mjs index 7426570d..29c181ce 100644 --- a/module/systemRegistration/migration-handlers/base.mjs +++ b/module/systemRegistration/migration-handlers/base.mjs @@ -1,8 +1,9 @@ /** * @import DHItem from "../../documents/item.mjs"; +* @import DhActor from "../../documents/actor.mjs"; */ - /** + * The base class of an async migration. * These are generally run between versions for things that require compendiums or must be done in post. * The migrate() functions calls the various updateXSource() functions. @@ -22,6 +23,16 @@ export class MigrationHandlerBase { return null; } + /** + * Update a world actor + * @param {DhActor} actor + * @returns {Promise} + * @protected + */ + async updateActorSource(actor) { + return null; + } + async migrate() { // todo: handle more than just migrating effects. Right now this can only migrate effects // NOTE: the preload is hardcoded, we should not hardcode it @@ -60,10 +71,39 @@ export class MigrationHandlerBase { } }; - for (const actor of game.actors) { + const updateActor = async actor => { + const actorUpdate = await this.updateActorSource(actor); + if (actorUpdate) { + batch.push({ + action: 'update', + documentName: 'Actor', + updates: [actorUpdate] + }); + } + + const aeUpdates = []; for (const item of actor.items) { await updateItem(item); } + + for (const effect of actor.effects) { + const changes = await this.updateActiveEffectSource(effect.toObject(), { parent: actor }); + if (changes) aeUpdates.push(changes); + } + if (aeUpdates.length) { + batch.push({ + action: 'update', + documentName: 'ActiveEffect', + updates: aeUpdates, + parent: actor + }); + } + } + + + for (const actor of game.actors) { + await updateActor(actor); + progress.advance(); } for (const item of game.items) { diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs index fef97b8f..af11cd2c 100644 --- a/module/systemRegistration/migrations.mjs +++ b/module/systemRegistration/migrations.mjs @@ -1,5 +1,6 @@ import { defaultRestOptions } from '../config/generalConfig.mjs'; import { Migration_2_5_2 } from './migration-handlers/2_5_2.mjs'; +import { Migration_2_6_0 } from './migration-handlers/2_6_0.mjs'; export async function runMigrations() { let lastMigrationVersion = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion); @@ -329,7 +330,8 @@ export async function runMigrations() { /* -------------------------------------------- */ const migrations = [ - new Migration_2_5_2() + new Migration_2_5_2(), + new Migration_2_6_0() ].filter(m => m.version && foundry.utils.isNewerVersion(m.version, lastMigrationVersion)); for (const handler of migrations) { diff --git a/styles/less/dialog/damage-selection/sheet.less b/styles/less/dialog/damage-selection/sheet.less index 9f8cfc8a..0bb0f2be 100644 --- a/styles/less/dialog/damage-selection/sheet.less +++ b/styles/less/dialog/damage-selection/sheet.less @@ -17,6 +17,13 @@ } } + .section-header { + font-size: var(--font-size-20); + color: light-dark(@dark, @beige); + text-align: center; + margin-bottom: -12px; + } + .bonuses { gap: 4px; .critical-chip { diff --git a/templates/actionTypes/cost.hbs b/templates/actionTypes/cost.hbs index 7a9f33d9..b8661b5b 100644 --- a/templates/actionTypes/cost.hbs +++ b/templates/actionTypes/cost.hbs @@ -9,7 +9,7 @@ {{/if}}
    {{formField ../fields.scalable label="DAGGERHEART.GENERAL.scalable" value=cost.scalable name=(concat "cost." index ".scalable") classes="checkbox" localize=true}} - {{formField ../fields.key choices=(@root.disableOption index @root.costOptions ../source) label="DAGGERHEART.GENERAL.resource" value=cost.key name=(concat "cost." index ".key") localize=true blank=false}} + {{formField ../fields.key choices=(@root.disableOption index @root.costOptions ../source) label="DAGGERHEART.GENERAL.Resource.single" value=cost.key name=(concat "cost." index ".key") localize=true blank=false}} {{formField ../fields.value label="DAGGERHEART.GENERAL.amount" value=cost.value name=(concat "cost." index ".value") localize=true}} {{formField ../fields.step label="DAGGERHEART.GENERAL.step" value=cost.step name=(concat "cost." index ".step") disabled=(not cost.scalable) localize=true}} diff --git a/templates/actionTypes/damage.hbs b/templates/actionTypes/damage.hbs index 03300840..d430b51f 100644 --- a/templates/actionTypes/damage.hbs +++ b/templates/actionTypes/damage.hbs @@ -1,92 +1,114 @@ - -
    - - {{#if (eq @root.source.type 'healing')}} - {{localize "DAGGERHEART.GENERAL.healing"}} - {{else}} +{{#unless (eq @root.source.type 'healing')}} +
    + {{localize "DAGGERHEART.GENERAL.damage"}} + {{#if source.main}} + + {{else}} + + {{/if}} + + + {{#if source.main}} +
    + {{#if @root.hasBaseDamage}} + {{formField @root.fields.damage.fields.main.fields.includeBase value=source.main.includeBase name="damage.main.includeBase" classes="checkbox" localize=true }} + {{/if}} + {{#unless (eq @root.source.type 'healing')}} + {{formField baseFields.main.fields.direct value=source.main.direct name=(concat path "damage.main.direct") localize=true classes="checkbox"}} + {{/unless}} + {{#if (and @root.isNPC (not (eq path 'system.attack.')))}} + {{formField baseFields.main.fields.groupAttack value=source.main.groupAttack name=(concat path "damage.main.groupAttack") localize=true classes="select"}} + {{/if}} +
    + {{> damageData damage=source.main fields=fields.main.fields basePath=(concat path "damage.main")}} + {{#if horde}} + {{> hordeDamage source=source.main fields=fields.main.fields basePath=(concat path "damage.main")}} + {{/if}} + {{#if (ne @root.source.type 'healing')}} + {{formField fields.main.fields.type value=source.main.type name=(concat path "damage.main.type") localize=true}} + {{/if}} {{/if}} - {{#unless (eq path 'system.attack.')}}{{/unless}} - -
    - {{#if @root.hasBaseDamage}} - {{formField @root.fields.damage.fields.includeBase value=@root.source.damage.includeBase name="damage.includeBase" classes="checkbox" localize=true }} - {{/if}} - {{#unless (eq @root.source.type 'healing')}} - {{formField baseFields.direct value=source.direct name=(concat path "damage.direct") localize=true classes="checkbox"}} - {{/unless}} - {{#if (and @root.isNPC (not (eq path 'system.attack.')))}} - {{formField baseFields.groupAttack value=source.groupAttack name=(concat path "damage.groupAttack") localize=true classes="select"}} - {{/if}} -
    +
    +{{/unless}} - {{!-- Handlebars uses Symbol.Iterator to produce index|key. This isn't compatible with our parts object, so we instead use applyTo, which is the same value --}} - {{#each source.parts as |dmg key|}} -
    - - - {{localize (concat "DAGGERHEART.CONFIG.HealingType." dmg.applyTo ".name")}} - {{#unless (or dmg.base ../path)}} - - {{/unless}} - - - {{#if (and (not @root.isNPC) @root.hasRoll (not dmg.base))}} - {{formField ../fields.resultBased value=dmg.resultBased name=(concat "damage.parts." dmg.applyTo ".resultBased") localize=true classes="checkbox"}} - {{/if}} - {{#if (and (not @root.isNPC) @root.hasRoll (not dmg.base) dmg.resultBased)}} -
    -
    - {{localize "DAGGERHEART.GENERAL.withThing" thing=(localize "DAGGERHEART.GENERAL.hope")}} - {{> formula fields=../fields.value.fields type=../fields.type dmg=dmg source=dmg.value target="value" key=dmg.applyTo path=../path}} -
    -
    - {{localize "DAGGERHEART.GENERAL.withThing" thing=(localize "DAGGERHEART.GENERAL.fear")}} - {{> formula fields=../fields.valueAlt.fields type=../fields.type dmg=dmg source=dmg.valueAlt target="valueAlt" key=dmg.applyTo path=../path}} -
    -
    - {{else}} - {{> formula fields=../fields.value.fields type=../fields.type dmg=dmg source=dmg.value target="value" key=dmg.applyTo path=../path}} - {{/if}} - - {{#if (and (eq dmg.applyTo 'hitPoints') (ne @root.source.type 'healing'))}} - {{formField ../fields.type value=dmg.type name=(concat ../path "damage.parts." dmg.applyTo ".type") localize=true}} - {{/if}} - - {{#if ../horde}} -
    - {{localize "DAGGERHEART.ACTORS.Adversary.hordeDamage"}} -
    - - {{formField ../fields.valueAlt.fields.flatMultiplier value=dmg.valueAlt.flatMultiplier name=(concat ../path "damage.parts." dmg.applyTo ".valueAlt.flatMultiplier") label="DAGGERHEART.ACTIONS.Settings.multiplier" classes="inline-child" localize=true }} - {{formField ../fields.valueAlt.fields.dice value=dmg.valueAlt.dice name=(concat ../path "damage.parts." dmg.applyTo ".valueAlt.dice") classes="inline-child" localize=true}} - {{formField ../fields.valueAlt.fields.bonus value=dmg.valueAlt.bonus name=(concat ../path "damage.parts." dmg.applyTo ".valueAlt.bonus") localize=true classes="inline-child"}} -
    -
    - {{/if}} - -
    -
    - {{/each}} - +{{#unless (eq path 'system.attack.')}} + {{! In the future, consider allowing this even on NPCs}} +
    + + {{#if (eq @root.source.type 'healing')}} + {{localize "DAGGERHEART.GENERAL.healing"}} + {{else}} + {{localize "DAGGERHEART.ACTIONS.Config.damage.markResources"}} + {{/if}} + {{#unless @root.allDamageTypesUsed}}{{/unless}} + + {{#each source.resources as |dmg key|}} +
    + + + {{localize (concat "DAGGERHEART.CONFIG.HealingType." dmg.applyTo ".name")}} + {{#unless (or dmg.base ../path)}} + + {{/unless}} + + {{> damageData damage=dmg fields=../fields.resources.element.fields basePath=(concat ../path "damage.resources." dmg.applyTo)}} +
    + + {{/each}} + +{{/unless}} {{#*inline "formula"}} - {{#unless dmg.base}} - {{formField fields.custom.fields.enabled value=source.custom.enabled name=(concat path "damage.parts." key "." target ".custom.enabled") classes="checkbox" localize=true}} - {{/unless}} - {{#if source.custom.enabled}} - {{formField fields.custom.fields.formula value=source.custom.formula name=(concat path "damage.parts." key "." target ".custom.formula") localize=true}} - {{else}} -
    - {{#unless @root.isNPC}} - {{formField fields.multiplier value=source.multiplier name=(concat path "damage.parts." key "." target ".multiplier") localize=true}} - {{/unless}} - {{#if (eq source.multiplier 'flat')}}{{formField fields.flatMultiplier value=source.flatMultiplier name=(concat path "damage.parts." key "." target ".flatMultiplier") localize=true }}{{/if}} - {{formField fields.dice value=source.dice name=(concat path "damage.parts." key "." target ".dice") localize=true}} - {{formField fields.bonus value=source.bonus name=(concat path "damage.parts." key "." target ".bonus") localize=true}} -
    - {{/if}} - {{#if @root.isNPC}} - - {{/if}} + {{#unless isBase}} + {{formField fields.custom.fields.enabled value=source.custom.enabled name=(concat basePath ".custom.enabled") classes="checkbox" localize=true}} + {{/unless}} + {{#if source.custom.enabled}} + {{formField fields.custom.fields.formula value=source.custom.formula name=(concat basePath ".custom.formula") localize=true}} + {{else}} +
    + {{#unless @root.isNPC}} + {{formField fields.multiplier value=source.multiplier name=(concat basePath ".multiplier") localize=true}} + {{/unless}} + {{#if (eq source.multiplier 'flat')}}{{formField fields.flatMultiplier value=source.flatMultiplier name=(concat basePath ".flatMultiplier") localize=true }}{{/if}} + {{formField fields.dice value=source.dice name=(concat basePath ".dice") localize=true}} + {{formField fields.bonus value=source.bonus name=(concat basePath ".bonus") localize=true}} +
    + {{/if}} + {{#if @root.isNPC}} + + {{/if}} +{{/inline}} + +{{#*inline "damageData"}} + {{#if (and (not @root.isNPC) @root.hasRoll (not damage.base))}} + {{formField fields.resultBased value=damage.resultBased name=(concat basePath ".resultBased") localize=true classes="checkbox"}} + {{/if}} + {{#if (and (not @root.isNPC) @root.hasRoll (not damage.base) damage.resultBased)}} +
    +
    + {{localize "DAGGERHEART.GENERAL.withThing" thing=(localize "DAGGERHEART.GENERAL.hope")}} + {{> formula key=damage.applyTo fields=fields.value.fields type=fields.type isBase=damage.base source=damage.value basePath=(concat basePath ".value")}} +
    +
    + {{localize "DAGGERHEART.GENERAL.withThing" thing=(localize "DAGGERHEART.GENERAL.fear")}} + {{> formula key=damage.applyTo fields=fields.valueAlt.fields type=fields.type isBase=damage.base source=damage.valueAlt basePath=(concat basePath ".valueAlt")}} +
    +
    + {{else}} + {{> formula key=damage.applyTo fields=fields.value.fields type=fields.type isBase=damage.base source=damage.value basePath=(concat basePath ".value")}} + {{/if}} + +{{/inline}} + +{{#*inline "hordeDamage"}} +
    + {{localize "DAGGERHEART.ACTORS.Adversary.hordeDamage"}} +
    + + {{formField fields.valueAlt.fields.flatMultiplier value=source.valueAlt.flatMultiplier name=(concat basePath ".valueAlt.flatMultiplier") label="DAGGERHEART.ACTIONS.Settings.multiplier" classes="inline-child" localize=true }} + {{formField fields.valueAlt.fields.dice value=source.valueAlt.dice name=(concat basePath ".valueAlt.dice") classes="inline-child" localize=true}} + {{formField fields.valueAlt.fields.bonus value=source.valueAlt.bonus name=(concat basePath ".valueAlt.bonus") localize=true classes="inline-child"}} +
    +
    {{/inline}} \ No newline at end of file diff --git a/templates/actionTypes/resource.hbs b/templates/actionTypes/resource.hbs index 9c8fc965..97304b9a 100644 --- a/templates/actionTypes/resource.hbs +++ b/templates/actionTypes/resource.hbs @@ -1,7 +1,7 @@
    -
    {{localize "DAGGERHEART.GENERAL.resource"}}
    +
    {{localize "DAGGERHEART.GENERAL.Resource.single"}}
    diff --git a/templates/dialogs/dice-roll/damageSelection.hbs b/templates/dialogs/dice-roll/damageSelection.hbs index 7bcd7063..e865ebe6 100644 --- a/templates/dialogs/dice-roll/damageSelection.hbs +++ b/templates/dialogs/dice-roll/damageSelection.hbs @@ -15,34 +15,7 @@ {{/each}}
    {{/if}} - - {{#each @root.formula}} -
    - {{localize "DAGGERHEART.GENERAL.formula"}}: {{roll.formula}} - - {{#with (lookup @root.config.GENERAL.healingTypes applyTo)}} - {{localize label}} - {{/with}} - {{#unless @root.hasHealing}} - {{#if damageTypes}} - {{#each damageTypes as | type | }} - {{#with (lookup @root.config.GENERAL.damageTypes type)}} - - {{/with}} - {{/each}} - {{/if}} - {{/unless}} - -
    -
    - - -
    - {{/each}} - + {{#if damageOptions.groupAttack}}
    {{localize "DAGGERHEART.ACTIONS.Settings.groupAttack.label"}} @@ -59,6 +32,45 @@
    {{/if}} + + {{#if @root.damageFormula}} + {{#with @root.damageFormula}} +
    + {{localize "DAGGERHEART.GENERAL.formula"}}: {{roll.formula}} + + {{localize "DAGGERHEART.GENERAL.damage"}} + {{#if damageTypes}} + {{#each damageTypes as | type | }} + {{#with (lookup @root.config.GENERAL.damageTypes type)}} + + {{/with}} + {{/each}} + {{/if}} + +
    +
    + + +
    + {{/with}} + {{/if}} + + {{#each @root.resourceFormulas}} +
    + {{localize "DAGGERHEART.GENERAL.formula"}}: {{roll.formula}} + + {{#with (lookup @root.config.GENERAL.healingTypes applyTo)}} + {{localize label}} + {{/with}} + +
    +
    + +
    + {{/each}} {{#unless (empty @root.modifiers)}}
    @@ -76,6 +88,7 @@ {{/each}}
    {{/unless}} +
    {{#if directDamage}} - {{/with}} + {{#with systemFields.attack.fields.damage.fields.main.fields as | fields | }} + {{#with ../document.system.attack.damage.main as | source | }} + {{localize "DAGGERHEART.GENERAL.damage"}} + {{localize "DAGGERHEART.ACTIONS.Config.general.customFormula"}} + {{formInput fields.value.fields.custom.fields.enabled value=source.value.custom.enabled name="system.attack.damage.main.value.custom.enabled"}} + {{#if source.value.custom.enabled}} + {{localize "DAGGERHEART.ACTIONS.Config.general.formula"}} + {{formInput fields.value.fields.custom.fields.formula value=source.value.custom.formula name="system.attack.damage.main.value.custom.formula"}} + {{else}} + {{localize "DAGGERHEART.GENERAL.Dice.single"}} + {{formInput fields.value.fields.dice value=source.value.dice name="system.attack.damage.main.value.dice"}} + {{localize "DAGGERHEART.GENERAL.bonus"}} + {{formInput fields.value.fields.bonus value=source.value.bonus name="system.attack.damage.main.value.bonus" localize=true}} + {{/if}} + {{localize "DAGGERHEART.GENERAL.type"}} + {{formInput fields.type value=source.type name="system.attack.damage.main.type" localize=true}} + {{localize "DAGGERHEART.CONFIG.DamageType.direct.name"}} + {{formInput @root.systemFields.attack.fields.damage.fields.main.fields.direct value=@root.document.system.attack.damage.main.direct name="system.attack.damage.main.direct" localize=true}} + + {{/with}} {{/with}}
    diff --git a/templates/ui/chat/parts/button-part.hbs b/templates/ui/chat/parts/button-part.hbs index 6bc5f372..a0075b5f 100644 --- a/templates/ui/chat/parts/button-part.hbs +++ b/templates/ui/chat/parts/button-part.hbs @@ -1,18 +1,18 @@
    {{#if areas.length}}{{/if}} {{#if hasDamage}} - {{#unless (empty damage)}} + {{#if damage.active}} {{else}} - {{/unless}} + {{/if}} {{/if}} {{#if hasHealing}} - {{#unless (empty damage)}} + {{#if damage.active}} {{else}} - {{/unless}} + {{/if}} {{/if}} {{#if (and hasEffect)}}{{/if}}
    \ 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 45b09b72..7d6d983d 100644 --- a/templates/ui/chat/parts/damage-part.hbs +++ b/templates/ui/chat/parts/damage-part.hbs @@ -1,4 +1,4 @@ -
    +
    {{#if hasHealing}} @@ -10,60 +10,79 @@
    - {{#each damage as | roll index | }} -
    {{localize (concat 'DAGGERHEART.CONFIG.HealingType.' index '.inChatRoll')}}: {{total}}
    + {{#if damage.main}} + {{> formula roll=damage.main label=(localize "DAGGERHEART.GENERAL.damage") }} + {{/if}} + {{#each damage.resources as | roll index | }} + {{> formula roll=roll label=(ifThen ../hasHealing (localize (concat 'DAGGERHEART.CONFIG.HealingType.' index '.name')) (localize (concat 'DAGGERHEART.CONFIG.HealingType.' index '.inChatRoll'))) }} {{/each}}
    - {{#each damage as | roll index | }} -
    - - {{#if ../hasHealing}}{{localize (concat 'DAGGERHEART.CONFIG.HealingType.' index '.name')}}{{else}}{{localize (concat 'DAGGERHEART.CONFIG.HealingType.' index '.inChatRoll')}}{{/if}}
    {{localize "DAGGERHEART.GENERAL.total"}}: {{roll.total}}
    {{#if (and (eq index "hitPoints") ../isDirect)}}
    {{localize "DAGGERHEART.CONFIG.DamageType.direct.short"}}
    {{/if}} -
    - {{#each roll.parts}} - {{#if (and (not @root.hasHealing) damageTypes.length)}} - - {{/if}} -
    - {{#if dice.length}} - {{#each dice}} - {{#each results}} - {{#unless discarded}} -
    -
    - {{#if hasRerolls}}{{/if}} - {{result}} -
    -
    - {{/unless}} - {{/each}} - {{/each}} - {{#if modifierTotal}} -
    -
    {{modifierTotal}}
    -
    - {{/if}} - {{else}} -
    -
    {{total}}
    -
    - {{/if}} -
    - {{/each}} -
    + {{#if damage.main}} + {{> damage label=(localize "DAGGERHEART.GENERAL.damage") roll=damage.main isDirect=isDirect }} + {{/if}} + + {{#each damage.resources as | roll index | }} + {{> damage + label=(ifThen ../hasHealing (localize (concat 'DAGGERHEART.CONFIG.HealingType.' index '.name')) (localize (concat 'DAGGERHEART.CONFIG.HealingType.' index '.inChatRoll'))) + roll=roll + isResource=true + }} {{/each}}
    -
    \ No newline at end of file +
    + +{{#*inline "formula"}} +
    {{label}}: {{roll.total}}
    +{{/inline}} + +{{#*inline "damage"}} +
    + + {{label}} +
    {{localize "DAGGERHEART.GENERAL.total"}}: {{roll.total}}
    {{#if isDirect}}
    {{localize "DAGGERHEART.CONFIG.DamageType.direct.short"}}
    {{/if}} +
    + {{#if (and (not @root.hasHealing) roll.options.damageTypes.length)}} + + {{/if}} +
    + {{#if roll.dice.length}} + {{#each roll.dice}} + {{#each results}} + {{#if active}} +
    +
    + {{#if hasRerolls}}{{/if}} + {{result}} +
    +
    + {{/if}} + {{/each}} + {{/each}} + + {{#if roll.modifierTotal}} +
    +
    {{roll.modifierTotal}}
    +
    + {{/if}} + {{else}} +
    +
    {{roll.total}}
    +
    + {{/if}} +
    +
    +{{/inline}} \ No newline at end of file diff --git a/templates/ui/tooltip/attack.hbs b/templates/ui/tooltip/attack.hbs index 8e4a1bb0..903c13a5 100644 --- a/templates/ui/tooltip/attack.hbs +++ b/templates/ui/tooltip/attack.hbs @@ -23,7 +23,7 @@
    {{/if}}
    - {{{damageFormula attack}}} {{{damageSymbols attack.damage.parts}}} + {{{damageFormula attack}}} {{{damageSymbols attack.damage.main}}}
    {{#if description}} diff --git a/templates/ui/tooltip/weapon.hbs b/templates/ui/tooltip/weapon.hbs index 4adb9c46..10889015 100644 --- a/templates/ui/tooltip/weapon.hbs +++ b/templates/ui/tooltip/weapon.hbs @@ -23,7 +23,7 @@ {{/with}}
    - {{{damageFormula item.system.attack}}} {{{damageSymbols item.system.attack.damage.parts}}} + {{{damageFormula item.system.attack}}} {{{damageSymbols item.system.attack.damage.main}}}
    {{#if description}} From 3efdcb6c9a92b8f5f861be4dc81f7405abc68e2e Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Sun, 19 Jul 2026 05:35:55 -0400 Subject: [PATCH 27/30] Allow deleting countdowns via right click context menu on the main panel (#2090) --- daggerheart.d.ts | 13 +++++ module/applications/ui/countdownEdit.mjs | 1 - module/applications/ui/countdowns.mjs | 66 +++++++++++++----------- module/config/settingsConfig.mjs | 3 ++ module/config/system.mjs | 1 + module/data/_types.d.ts | 7 +++ module/data/countdowns.mjs | 63 +++++++++++++++++++--- 7 files changed, 118 insertions(+), 36 deletions(-) create mode 100644 module/data/_types.d.ts diff --git a/daggerheart.d.ts b/daggerheart.d.ts index 891a3a2a..02abf063 100644 --- a/daggerheart.d.ts +++ b/daggerheart.d.ts @@ -11,6 +11,9 @@ import * as documents from './module/documents/_module.mjs'; import { macros } from './module/_module.mjs'; import * as dice from './module/dice/_module.mjs'; import * as fields from './module/data/fields/_module.mjs'; +import { gameSettings } from './module/config/settingsConfig.mjs'; +import DhCountdowns from './module/data/countdowns.mjs'; +import DhAutomation from './module/data/settings/Automation.mjs'; // Foundry's use of `Object.assign(globalThis) means many globally available objects are not read as such @@ -103,3 +106,13 @@ declare module '@client/packages/system.mjs' { }; } } + +declare module '@client/helpers/client-settings.mjs' { + // Add explicit typed overrides for auto complete. These require /** @type {"string"} on the vars themselves to work */ + export default interface ClientSettings { + get(namespace: 'daggerheart', key: typeof gameSettings.Automation): DhAutomation; + get(namespace: 'daggerheart', key: typeof gameSettings.Homebrew): DhHomebrew; + get(namespace: 'daggerheart', key: typeof gameSettings.Countdowns): DhCountdowns; + get(namespace: 'daggerheart', key: string): unknown; + } +} \ No newline at end of file diff --git a/module/applications/ui/countdownEdit.mjs b/module/applications/ui/countdownEdit.mjs index 1dbc56be..af1af46b 100644 --- a/module/applications/ui/countdownEdit.mjs +++ b/module/applications/ui/countdownEdit.mjs @@ -153,7 +153,6 @@ export default class CountdownEdit extends HandlebarsApplicationMixin(Applicatio action: socketEvent.Refresh, data: { refreshType: RefreshType.Countdown } }); - Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Countdown }); } static #addCountdown() { diff --git a/module/applications/ui/countdowns.mjs b/module/applications/ui/countdowns.mjs index 5cf79100..996a46ea 100644 --- a/module/applications/ui/countdowns.mjs +++ b/module/applications/ui/countdowns.mjs @@ -12,14 +12,11 @@ const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; export default class DhCountdowns extends HandlebarsApplicationMixin(ApplicationV2) { previousCountdownData = null; - changedCountdownsForAnimation = new Set(); constructor(options = {}) { super(options); - this.previousCountdownData = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns).countdowns; - this.setupHooks(); } /** @inheritDoc */ @@ -80,7 +77,15 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application return frame; } + /** @inheritdoc */ + async _onFirstRender(context, options) { + await super._onFirstRender(context, options); + this._createContextMenu(this._getCountdownContextOptions, '.countdown-container[data-countdown]', { + parentClassHooks: false, fixed: true + }); + } + /** @inheritdoc */ async _onRender(context, options) { await super._onRender(context, options); @@ -95,7 +100,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application /* Handle animations to draw attention to countdown values changing */ const typesToAnimate = new Set(); - for (const countdownKey of this.changedCountdownsForAnimation) { + for (const countdownKey of options.animate ?? []) { const shimmerAnimation = [ { backgroundPositionX: '98%' }, { backgroundPositionX: '0%' } @@ -127,8 +132,6 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application const element = this.element.querySelector(`.header-type-toggles .header-type[data-type="${type}"]`); element?.animate(pulseAnimation, pulseTiming); } - - this.changedCountdownsForAnimation.clear(); } /** Returns countdown data filtered by ownership */ @@ -137,17 +140,15 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application const values = Object.entries(setting.countdowns).map(([key, countdown]) => ({ key, countdown, - ownership: DhCountdowns.#getPlayerOwnership(game.user, setting, countdown) + ownership: countdown.getUserLevel(game.user) })); return values.filter(v => v.ownership !== CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE); } _getCountdownData() { - const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); - return this.#getCountdowns().reduce((acc, { key, countdown, ownership }) => { const playersWithAccess = game.users.reduce((acc, user) => { - const ownership = DhCountdowns.#getPlayerOwnership(user, setting, countdown); + const ownership = countdown.getUserLevel(user); if (!user.isGM && ownership && ownership !== CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) { acc.push(user); } @@ -213,19 +214,6 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application return context; } - static #getPlayerOwnership(user, setting, countdown) { - if (user.isGM) return CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER; - - const playerOwnership = countdown.ownership[user.id]; - return playerOwnership === undefined || playerOwnership === CONST.DOCUMENT_OWNERSHIP_LEVELS.INHERIT - ? setting.defaultOwnership - : playerOwnership; - } - - cooldownRefresh = ({ refreshType }) => { - if (refreshType === RefreshType.Countdown) this.render(); - }; - static canPerformEdit() { if (game.user.isGM) return true; @@ -323,18 +311,12 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application action: socketEvent.Refresh, data: { refreshType: RefreshType.Countdown } }); - Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Countdown }); - } - - setupHooks() { - Hooks.on(socketEvent.Refresh, this.cooldownRefresh.bind()); } async close(options) { /* Opt out of Foundry's standard behavior of closing all application windows marked as UI when Escape is pressed */ if (options.closeKey) return; - Hooks.off(socketEvent.Refresh, this.cooldownRefresh); return super.close(options); } @@ -377,4 +359,30 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application refreshType: RefreshType.Countdown }); } + + /** + * @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} + */ + _getCountdownContextOptions() { + /** @param {HTMLElement} element */ + const getCountdownFromElement = element => { + const id = element.closest('[data-countdown]').dataset.countdown; + if (!id) return null; + const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); + return setting.countdowns[id ?? '']; + } + + return [ + { + label: 'CONTROLS.CommonDelete', + icon: 'fa-solid fa-trash', + visible: element => { + return getCountdownFromElement(element)?.isOwner; + }, + onClick: (_, target) => { + getCountdownFromElement(target)?.delete(); + } + } + ]; + } } diff --git a/module/config/settingsConfig.mjs b/module/config/settingsConfig.mjs index 50841084..7aa1b0f7 100644 --- a/module/config/settingsConfig.mjs +++ b/module/config/settingsConfig.mjs @@ -27,8 +27,10 @@ export const menu = { }; export const gameSettings = { + /** @type {'Automation'} */ Automation: 'Automation', Metagaming: 'Metagaming', + /** @type {'Homebrew'} */ Homebrew: 'Homebrew', appearance: 'Appearance', GlobalOverrides: 'GlobalOverrides', @@ -37,6 +39,7 @@ export const gameSettings = { Fear: 'ResourcesFear' }, LevelTiers: 'LevelTiers', + /** @type {'Countdowns'} */ Countdowns: 'Countdowns', LastMigrationVersion: 'LastMigrationVersion', SpotlightRequestQueue: 'SpotlightRequestQueue', diff --git a/module/config/system.mjs b/module/config/system.mjs index 31dba518..de6630ec 100644 --- a/module/config/system.mjs +++ b/module/config/system.mjs @@ -12,6 +12,7 @@ import * as HOOKS from './hooksConfig.mjs'; import * as TRIGGER from './triggerConfig.mjs'; import * as ITEMBROWSER from './itemBrowserConfig.mjs'; +/** @type {"daggerheart"} */ export const SYSTEM_ID = 'daggerheart'; export const SYSTEM = { diff --git a/module/data/_types.d.ts b/module/data/_types.d.ts new file mode 100644 index 00000000..f0e2ade5 --- /dev/null +++ b/module/data/_types.d.ts @@ -0,0 +1,7 @@ +import { DhCountdown } from './countdowns.mjs' + +declare module './countdowns.mjs' { + export default interface DhCountdowns { + countdowns: Record; + } +} \ No newline at end of file diff --git a/module/data/countdowns.mjs b/module/data/countdowns.mjs index ffe4d26b..5fe01e20 100644 --- a/module/data/countdowns.mjs +++ b/module/data/countdowns.mjs @@ -1,3 +1,4 @@ +import { RefreshType, socketEvent } from '../systemRegistration/socket.mjs'; import FormulaField from './fields/formulaField.mjs'; export default class DhCountdowns extends foundry.abstract.DataModel { @@ -14,19 +15,36 @@ export default class DhCountdowns extends foundry.abstract.DataModel { }; } - handleChange() { + /** @inheritdoc */ + _initialize(options) { + super._initialize(options); + for (const [id, countdown] of Object.entries(this.countdowns)) { + countdown.id = id; + } + } + + async handleChange() { const previousCountdowns = foundry.ui.countdowns.previousCountdownData; const changedCountdowns = Object.entries(this.countdowns).reduce((acc, [key, countdown]) => { - const previousCountdown = previousCountdowns[key]; - if (!previousCountdown || (previousCountdown.progress.current !== countdown.progress.current)) { + const previous = previousCountdowns[key]; + const currentChanged = !previous || (previous.progress.current !== countdown.progress.current); + if (currentChanged && previous?.progress.start === countdown.progress.start) { acc.push(key); } - return acc; }, []); - for (const countdownKey of changedCountdowns) - foundry.ui.countdowns.changedCountdownsForAnimation.add(countdownKey); + // Re-render countdowns applications. When the change is due to an actual update, resync the editor + if (!foundry.utils.equals(previousCountdowns, this.countdowns)) { + await foundry.ui.countdowns.render({ animate: changedCountdowns }); + for (const instance of game.system.api.applications.ui.CountdownEdit.instances()) { + instance.data = this; + await instance.render(); + } + } + + // Inform modules of updates + Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Countdown }); } static migrateData(source) { @@ -160,6 +178,14 @@ export class DhCountdown extends foundry.abstract.DataModel { }, {}); } + /** + * A boolean indicator for whether the current game User has ownership rights for this countdown + * @returns {boolean} + */ + get isOwner() { + return this.getUserLevel(game.user) === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER; + } + /** @inheritDoc */ static migrateData(source) { if (source.progress.max) { @@ -170,4 +196,29 @@ export class DhCountdown extends foundry.abstract.DataModel { return super.migrateData(source); } + + /** + * Get the explicit permission level that a User has over this Document, a value in CONST.DOCUMENT_OWNERSHIP_LEVELS. + * Compendium content ignores the ownership field in favor of User role-based ownership. Otherwise, Documents use + * granular per-User ownership definitions and Embedded Documents defer to their parent ownership. + * + * @param {BaseUser} [user=game.user] The User being tested + * @returns {DocumentOwnershipNumber} A numeric permission level from {@link CONST.DOCUMENT_OWNERSHIP_LEVELS} + */ + getUserLevel(user) { + if (user.isGM) return CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER; + + const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); + const playerOwnership = this.ownership[user.id]; + return playerOwnership === undefined || playerOwnership === CONST.DOCUMENT_OWNERSHIP_LEVELS.INHERIT + ? setting.defaultOwnership + : playerOwnership; + } + + async delete() { + const setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); + const data = foundry.utils.deepClone(setting._source); + delete data.countdowns[this.id]; + await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, data); + } } From 8d68166e4c643c26bc82882fd50773b4caf19e18 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sun, 19 Jul 2026 12:42:16 +0200 Subject: [PATCH 28/30] Raised foundry compatability to 14.365 --- system.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system.json b/system.json index 214ab2ee..0aafe3f5 100644 --- a/system.json +++ b/system.json @@ -5,7 +5,7 @@ "version": "2.5.4", "compatibility": { "minimum": "14.364", - "verified": "14.364", + "verified": "14.365", "maximum": "14" }, "url": "https://github.com/Foundryborne/daggerheart", From 26bcc2dddc33dd6d4671ead3b1aef0881c6b1318 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Tue, 21 Jul 2026 02:08:52 +0200 Subject: [PATCH 29/30] [Feature] Reload Check (#2051) --- lang/en.json | 20 +++++++++-- .../applications/sheets/actors/character.mjs | 15 ++++++-- module/applications/ui/chatLog.mjs | 9 +++++ module/config/settingsConfig.mjs | 15 ++++++++ module/data/action/attackAction.mjs | 21 ++++++++++++ module/data/chat-message/actorRoll.mjs | 1 + module/data/item/weapon.mjs | 8 +++++ module/data/settings/Automation.mjs | 7 ++++ module/dice/dhRoll.mjs | 21 ++++++++++-- ...lack_Powder_Revolver_AokqTusPzn0hghkE.json | 9 +++++ .../weapon_Blunderbuss_SLFrK0WmldPo0shz.json | 9 +++++ .../weapon_Hand_Cannon_MyGz8nd5sieRQ7zl.json | 9 +++++ ...eapon_Ilmari_s_Rifle_TMrUzVC3KvcHmdt8.json | 9 +++++ ...eapon_Magus_Revolver_jGykNGQiKm63tCiE.json | 9 +++++ styles/less/global/inventory-item.less | 4 +++ styles/less/ui/chat/ability-use.less | 5 +++ styles/less/ui/chat/action.less | 7 +++- styles/less/ui/chat/chat.less | 34 ++++++++++++++++--- .../settings/automation-settings/roll.hbs | 1 + .../sheets/global/partials/item-resource.hbs | 10 ++++-- templates/ui/chat/parts/button-part.hbs | 3 ++ templates/ui/chat/roll.hbs | 11 ++++++ 22 files changed, 223 insertions(+), 14 deletions(-) diff --git a/lang/en.json b/lang/en.json index b65cade7..d0b8d0f7 100755 --- a/lang/en.json +++ b/lang/en.json @@ -126,6 +126,10 @@ "damageOnSave": "Damage on Save", "useDefaultItemValues": "Use default Item values" }, + "Reload": { + "checkReload": "Check Reload", + "reloadRequired": "Reload Required!" + }, "RollField": { "diceRolling": { "compare": "Should be", @@ -1322,6 +1326,11 @@ "short": "V. Far" } }, + "ReloadChoices": { + "off": { "label": "Don't Use" }, + "button": { "label": "Use button" }, + "auto": { "label": "Automatic" } + }, "RollTypes": { "trait": { "name": "Trait" @@ -2221,7 +2230,9 @@ }, "Resource": { "single": "Resource", - "plural": "Resources" + "plural": "Resources", + "unloaded": "The weapon is not loaded", + "loaded": "The weapon is loaded" }, "Roll": { "attack": "Attack Roll", @@ -2797,6 +2808,10 @@ "hint": "Effects with defined range dependency will automatically turn on/off depending on range" } }, + "reload": { + "label": "Reload Checking", + "hint": "If the system should present a button for checking if the character will need to reload or do it automatically" + }, "resourceScrollTexts": { "label": "Show Resource Change Scrolltexts", "hint": "When a character is damaged, uses armor etc, a scrolling text will briefly appear by the token to signify this." @@ -3258,7 +3273,8 @@ "knowTheTide": "Know The Tide gained a token", "lackingItemTransferPermission": "User {user} lacks owner permission needed to transfer items to {target}", "noTokenTargeted": "No token is targeted", - "behaviorRegionRequiresGM": "Creating a Region with an attached Behavior requires an online GM" + "behaviorRegionRequiresGM": "Creating a Region with an attached Behavior requires an online GM", + "reloadRequired": "The {weapon} must be reloaded to be used!" }, "Progress": { "migrationLabel": "Performing system migration. Please wait and do not close Foundry." diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index b5c04f78..b88ec6fd 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -3,7 +3,7 @@ import DhDeathMove from '../../dialogs/deathMove.mjs'; import { CharacterLevelup, LevelupViewMode } from '../../levelup/_module.mjs'; import DhCharacterCreation from '../../characterCreation/characterCreation.mjs'; import FilterMenu from '../../ux/filter-menu.mjs'; -import { getArmorSources, getDocFromElement, getDocFromElementSync, sortBy } from '../../../helpers/utils.mjs'; +import { getArmorSources, getDocFromElement, getDocFromElementSync, itemAbleRollParse, sortBy } from '../../../helpers/utils.mjs'; /**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */ @@ -29,6 +29,7 @@ export default class CharacterSheet extends DHBaseActorSheet { toggleResourceDice: CharacterSheet.#toggleResourceDice, handleResourceDice: CharacterSheet.#handleResourceDice, advanceResourceDie: CharacterSheet.#advanceResourceDie, + toggleItemReload: CharacterSheet.#onToggleItemReload, cancelBeastform: CharacterSheet.#cancelBeastform, toggleResourceManagement: CharacterSheet.#toggleResourceManagement, useDowntime: this.useDowntime, @@ -954,11 +955,21 @@ export default class CharacterSheet extends DHBaseActorSheet { }); } - /** */ static #advanceResourceDie(_, target) { this.updateResourceDie(target, true); } + static async #onToggleItemReload(_, target) { + const item = await getDocFromElement(target); + if (!item || !item.system.resource?.max) + return; + + await item.update({ + 'system.resource.value': item.system.needsReload ? + itemAbleRollParse(item.system.resource.max, this.document, item) : 0 + }) + } + lowerResourceDie(event) { event.preventDefault(); event.stopPropagation(); diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index c457a6a6..0ad436ec 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -152,6 +152,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo html.querySelectorAll('.risk-it-all-button').forEach(element => element.addEventListener('click', event => this.riskItAllClearStressAndHitPoints(event, data)) ); + for (const element of html.querySelectorAll('.roll-reload-check')) { + element.addEventListener('click', event => this.onRollReloadCheck(event, message)); + } }; setupHooks() { @@ -275,4 +278,10 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo const actor = game.actors.get(event.target.dataset.actorId); new game.system.api.applications.dialogs.RiskItAllDialog(actor, resourceValue).render({ force: true }); } + + async onRollReloadCheck(_event, messageData) { + const message = game.messages.get(messageData._id); + const needsReload = await message.system.action.handleReload?.({ awaitRoll: true }); + await message.update({ 'system.needsReload': needsReload }); + } } diff --git a/module/config/settingsConfig.mjs b/module/config/settingsConfig.mjs index 7aa1b0f7..daa89959 100644 --- a/module/config/settingsConfig.mjs +++ b/module/config/settingsConfig.mjs @@ -62,3 +62,18 @@ export const actionAutomationChoices = { label: 'DAGGERHEART.CONFIG.ActionAutomationChoices.always' } }; + +export const reloadChoices = { + off: { + id: 'off', + label: 'DAGGERHEART.CONFIG.ReloadChoices.off.label' + }, + button: { + id: 'button', + label: 'DAGGERHEART.CONFIG.ReloadChoices.button.label' + }, + auto: { + id: 'auto', + label: 'DAGGERHEART.CONFIG.ReloadChoices.auto.label' + } +}; \ No newline at end of file diff --git a/module/data/action/attackAction.mjs b/module/data/action/attackAction.mjs index dadc85f0..bf78520d 100644 --- a/module/data/action/attackAction.mjs +++ b/module/data/action/attackAction.mjs @@ -52,6 +52,10 @@ export default class DHAttackAction extends DHDamageAction { } async use(event, options) { + if (this.item?.system.needsReload) { + return ui.notifications.error(_loc('DAGGERHEART.UI.Notifications.reloadRequired', { weapon: this.item.name })); + } + const result = await super.use(event, options); if (result?.message?.system.action?.roll?.type === 'attack') { @@ -62,6 +66,23 @@ export default class DHAttackAction extends DHDamageAction { return result; } + async handleReload(options = { awaitRoll: false }) { + const roll = await new Roll('1d6').evaluate(); + if (game.modules.get('dice-so-nice')?.active) { + if (options.awaitRoll) + await game.dice3d.showForRoll(roll, game.user, true); + else + game.dice3d.showForRoll(roll, game.user, true); + } + + const needsToReload = roll.total === 1; + if (needsToReload) { + this.item.update({ 'system.resource.value': 0 }); + } + + return needsToReload; + } + /** * Generate a localized label array for this item subtype. * @returns {(string | { value: string, icons: string[] })[]} An array of localized strings and damage label objects. diff --git a/module/data/chat-message/actorRoll.mjs b/module/data/chat-message/actorRoll.mjs index dd81952b..7071ed1e 100644 --- a/module/data/chat-message/actorRoll.mjs +++ b/module/data/chat-message/actorRoll.mjs @@ -42,6 +42,7 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { hasEffect: new fields.BooleanField({ initial: false }), hasSave: new fields.BooleanField({ initial: false }), hasTarget: new fields.BooleanField({ initial: false }), + needsReload: new fields.BooleanField({ initial: false }), isDirect: new fields.BooleanField({ initial: false }), onSave: new fields.StringField(), source: new fields.SchemaField({ diff --git a/module/data/item/weapon.mjs b/module/data/item/weapon.mjs index 5ff2d8d1..3d50e68a 100644 --- a/module/data/item/weapon.mjs +++ b/module/data/item/weapon.mjs @@ -116,6 +116,14 @@ export default class DHWeapon extends AttachableItem { return this.weaponFeatures; } + get hasReload() { + return Boolean(this.weaponFeatures.find(x => x.value === 'reloading')); + } + + get needsReload() { + return this.hasReload && this.resource.value === 0; + } + /**@inheritdoc */ async getDescriptionData() { const baseDescription = this.description; diff --git a/module/data/settings/Automation.mjs b/module/data/settings/Automation.mjs index 35e87327..e7bc0458 100644 --- a/module/data/settings/Automation.mjs +++ b/module/data/settings/Automation.mjs @@ -196,6 +196,13 @@ export default class DhAutomation extends foundry.abstract.DataModel { }) }) }), + reload: new fields.StringField({ + required: true, + choices: CONFIG.DH.SETTINGS.reloadChoices, + initial: CONFIG.DH.SETTINGS.reloadChoices.button.id, + label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.reload.label', + hint: 'DAGGERHEART.SETTINGS.Automation.FIELDS.reload.hint' + }), autoExpireActiveEffects: new fields.BooleanField({ required: true, initial: true, diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index c78caa4f..62d9ff9f 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -117,7 +117,11 @@ export default class DHRoll extends BaseRoll { static async toMessage(roll, config) { const item = config.data.parent?.items?.get?.(config.source.item) ?? null; - const action = item ? item.system.actions.get(config.source.action) : null; + const actions = item ? [ + ...item.system.actions, + ...(item.system.attack?.id === config.source.action ? [item.system.attack] : []) + ] : []; + const action = actions.find(x => x.id === config.source.action); let actionDescription = null; if (action?.chatDisplay) { actionDescription = action @@ -129,6 +133,14 @@ export default class DHRoll extends BaseRoll { config.actionChatMessageHandled = true; } + const reloadSetting = + game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).reload; + const useReload = + item?.system.hasReload && + action?.type === 'attack' && + reloadSetting === CONFIG.DH.SETTINGS.reloadChoices.auto.id; + const needsReload = useReload ? await action?.handleReload?.() : false; + const cls = getDocumentClass('ChatMessage'), msgData = { type: this.messageType, @@ -136,7 +148,7 @@ export default class DHRoll extends BaseRoll { title: roll.title, speaker: cls.getSpeaker({ actor: roll.data?.parent }), sound: config.mute ? null : CONFIG.sounds.dice, - system: { ...config, actionDescription }, + system: { ...config, actionDescription, needsReload }, rolls: [roll] }; @@ -158,14 +170,17 @@ export default class DHRoll extends BaseRoll { if (!this._evaluated) return; const metagamingSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming); + const automationSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation); const chatData = await this._prepareChatRenderContext({ flavor, isPrivate, ...options }); return foundry.applications.handlebars.renderTemplate(template, { roll: this, ...chatData, + action: chatData.action, parent: chatData.parent, targetMode: chatData.targetMode, areas: chatData.action?.areas, - metagamingSettings + metagamingSettings, + automationSettings }); } diff --git a/src/packs/items/weapons/weapon_Black_Powder_Revolver_AokqTusPzn0hghkE.json b/src/packs/items/weapons/weapon_Black_Powder_Revolver_AokqTusPzn0hghkE.json index 34371c2b..f8b254f3 100644 --- a/src/packs/items/weapons/weapon_Black_Powder_Revolver_AokqTusPzn0hghkE.json +++ b/src/packs/items/weapons/weapon_Black_Powder_Revolver_AokqTusPzn0hghkE.json @@ -128,6 +128,15 @@ "source": "Daggerheart SRD", "page": 48, "artist": "" + }, + "resource": { + "type": "simple", + "value": 1, + "max": "1", + "recovery": null, + "progression": "decreasing", + "dieFaces": "d4", + "icon": "fa-solid fa-gun" } }, "effects": [], diff --git a/src/packs/items/weapons/weapon_Blunderbuss_SLFrK0WmldPo0shz.json b/src/packs/items/weapons/weapon_Blunderbuss_SLFrK0WmldPo0shz.json index 9b2f455a..570d8856 100644 --- a/src/packs/items/weapons/weapon_Blunderbuss_SLFrK0WmldPo0shz.json +++ b/src/packs/items/weapons/weapon_Blunderbuss_SLFrK0WmldPo0shz.json @@ -128,6 +128,15 @@ "source": "Daggerheart SRD", "page": 46, "artist": "" + }, + "resource": { + "type": "simple", + "value": 1, + "max": "1", + "recovery": null, + "progression": "decreasing", + "dieFaces": "d4", + "icon": "fa-solid fa-gun" } }, "effects": [], diff --git a/src/packs/items/weapons/weapon_Hand_Cannon_MyGz8nd5sieRQ7zl.json b/src/packs/items/weapons/weapon_Hand_Cannon_MyGz8nd5sieRQ7zl.json index 4967c6e4..f9d54bcd 100644 --- a/src/packs/items/weapons/weapon_Hand_Cannon_MyGz8nd5sieRQ7zl.json +++ b/src/packs/items/weapons/weapon_Hand_Cannon_MyGz8nd5sieRQ7zl.json @@ -128,6 +128,15 @@ "source": "Daggerheart SRD", "page": 50, "artist": "" + }, + "resource": { + "type": "simple", + "value": 1, + "max": "1", + "recovery": null, + "progression": "decreasing", + "dieFaces": "d4", + "icon": "fa-solid fa-gun" } }, "effects": [], diff --git a/src/packs/items/weapons/weapon_Ilmari_s_Rifle_TMrUzVC3KvcHmdt8.json b/src/packs/items/weapons/weapon_Ilmari_s_Rifle_TMrUzVC3KvcHmdt8.json index cddd762a..512168f5 100644 --- a/src/packs/items/weapons/weapon_Ilmari_s_Rifle_TMrUzVC3KvcHmdt8.json +++ b/src/packs/items/weapons/weapon_Ilmari_s_Rifle_TMrUzVC3KvcHmdt8.json @@ -128,6 +128,15 @@ "source": "Daggerheart SRD", "page": 49, "artist": "" + }, + "resource": { + "type": "simple", + "value": 1, + "max": "1", + "recovery": null, + "progression": "decreasing", + "dieFaces": "d4", + "icon": "fa-solid fa-gun" } }, "effects": [], diff --git a/src/packs/items/weapons/weapon_Magus_Revolver_jGykNGQiKm63tCiE.json b/src/packs/items/weapons/weapon_Magus_Revolver_jGykNGQiKm63tCiE.json index 9dbbb1c1..fd3e86d3 100644 --- a/src/packs/items/weapons/weapon_Magus_Revolver_jGykNGQiKm63tCiE.json +++ b/src/packs/items/weapons/weapon_Magus_Revolver_jGykNGQiKm63tCiE.json @@ -128,6 +128,15 @@ "source": "Daggerheart SRD", "page": 51, "artist": "" + }, + "resource": { + "type": "simple", + "value": 1, + "max": "1", + "recovery": null, + "progression": "decreasing", + "dieFaces": "d4", + "icon": "fa-solid fa-gun" } }, "effects": [], diff --git a/styles/less/global/inventory-item.less b/styles/less/global/inventory-item.less index d942133c..d2ca76c6 100644 --- a/styles/less/global/inventory-item.less +++ b/styles/less/global/inventory-item.less @@ -144,6 +144,10 @@ display: flex; align-items: center; gap: 4px; + + .unloaded { + opacity: 0.5; + } } } diff --git a/styles/less/ui/chat/ability-use.less b/styles/less/ui/chat/ability-use.less index c31136ad..9d3cb84e 100644 --- a/styles/less/ui/chat/ability-use.less +++ b/styles/less/ui/chat/ability-use.less @@ -133,6 +133,11 @@ height: 40px; flex: 1 1 calc(50% - 5px); + span { + font-family: @font-body; + font-weight: 700; + } + &:nth-last-child(1):nth-child(odd) { flex-basis: 100%; } diff --git a/styles/less/ui/chat/action.less b/styles/less/ui/chat/action.less index 6eeb7a52..cff617d8 100644 --- a/styles/less/ui/chat/action.less +++ b/styles/less/ui/chat/action.less @@ -46,9 +46,14 @@ padding: 0 8px; button { - height: 40px; + height: 36px; flex: 1 1 calc(50% - 5px); + span { + font-family: @font-body; + font-weight: 700; + } + &:nth-last-child(1):nth-child(odd) { flex-basis: 100%; } diff --git a/styles/less/ui/chat/chat.less b/styles/less/ui/chat/chat.less index 4d627e39..8c3f4b08 100644 --- a/styles/less/ui/chat/chat.less +++ b/styles/less/ui/chat/chat.less @@ -271,6 +271,28 @@ } } + .roll-reload-container { + text-align: center; + display: flex; + align-items: center; + justify-content: center; + gap: 4px; + + .reload-warning { + display: flex; + align-items: center; + border-radius: 5px; + width: fit-content; + gap: 5px; + cursor: pointer; + padding: 5px; + transition: all 0.3s ease; + color: @beige; + background: @red-40; + outline: 1px solid @red; + } + } + .roll-part-extra { display: flex; justify-content: center; @@ -622,15 +644,19 @@ .roll-buttons { display: flex; + flex-wrap: wrap; gap: 5px; + width: 100%; margin-top: 8px; button { - height: 32px; - flex: 1; + height: 36px; + flex: 1 1 calc(50% - 5px); + font-family: @font-body; + font-weight: 700; - &.end-button { - flex: 0; + &:nth-last-child(1):nth-child(odd) { + flex-basis: 100%; } } } diff --git a/templates/settings/automation-settings/roll.hbs b/templates/settings/automation-settings/roll.hbs index dc65f8ae..a0b19ee4 100644 --- a/templates/settings/automation-settings/roll.hbs +++ b/templates/settings/automation-settings/roll.hbs @@ -18,6 +18,7 @@

    {{localize (concat "DAGGERHEART.SETTINGS.Automation.FIELDS.roll." field.name ".hint")}}

    {{/each}} + {{formGroup settingFields.schema.fields.reload value=settingFields.reload localize=true}}
    diff --git a/templates/sheets/global/partials/item-resource.hbs b/templates/sheets/global/partials/item-resource.hbs index fbcf02ca..cde10d37 100644 --- a/templates/sheets/global/partials/item-resource.hbs +++ b/templates/sheets/global/partials/item-resource.hbs @@ -1,7 +1,13 @@ {{#if (eq item.system.resource.type 'simple')}}
    - - + {{#if item.system.hasReload}} + + + + {{else}} + + + {{/if}}
    {{else if (eq item.system.resource.type 'diceValue')}}
    diff --git a/templates/ui/chat/parts/button-part.hbs b/templates/ui/chat/parts/button-part.hbs index a0075b5f..b631192c 100644 --- a/templates/ui/chat/parts/button-part.hbs +++ b/templates/ui/chat/parts/button-part.hbs @@ -1,4 +1,7 @@
    + {{#if (eq automationSettings.reload 'button')}} + + {{/if}} {{#if areas.length}}{{/if}} {{#if hasDamage}} {{#if damage.active}} diff --git a/templates/ui/chat/roll.hbs b/templates/ui/chat/roll.hbs index c7b17b21..ab4e197e 100644 --- a/templates/ui/chat/roll.hbs +++ b/templates/ui/chat/roll.hbs @@ -1,5 +1,16 @@
    {{title}}
    + + {{#if (eq action.type 'attack')}} + {{#if needsReload}} +
    + {{#if needsReload}} +

    {{localize "DAGGERHEART.ACTIONS.Reload.reloadRequired"}}

    + {{/if}} +
    + {{/if}} + {{/if}} + {{#if actionDescription}}{{> 'systems/daggerheart/templates/ui/chat/parts/description-part.hbs'}}{{/if}} {{#if hasRoll}}
    {{localize "Result"}}
    From d50545af4ecc95e749e8f2bc334cffd48dbe6b97 Mon Sep 17 00:00:00 2001 From: Murilo Brito <91566541+moliloo@users.noreply.github.com> Date: Tue, 21 Jul 2026 16:50:03 -0300 Subject: [PATCH 30/30] [Rework] Fear Tracker (#2097) * rework fear tracker application, add static positions to hold the tracker, enhance token and bar display --- lang/en.json | 10 + module/applications/ui/chatLog.mjs | 6 + module/applications/ui/fearTracker.mjs | 203 ++++++++++++- module/config/generalConfig.mjs | 8 + module/data/settings/Appearance.mjs | 5 + styles/less/ui/countdown/countdown.less | 3 +- styles/less/ui/resources/resources.less | 284 ++++++++++++------ .../settings/appearance-settings/main.hbs | 4 + templates/ui/fearTracker.hbs | 46 ++- 9 files changed, 457 insertions(+), 112 deletions(-) diff --git a/lang/en.json b/lang/en.json index d0b8d0f7..7aa87c7a 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2699,6 +2699,9 @@ "displayFear": { "label": "Display Fear" }, + "fearPosition": { + "label": "Fear Position" + }, "displayCountdownUI": { "label": "Display Countdown UI" }, @@ -2750,6 +2753,13 @@ "token": "Tokens", "bar": "Bar", "hide": "Hide" + }, + "fearPosition": { + "free": "Free", + "topCenter": "Top + Center", + "bottomCenter": "Bottom + Center", + "rightTop": "Right + Top", + "leftBottom": "Left + Bottom" } }, "Automation": { diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index 0ad436ec..1ed4607f 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -1,6 +1,7 @@ import { enrichedDualityRoll } from '../../enrichers/DualityRollEnricher.mjs'; import { enrichedFateRoll, getFateTypeData } from '../../enrichers/FateRollEnricher.mjs'; import { getCommandTarget, rollCommandToJSON } from '../../helpers/utils.mjs'; +import FearTracker from './fearTracker.mjs'; export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLog { constructor(options) { @@ -279,6 +280,11 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo new game.system.api.applications.dialogs.RiskItAllDialog(actor, resourceValue).render({ force: true }); } + _toggleNotifications({ closing = false } = {}) { + super._toggleNotifications(closing) + FearTracker.handleOffSet(); + } + async onRollReloadCheck(_event, messageData) { const message = game.messages.get(messageData._id); const needsReload = await message.system.action.handleReload?.({ awaitRoll: true }); diff --git a/module/applications/ui/fearTracker.mjs b/module/applications/ui/fearTracker.mjs index 8c247f79..d1813736 100644 --- a/module/applications/ui/fearTracker.mjs +++ b/module/applications/ui/fearTracker.mjs @@ -13,6 +13,14 @@ const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; export default class FearTracker extends HandlebarsApplicationMixin(ApplicationV2) { constructor(options = {}) { super(options); + + this._dragData = { + isDragging: false, + startX: 0, + startY: 0, + startLeft: 0, + startTop: 0 + } } /** @inheritDoc */ @@ -21,19 +29,20 @@ export default class FearTracker extends HandlebarsApplicationMixin(ApplicationV classes: [], tag: 'div', window: { - frame: true, + frame: false, title: 'DAGGERHEART.GENERAL.fear', positioned: true, resizable: true, minimizable: false }, + classes: ['daggerheart', 'dh-style', 'fear-tracker'], actions: { setFear: FearTracker.setFear, increaseFear: FearTracker.increaseFear }, position: { - width: 222, - height: 222 + width: 540, + height: 'auto' } }; @@ -53,6 +62,10 @@ export default class FearTracker extends HandlebarsApplicationMixin(ApplicationV return game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear; } + get fearPosition() { + return game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance).fearPosition; + } + /* -------------------------------------------- */ /* Rendering */ /* -------------------------------------------- */ @@ -63,15 +76,48 @@ export default class FearTracker extends HandlebarsApplicationMixin(ApplicationV current = this.currentFear, max = this.maxFear, percent = (current / max) * 100, - isGM = game.user.isGM; + isGM = game.user.isGM, + locked = false, + isFree = this.fearPosition == 'free'; - return { display, current, max, percent, isGM }; + return { display, current, max, percent, isGM, locked, isFree }; } /** @override */ - async _preFirstRender(context, options) { - options.position = - game.user.getFlag(CONFIG.DH.id, 'app.resources.position') ?? FearTracker.DEFAULT_OPTIONS.position; + async _onRender(context, options) { + await super._onRender(context, options); + + this.#setupDragging(); + this.#setupResizing(); + + const fearPosition = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance).fearPosition; + + if (options.isFirstRender) FearTracker.handleOffSet(); + if (!options.force) return; + + this.handleStyleElement(fearPosition); + + switch (fearPosition) { + case 'topCenter': + document.getElementById('ui-top')?.appendChild(this.element); + break; + case 'bottomCenter': + document.getElementById('ui-bottom')?.prepend(this.element); + break; + case 'rightTop': + document.getElementById('ui-right-column-1')?.appendChild(this.element); + break; + case 'leftBottom': + document.getElementById('ui-left-column-1')?.insertBefore(this.element, document.getElementById('players')); + break; + + default: + document.body?.appendChild(this.element); + const position = + game.user.getFlag(CONFIG.DH.id, 'app.resources.position') ?? FearTracker.DEFAULT_OPTIONS.position; + this.setPosition(position); + break; + } } /** @override */ @@ -80,13 +126,29 @@ export default class FearTracker extends HandlebarsApplicationMixin(ApplicationV await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear, this.maxFear); } - _onPosition(position) { - game.user.setFlag(CONFIG.DH.id, 'app.resources.position', position); + handleStyleElement(fearPosition) { + for (const position of Object.values(CONFIG.DH.GENERAL.fearPosition)) { + this.element.classList.remove(position.value); + } + + this.element.classList.add(fearPosition); } - async close(options = {}) { - if (!options.allowed) return; - else super.close(options); + static handleOffSet() { + const fearTracker = document.getElementById('resources'); + const hotbar = document.getElementById('hotbar'); + + if (!fearTracker) return; + + const offset = Number(hotbar.style.getPropertyValue('--offset').replace(/px$/, '')) || 0; + + if (offset > 0) return; + + fearTracker.style.setProperty('--offset', `${offset - 13}px`); + } + + _onPosition(position) { + game.user.setFlag(CONFIG.DH.id, 'app.resources.position', position); } static async setFear(event, target) { @@ -110,4 +172,119 @@ export default class FearTracker extends HandlebarsApplicationMixin(ApplicationV value ); } + + // TODO: Remove methods later to use Foundry's dragger and resize methods + /* -------------------------------------------- */ + /* Dragging handlers */ + /* -------------------------------------------- */ + #setupDragging() { + const dragHandle = this.element.querySelector('.drag-handle'); + if (!dragHandle) return; + dragHandle.addEventListener('mousedown', this.#onDragStart.bind(this)); + } + + #onDragStart(event) { + if (event.button !== 0) return; + this._dragData.isDragging = true; + this._dragData.startX = event.clientX; + this._dragData.startY = event.clientY; + const rect = this.element.getBoundingClientRect(); + this._dragData.startLeft = rect.left; + this._dragData.startTop = rect.top; + this.element.style.cursor = 'grabbing'; + + this._dragHandler = this.#onDragging.bind(this); + this._dragEndHandler = this.#onDragEnd.bind(this); + window.addEventListener('mousemove', this._dragHandler); + window.addEventListener('mouseup', this._dragEndHandler); + } + + #onDragging(event) { + if (!this._dragData.isDragging) return; + + const dragX = event.clientX - this._dragData.startX; + const dragY = event.clientY - this._dragData.startY; + + this.element.style.left = `${this._dragData.startLeft + dragX}px`; + this.element.style.top = `${this._dragData.startTop + dragY}px`; + } + + #onDragEnd() { + if (!this._dragData.isDragging) return; + this._dragData.isDragging = false; + this.element.style.cursor = ''; + + if (this._dragHandler) window.removeEventListener('mousemove', this._dragHandler); + if (this._dragEndHandler) window.removeEventListener('mouseup', this._dragEndHandler); + + const rect = this.element.getBoundingClientRect(); + const pos = { top: rect.top, left: rect.left }; + + this.setPosition(pos); + } + + /* -------------------------------------------- */ + /* Resize handlers */ + /* -------------------------------------------- */ + + #setupResizing() { + const resizeHandle = this.element.querySelector('.resize-handle'); + if (!resizeHandle) return; + resizeHandle.addEventListener('mousedown', this.#onResizeStart.bind(this)); + } + + #onResizeStart(e) { + if (e.button !== 0) return; + e.stopPropagation(); + + let maxAllowedWidth = 10000; + + this._resizeData = { + isResizing: true, + startX: e.clientX, + startY: e.clientY, + startWidth: this.element.offsetWidth, + startHeight: this.element.offsetHeight, + maxAllowedWidth: Math.max(50, maxAllowedWidth) + }; + + this._resizeHandler = this.#onResizing.bind(this); + this._resizeEndHandler = this.#onResizeEnd.bind(this); + window.addEventListener('mousemove', this._resizeHandler); + window.addEventListener('mouseup', this._resizeEndHandler); + } + + #onResizing(e) { + if (!this._resizeData?.isResizing) return; + + const currentDx = e.clientX - this._resizeData.startX; + const potentialWidth = Math.max(50, this._resizeData.startWidth + currentDx); + + const width = Math.min(potentialWidth, this._resizeData.maxAllowedWidth); + + this.element.style.width = `${width}px`; + + if (width < 100) { + this.element.classList.add('narrow'); + } else { + this.element.classList.remove('narrow'); + } + } + + #onResizeEnd() { + if (!this._resizeData?.isResizing) return; + this._resizeData.isResizing = false; + + if (this._resizeHandler) window.removeEventListener('mousemove', this._resizeHandler); + if (this._resizeEndHandler) window.removeEventListener('mouseup', this._resizeEndHandler); + + let width = parseFloat(this.element.style.width); + + + if (isNaN(width)) { + width = this.element.getBoundingClientRect().width; + } + + this.setPosition({ width: width }); + } } diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index 802f5907..3876f6b5 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -924,6 +924,14 @@ export const fearDisplay = { hide: { value: 'hide', label: 'DAGGERHEART.SETTINGS.Appearance.fearDisplay.hide' } }; +export const fearPosition = { + free: { value: 'free', label: 'DAGGERHEART.SETTINGS.Appearance.fearPosition.free' }, + topCenter: { value: 'topCenter', label: 'DAGGERHEART.SETTINGS.Appearance.fearPosition.topCenter' }, + bottomCenter: { value: 'bottomCenter', label: 'DAGGERHEART.SETTINGS.Appearance.fearPosition.bottomCenter' }, + rightTop: { value: 'rightTop', label: 'DAGGERHEART.SETTINGS.Appearance.fearPosition.rightTop' }, + leftBottom: { value: 'leftBottom', label: 'DAGGERHEART.SETTINGS.Appearance.fearPosition.leftBottom' } +}; + export const basicOwnershiplevels = { 0: { value: 0, label: 'OWNERSHIP.NONE' }, 2: { value: 2, label: 'OWNERSHIP.OBSERVER' }, diff --git a/module/data/settings/Appearance.mjs b/module/data/settings/Appearance.mjs index 4db27be0..16f5fac0 100644 --- a/module/data/settings/Appearance.mjs +++ b/module/data/settings/Appearance.mjs @@ -41,6 +41,11 @@ export default class DhAppearance extends foundry.abstract.DataModel { choices: CONFIG.DH.GENERAL.fearDisplay, initial: CONFIG.DH.GENERAL.fearDisplay.token.value }), + fearPosition: new StringField({ + required: true, + choices: CONFIG.DH.GENERAL.fearPosition, + initial: CONFIG.DH.GENERAL.fearPosition.topCenter.value + }), displayCountdownUI: new BooleanField({ initial: true }), diceSoNice: new SchemaField({ hope: diceStyle({ fg: '#ffffff', bg: '#ffe760', outline: '#000000', edge: '#ffffff' }), diff --git a/styles/less/ui/countdown/countdown.less b/styles/less/ui/countdown/countdown.less index 96e01ffd..4e64d50a 100644 --- a/styles/less/ui/countdown/countdown.less +++ b/styles/less/ui/countdown/countdown.less @@ -32,7 +32,7 @@ background: var(--background); border-radius: 4px; opacity: var(--ui-fade-opacity); - transition: opacity var(--ui-fade-duration); + transition: opacity var(--ui-fade-delay) var(--ui-fade-duration); } &:not(.performance-low, .noblur) { @@ -41,6 +41,7 @@ &:hover::before { opacity: 1; + transition: opacity var(--ui-fade-duration); } #ui-right:has(#effects-display .effect-container) & { diff --git a/styles/less/ui/resources/resources.less b/styles/less/ui/resources/resources.less index 3982d990..27f11942 100644 --- a/styles/less/ui/resources/resources.less +++ b/styles/less/ui/resources/resources.less @@ -1,119 +1,221 @@ :root { - --shadow-text-stroke: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000; - --fear-animation: background 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease, opacity 0.3s ease; + --hotbar-size: 60px; +} + +#interface.theme-dark, +body.theme-dark { + .daggerheart.dh-style.fear-tracker { + --background: url(../assets/parchments/dh-parchment-dark.png); + } +} + +#interface.theme-light, +body.theme-light { + .daggerheart.dh-style.fear-tracker { + --background: url('../assets/parchments/dh-parchment-light.png') no-repeat center; + } +} + +#ui-middle:has(#hotbar.sm), +#ui-middle:has(#hotbar.md.offset) { + #resources { + &.top-center, + &.bottom-center { + width: calc((var(--hotbar-size) * 5) + 32px) !important; + } + } } #resources { + position: static; min-height: calc(var(--header-height) + 4rem); min-width: 4rem; color: #d3d3d3; - transition: var(--fear-animation); - header, - .controls, - .window-resize-handle { - transition: var(--fear-animation); + pointer-events: all; + padding: var(--spacer-8); + max-width: 540px; + min-width: 100px; + + &::before { + content: ' '; + position: absolute; + inset: 0; + background: var(--background); + border-radius: 8px; + opacity: var(--ui-fade-opacity); + transition: opacity var(--ui-fade-delay) var(--ui-fade-duration); + } - .window-content { - padding: 0.5rem; - #resource-fear { + + &:hover::before { + opacity: 1; + transition: opacity var(--ui-fade-duration); + } + + &.free { + position: absolute; + } + + &.topCenter, + &.bottomCenter { + margin: 1rem 0; + width: 100% !important; + transform: translateX(var(--offset)); + transition: all 250ms ease; + } + + &.rightTop { + width: 300px !important; + max-width: 300px; + } + + &.leftBottom { + width: 200px !important; + max-width: 200px; + background: transparent; + } + + &:not(.performance-low, .noblur) { + backdrop-filter: blur(5px); + } + + #ui-right:has(#effects-display .effect-container) & { + right: 62px; + } + + #resource-fear { + position: relative; + + &:hover { + .fear-header { + opacity: 1; + height: 18.75px; + visibility: visible; + } + + .resize-handle { + opacity: 1; + } + } + + .fear-header { + display: flex; + gap: 5px; + pointer-events: all; + margin-bottom: 0.5rem; + opacity: 0; + height: 0; + visibility: hidden; + transition: all 0.3s ease; + + .drag-handle { + cursor: grab; + } + + .fear-title { + font-size: var(--font-size-13); + } + } + + .fear-tokens { display: flex; flex-direction: row; - gap: 0.5rem 0.25rem; flex-wrap: wrap; - i { - font-size: var(--font-size-18); - border: 1px solid rgba(0, 0, 0, 0.5); + justify-content: center; + gap: 0.5rem 0.25rem; + + .fear-token { + font-size: var(--font-size-16); + border: 1.5px double light-dark(@dark-15, @dark-golden-80); border-radius: 50%; aspect-ratio: 1; display: flex; justify-content: center; align-items: center; - width: 3rem; + width: 2.5rem; background-color: @primary-color-fear; - -webkit-box-shadow: 0px 0px 5px 1px rgba(0, 0, 0, 0.75); - box-shadow: 0px 0px 5px 1px rgba(0, 0, 0, 0.75); - color: #d3d3d3; + color: @beige; flex-grow: 0; + text-shadow: none; + &.inactive { filter: grayscale(1) !important; opacity: 0.5; } } - .controls, - .resource-bar { - border: 2px solid rgb(153 122 79); - background-color: rgb(24 22 46); - } - .controls { - display: flex; - align-self: center; - border-radius: 50%; - align-items: center; - justify-content: center; - width: 30px; - height: 30px; - font-size: var(--font-size-20); - cursor: pointer; - &:hover { - font-size: 1.5rem; - } - &.disabled { - opacity: 0.5; - } - } - .resource-bar { - display: flex; - justify-content: center; - border-radius: 6px; - font-size: var(--font-size-20); - overflow: hidden; - position: relative; - padding: 0.25rem 0.5rem; - flex: 1; - text-shadow: var(--shadow-text-stroke); - &:before { - content: ''; - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: var(--fear-percent); - max-width: 100%; - background: linear-gradient(90deg, rgba(2, 0, 38, 1) 0%, rgba(199, 1, 252, 1) 100%); - z-index: 0; - border-radius: 4px; - } - span { - position: inherit; - z-index: 1; - } - &.fear { - } - } - &.isGM { - i { - cursor: pointer; - &:hover { - font-size: var(--font-size-20); - } - } - } } - } - button[data-action='close'] { - display: none; - } - &:not(:hover):not(.minimized) { - background: transparent; - box-shadow: unset; - border-color: transparent; - header, - #resource-fear .controls, - .window-resize-handle { + + .resize-handle { + position: absolute; + bottom: -12px; + right: -9px; + cursor: nwse-resize; opacity: 0; + transition: all 0.3s ease; + } + + + .resource-bar { + display: flex; + flex-direction: column; + align-items: center; + position: relative; + height: 30px; + width: 100%; + + .progress-bar { + position: absolute; + appearance: none; + width: 100%; + height: 100%; + border: 1px solid light-dark(@dark-15, @dark-golden-80); + border-radius: 999px; + z-index: 0; + background: @dark-blue; + + &::-webkit-progress-bar { + border: none; + background: @dark-blue; + border-radius: 999px; + } + &::-webkit-progress-value { + background: linear-gradient(90deg, rgba(2, 0, 38, 1) 0%, rgba(199, 1, 252, 1) 100%); + border-radius: 999px; + } + } + + .label { + margin: auto 0; + z-index: 2; + height: auto; + text-align: center; + font-size: var(--font-size-18); + color: @beige; + } + } + + .controls { + display: flex; + gap: 8px; + justify-content: center; + align-items: center; + margin-top: 0.5rem; + color: light-dark(@dark, @beige); + + .disabled { + opacity: 0.5; + } + } + + &.isGM { + .fear-token { + cursor: pointer; + transition: box-shadow 0.15s ease; + + &:hover { + box-shadow: 0 0 8px @primary-color-fear ; + } + } } - } - &:has(.fear-bar) { - min-width: 200px; } } diff --git a/templates/settings/appearance-settings/main.hbs b/templates/settings/appearance-settings/main.hbs index 32dd9e63..324ea653 100644 --- a/templates/settings/appearance-settings/main.hbs +++ b/templates/settings/appearance-settings/main.hbs @@ -8,6 +8,10 @@ value=setting.displayFear localize=true}} {{formGroup + fields.fearPosition + value=setting.fearPosition + localize=true}} + {{formGroup fields.displayCountdownUI value=setting.displayCountdownUI localize=true}} diff --git a/templates/ui/fearTracker.hbs b/templates/ui/fearTracker.hbs index 6832ab90..deae762a 100644 --- a/templates/ui/fearTracker.hbs +++ b/templates/ui/fearTracker.hbs @@ -1,16 +1,48 @@
    -
    +
    + {{#if isFree}} +
    +
    + +
    + {{localize 'DAGGERHEART.GENERAL.fear'}} +
    + {{/if}} + {{#if (eq display 'token')}} - {{#times max}} - - {{/times}} +
    + {{#times max}} + + + + {{/times}} +
    {{/if}} {{#if (eq display 'bar')}} - {{#if isGM}}
    -
    {{/if}}
    - {{current}}/{{max}} + +

    {{current}} / {{max}}

    - {{#if isGM}}
    +
    {{/if}} + {{#if isGM}} +
    + + +
    + {{/if}} + {{/if}} + + {{#if isFree}} + + + {{/if}}
    \ No newline at end of file