mirror of
https://github.com/Kornstalx/5etools-mirror-2.github.io.git
synced 2025-10-28 20:45:35 -05:00
3782 lines
138 KiB
JavaScript
3782 lines
138 KiB
JavaScript
"use strict";
|
||
|
||
// PARSING =============================================================================================================
|
||
globalThis.Parser = {};
|
||
|
||
Parser._parse_aToB = function (abMap, a, fallback) {
|
||
if (a === undefined || a === null) throw new TypeError("undefined or null object passed to parser");
|
||
if (typeof a === "string") a = a.trim();
|
||
if (abMap[a] !== undefined) return abMap[a];
|
||
return fallback !== undefined ? fallback : a;
|
||
};
|
||
|
||
Parser._parse_bToA = function (abMap, b, fallback) {
|
||
if (b === undefined || b === null) throw new TypeError("undefined or null object passed to parser");
|
||
if (typeof b === "string") b = b.trim();
|
||
for (const v in abMap) {
|
||
if (!abMap.hasOwnProperty(v)) continue;
|
||
if (abMap[v] === b) return v;
|
||
}
|
||
return fallback !== undefined ? fallback : b;
|
||
};
|
||
|
||
Parser.attrChooseToFull = function (attList) {
|
||
if (attList.length === 1) return `${Parser.attAbvToFull(attList[0])} modifier`;
|
||
else {
|
||
const attsTemp = [];
|
||
for (let i = 0; i < attList.length; ++i) {
|
||
attsTemp.push(Parser.attAbvToFull(attList[i]));
|
||
}
|
||
return `${attsTemp.join(" or ")} modifier (your choice)`;
|
||
}
|
||
};
|
||
|
||
Parser.numberToText = function (number) {
|
||
if (number == null) throw new TypeError(`undefined or null object passed to parser`);
|
||
if (Math.abs(number) >= 100) return `${number}`;
|
||
|
||
return `${number < 0 ? "negative " : ""}${Parser.numberToText._getPositiveNumberAsText(Math.abs(number))}`;
|
||
};
|
||
|
||
Parser.numberToText._getPositiveNumberAsText = num => {
|
||
const [preDotRaw, postDotRaw] = `${num}`.split(".");
|
||
|
||
if (!postDotRaw) return Parser.numberToText._getPositiveIntegerAsText(num);
|
||
|
||
let preDot = preDotRaw === "0" ? "" : `${Parser.numberToText._getPositiveIntegerAsText(Math.trunc(num))} and `;
|
||
|
||
// See also: `Parser.numberToVulgar`
|
||
switch (postDotRaw) {
|
||
case "125": return `${preDot}one-eighth`;
|
||
case "2": return `${preDot}one-fifth`;
|
||
case "25": return `${preDot}one-quarter`;
|
||
case "375": return `${preDot}three-eighths`;
|
||
case "4": return `${preDot}two-fifths`;
|
||
case "5": return `${preDot}one-half`;
|
||
case "6": return `${preDot}three-fifths`;
|
||
case "625": return `${preDot}five-eighths`;
|
||
case "75": return `${preDot}three-quarters`;
|
||
case "8": return `${preDot}four-fifths`;
|
||
case "875": return `${preDot}seven-eighths`;
|
||
|
||
default: {
|
||
// Handle recursive
|
||
const asNum = Number(`0.${postDotRaw}`);
|
||
|
||
if (asNum.toFixed(2) === (1 / 3).toFixed(2)) return `${preDot}one-third`;
|
||
if (asNum.toFixed(2) === (2 / 3).toFixed(2)) return `${preDot}two-thirds`;
|
||
|
||
if (asNum.toFixed(2) === (1 / 6).toFixed(2)) return `${preDot}one-sixth`;
|
||
if (asNum.toFixed(2) === (5 / 6).toFixed(2)) return `${preDot}five-sixths`;
|
||
}
|
||
}
|
||
};
|
||
|
||
Parser.numberToText._getPositiveIntegerAsText = num => {
|
||
switch (num) {
|
||
case 0: return "zero";
|
||
case 1: return "one";
|
||
case 2: return "two";
|
||
case 3: return "three";
|
||
case 4: return "four";
|
||
case 5: return "five";
|
||
case 6: return "six";
|
||
case 7: return "seven";
|
||
case 8: return "eight";
|
||
case 9: return "nine";
|
||
case 10: return "ten";
|
||
case 11: return "eleven";
|
||
case 12: return "twelve";
|
||
case 13: return "thirteen";
|
||
case 14: return "fourteen";
|
||
case 15: return "fifteen";
|
||
case 16: return "sixteen";
|
||
case 17: return "seventeen";
|
||
case 18: return "eighteen";
|
||
case 19: return "nineteen";
|
||
case 20: return "twenty";
|
||
case 30: return "thirty";
|
||
case 40: return "forty";
|
||
case 50: return "fifty";
|
||
case 60: return "sixty";
|
||
case 70: return "seventy";
|
||
case 80: return "eighty";
|
||
case 90: return "ninety";
|
||
default: {
|
||
const str = String(num);
|
||
return `${Parser.numberToText._getPositiveIntegerAsText(Number(`${str[0]}0`))}-${Parser.numberToText._getPositiveIntegerAsText(Number(str[1]))}`;
|
||
}
|
||
}
|
||
};
|
||
|
||
Parser.textToNumber = function (str) {
|
||
str = str.trim().toLowerCase();
|
||
if (!isNaN(str)) return Number(str);
|
||
switch (str) {
|
||
case "zero": return 0;
|
||
case "one": case "a": case "an": return 1;
|
||
case "two": case "double": return 2;
|
||
case "three": case "triple": return 3;
|
||
case "four": case "quadruple": return 4;
|
||
case "five": return 5;
|
||
case "six": return 6;
|
||
case "seven": return 7;
|
||
case "eight": return 8;
|
||
case "nine": return 9;
|
||
case "ten": return 10;
|
||
case "eleven": return 11;
|
||
case "twelve": return 12;
|
||
case "thirteen": return 13;
|
||
case "fourteen": return 14;
|
||
case "fifteen": return 15;
|
||
case "sixteen": return 16;
|
||
case "seventeen": return 17;
|
||
case "eighteen": return 18;
|
||
case "nineteen": return 19;
|
||
case "twenty": return 20;
|
||
case "thirty": return 30;
|
||
case "forty": return 40;
|
||
case "fifty": return 50;
|
||
case "sixty": return 60;
|
||
case "seventy": return 70;
|
||
case "eighty": return 80;
|
||
case "ninety": return 90;
|
||
}
|
||
return NaN;
|
||
};
|
||
|
||
Parser.numberToVulgar = function (number, {isFallbackOnFractional = true} = {}) {
|
||
const isNeg = number < 0;
|
||
const spl = `${number}`.replace(/^-/, "").split(".");
|
||
if (spl.length === 1) return number;
|
||
|
||
let preDot = spl[0] === "0" ? "" : spl[0];
|
||
if (isNeg) preDot = `-${preDot}`;
|
||
|
||
// See also: `Parser.numberToText._getPositiveNumberAsText`
|
||
switch (spl[1]) {
|
||
case "125": return `${preDot}⅛`;
|
||
case "2": return `${preDot}⅕`;
|
||
case "25": return `${preDot}¼`;
|
||
case "375": return `${preDot}⅜`;
|
||
case "4": return `${preDot}⅖`;
|
||
case "5": return `${preDot}½`;
|
||
case "6": return `${preDot}⅗`;
|
||
case "625": return `${preDot}⅝`;
|
||
case "75": return `${preDot}¾`;
|
||
case "8": return `${preDot}⅘`;
|
||
case "875": return `${preDot}⅞`;
|
||
|
||
default: {
|
||
// Handle recursive
|
||
const asNum = Number(`0.${spl[1]}`);
|
||
|
||
if (asNum.toFixed(2) === (1 / 3).toFixed(2)) return `${preDot}⅓`;
|
||
if (asNum.toFixed(2) === (2 / 3).toFixed(2)) return `${preDot}⅔`;
|
||
|
||
if (asNum.toFixed(2) === (1 / 6).toFixed(2)) return `${preDot}⅙`;
|
||
if (asNum.toFixed(2) === (5 / 6).toFixed(2)) return `${preDot}⅚`;
|
||
}
|
||
}
|
||
|
||
return isFallbackOnFractional ? Parser.numberToFractional(number) : null;
|
||
};
|
||
|
||
Parser.vulgarToNumber = function (str) {
|
||
const [, leading = "0", vulgar = ""] = /^(\d+)?([⅛¼⅜½⅝¾⅞⅓⅔⅙⅚])?$/.exec(str) || [];
|
||
let out = Number(leading);
|
||
switch (vulgar) {
|
||
case "⅛": out += 0.125; break;
|
||
case "¼": out += 0.25; break;
|
||
case "⅜": out += 0.375; break;
|
||
case "½": out += 0.5; break;
|
||
case "⅝": out += 0.625; break;
|
||
case "¾": out += 0.75; break;
|
||
case "⅞": out += 0.875; break;
|
||
case "⅓": out += 1 / 3; break;
|
||
case "⅔": out += 2 / 3; break;
|
||
case "⅙": out += 1 / 6; break;
|
||
case "⅚": out += 5 / 6; break;
|
||
case "": break;
|
||
default: throw new Error(`Unhandled vulgar part "${vulgar}"`);
|
||
}
|
||
return out;
|
||
};
|
||
|
||
Parser.numberToSuperscript = function (number) {
|
||
return `${number}`.split("").map(c => isNaN(c) ? c : Parser._NUMBERS_SUPERSCRIPT[Number(c)]).join("");
|
||
};
|
||
Parser._NUMBERS_SUPERSCRIPT = "⁰¹²³⁴⁵⁶⁷⁸⁹";
|
||
|
||
Parser.numberToSubscript = function (number) {
|
||
return `${number}`.split("").map(c => isNaN(c) ? c : Parser._NUMBERS_SUBSCRIPT[Number(c)]).join("");
|
||
};
|
||
Parser._NUMBERS_SUBSCRIPT = "₀₁₂₃₄₅₆₇₈₉";
|
||
|
||
Parser._greatestCommonDivisor = function (a, b) {
|
||
if (b < Number.EPSILON) return a;
|
||
return Parser._greatestCommonDivisor(b, Math.floor(a % b));
|
||
};
|
||
Parser.numberToFractional = function (number) {
|
||
const len = number.toString().length - 2;
|
||
let denominator = 10 ** len;
|
||
let numerator = number * denominator;
|
||
const divisor = Parser._greatestCommonDivisor(numerator, denominator);
|
||
numerator = Math.floor(numerator / divisor);
|
||
denominator = Math.floor(denominator / divisor);
|
||
|
||
return denominator === 1 ? String(numerator) : `${Math.floor(numerator)}/${Math.floor(denominator)}`;
|
||
};
|
||
|
||
Parser.ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||
|
||
Parser.attAbvToFull = function (abv) {
|
||
return Parser._parse_aToB(Parser.ATB_ABV_TO_FULL, abv);
|
||
};
|
||
|
||
Parser.attFullToAbv = function (full) {
|
||
return Parser._parse_bToA(Parser.ATB_ABV_TO_FULL, full);
|
||
};
|
||
|
||
Parser.sizeAbvToFull = function (abv) {
|
||
return Parser._parse_aToB(Parser.SIZE_ABV_TO_FULL, abv);
|
||
};
|
||
|
||
Parser.getAbilityModNumber = function (abilityScore) {
|
||
return Math.floor((abilityScore - 10) / 2);
|
||
};
|
||
|
||
Parser.getAbilityModifier = function (abilityScore) {
|
||
let modifier = Parser.getAbilityModNumber(abilityScore);
|
||
if (modifier >= 0) modifier = `+${modifier}`;
|
||
return `${modifier}`;
|
||
};
|
||
|
||
Parser.getSpeedString = (ent, {isMetric = false, isSkipZeroWalk = false} = {}) => {
|
||
if (ent.speed == null) return "\u2014";
|
||
|
||
const unit = isMetric ? Parser.metric.getMetricUnit({originalUnit: "ft.", isShortForm: true}) : "ft.";
|
||
if (typeof ent.speed === "object") {
|
||
const stack = [];
|
||
let joiner = ", ";
|
||
|
||
Parser.SPEED_MODES
|
||
.filter(mode => !ent.speed.hidden?.includes(mode))
|
||
.forEach(mode => Parser._getSpeedString_addSpeedMode({ent, prop: mode, stack, isMetric, isSkipZeroWalk, unit}));
|
||
|
||
if (ent.speed.choose && !ent.speed.hidden?.includes("choose")) {
|
||
joiner = "; ";
|
||
stack.push(`${ent.speed.choose.from.sort().joinConjunct(", ", " or ")} ${ent.speed.choose.amount} ${unit}${ent.speed.choose.note ? ` ${ent.speed.choose.note}` : ""}`);
|
||
}
|
||
|
||
return stack.join(joiner) + (ent.speed.note ? ` ${ent.speed.note}` : "");
|
||
}
|
||
|
||
return (isMetric ? Parser.metric.getMetricNumber({originalValue: ent.speed, originalUnit: Parser.UNT_FEET}) : ent.speed)
|
||
+ (ent.speed === "Varies" ? "" : ` ${unit} `);
|
||
};
|
||
Parser._getSpeedString_addSpeedMode = ({ent, prop, stack, isMetric, isSkipZeroWalk, unit}) => {
|
||
if (ent.speed[prop] || (!isSkipZeroWalk && prop === "walk")) Parser._getSpeedString_addSpeed({prop, speed: ent.speed[prop] || 0, isMetric, unit, stack});
|
||
if (ent.speed.alternate && ent.speed.alternate[prop]) ent.speed.alternate[prop].forEach(speed => Parser._getSpeedString_addSpeed({prop, speed, isMetric, unit, stack}));
|
||
};
|
||
Parser._getSpeedString_addSpeed = ({prop, speed, isMetric, unit, stack}) => {
|
||
const ptName = prop === "walk" ? "" : `${prop} `;
|
||
const ptValue = Parser._getSpeedString_getVal({prop, speed, isMetric});
|
||
const ptUnit = speed === true ? "" : ` ${unit}`;
|
||
const ptCondition = Parser._getSpeedString_getCondition({speed});
|
||
stack.push([ptName, ptValue, ptUnit, ptCondition].join(""));
|
||
};
|
||
Parser._getSpeedString_getVal = ({prop, speed, isMetric}) => {
|
||
if (speed === true && prop !== "walk") return "equal to your walking speed";
|
||
|
||
const num = speed === true
|
||
? 0
|
||
: speed.number != null ? speed.number : speed;
|
||
|
||
return isMetric ? Parser.metric.getMetricNumber({originalValue: num, originalUnit: Parser.UNT_FEET}) : num;
|
||
};
|
||
Parser._getSpeedString_getCondition = ({speed}) => speed.condition ? ` ${Renderer.get().render(speed.condition)}` : "";
|
||
|
||
Parser.SPEED_MODES = ["walk", "burrow", "climb", "fly", "swim"];
|
||
|
||
Parser.SPEED_TO_PROGRESSIVE = {
|
||
"walk": "walking",
|
||
"burrow": "burrowing",
|
||
"climb": "climbing",
|
||
"fly": "flying",
|
||
"swim": "swimming",
|
||
};
|
||
|
||
Parser.speedToProgressive = function (prop) {
|
||
return Parser._parse_aToB(Parser.SPEED_TO_PROGRESSIVE, prop);
|
||
};
|
||
|
||
Parser._addCommas = function (intNum) {
|
||
return `${intNum}`.replace(/(\d)(?=(\d{3})+$)/g, "$1,");
|
||
};
|
||
|
||
Parser.raceCreatureTypesToFull = function (creatureTypes) {
|
||
const hasSubOptions = creatureTypes.some(it => it.choose);
|
||
return creatureTypes
|
||
.map(it => {
|
||
if (!it.choose) return Parser.monTypeToFullObj(it).asText;
|
||
return [...it.choose]
|
||
.sort(SortUtil.ascSortLower)
|
||
.map(sub => Parser.monTypeToFullObj(sub).asText)
|
||
.joinConjunct(", ", " or ");
|
||
})
|
||
.joinConjunct(hasSubOptions ? "; " : ", ", " and ");
|
||
};
|
||
|
||
Parser.crToXp = function (cr, {isDouble = false} = {}) {
|
||
if (cr != null && cr.xp) return Parser._addCommas(`${isDouble ? cr.xp * 2 : cr.xp}`);
|
||
|
||
const toConvert = cr ? (cr.cr || cr) : null;
|
||
if (toConvert === "Unknown" || toConvert == null || !Parser.XP_CHART_ALT[toConvert]) return "Unknown";
|
||
// CR 0 creatures can be 0 or 10 XP, but 10 XP is used in almost every case.
|
||
// Exceptions, such as MM's Frog and Sea Horse, have their XP set to 0 on the creature
|
||
if (toConvert === "0") return "10";
|
||
const xp = Parser.XP_CHART_ALT[toConvert];
|
||
return Parser._addCommas(`${isDouble ? 2 * xp : xp}`);
|
||
};
|
||
|
||
Parser.crToXpNumber = function (cr) {
|
||
if (cr != null && cr.xp) return cr.xp;
|
||
const toConvert = cr ? (cr.cr || cr) : cr;
|
||
if (toConvert === "Unknown" || toConvert == null) return null;
|
||
return Parser.XP_CHART_ALT[toConvert] ?? null;
|
||
};
|
||
|
||
Parser.LEVEL_TO_XP_EASY = [0, 25, 50, 75, 125, 250, 300, 350, 450, 550, 600, 800, 1000, 1100, 1250, 1400, 1600, 2000, 2100, 2400, 2800];
|
||
Parser.LEVEL_TO_XP_MEDIUM = [0, 50, 100, 150, 250, 500, 600, 750, 900, 1100, 1200, 1600, 2000, 2200, 2500, 2800, 3200, 3900, 4100, 4900, 5700];
|
||
Parser.LEVEL_TO_XP_HARD = [0, 75, 150, 225, 375, 750, 900, 1100, 1400, 1600, 1900, 2400, 3000, 3400, 3800, 4300, 4800, 5900, 6300, 7300, 8500];
|
||
Parser.LEVEL_TO_XP_DEADLY = [0, 100, 200, 400, 500, 1100, 1400, 1700, 2100, 2400, 2800, 3600, 4500, 5100, 5700, 6400, 7200, 8800, 9500, 10900, 12700];
|
||
Parser.LEVEL_TO_XP_DAILY = [0, 300, 600, 1200, 1700, 3500, 4000, 5000, 6000, 7500, 9000, 10500, 11500, 13500, 15000, 18000, 20000, 25000, 27000, 30000, 40000];
|
||
|
||
Parser.LEVEL_XP_REQUIRED = [0, 300, 900, 2700, 6500, 14000, 23000, 34000, 48000, 64000, 85000, 100000, 120000, 140000, 165000, 195000, 225000, 265000, 305000, 355000];
|
||
|
||
Parser.CRS = ["0", "1/8", "1/4", "1/2", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30"];
|
||
|
||
Parser.levelToXpThreshold = function (level) {
|
||
return [Parser.LEVEL_TO_XP_EASY[level], Parser.LEVEL_TO_XP_MEDIUM[level], Parser.LEVEL_TO_XP_HARD[level], Parser.LEVEL_TO_XP_DEADLY[level]];
|
||
};
|
||
|
||
Parser.isValidCr = function (cr) {
|
||
return Parser.CRS.includes(cr);
|
||
};
|
||
|
||
Parser.crToNumber = function (cr, opts = {}) {
|
||
const {isDefaultNull = false} = opts;
|
||
|
||
if (cr === "Unknown" || cr === "\u2014" || cr == null) return isDefaultNull ? null : VeCt.CR_UNKNOWN;
|
||
if (cr.cr) return Parser.crToNumber(cr.cr, opts);
|
||
|
||
const parts = cr.trim().split("/");
|
||
if (!parts.length || parts.length >= 3) return isDefaultNull ? null : VeCt.CR_CUSTOM;
|
||
if (isNaN(parts[0])) return isDefaultNull ? null : VeCt.CR_CUSTOM;
|
||
|
||
if (parts.length === 2) {
|
||
if (isNaN(Number(parts[1]))) return isDefaultNull ? null : VeCt.CR_CUSTOM;
|
||
return Number(parts[0]) / Number(parts[1]);
|
||
}
|
||
|
||
return Number(parts[0]);
|
||
};
|
||
|
||
Parser.numberToCr = function (number, safe) {
|
||
// avoid dying if already-converted number is passed in
|
||
if (safe && typeof number === "string" && Parser.CRS.includes(number)) return number;
|
||
|
||
if (number == null) return "Unknown";
|
||
|
||
return Parser.numberToFractional(number);
|
||
};
|
||
|
||
Parser.crToPb = function (cr) {
|
||
if (cr === "Unknown" || cr == null) return 0;
|
||
cr = cr.cr || cr;
|
||
if (Parser.crToNumber(cr) < 5) return 2;
|
||
return Math.ceil(cr / 4) + 1;
|
||
};
|
||
|
||
Parser.levelToPb = function (level) {
|
||
if (!level) return 2;
|
||
return Math.ceil(level / 4) + 1;
|
||
};
|
||
|
||
Parser.SKILL_TO_ATB_ABV = {
|
||
"athletics": "str",
|
||
"acrobatics": "dex",
|
||
"sleight of hand": "dex",
|
||
"stealth": "dex",
|
||
"arcana": "int",
|
||
"history": "int",
|
||
"investigation": "int",
|
||
"nature": "int",
|
||
"religion": "int",
|
||
"animal handling": "wis",
|
||
"insight": "wis",
|
||
"medicine": "wis",
|
||
"perception": "wis",
|
||
"survival": "wis",
|
||
"deception": "cha",
|
||
"intimidation": "cha",
|
||
"performance": "cha",
|
||
"persuasion": "cha",
|
||
};
|
||
|
||
Parser.skillToAbilityAbv = function (skill) {
|
||
return Parser._parse_aToB(Parser.SKILL_TO_ATB_ABV, skill);
|
||
};
|
||
|
||
Parser.SKILL_TO_SHORT = {
|
||
"athletics": "ath",
|
||
"acrobatics": "acro",
|
||
"sleight of hand": "soh",
|
||
"stealth": "slth",
|
||
"arcana": "arc",
|
||
"history": "hist",
|
||
"investigation": "invn",
|
||
"nature": "natr",
|
||
"religion": "reli",
|
||
"animal handling": "hndl",
|
||
"insight": "ins",
|
||
"medicine": "med",
|
||
"perception": "perp",
|
||
"survival": "surv",
|
||
"deception": "decp",
|
||
"intimidation": "intm",
|
||
"performance": "perf",
|
||
"persuasion": "pers",
|
||
};
|
||
|
||
Parser.skillToShort = function (skill) {
|
||
return Parser._parse_aToB(Parser.SKILL_TO_SHORT, skill);
|
||
};
|
||
|
||
Parser.LANGUAGES_STANDARD = [
|
||
"Common",
|
||
"Dwarvish",
|
||
"Elvish",
|
||
"Giant",
|
||
"Gnomish",
|
||
"Goblin",
|
||
"Halfling",
|
||
"Orc",
|
||
];
|
||
|
||
Parser.LANGUAGES_EXOTIC = [
|
||
"Abyssal",
|
||
"Aquan",
|
||
"Auran",
|
||
"Celestial",
|
||
"Draconic",
|
||
"Deep Speech",
|
||
"Ignan",
|
||
"Infernal",
|
||
"Primordial",
|
||
"Sylvan",
|
||
"Terran",
|
||
"Undercommon",
|
||
];
|
||
|
||
Parser.LANGUAGES_SECRET = [
|
||
"Druidic",
|
||
"Thieves' cant",
|
||
];
|
||
|
||
Parser.LANGUAGES_ALL = [
|
||
...Parser.LANGUAGES_STANDARD,
|
||
...Parser.LANGUAGES_EXOTIC,
|
||
...Parser.LANGUAGES_SECRET,
|
||
].sort();
|
||
|
||
Parser.acToFull = function (ac, renderer) {
|
||
if (typeof ac === "string") return ac; // handle classic format
|
||
|
||
renderer = renderer || Renderer.get();
|
||
|
||
let stack = "";
|
||
let inBraces = false;
|
||
for (let i = 0; i < ac.length; ++i) {
|
||
const cur = ac[i];
|
||
const nxt = ac[i + 1];
|
||
|
||
if (cur.special != null) {
|
||
if (inBraces) inBraces = false;
|
||
|
||
stack += cur.special;
|
||
} else if (cur.ac) {
|
||
const isNxtBraces = nxt && nxt.braces;
|
||
|
||
if (!inBraces && cur.braces) {
|
||
stack += "(";
|
||
inBraces = true;
|
||
}
|
||
|
||
stack += cur.ac;
|
||
|
||
if (cur.from) {
|
||
// always brace nested braces
|
||
if (cur.braces) {
|
||
stack += " (";
|
||
} else {
|
||
stack += inBraces ? "; " : " (";
|
||
}
|
||
|
||
inBraces = true;
|
||
|
||
stack += cur.from.map(it => renderer.render(it)).join(", ");
|
||
|
||
if (cur.braces) {
|
||
stack += ")";
|
||
} else if (!isNxtBraces) {
|
||
stack += ")";
|
||
inBraces = false;
|
||
}
|
||
}
|
||
|
||
if (cur.condition) stack += ` ${renderer.render(cur.condition)}`;
|
||
|
||
if (inBraces && !isNxtBraces) {
|
||
stack += ")";
|
||
inBraces = false;
|
||
}
|
||
} else {
|
||
stack += cur;
|
||
}
|
||
|
||
if (nxt) {
|
||
if (nxt.braces) {
|
||
stack += inBraces ? "; " : " (";
|
||
inBraces = true;
|
||
} else stack += ", ";
|
||
}
|
||
}
|
||
if (inBraces) stack += ")";
|
||
|
||
return stack.trim();
|
||
};
|
||
|
||
Parser.MONSTER_COUNT_TO_XP_MULTIPLIER = [1, 1.5, 2, 2, 2, 2, 2.5, 2.5, 2.5, 2.5, 3, 3, 3, 3, 4];
|
||
Parser.numMonstersToXpMult = function (num, playerCount = 3) {
|
||
const baseVal = (() => {
|
||
if (num >= Parser.MONSTER_COUNT_TO_XP_MULTIPLIER.length) return 4;
|
||
return Parser.MONSTER_COUNT_TO_XP_MULTIPLIER[num - 1];
|
||
})();
|
||
|
||
if (playerCount < 3) return baseVal >= 3 ? baseVal + 1 : baseVal + 0.5;
|
||
else if (playerCount > 5) {
|
||
return baseVal === 4 ? 3 : baseVal - 0.5;
|
||
} else return baseVal;
|
||
};
|
||
|
||
Parser.armorFullToAbv = function (armor) {
|
||
return Parser._parse_bToA(Parser.ARMOR_ABV_TO_FULL, armor);
|
||
};
|
||
|
||
Parser.weaponFullToAbv = function (weapon) {
|
||
return Parser._parse_bToA(Parser.WEAPON_ABV_TO_FULL, weapon);
|
||
};
|
||
|
||
Parser._getSourceStringFromSource = function (source) {
|
||
if (source && source.source) return source.source;
|
||
return source;
|
||
};
|
||
Parser._buildSourceCache = function (dict) {
|
||
const out = {};
|
||
Object.entries(dict).forEach(([k, v]) => out[k.toLowerCase()] = v);
|
||
return out;
|
||
};
|
||
Parser._sourceJsonCache = null;
|
||
Parser.hasSourceJson = function (source) {
|
||
Parser._sourceJsonCache = Parser._sourceJsonCache || Parser._buildSourceCache(Object.keys(Parser.SOURCE_JSON_TO_FULL).mergeMap(k => ({[k]: k})));
|
||
return !!Parser._sourceJsonCache[source.toLowerCase()];
|
||
};
|
||
Parser._sourceFullCache = null;
|
||
Parser.hasSourceFull = function (source) {
|
||
Parser._sourceFullCache = Parser._sourceFullCache || Parser._buildSourceCache(Parser.SOURCE_JSON_TO_FULL);
|
||
return !!Parser._sourceFullCache[source.toLowerCase()];
|
||
};
|
||
Parser._sourceAbvCache = null;
|
||
Parser.hasSourceAbv = function (source) {
|
||
Parser._sourceAbvCache = Parser._sourceAbvCache || Parser._buildSourceCache(Parser.SOURCE_JSON_TO_ABV);
|
||
return !!Parser._sourceAbvCache[source.toLowerCase()];
|
||
};
|
||
Parser._sourceDateCache = null;
|
||
Parser.hasSourceDate = function (source) {
|
||
Parser._sourceDateCache = Parser._sourceDateCache || Parser._buildSourceCache(Parser.SOURCE_JSON_TO_DATE);
|
||
return !!Parser._sourceDateCache[source.toLowerCase()];
|
||
};
|
||
Parser.sourceJsonToJson = function (source) {
|
||
source = Parser._getSourceStringFromSource(source);
|
||
if (Parser.hasSourceJson(source)) return Parser._sourceJsonCache[source.toLowerCase()];
|
||
if (typeof PrereleaseUtil !== "undefined" && PrereleaseUtil.hasSourceJson(source)) return PrereleaseUtil.sourceJsonToSource(source).json;
|
||
if (typeof BrewUtil2 !== "undefined" && BrewUtil2.hasSourceJson(source)) return BrewUtil2.sourceJsonToSource(source).json;
|
||
return source;
|
||
};
|
||
Parser.sourceJsonToFull = function (source) {
|
||
source = Parser._getSourceStringFromSource(source);
|
||
if (Parser.hasSourceFull(source)) return Parser._sourceFullCache[source.toLowerCase()].replace(/'/g, "\u2019");
|
||
if (typeof PrereleaseUtil !== "undefined" && PrereleaseUtil.hasSourceJson(source)) return PrereleaseUtil.sourceJsonToFull(source).replace(/'/g, "\u2019");
|
||
if (typeof BrewUtil2 !== "undefined" && BrewUtil2.hasSourceJson(source)) return BrewUtil2.sourceJsonToFull(source).replace(/'/g, "\u2019");
|
||
return Parser._parse_aToB(Parser.SOURCE_JSON_TO_FULL, source).replace(/'/g, "\u2019");
|
||
};
|
||
Parser.sourceJsonToFullCompactPrefix = function (source) {
|
||
return Parser.sourceJsonToFull(source)
|
||
.replace(Parser.UA_PREFIX, Parser.UA_PREFIX_SHORT)
|
||
.replace(/^Unearthed Arcana (\d+): /, "UA$1: ")
|
||
.replace(Parser.AL_PREFIX, Parser.AL_PREFIX_SHORT)
|
||
.replace(Parser.PS_PREFIX, Parser.PS_PREFIX_SHORT);
|
||
};
|
||
Parser.sourceJsonToAbv = function (source) {
|
||
source = Parser._getSourceStringFromSource(source);
|
||
if (Parser.hasSourceAbv(source)) return Parser._sourceAbvCache[source.toLowerCase()];
|
||
if (typeof PrereleaseUtil !== "undefined" && PrereleaseUtil.hasSourceJson(source)) return PrereleaseUtil.sourceJsonToAbv(source);
|
||
if (typeof BrewUtil2 !== "undefined" && BrewUtil2.hasSourceJson(source)) return BrewUtil2.sourceJsonToAbv(source);
|
||
return Parser._parse_aToB(Parser.SOURCE_JSON_TO_ABV, source);
|
||
};
|
||
Parser.sourceJsonToDate = function (source) {
|
||
source = Parser._getSourceStringFromSource(source);
|
||
if (Parser.hasSourceDate(source)) return Parser._sourceDateCache[source.toLowerCase()];
|
||
if (typeof PrereleaseUtil !== "undefined" && PrereleaseUtil.hasSourceJson(source)) return PrereleaseUtil.sourceJsonToDate(source);
|
||
if (typeof BrewUtil2 !== "undefined" && BrewUtil2.hasSourceJson(source)) return BrewUtil2.sourceJsonToDate(source);
|
||
return Parser._parse_aToB(Parser.SOURCE_JSON_TO_DATE, source, null);
|
||
};
|
||
Parser.sourceJsonToColor = function (source) {
|
||
source = Parser._getSourceStringFromSource(source);
|
||
if (Parser.hasSourceAbv(source)) return "";
|
||
if (typeof PrereleaseUtil !== "undefined" && PrereleaseUtil.hasSourceJson(source)) return PrereleaseUtil.sourceJsonToColor(source);
|
||
if (typeof BrewUtil2 !== "undefined" && BrewUtil2.hasSourceJson(source)) return BrewUtil2.sourceJsonToColor(source);
|
||
return "";
|
||
};
|
||
|
||
Parser.sourceJsonToSourceClassname = function (source) {
|
||
const sourceCased = Parser.sourceJsonToJson(source);
|
||
return `source__${sourceCased}`;
|
||
};
|
||
|
||
Parser.sourceJsonToStyle = function (source) {
|
||
source = Parser._getSourceStringFromSource(source);
|
||
if (Parser.hasSourceJson(source)) return "";
|
||
if (typeof PrereleaseUtil !== "undefined" && PrereleaseUtil.hasSourceJson(source)) return PrereleaseUtil.sourceJsonToStyle(source);
|
||
if (typeof BrewUtil2 !== "undefined" && BrewUtil2.hasSourceJson(source)) return BrewUtil2.sourceJsonToStyle(source);
|
||
return "";
|
||
};
|
||
|
||
Parser.sourceJsonToStylePart = function (source) {
|
||
source = Parser._getSourceStringFromSource(source);
|
||
if (Parser.hasSourceJson(source)) return "";
|
||
if (typeof PrereleaseUtil !== "undefined" && PrereleaseUtil.hasSourceJson(source)) return PrereleaseUtil.sourceJsonToStylePart(source);
|
||
if (typeof BrewUtil2 !== "undefined" && BrewUtil2.hasSourceJson(source)) return BrewUtil2.sourceJsonToStylePart(source);
|
||
return "";
|
||
};
|
||
|
||
Parser.sourceJsonToMarkerHtml = function (source, {isList = true, additionalStyles = ""} = {}) {
|
||
source = Parser._getSourceStringFromSource(source);
|
||
// TODO(Future) consider enabling this
|
||
// if (SourceUtil.isPartneredSourceWotc(source)) return `<span class="help-subtle ve-source-marker ${isList ? `ve-source-marker--list` : ""} ve-source-marker--partnered ${additionalStyles}" title="D&D Partnered Source">${isList ? "" : "["}✦${isList ? "" : "]"}</span>`;
|
||
if (SourceUtil.isLegacySourceWotc(source)) return `<span class="help-subtle ve-source-marker ${isList ? `ve-source-marker--list` : ""} ve-source-marker--legacy ${additionalStyles}" title="Legacy Source">${isList ? "" : "["}ʟ${isList ? "" : "]"}</span>`;
|
||
return "";
|
||
};
|
||
|
||
Parser.stringToSlug = function (str) {
|
||
return str.trim().toLowerCase().toAscii().replace(/[^\w ]+/g, "").replace(/ +/g, "-");
|
||
};
|
||
|
||
Parser.stringToCasedSlug = function (str) {
|
||
return str.toAscii().replace(/[^\w ]+/g, "").replace(/ +/g, "-");
|
||
};
|
||
|
||
Parser.ITEM_SPELLCASTING_FOCUS_CLASSES = ["Artificer", "Bard", "Cleric", "Druid", "Paladin", "Ranger", "Sorcerer", "Warlock", "Wizard"];
|
||
|
||
Parser.itemValueToFull = function (item, opts = {isShortForm: false, isSmallUnits: false}) {
|
||
return Parser._moneyToFull(item, "value", "valueMult", opts);
|
||
};
|
||
|
||
Parser.itemValueToFullMultiCurrency = function (item, opts = {isShortForm: false, isSmallUnits: false}) {
|
||
return Parser._moneyToFullMultiCurrency(item, "value", "valueMult", opts);
|
||
};
|
||
|
||
Parser.itemVehicleCostsToFull = function (item, isShortForm) {
|
||
return {
|
||
travelCostFull: Parser._moneyToFull(item, "travelCost", "travelCostMult", {isShortForm}),
|
||
shippingCostFull: Parser._moneyToFull(item, "shippingCost", "shippingCostMult", {isShortForm}),
|
||
};
|
||
};
|
||
|
||
Parser.spellComponentCostToFull = function (item, isShortForm) {
|
||
return Parser._moneyToFull(item, "cost", "costMult", {isShortForm});
|
||
};
|
||
|
||
Parser.vehicleCostToFull = function (item, isShortForm) {
|
||
return Parser._moneyToFull(item, "cost", "costMult", {isShortForm});
|
||
};
|
||
|
||
Parser._moneyToFull = function (it, prop, propMult, opts = {isShortForm: false, isSmallUnits: false}) {
|
||
if (it[prop] == null && it[propMult] == null) return "";
|
||
if (it[prop] != null) {
|
||
const {coin, mult} = Parser.getCurrencyAndMultiplier(it[prop], it.currencyConversion);
|
||
return `${(it[prop] * mult).toLocaleString(undefined, {maximumFractionDigits: 5})}${opts.isSmallUnits ? `<span class="small ml-1">${coin}</span>` : ` ${coin}`}`;
|
||
} else if (it[propMult] != null) return opts.isShortForm ? `×${it[propMult]}` : `base value ×${it[propMult]}`;
|
||
return "";
|
||
};
|
||
|
||
Parser._moneyToFullMultiCurrency = function (it, prop, propMult, {isShortForm, multiplier} = {}) {
|
||
if (it[prop]) {
|
||
const conversionTable = Parser.getCurrencyConversionTable(it.currencyConversion);
|
||
|
||
const simplified = it.currencyConversion
|
||
? CurrencyUtil.doSimplifyCoins(
|
||
{
|
||
// Assume the e.g. item's value is in the lowest available denomination
|
||
[conversionTable[0]?.coin || "cp"]: it[prop] * (multiplier ?? conversionTable[0]?.mult ?? 1),
|
||
},
|
||
{
|
||
currencyConversionId: it.currencyConversion,
|
||
},
|
||
)
|
||
: CurrencyUtil.doSimplifyCoins({
|
||
cp: it[prop] * (multiplier ?? 1),
|
||
});
|
||
|
||
return [...conversionTable]
|
||
.reverse()
|
||
.filter(meta => simplified[meta.coin])
|
||
.map(meta => `${simplified[meta.coin].toLocaleString(undefined, {maximumFractionDigits: 5})} ${meta.coin}`)
|
||
.join(", ");
|
||
}
|
||
|
||
if (it[propMult]) return isShortForm ? `×${it[propMult]}` : `base value ×${it[propMult]}`;
|
||
|
||
return "";
|
||
};
|
||
|
||
Parser.DEFAULT_CURRENCY_CONVERSION_TABLE = [
|
||
{
|
||
coin: "cp",
|
||
mult: 1,
|
||
},
|
||
{
|
||
coin: "sp",
|
||
mult: 0.1,
|
||
},
|
||
{
|
||
coin: "gp",
|
||
mult: 0.01,
|
||
isFallback: true,
|
||
},
|
||
];
|
||
Parser.FULL_CURRENCY_CONVERSION_TABLE = [
|
||
{
|
||
coin: "cp",
|
||
mult: 1,
|
||
},
|
||
{
|
||
coin: "sp",
|
||
mult: 0.1,
|
||
},
|
||
{
|
||
coin: "ep",
|
||
mult: 0.02,
|
||
},
|
||
{
|
||
coin: "gp",
|
||
mult: 0.01,
|
||
isFallback: true,
|
||
},
|
||
{
|
||
coin: "pp",
|
||
mult: 0.001,
|
||
},
|
||
];
|
||
Parser.getCurrencyConversionTable = function (currencyConversionId) {
|
||
const fromPrerelease = currencyConversionId ? PrereleaseUtil.getMetaLookup("currencyConversions")?.[currencyConversionId] : null;
|
||
const fromBrew = currencyConversionId ? BrewUtil2.getMetaLookup("currencyConversions")?.[currencyConversionId] : null;
|
||
const conversionTable = fromPrerelease?.length
|
||
? fromPrerelease
|
||
: fromBrew?.length
|
||
? fromBrew
|
||
: Parser.DEFAULT_CURRENCY_CONVERSION_TABLE;
|
||
if (conversionTable !== Parser.DEFAULT_CURRENCY_CONVERSION_TABLE) conversionTable.sort((a, b) => SortUtil.ascSort(b.mult, a.mult));
|
||
return conversionTable;
|
||
};
|
||
Parser.getCurrencyAndMultiplier = function (value, currencyConversionId) {
|
||
const conversionTable = Parser.getCurrencyConversionTable(currencyConversionId);
|
||
|
||
if (!value) return conversionTable.find(it => it.isFallback) || conversionTable[0];
|
||
if (conversionTable.length === 1) return conversionTable[0];
|
||
if (!Number.isInteger(value) && value < conversionTable[0].mult) return conversionTable[0];
|
||
|
||
for (let i = conversionTable.length - 1; i >= 0; --i) {
|
||
if (Number.isInteger(value * conversionTable[i].mult)) return conversionTable[i];
|
||
}
|
||
|
||
return conversionTable.last();
|
||
};
|
||
|
||
Parser.COIN_ABVS = ["cp", "sp", "ep", "gp", "pp"];
|
||
Parser.COIN_ABV_TO_FULL = {
|
||
"cp": "copper pieces",
|
||
"sp": "silver pieces",
|
||
"ep": "electrum pieces",
|
||
"gp": "gold pieces",
|
||
"pp": "platinum pieces",
|
||
};
|
||
Parser.COIN_CONVERSIONS = [1, 10, 50, 100, 1000];
|
||
|
||
Parser.coinAbvToFull = function (coin) {
|
||
return Parser._parse_aToB(Parser.COIN_ABV_TO_FULL, coin);
|
||
};
|
||
|
||
/**
|
||
* @param currency Object of the form `{pp: <n>, gp: <m>, ... }`.
|
||
* @param isDisplayEmpty If "empty" values (i.e., those which are 0) should be displayed.
|
||
*/
|
||
Parser.getDisplayCurrency = function (currency, {isDisplayEmpty = false} = {}) {
|
||
return [...Parser.COIN_ABVS]
|
||
.reverse()
|
||
.filter(abv => isDisplayEmpty ? currency[abv] != null : currency[abv])
|
||
.map(abv => `${currency[abv].toLocaleString()} ${abv}`)
|
||
.join(", ");
|
||
};
|
||
|
||
Parser.itemWeightToFull = function (item, isShortForm) {
|
||
if (item.weight) {
|
||
// Handle pure integers
|
||
if (Math.round(item.weight) === item.weight) return `${item.weight} lb.${(item.weightNote ? ` ${item.weightNote}` : "")}`;
|
||
|
||
const integerPart = Math.floor(item.weight);
|
||
|
||
// Attempt to render the amount as (a number +) a vulgar
|
||
const vulgarGlyph = Parser.numberToVulgar(item.weight - integerPart, {isFallbackOnFractional: false});
|
||
if (vulgarGlyph) return `${integerPart || ""}${vulgarGlyph} lb.${(item.weightNote ? ` ${item.weightNote}` : "")}`;
|
||
|
||
// Fall back on decimal pounds or ounces
|
||
return `${(item.weight < 1 ? item.weight * 16 : item.weight).toLocaleString(undefined, {maximumFractionDigits: 5})} ${item.weight < 1 ? "oz" : "lb"}.${(item.weightNote ? ` ${item.weightNote}` : "")}`;
|
||
}
|
||
if (item.weightMult) return isShortForm ? `×${item.weightMult}` : `base weight ×${item.weightMult}`;
|
||
return "";
|
||
};
|
||
|
||
Parser.ITEM_RECHARGE_TO_FULL = {
|
||
round: "Every Round",
|
||
restShort: "Short Rest",
|
||
restLong: "Long Rest",
|
||
dawn: "Dawn",
|
||
dusk: "Dusk",
|
||
midnight: "Midnight",
|
||
week: "Week",
|
||
month: "Month",
|
||
year: "Year",
|
||
decade: "Decade",
|
||
century: "Century",
|
||
special: "Special",
|
||
};
|
||
Parser.itemRechargeToFull = function (recharge) {
|
||
return Parser._parse_aToB(Parser.ITEM_RECHARGE_TO_FULL, recharge);
|
||
};
|
||
|
||
Parser.ITEM_MISC_TAG_TO_FULL = {
|
||
"CF/W": "Creates Food/Water",
|
||
"CNS": "Consumable",
|
||
"TT": "Trinket Table",
|
||
};
|
||
Parser.itemMiscTagToFull = function (type) {
|
||
return Parser._parse_aToB(Parser.ITEM_MISC_TAG_TO_FULL, type);
|
||
};
|
||
|
||
Parser._decimalSeparator = (0.1).toLocaleString().substring(1, 2);
|
||
Parser._numberCleanRegexp = Parser._decimalSeparator === "." ? new RegExp(/[\s,]*/g, "g") : new RegExp(/[\s.]*/g, "g");
|
||
Parser._costSplitRegexp = Parser._decimalSeparator === "." ? new RegExp(/(\d+(\.\d+)?)([csegp]p)/) : new RegExp(/(\d+(,\d+)?)([csegp]p)/);
|
||
|
||
/** input e.g. "25 gp", "1,000pp" */
|
||
Parser.coinValueToNumber = function (value) {
|
||
if (!value) return 0;
|
||
// handle oddities
|
||
if (value === "Varies") return 0;
|
||
|
||
value = value
|
||
.replace(/\s*/, "")
|
||
.replace(Parser._numberCleanRegexp, "")
|
||
.toLowerCase();
|
||
const m = Parser._costSplitRegexp.exec(value);
|
||
if (!m) throw new Error(`Badly formatted value "${value}"`);
|
||
const ixCoin = Parser.COIN_ABVS.indexOf(m[3]);
|
||
if (!~ixCoin) throw new Error(`Unknown coin type "${m[3]}"`);
|
||
return Number(m[1]) * Parser.COIN_CONVERSIONS[ixCoin];
|
||
};
|
||
|
||
Parser.weightValueToNumber = function (value) {
|
||
if (!value) return 0;
|
||
|
||
if (Number(value)) return Number(value);
|
||
else throw new Error(`Badly formatted value ${value}`);
|
||
};
|
||
|
||
Parser.dmgTypeToFull = function (dmgType) {
|
||
return Parser._parse_aToB(Parser.DMGTYPE_JSON_TO_FULL, dmgType);
|
||
};
|
||
|
||
Parser.skillProficienciesToFull = function (skillProficiencies) {
|
||
function renderSingle (skProf) {
|
||
if (skProf.any) {
|
||
skProf = MiscUtil.copyFast(skProf);
|
||
skProf.choose = {"from": Object.keys(Parser.SKILL_TO_ATB_ABV), "count": skProf.any};
|
||
delete skProf.any;
|
||
}
|
||
|
||
const keys = Object.keys(skProf).sort(SortUtil.ascSortLower);
|
||
|
||
const ixChoose = keys.indexOf("choose");
|
||
if (~ixChoose) keys.splice(ixChoose, 1);
|
||
|
||
const baseStack = [];
|
||
keys.filter(k => skProf[k]).forEach(k => baseStack.push(Renderer.get().render(`{@skill ${k.toTitleCase()}}`)));
|
||
|
||
const chooseStack = [];
|
||
if (~ixChoose) {
|
||
const chObj = skProf.choose;
|
||
if (chObj.from.length === 18) {
|
||
chooseStack.push(`choose any ${!chObj.count || chObj.count === 1 ? "skill" : chObj.count}`);
|
||
} else {
|
||
chooseStack.push(`choose ${chObj.count || 1} from ${chObj.from.map(it => Renderer.get().render(`{@skill ${it.toTitleCase()}}`)).joinConjunct(", ", " and ")}`);
|
||
}
|
||
}
|
||
|
||
const base = baseStack.joinConjunct(", ", " and ");
|
||
const choose = chooseStack.join(""); // this should currently only ever be 1-length
|
||
|
||
if (baseStack.length && chooseStack.length) return `${base}; and ${choose}`;
|
||
else if (baseStack.length) return base;
|
||
else if (chooseStack.length) return choose;
|
||
}
|
||
|
||
return skillProficiencies.map(renderSingle).join(" <i>or</i> ");
|
||
};
|
||
|
||
// sp-prefix functions are for parsing spell data, and shared with the roll20 script
|
||
Parser.spSchoolAndSubschoolsAbvsToFull = function (school, subschools) {
|
||
if (!subschools || !subschools.length) return Parser.spSchoolAbvToFull(school);
|
||
else return `${Parser.spSchoolAbvToFull(school)} (${subschools.map(sub => Parser.spSchoolAbvToFull(sub)).join(", ")})`;
|
||
};
|
||
|
||
Parser.spSchoolAbvToFull = function (schoolOrSubschool) {
|
||
const out = Parser._parse_aToB(Parser.SP_SCHOOL_ABV_TO_FULL, schoolOrSubschool);
|
||
if (Parser.SP_SCHOOL_ABV_TO_FULL[schoolOrSubschool]) return out;
|
||
if (PrereleaseUtil.getMetaLookup("spellSchools")?.[schoolOrSubschool]) return PrereleaseUtil.getMetaLookup("spellSchools")?.[schoolOrSubschool].full;
|
||
if (BrewUtil2.getMetaLookup("spellSchools")?.[schoolOrSubschool]) return BrewUtil2.getMetaLookup("spellSchools")?.[schoolOrSubschool].full;
|
||
return out;
|
||
};
|
||
|
||
Parser.spSchoolAndSubschoolsAbvsShort = function (school, subschools) {
|
||
if (!subschools || !subschools.length) return Parser.spSchoolAbvToShort(school);
|
||
else return `${Parser.spSchoolAbvToShort(school)} (${subschools.map(sub => Parser.spSchoolAbvToShort(sub)).join(", ")})`;
|
||
};
|
||
|
||
Parser.spSchoolAbvToShort = function (school) {
|
||
const out = Parser._parse_aToB(Parser.SP_SCHOOL_ABV_TO_SHORT, school);
|
||
if (Parser.SP_SCHOOL_ABV_TO_SHORT[school]) return out;
|
||
if (PrereleaseUtil.getMetaLookup("spellSchools")?.[school]) return PrereleaseUtil.getMetaLookup("spellSchools")?.[school].short;
|
||
if (BrewUtil2.getMetaLookup("spellSchools")?.[school]) return BrewUtil2.getMetaLookup("spellSchools")?.[school].short;
|
||
if (out.length <= 4) return out;
|
||
return `${out.slice(0, 3)}.`;
|
||
};
|
||
|
||
Parser.spSchoolAbvToStyle = function (school) { // For prerelease/homebrew
|
||
const stylePart = Parser.spSchoolAbvToStylePart(school);
|
||
if (!stylePart) return stylePart;
|
||
return `style="${stylePart}"`;
|
||
};
|
||
|
||
Parser.spSchoolAbvToStylePart = function (school) { // For prerelease/homebrew
|
||
return Parser._spSchoolAbvToStylePart_prereleaseBrew({school, brewUtil: PrereleaseUtil})
|
||
|| Parser._spSchoolAbvToStylePart_prereleaseBrew({school, brewUtil: BrewUtil2})
|
||
|| "";
|
||
};
|
||
|
||
Parser._spSchoolAbvToStylePart_prereleaseBrew = function ({school, brewUtil}) {
|
||
const rawColor = brewUtil.getMetaLookup("spellSchools")?.[school]?.color;
|
||
if (!rawColor || !rawColor.trim()) return "";
|
||
const validColor = BrewUtilShared.getValidColor(rawColor);
|
||
if (validColor.length) return MiscUtil.getColorStylePart(validColor);
|
||
};
|
||
|
||
Parser.getOrdinalForm = function (i) {
|
||
i = Number(i);
|
||
if (isNaN(i)) return "";
|
||
const j = i % 10; const k = i % 100;
|
||
if (j === 1 && k !== 11) return `${i}st`;
|
||
if (j === 2 && k !== 12) return `${i}nd`;
|
||
if (j === 3 && k !== 13) return `${i}rd`;
|
||
return `${i}th`;
|
||
};
|
||
|
||
Parser.spLevelToFull = function (level) {
|
||
if (level === 0) return "Cantrip";
|
||
else return Parser.getOrdinalForm(level);
|
||
};
|
||
|
||
Parser.getArticle = function (str) {
|
||
str = `${str}`;
|
||
str = str.replace(/\d+/g, (...m) => Parser.numberToText(m[0]));
|
||
return /^[aeiou]/i.test(str) ? "an" : "a";
|
||
};
|
||
|
||
Parser.spLevelToFullLevelText = function (level, {isDash = false, isPluralCantrips = true} = {}) {
|
||
return `${Parser.spLevelToFull(level)}${(level === 0 ? (isPluralCantrips ? "s" : "") : `${isDash ? "-" : " "}level`)}`;
|
||
};
|
||
|
||
Parser.spLevelToSpellPoints = function (lvl) {
|
||
lvl = Number(lvl);
|
||
if (isNaN(lvl) || lvl === 0) return 0;
|
||
return Math.ceil(1.34 * lvl);
|
||
};
|
||
|
||
Parser.spMetaToArr = function (meta) {
|
||
if (!meta) return [];
|
||
return Object.entries(meta)
|
||
.filter(([_, v]) => v)
|
||
.sort(SortUtil.ascSort)
|
||
.map(([k]) => k);
|
||
};
|
||
|
||
Parser.spMetaToFull = function (meta) {
|
||
if (!meta) return "";
|
||
const metaTags = Parser.spMetaToArr(meta);
|
||
if (metaTags.length) return ` (${metaTags.join(", ")})`;
|
||
return "";
|
||
};
|
||
|
||
Parser.spLevelSchoolMetaToFull = function (level, school, meta, subschools) {
|
||
const levelPart = level === 0 ? Parser.spLevelToFull(level).toLowerCase() : `${Parser.spLevelToFull(level)}-level`;
|
||
const levelSchoolStr = level === 0 ? `${Parser.spSchoolAbvToFull(school)} ${levelPart}` : `${levelPart} ${Parser.spSchoolAbvToFull(school).toLowerCase()}`;
|
||
|
||
const metaArr = Parser.spMetaToArr(meta);
|
||
if (metaArr.length || (subschools && subschools.length)) {
|
||
const metaAndSubschoolPart = [
|
||
(subschools || []).map(sub => Parser.spSchoolAbvToFull(sub)).join(", "),
|
||
metaArr.join(", "),
|
||
].filter(Boolean).join("; ").toLowerCase();
|
||
return `${levelSchoolStr} (${metaAndSubschoolPart})`;
|
||
}
|
||
return levelSchoolStr;
|
||
};
|
||
|
||
Parser.spTimeListToFull = function (times, isStripTags) {
|
||
return times.map(t => `${Parser.getTimeToFull(t)}${t.condition ? `, ${isStripTags ? Renderer.stripTags(t.condition) : Renderer.get().render(t.condition)}` : ""}`).join(" or ");
|
||
};
|
||
|
||
Parser.getTimeToFull = function (time) {
|
||
return `${time.number ? `${time.number} ` : ""}${time.unit === "bonus" ? "bonus action" : time.unit}${time.number > 1 ? "s" : ""}`;
|
||
};
|
||
|
||
Parser.getMinutesToFull = function (mins, {isShort = false} = {}) {
|
||
const days = Math.floor(mins / (24 * 60));
|
||
mins = mins % (24 * 60);
|
||
|
||
const hours = Math.floor(mins / 60);
|
||
mins = mins % 60;
|
||
|
||
return [
|
||
days ? `${days} ${isShort ? `d` : `day${days > 1 ? "s" : ""}`}` : null,
|
||
hours ? `${hours} ${isShort ? `h` : `hour${hours > 1 ? "s" : ""}`}` : null,
|
||
mins ? `${mins} ${isShort ? `m` : `minute${mins > 1 ? "s" : ""}`}` : null,
|
||
].filter(Boolean)
|
||
.join(" ");
|
||
};
|
||
|
||
Parser.RNG_SPECIAL = "special";
|
||
Parser.RNG_POINT = "point";
|
||
Parser.RNG_LINE = "line";
|
||
Parser.RNG_CUBE = "cube";
|
||
Parser.RNG_CONE = "cone";
|
||
Parser.RNG_RADIUS = "radius";
|
||
Parser.RNG_SPHERE = "sphere";
|
||
Parser.RNG_HEMISPHERE = "hemisphere";
|
||
Parser.RNG_CYLINDER = "cylinder"; // homebrew only
|
||
Parser.RNG_SELF = "self";
|
||
Parser.RNG_SIGHT = "sight";
|
||
Parser.RNG_UNLIMITED = "unlimited";
|
||
Parser.RNG_UNLIMITED_SAME_PLANE = "plane";
|
||
Parser.RNG_TOUCH = "touch";
|
||
Parser.SP_RANGE_TYPE_TO_FULL = {
|
||
[Parser.RNG_SPECIAL]: "Special",
|
||
[Parser.RNG_POINT]: "Point",
|
||
[Parser.RNG_LINE]: "Line",
|
||
[Parser.RNG_CUBE]: "Cube",
|
||
[Parser.RNG_CONE]: "Cone",
|
||
[Parser.RNG_RADIUS]: "Radius",
|
||
[Parser.RNG_SPHERE]: "Sphere",
|
||
[Parser.RNG_HEMISPHERE]: "Hemisphere",
|
||
[Parser.RNG_CYLINDER]: "Cylinder",
|
||
[Parser.RNG_SELF]: "Self",
|
||
[Parser.RNG_SIGHT]: "Sight",
|
||
[Parser.RNG_UNLIMITED]: "Unlimited",
|
||
[Parser.RNG_UNLIMITED_SAME_PLANE]: "Unlimited on the same plane",
|
||
[Parser.RNG_TOUCH]: "Touch",
|
||
};
|
||
|
||
Parser.spRangeTypeToFull = function (range) {
|
||
return Parser._parse_aToB(Parser.SP_RANGE_TYPE_TO_FULL, range);
|
||
};
|
||
|
||
Parser.UNT_LBS = "lbs";
|
||
Parser.UNT_TONS_IMPERIAL = "tns";
|
||
Parser.UNT_TONS_METRIC = "Mg";
|
||
|
||
Parser.UNT_FEET = "feet";
|
||
Parser.UNT_YARDS = "yards";
|
||
Parser.UNT_MILES = "miles";
|
||
Parser.SP_DIST_TYPE_TO_FULL = {
|
||
[Parser.UNT_FEET]: "Feet",
|
||
[Parser.UNT_YARDS]: "Yards",
|
||
[Parser.UNT_MILES]: "Miles",
|
||
[Parser.RNG_SELF]: Parser.SP_RANGE_TYPE_TO_FULL[Parser.RNG_SELF],
|
||
[Parser.RNG_TOUCH]: Parser.SP_RANGE_TYPE_TO_FULL[Parser.RNG_TOUCH],
|
||
[Parser.RNG_SIGHT]: Parser.SP_RANGE_TYPE_TO_FULL[Parser.RNG_SIGHT],
|
||
[Parser.RNG_UNLIMITED]: Parser.SP_RANGE_TYPE_TO_FULL[Parser.RNG_UNLIMITED],
|
||
[Parser.RNG_UNLIMITED_SAME_PLANE]: Parser.SP_RANGE_TYPE_TO_FULL[Parser.RNG_UNLIMITED_SAME_PLANE],
|
||
};
|
||
|
||
Parser.spDistanceTypeToFull = function (range) {
|
||
return Parser._parse_aToB(Parser.SP_DIST_TYPE_TO_FULL, range);
|
||
};
|
||
|
||
Parser.SP_RANGE_TO_ICON = {
|
||
[Parser.RNG_SPECIAL]: "fa-star",
|
||
[Parser.RNG_POINT]: "",
|
||
[Parser.RNG_LINE]: "fa-grip-lines-vertical",
|
||
[Parser.RNG_CUBE]: "fa-cube",
|
||
[Parser.RNG_CONE]: "fa-traffic-cone",
|
||
[Parser.RNG_RADIUS]: "fa-hockey-puck",
|
||
[Parser.RNG_SPHERE]: "fa-globe",
|
||
[Parser.RNG_HEMISPHERE]: "fa-globe",
|
||
[Parser.RNG_CYLINDER]: "fa-database",
|
||
[Parser.RNG_SELF]: "fa-street-view",
|
||
[Parser.RNG_SIGHT]: "fa-eye",
|
||
[Parser.RNG_UNLIMITED_SAME_PLANE]: "fa-globe-americas",
|
||
[Parser.RNG_UNLIMITED]: "fa-infinity",
|
||
[Parser.RNG_TOUCH]: "fa-hand-paper",
|
||
};
|
||
|
||
Parser.spRangeTypeToIcon = function (range) {
|
||
return Parser._parse_aToB(Parser.SP_RANGE_TO_ICON, range);
|
||
};
|
||
|
||
Parser.spRangeToShortHtml = function (range) {
|
||
switch (range.type) {
|
||
case Parser.RNG_SPECIAL: return `<span class="fas fa-fw ${Parser.spRangeTypeToIcon(range.type)} help-subtle" title="Special"></span>`;
|
||
case Parser.RNG_POINT: return Parser.spRangeToShortHtml._renderPoint(range);
|
||
case Parser.RNG_LINE:
|
||
case Parser.RNG_CUBE:
|
||
case Parser.RNG_CONE:
|
||
case Parser.RNG_RADIUS:
|
||
case Parser.RNG_SPHERE:
|
||
case Parser.RNG_HEMISPHERE:
|
||
case Parser.RNG_CYLINDER:
|
||
return Parser.spRangeToShortHtml._renderArea(range);
|
||
}
|
||
};
|
||
Parser.spRangeToShortHtml._renderPoint = function (range) {
|
||
const dist = range.distance;
|
||
switch (dist.type) {
|
||
case Parser.RNG_SELF:
|
||
case Parser.RNG_SIGHT:
|
||
case Parser.RNG_UNLIMITED:
|
||
case Parser.RNG_UNLIMITED_SAME_PLANE:
|
||
case Parser.RNG_SPECIAL:
|
||
case Parser.RNG_TOUCH: return `<span class="fas fa-fw ${Parser.spRangeTypeToIcon(dist.type)} help-subtle" title="${Parser.spRangeTypeToFull(dist.type)}"></span>`;
|
||
case Parser.UNT_FEET:
|
||
case Parser.UNT_YARDS:
|
||
case Parser.UNT_MILES:
|
||
default:
|
||
return `${dist.amount} <span class="ve-small">${Parser.getSingletonUnit(dist.type, true)}</span>`;
|
||
}
|
||
};
|
||
Parser.spRangeToShortHtml._renderArea = function (range) {
|
||
const size = range.distance;
|
||
return `<span class="fas fa-fw ${Parser.spRangeTypeToIcon(Parser.RNG_SELF)} help-subtle" title="Self"></span> ${size.amount}<span class="ve-small">-${Parser.getSingletonUnit(size.type, true)}</span> ${Parser.spRangeToShortHtml._getAreaStyleString(range)}`;
|
||
};
|
||
Parser.spRangeToShortHtml._getAreaStyleString = function (range) {
|
||
return `<span class="fas fa-fw ${Parser.spRangeTypeToIcon(range.type)} help-subtle" title="${Parser.spRangeTypeToFull(range.type)}"></span>`;
|
||
};
|
||
|
||
Parser.spRangeToFull = function (range) {
|
||
switch (range.type) {
|
||
case Parser.RNG_SPECIAL: return Parser.spRangeTypeToFull(range.type);
|
||
case Parser.RNG_POINT: return Parser.spRangeToFull._renderPoint(range);
|
||
case Parser.RNG_LINE:
|
||
case Parser.RNG_CUBE:
|
||
case Parser.RNG_CONE:
|
||
case Parser.RNG_RADIUS:
|
||
case Parser.RNG_SPHERE:
|
||
case Parser.RNG_HEMISPHERE:
|
||
case Parser.RNG_CYLINDER:
|
||
return Parser.spRangeToFull._renderArea(range);
|
||
}
|
||
};
|
||
Parser.spRangeToFull._renderPoint = function (range) {
|
||
const dist = range.distance;
|
||
switch (dist.type) {
|
||
case Parser.RNG_SELF:
|
||
case Parser.RNG_SIGHT:
|
||
case Parser.RNG_UNLIMITED:
|
||
case Parser.RNG_UNLIMITED_SAME_PLANE:
|
||
case Parser.RNG_SPECIAL:
|
||
case Parser.RNG_TOUCH: return Parser.spRangeTypeToFull(dist.type);
|
||
case Parser.UNT_FEET:
|
||
case Parser.UNT_YARDS:
|
||
case Parser.UNT_MILES:
|
||
default:
|
||
return `${dist.amount} ${dist.amount === 1 ? Parser.getSingletonUnit(dist.type) : dist.type}`;
|
||
}
|
||
};
|
||
Parser.spRangeToFull._renderArea = function (range) {
|
||
const size = range.distance;
|
||
return `Self (${size.amount}-${Parser.getSingletonUnit(size.type)}${Parser.spRangeToFull._getAreaStyleString(range)}${range.type === Parser.RNG_CYLINDER ? `${size.amountSecondary != null && size.typeSecondary != null ? `, ${size.amountSecondary}-${Parser.getSingletonUnit(size.typeSecondary)}-high` : ""} cylinder` : ""})`;
|
||
};
|
||
Parser.spRangeToFull._getAreaStyleString = function (range) {
|
||
switch (range.type) {
|
||
case Parser.RNG_SPHERE: return " radius";
|
||
case Parser.RNG_HEMISPHERE: return `-radius ${range.type}`;
|
||
case Parser.RNG_CYLINDER: return "-radius";
|
||
default: return ` ${range.type}`;
|
||
}
|
||
};
|
||
|
||
Parser.getSingletonUnit = function (unit, isShort) {
|
||
switch (unit) {
|
||
case Parser.UNT_FEET:
|
||
return isShort ? "ft." : "foot";
|
||
case Parser.UNT_YARDS:
|
||
return isShort ? "yd." : "yard";
|
||
case Parser.UNT_MILES:
|
||
return isShort ? "mi." : "mile";
|
||
default: {
|
||
const fromPrerelease = Parser._getSingletonUnit_prereleaseBrew({unit, isShort, brewUtil: PrereleaseUtil});
|
||
if (fromPrerelease) return fromPrerelease;
|
||
|
||
const fromBrew = Parser._getSingletonUnit_prereleaseBrew({unit, isShort, brewUtil: BrewUtil2});
|
||
if (fromBrew) return fromBrew;
|
||
|
||
if (unit.charAt(unit.length - 1) === "s") return unit.slice(0, -1);
|
||
return unit;
|
||
}
|
||
}
|
||
};
|
||
|
||
Parser._getSingletonUnit_prereleaseBrew = function ({unit, isShort, brewUtil}) {
|
||
const fromBrew = brewUtil.getMetaLookup("spellDistanceUnits")?.[unit]?.["singular"];
|
||
if (fromBrew) return fromBrew;
|
||
};
|
||
|
||
Parser.RANGE_TYPES = [
|
||
{type: Parser.RNG_POINT, hasDistance: true, isRequireAmount: false},
|
||
|
||
{type: Parser.RNG_LINE, hasDistance: true, isRequireAmount: true},
|
||
{type: Parser.RNG_CUBE, hasDistance: true, isRequireAmount: true},
|
||
{type: Parser.RNG_CONE, hasDistance: true, isRequireAmount: true},
|
||
{type: Parser.RNG_RADIUS, hasDistance: true, isRequireAmount: true},
|
||
{type: Parser.RNG_SPHERE, hasDistance: true, isRequireAmount: true},
|
||
{type: Parser.RNG_HEMISPHERE, hasDistance: true, isRequireAmount: true},
|
||
{type: Parser.RNG_CYLINDER, hasDistance: true, isRequireAmount: true},
|
||
|
||
{type: Parser.RNG_SPECIAL, hasDistance: false, isRequireAmount: false},
|
||
];
|
||
|
||
Parser.DIST_TYPES = [
|
||
{type: Parser.RNG_SELF, hasAmount: false},
|
||
{type: Parser.RNG_TOUCH, hasAmount: false},
|
||
|
||
{type: Parser.UNT_FEET, hasAmount: true},
|
||
{type: Parser.UNT_YARDS, hasAmount: true},
|
||
{type: Parser.UNT_MILES, hasAmount: true},
|
||
|
||
{type: Parser.RNG_SIGHT, hasAmount: false},
|
||
{type: Parser.RNG_UNLIMITED_SAME_PLANE, hasAmount: false},
|
||
{type: Parser.RNG_UNLIMITED, hasAmount: false},
|
||
];
|
||
|
||
Parser.spComponentsToFull = function (comp, level, {isPlainText = false} = {}) {
|
||
if (!comp) return "None";
|
||
const out = [];
|
||
if (comp.v) out.push("V");
|
||
if (comp.s) out.push("S");
|
||
if (comp.m != null) {
|
||
const fnRender = isPlainText ? Renderer.stripTags.bind(Renderer) : Renderer.get().render.bind(Renderer.get());
|
||
out.push(`M${comp.m !== true ? ` (${fnRender(comp.m.text != null ? comp.m.text : comp.m)})` : ""}`);
|
||
}
|
||
if (comp.r) out.push(`R (${level} gp)`);
|
||
return out.join(", ") || "None";
|
||
};
|
||
|
||
Parser.SP_END_TYPE_TO_FULL = {
|
||
"dispel": "dispelled",
|
||
"trigger": "triggered",
|
||
"discharge": "discharged",
|
||
};
|
||
Parser.spEndTypeToFull = function (type) {
|
||
return Parser._parse_aToB(Parser.SP_END_TYPE_TO_FULL, type);
|
||
};
|
||
|
||
Parser.spDurationToFull = function (dur) {
|
||
let hasSubOr = false;
|
||
const outParts = dur.map(d => {
|
||
switch (d.type) {
|
||
case "special":
|
||
return "Special";
|
||
case "instant":
|
||
return `Instantaneous${d.condition ? ` (${d.condition})` : ""}`;
|
||
case "timed":
|
||
return `${d.concentration ? "Concentration, " : ""}${d.concentration ? "u" : d.duration.upTo ? "U" : ""}${d.concentration || d.duration.upTo ? "p to " : ""}${d.duration.amount} ${d.duration.amount === 1 ? d.duration.type : `${d.duration.type}s`}`;
|
||
case "permanent": {
|
||
if (d.ends) {
|
||
const endsToJoin = d.ends.map(m => Parser.spEndTypeToFull(m));
|
||
hasSubOr = hasSubOr || endsToJoin.length > 1;
|
||
return `Until ${endsToJoin.joinConjunct(", ", " or ")}`;
|
||
} else {
|
||
return "Permanent";
|
||
}
|
||
}
|
||
}
|
||
});
|
||
return `${outParts.joinConjunct(hasSubOr ? "; " : ", ", " or ")}${dur.length > 1 ? " (see below)" : ""}`;
|
||
};
|
||
|
||
Parser.DURATION_TYPES = [
|
||
{type: "instant", full: "Instantaneous"},
|
||
{type: "timed", hasAmount: true},
|
||
{type: "permanent", hasEnds: true},
|
||
{type: "special"},
|
||
];
|
||
|
||
Parser.DURATION_AMOUNT_TYPES = [
|
||
"turn",
|
||
"round",
|
||
"minute",
|
||
"hour",
|
||
"day",
|
||
"week",
|
||
"month",
|
||
"year",
|
||
];
|
||
|
||
Parser.spClassesToFull = function (sp, {isTextOnly = false, subclassLookup = {}} = {}) {
|
||
const fromSubclassList = Renderer.spell.getCombinedClasses(sp, "fromSubclass");
|
||
const fromSubclasses = Parser.spSubclassesToFull(fromSubclassList, {isTextOnly, subclassLookup});
|
||
const fromClassList = Renderer.spell.getCombinedClasses(sp, "fromClassList");
|
||
return `${Parser.spMainClassesToFull(fromClassList, {isTextOnly})}${fromSubclasses ? `, ${fromSubclasses}` : ""}`;
|
||
};
|
||
|
||
Parser.spMainClassesToFull = function (fromClassList, {isTextOnly = false} = {}) {
|
||
return fromClassList
|
||
.map(c => ({hash: UrlUtil.URL_TO_HASH_BUILDER[UrlUtil.PG_CLASSES](c), c}))
|
||
.filter(it => !ExcludeUtil.isInitialised || !ExcludeUtil.isExcluded(it.hash, "class", it.c.source))
|
||
.sort((a, b) => SortUtil.ascSort(a.c.name, b.c.name))
|
||
.map(it => {
|
||
if (isTextOnly) return it.c.name;
|
||
|
||
return `<span title="${it.c.definedInSource ? `Class source` : "Source"}: ${Parser.sourceJsonToFull(it.c.source)}${it.c.definedInSource ? `. Spell list defined in: ${Parser.sourceJsonToFull(it.c.definedInSource)}.` : ""}">${Renderer.get().render(`{@class ${it.c.name}|${it.c.source}}`)}</span>`;
|
||
})
|
||
.join(", ") || "";
|
||
};
|
||
|
||
Parser.spSubclassesToFull = function (fromSubclassList, {isTextOnly = false, subclassLookup = {}} = {}) {
|
||
return fromSubclassList
|
||
.filter(mt => {
|
||
if (!ExcludeUtil.isInitialised) return true;
|
||
const excludeClass = ExcludeUtil.isExcluded(UrlUtil.URL_TO_HASH_BUILDER[UrlUtil.PG_CLASSES](mt.class), "class", mt.class.source);
|
||
if (excludeClass) return false;
|
||
|
||
return !ExcludeUtil.isExcluded(
|
||
UrlUtil.URL_TO_HASH_BUILDER["subclass"]({
|
||
shortName: mt.subclass.name,
|
||
source: mt.subclass.source,
|
||
className: mt.class.name,
|
||
classSource: mt.class.source,
|
||
}),
|
||
"subclass",
|
||
mt.subclass.source,
|
||
{isNoCount: true},
|
||
);
|
||
})
|
||
.sort((a, b) => {
|
||
const byName = SortUtil.ascSort(a.class.name, b.class.name);
|
||
return byName || SortUtil.ascSort(a.subclass.name, b.subclass.name);
|
||
})
|
||
.map(c => Parser._spSubclassItem({fromSubclass: c, isTextOnly}))
|
||
.join(", ") || "";
|
||
};
|
||
|
||
Parser._spSubclassItem = function ({fromSubclass, isTextOnly}) {
|
||
const c = fromSubclass.class;
|
||
const sc = fromSubclass.subclass;
|
||
const text = `${sc.shortName}${sc.subSubclass ? ` (${sc.subSubclass})` : ""}`;
|
||
if (isTextOnly) return text;
|
||
|
||
const classPart = `<span title="Source: ${Parser.sourceJsonToFull(c.source)}${c.definedInSource ? ` From a class spell list defined in: ${Parser.sourceJsonToFull(c.definedInSource)}` : ""}">${Renderer.get().render(`{@class ${c.name}|${c.source}}`)}</span>`;
|
||
|
||
return `<span class="italic" title="Source: ${Parser.sourceJsonToFull(fromSubclass.subclass.source)}">${Renderer.get().render(`{@class ${c.name}|${c.source}|${text}|${sc.shortName}|${sc.source}}`)}</span> ${classPart}`;
|
||
};
|
||
|
||
Parser.SPELL_ATTACK_TYPE_TO_FULL = {};
|
||
Parser.SPELL_ATTACK_TYPE_TO_FULL["M"] = "Melee";
|
||
Parser.SPELL_ATTACK_TYPE_TO_FULL["R"] = "Ranged";
|
||
Parser.SPELL_ATTACK_TYPE_TO_FULL["O"] = "Other/Unknown";
|
||
|
||
Parser.spAttackTypeToFull = function (type) {
|
||
return Parser._parse_aToB(Parser.SPELL_ATTACK_TYPE_TO_FULL, type);
|
||
};
|
||
|
||
Parser.SPELL_AREA_TYPE_TO_FULL = {
|
||
ST: "Single Target",
|
||
MT: "Multiple Targets",
|
||
C: "Cube",
|
||
N: "Cone",
|
||
Y: "Cylinder",
|
||
S: "Sphere",
|
||
R: "Circle",
|
||
Q: "Square",
|
||
L: "Line",
|
||
H: "Hemisphere",
|
||
W: "Wall",
|
||
};
|
||
Parser.spAreaTypeToFull = function (type) {
|
||
return Parser._parse_aToB(Parser.SPELL_AREA_TYPE_TO_FULL, type);
|
||
};
|
||
|
||
Parser.SP_MISC_TAG_TO_FULL = {
|
||
HL: "Healing",
|
||
THP: "Grants Temporary Hit Points",
|
||
SGT: "Requires Sight",
|
||
PRM: "Permanent Effects",
|
||
SCL: "Scaling Effects",
|
||
SMN: "Summons Creature",
|
||
MAC: "Modifies AC",
|
||
TP: "Teleportation",
|
||
FMV: "Forced Movement",
|
||
RO: "Rollable Effects",
|
||
LGTS: "Creates Sunlight",
|
||
LGT: "Creates Light",
|
||
UBA: "Uses Bonus Action",
|
||
PS: "Plane Shifting",
|
||
OBS: "Obscures Vision",
|
||
DFT: "Difficult Terrain",
|
||
AAD: "Additional Attack Damage",
|
||
OBJ: "Affects Objects",
|
||
ADV: "Grants Advantage",
|
||
PIR: "Permanent If Repeated",
|
||
};
|
||
Parser.spMiscTagToFull = function (type) {
|
||
return Parser._parse_aToB(Parser.SP_MISC_TAG_TO_FULL, type);
|
||
};
|
||
|
||
Parser.SP_CASTER_PROGRESSION_TO_FULL = {
|
||
full: "Full",
|
||
"1/2": "Half",
|
||
"1/3": "One-Third",
|
||
"pact": "Pact Magic",
|
||
};
|
||
Parser.spCasterProgressionToFull = function (type) {
|
||
return Parser._parse_aToB(Parser.SP_CASTER_PROGRESSION_TO_FULL, type);
|
||
};
|
||
|
||
// mon-prefix functions are for parsing monster data, and shared with the roll20 script
|
||
Parser.monTypeToFullObj = function (type) {
|
||
const out = {
|
||
types: [],
|
||
tags: [],
|
||
asText: "",
|
||
asTextShort: "",
|
||
|
||
typeSidekick: null,
|
||
tagsSidekick: [],
|
||
asTextSidekick: null,
|
||
};
|
||
if (type == null) return out;
|
||
|
||
// handles e.g. "fey"
|
||
if (typeof type === "string") {
|
||
out.types = [type];
|
||
out.asText = type.toTitleCase();
|
||
out.asTextShort = out.asText;
|
||
return out;
|
||
}
|
||
|
||
if (type.type?.choose) {
|
||
out.types = type.type.choose;
|
||
} else {
|
||
out.types = [type.type];
|
||
}
|
||
|
||
if (type.swarmSize) {
|
||
out.tags.push("swarm");
|
||
out.asText = `swarm of ${Parser.sizeAbvToFull(type.swarmSize)} ${out.types.map(typ => Parser.monTypeToPlural(typ).toTitleCase()).joinConjunct(", ", " or ")}`;
|
||
out.asTextShort = out.asText;
|
||
out.swarmSize = type.swarmSize;
|
||
} else {
|
||
out.asText = out.types.map(typ => typ.toTitleCase()).joinConjunct(", ", " or ");
|
||
out.asTextShort = out.asText;
|
||
}
|
||
|
||
const tagMetas = Parser.monTypeToFullObj._getTagMetas(type.tags);
|
||
if (tagMetas.length) {
|
||
out.tags.push(...tagMetas.map(({filterTag}) => filterTag));
|
||
const ptTags = ` (${tagMetas.map(({displayTag}) => displayTag).join(", ")})`;
|
||
out.asText += ptTags;
|
||
out.asTextShort += ptTags;
|
||
}
|
||
|
||
if (type.note) out.asText += ` ${type.note}`;
|
||
|
||
// region Sidekick
|
||
if (type.sidekickType) {
|
||
out.typeSidekick = type.sidekickType;
|
||
if (!type.sidekickHidden) out.asTextSidekick = `${type.sidekickType}`;
|
||
|
||
const tagMetas = Parser.monTypeToFullObj._getTagMetas(type.sidekickTags);
|
||
if (tagMetas.length) {
|
||
out.tagsSidekick.push(...tagMetas.map(({filterTag}) => filterTag));
|
||
if (!type.sidekickHidden) out.asTextSidekick += ` (${tagMetas.map(({displayTag}) => displayTag).join(", ")})`;
|
||
}
|
||
}
|
||
// endregion
|
||
|
||
return out;
|
||
};
|
||
|
||
Parser.monTypeToFullObj._getTagMetas = (tags) => {
|
||
return tags
|
||
? tags.map(tag => {
|
||
if (typeof tag === "string") { // handles e.g. "Fiend (Devil)"
|
||
return {
|
||
filterTag: tag.toLowerCase(),
|
||
displayTag: tag.toTitleCase(),
|
||
};
|
||
} else { // handles e.g. "Humanoid (Chondathan Human)"
|
||
return {
|
||
filterTag: tag.tag.toLowerCase(),
|
||
displayTag: `${tag.prefix} ${tag.tag}`.toTitleCase(),
|
||
};
|
||
}
|
||
})
|
||
: [];
|
||
};
|
||
|
||
Parser.monTypeToPlural = function (type) {
|
||
return Parser._parse_aToB(Parser.MON_TYPE_TO_PLURAL, type);
|
||
};
|
||
|
||
Parser.monTypeFromPlural = function (type) {
|
||
return Parser._parse_bToA(Parser.MON_TYPE_TO_PLURAL, type);
|
||
};
|
||
|
||
Parser.monCrToFull = function (cr, {xp = null, isMythic = false} = {}) {
|
||
if (cr == null) return "";
|
||
|
||
if (typeof cr === "string") {
|
||
if (Parser.crToNumber(cr) >= VeCt.CR_CUSTOM) return `${cr}${xp != null ? ` (${xp} XP)` : ""}`;
|
||
|
||
xp = xp != null ? Parser._addCommas(xp) : Parser.crToXp(cr);
|
||
return `${cr} (${xp} XP${isMythic ? `, or ${Parser.crToXp(cr, {isDouble: true})} XP as a mythic encounter` : ""})`;
|
||
} else {
|
||
const stack = [Parser.monCrToFull(cr.cr, {xp: cr.xp, isMythic})];
|
||
if (cr.lair) stack.push(`${Parser.monCrToFull(cr.lair)} when encountered in lair`);
|
||
if (cr.coven) stack.push(`${Parser.monCrToFull(cr.coven)} when part of a coven`);
|
||
return stack.joinConjunct(", ", " or ");
|
||
}
|
||
};
|
||
|
||
Parser.getFullImmRes = function (toParse, {isPlainText = false} = {}) {
|
||
if (!toParse?.length) return "";
|
||
|
||
let maxDepth = 0;
|
||
|
||
const renderString = str => isPlainText ? Renderer.stripTags(`${str}`) : Renderer.get().render(`${str}`);
|
||
|
||
const render = (val, depth = 0) => {
|
||
maxDepth = Math.max(maxDepth, depth);
|
||
if (typeof val === "string") return renderString(val);
|
||
|
||
if (val.special) return renderString(val.special);
|
||
|
||
const stack = [];
|
||
|
||
if (val.preNote) stack.push(renderString(val.preNote));
|
||
|
||
const prop = val.immune ? "immune" : val.resist ? "resist" : val.vulnerable ? "vulnerable" : null;
|
||
if (prop) {
|
||
const toJoin = val[prop].length === Parser.DMG_TYPES.length && CollectionUtil.deepEquals(Parser.DMG_TYPES, val[prop])
|
||
? ["all damage"]
|
||
: val[prop].map(nxt => render(nxt, depth + 1));
|
||
stack.push(renderString(depth ? toJoin.join(maxDepth ? "; " : ", ") : toJoin.joinConjunct(", ", " and ")));
|
||
}
|
||
|
||
if (val.note) stack.push(renderString(val.note));
|
||
|
||
return stack.join(" ");
|
||
};
|
||
|
||
const arr = toParse.map(it => render(it));
|
||
|
||
if (arr.length <= 1) return arr.join("");
|
||
|
||
let out = "";
|
||
for (let i = 0; i < arr.length - 1; ++i) {
|
||
const it = arr[i];
|
||
const nxt = arr[i + 1];
|
||
|
||
const orig = toParse[i];
|
||
const origNxt = toParse[i + 1];
|
||
|
||
out += it;
|
||
out += (it.includes(",") || nxt.includes(",") || (orig && orig.cond) || (origNxt && origNxt.cond)) ? "; " : ", ";
|
||
}
|
||
out += arr.last();
|
||
return out;
|
||
};
|
||
|
||
Parser.getFullCondImm = function (condImm, {isPlainText = false, isEntry = false} = {}) {
|
||
if (isPlainText && isEntry) throw new Error(`Options "isPlainText" and "isEntry" are mutually exclusive!`);
|
||
|
||
if (!condImm?.length) return "";
|
||
|
||
const render = condition => {
|
||
if (isPlainText) return condition;
|
||
const ent = `{@condition ${condition}}`;
|
||
if (isEntry) return ent;
|
||
return Renderer.get().render(ent);
|
||
};
|
||
|
||
return condImm
|
||
.map(it => {
|
||
if (it.special) return it.special;
|
||
if (it.conditionImmune) return `${it.preNote ? `${it.preNote} ` : ""}${it.conditionImmune.map(render).join(", ")}${it.note ? ` ${it.note}` : ""}`;
|
||
return render(it);
|
||
})
|
||
.sort(SortUtil.ascSortLower).join(", ");
|
||
};
|
||
|
||
Parser.MON_SENSE_TAG_TO_FULL = {
|
||
"B": "blindsight",
|
||
"D": "darkvision",
|
||
"SD": "superior darkvision",
|
||
"T": "tremorsense",
|
||
"U": "truesight",
|
||
};
|
||
Parser.monSenseTagToFull = function (tag) {
|
||
return Parser._parse_aToB(Parser.MON_SENSE_TAG_TO_FULL, tag);
|
||
};
|
||
|
||
Parser.MON_SPELLCASTING_TAG_TO_FULL = {
|
||
"P": "Psionics",
|
||
"I": "Innate",
|
||
"F": "Form Only",
|
||
"S": "Shared",
|
||
"O": "Other",
|
||
"CA": "Class, Artificer",
|
||
"CB": "Class, Bard",
|
||
"CC": "Class, Cleric",
|
||
"CD": "Class, Druid",
|
||
"CP": "Class, Paladin",
|
||
"CR": "Class, Ranger",
|
||
"CS": "Class, Sorcerer",
|
||
"CL": "Class, Warlock",
|
||
"CW": "Class, Wizard",
|
||
};
|
||
Parser.monSpellcastingTagToFull = function (tag) {
|
||
return Parser._parse_aToB(Parser.MON_SPELLCASTING_TAG_TO_FULL, tag);
|
||
};
|
||
|
||
Parser.MON_MISC_TAG_TO_FULL = {
|
||
"AOE": "Has Areas of Effect",
|
||
"CUR": "Inflicts Curse",
|
||
"DIS": "Inflicts Disease",
|
||
"HPR": "Has HP Reduction",
|
||
"MW": "Has Weapon Attacks, Melee",
|
||
"RW": "Has Weapon Attacks, Ranged",
|
||
"MLW": "Has Melee Weapons",
|
||
"RNG": "Has Ranged Weapons",
|
||
"RCH": "Has Reach Attacks",
|
||
"THW": "Has Thrown Weapons",
|
||
};
|
||
Parser.monMiscTagToFull = function (tag) {
|
||
return Parser._parse_aToB(Parser.MON_MISC_TAG_TO_FULL, tag);
|
||
};
|
||
|
||
Parser.MON_LANGUAGE_TAG_TO_FULL = {
|
||
"AB": "Abyssal",
|
||
"AQ": "Aquan",
|
||
"AU": "Auran",
|
||
"C": "Common",
|
||
"CE": "Celestial",
|
||
"CS": "Can't Speak Known Languages",
|
||
"D": "Dwarvish",
|
||
"DR": "Draconic",
|
||
"DS": "Deep Speech",
|
||
"DU": "Druidic",
|
||
"E": "Elvish",
|
||
"G": "Gnomish",
|
||
"GI": "Giant",
|
||
"GO": "Goblin",
|
||
"GTH": "Gith",
|
||
"H": "Halfling",
|
||
"I": "Infernal",
|
||
"IG": "Ignan",
|
||
"LF": "Languages Known in Life",
|
||
"O": "Orc",
|
||
"OTH": "Other",
|
||
"P": "Primordial",
|
||
"S": "Sylvan",
|
||
"T": "Terran",
|
||
"TC": "Thieves' cant",
|
||
"TP": "Telepathy",
|
||
"U": "Undercommon",
|
||
"X": "Any (Choose)",
|
||
"XX": "All",
|
||
};
|
||
Parser.monLanguageTagToFull = function (tag) {
|
||
return Parser._parse_aToB(Parser.MON_LANGUAGE_TAG_TO_FULL, tag);
|
||
};
|
||
|
||
Parser.ENVIRONMENTS = ["arctic", "coastal", "desert", "forest", "grassland", "hill", "mountain", "swamp", "underdark", "underwater", "urban"];
|
||
|
||
// psi-prefix functions are for parsing psionic data, and shared with the roll20 script
|
||
Parser.PSI_ABV_TYPE_TALENT = "T";
|
||
Parser.PSI_ABV_TYPE_DISCIPLINE = "D";
|
||
Parser.PSI_ORDER_NONE = "None";
|
||
Parser.psiTypeToFull = type => Parser.psiTypeToMeta(type).full;
|
||
|
||
Parser.psiTypeToMeta = type => {
|
||
let out = {};
|
||
if (type === Parser.PSI_ABV_TYPE_TALENT) out = {hasOrder: false, full: "Talent"};
|
||
else if (type === Parser.PSI_ABV_TYPE_DISCIPLINE) out = {hasOrder: true, full: "Discipline"};
|
||
else if (PrereleaseUtil.getMetaLookup("psionicTypes")?.[type]) out = MiscUtil.copyFast(PrereleaseUtil.getMetaLookup("psionicTypes")[type]);
|
||
else if (BrewUtil2.getMetaLookup("psionicTypes")?.[type]) out = MiscUtil.copyFast(BrewUtil2.getMetaLookup("psionicTypes")[type]);
|
||
out.full = out.full || "Unknown";
|
||
out.short = out.short || out.full;
|
||
return out;
|
||
};
|
||
|
||
Parser.psiOrderToFull = (order) => {
|
||
return order === undefined ? Parser.PSI_ORDER_NONE : order;
|
||
};
|
||
|
||
Parser.prereqSpellToFull = function (spell, {isTextOnly = false} = {}) {
|
||
if (spell) {
|
||
const [text, suffix] = spell.split("#");
|
||
if (!suffix) return isTextOnly ? spell : Renderer.get().render(`{@spell ${spell}}`);
|
||
else if (suffix === "c") return (isTextOnly ? Renderer.stripTags : Renderer.get().render.bind(Renderer.get()))(`{@spell ${text}} cantrip`);
|
||
else if (suffix === "x") return (isTextOnly ? Renderer.stripTags : Renderer.get().render.bind(Renderer.get()))("{@spell hex} spell or a warlock feature that curses");
|
||
} else return VeCt.STR_NONE;
|
||
};
|
||
|
||
Parser.prereqPactToFull = function (pact) {
|
||
if (pact === "Chain") return "Pact of the Chain";
|
||
if (pact === "Tome") return "Pact of the Tome";
|
||
if (pact === "Blade") return "Pact of the Blade";
|
||
if (pact === "Talisman") return "Pact of the Talisman";
|
||
return pact;
|
||
};
|
||
|
||
Parser.prereqPatronToShort = function (patron) {
|
||
if (patron === "Any") return patron;
|
||
const mThe = /^The (.*?)$/.exec(patron);
|
||
if (mThe) return mThe[1];
|
||
return patron;
|
||
};
|
||
|
||
// NOTE: These need to be reflected in omnidexer.js to be indexed
|
||
Parser.OPT_FEATURE_TYPE_TO_FULL = {
|
||
AI: "Artificer Infusion",
|
||
ED: "Elemental Discipline",
|
||
EI: "Eldritch Invocation",
|
||
MM: "Metamagic",
|
||
"MV": "Maneuver",
|
||
"MV:B": "Maneuver, Battle Master",
|
||
"MV:C2-UA": "Maneuver, Cavalier V2 (UA)",
|
||
"AS:V1-UA": "Arcane Shot, V1 (UA)",
|
||
"AS:V2-UA": "Arcane Shot, V2 (UA)",
|
||
"AS": "Arcane Shot",
|
||
OTH: "Other",
|
||
"FS:F": "Fighting Style; Fighter",
|
||
"FS:B": "Fighting Style; Bard",
|
||
"FS:P": "Fighting Style; Paladin",
|
||
"FS:R": "Fighting Style; Ranger",
|
||
"PB": "Pact Boon",
|
||
"OR": "Onomancy Resonant",
|
||
"RN": "Rune Knight Rune",
|
||
"AF": "Alchemical Formula",
|
||
};
|
||
|
||
Parser.optFeatureTypeToFull = function (type) {
|
||
if (Parser.OPT_FEATURE_TYPE_TO_FULL[type]) return Parser.OPT_FEATURE_TYPE_TO_FULL[type];
|
||
if (PrereleaseUtil.getMetaLookup("optionalFeatureTypes")?.[type]) return PrereleaseUtil.getMetaLookup("optionalFeatureTypes")[type];
|
||
if (BrewUtil2.getMetaLookup("optionalFeatureTypes")?.[type]) return BrewUtil2.getMetaLookup("optionalFeatureTypes")[type];
|
||
return type;
|
||
};
|
||
|
||
Parser.CHAR_OPTIONAL_FEATURE_TYPE_TO_FULL = {
|
||
"SG": "Supernatural Gift",
|
||
"OF": "Optional Feature",
|
||
"DG": "Dark Gift",
|
||
"RF:B": "Replacement Feature: Background",
|
||
"CS": "Character Secret", // Specific to IDRotF (rules on page 14)
|
||
};
|
||
|
||
Parser.charCreationOptionTypeToFull = function (type) {
|
||
if (Parser.CHAR_OPTIONAL_FEATURE_TYPE_TO_FULL[type]) return Parser.CHAR_OPTIONAL_FEATURE_TYPE_TO_FULL[type];
|
||
if (PrereleaseUtil.getMetaLookup("charOption")?.[type]) return PrereleaseUtil.getMetaLookup("charOption")[type];
|
||
if (BrewUtil2.getMetaLookup("charOption")?.[type]) return BrewUtil2.getMetaLookup("charOption")[type];
|
||
return type;
|
||
};
|
||
|
||
Parser._ALIGNMENT_ABV_TO_FULL = {
|
||
"L": "lawful",
|
||
"N": "neutral",
|
||
"NX": "neutral (law/chaos axis)",
|
||
"NY": "neutral (good/evil axis)",
|
||
"C": "chaotic",
|
||
"G": "good",
|
||
"E": "evil",
|
||
// "special" values
|
||
"U": "unaligned",
|
||
"A": "any alignment",
|
||
};
|
||
|
||
Parser.alignmentAbvToFull = function (alignment) {
|
||
if (!alignment) return null; // used in sidekicks
|
||
|
||
if (typeof alignment === "object") {
|
||
// use in MTF Sacred Statue
|
||
if (alignment.special != null) return alignment.special;
|
||
|
||
// e.g. `{alignment: ["N", "G"], chance: 50}` or `{alignment: ["N", "G"]}`
|
||
return `${Parser.alignmentListToFull(alignment.alignment)}${alignment.chance ? ` (${alignment.chance}%)` : ""}${alignment.note ? ` (${alignment.note})` : ""}`;
|
||
}
|
||
|
||
alignment = alignment.toUpperCase();
|
||
return Parser._ALIGNMENT_ABV_TO_FULL[alignment] ?? alignment;
|
||
};
|
||
|
||
Parser.alignmentListToFull = function (alignList) {
|
||
if (!alignList) return "";
|
||
|
||
if (alignList.some(it => typeof it !== "string")) {
|
||
if (alignList.some(it => typeof it === "string")) throw new Error(`Mixed alignment types: ${JSON.stringify(alignList)}`);
|
||
|
||
// filter out any nonexistent alignments, as we don't care about "alignment does not exist" if there are other alignments
|
||
return alignList
|
||
.filter(it => it.alignment === undefined || it.alignment != null)
|
||
.map(it => it.special != null || it.chance != null || it.note != null ? Parser.alignmentAbvToFull(it) : Parser.alignmentListToFull(it.alignment)).join(" or ");
|
||
}
|
||
|
||
// assume all single-length arrays can be simply parsed
|
||
if (alignList.length === 1) return Parser.alignmentAbvToFull(alignList[0]);
|
||
// a pair of abv's, e.g. "L" "G"
|
||
if (alignList.length === 2) {
|
||
return alignList.map(a => Parser.alignmentAbvToFull(a)).join(" ");
|
||
}
|
||
if (alignList.length === 3) {
|
||
if (alignList.includes("NX") && alignList.includes("NY") && alignList.includes("N")) return "any neutral alignment";
|
||
}
|
||
// longer arrays should have a custom mapping
|
||
if (alignList.length === 5) {
|
||
if (!alignList.includes("G")) return "any non-good alignment";
|
||
if (!alignList.includes("E")) return "any non-evil alignment";
|
||
if (!alignList.includes("L")) return "any non-lawful alignment";
|
||
if (!alignList.includes("C")) return "any non-chaotic alignment";
|
||
}
|
||
if (alignList.length === 4) {
|
||
if (!alignList.includes("L") && !alignList.includes("NX")) return "any chaotic alignment";
|
||
if (!alignList.includes("G") && !alignList.includes("NY")) return "any evil alignment";
|
||
if (!alignList.includes("C") && !alignList.includes("NX")) return "any lawful alignment";
|
||
if (!alignList.includes("E") && !alignList.includes("NY")) return "any good alignment";
|
||
}
|
||
throw new Error(`Unmapped alignment: ${JSON.stringify(alignList)}`);
|
||
};
|
||
|
||
Parser.weightToFull = function (lbs, isSmallUnit) {
|
||
const tons = Math.floor(lbs / 2000);
|
||
lbs = lbs - (2000 * tons);
|
||
return [
|
||
tons ? `${tons}${isSmallUnit ? `<span class="ve-small ml-1">` : " "}ton${tons === 1 ? "" : "s"}${isSmallUnit ? `</span>` : ""}` : null,
|
||
lbs ? `${lbs}${isSmallUnit ? `<span class="ve-small ml-1">` : " "}lb.${isSmallUnit ? `</span>` : ""}` : null,
|
||
].filter(Boolean).join(", ");
|
||
};
|
||
|
||
Parser.RARITIES = ["common", "uncommon", "rare", "very rare", "legendary", "artifact"];
|
||
Parser.ITEM_RARITIES = ["none", ...Parser.RARITIES, "varies", "unknown", "unknown (magic)", "other"];
|
||
|
||
Parser.CAT_ID_CREATURE = 1;
|
||
Parser.CAT_ID_SPELL = 2;
|
||
Parser.CAT_ID_BACKGROUND = 3;
|
||
Parser.CAT_ID_ITEM = 4;
|
||
Parser.CAT_ID_CLASS = 5;
|
||
Parser.CAT_ID_CONDITION = 6;
|
||
Parser.CAT_ID_FEAT = 7;
|
||
Parser.CAT_ID_ELDRITCH_INVOCATION = 8;
|
||
Parser.CAT_ID_PSIONIC = 9;
|
||
Parser.CAT_ID_RACE = 10;
|
||
Parser.CAT_ID_OTHER_REWARD = 11;
|
||
Parser.CAT_ID_VARIANT_OPTIONAL_RULE = 12;
|
||
Parser.CAT_ID_ADVENTURE = 13;
|
||
Parser.CAT_ID_DEITY = 14;
|
||
Parser.CAT_ID_OBJECT = 15;
|
||
Parser.CAT_ID_TRAP = 16;
|
||
Parser.CAT_ID_HAZARD = 17;
|
||
Parser.CAT_ID_QUICKREF = 18;
|
||
Parser.CAT_ID_CULT = 19;
|
||
Parser.CAT_ID_BOON = 20;
|
||
Parser.CAT_ID_DISEASE = 21;
|
||
Parser.CAT_ID_METAMAGIC = 22;
|
||
Parser.CAT_ID_MANEUVER_BATTLEMASTER = 23;
|
||
Parser.CAT_ID_TABLE = 24;
|
||
Parser.CAT_ID_TABLE_GROUP = 25;
|
||
Parser.CAT_ID_MANEUVER_CAVALIER = 26;
|
||
Parser.CAT_ID_ARCANE_SHOT = 27;
|
||
Parser.CAT_ID_OPTIONAL_FEATURE_OTHER = 28;
|
||
Parser.CAT_ID_FIGHTING_STYLE = 29;
|
||
Parser.CAT_ID_CLASS_FEATURE = 30;
|
||
Parser.CAT_ID_VEHICLE = 31;
|
||
Parser.CAT_ID_PACT_BOON = 32;
|
||
Parser.CAT_ID_ELEMENTAL_DISCIPLINE = 33;
|
||
Parser.CAT_ID_ARTIFICER_INFUSION = 34;
|
||
Parser.CAT_ID_SHIP_UPGRADE = 35;
|
||
Parser.CAT_ID_INFERNAL_WAR_MACHINE_UPGRADE = 36;
|
||
Parser.CAT_ID_ONOMANCY_RESONANT = 37;
|
||
Parser.CAT_ID_RUNE_KNIGHT_RUNE = 37;
|
||
Parser.CAT_ID_ALCHEMICAL_FORMULA = 38;
|
||
Parser.CAT_ID_MANEUVER = 39;
|
||
Parser.CAT_ID_SUBCLASS = 40;
|
||
Parser.CAT_ID_SUBCLASS_FEATURE = 41;
|
||
Parser.CAT_ID_ACTION = 42;
|
||
Parser.CAT_ID_LANGUAGE = 43;
|
||
Parser.CAT_ID_BOOK = 44;
|
||
Parser.CAT_ID_PAGE = 45;
|
||
Parser.CAT_ID_LEGENDARY_GROUP = 46;
|
||
Parser.CAT_ID_CHAR_CREATION_OPTIONS = 47;
|
||
Parser.CAT_ID_RECIPES = 48;
|
||
Parser.CAT_ID_STATUS = 49;
|
||
Parser.CAT_ID_SKILLS = 50;
|
||
Parser.CAT_ID_SENSES = 51;
|
||
Parser.CAT_ID_DECK = 52;
|
||
Parser.CAT_ID_CARD = 53;
|
||
|
||
Parser.CAT_ID_TO_FULL = {};
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_CREATURE] = "Bestiary";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_SPELL] = "Spell";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_BACKGROUND] = "Background";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_ITEM] = "Item";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_CLASS] = "Class";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_CONDITION] = "Condition";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_FEAT] = "Feat";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_ELDRITCH_INVOCATION] = "Eldritch Invocation";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_PSIONIC] = "Psionic";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_RACE] = "Race";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_OTHER_REWARD] = "Other Reward";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_VARIANT_OPTIONAL_RULE] = "Variant/Optional Rule";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_ADVENTURE] = "Adventure";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_DEITY] = "Deity";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_OBJECT] = "Object";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_TRAP] = "Trap";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_HAZARD] = "Hazard";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_QUICKREF] = "Quick Reference";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_CULT] = "Cult";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_BOON] = "Boon";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_DISEASE] = "Disease";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_METAMAGIC] = "Metamagic";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_MANEUVER_BATTLEMASTER] = "Maneuver; Battlemaster";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_TABLE] = "Table";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_TABLE_GROUP] = "Table";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_MANEUVER_CAVALIER] = "Maneuver; Cavalier";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_ARCANE_SHOT] = "Arcane Shot";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_OPTIONAL_FEATURE_OTHER] = "Optional Feature";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_FIGHTING_STYLE] = "Fighting Style";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_CLASS_FEATURE] = "Class Feature";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_VEHICLE] = "Vehicle";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_PACT_BOON] = "Pact Boon";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_ELEMENTAL_DISCIPLINE] = "Elemental Discipline";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_ARTIFICER_INFUSION] = "Infusion";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_SHIP_UPGRADE] = "Ship Upgrade";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_INFERNAL_WAR_MACHINE_UPGRADE] = "Infernal War Machine Upgrade";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_ONOMANCY_RESONANT] = "Onomancy Resonant";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_RUNE_KNIGHT_RUNE] = "Rune Knight Rune";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_ALCHEMICAL_FORMULA] = "Alchemical Formula";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_MANEUVER] = "Maneuver";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_SUBCLASS] = "Subclass";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_SUBCLASS_FEATURE] = "Subclass Feature";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_ACTION] = "Action";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_LANGUAGE] = "Language";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_BOOK] = "Book";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_PAGE] = "Page";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_LEGENDARY_GROUP] = "Legendary Group";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_CHAR_CREATION_OPTIONS] = "Character Creation Option";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_RECIPES] = "Recipe";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_STATUS] = "Status";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_DECK] = "Deck";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_CARD] = "Card";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_SKILLS] = "Skill";
|
||
Parser.CAT_ID_TO_FULL[Parser.CAT_ID_SENSES] = "Sense";
|
||
|
||
Parser.pageCategoryToFull = function (catId) {
|
||
return Parser._parse_aToB(Parser.CAT_ID_TO_FULL, catId);
|
||
};
|
||
|
||
Parser.CAT_ID_TO_PROP = {};
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_CREATURE] = "monster";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_SPELL] = "spell";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_BACKGROUND] = "background";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_ITEM] = "item";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_CLASS] = "class";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_CONDITION] = "condition";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_FEAT] = "feat";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_PSIONIC] = "psionic";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_RACE] = "race";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_OTHER_REWARD] = "reward";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_VARIANT_OPTIONAL_RULE] = "variantrule";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_ADVENTURE] = "adventure";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_DEITY] = "deity";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_OBJECT] = "object";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_TRAP] = "trap";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_HAZARD] = "hazard";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_CULT] = "cult";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_BOON] = "boon";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_DISEASE] = "condition";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_TABLE] = "table";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_TABLE_GROUP] = "tableGroup";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_VEHICLE] = "vehicle";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_ELDRITCH_INVOCATION] = "optionalfeature";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_MANEUVER_CAVALIER] = "optionalfeature";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_ARCANE_SHOT] = "optionalfeature";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_OPTIONAL_FEATURE_OTHER] = "optionalfeature";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_FIGHTING_STYLE] = "optionalfeature";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_METAMAGIC] = "optionalfeature";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_MANEUVER_BATTLEMASTER] = "optionalfeature";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_PACT_BOON] = "optionalfeature";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_ELEMENTAL_DISCIPLINE] = "optionalfeature";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_ARTIFICER_INFUSION] = "optionalfeature";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_SHIP_UPGRADE] = "vehicleUpgrade";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_INFERNAL_WAR_MACHINE_UPGRADE] = "vehicleUpgrade";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_ONOMANCY_RESONANT] = "optionalfeature";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_RUNE_KNIGHT_RUNE] = "optionalfeature";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_ALCHEMICAL_FORMULA] = "optionalfeature";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_MANEUVER] = "optionalfeature";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_QUICKREF] = null;
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_CLASS_FEATURE] = "classFeature";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_SUBCLASS] = "subclass";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_SUBCLASS_FEATURE] = "subclassFeature";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_ACTION] = "action";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_LANGUAGE] = "language";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_BOOK] = "book";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_PAGE] = null;
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_LEGENDARY_GROUP] = "legendaryGroup";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_CHAR_CREATION_OPTIONS] = "charoption";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_RECIPES] = "recipe";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_STATUS] = "status";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_DECK] = "deck";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_CARD] = "card";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_SKILLS] = "skill";
|
||
Parser.CAT_ID_TO_PROP[Parser.CAT_ID_SENSES] = "sense";
|
||
|
||
Parser.pageCategoryToProp = function (catId) {
|
||
return Parser._parse_aToB(Parser.CAT_ID_TO_PROP, catId);
|
||
};
|
||
|
||
Parser.ABIL_ABVS = ["str", "dex", "con", "int", "wis", "cha"];
|
||
|
||
Parser.spClassesToCurrentAndLegacy = function (fromClassList) {
|
||
const current = [];
|
||
const legacy = [];
|
||
fromClassList.forEach(cls => {
|
||
if ((cls.name === "Artificer" && cls.source === "UAArtificer") || (cls.name === "Artificer (Revisited)" && cls.source === "UAArtificerRevisited")) legacy.push(cls);
|
||
else current.push(cls);
|
||
});
|
||
return [current, legacy];
|
||
};
|
||
|
||
/**
|
||
* Build a pair of strings; one with all current subclasses, one with all legacy subclasses
|
||
*
|
||
* @param sp a spell
|
||
* @param subclassLookup Data loaded from `generated/gendata-subclass-lookup.json`. Of the form: `{PHB: {Barbarian: {PHB: {Berserker: "Path of the Berserker"}}}}`
|
||
* @returns {*[]} A two-element array. First item is a string of all the current subclasses, second item a string of
|
||
* all the legacy/superseded subclasses
|
||
*/
|
||
Parser.spSubclassesToCurrentAndLegacyFull = function (sp, subclassLookup) {
|
||
return Parser._spSubclassesToCurrentAndLegacyFull({sp, subclassLookup, prop: "fromSubclass"});
|
||
};
|
||
|
||
Parser.spVariantSubclassesToCurrentAndLegacyFull = function (sp, subclassLookup) {
|
||
return Parser._spSubclassesToCurrentAndLegacyFull({sp, subclassLookup, prop: "fromSubclassVariant"});
|
||
};
|
||
|
||
Parser._spSubclassesToCurrentAndLegacyFull = ({sp, subclassLookup, prop}) => {
|
||
const fromSubclass = Renderer.spell.getCombinedClasses(sp, prop);
|
||
if (!fromSubclass.length) return ["", ""];
|
||
|
||
const current = [];
|
||
const legacy = [];
|
||
const curNames = new Set();
|
||
const toCheck = [];
|
||
fromSubclass
|
||
.filter(c => {
|
||
const excludeClass = ExcludeUtil.isExcluded(
|
||
UrlUtil.URL_TO_HASH_BUILDER[UrlUtil.PG_CLASSES]({name: c.class.name, source: c.class.source}),
|
||
"class",
|
||
c.class.source,
|
||
{isNoCount: true},
|
||
);
|
||
if (excludeClass) return false;
|
||
|
||
const excludeSubclass = ExcludeUtil.isExcluded(
|
||
UrlUtil.URL_TO_HASH_BUILDER["subclass"]({
|
||
shortName: c.subclass.shortName,
|
||
source: c.subclass.source,
|
||
className: c.class.name,
|
||
classSource: c.class.source,
|
||
}),
|
||
"subclass",
|
||
c.subclass.source,
|
||
{isNoCount: true},
|
||
);
|
||
if (excludeSubclass) return false;
|
||
|
||
return !Renderer.spell.isExcludedSubclassVariantSource({classDefinedInSource: c.class.definedInSource});
|
||
})
|
||
.sort((a, b) => {
|
||
const byName = SortUtil.ascSort(a.subclass.name, b.subclass.name);
|
||
return byName || SortUtil.ascSort(a.class.name, b.class.name);
|
||
})
|
||
.forEach(c => {
|
||
const nm = c.subclass.name;
|
||
const src = c.subclass.source;
|
||
|
||
const toAdd = Parser._spSubclassItem({fromSubclass: c, isTextOnly: false});
|
||
|
||
const fromLookup = MiscUtil.get(
|
||
subclassLookup,
|
||
c.class.source,
|
||
c.class.name,
|
||
c.subclass.source,
|
||
c.subclass.name,
|
||
);
|
||
|
||
if (fromLookup && fromLookup.isReprinted) {
|
||
legacy.push(toAdd);
|
||
} else if (SourceUtil.isNonstandardSource(src)) {
|
||
const cleanName = Parser._spSubclassesToCurrentAndLegacyFull.mapClassShortNameToMostRecent(
|
||
nm.split("(")[0].trim().split(/v\d+/)[0].trim(),
|
||
);
|
||
toCheck.push({"name": cleanName, "ele": toAdd});
|
||
} else {
|
||
current.push(toAdd);
|
||
curNames.add(nm);
|
||
}
|
||
});
|
||
|
||
toCheck.forEach(n => {
|
||
if (curNames.has(n.name)) {
|
||
legacy.push(n.ele);
|
||
} else {
|
||
current.push(n.ele);
|
||
}
|
||
});
|
||
|
||
return [current.join(", "), legacy.join(", ")];
|
||
};
|
||
|
||
/**
|
||
* Get the most recent iteration of a subclass name.
|
||
*/
|
||
Parser._spSubclassesToCurrentAndLegacyFull.mapClassShortNameToMostRecent = (shortName) => {
|
||
switch (shortName) {
|
||
case "Favored Soul": return "Divine Soul";
|
||
case "Undying Light": return "Celestial";
|
||
case "Deep Stalker": return "Gloom Stalker";
|
||
}
|
||
return shortName;
|
||
};
|
||
|
||
Parser.spVariantClassesToCurrentAndLegacy = function (fromVariantClassList) {
|
||
const current = [];
|
||
const legacy = [];
|
||
fromVariantClassList.forEach(cls => {
|
||
if (SourceUtil.isPrereleaseSource(cls.definedInSource)) legacy.push(cls);
|
||
else current.push(cls);
|
||
});
|
||
return [current, legacy];
|
||
};
|
||
|
||
Parser.attackTypeToFull = function (attackType) {
|
||
return Parser._parse_aToB(Parser.ATK_TYPE_TO_FULL, attackType);
|
||
};
|
||
|
||
Parser.trapHazTypeToFull = function (type) {
|
||
return Parser._parse_aToB(Parser.TRAP_HAZARD_TYPE_TO_FULL, type);
|
||
};
|
||
|
||
Parser.TRAP_HAZARD_TYPE_TO_FULL = {
|
||
MECH: "Mechanical Trap",
|
||
MAG: "Magical Trap",
|
||
SMPL: "Simple Trap",
|
||
CMPX: "Complex Trap",
|
||
HAZ: "Hazard",
|
||
WTH: "Weather",
|
||
ENV: "Environmental Hazard",
|
||
WLD: "Wilderness Hazard",
|
||
GEN: "Generic",
|
||
EST: "Eldritch Storm",
|
||
};
|
||
|
||
Parser.tierToFullLevel = function (tier) {
|
||
return Parser._parse_aToB(Parser.TIER_TO_FULL_LEVEL, tier);
|
||
};
|
||
|
||
Parser.TIER_TO_FULL_LEVEL = {};
|
||
Parser.TIER_TO_FULL_LEVEL[1] = "1st\u20134th Level";
|
||
Parser.TIER_TO_FULL_LEVEL[2] = "5th\u201310th Level";
|
||
Parser.TIER_TO_FULL_LEVEL[3] = "11th\u201316th Level";
|
||
Parser.TIER_TO_FULL_LEVEL[4] = "17th\u201320th Level";
|
||
|
||
Parser.trapInitToFull = function (init) {
|
||
return Parser._parse_aToB(Parser.TRAP_INIT_TO_FULL, init);
|
||
};
|
||
|
||
Parser.TRAP_INIT_TO_FULL = {};
|
||
Parser.TRAP_INIT_TO_FULL[1] = "initiative count 10";
|
||
Parser.TRAP_INIT_TO_FULL[2] = "initiative count 20";
|
||
Parser.TRAP_INIT_TO_FULL[3] = "initiative count 20 and initiative count 10";
|
||
|
||
Parser.ATK_TYPE_TO_FULL = {};
|
||
Parser.ATK_TYPE_TO_FULL["MW"] = "Melee Weapon Attack";
|
||
Parser.ATK_TYPE_TO_FULL["RW"] = "Ranged Weapon Attack";
|
||
|
||
Parser.bookOrdinalToAbv = (ordinal, preNoSuff) => {
|
||
if (ordinal === undefined) return "";
|
||
switch (ordinal.type) {
|
||
case "part": return `${preNoSuff ? " " : ""}Part ${ordinal.identifier}${preNoSuff ? "" : " \u2014 "}`;
|
||
case "chapter": return `${preNoSuff ? " " : ""}Ch. ${ordinal.identifier}${preNoSuff ? "" : ": "}`;
|
||
case "episode": return `${preNoSuff ? " " : ""}Ep. ${ordinal.identifier}${preNoSuff ? "" : ": "}`;
|
||
case "appendix": return `${preNoSuff ? " " : ""}App.${ordinal.identifier != null ? ` ${ordinal.identifier}` : ""}${preNoSuff ? "" : ": "}`;
|
||
case "level": return `${preNoSuff ? " " : ""}Level ${ordinal.identifier}${preNoSuff ? "" : ": "}`;
|
||
default: throw new Error(`Unhandled ordinal type "${ordinal.type}"`);
|
||
}
|
||
};
|
||
|
||
Parser.IMAGE_TYPE_TO_FULL = {
|
||
"map": "Map",
|
||
"mapPlayer": "Map (Player)",
|
||
};
|
||
Parser.imageTypeToFull = function (imageType) {
|
||
return Parser._parse_aToB(Parser.IMAGE_TYPE_TO_FULL, imageType, "Other");
|
||
};
|
||
|
||
Parser.nameToTokenName = function (name) {
|
||
return name
|
||
.toAscii()
|
||
.replace(/"/g, "");
|
||
};
|
||
|
||
Parser.bytesToHumanReadable = function (bytes, {fixedDigits = 2} = {}) {
|
||
if (bytes == null) return "";
|
||
if (!bytes) return "0 B";
|
||
const e = Math.floor(Math.log(bytes) / Math.log(1024));
|
||
return `${(bytes / Math.pow(1024, e)).toFixed(fixedDigits)} ${`\u200bKMGTP`.charAt(e)}B`;
|
||
};
|
||
|
||
Parser.SKL_ABV_ABJ = "A";
|
||
Parser.SKL_ABV_EVO = "V";
|
||
Parser.SKL_ABV_ENC = "E";
|
||
Parser.SKL_ABV_ILL = "I";
|
||
Parser.SKL_ABV_DIV = "D";
|
||
Parser.SKL_ABV_NEC = "N";
|
||
Parser.SKL_ABV_TRA = "T";
|
||
Parser.SKL_ABV_CON = "C";
|
||
Parser.SKL_ABV_PSI = "P";
|
||
Parser.SKL_ABVS = [
|
||
Parser.SKL_ABV_ABJ,
|
||
Parser.SKL_ABV_CON,
|
||
Parser.SKL_ABV_DIV,
|
||
Parser.SKL_ABV_ENC,
|
||
Parser.SKL_ABV_EVO,
|
||
Parser.SKL_ABV_ILL,
|
||
Parser.SKL_ABV_NEC,
|
||
Parser.SKL_ABV_PSI,
|
||
Parser.SKL_ABV_TRA,
|
||
];
|
||
|
||
Parser.SP_TM_ACTION = "action";
|
||
Parser.SP_TM_B_ACTION = "bonus";
|
||
Parser.SP_TM_REACTION = "reaction";
|
||
Parser.SP_TM_ROUND = "round";
|
||
Parser.SP_TM_MINS = "minute";
|
||
Parser.SP_TM_HRS = "hour";
|
||
Parser.SP_TM_SPECIAL = "special";
|
||
Parser.SP_TIME_SINGLETONS = [Parser.SP_TM_ACTION, Parser.SP_TM_B_ACTION, Parser.SP_TM_REACTION, Parser.SP_TM_ROUND];
|
||
Parser.SP_TIME_TO_FULL = {
|
||
[Parser.SP_TM_ACTION]: "Action",
|
||
[Parser.SP_TM_B_ACTION]: "Bonus Action",
|
||
[Parser.SP_TM_REACTION]: "Reaction",
|
||
[Parser.SP_TM_ROUND]: "Rounds",
|
||
[Parser.SP_TM_MINS]: "Minutes",
|
||
[Parser.SP_TM_HRS]: "Hours",
|
||
[Parser.SP_TM_SPECIAL]: "Special",
|
||
};
|
||
Parser.spTimeUnitToFull = function (timeUnit) {
|
||
return Parser._parse_aToB(Parser.SP_TIME_TO_FULL, timeUnit);
|
||
};
|
||
|
||
Parser.SP_TIME_TO_SHORT = {
|
||
[Parser.SP_TM_ROUND]: "Rnd.",
|
||
[Parser.SP_TM_MINS]: "Min.",
|
||
[Parser.SP_TM_HRS]: "Hr.",
|
||
};
|
||
Parser.spTimeUnitToShort = function (timeUnit) {
|
||
return Parser._parse_aToB(Parser.SP_TIME_TO_SHORT, timeUnit);
|
||
};
|
||
|
||
Parser.SP_TIME_TO_ABV = {
|
||
[Parser.SP_TM_ACTION]: "A",
|
||
[Parser.SP_TM_B_ACTION]: "BA",
|
||
[Parser.SP_TM_REACTION]: "R",
|
||
[Parser.SP_TM_ROUND]: "rnd",
|
||
[Parser.SP_TM_MINS]: "min",
|
||
[Parser.SP_TM_HRS]: "hr",
|
||
[Parser.SP_TM_SPECIAL]: "SPC",
|
||
};
|
||
Parser.spTimeUnitToAbv = function (timeUnit) {
|
||
return Parser._parse_aToB(Parser.SP_TIME_TO_ABV, timeUnit);
|
||
};
|
||
|
||
Parser.spTimeToShort = function (time, isHtml) {
|
||
if (!time) return "";
|
||
return (time.number === 1 && Parser.SP_TIME_SINGLETONS.includes(time.unit))
|
||
? `${Parser.spTimeUnitToAbv(time.unit).uppercaseFirst()}${time.condition ? "*" : ""}`
|
||
: `${time.number} ${isHtml ? `<span class="ve-small">` : ""}${Parser.spTimeUnitToAbv(time.unit)}${isHtml ? `</span>` : ""}${time.condition ? "*" : ""}`;
|
||
};
|
||
|
||
Parser.SKL_ABJ = "Abjuration";
|
||
Parser.SKL_EVO = "Evocation";
|
||
Parser.SKL_ENC = "Enchantment";
|
||
Parser.SKL_ILL = "Illusion";
|
||
Parser.SKL_DIV = "Divination";
|
||
Parser.SKL_NEC = "Necromancy";
|
||
Parser.SKL_TRA = "Transmutation";
|
||
Parser.SKL_CON = "Conjuration";
|
||
Parser.SKL_PSI = "Psionic";
|
||
|
||
Parser.SP_SCHOOL_ABV_TO_FULL = {};
|
||
Parser.SP_SCHOOL_ABV_TO_FULL[Parser.SKL_ABV_ABJ] = Parser.SKL_ABJ;
|
||
Parser.SP_SCHOOL_ABV_TO_FULL[Parser.SKL_ABV_EVO] = Parser.SKL_EVO;
|
||
Parser.SP_SCHOOL_ABV_TO_FULL[Parser.SKL_ABV_ENC] = Parser.SKL_ENC;
|
||
Parser.SP_SCHOOL_ABV_TO_FULL[Parser.SKL_ABV_ILL] = Parser.SKL_ILL;
|
||
Parser.SP_SCHOOL_ABV_TO_FULL[Parser.SKL_ABV_DIV] = Parser.SKL_DIV;
|
||
Parser.SP_SCHOOL_ABV_TO_FULL[Parser.SKL_ABV_NEC] = Parser.SKL_NEC;
|
||
Parser.SP_SCHOOL_ABV_TO_FULL[Parser.SKL_ABV_TRA] = Parser.SKL_TRA;
|
||
Parser.SP_SCHOOL_ABV_TO_FULL[Parser.SKL_ABV_CON] = Parser.SKL_CON;
|
||
Parser.SP_SCHOOL_ABV_TO_FULL[Parser.SKL_ABV_PSI] = Parser.SKL_PSI;
|
||
|
||
Parser.SP_SCHOOL_ABV_TO_SHORT = {};
|
||
Parser.SP_SCHOOL_ABV_TO_SHORT[Parser.SKL_ABV_ABJ] = "Abj.";
|
||
Parser.SP_SCHOOL_ABV_TO_SHORT[Parser.SKL_ABV_EVO] = "Evoc.";
|
||
Parser.SP_SCHOOL_ABV_TO_SHORT[Parser.SKL_ABV_ENC] = "Ench.";
|
||
Parser.SP_SCHOOL_ABV_TO_SHORT[Parser.SKL_ABV_ILL] = "Illu.";
|
||
Parser.SP_SCHOOL_ABV_TO_SHORT[Parser.SKL_ABV_DIV] = "Divin.";
|
||
Parser.SP_SCHOOL_ABV_TO_SHORT[Parser.SKL_ABV_NEC] = "Necro.";
|
||
Parser.SP_SCHOOL_ABV_TO_SHORT[Parser.SKL_ABV_TRA] = "Trans.";
|
||
Parser.SP_SCHOOL_ABV_TO_SHORT[Parser.SKL_ABV_CON] = "Conj.";
|
||
Parser.SP_SCHOOL_ABV_TO_SHORT[Parser.SKL_ABV_PSI] = "Psi.";
|
||
|
||
Parser.ATB_ABV_TO_FULL = {
|
||
"str": "Strength",
|
||
"dex": "Dexterity",
|
||
"con": "Constitution",
|
||
"int": "Intelligence",
|
||
"wis": "Wisdom",
|
||
"cha": "Charisma",
|
||
};
|
||
|
||
Parser.TP_ABERRATION = "aberration";
|
||
Parser.TP_BEAST = "beast";
|
||
Parser.TP_CELESTIAL = "celestial";
|
||
Parser.TP_CONSTRUCT = "construct";
|
||
Parser.TP_DRAGON = "dragon";
|
||
Parser.TP_ELEMENTAL = "elemental";
|
||
Parser.TP_FEY = "fey";
|
||
Parser.TP_FIEND = "fiend";
|
||
Parser.TP_GIANT = "giant";
|
||
Parser.TP_HUMANOID = "humanoid";
|
||
Parser.TP_MONSTROSITY = "monstrosity";
|
||
Parser.TP_OOZE = "ooze";
|
||
Parser.TP_PLANT = "plant";
|
||
Parser.TP_UNDEAD = "undead";
|
||
Parser.MON_TYPES = [Parser.TP_ABERRATION, Parser.TP_BEAST, Parser.TP_CELESTIAL, Parser.TP_CONSTRUCT, Parser.TP_DRAGON, Parser.TP_ELEMENTAL, Parser.TP_FEY, Parser.TP_FIEND, Parser.TP_GIANT, Parser.TP_HUMANOID, Parser.TP_MONSTROSITY, Parser.TP_OOZE, Parser.TP_PLANT, Parser.TP_UNDEAD];
|
||
Parser.MON_TYPE_TO_PLURAL = {};
|
||
Parser.MON_TYPE_TO_PLURAL[Parser.TP_ABERRATION] = "aberrations";
|
||
Parser.MON_TYPE_TO_PLURAL[Parser.TP_BEAST] = "beasts";
|
||
Parser.MON_TYPE_TO_PLURAL[Parser.TP_CELESTIAL] = "celestials";
|
||
Parser.MON_TYPE_TO_PLURAL[Parser.TP_CONSTRUCT] = "constructs";
|
||
Parser.MON_TYPE_TO_PLURAL[Parser.TP_DRAGON] = "dragons";
|
||
Parser.MON_TYPE_TO_PLURAL[Parser.TP_ELEMENTAL] = "elementals";
|
||
Parser.MON_TYPE_TO_PLURAL[Parser.TP_FEY] = "fey";
|
||
Parser.MON_TYPE_TO_PLURAL[Parser.TP_FIEND] = "fiends";
|
||
Parser.MON_TYPE_TO_PLURAL[Parser.TP_GIANT] = "giants";
|
||
Parser.MON_TYPE_TO_PLURAL[Parser.TP_HUMANOID] = "humanoids";
|
||
Parser.MON_TYPE_TO_PLURAL[Parser.TP_MONSTROSITY] = "monstrosities";
|
||
Parser.MON_TYPE_TO_PLURAL[Parser.TP_OOZE] = "oozes";
|
||
Parser.MON_TYPE_TO_PLURAL[Parser.TP_PLANT] = "plants";
|
||
Parser.MON_TYPE_TO_PLURAL[Parser.TP_UNDEAD] = "undead";
|
||
|
||
Parser.SZ_FINE = "F";
|
||
Parser.SZ_DIMINUTIVE = "D";
|
||
Parser.SZ_TINY = "T";
|
||
Parser.SZ_SMALL = "S";
|
||
Parser.SZ_MEDIUM = "M";
|
||
Parser.SZ_LARGE = "L";
|
||
Parser.SZ_HUGE = "H";
|
||
Parser.SZ_GARGANTUAN = "G";
|
||
Parser.SZ_COLOSSAL = "C";
|
||
Parser.SZ_VARIES = "V";
|
||
Parser.SIZE_ABVS = [Parser.SZ_TINY, Parser.SZ_SMALL, Parser.SZ_MEDIUM, Parser.SZ_LARGE, Parser.SZ_HUGE, Parser.SZ_GARGANTUAN, Parser.SZ_VARIES];
|
||
Parser.SIZE_ABV_TO_FULL = {};
|
||
Parser.SIZE_ABV_TO_FULL[Parser.SZ_FINE] = "Fine";
|
||
Parser.SIZE_ABV_TO_FULL[Parser.SZ_DIMINUTIVE] = "Diminutive";
|
||
Parser.SIZE_ABV_TO_FULL[Parser.SZ_TINY] = "Tiny";
|
||
Parser.SIZE_ABV_TO_FULL[Parser.SZ_SMALL] = "Small";
|
||
Parser.SIZE_ABV_TO_FULL[Parser.SZ_MEDIUM] = "Medium";
|
||
Parser.SIZE_ABV_TO_FULL[Parser.SZ_LARGE] = "Large";
|
||
Parser.SIZE_ABV_TO_FULL[Parser.SZ_HUGE] = "Huge";
|
||
Parser.SIZE_ABV_TO_FULL[Parser.SZ_GARGANTUAN] = "Gargantuan";
|
||
Parser.SIZE_ABV_TO_FULL[Parser.SZ_COLOSSAL] = "Colossal";
|
||
Parser.SIZE_ABV_TO_FULL[Parser.SZ_VARIES] = "Varies";
|
||
|
||
Parser.XP_CHART_ALT = {
|
||
"0": 10,
|
||
"1/8": 25,
|
||
"1/4": 50,
|
||
"1/2": 100,
|
||
"1": 200,
|
||
"2": 450,
|
||
"3": 700,
|
||
"4": 1100,
|
||
"5": 1800,
|
||
"6": 2300,
|
||
"7": 2900,
|
||
"8": 3900,
|
||
"9": 5000,
|
||
"10": 5900,
|
||
"11": 7200,
|
||
"12": 8400,
|
||
"13": 10000,
|
||
"14": 11500,
|
||
"15": 13000,
|
||
"16": 15000,
|
||
"17": 18000,
|
||
"18": 20000,
|
||
"19": 22000,
|
||
"20": 25000,
|
||
"21": 33000,
|
||
"22": 41000,
|
||
"23": 50000,
|
||
"24": 62000,
|
||
"25": 75000,
|
||
"26": 90000,
|
||
"27": 105000,
|
||
"28": 120000,
|
||
"29": 135000,
|
||
"30": 155000,
|
||
};
|
||
|
||
Parser.ARMOR_ABV_TO_FULL = {
|
||
"l.": "light",
|
||
"m.": "medium",
|
||
"h.": "heavy",
|
||
};
|
||
|
||
Parser.WEAPON_ABV_TO_FULL = {
|
||
"s.": "simple",
|
||
"m.": "martial",
|
||
};
|
||
|
||
Parser.CONDITION_TO_COLOR = {
|
||
"Blinded": "#525252",
|
||
"Charmed": "#f01789",
|
||
"Deafened": "#ababab",
|
||
"Exhausted": "#947a47",
|
||
"Frightened": "#c9ca18",
|
||
"Grappled": "#8784a0",
|
||
"Incapacitated": "#3165a0",
|
||
"Invisible": "#7ad2d6",
|
||
"Paralyzed": "#c00900",
|
||
"Petrified": "#a0a0a0",
|
||
"Poisoned": "#4dc200",
|
||
"Prone": "#5e60a0",
|
||
"Restrained": "#d98000",
|
||
"Stunned": "#a23bcb",
|
||
"Unconscious": "#3a40ad",
|
||
|
||
"Concentration": "#009f7a",
|
||
};
|
||
|
||
Parser.RULE_TYPE_TO_FULL = {
|
||
"O": "Optional",
|
||
"P": "Prerelease",
|
||
"V": "Variant",
|
||
"VO": "Variant Optional",
|
||
"VV": "Variant Variant",
|
||
"U": "Unknown",
|
||
};
|
||
|
||
Parser.ruleTypeToFull = function (ruleType) {
|
||
return Parser._parse_aToB(Parser.RULE_TYPE_TO_FULL, ruleType);
|
||
};
|
||
|
||
Parser.VEHICLE_TYPE_TO_FULL = {
|
||
"SHIP": "Ship",
|
||
"SPELLJAMMER": "Spelljammer Ship",
|
||
"INFWAR": "Infernal War Machine",
|
||
"CREATURE": "Creature",
|
||
"OBJECT": "Object",
|
||
"SHP:H": "Ship Upgrade, Hull",
|
||
"SHP:M": "Ship Upgrade, Movement",
|
||
"SHP:W": "Ship Upgrade, Weapon",
|
||
"SHP:F": "Ship Upgrade, Figurehead",
|
||
"SHP:O": "Ship Upgrade, Miscellaneous",
|
||
"IWM:W": "Infernal War Machine Variant, Weapon",
|
||
"IWM:A": "Infernal War Machine Upgrade, Armor",
|
||
"IWM:G": "Infernal War Machine Upgrade, Gadget",
|
||
};
|
||
|
||
Parser.vehicleTypeToFull = function (vehicleType) {
|
||
return Parser._parse_aToB(Parser.VEHICLE_TYPE_TO_FULL, vehicleType);
|
||
};
|
||
|
||
// SOURCES =============================================================================================================
|
||
|
||
Parser.SRC_5ETOOLS_TMP = "SRC_5ETOOLS_TMP"; // Temp source, used as a placeholder value
|
||
|
||
Parser.SRC_CoS = "CoS";
|
||
Parser.SRC_DMG = "DMG";
|
||
Parser.SRC_EEPC = "EEPC";
|
||
Parser.SRC_EET = "EET";
|
||
Parser.SRC_HotDQ = "HotDQ";
|
||
Parser.SRC_LMoP = "LMoP";
|
||
Parser.SRC_MM = "MM";
|
||
Parser.SRC_OotA = "OotA";
|
||
Parser.SRC_PHB = "PHB";
|
||
Parser.SRC_PotA = "PotA";
|
||
Parser.SRC_RoT = "RoT";
|
||
Parser.SRC_RoTOS = "RoTOS";
|
||
Parser.SRC_SCAG = "SCAG";
|
||
Parser.SRC_SKT = "SKT";
|
||
Parser.SRC_ToA = "ToA";
|
||
Parser.SRC_TLK = "TLK";
|
||
Parser.SRC_ToD = "ToD";
|
||
Parser.SRC_TTP = "TTP";
|
||
Parser.SRC_TYP = "TftYP";
|
||
Parser.SRC_TYP_AtG = "TftYP-AtG";
|
||
Parser.SRC_TYP_DiT = "TftYP-DiT";
|
||
Parser.SRC_TYP_TFoF = "TftYP-TFoF";
|
||
Parser.SRC_TYP_THSoT = "TftYP-THSoT";
|
||
Parser.SRC_TYP_TSC = "TftYP-TSC";
|
||
Parser.SRC_TYP_ToH = "TftYP-ToH";
|
||
Parser.SRC_TYP_WPM = "TftYP-WPM";
|
||
Parser.SRC_VGM = "VGM";
|
||
Parser.SRC_XGE = "XGE";
|
||
Parser.SRC_OGA = "OGA";
|
||
Parser.SRC_MTF = "MTF";
|
||
Parser.SRC_WDH = "WDH";
|
||
Parser.SRC_WDMM = "WDMM";
|
||
Parser.SRC_GGR = "GGR";
|
||
Parser.SRC_KKW = "KKW";
|
||
Parser.SRC_LLK = "LLK";
|
||
Parser.SRC_AZfyT = "AZfyT";
|
||
Parser.SRC_GoS = "GoS";
|
||
Parser.SRC_AI = "AI";
|
||
Parser.SRC_OoW = "OoW";
|
||
Parser.SRC_ESK = "ESK";
|
||
Parser.SRC_DIP = "DIP";
|
||
Parser.SRC_HftT = "HftT";
|
||
Parser.SRC_DC = "DC";
|
||
Parser.SRC_SLW = "SLW";
|
||
Parser.SRC_SDW = "SDW";
|
||
Parser.SRC_BGDIA = "BGDIA";
|
||
Parser.SRC_LR = "LR";
|
||
Parser.SRC_AL = "AL";
|
||
Parser.SRC_SAC = "SAC";
|
||
Parser.SRC_ERLW = "ERLW";
|
||
Parser.SRC_EFR = "EFR";
|
||
Parser.SRC_RMBRE = "RMBRE";
|
||
Parser.SRC_RMR = "RMR";
|
||
Parser.SRC_MFF = "MFF";
|
||
Parser.SRC_AWM = "AWM";
|
||
Parser.SRC_IMR = "IMR";
|
||
Parser.SRC_SADS = "SADS";
|
||
Parser.SRC_EGW = "EGW";
|
||
Parser.SRC_EGW_ToR = "ToR";
|
||
Parser.SRC_EGW_DD = "DD";
|
||
Parser.SRC_EGW_FS = "FS";
|
||
Parser.SRC_EGW_US = "US";
|
||
Parser.SRC_MOT = "MOT";
|
||
Parser.SRC_IDRotF = "IDRotF";
|
||
Parser.SRC_TCE = "TCE";
|
||
Parser.SRC_VRGR = "VRGR";
|
||
Parser.SRC_HoL = "HoL";
|
||
Parser.SRC_XMtS = "XMtS";
|
||
Parser.SRC_RtG = "RtG";
|
||
Parser.SRC_AitFR = "AitFR";
|
||
Parser.SRC_AitFR_ISF = "AitFR-ISF";
|
||
Parser.SRC_AitFR_THP = "AitFR-THP";
|
||
Parser.SRC_AitFR_AVT = "AitFR-AVT";
|
||
Parser.SRC_AitFR_DN = "AitFR-DN";
|
||
Parser.SRC_AitFR_FCD = "AitFR-FCD";
|
||
Parser.SRC_WBtW = "WBtW";
|
||
Parser.SRC_DoD = "DoD";
|
||
Parser.SRC_MaBJoV = "MaBJoV";
|
||
Parser.SRC_FTD = "FTD";
|
||
Parser.SRC_SCC = "SCC";
|
||
Parser.SRC_SCC_CK = "SCC-CK";
|
||
Parser.SRC_SCC_HfMT = "SCC-HfMT";
|
||
Parser.SRC_SCC_TMM = "SCC-TMM";
|
||
Parser.SRC_SCC_ARiR = "SCC-ARiR";
|
||
Parser.SRC_MPMM = "MPMM";
|
||
Parser.SRC_CRCotN = "CRCotN";
|
||
Parser.SRC_JttRC = "JttRC";
|
||
Parser.SRC_SAiS = "SAiS";
|
||
Parser.SRC_AAG = "AAG";
|
||
Parser.SRC_BAM = "BAM";
|
||
Parser.SRC_LoX = "LoX";
|
||
Parser.SRC_DoSI = "DoSI";
|
||
Parser.SRC_DSotDQ = "DSotDQ";
|
||
Parser.SRC_KftGV = "KftGV";
|
||
Parser.SRC_BGG = "BGG";
|
||
Parser.SRC_TDCSR = "TDCSR";
|
||
Parser.SRC_PaBTSO = "PaBTSO";
|
||
Parser.SRC_PAitM = "PAitM";
|
||
Parser.SRC_SatO = "SatO";
|
||
Parser.SRC_ToFW = "ToFW";
|
||
Parser.SRC_MPP = "MPP";
|
||
Parser.SRC_BMT = "BMT";
|
||
Parser.SRC_DMTCRG = "DMTCRG";
|
||
Parser.SRC_QftIS = "QftIS";
|
||
Parser.SRC_VEoR = "VEoR";
|
||
Parser.SRC_GHLoE = "GHLoE";
|
||
Parser.SRC_DoDk = "DoDk";
|
||
Parser.SRC_HWCS = "HWCS";
|
||
Parser.SRC_HWAitW = "HWAitW";
|
||
Parser.SRC_ToB1_2023 = "ToB1-2023";
|
||
Parser.SRC_TD = "TD";
|
||
Parser.SRC_SCREEN = "Screen";
|
||
Parser.SRC_SCREEN_WILDERNESS_KIT = "ScreenWildernessKit";
|
||
Parser.SRC_SCREEN_DUNGEON_KIT = "ScreenDungeonKit";
|
||
Parser.SRC_SCREEN_SPELLJAMMER = "ScreenSpelljammer";
|
||
Parser.SRC_HF = "HF";
|
||
Parser.SRC_HFFotM = "HFFotM";
|
||
Parser.SRC_HFStCM = "HFStCM";
|
||
Parser.SRC_CM = "CM";
|
||
Parser.SRC_NRH = "NRH";
|
||
Parser.SRC_NRH_TCMC = "NRH-TCMC";
|
||
Parser.SRC_NRH_AVitW = "NRH-AVitW";
|
||
Parser.SRC_NRH_ASS = "NRH-ASS"; // lmao
|
||
Parser.SRC_NRH_CoI = "NRH-CoI";
|
||
Parser.SRC_NRH_TLT = "NRH-TLT";
|
||
Parser.SRC_NRH_AWoL = "NRH-AWoL";
|
||
Parser.SRC_NRH_AT = "NRH-AT";
|
||
Parser.SRC_MGELFT = "MGELFT";
|
||
Parser.SRC_VD = "VD";
|
||
Parser.SRC_SjA = "SjA";
|
||
Parser.SRC_HAT_TG = "HAT-TG";
|
||
Parser.SRC_HAT_LMI = "HAT-LMI";
|
||
Parser.SRC_GotSF = "GotSF";
|
||
Parser.SRC_LK = "LK";
|
||
Parser.SRC_CoA = "CoA";
|
||
Parser.SRC_PiP = "PiP";
|
||
Parser.SRC_DitLCoT = "DitLCoT";
|
||
Parser.SRC_VNotEE = "VNotEE";
|
||
Parser.SRC_LRDT = "LRDT";
|
||
|
||
Parser.SRC_AL_PREFIX = "AL";
|
||
|
||
Parser.SRC_ALCoS = `${Parser.SRC_AL_PREFIX}CurseOfStrahd`;
|
||
Parser.SRC_ALEE = `${Parser.SRC_AL_PREFIX}ElementalEvil`;
|
||
Parser.SRC_ALRoD = `${Parser.SRC_AL_PREFIX}RageOfDemons`;
|
||
|
||
Parser.SRC_PS_PREFIX = "PS";
|
||
|
||
Parser.SRC_PSA = `${Parser.SRC_PS_PREFIX}A`;
|
||
Parser.SRC_PSI = `${Parser.SRC_PS_PREFIX}I`;
|
||
Parser.SRC_PSK = `${Parser.SRC_PS_PREFIX}K`;
|
||
Parser.SRC_PSZ = `${Parser.SRC_PS_PREFIX}Z`;
|
||
Parser.SRC_PSX = `${Parser.SRC_PS_PREFIX}X`;
|
||
Parser.SRC_PSD = `${Parser.SRC_PS_PREFIX}D`;
|
||
|
||
Parser.SRC_UA_PREFIX = "UA";
|
||
Parser.SRC_UA_ONE_PREFIX = "XUA";
|
||
Parser.SRC_MCVX_PREFIX = "MCV";
|
||
Parser.SRC_MisMVX_PREFIX = "MisMV";
|
||
Parser.SRC_AA_PREFIX = "AA";
|
||
|
||
Parser.SRC_UATMC = `${Parser.SRC_UA_PREFIX}TheMysticClass`;
|
||
Parser.SRC_MCV1SC = `${Parser.SRC_MCVX_PREFIX}1SC`;
|
||
Parser.SRC_MCV2DC = `${Parser.SRC_MCVX_PREFIX}2DC`;
|
||
Parser.SRC_MCV3MC = `${Parser.SRC_MCVX_PREFIX}3MC`;
|
||
Parser.SRC_MCV4EC = `${Parser.SRC_MCVX_PREFIX}4EC`;
|
||
Parser.SRC_MisMV1 = `${Parser.SRC_MisMVX_PREFIX}1`;
|
||
Parser.SRC_AATM = `${Parser.SRC_AA_PREFIX}TM`;
|
||
|
||
Parser.AL_PREFIX = "Adventurers League: ";
|
||
Parser.AL_PREFIX_SHORT = "AL: ";
|
||
Parser.PS_PREFIX = "Plane Shift: ";
|
||
Parser.PS_PREFIX_SHORT = "PS: ";
|
||
Parser.UA_PREFIX = "Unearthed Arcana: ";
|
||
Parser.UA_PREFIX_SHORT = "UA: ";
|
||
Parser.TftYP_NAME = "Tales from the Yawning Portal";
|
||
Parser.AitFR_NAME = "Adventures in the Forgotten Realms";
|
||
Parser.NRH_NAME = "NERDS Restoring Harmony";
|
||
Parser.MCVX_PREFIX = "Monstrous Compendium Volume ";
|
||
Parser.MisMVX_PREFIX = "Misplaced Monsters: Volume ";
|
||
Parser.AA_PREFIX = "Adventure Atlas: ";
|
||
|
||
Parser.SOURCE_JSON_TO_FULL = {};
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_CoS] = "Curse of Strahd";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_DMG] = "Dungeon Master's Guide";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_EEPC] = "Elemental Evil Player's Companion";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_EET] = "Elemental Evil: Trinkets";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_HotDQ] = "Hoard of the Dragon Queen";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_LMoP] = "Lost Mine of Phandelver";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_MM] = "Monster Manual";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_OotA] = "Out of the Abyss";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_PHB] = "Player's Handbook";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_PotA] = "Princes of the Apocalypse";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_RoT] = "The Rise of Tiamat";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_RoTOS] = "The Rise of Tiamat Online Supplement";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_SCAG] = "Sword Coast Adventurer's Guide";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_SKT] = "Storm King's Thunder";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_ToA] = "Tomb of Annihilation";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_TLK] = "The Lost Kenku";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_ToD] = "Tyranny of Dragons";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_TTP] = "The Tortle Package";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_TYP] = Parser.TftYP_NAME;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_TYP_AtG] = `${Parser.TftYP_NAME}: Against the Giants`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_TYP_DiT] = `${Parser.TftYP_NAME}: Dead in Thay`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_TYP_TFoF] = `${Parser.TftYP_NAME}: The Forge of Fury`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_TYP_THSoT] = `${Parser.TftYP_NAME}: The Hidden Shrine of Tamoachan`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_TYP_TSC] = `${Parser.TftYP_NAME}: The Sunless Citadel`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_TYP_ToH] = `${Parser.TftYP_NAME}: Tomb of Horrors`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_TYP_WPM] = `${Parser.TftYP_NAME}: White Plume Mountain`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_VGM] = "Volo's Guide to Monsters";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_XGE] = "Xanathar's Guide to Everything";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_OGA] = "One Grung Above";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_MTF] = "Mordenkainen's Tome of Foes";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_WDH] = "Waterdeep: Dragon Heist";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_WDMM] = "Waterdeep: Dungeon of the Mad Mage";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_GGR] = "Guildmasters' Guide to Ravnica";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_KKW] = "Krenko's Way";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_LLK] = "Lost Laboratory of Kwalish";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_AZfyT] = "A Zib for your Thoughts";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_GoS] = "Ghosts of Saltmarsh";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_AI] = "Acquisitions Incorporated";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_OoW] = "The Orrery of the Wanderer";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_ESK] = "Essentials Kit";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_DIP] = "Dragon of Icespire Peak";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_HftT] = "Hunt for the Thessalhydra";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_DC] = "Divine Contention";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_SLW] = "Storm Lord's Wrath";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_SDW] = "Sleeping Dragon's Wake";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_BGDIA] = "Baldur's Gate: Descent Into Avernus";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_LR] = "Locathah Rising";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_AL] = "Adventurers' League";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_SAC] = "Sage Advice Compendium";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_ERLW] = "Eberron: Rising from the Last War";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_EFR] = "Eberron: Forgotten Relics";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_RMBRE] = "The Lost Dungeon of Rickedness: Big Rick Energy";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_RMR] = "Dungeons & Dragons vs. Rick and Morty: Basic Rules";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_MFF] = "Mordenkainen's Fiendish Folio";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_AWM] = "Adventure with Muk";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_IMR] = "Infernal Machine Rebuild";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_SADS] = "Sapphire Anniversary Dice Set";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_EGW] = "Explorer's Guide to Wildemount";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_EGW_ToR] = "Tide of Retribution";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_EGW_DD] = "Dangerous Designs";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_EGW_FS] = "Frozen Sick";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_EGW_US] = "Unwelcome Spirits";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_MOT] = "Mythic Odysseys of Theros";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_IDRotF] = "Icewind Dale: Rime of the Frostmaiden";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_TCE] = "Tasha's Cauldron of Everything";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_VRGR] = "Van Richten's Guide to Ravenloft";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_HoL] = "The House of Lament";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_RtG] = "Return to Glory";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_AitFR] = Parser.AitFR_NAME;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_AitFR_ISF] = `${Parser.AitFR_NAME}: In Scarlet Flames`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_AitFR_THP] = `${Parser.AitFR_NAME}: The Hidden Page`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_AitFR_AVT] = `${Parser.AitFR_NAME}: A Verdant Tomb`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_AitFR_DN] = `${Parser.AitFR_NAME}: Deepest Night`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_AitFR_FCD] = `${Parser.AitFR_NAME}: From Cyan Depths`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_WBtW] = "The Wild Beyond the Witchlight";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_DoD] = "Domains of Delight";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_MaBJoV] = "Minsc and Boo's Journal of Villainy";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_FTD] = "Fizban's Treasury of Dragons";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_SCC] = "Strixhaven: A Curriculum of Chaos";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_SCC_CK] = "Campus Kerfuffle";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_SCC_HfMT] = "Hunt for Mage Tower";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_SCC_TMM] = "The Magister's Masquerade";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_SCC_ARiR] = "A Reckoning in Ruins";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_MPMM] = "Mordenkainen Presents: Monsters of the Multiverse";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_CRCotN] = "Critical Role: Call of the Netherdeep";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_JttRC] = "Journeys through the Radiant Citadel";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_SAiS] = "Spelljammer: Adventures in Space";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_AAG] = "Astral Adventurer's Guide";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_BAM] = "Boo's Astral Menagerie";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_LoX] = "Light of Xaryxis";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_DoSI] = "Dragons of Stormwreck Isle";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_DSotDQ] = "Dragonlance: Shadow of the Dragon Queen";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_KftGV] = "Keys from the Golden Vault";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_BGG] = "Bigby Presents: Glory of the Giants";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_TDCSR] = "Tal'Dorei Campaign Setting Reborn";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_PaBTSO] = "Phandelver and Below: The Shattered Obelisk";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_PAitM] = "Planescape: Adventures in the Multiverse";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_SatO] = "Sigil and the Outlands";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_ToFW] = "Turn of Fortune's Wheel";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_MPP] = "Morte's Planar Parade";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_BMT] = "The Book of Many Things";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_DMTCRG] = "The Deck of Many Things: Card Reference Guide";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_QftIS] = "Quests from the Infinite Staircase";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_VEoR] = "Vecna: Eve of Ruin";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_GHLoE] = "Grim Hollow: Lairs of Etharis";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_DoDk] = "Dungeons of Drakkenheim";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_HWCS] = "Humblewood Campaign Setting";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_HWAitW] = "Humblewood: Adventure in the Wood";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_ToB1_2023] = "Tome of Beasts 1 (2023 Edition)";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_TD] = "Tarot Deck";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_SCREEN] = "Dungeon Master's Screen";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_SCREEN_WILDERNESS_KIT] = "Dungeon Master's Screen: Wilderness Kit";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_SCREEN_DUNGEON_KIT] = "Dungeon Master's Screen: Dungeon Kit";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_SCREEN_SPELLJAMMER] = "Dungeon Master's Screen: Spelljammer";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_HF] = "Heroes' Feast";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_HFFotM] = "Heroes' Feast: Flavors of the Multiverse";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_HFStCM] = "Heroes' Feast: Saving the Childrens Menu";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_CM] = "Candlekeep Mysteries";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_NRH] = Parser.NRH_NAME;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_NRH_TCMC] = `${Parser.NRH_NAME}: The Candy Mountain Caper`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_NRH_AVitW] = `${Parser.NRH_NAME}: A Voice in the Wilderness`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_NRH_ASS] = `${Parser.NRH_NAME}: A Sticky Situation`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_NRH_CoI] = `${Parser.NRH_NAME}: Circus of Illusions`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_NRH_TLT] = `${Parser.NRH_NAME}: The Lost Tomb`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_NRH_AWoL] = `${Parser.NRH_NAME}: A Web of Lies`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_NRH_AT] = `${Parser.NRH_NAME}: Adventure Together`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_MGELFT] = "Muk's Guide To Everything He Learned From Tasha";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_VD] = "Vecna Dossier";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_SjA] = "Spelljammer Academy";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_HAT_TG] = "Honor Among Thieves: Thieves' Gallery";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_HAT_LMI] = "Honor Among Thieves: Legendary Magic Items";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_GotSF] = "Giants of the Star Forge";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_LK] = "Lightning Keep";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_CoA] = "Chains of Asmodeus";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_PiP] = "Peril in Pinebrook";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_DitLCoT] = "Descent into the Lost Caverns of Tsojcanth";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_VNotEE] = "Vecna: Nest of the Eldritch Eye";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_LRDT] = "Red Dragon's Tale: A LEGO Adventure";
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_ALCoS] = `${Parser.AL_PREFIX}Curse of Strahd`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_ALEE] = `${Parser.AL_PREFIX}Elemental Evil`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_ALRoD] = `${Parser.AL_PREFIX}Rage of Demons`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_PSA] = `${Parser.PS_PREFIX}Amonkhet`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_PSI] = `${Parser.PS_PREFIX}Innistrad`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_PSK] = `${Parser.PS_PREFIX}Kaladesh`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_PSZ] = `${Parser.PS_PREFIX}Zendikar`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_PSX] = `${Parser.PS_PREFIX}Ixalan`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_PSD] = `${Parser.PS_PREFIX}Dominaria`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_XMtS] = `X Marks the Spot`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_UATMC] = `${Parser.UA_PREFIX}The Mystic Class`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_MCV1SC] = `${Parser.MCVX_PREFIX}1: Spelljammer Creatures`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_MCV2DC] = `${Parser.MCVX_PREFIX}2: Dragonlance Creatures`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_MCV3MC] = `${Parser.MCVX_PREFIX}3: Minecraft Creatures`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_MCV4EC] = `${Parser.MCVX_PREFIX}4: Eldraine Creatures`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_MisMV1] = `${Parser.MisMVX_PREFIX}1`;
|
||
Parser.SOURCE_JSON_TO_FULL[Parser.SRC_AATM] = `${Parser.AA_PREFIX}The Mortuary`;
|
||
|
||
Parser.SOURCE_JSON_TO_ABV = {};
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_CoS] = "CoS";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_DMG] = "DMG";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_EEPC] = "EEPC";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_EET] = "EET";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_HotDQ] = "HotDQ";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_LMoP] = "LMoP";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_MM] = "MM";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_OotA] = "OotA";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_PHB] = "PHB";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_PotA] = "PotA";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_RoT] = "RoT";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_RoTOS] = "RoTOS";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_SCAG] = "SCAG";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_SKT] = "SKT";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_ToA] = "ToA";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_TLK] = "TLK";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_ToD] = "ToD";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_TTP] = "TTP";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_TYP] = "TftYP";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_TYP_AtG] = "TftYP";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_TYP_DiT] = "TftYP";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_TYP_TFoF] = "TftYP";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_TYP_THSoT] = "TftYP";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_TYP_TSC] = "TftYP";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_TYP_ToH] = "TftYP";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_TYP_WPM] = "TftYP";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_VGM] = "VGM";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_XGE] = "XGE";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_OGA] = "OGA";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_MTF] = "MTF";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_WDH] = "WDH";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_WDMM] = "WDMM";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_GGR] = "GGR";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_KKW] = "KKW";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_LLK] = "LLK";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_AZfyT] = "AZfyT";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_GoS] = "GoS";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_AI] = "AI";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_OoW] = "OoW";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_ESK] = "ESK";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_DIP] = "DIP";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_HftT] = "HftT";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_DC] = "DC";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_SLW] = "SLW";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_SDW] = "SDW";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_BGDIA] = "BGDIA";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_LR] = "LR";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_AL] = "AL";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_SAC] = "SAC";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_ERLW] = "ERLW";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_EFR] = "EFR";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_RMBRE] = "RMBRE";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_RMR] = "RMR";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_MFF] = "MFF";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_AWM] = "AWM";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_IMR] = "IMR";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_SADS] = "SADS";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_EGW] = "EGW";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_EGW_ToR] = "ToR";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_EGW_DD] = "DD";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_EGW_FS] = "FS";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_EGW_US] = "US";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_MOT] = "MOT";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_IDRotF] = "IDRotF";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_TCE] = "TCE";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_VRGR] = "VRGR";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_HoL] = "HoL";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_RtG] = "RtG";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_AitFR] = "AitFR";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_AitFR_ISF] = "AitFR-ISF";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_AitFR_THP] = "AitFR-THP";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_AitFR_AVT] = "AitFR-AVT";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_AitFR_DN] = "AitFR-DN";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_AitFR_FCD] = "AitFR-FCD";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_WBtW] = "WBtW";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_DoD] = "DoD";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_MaBJoV] = "MaBJoV";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_FTD] = "FTD";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_SCC] = "SCC";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_SCC_CK] = "SCC-CK";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_SCC_HfMT] = "SCC-HfMT";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_SCC_TMM] = "SCC-TMM";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_SCC_ARiR] = "SCC-ARiR";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_MPMM] = "MPMM";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_CRCotN] = "CRCotN";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_JttRC] = "JttRC";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_SAiS] = "SAiS";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_AAG] = "AAG";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_BAM] = "BAM";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_LoX] = "LoX";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_DoSI] = "DoSI";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_DSotDQ] = "DSotDQ";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_KftGV] = "KftGV";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_BGG] = "BGG";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_TDCSR] = "TDCSR";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_PaBTSO] = "PaBTSO";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_PAitM] = "PAitM";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_SatO] = "SatO";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_ToFW] = "ToFW";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_MPP] = "MPP";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_BMT] = "BMT";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_DMTCRG] = "DMTCRG";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_QftIS] = "QftIS";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_VEoR] = "VEoR";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_GHLoE] = "GHLoE";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_DoDk] = "DoDk";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_HWCS] = "HWCS";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_HWAitW] = "HWAitW";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_ToB1_2023] = "ToB1'23";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_TD] = "TD";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_SCREEN] = "Screen";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_SCREEN_WILDERNESS_KIT] = "ScWild";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_SCREEN_DUNGEON_KIT] = "ScDun";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_SCREEN_SPELLJAMMER] = "ScSJ";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_HF] = "HF";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_HFFotM] = "HFFotM";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_HFStCM] = "HFStCM";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_CM] = "CM";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_NRH] = "NRH";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_NRH_TCMC] = "NRH-TCMC";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_NRH_AVitW] = "NRH-AVitW";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_NRH_ASS] = "NRH-ASS";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_NRH_CoI] = "NRH-CoI";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_NRH_TLT] = "NRH-TLT";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_NRH_AWoL] = "NRH-AWoL";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_NRH_AT] = "NRH-AT";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_MGELFT] = "MGELFT";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_VD] = "VD";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_SjA] = "SjA";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_HAT_TG] = "HAT-TG";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_HAT_LMI] = "HAT-LMI";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_GotSF] = "GotSF";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_LK] = "LK";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_CoA] = "CoA";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_PiP] = "PiP";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_DitLCoT] = "DitLCoT";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_VNotEE] = "VNotEE";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_LRDT] = "LRDT";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_ALCoS] = "ALCoS";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_ALEE] = "ALEE";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_ALRoD] = "ALRoD";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_PSA] = "PSA";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_PSI] = "PSI";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_PSK] = "PSK";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_PSZ] = "PSZ";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_PSX] = "PSX";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_PSD] = "PSD";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_XMtS] = "XMtS";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_UATMC] = "UAMy";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_MCV1SC] = "MCV1SC";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_MCV2DC] = "MCV2DC";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_MCV3MC] = "MCV3MC";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_MCV4EC] = "MCV4EC";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_MisMV1] = "MisMV1";
|
||
Parser.SOURCE_JSON_TO_ABV[Parser.SRC_AATM] = "AATM";
|
||
|
||
Parser.SOURCE_JSON_TO_DATE = {};
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_CoS] = "2016-03-15";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_DMG] = "2014-12-09";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_EEPC] = "2015-03-10";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_EET] = "2015-03-10";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_HotDQ] = "2014-08-19";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_LMoP] = "2014-07-15";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_MM] = "2014-09-30";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_OotA] = "2015-09-15";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_PHB] = "2014-08-19";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_PotA] = "2015-04-07";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_RoT] = "2014-11-04";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_RoTOS] = "2014-11-04";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_SCAG] = "2015-11-03";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_SKT] = "2016-09-06";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_ToA] = "2017-09-19";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_TLK] = "2017-11-28";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_ToD] = "2019-10-22";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_TTP] = "2017-09-19";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_TYP] = "2017-04-04";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_TYP_AtG] = "2017-04-04";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_TYP_DiT] = "2017-04-04";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_TYP_TFoF] = "2017-04-04";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_TYP_THSoT] = "2017-04-04";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_TYP_TSC] = "2017-04-04";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_TYP_ToH] = "2017-04-04";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_TYP_WPM] = "2017-04-04";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_VGM] = "2016-11-15";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_XGE] = "2017-11-21";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_OGA] = "2017-10-11";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_MTF] = "2018-05-29";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_WDH] = "2018-09-18";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_WDMM] = "2018-11-20";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_GGR] = "2018-11-20";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_KKW] = "2018-11-20";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_LLK] = "2018-11-10";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_AZfyT] = "2019-03-05";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_GoS] = "2019-05-21";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_AI] = "2019-06-18";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_OoW] = "2019-06-18";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_ESK] = "2019-06-24";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_DIP] = "2019-06-24";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_HftT] = "2019-05-01";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_DC] = "2019-06-24";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_SLW] = "2019-06-24";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_SDW] = "2019-06-24";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_BGDIA] = "2019-09-17";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_LR] = "2019-09-19";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_SAC] = "2019-01-31";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_ERLW] = "2019-11-19";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_EFR] = "2019-11-19";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_RMBRE] = "2019-11-19";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_RMR] = "2019-11-19";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_MFF] = "2019-11-12";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_AWM] = "2019-11-12";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_IMR] = "2019-11-12";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_SADS] = "2019-12-12";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_EGW] = "2020-03-17";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_EGW_ToR] = "2020-03-17";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_EGW_DD] = "2020-03-17";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_EGW_FS] = "2020-03-17";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_EGW_US] = "2020-03-17";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_MOT] = "2020-06-02";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_IDRotF] = "2020-09-15";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_TCE] = "2020-11-17";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_VRGR] = "2021-05-18";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_HoL] = "2021-05-18";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_RtG] = "2021-05-21";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_AitFR] = "2021-06-30";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_AitFR_ISF] = "2021-06-30";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_AitFR_THP] = "2021-07-07";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_AitFR_AVT] = "2021-07-14";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_AitFR_DN] = "2021-07-21";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_AitFR_FCD] = "2021-07-28";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_WBtW] = "2021-09-21";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_DoD] = "2021-09-21";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_MaBJoV] = "2021-10-05";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_FTD] = "2021-11-26";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_SCC] = "2021-12-07";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_SCC_CK] = "2021-12-07";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_SCC_HfMT] = "2021-12-07";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_SCC_TMM] = "2021-12-07";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_SCC_ARiR] = "2021-12-07";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_MPMM] = "2022-01-25";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_CRCotN] = "2022-03-15";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_JttRC] = "2022-07-19";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_SAiS] = "2022-08-16";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_AAG] = "2022-08-16";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_BAM] = "2022-08-16";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_LoX] = "2022-08-16";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_DoSI] = "2022-07-31";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_DSotDQ] = "2022-11-22";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_KftGV] = "2023-02-21";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_BGG] = "2023-08-15";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_TDCSR] = "2022-01-18";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_PaBTSO] = "2023-09-19";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_PAitM] = "2023-10-17";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_SatO] = "2023-10-17";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_ToFW] = "2023-10-17";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_MPP] = "2023-10-17";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_BMT] = "2023-11-14";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_DMTCRG] = "2023-11-14";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_QftIS] = "2024-07-16";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_VEoR] = "2024-05-21";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_GHLoE] = "2023-11-30";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_DoDk] = "2023-12-21";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_HWCS] = "2019-06-17";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_HWAitW] = "2019-06-17";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_ToB1_2023] = "2023-05-31";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_TD] = "2022-05-24";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_SCREEN] = "2015-01-20";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_SCREEN_WILDERNESS_KIT] = "2020-11-17";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_SCREEN_DUNGEON_KIT] = "2020-09-21";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_SCREEN_SPELLJAMMER] = "2022-08-16";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_HF] = "2020-10-27";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_HFFotM] = "2023-11-07";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_HFStCM] = "2023-11-21";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_CM] = "2021-03-16";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_NRH] = "2021-09-01";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_NRH_TCMC] = "2021-09-01";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_NRH_AVitW] = "2021-09-01";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_NRH_ASS] = "2021-09-01";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_NRH_CoI] = "2021-09-01";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_NRH_TLT] = "2021-09-01";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_NRH_AWoL] = "2021-09-01";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_NRH_AT] = "2021-09-01";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_MGELFT] = "2020-12-01";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_VD] = "2022-06-09";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_SjA] = "2022-07-11"; // pt1; pt2 2022-07-18; pt3 2022-07-25; pt4 2022-08-01
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_HAT_TG] = "2023-03-06";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_HAT_LMI] = "2023-03-31";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_GotSF] = "2023-08-01";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_LK] = "2023-09-26";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_CoA] = "2023-10-30";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_PiP] = "2023-11-20";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_DitLCoT] = "2024-03-26";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_VNotEE] = "2024-04-16";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_LRDT] = "2024-04-01";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_ALCoS] = "2016-03-15";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_ALEE] = "2015-04-07";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_ALRoD] = "2015-09-15";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_PSA] = "2017-07-06";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_PSI] = "2016-07-12";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_PSK] = "2017-02-16";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_PSZ] = "2016-04-27";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_PSX] = "2018-01-09";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_PSD] = "2018-07-31";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_XMtS] = "2017-12-11";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_UATMC] = "2017-03-13";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_MCV1SC] = "2022-04-21";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_MCV2DC] = "2022-12-05";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_MCV3MC] = "2023-03-28";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_MCV4EC] = "2023-09-21";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_MisMV1] = "2023-05-03";
|
||
Parser.SOURCE_JSON_TO_DATE[Parser.SRC_AATM] = "2023-10-17";
|
||
|
||
// region Source categories
|
||
Parser.SOURCES_ADVENTURES = new Set([
|
||
Parser.SRC_LMoP,
|
||
Parser.SRC_HotDQ,
|
||
Parser.SRC_RoT,
|
||
Parser.SRC_RoTOS,
|
||
Parser.SRC_PotA,
|
||
Parser.SRC_OotA,
|
||
Parser.SRC_CoS,
|
||
Parser.SRC_SKT,
|
||
Parser.SRC_TYP,
|
||
Parser.SRC_TYP_AtG,
|
||
Parser.SRC_TYP_DiT,
|
||
Parser.SRC_TYP_TFoF,
|
||
Parser.SRC_TYP_THSoT,
|
||
Parser.SRC_TYP_TSC,
|
||
Parser.SRC_TYP_ToH,
|
||
Parser.SRC_TYP_WPM,
|
||
Parser.SRC_ToA,
|
||
Parser.SRC_TLK,
|
||
Parser.SRC_TTP,
|
||
Parser.SRC_WDH,
|
||
Parser.SRC_LLK,
|
||
Parser.SRC_WDMM,
|
||
Parser.SRC_KKW,
|
||
Parser.SRC_AZfyT,
|
||
Parser.SRC_GoS,
|
||
Parser.SRC_HftT,
|
||
Parser.SRC_OoW,
|
||
Parser.SRC_DIP,
|
||
Parser.SRC_SLW,
|
||
Parser.SRC_SDW,
|
||
Parser.SRC_DC,
|
||
Parser.SRC_BGDIA,
|
||
Parser.SRC_LR,
|
||
Parser.SRC_EFR,
|
||
Parser.SRC_RMBRE,
|
||
Parser.SRC_IMR,
|
||
Parser.SRC_EGW_ToR,
|
||
Parser.SRC_EGW_DD,
|
||
Parser.SRC_EGW_FS,
|
||
Parser.SRC_EGW_US,
|
||
Parser.SRC_IDRotF,
|
||
Parser.SRC_CM,
|
||
Parser.SRC_HoL,
|
||
Parser.SRC_XMtS,
|
||
Parser.SRC_RtG,
|
||
Parser.SRC_AitFR,
|
||
Parser.SRC_AitFR_ISF,
|
||
Parser.SRC_AitFR_THP,
|
||
Parser.SRC_AitFR_AVT,
|
||
Parser.SRC_AitFR_DN,
|
||
Parser.SRC_AitFR_FCD,
|
||
Parser.SRC_WBtW,
|
||
Parser.SRC_NRH,
|
||
Parser.SRC_NRH_TCMC,
|
||
Parser.SRC_NRH_AVitW,
|
||
Parser.SRC_NRH_ASS,
|
||
Parser.SRC_NRH_CoI,
|
||
Parser.SRC_NRH_TLT,
|
||
Parser.SRC_NRH_AWoL,
|
||
Parser.SRC_NRH_AT,
|
||
Parser.SRC_SCC,
|
||
Parser.SRC_SCC_CK,
|
||
Parser.SRC_SCC_HfMT,
|
||
Parser.SRC_SCC_TMM,
|
||
Parser.SRC_SCC_ARiR,
|
||
Parser.SRC_CRCotN,
|
||
Parser.SRC_JttRC,
|
||
Parser.SRC_SjA,
|
||
Parser.SRC_LoX,
|
||
Parser.SRC_DoSI,
|
||
Parser.SRC_DSotDQ,
|
||
Parser.SRC_KftGV,
|
||
Parser.SRC_GotSF,
|
||
Parser.SRC_PaBTSO,
|
||
Parser.SRC_LK,
|
||
Parser.SRC_CoA,
|
||
Parser.SRC_PiP,
|
||
Parser.SRC_DitLCoT,
|
||
Parser.SRC_VNotEE,
|
||
Parser.SRC_LRDT,
|
||
Parser.SRC_HFStCM,
|
||
Parser.SRC_GHLoE,
|
||
Parser.SRC_DoDk,
|
||
Parser.SRC_HWAitW,
|
||
|
||
Parser.SRC_AWM,
|
||
]);
|
||
Parser.SOURCES_CORE_SUPPLEMENTS = new Set(Object.keys(Parser.SOURCE_JSON_TO_FULL).filter(it => !Parser.SOURCES_ADVENTURES.has(it)));
|
||
Parser.SOURCES_NON_STANDARD_WOTC = new Set([
|
||
Parser.SRC_OGA,
|
||
Parser.SRC_LLK,
|
||
Parser.SRC_AZfyT,
|
||
Parser.SRC_LR,
|
||
Parser.SRC_TLK,
|
||
Parser.SRC_TTP,
|
||
Parser.SRC_AWM,
|
||
Parser.SRC_IMR,
|
||
Parser.SRC_SADS,
|
||
Parser.SRC_MFF,
|
||
Parser.SRC_XMtS,
|
||
Parser.SRC_RtG,
|
||
Parser.SRC_AitFR,
|
||
Parser.SRC_AitFR_ISF,
|
||
Parser.SRC_AitFR_THP,
|
||
Parser.SRC_AitFR_AVT,
|
||
Parser.SRC_AitFR_DN,
|
||
Parser.SRC_AitFR_FCD,
|
||
Parser.SRC_DoD,
|
||
Parser.SRC_MaBJoV,
|
||
Parser.SRC_NRH,
|
||
Parser.SRC_NRH_TCMC,
|
||
Parser.SRC_NRH_AVitW,
|
||
Parser.SRC_NRH_ASS,
|
||
Parser.SRC_NRH_CoI,
|
||
Parser.SRC_NRH_TLT,
|
||
Parser.SRC_NRH_AWoL,
|
||
Parser.SRC_NRH_AT,
|
||
Parser.SRC_MGELFT,
|
||
Parser.SRC_VD,
|
||
Parser.SRC_SjA,
|
||
Parser.SRC_HAT_TG,
|
||
Parser.SRC_HAT_LMI,
|
||
Parser.SRC_GotSF,
|
||
Parser.SRC_MCV3MC,
|
||
Parser.SRC_MCV4EC,
|
||
Parser.SRC_MisMV1,
|
||
Parser.SRC_LK,
|
||
Parser.SRC_AATM,
|
||
Parser.SRC_CoA,
|
||
Parser.SRC_PiP,
|
||
Parser.SRC_HFStCM,
|
||
]);
|
||
Parser.SOURCES_PARTNERED_WOTC = new Set([
|
||
Parser.SRC_RMBRE,
|
||
Parser.SRC_RMR,
|
||
Parser.SRC_EGW,
|
||
Parser.SRC_EGW_ToR,
|
||
Parser.SRC_EGW_DD,
|
||
Parser.SRC_EGW_FS,
|
||
Parser.SRC_EGW_US,
|
||
Parser.SRC_CRCotN,
|
||
Parser.SRC_TDCSR,
|
||
Parser.SRC_HftT,
|
||
Parser.SRC_GHLoE,
|
||
Parser.SRC_DoDk,
|
||
Parser.SRC_HWCS,
|
||
Parser.SRC_HWAitW,
|
||
Parser.SRC_ToB1_2023,
|
||
Parser.SRC_TD,
|
||
Parser.SRC_LRDT,
|
||
]);
|
||
Parser.SOURCES_LEGACY_WOTC = new Set([
|
||
Parser.SRC_EEPC,
|
||
Parser.SRC_VGM,
|
||
Parser.SRC_MTF,
|
||
]);
|
||
|
||
// An opinionated set of source that could be considered "core-core"
|
||
Parser.SOURCES_VANILLA = new Set([
|
||
Parser.SRC_DMG,
|
||
Parser.SRC_MM,
|
||
Parser.SRC_PHB,
|
||
Parser.SRC_SCAG,
|
||
// Parser.SRC_TTP, // "Legacy" source, removed in favor of MPMM
|
||
// Parser.SRC_VGM, // "Legacy" source, removed in favor of MPMM
|
||
Parser.SRC_XGE,
|
||
// Parser.SRC_MTF, // "Legacy" source, removed in favor of MPMM
|
||
Parser.SRC_SAC,
|
||
Parser.SRC_MFF,
|
||
Parser.SRC_SADS,
|
||
Parser.SRC_TCE,
|
||
Parser.SRC_FTD,
|
||
Parser.SRC_MPMM,
|
||
Parser.SRC_SCREEN,
|
||
Parser.SRC_SCREEN_WILDERNESS_KIT,
|
||
Parser.SRC_SCREEN_DUNGEON_KIT,
|
||
Parser.SRC_VD,
|
||
Parser.SRC_GotSF,
|
||
Parser.SRC_BGG,
|
||
Parser.SRC_MaBJoV,
|
||
Parser.SRC_CoA,
|
||
Parser.SRC_BMT,
|
||
Parser.SRC_DMTCRG,
|
||
]);
|
||
|
||
// Any opinionated set of sources that are """hilarious, dude"""
|
||
Parser.SOURCES_COMEDY = new Set([
|
||
Parser.SRC_AI,
|
||
Parser.SRC_OoW,
|
||
Parser.SRC_RMR,
|
||
Parser.SRC_RMBRE,
|
||
Parser.SRC_HftT,
|
||
Parser.SRC_AWM,
|
||
Parser.SRC_MGELFT,
|
||
Parser.SRC_HAT_TG,
|
||
Parser.SRC_HAT_LMI,
|
||
Parser.SRC_MCV3MC,
|
||
Parser.SRC_MisMV1,
|
||
Parser.SRC_LK,
|
||
Parser.SRC_PiP,
|
||
Parser.SRC_LRDT,
|
||
]);
|
||
|
||
// Any opinionated set of sources that are "other settings"
|
||
Parser.SOURCES_NON_FR = new Set([
|
||
Parser.SRC_GGR,
|
||
Parser.SRC_KKW,
|
||
Parser.SRC_ERLW,
|
||
Parser.SRC_EFR,
|
||
Parser.SRC_EGW,
|
||
Parser.SRC_EGW_ToR,
|
||
Parser.SRC_EGW_DD,
|
||
Parser.SRC_EGW_FS,
|
||
Parser.SRC_EGW_US,
|
||
Parser.SRC_MOT,
|
||
Parser.SRC_XMtS,
|
||
Parser.SRC_AZfyT,
|
||
Parser.SRC_SCC,
|
||
Parser.SRC_SCC_CK,
|
||
Parser.SRC_SCC_HfMT,
|
||
Parser.SRC_SCC_TMM,
|
||
Parser.SRC_SCC_ARiR,
|
||
Parser.SRC_CRCotN,
|
||
Parser.SRC_SjA,
|
||
Parser.SRC_SAiS,
|
||
Parser.SRC_AAG,
|
||
Parser.SRC_BAM,
|
||
Parser.SRC_LoX,
|
||
Parser.SRC_DSotDQ,
|
||
Parser.SRC_TDCSR,
|
||
Parser.SRC_PAitM,
|
||
Parser.SRC_SatO,
|
||
Parser.SRC_ToFW,
|
||
Parser.SRC_MPP,
|
||
Parser.SRC_MCV4EC,
|
||
Parser.SRC_LK,
|
||
Parser.SRC_GHLoE,
|
||
Parser.SRC_DoDk,
|
||
Parser.SRC_HWCS,
|
||
Parser.SRC_HWAitW,
|
||
Parser.SRC_ToB1_2023,
|
||
Parser.SRC_LRDT,
|
||
]);
|
||
|
||
// endregion
|
||
Parser.SOURCES_AVAILABLE_DOCS_BOOK = {};
|
||
[
|
||
Parser.SRC_PHB,
|
||
Parser.SRC_MM,
|
||
Parser.SRC_DMG,
|
||
Parser.SRC_SCAG,
|
||
Parser.SRC_VGM,
|
||
Parser.SRC_OGA,
|
||
Parser.SRC_XGE,
|
||
Parser.SRC_MTF,
|
||
Parser.SRC_GGR,
|
||
Parser.SRC_AI,
|
||
Parser.SRC_ERLW,
|
||
Parser.SRC_RMR,
|
||
Parser.SRC_EGW,
|
||
Parser.SRC_MOT,
|
||
Parser.SRC_TCE,
|
||
Parser.SRC_VRGR,
|
||
Parser.SRC_DoD,
|
||
Parser.SRC_MaBJoV,
|
||
Parser.SRC_FTD,
|
||
Parser.SRC_SCC,
|
||
Parser.SRC_MPMM,
|
||
Parser.SRC_AAG,
|
||
Parser.SRC_BAM,
|
||
Parser.SRC_HAT_TG,
|
||
Parser.SRC_SCREEN,
|
||
Parser.SRC_SCREEN_WILDERNESS_KIT,
|
||
Parser.SRC_SCREEN_DUNGEON_KIT,
|
||
Parser.SRC_SCREEN_SPELLJAMMER,
|
||
Parser.SRC_BGG,
|
||
Parser.SRC_TDCSR,
|
||
Parser.SRC_SatO,
|
||
Parser.SRC_MPP,
|
||
Parser.SRC_HF,
|
||
Parser.SRC_HFFotM,
|
||
Parser.SRC_BMT,
|
||
Parser.SRC_DMTCRG,
|
||
Parser.SRC_HWCS,
|
||
Parser.SRC_ToB1_2023,
|
||
Parser.SRC_TD,
|
||
].forEach(src => {
|
||
Parser.SOURCES_AVAILABLE_DOCS_BOOK[src] = src;
|
||
Parser.SOURCES_AVAILABLE_DOCS_BOOK[src.toLowerCase()] = src;
|
||
});
|
||
[
|
||
{src: Parser.SRC_PSA, id: "PS-A"},
|
||
{src: Parser.SRC_PSI, id: "PS-I"},
|
||
{src: Parser.SRC_PSK, id: "PS-K"},
|
||
{src: Parser.SRC_PSZ, id: "PS-Z"},
|
||
{src: Parser.SRC_PSX, id: "PS-X"},
|
||
{src: Parser.SRC_PSD, id: "PS-D"},
|
||
].forEach(({src, id}) => {
|
||
Parser.SOURCES_AVAILABLE_DOCS_BOOK[src] = id;
|
||
Parser.SOURCES_AVAILABLE_DOCS_BOOK[src.toLowerCase()] = id;
|
||
});
|
||
Parser.SOURCES_AVAILABLE_DOCS_ADVENTURE = {};
|
||
[
|
||
Parser.SRC_LMoP,
|
||
Parser.SRC_HotDQ,
|
||
Parser.SRC_RoT,
|
||
Parser.SRC_PotA,
|
||
Parser.SRC_OotA,
|
||
Parser.SRC_CoS,
|
||
Parser.SRC_SKT,
|
||
Parser.SRC_TYP_AtG,
|
||
Parser.SRC_TYP_DiT,
|
||
Parser.SRC_TYP_TFoF,
|
||
Parser.SRC_TYP_THSoT,
|
||
Parser.SRC_TYP_TSC,
|
||
Parser.SRC_TYP_ToH,
|
||
Parser.SRC_TYP_WPM,
|
||
Parser.SRC_ToA,
|
||
Parser.SRC_TLK,
|
||
Parser.SRC_TTP,
|
||
Parser.SRC_WDH,
|
||
Parser.SRC_LLK,
|
||
Parser.SRC_WDMM,
|
||
Parser.SRC_KKW,
|
||
Parser.SRC_AZfyT,
|
||
Parser.SRC_GoS,
|
||
Parser.SRC_HftT,
|
||
Parser.SRC_OoW,
|
||
Parser.SRC_DIP,
|
||
Parser.SRC_SLW,
|
||
Parser.SRC_SDW,
|
||
Parser.SRC_DC,
|
||
Parser.SRC_BGDIA,
|
||
Parser.SRC_LR,
|
||
Parser.SRC_EFR,
|
||
Parser.SRC_RMBRE,
|
||
Parser.SRC_IMR,
|
||
Parser.SRC_EGW_ToR,
|
||
Parser.SRC_EGW_DD,
|
||
Parser.SRC_EGW_FS,
|
||
Parser.SRC_EGW_US,
|
||
Parser.SRC_IDRotF,
|
||
Parser.SRC_CM,
|
||
Parser.SRC_HoL,
|
||
Parser.SRC_XMtS,
|
||
Parser.SRC_RtG,
|
||
Parser.SRC_AitFR_ISF,
|
||
Parser.SRC_AitFR_THP,
|
||
Parser.SRC_AitFR_AVT,
|
||
Parser.SRC_AitFR_DN,
|
||
Parser.SRC_AitFR_FCD,
|
||
Parser.SRC_WBtW,
|
||
Parser.SRC_NRH,
|
||
Parser.SRC_NRH_TCMC,
|
||
Parser.SRC_NRH_AVitW,
|
||
Parser.SRC_NRH_ASS,
|
||
Parser.SRC_NRH_CoI,
|
||
Parser.SRC_NRH_TLT,
|
||
Parser.SRC_NRH_AWoL,
|
||
Parser.SRC_NRH_AT,
|
||
Parser.SRC_SCC_CK,
|
||
Parser.SRC_SCC_HfMT,
|
||
Parser.SRC_SCC_TMM,
|
||
Parser.SRC_SCC_ARiR,
|
||
Parser.SRC_CRCotN,
|
||
Parser.SRC_JttRC,
|
||
Parser.SRC_LoX,
|
||
Parser.SRC_DoSI,
|
||
Parser.SRC_DSotDQ,
|
||
Parser.SRC_KftGV,
|
||
Parser.SRC_GotSF,
|
||
Parser.SRC_PaBTSO,
|
||
Parser.SRC_ToFW,
|
||
Parser.SRC_LK,
|
||
Parser.SRC_CoA,
|
||
Parser.SRC_PiP,
|
||
Parser.SRC_DitLCoT,
|
||
Parser.SRC_HFStCM,
|
||
Parser.SRC_GHLoE,
|
||
Parser.SRC_DoDk,
|
||
Parser.SRC_HWAitW,
|
||
Parser.SRC_QftIS,
|
||
Parser.SRC_LRDT,
|
||
Parser.SRC_VEoR,
|
||
Parser.SRC_VNotEE,
|
||
].forEach(src => {
|
||
Parser.SOURCES_AVAILABLE_DOCS_ADVENTURE[src] = src;
|
||
Parser.SOURCES_AVAILABLE_DOCS_ADVENTURE[src.toLowerCase()] = src;
|
||
});
|
||
|
||
Parser.getTagSource = function (tag, source) {
|
||
if (source && source.trim()) return source;
|
||
|
||
tag = tag.trim();
|
||
|
||
const tagMeta = Renderer.tag.TAG_LOOKUP[tag];
|
||
|
||
if (!tagMeta) throw new Error(`Unhandled tag "${tag}"`);
|
||
return tagMeta.defaultSource;
|
||
};
|
||
|
||
Parser.PROP_TO_TAG = {
|
||
"monster": "creature",
|
||
"optionalfeature": "optfeature",
|
||
"tableGroup": "table",
|
||
"vehicleUpgrade": "vehupgrade",
|
||
"baseitem": "item",
|
||
"itemGroup": "item",
|
||
"magicvariant": "item",
|
||
"subclass": "class",
|
||
};
|
||
Parser.getPropTag = function (prop) {
|
||
if (Parser.PROP_TO_TAG[prop]) return Parser.PROP_TO_TAG[prop];
|
||
return prop;
|
||
};
|
||
|
||
Parser.PROP_TO_DISPLAY_NAME = {
|
||
"variantrule": "Variant Rule",
|
||
"optionalfeature": "Option/Feature",
|
||
"magicvariant": "Magic Item Variant",
|
||
"baseitem": "Item (Base)",
|
||
"item": "Item",
|
||
"adventure": "Adventure",
|
||
"adventureData": "Adventure Text",
|
||
"book": "Book",
|
||
"bookData": "Book Text",
|
||
"makebrewCreatureTrait": "Homebrew Builder Creature Trait",
|
||
"charoption": "Other Character Creation Option",
|
||
|
||
"bonus": "Bonus Action",
|
||
"legendary": "Legendary Action",
|
||
"mythic": "Mythic Action",
|
||
"lairActions": "Lair Action",
|
||
"regionalEffects": "Regional Effect",
|
||
};
|
||
Parser.getPropDisplayName = function (prop, {suffix = ""} = {}) {
|
||
if (Parser.PROP_TO_DISPLAY_NAME[prop]) return `${Parser.PROP_TO_DISPLAY_NAME[prop]}${suffix}`;
|
||
|
||
const mFluff = /Fluff$/.exec(prop);
|
||
if (mFluff) return Parser.getPropDisplayName(prop.slice(0, -mFluff[0].length), {suffix: " Fluff"});
|
||
|
||
const mFoundry = /^foundry(?<prop>[A-Z].*)$/.exec(prop);
|
||
if (mFoundry) return Parser.getPropDisplayName(mFoundry.groups.prop.lowercaseFirst(), {suffix: " Foundry Data"});
|
||
|
||
return `${prop.split(/([A-Z][a-z]+)/g).filter(Boolean).join(" ").uppercaseFirst()}${suffix}`;
|
||
};
|
||
|
||
Parser.ITEM_TYPE_JSON_TO_ABV = {
|
||
"A": "ammunition",
|
||
"AF": "ammunition",
|
||
"AT": "artisan's tools",
|
||
"EM": "eldritch machine",
|
||
"EXP": "explosive",
|
||
"FD": "food and drink",
|
||
"G": "adventuring gear",
|
||
"GS": "gaming set",
|
||
"HA": "heavy armor",
|
||
"IDG": "illegal drug",
|
||
"INS": "instrument",
|
||
"LA": "light armor",
|
||
"M": "melee weapon",
|
||
"MA": "medium armor",
|
||
"MNT": "mount",
|
||
"MR": "master rune",
|
||
"GV": "generic variant",
|
||
"P": "potion",
|
||
"R": "ranged weapon",
|
||
"RD": "rod",
|
||
"RG": "ring",
|
||
"S": "shield",
|
||
"SC": "scroll",
|
||
"SCF": "spellcasting focus",
|
||
"OTH": "other",
|
||
"T": "tools",
|
||
"TAH": "tack and harness",
|
||
"TG": "trade good",
|
||
"$": "treasure",
|
||
"$A": "treasure (art object)",
|
||
"$C": "treasure (coinage)",
|
||
"$G": "treasure (gemstone)",
|
||
"VEH": "vehicle (land)",
|
||
"SHP": "vehicle (water)",
|
||
"AIR": "vehicle (air)",
|
||
"SPC": "vehicle (space)",
|
||
"WD": "wand",
|
||
};
|
||
|
||
Parser.DMGTYPE_JSON_TO_FULL = {
|
||
"A": "acid",
|
||
"B": "bludgeoning",
|
||
"C": "cold",
|
||
"F": "fire",
|
||
"O": "force",
|
||
"L": "lightning",
|
||
"N": "necrotic",
|
||
"P": "piercing",
|
||
"I": "poison",
|
||
"Y": "psychic",
|
||
"R": "radiant",
|
||
"S": "slashing",
|
||
"T": "thunder",
|
||
};
|
||
|
||
Parser.DMG_TYPES = ["acid", "bludgeoning", "cold", "fire", "force", "lightning", "necrotic", "piercing", "poison", "psychic", "radiant", "slashing", "thunder"];
|
||
Parser.CONDITIONS = ["blinded", "charmed", "deafened", "exhaustion", "frightened", "grappled", "incapacitated", "invisible", "paralyzed", "petrified", "poisoned", "prone", "restrained", "stunned", "unconscious"];
|
||
|
||
Parser.SENSES = [
|
||
{"name": "blindsight", "source": Parser.SRC_PHB},
|
||
{"name": "darkvision", "source": Parser.SRC_PHB},
|
||
{"name": "tremorsense", "source": Parser.SRC_MM},
|
||
{"name": "truesight", "source": Parser.SRC_PHB},
|
||
];
|
||
|
||
Parser.NUMBERS_ONES = ["", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"];
|
||
Parser.NUMBERS_TENS = ["", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"];
|
||
Parser.NUMBERS_TEENS = ["ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"];
|
||
|
||
// region Metric conversion
|
||
Parser.metric = {
|
||
// See MPMB's breakdown: https://old.reddit.com/r/dndnext/comments/6gkuec
|
||
MILES_TO_KILOMETRES: 1.6,
|
||
FEET_TO_METRES: 0.3, // 5 ft = 1.5 m
|
||
YARDS_TO_METRES: 0.9, // (as above)
|
||
POUNDS_TO_KILOGRAMS: 0.5, // 2 lb = 1 kg
|
||
|
||
getMetricNumber ({originalValue, originalUnit, toFixed = null}) {
|
||
if (originalValue == null || isNaN(originalValue)) return originalValue;
|
||
|
||
originalValue = Number(originalValue);
|
||
if (!originalValue) return originalValue;
|
||
|
||
let out = null;
|
||
switch (originalUnit) {
|
||
case "ft.": case "ft": case Parser.UNT_FEET: out = originalValue * Parser.metric.FEET_TO_METRES; break;
|
||
case "yd.": case "yd": case Parser.UNT_YARDS: out = originalValue * Parser.metric.YARDS_TO_METRES; break;
|
||
case "mi.": case "mi": case Parser.UNT_MILES: out = originalValue * Parser.metric.MILES_TO_KILOMETRES; break;
|
||
case "lb.": case "lb": case Parser.UNT_LBS: out = originalValue * Parser.metric.POUNDS_TO_KILOGRAMS; break;
|
||
default: return originalValue;
|
||
}
|
||
if (toFixed != null) return NumberUtil.toFixedNumber(out, toFixed);
|
||
return out;
|
||
},
|
||
|
||
getMetricUnit ({originalUnit, isShortForm = false, isPlural = true}) {
|
||
switch (originalUnit) {
|
||
case "ft.": case "ft": case Parser.UNT_FEET: return isShortForm ? "m" : `meter`[isPlural ? "toPlural" : "toString"]();
|
||
case "yd.": case "yd": case Parser.UNT_YARDS: return isShortForm ? "m" : `meter`[isPlural ? "toPlural" : "toString"]();
|
||
case "mi.": case "mi": case Parser.UNT_MILES: return isShortForm ? "km" : `kilometre`[isPlural ? "toPlural" : "toString"]();
|
||
case "lb.": case "lb": case Parser.UNT_LBS: return isShortForm ? "kg" : `kilogram`[isPlural ? "toPlural" : "toString"]();
|
||
default: return originalUnit;
|
||
}
|
||
},
|
||
};
|
||
// endregion
|
||
// region Map grids
|
||
|
||
Parser.MAP_GRID_TYPE_TO_FULL = {};
|
||
Parser.MAP_GRID_TYPE_TO_FULL["none"] = "None";
|
||
Parser.MAP_GRID_TYPE_TO_FULL["square"] = "Square";
|
||
Parser.MAP_GRID_TYPE_TO_FULL["hexRowsOdd"] = "Hex Rows (Odd)";
|
||
Parser.MAP_GRID_TYPE_TO_FULL["hexRowsEven"] = "Hex Rows (Even)";
|
||
Parser.MAP_GRID_TYPE_TO_FULL["hexColsOdd"] = "Hex Columns (Odd)";
|
||
Parser.MAP_GRID_TYPE_TO_FULL["hexColsEven"] = "Hex Columns (Even)";
|
||
|
||
Parser.mapGridTypeToFull = function (gridType) {
|
||
return Parser._parse_aToB(Parser.MAP_GRID_TYPE_TO_FULL, gridType);
|
||
};
|
||
// endregion
|