This commit is contained in:
WBHarry 2025-07-31 01:04:37 +02:00
parent 7bbbdce739
commit 798cf8c761
12 changed files with 239 additions and 129 deletions

View file

@ -2013,6 +2013,10 @@
"gm": { "label": "GM" },
"players": { "label": "Players" }
},
"levelupAuto": {
"label": "Levelup Automation",
"hint": "When you've made your choices and finish levelup, the numerical changes are automatically applied to your character."
},
"actionPoints": {
"label": "Action Points",
"hint": "Automatically give and take Action Points as combatants take their turns."

View file

@ -46,7 +46,8 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
tabs: { template: 'systems/daggerheart/templates/levelup/tabs/tab-navigation.hbs' },
advancements: { template: 'systems/daggerheart/templates/levelup/tabs/advancements.hbs' },
selections: { template: 'systems/daggerheart/templates/levelup/tabs/selections.hbs' },
summary: { template: 'systems/daggerheart/templates/levelup/tabs/summary.hbs' }
summary: { template: 'systems/daggerheart/templates/levelup/tabs/summary.hbs' },
footer: { template: 'systems/daggerheart/templates/levelup/tabs/footer.hbs' }
};
static TABS = {
@ -95,6 +96,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
const context = await super._prepareContext(_options);
context.levelup = this.levelup;
context.tabs = this._getTabs(this.constructor.TABS);
context.levelupAuto = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).levelupAuto;
return context;
}

View file

@ -0,0 +1,50 @@
import DHBaseActorSettings from '../sheets/api/actor-setting.mjs';
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
export default class DHCharacterSettings extends DHBaseActorSettings {
/**@inheritdoc */
static DEFAULT_OPTIONS = {
classes: ['character-settings'],
position: { width: 455, height: 'auto' },
actions: {},
dragDrop: [
{ dragSelector: null, dropSelector: '.tab.features' },
{ dragSelector: '.feature-item', dropSelector: null }
]
};
/**@override */
static PARTS = {
header: {
id: 'header',
template: 'systems/daggerheart/templates/sheets-settings/adversary-settings/header.hbs'
},
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
details: {
id: 'details',
template: 'systems/daggerheart/templates/sheets-settings/adversary-settings/details.hbs'
},
attack: {
id: 'attack',
template: 'systems/daggerheart/templates/sheets-settings/adversary-settings/attack.hbs'
},
experiences: {
id: 'experiences',
template: 'systems/daggerheart/templates/sheets-settings/adversary-settings/experiences.hbs'
},
features: {
id: 'features',
template: 'systems/daggerheart/templates/sheets-settings/adversary-settings/features.hbs'
}
};
/** @override */
static TABS = {
primary: {
tabs: [{ id: 'details' }, { id: 'attack' }, { id: 'experiences' }, { id: 'features' }],
initial: 'details',
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
}
};
}

View file

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

View file

