From 8e8c0de4878915cf1d8854582943176d7ce485e1 Mon Sep 17 00:00:00 2001 From: Christian Sagel Date: Thu, 30 Jan 2025 20:44:27 +0100 Subject: [PATCH 01/18] Add damage breakdown to chat message --- lang/en.json | 9 ++- module/pipelines/damage-pipeline.mjs | 7 ++- templates/chat/chat-apply-damage.hbs | 62 +++++++++++++++++++ .../chat/partials/inline-damage-icon.hbs | 2 +- 4 files changed, 75 insertions(+), 5 deletions(-) diff --git a/lang/en.json b/lang/en.json index 2711a9b6..b99aea9d 100644 --- a/lang/en.json +++ b/lang/en.json @@ -852,9 +852,9 @@ "ChatApplyMaxTargetWarning": "Too many actors targeted!", "ChatApplyDamageTooltip": "Click to apply damage to selected tokens.
[Ctrl + Click] to Open Damage Customizer
[Shift + Click] to ignore Resistances.
[Ctrl + Shift] + Click to ignore Resistances and Immunities.", "ChatApplyDamageNoActorsSelected": "No actors selected. Unable to apply damage.", - "ChatApplyDamageVulnerable": "{actor} was vulnerable to {type} and took {damage} damage from {from}", - "ChatApplyDamageNormal": "{actor} took {damage} {type} damage from {from}", - "ChatApplyDamageResistant": "{actor} was resistant to {type} and took {damage} damage from {from}", + "ChatApplyDamageVulnerable": "{actor} was vulnerable to {type} and lost {damage} hit points from {from}", + "ChatApplyDamageNormal": "{actor} was dealt {type} and lost {damage} hit points from {from}", + "ChatApplyDamageResistant": "{actor} was resistant to {type} and lost {damage} hit points from {from}", "ChatApplyDamageResistantIgnored": "{actor} took {damage} {type} damage from {from} (Resistance ignored)", "ChatApplyDamageImmune": "{actor} was immune to {type} and took {damage} damage from {from}", "ChatApplyDamageImmuneIgnored": "{actor} took {damage} {type} damage from {from} (Immunity ignored)", @@ -863,8 +863,11 @@ "ChatApplyResourceLossTooltip": "Click to spend the given resource on the actor who performed this action.", "ChatApplyResourceLossSelected": "Spend Selected", "ChatApplyResourceLossTargeted": "Spend Targeted", + "ChatToggleDamageBreakdown": "Toggle Damage Breakdown", "ChatRevertDamage": "Revert applied damage", "ChatRevertResourceLoss": "Revert resource loss", + "ChatAbsorbHitPoints": "Absorb half of hit points lost", + "ChatAbsorbMindPoints": "Absorb half of hit points lost", "ChatEvaluateAmountNoActor": "No reference to an actor provided", "ChatEvaluateAmountNoItem": "No reference to an item provided", "ChatEvaluateNoSkill": "The referenced skill was not found in the actor", diff --git a/module/pipelines/damage-pipeline.mjs b/module/pipelines/damage-pipeline.mjs index 76f94d33..cfa37b06 100644 --- a/module/pipelines/damage-pipeline.mjs +++ b/module/pipelines/damage-pipeline.mjs @@ -289,7 +289,6 @@ async function process(request) { damage: Math.abs(damageTaken), damageType: game.i18n.localize(FU.damageTypes[request.damageType]), affinityIcon: FU.affIcon[context.damageType], - breakdown: context.breakdown, }); updates.push( ChatMessage.create({ @@ -302,6 +301,7 @@ async function process(request) { damage: Math.abs(damageTaken), type: affinityString, from: request.sourceInfo.name, + breakdown: context.breakdown, }), }), ); @@ -406,6 +406,11 @@ function onRenderChatMessage(message, jQuery) { actor.showFloatyText(`${amountRecovered} HP`, `lightgreen`); return Promise.all(updates); }); + + jQuery.find(`a[data-action=toggleBreakdown]`).click(function (event) { + event.preventDefault(); + jQuery.find('#breakdown').toggleClass('hidden'); + }); } /** diff --git a/templates/chat/chat-apply-damage.hbs b/templates/chat/chat-apply-damage.hbs index 0ac7e727..52a2e9d2 100644 --- a/templates/chat/chat-apply-damage.hbs +++ b/templates/chat/chat-apply-damage.hbs @@ -1,6 +1,68 @@
{{{localize message actor=actor damage=damage type=type from=from}}}. +
+ +
+ + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/templates/chat/partials/inline-damage-icon.hbs b/templates/chat/partials/inline-damage-icon.hbs index f614cc64..5814b10f 100644 --- a/templates/chat/partials/inline-damage-icon.hbs +++ b/templates/chat/partials/inline-damage-icon.hbs @@ -1 +1 @@ -{{damageType}} \ No newline at end of file + {{damageType}} \ No newline at end of file From b16f736ed7e9cd788a1cd34e1602facb0175a84f Mon Sep 17 00:00:00 2001 From: Christian Sagel Date: Thu, 30 Jan 2025 21:19:57 +0100 Subject: [PATCH 02/18] Re-do the breakdown --- lang/en.json | 2 +- module/helpers/inline-helper.mjs | 11 ++++++++ module/pipelines/damage-pipeline.mjs | 42 ++++++++++++++++++++++------ templates/chat/chat-apply-damage.hbs | 37 +++++------------------- 4 files changed, 52 insertions(+), 40 deletions(-) diff --git a/lang/en.json b/lang/en.json index b99aea9d..8fce1397 100644 --- a/lang/en.json +++ b/lang/en.json @@ -867,7 +867,7 @@ "ChatRevertDamage": "Revert applied damage", "ChatRevertResourceLoss": "Revert resource loss", "ChatAbsorbHitPoints": "Absorb half of hit points lost", - "ChatAbsorbMindPoints": "Absorb half of hit points lost", + "ChatAbsorbMindPoints": "Absorb half of mind points lost", "ChatEvaluateAmountNoActor": "No reference to an actor provided", "ChatEvaluateAmountNoItem": "No reference to an item provided", "ChatEvaluateNoSkill": "The referenced skill was not found in the actor", diff --git a/module/helpers/inline-helper.mjs b/module/helpers/inline-helper.mjs index e5890959..cb59c826 100644 --- a/module/helpers/inline-helper.mjs +++ b/module/helpers/inline-helper.mjs @@ -164,6 +164,16 @@ function capitalize(word) { return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); } +function nicifyString(str) { + return str + .replace(/([a-z])([A-Z])/g, '$1 $2') + .replace(/[_.]+/g, ' ') + .trim() + .split(/\s+/) + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); +} + export const InlineHelper = { determineSource, appendAmountToAnchor, @@ -171,4 +181,5 @@ export const InlineHelper = { toBase64, fromBase64, capitalize, + nicifyString, }; diff --git a/module/pipelines/damage-pipeline.mjs b/module/pipelines/damage-pipeline.mjs index cfa37b06..f1608626 100644 --- a/module/pipelines/damage-pipeline.mjs +++ b/module/pipelines/damage-pipeline.mjs @@ -6,7 +6,7 @@ import { ChecksV2 } from '../checks/checks-v2.mjs'; import { CheckConfiguration } from '../checks/check-configuration.mjs'; import { DamageCustomizer } from './damage-customizer.mjs'; import { getSelected, getTargeted } from '../helpers/target-handler.mjs'; -import { InlineSourceInfo } from '../helpers/inline-helper.mjs'; +import { InlineHelper, InlineSourceInfo } from '../helpers/inline-helper.mjs'; import { ApplyTargetHookData, BeforeApplyHookData } from './legacy-hook-data.mjs'; /** @@ -56,6 +56,13 @@ export class DamageRequest extends PipelineRequest { } } +/** + * @typedef DamageBreakdown + * @property {String} step + * @property {String} effect + * @property {String} total + */ + // TODO: Decide whether to define in config.mjs. Though it's probably fine if they are all in english const Traits = { IgnoreResistance: 'ignore-resistance', @@ -69,7 +76,7 @@ const Traits = { * @property {Number} amount The base amount before bonuses or modifiers are applied * @property {Map} bonuses Increments * @property {Map} modifiers Multipliers - * @property {String} breakdown + * @property {DamageBreakdown[]} breakdown * @extends PipelineContext */ export class DamagePipelineContext extends PipelineContext { @@ -77,6 +84,7 @@ export class DamagePipelineContext extends PipelineContext { super(request, actor); this.bonuses = new Map(); this.modifiers = new Map(); + this.breakdown = []; this.calculateAmount(); } @@ -95,7 +103,24 @@ export class DamagePipelineContext extends PipelineContext { } addModifier(key, value) { - this.modifiers.set(key, value); + if (value !== 1) { + this.modifiers.set(key, value); + return true; + } + return false; + } + + /** + * @param {String} step + * @param {String} effect + * @param {Number} total + */ + recordStep(step, effect, total) { + this.breakdown.push({ + step: InlineHelper.nicifyString(step), + effect: effect, + total: total, + }); } } @@ -224,23 +249,22 @@ function calculateResult(context) { Hooks.call(FUHooks.DAMAGE_PIPELINE_PRE_CALCULATE, context); let result = context.amount; - let breakdown = `
    `; - breakdown += `
  • amount: ${context.amount}
  • `; + + /** @type DamageBreakdown[] */ + context.recordStep('initial', '', context.amount); // Increments (+-) for (const [key, value] of context.bonuses) { result += value; - breakdown += `
  • ${key}: ${value}
  • `; + context.recordStep(key, value, result); } // Multipliers (*) for (const [key, value] of context.modifiers) { result *= value; - breakdown += `
  • ${key}: *${value}
  • `; + context.recordStep(key, `* ${value}`, result); } - breakdown += `
`; context.result = result; - context.breakdown = breakdown; Hooks.call(FUHooks.DAMAGE_PIPELINE_POST_CALCULATE, context); return true; } diff --git a/templates/chat/chat-apply-damage.hbs b/templates/chat/chat-apply-damage.hbs index 52a2e9d2..70ed429e 100644 --- a/templates/chat/chat-apply-damage.hbs +++ b/templates/chat/chat-apply-damage.hbs @@ -32,36 +32,13 @@ - - Original -   - 22 physical - - - Type Change - Fire - 22 fire - - - Outgoing - +10 fire - 32 fire - - - Incoming - -5 all - 27 fire - - - Incoming - 1/2 all - 13 fire - - - Fire Affinity - Vuln. (x2) - 26 fire - + {{#each breakdown}} + + {{this.step}} + {{this.effect}} + {{this.total}} + + {{/each}} From 4088d3a34587bad75907408fbc913f49f55e29a4 Mon Sep 17 00:00:00 2001 From: Christian Sagel Date: Thu, 30 Jan 2025 21:28:42 +0100 Subject: [PATCH 03/18] Fix up the signs for the table --- module/pipelines/damage-pipeline.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/module/pipelines/damage-pipeline.mjs b/module/pipelines/damage-pipeline.mjs index f1608626..6b6a0f18 100644 --- a/module/pipelines/damage-pipeline.mjs +++ b/module/pipelines/damage-pipeline.mjs @@ -256,12 +256,12 @@ function calculateResult(context) { // Increments (+-) for (const [key, value] of context.bonuses) { result += value; - context.recordStep(key, value, result); + context.recordStep(key, value > 0 ? `+${value}` : value, result); } // Multipliers (*) for (const [key, value] of context.modifiers) { result *= value; - context.recordStep(key, `* ${value}`, result); + context.recordStep(key, `*${value}`, result); } context.result = result; From cc63d0f833365b2990aa3163221fc89d635ccff4 Mon Sep 17 00:00:00 2001 From: Christian Sagel Date: Thu, 30 Jan 2025 21:29:58 +0100 Subject: [PATCH 04/18] Commit CSS --- styles/css/projectfu.css | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/styles/css/projectfu.css b/styles/css/projectfu.css index ab09f87a..0ce9028e 100644 --- a/styles/css/projectfu.css +++ b/styles/css/projectfu.css @@ -3198,6 +3198,31 @@ opacity: 0.5; } +#tooltip .project-fu table { + + border-top: 1px solid var(--pfu-color-app-control-border); + border-bottom: 1px solid var(--pfu-color-app-control-border); + + & thead { + background: var(--pfu-color-app-control-fill); + color: var(--pfu-color-app-control-content); + border-bottom: 1px solid var(--pfu-color-app-control-border); + } + & caption { + caption-side: top; + font-weight: bold; + color: inherit; + } + + & th:first-child { + text-align: right; + } + + & th, td { + text-align: left; + } +} + /* ----------------------------------------- */ /* Item Card Structure */ /* ----------------------------------------- */ From b4966ff254ae007795599f8bebff538cac504b72 Mon Sep 17 00:00:00 2001 From: Christian Sagel Date: Fri, 31 Jan 2025 18:41:47 +0100 Subject: [PATCH 05/18] Fix bug regarding source info in damage message handling, set up health/mind leech on damage taken. --- module/helpers/inline-helper.mjs | 2 ++ module/pipelines/damage-pipeline.mjs | 34 ++++++++++++++++------ module/pipelines/resource-pipeline.mjs | 3 +- styles/css/projectfu.css | 25 ----------------- templates/chat/chat-apply-damage.hbs | 39 +++++++++++++++----------- 5 files changed, 52 insertions(+), 51 deletions(-) diff --git a/module/helpers/inline-helper.mjs b/module/helpers/inline-helper.mjs index cb59c826..9fc8c9d6 100644 --- a/module/helpers/inline-helper.mjs +++ b/module/helpers/inline-helper.mjs @@ -58,6 +58,8 @@ export class InlineSourceInfo { } return null; } + + static none = Object.freeze(new InlineSourceInfo('Unknown')); } /** diff --git a/module/pipelines/damage-pipeline.mjs b/module/pipelines/damage-pipeline.mjs index 6b6a0f18..d0b70515 100644 --- a/module/pipelines/damage-pipeline.mjs +++ b/module/pipelines/damage-pipeline.mjs @@ -8,6 +8,7 @@ import { DamageCustomizer } from './damage-customizer.mjs'; import { getSelected, getTargeted } from '../helpers/target-handler.mjs'; import { InlineHelper, InlineSourceInfo } from '../helpers/inline-helper.mjs'; import { ApplyTargetHookData, BeforeApplyHookData } from './legacy-hook-data.mjs'; +import { ResourcePipeline, ResourceRequest } from './resource-pipeline.mjs'; /** * @typedef ApplyTargetOverrides @@ -325,6 +326,7 @@ async function process(request) { damage: Math.abs(damageTaken), type: affinityString, from: request.sourceInfo.name, + sourceActorUuid: request.sourceInfo.actorUuid, breakdown: context.breakdown, }), }), @@ -340,10 +342,13 @@ async function process(request) { */ function onRenderChatMessage(message, jQuery) { const check = message.getFlag(SYSTEM, Flags.ChatMessage.CheckParams); - let sourceUuid = null; + let sourceItemUuid = null; + let sourceActorUuid = null; let sourceName; let baseDamageInfo; let disabled = false; + /** @type InlineSourceInfo **/ + let sourceInfo = null; if (check && check.damage) { sourceName = check.details.name; @@ -357,8 +362,10 @@ function onRenderChatMessage(message, jQuery) { if (ChecksV2.isCheck(message)) { const damage = CheckConfiguration.inspect(message).getDamage(); if (damage) { - sourceUuid = message.getFlag(SYSTEM, Flags.ChatMessage.CheckV2)?.itemUuid; + sourceActorUuid = message.getFlag(SYSTEM, Flags.ChatMessage.CheckV2)?.actorUuid; + sourceItemUuid = message.getFlag(SYSTEM, Flags.ChatMessage.CheckV2)?.itemUuid; sourceName = message.getFlag(SYSTEM, Flags.ChatMessage.Item)?.name; + sourceInfo = new InlineSourceInfo(sourceName, sourceActorUuid, sourceItemUuid); baseDamageInfo = { total: damage.total, type: damage.type, @@ -372,7 +379,7 @@ function onRenderChatMessage(message, jQuery) { baseDamageInfo, targets, async (extraDamageInfo) => { - await handleDamageApplication(event, targets, sourceUuid, sourceName, baseDamageInfo, extraDamageInfo); + await handleDamageApplication(event, targets, sourceInfo, baseDamageInfo, extraDamageInfo); disabled = false; }, () => { @@ -382,7 +389,7 @@ function onRenderChatMessage(message, jQuery) { }; const applyDefaultDamage = async (event, targets) => { - return handleDamageApplication(event, targets, sourceUuid, sourceName, baseDamageInfo, {}); + return handleDamageApplication(event, targets, sourceInfo, baseDamageInfo, {}); }; const handleClick = async (event, getTargetsFunction, action, alternateAction) => { @@ -411,7 +418,7 @@ function onRenderChatMessage(message, jQuery) { baseDamageInfo, targets, (extraDamageInfo) => { - handleDamageApplication(event, targets, sourceUuid, sourceName, baseDamageInfo, extraDamageInfo); + handleDamageApplication(event, targets, sourceInfo, baseDamageInfo, extraDamageInfo); disabled = false; }, () => { @@ -435,20 +442,29 @@ function onRenderChatMessage(message, jQuery) { event.preventDefault(); jQuery.find('#breakdown').toggleClass('hidden'); }); + + jQuery.find(`a[data-action=absorbDamage]`).click(async function (event) { + event.preventDefault(); + const amount = Number.parseInt(this.dataset.amount); + const resource = this.dataset.resource; + const uuid = this.dataset.uuid; + const sourceInfo = new InlineSourceInfo(`${resource.toUpperCase()} Leech`, uuid); + const targets = [sourceInfo.resolveActor()]; + const request = new ResourceRequest(sourceInfo, targets, resource, amount, false); + ResourcePipeline.processRecovery(request); + }); } /** * * @param {Event} event * @param {FUActor[]} targets - * @param {string} sourceUuid - * @param {string} sourceName + * @param {InlineSourceInfo} sourceInfo * @param {import('../helpers/typedefs.mjs').BaseDamageInfo} baseDamageInfo * @param {import('./damage-customizer.mjs').ExtraDamageInfo} extraDamageInfo * @returns {void} */ -async function handleDamageApplication(event, targets, sourceUuid, sourceName, baseDamageInfo, extraDamageInfo) { - const sourceInfo = new InlineSourceInfo(sourceName, sourceUuid, null); +async function handleDamageApplication(event, targets, sourceInfo, baseDamageInfo, extraDamageInfo) { const request = new DamageRequest(sourceInfo, targets, baseDamageInfo, extraDamageInfo); request.event = event; if (event.shiftKey) { diff --git a/module/pipelines/resource-pipeline.mjs b/module/pipelines/resource-pipeline.mjs index d77c46bb..87c841b7 100644 --- a/module/pipelines/resource-pipeline.mjs +++ b/module/pipelines/resource-pipeline.mjs @@ -98,7 +98,8 @@ async function processRecovery(request) { const updates = []; for (const actor of request.targets) { - const amountRecovered = Math.max(0, request.amount + (actor.system.bonuses.incomingRecovery[request.resourceType] || 0)); + const recoveryBonus = actor.system.bonuses.incomingRecovery[request.resourceType] || 0; + const amountRecovered = Math.max(0, request.amount + recoveryBonus); const attr = foundry.utils.getProperty(actor.system, request.attributeKey); const uncappedRecoveryValue = amountRecovered + attr.value; const updates = []; diff --git a/styles/css/projectfu.css b/styles/css/projectfu.css index 0ce9028e..ab09f87a 100644 --- a/styles/css/projectfu.css +++ b/styles/css/projectfu.css @@ -3198,31 +3198,6 @@ opacity: 0.5; } -#tooltip .project-fu table { - - border-top: 1px solid var(--pfu-color-app-control-border); - border-bottom: 1px solid var(--pfu-color-app-control-border); - - & thead { - background: var(--pfu-color-app-control-fill); - color: var(--pfu-color-app-control-content); - border-bottom: 1px solid var(--pfu-color-app-control-border); - } - & caption { - caption-side: top; - font-weight: bold; - color: inherit; - } - - & th:first-child { - text-align: right; - } - - & th, td { - text-align: left; - } -} - /* ----------------------------------------- */ /* Item Card Structure */ /* ----------------------------------------- */ diff --git a/templates/chat/chat-apply-damage.hbs b/templates/chat/chat-apply-damage.hbs index 70ed429e..95c0442d 100644 --- a/templates/chat/chat-apply-damage.hbs +++ b/templates/chat/chat-apply-damage.hbs @@ -2,22 +2,29 @@ {{{localize message actor=actor damage=damage type=type from=from}}}. -
- - - - - - - - - - - - - - - +
+ +
+ + + + + + + +
+ + +
+ + + + + + + +
+