Merge branch 'main' into feature/group-roll-rework

This commit is contained in:
WBHarry 2026-04-05 11:30:24 +02:00
commit eaa282a0ea
14 changed files with 96 additions and 24 deletions

View file

@ -366,8 +366,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
let rollIsSelected = false; let rollIsSelected = false;
for (const member of Object.values(members)) { for (const member of Object.values(members)) {
const rollFinished = Boolean(member.rollData); const rollFinished = Boolean(member.rollData);
const damageFinished = const damageFinished = member.rollData?.options?.hasDamage ? Boolean(member.rollData.options.damage) : true;
member.rollData?.options?.hasDamage !== undefined ? member.rollData.options.damage : true;
rollsAreFinished = rollsAreFinished && rollFinished && damageFinished; rollsAreFinished = rollsAreFinished && rollFinished && damageFinished;
rollIsSelected = rollIsSelected || member.selected; rollIsSelected = rollIsSelected || member.selected;

View file

@ -484,7 +484,7 @@ export const defaultRestOptions = {
value: { value: {
custom: { custom: {
enabled: true, enabled: true,
formula: '@system.armorScore' formula: '@system.armorScore.max'
} }
} }
} }

View file

@ -44,7 +44,8 @@ export default class ArmorChange extends foundry.abstract.DataModel {
label: 'Armor', label: 'Armor',
defaultPriority: 20, defaultPriority: 20,
handler: (actor, change, _options, _field, replacementData) => { handler: (actor, change, _options, _field, replacementData) => {
const parsedMax = itemAbleRollParse(change.value.max, actor, change.effect.parent); const baseParsedMax = itemAbleRollParse(change.value.max, actor, change.effect.parent);
const parsedMax = new Roll(baseParsedMax).evaluateSync().total;
game.system.api.documents.DhActiveEffect.applyChange( game.system.api.documents.DhActiveEffect.applyChange(
actor, actor,
{ {
@ -110,6 +111,8 @@ export default class ArmorChange extends foundry.abstract.DataModel {
}; };
get isSuppressed() { get isSuppressed() {
if (!this.parent.parent?.actor) return false;
switch (this.value.interaction) { switch (this.value.interaction) {
case CONFIG.DH.GENERAL.activeEffectArmorInteraction.active.id: case CONFIG.DH.GENERAL.activeEffectArmorInteraction.active.id:
return !this.parent.parent?.actor.system.armor; return !this.parent.parent?.actor.system.armor;

View file

@ -246,7 +246,7 @@ export default class DHRoll extends Roll {
return (this._formula = this.constructor.getFormula(this.terms)); return (this._formula = this.constructor.getFormula(this.terms));
} }
/** /**
* Calculate total modifiers of any rolls, including non-dh rolls. * Calculate total modifiers of any rolls, including non-dh rolls.
* This exists because damage rolls still may receive base roll classes * This exists because damage rolls still may receive base roll classes
*/ */
@ -256,7 +256,7 @@ export default class DHRoll extends Roll {
if (!roll.terms[i].isDeterministic) continue; if (!roll.terms[i].isDeterministic) continue;
const termTotal = roll.terms[i].total; const termTotal = roll.terms[i].total;
if (typeof termTotal === 'number') { if (typeof termTotal === 'number') {
const multiplier = roll.terms[i - 1]?.operator === " - " ? -1 : 1; const multiplier = roll.terms[i - 1]?.operator === ' - ' ? -1 : 1;
modifierTotal += multiplier * termTotal; modifierTotal += multiplier * termTotal;
} }
} }
@ -272,7 +272,7 @@ export default class DHRoll extends Roll {
const changeKeys = this.getActionChangeKeys(); const changeKeys = this.getActionChangeKeys();
return ( return (
this.options.effects?.reduce((acc, effect) => { this.options.effects?.reduce((acc, effect) => {
if (effect.system.changes.some(x => changeKeys.some(key => x.key.includes(key)))) { if (effect.system.changes.some(x => changeKeys.some(key => x.key?.includes(key)))) {
acc[effect.id] = { acc[effect.id] = {
id: effect.id, id: effect.id,
name: effect.name, name: effect.name,

View file

@ -1,9 +1,13 @@
import DualityDie from './dualityDie.mjs'; import DualityDie from './dualityDie.mjs';
import HopeDie from './hopeDie.mjs';
import FearDie from './fearDie.mjs';
import AdvantageDie from './advantageDie.mjs'; import AdvantageDie from './advantageDie.mjs';
import DisadvantageDie from './disadvantageDie.mjs'; import DisadvantageDie from './disadvantageDie.mjs';
export const diceTypes = { export const diceTypes = {
DualityDie, DualityDie,
HopeDie,
FearDie,
AdvantageDie, AdvantageDie,
DisadvantageDie DisadvantageDie
}; };

View file

@ -43,9 +43,10 @@ export default class DualityDie extends foundry.dice.terms.Die {
options: { appearance: {} } options: { appearance: {} }
}; };
const preset = await getDiceSoNicePreset(diceSoNice[key], faces); const diceAppearance = await this.getDiceSoNiceAppearance(options.liveRoll.roll);
diceSoNiceRoll.dice[0].options.appearance = preset.appearance; diceSoNiceRoll.dice[0].options.appearance = diceAppearance.appearance;
diceSoNiceRoll.dice[0].options.modelFile = preset.modelFile; diceSoNiceRoll.dice[0].options.modelFile = diceAppearance.modelFile;
diceSoNiceRoll.dice[0].results = diceSoNiceRoll.dice[0].results.filter(x => x.active);
await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true); await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true);
} else { } else {
@ -59,4 +60,11 @@ export default class DualityDie extends foundry.dice.terms.Die {
this.#updateResources(oldDuality, newDuality, options.liveRoll.actor); this.#updateResources(oldDuality, newDuality, options.liveRoll.actor);
} }
} }
/**
* Overridden by extending classes HopeDie and FearDie
*/
async getDiceSoNiceAppearance() {
return {};
}
} }

View file

@ -0,0 +1,9 @@
import { getDiceSoNicePresets } from '../../config/generalConfig.mjs';
import DualityDie from './dualityDie.mjs';
export default class FearDie extends DualityDie {
async getDiceSoNiceAppearance(roll) {
const { fear } = await getDiceSoNicePresets(roll, this.denomination, this.denomination);
return fear;
}
}

View file

@ -0,0 +1,9 @@
import { getDiceSoNicePresets } from '../../config/generalConfig.mjs';
import DualityDie from './dualityDie.mjs';
export default class HopeDie extends DualityDie {
async getDiceSoNiceAppearance(roll) {
const { hope } = await getDiceSoNicePresets(roll, this.denomination, this.denomination);
return hope;
}
}

View file

@ -24,7 +24,7 @@ export default class DualityRoll extends D20Roll {
} }
get dHope() { get dHope() {
if (!(this.dice[0] instanceof game.system.api.dice.diceTypes.DualityDie)) this.createBaseDice(); if (!(this.dice[0] instanceof game.system.api.dice.diceTypes.HopeDie)) this.createBaseDice();
return this.dice[0]; return this.dice[0];
} }
@ -34,7 +34,7 @@ export default class DualityRoll extends D20Roll {
} }
get dFear() { get dFear() {
if (!(this.dice[1] instanceof game.system.api.dice.diceTypes.DualityDie)) this.createBaseDice(); if (!(this.dice[1] instanceof game.system.api.dice.diceTypes.FearDie)) this.createBaseDice();
return this.dice[1]; return this.dice[1];
} }
@ -68,8 +68,8 @@ export default class DualityRoll extends D20Roll {
} }
get extraDice() { get extraDice() {
const { DualityDie, AdvantageDie, DisadvantageDie } = game.system.api.dice.diceTypes; const { HopeDie, FearDie, AdvantageDie, DisadvantageDie } = game.system.api.dice.diceTypes;
return this.dice.filter(x => ![DualityDie, AdvantageDie, DisadvantageDie].some(die => x instanceof die)); return this.dice.filter(x => ![HopeDie, FearDie, AdvantageDie, DisadvantageDie].some(die => x instanceof die));
} }
setRallyChoices() { setRallyChoices() {
@ -125,8 +125,8 @@ export default class DualityRoll extends D20Roll {
/** @inheritDoc */ /** @inheritDoc */
static fromData(data) { static fromData(data) {
data.terms[0].class = 'DualityDie'; data.terms[0].class = 'HopeDie';
data.terms[2].class = 'DualityDie'; data.terms[2].class = 'FearDie';
if (data.options.roll.advantage?.type && data.terms[4]?.faces) { if (data.options.roll.advantage?.type && data.terms[4]?.faces) {
data.terms[4].class = data.options.roll.advantage.type === 1 ? 'AdvantageDie' : 'DisadvantageDie'; data.terms[4].class = data.options.roll.advantage.type === 1 ? 'AdvantageDie' : 'DisadvantageDie';
} }
@ -135,18 +135,18 @@ export default class DualityRoll extends D20Roll {
createBaseDice() { createBaseDice() {
if ( if (
this.dice[0] instanceof game.system.api.dice.diceTypes.DualityDie && this.dice[0] instanceof game.system.api.dice.diceTypes.HopeDie &&
this.dice[1] instanceof game.system.api.dice.diceTypes.DualityDie this.dice[1] instanceof game.system.api.dice.diceTypes.FearDie
) { ) {
this.terms = [this.terms[0], this.terms[1], this.terms[2]]; this.terms = [this.terms[0], this.terms[1], this.terms[2]];
return; return;
} }
this.terms[0] = new game.system.api.dice.diceTypes.DualityDie({ this.terms[0] = new game.system.api.dice.diceTypes.HopeDie({
faces: this.data.rules.dualityRoll?.defaultHopeDice ?? 12 faces: this.data.rules.dualityRoll?.defaultHopeDice ?? 12
}); });
this.terms[1] = new foundry.dice.terms.OperatorTerm({ operator: '+' }); this.terms[1] = new foundry.dice.terms.OperatorTerm({ operator: '+' });
this.terms[2] = new game.system.api.dice.diceTypes.DualityDie({ this.terms[2] = new game.system.api.dice.diceTypes.FearDie({
faces: this.data.rules.dualityRoll?.defaultFearDice ?? 12 faces: this.data.rules.dualityRoll?.defaultFearDice ?? 12
}); });
} }

View file

@ -1,3 +1,4 @@
import { defaultRestOptions } from '../config/generalConfig.mjs';
import { RefreshType, socketEvent } from './socket.mjs'; import { RefreshType, socketEvent } from './socket.mjs';
export async function runMigrations() { export async function runMigrations() {
@ -341,6 +342,18 @@ export async function runMigrations() {
lastMigrationVersion = '2.0.0'; lastMigrationVersion = '2.0.0';
} }
if (foundry.utils.isNewerVersion('2.0.4', lastMigrationVersion)) {
const downtimeMoves = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew);
if (restMoves.longRest.moves.repairArmor) {
await downtimeMoves.updateSource({
'restMoves.longRest.moves.repairArmor': defaultRestOptions.longRest().repairArmor
});
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, downtimeMoves.toObject());
}
lastMigrationVersion = '2.0.4';
}
//#endregion //#endregion
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, lastMigrationVersion); await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, lastMigrationVersion);

View file

@ -4,6 +4,7 @@
.application.daggerheart { .application.daggerheart {
prose-mirror { prose-mirror {
height: 100% !important; height: 100% !important;
width: 100%;
.editor-menu { .editor-menu {
background-color: transparent; background-color: transparent;

View file

@ -155,6 +155,26 @@ body.game:is(.performance-low, .noblur) {
} }
} }
.stat-section {
position: relative;
display: flex;
gap: 10px;
background-color: light-dark(transparent, @dark-blue);
color: light-dark(@dark-blue, @golden);
padding: 5px 10px;
border: 1px solid light-dark(@dark-blue, @golden);
border-radius: 3px;
align-items: center;
width: fit-content;
h4 {
font-size: var(--font-size-12);
font-weight: bold;
text-transform: uppercase;
color: light-dark(@dark-blue, @golden);
}
}
.threshold-section { .threshold-section {
display: flex; display: flex;
align-self: center; align-self: center;

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.0.1", "version": "2.0.3",
"compatibility": { "compatibility": {
"minimum": "14.359", "minimum": "14.359",
"verified": "14.359", "verified": "14.359",
@ -296,7 +296,7 @@
"background": "systems/daggerheart/assets/logos/FoundrybornBackgroundLogo.png", "background": "systems/daggerheart/assets/logos/FoundrybornBackgroundLogo.png",
"primaryTokenAttribute": "resources.hitPoints", "primaryTokenAttribute": "resources.hitPoints",
"secondaryTokenAttribute": "resources.stress", "secondaryTokenAttribute": "resources.stress",
"url": "https://your/hosted/system/repo/", "url": "https://github.com/Foundryborne/daggerheart",
"manifest": "https://your/hosted/system/repo/system.json", "manifest": "https://raw.githubusercontent.com/Foundryborne/daggerheart/main/system.json",
"download": "https://your/packaged/download/archive.zip" "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.0.3/system.zip"
} }

View file

@ -87,6 +87,12 @@
</div> </div>
{{/unless}} {{/unless}}
{{#if (eq actor.type 'character')}}
<div class="stat-section">
<h4>{{localize "DAGGERHEART.GENERAL.evasion"}}: {{actor.system.evasion}}</h4>
</div>
{{/if}}
{{#unless (eq actor.type 'companion')}} {{#unless (eq actor.type 'companion')}}
<div class="threshold-section"> <div class="threshold-section">
<h4 class="threshold-label">{{localize "DAGGERHEART.GENERAL.DamageThresholds.minor"}}</h4> <h4 class="threshold-label">{{localize "DAGGERHEART.GENERAL.DamageThresholds.minor"}}</h4>