[V14] 1354 - Armor Effect (#1652)

* Initial

* progress

* Working armor application

* .

* Added a updateArmorValue function that updates armoreffects according to an auto order

* .

* Added createDialog

* .

* Updated Armor SRD

* .

* Fixed character sheet armor update

* Updated itemconfig

* Actions now use createDialog for effects

* .

* .

* Fixed ArmorEffect max being a string

* Fixed SRD armor effects

* Finally finished the migration ._.

* SRD finalization

* Added ArmoreEffect.armorInteraction option

* Added ArmorManagement menu

* Fixed DamageReductionDialog

* Fixed ArmorManagement pip syle

* feat: add style to armors tooltip, add a style to make armor slot label more clear that was a button and add a tooltip location

* .

* Removed tooltip on manageArmor

* Fixes

* Fixed Downtime armor repair

* Removed ArmorScore from character data model and instead adding it in basePrep

* [Feature] ArmorEffect reworked into ChangeType on BaseEffect (#1739)

* Initial

* .

* Single armor rework start

* More fixes

* Fixed DamageReductionDialog

* Removed last traces of ArmorEffect

* .

* Corrected the SRD to use base effects again

* Removed bare bones armor item

* [V14] Refactor ArmorChange schema and fix some bugs (#1742)

* Refactor ArmorChange schema and fix some bugs

* Add current back to schema

* Fixed so changing armor values and taking damage works again

* Fixed so that scrolltexts for armor changes work again

* Removed old marks on armor.system

* Restored damageReductionDialog armorScore.value

* Use toggle for css class addition/removal

* Fix armor change type choices

* Added ArmorChange DamageThresholds

---------

Co-authored-by: WBHarry <williambjrklund@gmail.com>

* [V14] Armor System ArmorScore (#1744)

* Readded so that armor items have their system defined armor instead of using an ActiveEffect

* Consolidate armor source retrieval

* Fix regression with updating armor when sources are disabled

* Simplify armor pip update

* Use helper in damage reduction dialog

* .

* Corrected SRD Armor Items

---------

Co-authored-by: Carlos Fernandez <cfern1990@gmail.com>

* Updated migrations

* Migrations are now not horrible =D

---------

Co-authored-by: Murilo Brito <dev.murilobrito@gmail.com>
Co-authored-by: Carlos Fernandez <CarlosFdez@users.noreply.github.com>
Co-authored-by: Carlos Fernandez <cfern1990@gmail.com>
This commit is contained in:
WBHarry 2026-03-22 01:57:46 +01:00 committed by GitHub
parent a3f515cf6d
commit ef53a7c561
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
94 changed files with 1961 additions and 545 deletions

View file

@ -3,7 +3,7 @@ import DhDeathMove from '../../dialogs/deathMove.mjs';
import { CharacterLevelup, LevelupViewMode } from '../../levelup/_module.mjs';
import DhCharacterCreation from '../../characterCreation/characterCreation.mjs';
import FilterMenu from '../../ux/filter-menu.mjs';
import { getDocFromElement, getDocFromElementSync } from '../../../helpers/utils.mjs';
import { getArmorSources, getDocFromElement, getDocFromElementSync } from '../../../helpers/utils.mjs';
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
@ -34,7 +34,8 @@ export default class CharacterSheet extends DHBaseActorSheet {
cancelBeastform: CharacterSheet.#cancelBeastform,
toggleResourceManagement: CharacterSheet.#toggleResourceManagement,
useDowntime: this.useDowntime,
viewParty: CharacterSheet.#viewParty
viewParty: CharacterSheet.#viewParty,
toggleArmorMangement: CharacterSheet.#toggleArmorManagement
},
window: {
resizable: true,
@ -638,12 +639,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 });
}
/* -------------------------------------------- */
@ -803,10 +804,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 });
}
/**
@ -932,6 +936,99 @@ 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 = getArmorSources(this.document)
.filter(s => !s.disabled)
.toReversed()
.map(({ name, document, data }) => ({
...data,
uuid: document.uuid,
name
}));
if (!armorSources.length) 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,
useResourcePips
}
);
game.tooltip.dismissLockedTooltips();
game.tooltip.activate(target, {
html,
locked: true,
cssClass: 'bordered-tooltip',
direction: 'DOWN'
});
html.querySelectorAll('.armor-slot').forEach(element => {
element.addEventListener('click', CharacterSheet.armorSourcePipUpdate);
});
}
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 armorSourcePipUpdate(event) {
const target = event.target.closest('.armor-slot');
const { uuid, value } = target.dataset;
const document = await foundry.utils.fromUuid(uuid);
let inputValue = Number.parseInt(value);
let decreasing = false;
let newCurrent = 0;
if (document.type === 'armor') {
decreasing = document.system.armor.current >= inputValue;
newCurrent = decreasing ? inputValue - 1 : inputValue;
await document.update({ 'system.armor.current': newCurrent });
} else if (document.system.armorData) {
const { current } = document.system.armorData;
decreasing = current >= inputValue;
newCurrent = decreasing ? inputValue - 1 : inputValue;
const newChanges = document.system.changes.map(change => ({
...change,
value: change.type === 'armor' ? { ...change.value, current: newCurrent } : change.value
}));
await document.update({ 'system.changes': newChanges });
} else {
return;
}
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 >= newCurrent) {
armorSlot.classList.remove('fa-shield');
armorSlot.classList.add('fa-shield-halved');
} else if (!decreasing && index < newCurrent) {
armorSlot.classList.add('fa-shield');
armorSlot.classList.remove('fa-shield-halved');
}
}
}
static async #toggleResourceManagement(event, button) {
event.stopPropagation();
const existingTooltip = document.body.querySelector('.locked-tooltip .resource-management-container');
@ -965,7 +1062,6 @@ export default class CharacterSheet extends DHBaseActorSheet {
);
const target = button.closest('.resource-section');
game.tooltip.dismissLockedTooltips();
game.tooltip.activate(target, {
html,

View file

@ -189,11 +189,14 @@ export default class Party extends DHBaseActorSheet {
* Toggles a armor slot resource value.
* @type {ApplicationClickAction}
*/
static async #toggleArmorSlot(_, target, element) {
const armorItem = await foundry.utils.fromUuid(target.dataset.itemUuid);
const armorValue = Number.parseInt(target.dataset.value);
const newValue = armorItem.system.marks.value >= armorValue ? armorValue - 1 : armorValue;
await armorItem.update({ 'system.marks.value': newValue });
static async #toggleArmorSlot(_, target) {
const actor = game.actors.get(target.dataset.actorId);
const { value, max } = actor.system.armorScore;
const inputValue = Number.parseInt(target.dataset.value);
const newValue = value >= inputValue ? inputValue - 1 : inputValue;
const changeValue = Math.min(newValue - value, max - value);
await actor.system.updateArmorValue({ value: changeValue });
this.render();
}

View file

@ -749,11 +749,13 @@ 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) {

View file

@ -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({ noTransferArmor: true })) {
const list = effect.active ? context.effects.actives : context.effects.inactives;
list.push(effect);
}

View file

@ -47,6 +47,15 @@ export default class ArmorSheet extends ItemAttachmentSheet(DHBaseItemSheet) {
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.armorChange.updateArmorMax(value);
this.render();
}
/**
* Callback function used by `tagifyElement`.
* @param {Array<Object>} selectedOptions - The currently selected tag objects.