Merge branch 'main' into release

This commit is contained in:
WBHarry 2025-12-13 23:06:23 +01:00
commit c7aed6825a
50 changed files with 560 additions and 153 deletions

View file

@ -1801,6 +1801,7 @@
"plural": "Costs" "plural": "Costs"
}, },
"Damage": { "Damage": {
"massive": "Massive",
"severe": "Severe", "severe": "Severe",
"major": "Major", "major": "Major",
"minor": "Minor", "minor": "Minor",
@ -2440,9 +2441,12 @@
}, },
"currency": { "currency": {
"title": "Currency Overrides", "title": "Currency Overrides",
"changeIcon": "Change Currency Icon",
"currencyName": "Currency Name", "currencyName": "Currency Name",
"coinName": "Coin Name", "coinName": "Coin Name",
"handfulName": "Handful Name", "handfulName": "Handful Name",
"iconName": "Icon Name",
"iconNameHint": "Icons are from fontawesome",
"bagName": "Bag Name", "bagName": "Bag Name",
"chestName": "Chest Name" "chestName": "Chest Name"
}, },
@ -2504,6 +2508,11 @@
"hint": "Apply variant rules from the Daggerheart system", "hint": "Apply variant rules from the Daggerheart system",
"name": "Variant Rules", "name": "Variant Rules",
"actionTokens": "Action Tokens" "actionTokens": "Action Tokens"
},
"SpotlightRequestQueue": {
"name": "Spotlight Request Queue",
"label": "Spotlight Request Queue",
"hint": "Adds more structure to spotlight requests by ordering them from oldest to newest"
} }
}, },
"Resources": { "Resources": {
@ -2517,6 +2526,10 @@
"actionTokens": { "actionTokens": {
"enabled": { "label": "Enabled" }, "enabled": { "label": "Enabled" },
"tokens": { "label": "Tokens" } "tokens": { "label": "Tokens" }
},
"massiveDamage":{
"title":"Massive Damage",
"enabled": { "label": "Enabled" }
} }
} }
}, },

View file

@ -197,6 +197,8 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD {
for (const effect of activeEffects) { for (const effect of activeEffects) {
for (const statusId of effect.statuses) { for (const statusId of effect.statuses) {
const status = choices[statusId]; const status = choices[statusId];
if (!status) continue;
status.instances = 1 + (status.instances ?? 0); status.instances = 1 + (status.instances ?? 0);
status.locked = status.locked || effect.condition || status.instances > 1; status.locked = status.locked || effect.condition || status.instances > 1;
if (!status) continue; if (!status) continue;

View file

@ -32,6 +32,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
icon: 'fa-solid fa-gears' icon: 'fa-solid fa-gears'
}, },
actions: { actions: {
editCurrencyIcon: this.changeCurrencyIcon,
addItem: this.addItem, addItem: this.addItem,
editItem: this.editItem, editItem: this.editItem,
removeItem: this.removeItem, removeItem: this.removeItem,
@ -115,6 +116,45 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
this.render(); this.render();
} }
static async changeCurrencyIcon(_, target) {
const type = target.dataset.currency;
const currentIcon = this.settings.currency[type].icon;
const icon = await foundry.applications.api.DialogV2.input({
classes: ['daggerheart', 'dh-style', 'change-currency-icon'],
content: await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/settings/homebrew-settings/change-currency-icon.hbs',
{ currentIcon }
),
window: {
title: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.currency.changeIcon'),
icon: 'fa-solid fa-coins'
},
render: (_, dialog) => {
const icon = dialog.element.querySelector('.displayed-icon i');
const input = dialog.element.querySelector('input');
const reset = dialog.element.querySelector('button[data-action=reset]');
input.addEventListener('input', () => {
icon.classList.value = input.value;
});
reset.addEventListener('click', () => {
const currencyField = DhHomebrew.schema.fields.currency.fields[type];
const initial = currencyField.fields.icon.getInitialValue();
input.value = icon.classList.value = initial;
});
},
ok: {
callback: (_, button) => button.form.elements.icon.value
}
});
if (icon !== null) {
await this.settings.updateSource({
[`currency.${type}.icon`]: icon
});
this.render();
}
}
static async addItem(_, target) { static async addItem(_, target) {
const { type } = target.dataset; const { type } = target.dataset;
if (['shortRest', 'longRest'].includes(type)) { if (['shortRest', 'longRest'].includes(type)) {

View file

@ -685,8 +685,6 @@ export default class CharacterSheet extends DHBaseActorSheet {
ability: abilityLabel ability: abilityLabel
}) })
}); });
if (result) game.system.api.fields.ActionFields.CostField.execute.call(this, result);
} }
//TODO: redo toggleEquipItem method //TODO: redo toggleEquipItem method

View file

@ -178,6 +178,60 @@ export default function DHApplicationMixin(Base) {
_attachPartListeners(partId, htmlElement, options) { _attachPartListeners(partId, htmlElement, options) {
super._attachPartListeners(partId, htmlElement, options); super._attachPartListeners(partId, htmlElement, options);
this._dragDrop.forEach(d => d.bind(htmlElement)); this._dragDrop.forEach(d => d.bind(htmlElement));
for (const deltaInput of htmlElement.querySelectorAll('input[data-allow-delta]')) {
deltaInput.dataset.numValue = deltaInput.value;
deltaInput.inputMode = 'numeric';
deltaInput.pattern = '^[+=\\-]?\d*';
const handleUpdate = (delta = 0) => {
const min = Number(deltaInput.min) || 0;
const max = Number(deltaInput.max) || Infinity;
const current = Number(deltaInput.dataset.numValue);
const rawNumber = Number(deltaInput.value);
if (Number.isNaN(rawNumber)) {
deltaInput.value = delta ? Math.clamp(current + delta, min, max) : current;
return;
}
const newValue =
deltaInput.value.startsWith('+') || deltaInput.value.startsWith('-')
? Math.clamp(current + rawNumber + delta, min, max)
: Math.clamp(rawNumber + delta, min, max);
deltaInput.value = deltaInput.dataset.numValue = newValue;
};
// Force valid characters while inputting
deltaInput.addEventListener('input', () => {
deltaInput.value = /[+=\-]?\d*/.exec(deltaInput.value)?.at(0) ?? deltaInput.value;
});
// Recreate Keyup/Keydown support
deltaInput.addEventListener('keydown', event => {
const step = event.key === 'ArrowUp' ? 1 : event.key === 'ArrowDown' ? -1 : 0;
if (step !== 0) {
handleUpdate(step);
deltaInput.dispatchEvent(new Event("change", { bubbles: true }));
}
});
// Mousewheel while focused support
deltaInput.addEventListener(
'wheel',
event => {
if (deltaInput === document.activeElement) {
event.preventDefault();
handleUpdate(Math.sign(-1 * event.deltaY));
deltaInput.dispatchEvent(new Event("change", { bubbles: true }));
}
},
{ passive: false }
);
deltaInput.addEventListener('change', () => {
handleUpdate();
});
}
} }
/**@inheritdoc */ /**@inheritdoc */

View file

@ -245,7 +245,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
}); });
if (!result) return; if (!result) return;
await game.system.api.fields.ActionFields.CostField.execute.call({ actor }, result);
const newMessageData = foundry.utils.deepClone(message.system); const newMessageData = foundry.utils.deepClone(message.system);
foundry.utils.setProperty(newMessageData, `${path}.result`, result.roll); foundry.utils.setProperty(newMessageData, `${path}.result`, result.roll);

