From c49079c57c13d45cd6296df6128ebeaa93673608 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Wed, 5 Nov 2025 23:51:14 +0100 Subject: [PATCH] Finished GroupRoll --- lang/en.json | 15 +- module/applications/dialogs/d20RollDialog.mjs | 2 +- .../dialogs/group-roll-dialog.mjs | 61 +++-- module/applications/ui/chatLog.mjs | 167 +++++++++++++- module/applications/ui/fearTracker.mjs | 2 +- module/data/chat-message/_modules.mjs | 2 + module/data/chat-message/groupRoll.mjs | 31 +++ module/dice/dhRoll.mjs | 10 +- module/helpers/handlebarsHelper.mjs | 7 +- styles/less/dialog/group-roll/group-roll.less | 3 + styles/less/ui/chat/group-roll.less | 210 ++++++++++++++++++ styles/less/ui/index.less | 1 + system.json | 1 + templates/dialogs/group-roll/group-roll.hbs | 42 ++-- templates/ui/chat/groupRoll.hbs | 120 ++++++++++ 15 files changed, 632 insertions(+), 42 deletions(-) create mode 100644 module/data/chat-message/groupRoll.mjs create mode 100644 styles/less/ui/chat/group-roll.less create mode 100644 templates/ui/chat/groupRoll.hbs diff --git a/lang/en.json b/lang/en.json index b3a7c425..7dacd0ea 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2488,6 +2488,17 @@ "title": "Effects Applied" }, "featureTitle": "Class Feature", + "groupRoll": { + "title": "Group Roll", + "leader": "Leader", + "partyTeam": "Party Team", + "team": "Team", + "selectLeader": "Select a Leader", + "selectMember": "Select a Member", + "rerollTitle": "Reroll Group Roll", + "rerollContent": "Are you sure you want to reroll your {trait} check?", + "rerollTooltip": "Reroll" + }, "healingRoll": { "title": "Heal - {damage}", "heal": "Heal", @@ -2629,7 +2640,9 @@ "noDiceSystem": "Your selected dice {system} does not have a {faces} dice", "gmMenuRefresh": "You refreshed all actions and resources {types}", "subclassAlreadyLinked": "{name} is already a subclass in the class {class}. Remove it from there if you want it to be a subclass to this class.", - "gmRequired": "This action requires an online GM" + "gmRequired": "This action requires an online GM", + "gmOnly": "This can only be accessed by the GM", + "noActorOwnership": "You do not have permissions for this character" }, "Sidebar": { "daggerheartMenu": { diff --git a/module/applications/dialogs/d20RollDialog.mjs b/module/applications/dialogs/d20RollDialog.mjs index 0efde2de..2534a2b8 100644 --- a/module/applications/dialogs/d20RollDialog.mjs +++ b/module/applications/dialogs/d20RollDialog.mjs @@ -123,7 +123,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio } const tagTeamSetting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll); - if (tagTeamSetting.members[this.actor.id]) { + if (tagTeamSetting.members[this.actor.id] && !this.config.skips?.createMessage) { context.activeTagTeamRoll = true; context.tagTeamSelected = this.config.tagTeamSelected; } diff --git a/module/applications/dialogs/group-roll-dialog.mjs b/module/applications/dialogs/group-roll-dialog.mjs index 33508ef6..2cb79563 100644 --- a/module/applications/dialogs/group-roll-dialog.mjs +++ b/module/applications/dialogs/group-roll-dialog.mjs @@ -19,6 +19,9 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat tag: 'form', classes: ['daggerheart', 'views', 'dh-style', 'dialog', 'group-roll'], position: { width: 'auto', height: 'auto' }, + window: { + title: 'DAGGERHEART.UI.Chat.groupRoll.title' + }, actions: { roll: GroupRollDialog.#roll, removeLeader: GroupRollDialog.#removeLeader, @@ -36,17 +39,20 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat _attachPartListeners(partId, htmlElement, options) { super._attachPartListeners(partId, htmlElement, options); - const changeChoices = this.actors; + const leaderChoices = this.actors.filter(x => this.actorsMembers.every(member => member.actor?.id !== x.id)); + const memberChoices = this.actors.filter( + x => this.actorLeader?.actor?.id !== x.id && this.actorsMembers.every(member => member.actor?.id !== x.id) + ); htmlElement.querySelectorAll('.leader-change-input').forEach(element => { autocomplete({ input: element, fetch: function (text, update) { if (!text) { - update(changeChoices); + update(leaderChoices); } else { text = text.toLowerCase(); - var suggestions = changeChoices.filter(n => n.name.toLowerCase().includes(text)); + var suggestions = leaderChoices.filter(n => n.name.toLowerCase().includes(text)); update(suggestions); } }, @@ -76,7 +82,7 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat }, onSelect: actor => { element.value = actor.uuid; - this.actorLeader = { actor: actor, trait: '', difficulty: 0 }; + this.actorLeader = { actor: actor, trait: 'agility', difficulty: 0 }; this.render(); }, click: e => e.fetch(), @@ -92,10 +98,10 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat input: element, fetch: function (text, update) { if (!text) { - update(changeChoices); + update(memberChoices); } else { text = text.toLowerCase(); - var suggestions = changeChoices.filter(n => n.name.toLowerCase().includes(text)); + var suggestions = memberChoices.filter(n => n.name.toLowerCase().includes(text)); update(suggestions); } }, @@ -125,7 +131,7 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat }, onSelect: actor => { element.value = actor.uuid; - this.actorsMembers.push({ actor: actor, trait: '', difficulty: 0 }); + this.actorsMembers.push({ actor: actor, trait: 'agility', difficulty: 0 }); this.render({ force: true }); }, click: e => e.fetch(), @@ -140,14 +146,22 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat async _prepareContext(_options) { const context = await super._prepareContext(_options); context.leader = this.actorLeader; - context.members = [...this.actorsMembers]; - context.traitList = Object.values(abilities).map(trait => ({ - label: game.i18n.localize(trait.label), - value: trait.id - })); + context.members = this.actorsMembers; + context.traitList = abilities; + + context.allSelected = this.actorsMembers.length + (this.actorLeader?.actor ? 1 : 0) === this.actors.length; + context.rollDisabled = context.members.length === 0 || !this.actorLeader?.actor; + return context; } + static updateData(event, _, formData) { + const { actorLeader, actorsMembers } = foundry.utils.expandObject(formData.object); + this.actorLeader = foundry.utils.mergeObject(this.actorLeader, actorLeader); + this.actorsMembers = foundry.utils.mergeObject(this.actorsMembers, actorsMembers); + this.render(true); + } + static async #removeLeader(_, button) { this.actorLeader = null; this.render(); @@ -158,8 +172,25 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat this.render(); } - static async #roll(_, button) { - console.log(this.leader, this.members); - console.log(this); + static async #roll() { + const cls = getDocumentClass('ChatMessage'); + const systemData = { + leader: this.actorLeader, + members: this.actorsMembers + }; + const msg = { + type: 'groupRoll', + user: game.user.id, + speaker: cls.getSpeaker(), + title: game.i18n.localize('DAGGERHEART.UI.Chat.groupRoll.title'), + system: systemData, + content: await foundry.applications.handlebars.renderTemplate( + 'systems/daggerheart/templates/ui/chat/groupRoll.hbs', + { system: systemData } + ) + }; + + cls.create(msg); + this.close(); } } diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index d8e4221d..0b9a9f23 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -1,4 +1,5 @@ -import { RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; +import { abilities } from '../../config/actorConfig.mjs'; +import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLog { constructor(options) { @@ -67,6 +68,18 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo html.querySelectorAll('.reroll-button').forEach(element => element.addEventListener('click', event => this.rerollEvent(event, data.message)) ); + html.querySelectorAll('.group-roll-button').forEach(element => + element.addEventListener('click', event => this.groupRollButton(event, data.message)) + ); + html.querySelectorAll('.group-roll-reroll').forEach(element => + element.addEventListener('click', event => this.groupRollReroll(event, data.message)) + ); + html.querySelectorAll('.group-roll-success').forEach(element => + element.addEventListener('click', event => this.groupRollSuccessEvent(event, data.message)) + ); + html.querySelectorAll('.group-roll-header-expand-section').forEach(element => + element.addEventListener('click', this.groupRollExpandSection) + ); }; setupHooks() { @@ -176,4 +189,156 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo }); } } + + async groupRollButton(event, message) { + const path = event.currentTarget.dataset.path; + const { actor: actorData, trait } = foundry.utils.getProperty(message.system, path); + const actor = game.actors.get(actorData._id); + + if (!actor.testUserPermission(game.user, 'OWNER')) { + return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.noActorOwnership')); + } + + const traitLabel = game.i18n.localize(abilities[trait].label); + const config = { + event: event, + title: `${game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll')}: ${actor.name}`, + headerTitle: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', { + ability: traitLabel + }), + roll: { + trait: trait, + advantage: 0, + modifiers: [{ label: traitLabel, value: actor.system.traits[trait].value }] + }, + hasRoll: true, + skips: { + createMessage: true, + resources: true + } + }; + const result = await actor.diceRoll({ + ...config, + headerTitle: `${game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll')}: ${actor.name}`, + title: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', { + ability: traitLabel + }) + }); + + const renderData = { system: foundry.utils.deepClone(message.system) }; + foundry.utils.setProperty(renderData.system, `${path}.result`, result.roll); + + const updatedContent = await foundry.applications.handlebars.renderTemplate( + 'systems/daggerheart/templates/ui/chat/groupRoll.hbs', + { ...renderData, user: game.user } + ); + const mess = game.messages.get(message._id); + + await emitAsGM( + GMUpdateEvent.UpdateDocument, + mess.update.bind(mess), + { + ...renderData, + content: updatedContent + }, + mess.uuid + ); + } + + async groupRollReroll(event, message) { + const path = event.currentTarget.dataset.path; + const { actor: actorData, trait } = foundry.utils.getProperty(message.system, path); + const actor = game.actors.get(actorData._id); + + if (!actor.testUserPermission(game.user, 'OWNER')) { + return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.noActorOwnership')); + } + + const traitLabel = game.i18n.localize(abilities[trait].label); + + const config = { + event: event, + title: `${game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll')}: ${actor.name}`, + headerTitle: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', { + ability: traitLabel + }), + roll: { + trait: trait, + advantage: 0, + modifiers: [{ label: traitLabel, value: actor.system.traits[trait].value }] + }, + hasRoll: true, + skips: { + createMessage: true + } + }; + const result = await actor.diceRoll({ + ...config, + headerTitle: `${game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll')}: ${actor.name}`, + title: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', { + ability: traitLabel + }) + }); + + const renderData = { system: foundry.utils.deepClone(message.system) }; + foundry.utils.setProperty(renderData.system, `${path}.result`, { ...result.roll, rerolled: true }); + + const updatedContent = await foundry.applications.handlebars.renderTemplate( + 'systems/daggerheart/templates/ui/chat/groupRoll.hbs', + { ...renderData, user: game.user } + ); + const mess = game.messages.get(message._id); + await emitAsGM( + GMUpdateEvent.UpdateDocument, + mess.update.bind(mess), + { + ...renderData, + content: updatedContent + }, + mess.uuid + ); + } + + async groupRollSuccessEvent(event, message) { + if (!game.user.isGM) { + return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.gmOnly')); + } + + const { path, success } = event.currentTarget.dataset; + const { actor: actorData } = foundry.utils.getProperty(message.system, path); + const actor = game.actors.get(actorData._id); + + if (!actor.testUserPermission(game.user, 'OWNER')) { + return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.noActorOwnership')); + } + + const renderData = { system: foundry.utils.deepClone(message.system) }; + foundry.utils.setProperty(renderData.system, `${path}.manualSuccess`, Boolean(success)); + + const updatedContent = await foundry.applications.handlebars.renderTemplate( + 'systems/daggerheart/templates/ui/chat/groupRoll.hbs', + { ...renderData, user: game.user } + ); + const mess = game.messages.get(message._id); + await emitAsGM( + GMUpdateEvent.UpdateDocument, + mess.update.bind(mess), + { + ...renderData, + content: updatedContent + }, + mess.uuid + ); + } + + async groupRollExpandSection(event) { + event.target + .closest('.group-roll-header-expand-section') + .querySelectorAll('i') + .forEach(element => { + element.classList.toggle('fa-angle-up'); + element.classList.toggle('fa-angle-down'); + }); + event.target.closest('.group-roll-section').querySelector('.group-roll-content').classList.toggle('closed'); + } } diff --git a/module/applications/ui/fearTracker.mjs b/module/applications/ui/fearTracker.mjs index 2b7c4dac..e9c816db 100644 --- a/module/applications/ui/fearTracker.mjs +++ b/module/applications/ui/fearTracker.mjs @@ -1,4 +1,4 @@ -import { emitAsGM, GMUpdateEvent, socketEvent } from '../../systemRegistration/socket.mjs'; +import { emitAsGM, GMUpdateEvent } from '../../systemRegistration/socket.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; diff --git a/module/data/chat-message/_modules.mjs b/module/data/chat-message/_modules.mjs index 67046248..ec095aac 100644 --- a/module/data/chat-message/_modules.mjs +++ b/module/data/chat-message/_modules.mjs @@ -1,5 +1,6 @@ import DHAbilityUse from './abilityUse.mjs'; import DHActorRoll from './actorRoll.mjs'; +import DHGroupRoll from './groupRoll.mjs'; import DHSystemMessage from './systemMessage.mjs'; export const config = { @@ -7,5 +8,6 @@ export const config = { adversaryRoll: DHActorRoll, damageRoll: DHActorRoll, dualityRoll: DHActorRoll, + groupRoll: DHGroupRoll, systemMessage: DHSystemMessage }; diff --git a/module/data/chat-message/groupRoll.mjs b/module/data/chat-message/groupRoll.mjs new file mode 100644 index 00000000..0ca5faff --- /dev/null +++ b/module/data/chat-message/groupRoll.mjs @@ -0,0 +1,31 @@ +import { abilities } from '../../config/actorConfig.mjs'; + +export default class DHGroupRoll extends foundry.abstract.TypeDataModel { + static defineSchema() { + const fields = foundry.data.fields; + + return { + leader: new fields.EmbeddedDataField(GroupRollMemberField), + members: new fields.ArrayField(new fields.EmbeddedDataField(GroupRollMemberField)) + }; + } +} + +class GroupRollMemberField extends foundry.abstract.DataModel { + static defineSchema() { + const fields = foundry.data.fields; + + return { + actor: new fields.ObjectField(), + trait: new fields.StringField({ choices: abilities }), + difficulty: new fields.StringField(), + result: new fields.ObjectField({ nullable: true, initial: null }), + manualSuccess: new fields.BooleanField({ nullable: true, initial: null }) + }; + } + + /* Can be expanded if we handle automation of success/failure */ + get success() { + return manualSuccess; + } +} diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index bbe3628b..da40696b 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -70,8 +70,11 @@ export default class DHRoll extends Roll { if (Hooks.call(`${CONFIG.DH.id}.postRoll${hook.capitalize()}`, config, message) === false) return null; } - // Create Chat Message - if (!config.source?.message) config.message = await this.toMessage(roll, config); + if (config.skips?.createMessage && game.modules.get('dice-so-nice')?.active) { + await game.dice3d.showForRoll(roll, game.user, true); + } else if (!config.source?.message) { + config.message = await this.toMessage(roll, config); + } } static postEvaluate(roll, config = {}) { @@ -237,7 +240,8 @@ export const registerRollDiceHooks = () => { !config.source?.actor || (game.user.isGM ? !hopeFearAutomation.gm : !hopeFearAutomation.players) || config.actionType === 'reaction' || - config.tagTeamSelected + config.tagTeamSelected || + config.skips?.resources ) return; const actor = await fromUuid(config.source.actor); diff --git a/module/helpers/handlebarsHelper.mjs b/module/helpers/handlebarsHelper.mjs index 847b04ce..2aa72dfc 100644 --- a/module/helpers/handlebarsHelper.mjs +++ b/module/helpers/handlebarsHelper.mjs @@ -15,7 +15,8 @@ export default class RegisterHandlebarsHelpers { setVar: this.setVar, empty: this.empty, pluralize: this.pluralize, - positive: this.positive + positive: this.positive, + isNullish: this.isNullish }); } static add(a, b) { @@ -94,4 +95,8 @@ export default class RegisterHandlebarsHelpers { static positive(a) { return Math.abs(Number(a)); } + + static isNullish(a) { + return a === null || a === undefined; + } } diff --git a/styles/less/dialog/group-roll/group-roll.less b/styles/less/dialog/group-roll/group-roll.less index f7a5675f..f2895d31 100644 --- a/styles/less/dialog/group-roll/group-roll.less +++ b/styles/less/dialog/group-roll/group-roll.less @@ -44,4 +44,7 @@ margin-left: auto; } } + .tooltip-container { + width: 100%; + } } diff --git a/styles/less/ui/chat/group-roll.less b/styles/less/ui/chat/group-roll.less new file mode 100644 index 00000000..132b6cc9 --- /dev/null +++ b/styles/less/ui/chat/group-roll.less @@ -0,0 +1,210 @@ +.daggerheart .chat-message .message-content .group-roll { + display: flex; + flex-direction: column; + gap: 8px; + padding-bottom: 8px; + + .group-roll-section { + display: flex; + flex-direction: column; + gap: 4px; + + .group-roll-header { + display: flex; + align-items: center; + font-size: 14px; + margin-bottom: 0; + font-weight: normal; + + &.first { + margin-top: 5px; + } + + .group-roll-header-expand-section { + display: flex; + align-items: center; + gap: 4px; + cursor: pointer; + + label { + cursor: pointer; + } + + i { + color: light-dark(@dark-blue, @golden); + } + } + } + + .group-roll-content { + display: flex; + flex-direction: column; + gap: 16px; + border-radius: 5px; + padding: 5px; + overflow: hidden; + height: auto; + transition: all 0.3s ease; + + &.closed { + height: 0; + padding-top: 0; + padding-bottom: 0; + } + + &.finished { + background: light-dark(@dark-blue-10, @golden-10); + } + + .group-roll-main-roll { + display: flex; + flex-direction: column; + + .divider { + font-size: 14px; + margin-bottom: 0; + font-weight: normal; + } + + .main-roll-content { + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + color: light-dark(@dark-blue, @golden); + + .main-value { + font-size: var(--font-size-24); + font-weight: bold; + } + + .main-text { + font-size: var(--font-size-16); + margin-top: 2px; + } + } + } + + .group-roll-member { + display: flex; + justify-content: space-between; + + .group-roll-data { + display: flex; + gap: 4px; + + img { + width: 42px; + height: 42px; + border-radius: 50%; + } + + .group-roll-label-container { + display: flex; + flex-direction: column; + justify-content: space-between; + + .group-roll-label-inner-container { + display: flex; + gap: 8px; + } + + .group-roll-modifier { + padding: 2px 8px; + border: 1px solid light-dark(@green, @green); + border-radius: 6px; + color: light-dark(@green, @green); + background: light-dark(@green-40, @green-40); + + &.failure { + border-color: light-dark(@red, @red); + color: light-dark(@red, @red); + background: light-dark(@red-40, @red-40); + } + } + + .group-roll-trait { + padding: 2px 8px; + border: 1px solid light-dark(white, white); + border-radius: 6px; + color: light-dark(white, white); + background: light-dark(@beige-80, @beige-80); + } + } + } + + .group-roll-rolling { + img { + width: 42px; + height: 42px; + + &:hover { + filter: drop-shadow(0 0 8px light-dark(@dark-blue, @golden)); + } + } + } + + .roll-results { + display: flex; + align-items: center; + border-radius: 5px; + width: fit-content; + gap: 16px; + cursor: pointer; + padding: 5px; + background: light-dark(@dark-blue-10, @golden-10); + color: light-dark(@dark-blue, @golden); + + &.finished { + background-color: initial; + } + + .reroll-result-container { + display: flex; + align-items: center; + gap: 16px; + + .label { + font-style: normal; + font-weight: 400; + font-size: var(--font-size-18); + line-height: 17px; + } + + i { + font-size: 16px; + } + + .success, + .success i { + color: @green; + } + + .failure, + .failure i { + color: @red; + } + } + + .group-roll-reroll { + position: relative; + display: flex; + align-items: center; + justify-content: center; + + .dice-icon { + width: 24px; + } + + .reroll-icon { + position: absolute; + font-size: 14px; + color: black; + filter: drop-shadow(0 0 3px black); + } + } + } + } + } + } +} diff --git a/styles/less/ui/index.less b/styles/less/ui/index.less index 296ef325..1713ae3e 100644 --- a/styles/less/ui/index.less +++ b/styles/less/ui/index.less @@ -4,6 +4,7 @@ @import './chat/damage-summary.less'; @import './chat/downtime.less'; @import './chat/effect-summary.less'; +@import './chat/group-roll.less'; @import './chat/refresh-message.less'; @import './chat/sheet.less'; diff --git a/system.json b/system.json index 8831b42a..a16b3562 100644 --- a/system.json +++ b/system.json @@ -271,6 +271,7 @@ "damageRoll": {}, "abilityUse": {}, "tagTeam": {}, + "groupRoll": {}, "systemMessage": {} } }, diff --git a/templates/dialogs/group-roll/group-roll.hbs b/templates/dialogs/group-roll/group-roll.hbs index fcf31ac2..9486aa7e 100644 --- a/templates/dialogs/group-roll/group-roll.hbs +++ b/templates/dialogs/group-roll/group-roll.hbs @@ -1,14 +1,14 @@
-

Group Roll

+

{{localize "DAGGERHEART.UI.Chat.groupRoll.title"}}

- Leader + {{localize "DAGGERHEART.UI.Chat.groupRoll.leader"}} {{#unless leader.actor}} - +
- Select a Leader + {{localize "DAGGERHEART.UI.Chat.groupRoll.selectLeader"}}
{{else}}
@@ -18,13 +18,13 @@
- + {{selectOptions traitList selected=leader.trait labelAttr="label" valueAttr="id" localize=true }}
- +
@@ -38,10 +38,12 @@
- Party Team - + {{localize "DAGGERHEART.UI.Chat.groupRoll.partyTeam"}} + + + {{#if (gt this.members.length 0)}} - {{#each members as |member|}} + {{#each members as |member index|}}
{{member.actor.name}}
@@ -49,30 +51,32 @@
- + {{selectOptions @root.traitList selected=member.trait labelAttr="label" valueAttr="id" localize=true }}
- +
{{/each}} {{/if}} -
- Select a Member -
+ {{#unless allSelected}} +
+ {{localize "DAGGERHEART.UI.Chat.groupRoll.selectMember"}} +
+ {{/unless}}
-
\ No newline at end of file diff --git a/templates/ui/chat/groupRoll.hbs b/templates/ui/chat/groupRoll.hbs new file mode 100644 index 00000000..54c62ca9 --- /dev/null +++ b/templates/ui/chat/groupRoll.hbs @@ -0,0 +1,120 @@ +
+
+

+ +

+
+
+
+ +
+
{{system.leader.actor.name}}
+
+ {{#unless (isNullish system.leader.manualSuccess)}} + + {{/unless}} +
{{localize (concat "DAGGERHEART.CONFIG.Traits." system.leader.trait ".name")}}
+
+
+
+ {{#unless system.leader.result}} +
+ +
+ {{else}} +
+ {{#if (isNullish system.leader.manualSuccess)}} +
+ {{system.leader.result.total}} + + + +
+ + + + + + {{else}} +
+ {{#if system.leader.manualSuccess}} + + {{else}} + + {{/if}} +
+ {{/if}} +
+ {{/unless}} +
+ {{#unless (isNullish system.leader.manualSuccess)}} +
+

+ {{localize "DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle" ability=(localize (concat "DAGGERHEART.CONFIG.Traits." system.leader.trait ".name"))}} +

+ + {{system.leader.result.total}} + {{localize "DAGGERHEART.GENERAL.withThing" thing=system.leader.result.result.label}} + +
+ {{/unless}} +
+
+
+

+ + + + + +

+
+ {{#each system.members as |member index|}} +
+
+ +
+
{{member.actor.name}}
+
+ {{#unless (isNullish member.manualSuccess)}} + + {{/unless}} +
{{localize (concat "DAGGERHEART.CONFIG.Traits." member.trait ".name")}}
+
+
+
+ {{#unless member.result}} +
+ +
+ {{else}} +
+ {{#if (isNullish member.manualSuccess)}} +
+ {{member.result.total}} + + + +
+ + + + + + {{else}} +
+ {{member.result.total}} + {{#if member.manualSuccess}} + + {{else}} + + {{/if}} +
+ {{/if}} +
+ {{/unless}} +
+ {{/each}} +
+
+
\ No newline at end of file