From ce96ffa0a3885590e3290969f46c1395ec7752e8 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 25 Jan 2026 15:21:06 +0100 Subject: [PATCH] [Feature] 1383 - Companion Bonus Levelups (#1565) * Fixed so that companions can get bonus levelupchoices from their partner * Fixed collection prep order * Added ActiveEffects to Beastbound features * Corrected styling * Added migration for overleveled companions * Raised version * Moved migration to 1.6.0. Sillyness --- daggerheart.mjs | 2 + .../applications/levelup/companionLevelup.mjs | 6 +- .../applications/sheets/actors/companion.mjs | 9 - module/data/actor/character.mjs | 15 + module/data/actor/companion.mjs | 27 +- module/data/companionLevelup.mjs | 370 ++++++++++++++++++ module/documents/actor.mjs | 5 + module/documents/collections/_module.mjs | 1 + .../documents/collections/actorCollection.mjs | 14 + module/systemRegistration/migrations.mjs | 20 + ...re_Advanced_Training_uGcs785h94RMtueH.json | 46 ++- ...ture_Expert_Training_iCXtOWBKv1FdKdWz.json | 46 ++- .../less/sheets/actors/companion/header.less | 2 +- system.json | 2 +- templates/sheets/actors/companion/header.hbs | 4 +- 15 files changed, 551 insertions(+), 18 deletions(-) create mode 100644 module/data/companionLevelup.mjs create mode 100644 module/documents/collections/_module.mjs create mode 100644 module/documents/collections/actorCollection.mjs diff --git a/daggerheart.mjs b/daggerheart.mjs index 7ae01590..61e74f9b 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -3,6 +3,7 @@ import * as applications from './module/applications/_module.mjs'; import * as data from './module/data/_module.mjs'; import * as models from './module/data/_module.mjs'; import * as documents from './module/documents/_module.mjs'; +import * as collections from './module/documents/collections/_module.mjs'; import * as dice from './module/dice/_module.mjs'; import * as fields from './module/data/fields/_module.mjs'; import RegisterHandlebarsHelpers from './module/helpers/handlebarsHelper.mjs'; @@ -36,6 +37,7 @@ CONFIG.Dice.daggerheart = { CONFIG.Actor.documentClass = documents.DhpActor; CONFIG.Actor.dataModels = models.actors.config; +CONFIG.Actor.collection = collections.DhActorCollection; CONFIG.Item.documentClass = documents.DHItem; CONFIG.Item.dataModels = models.items.config; diff --git a/module/applications/levelup/companionLevelup.mjs b/module/applications/levelup/companionLevelup.mjs index 4b8f9b47..7f11ccff 100644 --- a/module/applications/levelup/companionLevelup.mjs +++ b/module/applications/levelup/companionLevelup.mjs @@ -1,6 +1,6 @@ import BaseLevelUp from './levelup.mjs'; import { defaultCompanionTier, LevelOptionType } from '../../data/levelTier.mjs'; -import { DhLevelup } from '../../data/levelup.mjs'; +import { DhCompanionLevelup as DhLevelup } from '../../data/companionLevelup.mjs'; import { diceTypes, range } from '../../config/generalConfig.mjs'; export default class DhCompanionLevelUp extends BaseLevelUp { @@ -9,7 +9,9 @@ export default class DhCompanionLevelUp extends BaseLevelUp { this.levelTiers = this.addBonusChoices(defaultCompanionTier); const playerLevelupData = actor.system.levelData; - this.levelup = new DhLevelup(DhLevelup.initializeData(this.levelTiers, playerLevelupData)); + this.levelup = new DhLevelup( + DhLevelup.initializeData(this.levelTiers, playerLevelupData, actor.system.levelupChoicesLeft) + ); } async _preparePartContext(partId, context) { diff --git a/module/applications/sheets/actors/companion.mjs b/module/applications/sheets/actors/companion.mjs index 21f09f02..b30b9c07 100644 --- a/module/applications/sheets/actors/companion.mjs +++ b/module/applications/sheets/actors/companion.mjs @@ -38,15 +38,6 @@ export default class DhCompanionSheet extends DHBaseActorSheet { } }; - /** @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 */ /* -------------------------------------------- */ diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index 594f078c..e8da2e10 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -662,6 +662,11 @@ export default class DhCharacter extends BaseDataActor { const globalHopeMax = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxHope; this.resources.hope.max = globalHopeMax - this.scars; this.resources.hitPoints.max += this.class.value?.system?.hitPoints ?? 0; + + /* Companion Related Data */ + this.companionData = { + levelupChoices: this.levelData.level.current - 1 + }; } prepareDerivedData() { @@ -733,6 +738,16 @@ export default class DhCharacter extends BaseDataActor { }; } } + + /* Force companion data prep */ + if (this.companion) { + if ( + changes.system?.levelData?.level?.current !== undefined && + changes.system.levelData.level.current !== this._source.levelData.level.current + ) { + this.companion.update(this.companion.toObject(), { diff: false, recursive: false }); + } + } } async _preDelete() { diff --git a/module/data/actor/companion.mjs b/module/data/actor/companion.mjs index fa1965bd..a2cb5db3 100644 --- a/module/data/actor/companion.mjs +++ b/module/data/actor/companion.mjs @@ -108,7 +108,11 @@ export default class DhCompanion extends BaseDataActor { get proficiency() { return this.partner?.system?.proficiency ?? 1; } - + + get canLevelUp() { + return this.levelupChoicesLeft > 0; + } + isItemValid() { return false; } @@ -147,6 +151,17 @@ export default class DhCompanion extends BaseDataActor { } } + prepareDerivedData() { + /* Partner Related Setup */ + if (this.partner) { + this.levelData.level.changed = this.partner.system.levelData.level.current; + this.levelupChoicesLeft = Object.values(this.levelData.levelups).reduce((acc, curr) => { + acc = Math.max(acc - curr.selections.length, 0); + return acc; + }, this.partner.system.companionData.levelupChoices); + } + } + async _preUpdate(changes, options, userId) { const allowed = await super._preUpdate(changes, options, userId); if (allowed === false) return; @@ -162,6 +177,16 @@ export default class DhCompanion extends BaseDataActor { changes.system.experiences[experience].core = true; } } + + /* Force partner data prep */ + if (this.partner) { + if ( + changes.system?.levelData?.level?.current !== undefined && + changes.system.levelData.level.current !== this._source.levelData.level.current + ) { + this.partner.update(this.partner.toObject(), { diff: false, recursive: false }); + } + } } async _preDelete() { diff --git a/module/data/companionLevelup.mjs b/module/data/companionLevelup.mjs new file mode 100644 index 00000000..7ab61210 --- /dev/null +++ b/module/data/companionLevelup.mjs @@ -0,0 +1,370 @@ +import { abilities } from '../config/actorConfig.mjs'; +import { chunkify } from '../helpers/utils.mjs'; +import { LevelOptionType } from './levelTier.mjs'; + +export class DhCompanionLevelup extends foundry.abstract.DataModel { + static initializeData(levelTierData, pcLevelData, origChoicesLeft) { + let choicesLeft = origChoicesLeft; + + const { current, changed } = pcLevelData.level; + const bonusChoicesOnly = current === changed; + const startLevel = bonusChoicesOnly ? current : current + 1; + const endLevel = bonusChoicesOnly ? startLevel : changed; + + const tiers = {}; + const levels = {}; + const tierKeys = Object.keys(levelTierData.tiers); + tierKeys.forEach(key => { + const tier = levelTierData.tiers[key]; + const belongingLevels = []; + for (var i = tier.levels.start; i <= tier.levels.end; i++) { + if (i <= endLevel) { + const initialAchievements = i === tier.levels.start ? tier.initialAchievements : {}; + const experiences = initialAchievements.experience + ? [...Array(initialAchievements.experience.nr).keys()].reduce((acc, _) => { + acc[foundry.utils.randomID()] = { + name: '', + modifier: initialAchievements.experience.modifier + }; + return acc; + }, {}) + : {}; + + const currentChoices = pcLevelData.levelups[i]?.selections?.length; + const maxSelections = + i === endLevel + ? choicesLeft + (currentChoices ?? 0) + : (currentChoices ?? tier.maxSelections[i]); + if (!pcLevelData.levelups[i]) choicesLeft -= maxSelections; + + levels[i] = DhLevelupLevel.initializeData(pcLevelData.levelups[i], maxSelections, { + ...initialAchievements, + experiences, + domainCards: {} + }); + } + + 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, + options: Object.keys(tier.options).reduce((acc, key) => { + acc[key] = tier.options[key].toObject?.() ?? tier.options[key]; + return acc; + }, {}) + }; + }); + + return { + tiers, + levels, + startLevel, + currentLevel: startLevel, + endLevel + }; + } + + static defineSchema() { + const fields = foundry.data.fields; + + return { + tiers: new fields.TypedObjectField( + new fields.SchemaField({ + name: new fields.StringField({ required: true }), + belongingLevels: new fields.ArrayField(new fields.NumberField({ required: true, integer: true })), + options: new fields.TypedObjectField( + new fields.SchemaField({ + label: new fields.StringField({ required: true }), + checkboxSelections: new fields.NumberField({ required: true, integer: true }), + minCost: new fields.NumberField({ required: true, integer: true }), + type: new fields.StringField({ required: true, choices: LevelOptionType }), + value: new fields.NumberField({ integer: true }), + amount: new fields.NumberField({ integer: true }) + }) + ) + }) + ), + levels: new fields.TypedObjectField(new fields.EmbeddedDataField(DhLevelupLevel)), + startLevel: new fields.NumberField({ required: true, integer: true }), + currentLevel: new fields.NumberField({ required: true, integer: true }), + endLevel: new fields.NumberField({ required: true, integer: true }) + }; + } + + #levelFinished(levelKey) { + const allSelectionsMade = this.levels[levelKey].nrSelections.available === 0; + const allChoicesMade = Object.keys(this.levels[levelKey].choices).every(choiceKey => { + const choice = this.levels[levelKey].choices[choiceKey]; + return Object.values(choice).every(checkbox => { + switch (choiceKey) { + case 'trait': + case 'experience': + case 'domainCard': + case 'subclass': + case 'vicious': + return checkbox.data.length === (checkbox.amount ?? 1); + case 'multiclass': + const classSelected = checkbox.data.length === 1; + const domainSelected = checkbox.secondaryData.domain; + const subclassSelected = checkbox.secondaryData.subclass; + return classSelected && domainSelected && subclassSelected; + default: + return true; + } + }); + }); + const experiencesSelected = !this.levels[levelKey].achievements.experiences + ? true + : Object.values(this.levels[levelKey].achievements.experiences).every(exp => exp.name); + const domainCardsSelected = Object.values(this.levels[levelKey].achievements.domainCards) + .filter(x => x.level <= this.endLevel) + .every(card => card.uuid); + const allAchievementsSelected = experiencesSelected && domainCardsSelected; + + return allSelectionsMade && allChoicesMade && allAchievementsSelected; + } + + get currentLevelFinished() { + return this.#levelFinished(this.currentLevel); + } + + get allLevelsFinished() { + return Object.keys(this.levels) + .filter(level => Number(level) >= this.startLevel) + .every(this.#levelFinished.bind(this)); + } + + get unmarkedTraits() { + const possibleLevels = Object.values(this.tiers).reduce((acc, tier) => { + if (tier.belongingLevels.includes(this.currentLevel)) acc = tier.belongingLevels; + return acc; + }, []); + + return Object.keys(this.levels) + .filter(key => possibleLevels.some(x => x === Number(key))) + .reduce( + (acc, levelKey) => { + const level = this.levels[levelKey]; + Object.values(level.choices).forEach(choice => + Object.values(choice).forEach(checkbox => { + if ( + checkbox.type === 'trait' && + checkbox.data.length > 0 && + Number(levelKey) !== this.currentLevel + ) { + checkbox.data.forEach(data => delete acc[data]); + } + }) + ); + + return acc; + }, + { ...abilities } + ); + } + + get classUpgradeChoices() { + let subclasses = []; + let multiclass = null; + Object.keys(this.levels).forEach(levelKey => { + const level = this.levels[levelKey]; + Object.values(level.choices).forEach(choice => { + Object.values(choice).forEach(checkbox => { + if (checkbox.type === 'multiclass') { + multiclass = { + class: checkbox.data.length > 0 ? checkbox.data[0] : null, + domain: checkbox.secondaryData.domain ?? null, + subclass: checkbox.secondaryData.subclass ?? null, + tier: checkbox.tier, + level: levelKey + }; + } + if (checkbox.type === 'subclass') { + subclasses.push({ + tier: checkbox.tier, + level: levelKey + }); + } + }); + }); + }); + return { subclasses, multiclass }; + } + + get tiersForRendering() { + const tierKeys = Object.keys(this.tiers); + const selections = Object.keys(this.levels).reduce( + (acc, key) => { + const level = this.levels[key]; + Object.keys(level.choices).forEach(optionKey => { + const choice = level.choices[optionKey]; + Object.keys(choice).forEach(checkboxNr => { + const checkbox = choice[checkboxNr]; + if (!acc[checkbox.tier][optionKey]) acc[checkbox.tier][optionKey] = {}; + Object.keys(choice).forEach(checkboxNr => { + acc[checkbox.tier][optionKey][checkboxNr] = { ...checkbox, level: Number(key) }; + }); + }); + }); + + return acc; + }, + tierKeys.reduce((acc, key) => { + acc[key] = {}; + return acc; + }, {}) + ); + + const { multiclass, subclasses } = this.classUpgradeChoices; + return tierKeys.map((tierKey, tierIndex) => { + const tier = this.tiers[tierKey]; + const multiclassInTier = multiclass?.tier === Number(tierKey); + const subclassInTier = subclasses.some(x => x.tier === Number(tierKey)); + + return { + name: game.i18n.localize(tier.name), + active: this.currentLevel >= Math.min(...tier.belongingLevels), + groups: Object.keys(tier.options).map(optionKey => { + const option = tier.options[optionKey]; + + const checkboxes = [...Array(option.checkboxSelections).keys()].flatMap(index => { + const checkboxNr = index + 1; + const checkboxData = selections[tierKey]?.[optionKey]?.[checkboxNr]; + const checkbox = { ...option, checkboxNr, tier: tierKey }; + + if (checkboxData) { + checkbox.level = checkboxData.level; + checkbox.selected = true; + checkbox.disabled = checkbox.level !== this.currentLevel; + } + + if (optionKey === 'multiclass') { + if ((multiclass && !multiclassInTier) || subclassInTier) { + checkbox.disabled = true; + } + } + + if (optionKey === 'subclass' && multiclassInTier) { + checkbox.disabled = true; + } + + return checkbox; + }); + + let label = game.i18n.localize(option.label); + if (optionKey === 'domainCard') { + const maxLevel = tier.belongingLevels[tier.belongingLevels.length - 1]; + label = game.i18n.format(option.label, { maxLevel }); + } + + return { + label: label, + checkboxGroups: chunkify(checkboxes, option.minCost, chunkedBoxes => { + const anySelected = chunkedBoxes.some(x => x.selected); + const anyDisabled = chunkedBoxes.some(x => x.disabled); + return { + multi: option.minCost > 1, + checkboxes: chunkedBoxes.map(x => ({ + ...x, + selected: anySelected, + disabled: anyDisabled + })) + }; + }) + }; + }) + }; + }); + } +} + +export class DhLevelupLevel extends foundry.abstract.DataModel { + static initializeData(levelData = { selections: [] }, maxSelections, achievements) { + return { + maxSelections: maxSelections, + achievements: { + experiences: levelData.achievements?.experiences ?? achievements.experiences ?? {}, + domainCards: levelData.achievements?.domainCards + ? levelData.achievements.domainCards.reduce((acc, card, index) => { + acc[index] = { ...card }; + return acc; + }, {}) + : (achievements.domainCards ?? {}), + proficiency: levelData.achievements?.proficiency ?? achievements.proficiency ?? null + }, + choices: levelData.selections.reduce((acc, data) => { + if (!acc[data.optionKey]) acc[data.optionKey] = {}; + acc[data.optionKey][data.checkboxNr] = { ...data }; + + return acc; + }, {}) + }; + } + + static defineSchema() { + const fields = foundry.data.fields; + + return { + maxSelections: new fields.NumberField({ required: true, integer: true }), + achievements: new fields.SchemaField({ + experiences: new fields.TypedObjectField( + new fields.SchemaField({ + name: new fields.StringField({ required: true }), + modifier: new fields.NumberField({ required: true, integer: true }) + }) + ), + domainCards: new fields.TypedObjectField( + new fields.SchemaField({ + uuid: new fields.StringField({ required: true, nullable: true, initial: null }), + itemUuid: new fields.StringField({ required: true }), + level: new fields.NumberField({ required: true, integer: true }) + }) + ), + proficiency: new fields.NumberField({ integer: true }) + }), + choices: new fields.TypedObjectField( + new fields.TypedObjectField( + new fields.SchemaField({ + tier: new fields.NumberField({ required: true, integer: true }), + minCost: new fields.NumberField({ required: true, integer: true }), + amount: new fields.NumberField({ integer: true }), + value: new fields.StringField(), + data: new fields.ArrayField(new fields.StringField()), + secondaryData: new fields.TypedObjectField(new fields.StringField()), + type: new fields.StringField({ required: true }) + }) + ) + ) + }; + } + + get nrSelections() { + const selections = Object.keys(this.choices).reduce((acc, choiceKey) => { + const choice = this.choices[choiceKey]; + acc += Object.values(choice).reduce((acc, x) => acc + x.minCost, 0); + + return acc; + }, 0); + + return { + selections: selections, + available: this.maxSelections - selections + }; + } +} diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 9cfe3385..e8bea0bf 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -241,6 +241,11 @@ export default class DhpActor extends Actor { } } }); + + if (this.system.companion) { + this.system.companion.updateLevel(usedLevel); + } + this.sheet.render(); } } diff --git a/module/documents/collections/_module.mjs b/module/documents/collections/_module.mjs new file mode 100644 index 00000000..a24c1d85 --- /dev/null +++ b/module/documents/collections/_module.mjs @@ -0,0 +1 @@ +export { default as DhActorCollection } from './actorCollection.mjs'; diff --git a/module/documents/collections/actorCollection.mjs b/module/documents/collections/actorCollection.mjs new file mode 100644 index 00000000..a3714b30 --- /dev/null +++ b/module/documents/collections/actorCollection.mjs @@ -0,0 +1,14 @@ +export default class DhActorCollection extends foundry.documents.collections.Actors { + /** Ensure companions are initialized after all other subtypes. */ + _initialize() { + super._initialize(); + const companions = []; + for (const actor of this.values()) { + if (actor.type === 'companion') companions.push(actor); + } + for (const actor of companions) { + this.delete(actor.id); + this.set(actor.id, actor); + } + } +} diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs index 086647bf..743d42a4 100644 --- a/module/systemRegistration/migrations.mjs +++ b/module/systemRegistration/migrations.mjs @@ -212,6 +212,7 @@ export async function runMigrations() { } if (foundry.utils.isNewerVersion('1.5.5', lastMigrationVersion)) { + /* Clear out Environments that were added directly from compendium */ for (const scene of game.scenes) { if (!scene.flags.daggerheart) continue; const systemData = new game.system.api.data.scenes.DHScene(scene.flags.daggerheart); @@ -226,6 +227,25 @@ export async function runMigrations() { lastMigrationVersion = '1.5.5'; } + + if (foundry.utils.isNewerVersion('1.6.0', lastMigrationVersion)) { + /* Delevel any companions that are higher level than their partner character */ + for (const companion of game.actors.filter(x => x.type === 'companion')) { + if (companion.system.levelData.level.current <= 1) continue; + + if (!companion.system.partner) { + await companion.updateLevel(1); + } else { + const endLevel = companion.system.partner.system.levelData.level.current; + if (endLevel < companion.system.levelData.level.current) { + companion.system.levelData.level.changed = companion.system.levelData.level.current; + await companion.updateLevel(endLevel); + } + } + } + + lastMigrationVersion = '1.6.0'; + } //#endregion await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion, lastMigrationVersion); diff --git a/src/packs/subclasses/feature_Advanced_Training_uGcs785h94RMtueH.json b/src/packs/subclasses/feature_Advanced_Training_uGcs785h94RMtueH.json index 8f9c63f1..16a5cd79 100644 --- a/src/packs/subclasses/feature_Advanced_Training_uGcs785h94RMtueH.json +++ b/src/packs/subclasses/feature_Advanced_Training_uGcs785h94RMtueH.json @@ -16,7 +16,51 @@ "artist": "" } }, - "effects": [], + "effects": [ + { + "name": "Advanced Training", + "type": "base", + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, + "_id": "bKOuMxhB2Jth3j2T", + "img": "icons/creatures/mammals/wolf-howl-moon-gray.webp", + "changes": [ + { + "key": "system.companionData.levelupChoices", + "mode": 2, + "value": "2", + "priority": null + } + ], + "disabled": false, + "duration": { + "startTime": null, + "combat": null, + "seconds": null, + "rounds": null, + "turns": null, + "startRound": null, + "startTurn": null + }, + "description": "

Choose two additional level-up options for your companion.

", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!uGcs785h94RMtueH.bKOuMxhB2Jth3j2T" + } + ], "sort": 0, "ownership": { "default": 0, diff --git a/src/packs/subclasses/feature_Expert_Training_iCXtOWBKv1FdKdWz.json b/src/packs/subclasses/feature_Expert_Training_iCXtOWBKv1FdKdWz.json index e5850da6..06819dc9 100644 --- a/src/packs/subclasses/feature_Expert_Training_iCXtOWBKv1FdKdWz.json +++ b/src/packs/subclasses/feature_Expert_Training_iCXtOWBKv1FdKdWz.json @@ -16,7 +16,51 @@ "artist": "" } }, - "effects": [], + "effects": [ + { + "name": "Expert Training", + "type": "base", + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, + "_id": "rknTONvaUDZ2Yz1W", + "img": "icons/creatures/mammals/dog-husky-white-blue.webp", + "changes": [ + { + "key": "system.companionData.levelupChoices", + "mode": 2, + "value": "1", + "priority": null + } + ], + "disabled": false, + "duration": { + "startTime": null, + "combat": null, + "seconds": null, + "rounds": null, + "turns": null, + "startRound": null, + "startTurn": null + }, + "description": "

Choose an additional level-up option for your companion.

", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!items.effects!iCXtOWBKv1FdKdWz.rknTONvaUDZ2Yz1W" + } + ], "sort": 0, "ownership": { "default": 0, diff --git a/styles/less/sheets/actors/companion/header.less b/styles/less/sheets/actors/companion/header.less index 3616a6b3..2a162a25 100644 --- a/styles/less/sheets/actors/companion/header.less +++ b/styles/less/sheets/actors/companion/header.less @@ -207,7 +207,7 @@ .input-section { display: flex; align-items: center; - justify-content: space-between; + gap: 8px; } } diff --git a/system.json b/system.json index fc7d0139..8624bab7 100644 --- a/system.json +++ b/system.json @@ -2,7 +2,7 @@ "id": "daggerheart", "title": "Daggerheart", "description": "An unofficial implementation of the Daggerheart system", - "version": "1.5.5", + "version": "1.6.0", "compatibility": { "minimum": "13.346", "verified": "13.351", diff --git a/templates/sheets/actors/companion/header.hbs b/templates/sheets/actors/companion/header.hbs index a543f71c..8b63e396 100644 --- a/templates/sheets/actors/companion/header.hbs +++ b/templates/sheets/actors/companion/header.hbs @@ -31,7 +31,7 @@

{{localize 'DAGGERHEART.GENERAL.level'}}
- {{#if document.system.levelData.canLevelUp}} + {{#if document.system.canLevelUp}}