View file

@ -42,13 +42,13 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
const modifierBP = const modifierBP =
this.combats this.combats
.find(x => x.active) .find(x => x.active)
?.system?.extendedBattleToggles?.reduce((acc, toggle) => acc + toggle.category, 0) ?? 0; ?.system?.extendedBattleToggles?.reduce((acc, toggle) => (acc ?? 0) + toggle.category, null) ?? null;
const maxBP = CONFIG.DH.ENCOUNTER.BaseBPPerEncounter(context.characters.length) + modifierBP; const maxBP = CONFIG.DH.ENCOUNTER.BaseBPPerEncounter(context.characters.length) + modifierBP;
const currentBP = AdversaryBPPerEncounter(context.adversaries, context.characters); const currentBP = AdversaryBPPerEncounter(context.adversaries, context.characters);
Object.assign(context, { Object.assign(context, {
fear: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear), fear: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear),
battlepoints: { max: maxBP, current: currentBP, hasModifierBP: Boolean(modifierBP) } battlepoints: { max: maxBP, current: currentBP, hasModifierBP: modifierBP !== null }
}); });
} }
@ -57,21 +57,21 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
const adversaries = context.turns?.filter(x => x.isNPC) ?? []; const adversaries = context.turns?.filter(x => x.isNPC) ?? [];
const characters = context.turns?.filter(x => !x.isNPC) ?? []; const characters = context.turns?.filter(x => !x.isNPC) ?? [];
const spotlightQueueEnabled = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.SpotlightRequestQueue);
const spotlightRequests = characters const spotlightRequests = characters
?.filter(x => !x.isNPC) ?.filter(x => !x.isNPC && spotlightQueueEnabled)
.filter(x => x.system.spotlight.requestOrderIndex > 0) .filter(x => x.system.spotlight.requestOrderIndex > 0)
.sort((a, b) => { .sort((a, b) => {
const valueA = a.system.spotlight.requestOrderIndex; const valueA = a.system.spotlight.requestOrderIndex;
const valueB = b.system.spotlight.requestOrderIndex; const valueB = b.system.spotlight.requestOrderIndex;
return valueA - valueB; return valueA - valueB;
}); });
Object.assign(context, { Object.assign(context, {
actionTokens: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).actionTokens, actionTokens: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).actionTokens,
adversaries, adversaries,
characters: characters?.filter(x => !x.isNPC).filter(x => x.system.spotlight.requestOrderIndex == 0), characters: characters?.filter(x => !x.isNPC).filter(x => !spotlightQueueEnabled || x.system.spotlight.requestOrderIndex == 0),
spotlightRequests spotlightRequests
}); });
} }
@ -161,9 +161,11 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
if (this.viewed.turn !== toggleTurn) { if (this.viewed.turn !== toggleTurn) {
const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns; const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns;
await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.spotlight.id);
if (combatant.actor.type === 'character') { if (combatant.actor.type === 'character') {
await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.characterSpotlight.id); await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.spotlight.id,
CONFIG.DH.GENERAL.countdownProgressionTypes.characterSpotlight.id);
} else {
await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.spotlight.id);
} }
const autoPoints = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).actionPoints; const autoPoints = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).actionPoints;

View file

