From 2d9e418ba75e5d44780542031f51921a4fe914e1 Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Mon, 16 May 2022 03:46:53 +0200 Subject: [PATCH] refactor: cleanup --- lang/de.json | 3 + lang/en.json | 3 + src/apps/sidebar/combat-tracker.ts | 5 ++ src/data/documents/active-effect.ts | 3 + src/data/documents/combat.ts | 31 +++++--- src/data/documents/combatant.ts | 111 +++++++++++++++++++--------- src/logger.ts | 26 ------- src/systems/ds4.ts | 14 ++-- templates/combat-tracker.hbs | 1 + 9 files changed, 118 insertions(+), 79 deletions(-) delete mode 100644 src/logger.ts diff --git a/lang/de.json b/lang/de.json index 74faa37..2c6410e 100644 --- a/lang/de.json +++ b/lang/de.json @@ -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." } diff --git a/lang/en.json b/lang/en.json index 569c944..698bec6 100644 --- a/lang/en.json +++ b/lang/en.json @@ -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." } diff --git a/src/apps/sidebar/combat-tracker.ts b/src/apps/sidebar/combat-tracker.ts index ae52343..3a8adbe 100644 --- a/src/apps/sidebar/combat-tracker.ts +++ b/src/apps/sidebar/combat-tracker.ts @@ -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(); diff --git a/src/data/documents/active-effect.ts b/src/data/documents/active-effect.ts index 0ab6ef0..d11fe6d 100644 --- a/src/data/documents/active-effect.ts +++ b/src/data/documents/active-effect.ts @@ -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) { diff --git a/src/data/documents/combat.ts b/src/data/documents/combat.ts index 0d3605c..699f407 100644 --- a/src/data/documents/combat.ts +++ b/src/data/documents/combat.ts @@ -29,10 +29,9 @@ const CombatMixin = (BaseCombat: typeof Combat) => { return 0; } - override async nextRound(): Promise { - 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 { + 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 { + throw new Error('Not implemented!'); + } + + override previousTurn(): Promise { + 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; diff --git a/src/data/documents/combatant.ts b/src/data/documents/combatant.ts index 0881c5b..4672951 100644 --- a/src/data/documents/combatant.ts +++ b/src/data/documents/combatant.ts @@ -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 { - return this.update({ [`flags.${packageId}.waiting`]: !this.waiting, initiative: this.parent?.round }); + const update: Record = { [`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): Promise { - 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 { + 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): Promise { - 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): Promise { + /** + * Update tiebreaker data for a given creation or update. + * @param data The data of the creation / update + */ + async #updateTiebreakerData(data: DeepPartial): Promise { 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 { - 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 { + 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): Promise { + await super._preCreate(...args); + await this.#updateTiebreakerData(args[0]); + } + + protected override async _preUpdate(...args: Parameters): Promise { + 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 => { +const defaultGetTiebreaker = async (combatant: TickwerkCombatant, combatants: TickwerkCombatant[]): Promise => { 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; @@ -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; + getTiebreaker?: (combatant: TickwerkCombatant, combatants: TickwerkCombatant[]) => Promise; getInitiativeFormula?: (combatant: TickwerkCombatant) => string; }; } diff --git a/src/logger.ts b/src/logger.ts deleted file mode 100644 index 26b206c..0000000 --- a/src/logger.ts +++ /dev/null @@ -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; diff --git a/src/systems/ds4.ts b/src/systems/ds4.ts index 148f3ab..0667c5d 100644 --- a/src/systems/ds4.ts +++ b/src/systems/ds4.ts @@ -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 => { +const getTiebreaker = async (combatant: TickwerkCombatant, combatants: TickwerkCombatant[]): Promise => { 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) => { diff --git a/templates/combat-tracker.hbs b/templates/combat-tracker.hbs index 310f334..6c74ff3 100644 --- a/templates/combat-tracker.hbs +++ b/templates/combat-tracker.hbs @@ -1,4 +1,5 @@ {{!-- +SPDX-FileCopyrightText: 2022 Foundry Gaming LLC. SPDX-FileCopyrightText: 2022 Johannes Loher SPDX-License-Identifier: MIT