DiceSoNice Animation Overrides (#1726)

* .

* .

* Fixed hope/fear animation input
This commit is contained in:
WBHarry 2026-03-12 23:34:13 +01:00 committed by GitHub
parent dd376705a9
commit 308a04e6c5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 242 additions and 95 deletions

View file

@ -2752,9 +2752,11 @@
"label": "Appearance Settings", "label": "Appearance Settings",
"hint": "Modify the look of various parts of the system", "hint": "Modify the look of various parts of the system",
"duality": "Duality Rolls", "duality": "Duality Rolls",
"globalSettings": "GM Settings",
"globalAnimations": "Global Animations", "globalAnimations": "Global Animations",
"diceAppearance": "Dice Appearance", "diceAppearance": "Dice Appearance",
"animations": "Animations", "animations": "Animations",
"defaultAnimations": "Set Animations As Player Defaults",
"previewAnimation": "Preview Animation", "previewAnimation": "Preview Animation",
"diceSoNice": { "diceSoNice": {
"title": "Dice So Nice", "title": "Dice So Nice",

View file

@ -50,7 +50,8 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
{ id: 'hope', label: 'DAGGERHEART.GENERAL.hope' }, { id: 'hope', label: 'DAGGERHEART.GENERAL.hope' },
{ id: 'fear', label: 'DAGGERHEART.GENERAL.fear' }, { id: 'fear', label: 'DAGGERHEART.GENERAL.fear' },
{ id: 'advantage', label: 'DAGGERHEART.GENERAL.Advantage.full' }, { id: 'advantage', label: 'DAGGERHEART.GENERAL.Advantage.full' },
{ id: 'disadvantage', label: 'DAGGERHEART.GENERAL.Disadvantage.full' } { id: 'disadvantage', label: 'DAGGERHEART.GENERAL.Disadvantage.full' },
{ id: 'general', label: 'DAGGERHEART.GENERAL.general' }
], ],
initial: 'hope' initial: 'hope'
} }
@ -70,6 +71,14 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
} }
} }
_attachPartListeners(partId, htmlElement, options) {
super._attachPartListeners(partId, htmlElement, options);
htmlElement
.querySelector('.default-animations-input')
?.addEventListener('change', this.toggleSFXOverride.bind(this));
}
/** @inheritdoc */ /** @inheritdoc */
_configureRenderParts(options) { _configureRenderParts(options) {
const parts = super._configureRenderParts(options); const parts = super._configureRenderParts(options);
@ -83,15 +92,20 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
/**@inheritdoc */ /**@inheritdoc */
async _prepareContext(options) { async _prepareContext(options) {
const context = await super._prepareContext(options); const context = await super._prepareContext(options);
if (options.isFirstRender) if (options.isFirstRender) {
this.setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance); this.setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance);
this.globalOverrides = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.GlobalOverrides);
}
context.setting = this.setting; context.setting = this.setting;
context.globalOverrides = this.globalOverrides;
context.fields = this.setting.schema.fields; context.fields = this.setting.schema.fields;
context.tabs = this._prepareTabs('general'); context.tabs = this._prepareTabs('general');
context.dsnTabs = this._prepareTabs('diceSoNice'); context.dsnTabs = this._prepareTabs('diceSoNice');
context.isGM = game.user.isGM;
return context; return context;
} }
@ -162,6 +176,7 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
(acc, key) => ({ (acc, key) => ({
...acc, ...acc,
[key]: { [key]: {
diceTab: key !== 'general',
values: this.setting.diceSoNice[key], values: this.setting.diceSoNice[key],
fields: this.setting.schema.getField(`diceSoNice.${key}`).fields, fields: this.setting.schema.getField(`diceSoNice.${key}`).fields,
animations: ['hope', 'fear'].includes(key) ? getAnimationsOptions(key) : {} animations: ['hope', 'fear'].includes(key) ? getAnimationsOptions(key) : {}
@ -188,6 +203,12 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
/* -------------------------------------------- */ /* -------------------------------------------- */
async toggleSFXOverride(event) {
await this.globalOverrides.diceSoNiceSFXUpdate(this.setting, event.target.checked);
this.globalOverrides = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.GlobalOverrides);
this.render();
}
/** /**
* Submit the configuration form. * Submit the configuration form.
* @this {DHAppearanceSettings} * @this {DHAppearanceSettings}

View file

@ -699,7 +699,7 @@ export const daggerheartDiceAnimationEvents = {
}; };
const getDiceSoNiceSFX = sfxOptions => { const getDiceSoNiceSFX = sfxOptions => {
const { diceSoNice } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance); const diceSoNice = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance).diceSoNiceData;
const criticalAnimationData = diceSoNice.sfx.critical; const criticalAnimationData = diceSoNice.sfx.critical;
if (sfxOptions.critical && criticalAnimationData.class) { if (sfxOptions.critical && criticalAnimationData.class) {
return { return {
@ -753,7 +753,7 @@ export const getDiceSoNicePresets = async (
advantageFaces = 'd6', advantageFaces = 'd6',
disadvantageFaces = 'd6' disadvantageFaces = 'd6'
) => { ) => {
const { diceSoNice } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance); const diceSoNice = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance).diceSoNiceData;
const { isCritical, withHope, withFear } = result; const { isCritical, withHope, withFear } = result;

View file

@ -26,6 +26,7 @@ export const gameSettings = {
Metagaming: 'Metagaming', Metagaming: 'Metagaming',
Homebrew: 'Homebrew', Homebrew: 'Homebrew',
appearance: 'Appearance', appearance: 'Appearance',
GlobalOverrides: 'GlobalOverrides',
variantRules: 'VariantRules', variantRules: 'VariantRules',
Resources: { Resources: {
Fear: 'ResourcesFear' Fear: 'ResourcesFear'

View file

@ -1,19 +1,19 @@
export default class DhAppearance extends foundry.abstract.DataModel { export default class DhAppearance extends foundry.abstract.DataModel {
static LOCALIZATION_PREFIXES = ['DAGGERHEART.SETTINGS.Appearance']; static LOCALIZATION_PREFIXES = ['DAGGERHEART.SETTINGS.Appearance'];
static sfxSchema = () =>
new foundry.data.fields.SchemaField({
class: new foundry.data.fields.StringField({
nullable: true,
initial: null,
blank: true,
choices: CONFIG.DH.GENERAL.diceSoNiceSFXClasses
})
});
static defineSchema() { static defineSchema() {
const { StringField, ColorField, BooleanField, SchemaField } = foundry.data.fields; const { StringField, ColorField, BooleanField, SchemaField } = foundry.data.fields;
const sfxSchema = () =>
new SchemaField({
class: new StringField({
nullable: true,
initial: null,
blank: true,
choices: CONFIG.DH.GENERAL.diceSoNiceSFXClasses
})
});
// helper to create dice style schema // helper to create dice style schema
const diceStyle = ({ fg, bg, outline, edge }) => const diceStyle = ({ fg, bg, outline, edge }) =>
new SchemaField({ new SchemaField({
@ -27,7 +27,7 @@ export default class DhAppearance extends foundry.abstract.DataModel {
system: new StringField({ initial: 'standard', required: true, blank: false }), system: new StringField({ initial: 'standard', required: true, blank: false }),
font: new StringField({ initial: 'auto', required: true, blank: false }), font: new StringField({ initial: 'auto', required: true, blank: false }),
sfx: new SchemaField({ sfx: new SchemaField({
higher: sfxSchema() higher: DhAppearance.sfxSchema()
}) })
}); });
@ -45,7 +45,7 @@ export default class DhAppearance extends foundry.abstract.DataModel {
advantage: diceStyle({ fg: '#ffffff', bg: '#008000', outline: '#000000', edge: '#ffffff' }), advantage: diceStyle({ fg: '#ffffff', bg: '#008000', outline: '#000000', edge: '#ffffff' }),
disadvantage: diceStyle({ fg: '#000000', bg: '#b30000', outline: '#ffffff', edge: '#000000' }), disadvantage: diceStyle({ fg: '#000000', bg: '#b30000', outline: '#ffffff', edge: '#000000' }),
sfx: new SchemaField({ sfx: new SchemaField({
critical: sfxSchema() critical: DhAppearance.sfxSchema()
}) })
}), }),
extendCharacterDescriptions: new BooleanField(), extendCharacterDescriptions: new BooleanField(),
@ -81,4 +81,48 @@ export default class DhAppearance extends foundry.abstract.DataModel {
showGenericStatusEffects: new BooleanField({ initial: true }) showGenericStatusEffects: new BooleanField({ initial: true })
}; };
} }
get diceSoNiceData() {
const globalOverrides = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.GlobalOverrides);
const getSFX = (baseClientData, overrideKey) => {
if (!globalOverrides.diceSoNice.sfx.overrideEnabled) return baseClientData;
const overrideData = globalOverrides.diceSoNice.sfx[overrideKey];
const clientData = foundry.utils.deepClone(baseClientData);
return Object.keys(clientData).reduce((acc, key) => {
const data = clientData[key];
acc[key] = Object.keys(data).reduce((acc, dataKey) => {
const value = data[dataKey];
acc[dataKey] = value ? value : overrideData[key][dataKey];
return acc;
}, {});
return acc;
}, {});
};
return {
...this.diceSoNice,
sfx: getSFX(this.diceSoNice.sfx, 'global'),
hope: {
...this.diceSoNice.hope,
sfx: getSFX(this.diceSoNice.hope.sfx, 'hope')
},
fear: {
...this.diceSoNice.fear,
sfx: getSFX(this.diceSoNice.fear.sfx, 'fear')
}
};
}
/** Invoked by the setting when data changes */
handleChange() {
if (this.displayFear) {
if (ui.resources) {
if (this.displayFear === 'hide') ui.resources.close({ allowed: true });
else ui.resources.render({ force: true });
}
}
const globalOverrides = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.GlobalOverrides);
globalOverrides.diceSoNiceSFXUpdate(this);
}
} }

View file

@ -0,0 +1,55 @@
import DhAppearance from './Appearance.mjs';
/**
* A setting to handle cases where we want to allow the GM to set a global default for client settings.
*/
export default class DhGlobalOverrides extends foundry.abstract.DataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
diceSoNice: new fields.SchemaField({
sfx: new fields.SchemaField({
overrideEnabled: new fields.BooleanField(),
global: new fields.SchemaField({
critical: DhAppearance.sfxSchema()
}),
hope: new fields.SchemaField({
higher: DhAppearance.sfxSchema()
}),
fear: new fields.SchemaField({
higher: DhAppearance.sfxSchema()
})
})
})
};
}
async diceSoNiceSFXUpdate(appearanceSettings, enabled) {
if (!game.user.isGM) return;
const newEnabled = enabled !== undefined ? enabled : this.diceSoNice.sfx.overrideEnabled;
if (newEnabled) {
const newOverrides = foundry.utils.mergeObject(this.toObject(), {
diceSoNice: {
sfx: {
overrideEnabled: true,
global: appearanceSettings.diceSoNice.sfx,
hope: appearanceSettings.diceSoNice.hope.sfx,
fear: appearanceSettings.diceSoNice.fear.sfx
}
}
});
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.GlobalOverrides, newOverrides);
} else {
const newOverrides = {
...this.toObject(),
diceSoNice: {
sfx: {
overrideEnabled: false
}
}
};
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.GlobalOverrides, newOverrides);
}
}
}