@ -245,14 +245,20 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
return super.close(options); return super.close(options);
} }
static async updateCountdowns(progressType) { /**
* Sends updates of the countdowns to the GM player. Since this is asynchronous, be sure to
* update all the countdowns at the same time.
*
* @param {...any} progressTypes Countdowns to be updated
*/
static async updateCountdowns(...progressTypes) {
const { countdownAutomation } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation); const { countdownAutomation } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation);
if (!countdownAutomation) return; if (!countdownAutomation) return;
const countdownSetting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); const countdownSetting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns);
const updatedCountdowns = Object.keys(countdownSetting.countdowns).reduce((acc, key) => { const updatedCountdowns = Object.keys(countdownSetting.countdowns).reduce((acc, key) => {
const countdown = countdownSetting.countdowns[key]; const countdown = countdownSetting.countdowns[key];
if (countdown.progress.type === progressType && countdown.progress.current > 0) { if (progressTypes.indexOf(countdown.progress.type) !== -1 && countdown.progress.current > 0) {
acc.push(key); acc.push(key);
} }
@ -260,7 +266,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
}, []); }, []);
const countdownData = countdownSetting.toObject(); const countdownData = countdownSetting.toObject();
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, { const settings = {
...countdownData, ...countdownData,
countdowns: Object.keys(countdownData.countdowns).reduce((acc, key) => { countdowns: Object.keys(countdownData.countdowns).reduce((acc, key) => {
const countdown = foundry.utils.deepClone(countdownData.countdowns[key]); const countdown = foundry.utils.deepClone(countdownData.countdowns[key]);
@ -271,14 +277,12 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
acc[key] = countdown; acc[key] = countdown;
return acc; return acc;
}, {}) }, {})
};
await emitAsGM(GMUpdateEvent.UpdateCountdowns,
DhCountdowns.gmSetSetting.bind(settings),
settings, null, {
refreshType: RefreshType.Countdown
}); });
const data = { refreshType: RefreshType.Countdown };
await game.socket.emit(`system.${CONFIG.DH.id}`, {
action: socketEvent.Refresh,
data
});
Hooks.callAll(socketEvent.Refresh, data);
} }
async _onRender(context, options) { async _onRender(context, options) {

View file

@ -87,7 +87,7 @@ export default class DhEffectsDisplay extends HandlebarsApplicationMixin(Applica
async removeEffect(event) { async removeEffect(event) {
const element = event.target.closest('.effect-container'); const element = event.target.closest('.effect-container');
const effects = DhEffectsDisplay.getTokenEffects(); const effects = DhEffectsDisplay.getTokenEffects();
const effect = effects.find(x => x.id === element.id); const effect = effects.find(x => x.id === element.dataset.effectId);
await effect.delete(); await effect.delete();
this.render(); this.render();
} }

View file

@ -21,7 +21,7 @@ export const AdversaryBPPerEncounter = (adversaries, characters) => {
if (type.partyAmountPerBP) { if (type.partyAmountPerBP) {
acc += characters.length === 0 ? 0 : Math.ceil(entry.nr / characters.length); acc += characters.length === 0 ? 0 : Math.ceil(entry.nr / characters.length);
} else { } else {
acc += bpCost; acc += bpCost * entry.nr;
} }
return acc; return acc;

View file

@ -28,7 +28,8 @@ export const gameSettings = {
LevelTiers: 'LevelTiers', LevelTiers: 'LevelTiers',
Countdowns: 'Countdowns', Countdowns: 'Countdowns',
LastMigrationVersion: 'LastMigrationVersion', LastMigrationVersion: 'LastMigrationVersion',
TagTeamRoll: 'TagTeamRoll' TagTeamRoll: 'TagTeamRoll',
SpotlightRequestQueue: 'SpotlightRequestQueue',
}; };
export const actionAutomationChoices = { export const actionAutomationChoices = {

View file

@ -1,14 +1,15 @@
import { defaultRestOptions } from '../../config/generalConfig.mjs'; import { defaultRestOptions } from '../../config/generalConfig.mjs';
import { ActionsField } from '../fields/actionField.mjs'; import { ActionsField } from '../fields/actionField.mjs';
const currencyField = (initial, label) => const currencyField = (initial, label, icon) =>
new foundry.data.fields.SchemaField({ new foundry.data.fields.SchemaField({
enabled: new foundry.data.fields.BooleanField({ required: true, initial: true }), enabled: new foundry.data.fields.BooleanField({ required: true, initial: true }),
label: new foundry.data.fields.StringField({ label: new foundry.data.fields.StringField({
required: true, required: true,
initial, initial,
label label
}) }),
icon: new foundry.data.fields.StringField({ required: true, nullable: false, blank: true, initial: icon })
}); });
export default class DhHomebrew extends foundry.abstract.DataModel { export default class DhHomebrew extends foundry.abstract.DataModel {
@ -45,10 +46,22 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
initial: 'Gold', initial: 'Gold',
label: 'DAGGERHEART.SETTINGS.Homebrew.currency.currencyName' label: 'DAGGERHEART.SETTINGS.Homebrew.currency.currencyName'
}), }),
coins: currencyField('Coins', 'DAGGERHEART.SETTINGS.Homebrew.currency.coinName'), coins: currencyField(
handfuls: currencyField('Handfuls', 'DAGGERHEART.SETTINGS.Homebrew.currency.handfulName'), 'Coins',
bags: currencyField('Bags', 'DAGGERHEART.SETTINGS.Homebrew.currency.bagName'), 'DAGGERHEART.SETTINGS.Homebrew.currency.coinName',
chests: currencyField('Chests', 'DAGGERHEART.SETTINGS.Homebrew.currency.chestName') 'fa-solid fa-coin-front'
),
handfuls: currencyField(
'Handfuls',
'DAGGERHEART.SETTINGS.Homebrew.currency.handfulName',
'fa-solid fa-coins'
),
bags: currencyField('Bags', 'DAGGERHEART.SETTINGS.Homebrew.currency.bagName', 'fa-solid fa-sack'),
chests: currencyField(
'Chests',
'DAGGERHEART.SETTINGS.Homebrew.currency.chestName',
'fa-solid fa-treasure-chest'
)
}), }),
restMoves: new fields.SchemaField({ restMoves: new fields.SchemaField({
longRest: new fields.SchemaField({ longRest: new fields.SchemaField({
@ -139,22 +152,10 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
/** @inheritDoc */ /** @inheritDoc */
_initializeSource(source, options = {}) { _initializeSource(source, options = {}) {
source = super._initializeSource(source, options); source = super._initializeSource(source, options);
source.currency.coins = { for (const type of ['coins', 'handfuls', 'bags', 'chests']) {
enabled: source.currency.coins.enabled ?? true, const initial = this.schema.fields.currency.fields[type].getInitialValue();
label: source.currency.coins.label || source.currency.coins source.currency[type] = foundry.utils.mergeObject(initial, source.currency[type], { inplace: false });
}; }
source.currency.handfuls = {
enabled: source.currency.handfuls.enabled ?? true,
label: source.currency.handfuls.label || source.currency.handfuls
};
source.currency.bags = {
enabled: source.currency.bags.enabled ?? true,
label: source.currency.bags.label || source.currency.bags
};
source.currency.chests = {
enabled: source.currency.chests.enabled ?? true,
label: source.currency.chests.label || source.currency.chests
};
return source; return source;
} }
} }

View file

@ -39,6 +39,13 @@ export default class DhVariantRules extends foundry.abstract.DataModel {
label: 'DAGGERHEART.CONFIG.Range.close.name' label: 'DAGGERHEART.CONFIG.Range.close.name'
}), }),
far: new fields.NumberField({ required: true, initial: 60, label: 'DAGGERHEART.CONFIG.Range.far.name' }) far: new fields.NumberField({ required: true, initial: 60, label: 'DAGGERHEART.CONFIG.Range.far.name' })
}),
massiveDamage: new fields.SchemaField({
enabled: new fields.BooleanField({
required: true,
initial: false,
label: 'DAGGERHEART.SETTINGS.VariantRules.FIELDS.massiveDamage.enabled.label'
})
}) })
}; };
} }

View file

