diff --git a/src/settings/settings.ts b/src/settings/settings.ts index 06d46e2..bf8f159 100644 --- a/src/settings/settings.ts +++ b/src/settings/settings.ts @@ -20,7 +20,9 @@ import { DEFAULT_UNDEFINED, EDIT, HP, - INITIATIVE + INITIATIVE, + OVERFLOW_TYPE, + RESOLVE_TIES } from "../utils"; import { RpgSystemSetting, getRpgSystem } from "../utils/rpg-system"; import type { Party } from "./settings.types"; @@ -209,10 +211,10 @@ export default class InitiativeTrackerSettings extends PluginSettingTab { "Set what happens to healing which goes above creatures' max HP threshold." ) .addDropdown((d) => { - d.addOption("ignore", "Ignore"); - d.addOption("temp", "Add to temp HP"); - d.addOption("current", "Add to current HP"); - d.setValue(this.plugin.data.hpOverflow ?? "ignore"); + d.addOption(OVERFLOW_TYPE.ignore, "Ignore"); + d.addOption(OVERFLOW_TYPE.temp, "Add to temp HP"); + d.addOption(OVERFLOW_TYPE.current, "Add to current HP"); + d.setValue(this.plugin.data.hpOverflow ?? OVERFLOW_TYPE.ignore); d.onChange(async (v) => { this.plugin.data.hpOverflow = v; this.plugin.saveSettings(); @@ -329,6 +331,21 @@ export default class InitiativeTrackerSettings extends PluginSettingTab { this.display(); }); }); + new Setting(additionalContainer) + .setName("Resolve Initiative Ties") + .setDesc( + "Define what happens if two creatures have the same initiative." + ) + .addDropdown((d) => { + d.addOption(RESOLVE_TIES.playerFirst, "Player first"); + d.addOption(RESOLVE_TIES.npcFirst, "NPC first"); + d.addOption(RESOLVE_TIES.random, "Random"); + d.setValue(this.plugin.data.resolveTies ?? RESOLVE_TIES.playerFirst); + d.onChange(async (v) => { + this.plugin.data.resolveTies = v; + this.plugin.saveSettings(); + }); + }); } private _displayPlayers(additionalContainer: HTMLDetailsElement) { additionalContainer.empty(); diff --git a/src/settings/settings.types.ts b/src/settings/settings.types.ts index 55dc32c..d899af4 100644 --- a/src/settings/settings.types.ts +++ b/src/settings/settings.types.ts @@ -39,6 +39,7 @@ export interface InitiativeTrackerData { warnedAboutImports: boolean; logging: boolean; logFolder: string; + resolveTies: string; useLegacy: boolean; diplayPlayerHPValues: boolean; rollHP: boolean; diff --git a/src/tracker/stores/tracker.ts b/src/tracker/stores/tracker.ts index 5bfdd26..d62dec6 100644 --- a/src/tracker/stores/tracker.ts +++ b/src/tracker/stores/tracker.ts @@ -15,6 +15,7 @@ import type { InitiativeTrackerData } from "src/settings/settings.types"; import type { InitiativeViewState } from "../view.types"; import { OVERFLOW_TYPE, + RESOLVE_TIES, RollPlayerInitiativeBehavior, getRpgSystem } from "src/utils"; @@ -95,7 +96,7 @@ function createTracker() { return data.descending; }); let _settings: InitiativeTrackerData | null; - + const condensed = derived(creatures, (values) => { if (_settings?.condense) { values.forEach((creature, _, arr) => { @@ -116,9 +117,40 @@ function createTracker() { const ordered = derived([condensed, data], ([values, data]) => { const sort = [...values]; sort.sort((a, b) => { - return data.descending + /* Order creatures in this order: + 1. By initiative + 2. By manual order (drag & drop) + 3. According to the resolveTies setting */ + if (a.initiative != b.initiative) { + return data.descending ? b.initiative - a.initiative : a.initiative - b.initiative; + } + + if ( + a.manualOrder !== null && a.manualOrder !== undefined && + b.manualOrder !== null && b.manualOrder !== undefined && + a.manualOrder !== b.manualOrder + ) { + const aOrder = a.manualOrder || 0; + const bOrder = b.manualOrder || 0; + return aOrder - bOrder; + } + + switch (_settings.resolveTies) { + case RESOLVE_TIES.random: + return Math.random() < 0.5 ? 1 : -1; + case RESOLVE_TIES.playerFirst: + case RESOLVE_TIES.npcFirst: + const aPlayer = a.player ? 1 : 0; + const bPlayer = b.player ? 1 : 0; + if (_settings.resolveTies == RESOLVE_TIES.playerFirst) { + return bPlayer - aPlayer + } else { + return aPlayer - bPlayer + } + } + }); current_order = sort; return sort; @@ -336,6 +368,7 @@ function createTracker() { creature.modifier ); } + creature.manualOrder = null; } return creatures; } diff --git a/src/tracker/ui/creatures/Table.svelte b/src/tracker/ui/creatures/Table.svelte index f1dd016..6d7a01b 100644 --- a/src/tracker/ui/creatures/Table.svelte +++ b/src/tracker/ui/creatures/Table.svelte @@ -54,7 +54,10 @@ tracker.logNewInitiative(dropped.creature); } items = e.detail.items; - $tracker = [...items.map(({ creature }) => creature)]; + $tracker = [...items.map(({ creature }, i) => { + creature.manualOrder = i; + return creature; + })]; } const diceIcon = (node: HTMLElement) => { diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 26d63db..5b2f497 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -18,6 +18,18 @@ export enum RollPlayerInitiativeBehavior { SetToZero } +export const OVERFLOW_TYPE: { [key: string]: string } = { + ignore: "ignore", + current: "current", + temp: "temp" +}; + +export const RESOLVE_TIES: { [key: string]: string } = { + playerFirst: "playerFirst", + npcFirst: "npcFirst", + random: "random", +}; + export const DEFAULT_SETTINGS: InitiativeTrackerData = { players: [], parties: [], @@ -54,11 +66,12 @@ export const DEFAULT_SETTINGS: InitiativeTrackerData = { player: true, builder: true }, - hpOverflow: "ignore", + hpOverflow: OVERFLOW_TYPE.ignore, additiveTemp: false, rpgSystem: "dnd5e", logging: false, logFolder: "/", + resolveTies: RESOLVE_TIES.playerFirst, useLegacy: false, diplayPlayerHPValues: true, rollHP: false, @@ -71,12 +84,6 @@ export const DEFAULT_SETTINGS: InitiativeTrackerData = { rollPlayerInitiatives: RollPlayerInitiativeBehavior.Always }; -export const OVERFLOW_TYPE: { [key: string]: string } = { - ignore: "ignore", - current: "current", - temp: "temp" -}; - export const DECIMAL_TO_VULGAR_FRACTION: Record = { 0.125: "⅛", 0.25: "¼", diff --git a/src/utils/creature.ts b/src/utils/creature.ts index d92a5d1..66164b3 100644 --- a/src/utils/creature.ts +++ b/src/utils/creature.ts @@ -35,6 +35,7 @@ export class Creature { status: Set = new Set(); marker: string; initiative: number; + manualOrder: number; static: boolean = false; source: string | string[]; id: string;