Merged with development

This commit is contained in:
WBHarry 2025-09-06 19:14:02 +02:00
commit 0712ea70ae
24 changed files with 587 additions and 368 deletions

View file

@ -170,7 +170,9 @@
"hint": "Add single words or short text as reminders and hints of what a character has advantage on."
},
"age": "Age",
"backgroundQuestions": "Backgrounds",
"companionFeatures": "Companion Features",
"connections": "Connections",
"contextMenu": {
"consume": "Consume Item",
"equip": "Equip",
@ -267,7 +269,8 @@
"experience": "Experience",
"traits": "Traits",
"domainCards": "Domain Cards",
"equipment": "Equipment"
"equipment": "Equipment",
"story": "Story"
},
"ancestryNamePlaceholder": "Your ancestry's name",
"buttonTitle": "Character Setup",
@ -288,6 +291,7 @@
"selectSubclass": "Select Subclass",
"startingItems": "Starting Items",
"story": "Story",
"storyExplanation": "Select which background and connection prompts you want to copy into your character's background.",
"suggestedArmor": "Suggested Armor",
"suggestedPrimaryWeapon": "Suggested Primary Weapon",
"suggestedSecondaryWeapon": "Suggested Secondary Weapon",
@ -1913,7 +1917,8 @@
"roll": "Roll",
"rules": "Rules",
"types": "Types",
"itemFeatures": "Item Features"
"itemFeatures": "Item Features",
"questions": "Questions"
},
"Tiers": {
"singular": "Tier",
@ -2150,20 +2155,43 @@
"SETTINGS": {
"Appearance": {
"FIELDS": {
"displayFear": { "label": "Fear Display" },
"dualityColorScheme": { "label": "Chat Style" },
"hideAttribution": { "label": "Hide Attribution" },
"displayFear": {
"label": "Display Fear"
},
"showGenericStatusEffects": {
"label": "Show Foundry Status Effects"
},
"hideAttribution": {
"label": "Hide Attribution"
},
"expandedTitle": "Auto-expand Descriptions",
"extendCharacterDescriptions": { "label": "Characters" },
"extendAdversaryDescriptions": { "label": "Adversaries" },
"extendEnvironmentDescriptions": { "label": "Environments" },
"extendItemDescriptions": { "label": "Items" },
"expandRollMessage": "Auto-expand Message Sections",
"expandRollMessageDesc": { "label": "Description" },
"expandRollMessageRoll": { "label": "Formula" },
"expandRollMessageDamage": { "label": "Damage/Healing" },
"expandRollMessageTarget": { "label": "Target" },
"showGenericStatusEffects": { "label": "Show Foundry Status Effects" }
"extendCharacterDescriptions": {
"label": "Characters"
},
"extendAdversaryDescriptions": {
"label": "Adversaries"
},
"extendEnvironmentDescriptions": {
"label": "Environments"
},
"extendItemDescriptions": {
"label": "Items"
},
"expandRollMessage": {
"title": "Auto-expand Message Sections",
"desc": {
"label": "Description"
},
"roll": {
"label": "Formula"
},
"damage": {
"label": "Damage/Healing"
},
"target": {
"label": "Target"
}
}
},
"fearDisplay": {
"token": "Tokens",
@ -2310,10 +2338,8 @@
"hint": "System ruler setup for displaying ranges in Daggerheart"
},
"appearance": {
"title": "Appearance Settings",
"label": "Appearance Settings",
"hint": "Modify the look of various parts of the system",
"name": "Appearance Settings",
"duality": "Duality Rolls",
"diceSoNice": {
"title": "Dice So Nice",

View file

@ -3,43 +3,48 @@ import { getDiceSoNicePreset } from '../../config/generalConfig.mjs';
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
/**
* @import {ApplicationClickAction} from "@client/applications/_types.mjs"
*/
export default class DHAppearanceSettings extends HandlebarsApplicationMixin(ApplicationV2) {
constructor() {
super({});
this.settings = new DhAppearance(
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance).toObject()
);
}
get title() {
return game.i18n.localize('DAGGERHEART.SETTINGS.Menu.title');
}
/**@inheritdoc */
static DEFAULT_OPTIONS = {
tag: 'form',
id: 'daggerheart-appearance-settings',
classes: ['daggerheart', 'dialog', 'dh-style', 'setting'],
position: { width: '600', height: 'auto' },
window: {
title: 'DAGGERHEART.SETTINGS.Menu.title',
icon: 'fa-solid fa-gears'
},
actions: {
reset: this.reset,
save: this.save,
preview: this.preview
reset: DHAppearanceSettings.#onReset,
preview: DHAppearanceSettings.#onPreview
},
form: { handler: this.updateData, submitOnChange: true }
form: {
closeOnSubmit: true,
handler: DHAppearanceSettings.#onSubmit
}
};
static PARTS = {
main: {
template: 'systems/daggerheart/templates/settings/appearance-settings.hbs'
}
header: { template: 'systems/daggerheart/templates/settings/appearance-settings/header.hbs' },
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
main: { template: 'systems/daggerheart/templates/settings/appearance-settings/main.hbs' },
diceSoNice: { template: 'systems/daggerheart/templates/settings/appearance-settings/diceSoNice.hbs' },
footer: { template: 'templates/generic/form-footer.hbs' }
};
/** @inheritdoc */
static TABS = {
general: {
tabs: [
{ id: 'main', label: 'DAGGERHEART.GENERAL.Tabs.general' },
{ id: 'diceSoNice', label: 'DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.title' }
],
initial: 'main'
},
diceSoNice: {
tabs: [
{ id: 'hope', label: 'DAGGERHEART.GENERAL.hope' },
@ -51,79 +56,149 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
}
};
changeTab(tab, group, options) {
super.changeTab(tab, group, options);
/**@type {DhAppearance}*/
setting;
this.render();
static #localized = false;
/** @inheritDoc */
async _preFirstRender(_context, _options) {
await super._preFirstRender(_context, _options);
if (!DHAppearanceSettings.#localized) {
foundry.helpers.Localization.localizeDataModel(this.setting.constructor);
DHAppearanceSettings.#localized = true;
}
}
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
context.settingFields = this.settings;
context.showDiceSoNice = game.modules.get('dice-so-nice')?.active;
if (game.dice3d) {
context.diceSoNiceTextures = game.dice3d.exports.TEXTURELIST;
context.diceSoNiceColorsets = game.dice3d.exports.COLORSETS;
context.diceSoNiceMaterials = Object.keys(game.dice3d.DiceFactory.material_options).map(key => ({
key: key,
name: `DICESONICE.Material${key.capitalize()}`
}));
context.diceSoNiceSystems = [];
for (const [key, system] of game.dice3d.DiceFactory.systems.entries()) {
context.diceSoNiceSystems.push({ key, name: system.name });
}
/** @inheritdoc */
_configureRenderParts(options) {
const parts = super._configureRenderParts(options);
if (!game.modules.get('dice-so-nice')?.active) {
delete parts.diceSoNice;
delete parts.tabs;
}
return parts;
}
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
};
/**@inheritdoc */
async _prepareContext(options) {
const context = await super._prepareContext(options);
if (options.isFirstRender)
this.setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance);
context.setting = this.setting;
context.fields = this.setting.schema.fields;
context.tabs = this._prepareTabs('general');
context.dsnTabs = this._prepareTabs('diceSoNice');
return context;
}
static async updateData(event, element, formData) {
const updatedSettings = foundry.utils.expandObject(formData.object);
await this.settings.updateSource(updatedSettings);
this.render();
/**@inheritdoc */
async _preparePartContext(partId, context, options) {
const partContext = await super._preparePartContext(partId, context, options);
if (partId in context.tabs) partContext.tab = partContext.tabs[partId];
switch (partId) {
case 'diceSoNice':
await this.prepareDiceSoNiceContext(partContext);
break;
case 'footer':
partContext.buttons = [
{ type: 'button', action: 'reset', icon: 'fa-solid fa-arrow-rotate-left', label: 'Reset' },
{ type: 'submit', icon: 'fa-solid fa-floppy-disk', label: 'Save Changes' }
];
break;
}
return partContext;
}
static async preview() {
const source = this.settings._source.diceSoNice[this.tabGroups.diceSoNice];
let faces = 'd12';
switch (this.tabGroups.diceSoNice) {
case 'advantage':
case 'disadvantage':
faces = 'd6';
}
const preset = await getDiceSoNicePreset(source, faces);
const diceSoNiceRoll = await new Roll(`1${faces}`).evaluate();
/**
* Prepare render context for the DSN part.
* @param {ApplicationRenderContext} context
* @returns {Promise<void>}
* @protected
*/
async prepareDiceSoNiceContext(context) {
context.diceSoNiceTextures = Object.entries(game.dice3d.exports.TEXTURELIST).reduce(
(acc, [k, v]) => ({
...acc,
[k]: v.name
}),
{}
);
context.diceSoNiceColorsets = Object.values(game.dice3d.exports.COLORSETS).reduce(
(acc, v) => ({
...acc,
[v.id]: v.description
}),
{}
);
context.diceSoNiceMaterials = Object.keys(game.dice3d.DiceFactory.material_options).reduce(
(acc, key) => ({
...acc,
[key]: `DICESONICE.Material${key.capitalize()}`
}),
{}
);
context.diceSoNiceSystems = Object.fromEntries(
[...game.dice3d.DiceFactory.systems].map(([k, v]) => [k, v.name])
);
foundry.utils.mergeObject(
context.dsnTabs,
['hope', 'fear', 'advantage', 'disadvantage'].reduce(
(acc, key) => ({
...acc,
[key]: {
values: this.setting.diceSoNice[key],
fields: this.setting.schema.getField(`diceSoNice.${key}`).fields
}
}),
{}
)
);
}
/**
* Submit the configuration form.
* @this {DHAppearanceSettings}
* @param {SubmitEvent} event
* @param {HTMLFormElement} form
* @param {foundry.applications.ux.FormDataExtended} formData
* @returns {Promise<void>}
*/
static async #onSubmit(event, form, formData) {
const data = this.setting.schema.clean(foundry.utils.expandObject(formData.object));
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance, data);
}
/* -------------------------------------------- */
/**
* Submit the configuration form.
* @this {DHAppearanceSettings}
* @type {ApplicationClickAction}
*/
static async #onPreview(_, target) {
const formData = new foundry.applications.ux.FormDataExtended(target.closest('form'));
const { diceSoNice } = foundry.utils.expandObject(formData.object);
const { key } = target.dataset;
const faces = ['advantage', 'disadvantage'].includes(key) ? 'd6' : 'd12';
const preset = await getDiceSoNicePreset(diceSoNice[key], faces);
const diceSoNiceRoll = await new foundry.dice.Roll(`1${faces}`).evaluate();
diceSoNiceRoll.dice[0].options.appearance = preset.appearance;
diceSoNiceRoll.dice[0].options.modelFile = preset.modelFile;
await game.dice3d.showForRoll(diceSoNiceRoll, game.user, false);
}
static async reset() {
this.settings = new DhAppearance();
this.render();
}
static async save() {
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance, this.settings.toObject());
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;
/**
* Reset the form back to default values.
* @this {DHAppearanceSettings}
* @type {ApplicationClickAction}
*/
static async #onReset() {
this.setting = new this.setting.constructor();
this.render({ force: false });
}
}

View file

@ -46,6 +46,10 @@ export default class ClassSheet extends DHBaseItemSheet {
template: 'systems/daggerheart/templates/sheets/items/class/settings.hbs',
scrollable: ['.settings']
},
questions: {
template: 'systems/daggerheart/templates/sheets/items/class/questions.hbs',
scrollable: ['.questions']
},
effects: {
template: 'systems/daggerheart/templates/sheets/global/tabs/tab-effects.hbs',
scrollable: ['.effects']
@ -55,7 +59,13 @@ export default class ClassSheet extends DHBaseItemSheet {
/** @inheritdoc */
static TABS = {
primary: {
tabs: [{ id: 'description' }, { id: 'features' }, { id: 'settings' }, { id: 'effects' }],
tabs: [
{ id: 'description' },
{ id: 'features' },
{ id: 'settings' },
{ id: 'questions' },
{ id: 'effects' }
],
initial: 'description',
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
}

View file

@ -55,9 +55,12 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
}
get action() {
const actionItem = this.actionItem;
if (!actionItem || !this.source.action) return null;
return actionItem.system.actionsList?.find(a => a.id === this.source.action);
const actionActor = this.actionActor,
actionItem = this.actionItem;
if (!this.source.action) return null;
if (actionItem) return actionItem.system.actionsList?.find(a => a.id === this.source.action);
else if (actionActor?.system.attack?._id === this.source.action) return actionActor.system.attack;
return null;
}
get targetMode() {

View file

@ -30,8 +30,13 @@ export default class DamageField extends fields.SchemaField {
* @param {boolean} [force=false] If the method should be executed outside of Action workflow, for ChatMessage button for example.
*/
static async execute(config, messageId = null, force = false) {
if(!this.hasDamage && !this.hasHealing) return;
if((this.hasRoll && DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.never.id) && !force) return;
if (!this.hasDamage && !this.hasHealing) return;
if (
this.hasRoll &&
DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.never.id &&
!force
)
return;
let formulas = this.damage.parts.map(p => ({
formula: DamageField.getFormulaValue.call(this, p, config).getFormula(this.actor),
@ -51,17 +56,18 @@ export default class DamageField extends fields.SchemaField {
};
delete damageConfig.evaluate;
if(DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.always.id) damageConfig.dialog.configure = false;
if (DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.always.id)
damageConfig.dialog.configure = false;
if (config.hasSave) config.onSave = damageConfig.onSave = this.save.damageMod;
damageConfig.source.message = config.message?._id ?? messageId;
damageConfig.directDamage = !!damageConfig.source?.message;
if(damageConfig.source?.message && game.modules.get('dice-so-nice')?.active)
await game.dice3d.waitFor3DAnimationByMessageID(damageConfig.source.message);
// if(damageConfig.source?.message && game.modules.get('dice-so-nice')?.active)
// await game.dice3d.waitFor3DAnimationByMessageID(damageConfig.source.message);
const damageResult = await CONFIG.Dice.daggerheart.DamageRoll.build(damageConfig);
if(!damageResult) return false;
if (!damageResult) return false;
config.damage = damageResult.damage;
config.message ??= damageConfig.message;
}
@ -70,19 +76,15 @@ export default class DamageField extends fields.SchemaField {
* Apply Damage/Healing Action Worflow part.
* @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods.
* @param {*[]} targets Arrays of targets to bypass pre-selected ones.
* @param {boolean} force If the method should be executed outside of Action workflow, for ChatMessage button for example.
* @param {boolean} force If the method should be executed outside of Action workflow, for ChatMessage button for example.
*/
static async applyDamage(config, targets = null, force = false) {
targets ??= config.targets.filter(target => target.hit);
if(!config.damage || !targets?.length || (!DamageField.getApplyAutomation() && !force)) return;
if (!config.damage || !targets?.length || (!DamageField.getApplyAutomation() && !force)) return;
for (let target of targets) {
const actor = fromUuidSync(target.actorId);
if(!actor) continue;
if (
!config.hasHealing &&
config.onSave &&
target.saved?.success === true
) {
if (!actor) continue;
if (!config.hasHealing && config.onSave && target.saved?.success === true) {
const mod = CONFIG.DH.ACTIONS.damageOnSave[config.onSave]?.mod ?? 1;
Object.entries(config.damage).forEach(([k, v]) => {
v.total = 0;
@ -97,17 +99,17 @@ export default class DamageField extends fields.SchemaField {
else actor.takeDamage(config.damage, config.isDirect);
}
}
/**
* Return value or valueAlt from damage part
* Must be called within Action context or similar.
* @param {object} part Damage Part
* @param {object} part Damage Part
* @param {object} data Action getRollData
* @returns Formula value object
*/
static getFormulaValue(part, data) {
let formulaValue = part.value;
if (data.hasRoll && part.resultBased && data.roll.result.duality === -1) return part.valueAlt;
const isAdversary = this.actor.type === 'adversary';
@ -123,8 +125,8 @@ export default class DamageField extends fields.SchemaField {
* Prepare formulas for Damage Roll
* Must be called within Action context or similar.
* @param {object[]} formulas Array of formatted formulas object
* @param {object} data Action getRollData
* @returns
* @param {object} data Action getRollData
* @returns
*/
static formatFormulas(formulas, data) {
const formattedFormulas = [];
@ -145,16 +147,25 @@ export default class DamageField extends fields.SchemaField {
* @returns {string} Id from settingsConfig.mjs actionAutomationChoices
*/
static getAutomation() {
return (game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damage.gm) || (!game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damage.players)
return (
(game.user.isGM &&
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damage.gm) ||
(!game.user.isGM &&
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damage.players)
);
}
/**
* Return the automation setting for applyDamage method for current user role
* @returns {boolean} If applyDamage should be triggered automatically
*/
static getApplyAutomation() {
return (game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damageApply.gm) || (!game.user.isGM && game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damageApply.players)
return (
(game.user.isGM &&
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damageApply.gm) ||
(!game.user.isGM &&
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).roll.damageApply.players)
);
}
}

View file

@ -49,6 +49,8 @@ export default class DHClass extends BaseDataItem {
suggestedSecondaryWeapon: new ForeignDocumentUUIDField({ type: 'Item' }),
suggestedArmor: new ForeignDocumentUUIDField({ type: 'Item' })
}),
backgroundQuestions: new fields.ArrayField(new fields.StringField(), { initial: ['', '', ''] }),
connections: new fields.ArrayField(new fields.StringField(), { initial: ['', '', ''] }),
isMulticlass: new fields.BooleanField({ initial: false })
};
}
@ -96,6 +98,20 @@ export default class DHClass extends BaseDataItem {
}
}
}
if (!data.system.isMulticlass) {
const addQuestions = (base, questions) => {
return `${base}${questions.map(q => `<p><strong>${q}</strong></p>`).join('<br/>')}`;
};
const backgroundQuestions = data.system.backgroundQuestions.filter(x => x);
const connections = data.system.connections.filter(x => x);
await this.actor.update({
'system.biography': {
background: addQuestions(this.actor.system.biography.background, backgroundQuestions),
connections: addQuestions(this.actor.system.biography.connections, connections)
}
});
}
}
const allowed = await super._preCreate(data, options, user);

View file

@ -1,100 +1,46 @@
import { fearDisplay } from '../../config/generalConfig.mjs';
export default class DhAppearance extends foundry.abstract.DataModel {
static LOCALIZATION_PREFIXES = ['DAGGERHEART.SETTINGS.Appearance'];
static defineSchema() {
const fields = foundry.data.fields;
const { StringField, ColorField, BooleanField, SchemaField } = foundry.data.fields;
// helper to create dice style schema
const diceStyle = ({ fg, bg, outline, edge }) =>
new SchemaField({
foreground: new ColorField({ required: true, initial: fg }),
background: new ColorField({ required: true, initial: bg }),
outline: new ColorField({ required: true, initial: outline }),
edge: new ColorField({ required: true, initial: edge }),
texture: new StringField({ initial: 'astralsea', required: true, blank: false }),
colorset: new StringField({ initial: 'inspired', required: true, blank: false }),
material: new StringField({ initial: 'metal', required: true, blank: false }),
system: new StringField({ initial: 'standard', required: true, blank: false })
});
return {
displayFear: new fields.StringField({
displayFear: new StringField({
required: true,
choices: fearDisplay,
initial: fearDisplay.token.value,
label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.displayFear.label'
choices: CONFIG.DH.GENERAL.fearDisplay,
initial: CONFIG.DH.GENERAL.fearDisplay.token.value
}),
diceSoNice: new fields.SchemaField({
hope: new fields.SchemaField({
foreground: new fields.ColorField({ required: true, initial: '#ffffff' }),
background: new fields.ColorField({ required: true, initial: '#ffe760' }),
outline: new fields.ColorField({ required: true, initial: '#000000' }),
edge: new fields.ColorField({ required: true, initial: '#ffffff' }),
texture: new fields.StringField({ initial: 'astralsea' }),
colorset: new fields.StringField({ initial: 'inspired' }),
material: new fields.StringField({ initial: 'metal' }),
system: new fields.StringField({ initial: 'standard' })
}),
fear: new fields.SchemaField({
foreground: new fields.ColorField({ required: true, initial: '#000000' }),
background: new fields.ColorField({ required: true, initial: '#0032b1' }),
outline: new fields.ColorField({ required: true, initial: '#ffffff' }),
edge: new fields.ColorField({ required: true, initial: '#000000' }),
texture: new fields.StringField({ initial: 'astralsea' }),
colorset: new fields.StringField({ initial: 'inspired' }),
material: new fields.StringField({ initial: 'metal' }),
system: new fields.StringField({ initial: 'standard' })
}),
advantage: new fields.SchemaField({
foreground: new fields.ColorField({ required: true, initial: '#ffffff' }),
background: new fields.ColorField({ required: true, initial: '#008000' }),
outline: new fields.ColorField({ required: true, initial: '#000000' }),
edge: new fields.ColorField({ required: true, initial: '#ffffff' }),
texture: new fields.StringField({ initial: 'astralsea' }),
colorset: new fields.StringField({ initial: 'inspired' }),
material: new fields.StringField({ initial: 'metal' }),
system: new fields.StringField({ initial: 'standard' })
}),
disadvantage: new fields.SchemaField({
foreground: new fields.ColorField({ required: true, initial: '#000000' }),
background: new fields.ColorField({ required: true, initial: '#b30000' }),
outline: new fields.ColorField({ required: true, initial: '#ffffff' }),
edge: new fields.ColorField({ required: true, initial: '#000000' }),
texture: new fields.StringField({ initial: 'astralsea' }),
colorset: new fields.StringField({ initial: 'inspired' }),
material: new fields.StringField({ initial: 'metal' }),
system: new fields.StringField({ initial: 'standard' })
})
diceSoNice: new SchemaField({
hope: diceStyle({ fg: '#ffffff', bg: '#ffe760', outline: '#000000', edge: '#ffffff' }),
fear: diceStyle({ fg: '#000000', bg: '#0032b1', outline: '#ffffff', edge: '#000000' }),
advantage: diceStyle({ fg: '#ffffff', bg: '#008000', outline: '#000000', edge: '#ffffff' }),
disadvantage: diceStyle({ fg: '#000000', bg: '#b30000', outline: '#ffffff', edge: '#000000' })
}),
showGenericStatusEffects: new fields.BooleanField({
initial: true,
label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.showGenericStatusEffects.label'
extendCharacterDescriptions: new BooleanField(),
extendAdversaryDescriptions: new BooleanField(),
extendEnvironmentDescriptions: new BooleanField(),
extendItemDescriptions: new BooleanField(),
expandRollMessage: new SchemaField({
desc: new BooleanField(),
roll: new BooleanField(),
damage: new BooleanField(),
target: new BooleanField()
}),
extendCharacterDescriptions: new fields.BooleanField({
initial: false,
label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.extendCharacterDescriptions.label'
}),
extendAdversaryDescriptions: new fields.BooleanField({
initial: false,
label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.extendAdversaryDescriptions.label'
}),
extendEnvironmentDescriptions: new fields.BooleanField({
initial: false,
label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.extendEnvironmentDescriptions.label'
}),
extendItemDescriptions: new fields.BooleanField({
initial: false,
label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.extendItemDescriptions.label'
}),
expandRollMessage: new fields.SchemaField({
desc: new fields.BooleanField({
initial: false,
label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.expandRollMessageDesc.label'
}),
roll: new fields.BooleanField({
initial: false,
label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.expandRollMessageRoll.label'
}),
damage: new fields.BooleanField({
initial: false,
label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.expandRollMessageDamage.label'
}),
target: new fields.BooleanField({
initial: false,
label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.expandRollMessageTarget.label'
})
}),
hideAttribution: new fields.BooleanField({
required: true,
initial: false,
label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.hideAttribution.label'
})
hideAttribution: new BooleanField(),
showGenericStatusEffects: new BooleanField({ initial: true })
};
}
}

View file

@ -72,7 +72,7 @@ const registerMenus = () => {
});
game.settings.registerMenu(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance, {
name: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.appearance.title'),
name: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.appearance.label'),
label: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.appearance.label'),
hint: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.appearance.hint'),
icon: 'fa-solid fa-palette',

View file

@ -63,7 +63,17 @@
"source": "Daggerheart SRD",
"page": 9,
"artist": ""
}
},
"backgroundQuestions": [
"Who from your community taught you to have such confidence in yourself?",
"You were in love once. Who did you adore, and how did they hurt you?",
"Youve always looked up to another bard. Who are they, and why do you idolize them?"
],
"connections": [
"What made you realize we were going to be such good friends?",
"What do I do that annoys you?",
"Why do you grab my hand at night?"
]
},
"effects": [],
"ownership": {
@ -77,10 +87,10 @@
"exportSource": null,
"coreVersion": "13.347",
"systemId": "daggerheart",
"systemVersion": "1.1.0",
"systemVersion": "1.1.2",
"createdTime": 1754174600538,
"modifiedTime": 1755943467705,
"lastModifiedBy": "tt3PwMBXcTLCtIQU"
"modifiedTime": 1756399046200,
"lastModifiedBy": "gbAAZWyczKwejDNh"
},
"_id": "vegl3bFOq3pcFTWT",
"sort": 300000,

View file

@ -63,7 +63,17 @@
"source": "Daggerheart SRD",
"page": 10,
"artist": ""
}
},
"backgroundQuestions": [
"Why was the community you grew up in so reliant on nature and its creatures?",
"Who was the first wild animal you bonded with? Why did your bond end?",
"Who has been trying to hunt you down? What do they want from you?"
],
"connections": [
"What did you confide in me that makes me leap into danger for you every time?",
"What animal do I say you remind me of?",
"What affectionate nickname have you given me?"
]
},
"effects": [],
"folder": null,
@ -79,10 +89,10 @@
"exportSource": null,
"coreVersion": "13.347",
"systemId": "daggerheart",
"systemVersion": "1.1.0",
"systemVersion": "1.1.2",
"createdTime": 1754222247012,
"modifiedTime": 1755943479440,
"lastModifiedBy": "tt3PwMBXcTLCtIQU"
"modifiedTime": 1756399003725,
"lastModifiedBy": "gbAAZWyczKwejDNh"
},
"_key": "!items!ZNwUTCyGCEcidZFv"
}

View file

@ -59,7 +59,17 @@
"source": "Daggerheart SRD",
"page": 15,
"artist": ""
}
},
"backgroundQuestions": [
"Who from your community did you fail to protect, and why do you still think of them?",
"Youve been tasked with protecting something important and delivering\nit somewhere dangerous. What is it, and where does it need to go?",
"You consider an aspect of yourself to be a weakness. What is it, and how has it affected you?"
],
"connections": [
"How did I save your life the first time we met?",
"What small gift did you give me that you notice I always carry with me?",
"What lie have you told me about yourself that I absolutely believe?"
]
},
"effects": [],
"folder": null,
@ -75,10 +85,10 @@
"exportSource": null,
"coreVersion": "13.347",
"systemId": "daggerheart",
"systemVersion": "1.1.0",
"systemVersion": "1.1.2",
"createdTime": 1754246931974,
"modifiedTime": 1755943488697,
"lastModifiedBy": "tt3PwMBXcTLCtIQU"
"modifiedTime": 1756398951257,
"lastModifiedBy": "gbAAZWyczKwejDNh"
},
"_key": "!items!nRAyoC0fOzXPDa4z"
}

View file

@ -59,7 +59,17 @@
"source": "Daggerheart SRD",
"page": 16,
"artist": ""
}
},
"backgroundQuestions": [
"A terrible creature hurt your community, and youve vowed to hunt them down. What are they, and what unique trail or sign do they leave behind?",
"Your first kill almost killed you, too. What was it, and what part of you was never the same after that event?",
"Youve traveled many dangerous lands, but what is the one place you refuse to go?"
],
"connections": [
"What friendly competition do we have?",
"Why do you act differently when were alone than when others are around?",
"What threat have you asked me to watch for, and why are you worried about it?"
]
},
"effects": [],
"folder": null,
@ -75,10 +85,10 @@
"exportSource": null,
"coreVersion": "13.347",
"systemId": "daggerheart",
"systemVersion": "1.1.0",
"systemVersion": "1.1.2",
"createdTime": 1754268869310,
"modifiedTime": 1755943505024,
"lastModifiedBy": "tt3PwMBXcTLCtIQU"
"modifiedTime": 1756398897309,
"lastModifiedBy": "gbAAZWyczKwejDNh"
},
"_key": "!items!BTyfve69LKqoOi9S"
}

View file

@ -63,7 +63,17 @@
"source": "Daggerheart SRD",
"page": 19,
"artist": ""
}
},
"backgroundQuestions": [
"What did you get caught doing that got you exiled from your home community?",
"You used to have a different life, but youve tried to leave it behind. Who from your past is still chasing you?",
"Who from your past were you most sad to say goodbye to?"
],
"connections": [
"What did I recently convince you to do that got us both in trouble?",
"What have I discovered about your past that I hold secret from the others?",
"Who do you know from my past, and how have they influenced your feelings about me?"
]
},
"effects": [],
"folder": null,
@ -79,10 +89,10 @@
"exportSource": null,
"coreVersion": "13.347",
"systemId": "daggerheart",
"systemVersion": "1.1.0",
"systemVersion": "1.1.2",
"createdTime": 1754325275832,
"modifiedTime": 1755943515533,
"lastModifiedBy": "tt3PwMBXcTLCtIQU"
"modifiedTime": 1756398839983,
"lastModifiedBy": "gbAAZWyczKwejDNh"
},
"_key": "!items!CvHlkHZfpMiCz5uT"
}