@ -237,6 +237,51 @@ export default class DHRoll extends Roll {
} }
} }
async function automateHopeFear(config) {
const automationSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation);
const hopeFearAutomation = automationSettings.hopeFear;
if (!config.source?.actor ||
(game.user.isGM ? !hopeFearAutomation.gm : !hopeFearAutomation.players) ||
config.actionType === 'reaction' ||
config.tagTeamSelected ||
config.skips?.resources)
return;
const actor = await fromUuid(config.source.actor);
let updates = [];
if (!actor) return;
if (config.rerolledRoll) {
if (config.roll.result.duality != config.rerolledRoll.result.duality) {
const hope = (config.roll.isCritical || config.roll.result.duality === 1 ? 1 : 0)
- (config.rerolledRoll.isCritical || config.rerolledRoll.result.duality === 1 ? 1 : 0);
const stress = (config.roll.isCritical ? 1 : 0) - (config.rerolledRoll.isCritical ? 1 : 0);
const fear = (config.roll.result.duality === -1 ? 1 : 0)
- (config.rerolledRoll.result.duality === -1 ? 1 : 0)
if (hope !== 0)
updates.push({ key: 'hope', value: hope, total: -1 * hope, enabled: true });
if (stress !== 0)
updates.push({ key: 'stress', value: -1 * stress, total: stress, enabled: true });
if (fear !== 0)
updates.push({ key: 'fear', value: fear, total: -1 * fear, enabled: true });
}
} else {
if (config.roll.isCritical || config.roll.result.duality === 1)
updates.push({ key: 'hope', value: 1, total: -1, enabled: true });
if (config.roll.isCritical)
updates.push({ key: 'stress', value: -1, total: 1, enabled: true });
if (config.roll.result.duality === -1)
updates.push({ key: 'fear', value: 1, total: -1, enabled: true });
}
if (updates.length) {
const target = actor.system.partner ?? actor;
if (!['dead', 'defeated', 'unconscious'].some(x => actor.statuses.has(x))) {
await target.modifyResource(updates);
}
}
}
export const registerRollDiceHooks = () => { export const registerRollDiceHooks = () => {
Hooks.on(`${CONFIG.DH.id}.postRollDuality`, async (config, message) => { Hooks.on(`${CONFIG.DH.id}.postRollDuality`, async (config, message) => {
const automationSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation); const automationSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation);
@ -247,45 +292,16 @@ export const registerRollDiceHooks = () => {
!config.skips?.updateCountdowns !config.skips?.updateCountdowns
) { ) {
const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns; const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns;
await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.actionRoll.id);
if (config.roll.result.duality === -1) { if (config.roll.result.duality === -1) {
await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.fear.id); await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.actionRoll.id,
CONFIG.DH.GENERAL.countdownProgressionTypes.fear.id);
} else {
await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.actionRoll.id);
} }
} }
const hopeFearAutomation = automationSettings.hopeFear; await automateHopeFear(config);
if (
!config.source?.actor ||
(game.user.isGM ? !hopeFearAutomation.gm : !hopeFearAutomation.players) ||
config.actionType === 'reaction' ||
config.tagTeamSelected ||
config.skips?.resources
)
return;
const actor = await fromUuid(config.source.actor);
let updates = [];
if (!actor) return;
if (config.roll.isCritical || config.roll.result.duality === 1)
updates.push({ key: 'hope', value: 1, total: -1, enabled: true });
if (config.roll.isCritical) updates.push({ key: 'stress', value: 1, total: -1, enabled: true });
if (config.roll.result.duality === -1) updates.push({ key: 'fear', value: 1, total: -1, enabled: true });
if (config.rerolledRoll) {
if (config.rerolledRoll.isCritical || config.rerolledRoll.result.duality === 1)
updates.push({ key: 'hope', value: -1, total: 1, enabled: true });
if (config.rerolledRoll.isCritical) updates.push({ key: 'stress', value: -1, total: 1, enabled: true });
if (config.rerolledRoll.result.duality === -1)
updates.push({ key: 'fear', value: -1, total: 1, enabled: true });
}
if (updates.length) {
const target = actor.system.partner ?? actor;
if (!['dead', 'defeated', 'unconscious'].some(x => actor.statuses.has(x))) {
if (config.rerolledRoll) target.modifyResource(updates);
else config.costs = [...(config.costs ?? []), ...updates];
}
}
if (!config.roll.hasOwnProperty('success') && !config.targets?.length) return; if (!config.roll.hasOwnProperty('success') && !config.targets?.length) return;
@ -296,7 +312,5 @@ export const registerRollDiceHooks = () => {
const currentCombatant = game.combat.combatants.get(game.combat.current?.combatantId); const currentCombatant = game.combat.combatants.get(game.combat.current?.combatantId);
if (currentCombatant?.actorId == actor.id) ui.combat.setCombatantSpotlight(currentCombatant.id); if (currentCombatant?.actorId == actor.id) ui.combat.setCombatantSpotlight(currentCombatant.id);
} }
return;
}); });
}; };

View file

@ -262,8 +262,7 @@ export default class DualityRoll extends D20Roll {
targets: message.system.targets, targets: message.system.targets,
tagTeamSelected: Object.values(tagTeamSettings.members).some(x => x.messageId === message._id), tagTeamSelected: Object.values(tagTeamSettings.members).some(x => x.messageId === message._id),
roll: newRoll, roll: newRoll,
rerolledRoll: rerolledRoll: message.system.roll
newRoll.result.duality !== message.system.roll.result.duality ? message.system.roll : undefined
}); });
return { newRoll, parsedRoll }; return { newRoll, parsedRoll };
} }

View file

@ -679,6 +679,10 @@ export default class DhpActor extends Actor {
return updates; return updates;
} }
/**
* Resources are modified asynchronously, so be careful not to update the same resource in
* quick succession.
*/
async modifyResource(resources) { async modifyResource(resources) {
if (!resources?.length) return; if (!resources?.length) return;
@ -761,6 +765,10 @@ export default class DhpActor extends Actor {
} }
convertDamageToThreshold(damage) { convertDamageToThreshold(damage) {
const massiveDamageEnabled=game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).massiveDamage.enabled;
if (massiveDamageEnabled && damage >= (this.system.damageThresholds.severe * 2)) {
return 4;
}
return damage >= this.system.damageThresholds.severe ? 3 : damage >= this.system.damageThresholds.major ? 2 : 1; return damage >= this.system.damageThresholds.severe ? 3 : damage >= this.system.damageThresholds.major ? 2 : 1;
} }
@ -854,7 +862,7 @@ export default class DhpActor extends Actor {
acc.push(effect); acc.push(effect);
const currentStatusActiveEffects = acc.filter( const currentStatusActiveEffects = acc.filter(
x => x.statuses.size === 1 && x.name === game.i18n.localize(statusMap.get(x.statuses.first()).name) x => x.statuses.size === 1 && x.name === game.i18n.localize(statusMap.get(x.statuses.first())?.name)
); );
for (var status of effect.statuses) { for (var status of effect.statuses) {

View file

@ -10,6 +10,7 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
let html = options.html; let html = options.html;
if (element.dataset.tooltip?.startsWith('#battlepoints#')) { if (element.dataset.tooltip?.startsWith('#battlepoints#')) {
this.#wide = true; this.#wide = true;
this.#bordered = true;
html = await this.getBattlepointHTML(element.dataset.combatId); html = await this.getBattlepointHTML(element.dataset.combatId);
options.direction = this._determineItemTooltipDirection(element); options.direction = this._determineItemTooltipDirection(element);
@ -22,6 +23,7 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
return; return;
} else { } else {
this.#wide = false; this.#wide = false;
this.#bordered = false;
} }
if (element.dataset.tooltip === '#effect-display#') { if (element.dataset.tooltip === '#effect-display#') {
@ -73,7 +75,8 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
{ {
item: item, item: item,
description: item.system?.enrichedDescription ?? item.enrichedDescription, description: item.system?.enrichedDescription ?? item.enrichedDescription,
config: CONFIG.DH config: CONFIG.DH,
allDomains: CONFIG.DH.DOMAIN.allDomains()
} }
); );
@ -168,14 +171,6 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
super.activate(element, { ...options, html: html }); super.activate(element, { ...options, html: html });
} }
_setStyle(position = {}) {
super._setStyle(position);
if (this.#bordered) {
this.tooltip.classList.add('bordered-tooltip');
}
}
_determineItemTooltipDirection(element, prefered = this.constructor.TOOLTIP_DIRECTIONS.LEFT) { _determineItemTooltipDirection(element, prefered = this.constructor.TOOLTIP_DIRECTIONS.LEFT) {
const pos = element.getBoundingClientRect(); const pos = element.getBoundingClientRect();
const dirs = this.constructor.TOOLTIP_DIRECTIONS; const dirs = this.constructor.TOOLTIP_DIRECTIONS;
@ -247,12 +242,17 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
if (this.#wide) { if (this.#wide) {
this.tooltip.classList.add('wide'); this.tooltip.classList.add('wide');
} }
if (this.#bordered) {
this.tooltip.classList.add('bordered-tooltip');
}
} }
/**@inheritdoc */ /**@inheritdoc */
lockTooltip() { lockTooltip() {
const clone = super.lockTooltip(); const clone = super.lockTooltip();
clone.classList.add('wide'); if (this.#wide) clone.classList.add('wide');
if (this.#bordered) clone.classList.add('bordered-tooltip');
return clone; return clone;
} }

View file

@ -198,7 +198,7 @@ foundry.dice.terms.Die.prototype.selfCorrecting = function (modifier) {
}; };
export const getDamageKey = damage => { export const getDamageKey = damage => {
return ['none', 'minor', 'major', 'severe', 'any'][damage]; return ['none', 'minor', 'major', 'severe', 'massive','any'][damage];
}; };
export const getDamageLabel = damage => { export const getDamageLabel = damage => {
@ -211,7 +211,8 @@ export const damageKeyToNumber = key => {
minor: 1, minor: 1,
major: 2, major: 2,
severe: 3, severe: 3,
any: 4 massive: 4,
any: 5
}[key]; }[key];
}; };

