diff --git a/daggerheart.mjs b/daggerheart.mjs index d8ebb713..48f4a615 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -316,7 +316,7 @@ const updateActorsRangeDependentEffects = async token => { CONFIG.DH.SETTINGS.gameSettings.variantRules ).rangeMeasurement; - for (let effect of token.actor.allApplicableEffects()) { + for (let effect of token.actor?.allApplicableEffects() ?? []) { if (!effect.system.rangeDependence?.enabled) continue; const { target, range, type } = effect.system.rangeDependence; diff --git a/lang/en.json b/lang/en.json index 4f1007c4..a78ed588 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2781,7 +2781,9 @@ "gmRequired": "This action requires an online GM", "gmOnly": "This can only be accessed by the GM", "noActorOwnership": "You do not have permissions for this character", - "documentIsMissing": "The {documentType} is missing from the world." + "documentIsMissing": "The {documentType} is missing from the world.", + "tokenActorMissing": "{name} is missing an Actor", + "tokenActorsMissing": "[{names}] missing Actors" }, "Sidebar": { "actorDirectory": { diff --git a/module/applications/hud/tokenHUD.mjs b/module/applications/hud/tokenHUD.mjs index b1136995..87c3e88e 100644 --- a/module/applications/hud/tokenHUD.mjs +++ b/module/applications/hud/tokenHUD.mjs @@ -21,6 +21,8 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD { async _prepareContext(options) { const context = await super._prepareContext(options); + if (!this.actor) return context; + context.partyOnCanvas = this.actor.type === 'party' && this.actor.system.partyMembers.some(member => member.getActiveTokens().length > 0); @@ -58,14 +60,33 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD { } static async #onToggleCombat() { + const tokensWithoutActors = canvas.tokens.controlled.filter(t => !t.actor); + const warning = + tokensWithoutActors.length === 1 + ? game.i18n.format('DAGGERHEART.UI.Notifications.tokenActorMissing', { + name: tokensWithoutActors[0].name + }) + : game.i18n.format('DAGGERHEART.UI.Notifications.tokenActorsMissing', { + names: tokensWithoutActors.map(x => x.name).join(', ') + }); + const tokens = canvas.tokens.controlled - .filter(t => !t.actor || !DHTokenHUD.#nonCombatTypes.includes(t.actor.type)) + .filter(t => t.actor && !DHTokenHUD.#nonCombatTypes.includes(t.actor.type)) .map(t => t.document); - if (!this.object.controlled) tokens.push(this.document); + if (!this.object.controlled && this.document.actor) tokens.push(this.document); try { - if (this.document.inCombat) await TokenDocument.implementation.deleteCombatants(tokens); - else await TokenDocument.implementation.createCombatants(tokens); + if (this.document.inCombat) { + const tokensInCombat = tokens.filter(t => t.inCombat); + await TokenDocument.implementation.deleteCombatants([...tokensInCombat, ...tokensWithoutActors]); + } else { + if (tokensWithoutActors.length) { + ui.notifications.warn(warning); + } + + const tokensOutOfCombat = tokens.filter(t => !t.inCombat); + await TokenDocument.implementation.createCombatants(tokensOutOfCombat); + } } catch (err) { ui.notifications.warn(err.message); } diff --git a/module/applications/ui/combatTracker.mjs b/module/applications/ui/combatTracker.mjs index babc4a65..288ba8ad 100644 --- a/module/applications/ui/combatTracker.mjs +++ b/module/applications/ui/combatTracker.mjs @@ -127,7 +127,7 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C resource, active: index === combat.turn, canPing: combatant.sceneId === canvas.scene?.id && game.user.hasPermission('PING_CANVAS'), - type: combatant.actor.system.type, + type: combatant.actor?.system?.type, img: await this._getCombatantThumbnail(combatant) }; @@ -165,7 +165,7 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C if (this.viewed.turn !== toggleTurn) { const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns; - if (combatant.actor.type === 'character') { + if (combatant.actor?.type === 'character') { await updateCountdowns( CONFIG.DH.GENERAL.countdownProgressionTypes.spotlight.id, CONFIG.DH.GENERAL.countdownProgressionTypes.characterSpotlight.id diff --git a/module/config/encounterConfig.mjs b/module/config/encounterConfig.mjs index 7565652f..4e0f8a6e 100644 --- a/module/config/encounterConfig.mjs +++ b/module/config/encounterConfig.mjs @@ -9,7 +9,7 @@ export const AdversaryBPPerEncounter = (adversaries, characters) => { ); if (existingEntry) { existingEntry.nr += 1; - } else { + } else if (adversary.type) { acc.push({ adversary, nr: 1 }); } return acc; diff --git a/module/data/combat.mjs b/module/data/combat.mjs index 565afffc..3671a2cd 100644 --- a/module/data/combat.mjs +++ b/module/data/combat.mjs @@ -15,8 +15,9 @@ export default class DhCombat extends foundry.abstract.TypeDataModel { get extendedBattleToggles() { const modifiers = CONFIG.DH.ENCOUNTER.BPModifiers; const adversaries = - this.parent.turns?.filter(x => x.isNPC)?.map(x => ({ ...x.actor, type: x.actor.system.type })) ?? []; - const characters = this.parent.turns?.filter(x => !x.isNPC) ?? []; + this.parent.turns?.filter(x => x.actor && x.isNPC)?.map(x => ({ ...x.actor, type: x.actor.system.type })) ?? + []; + const characters = this.parent.turns?.filter(x => x.actor && !x.isNPC) ?? []; const activeAutomatic = Object.keys(modifiers).reduce((acc, categoryKey) => { const category = modifiers[categoryKey]; diff --git a/module/documents/token.mjs b/module/documents/token.mjs index c3babaa1..4ac29264 100644 --- a/module/documents/token.mjs +++ b/module/documents/token.mjs @@ -83,7 +83,7 @@ export default class DHToken extends CONFIG.Token.documentClass { if (combat?.system?.battleToggles?.length) { await combat.toggleModifierEffects( true, - tokens.map(x => x.actor) + tokens.filter(x => x.actor).map(x => x.actor) ); } super.createCombatants(tokens, combat ?? {}); @@ -95,7 +95,7 @@ export default class DHToken extends CONFIG.Token.documentClass { if (combat?.system?.battleToggles?.length) { await combat.toggleModifierEffects( false, - tokens.map(x => x.actor) + tokens.filter(x => x.actor).map(x => x.actor) ); } super.deleteCombatants(tokens, combat ?? {}); diff --git a/module/documents/tooltipManager.mjs b/module/documents/tooltipManager.mjs index b0a107b9..3ea6703c 100644 --- a/module/documents/tooltipManager.mjs +++ b/module/documents/tooltipManager.mjs @@ -262,7 +262,7 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti const combat = game.combats.get(combatId); const adversaries = combat.turns?.filter(x => x.actor?.isNPC)?.map(x => ({ ...x.actor, type: x.actor.system.type })) ?? []; - const characters = combat.turns?.filter(x => !x.isNPC) ?? []; + const characters = combat.turns?.filter(x => !x.isNPC && x.actor) ?? []; const nrCharacters = characters.length; const currentBP = AdversaryBPPerEncounter(adversaries, characters); @@ -272,7 +272,7 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti ); const categories = combat.combatants.reduce((acc, combatant) => { - if (combatant.actor.type === 'adversary') { + if (combatant.actor?.type === 'adversary') { const keyData = Object.keys(acc).reduce((identifiers, categoryKey) => { if (identifiers) return identifiers; const category = acc[categoryKey]; @@ -352,7 +352,7 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti await combat.toggleModifierEffects( event.target.checked, - combat.combatants.filter(x => x.actor.type === 'adversary').map(x => x.actor), + combat.combatants.filter(x => x.actor?.type === 'adversary').map(x => x.actor), category, grouping ); diff --git a/system.json b/system.json index c5fd61f2..5570bdbf 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.4.5", + "version": "1.4.6", "compatibility": { "minimum": "13.346", "verified": "13.351",