Added BP calculation and tooltip breakdown of BP sources

This commit is contained in:
WBHarry 2025-11-28 18:53:34 +01:00
parent 630ba5ab7d
commit 51a0fb82eb
12 changed files with 251 additions and 12 deletions

View file

@ -682,6 +682,14 @@
"description": "Enemies that enhance their allies and/or disrupt their opponents."
}
},
"AdversaryTypeCost": {
"minion": "for each group of Minions equal to the size of the party.",
"support": "for each Social or Support adversary.",
"standard": "for each Horde, Ranged, Skulk, or Standard adversary.",
"leader": "for each Leader adversary.",
"bruiser": "for each Bruiser adversary.",
"solo": "for each Solo adversary."
},
"ArmorFeature": {
"burning": {
"name": "Burning",
@ -905,6 +913,14 @@
"evolved": "Evolved",
"hybrid": "Hybrid"
},
"BPModifiers": {
"increaseDamage": "if you add +1d4 (or a static +2) to all adversaries' damage rolls (to increase the challenge without lengthening the battle)",
"lessDifficult": "if the fight should be less difficult or shorter.",
"lowerTier": "if you choose an adversary from a lower tier.",
"manySolos": "if you're using 2 or more Solo adversaries.",
"moreDangerous": "if the fight should be more dangerous or last longer",
"noToughies": "if you don't include any Bruisers, Hordes, Leaders, or Solos"
},
"Burden": {
"oneHanded": "One-Handed",
"twoHanded": "Two-Handed"

View file

@ -20,11 +20,53 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
}
};
/** @inheritDoc */
async _preparePartContext(_partId, context, _options) {
return context;
}
async _prepareContext(options) {
const context = await super._prepareContext(options);
await this._prepareTrackerContext(context, options);
await this._prepareCombatContext(context, options);
return context;
}
async _prepareCombatContext(context, options) {
await super._prepareCombatContext(context, options);
const adversaryTypes = CONFIG.DH.ACTOR.allAdversaryTypes();
const maxBP = CONFIG.DH.ENCOUNTER.BaseBPPerEncounter(context.characters.length);
const currentBP = context.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 += context.characters.length === 0 ? 0 : Math.ceil(entry.nr / context.characters.length);
} else {
acc += bpCost;
}
return acc;
}, 0);
Object.assign(context, {
fear: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear)
fear: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear),
battlepoints: { max: maxBP, current: currentBP }
});
}
@ -99,6 +141,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,
img: await this._getCombatantThumbnail(combatant)
};

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 itemConfig from './itemConfig.mjs';

View file

@ -108,52 +108,63 @@ 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
},
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,79 @@
export const BaseBPPerEncounter = nrCharacters => 3 * nrCharacters + 2;
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'
},
increaseDamage: {
sort: 2,
description: 'DAGGERHEART.CONFIG.BPModifiers.increaseDamage'
}
},
[-1]: {
lessDifficult: {
sort: 2,
description: 'DAGGERHEART.CONFIG.BPModifiers.lessDifficult'
}
},
1: {
lowerTier: {
sort: 1,
description: 'DAGGERHEART.CONFIG.BPModifiers.lowerTier'
},
noToughies: {
sort: 2,
description: 'DAGGERHEART.CONFIG.BPModifiers.noToughies'
}
},
2: {
moreDangerous: {
sort: 2,
description: 'DAGGERHEART.CONFIG.BPModifiers.moreDangerous'
}
}
};

View file

