Merge branch 'main' into iss4-attach-items

This commit is contained in:
psitacus 2025-07-12 03:31:26 -06:00
commit f61cc23daa
93 changed files with 3803 additions and 2390 deletions

View file

@ -1,5 +1,6 @@
export * as characterCreation from './characterCreation/_module.mjs';
export * as dialogs from './dialogs/_module.mjs';
export * as hud from './hud/_module.mjs';
export * as levelup from './levelup/_module.mjs';
export * as settings from './settings/_module.mjs';
export * as sheets from './sheets/_module.mjs';

View file

@ -63,6 +63,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
const context = await super._prepareContext(_options);
context.rollConfig = this.config;
context.hasRoll = !!this.config.roll;
context.canRoll = true;
if (this.config.costs?.length) {
const updatedCosts = this.action.calcCosts(this.config.costs);
context.costs = updatedCosts;
@ -84,7 +85,6 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
context.advantage = this.config.roll?.advantage;
context.disadvantage = this.config.roll?.disadvantage;
context.diceOptions = CONFIG.DH.GENERAL.diceTypes;
context.canRoll = true;
context.isLite = this.config.roll?.lite;
context.extraFormula = this.config.extraFormula;
context.formula = this.roll.constructFormula(this.config);

View file

@ -3,7 +3,7 @@ import { damageKeyToNumber, getDamageLabel } from '../../helpers/utils.mjs';
const { DialogV2, ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
export default class DamageReductionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(resolve, reject, actor, damage) {
constructor(resolve, reject, actor, damage, damageType) {
super({});
this.resolve = resolve;
@ -11,37 +11,46 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
this.actor = actor;
this.damage = damage;
const maxArmorMarks = Math.min(
actor.system.armorScore - actor.system.armor.system.marks.value,
actor.system.rules.maxArmorMarked.total
);
const canApplyArmor = actor.system.armorApplicableDamageTypes[damageType];
const maxArmorMarks = canApplyArmor
? Math.min(
actor.system.armorScore - actor.system.armor.system.marks.value,
actor.system.rules.damageReduction.maxArmorMarked.total
)
: 0;
const armor = [...Array(maxArmorMarks).keys()].reduce((acc, _) => {
acc[foundry.utils.randomID()] = { selected: false };
return acc;
}, {});
const stress = [...Array(actor.system.rules.maxArmorMarked.stressExtra ?? 0).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 };
return acc;
},
{}
);
this.marks = { armor, stress };
this.availableStressReductions = Object.keys(actor.system.rules.stressDamageReduction).reduce((acc, key) => {
const dr = actor.system.rules.stressDamageReduction[key];
if (dr.enabled) {
if (acc === null) acc = {};
this.availableStressReductions = Object.keys(actor.system.rules.damageReduction.stressDamageReduction).reduce(
(acc, key) => {
const dr = actor.system.rules.damageReduction.stressDamageReduction[key];
if (dr.enabled) {
if (acc === null) acc = {};
const damage = damageKeyToNumber(key);
acc[damage] = {
cost: dr.cost,
selected: false,
from: getDamageLabel(damage),
to: getDamageLabel(damage - 1)
};
}
const damage = damageKeyToNumber(key);
acc[damage] = {
cost: dr.cost,
selected: false,
from: getDamageLabel(damage),
to: getDamageLabel(damage - 1)
};
}
return acc;
}, null);
return acc;
},
null
);
}
get title() {
@ -90,7 +99,8 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
context.armorScore = this.actor.system.armorScore;
context.armorMarks = currentMarks;
context.basicMarksUsed = selectedArmorMarks.length === this.actor.system.rules.maxArmorMarked.total;
context.basicMarksUsed =
selectedArmorMarks.length === this.actor.system.rules.damageReduction.maxArmorMarked.total;
const stressReductionStress = this.availableStressReductions
? stressReductions.reduce((acc, red) => acc + red.cost, 0)
@ -122,12 +132,15 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
getDamageInfo = () => {
const selectedArmorMarks = Object.values(this.marks.armor).filter(x => x.selected);
const selectedStressMarks = Object.values(this.marks.stress).filter(x => x.selected);
const stressReductions = Object.values(this.availableStressReductions ?? {}).filter(red => red.selected);
const stressReductions = this.availableStressReductions
? Object.values(this.availableStressReductions).filter(red => red.selected)
: [];
const currentMarks =
this.actor.system.armor.system.marks.value + selectedArmorMarks.length + selectedStressMarks.length;
const currentDamage =
this.damage - selectedArmorMarks.length - selectedStressMarks.length - stressReductions.length;
const armorMarkReduction =
selectedArmorMarks.length * this.actor.system.rules.damageReduction.increasePerArmorMark;
const currentDamage = this.damage - armorMarkReduction - selectedStressMarks.length - stressReductions.length;
return { selectedArmorMarks, selectedStressMarks, stressReductions, currentMarks, currentDamage };
};
@ -216,11 +229,11 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
await super.close({});
}
static async armorStackQuery({actorId, damage}) {
static async armorStackQuery({ actorId, damage, type }) {
return new Promise(async (resolve, reject) => {
const actor = await fromUuid(actorId);
if(!actor || !actor?.isOwner) reject();
new DamageReductionDialog(resolve, reject, actor, damage).render({ force: true });
})
if (!actor || !actor?.isOwner) reject();
new DamageReductionDialog(resolve, reject, actor, damage, type).render({ force: true });
});
}
}

View file

@ -0,0 +1 @@
export { default as DHTokenHUD } from './tokenHud.mjs';

View file

