diff --git a/daggerheart.mjs b/daggerheart.mjs index de8cbe4f..c17117ac 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -74,6 +74,7 @@ Hooks.once('init', () => { CONFIG.ChatMessage.dataModels = { dualityRoll: models.DhpDualityRoll, adversaryRoll: models.DhpAdversaryRoll, + damageRoll: models.DhpDamageRoll, abilityUse: models.DhpAbilityUse }; CONFIG.ChatMessage.documentClass = applications.DhpChatMessage; diff --git a/lang/en.json b/lang/en.json index 691afd13..a5b81d59 100644 --- a/lang/en.json +++ b/lang/en.json @@ -86,7 +86,8 @@ "SecondaryEquipWhileTwohanded": "A secondary weapon can't be equipped together with a Two-Handed weapon.", "TwohandedEquipWhileSecondary": "Can't equip a Two-Handed weapon together with a secondary weapon.", "SelectClassBeforeSubclass": "Select a Class before selecting a Subclass.", - "SubclassNotOfClass": "This Subclass doesn't belong to your current Class." + "SubclassNotOfClass": "This Subclass doesn't belong to your current Class.", + "AttackTargetDoesNotExist": "The target token no longer exists" }, "Error": { "NoClassSelected": "Your character has no class selected!", @@ -101,8 +102,14 @@ "Hope": "Hope", "Fear": "Fear", "CriticalSuccess": "Critical Success", - "Advantage": "Advantage", - "Disadvantage": "Disadvantage", + "Advantage": { + "Full": "Advantage", + "Short": "Adv" + }, + "Disadvantage": { + "Full": "Disadvantage", + "Short": "Dis" + }, "OK": "OK", "Cancel": "Cancel", "Or": "Or", @@ -748,6 +755,7 @@ "AdvantageChooseTitle": "Select Hope Dice" }, "DamageRoll": { + "DealDamageToTargets": "Damage Hit Targets", "DealDamage": "Deal Damage" }, "HealingRoll": { @@ -899,8 +907,8 @@ "Stress": "Stress", "Experience": "Experience", "Experiences": "Experiences", - "Moves": "Moves", - "NewMove": "New Move" + "Features": "Features", + "NewFeature": "New Feature" }, "Environment": { "ToneAndFeel": "Tone And feel", diff --git a/module/applications/chatMessage.mjs b/module/applications/chatMessage.mjs index e83e174f..004e7dd5 100644 --- a/module/applications/chatMessage.mjs +++ b/module/applications/chatMessage.mjs @@ -1,6 +1,11 @@ export default class DhpChatMesssage extends ChatMessage { async renderHTML() { - if (this.type === 'dualityRoll' || this.type === 'adversaryRoll' || this.type === 'abilityUse') { + if ( + this.type === 'dualityRoll' || + this.type === 'adversaryRoll' || + this.type === 'damageRoll' || + this.type === 'abilityUse' + ) { this.content = await foundry.applications.handlebars.renderTemplate(this.content, this.system); } diff --git a/module/applications/damageSelectionDialog.mjs b/module/applications/damageSelectionDialog.mjs index 4540ae6d..5553c98f 100644 --- a/module/applications/damageSelectionDialog.mjs +++ b/module/applications/damageSelectionDialog.mjs @@ -1,7 +1,7 @@ const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; export default class DamageSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) { - constructor(rollString, bonusDamage, hope, resolve) { + constructor(rollString, bonusDamage, resolve, hope = 0) { super({}); this.data = { diff --git a/module/applications/npcRollSelectionDialog.mjs b/module/applications/npcRollSelectionDialog.mjs index bee3dd16..b7c62c76 100644 --- a/module/applications/npcRollSelectionDialog.mjs +++ b/module/applications/npcRollSelectionDialog.mjs @@ -6,7 +6,6 @@ export default class NpcRollSelectionDialog extends FormApplication { this.resolve = resolve; this.selectedExperiences = []; this.data = { - nrDice: 1, advantage: null }; } @@ -19,7 +18,7 @@ export default class NpcRollSelectionDialog extends FormApplication { const defaults = super.defaultOptions; const overrides = { height: 'auto', - width: 400, + width: 500, id: 'roll-selection', template: 'systems/daggerheart/templates/views/npcRollSelection.hbs', closeOnSubmit: false, @@ -34,11 +33,11 @@ export default class NpcRollSelectionDialog extends FormApplication { async getData() { const context = super.getData(); - context.nrDice = this.data.nrDice; context.advantage = this.data.advantage; - context.experiences = this.experiences.map(x => ({ + context.experiences = Object.values(this.experiences).map(x => ({ ...x, - selected: this.selectedExperiences.find(selected => selected.id === x.id) + selected: this.selectedExperiences.find(selected => selected.id === x.id), + value: `${x.value >= 0 ? '+' : '-'}${x.value}` })); return context; @@ -47,17 +46,10 @@ export default class NpcRollSelectionDialog extends FormApplication { activateListeners(html) { super.activateListeners(html); - html.find('.increase').click(_ => this.updateNrDice(1)); - html.find('.decrease').click(_ => this.updateNrDice(-1)); html.find('.advantage').click(_ => this.updateIsAdvantage(true)); html.find('.disadvantage').click(_ => this.updateIsAdvantage(false)); html.find('.roll-button').click(this.finish.bind(this)); - html.find('.roll-dialog-chip').click(this.selectExperience.bind(this)); - } - - updateNrDice(value) { - this.data.nrDice += value; - this.render(); + html.find('.experience-chip').click(this.selectExperience.bind(this)); } updateIsAdvantage(advantage) { @@ -66,9 +58,9 @@ export default class NpcRollSelectionDialog extends FormApplication { } selectExperience(event) { - const experience = this.experiences[event.currentTarget.dataset.key]; - this.selectedExperiences = this.selectedExperiences.find(x => x.name === experience.name) - ? this.selectedExperiences.filter(x => x.name !== experience.name) + const experience = Object.values(this.experiences).find(experience => experience.id === event.currentTarget.id); + this.selectedExperiences = this.selectedExperiences.find(x => x.id === experience.id) + ? this.selectedExperiences.filter(x => x.id !== experience.id) : [...this.selectedExperiences, experience]; this.render(); diff --git a/module/applications/sheets/adversary.mjs b/module/applications/sheets/adversary.mjs index 25afe393..c1637dc9 100644 --- a/module/applications/sheets/adversary.mjs +++ b/module/applications/sheets/adversary.mjs @@ -349,7 +349,7 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) { static async attackRoll(event, button) { const modifier = Number.parseInt(button.dataset.value); - const { roll, diceResults, modifiers } = await this.actor.diceRoll( + const { roll, dice, advantageState, modifiers } = await this.actor.diceRoll( { title: `${this.actor.name} - Attack Roll`, value: modifier }, event.shiftKey ); @@ -365,11 +365,14 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) { const cls = getDocumentClass('ChatMessage'); const msg = new cls({ type: 'adversaryRoll', + sound: CONFIG.sounds.dice, system: { + origin: this.document.id, roll: roll._formula, + advantageState, total: roll._total, modifiers: modifiers, - diceResults: diceResults, + dice: dice, targets: targets, damage: { value: button.dataset.damage, type: button.dataset.damageType } }, @@ -381,16 +384,15 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) { } static async addExperience() { + const experienceId = foundry.utils.randomID(); await this.document.update({ - 'system.experiences': [...this.document.system.experiences, { name: 'Experience', value: 1 }] + [`system.experiences.${experienceId}`]: { id: experienceId, name: 'Experience', value: 1 } }); } static async removeExperience(_, button) { await this.document.update({ - 'system.experiences': this.document.system.experiences.filter( - (_, index) => index !== Number.parseInt(button.dataset.experience) - ) + [`system.experiences.-=${button.dataset.experience}`]: null }); } diff --git a/module/data/_module.mjs b/module/data/_module.mjs index bc3839d7..4822229f 100644 --- a/module/data/_module.mjs +++ b/module/data/_module.mjs @@ -14,5 +14,6 @@ export { default as DhpWeapon } from './weapon.mjs'; export { default as DhpArmor } from './armor.mjs'; export { default as DhpDualityRoll } from './dualityRoll.mjs'; export { default as DhpAdversaryRoll } from './adversaryRoll.mjs'; +export { default as DhpDamageRoll } from './damageRoll.mjs'; export { default as DhpAbilityUse } from './abilityUse.mjs'; export { default as DhpEnvironment } from './environment.mjs'; diff --git a/module/data/adversary.mjs b/module/data/adversary.mjs index 6d15cf3c..f625c28f 100644 --- a/module/data/adversary.mjs +++ b/module/data/adversary.mjs @@ -35,20 +35,20 @@ export default class DhpAdversary extends foundry.abstract.TypeDataModel { }), difficulty: new fields.NumberField({ initial: 1, integer: true }), damageThresholds: new fields.SchemaField({ - minor: new fields.NumberField({ initial: 0, integer: true }), major: new fields.NumberField({ initial: 0, integer: true }), severe: new fields.NumberField({ initial: 0, integer: true }) }), - experiences: new fields.ArrayField( + experiences: new fields.TypedObjectField( new fields.SchemaField({ - name: new fields.StringField({}), + id: new fields.StringField({ required: true }), + name: new fields.StringField(), value: new fields.NumberField({ integer: true, nullable: true, initial: null }) }) ) }; } - get moves() { + get features() { return this.parent.items.filter(x => x.type === 'feature'); } } diff --git a/module/data/adversaryRoll.mjs b/module/data/adversaryRoll.mjs index a218f65c..3619b143 100644 --- a/module/data/adversaryRoll.mjs +++ b/module/data/adversaryRoll.mjs @@ -3,6 +3,7 @@ export default class DhpAdversaryRoll extends foundry.abstract.TypeDataModel { const fields = foundry.data.fields; return { + origin: new fields.StringField({ required: true }), roll: new fields.StringField({}), total: new fields.NumberField({ integer: true }), modifiers: new fields.ArrayField( @@ -12,12 +13,8 @@ export default class DhpAdversaryRoll extends foundry.abstract.TypeDataModel { title: new fields.StringField({}) }) ), - diceResults: new fields.ArrayField( - new fields.SchemaField({ - value: new fields.NumberField({ integer: true }), - discarded: new fields.BooleanField({ initial: false }) - }) - ), + advantageState: new fields.NumberField({ required: true, choices: [0, 1, 2], initial: 0 }), + dice: new fields.EmbeddedDataField(DhpAdversaryRollDice), targets: new fields.ArrayField( new fields.SchemaField({ id: new fields.StringField({}), @@ -39,17 +36,17 @@ export default class DhpAdversaryRoll extends foundry.abstract.TypeDataModel { } prepareDerivedData() { - const diceKeys = Object.keys(this.diceResults); + const diceKeys = Object.keys(this.dice.rolls); const highestIndex = 0; for (var index in diceKeys) { const resultIndex = Number.parseInt(index); if (highestIndex === resultIndex) continue; - const current = this.diceResults[resultIndex]; - const highest = this.diceResults[highestIndex]; + const current = this.dice.rolls[resultIndex]; + const highest = this.dice.rolls[highestIndex]; - if (current.value > highest.value) this.diceResults[highestIndex].discarded = true; - else this.diceResults[resultIndex].discarded = true; + if (current.value > highest.value) this.dice.rolls[highestIndex].discarded = true; + else this.dice.rolls[resultIndex].discarded = true; } this.targets.forEach(target => { @@ -57,3 +54,23 @@ export default class DhpAdversaryRoll extends foundry.abstract.TypeDataModel { }); } } + +class DhpAdversaryRollDice extends foundry.abstract.DataModel { + static defineSchema() { + const fields = foundry.data.fields; + + return { + type: new fields.StringField({ required: true }), + rolls: new fields.ArrayField( + new fields.SchemaField({ + value: new fields.NumberField({ required: true, integer: true }), + discarded: new fields.BooleanField({ initial: false }) + }) + ) + }; + } + + get rollTotal() { + return this.rolls.reduce((acc, roll) => acc + roll.value, 0); + } +} diff --git a/module/data/damageRoll.mjs b/module/data/damageRoll.mjs new file mode 100644 index 00000000..c44819c5 --- /dev/null +++ b/module/data/damageRoll.mjs @@ -0,0 +1,42 @@ +export default class DhpDamageRoll extends foundry.abstract.TypeDataModel { + static defineSchema() { + const fields = foundry.data.fields; + + return { + roll: new fields.StringField({ required: true }), + damage: new fields.SchemaField({ + total: new fields.NumberField({ required: true, integer: true }), + type: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.damageTypes), integer: false }) + }), + dice: new fields.ArrayField(new fields.EmbeddedDataField(DhpDamageDice)), + modifiers: new fields.ArrayField( + new fields.SchemaField({ + value: new fields.NumberField({ required: true, integer: true }), + operator: new fields.StringField({ required: true, choices: ['+', '-', '*', '/'] }) + }) + ), + targets: new fields.ArrayField( + new fields.SchemaField({ + id: new fields.StringField({ required: true }), + name: new fields.StringField(), + img: new fields.StringField() + }) + ) + }; + } +} + +class DhpDamageDice extends foundry.abstract.DataModel { + static defineSchema() { + const fields = foundry.data.fields; + + return { + type: new fields.StringField({ required: true }), + rolls: new fields.ArrayField(new fields.NumberField({ required: true, integer: true })) + }; + } + + get rollTotal() { + return this.rolls.reduce((acc, roll) => acc + roll, 0); + } +} diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index cbcf0ed5..5c9429f9 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -68,7 +68,6 @@ export default class DhpActor extends Actor { } async npcRoll(modifier, shiftKey) { - let nrDice = 1; let advantage = null; const modifiers = [ @@ -84,7 +83,6 @@ export default class DhpActor extends Actor { }); const result = await dialogClosed; - nrDice = result.nrDice; advantage = result.advantage; result.experiences.forEach(x => modifiers.push({ @@ -95,13 +93,20 @@ export default class DhpActor extends Actor { ); } - const roll = new Roll( - `${nrDice}d20${advantage === true ? 'kh' : advantage === false ? 'kl' : ''} ${modifiers.map(x => `+ ${x.value}`).join(' ')}` + const roll = Roll.create( + `${advantage === true || advantage === false ? 2 : 1}d20${advantage === true ? 'kh' : advantage === false ? 'kl' : ''} ${modifiers.map(x => `+ ${x.value}`).join(' ')}` ); let rollResult = await roll.evaluate(); - const diceResults = rollResult.dice.flatMap(x => x.results.flatMap(result => ({ value: result.result }))); + const dice = []; + for (var i = 0; i < rollResult.terms.length; i++) { + const term = rollResult.terms[i]; + if (term.faces) { + dice.push({ type: `d${term.faces}`, rolls: term.results.map(x => ({ value: x.result })) }); + } + } - return { roll, diceResults: diceResults, modifiers: modifiers }; + // There is Only ever one dice term here + return { roll, dice: dice[0], modifiers, advantageState: advantage === true ? 1 : advantage === false ? 2 : 0 }; } async dualityRoll(modifier, shiftKey, bonusDamage = []) { @@ -245,14 +250,12 @@ export default class DhpActor extends Actor { }; } - async damageRoll(damage, shiftKey) { + async damageRoll(damage, targets, shiftKey) { let rollString = damage.value; let bonusDamage = damage.bonusDamage?.filter(x => x.initiallySelected) ?? []; if (!shiftKey) { const dialogClosed = new Promise((resolve, _) => { - new DamageSelectionDialog(rollString, bonusDamage, this.system.resources.hope.value, resolve).render( - true - ); + new DamageSelectionDialog(rollString, bonusDamage, resolve).render(true); }); const result = await dialogClosed; bonusDamage = result.bonusDamage; @@ -274,23 +277,30 @@ export default class DhpActor extends Actor { for (var i = 0; i < rollResult.terms.length; i++) { const term = rollResult.terms[i]; if (term.faces) { - dice.push({ type: `d${term.faces}`, value: term.total }); + dice.push({ type: `d${term.faces}`, rolls: term.results.map(x => x.result) }); } else if (term.operator) { } else if (term.number) { const operator = i === 0 ? '' : rollResult.terms[i - 1].operator; - modifiers.push(`${operator}${term.number}`); + modifiers.push({ value: term.number, operator: operator }); } } const cls = getDocumentClass('ChatMessage'); const msg = new cls({ + type: 'damageRoll', user: game.user.id, - content: await renderTemplate('systems/daggerheart/templates/chat/damage-roll.hbs', { + sound: CONFIG.sounds.dice, + system: { roll: rollString, - total: rollResult.total, + damage: { + total: rollResult.total, + type: damage.type + }, dice: dice, - modifiers: modifiers - }), + modifiers: modifiers, + targets: targets + }, + content: 'systems/daggerheart/templates/chat/damage-roll.hbs', rolls: [roll] }); diff --git a/module/ui/chatLog.mjs b/module/ui/chatLog.mjs index 49972386..e6553e3c 100644 --- a/module/ui/chatLog.mjs +++ b/module/ui/chatLog.mjs @@ -17,16 +17,21 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo html.querySelectorAll('.roll-damage-button').forEach(element => element.addEventListener('click', event => this.onRollDamage(event, data.message)) ); - html.querySelectorAll('.target-container').forEach(element => - element.addEventListener('hover', hover(this.hoverTarget, this.unhoverTarget)) - ); // ???? - // html.find('.target-container').mouseout(this.unhoverTarget); - html.querySelectorAll('.damage-button').forEach(element => element.addEventListener('click', this.onDamage)); + html.querySelectorAll('.target-container').forEach(element => { + element.addEventListener('mouseenter', this.hoverTarget); + element.addEventListener('mouseleave', this.unhoverTarget); + element.addEventListener('click', this.clickTarget); + }); + html.querySelectorAll('.damage-button').forEach(element => + element.addEventListener('click', event => this.onDamage(event, data.message)) + ); html.querySelectorAll('.healing-button').forEach(element => element.addEventListener('click', this.onHealing)); html.querySelectorAll('.target-indicator').forEach(element => element.addEventListener('click', this.onToggleTargets) ); - html.querySelectorAll('.advantage').forEach(element => element.hover(this.hoverAdvantage)); // ?? + html.querySelectorAll('.advantage').forEach(element => + element.addEventListener('mouseenter', this.hoverAdvantage) + ); html.querySelectorAll('.advantage').forEach(element => element.addEventListener('click', event => this.selectAdvantage.bind(this)(event, data.message)) ); @@ -46,31 +51,49 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo onRollDamage = async (event, message) => { event.stopPropagation(); + const actor = game.actors.get(message.system.origin); + if (!actor || !game.user.isGM) return true; - await game.user.character.damageRoll(message.system.damage, event.shiftKey); + await actor.damageRoll( + message.system.damage, + message.system.targets.filter(x => x.hit).map(x => ({ id: x.id, name: x.name, img: x.img })), + event.shiftKey + ); }; hoverTarget = event => { event.stopPropagation(); const token = canvas.tokens.get(event.currentTarget.dataset.token); - if (!token.controlled) token._onHoverIn(event, { hoverOutOthers: true }); + if (!token?.controlled) token._onHoverIn(event, { hoverOutOthers: true }); }; unhoverTarget = event => { const token = canvas.tokens.get(event.currentTarget.dataset.token); - if (!token.controlled) token._onHoverOut(event); + if (!token?.controlled) token._onHoverOut(event); }; - onDamage = async event => { + clickTarget = event => { event.stopPropagation(); - const damage = Number.parseInt(event.currentTarget.dataset.value); - const targets = Array.from(game.user.targets); + const token = canvas.tokens.get(event.currentTarget.dataset.token); + if (!token) { + ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.AttackTargetDoesNotExist')); + return; + } + + game.canvas.pan(token); + }; + + onDamage = async (event, message) => { + event.stopPropagation(); + const targets = event.currentTarget.dataset.targetHit + ? message.system.targets.map(target => game.canvas.tokens.get(target.id)) + : Array.from(game.user.targets); if (targets.length === 0) ui.notifications.info(game.i18n.localize('DAGGERHEART.Notification.Info.NoTargetsSelected')); for (var target of targets) { - await target.actor.takeDamage(damage, event.currentTarget.dataset.type); + await target.actor.takeDamage(message.system.damage.total, message.system.damage.type); } }; diff --git a/styles/application.less b/styles/application.less index a0f61883..82da864b 100644 --- a/styles/application.less +++ b/styles/application.less @@ -296,6 +296,7 @@ div.daggerheart.views.multiclass { display: flex; align-items: center; margin-bottom: @fullMargin; + gap: 16px; .dice-container { display: flex; @@ -306,6 +307,7 @@ div.daggerheart.views.multiclass { position: relative; display: flex; align-items: center; + justify-content: center; i { font-size: 18px; @@ -319,12 +321,24 @@ div.daggerheart.views.multiclass { .dice-number { position: absolute; - top: calc(50% - 14px); - left: calc(50% - 7px); font-size: 24px; font-weight: bold; } } + + .advantage-container { + display: flex; + flex-direction: column; + gap: 2px; + flex: 1; + + .advantage-button { + &.active, + &:hover { + background: var(--button-hover-background-color); + } + } + } } } @@ -333,33 +347,19 @@ div.daggerheart.views.multiclass { align-items: flex-start; flex-wrap: wrap; gap: @halfMargin; - flex: 2; + flex: 1; height: 100%; - .roll-dialog-chip { - border: @thinBorder solid black; - border-radius: 6px; - flex-basis: calc(50% - 2px); - display: flex; - align-items: center; - justify-content: space-between; - cursor: pointer; - padding: @fullPadding; - background: grey; - overflow: hidden; + .experience-chip { + opacity: 0.6; + border-radius: 16px; + width: calc(50% - 4px); + white-space: nowrap; - &.hover { - filter: drop-shadow(0 0 3px @mainShadow); - } - - span { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - - &.selected i { - color: green; + &.active, + &:hover { + opacity: 1; + background: var(--button-hover-background-color); } } } diff --git a/styles/chat.less b/styles/chat.less index fe1483e3..e39fbc2e 100644 --- a/styles/chat.less +++ b/styles/chat.less @@ -30,7 +30,7 @@ &.roll { .dice-tooltip { - .dice-rolls { + .dice-rolls.duality { display: flex; align-items: center; justify-content: space-around; @@ -93,6 +93,11 @@ font-size: 16px; } } + + .attack-roll-advantage-container { + text-align: end; + font-weight: bold; + } } .dice-total { @@ -159,8 +164,20 @@ } } - .roll-damage-button { - margin-top: 5px; + .dice-actions { + display: flex; + gap: 4px; + + button { + flex: 1; + } + } + + .dice-result { + .roll-damage-button, + .damage-button { + margin-top: 5px; + } } } diff --git a/styles/daggerheart.css b/styles/daggerheart.css index a1b1f200..745fee57 100644 --- a/styles/daggerheart.css +++ b/styles/daggerheart.css @@ -1760,55 +1760,59 @@ .daggerheart.chat.downtime .downtime-refresh-container .refresh-title { font-weight: bold; } -.daggerheart.chat.roll .dice-tooltip .dice-rolls { +.daggerheart.chat.roll .dice-tooltip .dice-rolls.duality { display: flex; align-items: center; justify-content: space-around; } -.daggerheart.chat.roll .dice-tooltip .dice-rolls .dice-hope-container { +.daggerheart.chat.roll .dice-tooltip .dice-rolls.duality .dice-hope-container { display: flex; } -.daggerheart.chat.roll .dice-tooltip .dice-rolls .dice-hope-container .roll.die:not(:last-of-type) { +.daggerheart.chat.roll .dice-tooltip .dice-rolls.duality .dice-hope-container .roll.die:not(:last-of-type) { margin-right: 8px; } -.daggerheart.chat.roll .dice-tooltip .dice-rolls .modifiers-container { +.daggerheart.chat.roll .dice-tooltip .dice-rolls.duality .modifiers-container { display: flex; } -.daggerheart.chat.roll .dice-tooltip .dice-rolls .modifiers-container .modifier-value:not(:last-of-type) { +.daggerheart.chat.roll .dice-tooltip .dice-rolls.duality .modifiers-container .modifier-value:not(:last-of-type) { margin-right: 8px; } -.daggerheart.chat.roll .dice-tooltip .dice-rolls .roll.die.hope { +.daggerheart.chat.roll .dice-tooltip .dice-rolls.duality .roll.die.hope { color: white; -webkit-text-stroke-color: #008080; -webkit-text-stroke-width: 1.5px; font-weight: 400; } -.daggerheart.chat.roll .dice-tooltip .dice-rolls .roll.die.fear { +.daggerheart.chat.roll .dice-tooltip .dice-rolls.duality .roll.die.fear { color: white; -webkit-text-stroke-color: #430070; -webkit-text-stroke-width: 1.5px; font-weight: 400; } -.daggerheart.chat.roll .dice-tooltip .dice-rolls .roll.die.disadvantage { +.daggerheart.chat.roll .dice-tooltip .dice-rolls.duality .roll.die.disadvantage { color: white; -webkit-text-stroke-color: #b30000; -webkit-text-stroke-width: 1.5px; font-weight: 400; } -.daggerheart.chat.roll .dice-tooltip .dice-rolls .roll.die.advantage { +.daggerheart.chat.roll .dice-tooltip .dice-rolls.duality .roll.die.advantage { color: white; -webkit-text-stroke-color: green; -webkit-text-stroke-width: 1.5px; font-weight: 400; } -.daggerheart.chat.roll .dice-tooltip .dice-rolls .roll.die.unused { +.daggerheart.chat.roll .dice-tooltip .dice-rolls.duality .roll.die.unused { opacity: 0.3; } -.daggerheart.chat.roll .dice-tooltip .dice-rolls .modifier-value { +.daggerheart.chat.roll .dice-tooltip .dice-rolls.duality .modifier-value { text-align: center; font-weight: bold; font-size: 16px; } +.daggerheart.chat.roll .dice-tooltip .attack-roll-advantage-container { + text-align: end; + font-weight: bold; +} .daggerheart.chat.roll .dice-total .dice-total-value .hope { color: #008080; } @@ -1859,7 +1863,15 @@ justify-content: center; margin-right: 32px; } -.daggerheart.chat.roll .roll-damage-button { +.daggerheart.chat.roll .dice-actions { + display: flex; + gap: 4px; +} +.daggerheart.chat.roll .dice-actions button { + flex: 1; +} +.daggerheart.chat.roll .dice-result .roll-damage-button, +.daggerheart.chat.roll .dice-result .damage-button { margin-top: 5px; } .daggerheart.chat.domain-card { @@ -2227,6 +2239,7 @@ div.daggerheart.views.multiclass { display: flex; align-items: center; margin-bottom: 8px; + gap: 16px; } .daggerheart.views.npc-roll-selection .npc-roll-dialog-container .selection-container .dice-container { display: flex; @@ -2241,6 +2254,7 @@ div.daggerheart.views.multiclass { position: relative; display: flex; align-items: center; + justify-content: center; } .daggerheart.views.npc-roll-selection .npc-roll-dialog-container @@ -2267,52 +2281,57 @@ div.daggerheart.views.multiclass { .dice-inner-container .dice-number { position: absolute; - top: calc(50% - 14px); - left: calc(50% - 7px); font-size: 24px; font-weight: bold; } +.daggerheart.views.npc-roll-selection + .npc-roll-dialog-container + .selection-container + .dice-container + .advantage-container { + display: flex; + flex-direction: column; + gap: 2px; + flex: 1; +} +.daggerheart.views.npc-roll-selection + .npc-roll-dialog-container + .selection-container + .dice-container + .advantage-container + .advantage-button.active, +.daggerheart.views.npc-roll-selection + .npc-roll-dialog-container + .selection-container + .dice-container + .advantage-container + .advantage-button:hover { + background: var(--button-hover-background-color); +} .daggerheart.views.npc-roll-selection .npc-roll-dialog-container .roll-dialog-experience-container { display: flex; align-items: flex-start; flex-wrap: wrap; gap: 4px; - flex: 2; + flex: 1; height: 100%; } -.daggerheart.views.npc-roll-selection .npc-roll-dialog-container .roll-dialog-experience-container .roll-dialog-chip { - border: 1px solid black; - border-radius: 6px; - flex-basis: calc(50% - 2px); - display: flex; - align-items: center; - justify-content: space-between; - cursor: pointer; - padding: 4px; - background: grey; - overflow: hidden; -} -.daggerheart.views.npc-roll-selection - .npc-roll-dialog-container - .roll-dialog-experience-container - .roll-dialog-chip.hover { - filter: drop-shadow(0 0 3px red); -} -.daggerheart.views.npc-roll-selection - .npc-roll-dialog-container - .roll-dialog-experience-container - .roll-dialog-chip - span { - overflow: hidden; +.daggerheart.views.npc-roll-selection .npc-roll-dialog-container .roll-dialog-experience-container .experience-chip { + opacity: 0.6; + border-radius: 16px; + width: calc(50% - 4px); white-space: nowrap; - text-overflow: ellipsis; } .daggerheart.views.npc-roll-selection .npc-roll-dialog-container .roll-dialog-experience-container - .roll-dialog-chip.selected - i { - color: green; + .experience-chip.active, +.daggerheart.views.npc-roll-selection + .npc-roll-dialog-container + .roll-dialog-experience-container + .experience-chip:hover { + opacity: 1; + background: var(--button-hover-background-color); } .daggerheart.views.multiclass .multiclass-container { margin-bottom: 16px; diff --git a/template.json b/template.json index e66dba4a..7e6c3c67 100644 --- a/template.json +++ b/template.json @@ -37,9 +37,10 @@ "combat": {} }, "ChatMessage": { - "types": ["dualityRoll", "adversaryRoll", "abilityUse"], + "types": ["dualityRoll", "adversaryRoll", "damageRoll", "abilityUse"], "dualityRoll": {}, "adversaryRoll": {}, + "damageRoll": {}, "abilityUse": {} } } diff --git a/templates/chat/adversary-attack-roll.hbs b/templates/chat/adversary-attack-roll.hbs index 8ed8f094..2951c803 100644 --- a/templates/chat/adversary-attack-roll.hbs +++ b/templates/chat/adversary-attack-roll.hbs @@ -1,20 +1,27 @@ -