@ -1,17 +1,19 @@
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';
import * as EFFECTS from './effectConfig.mjs';
import * as ACTIONS from './actionConfig.mjs';
import * as FLAGS from './flagsConfig.mjs';
import * as ITEMBROWSER from './itemBrowserConfig.mjs'
import * as ITEMBROWSER from './itemBrowserConfig.mjs';
export const SYSTEM_ID = 'daggerheart';
export const SYSTEM = {
id: SYSTEM_ID,
ENCOUNTER,
GENERAL,
DOMAIN,
ACTOR,

View file

@ -24,6 +24,53 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
this.tooltip.innerHTML = html;
options.direction = this._determineItemTooltipDirection(element);
}
} else if (element.dataset.tooltip?.startsWith('#battlepoints#')) {
const combat = game.combats.get(element.dataset.combatId);
const nrCharacters = Number(element.dataset.nrCharacters);
const currentBP = element.dataset.bpCurrent;
const maxBP = element.dataset.bpMax;
const categories = combat.combatants.reduce((acc, combatant) => {
if (combatant.actor.type === 'adversary') {
const keyData = Object.keys(acc).reduce((identifiers, categoryKey) => {
if (identifiers) return identifiers;
const category = acc[categoryKey];
const groupingIndex = category.findIndex(grouping =>
grouping.types.includes(combatant.actor.system.type)
);
if (groupingIndex !== -1) identifiers = { categoryKey, groupingIndex };
return identifiers;
}, null);
if (keyData) {
const { categoryKey, groupingIndex } = keyData;
const grouping = acc[categoryKey][groupingIndex];
const partyAmount =
CONFIG.DH.ACTOR.adversaryTypes[combatant.actor.system.type].partyAmountPerBP;
grouping.individuals = (grouping.individuals ?? 0) + 1;
const currentNr = grouping.nr ?? 0;
grouping.nr = partyAmount
? Math.ceil(grouping.individuals / (nrCharacters ?? 0))
: currentNr + 1;
}
}
return acc;
}, foundry.utils.deepClone(CONFIG.DH.ENCOUNTER.adversaryTypeCostBrackets));
html = await foundry.applications.handlebars.renderTemplate(
`systems/daggerheart/templates/ui/tooltip/battlepoints.hbs`,
{
categories,
nrCharacters,
currentBP,
maxBP
}
);
this.tooltip.innerHTML = html;
options.direction = this._determineItemTooltipDirection(element);
} else {
const attack = element.dataset.tooltip?.startsWith('#attack#');
if (attack) {

View file

@ -4,6 +4,12 @@
.encounter-title {
text-align: left;
flex: none;
}
.encounter-battlepoints {
display: flex;
cursor: help;
}
.inner-controls {

View file

@ -1,2 +1,3 @@
@import './tooltip/tooltip.less';
@import './tooltip/battlepoints.less';
@import './autocomplete/autocomplete.less';

View file

@ -0,0 +1,16 @@
.daggerheart.dh-style.tooltip {
.battlepoint-categories-container {
display: flex;
flex-direction: column;
gap: 8px;
.battlepoint-grouping-container {
display: flex;
gap: 4px;
.unselected-grouping {
opacity: 0.4;
}
}
}
}

View file

@ -64,6 +64,8 @@
{{/if}}
</strong>
<div class="encounter-battlepoints" data-tooltip="#battlepoints#" data-combat-id="{{combat.id}}" data-bp-current="{{battlepoints.current}}" data-bp-max="{{battlepoints.max}}" data-nr-characters="{{characters.length}}">{{battlepoints.current}}/{{battlepoints.max}} BP</div>
{{!-- Combat Controls --}}
<div class="inner-controls">
{{#if hasCombat}}

View file

@ -0,0 +1,15 @@
<div class="daggerheart dh-style tooltip">
<div class="battlepoint-categories-container">
{{#each categories as |category key|}}
{{#each category as |grouping index|}}
<div class="battlepoint-grouping-container">
{{#if grouping.nr}}
<label>{{key}} BP - {{concat (localize grouping.description) ' ' '('grouping.nr 'x)'}}</label>
{{else}}
<label class="unselected-grouping">{{key}}BP - {{localize grouping.description}}</label>
{{/if}}
</div>
{{/each}}
{{/each}}
</div>
</div>