From 6fecd889c0a58821f469470093461fea903ba365 Mon Sep 17 00:00:00 2001 From: valentine195 <38669521+valentine195@users.noreply.github.com> Date: Tue, 6 Jul 2021 13:52:13 -0400 Subject: [PATCH] 1.3.1 - Fixed some issues with CritterDB imports - Added support for CritterDB bestiary importing - Added "view creature" button to settings --- manifest.json | 2 +- package.json | 2 +- src/importers/5eToolsImport.ts | 4 - src/importers/CritterDBImport.ts | 254 ++++++++++++++++++------------- src/renderer/statblock.ts | 13 +- src/settings/settings.ts | 6 +- src/util/suggester.ts | 90 ++++++++++- versions.json | 2 +- 8 files changed, 249 insertions(+), 124 deletions(-) diff --git a/manifest.json b/manifest.json index 71d8b524..2b9412c2 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-5e-statblocks", "name": "5e Statblocks", - "version": "1.3.0", + "version": "1.3.1", "description": "Create 5e styled statblocks in Obsidian.md", "minAppVersion": "0.12.0", "author": "Jeremy Valentine", diff --git a/package.json b/package.json index 113d9fc3..01fce26b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-5e-statblocks", - "version": "1.3.0", + "version": "1.3.1", "description": "Create 5e styled statblocks in Obsidian.md", "main": "main.js", "scripts": { diff --git a/src/importers/5eToolsImport.ts b/src/importers/5eToolsImport.ts index 625169c6..5d345791 100644 --- a/src/importers/5eToolsImport.ts +++ b/src/importers/5eToolsImport.ts @@ -62,10 +62,6 @@ async function buildMonsterFromFile(file: File): Promise { value: string ] ) => { - console.log( - "🚀 ~ file: 5eToolsImport.ts ~ line 62 ~ thr.value", - thr - ); const [, v] = thr[1].match(/.*(\d+)/); return { [abilityMap[thr[0]]]: v }; } diff --git a/src/importers/CritterDBImport.ts b/src/importers/CritterDBImport.ts index 54b13df7..9eeb8ef3 100644 --- a/src/importers/CritterDBImport.ts +++ b/src/importers/CritterDBImport.ts @@ -3,127 +3,171 @@ import { Monster } from "@types"; export const ImportFromCritterDB = async ( ...files: File[] ): Promise> => { - const importedMonsters: Map = new Map(); + let importedMonsters: Map = new Map(); for (let file of files) { try { - const monster = await buildMonsterFromFile(file); - importedMonsters.set(monster.name, monster); + const monsters = await buildMonsterFromFile(file); + importedMonsters = new Map([...importedMonsters, ...monsters]); } catch (e) {} } return importedMonsters; }; -async function buildMonsterFromFile(file: File): Promise { +async function buildMonsterFromFile(file: File): Promise> { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = async (event: any) => { + const importedMonsters: Map = new Map(); try { - const monster = JSON.parse(event.target.result); - - const importedMonster: Monster = { - name: monster.name, - source: "CritterDB", - type: monster.stats.race, - subtype: "", - size: monster.stats.size, - alignment: monster.stats.alignment, - hp: monster.stats.hitPoints, - hit_dice: monster.stats.hitPointsStr, - ac: monster.stats.armorClass, - speed: monster.stats.speed, - stats: [ - monster.stats.abilityScores.strength, - monster.stats.abilityScores.dexterity, - monster.stats.abilityScores.constitution, - monster.stats.abilityScores.intelligence, - monster.stats.abilityScores.wisdom, - monster.stats.abilityScores.charisma - ], - damage_immunities: - monster.stats.damageImmunities - ?.join("; ") - .toLowerCase() - .trim() ?? "", - damage_resistances: - monster.stats.damageResistances - ?.join(", ") - .toLowerCase() - .trim() ?? "", - damage_vulnerabilities: - monster.stats.damageVulnerabilities - ?.join(", ") - .toLowerCase() - .trim() ?? "", - condition_immunities: - monster.stats.conditionImmunities - ?.join(", ") - .toLowerCase() - .trim() ?? "", - saves: monster.stats.savingThrows?.map( - (thr: { - ability: keyof Monster["saves"]; - value: number; - }) => { - return { [thr.ability]: thr.value }; - } - ), - skillsaves: - monster.stats.skills?.map( - ({ - name, - value - }: { - name: string; + const parsed = JSON.parse(event.target.result); + let monsters = []; + if (parsed.creatures) { + monsters = parsed.creatures; + } else { + monsters = [parsed]; + } + for (let monster of monsters) { + const importedMonster: Monster = { + name: monster.name, + source: "CritterDB", + type: monster.stats.race, + subtype: "", + size: monster.stats.size, + alignment: monster.stats.alignment, + hp: monster.stats.hitPoints, + hit_dice: `${Math.floor( + monster.stats.extraHealthFromConstitution / + monster.stats.abilityScoreModifiers.constitution + )}d${monster.stats.hitDieSize} + ${ + monster.stats.extraHealthFromConstitution + }`, + ac: monster.stats.armorClass, + speed: monster.stats.speed, + stats: [ + monster.stats.abilityScores.strength, + monster.stats.abilityScores.dexterity, + monster.stats.abilityScores.constitution, + monster.stats.abilityScores.intelligence, + monster.stats.abilityScores.wisdom, + monster.stats.abilityScores.charisma + ], + damage_immunities: + monster.stats.damageImmunities + ?.join("; ") + .toLowerCase() + .trim() ?? "", + damage_resistances: + monster.stats.damageResistances + ?.join(", ") + .toLowerCase() + .trim() ?? "", + damage_vulnerabilities: + monster.stats.damageVulnerabilities + ?.join(", ") + .toLowerCase() + .trim() ?? "", + condition_immunities: + monster.stats.conditionImmunities + ?.join(", ") + .toLowerCase() + .trim() ?? "", + saves: monster.stats.savingThrows?.map( + (thr: { + ability: keyof Monster["saves"]; value: number; }) => { - return { - [name]: value - }; - } - ) ?? [], - senses: monster.stats.senses?.join(", ").trim() ?? "", - languages: monster.stats.languages?.join(", ").trim() ?? "", - cr: monster.stats.challengeRating ?? "", - traits: - monster.stats.additionalAbilities?.map( - (trait: { name: string; description: string }) => { - return { - name: trait.name, - desc: trait.description - }; - } - ) ?? [], - actions: - monster.stats.actions?.map( - (trait: { name: string; description: string }) => { - return { - name: trait.name, - desc: trait.description - }; - } - ) ?? [], - reactions: - monster.stats.reactions?.map( - (trait: { name: string; description: string }) => { - return { - name: trait.name, - desc: trait.description - }; - } - ) ?? [], - legendary_actions: - monster.stats.legendaryActions?.map( - (trait: { name: string; description: string }) => { - return { - name: trait.name, - desc: trait.description - }; + return { [thr.ability]: thr.value }; } - ) ?? [] - }; + ), + skillsaves: + monster.stats.skills + ?.map( + ({ + name, + value, + modifier + }: { + name: string; + value: number; + modifier: number; + }) => { + if (!value && !modifier) return; + return { + [name]: value ?? modifier + }; + } + ) + .filter((x: any) => x) ?? [], + senses: monster.stats.senses?.join(", ").trim() ?? "", + languages: + monster.stats.languages?.join(", ").trim() ?? "", + cr: monster.stats.challengeRating ?? "", + traits: + monster.stats.additionalAbilities?.map( + (trait: { + name: string; + description: string; + }) => { + return { + name: trait.name, + desc: trait.description.replace( + /<[^>]*>/g, + "" + ) + }; + } + ) ?? [], + actions: + monster.stats.actions?.map( + (trait: { + name: string; + description: string; + }) => { + return { + name: trait.name, + desc: trait.description.replace( + /<[^>]*>/g, + "" + ) + }; + } + ) ?? [], + reactions: + monster.stats.reactions?.map( + (trait: { + name: string; + description: string; + }) => { + return { + name: trait.name, + desc: trait.description.replace( + /<[^>]*>/g, + "" + ) + }; + } + ) ?? [], + legendary_actions: + monster.stats.legendaryActions?.map( + (trait: { + name: string; + description: string; + }) => { + return { + name: trait.name, + desc: trait.description.replace( + /<[^>]*>/g, + "" + ) + }; + } + ) ?? [] + }; + importedMonsters.set(importedMonster.name, importedMonster); + } - resolve(importedMonster); + resolve(importedMonsters); } catch (e) { reject(); } diff --git a/src/renderer/statblock.ts b/src/renderer/statblock.ts index 707e1523..ad754c16 100644 --- a/src/renderer/statblock.ts +++ b/src/renderer/statblock.ts @@ -21,7 +21,8 @@ export default class StatBlockRenderer extends MarkdownRenderChild { container: HTMLElement, monster: Monster, private plugin: StatblockMonsterPlugin, - private canSave: boolean + private canSave: boolean, + private canExport: boolean = true ) { super(container); this.monster = monster; @@ -326,10 +327,12 @@ export default class StatBlockRenderer extends MarkdownRenderChild { saveEl.onclick = () => this.plugin.saveMonster(this.monster); setIcon(saveEl, SAVE_SYMBOL); } - const iconEl = iconsEl.createDiv("clickable-icon"); - iconEl.onclick = () => - this.plugin.exportAsPng(name, this.containerEl); - setIcon(iconEl, EXPORT_SYMBOL); + if (this.canExport) { + const iconEl = iconsEl.createDiv("clickable-icon"); + iconEl.onclick = () => + this.plugin.exportAsPng(name, this.containerEl); + setIcon(iconEl, EXPORT_SYMBOL); + } needRule = true; } diff --git a/src/settings/settings.ts b/src/settings/settings.ts index 9b312610..3a0d6f23 100644 --- a/src/settings/settings.ts +++ b/src/settings/settings.ts @@ -1,6 +1,7 @@ -import { StatblockMonsterPlugin } from "@types"; +import { Monster, StatblockMonsterPlugin } from "@types"; import { App, + Modal, Notice, PluginSettingTab, Setting, @@ -307,7 +308,7 @@ export default class StatblockSettingTab extends PluginSettingTab { }` ); } - this.display(); + suggester._onInputChanged(); }; suggester.onInputChanged = () => searchMonsters.setDesc( @@ -320,3 +321,4 @@ export default class StatblockSettingTab extends PluginSettingTab { } } } + diff --git a/src/util/suggester.ts b/src/util/suggester.ts index fb6c7943..cb010a7a 100644 --- a/src/util/suggester.ts +++ b/src/util/suggester.ts @@ -3,12 +3,15 @@ import { App, FuzzyMatch, FuzzySuggestModal, + Modal, Notice, Scope, Setting, SuggestModal, TextComponent } from "obsidian"; +import StatBlockRenderer from "src/renderer/statblock"; +import { getColumns } from "./util"; /* import { createPopper, Instance as PopperInstance } from "@popperjs/core"; */ class Suggester { @@ -261,11 +264,28 @@ export class MonsterSuggester extends SuggestionModal { } content.setDesc(item.source); - content.addExtraButton((b) => { - b.setIcon("trash") - .setTooltip("Delete") - .onClick(() => this.onRemoveItem(item)); - }); + content + .addExtraButton((b) => { + b.setIcon("magnifying-glass") + .setTooltip("View") + .onClick(() => { + const modal = new ViewMonsterModal(this.plugin, item); + modal.open(); + }); + }) + /* .addExtraButton((b) => { + b.setIcon("pencil") + .setTooltip("Edit") + .onClick(() => { + const modal = new EditMonsterModal(this.plugin, item); + modal.open(); + }); + }) */ + .addExtraButton((b) => { + b.setIcon("trash") + .setTooltip("Delete") + .onClick(() => this.onRemoveItem(item)); + }); } getItems() { return this.plugin.sorted.filter(({ source }) => @@ -275,3 +295,63 @@ export class MonsterSuggester extends SuggestionModal { onClose(item?: Monster) {} onRemoveItem(item: Monster) {} } + +class ViewMonsterModal extends Modal { + constructor( + private plugin: StatblockMonsterPlugin, + private monster: Monster + ) { + super(plugin.app); + } + async display() { + const renderer = new StatBlockRenderer( + this.contentEl, + this.monster, + this.plugin, + false, + false + ); + renderer.loaded = true; + let columns = + this.contentEl.getBoundingClientRect().height > 500 + ? Math.ceil(this.contentEl.getBoundingClientRect().height / 750) + : 1; + if (columns >= 1) renderer.setWidth(columns * 400, true); + if (columns === 0) renderer.setMaxWidth(400); + renderer.statblockEl.toggleVisibility(true); + } + onOpen() { + this.display(); + } +} +class EditMonsterModal extends Modal { + private _tempMonster: Monster; + constructor( + private plugin: StatblockMonsterPlugin, + private monster: Monster + ) { + super(plugin.app); + this._tempMonster = { ...monster }; + } + async display() { + const { contentEl } = this; + + const topLevel = createDiv(); + topLevel.createEl("h2", { text: "Edit Monster" }); + + /** + * Name + * Source + * Type + * Size + * Alignment + * HP, AC + */ + const basicStats = createDiv(); + + contentEl.appendChild(topLevel); + } + onOpen() { + this.display(); + } +} diff --git a/versions.json b/versions.json index 476001ec..790682a0 100644 --- a/versions.json +++ b/versions.json @@ -3,5 +3,5 @@ "1.0.0": "0.12.0", "1.1.0": "0.12.0", "1.2.0": "0.12.0", - "1.3.0": "0.12.0" + "1.3.1": "0.12.0" } \ No newline at end of file