[Feature] Encounter Battlepoints (#1346)

* Added BP calculation and tooltip breakdown of BP sources

* Added Modifiers

* Fixed automatic battleToggles

* Corrected 'NoToughies' conditional

* Fixed GM-only visibility

* Fixed combatant isNPC
This commit is contained in:
WBHarry 2025-12-06 21:11:34 +01:00 committed by GitHub
parent c3cb9121af
commit 451bef4c92
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 712 additions and 141 deletions

View file

@ -2,6 +2,7 @@ export * as actionConfig from './actionConfig.mjs';
export * as actorConfig from './actorConfig.mjs';
export * as domainConfig from './domainConfig.mjs';
export * as effectConfig from './effectConfig.mjs';
export * as encounterConfig from './encounterConfig.mjs';
export * as flagsConfig from './flagsConfig.mjs';
export * as generalConfig from './generalConfig.mjs';
export * as hooksConfig from './hooksConfig.mjs';

View file

@ -108,52 +108,64 @@ export const adversaryTypes = {
bruiser: {
id: 'bruiser',
label: 'DAGGERHEART.CONFIG.AdversaryType.bruiser.label',
description: 'DAGGERHEART.ACTORS.Adversary.bruiser.description'
description: 'DAGGERHEART.ACTORS.Adversary.bruiser.description',
bpCost: 4
},
horde: {
id: 'horde',
label: 'DAGGERHEART.CONFIG.AdversaryType.horde.label',
description: 'DAGGERHEART.ACTORS.Adversary.horde.description'
description: 'DAGGERHEART.ACTORS.Adversary.horde.description',
bpCost: 2
},
leader: {
id: 'leader',
label: 'DAGGERHEART.CONFIG.AdversaryType.leader.label',
description: 'DAGGERHEART.ACTORS.Adversary.leader.description'
description: 'DAGGERHEART.ACTORS.Adversary.leader.description',
bpCost: 3,
bpDescription: 'DAGGERHEART.CONFIG.AdversaryType.leader.'
},
minion: {
id: 'minion',
label: 'DAGGERHEART.CONFIG.AdversaryType.minion.label',
description: 'DAGGERHEART.ACTORS.Adversary.minion.description'
description: 'DAGGERHEART.ACTORS.Adversary.minion.description',
bpCost: 1,
partyAmountPerBP: true
},
ranged: {
id: 'ranged',
label: 'DAGGERHEART.CONFIG.AdversaryType.ranged.label',
description: 'DAGGERHEART.ACTORS.Adversary.ranged.description'
description: 'DAGGERHEART.ACTORS.Adversary.ranged.description',
bpCost: 2
},
skulk: {
id: 'skulk',
label: 'DAGGERHEART.CONFIG.AdversaryType.skulk.label',
description: 'DAGGERHEART.ACTORS.Adversary.skulk.description'
description: 'DAGGERHEART.ACTORS.Adversary.skulk.description',
bpCost: 2
},
social: {
id: 'social',
label: 'DAGGERHEART.CONFIG.AdversaryType.social.label',
description: 'DAGGERHEART.ACTORS.Adversary.social.description'
description: 'DAGGERHEART.ACTORS.Adversary.social.description',
bpCost: 1
},
solo: {
id: 'solo',
label: 'DAGGERHEART.CONFIG.AdversaryType.solo.label',
description: 'DAGGERHEART.ACTORS.Adversary.solo.description'
description: 'DAGGERHEART.ACTORS.Adversary.solo.description',
bpCost: 5
},
standard: {
id: 'standard',
label: 'DAGGERHEART.CONFIG.AdversaryType.standard.label',
description: 'DAGGERHEART.ACTORS.Adversary.standard.description'
description: 'DAGGERHEART.ACTORS.Adversary.standard.description',
bpCost: 2
},
support: {
id: 'support',
label: 'DAGGERHEART.CONFIG.AdversaryType.support.label',
description: 'DAGGERHEART.ACTORS.Adversary.support.description'
description: 'DAGGERHEART.ACTORS.Adversary.support.description',
bpCost: 1
}
};

View file

@ -0,0 +1,145 @@
export const BaseBPPerEncounter = nrCharacters => 3 * nrCharacters + 2;
export const AdversaryBPPerEncounter = (adversaries, characters) => {
const adversaryTypes = CONFIG.DH.ACTOR.allAdversaryTypes();
return adversaries
.reduce((acc, adversary) => {
const existingEntry = acc.find(
x => x.adversary.name === adversary.name && x.adversary.type === adversary.type
);
if (existingEntry) {
existingEntry.nr += 1;
} else {
acc.push({ adversary, nr: 1 });
}
return acc;
}, [])
.reduce((acc, entry) => {
const adversary = entry.adversary;
const type = adversaryTypes[adversary.type];
const bpCost = type.bpCost ?? 0;
if (type.partyAmountPerBP) {
acc += characters.length === 0 ? 0 : Math.ceil(entry.nr / characters.length);
} else {
acc += bpCost;
}
return acc;
}, 0);
};
export const adversaryTypeCostBrackets = {
1: [
{
sort: 1,
types: ['minion'],
description: 'DAGGERHEART.CONFIG.AdversaryTypeCost.minion'
},
{
sort: 2,
types: ['social', 'support'],
description: 'DAGGERHEART.CONFIG.AdversaryTypeCost.support'
}
],
2: [
{
sort: 1,
types: ['horde', 'ranged', 'skulk', 'standard'],
description: 'DAGGERHEART.CONFIG.AdversaryTypeCost.standard'
}
],
3: [
{
sort: 1,
types: ['leader'],
description: 'DAGGERHEART.CONFIG.AdversaryTypeCost.leader'
}
],
4: [
{
sort: 1,
types: ['bruiser'],
description: 'DAGGERHEART.CONFIG.AdversaryTypeCost.bruiser'
}
],
5: [
{
sort: 1,
types: ['solo'],
description: 'DAGGERHEART.CONFIG.AdversaryTypeCost.solo'
}
]
};
export const BPModifiers = {
[-2]: {
manySolos: {
sort: 1,
description: 'DAGGERHEART.CONFIG.BPModifiers.manySolos.description',
automatic: true,
conditional: (_combat, adversaries) => {
return adversaries.filter(x => x.system.type === 'solo').length > 1;
}
},
increaseDamage: {
sort: 2,
description: 'DAGGERHEART.CONFIG.BPModifiers.increaseDamage.description',
effects: [
{
name: 'DAGGERHEART.CONFIG.BPModifiers.increaseDamage.effect.name',
description: 'DAGGERHEART.CONFIG.BPModifiers.increaseDamage.effect.description',
img: 'icons/magic/control/buff-flight-wings-red.webp',
changes: [
{
key: 'system.bonuses.damage.physical.dice',
mode: 2,
value: '1d4'
},
{
key: 'system.bonuses.damage.magical.dice',
mode: 2,
value: '1d4'
}
]
}
]
}
},
[-1]: {
lessDifficult: {
sort: 2,
description: 'DAGGERHEART.CONFIG.BPModifiers.lessDifficult.description'
}
},
1: {
lowerTier: {
sort: 1,
description: 'DAGGERHEART.CONFIG.BPModifiers.lowerTier.description',
automatic: true,
conditional: (_combat, adversaries, characters) => {
const characterMaxTier = characters.reduce((maxTier, character) => {
return character.system.tier > maxTier ? character.system.tier : maxTier;
}, 1);
return adversaries.some(adversary => adversary.system.tier < characterMaxTier);
}
},
noToughies: {
sort: 2,
description: 'DAGGERHEART.CONFIG.BPModifiers.noToughies.description',
automatic: true,
conditional: (_combat, adversaries) => {
const toughyTypes = ['bruiser', 'horde', 'leader', 'solo'];
return (
adversaries.length > 0 &&
!adversaries.some(adversary => toughyTypes.includes(adversary.system.type))
);
}
}
},
2: {
moreDangerous: {
sort: 2,
description: 'DAGGERHEART.CONFIG.BPModifiers.moreDangerous.description'
}
}
};

View file

@ -26,3 +26,5 @@ export const userFlags = {
welcomeMessage: 'welcome-message',
countdownMode: 'countdown-mode'
};
export const combatToggle = 'combat-toggle-origin';

View file

@ -1,5 +1,6 @@
import * as GENERAL from './generalConfig.mjs';
import * as DOMAIN from './domainConfig.mjs';
import * as ENCOUNTER from './encounterConfig.mjs';
import * as ACTOR from './actorConfig.mjs';
import * as ITEM from './itemConfig.mjs';
import * as SETTINGS from './settingsConfig.mjs';
@ -13,6 +14,7 @@ export const SYSTEM_ID = 'daggerheart';
export const SYSTEM = {
id: SYSTEM_ID,
ENCOUNTER,
GENERAL,
DOMAIN,
ACTOR,