Consolidate armor source retrieval

This commit is contained in:
Carlos Fernandez 2026-03-21 18:57:51 -04:00
parent 1cdabf15a5
commit b6b207299c
4 changed files with 68 additions and 62 deletions

View file

@ -3,7 +3,7 @@ import DhDeathMove from '../../dialogs/deathMove.mjs';
import { CharacterLevelup, LevelupViewMode } from '../../levelup/_module.mjs'; import { CharacterLevelup, LevelupViewMode } from '../../levelup/_module.mjs';
import DhCharacterCreation from '../../characterCreation/characterCreation.mjs'; import DhCharacterCreation from '../../characterCreation/characterCreation.mjs';
import FilterMenu from '../../ux/filter-menu.mjs'; import FilterMenu from '../../ux/filter-menu.mjs';
import { getDocFromElement, getDocFromElementSync } from '../../../helpers/utils.mjs'; import { getArmorSources, getDocFromElement, getDocFromElementSync } from '../../../helpers/utils.mjs';
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */ /**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
@ -943,29 +943,14 @@ export default class CharacterSheet extends DHBaseActorSheet {
return; return;
} }
const armorSources = []; const armorSources = getArmorSources(this.document)
for (var effect of Array.from(this.document.allApplicableEffects())) { .filter(s => !s.disabled)
const origin = effect.origin ? await foundry.utils.fromUuid(effect.origin) : effect.parent; .toReversed()
if (!effect.system.armorData || effect.disabled || effect.isSuppressed) continue; .map(({ name, document, data }) => ({
...data,
const useEffectName = origin.type === 'armor' || origin instanceof Actor; uuid: document.uuid,
const name = useEffectName ? effect.name : origin.name; name
armorSources.push({ }));
uuid: effect.uuid,
name,
...effect.system.armorData
});
}
if (this.document.system.armor) {
armorSources.push({
...this.document.system.armor.system.armor,
uuid: this.document.system.armor.uuid,
name: this.document.system.armor.name,
isArmorItem: true
});
}
if (!armorSources.length) return; if (!armorSources.length) return;
const useResourcePips = game.settings.get( const useResourcePips = game.settings.get(
@ -1005,34 +990,31 @@ export default class CharacterSheet extends DHBaseActorSheet {
/** Update specific armor source */ /** Update specific armor source */
static async armorSourcePipUpdate(event) { static async armorSourcePipUpdate(event) {
const target = event.target.closest('.armor-slot'); const target = event.target.closest('.armor-slot');
const { uuid, value, isArmorItem: isArmorItemString } = target.dataset; const { uuid, value } = target.dataset;
const isArmorItem = Boolean(isArmorItemString); const document = await foundry.utils.fromUuid(uuid);
let inputValue = Number.parseInt(value); let inputValue = Number.parseInt(value);
let decreasing = false; let decreasing = false;
let newCurrent = 0; let newCurrent = 0;
if (isArmorItem) { if (document.type === 'armor') {
const armor = await foundry.utils.fromUuid(uuid); decreasing = document.system.armor.current >= inputValue;
decreasing = armor.system.armor.current >= inputValue;
newCurrent = decreasing ? inputValue - 1 : inputValue; newCurrent = decreasing ? inputValue - 1 : inputValue;
await document.update({ 'system.armor.current': newCurrent });
await armor.update({ 'system.armor.current': newCurrent });
} else { } else {
const effect = await foundry.utils.fromUuid(uuid); const armorChange = document.system.armorChange;
const armorChange = effect.system.armorChange;
if (!armorChange) return; if (!armorChange) return;
const { current } = effect.system.armorData; const { current } = document.system.armorData;
decreasing = current >= inputValue; decreasing = current >= inputValue;
newCurrent = decreasing ? inputValue - 1 : inputValue; newCurrent = decreasing ? inputValue - 1 : inputValue;
const newChanges = effect.system.changes.map(change => ({ const newChanges = document.system.changes.map(change => ({
...change, ...change,
value: change.type === 'armor' ? { ...change.value, current: newCurrent } : change.value value: change.type === 'armor' ? { ...change.value, current: newCurrent } : change.value
})); }));
await effect.update({ 'system.changes': newChanges }); await document.update({ 'system.changes': newChanges });
} }
const container = target.closest('.slot-bar'); const container = target.closest('.slot-bar');

View file

@ -6,7 +6,7 @@ import DhCreature from './creature.mjs';
import { attributeField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs'; import { attributeField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs';
import { ActionField } from '../fields/actionField.mjs'; import { ActionField } from '../fields/actionField.mjs';
import DHCharacterSettings from '../../applications/sheets-configs/character-settings.mjs'; import DHCharacterSettings from '../../applications/sheets-configs/character-settings.mjs';
import { orderSourcesForArmorAutoChange } from '../../helpers/utils.mjs'; import { getArmorSources } from '../../helpers/utils.mjs';
export default class DhCharacter extends DhCreature { export default class DhCharacter extends DhCreature {
/**@override */ /**@override */
@ -470,10 +470,7 @@ export default class DhCharacter extends DhCreature {
const increasing = armorChange >= 0; const increasing = armorChange >= 0;
let remainingChange = Math.abs(armorChange); let remainingChange = Math.abs(armorChange);
const armorSources = Array.from(this.parent.allApplicableEffects()).filter(x => x.system.armorData); const orderedSources = getArmorSources(this.parent);
if (this.armor) armorSources.push(this.armor);
const orderedSources = orderSourcesForArmorAutoChange(armorSources, increasing);
const handleArmorData = (embeddedUpdates, doc, armorData) => { const handleArmorData = (embeddedUpdates, doc, armorData) => {
let usedArmorChange = 0; let usedArmorChange = 0;
@ -501,7 +498,7 @@ export default class DhCharacter extends DhCreature {
const armorUpdates = []; const armorUpdates = [];
const effectUpdates = []; const effectUpdates = [];
for (const armorSource of orderedSources) { for (const { document: armorSource } of orderedSources) {
const usedArmorChange = handleArmorData( const usedArmorChange = handleArmorData(
armorSource.type === 'armor' ? armorUpdates : effectUpdates, armorSource.type === 'armor' ? armorUpdates : effectUpdates,
armorSource.parent, armorSource.parent,

View file

@ -744,15 +744,27 @@ export function getUnusedDamageTypes(parts) {
}, []); }, []);
} }
/** /** Returns resolved armor sources ordered by application order */
* export function getArmorSources(actor) {
* @param {{ type: string, parent: Object, disabled: boolean, isSuppressed: boolean }} sources const rawArmorSources = Array.from(actor.allApplicableEffects()).filter(x => x.system.armorData);
* @param { boolean } increasing if (actor.system.armor) rawArmorSources.push(actor.system.armor);
* @returns
*/ const data = rawArmorSources.map(doc => {
export function orderSourcesForArmorAutoChange(sources, increasing) { // Get the origin item. Since the actor is already loaded, it should already be cached
const getSourceWeight = source => { // Consider the relative function versions if this causes an issue
switch (source.type) { const isItem = doc instanceof Item;
const origin = isItem ? doc : doc.origin ? foundry.utils.fromUuidSync(doc.origin) : doc.parent;
return {
origin,
name: origin.name,
document: doc,
data: doc.system.armor ?? doc.system.armorData,
disabled: !!doc.disabled || !!doc.isSuppressed
};
});
return sortBy(data, ({ origin }) => {
switch (origin?.type) {
case 'class': case 'class':
case 'subclass': case 'subclass':
case 'ancestry': case 'ancestry':
@ -760,23 +772,38 @@ export function orderSourcesForArmorAutoChange(sources, increasing) {
case 'feature': case 'feature':
case 'domainCard': case 'domainCard':
return 2; return 2;
case 'armor':
return 3;
case 'loot': case 'loot':
case 'consumable': case 'consumable':
return 3;
case 'character':
return 4; return 4;
case 'weapon': case 'weapon':
return 5; return 5;
case 'character': case 'armor':
return 6; return 6;
default: default:
return 1; return 1;
} }
}; });
}
return sources
.filter(x => !x.disabled && !x.isSuppressed) /**
.sort((a, b) => * Returns an array sorted by a function that returns a thing to compare, or an array to compare in order
increasing ? getSourceWeight(b) - getSourceWeight(a) : getSourceWeight(a) - getSourceWeight(b) * Similar to lodash's sortBy function.
); */
export function sortBy(arr, fn) {
const directCompare = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
const cmp = (a, b) => {
const resultA = fn(a);
const resultB = fn(b);
if (Array.isArray(resultA) && Array.isArray(resultB)) {
for (let idx = 0; idx < Math.min(resultA.length, resultB.length); idx++) {
const result = directCompare(resultA[idx], resultB[idx]);
if (result !== 0) return result;
}
return 0;
}
return directCompare(resultA, resultB);
};
return arr.sort(cmp);
} }

View file

@ -5,7 +5,7 @@
<p class="armor-source-label">{{source.name}}</p> <p class="armor-source-label">{{source.name}}</p>
<div class="slot-bar"> <div class="slot-bar">
{{#times source.max}} {{#times source.max}}
<a class='armor-slot' data-value="{{add this 1}}" data-uuid="{{source.uuid}}" data-is-armor-item="{{source.isArmorItem}}"> <a class='armor-slot' data-value="{{add this 1}}" data-uuid="{{source.uuid}}">
{{#if (gte ../current (add this 1))}} {{#if (gte ../current (add this 1))}}
<i class="fa-solid fa-shield" data-index="{{this}}"></i> <i class="fa-solid fa-shield" data-index="{{this}}"></i>
{{else}} {{else}}