Compare commits

...

16 commits

Author SHA1 Message Date
WBHarry
b186f22cc7 Raised version 2026-04-26 11:29:55 +02:00
Carlos Fernandez
047e77154a
[Fix] companion hope levelup feature and default stress (#1839)
Some checks are pending
Project CI / build (24.x) (push) Waiting to run
* Fix issues with companions

* Remove unused companion hope resource
2026-04-26 11:20:06 +02:00
Carlos Fernandez
53f77972e3
Order character sidebar equip items (#1837) 2026-04-26 10:43:42 +02:00
Carlos Fernandez
cc822856e5
[Feature] Redesign group roll dialog (#1824)
* Redesign group roll dialog

* Style and restore aid modifiers

* Move successful buttons to inner result

* Localize and finish dialog

* Remove empty line

* Fix error and remove old styles

* Make icon pop a tiny bit better

* Fix advantage/disadvantage
2026-04-26 10:43:10 +02:00
Carlos Fernandez
6d09c5504d
[Fix] origin change values and prioritize item options (#1835)
Some checks are pending
Project CI / build (24.x) (push) Waiting to run
* Fix origin key replacement and prioritize item options

* Rename key to value

* Some fixes

* Attempt to always retrieve the source item for same actor origin

* Fix getters in item roll data

* Performance improvement

* Allow apply to item system data

* Replace implementation with shallow proxy

* Add delete to the shallow proxy

* Add proxy trap for the in operator

---------

Co-authored-by: WBHarry <williambjrklund@gmail.com>
2026-04-26 00:06:41 +02:00
WBHarry
c82bcbeb01 Linted 2026-04-25 22:50:59 +02:00
WBHarry
d0afee59d8 Corrected secondaryWeapon translation 2026-04-25 18:51:49 +02:00
WBHarry
4d17a7d9bf Fixed that new weapons couldn't be created on the Character Sheet 2026-04-25 17:45:50 +02:00
WBHarry
b8e00b2807 Translation fixes 2026-04-25 17:39:20 +02:00
WBHarry
e5ae56f45c
Fixed so that replaceFormulaValue doesn't loose getter values (#1836)
Some checks are pending
Project CI / build (24.x) (push) Waiting to run
2026-04-25 13:01:13 +02:00
Carlos Fernandez
ccb0073cef
Prevent class sheet and character errors when features/domains no longer exist (#1833)
Some checks are pending
Project CI / build (24.x) (push) Waiting to run
2026-04-24 20:49:42 +02:00
Carlos Fernandez
4a60c56462
Fix husky/lint-staged integration (#1831) 2026-04-24 20:48:42 +02:00
Carlos Fernandez
84afec31a7
[Fix] class domains when setting max domains to null or 0 (#1832)
* Fix class domains when setting max domains to null or 0

* Convert max loadout to use data prep
2026-04-24 20:47:33 +02:00
WBHarry
da11510e02 Reverted party auto permission
Some checks are pending
Project CI / build (24.x) (push) Waiting to run
2026-04-24 00:28:08 +02:00
WBHarry
f45b1210c7 Adding so that newly creatd parties default to having the ownership default be owner
Some checks are pending
Project CI / build (24.x) (push) Waiting to run
2026-04-23 23:01:12 +02:00
WBHarry
41829bc9d5 Fixed so that Homebrew Item Features without effects don't error out because effects are expected 2026-04-23 22:51:54 +02:00
50 changed files with 926 additions and 1165 deletions

View file

@ -3,7 +3,7 @@ var gulp = require('gulp');
var less = require('gulp-less'); var less = require('gulp-less');
gulp.task('less', function (cb) { gulp.task('less', function (cb) {
gulp.src('styles/daggerheart.less').pipe(less()).pipe(gulp.dest('styles')); gulp.src('styles/daggerheart.less').pipe(less()).on('error', console.error.bind(console)).pipe(gulp.dest('styles'));
cb(); cb();
}); });

View file

@ -758,15 +758,20 @@
} }
}, },
"GroupRollSelect": { "GroupRollSelect": {
"title": "Group Roll", "cancelConfirmText": "Are you sure you want to cancel the Group Roll? This will close it for all other players too.",
"aidingCharacters": "Aiding Characters", "cancelConfirmTitle": "Cancel Group Roll",
"initializationTitle": "Character Selection",
"finishGroupRoll": "Finish Group Roll",
"leader": "Leader", "leader": "Leader",
"leaderRoll": "Leader Roll", "leaderRoll": "Leader Roll",
"members": "Members",
"openDialogForAll": "Open Dialog For All", "openDialogForAll": "Open Dialog For All",
"removeRoll": "Remove Roll",
"resultsHint": "Results will appear when characters roll",
"selectLeaderHint": "Select one Character to be the leader",
"selectParticipantsHint": "Select one Character to be the leader",
"startGroupRoll": "Start Group Roll", "startGroupRoll": "Start Group Roll",
"finishGroupRoll": "Finish Group Roll", "title": "Group Roll"
"cancelConfirmTitle": "Cancel Group Roll",
"cancelConfirmText": "Are you sure you want to cancel the Group Roll? This will close it for all other players too."
}, },
"TokenConfig": { "TokenConfig": {
"actorSizeUsed": "Actor size is set, determining the dimensions" "actorSizeUsed": "Actor size is set, determining the dimensions"

View file

@ -37,17 +37,17 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
tag: 'form', tag: 'form',
id: 'GroupRollDialog', id: 'GroupRollDialog',
classes: ['daggerheart', 'views', 'dh-style', 'dialog', 'group-roll-dialog'], classes: ['daggerheart', 'views', 'dh-style', 'dialog', 'group-roll-dialog'],
position: { width: 550, height: 'auto' }, position: { width: 390, height: 'auto' },
window: {
icon: 'fa-solid fa-users'
},
actions: { actions: {
toggleSelectMember: this.#toggleSelectMember, toggleSelectMember: this.#toggleSelectMember,
startGroupRoll: this.#startGroupRoll, startGroupRoll: this.#startGroupRoll,
makeRoll: this.#makeRoll, makeRoll: this.#makeRoll,
removeRoll: this.#removeRoll, removeRoll: this.#removeRoll,
rerollDice: this.#rerollDice, rerollDice: this.#rerollDice,
makeLeaderRoll: this.#makeLeaderRoll, markSuccessful: this.#markSuccessful,
removeLeaderRoll: this.#removeLeaderRoll,
rerollLeaderDice: this.#rerollLeaderDice,
markSuccessfull: this.#markSuccessfull,
cancelRoll: this.#onCancelRoll, cancelRoll: this.#onCancelRoll,
finishRoll: this.#finishRoll finishRoll: this.#finishRoll
}, },
@ -59,17 +59,21 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
id: 'initialization', id: 'initialization',
template: 'systems/daggerheart/templates/dialogs/groupRollDialog/initialization.hbs' template: 'systems/daggerheart/templates/dialogs/groupRollDialog/initialization.hbs'
}, },
main: {
id: 'main',
template: 'systems/daggerheart/templates/dialogs/groupRollDialog/main.hbs'
},
leader: { leader: {
id: 'leader', id: 'leader',
template: 'systems/daggerheart/templates/dialogs/groupRollDialog/leader.hbs' template: 'systems/daggerheart/templates/dialogs/groupRollDialog/parts/member.hbs'
}, },
groupRoll: { result: {
id: 'groupRoll', id: 'result',
template: 'systems/daggerheart/templates/dialogs/groupRollDialog/groupRoll.hbs' template: 'systems/daggerheart/templates/dialogs/groupRollDialog/parts/result.hbs'
}, },
footer: { footer: {
id: 'footer', id: 'footer',
template: 'systems/daggerheart/templates/dialogs/groupRollDialog/footer.hbs' template: 'systems/daggerheart/templates/dialogs/groupRollDialog/parts/footer.hbs'
} }
}; };
@ -89,40 +93,14 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
} }
_configureRenderParts(options) { _configureRenderParts(options) {
const { initialization, leader, groupRoll, footer } = super._configureRenderParts(options); const parts = super._configureRenderParts(options);
const augmentedParts = { initialization };
for (const memberKey of Object.keys(this.party.system.groupRoll.aidingCharacters)) { for (const memberKey of Object.keys(this.party.system.groupRoll.aidingCharacters)) {
augmentedParts[memberKey] = { parts[memberKey] = {
id: memberKey, id: memberKey,
template: 'systems/daggerheart/templates/dialogs/groupRollDialog/groupRollMember.hbs' template: 'systems/daggerheart/templates/dialogs/groupRollDialog/parts/member.hbs'
}; };
} }
return parts;
augmentedParts.leader = leader;
augmentedParts.groupRoll = groupRoll;
augmentedParts.footer = footer;
return augmentedParts;
}
/**@inheritdoc */
async _onRender(context, options) {
await super._onRender(context, options);
if (this.element.querySelector('.team-container')) return;
if (this.tabGroups.application !== this.constructor.PARTS.initialization.id) {
const initializationPart = this.element.querySelector('.initialization-container');
initializationPart.insertAdjacentHTML('afterend', '<div class="team-container"></div>');
initializationPart.insertAdjacentHTML(
'afterend',
`<div class="section-title">${game.i18n.localize('DAGGERHEART.APPLICATIONS.GroupRollSelect.aidingCharacters')}</div>`
);
const teamContainer = this.element.querySelector('.team-container');
for (const memberContainer of this.element.querySelectorAll('.team-member-container'))
teamContainer.appendChild(memberContainer);
}
} }
async _prepareContext(_options) { async _prepareContext(_options) {
@ -134,6 +112,7 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
context.data = this.party.system.groupRoll; context.data = this.party.system.groupRoll;
context.traitOptions = CONFIG.DH.ACTOR.abilities; context.traitOptions = CONFIG.DH.ACTOR.abilities;
context.members = {}; context.members = {};
context.aidKeys = Object.keys(this.party.system.groupRoll.aidingCharacters);
context.allHaveRolled = Object.keys(context.data.participants).every(key => { context.allHaveRolled = Object.keys(context.data.participants).every(key => {
const data = context.data.participants[key]; const data = context.data.participants[key];
return Boolean(data.rollData); return Boolean(data.rollData);
@ -145,6 +124,7 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
async _preparePartContext(partId, context, options) { async _preparePartContext(partId, context, options) {
const partContext = await super._preparePartContext(partId, context, options); const partContext = await super._preparePartContext(partId, context, options);
partContext.partId = partId; partContext.partId = partId;
partContext.leader = this.getRollCharacterData(this.party.system.groupRoll.leader);
switch (partId) { switch (partId) {
case 'initialization': case 'initialization':
@ -162,19 +142,14 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
partContext.canStartGroupRoll = selectedMembers.length > 1 && this.leader?.memberId; partContext.canStartGroupRoll = selectedMembers.length > 1 && this.leader?.memberId;
partContext.openForAllPlayers = this.openForAllPlayers; partContext.openForAllPlayers = this.openForAllPlayers;
break; break;
case 'leader': case 'result':
partContext.leader = this.getRollCharacterData(this.party.system.groupRoll.leader);
break;
case 'groupRoll':
const leader = this.party.system.groupRoll.leader; const leader = this.party.system.groupRoll.leader;
partContext.hasRolled = partContext.hasRolled =
leader?.rollData || leader?.rollData ||
Object.values(this.party.system.groupRoll?.aidingCharacters ?? {}).some( Object.values(this.party.system.groupRoll?.aidingCharacters ?? {}).some(x => x.successful !== null);
x => x.successfull !== null
);
const { modifierTotal, modifiers } = Object.values(this.party.system.groupRoll.aidingCharacters).reduce( const { modifierTotal, modifiers } = Object.values(this.party.system.groupRoll.aidingCharacters).reduce(
(acc, curr) => { (acc, curr) => {
const modifier = curr.successfull === true ? 1 : curr.successfull === false ? -1 : null; const modifier = curr.successful === true ? 1 : curr.successful === false ? -1 : null;
if (modifier) { if (modifier) {
acc.modifierTotal += modifier; acc.modifierTotal += modifier;
acc.modifiers.push(modifier); acc.modifiers.push(modifier);
@ -200,7 +175,7 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
case 'footer': case 'footer':
partContext.canFinishRoll = partContext.canFinishRoll =
Boolean(this.party.system.groupRoll.leader?.rollData) && Boolean(this.party.system.groupRoll.leader?.rollData) &&
Object.values(this.party.system.groupRoll.aidingCharacters).every(x => x.successfull !== null); Object.values(this.party.system.groupRoll.aidingCharacters).every(x => x.successful !== null);
break; break;
} }
@ -216,20 +191,42 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
if (!data) return {}; if (!data) return {};
const actor = game.actors.get(data.id); const actor = game.actors.get(data.id);
const isLeader = data === this.party.system.groupRoll.leader;
const roll = data.roll;
const withTypeSuffix = !roll ? null : roll.isCritical ? 'criticalShort' : roll.withHope ? 'hope' : 'fear';
const thing = withTypeSuffix ? _loc(`DAGGERHEART.GENERAL.${withTypeSuffix}`) : null;
return { return {
...data, ...data,
type: isLeader ? 'leader' : 'aid',
basePath: isLeader ? 'system.groupRoll.leader' : `system.groupRoll.aidingCharacters.${data.id}`,
rollChoiceLabel: _loc(CONFIG.DH.ACTOR.abilities[data.rollChoice]?.label),
roll: data.roll, roll: data.roll,
isEditable: actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER), isEditable: actor?.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER),
key: partId, key: partId,
readyToRoll: Boolean(data.rollChoice), readyToRoll: Boolean(data.rollChoice),
hasRolled: Boolean(data.rollData) hasRolled: Boolean(data.rollData),
modifier: data.successful ? 1 : data.successful === false ? -1 : 0,
withLabelShort: thing ? _loc('DAGGERHEART.GENERAL.withThing', { thing }) : null
}; };
} }
#getCharacterDataById(id) {
if (!id) return null;
const groupRoll = this.party.system.groupRoll;
if (id === 'leader' || id === groupRoll.leader?.id) {
return { data: groupRoll.leader, basePath: 'system.groupRoll.leader' };
} else if (id in groupRoll.aidingCharacters) {
return { data: groupRoll.aidingCharacters[id], basePath: `system.groupRoll.aidingCharacters.${id}` };
}
return null;
}
static async updateData(event, _, formData) { static async updateData(event, _, formData) {
const partyData = foundry.utils.expandObject(formData.object); const partyData = foundry.utils.expandObject(formData.object);
this.updatePartyData(partyData, this.getUpdatingParts(event.target)); this.updatePartyData(partyData, this.getUpdatingParts(event.target));
} }
@ -256,16 +253,16 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
} }
getUpdatingParts(target) { getUpdatingParts(target) {
const { initialization, leader, groupRoll, footer } = this.constructor.PARTS; const { initialization, leader, result, footer } = this.constructor.PARTS;
const isInitialization = this.tabGroups.application === initialization.id; const isInitialization = this.tabGroups.application === initialization.id;
const updatingMember = target.closest('.team-member-container')?.dataset?.memberKey; const updatingMember = target.closest('.member-roll-container.aid')?.dataset?.memberKey;
const updatingLeader = target.closest('.main-character-outer-container'); const updatingLeader = target.closest('.member-roll-container.leader');
return [ return [
...(isInitialization ? [initialization.id] : []), ...(isInitialization ? [initialization.id] : []),
...(updatingMember ? [updatingMember] : []), ...(updatingMember ? [updatingMember] : []),
...(updatingLeader ? [leader.id] : []), ...(updatingLeader ? [leader.id] : []),
...(!isInitialization ? [groupRoll.id, footer.id] : []) ...(!isInitialization ? [result.id, footer.id] : [])
]; ];
} }
@ -304,6 +301,9 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
static #toggleSelectMember(_, button) { static #toggleSelectMember(_, button) {
const member = this.partyMembers.find(x => x.id === button.dataset.id); const member = this.partyMembers.find(x => x.id === button.dataset.id);
member.selected = !member.selected; member.selected = !member.selected;
if (this.leader?.memberId === member.id) {
this.leader = null;
}
this.render(); this.render();
} }
@ -343,11 +343,14 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
} }
//#endregion //#endregion
async makeRoll(button, characterData, path) { /** @this GroupRollDialog */
const actor = game.actors.find(x => x.id === characterData.id); static async #makeRoll(_event, button) {
const member = button.closest('[data-member-key]').dataset.memberKey;
const { data, basePath } = this.#getCharacterDataById(member);
const actor = game.actors.find(x => x.id === data.id);
if (!actor) return; if (!actor) return;
const result = await actor.rollTrait(characterData.rollChoice, { const result = await actor.rollTrait(data.rollChoice, {
skips: { skips: {
createMessage: true, createMessage: true,
resources: true, resources: true,
@ -356,53 +359,40 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
}); });
if (!result) return; if (!result) return;
// todo: move logic to actor.rollTrait() or actor.diceRoll()
if (!game.modules.get('dice-so-nice')?.active) foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice }); if (!game.modules.get('dice-so-nice')?.active) foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice });
const rollData = result.messageRoll.toJSON(); const rollData = result.messageRoll.toJSON();
delete rollData.options.messageRoll; delete rollData.options.messageRoll;
this.updatePartyData( this.updatePartyData(
{ {
[path]: rollData [basePath]: { rollData, successful: null }
}, },
this.getUpdatingParts(button) this.getUpdatingParts(button)
); );
} }
static async #makeRoll(_event, button) { /** @this GroupRollDialog */
const { member } = button.dataset; static async #removeRoll(_event, button) {
const character = this.party.system.groupRoll.aidingCharacters[member]; const member = button.closest('[data-member-key]').dataset.memberKey;
this.makeRoll(button, character, `system.groupRoll.aidingCharacters.${member}.rollData`); const { basePath } = this.#getCharacterDataById(member);
}
static async #makeLeaderRoll(_event, button) {
const character = this.party.system.groupRoll.leader;
this.makeRoll(button, character, 'system.groupRoll.leader.rollData');
}
async removeRoll(button, path) {
this.updatePartyData( this.updatePartyData(
{ {
[path]: { [basePath]: {
rollData: null, rollData: null,
rollChoice: null, rollChoice: null,
selected: false, selected: false,
successfull: null successful: null
} }
}, },
this.getUpdatingParts(button) this.getUpdatingParts(button)
); );
} }
static async #removeRoll(_event, button) { /** @this GroupRollDialog */
this.removeRoll(button, `system.groupRoll.aidingCharacters.${button.dataset.member}`); static async #rerollDice(_, button) {
}
static async #removeLeaderRoll(_event, button) {
this.removeRoll(button, 'system.groupRoll.leader');
}
async rerollDice(button, data, path) {
const { diceType } = button.dataset; const { diceType } = button.dataset;
const { data, basePath } = this.#getCharacterDataById(button.dataset.member);
const dieIndex = diceType === 'hope' ? 0 : diceType === 'fear' ? 1 : 2; const dieIndex = diceType === 'hope' ? 0 : diceType === 'fear' ? 1 : 2;
const newRoll = game.system.api.dice.DualityRoll.fromData(data.rollData); const newRoll = game.system.api.dice.DualityRoll.fromData(data.rollData);
@ -416,31 +406,19 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
const rollData = newRoll.toJSON(); const rollData = newRoll.toJSON();
this.updatePartyData( this.updatePartyData(
{ {
[path]: rollData [`${basePath}.rollData`]: rollData
}, },
this.getUpdatingParts(button) this.getUpdatingParts(button)
); );
} }
static async #rerollDice(_, button) { static #markSuccessful(_event, button) {
const { member } = button.dataset; const memberKey = button.closest('[data-member-key]').dataset.memberKey;
this.rerollDice( const previousValue = this.party.system.groupRoll.aidingCharacters[memberKey].successful;
button, const newValue = Boolean(button.dataset.success === 'true');
this.party.system.groupRoll.aidingCharacters[member],
`system.groupRoll.aidingCharacters.${member}.rollData`
);
}
static async #rerollLeaderDice(_, button) {
this.rerollDice(button, this.party.system.groupRoll.leader, `system.groupRoll.leader.rollData`);
}
static #markSuccessfull(_event, button) {
const previousValue = this.party.system.groupRoll.aidingCharacters[button.dataset.member].successfull;
const newValue = Boolean(button.dataset.successfull === 'true');
this.updatePartyData( this.updatePartyData(
{ {
[`system.groupRoll.aidingCharacters.${button.dataset.member}.successfull`]: [`system.groupRoll.aidingCharacters.${memberKey}.successful`]:
previousValue === newValue ? null : newValue previousValue === newValue ? null : newValue
}, },
this.getUpdatingParts(button) this.getUpdatingParts(button)
@ -484,7 +462,7 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
static async #finishRoll() { static async #finishRoll() {
const totalRoll = this.party.system.groupRoll.leader.roll; const totalRoll = this.party.system.groupRoll.leader.roll;
for (const character of Object.values(this.party.system.groupRoll.aidingCharacters)) { for (const character of Object.values(this.party.system.groupRoll.aidingCharacters)) {
totalRoll.terms.push(new foundry.dice.terms.OperatorTerm({ operator: character.successfull ? '+' : '-' })); totalRoll.terms.push(new foundry.dice.terms.OperatorTerm({ operator: character.successful ? '+' : '-' }));
totalRoll.terms.push(new foundry.dice.terms.NumericTerm({ number: 1 })); totalRoll.terms.push(new foundry.dice.terms.NumericTerm({ number: 1 }));
} }

View file

@ -188,8 +188,9 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
if (type === 'effect') { if (type === 'effect') {
const move = foundry.utils.getProperty(this.settings, this.movePath); const move = foundry.utils.getProperty(this.settings, this.movePath);
for (const action of move.actions) { for (const action of move.actions) {
const remainingEffects = action.effects.filter(x => x._id !== id); const actionEffects = action.effects ?? [];
if (action.effects.length !== remainingEffects.length) { const remainingEffects = actionEffects.filter(x => x._id !== id);
if (actionEffects.length !== remainingEffects.length) {
await action.update({ await action.update({
effects: remainingEffects.map(x => { effects: remainingEffects.map(x => {
const { _id, ...rest } = x; const { _id, ...rest } = x;

View file

@ -3,7 +3,7 @@ import DhDeathMove from '../../dialogs/deathMove.mjs';
import { CharacterLevelup, LevelupViewMode } from '../../levelup/_module.mjs'; import { CharacterLevelup, LevelupViewMode } from '../../levelup/_module.mjs';
import DhCharacterCreation from '../../characterCreation/characterCreation.mjs'; import DhCharacterCreation from '../../characterCreation/characterCreation.mjs';
import FilterMenu from '../../ux/filter-menu.mjs'; import FilterMenu from '../../ux/filter-menu.mjs';
import { getArmorSources, getDocFromElement, getDocFromElementSync } from '../../../helpers/utils.mjs'; import { getArmorSources, getDocFromElement, getDocFromElementSync, sortBy } from '../../../helpers/utils.mjs';
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */ /**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
@ -226,6 +226,11 @@ export default class CharacterSheet extends DHBaseActorSheet {
context.resources.stress.emptyPips = context.resources.stress.emptyPips =
context.resources.stress.max < maxResource ? maxResource - context.resources.stress.max : 0; context.resources.stress.max < maxResource ? maxResource - context.resources.stress.max : 0;
context.equippedItems = sortBy(
this.document.items.filter(i => i.system.equipped),
i => (i.type === 'weapon' ? (i.system.secondary ? 1 : 0) : 2)
);
context.beastformActive = this.document.effects.find(x => x.type === 'beastform'); context.beastformActive = this.document.effects.find(x => x.type === 'beastform');
return context; return context;

View file

@ -725,7 +725,7 @@ export default function DHApplicationMixin(Base) {
: null : null
: this.document; : this.document;
let systemData = {}; let systemData = null;
if (featureOnCharacter) { if (featureOnCharacter) {
systemData = { systemData = {
originItemType: this.document.type, originItemType: this.document.type,
@ -738,10 +738,11 @@ export default function DHApplicationMixin(Base) {
const data = { const data = {
name: cls.defaultName({ type, parent }), name: cls.defaultName({ type, parent }),
type, type
system: systemData
}; };
if (systemData) data.system = systemData;
if (inVault) data['system.inVault'] = true; if (inVault) data['system.inVault'] = true;
if (disabled) data.disabled = true; if (disabled) data.disabled = true;
if (type === 'domainCard' && parent?.system.domains?.length) { if (type === 'domainCard' && parent?.system.domains?.length) {

View file

@ -177,8 +177,8 @@ export const typeConfig = {
key: 'system.secondary', key: 'system.secondary',
label: 'DAGGERHEART.UI.ItemBrowser.subtype', label: 'DAGGERHEART.UI.ItemBrowser.subtype',
choices: [ choices: [
{ value: false, label: 'DAGGERHEART.ITEMS.Weapon.primaryWeapon' }, { value: false, label: 'DAGGERHEART.ITEMS.Weapon.primaryWeapon.full' },
{ value: true, label: 'DAGGERHEART.ITEMS.Weapon.secondaryWeapon' } { value: true, label: 'DAGGERHEART.ITEMS.Weapon.secondaryWeapon.full' }
] ]
}, },
{ {

View file

@ -453,7 +453,7 @@ export const allArmorFeatures = () => {
const feature = homebrewFeatures[key]; const feature = homebrewFeatures[key];
const actions = feature.actions.map(action => ({ const actions = feature.actions.map(action => ({
...action, ...action,
effects: action.effects.map(effect => feature.effects.find(x => x.id === effect._id)), effects: action.effects?.map(effect => feature.effects.find(x => x.id === effect._id)) ?? [],
type: action.type type: action.type
})); }));
const actionEffects = actions.flatMap(a => a.effects); const actionEffects = actions.flatMap(a => a.effects);
@ -1407,7 +1407,7 @@ export const allWeaponFeatures = () => {
const actions = feature.actions.map(action => ({ const actions = feature.actions.map(action => ({
...action, ...action,
effects: action.effects.map(effect => feature.effects.find(x => x.id === effect._id)), effects: action.effects?.map(effect => feature.effects.find(x => x.id === effect._id)) ?? [],
type: action.type type: action.type
})); }));
const actionEffects = actions.flatMap(a => a.effects); const actionEffects = actions.flatMap(a => a.effects);

View file

@ -57,15 +57,9 @@ const companionBaseResources = Object.freeze({
stress: { stress: {
id: 'stress', id: 'stress',
initial: 0, initial: 0,
max: 0, max: 3,
reverse: true, reverse: true,
label: 'DAGGERHEART.GENERAL.stress' label: 'DAGGERHEART.GENERAL.stress'
},
hope: {
id: 'hope',
initial: 0,
reverse: false,
label: 'DAGGERHEART.GENERAL.hope'
} }
}); });

View file

@ -189,6 +189,9 @@ export default class DhpAdversary extends DhCreature {
prepareDerivedData() { prepareDerivedData() {
super.prepareDerivedData(); super.prepareDerivedData();
this.attack.roll.isStandardAttack = true; this.attack.roll.isStandardAttack = true;
// Clamp resources (must be done last to ensure all updates occur)
this.resources.clamp();
} }
_getTags() { _getTags() {

View file

@ -1,6 +1,6 @@
import DHBaseActorSettings from '../../applications/sheets/api/actor-setting.mjs'; import DHBaseActorSettings from '../../applications/sheets/api/actor-setting.mjs';
import DHItem from '../../documents/item.mjs'; import DHItem from '../../documents/item.mjs';
import { getScrollTextData } from '../../helpers/utils.mjs'; import { createShallowProxy, getScrollTextData } from '../../helpers/utils.mjs';
const fields = foundry.data.fields; const fields = foundry.data.fields;
@ -180,8 +180,7 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
* @returns {object} * @returns {object}
*/ */
getRollData() { getRollData() {
const data = { ...this }; return createShallowProxy(this);
return data;
} }
/** /**

View file

@ -391,8 +391,9 @@ export default class DhCharacter extends DhCreature {
return this.domains.map(key => { return this.domains.map(key => {
const domain = allDomainData[key]; const domain = allDomainData[key];
return { return {
id: key,
...domain, ...domain,
label: game.i18n.localize(domain.label) label: game.i18n.localize(domain?.label) ?? key
}; };
}); });
} }
@ -410,14 +411,11 @@ export default class DhCharacter extends DhCreature {
} }
get loadoutSlot() { get loadoutSlot() {
const loadoutCount = this.domainCards.loadout?.length ?? 0, const loadoutCount = this.domainCards.loadout?.length ?? 0;
worldSetting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxLoadout, const worldSetting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxLoadout;
max = !worldSetting ? null : worldSetting + this.bonuses.maxLoadout;
return { return {
current: loadoutCount, current: loadoutCount,
available: !max ? true : Math.max(max - loadoutCount, 0), available: loadoutCount < worldSetting
max
}; };
} }
@ -780,6 +778,8 @@ export default class DhCharacter extends DhCreature {
prepareDerivedData() { prepareDerivedData() {
super.prepareDerivedData(); super.prepareDerivedData();
this.resources.hope.max -= this.scars;
if (this.companion) { if (this.companion) {
for (let levelKey in this.companion.system.levelData.levelups) { for (let levelKey in this.companion.system.levelData.levelups) {
const level = this.companion.system.levelData.levelups[levelKey]; const level = this.companion.system.levelData.levelups[levelKey];
@ -793,7 +793,6 @@ export default class DhCharacter extends DhCreature {
} }
} }
this.resources.hope.max -= this.scars;
this.attack.roll.trait = this.rules.attack.roll.trait ?? this.attack.roll.trait; this.attack.roll.trait = this.rules.attack.roll.trait ?? this.attack.roll.trait;
this.resources.armor = { this.resources.armor = {
@ -803,6 +802,9 @@ export default class DhCharacter extends DhCreature {
}; };
this.attack.damage.parts.hitPoints.value.custom.formula = `@prof${this.basicAttackDamageDice}${this.rules.attack.damage.bonus ? ` + ${this.rules.attack.damage.bonus}` : ''}`; this.attack.damage.parts.hitPoints.value.custom.formula = `@prof${this.basicAttackDamageDice}${this.rules.attack.damage.bonus ? ` + ${this.rules.attack.damage.bonus}` : ''}`;
// Clamp resources (must be done last to ensure all updates occur)
this.resources.clamp();
} }
getRollData() { getRollData() {

View file

@ -144,9 +144,6 @@ export default class DhCompanion extends DhCreature {
const level = this.levelData.levelups[levelKey]; const level = this.levelData.levelups[levelKey];
for (let selection of level.selections) { for (let selection of level.selections) {
switch (selection.type) { switch (selection.type) {
case 'hope':
this.resources.hope += selection.value;
break;
case 'vicious': case 'vicious':
if (selection.data[0] === 'damage') { if (selection.data[0] === 'damage') {
this.attack.damage.parts.hitPoints.value.dice = adjustDice( this.attack.damage.parts.hitPoints.value.dice = adjustDice(
@ -183,6 +180,9 @@ export default class DhCompanion extends DhCreature {
return acc; return acc;
}, this.partner.system.companionData.levelupChoices); }, this.partner.system.companionData.levelupChoices);
} }
// Clamp resources (must be done last to ensure all updates occur)
this.resources.clamp();
} }
async _preUpdate(changes, options, userId) { async _preUpdate(changes, options, userId) {

View file

@ -60,14 +60,4 @@ export default class DhCreature extends BaseDataActor {
} }
} }
} }
prepareDerivedData() {
const minLimitResource = resource => {
if (resource) resource.value = Math.min(resource.value, resource.max);
};
minLimitResource(this.resources.stress);
minLimitResource(this.resources.hitPoints);
minLimitResource(this.resources.hope);
}
} }

View file

@ -18,7 +18,7 @@ export default class DhParty extends BaseDataActor {
const fields = foundry.data.fields; const fields = foundry.data.fields;
return { return {
...super.defineSchema(), ...super.defineSchema(),
partyMembers: new ForeignDocumentUUIDArrayField({ type: 'Actor' }, { prune: true }), partyMembers: new ForeignDocumentUUIDArrayField({ type: 'Actor' }),
notes: new fields.HTMLField(), notes: new fields.HTMLField(),
gold: new GoldField(), gold: new GoldField(),
tagTeam: new fields.EmbeddedDataField(TagTeamData), tagTeam: new fields.EmbeddedDataField(TagTeamData),

View file

@ -80,6 +80,18 @@ class ResourcesField extends fields.TypedObjectField {
value.isReversed = resources[key].reverse; value.isReversed = resources[key].reverse;
value.max = typeof resource.max === 'number' ? (value.max ?? resource.max) : null; value.max = typeof resource.max === 'number' ? (value.max ?? resource.max) : null;
} }
Object.defineProperty(data, 'clamp', {
value: function () {
for (const key of Object.keys(this)) {
const resource = this[key];
if (typeof resource?.max === 'number') {
resource.value = Math.clamp(resource.value, 0, resource.max);
}
}
},
enumerable: false
});
return data; return data;
} }

View file

@ -10,6 +10,7 @@ export default class ForeignDocumentUUIDArrayField extends foundry.data.fields.A
*/ */
constructor(fieldOption = {}, options = {}, context = {}) { constructor(fieldOption = {}, options = {}, context = {}) {
super(new ForeignDocumentUUIDField(fieldOption), options, context); super(new ForeignDocumentUUIDField(fieldOption), options, context);
this.options.prune ??= true;
} }
/** @inheritdoc */ /** @inheritdoc */

View file

@ -30,7 +30,7 @@ export class CharacterData extends foundry.abstract.DataModel {
}), }),
rollData: new fields.JSONField({ nullable: true, initial: null }), rollData: new fields.JSONField({ nullable: true, initial: null }),
selected: new fields.BooleanField({ initial: false }), selected: new fields.BooleanField({ initial: false }),
successfull: new fields.BooleanField({ nullable: true, initial: null }) successful: new fields.BooleanField({ nullable: true, initial: null })
}; };
} }

View file

@ -7,7 +7,12 @@
* @property {boolean} isInventoryItem- Indicates whether items of this type is a Inventory Item * @property {boolean} isInventoryItem- Indicates whether items of this type is a Inventory Item
*/ */
import { addLinkedItemsDiff, getScrollTextData, updateLinkedItemApps } from '../../helpers/utils.mjs'; import {
addLinkedItemsDiff,
getScrollTextData,
createShallowProxy,
updateLinkedItemApps
} from '../../helpers/utils.mjs';
import { ActionsField } from '../fields/actionField.mjs'; import { ActionsField } from '../fields/actionField.mjs';
import FormulaField from '../fields/formulaField.mjs'; import FormulaField from '../fields/formulaField.mjs';
@ -159,8 +164,8 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
* @returns {object} * @returns {object}
*/ */
getRollData(options = {}) { getRollData(options = {}) {
const actorRollData = this.actor?.getRollData() ?? {}; const data = this.actor?.getRollData() ?? {};
const data = { ...actorRollData, item: { ...this } }; data.item = createShallowProxy(this);
return data; return data;
} }

View file

@ -28,7 +28,10 @@ export default class DHWeapon extends AttachableItem {
equipped: new fields.BooleanField({ initial: false }), equipped: new fields.BooleanField({ initial: false }),
//SETTINGS //SETTINGS
secondary: new fields.BooleanField({ initial: false, label: 'DAGGERHEART.ITEMS.Weapon.secondaryWeapon' }), secondary: new fields.BooleanField({
initial: false,
label: 'DAGGERHEART.ITEMS.Weapon.secondaryWeapon.full'
}),
burden: new fields.StringField({ burden: new fields.StringField({
required: true, required: true,
choices: CONFIG.DH.GENERAL.burden, choices: CONFIG.DH.GENERAL.burden,

View file

@ -19,7 +19,7 @@ export default class DHScene extends foundry.abstract.DataModel {
close: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.close.name' }), close: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.close.name' }),
far: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.far.name' }) far: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.far.name' })
}), }),
sceneEnvironments: new ForeignDocumentUUIDArrayField({ type: 'Actor', prune: true }) sceneEnvironments: new ForeignDocumentUUIDArrayField({ type: 'Actor' })
}; };
} }
} }

View file

@ -54,7 +54,7 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
maxDomains: new fields.NumberField({ maxDomains: new fields.NumberField({
required: true, required: true,
integer: true, integer: true,
min: 1, min: 0,
initial: 2, initial: 2,
label: 'DAGGERHEART.SETTINGS.Homebrew.FIELDS.maxDomains.label' label: 'DAGGERHEART.SETTINGS.Homebrew.FIELDS.maxDomains.label'
}), }),
@ -196,6 +196,12 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
return source; return source;
} }
_initialize(options) {
super._initialize(options);
this.maxDomains ||= Infinity;
this.maxLoadout ||= Infinity;
}
/** Invoked by the setting when data changes */ /** Invoked by the setting when data changes */
handleChange() { handleChange() {
if (this.maxFear) { if (this.maxFear) {

View file

@ -169,27 +169,36 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
super._applyChangeUnguided(actor, change, changes, options); super._applyChangeUnguided(actor, change, changes, options);
} }
static getChangeValue(model, change, effect) { /** Recursively finds the first parent document of the given object */
let key = change.value.toString(); static #resolveParentDocument(model, documentClass) {
const isOriginTarget = key.toLowerCase().includes('origin.@'); return model instanceof documentClass
let parseModel = model; ? model
if (isOriginTarget && effect.origin) { : model.parent
key = change.key.replaceAll(/origin\.@/gi, '@'); ? this.#resolveParentDocument(model.parent, documentClass)
try { : null;
const originEffect = foundry.utils.fromUuidSync(effect.origin);
const doc =
originEffect.parent?.parent instanceof game.system.api.documents.DhpActor
? originEffect.parent
: originEffect.parent.parent;
if (doc) parseModel = doc;
} catch (_) {}
} }
static getChangeValue(model, change, effect) {
let value = change.value.toString();
const useOrigin = value.toLowerCase().includes('origin.@') && effect.origin;
let origin = null;
if (effect.origin) {
if (useOrigin) value = value.replaceAll(/origin\.@/gi, '@');
const originEffect = foundry.utils.fromUuidSync(effect.origin);
origin = this.#resolveParentDocument(originEffect, Item);
}
// Get the actor and item documents. Note that actor roll data is inclusive of system roll data
const actor = this.#resolveParentDocument(model, Actor);
const item =
(useOrigin ? origin : null) ??
this.#resolveParentDocument(effect.parent, Item) ??
(origin?.actor === actor ? origin : null);
const stackingParsedValue = effect.system.stacking const stackingParsedValue = effect.system.stacking
? Roll.replaceFormulaData(key, { stacks: effect.system.stacking.value }) ? Roll.replaceFormulaData(value, { stacks: effect.system.stacking.value })
: key; : value;
const evalValue = itemAbleRollParse(stackingParsedValue, parseModel, effect.parent); const evalValue = this.effectSafeEval(itemAbleRollParse(stackingParsedValue, actor, item));
return evalValue ?? key; return evalValue ?? value;
} }
/** /**

View file

@ -1,7 +1,7 @@
import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs'; import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs';
import { LevelOptionType } from '../data/levelTier.mjs'; import { LevelOptionType } from '../data/levelTier.mjs';
import DHFeature from '../data/item/feature.mjs'; import DHFeature from '../data/item/feature.mjs';
import { createScrollText, damageKeyToNumber, getDamageKey } from '../helpers/utils.mjs'; import { createScrollText, damageKeyToNumber, getDamageKey, createShallowProxy } from '../helpers/utils.mjs';
import DhCompanionLevelUp from '../applications/levelup/companionLevelup.mjs'; import DhCompanionLevelUp from '../applications/levelup/companionLevelup.mjs';
import { ResourceUpdateMap } from '../data/action/baseAction.mjs'; import { ResourceUpdateMap } from '../data/action/baseAction.mjs';
import { abilities } from '../config/actorConfig.mjs'; import { abilities } from '../config/actorConfig.mjs';
@ -99,7 +99,7 @@ export default class DhpActor extends Actor {
} }
// Configure prototype token settings // Configure prototype token settings
if (['character', 'companion', 'party'].includes(this.type)) if (['character', 'companion', 'party'].includes(this.type)) {
Object.assign(update, { Object.assign(update, {
prototypeToken: { prototypeToken: {
sight: { enabled: true }, sight: { enabled: true },
@ -107,6 +107,8 @@ export default class DhpActor extends Actor {
disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY
} }
}); });
}
this.updateSource(update); this.updateSource(update);
} }
@ -593,10 +595,7 @@ export default class DhpActor extends Actor {
/**@inheritdoc */ /**@inheritdoc */
getRollData() { getRollData() {
const rollData = foundry.utils.deepClone(super.getRollData()); const rollData = createShallowProxy(super.getRollData());
/* system gets repeated infinately which causes issues when trying to use the data for document creation */
delete rollData.system;
rollData.id = this.id; rollData.id = this.id;
rollData.name = this.name; rollData.name = this.name;
rollData.system = this.system.getRollData(); rollData.system = this.system.getRollData();

View file

@ -54,13 +54,7 @@ export default class DHItem extends foundry.documents.Item {
* @returns * @returns
*/ */
getRollData(options = {}) { getRollData(options = {}) {
let data; let data = this.system.getRollData(options);
if (this.system.getRollData) data = this.system.getRollData(options);
else {
const actorRollData = this.actor?.getRollData(options) ?? {};
data = { ...actorRollData, item: { ...this.system } };
}
if (data?.item) { if (data?.item) {
data.item.flags = { ...this.flags }; data.item.flags = { ...this.flags };
data.item.name = this.name; data.item.name = this.name;

View file

@ -189,17 +189,14 @@ export const getDeleteKeys = (property, innerProperty, innerPropertyDefaultValue
// Fix on Foundry native formula replacement for DH // Fix on Foundry native formula replacement for DH
const nativeReplaceFormulaData = Roll.replaceFormulaData; const nativeReplaceFormulaData = Roll.replaceFormulaData;
Roll.replaceFormulaData = function (formula, baseData = {}, { missing, warn = false } = {}) { Roll.replaceFormulaData = function (formula, data = {}, { missing, warn = false } = {}) {
/* Inserting global data */ /* Inserting global data */
const data = { const defaultingTypes = [
...baseData, ...Object.keys(CONFIG.DH.GENERAL.multiplierTypes).map(x => ({ term: x, default: 1 })),
partySize: game.actors?.party?.system.partyMembers.length ?? 0 { term: 'partySize', default: game.actors?.party?.system.partyMembers.length ?? 0 }
}; ];
const terms = Object.keys(CONFIG.DH.GENERAL.multiplierTypes).map(type => { formula = defaultingTypes.reduce((a, c) => a.replaceAll(`@${c.term}`, data[c.term] ?? c.default), formula);
return { term: type, default: 1 };
});
formula = terms.reduce((a, c) => a.replaceAll(`@${c.term}`, data[c.term] ?? c.default), formula);
return nativeReplaceFormulaData(formula, data, { missing, warn }); return nativeReplaceFormulaData(formula, data, { missing, warn });
}; };
@ -375,10 +372,11 @@ export const itemAbleRollParse = (value, actor, item) => {
const isItemTarget = value.toLowerCase().includes('item.@'); const isItemTarget = value.toLowerCase().includes('item.@');
const slicedValue = isItemTarget ? value.replaceAll(/item\.@/gi, '@') : value; const slicedValue = isItemTarget ? value.replaceAll(/item\.@/gi, '@') : value;
const model = isItemTarget ? item : actor; const model = isItemTarget || item instanceof Item ? item : actor;
const rollData = isItemTarget || !model?.getRollData ? model : model.getRollData();
try { try {
return Roll.replaceFormulaData(slicedValue, isItemTarget || !model?.getRollData ? model : model.getRollData()); return Roll.replaceFormulaData(slicedValue, rollData);
} catch (_) { } catch (_) {
return ''; return '';
} }
@ -812,3 +810,31 @@ export function sortBy(arr, fn) {
}; };
return arr.sort(cmp); return arr.sort(cmp);
} }
/**
* Creates a proxy that allows retrieval of top data but diverts updates to a different object.
* Generally used for roll data
*/
export function createShallowProxy(obj) {
if (obj._isShallowProxy) return obj;
const overrides = {};
return new Proxy(obj, {
get(target, prop, receiver) {
if (prop === '_isShallowProxy') return true;
if (prop in overrides) return overrides[prop];
return Reflect.get(target, prop, receiver);
},
set(_target, prop, newValue) {
overrides[prop] = newValue;
return true;
},
deleteProperty(_target, prop) {
delete overrides[prop];
return true;
},
has(target, key) {
return key in overrides || key in target;
}
});
}

391
package-lock.json generated
View file

@ -19,8 +19,8 @@
"eslint": "^10.2.1", "eslint": "^10.2.1",
"eslint-plugin-prettier": "^5.5.5", "eslint-plugin-prettier": "^5.5.5",
"globals": "^17.5.0", "globals": "^17.5.0",
"husky": "^9.1.5", "husky": "^9.1.7",
"lint-staged": "^15.2.10", "lint-staged": "^16.4.0",
"postcss": "^8.4.32", "postcss": "^8.4.32",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"rollup-plugin-postcss": "^4.0.2" "rollup-plugin-postcss": "^4.0.2"
@ -752,10 +752,11 @@
} }
}, },
"node_modules/ansi-escapes": { "node_modules/ansi-escapes": {
"version": "7.0.0", "version": "7.3.0",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz",
"integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"environment": "^1.0.0" "environment": "^1.0.0"
}, },
@ -767,10 +768,11 @@
} }
}, },
"node_modules/ansi-regex": { "node_modules/ansi-regex": {
"version": "6.1.0", "version": "6.2.2",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
"dev": true, "dev": true,
"license": "MIT",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },
@ -779,10 +781,11 @@
} }
}, },
"node_modules/ansi-styles": { "node_modules/ansi-styles": {
"version": "6.2.1", "version": "6.2.3",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
"dev": true, "dev": true,
"license": "MIT",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },
@ -1207,6 +1210,7 @@
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
"integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"restore-cursor": "^5.0.0" "restore-cursor": "^5.0.0"
}, },
@ -1218,39 +1222,34 @@
} }
}, },
"node_modules/cli-truncate": { "node_modules/cli-truncate": {
"version": "4.0.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz",
"integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", "integrity": "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"slice-ansi": "^5.0.0", "slice-ansi": "^8.0.0",
"string-width": "^7.0.0" "string-width": "^8.2.0"
}, },
"engines": { "engines": {
"node": ">=18" "node": ">=20"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/cli-truncate/node_modules/emoji-regex": {
"version": "10.4.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
"integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
"dev": true
},
"node_modules/cli-truncate/node_modules/string-width": { "node_modules/cli-truncate/node_modules/string-width": {
"version": "7.2.0", "version": "8.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.0.tgz",
"integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "integrity": "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"emoji-regex": "^10.3.0", "get-east-asian-width": "^1.5.0",
"get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.2"
"strip-ansi": "^7.1.0"
}, },
"engines": { "engines": {
"node": ">=18" "node": ">=20"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
@ -1357,15 +1356,17 @@
"version": "2.0.20", "version": "2.0.20",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
"dev": true "dev": true,
"license": "MIT"
}, },
"node_modules/commander": { "node_modules/commander": {
"version": "13.1.0", "version": "14.0.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
"integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
"dev": true, "dev": true,
"license": "MIT",
"engines": { "engines": {
"node": ">=18" "node": ">=20"
} }
}, },
"node_modules/commondir": { "node_modules/commondir": {
@ -1854,6 +1855,7 @@
"resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
"integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
"dev": true, "dev": true,
"license": "MIT",
"engines": { "engines": {
"node": ">=18" "node": ">=18"
}, },
@ -2175,33 +2177,11 @@
} }
}, },
"node_modules/eventemitter3": { "node_modules/eventemitter3": {
"version": "5.0.1", "version": "5.0.4",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
"dev": true
},
"node_modules/execa": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
"integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
"dev": true, "dev": true,
"dependencies": { "license": "MIT"
"cross-spawn": "^7.0.3",
"get-stream": "^8.0.1",
"human-signals": "^5.0.0",
"is-stream": "^3.0.0",
"merge-stream": "^2.0.0",
"npm-run-path": "^5.1.0",
"onetime": "^6.0.0",
"signal-exit": "^4.1.0",
"strip-final-newline": "^3.0.0"
},
"engines": {
"node": ">=16.17"
},
"funding": {
"url": "https://github.com/sindresorhus/execa?sponsor=1"
}
}, },
"node_modules/expand-tilde": { "node_modules/expand-tilde": {
"version": "2.0.2", "version": "2.0.2",
@ -2471,10 +2451,11 @@
} }
}, },
"node_modules/get-east-asian-width": { "node_modules/get-east-asian-width": {
"version": "1.3.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz",
"integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==",
"dev": true, "dev": true,
"license": "MIT",
"engines": { "engines": {
"node": ">=18" "node": ">=18"
}, },
@ -2519,18 +2500,6 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/get-stream": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
"integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
"dev": true,
"engines": {
"node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/glob": { "node_modules/glob": {
"version": "8.1.0", "version": "8.1.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
@ -2931,20 +2900,12 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/human-signals": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
"integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
"dev": true,
"engines": {
"node": ">=16.17.0"
}
},
"node_modules/husky": { "node_modules/husky": {
"version": "9.1.7", "version": "9.1.7",
"resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
"integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==",
"dev": true, "dev": true,
"license": "MIT",
"bin": { "bin": {
"husky": "bin.js" "husky": "bin.js"
}, },
@ -3213,12 +3174,16 @@
} }
}, },
"node_modules/is-fullwidth-code-point": { "node_modules/is-fullwidth-code-point": {
"version": "4.0.0", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz",
"integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": {
"get-east-asian-width": "^1.3.1"
},
"engines": { "engines": {
"node": ">=12" "node": ">=18"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
@ -3321,18 +3286,6 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/is-stream": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
"integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
"dev": true,
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-typed-array": { "node_modules/is-typed-array": {
"version": "1.1.15", "version": "1.1.15",
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
@ -3545,52 +3498,38 @@
"node": ">=10.13.0" "node": ">=10.13.0"
} }
}, },
"node_modules/lilconfig": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
"dev": true,
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/antonk52"
}
},
"node_modules/lint-staged": { "node_modules/lint-staged": {
"version": "15.5.2", "version": "16.4.0",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.5.2.tgz", "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.4.0.tgz",
"integrity": "sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w==", "integrity": "sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"chalk": "^5.4.1", "commander": "^14.0.3",
"commander": "^13.1.0", "listr2": "^9.0.5",
"debug": "^4.4.0", "picomatch": "^4.0.3",
"execa": "^8.0.1",
"lilconfig": "^3.1.3",
"listr2": "^8.2.5",
"micromatch": "^4.0.8",
"pidtree": "^0.6.0",
"string-argv": "^0.3.2", "string-argv": "^0.3.2",
"yaml": "^2.7.0" "tinyexec": "^1.0.4",
"yaml": "^2.8.2"
}, },
"bin": { "bin": {
"lint-staged": "bin/lint-staged.js" "lint-staged": "bin/lint-staged.js"
}, },
"engines": { "engines": {
"node": ">=18.12.0" "node": ">=20.17"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/lint-staged" "url": "https://opencollective.com/lint-staged"
} }
}, },
"node_modules/listr2": { "node_modules/listr2": {
"version": "8.3.3", "version": "9.0.5",
"resolved": "https://registry.npmjs.org/listr2/-/listr2-8.3.3.tgz", "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz",
"integrity": "sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==", "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"cli-truncate": "^4.0.0", "cli-truncate": "^5.0.0",
"colorette": "^2.0.20", "colorette": "^2.0.20",
"eventemitter3": "^5.0.1", "eventemitter3": "^5.0.1",
"log-update": "^6.1.0", "log-update": "^6.1.0",
@ -3598,7 +3537,7 @@
"wrap-ansi": "^9.0.0" "wrap-ansi": "^9.0.0"
}, },
"engines": { "engines": {
"node": ">=18.0.0" "node": ">=20.0.0"
} }
}, },
"node_modules/loader-utils": { "node_modules/loader-utils": {
@ -3664,6 +3603,7 @@
"resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
"integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"ansi-escapes": "^7.0.0", "ansi-escapes": "^7.0.0",
"cli-cursor": "^5.0.0", "cli-cursor": "^5.0.0",
@ -3678,26 +3618,12 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/log-update/node_modules/is-fullwidth-code-point": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz",
"integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==",
"dev": true,
"dependencies": {
"get-east-asian-width": "^1.0.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/log-update/node_modules/slice-ansi": { "node_modules/log-update/node_modules/slice-ansi": {
"version": "7.1.0", "version": "7.1.2",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz",
"integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"ansi-styles": "^6.2.1", "ansi-styles": "^6.2.1",
"is-fullwidth-code-point": "^5.0.0" "is-fullwidth-code-point": "^5.0.0"
@ -3766,12 +3692,6 @@
"integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
"dev": true "dev": true
}, },
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
"dev": true
},
"node_modules/micromatch": { "node_modules/micromatch": {
"version": "4.0.8", "version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
@ -3807,23 +3727,12 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/mimic-fn": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
"integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/mimic-function": { "node_modules/mimic-function": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
"integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
"dev": true, "dev": true,
"license": "MIT",
"engines": { "engines": {
"node": ">=18" "node": ">=18"
}, },
@ -3985,33 +3894,6 @@
"node": ">= 10.13.0" "node": ">= 10.13.0"
} }
}, },
"node_modules/npm-run-path": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
"integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
"dev": true,
"dependencies": {
"path-key": "^4.0.0"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/npm-run-path/node_modules/path-key": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
"integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/nth-check": { "node_modules/nth-check": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
@ -4066,15 +3948,16 @@
} }
}, },
"node_modules/onetime": { "node_modules/onetime": {
"version": "6.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
"integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"mimic-fn": "^4.0.0" "mimic-function": "^5.0.0"
}, },
"engines": { "engines": {
"node": ">=12" "node": ">=18"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
@ -4259,10 +4142,11 @@
"dev": true "dev": true
}, },
"node_modules/picomatch": { "node_modules/picomatch": {
"version": "4.0.2", "version": "4.0.4",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true, "dev": true,
"license": "MIT",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },
@ -4270,18 +4154,6 @@
"url": "https://github.com/sponsors/jonschlinkert" "url": "https://github.com/sponsors/jonschlinkert"
} }
}, },
"node_modules/pidtree": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz",
"integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
"dev": true,
"bin": {
"pidtree": "bin/pidtree.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/pify": { "node_modules/pify": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
@ -5169,6 +5041,7 @@
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
"integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"onetime": "^7.0.0", "onetime": "^7.0.0",
"signal-exit": "^4.1.0" "signal-exit": "^4.1.0"
@ -5180,21 +5053,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/restore-cursor/node_modules/onetime": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
"integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
"dev": true,
"dependencies": {
"mimic-function": "^5.0.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/reusify": { "node_modules/reusify": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
@ -5208,7 +5066,8 @@
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
"dev": true "dev": true,
"license": "MIT"
}, },
"node_modules/rollup": { "node_modules/rollup": {
"version": "4.44.0", "version": "4.44.0",
@ -5488,6 +5347,7 @@
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"dev": true, "dev": true,
"license": "ISC",
"engines": { "engines": {
"node": ">=14" "node": ">=14"
}, },
@ -5496,16 +5356,17 @@
} }
}, },
"node_modules/slice-ansi": { "node_modules/slice-ansi": {
"version": "5.0.0", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz",
"integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", "integrity": "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"ansi-styles": "^6.0.0", "ansi-styles": "^6.2.3",
"is-fullwidth-code-point": "^4.0.0" "is-fullwidth-code-point": "^5.1.0"
}, },
"engines": { "engines": {
"node": ">=12" "node": ">=20"
}, },
"funding": { "funding": {
"url": "https://github.com/chalk/slice-ansi?sponsor=1" "url": "https://github.com/chalk/slice-ansi?sponsor=1"
@ -5639,12 +5500,13 @@
} }
}, },
"node_modules/strip-ansi": { "node_modules/strip-ansi": {
"version": "7.1.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"ansi-regex": "^6.0.1" "ansi-regex": "^6.2.2"
}, },
"engines": { "engines": {
"node": ">=12" "node": ">=12"
@ -5653,18 +5515,6 @@
"url": "https://github.com/chalk/strip-ansi?sponsor=1" "url": "https://github.com/chalk/strip-ansi?sponsor=1"
} }
}, },
"node_modules/strip-final-newline": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
"integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/style-inject": { "node_modules/style-inject": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/style-inject/-/style-inject-0.3.0.tgz", "resolved": "https://registry.npmjs.org/style-inject/-/style-inject-0.3.0.tgz",
@ -5800,6 +5650,16 @@
"readable-stream": "3" "readable-stream": "3"
} }
}, },
"node_modules/tinyexec": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz",
"integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/to-regex-range": { "node_modules/to-regex-range": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@ -6082,10 +5942,11 @@
} }
}, },
"node_modules/wrap-ansi": { "node_modules/wrap-ansi": {
"version": "9.0.0", "version": "9.0.2",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
"integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"ansi-styles": "^6.2.1", "ansi-styles": "^6.2.1",
"string-width": "^7.0.0", "string-width": "^7.0.0",
@ -6099,16 +5960,18 @@
} }
}, },
"node_modules/wrap-ansi/node_modules/emoji-regex": { "node_modules/wrap-ansi/node_modules/emoji-regex": {
"version": "10.4.0", "version": "10.6.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
"integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
"dev": true "dev": true,
"license": "MIT"
}, },
"node_modules/wrap-ansi/node_modules/string-width": { "node_modules/wrap-ansi/node_modules/string-width": {
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
"integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"emoji-regex": "^10.3.0", "emoji-regex": "^10.3.0",
"get-east-asian-width": "^1.0.0", "get-east-asian-width": "^1.0.0",
@ -6135,15 +5998,19 @@
} }
}, },
"node_modules/yaml": { "node_modules/yaml": {
"version": "2.8.0", "version": "2.8.3",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
"integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
"dev": true, "dev": true,
"license": "ISC",
"bin": { "bin": {
"yaml": "bin.mjs" "yaml": "bin.mjs"
}, },
"engines": { "engines": {
"node": ">= 14.6" "node": ">= 14.6"
},
"funding": {
"url": "https://github.com/sponsors/eemeli"
} }
}, },
"node_modules/yargs": { "node_modules/yargs": {

View file

@ -19,7 +19,8 @@
"createSymlink": "node ./tools/create-symlink.mjs", "createSymlink": "node ./tools/create-symlink.mjs",
"setup:dev": "node ./tools/dev-setup.mjs", "setup:dev": "node ./tools/dev-setup.mjs",
"lint": "eslint", "lint": "eslint",
"lint:fix": "eslint --fix" "lint:fix": "eslint --fix",
"prepare": "husky"
}, },
"devDependencies": { "devDependencies": {
"@foundryvtt/foundryvtt-cli": "^1.0.2", "@foundryvtt/foundryvtt-cli": "^1.0.2",
@ -29,13 +30,13 @@
"eslint": "^10.2.1", "eslint": "^10.2.1",
"eslint-plugin-prettier": "^5.5.5", "eslint-plugin-prettier": "^5.5.5",
"globals": "^17.5.0", "globals": "^17.5.0",
"husky": "^9.1.5", "husky": "^9.1.7",
"lint-staged": "^15.2.10", "lint-staged": "^16.4.0",
"postcss": "^8.4.32", "postcss": "^8.4.32",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"rollup-plugin-postcss": "^4.0.2" "rollup-plugin-postcss": "^4.0.2"
}, },
"lint-staged": { "lint-staged": {
"**/*": "prettier --write --ignore-unknown" "**/*": "eslint --fix"
} }
} }

