From 6b6f4e61d600162aa423500f0577bf73ce174a35 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Tue, 15 Jul 2025 03:45:16 +0200 Subject: [PATCH 1/4] Added back evasion property setting (#339) --- module/data/actor/character.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index 3ee7c980..e8322671 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -323,6 +323,8 @@ export default class DhCharacter extends BaseDataActor { } prepareBaseData() { + this.evasion = this.class.value?.system?.evasion ?? 0; + const currentLevel = this.levelData.level.current; const currentTier = currentLevel === 1 From ef45cd71f8f1c053eb8e158c5f3cc5cc364b1161 Mon Sep 17 00:00:00 2001 From: Psitacus <59754077+Psitacus@users.noreply.github.com> Date: Tue, 15 Jul 2025 04:03:12 -0600 Subject: [PATCH 2/4] Iss337 - Allow armor slots to be edited from the character sheet (#345) * add armor slot input * working progress bar * fix label styling * fix more styling * fix styling issue on firefix * Restored armorSlots when empty and added localization --------- Co-authored-by: psitacus Co-authored-by: WBHarry --- lang/en.json | 1 + .../applications/sheets/actors/character.mjs | 15 ++ .../less/sheets/actors/character/sidebar.less | 135 +++++++++++++++++- templates/sheets/actors/character/sidebar.hbs | 34 +++-- 4 files changed, 172 insertions(+), 13 deletions(-) diff --git a/lang/en.json b/lang/en.json index 96f5a9dd..a2d4bfd8 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1138,6 +1138,7 @@ "plural": "Traits" }, "activeEffects": "Active Effects", + "armorSlots": "Armor Slots", "attack": "Attack", "basics": "Basics", "bonus": "Bonus", diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index eae7a223..99545d4e 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -103,6 +103,11 @@ export default class CharacterSheet extends DHBaseActorSheet { htmlElement.querySelectorAll('.inventory-item-quantity').forEach(element => { element.addEventListener('change', this.updateItemQuantity.bind(this)); }); + + // Add listener for armor marks input + htmlElement.querySelectorAll('.armor-marks-input').forEach(element => { + element.addEventListener('change', this.updateArmorMarks.bind(this)); + }); } /** @inheritDoc */ @@ -515,6 +520,16 @@ export default class CharacterSheet extends DHBaseActorSheet { this.render(); } + async updateArmorMarks(event) { + const armor = this.document.system.armor; + if (!armor) return; + + const maxMarks = this.document.system.armorScore; + const value = Math.min(Math.max(Number(event.currentTarget.value), 0), maxMarks); + await armor.update({ 'system.marks.value': value }); + this.render(); + } + /* -------------------------------------------- */ /* Application Clicks Actions */ /* -------------------------------------------- */ diff --git a/styles/less/sheets/actors/character/sidebar.less b/styles/less/sheets/actors/character/sidebar.less index f46a9628..e8662f86 100644 --- a/styles/less/sheets/actors/character/sidebar.less +++ b/styles/less/sheets/actors/character/sidebar.less @@ -174,9 +174,114 @@ gap: 5px; justify-content: center; + .status-bar.armor-slots { + display: flex; + justify-content: center; + position: relative; + width: 95px; + height: 30px; + + .status-label { + padding: 2px 10px; + position: relative; + top: 30px; + height: 22px; + width: 95px; + border-radius: 3px; + background: light-dark(@dark-blue, @golden); + + h4 { + font-weight: bold; + text-align: center; + line-height: 18px; + color: light-dark(@beige, @dark-blue); + font-size: 12px; + } + } + .status-value { + position: absolute; + display: flex; + padding: 0 6px; + font-size: 1.2rem; + align-items: center; + width: 80px; + height: 30px; + justify-content: center; + text-align: center; + z-index: 2; + color: light-dark(@dark-blue, @beige); + border: 1px solid light-dark(@dark-blue, @golden); + border-bottom: none; + border-radius: 6px 6px 0 0; + + input[type='number'] { + background: transparent; + font-size: 1.2rem; + width: 30px; + height: 20px; + text-align: center; + border: none; + outline: 2px solid transparent; + color: light-dark(@dark-blue, @beige); + + &.bar-input { + padding: 0; + color: light-dark(@dark-blue, @beige); + backdrop-filter: none; + background: transparent; + transition: all 0.3s ease; + + &:hover, + &:focus { + background: @semi-transparent-dark-blue; + backdrop-filter: blur(9.5px); + } + } + } + + .bar-label { + width: 30px; + } + } + + .progress-bar { + position: absolute; + appearance: none; + width: 80px; + height: 30px; + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 6px; + z-index: 1; + background: light-dark(transparent, @dark-blue); + border-bottom: none; + border-radius: 6px 6px 0 0; + + &::-webkit-progress-bar { + border: none; + background: light-dark(transparent, @dark-blue); + } + &::-webkit-progress-value { + background: @gradient-stress; + } + &.stress-color::-webkit-progress-value { + background: @gradient-stress; + } + &::-moz-progress-bar { + background: @gradient-stress; + } + &.stress-color::-moz-progress-bar { + background: @gradient-stress; + } + } + } + .status-number { justify-items: center; + &.armor-slots { + width: 95px; + } + .status-value { position: relative; display: flex; @@ -192,9 +297,33 @@ background: light-dark(transparent, @dark-blue); z-index: 2; - &.armor-slots { - width: 80px; - height: 30px; + input[type='number'] { + background: transparent; + font-size: 1.2rem; + width: 30px; + height: 20px; + text-align: center; + border: none; + outline: 2px solid transparent; + color: light-dark(@dark-blue, @beige); + + &.bar-input { + padding: 0; + color: light-dark(@dark-blue, @beige); + backdrop-filter: none; + background: transparent; + transition: all 0.3s ease; + + &:hover, + &:focus { + background: @semi-transparent-dark-blue; + backdrop-filter: blur(9.5px); + } + } + } + + .bar-label { + width: 30px; } } diff --git a/templates/sheets/actors/character/sidebar.hbs b/templates/sheets/actors/character/sidebar.hbs index 7b5659d6..51816443 100644 --- a/templates/sheets/actors/character/sidebar.hbs +++ b/templates/sheets/actors/character/sidebar.hbs @@ -48,18 +48,32 @@ -
-
- {{#if document.system.armor.system.marks}} -

{{document.system.armor.system.marks.value}}/{{document.system.armorScore}}

- {{else}} + {{#if document.system.armor.system.marks}} +
+
+

+

/

+

{{document.system.armorScore}}

+
+ +
+

{{localize "DAGGERHEART.GENERAL.armorSlots"}}

+
+
+ {{else}} +
+

-

- {{/if}} +
+
+

{{localize "DAGGERHEART.GENERAL.armorSlots"}}

+
-
-

Armor Slots

-
-
+ {{/if}}
From 422f28c93c518027739afc897b108ddde41f78f8 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Tue, 15 Jul 2025 14:07:07 +0200 Subject: [PATCH 3/4] Removed all instances of justify-items and replaced with flex solution (#343) --- styles/less/sheets/actors/adversary/sidebar.less | 4 +++- styles/less/sheets/actors/character/sidebar.less | 4 +++- styles/less/sheets/actors/companion/header.less | 1 - styles/less/sheets/actors/environment/header.less | 4 +++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/styles/less/sheets/actors/adversary/sidebar.less b/styles/less/sheets/actors/adversary/sidebar.less index c1bd1856..f8a34874 100644 --- a/styles/less/sheets/actors/adversary/sidebar.less +++ b/styles/less/sheets/actors/adversary/sidebar.less @@ -215,7 +215,9 @@ justify-content: center; .status-number { - justify-items: center; + display: flex; + align-items: center; + flex-direction: column; .status-value { position: relative; diff --git a/styles/less/sheets/actors/character/sidebar.less b/styles/less/sheets/actors/character/sidebar.less index e8662f86..2c88d14f 100644 --- a/styles/less/sheets/actors/character/sidebar.less +++ b/styles/less/sheets/actors/character/sidebar.less @@ -276,7 +276,9 @@ } .status-number { - justify-items: center; + display: flex; + align-items: center; + flex-direction: column; &.armor-slots { width: 95px; diff --git a/styles/less/sheets/actors/companion/header.less b/styles/less/sheets/actors/companion/header.less index 832a6050..b343146f 100644 --- a/styles/less/sheets/actors/companion/header.less +++ b/styles/less/sheets/actors/companion/header.less @@ -92,7 +92,6 @@ position: relative; width: 100px; height: 40px; - justify-items: center; .status-label { position: relative; diff --git a/styles/less/sheets/actors/environment/header.less b/styles/less/sheets/actors/environment/header.less index e3ca0eec..1276b276 100644 --- a/styles/less/sheets/actors/environment/header.less +++ b/styles/less/sheets/actors/environment/header.less @@ -60,7 +60,9 @@ } .status-number { - justify-items: center; + display: flex; + align-items: center; + flex-direction: column; .status-value { position: relative; From 37c1d7ad880894cd30e002d56b1df064415836e3 Mon Sep 17 00:00:00 2001 From: Dapoulp <74197441+Dapoulp@users.noreply.github.com> Date: Tue, 15 Jul 2025 15:01:34 +0200 Subject: [PATCH 4/4] Feature/163 actor subdatas (#346) * Actor Roll bonuses * Removed console log and comment --------- Co-authored-by: WBHarry --- lang/en.json | 8 ++-- module/applications/ui/chatLog.mjs | 2 - module/config/generalConfig.mjs | 12 +++--- module/data/action/attackAction.mjs | 8 +++- module/data/action/baseAction.mjs | 11 ++++- module/data/action/damageAction.mjs | 5 +++ module/data/action/healingAction.mjs | 4 ++ module/data/actor/adversary.mjs | 25 ++++++----- module/data/actor/character.mjs | 56 ++++++++++--------------- module/data/actor/companion.mjs | 19 +++++---- module/data/fields/actorField.mjs | 28 +++++++++++++ module/data/item/weapon.mjs | 2 +- module/dice/d20Roll.mjs | 34 ++++++++------- module/dice/damageRoll.mjs | 18 ++++++++ module/dice/dhRoll.mjs | 47 ++++++++++++++++++--- module/dice/dualityRoll.mjs | 19 ++++++--- module/documents/actor.mjs | 2 +- module/helpers/handlebarsHelper.mjs | 5 ++- module/helpers/utils.mjs | 2 +- styles/less/ui/chat/theme-colorful.less | 2 + templates/dialogs/actionSelect.hbs | 4 +- 21 files changed, 210 insertions(+), 103 deletions(-) create mode 100644 module/data/fields/actorField.mjs diff --git a/lang/en.json b/lang/en.json index a2d4bfd8..bfaac070 100755 --- a/lang/en.json +++ b/lang/en.json @@ -686,11 +686,11 @@ } }, "RollTypes": { - "ability": { - "name": "Ability" + "trait": { + "name": "Trait" }, - "weapon": { - "name": "Weapon" + "attack": { + "name": "Attack" }, "spellcast": { "name": "SpellCast" diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index f295d2a6..aa604dda 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -14,8 +14,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo } addChatListeners = async (app, html, data) => { - super.addChatListeners(app, html, data); - html.querySelectorAll('.duality-action-damage').forEach(element => element.addEventListener('click', event => this.onRollDamage(event, data.message)) ); diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index 4216f631..3522b41a 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -388,17 +388,17 @@ export const countdownTypes = { } }; export const rollTypes = { - weapon: { - id: 'weapon', - label: 'DAGGERHEART.CONFIG.RollTypes.weapon.name' + attack: { + id: 'attack', + label: 'DAGGERHEART.CONFIG.RollTypes.attack.name' }, spellcast: { id: 'spellcast', label: 'DAGGERHEART.CONFIG.RollTypes.spellcast.name' }, - ability: { - id: 'ability', - label: 'DAGGERHEART.CONFIG.RollTypes.ability.name' + trait: { + id: 'trait', + label: 'DAGGERHEART.CONFIG.RollTypes.trait.name' }, diceSet: { id: 'diceSet', diff --git a/module/data/action/attackAction.mjs b/module/data/action/attackAction.mjs index d953416a..137879b8 100644 --- a/module/data/action/attackAction.mjs +++ b/module/data/action/attackAction.mjs @@ -5,7 +5,7 @@ export default class DHAttackAction extends DHDamageAction { static extraSchemas = [...super.extraSchemas, ...['roll', 'save']]; static getRollType(parent) { - return parent.type === 'weapon' ? 'weapon' : 'spellcast'; + return parent.type === 'weapon' ? 'attack' : 'spellcast'; } get chatTemplate() { @@ -21,7 +21,7 @@ export default class DHAttackAction extends DHDamageAction { } if (this.roll.useDefault) { this.roll.trait = this.item.system.attack.roll.trait; - this.roll.type = 'weapon'; + this.roll.type = 'attack'; } } } @@ -37,4 +37,8 @@ export default class DHAttackAction extends DHDamageAction { base: true }; } + + // get modifiers() { + // return []; + // } } diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 6fdadfe2..4140e024 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -150,7 +150,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel { } static getRollType(parent) { - return 'ability'; + return 'trait'; } static getSourceConfig(parent) { @@ -308,7 +308,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel { prepareRoll() { const roll = { - modifiers: [], + modifiers: this.modifiers, trait: this.roll?.trait, label: 'Attack', type: this.actionType, @@ -362,6 +362,13 @@ export default class DHBaseAction extends foundry.abstract.DataModel { get hasRoll() { return !!this.roll?.type || !!this.roll?.bonus; } + + get modifiers() { + if(!this.actor) return []; + const modifiers = []; + /** Placeholder for specific bonuses **/ + return modifiers; + } /* ROLL */ /* SAVE */ diff --git a/module/data/action/damageAction.mjs b/module/data/action/damageAction.mjs index 7f7faeee..492c4184 100644 --- a/module/data/action/damageAction.mjs +++ b/module/data/action/damageAction.mjs @@ -28,6 +28,7 @@ export default class DHDamageAction extends DHBaseAction { hasSave: this.hasSave, isCritical: data.system?.roll?.isCritical ?? false, source: data.system?.source, + data: this.getRollData(), damageTypes, event }; @@ -39,4 +40,8 @@ export default class DHDamageAction extends DHBaseAction { roll = CONFIG.Dice.daggerheart.DamageRoll.build(config); } + + // get modifiers() { + // return []; + // } } diff --git a/module/data/action/healingAction.mjs b/module/data/action/healingAction.mjs index 26e53817..4c1366a1 100644 --- a/module/data/action/healingAction.mjs +++ b/module/data/action/healingAction.mjs @@ -39,4 +39,8 @@ export default class DHHealingAction extends DHBaseAction { get chatTemplate() { return 'systems/daggerheart/templates/ui/chat/healing-roll.hbs'; } + + get modifiers() { + return []; + } } diff --git a/module/data/actor/adversary.mjs b/module/data/actor/adversary.mjs index 78e08323..6ecee0a1 100644 --- a/module/data/actor/adversary.mjs +++ b/module/data/actor/adversary.mjs @@ -1,13 +1,7 @@ import DHAdversarySettings from '../../applications/sheets-configs/adversary-settings.mjs'; import ActionField from '../fields/actionField.mjs'; import BaseDataActor from './base.mjs'; - -const resourceField = () => - new foundry.data.fields.SchemaField({ - value: new foundry.data.fields.NumberField({ initial: 0, integer: true }), - max: new foundry.data.fields.NumberField({ initial: 0, integer: true }), - isReversed: new foundry.data.fields.BooleanField({ initial: true }) - }); +import { resourceField, bonusField } from '../fields/actorField.mjs'; export default class DhpAdversary extends BaseDataActor { static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Adversary']; @@ -43,8 +37,8 @@ export default class DhpAdversary extends BaseDataActor { severe: new fields.NumberField({ required: true, initial: 0, integer: true }) }), resources: new fields.SchemaField({ - hitPoints: resourceField(), - stress: resourceField() + hitPoints: resourceField(0, true), + stress: resourceField(0, true) }), attack: new ActionField({ initial: { @@ -59,7 +53,7 @@ export default class DhpAdversary extends BaseDataActor { amount: 1 }, roll: { - type: 'weapon' + type: 'attack' }, damage: { parts: [ @@ -80,9 +74,14 @@ export default class DhpAdversary extends BaseDataActor { }) ), bonuses: new fields.SchemaField({ - difficulty: new fields.SchemaField({ - all: new fields.NumberField({ integer: true, initial: 0 }), - reaction: new fields.NumberField({ integer: true, initial: 0 }) + roll: new fields.SchemaField({ + attack: bonusField(), + action: bonusField(), + reaction: bonusField() + }), + damage: new fields.SchemaField({ + physical: bonusField(), + magical: bonusField() }) }) }; diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index e8322671..0df5dd2f 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -2,25 +2,7 @@ import { burden } from '../../config/generalConfig.mjs'; import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs'; import DhLevelData from '../levelData.mjs'; import BaseDataActor from './base.mjs'; - -const attributeField = () => - new foundry.data.fields.SchemaField({ - value: new foundry.data.fields.NumberField({ initial: 0, integer: true }), - tierMarked: new foundry.data.fields.BooleanField({ initial: false }) - }); - -const resourceField = (max, reverse = false) => - new foundry.data.fields.SchemaField({ - value: new foundry.data.fields.NumberField({ initial: 0, integer: true }), - max: new foundry.data.fields.NumberField({ initial: max, integer: true }), - isReversed: new foundry.data.fields.BooleanField({ initial: reverse }) - }); - -const stressDamageReductionRule = () => - new foundry.data.fields.SchemaField({ - enabled: new foundry.data.fields.BooleanField({ required: true, initial: false }), - cost: new foundry.data.fields.NumberField({ integer: true }) - }); +import { attributeField, resourceField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs'; export default class DhCharacter extends BaseDataActor { static get metadata() { @@ -94,22 +76,25 @@ export default class DhCharacter extends BaseDataActor { levelData: new fields.EmbeddedDataField(DhLevelData), bonuses: new fields.SchemaField({ roll: new fields.SchemaField({ - attack: new fields.NumberField({ integer: true, initial: 0 }), - primaryWeapon: new fields.SchemaField({ - attack: new fields.NumberField({ integer: true, initial: 0 }) - }), - spellcast: new fields.NumberField({ integer: true, initial: 0 }), - action: new fields.NumberField({ integer: true, initial: 0 }), - hopeOrFear: new fields.NumberField({ integer: true, initial: 0 }) + attack: bonusField(), + spellcast: bonusField(), + trait: bonusField(), + action: bonusField(), + reaction: bonusField(), + primaryWeapon: bonusField(), + secondaryWeapon: bonusField() }), damage: new fields.SchemaField({ - all: new fields.NumberField({ integer: true, initial: 0 }), - physical: new fields.NumberField({ integer: true, initial: 0 }), - magic: new fields.NumberField({ integer: true, initial: 0 }), - primaryWeapon: new fields.SchemaField({ - bonus: new fields.NumberField({ integer: true }), - extraDice: new fields.NumberField({ integer: true }) - }) + physical: bonusField(), + magical: bonusField(), + primaryWeapon: bonusField(), + secondaryWeapon: bonusField() + }), + healing: bonusField(), + range: new fields.SchemaField({ + weapon: new fields.NumberField({ integer: true, initial: 0 }), + spell: new fields.NumberField({ integer: true, initial: 0 }), + other: new fields.NumberField({ integer: true, initial: 0 }) }) }), companion: new ForeignDocumentUUIDField({ type: 'Actor', nullable: true, initial: null }), @@ -181,6 +166,11 @@ export default class DhCharacter extends BaseDataActor { return !this.class.value || !this.class.subclass; } + get spellcastModifier() { + const subClasses = this.parent.items.filter(x => x.type === 'subclass') ?? []; + return Math.max(subClasses?.map(sc => this.traits[sc.system.spellcastingTrait]?.value)); + } + get spellcastingModifiers() { return { main: this.class.subclass?.system?.spellcastingTrait, diff --git a/module/data/actor/companion.mjs b/module/data/actor/companion.mjs index ea417bd8..f7e94311 100644 --- a/module/data/actor/companion.mjs +++ b/module/data/actor/companion.mjs @@ -4,6 +4,7 @@ import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs'; import ActionField from '../fields/actionField.mjs'; import { adjustDice, adjustRange } from '../../helpers/utils.mjs'; import DHCompanionSettings from '../../applications/sheets-configs/companion-settings.mjs'; +import { resourceField, bonusField } from '../fields/actorField.mjs'; export default class DhCompanion extends BaseDataActor { static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Companion']; @@ -23,11 +24,7 @@ export default class DhCompanion extends BaseDataActor { ...super.defineSchema(), partner: new ForeignDocumentUUIDField({ type: 'Actor' }), resources: new fields.SchemaField({ - stress: new fields.SchemaField({ - value: new fields.NumberField({ initial: 0, integer: true }), - max: new fields.NumberField({ initial: 3, integer: true }), - isReversed: new foundry.data.fields.BooleanField({ initial: true }) - }), + stress: resourceField(3, true), hope: new fields.NumberField({ initial: 0, integer: true }) }), evasion: new fields.NumberField({ required: true, min: 1, initial: 10, integer: true }), @@ -56,7 +53,7 @@ export default class DhCompanion extends BaseDataActor { amount: 1 }, roll: { - type: 'weapon', + type: 'attack', bonus: 0, trait: 'instinct' }, @@ -74,7 +71,13 @@ export default class DhCompanion extends BaseDataActor { } }), actions: new fields.ArrayField(new ActionField()), - levelData: new fields.EmbeddedDataField(DhLevelData) + levelData: new fields.EmbeddedDataField(DhLevelData), + bonuses: new fields.SchemaField({ + damage: new fields.SchemaField({ + physical: bonusField(), + magical: bonusField() + }) + }) }; } @@ -89,7 +92,7 @@ export default class DhCompanion extends BaseDataActor { } prepareBaseData() { - const partnerSpellcastingModifier = this.partner?.system?.spellcastingModifiers?.main; + const partnerSpellcastingModifier = this.partner?.system?.spellcastModifier; const spellcastingModifier = this.partner?.system?.traits?.[partnerSpellcastingModifier]?.value; this.attack.roll.bonus = spellcastingModifier ?? 0; // Needs to expand on which modifier it is that should be used because of multiclassing; diff --git a/module/data/fields/actorField.mjs b/module/data/fields/actorField.mjs new file mode 100644 index 00000000..dc8dcbac --- /dev/null +++ b/module/data/fields/actorField.mjs @@ -0,0 +1,28 @@ +const fields = foundry.data.fields; + +const attributeField = () => + new fields.SchemaField({ + value: new fields.NumberField({ initial: 0, integer: true }), + tierMarked: new fields.BooleanField({ initial: false }) + }); + +const resourceField = (max = 0, reverse = false) => + new fields.SchemaField({ + value: new fields.NumberField({ initial: 0, integer: true }), + max: new fields.NumberField({ initial: max, integer: true }), + isReversed: new fields.BooleanField({ initial: reverse }) + }); + +const stressDamageReductionRule = () => + new fields.SchemaField({ + enabled: new fields.BooleanField({ required: true, initial: false }), + cost: new fields.NumberField({ integer: true }) + }); + +const bonusField = () => + new fields.SchemaField({ + bonus: new fields.NumberField({ integer: true, initial: 0 }), + dice: new fields.ArrayField(new fields.StringField()) + }) + +export { attributeField, resourceField, stressDamageReductionRule, bonusField }; \ No newline at end of file diff --git a/module/data/item/weapon.mjs b/module/data/item/weapon.mjs index 9fbb3eba..0d0c7f76 100644 --- a/module/data/item/weapon.mjs +++ b/module/data/item/weapon.mjs @@ -50,7 +50,7 @@ export default class DHWeapon extends AttachableItem { }, roll: { trait: 'agility', - type: 'weapon' + type: 'attack' }, damage: { parts: [ diff --git a/module/dice/d20Roll.mjs b/module/dice/d20Roll.mjs index c9e9d428..8e13bf46 100644 --- a/module/dice/d20Roll.mjs +++ b/module/dice/d20Roll.mjs @@ -74,7 +74,6 @@ export default class D20Roll extends DHRoll { } constructFormula(config) { - // this.terms = []; this.createBaseDice(); this.configureModifiers(); this.resetFormula(); @@ -91,7 +90,10 @@ export default class D20Roll extends DHRoll { configureModifiers() { this.applyAdvantage(); - this.applyBaseBonus(); + + this.baseTerms = foundry.utils.deepClone(this.terms); + + this.options.roll.modifiers = this.applyBaseBonus(); this.options.experiences?.forEach(m => { if (this.options.data.experiences?.[m]) @@ -100,13 +102,8 @@ export default class D20Roll extends DHRoll { value: this.options.data.experiences[m].value }); }); - - this.options.roll.modifiers?.forEach(m => { - this.terms.push(...this.formatModifier(m.value)); - }); - - this.baseTerms = foundry.utils.deepClone(this.terms); - + + this.addModifiers(); if (this.options.extraFormula) { this.terms.push( new foundry.dice.terms.OperatorTerm({ operator: '+' }), @@ -125,13 +122,18 @@ export default class D20Roll extends DHRoll { } applyBaseBonus() { - this.options.roll.modifiers = []; - if (!this.options.roll.bonus) return; - this.options.roll.modifiers.push({ - label: 'Bonus to Hit', - value: this.options.roll.bonus - // value: Roll.replaceFormulaData('@attackBonus', this.data) - }); + const modifiers = []; + + if(this.options.roll.bonus) + modifiers.push({ + label: 'Bonus to Hit', + value: this.options.roll.bonus + }); + + modifiers.push(...this.getBonus(`roll.${this.options.type}`, `${this.options.type.capitalize()} Bonus`)); + modifiers.push(...this.getBonus(`roll.${this.options.roll.type}`, `${this.options.roll.type.capitalize()} Bonus`)); + + return modifiers; } static async buildEvaluate(roll, config = {}, message = {}) { diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs index 2182d3d0..8b835583 100644 --- a/module/dice/damageRoll.mjs +++ b/module/dice/damageRoll.mjs @@ -24,8 +24,26 @@ export default class DamageRoll extends DHRoll { } } + applyBaseBonus() { + const modifiers = [], + type = this.options.messageType ?? 'damage'; + + modifiers.push(...this.getBonus(`${type}`, `${type.capitalize()} Bonus`)); + this.options.damageTypes?.forEach(t => { + modifiers.push(...this.getBonus(`${type}.${t}`, `${t.capitalize()} ${type.capitalize()} Bonus`)); + }); + const weapons = ['primaryWeapon', 'secondaryWeapon']; + weapons.forEach(w => { + if(this.options.source.item && this.options.source.item === this.data[w]?.id) + modifiers.push(...this.getBonus(`${type}.${w}`, 'Weapon Bonus')); + }); + + return modifiers; + } + constructFormula(config) { super.constructFormula(config); + if (config.isCritical) { const tmpRoll = new Roll(this._formula)._evaluateSync({ maximize: true }), criticalBonus = tmpRoll.total - this.constructor.calculateTotalModifiers(tmpRoll); diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index 8744223a..22903d6a 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -4,6 +4,7 @@ export default class DHRoll extends Roll { baseTerms = []; constructor(formula, data, options) { super(formula, data, options); + if(!this.data || !Object.keys(this.data).length) this.data = options.data; } static messageType = 'adversaryRoll'; @@ -99,11 +100,44 @@ export default class DHRoll extends Roll { } formatModifier(modifier) { - const numTerm = modifier < 0 ? '-' : '+'; - return [ - new foundry.dice.terms.OperatorTerm({ operator: numTerm }), - new foundry.dice.terms.NumericTerm({ number: Math.abs(modifier) }) - ]; + if(Array.isArray(modifier)) { + return [ + new foundry.dice.terms.OperatorTerm({ operator: '+' }), + ...this.constructor.parse(modifier.join(' + '), this.options.data) + ]; + } else { + const numTerm = modifier < 0 ? '-' : '+'; + return [ + new foundry.dice.terms.OperatorTerm({ operator: numTerm }), + new foundry.dice.terms.NumericTerm({ number: Math.abs(modifier) }) + ]; + } + } + + applyBaseBonus() { + return []; + } + + addModifiers() { + this.options.roll.modifiers?.forEach(m => { + this.terms.push(...this.formatModifier(m.value)); + }); + } + + getBonus(path, label) { + const bonus = foundry.utils.getProperty(this.data.bonuses, path), + modifiers = []; + if(bonus?.bonus) + modifiers.push({ + label: label, + value: bonus?.bonus + }); + if(bonus?.dice?.length) + modifiers.push({ + label: label, + value: bonus?.dice + }); + return modifiers; } getFaces(faces) { @@ -113,6 +147,9 @@ export default class DHRoll extends Roll { constructFormula(config) { this.terms = Roll.parse(this.options.roll.formula, config.data); + this.options.roll.modifiers = this.applyBaseBonus(); + this.addModifiers(); + if (this.options.extraFormula) { this.terms.push( new foundry.dice.terms.OperatorTerm({ operator: '+' }), diff --git a/module/dice/dualityRoll.mjs b/module/dice/dualityRoll.mjs index 1044b93a..99e4fa42 100644 --- a/module/dice/dualityRoll.mjs +++ b/module/dice/dualityRoll.mjs @@ -119,12 +119,21 @@ export default class DualityRoll extends D20Roll { } applyBaseBonus() { - this.options.roll.modifiers = []; - if (!this.options.roll.trait) return; - this.options.roll.modifiers.push({ - label: `DAGGERHEART.CONFIG.Traits.${this.options.roll.trait}.name`, - value: Roll.replaceFormulaData(`@traits.${this.options.roll.trait}.value`, this.data) + const modifiers = super.applyBaseBonus(); + + if(this.options.roll.trait && this.data.traits[this.options.roll.trait]) + modifiers.unshift({ + label: `DAGGERHEART.CONFIG.Traits.${this.options.roll.trait}.name`, + value: this.data.traits[this.options.roll.trait].value + }); + + const weapons = ['primaryWeapon', 'secondaryWeapon']; + weapons.forEach(w => { + if(this.options.source.item && this.options.source.item === this.data[w]?.id) + modifiers.push(...this.getBonus(`roll.${w}`, 'Weapon Bonus')); }); + + return modifiers; } static postEvaluate(roll, config = {}) { diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index c789064b..73a8a3db 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -371,7 +371,7 @@ export default class DhpActor extends Actor { getRollData() { const rollData = super.getRollData(); rollData.prof = this.system.proficiency ?? 1; - rollData.cast = this.system.spellcast ?? 1; + rollData.cast = this.system.spellcastModifier ?? 1; return rollData; } diff --git a/module/helpers/handlebarsHelper.mjs b/module/helpers/handlebarsHelper.mjs index 407f065e..17326dc2 100644 --- a/module/helpers/handlebarsHelper.mjs +++ b/module/helpers/handlebarsHelper.mjs @@ -39,8 +39,9 @@ export default class RegisterHandlebarsHelpers { } static damageSymbols(damageParts) { - const symbols = new Set(); - damageParts.forEach(part => symbols.add(...CONFIG.DH.GENERAL.damageTypes[part.type].icon)); + const symbols = [...new Set(damageParts.reduce((a, c) => a.concat([...c.type]), []))].map( + p => CONFIG.DH.GENERAL.damageTypes[p].icon + ); return new Handlebars.SafeString(Array.from(symbols).map(symbol => ``)); } diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 1e694aad..336ecf5b 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -306,7 +306,7 @@ export const itemAbleRollParse = (value, actor, item) => { const isItemTarget = value.toLowerCase().startsWith('item.'); const slicedValue = isItemTarget ? value.slice(5) : value; try { - return Roll.safeEval(Roll.replaceFormulaData(slicedValue, isItemTarget ? item : actor)); + return Roll.replaceFormulaData(slicedValue, isItemTarget ? item : actor); } catch (_) { return ''; } diff --git a/styles/less/ui/chat/theme-colorful.less b/styles/less/ui/chat/theme-colorful.less index d9c4e08e..3de5b432 100644 --- a/styles/less/ui/chat/theme-colorful.less +++ b/styles/less/ui/chat/theme-colorful.less @@ -44,12 +44,14 @@ display: flex; gap: 2px; margin-bottom: 4px; + flex-wrap: wrap; .duality-modifier { padding: 2px; border-radius: 6px; border: 1px solid; background: var(--color-dark-6); font-size: 12px; + white-space: nowrap; } } .dice-flavor { diff --git a/templates/dialogs/actionSelect.hbs b/templates/dialogs/actionSelect.hbs index 200ef29c..f2190121 100644 --- a/templates/dialogs/actionSelect.hbs +++ b/templates/dialogs/actionSelect.hbs @@ -5,8 +5,8 @@
    {{#each actions}}
  • - - {{ name }} + +
  • {{/each}}