[Feature] Downtime Improvements (#411)

* Initial

* Fixed dialog again

* Fix healing targeted resource in setting

* Removed unused templates

---------

Co-authored-by: Dapoolp <elcatnet@gmail.com>
This commit is contained in:
WBHarry 2025-07-25 21:31:05 +02:00 committed by GitHub
parent 3f95740b7a
commit 62b9a8fbee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 538 additions and 457 deletions

View file

@ -1472,6 +1472,7 @@
"Appearance": { "Appearance": {
"FIELDS": { "FIELDS": {
"displayFear": { "label": "Fear Display" }, "displayFear": { "label": "Fear Display" },
"dualityColorScheme": { "label": "Chat Style" },
"showGenericStatusEffects": { "label": "Show Foundry Status Effects" } "showGenericStatusEffects": { "label": "Show Foundry Status Effects" }
}, },
"fearDisplay": { "fearDisplay": {
@ -1552,7 +1553,7 @@
"duality": "Duality Rolls", "duality": "Duality Rolls",
"diceSoNice": { "diceSoNice": {
"title": "Dice So Nice", "title": "Dice So Nice",
"hint": "Coloration of Hope and Fear dice if the Dice So Nice module is used.", "hint": "Coloration of Duality dice if the Dice So Nice module is used.",
"foreground": "Foreground", "foreground": "Foreground",
"background": "Background", "background": "Background",
"outline": "Outline", "outline": "Outline",

View file

@ -64,8 +64,8 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
context.selectedActivity = this.selectedActivity; context.selectedActivity = this.selectedActivity;
context.moveData = this.moveData; context.moveData = this.moveData;
const shortRestMovesSelected = this.#nrSelectedMoves('shortRest'); const shortRestMovesSelected = this.nrSelectedMoves('shortRest');
const longRestMovesSelected = this.#nrSelectedMoves('longRest'); const longRestMovesSelected = this.nrSelectedMoves('longRest');
context.nrChoices = { context.nrChoices = {
...this.nrChoices, ...this.nrChoices,
shortRest: { shortRest: {
@ -89,7 +89,7 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
static selectMove(_, target) { static selectMove(_, target) {
const { category, move } = target.dataset; const { category, move } = target.dataset;
const nrSelected = this.#nrSelectedMoves(category); const nrSelected = this.nrSelectedMoves(category);
if (nrSelected + this.nrChoices[category].taken >= this.nrChoices[category].max) { if (nrSelected + this.nrChoices[category].taken >= this.nrChoices[category].max) {
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noMoreMoves')); ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noMoreMoves'));
@ -176,7 +176,7 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
this.render(); this.render();
} }
#nrSelectedMoves(category) { nrSelectedMoves(category) {
return Object.values(this.moveData[category].moves).reduce((acc, x) => acc + (x.selected ?? 0), 0); return Object.values(this.moveData[category].moves).reduce((acc, x) => acc + (x.selected ?? 0), 0);
} }
} }

View file

@ -36,10 +36,37 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
} }
}; };
/** @inheritdoc */
static TABS = {
diceSoNice: {
tabs: [
{ id: 'hope', label: 'DAGGERHEART.GENERAL.hope' },
{ id: 'fear', label: 'DAGGERHEART.GENERAL.fear' },
{ id: 'advantage', label: 'DAGGERHEART.GENERAL.Advantage.full' },
{ id: 'disadvantage', label: 'DAGGERHEART.GENERAL.Advantage.full' }
],
initial: 'hope'
}
};
changeTab(tab, group, options) {
super.changeTab(tab, group, options);
this.render();
}
async _prepareContext(_options) { async _prepareContext(_options) {
const context = await super._prepareContext(_options); const context = await super._prepareContext(_options);
context.settingFields = this.settings; context.settingFields = this.settings;
context.diceSoNiceTextures = game.dice3d?.exports?.TEXTURELIST ?? {};
context.diceSoNiceColorsets = game.dice3d?.exports?.COLORSETS ?? {};
context.diceTab = {
key: this.tabGroups.diceSoNice,
source: this.settings._source.diceSoNice[this.tabGroups.diceSoNice],
fields: this.settings.schema.fields.diceSoNice.fields[this.tabGroups.diceSoNice].fields
};
return context; return context;
} }
@ -65,4 +92,13 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
this.close(); this.close();
} }
_getTabs(tabs) {
for (const v of Object.values(tabs)) {
v.active = this.tabGroups[v.group] ? this.tabGroups[v.group] === v.id : v.active;
v.cssClass = v.active ? 'active' : '';
}
return tabs;
}
} }

View file

