Compare commits

..

9 commits

Author SHA1 Message Date
WBHarry
3527fd7959 Raised version
Some checks are pending
Project CI / build (24.x) (push) Waiting to run
2026-06-05 12:31:30 +02:00
Carlos Fernandez
f0a7539018
Update README.md (#1976) 2026-06-05 12:25:44 +02:00
Carlos Fernandez
5be79f4ab8
Fix several issues with inline damage (#1973)
Some checks are pending
Project CI / build (24.x) (push) Waiting to run
2026-06-05 11:33:20 +02:00
Carlos Fernandez
2fc5b01f09
Fix rerolling when hope/fear automation is enabled (#1972) 2026-06-05 11:31:01 +02:00
WBHarry
52b81de11f
Fixed so that the saved data for an experience that is in the character data is used over that in the levelup data if available (#1971)
Some checks are pending
Project CI / build (24.x) (push) Waiting to run
2026-06-04 18:30:41 -04:00
Carlos Fernandez
c0c9095847
[Fix] Preload ancestry and community features in description (#1967)
* Preload ancestry and community features in description

* Corrected comments

---------

Co-authored-by: WBHarry <williambjrklund@gmail.com>
2026-06-04 20:08:40 +02:00
Carlos Fernandez
5ac4fc3b9c
[Fix] visual quirk with blur in unfocused countdown (#1970)
Some checks are pending
Project CI / build (24.x) (push) Waiting to run
* Fix visual quirk with blur in unfocused countdown

* Snuck in fixes and refactors
2026-06-04 11:42:17 +02:00
Carlos Fernandez
6747be49b2
Allow removing empty string domains (#1968) 2026-06-04 11:15:41 +02:00
WBHarry
77c5cfcbb7 Fixed so that actions on homebrew downtime/items don't crash due to note having metadata
Some checks are pending
Project CI / build (24.x) (push) Waiting to run
2026-06-03 21:46:23 +02:00
17 changed files with 57 additions and 37 deletions

View file

@ -66,6 +66,10 @@ You can find the documentation here: https://github.com/Foundryborne/daggerheart
Looking to contribute to the project? Look no further, check out our [contributing guide](CONTRIBUTING.md), and keep the [Code of Conduct](coc.md) in mind when working on things.
## AI Policy
The Foundryborne Daggerheart system does not make use of AI (generative or otherwise) for any area of its implementation. We expect all contributors to follow this same policy when contributing with a pull request; contributions made using AI will be rejected outright.
## Disclaimer:
**Daggerheart System**

View file

@ -358,14 +358,14 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
const experienceIncreaseTagify = htmlElement.querySelector('.levelup-experience-increases');
if (experienceIncreaseTagify) {
const allExperiences = {
...this.actor.system.experiences,
...Object.values(this.levelup.levels).reduce((acc, level) => {
for (const key of Object.keys(level.achievements.experiences)) {
acc[key] = level.achievements.experiences[key];
}
return acc;
}, {})
}, {}),
...this.actor.system.experiences
};
tagifyElement(
experienceIncreaseTagify,

View file

@ -111,7 +111,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
switch (partId) {
case 'domains':
const selectedDomain = this.selected.domain ? this.settings.domains[this.selected.domain] : null;
const selectedDomain = this.settings.domains[this.selected.domain] ?? null;
const enrichedDescription = selectedDomain
? await foundry.applications.ux.TextEditor.implementation.enrichHTML(selectedDomain.description)
: null;

View file

@ -204,7 +204,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
};
}
if (this.action.parent.metadata.isInventoryItem) {
if (this.action.parent.metadata?.isInventoryItem) {
options.quantity = {
label: 'DAGGERHEART.GENERAL.itemQuantity',
group: 'Global'

View file

@ -31,9 +31,9 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
minimizable: false
},
actions: {
toggleViewMode: DhCountdowns.#toggleViewMode,
editCountdowns: DhCountdowns.#editCountdowns,
loopCountdown: DhCountdowns.#loopCountdown,
toggleViewMode: DhCountdowns.#onToggleViewMode,
editCountdowns: DhCountdowns.#onEditCountdowns,
loopCountdown: DhCountdowns.#onLoopCountdown,
decreaseCountdown: (_, target) => this.editCountdown(false, target),
increaseCountdown: (_, target) => this.editCountdown(true, target)
},
@ -147,7 +147,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
return true;
}
static async #toggleViewMode() {
static async #onToggleViewMode() {
const currentMode = game.user.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.userFlags.countdownMode);
const appMode = CONFIG.DH.GENERAL.countdownAppMode;
const newMode = currentMode === appMode.textIcon ? appMode.iconOnly : appMode.textIcon;
@ -158,15 +158,16 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
this.render();
}
static async #editCountdowns() {
static async #onEditCountdowns() {
new game.system.api.applications.ui.CountdownEdit().render(true);
}
static async #loopCountdown(_, target) {
static async #onLoopCountdown(_, target) {
if (!DhCountdowns.canPerformEdit()) return;
const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns);
const countdown = settings.countdowns[target.id];
const countdownId = target.closest('[data-countdown]').dataset.countdown;
const countdown = settings.countdowns[countdownId];
let progressMax = countdown.progress.start;
let message = null;
@ -185,7 +186,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
await waitForDiceSoNice(message);
await settings.updateSource({
[`countdowns.${target.id}.progress`]: {
[`countdowns.${countdownId}.progress`]: {
current: newMax,
start: newMax
}
@ -199,11 +200,12 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
if (!DhCountdowns.canPerformEdit()) return;
const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns);
const countdown = settings.countdowns[target.id];
const countdownId = target.closest('[data-countdown]').dataset.countdown;
const countdown = settings.countdowns[countdownId];
const newCurrent = increase
? Math.min(countdown.progress.current + 1, countdown.progress.start)
: Math.max(countdown.progress.current - 1, 0);
await settings.updateSource({ [`countdowns.${target.id}.progress.current`]: newCurrent });
await settings.updateSource({ [`countdowns.${countdownId}.progress.current`]: newCurrent });
await emitGMUpdate(GMUpdateEvent.UpdateCountdowns, DhCountdowns.gmSetSetting.bind(settings), settings, null, {
refreshType: RefreshType.Countdown
});

View file

@ -1,5 +1,6 @@
import { calculateExpectedValue, parseTermsFromSimpleFormula } from '../../helpers/utils.mjs';
import { adversaryExpectedDamage, adversaryScalingData } from '../../config/actorConfig.mjs';
import { parseInlineParams } from '../../enrichers/parser.mjs';
export function getTierAdjustedAdversary(source, tier) {
const currentTier = source.tier ?? 1;
@ -60,8 +61,8 @@ export function getTierAdjustedAdversary(source, tier) {
const descriptionFormulas = [];
for (const withDescription of [item.system, ...Object.values(item.system.actions)]) {
withDescription.description = withDescription.description.replace(damageRegex, (match, inner) => {
const { value: formula } = parseInlineParams(inner);
if (!formula || !type) return match;
const { value: formula } = parseInlineParams(inner, { first: 'value' });
if (!formula) return match;
try {
const newFormula = calculateAdjustedDamage(formula, 'action', damageMeta)?.formula;

View file

@ -1,6 +1,6 @@
import BaseDataItem from './base.mjs';
import ItemLinkFields from '../../data/fields/itemLinkFields.mjs';
import { getFeaturesHTMLData } from '../../helpers/utils.mjs';
import { fromUuids, getFeaturesHTMLData } from '../../helpers/utils.mjs';
export default class DHAncestry extends BaseDataItem {
/** @inheritDoc */
@ -45,6 +45,10 @@ export default class DHAncestry extends BaseDataItem {
/**@inheritdoc */
async getDescriptionData() {
// Preload all ancestry features for acquisition from the cache
// todo: make feature acquisition async and replace feature helpers for methods
await fromUuids(this._source.features.map(f => f.item));
const baseDescription = this.description;
const features = await getFeaturesHTMLData(this.features);

View file

@ -1,4 +1,4 @@
import { getFeaturesHTMLData } from '../../helpers/utils.mjs';
import { fromUuids, getFeaturesHTMLData } from '../../helpers/utils.mjs';
import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs';
import BaseDataItem from './base.mjs';
@ -27,6 +27,10 @@ export default class DHCommunity extends BaseDataItem {
/**@inheritdoc */
async getDescriptionData() {
// Preload all community features for acquisition from the cache
// todo: make feature acquisition async and replace feature helpers for methods
await fromUuids(this._source.features);
const baseDescription = this.description;
const features = await getFeaturesHTMLData(this.features);

View file

@ -91,7 +91,7 @@ export default class DHSubclass extends BaseDataItem {
? game.i18n.localize(CONFIG.DH.ACTOR.abilities[this.spellcastingTrait].label)
: null;
// Preload all class features for acquisition from the cache
// Preload all subclass features for acquisition from the cache
// todo: make feature acquisition async and replace feature helpers for methods
await fromUuids(this._source.features.map(f => f.item));

View file

@ -37,6 +37,7 @@ export default class DHRoll extends Roll {
static async buildConfigure(config = {}, message = {}) {
config.hooks = [...this.getHooks(), ''];
config.dialog ??= {};
config.damageOptions ??= {};
for (const hook of config.hooks) {
if (Hooks.call(`${CONFIG.DH.id}.preRoll${hook.capitalize()}`, config, message) === false) return null;

View file

@ -1,3 +1,5 @@
import { ResourceUpdateMap } from '../data/action/baseAction.mjs';
export function updateResourcesForDualityReroll(oldDuality, newDuality, actor) {
const { hopeFear } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation);
if (game.user.isGM ? !hopeFear.gm : !hopeFear.players) return;

View file

@ -1,7 +1,7 @@
import { parseInlineParams } from './parser.mjs';
export default function DhDamageEnricher(match, _options) {
const { value, type, inline } = parseInlineParams(match[1]);
const { value, type, inline } = parseInlineParams(match[1], { first: 'value' });
if (!value || !type) return match[0];
return getDamageMessage(value, type, inline, match[0]);
}
@ -59,7 +59,7 @@ export const renderDamageButton = async event => {
{
formula: value,
applyTo: CONFIG.DH.GENERAL.healingTypes.hitPoints.id,
type: type
damageTypes: type
}
]
};

View file

@ -8,7 +8,7 @@ export function parseInlineParams(paramString, { first } = {}) {
const parts = paramString.split('|').map(x => x.trim());
const params = {};
for (const [idx, param] of parts.entries()) {
if (first && idx === 0) {
if (first && idx === 0 && !param.includes(':')) {
params[first] = param;
} else {
const parts = param.split(':');

View file

@ -879,6 +879,7 @@ export async function fromUuids(uuids) {
const packEmbeddedEntries = entries.filter(
e =>
!(e.value instanceof Document) &&
e.parsed &&
e.parsed.collection instanceof foundry.documents.collections.CompendiumCollection &&
e.parsed.embedded.length > 0
);
@ -895,7 +896,7 @@ export async function fromUuids(uuids) {
const pack = game.packs.get(packGroup[0].value.pack);
if (!pack) continue;
const ids = packGroup.map(p => p.parsed.id);
const ids = packGroup.map(p => p.parsed?.id).filter(id => !!id);
const documents = await pack.getDocuments({ _id__in: ids });
for (const p of packGroup) {
p.value = documents.find(d => d.id === p.parsed.id) ?? p.value;

View file

@ -18,7 +18,7 @@
border: 0;
box-shadow: none;
color: @color-text-primary;
width: 300px;
width: 18.75rem;
pointer-events: all;
align-self: flex-end;
transition: 0.3s right ease-in-out;
@ -36,7 +36,7 @@
transition: opacity var(--ui-fade-duration);
}
:not(.performance-low, .noblur) {
&:not(.performance-low, .noblur) {
backdrop-filter: blur(5px);
}
@ -49,8 +49,7 @@
}
&.icon-only {
width: 180px;
min-width: 180px;
width: 12rem;
}
.countdowns-header,
@ -108,8 +107,8 @@
gap: 16px;
img {
width: 44px;
height: 44px;
width: 2.75rem;
height: 2.75rem;
border-radius: 6px;
}
@ -127,7 +126,7 @@
.countdown-tool-controls {
display: flex;
align-items: center;
gap: 16px;
gap: var(--spacer-12);
}
.progress-tag {

View file

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

View file

@ -11,18 +11,20 @@
</header>
<div class="countdowns-container">
{{#each countdowns as | countdown id |}}
<div class="countdown-container {{#if ../iconOnly}}icon-only{{/if}}">
<div class="countdown-container {{#if ../iconOnly}}icon-only{{/if}}" data-countdown="{{id}}">
<div class="countdown-main-container">
<img src="{{countdown.img}}" {{#if ../iconOnly}}data-tooltip="{{countdown.name}}"{{/if}}/>
<div class="countdown-content">
{{#unless ../iconOnly}}<label>{{countdown.name}}</label>{{/unless}}
{{#unless ../iconOnly}}
<header>{{countdown.name}}</header>
{{/unless}}
<div class="countdown-tools">
<div class="countdown-tool-controls">
{{#if countdown.editable}}<a data-action="decreaseCountdown" id="{{id}}"><i class="fa-solid fa-minus"></i></a>{{/if}}
{{#if countdown.editable}}<a data-action="decreaseCountdown"><i class="fa-solid fa-minus"></i></a>{{/if}}
<div class="progress-tag">
{{countdown.progress.current}}/{{countdown.progress.start}}
</div>
{{#if countdown.editable}}<a data-action="increaseCountdown" id="{{id}}"><i class="fa-solid fa-plus"></i></a>{{/if}}
{{#if countdown.editable}}<a data-action="increaseCountdown"><i class="fa-solid fa-plus"></i></a>{{/if}}
</div>
<div class="countdown-tool-icons">
{{#if (not ../iconOnly)}}
@ -31,7 +33,7 @@
{{/if}}
{{#unless (eq countdown.progress.looping "noLooping")}}
<span data-tooltip="{{countdown.loopTooltip}}">
<a class="looping-container {{#if countdown.shouldLoop}}should-loop{{/if}}" {{#if countdown.loopDisabled}}disabled{{/if}} data-action="loopCountdown" id="{{id}}">
<a class="looping-container {{#if countdown.shouldLoop}}should-loop{{/if}}" {{#if countdown.loopDisabled}}disabled{{/if}} data-action="loopCountdown">
<i class="loop-marker fa-solid fa-repeat"></i>
{{#if (eq countdown.progress.looping "increasing")}}
<i class="direction-marker fa-solid fa-angles-up" data-tooltip="{{localize "DAGGERHEART.UI.Countdowns.increasingLoop"}}"></i>