From 7bc5ea4910a12e4c633e3f2770638e4e69bee1d7 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Fri, 6 Feb 2026 09:59:06 +0100 Subject: [PATCH 01/23] Initial --- daggerheart.mjs | 12 ++++ lang/en.json | 4 +- .../applications/sheets-configs/_module.mjs | 1 + .../armorActiveEffectConfig.mjs | 59 +++++++++++++++++ module/data/activeEffect/_module.mjs | 6 +- module/data/activeEffect/armorEffect.mjs | 64 +++++++++++++++++++ module/documents/activeEffect.mjs | 5 +- .../sheets/activeEffects/armorEffects.less | 16 +++++ styles/less/sheets/index.less | 1 + system.json | 3 +- .../sheets/activeEffect/armor/details.hbs | 20 ++++++ .../sheets/activeEffect/armor/settings.hbs | 12 ++++ 12 files changed, 198 insertions(+), 5 deletions(-) create mode 100644 module/applications/sheets-configs/armorActiveEffectConfig.mjs create mode 100644 module/data/activeEffect/armorEffect.mjs create mode 100644 styles/less/sheets/activeEffects/armorEffects.less create mode 100644 templates/sheets/activeEffect/armor/details.hbs create mode 100644 templates/sheets/activeEffect/armor/settings.hbs diff --git a/daggerheart.mjs b/daggerheart.mjs index 8c817327..92908045 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -209,10 +209,22 @@ Hooks.once('init', () => { SYSTEM.id, applications.sheetConfigs.ActiveEffectConfig, { + types: ['base', 'beastform', 'horde'], makeDefault: true, label: sheetLabel('DOCUMENT.ActiveEffect') } ); + DocumentSheetConfig.registerSheet( + CONFIG.ActiveEffect.documentClass, + SYSTEM.id, + applications.sheetConfigs.ArmorActiveEffectConfig, + { + types: ['armor'], + makeDefault: true, + label: () => + `${game.i18n.localize('TYPES.ActiveEffect.armor')} ${game.i18n.localize('DAGGERHEART.GENERAL.effect')}` + } + ); game.socket.on(`system.${SYSTEM.id}`, socketRegistration.handleSocketEvent); diff --git a/lang/en.json b/lang/en.json index c4b611d0..0d62bd8b 100755 --- a/lang/en.json +++ b/lang/en.json @@ -16,7 +16,8 @@ "ActiveEffect": { "base": "Standard", "beastform": "Beastform", - "horde": "Horde" + "horde": "Horde", + "armor": "Armor" }, "Actor": { "character": "Character", @@ -2167,6 +2168,7 @@ "duality": "Duality", "dualityDice": "Duality Dice", "dualityRoll": "Duality Roll", + "effect": "Effect", "enabled": "Enabled", "evasion": "Evasion", "equipment": "Equipment", diff --git a/module/applications/sheets-configs/_module.mjs b/module/applications/sheets-configs/_module.mjs index d3fb3c39..8b09dc29 100644 --- a/module/applications/sheets-configs/_module.mjs +++ b/module/applications/sheets-configs/_module.mjs @@ -6,6 +6,7 @@ export { default as CompanionSettings } from './companion-settings.mjs'; export { default as SettingActiveEffectConfig } from './setting-active-effect-config.mjs'; export { default as SettingFeatureConfig } from './setting-feature-config.mjs'; export { default as EnvironmentSettings } from './environment-settings.mjs'; +export { default as ArmorActiveEffectConfig } from './armorActiveEffectConfig.mjs'; export { default as ActiveEffectConfig } from './activeEffectConfig.mjs'; export { default as DhTokenConfig } from './token-config.mjs'; export { default as DhPrototypeTokenConfig } from './prototype-token-config.mjs'; diff --git a/module/applications/sheets-configs/armorActiveEffectConfig.mjs b/module/applications/sheets-configs/armorActiveEffectConfig.mjs new file mode 100644 index 00000000..ef407782 --- /dev/null +++ b/module/applications/sheets-configs/armorActiveEffectConfig.mjs @@ -0,0 +1,59 @@ +const { HandlebarsApplicationMixin, DocumentSheetV2 } = foundry.applications.api; + +export default class ArmorActiveEffectConfig extends HandlebarsApplicationMixin(DocumentSheetV2) { + static DEFAULT_OPTIONS = { + classes: ['daggerheart', 'sheet', 'dh-style', 'active-effect-config', 'armor-effect-config'], + form: { + handler: this.updateForm, + submitOnChange: true, + closeOnSubmit: false + }, + actions: { + addEffect: ArmorActiveEffectConfig.#addEffect + } + }; + + static PARTS = { + header: { template: 'systems/daggerheart/templates/sheets/activeEffect/header.hbs' }, + tabs: { template: 'templates/generic/tab-navigation.hbs' }, + details: { template: 'systems/daggerheart/templates/sheets/activeEffect/armor/details.hbs' }, + settings: { template: 'systems/daggerheart/templates/sheets/activeEffect/armor/settings.hbs' }, + footer: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-form-footer.hbs' } + }; + + static TABS = { + sheet: { + tabs: [ + { id: 'details', icon: 'fa-solid fa-book' }, + { id: 'settings', icon: 'fa-solid fa-bars', label: 'DAGGERHEART.GENERAL.Tabs.settings' } + ], + initial: 'details', + labelPrefix: 'EFFECT.TABS' + } + }; + + async _prepareContext(options) { + const context = await super._prepareContext(options); + context.systemFields = context.document.system.schema.fields; + + return context; + } + + /** @inheritDoc */ + async _preparePartContext(partId, context) { + const partContext = await super._preparePartContext(partId, context); + if (partId in partContext.tabs) partContext.tab = partContext.tabs[partId]; + + return partContext; + } + + async updateForm(_event, _form, formData) { + await this.document.update(formData.object); + this.render(); + } + + static #addEffect() { + this.document.update({ 'system.changes': [...this.document.system.changes, {}] }); + this.render(); + } +} diff --git a/module/data/activeEffect/_module.mjs b/module/data/activeEffect/_module.mjs index 1a50088a..b4b89c78 100644 --- a/module/data/activeEffect/_module.mjs +++ b/module/data/activeEffect/_module.mjs @@ -1,11 +1,13 @@ import BaseEffect from './baseEffect.mjs'; import BeastformEffect from './beastformEffect.mjs'; import HordeEffect from './hordeEffect.mjs'; +import ArmorEffect from './armorEffect.mjs'; -export { BaseEffect, BeastformEffect, HordeEffect }; +export { BaseEffect, BeastformEffect, HordeEffect, ArmorEffect }; export const config = { base: BaseEffect, beastform: BeastformEffect, - horde: HordeEffect + horde: HordeEffect, + armor: ArmorEffect }; diff --git a/module/data/activeEffect/armorEffect.mjs b/module/data/activeEffect/armorEffect.mjs new file mode 100644 index 00000000..31ce4dca --- /dev/null +++ b/module/data/activeEffect/armorEffect.mjs @@ -0,0 +1,64 @@ +export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel { + static defineSchema() { + const fields = foundry.data.fields; + + return { + ...super.defineSchema(), + changes: new fields.ArrayField( + new fields.SchemaField({ + type: new fields.StringField({ + required: true, + blank: false, + choices: CONFIG.DH.GENERAL.activeEffectModes, + initial: CONFIG.DH.GENERAL.activeEffectModes.add.id, + validate: ArmorEffect.#validateType + }), + phase: new fields.StringField({ required: true, blank: false, initial: 'initial' }), + priority: new fields.NumberField(), + marked: new fields.NumberField({ + required: true, + integer: true, + initial: 0, + min: 0, + label: 'DAGGERHEART.GENERAL.value' + }), + max: new fields.NumberField({ + required: true, + integer: true, + initial: 1, + min: 1, + label: 'DAGGERHEART.GENERAL.max' + }) + }) + ) + }; + } + + static applyChangeField(model, change, field) { + return [model, change, field]; + } + + prepareBaseData() { + for (const change of this.changes) { + change.key = 'system.armorTest'; + change.value = Math.max(change.max - change.marked, change.max); + } + } + + /** + * Validate that an {@link EffectChangeData#type} string is well-formed. + * @param {string} type The string to be validated + * @returns {true} + * @throws {Error} An error if the type string is malformed + */ + static #validateType(type) { + if (type.length < 3) throw new Error('must be at least three characters long'); + if (!/^custom\.-?\d+$/.test(type) && !type.split('.').every(s => /^[a-z0-9]+$/i.test(s))) { + throw new Error( + 'A change type must either be a sequence of dot-delimited, alpha-numeric substrings or of the form' + + ' "custom.{number}"' + ); + } + return true; + } +} diff --git a/module/documents/activeEffect.mjs b/module/documents/activeEffect.mjs index 9a326282..82ac17f1 100644 --- a/module/documents/activeEffect.mjs +++ b/module/documents/activeEffect.mjs @@ -62,7 +62,7 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect { throw new Error('The array of sub-types to restrict to must not be empty.'); } - const creatableEffects = ['base']; + const creatableEffects = ['base', 'armor']; const documentTypes = this.TYPES.filter(type => creatableEffects.includes(type)).map(type => { const labelKey = `TYPES.ActiveEffect.${type}`; const label = game.i18n.has(labelKey) ? game.i18n.localize(labelKey) : type; @@ -140,6 +140,9 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect { /**@inheritdoc*/ static applyChangeField(model, change, field) { + if (this.system?.applyChangeField) + super.applyChangeField(...this.system.applyChangeField(model, change, field)); + change.value = Number.isNumeric(change.value) ? change.value : DhActiveEffect.getChangeValue(model, change, change.effect); diff --git a/styles/less/sheets/activeEffects/armorEffects.less b/styles/less/sheets/activeEffects/armorEffects.less new file mode 100644 index 00000000..9756edce --- /dev/null +++ b/styles/less/sheets/activeEffects/armorEffects.less @@ -0,0 +1,16 @@ +.application.sheet.daggerheart.dh-style.armor-effect-config { + .armor-effects-container { + display: flex; + flex-direction: column; + gap: 8px; + + .armor-effect-container { + display: flex; + gap: 4px; + + * { + flex: 1; + } + } + } +} diff --git a/styles/less/sheets/index.less b/styles/less/sheets/index.less index e5ffbf3e..25ec6fc3 100644 --- a/styles/less/sheets/index.less +++ b/styles/less/sheets/index.less @@ -44,3 +44,4 @@ @import './actions/actions.less'; @import './activeEffects/activeEffects.less'; +@import './activeEffects/armorEffects.less'; diff --git a/system.json b/system.json index 48a2319a..d62142a0 100644 --- a/system.json +++ b/system.json @@ -278,7 +278,8 @@ }, "ActiveEffect": { "beastform": {}, - "horde": {} + "horde": {}, + "armor": {} }, "Combat": { "combat": {} diff --git a/templates/sheets/activeEffect/armor/details.hbs b/templates/sheets/activeEffect/armor/details.hbs new file mode 100644 index 00000000..2b16560e --- /dev/null +++ b/templates/sheets/activeEffect/armor/details.hbs @@ -0,0 +1,20 @@ +
+ {{formGroup fields.tint value=source.tint rootId=rootId placeholder="#ffffff"}} + {{formGroup fields.description value=source.description rootId=rootId}} + {{formGroup fields.disabled value=source.disabled rootId=rootId}} + + {{#if isActorEffect}} +
+ +
+ +
+
+ {{/if}} + + {{#if isItemEffect}} + {{formGroup fields.transfer value=source.transfer rootId=rootId label=legacyTransfer.label hint=(localize "DAGGERHEART.EFFECTS.Attachments.transferHint")}} + {{/if}} + + {{formGroup fields.showIcon value=source.showIcon options=showIconOptions rootId=rootId}} +
\ No newline at end of file diff --git a/templates/sheets/activeEffect/armor/settings.hbs b/templates/sheets/activeEffect/armor/settings.hbs new file mode 100644 index 00000000..58a22d63 --- /dev/null +++ b/templates/sheets/activeEffect/armor/settings.hbs @@ -0,0 +1,12 @@ +
+ + +
+ {{#each source.system.changes as |change index|}} +
+ {{formGroup @root.systemFields.changes.element.fields.marked value=change.marked localize=true}} + {{formGroup @root.systemFields.changes.element.fields.max value=change.max localize=true}} +
+ {{/each}} +
+
\ No newline at end of file From 7036a53c71bc4dde7fd5aa957523da6991124b9e Mon Sep 17 00:00:00 2001 From: WBHarry Date: Mon, 9 Feb 2026 16:37:21 +0100 Subject: [PATCH 02/23] progress --- .../armorActiveEffectConfig.mjs | 12 ++++++-- module/data/activeEffect/armorEffect.mjs | 4 +-- module/data/actor/character.mjs | 28 +++++++++++++------ module/documents/activeEffect.mjs | 5 ++++ .../sheets/activeEffect/armor/footer.hbs | 3 ++ .../sheets/activeEffect/armor/settings.hbs | 4 +-- templates/sheets/actors/character/sidebar.hbs | 10 +++---- 7 files changed, 45 insertions(+), 21 deletions(-) create mode 100644 templates/sheets/activeEffect/armor/footer.hbs diff --git a/module/applications/sheets-configs/armorActiveEffectConfig.mjs b/module/applications/sheets-configs/armorActiveEffectConfig.mjs index ef407782..c70422b9 100644 --- a/module/applications/sheets-configs/armorActiveEffectConfig.mjs +++ b/module/applications/sheets-configs/armorActiveEffectConfig.mjs @@ -2,6 +2,7 @@ const { HandlebarsApplicationMixin, DocumentSheetV2 } = foundry.applications.api export default class ArmorActiveEffectConfig extends HandlebarsApplicationMixin(DocumentSheetV2) { static DEFAULT_OPTIONS = { + tag: 'form', classes: ['daggerheart', 'sheet', 'dh-style', 'active-effect-config', 'armor-effect-config'], form: { handler: this.updateForm, @@ -9,7 +10,8 @@ export default class ArmorActiveEffectConfig extends HandlebarsApplicationMixin( closeOnSubmit: false }, actions: { - addEffect: ArmorActiveEffectConfig.#addEffect + addEffect: ArmorActiveEffectConfig.#addEffect, + finish: ArmorActiveEffectConfig.#finish } }; @@ -18,7 +20,7 @@ export default class ArmorActiveEffectConfig extends HandlebarsApplicationMixin( tabs: { template: 'templates/generic/tab-navigation.hbs' }, details: { template: 'systems/daggerheart/templates/sheets/activeEffect/armor/details.hbs' }, settings: { template: 'systems/daggerheart/templates/sheets/activeEffect/armor/settings.hbs' }, - footer: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-form-footer.hbs' } + footer: { template: 'systems/daggerheart/templates/sheets/activeEffect/armor/footer.hbs' } }; static TABS = { @@ -47,7 +49,7 @@ export default class ArmorActiveEffectConfig extends HandlebarsApplicationMixin( return partContext; } - async updateForm(_event, _form, formData) { + static async updateForm(_event, _form, formData) { await this.document.update(formData.object); this.render(); } @@ -56,4 +58,8 @@ export default class ArmorActiveEffectConfig extends HandlebarsApplicationMixin( this.document.update({ 'system.changes': [...this.document.system.changes, {}] }); this.render(); } + + static #finish() { + this.close(); + } } diff --git a/module/data/activeEffect/armorEffect.mjs b/module/data/activeEffect/armorEffect.mjs index 31ce4dca..5c125cea 100644 --- a/module/data/activeEffect/armorEffect.mjs +++ b/module/data/activeEffect/armorEffect.mjs @@ -40,8 +40,8 @@ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel prepareBaseData() { for (const change of this.changes) { - change.key = 'system.armorTest'; - change.value = Math.max(change.max - change.marked, change.max); + change.key = 'system.armorScore.value'; + change.value = Math.min(change.max - change.marked, change.max); } } diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index c79bb078..b5edbb23 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -62,7 +62,20 @@ export default class DhCharacter extends BaseDataActor { label: 'DAGGERHEART.GENERAL.proficiency' }), evasion: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.evasion' }), - armorScore: new fields.NumberField({ integer: true, initial: 0, label: 'DAGGERHEART.GENERAL.armorScore' }), + armorScore: new fields.SchemaField({ + value: new fields.NumberField({ + integer: true, + initial: 0, + min: 0, + label: 'DAGGERHEART.GENERAL.armorScore' + }), + max: new fields.NumberField({ + integer: true, + initial: 0, + min: 0, + label: 'DAGGERHEART.GENERAL.armorScore' + }) + }), damageThresholds: new fields.SchemaField({ severe: new fields.NumberField({ integer: true, @@ -665,14 +678,12 @@ export default class DhCharacter extends BaseDataActor { } } - const armor = this.armor; - this.armorScore = armor ? armor.system.baseScore : 0; this.damageThresholds = { - major: armor - ? armor.system.baseThresholds.major + this.levelData.level.current + major: this.armor + ? this.armor.system.baseThresholds.major + this.levelData.level.current : this.levelData.level.current, - severe: armor - ? armor.system.baseThresholds.severe + this.levelData.level.current + severe: this.armor + ? this.armor.system.baseThresholds.severe + this.levelData.level.current : this.levelData.level.current * 2 }; @@ -705,8 +716,7 @@ export default class DhCharacter extends BaseDataActor { this.attack.roll.trait = this.rules.attack.roll.trait ?? this.attack.roll.trait; this.resources.armor = { - value: this.armor?.system?.marks?.value ?? 0, - max: this.armorScore, + ...this.armorScore, isReversed: true }; diff --git a/module/documents/activeEffect.mjs b/module/documents/activeEffect.mjs index efe8efea..22a36653 100644 --- a/module/documents/activeEffect.mjs +++ b/module/documents/activeEffect.mjs @@ -139,6 +139,11 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect { /* Methods */ /* -------------------------------------------- */ + /**@inheritdoc */ + static applyChange(actor, change, options) { + super.applyChange(actor, change, options); + } + /**@inheritdoc*/ static applyChangeField(model, change, field) { if (this.system?.applyChangeField) diff --git a/templates/sheets/activeEffect/armor/footer.hbs b/templates/sheets/activeEffect/armor/footer.hbs new file mode 100644 index 00000000..761189ff --- /dev/null +++ b/templates/sheets/activeEffect/armor/footer.hbs @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/templates/sheets/activeEffect/armor/settings.hbs b/templates/sheets/activeEffect/armor/settings.hbs index 58a22d63..087cdef6 100644 --- a/templates/sheets/activeEffect/armor/settings.hbs +++ b/templates/sheets/activeEffect/armor/settings.hbs @@ -4,8 +4,8 @@
{{#each source.system.changes as |change index|}}
- {{formGroup @root.systemFields.changes.element.fields.marked value=change.marked localize=true}} - {{formGroup @root.systemFields.changes.element.fields.max value=change.max localize=true}} + {{formGroup @root.systemFields.changes.element.fields.marked name=(concat 'system.changes.' index '.marked') value=change.marked localize=true}} + {{formGroup @root.systemFields.changes.element.fields.max name=(concat 'system.changes.' index '.max') value=change.max localize=true}}
{{/each}}
diff --git a/templates/sheets/actors/character/sidebar.hbs b/templates/sheets/actors/character/sidebar.hbs index b2757b55..5720f253 100644 --- a/templates/sheets/actors/character/sidebar.hbs +++ b/templates/sheets/actors/character/sidebar.hbs @@ -30,7 +30,7 @@ - {{#if document.system.armor.system.marks}} + {{#if document.system.armorScore.max}}
{{#if useResourcePips}}
@@ -52,14 +52,14 @@
{{else}}
- + / - {{document.system.armorScore}} + {{document.system.armorScore.max}}

{{localize "DAGGERHEART.GENERAL.armorSlots"}}

From 514e0260eba3b92bb19f964728d96e9c7398a412 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Mon, 9 Feb 2026 18:23:30 +0100 Subject: [PATCH 03/23] Working armor application --- daggerheart.mjs | 1 + module/applications/sheets/items/armor.mjs | 17 ++++++ module/config/generalConfig.mjs | 5 ++ module/data/activeEffect/_module.mjs | 4 ++ module/data/activeEffect/armorEffect.mjs | 61 ++++++++++++++++++---- module/data/item/armor.mjs | 15 ++++-- module/documents/activeEffect.mjs | 2 +- module/documents/actor.mjs | 4 ++ templates/sheets/items/armor/settings.hbs | 8 ++- 9 files changed, 99 insertions(+), 18 deletions(-) diff --git a/daggerheart.mjs b/daggerheart.mjs index a540de90..294170fb 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -43,6 +43,7 @@ CONFIG.Item.dataModels = models.items.config; CONFIG.ActiveEffect.documentClass = documents.DhActiveEffect; CONFIG.ActiveEffect.dataModels = models.activeEffects.config; +CONFIG.ActiveEffect.changeTypes = { ...CONFIG.ActiveEffect.changeTypes, ...models.activeEffects.changeTypes }; CONFIG.Combat.documentClass = documents.DhpCombat; CONFIG.Combat.dataModels = { base: models.DhCombat }; diff --git a/module/applications/sheets/items/armor.mjs b/module/applications/sheets/items/armor.mjs index 2550b415..081ada0e 100644 --- a/module/applications/sheets/items/armor.mjs +++ b/module/applications/sheets/items/armor.mjs @@ -34,6 +34,13 @@ export default class ArmorSheet extends ItemAttachmentSheet(DHBaseItemSheet) { ...super.PARTS }; + _attachPartListeners(partId, htmlElement, options) { + super._attachPartListeners(partId, htmlElement, options); + + for (const element of htmlElement.querySelectorAll('.base-score-input')) + element.addEventListener('change', this.updateArmorEffect.bind(this)); + } + /**@inheritdoc */ async _preparePartContext(partId, context) { await super._preparePartContext(partId, context); @@ -41,12 +48,22 @@ export default class ArmorSheet extends ItemAttachmentSheet(DHBaseItemSheet) { switch (partId) { case 'settings': context.features = this.document.system.armorFeatures.map(x => x.value); + context.armorScore = this.document.system.armorEffect?.system.armorData?.max; break; } return context; } + async updateArmorEffect(event) { + const value = Number.parseInt(event.target.value); + const armorEffect = this.document.system.armorEffect; + if (Number.isNaN(value) || !armorEffect) return; + + await armorEffect.system.updateArmorMax(value); + this.render(); + } + /** * Callback function used by `tagifyElement`. * @param {Array} selectedOptions - The currently selected tag objects. diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index 1d9f8126..80ac546e 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -854,6 +854,11 @@ export const sceneRangeMeasurementSetting = { }; export const activeEffectModes = { + armor: { + id: 'armor', + priority: 20, + label: 'TYPES.ActiveEffect.armor' + }, custom: { id: 'custom', priority: 0, diff --git a/module/data/activeEffect/_module.mjs b/module/data/activeEffect/_module.mjs index b4b89c78..62f10e3e 100644 --- a/module/data/activeEffect/_module.mjs +++ b/module/data/activeEffect/_module.mjs @@ -11,3 +11,7 @@ export const config = { horde: HordeEffect, armor: ArmorEffect }; + +export const changeTypes = { + armor: ArmorEffect.armorChangeEffect +}; diff --git a/module/data/activeEffect/armorEffect.mjs b/module/data/activeEffect/armorEffect.mjs index 5c125cea..61c897f9 100644 --- a/module/data/activeEffect/armorEffect.mjs +++ b/module/data/activeEffect/armorEffect.mjs @@ -9,12 +9,11 @@ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel type: new fields.StringField({ required: true, blank: false, - choices: CONFIG.DH.GENERAL.activeEffectModes, - initial: CONFIG.DH.GENERAL.activeEffectModes.add.id, + initial: CONFIG.DH.GENERAL.activeEffectModes.armor.id, validate: ArmorEffect.#validateType }), phase: new fields.StringField({ required: true, blank: false, initial: 'initial' }), - priority: new fields.NumberField(), + priority: new fields.NumberField({ integer: true, initial: 20 }), marked: new fields.NumberField({ required: true, integer: true, @@ -34,13 +33,57 @@ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel }; } + get armorData() { + if (this.changes.length !== 1) return { value: 0, max: 0 }; + return { value: this.changes[0].value, max: this.changes[0].max }; + } + + async updateArmorMax(newMax) { + if (this.changes.length !== 1) return; + const newChanges = this.changes.map(change => ({ + ...change, + max: newMax, + marked: Math.min(change.marked, newMax) + })); + await this.parent.update({ 'system.changes': newChanges }); + } + static applyChangeField(model, change, field) { return [model, change, field]; } + static armorChangeEffect = { + label: 'Armor', + defaultPriortiy: 20, + handler: (actor, change, _options, _field, replacementData) => { + game.system.api.documents.DhActiveEffect.applyChange( + actor, + { + ...change, + key: 'system.armorScore.value', + type: CONFIG.DH.GENERAL.activeEffectModes.add.id, + value: change.value + }, + replacementData + ); + game.system.api.documents.DhActiveEffect.applyChange( + actor, + { + ...change, + key: 'system.armorScore.max', + type: CONFIG.DH.GENERAL.activeEffectModes.add.id, + value: change.max + }, + replacementData + ); + return {}; + }, + render: null + }; + prepareBaseData() { for (const change of this.changes) { - change.key = 'system.armorScore.value'; + change.key = 'system.armorScore'; change.value = Math.min(change.max - change.marked, change.max); } } @@ -52,13 +95,9 @@ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel * @throws {Error} An error if the type string is malformed */ static #validateType(type) { - if (type.length < 3) throw new Error('must be at least three characters long'); - if (!/^custom\.-?\d+$/.test(type) && !type.split('.').every(s => /^[a-z0-9]+$/i.test(s))) { - throw new Error( - 'A change type must either be a sequence of dot-delimited, alpha-numeric substrings or of the form' + - ' "custom.{number}"' - ); - } + if (type !== CONFIG.DH.GENERAL.activeEffectModes.armor.id) + throw new Error('An armor effect must have change.type "armor"'); + return true; } } diff --git a/module/data/item/armor.mjs b/module/data/item/armor.mjs index 050b66d4..d56eb7d2 100644 --- a/module/data/item/armor.mjs +++ b/module/data/item/armor.mjs @@ -19,7 +19,6 @@ export default class DHArmor extends AttachableItem { ...super.defineSchema(), tier: new fields.NumberField({ required: true, integer: true, initial: 1, min: 1 }), equipped: new fields.BooleanField({ initial: false }), - baseScore: new fields.NumberField({ integer: true, initial: 0 }), armorFeatures: new fields.ArrayField( new fields.SchemaField({ value: new fields.StringField({ @@ -54,6 +53,11 @@ export default class DHArmor extends AttachableItem { ); } + get armorEffect() { + /* TODO: make armors only able to have on armor effect, or handle in some other way */ + return this.parent.effects.find(x => x.type === 'armor'); + } + /**@inheritdoc */ async getDescriptionData() { const baseDescription = this.description; @@ -159,8 +163,9 @@ export default class DHArmor extends AttachableItem { * @returns {string[]} An array of localized tag strings. */ _getTags() { + const baseScore = this.armorEffect?.system.armorData?.value; const tags = [ - `${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${this.baseScore}`, + `${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${baseScore}`, `${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseThresholds.base')}: ${this.baseThresholds.major} / ${this.baseThresholds.severe}` ]; @@ -173,8 +178,10 @@ export default class DHArmor extends AttachableItem { */ _getLabels() { const labels = []; - if (this.baseScore) - labels.push(`${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${this.baseScore}`); + if (this.armorEffect) + labels.push( + `${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${this.armorEffect.system.armorData?.value}` + ); return labels; } diff --git a/module/documents/activeEffect.mjs b/module/documents/activeEffect.mjs index 22a36653..8e7a2252 100644 --- a/module/documents/activeEffect.mjs +++ b/module/documents/activeEffect.mjs @@ -15,7 +15,7 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect { } // Then apply the standard suppression rules - if (['weapon', 'armor'].includes(this.parent?.type)) { + if (['weapon', 'armor'].includes(this.parent?.type) && this.transfer) { return !this.parent.system.equipped; } diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index cb51a255..4807f497 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -992,4 +992,8 @@ export default class DhpActor extends Actor { return allTokens; } + + applyActiveEffects(phase) { + super.applyActiveEffects(phase); + } } diff --git a/templates/sheets/items/armor/settings.hbs b/templates/sheets/items/armor/settings.hbs index e7bde6fe..11cd2b8b 100644 --- a/templates/sheets/items/armor/settings.hbs +++ b/templates/sheets/items/armor/settings.hbs @@ -7,8 +7,12 @@ {{localize tabs.settings.label}} {{localize "DAGGERHEART.GENERAL.Tiers.singular"}} {{formField systemFields.tier value=source.system.tier}} - {{localize "DAGGERHEART.ITEMS.Armor.baseScore"}} - {{formField systemFields.baseScore value=source.system.baseScore}} + {{#if this.armorScore includeZero=true}} + {{localize "DAGGERHEART.ITEMS.Armor.baseScore"}} +
+ +
+ {{/if}} {{localize "TYPES.Item.feature"}} From fb9f89fa9d07dbb39a60579ec5fb6bb12080ae96 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Mon, 9 Feb 2026 19:38:29 +0100 Subject: [PATCH 04/23] . --- lang/en.json | 7 +- .../armorActiveEffectConfig.mjs | 6 - module/applications/sheets/items/armor.mjs | 2 +- module/data/activeEffect/armorEffect.mjs | 125 +++++++++++++----- module/data/actor/character.mjs | 5 + module/data/item/armor.mjs | 28 ++-- module/documents/activeEffect.mjs | 7 +- .../sheets/activeEffect/armor/settings.hbs | 4 +- 8 files changed, 127 insertions(+), 57 deletions(-) diff --git a/lang/en.json b/lang/en.json index ab348696..26647cc2 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1849,6 +1849,9 @@ "Attachments": { "attachHint": "Drop items here to attach them", "transferHint": "If checked, this effect will be applied to any actor that owns this Effect's parent Item. The effect is always applied if this Item is attached to another one." + }, + "Armor": { + "newArmorEffect": "Armor Effect" } }, "GENERAL": { @@ -2975,7 +2978,9 @@ "tokenActorMissing": "{name} is missing an Actor", "tokenActorsMissing": "[{names}] missing Actors", "domainTouchRequirement": "This domain card requires {nr} {domain} cards in the loadout to be used", - "knowTheTide": "Know The Tide gained a token" + "knowTheTide": "Know The Tide gained a token", + "cannotAlterArmorEffectChanges": "You cannot alter the changes length of an armor effect", + "cannotAlterArmorEffectType": "You cannot alter the type of armor effect changes" }, "Sidebar": { "actorDirectory": { diff --git a/module/applications/sheets-configs/armorActiveEffectConfig.mjs b/module/applications/sheets-configs/armorActiveEffectConfig.mjs index c70422b9..131558a0 100644 --- a/module/applications/sheets-configs/armorActiveEffectConfig.mjs +++ b/module/applications/sheets-configs/armorActiveEffectConfig.mjs @@ -10,7 +10,6 @@ export default class ArmorActiveEffectConfig extends HandlebarsApplicationMixin( closeOnSubmit: false }, actions: { - addEffect: ArmorActiveEffectConfig.#addEffect, finish: ArmorActiveEffectConfig.#finish } }; @@ -54,11 +53,6 @@ export default class ArmorActiveEffectConfig extends HandlebarsApplicationMixin( this.render(); } - static #addEffect() { - this.document.update({ 'system.changes': [...this.document.system.changes, {}] }); - this.render(); - } - static #finish() { this.close(); } diff --git a/module/applications/sheets/items/armor.mjs b/module/applications/sheets/items/armor.mjs index 081ada0e..de61e5b1 100644 --- a/module/applications/sheets/items/armor.mjs +++ b/module/applications/sheets/items/armor.mjs @@ -48,7 +48,7 @@ export default class ArmorSheet extends ItemAttachmentSheet(DHBaseItemSheet) { switch (partId) { case 'settings': context.features = this.document.system.armorFeatures.map(x => x.value); - context.armorScore = this.document.system.armorEffect?.system.armorData?.max; + context.armorScore = this.document.system.armorData.max; break; } diff --git a/module/data/activeEffect/armorEffect.mjs b/module/data/activeEffect/armorEffect.mjs index 61c897f9..bd397abe 100644 --- a/module/data/activeEffect/armorEffect.mjs +++ b/module/data/activeEffect/armorEffect.mjs @@ -1,3 +1,7 @@ +/** + * ArmorEffects are ActiveEffects that have a static changes field of length 1. It includes current and maximum armor. + * When applied to a character, it adds to their currently marked and maximum armor. + */ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel { static defineSchema() { const fields = foundry.data.fields; @@ -14,7 +18,7 @@ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel }), phase: new fields.StringField({ required: true, blank: false, initial: 'initial' }), priority: new fields.NumberField({ integer: true, initial: 20 }), - marked: new fields.NumberField({ + value: new fields.NumberField({ required: true, integer: true, initial: 0, @@ -28,28 +32,35 @@ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel min: 1, label: 'DAGGERHEART.GENERAL.max' }) - }) + }), + { + initial: [ + { + type: CONFIG.DH.GENERAL.activeEffectModes.armor.id, + phase: 'initial', + priority: 20, + value: 0, + max: 1 + } + ] + } ) }; } - get armorData() { - if (this.changes.length !== 1) return { value: 0, max: 0 }; - return { value: this.changes[0].value, max: this.changes[0].max }; - } + /* Type Functions */ - async updateArmorMax(newMax) { - if (this.changes.length !== 1) return; - const newChanges = this.changes.map(change => ({ - ...change, - max: newMax, - marked: Math.min(change.marked, newMax) - })); - await this.parent.update({ 'system.changes': newChanges }); - } + /** + * Validate that an {@link EffectChangeData#type} string is well-formed. + * @param {string} type The string to be validated + * @returns {true} + * @throws {Error} An error if the type string is malformed + */ + static #validateType(type) { + if (type !== CONFIG.DH.GENERAL.activeEffectModes.armor.id) + throw new Error('An armor effect must have change.type "armor"'); - static applyChangeField(model, change, field) { - return [model, change, field]; + return true; } static armorChangeEffect = { @@ -81,23 +92,71 @@ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel render: null }; + /* Helpers */ + + get armorChange() { + if (this.changes.length !== 1) + throw new Error('Unexpected error. An armor effect should have a changes field of length 1.'); + return this.changes[0]; + } + + get armorData() { + return { value: this.armorChange.value, max: this.armorChange.max }; + } + + async updateArmorMax(newMax) { + const newChanges = [ + { + ...this.armorChange, + max: newMax, + value: Math.min(this.armorChange.value, newMax) + } + ]; + await this.parent.update({ 'system.changes': newChanges }); + } + + /* Overrides */ + prepareBaseData() { - for (const change of this.changes) { - change.key = 'system.armorScore'; - change.value = Math.min(change.max - change.marked, change.max); + const armorChange = this.armorChange; + armorChange.key = 'system.armorScore'; + } + + static getDefaultEffectData() { + return { + type: 'armor', + name: game.i18n.localize('DAGGERHEART.EFFECTS.Armor.newArmorEffect'), + img: 'icons/equipment/chest/breastplate-helmet-metal.webp' + }; + } + + async _preCreate(data, options, user) { + const allowed = await super._preCreate(data, options, user); + if (allowed === false) return; + + await this.updateSource({ ...ArmorEffect.getDefaultEffectData(), data }); + } + + async _preUpdate(changes, options, user) { + const allowed = await super._preUpdate(changes, options, user); + if (allowed === false) return false; + + if (changes.system?.changes) { + const changesChanged = changes.system.changes.length !== this.changes.length; + if (changesChanged) { + ui.notifications.error( + game.i18n.localize('DAGGERHEART.UI.Notifications.cannotAlterArmorEffectChanges') + ); + return false; + } + + if ( + changes.system.changes.length === 1 && + changes.system.changes[0].type !== CONFIG.DH.GENERAL.activeEffectModes.armor.id + ) { + ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.cannotAlterArmorEffectType')); + return false; + } } } - - /** - * Validate that an {@link EffectChangeData#type} string is well-formed. - * @param {string} type The string to be validated - * @returns {true} - * @throws {Error} An error if the type string is malformed - */ - static #validateType(type) { - if (type !== CONFIG.DH.GENERAL.activeEffectModes.armor.id) - throw new Error('An armor effect must have change.type "armor"'); - - return true; - } } diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index b5edbb23..128fef78 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -458,6 +458,11 @@ export default class DhCharacter extends BaseDataActor { return this.parent.items.find(x => x.type === 'armor' && x.system.equipped); } + /* TODO: Prep datastructure to be useful when applying automatic armor damage order */ + get armorEffects() { + return Array.from(this.parent.allApplicableEffects()); + } + get activeBeastform() { return this.parent.effects.find(x => x.type === 'beastform'); } diff --git a/module/data/item/armor.mjs b/module/data/item/armor.mjs index d56eb7d2..117f3963 100644 --- a/module/data/item/armor.mjs +++ b/module/data/item/armor.mjs @@ -54,10 +54,16 @@ export default class DHArmor extends AttachableItem { } get armorEffect() { - /* TODO: make armors only able to have on armor effect, or handle in some other way */ return this.parent.effects.find(x => x.type === 'armor'); } + get armorData() { + const armorEffect = this.armorEffect; + if (!armorEffect) return { value: 0, max: 0 }; + + return armorEffect.system.armorData; + } + /**@inheritdoc */ async getDescriptionData() { const baseDescription = this.description; @@ -73,6 +79,17 @@ export default class DHArmor extends AttachableItem { return { prefix, value: baseDescription, suffix: null }; } + /**@inheritdoc */ + async _onCreate(_data, _options, userId) { + if (userId !== game.user.id) return; + + if (!this.parent.effects.some(x => x.type === 'armor')) { + this.parent.createEmbeddedDocuments('ActiveEffect', [ + game.system.api.data.activeEffects.ArmorEffect.getDefaultEffectData() + ]); + } + } + /**@inheritdoc */ async _preUpdate(changes, options, user) { const allowed = await super._preUpdate(changes, options, user); @@ -163,9 +180,8 @@ export default class DHArmor extends AttachableItem { * @returns {string[]} An array of localized tag strings. */ _getTags() { - const baseScore = this.armorEffect?.system.armorData?.value; const tags = [ - `${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${baseScore}`, + `${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${this.armorData.value}`, `${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseThresholds.base')}: ${this.baseThresholds.major} / ${this.baseThresholds.severe}` ]; @@ -177,11 +193,7 @@ export default class DHArmor extends AttachableItem { * @returns {(string | { value: string, icons: string[] })[]} An array of localized strings and damage label objects. */ _getLabels() { - const labels = []; - if (this.armorEffect) - labels.push( - `${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${this.armorEffect.system.armorData?.value}` - ); + const labels = [`${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${this.armorData.value}`]; return labels; } diff --git a/module/documents/activeEffect.mjs b/module/documents/activeEffect.mjs index 8e7a2252..ac30c678 100644 --- a/module/documents/activeEffect.mjs +++ b/module/documents/activeEffect.mjs @@ -146,18 +146,15 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect { /**@inheritdoc*/ static applyChangeField(model, change, field) { - if (this.system?.applyChangeField) - super.applyChangeField(...this.system.applyChangeField(model, change, field)); - change.value = Number.isNumeric(change.value) ? change.value : DhActiveEffect.getChangeValue(model, change, change.effect); super.applyChangeField(model, change, field); } - _applyLegacy(actor, change, changes) { + _applyChangeUnguided(actor, change, changes, options) { change.value = DhActiveEffect.getChangeValue(actor, change, change.effect); - super._applyLegacy(actor, change, changes); + super._applyChangeUnguided(actor, change, changes, options); } static getChangeValue(model, change, effect) { diff --git a/templates/sheets/activeEffect/armor/settings.hbs b/templates/sheets/activeEffect/armor/settings.hbs index 087cdef6..c0f77b35 100644 --- a/templates/sheets/activeEffect/armor/settings.hbs +++ b/templates/sheets/activeEffect/armor/settings.hbs @@ -1,10 +1,8 @@
- -
{{#each source.system.changes as |change index|}}
- {{formGroup @root.systemFields.changes.element.fields.marked name=(concat 'system.changes.' index '.marked') value=change.marked localize=true}} + {{formGroup @root.systemFields.changes.element.fields.value name=(concat 'system.changes.' index '.value') value=change.value localize=true}} {{formGroup @root.systemFields.changes.element.fields.max name=(concat 'system.changes.' index '.max') value=change.max localize=true}}
{{/each}} From 9dc5830e18c7350a11b17c9d5f4bddc44998cf8d Mon Sep 17 00:00:00 2001 From: WBHarry Date: Mon, 9 Feb 2026 21:51:58 +0100 Subject: [PATCH 05/23] Added a updateArmorValue function that updates armoreffects according to an auto order --- module/data/activeEffect/armorEffect.mjs | 28 +++++++++++++ module/data/actor/character.mjs | 52 +++++++++++++++++++++--- 2 files changed, 75 insertions(+), 5 deletions(-) diff --git a/module/data/activeEffect/armorEffect.mjs b/module/data/activeEffect/armorEffect.mjs index bd397abe..f52f5efc 100644 --- a/module/data/activeEffect/armorEffect.mjs +++ b/module/data/activeEffect/armorEffect.mjs @@ -115,6 +115,34 @@ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel await this.parent.update({ 'system.changes': newChanges }); } + static orderEffectsForAutoChange(armorEffects, increasing) { + const getEffectWeight = effect => { + switch (effect.parent.type) { + case 'loot': + case 'consumable': + return 2; + case 'class': + case 'subclass': + case 'ancestry': + case 'community': + case 'feature': + case 'domainCard': + return 3; + case 'weapon': + case 'armor': + return 4; + case 'character': + return 5; + default: + return 1; + } + }; + + return armorEffects.sort((a, b) => + increasing ? getEffectWeight(b) - getEffectWeight(a) : getEffectWeight(a) - getEffectWeight(b) + ); + } + /* Overrides */ prepareBaseData() { diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index 128fef78..294fa77e 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -458,11 +458,6 @@ export default class DhCharacter extends BaseDataActor { return this.parent.items.find(x => x.type === 'armor' && x.system.equipped); } - /* TODO: Prep datastructure to be useful when applying automatic armor damage order */ - get armorEffects() { - return Array.from(this.parent.allApplicableEffects()); - } - get activeBeastform() { return this.parent.effects.find(x => x.type === 'beastform'); } @@ -512,6 +507,53 @@ export default class DhCharacter extends BaseDataActor { } } + async updateArmorValue(armorChange) { + if (armorChange === 0) return; + + const increasing = armorChange >= 0; + let remainingChange = Math.abs(armorChange); + const armorEffects = Array.from(this.parent.allApplicableEffects()).filter(x => x.type === 'armor'); + const orderedEffects = game.system.api.data.activeEffects.ArmorEffect.orderEffectsForAutoChange( + armorEffects, + increasing + ); + + const embeddedUpdates = []; + for (const armorEffect of orderedEffects) { + let usedArmorChange = 0; + if (increasing) { + const remainingArmor = armorEffect.system.armorChange.max - armorEffect.system.armorChange.value; + usedArmorChange = Math.min(remainingChange, remainingArmor); + remainingChange -= usedArmorChange; + } else { + const changeChange = Math.min(armorEffect.system.armorChange.value, remainingChange); + usedArmorChange -= changeChange; + remainingChange -= changeChange; + } + + if (!usedArmorChange) continue; + else { + if (!embeddedUpdates[armorEffect.parent.id]) + embeddedUpdates[armorEffect.parent.id] = { doc: armorEffect.parent, updates: [] }; + + embeddedUpdates[armorEffect.parent.id].updates.push({ + '_id': armorEffect.id, + 'system.changes': [ + { + ...armorEffect.system.armorChange, + value: armorEffect.system.armorChange.value + usedArmorChange + } + ] + }); + } + + if (remainingChange === 0) break; + } + + for (const { doc, updates } of Object.values(embeddedUpdates)) + doc.updateEmbeddedDocuments('ActiveEffect', updates); + } + get sheetLists() { const ancestryFeatures = [], communityFeatures = [], From a76479e9b7117f4f87a14a5a840da0ed285a752b Mon Sep 17 00:00:00 2001 From: WBHarry Date: Mon, 9 Feb 2026 22:43:21 +0100 Subject: [PATCH 06/23] . --- .../dialogs/damageReductionDialog.mjs | 6 ++-- module/applications/sheets/api/base-actor.mjs | 16 +++++----- module/applications/sheets/items/armor.mjs | 4 +++ module/data/actor/character.mjs | 29 +++++++++++-------- module/documents/actor.mjs | 19 +++++++----- 5 files changed, 44 insertions(+), 30 deletions(-) diff --git a/module/applications/dialogs/damageReductionDialog.mjs b/module/applications/dialogs/damageReductionDialog.mjs index cd0a5cf7..494bad0f 100644 --- a/module/applications/dialogs/damageReductionDialog.mjs +++ b/module/applications/dialogs/damageReductionDialog.mjs @@ -21,7 +21,7 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap ); const canApplyArmor = damageType.every(t => actor.system.armorApplicableDamageTypes[t] === true); - const availableArmor = actor.system.armorScore - actor.system.armor.system.marks.value; + const availableArmor = actor.system.armorScore.max - actor.system.armorScore.value; const maxArmorMarks = canApplyArmor ? availableArmor : 0; const armor = [...Array(maxArmorMarks).keys()].reduce((acc, _) => { @@ -124,7 +124,7 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap const { selectedArmorMarks, selectedStressMarks, stressReductions, currentMarks, currentDamage } = this.getDamageInfo(); - context.armorScore = this.actor.system.armorScore; + context.armorScore = this.actor.system.armorScore.max; context.armorMarks = currentMarks; context.basicMarksUsed = selectedArmorMarks.length === this.actor.system.rules.damageReduction.maxArmorMarked.value; @@ -218,7 +218,7 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap } if (this.rulesOn) { - if (!currentMark.selected && currentMarks === this.actor.system.armorScore) { + if (!currentMark.selected && currentMarks === this.actor.system.armorScore.max) { ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noAvailableArmorMarks')); return; } diff --git a/module/applications/sheets/api/base-actor.mjs b/module/applications/sheets/api/base-actor.mjs index 85ecd616..e619daad 100644 --- a/module/applications/sheets/api/base-actor.mjs +++ b/module/applications/sheets/api/base-actor.mjs @@ -36,7 +36,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { ], dragDrop: [ { dragSelector: '.inventory-item[data-type="attack"]', dropSelector: null }, - { dragSelector: ".currency[data-currency] .drag-handle", dropSelector: null } + { dragSelector: '.currency[data-currency] .drag-handle', dropSelector: null } ] }; @@ -92,7 +92,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { value: context.source.system.gold[key] }; } - context.inventory.hasCurrency = Object.values(context.inventory.currencies).some((c) => c.enabled); + context.inventory.hasCurrency = Object.values(context.inventory.currencies).some(c => c.enabled); } return context; @@ -160,7 +160,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { inactives: [] }; - for (const effect of this.actor.allApplicableEffects()) { + for (const effect of this.actor.allApplicableEffects({ noArmor: true })) { const list = effect.active ? context.effects.actives : context.effects.inactives; list.push(effect); } @@ -270,7 +270,9 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { currency }); if (quantity) { - originActor.update({ [`system.gold.${currency}`]: Math.max(0, originActor.system.gold[currency] - quantity) }); + originActor.update({ + [`system.gold.${currency}`]: Math.max(0, originActor.system.gold[currency] - quantity) + }); this.document.update({ [`system.gold.${currency}`]: this.document.system.gold[currency] + quantity }); } return; @@ -339,7 +341,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { */ async _onDragStart(event) { // Handle drag/dropping currencies - const currencyEl = event.currentTarget.closest(".currency[data-currency]"); + const currencyEl = event.currentTarget.closest('.currency[data-currency]'); if (currencyEl) { const currency = currencyEl.dataset.currency; const data = { type: 'Currency', currency, originActor: this.document.uuid }; @@ -359,8 +361,8 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { event.dataTransfer.setData('text/plain', JSON.stringify(attackData)); event.dataTransfer.setDragImage(attackItem.querySelector('img'), 60, 0); return; - } - + } + const item = await getDocFromElement(event.target); if (item) { const dragData = { diff --git a/module/applications/sheets/items/armor.mjs b/module/applications/sheets/items/armor.mjs index de61e5b1..4c69c822 100644 --- a/module/applications/sheets/items/armor.mjs +++ b/module/applications/sheets/items/armor.mjs @@ -50,6 +50,10 @@ export default class ArmorSheet extends ItemAttachmentSheet(DHBaseItemSheet) { context.features = this.document.system.armorFeatures.map(x => x.value); context.armorScore = this.document.system.armorData.max; break; + case 'effects': + context.effects.actives = context.effects.actives.filter(x => x.type !== 'armor'); + context.effects.inactives = context.effects.actives.filter(x => x.type !== 'armor'); + break; } return context; diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index 294fa77e..87f203ec 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -507,8 +507,8 @@ export default class DhCharacter extends BaseDataActor { } } - async updateArmorValue(armorChange) { - if (armorChange === 0) return; + async updateArmorValue({ value: armorChange = 0, clear = false }) { + if (armorChange === 0 && !clear) return; const increasing = armorChange >= 0; let remainingChange = Math.abs(armorChange); @@ -521,14 +521,18 @@ export default class DhCharacter extends BaseDataActor { const embeddedUpdates = []; for (const armorEffect of orderedEffects) { let usedArmorChange = 0; - if (increasing) { - const remainingArmor = armorEffect.system.armorChange.max - armorEffect.system.armorChange.value; - usedArmorChange = Math.min(remainingChange, remainingArmor); - remainingChange -= usedArmorChange; + if (clear) { + usedArmorChange -= armorEffect.system.armorChange.value; } else { - const changeChange = Math.min(armorEffect.system.armorChange.value, remainingChange); - usedArmorChange -= changeChange; - remainingChange -= changeChange; + if (increasing) { + const remainingArmor = armorEffect.system.armorChange.max - armorEffect.system.armorChange.value; + usedArmorChange = Math.min(remainingChange, remainingArmor); + remainingChange -= usedArmorChange; + } else { + const changeChange = Math.min(armorEffect.system.armorChange.value, remainingChange); + usedArmorChange -= changeChange; + remainingChange -= changeChange; + } } if (!usedArmorChange) continue; @@ -547,11 +551,12 @@ export default class DhCharacter extends BaseDataActor { }); } - if (remainingChange === 0) break; + if (remainingChange === 0 && !clear) break; } - for (const { doc, updates } of Object.values(embeddedUpdates)) - doc.updateEmbeddedDocuments('ActiveEffect', updates); + const updateValues = Object.values(embeddedUpdates); + for (const [index, { doc, updates }] of updateValues.entries()) + doc.updateEmbeddedDocuments('ActiveEffect', updates, { render: index === updateValues.length - 1 }); } get sheetLists() { diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 4807f497..2eafc70f 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -784,12 +784,7 @@ export default class DhpActor extends Actor { ); break; case 'armor': - if (this.system.armor?.system?.marks) { - updates.armor.resources['system.marks.value'] = Math.max( - Math.min(valueFunc(this.system.armor.system.marks, r), this.system.armorScore), - 0 - ); - } + this.system.updateArmorValue(r); break; default: if (this.system.resources?.[r.key]) { @@ -993,7 +988,15 @@ export default class DhpActor extends Actor { return allTokens; } - applyActiveEffects(phase) { - super.applyActiveEffects(phase); + /**@inheritdoc */ + *allApplicableEffects({ noArmor } = {}) { + for (const effect of this.effects) { + yield effect; + } + for (const item of this.items) { + for (const effect of item.effects) { + if (effect.transfer && (!noArmor || effect.type !== 'armor')) yield effect; + } + } } } From 94262ba6ec195b1da3e6de0f1a809bb3847aa73a Mon Sep 17 00:00:00 2001 From: WBHarry Date: Mon, 9 Feb 2026 23:35:50 +0100 Subject: [PATCH 07/23] Added createDialog --- module/applications/sheets/api/application-mixin.mjs | 6 ++++++ module/documents/activeEffect.mjs | 2 +- templates/sheets/actors/character/effects.hbs | 1 + templates/sheets/global/tabs/tab-effects.hbs | 1 + 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index df09ff41..4de3d2be 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -712,17 +712,23 @@ export default function DHApplicationMixin(Base) { const cls = type === 'action' ? game.system.api.models.actions.actionsTypes.base : getDocumentClass(documentClass); + const data = { name: cls.defaultName({ type, parent }), type, system: systemData }; + if (inVault) data['system.inVault'] = true; if (disabled) data.disabled = true; if (type === 'domainCard' && parent?.system.domains?.length) { data.system.domain = parent.system.domains[0]; } + if (documentClass === 'ActiveEffect') { + return cls.createDialog(data, { parent: this.document }); + } + const doc = await cls.create(data, { parent, renderSheet: !event.shiftKey }); if (parentIsItem && type === 'feature') { await this.document.update({ diff --git a/module/documents/activeEffect.mjs b/module/documents/activeEffect.mjs index ac30c678..055267b2 100644 --- a/module/documents/activeEffect.mjs +++ b/module/documents/activeEffect.mjs @@ -62,7 +62,7 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect { throw new Error('The array of sub-types to restrict to must not be empty.'); } - const creatableEffects = ['base', 'armor']; + const creatableEffects = types || ['base', 'armor']; const documentTypes = this.TYPES.filter(type => creatableEffects.includes(type)).map(type => { const labelKey = `TYPES.ActiveEffect.${type}`; const label = game.i18n.has(labelKey) ? game.i18n.localize(labelKey) : type; diff --git a/templates/sheets/actors/character/effects.hbs b/templates/sheets/actors/character/effects.hbs index 30fb31f4..3355d575 100644 --- a/templates/sheets/actors/character/effects.hbs +++ b/templates/sheets/actors/character/effects.hbs @@ -18,6 +18,7 @@ collection=effects.inactives canCreate=true hideResources=true + disabled=true }}
\ No newline at end of file diff --git a/templates/sheets/global/tabs/tab-effects.hbs b/templates/sheets/global/tabs/tab-effects.hbs index c89c6ff4..7d731b6f 100644 --- a/templates/sheets/global/tabs/tab-effects.hbs +++ b/templates/sheets/global/tabs/tab-effects.hbs @@ -17,5 +17,6 @@ collection=effects.inactives canCreate=true hideResources=true + disabled=true }} \ No newline at end of file From 47b16392ebe26b98a4773f274e2ab70400d50b1a Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 10 Feb 2026 17:37:59 +0100 Subject: [PATCH 08/23] . --- module/applications/sheets/api/base-actor.mjs | 2 +- module/data/action/baseAction.mjs | 22 ++++++++++--------- module/data/activeEffect/armorEffect.mjs | 15 +++++++++++++ module/data/actor/base.mjs | 4 ++-- module/data/item/base.mjs | 8 +++++-- module/documents/actor.mjs | 16 +++++--------- module/helpers/utils.mjs | 12 ++++++---- 7 files changed, 49 insertions(+), 30 deletions(-) diff --git a/module/applications/sheets/api/base-actor.mjs b/module/applications/sheets/api/base-actor.mjs index e619daad..2fb3f68a 100644 --- a/module/applications/sheets/api/base-actor.mjs +++ b/module/applications/sheets/api/base-actor.mjs @@ -160,7 +160,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { inactives: [] }; - for (const effect of this.actor.allApplicableEffects({ noArmor: true })) { + for (const effect of this.actor.allApplicableEffects({ noTransferArmor: true })) { const list = effect.active ? context.effects.actives : context.effects.inactives; list.push(effect); } diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index f6ffe75f..23d30cb2 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -295,17 +295,19 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel static async getEffects(actor, effectParent) { if (!actor) return []; - return Array.from(await actor.allApplicableEffects()).filter(effect => { - /* Effects on weapons only ever apply for the weapon itself */ - if (effect.parent.type === 'weapon') { - /* Unless they're secondary - then they apply only to other primary weapons */ - if (effect.parent.system.secondary) { - if (effectParent?.type !== 'weapon' || effectParent?.system.secondary) return false; - } else if (effectParent?.id !== effect.parent.id) return false; - } + return Array.from(await actor.allApplicableEffects({ noTransferArmor: true, noSelfArmor: true })).filter( + effect => { + /* Effects on weapons only ever apply for the weapon itself */ + if (effect.parent.type === 'weapon') { + /* Unless they're secondary - then they apply only to other primary weapons */ + if (effect.parent.system.secondary) { + if (effectParent?.type !== 'weapon' || effectParent?.system.secondary) return false; + } else if (effectParent?.id !== effect.parent.id) return false; + } - return !effect.isSuppressed; - }); + return !effect.isSuppressed; + } + ); } /** diff --git a/module/data/activeEffect/armorEffect.mjs b/module/data/activeEffect/armorEffect.mjs index f52f5efc..597cdf27 100644 --- a/module/data/activeEffect/armorEffect.mjs +++ b/module/data/activeEffect/armorEffect.mjs @@ -1,3 +1,5 @@ +import { getScrollTextData } from '../../helpers/utils.mjs'; + /** * ArmorEffects are ActiveEffects that have a static changes field of length 1. It includes current and maximum armor. * When applied to a character, it adds to their currently marked and maximum armor. @@ -185,6 +187,19 @@ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.cannotAlterArmorEffectType')); return false; } + + if (changes.system.changes[0].value !== this.armorChange.value && this.parent.actor?.type === 'character') { + const increased = changes.system.changes[0].value > this.armorChange.value; + const value = -1 * (this.armorChange.value - changes.system.changes[0].value); + options.scrollingTextData = [getScrollTextData(increased, value, 'armor')]; + } } } + + _onUpdate(changes, options, userId) { + super._onUpdate(changes, options, userId); + + if (options.scrollingTextData && this.parent.actor?.type === 'character') + this.parent.actor.queueScrollText(options.scrollingTextData); + } } diff --git a/module/data/actor/base.mjs b/module/data/actor/base.mjs index 3b4de08d..14bc4fe8 100644 --- a/module/data/actor/base.mjs +++ b/module/data/actor/base.mjs @@ -1,6 +1,6 @@ import DHBaseActorSettings from '../../applications/sheets/api/actor-setting.mjs'; import DHItem from '../../documents/item.mjs'; -import { getScrollTextData } from '../../helpers/utils.mjs'; +import { getResourceScrollTextData } from '../../helpers/utils.mjs'; const fields = foundry.data.fields; @@ -211,7 +211,7 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel { const textData = Object.keys(changes.system.resources).reduce((acc, key) => { const resource = changes.system.resources[key]; if (resource.value !== undefined && resource.value !== this.resources[key].value) { - acc.push(getScrollTextData(this.resources, resource, key)); + acc.push(getResourceScrollTextData(this.resources, resource, key)); } return acc; diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs index 447da3bf..334b0c5e 100644 --- a/module/data/item/base.mjs +++ b/module/data/item/base.mjs @@ -8,7 +8,7 @@ * @property {boolean} isInventoryItem- Indicates whether items of this type is a Inventory Item */ -import { addLinkedItemsDiff, getScrollTextData, updateLinkedItemApps } from '../../helpers/utils.mjs'; +import { addLinkedItemsDiff, getResourceScrollTextData, updateLinkedItemApps } from '../../helpers/utils.mjs'; import { ActionsField } from '../fields/actionField.mjs'; import FormulaField from '../fields/formulaField.mjs'; @@ -224,7 +224,11 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { const armorChanged = changed.system?.marks?.value !== undefined && changed.system.marks.value !== this.marks.value; if (armorChanged && autoSettings.resourceScrollTexts && this.parent.parent?.type === 'character') { - const armorData = getScrollTextData(this.parent.parent.system.resources, changed.system.marks, 'armor'); + const armorData = getResourceScrollTextData( + this.parent.parent.system.resources, + changed.system.marks, + 'armor' + ); options.scrollingTextData = [armorData]; } diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 2eafc70f..da399df5 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -573,8 +573,7 @@ export default class DhpActor extends Actor { const availableStress = this.system.resources.stress.max - this.system.resources.stress.value; const canUseArmor = - this.system.armor && - this.system.armor.system.marks.value < this.system.armorScore && + this.system.armorScore.value < this.system.armorScore.max && type.every(t => this.system.armorApplicableDamageTypes[t] === true); const canUseStress = Object.keys(stressDamageReduction).reduce((acc, x) => { const rule = stressDamageReduction[x]; @@ -614,12 +613,7 @@ export default class DhpActor extends Actor { const hpDamage = updates.find(u => u.key === CONFIG.DH.GENERAL.healingTypes.hitPoints.id); if (hpDamage?.value) { hpDamage.value = this.convertDamageToThreshold(hpDamage.value); - if ( - this.type === 'character' && - !isDirect && - this.system.armor && - this.#canReduceDamage(hpDamage.value, hpDamage.damageTypes) - ) { + if (this.type === 'character' && !isDirect && this.#canReduceDamage(hpDamage.value, hpDamage.damageTypes)) { const armorSlotResult = await this.owner.query( 'armorSlot', { @@ -989,13 +983,13 @@ export default class DhpActor extends Actor { } /**@inheritdoc */ - *allApplicableEffects({ noArmor } = {}) { + *allApplicableEffects({ noSelfArmor, noTransferArmor } = {}) { for (const effect of this.effects) { - yield effect; + if (!noSelfArmor || effect.type !== 'armor') yield effect; } for (const item of this.items) { for (const effect of item.effects) { - if (effect.transfer && (!noArmor || effect.type !== 'armor')) yield effect; + if (effect.transfer && (!noTransferArmor || effect.type !== 'armor')) yield effect; } } } diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 980349ba..18f7dfd7 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -378,14 +378,18 @@ export const arraysEqual = (a, b) => export const setsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value)); -export function getScrollTextData(resources, resource, key) { - const { reversed, label } = CONFIG.DH.ACTOR.scrollingTextResource[key]; - const { BOTTOM, TOP } = CONST.TEXT_ANCHOR_POINTS; +export function getResourceScrollTextData(resources, resource, key) { const increased = resources[key].value < resource.value; const value = -1 * (resources[key].value - resource.value); - const text = `${game.i18n.localize(label)} ${value.signedString()}`; + return getScrollTextData(increased, value, key); +} +export function getScrollTextData(increased, value, key) { + const { reversed, label } = CONFIG.DH.ACTOR.scrollingTextResource[key]; + const { BOTTOM, TOP } = CONST.TEXT_ANCHOR_POINTS; + + const text = `${game.i18n.localize(label)} ${value.signedString()}`; const stroke = increased ? (reversed ? 0xffffff : 0x000000) : reversed ? 0x000000 : 0xffffff; const fill = increased ? (reversed ? 0x0032b1 : 0xffe760) : reversed ? 0xffe760 : 0x0032b1; const direction = increased ? (reversed ? BOTTOM : TOP) : reversed ? TOP : BOTTOM; From b9e3eec34c5be5049b58f447c5c237526885e5b5 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 10 Feb 2026 21:44:22 +0100 Subject: [PATCH 09/23] Updated Armor SRD --- lang/en.json | 3 + module/applications/ui/_module.mjs | 1 + module/applications/ui/progress.mjs | 27 +++++ module/data/activeEffect/armorEffect.mjs | 12 +-- module/data/item/armor.mjs | 21 +++- module/documents/item.mjs | 10 ++ module/systemRegistration/migrations.mjs | 100 ++++++++++++++++++ ...nced_Chainmail_Armor_LzLOJ9EVaHWAjoq9.json | 38 +++++++ ...ced_Full_Plate_Armor_crIbCb9NZ4K0VpoU.json | 38 +++++++ ...anced_Gambeson_Armor_epkAmlZVk7HOfUUT.json | 38 +++++++ ...vanced_Leather_Armor_itSOp2GCyem0f7oM.json | 41 ++++++- .../armor_Bare_Bones_ITAjcigTcUw5pMCN.json | 38 +++++++ ..._Bellamoi_Fine_Armor_WuoVwZA53XRAIt6d.json | 38 +++++++ ...rmor_Bladefare_Armor_mNN6pvcsS10ChrWF.json | 38 +++++++ ...rmor_Chainmail_Armor_haULhuEg37zUUvhb.json | 38 +++++++ ...mor_Channeling_Armor_vMJxEWz1srfwMsoj.json | 38 +++++++ ...or_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json | 41 ++++++- ...or_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json | 41 ++++++- ...lundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json | 38 +++++++ ...mor_Emberwoven_Armor_bcQUh4QG3qFX0Vx6.json | 41 ++++++- ...Full_Fortified_Armor_7emTSt6nhZuTlvt5.json | 38 +++++++ ...mor_Full_Plate_Armor_UdUJNa31WxFW2noa.json | 38 +++++++ ...armor_Gambeson_Armor_yJFp1bfpecDcStVK.json | 38 +++++++ ...mor_Harrowbone_Armor_dvyQeUVRLc9y6rnt.json | 41 ++++++- ...oved_Chainmail_Armor_K5WkjS0NGqHYmhU3.json | 38 +++++++ ...ved_Full_Plate_Armor_9f7RozpPTqrzJS1m.json | 38 +++++++ ...roved_Gambeson_Armor_jphnMZjnS2FkOH3s.json | 38 +++++++ ...proved_Leather_Armor_t91M61pSCMKStTNt.json | 41 ++++++- ...ee_Breastplate_Armor_tzZntboNtHL5C6VM.json | 38 +++++++ .../armor_Leather_Armor_nibfdNtp2PtxvbVz.json | 41 ++++++- ...dary_Chainmail_Armor_EsIN5OLKe9ZYFNXZ.json | 38 +++++++ ...ary_Full_Plate_Armor_SXWjUR2aUR6bYvdl.json | 38 +++++++ ...ndary_Gambeson_Armor_c6tMXz4rPf9ioQrf.json | 38 +++++++ ...endary_Leather_Armor_Tptgl5WOj76TyFn7.json | 41 ++++++- ...armor_Monett_s_Cloak_AQzU2RsqS5V5bd1v.json | 38 +++++++ ...armor_Rosewild_Armor_tN8kAeBvNKM3EBFo.json | 41 ++++++- ...nes_of_Fortification_P4qAEDJUoNLgVRsA.json | 41 ++++++- ...netan_Floating_Armor_tHlBUDQC24YMZqd6.json | 41 ++++++- ...mor_Savior_Chainmail_8X16lJQ3xltTwynm.json | 38 +++++++ ...r_Spiked_Plate_Armor_QjwsIhXKqnlvRBMv.json | 38 +++++++ ...mor_Tyris_Soft_Armor_PSW3BxCGmtLeWOxM.json | 41 ++++++- ...r_Veritas_Opal_Armor_OvzgUTYy2RCN85vV.json | 41 ++++++- 42 files changed, 1520 insertions(+), 23 deletions(-) create mode 100644 module/applications/ui/progress.mjs diff --git a/lang/en.json b/lang/en.json index 26647cc2..b6684e59 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2982,6 +2982,9 @@ "cannotAlterArmorEffectChanges": "You cannot alter the changes length of an armor effect", "cannotAlterArmorEffectType": "You cannot alter the type of armor effect changes" }, + "Progress": { + "migrationLabel": "Performing system migration. Please wait and do not close Foundry." + }, "Sidebar": { "actorDirectory": { "tier": "Tier {tier} {type}", diff --git a/module/applications/ui/_module.mjs b/module/applications/ui/_module.mjs index 8c5c020e..80d3ebe4 100644 --- a/module/applications/ui/_module.mjs +++ b/module/applications/ui/_module.mjs @@ -7,3 +7,4 @@ export { default as DhFearTracker } from './fearTracker.mjs'; export { default as DhHotbar } from './hotbar.mjs'; export { default as DhSceneNavigation } from './sceneNavigation.mjs'; export { ItemBrowser } from './itemBrowser.mjs'; +export { default as DhProgress } from './progress.mjs'; diff --git a/module/applications/ui/progress.mjs b/module/applications/ui/progress.mjs new file mode 100644 index 00000000..eca4ad6b --- /dev/null +++ b/module/applications/ui/progress.mjs @@ -0,0 +1,27 @@ +export default class DhProgress { + #notification; + + constructor({ max, label = '' }) { + this.max = max; + this.label = label; + this.#notification = ui.notifications.info(this.label, { progress: true }); + } + + updateMax(newMax) { + this.max = newMax; + } + + advance({ by = 1, label = this.label } = {}) { + if (this.value === this.max) return; + this.value += Math.abs(by); + this.#notification.update({ message: label, pct: this.value / this.max }); + } + + close({ label = '' } = {}) { + this.#notification.update({ message: label, pct: 1 }); + } + + static createMigrationProgress(max = 0) { + return new DhProgress({ max, label: game.i18n.localize('DAGGERHEART.UI.Progress.migrationLabel') }); + } +} diff --git a/module/data/activeEffect/armorEffect.mjs b/module/data/activeEffect/armorEffect.mjs index 597cdf27..c277c904 100644 --- a/module/data/activeEffect/armorEffect.mjs +++ b/module/data/activeEffect/armorEffect.mjs @@ -107,9 +107,10 @@ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel } async updateArmorMax(newMax) { + const { effect, ...baseChange } = this.armorChange; const newChanges = [ { - ...this.armorChange, + ...baseChange, max: newMax, value: Math.min(this.armorChange.value, newMax) } @@ -152,7 +153,7 @@ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel armorChange.key = 'system.armorScore'; } - static getDefaultEffectData() { + static getDefaultObject() { return { type: 'armor', name: game.i18n.localize('DAGGERHEART.EFFECTS.Armor.newArmorEffect'), @@ -160,13 +161,6 @@ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel }; } - async _preCreate(data, options, user) { - const allowed = await super._preCreate(data, options, user); - if (allowed === false) return; - - await this.updateSource({ ...ArmorEffect.getDefaultEffectData(), data }); - } - async _preUpdate(changes, options, user) { const allowed = await super._preUpdate(changes, options, user); if (allowed === false) return false; diff --git a/module/data/item/armor.mjs b/module/data/item/armor.mjs index 117f3963..3170627f 100644 --- a/module/data/item/armor.mjs +++ b/module/data/item/armor.mjs @@ -85,7 +85,7 @@ export default class DHArmor extends AttachableItem { if (!this.parent.effects.some(x => x.type === 'armor')) { this.parent.createEmbeddedDocuments('ActiveEffect', [ - game.system.api.data.activeEffects.ArmorEffect.getDefaultEffectData() + game.system.api.data.activeEffects.ArmorEffect.getDefaultObject() ]); } } @@ -175,6 +175,25 @@ export default class DHArmor extends AttachableItem { } } + /** @inheritDoc */ + static migrateDocumentData(source) { + if (source.system.baseScore !== undefined && !source.effects.some(x => x.type === 'armor')) { + // source.effects.push({ + // ...game.system.api.data.activeEffects.ArmorEffect.getDefaultObject(), + // changes: [{ + // type: CONFIG.DH.GENERAL.activeEffectModes.armor.id, + // phase: 'initial', + // priority: 20, + // value: 0, + // max: source.system.baseScore + // }], + // }); + if (!source.flags) source.flags = {}; + if (!source.flags.daggerheart) source.flags.daggerheart = {}; + source.flags.daggerheart.baseScoreMigrationValue = source.system.baseScore; + } + } + /** * Generates a list of localized tags based on this item's type-specific properties. * @returns {string[]} An array of localized tag strings. diff --git a/module/documents/item.mjs b/module/documents/item.mjs index 67f7d253..ce87db4e 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -230,4 +230,14 @@ export default class DHItem extends foundry.documents.Item { async _preDelete() { this.deleteTriggers(); } + + /** @inheritDoc */ + static migrateData(source) { + const documentClass = game.system.api.data.items[`DH${source.type.capitalize()}`]; + if (documentClass?.migrateDocumentData) { + documentClass.migrateDocumentData(source); + } + + return super.migrateData(source); + } } diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs index 4216c38f..858481c5 100644 --- a/module/systemRegistration/migrations.mjs +++ b/module/systemRegistration/migrations.mjs @@ -246,6 +246,106 @@ export async function runMigrations() { lastMigrationVersion = '1.6.0'; } + + if (foundry.utils.isNewerVersion('2.0.0', lastMigrationVersion)) { + /* Migrate existing armors to the new Armor Effects */ + const progress = game.system.api.applications.ui.DhProgress.createMigrationProgress(0); + + const lockedPacks = []; + const itemPacks = game.packs.filter(x => x.metadata.type === 'Item'); + const actorPacks = game.packs.filter(x => x.metadata.type === 'Actor'); + + const getIndexes = async (packs, type) => { + const indexes = []; + for (const pack of packs) { + const indexValues = pack.index.values().reduce((acc, index) => { + if (index.type === type) acc.push(index.uuid); + return acc; + }, []); + + if (indexValues.length && pack.locked) { + lockedPacks.push(pack.collection); + await pack.configure({ locked: false }); + } + + indexes.push(...indexValues); + } + + return indexes; + }; + + const armorEntries = await getIndexes(itemPacks, 'armor'); + const actorEntries = await getIndexes(actorPacks, 'actor'); + + const worldArmors = game.items.filter(x => x instanceof game.system.api.documents.DHItem && x.type === 'armor'); + + for (const character of game.actors.filter(x => x.type === 'character')) { + worldArmors.push(...character.items.filter(x => x.type === 'armor')); + } + + /* The async fetches are the mainstay of time. Leaving 1 progress for the sync logic */ + const newMax = armorEntries.length + actorEntries.length + 1; + progress.updateMax(newMax); + + const compendiumArmors = []; + for (const entry of armorEntries) { + const armor = await foundry.utils.fromUuid(entry); + compendiumArmors.push(armor); + progress.advance(); + } + + for (const entry of actorEntries) { + const actor = await foundry.utils.fromUuid(entry); + compendiumArmors.push(...actor.items.filter(x => x.type === 'armor')); + progress.advance(); + } + + // const lockedPacks = []; + // const compendiumArmors = []; + // const compendiumCharacters = []; + // for (let pack of game.packs.filter(x => x.metadata.type === 'Item')) { + // if (pack.locked) { + // lockedPacks.push(pack.collection); + // await pack.configure({ locked: false }); + // } + + // const documents = await pack.getDocuments({ type: 'armor' }); + // compendiumArmors.push(...documents.filter(x => x instanceof game.system.api.documents.DHItem && x.type === 'armor')); + // compendiumCharacters.push(...documents.filter(x => x.type === 'character')); + // } + + for (const armor of [...compendiumArmors, ...worldArmors]) { + const hasArmorEffect = armor.effects.some(x => x.type === 'armor'); + const migrationArmorScore = armor.flags.daggerheart?.baseScoreMigrationValue; + if (migrationArmorScore !== undefined && !hasArmorEffect) { + await armor.createEmbeddedDocuments('ActiveEffect', [ + { + ...game.system.api.data.activeEffects.ArmorEffect.getDefaultObject(), + changes: [ + { + type: CONFIG.DH.GENERAL.activeEffectModes.armor.id, + phase: 'initial', + priority: 20, + value: 0, + max: migrationArmorScore + } + ] + } + ]); + } + } + + progress.advance(); + + for (let packId of lockedPacks) { + const pack = game.packs.get(packId); + await pack.configure({ locked: true }); + } + + progress.close(); + + // lastMigrationVersion = '2.0.0'; + } //#endregion await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, lastMigrationVersion); 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 174f20c8..e7c726af 100644 --- a/src/packs/items/armors/armor_Advanced_Chainmail_Armor_LzLOJ9EVaHWAjoq9.json +++ b/src/packs/items/armors/armor_Advanced_Chainmail_Armor_LzLOJ9EVaHWAjoq9.json @@ -63,6 +63,44 @@ "compendiumSource": null }, "_key": "!items.effects!LzLOJ9EVaHWAjoq9.qlzHOAnpBYzosQxK" + }, + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 6 + } + ] + }, + "_id": "I0649iXfgoME38fU", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!LzLOJ9EVaHWAjoq9.I0649iXfgoME38fU" } ], "sort": 0, 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 dbc9d29f..2a06068f 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 @@ -68,6 +68,44 @@ "compendiumSource": null }, "_key": "!items.effects!crIbCb9NZ4K0VpoU.awdHgEaM54G3emOU" + }, + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 6 + } + ] + }, + "_id": "kRaWET7LV25rD4jy", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!crIbCb9NZ4K0VpoU.kRaWET7LV25rD4jy" } ], "sort": 0, 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 c9ffc8a3..84c3b35a 100644 --- a/src/packs/items/armors/armor_Advanced_Gambeson_Armor_epkAmlZVk7HOfUUT.json +++ b/src/packs/items/armors/armor_Advanced_Gambeson_Armor_epkAmlZVk7HOfUUT.json @@ -63,6 +63,44 @@ "compendiumSource": null }, "_key": "!items.effects!epkAmlZVk7HOfUUT.Fq9Q93IHCchhfSss" + }, + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 5 + } + ] + }, + "_id": "lJBLFQHDjmgLsLL8", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!epkAmlZVk7HOfUUT.lJBLFQHDjmgLsLL8" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Advanced_Leather_Armor_itSOp2GCyem0f7oM.json b/src/packs/items/armors/armor_Advanced_Leather_Armor_itSOp2GCyem0f7oM.json index 4e1927e3..c7d039df 100644 --- a/src/packs/items/armors/armor_Advanced_Leather_Armor_itSOp2GCyem0f7oM.json +++ b/src/packs/items/armors/armor_Advanced_Leather_Armor_itSOp2GCyem0f7oM.json @@ -25,7 +25,46 @@ "artist": "" } }, - "effects": [], + "effects": [ + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 5 + } + ] + }, + "_id": "1vzHmkVScl1KyHxy", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!itSOp2GCyem0f7oM.1vzHmkVScl1KyHxy" + } + ], "sort": 0, "ownership": { "default": 0, diff --git a/src/packs/items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json b/src/packs/items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json index 5158b100..3e882f9f 100644 --- a/src/packs/items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json +++ b/src/packs/items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json @@ -63,6 +63,44 @@ "compendiumSource": null }, "_key": "!items.effects!ITAjcigTcUw5pMCN.8ze88zUwdkQSKKJq" + }, + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 3 + } + ] + }, + "_id": "B5hlwTWBUSJYZurq", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!ITAjcigTcUw5pMCN.B5hlwTWBUSJYZurq" } ], "sort": 0, 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 ce4e35fd..dafbe5b1 100644 --- a/src/packs/items/armors/armor_Bellamoi_Fine_Armor_WuoVwZA53XRAIt6d.json +++ b/src/packs/items/armors/armor_Bellamoi_Fine_Armor_WuoVwZA53XRAIt6d.json @@ -63,6 +63,44 @@ "compendiumSource": null }, "_key": "!items.effects!WuoVwZA53XRAIt6d.Hy0sNtFS1JAXxgwC" + }, + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 5 + } + ] + }, + "_id": "lDRMjmZXRJDbhK03", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!WuoVwZA53XRAIt6d.lDRMjmZXRJDbhK03" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Bladefare_Armor_mNN6pvcsS10ChrWF.json b/src/packs/items/armors/armor_Bladefare_Armor_mNN6pvcsS10ChrWF.json index 8b276d5f..8a6f1132 100644 --- a/src/packs/items/armors/armor_Bladefare_Armor_mNN6pvcsS10ChrWF.json +++ b/src/packs/items/armors/armor_Bladefare_Armor_mNN6pvcsS10ChrWF.json @@ -63,6 +63,44 @@ "compendiumSource": null }, "_key": "!items.effects!mNN6pvcsS10ChrWF.s8KtTIngTjnOlaTP" + }, + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 6 + } + ] + }, + "_id": "qYkj3jKDdFzflfh4", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!mNN6pvcsS10ChrWF.qYkj3jKDdFzflfh4" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Chainmail_Armor_haULhuEg37zUUvhb.json b/src/packs/items/armors/armor_Chainmail_Armor_haULhuEg37zUUvhb.json index f7526e96..7c161931 100644 --- a/src/packs/items/armors/armor_Chainmail_Armor_haULhuEg37zUUvhb.json +++ b/src/packs/items/armors/armor_Chainmail_Armor_haULhuEg37zUUvhb.json @@ -63,6 +63,44 @@ "compendiumSource": null }, "_key": "!items.effects!haULhuEg37zUUvhb.ZfO5NjpqEIzZVlPq" + }, + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 4 + } + ] + }, + "_id": "d6ICO5qZArh0xF1y", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!haULhuEg37zUUvhb.d6ICO5qZArh0xF1y" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Channeling_Armor_vMJxEWz1srfwMsoj.json b/src/packs/items/armors/armor_Channeling_Armor_vMJxEWz1srfwMsoj.json index a4bd0fea..f7306c06 100644 --- a/src/packs/items/armors/armor_Channeling_Armor_vMJxEWz1srfwMsoj.json +++ b/src/packs/items/armors/armor_Channeling_Armor_vMJxEWz1srfwMsoj.json @@ -63,6 +63,44 @@ "compendiumSource": null }, "_key": "!items.effects!vMJxEWz1srfwMsoj.8bwf1Ri3jYkjphEv" + }, + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 5 + } + ] + }, + "_id": "2q3uXc7EbTNSIjs8", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!vMJxEWz1srfwMsoj.2q3uXc7EbTNSIjs8" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json b/src/packs/items/armors/armor_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json index 5b39e41d..41b34d96 100644 --- a/src/packs/items/armors/armor_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json +++ b/src/packs/items/armors/armor_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json @@ -62,7 +62,46 @@ "artist": "" } }, - "effects": [], + "effects": [ + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 5 + } + ] + }, + "_id": "a8frrkkR4i2TBFdF", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!mdQ69eFHyAQUDmE7.a8frrkkR4i2TBFdF" + } + ], "sort": 0, "ownership": { "default": 0, diff --git a/src/packs/items/armors/armor_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json b/src/packs/items/armors/armor_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json index df692143..1cb28c6f 100644 --- a/src/packs/items/armors/armor_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json +++ b/src/packs/items/armors/armor_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json @@ -88,7 +88,46 @@ "artist": "" } }, - "effects": [], + "effects": [ + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 7 + } + ] + }, + "_id": "a1x2R28RtXE2jqu5", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!hAY6UgdGT7dj22Pr.a1x2R28RtXE2jqu5" + } + ], "sort": 0, "ownership": { "default": 0, 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 d63ce4df..c1b233a9 100644 --- a/src/packs/items/armors/armor_Elundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json +++ b/src/packs/items/armors/armor_Elundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json @@ -64,6 +64,44 @@ "compendiumSource": null }, "_key": "!items.effects!Q6LxmtFetDDkoZVZ.xGxqTCO8MjNq5Cw6" + }, + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 4 + } + ] + }, + "_id": "4yImObrCOaWLGxgH", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!Q6LxmtFetDDkoZVZ.4yImObrCOaWLGxgH" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Emberwoven_Armor_bcQUh4QG3qFX0Vx6.json b/src/packs/items/armors/armor_Emberwoven_Armor_bcQUh4QG3qFX0Vx6.json index 8ccc27e3..5ffdcc2e 100644 --- a/src/packs/items/armors/armor_Emberwoven_Armor_bcQUh4QG3qFX0Vx6.json +++ b/src/packs/items/armors/armor_Emberwoven_Armor_bcQUh4QG3qFX0Vx6.json @@ -86,7 +86,46 @@ "artist": "" } }, - "effects": [], + "effects": [ + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 6 + } + ] + }, + "_id": "8VtWedDMEX0tbqTn", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!bcQUh4QG3qFX0Vx6.8VtWedDMEX0tbqTn" + } + ], "sort": 0, "ownership": { "default": 0, 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 8eb964cc..25e47bc3 100644 --- a/src/packs/items/armors/armor_Full_Fortified_Armor_7emTSt6nhZuTlvt5.json +++ b/src/packs/items/armors/armor_Full_Fortified_Armor_7emTSt6nhZuTlvt5.json @@ -63,6 +63,44 @@ "compendiumSource": null }, "_key": "!items.effects!7emTSt6nhZuTlvt5.QIefVb73cm9gYju8" + }, + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 4 + } + ] + }, + "_id": "TRI0rfHs8RTSCmuY", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!7emTSt6nhZuTlvt5.TRI0rfHs8RTSCmuY" } ], "sort": 0, 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 1ea120ed..3bdb1c56 100644 --- a/src/packs/items/armors/armor_Full_Plate_Armor_UdUJNa31WxFW2noa.json +++ b/src/packs/items/armors/armor_Full_Plate_Armor_UdUJNa31WxFW2noa.json @@ -68,6 +68,44 @@ "compendiumSource": null }, "_key": "!items.effects!UdUJNa31WxFW2noa.mfKMW9SX3Mnos1nY" + }, + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 4 + } + ] + }, + "_id": "VpaGM3KSKQFG5wC8", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!UdUJNa31WxFW2noa.VpaGM3KSKQFG5wC8" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Gambeson_Armor_yJFp1bfpecDcStVK.json b/src/packs/items/armors/armor_Gambeson_Armor_yJFp1bfpecDcStVK.json index 1c775402..6b30d4bc 100644 --- a/src/packs/items/armors/armor_Gambeson_Armor_yJFp1bfpecDcStVK.json +++ b/src/packs/items/armors/armor_Gambeson_Armor_yJFp1bfpecDcStVK.json @@ -63,6 +63,44 @@ "compendiumSource": null }, "_key": "!items.effects!yJFp1bfpecDcStVK.v1FNEsypRF5W6vVc" + }, + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 3 + } + ] + }, + "_id": "qNXDdLhZkPe6Wnxa", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!yJFp1bfpecDcStVK.qNXDdLhZkPe6Wnxa" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Harrowbone_Armor_dvyQeUVRLc9y6rnt.json b/src/packs/items/armors/armor_Harrowbone_Armor_dvyQeUVRLc9y6rnt.json index 61d1fed7..9fd7971d 100644 --- a/src/packs/items/armors/armor_Harrowbone_Armor_dvyQeUVRLc9y6rnt.json +++ b/src/packs/items/armors/armor_Harrowbone_Armor_dvyQeUVRLc9y6rnt.json @@ -79,7 +79,46 @@ "artist": "" } }, - "effects": [], + "effects": [ + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 4 + } + ] + }, + "_id": "E3Zwl9T3EuK7hOOB", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!dvyQeUVRLc9y6rnt.E3Zwl9T3EuK7hOOB" + } + ], "sort": 0, "ownership": { "default": 0, 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 96e320d1..18b4f699 100644 --- a/src/packs/items/armors/armor_Improved_Chainmail_Armor_K5WkjS0NGqHYmhU3.json +++ b/src/packs/items/armors/armor_Improved_Chainmail_Armor_K5WkjS0NGqHYmhU3.json @@ -63,6 +63,44 @@ "compendiumSource": null }, "_key": "!items.effects!K5WkjS0NGqHYmhU3.JHupzYULxdQzFzuj" + }, + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 5 + } + ] + }, + "_id": "QXvJ3gL1kNcOLaqC", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!K5WkjS0NGqHYmhU3.QXvJ3gL1kNcOLaqC" } ], "sort": 0, 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 ee63a774..bd163402 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 @@ -68,6 +68,44 @@ "compendiumSource": null }, "_key": "!items.effects!9f7RozpPTqrzJS1m.wstJ1aKKtmXgCwxB" + }, + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 5 + } + ] + }, + "_id": "7ahyQs2byVwsUVAF", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!9f7RozpPTqrzJS1m.7ahyQs2byVwsUVAF" } ], "sort": 0, 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 6f4ea1c3..aca1d66d 100644 --- a/src/packs/items/armors/armor_Improved_Gambeson_Armor_jphnMZjnS2FkOH3s.json +++ b/src/packs/items/armors/armor_Improved_Gambeson_Armor_jphnMZjnS2FkOH3s.json @@ -63,6 +63,44 @@ "compendiumSource": null }, "_key": "!items.effects!jphnMZjnS2FkOH3s.BFwU3ErPaajUSMUz" + }, + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 4 + } + ] + }, + "_id": "uF8AksqGBBfKrrVM", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!jphnMZjnS2FkOH3s.uF8AksqGBBfKrrVM" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Improved_Leather_Armor_t91M61pSCMKStTNt.json b/src/packs/items/armors/armor_Improved_Leather_Armor_t91M61pSCMKStTNt.json index a4f38cc6..5413c999 100644 --- a/src/packs/items/armors/armor_Improved_Leather_Armor_t91M61pSCMKStTNt.json +++ b/src/packs/items/armors/armor_Improved_Leather_Armor_t91M61pSCMKStTNt.json @@ -25,7 +25,46 @@ "artist": "" } }, - "effects": [], + "effects": [ + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 4 + } + ] + }, + "_id": "OqL5x4lkQvjbzSGx", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!t91M61pSCMKStTNt.OqL5x4lkQvjbzSGx" + } + ], "sort": 0, "ownership": { "default": 0, 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 a9e9eaca..feba37e9 100644 --- a/src/packs/items/armors/armor_Irontree_Breastplate_Armor_tzZntboNtHL5C6VM.json +++ b/src/packs/items/armors/armor_Irontree_Breastplate_Armor_tzZntboNtHL5C6VM.json @@ -75,6 +75,44 @@ "compendiumSource": null }, "_key": "!items.effects!tzZntboNtHL5C6VM.P3aCN8PQgPXP4C9M" + }, + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 4 + } + ] + }, + "_id": "wKp8iBd3KfaMlzJh", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!tzZntboNtHL5C6VM.wKp8iBd3KfaMlzJh" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Leather_Armor_nibfdNtp2PtxvbVz.json b/src/packs/items/armors/armor_Leather_Armor_nibfdNtp2PtxvbVz.json index 37a13f2b..2f1548a7 100644 --- a/src/packs/items/armors/armor_Leather_Armor_nibfdNtp2PtxvbVz.json +++ b/src/packs/items/armors/armor_Leather_Armor_nibfdNtp2PtxvbVz.json @@ -25,7 +25,46 @@ "artist": "" } }, - "effects": [], + "effects": [ + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 3 + } + ] + }, + "_id": "TbWKQ0R6AfNNeqNd", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!nibfdNtp2PtxvbVz.TbWKQ0R6AfNNeqNd" + } + ], "sort": 0, "ownership": { "default": 0, 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 4bee5e4f..0b540fd5 100644 --- a/src/packs/items/armors/armor_Legendary_Chainmail_Armor_EsIN5OLKe9ZYFNXZ.json +++ b/src/packs/items/armors/armor_Legendary_Chainmail_Armor_EsIN5OLKe9ZYFNXZ.json @@ -63,6 +63,44 @@ "compendiumSource": null }, "_key": "!items.effects!EsIN5OLKe9ZYFNXZ.8Oa6Y375X8UpcPph" + }, + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 7 + } + ] + }, + "_id": "QAkiVlwfclxQ6JSD", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!EsIN5OLKe9ZYFNXZ.QAkiVlwfclxQ6JSD" } ], "sort": 0, 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 baf544c2..4e3ae4ed 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 @@ -68,6 +68,44 @@ "compendiumSource": null }, "_key": "!items.effects!SXWjUR2aUR6bYvdl.zvzkRX2Uevemmbz4" + }, + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 7 + } + ] + }, + "_id": "mMYVCcmoBJxjU0er", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!SXWjUR2aUR6bYvdl.mMYVCcmoBJxjU0er" } ], "sort": 0, 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 338c85e8..b6899fbd 100644 --- a/src/packs/items/armors/armor_Legendary_Gambeson_Armor_c6tMXz4rPf9ioQrf.json +++ b/src/packs/items/armors/armor_Legendary_Gambeson_Armor_c6tMXz4rPf9ioQrf.json @@ -63,6 +63,44 @@ "compendiumSource": null }, "_key": "!items.effects!c6tMXz4rPf9ioQrf.3AUNxBoj7mp1ziJQ" + }, + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 6 + } + ] + }, + "_id": "vgnBNFSXks1BcFQ5", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!c6tMXz4rPf9ioQrf.vgnBNFSXks1BcFQ5" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Legendary_Leather_Armor_Tptgl5WOj76TyFn7.json b/src/packs/items/armors/armor_Legendary_Leather_Armor_Tptgl5WOj76TyFn7.json index 42334dc4..a9d680af 100644 --- a/src/packs/items/armors/armor_Legendary_Leather_Armor_Tptgl5WOj76TyFn7.json +++ b/src/packs/items/armors/armor_Legendary_Leather_Armor_Tptgl5WOj76TyFn7.json @@ -25,7 +25,46 @@ "artist": "" } }, - "effects": [], + "effects": [ + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 6 + } + ] + }, + "_id": "m6HRZpgaMnuw1dE7", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!Tptgl5WOj76TyFn7.m6HRZpgaMnuw1dE7" + } + ], "sort": 0, "ownership": { "default": 0, 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 9a8e1f22..ce7d94c6 100644 --- a/src/packs/items/armors/armor_Monett_s_Cloak_AQzU2RsqS5V5bd1v.json +++ b/src/packs/items/armors/armor_Monett_s_Cloak_AQzU2RsqS5V5bd1v.json @@ -63,6 +63,44 @@ "compendiumSource": null }, "_key": "!items.effects!AQzU2RsqS5V5bd1v.3n4O7PyAWMEFdr5p" + }, + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 6 + } + ] + }, + "_id": "aRwIF0ss6R7AYNZf", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!AQzU2RsqS5V5bd1v.aRwIF0ss6R7AYNZf" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Rosewild_Armor_tN8kAeBvNKM3EBFo.json b/src/packs/items/armors/armor_Rosewild_Armor_tN8kAeBvNKM3EBFo.json index 0f0f6430..ee950d4f 100644 --- a/src/packs/items/armors/armor_Rosewild_Armor_tN8kAeBvNKM3EBFo.json +++ b/src/packs/items/armors/armor_Rosewild_Armor_tN8kAeBvNKM3EBFo.json @@ -55,7 +55,46 @@ "artist": "" } }, - "effects": [], + "effects": [ + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 5 + } + ] + }, + "_id": "YvXWUYVaXDHugsEr", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!tN8kAeBvNKM3EBFo.YvXWUYVaXDHugsEr" + } + ], "sort": 0, "ownership": { "default": 0, diff --git a/src/packs/items/armors/armor_Runes_of_Fortification_P4qAEDJUoNLgVRsA.json b/src/packs/items/armors/armor_Runes_of_Fortification_P4qAEDJUoNLgVRsA.json index 240a4f3e..b0501649 100644 --- a/src/packs/items/armors/armor_Runes_of_Fortification_P4qAEDJUoNLgVRsA.json +++ b/src/packs/items/armors/armor_Runes_of_Fortification_P4qAEDJUoNLgVRsA.json @@ -62,7 +62,46 @@ "artist": "" } }, - "effects": [], + "effects": [ + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 6 + } + ] + }, + "_id": "vkJeIaXB25W3MAt1", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!P4qAEDJUoNLgVRsA.vkJeIaXB25W3MAt1" + } + ], "sort": 0, "ownership": { "default": 0, diff --git a/src/packs/items/armors/armor_Runetan_Floating_Armor_tHlBUDQC24YMZqd6.json b/src/packs/items/armors/armor_Runetan_Floating_Armor_tHlBUDQC24YMZqd6.json index 8d4af425..690045f9 100644 --- a/src/packs/items/armors/armor_Runetan_Floating_Armor_tHlBUDQC24YMZqd6.json +++ b/src/packs/items/armors/armor_Runetan_Floating_Armor_tHlBUDQC24YMZqd6.json @@ -62,7 +62,46 @@ "artist": "" } }, - "effects": [], + "effects": [ + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 4 + } + ] + }, + "_id": "2dj1LoZcV6tCKpKj", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!tHlBUDQC24YMZqd6.2dj1LoZcV6tCKpKj" + } + ], "sort": 0, "ownership": { "default": 0, diff --git a/src/packs/items/armors/armor_Savior_Chainmail_8X16lJQ3xltTwynm.json b/src/packs/items/armors/armor_Savior_Chainmail_8X16lJQ3xltTwynm.json index 714e8592..eafe1420 100644 --- a/src/packs/items/armors/armor_Savior_Chainmail_8X16lJQ3xltTwynm.json +++ b/src/packs/items/armors/armor_Savior_Chainmail_8X16lJQ3xltTwynm.json @@ -93,6 +93,44 @@ "compendiumSource": null }, "_key": "!items.effects!8X16lJQ3xltTwynm.rkrqlwqtR9REgRx7" + }, + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 8 + } + ] + }, + "_id": "3Kn7ZRjhrw1WfALW", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!8X16lJQ3xltTwynm.3Kn7ZRjhrw1WfALW" } ], "sort": 0, 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 cb5fc720..cbd1e023 100644 --- a/src/packs/items/armors/armor_Spiked_Plate_Armor_QjwsIhXKqnlvRBMv.json +++ b/src/packs/items/armors/armor_Spiked_Plate_Armor_QjwsIhXKqnlvRBMv.json @@ -68,6 +68,44 @@ "compendiumSource": null }, "_key": "!items.effects!QjwsIhXKqnlvRBMv.V8CcTcVAIxHq8KNd" + }, + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 5 + } + ] + }, + "_id": "6YpS3uYWIbeSgreg", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!QjwsIhXKqnlvRBMv.6YpS3uYWIbeSgreg" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Tyris_Soft_Armor_PSW3BxCGmtLeWOxM.json b/src/packs/items/armors/armor_Tyris_Soft_Armor_PSW3BxCGmtLeWOxM.json index da640830..0837f512 100644 --- a/src/packs/items/armors/armor_Tyris_Soft_Armor_PSW3BxCGmtLeWOxM.json +++ b/src/packs/items/armors/armor_Tyris_Soft_Armor_PSW3BxCGmtLeWOxM.json @@ -55,7 +55,46 @@ "artist": "" } }, - "effects": [], + "effects": [ + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 5 + } + ] + }, + "_id": "tiE0sRrTm2Ex9TAO", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!PSW3BxCGmtLeWOxM.tiE0sRrTm2Ex9TAO" + } + ], "sort": 0, "ownership": { "default": 0, diff --git a/src/packs/items/armors/armor_Veritas_Opal_Armor_OvzgUTYy2RCN85vV.json b/src/packs/items/armors/armor_Veritas_Opal_Armor_OvzgUTYy2RCN85vV.json index 08a1b573..ca76336c 100644 --- a/src/packs/items/armors/armor_Veritas_Opal_Armor_OvzgUTYy2RCN85vV.json +++ b/src/packs/items/armors/armor_Veritas_Opal_Armor_OvzgUTYy2RCN85vV.json @@ -55,7 +55,46 @@ "artist": "" } }, - "effects": [], + "effects": [ + { + "type": "armor", + "name": "Armor Effect", + "img": "icons/equipment/chest/breastplate-helmet-metal.webp", + "system": { + "changes": [ + { + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": 6 + } + ] + }, + "_id": "fpPIhNaFxaz40Iaj", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!OvzgUTYy2RCN85vV.fpPIhNaFxaz40Iaj" + } + ], "sort": 0, "ownership": { "default": 0, From aef3d3cd045754bb6f5d6a1f84d0060ccfeeb923 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 10 Feb 2026 22:04:13 +0100 Subject: [PATCH 10/23] . --- module/data/item/armor.mjs | 14 ++------------ module/systemRegistration/migrations.mjs | 14 -------------- 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/module/data/item/armor.mjs b/module/data/item/armor.mjs index 3170627f..e0bee054 100644 --- a/module/data/item/armor.mjs +++ b/module/data/item/armor.mjs @@ -178,16 +178,6 @@ export default class DHArmor extends AttachableItem { /** @inheritDoc */ static migrateDocumentData(source) { if (source.system.baseScore !== undefined && !source.effects.some(x => x.type === 'armor')) { - // source.effects.push({ - // ...game.system.api.data.activeEffects.ArmorEffect.getDefaultObject(), - // changes: [{ - // type: CONFIG.DH.GENERAL.activeEffectModes.armor.id, - // phase: 'initial', - // priority: 20, - // value: 0, - // max: source.system.baseScore - // }], - // }); if (!source.flags) source.flags = {}; if (!source.flags.daggerheart) source.flags.daggerheart = {}; source.flags.daggerheart.baseScoreMigrationValue = source.system.baseScore; @@ -200,7 +190,7 @@ export default class DHArmor extends AttachableItem { */ _getTags() { const tags = [ - `${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${this.armorData.value}`, + `${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${this.armorData.max}`, `${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseThresholds.base')}: ${this.baseThresholds.major} / ${this.baseThresholds.severe}` ]; @@ -212,7 +202,7 @@ export default class DHArmor extends AttachableItem { * @returns {(string | { value: string, icons: string[] })[]} An array of localized strings and damage label objects. */ _getLabels() { - const labels = [`${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${this.armorData.value}`]; + const labels = [`${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${this.armorData.max}`]; return labels; } diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs index 858481c5..2b558ee2 100644 --- a/module/systemRegistration/migrations.mjs +++ b/module/systemRegistration/migrations.mjs @@ -300,20 +300,6 @@ export async function runMigrations() { progress.advance(); } - // const lockedPacks = []; - // const compendiumArmors = []; - // const compendiumCharacters = []; - // for (let pack of game.packs.filter(x => x.metadata.type === 'Item')) { - // if (pack.locked) { - // lockedPacks.push(pack.collection); - // await pack.configure({ locked: false }); - // } - - // const documents = await pack.getDocuments({ type: 'armor' }); - // compendiumArmors.push(...documents.filter(x => x instanceof game.system.api.documents.DHItem && x.type === 'armor')); - // compendiumCharacters.push(...documents.filter(x => x.type === 'character')); - // } - for (const armor of [...compendiumArmors, ...worldArmors]) { const hasArmorEffect = armor.effects.some(x => x.type === 'armor'); const migrationArmorScore = armor.flags.daggerheart?.baseScoreMigrationValue; From 81e61a7386134d24721c5098520151bfb9913ca3 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 10 Feb 2026 22:30:55 +0100 Subject: [PATCH 11/23] Fixed character sheet armor update --- .../applications/sheets/actors/character.mjs | 21 +++++++++++-------- templates/sheets/actors/character/sidebar.hbs | 8 +++---- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 4ecaeb06..9ba52000 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -629,12 +629,12 @@ export default class CharacterSheet extends DHBaseActorSheet { } async updateArmorMarks(event) { - const armor = this.document.system.armor; - if (!armor) return; + const inputValue = Number(event.currentTarget.value); + const { value, max } = this.document.system.armorScore; + const changeValue = Math.min(inputValue - value, max - value); - const maxMarks = this.document.system.armorScore; - const value = Math.min(Math.max(Number(event.currentTarget.value), 0), maxMarks); - await armor.update({ 'system.marks.value': value }); + event.currentTarget.value = inputValue < 0 ? 0 : value + changeValue; + this.document.system.updateArmorValue({ value: changeValue }); } /* -------------------------------------------- */ @@ -813,10 +813,13 @@ export default class CharacterSheet extends DHBaseActorSheet { * Toggles ArmorScore resource value. * @type {ApplicationClickAction} */ - static async #toggleArmor(_, button, element) { - const ArmorValue = Number.parseInt(button.dataset.value); - const newValue = this.document.system.armor.system.marks.value >= ArmorValue ? ArmorValue - 1 : ArmorValue; - await this.document.system.armor.update({ 'system.marks.value': newValue }); + static async #toggleArmor(_, button, _element) { + const { value, max } = this.document.system.armorScore; + const inputValue = Number.parseInt(button.dataset.value); + const newValue = value >= inputValue ? inputValue - 1 : inputValue; + const changeValue = Math.min(newValue - value, max - value); + + this.document.system.updateArmorValue({ value: changeValue }); } /** diff --git a/templates/sheets/actors/character/sidebar.hbs b/templates/sheets/actors/character/sidebar.hbs index 5720f253..523c3974 100644 --- a/templates/sheets/actors/character/sidebar.hbs +++ b/templates/sheets/actors/character/sidebar.hbs @@ -35,9 +35,9 @@ {{#if useResourcePips}} {{else}} From 653492c73592cad53c222c36cd7f92535645ff2a Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 10 Feb 2026 22:47:55 +0100 Subject: [PATCH 12/23] Updated itemconfig --- module/config/itemConfig.mjs | 48 ++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/module/config/itemConfig.mjs b/module/config/itemConfig.mjs index 7d80e597..06f3b004 100644 --- a/module/config/itemConfig.mjs +++ b/module/config/itemConfig.mjs @@ -491,17 +491,24 @@ export const weaponFeatures = { description: 'DAGGERHEART.CONFIG.WeaponFeature.barrier.effects.barrier.description', img: 'icons/skills/melee/shield-block-bash-blue.webp', changes: [ - { - key: 'system.armorScore', - mode: 2, - value: 'ITEM.@system.tier + 1' - }, { key: 'system.evasion', mode: 2, value: '-1' } ] + }, + { + type: 'armor', + 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: [ + { + type: 'armor', + max: 'ITEM.@system.tier + 1' + } + ] } ] }, @@ -791,11 +798,6 @@ export const weaponFeatures = { description: 'DAGGERHEART.CONFIG.WeaponFeature.doubleDuty.effects.doubleDuty.description', img: 'icons/skills/melee/sword-shield-stylized-white.webp', changes: [ - { - key: 'system.armorScore', - mode: 2, - value: '1' - }, { key: 'system.bonuses.damage.primaryWeapon.bonus', mode: 2, @@ -810,6 +812,26 @@ export const weaponFeatures = { type: 'withinRange' } } + }, + { + type: 'armor', + name: 'DAGGERHEART.CONFIG.WeaponFeature.doubleDuty.effects.doubleDuty.name', + description: 'DAGGERHEART.CONFIG.WeaponFeature.doubleDuty.effects.doubleDuty.description', + img: 'icons/skills/melee/sword-shield-stylized-white.webp', + changes: [ + { + type: 'armor', + max: 1 + } + ], + system: { + rangeDependence: { + enabled: true, + range: 'melee', + target: 'hostile', + type: 'withinRange' + } + } } ] }, @@ -1188,14 +1210,14 @@ export const weaponFeatures = { description: 'DAGGERHEART.CONFIG.WeaponFeature.protective.description', effects: [ { + type: 'armor', 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: 'system.armorScore', - mode: 2, - value: 'ITEM.@system.tier' + type: 'armor', + max: 'ITEM.@system.tier' } ] } From c2c2050009f19ef93f608dcea4016ec76a7e842c Mon Sep 17 00:00:00 2001 From: WBHarry Date: Wed, 11 Feb 2026 00:27:14 +0100 Subject: [PATCH 13/23] Actions now use createDialog for effects --- module/applications/sheets-configs/action-config.mjs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/module/applications/sheets-configs/action-config.mjs b/module/applications/sheets-configs/action-config.mjs index 0dbc377a..e75e16ab 100644 --- a/module/applications/sheets-configs/action-config.mjs +++ b/module/applications/sheets-configs/action-config.mjs @@ -24,9 +24,12 @@ export default class DHActionConfig extends DHActionBaseConfig { const effectData = this._addEffectData.bind(this)(); const data = this.action.toObject(); - const [created] = await this.action.item.createEmbeddedDocuments('ActiveEffect', [effectData], { + const created = await game.system.api.documents.DhActiveEffect.createDialog(effectData, { + parent: this.action.item, render: false }); + if (!created) return; + data.effects.push({ _id: created._id }); this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); this.action.item.effects.get(created._id).sheet.render(true); From 09ef7fa96fe5fa9b02d1140427faf0a4dfd18802 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Wed, 11 Feb 2026 01:43:30 +0100 Subject: [PATCH 14/23] . --- lang/en.json | 3 ++- module/documents/actor.mjs | 4 ++++ module/documents/item.mjs | 2 +- module/systemRegistration/migrations.mjs | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lang/en.json b/lang/en.json index b6684e59..14218311 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2980,7 +2980,8 @@ "domainTouchRequirement": "This domain card requires {nr} {domain} cards in the loadout to be used", "knowTheTide": "Know The Tide gained a token", "cannotAlterArmorEffectChanges": "You cannot alter the changes length of an armor effect", - "cannotAlterArmorEffectType": "You cannot alter the type of armor effect changes" + "cannotAlterArmorEffectType": "You cannot alter the type of armor effect changes", + "cannotAlterArmorEffectKey": "You cannot alter they key of armor effect changes" }, "Progress": { "migrationLabel": "Performing system migration. Please wait and do not close Foundry." diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index da399df5..64e877c9 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -993,4 +993,8 @@ export default class DhpActor extends Actor { } } } + + applyActiveEffects(phase) { + super.applyActiveEffects(phase); + } } diff --git a/module/documents/item.mjs b/module/documents/item.mjs index ce87db4e..56048a81 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -233,7 +233,7 @@ export default class DHItem extends foundry.documents.Item { /** @inheritDoc */ static migrateData(source) { - const documentClass = game.system.api.data.items[`DH${source.type.capitalize()}`]; + const documentClass = game.system.api.data.items[`DH${source.type?.capitalize()}`]; if (documentClass?.migrateDocumentData) { documentClass.migrateDocumentData(source); } diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs index 2b558ee2..6bfb32ee 100644 --- a/module/systemRegistration/migrations.mjs +++ b/module/systemRegistration/migrations.mjs @@ -313,7 +313,7 @@ export async function runMigrations() { phase: 'initial', priority: 20, value: 0, - max: migrationArmorScore + max: migrationArmorScore.toString() } ] } From b0f8442aaaf695f897af3ad4e688ed161fb87dd6 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Wed, 11 Feb 2026 09:45:34 +0100 Subject: [PATCH 15/23] . --- .../sheets/api/application-mixin.mjs | 2 +- module/data/activeEffect/armorEffect.mjs | 73 ++++++++++++------- 2 files changed, 46 insertions(+), 29 deletions(-) diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 4de3d2be..aa04baed 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -511,7 +511,7 @@ export default function DHApplicationMixin(Base) { icon: 'fa-solid fa-trash', condition: target => { const doc = getDocFromElementSync(target); - return doc && doc.type !== 'beastform'; + return !doc || doc.type !== 'beastform'; }, callback: async (target, event) => { const doc = await getDocFromElement(target); diff --git a/module/data/activeEffect/armorEffect.mjs b/module/data/activeEffect/armorEffect.mjs index c277c904..b82ef9e3 100644 --- a/module/data/activeEffect/armorEffect.mjs +++ b/module/data/activeEffect/armorEffect.mjs @@ -1,4 +1,4 @@ -import { getScrollTextData } from '../../helpers/utils.mjs'; +import { getScrollTextData, itemAbleRollParse } from '../../helpers/utils.mjs'; /** * ArmorEffects are ActiveEffects that have a static changes field of length 1. It includes current and maximum armor. @@ -12,6 +12,11 @@ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel ...super.defineSchema(), changes: new fields.ArrayField( new fields.SchemaField({ + key: new fields.StringField({ + required: true, + nullable: false, + initial: 'system.armorScore' + }), type: new fields.StringField({ required: true, blank: false, @@ -27,11 +32,10 @@ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel min: 0, label: 'DAGGERHEART.GENERAL.value' }), - max: new fields.NumberField({ + max: new fields.StringField({ required: true, - integer: true, - initial: 1, - min: 1, + nullable: false, + initial: '1', label: 'DAGGERHEART.GENERAL.max' }) }), @@ -99,7 +103,13 @@ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel get armorChange() { if (this.changes.length !== 1) throw new Error('Unexpected error. An armor effect should have a changes field of length 1.'); - return this.changes[0]; + + const actor = this.parent.actor?.type === 'character' ? this.parent.actor : null; + const changeData = this.changes[0]; + return { + ...changeData, + max: actor ? itemAbleRollParse(changeData.max, actor, this.parent.parent) : changeData.max + }; } get armorData() { @@ -121,21 +131,22 @@ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel static orderEffectsForAutoChange(armorEffects, increasing) { const getEffectWeight = effect => { switch (effect.parent.type) { - case 'loot': - case 'consumable': - return 2; case 'class': case 'subclass': case 'ancestry': case 'community': case 'feature': case 'domainCard': - return 3; - case 'weapon': + return 2; case 'armor': + return 3; + case 'loot': + case 'consumable': return 4; - case 'character': + case 'weapon': return 5; + case 'character': + return 6; default: return 1; } @@ -148,11 +159,6 @@ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel /* Overrides */ - prepareBaseData() { - const armorChange = this.armorChange; - armorChange.key = 'system.armorScore'; - } - static getDefaultObject() { return { type: 'armor', @@ -174,18 +180,29 @@ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel return false; } - if ( - changes.system.changes.length === 1 && - changes.system.changes[0].type !== CONFIG.DH.GENERAL.activeEffectModes.armor.id - ) { - ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.cannotAlterArmorEffectType')); - return false; - } + if (changes.system.changes.length === 1) { + if (changes.system.changes[0].type !== CONFIG.DH.GENERAL.activeEffectModes.armor.id) { + ui.notifications.error( + game.i18n.localize('DAGGERHEART.UI.Notifications.cannotAlterArmorEffectType') + ); + return false; + } - if (changes.system.changes[0].value !== this.armorChange.value && this.parent.actor?.type === 'character') { - const increased = changes.system.changes[0].value > this.armorChange.value; - const value = -1 * (this.armorChange.value - changes.system.changes[0].value); - options.scrollingTextData = [getScrollTextData(increased, value, 'armor')]; + if (changes.system.changes[0].key !== 'system.armorScore') { + ui.notifications.error( + game.i18n.localize('DAGGERHEART.UI.Notifications.cannotAlterArmorEffectKey') + ); + return false; + } + + if ( + changes.system.changes[0].value !== this.armorChange.value && + this.parent.actor?.type === 'character' + ) { + const increased = changes.system.changes[0].value > this.armorChange.value; + const value = -1 * (this.armorChange.value - changes.system.changes[0].value); + options.scrollingTextData = [getScrollTextData(increased, value, 'armor')]; + } } } } From 49e834062b1cffd8180cd91da7a6f69598dfb7c8 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Wed, 11 Feb 2026 10:22:24 +0100 Subject: [PATCH 16/23] Fixed ArmorEffect max being a string --- module/applications/dialogs/damageReductionDialog.mjs | 2 +- module/data/activeEffect/armorEffect.mjs | 4 +++- .../armor_Advanced_Chainmail_Armor_LzLOJ9EVaHWAjoq9.json | 7 ++++--- .../armor_Advanced_Full_Plate_Armor_crIbCb9NZ4K0VpoU.json | 7 ++++--- .../armor_Advanced_Gambeson_Armor_epkAmlZVk7HOfUUT.json | 7 ++++--- .../armor_Advanced_Leather_Armor_itSOp2GCyem0f7oM.json | 7 ++++--- .../items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json | 7 ++++--- .../armors/armor_Bellamoi_Fine_Armor_WuoVwZA53XRAIt6d.json | 7 ++++--- .../armors/armor_Bladefare_Armor_mNN6pvcsS10ChrWF.json | 7 ++++--- .../armors/armor_Chainmail_Armor_haULhuEg37zUUvhb.json | 7 ++++--- .../armors/armor_Channeling_Armor_vMJxEWz1srfwMsoj.json | 7 ++++--- .../armors/armor_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json | 7 ++++--- .../armors/armor_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json | 7 ++++--- .../armor_Elundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json | 7 ++++--- .../armors/armor_Emberwoven_Armor_bcQUh4QG3qFX0Vx6.json | 7 ++++--- .../armor_Full_Fortified_Armor_7emTSt6nhZuTlvt5.json | 7 ++++--- .../armors/armor_Full_Plate_Armor_UdUJNa31WxFW2noa.json | 7 ++++--- .../armors/armor_Gambeson_Armor_yJFp1bfpecDcStVK.json | 7 ++++--- .../armors/armor_Harrowbone_Armor_dvyQeUVRLc9y6rnt.json | 7 ++++--- .../armor_Improved_Chainmail_Armor_K5WkjS0NGqHYmhU3.json | 7 ++++--- .../armor_Improved_Full_Plate_Armor_9f7RozpPTqrzJS1m.json | 7 ++++--- .../armor_Improved_Gambeson_Armor_jphnMZjnS2FkOH3s.json | 7 ++++--- .../armor_Improved_Leather_Armor_t91M61pSCMKStTNt.json | 7 ++++--- .../armor_Irontree_Breastplate_Armor_tzZntboNtHL5C6VM.json | 7 ++++--- .../items/armors/armor_Leather_Armor_nibfdNtp2PtxvbVz.json | 7 ++++--- .../armor_Legendary_Chainmail_Armor_EsIN5OLKe9ZYFNXZ.json | 7 ++++--- .../armor_Legendary_Full_Plate_Armor_SXWjUR2aUR6bYvdl.json | 7 ++++--- .../armor_Legendary_Gambeson_Armor_c6tMXz4rPf9ioQrf.json | 7 ++++--- .../armor_Legendary_Leather_Armor_Tptgl5WOj76TyFn7.json | 7 ++++--- .../armors/armor_Monett_s_Cloak_AQzU2RsqS5V5bd1v.json | 7 ++++--- .../armors/armor_Rosewild_Armor_tN8kAeBvNKM3EBFo.json | 7 ++++--- .../armor_Runes_of_Fortification_P4qAEDJUoNLgVRsA.json | 7 ++++--- .../armor_Runetan_Floating_Armor_tHlBUDQC24YMZqd6.json | 7 ++++--- .../armors/armor_Savior_Chainmail_8X16lJQ3xltTwynm.json | 7 ++++--- .../armors/armor_Spiked_Plate_Armor_QjwsIhXKqnlvRBMv.json | 7 ++++--- .../armors/armor_Tyris_Soft_Armor_PSW3BxCGmtLeWOxM.json | 7 ++++--- .../armors/armor_Veritas_Opal_Armor_OvzgUTYy2RCN85vV.json | 7 ++++--- 37 files changed, 144 insertions(+), 107 deletions(-) diff --git a/module/applications/dialogs/damageReductionDialog.mjs b/module/applications/dialogs/damageReductionDialog.mjs index 494bad0f..d4a2b4d3 100644 --- a/module/applications/dialogs/damageReductionDialog.mjs +++ b/module/applications/dialogs/damageReductionDialog.mjs @@ -173,7 +173,7 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap ? Object.values(this.availableStressReductions).filter(red => red.selected) : []; const currentMarks = - this.actor.system.armor.system.marks.value + selectedArmorMarks.length + selectedStressMarks.length; + this.actor.system.armorScore.value + selectedArmorMarks.length + selectedStressMarks.length; const armorMarkReduction = selectedArmorMarks.length * this.actor.system.rules.damageReduction.increasePerArmorMark; diff --git a/module/data/activeEffect/armorEffect.mjs b/module/data/activeEffect/armorEffect.mjs index b82ef9e3..93a7ed3d 100644 --- a/module/data/activeEffect/armorEffect.mjs +++ b/module/data/activeEffect/armorEffect.mjs @@ -42,11 +42,12 @@ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel { initial: [ { + key: 'system.armorScore', type: CONFIG.DH.GENERAL.activeEffectModes.armor.id, phase: 'initial', priority: 20, value: 0, - max: 1 + max: '1' } ] } @@ -161,6 +162,7 @@ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel static getDefaultObject() { return { + key: 'system.armorScore', type: 'armor', name: game.i18n.localize('DAGGERHEART.EFFECTS.Armor.newArmorEffect'), img: 'icons/equipment/chest/breastplate-helmet-metal.webp' 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 e7c726af..7287d027 100644 --- a/src/packs/items/armors/armor_Advanced_Chainmail_Armor_LzLOJ9EVaHWAjoq9.json +++ b/src/packs/items/armors/armor_Advanced_Chainmail_Armor_LzLOJ9EVaHWAjoq9.json @@ -75,11 +75,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 6 + "max": "6", + "key": "system.armorScore" } ] }, - "_id": "I0649iXfgoME38fU", + "_id": "xU3zAv0sBiOGAE4i", "disabled": false, "start": null, "duration": { @@ -100,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!LzLOJ9EVaHWAjoq9.I0649iXfgoME38fU" + "_key": "!items.effects!LzLOJ9EVaHWAjoq9.xU3zAv0sBiOGAE4i" } ], "sort": 0, 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 2a06068f..6522c67a 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 @@ -80,11 +80,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 6 + "max": "6", + "key": "system.armorScore" } ] }, - "_id": "kRaWET7LV25rD4jy", + "_id": "TMxnzDzCmVibJWQ0", "disabled": false, "start": null, "duration": { @@ -105,7 +106,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!crIbCb9NZ4K0VpoU.kRaWET7LV25rD4jy" + "_key": "!items.effects!crIbCb9NZ4K0VpoU.TMxnzDzCmVibJWQ0" } ], "sort": 0, 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 84c3b35a..d9ee9649 100644 --- a/src/packs/items/armors/armor_Advanced_Gambeson_Armor_epkAmlZVk7HOfUUT.json +++ b/src/packs/items/armors/armor_Advanced_Gambeson_Armor_epkAmlZVk7HOfUUT.json @@ -75,11 +75,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 5 + "max": "5", + "key": "system.armorScore" } ] }, - "_id": "lJBLFQHDjmgLsLL8", + "_id": "HxZEKljAth8b5Wcv", "disabled": false, "start": null, "duration": { @@ -100,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!epkAmlZVk7HOfUUT.lJBLFQHDjmgLsLL8" + "_key": "!items.effects!epkAmlZVk7HOfUUT.HxZEKljAth8b5Wcv" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Advanced_Leather_Armor_itSOp2GCyem0f7oM.json b/src/packs/items/armors/armor_Advanced_Leather_Armor_itSOp2GCyem0f7oM.json index c7d039df..09a45a8a 100644 --- a/src/packs/items/armors/armor_Advanced_Leather_Armor_itSOp2GCyem0f7oM.json +++ b/src/packs/items/armors/armor_Advanced_Leather_Armor_itSOp2GCyem0f7oM.json @@ -37,11 +37,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 5 + "max": "5", + "key": "system.armorScore" } ] }, - "_id": "1vzHmkVScl1KyHxy", + "_id": "jSGmBv0I5FhxmTen", "disabled": false, "start": null, "duration": { @@ -62,7 +63,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!itSOp2GCyem0f7oM.1vzHmkVScl1KyHxy" + "_key": "!items.effects!itSOp2GCyem0f7oM.jSGmBv0I5FhxmTen" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json b/src/packs/items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json index 3e882f9f..e555854d 100644 --- a/src/packs/items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json +++ b/src/packs/items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json @@ -75,11 +75,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 3 + "max": "3", + "key": "system.armorScore" } ] }, - "_id": "B5hlwTWBUSJYZurq", + "_id": "rm3t1XHFXaoZvw5v", "disabled": false, "start": null, "duration": { @@ -100,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!ITAjcigTcUw5pMCN.B5hlwTWBUSJYZurq" + "_key": "!items.effects!ITAjcigTcUw5pMCN.rm3t1XHFXaoZvw5v" } ], "sort": 0, 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 dafbe5b1..fc7c62eb 100644 --- a/src/packs/items/armors/armor_Bellamoi_Fine_Armor_WuoVwZA53XRAIt6d.json +++ b/src/packs/items/armors/armor_Bellamoi_Fine_Armor_WuoVwZA53XRAIt6d.json @@ -75,11 +75,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 5 + "max": "5", + "key": "system.armorScore" } ] }, - "_id": "lDRMjmZXRJDbhK03", + "_id": "34OQBJZZV3d5AN7U", "disabled": false, "start": null, "duration": { @@ -100,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!WuoVwZA53XRAIt6d.lDRMjmZXRJDbhK03" + "_key": "!items.effects!WuoVwZA53XRAIt6d.34OQBJZZV3d5AN7U" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Bladefare_Armor_mNN6pvcsS10ChrWF.json b/src/packs/items/armors/armor_Bladefare_Armor_mNN6pvcsS10ChrWF.json index 8a6f1132..a82f0ba4 100644 --- a/src/packs/items/armors/armor_Bladefare_Armor_mNN6pvcsS10ChrWF.json +++ b/src/packs/items/armors/armor_Bladefare_Armor_mNN6pvcsS10ChrWF.json @@ -75,11 +75,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 6 + "max": "6", + "key": "system.armorScore" } ] }, - "_id": "qYkj3jKDdFzflfh4", + "_id": "6AGrG6Y1wUSY3mg5", "disabled": false, "start": null, "duration": { @@ -100,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!mNN6pvcsS10ChrWF.qYkj3jKDdFzflfh4" + "_key": "!items.effects!mNN6pvcsS10ChrWF.6AGrG6Y1wUSY3mg5" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Chainmail_Armor_haULhuEg37zUUvhb.json b/src/packs/items/armors/armor_Chainmail_Armor_haULhuEg37zUUvhb.json index 7c161931..9fc6b7db 100644 --- a/src/packs/items/armors/armor_Chainmail_Armor_haULhuEg37zUUvhb.json +++ b/src/packs/items/armors/armor_Chainmail_Armor_haULhuEg37zUUvhb.json @@ -75,11 +75,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 4 + "max": "4", + "key": "system.armorScore" } ] }, - "_id": "d6ICO5qZArh0xF1y", + "_id": "RXsc2d47cauTWTf0", "disabled": false, "start": null, "duration": { @@ -100,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!haULhuEg37zUUvhb.d6ICO5qZArh0xF1y" + "_key": "!items.effects!haULhuEg37zUUvhb.RXsc2d47cauTWTf0" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Channeling_Armor_vMJxEWz1srfwMsoj.json b/src/packs/items/armors/armor_Channeling_Armor_vMJxEWz1srfwMsoj.json index f7306c06..d37d45ff 100644 --- a/src/packs/items/armors/armor_Channeling_Armor_vMJxEWz1srfwMsoj.json +++ b/src/packs/items/armors/armor_Channeling_Armor_vMJxEWz1srfwMsoj.json @@ -75,11 +75,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 5 + "max": "5", + "key": "system.armorScore" } ] }, - "_id": "2q3uXc7EbTNSIjs8", + "_id": "dKK4sbP3DZQYdmTn", "disabled": false, "start": null, "duration": { @@ -100,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!vMJxEWz1srfwMsoj.2q3uXc7EbTNSIjs8" + "_key": "!items.effects!vMJxEWz1srfwMsoj.dKK4sbP3DZQYdmTn" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json b/src/packs/items/armors/armor_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json index 41b34d96..136b89d6 100644 --- a/src/packs/items/armors/armor_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json +++ b/src/packs/items/armors/armor_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json @@ -74,11 +74,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 5 + "max": "5", + "key": "system.armorScore" } ] }, - "_id": "a8frrkkR4i2TBFdF", + "_id": "kNq8nLq1ljLZZGDg", "disabled": false, "start": null, "duration": { @@ -99,7 +100,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!mdQ69eFHyAQUDmE7.a8frrkkR4i2TBFdF" + "_key": "!items.effects!mdQ69eFHyAQUDmE7.kNq8nLq1ljLZZGDg" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json b/src/packs/items/armors/armor_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json index 1cb28c6f..9b858104 100644 --- a/src/packs/items/armors/armor_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json +++ b/src/packs/items/armors/armor_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json @@ -100,11 +100,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 7 + "max": "7", + "key": "system.armorScore" } ] }, - "_id": "a1x2R28RtXE2jqu5", + "_id": "sBTZAS0aYcE15RwZ", "disabled": false, "start": null, "duration": { @@ -125,7 +126,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!hAY6UgdGT7dj22Pr.a1x2R28RtXE2jqu5" + "_key": "!items.effects!hAY6UgdGT7dj22Pr.sBTZAS0aYcE15RwZ" } ], "sort": 0, 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 c1b233a9..d84108d8 100644 --- a/src/packs/items/armors/armor_Elundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json +++ b/src/packs/items/armors/armor_Elundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json @@ -76,11 +76,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 4 + "max": "4", + "key": "system.armorScore" } ] }, - "_id": "4yImObrCOaWLGxgH", + "_id": "uFvhPlk3FVGfQST4", "disabled": false, "start": null, "duration": { @@ -101,7 +102,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!Q6LxmtFetDDkoZVZ.4yImObrCOaWLGxgH" + "_key": "!items.effects!Q6LxmtFetDDkoZVZ.uFvhPlk3FVGfQST4" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Emberwoven_Armor_bcQUh4QG3qFX0Vx6.json b/src/packs/items/armors/armor_Emberwoven_Armor_bcQUh4QG3qFX0Vx6.json index 5ffdcc2e..92652f66 100644 --- a/src/packs/items/armors/armor_Emberwoven_Armor_bcQUh4QG3qFX0Vx6.json +++ b/src/packs/items/armors/armor_Emberwoven_Armor_bcQUh4QG3qFX0Vx6.json @@ -98,11 +98,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 6 + "max": "6", + "key": "system.armorScore" } ] }, - "_id": "8VtWedDMEX0tbqTn", + "_id": "Dg8Gx6G3nwAH9wt2", "disabled": false, "start": null, "duration": { @@ -123,7 +124,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!bcQUh4QG3qFX0Vx6.8VtWedDMEX0tbqTn" + "_key": "!items.effects!bcQUh4QG3qFX0Vx6.Dg8Gx6G3nwAH9wt2" } ], "sort": 0, 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 25e47bc3..a4e21b75 100644 --- a/src/packs/items/armors/armor_Full_Fortified_Armor_7emTSt6nhZuTlvt5.json +++ b/src/packs/items/armors/armor_Full_Fortified_Armor_7emTSt6nhZuTlvt5.json @@ -75,11 +75,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 4 + "max": "4", + "key": "system.armorScore" } ] }, - "_id": "TRI0rfHs8RTSCmuY", + "_id": "XPbNhspFyOj8RIQJ", "disabled": false, "start": null, "duration": { @@ -100,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!7emTSt6nhZuTlvt5.TRI0rfHs8RTSCmuY" + "_key": "!items.effects!7emTSt6nhZuTlvt5.XPbNhspFyOj8RIQJ" } ], "sort": 0, 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 3bdb1c56..0400f59e 100644 --- a/src/packs/items/armors/armor_Full_Plate_Armor_UdUJNa31WxFW2noa.json +++ b/src/packs/items/armors/armor_Full_Plate_Armor_UdUJNa31WxFW2noa.json @@ -76,15 +76,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": 4 + "max": "4" } ] }, - "_id": "VpaGM3KSKQFG5wC8", + "_id": "zji5nzTC1y8BUWHn", "disabled": false, "start": null, "duration": { @@ -105,7 +106,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!UdUJNa31WxFW2noa.VpaGM3KSKQFG5wC8" + "_key": "!items.effects!UdUJNa31WxFW2noa.zji5nzTC1y8BUWHn" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Gambeson_Armor_yJFp1bfpecDcStVK.json b/src/packs/items/armors/armor_Gambeson_Armor_yJFp1bfpecDcStVK.json index 6b30d4bc..eb8285cb 100644 --- a/src/packs/items/armors/armor_Gambeson_Armor_yJFp1bfpecDcStVK.json +++ b/src/packs/items/armors/armor_Gambeson_Armor_yJFp1bfpecDcStVK.json @@ -75,11 +75,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 3 + "max": "3", + "key": "system.armorScore" } ] }, - "_id": "qNXDdLhZkPe6Wnxa", + "_id": "72LkcLIihluGgx48", "disabled": false, "start": null, "duration": { @@ -100,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!yJFp1bfpecDcStVK.qNXDdLhZkPe6Wnxa" + "_key": "!items.effects!yJFp1bfpecDcStVK.72LkcLIihluGgx48" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Harrowbone_Armor_dvyQeUVRLc9y6rnt.json b/src/packs/items/armors/armor_Harrowbone_Armor_dvyQeUVRLc9y6rnt.json index 9fd7971d..43057efa 100644 --- a/src/packs/items/armors/armor_Harrowbone_Armor_dvyQeUVRLc9y6rnt.json +++ b/src/packs/items/armors/armor_Harrowbone_Armor_dvyQeUVRLc9y6rnt.json @@ -91,11 +91,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 4 + "max": "4", + "key": "system.armorScore" } ] }, - "_id": "E3Zwl9T3EuK7hOOB", + "_id": "GYRwYD3CHW9q4N29", "disabled": false, "start": null, "duration": { @@ -116,7 +117,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!dvyQeUVRLc9y6rnt.E3Zwl9T3EuK7hOOB" + "_key": "!items.effects!dvyQeUVRLc9y6rnt.GYRwYD3CHW9q4N29" } ], "sort": 0, 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 18b4f699..69b1bbae 100644 --- a/src/packs/items/armors/armor_Improved_Chainmail_Armor_K5WkjS0NGqHYmhU3.json +++ b/src/packs/items/armors/armor_Improved_Chainmail_Armor_K5WkjS0NGqHYmhU3.json @@ -75,11 +75,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 5 + "max": "5", + "key": "system.armorScore" } ] }, - "_id": "QXvJ3gL1kNcOLaqC", + "_id": "2KD6EdRL2L2gQkMR", "disabled": false, "start": null, "duration": { @@ -100,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!K5WkjS0NGqHYmhU3.QXvJ3gL1kNcOLaqC" + "_key": "!items.effects!K5WkjS0NGqHYmhU3.2KD6EdRL2L2gQkMR" } ], "sort": 0, 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 bd163402..eb62a85b 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 @@ -80,11 +80,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 5 + "max": "5", + "key": "system.armorScore" } ] }, - "_id": "7ahyQs2byVwsUVAF", + "_id": "ehijWY3PGw1OaQr0", "disabled": false, "start": null, "duration": { @@ -105,7 +106,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!9f7RozpPTqrzJS1m.7ahyQs2byVwsUVAF" + "_key": "!items.effects!9f7RozpPTqrzJS1m.ehijWY3PGw1OaQr0" } ], "sort": 0, 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 aca1d66d..2e1b4f93 100644 --- a/src/packs/items/armors/armor_Improved_Gambeson_Armor_jphnMZjnS2FkOH3s.json +++ b/src/packs/items/armors/armor_Improved_Gambeson_Armor_jphnMZjnS2FkOH3s.json @@ -75,11 +75,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 4 + "max": "4", + "key": "system.armorScore" } ] }, - "_id": "uF8AksqGBBfKrrVM", + "_id": "d6QRNZ1X4wdoy82q", "disabled": false, "start": null, "duration": { @@ -100,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!jphnMZjnS2FkOH3s.uF8AksqGBBfKrrVM" + "_key": "!items.effects!jphnMZjnS2FkOH3s.d6QRNZ1X4wdoy82q" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Improved_Leather_Armor_t91M61pSCMKStTNt.json b/src/packs/items/armors/armor_Improved_Leather_Armor_t91M61pSCMKStTNt.json index 5413c999..3dce3151 100644 --- a/src/packs/items/armors/armor_Improved_Leather_Armor_t91M61pSCMKStTNt.json +++ b/src/packs/items/armors/armor_Improved_Leather_Armor_t91M61pSCMKStTNt.json @@ -37,11 +37,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 4 + "max": "4", + "key": "system.armorScore" } ] }, - "_id": "OqL5x4lkQvjbzSGx", + "_id": "lQNHCtW6HnIj0b7F", "disabled": false, "start": null, "duration": { @@ -62,7 +63,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!t91M61pSCMKStTNt.OqL5x4lkQvjbzSGx" + "_key": "!items.effects!t91M61pSCMKStTNt.lQNHCtW6HnIj0b7F" } ], "sort": 0, 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 feba37e9..9be75429 100644 --- a/src/packs/items/armors/armor_Irontree_Breastplate_Armor_tzZntboNtHL5C6VM.json +++ b/src/packs/items/armors/armor_Irontree_Breastplate_Armor_tzZntboNtHL5C6VM.json @@ -87,11 +87,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 4 + "max": "4", + "key": "system.armorScore" } ] }, - "_id": "wKp8iBd3KfaMlzJh", + "_id": "jnjdtSTQF1zTSkEr", "disabled": false, "start": null, "duration": { @@ -112,7 +113,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!tzZntboNtHL5C6VM.wKp8iBd3KfaMlzJh" + "_key": "!items.effects!tzZntboNtHL5C6VM.jnjdtSTQF1zTSkEr" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Leather_Armor_nibfdNtp2PtxvbVz.json b/src/packs/items/armors/armor_Leather_Armor_nibfdNtp2PtxvbVz.json index 2f1548a7..5f79c177 100644 --- a/src/packs/items/armors/armor_Leather_Armor_nibfdNtp2PtxvbVz.json +++ b/src/packs/items/armors/armor_Leather_Armor_nibfdNtp2PtxvbVz.json @@ -37,11 +37,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 3 + "max": "3", + "key": "system.armorScore" } ] }, - "_id": "TbWKQ0R6AfNNeqNd", + "_id": "bkEJ55HhIYFnX1Tz", "disabled": false, "start": null, "duration": { @@ -62,7 +63,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!nibfdNtp2PtxvbVz.TbWKQ0R6AfNNeqNd" + "_key": "!items.effects!nibfdNtp2PtxvbVz.bkEJ55HhIYFnX1Tz" } ], "sort": 0, 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 0b540fd5..993b35cf 100644 --- a/src/packs/items/armors/armor_Legendary_Chainmail_Armor_EsIN5OLKe9ZYFNXZ.json +++ b/src/packs/items/armors/armor_Legendary_Chainmail_Armor_EsIN5OLKe9ZYFNXZ.json @@ -75,11 +75,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 7 + "max": "7", + "key": "system.armorScore" } ] }, - "_id": "QAkiVlwfclxQ6JSD", + "_id": "PZsaURELHOaRJK28", "disabled": false, "start": null, "duration": { @@ -100,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!EsIN5OLKe9ZYFNXZ.QAkiVlwfclxQ6JSD" + "_key": "!items.effects!EsIN5OLKe9ZYFNXZ.PZsaURELHOaRJK28" } ], "sort": 0, 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 4e3ae4ed..ba4eb949 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 @@ -80,11 +80,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 7 + "max": "7", + "key": "system.armorScore" } ] }, - "_id": "mMYVCcmoBJxjU0er", + "_id": "IMPH2qFG7zXaxefg", "disabled": false, "start": null, "duration": { @@ -105,7 +106,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!SXWjUR2aUR6bYvdl.mMYVCcmoBJxjU0er" + "_key": "!items.effects!SXWjUR2aUR6bYvdl.IMPH2qFG7zXaxefg" } ], "sort": 0, 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 b6899fbd..b0af0790 100644 --- a/src/packs/items/armors/armor_Legendary_Gambeson_Armor_c6tMXz4rPf9ioQrf.json +++ b/src/packs/items/armors/armor_Legendary_Gambeson_Armor_c6tMXz4rPf9ioQrf.json @@ -75,11 +75,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 6 + "max": "6", + "key": "system.armorScore" } ] }, - "_id": "vgnBNFSXks1BcFQ5", + "_id": "TtMaMntKKpcTU054", "disabled": false, "start": null, "duration": { @@ -100,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!c6tMXz4rPf9ioQrf.vgnBNFSXks1BcFQ5" + "_key": "!items.effects!c6tMXz4rPf9ioQrf.TtMaMntKKpcTU054" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Legendary_Leather_Armor_Tptgl5WOj76TyFn7.json b/src/packs/items/armors/armor_Legendary_Leather_Armor_Tptgl5WOj76TyFn7.json index a9d680af..6460fc02 100644 --- a/src/packs/items/armors/armor_Legendary_Leather_Armor_Tptgl5WOj76TyFn7.json +++ b/src/packs/items/armors/armor_Legendary_Leather_Armor_Tptgl5WOj76TyFn7.json @@ -37,11 +37,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 6 + "max": "6", + "key": "system.armorScore" } ] }, - "_id": "m6HRZpgaMnuw1dE7", + "_id": "UtbfSKO8hmJanog5", "disabled": false, "start": null, "duration": { @@ -62,7 +63,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!Tptgl5WOj76TyFn7.m6HRZpgaMnuw1dE7" + "_key": "!items.effects!Tptgl5WOj76TyFn7.UtbfSKO8hmJanog5" } ], "sort": 0, 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 ce7d94c6..91ab3f80 100644 --- a/src/packs/items/armors/armor_Monett_s_Cloak_AQzU2RsqS5V5bd1v.json +++ b/src/packs/items/armors/armor_Monett_s_Cloak_AQzU2RsqS5V5bd1v.json @@ -75,11 +75,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 6 + "max": "6", + "key": "system.armorScore" } ] }, - "_id": "aRwIF0ss6R7AYNZf", + "_id": "kskfQTQTgCgmQR6b", "disabled": false, "start": null, "duration": { @@ -100,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!AQzU2RsqS5V5bd1v.aRwIF0ss6R7AYNZf" + "_key": "!items.effects!AQzU2RsqS5V5bd1v.kskfQTQTgCgmQR6b" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Rosewild_Armor_tN8kAeBvNKM3EBFo.json b/src/packs/items/armors/armor_Rosewild_Armor_tN8kAeBvNKM3EBFo.json index ee950d4f..a7c2ab93 100644 --- a/src/packs/items/armors/armor_Rosewild_Armor_tN8kAeBvNKM3EBFo.json +++ b/src/packs/items/armors/armor_Rosewild_Armor_tN8kAeBvNKM3EBFo.json @@ -67,11 +67,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 5 + "max": "5", + "key": "system.armorScore" } ] }, - "_id": "YvXWUYVaXDHugsEr", + "_id": "eT5j1FNPPQOdLO2Q", "disabled": false, "start": null, "duration": { @@ -92,7 +93,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!tN8kAeBvNKM3EBFo.YvXWUYVaXDHugsEr" + "_key": "!items.effects!tN8kAeBvNKM3EBFo.eT5j1FNPPQOdLO2Q" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Runes_of_Fortification_P4qAEDJUoNLgVRsA.json b/src/packs/items/armors/armor_Runes_of_Fortification_P4qAEDJUoNLgVRsA.json index b0501649..c1beaad7 100644 --- a/src/packs/items/armors/armor_Runes_of_Fortification_P4qAEDJUoNLgVRsA.json +++ b/src/packs/items/armors/armor_Runes_of_Fortification_P4qAEDJUoNLgVRsA.json @@ -74,11 +74,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 6 + "max": "6", + "key": "system.armorScore" } ] }, - "_id": "vkJeIaXB25W3MAt1", + "_id": "UmpUTOMR2UBmrAu6", "disabled": false, "start": null, "duration": { @@ -99,7 +100,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!P4qAEDJUoNLgVRsA.vkJeIaXB25W3MAt1" + "_key": "!items.effects!P4qAEDJUoNLgVRsA.UmpUTOMR2UBmrAu6" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Runetan_Floating_Armor_tHlBUDQC24YMZqd6.json b/src/packs/items/armors/armor_Runetan_Floating_Armor_tHlBUDQC24YMZqd6.json index 690045f9..a26eb694 100644 --- a/src/packs/items/armors/armor_Runetan_Floating_Armor_tHlBUDQC24YMZqd6.json +++ b/src/packs/items/armors/armor_Runetan_Floating_Armor_tHlBUDQC24YMZqd6.json @@ -74,11 +74,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 4 + "max": "4", + "key": "system.armorScore" } ] }, - "_id": "2dj1LoZcV6tCKpKj", + "_id": "s39jgXMmi4fDHuaE", "disabled": false, "start": null, "duration": { @@ -99,7 +100,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!tHlBUDQC24YMZqd6.2dj1LoZcV6tCKpKj" + "_key": "!items.effects!tHlBUDQC24YMZqd6.s39jgXMmi4fDHuaE" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Savior_Chainmail_8X16lJQ3xltTwynm.json b/src/packs/items/armors/armor_Savior_Chainmail_8X16lJQ3xltTwynm.json index eafe1420..371cf794 100644 --- a/src/packs/items/armors/armor_Savior_Chainmail_8X16lJQ3xltTwynm.json +++ b/src/packs/items/armors/armor_Savior_Chainmail_8X16lJQ3xltTwynm.json @@ -105,11 +105,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 8 + "max": "8", + "key": "system.armorScore" } ] }, - "_id": "3Kn7ZRjhrw1WfALW", + "_id": "SWXFcH4qbmPYI7WH", "disabled": false, "start": null, "duration": { @@ -130,7 +131,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!8X16lJQ3xltTwynm.3Kn7ZRjhrw1WfALW" + "_key": "!items.effects!8X16lJQ3xltTwynm.SWXFcH4qbmPYI7WH" } ], "sort": 0, 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 cbd1e023..114b92f5 100644 --- a/src/packs/items/armors/armor_Spiked_Plate_Armor_QjwsIhXKqnlvRBMv.json +++ b/src/packs/items/armors/armor_Spiked_Plate_Armor_QjwsIhXKqnlvRBMv.json @@ -80,11 +80,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 5 + "max": "5", + "key": "system.armorScore" } ] }, - "_id": "6YpS3uYWIbeSgreg", + "_id": "OtOaxh7BCM2OMOmS", "disabled": false, "start": null, "duration": { @@ -105,7 +106,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!QjwsIhXKqnlvRBMv.6YpS3uYWIbeSgreg" + "_key": "!items.effects!QjwsIhXKqnlvRBMv.OtOaxh7BCM2OMOmS" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Tyris_Soft_Armor_PSW3BxCGmtLeWOxM.json b/src/packs/items/armors/armor_Tyris_Soft_Armor_PSW3BxCGmtLeWOxM.json index 0837f512..45fea752 100644 --- a/src/packs/items/armors/armor_Tyris_Soft_Armor_PSW3BxCGmtLeWOxM.json +++ b/src/packs/items/armors/armor_Tyris_Soft_Armor_PSW3BxCGmtLeWOxM.json @@ -67,11 +67,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 5 + "max": "5", + "key": "system.armorScore" } ] }, - "_id": "tiE0sRrTm2Ex9TAO", + "_id": "DgAQc09o3x6zn6DQ", "disabled": false, "start": null, "duration": { @@ -92,7 +93,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!PSW3BxCGmtLeWOxM.tiE0sRrTm2Ex9TAO" + "_key": "!items.effects!PSW3BxCGmtLeWOxM.DgAQc09o3x6zn6DQ" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Veritas_Opal_Armor_OvzgUTYy2RCN85vV.json b/src/packs/items/armors/armor_Veritas_Opal_Armor_OvzgUTYy2RCN85vV.json index ca76336c..e9f98300 100644 --- a/src/packs/items/armors/armor_Veritas_Opal_Armor_OvzgUTYy2RCN85vV.json +++ b/src/packs/items/armors/armor_Veritas_Opal_Armor_OvzgUTYy2RCN85vV.json @@ -67,11 +67,12 @@ "phase": "initial", "priority": 20, "value": 0, - "max": 6 + "max": "6", + "key": "system.armorScore" } ] }, - "_id": "fpPIhNaFxaz40Iaj", + "_id": "2gMOVh1Hty0nVWy4", "disabled": false, "start": null, "duration": { @@ -92,7 +93,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!OvzgUTYy2RCN85vV.fpPIhNaFxaz40Iaj" + "_key": "!items.effects!OvzgUTYy2RCN85vV.2gMOVh1Hty0nVWy4" } ], "sort": 0, From 26ae17c3cf66d5c0eedd4d3190d8e332d0482299 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Wed, 11 Feb 2026 11:04:40 +0100 Subject: [PATCH 17/23] Fixed SRD armor effects --- module/data/activeEffect/armorEffect.mjs | 14 ++- .../domainCard_Armorer_cy8GjBPGc9w9RaGO.json | 46 ++++--- ...omainCard_Bare_Bones_l5D9kq901JDESaXw.json | 95 ++++++++++++++- ...mainCard_Book_of_Ava_YtZzYBtR0yLPPA93.json | 48 ++++---- ...inCard_Valor_Touched_k1AtYd3lSchIymBr.json | 46 ++++--- .../folders_Splendor_TL1TutmbeCVJ06nR.json | 2 +- .../armor_Bare_Bones_ITAjcigTcUw5pMCN.json | 114 ------------------ .../folders_Special_tI3bfr6Sgi16Z7zm.json | 12 -- 8 files changed, 168 insertions(+), 209 deletions(-) delete mode 100644 src/packs/items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json delete mode 100644 src/packs/items/armors/folders_Special_tI3bfr6Sgi16Z7zm.json diff --git a/module/data/activeEffect/armorEffect.mjs b/module/data/activeEffect/armorEffect.mjs index 93a7ed3d..d32d24b2 100644 --- a/module/data/activeEffect/armorEffect.mjs +++ b/module/data/activeEffect/armorEffect.mjs @@ -107,9 +107,13 @@ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel const actor = this.parent.actor?.type === 'character' ? this.parent.actor : null; const changeData = this.changes[0]; + const maxParse = actor ? itemAbleRollParse(changeData.max, actor, this.parent.parent) : null; + const maxRoll = maxParse ? new Roll(maxParse).evaluateSync() : null; + const maxEvaluated = maxRoll ? (maxRoll.isDeterministic ? maxRoll.total : null) : null; + return { ...changeData, - max: actor ? itemAbleRollParse(changeData.max, actor, this.parent.parent) : changeData.max + max: maxEvaluated ?? changeData.max }; } @@ -153,9 +157,11 @@ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel } }; - return armorEffects.sort((a, b) => - increasing ? getEffectWeight(b) - getEffectWeight(a) : getEffectWeight(a) - getEffectWeight(b) - ); + return armorEffects + .filter(x => !x.disabled && !x.isSuppressed) + .sort((a, b) => + increasing ? getEffectWeight(b) - getEffectWeight(a) : getEffectWeight(a) - getEffectWeight(b) + ); } /* Overrides */ diff --git a/src/packs/domains/domainCard_Armorer_cy8GjBPGc9w9RaGO.json b/src/packs/domains/domainCard_Armorer_cy8GjBPGc9w9RaGO.json index aa9910dc..cb7bec9f 100644 --- a/src/packs/domains/domainCard_Armorer_cy8GjBPGc9w9RaGO.json +++ b/src/packs/domains/domainCard_Armorer_cy8GjBPGc9w9RaGO.json @@ -90,46 +90,42 @@ "effects": [ { "name": "Armorer", - "type": "base", + "type": "armor", "system": { - "rangeDependence": { - "enabled": false, - "type": "withinRange", - "target": "hostile", - "range": "melee" - } + "changes": [ + { + "key": "system.armorScore", + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": "1" + } + ] }, - "_id": "cED730OjuMW5haJR", + "_id": "PczrmraHWZ54NJsW", "img": "icons/tools/hand/hammer-and-nail.webp", - "changes": [ - { - "key": "system.armorScore", - "mode": 2, - "value": "1", - "priority": null - } - ], "disabled": false, + "start": null, "duration": { - "startTime": null, - "combat": null, - "seconds": null, - "rounds": null, - "turns": null, - "startRound": null, - "startTurn": null + "value": null, + "units": "seconds", + "expiry": null, + "expired": false }, - "description": "

While you’re wearing armor, gain a +1 bonus to your Armor Score.

", + "description": "

While you’re wearing armor, gain a +1 bonus to your Armor Score.

", "origin": null, "tint": "#ffffff", "transfer": true, "statuses": [], + "showIcon": 1, + "folder": null, "sort": 0, "flags": {}, "_stats": { "compendiumSource": null }, - "_key": "!items.effects!cy8GjBPGc9w9RaGO.cED730OjuMW5haJR" + "_key": "!items.effects!cy8GjBPGc9w9RaGO.PczrmraHWZ54NJsW" } ], "ownership": { diff --git a/src/packs/domains/domainCard_Bare_Bones_l5D9kq901JDESaXw.json b/src/packs/domains/domainCard_Bare_Bones_l5D9kq901JDESaXw.json index 3b1ea76a..f6474623 100644 --- a/src/packs/domains/domainCard_Bare_Bones_l5D9kq901JDESaXw.json +++ b/src/packs/domains/domainCard_Bare_Bones_l5D9kq901JDESaXw.json @@ -4,7 +4,7 @@ "type": "domainCard", "folder": "QpOL7jPbMBzH96qR", "system": { - "description": "

When you choose not to equip armor, you have a base Armor Score of 3 + your Strength and use the following as your base damage thresholds:

  • Tier 1: 9/19
  • Tier 2: 11/24
  • Tier 3: 13/31
  • Tier 4: 15/38

Equip the below armor to use Bare Bones.

@UUID[Compendium.daggerheart.armors.Item.ITAjcigTcUw5pMCN]{Bare Bones}

", + "description": "

When you choose not to equip armor, you have a base Armor Score of 3 + your Strength and use the following as your base damage thresholds:

  • Tier 1: 9/19

  • Tier 2: 11/24

  • Tier 3: 13/31

  • Tier 4: 15/38

Equip the below armor to use Bare Bones.

", "domain": "valor", "recallCost": 0, "level": 1, @@ -19,7 +19,98 @@ } }, "flags": {}, - "effects": [], + "effects": [ + { + "name": "Bare Bones Armor", + "type": "armor", + "system": { + "changes": [ + { + "value": 0, + "max": "3 + @system.traits.strength.value", + "key": "system.armorScore", + "type": "armor", + "phase": "initial", + "priority": 20 + } + ] + }, + "_id": "Zn1nNUwjlkbRfbMc", + "img": "icons/magic/control/buff-strength-muscle-damage-orange.webp", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "

You have a base Armor Score of 3 + your Strength

", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!l5D9kq901JDESaXw.Zn1nNUwjlkbRfbMc" + }, + { + "name": "Bare Bones", + "type": "base", + "system": { + "changes": [ + { + "key": "system.damageThresholds.major", + "type": "add", + "value": "9 + (@tier - 1) * 5 + max(0, (@tier -2) * 2 )", + "priority": null, + "phase": "initial" + }, + { + "key": "system.damageThresholds.severe", + "type": "add", + "value": "19 + (@tier - 1) * 5 + max(0, (@tier -2) * 2 )", + "priority": null, + "phase": "initial" + } + ], + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, + "_id": "FazU8RFjMmTpXs7Z", + "img": "icons/magic/control/buff-strength-muscle-damage-orange.webp", + "disabled": false, + "start": null, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "

You use the following as your base damage thresholds:

  • Tier 1: 9/19

  • Tier 2: 11/24

  • Tier 3: 13/31

  • Tier 4: 15/38

", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "showIcon": 1, + "folder": null, + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!l5D9kq901JDESaXw.FazU8RFjMmTpXs7Z" + } + ], "ownership": { "default": 0, "MQSznptE5yLT7kj8": 3 diff --git a/src/packs/domains/domainCard_Book_of_Ava_YtZzYBtR0yLPPA93.json b/src/packs/domains/domainCard_Book_of_Ava_YtZzYBtR0yLPPA93.json index 4ed5bd63..6dfc851a 100644 --- a/src/packs/domains/domainCard_Book_of_Ava_YtZzYBtR0yLPPA93.json +++ b/src/packs/domains/domainCard_Book_of_Ava_YtZzYBtR0yLPPA93.json @@ -105,7 +105,7 @@ }, "effects": [ { - "_id": "LdcT1nrkd5ORCU4n", + "_id": "OKf8Kjr6Px8A3ubJ", "onSave": false } ], @@ -252,43 +252,39 @@ "img": "icons/magic/defensive/shield-barrier-glowing-triangle-blue.webp", "origin": "Compendium.daggerheart.domains.Item.YtZzYBtR0yLPPA93", "transfer": false, - "_id": "LdcT1nrkd5ORCU4n", - "type": "base", + "type": "armor", + "_id": "OKf8Kjr6Px8A3ubJ", "system": { - "rangeDependence": { - "enabled": false, - "type": "withinRange", - "target": "hostile", - "range": "melee" - } + "changes": [ + { + "key": "system.armorScore", + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": "1" + } + ] }, - "changes": [ - { - "key": "system.armorScore", - "mode": 2, - "value": "1", - "priority": null - } - ], "disabled": false, + "start": null, "duration": { - "startTime": null, - "combat": null, - "seconds": null, - "rounds": null, - "turns": null, - "startRound": null, - "startTurn": null + "value": null, + "units": "seconds", + "expiry": null, + "expired": false }, - "description": "

+1 bonus to your Armor Score until your next rest, or the caster cast's Tava’s Armor again.

", + "description": "

Spend a Hope to give a target you can touch a +1 bonus to their Armor Score until their next rest or you cast Tava’s Armor again.

", "tint": "#ffffff", "statuses": [], + "showIcon": 1, + "folder": null, "sort": 0, "flags": {}, "_stats": { "compendiumSource": null }, - "_key": "!items.effects!YtZzYBtR0yLPPA93.LdcT1nrkd5ORCU4n" + "_key": "!items.effects!YtZzYBtR0yLPPA93.OKf8Kjr6Px8A3ubJ" } ], "ownership": { diff --git a/src/packs/domains/domainCard_Valor_Touched_k1AtYd3lSchIymBr.json b/src/packs/domains/domainCard_Valor_Touched_k1AtYd3lSchIymBr.json index 20fe18ea..ee6e377e 100644 --- a/src/packs/domains/domainCard_Valor_Touched_k1AtYd3lSchIymBr.json +++ b/src/packs/domains/domainCard_Valor_Touched_k1AtYd3lSchIymBr.json @@ -91,46 +91,42 @@ "effects": [ { "name": "Valor-Touched", - "type": "base", + "type": "armor", "system": { - "rangeDependence": { - "enabled": false, - "type": "withinRange", - "target": "hostile", - "range": "melee" - } + "changes": [ + { + "key": "system.armorScore", + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": "1" + } + ] }, - "_id": "H9lgIqqp1imSNOv9", + "_id": "JLw50ONfq1KJh1iM", "img": "icons/magic/control/control-influence-rally-purple.webp", - "changes": [ - { - "key": "system.armorScore", - "mode": 2, - "value": "1", - "priority": null - } - ], "disabled": false, + "start": null, "duration": { - "startTime": null, - "combat": null, - "seconds": null, - "rounds": null, - "turns": null, - "startRound": null, - "startTurn": null + "value": null, + "units": "seconds", + "expiry": null, + "expired": false }, - "description": "
  • +1 bonus to your Armor Score

  • When you mark 1 or more Hit Points without marking an Armor Slot, clear an Armor Slot.

", + "description": "

+1 bonus to your Armor Score

", "origin": null, "tint": "#ffffff", "transfer": true, "statuses": [], + "showIcon": 1, + "folder": null, "sort": 0, "flags": {}, "_stats": { "compendiumSource": null }, - "_key": "!items.effects!k1AtYd3lSchIymBr.H9lgIqqp1imSNOv9" + "_key": "!items.effects!k1AtYd3lSchIymBr.JLw50ONfq1KJh1iM" } ], "ownership": { diff --git a/src/packs/domains/folders_Splendor_TL1TutmbeCVJ06nR.json b/src/packs/domains/folders_Splendor_TL1TutmbeCVJ06nR.json index d7032288..9c220def 100644 --- a/src/packs/domains/folders_Splendor_TL1TutmbeCVJ06nR.json +++ b/src/packs/domains/folders_Splendor_TL1TutmbeCVJ06nR.json @@ -6,7 +6,7 @@ "sorting": "m", "_id": "TL1TutmbeCVJ06nR", "description": "", - "sort": 900000, + "sort": 750000, "flags": {}, "_key": "!folders!TL1TutmbeCVJ06nR" } diff --git a/src/packs/items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json b/src/packs/items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json deleted file mode 100644 index e555854d..00000000 --- a/src/packs/items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json +++ /dev/null @@ -1,114 +0,0 @@ -{ - "folder": "tI3bfr6Sgi16Z7zm", - "name": "Bare Bones", - "type": "armor", - "_id": "ITAjcigTcUw5pMCN", - "img": "icons/magic/control/buff-strength-muscle-damage.webp", - "system": { - "description": "

When you choose not to equip armor, you have a base Armor Score of 3 + your Strength and use the following as your base damage thresholds:

  • Tier 1: 9/19
  • Tier 2: 11/24
  • Tier 3: 13/31
  • Tier 4: 15/38
", - "actions": {}, - "attached": [], - "tier": 1, - "equipped": false, - "baseScore": 3, - "armorFeatures": [], - "marks": { - "value": 0 - }, - "baseThresholds": { - "major": 9, - "severe": 19 - } - }, - "effects": [ - { - "name": "Bare Bones", - "type": "base", - "system": { - "rangeDependence": { - "enabled": false, - "type": "withinRange", - "target": "hostile", - "range": "melee" - } - }, - "_id": "8ze88zUwdkQSKKJq", - "img": "icons/magic/control/buff-strength-muscle-damage.webp", - "changes": [ - { - "key": "system.armorScore", - "mode": 2, - "value": "@system.traits.strength.value", - "priority": 21 - } - ], - "disabled": false, - "duration": { - "startTime": null, - "combat": null, - "seconds": null, - "rounds": null, - "turns": null, - "startRound": null, - "startTurn": null - }, - "description": "

When you choose not to equip armor, you have a base Armor Score of 3 + your Strength and use the following as your base damage thresholds:

  • Tier 1: 9/19
  • Tier 2: 11/24
  • Tier 3: 13/31
  • Tier 4: 15/38
", - "origin": null, - "tint": "#ffffff", - "transfer": true, - "statuses": [], - "sort": 0, - "flags": {}, - "_stats": { - "compendiumSource": null - }, - "_key": "!items.effects!ITAjcigTcUw5pMCN.8ze88zUwdkQSKKJq" - }, - { - "type": "armor", - "name": "Armor Effect", - "img": "icons/equipment/chest/breastplate-helmet-metal.webp", - "system": { - "changes": [ - { - "type": "armor", - "phase": "initial", - "priority": 20, - "value": 0, - "max": "3", - "key": "system.armorScore" - } - ] - }, - "_id": "rm3t1XHFXaoZvw5v", - "disabled": false, - "start": null, - "duration": { - "value": null, - "units": "seconds", - "expiry": null, - "expired": false - }, - "description": "", - "origin": null, - "tint": "#ffffff", - "transfer": true, - "statuses": [], - "showIcon": 1, - "folder": null, - "sort": 0, - "flags": {}, - "_stats": { - "compendiumSource": null - }, - "_key": "!items.effects!ITAjcigTcUw5pMCN.rm3t1XHFXaoZvw5v" - } - ], - "sort": 0, - "ownership": { - "default": 0, - "MQSznptE5yLT7kj8": 3 - }, - "flags": {}, - "_key": "!items!ITAjcigTcUw5pMCN" -} diff --git a/src/packs/items/armors/folders_Special_tI3bfr6Sgi16Z7zm.json b/src/packs/items/armors/folders_Special_tI3bfr6Sgi16Z7zm.json deleted file mode 100644 index 65c4eca8..00000000 --- a/src/packs/items/armors/folders_Special_tI3bfr6Sgi16Z7zm.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "type": "Item", - "folder": null, - "name": "Special", - "color": null, - "sorting": "a", - "_id": "tI3bfr6Sgi16Z7zm", - "description": "", - "sort": 0, - "flags": {}, - "_key": "!folders!tI3bfr6Sgi16Z7zm" -} From 90ca39ebda8236e6f64183a1d8c9433663a3155c Mon Sep 17 00:00:00 2001 From: WBHarry Date: Wed, 11 Feb 2026 16:06:47 +0100 Subject: [PATCH 18/23] Finally finished the migration ._. --- .../armorActiveEffectConfig.mjs | 7 + module/applications/ui/progress.mjs | 2 +- module/systemRegistration/migrations.mjs | 147 ++++++++++++++---- 3 files changed, 121 insertions(+), 35 deletions(-) diff --git a/module/applications/sheets-configs/armorActiveEffectConfig.mjs b/module/applications/sheets-configs/armorActiveEffectConfig.mjs index 131558a0..7d8070e0 100644 --- a/module/applications/sheets-configs/armorActiveEffectConfig.mjs +++ b/module/applications/sheets-configs/armorActiveEffectConfig.mjs @@ -45,6 +45,13 @@ export default class ArmorActiveEffectConfig extends HandlebarsApplicationMixin( const partContext = await super._preparePartContext(partId, context); if (partId in partContext.tabs) partContext.tab = partContext.tabs[partId]; + switch (partId) { + case 'details': + partContext.isActorEffect = this.document.parent?.documentName === 'Actor'; + partContext.isItemEffect = this.document.parent?.documentName === 'Item'; + break; + } + return partContext; } diff --git a/module/applications/ui/progress.mjs b/module/applications/ui/progress.mjs index eca4ad6b..2fb1b445 100644 --- a/module/applications/ui/progress.mjs +++ b/module/applications/ui/progress.mjs @@ -13,7 +13,7 @@ export default class DhProgress { advance({ by = 1, label = this.label } = {}) { if (this.value === this.max) return; - this.value += Math.abs(by); + this.value = (this.value ?? 0) + Math.abs(by); this.#notification.update({ message: label, pct: this.value / this.max }); } diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs index 6bfb32ee..dff85fef 100644 --- a/module/systemRegistration/migrations.mjs +++ b/module/systemRegistration/migrations.mjs @@ -248,9 +248,10 @@ export async function runMigrations() { } if (foundry.utils.isNewerVersion('2.0.0', lastMigrationVersion)) { - /* Migrate existing armors to the new Armor Effects */ const progress = game.system.api.applications.ui.DhProgress.createMigrationProgress(0); + const progressBuffer = 50; + //#region Data Setup const lockedPacks = []; const itemPacks = game.packs.filter(x => x.metadata.type === 'Item'); const actorPacks = game.packs.filter(x => x.metadata.type === 'Actor'); @@ -259,7 +260,7 @@ export async function runMigrations() { const indexes = []; for (const pack of packs) { const indexValues = pack.index.values().reduce((acc, index) => { - if (index.type === type) acc.push(index.uuid); + if (!type || index.type === type) acc.push(index.uuid); return acc; }, []); @@ -274,54 +275,132 @@ export async function runMigrations() { return indexes; }; - const armorEntries = await getIndexes(itemPacks, 'armor'); - const actorEntries = await getIndexes(actorPacks, 'actor'); + const itemEntries = await getIndexes(itemPacks); + const characterEntries = await getIndexes(actorPacks, 'character'); - const worldArmors = game.items.filter(x => x instanceof game.system.api.documents.DHItem && x.type === 'armor'); - - for (const character of game.actors.filter(x => x.type === 'character')) { - worldArmors.push(...character.items.filter(x => x.type === 'armor')); - } + const worldItems = game.items; + const worldCharacters = game.actors.filter(x => x.type === 'character'); /* The async fetches are the mainstay of time. Leaving 1 progress for the sync logic */ - const newMax = armorEntries.length + actorEntries.length + 1; + const newMax = itemEntries.length + characterEntries.length + progressBuffer; progress.updateMax(newMax); - const compendiumArmors = []; - for (const entry of armorEntries) { - const armor = await foundry.utils.fromUuid(entry); - compendiumArmors.push(armor); + const compendiumItems = []; + for (const entry of itemEntries) { + const item = await foundry.utils.fromUuid(entry); + compendiumItems.push(item); progress.advance(); } - for (const entry of actorEntries) { - const actor = await foundry.utils.fromUuid(entry); - compendiumArmors.push(...actor.items.filter(x => x.type === 'armor')); + const compendiumCharacters = []; + for (const entry of characterEntries) { + const character = await foundry.utils.fromUuid(entry); + compendiumCharacters.push(character); progress.advance(); } + //#endregion - for (const armor of [...compendiumArmors, ...worldArmors]) { - const hasArmorEffect = armor.effects.some(x => x.type === 'armor'); - const migrationArmorScore = armor.flags.daggerheart?.baseScoreMigrationValue; - if (migrationArmorScore !== undefined && !hasArmorEffect) { - await armor.createEmbeddedDocuments('ActiveEffect', [ + /* Migrate existing effects modifying armor, creating new Armor Effects instead */ + const migrateEffects = async entity => { + const effectChangeData = []; + for (const effect of entity.effects) { + const oldArmorChanges = effect.system.changes.filter(x => x.key === 'system.armorScore'); + if (!oldArmorChanges.length) continue; + + const changeData = {}; + const newChanges = effect.system.changes.filter(x => x.key !== 'system.armorScore'); + if (newChanges.length) { + await effect.update({ 'system.changes': newChanges }); + } else { + changeData.deleteId = effect.id; + } + + const oldEffectData = effect.toObject(); + changeData.createData = { + ...oldEffectData, + type: 'armor', + system: { + ...oldEffectData.sytem, + changes: oldArmorChanges.map(change => ({ + key: 'system.armorScore', + type: CONFIG.DH.GENERAL.activeEffectModes.armor.id, + phase: 'initial', + priority: 20, + value: 0, + max: change.value + })) + } + }; + effectChangeData.push(changeData); + } + + for (const changeData of effectChangeData) { + const relatedActions = Array.from(entity.system.actions ?? []).filter(x => + x.effects.some(effect => effect._id === changeData.deleteId) + ); + const [newEffect] = await entity.createEmbeddedDocuments('ActiveEffect', [ { - ...game.system.api.data.activeEffects.ArmorEffect.getDefaultObject(), - changes: [ - { - type: CONFIG.DH.GENERAL.activeEffectModes.armor.id, - phase: 'initial', - priority: 20, - value: 0, - max: migrationArmorScore.toString() - } - ] + ...changeData.createData, + transfer: relatedActions.length ? false : true } ]); + for (const action of relatedActions) { + await action.update({ + effects: action.effects.map(effect => ({ + ...effect, + _id: effect._id === changeData.deleteId ? newEffect.id : effect._id + })) + }); + } } + + await entity.deleteEmbeddedDocuments( + 'ActiveEffect', + effectChangeData.reduce((acc, data) => { + if (data.deleteId) acc.push(data.deleteId); + return acc; + }, []) + ); + }; + + /* Migrate existing armors to the new Armor Effects */ + const migrateItems = async items => { + for (const item of items) { + await migrateEffects(item); + + if (item instanceof game.system.api.documents.DHItem && item.type === 'armor') { + const hasArmorEffect = item.effects.some(x => x.type === 'armor'); + const migrationArmorScore = item.flags.daggerheart?.baseScoreMigrationValue; + if (migrationArmorScore !== undefined && !hasArmorEffect) { + await item.createEmbeddedDocuments('ActiveEffect', [ + { + ...game.system.api.data.activeEffects.ArmorEffect.getDefaultObject(), + changes: [ + { + key: 'system.armorScore', + type: CONFIG.DH.GENERAL.activeEffectModes.armor.id, + phase: 'initial', + priority: 20, + value: 0, + max: migrationArmorScore.toString() + } + ] + } + ]); + } + } + } + }; + + await migrateItems([...compendiumItems, ...worldItems]); + progress.advance({ by: progressBuffer / 2 }); + + for (const actor of [...compendiumCharacters, ...worldCharacters]) { + await migrateEffects(actor); + await migrateItems(actor.items); } - progress.advance(); + progress.advance({ by: progressBuffer / 2 }); for (let packId of lockedPacks) { const pack = game.packs.get(packId); @@ -330,7 +409,7 @@ export async function runMigrations() { progress.close(); - // lastMigrationVersion = '2.0.0'; + lastMigrationVersion = '2.0.0'; } //#endregion From c067fd0e10f1d557e2cfe9f918aa56a74e1df619 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Wed, 11 Feb 2026 16:21:34 +0100 Subject: [PATCH 19/23] SRD finalization --- .../domainCard_Armorer_cy8GjBPGc9w9RaGO.json | 12 ++-- ...omainCard_Bare_Bones_l5D9kq901JDESaXw.json | 12 ++-- ...mainCard_Book_of_Ava_YtZzYBtR0yLPPA93.json | 14 ++-- ...inCard_Valor_Touched_k1AtYd3lSchIymBr.json | 12 ++-- .../folders_Splendor_TL1TutmbeCVJ06nR.json | 2 +- ...nced_Chainmail_Armor_LzLOJ9EVaHWAjoq9.json | 8 +-- ...ced_Full_Plate_Armor_crIbCb9NZ4K0VpoU.json | 8 +-- ...anced_Gambeson_Armor_epkAmlZVk7HOfUUT.json | 8 +-- ...vanced_Leather_Armor_itSOp2GCyem0f7oM.json | 8 +-- .../armor_Bare_Bones_ITAjcigTcUw5pMCN.json | 71 ++++++++++++++++++ ..._Bellamoi_Fine_Armor_WuoVwZA53XRAIt6d.json | 8 +-- ...rmor_Bladefare_Armor_mNN6pvcsS10ChrWF.json | 8 +-- ...rmor_Chainmail_Armor_haULhuEg37zUUvhb.json | 8 +-- ...mor_Channeling_Armor_vMJxEWz1srfwMsoj.json | 8 +-- ...or_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json | 8 +-- ...or_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json | 8 +-- ...lundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json | 8 +-- ...mor_Emberwoven_Armor_bcQUh4QG3qFX0Vx6.json | 8 +-- ...Full_Fortified_Armor_7emTSt6nhZuTlvt5.json | 8 +-- ...mor_Full_Plate_Armor_UdUJNa31WxFW2noa.json | 4 +- ...armor_Gambeson_Armor_yJFp1bfpecDcStVK.json | 8 +-- ...mor_Harrowbone_Armor_dvyQeUVRLc9y6rnt.json | 8 +-- ...oved_Chainmail_Armor_K5WkjS0NGqHYmhU3.json | 8 +-- ...ved_Full_Plate_Armor_9f7RozpPTqrzJS1m.json | 8 +-- ...roved_Gambeson_Armor_jphnMZjnS2FkOH3s.json | 8 +-- ...proved_Leather_Armor_t91M61pSCMKStTNt.json | 8 +-- ...ee_Breastplate_Armor_tzZntboNtHL5C6VM.json | 8 +-- .../armor_Leather_Armor_nibfdNtp2PtxvbVz.json | 8 +-- ...dary_Chainmail_Armor_EsIN5OLKe9ZYFNXZ.json | 8 +-- ...ary_Full_Plate_Armor_SXWjUR2aUR6bYvdl.json | 8 +-- ...ndary_Gambeson_Armor_c6tMXz4rPf9ioQrf.json | 8 +-- ...endary_Leather_Armor_Tptgl5WOj76TyFn7.json | 8 +-- ...armor_Monett_s_Cloak_AQzU2RsqS5V5bd1v.json | 8 +-- ...armor_Rosewild_Armor_tN8kAeBvNKM3EBFo.json | 8 +-- ...nes_of_Fortification_P4qAEDJUoNLgVRsA.json | 8 +-- ...netan_Floating_Armor_tHlBUDQC24YMZqd6.json | 8 +-- ...mor_Savior_Chainmail_8X16lJQ3xltTwynm.json | 8 +-- ...r_Spiked_Plate_Armor_QjwsIhXKqnlvRBMv.json | 8 +-- ...mor_Tyris_Soft_Armor_PSW3BxCGmtLeWOxM.json | 8 +-- ...r_Veritas_Opal_Armor_OvzgUTYy2RCN85vV.json | 8 +-- .../folders_Special_tI3bfr6Sgi16Z7zm.json | 12 ++++ ...dvanced_Round_Shield_hiEOGF2reabGLUoi.json | 41 ++++++----- ...dvanced_Tower_Shield_OfOzQbs4hg6QbfTG.json | 72 +++++++++++++++---- ...mproved_Round_Shield_DlinEBGZfIlvreO3.json | 41 ++++++----- ...mproved_Tower_Shield_bxt3NsbMqTSdI5ab.json | 72 +++++++++++++++---- .../weapon_Labrys_Axe_ijWppQzSOqVCb3rE.json | 35 +++++---- ...gendary_Round_Shield_A28WL9E2lJ3iLZHW.json | 41 ++++++----- ...gendary_Tower_Shield_MaJIROht7A9LxIZx.json | 72 +++++++++++++++---- .../weapon_Round_Shield_mxwWKDujgsRcZWPT.json | 41 ++++++----- ...weapon_Spiked_Shield_vzyzFwLUniWZV1rt.json | 71 ++++++++++++++---- .../weapon_Tower_Shield_C9aWpK1shVMWP4m5.json | 72 +++++++++++++++---- 51 files changed, 637 insertions(+), 324 deletions(-) create mode 100644 src/packs/items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json create mode 100644 src/packs/items/armors/folders_Special_tI3bfr6Sgi16Z7zm.json diff --git a/src/packs/domains/domainCard_Armorer_cy8GjBPGc9w9RaGO.json b/src/packs/domains/domainCard_Armorer_cy8GjBPGc9w9RaGO.json index cb7bec9f..c06a34ab 100644 --- a/src/packs/domains/domainCard_Armorer_cy8GjBPGc9w9RaGO.json +++ b/src/packs/domains/domainCard_Armorer_cy8GjBPGc9w9RaGO.json @@ -103,29 +103,29 @@ } ] }, - "_id": "PczrmraHWZ54NJsW", + "_id": "tJw2JIPcT9hEMRXg", "img": "icons/tools/hand/hammer-and-nail.webp", "disabled": false, - "start": null, "duration": { "value": null, "units": "seconds", "expiry": null, "expired": false }, - "description": "

While you’re wearing armor, gain a +1 bonus to your Armor Score.

", + "description": "

While you’re wearing armor, gain a +1 bonus to your Armor Score.

", "origin": null, "tint": "#ffffff", "transfer": true, "statuses": [], - "showIcon": 1, - "folder": null, "sort": 0, "flags": {}, "_stats": { "compendiumSource": null }, - "_key": "!items.effects!cy8GjBPGc9w9RaGO.PczrmraHWZ54NJsW" + "start": null, + "showIcon": 1, + "folder": null, + "_key": "!items.effects!cy8GjBPGc9w9RaGO.tJw2JIPcT9hEMRXg" } ], "ownership": { diff --git a/src/packs/domains/domainCard_Bare_Bones_l5D9kq901JDESaXw.json b/src/packs/domains/domainCard_Bare_Bones_l5D9kq901JDESaXw.json index f6474623..7e0129d7 100644 --- a/src/packs/domains/domainCard_Bare_Bones_l5D9kq901JDESaXw.json +++ b/src/packs/domains/domainCard_Bare_Bones_l5D9kq901JDESaXw.json @@ -4,7 +4,7 @@ "type": "domainCard", "folder": "QpOL7jPbMBzH96qR", "system": { - "description": "

When you choose not to equip armor, you have a base Armor Score of 3 + your Strength and use the following as your base damage thresholds:

  • Tier 1: 9/19

  • Tier 2: 11/24

  • Tier 3: 13/31

  • Tier 4: 15/38

Equip the below armor to use Bare Bones.

", + "description": "

When you choose not to equip armor, you have a base Armor Score of 3 + your Strength and use the following as your base damage thresholds:

  • Tier 1: 9/19
  • Tier 2: 11/24
  • Tier 3: 13/31
  • Tier 4: 15/38

Equip the below armor to use Bare Bones.

@UUID[Compendium.daggerheart.armors.Item.ITAjcigTcUw5pMCN]{Bare Bones}

", "domain": "valor", "recallCost": 0, "level": 1, @@ -35,7 +35,7 @@ } ] }, - "_id": "Zn1nNUwjlkbRfbMc", + "_id": "FCsgz7Tdsw6QUzBs", "img": "icons/magic/control/buff-strength-muscle-damage-orange.webp", "disabled": false, "start": null, @@ -45,7 +45,7 @@ "expiry": null, "expired": false }, - "description": "

You have a base Armor Score of 3 + your Strength

", + "description": "

You have a base Armor Score of 3 + your Strength.

", "origin": null, "tint": "#ffffff", "transfer": true, @@ -57,7 +57,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!l5D9kq901JDESaXw.Zn1nNUwjlkbRfbMc" + "_key": "!items.effects!l5D9kq901JDESaXw.FCsgz7Tdsw6QUzBs" }, { "name": "Bare Bones", @@ -86,7 +86,7 @@ "range": "melee" } }, - "_id": "FazU8RFjMmTpXs7Z", + "_id": "8flPpWNoBeuFPFTK", "img": "icons/magic/control/buff-strength-muscle-damage-orange.webp", "disabled": false, "start": null, @@ -108,7 +108,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!l5D9kq901JDESaXw.FazU8RFjMmTpXs7Z" + "_key": "!items.effects!l5D9kq901JDESaXw.8flPpWNoBeuFPFTK" } ], "ownership": { diff --git a/src/packs/domains/domainCard_Book_of_Ava_YtZzYBtR0yLPPA93.json b/src/packs/domains/domainCard_Book_of_Ava_YtZzYBtR0yLPPA93.json index 6dfc851a..da2d75f1 100644 --- a/src/packs/domains/domainCard_Book_of_Ava_YtZzYBtR0yLPPA93.json +++ b/src/packs/domains/domainCard_Book_of_Ava_YtZzYBtR0yLPPA93.json @@ -105,7 +105,7 @@ }, "effects": [ { - "_id": "OKf8Kjr6Px8A3ubJ", + "_id": "ptYT10JZ2WJHvFMd", "onSave": false } ], @@ -252,8 +252,8 @@ "img": "icons/magic/defensive/shield-barrier-glowing-triangle-blue.webp", "origin": "Compendium.daggerheart.domains.Item.YtZzYBtR0yLPPA93", "transfer": false, + "_id": "ptYT10JZ2WJHvFMd", "type": "armor", - "_id": "OKf8Kjr6Px8A3ubJ", "system": { "changes": [ { @@ -267,24 +267,24 @@ ] }, "disabled": false, - "start": null, "duration": { "value": null, "units": "seconds", "expiry": null, "expired": false }, - "description": "

Spend a Hope to give a target you can touch a +1 bonus to their Armor Score until their next rest or you cast Tava’s Armor again.

", + "description": "

+1 bonus to your Armor Score until your next rest, or the caster cast's Tava’s Armor again.

", "tint": "#ffffff", "statuses": [], - "showIcon": 1, - "folder": null, "sort": 0, "flags": {}, "_stats": { "compendiumSource": null }, - "_key": "!items.effects!YtZzYBtR0yLPPA93.OKf8Kjr6Px8A3ubJ" + "start": null, + "showIcon": 1, + "folder": null, + "_key": "!items.effects!YtZzYBtR0yLPPA93.ptYT10JZ2WJHvFMd" } ], "ownership": { diff --git a/src/packs/domains/domainCard_Valor_Touched_k1AtYd3lSchIymBr.json b/src/packs/domains/domainCard_Valor_Touched_k1AtYd3lSchIymBr.json index ee6e377e..7df5e095 100644 --- a/src/packs/domains/domainCard_Valor_Touched_k1AtYd3lSchIymBr.json +++ b/src/packs/domains/domainCard_Valor_Touched_k1AtYd3lSchIymBr.json @@ -104,29 +104,29 @@ } ] }, - "_id": "JLw50ONfq1KJh1iM", + "_id": "Ma8Zp005QYKPWIEN", "img": "icons/magic/control/control-influence-rally-purple.webp", "disabled": false, - "start": null, "duration": { "value": null, "units": "seconds", "expiry": null, "expired": false }, - "description": "

+1 bonus to your Armor Score

", + "description": "
  • +1 bonus to your Armor Score

  • When you mark 1 or more Hit Points without marking an Armor Slot, clear an Armor Slot.

", "origin": null, "tint": "#ffffff", "transfer": true, "statuses": [], - "showIcon": 1, - "folder": null, "sort": 0, "flags": {}, "_stats": { "compendiumSource": null }, - "_key": "!items.effects!k1AtYd3lSchIymBr.JLw50ONfq1KJh1iM" + "start": null, + "showIcon": 1, + "folder": null, + "_key": "!items.effects!k1AtYd3lSchIymBr.Ma8Zp005QYKPWIEN" } ], "ownership": { diff --git a/src/packs/domains/folders_Splendor_TL1TutmbeCVJ06nR.json b/src/packs/domains/folders_Splendor_TL1TutmbeCVJ06nR.json index 9c220def..d7032288 100644 --- a/src/packs/domains/folders_Splendor_TL1TutmbeCVJ06nR.json +++ b/src/packs/domains/folders_Splendor_TL1TutmbeCVJ06nR.json @@ -6,7 +6,7 @@ "sorting": "m", "_id": "TL1TutmbeCVJ06nR", "description": "", - "sort": 750000, + "sort": 900000, "flags": {}, "_key": "!folders!TL1TutmbeCVJ06nR" } 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 7287d027..91b1f890 100644 --- a/src/packs/items/armors/armor_Advanced_Chainmail_Armor_LzLOJ9EVaHWAjoq9.json +++ b/src/packs/items/armors/armor_Advanced_Chainmail_Armor_LzLOJ9EVaHWAjoq9.json @@ -71,16 +71,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "6", - "key": "system.armorScore" + "max": "6" } ] }, - "_id": "xU3zAv0sBiOGAE4i", + "_id": "YehcKtTeJ18q0THd", "disabled": false, "start": null, "duration": { @@ -101,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!LzLOJ9EVaHWAjoq9.xU3zAv0sBiOGAE4i" + "_key": "!items.effects!LzLOJ9EVaHWAjoq9.YehcKtTeJ18q0THd" } ], "sort": 0, 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 6522c67a..f1dd3086 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 @@ -76,16 +76,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "6", - "key": "system.armorScore" + "max": "6" } ] }, - "_id": "TMxnzDzCmVibJWQ0", + "_id": "Xp0MlTLdCe2oP36X", "disabled": false, "start": null, "duration": { @@ -106,7 +106,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!crIbCb9NZ4K0VpoU.TMxnzDzCmVibJWQ0" + "_key": "!items.effects!crIbCb9NZ4K0VpoU.Xp0MlTLdCe2oP36X" } ], "sort": 0, 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 d9ee9649..1618fe85 100644 --- a/src/packs/items/armors/armor_Advanced_Gambeson_Armor_epkAmlZVk7HOfUUT.json +++ b/src/packs/items/armors/armor_Advanced_Gambeson_Armor_epkAmlZVk7HOfUUT.json @@ -71,16 +71,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "5", - "key": "system.armorScore" + "max": "5" } ] }, - "_id": "HxZEKljAth8b5Wcv", + "_id": "GyPhsm7zLznZDfN2", "disabled": false, "start": null, "duration": { @@ -101,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!epkAmlZVk7HOfUUT.HxZEKljAth8b5Wcv" + "_key": "!items.effects!epkAmlZVk7HOfUUT.GyPhsm7zLznZDfN2" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Advanced_Leather_Armor_itSOp2GCyem0f7oM.json b/src/packs/items/armors/armor_Advanced_Leather_Armor_itSOp2GCyem0f7oM.json index 09a45a8a..b7a63b40 100644 --- a/src/packs/items/armors/armor_Advanced_Leather_Armor_itSOp2GCyem0f7oM.json +++ b/src/packs/items/armors/armor_Advanced_Leather_Armor_itSOp2GCyem0f7oM.json @@ -33,16 +33,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "5", - "key": "system.armorScore" + "max": "5" } ] }, - "_id": "jSGmBv0I5FhxmTen", + "_id": "XJueICAnl5vu2q2U", "disabled": false, "start": null, "duration": { @@ -63,7 +63,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!itSOp2GCyem0f7oM.jSGmBv0I5FhxmTen" + "_key": "!items.effects!itSOp2GCyem0f7oM.XJueICAnl5vu2q2U" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json b/src/packs/items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json new file mode 100644 index 00000000..a0b2aae3 --- /dev/null +++ b/src/packs/items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json @@ -0,0 +1,71 @@ +{ + "folder": "tI3bfr6Sgi16Z7zm", + "name": "Bare Bones", + "type": "armor", + "_id": "ITAjcigTcUw5pMCN", + "img": "icons/magic/control/buff-strength-muscle-damage.webp", + "system": { + "description": "

When you choose not to equip armor, you have a base Armor Score of 3 + your Strength and use the following as your base damage thresholds:

  • Tier 1: 9/19
  • Tier 2: 11/24
  • Tier 3: 13/31
  • Tier 4: 15/38
", + "actions": {}, + "attached": [], + "tier": 1, + "equipped": false, + "baseScore": 3, + "armorFeatures": [], + "marks": { + "value": 0 + }, + "baseThresholds": { + "major": 9, + "severe": 19 + } + }, + "effects": [ + { + "name": "Bare Bones", + "type": "armor", + "system": { + "changes": [ + { + "key": "system.armorScore", + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": "@system.traits.strength.value" + } + ] + }, + "_id": "C7as6q5bx3S0Xxfn", + "img": "icons/magic/control/buff-strength-muscle-damage.webp", + "disabled": false, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "description": "

When you choose not to equip armor, you have a base Armor Score of 3 + your Strength and use the following as your base damage thresholds:

  • Tier 1: 9/19
  • Tier 2: 11/24
  • Tier 3: 13/31
  • Tier 4: 15/38
", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "start": null, + "showIcon": 1, + "folder": null, + "_key": "!items.effects!ITAjcigTcUw5pMCN.C7as6q5bx3S0Xxfn" + } + ], + "sort": 0, + "ownership": { + "default": 0, + "MQSznptE5yLT7kj8": 3 + }, + "flags": {}, + "_key": "!items!ITAjcigTcUw5pMCN" +} 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 fc7c62eb..9242aed9 100644 --- a/src/packs/items/armors/armor_Bellamoi_Fine_Armor_WuoVwZA53XRAIt6d.json +++ b/src/packs/items/armors/armor_Bellamoi_Fine_Armor_WuoVwZA53XRAIt6d.json @@ -71,16 +71,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "5", - "key": "system.armorScore" + "max": "5" } ] }, - "_id": "34OQBJZZV3d5AN7U", + "_id": "2OMciFns3bSETeH9", "disabled": false, "start": null, "duration": { @@ -101,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!WuoVwZA53XRAIt6d.34OQBJZZV3d5AN7U" + "_key": "!items.effects!WuoVwZA53XRAIt6d.2OMciFns3bSETeH9" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Bladefare_Armor_mNN6pvcsS10ChrWF.json b/src/packs/items/armors/armor_Bladefare_Armor_mNN6pvcsS10ChrWF.json index a82f0ba4..cc245d50 100644 --- a/src/packs/items/armors/armor_Bladefare_Armor_mNN6pvcsS10ChrWF.json +++ b/src/packs/items/armors/armor_Bladefare_Armor_mNN6pvcsS10ChrWF.json @@ -71,16 +71,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "6", - "key": "system.armorScore" + "max": "6" } ] }, - "_id": "6AGrG6Y1wUSY3mg5", + "_id": "dIb9PWvzyS3jYDUj", "disabled": false, "start": null, "duration": { @@ -101,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!mNN6pvcsS10ChrWF.6AGrG6Y1wUSY3mg5" + "_key": "!items.effects!mNN6pvcsS10ChrWF.dIb9PWvzyS3jYDUj" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Chainmail_Armor_haULhuEg37zUUvhb.json b/src/packs/items/armors/armor_Chainmail_Armor_haULhuEg37zUUvhb.json index 9fc6b7db..a45cfa04 100644 --- a/src/packs/items/armors/armor_Chainmail_Armor_haULhuEg37zUUvhb.json +++ b/src/packs/items/armors/armor_Chainmail_Armor_haULhuEg37zUUvhb.json @@ -71,16 +71,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "4", - "key": "system.armorScore" + "max": "4" } ] }, - "_id": "RXsc2d47cauTWTf0", + "_id": "hA0EcaykFiIpg4ZH", "disabled": false, "start": null, "duration": { @@ -101,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!haULhuEg37zUUvhb.RXsc2d47cauTWTf0" + "_key": "!items.effects!haULhuEg37zUUvhb.hA0EcaykFiIpg4ZH" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Channeling_Armor_vMJxEWz1srfwMsoj.json b/src/packs/items/armors/armor_Channeling_Armor_vMJxEWz1srfwMsoj.json index d37d45ff..6f8ec29d 100644 --- a/src/packs/items/armors/armor_Channeling_Armor_vMJxEWz1srfwMsoj.json +++ b/src/packs/items/armors/armor_Channeling_Armor_vMJxEWz1srfwMsoj.json @@ -71,16 +71,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "5", - "key": "system.armorScore" + "max": "5" } ] }, - "_id": "dKK4sbP3DZQYdmTn", + "_id": "Wejd1c4e8VtnFoc4", "disabled": false, "start": null, "duration": { @@ -101,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!vMJxEWz1srfwMsoj.dKK4sbP3DZQYdmTn" + "_key": "!items.effects!vMJxEWz1srfwMsoj.Wejd1c4e8VtnFoc4" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json b/src/packs/items/armors/armor_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json index 136b89d6..335ab56f 100644 --- a/src/packs/items/armors/armor_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json +++ b/src/packs/items/armors/armor_Dragonscale_Armor_mdQ69eFHyAQUDmE7.json @@ -70,16 +70,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "5", - "key": "system.armorScore" + "max": "5" } ] }, - "_id": "kNq8nLq1ljLZZGDg", + "_id": "FgjNYkbghYSz8gwW", "disabled": false, "start": null, "duration": { @@ -100,7 +100,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!mdQ69eFHyAQUDmE7.kNq8nLq1ljLZZGDg" + "_key": "!items.effects!mdQ69eFHyAQUDmE7.FgjNYkbghYSz8gwW" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json b/src/packs/items/armors/armor_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json index 9b858104..2a1e8c83 100644 --- a/src/packs/items/armors/armor_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json +++ b/src/packs/items/armors/armor_Dunamis_Silkchain_hAY6UgdGT7dj22Pr.json @@ -96,16 +96,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "7", - "key": "system.armorScore" + "max": "7" } ] }, - "_id": "sBTZAS0aYcE15RwZ", + "_id": "n4wyEBHbHIuYNBzt", "disabled": false, "start": null, "duration": { @@ -126,7 +126,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!hAY6UgdGT7dj22Pr.sBTZAS0aYcE15RwZ" + "_key": "!items.effects!hAY6UgdGT7dj22Pr.n4wyEBHbHIuYNBzt" } ], "sort": 0, 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 d84108d8..e19c363b 100644 --- a/src/packs/items/armors/armor_Elundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json +++ b/src/packs/items/armors/armor_Elundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json @@ -72,16 +72,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "4", - "key": "system.armorScore" + "max": "4" } ] }, - "_id": "uFvhPlk3FVGfQST4", + "_id": "gZfuMqjYTYLspQop", "disabled": false, "start": null, "duration": { @@ -102,7 +102,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!Q6LxmtFetDDkoZVZ.uFvhPlk3FVGfQST4" + "_key": "!items.effects!Q6LxmtFetDDkoZVZ.gZfuMqjYTYLspQop" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Emberwoven_Armor_bcQUh4QG3qFX0Vx6.json b/src/packs/items/armors/armor_Emberwoven_Armor_bcQUh4QG3qFX0Vx6.json index 92652f66..5be30445 100644 --- a/src/packs/items/armors/armor_Emberwoven_Armor_bcQUh4QG3qFX0Vx6.json +++ b/src/packs/items/armors/armor_Emberwoven_Armor_bcQUh4QG3qFX0Vx6.json @@ -94,16 +94,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "6", - "key": "system.armorScore" + "max": "6" } ] }, - "_id": "Dg8Gx6G3nwAH9wt2", + "_id": "5t3jCX3AGiWBB4DN", "disabled": false, "start": null, "duration": { @@ -124,7 +124,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!bcQUh4QG3qFX0Vx6.Dg8Gx6G3nwAH9wt2" + "_key": "!items.effects!bcQUh4QG3qFX0Vx6.5t3jCX3AGiWBB4DN" } ], "sort": 0, 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 a4e21b75..52aa784b 100644 --- a/src/packs/items/armors/armor_Full_Fortified_Armor_7emTSt6nhZuTlvt5.json +++ b/src/packs/items/armors/armor_Full_Fortified_Armor_7emTSt6nhZuTlvt5.json @@ -71,16 +71,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "4", - "key": "system.armorScore" + "max": "4" } ] }, - "_id": "XPbNhspFyOj8RIQJ", + "_id": "9jCrg3Acd75jVclW", "disabled": false, "start": null, "duration": { @@ -101,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!7emTSt6nhZuTlvt5.XPbNhspFyOj8RIQJ" + "_key": "!items.effects!7emTSt6nhZuTlvt5.9jCrg3Acd75jVclW" } ], "sort": 0, 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 0400f59e..c603305b 100644 --- a/src/packs/items/armors/armor_Full_Plate_Armor_UdUJNa31WxFW2noa.json +++ b/src/packs/items/armors/armor_Full_Plate_Armor_UdUJNa31WxFW2noa.json @@ -85,7 +85,7 @@ } ] }, - "_id": "zji5nzTC1y8BUWHn", + "_id": "OmGtjOMcTHNN6OsH", "disabled": false, "start": null, "duration": { @@ -106,7 +106,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!UdUJNa31WxFW2noa.zji5nzTC1y8BUWHn" + "_key": "!items.effects!UdUJNa31WxFW2noa.OmGtjOMcTHNN6OsH" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Gambeson_Armor_yJFp1bfpecDcStVK.json b/src/packs/items/armors/armor_Gambeson_Armor_yJFp1bfpecDcStVK.json index eb8285cb..36484785 100644 --- a/src/packs/items/armors/armor_Gambeson_Armor_yJFp1bfpecDcStVK.json +++ b/src/packs/items/armors/armor_Gambeson_Armor_yJFp1bfpecDcStVK.json @@ -71,16 +71,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "3", - "key": "system.armorScore" + "max": "3" } ] }, - "_id": "72LkcLIihluGgx48", + "_id": "ySw8mkws8rxzxsg4", "disabled": false, "start": null, "duration": { @@ -101,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!yJFp1bfpecDcStVK.72LkcLIihluGgx48" + "_key": "!items.effects!yJFp1bfpecDcStVK.ySw8mkws8rxzxsg4" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Harrowbone_Armor_dvyQeUVRLc9y6rnt.json b/src/packs/items/armors/armor_Harrowbone_Armor_dvyQeUVRLc9y6rnt.json index 43057efa..7f69317b 100644 --- a/src/packs/items/armors/armor_Harrowbone_Armor_dvyQeUVRLc9y6rnt.json +++ b/src/packs/items/armors/armor_Harrowbone_Armor_dvyQeUVRLc9y6rnt.json @@ -87,16 +87,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "4", - "key": "system.armorScore" + "max": "4" } ] }, - "_id": "GYRwYD3CHW9q4N29", + "_id": "nyoNusMuukJt1MJw", "disabled": false, "start": null, "duration": { @@ -117,7 +117,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!dvyQeUVRLc9y6rnt.GYRwYD3CHW9q4N29" + "_key": "!items.effects!dvyQeUVRLc9y6rnt.nyoNusMuukJt1MJw" } ], "sort": 0, 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 69b1bbae..dd674a8b 100644 --- a/src/packs/items/armors/armor_Improved_Chainmail_Armor_K5WkjS0NGqHYmhU3.json +++ b/src/packs/items/armors/armor_Improved_Chainmail_Armor_K5WkjS0NGqHYmhU3.json @@ -71,16 +71,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "5", - "key": "system.armorScore" + "max": "5" } ] }, - "_id": "2KD6EdRL2L2gQkMR", + "_id": "7FdWcilv74zKcXWk", "disabled": false, "start": null, "duration": { @@ -101,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!K5WkjS0NGqHYmhU3.2KD6EdRL2L2gQkMR" + "_key": "!items.effects!K5WkjS0NGqHYmhU3.7FdWcilv74zKcXWk" } ], "sort": 0, 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 eb62a85b..00b2afa9 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 @@ -76,16 +76,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "5", - "key": "system.armorScore" + "max": "5" } ] }, - "_id": "ehijWY3PGw1OaQr0", + "_id": "qgA4nbITVOp2WTpl", "disabled": false, "start": null, "duration": { @@ -106,7 +106,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!9f7RozpPTqrzJS1m.ehijWY3PGw1OaQr0" + "_key": "!items.effects!9f7RozpPTqrzJS1m.qgA4nbITVOp2WTpl" } ], "sort": 0, 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 2e1b4f93..3e2a52ba 100644 --- a/src/packs/items/armors/armor_Improved_Gambeson_Armor_jphnMZjnS2FkOH3s.json +++ b/src/packs/items/armors/armor_Improved_Gambeson_Armor_jphnMZjnS2FkOH3s.json @@ -71,16 +71,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "4", - "key": "system.armorScore" + "max": "4" } ] }, - "_id": "d6QRNZ1X4wdoy82q", + "_id": "dtkOq7rUKj5nLGJP", "disabled": false, "start": null, "duration": { @@ -101,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!jphnMZjnS2FkOH3s.d6QRNZ1X4wdoy82q" + "_key": "!items.effects!jphnMZjnS2FkOH3s.dtkOq7rUKj5nLGJP" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Improved_Leather_Armor_t91M61pSCMKStTNt.json b/src/packs/items/armors/armor_Improved_Leather_Armor_t91M61pSCMKStTNt.json index 3dce3151..7dfe14e7 100644 --- a/src/packs/items/armors/armor_Improved_Leather_Armor_t91M61pSCMKStTNt.json +++ b/src/packs/items/armors/armor_Improved_Leather_Armor_t91M61pSCMKStTNt.json @@ -33,16 +33,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "4", - "key": "system.armorScore" + "max": "4" } ] }, - "_id": "lQNHCtW6HnIj0b7F", + "_id": "JqnUKeUDbH4YJbVb", "disabled": false, "start": null, "duration": { @@ -63,7 +63,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!t91M61pSCMKStTNt.lQNHCtW6HnIj0b7F" + "_key": "!items.effects!t91M61pSCMKStTNt.JqnUKeUDbH4YJbVb" } ], "sort": 0, 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 9be75429..8b978654 100644 --- a/src/packs/items/armors/armor_Irontree_Breastplate_Armor_tzZntboNtHL5C6VM.json +++ b/src/packs/items/armors/armor_Irontree_Breastplate_Armor_tzZntboNtHL5C6VM.json @@ -83,16 +83,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "4", - "key": "system.armorScore" + "max": "4" } ] }, - "_id": "jnjdtSTQF1zTSkEr", + "_id": "6Mh24zh1c3aK60wZ", "disabled": false, "start": null, "duration": { @@ -113,7 +113,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!tzZntboNtHL5C6VM.jnjdtSTQF1zTSkEr" + "_key": "!items.effects!tzZntboNtHL5C6VM.6Mh24zh1c3aK60wZ" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Leather_Armor_nibfdNtp2PtxvbVz.json b/src/packs/items/armors/armor_Leather_Armor_nibfdNtp2PtxvbVz.json index 5f79c177..1b3cae95 100644 --- a/src/packs/items/armors/armor_Leather_Armor_nibfdNtp2PtxvbVz.json +++ b/src/packs/items/armors/armor_Leather_Armor_nibfdNtp2PtxvbVz.json @@ -33,16 +33,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "3", - "key": "system.armorScore" + "max": "3" } ] }, - "_id": "bkEJ55HhIYFnX1Tz", + "_id": "2enPnnikOoG0oIZP", "disabled": false, "start": null, "duration": { @@ -63,7 +63,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!nibfdNtp2PtxvbVz.bkEJ55HhIYFnX1Tz" + "_key": "!items.effects!nibfdNtp2PtxvbVz.2enPnnikOoG0oIZP" } ], "sort": 0, 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 993b35cf..fd3d1f9f 100644 --- a/src/packs/items/armors/armor_Legendary_Chainmail_Armor_EsIN5OLKe9ZYFNXZ.json +++ b/src/packs/items/armors/armor_Legendary_Chainmail_Armor_EsIN5OLKe9ZYFNXZ.json @@ -71,16 +71,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "7", - "key": "system.armorScore" + "max": "7" } ] }, - "_id": "PZsaURELHOaRJK28", + "_id": "kqNdkD1d4FOQloMV", "disabled": false, "start": null, "duration": { @@ -101,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!EsIN5OLKe9ZYFNXZ.PZsaURELHOaRJK28" + "_key": "!items.effects!EsIN5OLKe9ZYFNXZ.kqNdkD1d4FOQloMV" } ], "sort": 0, 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 ba4eb949..a32a6be5 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 @@ -76,16 +76,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "7", - "key": "system.armorScore" + "max": "7" } ] }, - "_id": "IMPH2qFG7zXaxefg", + "_id": "kFxM3Et2bPzghJWm", "disabled": false, "start": null, "duration": { @@ -106,7 +106,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!SXWjUR2aUR6bYvdl.IMPH2qFG7zXaxefg" + "_key": "!items.effects!SXWjUR2aUR6bYvdl.kFxM3Et2bPzghJWm" } ], "sort": 0, 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 b0af0790..85fdb01b 100644 --- a/src/packs/items/armors/armor_Legendary_Gambeson_Armor_c6tMXz4rPf9ioQrf.json +++ b/src/packs/items/armors/armor_Legendary_Gambeson_Armor_c6tMXz4rPf9ioQrf.json @@ -71,16 +71,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "6", - "key": "system.armorScore" + "max": "6" } ] }, - "_id": "TtMaMntKKpcTU054", + "_id": "jdD0dJoh8gdGdh6W", "disabled": false, "start": null, "duration": { @@ -101,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!c6tMXz4rPf9ioQrf.TtMaMntKKpcTU054" + "_key": "!items.effects!c6tMXz4rPf9ioQrf.jdD0dJoh8gdGdh6W" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Legendary_Leather_Armor_Tptgl5WOj76TyFn7.json b/src/packs/items/armors/armor_Legendary_Leather_Armor_Tptgl5WOj76TyFn7.json index 6460fc02..7019a908 100644 --- a/src/packs/items/armors/armor_Legendary_Leather_Armor_Tptgl5WOj76TyFn7.json +++ b/src/packs/items/armors/armor_Legendary_Leather_Armor_Tptgl5WOj76TyFn7.json @@ -33,16 +33,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "6", - "key": "system.armorScore" + "max": "6" } ] }, - "_id": "UtbfSKO8hmJanog5", + "_id": "gP4PsefQLSTcBAQm", "disabled": false, "start": null, "duration": { @@ -63,7 +63,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!Tptgl5WOj76TyFn7.UtbfSKO8hmJanog5" + "_key": "!items.effects!Tptgl5WOj76TyFn7.gP4PsefQLSTcBAQm" } ], "sort": 0, 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 91ab3f80..464d6505 100644 --- a/src/packs/items/armors/armor_Monett_s_Cloak_AQzU2RsqS5V5bd1v.json +++ b/src/packs/items/armors/armor_Monett_s_Cloak_AQzU2RsqS5V5bd1v.json @@ -71,16 +71,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "6", - "key": "system.armorScore" + "max": "6" } ] }, - "_id": "kskfQTQTgCgmQR6b", + "_id": "xMJr6Zj9zZd7v5uD", "disabled": false, "start": null, "duration": { @@ -101,7 +101,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!AQzU2RsqS5V5bd1v.kskfQTQTgCgmQR6b" + "_key": "!items.effects!AQzU2RsqS5V5bd1v.xMJr6Zj9zZd7v5uD" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Rosewild_Armor_tN8kAeBvNKM3EBFo.json b/src/packs/items/armors/armor_Rosewild_Armor_tN8kAeBvNKM3EBFo.json index a7c2ab93..172e100b 100644 --- a/src/packs/items/armors/armor_Rosewild_Armor_tN8kAeBvNKM3EBFo.json +++ b/src/packs/items/armors/armor_Rosewild_Armor_tN8kAeBvNKM3EBFo.json @@ -63,16 +63,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "5", - "key": "system.armorScore" + "max": "5" } ] }, - "_id": "eT5j1FNPPQOdLO2Q", + "_id": "ebhSsuWrFYUVkGXC", "disabled": false, "start": null, "duration": { @@ -93,7 +93,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!tN8kAeBvNKM3EBFo.eT5j1FNPPQOdLO2Q" + "_key": "!items.effects!tN8kAeBvNKM3EBFo.ebhSsuWrFYUVkGXC" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Runes_of_Fortification_P4qAEDJUoNLgVRsA.json b/src/packs/items/armors/armor_Runes_of_Fortification_P4qAEDJUoNLgVRsA.json index c1beaad7..66a88157 100644 --- a/src/packs/items/armors/armor_Runes_of_Fortification_P4qAEDJUoNLgVRsA.json +++ b/src/packs/items/armors/armor_Runes_of_Fortification_P4qAEDJUoNLgVRsA.json @@ -70,16 +70,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "6", - "key": "system.armorScore" + "max": "6" } ] }, - "_id": "UmpUTOMR2UBmrAu6", + "_id": "pw8CD3IFNqb7530v", "disabled": false, "start": null, "duration": { @@ -100,7 +100,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!P4qAEDJUoNLgVRsA.UmpUTOMR2UBmrAu6" + "_key": "!items.effects!P4qAEDJUoNLgVRsA.pw8CD3IFNqb7530v" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Runetan_Floating_Armor_tHlBUDQC24YMZqd6.json b/src/packs/items/armors/armor_Runetan_Floating_Armor_tHlBUDQC24YMZqd6.json index a26eb694..3039c375 100644 --- a/src/packs/items/armors/armor_Runetan_Floating_Armor_tHlBUDQC24YMZqd6.json +++ b/src/packs/items/armors/armor_Runetan_Floating_Armor_tHlBUDQC24YMZqd6.json @@ -70,16 +70,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "4", - "key": "system.armorScore" + "max": "4" } ] }, - "_id": "s39jgXMmi4fDHuaE", + "_id": "Y4ZSoO0iGxLiNr80", "disabled": false, "start": null, "duration": { @@ -100,7 +100,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!tHlBUDQC24YMZqd6.s39jgXMmi4fDHuaE" + "_key": "!items.effects!tHlBUDQC24YMZqd6.Y4ZSoO0iGxLiNr80" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Savior_Chainmail_8X16lJQ3xltTwynm.json b/src/packs/items/armors/armor_Savior_Chainmail_8X16lJQ3xltTwynm.json index 371cf794..739f15d9 100644 --- a/src/packs/items/armors/armor_Savior_Chainmail_8X16lJQ3xltTwynm.json +++ b/src/packs/items/armors/armor_Savior_Chainmail_8X16lJQ3xltTwynm.json @@ -101,16 +101,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "8", - "key": "system.armorScore" + "max": "8" } ] }, - "_id": "SWXFcH4qbmPYI7WH", + "_id": "vNGSiFtcEBCz6jFQ", "disabled": false, "start": null, "duration": { @@ -131,7 +131,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!8X16lJQ3xltTwynm.SWXFcH4qbmPYI7WH" + "_key": "!items.effects!8X16lJQ3xltTwynm.vNGSiFtcEBCz6jFQ" } ], "sort": 0, 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 114b92f5..b8b12bc3 100644 --- a/src/packs/items/armors/armor_Spiked_Plate_Armor_QjwsIhXKqnlvRBMv.json +++ b/src/packs/items/armors/armor_Spiked_Plate_Armor_QjwsIhXKqnlvRBMv.json @@ -76,16 +76,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "5", - "key": "system.armorScore" + "max": "5" } ] }, - "_id": "OtOaxh7BCM2OMOmS", + "_id": "jfcv9NL2RtlfpECz", "disabled": false, "start": null, "duration": { @@ -106,7 +106,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!QjwsIhXKqnlvRBMv.OtOaxh7BCM2OMOmS" + "_key": "!items.effects!QjwsIhXKqnlvRBMv.jfcv9NL2RtlfpECz" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Tyris_Soft_Armor_PSW3BxCGmtLeWOxM.json b/src/packs/items/armors/armor_Tyris_Soft_Armor_PSW3BxCGmtLeWOxM.json index 45fea752..8be5ce42 100644 --- a/src/packs/items/armors/armor_Tyris_Soft_Armor_PSW3BxCGmtLeWOxM.json +++ b/src/packs/items/armors/armor_Tyris_Soft_Armor_PSW3BxCGmtLeWOxM.json @@ -63,16 +63,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "5", - "key": "system.armorScore" + "max": "5" } ] }, - "_id": "DgAQc09o3x6zn6DQ", + "_id": "E8iCCJSpPbCMostx", "disabled": false, "start": null, "duration": { @@ -93,7 +93,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!PSW3BxCGmtLeWOxM.DgAQc09o3x6zn6DQ" + "_key": "!items.effects!PSW3BxCGmtLeWOxM.E8iCCJSpPbCMostx" } ], "sort": 0, diff --git a/src/packs/items/armors/armor_Veritas_Opal_Armor_OvzgUTYy2RCN85vV.json b/src/packs/items/armors/armor_Veritas_Opal_Armor_OvzgUTYy2RCN85vV.json index e9f98300..301c1d25 100644 --- a/src/packs/items/armors/armor_Veritas_Opal_Armor_OvzgUTYy2RCN85vV.json +++ b/src/packs/items/armors/armor_Veritas_Opal_Armor_OvzgUTYy2RCN85vV.json @@ -63,16 +63,16 @@ "system": { "changes": [ { + "key": "system.armorScore", "type": "armor", "phase": "initial", "priority": 20, "value": 0, - "max": "6", - "key": "system.armorScore" + "max": "6" } ] }, - "_id": "2gMOVh1Hty0nVWy4", + "_id": "avGWSRMFFLbdJaYC", "disabled": false, "start": null, "duration": { @@ -93,7 +93,7 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!OvzgUTYy2RCN85vV.2gMOVh1Hty0nVWy4" + "_key": "!items.effects!OvzgUTYy2RCN85vV.avGWSRMFFLbdJaYC" } ], "sort": 0, diff --git a/src/packs/items/armors/folders_Special_tI3bfr6Sgi16Z7zm.json b/src/packs/items/armors/folders_Special_tI3bfr6Sgi16Z7zm.json new file mode 100644 index 00000000..65c4eca8 --- /dev/null +++ b/src/packs/items/armors/folders_Special_tI3bfr6Sgi16Z7zm.json @@ -0,0 +1,12 @@ +{ + "type": "Item", + "folder": null, + "name": "Special", + "color": null, + "sorting": "a", + "_id": "tI3bfr6Sgi16Z7zm", + "description": "", + "sort": 0, + "flags": {}, + "_key": "!folders!tI3bfr6Sgi16Z7zm" +} diff --git a/src/packs/items/weapons/weapon_Advanced_Round_Shield_hiEOGF2reabGLUoi.json b/src/packs/items/weapons/weapon_Advanced_Round_Shield_hiEOGF2reabGLUoi.json index c1c4fba5..549df4d8 100644 --- a/src/packs/items/weapons/weapon_Advanced_Round_Shield_hiEOGF2reabGLUoi.json +++ b/src/packs/items/weapons/weapon_Advanced_Round_Shield_hiEOGF2reabGLUoi.json @@ -113,26 +113,26 @@ "name": "Protective", "description": "

Add the item's Tier to your Armor Score

", "img": "icons/skills/melee/shield-block-gray-orange.webp", - "changes": [ - { - "key": "system.armorScore", - "mode": 2, - "value": "ITEM.@system.tier", - "priority": null - } - ], - "_id": "i5HfkF5aKQuUCTEG", - "type": "base", - "system": {}, + "_id": "7285CRGdZfHCEtT2", + "type": "armor", + "system": { + "changes": [ + { + "key": "system.armorScore", + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": "ITEM.@system.tier" + } + ] + }, "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 }, "origin": null, "tint": "#ffffff", @@ -143,7 +143,10 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!hiEOGF2reabGLUoi.i5HfkF5aKQuUCTEG" + "start": null, + "showIcon": 1, + "folder": null, + "_key": "!items.effects!hiEOGF2reabGLUoi.7285CRGdZfHCEtT2" } ], "sort": 0, diff --git a/src/packs/items/weapons/weapon_Advanced_Tower_Shield_OfOzQbs4hg6QbfTG.json b/src/packs/items/weapons/weapon_Advanced_Tower_Shield_OfOzQbs4hg6QbfTG.json index b2fb16d8..96ac78cb 100644 --- a/src/packs/items/weapons/weapon_Advanced_Tower_Shield_OfOzQbs4hg6QbfTG.json +++ b/src/packs/items/weapons/weapon_Advanced_Tower_Shield_OfOzQbs4hg6QbfTG.json @@ -113,25 +113,25 @@ "name": "Barrier", "description": "Gain Weapon Tier + 1 to Armor Score; -1 to Evasion", "img": "icons/skills/melee/shield-block-bash-blue.webp", - "changes": [ - { - "key": "system.armorScore", - "mode": 2, - "value": "ITEM.@system.tier + 1" - }, - { - "key": "system.evasion", - "mode": 2, - "value": "-1" - } - ], "_id": "87gedjJZGdFY81Mt", "type": "base", - "system": {}, + "system": { + "changes": [ + { + "key": "system.evasion", + "type": "add", + "value": -1, + "phase": "initial", + "priority": 0 + } + ] + }, "disabled": false, "duration": { - "startTime": null, - "combat": null + "value": null, + "units": "seconds", + "expiry": null, + "expired": false }, "origin": null, "tint": "#ffffff", @@ -142,7 +142,49 @@ "_stats": { "compendiumSource": null }, + "start": null, + "showIcon": 1, + "folder": null, "_key": "!items.effects!OfOzQbs4hg6QbfTG.87gedjJZGdFY81Mt" + }, + { + "name": "Barrier", + "description": "Gain Weapon Tier + 1 to Armor Score; -1 to Evasion", + "img": "icons/skills/melee/shield-block-bash-blue.webp", + "_id": "J0f7zqqOr61ADpdy", + "type": "armor", + "system": { + "changes": [ + { + "key": "system.armorScore", + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": "ITEM.@system.tier + 1" + } + ] + }, + "disabled": false, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "start": null, + "showIcon": 1, + "folder": null, + "_key": "!items.effects!OfOzQbs4hg6QbfTG.J0f7zqqOr61ADpdy" } ], "sort": 0, diff --git a/src/packs/items/weapons/weapon_Improved_Round_Shield_DlinEBGZfIlvreO3.json b/src/packs/items/weapons/weapon_Improved_Round_Shield_DlinEBGZfIlvreO3.json index 53a8e9b6..fcd3caed 100644 --- a/src/packs/items/weapons/weapon_Improved_Round_Shield_DlinEBGZfIlvreO3.json +++ b/src/packs/items/weapons/weapon_Improved_Round_Shield_DlinEBGZfIlvreO3.json @@ -113,26 +113,26 @@ "name": "Protective", "description": "