@ -1,148 +0,0 @@
import { actionsTypes } from '../../../data/action/_module.mjs';
import DHActionConfig from '../../sheets-configs/action-config.mjs';
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
export default class DhSettingsActionView extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(resolve, reject, title, name, icon, img, description, actions) {
super({});
this.resolve = resolve;
this.reject = reject;
this.viewTitle = title;
this.name = name;
this.icon = icon;
this.img = img;
this.description = description;
this.actions = actions;
}
get title() {
return this.viewTitle;
}
static DEFAULT_OPTIONS = {
tag: 'form',
classes: ['daggerheart', 'setting', 'dh-style'],
position: { width: 440, height: 'auto' },
actions: {
editImage: this.onEditImage,
addItem: this.addItem,
editItem: this.editItem,
removeItem: this.removeItem,
resetMoves: this.resetMoves,
saveForm: this.saveForm
},
form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false }
};
static PARTS = {
header: { template: 'systems/daggerheart/templates/settings/components/action-view-header.hbs' },
main: {
template: 'systems/daggerheart/templates/settings/components/action-view.hbs'
},
footer: { template: 'systems/daggerheart/templates/settings/components/action-view-footer.hbs' }
};
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
context.name = this.name;
context.icon = this.icon;
context.img = this.img;
context.description = this.description;
context.enrichedDescription = await foundry.applications.ux.TextEditor.enrichHTML(context.description);
context.actions = this.actions;
return context;
}
static async updateData(event, element, formData) {
const { name, icon, description } = foundry.utils.expandObject(formData.object);
this.name = name;
this.icon = icon;
this.description = description;
this.render();
}
static async saveForm(event) {
this.resolve({
name: this.name,
icon: this.icon,
img: this.img,
description: this.description,
actions: this.actions
});
this.close(true);
}
static close(fromSave) {
if (!fromSave) {
this.reject();
}
super.close();
}
static onEditImage() {
const fp = new foundry.applications.apps.FilePicker.implementation({
current: this.img,
type: 'image',
callback: async path => {
this.img = path;
this.render();
},
top: this.position.top + 40,
left: this.position.left + 10
});
return fp.browse();
}
async selectActionType() {
const content = await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/actionTypes/actionType.hbs',
{ types: CONFIG.DH.ACTIONS.actionTypes }
),
title = 'Select Action Type',
type = 'form',
data = {};
return Dialog.prompt({
title,
label: title,
content,
type,
callback: html => {
const form = html[0].querySelector('form'),
fd = new foundry.applications.ux.FormDataExtended(form);
foundry.utils.mergeObject(data, fd.object, { inplace: true });
return data;
},
rejectClose: false
});
}
static async addItem() {
const actionType = await this.selectActionType();
const cls = actionsTypes[actionType?.type] ?? actionsTypes.attack,
action = new cls({
_id: foundry.utils.randomID(),
type: actionType.type,
name: game.i18n.localize(CONFIG.DH.ACTIONS.actionTypes[actionType.type].name),
...cls.getSourceConfig(this.document)
});
this.actions.push(action);
this.render();
}
static async editItem(_, button) {
await new DHActionConfig(this.actions[button.dataset.id]).render(true);
}
static removeItem(event, button) {
this.actions = this.actions.filter((_, index) => index !== Number.parseInt(button.dataset.id));
this.render();
}
static resetMoves() {}
}

View file

