Merge branch 'main' into release

This commit is contained in:
WBHarry 2025-11-25 00:52:11 +01:00
commit fc5626ac47
43 changed files with 362 additions and 152 deletions

View file

@ -21,8 +21,56 @@ import { registerRollDiceHooks } from './module/dice/dhRoll.mjs';
import './node_modules/@yaireo/tagify/dist/tagify.css';
import TemplateManager from './module/documents/templateManager.mjs';
CONFIG.DH = SYSTEM;
CONFIG.TextEditor.enrichers.push(...enricherConfig);
CONFIG.Dice.rolls = [BaseRoll, DHRoll, DualityRoll, D20Roll, DamageRoll];
CONFIG.Dice.daggerheart = {
DHRoll: DHRoll,
DualityRoll: DualityRoll,
D20Roll: D20Roll,
DamageRoll: DamageRoll
};
CONFIG.Actor.documentClass = documents.DhpActor;
CONFIG.Actor.dataModels = models.actors.config;
CONFIG.Item.documentClass = documents.DHItem;
CONFIG.Item.dataModels = models.items.config;
CONFIG.ActiveEffect.documentClass = documents.DhActiveEffect;
CONFIG.ActiveEffect.dataModels = models.activeEffects.config;
CONFIG.Combat.documentClass = documents.DhpCombat;
CONFIG.Combat.dataModels = { base: models.DhCombat };
CONFIG.Combatant.dataModels = { base: models.DhCombatant };
CONFIG.ChatMessage.dataModels = models.chatMessages.config;
CONFIG.ChatMessage.documentClass = documents.DhChatMessage;
CONFIG.ChatMessage.template = 'systems/daggerheart/templates/ui/chat/chat-message.hbs';
CONFIG.Canvas.rulerClass = placeables.DhRuler;
CONFIG.Canvas.layers.templates.layerClass = placeables.DhTemplateLayer;
CONFIG.MeasuredTemplate.objectClass = placeables.DhMeasuredTemplate;
CONFIG.Token.documentClass = documents.DhToken;
CONFIG.Token.prototypeSheetClass = applications.sheetConfigs.DhPrototypeTokenConfig;
CONFIG.Token.objectClass = placeables.DhTokenPlaceable;
CONFIG.Token.rulerClass = placeables.DhTokenRuler;
CONFIG.Token.hudClass = applications.hud.DHTokenHUD;
CONFIG.ui.combat = applications.ui.DhCombatTracker;
CONFIG.ui.chat = applications.ui.DhChatLog;
CONFIG.ui.hotbar = applications.ui.DhHotbar;
CONFIG.ui.sidebar = applications.sidebar.DhSidebar;
CONFIG.ui.daggerheartMenu = applications.sidebar.DaggerheartMenu;
CONFIG.ui.resources = applications.ui.DhFearTracker;
CONFIG.ui.countdowns = applications.ui.DhCountdowns;
CONFIG.ux.ContextMenu = applications.ux.DHContextMenu;
CONFIG.ux.TooltipManager = documents.DhTooltipManager;
CONFIG.ux.TemplateManager = new TemplateManager();
Hooks.once('init', () => {
CONFIG.DH = SYSTEM;
game.system.api = {
applications,
data,
@ -32,31 +80,12 @@ Hooks.once('init', () => {
fields
};
CONFIG.TextEditor.enrichers.push(...enricherConfig);
CONFIG.Dice.daggerheart = {
DHRoll: DHRoll,
DualityRoll: DualityRoll,
D20Roll: D20Roll,
DamageRoll: DamageRoll
};
CONFIG.Dice.rolls = [BaseRoll, DHRoll, DualityRoll, D20Roll, DamageRoll];
CONFIG.MeasuredTemplate.objectClass = placeables.DhMeasuredTemplate;
const { DocumentSheetConfig } = foundry.applications.apps;
CONFIG.Token.documentClass = documents.DhToken;
CONFIG.Token.prototypeSheetClass = applications.sheetConfigs.DhPrototypeTokenConfig;
DocumentSheetConfig.unregisterSheet(TokenDocument, 'core', foundry.applications.sheets.TokenConfig);
DocumentSheetConfig.registerSheet(TokenDocument, SYSTEM.id, applications.sheetConfigs.DhTokenConfig, {
makeDefault: true
});
CONFIG.Item.documentClass = documents.DHItem;
//Registering the Item DataModel
CONFIG.Item.dataModels = models.items.config;
const { Items, Actors } = foundry.documents.collections;
Items.unregisterSheet('core', foundry.applications.sheets.ItemSheetV2);
Items.registerSheet(SYSTEM.id, applications.sheets.items.Ancestry, { types: ['ancestry'], makeDefault: true });
@ -74,9 +103,6 @@ Hooks.once('init', () => {
Items.registerSheet(SYSTEM.id, applications.sheets.items.Armor, { types: ['armor'], makeDefault: true });
Items.registerSheet(SYSTEM.id, applications.sheets.items.Beastform, { types: ['beastform'], makeDefault: true });
CONFIG.Actor.documentClass = documents.DhpActor;
CONFIG.Actor.dataModels = models.actors.config;
Actors.unregisterSheet('core', foundry.applications.sheets.ActorSheetV2);
Actors.registerSheet(SYSTEM.id, applications.sheets.actors.Character, { types: ['character'], makeDefault: true });
Actors.registerSheet(SYSTEM.id, applications.sheets.actors.Companion, { types: ['companion'], makeDefault: true });
@ -90,9 +116,6 @@ Hooks.once('init', () => {
makeDefault: true
});
CONFIG.ActiveEffect.documentClass = documents.DhActiveEffect;
CONFIG.ActiveEffect.dataModels = models.activeEffects.config;
DocumentSheetConfig.unregisterSheet(
CONFIG.ActiveEffect.documentClass,
'core',
@ -107,38 +130,6 @@ Hooks.once('init', () => {
}
);
CONFIG.Token.hudClass = applications.hud.DHTokenHUD;
CONFIG.Combat.dataModels = {
base: models.DhCombat
};
CONFIG.Combatant.dataModels = {
base: models.DhCombatant
};
CONFIG.ChatMessage.dataModels = models.chatMessages.config;
CONFIG.ChatMessage.documentClass = documents.DhChatMessage;
CONFIG.ChatMessage.template = 'systems/daggerheart/templates/ui/chat/chat-message.hbs';
CONFIG.Canvas.rulerClass = placeables.DhRuler;
CONFIG.Canvas.layers.templates.layerClass = placeables.DhTemplateLayer;
CONFIG.Token.objectClass = placeables.DhTokenPlaceable;
CONFIG.Combat.documentClass = documents.DhpCombat;
CONFIG.ui.combat = applications.ui.DhCombatTracker;
CONFIG.ui.chat = applications.ui.DhChatLog;
CONFIG.ui.hotbar = applications.ui.DhHotbar;
CONFIG.ui.sidebar = applications.sidebar.DhSidebar;
CONFIG.ui.daggerheartMenu = applications.sidebar.DaggerheartMenu;
CONFIG.Token.rulerClass = placeables.DhTokenRuler;
CONFIG.ui.resources = applications.ui.DhFearTracker;
CONFIG.ui.countdowns = applications.ui.DhCountdowns;
CONFIG.ux.ContextMenu = applications.ux.DHContextMenu;
CONFIG.ux.TooltipManager = documents.DhTooltipManager;
CONFIG.ux.TemplateManager = new TemplateManager();
game.socket.on(`system.${SYSTEM.id}`, socketRegistration.handleSocketEvent);
// Make Compendium Dialog resizable

View file

@ -103,6 +103,7 @@
"Settings": {
"attackBonus": "Attack Bonus",
"attackName": "Attack Name",
"criticalThreshold": "Critical Threshold",
"includeBase": { "label": "Include Item Damage" },
"multiplier": "Multiplier",
"saveHint": "Set a default Trait to enable Reaction Roll. It can be changed later in Reaction Roll Dialog.",
@ -343,7 +344,8 @@
"progress": {
"current": { "label": "Current" },
"looping": { "label": "Looping" },
"max": { "label": "Max" },
"start": { "label": "Start" },
"startFormula": { "label": "Start Formula" },
"type": {
"label": { "label": "Label", "hint": "Used for custom" },
"value": { "label": "Value" }
@ -371,12 +373,13 @@
"newCountdown": "New Countdown",
"removeCountdownTitle": "Remove Countdown",
"removeCountdownText": "Are you sure you want to remove the countdown: {name}?",
"current": "Current",
"max": "Max",
"currentCountdownValue": "Current: {value}",
"currentCountdownMax": "Max: {value}",
"current": "Current Value",
"start": "Start Value",
"startFormula": "Randomized Start Value Formula",
"currentCountdownCurrent": "Current: {value}",
"currentCountdownStart": "Start: {value}",
"category": "Category",
"progressionType": "Progression Type",
"progressionType": "Progression",
"decreasing": "Decreasing",
"looping": "Looping",
"defaultOwnershipTooltip": "The default player ownership of countdowns",

View file

@ -34,7 +34,7 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD {
context.systemStatusEffects = Object.keys(context.statusEffects).reduce((acc, key) => {
const effect = context.statusEffects[key];
if (effect.systemEffect) {
const disabled = !effect.isActive && this.actor.system.rules.conditionImmunities[key];
const disabled = !effect.isActive && this.actor.system.rules?.conditionImmunities?.[key];
acc[key] = { ...effect, disabled };
}

View file

@ -1,4 +1,5 @@
import { DhCountdown } from '../../data/countdowns.mjs';
import { waitForDiceSoNice } from '../../helpers/utils.mjs';
import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
@ -26,6 +27,7 @@ export default class CountdownEdit extends HandlebarsApplicationMixin(Applicatio
toggleCountdownEdit: CountdownEdit.#toggleCountdownEdit,
editCountdownImage: CountdownEdit.#editCountdownImage,
editCountdownOwnership: CountdownEdit.#editCountdownOwnership,
randomiseCountdownStart: CountdownEdit.#randomiseCountdownStart,
removeCountdown: CountdownEdit.#removeCountdown
},
form: { handler: this.updateData, submitOnChange: true }
@ -57,6 +59,7 @@ export default class CountdownEdit extends HandlebarsApplicationMixin(Applicatio
? 'DAGGERHEART.UI.Countdowns.decreasingLoop'
: 'DAGGERHEART.UI.Countdowns.loop'
: null;
const randomizeValid = !new Roll(countdown.progress.startFormula ?? '').isDeterministic;
acc[key] = {
...countdown,
typeName: game.i18n.localize(CONFIG.DH.GENERAL.countdownBaseTypes[countdown.type].label),
@ -67,6 +70,7 @@ export default class CountdownEdit extends HandlebarsApplicationMixin(Applicatio
)
},
editing: this.editingCountdowns.has(key),
randomizeValid,
loopTooltip
};
@ -123,18 +127,26 @@ export default class CountdownEdit extends HandlebarsApplicationMixin(Applicatio
// Sync current and max if max is changing and they were equal before
for (const [id, countdown] of Object.entries(settingsData.countdowns ?? {})) {
const existing = this.data.countdowns[id];
const wasEqual = existing && existing.progress.current === existing.progress.max;
if (wasEqual && countdown.progress.max !== existing.progress.max) {
countdown.progress.current = countdown.progress.max;
} else {
countdown.progress.current = Math.min(countdown.progress.current, countdown.progress.max);
}
countdown.progress.current = this.getMatchingCurrentValue(
existing,
countdown.progress.start,
countdown.progress.current
);
}
this.hideNewCountdowns = hideNewCountdowns;
this.updateSetting(settingsData);
}
getMatchingCurrentValue(oldCount, newStart, newCurrent) {
const wasEqual = oldCount && oldCount.progress.current === oldCount.progress.start;
if (wasEqual && newStart !== oldCount.progress.start) {
return newStart;
} else {
return Math.min(newCurrent, newStart);
}
}
async gmSetSetting(data) {
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, data),
game.socket.emit(`system.${CONFIG.DH.id}`, {
@ -190,6 +202,21 @@ export default class CountdownEdit extends HandlebarsApplicationMixin(Applicatio
this.updateSetting({ [`countdowns.${button.dataset.countdownId}`]: data });
}
static async #randomiseCountdownStart(_, button) {
const countdown = this.data.countdowns[button.dataset.countdownId];
const roll = await new Roll(countdown.progress.startFormula).roll();
const message = await roll.toMessage({ title: 'Countdown' });
await waitForDiceSoNice(message);
await this.updateSetting({
[`countdowns.${button.dataset.countdownId}.progress`]: {
start: roll.total,
current: this.getMatchingCurrentValue(countdown, roll.total, countdown.progress.current)
}
});
this.render();
}
static async #removeCountdown(event, button) {
const { countdownId } = button.dataset;

View file

@ -1,3 +1,4 @@
import { waitForDiceSoNice } from '../../helpers/utils.mjs';
import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
@ -123,13 +124,14 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
: 'DAGGERHEART.UI.Countdowns.loop'
: null;
const loopDisabled =
!countdownEditable || (isLooping && (countdown.progress.current > 0 || countdown.progress.max === '0'));
!countdownEditable ||
(isLooping && (countdown.progress.current > 0 || countdown.progress.start === '0'));
acc[key] = {
...countdown,
editable: countdownEditable,
noPlayerAccess: nonGmPlayers.length && playersWithAccess.length === 0,
shouldLoop: isLooping && countdown.progress.current === 0 && countdown.progress.max > 0,
shouldLoop: isLooping && countdown.progress.current === 0 && countdown.progress.start > 0,
loopDisabled: isLooping ? loopDisabled : null,
loopTooltip: isLooping && game.i18n.localize(loopTooltip)
};
@ -182,16 +184,27 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns);
const countdown = settings.countdowns[target.id];
let progressMax = countdown.progress.start;
let message = null;
if (countdown.progress.startFormula) {
const roll = await new Roll(countdown.progress.startFormula).evaluate();
progressMax = roll.total;
message = await roll.toMessage();
}
const newMax =
countdown.progress.looping === CONFIG.DH.GENERAL.countdownLoopingTypes.increasing.id
? Number(countdown.progress.max) + 1
? Number(progressMax) + 1
: countdown.progress.looping === CONFIG.DH.GENERAL.countdownLoopingTypes.decreasing.id
? Math.max(Number(countdown.progress.max) - 1, 0)
: countdown.progress.max;
? Math.max(Number(progressMax) - 1, 0)
: progressMax;
await waitForDiceSoNice(message);
await settings.updateSource({
[`countdowns.${target.id}.progress`]: {
current: newMax,
max: newMax
start: newMax
}
});
await emitAsGM(GMUpdateEvent.UpdateCountdowns, DhCountdowns.gmSetSetting.bind(settings), settings, null, {
@ -205,7 +218,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns);
const countdown = settings.countdowns[target.id];
const newCurrent = increase
? Math.min(countdown.progress.current + 1, countdown.progress.max)
? 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 emitAsGM(GMUpdateEvent.UpdateCountdowns, DhCountdowns.gmSetSetting.bind(settings), settings, null, {

View file

@ -8,7 +8,10 @@ export default class DhCountdownAction extends DHBaseAction {
...super.defaultValues,
countdown: {
name: this.parent.parent.name,
img: this.img
img: this.img,
progress: {
startFormula: '1'
}
}
};
}
@ -21,10 +24,26 @@ export default class DhCountdownAction extends DHBaseAction {
{
...game.system.api.data.countdowns.DhCountdown.defaultCountdown(),
name: parent.parent.name,
img: parent.parent.img
img: parent.parent.img,
progress: {
startFormula: '1'
}
}
];
return updateSource;
}
/** @inheritDoc */
static migrateData(source) {
for (const countdown of source.countdown) {
if (countdown.progress.max) {
countdown.progress.startFormula = countdown.progress.max;
countdown.progress.start = 1;
countdown.progress.max = null;
}
}
return super.migrateData(source);
}
}

View file

@ -39,6 +39,7 @@ export default class DhpAdversary extends BaseDataActor {
integer: true,
label: 'DAGGERHEART.GENERAL.hordeHp'
}),
criticalThreshold: new fields.NumberField({ required: true, integer: true, min: 1, max: 20, initial: 20 }),
damageThresholds: new fields.SchemaField({
major: new fields.NumberField({
required: true,
@ -54,8 +55,20 @@ export default class DhpAdversary extends BaseDataActor {
})
}),
resources: new fields.SchemaField({
hitPoints: resourceField(0, 0, 'DAGGERHEART.GENERAL.HitPoints.plural', true),
stress: resourceField(0, 0, 'DAGGERHEART.GENERAL.stress', true)
hitPoints: resourceField(
0,
0,
'DAGGERHEART.GENERAL.HitPoints.plural',
true,
game.i18n.localize('DAGGERHEART.GENERAL.max')
),
stress: resourceField(
0,
0,
'DAGGERHEART.GENERAL.stress',
true,
game.i18n.localize('DAGGERHEART.GENERAL.max')
)
}),
rules: new fields.SchemaField({
conditionImmunities: new fields.SchemaField({

View file

@ -167,10 +167,15 @@ export class DhCountdown extends foundry.abstract.DataModel {
initial: 1,
label: 'DAGGERHEART.APPLICATIONS.Countdown.FIELDS.countdowns.element.progress.current.label'
}),
max: new FormulaField({
start: new fields.NumberField({
required: true,
integer: true,
initial: 1,
label: 'DAGGERHEART.APPLICATIONS.Countdown.FIELDS.countdowns.element.progress.max.label',
label: 'DAGGERHEART.APPLICATIONS.Countdown.FIELDS.countdowns.element.progress.start.label',
deterministic: false
}),
startFormula: new FormulaField({
label: 'DAGGERHEART.APPLICATIONS.Countdown.FIELDS.countdowns.element.progress.startFormula.label',
deterministic: false
}),
looping: new fields.StringField({
@ -206,7 +211,7 @@ export class DhCountdown extends foundry.abstract.DataModel {
ownership: ownership,
progress: {
current: 1,
max: 1
start: 1
}
};
}
@ -225,4 +230,15 @@ export class DhCountdown extends foundry.abstract.DataModel {
return acc;
}, {});
}
/** @inheritDoc */
static migrateData(source) {
if (source.progress.max) {
source.progress.start = Number(source.progress.max);
source.progress.max = null;
source.progress.startFormula = null;
}
return super.migrateData(source);
}
}

View file

@ -40,18 +40,40 @@ export default class CountdownField extends fields.ArrayField {
}
const data = { countdowns: {} };
const countdownMessages = [];
for (let countdown of config.countdowns) {
const { total: max } = await new Roll(countdown.progress.max).evaluate();
let startFormula = countdown.progress.startFormula ? countdown.progress.startFormula : null;
let countdownStart = startFormula ?? '1';
if (startFormula) {
const roll = await new Roll(startFormula).roll();
if (roll.dice.length > 0) {
countdownStart = roll.total;
const message = await roll.toMessage();
countdownMessages.push(message);
} else {
startFormula = null;
}
}
data.countdowns[foundry.utils.randomID()] = {
...countdown,
progress: {
...countdown.progress,
current: max,
max: max
current: countdownStart,
start: countdownStart,
startFormula
}
};
}
if (game.modules.get('dice-so-nice')?.active) {
await Promise.all(
countdownMessages.map(message => {
return game.dice3d.waitFor3DAnimationByMessageID(message.id);
})
);
}
await emitAsGM(
GMUpdateEvent.UpdateCountdowns,
async () => {

View file

@ -13,8 +13,6 @@ export default class D20Roll extends DHRoll {
DISADVANTAGE: -1
};
static CRITICAL_TRESHOLD = 20;
static DefaultDialog = D20RollDialog;
get title() {
@ -37,7 +35,7 @@ export default class D20Roll extends DHRoll {
get isCritical() {
if (!this.d20._evaluated) return;
return this.d20.total >= this.constructor.CRITICAL_TRESHOLD;
return this.d20.total >= this.data.system.criticalThreshold;
}
get hasAdvantage() {

View file

@ -445,3 +445,9 @@ export function itemIsIdentical(a, b) {
return compendiumSource && name & description;
}
export async function waitForDiceSoNice(message) {
if (message && game.modules.get('dice-so-nice')?.active) {
await game.dice3d.waitFor3DAnimationByMessageID(message.id);
}
}

View file

@ -738,7 +738,8 @@
"progress": {
"looping": "looping",
"type": "actionRoll",
"max": "1d6",
"start": 1,
"startFormula": "1d6",
"current": 1
},
"ownership": {}

View file

@ -794,7 +794,8 @@
"progress": {
"looping": "looping",
"type": "actionRoll",
"max": "2d6",
"start": 1,
"startFormula": "2d6",
"current": 1
},
"ownership": {}

View file

@ -476,7 +476,8 @@
"progress": {
"looping": "looping",
"type": "custom",
"max": "6",
"start": 1,
"startFormula": "6",
"current": 1
},
"ownership": {}

View file

@ -579,7 +579,8 @@
"progress": {
"looping": "looping",
"type": "actionRoll",
"max": "2d6",
"start": 1,
"startFormula": "2d6",
"current": 1
},
"ownership": {}

View file

@ -623,7 +623,8 @@
"progress": {
"looping": "decreasing",
"type": "actionRoll",
"max": "8",
"start": 1,
"startFormula": "8",
"current": 1
},
"ownership": {}

View file

@ -634,7 +634,8 @@
"progress": {
"looping": "noLooping",
"type": "actionRoll",
"max": "1d8",
"start": 1,
"startFormula": "1d8",
"current": 1
},
"ownership": {}

View file

@ -630,7 +630,8 @@
"progress": {
"looping": "noLooping",
"type": "custom",
"max": "4",
"start": 1,
"startFormula": "4",
"current": 1
},
"ownership": {}

View file

@ -361,7 +361,8 @@
"progress": {
"looping": "noLooping",
"type": "characterAttack",
"max": "5",
"start": 1,
"startFormula": "5",
"current": 1
},
"ownership": {}

View file

@ -640,7 +640,8 @@
"progress": {
"looping": "looping",
"type": "actionRoll",
"max": "1d6",
"start": 1,
"startFormula": "1d6",
"current": 1
},
"ownership": {}

View file

@ -376,7 +376,8 @@
"progress": {
"looping": "noLooping",
"type": "custom",
"max": "8",
"start": 1,
"startFormula": "8",
"current": 1
},
"ownership": {}

View file

@ -625,7 +625,8 @@
"progress": {
"looping": "looping",
"type": "actionRoll",
"max": "1d6",
"start": 1,
"startFormula": "1d6",
"current": 1
},
"ownership": {}

View file

@ -488,7 +488,8 @@
"progress": {
"looping": "noLooping",
"type": "custom",
"max": "6",
"start": 1,
"startFormula": "6",
"current": 1
},
"ownership": {}

View file

@ -543,7 +543,8 @@
"progress": {
"looping": "noLooping",
"type": "custom",
"max": "8",
"start": 1,
"startFormula": "8",
"current": 1
},
"ownership": {}

View file

@ -862,7 +862,8 @@
"progress": {
"looping": "noLooping",
"type": "fear",
"max": "1d12",
"start": 1,
"startFormula": "1d12",
"current": 1
},
"ownership": {}

View file

@ -63,7 +63,8 @@
"progress": {
"looping": "noLooping",
"type": "custom",
"max": "8",
"start": 1,
"startFormula": "8",
"current": 1
},
"ownership": {}

View file

@ -545,7 +545,8 @@
"progress": {
"looping": "looping",
"type": "actionRoll",
"max": "6",
"start": 1,
"startFormula": "6",
"current": 1
},
"ownership": {}

View file

@ -291,7 +291,8 @@
"progress": {
"looping": "noLooping",
"type": "custom",
"max": "6",
"start": 1,
"startFormula": "6",
"current": 1
},
"ownership": {}
@ -304,7 +305,8 @@
"progress": {
"looping": "noLooping",
"type": "custom",
"max": "4",
"start": 1,
"startFormula": "4",
"current": 1
},
"ownership": {}

View file

@ -283,7 +283,8 @@
"progress": {
"looping": "noLooping",
"type": "custom",
"max": "6",
"start": 1,
"startFormula": "6",
"current": 1
},
"ownership": {}

View file

@ -225,7 +225,8 @@
"progress": {
"looping": "noLooping",
"type": "custom",
"max": "8",
"start": 1,
"startFormula": "8",
"current": 1
},
"ownership": {}
@ -341,7 +342,7 @@
"progress": {
"looping": "looping",
"type": "actionRoll",
"max": "4",
"startValue": "4",
"current": 1
},
"ownership": {}

View file

@ -174,7 +174,8 @@
"progress": {
"looping": "noLooping",
"type": "custom",
"max": "12",
"start": 1,
"startFormula": "12",
"current": 1
},
"ownership": {}

View file

@ -374,7 +374,8 @@
"progress": {
"looping": "noLooping",
"type": "fear",
"max": "6",
"start": 1,
"startFormula": "6",
"current": 1
},
"ownership": {}

View file

@ -197,7 +197,8 @@
"progress": {
"looping": "noLooping",
"type": "custom",
"max": "8",
"start": 1,
"startFormula": "8",
"current": 1
},
"ownership": {}
@ -607,7 +608,7 @@
"progress": {
"looping": "noLooping",
"type": "custom",
"max": "10",
"startValue": "10",
"current": 1
},
"ownership": {}

View file

@ -361,7 +361,8 @@
"progress": {
"looping": "noLooping",
"type": "custom",
"max": "5",
"start": 1,
"startFormula": "5",
"current": 1
},
"ownership": {}

View file

@ -404,7 +404,8 @@
"progress": {
"looping": "looping",
"type": "actionRoll",
"max": "4",
"start": 1,
"startFormula": "4",
"current": 1
},
"ownership": {}

View file

@ -183,7 +183,8 @@
"progress": {
"looping": "noLooping",
"type": "custom",
"max": "4",
"start": 1,
"startFormula": "4",
"current": 1
},
"ownership": {}

View file

@ -129,12 +129,20 @@
}
}
}
.countdown-edit-subrow {
.countdown-edit-row-container {
display: flex;
gap: 8px;
flex-direction: column;
gap: 2px;
.countdown-edit-subrow {
display: flex;
gap: 8px;
}
}
.countdown-edit-input {
position: relative;
flex: 1;
display: flex;
flex-direction: column;
@ -151,11 +159,56 @@
&.tiny {
flex: 0;
label {
white-space: nowrap;
}
input {
min-width: 2.5rem;
}
}
.countdown-random-input {
padding-right: var(--input-height);
}
.countdown-random-button {
position: absolute;
right: 0px;
bottom: 0px;
border-radius: 0 6px 6px 0;
height: var(--input-height);
display: flex;
align-items: center;
justify-content: center;
padding: 0 8px;
&[disabled] {
opacity: 0.5;
}
i {
font-size: 18px;
}
}
// .countdown-random-container {
// width: 100%;
// display: flex;
// input {
// border-radius: 6px 0 0 6px;
// border-right: 0;
// }
// button {
// border-radius: 0 6px 6px 0;
// height: var(--input-height);
// border: 1px solid white;
// }
// }
input,
select {
background: light-dark(@beige, @dark-blue);

View file

@ -2,7 +2,7 @@
"id": "daggerheart",
"title": "Daggerheart",
"description": "An unofficial implementation of the Daggerheart system",
"version": "1.2.5",
"version": "1.2.6",
"compatibility": {
"minimum": "13",
"verified": "13.351",

View file

@ -16,7 +16,7 @@
<div class="nest-inputs">
{{formField ../fields.progress.fields.looping value=countdown.progress.looping name=(concat "countdown." index ".progress.looping") localize=true}}
{{formField ../fields.progress.fields.type value=countdown.progress.type name=(concat "countdown." index ".progress.type") localize=true}}
{{formField ../fields.progress.fields.max value=countdown.progress.max name=(concat "countdown." index ".progress.max") localize=true}}
{{formField ../fields.progress.fields.startFormula value=countdown.progress.startFormula name=(concat "countdown." index ".progress.startFormula") label="DAGGERHEART.APPLICATIONS.CountdownEdit.start" localize=true}}
</div>
{{/each}}
</fieldset>

View file

@ -8,8 +8,9 @@
{{formGroup systemFields.attack.fields.img value=document._source.system.attack.img label="DAGGERHEART.GENERAL.imagePath" name="system.attack.img" localize=true}}
{{formGroup systemFields.attack.fields.name value=document._source.system.attack.name label="DAGGERHEART.ACTIONS.Settings.attackName" name="system.attack.name" localize=true}}
</fieldset>
<fieldset class="flex">
<fieldset class="one-column">
<legend>{{localize "DAGGERHEART.GENERAL.attack"}}</legend>
<div class="nest-inputs">
{{formField systemFields.attack.fields.roll.fields.bonus value=document._source.system.attack.roll.bonus label="DAGGERHEART.ACTIONS.Settings.attackBonus" name="system.attack.roll.bonus" localize=true}}
{{formField systemFields.attack.fields.range value=document._source.system.attack.range label="DAGGERHEART.GENERAL.range" name="system.attack.range" localize=true}}
{{#if systemFields.attack.fields.target.fields}}
@ -18,6 +19,8 @@
{{ formField systemFields.attack.fields.target.fields.amount value=document._source.system.attack.target.amount label="DAGGERHEART.GENERAL.amount" name="system.attack.target.amount" localize=true}}
{{/if}}
{{/if}}
</div>
{{formGroup systemFields.criticalThreshold value=document._source.system.criticalThreshold label="DAGGERHEART.ACTIONS.Settings.criticalThreshold" name="system.criticalThreshold" localize=true}}
</fieldset>
{{> 'systems/daggerheart/templates/actionTypes/damage.hbs' fields=systemFields.attack.fields.damage.fields.parts.element.fields source=document.system.attack.damage path="system.attack." directField=systemFields.attack.fields.damage.fields.direct horde=(eq document._source.system.type 'horde')}}
</section>

View file

@ -34,6 +34,16 @@
<h4>{{localize "DAGGERHEART.GENERAL.attack"}}</h4>
</div>
</div>
{{#if (lt source.system.criticalThreshold 20)}}
<div class="status-number">
<div class='status-value armor-slots'>
<p>{{source.system.criticalThreshold}}+</p>
</div>
<div class="status-label">
<h4>{{localize "DAGGERHEART.GENERAL.criticalShort"}}</h4>
</div>
</div>
{{/if}}
</div>
<div class="status-section">
<div class="threshold-section">

View file

@ -29,8 +29,8 @@
<div class="countdown-edit-text">
<h4>{{countdown.name}}</h4>
<div class="countdown-edit-subtext">
<div class="countdown-edit-sub-tag">{{localize "DAGGERHEART.APPLICATIONS.CountdownEdit.currentCountdownValue" value=countdown.progress.current}}</div>
<div class="countdown-edit-sub-tag">{{localize "DAGGERHEART.APPLICATIONS.CountdownEdit.currentCountdownMax" value=countdown.progress.max}}</div>
<div class="countdown-edit-sub-tag">{{localize "DAGGERHEART.APPLICATIONS.CountdownEdit.currentCountdownCurrent" value=countdown.progress.current}}</div>
<div class="countdown-edit-sub-tag">{{localize "DAGGERHEART.APPLICATIONS.CountdownEdit.currentCountdownStart" value=countdown.progress.start}}</div>
<div class="countdown-edit-sub-tag">{{countdown.typeName}}</div>
<div class="countdown-edit-sub-tag">{{countdown.progress.typeName}}</div>
@ -59,32 +59,42 @@
</div>
</div>
{{#if countdown.editing}}
<div class="countdown-edit-subrow">
<div class="countdown-edit-input tiny">
<label>{{localize "DAGGERHEART.APPLICATIONS.CountdownEdit.current"}}</label>
<input type="number" name="{{concat "countdowns." id ".progress.current"}}" value="{{countdown.progress.current}}" />
<div class="countdown-edit-row-container">
<div class="countdown-edit-subrow">
<div class="countdown-edit-input tiny">
<label>{{localize "DAGGERHEART.APPLICATIONS.CountdownEdit.current"}}</label>
<input type="number" name="{{concat "countdowns." id ".progress.current"}}" value="{{countdown.progress.current}}" />
</div>
<div class="countdown-edit-input tiny">
<label>{{localize "DAGGERHEART.APPLICATIONS.CountdownEdit.start"}}</label>
<input type="number" name="{{concat "countdowns." id ".progress.start"}}" value="{{countdown.progress.start}}" />
</div>
<div class="countdown-edit-input">
<label>{{localize "DAGGERHEART.APPLICATIONS.CountdownEdit.startFormula"}}</label>
<input class="countdown-random-input" type="text" name="{{concat "countdowns." id ".progress.startFormula"}}" value="{{countdown.progress.startFormula}}" />
<a class="countdown-random-button" data-action="randomiseCountdownStart" data-countdown-id="{{id}}" {{#unless countdown.randomizeValid}}disabled{{/unless}}><i class="fa-solid fa-dice fa-fw"></i></a>
</div>
</div>
<div class="countdown-edit-input tiny">
<label>{{localize "DAGGERHEART.APPLICATIONS.CountdownEdit.max"}}</label>
<input type="number" name="{{concat "countdowns." id ".progress.max"}}" value="{{countdown.progress.max}}" />
</div>
<div class="countdown-edit-input tiny type-input">
<label>{{localize "DAGGERHEART.APPLICATIONS.CountdownEdit.category"}}</label>
<select name="{{concat "countdowns." id ".type"}}">
{{selectOptions ../countdownBaseTypes selected=countdown.type localize=true}}
</select>
</div>
<div class="countdown-edit-input">
<label>{{localize "DAGGERHEART.APPLICATIONS.CountdownEdit.progressionType"}}</label>
<select name="{{concat "countdowns." id ".progress.type"}}">
{{selectOptions ../countdownProgressionTypes selected=countdown.progress.type localize=true}}
</select>
</div>
<div class="countdown-edit-input looping-input tiny">
<label>{{localize "DAGGERHEART.APPLICATIONS.CountdownEdit.looping"}}</label>
<select name="{{concat "countdowns." id ".progress.looping"}}">
{{selectOptions ../countdownLoopingTypes selected=countdown.progress.looping localize=true}}
</select>
<div class="countdown-edit-subrow">
<div class="countdown-edit-input tiny type-input">
<label>{{localize "DAGGERHEART.APPLICATIONS.CountdownEdit.category"}}</label>
<select name="{{concat "countdowns." id ".type"}}">
{{selectOptions ../countdownBaseTypes selected=countdown.type localize=true}}
</select>
</div>
<div class="countdown-edit-input">
<label>{{localize "DAGGERHEART.APPLICATIONS.CountdownEdit.progressionType"}}</label>
<select name="{{concat "countdowns." id ".progress.type"}}">
{{selectOptions ../countdownProgressionTypes selected=countdown.progress.type localize=true}}
</select>
</div>
<div class="countdown-edit-input looping-input tiny">
<label>{{localize "DAGGERHEART.APPLICATIONS.CountdownEdit.looping"}}</label>
<select name="{{concat "countdowns." id ".progress.looping"}}">
{{selectOptions ../countdownLoopingTypes selected=countdown.progress.looping localize=true}}
</select>
</div>
</div>
</div>
{{/if}}

View file

@ -10,7 +10,7 @@
<div class="countdown-tool-controls">
{{#if countdown.editable}}<a data-action="decreaseCountdown" id="{{id}}"><i class="fa-solid fa-minus"></i></a>{{/if}}
<div class="progress-tag">
{{countdown.progress.current}}/{{countdown.progress.max}}
{{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}}
</div>