Merge branch 'main' into rework/chat-message-damage

This commit is contained in:
Carlos Fernandez 2026-07-18 17:28:50 -04:00
commit c90c6afc19
46 changed files with 775 additions and 466 deletions

View file

@ -185,8 +185,6 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV
if (result === undefined) return;
const autoExpandDescription = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance)
.expandRollMessage?.desc;
const cls = getDocumentClass('ChatMessage');
const msg = {
@ -202,7 +200,6 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV
img: this.selectedMove.img,
description: game.i18n.localize(this.selectedMove.description),
result: result,
open: autoExpandDescription ? 'open' : '',
showRiskItAllButton: this.showRiskItAllButton,
riskItAllButtonLabel: this.riskItAllButtonLabel,
riskItAllHope: this.riskItAllHope

View file

@ -196,9 +196,6 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
.filter(x => x.testUserPermission(game.user, 'LIMITED'))
.filter(x => x.uuid !== this.actor.uuid);
const autoExpandDescription = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance)
.expandRollMessage?.desc;
const cls = getDocumentClass('ChatMessage');
const msg = {
user: game.user.id,
@ -219,8 +216,7 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
actor: { name: this.actor.name, img: this.actor.img },
moves: moves,
characters: characters,
selfId: this.actor.uuid,
open: autoExpandDescription ? 'open' : ''
selfId: this.actor.uuid
}
),
flags: {

View file

@ -30,7 +30,8 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
addFeature: DHBaseItemSheet.#addFeature,
deleteFeature: DHBaseItemSheet.#deleteFeature,
addResource: DHBaseItemSheet.#addResource,
removeResource: DHBaseItemSheet.#removeResource
removeResource: DHBaseItemSheet.#removeResource,
editGMNote: DHBaseItemSheet.#onEditGMNote
},
dragDrop: [
{ dragSelector: null, dropSelector: '.drop-section' },
@ -76,10 +77,16 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
/**@inheritdoc */
async _preparePartContext(partId, context, options) {
await super._preparePartContext(partId, context, options);
const TextEditor = foundry.applications.ux.TextEditor.implementation;
switch (partId) {
case 'description':
context.enrichedDescription = await this.document.system.getEnrichedDescription();
context.enrichedDescription = await this.document.system.getEnrichedDescription({ gmNotes: false });
context.enrichedGMNotes = await TextEditor.implementation.enrichHTML(this.item.system.gmNotes, {
relativeTo: this.item,
rollData: this.item.getRollData(),
secrets: this.item.isOwner
})
break;
case 'effects':
await this._prepareEffectsContext(context, options);
@ -331,4 +338,45 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
}
}
}
/**
* Handles the Add GM Note button being pressed. This is only used when an item has no GM notes.
* Later edits to a GM note instead go through the normal editor toggle workflow.
* @this DHBaseItemSheet
*/
static #onEditGMNote() {
// Open the editor, which might be hidden. We remove the css class to hide temporarily
// so that menu auto resizing functions properly.
const editor = this.element.querySelector('prose-mirror[name="system.gmNotes"]');
const wasHidden = editor.classList.contains('hide-if-inactive');
editor.classList.remove('hide-if-inactive');
editor.open = true;
window.setTimeout(() => {
if (wasHidden) editor.classList.add('hide-if-inactive');
}, 0);
}
/** @inheritdoc */
async _onRender(context, options) {
await super._onRender(context, options);
// Render an add gmnotes button if there are no set GM notes.
// We need to re-render on close since its possible to prosemirror to close *without* triggering a full re-render
if (game.user.isGM && !this.item.system.gmNotes) {
const description = this.element.querySelector('[name="system.description"]');
const addButton = () => {
if (description.querySelector('[data-action=editGMNote]')) return;
const button = document.createElement('button');
button.type = 'button';
button.classList.add('icon', 'toggle', 'fa-regular', 'fa-note-medical');
button.dataset.action = 'editGMNote';
button.dataset.tooltip = 'DAGGERHEART.ITEMS.Base.addGMNote';
description.appendChild(button);
}
addButton();
description.addEventListener('close', () => addButton());
}
}
}