Add the item's Tier to your Armor Score

", "img": "icons/skills/melee/shield-block-gray-orange.webp", - "changes": [ - { - "key": "system.armorScore", - "mode": 2, - "value": "ITEM.@system.tier", - "priority": null - } - ], - "_id": "cXWSV50apzaNQkdA", - "type": "base", - "system": {}, + "_id": "pZCrWd7zLTarvEQK", + "type": "armor", + "system": { + "changes": [ + { + "key": "system.armorScore", + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": "ITEM.@system.tier" + } + ] + }, "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 }, "origin": null, "tint": "#ffffff", @@ -143,7 +143,10 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!DlinEBGZfIlvreO3.cXWSV50apzaNQkdA" + "start": null, + "showIcon": 1, + "folder": null, + "_key": "!items.effects!DlinEBGZfIlvreO3.pZCrWd7zLTarvEQK" } ], "sort": 0, diff --git a/src/packs/items/weapons/weapon_Improved_Tower_Shield_bxt3NsbMqTSdI5ab.json b/src/packs/items/weapons/weapon_Improved_Tower_Shield_bxt3NsbMqTSdI5ab.json index 839d4352..b1b4279f 100644 --- a/src/packs/items/weapons/weapon_Improved_Tower_Shield_bxt3NsbMqTSdI5ab.json +++ b/src/packs/items/weapons/weapon_Improved_Tower_Shield_bxt3NsbMqTSdI5ab.json @@ -113,25 +113,25 @@ "name": "Barrier", "description": "Gain Weapon Tier + 1 to Armor Score; -1 to Evasion", "img": "icons/skills/melee/shield-block-bash-blue.webp", - "changes": [ - { - "key": "system.armorScore", - "mode": 2, - "value": "ITEM.@system.tier + 1" - }, - { - "key": "system.evasion", - "mode": 2, - "value": "-1" - } - ], "_id": "tkNEA1PO3jEFhKCa", "type": "base", - "system": {}, + "system": { + "changes": [ + { + "key": "system.evasion", + "type": "add", + "value": -1, + "phase": "initial", + "priority": 0 + } + ] + }, "disabled": false, "duration": { - "startTime": null, - "combat": null + "value": null, + "units": "seconds", + "expiry": null, + "expired": false }, "origin": null, "tint": "#ffffff", @@ -142,7 +142,49 @@ "_stats": { "compendiumSource": null }, + "start": null, + "showIcon": 1, + "folder": null, "_key": "!items.effects!bxt3NsbMqTSdI5ab.tkNEA1PO3jEFhKCa" + }, + { + "name": "Barrier", + "description": "Gain Weapon Tier + 1 to Armor Score; -1 to Evasion", + "img": "icons/skills/melee/shield-block-bash-blue.webp", + "_id": "XugJeHJdnC6IymSa", + "type": "armor", + "system": { + "changes": [ + { + "key": "system.armorScore", + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": "ITEM.@system.tier + 1" + } + ] + }, + "disabled": false, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "start": null, + "showIcon": 1, + "folder": null, + "_key": "!items.effects!bxt3NsbMqTSdI5ab.XugJeHJdnC6IymSa" } ], "sort": 0, diff --git a/src/packs/items/weapons/weapon_Labrys_Axe_ijWppQzSOqVCb3rE.json b/src/packs/items/weapons/weapon_Labrys_Axe_ijWppQzSOqVCb3rE.json index 4958bbe5..2272926c 100644 --- a/src/packs/items/weapons/weapon_Labrys_Axe_ijWppQzSOqVCb3rE.json +++ b/src/packs/items/weapons/weapon_Labrys_Axe_ijWppQzSOqVCb3rE.json @@ -113,20 +113,26 @@ "name": "Protective", "description": "Add your character's Tier to your Armor Score", "img": "icons/skills/melee/shield-block-gray-orange.webp", - "changes": [ - { - "key": "system.armorScore", - "mode": 2, - "value": "ITEM.@system.tier" - } - ], - "_id": "qTxADRsQnKiYfOiQ", - "type": "base", - "system": {}, + "_id": "vnR4Zhnb0rOqwrFw", + "type": "armor", + "system": { + "changes": [ + { + "key": "system.armorScore", + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": "ITEM.@system.tier" + } + ] + }, "disabled": false, "duration": { - "startTime": null, - "combat": null + "value": null, + "units": "seconds", + "expiry": null, + "expired": false }, "origin": null, "tint": "#ffffff", @@ -137,7 +143,10 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!ijWppQzSOqVCb3rE.qTxADRsQnKiYfOiQ" + "start": null, + "showIcon": 1, + "folder": null, + "_key": "!items.effects!ijWppQzSOqVCb3rE.vnR4Zhnb0rOqwrFw" } ], "sort": 0, diff --git a/src/packs/items/weapons/weapon_Legendary_Round_Shield_A28WL9E2lJ3iLZHW.json b/src/packs/items/weapons/weapon_Legendary_Round_Shield_A28WL9E2lJ3iLZHW.json index c7b18355..81045ea3 100644 --- a/src/packs/items/weapons/weapon_Legendary_Round_Shield_A28WL9E2lJ3iLZHW.json +++ b/src/packs/items/weapons/weapon_Legendary_Round_Shield_A28WL9E2lJ3iLZHW.json @@ -113,26 +113,26 @@ "name": "Protective", "description": "

