tickwerk/src/data/documents/combat.js
Johannes Loher b6411f39dc feat: update for v10
Unfortunately, ditching TypeScript is needed for that :(
2022-11-28 00:52:31 +01:00

176 lines
4.9 KiB
JavaScript

// SPDX-FileCopyrightText: 2022 Johannes Loher
//
// SPDX-License-Identifier: MIT
import { packageId } from '../../constants';
export const registerCombatFunctionality = () => {
CONFIG.Combat.documentClass = CombatMixin(CONFIG.Combat.documentClass);
};
/**
* Enhance a combat class with functionality for the Tickwerk.
* @param {typeof Combat} BaseCombat The combat class to enhance
* @returns A combat class, adapted for the Tickwerk
*/
const CombatMixin = (BaseCombat) => {
return class TickwerkCombat extends BaseCombat {
/** @override */
get combatant() {
return this.turns?.[0];
}
/** @override */
get nextCombatant() {
return this.turns?.[1];
}
/** @override */
get started() {
return (this.turns?.length ?? 0) > 0 && (this.getFlag(packageId, 'started') ?? false);
}
/**
* The current tick value of the Combat encounter.
* @type {number}
*/
get tickValue() {
const tickValues = this.combatants
.filter((combatant) => !combatant.isDefeated && !combatant.waiting)
.map((combatant) => combatant.initiative)
.filter((tickValue) => tickValue !== null);
const tickValue = Math.min(...tickValues);
return tickValue === Infinity ? 0 : tickValue;
}
/** @override */
prepareDerivedData() {
super.prepareDerivedData();
this.turn = this.started ? 0 : null;
}
/** @override */
async nextRound() {
throw new Error('Not implemented!');
}
/** @override */
async nextTurn() {
await this.combatant?.advanceTicksDialog();
return this;
}
/** @override */
previousRound() {
throw new Error('Not implemented!');
}
/** @override */
previousTurn() {
throw new Error('Not implemented!');
}
/** @override */
async resetAll() {
for (const c of this.combatants) {
c.updateSource({ initiative: null });
}
return this.update(
{
turn: 0,
combatants: this.combatants.toObject().map((combatant) => {
if (combatant.flags.tickwerk.tiebreaker) delete combatant.flags.tickwerk.tiebreaker;
return { ...combatant, [`flags.${packageId}.-=tiebreaker`]: null };
}),
flags: { [packageId]: { started: false } },
},
{ diff: false },
);
}
/** @override */
setupTurns() {
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?.tokenId ?? null,
};
return (this.turns = turns);
}
/** @override */
async startCombat() {
const hasCombatantWithTickValue = this.combatants.find(
(combatant) => !combatant.isDefeated && combatant.initiative !== null,
);
if (!hasCombatantWithTickValue) {
ui.notifications?.warn('TICKWERK.WarningCannotStartCombat', { localize: true });
return this;
}
this._playCombatSound('startEncounter');
const updateData = { flags: { [packageId]: { started: true } } };
Hooks.callAll('combatStart', this, updateData);
return this.update(updateData);
}
/** @override */
_sortCombatants(a, b) {
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;
}
/** @override */
async _preUpdate(changed, options, user) {
delete changed.round;
delete changed.turn;
options.tickwerk = {
combatantBeforeUpdate: this.combatant?.id,
nextCombatantBeforeUpdate: this.nextCombatant?.id,
};
return super._preUpdate(changed, options, user);
}
/** @override */
_onUpdate(data, options, userId) {
super._onUpdate(data, options, userId);
if (game.user.character) {
const { combatantBeforeUpdate, nextCombatantBeforeUpdate } = options.tickwerk ?? {};
if (
combatantBeforeUpdate !== undefined &&
combatantBeforeUpdate !== this.combatant &&
this.combatant?.actorId === game.user.character._id
) {
this._playCombatSound('yourTurn');
} else if (
nextCombatantBeforeUpdate !== undefined &&
nextCombatantBeforeUpdate !== this.nextCombatant &&
this.nextCombatant?.actorId === game.user.character._id
)
this._playCombatSound('nextUp');
}
}
};
};