Changed so companion can level up on its own (#879)

This commit is contained in:
WBHarry 2025-08-13 10:39:23 +02:00 committed by GitHub
parent 3d6a276bbc
commit 294055ad51
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 216 additions and 40 deletions

View file

@ -189,7 +189,11 @@
"title": "Multiclass Subclass",
"text": "Do you want to add this subclass as your multiclass subclass?"
},
"cannotRemoveCoreExperience": "You are using Levelup Auto. You cannot remove an experience given to you by the rule progression."
"cannotRemoveCoreExperience": "You are using Levelup Auto. You cannot remove an experience given to you by the rule progression.",
"companionLevelup": {
"confirmTitle": "Companion Levelup",
"confirmText": "Would you like to level up your companion {name} by {levelChange} levels at this time? (You can do it manually later)"
}
},
"Companion": {
"FIELDS": {
@ -2370,7 +2374,8 @@
"rulesOn": "Rules On",
"rulesOff": "Rules Off",
"remainingUses": "Uses refresh on {type}",
"rightClickExtand": "Right-Click to extand"
"rightClickExtand": "Right-Click to extand",
"companionPartnerLevelBlock": "The companion needs an assigned partner to level up."
}
}
}

View file

@ -67,6 +67,28 @@ export default class DhCompanionLevelUp extends BaseLevelUp {
const levelKeys = Object.keys(this.levelup.levels);
const actorDamageDice = this.actor.system.attack.damage.parts[0].value.dice;
const actorRange = this.actor.system.attack.range;
let achievementExperiences = [];
for (var levelKey of levelKeys) {
const level = this.levelup.levels[levelKey];
if (Number(levelKey) < this.levelup.startLevel) continue;
achievementExperiences = level.achievements.experiences
? Object.values(level.achievements.experiences).reduce((acc, experience) => {
if (experience.name) acc.push(experience);
return acc;
}, [])
: [];
}
context.achievements = {
experiences: {
values: achievementExperiences,
shown: achievementExperiences.length > 0
}
};
context.achievements = context.achievements.experiences.shown ? context.achievements : undefined;
const advancement = {};
for (var levelKey of levelKeys) {
const level = this.levelup.levels[levelKey];

View file

@ -1,3 +1,4 @@
import DhCompanionLevelUp from '../../levelup/companionLevelup.mjs';
import DHBaseActorSheet from '../api/base-actor.mjs';
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
@ -6,7 +7,9 @@ export default class DhCompanionSheet extends DHBaseActorSheet {
static DEFAULT_OPTIONS = {
classes: ['actor', 'companion'],
position: { width: 300 },
actions: {}
actions: {
levelManagement: DhCompanionSheet.#levelManagement
}
};
static PARTS = {
@ -25,4 +28,25 @@ export default class DhCompanionSheet extends DHBaseActorSheet {
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
}
};
/** @inheritDoc */
async _onRender(context, options) {
await super._onRender(context, options);
this.element
.querySelector('.level-value')
?.addEventListener('change', event => this.document.updateLevel(Number(event.currentTarget.value)));
}
/* -------------------------------------------- */
/* Application Clicks Actions */
/* -------------------------------------------- */
/**
* Opens the companions level management window.
* @type {ApplicationClickAction}
*/
static #levelManagement() {
new DhCompanionLevelUp(this.document).render({ force: true });
}
}

View file

@ -602,7 +602,20 @@ export default class DhCharacter extends BaseDataActor {
}
prepareDerivedData() {
const baseHope = this.resources.hope.value + (this.companion?.system?.resources?.hope ?? 0);
let baseHope = this.resources.hope.value;
if (this.companion) {
for (let levelKey in this.companion.system.levelData.levelups) {
const level = this.companion.system.levelData.levelups[levelKey];
for (let selection of level.selections) {
switch (selection.type) {
case 'hope':
this.resources.hope.max += selection.value;
break;
}
}
}
}
this.resources.hope.value = Math.min(baseHope, this.resources.hope.max);
this.attack.roll.trait = this.rules.attack.roll.trait ?? this.attack.roll.trait;

View file

@ -40,12 +40,14 @@ export default class DhCompanion extends BaseDataActor {
experiences: new fields.TypedObjectField(
new fields.SchemaField({
name: new fields.StringField({}),
value: new fields.NumberField({ integer: true, initial: 0 })
value: new fields.NumberField({ integer: true, initial: 0 }),
description: new fields.StringField(),
core: new fields.BooleanField({ initial: false })
}),
{
initial: {
experience1: { value: 2 },
experience2: { value: 2 }
experience1: { value: 2, core: true },
experience2: { value: 2, core: true }
}
}
),
@ -134,6 +136,23 @@ export default class DhCompanion extends BaseDataActor {
}
}
async _preUpdate(changes, options, userId) {
const allowed = await super._preUpdate(changes, options, userId);
if (allowed === false) return;
/* The first two experiences are always marked as core */
if (changes.system?.experiences && Object.keys(this.experiences).length < 2) {
const experiences = new Set(Object.keys(this.experiences));
const changeExperiences = new Set(Object.keys(changes.system.experiences));
const newExperiences = Array.from(changeExperiences.difference(experiences));
for (var i = 0; i < Math.min(newExperiences.length, 2 - experiences.size); i++) {
const experience = newExperiences[i];
changes.system.experiences[experience].core = true;
}
}
}
async _preDelete() {
if (this.partner) {
await this.partner.update({ 'system.companion': null });

View file

@ -404,7 +404,27 @@ export const defaultCompanionTier = {
start: 2,
end: 10
},
initialAchievements: {},
initialAchievements: {
experience: {
nr: 1,
modifier: 2
}
},
/* Improved this. Quick solution for companions */
extraAchievements: {
5: {
experience: {
nr: 1,
modifier: 2
}
},
8: {
experience: {
nr: 1,
modifier: 2
}
}
},
availableOptions: 1,
domainCardByLevel: 0,
options: {

View file

@ -26,6 +26,7 @@ export class DhLevelup extends foundry.abstract.DataModel {
return acc;
}, {})
: {};
const domainCards = [...Array(tier.domainCardByLevel).keys()].reduce((acc, _) => {
const id = foundry.utils.randomID();
acc[id] = { uuid: null, itemUuid: null, level: i };
@ -42,6 +43,20 @@ export class DhLevelup extends foundry.abstract.DataModel {
belongingLevels.push(i);
}
/* Improve. Temporary handling for Companion new experiences */
Object.keys(tier.extraAchievements ?? {}).forEach(key => {
const level = Number(key);
if (level >= startLevel && level <= endLevel) {
const levelExtras = tier.extraAchievements[level];
if (levelExtras.experience) {
levels[level].achievements.experiences[foundry.utils.randomID()] = {
name: '',
modifier: levelExtras.experience.modifier
};
}
}
});
tiers[key] = {
name: tier.name,
belongingLevels: belongingLevels,

View file

@ -2,6 +2,7 @@ import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs';
import { LevelOptionType } from '../data/levelTier.mjs';
import DHFeature from '../data/item/feature.mjs';
import { damageKeyToNumber } from '../helpers/utils.mjs';
import DhCompanionLevelUp from '../applications/levelup/companionLevelup.mjs';
export default class DhpActor extends Actor {
/**
@ -142,9 +143,6 @@ export default class DhpActor extends Actor {
}, {})
});
this.update(getUpdate());
if (this.system.companion) {
this.system.companion.update(getUpdate());
}
}
if (subclassFeatureState.class) {
@ -195,10 +193,6 @@ export default class DhpActor extends Actor {
}
});
this.sheet.render();
if (this.system.companion) {
this.system.companion.updateLevel(newLevel);
}
}
}
@ -219,16 +213,6 @@ export default class DhpActor extends Actor {
core: true
}
});
if (this.system.companion) {
await this.system.companion.update({
[`system.experiences.${experienceKey}`]: {
name: '',
value: experience.modifier,
core: true
}
});
}
}
}
@ -405,6 +389,7 @@ export default class DhpActor extends Actor {
};
}
const levelChange = this.system.levelData.level.changed - this.system.levelData.level.current;
await this.update({
system: {
levelData: {
@ -417,8 +402,21 @@ export default class DhpActor extends Actor {
});
this.sheet.render();
if (this.system.companion) {
this.system.companion.updateLevel(this.system.levelData.level.changed);
if (this.system.companion && !this.system.companion.system.levelData.canLevelUp) {
const confirmed = await foundry.applications.api.DialogV2.confirm({
window: {
title: game.i18n.localize('DAGGERHEART.ACTORS.Character.companionLevelup.confirmTitle')
},
content: game.i18n.format('DAGGERHEART.ACTORS.Character.companionLevelup.confirmText', {
name: this.system.companion.name,
levelChange: levelChange
})
});
if (!confirmed) return;
await this.system.companion.updateLevel(this.system.companion.system.levelData.level.current + levelChange);
new DhCompanionLevelUp(this.system.companion).render({ force: true });
}
}

View file

@ -185,9 +185,50 @@
}
}
.level-up-label {
font-size: 24px;
padding-top: 8px;
.level-div {
white-space: nowrap;
display: flex;
justify-content: end;
.label {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
.input-section {
display: flex;
align-items: center;
justify-content: space-between;
}
}
input {
width: 40px;
padding: 0;
text-align: center;
border: 0;
}
.level-button {
color: light-dark(@dark, @beige);
font-size: 18px;
line-height: 1;
min-height: unset;
height: min-content;
padding: 4px;
font-family: 'Cinzel', serif;
margin: 0;
font-weight: normal;
border-color: light-dark(@dark-blue, @golden);
background-color: light-dark(transparent, @deep-black);
&:hover {
background-image: none;
background-color: var(--color-warm-2);
filter: drop-shadow(0 0 3px lightgray);
}
}
}
}

View file

@ -1,6 +1,6 @@
<section
class='tab {{tabs.details.cssClass}} {{tabs.details.id}}'
data-tab='{{tabs.details.id}}'
data-tab='{{tabs.details.id}}'
data-group='{{tabs.details.group}}'
>
<fieldset class="one-column">
@ -14,7 +14,7 @@
<div class="form-fields">
<label>{{localize "DAGGERHEART.ACTORS.Companion.FIELDS.partner.label"}}</label>
<select class="partner-value" name="system.partner">
{{selectOptions playerCharacters selected=document._source.system.partner.uuid labelAttr="name" valueAttr="key" blank=""}}
{{selectOptions playerCharacters selected=document.system.partner.uuid labelAttr="name" valueAttr="key" blank=""}}
</select>
</div>
</div>

View file

@ -12,12 +12,16 @@
<ul class="experience-list">
{{#each document._source.system.experiences as |experience key|}}
<li class="experience-item">
<div class="experience-inner-item {{#if @root.levelupAuto}}no-controls{{/if}}">
<div class="experience-inner-item">
<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>
<span data-tooltip="{{#if experience.core}}{{localize 'DAGGERHEART.ACTORS.Character.cannotRemoveCoreExperience'}}{{else}}{{localize 'CONTROLS.CommonDelete'}}{{/if}}">
<a data-action="removeExperience" data-experience="{{key}}" class="{{#if experience.core}}disabled{{/if}}">
<i class="fa-solid fa-trash"></i>
</a>
</span>
</div>
<textarea name="system.experiences.{{key}}.description">{{experience.description}}</textarea>
</li>
{{/each}}

View file

@ -44,7 +44,7 @@
<side-line-div></side-line-div>
</div>
{{#each source.system.experiences as |experience id|}}
<div class="experience-row">
<div class="experience-row" data-tooltip-text="{{experience.description}}">
<div class="experience-value">
+{{experience.value}}
</div>

View file

@ -32,10 +32,25 @@
<h4>{{localize "DAGGERHEART.GENERAL.stress"}}</h4>
</div>
</div>
<h3 class="level-up-label">
{{localize "DAGGERHEART.GENERAL.level"}}
{{source.system.levelData.level.changed}}
</h3>
<div class='level-div'>
<h3 class='label'>
{{localize 'DAGGERHEART.GENERAL.level'}}
<div class="input-section">
{{#if document.system.levelData.canLevelUp}}
<button
type="button"
class="level-button glow" data-tooltip="{{localize "DAGGERHEART.ACTORS.Character.levelUp"}}"
data-action="levelManagement"
>
<i class="fa-solid fa-angles-up"></i>
</button>
{{/if}}
<span {{#unless document.system.partner}}data-tooltip="{{localize "DAGGERHEART.UI.Tooltip.companionPartnerLevelBlock"}}"{{/unless}}>
<input type="text" data-dtype="Number" class="level-value" value={{document.system.levelData.level.changed}} {{#if document.system.needsCharacterSetup}}disabled{{/if}} {{disabled (not document.system.partner)}} />
</span>
</div>
</h3>
</div>
</div>
<div class="companion-navigation">
{{> 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs'}}