This commit is contained in:
TheGiddyLimit
2024-06-23 22:13:57 +01:00
parent ed9833eefc
commit e5844f8a3f
279 changed files with 38254 additions and 7675 deletions

View File

@@ -1,6 +1,151 @@
"use strict";
function getFnListSort (prop) {
switch (prop) {
case "spell":
case "roll20Spell":
case "foundrySpell":
case "spellList":
case "monster":
case "foundryMonster":
case "monsterFluff":
case "monsterTemplate":
case "makebrewCreatureTrait":
case "makebrewCreatureAction":
case "action":
case "foundryAction":
case "background":
case "legendaryGroup":
case "language":
case "languageScript":
case "name":
case "condition":
case "disease":
case "status":
case "cult":
case "boon":
case "feat":
case "foundryFeat":
case "vehicle":
case "vehicleUpgrade":
case "foundryVehicleUpgrade":
case "backgroundFluff":
case "featFluff":
case "optionalfeatureFluff":
case "conditionFluff":
case "spellFluff":
case "itemFluff":
case "languageFluff":
case "vehicleFluff":
case "objectFluff":
case "raceFluff":
case "item":
case "foundryItem":
case "baseitem":
case "magicvariant":
case "foundryMagicvariant":
case "itemGroup":
case "itemMastery":
case "object":
case "optionalfeature":
case "foundryOptionalfeature":
case "psionic":
case "reward":
case "foundryReward":
case "rewardFluff":
case "variantrule":
case "race":
case "foundryRace":
case "foundryRaceFeature":
case "table":
case "trap":
case "trapFluff":
case "hazard":
case "hazardFluff":
case "charoption":
case "charoptionFluff":
case "recipe":
case "recipeFluff":
case "sense":
case "skill":
case "deck":
case "citation":
case "foundryMap":
return SortUtil.ascSortGenericEntity.bind(SortUtil);
case "deity":
return SortUtil.ascSortDeity.bind(SortUtil);
case "card":
return SortUtil.ascSortCard.bind(SortUtil);
case "class":
case "classFluff":
case "foundryClass":
return (a, b) => SortUtil.ascSortDateString(Parser.sourceJsonToDate(b.source), Parser.sourceJsonToDate(a.source)) || SortUtil.ascSortLower(a.name, b.name) || SortUtil.ascSortLower(a.source, b.source);
case "subclass":
case "subclassFluff":
case "foundrySubclass":
return (a, b) => SortUtil.ascSortDateString(Parser.sourceJsonToDate(b.source), Parser.sourceJsonToDate(a.source)) || SortUtil.ascSortLower(a.name, b.name);
case "classFeature":
case "foundryClassFeature":
return (a, b) => SortUtil.ascSortLower(a.classSource, b.classSource)
|| SortUtil.ascSortLower(a.className, b.className)
|| SortUtil.ascSort(a.level, b.level)
|| SortUtil.ascSortGenericEntity(a, b);
case "subclassFeature":
case "foundrySubclassFeature":
return (a, b) => SortUtil.ascSortLower(a.classSource, b.classSource)
|| SortUtil.ascSortLower(a.className, b.className)
|| SortUtil.ascSortLower(a.subclassSource, b.subclassSource)
|| SortUtil.ascSortLower(a.subclassShortName, b.subclassShortName)
|| SortUtil.ascSort(a.level, b.level)
|| SortUtil.ascSort(a.header || 0, b.header || 0)
|| SortUtil.ascSortGenericEntity(a, b);
case "subrace": return (a, b) => SortUtil.ascSortLower(a.raceName, b.raceName)
|| SortUtil.ascSortLower(a.raceSource, b.raceSource)
|| SortUtil.ascSortLower(a.name || "", b.name || "")
|| SortUtil.ascSortLower(a.source, b.source);
case "backgroundFeature": return (a, b) => SortUtil.ascSortLower(a.backgroundName, b.backgroundName)
|| SortUtil.ascSortLower(a.backgroundSource, b.backgroundSource)
|| SortUtil.ascSortGenericEntity(a, b);
case "encounter":
return SortUtil.ascSortEncounter.bind(SortUtil);
case "adventure": return SortUtil.ascSortAdventure.bind(SortUtil);
case "book": return SortUtil.ascSortBook.bind(SortUtil);
case "adventureData":
case "bookData":
return SortUtil.ascSortBookData.bind(SortUtil);
case "monsterfeatures":
return (a, b) => SortUtil.ascSortLower(a.name, b.name);
default: throw new Error(`Unhandled prop "${prop}"`);
}
}
class PropOrder {
static _getKeyProp (keyInfo) {
return typeof keyInfo === "string" ? keyInfo : keyInfo.key;
}
/* -------------------------------------------- */
/**
* @param obj
* @param [opts] Options object.
* @param [opts.fnUnhandledKey] Function to call on each unhandled key.
* @param [opts.isFoundryPrefixProps] If root keys should be treated as having a "foundry" prefix.
*/
static getOrderedRoot (obj, opts) {
opts ||= {};
return this._getOrdered(obj, PropOrder._ROOT, opts, "root");
}
static hasOrderRoot (obj) {
return PropOrder._ROOT
.filter(keyInfo => !(keyInfo instanceof PropOrder._IgnoredKey))
.some(keyInfo => obj[this._getKeyProp(keyInfo)] != null);
}
/* -------------------------------------------- */
/**
* @param obj
* @param dataProp
@@ -8,7 +153,7 @@ class PropOrder {
* @param [opts.fnUnhandledKey] Function to call on each unhandled key.
*/
static getOrdered (obj, dataProp, opts) {
opts = opts || {};
opts ||= {};
const order = PropOrder._PROP_TO_LIST[dataProp];
if (!order) throw new Error(`Unhandled prop "${dataProp}"`);
@@ -16,44 +161,79 @@ class PropOrder {
return this._getOrdered(obj, order, opts, dataProp);
}
static _getOrdered (obj, order, opts, path) {
static _getModifiedProp ({keyInfo, isFoundryPrefixProps}) {
const prop = this._getKeyProp(keyInfo);
if (!isFoundryPrefixProps || prop.startsWith("_")) return prop;
return prop.replace(/^foundry/, "").lowercaseFirst();
}
static _getOrdered (obj, order, opts, logPath) {
const out = {};
const keySet = new Set(Object.keys(obj));
const seenKeys = new Set();
order.forEach(k => {
if (typeof k === "string") {
seenKeys.add(k);
if (keySet.has(k)) out[k] = obj[k];
} else {
const key = k.key;
seenKeys.add(key);
order
.forEach(keyInfo => {
const prop = this._getKeyProp(keyInfo);
const propMod = this._getModifiedProp({keyInfo, isFoundryPrefixProps: opts.isFoundryPrefixProps});
if (keySet.has(key)) {
if (!obj[key]) return out[key] = obj[key]; // Handle nulls
if (opts.isFoundryPrefixProps && !prop.startsWith("_") && !prop.startsWith("foundry")) return;
if (k instanceof PropOrder._ObjectKey) {
const nxtPath = `${path}.${key}`;
if (k.fnGetOrder) out[key] = this._getOrdered(obj[key], k.fnGetOrder(obj[key]), opts, nxtPath);
else if (k.order) out[key] = this._getOrdered(obj[key], k.order, opts, nxtPath);
else out[key] = obj[key];
} else if (k instanceof PropOrder._ArrayKey) {
const nxtPath = `${path}[n].${key}`;
if (k.fnGetOrder) out[key] = obj[key].map(it => this._getOrdered(it, k.fnGetOrder(obj[key]), opts, nxtPath));
else if (k.order) out[key] = obj[key].map(it => this._getOrdered(it, k.order, opts, nxtPath));
else out[key] = obj[key];
if (!keySet.has(propMod)) return;
seenKeys.add(propMod);
if (k.fnSort && out[key] instanceof Array) out[key].sort(k.fnSort);
} else throw new Error(`Unimplemented!`);
if (typeof keyInfo === "string") {
out[propMod] = obj[propMod];
return;
}
}
});
if (!obj[propMod]) return out[propMod] = obj[propMod]; // Handle nulls
const optsNxt = {
...opts,
// Only used at the root
isFoundryPrefixProps: false,
};
if (keyInfo instanceof PropOrder._ObjectKey) {
const logPathNxt = `${logPath}.${prop}${propMod !== prop ? ` (${propMod})` : ""}`;
if (keyInfo.fnGetOrder) out[propMod] = this._getOrdered(obj[propMod], keyInfo.fnGetOrder(obj[propMod]), optsNxt, logPathNxt);
else if (keyInfo.order) out[propMod] = this._getOrdered(obj[propMod], keyInfo.order, optsNxt, logPathNxt);
else out[propMod] = obj[propMod];
return;
}
if (keyInfo instanceof PropOrder._ArrayKey) {
const logPathNxt = `${logPath}[n].${prop}${propMod !== prop ? ` (${propMod})` : ""}`;
if (keyInfo.fnGetOrder) out[propMod] = obj[propMod].map(it => this._getOrdered(it, keyInfo.fnGetOrder(obj[propMod]), optsNxt, logPathNxt));
else if (keyInfo.order) out[propMod] = obj[propMod].map(it => this._getOrdered(it, keyInfo.order, optsNxt, logPathNxt));
else out[propMod] = obj[propMod];
if (keyInfo.fnSort && out[propMod] instanceof Array) out[propMod].sort(keyInfo.fnSort);
return;
}
if (keyInfo instanceof PropOrder._IgnoredKey) {
out[propMod] = obj[propMod];
return;
}
throw new Error(`Unimplemented!`);
});
// ensure any non-orderable keys are maintained
const otherKeys = CollectionUtil.setDiff(keySet, seenKeys);
[...otherKeys].forEach(k => {
out[k] = obj[k];
if (opts.fnUnhandledKey) opts.fnUnhandledKey(`${path}.${k}`);
[...otherKeys].forEach(prop => {
out[prop] = obj[prop];
if (!opts.fnUnhandledKey) return;
const propMod = opts.isFoundryPrefixProps ? `foundry${prop.uppercaseFirst()}` : prop;
const logPathNxt = `${logPath}.${prop}${propMod !== prop ? ` (${propMod})` : ""}`;
opts.fnUnhandledKey(logPathNxt);
});
return out;
@@ -115,6 +295,22 @@ PropOrder._ArrayKey = class {
this.order = opts.order;
this.fnSort = opts.fnSort;
}
static getRootKey (prop) {
return new this(
prop,
{
fnGetOrder: () => PropOrder._PROP_TO_LIST[prop],
fnSort: getFnListSort(prop),
},
);
}
};
PropOrder._IgnoredKey = class {
constructor (key) {
this.key = key;
}
};
PropOrder._PROPS_FOUNDRY_DATA = [
@@ -157,6 +353,10 @@ PropOrder._FOUNDRY_GENERIC = [
"flags",
"img",
new PropOrder._ObjectKey("subEntities", {
fnGetOrder: () => PropOrder._ROOT,
}),
"_merge",
];
PropOrder._FOUNDRY_GENERIC_FEATURE = [
@@ -179,6 +379,10 @@ PropOrder._FOUNDRY_GENERIC_FEATURE = [
fnGetOrder: () => PropOrder._ENTRY_DATA_OBJECT,
}),
new PropOrder._ObjectKey("subEntities", {
fnGetOrder: () => PropOrder._ROOT,
}),
"_merge",
];
PropOrder._MONSTER = [
@@ -743,6 +947,11 @@ PropOrder._FOUNDRY_CLASS = [
"source",
"system",
"effects",
"flags",
"img",
"advancement",
"chooseSystem",
"isChooseSystemRenderEntries",
@@ -786,6 +995,9 @@ PropOrder._SUBCLASS = [
"preparedSpellsProgression",
"cantripProgression",
"spellsKnownProgression",
"spellsKnownProgressionFixed",
"spellsKnownProgressionFixedAllowLowerLevel",
"spellsKnownProgressionFixedByLevel",
"additionalSpells",
@@ -830,6 +1042,11 @@ PropOrder._FOUNDRY_SUBCLASS = [
"className",
"classSource",
"system",
"effects",
"flags",
"img",
"advancement",
"chooseSystem",
"isChooseSystemRenderEntries",
@@ -947,7 +1164,9 @@ PropOrder._FOUNDRY_CLASS_FEATURE = [
"actorDataMod",
"actorTokenMod",
"subEntities",
new PropOrder._ObjectKey("subEntities", {
fnGetOrder: () => PropOrder._ROOT,
}),
];
PropOrder._FOUNDRY_SUBCLASS_FEATURE = [
"name",
@@ -978,7 +1197,9 @@ PropOrder._FOUNDRY_SUBCLASS_FEATURE = [
"actorDataMod",
"actorTokenMod",
"subEntities",
new PropOrder._ObjectKey("subEntities", {
fnGetOrder: () => PropOrder._ROOT,
}),
];
PropOrder._LANGUAGE = [
"name",
@@ -2143,4 +2364,164 @@ PropOrder._PROP_TO_LIST = {
"foundryMap": PropOrder._FOUNDRY_MAP,
};
PropOrder._ROOT = [
"$schema",
new PropOrder._ObjectKey("_meta", {
fnGetOrder: () => PropOrder._META,
}),
// region Player options
PropOrder._ArrayKey.getRootKey("class"),
PropOrder._ArrayKey.getRootKey("foundryClass"),
PropOrder._ArrayKey.getRootKey("classFluff"),
PropOrder._ArrayKey.getRootKey("subclass"),
PropOrder._ArrayKey.getRootKey("foundrySubclass"),
PropOrder._ArrayKey.getRootKey("subclassFluff"),
PropOrder._ArrayKey.getRootKey("classFeature"),
PropOrder._ArrayKey.getRootKey("foundryClassFeature"),
PropOrder._ArrayKey.getRootKey("subclassFeature"),
PropOrder._ArrayKey.getRootKey("foundrySubclassFeature"),
PropOrder._ArrayKey.getRootKey("optionalfeature"),
PropOrder._ArrayKey.getRootKey("optionalfeatureFluff"),
PropOrder._ArrayKey.getRootKey("foundryOptionalfeature"),
PropOrder._ArrayKey.getRootKey("background"),
PropOrder._ArrayKey.getRootKey("backgroundFeature"),
PropOrder._ArrayKey.getRootKey("backgroundFluff"),
PropOrder._ArrayKey.getRootKey("race"),
PropOrder._ArrayKey.getRootKey("subrace"),
PropOrder._ArrayKey.getRootKey("foundryRace"),
PropOrder._ArrayKey.getRootKey("foundryRaceFeature"),
PropOrder._ArrayKey.getRootKey("raceFluff"),
new PropOrder._IgnoredKey("raceFluffMeta"),
PropOrder._ArrayKey.getRootKey("feat"),
PropOrder._ArrayKey.getRootKey("foundryFeat"),
PropOrder._ArrayKey.getRootKey("featFluff"),
PropOrder._ArrayKey.getRootKey("reward"),
PropOrder._ArrayKey.getRootKey("foundryReward"),
PropOrder._ArrayKey.getRootKey("rewardFluff"),
PropOrder._ArrayKey.getRootKey("charoption"),
PropOrder._ArrayKey.getRootKey("charoptionFluff"),
// endregion
// region General entities
PropOrder._ArrayKey.getRootKey("spell"),
PropOrder._ArrayKey.getRootKey("spellFluff"),
PropOrder._ArrayKey.getRootKey("foundrySpell"),
PropOrder._ArrayKey.getRootKey("spellList"),
PropOrder._ArrayKey.getRootKey("baseitem"),
PropOrder._ArrayKey.getRootKey("item"),
PropOrder._ArrayKey.getRootKey("itemGroup"),
PropOrder._ArrayKey.getRootKey("magicvariant"),
PropOrder._ArrayKey.getRootKey("itemFluff"),
PropOrder._ArrayKey.getRootKey("foundryItem"),
PropOrder._ArrayKey.getRootKey("foundryMagicvariant"),
new PropOrder._IgnoredKey("itemProperty"),
new PropOrder._IgnoredKey("reducedItemProperty"),
new PropOrder._IgnoredKey("itemType"),
new PropOrder._IgnoredKey("itemTypeAdditionalEntries"),
new PropOrder._IgnoredKey("reducedItemType"),
new PropOrder._IgnoredKey("itemEntry"),
PropOrder._ArrayKey.getRootKey("itemMastery"),
new PropOrder._IgnoredKey("linkedLootTables"),
PropOrder._ArrayKey.getRootKey("deck"),
PropOrder._ArrayKey.getRootKey("card"),
PropOrder._ArrayKey.getRootKey("deity"),
PropOrder._ArrayKey.getRootKey("language"),
PropOrder._ArrayKey.getRootKey("languageScript"),
PropOrder._ArrayKey.getRootKey("languageFluff"),
// endregion
// region GM-specific
PropOrder._ArrayKey.getRootKey("monster"),
PropOrder._ArrayKey.getRootKey("monsterFluff"),
PropOrder._ArrayKey.getRootKey("foundryMonster"),
PropOrder._ArrayKey.getRootKey("legendaryGroup"),
PropOrder._ArrayKey.getRootKey("monsterTemplate"),
PropOrder._ArrayKey.getRootKey("object"),
PropOrder._ArrayKey.getRootKey("objectFluff"),
PropOrder._ArrayKey.getRootKey("vehicle"),
PropOrder._ArrayKey.getRootKey("vehicleUpgrade"),
PropOrder._ArrayKey.getRootKey("foundryVehicleUpgrade"),
PropOrder._ArrayKey.getRootKey("vehicleFluff"),
PropOrder._ArrayKey.getRootKey("cult"),
PropOrder._ArrayKey.getRootKey("boon"),
PropOrder._ArrayKey.getRootKey("trap"),
PropOrder._ArrayKey.getRootKey("trapFluff"),
PropOrder._ArrayKey.getRootKey("hazard"),
PropOrder._ArrayKey.getRootKey("hazardFluff"),
PropOrder._ArrayKey.getRootKey("encounter"),
PropOrder._ArrayKey.getRootKey("name"),
// endregion
// region Rules
PropOrder._ArrayKey.getRootKey("variantrule"),
PropOrder._ArrayKey.getRootKey("table"),
PropOrder._ArrayKey.getRootKey("condition"),
PropOrder._ArrayKey.getRootKey("conditionFluff"),
PropOrder._ArrayKey.getRootKey("disease"),
PropOrder._ArrayKey.getRootKey("status"),
PropOrder._ArrayKey.getRootKey("action"),
PropOrder._ArrayKey.getRootKey("foundryAction"),
PropOrder._ArrayKey.getRootKey("skill"),
PropOrder._ArrayKey.getRootKey("sense"),
PropOrder._ArrayKey.getRootKey("citation"),
PropOrder._ArrayKey.getRootKey("adventure"),
PropOrder._ArrayKey.getRootKey("adventureData"),
PropOrder._ArrayKey.getRootKey("book"),
PropOrder._ArrayKey.getRootKey("bookData"),
// endregion
// region Other
PropOrder._ArrayKey.getRootKey("recipe"),
PropOrder._ArrayKey.getRootKey("recipeFluff"),
// endregion
// region Legacy content
PropOrder._ArrayKey.getRootKey("psionic"),
new PropOrder._IgnoredKey("psionicDisciplineFocus"),
new PropOrder._IgnoredKey("psionicDisciplineActive"),
// endregion
// region Tooling
PropOrder._ArrayKey.getRootKey("makebrewCreatureTrait"),
PropOrder._ArrayKey.getRootKey("makebrewCreatureAction"),
PropOrder._ArrayKey.getRootKey("monsterfeatures"),
// endregion
// region Roll20-specific
PropOrder._ArrayKey.getRootKey("roll20Spell"),
// endregion
// region Non-brew data
new PropOrder._IgnoredKey("blocklist"),
// endregion
// region Misc ignored keys
new PropOrder._IgnoredKey("data"),
// endregion
];
globalThis.PropOrder = PropOrder;