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" }, "gm": { "label": "GM" },
"players": { "label": "Players" } "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": { "actionPoints": {
"label": "Action Points", "label": "Action Points",
"hint": "Automatically give and take Action Points as combatants take their turns." "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' }, tabs: { template: 'systems/daggerheart/templates/levelup/tabs/tab-navigation.hbs' },
advancements: { template: 'systems/daggerheart/templates/levelup/tabs/advancements.hbs' }, advancements: { template: 'systems/daggerheart/templates/levelup/tabs/advancements.hbs' },
selections: { template: 'systems/daggerheart/templates/levelup/tabs/selections.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 = { static TABS = {
@ -95,6 +96,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
const context = await super._prepareContext(_options); const context = await super._prepareContext(_options);
context.levelup = this.levelup; context.levelup = this.levelup;
context.tabs = this._getTabs(this.constructor.TABS); context.tabs = this._getTabs(this.constructor.TABS);
context.levelupAuto = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).levelupAuto;
return context; 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, 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: {
@ -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) { 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 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( : 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 => currentLevel >= tier.levels.start && currentLevel <= tier.levels.end
).tier; ).tier;
for (let levelKey in this.levelData.levelups) { if (game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).levelupAuto) {
const level = this.levelData.levelups[levelKey]; 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) { for (let selection of level.selections) {
switch (selection.type) { switch (selection.type) {
case 'trait': case 'trait':
selection.data.forEach(data => { selection.data.forEach(data => {
this.traits[data].value += 1; this.traits[data].value += 1;
this.traits[data].tierMarked = selection.tier === currentTier; this.traits[data].tierMarked = selection.tier === currentTier;
}); });
break; break;
case 'hitPoint': case 'hitPoint':
this.resources.hitPoints.max += selection.value; this.resources.hitPoints.max += selection.value;
break; break;
case 'stress': case 'stress':
this.resources.stress.max += selection.value; this.resources.stress.max += selection.value;
break; break;
case 'evasion': case 'evasion':
this.evasion += selection.value; this.evasion += selection.value;
break; break;
case 'proficiency': case 'proficiency':
this.proficiency += selection.value; this.proficiency += selection.value;
break; break;
case 'experience': case 'experience':
Object.keys(this.experiences).forEach(key => { Object.keys(this.experiences).forEach(key => {
const experience = this.experiences[key]; const experience = this.experiences[key];
experience.value += selection.value; experience.value += selection.value;
}); });
break; break;
}
} }
} }
} }

View file