@ -1,6 +1,4 @@
import { DhHomebrew } from '../../data/settings/_module.mjs'; import { DhHomebrew } from '../../data/settings/_module.mjs';
import DhSettingsActionView from './components/settingsActionsView.mjs';
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
export default class DhHomebrewSettings extends HandlebarsApplicationMixin(ApplicationV2) { export default class DhHomebrewSettings extends HandlebarsApplicationMixin(ApplicationV2) {
@ -73,23 +71,21 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
static async editItem(_, target) { static async editItem(_, target) {
const move = this.settings.restMoves[target.dataset.type].moves[target.dataset.id]; const move = this.settings.restMoves[target.dataset.type].moves[target.dataset.id];
new Promise((resolve, reject) => { const path = `restMoves.${target.dataset.type}.moves.${target.dataset.id}`;
new DhSettingsActionView( const editedMove = await game.system.api.applications.sheetConfigs.DowntimeConfig.configure(
resolve, move,
reject, path,
game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.downtimeMoves'), this.settings
move.name, );
move.icon, if (!editedMove) return;
move.img,
move.description, await this.updateAction.bind(this)(editedMove, target.dataset.type, target.dataset.id);
move.actions
).render(true);
}).then(data => this.updateAction.bind(this)(data, target.dataset.type, target.dataset.id));
} }
async updateAction(data, type, id) { async updateAction(data, type, id) {
await this.settings.updateSource({ await this.settings.updateSource({
[`restMoves.${type}.moves.${id}`]: { [`restMoves.${type}.moves.${id}`]: {
actions: data.actions,
name: data.name, name: data.name,
icon: data.icon, icon: data.icon,
img: data.img, img: data.img,
@ -139,7 +135,11 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
acc[key] = { acc[key] = {
...move, ...move,
name: game.i18n.localize(move.name), name: game.i18n.localize(move.name),
description: game.i18n.localize(move.description) description: game.i18n.localize(move.description),
actions: move.actions.map(action => ({
...action,
name: game.i18n.localize(action.name)
}))
}; };
return acc; return acc;

View file

@ -1,6 +1,7 @@
export { default as ActionConfig } from './action-config.mjs'; export { default as ActionConfig } from './action-config.mjs';
export { default as AdversarySettings } from './adversary-settings.mjs'; export { default as AdversarySettings } from './adversary-settings.mjs';
export { default as CompanionSettings } from './companion-settings.mjs'; export { default as CompanionSettings } from './companion-settings.mjs';
export { default as DowntimeConfig } from './downtimeConfig.mjs';
export { default as EnvironmentSettings } from './environment-settings.mjs'; export { default as EnvironmentSettings } from './environment-settings.mjs';
export { default as ActiveEffectConfig } from './activeEffectConfig.mjs'; export { default as ActiveEffectConfig } from './activeEffectConfig.mjs';
export { default as DhTokenConfig } from './token-config.mjs'; export { default as DhTokenConfig } from './token-config.mjs';

View file

@ -2,10 +2,11 @@ import DaggerheartSheet from '../sheets/daggerheart-sheet.mjs';
const { ApplicationV2 } = foundry.applications.api; const { ApplicationV2 } = foundry.applications.api;
export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) { export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
constructor(action) { constructor(action, sheetUpdate) {
super({}); super({});
this.action = action; this.action = action;
this.sheetUpdate = sheetUpdate;
this.openSection = null; this.openSection = null;
} }
@ -171,6 +172,8 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
const submitData = this._prepareSubmitData(event, formData), const submitData = this._prepareSubmitData(event, formData),
data = foundry.utils.mergeObject(this.action.toObject(), submitData); data = foundry.utils.mergeObject(this.action.toObject(), submitData);
this.action = await this.action.update(data); this.action = await this.action.update(data);
this.sheetUpdate?.(this.action);
this.render(); this.render();
} }
@ -195,7 +198,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
if (!this.action.damage.parts) return; if (!this.action.damage.parts) return;
const data = this.action.toObject(), const data = this.action.toObject(),
part = {}; part = {};
if(this.action.actor?.isNPC) part.value = { multiplier: 'flat' }; if (this.action.actor?.isNPC) part.value = { multiplier: 'flat' };
data.damage.parts.push(part); data.damage.parts.push(part);
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
} }

View file

@ -0,0 +1,162 @@
import { actionsTypes } from '../../data/action/_module.mjs';
import DHActionConfig from './action-config.mjs';
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
export default class DowntimeConfig extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(move, movePath, settings, options) {
super(options);
this.move = move;
this.movePath = movePath;
this.actionsPath = `${movePath}.actions`;
this.settings = settings;
}
get title() {
return game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.downtimeMoves');
}
static DEFAULT_OPTIONS = {
tag: 'form',
classes: ['daggerheart', 'setting', 'dh-style'],
position: { width: 440, height: 'auto' },
window: {
icon: 'fa-solid fa-gears'
},
actions: {
editImage: this.onEditImage,
addItem: this.addItem,
editItem: this.editItem,
removeItem: this.removeItem,
resetMoves: this.resetMoves,
saveForm: this.saveForm
},
form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false }
};
static PARTS = {
header: { template: 'systems/daggerheart/templates/settings/downtime-config/header.hbs' },
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
main: { template: 'systems/daggerheart/templates/settings/downtime-config/main.hbs' },
actions: { template: 'systems/daggerheart/templates/settings/downtime-config/actions.hbs' },
footer: { template: 'systems/daggerheart/templates/settings/downtime-config/footer.hbs' }
};
/** @inheritdoc */
static TABS = {
primary: {
tabs: [{ id: 'main' }, { id: 'actions' }],
initial: 'main',
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
}
};
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
context.move = this.move;
context.move.enrichedDescription = await foundry.applications.ux.TextEditor.enrichHTML(
context.move.description
);
return context;
}
static async updateData(event, element, formData) {
const data = foundry.utils.expandObject(formData.object);
foundry.utils.mergeObject(this.move, data);
this.render();
}
static async saveForm() {
this.close({ submitted: true });
}
static onEditImage() {
const fp = new foundry.applications.apps.FilePicker.implementation({
current: this.img,
type: 'image',
callback: async path => {
this.move.img = path;
this.render();
},
top: this.position.top + 40,
left: this.position.left + 10
});
return fp.browse();
}
async selectActionType() {
return (
(await foundry.applications.api.DialogV2.input({
window: { title: game.i18n.localize('DAGGERHEART.CONFIG.SelectAction.selectType') },
content: await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/actionTypes/actionType.hbs',
{ types: CONFIG.DH.ACTIONS.actionTypes }
),
ok: {
label: game.i18n.format('DOCUMENT.Create', {
type: game.i18n.localize('DAGGERHEART.GENERAL.Action.single')
})
}
})) ?? {}
);
}
static async addItem() {
const { type: actionType } = await this.selectActionType();
if (!actionType) return;
const cls = actionsTypes[actionType] ?? actionsTypes.attack,
action = new cls(
{
type: actionType,
name: game.i18n.localize(CONFIG.DH.ACTIONS.actionTypes[actionType].name),
img: 'icons/magic/life/cross-worn-green.webp',
actionType: 'action',
systemPath: this.actionsPath
},
{
parent: this.settings
}
);
await this.settings.updateSource({ [`${this.actionsPath}.${action.id}`]: action });
this.move = foundry.utils.getProperty(this.settings, this.movePath);
this.render();
}
static async editItem(_, target) {
const actionId = target.dataset.id;
const action = this.move.actions.get(actionId);
await new DHActionConfig(action, async updatedMove => {
await this.settings.updateSource({ [`${this.actionsPath}.${actionId}`]: updatedMove });
this.move = foundry.utils.getProperty(this.settings, this.movePath);
this.render();
}).render(true);
}
static async removeItem(_, target) {
await this.settings.updateSource({ [`${this.actionsPath}.-=${target.dataset.id}`]: null });
this.move = foundry.utils.getProperty(this.settings, this.movePath);
this.render();
}
static resetMoves() {}
/** @override */
_onClose(options = {}) {
if (!options.submitted) this.move = null;
}
static async configure(move, movePath, settings, options = {}) {
return new Promise(resolve => {
const app = new this(move, movePath, settings, options);
app.addEventListener('close', () => resolve(app.move), { once: true });
app.render({ force: true });
});
}
}

View file

@ -138,8 +138,8 @@ export const defaultRestOptions = {
icon: 'fa-solid fa-bandage', icon: 'fa-solid fa-bandage',
img: 'icons/magic/life/cross-worn-green.webp', img: 'icons/magic/life/cross-worn-green.webp',
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.tendToWounds.description'), description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.tendToWounds.description'),
actions: [ actions: {
{ tendToWounds: {
type: 'healing', type: 'healing',
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.tendToWounds.name'), name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.tendToWounds.name'),
img: 'icons/magic/life/cross-worn-green.webp', img: 'icons/magic/life/cross-worn-green.webp',
@ -154,7 +154,7 @@ export const defaultRestOptions = {
} }
} }
} }
] }
}, },
clearStress: { clearStress: {
id: 'clearStress', id: 'clearStress',
@ -162,8 +162,8 @@ export const defaultRestOptions = {
icon: 'fa-regular fa-face-surprise', icon: 'fa-regular fa-face-surprise',
img: 'icons/magic/perception/eye-ringed-green.webp', img: 'icons/magic/perception/eye-ringed-green.webp',
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.clearStress.description'), description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.clearStress.description'),
actions: [ actions: {
{ clearStress: {
type: 'healing', type: 'healing',
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.clearStress.name'), name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.clearStress.name'),
img: 'icons/magic/perception/eye-ringed-green.webp', img: 'icons/magic/perception/eye-ringed-green.webp',
@ -178,7 +178,7 @@ export const defaultRestOptions = {
} }
} }
} }
] }
}, },
repairArmor: { repairArmor: {
id: 'repairArmor', id: 'repairArmor',
@ -186,8 +186,8 @@ export const defaultRestOptions = {
icon: 'fa-solid fa-hammer', icon: 'fa-solid fa-hammer',
img: 'icons/skills/trades/smithing-anvil-silver-red.webp', img: 'icons/skills/trades/smithing-anvil-silver-red.webp',
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.repairArmor.description'), description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.repairArmor.description'),
actions: [ actions: {
{ repairArmor: {
type: 'healing', type: 'healing',
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.repairArmor.name'), name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.repairArmor.name'),
img: 'icons/skills/trades/smithing-anvil-silver-red.webp', img: 'icons/skills/trades/smithing-anvil-silver-red.webp',
@ -202,7 +202,7 @@ export const defaultRestOptions = {
} }
} }
} }
] }
}, },
prepare: { prepare: {
id: 'prepare', id: 'prepare',
@ -210,7 +210,7 @@ export const defaultRestOptions = {
icon: 'fa-solid fa-dumbbell', icon: 'fa-solid fa-dumbbell',
img: 'icons/skills/trades/academics-merchant-scribe.webp', img: 'icons/skills/trades/academics-merchant-scribe.webp',
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.prepare.description'), description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.prepare.description'),
actions: [] actions: {}
} }
}), }),
longRest: () => ({ longRest: () => ({
@ -220,7 +220,23 @@ export const defaultRestOptions = {
icon: 'fa-solid fa-bandage', icon: 'fa-solid fa-bandage',
img: 'icons/magic/life/cross-worn-green.webp', img: 'icons/magic/life/cross-worn-green.webp',
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.tendToWounds.description'), description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.tendToWounds.description'),
actions: [] actions: {
tendToWounds: {
type: 'healing',
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.tendToWounds.name'),
img: 'icons/magic/life/cross-worn-green.webp',
actionType: 'action',
healing: {
applyTo: healingTypes.hitPoints.id,
value: {
custom: {
enabled: true,
formula: '@system.resources.hitPoints.max'
}
}
}
}
}
}, },
clearStress: { clearStress: {
id: 'clearStress', id: 'clearStress',
@ -228,7 +244,23 @@ export const defaultRestOptions = {
icon: 'fa-regular fa-face-surprise', icon: 'fa-regular fa-face-surprise',
img: 'icons/magic/perception/eye-ringed-green.webp', img: 'icons/magic/perception/eye-ringed-green.webp',
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.clearStress.description'), description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.clearStress.description'),
actions: [] actions: {
clearStress: {
type: 'healing',
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.clearStress.name'),
img: 'icons/magic/perception/eye-ringed-green.webp',
actionType: 'action',
healing: {
applyTo: healingTypes.stress.id,
value: {
custom: {
enabled: true,
formula: '@system.resources.stress.max'
}
}
}
}
}
}, },
repairArmor: { repairArmor: {
id: 'repairArmor', id: 'repairArmor',
@ -236,7 +268,23 @@ export const defaultRestOptions = {
icon: 'fa-solid fa-hammer', icon: 'fa-solid fa-hammer',
img: 'icons/skills/trades/smithing-anvil-silver-red.webp', img: 'icons/skills/trades/smithing-anvil-silver-red.webp',
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.repairArmor.description'), description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.repairArmor.description'),
actions: [] actions: {
repairArmor: {
type: 'healing',
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.repairArmor.name'),
img: 'icons/skills/trades/smithing-anvil-silver-red.webp',
actionType: 'action',
healing: {
applyTo: healingTypes.armorStack.id,
value: {
custom: {
enabled: true,
formula: '@system.armorScore'
}
}
}
}
}
}, },
prepare: { prepare: {
id: 'prepare', id: 'prepare',
@ -244,7 +292,7 @@ export const defaultRestOptions = {
icon: 'fa-solid fa-dumbbell', icon: 'fa-solid fa-dumbbell',
img: 'icons/skills/trades/academics-merchant-scribe.webp', img: 'icons/skills/trades/academics-merchant-scribe.webp',
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.prepare.description'), description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.prepare.description'),
actions: [] actions: {}
}, },
workOnAProject: { workOnAProject: {
id: 'workOnAProject', id: 'workOnAProject',
@ -252,7 +300,7 @@ export const defaultRestOptions = {
icon: 'fa-solid fa-diagram-project', icon: 'fa-solid fa-diagram-project',
img: 'icons/skills/social/thumbsup-approval-like.webp', img: 'icons/skills/social/thumbsup-approval-like.webp',
description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.workOnAProject.description'), description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.workOnAProject.description'),
actions: [] actions: {}
} }
}) })
}; };