Add the item's Tier to your Armor Score

", "img": "icons/skills/melee/shield-block-gray-orange.webp", - "changes": [ - { - "key": "system.armorScore", - "mode": 2, - "value": "ITEM.@system.tier", - "priority": null - } - ], - "_id": "Z2p00q5h6x6seXys", - "type": "base", - "system": {}, + "_id": "EixxJrRHyc6kj3Wg", + "type": "armor", + "system": { + "changes": [ + { + "key": "system.armorScore", + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": "ITEM.@system.tier" + } + ] + }, "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 }, "origin": null, "tint": "#ffffff", @@ -143,7 +143,10 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!A28WL9E2lJ3iLZHW.Z2p00q5h6x6seXys" + "start": null, + "showIcon": 1, + "folder": null, + "_key": "!items.effects!A28WL9E2lJ3iLZHW.EixxJrRHyc6kj3Wg" } ], "sort": 0, diff --git a/src/packs/items/weapons/weapon_Legendary_Tower_Shield_MaJIROht7A9LxIZx.json b/src/packs/items/weapons/weapon_Legendary_Tower_Shield_MaJIROht7A9LxIZx.json index 47e707d3..6ae5d720 100644 --- a/src/packs/items/weapons/weapon_Legendary_Tower_Shield_MaJIROht7A9LxIZx.json +++ b/src/packs/items/weapons/weapon_Legendary_Tower_Shield_MaJIROht7A9LxIZx.json @@ -113,25 +113,25 @@ "name": "Barrier", "description": "Gain Weapon Tier + 1 to Armor Score; -1 to Evasion", "img": "icons/skills/melee/shield-block-bash-blue.webp", - "changes": [ - { - "key": "system.armorScore", - "mode": 2, - "value": "ITEM.@system.tier + 1" - }, - { - "key": "system.evasion", - "mode": 2, - "value": "-1" - } - ], "_id": "lBJMzxdGO2nJdTQS", "type": "base", - "system": {}, + "system": { + "changes": [ + { + "key": "system.evasion", + "type": "add", + "value": -1, + "phase": "initial", + "priority": 0 + } + ] + }, "disabled": false, "duration": { - "startTime": null, - "combat": null + "value": null, + "units": "seconds", + "expiry": null, + "expired": false }, "origin": null, "tint": "#ffffff", @@ -142,7 +142,49 @@ "_stats": { "compendiumSource": null }, + "start": null, + "showIcon": 1, + "folder": null, "_key": "!items.effects!MaJIROht7A9LxIZx.lBJMzxdGO2nJdTQS" + }, + { + "name": "Barrier", + "description": "Gain Weapon Tier + 1 to Armor Score; -1 to Evasion", + "img": "icons/skills/melee/shield-block-bash-blue.webp", + "_id": "1fgUIaXl6VQrhP7j", + "type": "armor", + "system": { + "changes": [ + { + "key": "system.armorScore", + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": "ITEM.@system.tier + 1" + } + ] + }, + "disabled": false, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "start": null, + "showIcon": 1, + "folder": null, + "_key": "!items.effects!MaJIROht7A9LxIZx.1fgUIaXl6VQrhP7j" } ], "sort": 0, diff --git a/src/packs/items/weapons/weapon_Round_Shield_mxwWKDujgsRcZWPT.json b/src/packs/items/weapons/weapon_Round_Shield_mxwWKDujgsRcZWPT.json index 47b096af..d16c550c 100644 --- a/src/packs/items/weapons/weapon_Round_Shield_mxwWKDujgsRcZWPT.json +++ b/src/packs/items/weapons/weapon_Round_Shield_mxwWKDujgsRcZWPT.json @@ -113,26 +113,26 @@ "name": "Protective", "description": "