View file

@ -13,6 +13,16 @@ export const registerDHSettings = () => {
registerMenuSettings(); registerMenuSettings();
registerMenus(); registerMenus();
registerNonConfigSettings(); registerNonConfigSettings();
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.SpotlightRequestQueue, {
name: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.SpotlightRequestQueue.name'),
label: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.SpotlightRequestQueue.label'),
hint: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.SpotlightRequestQueue.hint'),
scope: 'world',
config: true,
type: Boolean,
onChange: () => ui.combat.render(),
})
}; };
const registerMenuSettings = () => { const registerMenuSettings = () => {

View file

@ -427,7 +427,8 @@
} }
} }
], ],
"includeBase": false "includeBase": false,
"direct": true
}, },
"target": { "target": {
"type": "any", "type": "any",

View file

@ -307,7 +307,8 @@
} }
} }
], ],
"includeBase": false "includeBase": false,
"direct": true
}, },
"target": { "target": {
"type": "any", "type": "any",
@ -319,7 +320,7 @@
"trait": null, "trait": null,
"difficulty": null, "difficulty": null,
"bonus": null, "bonus": null,
"advState": "neutral", "advState": "advantage",
"diceRolling": { "diceRolling": {
"multiplier": "prof", "multiplier": "prof",
"flatMultiplier": 1, "flatMultiplier": 1,

View file

@ -39,7 +39,8 @@
"experiences": { "experiences": {
"7GpgCWSe6hNwnOO7": { "7GpgCWSe6hNwnOO7": {
"name": "Throw", "name": "Throw",
"value": 2 "value": 2,
"description": ""
} }
}, },
"bonuses": { "bonuses": {
@ -105,7 +106,8 @@
}, },
"base": false "base": false
} }
] ],
"direct": true
}, },
"name": "Club", "name": "Club",
"img": "icons/weapons/clubs/club-banded-barbed-black.webp", "img": "icons/weapons/clubs/club-banded-barbed-black.webp",
@ -337,10 +339,11 @@
{ {
"value": { "value": {
"custom": { "custom": {
"enabled": false "enabled": false,
"formula": ""
}, },
"flatMultiplier": 1, "flatMultiplier": 1,
"dice": "d12", "dice": "d10",
"bonus": 2, "bonus": 2,
"multiplier": "flat" "multiplier": "flat"
}, },
@ -356,12 +359,14 @@
"dice": "d6", "dice": "d6",
"bonus": null, "bonus": null,
"custom": { "custom": {
"enabled": false "enabled": false,
"formula": ""
} }
} }
} }
], ],
"includeBase": false "includeBase": false,
"direct": true
}, },
"target": { "target": {
"type": "any", "type": "any",
@ -528,7 +533,8 @@
} }
} }
], ],
"includeBase": false "includeBase": false,
"direct": true
}, },
"target": { "target": {
"type": "any", "type": "any",

View file

@ -107,7 +107,8 @@
}, },
"base": false "base": false
} }
] ],
"direct": true
}, },
"img": "icons/magic/symbols/rune-sigil-rough-white-teal.webp", "img": "icons/magic/symbols/rune-sigil-rough-white-teal.webp",
"type": "attack", "type": "attack",

View file

