Improve Class item support

Fix #7.
master
Matheus Clemente 2024-02-26 19:09:29 -03:00
parent baabb01011
commit 7c236bdc41
4 changed files with 257 additions and 482 deletions

View File

@ -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"

View File

@ -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,58 +665,59 @@ 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;
|| this.settings.allowNpcBrowser if (!this.settings.allowItemBrowser && sidebarName === "items") return;
|| this.settings.allowFeatBrowser if (!(this.settings.allowSpellBrowser
|| this.settings.allowItemBrowser) { || this.settings.allowNpcBrowser
|| this.settings.allowFeatBrowser
const cbButton = $( || this.settings.allowItemBrowser)) return;
`<button class="compendium-browser-btn"><i class="fas fa-fire"></i> ${game.i18n.localize(
"CMPBrowser.compendiumBrowser"
)}</button>`
);
html.find(".compendium-browser-btn").remove();
// adding to directory-list since the footer doesn't exist if the user is not gm
html.find(".directory-footer").append(cbButton);
// Handle button clicks
cbButton.click((ev) => {
ev.preventDefault();
// 0.4.1: Reset filters when you click button
this.resetFilters();
// 0.4.3: Reset everything (including data) when you press the button - calls afterRender() hook
if (!this.refreshList) {
if (game.user.isGM || this.settings.allowSpellBrowser) {
this.refreshList = "spell";
} else if (this.settings.allowFeatBrowser) {
this.refreshList = "feat";
} else if (this.settings.allowItemBrowser) {
this.refreshList = "item";
} else if (this.settings.allowNPCBrowser) {
this.refreshList = "npc";
}
}
this.render(true);
});
} }
const cbButton = $(
`<button class="compendium-browser-btn"><i class="fas fa-fire"></i> ${game.i18n.localize(
"CMPBrowser.compendiumBrowser"
)}</button>`
);
html.find(".compendium-browser-btn").remove();
// adding to directory-list since the footer doesn't exist if the user is not gm
html.find(".directory-footer").append(cbButton);
if (sidebarName === "compendium") sidebarName = null;
// Handle button clicks
cbButton.click((ev) => {
ev.preventDefault();
this.resetFilters();
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) {
this.refreshList = "spell";
} else if (this.settings.allowFeatBrowser) {
this.refreshList = "feat";
} else if (this.settings.allowItemBrowser) {
this.refreshList = "item";
} else if (this.settings.allowNPCBrowser) {
this.refreshList = "npc";
}
}
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);

View File

@ -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",

View File

@ -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,
});
} }