View file

@ -0,0 +1,46 @@
h1 {
color: light-dark(@dark-blue, @golden);
font-family: var(--dh-font-subtitle);
font-size: var(--font-size-24);
text-align: center;
font-weight: 700;
}
header {
--bar-color: light-dark(@dark-blue, @golden);
color: light-dark(@dark, @beige);
display: flex;
justify-content: center;
align-items: center;
&:not(:first-child) {
margin-top: var(--spacer-8);
}
span {
padding: 0 10px;
}
&:before {
content: ' ';
flex: 1;
height: 1px;
background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, var(--bar-color) 100%);
}
&:after {
content: ' ';
flex: 1;
height: 1px;
background: linear-gradient(90deg, var(--bar-color) 0%, rgba(0, 0, 0, 0) 100%);
}
}
img.portrait {
border-radius: 50%;
border: none;
object-fit: cover;
object-position: center top;
width: 2.5rem;
height: 2.5rem;
}

View file

@ -0,0 +1,8 @@
.daggerheart.dialog.dh-style.views.group-roll-dialog {
.window-content {
@import "./_common.less";
}
}
@import "./initialization.less";
@import "./main.less";

View file

@ -1,62 +1,59 @@
.theme-light .daggerheart.dialog.dh-style.views.group-roll-dialog { .daggerheart.dialog.dh-style.views.group-roll-dialog {
.initialization-container .members-container .member-container { .initialization-container.active {
.member-name { display: flex;
background-image: url('../assets/parchments/dh-parchment-light.png'); flex-direction: column;
} overflow: hidden;
.main-roll {
display: flex;
flex-direction: column;
text-align: center;
padding-bottom: 4px;
&.inactive {
opacity: 0.4;
} }
} }
.daggerheart.dialog.dh-style.views.group-roll-dialog { .hint {
.initialization-container { color: var(--color-form-hint);
h2 { line-height: 1;
text-align: center; padding: var(--spacer-8) 0;
font-family: var(--dh-font-body);
font-size: var(--font-size-12);
font-weight: 300;
} }
.members-container { .members-container {
display: grid; display: flex;
grid-template-columns: 1fr 1fr 1fr 1fr; flex-direction: column;
gap: 8px; gap: 8px;
overflow-y: auto;
.member-container { .member-container {
position: relative;
display: flex; display: flex;
position: relative;
justify-content: center; justify-content: center;
height: unset;
padding: 4px 8px;
gap: var(--spacer-8);
flex: 0 0 auto;
&.inactive { &:not(.inactive) {
opacity: 0.4; background: @golden-bg;
} }
.member-name { .name {
position: absolute; flex: 1;
padding: 0 2px;
border: 1px solid;
border-radius: 6px;
margin-top: 4px;
color: light-dark(@dark, @beige); color: light-dark(@dark, @beige);
background-image: url('../assets/parchments/dh-parchment-dark.png'); font-weight: 500;
text-align: center; text-align: left;
} }
img {
border-radius: 6px;
border: 1px solid light-dark(@dark-blue, @golden);
}
}
}
.main-roll {
margin-top: 8px;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
&.inactive {
opacity: 0.4;
} }
} }
footer { footer {
margin-top: 8px; margin-top: 12px;
display: flex; display: flex;
gap: 8px; gap: 8px;

View file

@ -1,35 +0,0 @@
.daggerheart.dialog.dh-style.views.group-roll-dialog {
.main-character-outer-container {
&.inactive {
opacity: 0.3;
pointer-events: none;
}
.main-character-container {
.character-info {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 64px;
img {
height: 64px;
border-radius: 6px;
border: 1px solid light-dark(@dark-blue, @golden);
}
.character-data {
padding-left: 0.75rem;
flex: 1;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
text-align: left;
font-size: var(--font-size-18);
}
}
}
}
}

View file

@ -0,0 +1,273 @@
.daggerheart.dialog.dh-style.views.group-roll-dialog {
.group-roll.active {
display: flex;
flex-direction: column;
overflow: hidden;
a.roll-button {
&:hover {
text-shadow: none;
filter: drop-shadow(0 0 3px @golden-90);
}
}
.aiding-members {
display: flex;
flex-direction: column;
gap: 6px;
overflow-y: auto;
}
.item-tags {
gap: 6px;
.tag.failure,
.tag.success {
font-weight: 600;
justify-content: center;
min-width: 3ch;
}
.tag.success {
border-color: @green;
background: @green-10;
color: @green;
}
.tag.failure {
border-color: @red;
background: @red-10;
color: @red;
}
.tag {
padding-top: 0;
padding-bottom: 0;
line-height: 1.1875rem;
}
}
.with-result {
border-radius: 5px;
background: var(--duality-bg);
color: var(--color-light-2);
&.hope {
--duality-text-color: @golden;
--duality-bg: url(../assets/parchments/dh-parchment-hope.png);
}
&.fear {
--duality-text-color: @chat-blue;
--duality-bg: url(../assets/parchments/dh-parchment-fear.png);
}
&.critical {
--duality-text-color: @chat-purple;
--duality-bg: url(../assets/parchments/dh-parchment-critical.png);
}
.duality-label {
font-family: var(--dh-font-subtitle);
color: var(--duality-text-color);
font-weight: 700;
}
}
.member-roll-container {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
min-height: 3.375rem;
&.inactive {
pointer-events: none;
}
.name-area {
display: flex;
flex-direction: column;
flex: 1;
justify-content: center;
.name {
font-weight: 500;
}
.trait {
display: flex;
align-items: center;
gap: 6px;
select {
--input-height: 2em;
width: auto;
}
}
.item-tags {
align-items: stretch;
}
}
.buttons {
display: flex;
flex-direction: row;
button {
--button-text-color: var(--color-text-primary);
--button-size: 1.5em;
padding: 0 var(--spacer-4);
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
}
a.roll-button.initial-roll {
width: 1.875rem;
height: 1.875rem;
margin-right: 2px; /** makes hover look a bit nicer */
}
.with-result {
display: flex;
justify-content: end;
align-items: center;
gap: 6px;
.roll-data {
display: flex;
flex-direction: column;
align-items: end;
justify-content: center;
padding: 0 4px;
min-height: 3rem;
.duality-label {
font-size: var(--font-size-15);
.unused-damage {
text-decoration: line-through;
}
.with {
font-size: var(--font-size-10);
}
}
.roll-dice-container {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
gap: 2px;
.roll-dice {
position: relative;
display: flex;
align-items: center;
justify-content: center;
.dice-label {
position: absolute;
color: white;
font-size: 1rem;
paint-order: stroke fill;
-webkit-text-stroke: 2px black;
}
img {
height: 1.3125rem;
}
}
.roll-operator {
font-size: var(--font-size-18);
padding: 0 1px;
}
.roll-value {
font-size: var(--font-size-16);
padding: 0 1px;
}
}
}
.buttons {
flex-direction: column;
button {
color: var(--medium-red);
&[data-success=true] {
color: var(--green);
}
&.active {
text-shadow: 0 0 1px light-dark(@dark-80, @beige-80);
}
&.inactive {
opacity: 0.35;
}
}
}
}
}
}
.group-roll-results {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 4px;
font-size: var(--font-size-12);
padding: 6px 12px;
margin-top: 8px;
&.empty {
color: light-dark(@dark-blue-90, @beige-80);
border-radius: 3px;
justify-content: center;
border: 1px dashed light-dark(@dark-blue-90, @beige-80);
text-align: center;
height: 3.25rem;
font-family: @font-body;
}
.row {
display: flex;
align-items: center;
justify-content: space-between;
}
.divider {
height: 1px;
background-image: linear-gradient(90deg, transparent 0%, var(--duality-text-color) 50%, transparent 100%);
margin-block: var(--spacer-4);
}
.modifiers .item-tags {
min-height: calc(2px + 1.1875rem);
}
.total {
.label {
font-size: var(--font-size-14);
}
.duality-label {
display: flex;
align-items: center;
gap: var(--spacer-4);
.value {
font-size: 20px;
}
}
}
}
.finish-container {
margin-top: 16px;
gap: 16px;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
.finish-button {
grid-column: span 2;
}
}
}

View file

@ -1,265 +0,0 @@
.daggerheart.dialog.dh-style.views.group-roll-dialog {
.team-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
margin-bottom: 16px;
.team-member-container {
display: flex;
flex-direction: column;
justify-content: space-between;
gap: 8px;
flex: 1;
&.inactive {
opacity: 0.3;
pointer-events: none;
}
.data-container {
display: flex;
flex-direction: column;
gap: 8px;
width: 100%;
}
.member-info {
display: flex;
align-items: start;
width: 100%;
img {
height: 64px;
border-radius: 6px;
border: 1px solid light-dark(@dark-blue, @golden);
}
.member-data {
padding-left: 0.75rem;
flex: 1;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
text-align: left;
font-size: var(--font-size-18);
}
}
}
}
.roll-container {
display: flex;
flex-direction: column;
}
.roll-title {
font-size: var(--font-size-20);
font-weight: bold;
color: light-dark(@dark-blue, @golden);
text-align: center;
display: flex;
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: var(--text-color);
content: '';
flex: 1;
height: 2px;
}
&::before {
background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, light-dark(@dark-blue, @golden) 100%);
}
&::after {
background: linear-gradient(90deg, light-dark(@dark-blue, @golden) 0%, rgba(0, 0, 0, 0) 100%);
}
}
.roll-tools {
display: flex;
gap: 4px;
align-items: center;
img {
height: 16px;
}
a {
display: flex;
font-size: 16px;
&:hover {
text-shadow: none;
filter: drop-shadow(0 0 8px var(--golden));
}
}
}
.roll-data {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
&.hope {
--text-color: @golden;
--bg-color: @golden-40;
}
&.fear {
--text-color: @chat-blue;
--bg-color: @chat-blue-40;
}
&.critical {
--text-color: @chat-purple;
--bg-color: @chat-purple-40;
}
.duality-label {
color: var(--text-color);
font-size: var(--font-size-20);
font-weight: bold;
text-align: center;
.unused-damage {
text-decoration: line-through;
}
}
.roll-dice-container {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
gap: 8px;
.roll-dice {
position: relative;
display: flex;
align-items: center;
justify-content: center;
.dice-label {
position: absolute;
color: white;
font-size: 1rem;
paint-order: stroke fill;
-webkit-text-stroke: 2px black;
}
img {
height: 32px;
}
}
.roll-operator {
font-size: var(--font-size-24);
}
.roll-value {
font-size: 18px;
}
}
.roll-total {
background: var(--bg-color);
color: var(--text-color);
border-radius: 4px;
padding: 3px;
}
}
.roll-success-container {
display: flex;
align-items: center;
justify-content: space-around;
.roll-success-tools {
display: flex;
align-items: center;
gap: 4px;
color: light-dark(@dark-blue, @golden);
i {
font-size: 24px;
}
}
.roll-success-modifier {
display: flex;
align-items: center;
justify-content: right;
gap: 2px;
font-size: var(--font-size-20);
padding: 0px 4px;
&.success {
background: @green-10;
color: @green;
}
&.failure {
background: @red-10;
color: @red;
}
}
}
.section-title {
font-size: var(--font-size-18);
font-weight: bold;
}
.group-roll-results {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
font-size: var(--font-size-20);
.group-roll-container {
display: flex;
align-items: center;
gap: 2px;
}
}
.finish-container {
margin-top: 16px;
gap: 16px;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
.finish-button {
grid-column: span 2;
}
}
.hint {
text-align: center;
}
}

View file

@ -1,50 +0,0 @@
@import '../../utils/colors.less';
.application.daggerheart.group-roll {
fieldset.one-column {
min-width: 500px;
margin-bottom: 10px;
}
.actor-item {
display: flex;
align-items: center;
gap: 15px;
width: 100%;
img {
height: 40px;
width: 40px;
border-radius: 50%;
object-fit: cover;
}
.actor-info {
display: flex;
flex-direction: column;
gap: 10px;
.actor-check-info {
display: flex;
gap: 10px;
.form-fields {
display: flex;
gap: 5px;
align-items: center;
input {
max-width: 40px;
text-align: center;
}
}
}
}
.controls {
margin-left: auto;
}
}
.tooltip-container {
width: 100%;
}
}

View file

@ -31,14 +31,10 @@
@import './reroll-dialog/sheet.less'; @import './reroll-dialog/sheet.less';
@import './group-roll/group-roll.less';
@import './tag-team-dialog/initialization.less'; @import './tag-team-dialog/initialization.less';
@import './tag-team-dialog/sheet.less'; @import './tag-team-dialog/sheet.less';
@import './group-roll-dialog/initialization.less'; @import './group-roll-dialog/index.less';
@import './group-roll-dialog/leader.less';
@import './group-roll-dialog/sheet.less';
@import './image-select/sheet.less'; @import './image-select/sheet.less';

View file

@ -100,7 +100,7 @@
scrollbar-color: light-dark(@dark-blue, @golden) transparent; scrollbar-color: light-dark(@dark-blue, @golden) transparent;
} }
button { button:where(:not(.plain)) {
background: light-dark(transparent, @golden); background: light-dark(transparent, @golden);
border: 1px solid light-dark(@dark-blue, @dark-blue); border: 1px solid light-dark(@dark-blue, @dark-blue);
color: light-dark(@dark-blue, @dark-blue); color: light-dark(@dark-blue, @dark-blue);

View file

@ -2,7 +2,7 @@
"id": "daggerheart", "id": "daggerheart",
"title": "Daggerheart", "title": "Daggerheart",
"description": "An unofficial implementation of the Daggerheart system", "description": "An unofficial implementation of the Daggerheart system",
"version": "2.2.0", "version": "2.2.1",
"compatibility": { "compatibility": {
"minimum": "14.359", "minimum": "14.359",
"verified": "14.360", "verified": "14.360",
@ -10,7 +10,7 @@
}, },
"url": "https://github.com/Foundryborne/daggerheart", "url": "https://github.com/Foundryborne/daggerheart",
"manifest": "https://raw.githubusercontent.com/Foundryborne/daggerheart/v14/system.json", "manifest": "https://raw.githubusercontent.com/Foundryborne/daggerheart/v14/system.json",
"download": "https://github.com/Foundryborne/daggerheart/releases/download/2.2.0/system.zip", "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.2.1/system.zip",
"authors": [ "authors": [
{ {
"name": "WBHarry" "name": "WBHarry"

View file

@ -1,84 +0,0 @@
<div class="group-roll">
<header class="dialog-header">
<h1>{{localize "DAGGERHEART.UI.Chat.groupRoll.title"}}</h1>
</header>
<fieldset class="one-column">
<legend>{{localize "DAGGERHEART.UI.Chat.groupRoll.leader"}}</legend>
{{#unless leader.actor}}
<input type="text" class="leader-change-input" />
<div class="drag-area">
<span>{{localize "DAGGERHEART.UI.Chat.groupRoll.selectLeader"}}</span>
</div>
{{else}}
<div class="actor-item">
<img src="{{leader.actor.img}}" alt="{{leader.actor.name}}">
<div class="actor-info">
<span class="actor-name">{{leader.actor.name}}</span>
<div class="actor-check-info">
<div class="form-fields">
<label>{{localize "DAGGERHEART.GENERAL.Trait.single"}}</label>
<select name="actorLeader.trait">
{{selectOptions traitList selected=leader.trait labelAttr="label" valueAttr="id" localize=true }}
</select>
</div>
{{!-- Not used yet --}}
{{!-- <div class="form-fields">
<label>{{localize "DAGGERHEART.GENERAL.difficulty"}}</label>
<input type="number" name="actorLeader.difficulty" value="{{leader.difficulty}}" data-dtype="Number">
</div> --}}
</div>
</div>
<div class="controls">
<a data-action="removeLeader" data-leader-uuid="{{leader.actor.uuid}}">
<i class="fa-solid fa-trash"></i>
</a>
</div>
</div>
{{/unless}}
</fieldset>
<fieldset class="one-column">
<legend>{{localize "DAGGERHEART.UI.Chat.groupRoll.partyTeam"}}</legend>
<span class="tooltip-container" {{#if allSelected}}data-tooltip="{{localize "DAGGERHEART.UI.Chat.groupRoll.wholePartySelected"}}"{{/if}}>
<input type="text" class="team-push-input" {{disabled @root.allSelected}}/>
</span>
{{#if (gt this.members.length 0)}}
{{#each members as |member index|}}
<div class="actor-item">
<img src="{{member.actor.img}}" alt="{{member.actor.name}}">
<div class="actor-info">
<span class="actor-name">{{member.actor.name}}</span>
<div class="actor-check-info">
<div class="form-fields">
<label>{{localize "DAGGERHEART.GENERAL.Trait.single"}}</label>
<select name="{{concat "actorsMembers." index ".trait"}}">
{{selectOptions @root.traitList selected=member.trait labelAttr="label" valueAttr="id" localize=true }}
</select>
</div>
{{!-- Not used yet --}}
{{!-- <div class="form-fields">
<label>{{localize "DAGGERHEART.GENERAL.difficulty"}}</label>
<input type="number" name="{{concat "actorsMembers." index ".difficulty"}}" value="{{member.difficulty}}" data-dtype="Number">
</div> --}}
</div>
</div>
<div class="controls">
<a data-action="removeMember" data-member-uuid="{{member.actor.uuid}}">
<i class="fa-solid fa-trash"></i>
</a>
</div>
</div>
{{/each}}
{{/if}}
{{#unless allSelected}}
<div class="drag-area">
<span>{{localize "DAGGERHEART.UI.Chat.groupRoll.selectMember"}}</span>
</div>
{{/unless}}
</fieldset>
<button class="submit-btn" data-action="roll" {{disabled @root.rollDisabled}}>
<i class="fa-solid fa-dice"></i>
<span>{{localize "DAGGERHEART.GENERAL.roll"}}</span>
</button>
</div>

View file

@ -1,20 +0,0 @@
<section class="tab {{#if tabs.groupRoll.active}} active{{/if}}" data-group="{{tabs.groupRoll.group}}" data-tab="{{tabs.groupRoll.id}}">
<fieldset>
<legend>{{localize "DAGGERHEART.GENERAL.result.single"}}</legend>
<div class="group-roll-results">
{{#if hasRolled}}<span class="roll-title {{groupRoll.totalDualityClass}}">{{groupRoll.total}} {{groupRoll.totalLabel}}</span>{{/if}}
<div class="group-roll-container">
<span>{{#if groupRoll.leaderTotal includeZero=true}}{{groupRoll.leaderTotal}}{{else}}{{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.leaderRoll"}}{{/if}}</span>
{{#each groupRoll.modifiers as |modifier|}}
<span>{{#if (gte modifier 0)}}+{{else}}-{{/if}}</span>
<span>{{positive modifier}}</span>
{{/each}}
{{#unless groupRoll.modifiers.length}}
<span>+</span>
<span>{{localize "DAGGERHEART.GENERAL.Modifier.plural"}}</span>
{{/unless}}
</div>
</div>
</fieldset>
</section>

View file

@ -1,85 +0,0 @@
{{#with (lookup members partId)}}
<fieldset class="team-member-container {{#if @root.allHaveRolled}}select-padding{{/if}} {{#unless isEditable}}inactive{{/unless}}" data-member-key="{{@root.partId}}">
<div class="data-container">
<div class="member-info">
<img src="{{img}}" />
<div class="member-data">
<span class="member-name">{{name}}</span>
<div class="roll-setup">
<div class="form-group">
<div class="form-fields">
{{!-- <label>{{localize "DAGGERHEART.GENERAL.Trait.single"}}</label> --}}
<select name="{{concat "system.groupRoll.aidingCharacters." @root.partId ".rollChoice"}}" {{#if hasRolled}}disabled{{/if}}>
{{selectOptions ../traitOptions selected=rollChoice localize=true}}
</select>
</div>
</div>
</div>
</div>
</div>
{{#if readyToRoll}}
<div class="roll-container">
<span class="roll-title">
{{localize "DAGGERHEART.GENERAL.roll"}}
<div class="roll-tools">
<a class="roll-button" data-action="makeRoll" data-member="{{@root.partId}}">
<img src="systems/daggerheart/assets/icons/dice/hope/d12.svg" />
</a>
{{#if hasRolled}}
<a class="delete-button" data-action="removeRoll" data-member="{{@root.partId}}">
<i class="fa-solid fa-trash"></i>
</a>
{{/if}}
</div>
</span>
{{#if roll}}
<div class="roll-data {{#if roll.withHope}}hope{{else if roll.withFear}}fear{{else}}critical{{/if}}">
<div class="duality-label">{{roll.total}} {{localize "DAGGERHEART.GENERAL.withThing" thing=roll.totalLabel}}</div>
<div class="roll-dice-container">
<a class="roll-dice" data-action="rerollDice" data-member="{{@root.partId}}" data-dice-type="hope">
<span class="dice-label">{{roll.dHope.total}}</span>
<img src="{{concat "systems/daggerheart/assets/icons/dice/hope/" roll.dHope.denomination ".svg"}}" />
</a>
<span class="roll-operator">+</span>
<a class="roll-dice" data-action="rerollDice" data-member="{{@root.partId}}" data-dice-type="fear">
<span class="dice-label">{{roll.dFear.total}}</span>
<img src="{{concat "systems/daggerheart/assets/icons/dice/fear/" roll.dFear.denomination ".svg"}}" />
</a>
{{#if roll.advantage.type}}
<span class="roll-operator">{{#if (eq roll.advantage.type 1)}}+{{else}}-{{/if}}</span>
<span class="roll-dice">
<span class="dice-label">{{roll.advantage.value}}</span>
<img src="{{concat "systems/daggerheart/assets/icons/dice/" (ifThen (eq roll.advantage.type 1) "adv/" "disadv/") roll.advantage.dice ".svg"}}" />
</span>
{{/if}}
<span class="roll-operator">{{#if (gte roll.modifierTotal 0)}}+{{else}}-{{/if}}</span>
<span class="roll-value">{{positive roll.modifierTotal}}</span>
</div>
</div>
{{else}}
<span class="hint">{{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.makeYourRoll"}}</span>
{{/if}}
</div>
{{/if}}
{{#if hasRolled}}
<div class="roll-success-container">
{{#if ../isGM}}
<div class="roll-success-tools">
<a data-action="markSuccessfull" data-member="{{@root.partId}}" data-successfull="true">
<i class="{{#if successfull}}fa-solid{{else}}fa-regular{{/if}} fa-circle-check"></i>
</a>
<a data-action="markSuccessfull" data-member="{{@root.partId}}">
<i class="{{#unless successfull}}fa-solid{{else}}fa-regular{{/unless}} fa-circle-xmark"></i>
</a>
</div>
{{/if}}
<div class="roll-success-modifier {{#if successfull}}success{{else if (not (isNullish successfull))}}failure{{/if}}">
{{localize "DAGGERHEART.GENERAL.Modifier.single"}}{{#if successfull}} + 1{{else if (isNullish successfull)}} + ?{{else}} - 1{{/if}}
</div>
</div>
{{/if}}
</div>
</fieldset>
{{/with}}

View file

@ -1,32 +1,43 @@
<section class="initialization-container tab {{#if tabs.initialization.active}} active{{/if}}" data-group="{{tabs.initialization.group}}" data-tab="{{tabs.initialization.id}}"> <section class="initialization-container tab {{#if tabs.initialization.active}} active{{/if}}" data-group="{{tabs.initialization.group}}" data-tab="{{tabs.initialization.id}}">
<div class="members-container"> <h1>{{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.initializationTitle"}}</h1>
{{#each memberSelection as |member|}} <header><span>{{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.leader"}}</span></header>
<a <section class="main-roll {{#if selectedLeaderDisabled}}inactive{{/if}}">
class="member-container {{#unless member.selected}}inactive {{#if ../allselected}}locked{{/if}}{{/unless}}" <div class="hint">{{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.selectLeaderHint"}}</div>
data-action="toggleSelectMember" data-id="{{member.id}}" {{#if (and (not member.selected) ../allSelected)}}disabled{{/if}}
>
<span class="member-name">{{member.name}}</span>
<img src="{{member.img}}" />
</a>
{{/each}}
</div>
<div class="main-roll {{#if selectedLeaderDisabled}}inactive{{/if}}">
<div class="form-group">
<label>{{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.leader"}}</label>
<div class="form-fields">
<select class="main-character-field" {{#if selectedLeaderDisabled}}disabled{{/if}}> <select class="main-character-field" {{#if selectedLeaderDisabled}}disabled{{/if}}>
{{selectOptions selectedLeaderOptions selected=selectedLeader.memberId blank="" }} {{selectOptions selectedLeaderOptions selected=selectedLeader.memberId blank="" }}
</select> </select>
</div> </section>
</div>
<header><span>{{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.members"}}</span></header>
<div class="hint">{{"DAGGERHEART.APPLICATIONS.GroupRollSelect.selectParticipantsHint"}}</div>
<div class="members-container">
{{#each memberSelection as |member|}}
<button
class="plain member-container {{#unless member.selected}}inactive {{#if ../allselected}}locked{{/if}}{{/unless}}"
data-action="toggleSelectMember"
data-id="{{member.id}}"
{{#if (and (not member.selected) ../allSelected)}}disabled{{/if}}
>
<img class="portrait" src="{{member.img}}" />
<span class="name">
{{member.name}}
{{#if (eq @root.selectedLeader.memberId member.id)}}
<i class="fa-solid fa-crown" inert></i>
{{/if}}
</span>
<input type="checkbox" {{#if member.selected}}checked{{/if}} />
</button>
{{/each}}
</div> </div>
<footer> <footer>
<button type="button" data-action="startGroupRoll" {{#unless canStartGroupRoll}}disabled{{/unless}}>{{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.startGroupRoll"}} <i class="fa-solid fa-arrow-right-long"></i></button>
<div class="finish-tools {{#unless canStartGroupRoll}}inactive{{/unless}}"> <div class="finish-tools {{#unless canStartGroupRoll}}inactive{{/unless}}">
<span>{{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.openDialogForAll"}}</span> <span>{{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.openDialogForAll"}}</span>
<input type="checkbox" class="openforall-field" {{#unless canStartGroupRoll}}disabled{{/unless}} {{checked openForAllPlayers}} /> <input type="checkbox" class="openforall-field" {{#unless canStartGroupRoll}}disabled{{/unless}} {{checked openForAllPlayers}} />
</div> </div>
<button type="button" data-action="startGroupRoll" {{#unless canStartGroupRoll}}disabled{{/unless}}>
<i class="fa-solid fa-arrow-right-long" inert></i>
{{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.startGroupRoll"}}
</button>
</footer> </footer>
</section> </section>

View file

@ -1,73 +0,0 @@
<section class="tab {{#if tabs.groupRoll.active}} active{{/if}}" data-group="{{tabs.groupRoll.group}}" data-tab="{{tabs.groupRoll.id}}">
{{#with leader}}
<div class="main-character-outer-container {{#unless isEditable}}inactive{{/unless}}">
<div class="section-title">{{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.leader"}}</div>
<fieldset>
<div class="main-character-container">
<div class="character-info">
<img src="{{img}}" />
<div class="character-data">
<span>{{name}}</span>
<div class="roll-setup">
<div class="form-group">
<div class="form-fields">
<select name="system.groupRoll.leader.rollChoice" {{#if hasRolled}}disabled{{/if}}>
{{selectOptions ../traitOptions selected=rollChoice localize=true}}
</select>
</div>
</div>
</div>
</div>
</div>
</div>
{{#if readyToRoll}}
<div class="roll-container">
<span class="roll-title">
{{localize "DAGGERHEART.GENERAL.roll"}}
<div class="roll-tools">
<a class="roll-button" data-action="makeLeaderRoll">
<img src="systems/daggerheart/assets/icons/dice/hope/d12.svg" />
</a>
{{#if hasRolled}}
<a class="delete-button" data-action="removeLeaderRoll">
<i class="fa-solid fa-trash"></i>
</a>
{{/if}}
</div>
</span>
{{#if roll}}
<div class="roll-data {{#if roll.withHope}}hope{{else if roll.withFear}}fear{{else}}critical{{/if}}">
<div class="duality-label">{{roll.total}} {{localize "DAGGERHEART.GENERAL.withThing" thing=roll.totalLabel}}</div>
<div class="roll-dice-container">
<a class="roll-dice" data-action="rerollLeaderDice" data-dice-type="hope">
<span class="dice-label">{{roll.dHope.total}}</span>
<img src="{{concat "systems/daggerheart/assets/icons/dice/hope/" roll.dHope.denomination ".svg"}}" />
</a>
<span class="roll-operator">+</span>
<a class="roll-dice" data-action="rerollLeaderDice" data-dice-type="fear">
<span class="dice-label">{{roll.dFear.total}}</span>
<img src="{{concat "systems/daggerheart/assets/icons/dice/fear/" roll.dFear.denomination ".svg"}}" />
</a>
{{#if roll.advantage.type}}
<span class="roll-operator">{{#if (eq roll.advantage.type 1)}}+{{else}}-{{/if}}</span>
<span class="roll-dice">
<span class="dice-label">{{roll.advantage.value}}</span>
<img src="{{concat "systems/daggerheart/assets/icons/dice/" (ifThen (eq roll.advantage.type 1) "adv/" "disadv/") roll.advantage.dice ".svg"}}" />
</span>
{{/if}}
<span class="roll-operator">{{#if (gte roll.modifierTotal 0)}}+{{else}}-{{/if}}</span>
<span class="roll-value">{{positive roll.modifierTotal}}</span>
</div>
</div>
{{else}}
<span class="hint">{{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.makeYourRoll"}}</span>
{{/if}}
</div>
{{/if}}
</fieldset>
</div>
{{/with}}
</section>

View file

@ -0,0 +1,15 @@
<section class="group-roll tab {{#if tabs.groupRoll.active}} active{{/if}}" data-group="{{tabs.groupRoll.group}}" data-tab="{{tabs.groupRoll.id}}">
<h1>{{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.title"}}</h1>
<header><span>{{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.members"}}</span></header>
<div class="aiding-members">
{{#each aidKeys as |key|}}
<div data-application-part="{{key}}"></div>
{{/each}}
</div>
<header><span>{{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.leader"}}</span></header>
<div data-application-part="leader"></div>
<header><span>{{localize "DAGGERHEART.GENERAL.result.single"}}</span></header>
<div data-application-part="result"></div>
<div data-application-part="footer"></div>
</section>

View file

@ -1,6 +1,9 @@
<section class="tab {{#if tabs.groupRoll.active}} active{{/if}}" data-group="{{tabs.groupRoll.group}}" data-tab="{{tabs.groupRoll.id}}"> <section class="tab {{#if tabs.groupRoll.active}} active{{/if}}" data-group="{{tabs.groupRoll.group}}" data-tab="{{tabs.groupRoll.id}}">
<div class="finish-container"> <div class="finish-container">
<button type="button" data-action="cancelRoll">{{localize "COMMON.Cancel"}}</button> <button type="button" data-action="cancelRoll">{{localize "COMMON.Cancel"}}</button>
<button type="button" data-action="finishRoll" {{#unless canFinishRoll}}disabled{{/unless}} class="finish-button">{{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.finishGroupRoll"}}</button> <button type="button" data-action="finishRoll" {{#unless canFinishRoll}}disabled{{/unless}} class="finish-button">
<i class="fa-solid fa-dice" inert></i>
{{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.finishGroupRoll"}}
</button>
</div> </div>
</section> </section>

View file

@ -0,0 +1,95 @@
{{#with (ifThen (eq partId "leader") leader (lookup members partId))}}
<section class="member-roll-container {{type}} {{#if @root.allHaveRolled}}select-padding{{/if}} {{#unless isEditable}}inactive{{/unless}}" data-member-key="{{@root.partId}}">
<img class="portrait" src="{{img}}" />
<div class="name-area">
<span class="name">{{name}}</span>
{{#if hasRolled}}
<div class="trait item-tags">
<div class="tag">{{rollChoiceLabel}}</div>
{{#if modifier}}
<span class="tag {{#if (gte modifier 0)}}success{{else}}failure{{/if}}">
{{numberFormat modifier sign=true}}
</span>
{{/if}}
{{#if isEditable}}
<div class="buttons">
<button type="button" data-action="makeRoll" class="plain" data-tooltip="DAGGERHEART.GENERAL.Roll.basic">
<img class="roll-img duality" src="systems/daggerheart/assets/icons/dice/duality/DualityBW.svg" alt="2d12">
</button>
<button
type="button"
data-action="removeRoll"
class="plain icon fa-solid fa-trash"
data-tooltip="DAGGERHEART.APPLICATIONS.GroupRollSelect.removeRoll"
></button>
</div>
{{/if}}
</div>
{{else if readyToRoll}}
<div class="trait">
{{localize "DAGGERHEART.CONFIG.RollTypes.trait.name" }}
<select name="{{basePath}}.rollChoice" {{#if hasRolled}}disabled{{/if}}>
{{selectOptions ../traitOptions selected=rollChoice localize=true}}
</select>
</div>
{{/if}}
</div>
{{#if roll}}
<div class="with-result {{#if roll.withHope}}hope{{else if roll.withFear}}fear{{else}}critical{{/if}}">
<div class="roll-data">
<div class="duality-label">
{{roll.total}}
<span class="with">{{withLabelShort}}</span>
</div>
<div class="roll-dice-container">
<a class="roll-dice" data-action="rerollDice" data-member="{{@root.partId}}" data-dice-type="hope">
<span class="dice-label">{{roll.dHope.total}}</span>
<img src="{{concat "systems/daggerheart/assets/icons/dice/hope/" roll.dHope.denomination ".svg"}}" />
</a>
<a class="roll-dice" data-action="rerollDice" data-member="{{@root.partId}}" data-dice-type="fear">
<span class="dice-label">{{roll.dFear.total}}</span>
<img src="{{concat "systems/daggerheart/assets/icons/dice/fear/" roll.dFear.denomination ".svg"}}" />
</a>
{{#if roll.hasAdvantage}}
<span class="roll-operator">+</span>
<span class="roll-dice">
<span class="dice-label">{{roll.dAdvantage.total}}</span>
<img src="{{concat "systems/daggerheart/assets/icons/dice/adv/d" roll.advantageFaces ".svg"}}" />
</span>
{{/if}}
{{#if roll.hasDisadvantage}}
<span class="roll-operator">-</span>
<span class="roll-dice">
<span class="dice-label">{{roll.dDisadvantage.total}}</span>
<img src="{{concat "systems/daggerheart/assets/icons/dice/disadv/d" roll.advantageFaces ".svg"}}" />
</span>
{{/if}}
<span class="roll-operator ">{{#if (gte roll.modifierTotal 0)}}+{{else}}-{{/if}}</span>
<span class="roll-value">{{positive roll.modifierTotal}}</span>
</div>
</div>
{{#if (and @root.isGM (ne ../partId "leader"))}}
<div class="buttons">
<button
type="button"
data-action="markSuccessful"
data-success="true"
class="plain icon fa-solid fa-check {{#if modifier}}{{#if successful}}active{{else}}inactive{{/if}}{{/if}}"
data-tooltip="DAGGERHEART.GENERAL.success"
></button>
<button
type="button"
data-action="markSuccessful"
class="plain icon fa-solid fa-times {{#if modifier}}{{#if successful}}inactive{{else}}active{{/if}}{{/if}}"
data-tooltip="DAGGERHEART.GENERAL.failure"
></button>
</div>
{{/if}}
</div>
{{else if (and readyToRoll isEditable)}}
<a class="roll-button initial-roll" data-action="makeRoll" data-member="{{@root.partId}}" data-tooltip="DAGGERHEART.GENERAL.Roll.basic">
<img class="roll-img duality" src="systems/daggerheart/assets/icons/dice/duality/DualityBW.svg" alt="2d12">
</a>
{{/if}}
</section>
{{/with}}

View file

@ -0,0 +1,30 @@
{{#if (and hasRolled leader.roll)}}
<div class="group-roll-results with-result {{groupRoll.totalDualityClass}}">
<div class="row leader">
<span class="label">{{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.leaderRoll"}}</span>
<span class="duality-label">
{{leader.roll.total}}
<span class="with">{{localize "DAGGERHEART.GENERAL.withThing" thing=leader.roll.totalLabel}}</span>
</span>
</div>
<div class="row modifiers">
<span class="label">{{localize "DAGGERHEART.GENERAL.Modifier.plural"}}</span>
<div class="item-tags">
{{#each groupRoll.modifiers as |modifier|}}
<span class="tag {{#if (gte modifier 0)}}success{{else}}failure{{/if}}">
{{numberFormat modifier sign=true}}
</span>
{{/each}}
</div>
</div>
<div class="divider"></div>
<div class="row total">
<span class="label">{{localize "DAGGERHEART.GENERAL.total"}}</span>
<span class="duality-label"><span class="value">{{groupRoll.total}}</span> {{groupRoll.totalLabel}}</span>
</div>
</div>
{{else}}
<div class="group-roll-results empty">
{{localize "DAGGERHEART.APPLICATIONS.GroupRollSelect.resultsHint"}}
</div>
{{/if}}

View file

@ -32,7 +32,7 @@
<footer class="form-footer"> <footer class="form-footer">
<button data-action="reset"> <button data-action="reset">
<i class="fa-solid fa-arrow-rotate-left"></i> <i class="fa-solid fa-arrow-rotate-left"></i>
<span>{{localize "SETTINGS.UI.ACTIONS.ResetReset"}}</span> <span>{{localize "SETTINGS.UI.ACTIONS.Reset"}}</span>
</button> </button>
<button data-action="save" > <button data-action="save" >
<i class="fa-solid fa-floppy-disk"></i> <i class="fa-solid fa-floppy-disk"></i>

View file

@ -113,13 +113,11 @@
type='attack' type='attack'
}} }}
{{/if}} {{/if}}
{{#each document.items as |item|}} {{#each equippedItems as |item|}}
{{#if item.system.equipped}}
{{> 'daggerheart.inventory-item-compact' {{> 'daggerheart.inventory-item-compact'
item=item item=item
type=item.type type=item.type
}} }}
{{/if}}
{{/each}} {{/each}}
</ul> </ul>
</div> </div>

View file

@ -66,7 +66,7 @@
{{#if (and combats.length user.isGM)}} {{#if (and combats.length user.isGM)}}
<div class="encounter-battlepoints" data-tooltip="#battlepoints#" data-combat-id="{{combat.id}}"> <div class="encounter-battlepoints" data-tooltip="#battlepoints#" data-combat-id="{{combat.id}}">
{{battlepoints.current}}/{{battlepoints.max}} BP{{#if battlepoints.hasModifierBP}}*{{/if}} {{battlepoints.current}}/{{battlepoints.max}} {{localize "DAGGERHEART.GENERAL.Battlepoints.short"}}{{#if battlepoints.hasModifierBP}}*{{/if}}
</div> </div>
{{/if}} {{/if}}