From 7ca420ae0e702b2386c22047c975354b9510c842 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Fri, 10 Apr 2026 15:33:44 -0400 Subject: [PATCH 1/6] [Feature] Redesign and merge party members and resources tabs (#1784) --- lang/en.json | 15 + module/applications/sheets/actors/party.mjs | 85 ++++- .../sheets/actors/party/party-members.less | 307 ++++++++++++++++-- .../less/sheets/actors/party/resources.less | 196 ----------- styles/less/sheets/index.less | 1 - templates/sheets/actors/party/header.hbs | 1 - .../sheets/actors/party/party-members.hbs | 170 ++++++++-- templates/sheets/actors/party/resources.hbs | 104 ------ 8 files changed, 522 insertions(+), 357 deletions(-) delete mode 100644 styles/less/sheets/actors/party/resources.less delete mode 100644 templates/sheets/actors/party/resources.hbs diff --git a/lang/en.json b/lang/en.json index 9bcf02b5..ec2130b8 100755 --- a/lang/en.json +++ b/lang/en.json @@ -316,6 +316,21 @@ } }, "newAdversary": "New Adversary" + }, + "Party": { + "Subtitle": { + "character": "{community} {ancestry} | {subclass} {class}", + "companion": "Companion of {partner}" + }, + "RemoveConfirmation": { + "title": "Remove member {name}", + "text": "Are you sure you want to remove {name} from the party?" + }, + "Thresholds": { + "minor": "MIN", + "major": "MAJ", + "severe": "SEV" + } } }, "APPLICATIONS": { diff --git a/module/applications/sheets/actors/party.mjs b/module/applications/sheets/actors/party.mjs index c5e77112..7c8c2338 100644 --- a/module/applications/sheets/actors/party.mjs +++ b/module/applications/sheets/actors/party.mjs @@ -1,5 +1,5 @@ import DHBaseActorSheet from '../api/base-actor.mjs'; -import { getDocFromElement } from '../../../helpers/utils.mjs'; +import { getDocFromElement, sortBy } from '../../../helpers/utils.mjs'; import { ItemBrowser } from '../../ui/itemBrowser.mjs'; import FilterMenu from '../../ux/filter-menu.mjs'; import DaggerheartMenu from '../../sidebar/tabs/daggerheartMenu.mjs'; @@ -18,13 +18,14 @@ export default class Party extends DHBaseActorSheet { static DEFAULT_OPTIONS = { classes: ['party'], position: { - width: 550, + width: 600, height: 900 }, window: { resizable: true }, actions: { + openDocument: Party.#openDocument, deletePartyMember: Party.#deletePartyMember, deleteItem: Party.#deleteItem, toggleHope: Party.#toggleHope, @@ -45,10 +46,6 @@ export default class Party extends DHBaseActorSheet { header: { template: 'systems/daggerheart/templates/sheets/actors/party/header.hbs' }, tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, partyMembers: { template: 'systems/daggerheart/templates/sheets/actors/party/party-members.hbs' }, - resources: { - template: 'systems/daggerheart/templates/sheets/actors/party/resources.hbs', - scrollable: [''] - }, /* NOT YET IMPLEMENTED */ // projects: { // template: 'systems/daggerheart/templates/sheets/actors/party/projects.hbs', @@ -66,7 +63,6 @@ export default class Party extends DHBaseActorSheet { primary: { tabs: [ { id: 'partyMembers' }, - { id: 'resources' }, /* NOT YET IMPLEMENTED */ // { id: 'projects' }, { id: 'inventory' }, @@ -96,6 +92,8 @@ export default class Party extends DHBaseActorSheet { case 'header': await this._prepareHeaderContext(context, options); break; + case 'partyMembers': + await this._prepareMembersContext(context, options); case 'notes': await this._prepareNotesContext(context, options); break; @@ -121,6 +119,59 @@ export default class Party extends DHBaseActorSheet { context.tagTeamActive = Boolean(this.document.system.tagTeam.initiator); } + async _prepareMembersContext(context, _options) { + context.partyMembers = []; + const traits = ['agility', 'strength', 'finesse', 'instinct', 'presence', 'knowledge']; + for (const actor of this.document.system.partyMembers) { + const weapons = []; + if (actor.type === 'character') { + if (actor.system.usedUnarmed) { + weapons.push(actor.system.usedUnarmed); + } + const equipped = actor.items.filter(i => i.system.equipped && i.type === 'weapon'); + weapons.push(...sortBy(equipped, i => (i.system.secondary ? 1 : 0))); + } + + context.partyMembers.push({ + uuid: actor.uuid, + img: actor.img, + name: actor.name, + subtitle: (() => { + if (!['character', 'companion'].includes(actor.type)) { + return game.i18n.format(`TYPES.Actor.${actor.type}`); + } + + const { value: classItem, subclass } = actor.system.class ?? {}; + const partner = actor.system.partner; + const ancestry = actor.system.ancestry; + const community = actor.system.community; + if (partner || (classItem && subclass && ancestry && community)) { + return game.i18n.format(`DAGGERHEART.ACTORS.Party.Subtitle.${actor.type}`, { + class: classItem?.name, + subclass: subclass?.name, + partner: partner?.name, + ancestry: ancestry?.name, + community: community?.name + }); + } + })(), + type: actor.type, + resources: actor.system.resources, + armorScore: actor.system.armorScore, + damageThresholds: actor.system.damageThresholds, + evasion: actor.system.evasion, + difficulty: actor.system.difficulty, + traits: actor.system.traits + ? traits.map(t => ({ + label: game.i18n.localize(`DAGGERHEART.CONFIG.Traits.${t}.short`), + value: actor.system.traits[t].value + })) + : null, + weapons + }); + } + } + /** * Prepare render context for the Biography part. * @param {ApplicationRenderContext} context @@ -149,6 +200,12 @@ export default class Party extends DHBaseActorSheet { } } + static async #openDocument(_, target) { + const uuid = target.dataset.uuid; + const document = await foundry.utils.fromUuid(uuid); + document?.sheet?.render(true); + } + /** * Toggles a hope resource value. * @type {ApplicationClickAction} @@ -190,7 +247,7 @@ export default class Party extends DHBaseActorSheet { * @type {ApplicationClickAction} */ static async #toggleArmorSlot(_, target) { - const actor = game.actors.get(target.dataset.actorId); + const actor = await foundry.utils.fromUuid(target.dataset.actorId); const { value, max } = actor.system.armorScore; const inputValue = Number.parseInt(target.dataset.value); const newValue = value >= inputValue ? inputValue - 1 : inputValue; @@ -425,25 +482,23 @@ export default class Party extends DHBaseActorSheet { } static async #deletePartyMember(event, target) { - const doc = await getDocFromElement(target.closest('.inventory-item')); - + const doc = await foundry.utils.fromUuid(target.closest('[data-uuid]')?.dataset.uuid); if (!event.shiftKey) { const confirmed = await foundry.applications.api.DialogV2.confirm({ window: { - title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', { - type: game.i18n.localize('TYPES.Actor.adversary'), + title: game.i18n.format('DAGGERHEART.ACTORS.Party.RemoveConfirmation.title', { name: doc.name }) }, - content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: doc.name }) + content: game.i18n.format('DAGGERHEART.ACTORS.Party.RemoveConfirmation.text', { name: doc.name }) }); if (!confirmed) return; } const currentMembers = this.document.system.partyMembers.map(x => x.uuid); - const newMemberdList = currentMembers.filter(uuid => uuid !== doc.uuid); - await this.document.update({ 'system.partyMembers': newMemberdList }); + const newMembersList = currentMembers.filter(uuid => uuid !== doc.uuid); + await this.document.update({ 'system.partyMembers': newMembersList }); } static async #deleteItem(event, target) { diff --git a/styles/less/sheets/actors/party/party-members.less b/styles/less/sheets/actors/party/party-members.less index a433ae34..155fcc36 100644 --- a/styles/less/sheets/actors/party/party-members.less +++ b/styles/less/sheets/actors/party/party-members.less @@ -1,28 +1,293 @@ @import '../../../utils/colors.less'; @import '../../../utils/fonts.less'; +@import '../../../utils/mixin.less'; -.application.sheet.daggerheart.actor.dh-style.party { - .tab.partyMembers { - max-height: 400px; - overflow: auto; +body.game:is(.performance-low, .noblur) { + .application.sheet.daggerheart.actor.dh-style.party .tab.resources .actors-list .actor-resources { + background: light-dark(@dark-blue, @dark-golden); - .actors-list { - display: flex; - flex-direction: column; - gap: 10px; - align-items: center; - width: 100%; - } - .actors-dragger { - display: flex; - align-items: center; - justify-content: center; - box-sizing: border-box; - width: 100%; - height: 40px; - border: 1px dashed light-dark(@dark-blue-50, @beige-50); - border-radius: 3px; - color: light-dark(@dark-blue-50, @beige-50); + .actor-name { + background: light-dark(@dark-blue, @dark-golden); } } } + +.application.sheet.daggerheart.actor.dh-style.party .tab.partyMembers { + overflow: auto; + + .actors-list { + display: flex; + flex-direction: column; + gap: 8px; + align-items: stretch; + width: 100%; + + .actor-resources { + display: grid; + grid-template: + "img header" min-content + "img body" 1fr + / 7.5rem 1fr; + gap: 6px; + column-gap: 12px; + padding: 6px; + background-color: light-dark(@dark-blue-10, @golden-10); + + .actor-img-frame { + grid-area: img; + width: 7.5rem; + height: 7.5rem; + position: relative; + + .actor-img { + object-fit: cover; + object-position: top center; + border-radius: 6px; + width: 100%; + height: 100%; + } + + .equipped-weapons { + position: absolute; + top: -2px; + left: -3px; + display: flex; + flex-direction: column; + gap: 1px; + img { + border-radius: 50%; + width: 24px; + height: 24px; + border: 1px solid light-dark(@dark-blue, @golden); + object-fit: cover; + } + } + + .evasion { + position: absolute; + top: 1px; + right: 1px; + width: 1.75rem; + height: 1.75rem; + background: url('../assets/svg/trait-shield.svg') no-repeat; + background-size: 100%; + font-size: var(--font-size-14); + font-weight: 700; + display: flex; + align-items: center; + justify-content: center; + } + + .threshold-section { + position: absolute; + left: 0; + right: 0; + bottom: -2px; + margin: auto; + + display: flex; + gap: 4px; + background-color: light-dark(transparent, @dark-blue); + color: light-dark(@dark-blue, @golden); + padding: 4px 6px; + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 3px; + align-items: baseline; + width: fit-content; + + h4 { + font-weight: bold; + text-transform: uppercase; + white-space: nowrap; + + &.threshold-label { + font-size: var(--font-size-10); + color: light-dark(@dark-blue, @golden); + } + + &.threshold-value { + font-size: var(--font-size-11); + color: light-dark(@dark, @beige); + } + } + } + } + + header { + grid-area: header; + display: grid; + grid-template: + "name hope" min-content + "subtitle subtitle" min-content + / 1fr min-content; + + .actor-name { + width: 100%; + z-index: 1; + font-size: var(--font-size-20); + color: light-dark(@beige, @golden); + font-weight: bold; + } + + .delete-icon { + font-size: 0.75em; + } + + .subtitle { + grid-area: subtitle; + font-size: var(--font-size-14); + } + + .hope-section { + display: flex; + background-color: light-dark(transparent, @dark-blue); + color: light-dark(@dark-blue, @golden); + padding: 3px 6px; + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 3px; + align-items: center; + width: fit-content; + margin-left: auto; + + h4 { + font-size: var(--font-size-12); + font-weight: bold; + text-transform: uppercase; + color: light-dark(@dark-blue, @golden); + margin-right: 3px; + } + + .hope-value { + display: flex; + cursor: pointer; + font-size: var(--font-size-12); + margin-left: 1px; + } + } + } + + .body { + grid-area: body; + display: flex; + align-items: start; + justify-content: space-between; + } + + .resources { + display: flex; + flex-direction: column; + gap: 4px; + + .slot-section { + display: flex; + flex-direction: row; + align-items: stretch; + + .slot-label { + display: flex; + align-items: center; + color: light-dark(@beige, @dark-blue); + background: light-dark(@dark-blue, @golden); + padding: 0 4px; + width: fit-content; + font-weight: bold; + border-radius: 6px 0px 0px 6px; + font-size: var(--font-size-12); + white-space: nowrap; + + .label { + padding-right: 2px; + } + + .value { + font-variant-numeric: tabular-nums; + .current { + display: inline-block; + text-align: end; + width: 2ch; + } + .max { + display: inline-block; + text-align: start; + width: 2ch; + } + } + } + + .slot-bar { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 4px; + + background-color: light-dark(@dark-blue-10, @dark-blue); + color: light-dark(@dark-blue, @golden); + padding: 2px 5px; + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 0 6px 6px 0; + width: fit-content; + min-height: 22px; + + .armor-slot { + cursor: pointer; + transition: all 0.3s ease; + font-size: var(--font-size-12); + + .fa-shield-halved { + color: light-dark(@dark-blue-40, @golden-40); + } + } + + .slot { + width: 16px; + height: 10px; + border: 1px solid light-dark(@dark-blue, @golden); + background: light-dark(@dark-blue-10, @golden-10); + border-radius: 3px; + transition: all 0.3s ease; + cursor: pointer; + + &.filled { + background: light-dark(@dark-blue, @golden); + } + } + } + } + } + + .traits { + background-color: light-dark(@dark-blue-10, @dark-blue); + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 6px; + display: grid; + grid-template-columns: 1fr 1fr; + font-size: var(--font-size-12); + padding: 3px 4px; + gap: 3px 7px; + .trait { + display: flex; + justify-content: space-between; + gap: 3px; + .label { + color: light-dark(@dark-blue, @golden); + } + .value { + font-weight: 600; + } + } + } + } + } + + .actors-dragger { + display: flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + width: 100%; + height: 40px; + border: 1px dashed light-dark(@dark-blue-50, @beige-50); + border-radius: 3px; + color: light-dark(@dark-blue-50, @beige-50); + } +} diff --git a/styles/less/sheets/actors/party/resources.less b/styles/less/sheets/actors/party/resources.less deleted file mode 100644 index 4db254bf..00000000 --- a/styles/less/sheets/actors/party/resources.less +++ /dev/null @@ -1,196 +0,0 @@ -@import '../../../utils/colors.less'; -@import '../../../utils/fonts.less'; -@import '../../../utils/mixin.less'; - -body.game:is(.performance-low, .noblur) { - .application.sheet.daggerheart.actor.dh-style.party .tab.resources .actors-list .actor-resources { - background: light-dark(@dark-blue, @dark-golden); - - .actor-name { - background: light-dark(@dark-blue, @dark-golden); - } - } -} - -.application.sheet.daggerheart.actor.dh-style.party { - .tab.resources { - overflow: auto; - - .actors-list { - display: flex; - flex-direction: row; - flex-wrap: wrap; - gap: 10px; - align-items: center; - width: 100%; - justify-content: center; - - .actor-resources { - display: flex; - flex-direction: column; - position: relative; - background: light-dark(@dark-blue-40, @dark-golden-40); - border-radius: 6px; - border: 1px solid light-dark(@dark-blue, @golden); - width: 230px; - height: -webkit-fill-available; - - .actor-name { - position: absolute; - top: 0px; - background: light-dark(@dark-blue-90, @dark-golden-80); - backdrop-filter: blur(6.5px); - border-radius: 6px 6px 0px 0px; - text-align: center; - width: 100%; - z-index: 1; - font-size: var(--font-size-20); - color: light-dark(@beige, @golden); - font-weight: bold; - padding: 5px 0; - } - - .actor-img { - height: 150px; - object-fit: cover; - object-position: top center; - border-radius: 6px 6px 0px 0px; - mask-image: linear-gradient(180deg, black 88%, transparent 100%); - } - - .resources { - display: flex; - flex-direction: column; - gap: 10px; - align-items: center; - margin: 10px; - - .slot-section { - display: flex; - flex-direction: column; - align-items: center; - - .slot-bar { - display: flex; - flex-wrap: wrap; - gap: 5px; - width: 239px; - - background-color: light-dark(@dark-blue-10, @dark-blue); - color: light-dark(@dark-blue, @golden); - padding: 5px; - border: 1px solid light-dark(@dark-blue, @golden); - border-radius: 6px; - width: fit-content; - - .armor-slot { - cursor: pointer; - transition: all 0.3s ease; - font-size: var(--font-size-12); - - .fa-shield-halved { - color: light-dark(@dark-blue-40, @golden-40); - } - } - - .slot { - width: 20px; - height: 10px; - border: 1px solid light-dark(@dark-blue, @golden); - background: light-dark(@dark-blue-10, @golden-10); - border-radius: 3px; - transition: all 0.3s ease; - cursor: pointer; - - &.filled { - background: light-dark(@dark-blue, @golden); - } - } - } - .slot-label { - display: flex; - align-items: center; - color: light-dark(@beige, @dark-blue); - background: light-dark(@dark-blue, @golden); - padding: 0 5px; - width: fit-content; - font-weight: bold; - border-radius: 0px 0px 5px 5px; - font-size: var(--font-size-12); - - .label { - padding-right: 5px; - } - - .value { - padding-left: 6px; - border-left: 1px solid light-dark(@beige, @dark-golden); - } - } - } - - .hope-section { - position: relative; - display: flex; - gap: 10px; - background-color: light-dark(transparent, @dark-blue); - color: light-dark(@dark-blue, @golden); - padding: 5px 10px; - border: 1px solid light-dark(@dark-blue, @golden); - border-radius: 3px; - align-items: center; - width: fit-content; - - h4 { - font-size: var(--font-size-12); - font-weight: bold; - text-transform: uppercase; - color: light-dark(@dark-blue, @golden); - } - - .hope-value { - display: flex; - cursor: pointer; - font-size: var(--font-size-12); - } - } - - .threshold-section { - display: flex; - align-self: center; - gap: 10px; - background-color: light-dark(transparent, @dark-blue); - color: light-dark(@dark-blue, @golden); - padding: 5px 10px; - border: 1px solid light-dark(@dark-blue, @golden); - border-radius: 3px; - align-items: center; - width: fit-content; - - h4 { - font-size: var(--font-size-12); - font-weight: bold; - text-transform: uppercase; - color: light-dark(@dark-blue, @golden); - - &.threshold-value { - color: light-dark(@dark, @beige); - } - } - } - } - } - } - .actors-dragger { - display: flex; - align-items: center; - justify-content: center; - box-sizing: border-box; - width: 100%; - height: 40px; - border: 1px dashed light-dark(@dark-blue-50, @beige-50); - border-radius: 3px; - color: light-dark(@dark-blue-50, @beige-50); - } - } -} diff --git a/styles/less/sheets/index.less b/styles/less/sheets/index.less index e5ffbf3e..7d595614 100644 --- a/styles/less/sheets/index.less +++ b/styles/less/sheets/index.less @@ -31,7 +31,6 @@ @import './actors/party/party-members.less'; @import './actors/party/sheet.less'; @import './actors/party/inventory.less'; -@import './actors/party/resources.less'; @import './items/beastform.less'; @import './items/class.less'; diff --git a/templates/sheets/actors/party/header.hbs b/templates/sheets/actors/party/header.hbs index f39f683f..3fdb137d 100644 --- a/templates/sheets/actors/party/header.hbs +++ b/templates/sheets/actors/party/header.hbs @@ -3,7 +3,6 @@

-

Party

\ No newline at end of file diff --git a/templates/sheets/actors/party/party-members.hbs b/templates/sheets/actors/party/party-members.hbs index b3dd53e6..8a113ac8 100644 --- a/templates/sheets/actors/party/party-members.hbs +++ b/templates/sheets/actors/party/party-members.hbs @@ -18,24 +18,156 @@ Help Action --}} + + -
- {{localize tabs.partyMembers.label}} - - {{#unless document.system.partyMembers.length}} -
- {{localize "DAGGERHEART.GENERAL.dropActorsHere"}} -
- {{/unless}} -
- \ No newline at end of file + + {{#unless document.system.partyMembers.length}} +
+ {{localize "DAGGERHEART.GENERAL.dropActorsHere"}} +
+ {{/unless}} + diff --git a/templates/sheets/actors/party/resources.hbs b/templates/sheets/actors/party/resources.hbs deleted file mode 100644 index bfbfb578..00000000 --- a/templates/sheets/actors/party/resources.hbs +++ /dev/null @@ -1,104 +0,0 @@ -
-
- - -
- -
- {{localize tabs.resources.label}} -
    - {{#each document.system.partyMembers as |actor id|}} -
  • -

    {{actor.name}}

    - -
    - {{#unless (eq actor.type 'companion') }} -
    -
    - {{#times actor.system.resources.hitPoints.max}} - - - {{/times}} -
    -
    - {{localize "DAGGERHEART.GENERAL.HitPoints.short"}} - {{actor.system.resources.hitPoints.value}} / {{actor.system.resources.hitPoints.max}} -
    -
    - {{/unless}} - -
    -
    - {{#times actor.system.resources.stress.max}} - - - {{/times}} -
    -
    - {{localize "DAGGERHEART.GENERAL.stress"}} - {{actor.system.resources.stress.value}} / {{actor.system.resources.stress.max}} -
    -
    - - {{#if actor.system.armorScore.max}} -
    -
    - {{#times actor.system.armorScore.max}} - - {{#if (gte actor.system.armorScore.value (add this 1))}} - - {{else}} - - {{/if}} - - {{/times}} -
    -
    - {{localize "DAGGERHEART.GENERAL.armorSlots"}} - {{actor.system.armorScore.value}} / {{actor.system.armorScore.max}} -
    -
    - {{/if}} - - {{#unless (or (eq actor.type 'companion') (eq actor.type 'adversary')) }} -
    -

    {{localize "DAGGERHEART.GENERAL.hope"}}

    - {{#times actor.system.resources.hope.max}} - - {{#if (gte actor.system.resources.hope.value (add this 1))}} - - {{else}} - - {{/if}} - - {{/times}} -
    - {{/unless}} - - {{#unless (eq actor.type 'companion')}} -
    -

    {{localize "DAGGERHEART.GENERAL.DamageThresholds.minor"}}

    -

    {{actor.system.damageThresholds.major}}

    -

    {{localize "DAGGERHEART.GENERAL.DamageThresholds.major"}}

    -

    {{actor.system.damageThresholds.severe}}

    -

    {{localize "DAGGERHEART.GENERAL.DamageThresholds.severe"}}

    -
    - {{/unless}} -
    -
  • - {{/each}} -
-
-
\ No newline at end of file From 154c1c939bca6144f17a9369474de0ade7fcbcbd Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sat, 11 Apr 2026 02:02:32 +0200 Subject: [PATCH 2/6] Improvements --- lang/en.json | 3 +- .../applications/dialogs/groupRollDialog.mjs | 133 +++++++++--------- module/applications/sheets/actors/party.mjs | 1 + .../group-roll-dialog/initialization.less | 19 +++ .../less/dialog/group-roll-dialog/sheet.less | 25 +++- .../tag-team-dialog/initialization.less | 11 ++ .../dialogs/groupRollDialog/groupRoll.hbs | 8 +- .../groupRollMainCharacter.hbs | 126 +++++++++-------- .../groupRollDialog/initialization.hbs | 2 +- .../sheets/actors/party/party-members.hbs | 7 +- 10 files changed, 197 insertions(+), 138 deletions(-) diff --git a/lang/en.json b/lang/en.json index ef1e4936..8424336a 100755 --- a/lang/en.json +++ b/lang/en.json @@ -747,7 +747,8 @@ }, "GroupRollSelect": { "title": "Group Roll", - "mainCharacter": "Main Character", + "leader": "Leader", + "leaderRoll": "Leader Roll", "openDialogForAll": "Open Dialog For All", "startGroupRoll": "Start Group Roll", "cancelGroupRoll": "Cancel", diff --git a/module/applications/dialogs/groupRollDialog.mjs b/module/applications/dialogs/groupRollDialog.mjs index b5169443..dc24654e 100644 --- a/module/applications/dialogs/groupRollDialog.mjs +++ b/module/applications/dialogs/groupRollDialog.mjs @@ -1,3 +1,4 @@ +import { ResourceUpdateMap } from '../../data/action/baseAction.mjs'; import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; import Party from '../sheets/actors/party.mjs'; @@ -109,15 +110,19 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat await super._onRender(context, options); if (this.element.querySelector('.team-container')) return; - const initializationPart = this.element.querySelector('.initialization-container'); - initializationPart.insertAdjacentHTML('afterend', '
'); - initializationPart.insertAdjacentHTML( - 'afterend', - `
${game.i18n.localize('Aiding Characters')}
` - ); - const teamContainer = this.element.querySelector('.team-container'); - for (const memberContainer of this.element.querySelectorAll('.team-member-container')) - teamContainer.appendChild(memberContainer); + + if (this.tabGroups.application !== this.constructor.PARTS.initialization.id) { + const initializationPart = this.element.querySelector('.initialization-container'); + initializationPart.insertAdjacentHTML('afterend', '
'); + initializationPart.insertAdjacentHTML( + 'afterend', + `
${game.i18n.localize('Aiding Characters')}
` + ); + + const teamContainer = this.element.querySelector('.team-container'); + for (const memberContainer of this.element.querySelectorAll('.team-member-container')) + teamContainer.appendChild(memberContainer); + } } async _prepareContext(_options) { @@ -161,6 +166,12 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat partContext.mainCharacter = this.getRollCharacterData(this.party.system.groupRoll.mainCharacter); break; case 'groupRoll': + const leader = this.party.system.groupRoll.mainCharacter; + partContext.hasRolled = + leader?.rollData || + Object.values(this.party.system.groupRoll?.aidingCharacters ?? {}).some( + x => x.successfull !== null + ); const { modifierTotal, modifiers } = Object.values(this.party.system.groupRoll.aidingCharacters).reduce( (acc, curr) => { const modifier = curr.successfull === true ? 1 : curr.successfull === false ? -1 : null; @@ -173,15 +184,14 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat }, { modifierTotal: 0, modifiers: [] } ); - const mainCharacterTotal = this.party.system.groupRoll.mainCharacter?.rollData - ? this.party.system.groupRoll.mainCharacter.roll.total - : null; + const mainCharacterTotal = leader?.rollData ? leader.roll.total : null; partContext.groupRoll = { - totalLabel: this.party.system.groupRoll.mainCharacter?.rollData + totalLabel: leader?.rollData ? game.i18n.format('DAGGERHEART.GENERAL.withThing', { - thing: this.party.system.groupRoll.mainCharacter.roll.totalLabel + thing: leader.roll.totalLabel }) : null, + totalDualityClass: leader?.roll?.isCritical ? 'critical' : leader?.roll?.withHope ? 'hope' : 'fear', total: mainCharacterTotal + modifierTotal, mainCharacterTotal, modifiers @@ -333,13 +343,10 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat } //#endregion - static async #makeRoll(_event, button) { - const { member } = button.dataset; - - const actor = game.actors.find(x => x.id === member); + async makeRoll(button, characterData, path) { + const actor = game.actors.find(x => x.id === characterData.id); if (!actor) return; - const characterData = this.party.system.groupRoll.aidingCharacters[member]; const result = await actor.rollTrait(characterData.rollChoice, { skips: { createMessage: true, @@ -354,36 +361,43 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat delete rollData.options.messageRoll; this.updatePartyData( { - [`system.groupRoll.aidingCharacters.${member}.rollData`]: rollData + [path]: rollData }, this.getUpdatingParts(button) ); } - static async #removeRoll(_, button) { + static async #makeRoll(_event, button) { + const { member } = button.dataset; + const character = this.party.system.groupRoll.aidingCharacters[member]; + this.makeRoll(button, character, `system.groupRoll.aidingCharacters.${member}.rollData`); + } + + static async #makeMainCharacterRoll(_event, button) { + const character = this.party.system.groupRoll.mainCharacter; + this.makeRoll(button, character, 'system.groupRoll.mainCharacter.rollData'); + } + + async removeRoll(button, path) { this.updatePartyData( { - [`system.groupRoll.aidingCharacters.${button.dataset.member}`]: { + [path]: { rollData: null, rollChoice: null, - selected: false + selected: false, + successfull: null } }, this.getUpdatingParts(button) ); } - static async #rerollDice(_, button) { - const { member } = button.dataset; - this.rerollDice( - button, - this.party.system.groupRoll.aidingCharacters[member], - `system.groupRoll.aidingCharacters.${member}.rollData` - ); + static async #removeRoll(_event, button) { + this.removeRoll(button, `system.groupRoll.aidingCharacters.${button.dataset.member}`); } - static async #rerollMainCharacterDice(_, button) { - this.rerollDice(button, this.party.system.groupRoll.mainCharacter, `system.groupRoll.mainCharacter.rollData`); + static async #removeMainCharacterRoll(_event, button) { + this.removeRoll(button, 'system.groupRoll.mainCharacter'); } async rerollDice(button, data, path) { @@ -407,42 +421,17 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat ); } - static async #makeMainCharacterRoll(_event, button) { - const actor = game.actors.find(x => x.id === this.party.system.groupRoll.mainCharacter.id); - if (!actor) return; - - const characterData = this.party.system.groupRoll.mainCharacter; - const result = await actor.rollTrait(characterData.rollChoice, { - skips: { - createMessage: true, - resources: true, - triggers: true - } - }); - - if (!game.modules.get('dice-so-nice')?.active) foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice }); - - const rollData = result.messageRoll.toJSON(); - delete rollData.options.messageRoll; - this.updatePartyData( - { - [`system.groupRoll.mainCharacter.rollData`]: rollData - }, - this.getUpdatingParts(button) + static async #rerollDice(_, button) { + const { member } = button.dataset; + this.rerollDice( + button, + this.party.system.groupRoll.aidingCharacters[member], + `system.groupRoll.aidingCharacters.${member}.rollData` ); } - static async #removeMainCharacterRoll(_event, button) { - this.updatePartyData( - { - [`system.groupRoll.mainCharacter`]: { - rollData: null, - rollChoice: null, - selected: false - } - }, - this.getUpdatingParts(button) - ); + static async #rerollMainCharacterDice(_, button) { + this.rerollDice(button, this.party.system.groupRoll.mainCharacter, `system.groupRoll.mainCharacter.rollData`); } static #markSuccessfull(_event, button) { @@ -517,6 +506,20 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat await cls.create(msgData); + const resourceMap = new ResourceUpdateMap(actor); + if (totalRoll.isCritical) { + resourceMap.addResources([ + { key: 'stress', value: -1, total: 1 }, + { key: 'hope', value: 1, total: 1 } + ]); + } else if (totalRoll.withHope) { + resourceMap.addResources([{ key: 'hope', value: 1, total: 1 }]); + } else { + resourceMap.addResources([{ key: 'fear', value: 1, total: 1 }]); + } + + resourceMap.updateResources(); + /* Fin */ this.cancelRoll({ confirm: false }); } diff --git a/module/applications/sheets/actors/party.mjs b/module/applications/sheets/actors/party.mjs index 86f57ae5..b7ea9b9f 100644 --- a/module/applications/sheets/actors/party.mjs +++ b/module/applications/sheets/actors/party.mjs @@ -116,6 +116,7 @@ export default class Party extends DHBaseActorSheet { relativeTo: this.document }); context.tagTeamActive = Boolean(this.document.system.tagTeam.initiator); + context.groupRollActive = Boolean(this.document.system.groupRoll.mainCharacter); } async _prepareMembersContext(context, _options) { diff --git a/styles/less/dialog/group-roll-dialog/initialization.less b/styles/less/dialog/group-roll-dialog/initialization.less index 211495ee..96990339 100644 --- a/styles/less/dialog/group-roll-dialog/initialization.less +++ b/styles/less/dialog/group-roll-dialog/initialization.less @@ -1,3 +1,11 @@ +.theme-light .daggerheart.dialog.dh-style.views.group-roll-dialog { + .initialization-container .members-container .member-container { + .member-name { + background-image: url('../assets/parchments/dh-parchment-light.png'); + } + } +} + .daggerheart.dialog.dh-style.views.group-roll-dialog { .initialization-container { h2 { @@ -20,6 +28,17 @@ .member-name { position: absolute; + padding: 0 2px; + border: 1px solid; + border-radius: 6px; + margin-top: 4px; + color: light-dark(@dark, @beige); + background-image: url('../assets/parchments/dh-parchment-dark.png'); + } + + img { + border-radius: 6px; + border: 1px solid light-dark(@dark-blue, @golden); } } } diff --git a/styles/less/dialog/group-roll-dialog/sheet.less b/styles/less/dialog/group-roll-dialog/sheet.less index 571d0d38..823f6cbf 100644 --- a/styles/less/dialog/group-roll-dialog/sheet.less +++ b/styles/less/dialog/group-roll-dialog/sheet.less @@ -12,6 +12,11 @@ gap: 8px; flex: 1; + &.inactive { + opacity: 0.3; + pointer-events: none; + } + .data-container { display: flex; flex-direction: column; @@ -59,9 +64,27 @@ align-items: center; gap: 8px; + &.hope, + &.fear, + &.critical { + color: var(--text-color); + } + + &.hope { + --text-color: @golden; + } + + &.fear { + --text-color: @chat-blue; + } + + &.critical { + --text-color: @chat-purple; + } + &::before, &::after { - color: light-dark(@dark-blue, @golden); + color: var(--text-color); content: ''; flex: 1; height: 2px; diff --git a/styles/less/dialog/tag-team-dialog/initialization.less b/styles/less/dialog/tag-team-dialog/initialization.less index 30676f82..0d16aa3b 100644 --- a/styles/less/dialog/tag-team-dialog/initialization.less +++ b/styles/less/dialog/tag-team-dialog/initialization.less @@ -20,6 +20,17 @@ .member-name { position: absolute; + padding: 0 2px; + border: 1px solid; + border-radius: 6px; + margin-top: 4px; + color: light-dark(@dark, @beige); + background-image: url('../assets/parchments/dh-parchment-dark.png'); + } + + img { + border-radius: 6px; + border: 1px solid light-dark(@dark-blue, @golden); } } } diff --git a/templates/dialogs/groupRollDialog/groupRoll.hbs b/templates/dialogs/groupRollDialog/groupRoll.hbs index 24f317a2..4b28bc30 100644 --- a/templates/dialogs/groupRollDialog/groupRoll.hbs +++ b/templates/dialogs/groupRollDialog/groupRoll.hbs @@ -3,13 +3,17 @@ {{localize "Result"}}
- {{groupRoll.total}} {{groupRoll.totalLabel}} + {{#if hasRolled}}{{groupRoll.total}} {{groupRoll.totalLabel}}{{/if}}
- {{#if groupRoll.mainCharacterTotal includeZero=true}}{{groupRoll.mainCharacterTotal}}{{else}}{{localize "
"}}{{/if}} + {{#if groupRoll.mainCharacterTotal includeZero=true}}{{groupRoll.mainCharacterTotal}}{{else}}{{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.leaderRoll"}}{{/if}} {{#each groupRoll.modifiers as |modifier|}} {{#if (gte modifier 0)}}+{{else}}-{{/if}} {{positive modifier}} {{/each}} + {{#unless groupRoll.modifiers.length}} + + + {{localize "DAGGERHEART.GENERAL.Modifier.plural"}} + {{/unless}}
diff --git a/templates/dialogs/groupRollDialog/groupRollMainCharacter.hbs b/templates/dialogs/groupRollDialog/groupRollMainCharacter.hbs index 0a090acf..bf804aa6 100644 --- a/templates/dialogs/groupRollDialog/groupRollMainCharacter.hbs +++ b/templates/dialogs/groupRollDialog/groupRollMainCharacter.hbs @@ -1,71 +1,73 @@ -{{#with mainCharacter}} -
-
{{localize "Main Character"}}
-
-
-
- -
- {{name}} -
-
-
- +
+ {{#with mainCharacter}} +
+
{{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.leader"}}
+
+
+
+ +
+ {{name}} +
+
+
+ +
-
- {{#if readyToRoll}} -
- - {{localize "DAGGERHEART.GENERAL.roll"}} -
- - - + {{#if readyToRoll}} +
+ + {{localize "DAGGERHEART.GENERAL.roll"}} +
+ + + - {{#if hasRolled}} - - - - {{/if}} -
-
- - {{#if roll}} -
-
{{roll.total}} {{localize "DAGGERHEART.GENERAL.withThing" thing=roll.totalLabel}}
-
- - {{roll.dHope.total}} - - - + - - {{roll.dFear.total}} - - - {{#if roll.advantage.type}} - {{#if (eq roll.advantage.type 1)}}+{{else}}-{{/if}} - - {{roll.advantage.value}} - - + {{#if hasRolled}} + + + {{/if}} - {{#if (gte roll.modifierTotal 0)}}+{{else}}-{{/if}} - {{positive roll.modifierTotal}}
-
- {{else}} - {{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.makeYourRoll"}} - {{/if}} -
- {{/if}} -
-
-{{/with}} \ No newline at end of file + + + {{#if roll}} +
+
{{roll.total}} {{localize "DAGGERHEART.GENERAL.withThing" thing=roll.totalLabel}}
+
+ + {{roll.dHope.total}} + + + + + + {{roll.dFear.total}} + + + {{#if roll.advantage.type}} + {{#if (eq roll.advantage.type 1)}}+{{else}}-{{/if}} + + {{roll.advantage.value}} + + + {{/if}} + {{#if (gte roll.modifierTotal 0)}}+{{else}}-{{/if}} + {{positive roll.modifierTotal}} +
+
+ {{else}} + {{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.makeYourRoll"}} + {{/if}} + + {{/if}} + + + {{/with}} + \ No newline at end of file diff --git a/templates/dialogs/groupRollDialog/initialization.hbs b/templates/dialogs/groupRollDialog/initialization.hbs index 06741363..4628c381 100644 --- a/templates/dialogs/groupRollDialog/initialization.hbs +++ b/templates/dialogs/groupRollDialog/initialization.hbs @@ -13,7 +13,7 @@
- +
- {{selectOptions selectedMainCharacterOptions selected=selectedMainCharacter.memberId blank="" }} +
diff --git a/templates/dialogs/groupRollDialog/groupRollMainCharacter.hbs b/templates/dialogs/groupRollDialog/leader.hbs similarity index 94% rename from templates/dialogs/groupRollDialog/groupRollMainCharacter.hbs rename to templates/dialogs/groupRollDialog/leader.hbs index bf804aa6..78fbcb42 100644 --- a/templates/dialogs/groupRollDialog/groupRollMainCharacter.hbs +++ b/templates/dialogs/groupRollDialog/leader.hbs @@ -1,5 +1,5 @@
- {{#with mainCharacter}} + {{#with leader}}
{{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.leader"}}
@@ -11,7 +11,7 @@
- {{selectOptions ../traitOptions selected=rollChoice localize=true}}
@@ -26,12 +26,12 @@ {{localize "DAGGERHEART.GENERAL.roll"}}
- + {{#if hasRolled}} - + {{/if}} @@ -42,12 +42,12 @@
{{roll.total}} {{localize "DAGGERHEART.GENERAL.withThing" thing=roll.totalLabel}}
- + {{roll.dHope.total}} + - + {{roll.dFear.total}} From 7e54ed1218cc502d307feb1c8475f98509b04581 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sat, 11 Apr 2026 02:28:49 +0200 Subject: [PATCH 5/6] Localization fixes --- lang/en.json | 13 ------------- module/applications/dialogs/groupRollDialog.mjs | 2 +- .../dialogs/groupRollDialog/groupRollMember.hbs | 2 +- 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/lang/en.json b/lang/en.json index 8424336a..65323790 100755 --- a/lang/en.json +++ b/lang/en.json @@ -550,18 +550,6 @@ }, "takeDowntime": "Take Downtime" }, - "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} roll?", - "rerollTooltip": "Reroll", - "wholePartySelected": "The whole party is selected" - }, "HUD": { "tokenHUD": { "genericEffects": "Foundry Effects", @@ -2413,7 +2401,6 @@ }, "maxWithThing": "Max {thing}", "missingDragDropThing": "Drop {thing} here", - "modifier": "Modifier", "multiclass": "Multiclass", "newCategory": "New Category", "newThing": "New {thing}", diff --git a/module/applications/dialogs/groupRollDialog.mjs b/module/applications/dialogs/groupRollDialog.mjs index d178a7e4..a488f315 100644 --- a/module/applications/dialogs/groupRollDialog.mjs +++ b/module/applications/dialogs/groupRollDialog.mjs @@ -30,7 +30,7 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat } get title() { - return game.i18n.localize('DAGGERHEART.APPLICATIONS.GroupRoll.title'); + return game.i18n.localize('DAGGERHEART.APPLICATIONS.GroupRollSelect.title'); } static DEFAULT_OPTIONS = { diff --git a/templates/dialogs/groupRollDialog/groupRollMember.hbs b/templates/dialogs/groupRollDialog/groupRollMember.hbs index af1e7909..acf8e8f1 100644 --- a/templates/dialogs/groupRollDialog/groupRollMember.hbs +++ b/templates/dialogs/groupRollDialog/groupRollMember.hbs @@ -76,7 +76,7 @@
{{/if}}
- {{localize "DAGGERHEART.GENERAL.modifier"}}{{#if successfull}} + 1{{else if (isNullish successfull)}} + ?{{else}} - 1{{/if}} + {{localize "DAGGERHEART.GENERAL.Modifier.single"}}{{#if successfull}} + 1{{else if (isNullish successfull)}} + ?{{else}} - 1{{/if}}
{{/if}} From dddc8413cbdd6e93c963613ef2cc331cd209f104 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sat, 11 Apr 2026 02:29:53 +0200 Subject: [PATCH 6/6] . --- templates/dialogs/groupRollDialog/groupRoll.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/dialogs/groupRollDialog/groupRoll.hbs b/templates/dialogs/groupRollDialog/groupRoll.hbs index f33c6ad2..edf1c980 100644 --- a/templates/dialogs/groupRollDialog/groupRoll.hbs +++ b/templates/dialogs/groupRollDialog/groupRoll.hbs @@ -1,6 +1,6 @@
- {{localize "Result"}} + {{localize "DAGGERHEART.GENERAL.result.single"}}
{{#if hasRolled}}{{groupRoll.total}} {{groupRoll.totalLabel}}{{/if}}