refactor: cleanup

This commit is contained in:
Johannes Loher 2022-05-16 03:46:53 +02:00
parent ddcbcffe4c
commit 2d9e418ba7
9 changed files with 118 additions and 79 deletions

View file

@ -4,5 +4,8 @@
"TICKWERK.Tick": "Tick",
"TICKWERK.Wait": "Abwarten",
"TICKWERK.Waiting": "Abwarten",
"TICKWERK.WarningCannotAdvanceWhileWaiting": "Während des Abwartens ist es nicht möglich, auf der Tickleiste vorzurücken.",
"TICKWERK.WarningCannotAdvanceWithoutStartedCombat": "Solange der Kampf nicht gestartet ist es nicht möglich, auf der Tickleiste vorzurücken.",
"TICKWERK.WarningCannotAdvanceWithoutTickValue": "Ohne Tickwert ist es nicht möglich, auf der Tickleiste vorzurücken.",
"TICKWERK.WarningCannotStartCombat": "Der Kampf kann nur begonnen werden, wenn mindestens ein Kampfteilnehmer einen Tickwert hat."
}

View file

@ -4,5 +4,8 @@
"TICKWERK.Tick": "Tick",
"TICKWERK.Wait": "Wait",
"TICKWERK.Waiting": "Waiting",
"TICKWERK.WarningCannotAdvanceWhileWaiting": "Cannot advance while waiting.",
"TICKWERK.WarningCannotAdvanceWithoutStartedCombat": "Cannot advance without the combat being started.",
"TICKWERK.WarningCannotAdvanceWithoutTickValue": "Cannot advance without having a tick value.",
"TICKWERK.WarningCannotStartCombat": "In order to start the combat, there needs to be at least one combatant with a tick value."
}

View file

