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,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);
|
||||
}
|
||||
Reference in New Issue
Block a user