View file

@ -256,7 +256,8 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
if (Hooks.call(`${CONFIG.DH.id}.postUseAction`, this, config) === false) return;
if (this.chatDisplay && !config.skips.createMessage && !config.actionChatMessageHandled) await this.toChat();
if (this.chatDisplay && !config.skips.createMessage && !config.actionChatMessageHandled)
await this.toChat(null, config);
return config;
}

View file

@ -276,10 +276,18 @@ export class DHActionDiceData extends foundry.abstract.DataModel {
};
}
/**
* @returns {string} the formula associated with this damage field
*/
getFormula() {
const multiplier = this.multiplier === 'flat' ? this.flatMultiplier : `@${this.multiplier}`,
bonus = this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : '';
return this.custom.enabled ? this.custom.formula : `${multiplier ?? 1}${this.dice}${bonus}`;
if (this.custom.enabled) return this.custom.formula;
const multiplier = this.multiplier === 'flat' ? this.flatMultiplier : `@${this.multiplier}`;
if (!multiplier) return String(this.bonus || 0);
const dice = `${multiplier ?? 1}${this.dice}`;
const sign = this.bonus < 0 ? ' - ' : ' + ';
return this.bonus ? `${dice} ${sign} ${Math.abs(this.bonus)}` : dice;
}
}

View file

@ -23,7 +23,7 @@ export default class DHSummonField extends fields.ArrayField {
super(summonFields, options, context);
}
static async execute() {
static async execute(config) {
if (!canvas.scene) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.summon.error'));
return;
@ -36,6 +36,7 @@ export default class DHSummonField extends fields.ArrayField {
const rolls = [];
const summonData = [];
const chatMessageData = [];
for (const summon of this.summon) {
const roll = new Roll(itemAbleRollParse(summon.count, this.actor, this.item));
await roll.evaluate();
@ -54,17 +55,18 @@ export default class DHSummonField extends fields.ArrayField {
tokenPreviewName: `${actor.prototypeToken.name}${remaining > 1 ? ` (${remaining}x)` : ''}`
});
}
chatMessageData.push({
data: actor,
quantity: countNumber
});
}
if (rolls.length) await triggerChatRollFx(rolls);
this.actor.sheet?.minimize();
DHSummonField.handleSummon(summonData, this.actor);
}
static async handleSummon(summonData, actionActor) {
await CONFIG.ux.TokenManager.createTokensWithPreview(summonData, { elevation: actionActor.token?.elevation });
return actionActor.sheet?.maximize();
await CONFIG.ux.TokenManager.createTokensWithPreview(summonData, { elevation: this.actor.token?.elevation });
this.actor.sheet?.maximize();
config.summonData = chatMessageData;
}
}

View file