@ -14,6 +14,11 @@ export default class DhAutomation extends foundry.abstract.DataModel {
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.hopeFear.players.label' 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({ actionPoints: new fields.BooleanField({
required: true, required: true,
initial: false, initial: false,

View file

@ -172,26 +172,30 @@ export default class DhpActor extends Actor {
} }
async levelUp(levelupData) { async levelUp(levelupData) {
const levelupAuto = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).levelupAuto;
const levelups = {}; const levelups = {};
for (var levelKey of Object.keys(levelupData)) { for (var levelKey of Object.keys(levelupData)) {
const level = levelupData[levelKey]; const level = levelupData[levelKey];
for (var experienceKey in level.achievements.experiences) { if (levelupAuto) {
const experience = level.achievements.experiences[experienceKey]; for (var experienceKey in level.achievements.experiences) {
await this.update({ const experience = level.achievements.experiences[experienceKey];
[`system.experiences.${experienceKey}`]: { await this.update({
name: experience.name,
value: experience.modifier
}
});
if (this.system.companion) {
await this.system.companion.update({
[`system.experiences.${experienceKey}`]: { [`system.experiences.${experienceKey}`]: {
name: '', name: experience.name,
value: experience.modifier 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 addition of featureAdditions) {
for (var featureData of addition.features) { if (levelupAuto) {
const feature = new DHFeature({ for (var featureData of addition.features) {
...featureData, const feature = new DHFeature({
description: game.i18n.localize(featureData.description)
});
const document = featureData.toPartner && this.system.partner ? this.system.partner : this;
const embeddedItem = await document.createEmbeddedDocuments('Item', [
{
...featureData, ...featureData,
name: game.i18n.localize(featureData.name), description: game.i18n.localize(featureData.description)
type: 'feature', });
system: feature
} const document = featureData.toPartner && this.system.partner ? this.system.partner : this;
]); const embeddedItem = await document.createEmbeddedDocuments('Item', [
const newFeature = { {
onPartner: Boolean(featureData.toPartner && this.system.partner), ...featureData,
id: embeddedItem[0].id name: game.i18n.localize(featureData.name),
}; type: 'feature',
addition.checkbox.features = !addition.checkbox.features system: feature
? [newFeature] }
: [...addition.checkbox.features, newFeature]; ]);
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); selections.push(addition.checkbox);
} }
if (multiclass) { if (multiclass) {
const subclassItem = await foundry.utils.fromUuid(multiclass.secondaryData.subclass); if (levelupAuto) {
const subclassData = subclassItem.toObject(); const subclassItem = await foundry.utils.fromUuid(multiclass.secondaryData.subclass);
const multiclassItem = await foundry.utils.fromUuid(multiclass.data[0]); const subclassData = subclassItem.toObject();
const multiclassData = multiclassItem.toObject(); const multiclassItem = await foundry.utils.fromUuid(multiclass.data[0]);
const multiclassData = multiclassItem.toObject();
const embeddedItem = await this.createEmbeddedDocuments('Item', [ const embeddedItem = await this.createEmbeddedDocuments('Item', [
{ {
...multiclassData, ...multiclassData,
system: { system: {
...multiclassData.system, ...multiclassData.system,
domains: [multiclass.secondaryData.domain], domains: [multiclass.secondaryData.domain],
isMulticlass: true isMulticlass: true
}
} }
} ]);
]);
await this.createEmbeddedDocuments('Item', [ await this.createEmbeddedDocuments('Item', [
{ {
...subclassData, ...subclassData,
system: { system: {
...subclassData.system, ...subclassData.system,
isMulticlass: true 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) { for (var domainCard of domainCards) {
const item = await foundry.utils.fromUuid(domainCard.data[0]); if (levelupAuto) {
const embeddedItem = await this.createEmbeddedDocuments('Item', [item.toObject()]); const item = await foundry.utils.fromUuid(domainCard.data[0]);
selections.push({ ...domainCard, itemUuid: embeddedItem[0].uuid }); const embeddedItem = await this.createEmbeddedDocuments('Item', [item.toObject()]);
selections.push({ ...domainCard, itemUuid: embeddedItem[0].uuid });
} else {
selections.push({ ...domainCard });
}
} }
const achievementDomainCards = []; const achievementDomainCards = [];
for (var card of Object.values(level.achievements.domainCards)) { if (levelupAuto) {
const item = await foundry.utils.fromUuid(card.uuid); for (var card of Object.values(level.achievements.domainCards)) {
const embeddedItem = await this.createEmbeddedDocuments('Item', [item.toObject()]); const item = await foundry.utils.fromUuid(card.uuid);
card.itemUuid = embeddedItem[0].uuid; const embeddedItem = await this.createEmbeddedDocuments('Item', [item.toObject()]);
achievementDomainCards.push(card); card.itemUuid = embeddedItem[0].uuid;
achievementDomainCards.push(card);
}
} }
if (subclassFeatureState.class) { 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'> <section class='tab-navigation'>
<line-div></line-div> {{#if levelupAuto}}
<div class="levelup-navigation-container"> <line-div></line-div>
{{#if this.showTabs}} <div class="levelup-navigation-container">
<nav class='feature-tab sheet-tabs tabs' data-group='primary'> {{#if this.showTabs}}
{{#each tabs as |tab|}} <nav class='feature-tab sheet-tabs tabs' data-group='primary'>
{{#if (not (eq tab.id 'summary'))}} {{#each tabs as |tab|}}
<div class="levelup-tab-container"> {{#if (not (eq tab.id 'summary'))}}
<a class='{{tab.id}} {{tab.cssClass}}' data-action='tab' data-group='{{tab.group}}' data-tab='{{tab.id}}'> <div class="levelup-tab-container">
{{localize tab.label}} <a class='{{tab.id}} {{tab.cssClass}}' data-action='tab' data-group='{{tab.group}}' data-tab='{{tab.id}}'>
</a> {{localize tab.label}}
{{#if tab.progress}} </a>
<div>{{tab.progress.selected}}/{{tab.progress.max}}</div> {{#if tab.progress}}
{{/if}} <div>{{tab.progress.selected}}/{{tab.progress.max}}</div>
</div> {{/if}}
{{/if}} </div>
{{/each}} {{/if}}
</nav> {{/each}}
{{/if}} </nav>
<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}}
{{#if this.navigate.next.show}} {{#if this.levelupAuto}}
{{#if this.navigate.next.toSummary}} <div class="levelup-navigation-actions {{#if (not this.showTabs)}}test{{/if}}">
<button data-action="activatePart" data-part="summary" {{#if this.navigate.next.disabled}}disabled{{/if}}>{{localize "DAGGERHEART.APPLICATIONS.Levelup.navigateToSummary"}}</button> {{#if this.navigate.previous.fromSummary}}
{{else}} <button data-action="activatePart" data-part="advancements">{{localize "DAGGERHEART.APPLICATIONS.Levelup.navigateToLevelup"}}</button>
<button data-action="updateCurrentLevel" data-forward="true" {{#if this.navigate.next.disabled}}disabled{{/if}}>{{this.navigate.next.label}}</button> {{else}}
{{/if}} {{#if (not this.navigate.previous.disabled)}}
{{else}} <button data-action="updateCurrentLevel" >{{this.navigate.previous.label}}</button>
<div></div> {{/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}} {{/if}}
</div> </div>
</div> <line-div></line-div>
<line-div></line-div> {{/if}}
</section> </section>

View file

@ -11,6 +11,7 @@
{{formGroup settingFields.schema.fields.actionPoints value=settingFields._source.actionPoints localize=true}} {{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.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.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"> <footer class="form-footer">
<button data-action="reset"> <button data-action="reset">

View file

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