parent
baabb01011
commit
7c236bdc41
|
@ -26,19 +26,6 @@
|
||||||
"ItemsPacksMonsterHunter": "Monster Hunter's Pack",
|
"ItemsPacksMonsterHunter": "Monster Hunter's Pack",
|
||||||
"ItemsPacksPriest": "Priest's Pack",
|
"ItemsPacksPriest": "Priest's Pack",
|
||||||
"ItemsPacksScholar": "Scholar's Pack",
|
"ItemsPacksScholar": "Scholar's Pack",
|
||||||
"artificer": "Artificer",
|
|
||||||
"barbarian": "Barbarian",
|
|
||||||
"bard": "Bard",
|
|
||||||
"cleric": "Cleric",
|
|
||||||
"druid": "Druid",
|
|
||||||
"fighter": "Fighter",
|
|
||||||
"monk": "Monk",
|
|
||||||
"paladin": "Paladin",
|
|
||||||
"ranger": "Ranger",
|
|
||||||
"rogue": "Rogue",
|
|
||||||
"sorcerer": "Sorcerer",
|
|
||||||
"warlock": "Warlock",
|
|
||||||
"wizard": "Wizard",
|
|
||||||
"general": "General",
|
"general": "General",
|
||||||
"overall": "Overall Type",
|
"overall": "Overall Type",
|
||||||
"subfeature": "Subfeature Type",
|
"subfeature": "Subfeature Type",
|
||||||
|
@ -49,7 +36,7 @@
|
||||||
"dmgDealt": "Damage Dealt",
|
"dmgDealt": "Damage Dealt",
|
||||||
"Tab": {
|
"Tab": {
|
||||||
"SpellBrowser": "Spells",
|
"SpellBrowser": "Spells",
|
||||||
"FeatBrowser": "Feats",
|
"FeatBrowser": "Features",
|
||||||
"ItemBrowser": "Items",
|
"ItemBrowser": "Items",
|
||||||
"NPCBrowser": "Actors",
|
"NPCBrowser": "Actors",
|
||||||
"Settings": "Settings"
|
"Settings": "Settings"
|
||||||
|
|
|
@ -7,6 +7,39 @@ const NOT_MIGRATED = "NotMigratedException";
|
||||||
const COMPENDIUM_BROWSER = "compendium-browser";
|
const COMPENDIUM_BROWSER = "compendium-browser";
|
||||||
|
|
||||||
class CompendiumBrowser extends Application {
|
class CompendiumBrowser extends Application {
|
||||||
|
constructor(options={}) {
|
||||||
|
super(options);
|
||||||
|
|
||||||
|
this.provider = new dnd5eProvider();
|
||||||
|
|
||||||
|
// Reset the filters used in the dialog
|
||||||
|
this.spellFilters = {
|
||||||
|
registeredFilterCategorys: {},
|
||||||
|
activeFilters: {},
|
||||||
|
};
|
||||||
|
this.npcFilters = {
|
||||||
|
registeredFilterCategorys: {},
|
||||||
|
activeFilters: {},
|
||||||
|
};
|
||||||
|
this.featFilters = {
|
||||||
|
registeredFilterCategorys: {},
|
||||||
|
activeFilters: {},
|
||||||
|
};
|
||||||
|
this.itemFilters = {
|
||||||
|
registeredFilterCategorys: {},
|
||||||
|
activeFilters: {},
|
||||||
|
};
|
||||||
|
this.changeTabs = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setup() {
|
||||||
|
await this.provider.getClasses();
|
||||||
|
this.addSpellFilters();
|
||||||
|
this.addFeatFilters();
|
||||||
|
this.addItemFilters();
|
||||||
|
this.addNpcFilters();
|
||||||
|
}
|
||||||
|
|
||||||
static get defaultOptions() {
|
static get defaultOptions() {
|
||||||
return mergeObject(super.defaultOptions, {
|
return mergeObject(super.defaultOptions, {
|
||||||
title: "CMPBrowser.compendiumBrowser",
|
title: "CMPBrowser.compendiumBrowser",
|
||||||
|
@ -23,6 +56,10 @@ class CompendiumBrowser extends Application {
|
||||||
return game.settings.get(COMPENDIUM_BROWSER, "maxload");
|
return game.settings.get(COMPENDIUM_BROWSER, "maxload");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get settings() {
|
||||||
|
return game.settings.get(COMPENDIUM_BROWSER, "settings");
|
||||||
|
}
|
||||||
|
|
||||||
static get extraButtonsGlobal() {
|
static get extraButtonsGlobal() {
|
||||||
return game.settings.get(COMPENDIUM_BROWSER, "extraButtonsGlobal");
|
return game.settings.get(COMPENDIUM_BROWSER, "extraButtonsGlobal");
|
||||||
}
|
}
|
||||||
|
@ -43,33 +80,6 @@ class CompendiumBrowser extends Application {
|
||||||
return game.settings.get(COMPENDIUM_BROWSER, "bannersLocal");
|
return game.settings.get(COMPENDIUM_BROWSER, "bannersLocal");
|
||||||
}
|
}
|
||||||
|
|
||||||
async setup() {
|
|
||||||
// load settings
|
|
||||||
this.initSettings();
|
|
||||||
|
|
||||||
// Reset the filters used in the dialog
|
|
||||||
this.spellFilters = {
|
|
||||||
registeredFilterCategorys: {},
|
|
||||||
activeFilters: {},
|
|
||||||
};
|
|
||||||
this.npcFilters = {
|
|
||||||
registeredFilterCategorys: {},
|
|
||||||
activeFilters: {},
|
|
||||||
};
|
|
||||||
this.featFilters = {
|
|
||||||
registeredFilterCategorys: {},
|
|
||||||
activeFilters: {},
|
|
||||||
};
|
|
||||||
this.itemFilters = {
|
|
||||||
registeredFilterCategorys: {},
|
|
||||||
activeFilters: {},
|
|
||||||
};
|
|
||||||
this.addSpellFilters();
|
|
||||||
this.addFeatFilters();
|
|
||||||
this.addItemFilters();
|
|
||||||
this.addNpcFilters();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
_onChangeTab(event, tabs, active) {
|
_onChangeTab(event, tabs, active) {
|
||||||
super._onChangeTab(event, tabs, active);
|
super._onChangeTab(event, tabs, active);
|
||||||
|
@ -95,6 +105,15 @@ class CompendiumBrowser extends Application {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_activateCoreListeners(html) {
|
||||||
|
super._activateCoreListeners(html);
|
||||||
|
if (this.changeTabs !== null) {
|
||||||
|
const tabName = this.changeTabs.toString();
|
||||||
|
if (tabName !== this._tabs[0].active) this._tabs[0].activate(tabName);
|
||||||
|
this.changeTabs = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
activateItemListListeners(html) {
|
activateItemListListeners(html) {
|
||||||
// show entity sheet
|
// show entity sheet
|
||||||
html.find(".item-edit").click((ev) => {
|
html.find(".item-edit").click((ev) => {
|
||||||
|
@ -646,13 +665,15 @@ class CompendiumBrowser extends Application {
|
||||||
return npcs;
|
return npcs;
|
||||||
}
|
}
|
||||||
|
|
||||||
hookCompendiumList(html) {
|
hookCompendiumList(html, sidebarName) {
|
||||||
if (game.user.isGM
|
if (!game.user.isGM) {
|
||||||
|| this.settings.allowSpellBrowser
|
if (!this.settings.allowNpcBrowser && sidebarName === "actors") return;
|
||||||
|
if (!this.settings.allowItemBrowser && sidebarName === "items") return;
|
||||||
|
if (!(this.settings.allowSpellBrowser
|
||||||
|| this.settings.allowNpcBrowser
|
|| this.settings.allowNpcBrowser
|
||||||
|| this.settings.allowFeatBrowser
|
|| this.settings.allowFeatBrowser
|
||||||
|| this.settings.allowItemBrowser) {
|
|| this.settings.allowItemBrowser)) return;
|
||||||
|
}
|
||||||
const cbButton = $(
|
const cbButton = $(
|
||||||
`<button class="compendium-browser-btn"><i class="fas fa-fire"></i> ${game.i18n.localize(
|
`<button class="compendium-browser-btn"><i class="fas fa-fire"></i> ${game.i18n.localize(
|
||||||
"CMPBrowser.compendiumBrowser"
|
"CMPBrowser.compendiumBrowser"
|
||||||
|
@ -663,14 +684,20 @@ class CompendiumBrowser extends Application {
|
||||||
// adding to directory-list since the footer doesn't exist if the user is not gm
|
// adding to directory-list since the footer doesn't exist if the user is not gm
|
||||||
html.find(".directory-footer").append(cbButton);
|
html.find(".directory-footer").append(cbButton);
|
||||||
|
|
||||||
|
if (sidebarName === "compendium") sidebarName = null;
|
||||||
|
|
||||||
// Handle button clicks
|
// Handle button clicks
|
||||||
cbButton.click((ev) => {
|
cbButton.click((ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
// 0.4.1: Reset filters when you click button
|
|
||||||
this.resetFilters();
|
this.resetFilters();
|
||||||
// 0.4.3: Reset everything (including data) when you press the button - calls afterRender() hook
|
|
||||||
|
|
||||||
if (!this.refreshList) {
|
if (sidebarName === "actors") {
|
||||||
|
this.refreshList = "npc";
|
||||||
|
this.changeTabs = "npc";
|
||||||
|
} else if (sidebarName === "items") {
|
||||||
|
this.refreshList = "item";
|
||||||
|
this.changeTabs = "item";
|
||||||
|
} else if (!this.refreshList) {
|
||||||
if (game.user.isGM || this.settings.allowSpellBrowser) {
|
if (game.user.isGM || this.settings.allowSpellBrowser) {
|
||||||
this.refreshList = "spell";
|
this.refreshList = "spell";
|
||||||
} else if (this.settings.allowFeatBrowser) {
|
} else if (this.settings.allowFeatBrowser) {
|
||||||
|
@ -684,20 +711,13 @@ class CompendiumBrowser extends Application {
|
||||||
this.render(true);
|
this.render(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* Hook to load the first data */
|
/* Hook to load the first data */
|
||||||
static afterRender(cb, html) {
|
static afterRender(cb, html) {
|
||||||
// 0.4.3: Because a render always resets ALL the displayed filters (on all tabs) to unselected , we have to blank all the lists as well
|
if (!cb?.refreshList) return;
|
||||||
// (because the current HTML template doesn't set the selected filter values)
|
|
||||||
if (!cb?.refreshList) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cb.replaceList(html, cb.refreshList);
|
cb.replaceList(html, cb.refreshList);
|
||||||
|
|
||||||
// cb.refreshList = null;
|
|
||||||
|
|
||||||
if (CompendiumBrowser.postRender) {
|
if (CompendiumBrowser.postRender) {
|
||||||
CompendiumBrowser.postRender();
|
CompendiumBrowser.postRender();
|
||||||
}
|
}
|
||||||
|
@ -922,17 +942,19 @@ class CompendiumBrowser extends Application {
|
||||||
}
|
}
|
||||||
} else if (item.type === "feat" || item.type === "class") {
|
} else if (item.type === "feat" || item.type === "class") {
|
||||||
// getting class
|
// getting class
|
||||||
let reqString = item.requirements?.replace(/[0-9]/g, "").trim();
|
const matchedClass = [];
|
||||||
let matchedClass = [];
|
const reqString = item.requirements?.replace(/\d/g, "").trim();
|
||||||
for (let c in this.subClasses) {
|
if (reqString) {
|
||||||
if (reqString && reqString.toLowerCase().indexOf(c) !== -1) {
|
const reqStringLower = reqString.toLowerCase();
|
||||||
matchedClass.push(c);
|
for (const [className, classInfo] of Object.entries(this.provider.classes)) {
|
||||||
} else {
|
const isClassMatch = reqStringLower && className.toLowerCase().includes(reqStringLower);
|
||||||
for (let subClass of this.subClasses[c]) {
|
const isSubclassMatch = classInfo.subclasses.some(
|
||||||
if (reqString && reqString.indexOf(subClass) !== -1) {
|
// TODO compare with id and label
|
||||||
matchedClass.push(c);
|
(subClass) => reqString.includes(subClass)
|
||||||
break;
|
);
|
||||||
}
|
|
||||||
|
if (isClassMatch || isSubclassMatch) {
|
||||||
|
matchedClass.push(className);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -942,7 +964,6 @@ class CompendiumBrowser extends Application {
|
||||||
// getting uses/ressources status
|
// getting uses/ressources status
|
||||||
item.usesRessources = item5e.hasLimitedUses;
|
item.usesRessources = item5e.hasLimitedUses;
|
||||||
} else if (item.type === "subclass") {
|
} else if (item.type === "subclass") {
|
||||||
// subclasses dont exist lower then version 10
|
|
||||||
item.classRequirement = [item.system.classIdentifier];
|
item.classRequirement = [item.system.classIdentifier];
|
||||||
item.classRequirementString = item.system.classIdentifier;
|
item.classRequirementString = item.system.classIdentifier;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1145,113 +1166,6 @@ class CompendiumBrowser extends Application {
|
||||||
return newObj;
|
return newObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
initSettings() {
|
|
||||||
let defaultSettings = {
|
|
||||||
loadedSpellCompendium: {},
|
|
||||||
loadedNpcCompendium: {},
|
|
||||||
};
|
|
||||||
for (let compendium of game.packs) {
|
|
||||||
if (compendium.documentName === "Item") {
|
|
||||||
defaultSettings.loadedSpellCompendium[compendium.collection] = {
|
|
||||||
load: true,
|
|
||||||
name: `${compendium.metadata.label} (${compendium.collection})`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (compendium.documentName === "Actor") {
|
|
||||||
defaultSettings.loadedNpcCompendium[compendium.collection] = {
|
|
||||||
load: true,
|
|
||||||
name: `${compendium.metadata.label} (${compendium.collection})`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// creating game setting container
|
|
||||||
game.settings.register(COMPENDIUM_BROWSER, "settings", {
|
|
||||||
name: "Compendium Browser Settings",
|
|
||||||
hint: "Settings to exclude packs from loading and visibility of the browser",
|
|
||||||
default: defaultSettings,
|
|
||||||
type: Object,
|
|
||||||
scope: "world",
|
|
||||||
onChange: (settings) => {
|
|
||||||
this.settings = settings;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
game.settings.register(COMPENDIUM_BROWSER, "maxload", {
|
|
||||||
name: game.i18n.localize("CMPBrowser.SETTING.Maxload.NAME"),
|
|
||||||
hint: game.i18n.localize("CMPBrowser.SETTING.Maxload.HINT"),
|
|
||||||
scope: "world",
|
|
||||||
config: true,
|
|
||||||
default: 600,
|
|
||||||
type: Number,
|
|
||||||
range: {
|
|
||||||
// If range is specified, the resulting setting will be a range slider
|
|
||||||
min: 200,
|
|
||||||
max: 2000,
|
|
||||||
step: 100,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
game.settings.register(COMPENDIUM_BROWSER, "extraButtonsGlobal", {
|
|
||||||
name: game.i18n.localize("CMPBrowser.SETTING.extraButtonsGlobal.NAME"),
|
|
||||||
hint: game.i18n.localize("CMPBrowser.SETTING.extraButtonsGlobal.HINT"),
|
|
||||||
scope: "world",
|
|
||||||
config: true,
|
|
||||||
default: true,
|
|
||||||
type: Boolean,
|
|
||||||
});
|
|
||||||
game.settings.register(COMPENDIUM_BROWSER, "extraSheetButtons", {
|
|
||||||
name: game.i18n.localize("CMPBrowser.SETTING.extraSheetButtons.NAME"),
|
|
||||||
hint: game.i18n.localize("CMPBrowser.SETTING.extraSheetButtons.HINT"),
|
|
||||||
scope: "client",
|
|
||||||
config: true,
|
|
||||||
default: true,
|
|
||||||
type: Boolean,
|
|
||||||
});
|
|
||||||
game.settings.register(COMPENDIUM_BROWSER, "extraAdvancementButtons", {
|
|
||||||
name: game.i18n.localize("CMPBrowser.SETTING.extraAdvancementButtons.NAME"),
|
|
||||||
hint: game.i18n.localize("CMPBrowser.SETTING.extraAdvancementButtons.HINT"),
|
|
||||||
scope: "client",
|
|
||||||
config: true,
|
|
||||||
default: true,
|
|
||||||
type: Boolean,
|
|
||||||
});
|
|
||||||
game.settings.register(COMPENDIUM_BROWSER, "bannersGlobal", {
|
|
||||||
name: game.i18n.localize("CMPBrowser.SETTING.bannersGlobal.NAME"),
|
|
||||||
hint: game.i18n.localize("CMPBrowser.SETTING.bannersGlobal.HINT"),
|
|
||||||
scope: "world",
|
|
||||||
config: true,
|
|
||||||
default: true,
|
|
||||||
type: Boolean,
|
|
||||||
});
|
|
||||||
game.settings.register(COMPENDIUM_BROWSER, "bannersLocal", {
|
|
||||||
name: game.i18n.localize("CMPBrowser.SETTING.bannersLocal.NAME"),
|
|
||||||
hint: game.i18n.localize("CMPBrowser.SETTING.bannersLocal.HINT"),
|
|
||||||
scope: "client",
|
|
||||||
config: true,
|
|
||||||
default: true,
|
|
||||||
type: Boolean,
|
|
||||||
});
|
|
||||||
|
|
||||||
// load settings from container and apply to default settings (available compendie might have changed)
|
|
||||||
let settings = game.settings.get(COMPENDIUM_BROWSER, "settings");
|
|
||||||
for (let compKey in defaultSettings.loadedSpellCompendium) {
|
|
||||||
// v0.7.1 Check for settings.loadedSpellCompendium
|
|
||||||
if (settings.loadedSpellCompendium && settings.loadedSpellCompendium[compKey] !== undefined) {
|
|
||||||
defaultSettings.loadedSpellCompendium[compKey].load = settings.loadedSpellCompendium[compKey].load;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let compKey in defaultSettings.loadedNpcCompendium) {
|
|
||||||
// v0.7.1 Check for settings.loadedNpcCompendium
|
|
||||||
if (settings.loadedNpcCompendium && settings.loadedNpcCompendium[compKey] !== undefined) {
|
|
||||||
defaultSettings.loadedNpcCompendium[compKey].load = settings.loadedNpcCompendium[compKey].load;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defaultSettings.allowSpellBrowser = !!settings.allowSpellBrowser;
|
|
||||||
defaultSettings.allowFeatBrowser = !!settings.allowFeatBrowser;
|
|
||||||
defaultSettings.allowItemBrowser = !!settings.allowItemBrowser;
|
|
||||||
defaultSettings.allowNpcBrowser = !!settings.allowNpcBrowser;
|
|
||||||
|
|
||||||
this.settings = defaultSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FILTERS - Added on the Ready hook
|
// FILTERS - Added on the Ready hook
|
||||||
// 0.4.0 Make this async so filters can be added all at once
|
// 0.4.0 Make this async so filters can be added all at once
|
||||||
async addFilter(entityType, category, label, path, type, possibleValues = null, valIsArray = false) {
|
async addFilter(entityType, category, label, path, type, possibleValues = null, valIsArray = false) {
|
||||||
|
@ -1290,7 +1204,7 @@ class CompendiumBrowser extends Application {
|
||||||
}
|
}
|
||||||
|
|
||||||
async addSpellFilters() {
|
async addSpellFilters() {
|
||||||
this.addSpellFilter("CMPBrowser.general", "DND5E.Source", "system.source", "text");
|
this.addSpellFilter("CMPBrowser.general", "DND5E.Source", "system.source.book", "text");
|
||||||
this.addSpellFilter("CMPBrowser.general", "DND5E.Level", "system.level", "multiSelect", {
|
this.addSpellFilter("CMPBrowser.general", "DND5E.Level", "system.level", "multiSelect", {
|
||||||
0: "DND5E.SpellCantrip",
|
0: "DND5E.SpellCantrip",
|
||||||
1: "1",
|
1: "1",
|
||||||
|
@ -1332,23 +1246,17 @@ class CompendiumBrowser extends Application {
|
||||||
"select",
|
"select",
|
||||||
this._sortPackValues(CONFIG.DND5E.damageTypes)
|
this._sortPackValues(CONFIG.DND5E.damageTypes)
|
||||||
);
|
);
|
||||||
// JV-082: Fix for missing "Class" search feature
|
const classes = Object.fromEntries(
|
||||||
|
Object.entries(this.provider.classes).map(([k, v]) => {
|
||||||
|
return [k, v.label];
|
||||||
|
})
|
||||||
|
);
|
||||||
this.addSpellFilter(
|
this.addSpellFilter(
|
||||||
"CMPBrowser.general",
|
"CMPBrowser.general",
|
||||||
"ITEM.TypeClass",
|
"ITEM.TypeClass",
|
||||||
"classes",
|
"classes",
|
||||||
"select",
|
"select",
|
||||||
this._sortPackValues({
|
this._sortPackValues(classes),
|
||||||
artificer: "CMPBrowser.artificer",
|
|
||||||
bard: "CMPBrowser.bard",
|
|
||||||
cleric: "CMPBrowser.cleric",
|
|
||||||
druid: "CMPBrowser.druid",
|
|
||||||
paladin: "CMPBrowser.paladin",
|
|
||||||
ranger: "CMPBrowser.ranger",
|
|
||||||
sorcerer: "CMPBrowser.sorcerer",
|
|
||||||
warlock: "CMPBrowser.warlock",
|
|
||||||
wizard: "CMPBrowser.wizard",
|
|
||||||
}),
|
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
this.addSpellFilter("DND5E.SpellComponents", "DND5E.Ritual", "system.properties.ritual", "bool");
|
this.addSpellFilter("DND5E.SpellComponents", "DND5E.Ritual", "system.properties.ritual", "bool");
|
||||||
|
@ -1359,7 +1267,7 @@ class CompendiumBrowser extends Application {
|
||||||
}
|
}
|
||||||
|
|
||||||
async addItemFilters() {
|
async addItemFilters() {
|
||||||
this.addItemFilter("CMPBrowser.general", "DND5E.Source", "system.source", "text");
|
this.addItemFilter("CMPBrowser.general", "DND5E.Source", "system.source.book", "text");
|
||||||
|
|
||||||
this.addItemFilter(
|
this.addItemFilter(
|
||||||
"CMPBrowser.general",
|
"CMPBrowser.general",
|
||||||
|
@ -1444,27 +1352,18 @@ class CompendiumBrowser extends Application {
|
||||||
async addFeatFilters() {
|
async addFeatFilters() {
|
||||||
// Feature Filters
|
// Feature Filters
|
||||||
// Foundry v10+ Item#data is now Item#system
|
// Foundry v10+ Item#data is now Item#system
|
||||||
this.addFeatFilter("CMPBrowser.general", "DND5E.Source", "system.source", "text");
|
this.addFeatFilter("CMPBrowser.general", "DND5E.Source", "system.source.book", "text");
|
||||||
|
const classes = Object.fromEntries(
|
||||||
|
Object.entries(this.provider.classes).map(([k, v]) => {
|
||||||
|
return [k, v.label];
|
||||||
|
})
|
||||||
|
);
|
||||||
this.addFeatFilter(
|
this.addFeatFilter(
|
||||||
"CMPBrowser.general",
|
"CMPBrowser.general",
|
||||||
"ITEM.TypeClass",
|
"ITEM.TypeClass",
|
||||||
"classRequirement",
|
"classRequirement",
|
||||||
"select",
|
"select",
|
||||||
this._sortPackValues({
|
this._sortPackValues(classes),
|
||||||
artificer: "CMPBrowser.artificer",
|
|
||||||
barbarian: "CMPBrowser.barbarian",
|
|
||||||
bard: "CMPBrowser.bard",
|
|
||||||
cleric: "CMPBrowser.cleric",
|
|
||||||
druid: "CMPBrowser.druid",
|
|
||||||
fighter: "CMPBrowser.fighter",
|
|
||||||
monk: "CMPBrowser.monk",
|
|
||||||
paladin: "CMPBrowser.paladin",
|
|
||||||
ranger: "CMPBrowser.ranger",
|
|
||||||
rogue: "CMPBrowser.rogue",
|
|
||||||
sorcerer: "CMPBrowser.sorcerer",
|
|
||||||
warlock: "CMPBrowser.warlock",
|
|
||||||
wizard: "CMPBrowser.wizard",
|
|
||||||
}),
|
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1539,7 +1438,7 @@ class CompendiumBrowser extends Application {
|
||||||
async addNpcFilters() {
|
async addNpcFilters() {
|
||||||
// NPC Filters
|
// NPC Filters
|
||||||
|
|
||||||
this.addNpcFilter("CMPBrowser.general", "DND5E.Source", "system.details.source", "text");
|
this.addNpcFilter("CMPBrowser.general", "DND5E.Source", "system.details.source.book", "text");
|
||||||
this.addNpcFilter("CMPBrowser.general", "DND5E.Size", "system.traits.size", "select", CONFIG.DND5E.actorSizes);
|
this.addNpcFilter("CMPBrowser.general", "DND5E.Size", "system.traits.size", "select", CONFIG.DND5E.actorSizes);
|
||||||
|
|
||||||
this.addNpcFilter("CMPBrowser.general", "CMPBrowser.hasLegAct", "system.resources.legact.max", "bool");
|
this.addNpcFilter("CMPBrowser.general", "CMPBrowser.hasLegAct", "system.resources.legact.max", "bool");
|
||||||
|
@ -1796,260 +1695,27 @@ class CompendiumBrowser extends Application {
|
||||||
console.warn(filterTarget);
|
console.warn(filterTarget);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async addTidySheetButton(cb, html, actor) {
|
|
||||||
await CompendiumBrowser.createBanners(html);
|
|
||||||
await CompendiumBrowser.addButtons(html, actor);
|
|
||||||
}
|
|
||||||
|
|
||||||
static async addButtons(html, actor) {
|
|
||||||
|
|
||||||
// exit out because we dont want these
|
|
||||||
if (!CompendiumBrowser.extraButtonsGlobal || !CompendiumBrowser.extraSheetButtons) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await CompendiumBrowser.addTidyFeatureButton(html, "race");
|
|
||||||
await CompendiumBrowser.addTidyFeatureButton(html, "background");
|
|
||||||
await CompendiumBrowser.addTidyFeatureButton(html, "class");
|
|
||||||
|
|
||||||
await html.find(".spell-browser-btn").remove();
|
|
||||||
|
|
||||||
let tabBar = html.find("div.tab.spellbook .spellcasting-ability");
|
|
||||||
|
|
||||||
const tooltip = game.i18n.localize("CMPBrowser.ToolTip.Spells");
|
|
||||||
const cbButton = $(
|
|
||||||
`<div style="flex: 0 0 22px; align-self: center; text-align: center;">
|
|
||||||
<a title="${tooltip}" class="compendium-browser spell-browser-btn">
|
|
||||||
<i class="fa-duotone fa-book"></i>
|
|
||||||
</a>
|
|
||||||
</div>`
|
|
||||||
);
|
|
||||||
|
|
||||||
tabBar.append(cbButton);
|
|
||||||
|
|
||||||
CompendiumBrowser.addSpellsButton(cbButton, actor.actor);
|
|
||||||
}
|
|
||||||
|
|
||||||
static async createBanners(html) {
|
|
||||||
// Don't build the banners if configuration is turned off
|
|
||||||
if (!CompendiumBrowser.bannersGlobal || !CompendiumBrowser.bannersLocal) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let MAP_THING = {};
|
|
||||||
MAP_THING[game.i18n.localize("DND5E.Race")] = "race";
|
|
||||||
MAP_THING[game.i18n.localize("DND5E.Background")] = "background";
|
|
||||||
MAP_THING[game.i18n.localize("ITEM.TypeClassPl")] = "class";
|
|
||||||
|
|
||||||
let isSearchable = (name) => {
|
|
||||||
return Object.keys(MAP_THING).includes(name);
|
|
||||||
};
|
|
||||||
|
|
||||||
// searches in a similar way to how tidy sheets does it.
|
|
||||||
// probably should just use actor data instead of going through the html
|
|
||||||
html.find(".inventory-list.features-list .item-list").filter(function () {
|
|
||||||
// find any section that is searchable
|
|
||||||
return isSearchable($(this.previousElementSibling).find("h3.item-name")[0].innerText)
|
|
||||||
// find any section that is empty
|
|
||||||
&& $(this).find("li.item").length === 0;
|
|
||||||
}).each( function () {
|
|
||||||
let type = MAP_THING[$(this.previousElementSibling).find("h3.item-name")[0].innerText];
|
|
||||||
let banner = $(`<span class="notice" style="background:rgba(30, 30, 30, 1)">${game.i18n.localize(`CMPBrowser.FindA.${type}`)}</span>`);
|
|
||||||
|
|
||||||
banner.insertAfter(this);
|
|
||||||
|
|
||||||
banner.click(async (ev) => {
|
|
||||||
ev.preventDefault();
|
|
||||||
|
|
||||||
game.compendiumBrowser.renderWith("feat", [{ section: "CMPBrowsergeneral", label: "CMPBrowser.overall", value: type }]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static async addDefaultSheetButton(cb, html, actor) {
|
|
||||||
// exit out because we dont want these
|
|
||||||
if (!CompendiumBrowser.extraButtonsGlobal || !CompendiumBrowser.extraSheetButtons) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cb.options.classes.includes("tidy5e")) {
|
|
||||||
// no need as tidy sheet render will handle it
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await CompendiumBrowser.addDefaultFeatureButton(html, "race");
|
|
||||||
await CompendiumBrowser.addDefaultFeatureButton(html, "background");
|
|
||||||
await CompendiumBrowser.addDefaultFeatureButton(html, "class");
|
|
||||||
|
|
||||||
// handle spell browser button
|
|
||||||
await html.find(".spell-browser-btn").remove();
|
|
||||||
|
|
||||||
let tabBar = html.find("div.spellbook-filters");
|
|
||||||
const cbButton = $(
|
|
||||||
`<div style="flex: 0 0 22px; align-self: center; text-align: center;">
|
|
||||||
<a data-tooltip="CMPBrowser.ToolTip.Spells" class="compendium-browser spell-browser-btn">
|
|
||||||
<i class="fa-duotone fa-book"></i>
|
|
||||||
</a>
|
|
||||||
</div>`
|
|
||||||
);
|
|
||||||
|
|
||||||
tabBar.append(cbButton);
|
|
||||||
|
|
||||||
CompendiumBrowser.addSpellsButton(cbButton, actor.actor);
|
|
||||||
}
|
|
||||||
|
|
||||||
static addSpellsButton(cbButton, character) {
|
|
||||||
cbButton.click(async (ev) => {
|
|
||||||
ev.preventDefault();
|
|
||||||
|
|
||||||
let target = [];
|
|
||||||
|
|
||||||
target = target.concat(CompendiumBrowser.findCasterClass(character));
|
|
||||||
target = target.concat(CompendiumBrowser.findMaxCasterLevel(character));
|
|
||||||
|
|
||||||
game.compendiumBrowser.renderWith("spell", target);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static async addTidyFeatureButton(html, type) {
|
|
||||||
const featBars = html.find(`div.features a.item-create[data-type="${type}"]`);
|
|
||||||
|
|
||||||
const tooltip = game.i18n.localize("CMPBrowser.ToolTip.Features");
|
|
||||||
const cbButton = $(
|
|
||||||
`<a style="flex: 0 0 15px; align-self: center; text-align: center; class="compendium-browser ${type}-browser-btn" title="${tooltip}">
|
|
||||||
<i class="fa-duotone fa-book"></i>
|
|
||||||
</a>`
|
|
||||||
);
|
|
||||||
|
|
||||||
$(featBars[0].parentNode).append(cbButton);
|
|
||||||
cbButton.click(async (ev) => {
|
|
||||||
ev.preventDefault();
|
|
||||||
|
|
||||||
game.compendiumBrowser.renderWith("feat", [{ section: "CMPBrowsergeneral", label: "CMPBrowser.overall", value: type }]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static async addDefaultFeatureButton(html, type) {
|
|
||||||
await html.find(`.${type}-browser-btn`).remove();
|
|
||||||
|
|
||||||
const featBars = html.find(`div.features li.items-header a.item-control[data-type="${type}"]`);
|
|
||||||
|
|
||||||
// Other sheets (like tidysheet) may cause this problem
|
|
||||||
if (!featBars.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cbButton = $(
|
|
||||||
`<a style="flex: 0 0 15px; align-self: center; text-align: center; class="compendium-browser ${type}-browser-btn" data-tooltip="CMPBrowser.ToolTip.Features">
|
|
||||||
<i class="fa-duotone fa-book"></i>
|
|
||||||
</a>`
|
|
||||||
);
|
|
||||||
|
|
||||||
$(featBars[0].parentNode).append(cbButton);
|
|
||||||
$(featBars[0].parentNode).css({"flex-basis": "60px"});
|
|
||||||
|
|
||||||
cbButton.click(async (ev) => {
|
|
||||||
ev.preventDefault();
|
|
||||||
|
|
||||||
game.compendiumBrowser.renderWith("feat", [{ section: "CMPBrowsergeneral", label: "CMPBrowser.overall", value: type }]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static async addASISheetButton(cb, html) {
|
|
||||||
// exit out because we dont want these
|
|
||||||
if (!CompendiumBrowser.extraButtonsGlobal || !CompendiumBrowser.extraAdvancementButtons) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await html.find(".feat-browser-btn").remove();
|
|
||||||
|
|
||||||
let dropArea = html.find("h3:nth-child(3)");
|
|
||||||
const cbButton = $(
|
|
||||||
`<span style="font-size: 16px;">
|
|
||||||
<a data-tooltip="CMPBrowser.ToolTip.Feats" class="compendium-browser feat-browser-btn">
|
|
||||||
<i class="fa-duotone fa-book"></i>
|
|
||||||
</a>
|
|
||||||
</span>`
|
|
||||||
);
|
|
||||||
|
|
||||||
dropArea.append(cbButton);
|
|
||||||
|
|
||||||
cbButton.click(async (ev) => {
|
|
||||||
ev.preventDefault();
|
|
||||||
|
|
||||||
game.compendiumBrowser.renderWith("feat", [
|
|
||||||
{
|
|
||||||
section: "CMPBrowsergeneral",
|
|
||||||
label: "DND5EItemFeatureType",
|
|
||||||
value: "feat",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// find the first caster class of the character
|
|
||||||
static findCasterClass(character) {
|
|
||||||
const options = ["artificer", "bard", "cleric", "druid", "paladin", "ranger", "sorcerer", "warlock", "wizard"];
|
|
||||||
|
|
||||||
for (let cls of Object.keys(character.classes)) {
|
|
||||||
if (options.includes(cls)) {
|
|
||||||
return [{ section: "CMPBrowsergeneral", label: "ITEM.TypeClass", value: cls }];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
static findMaxCasterLevel(character) {
|
|
||||||
// find max spell level
|
|
||||||
let maxLevel = Object.keys(character.system.spells).reduce((acc, spell) => {
|
|
||||||
// special case for pact magic
|
|
||||||
if (spell === "pact") {
|
|
||||||
return Math.max(character.system.spells[spell].level, acc);
|
|
||||||
} else {
|
|
||||||
let spellObject = character.system.spells[spell];
|
|
||||||
if ((spellObject.override ?? spellObject.max) > 0) {
|
|
||||||
let match = spell.match(/spell(?<lvl>\d+)/);
|
|
||||||
return Math.max(parseInt(match.groups.lvl), acc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
if (maxLevel > 0) {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
section: "CMPBrowsergeneral",
|
|
||||||
label: "DND5ELevel",
|
|
||||||
values: [...Array(maxLevel + 1).keys()],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Hooks.once("init", async () => {
|
Hooks.once("init", async () => {
|
||||||
registerSettings();
|
|
||||||
await preloadTemplates();
|
await preloadTemplates();
|
||||||
|
game.compendiumBrowser = new CompendiumBrowser();
|
||||||
});
|
});
|
||||||
|
|
||||||
Hooks.once("setup", () => {
|
Hooks.once("setup", async () => {
|
||||||
game.compendiumBrowser = new CompendiumBrowser();
|
registerSettings();
|
||||||
game.compendiumBrowser.setup();
|
await game.compendiumBrowser.setup();
|
||||||
});
|
});
|
||||||
|
|
||||||
Hooks.on("changeSidebarTab", (app) => {
|
Hooks.on("changeSidebarTab", (app) => {
|
||||||
if (app.tabName !== "compendium") return;
|
if (["actors", "items", "compendium"].includes(app.tabName)) {
|
||||||
game.compendiumBrowser.hookCompendiumList(app.element);
|
game.compendiumBrowser.hookCompendiumList(app.element, app.tabName);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
Hooks.on("renderSidebarTab", (app, html, data) => {
|
Hooks.on("renderSidebarTab", (app, html, data) => {
|
||||||
if (app.tabName !== "compendium") return;
|
if (["actors", "items", "compendium"].includes(app.tabName)) {
|
||||||
game.compendiumBrowser.hookCompendiumList(html);
|
game.compendiumBrowser.hookCompendiumList(html, app.tabName);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function stripSpecialCharacters(str) {
|
function stripSpecialCharacters(str) {
|
||||||
|
@ -2089,8 +1755,4 @@ function getPropByString(obj, propString) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Hooks.on("renderActorSheet5eCharacter", CompendiumBrowser.addDefaultSheetButton);
|
|
||||||
Hooks.on("renderTidy5eSheet", CompendiumBrowser.addTidySheetButton);
|
|
||||||
Hooks.on("renderAbilityScoreImprovementFlow", CompendiumBrowser.addASISheetButton);
|
|
||||||
|
|
||||||
Hooks.on("renderCompendiumBrowser", CompendiumBrowser.afterRender);
|
Hooks.on("renderCompendiumBrowser", CompendiumBrowser.afterRender);
|
||||||
|
|
|
@ -1,4 +1,50 @@
|
||||||
export class dnd5eProvider {
|
export class dnd5eProvider {
|
||||||
|
classes = {};
|
||||||
|
|
||||||
|
async getClasses() {
|
||||||
|
const subclasses = {};
|
||||||
|
for (let pack of game.packs) {
|
||||||
|
if (pack.documentName === "Item") {
|
||||||
|
const indexes = await pack.getIndex({ fields: ["system.identifier", "system.classIdentifier"] });
|
||||||
|
const classes = indexes.filter((entry) => entry.type === "class");
|
||||||
|
if (classes.length) {
|
||||||
|
classes.map((entry) => {
|
||||||
|
return {
|
||||||
|
label: entry.name,
|
||||||
|
identifier: entry.system.identifier
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.forEach((c) => {
|
||||||
|
this.classes[c.identifier] = {
|
||||||
|
label: c.label,
|
||||||
|
subclasses: []
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const _subclasses = indexes.filter((entry) => entry.type === "subclass");
|
||||||
|
if (_subclasses.length) {
|
||||||
|
_subclasses.map((entry) => {
|
||||||
|
return {
|
||||||
|
identifier: entry.system.identifier,
|
||||||
|
classId: entry.system.classIdentifier
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.forEach((subclass) => {
|
||||||
|
if (subclasses[subclass.classId]) {
|
||||||
|
subclasses[subclass.classId].subclasses.push(subclass.identifier);
|
||||||
|
} else {
|
||||||
|
subclasses[subclass.classId] = {
|
||||||
|
// TODO change to object with {label, id} pair
|
||||||
|
subclasses: [subclass.identifier]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.classes = foundry.utils.mergeObject(this.classes, subclasses);
|
||||||
|
}
|
||||||
|
|
||||||
static classList = {
|
static classList = {
|
||||||
abidalzimshorridwilting: "sorcerer,wizard",
|
abidalzimshorridwilting: "sorcerer,wizard",
|
||||||
absorbelements: "artificer,druid,ranger,sorcerer,wizard",
|
absorbelements: "artificer,druid,ranger,sorcerer,wizard",
|
||||||
|
|
|
@ -4,4 +4,84 @@
|
||||||
|
|
||||||
export function registerSettings() {
|
export function registerSettings() {
|
||||||
// Register any custom module settings here
|
// Register any custom module settings here
|
||||||
|
const defaultSettings = {
|
||||||
|
loadedSpellCompendium: {},
|
||||||
|
loadedNpcCompendium: {},
|
||||||
|
};
|
||||||
|
for (const compendium of game.packs) {
|
||||||
|
if (compendium.documentName === "Item") {
|
||||||
|
defaultSettings.loadedSpellCompendium[compendium.collection] = {
|
||||||
|
load: true,
|
||||||
|
name: `${compendium.metadata.label} (${compendium.collection})`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (compendium.documentName === "Actor") {
|
||||||
|
defaultSettings.loadedNpcCompendium[compendium.collection] = {
|
||||||
|
load: true,
|
||||||
|
name: `${compendium.metadata.label} (${compendium.collection})`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// creating game setting container
|
||||||
|
game.settings.register("compendium-browser", "settings", {
|
||||||
|
name: "Compendium Browser Settings",
|
||||||
|
hint: "Settings to exclude packs from loading and visibility of the browser",
|
||||||
|
default: defaultSettings,
|
||||||
|
type: Object,
|
||||||
|
scope: "world"
|
||||||
|
});
|
||||||
|
game.settings.register("compendium-browser", "maxload", {
|
||||||
|
name: game.i18n.localize("CMPBrowser.SETTING.Maxload.NAME"),
|
||||||
|
hint: game.i18n.localize("CMPBrowser.SETTING.Maxload.HINT"),
|
||||||
|
scope: "world",
|
||||||
|
config: true,
|
||||||
|
default: 600,
|
||||||
|
type: Number,
|
||||||
|
range: {
|
||||||
|
// If range is specified, the resulting setting will be a range slider
|
||||||
|
min: 200,
|
||||||
|
max: 2000,
|
||||||
|
step: 100,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
game.settings.register("compendium-browser", "extraButtonsGlobal", {
|
||||||
|
name: game.i18n.localize("CMPBrowser.SETTING.extraButtonsGlobal.NAME"),
|
||||||
|
hint: game.i18n.localize("CMPBrowser.SETTING.extraButtonsGlobal.HINT"),
|
||||||
|
scope: "world",
|
||||||
|
config: true,
|
||||||
|
default: true,
|
||||||
|
type: Boolean,
|
||||||
|
});
|
||||||
|
game.settings.register("compendium-browser", "extraSheetButtons", {
|
||||||
|
name: game.i18n.localize("CMPBrowser.SETTING.extraSheetButtons.NAME"),
|
||||||
|
hint: game.i18n.localize("CMPBrowser.SETTING.extraSheetButtons.HINT"),
|
||||||
|
scope: "client",
|
||||||
|
config: true,
|
||||||
|
default: true,
|
||||||
|
type: Boolean,
|
||||||
|
});
|
||||||
|
game.settings.register("compendium-browser", "extraAdvancementButtons", {
|
||||||
|
name: game.i18n.localize("CMPBrowser.SETTING.extraAdvancementButtons.NAME"),
|
||||||
|
hint: game.i18n.localize("CMPBrowser.SETTING.extraAdvancementButtons.HINT"),
|
||||||
|
scope: "client",
|
||||||
|
config: true,
|
||||||
|
default: true,
|
||||||
|
type: Boolean,
|
||||||
|
});
|
||||||
|
game.settings.register("compendium-browser", "bannersGlobal", {
|
||||||
|
name: game.i18n.localize("CMPBrowser.SETTING.bannersGlobal.NAME"),
|
||||||
|
hint: game.i18n.localize("CMPBrowser.SETTING.bannersGlobal.HINT"),
|
||||||
|
scope: "world",
|
||||||
|
config: true,
|
||||||
|
default: true,
|
||||||
|
type: Boolean,
|
||||||
|
});
|
||||||
|
game.settings.register("compendium-browser", "bannersLocal", {
|
||||||
|
name: game.i18n.localize("CMPBrowser.SETTING.bannersLocal.NAME"),
|
||||||
|
hint: game.i18n.localize("CMPBrowser.SETTING.bannersLocal.HINT"),
|
||||||
|
scope: "client",
|
||||||
|
config: true,
|
||||||
|
default: true,
|
||||||
|
type: Boolean,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue