daggerheart/module/applications/ui/combatTracker.mjs

221 lines
8.5 KiB
JavaScript

import { AdversaryBPPerEncounter } from '../../config/encounterConfig.mjs';
export default class DhCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {
static DEFAULT_OPTIONS = {
actions: {
requestSpotlight: this.requestSpotlight,
toggleSpotlight: this.toggleSpotlight,
setActionTokens: this.setActionTokens
}
};
static PARTS = {
header: {
template: 'systems/daggerheart/templates/ui/combatTracker/combatTrackerHeader.hbs'
},
tracker: {
template: 'systems/daggerheart/templates/ui/combatTracker/combatTracker.hbs'
},
footer: {
template: 'systems/daggerheart/templates/ui/combatTracker/combatTrackerFooter.hbs'
}
};
/** @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 modifierBP =
this.combats
.find(x => x.active)
?.system?.extendedBattleToggles?.reduce((acc, toggle) => (acc ?? 0) + toggle.category, null) ?? null;
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, hasModifierBP: modifierBP !== null }
});
}
async _prepareTrackerContext(context, options) {
await super._prepareTrackerContext(context, options);
const adversaries = context.turns?.filter(x => x.isNPC) ?? [];
const characters = context.turns?.filter(x => !x.isNPC) ?? [];
const spotlightQueueEnabled = game.settings.get(
CONFIG.DH.id,
CONFIG.DH.SETTINGS.gameSettings.SpotlightRequestQueue
);
const spotlightRequests = characters
?.filter(x => !x.isNPC && spotlightQueueEnabled)
.filter(x => x.system.spotlight.requestOrderIndex > 0)
.sort((a, b) => {
const valueA = a.system.spotlight.requestOrderIndex;
const valueB = b.system.spotlight.requestOrderIndex;
return valueA - valueB;
});
Object.assign(context, {
actionTokens: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).actionTokens,
adversaries,
characters: characters
?.filter(x => !x.isNPC)
.filter(x => !spotlightQueueEnabled || x.system.spotlight.requestOrderIndex == 0),
spotlightRequests
});
}
_getCombatContextOptions() {
return [
{
name: 'COMBAT.ClearMovementHistories',
icon: '<i class="fa-solid fa-shoe-prints"></i>',
condition: () => game.user.isGM && this.viewed?.combatants.size > 0,
callback: () => this.viewed.clearMovementHistories()
},
{
name: 'COMBAT.Delete',
icon: '<i class="fa-solid fa-trash"></i>',
condition: () => game.user.isGM && !!this.viewed,
callback: () => this.viewed.endCombat()
}
];
}
getDefeatedId(combatant) {
if (!combatant.actor) return CONFIG.specialStatusEffects.DEFEATED;
const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).defeated;
return settings[`${combatant.actor.type}Default`];
}
/** @inheritdoc */
async _onToggleDefeatedStatus(combatant) {
const isDefeated = !combatant.isDefeated;
await combatant.update({ defeated: isDefeated });
await combatant.actor?.toggleStatusEffect(this.getDefeatedId(combatant), { overlay: true, active: isDefeated });
}
/** @inheritdoc */
async _prepareTurnContext(combat, combatant, index) {
const { id, name, isOwner, isDefeated, hidden, initiative, permission } = combatant;
const resource = permission >= CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER ? combatant.resource : null;
const hasDecimals = Number.isFinite(initiative) && !Number.isInteger(initiative);
const turn = {
hasDecimals,
hidden,
id,
isDefeated,
initiative,
isOwner,
name,
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)
};
turn.css = [turn.active ? 'active' : null, hidden ? 'hide' : null, isDefeated ? 'defeated' : null].filterJoin(
' '
);
const defeatedId = this.getDefeatedId(combatant);
const effects = [];
for (const effect of combatant.actor?.temporaryEffects ?? []) {
if (effect.statuses.has(defeatedId)) turn.isDefeated = true;
else if (effect.img) effects.push({ img: effect.img, name: effect.name });
}
turn.effects = {
icons: effects,
tooltip: this._formatEffectsTooltip(effects)
};
return { ...turn, isNPC: combatant.isNPC, system: combatant.system.toObject() };
}
async setCombatantSpotlight(combatantId) {
const update = {
system: {
'spotlight.requesting': false,
'spotlight.requestOrderIndex': 0
}
};
const combatant = this.viewed.combatants.get(combatantId);
const toggleTurn = this.viewed.combatants.contents
.sort(this.viewed._sortCombatants)
.map(x => x.id)
.indexOf(combatantId);
if (this.viewed.turn !== toggleTurn) {
const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns;
if (combatant.actor.type === 'character') {
await updateCountdowns(
CONFIG.DH.GENERAL.countdownProgressionTypes.spotlight.id,
CONFIG.DH.GENERAL.countdownProgressionTypes.characterSpotlight.id
);
} else {
await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.spotlight.id);
}
const autoPoints = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).actionPoints;
if (autoPoints) {
update.system.actionTokens = Math.max(combatant.system.actionTokens - 1, 0);
}
}
await this.viewed.update({
turn: this.viewed.turn === toggleTurn ? null : toggleTurn,
round: this.viewed.round + 1
});
await combatant.update(update);
}
static async requestSpotlight(_, target) {
const characters = this.viewed.turns?.filter(x => !x.isNPC) ?? [];
const orderValues = characters.map(character => character.system.spotlight.requestOrderIndex);
const maxRequestIndex = Math.max(...orderValues);
const { combatantId } = target.closest('[data-combatant-id]')?.dataset ?? {};
const combatant = this.viewed.combatants.get(combatantId);
await combatant.update({
'system.spotlight': {
requesting: !combatant.system.spotlight.requesting,
requestOrderIndex: !combatant.system.spotlight.requesting ? maxRequestIndex + 1 : 0
}
});
this.render();
}
static async toggleSpotlight(_, target) {
const { combatantId } = target.closest('[data-combatant-id]')?.dataset ?? {};
await this.setCombatantSpotlight(combatantId);
}
static async setActionTokens(_, target) {
const { combatantId, tokenIndex } = target.closest('[data-combatant-id]')?.dataset ?? {};
const combatant = this.viewed.combatants.get(combatantId);
const changeIndex = Number(tokenIndex);
const newIndex = combatant.system.actionTokens > changeIndex ? changeIndex : changeIndex + 1;
await combatant.update({ 'system.actionTokens': newIndex });
this.render();
}
}