Add the item's Tier to your Armor Score

", "img": "icons/skills/melee/shield-block-gray-orange.webp", - "changes": [ - { - "key": "system.armorScore", - "mode": 2, - "value": "ITEM.@system.tier", - "priority": null - } - ], - "_id": "M70a81e0Mg66jHRL", - "type": "base", - "system": {}, + "_id": "eV4lFIpQMiKERj4U", + "type": "armor", + "system": { + "changes": [ + { + "key": "system.armorScore", + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": "ITEM.@system.tier" + } + ] + }, "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 }, "origin": null, "tint": "#ffffff", @@ -143,7 +143,10 @@ "_stats": { "compendiumSource": null }, - "_key": "!items.effects!mxwWKDujgsRcZWPT.M70a81e0Mg66jHRL" + "start": null, + "showIcon": 1, + "folder": null, + "_key": "!items.effects!mxwWKDujgsRcZWPT.eV4lFIpQMiKERj4U" } ], "sort": 0, diff --git a/src/packs/items/weapons/weapon_Spiked_Shield_vzyzFwLUniWZV1rt.json b/src/packs/items/weapons/weapon_Spiked_Shield_vzyzFwLUniWZV1rt.json index edadecf9..40e451bd 100644 --- a/src/packs/items/weapons/weapon_Spiked_Shield_vzyzFwLUniWZV1rt.json +++ b/src/packs/items/weapons/weapon_Spiked_Shield_vzyzFwLUniWZV1rt.json @@ -113,32 +113,31 @@ "name": "Double Duty", "description": "+1 to Armor Score; +1 to primary weapon damage within Melee range", "img": "icons/skills/melee/sword-shield-stylized-white.webp", - "changes": [ - { - "key": "system.armorScore", - "mode": 2, - "value": "1" - }, - { - "key": "system.bonuses.damage.primaryWeapon.bonus", - "mode": 2, - "value": "1" - } - ], "system": { "rangeDependence": { "enabled": true, "range": "melee", "target": "hostile", "type": "withinRange" - } + }, + "changes": [ + { + "key": "system.bonuses.damage.primaryWeapon.bonus", + "type": "add", + "value": 1, + "phase": "initial", + "priority": 0 + } + ] }, "_id": "d3TJtlpoHBCztbom", "type": "base", "disabled": false, "duration": { - "startTime": null, - "combat": null + "value": null, + "units": "seconds", + "expiry": null, + "expired": false }, "origin": null, "tint": "#ffffff", @@ -149,7 +148,49 @@ "_stats": { "compendiumSource": null }, + "start": null, + "showIcon": 1, + "folder": null, "_key": "!items.effects!vzyzFwLUniWZV1rt.d3TJtlpoHBCztbom" + }, + { + "name": "Double Duty", + "description": "+1 to Armor Score; +1 to primary weapon damage within Melee range", + "img": "icons/skills/melee/sword-shield-stylized-white.webp", + "system": { + "changes": [ + { + "key": "system.armorScore", + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": "1" + } + ] + }, + "_id": "mvUY9LGfwICak7cE", + "type": "armor", + "disabled": false, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "start": null, + "showIcon": 1, + "folder": null, + "_key": "!items.effects!vzyzFwLUniWZV1rt.mvUY9LGfwICak7cE" } ], "sort": 0, diff --git a/src/packs/items/weapons/weapon_Tower_Shield_C9aWpK1shVMWP4m5.json b/src/packs/items/weapons/weapon_Tower_Shield_C9aWpK1shVMWP4m5.json index d49b7de7..9e3e9968 100644 --- a/src/packs/items/weapons/weapon_Tower_Shield_C9aWpK1shVMWP4m5.json +++ b/src/packs/items/weapons/weapon_Tower_Shield_C9aWpK1shVMWP4m5.json @@ -113,25 +113,25 @@ "name": "Barrier", "description": "Gain Weapon Tier + 1 to Armor Score; -1 to Evasion", "img": "icons/skills/melee/shield-block-bash-blue.webp", - "changes": [ - { - "key": "system.armorScore", - "mode": 2, - "value": "ITEM.@system.tier + 1" - }, - { - "key": "system.evasion", - "mode": 2, - "value": "-1" - } - ], "_id": "8r0TcKWXboFV0eqS", "type": "base", - "system": {}, + "system": { + "changes": [ + { + "key": "system.evasion", + "type": "add", + "value": -1, + "phase": "initial", + "priority": 0 + } + ] + }, "disabled": false, "duration": { - "startTime": null, - "combat": null + "value": null, + "units": "seconds", + "expiry": null, + "expired": false }, "origin": null, "tint": "#ffffff", @@ -142,7 +142,49 @@ "_stats": { "compendiumSource": null }, + "start": null, + "showIcon": 1, + "folder": null, "_key": "!items.effects!C9aWpK1shVMWP4m5.8r0TcKWXboFV0eqS" + }, + { + "name": "Barrier", + "description": "Gain Weapon Tier + 1 to Armor Score; -1 to Evasion", + "img": "icons/skills/melee/shield-block-bash-blue.webp", + "_id": "tLRc4UAnGuIq7er3", + "type": "armor", + "system": { + "changes": [ + { + "key": "system.armorScore", + "type": "armor", + "phase": "initial", + "priority": 20, + "value": 0, + "max": "ITEM.@system.tier + 1" + } + ] + }, + "disabled": false, + "duration": { + "value": null, + "units": "seconds", + "expiry": null, + "expired": false + }, + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "start": null, + "showIcon": 1, + "folder": null, + "_key": "!items.effects!C9aWpK1shVMWP4m5.tLRc4UAnGuIq7er3" } ], "sort": 0, From 10baf0ba105dbf70d10f8837abb197d4bd123891 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Wed, 11 Feb 2026 17:00:58 +0100 Subject: [PATCH 20/23] Added ArmoreEffect.armorInteraction option --- lang/en.json | 13 ++++++++++- .../armorActiveEffectConfig.mjs | 1 + module/config/generalConfig.mjs | 6 +++++ module/data/activeEffect/armorEffect.mjs | 22 ++++++++++++++++++- module/documents/activeEffect.mjs | 2 ++ .../domainCard_Armorer_cy8GjBPGc9w9RaGO.json | 5 +++-- ...omainCard_Bare_Bones_l5D9kq901JDESaXw.json | 3 ++- .../sheets/activeEffects/armorEffects.less | 15 ++----------- .../sheets/activeEffect/armor/settings.hbs | 19 +++++++++++----- 9 files changed, 62 insertions(+), 24 deletions(-) diff --git a/lang/en.json b/lang/en.json index 14218311..9f528c14 100755 --- a/lang/en.json +++ b/lang/en.json @@ -751,6 +751,11 @@ "bruiser": "for each Bruiser adversary.", "solo": "for each Solo adversary." }, + "ArmorInteraction": { + "none": { "label": "Ignores Armor" }, + "active": { "label": "Only Active With Armor" }, + "inactive": { "label": "Only Active Without Armor" } + }, "ArmorFeature": { "burning": { "name": "Burning", @@ -1851,7 +1856,13 @@ "transferHint": "If checked, this effect will be applied to any actor that owns this Effect's parent Item. The effect is always applied if this Item is attached to another one." }, "Armor": { - "newArmorEffect": "Armor Effect" + "newArmorEffect": "Armor Effect", + "FIELDS": { + "armorInteraction": { + "label": "Armor Interaction", + "hint": "Does the character wearing armor suppress this effect?" + } + } } }, "GENERAL": { diff --git a/module/applications/sheets-configs/armorActiveEffectConfig.mjs b/module/applications/sheets-configs/armorActiveEffectConfig.mjs index 7d8070e0..3dca8ef1 100644 --- a/module/applications/sheets-configs/armorActiveEffectConfig.mjs +++ b/module/applications/sheets-configs/armorActiveEffectConfig.mjs @@ -9,6 +9,7 @@ export default class ArmorActiveEffectConfig extends HandlebarsApplicationMixin( submitOnChange: true, closeOnSubmit: false }, + position: { width: 560 }, actions: { finish: ArmorActiveEffectConfig.#finish } diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index 80ac546e..b2833580 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -890,3 +890,9 @@ export const activeEffectModes = { label: 'EFFECT.CHANGES.TYPES.override' } }; + +export const activeEffectArmorInteraction = { + none: { id: 'none', label: 'DAGGERHEART.CONFIG.ArmorInteraction.none.label' }, + active: { id: 'active', label: 'DAGGERHEART.CONFIG.ArmorInteraction.active.label' }, + inactive: { id: 'inactive', label: 'DAGGERHEART.CONFIG.ArmorInteraction.inactive.label' } +}; diff --git a/module/data/activeEffect/armorEffect.mjs b/module/data/activeEffect/armorEffect.mjs index d32d24b2..894bf3ff 100644 --- a/module/data/activeEffect/armorEffect.mjs +++ b/module/data/activeEffect/armorEffect.mjs @@ -51,10 +51,30 @@ export default class ArmorEffect extends foundry.data.ActiveEffectTypeDataModel } ] } - ) + ), + armorInteraction: new fields.StringField({ + required: true, + choices: CONFIG.DH.GENERAL.activeEffectArmorInteraction, + initial: CONFIG.DH.GENERAL.activeEffectArmorInteraction.none.id, + label: 'DAGGERHEART.EFFECTS.Armor.FIELDS.armorInteraction.label', + hint: 'DAGGERHEART.EFFECTS.Armor.FIELDS.armorInteraction.hint' + }) }; } + get isSuppressed() { + if (this.parent.actor?.type !== 'character') return false; + + switch (this.armorInteraction) { + case CONFIG.DH.GENERAL.activeEffectArmorInteraction.active.id: + return !this.parent.actor.system.armor; + case CONFIG.DH.GENERAL.activeEffectArmorInteraction.inactive.id: + return Boolean(this.parent.actor.system.armor); + default: + return false; + } + } + /* Type Functions */ /** diff --git a/module/documents/activeEffect.mjs b/module/documents/activeEffect.mjs index 055267b2..56981c52 100644 --- a/module/documents/activeEffect.mjs +++ b/module/documents/activeEffect.mjs @@ -8,6 +8,8 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect { /**@override */ get isSuppressed() { + if (this.system.isSuppressed === true) return true; + // If this is a copied effect from an attachment, never suppress it // (These effects have attachmentSource metadata) if (this.flags?.daggerheart?.attachmentSource) { diff --git a/src/packs/domains/domainCard_Armorer_cy8GjBPGc9w9RaGO.json b/src/packs/domains/domainCard_Armorer_cy8GjBPGc9w9RaGO.json index c06a34ab..096be253 100644 --- a/src/packs/domains/domainCard_Armorer_cy8GjBPGc9w9RaGO.json +++ b/src/packs/domains/domainCard_Armorer_cy8GjBPGc9w9RaGO.json @@ -101,7 +101,8 @@ "value": 0, "max": "1" } - ] + ], + "armorInteraction": "active" }, "_id": "tJw2JIPcT9hEMRXg", "img": "icons/tools/hand/hammer-and-nail.webp", @@ -112,7 +113,7 @@ "expiry": null, "expired": false }, - "description": "