@ -0,0 +1,84 @@
export default class DHTokenHUD extends TokenHUD {
static DEFAULT_OPTIONS = {
classes: ['daggerheart']
};
/** @override */
static PARTS = {
hud: {
root: true,
template: 'systems/daggerheart/templates/hud/tokenHUD.hbs'
}
};
async _prepareContext(options) {
const context = await super._prepareContext(options);
context.systemStatusEffects = Object.keys(context.statusEffects).reduce((acc, key) => {
const effect = context.statusEffects[key];
if (effect.systemEffect) acc[key] = effect;
return acc;
}, {});
const useGeneric = game.settings.get(
CONFIG.DH.id,
CONFIG.DH.SETTINGS.gameSettings.appearance
).showGenericStatusEffects;
context.genericStatusEffects = useGeneric
? Object.keys(context.statusEffects).reduce((acc, key) => {
const effect = context.statusEffects[key];
if (!effect.systemEffect) acc[key] = effect;
return acc;
}, {})
: null;
return context;
}
_getStatusEffectChoices() {
// Include all HUD-enabled status effects
const choices = {};
for (const status of CONFIG.statusEffects) {
if (
status.hud === false ||
(foundry.utils.getType(status.hud) === 'Object' &&
status.hud.actorTypes?.includes(this.document.actor.type) === false)
) {
continue;
}
choices[status.id] = {
_id: status._id,
id: status.id,
systemEffect: status.systemEffect,
title: game.i18n.localize(status.name ?? /** @deprecated since v12 */ status.label),
src: status.img ?? /** @deprecated since v12 */ status.icon,
isActive: false,
isOverlay: false
};
}
// Update the status of effects which are active for the token actor
const activeEffects = this.actor?.effects || [];
for (const effect of activeEffects) {
for (const statusId of effect.statuses) {
const status = choices[statusId];
if (!status) continue;
if (status._id) {
if (status._id !== effect.id) continue;
} else {
if (effect.statuses.size !== 1) continue;
}
status.isActive = true;
if (effect.getFlag('core', 'overlay')) status.isOverlay = true;
break;
}
}
// Flag status CSS class
for (const status of Object.values(choices)) {
status.cssClass = [status.isActive ? 'active' : null, status.isOverlay ? 'overlay' : null].filterJoin(' ');
}
return choices;
}
}

View file

@ -10,6 +10,7 @@ export default class AdversarySheet extends DHBaseActorSheet {
actions: {
reactionRoll: AdversarySheet.#reactionRoll,
useItem: this.useItem,
useAction: this.useItem,
toChat: this.toChat
},
window: {

View file

@ -24,6 +24,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
levelManagement: CharacterSheet.#levelManagement,
toggleEquipItem: CharacterSheet.#toggleEquipItem,
useItem: this.useItem, //TODO Fix this
useAction: this.useAction,
toChat: this.toChat
},
window: {
@ -625,6 +626,20 @@ export default class CharacterSheet extends DHBaseActorSheet {
}
}
/**
* Use an action
* @type {ApplicationClickAction}
*/
static async useAction(event, button) {
const item = this.getItem(button);
if (!item) return;
const action = item.system.actions.find(x => x.id === button.dataset.actionId);
if (!action) return;
action.use(event);
}
/**
* Send item to Chat
* @type {ApplicationClickAction}

View file

@ -11,6 +11,7 @@ export default class DhpEnvironment extends DHBaseActorSheet {
},
actions: {
useItem: this.useItem,
useAction: this.useItem,
toChat: this.toChat
},
dragDrop: [{ dragSelector: '.action-section .inventory-item', dropSelector: null }]

View file

@ -36,7 +36,7 @@ export default class ArmorSheet extends ItemAttachmentSheet(DHBaseItemSheet) {
switch (partId) {
case 'settings':
context.features = this.document.system.features.map(x => x.value);
context.features = this.document.system.armorFeatures.map(x => x.value);
break;
}
@ -48,6 +48,6 @@ export default class ArmorSheet extends ItemAttachmentSheet(DHBaseItemSheet) {
* @param {Array<Object>} selectedOptions - The currently selected tag objects.
*/
static async #onFeatureSelect(selectedOptions) {
await this.document.update({ 'system.features': selectedOptions.map(x => ({ value: x.value })) });
await this.document.update({ 'system.armorFeatures': selectedOptions.map(x => ({ value: x.value })) });
}
}

View file

@ -35,7 +35,7 @@ export default class WeaponSheet extends ItemAttachmentSheet(DHBaseItemSheet) {
await super._preparePartContext(partId, context);
switch (partId) {
case 'settings':
context.features = this.document.system.features.map(x => x.value);
context.features = this.document.system.weaponFeatures.map(x => x.value);
context.systemFields.attack.fields = this.document.system.attack.schema.fields;
break;
}
@ -47,6 +47,6 @@ export default class WeaponSheet extends ItemAttachmentSheet(DHBaseItemSheet) {
* @param {Array<Object>} selectedOptions - The currently selected tag objects.
*/
static async #onFeatureSelect(selectedOptions) {
await this.document.update({ 'system.features': selectedOptions.map(x => ({ value: x.value })) });
await this.document.update({ 'system.weaponFeatures': selectedOptions.map(x => ({ value: x.value })) });
}
}

View file

@ -80,8 +80,8 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
actor.system.attack?._id === actionId
? actor.system.attack
: item.system.attack?._id === actionId
? item.system.attack
: item?.system?.actions?.find(a => a._id === actionId);
? item.system.attack
: item?.system?.actions?.find(a => a._id === actionId);
return action;
}