@ -269,10 +269,7 @@ export function ActionMixin(Base) {
return this.delete();
}
async toChat(origin) {
const autoExpandDescription = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance)
.expandRollMessage?.desc;
async toChat(origin, config) {
const cls = getDocumentClass('ChatMessage');
const systemData = {
title: game.i18n.localize('DAGGERHEART.CONFIG.FeatureForm.action'),
@ -282,7 +279,7 @@ export function ActionMixin(Base) {
img: this.baseAction ? this.parent.parent.img : this.img,
tags: this.tags ? this.tags : ['Spell', 'Arcana', 'Lv 10'],
areas: this.areas,
summon: this.summon
summon: config?.summonData
},
source: {
actor: this.actor.uuid,
@ -307,7 +304,7 @@ export function ActionMixin(Base) {
system: systemData,
content: await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/ui/chat/action.hbs',
{ ...systemData, open: autoExpandDescription ? 'open' : '' }
systemData
),
flags: {
daggerheart: {

View file

@ -49,7 +49,10 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
})
};
if (this.metadata.hasDescription) schema.description = new fields.HTMLField({ required: true, nullable: true });
if (this.metadata.hasDescription) {
schema.description = new fields.HTMLField({ required: true, nullable: true });
schema.gmNotes = new fields.HTMLField({ required: true, nullable: true });
}
if (this.metadata.hasResource) {
schema.resource = new fields.SchemaField(
@ -134,7 +137,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
/**
* Augments the description for the item with type specific info to display. Implemented in applicable item subtypes.
* @param {object} [options] - Options that modify the styling of the rendered template. { headerStyle: undefined|'none'|'large' }
* @returns {string}
* @returns {Promise<{ prefix: string | null; value: string | null; suffix: string | null }>}
*/
async getDescriptionData(_options) {
return { prefix: null, value: this.description, suffix: null };
@ -143,16 +146,26 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
/**
* Gets the enriched and augmented description for the item.
* @param {object} [options] - Options that modify the styling of the rendered template. { headerStyle: undefined|'none'|'large' }
* @returns {string}
* @returns {Promise<string>}
*/
async getEnrichedDescription() {
async getEnrichedDescription({ gmNotes = true } = {}) {
if (!this.metadata.hasDescription) return '';
const { prefix, value, suffix } = await this.getDescriptionData();
const fullDescription = [prefix, value, suffix].filter(p => !!p).join('\n<hr>\n');
let fullDescription = [prefix, value, suffix].filter(p => !!p).join('\n<hr>\n');
if (this.gmNotes && gmNotes) {
const gmNotesElement = document.createElement('section');
gmNotesElement.classList.add('gm-notes-section');
gmNotesElement.dataset.visibility = 'gm';
const header = document.createElement('header');
header.classList.add('gm-notes');
header.textContent = _loc('DAGGERHEART.ITEMS.FIELDS.gmNotes.label');
gmNotesElement.innerHTML = header.outerHTML + this.gmNotes;
fullDescription += gmNotesElement.outerHTML;
}
return await foundry.applications.ux.TextEditor.implementation.enrichHTML(fullDescription, {
relativeTo: this,
relativeTo: this.parent,
rollData: this.getRollData(),
secrets: this.parent.isOwner
});

View file

@ -3,6 +3,13 @@ import { emitGMUpdate, emitGMCreate, GMUpdateEvent } from '../systemRegistration
export default class DhpChatMessage extends foundry.documents.ChatMessage {
targetHook = null;
static #EXPAND_SECTIONS = [
{ selector: 'roll-section [data-action="expandRoll"]', key: 'roll' },
{ selector: 'damage-section', key: 'damage' },
{ selector: 'target-section', key: 'target' },
{ selector: 'description-section', key: 'desc' }
];
async renderHTML() {
const actor = game.actors.get(this.speaker.actor);
const actorData =
@ -89,23 +96,26 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
}
}
// Check registered selectors and the main item section for expanding
// Preserving during re-render is handled by core foundry on anything with [data-action=expandRoll]
const autoExpandRoll = game.settings.get(
CONFIG.DH.id,
CONFIG.DH.SETTINGS.gameSettings.appearance
).expandRollMessage,
rollSections = html.querySelectorAll('.roll-part'),
itemDesc = html.querySelector('.domain-card-move');
rollSections.forEach(s => {
if (s.classList.contains('roll-section')) {
const toExpand = s.querySelector('[data-action="expandRoll"]');
toExpand.classList.toggle('expanded', autoExpandRoll.roll);
} else if (s.classList.contains('damage-section'))
s.classList.toggle('expanded', autoExpandRoll.damage);
else if (s.classList.contains('target-section')) s.classList.toggle('expanded', autoExpandRoll.target);
else if (s.classList.contains('description-section'))
s.classList.toggle('expanded', autoExpandRoll.desc);
});
if (itemDesc && autoExpandRoll.desc) itemDesc.setAttribute('open', '');
CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance
).expandRollMessage;
for (const { selector, key } of DhpChatMessage.#EXPAND_SECTIONS) {
const elements = html.querySelectorAll(selector);
for (const element of elements) {
element.classList.toggle('expanded', autoExpandRoll[key]);
}
}
// Auto expand the item description. These are not preserved by foundry during re-renders
const itemDesc = html.querySelector('details');
if (itemDesc) {
const existing = document.querySelector(`.chat-message[data-message-id="${this.id}"] details`);
if (existing?.hasAttribute('open') ?? autoExpandRoll.desc) {
itemDesc.setAttribute('open', '');
}
}
}
if (!this.isAuthor && !this.speakerActor?.isOwner) {

View file

@ -208,10 +208,7 @@ export default class DHItem extends foundry.documents.Item {
tags: this._getTags()
},
actions: item.system.actionsList,
description: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.system.description, {
relativeTo: this.parent,
rollData: this.parent?.getRollData() ?? {}
})
description: await this.system.getEnrichedDescription()
};
const msg = {

View file

@ -19,10 +19,10 @@ export default class DhTokenManager {
}
}
return await canvas.tokens.placeTokens(
const placedData = await canvas.tokens.placeTokens(
[
{
...actor.prototypeToken.toObject(),
...(await actor.getTokenDocument()).toObject(),
actorId: actor.id,
displayName: 50,
...tokenData
@ -30,6 +30,8 @@ export default class DhTokenManager {
],
{ create: false }
);
return placedData?.[0] ?? null;
}
/**
@ -46,22 +48,24 @@ export default class DhTokenManager {
const createElevation = elevation ?? level.elevation.bottom;
for (const tokenData of tokensData) {
const previewTokens = await this.createPreview(tokenData.actor, {
const previewToken = await this.createPreview(tokenData.actor, {
name: tokenData.tokenPreviewName,
level: game.user.viewedLevel,
elevation: createElevation,
flags: { daggerheart: { createPlacement: true } }
});
if (!previewTokens?.length) return null;
if (!previewToken) return null;
const finalTokenData = {
...previewToken.toObject(),
name: tokenData.actor.prototypeToken.name,
displayName: tokenData.actor.prototypeToken.displayName,
flags: tokenData.actor.prototypeToken.flags
};
await canvas.scene.createEmbeddedDocuments(
'Token',
previewTokens.map(x => ({
...x.toObject(),
name: tokenData.actor.prototypeToken.name,
displayName: tokenData.actor.prototypeToken.displayName,
flags: tokenData.actor.prototypeToken.flags
})),
[finalTokenData],
{ controlObject: true, parent: canvas.scene }
);
}

View file

@ -4,199 +4,234 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
#wide = false;
#bordered = false;
/** @inheritdoc */
async activate(element, options = {}) {
const { TextEditor } = foundry.applications.ux;
this.#wide = false;
this.#bordered = false;
let html = options.html;
if (element.dataset.tooltip?.startsWith('#battlepoints#')) {
this.#wide = true;
this.#bordered = true;
html = await this.getBattlepointHTML(element.dataset.combatId);
options.direction = this._determineItemTooltipDirection(element);
super.activate(element, { ...options, html: html });
const lockedTooltip = this.lockTooltip();
lockedTooltip.querySelectorAll('.battlepoint-toggle-container input').forEach(element => {
element.addEventListener('input', this.toggleModifier.bind(this));
});
return;
} else {
this.#wide = false;
this.#bordered = false;
const isMacro = document.getElementById('action-bar').contains(element);
const macro = isMacro ? game.macros.get(game.user.hotbar[Number(element.dataset.slot)] ?? null) : null;
const macroItemUuid = macro?.type === 'script' ? macro.command.match(/await game\.system\.api\.applications\.ui\.DhHotbar\.useItem\("([^"]+)"\);/)?.[1] : null;
if (macroItemUuid && await fromUuid(macroItemUuid, { strict: false })) {
element.dataset.tooltip = `#item#${macroItemUuid}`;
options.direction = this.constructor.TOOLTIP_DIRECTIONS.UP;
}
if (element.dataset.tooltip === '#effect-display#') {
this.#bordered = true;
let effect = {};
if (element.dataset.uuid) {
const effectItem = await foundry.utils.fromUuid(element.dataset.uuid);
const effectData = effectItem.toObject();
let html = options.html;
const key = element.dataset.tooltip?.match(/^#([\w-]+)#/)?.[1];
switch (key) {
case 'battlepoints':
return this.#activateBattlepoints(element, options);
case 'effect-display':
html = await this.#activateEffectDisplay(element, options);
break;
case 'item':
html = await this.#activateItem(element, options);
break;
case 'attack':
html = await this.#activateAttack(element, options);
break;
case 'shortRest':
case 'longRest':
html = await this.#activateRest(element, options);
break;
case 'advantage':
case 'disadvantage':
html = await this.#activateAdvantageDisadvantage(element, options);
break;
case 'deathMove':
html = await this.#activateDeathMove(element, options);
break;
}
effect = {
...effectData,
name: game.i18n.localize(effectData.name)
this.noOffset = options.noOffset;
super.activate(element, { ...options, html });
}
async #activateBattlepoints(element, options) {
this.#wide = true;
this.#bordered = true;
const html = await this.getBattlepointHTML(element.dataset.combatId);
options.direction = this._determineItemTooltipDirection(element);
super.activate(element, { ...options, html: html });
const lockedTooltip = this.lockTooltip();
lockedTooltip.querySelectorAll('.battlepoint-toggle-container input').forEach(element => {
element.addEventListener('input', this.toggleModifier.bind(this));
});
}
async #activateEffectDisplay(element, options) {
this.#bordered = true;
let effect = {};
if (element.dataset.uuid) {
const effectItem = await foundry.utils.fromUuid(element.dataset.uuid);
const effectData = effectItem.toObject();
effect = {
...effectData,
name: game.i18n.localize(effectData.name)
};
if (effectData.type === 'beastform') {
const beastformData = {
features: [],
advantageOn: effectData.system.advantageOn,
beastformAttackData: game.system.api.data.items.DHBeastform.getBeastformAttackData(effectItem)
};
if (effectData.type === 'beastform') {
const beastformData = {
features: [],
advantageOn: effectData.system.advantageOn,
beastformAttackData: game.system.api.data.items.DHBeastform.getBeastformAttackData(effectItem)
};
const features = effectItem.parent.items.filter(x => effectItem.system.featureIds.includes(x.id));
for (const feature of features) {
const featureData = feature.toObject();
featureData.enrichedDescription = await feature.system.getEnrichedDescription();
beastformData.features.push(featureData);
}
effect.description = await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/ui/tooltip/parts/beastformData.hbs',
{
item: { system: beastformData }
}
);
} else {
effect.description = game.i18n.localize(
effectData.description ?? effectData.parent.system.description
);
const features = effectItem.parent.items.filter(x => effectItem.system.featureIds.includes(x.id));
for (const feature of features) {
const featureData = feature.toObject();
featureData.enrichedDescription = await feature.system.getEnrichedDescription();
beastformData.features.push(featureData);
}
} else {
const conditions = CONFIG.DH.GENERAL.conditions();
const condition = conditions[element.dataset.condition];
effect = {
...condition,
name: game.i18n.localize(condition.name),
description: game.i18n.localize(condition.description),
appliedBy: element.dataset.appliedBy,
isLockedCondition: true
};
}
html = await foundry.applications.handlebars.renderTemplate(
`systems/daggerheart/templates/ui/tooltip/effect-display.hbs`,
effect.description = await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/ui/tooltip/parts/beastformData.hbs',
{
item: { system: beastformData }
}
);
} else {
effect.description = game.i18n.localize(
effectData.description ?? effectData.parent.system.description
);
}
} else {
const conditions = CONFIG.DH.GENERAL.conditions();
const condition = conditions[element.dataset.condition];
effect = {
...condition,
name: game.i18n.localize(condition.name),
description: game.i18n.localize(condition.description),
appliedBy: element.dataset.appliedBy,
isLockedCondition: true
};
}
const html = await foundry.applications.handlebars.renderTemplate(
`systems/daggerheart/templates/ui/tooltip/effect-display.hbs`,
{
effect
}
);
this.tooltip.innerHTML = html;
options.direction = this._determineItemTooltipDirection(element);
return html;
}
async #activateItem(element, options) {
const itemUuid = element.dataset.tooltip.slice(6);
const item = await foundry.utils.fromUuid(itemUuid);
if (item) {
const isAction = item instanceof game.system.api.models.actions.actionsTypes.base;
const isEffect = item instanceof ActiveEffect;
await this.enrichText(item);
const type = isAction ? 'action' : isEffect ? 'effect' : item.type;
const html = await foundry.applications.handlebars.renderTemplate(
`systems/daggerheart/templates/ui/tooltip/${type}.hbs`,
{
effect
item: item,
description: item.system?.enrichedDescription ?? item.enrichedDescription,
config: CONFIG.DH,
allDomains: CONFIG.DH.DOMAIN.allDomains()
}
);
this.tooltip.innerHTML = html;
options.direction = this._determineItemTooltipDirection(element);
} else {
this.#bordered = false;
options.direction ??= this._determineItemTooltipDirection(element);
return html;
}
if (element.dataset.tooltip?.startsWith('#item#')) {
const itemUuid = element.dataset.tooltip.slice(6);
const item = await foundry.utils.fromUuid(itemUuid);
if (item) {
const isAction = item instanceof game.system.api.models.actions.actionsTypes.base;
const isEffect = item instanceof ActiveEffect;
await this.enrichText(item);
return null;
}
const type = isAction ? 'action' : isEffect ? 'effect' : item.type;
html = await foundry.applications.handlebars.renderTemplate(
`systems/daggerheart/templates/ui/tooltip/${type}.hbs`,
{
item: item,
description: item.system?.enrichedDescription ?? item.enrichedDescription,
config: CONFIG.DH,
allDomains: CONFIG.DH.DOMAIN.allDomains()
}
);
async #activateAttack(element, options) {
const actorUuid = element.dataset.tooltip.slice(8);
const actor = await foundry.utils.fromUuid(actorUuid);
const attack = actor.system.attack;
this.tooltip.innerHTML = html;
options.direction = this._determineItemTooltipDirection(element);
const description = await foundry.applications.ux.TextEditor.enrichHTML(attack.description);
const html = await foundry.applications.handlebars.renderTemplate(
`systems/daggerheart/templates/ui/tooltip/attack.hbs`,
{
attack: attack,
description: description,
parent: actor,
config: CONFIG.DH
}
} else {
const attack = element.dataset.tooltip?.startsWith('#attack#');
if (attack) {
const actorUuid = element.dataset.tooltip.slice(8);
const actor = await foundry.utils.fromUuid(actorUuid);
const attack = actor.system.attack;
);
const description = await TextEditor.enrichHTML(attack.description);
html = await foundry.applications.handlebars.renderTemplate(
`systems/daggerheart/templates/ui/tooltip/attack.hbs`,
{
attack: attack,
description: description,
parent: actor,
config: CONFIG.DH
}
);
this.tooltip.innerHTML = html;
return html;
}
this.tooltip.innerHTML = html;
}
async #activateAdvantageDisadvantage(element, options) {
const isAdvantage = element.dataset.tooltip?.startsWith('#advantage#');
const actorUuid = element.dataset.tooltip.slice(isAdvantage ? 11 : 14);
const actor = await foundry.utils.fromUuid(actorUuid);
const shortRest = element.dataset.tooltip?.startsWith('#shortRest#');
const longRest = element.dataset.tooltip?.startsWith('#longRest#');
if (shortRest || longRest) {
const key = element.dataset.tooltip.slice(shortRest ? 11 : 10);
const moves = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).restMoves[
element.dataset.restType
].moves;
const move = moves[key];
const description = await TextEditor.enrichHTML(move.description);
html = await foundry.applications.handlebars.renderTemplate(
`systems/daggerheart/templates/ui/tooltip/downtime.hbs`,
{
move: move,
description: description
}
);
this.tooltip.innerHTML = html;
options.direction = this._determineItemTooltipDirection(
element,
this.constructor.TOOLTIP_DIRECTIONS.RIGHT
);
}
const isAdvantage = element.dataset.tooltip?.startsWith('#advantage#');
const isDisadvantage = element.dataset.tooltip?.startsWith('#disadvantage#');
if (isAdvantage || isDisadvantage) {
const actorUuid = element.dataset.tooltip.slice(isAdvantage ? 11 : 14);
const actor = await foundry.utils.fromUuid(actorUuid);
if (actor) {
html = await foundry.applications.handlebars.renderTemplate(
`systems/daggerheart/templates/ui/tooltip/advantage.hbs`,
{
sources: isAdvantage ? actor.system.advantageSources : actor.system.disadvantageSources
}
);
this.tooltip.innerHTML = html;
if (actor) {
const html = await foundry.applications.handlebars.renderTemplate(
`systems/daggerheart/templates/ui/tooltip/advantage.hbs`,
{
sources: isAdvantage ? actor.system.advantageSources : actor.system.disadvantageSources
}
}
);
const deathMove = element.dataset.tooltip?.startsWith('#deathMove#');
if (deathMove) {
const name = element.dataset.deathName;
const img = element.dataset.deathImg;
const description = element.dataset.deathDescription;
html = await foundry.applications.handlebars.renderTemplate(
`systems/daggerheart/templates/ui/tooltip/death-move.hbs`,
{
move: { name: name, img: img, description: description }
}
);
this.tooltip.innerHTML = html;
options.direction = this._determineItemTooltipDirection(
element,
this.constructor.TOOLTIP_DIRECTIONS.RIGHT
);
}
this.tooltip.innerHTML = html;
return html;
}
return null;
}
this.noOffset = options.noOffset;
super.activate(element, { ...options, html: html });
async #activateDeathMove(element, options) {
const name = element.dataset.deathName;
const img = element.dataset.deathImg;
const description = element.dataset.deathDescription;
const html = await foundry.applications.handlebars.renderTemplate(
`systems/daggerheart/templates/ui/tooltip/death-move.hbs`,
{
move: { name: name, img: img, description: description }
}
);
this.tooltip.innerHTML = html;
options.direction = this._determineItemTooltipDirection(
element,
this.constructor.TOOLTIP_DIRECTIONS.RIGHT
);
return html;
}
async #activateRest(element, options) {
const isShortRest = element.dataset.tooltip?.startsWith('#shortRest#');
const key = element.dataset.tooltip.slice(isShortRest ? 11 : 10);
const moves = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).restMoves[
element.dataset.restType
].moves;
const move = moves[key];
const description = await foundry.applications.ux.TextEditor.enrichHTML(move.description);
const html = await foundry.applications.handlebars.renderTemplate(
`systems/daggerheart/templates/ui/tooltip/downtime.hbs`,
{
move: move,
description: description
}
);
this.tooltip.innerHTML = html;
options.direction = this._determineItemTooltipDirection(
element,
this.constructor.TOOLTIP_DIRECTIONS.RIGHT
);
return html;
}
_setAnchor(direction) {

View file

@ -15,7 +15,7 @@ export class Migration_2_5_2 extends MigrationHandlerBase {
const srdEffect = srdItem?.effects.find(x => x.name === effectSource.name);
if (change.type === 'custom') {
const srdChange = srdEffect ? srdEffect.system.changes[i] : null;
if (
if (srdChange &&
change.key === srdChange.key &&
change.value === srdChange.value &&
change.type !== srdChange.type