diff --git a/daggerheart.mjs b/daggerheart.mjs index 180204ea..4287074c 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -213,7 +213,16 @@ Hooks.on('chatMessage', (_, message) => { const attributeRoll = `${attribute?.data?.value ? `${attribute.data.value > 0 ? `+${attribute.data.value}` : `${attribute.data.value}`}` : ''}`; const roll = new Roll(`${hopeAndFearRoll}${advantageRoll}${attributeRoll}`); await roll.evaluate(); - resolve({ roll, attribute, title }); + resolve({ + roll, + attribute: attribute + ? { + value: attribute.data.value, + label: `${game.i18n.localize(abilities[attributeValue].label)} ${attribute.data.value >= 0 ? `+` : `-`}${attribute.data.value}` + } + : undefined, + title + }); }).then(({ roll, attribute, title }) => { const cls = getDocumentClass('ChatMessage'); const msgData = { @@ -223,7 +232,7 @@ Hooks.on('chatMessage', (_, message) => { title: title, origin: target?.id, roll: roll._formula, - modifiers: attribute ? [{ value: attribute.data.value }] : [], + modifiers: attribute ? [attribute] : [], hope: { dice: rollCommand.hope ?? 'd12', value: roll.dice[0].total }, fear: { dice: rollCommand.fear ?? 'd12', value: roll.dice[1].total }, advantage: diff --git a/lang/en.json b/lang/en.json index f186df20..9f1fb9c4 100644 --- a/lang/en.json +++ b/lang/en.json @@ -77,6 +77,14 @@ "Name": "Enable Range Measurement", "Hint": "Enable measuring of ranges with the ruler according to set distances." } + }, + "DualityRollColor": { + "Name": "Duality Roll Colour Scheme", + "Hint": "The display type for Duality Rolls", + "Options": { + "Colorful": "Colorful", + "Normal": "Normal" + } } }, "Notification": { diff --git a/module/applications/chatMessage.mjs b/module/applications/chatMessage.mjs index 004e7dd5..c5964023 100644 --- a/module/applications/chatMessage.mjs +++ b/module/applications/chatMessage.mjs @@ -1,3 +1,6 @@ +import { DualityRollColor } from '../config/settingsConfig.mjs'; +import DhpDualityRoll from '../data/dualityRoll.mjs'; + export default class DhpChatMesssage extends ChatMessage { async renderHTML() { if ( @@ -9,6 +12,20 @@ export default class DhpChatMesssage extends ChatMessage { this.content = await foundry.applications.handlebars.renderTemplate(this.content, this.system); } - return super.renderHTML(); + /* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */ + const html = await super.renderHTML(); + if ( + this.type === 'dualityRoll' && + game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.DualityRollColor) === + DualityRollColor.colorful.value + ) { + html.classList.add('duality'); + const dualityResult = this.system.dualityResult; + if (dualityResult === DhpDualityRoll.dualityResult.hope) html.classList.add('hope'); + else if (dualityResult === DhpDualityRoll.dualityResult.fear) html.classList.add('fear'); + else html.classList.add('critical'); + } + + return html; } } diff --git a/module/applications/settings.mjs b/module/applications/settings.mjs index d66a2460..80036bb5 100644 --- a/module/applications/settings.mjs +++ b/module/applications/settings.mjs @@ -1,3 +1,5 @@ +import { DualityRollColor } from '../config/settingsConfig.mjs'; + class DhpAutomationSettings extends FormApplication { constructor(object = {}, options = {}) { super(object, options); @@ -213,6 +215,16 @@ export const registerDHPSettings = () => { } }); + game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.DualityRollColor, { + name: game.i18n.localize('DAGGERHEART.Settings.DualityRollColor.Name'), + hint: game.i18n.localize('DAGGERHEART.Settings.DualityRollColor.Hint'), + scope: 'world', + config: true, + type: Number, + choices: Object.values(DualityRollColor), + default: DualityRollColor.colorful.value + }); + game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.menu.Automation.Name, { name: game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Name'), label: game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Label'), diff --git a/module/applications/sheets/pc.mjs b/module/applications/sheets/pc.mjs index 6d16b44c..4a204706 100644 --- a/module/applications/sheets/pc.mjs +++ b/module/applications/sheets/pc.mjs @@ -567,7 +567,7 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) { const { roll, hope, fear, advantage, disadvantage, modifiers, bonusDamageString } = await this.document.dualityRoll( - { title: 'Attribute Modifier', value: modifier }, + { title: game.i18n.localize(abilities[weapon.system.trait].label), value: modifier }, event.shiftKey, damage.bonusDamage ); diff --git a/module/config/settingsConfig.mjs b/module/config/settingsConfig.mjs index 5a4674da..d844c1ff 100644 --- a/module/config/settingsConfig.mjs +++ b/module/config/settingsConfig.mjs @@ -24,5 +24,17 @@ export const gameSettings = { General: { AbilityArray: 'AbilityArray', RangeMeasurement: 'RangeMeasurement' + }, + DualityRollColor: 'DualityRollColor' +}; + +export const DualityRollColor = { + colorful: { + value: 0, + label: 'DAGGERHEART.Settings.DualityRollColor.Options.Colorful' + }, + normal: { + value: 1, + label: 'DAGGERHEART.Settings.DualityRollColor.Options.Normal' } }; diff --git a/module/data/dualityRoll.mjs b/module/data/dualityRoll.mjs index 548f391c..50909a46 100644 --- a/module/data/dualityRoll.mjs +++ b/module/data/dualityRoll.mjs @@ -1,3 +1,5 @@ +import { DualityRollColor } from '../config/settingsConfig.mjs'; + const fields = foundry.data.fields; const diceField = () => new fields.SchemaField({ @@ -6,6 +8,12 @@ const diceField = () => }); export default class DhpDualityRoll extends foundry.abstract.TypeDataModel { + static dualityResult = { + hope: 1, + fear: 2, + critical: 3 + }; + static defineSchema() { return { title: new fields.StringField(), @@ -57,19 +65,34 @@ export default class DhpDualityRoll extends foundry.abstract.TypeDataModel { } get total() { - const modifiers = this.modifiers.reduce((acc, x) => acc + x.value, 0); const advantage = this.advantage.value ? this.advantage.value : this.disadvantage.value ? -this.disadvantage.value : 0; - return this.diceTotal + advantage + modifiers; + return this.diceTotal + advantage + this.modifierTotal.value; } get diceTotal() { return this.hope.value + this.fear.value; } + get modifierTotal() { + const total = this.modifiers.reduce((acc, x) => acc + x.value, 0); + return { + value: total, + label: total > 0 ? `+${total}` : total < 0 ? `-${total}` : '' + }; + } + + get dualityResult() { + return this.hope.value > this.fear.value + ? this.constructor.dualityResult.hope + : this.fear.value > this.hope.value + ? this.constructor.dualityResult.fear + : this.constructor.dualityResult.critical; + } + get totalLabel() { const label = this.hope.value > this.fear.value @@ -81,6 +104,13 @@ export default class DhpDualityRoll extends foundry.abstract.TypeDataModel { return game.i18n.localize(label); } + get colorful() { + return ( + game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.DualityRollColor) === + DualityRollColor.colorful.value + ); + } + prepareDerivedData() { const total = this.total; diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 2da03417..6db5f18e 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -119,7 +119,10 @@ export default class DhpActor extends Actor { const modifiers = [ { value: modifier.value ? Number.parseInt(modifier.value) : 0, - label: modifier.value >= 0 ? `+${modifier.value}` : `-${modifier.value}`, + label: + modifier.value >= 0 + ? `${modifier.title} +${modifier.value}` + : `${modifier.title} -${modifier.value}`, title: modifier.title } ]; diff --git a/module/ui/chatLog.mjs b/module/ui/chatLog.mjs index ec5e257f..183060b0 100644 --- a/module/ui/chatLog.mjs +++ b/module/ui/chatLog.mjs @@ -14,7 +14,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo } addChatListeners = async (app, html, data) => { - html.querySelectorAll('.roll-damage-button').forEach(element => + html.querySelectorAll('.duality-action').forEach(element => element.addEventListener('click', event => this.onRollDamage(event, data.message)) ); html.querySelectorAll('.target-container').forEach(element => { diff --git a/styles/chat.less b/styles/chat.less index 01b94049..f88c53d9 100644 --- a/styles/chat.less +++ b/styles/chat.less @@ -1,3 +1,287 @@ +.chat-message { + &.duality { + border-color: black; + padding: 8px 0 0 0; + + .message-header { + color: var(--color-light-3); + padding: 0 8px; + } + + .duality-data { + display: flex; + flex-direction: column; + + .duality-title { + color: var(--color-light-1); + text-shadow: 0 0 1px black; + border-bottom: 1px solid; + margin-bottom: 2px; + display: flex; + align-items: end; + justify-content: space-between; + padding: 0 8px; + + .duality-result-value { + border: 1px solid var(--color-dark-5); + padding: 2px; + font-weight: bold; + background: var(--color-dark-1); + margin-bottom: 4px; + font-size: 16px; + } + } + + .duality-modifiers { + display: flex; + gap: 2px; + margin-bottom: 4px; + padding: 0 8px; + + .duality-modifier { + padding: 2px; + border-radius: 6px; + border: 1px solid; + background: var(--color-dark-6); + font-size: 12px; + } + } + + .duality-line { + display: flex; + align-items: end; + justify-content: space-between; + padding: 0 8px; + + &.simple { + padding-right: 0; + } + + .dice-outer-container { + display: flex; + align-items: center; + gap: 4px; + margin-bottom: 4px; + + .dice-container { + display: flex; + flex-direction: column; + gap: 2px; + + .dice-title { + color: var(--color-light-1); + text-shadow: 0 0 1px black; + } + + .dice-inner-container { + display: flex; + align-items: center; + justify-content: center; + position: relative; + + .dice-wrapper { + height: 24px; + width: 24px; + position: relative; + display: flex; + align-items: center; + justify-content: center; + clip-path: polygon( + 50% 0%, + 80% 10%, + 100% 35%, + 100% 70%, + 80% 90%, + 50% 100%, + 20% 90%, + 0% 70%, + 0% 35%, + 20% 10% + ); + + .dice { + height: 26px; + width: 26px; + max-width: unset; + position: absolute; + } + } + + .dice-value { + position: absolute; + font-weight: bold; + font-size: 16px; + } + + &.hope { + .dice-wrapper { + background: black; + + .dice { + filter: brightness(0) saturate(100%) invert(79%) sepia(79%) saturate(333%) + hue-rotate(352deg) brightness(102%) contrast(103%); + } + } + + .dice-value { + color: var(--color-dark-1); + text-shadow: 0 0 4px white; + } + } + + &.fear { + .dice-wrapper { + background: white; + + .dice { + filter: brightness(0) saturate(100%) invert(12%) sepia(88%) saturate(4321%) + hue-rotate(221deg) brightness(92%) contrast(110%); + } + } + + .dice-value { + color: var(--color-light-1); + text-shadow: 0 0 4px black; + } + } + } + } + + .advantage-container { + padding-top: 21px; + + .dice-wrapper { + height: 24px; + width: 24px; + position: relative; + display: flex; + align-items: center; + justify-content: center; + + .dice { + height: 26px; + width: 26px; + max-width: unset; + position: absolute; + } + + .dice-value { + position: absolute; + font-weight: bold; + font-size: 16px; + } + } + + &.advantage { + .dice-wrapper { + .dice { + filter: brightness(0) saturate(100%) invert(18%) sepia(92%) saturate(4133%) + hue-rotate(96deg) brightness(104%) contrast(107%); + } + } + } + + &.disadvantage { + .dice-wrapper { + .dice { + filter: brightness(0) saturate(100%) invert(9%) sepia(78%) saturate(6903%) + hue-rotate(11deg) brightness(93%) contrast(117%); + } + } + } + } + + .duality-modifier { + padding-top: 21px; + color: var(--color-light-1); + text-shadow: 0 0 1px black; + font-size: 16px; + } + } + } + } + + .duality-result { + display: flex; + flex-direction: column; + align-items: end; + justify-content: center; + gap: 2px; + color: var(--color-light-1); + text-shadow: 0 0 1px black; + font-weight: bold; + background: var(--color-dark-1); + padding: 4px; + border-radius: 6px 0 0 0; + } + + .duality-actions { + display: flex; + justify-content: space-between; + + .duality-action { + color: var(--color-light-1); + text-shadow: 0 0 1px black; + font-weight: bold; + background: var(--color-dark-1); + padding: 4px; + border-radius: 0 6px 0 0; + border-color: black; + min-height: unset; + height: 26px; + } + } + + &.hope { + background: linear-gradient(0, @hopeBackgroundEnd 40px, @hopeBackgroundStart); + + // .dice-result { + // color: @hope; + // text-shadow: 0 0 3px black; + // } + + // .dice-total, .dice-formula { + // border-color: @hope; + // background: rgba(@hope, 0.5); + // } + } + &.fear { + background: linear-gradient(0, @fearBackgroundEnd, @fearBackgroundStart); + + // .dice-result { + // color: var(--color-dark-1); + // text-shadow: 0 0 1px var(--color-light-1); + // } + + // .dice-total, .dice-formula { + // border-color: @fear; + // background: @fearAccent; + // } + } + &.critical { + background: linear-gradient(0, @criticalBackgroundEnd, @criticalBackgroundStart); + + // .dice-result { + // color: var(--color-dark-1); + // text-shadow: 0 0 1px var(--color-light-1); + // } + + // .dice-total, .dice-formula { + // border-color: @critical; + // background: @criticalAccent; + // } + } + + .dice-roll { + color: var(--color-dark-1); + + .dice-flavor { + color: var(--color-light-1); + } + } + } +} + .daggerheart.chat { &.downtime { display: flex; diff --git a/styles/daggerheart.css b/styles/daggerheart.css index f9ab3ae3..42e01ed9 100644 --- a/styles/daggerheart.css +++ b/styles/daggerheart.css @@ -6,6 +6,7 @@ /* General */ /* Drop Shadows */ /* Background */ +/* Duality */ @import '../node_modules/@yaireo/tagify/dist/tagify.css'; .daggerheart.sheet.class .editor { height: 500px; @@ -1346,6 +1347,196 @@ cursor: pointer; filter: drop-shadow(0 0 3px red); } +.chat-message.duality { + border-color: black; + padding: 8px 0 0 0; +} +.chat-message.duality .message-header { + color: var(--color-light-3); + padding: 0 8px; +} +.chat-message.duality .duality-data { + display: flex; + flex-direction: column; +} +.chat-message.duality .duality-data .duality-title { + color: var(--color-light-1); + text-shadow: 0 0 1px black; + border-bottom: 1px solid; + margin-bottom: 2px; + display: flex; + align-items: end; + justify-content: space-between; + padding: 0 8px; +} +.chat-message.duality .duality-data .duality-title .duality-result-value { + border: 1px solid var(--color-dark-5); + padding: 2px; + font-weight: bold; + background: var(--color-dark-1); + margin-bottom: 4px; + font-size: 16px; +} +.chat-message.duality .duality-data .duality-modifiers { + display: flex; + gap: 2px; + margin-bottom: 4px; + padding: 0 8px; +} +.chat-message.duality .duality-data .duality-modifiers .duality-modifier { + padding: 2px; + border-radius: 6px; + border: 1px solid; + background: var(--color-dark-6); + font-size: 12px; +} +.chat-message.duality .duality-data .duality-line { + display: flex; + align-items: end; + justify-content: space-between; + padding: 0 8px; +} +.chat-message.duality .duality-data .duality-line.simple { + padding-right: 0; +} +.chat-message.duality .duality-data .duality-line .dice-outer-container { + display: flex; + align-items: center; + gap: 4px; + margin-bottom: 4px; +} +.chat-message.duality .duality-data .duality-line .dice-outer-container .dice-container { + display: flex; + flex-direction: column; + gap: 2px; +} +.chat-message.duality .duality-data .duality-line .dice-outer-container .dice-container .dice-title { + color: var(--color-light-1); + text-shadow: 0 0 1px black; +} +.chat-message.duality .duality-data .duality-line .dice-outer-container .dice-container .dice-inner-container { + display: flex; + align-items: center; + justify-content: center; + position: relative; +} +.chat-message.duality .duality-data .duality-line .dice-outer-container .dice-container .dice-inner-container .dice-wrapper { + height: 24px; + width: 24px; + position: relative; + display: flex; + align-items: center; + justify-content: center; + clip-path: polygon(50% 0%, 80% 10%, 100% 35%, 100% 70%, 80% 90%, 50% 100%, 20% 90%, 0% 70%, 0% 35%, 20% 10%); +} +.chat-message.duality .duality-data .duality-line .dice-outer-container .dice-container .dice-inner-container .dice-wrapper .dice { + height: 26px; + width: 26px; + max-width: unset; + position: absolute; +} +.chat-message.duality .duality-data .duality-line .dice-outer-container .dice-container .dice-inner-container .dice-value { + position: absolute; + font-weight: bold; + font-size: 16px; +} +.chat-message.duality .duality-data .duality-line .dice-outer-container .dice-container .dice-inner-container.hope .dice-wrapper { + background: black; +} +.chat-message.duality .duality-data .duality-line .dice-outer-container .dice-container .dice-inner-container.hope .dice-wrapper .dice { + filter: brightness(0) saturate(100%) invert(79%) sepia(79%) saturate(333%) hue-rotate(352deg) brightness(102%) contrast(103%); +} +.chat-message.duality .duality-data .duality-line .dice-outer-container .dice-container .dice-inner-container.hope .dice-value { + color: var(--color-dark-1); + text-shadow: 0 0 4px white; +} +.chat-message.duality .duality-data .duality-line .dice-outer-container .dice-container .dice-inner-container.fear .dice-wrapper { + background: white; +} +.chat-message.duality .duality-data .duality-line .dice-outer-container .dice-container .dice-inner-container.fear .dice-wrapper .dice { + filter: brightness(0) saturate(100%) invert(12%) sepia(88%) saturate(4321%) hue-rotate(221deg) brightness(92%) contrast(110%); +} +.chat-message.duality .duality-data .duality-line .dice-outer-container .dice-container .dice-inner-container.fear .dice-value { + color: var(--color-light-1); + text-shadow: 0 0 4px black; +} +.chat-message.duality .duality-data .duality-line .dice-outer-container .advantage-container { + padding-top: 21px; +} +.chat-message.duality .duality-data .duality-line .dice-outer-container .advantage-container .dice-wrapper { + height: 24px; + width: 24px; + position: relative; + display: flex; + align-items: center; + justify-content: center; +} +.chat-message.duality .duality-data .duality-line .dice-outer-container .advantage-container .dice-wrapper .dice { + height: 26px; + width: 26px; + max-width: unset; + position: absolute; +} +.chat-message.duality .duality-data .duality-line .dice-outer-container .advantage-container .dice-wrapper .dice-value { + position: absolute; + font-weight: bold; + font-size: 16px; +} +.chat-message.duality .duality-data .duality-line .dice-outer-container .advantage-container.advantage .dice-wrapper .dice { + filter: brightness(0) saturate(100%) invert(18%) sepia(92%) saturate(4133%) hue-rotate(96deg) brightness(104%) contrast(107%); +} +.chat-message.duality .duality-data .duality-line .dice-outer-container .advantage-container.disadvantage .dice-wrapper .dice { + filter: brightness(0) saturate(100%) invert(9%) sepia(78%) saturate(6903%) hue-rotate(11deg) brightness(93%) contrast(117%); +} +.chat-message.duality .duality-data .duality-line .dice-outer-container .duality-modifier { + padding-top: 21px; + color: var(--color-light-1); + text-shadow: 0 0 1px black; + font-size: 16px; +} +.chat-message.duality .duality-result { + display: flex; + flex-direction: column; + align-items: end; + justify-content: center; + gap: 2px; + color: var(--color-light-1); + text-shadow: 0 0 1px black; + font-weight: bold; + background: var(--color-dark-1); + padding: 4px; + border-radius: 6px 0 0 0; +} +.chat-message.duality .duality-actions { + display: flex; + justify-content: space-between; +} +.chat-message.duality .duality-actions .duality-action { + color: var(--color-light-1); + text-shadow: 0 0 1px black; + font-weight: bold; + background: var(--color-dark-1); + padding: 4px; + border-radius: 0 6px 0 0; + border-color: black; + min-height: unset; + height: 26px; +} +.chat-message.duality.hope { + background: linear-gradient(0, brown 40px, black); +} +.chat-message.duality.fear { + background: linear-gradient(0, blue, #0f0f61); +} +.chat-message.duality.critical { + background: linear-gradient(0, purple, #250825); +} +.chat-message.duality .dice-roll { + color: var(--color-dark-1); +} +.chat-message.duality .dice-roll .dice-flavor { + color: var(--color-light-1); +} .daggerheart.chat.downtime { display: flex; flex-direction: column; @@ -1391,7 +1582,7 @@ } .daggerheart.chat.roll .dice-tooltip .dice-rolls.duality .roll.die.hope { color: white; - -webkit-text-stroke-color: #ffd700; + -webkit-text-stroke-color: #ffe760; -webkit-text-stroke-width: 1.5px; font-weight: 400; } @@ -1415,7 +1606,7 @@ } .daggerheart.chat.roll .dice-tooltip .dice-rolls.duality .roll.die.advantage { color: white; - -webkit-text-stroke-color: green; + -webkit-text-stroke-color: #008000; -webkit-text-stroke-width: 1.5px; font-weight: 400; } @@ -1432,9 +1623,9 @@ font-weight: bold; } .daggerheart.chat.roll .dice-total.duality.hope { - border-color: #ffd700; + border-color: #ffe760; border-width: 3px; - background: rgba(255, 215, 0, 0.5); + background: rgba(255, 231, 96, 0.5); } .daggerheart.chat.roll .dice-total.duality.fear { border-color: #0032b1; @@ -1447,7 +1638,7 @@ background: rgba(67, 0, 112, 0.5); } .daggerheart.chat.roll .dice-total .dice-total-value .hope { - color: #ffd700; + color: #ffe760; } .daggerheart.chat.roll .dice-total .dice-total-value .fear { color: #0032b1; diff --git a/styles/variables/colors.less b/styles/variables/colors.less index d7e5964d..b69892e0 100644 --- a/styles/variables/colors.less +++ b/styles/variables/colors.less @@ -1,8 +1,5 @@ /* General */ -@hope: #ffd700; -@fear: #0032b1; -@critical: #430070; -@advantage: green; +@advantage: #008000; @disadvantage: #b30000; @miss: rgb(255, 0, 0); @hit: rgb(0, 128, 0); @@ -22,3 +19,16 @@ @secondaryAccent: #708090; @formBackground: #782e22; @hoverBackground: #2f4f4f40; + +/* Duality */ +@hope: #ffe760; +@hopeBackgroundStart: black; +@hopeBackgroundEnd: brown; +@fear: #0032b1; +@fearAccent: #2555cd; +@fearBackgroundStart: #0f0f61; +@fearBackgroundEnd: blue; +@critical: #430070; +@criticalAccent: #66159c; +@criticalBackgroundStart: #250825; +@criticalBackgroundEnd: purple; diff --git a/templates/chat/attack-roll.hbs b/templates/chat/attack-roll.hbs index bbab6ced..19653b7e 100644 --- a/templates/chat/attack-roll.hbs +++ b/templates/chat/attack-roll.hbs @@ -1,80 +1,160 @@