From 9189a95ea3b150919e0a5e24e6cc291aa2d284c7 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Wed, 9 Jul 2025 02:18:26 +0200 Subject: [PATCH] Bugfix - Companion Levelup Features (#303) * Fixed so that features gained from companion levleup are granted properly to its partner * Fixed localization error I noticed --- lang/en.json | 3 +- module/applications/levelup/levelup.mjs | 2 +- .../applications/sheets/actors/character.mjs | 16 +-- module/data/levelData.mjs | 11 ++- module/data/levelTier.mjs | 9 +- module/dice/dhRoll.mjs | 1 + module/documents/actor.mjs | 97 +++++++++++-------- 7 files changed, 79 insertions(+), 60 deletions(-) diff --git a/lang/en.json b/lang/en.json index 797781d8..b5ed3b3f 100755 --- a/lang/en.json +++ b/lang/en.json @@ -107,7 +107,8 @@ "sendToChat": "Send To Chat", "toLoadout": "Send to Loadout", "toVault": "Send to Vault", - "unequip": "Unequip" + "unequip": "Unequip", + "useItem": "Use Item" }, "faith": "Faith", "levelUp": "You can level up", diff --git a/module/applications/levelup/levelup.mjs b/module/applications/levelup/levelup.mjs index eb120ac5..93910fe7 100644 --- a/module/applications/levelup/levelup.mjs +++ b/module/applications/levelup/levelup.mjs @@ -128,7 +128,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2) context.tabs.advancements.progress = { selected: selections, max: currentLevel.maxSelections }; context.showTabs = this.tabGroups.primary !== 'summary'; break; - const { current: currentActorLevel, changed: changedActorLevel } = this.actor.system.levelData.level; + const actorArmor = this.actor.system.armor; const levelKeys = Object.keys(this.levelup.levels); let achivementProficiency = 0; diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index c24bcfec..e70774f5 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -202,7 +202,7 @@ export default class CharacterSheet extends DHBaseActorSheet { return [ { - name: 'DAGGERHEART.Sheets.PC.ContextMenu.UseItem', + name: 'DAGGERHEART.ACTORS.Character.contextMenu.useItem', icon: '', condition: el => { const item = getItem(el); @@ -211,7 +211,7 @@ export default class CharacterSheet extends DHBaseActorSheet { callback: (button, event) => CharacterSheet.useItem.call(this, event, button) }, { - name: 'DAGGERHEART.Sheets.PC.ContextMenu.Equip', + name: 'DAGGERHEART.ACTORS.Character.contextMenu.equip', icon: '', condition: el => { const item = getItem(el); @@ -220,7 +220,7 @@ export default class CharacterSheet extends DHBaseActorSheet { callback: CharacterSheet.#toggleEquipItem.bind(this) }, { - name: 'DAGGERHEART.Sheets.PC.ContextMenu.Unequip', + name: 'DAGGERHEART.ACTORS.Character.contextMenu.unequip', icon: '', condition: el => { const item = getItem(el); @@ -229,7 +229,7 @@ export default class CharacterSheet extends DHBaseActorSheet { callback: CharacterSheet.#toggleEquipItem.bind(this) }, { - name: 'DAGGERHEART.Sheets.PC.ContextMenu.ToLoadout', + name: 'DAGGERHEART.ACTORS.Character.contextMenu.toLoadout', icon: '', condition: el => { const item = getItem(el); @@ -238,7 +238,7 @@ export default class CharacterSheet extends DHBaseActorSheet { callback: target => getItem(target).update({ 'system.inVault': false }) }, { - name: 'DAGGERHEART.Sheets.PC.ContextMenu.ToVault', + name: 'DAGGERHEART.ACTORS.Character.contextMenu.toVault', icon: '', condition: el => { const item = getItem(el); @@ -247,17 +247,17 @@ export default class CharacterSheet extends DHBaseActorSheet { callback: target => getItem(target).update({ 'system.inVault': true }) }, { - name: 'DAGGERHEART.Sheets.PC.ContextMenu.SendToChat', + name: 'DAGGERHEART.ACTORS.Character.contextMenu.sendToChat', icon: '', callback: CharacterSheet.toChat.bind(this) }, { - name: 'DAGGERHEART.Sheets.PC.ContextMenu.Edit', + name: 'CONTROLS.CommonEdit', icon: '', callback: target => getItem(target).sheet.render({ force: true }) }, { - name: 'DAGGERHEART.Sheets.PC.ContextMenu.Delete', + name: 'CONTROLS.CommonDelete', icon: '', callback: async el => { const item = getItem(el); diff --git a/module/data/levelData.mjs b/module/data/levelData.mjs index 2432a313..669077ee 100644 --- a/module/data/levelData.mjs +++ b/module/data/levelData.mjs @@ -43,7 +43,12 @@ export default class DhLevelData extends foundry.abstract.DataModel { data: new fields.ArrayField(new fields.StringField({ required: true })), secondaryData: new fields.TypedObjectField(new fields.StringField({ required: true })), itemUuid: new fields.DocumentUUIDField({ required: true }), - featureIds: new fields.ArrayField(new fields.StringField()) + features: new fields.ArrayField( + new fields.SchemaField({ + onPartner: new fields.BooleanField(), + id: new fields.StringField() + }) + ) }) ) }) @@ -51,10 +56,6 @@ export default class DhLevelData extends foundry.abstract.DataModel { }; } - get actions() { - return Object.values(this.levelups).flatMap(level => level.selections.flatMap(s => s.actions)); - } - get canLevelUp() { return this.level.current < this.level.changed; } diff --git a/module/data/levelTier.mjs b/module/data/levelTier.mjs index b037d921..e9e8d47b 100644 --- a/module/data/levelTier.mjs +++ b/module/data/levelTier.mjs @@ -70,7 +70,8 @@ export const CompanionLevelOptionType = { { name: 'DAGGERHEART.APPLICATIONS.Levelup.actions.creatureComfort.name', img: 'icons/magic/life/heart-cross-purple-orange.webp', - description: 'DAGGERHEART.APPLICATIONS.Levelup.actions.creatureComfort.description' + description: 'DAGGERHEART.APPLICATIONS.Levelup.actions.creatureComfort.description', + toPartner: true } ] }, @@ -81,7 +82,8 @@ export const CompanionLevelOptionType = { { name: 'DAGGERHEART.APPLICATIONS.Levelup.actions.armored.name', img: 'icons/equipment/shield/kite-wooden-oak-glow.webp', - description: 'DAGGERHEART.APPLICATIONS.Levelup.actions.armored.description' + description: 'DAGGERHEART.APPLICATIONS.Levelup.actions.armored.description', + toPartner: true } ] }, @@ -100,7 +102,8 @@ export const CompanionLevelOptionType = { { name: 'DAGGERHEART.APPLICATIONS.Levelup.actions.bonded.name', img: 'icons/magic/life/heart-red-blue.webp', - description: 'DAGGERHEART.APPLICATIONS.Levelup.actions.bonded.description' + description: 'DAGGERHEART.APPLICATIONS.Levelup.actions.bonded.description', + toPartner: true } ] }, diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index c3918a13..13246ac9 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -139,6 +139,7 @@ export default class DHRoll extends Roll { export const registerRollDiceHooks = () => { Hooks.on(`${CONFIG.DH.id}.postRollDuality`, async (config, message) => { if ( + !config.source?.actor || !game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).hope || config.roll.type === 'reaction' ) diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 1fab0f71..cfe101fa 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -5,13 +5,13 @@ import { LevelOptionType } from '../data/levelTier.mjs'; import DHFeature from '../data/item/feature.mjs'; export default class DhpActor extends Actor { - /** * Return the first Actor active owner. */ get owner() { - const user = this.hasPlayerOwner && game.users.players.find(u => this.testUserPermission(u, "OWNER") && u.active);; - if(!user) return game.user.isGM ? game.user : null; + const user = + this.hasPlayerOwner && game.users.players.find(u => this.testUserPermission(u, 'OWNER') && u.active); + if (!user) return game.user.isGM ? game.user : null; return user; } @@ -61,7 +61,7 @@ export default class DhpActor extends Actor { return acc; }, {}); - const featureIds = []; + const features = []; const domainCards = []; const experiences = []; const subclassFeatureState = { class: null, multiclass: null }; @@ -74,7 +74,7 @@ export default class DhpActor extends Actor { const advancementCards = level.selections.filter(x => x.type === 'domainCard').map(x => x.itemUuid); domainCards.push(...achievementCards, ...advancementCards); experiences.push(...Object.keys(level.achievements.experiences)); - featureIds.push(...level.selections.flatMap(x => x.featureIds)); + features.push(...level.selections.flatMap(x => x.features)); const subclass = level.selections.find(x => x.type === 'subclass'); if (subclass) { @@ -88,8 +88,11 @@ export default class DhpActor extends Actor { multiclass = level.selections.find(x => x.type === 'multiclass'); }); - for (let featureId of featureIds) { - this.items.get(featureId).delete(); + for (let feature of features) { + if (feature.onPartner && !this.system.partner) continue; + + const document = feature.onPartner ? this.system.partner : this; + document.items.get(feature.id)?.delete(); } if (experiences.length > 0) { @@ -153,7 +156,6 @@ export default class DhpActor extends Actor { } async levelUp(levelupData) { - const actions = []; const levelups = {}; for (var levelKey of Object.keys(levelupData)) { const level = levelupData[levelKey]; @@ -237,7 +239,9 @@ export default class DhpActor extends Actor { ...featureData, description: game.i18n.localize(featureData.description) }); - const embeddedItem = await this.createEmbeddedDocuments('Item', [ + + const document = featureData.toPartner && this.system.partner ? this.system.partner : this; + const embeddedItem = await document.createEmbeddedDocuments('Item', [ { ...featureData, name: game.i18n.localize(featureData.name), @@ -245,9 +249,13 @@ export default class DhpActor extends Actor { system: feature } ]); - addition.checkbox.featureIds = !addition.checkbox.featureIds - ? [embeddedItem[0].id] - : [...addition.checkbox.featureIds, embeddedItem[0].id]; + 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); @@ -317,7 +325,6 @@ export default class DhpActor extends Actor { await this.update({ system: { - actions: [...this.system.actions, ...actions], levelData: { level: { current: this.system.levelData.level.changed @@ -369,16 +376,16 @@ export default class DhpActor extends Actor { const modifier = roll.modifier !== null ? Number.parseInt(roll.modifier) : null; return modifier !== null ? [ - { - value: modifier, - label: roll.label - ? modifier >= 0 - ? `${roll.label} +${modifier}` - : `${roll.label} ${modifier}` - : null, - title: roll.label - } - ] + { + value: modifier, + label: roll.label + ? modifier >= 0 + ? `${roll.label} +${modifier}` + : `${roll.label} ${modifier}` + : null, + title: roll.label + } + ] : []; } @@ -460,7 +467,7 @@ export default class DhpActor extends Actor { if (Hooks.call(`${CONFIG.DH.id}.postDamageTreshold`, this, hpDamage, damage, type) === false) return null; - if(!hpDamage) return; + if (!hpDamage) return; const updates = [{ value: hpDamage, type: 'hitPoints' }]; @@ -469,8 +476,8 @@ export default class DhpActor extends Actor { this.system.armor && this.system.armor.system.marks.value < this.system.armorScore ) { - const armorStackResult = await this.owner.query('armorStack', {actorId: this.uuid, damage: hpDamage}); - if(armorStackResult) { + const armorStackResult = await this.owner.query('armorStack', { actorId: this.uuid, damage: hpDamage }); + if (armorStackResult) { const { modifiedDamage, armorSpent, stressSpent } = armorStackResult; updates.find(u => u.type === 'hitPoints').value = modifiedDamage; updates.push( @@ -479,7 +486,7 @@ export default class DhpActor extends Actor { ); } } - + await this.modifyResource(updates); if (Hooks.call(`${CONFIG.DH.id}.postTakeDamage`, this, damage, type) === false) return null; @@ -493,7 +500,7 @@ export default class DhpActor extends Actor { async modifyResource(resources) { if (!resources.length) return; - if(resources.find(r => r.type === 'stress')) this.convertStressDamageToHP(resources); + if (resources.find(r => r.type === 'stress')) this.convertStressDamageToHP(resources); let updates = { actor: { target: this, resources: {} }, armor: { target: this.system.armor, resources: {} } }; resources.forEach(r => { switch (r.type) { @@ -521,7 +528,12 @@ export default class DhpActor extends Actor { }); Object.values(updates).forEach(async u => { if (Object.keys(u.resources).length > 0) { - await emitAsGM(GMUpdateEvent.UpdateDocument, u.target.update.bind(u.target), u.resources, u.target.uuid); + await emitAsGM( + GMUpdateEvent.UpdateDocument, + u.target.update.bind(u.target), + u.resources, + u.target.uuid + ); /* if (game.user.isGM) { await u.target.update(u.resources); } else { @@ -540,27 +552,28 @@ export default class DhpActor extends Actor { convertDamageToThreshold(damage) { return damage >= this.system.damageThresholds.severe - ? 3 - : damage >= this.system.damageThresholds.major - ? 2 - : damage >= this.system.damageThresholds.minor - ? 1 - : 0; + ? 3 + : damage >= this.system.damageThresholds.major + ? 2 + : damage >= this.system.damageThresholds.minor + ? 1 + : 0; } convertStressDamageToHP(resources) { const stressDamage = resources.find(r => r.type === 'stress'), newValue = this.system.resources.stress.value + stressDamage.value; - if(newValue <= this.system.resources.stress.maxTotal) return; + if (newValue <= this.system.resources.stress.maxTotal) return; const hpDamage = resources.find(r => r.type === 'hitPoints'); - if(hpDamage) hpDamage.value++; - else resources.push({ - type: 'hitPoints', - value: 1 - }) + if (hpDamage) hpDamage.value++; + else + resources.push({ + type: 'hitPoints', + value: 1 + }); } } export const registerDHActorHooks = () => { CONFIG.queries.armorStack = DamageReductionDialog.armorStackQuery; -} \ No newline at end of file +};