View file

@ -59,7 +59,17 @@
"source": "Daggerheart SRD",
"page": 20,
"artist": ""
}
},
"backgroundQuestions": [
"Which god did you devote yourself to? What incredible feat did they perform for you in a moment of desperation?",
"How did your appearance change after taking your oath?",
"In what strange or unique way do you communicate with your god?"
],
"connections": [
"What promise did you make me agree to, should you die on the battlefield?",
"Why do you ask me so many questions about my god?",
"Youve told me to protect one member of our party above all others, even yourself. Who are they and why?"
]
},
"effects": [],
"folder": null,
@ -75,10 +85,10 @@
"exportSource": null,
"coreVersion": "13.347",
"systemId": "daggerheart",
"systemVersion": "1.1.0",
"systemVersion": "1.1.2",
"createdTime": 1754351482530,
"modifiedTime": 1755943523935,
"lastModifiedBy": "tt3PwMBXcTLCtIQU"
"modifiedTime": 1756398795596,
"lastModifiedBy": "gbAAZWyczKwejDNh"
},
"_key": "!items!5ZnlJ5bEoyOTkUJv"
}

View file

@ -67,7 +67,17 @@
"source": "Daggerheart SRD",
"page": 22,
"artist": ""
}
},
"backgroundQuestions": [
"What did you do that made the people in your community wary of you?",
"What mentor taught you to control your untamed magic, and why are they no longer able to guide you?",
"You have a deep fear you hide from everyone. What is it, and why does it scare you?"
],
"connections": [
"Why do you trust me so deeply?",
"What did I do that makes you cautious around me?",
"Why do we keep our shared past a secret?"
]
},
"effects": [],
"folder": null,
@ -83,10 +93,10 @@
"exportSource": null,
"coreVersion": "13.347",
"systemId": "daggerheart",
"systemVersion": "1.1.0",
"systemVersion": "1.1.2",
"createdTime": 1754349743129,
"modifiedTime": 1755943536635,
"lastModifiedBy": "tt3PwMBXcTLCtIQU"
"modifiedTime": 1756398741027,
"lastModifiedBy": "gbAAZWyczKwejDNh"
},
"_key": "!items!DchOzHcWIJE9FKcR"
}

