[Fix] Automation Fixes (#342)

* Initial

* Split HopeFear automation by GM and Players

* Fixed ActionToken automation
This commit is contained in:
WBHarry 2025-07-15 15:59:01 +02:00 committed by GitHub
parent 0dd5b53313
commit aa2714e021
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 87 additions and 76 deletions

View file

@ -5,7 +5,7 @@ import * as documents from './module/documents/_module.mjs';
import RegisterHandlebarsHelpers from './module/helpers/handlebarsHelper.mjs'; import RegisterHandlebarsHelpers from './module/helpers/handlebarsHelper.mjs';
import { DhDualityRollEnricher, DhTemplateEnricher } from './module/enrichers/_module.mjs'; import { DhDualityRollEnricher, DhTemplateEnricher } from './module/enrichers/_module.mjs';
import { getCommandTarget, rollCommandToJSON } from './module/helpers/utils.mjs'; import { getCommandTarget, rollCommandToJSON } from './module/helpers/utils.mjs';
import { NarrativeCountdowns, registerCountdownApplicationHooks } from './module/applications/ui/countdowns.mjs'; import { NarrativeCountdowns } from './module/applications/ui/countdowns.mjs';
import { DualityRollColor } from './module/data/settings/Appearance.mjs'; import { DualityRollColor } from './module/data/settings/Appearance.mjs';
import { DHRoll, DualityRoll, D20Roll, DamageRoll, DualityDie } from './module/dice/_module.mjs'; import { DHRoll, DualityRoll, D20Roll, DamageRoll, DualityDie } from './module/dice/_module.mjs';
import { renderDualityButton } from './module/enrichers/DualityRollEnricher.mjs'; import { renderDualityButton } from './module/enrichers/DualityRollEnricher.mjs';
@ -168,7 +168,6 @@ Hooks.on('ready', () => {
registerCountdownHooks(); registerCountdownHooks();
socketRegistration.registerSocketHooks(); socketRegistration.registerSocketHooks();
registerCountdownApplicationHooks();
registerRollDiceHooks(); registerRollDiceHooks();
registerDHActorHooks(); registerDHActorHooks();
}); });

View file

