From 9a9bff08c015536bc661b069aaa7fc70ec09a448 Mon Sep 17 00:00:00 2001
From: Johannes Loher <johannes.loher@fg4f.de>
Date: Thu, 19 May 2022 00:28:55 +0200
Subject: [PATCH] refactor: move advance ticks dialog into combatant

---
 src/data/documents/combat.ts    | 24 +-----------------------
 src/data/documents/combatant.ts | 29 +++++++++++++++++++++++++++++
 src/systems/ds4.ts              | 14 ++++++--------
 3 files changed, 36 insertions(+), 31 deletions(-)

diff --git a/src/data/documents/combat.ts b/src/data/documents/combat.ts
index 1322917..1ea1e93 100644
--- a/src/data/documents/combat.ts
+++ b/src/data/documents/combat.ts
@@ -3,7 +3,6 @@
 // SPDX-License-Identifier: MIT
 
 import { packageId } from '../../constants';
-import { getGame } from '../../helpers';
 
 import type { TickwerkCombatant } from './combatant';
 
@@ -46,28 +45,7 @@ const CombatMixin = (BaseCombat: typeof Combat) => {
     }
 
     override async nextTurn() {
-      const game = getGame();
-
-      const combatant = this.combatant;
-      if (combatant === undefined || 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.advanceTicks(ticks);
-      }
-
+      await this.combatant?.advanceTicksDialog();
       return this;
     }
 
diff --git a/src/data/documents/combatant.ts b/src/data/documents/combatant.ts
index f38305d..8e0a9a4 100644
--- a/src/data/documents/combatant.ts
+++ b/src/data/documents/combatant.ts
@@ -4,6 +4,7 @@
 
 import type { CombatantDataConstructorData } from '@league-of-foundry-developers/foundry-vtt-types/src/foundry/common/data/data.mjs/combatantData';
 import { packageId } from '../../constants';
+import { getGame } from '../../helpers';
 
 export const registerCombatantFunctionality = () => {
   CONFIG.Combatant.documentClass = CombatantMixin(CONFIG.Combatant.documentClass);
@@ -62,6 +63,25 @@ const CombatantMixin = (BaseCombatant: typeof Combatant) => {
       }
     }
 
+    async advanceTicksDialog(): Promise<void> {
+      const game = getGame();
+
+      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 this.advanceTicks(ticks);
+      }
+    }
+
     /**
      * Update tiebreaker data for a given creation or update.
      * @param data The data of the creation / update
@@ -91,6 +111,15 @@ const CombatantMixin = (BaseCombatant: typeof Combatant) => {
       return getTiebreaker(this, combatants);
     }
 
+    override testUserPermission(
+      user: foundry.documents.BaseUser,
+      permission: keyof typeof foundry.CONST.DOCUMENT_PERMISSION_LEVELS | foundry.CONST.DOCUMENT_PERMISSION_LEVELS,
+      { exact }: { exact?: boolean | undefined } = {},
+    ): boolean {
+      if (user.isGM) return true;
+      return super.testUserPermission(user, permission, { exact });
+    }
+
     protected override _getInitiativeFormula(): string {
       const getInitiativeFormula = CONFIG.tickwerk?.getInitiativeFormula;
       if (getInitiativeFormula) return getInitiativeFormula(this);
diff --git a/src/systems/ds4.ts b/src/systems/ds4.ts
index 7dcbcb5..7846aba 100644
--- a/src/systems/ds4.ts
+++ b/src/systems/ds4.ts
@@ -22,15 +22,9 @@ const getTiebreaker = async (combatant: TickwerkCombatant, combatants: TickwerkC
 
   for (const combatant of ds4combatants) {
     const tiebreaker = combatant._newTiebreaker ?? combatant.getFlag(packageId, 'tiebreaker') ?? 0;
-    if (
-      (combatant.actor?.data.data.combatValues.initiative.total ?? -Infinity) >
-      (ds4combatant.actor?.data.data.combatValues.initiative.total ?? -Infinity)
-    ) {
+    if (getInitiative(combatant) > getInitiative(ds4combatant)) {
       lowerBounds.push(tiebreaker);
-    } else if (
-      (combatant.actor?.data.data.combatValues.initiative.total ?? -Infinity) <
-      (ds4combatant.actor?.data.data.combatValues.initiative.total ?? -Infinity)
-    ) {
+    } else if (getInitiative(combatant) < getInitiative(ds4combatant)) {
       upperBounds.push(tiebreaker);
     } else {
       equals.push(tiebreaker);
@@ -74,6 +68,10 @@ const getInitiativeFormula = (combatant: TickwerkCombatant) => {
 
 type DS4TickwerkCombatant = TickwerkCombatant & { actor: (Actor & { data: { data: ActorData } }) | null };
 
+const getInitiative = (combatant: DS4TickwerkCombatant): number => {
+  return combatant.actor?.data.data.combatValues.initiative.total ?? -Infinity;
+};
+
 interface ActorData {
   combatValues: {
     initiative: {