mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-11 19:25:21 +01:00
Added BP calculation and tooltip breakdown of BP sources
This commit is contained in:
parent
630ba5ab7d
commit
51a0fb82eb
12 changed files with 251 additions and 12 deletions
16
lang/en.json
16
lang/en.json
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
79
module/config/encounterConfig.mjs
Normal file
79
module/config/encounterConfig.mjs
Normal 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'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,12 @@
|
|||
|
||||
.encounter-title {
|
||||
text-align: left;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.encounter-battlepoints {
|
||||
display: flex;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.inner-controls {
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
@import './tooltip/tooltip.less';
|
||||
@import './tooltip/battlepoints.less';
|
||||
@import './autocomplete/autocomplete.less';
|
||||
|
|
|
|||
16
styles/less/ux/tooltip/battlepoints.less
Normal file
16
styles/less/ux/tooltip/battlepoints.less
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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}}
|
||||
|
|
|
|||
15
templates/ui/tooltip/battlepoints.hbs
Normal file
15
templates/ui/tooltip/battlepoints.hbs
Normal 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>
|
||||
Loading…
Add table
Add a link
Reference in a new issue