diff --git a/daggerheart.mjs b/daggerheart.mjs index 6fb6cc40..0d78e8fe 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -136,6 +136,7 @@ Hooks.once('init', () => { CONFIG.Canvas.rulerClass = placeables.DhRuler; CONFIG.Canvas.layers.templates.layerClass = placeables.DhTemplateLayer; + CONFIG.Token.objectClass = placeables.DhTokenPlaceable; CONFIG.Combat.documentClass = documents.DhpCombat; CONFIG.ui.combat = applications.ui.DhCombatTracker; CONFIG.ui.chat = applications.ui.DhChatLog; diff --git a/lang/en.json b/lang/en.json index 11afac01..4af86abe 100755 --- a/lang/en.json +++ b/lang/en.json @@ -99,6 +99,7 @@ "tier": { "label": "Tier" }, "type": { "label": "Type" } }, + "hordeDamage": "Horde Damage", "horderHp": "Horde/HP" }, "Character": { @@ -1214,6 +1215,7 @@ "damage": "Damage", "damageType": "Damage Type", "description": "Description", + "difficulty": "Difficulty", "duality": "Duality", "dualityRoll": "Duality Roll", "enabled": "Enabled", @@ -1224,7 +1226,11 @@ }, "fear": "Fear", "features": "Features", - "hitPoints": "Hit Points", + "hitPoints": { + "single": "Hit Point", + "plural": "Hit Points", + "short": "HP" + }, "hope": "Hope", "hordeHp": "Horde HP", "inactiveEffects": "Inactive Effects", @@ -1251,7 +1257,8 @@ "use": "Use", "used": "Used", "uses": "Uses", - "value": "Value" + "value": "Value", + "withThing": "With {thing}" }, "ITEMS": { "FIELDS": { @@ -1355,9 +1362,9 @@ "label": "Action Points", "hint": "Automatically give and take Action Points as combatants take their turns." }, - "countdowns": { - "label": "Countdowns", - "hint": "Automatically progress non-custom countdowns" + "hordeDamage": { + "label": "Automatic Horde Damage", + "hint": "Automatically active horde effect to lower damage when reaching half or lower HP." } } }, diff --git a/module/canvas/placeables/_module.mjs b/module/canvas/placeables/_module.mjs index 3610559c..78242839 100644 --- a/module/canvas/placeables/_module.mjs +++ b/module/canvas/placeables/_module.mjs @@ -1,4 +1,5 @@ export { default as DhMeasuredTemplate } from './measuredTemplate.mjs'; export { default as DhRuler } from './ruler.mjs'; export { default as DhTemplateLayer } from './templateLayer.mjs'; +export { default as DhTokenPlaceable } from './token.mjs'; export { default as DhTokenRuler } from './tokenRuler.mjs'; diff --git a/module/canvas/placeables/token.mjs b/module/canvas/placeables/token.mjs new file mode 100644 index 00000000..967df0f8 --- /dev/null +++ b/module/canvas/placeables/token.mjs @@ -0,0 +1,36 @@ +export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { + /** @inheritDoc */ + async _drawEffects() { + this.effects.renderable = false; + + // Clear Effects Container + this.effects.removeChildren().forEach(c => c.destroy()); + this.effects.bg = this.effects.addChild(new PIXI.Graphics()); + this.effects.bg.zIndex = -1; + this.effects.overlay = null; + + // Categorize effects + const activeEffects = this.actor ? Array.from(this.actor.effects).filter(x => !x.disabled) : []; + const overlayEffect = activeEffects.findLast(e => e.img && e.getFlag('core', 'overlay')); + + // Draw effects + const promises = []; + for (const [i, effect] of activeEffects.entries()) { + if (!effect.img) continue; + const promise = + effect === overlayEffect + ? this._drawOverlay(effect.img, effect.tint) + : this._drawEffect(effect.img, effect.tint); + promises.push( + promise.then(e => { + if (e) e.zIndex = i; + }) + ); + } + await Promise.allSettled(promises); + + this.effects.sortChildren(); + this.effects.renderable = true; + this.renderFlags.set({ refreshEffects: true }); + } +} diff --git a/module/data/action/damageAction.mjs b/module/data/action/damageAction.mjs index 388c5eb8..ccd996f7 100644 --- a/module/data/action/damageAction.mjs +++ b/module/data/action/damageAction.mjs @@ -6,6 +6,15 @@ export default class DHDamageAction extends DHBaseAction { getFormulaValue(part, data) { let formulaValue = part.value; if (this.hasRoll && part.resultBased && data.system.roll.result.duality === -1) return part.valueAlt; + + const isAdversary = this.actor.type === 'adversary'; + if (isAdversary && this.actor.system.type === CONFIG.DH.ACTOR.adversaryTypes.horde.id) { + const hasHordeDamage = this.actor.effects.find( + x => x.name === game.i18n.localize('DAGGERHEART.CONFIG.AdversaryType.horde.label') + ); + if (hasHordeDamage) return part.valueAlt; + } + return formulaValue; } @@ -21,7 +30,7 @@ export default class DHDamageAction extends DHBaseAction { bonusDamage = []; if (isNaN(formula)) formula = Roll.replaceFormulaData(formula, this.getRollData(systemData)); - + const config = { title: game.i18n.format('DAGGERHEART.UI.Chat.damageRoll.title', { damage: this.name }), roll: { formula }, diff --git a/module/data/actor/adversary.mjs b/module/data/actor/adversary.mjs index e77a4855..00a19d05 100644 --- a/module/data/actor/adversary.mjs +++ b/module/data/actor/adversary.mjs @@ -52,7 +52,7 @@ export default class DhpAdversary extends BaseDataActor { }) }), resources: new fields.SchemaField({ - hitPoints: resourceField(0, 'DAGGERHEART.GENERAL.hitPoints', true), + hitPoints: resourceField(0, 'DAGGERHEART.GENERAL.hitPoints.plural', true), stress: resourceField(0, 'DAGGERHEART.GENERAL.stress', true) }), attack: new ActionField({ @@ -109,4 +109,37 @@ export default class DhpAdversary extends BaseDataActor { get features() { return this.parent.items.filter(x => x.type === 'feature'); } + + async _preUpdate(changes, options, user) { + const allowed = await super._preUpdate(changes, options, user); + if (allowed === false) return false; + + if (this.type === CONFIG.DH.ACTOR.adversaryTypes.horde.id) { + if (changes.system?.resources?.hitPoints?.value) { + const halfHP = Math.ceil(this.resources.hitPoints.max / 2); + const newHitPoints = changes.system.resources.hitPoints.value; + const previouslyAboveHalf = this.resources.hitPoints.value < halfHP; + const loweredBelowHalf = previouslyAboveHalf && newHitPoints >= halfHP; + const raisedAboveHalf = !previouslyAboveHalf && newHitPoints < halfHP; + if (loweredBelowHalf) { + await this.parent.createEmbeddedDocuments('ActiveEffect', [ + { + name: game.i18n.localize('DAGGERHEART.CONFIG.AdversaryType.horde.label'), + img: 'icons/magic/movement/chevrons-down-yellow.webp', + disabled: !game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation) + .hordeDamage + } + ]); + } else if (raisedAboveHalf) { + const hordeEffects = this.parent.effects.filter( + x => x.name === game.i18n.localize('DAGGERHEART.CONFIG.AdversaryType.horde.label') + ); + await this.parent.deleteEmbeddedDocuments( + 'ActiveEffect', + hordeEffects.map(x => x.id) + ); + } + } + } + } } diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index 68901b35..804eabec 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -21,7 +21,7 @@ export default class DhCharacter extends BaseDataActor { return { ...super.defineSchema(), resources: new fields.SchemaField({ - hitPoints: resourceField(0, 'DAGGERHEART.GENERAL.hitPoints', true), + hitPoints: resourceField(0, 'DAGGERHEART.GENERAL.hitPoints.plural', true), stress: resourceField(6, 'DAGGERHEART.GENERAL.stress', true), hope: resourceField(6, 'DAGGERHEART.GENERAL.hope') }), diff --git a/module/data/item/class.mjs b/module/data/item/class.mjs index 4d951ed4..281b0a48 100644 --- a/module/data/item/class.mjs +++ b/module/data/item/class.mjs @@ -24,7 +24,7 @@ export default class DHClass extends BaseDataItem { integer: true, min: 1, initial: 5, - label: 'DAGGERHEART.GENERAL.hitPoints' + label: 'DAGGERHEART.GENERAL.hitPoints.plural' }), evasion: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.evasion' }), hopeFeatures: new ForeignDocumentUUIDArrayField({ type: 'Item' }), diff --git a/module/data/settings/Automation.mjs b/module/data/settings/Automation.mjs index 4291423b..63377b26 100644 --- a/module/data/settings/Automation.mjs +++ b/module/data/settings/Automation.mjs @@ -1,6 +1,4 @@ export default class DhAutomation extends foundry.abstract.DataModel { - static LOCALIZATION_PREFIXES = ['DAGGERHEART.SETTINGS.Automation']; // Doesn't work for some reason - static defineSchema() { const fields = foundry.data.fields; return { @@ -20,6 +18,11 @@ export default class DhAutomation extends foundry.abstract.DataModel { required: true, initial: false, label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.actionPoints.label' + }), + hordeDamage: new fields.BooleanField({ + required: true, + initial: true, + label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.hordeDamage.label' }) }; } diff --git a/styles/less/dialog/dice-roll/roll-selection.less b/styles/less/dialog/dice-roll/roll-selection.less index 14bccf3d..55db8bb7 100644 --- a/styles/less/dialog/dice-roll/roll-selection.less +++ b/styles/less/dialog/dice-roll/roll-selection.less @@ -1,6 +1,13 @@ @import '../../utils/colors.less'; @import '../../utils/fonts.less'; +.theme-light .application.daggerheart.dialog.dh-style.views.roll-selection { + .roll-dialog-container .dices-section .dice-option .dice-icon.normal { + filter: brightness(0) saturate(100%) invert(13%) sepia(1%) saturate(0%) hue-rotate(10deg) brightness(98%) + contrast(100%); + } +} + .application.daggerheart.dialog.dh-style.views.roll-selection { .roll-dialog-container { display: flex; @@ -24,6 +31,7 @@ height: 70px; object-fit: contain; } + .dice-select { display: flex; align-items: center; diff --git a/templates/actionTypes/damage.hbs b/templates/actionTypes/damage.hbs index cbd1f503..3c125bd7 100644 --- a/templates/actionTypes/damage.hbs +++ b/templates/actionTypes/damage.hbs @@ -1,7 +1,7 @@
- Damage + {{localize "DAGGERHEART.GENERAL.damage"}} {{#unless (eq path 'system.attack.')}}{{/unless}} {{#unless (or @root.isNPC path)}} @@ -23,6 +23,16 @@ {{/if}} {{formField ../fields.type value=dmg.type name=(concat ../path "damage.parts." index ".type") localize=true}} + {{#if ../horde}} +
+ {{localize "DAGGERHEART.ACTORS.Adversary.hordeDamage"}} +
+ {{formField ../fields.valueAlt.fields.flatMultiplier value=dmg.valueAlt.flatMultiplier name=(concat ../path "damage.parts." index ".valueAlt.flatMultiplier") label="Multiplier" classes="inline-child" }} + {{formField ../fields.valueAlt.fields.dice value=dmg.valueAlt.dice name=(concat ../path "damage.parts." index ".valueAlt.dice") classes="inline-child"}} + {{formField ../fields.valueAlt.fields.bonus value=dmg.valueAlt.bonus name=(concat ../path "damage.parts." index ".valueAlt.bonus") localize=true classes="inline-child"}} +
+
+ {{/if}} {{else}} {{#with (@root.getRealIndex index) as | realIndex |}}
@@ -33,11 +43,11 @@ {{#if (and (not @root.isNPC) @root.hasRoll (not dmg.base) dmg.resultBased)}}
- With Hope + {{localize "DAGGERHEART.GENERAL.withThing" thing=(localize "DAGGERHEART.GENERAL.hope")}} {{> formula fields=../../fields.value.fields type=../../fields.type dmg=dmg source=dmg.value target="value" realIndex=realIndex}}
- With Fear + {{localize "DAGGERHEART.GENERAL.withThing" thing=(localize "DAGGERHEART.GENERAL.fear")}} {{> formula fields=../../fields.valueAlt.fields type=../../fields.type dmg=dmg source=dmg.valueAlt target="valueAlt" realIndex=realIndex}}
diff --git a/templates/dialogs/dice-roll/rollSelection.hbs b/templates/dialogs/dice-roll/rollSelection.hbs index 11fce27a..0d2f3f48 100644 --- a/templates/dialogs/dice-roll/rollSelection.hbs +++ b/templates/dialogs/dice-roll/rollSelection.hbs @@ -5,7 +5,7 @@
{{#if (eq @root.rollType 'D20Roll')}}
- +