Fixed automatic battleToggles

This commit is contained in:
WBHarry 2025-11-29 15:25:47 +01:00
parent e32454b7c7
commit d7bbc9e650
10 changed files with 201 additions and 21 deletions

View file

@ -914,12 +914,28 @@
"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"
"increaseDamage": {
"description": "if you add +1d4 (or a static +2) to all adversaries' damage rolls (to increase the challenge without lengthening the battle)",
"effect": {
"name": "Increase Damage",
"description": "Add 1d4 to damage"
}
},
"lessDifficult": {
"description": "if the fight should be less difficult or shorter."
},
"lowerTier": {
"description": "if you choose an adversary from a lower tier."
},
"manySolos": {
"description": "if you're using 2 or more Solo adversaries."
},
"moreDangerous": {
"description": "if the fight should be more dangerous or last longer"
},
"noToughies": {
"description": "if you don't include any Bruisers, Hordes, Leaders, or Solos"
}
},
"Burden": {
"oneHanded": "One-Handed",

View file

@ -42,7 +42,7 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
const modifierBP =
this.combats
.find(x => x.active)
?.system?.battleToggles?.reduce((acc, toggle) => acc + toggle.category, 0) ?? 0;
?.system?.extendedBattleToggles?.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);

View file

@ -75,33 +75,68 @@ export const BPModifiers = {
[-2]: {
manySolos: {
sort: 1,
description: 'DAGGERHEART.CONFIG.BPModifiers.manySolos'
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: '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: 'DAGGERHEART.CONFIG.BPModifiers.lessDifficult.description'
}
},
1: {
lowerTier: {
sort: 1,
description: 'DAGGERHEART.CONFIG.BPModifiers.lowerTier'
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: 'DAGGERHEART.CONFIG.BPModifiers.noToughies.description',
automatic: true,
conditional: (_combat, adversaries) => {
const toughyTypes = ['bruiser', 'horde', 'leader', 'solo'];
return !adversaries.some(adversary => toughyTypes.includes(adversary.system.type));
}
}
},
2: {
moreDangerous: {
sort: 2,
description: 'DAGGERHEART.CONFIG.BPModifiers.moreDangerous'
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

@ -10,4 +10,30 @@ export default class DhCombat extends foundry.abstract.TypeDataModel {
)
};
}
/** Includes automatic BPModifiers */
get extendedBattleToggles() {
const modifiers = CONFIG.DH.ENCOUNTER.BPModifiers;
const adversaries =
this.parent.turns?.filter(x => x.isNPC)?.map(x => ({ ...x.actor, type: x.actor.system.type })) ?? [];
const characters = this.parent.turns?.filter(x => !x.isNPC) ?? [];
const activeAutomatic = Object.keys(modifiers).reduce((acc, categoryKey) => {
const category = modifiers[categoryKey];
acc.push(
...Object.keys(category).reduce((acc, groupingKey) => {
const grouping = category[groupingKey];
if (grouping.automatic && grouping.conditional?.(this.parent, adversaries, characters)) {
acc.push({ category: Number(categoryKey), grouping: groupingKey });
}
return acc;
}, [])
);
return acc;
}, []);
return [...this.battleToggles, ...activeAutomatic];
}
}

View file

@ -16,4 +16,57 @@ export default class DhpCombat extends Combat {
return a.name.localeCompare(b.name);
}
async toggleModifierEffects(add, actors, category, groupingKey) {
const effectData = category && groupingKey ? [{ category, grouping: groupingKey }] : this.system.battleToggles;
if (add) {
const effects = effectData.reduce((acc, toggle) => {
const grouping = CONFIG.DH.ENCOUNTER.BPModifiers[toggle.category]?.[toggle.grouping];
if (!grouping?.effects?.length) return acc;
acc.push(
...grouping.effects.map(effect => ({
...effect,
name: game.i18n.localize(effect.name),
description: game.i18n.localize(effect.description),
flags: {
[`${CONFIG.DH.id}.${CONFIG.DH.FLAGS.combatToggle}`]: {
category: toggle.category,
grouping: toggle.grouping
}
}
}))
);
return acc;
}, []);
if (!effects.length) return;
for (let actor of actors) {
await actor.createEmbeddedDocuments(
'ActiveEffect',
effects.map(effect => ({
...effect,
name: game.i18n.localize(effect.name),
description: game.i18n.localize(effect.description)
}))
);
}
} else {
for (let actor of actors) {
await actor.deleteEmbeddedDocuments(
'ActiveEffect',
actor.effects
.filter(x => {
const flag = x.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.combatToggle);
if (!flag) return false;
return effectData.some(
data => flag.category == data.category && flag.grouping === data.grouping
);
})
.map(x => x.id)
);
}
}
}
}