While you’re wearing armor, gain a +1 bonus to your Armor Score.

", + "description": "

While you’re wearing armor, gain a +1 bonus to your Armor Score.

", "origin": null, "tint": "#ffffff", "transfer": true, diff --git a/src/packs/domains/domainCard_Bare_Bones_l5D9kq901JDESaXw.json b/src/packs/domains/domainCard_Bare_Bones_l5D9kq901JDESaXw.json index 7e0129d7..40af13a7 100644 --- a/src/packs/domains/domainCard_Bare_Bones_l5D9kq901JDESaXw.json +++ b/src/packs/domains/domainCard_Bare_Bones_l5D9kq901JDESaXw.json @@ -33,7 +33,8 @@ "phase": "initial", "priority": 20 } - ] + ], + "armorInteraction": "inactive" }, "_id": "FCsgz7Tdsw6QUzBs", "img": "icons/magic/control/buff-strength-muscle-damage-orange.webp", diff --git a/styles/less/sheets/activeEffects/armorEffects.less b/styles/less/sheets/activeEffects/armorEffects.less index 9756edce..fd5c89b1 100644 --- a/styles/less/sheets/activeEffects/armorEffects.less +++ b/styles/less/sheets/activeEffects/armorEffects.less @@ -1,16 +1,5 @@ .application.sheet.daggerheart.dh-style.armor-effect-config { - .armor-effects-container { - display: flex; - flex-direction: column; - gap: 8px; - - .armor-effect-container { - display: flex; - gap: 4px; - - * { - flex: 1; - } - } + .tab-form-footer { + margin-top: 8px; } } diff --git a/templates/sheets/activeEffect/armor/settings.hbs b/templates/sheets/activeEffect/armor/settings.hbs index c0f77b35..12ca2154 100644 --- a/templates/sheets/activeEffect/armor/settings.hbs +++ b/templates/sheets/activeEffect/armor/settings.hbs @@ -1,10 +1,17 @@
-
- {{#each source.system.changes as |change index|}} -
+
+ {{localize "Armor"}} +
+ {{#each source.system.changes as |change index|}} {{formGroup @root.systemFields.changes.element.fields.value name=(concat 'system.changes.' index '.value') value=change.value localize=true}} {{formGroup @root.systemFields.changes.element.fields.max name=(concat 'system.changes.' index '.max') value=change.max localize=true}} -
- {{/each}} -
+ {{/each}} +
+ + +
+ {{localize "DAGGERHEART.GENERAL.general"}} + + {{formGroup @root.systemFields.armorInteraction name="system.armorInteraction" value=source.system.armorInteraction localize=true}} +
\ No newline at end of file From 3b011e258002c3ac71927f75b5394159ac15ed2c Mon Sep 17 00:00:00 2001 From: WBHarry Date: Fri, 13 Feb 2026 15:36:13 +0100 Subject: [PATCH 21/23] Added ArmorManagement menu --- .../applications/sheets/actors/character.mjs | 74 ++++++++++++++++++- .../less/sheets/actors/character/sidebar.less | 9 +++ styles/less/ux/index.less | 1 + templates/sheets/actors/character/sidebar.hbs | 3 +- 4 files changed, 85 insertions(+), 2 deletions(-) diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 9ba52000..c9a6047b 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -34,7 +34,8 @@ export default class CharacterSheet extends DHBaseActorSheet { advanceResourceDie: CharacterSheet.#advanceResourceDie, cancelBeastform: CharacterSheet.#cancelBeastform, useDowntime: this.useDowntime, - viewParty: CharacterSheet.#viewParty + viewParty: CharacterSheet.#viewParty, + toggleArmorMangement: CharacterSheet.#toggleArmorManagement }, window: { resizable: true, @@ -945,6 +946,77 @@ export default class CharacterSheet extends DHBaseActorSheet { }); } + static async #toggleArmorManagement(_event, target) { + const existingTooltip = document.body.querySelector('.locked-tooltip .armor-management-container'); + if (existingTooltip) { + game.tooltip.dismissLockedTooltips(); + return; + } + + const armorSources = []; + for (var effect of Array.from(this.document.allApplicableEffects())) { + const origin = effect.origin ? await foundry.utils.fromUuid(effect.origin) : effect.parent; + if (effect.type !== 'armor' || effect.disabled || effect.isSuppressed) continue; + armorSources.push({ + uuid: effect.uuid, + name: origin.name, + ...effect.system.armorData + }); + } + + if (armorSources.length <= 1) return; + + const html = document.createElement('div'); + html.innerHTML = await foundry.applications.handlebars.renderTemplate( + `systems/daggerheart/templates/ui/tooltip/armorManagement.hbs`, + { + sources: armorSources + } + ); + + game.tooltip.dismissLockedTooltips(); + game.tooltip.activate(target, { + html, + locked: true, + cssClass: 'bordered-tooltip', + direction: 'DOWN' + }); + + html.querySelectorAll('.armor-marks-input').forEach(element => { + element.addEventListener('blur', CharacterSheet.armorSourceUpdate); + element.addEventListener('input', CharacterSheet.armorSourceInput); + }); + } + + static async armorSourceInput(event) { + const effect = await foundry.utils.fromUuid(event.target.dataset.uuid); + const value = Math.max(Math.min(Number.parseInt(event.target.value), effect.system.armorData.max), 0); + event.target.value = value; + const progressBar = event.target.closest('.status-bar.armor-slots').querySelector('progress'); + progressBar.value = value; + } + + /** Update specific armor source */ + static async armorSourceUpdate(event) { + const effect = await foundry.utils.fromUuid(event.target.dataset.uuid); + if (effect.system.changes.length !== 1) return; + const armorEffect = effect.system.changes[0]; + const value = Math.max(Math.min(Number.parseInt(event.target.value), effect.system.armorData.max), 0); + + const newChanges = [ + { + ...armorEffect, + value + } + ]; + + event.target.value = value; + const progressBar = event.target.closest('.status-bar.armor-slots').querySelector('progress'); + progressBar.value = value; + + await effect.update({ 'system.changes': newChanges }); + } + /** * Open the downtime application. * @type {ApplicationClickAction} diff --git a/styles/less/sheets/actors/character/sidebar.less b/styles/less/sheets/actors/character/sidebar.less index 04baf2b9..42e40386 100644 --- a/styles/less/sheets/actors/character/sidebar.less +++ b/styles/less/sheets/actors/character/sidebar.less @@ -298,6 +298,10 @@ border-radius: 3px; background: light-dark(@dark-blue, @golden); clip-path: none; + cursor: pointer; + display: flex; + align-items: center; + gap: 4px; h4 { font-weight: bold; @@ -306,6 +310,11 @@ color: light-dark(@beige, @dark-blue); font-size: var(--font-size-12); } + + i { + font-size: 12px; + color: light-dark(@beige, @dark-blue); + } } .slot-value { position: absolute; diff --git a/styles/less/ux/index.less b/styles/less/ux/index.less index 0bd1b71e..abdb9bc6 100644 --- a/styles/less/ux/index.less +++ b/styles/less/ux/index.less @@ -1,4 +1,5 @@ @import './tooltip/tooltip.less'; +@import './tooltip/armorManagement.less'; @import './tooltip/battlepoints.less'; @import './tooltip/bordered-tooltip.less'; @import './tooltip/domain-cards.less'; diff --git a/templates/sheets/actors/character/sidebar.hbs b/templates/sheets/actors/character/sidebar.hbs index 523c3974..10017343 100644 --- a/templates/sheets/actors/character/sidebar.hbs +++ b/templates/sheets/actors/character/sidebar.hbs @@ -61,8 +61,9 @@ value='{{document.system.armorScore.value}}' max='{{document.system.armorScore.max}}' > -
+

{{localize "DAGGERHEART.GENERAL.armorSlots"}}

+
{{/if}}
From 8f0b1f6ed31dafa921e9a4b285d7e1b057f536ec Mon Sep 17 00:00:00 2001 From: WBHarry Date: Fri, 13 Feb 2026 23:01:55 +0100 Subject: [PATCH 22/23] Fixed DamageReductionDialog --- lang/en.json | 2 +- .../dialogs/damageReductionDialog.mjs | 134 +++++++++++++----- module/data/actor/character.mjs | 12 ++ module/documents/actor.mjs | 11 +- .../damage-reduction-container.less | 26 +++- templates/dialogs/damageReduction.hbs | 56 ++++---- 6 files changed, 172 insertions(+), 69 deletions(-) diff --git a/lang/en.json b/lang/en.json index 54c70ae9..59b058ab 100755 --- a/lang/en.json +++ b/lang/en.json @@ -437,7 +437,7 @@ "text": "Are you sure you want to delete {name}?" }, "DamageReduction": { - "armorMarks": "Armor Marks", + "maxUseableArmor": "Useable Armor Slots", "armorWithStress": "Spend 1 stress to use an extra mark", "thresholdImmunities": "Threshold Immunities", "stress": "Stress", diff --git a/module/applications/dialogs/damageReductionDialog.mjs b/module/applications/dialogs/damageReductionDialog.mjs index d4a2b4d3..31e4f72d 100644 --- a/module/applications/dialogs/damageReductionDialog.mjs +++ b/module/applications/dialogs/damageReductionDialog.mjs @@ -10,7 +10,8 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap this.reject = reject; this.actor = actor; this.damage = damage; - this.damageType = damageType; + // this.damageType = damageType; + this.damageType = ['physical']; this.rulesDefault = game.settings.get( CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation @@ -20,14 +21,25 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap this.rulesDefault ); - const canApplyArmor = damageType.every(t => actor.system.armorApplicableDamageTypes[t] === true); - const availableArmor = actor.system.armorScore.max - actor.system.armorScore.value; - const maxArmorMarks = canApplyArmor ? availableArmor : 0; + const allArmorEffects = Array.from(actor.allApplicableEffects()).filter(x => x.type === 'armor'); + const orderedArmorEffects = game.system.api.data.activeEffects.ArmorEffect.orderEffectsForAutoChange( + allArmorEffects, + true + ); + const armor = orderedArmorEffects.reduce((acc, effect) => { + if (effect.type !== 'armor') return acc; + const { value, max } = effect.system.armorData; + acc.push({ + effect: effect, + marks: [...Array(max).keys()].reduce((acc, _, index) => { + const spent = index < value; + acc[foundry.utils.randomID()] = { selected: false, disabled: spent, spent }; + return acc; + }, {}) + }); - const armor = [...Array(maxArmorMarks).keys()].reduce((acc, _) => { - acc[foundry.utils.randomID()] = { selected: false }; return acc; - }, {}); + }, []); const stress = [...Array(actor.system.rules.damageReduction.maxArmorMarked.stressExtra ?? 0).keys()].reduce( (acc, _) => { acc[foundry.utils.randomID()] = { selected: false }; @@ -121,13 +133,11 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap context.thresholdImmunities = Object.keys(this.thresholdImmunities).length > 0 ? this.thresholdImmunities : null; - const { selectedArmorMarks, selectedStressMarks, stressReductions, currentMarks, currentDamage } = + const { selectedStressMarks, stressReductions, currentMarks, currentDamage, maxArmorUsed, availableArmor } = this.getDamageInfo(); context.armorScore = this.actor.system.armorScore.max; context.armorMarks = currentMarks; - context.basicMarksUsed = - selectedArmorMarks.length === this.actor.system.rules.damageReduction.maxArmorMarked.value; const stressReductionStress = this.availableStressReductions ? stressReductions.reduce((acc, red) => acc + red.cost, 0) @@ -141,16 +151,27 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap } : null; - const maxArmor = this.actor.system.rules.damageReduction.maxArmorMarked.value; - context.marks = { - armor: Object.keys(this.marks.armor).reduce((acc, key, index) => { - const mark = this.marks.armor[key]; - if (!this.rulesOn || index + 1 <= maxArmor) acc[key] = mark; + context.maxArmorUsed = maxArmorUsed; + context.availableArmor = availableArmor; + context.basicMarksUsed = availableArmor === 0 || selectedStressMarks.length; - return acc; - }, {}), + const armorSources = []; + for (const source of this.marks.armor) { + const parent = source.effect.origin + ? await foundry.utils.fromUuid(source.effect.origin) + : source.effect.parent; + armorSources.push({ + label: parent.name, + uuid: source.effect.uuid, + marks: source.marks + }); + } + context.marks = { + armor: armorSources, stress: this.marks.stress }; + + context.usesStressArmor = Object.keys(context.marks.stress).length; context.availableStressReductions = this.availableStressReductions; context.damage = getDamageLabel(this.damage); @@ -167,27 +188,31 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap } getDamageInfo = () => { - const selectedArmorMarks = Object.values(this.marks.armor).filter(x => x.selected); + const selectedArmorMarks = this.marks.armor.flatMap(x => Object.values(x.marks).filter(x => x.selected)); const selectedStressMarks = Object.values(this.marks.stress).filter(x => x.selected); const stressReductions = this.availableStressReductions ? Object.values(this.availableStressReductions).filter(red => red.selected) : []; - const currentMarks = - this.actor.system.armorScore.value + selectedArmorMarks.length + selectedStressMarks.length; + const currentMarks = this.actor.system.armorScore.value + selectedArmorMarks.length; + + const maxArmorUsed = this.actor.system.rules.damageReduction.maxArmorMarked.value + selectedStressMarks.length; + const availableArmor = + maxArmorUsed - + this.marks.armor.reduce((acc, source) => { + acc += Object.values(source.marks).filter(x => x.selected).length; + return acc; + }, 0); const armorMarkReduction = selectedArmorMarks.length * this.actor.system.rules.damageReduction.increasePerArmorMark; - let currentDamage = Math.max( - this.damage - armorMarkReduction - selectedStressMarks.length - stressReductions.length, - 0 - ); + let currentDamage = Math.max(this.damage - armorMarkReduction - stressReductions.length, 0); if (this.reduceSeverity) { currentDamage = Math.max(currentDamage - this.reduceSeverity, 0); } if (this.thresholdImmunities[currentDamage]) currentDamage = 0; - return { selectedArmorMarks, selectedStressMarks, stressReductions, currentMarks, currentDamage }; + return { selectedStressMarks, stressReductions, currentMarks, currentDamage, maxArmorUsed, availableArmor }; }; static toggleRules() { @@ -209,8 +234,8 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap } static setMarks(_, target) { - const currentMark = this.marks[target.dataset.type][target.dataset.key]; - const { selectedStressMarks, stressReductions, currentMarks, currentDamage } = this.getDamageInfo(); + const currentMark = foundry.utils.getProperty(this.marks, target.dataset.path); + const { selectedStressMarks, stressReductions, currentDamage, availableArmor } = this.getDamageInfo(); if (!currentMark.selected && currentDamage === 0) { ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.damageAlreadyNone')); @@ -218,12 +243,18 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap } if (this.rulesOn) { - if (!currentMark.selected && currentMarks === this.actor.system.armorScore.max) { + if (target.dataset.type === 'armor' && !currentMark.selected && !availableArmor) { ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noAvailableArmorMarks')); return; } } + const stressUsed = selectedStressMarks.length; + if (target.dataset.type === 'armor' && stressUsed) { + const updateResult = this.updateStressArmor(target.dataset.id, !currentMark.selected); + if (updateResult === false) return; + } + if (currentMark.selected) { const currentDamageLabel = getDamageLabel(currentDamage); for (let reduction of stressReductions) { @@ -232,8 +263,16 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap } } - if (target.dataset.type === 'armor' && selectedStressMarks.length > 0) { - selectedStressMarks.forEach(mark => (mark.selected = false)); + if (target.dataset.type === 'stress' && currentMark.armorMarkId) { + for (const source of this.marks.armor) { + const match = Object.keys(source.marks).find(key => key === currentMark.armorMarkId); + if (match) { + source.marks[match].selected = false; + break; + } + } + + currentMark.armorMarkId = null; } } @@ -241,6 +280,25 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap this.render(); } + updateStressArmor(armorMarkId, select) { + let stressMarkKey = null; + if (select) { + stressMarkKey = Object.keys(this.marks.stress).find( + key => this.marks.stress[key].selected && !this.marks.stress[key].armorMarkId + ); + } else { + stressMarkKey = Object.keys(this.marks.stress).find( + key => this.marks.stress[key].armorMarkId === armorMarkId + ); + if (!stressMarkKey) + stressMarkKey = Object.keys(this.marks.stress).find(key => this.marks.stress[key].selected); + } + + if (!stressMarkKey) return false; + + this.marks.stress[stressMarkKey].armorMarkId = select ? armorMarkId : null; + } + static useStressReduction(_, target) { const damageValue = Number(target.dataset.reduction); const stressReduction = this.availableStressReductions[damageValue]; @@ -279,11 +337,19 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap } static async takeDamage() { - const { selectedArmorMarks, selectedStressMarks, stressReductions, currentDamage } = this.getDamageInfo(); - const armorSpent = selectedArmorMarks.length + selectedStressMarks.length; - const stressSpent = selectedStressMarks.length + stressReductions.reduce((acc, red) => acc + red.cost, 0); + const { selectedStressMarks, stressReductions, currentDamage } = this.getDamageInfo(); + const armorChanges = this.marks.armor.reduce((acc, source) => { + const amount = Object.values(source.marks).filter(x => x.selected).length; + if (!amount) return acc; - this.resolve({ modifiedDamage: currentDamage, armorSpent, stressSpent }); + acc.push({ uuid: source.effect.uuid, amount }); + return acc; + }, []); + const stressSpent = + selectedStressMarks.filter(x => x.armorMarkId).length + + stressReductions.reduce((acc, red) => acc + red.cost, 0); + + this.resolve({ modifiedDamage: currentDamage, armorChanges, stressSpent }); await this.close(true); } diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index 87f203ec..1ede6554 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -559,6 +559,18 @@ export default class DhCharacter extends BaseDataActor { doc.updateEmbeddedDocuments('ActiveEffect', updates, { render: index === updateValues.length - 1 }); } + async updateArmorEffectValue({ uuid, value }) { + const effect = await foundry.utils.fromUuid(uuid); + await effect.update({ + 'system.changes': [ + { + ...effect.system.armorChange, + value: effect.system.armorChange.value + value + } + ] + }); + } + get sheetLists() { const ancestryFeatures = [], communityFeatures = [], diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 64e877c9..2bc0179a 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -626,12 +626,10 @@ export default class DhpActor extends Actor { } ); if (armorSlotResult) { - const { modifiedDamage, armorSpent, stressSpent } = armorSlotResult; + const { modifiedDamage, armorChanges, stressSpent } = armorSlotResult; updates.find(u => u.key === 'hitPoints').value = modifiedDamage; - if (armorSpent) { - const armorUpdate = updates.find(u => u.key === 'armor'); - if (armorUpdate) armorUpdate.value += armorSpent; - else updates.push({ value: armorSpent, key: 'armor' }); + for (const armorChange of armorChanges) { + updates.push({ value: armorChange.amount, key: 'armor', uuid: armorChange.uuid }); } if (stressSpent) { const stressUpdate = updates.find(u => u.key === 'stress'); @@ -778,7 +776,8 @@ export default class DhpActor extends Actor { ); break; case 'armor': - this.system.updateArmorValue(r); + if (!r.uuid) this.system.updateArmorValue(r); + else this.system.updateArmorEffectValue(r); break; default: if (this.system.resources?.[r.key]) { diff --git a/styles/less/dialog/damage-reduction/damage-reduction-container.less b/styles/less/dialog/damage-reduction/damage-reduction-container.less index 2f343fb3..e8242bdd 100644 --- a/styles/less/dialog/damage-reduction/damage-reduction-container.less +++ b/styles/less/dialog/damage-reduction/damage-reduction-container.less @@ -35,7 +35,10 @@ display: flex; flex-direction: column; align-items: center; - width: 100%; + + &.full-width { + width: 100%; + } } .padded { @@ -45,6 +48,7 @@ .armor-title { margin: 0; white-space: nowrap; + width: 100%; } .resources-container { @@ -62,12 +66,17 @@ .mark-selection { display: flex; - align-items: center; + flex-direction: column; width: 100%; margin: 0; + h4 { + margin: 0; + } + .mark-selection-inner { display: flex; + justify-content: center; gap: 8px; .mark-container { @@ -91,6 +100,19 @@ opacity: 0.2; } + &.spent { + ::after { + position: absolute; + content: '/'; + color: red; + font-weight: 700; + font-size: 1.8em; + left: -1px; + top: -7px; + rotate: 13deg; + } + } + .fa-shield { position: relative; right: 0.5px; diff --git a/templates/dialogs/damageReduction.hbs b/templates/dialogs/damageReduction.hbs index 57d7ee61..50fe3422 100644 --- a/templates/dialogs/damageReduction.hbs +++ b/templates/dialogs/damageReduction.hbs @@ -7,53 +7,57 @@
-

{{localize "DAGGERHEART.APPLICATIONS.DamageReduction.armorMarks"}}

-
{{armorMarks}}/{{armorScore}}
+

{{localize "DAGGERHEART.APPLICATIONS.DamageReduction.maxUseableArmor"}}

+
{{availableArmor}}
- {{#if this.stress}} -
-

{{localize "DAGGERHEART.APPLICATIONS.DamageReduction.stress"}}

-
{{this.stress.value}}/{{this.stress.max}}
-
- {{/if}}
-
-

-
- {{#each marks.armor}} -
- -
- {{/each}} +
+ {{#each marks.armor as |source|}} + + {{/each}} + {{#if usesStressArmor}} +
+

{{localize "Stress"}}

+
{{#each marks.stress}}
{{/each}} +
-

-
{{localize "DAGGERHEART.APPLICATIONS.DamageReduction.usedMarks"}}
+ {{/if}}
{{#if availableStressReductions}}
-

{{localize "DAGGERHEART.APPLICATIONS.DamageReduction.stressReduction"}}

+

{{localize "DAGGERHEART.APPLICATIONS.DamageReduction.stressReduction"}}

{{/if}} {{#each availableStressReductions}}
-

+

{{#if this.any}} {{localize "DAGGERHEART.GENERAL.any"}} @@ -74,7 +78,7 @@ {{#if reduceSeverity}}
-

{{localize "DAGGERHEART.APPLICATIONS.DamageReduction.reduceSeverity" nr=reduceSeverity}}

+

{{localize "DAGGERHEART.APPLICATIONS.DamageReduction.reduceSeverity" nr=reduceSeverity}}

{{/if}} @@ -82,7 +86,7 @@ {{#if thresholdImmunities}}
-

{{localize "DAGGERHEART.APPLICATIONS.DamageReduction.thresholdImmunities"}}

+

{{localize "DAGGERHEART.APPLICATIONS.DamageReduction.thresholdImmunities"}}

{{/if}} From 6176c3265d4e7b30ac0101001ce657cc83297b59 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sat, 14 Feb 2026 01:39:13 +0100 Subject: [PATCH 23/23] Fixed ArmorManagement pip syle --- .../applications/sheets/actors/character.mjs | 46 +++++++++++++++++-- .../less/sheets/actors/character/sidebar.less | 17 +++++++ styles/less/ux/tooltip/armorManagement.less | 23 ++++++++++ templates/sheets/actors/character/sidebar.hbs | 13 ++++-- templates/ui/tooltip/armorManagement.hbs | 43 +++++++++++------ 5 files changed, 121 insertions(+), 21 deletions(-) diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index c9a6047b..b9b6a123 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -966,11 +966,16 @@ export default class CharacterSheet extends DHBaseActorSheet { if (armorSources.length <= 1) return; + const useResourcePips = game.settings.get( + CONFIG.DH.id, + CONFIG.DH.SETTINGS.gameSettings.appearance + ).useResourcePips; const html = document.createElement('div'); html.innerHTML = await foundry.applications.handlebars.renderTemplate( `systems/daggerheart/templates/ui/tooltip/armorManagement.hbs`, { - sources: armorSources + sources: armorSources, + useResourcePips } ); @@ -986,6 +991,10 @@ export default class CharacterSheet extends DHBaseActorSheet { element.addEventListener('blur', CharacterSheet.armorSourceUpdate); element.addEventListener('input', CharacterSheet.armorSourceInput); }); + + html.querySelectorAll('.armor-slot').forEach(element => { + element.addEventListener('click', CharacterSheet.armorSourcePipUpdate); + }); } static async armorSourceInput(event) { @@ -1000,12 +1009,11 @@ export default class CharacterSheet extends DHBaseActorSheet { static async armorSourceUpdate(event) { const effect = await foundry.utils.fromUuid(event.target.dataset.uuid); if (effect.system.changes.length !== 1) return; - const armorEffect = effect.system.changes[0]; const value = Math.max(Math.min(Number.parseInt(event.target.value), effect.system.armorData.max), 0); const newChanges = [ { - ...armorEffect, + ...effect.system.changes[0], value } ]; @@ -1017,6 +1025,38 @@ export default class CharacterSheet extends DHBaseActorSheet { await effect.update({ 'system.changes': newChanges }); } + static async armorSourcePipUpdate(event) { + const target = event.target.closest('.armor-slot'); + const effect = await foundry.utils.fromUuid(target.dataset.uuid); + if (effect.system.changes.length !== 1) return; + const { value, max } = effect.system.armorData; + + const inputValue = Number.parseInt(target.dataset.value); + const decreasing = value >= inputValue; + const newValue = decreasing ? inputValue - 1 : inputValue; + + const newChanges = [ + { + ...effect.system.changes[0], + value: newValue + } + ]; + + const container = target.closest('.slot-bar'); + for (const armorSlot of container.querySelectorAll('.armor-slot i')) { + const index = Number.parseInt(armorSlot.dataset.index); + if (decreasing && index >= newValue) { + armorSlot.classList.remove('fa-shield'); + armorSlot.classList.add('fa-shield-halved'); + } else if (!decreasing && index < newValue) { + armorSlot.classList.add('fa-shield'); + armorSlot.classList.remove('fa-shield-halved'); + } + } + + await effect.update({ 'system.changes': newChanges }); + } + /** * Open the downtime application. * @type {ApplicationClickAction} diff --git a/styles/less/sheets/actors/character/sidebar.less b/styles/less/sheets/actors/character/sidebar.less index 42e40386..ddd3a348 100644 --- a/styles/less/sheets/actors/character/sidebar.less +++ b/styles/less/sheets/actors/character/sidebar.less @@ -276,6 +276,23 @@ } } + .slot-label { + .slot-value-container { + position: relative; + display: flex; + align-items: center; + justify-content: center; + width: 100%; + + i { + position: absolute; + right: 0; + font-size: 12px; + color: light-dark(@beige, @dark-blue); + } + } + } + .status-value { padding: 0 5px; } diff --git a/styles/less/ux/tooltip/armorManagement.less b/styles/less/ux/tooltip/armorManagement.less index bc716fa0..390c0a00 100644 --- a/styles/less/ux/tooltip/armorManagement.less +++ b/styles/less/ux/tooltip/armorManagement.less @@ -97,4 +97,27 @@ } } } + + .slot-bar { + display: flex; + flex-wrap: wrap; + gap: 4px; + padding: 5px; + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 6px; + z-index: 1; + background: @dark-blue; + justify-content: center; + color: light-dark(@dark-blue, @golden); + + .armor-slot { + cursor: pointer; + transition: all 0.3s ease; + font-size: var(--font-size-12); + + .fa-shield-halved { + color: light-dark(@dark-blue-40, @golden-40); + } + } + } } diff --git a/templates/sheets/actors/character/sidebar.hbs b/templates/sheets/actors/character/sidebar.hbs index 10017343..d3be4983 100644 --- a/templates/sheets/actors/character/sidebar.hbs +++ b/templates/sheets/actors/character/sidebar.hbs @@ -45,10 +45,13 @@ {{/times}}
- +
+ {{document.system.armorScore.value}} / {{document.system.armorScore.max}} + +
+

{{else}}
@@ -61,10 +64,10 @@ value='{{document.system.armorScore.value}}' max='{{document.system.armorScore.max}}' > - + {{/if}}
{{else}} diff --git a/templates/ui/tooltip/armorManagement.hbs b/templates/ui/tooltip/armorManagement.hbs index aa8c9878..f5cf0d32 100644 --- a/templates/ui/tooltip/armorManagement.hbs +++ b/templates/ui/tooltip/armorManagement.hbs @@ -1,19 +1,36 @@
{{#each sources as |source|}} -
- -
-
- - / - {{source.max}} + {{#if ../useResourcePips}} +
+ + -
-
+ {{else}} +
+ +
+
+ + / + {{source.max}} +
+ +
+
+ {{/if}} {{/each}}
\ No newline at end of file