Added Character-Settings

This commit is contained in:
WBHarry 2025-07-31 04:12:35 +02:00
parent 798cf8c761
commit 8555b7a044
17 changed files with 258 additions and 58 deletions

View file

@ -167,6 +167,10 @@
"backgroundTitle": "Background", "backgroundTitle": "Background",
"characteristics": "Characteristics", "characteristics": "Characteristics",
"connectionsTitle": "Connections" "connectionsTitle": "Connections"
},
"experienceDataRemoveConfirmation": {
"title": "Remove Experience Data",
"text": "The experience you are about to remove has levelup data linked to it (assumably because you did levelups with the 'levelupAuto' automation setting on). Removing it will remove this automation data aswell. Do you want to proceed?"
} }
}, },
"Companion": { "Companion": {
@ -1859,6 +1863,7 @@
"levelUp": "Level Up", "levelUp": "Level Up",
"loadout": "Loadout", "loadout": "Loadout",
"max": "Max", "max": "Max",
"maxWithThing": "Max {thing}",
"multiclass": "Multiclass", "multiclass": "Multiclass",
"newCategory": "New Category", "newCategory": "New Category",
"none": "None", "none": "None",

View file

@ -1,4 +1,5 @@
export { default as ActionConfig } from './action-config.mjs'; export { default as ActionConfig } from './action-config.mjs';
export { default as CharacterSettings } from './character-settings.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 DowntimeConfig } from './downtimeConfig.mjs';

View file

@ -7,7 +7,10 @@ export default class DHCharacterSettings extends DHBaseActorSettings {
static DEFAULT_OPTIONS = { static DEFAULT_OPTIONS = {
classes: ['character-settings'], classes: ['character-settings'],
position: { width: 455, height: 'auto' }, position: { width: 455, height: 'auto' },
actions: {}, actions: {
addExperience: DHCharacterSettings.#addExperience,
removeExperience: DHCharacterSettings.#removeExperience
},
dragDrop: [ dragDrop: [
{ dragSelector: null, dropSelector: '.tab.features' }, { dragSelector: null, dropSelector: '.tab.features' },
{ dragSelector: '.feature-item', dropSelector: null } { dragSelector: '.feature-item', dropSelector: null }
@ -18,33 +21,111 @@ export default class DHCharacterSettings extends DHBaseActorSettings {
static PARTS = { static PARTS = {
header: { header: {
id: 'header', id: 'header',
template: 'systems/daggerheart/templates/sheets-settings/adversary-settings/header.hbs' template: 'systems/daggerheart/templates/sheets-settings/character-settings/header.hbs'
}, },
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
details: { details: {
id: 'details', id: 'details',
template: 'systems/daggerheart/templates/sheets-settings/adversary-settings/details.hbs' template: 'systems/daggerheart/templates/sheets-settings/character-settings/details.hbs'
},
attack: {
id: 'attack',
template: 'systems/daggerheart/templates/sheets-settings/adversary-settings/attack.hbs'
}, },
experiences: { experiences: {
id: 'experiences', id: 'experiences',
template: 'systems/daggerheart/templates/sheets-settings/adversary-settings/experiences.hbs' template: 'systems/daggerheart/templates/sheets-settings/character-settings/experiences.hbs'
},
features: {
id: 'features',
template: 'systems/daggerheart/templates/sheets-settings/adversary-settings/features.hbs'
} }
}; };
/** @override */ /** @override */
static TABS = { static TABS = {
primary: { primary: {
tabs: [{ id: 'details' }, { id: 'attack' }, { id: 'experiences' }, { id: 'features' }], tabs: [{ id: 'details' }, { id: 'experiences' }],
initial: 'details', initial: 'details',
labelPrefix: 'DAGGERHEART.GENERAL.Tabs' labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
} }
}; };
/**@inheritdoc */
async _prepareContext(options) {
const context = await super._prepareContext(options);
context.levelupAuto = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).levelupAuto;
return context;
}
/* -------------------------------------------- */
/**
* Adds a new experience entry to the actor.
* @type {ApplicationClickAction}
*/
static async #addExperience() {
const newExperience = {
name: 'Experience',
modifier: 0
};
await this.actor.update({ [`system.experiences.${foundry.utils.randomID()}`]: newExperience });
}
/**
* Removes an experience entry from the actor.
* @type {ApplicationClickAction}
*/
static async #removeExperience(_, target) {
const experience = this.actor.system.experiences[target.dataset.experience];
const updates = {};
const relinkData = Object.keys(this.actor.system.levelData.levelups).reduce((acc, key) => {
const level = this.actor.system.levelData.levelups[key];
const selectionIndex = level.selections.findIndex(
x => x.optionKey === 'experience' && x.data[0] === target.dataset.experience
);
if (selectionIndex !== -1)
acc.push({ levelKey: key, selectionIndex, experience: target.dataset.experience });
return acc;
}, []);
if (relinkData.length > 0) {
const confirmed = await foundry.applications.api.DialogV2.confirm({
window: {
title: game.i18n.localize('DAGGERHEART.ACTORS.Character.experienceDataRemoveConfirmation.title')
},
content: game.i18n.localize('DAGGERHEART.ACTORS.Character.experienceDataRemoveConfirmation.text')
});
if (!confirmed) return;
relinkData.forEach(data => {
updates[`system.levelData.levelups.${data.levelKey}.selections`] = this.actor.system.levelData.levelups[
data.levelKey
].selections.reduce((acc, selection, index) => {
if (
index === data.selectionIndex &&
selection.optionKey === 'experience' &&
selection.data.includes(data.experience)
) {
acc.push({ ...selection, data: selection.data.filter(x => x !== data.experience) });
} else {
acc.push(selection);
}
return acc;
}, []);
});
} else {
const confirmed = await foundry.applications.api.DialogV2.confirm({
window: {
title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', {
type: game.i18n.localize(`DAGGERHEART.GENERAL.Experience.single`),
name: experience.name
})
},
content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { name: experience.name })
});
if (!confirmed) return;
}
await this.actor.update({
...updates,
[`system.experiences.-=${target.dataset.experience}`]: null
});
}
} }

