Grabbed dndbeyond's source code
``` ~/go/bin/sourcemapper -output ddb -jsurl https://media.dndbeyond.com/character-app/static/js/main.90aa78c5.js ```
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
import { orderBy } from "lodash";
|
||||
|
||||
import { Constants } from "@dndbeyond/character-rules-engine/es";
|
||||
import { DiceOperation, DieTerm, RollRequest } from "@dndbeyond/dice";
|
||||
|
||||
interface Hack__OrderableDieValueInfo {
|
||||
idx: number;
|
||||
value: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* generate lookup of dieTerm.dice idx to DieTerm
|
||||
* for RollValueContract dieType
|
||||
*
|
||||
* @param rollRequestResult
|
||||
*/
|
||||
export function hack__generateDiceRollResultTermLookup(
|
||||
rollRequestResult: RollRequest
|
||||
): Record<number, DieTerm> {
|
||||
const hack__diceRollResultTermLookup: Record<number, DieTerm> = {};
|
||||
|
||||
let dieCurrentIdx: number = 0;
|
||||
rollRequestResult.rolls[0].diceNotation.set.forEach((dieTerm) => {
|
||||
dieTerm.dice.forEach((die) => {
|
||||
hack__diceRollResultTermLookup[dieCurrentIdx] = dieTerm;
|
||||
dieCurrentIdx++;
|
||||
});
|
||||
});
|
||||
|
||||
return hack__diceRollResultTermLookup;
|
||||
}
|
||||
|
||||
/**
|
||||
* generate lookup of RollResult.values array idx to DiceRollExcludeTypeEnum
|
||||
* for RollValueContract excludeType
|
||||
*
|
||||
* @param rollRequestResult
|
||||
*/
|
||||
export function hack__generateDiceRollResultValueExcludeTypeLookup(
|
||||
rollRequestResult: RollRequest
|
||||
): Record<number, Constants.DiceRollExcludeTypeEnum> {
|
||||
const hack__diceRollResultValueExcludeTypeLookup: Record<
|
||||
number,
|
||||
Constants.DiceRollExcludeTypeEnum
|
||||
> = {};
|
||||
|
||||
//RollResult | undefined - RollResult is not exported from Dice;
|
||||
// only expect to have one single rollRequest.
|
||||
const rollResult = rollRequestResult.rolls[0].result;
|
||||
|
||||
let dieGroupStartIdx: number = 0;
|
||||
|
||||
rollRequestResult.rolls[0].diceNotation.set.forEach((dieTerm) => {
|
||||
let diceCount = dieTerm.count;
|
||||
|
||||
//determine sortOrder based on dieTerm.operation
|
||||
let sortOrder: "asc" | "desc" | null = null;
|
||||
switch (dieTerm.operation) {
|
||||
case DiceOperation.Min:
|
||||
sortOrder = "asc";
|
||||
break;
|
||||
case DiceOperation.Max:
|
||||
default:
|
||||
sortOrder = "desc";
|
||||
}
|
||||
|
||||
let dieTermResults =
|
||||
rollResult?.values.slice(
|
||||
dieGroupStartIdx,
|
||||
dieGroupStartIdx + diceCount
|
||||
) ?? [];
|
||||
let hack__orderableDieValueInfos: Array<Hack__OrderableDieValueInfo> =
|
||||
dieTermResults.map((value, idx) => ({
|
||||
idx: dieGroupStartIdx + idx,
|
||||
value,
|
||||
}));
|
||||
let orderedDieTermResults = orderBy(
|
||||
hack__orderableDieValueInfos,
|
||||
(valueInfo) => valueInfo.value,
|
||||
sortOrder
|
||||
);
|
||||
|
||||
//build-up hack__diceRollResultValueExcludeTypeLookup
|
||||
orderedDieTermResults.slice(0, dieTerm.operand).forEach((dieTermResult) => {
|
||||
hack__diceRollResultValueExcludeTypeLookup[dieTermResult.idx] =
|
||||
Constants.DiceRollExcludeTypeEnum.ALL;
|
||||
});
|
||||
orderedDieTermResults.slice(dieTerm.operand).forEach((dieTermResult) => {
|
||||
hack__diceRollResultValueExcludeTypeLookup[dieTermResult.idx] =
|
||||
Constants.DiceRollExcludeTypeEnum.NONE;
|
||||
});
|
||||
|
||||
//move diceGroupStartIdx
|
||||
dieGroupStartIdx += diceCount;
|
||||
});
|
||||
|
||||
return hack__diceRollResultValueExcludeTypeLookup;
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
import {
|
||||
DiceUtils,
|
||||
DiceContract,
|
||||
RollValueContract,
|
||||
HelperUtils,
|
||||
} from "@dndbeyond/character-rules-engine/es";
|
||||
import {
|
||||
DiceEvent,
|
||||
Dice,
|
||||
RollRequest,
|
||||
RollType,
|
||||
RollKind,
|
||||
DiceNotation,
|
||||
} from "@dndbeyond/dice";
|
||||
|
||||
import { DiceComponentUtils, TypeScriptUtils } from "../index";
|
||||
import {
|
||||
hack__generateDiceRollResultTermLookup,
|
||||
hack__generateDiceRollResultValueExcludeTypeLookup,
|
||||
} from "./hacks";
|
||||
|
||||
/**
|
||||
* Adds an event listener that sets the state variable isCriticalHit to false when a roll is performed
|
||||
* that is not this component's to-hit roll
|
||||
*
|
||||
* Note: Move this to a hook if the components go classless
|
||||
*/
|
||||
export function setupResetCritStateOnRoll(
|
||||
rollAction: string,
|
||||
rollNode: React.ReactNode
|
||||
): (eventData: any) => void {
|
||||
let handler = _handler.bind(undefined, rollAction, rollNode);
|
||||
Dice.addEventListener(DiceEvent.ROLL, handler);
|
||||
return handler;
|
||||
}
|
||||
|
||||
export function _handler(
|
||||
rollAction: string,
|
||||
rollNode: React.PureComponent<any, any>,
|
||||
result: RollRequest
|
||||
): void {
|
||||
let shouldResetCritState =
|
||||
// A multi-roll was performed
|
||||
result.rolls.length !== 1 ||
|
||||
// A roll from another action was performed
|
||||
rollAction != result.action ||
|
||||
// This didn't have a to-hit roll, and we already know it is this same action
|
||||
result.rolls.find((r) => r.rollType != RollType.ToHit);
|
||||
|
||||
if (shouldResetCritState) {
|
||||
rollNode?.setState({ isCriticalHit: false });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for a critical hit
|
||||
*/
|
||||
export function isCriticalRoll(result: RollRequest): boolean {
|
||||
// Does this roll have a to-hit roll?
|
||||
const hitRoll = result.rolls.find((r) => r.rollType == RollType.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 RollKind.Disadvantage:
|
||||
critRoll = numCritRolls >= 2;
|
||||
break;
|
||||
case RollKind.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 = DiceNotation.parseDiceNotation(DiceUtils.renderDice(damage));
|
||||
for (let i = 0; i < dn.set.length; i++) {
|
||||
// Double the dice on each term
|
||||
dn.set[i].count *= 2;
|
||||
}
|
||||
return dn.toDiceNotationString();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @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: RollRequest
|
||||
): 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);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* https://github.com/Microsoft/TypeScript/issues/16069#issuecomment-369374214
|
||||
* Used to get around array filter function that errors because it doesn't recognize the filter
|
||||
* is removing nulls
|
||||
* @param input
|
||||
*/
|
||||
export function isNotNullOrUndefined<T extends Object>(
|
||||
input: null | undefined | T
|
||||
): input is T {
|
||||
return input !== null;
|
||||
}
|
||||
Reference in New Issue
Block a user