```` <%* const modalForm = app.plugins.plugins.modalforms.api; const result = await modalForm.openForm("new-creature"); if (!result) { new Notice("Form cancelled."); return; } function getFieldAsString(fieldName, defaultValue = "") { let value = result[fieldName]; if (!value) return defaultValue; return String(value).trim(); } function getFieldAsNumber(fieldName, defaultValue = 0) { let value = result[fieldName]; return value ? Math.floor(Number(value)) : defaultValue; } function getFieldAsBoolean(fieldName) { return result[fieldName] ? true : false; } function toYamlList(str) { if (!str) return "- None"; let items = str.split(",").map(s => s.trim()).filter(s => s.length > 0); return items.length === 0 ? "- None" : items.map(item => `- ${item}`).join("\n"); } // Function to calculate ability modifier function getAbilityModifier(score) { let mod = Math.floor((score - 10) / 2); return mod >= 0 ? `+${mod}` : `(${mod})`; } // Calculate Proficiency Bonus based on CR function getProficiencyBonus(cr) { let crToProficiency = { "0": 2, "1/8": 2, "1/4": 2, "1/2": 2, "1": 2, "2": 2, "3": 2, "4": 2, "5": 3, "6": 3, "7": 3, "8": 3, "9": 4, "10": 4, "11": 4, "12": 4, "13": 5, "14": 5, "15": 5, "16": 5, "17": 6, "18": 6, "19": 6, "20": 6, "21": 7, "22": 7, "23": 7, "24": 7, "25": 8, "26": 8, "27": 8, "28": 8, "29": 9, "30": 9 }; return crToProficiency[cr] || 2; } let name = getFieldAsString("name"); let classification = getFieldAsString("location"); // Folder classification let size = getFieldAsString("size", "Medium"); let type = getFieldAsString("type", "humanoid"); let alignment = getFieldAsString("alignment", "unaligned"); let ac = String(getFieldAsNumber("ac") || "10"); let naturalArmor = getFieldAsBoolean("armortype") ? "Natural Armor" : "None"; let hp = String(getFieldAsNumber("hp") || "1"); let hitDie = getFieldAsString("hitdie") || "1d6"; let walkSpeed = String(getFieldAsNumber("w-speed") || "30"); let swimSpeed = String(getFieldAsNumber("s-speed") || "0"); let flySpeed = String(getFieldAsNumber("f-speed") || "0"); let str = String(getFieldAsNumber("cre-str") || "10"); let dex = String(getFieldAsNumber("cre-dex") || "10"); let con = String(getFieldAsNumber("cre-con") || "10"); let int = String(getFieldAsNumber("cre-int") || "10"); let wis = String(getFieldAsNumber("cre-wis") || "10"); let cha = String(getFieldAsNumber("cre-cha") || "10"); let challengeRating = getFieldAsString("cr") || "0"; let proficiencyBonus = String(getProficiencyBonus(challengeRating)); let savingThrows = toYamlList(getFieldAsString("savethrow")); let skills = toYamlList(getFieldAsString("skills")); let vulnerabilities = toYamlList(getFieldAsString("dmg_vul")); let resistances = toYamlList(getFieldAsString("dmg_res")); let immunities = toYamlList(getFieldAsString("dmg_imm")); let conditionImmunities = toYamlList(getFieldAsString("cond_imm")); let senses = toYamlList(getFieldAsString("senses")); let traits = getFieldAsString("traits"); let actions = getFieldAsString("actions"); let reactions = getFieldAsString("reactions"); let legendaryActions = getFieldAsString("legendary"); let lairActions = getFieldAsString("lair-action"); let regionalEffects = getFieldAsString("regional"); let description = getFieldAsString("description"); let narrative = getFieldAsString("narrative"); // Build frontmatter let frontmatter = `--- name: "${name}" classification: "${classification}" size: "${size}" type: "${type}" alignment: "${alignment}" armor_class: ${ac} (${naturalArmor}) hit_points: ${hp} hit_die: "${hitDie}" speed: Walk: ${walkSpeed} ft Swim: ${swimSpeed} ft Fly: ${flySpeed} ft strength: ${str} dexterity: ${dex} constitution: ${con} intelligence: ${int} wisdom: ${wis} charisma: ${cha} proficiency_bonus: +${proficiencyBonus} saving_throws: ${savingThrows} skills: ${skills} damage_vulnerabilities: ${vulnerabilities} damage_resistances: ${resistances} damage_immunities: ${immunities} condition_immunities: ${conditionImmunities} senses: ${senses} challenge_rating: "${challengeRating}" ---`; // Body with `= this.field_name` references and inline modifier calculations let statBlock = `# \`= this.name\` _\`= this.size\` \`= this.type\`, \`= this.alignment\`_ >[!info] *Description* >*${narrative || "None"}* **Armor Class:** \`= this.armor_class\` **Hit Points:** \`= this.hit_points\` (\`= this.hit_die\`) **Speed:** Walk \`= this.speed.Walk\`, Swim \`= this.speed.Swim\`, Fly \`= this.speed.Fly\` ### **Abilities** | STR | DEX | CON | INT | WIS | CHA | |:---:|:---:|:---:|:---:|:---:|:---:| | \`= this.strength\` (\`= floor((this.strength - 10) / 2)\`) | \`= this.dexterity\` (\`= floor((this.dexterity - 10) / 2)\`) | \`= this.constitution\` (\`= floor((this.constitution - 10) / 2)\`) | \`= this.intelligence\` (\`= floor((this.intelligence - 10) / 2)\`) | \`= this.wisdom\` (\`= floor((this.wisdom - 10) / 2)\`) | \`= this.charisma\` (\`= floor((this.charisma - 10) / 2)\`) | **Proficiency Bonus:** +\`= this.proficiency_bonus\` **Saving Throws:** \`= this.saving_throws\` **Skills:** \`= this.skills\` **Damage Vulnerabilities:** \`= this.damage_vulnerabilities\` **Damage Resistances:** \`= this.damage_resistances\` **Damage Immunities:** \`= this.damage_immunities\` **Condition Immunities:** \`= this.condition_immunities\` **Senses:** \`= this.senses\` **Challenge Rating:** \`= this.challenge_rating\` ##### **Traits** ${traits || "None"} ##### **Actions** ${actions || "None"} ##### **Reactions** ${reactions || "None"} ##### **Legendary Actions** ${legendaryActions || "None"} ##### **Lair Actions** ${lairActions || "None"} ##### **Regional Effects** ${regionalEffects || "None"} ## Description ${description || "None"} `; // Function to convert multi-line text into YAML lists function formatYamlList(text) { if (!text || text.trim() === "") return " - none"; // Handle empty cases return text .split("\n") .map(line => { let parts = line.split(":"); if (parts.length < 2) return ""; // Ensure valid format let name = parts[0].trim(); let desc = parts.slice(1).join(":").trim(); return ` - name: "${name}"\n desc: "${desc}"`; }) .filter(line => line !== "") // Remove empty lines .join("\n"); } // Ensure folder path is valid and does not contain problematic characters let sanitizedClassification = classification.replace(/[^a-zA-Z0-9-_ ]/g, "").trim(); let fileName = name.replace(/[^a-zA-Z0-9-_ ]/g, "").trim(); let filePath = `Compendium/Bestiary/${sanitizedClassification}/${fileName}`; await tp.file.create_new(frontmatter.trim() + "\n" + statBlock.trim(), filePath); await new Promise(resolve => setTimeout(resolve, 2000)); // Open the created file let newFile = tp.file.find_tfile(filePath) || app.vault.getAbstractFileByPath(filePath); if (newFile) { app.workspace.openLinkText(newFile.basename, newFile.path, true); } else { new Notice("File not found: " + filePath); } %>