@ -108,7 +108,8 @@
}, },
"base": false "base": false
} }
] ],
"direct": true
}, },
"type": "attack", "type": "attack",
"chatDisplay": false "chatDisplay": false
@ -358,7 +359,8 @@
} }
} }
], ],
"includeBase": false "includeBase": false,
"direct": true
}, },
"target": { "target": {
"type": "any", "type": "any",

View file

@ -377,7 +377,8 @@
} }
} }
], ],
"includeBase": false "includeBase": false,
"direct": true
}, },
"target": { "target": {
"type": "any", "type": "any",

View file

@ -233,7 +233,89 @@
"system": { "system": {
"description": "<p> The Assassin deals direct damage while theyre <em>Hidden</em>.</p>", "description": "<p> The Assassin deals direct damage while theyre <em>Hidden</em>.</p>",
"resource": null, "resource": null,
"actions": {}, "actions": {
"xFBE0jLf96fbCY7K": {
"type": "attack",
"_id": "xFBE0jLf96fbCY7K",
"systemPath": "actions",
"baseAction": false,
"description": "<p>The Assassin deals direct damage while theyre <em>Hidden</em>.</p>",
"chatDisplay": true,
"originItem": {
"type": "itemCollection"
},
"actionType": "action",
"cost": [],
"uses": {
"value": null,
"max": "",
"recovery": null,
"consumeOnSuccess": false
},
"damage": {
"parts": [
{
"value": {
"custom": {
"enabled": false,
"formula": ""
},
"flatMultiplier": 2,
"dice": "d10",
"bonus": 2,
"multiplier": "flat"
},
"applyTo": "hitPoints",
"type": [
"physical"
],
"base": false,
"resultBased": false,
"valueAlt": {
"multiplier": "prof",
"flatMultiplier": 1,
"dice": "d6",
"bonus": null,
"custom": {
"enabled": false,
"formula": ""
}
}
}
],
"includeBase": false,
"direct": true
},
"target": {
"type": "any",
"amount": null
},
"effects": [],
"roll": {
"type": "attack",
"trait": null,
"difficulty": null,
"bonus": null,
"advState": "neutral",
"diceRolling": {
"multiplier": "prof",
"flatMultiplier": 1,
"dice": "d6",
"compare": null,
"treshold": null
},
"useDefault": false
},
"save": {
"trait": null,
"difficulty": null,
"damageMod": "none"
},
"name": "Hidden attack",
"img": "icons/magic/perception/silhouette-stealth-shadow.webp",
"range": "close"
}
},
"originItemType": null, "originItemType": null,
"originId": null "originId": null
}, },

View file

@ -478,7 +478,8 @@
} }
} }
], ],
"includeBase": false "includeBase": false,
"direct": true
}, },
"target": { "target": {
"type": "any", "type": "any",

View file

@ -340,6 +340,87 @@
"name": "Curse", "name": "Curse",
"img": "icons/magic/unholy/hand-marked-pink.webp", "img": "icons/magic/unholy/hand-marked-pink.webp",
"range": "veryClose" "range": "veryClose"
},
"zLKfwa8a2YBRLKAF": {
"type": "attack",
"_id": "zLKfwa8a2YBRLKAF",
"systemPath": "actions",
"baseAction": false,
"description": "<p> Attacks made by the Hunter against a Deathlocked target deal direct damage.</p>",
"chatDisplay": true,
"originItem": {
"type": "itemCollection"
},
"actionType": "action",
"cost": [],
"uses": {
"value": null,
"max": "",
"recovery": null,
"consumeOnSuccess": false
},
"damage": {
"parts": [
{
"value": {
"custom": {
"enabled": false,
"formula": ""
},
"flatMultiplier": 2,
"dice": "d12",
"bonus": 1,
"multiplier": "flat"
},
"applyTo": "hitPoints",
"type": [
"physical"
],
"base": false,
"resultBased": false,
"valueAlt": {
"multiplier": "prof",
"flatMultiplier": 1,
"dice": "d6",
"bonus": null,
"custom": {
"enabled": false,
"formula": ""
}
}
}
],
"includeBase": false,
"direct": true
},
"target": {
"type": "any",
"amount": null
},
"effects": [],
"roll": {
"type": "attack",
"trait": null,
"difficulty": null,
"bonus": null,
"advState": "neutral",
"diceRolling": {
"multiplier": "prof",
"flatMultiplier": 1,
"dice": "d6",
"compare": null,
"treshold": null
},
"useDefault": false
},
"save": {
"trait": null,
"difficulty": null,
"damageMod": "none"
},
"name": "Deathlocked attack",
"img": "icons/magic/unholy/hand-marked-pink.webp",
"range": "veryClose"
} }
}, },
"originItemType": null, "originItemType": null,

View file

