// SPDX-FileCopyrightText: 2022 Johannes Loher // // SPDX-License-Identifier: MIT import { packageId } from '../constants'; import { getGame } from '../helpers'; import type { TickwerkCombatant } from '../data/documents/combatant'; export const registerDS4SpecificFunctionality = () => { if (CONFIG.tickwerk === undefined) CONFIG.tickwerk = {}; foundry.utils.mergeObject(CONFIG.tickwerk, { getTiebreaker, getInitiativeFormula }); registerRollItemSetting(); Hooks.on('ds4.rollItem', onRollItem); }; const getTiebreaker = async (combatant: TickwerkCombatant, combatants: TickwerkCombatant[]): Promise => { const ds4combatant = combatant as DS4TickwerkCombatant; const ds4combatants = combatants as DS4TickwerkCombatant[]; if (combatants.length === 0) return 0; const lowerBounds: number[] = []; const upperBounds: number[] = []; const equals: number[] = []; for (const combatant of ds4combatants) { const tiebreaker = combatant._newTiebreaker ?? combatant.getFlag(packageId, 'tiebreaker') ?? 0; if (getInitiative(combatant) > getInitiative(ds4combatant)) { lowerBounds.push(tiebreaker); } else if (getInitiative(combatant) < getInitiative(ds4combatant)) { upperBounds.push(tiebreaker); } else { equals.push(tiebreaker); } } equals.sort(); const positionAmongEquals = Math.floor(twist.random() * (equals.length + 1)); equals.forEach((equal, index) => { if (index < positionAmongEquals) { lowerBounds.push(equal); } else { upperBounds.push(equal); } }); const lowerBound = Math.max(...lowerBounds); const upperBound = Math.min(...upperBounds); if (lowerBound === -Infinity) { if (upperBound === Infinity) { return 0; } else { return upperBound - 1; } } else { if (upperBound === Infinity) { return lowerBound + 1; } else { return (lowerBound + upperBound) / 2; } } }; const getInitiativeFormula = (combatant: TickwerkCombatant) => { const started = combatant.combat?.started ?? false; if (!started) return '-@combatValues.initiative.total'; const tickValue = combatant.combat?.round ?? 0; return `max(${tickValue} + 10 - @combatValues.initiative.total, ${tickValue})`; }; 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: { total: number; }; }; } declare global { // eslint-disable-next-line @typescript-eslint/no-namespace namespace Hooks { interface StaticCallbacks { 'ds4.rollItem': (item: Item) => void; } } } const onRollItem = (item: Item) => { const game = getGame(); if (game.settings.get(packageId, 'ds4.reactToRollItemHook')) { if (['weapon', 'spell'].includes(item.type) && item.actor?.id) { const combatants = item.actor .getActiveTokens(false, true) .map((token) => game.combat?.getCombatantByToken(token.id ?? '')); for (const combatant of combatants) { if (combatant?.parent?.started) combatant?.advanceTicksDialog(); } } } }; const registerRollItemSetting = () => { getGame().settings.register(packageId, 'ds4.reactToRollItemHook', { name: 'TICKWERK.SettingDS4ReactToRollItemHookName', hint: 'TICKWERK.SettingDS4ReactToRollItemHookHint', scope: 'client', config: true, type: Boolean, default: true, }); };