140 lines
4.1 KiB
TypeScript
140 lines
4.1 KiB
TypeScript
|
// SPDX-FileCopyrightText: 2022 Johannes Loher
|
||
|
//
|
||
|
// SPDX-License-Identifier: MIT
|
||
|
|
||
|
import { packageId } from '../../constants';
|
||
|
import { getGame } from '../../helpers';
|
||
|
|
||
|
import type { TickwerkCombatant } from './combatant';
|
||
|
|
||
|
export const registerCombatFunctionality = () => {
|
||
|
CONFIG.Combat.documentClass = CombatMixin(CONFIG.Combat.documentClass);
|
||
|
};
|
||
|
|
||
|
const CombatMixin = (BaseCombat: typeof Combat) => {
|
||
|
return class TickwerkCombat extends BaseCombat {
|
||
|
override get combatant() {
|
||
|
return this.turns[0];
|
||
|
}
|
||
|
|
||
|
override get round() {
|
||
|
return this.tickValue;
|
||
|
}
|
||
|
|
||
|
override get started() {
|
||
|
return this.turns.length > 0 && (this.getFlag(packageId, 'started') ?? false);
|
||
|
}
|
||
|
|
||
|
override get turn() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
override async nextRound(): Promise<never> {
|
||
|
throw new Error('Not implemented!');
|
||
|
}
|
||
|
|
||
|
get tickValue(): number {
|
||
|
const tickValues = this.combatants
|
||
|
.filter((combatant) => !combatant.isDefeated)
|
||
|
.map((combatant) => combatant.initiative)
|
||
|
.filter((tickValue): tickValue is number => tickValue !== null);
|
||
|
const tickValue = Math.min(...tickValues);
|
||
|
return tickValue === Infinity ? 0 : tickValue;
|
||
|
}
|
||
|
|
||
|
override async nextTurn() {
|
||
|
const game = getGame();
|
||
|
|
||
|
const combatant = this.combatant;
|
||
|
if (combatant === undefined || combatant.initiative === null || combatant.id === null) {
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
const ticks = await Dialog.prompt({
|
||
|
title: game.i18n.localize('TICKWERK.AdvanceTicks'),
|
||
|
content: '<input name="ticks" type="number" value="5" min="0" />',
|
||
|
label: game.i18n.localize('TICKWERK.AdvanceTicks'),
|
||
|
callback: (html) => {
|
||
|
const ticks = html[0]?.querySelector<HTMLInputElement>('input[name="ticks"]')?.value;
|
||
|
return ticks !== undefined ? parseInt(ticks) : undefined;
|
||
|
},
|
||
|
rejectClose: false,
|
||
|
});
|
||
|
|
||
|
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
|
||
|
}
|
||
|
}
|
||
|
|
||
|
override async resetAll() {
|
||
|
for (const c of this.combatants) {
|
||
|
c.data.update({ initiative: null });
|
||
|
}
|
||
|
return this.update(
|
||
|
{ turn: 0, combatants: this.combatants.toObject(), flags: { [packageId]: { started: false } } },
|
||
|
{ diff: false },
|
||
|
);
|
||
|
}
|
||
|
|
||
|
override setupTurns(): this['turns'] {
|
||
|
const turns = this.combatants.contents.sort(this._sortCombatants);
|
||
|
|
||
|
const c = turns[0];
|
||
|
this.current = {
|
||
|
round: this.round,
|
||
|
turn: 0,
|
||
|
combatantId: c?.id ?? null,
|
||
|
tokenId: c?.data.tokenId ?? null,
|
||
|
};
|
||
|
return (this.turns = turns);
|
||
|
}
|
||
|
|
||
|
override async startCombat(): Promise<this | undefined> {
|
||
|
const hasCombatantWithTickValue = this.combatants.find(
|
||
|
(combatant) => !combatant.isDefeated && combatant.initiative !== null,
|
||
|
);
|
||
|
if (!hasCombatantWithTickValue) {
|
||
|
ui.notifications?.warn('TICKWERK.WarningCannotStartCombat', { localize: true });
|
||
|
return this;
|
||
|
}
|
||
|
return this.setFlag(packageId, 'started', true);
|
||
|
}
|
||
|
|
||
|
protected override _sortCombatants(a: TickwerkCombatant, b: TickwerkCombatant): number {
|
||
|
const da = a.isDefeated ? 1 : 0;
|
||
|
const db = b.isDefeated ? 1 : 0;
|
||
|
const cd = da - db;
|
||
|
if (cd !== 0) return cd;
|
||
|
|
||
|
const wa = a.waiting ? 1 : 0;
|
||
|
const wb = b.waiting ? 1 : 0;
|
||
|
const cw = wa - wb;
|
||
|
if (cw !== 0) return cw;
|
||
|
|
||
|
const ia = a.initiative ?? Infinity;
|
||
|
const ib = b.initiative ?? Infinity;
|
||
|
const ci = ia - ib;
|
||
|
if (ci !== 0) return ci;
|
||
|
|
||
|
const tba = a.getFlag(packageId, 'tieBreaker') ?? 0;
|
||
|
const tbb = b.getFlag(packageId, 'tieBreaker') ?? 0;
|
||
|
const ctb = tba - tbb;
|
||
|
if (ctb !== 0) return ctb;
|
||
|
|
||
|
return (b.id ?? '') > (a.id ?? '') ? 1 : -1;
|
||
|
}
|
||
|
};
|
||
|
};
|
||
|
|
||
|
declare global {
|
||
|
interface FlagConfig {
|
||
|
Combat: {
|
||
|
tickwerk?: {
|
||
|
started?: boolean;
|
||
|
};
|
||
|
};
|
||
|
}
|
||
|
}
|