View file

@ -3,3 +3,4 @@ export { default as DhAutomation } from './Automation.mjs';
export { default as DhHomebrew } from './Homebrew.mjs'; export { default as DhHomebrew } from './Homebrew.mjs';
export { default as DhMetagaming } from './Metagaming.mjs'; export { default as DhMetagaming } from './Metagaming.mjs';
export { default as DhVariantRules } from './VariantRules.mjs'; export { default as DhVariantRules } from './VariantRules.mjs';
export { default as DhGlobalOverrides } from './GlobalOverrides.mjs';

View file

@ -46,6 +46,7 @@ export const preloadHandlebarsTemplates = async function () {
'systems/daggerheart/templates/ui/chat/parts/target-part.hbs', 'systems/daggerheart/templates/ui/chat/parts/target-part.hbs',
'systems/daggerheart/templates/ui/chat/parts/button-part.hbs', 'systems/daggerheart/templates/ui/chat/parts/button-part.hbs',
'systems/daggerheart/templates/ui/itemBrowser/itemContainer.hbs', 'systems/daggerheart/templates/ui/itemBrowser/itemContainer.hbs',
'systems/daggerheart/templates/scene/dh-config.hbs' 'systems/daggerheart/templates/scene/dh-config.hbs',
'systems/daggerheart/templates/settings/appearance-settings/diceSoNiceTab.hbs'
]); ]);
}; };

