Merged with development

This commit is contained in:
WBHarry 2026-01-13 16:30:08 +01:00
commit c32e812803
120 changed files with 2380 additions and 469 deletions

View file

@ -8,3 +8,4 @@ export { default as DhScene } from './scene.mjs';
export { default as DhToken } from './token.mjs';
export { default as DhTooltipManager } from './tooltipManager.mjs';
export { default as DhTemplateManager } from './templateManager.mjs';
export { default as DhTokenManager } from './tokenManager.mjs';

View file

@ -20,7 +20,10 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
}
if (this.parent?.type === 'domainCard') {
return this.parent.system.inVault;
const isVaultSupressed = this.parent.system.isVaultSupressed;
const domainTouchedSupressed = this.parent.system.isDomainTouchedSuppressed;
return isVaultSupressed || domainTouchedSupressed;
}
return super.isSuppressed;
@ -106,23 +109,29 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
/**@inheritdoc*/
static applyField(model, change, field) {
const isOriginTarget = change.value.toLowerCase().includes('origin.@');
change.value = DhActiveEffect.getChangeValue(model, change, change.effect);
super.applyField(model, change, field);
}
/** */
static getChangeValue(model, change, effect) {
let value = change.value;
const isOriginTarget = value.toLowerCase().includes('origin.@');
let parseModel = model;
if (isOriginTarget && change.effect.origin) {
change.value = change.value.replaceAll(/origin\.@/gi, '@');
if (isOriginTarget && effect.origin) {
value = change.value.replaceAll(/origin\.@/gi, '@');
try {
const effect = foundry.utils.fromUuidSync(change.effect.origin);
const originEffect = foundry.utils.fromUuidSync(effect.origin);
const doc =
effect.parent?.parent instanceof game.system.api.documents.DhpActor
? effect.parent
: effect.parent.parent;
originEffect.parent?.parent instanceof game.system.api.documents.DhpActor
? originEffect.parent
: originEffect.parent.parent;
if (doc) parseModel = doc;
} catch (_) {}
}
const evalValue = this.effectSafeEval(itemAbleRollParse(change.value, parseModel, change.effect.parent));
change.value = evalValue ?? change.value;
super.applyField(model, change, field);
const evalValue = this.effectSafeEval(itemAbleRollParse(value, parseModel, effect.parent));
return evalValue ?? value;
}
/**

View file

@ -646,6 +646,19 @@ export default class DhpActor extends Actor {
}
}
const results = await game.system.registeredTriggers.runTrigger(
CONFIG.DH.TRIGGER.triggers.postDamageReduction.id,
this,
updates,
this
);
if (results?.length) {
const resourceMap = new ResourceUpdateMap(results[0].originActor);
for (var result of results) resourceMap.addResources(result.updates);
resourceMap.updateResources();
}
updates.forEach(
u =>
(u.value =

View file

@ -166,7 +166,10 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
event.stopPropagation();
const config = foundry.utils.deepClone(this.system);
config.event = event;
await this.system.action?.workflow.get('damage')?.execute(config, this._id, true);
if (this.system.action) {
await this.system.action.addEffects(config);
await this.system.action.workflow.get('damage')?.execute(config, this._id, true);
}
Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.TagTeamRoll });
await game.socket.emit(`system.${CONFIG.DH.id}`, {

View file

@ -31,7 +31,7 @@ export default class DHItem extends foundry.documents.Item {
static async createDocuments(sources, operation) {
// Ensure that items being created are valid to the actor its being added to
const actor = operation.parent;
sources = actor?.system?.isItemValid ? sources.filter((s) => actor.system.isItemValid(s)) : sources;
sources = actor?.system?.isItemValid ? sources.filter(s => actor.system.isItemValid(s)) : sources;
return super.createDocuments(sources, operation);
}
@ -146,6 +146,16 @@ export default class DHItem extends foundry.documents.Item {
/* -------------------------------------------- */
async use(event) {
/* DomainCard check. Can be expanded or made neater */
if (this.system.isDomainTouchedSuppressed) {
return ui.notifications.warn(
game.i18n.format('DAGGERHEART.UI.Notifications.domainTouchRequirement', {
nr: this.domainTouched,
domain: game.i18n.localize(CONFIG.DH.DOMAIN.allDomains()[this.domain].label)
})
);
}
const actions = new Set(this.system.actionsList);
if (actions?.size) {
let action = actions.first();

View file

@ -37,4 +37,30 @@ export default class DhScene extends Scene {
this.#sizeSyncBatch.clear();
this.updateEmbeddedDocuments('Token', entries, { animation: { movementSpeed: 1.5 } });
}, 0);
prepareBaseData() {
super.prepareBaseData();
if (this instanceof game.system.api.documents.DhScene) {
const system = new game.system.api.data.scenes.DHScene(this.flags.daggerheart);
// Register this scene to all environements
for (const environment of system.sceneEnvironments) {
environment.system.scenes?.add(this);
}
}
}
_onDelete(options, userId) {
super._onDelete(options, userId);
if (this instanceof game.system.api.documents.DhScene) {
const system = new game.system.api.data.scenes.DHScene(this.flags.daggerheart);
// Clear this scene from all environments that aren't deleted
for (const environment of system.sceneEnvironments) {
environment?.system?.scenes?.delete(this);
}
}
}
}

View file

@ -0,0 +1,104 @@
/**
* A singleton class that handles preview tokens.
*/
export default class DhTokenManager {
#activePreview;
#actor;
#resolve;
/**
* Create a template preview, deactivating any existing ones.
* @param {object} data
*/
async createPreview(actor, tokenData) {
this.#actor = actor;
const token = await canvas.tokens._createPreview(
{
...actor.prototypeToken,
displayName: 50,
...tokenData
},
{ renderSheet: false, actor }
);
this.#activePreview = {
document: token.document,
object: token,
origin: { x: token.document.x, y: token.document.y }
};
this.#activePreview.events = {
contextmenu: this.#cancelTemplate.bind(this),
mousedown: this.#confirmTemplate.bind(this),
mousemove: this.#onDragMouseMove.bind(this)
};
canvas.stage.on('mousemove', this.#activePreview.events.mousemove);
canvas.stage.on('mousedown', this.#activePreview.events.mousedown);
canvas.app.view.addEventListener('contextmenu', this.#activePreview.events.contextmenu);
}
/* Currently intended for using as a preview of where to create a token. (note the flag) */
async createPreviewAsync(actor, tokenData = {}) {
return new Promise(resolve => {
this.#resolve = resolve;
this.createPreview(actor, { ...tokenData, flags: { daggerheart: { createPlacement: true } } });
});
}
/**
* Handles the movement of the token preview on mousedrag.
* @param {mousemove Event} event
*/
#onDragMouseMove(event) {
event.stopPropagation();
const { moveTime, object } = this.#activePreview;
const update = {};
const now = Date.now();
if (now - (moveTime || 0) <= 16) return;
this.#activePreview.moveTime = now;
let cursor = event.getLocalPosition(canvas.templates);
Object.assign(update, canvas.grid.getTopLeftPoint(cursor));
object.document.updateSource(update);
object.renderFlags.set({ refresh: true });
}
/**
* Cancels the preview token on right-click.
* @param {contextmenu Event} event
*/
#cancelTemplate(_event, resolved) {
const { mousemove, mousedown, contextmenu } = this.#activePreview.events;
this.#activePreview.object.destroy();
canvas.stage.off('mousemove', mousemove);
canvas.stage.off('mousedown', mousedown);
canvas.app.view.removeEventListener('contextmenu', contextmenu);
if (this.#resolve && !resolved) this.#resolve(false);
}
/**
* Creates a real Actor and token at the preview location and cancels the preview.
* @param {click Event} event
*/
async #confirmTemplate(event) {
event.stopPropagation();
this.#cancelTemplate(event, true);
const actor = this.#actor.inCompendium
? await game.system.api.documents.DhpActor.create(this.#actor.toObject())
: this.#actor;
const tokenData = await actor.getTokenDocument();
const result = await canvas.scene.createEmbeddedDocuments('Token', [
{ ...tokenData, x: this.#activePreview.document.x, y: this.#activePreview.document.y }
]);
this.#activePreview = undefined;
if (this.#resolve && result.length) this.#resolve(result[0]);
}
}

View file

@ -220,12 +220,15 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
for (const [index, itemValue] of pathValue.entries()) {
const itemIsAction = itemValue instanceof game.system.api.models.actions.actionsTypes.base;
const value = itemIsAction || !itemValue?.item ? itemValue : itemValue.item;
const enrichedValue = await TextEditor.enrichHTML(value.system?.description ?? value.description);
const enrichedValue =
(await value.system?.getEnrichedDescription?.()) ??
(await TextEditor.enrichHTML(value.system?.description ?? value.description));
if (itemIsAction) value.enrichedDescription = enrichedValue;
else foundry.utils.setProperty(item, `${basePath}.${index}.enrichedDescription`, enrichedValue);
}
} else {
const enrichedValue = await TextEditor.enrichHTML(pathValue);
const enrichedValue =
(await item.system?.getEnrichedDescription?.()) ?? (await TextEditor.enrichHTML(pathValue));
foundry.utils.setProperty(
item,
`${data.path ? `${data.path}.` : ''}enriched${data.name.capitalize()}`,