View file

@ -63,7 +63,17 @@
"source": "Daggerheart SRD",
"page": 23,
"artist": ""
}
},
"backgroundQuestions": [
"Who taught you to fight, and why did they stay behind when you left home?",
"Somebody defeated you in battle years ago and left you to die. Who was it, and how did they betray you?",
"What legendary place have you always wanted to visit, and why is it so special?"
],
"connections": [
"We knew each other long before this party came together. How?",
"What mundane task do you usually help me with off the battlefield?",
"What fear am I helping you overcome?"
]
},
"effects": [],
"folder": null,
@ -79,10 +89,10 @@
"exportSource": null,
"coreVersion": "13.347",
"systemId": "daggerheart",
"systemVersion": "1.1.0",
"systemVersion": "1.1.2",
"createdTime": 1754255776706,
"modifiedTime": 1755943545980,
"lastModifiedBy": "tt3PwMBXcTLCtIQU"
"modifiedTime": 1756398696324,
"lastModifiedBy": "gbAAZWyczKwejDNh"
},
"_key": "!items!xCUWwJz4WSthvLfy"
}

View file

@ -63,7 +63,17 @@
"source": "Daggerheart SRD",
"page": 25,
"artist": ""
}
},
"backgroundQuestions": [
"What responsibilities did your community once count on you for?\nHow did you let them down?",
"Youve spent your life searching for a book or object of great\nsignificance. What is it, and why is it so important to you?",
"You have a powerful rival. Who are they, and why are you so determined to defeat them?"
],
"connections": [
"What favor have I asked of you that youre not sure you can fulfill?",
"What weird hobby or strange fascination do we both share?",
"What secret about yourself have you entrusted only to me?"
]
},
"effects": [],
"folder": null,
@ -79,10 +89,10 @@
"exportSource": null,
"coreVersion": "13.347",
"systemId": "daggerheart",
"systemVersion": "1.1.0",
"systemVersion": "1.1.2",
"createdTime": 1754253505323,
"modifiedTime": 1755943555087,
"lastModifiedBy": "tt3PwMBXcTLCtIQU"
"modifiedTime": 1756391897762,
"lastModifiedBy": "gbAAZWyczKwejDNh"
},
"_key": "!items!5LwX4m8ziY3F1ZGC"
}

