Files
5etools-mirror-2.github.io/js/spells.js
TheGiddyLimit 5e0cc455b9 v1.204.0
2024-04-02 22:46:42 +01:00

413 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use strict";
class SpellsSublistManager extends SublistManager {
static get _ROW_TEMPLATE () {
return [
new SublistCellTemplate({
name: "Name",
css: "bold ve-col-3-2 pl-0",
colStyle: "",
}),
new SublistCellTemplate({
name: "Level",
css: "capitalize ve-col-1-5 ve-text-center",
colStyle: "text-center",
}),
new SublistCellTemplate({
name: "Time",
css: "ve-col-1-8 ve-text-center",
colStyle: "text-center",
}),
new SublistCellTemplate({
name: "School",
css: "capitalize ve-col-1-6 ve-text-center",
colStyle: "text-center",
}),
new SublistCellTemplate({
name: "C.",
css: "concentration--sublist ve-col-0-7 ve-text-center",
colStyle: "text-center",
}),
new SublistCellTemplate({
name: "Range",
css: "range ve-col-3-2 pr-0 text-right",
colStyle: "text-right",
}),
];
}
pGetSublistItem (spell, hash) {
const school = Parser.spSchoolAndSubschoolsAbvsShort(spell.school, spell.subschools);
const time = PageFilterSpells.getTblTimeStr(spell.time[0]);
const concentration = spell._isConc ? "×" : "";
const range = Parser.spRangeToFull(spell.range);
const cellsText = [
spell.name,
PageFilterSpells.getTblLevelStr(spell),
time,
new SublistCell({
text: school,
title: Parser.spSchoolAndSubschoolsAbvsToFull(spell.school, spell.subschools),
css: `sp__school-${spell.school}`,
style: Parser.spSchoolAbvToStyle(spell.school),
}),
new SublistCell({
text: concentration,
title: concentration ? "Concentration" : "",
}),
range,
];
const $ele = $(`<div class="lst__row lst__row--sublist ve-flex-col">
<a href="#${UrlUtil.autoEncodeHash(spell)}" title="${spell.name}" class="lst--border lst__row-inner">
${this.constructor._getRowCellsHtml({values: cellsText})}
</a>
</div>`)
.contextmenu(evt => this._handleSublistItemContextMenu(evt, listItem))
.click(evt => this._listSub.doSelect(listItem, evt));
const listItem = new ListItem(
hash,
$ele,
spell.name,
{
hash,
school,
level: spell.level,
time,
concentration,
range,
normalisedTime: spell._normalisedTime,
normalisedRange: spell._normalisedRange,
},
{
entity: spell,
mdRow: [...cellsText],
},
);
return listItem;
}
}
class SpellsPageSettingsManager extends ListPageSettingsManager {
_getSettings () {
return {
...RenderSpells.SETTINGS,
};
}
}
class SpellPageBookView extends ListPageBookView {
static _BOOK_VIEW_MODE_K = "bookViewMode";
constructor (opts) {
super({
pageTitle: "Spells Book View",
namePlural: "spells",
propMarkdown: "spell",
...opts,
});
this._bookViewLastOrder = null;
this._$wrpContent = null;
}
_getSorted (a, b) {
return this._bookViewLastOrder === "0" ? SortUtil.ascSort(a.level, b.level) : SortUtil.ascSortLower(a.name, b.name);
}
async _$pGetWrpControls ({$wrpContent}) {
const out = await super._$pGetWrpControls({$wrpContent});
const {$wrpPrint} = out;
this._bookViewLastOrder = StorageUtil.syncGetForPage(SpellPageBookView._BOOK_VIEW_MODE_K);
if (this._bookViewLastOrder != null) this._bookViewLastOrder = `${this._bookViewLastOrder}`;
const $selSortMode = $(`<select class="form-control input-sm">
<option value="0">Spell Level</option>
<option value="1">Alphabetical</option>
</select>`)
.change(() => {
if (!this._bookViewToShow.length && Hist.lastLoadedId != null) return;
const val = $selSortMode.val();
if (val === "0") this._renderByLevel();
else this._renderByAlpha();
StorageUtil.syncSetForPage(SpellPageBookView._BOOK_VIEW_MODE_K, val);
});
if (this._bookViewLastOrder != null) $selSortMode.val(this._bookViewLastOrder);
$$`<div class="ve-flex-vh-center ml-3"><div class="mr-2 no-wrap">Sort order:</div>${$selSortMode}</div>`.appendTo($wrpPrint);
return out;
}
_renderSpell ({stack, sp}) {
stack.push(`<div class="bkmv__wrp-item ve-inline-block print__ve-block print__my-2"><table class="w-100 stats stats--book stats--bkmv"><tbody>`);
stack.push(Renderer.spell.getCompactRenderedString(sp));
stack.push(`</tbody></table></div>`);
}
_renderByLevel () {
let isAnyEntityRendered = false;
const stack = [];
for (let i = 0; i < 10; ++i) {
const atLvl = this._bookViewToShow.filter(sp => sp.level === i);
if (atLvl.length) {
stack.push(`<div class="bkmv__no-breaks">`);
stack.push(`<div class="bkmv__spacer-name ve-flex-v-center no-shrink no-print pl-2">${Parser.spLevelToFullLevelText(i)}</div>`);
atLvl.forEach(sp => this._renderSpell({stack, sp}));
isAnyEntityRendered = true;
stack.push(`</div>`);
}
}
this._$wrpContent.empty().append(stack.join(""));
this._bookViewLastOrder = "0";
return {isAnyEntityRendered};
}
_renderByAlpha () {
const stack = [];
this._bookViewToShow.forEach(sp => this._renderSpell({stack, sp}));
this._$wrpContent.empty().append(stack.join(""));
this._bookViewLastOrder = "1";
return {isAnyEntityRendered: !!this._bookViewToShow.length};
}
_renderNoneSelected () {
const stack = [];
stack.push(`<div class="w-100 h-100 no-breaks">`);
this._renderSpell({stack, sp: this._fnGetEntLastLoaded()});
stack.push(`</div>`);
this._$wrpContent.empty().append(stack.join(""));
return {isAnyEntityRendered: false};
}
_renderSpells () {
if (!this._bookViewToShow.length && Hist.lastLoadedId != null) return this._renderNoneSelected();
else if (this._bookViewLastOrder === "1") return this._renderByAlpha();
else return this._renderByLevel();
}
async _pGetRenderContentMeta ({$wrpContent, $wrpControls}) {
this._$wrpContent = $wrpContent;
$wrpContent.addClass("p-2");
this._bookViewToShow = this._sublistManager.getSublistedEntities()
.sort((a, b) => SortUtil.ascSortLower(a.name, b.name));
const {isAnyEntityRendered} = this._renderSpells();
return {
cntSelectedEnts: this._bookViewToShow.length,
isAnyEntityRendered,
};
}
}
class SpellsPage extends ListPageMultiSource {
constructor () {
const pFnGetFluff = Renderer.spell.pGetFluff.bind(Renderer.spell);
super({
pageFilter: new PageFilterSpells(),
listOptions: {
fnSort: PageFilterSpells.sortSpells,
},
dataProps: ["spell"],
pFnGetFluff,
bookViewOptions: {
ClsBookView: SpellPageBookView,
},
tableViewOptions: {
title: "Spells",
colTransforms: {
name: UtilsTableview.COL_TRANSFORM_NAME,
source: UtilsTableview.COL_TRANSFORM_SOURCE,
page: UtilsTableview.COL_TRANSFORM_PAGE,
level: {name: "Level", transform: (it) => Parser.spLevelToFull(it)},
time: {name: "Casting Time", transform: (it) => PageFilterSpells.getTblTimeStr(it[0])},
duration: {name: "Duration", transform: (it) => Parser.spDurationToFull(it)},
_school: {
name: "School",
transform: (sp) => {
const ptMeta = Parser.spMetaToArr(sp.meta);
return `<span class="sp__school-${sp.school}" ${Parser.spSchoolAbvToStyle(sp.school)}>${Parser.spSchoolAndSubschoolsAbvsToFull(sp.school, sp.subschools)}</span>${ptMeta.length ? ` (${ptMeta.join(", ")})` : ""}`;
},
},
range: {name: "Range", transform: (it) => Parser.spRangeToFull(it)},
_components: {name: "Components", transform: (sp) => Parser.spComponentsToFull(sp.components, sp.level, {isPlainText: true})},
_classes: {
name: "Classes",
transform: (sp) => {
const [current] = Parser.spClassesToCurrentAndLegacy(Renderer.spell.getCombinedClasses(sp, "fromClassList"));
return Parser.spMainClassesToFull(current);
},
},
_classesVariant: {
name: "Optional/Variant Classes",
transform: (sp) => {
const [current] = Parser.spVariantClassesToCurrentAndLegacy(Renderer.spell.getCombinedClasses(sp, "fromClassListVariant"));
return Parser.spMainClassesToFull(current);
},
},
entries: {name: "Text", transform: (it) => Renderer.get().render({type: "entries", entries: it}, 1), flex: 3},
entriesHigherLevel: {name: "At Higher Levels", transform: (it) => Renderer.get().render({type: "entries", entries: (it || [])}, 1), flex: 2},
},
},
isMarkdownPopout: true,
propLoader: "spell",
listSyntax: new ListSyntaxSpells({fnGetDataList: () => this._dataList, pFnGetFluff}),
compSettings: new SpellsPageSettingsManager(),
});
this._lastFilterValues = null;
this._subclassLookup = {};
this._bookViewLastOrder = null;
}
get _bindOtherButtonsOptions () {
return {
upload: {
pFnPreLoad: (...args) => this._pPreloadSublistSources(...args),
},
sendToBrew: {
mode: "spellBuilder",
fnGetMeta: () => ({
page: UrlUtil.getCurrentPage(),
source: Hist.getHashSource(),
hash: Hist.getHashParts()[0],
}),
},
other: [
this._bindOtherButtonsOptions_openAsSinglePage({slugPage: "spells", fnGetHash: () => Hist.getHashParts()[0]}),
].filter(Boolean),
};
}
getListItem (spell, spI) {
const hash = UrlUtil.autoEncodeHash(spell);
if (this._seenHashes.has(hash)) return null;
this._seenHashes.add(hash);
const isExcluded = ExcludeUtil.isExcluded(hash, "spell", spell.source);
this._pageFilter.mutateAndAddToFilters(spell, isExcluded);
const source = Parser.sourceJsonToAbv(spell.source);
const time = PageFilterSpells.getTblTimeStr(spell.time[0]);
const school = Parser.spSchoolAndSubschoolsAbvsShort(spell.school, spell.subschools);
const concentration = spell._isConc ? "×" : "";
const range = Parser.spRangeToFull(spell.range);
const eleLi = e_({
tag: "div",
clazz: `lst__row ve-flex-col ${isExcluded ? "lst__row--blocklisted" : ""}`,
click: (evt) => this._list.doSelect(listItem, evt),
contextmenu: (evt) => this._openContextMenu(evt, this._list, listItem),
children: [
e_({
tag: "a",
href: `#${hash}`,
clazz: "lst--border lst__row-inner",
children: [
e_({tag: "span", clazz: `bold ve-col-2-9 pl-0`, text: spell.name}),
e_({tag: "span", clazz: `ve-col-1-5 ve-text-center`, text: PageFilterSpells.getTblLevelStr(spell)}),
e_({tag: "span", clazz: `ve-col-1-7 ve-text-center`, text: time}),
e_({
tag: "span",
clazz: `ve-col-1-2 sp__school-${spell.school} ve-text-center`,
title: Parser.spSchoolAndSubschoolsAbvsToFull(spell.school, spell.subschools),
style: Parser.spSchoolAbvToStylePart(spell.school),
text: school,
}),
e_({tag: "span", clazz: `ve-col-0-6 ve-text-center`, title: "Concentration", text: concentration}),
e_({tag: "span", clazz: `ve-col-2-4 text-right`, text: range}),
e_({
tag: "span",
clazz: `ve-col-1-7 ve-text-center ${Parser.sourceJsonToColor(spell.source)} pr-0`,
style: Parser.sourceJsonToStylePart(spell.source),
title: `${Parser.sourceJsonToFull(spell.source)}${Renderer.utils.getSourceSubText(spell)}`,
text: source,
}),
],
}),
],
});
const listItem = new ListItem(
spI,
eleLi,
spell.name,
{
hash,
source,
level: spell.level,
time,
school: Parser.spSchoolAbvToFull(spell.school),
concentration,
normalisedTime: spell._normalisedTime,
normalisedRange: spell._normalisedRange,
},
{
isExcluded,
},
);
return listItem;
}
_tabTitleStats = "Spell";
_renderStats_doBuildStatsTab ({ent}) {
this._$pgContent.empty().append(RenderSpells.$getRenderedSpell(ent, this._subclassLookup, {settings: this._compSettings.getValues()}));
}
async _pOnLoad_pPreDataLoad () {
const subclassLookup = await DataUtil.class.pGetSubclassLookup();
Object.assign(this._subclassLookup, subclassLookup);
}
async _pOnLoad_pPreDataAdd () {
Renderer.spell.populatePrereleaseLookup(await PrereleaseUtil.pGetBrewProcessed());
Renderer.spell.populateBrewLookup(await BrewUtil2.pGetBrewProcessed());
}
async _pPreloadSublistSources (json) {
const loaded = Object.keys(this._loadedSources)
.filter(it => this._loadedSources[it].loaded);
const lowerSources = json.sources.map(it => it.toLowerCase());
const toLoad = Object.keys(this._loadedSources)
.filter(it => !loaded.includes(it))
.filter(it => lowerSources.includes(it.toLowerCase()));
const loadTotal = toLoad.length;
if (loadTotal) {
await Promise.all(toLoad.map(src => this._pLoadSource(src, "yes")));
}
}
async pHandleUnknownHash (link, sub) {
const src = Object.keys(this._loadedSources)
.find(src => src.toLowerCase() === (UrlUtil.decodeHash(link)[1] || "").toLowerCase());
if (src) {
await this._pLoadSource(src, "yes");
Hist.hashChange();
}
}
}
const spellsPage = new SpellsPage();
spellsPage.sublistManager = new SpellsSublistManager();
window.addEventListener("load", () => spellsPage.pOnLoad());