Fixed a lot of cases where we expected a combatant to have an attached actor (#1520)

This commit is contained in:
WBHarry 2026-01-10 00:21:44 +01:00 committed by GitHub
parent e6973fabd0
commit 0b343c9f52
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 41 additions and 17 deletions

View file

@ -316,7 +316,7 @@ const updateActorsRangeDependentEffects = async token => {
CONFIG.DH.SETTINGS.gameSettings.variantRules CONFIG.DH.SETTINGS.gameSettings.variantRules
).rangeMeasurement; ).rangeMeasurement;
for (let effect of token.actor.allApplicableEffects()) { for (let effect of token.actor?.allApplicableEffects() ?? []) {
if (!effect.system.rangeDependence?.enabled) continue; if (!effect.system.rangeDependence?.enabled) continue;
const { target, range, type } = effect.system.rangeDependence; const { target, range, type } = effect.system.rangeDependence;

View file

@ -2781,7 +2781,9 @@
"gmRequired": "This action requires an online GM", "gmRequired": "This action requires an online GM",
"gmOnly": "This can only be accessed by the GM", "gmOnly": "This can only be accessed by the GM",
"noActorOwnership": "You do not have permissions for this character", "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": { "Sidebar": {
"actorDirectory": { "actorDirectory": {

View file

@ -21,6 +21,8 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD {
async _prepareContext(options) { async _prepareContext(options) {
const context = await super._prepareContext(options); const context = await super._prepareContext(options);
if (!this.actor) return context;
context.partyOnCanvas = context.partyOnCanvas =
this.actor.type === 'party' && this.actor.type === 'party' &&
this.actor.system.partyMembers.some(member => member.getActiveTokens().length > 0); 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() { 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 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); .map(t => t.document);
if (!this.object.controlled) tokens.push(this.document); if (!this.object.controlled && this.document.actor) tokens.push(this.document);
try { try {
if (this.document.inCombat) await TokenDocument.implementation.deleteCombatants(tokens); if (this.document.inCombat) {
else await TokenDocument.implementation.createCombatants(tokens); 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) { } catch (err) {
ui.notifications.warn(err.message); ui.notifications.warn(err.message);
} }

View file

@ -127,7 +127,7 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
resource, resource,
active: index === combat.turn, active: index === combat.turn,
canPing: combatant.sceneId === canvas.scene?.id && game.user.hasPermission('PING_CANVAS'), 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) img: await this._getCombatantThumbnail(combatant)
}; };
@ -165,7 +165,7 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
if (this.viewed.turn !== toggleTurn) { if (this.viewed.turn !== toggleTurn) {
const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns; const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns;
if (combatant.actor.type === 'character') { if (combatant.actor?.type === 'character') {
await updateCountdowns( await updateCountdowns(
CONFIG.DH.GENERAL.countdownProgressionTypes.spotlight.id, CONFIG.DH.GENERAL.countdownProgressionTypes.spotlight.id,
CONFIG.DH.GENERAL.countdownProgressionTypes.characterSpotlight.id CONFIG.DH.GENERAL.countdownProgressionTypes.characterSpotlight.id

View file

@ -9,7 +9,7 @@ export const AdversaryBPPerEncounter = (adversaries, characters) => {
); );
if (existingEntry) { if (existingEntry) {
existingEntry.nr += 1; existingEntry.nr += 1;
} else { } else if (adversary.type) {
acc.push({ adversary, nr: 1 }); acc.push({ adversary, nr: 1 });
} }
return acc; return acc;

View file

@ -15,8 +15,9 @@ export default class DhCombat extends foundry.abstract.TypeDataModel {
get extendedBattleToggles() { get extendedBattleToggles() {
const modifiers = CONFIG.DH.ENCOUNTER.BPModifiers; const modifiers = CONFIG.DH.ENCOUNTER.BPModifiers;
const adversaries = const adversaries =
this.parent.turns?.filter(x => x.isNPC)?.map(x => ({ ...x.actor, type: x.actor.system.type })) ?? []; 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.isNPC) ?? []; [];
const characters = this.parent.turns?.filter(x => x.actor && !x.isNPC) ?? [];
const activeAutomatic = Object.keys(modifiers).reduce((acc, categoryKey) => { const activeAutomatic = Object.keys(modifiers).reduce((acc, categoryKey) => {
const category = modifiers[categoryKey]; const category = modifiers[categoryKey];

View file

@ -83,7 +83,7 @@ export default class DHToken extends CONFIG.Token.documentClass {
if (combat?.system?.battleToggles?.length) { if (combat?.system?.battleToggles?.length) {
await combat.toggleModifierEffects( await combat.toggleModifierEffects(
true, true,
tokens.map(x => x.actor) tokens.filter(x => x.actor).map(x => x.actor)
); );
} }
super.createCombatants(tokens, combat ?? {}); super.createCombatants(tokens, combat ?? {});
@ -95,7 +95,7 @@ export default class DHToken extends CONFIG.Token.documentClass {
if (combat?.system?.battleToggles?.length) { if (combat?.system?.battleToggles?.length) {
await combat.toggleModifierEffects( await combat.toggleModifierEffects(
false, false,
tokens.map(x => x.actor) tokens.filter(x => x.actor).map(x => x.actor)
); );
} }
super.deleteCombatants(tokens, combat ?? {}); super.deleteCombatants(tokens, combat ?? {});

View file

@ -262,7 +262,7 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
const combat = game.combats.get(combatId); const combat = game.combats.get(combatId);
const adversaries = const adversaries =
combat.turns?.filter(x => x.actor?.isNPC)?.map(x => ({ ...x.actor, type: x.actor.system.type })) ?? []; 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 nrCharacters = characters.length;
const currentBP = AdversaryBPPerEncounter(adversaries, characters); 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) => { 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) => { const keyData = Object.keys(acc).reduce((identifiers, categoryKey) => {
if (identifiers) return identifiers; if (identifiers) return identifiers;
const category = acc[categoryKey]; const category = acc[categoryKey];
@ -352,7 +352,7 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
await combat.toggleModifierEffects( await combat.toggleModifierEffects(
event.target.checked, 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, category,
grouping grouping
); );

View file

@ -2,7 +2,7 @@
"id": "daggerheart", "id": "daggerheart",
"title": "Daggerheart", "title": "Daggerheart",
"description": "An unofficial implementation of the Daggerheart system", "description": "An unofficial implementation of the Daggerheart system",
"version": "1.4.5", "version": "1.4.6",
"compatibility": { "compatibility": {
"minimum": "13.346", "minimum": "13.346",
"verified": "13.351", "verified": "13.351",