Added Modifiers

This commit is contained in:
WBHarry 2025-11-29 13:22:10 +01:00
parent 51a0fb82eb
commit e32454b7c7
9 changed files with 344 additions and 205 deletions

View file

@ -1,3 +1,5 @@
import { AdversaryBPPerEncounter } from '../../config/encounterConfig.mjs';
export default class DhCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {
static DEFAULT_OPTIONS = {
actions: {
@ -37,36 +39,16 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
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);
const modifierBP =
this.combats
.find(x => x.active)
?.system?.battleToggles?.reduce((acc, toggle) => acc + toggle.category, 0) ?? 0;
const maxBP = CONFIG.DH.ENCOUNTER.BaseBPPerEncounter(context.characters.length) + modifierBP;
const currentBP = AdversaryBPPerEncounter(context.adversaries, context.characters);
Object.assign(context, {
fear: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear),
battlepoints: { max: maxBP, current: currentBP }
battlepoints: { max: maxBP, current: currentBP, hasModifierBP: Boolean(modifierBP) }
});
}

View file

@ -121,7 +121,8 @@ export const adversaryTypes = {
id: 'leader',
label: 'DAGGERHEART.CONFIG.AdversaryType.leader.label',
description: 'DAGGERHEART.ACTORS.Adversary.leader.description',
bpCost: 3
bpCost: 3,
bpDescription: 'DAGGERHEART.CONFIG.AdversaryType.leader.'
},
minion: {
id: 'minion',

View file

@ -1,5 +1,33 @@
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: [
{

View file

@ -1,6 +1,13 @@
export default class DhCombat extends foundry.abstract.TypeDataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {};
return {
battleToggles: new fields.ArrayField(
new fields.SchemaField({
category: new fields.NumberField({ required: true, integer: true }),
grouping: new fields.StringField({ required: true })
})
)
};
}
}

View file

@ -1,8 +1,28 @@
import { AdversaryBPPerEncounter, BaseBPPerEncounter } from '../config/encounterConfig.mjs';
export default class DhTooltipManager extends foundry.helpers.interaction.TooltipManager {
#wide = false;
async activate(element, options = {}) {
const { TextEditor } = foundry.applications.ux;
let html = options.html;
if (element.dataset.tooltip?.startsWith('#battlepoints#')) {
this.#wide = true;
html = await this.getBattlepointHTML(element.dataset.combatId);
options.direction = this._determineItemTooltipDirection(element);
super.activate(element, { ...options, html: html });
const lockedTooltip = this.lockTooltip();
lockedTooltip.querySelectorAll('.battlepoint-toggle-container input').forEach(element => {
element.addEventListener('input', this.toggleModifier.bind(this));
});
return;
} else {
this.#wide = false;
}
if (element.dataset.tooltip?.startsWith('#item#')) {
const itemUuid = element.dataset.tooltip.slice(6);
const item = await foundry.utils.fromUuid(itemUuid);
@ -24,53 +44,6 @@ 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) {
@ -222,4 +195,117 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
}
}
}
/**@inheritdoc */
_setStyle(position = {}) {
super._setStyle(position);
if (this.#wide) {
this.tooltip.classList.add('wide');
}
}
/**@inheritdoc */
lockTooltip() {
const clone = super.lockTooltip();
clone.classList.add('wide');
return clone;
}
/** Get HTML for Battlepoints tooltip */
async getBattlepointHTML(combatId) {
const combat = game.combats.get(combatId);
const adversaries =
combat.turns?.filter(x => x.isNPC)?.map(x => ({ ...x.actor, type: x.actor.system.type })) ?? [];
const characters = combat.turns?.filter(x => !x.isNPC) ?? [];
const nrCharacters = characters.length;
const currentBP = AdversaryBPPerEncounter(adversaries, characters);
const maxBP = combat.system.battleToggles.reduce(
(acc, toggle) => acc + toggle.category,
BaseBPPerEncounter(nrCharacters)
);
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));
const toggles = Object.keys(CONFIG.DH.ENCOUNTER.BPModifiers)
.reduce((acc, categoryKey) => {
const category = CONFIG.DH.ENCOUNTER.BPModifiers[categoryKey];
acc.push(
...Object.keys(category).reduce((acc, toggleKey) => {
acc.push({
...category[toggleKey],
categoryKey: Number(categoryKey),
toggleKey,
checked: combat.system.battleToggles.find(
x => x.category == categoryKey && x.grouping === toggleKey
)
});
return acc;
}, [])
);
return acc;
}, [])
.sort((a, b) => {
if (a.categoryKey < b.categoryKey) return -1;
if (a.categoryKey > b.categoryKey) return 1;
else return a.toggleKey.localeCompare(b.toggleKey);
});
return await foundry.applications.handlebars.renderTemplate(
`systems/daggerheart/templates/ui/tooltip/battlepoints.hbs`,
{
combatId: combat.id,
nrCharacters,
currentBP,
maxBP,
categories,
toggles
}
);
}
/** Enable/disable a BP modifier */
async toggleModifier(event) {
const { combatId, category, grouping } = event.target.dataset;
const combat = game.combats.get(combatId);
await combat.update({
system: {
battleToggles: combat.system.battleToggles.some(x => x.category == category && x.grouping === grouping)
? combat.system.battleToggles.filter(x => x.category != category && x.grouping !== grouping)
: [...combat.system.battleToggles, { category: Number(category), grouping }]
}
});
this.tooltip.innerHTML = await this.getBattlepointHTML(combatId);
const lockedTooltip = this.lockTooltip();
lockedTooltip.querySelectorAll('.battlepoint-toggle-container input').forEach(element => {
element.addEventListener('input', this.toggleModifier.bind(this));
});
}
}