@ -511,37 +511,39 @@ export default class DhCharacter extends BaseDataActor {
: Object.values(game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers).find(
tier => currentLevel >= tier.levels.start && currentLevel <= tier.levels.end
).tier;
for (let levelKey in this.levelData.levelups) {
const level = this.levelData.levelups[levelKey];
if (game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).levelupAuto) {
for (let levelKey in this.levelData.levelups) {
const level = this.levelData.levelups[levelKey];
this.proficiency += level.achievements.proficiency;
this.proficiency += level.achievements.proficiency;
for (let selection of level.selections) {
switch (selection.type) {
case 'trait':
selection.data.forEach(data => {
this.traits[data].value += 1;
this.traits[data].tierMarked = selection.tier === currentTier;
});
break;
case 'hitPoint':
this.resources.hitPoints.max += selection.value;
break;
case 'stress':
this.resources.stress.max += selection.value;
break;
case 'evasion':
this.evasion += selection.value;
break;
case 'proficiency':
this.proficiency += selection.value;
break;
case 'experience':
Object.keys(this.experiences).forEach(key => {
const experience = this.experiences[key];
experience.value += selection.value;
});
break;
for (let selection of level.selections) {
switch (selection.type) {
case 'trait':
selection.data.forEach(data => {
this.traits[data].value += 1;
this.traits[data].tierMarked = selection.tier === currentTier;
});
break;
case 'hitPoint':
this.resources.hitPoints.max += selection.value;
break;
case 'stress':
this.resources.stress.max += selection.value;
break;
case 'evasion':
this.evasion += selection.value;
break;
case 'proficiency':
this.proficiency += selection.value;
break;
case 'experience':
Object.keys(this.experiences).forEach(key => {
const experience = this.experiences[key];
experience.value += selection.value;
});
break;
}
}
}
}

View file

@ -14,6 +14,11 @@ export default class DhAutomation extends foundry.abstract.DataModel {
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.hopeFear.players.label'
})
}),
levelupAuto: new fields.BooleanField({
required: true,
initial: true,
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.levelupAuto.label'
}),
actionPoints: new fields.BooleanField({
required: true,
initial: false,

View file

@ -172,26 +172,30 @@ export default class DhpActor extends Actor {
}
async levelUp(levelupData) {
const levelupAuto = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).levelupAuto;
const levelups = {};
for (var levelKey of Object.keys(levelupData)) {
const level = levelupData[levelKey];
for (var experienceKey in level.achievements.experiences) {
const experience = level.achievements.experiences[experienceKey];
await this.update({
[`system.experiences.${experienceKey}`]: {
name: experience.name,
value: experience.modifier
}
});
if (this.system.companion) {
await this.system.companion.update({
if (levelupAuto) {
for (var experienceKey in level.achievements.experiences) {
const experience = level.achievements.experiences[experienceKey];
await this.update({
[`system.experiences.${experienceKey}`]: {
name: '',
name: experience.name,
value: experience.modifier
}
});
if (this.system.companion) {
await this.system.companion.update({
[`system.experiences.${experienceKey}`]: {
name: '',
value: experience.modifier
}
});
}
}
}
@ -250,74 +254,86 @@ export default class DhpActor extends Actor {
}
for (var addition of featureAdditions) {
for (var featureData of addition.features) {
const feature = new DHFeature({
...featureData,
description: game.i18n.localize(featureData.description)
});
const document = featureData.toPartner && this.system.partner ? this.system.partner : this;
const embeddedItem = await document.createEmbeddedDocuments('Item', [
{
if (levelupAuto) {
for (var featureData of addition.features) {
const feature = new DHFeature({
...featureData,
name: game.i18n.localize(featureData.name),
type: 'feature',
system: feature
}
]);
const newFeature = {
onPartner: Boolean(featureData.toPartner && this.system.partner),
id: embeddedItem[0].id
};
addition.checkbox.features = !addition.checkbox.features
? [newFeature]
: [...addition.checkbox.features, newFeature];
description: game.i18n.localize(featureData.description)
});
const document = featureData.toPartner && this.system.partner ? this.system.partner : this;
const embeddedItem = await document.createEmbeddedDocuments('Item', [
{
...featureData,
name: game.i18n.localize(featureData.name),
type: 'feature',
system: feature
}
]);
const newFeature = {
onPartner: Boolean(featureData.toPartner && this.system.partner),
id: embeddedItem[0].id
};
addition.checkbox.features = !addition.checkbox.features
? [newFeature]
: [...addition.checkbox.features, newFeature];
}
}
selections.push(addition.checkbox);
}
if (multiclass) {
const subclassItem = await foundry.utils.fromUuid(multiclass.secondaryData.subclass);
const subclassData = subclassItem.toObject();
const multiclassItem = await foundry.utils.fromUuid(multiclass.data[0]);
const multiclassData = multiclassItem.toObject();
if (levelupAuto) {
const subclassItem = await foundry.utils.fromUuid(multiclass.secondaryData.subclass);
const subclassData = subclassItem.toObject();
const multiclassItem = await foundry.utils.fromUuid(multiclass.data[0]);
const multiclassData = multiclassItem.toObject();
const embeddedItem = await this.createEmbeddedDocuments('Item', [
{
...multiclassData,
system: {
...multiclassData.system,
domains: [multiclass.secondaryData.domain],
isMulticlass: true
const embeddedItem = await this.createEmbeddedDocuments('Item', [
{
...multiclassData,
system: {
...multiclassData.system,
domains: [multiclass.secondaryData.domain],
isMulticlass: true
}
}
}
]);
]);
await this.createEmbeddedDocuments('Item', [
{
...subclassData,
system: {
...subclassData.system,
isMulticlass: true
await this.createEmbeddedDocuments('Item', [
{
...subclassData,
system: {
...subclassData.system,
isMulticlass: true
}
}
}
]);
selections.push({ ...multiclass, itemUuid: embeddedItem[0].uuid });
]);
selections.push({ ...multiclass, itemUuid: embeddedItem[0].uuid });
} else {
selections.push({ ...multiclass });
}
}
for (var domainCard of domainCards) {
const item = await foundry.utils.fromUuid(domainCard.data[0]);
const embeddedItem = await this.createEmbeddedDocuments('Item', [item.toObject()]);
selections.push({ ...domainCard, itemUuid: embeddedItem[0].uuid });
if (levelupAuto) {
const item = await foundry.utils.fromUuid(domainCard.data[0]);
const embeddedItem = await this.createEmbeddedDocuments('Item', [item.toObject()]);
selections.push({ ...domainCard, itemUuid: embeddedItem[0].uuid });
} else {
selections.push({ ...domainCard });
}
}
const achievementDomainCards = [];
for (var card of Object.values(level.achievements.domainCards)) {
const item = await foundry.utils.fromUuid(card.uuid);
const embeddedItem = await this.createEmbeddedDocuments('Item', [item.toObject()]);
card.itemUuid = embeddedItem[0].uuid;
achievementDomainCards.push(card);
if (levelupAuto) {
for (var card of Object.values(level.achievements.domainCards)) {
const item = await foundry.utils.fromUuid(card.uuid);
const embeddedItem = await this.createEmbeddedDocuments('Item', [item.toObject()]);
card.itemUuid = embeddedItem[0].uuid;
achievementDomainCards.push(card);
}
}
if (subclassFeatureState.class) {

View file

@ -104,4 +104,10 @@
}
}
}
.levelup-footer {
display: flex;
gap: 8px;
margin-top: 8px;
}
}

View file

@ -0,0 +1,8 @@
<section class='tab-footer'>
{{#unless levelupAuto}}
<div class="levelup-footer">
<button type="button" data-action="close">{{localize "Cancel"}}</button>
<button type="button" data-action="save">{{localize "Save"}}</button>
</div>
{{/unless}}
</section>

View file

@ -1,40 +1,44 @@
<section class='tab-navigation'>
<line-div></line-div>
<div class="levelup-navigation-container">
{{#if this.showTabs}}
<nav class='feature-tab sheet-tabs tabs' data-group='primary'>
{{#each tabs as |tab|}}
{{#if (not (eq tab.id 'summary'))}}
<div class="levelup-tab-container">
<a class='{{tab.id}} {{tab.cssClass}}' data-action='tab' data-group='{{tab.group}}' data-tab='{{tab.id}}'>
{{localize tab.label}}
</a>
{{#if tab.progress}}
<div>{{tab.progress.selected}}/{{tab.progress.max}}</div>
{{/if}}
</div>
{{/if}}
{{/each}}
</nav>
{{/if}}
<div class="levelup-navigation-actions {{#if (not this.showTabs)}}test{{/if}}">
{{#if this.navigate.previous.fromSummary}}
<button data-action="activatePart" data-part="advancements">{{localize "DAGGERHEART.APPLICATIONS.Levelup.navigateToLevelup"}}</button>
{{else}}
{{#if (not this.navigate.previous.disabled)}}
<button data-action="updateCurrentLevel" >{{this.navigate.previous.label}}</button>
{{/if}}
{{#if levelupAuto}}
<line-div></line-div>
<div class="levelup-navigation-container">
{{#if this.showTabs}}
<nav class='feature-tab sheet-tabs tabs' data-group='primary'>
{{#each tabs as |tab|}}
{{#if (not (eq tab.id 'summary'))}}
<div class="levelup-tab-container">
<a class='{{tab.id}} {{tab.cssClass}}' data-action='tab' data-group='{{tab.group}}' data-tab='{{tab.id}}'>
{{localize tab.label}}
</a>
{{#if tab.progress}}
<div>{{tab.progress.selected}}/{{tab.progress.max}}</div>
{{/if}}
</div>
{{/if}}
{{/each}}
</nav>
{{/if}}
{{#if this.navigate.next.show}}
{{#if this.navigate.next.toSummary}}
<button data-action="activatePart" data-part="summary" {{#if this.navigate.next.disabled}}disabled{{/if}}>{{localize "DAGGERHEART.APPLICATIONS.Levelup.navigateToSummary"}}</button>
{{else}}
<button data-action="updateCurrentLevel" data-forward="true" {{#if this.navigate.next.disabled}}disabled{{/if}}>{{this.navigate.next.label}}</button>
{{/if}}
{{else}}
<div></div>
{{#if this.levelupAuto}}
<div class="levelup-navigation-actions {{#if (not this.showTabs)}}test{{/if}}">
{{#if this.navigate.previous.fromSummary}}
<button data-action="activatePart" data-part="advancements">{{localize "DAGGERHEART.APPLICATIONS.Levelup.navigateToLevelup"}}</button>
{{else}}
{{#if (not this.navigate.previous.disabled)}}
<button data-action="updateCurrentLevel" >{{this.navigate.previous.label}}</button>
{{/if}}
{{/if}}
{{#if this.navigate.next.show}}
{{#if this.navigate.next.toSummary}}
<button data-action="activatePart" data-part="summary" {{#if this.navigate.next.disabled}}disabled{{/if}}>{{localize "DAGGERHEART.APPLICATIONS.Levelup.navigateToSummary"}}</button>
{{else}}
<button data-action="updateCurrentLevel" data-forward="true" {{#if this.navigate.next.disabled}}disabled{{/if}}>{{this.navigate.next.label}}</button>
{{/if}}
{{else}}
<div></div>
{{/if}}
</div>
{{/if}}
</div>
</div>
<line-div></line-div>
<line-div></line-div>
{{/if}}
</section>

View file

@ -11,6 +11,7 @@
{{formGroup settingFields.schema.fields.actionPoints value=settingFields._source.actionPoints localize=true}}
{{formGroup settingFields.schema.fields.hordeDamage value=settingFields._source.hordeDamage localize=true}}
{{formGroup settingFields.schema.fields.effects.fields.rangeDependent value=settingFields._source.effects.rangeDependent localize=true}}
{{formGroup settingFields.schema.fields.levelupAuto value=settingFields._source.levelupAuto localize=true}}
<footer class="form-footer">
<button data-action="reset">

View file

@ -12,13 +12,14 @@
<div class='level-div'>
<h3 class='label'>
<button data-action="openConfig">Temp</button>
{{#if (or document.system.needsCharacterSetup document.system.levelData.canLevelUp)}}
<button
type="button"
class="level-button glow" data-tooltip="{{#if document.system.needsCharacterSetup}}{{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.buttonTitle"}}{{else}}{{localize "DAGGERHEART.ACTORS.Character.levelUp"}}{{/if}}"
data-action="levelManagement"
>
<i class="fa-solid fa-triangle-exclamation"></i>
<i class="fa-solid fa-angles-up"></i>
</button>
{{/if}}
{{localize 'DAGGERHEART.GENERAL.level'}}