diff --git a/lang/en.json b/lang/en.json index 1096036f..ab109294 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1863,6 +1863,7 @@ "actorName": "Actor Name", "amount": "Amount", "any": "Any", + "armor": "Armor", "armorScore": "Armor Score", "activeEffects": "Active Effects", "armorSlots": "Armor Slots", @@ -2098,6 +2099,10 @@ "label": "Effect Range Dependent", "hint": "Effects with defined range dependency will automatically turn on/off depending on range" } + }, + "resourceScrollTexts": { + "label": "Show Resource Change Scrolltexts", + "hint": "When a character is damaged, uses armor etc, a scrolling text will briefly appear by the token to signify this." } } }, diff --git a/module/config/actorConfig.mjs b/module/config/actorConfig.mjs index edf3a9f3..6453cd78 100644 --- a/module/config/actorConfig.mjs +++ b/module/config/actorConfig.mjs @@ -55,6 +55,24 @@ export const abilities = { } }; +export const scrollingTextResource = { + hitPoints: { + label: 'DAGGERHEART.GENERAL.HitPoints.plural', + reversed: true + }, + stress: { + label: 'DAGGERHEART.GENERAL.stress', + reversed: true + }, + hope: { + label: 'DAGGERHEART.GENERAL.hope' + }, + armor: { + label: 'DAGGERHEART.GENERAL.armor', + reversed: true + } +}; + export const featureProperties = { agility: { name: 'DAGGERHEART.CONFIG.Traits.agility.name', diff --git a/module/data/actor/base.mjs b/module/data/actor/base.mjs index 386b9695..a32ac9dd 100644 --- a/module/data/actor/base.mjs +++ b/module/data/actor/base.mjs @@ -1,4 +1,5 @@ import DHBaseActorSettings from '../../applications/sheets/api/actor-setting.mjs'; +import { createScrollText, getScrollTextData } from '../../helpers/utils.mjs'; const resistanceField = (resistanceLabel, immunityLabel, reductionLabel) => new foundry.data.fields.SchemaField({ @@ -88,4 +89,28 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel { const data = { ...this }; return data; } + + async _preUpdate(changes, options, userId) { + const allowed = await super._preUpdate(changes, options, userId); + if (allowed === false) return; + + const autoSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation); + if (changes.system?.resources && autoSettings.resourceScrollTexts) { + const textData = Object.keys(changes.system.resources).reduce((acc, key) => { + const resource = changes.system.resources[key]; + if (resource.value !== undefined && resource.value !== this.resources[key].value) { + acc.push(getScrollTextData(this.resources, resource, key)); + } + + return acc; + }, []); + options.scrollingTextData = textData; + } + } + + _onUpdate(changes, options, userId) { + super._onUpdate(changes, options, userId); + + createScrollText(this.parent, options.scrollingTextData); + } } diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs index 4203f5cc..61b2ce5f 100644 --- a/module/data/item/base.mjs +++ b/module/data/item/base.mjs @@ -8,7 +8,7 @@ * @property {boolean} isInventoryItem- Indicates whether items of this type is a Inventory Item */ -import { addLinkedItemsDiff, updateLinkedItemApps } from '../../helpers/utils.mjs'; +import { addLinkedItemsDiff, createScrollText, getScrollTextData, updateLinkedItemApps } from '../../helpers/utils.mjs'; import { ActionsField } from '../fields/actionField.mjs'; import FormulaField from '../fields/formulaField.mjs'; @@ -184,11 +184,20 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { if (allowed === false) return false; addLinkedItemsDiff(changed.system?.features, this.features, options); + + const autoSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation); + const armorChanged = + changed.system?.marks?.value !== undefined && changed.system.marks.value !== this.marks.value; + if (armorChanged && autoSettings.resourceScrollTexts && this.parent.parent?.type === 'character') { + const armorData = getScrollTextData(this.parent.parent.system.resources, changed.system.marks, 'armor'); + options.scrollingTextData = [armorData]; + } } _onUpdate(changed, options, userId) { super._onUpdate(changed, options, userId); updateLinkedItemApps(options, this.parent.sheet); + createScrollText(this.parent?.parent, options.scrollingTextData); } } diff --git a/module/data/settings/Automation.mjs b/module/data/settings/Automation.mjs index 84b7469c..d2842148 100644 --- a/module/data/settings/Automation.mjs +++ b/module/data/settings/Automation.mjs @@ -40,6 +40,11 @@ export default class DhAutomation extends foundry.abstract.DataModel { choices: CONFIG.DH.GENERAL.ruleChoice, initial: CONFIG.DH.GENERAL.ruleChoice.onWithToggle.id, label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.damageReductionRulesDefault.label' + }), + resourceScrollTexts: new fields.BooleanField({ + required: true, + initial: true, + label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.resourceScrollTexts.label' }) }; } diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 4de67e86..ebbe5131 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -324,3 +324,34 @@ export const arraysEqual = (a, b) => [...new Set([...a, ...b])].every(v => a.filter(e => e === v).length === b.filter(e => e === v).length); export const setsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value)); + +export function getScrollTextData(resources, resource, key) { + const { reversed, label } = CONFIG.DH.ACTOR.scrollingTextResource[key]; + const { BOTTOM, TOP } = CONST.TEXT_ANCHOR_POINTS; + const increased = resources[key].value < resource.value; + const value = -1 * (resources[key].value - resource.value); + + const text = `${game.i18n.localize(label)} ${value.signedString()}`; + + const stroke = increased ? (reversed ? 0xffffff : 0x000000) : reversed ? 0x000000 : 0xffffff; + const fill = increased ? (reversed ? 0x0032b1 : 0xffe760) : reversed ? 0xffe760 : 0x0032b1; + const direction = increased ? (reversed ? BOTTOM : TOP) : reversed ? TOP : BOTTOM; + + return { text, stroke, fill, direction }; +} + +export function createScrollText(actor, optionsData) { + if (actor && optionsData?.length) { + actor.getDependentTokens().forEach(token => { + optionsData.forEach(data => { + const { text, ...options } = data; + canvas.interface.createScrollingText(token.getCenterPoint(), data.text, { + duration: 2000, + distance: token.h, + jitter: 0, + ...options + }); + }); + }); + } +} diff --git a/templates/settings/automation-settings.hbs b/templates/settings/automation-settings.hbs index f421380f..0d193869 100644 --- a/templates/settings/automation-settings.hbs +++ b/templates/settings/automation-settings.hbs @@ -13,6 +13,7 @@ {{formGroup settingFields.schema.fields.effects.fields.rangeDependent value=settingFields._source.effects.rangeDependent localize=true}} {{formGroup settingFields.schema.fields.levelupAuto value=settingFields._source.levelupAuto localize=true}} {{formGroup settingFields.schema.fields.damageReductionRulesDefault value=settingFields._source.damageReductionRulesDefault localize=true}} + {{formGroup settingFields.schema.fields.resourceScrollTexts value=settingFields._source.resourceScrollTexts localize=true}}