View file

@ -1,5 +1,5 @@
import DHActionConfig from "../../applications/sheets-configs/action-config.mjs"; import DHActionConfig from '../../applications/sheets-configs/action-config.mjs';
import MappingField from "./mappingField.mjs"; import MappingField from './mappingField.mjs';
/** /**
* Specialized collection type for stored actions. * Specialized collection type for stored actions.
@ -7,52 +7,52 @@ import MappingField from "./mappingField.mjs";
* @param {Action[]} entries The actions to store. * @param {Action[]} entries The actions to store.
*/ */
export class ActionCollection extends Collection { export class ActionCollection extends Collection {
constructor(model, entries) { constructor(model, entries) {
super(); super();
this.#model = model; this.#model = model;
for ( const entry of entries ) { for (const entry of entries) {
if ( !(entry instanceof game.system.api.models.actions.actionsTypes.base) ) continue; if (!(entry instanceof game.system.api.models.actions.actionsTypes.base)) continue;
this.set(entry._id, entry); this.set(entry._id, entry);
}
} }
}
/* -------------------------------------------- */ /* -------------------------------------------- */
/* Properties */ /* Properties */
/* -------------------------------------------- */ /* -------------------------------------------- */
/** /**
* The parent DataModel to which this ActionCollection belongs. * The parent DataModel to which this ActionCollection belongs.
* @type {DataModel} * @type {DataModel}
*/ */
#model; #model;
/* -------------------------------------------- */ /* -------------------------------------------- */
/* -------------------------------------------- */ /* -------------------------------------------- */
/* Methods */ /* Methods */
/* -------------------------------------------- */ /* -------------------------------------------- */
/* -------------------------------------------- */ /* -------------------------------------------- */
/** /**
* Test the given predicate against every entry in the Collection. * Test the given predicate against every entry in the Collection.
* @param {function(*, number, ActionCollection): boolean} predicate The predicate. * @param {function(*, number, ActionCollection): boolean} predicate The predicate.
* @returns {boolean} * @returns {boolean}
*/ */
every(predicate) { every(predicate) {
return this.reduce((pass, v, i) => pass && predicate(v, i, this), true); return this.reduce((pass, v, i) => pass && predicate(v, i, this), true);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
/** /**
* Convert the ActionCollection to an array of simple objects. * Convert the ActionCollection to an array of simple objects.
* @param {boolean} [source=true] Draw data for contained Documents from the underlying data source? * @param {boolean} [source=true] Draw data for contained Documents from the underlying data source?
* @returns {object[]} The extracted array of primitive objects. * @returns {object[]} The extracted array of primitive objects.
*/ */
toObject(source=true) { toObject(source = true) {
return this.map(doc => doc.toObject(source)); return this.map(doc => doc.toObject(source));
} }
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -61,17 +61,17 @@ export class ActionCollection extends Collection {
* Field that stores actions. * Field that stores actions.
*/ */
export class ActionsField extends MappingField { export class ActionsField extends MappingField {
constructor(options) { constructor(options) {
super(new ActionField(), options); super(new ActionField(), options);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
/** @inheritDoc */ /** @inheritDoc */
initialize(value, model, options) { initialize(value, model, options) {
const actions = Object.values(super.initialize(value, model, options)); const actions = Object.values(super.initialize(value, model, options));
return new ActionCollection(model, actions); return new ActionCollection(model, actions);
} }
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -81,7 +81,10 @@ export class ActionsField extends MappingField {
*/ */
export class ActionField extends foundry.data.fields.ObjectField { export class ActionField extends foundry.data.fields.ObjectField {
getModel(value) { getModel(value) {
return game.system.api.models.actions.actionsTypes[value.type] ?? game.system.api.models.actions.actionsTypes.attack; return (
game.system.api.models.actions.actionsTypes[value.type] ??
game.system.api.models.actions.actionsTypes.attack
);
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -122,138 +125,146 @@ export class ActionField extends foundry.data.fields.ObjectField {
export function ActionMixin(Base) { export function ActionMixin(Base) {
class Action extends Base { class Action extends Base {
static metadata = Object.freeze({ static metadata = Object.freeze({
name: "Action", name: 'Action',
label: "DAGGERHEART.GENERAL.Action.single", label: 'DAGGERHEART.GENERAL.Action.single',
sheetClass: DHActionConfig sheetClass: DHActionConfig
}); });
static _sheets = new Map(); static _sheets = new Map();
static get documentName() { static get documentName() {
return this.metadata.name; return this.metadata.name;
} }
get documentName() { get documentName() {
return this.constructor.documentName; return this.constructor.documentName;
} }
static defaultName() { static defaultName() {
return this.documentName; return this.documentName;
} }
get relativeUUID() { get relativeUUID() {
return `.Item.${this.item.id}.Action.${this.id}`; return `.Item.${this.item.id}.Action.${this.id}`;
} }
get uuid() { get uuid() {
return `${this.item.uuid}.${this.documentName}.${this.id}`; return `${this.item.uuid}.${this.documentName}.${this.id}`;
} }
get sheet() { get sheet() {
if(!this.constructor._sheets.has(this.uuid)) { if (!this.constructor._sheets.has(this.uuid)) {
const sheet = new this.constructor.metadata.sheetClass(this); const sheet = new this.constructor.metadata.sheetClass(this);
this.constructor._sheets.set(this.uuid, sheet); this.constructor._sheets.set(this.uuid, sheet);
} }
return this.constructor._sheets.get(this.uuid); return this.constructor._sheets.get(this.uuid);
} }
get inCollection() { get inCollection() {
return foundry.utils.getProperty(this.parent, this.systemPath) instanceof Collection; return foundry.utils.getProperty(this.parent, this.systemPath) instanceof Collection;
} }
static async create(data, operation={}) { static async create(data, operation = {}) {
const { parent, renderSheet } = operation; const { parent, renderSheet } = operation;
let { type } = data; let { type } = data;
if(!type || !game.system.api.models.actions.actionsTypes[type]) { if (!type || !game.system.api.models.actions.actionsTypes[type]) {
({ type } = ({ type } =
(await foundry.applications.api.DialogV2.input({ (await foundry.applications.api.DialogV2.input({
window: { title: 'Select Action Type' }, window: { title: game.i18n.localize('DAGGERHEART.CONFIG.SelectAction.selectType') },
position: { width: 300 }, position: { width: 300 },
classes: ['daggerheart', 'dh-style'], classes: ['daggerheart', 'dh-style'],
content: await foundry.applications.handlebars.renderTemplate( content: await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/actionTypes/actionType.hbs', 'systems/daggerheart/templates/actionTypes/actionType.hbs',
{ { types: CONFIG.DH.ACTIONS.actionTypes }
types: CONFIG.DH.ACTIONS.actionTypes, ),
itemName: parent.parent?.name ok: {
label: game.i18n.format('DOCUMENT.Create', {
type: game.i18n.localize('DAGGERHEART.GENERAL.Action.single')
})
} }
), })) ?? {});
ok: { }
label: game.i18n.format('DOCUMENT.Create', { if (!type) return;
type: game.i18n.localize('DAGGERHEART.GENERAL.Action.single')
}) const cls = game.system.api.models.actions.actionsTypes[type];
} const action = new cls(
})) ?? {}); {
} type,
if (!type) return; ...cls.getSourceConfig(parent)
},
const cls = game.system.api.models.actions.actionsTypes[type]; {
const action = new cls( parent
{ }
type, );
...cls.getSourceConfig(parent) const created = await parent.parent.update({ [`system.actions.${action.id}`]: action.toObject() });
}, const newAction = parent.actions.get(action.id);
{ if (!newAction) return null;
parent if (renderSheet) newAction.sheet.render({ force: true });
} return newAction;
);
const created = await parent.parent.update({ [`system.actions.${action.id}`]: action.toObject() });
const newAction = parent.actions.get(action.id);
if(!newAction) return null;
if( renderSheet ) newAction.sheet.render({ force: true });
return newAction;
} }
async update(updates, options={}) { async update(updates, options = {}) {
const path = this.inCollection ? `system.${this.systemPath}.${this.id}` : `system.${this.systemPath}`, const isSetting = !this.parent.parent;
result = await this.item.update({[path]: updates}, options); const basePath = isSetting ? this.systemPath : `system.${this.systemPath}`;
return this.inCollection ? foundry.utils.getProperty(result, `system.${this.systemPath}`).get(this.id) : foundry.utils.getProperty(result, `system.${this.systemPath}`); const path = this.inCollection ? `${basePath}.${this.id}` : basePath;
let result = null;
if (isSetting) {
await this.parent.updateSource({ [path]: updates }, options);
result = this.parent;
} else {
result = await this.item.update({ [path]: updates }, options);
}
return this.inCollection
? foundry.utils.getProperty(result, basePath).get(this.id)
: foundry.utils.getProperty(result, basePath);
} }
delete() { delete() {
if(!this.inCollection) return this.item; if (!this.inCollection) return this.item;
const action = foundry.utils.getProperty(this.item, `system.${this.systemPath}`)?.get(this.id); const action = foundry.utils.getProperty(this.item, `system.${this.systemPath}`)?.get(this.id);
if ( !action ) return this.item; if (!action) return this.item;
this.item.update({ [`system.${this.systemPath}.-=${this.id}`]: null }); this.item.update({ [`system.${this.systemPath}.-=${this.id}`]: null });
this.constructor._sheets.get(this.uuid)?.close(); this.constructor._sheets.get(this.uuid)?.close();
} }
async deleteDialog() { async deleteDialog() {
const confirmed = await foundry.applications.api.DialogV2.confirm({ const confirmed = await foundry.applications.api.DialogV2.confirm({
window: { window: {
title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', { title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', {
type: game.i18n.localize(`DAGGERHEART.GENERAL.Action.single`), type: game.i18n.localize(`DAGGERHEART.GENERAL.Action.single`),
name: this.name name: this.name
}) })
}, },
content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', {
name: this.name name: this.name
}) })
}); });
if (!confirmed) return; if (!confirmed) return;
return this.delete(); return this.delete();
} }
async toChat(origin) { async toChat(origin) {
const cls = getDocumentClass('ChatMessage'); const cls = getDocumentClass('ChatMessage');
const systemData = { const systemData = {
title: game.i18n.localize('DAGGERHEART.CONFIG.ActionType.action'), title: game.i18n.localize('DAGGERHEART.CONFIG.ActionType.action'),
origin: origin, origin: origin,
img: this.img, img: this.img,
name: this.name, name: this.name,
description: this.description, description: this.description,
actions: [] actions: []
}; };
const msg = { const msg = {
type: 'abilityUse', type: 'abilityUse',
user: game.user.id, user: game.user.id,
system: systemData, system: systemData,
content: await foundry.applications.handlebars.renderTemplate( content: await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/ui/chat/ability-use.hbs', 'systems/daggerheart/templates/ui/chat/ability-use.hbs',
systemData systemData
) )
}; };
cls.create(msg); cls.create(msg);
} }
} }

View file

@ -13,7 +13,8 @@ export default class DhAppearance extends foundry.abstract.DataModel {
dualityColorScheme: new fields.StringField({ dualityColorScheme: new fields.StringField({
required: true, required: true,
choices: DualityRollColor, choices: DualityRollColor,
initial: DualityRollColor.normal.value initial: DualityRollColor.normal.value,
label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.dualityColorScheme.label'
}), }),
diceSoNice: new fields.SchemaField({ diceSoNice: new fields.SchemaField({
hope: new fields.SchemaField({ hope: new fields.SchemaField({

View file

@ -1,4 +1,5 @@
import { defaultRestOptions } from '../../config/generalConfig.mjs'; import { defaultRestOptions } from '../../config/generalConfig.mjs';
import { ActionsField } from '../fields/actionField.mjs';
export default class DhHomebrew extends foundry.abstract.DataModel { export default class DhHomebrew extends foundry.abstract.DataModel {
static LOCALIZATION_PREFIXES = ['DAGGERHEART.SETTINGS.Homebrew']; // Doesn't work for some reason static LOCALIZATION_PREFIXES = ['DAGGERHEART.SETTINGS.Homebrew']; // Doesn't work for some reason
@ -61,7 +62,7 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
base64: false base64: false
}), }),
description: new fields.HTMLField(), description: new fields.HTMLField(),
actions: new fields.ArrayField(new fields.ObjectField()) actions: new ActionsField()
}), }),
{ initial: defaultRestOptions.longRest() } { initial: defaultRestOptions.longRest() }
) )
@ -78,7 +79,7 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
base64: false base64: false
}), }),
description: new fields.HTMLField(), description: new fields.HTMLField(),
actions: new fields.ArrayField(new fields.ObjectField()) actions: new ActionsField()
}), }),
{ initial: defaultRestOptions.shortRest() } { initial: defaultRestOptions.shortRest() }
) )