@ -1337,9 +1337,10 @@
"hint": "Automatically increase the GM's fear pool on a fear duality roll result." "hint": "Automatically increase the GM's fear pool on a fear duality roll result."
}, },
"FIELDS": { "FIELDS": {
"hope": { "hopeFear": {
"label": "Hope", "label": "Hope & Fear",
"hint": "Automatically increase a character's hope on a hope duality roll result." "gm": { "label": "GM" },
"players": { "label": "Players" }
}, },
"actionPoints": { "actionPoints": {
"label": "Action Points", "label": "Action Points",

View file

@ -1,4 +1,4 @@
export default class DHTokenHUD extends TokenHUD { export default class DHTokenHUD extends foundry.applications.hud.TokenHUD {
static DEFAULT_OPTIONS = { static DEFAULT_OPTIONS = {
classes: ['daggerheart'] classes: ['daggerheart']
}; };

View file

@ -669,10 +669,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
} else if (item instanceof ActiveEffect) { } else if (item instanceof ActiveEffect) {
item.toChat(this); item.toChat(this);
} else { } else {
const wasUsed = await item.use(event); item.use(event);
if (wasUsed && item.type === 'weapon') {
Hooks.callAll(CONFIG.DH.HOOKS.characterAttack, {});
}
} }
} }

View file

@ -66,6 +66,11 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
} }
async setCombatantSpotlight(combatantId) { async setCombatantSpotlight(combatantId) {
const update = {
system: {
'spotlight.requesting': false
}
};
const combatant = this.viewed.combatants.get(combatantId); const combatant = this.viewed.combatants.get(combatantId);
const toggleTurn = this.viewed.combatants.contents const toggleTurn = this.viewed.combatants.contents
@ -73,10 +78,18 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
.map(x => x.id) .map(x => x.id)
.indexOf(combatantId); .indexOf(combatantId);
if (this.viewed.turn !== toggleTurn) Hooks.callAll(CONFIG.DH.HOOKS.spotlight, {}); if (this.viewed.turn !== toggleTurn) {
const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns;
await updateCountdowns(CONFIG.DH.GENERAL.countdownTypes.spotlight.id);
const autoPoints = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).actionPoints;
if (autoPoints) {
update.system.actionTokens = Math.max(combatant.system.actionTokens - 1, 0);
}
}
await this.viewed.update({ turn: this.viewed.turn === toggleTurn ? null : toggleTurn }); await this.viewed.update({ turn: this.viewed.turn === toggleTurn ? null : toggleTurn });
await combatant.update({ 'system.spotlight.requesting': false }); await combatant.update(update);
} }
static async requestSpotlight(_, target) { static async requestSpotlight(_, target) {

View file

@ -1,4 +1,3 @@
import { countdownTypes } from '../../config/generalConfig.mjs';
import { GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; import { GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
import constructHTMLButton from '../../helpers/utils.mjs'; import constructHTMLButton from '../../helpers/utils.mjs';
import OwnershipSelection from '../dialogs/ownershipSelection.mjs'; import OwnershipSelection from '../dialogs/ownershipSelection.mjs';
@ -328,43 +327,29 @@ export class EncounterCountdowns extends Countdowns {
}; };
} }
export const registerCountdownApplicationHooks = () => { export async function updateCountdowns(progressType) {
const updateCountdowns = async shouldProgress => { const countdownSetting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns);
if (game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).countdowns) { const update = Object.keys(countdownSetting).reduce((update, typeKey) => {
const countdownSetting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); return foundry.utils.mergeObject(
for (let countdownCategoryKey in countdownSetting) { update,
const countdownCategory = countdownSetting[countdownCategoryKey]; Object.keys(countdownSetting[typeKey].countdowns).reduce((acc, countdownKey) => {
for (let countdownKey in countdownCategory.countdowns) { const countdown = countdownSetting[typeKey].countdowns[countdownKey];
const countdown = countdownCategory.countdowns[countdownKey]; if (countdown.progress.current > 0 && countdown.progress.type.value === progressType) {
acc[`${typeKey}.countdowns.${countdownKey}.progress.current`] = countdown.progress.current - 1;
if (shouldProgress(countdown)) {
await countdownSetting.updateSource({
[`${countdownCategoryKey}.countdowns.${countdownKey}.progress.current`]:
countdown.progress.current - 1
});
await game.settings.set(
CONFIG.DH.id,
CONFIG.DH.SETTINGS.gameSettings.Countdowns,
countdownSetting
);
foundry.applications.instances.get(`${countdownCategoryKey}-countdowns`)?.render();
}
} }
}
}
};
Hooks.on(CONFIG.DH.HOOKS.characterAttack, async () => { return acc;
updateCountdowns(countdown => { }, {})
return ( );
countdown.progress.type.value === countdownTypes.characterAttack.id && countdown.progress.current > 0 }, {});
);
});
});
Hooks.on(CONFIG.DH.HOOKS.spotlight, async () => { await countdownSetting.updateSource(update);
updateCountdowns(countdown => { await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, countdownSetting);
return countdown.progress.type.value === countdownTypes.spotlight.id && countdown.progress.current > 0;
}); const data = { refreshType: RefreshType.Countdown };
await game.socket.emit(`system.${CONFIG.DH.id}`, {
action: socketEvent.Refresh,
data
}); });
}; Hooks.callAll(socketEvent.Refresh, data);
}

View file

@ -4,7 +4,6 @@ export * as domainConfig from './domainConfig.mjs';
export * as effectConfig from './effectConfig.mjs'; export * as effectConfig from './effectConfig.mjs';
export * as flagsConfig from './flagsConfig.mjs'; export * as flagsConfig from './flagsConfig.mjs';
export * as generalConfig from './generalConfig.mjs'; export * as generalConfig from './generalConfig.mjs';
export * as hooksConfig from './hooksConfig.mjs';
export * as itemConfig from './itemConfig.mjs'; export * as itemConfig from './itemConfig.mjs';
export * as settingsConfig from './settingsConfig.mjs'; export * as settingsConfig from './settingsConfig.mjs';
export * as systemConfig from './system.mjs'; export * as systemConfig from './system.mjs';