View file

@ -1,6 +1,13 @@
import { defaultLevelTiers, DhLevelTiers } from '../data/levelTier.mjs'; import { defaultLevelTiers, DhLevelTiers } from '../data/levelTier.mjs';
import DhCountdowns from '../data/countdowns.mjs'; import DhCountdowns from '../data/countdowns.mjs';
import { DhAppearance, DhAutomation, DhHomebrew, DhMetagaming, DhVariantRules } from '../data/settings/_module.mjs'; import {
DhAppearance,
DhAutomation,
DhGlobalOverrides,
DhHomebrew,
DhMetagaming,
DhVariantRules
} from '../data/settings/_module.mjs';
import { import {
DhAppearanceSettings, DhAppearanceSettings,
DhAutomationSettings, DhAutomationSettings,
@ -54,17 +61,18 @@ const registerMenuSettings = () => {
} }
}); });
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.GlobalOverrides, {
scope: 'world',
config: false,
type: DhGlobalOverrides
});
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance, { game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance, {
scope: 'client', scope: 'client',
config: false, config: false,
type: DhAppearance, type: DhAppearance,
onChange: value => { onChange: value => {
if (value.displayFear) { value.handleChange();
if (ui.resources) {
if (value.displayFear === 'hide') ui.resources.close({ allowed: true });
else ui.resources.render({ force: true });
}
}
} }
}); });
}; };

