115 lines
3.4 KiB
JavaScript
115 lines
3.4 KiB
JavaScript
// SPDX-FileCopyrightText: 2022 Johannes Loher
|
|
//
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
import { packageId } from '../constants';
|
|
import { getGame } from '../helpers';
|
|
|
|
export const registerDS4SpecificFunctionality = () => {
|
|
if (CONFIG.tickwerk === undefined) CONFIG.tickwerk = {};
|
|
foundry.utils.mergeObject(CONFIG.tickwerk, { getTiebreaker, getInitiativeFormula, tickTime: 0.5 });
|
|
|
|
registerRollItemSetting();
|
|
Hooks.on('ds4.rollItem', onRollItem);
|
|
};
|
|
|
|
/** @type {import("../data/documents/combatant").GetTiebreaker} */
|
|
const getTiebreaker = async (combatant, combatants, waiting) => {
|
|
if (combatants.length === 0) return 0;
|
|
|
|
/** @type {number[]} */
|
|
const lowerBounds = [];
|
|
/** @type {number[]} */
|
|
const upperBounds = [];
|
|
/** @type {number[]} */
|
|
const equals = [];
|
|
|
|
for (const other of combatants) {
|
|
const tiebreaker = other._newTiebreaker ?? other.getFlag(packageId, 'tiebreaker') ?? 0;
|
|
if (getInitiative(other) > getInitiative(combatant) || waiting === false) {
|
|
lowerBounds.push(tiebreaker);
|
|
} else if (getInitiative(other) < getInitiative(combatant)) {
|
|
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;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get the initiative formula for a combatant.
|
|
* @param {TickwerkCombatant} combatant The combatant for which to get the initiative formula
|
|
* @returns {string} The initiative formula
|
|
*/
|
|
const getInitiativeFormula = (combatant) => {
|
|
const started = combatant.combat?.started ?? false;
|
|
if (!started) return '-@combatValues.initiative.total';
|
|
const tickValue = combatant.combat?.tickValue ?? 0;
|
|
return `max(${tickValue} + 10 - @combatValues.initiative.total, ${tickValue})`;
|
|
};
|
|
|
|
/**
|
|
* Get the initiative for a combatant.
|
|
* @param {TickwerkCombatant} combatant The combatant for which to get the initiative
|
|
* @returns {number}
|
|
*/
|
|
const getInitiative = (combatant) => {
|
|
return combatant.actor?.system.combatValues.initiative.total ?? -Infinity;
|
|
};
|
|
|
|
/**
|
|
* React to an item roll by prompting the user to advance ticks.
|
|
* @param {Item} item The item that has been rolled
|
|
*/
|
|
const onRollItem = (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,
|
|
});
|
|
};
|