@ -21,11 +21,16 @@ const CombatTrackerMixin = (BaseCombatTracker: typeof CombatTracker) => {
turns: data.turns.map((turn) => ({ ...turn, waiting: this.viewed?.combatants.get(turn.id)?.waiting })),
} as CombatTracker.Data; // TODO: Improve upstream types
}
override activateListeners(html: JQuery): void {
super.activateListeners(html);
html.find('.combatant-control[data-control="toggleWaiting"]').on('click', this._onToggleWaiting.bind(this));
}
/**
* Handle clicks on the Combatant waiting control button.
* @param event The originating click event
*/
_onToggleWaiting(event: JQuery.ClickEvent) {
event.preventDefault();
event.stopPropagation();

View file

@ -15,9 +15,12 @@ const onActiveEffectChanged = (activeEffect: ActiveEffect) => {
const parent = activeEffect.parent;
const actorId = parent?.id;
if (!(parent instanceof Actor) || actorId === null || actorId === undefined) return;
const statusId = activeEffect.getFlag('core', 'statusId');
if (statusId === CONFIG.Combat.defeatedStatusId) {
const relevantCombats = game.combats?.filter((combat) => combat.getCombatantByActor(actorId) !== undefined) ?? [];
for (const combat of relevantCombats) {
combat.setupTurns();
if (combat === game.combat) {

View file

@ -29,10 +29,9 @@ const CombatMixin = (BaseCombat: typeof Combat) => {
return 0;
}
override async nextRound(): Promise<never> {
throw new Error('Not implemented!');
}
/**
* The current tick value of the Combat encounter.
*/
get tickValue(): number {
const tickValues = this.combatants
.filter((combatant) => !combatant.isDefeated)
@ -42,11 +41,15 @@ const CombatMixin = (BaseCombat: typeof Combat) => {
return tickValue === Infinity ? 0 : tickValue;
}
override async nextRound(): Promise<never> {
throw new Error('Not implemented!');
}
override async nextTurn() {
const game = getGame();
const combatant = this.combatant;
if (combatant === undefined || combatant.initiative === null || combatant.id === null) {
if (combatant === undefined || combatant.id === null) {
return this;
}
@ -62,10 +65,18 @@ const CombatMixin = (BaseCombat: typeof Combat) => {
});
if (ticks !== undefined && ticks !== null) {
await combatant.update({ initiative: combatant.initiative + ticks });
const advanceTime = ticks * CONFIG.time.roundTime;
return this.update(undefined, { diff: false, advanceTime } as DocumentModificationContext); // TODO: improve upstream types to allow this without type assertion
await combatant.advanceTicks(ticks);
}
return this;
}
override previousRound(): Promise<never> {
throw new Error('Not implemented!');
}
override previousTurn(): Promise<never> {
throw new Error('Not implemented!');
}
override async resetAll() {
@ -118,8 +129,8 @@ const CombatMixin = (BaseCombat: typeof Combat) => {
const ci = ia - ib;
if (ci !== 0) return ci;
const tba = a.getFlag(packageId, 'tieBreaker') ?? 0;
const tbb = b.getFlag(packageId, 'tieBreaker') ?? 0;
const tba = a.getFlag(packageId, 'tiebreaker') ?? 0;
const tbb = b.getFlag(packageId, 'tiebreaker') ?? 0;
const ctb = tba - tbb;
if (ctb !== 0) return ctb;

View file

@ -17,9 +17,9 @@ const CombatantMixin = (BaseCombatant: typeof Combatant) => {
_newInitiative: number | null | undefined;
/**
* An temporary property to make changes to the tieBreaker available to other instances in their `_pre…` methods.
* An temporary property to make changes to the tiebreaker available to other instances in their `_pre…` methods.
*/
_newTieBreaker: number | undefined;
_newTiebreaker: number | undefined;
/***
* Is this combatant currently waiting?
@ -28,31 +28,45 @@ const CombatantMixin = (BaseCombatant: typeof Combatant) => {
return this.getFlag(packageId, 'waiting') ?? false;
}
/**
* Toggle the waiting state of this combatant.
*/
toggleWaiting(): Promise<this | undefined> {
return this.update({ [`flags.${packageId}.waiting`]: !this.waiting, initiative: this.parent?.round });
const update: Record<string, unknown> = { [`flags.${packageId}.waiting`]: !this.waiting };
if (this.parent?.started && this.waiting) update.initiative = this.parent?.round;
return this.update(update);
}
protected override async _preCreate(...args: Parameters<Combatant['_preCreate']>): Promise<void> {
await super._preCreate(...args);
await this.#updateTieBreakerData(args[0]);
/**
* Advance for the given number of ticks.
* @param ticks The number of ticks to advance for
*/
async advanceTicks(ticks: number): Promise<void> {
if (this.initiative === null) {
ui.notifications?.warn('TICKWERK.WarningCannotAdvanceWithoutTickValue', { localize: true });
return;
}
if (this.waiting) {
ui.notifications?.warn('TICKWERK.WarningCannotAdvanceWhileWaiting', { localize: true });
return;
}
if (!this.combat?.started) {
ui.notifications?.warn('TICKWERK.WarningCannotAdvanceWithoutStartedCombat', { localize: true });
return;
}
await this.update({ initiative: this.initiative + ticks });
const advanceTime = ticks * CONFIG.time.roundTime;
if (advanceTime !== 0) {
await this.combat?.update(undefined, { diff: false, advanceTime } as DocumentModificationContext);
}
}
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> {
/**
* Update tiebreaker data for a given creation or update.
* @param data The data of the creation / update
*/
async #updateTiebreakerData(data: DeepPartial<CombatantDataConstructorData>): Promise<void> {
if ('initiative' in data) {
const combatantsWithSameTickValue =
this.parent?.combatants.filter((combatant) => {
@ -60,16 +74,21 @@ const CombatantMixin = (BaseCombatant: typeof Combatant) => {
combatant._newInitiative !== undefined ? combatant._newInitiative : combatant.initiative;
return otherInitiative === data.initiative;
}) ?? [];
const tieBreaker = await this.#getTieBreaker(combatantsWithSameTickValue);
setProperty(data, `flags.${packageId}.tieBreaker`, tieBreaker);
const tiebreaker = await this.#getTiebreaker(combatantsWithSameTickValue);
setProperty(data, `flags.${packageId}.tiebreaker`, tiebreaker);
this._newInitiative = data.initiative;
this._newTieBreaker = tieBreaker;
this._newTiebreaker = tiebreaker;
}
}
async #getTieBreaker(combatants: TickwerkCombatant[]): Promise<number> {
const getTieBreaker = CONFIG.tickwerk?.getTieBreaker ?? defaultGetTieBreaker;
return getTieBreaker(this, combatants);
/**
* Get a tiebreaker between this combatant and the given other combatants.
* @param combatants The other combatants among which to find a tiebreaker
* @returns A promise that resolves to the tiebreaker
*/
async #getTiebreaker(combatants: TickwerkCombatant[]): Promise<number> {
const getTiebreaker = CONFIG.tickwerk?.getTiebreaker ?? defaultGetTiebreaker;
return getTiebreaker(this, combatants);
}
protected override _getInitiativeFormula(): string {
@ -77,19 +96,39 @@ const CombatantMixin = (BaseCombatant: typeof Combatant) => {
if (getInitiativeFormula) return getInitiativeFormula(this);
return super._getInitiativeFormula();
}
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;
}
};
};
const defaultGetTieBreaker = async (combatant: TickwerkCombatant, combatants: TickwerkCombatant[]): Promise<number> => {
const defaultGetTiebreaker = async (combatant: TickwerkCombatant, combatants: TickwerkCombatant[]): Promise<number> => {
if (combatants.length === 0) return 0;
const tieBreakers = combatants.map((combatant) => {
const tiebreakers = combatants.map((combatant) => {
return (
(combatant._newTieBreaker !== undefined
? combatant._newTieBreaker
: combatant.getFlag(packageId, 'tieBreaker')) ?? 0
(combatant._newTiebreaker !== undefined
? combatant._newTiebreaker
: combatant.getFlag(packageId, 'tiebreaker')) ?? 0
);
});
return Math.max(...tieBreakers) + 1;
return Math.max(...tiebreakers) + 1;
};
export type TickwerkCombatantConstructor = ReturnType<typeof CombatantMixin>;
@ -99,7 +138,7 @@ declare global {
interface FlagConfig {
Combatant: {
tickwerk: {
tieBreaker?: number | undefined;
tiebreaker?: number | undefined;
waiting?: boolean | undefined;
};
};
@ -111,7 +150,7 @@ declare global {
interface CONFIG {
tickwerk?: {
getTieBreaker?: (combatant: TickwerkCombatant, combatants: TickwerkCombatant[]) => Promise<number>;
getTiebreaker?: (combatant: TickwerkCombatant, combatants: TickwerkCombatant[]) => Promise<number>;
getInitiativeFormula?: (combatant: TickwerkCombatant) => string;
};
}

View file

@ -1,26 +0,0 @@
// SPDX-FileCopyrightText: 2021 Johannes Loher
//
// SPDX-License-Identifier: MIT
import { packageId } from './constants';
const loggingContext = packageId;
const loggingSeparator = '|';
type LogLevel = 'debug' | 'info' | 'warning' | 'error';
type LoggingFunction = (...data: unknown[]) => void;
const getLoggingFunction = (type: LogLevel = 'info'): LoggingFunction => {
const log = { debug: console.debug, info: console.info, warning: console.warn, error: console.error }[type];
return (...data: unknown[]) => log(loggingContext, loggingSeparator, ...data);
};
const logger = Object.freeze({
debug: getLoggingFunction('debug'),
info: getLoggingFunction('info'),
warn: getLoggingFunction('warning'),
error: getLoggingFunction('error'),
getLoggingFunction,
});
export default logger;

View file

@ -8,19 +8,19 @@ import type { TickwerkCombatant } from '../data/documents/combatant';
export const registerDS4SpecificFunctionality = () => {
if (CONFIG.tickwerk === undefined) CONFIG.tickwerk = {};
foundry.utils.mergeObject(CONFIG.tickwerk, { getTieBreaker, getInitiativeFormula });
foundry.utils.mergeObject(CONFIG.tickwerk, { getTiebreaker, getInitiativeFormula });
};
const getTieBreaker = async (combatant: TickwerkCombatant, combatants: TickwerkCombatant[]): Promise<number> => {
const getTiebreaker = async (combatant: TickwerkCombatant, combatants: TickwerkCombatant[]): Promise<number> => {
if (combatants.length === 0) return 0;
const tieBreakers = combatants.map((combatant) => {
const tiebreakers = combatants.map((combatant) => {
return (
(combatant._newTieBreaker !== undefined
? combatant._newTieBreaker
: combatant.getFlag(packageId, 'tieBreaker')) ?? 0
(combatant._newTiebreaker !== undefined
? combatant._newTiebreaker
: combatant.getFlag(packageId, 'tiebreaker')) ?? 0
);
});
return Math.max(...tieBreakers) + 1;
return Math.max(...tiebreakers) + 1;
};
const getInitiativeFormula = (combatant: TickwerkCombatant) => {

View file

@ -1,4 +1,5 @@
{{!--
SPDX-FileCopyrightText: 2022 Foundry Gaming LLC.
SPDX-FileCopyrightText: 2022 Johannes Loher
SPDX-License-Identifier: MIT