View file

@ -25,7 +25,6 @@ export default class CharacterSheet extends DHBaseActorSheet {
toggleEquipItem: CharacterSheet.#toggleEquipItem, toggleEquipItem: CharacterSheet.#toggleEquipItem,
toggleResourceDice: CharacterSheet.#toggleResourceDice, toggleResourceDice: CharacterSheet.#toggleResourceDice,
handleResourceDice: CharacterSheet.#handleResourceDice, handleResourceDice: CharacterSheet.#handleResourceDice,
openConfig: CharacterSheet.#openConfig,
useDowntime: this.useDowntime useDowntime: this.useDowntime
}, },
window: { window: {
@ -717,20 +716,14 @@ export default class CharacterSheet extends DHBaseActorSheet {
}); });
} }
/**
* Open the character config sheet.
* @type {ApplicationClickAction}
*/
static async #openConfig() {}
/** /**
* Open the downtime application. * Open the downtime application.
* @type {ApplicationClickAction} * @type {ApplicationClickAction}
*/ */
static useDowntime(_, button) { static useDowntime(_, button) {
new game.system.api.applications.dialogs.Downtime(this.document, button.dataset.type === 'shortRest').render( new game.system.api.applications.dialogs.Downtime(this.document, button.dataset.type === 'shortRest').render({
true force: true
); });
} }
async _onDragStart(event) { async _onDragStart(event) {

View file

@ -87,7 +87,8 @@ export default class DhpAdversary extends BaseDataActor {
experiences: new fields.TypedObjectField( experiences: new fields.TypedObjectField(
new fields.SchemaField({ new fields.SchemaField({
name: new fields.StringField(), name: new fields.StringField(),
value: new fields.NumberField({ required: true, integer: true, initial: 1 }) value: new fields.NumberField({ required: true, integer: true, initial: 1 }),
description: new fields.StringField()
}) })
), ),
bonuses: new fields.SchemaField({ bonuses: new fields.SchemaField({

View file

@ -4,6 +4,7 @@ import DhLevelData from '../levelData.mjs';
import BaseDataActor from './base.mjs'; import BaseDataActor from './base.mjs';
import { attributeField, resourceField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs'; import { attributeField, resourceField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs';
import { ActionField } from '../fields/actionField.mjs'; import { ActionField } from '../fields/actionField.mjs';
import DHCharacterSettings from '../../applications/sheets-configs/character-settings.mjs';
export default class DhCharacter extends BaseDataActor { export default class DhCharacter extends BaseDataActor {
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Character']; static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Character'];
@ -12,6 +13,7 @@ export default class DhCharacter extends BaseDataActor {
return foundry.utils.mergeObject(super.metadata, { return foundry.utils.mergeObject(super.metadata, {
label: 'TYPES.Actor.character', label: 'TYPES.Actor.character',
type: 'character', type: 'character',
settingSheet: DHCharacterSettings,
isNPC: false isNPC: false
}); });
} }
@ -56,7 +58,8 @@ export default class DhCharacter extends BaseDataActor {
experiences: new fields.TypedObjectField( experiences: new fields.TypedObjectField(
new fields.SchemaField({ new fields.SchemaField({
name: new fields.StringField(), name: new fields.StringField(),
value: new fields.NumberField({ integer: true, initial: 0 }) value: new fields.NumberField({ integer: true, initial: 0 }),
description: new fields.StringField()
}) })
), ),
gold: new fields.SchemaField({ gold: new fields.SchemaField({
@ -538,9 +541,9 @@ export default class DhCharacter extends BaseDataActor {
this.proficiency += selection.value; this.proficiency += selection.value;
break; break;
case 'experience': case 'experience':
Object.keys(this.experiences).forEach(key => { selection.data.forEach(id => {
const experience = this.experiences[key]; const experience = this.experiences[id];
experience.value += selection.value; if (experience) experience.value += selection.value;
}); });
break; break;
} }
@ -568,7 +571,7 @@ export default class DhCharacter extends BaseDataActor {
this.attack.roll.trait = this.rules.attack.roll.trait ?? this.attack.roll.trait; this.attack.roll.trait = this.rules.attack.roll.trait ?? this.attack.roll.trait;
this.resources.armor = { this.resources.armor = {
value: this.armor.system.marks.value, value: this.armor?.system?.marks?.value ?? 0,
max: this.armorScore, max: this.armorScore,
isReversed: true isReversed: true
}; };

View file

@ -9,7 +9,11 @@ const attributeField = label =>
const resourceField = (max = 0, label, reverse = false) => const resourceField = (max = 0, label, reverse = false) =>
new fields.SchemaField({ new fields.SchemaField({
value: new fields.NumberField({ initial: 0, min: 0, integer: true, label }), value: new fields.NumberField({ initial: 0, min: 0, integer: true, label }),
max: new fields.NumberField({ initial: max, integer: true }), max: new fields.NumberField({
initial: max,
integer: true,
label: game.i18n.format('DAGGERHEART.GENERAL.maxWithThing', { thing: game.i18n.localize(label) })
}),
isReversed: new fields.BooleanField({ initial: reverse }) isReversed: new fields.BooleanField({ initial: reverse })
}); });

View file

@ -9,21 +9,30 @@
margin-bottom: 12px; margin-bottom: 12px;
} }
.experience-list { .experience-item {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 10px; gap: 5px;
.experience-item { .experience-inner-item {
display: grid; display: grid;
grid-template-columns: 3fr 1fr 30px; grid-template-columns: 3fr 1fr 30px;
align-items: center; align-items: center;
gap: 5px; gap: 5px;
&.no-controls {
grid-template-columns: 3fr 1fr;
}
a { a {
text-align: center; text-align: center;
} }
} }
textarea {
width: 100%;
resize: none;
}
} }
} }
} }

View file

@ -0,0 +1,36 @@
.application.daggerheart.dh-style.dialog {
.tab.details {
.traits-inner-container {
width: 100%;
display: flex;
align-items: center;
justify-content: space-evenly;
gap: 8px;
.trait-container {
width: 60px;
height: 60px;
background: url(../assets/svg/trait-shield.svg) no-repeat;
display: flex;
flex-direction: column;
align-items: center;
div {
filter: drop-shadow(0 0 3px black);
text-shadow: 0 0 3px black;
font-family: @font-body;
font-size: 12px;
}
input {
text-align: center;
width: 32px;
height: 24px;
position: relative;
top: 2px;
padding: 0;
}
}
}
}
}

View file

@ -2,6 +2,7 @@
@import './adversary-settings/sheet.less'; @import './adversary-settings/sheet.less';
@import './adversary-settings/experiences.less'; @import './adversary-settings/experiences.less';
@import './adversary-settings/features.less'; @import './adversary-settings/features.less';
@import './character-settings/sheet.less';
@import './environment-settings/features.less'; @import './environment-settings/features.less';
@import './environment-settings/adversaries.less'; @import './environment-settings/adversaries.less';

View file

@ -12,9 +12,13 @@
<ul class="experience-list"> <ul class="experience-list">
{{#each document.system.experiences as |experience key|}} {{#each document.system.experiences as |experience key|}}
<li class="experience-item"> <li class="experience-item">
<input class="name" type="text" name="system.experiences.{{key}}.name" value="{{experience.name}}" /> <div class="experience-inner-item">
<input class="modifier" type="text" name="system.experiences.{{key}}.value" value="{{experience.value}}" data-dtype="Number" /> <input class="name" type="text" name="system.experiences.{{key}}.name" value="{{experience.name}}" />
<a data-action="removeExperience" data-experience="{{key}}" data-tooltip="{{localize 'CONTROLS.CommonDelete'}}"><i class="fa-solid fa-trash"></i></a> <input class="modifier" type="text" name="system.experiences.{{key}}.value" value="{{experience.value}}" data-dtype="Number" />
<a data-action="removeExperience" data-experience="{{key}}" data-tooltip="{{localize 'CONTROLS.CommonDelete'}}"><i class="fa-solid fa-trash"></i></a>
</div>
<textarea name="system.experiences.{{key}}.description">{{experience.description}}</textarea>
</li> </li>
{{/each}} {{/each}}
</ul> </ul>

View file

@ -0,0 +1,34 @@
<section
class='tab {{tabs.details.cssClass}} {{tabs.details.id}}'
data-tab='{{tabs.details.id}}'
data-group='{{tabs.details.group}}'
>
<fieldset class="one-column">
<legend>{{localize 'DAGGERHEART.GENERAL.Trait.plural'}}</legend>
<div class="nest-inputs">
<div class="traits-inner-container">
{{#each document.system.traits as | trait key |}}
<div class="trait-container">
<div>{{localize (concat "DAGGERHEART.CONFIG.Traits." key ".name" )}}</div>
<input type="text" data-dtype="Number" name="{{concat "system.traits." key ".value"}}" value="{{trait.value}}" />
</div>
{{/each}}
</div>
</div>
</fieldset>
<fieldset class="one-column">
<legend>{{localize 'DAGGERHEART.GENERAL.basics'}}</legend>
<div class="two-columns even">
{{formGroup systemFields.resources.fields.hitPoints.fields.value value=document.system.resources.hitPoints.value localize=true}}
{{formGroup systemFields.resources.fields.hitPoints.fields.max value=document.system.resources.hitPoints.max localize=true}}
{{formGroup systemFields.resources.fields.stress.fields.value value=document.system.resources.stress.value localize=true}}
{{formGroup systemFields.resources.fields.stress.fields.max value=document.system.resources.stress.max localize=true}}
{{formGroup systemFields.proficiency value=document.proficiency localize=true}}
</div>
</fieldset>
</section>

View file

@ -0,0 +1,26 @@
<section
class='tab {{tabs.experiences.cssClass}} {{tabs.experiences.id}}'
data-tab='{{tabs.experiences.id}}'
data-group='{{tabs.experiences.group}}'
>
<button type="button" class="add-experience-btn" type="button" data-action="addExperience">
<span>{{localize "DOCUMENT.New" type=(localize "DAGGERHEART.GENERAL.experience.single")}}</span>
</button>
<fieldset>
<legend>{{localize tabs.experiences.label}}</legend>
<ul class="experience-list">
{{#each document.system.experiences as |experience key|}}
<li class="experience-item">
<div class="experience-inner-item {{#if @root.levelupAuto}}no-controls{{/if}}">
<input class="name" type="text" name="system.experiences.{{key}}.name" value="{{experience.name}}" />
<input class="modifier" type="text" name="system.experiences.{{key}}.value" value="{{experience.value}}" data-dtype="Number" />
{{#unless @root.levelupAuto}}<a data-action="removeExperience" data-experience="{{key}}" data-tooltip="{{localize 'CONTROLS.CommonDelete'}}"><i class="fa-solid fa-trash"></i></a>{{/unless}}
</div>
<textarea name="system.experiences.{{key}}.description">{{experience.description}}</textarea>
</li>
{{/each}}
</ul>
</fieldset>
</section>

View file

@ -0,0 +1,3 @@
<header class="dialog-header">
<h1>{{document.name}}</h1>
</header>

View file

@ -97,17 +97,17 @@
</div> </div>
<div class="experience-list"> <div class="experience-list">
{{#each source.system.experiences as |experience id|}} {{#each source.system.experiences as |experience id|}}
<div class="experience-row"> <div class="experience-row" data-tooltip-text="{{experience.description}}">
<div class="experience-value"> <div class="experience-value">
+{{experience.value}} +{{experience.value}}
</div>
<span class="experience-name">{{experience.name}}</span>
<div class="controls">
<a data-action="sendExpToChat" data-id="{{id}}">
<i class="fa-regular fa-message"></i>
</a>
</div>
</div> </div>
<span class="experience-name">{{experience.name}}</span>
<div class="controls">
<a data-action="sendExpToChat" data-id="{{id}}">
<i class="fa-regular fa-message"></i>
</a>
</div>
</div>
{{/each}} {{/each}}
</div> </div>
</div> </div>

View file

@ -12,7 +12,7 @@
<div class='level-div'> <div class='level-div'>
<h3 class='label'> <h3 class='label'>
<button data-action="openConfig">Temp</button> <button data-action="openSettings">Temp</button>
{{#if (or document.system.needsCharacterSetup document.system.levelData.canLevelUp)}} {{#if (or document.system.needsCharacterSetup document.system.levelData.canLevelUp)}}
<button <button
type="button" type="button"

View file

@ -142,18 +142,17 @@
</div> </div>
<div class="experience-list"> <div class="experience-list">
{{#each document.system.experiences as |experience id|}} {{#each document.system.experiences as |experience id|}}
<div class="experience-row"> <div class="experience-row" data-tooltip-text="{{experience.description}}">
<div class="experience-value"> <div class="experience-value">
+{{experience.value}} +{{experience.value}}
</div>
<div>{{experience.name}}</div>
<div class="controls">
<a data-action="sendExpToChat" data-type="experience" data-id="{{id}}">
<i class="fa-regular fa-message"></i>
</a>
</div>
</div> </div>
<input name="system.experiences.{{id}}.name" data-experience={{id}}
value="{{experience.name}}" type="text" />
<div class="controls">
<a data-action="sendExpToChat" data-type="experience" data-id="{{id}}">
<i class="fa-regular fa-message"></i>
</a>
</div>
</div>
{{/each}} {{/each}}
</div> </div>
</div> </div>