Merged with development

This commit is contained in:
WBHarry 2026-01-12 14:55:53 +01:00
commit 379398f2c7
46 changed files with 968 additions and 73 deletions

View file

@ -2,6 +2,7 @@ import DhpActor from '../../documents/actor.mjs';
import D20RollDialog from '../../applications/dialogs/d20RollDialog.mjs';
import { ActionMixin } from '../fields/actionField.mjs';
import { originItemField } from '../chat-message/actorRoll.mjs';
import TriggerField from '../fields/triggerField.mjs';
const fields = foundry.data.fields;
@ -34,7 +35,8 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
nullable: false,
required: true
}),
targetUuid: new fields.StringField({ initial: undefined })
targetUuid: new fields.StringField({ initial: undefined }),
triggers: new fields.ArrayField(new TriggerField())
};
this.extraSchemas.forEach(s => {
@ -342,6 +344,10 @@ export class ResourceUpdateMap extends Map {
}
addResources(resources) {
if (!resources?.length) return;
const invalidResources = resources.some(resource => !resource.key);
if (invalidResources) return;
for (const resource of resources) {
if (!resource.key) continue;

View file

@ -1,8 +1,11 @@
import BaseDataActor from './base.mjs';
import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs';
import DHEnvironmentSettings from '../../applications/sheets-configs/environment-settings.mjs';
import { RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
export default class DhEnvironment extends BaseDataActor {
scenes = new Set();
/**@override */
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Environment'];
@ -53,6 +56,31 @@ export default class DhEnvironment extends BaseDataActor {
}
isItemValid(source) {
return source.type === "feature";
return source.type === 'feature';
}
_onUpdate(changes, options, userId) {
super._onUpdate(changes, options, userId);
for (const scene of this.scenes) {
scene.render();
}
}
_onDelete(options, userId) {
super._onDelete(options, userId);
for (const scene of this.scenes) {
if (game.user.isActiveGM) {
const newSceneEnvironments = scene.flags.daggerheart.sceneEnvironments.filter(
x => x !== this.parent.uuid
);
scene.update({ 'flags.daggerheart.sceneEnvironments': newSceneEnvironments }).then(() => {
Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Scene });
game.socket.emit(`system.${CONFIG.DH.id}`, {
action: socketEvent.Refresh,
data: { refreshType: RefreshType.TagTeamRoll }
});
});
}
}
}
}

View file

@ -15,8 +15,9 @@ export default class DhCombat extends foundry.abstract.TypeDataModel {
get extendedBattleToggles() {
const modifiers = CONFIG.DH.ENCOUNTER.BPModifiers;
const adversaries =
this.parent.turns?.filter(x => x.isNPC)?.map(x => ({ ...x.actor, type: x.actor.system.type })) ?? [];
const characters = this.parent.turns?.filter(x => !x.isNPC) ?? [];
this.parent.turns?.filter(x => x.actor && x.isNPC)?.map(x => ({ ...x.actor, type: x.actor.system.type })) ??
[];
const characters = this.parent.turns?.filter(x => x.actor && !x.isNPC) ?? [];
const activeAutomatic = Object.keys(modifiers).reduce((acc, categoryKey) => {
const category = modifiers[categoryKey];

View file

@ -2,5 +2,6 @@ export { ActionCollection } from './actionField.mjs';
export { default as FormulaField } from './formulaField.mjs';
export { default as ForeignDocumentUUIDField } from './foreignDocumentUUIDField.mjs';
export { default as ForeignDocumentUUIDArrayField } from './foreignDocumentUUIDArrayField.mjs';
export { default as TriggerField } from './triggerField.mjs';
export { default as MappingField } from './mappingField.mjs';
export * as ActionFields from './action/_module.mjs';

View file

@ -0,0 +1,24 @@
export default class TriggerField extends foundry.data.fields.SchemaField {
constructor(context) {
super(
{
trigger: new foundry.data.fields.StringField({
nullable: false,
blank: false,
initial: CONFIG.DH.TRIGGER.triggers.dualityRoll.id,
choices: CONFIG.DH.TRIGGER.triggers,
label: 'DAGGERHEART.CONFIG.Triggers.triggerType'
}),
triggeringActorType: new foundry.data.fields.StringField({
nullable: false,
blank: false,
initial: CONFIG.DH.TRIGGER.triggerActorTargetType.any.id,
choices: CONFIG.DH.TRIGGER.triggerActorTargetType,
label: 'DAGGERHEART.CONFIG.Triggers.triggeringActorType'
}),
command: new foundry.data.fields.JavaScriptField({ async: true })
},
context
);
}
}

View file

@ -54,6 +54,21 @@ export default class DHArmor extends AttachableItem {
);
}
/**@inheritdoc */
async getDescriptionData() {
const baseDescription = this.description;
const allFeatures = CONFIG.DH.ITEM.allArmorFeatures();
const features = this.armorFeatures.map(x => allFeatures[x.value]);
if (!features.length) return { prefix: null, value: baseDescription, suffix: null };
const prefix = await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/sheets/items/armor/description.hbs',
{ features }
);
return { prefix, value: baseDescription, suffix: null };
}
/**@inheritdoc */
async _preUpdate(changes, options, user) {
const allowed = await super._preUpdate(changes, options, user);

View file

@ -8,7 +8,7 @@
* @property {boolean} isInventoryItem- Indicates whether items of this type is a Inventory Item
*/
import { addLinkedItemsDiff, createScrollText, getScrollTextData, updateLinkedItemApps } from '../../helpers/utils.mjs';
import { addLinkedItemsDiff, getScrollTextData, updateLinkedItemApps } from '../../helpers/utils.mjs';
import { ActionsField } from '../fields/actionField.mjs';
import FormulaField from '../fields/formulaField.mjs';
@ -124,6 +124,33 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
return [source, page ? `pg ${page}.` : null].filter(x => x).join('. ');
}
/**
* 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}
*/
async getDescriptionData(_options) {
return { prefix: null, value: this.description, suffix: null };
}
/**
* 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}
*/
async getEnrichedDescription() {
if (!this.metadata.hasDescription) return '';
const { prefix, value, suffix } = await this.getDescriptionData();
const fullDescription = [prefix, value, suffix].filter(p => !!p).join('\n<hr>\n');
return await foundry.applications.ux.TextEditor.implementation.enrichHTML(fullDescription, {
relativeTo: this,
rollData: this.getRollData(),
secrets: this.isOwner
});
}
/**
* Obtain a data object used to evaluate any dice rolls associated with this Item Type
* @param {object} [options] - Options which modify the getRollData method.
@ -135,6 +162,30 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
return data;
}
prepareBaseData() {
super.prepareBaseData();
for (const action of this.actions ?? []) {
if (!action.actor) continue;
const actionsToRegister = [];
for (let i = 0; i < action.triggers.length; i++) {
const trigger = action.triggers[i];
const { args } = CONFIG.DH.TRIGGER.triggers[trigger.trigger];
const fn = new foundry.utils.AsyncFunction(...args, `{${trigger.command}\n}`);
actionsToRegister.push(fn.bind(action));
if (i === action.triggers.length - 1)
game.system.registeredTriggers.registerTriggers(
trigger.trigger,
action.actor?.uuid,
trigger.triggeringActorType,
this.parent.uuid,
actionsToRegister
);
}
}
}
async _preCreate(data, options, user) {
// Skip if no initial action is required or actions already exist
if (this.metadata.hasInitialAction && foundry.utils.isEmpty(this.actions)) {

View file

@ -110,6 +110,21 @@ export default class DHWeapon extends AttachableItem {
);
}
/**@inheritdoc */
async getDescriptionData() {
const baseDescription = this.description;
const allFeatures = CONFIG.DH.ITEM.allWeaponFeatures();
const features = this.weaponFeatures.map(x => allFeatures[x.value]);
if (!features.length) return { prefix: null, value: baseDescription, suffix: null };
const prefix = await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/sheets/items/weapon/description.hbs',
{ features }
);
return { prefix, value: baseDescription, suffix: null };
}
prepareDerivedData() {
this.attack.roll.trait = this.rules.attack.roll.trait ?? this.attack.roll.trait;
}

View file

@ -1,3 +1,8 @@
import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs';
/* Foundry does not add any system data for subtyped Scenes. The data model is therefore used by instantiating a new instance of it for sceneConfigSettings.mjs.
Needed dataprep and lifetime hooks are handled in documents/scene.
*/
export default class DHScene extends foundry.abstract.DataModel {
static defineSchema() {
const fields = foundry.data.fields;
@ -13,7 +18,8 @@ export default class DHScene extends foundry.abstract.DataModel {
veryClose: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.veryClose.name' }),
close: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.close.name' }),
far: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.far.name' })
})
}),
sceneEnvironments: new ForeignDocumentUUIDArrayField({ type: 'Actor', prune: true })
};
}
}

View file

@ -173,6 +173,13 @@ export default class DhAutomation extends foundry.abstract.DataModel {
label: 'DAGGERHEART.GENERAL.player.plurial'
})
})
}),
triggers: new fields.SchemaField({
enabled: new fields.BooleanField({
nullable: false,
initial: true,
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.triggers.enabled.label'
})
})
};
}