tickwerk/src/data/documents/combatant.ts

119 lines
4.0 KiB
TypeScript

// SPDX-FileCopyrightText: 2022 Johannes Loher
//
// SPDX-License-Identifier: MIT
import type { CombatantDataConstructorData } from '@league-of-foundry-developers/foundry-vtt-types/src/foundry/common/data/data.mjs/combatantData';
import { packageId } from '../../constants';
export const registerCombatantFunctionality = () => {
CONFIG.Combatant.documentClass = CombatantMixin(CONFIG.Combatant.documentClass);
};
const CombatantMixin = (BaseCombatant: typeof Combatant) => {
return class TickwerkCombatant extends BaseCombatant {
/**
* An temporary property to make changes to the initiative available to other instances in their `_pre…` methods.
*/
_newInitiative: number | null | undefined;
/**
* An temporary property to make changes to the tieBreaker available to other instances in their `_pre…` methods.
*/
_newTieBreaker: number | undefined;
/***
* Is this combatant currently waiting?
*/
get waiting(): boolean {
return this.getFlag(packageId, 'waiting') ?? false;
}
toggleWaiting(): Promise<this | undefined> {
return this.update({ [`flags.${packageId}.waiting`]: !this.waiting, initiative: this.parent?.round });
}
protected override async _preCreate(...args: Parameters<Combatant['_preCreate']>): Promise<void> {
await super._preCreate(...args);
await this.#updateTieBreakerData(args[0]);
}
protected override async _preUpdate(...args: Parameters<Combatant['_preUpdate']>): Promise<void> {
await super._preUpdate(...args);
await this.#updateTieBreakerData(args[0]);
}
protected override _onCreate(): void {
this._newInitiative = undefined;
this._newTieBreaker = undefined;
}
protected override _onUpdate(): void {
this._newInitiative = undefined;
this._newTieBreaker = undefined;
}
async #updateTieBreakerData(data: DeepPartial<CombatantDataConstructorData>): Promise<void> {
if ('initiative' in data) {
const combatantsWithSameTickValue =
this.parent?.combatants.filter((combatant) => {
const otherInitiative =
combatant._newInitiative !== undefined ? combatant._newInitiative : combatant.initiative;
return otherInitiative === data.initiative;
}) ?? [];
const tieBreaker = await this.#getTieBreaker(combatantsWithSameTickValue);
setProperty(data, `flags.${packageId}.tieBreaker`, tieBreaker);
this._newInitiative = data.initiative;
this._newTieBreaker = tieBreaker;
}
}
async #getTieBreaker(combatants: TickwerkCombatant[]): Promise<number> {
const getTieBreaker = CONFIG.tickwerk?.getTieBreaker ?? defaultGetTieBreaker;
return getTieBreaker(this, combatants);
}
protected override _getInitiativeFormula(): string {
const getInitiativeFormula = CONFIG.tickwerk?.getInitiativeFormula;
if (getInitiativeFormula) return getInitiativeFormula(this);
return super._getInitiativeFormula();
}
};
};
const defaultGetTieBreaker = async (combatant: TickwerkCombatant, combatants: TickwerkCombatant[]): Promise<number> => {
if (combatants.length === 0) return 0;
const tieBreakers = combatants.map((combatant) => {
return (
(combatant._newTieBreaker !== undefined
? combatant._newTieBreaker
: combatant.getFlag(packageId, 'tieBreaker')) ?? 0
);
});
return Math.max(...tieBreakers) + 1;
};
export type TickwerkCombatantConstructor = ReturnType<typeof CombatantMixin>;
export type TickwerkCombatant = InstanceType<TickwerkCombatantConstructor>;
declare global {
interface FlagConfig {
Combatant: {
tickwerk: {
tieBreaker?: number | undefined;
waiting?: boolean | undefined;
};
};
}
interface DocumentClassConfig {
Combatant: TickwerkCombatantConstructor;
}
interface CONFIG {
tickwerk?: {
getTieBreaker?: (combatant: TickwerkCombatant, combatants: TickwerkCombatant[]) => Promise<number>;
getInitiativeFormula?: (combatant: TickwerkCombatant) => string;
};
}
}