View file

@ -1,7 +1,15 @@
.sheet.daggerheart.dh-style { .daggerheart.dh-style {
.tab-form-footer { .tab-form-footer {
display: flex; display: flex;
padding: 0 10px; gap: 8px;
&.spaced {
margin-top: 8px;
}
&.padded {
padding: 0 10px;
}
button { button {
flex: 1; flex: 1;

View file

@ -29,6 +29,7 @@
} }
.settings-items { .settings-items {
width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px;

View file

@ -24,7 +24,7 @@
{{else}} {{else}}
{{> formula fields=fields.value.fields source=source.value target="value"}} {{> formula fields=fields.value.fields source=source.value target="value"}}
{{/if}} {{/if}}
{{formField fields.applyTo value=source.type name="healing.applyTo" localize=true}} {{formField fields.applyTo value=source.applyTo name="healing.applyTo" localize=true}}
</fieldset> </fieldset>
{{#*inline "formula"}} {{#*inline "formula"}}

View file

@ -5,110 +5,48 @@
{{formGroup settingFields.schema.fields.displayFear value=settingFields._source.displayFear localize=true}} {{formGroup settingFields.schema.fields.displayFear value=settingFields._source.displayFear localize=true}}
{{formGroup settingFields.schema.fields.showGenericStatusEffects value=settingFields._source.showGenericStatusEffects localize=true}} {{formGroup settingFields.schema.fields.showGenericStatusEffects value=settingFields._source.showGenericStatusEffects localize=true}}
{{formGroup settingFields.schema.fields.dualityColorScheme value=settingFields._source.dualityColorScheme localize=true}}
<fieldset> <fieldset>
<legend>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.duality"}}</legend> <legend>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.title"}}</legend>
{{formInput settingFields.schema.fields.dualityColorScheme value=settingFields._source.dualityColorScheme localize=true}}
<h2>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.title"}}</h2>
<div class="title-hint">{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.hint"}}</div> <div class="title-hint">{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.hint"}}</div>
<fieldset> <section class='tab-navigation'>
<legend>{{localize "DAGGERHEART.GENERAL.hope"}}</legend> <div class='navigation-container'>
<div class="navigation-inner-container">
<div class="field-section"> <line-div></line-div>
<div class="split-section"> <nav class='feature-tab sheet-tabs tabs' data-group='diceSoNice'>
<div class="label-container"> {{#each tabs as |tab|}}
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.foreground"}}</label> <a class='{{tab.id}} {{tab.cssClass}}' data-action='tab' data-group='{{tab.group}}' data-tab='{{tab.id}}'>
{{formInput settingFields.schema.fields.diceSoNice.fields.hope.fields.foreground value=settingFields._source.diceSoNice.hope.foreground localize=true}} {{localize tab.label}}
</div> </a>
<div class="label-container"> {{/each}}
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.background"}}</label> </nav>
{{formInput settingFields.schema.fields.diceSoNice.fields.hope.fields.background value=settingFields._source.diceSoNice.hope.background localize=true}} <line-div></line-div>
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.outline"}}</label>
{{formInput settingFields.schema.fields.diceSoNice.fields.hope.fields.outline value=settingFields._source.diceSoNice.hope.outline localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.edge"}}</label>
{{formInput settingFields.schema.fields.diceSoNice.fields.hope.fields.edge value=settingFields._source.diceSoNice.hope.edge localize=true}}
</div>
</div> </div>
</div> </div>
</fieldset> </section>
<fieldset>
<legend>{{localize "DAGGERHEART.GENERAL.fear"}}</legend>
<div class="field-section"> <div class="field-section">
<div class="split-section"> <div class="split-section">
<div class="label-container"> <div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.foreground"}}</label> <label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.foreground"}}</label>
{{formInput settingFields.schema.fields.diceSoNice.fields.fear.fields.foreground value=settingFields._source.diceSoNice.fear.foreground localize=true}} {{formInput diceTab.fields.foreground value=diceTab.source.foreground localize=true}}
</div> </div>
<div class="label-container"> <div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.background"}}</label> <label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.background"}}</label>
{{formInput settingFields.schema.fields.diceSoNice.fields.fear.fields.background value=settingFields._source.diceSoNice.fear.background localize=true}} {{formInput diceTab.fields.background value=diceTab.source.background localize=true}}
</div> </div>
<div class="label-container"> <div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.outline"}}</label> <label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.outline"}}</label>
{{formInput settingFields.schema.fields.diceSoNice.fields.fear.fields.outline value=settingFields._source.diceSoNice.fear.outline localize=true}} {{formInput diceTab.fields.outline value=diceTab.source.outline localize=true}}
</div> </div>
<div class="label-container"> <div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.edge"}}</label> <label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.edge"}}</label>
{{formInput settingFields.schema.fields.diceSoNice.fields.fear.fields.edge value=settingFields._source.diceSoNice.fear.edge localize=true}} {{formInput diceTab.fields.edge value=diceTab.source.edge localize=true}}
</div>
</div> </div>
</div> </div>
</fieldset> </div>
<fieldset>
<legend>{{localize "DAGGERHEART.GENERAL.Advantage.full"}}</legend>
<div class="field-section">
<div class="split-section">
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.foreground"}}</label>
{{formInput settingFields.schema.fields.diceSoNice.fields.advantage.fields.foreground value=settingFields._source.diceSoNice.advantage.foreground localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.background"}}</label>
{{formInput settingFields.schema.fields.diceSoNice.fields.advantage.fields.background value=settingFields._source.diceSoNice.advantage.background localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.outline"}}</label>
{{formInput settingFields.schema.fields.diceSoNice.fields.advantage.fields.outline value=settingFields._source.diceSoNice.advantage.outline localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.edge"}}</label>
{{formInput settingFields.schema.fields.diceSoNice.fields.advantage.fields.edge value=settingFields._source.diceSoNice.advantage.edge localize=true}}
</div>
</div>
</div>
</fieldset>
<fieldset>
<legend>{{localize "DAGGERHEART.GENERAL.Disadvantage.full"}}</legend>
<div class="field-section">
<div class="split-section">
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.foreground"}}</label>
{{formInput settingFields.schema.fields.diceSoNice.fields.disadvantage.fields.foreground value=settingFields._source.diceSoNice.disadvantage.foreground localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.background"}}</label>
{{formInput settingFields.schema.fields.diceSoNice.fields.disadvantage.fields.background value=settingFields._source.diceSoNice.disadvantage.background localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.outline"}}</label>
{{formInput settingFields.schema.fields.diceSoNice.fields.disadvantage.fields.outline value=settingFields._source.diceSoNice.disadvantage.outline localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.edge"}}</label>
{{formInput settingFields.schema.fields.diceSoNice.fields.disadvantage.fields.edge value=settingFields._source.diceSoNice.disadvantage.edge localize=true}}
</div>
</div>
</div>
</fieldset>
</fieldset> </fieldset>
<footer class="form-footer"> <footer class="form-footer">

View file

@ -1,17 +0,0 @@
<div>
<div class="form-group">
<label>{{localize "Icon"}}</label>
<div class="form-field">
<input type="text" name="icon" value="{{icon}}" />
</div>
</div>
<fieldset>
<legend>{{localize "Description"}}</legend>
<prose-mirror name="description" value="{{description}}">
{{{ enrichedDescription }}}
</prose-mirror>
</fieldset>
</div>

View file

@ -0,0 +1,15 @@
<section
class='tab {{tabs.actions.cssClass}} {{tabs.actions.id}}'
data-tab='{{tabs.actions.id}}'
data-group='{{tabs.actions.group}}'
>
<fieldset class="one-column">
<legend>{{localize "DAGGERHEART.GENERAL.Action.plural"}} <a><i class="fa-solid fa-plus icon-button" data-action="addItem"></i></a></legend>
<div class="settings-items">
{{#each move.actions as |action|}}
{{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" id=action.id }}
{{/each}}
</div>
</fieldset>
</section>

View file

@ -1,3 +1,4 @@
<section class='tab-form-footer'> <section class='tab-form-footer spaced'>
<button data-action="close">{{localize "Cancel"}}</button>
<button data-action="saveForm"><i class="fa-solid fa-floppy-disk"></i> {{localize "Save"}}</button> <button data-action="saveForm"><i class="fa-solid fa-floppy-disk"></i> {{localize "Save"}}</button>
</section> </section>

View file

@ -1,6 +1,6 @@
<header class='settings-item-header'> <header class='settings-item-header'>
<img class='profile' src='{{this.img}}' data-action='editImage' data-edit='img' /> <img class='profile' src='{{move.img}}' data-action='editImage' data-edit='img' />
<div class='item-info'> <div class='item-info'>
<h1 class='item-name'><input type='text' name='name' value='{{this.name}}' /></h1> <h1 class='item-name'><input type='text' name='name' value='{{move.name}}' /></h1>
</div> </div>
</header> </header>

View file

@ -0,0 +1,19 @@
<section
class='tab {{tabs.main.cssClass}} {{tabs.main.id}}'
data-tab='{{tabs.main.id}}'
data-group='{{tabs.main.group}}'
>
<fieldset class="one-column">
<legend>{{localize "Icon"}}</legend>
<input type="text" name="icon" value="{{move.icon}}" />
</fieldset>
<fieldset class="one-column">
<legend>{{localize "Description"}}</legend>
<prose-mirror name="description" value="{{move.description}}">
{{{ move.enrichedDescription }}}
</prose-mirror>
</fieldset>
</section>

View file

@ -12,6 +12,6 @@
</fieldset> </fieldset>
<fieldset class="action-category"> <fieldset class="action-category">
<legend>{{localize "DAGGERHEART.GENERAL.description"}}</legend> <legend>{{localize "DAGGERHEART.GENERAL.description"}}</legend>
{{formInput fields.description value=source.description name="description" }} {{formInput fields.description value=source.description enriched=source.description name="description" toggled=true }}
</fieldset> </fieldset>
</section> </section>

View file

@ -9,7 +9,7 @@
<div class="resources-section"> <div class="resources-section">
<div class="status-bar"> <div class="status-bar">
<div class='status-value'> <div class='status-value'>
<p><input class="bar-input" name="system.resources.hitPoints.value" <p><input class="bar-input" name="system.resources.hitPoints.value" min="0" max='{{source.system.resources.hitPoints.max}}'
value="{{source.system.resources.hitPoints.value}}" type="number"></p> value="{{source.system.resources.hitPoints.value}}" type="number"></p>
<p>/</p> <p>/</p>
<p class="bar-label">{{source.system.resources.hitPoints.max}}</p> <p class="bar-label">{{source.system.resources.hitPoints.max}}</p>
@ -23,7 +23,7 @@
</div> </div>
<div class="status-bar"> <div class="status-bar">
<div class='status-value'> <div class='status-value'>
<p><input class="bar-input" name="system.resources.stress.value" <p><input class="bar-input" name="system.resources.stress.value" min="0" max='{{source.system.resources.stress.max}}'
value="{{source.system.resources.stress.value}}" type="number"></p> value="{{source.system.resources.stress.value}}" type="number"></p>
<p>/</p> <p>/</p>
<p class="bar-label">{{source.system.resources.stress.max}}</p> <p class="bar-label">{{source.system.resources.stress.max}}</p>

View file

@ -19,7 +19,7 @@
</div> </div>
<div class="status-bar"> <div class="status-bar">
<div class='status-value'> <div class='status-value'>
<p><input class="bar-input" name="system.resources.stress.value" value="{{document.system.resources.stress.value}}" type="number"></p> <p><input class="bar-input" name="system.resources.stress.value" value="{{document.system.resources.stress.value}}" min="0" max='{{document.system.resources.stress.max}}' type="number"></p>
<p>/</p> <p>/</p>
<p class="bar-label">{{document.system.resources.stress.max}}</p> <p class="bar-label">{{document.system.resources.stress.max}}</p>
</div> </div>

View file

@ -1,3 +1,3 @@
<section class='tab-form-footer'> <section class='tab-form-footer padded'>
<button type="submit"><i class="fa-solid fa-floppy-disk"></i> {{localize "Save"}}</button> <button type="submit"><i class="fa-solid fa-floppy-disk"></i> {{localize "Save"}}</button>
</section> </section>