From 294055ad51c4b127feec76f826056864b15098b1 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Wed, 13 Aug 2025 10:39:23 +0200 Subject: [PATCH] Changed so companion can level up on its own (#879) --- lang/en.json | 9 +++- .../applications/levelup/companionLevelup.mjs | 22 +++++++++ .../applications/sheets/actors/companion.mjs | 26 +++++++++- module/data/actor/character.mjs | 15 +++++- module/data/actor/companion.mjs | 25 ++++++++-- module/data/levelTier.mjs | 22 ++++++++- module/data/levelup.mjs | 15 ++++++ module/documents/actor.mjs | 36 +++++++------- .../less/sheets/actors/companion/header.less | 47 +++++++++++++++++-- .../companion-settings/details.hbs | 4 +- .../companion-settings/experiences.hbs | 10 ++-- templates/sheets/actors/companion/details.hbs | 2 +- templates/sheets/actors/companion/header.hbs | 23 +++++++-- 13 files changed, 216 insertions(+), 40 deletions(-) diff --git a/lang/en.json b/lang/en.json index a06fa69d..f473d98d 100755 --- a/lang/en.json +++ b/lang/en.json @@ -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." } } } diff --git a/module/applications/levelup/companionLevelup.mjs b/module/applications/levelup/companionLevelup.mjs index 0391ce0c..4b8f9b47 100644 --- a/module/applications/levelup/companionLevelup.mjs +++ b/module/applications/levelup/companionLevelup.mjs @@ -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]; diff --git a/module/applications/sheets/actors/companion.mjs b/module/applications/sheets/actors/companion.mjs index 87cfda27..dd102b1e 100644 --- a/module/applications/sheets/actors/companion.mjs +++ b/module/applications/sheets/actors/companion.mjs @@ -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 }); + } } diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index c2a8f400..71b734cd 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -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; diff --git a/module/data/actor/companion.mjs b/module/data/actor/companion.mjs index 7a11f1d1..48572460 100644 --- a/module/data/actor/companion.mjs +++ b/module/data/actor/companion.mjs @@ -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 }); diff --git a/module/data/levelTier.mjs b/module/data/levelTier.mjs index e9e8d47b..e42cfc54 100644 --- a/module/data/levelTier.mjs +++ b/module/data/levelTier.mjs @@ -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: { diff --git a/module/data/levelup.mjs b/module/data/levelup.mjs index 0f248f45..665b3264 100644 --- a/module/data/levelup.mjs +++ b/module/data/levelup.mjs @@ -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, diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index c75db559..68c12b6f 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -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 }); } } diff --git a/styles/less/sheets/actors/companion/header.less b/styles/less/sheets/actors/companion/header.less index 9f1cc909..bc1995aa 100644 --- a/styles/less/sheets/actors/companion/header.less +++ b/styles/less/sheets/actors/companion/header.less @@ -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); + } + } } } diff --git a/templates/sheets-settings/companion-settings/details.hbs b/templates/sheets-settings/companion-settings/details.hbs index e4293443..a15fc02e 100644 --- a/templates/sheets-settings/companion-settings/details.hbs +++ b/templates/sheets-settings/companion-settings/details.hbs @@ -1,6 +1,6 @@
@@ -14,7 +14,7 @@
diff --git a/templates/sheets-settings/companion-settings/experiences.hbs b/templates/sheets-settings/companion-settings/experiences.hbs index 9aebc2c3..9d4aefe4 100644 --- a/templates/sheets-settings/companion-settings/experiences.hbs +++ b/templates/sheets-settings/companion-settings/experiences.hbs @@ -12,12 +12,16 @@