View file

@ -1,5 +1,5 @@
.daggerheart.dh-style.setting.appearance-settings { .daggerheart.dh-style.setting.appearance-settings {
.tab.active[data-tab="diceSoNice"] { .tab.active[data-tab='diceSoNice'] {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;

View file

@ -1,13 +1,6 @@
<section class="tab {{#if tab.active}} active{{/if}}" data-group="{{tab.group}}" data-tab="{{tab.id}}"> <section class="tab {{#if tab.active}} active{{/if}}" data-group="{{tab.group}}" data-tab="{{tab.id}}">
<div class="title-hint">{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.hint"}}</div> <div class="title-hint">{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.hint"}}</div>
<h3>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.globalAnimations"}}</h3>
<div class="label-container full-width">
<label>{{localize "DAGGERHEART.CONFIG.DaggerheartDiceAnimationEvents.critical.name"}}</label>
{{formInput fields.diceSoNice.fields.sfx.fields.critical.fields.class value=setting.diceSoNice.sfx.critical.class blank="" localize=true}}
</div>
<section class='tab-navigation'> <section class='tab-navigation'>
<div class='navigation-container'> <div class='navigation-container'>
<div class="navigation-inner-container"> <div class="navigation-inner-container">
@ -24,68 +17,27 @@
</div> </div>
</section> </section>
{{#each dsnTabs as |dsnTab|}} {{#each dsnTabs as |dsnTab|}}
<section class="tab {{#if dsnTab.active}} active{{/if}}" data-group="{{dsnTab.group}}" data-tab="{{dsnTab.id}}"> <section class="tab {{#if dsnTab.active}} active{{/if}}" data-group="{{dsnTab.group}}" data-tab="{{dsnTab.id}}">
<div class="field-section"> {{#if dsnTab.diceTab}}
<h3>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceAppearance"}}</h3> {{> "systems/daggerheart/templates/settings/appearance-settings/diceSoNiceTab.hbs" dsnTab }}
{{else}}
<div class="label-container full-width"> <div class="field-section">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.system"}}</label> {{#if ../isGM}}
{{formInput fields.system value=values.system localize=true choices=@root.diceSoNiceSystems}} <h3>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.globalSettings"}}</h3>
</div> <div class="label-container">
<div class="split-section"> <label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.defaultAnimations"}}</label>
<div class="label-container"> <input type="checkbox" class="default-animations-input" {{checked ../globalOverrides.diceSoNice.sfx.overrideEnabled}} />
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.foreground"}}</label> </div>
{{formInput fields.foreground value=values.foreground localize=true}} {{/if}}
</div> <h3>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.globalAnimations"}}</h3>
<div class="label-container"> <div class="split-section">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.background"}}</label> <div class="label-container full-width">
{{formInput fields.background value=values.background localize=true}} <label>{{localize "DAGGERHEART.CONFIG.DaggerheartDiceAnimationEvents.critical.name"}}</label>
</div> {{formInput ../fields.diceSoNice.fields.sfx.fields.critical.fields.class value=../setting.diceSoNice.sfx.critical.class blank="" localize=true}}
<div class="label-container"> </div>
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.outline"}}</label> </div>
{{formInput fields.outline value=values.outline localize=true}} </div>
</div> {{/if}}
<div class="label-container"> </section>
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.edge"}}</label>
{{formInput fields.edge value=values.edge localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.colorset"}}</label>
{{formInput fields.colorset value=values.colorset choices=@root.diceSoNiceColorsets localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.texture"}}</label>
{{formInput fields.texture value=values.texture choices=@root.diceSoNiceTextures localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.material"}}</label>
{{formInput fields.material value=values.material choices=@root.diceSoNiceMaterials localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.font"}}</label>
{{formInput fields.font value=values.font choices=@root.diceSoNiceFonts localize=true}}
</div>
</div>
{{#if dsnTab.animations}}
<h3>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.animations"}}</h3>
<div class="label-container full-width">
<label>{{localize "DAGGERHEART.CONFIG.DaggerheartDiceAnimationEvents.higher.name"}}</label>
{{formInput fields.sfx.fields.higher.fields.class value=values.sfx.higher.class blank="" localize=true}}
</div>
{{/if}}
<div class="diceSoNice-footer">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.previewAnimation"}}</label>
<select name="{{concat dsnTab.id "PreviewAnimation"}}">
{{selectOptions @root.animationEvents selected=@root.previewAnimation blank="" localize=true }}
</select>
<button type="button" data-action="preview" data-key="{{dsnTab.id}}">
<i class="fa-solid fa-dice"></i>
<span>{{localize "DAGGERHEART.GENERAL.preview"}}</span>
</button>
</div>
</div>
</section>
{{/each}} {{/each}}
</section> </section>

View file

@ -0,0 +1,62 @@
<div class="field-section">
<h3>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceAppearance"}}</h3>
<div class="label-container full-width">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.system"}}</label>
{{formInput fields.system value=values.system localize=true choices=@root.diceSoNiceSystems}}
</div>
<div class="split-section">
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.foreground"}}</label>
{{formInput fields.foreground value=values.foreground localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.background"}}</label>
{{formInput fields.background value=values.background localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.outline"}}</label>
{{formInput fields.outline value=values.outline localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.edge"}}</label>
{{formInput fields.edge value=values.edge localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.colorset"}}</label>
{{formInput fields.colorset value=values.colorset choices=@root.diceSoNiceColorsets localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.texture"}}</label>
{{formInput fields.texture value=values.texture choices=@root.diceSoNiceTextures localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.material"}}</label>
{{formInput fields.material value=values.material choices=@root.diceSoNiceMaterials localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.font"}}</label>
{{formInput fields.font value=values.font choices=@root.diceSoNiceFonts localize=true}}
</div>
</div>
{{#if animations}}
<h3>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.animations"}}</h3>
<div class="label-container full-width">
<label>{{localize "DAGGERHEART.CONFIG.DaggerheartDiceAnimationEvents.higher.name"}}</label>
{{formInput fields.sfx.fields.higher.fields.class value=values.sfx.higher.class blank="" localize=true}}
</div>
{{/if}}
<div class="diceSoNice-footer">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.previewAnimation"}}</label>
<select name="{{concat id "PreviewAnimation"}}">
{{selectOptions @root.animationEvents selected=@root.previewAnimation blank="" localize=true }}
</select>
<button type="button" data-action="preview" data-key="{{id}}">
<i class="fa-solid fa-dice"></i>
<span>{{localize "DAGGERHEART.GENERAL.preview"}}</span>
</button>
</div>
</div>