diff --git a/lang/en.json b/lang/en.json index 5360a646..ebbe1244 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2263,7 +2263,8 @@ "rangeAndTarget": "Range & Target", "dragApplyEffect": "Drag effect to apply it to an actor", "appliedEvenIfSuccessful": "Applied even if save succeeded", - "diceIsRerolled": "The dice has been rerolled (x{times})" + "diceIsRerolled": "The dice has been rerolled (x{times})", + "pendingSaves": "Pending Reaction Rolls" } } } diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 6bbef5b0..1149d72a 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -611,7 +611,8 @@ export default class CharacterSheet extends DHBaseActorSheet { }), roll: { trait: button.dataset.attribute - } + }, + hasRoll: true }; this.document.diceRoll(config); } diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index 89792c18..30a83f77 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -24,7 +24,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo html.querySelectorAll('.duality-action-damage').forEach(element => element.addEventListener('click', event => this.onRollDamage(event, data.message)) ); - html.querySelectorAll('.target-save-container').forEach(element => + html.querySelectorAll('.target-save').forEach(element => element.addEventListener('click', event => this.onRollSave(event, data.message)) ); html.querySelectorAll('.roll-all-save-button').forEach(element => @@ -124,7 +124,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo event.stopPropagation(); if (!game.user.isGM) return; const targets = event.target.parentElement.querySelectorAll( - '.target-section > [data-token] .target-save-container' + '[data-token] .target-save' ); const actor = await this.getActor(message.system.source.actor), action = this.getAction(actor, message.system.source.item, message.system.source.action); @@ -211,6 +211,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo async onDamage(event, message) { event.stopPropagation(); const { isHit, targets } = this.getTargetList(event, message); + console.log(message, isHit, targets) if (message.system.onSave && isHit) { const pendingingSaves = message.system.targets.filter( @@ -229,8 +230,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected')); for (let target of targets) { - let damages = foundry.utils.deepClone(message.system.damage?.roll ?? message.system.roll); + let damages = foundry.utils.deepClone(message.system.damage); if ( + !message.system.hasHealing && message.system.onSave && message.system.targets.find(t => t.id === target.id)?.saved?.success === true ) { diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 105343fb..a7523b29 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -1,6 +1,7 @@ import DhpActor from '../../documents/actor.mjs'; import D20RollDialog from '../../applications/dialogs/d20RollDialog.mjs'; import { ActionMixin } from '../fields/actionField.mjs'; +import { abilities } from '../../config/actorConfig.mjs'; const fields = foundry.data.fields; @@ -157,22 +158,26 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel prepareConfig(event) { return { event, - title: this.item.name, + title: `${this.item.name}: ${this.name}`, source: { item: this.item._id, action: this._id, actor: this.actor.uuid }, - dialog: {}, + dialog: { + configure: this.hasRoll + }, type: this.type, hasRoll: this.hasRoll, hasDamage: this.damage?.parts?.length && this.type !== 'healing', hasHealing: this.damage?.parts?.length && this.type === 'healing', hasEffect: !!this.effects?.length, hasSave: this.hasSave, + hasTarget: true, selectedRollMode: game.settings.get('core', 'rollMode'), isFastForward: event.shiftKey, - data: this.getRollData() + data: this.getRollData(), + evaluate: this.hasRoll }; } @@ -189,7 +194,9 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel formula: this.roll.getFormula(), advantage: CONFIG.DH.ACTIONS.advantageState[this.roll.advState].value }; - if (this.roll?.type === 'diceSet') roll.lite = true; + if (this.roll?.type === 'diceSet' + || !this.hasRoll + ) roll.lite = true; return roll; } @@ -293,19 +300,27 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel /* SAVE */ async rollSave(actor, event, message) { if (!actor) return; + const title = actor.isNPC + ? game.i18n.localize('DAGGERHEART.GENERAL.reactionRoll') + : game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', { + ability: game.i18n.localize(abilities[this.save.trait]?.label) + }); return actor.diceRoll({ event, - title: 'Roll Save', + title, roll: { trait: this.save.trait, difficulty: this.save.difficulty ?? this.actor?.baseSaveDifficulty, type: 'reaction' }, + type: 'trait', + hasRoll: true, data: actor.getRollData() }); } updateSaveMessage(result, message, targetId) { + if(!result) return; const updateMsg = this.updateChatMessage.bind(this, message, targetId, { result: result.roll.total, success: result.roll.success diff --git a/module/data/action/damageAction.mjs b/module/data/action/damageAction.mjs index c8da5737..f106b58e 100644 --- a/module/data/action/damageAction.mjs +++ b/module/data/action/damageAction.mjs @@ -34,6 +34,7 @@ export default class DHDamageAction extends DHBaseAction { } async rollDamage(event, data) { + // console.log(data) const systemData = data.system ?? data; let formulas = this.damage.parts.map(p => ({ @@ -46,6 +47,36 @@ export default class DHDamageAction extends DHBaseAction { formulas = this.formatFormulas(formulas, systemData); + delete systemData.evaluate; + systemData.targets.forEach(t => t.hit = true); + const config = { + ...systemData, + roll: formulas, + dialog: {}, + data: this.getRollData() + } + if (this.hasSave) config.onSave = this.save.damageMod; + if (data.system) { + config.source.message = data._id; + config.directDamage = false; + } else { + config.directDamage = true; + } + + return CONFIG.Dice.daggerheart.DamageRoll.build(config); + + /* const systemData = data.system ?? data; + + let formulas = this.damage.parts.map(p => ({ + formula: this.getFormulaValue(p, data).getFormula(this.actor), + damageTypes: p.applyTo === 'hitPoints' && !p.type.size ? new Set(['physical']) : p.type, + applyTo: p.applyTo + })); + + if (!formulas.length) return; + + formulas = this.formatFormulas(formulas, systemData); + const config = { title: game.i18n.format(`DAGGERHEART.UI.Chat.${ this.type === 'healing' ? 'healing' : 'damage'}Roll.title`, { damage: game.i18n.localize(this.name) }), roll: formulas, @@ -53,6 +84,9 @@ export default class DHDamageAction extends DHBaseAction { hasSave: this.hasSave, isCritical: systemData.roll?.isCritical ?? false, isHealing: this.type === 'healing', + hasDamage: this.type !== 'healing', + hasHealing: this.type === 'healing', + hasTarget: true, source: systemData.source, data: this.getRollData(), event @@ -65,6 +99,6 @@ export default class DHDamageAction extends DHBaseAction { config.directDamage = true; } - return CONFIG.Dice.daggerheart.DamageRoll.build(config); + return CONFIG.Dice.daggerheart.DamageRoll.build(config); */ } } diff --git a/module/data/chat-message/adversaryRoll.mjs b/module/data/chat-message/adversaryRoll.mjs index 10bf80a9..bb205521 100644 --- a/module/data/chat-message/adversaryRoll.mjs +++ b/module/data/chat-message/adversaryRoll.mjs @@ -26,6 +26,8 @@ export default class DHAdversaryRoll extends foundry.abstract.TypeDataModel { hasHealing: new fields.BooleanField({ initial: false }), hasEffect: new fields.BooleanField({ initial: false }), hasSave: new fields.BooleanField({ initial: false }), + hasTarget: new fields.BooleanField({ initial: false }), + onSave: new fields.StringField(), source: new fields.SchemaField({ actor: new fields.StringField(), item: new fields.StringField(), @@ -54,5 +56,8 @@ export default class DHAdversaryRoll extends foundry.abstract.TypeDataModel { return a; }, {hit: 0, miss: 0}) } + this.pendingSaves = this.targets.filter( + target => target.hit && target.saved.success === null + ).length > 0; } } diff --git a/module/data/chat-message/damageRoll.mjs b/module/data/chat-message/damageRoll.mjs index d485e23c..71de2280 100644 --- a/module/data/chat-message/damageRoll.mjs +++ b/module/data/chat-message/damageRoll.mjs @@ -19,7 +19,7 @@ export default class DHDamageRoll extends foundry.abstract.TypeDataModel { }) }) ), - targetSelection: new fields.BooleanField({ initial: true }), + targetSelection: new fields.BooleanField({ initial: false }), hasSave: new fields.BooleanField({ initial: false }), isHealing: new fields.BooleanField({ initial: false }), onSave: new fields.StringField(), @@ -29,7 +29,12 @@ export default class DHDamageRoll extends foundry.abstract.TypeDataModel { action: new fields.StringField(), message: new fields.StringField() }), - directDamage: new fields.BooleanField({ initial: true }) + directDamage: new fields.BooleanField({ initial: true }), + damage: new fields.ObjectField(), + hasRoll: new fields.BooleanField({ initial: false }), + hasDamage: new fields.BooleanField({ initial: false }), + hasHealing: new fields.BooleanField({ initial: false }), + hasEffect: new fields.BooleanField({ initial: false }) }; } @@ -46,5 +51,15 @@ export default class DHDamageRoll extends foundry.abstract.TypeDataModel { game.system.api.fields.ActionFields.TargetField.formatTarget(t) ) : this.targets; + if(this.targetSelection === true) { + this.targetShort = this.targets.reduce((a,c) => { + if(c.hit) a.hit += 1; + else c.miss += 1; + return a; + }, {hit: 0, miss: 0}) + } + this.pendingSaves = this.targets.filter( + target => target.hit && target.saved.success === null + ).length > 0; } } diff --git a/module/data/fields/action/costField.mjs b/module/data/fields/action/costField.mjs index ef51e086..abfc2e63 100644 --- a/module/data/fields/action/costField.mjs +++ b/module/data/fields/action/costField.mjs @@ -26,7 +26,7 @@ export default class CostField extends fields.ArrayField { } static calcCosts(costs) { - console.log(costs, CostField.getResources.call(this, costs)); + // console.log(costs, CostField.getResources.call(this, costs)); const resources = CostField.getResources.call(this, costs); return costs.map(c => { c.scale = c.scale ?? 1; diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs index f916dde3..01266984 100644 --- a/module/dice/damageRoll.mjs +++ b/module/dice/damageRoll.mjs @@ -6,19 +6,20 @@ export default class DamageRoll extends DHRoll { super(formula, data, options); } - static messageType = 'damageRoll'; + static messageType = 'dualityRoll'; static DefaultDialog = DamageDialog; static async buildEvaluate(roll, config = {}, message = {}) { - if (config.evaluate !== false) { - // if (config.dialog.configure === false) roll.constructFormula(config); + console.log(roll,config) + if (config.evaluate !== false) for (const roll of config.roll) await roll.roll.evaluate(); - } + roll._evaluated = true; const parts = config.roll.map(r => this.postEvaluate(r)); - config.roll = this.unifyDamageRoll(parts); + config.damage = this.unifyDamageRoll(parts); + config.targetSelection = config.targets?.length } static postEvaluate(roll, config = {}) { @@ -32,14 +33,6 @@ export default class DamageRoll extends DHRoll { }; } - static async buildPost(roll, config, message) { - await super.buildPost(roll, config, message); - if (config.source?.message) { - const chatMessage = ui.chat.collection.get(config.source.message); - chatMessage.update({ 'system.damage': config }); - } - } - static unifyDamageRoll(rolls) { const unified = {}; rolls.forEach(r => { diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index 9d9026c5..e3c42b30 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -52,8 +52,10 @@ export default class DHRoll extends Roll { } static async buildEvaluate(roll, config = {}, message = {}) { - if (config.evaluate !== false) await roll.evaluate(); - config.roll = this.postEvaluate(roll, config); + if (config.evaluate !== false) { + await roll.evaluate(); + config.roll = this.postEvaluate(roll, config); + } } static async buildPost(roll, config, message) { @@ -62,15 +64,24 @@ export default class DHRoll extends Roll { } // Create Chat Message - if (roll instanceof CONFIG.Dice.daggerheart.DamageRoll && Object.values(config.roll)?.length) { - const pool = foundry.dice.terms.PoolTerm.fromRolls( - Object.values(config.roll).flatMap(r => r.parts.map(p => p.roll)) - ); - roll = Roll.fromTerms([pool]); - } + // if (roll instanceof CONFIG.Dice.daggerheart.DamageRoll && Object.values(config.roll)?.length) { + // const pool = foundry.dice.terms.PoolTerm.fromRolls( + // Object.values(config.roll).flatMap(r => r.parts.map(p => p.roll)) + // ); + // roll = Roll.fromTerms([pool]); + // } if (config.source?.message) { if (game.modules.get('dice-so-nice')?.active) await game.dice3d.showForRoll(roll, game.user, true); - } else config.message = await this.toMessage(roll, config); + const chatMessage = ui.chat.collection.get(config.source.message); + chatMessage.update({ 'system.damage': config.damage }); + } else { + console.log(roll, config) + config.message = await this.toMessage(roll, config); + // if(roll._evaluated) { + // const cls = getDocumentClass('ChatMessage'); + // await cls.create(config.message, { rollMode: config.selectedRollMode }); + // } + } } static postEvaluate(roll, config = {}) { @@ -97,7 +108,11 @@ export default class DHRoll extends Roll { system: config, rolls: [roll] }; - return await cls.create(msg, { rollMode: config.selectedRollMode }); + // msg.applyRollMode(config.selectedRollMode); + // return msg; + if(roll._evaluated) return await cls.create(msg, { rollMode: config.selectedRollMode }); + return msg; + // return await cls.create(msg); } static applyKeybindings(config) { diff --git a/module/documents/chatMessage.mjs b/module/documents/chatMessage.mjs index a0c5d2e8..51b34ab0 100644 --- a/module/documents/chatMessage.mjs +++ b/module/documents/chatMessage.mjs @@ -17,7 +17,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { if (this.type === 'dualityRoll') { html.classList.add('duality'); - switch (this.system.roll.result.duality) { + switch (this.system.roll?.result?.duality) { case 1: html.classList.add('hope'); break; diff --git a/module/helpers/handlebarsHelper.mjs b/module/helpers/handlebarsHelper.mjs index 7c862f92..6d6c2bbc 100644 --- a/module/helpers/handlebarsHelper.mjs +++ b/module/helpers/handlebarsHelper.mjs @@ -11,7 +11,8 @@ export default class RegisterHandlebarsHelpers { damageSymbols: this.damageSymbols, rollParsed: this.rollParsed, hasProperty: foundry.utils.hasProperty, - setVar: this.setVar + setVar: this.setVar, + empty: this.empty }); } static add(a, b) { @@ -65,4 +66,9 @@ export default class RegisterHandlebarsHelpers { static setVar(name, value, context) { this[name] = value; } + + static empty(object) { + if(!(typeof object === 'object')) return true; + return Object.keys(object).length === 0; + } } diff --git a/styles/less/ui/chat/chat-temp.less b/styles/less/ui/chat/chat-temp.less index 9d8f2078..676780ed 100644 --- a/styles/less/ui/chat/chat-temp.less +++ b/styles/less/ui/chat/chat-temp.less @@ -34,11 +34,16 @@ font-size: var(--font-size-12); padding: 0 20px; + > .roll-part-header { + font-size: var(--font-size-14); + } + .roll-part-header { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; color: light-dark(@dark, @beige); + margin: 5px 0; span { display: flex; @@ -169,12 +174,16 @@ .target-choice { display: flex; - justify-content: space-evenly; font-size: var(--font-size-14); color: var(--text-color); padding: 5px 0; - .target-selected { + .button-target-selection { + flex: 1; + text-align: center; + } + + .button-target-selection:hover, .target-selected { font-weight: bold; text-shadow: 0px 0px 8px var(--text-color); } @@ -201,6 +210,26 @@ .target-data { flex: 1; } + + .target-save { + display: flex; + align-items: center; + justify-content: center; + width: 22px; + height: 22px; + &:hover > i { + scale: 1.2; + } + + i { + &.fa-check { + color: @green; + } + &.fa-xmark { + color: @medium-red; + } + } + } } } @@ -278,6 +307,23 @@ .roll-part-content { gap: 10px; } + + .roll-part-extra { + position: relative; + + .target-pending-saves { + display: flex; + align-items: center; + justify-content: center; + height: 25px; + width: 25px; + &.is-absolute { + position: absolute; + bottom: 0; + right: 0; + } + } + } } .roll-buttons { diff --git a/templates/dialogs/dice-roll/costSelection.hbs b/templates/dialogs/dice-roll/costSelection.hbs index 3ece4cbf..12200989 100644 --- a/templates/dialogs/dice-roll/costSelection.hbs +++ b/templates/dialogs/dice-roll/costSelection.hbs @@ -8,7 +8,7 @@ - {{log @root}} + {{/if}} diff --git a/templates/ui/chat/parts/button-part.hbs b/templates/ui/chat/parts/button-part.hbs index f85d32f2..71c5cc19 100644 --- a/templates/ui/chat/parts/button-part.hbs +++ b/templates/ui/chat/parts/button-part.hbs @@ -1,17 +1,17 @@
\ No newline at end of file diff --git a/templates/ui/chat/parts/damage-part.hbs b/templates/ui/chat/parts/damage-part.hbs index 9d230505..a8fd8b04 100644 --- a/templates/ui/chat/parts/damage-part.hbs +++ b/templates/ui/chat/parts/damage-part.hbs @@ -1,8 +1,8 @@ -