From 16173363d4450f4e0c9856b2bc58a1317bd85f8e Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 24 Aug 2025 02:24:57 +0200 Subject: [PATCH] [Feature] 687 - Companion Improvements (#1049) * Companions can't be put into CombatState anymore. Companions now have a Action Roll button * Added handling for multiselect toggleCombat --- module/applications/hud/tokenHUD.mjs | 25 +++++++++- .../applications/sheets/actors/companion.mjs | 47 +++++++++++++++++++ .../less/sheets/actors/companion/details.less | 13 +++++ templates/sheets/actors/companion/details.hbs | 3 ++ 4 files changed, 87 insertions(+), 1 deletion(-) diff --git a/module/applications/hud/tokenHUD.mjs b/module/applications/hud/tokenHUD.mjs index 5c29260b..48d5ac89 100644 --- a/module/applications/hud/tokenHUD.mjs +++ b/module/applications/hud/tokenHUD.mjs @@ -1,6 +1,9 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD { static DEFAULT_OPTIONS = { - classes: ['daggerheart'] + classes: ['daggerheart'], + actions: { + combat: DHTokenHUD.#onToggleCombat + } }; /** @override */ @@ -11,8 +14,14 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD { } }; + static #nonCombatTypes = ['environment', 'companion']; + async _prepareContext(options) { const context = await super._prepareContext(options); + + context.canToggleCombat = DHTokenHUD.#nonCombatTypes.includes(this.actor.type) + ? false + : context.canToggleCombat; context.systemStatusEffects = Object.keys(context.statusEffects).reduce((acc, key) => { const effect = context.statusEffects[key]; if (effect.systemEffect) acc[key] = effect; @@ -36,6 +45,20 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD { return context; } + static async #onToggleCombat() { + const tokens = canvas.tokens.controlled + .filter(t => !t.actor || !DHTokenHUD.#nonCombatTypes.includes(t.actor.type)) + .map(t => t.document); + if (!this.object.controlled) tokens.push(this.document); + + try { + if (this.document.inCombat) await TokenDocument.implementation.deleteCombatants(tokens); + else await TokenDocument.implementation.createCombatants(tokens); + } catch (err) { + ui.notifications.warn(err.message); + } + } + _getStatusEffectChoices() { // Include all HUD-enabled status effects const choices = {}; diff --git a/module/applications/sheets/actors/companion.mjs b/module/applications/sheets/actors/companion.mjs index 2b82f50a..82aee312 100644 --- a/module/applications/sheets/actors/companion.mjs +++ b/module/applications/sheets/actors/companion.mjs @@ -8,6 +8,7 @@ export default class DhCompanionSheet extends DHBaseActorSheet { classes: ['actor', 'companion'], position: { width: 340 }, actions: { + actionRoll: DhCompanionSheet.#actionRoll, levelManagement: DhCompanionSheet.#levelManagement } }; @@ -45,6 +46,52 @@ export default class DhCompanionSheet extends DHBaseActorSheet { /* Application Clicks Actions */ /* -------------------------------------------- */ + /** + * + */ + static async #actionRoll(event) { + const partner = this.actor.system.partner; + const config = { + event, + title: `${game.i18n.localize('DAGGERHEART.GENERAL.Roll.action')}: ${this.actor.name}`, + headerTitle: `Companion ${game.i18n.localize('DAGGERHEART.GENERAL.Roll.action')}`, + roll: { + trait: partner.system.spellcastModifierTrait?.key + }, + hasRoll: true, + data: partner.getRollData() + }; + + const result = await partner.diceRoll(config); + this.consumeResource(result?.costs); + } + + // Remove when Action Refactor part #2 done + async consumeResource(costs) { + if (!costs?.length) return; + + const partner = this.actor.system.partner; + const usefulResources = { + ...foundry.utils.deepClone(partner.system.resources), + fear: { + value: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear), + max: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear, + reversed: false + } + }; + const resources = game.system.api.fields.ActionFields.CostField.getRealCosts(costs).map(c => { + const resource = usefulResources[c.key]; + return { + key: c.key, + value: (c.total ?? c.value) * (resource.isReversed ? 1 : -1), + target: resource.target, + keyIsID: resource.keyIsID + }; + }); + + await partner.modifyResource(resources); + } + /** * Opens the companions level management window. * @type {ApplicationClickAction} diff --git a/styles/less/sheets/actors/companion/details.less b/styles/less/sheets/actors/companion/details.less index 2df14b23..cbdc25e6 100644 --- a/styles/less/sheets/actors/companion/details.less +++ b/styles/less/sheets/actors/companion/details.less @@ -77,4 +77,17 @@ } } } + + .action-section { + display: flex; + padding: 0 10px; + margin-top: 20px; + width: 100%; + + button { + height: 40px; + width: 100%; + font-weight: 600; + } + } } diff --git a/templates/sheets/actors/companion/details.hbs b/templates/sheets/actors/companion/details.hbs index dd28e745..eee2122c 100644 --- a/templates/sheets/actors/companion/details.hbs +++ b/templates/sheets/actors/companion/details.hbs @@ -56,4 +56,7 @@ {{/each}} +
+ +
\ No newline at end of file