This commit is contained in:
WBHarry 2026-01-02 21:07:05 +01:00
parent bca7e0d3c9
commit 36eac51041
14 changed files with 171 additions and 8 deletions

View file

@ -84,6 +84,8 @@ Hooks.once('init', () => {
fields
};
game.system.registeredTriggers = new RegisteredTriggers();
const { DocumentSheetConfig } = foundry.applications.apps;
DocumentSheetConfig.unregisterSheet(TokenDocument, 'core', foundry.applications.sheets.TokenConfig);
DocumentSheetConfig.registerSheet(TokenDocument, SYSTEM.id, applications.sheetConfigs.DhTokenConfig, {
@ -378,3 +380,33 @@ Hooks.on('moveToken', async (movedToken, data) => {
Hooks.on('renderCompendiumDirectory', (app, html) => applications.ui.ItemBrowser.injectSidebarButton(html));
Hooks.on('renderDocumentDirectory', (app, html) => applications.ui.ItemBrowser.injectSidebarButton(html));
class RegisteredTriggers extends Map {
constructor() {
super();
}
async registerTriggers(trigger, actor, uuid, commands) {
const existingTrigger = this.get(trigger);
if (!existingTrigger) this.set(trigger, new Map());
this.get(trigger).set(uuid, { actor, commands });
}
async runTrigger(trigger, currentActor, ...args) {
const updates = [];
const dualityTrigger = this.get(trigger);
if (dualityTrigger) {
for (let { actor, commands } of dualityTrigger.values()) {
if (currentActor?.uuid !== actor) continue;
for (let command of commands) {
const commandUpdates = await command(...args);
if (commandUpdates?.length) updates.push(...commandUpdates);
}
}
}
return updates;
}
}

View file

@ -1217,6 +1217,10 @@
}
}
},
"Triggers": {
"dualityRoll": "Duality Roll",
"fearRoll": "Fear Roll"
},
"WeaponFeature": {
"barrier": {
"name": "Barrier",
@ -2057,7 +2061,8 @@
"itemFeatures": "Item Features",
"questions": "Questions",
"configuration": "Configuration",
"base": "Base"
"base": "Base",
"triggers": "Triggers"
},
"Tiers": {
"singular": "Tier",

View file

@ -29,7 +29,9 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
removeElement: this.removeElement,
editEffect: this.editEffect,
addDamage: this.addDamage,
removeDamage: this.removeDamage
removeDamage: this.removeDamage,
addTrigger: this.addTrigger,
removeTrigger: this.removeTrigger
},
form: {
handler: this.updateForm,
@ -55,6 +57,10 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
effect: {
id: 'effect',
template: 'systems/daggerheart/templates/sheets-settings/action-settings/effect.hbs'
},
trigger: {
id: 'trigger',
template: 'systems/daggerheart/templates/sheets-settings/action-settings/trigger.hbs'
}
};
@ -82,6 +88,14 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
id: 'effect',
icon: null,
label: 'DAGGERHEART.GENERAL.Tabs.effects'
},
trigger: {
active: false,
cssClass: '',
group: 'primary',
id: 'trigger',
icon: null,
label: 'DAGGERHEART.GENERAL.Tabs.triggers'
}
};
@ -111,6 +125,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
context.baseSaveDifficulty = this.action.actor?.baseSaveDifficulty;
context.baseAttackBonus = this.action.actor?.system.attack?.roll.bonus;
context.hasRoll = this.action.hasRoll;
context.triggerOptions = CONFIG.DH.TRIGGER.triggers;
const settingsTiers = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers;
context.tierOptions = [
@ -224,6 +239,18 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
}
static addTrigger() {
const data = this.action.toObject();
data.triggers.push({ hook: CONFIG.DH.TRIGGER.triggers.dualityRoll.id });
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
}
static removeTrigger(_event, button) {
const data = this.action.toObject();
data.triggers = data.triggers.filter((_, index) => index !== Number.parseInt(button.dataset.index));
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
}
/** Specific implementation in extending classes **/
static async addEffect(_event) {}
static removeEffect(_event, _button) {}

View file

@ -10,3 +10,4 @@ export * as itemConfig from './itemConfig.mjs';
export * as settingsConfig from './settingsConfig.mjs';
export * as systemConfig from './system.mjs';
export * as itemBrowserConfig from './itemBrowserConfig.mjs';
export * as triggerConfig from './triggerConfig.mjs';

View file

@ -1,5 +1,3 @@
const hooksConfig = {
export const hooksConfig = {
effectDisplayToggle: 'DHEffectDisplayToggle'
};
export default hooksConfig;

View file

@ -7,7 +7,8 @@ import * as SETTINGS from './settingsConfig.mjs';
import * as EFFECTS from './effectConfig.mjs';
import * as ACTIONS from './actionConfig.mjs';
import * as FLAGS from './flagsConfig.mjs';
import HOOKS from './hooksConfig.mjs';
import * as HOOKS from './hooksConfig.mjs';
import * as TRIGGER from './triggerConfig.mjs';
import * as ITEMBROWSER from './itemBrowserConfig.mjs';
export const SYSTEM_ID = 'daggerheart';
@ -24,5 +25,6 @@ export const SYSTEM = {
ACTIONS,
FLAGS,
HOOKS,
TRIGGER,
ITEMBROWSER
};

View file

@ -0,0 +1,10 @@
export const triggers = {
dualityRoll: {
id: 'dualityRoll',
label: 'DAGGERHEART.CONFIG.Triggers.dualityRoll'
},
fearRoll: {
id: 'fearRoll',
label: 'DAGGERHEART.CONFIG.Triggers.fearRoll'
}
};

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 => {
@ -343,6 +345,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

@ -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,15 @@
export default class TriggerField extends foundry.data.fields.SchemaField {
constructor(context) {
super(
{
trigger: new foundry.data.fields.StringField({
nullable: false,
initial: CONFIG.DH.TRIGGER.triggers.dualityRoll.id,
choices: CONFIG.DH.TRIGGER.triggers
}),
command: new foundry.data.fields.JavaScriptField({ async: true })
},
context
);
}
}

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';
@ -135,6 +135,26 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
return data;
}
prepareBaseData() {
super.prepareBaseData();
for (const action of this.actions) {
const actionsToRegister = [];
for (let i = 0; i < action.triggers.length; i++) {
const trigger = action.triggers[i];
const fn = new foundry.utils.AsyncFunction('roll', 'actor', `{${trigger.command}\n}`);
actionsToRegister.push(fn.bind(action));
if (i === action.triggers.length - 1)
game.system.registeredTriggers.registerTriggers(
trigger.trigger,
action.actor?.uuid,
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

@ -224,6 +224,30 @@ export default class DualityRoll extends D20Roll {
await super.buildPost(roll, config, message);
await DualityRoll.dualityUpdate(config);
await DualityRoll.handleTriggers(roll, config);
}
static async handleTriggers(roll, config) {
const updates = [];
const dualityUpdates = await game.system.registeredTriggers.runTrigger(
CONFIG.DH.TRIGGER.triggers.dualityRoll.id,
roll.data?.parent,
roll,
roll.data?.parent
);
if (dualityUpdates?.length) updates.push(...dualityUpdates);
if (config.roll.result.duality === -1) {
const fearUpdates = await game.system.registeredTriggers.runTrigger(
CONFIG.DH.TRIGGER.triggers.fearRoll.id,
roll.data?.parent,
roll,
roll.data?.parent
);
if (fearUpdates?.length) updates.push(...fearUpdates);
}
config.resourceUpdates.addResources(updates);
}
static async addDualityResourceUpdates(config) {

View file

@ -545,6 +545,10 @@
font-size: var(--font-size-12);
padding-left: 3px;
}
code-mirror {
width: 100%;
}
}
.application.setting.dh-style {

View file

@ -0,0 +1,18 @@
<section
class="tab {{this.tabs.trigger.cssClass}}"
data-group="primary"
data-tab="trigger"
>
<button data-action="addTrigger">{{localize "Add Trigger"}} <i class="fa-solid fa-plus icon-button"></i></button>
{{#each @root.source.triggers as |trigger index|}}
<fieldset class="one-column">
<legend><a data-action="removeTrigger" data-index="{{index}}"><i class="fa-solid fa-trash"></i></a></legend>
<select id="triggerOptionSelect">
{{selectOptions @root.triggerOptions seleced=trigger.trigger localize=true}}
</select>
{{formInput @root.fields.triggers.element.fields.command value=trigger.command elementType="code-mirror" name=(concat "triggers." index ".command") aria=(object label=(localize "Test"))}}
</fieldset>
{{/each}}
</section>