View file

@ -72,8 +72,32 @@ export default class DHToken extends TokenDocument {
}
return attributes;
}
_shouldRecordMovementHistory() {
return false;
}
/**@inheritdoc */
static async createCombatants(tokens, combat) {
combat ??= game.combats.viewed;
if (combat?.system?.battleToggles?.length) {
await combat.toggleModifierEffects(
true,
tokens.map(x => x.actor)
);
}
super.createCombatants(tokens, combat ?? {});
}
/**@inheritdoc */
static async deleteCombatants(tokens, { combat } = {}) {
combat ??= game.combats.viewed;
if (combat?.system?.battleToggles?.length) {
await combat.toggleModifierEffects(
false,
tokens.map(x => x.actor)
);
}
super.deleteCombatants(tokens, combat ?? {});
}
}

View file

@ -221,7 +221,7 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
const nrCharacters = characters.length;
const currentBP = AdversaryBPPerEncounter(adversaries, characters);
const maxBP = combat.system.battleToggles.reduce(
const maxBP = combat.system.extendedBattleToggles.reduce(
(acc, toggle) => acc + toggle.category,
BaseBPPerEncounter(nrCharacters)
);
@ -252,18 +252,21 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
return acc;
}, foundry.utils.deepClone(CONFIG.DH.ENCOUNTER.adversaryTypeCostBrackets));
const extendedBattleToggles = combat.system.extendedBattleToggles;
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) => {
const grouping = category[toggleKey];
acc.push({
...category[toggleKey],
...grouping,
categoryKey: Number(categoryKey),
toggleKey,
checked: combat.system.battleToggles.find(
checked: extendedBattleToggles.find(
x => x.category == categoryKey && x.grouping === toggleKey
)
),
disabled: grouping.automatic
});
return acc;
@ -302,6 +305,13 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
}
});
await combat.toggleModifierEffects(
event.target.checked,
combat.combatants.filter(x => x.actor.type === 'adversary').map(x => x.actor),
category,
grouping
);
this.tooltip.innerHTML = await this.getBattlepointHTML(combatId);
const lockedTooltip = this.lockTooltip();
lockedTooltip.querySelectorAll('.battlepoint-toggle-container input').forEach(element => {

View file

@ -24,6 +24,16 @@
display: flex;
gap: 4px;
&.inactive {
opacity: 0.4;
}
.grouping-lock {
font-size: 17.5px;
margin: 0 5px;
color: @golden;
}
input {
margin-top: 0;
}

View file

@ -7,7 +7,7 @@
{{#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>
<label class="unselected-grouping">{{key}} BP - {{localize grouping.description}}</label>
{{/if}}
</div>
{{/each}}
@ -16,8 +16,12 @@
<div class="battlepoint-toggles-container">
<h3>{{localize "Modifiers"}}</h3>
{{#each toggles as |toggle|}}
<div class="battlepoint-toggle-container">
<input type="checkbox" data-combat-id="{{@root.combatId}}" data-category="{{toggle.categoryKey}}" data-grouping="{{toggle.toggleKey}}" {{checked toggle.checked}} />
<div class="battlepoint-toggle-container {{#if (and toggle.disabled (not toggle.checked))}}inactive{{/if}}">
{{#if toggle.disabled}}
<i class="grouping-lock fa-solid {{#if toggle.checked}}fa-lock{{else}}fa-unlock{{/if}}"></i>
{{else}}
<input type="checkbox" data-combat-id="{{@root.combatId}}" data-category="{{toggle.categoryKey}}" data-grouping="{{toggle.toggleKey}}" {{checked toggle.checked}} />
{{/if}}
<label class="unselected-grouping">{{toggle.categoryKey}} BP: {{localize toggle.description}}</label>
</div>
{{/each}}