@ -312,9 +312,10 @@
{ {
"value": { "value": {
"custom": { "custom": {
"enabled": false "enabled": false,
"formula": ""
}, },
"flatMultiplier": 1, "flatMultiplier": 2,
"dice": "d6", "dice": "d6",
"bonus": 8, "bonus": 8,
"multiplier": "flat" "multiplier": "flat"
@ -331,12 +332,14 @@
"dice": "d6", "dice": "d6",
"bonus": null, "bonus": null,
"custom": { "custom": {
"enabled": false "enabled": false,
"formula": ""
} }
} }
} }
], ],
"includeBase": false "includeBase": false,
"direct": true
}, },
"target": { "target": {
"type": "any", "type": "any",

View file

@ -804,7 +804,8 @@
} }
} }
], ],
"includeBase": false "includeBase": false,
"direct": true
}, },
"target": { "target": {
"type": "any", "type": "any",

View file

@ -457,7 +457,8 @@
} }
} }
], ],
"includeBase": false "includeBase": false,
"direct": true
}, },
"target": { "target": {
"type": "any", "type": "any",

View file

@ -32,7 +32,7 @@
"img": "icons/equipment/chest/breastplate-helmet-metal.webp", "img": "icons/equipment/chest/breastplate-helmet-metal.webp",
"changes": [ "changes": [
{ {
"key": "system.rules.damageReduction.maxArmorMarked.stressExtra", "key": "system.rules.damageReduction.maxArmorMarked.value",
"mode": 2, "mode": 2,
"value": "1", "value": "1",
"priority": null "priority": null

View file

@ -15,7 +15,16 @@
"description": "<p>Mark a Stress to pick up and carry another willing creature approximately your size or smaller.</p>", "description": "<p>Mark a Stress to pick up and carry another willing creature approximately your size or smaller.</p>",
"chatDisplay": true, "chatDisplay": true,
"actionType": "action", "actionType": "action",
"cost": [], "cost": [
{
"scalable": false,
"key": "stress",
"value": 1,
"itemId": null,
"step": null,
"consumeOnSuccess": false
}
],
"uses": { "uses": {
"value": null, "value": null,
"max": "", "max": "",

View file

@ -37,3 +37,5 @@
@import './image-select/sheet.less'; @import './image-select/sheet.less';
@import './item-transfer/sheet.less'; @import './item-transfer/sheet.less';
@import './settings/change-currency-icon.less';

View file

@ -0,0 +1,13 @@
.application.daggerheart.dialog.dh-style.change-currency-icon {
.displayed-icon {
height: 2.5rem;
text-align: center;
font-size: 2.5rem;
margin-bottom: 1.25rem;
}
.input-row {
display: flex;
gap: 4px;
align-items: center;
}
}

View file

@ -56,7 +56,7 @@
.effect-locked { .effect-locked {
position: absolute; position: absolute;
bottom: 2px; bottom: 2px;
right: 2px; left: 11.5px;
font-size: 12px; font-size: 12px;
color: @golden; color: @golden;
filter: drop-shadow(0 0 3px black); filter: drop-shadow(0 0 3px black);

View file

@ -2,14 +2,14 @@
@import './mixin.less'; @import './mixin.less';
:root { :root {
--font-title: 'Cinzel Decorative'; --dh-font-title: 'Cinzel Decorative';
--font-subtitle: 'Cinzel'; --dh-font-subtitle: 'Cinzel';
--font-body: 'Montserrat'; --dh-font-body: 'Montserrat';
} }
@font-title: ~"var(--font-title, 'Cinzel Decorative'), serif"; @font-title: ~"var(--dh-font-title, 'Cinzel Decorative'), serif";
@font-subtitle: ~"var(--font-subtitle, 'Cinzel'), serif"; @font-subtitle: ~"var(--dh-font-subtitle, 'Cinzel'), serif";
@font-body: ~"var(--font-body, 'Montserrat'), sans-serif"; @font-body: ~"var(--dh-font-body, 'Montserrat'), sans-serif";
.dh-style { .dh-style {
.dh-typography(); .dh-typography();

View file

@ -3,7 +3,12 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px;
margin-bottom: 16px;
.battlepoint-categories-inner-container {
display: flex;
flex-direction: column;
gap: 8px;
}
.battlepoint-grouping-container { .battlepoint-grouping-container {
display: flex; display: flex;

View file

@ -1,4 +1,5 @@
#tooltip.bordered-tooltip { #tooltip.bordered-tooltip,
.locked-tooltip.bordered-tooltip {
border: 1px solid @golden; border: 1px solid @golden;
background-image: url('../assets/parchments/dh-parchment-dark.png'); background-image: url('../assets/parchments/dh-parchment-dark.png');
@ -14,6 +15,7 @@
.tooltip-header { .tooltip-header {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center;
text-align: start; text-align: start;
padding: 5px; padding: 5px;
gap: 0px; gap: 0px;

View file

@ -2,6 +2,10 @@
.locked-tooltip { .locked-tooltip {
&.wide { &.wide {
max-width: 480px; max-width: 480px;
.daggerheart.dh-style.tooltip {
align-items: start;
}
} }
.daggerheart.dh-style.tooltip { .daggerheart.dh-style.tooltip {

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": "1.3.1", "version": "1.3.2",
"compatibility": { "compatibility": {
"minimum": "13.346", "minimum": "13.346",
"verified": "13.351", "verified": "13.351",

View file

@ -46,8 +46,12 @@
</button> </button>
<div class="palette status-effects" data-palette="effects"> <div class="palette status-effects" data-palette="effects">
{{#each systemStatusEffects as |status|}} {{#each systemStatusEffects as |status|}}
<div class="effect-control-container" {{#if status.disabled}}data-tooltip="{{localize "DAGGERHEART.UI.Tooltip.immune"}}"{{else if status.title}}data-tooltip-text="{{status.title}}"{{/if}}> <div
<img class="effect-control {{status.cssClass}} {{#if (or status.disabled status.locked)}}disabled{{/if}}" src="{{status.src}}" data-action="effect" data-status-id="{{status.id}}"> class="effect-control effect-control-container {{status.cssClass}} {{#if (or status.disabled status.locked)}}disabled{{/if}}"
{{#if status.disabled}}data-tooltip="{{localize "DAGGERHEART.UI.Tooltip.immune"}}"{{else if status.title}}data-tooltip-text="{{status.title}}"{{/if}}
data-action="effect" data-status-id="{{status.id}}"
>
<img src="{{status.src}}" />
{{#if status.disabled}} {{#if status.disabled}}
<span class="effect-control-disabled-marker">/</span> <span class="effect-control-disabled-marker">/</span>
{{/if}} {{/if}}
@ -57,8 +61,12 @@
{{#if genericStatusEffects}} {{#if genericStatusEffects}}
<label class="palette-category-title">{{localize "DAGGERHEART.APPLICATIONS.HUD.tokenHUD.genericEffects"}}</label> <label class="palette-category-title">{{localize "DAGGERHEART.APPLICATIONS.HUD.tokenHUD.genericEffects"}}</label>
{{#each genericStatusEffects as |status|}} {{#each genericStatusEffects as |status|}}
<div class="effect-control-container" {{#if status.title}}data-tooltip-text="{{status.title}}"{{/if}}> <div
<img class="effect-control {{status.cssClass}} {{#if (or status.disabled status.locked)}}disabled{{/if}}" src="{{status.src}}" data-action="effect" data-status-id="{{status.id}}" > class="effect-control effect-control-container {{status.cssClass}} {{#if (or status.disabled status.locked)}}disabled{{/if}}"
{{#if status.title}}data-tooltip-text="{{status.title}}"{{/if}}
data-action="effect" data-status-id="{{status.id}}"
>
<img src="{{status.src}}" />
{{#if status.locked}}<i class="effect-locked fa-solid fa-lock"></i>{{/if}} {{#if status.locked}}<i class="effect-locked fa-solid fa-lock"></i>{{/if}}
</div> </div>
{{/each}} {{/each}}

View file

@ -0,0 +1,10 @@
<div>
<div class="displayed-icon">
<i class="{{currentIcon}}" inert></i>
</div>
<span>{{localize "DAGGERHEART.SETTINGS.Homebrew.currency.iconName"}} (<em>{{localize "DAGGERHEART.SETTINGS.Homebrew.currency.iconNameHint"}}</em>)</span>
<div class="input-row">
<input type="text" name="icon" value="{{currentIcon}}" />
<button type="button" data-action="reset" class="plain icon fa-solid fa-redo"></button>
</div>
</div>

View file

@ -33,18 +33,22 @@
{{formGroup settingFields.schema.fields.currency.fields.title value=settingFields._source.currency.title localize=true}} {{formGroup settingFields.schema.fields.currency.fields.title value=settingFields._source.currency.title localize=true}}
</div> </div>
<div class="toggleable-row"> <div class="toggleable-row">
<button class="icon {{settingFields._source.currency.coins.icon}}" data-action="editCurrencyIcon" data-currency="coins" data-tooltip="DAGGERHEART.SETTINGS.Homebrew.currency.changeIcon"></button>
{{formGroup settingFields.schema.fields.currency.fields.coins.fields.label value=settingFields._source.currency.coins.label localize=true}} {{formGroup settingFields.schema.fields.currency.fields.coins.fields.label value=settingFields._source.currency.coins.label localize=true}}
<input type="checkbox" name="currency.coins.enabled" {{checked settingFields._source.currency.coins.enabled}} /> <input type="checkbox" name="currency.coins.enabled" {{checked settingFields._source.currency.coins.enabled}} />
</div> </div>
<div class="toggleable-row"> <div class="toggleable-row">
<button class="icon {{settingFields._source.currency.handfuls.icon}}" data-action="editCurrencyIcon" data-currency="handfuls" data-tooltip="DAGGERHEART.SETTINGS.Homebrew.currency.changeIcon"></button>
{{formGroup settingFields.schema.fields.currency.fields.handfuls.fields.label value=settingFields._source.currency.handfuls.label localize=true}} {{formGroup settingFields.schema.fields.currency.fields.handfuls.fields.label value=settingFields._source.currency.handfuls.label localize=true}}
<input type="checkbox" name="currency.handfuls.enabled" {{checked settingFields._source.currency.handfuls.enabled}} /> <input type="checkbox" name="currency.handfuls.enabled" {{checked settingFields._source.currency.handfuls.enabled}} />
</div> </div>
<div class="toggleable-row"> <div class="toggleable-row">
<button class="icon {{settingFields._source.currency.bags.icon}}" data-action="editCurrencyIcon" data-currency="bags" data-tooltip="DAGGERHEART.SETTINGS.Homebrew.currency.changeIcon"></button>
{{formGroup settingFields.schema.fields.currency.fields.bags.fields.label value=settingFields._source.currency.bags.label localize=true}} {{formGroup settingFields.schema.fields.currency.fields.bags.fields.label value=settingFields._source.currency.bags.label localize=true}}
<input type="checkbox" name="currency.bags.enabled" {{checked settingFields._source.currency.bags.enabled}} /> <input type="checkbox" name="currency.bags.enabled" {{checked settingFields._source.currency.bags.enabled}} />
</div> </div>
<div class="toggleable-row"> <div class="toggleable-row">
<button class="icon {{settingFields._source.currency.chests.icon}}" data-action="editCurrencyIcon" data-currency="chests" data-tooltip="DAGGERHEART.SETTINGS.Homebrew.currency.changeIcon"></button>
{{formGroup settingFields.schema.fields.currency.fields.chests.fields.label value=settingFields._source.currency.chests.label localize=true}} {{formGroup settingFields.schema.fields.currency.fields.chests.fields.label value=settingFields._source.currency.chests.label localize=true}}
<input type="checkbox" name="currency.chests.enabled" {{checked settingFields._source.currency.chests.enabled}} /> <input type="checkbox" name="currency.chests.enabled" {{checked settingFields._source.currency.chests.enabled}} />
</div> </div>

View file

@ -22,6 +22,13 @@
</div> </div>
</div> </div>
<div class="form-group">
<label>{{localize "DAGGERHEART.SETTINGS.VariantRules.FIELDS.massiveDamage.title"}}</label>
<div class="form-fields">
{{formGroup settingFields.schema.fields.massiveDamage.fields.enabled value=settingFields._source.massiveDamage.enabled localize=true}}
</div>
</div>
<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>

View file

@ -17,8 +17,10 @@
{{#each this.inventory.currencies as | currency |}} {{#each this.inventory.currencies as | currency |}}
{{#if currency.enabled}} {{#if currency.enabled}}
<div class="input"> <div class="input">
<span>{{localize currency.label}}</span> <span>
{{formInput currency.field value=currency.value enriched=currency.value toggled=true}} <i class="{{currency.icon}}" inert></i> {{localize currency.label}}
</span>
<input type="text" name="{{currency.field.fieldPath}}" data-allow-delta value="{{currency.value}}" data-dtype="Number" min="0" step="1" />
</div> </div>
{{/if}} {{/if}}
{{/each}} {{/each}}

View file

@ -20,8 +20,10 @@
{{#each this.inventory.currencies as | currency |}} {{#each this.inventory.currencies as | currency |}}
{{#if currency.enabled}} {{#if currency.enabled}}
<div class="input"> <div class="input">
<span>{{localize currency.label}}</span> <span>
{{formInput currency.field value=currency.value enriched=currency.value toggled=true}} <i class="{{currency.icon}}" inert></i> {{localize currency.label}}
</span>
<input type="text" name="{{currency.field.fieldPath}}" data-allow-delta value="{{currency.value}}" data-dtype="Number" min="0" step="1" />
</div> </div>
{{/if}} {{/if}}
{{/each}} {{/each}}

View file

@ -1,8 +1,9 @@
<div> <div>
<div class="effects-display-container"> <div class="effects-display-container">
{{#each effects as | effect |}} {{#each effects as | effect |}}
<span class="effect-container {{#if effect.condition}}disabled{{/if}}" data-tooltip="#effect-display#" id="{{effect.id}}" <span class="effect-container {{#if effect.condition}}disabled{{/if}}" data-tooltip="#effect-display#" data-effect-id="{{effect.id}}"
data-applied-by="{{effect.appliedBy}}" {{#if effect.condition}}data-condition="{{effect.condition}}"{{else}}data-uuid="{{effect.uuid}}"{{/if}} {{#if effect.appliedBy}}data-applied-by="{{effect.appliedBy}}"{{/if}}
{{#if effect.condition}}data-condition="{{effect.condition}}"{{else}}data-uuid="{{effect.uuid}}"{{/if}}
> >
<a {{#if effect.condition}}disabled{{/if}}> <a {{#if effect.condition}}disabled{{/if}}>
<img src="{{effect.img}}" /> <img src="{{effect.img}}" />

View file

@ -1,20 +1,24 @@
<div class="daggerheart dh-style tooltip"> <div class="daggerheart dh-style tooltip">
<div class="tooltip-header"><h2>{{localize "Adversaries"}} ({{currentBP}}/{{maxBP}})</h2></div>
<div class="battlepoint-categories-container"> <div class="battlepoint-categories-container">
<h3>{{localize "Adversaries"}} ({{currentBP}}/{{maxBP}})</h3> <div class="battlepoint-categories-inner-container">
{{#each categories as |category key|}} {{#each categories as |category key|}}
{{#each category as |grouping index|}} {{#each category as |grouping index|}}
<div class="battlepoint-grouping-container"> <div class="battlepoint-grouping-container">
{{#if grouping.nr}} {{#if grouping.nr}}
<label>{{key}} BP: {{concat (localize grouping.description) ' ' '('grouping.nr 'x)'}}</label> <label>{{key}} BP: {{concat (localize grouping.description) ' ' '('grouping.nr 'x)'}}</label>
{{else}} {{else}}
<label class="unselected-grouping">{{key}} BP - {{localize grouping.description}}</label> <label class="unselected-grouping">{{key}} BP - {{localize grouping.description}}</label>
{{/if}} {{/if}}
</div> </div>
{{/each}}
{{/each}} {{/each}}
{{/each}} </div>
</div> </div>
<div class="tooltip-header"><h2>{{localize "Modifiers"}}</h2></div>
<div class="battlepoint-toggles-container"> <div class="battlepoint-toggles-container">
<h3>{{localize "Modifiers"}}</h3>
{{#each toggles as |toggle|}} {{#each toggles as |toggle|}}
<div class="battlepoint-toggle-container {{#if (and toggle.disabled (not toggle.checked))}}inactive{{/if}}"> <div class="battlepoint-toggle-container {{#if (and toggle.disabled (not toggle.checked))}}inactive{{/if}}">
{{#if toggle.disabled}} {{#if toggle.disabled}}

View file

@ -6,7 +6,7 @@
<div class="tooltip-information-section"> <div class="tooltip-information-section">
<div class="tooltip-information"> <div class="tooltip-information">
<label>{{localize "DAGGERHEART.GENERAL.Domain.single"}}</label> <label>{{localize "DAGGERHEART.GENERAL.Domain.single"}}</label>
{{#with (lookup config.DOMAIN.domains item.system.domain) as | domain |}} {{#with (lookup allDomains item.system.domain) as | domain |}}
<div>{{localize domain.label}}</div> <div>{{localize domain.label}}</div>
{{/with}} {{/with}}
</div> </div>