View file

@ -376,15 +376,15 @@ export const abilityCosts = {
export const countdownTypes = { export const countdownTypes = {
spotlight: { spotlight: {
id: 'spotlight', id: 'spotlight',
label: 'DAGGERHEART.CONFIG.CountdownTypes.Spotlight' label: 'DAGGERHEART.CONFIG.CountdownType.spotlight'
}, },
characterAttack: { characterAttack: {
id: 'characterAttack', id: 'characterAttack',
label: 'DAGGERHEART.CONFIG.CountdownTypes.CharacterAttack' label: 'DAGGERHEART.CONFIG.CountdownType.characterAttack'
}, },
custom: { custom: {
id: 'custom', id: 'custom',
label: 'DAGGERHEART.CONFIG.CountdownTypes.Custom' label: 'DAGGERHEART.CONFIG.CountdownType.custom'
} }
}; };
export const rollTypes = { export const rollTypes = {

View file

@ -1,4 +0,0 @@
export const hooks = {
characterAttack: 'characterAttackHook',
spotlight: 'spotlightHook'
};

View file

@ -3,7 +3,6 @@ import * as DOMAIN from './domainConfig.mjs';
import * as ACTOR from './actorConfig.mjs'; import * as ACTOR from './actorConfig.mjs';
import * as ITEM from './itemConfig.mjs'; import * as ITEM from './itemConfig.mjs';
import * as SETTINGS from './settingsConfig.mjs'; import * as SETTINGS from './settingsConfig.mjs';
import { hooks as HOOKS } from './hooksConfig.mjs';
import * as EFFECTS from './effectConfig.mjs'; import * as EFFECTS from './effectConfig.mjs';
import * as ACTIONS from './actionConfig.mjs'; import * as ACTIONS from './actionConfig.mjs';
import * as FLAGS from './flagsConfig.mjs'; import * as FLAGS from './flagsConfig.mjs';
@ -17,7 +16,6 @@ export const SYSTEM = {
ACTOR, ACTOR,
ITEM, ITEM,
SETTINGS, SETTINGS,
HOOKS,
EFFECTS, EFFECTS,
ACTIONS, ACTIONS,
FLAGS FLAGS

View file

@ -38,6 +38,15 @@ export default class DHAttackAction extends DHDamageAction {
}; };
} }
async use(event, ...args) {
const result = await super.use(event, args);
const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns;
await updateCountdowns(CONFIG.DH.GENERAL.countdownTypes.characterAttack.id);
return result;
}
// get modifiers() { // get modifiers() {
// return []; // return [];
// } // }

View file

@ -102,7 +102,7 @@ class DhCountdown extends foundry.abstract.DataModel {
value: new fields.StringField({ value: new fields.StringField({
required: true, required: true,
choices: CONFIG.DH.GENERAL.countdownTypes, choices: CONFIG.DH.GENERAL.countdownTypes,
initial: CONFIG.DH.GENERAL.countdownTypes.spotlight.id, initial: CONFIG.DH.GENERAL.countdownTypes.custom.id,
label: 'DAGGERHEART.APPLICATIONS.Countdown.FIELDS.countdowns.element.progress.type.value.label' label: 'DAGGERHEART.APPLICATIONS.Countdown.FIELDS.countdowns.element.progress.type.value.label'
}), }),
label: new fields.StringField({ label: new fields.StringField({
@ -132,7 +132,13 @@ class DhCountdown extends foundry.abstract.DataModel {
export const registerCountdownHooks = () => { export const registerCountdownHooks = () => {
Hooks.on(socketEvent.Refresh, ({ refreshType, application }) => { Hooks.on(socketEvent.Refresh, ({ refreshType, application }) => {
if (refreshType === RefreshType.Countdown) { if (refreshType === RefreshType.Countdown) {
foundry.applications.instances.get(application)?.render(); if (application) {
foundry.applications.instances.get(application)?.render();
} else {
foundry.applications.instances.get('narrative-countdowns').render();
foundry.applications.instances.get('encounter-countdowns').render();
}
return false; return false;
} }
}); });

View file

@ -4,20 +4,22 @@ export default class DhAutomation extends foundry.abstract.DataModel {
static defineSchema() { static defineSchema() {
const fields = foundry.data.fields; const fields = foundry.data.fields;
return { return {
hope: new fields.BooleanField({ hopeFear: new fields.SchemaField({
required: true, gm: new fields.BooleanField({
initial: false, required: true,
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.hope.label' initial: false,
}), // Label need to be updated into something like "Duality Roll Auto Gain" + a hint label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.hopeFear.gm.label'
}),
players: new fields.BooleanField({
required: true,
initial: false,
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.hopeFear.players.label'
})
}),
actionPoints: new fields.BooleanField({ actionPoints: new fields.BooleanField({
required: true, required: true,
initial: false, initial: false,
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.actionPoints.label' label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.actionPoints.label'
}),
countdowns: new fields.BooleanField({
requireD: true,
initial: false,
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.countdowns.label'
}) })
}; };
} }

View file

@ -175,9 +175,10 @@ export default class DHRoll extends Roll {
export const registerRollDiceHooks = () => { export const registerRollDiceHooks = () => {
Hooks.on(`${CONFIG.DH.id}.postRollDuality`, async (config, message) => { Hooks.on(`${CONFIG.DH.id}.postRollDuality`, async (config, message) => {
const hopeFearAutomation = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).hopeFear;
if ( if (
!config.source?.actor || !config.source?.actor ||
!game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).hope || (game.user.isGM ? !hopeFearAutomation.gm : !hopeFearAutomation.players) ||
config.roll.type === 'reaction' config.roll.type === 'reaction'
) )
return; return;
@ -185,9 +186,9 @@ export const registerRollDiceHooks = () => {
const actor = await fromUuid(config.source.actor), const actor = await fromUuid(config.source.actor),
updates = []; updates = [];
if (!actor) return; if (!actor) return;
if (config.roll.isCritical || config.roll.result.duality === 1) updates.push({ type: 'hope', value: 1 }); if (config.roll.isCritical || config.roll.result.duality === 1) updates.push({ key: 'hope', value: 1 });
if (config.roll.isCritical) updates.push({ type: 'stress', value: -1 }); if (config.roll.isCritical) updates.push({ key: 'stress', value: -1 });
if (config.roll.result.duality === -1) updates.push({ type: 'fear', value: 1 }); if (config.roll.result.duality === -1) updates.push({ key: 'fear', value: 1 });
if (updates.length) actor.modifyResource(updates); if (updates.length) actor.modifyResource(updates);

View file

@ -1,5 +1,10 @@
<div> <div>
{{formGroup settingFields.schema.fields.hope value=settingFields._source.hope localize=true}} <div class="form-group">
<label>{{localize "DAGGERHEART.SETTINGS.Automation.FIELDS.hopeFear.label"}}</label>
{{formGroup settingFields.schema.fields.hopeFear.fields.gm value=settingFields._source.hopeFear.gm localize=true}}
{{formGroup settingFields.schema.fields.hopeFear.fields.players value=settingFields._source.hopeFear.players localize=true}}
</div>
{{formGroup settingFields.schema.fields.actionPoints value=settingFields._source.actionPoints localize=true}} {{formGroup settingFields.schema.fields.actionPoints value=settingFields._source.actionPoints localize=true}}
{{formGroup settingFields.schema.fields.countdowns value=settingFields._source.countdowns localize=true}} {{formGroup settingFields.schema.fields.countdowns value=settingFields._source.countdowns localize=true}}