2025-05-28 15:36:51 -07:00

279 lines
6.6 KiB
TypeScript

import { groupBy, keyBy } from "lodash";
import {
HelperUtils,
RollGroupContract,
RollResultContract,
} from "@dndbeyond/character-rules-engine/es";
import { RollResultGroupsLookup } from "./typings";
/**
*
* @param componentKey
* @param rollResultGroupsLookup
*/
export function getGroupsByComponentKey(
componentKey: string,
rollResultGroupsLookup: RollResultGroupsLookup
): Array<RollGroupContract> {
return HelperUtils.lookupDataOrFallback(
rollResultGroupsLookup,
componentKey,
[]
);
}
/**
*
* @param componentKey
* @param rollResultGroupsLookup
*/
export function getComponentOrderedGroups(
componentKey: string,
rollResultGroupsLookup: RollResultGroupsLookup
): Array<RollGroupContract> {
return getOrderedLinkedEntries(
getGroupsByComponentKey(componentKey, rollResultGroupsLookup)
);
}
/**
*
* @param rollResults
*/
export function getGroupOrderedRollResults(
rollResults: Array<RollResultContract>
): Array<RollResultContract> {
return getOrderedLinkedEntries(rollResults);
}
/**
*
* @param linkedEntries
*/
export function getOrderedLinkedEntries<T extends RollGroupContract>(
linkedEntries: Array<T>
): Array<T>;
export function getOrderedLinkedEntries<T extends RollResultContract>(
linkedEntries: Array<T>
): Array<T>;
export function getOrderedLinkedEntries<T extends any>(
linkedEntries: Array<T>
) {
// @ts-ignore
return helper__getOrderedLinkedEntries(
linkedEntries,
getLinkedEntryKey as (entry: T) => string | null,
getLinkedEntryNextKey as (entry: T) => string | null
);
}
//probs in HelperUtils
export function helper__getOrderedLinkedEntries<T extends any>(
linkedEntries: Array<T>,
getLinkedEntryKey: (entry: T) => string | null,
getLinkedEntryNextKey: (entry: T) => string | null
): Array<T> {
const entryByNextKeyLookup = keyBy(
linkedEntries,
(entry) => getLinkedEntryNextKey(entry) ?? "null"
);
let orderedEntries: Array<T> = [];
let currentEntry: T | null = HelperUtils.lookupDataOrFallback(
entryByNextKeyLookup,
"null"
);
// Ordering fallback if no current entry is found
if (!currentEntry) {
// Find entry by nextKey not being included in list of current keys
const includedKeys = linkedEntries.map(
(entry) => getLinkedEntryKey(entry) ?? "null"
);
currentEntry =
linkedEntries.find(
(entry) =>
!includedKeys.includes(getLinkedEntryNextKey(entry) ?? "null")
) ?? null;
}
let counter: number = 0;
while (counter <= linkedEntries.length && currentEntry) {
orderedEntries.push(currentEntry);
const nextEntryKey = getLinkedEntryKey(currentEntry);
currentEntry = null;
if (nextEntryKey) {
const nextEntry = HelperUtils.lookupDataOrFallback(
entryByNextKeyLookup,
nextEntryKey
);
if (nextEntry) {
currentEntry = nextEntry;
}
}
counter++;
}
return orderedEntries.reverse();
}
/**
*
* @param linkedEntry
*/
export function getLinkedEntryNextKey(
linkedEntry: RollResultContract | RollGroupContract
): string | null {
if (isLinkedEntryRollGroupContract(linkedEntry)) {
return linkedEntry.nextGroupKey;
} else if (isLinkedEntryRollResultContract(linkedEntry)) {
return linkedEntry.nextRollKey;
} else {
return null;
}
}
/**
*
* @param linkedEntry
*/
export function getLinkedEntryKey(
linkedEntry: RollResultContract | RollGroupContract
): string | null {
if (isLinkedEntryRollGroupContract(linkedEntry)) {
return linkedEntry.groupKey;
} else if (isLinkedEntryRollResultContract(linkedEntry)) {
return linkedEntry.rollKey;
} else {
return null;
}
}
/**
*
* @param linkedEntry
*/
export function isLinkedEntryRollGroupContract(
linkedEntry: RollResultContract | RollGroupContract
): linkedEntry is RollGroupContract {
return (linkedEntry as RollGroupContract).groupKey !== undefined;
}
/**
*
* @param linkedEntry
*/
export function isLinkedEntryRollResultContract(
linkedEntry: RollResultContract | RollGroupContract
): linkedEntry is RollResultContract {
return (linkedEntry as RollResultContract).rollKey !== undefined;
}
/**
*
* @param componentKey
* @param rollResultGroupsLookup
*/
export function generateComponentOrderedGroups(
componentKey: string,
rollResultGroupsLookup: RollResultGroupsLookup
): Array<RollGroupContract> {
const groups = getGroupsByComponentKey(componentKey, rollResultGroupsLookup);
const groupByGroupKeyLookup = keyBy(groups, (group) => group.groupKey);
const nextGroupKeyGroupsLookup = groupBy(
groups,
(group) => group.nextGroupKey
);
let orderedGroups: Array<RollGroupContract> = [];
//process any groups with nextGroupKey pointing to non-existent group
groups.forEach((group) => {
if (group.nextGroupKey) {
const nextGroup = HelperUtils.lookupDataOrFallback(
groupByGroupKeyLookup,
group.nextGroupKey
);
if (!nextGroup) {
orderedGroups.push(
...getOrderedGroupsByEndKey(
group.nextGroupKey,
groups,
groupByGroupKeyLookup,
nextGroupKeyGroupsLookup
)
);
}
}
});
//process any groups with null nextGroupKey
const nullTierGroups = getOrderedGroupsByEndKey(
"null",
groups,
groupByGroupKeyLookup,
nextGroupKeyGroupsLookup
);
return [...orderedGroups, ...nullTierGroups];
}
/**
*
* @param endKey
* @param groups
* @param groupByGroupKeyLookup
* @param nextGroupKeyGroupsLookup
*/
function getOrderedGroupsByEndKey(
endKey: string,
groups: Array<RollGroupContract>,
groupByGroupKeyLookup: Record<
RollGroupContract["groupKey"],
RollGroupContract
>,
nextGroupKeyGroupsLookup: Record<string, Array<RollGroupContract>>
): Array<RollGroupContract> {
const orderedGroups: Array<RollGroupContract> = [];
let currentTierGroups: Array<RollGroupContract> = [];
const endTierGroups = HelperUtils.lookupDataOrFallback(
nextGroupKeyGroupsLookup,
endKey
);
if (endTierGroups) {
currentTierGroups.push(...endTierGroups);
}
//find each tier of groups by nextGroupKey
let counter: number = 0;
while (
counter <= groups.length &&
currentTierGroups &&
currentTierGroups.length > 0
) {
const nextTierGroups: Array<RollGroupContract> = [];
currentTierGroups.forEach((group) => {
const foundGroups = HelperUtils.lookupDataOrFallback(
nextGroupKeyGroupsLookup,
group.groupKey
);
if (foundGroups) {
nextTierGroups.push(...foundGroups);
}
});
orderedGroups.push(...currentTierGroups);
currentTierGroups = nextTierGroups;
counter++;
}
return orderedGroups.reverse();
}