View file

@ -43,4 +43,18 @@
}
}
}
.tab.questions {
.questions-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
.questions-section {
display: flex;
flex-direction: column;
gap: 4px;
}
}
}
}

View file

@ -279,11 +279,11 @@
}
&[data-sort-type='ASC']:after {
content: '\f0d7';
content: '\f884';
}
&[data-sort-type='DESC']:after {
content: '\f0d8';
content: '\f885';
}
}
}
@ -395,7 +395,7 @@
text-align: center;
font-weight: bold;
}
.hint {
flex: unset;
}
@ -409,7 +409,8 @@
&.lite,
&.no-folder {
.compendium-sidebar, .menu-path {
.compendium-sidebar,
.menu-path {
display: none;
}
}

View file

@ -1,116 +0,0 @@
<div>
<header class="dialog-header">
<h1>{{localize 'DAGGERHEART.SETTINGS.Menu.appearance.name'}}</h1>
</header>
{{formGroup settingFields.schema.fields.hideAttribution value=settingFields._source.hideAttribution localize=true}}
<fieldset>
<legend>{{localize 'DAGGERHEART.GENERAL.fear'}}</legend>
{{formGroup settingFields.schema.fields.displayFear value=settingFields._source.displayFear localize=true}}
{{formGroup settingFields.schema.fields.showGenericStatusEffects value=settingFields._source.showGenericStatusEffects localize=true}}
</fieldset>
<fieldset>
<legend>{{localize 'DAGGERHEART.SETTINGS.Appearance.FIELDS.expandedTitle'}}</legend>
{{formGroup settingFields.schema.fields.extendCharacterDescriptions value=settingFields._source.extendCharacterDescriptions localize=true}}
{{formGroup settingFields.schema.fields.extendAdversaryDescriptions value=settingFields._source.extendAdversaryDescriptions localize=true}}
{{formGroup settingFields.schema.fields.extendEnvironmentDescriptions value=settingFields._source.extendEnvironmentDescriptions localize=true}}
{{formGroup settingFields.schema.fields.extendItemDescriptions value=settingFields._source.extendItemDescriptions localize=true}}
</fieldset>
<fieldset>
<legend>{{localize 'DAGGERHEART.SETTINGS.Appearance.FIELDS.expandRollMessage'}}</legend>
{{formGroup settingFields.schema.fields.expandRollMessage.fields.desc value=settingFields.expandRollMessage.desc localize=true}}
{{formGroup settingFields.schema.fields.expandRollMessage.fields.roll value=settingFields.expandRollMessage.roll localize=true}}
{{formGroup settingFields.schema.fields.expandRollMessage.fields.damage value=settingFields.expandRollMessage.damage localize=true}}
{{formGroup settingFields.schema.fields.expandRollMessage.fields.target value=settingFields.expandRollMessage.target localize=true}}
</fieldset>
{{#if showDiceSoNice}}
<fieldset>
<legend>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.title"}}</legend>
<div class="title-hint">{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.hint"}}</div>
<section class='tab-navigation'>
<div class='navigation-container'>
<div class="navigation-inner-container">
<line-div></line-div>
<nav class='feature-tab sheet-tabs tabs' data-group='diceSoNice'>
{{#each tabs as |tab|}}
<a class='{{tab.id}} {{tab.cssClass}}' data-action='tab' data-group='{{tab.group}}' data-tab='{{tab.id}}'>
{{localize tab.label}}
</a>
{{/each}}
</nav>
<line-div></line-div>
</div>
</div>
</section>
<div class="field-section">
<div class="label-container full-width">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.system"}}</label>
<select name="{{concat "diceSoNice." diceTab.key ".system"}}">
{{selectOptions diceSoNiceSystems selected=diceTab.source.system valueAttr="key" labelAttr="name" localize=true}}
</select>
</div>
<div class="split-section">
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.foreground"}}</label>
{{formInput diceTab.fields.foreground value=diceTab.source.foreground localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.background"}}</label>
{{formInput diceTab.fields.background value=diceTab.source.background localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.outline"}}</label>
{{formInput diceTab.fields.outline value=diceTab.source.outline localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.edge"}}</label>
{{formInput diceTab.fields.edge value=diceTab.source.edge localize=true}}
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.colorset"}}</label>
<select name="{{concat "diceSoNice." diceTab.key ".colorset"}}">
{{selectOptions diceSoNiceColorsets selected=diceTab.source.colorset labelAttr="description" localize=true}}
</select>
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.texture"}}</label>
<select name="{{concat "diceSoNice." diceTab.key ".texture"}}">
{{selectOptions diceSoNiceTextures selected=diceTab.source.texture labelAttr="name" localize=true}}
</select>
</div>
<div class="label-container">
<label>{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.material"}}</label>
<select name="{{concat "diceSoNice." diceTab.key ".material"}}">
{{selectOptions diceSoNiceMaterials selected=diceTab.source.material valueAttr="key" labelAttr="name" localize=true}}
</select>
</div>
<div class="button-container">
<button data-action="preview">
<i class="fa-solid fa-dice"></i>
<span>{{localize "Preview"}}</span>
</button>
</div>
</div>
</div>
</fieldset>
{{/if}}
<footer class="form-footer">
<button data-action="reset">
<i class="fa-solid fa-arrow-rotate-left"></i>
<span>{{localize "Reset"}}</span>
</button>
<button data-action="save" >
<i class="fa-solid fa-floppy-disk"></i>
<span>{{localize "Save Changes"}}</span>
</button>
</footer>
</div>

View file

@ -0,0 +1,67 @@
<section class="tab {{#if tab.active}} active{{/if}}" data-group="{{tab.group}}" data-tab="{{tab.id}}">
<fieldset>
<div class="title-hint">{{localize "DAGGERHEART.SETTINGS.Menu.appearance.diceSoNice.hint"}}</div>
<section class='tab-navigation'>
<div class='navigation-container'>
<div class="navigation-inner-container">
<line-div></line-div>
<nav class='feature-tab sheet-tabs tabs' data-group='diceSoNice'>
{{#each dsnTabs as |tab|}}
<a class='{{tab.id}} {{tab.cssClass}}' data-action='tab' data-group='{{tab.group}}' data-tab='{{tab.id}}'>
{{localize tab.label}}
</a>
{{/each}}
</nav>
<line-div></line-div>
</div>
</div>
</section>
{{#each dsnTabs as |dsnTab|}}
<section class="tab {{#if dsnTab.active}} active{{/if}}" data-group="{{dsnTab.group}}" data-tab="{{dsnTab.id}}">
<div class="field-section">
<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="button-container">
<button type="button" data-action="preview" data-key="{{dsnTab.id}}">
<i class="fa-solid fa-dice"></i>
<span>{{localize "Preview"}}</span>
</button>
</div>
</div>
</div>
</section>
{{/each}}
</fieldset>
</section>

View file

@ -0,0 +1,3 @@
<header class="dialog-header">
<h1>{{localize 'DAGGERHEART.SETTINGS.Menu.appearance.label'}}</h1>
</header>

View file

@ -0,0 +1,46 @@
<section class="tab {{#if tab.active}} active{{/if}}" data-group="{{tab.group}}" data-tab="{{tab.id}}">
{{formGroup
fields.displayFear
value=setting.displayFear
localize=true}}
{{formGroup
fields.showGenericStatusEffects
value=setting.showGenericStatusEffects
localize=true}}
{{formGroup
fields.hideAttribution
value=setting.hideAttribution
localize=true}}
<fieldset>
<legend>{{localize 'DAGGERHEART.SETTINGS.Appearance.FIELDS.expandedTitle'}}</legend>
{{formGroup
fields.extendCharacterDescriptions
value=setting.extendCharacterDescriptions
localize=true}}
{{formGroup
fields.extendAdversaryDescriptions
value=setting.extendAdversaryDescriptions
localize=true}}
{{formGroup
fields.extendEnvironmentDescriptions
value=setting.extendEnvironmentDescriptions
localize=true}}
{{formGroup
fields.extendItemDescriptions
value=setting.extendItemDescriptions
localize=true}}
</fieldset>
<fieldset>
<legend>{{localize 'DAGGERHEART.SETTINGS.Appearance.FIELDS.expandRollMessage.title'}}</legend>
{{formGroup fields.expandRollMessage.fields.desc value=setting.expandRollMessage.desc
localize=true}}
{{formGroup fields.expandRollMessage.fields.roll value=setting.expandRollMessage.roll
localize=true}}
{{formGroup fields.expandRollMessage.fields.damage
value=setting.expandRollMessage.damage localize=true}}
{{formGroup fields.expandRollMessage.fields.target
value=setting.expandRollMessage.target localize=true}}
</fieldset>
</section>

View file

@ -0,0 +1,27 @@
<section
class='tab {{tabs.questions.cssClass}} {{tabs.questions.id}}'
data-tab='{{tabs.questions.id}}'
data-group='{{tabs.questions.group}}'
>
<div class="questions-container">
<fieldset>
<legend>{{localize "DAGGERHEART.ACTORS.Character.backgroundQuestions"}}</legend>
<div class="questions-section">
{{#each source.system.backgroundQuestions as | question index |}}
<textarea name="{{concat "system.backgroundQuestions." index}}">{{question}}</textarea>
{{/each}}
</div>
</fieldset>
<fieldset>
<legend>{{localize "DAGGERHEART.ACTORS.Character.connections"}}</legend>
<div class="questions-section">
{{#each source.system.connections as | connection index |}}
<textarea name="{{concat "system.connections." index}}">{{connection}}</textarea>
{{/each}}
</div>
</fieldset>
</div>
</section>