131 lines
3.5 KiB
TypeScript
131 lines
3.5 KiB
TypeScript
import {
|
|
DiceUtils,
|
|
DiceContract,
|
|
RollValueContract,
|
|
HelperUtils,
|
|
} from "@dndbeyond/character-rules-engine/es";
|
|
import {
|
|
RollKinds,
|
|
RollTypes,
|
|
} from "@dndbeyond/pocket-dimension-dice/constants";
|
|
import { parseDiceNotation } from "@dndbeyond/pocket-dimension-dice/helpers/parseDiceNotation";
|
|
import { toDiceNotationString } from "@dndbeyond/pocket-dimension-dice/helpers/toDiceNotationString";
|
|
import { RollDicePayload } from "@dndbeyond/pocket-dimension-dice/types";
|
|
|
|
import { DiceComponentUtils, TypeScriptUtils } from "../index";
|
|
import {
|
|
hack__generateDiceRollResultTermLookup,
|
|
hack__generateDiceRollResultValueExcludeTypeLookup,
|
|
} from "./hacks";
|
|
|
|
/**
|
|
* Checks for a critical hit
|
|
*/
|
|
export function isCriticalRoll(result: RollDicePayload["data"]): boolean {
|
|
// Does this roll have a to-hit roll?
|
|
const hitRoll = result.rolls.find((r) => r.rollType == RollTypes.ToHit);
|
|
if (hitRoll) {
|
|
const minRollForCrit: number = 20;
|
|
let numCritRolls: number =
|
|
hitRoll.result?.values.filter((v) => v >= minRollForCrit).length ?? 0;
|
|
|
|
let critRoll: boolean = false;
|
|
switch (hitRoll.rollKind) {
|
|
case RollKinds.Disadvantage:
|
|
critRoll = numCritRolls >= 2;
|
|
break;
|
|
case RollKinds.Advantage:
|
|
default:
|
|
critRoll = numCritRolls > 0;
|
|
break;
|
|
}
|
|
return critRoll;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns a dice notation string taking into account the current state
|
|
* (doubles damage dice when in critical hit state)
|
|
*
|
|
* @param isCriticalHit Flags if the dice notation is in a critical hit state
|
|
* @param damage Normally displayed damage
|
|
*/
|
|
export function getDamageDiceNotation(
|
|
damage: number | DiceContract | null,
|
|
isCriticalHit?: boolean
|
|
): string {
|
|
if (typeof damage === "number") {
|
|
return damage.toString();
|
|
}
|
|
|
|
if (!isCriticalHit) {
|
|
return DiceUtils.renderDice(damage);
|
|
}
|
|
|
|
let dn = parseDiceNotation(DiceUtils.renderDice(damage));
|
|
|
|
for (let i = 0; i < dn.set.length; i++) {
|
|
// Double the dice on each term
|
|
if (dn.set[i]) {
|
|
dn.set[i]!.count *= 2;
|
|
}
|
|
}
|
|
|
|
return toDiceNotationString(dn.set, dn.constant);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param dieType
|
|
*/
|
|
export function getDieTypeValue(dieType: string): number {
|
|
return Number(dieType.substr(1));
|
|
}
|
|
|
|
/**
|
|
* Returns Array<RollValueContract> given the a rollRequestResult with a single RollResult
|
|
*
|
|
* @param rollRequestResult
|
|
*/
|
|
export function generateRollValueContracts(
|
|
rollRequestResult: RollDicePayload["data"]
|
|
): Array<RollValueContract> | null {
|
|
//these hacks make it easier to grab the data from the dice api until the api is updated
|
|
const hack__diceRollResultTermLookup =
|
|
hack__generateDiceRollResultTermLookup(rollRequestResult);
|
|
const hack__diceRollResultValueExcludeTypeLookup =
|
|
hack__generateDiceRollResultValueExcludeTypeLookup(rollRequestResult);
|
|
|
|
//RollResult | undefined - RollResult is not exported from Dice;
|
|
// only expect to have one single rollRequest.
|
|
const rollResult = rollRequestResult.rolls[0].result ?? null;
|
|
|
|
if (!rollResult) {
|
|
return null;
|
|
}
|
|
|
|
return rollResult.values
|
|
.map((value, idx) => {
|
|
const dieTerm = HelperUtils.lookupDataOrFallback(
|
|
hack__diceRollResultTermLookup,
|
|
idx
|
|
);
|
|
const excludeType = HelperUtils.lookupDataOrFallback(
|
|
hack__diceRollResultValueExcludeTypeLookup,
|
|
idx
|
|
);
|
|
|
|
if (!dieTerm || !excludeType) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
value,
|
|
dieType: DiceComponentUtils.getDieTypeValue(dieTerm.dieType),
|
|
excludeType,
|
|
};
|
|
})
|
|
.filter(TypeScriptUtils.isNotNullOrUndefined);
|
|
}
|