let current = dv.current();let title = current.name || current.file.name;// --- CONFIGURATION ---// Set to your silhouette image path or filename (e.g., "npc-images/silhouette.webp" or "silhouette.webp").// If the file doesn't exist or is set to "", it will gracefully display the text message below.let fallbackImage = "npc-images/silhouette.webp"; // ---------------------// 1. Gather all potential names (Title, quoted nicknames, and Obsidian Aliases)let rawNames = [title];// Extract quoted nickname if present in title (e.g., Wulfric "The Bear" -> The Bear)let matchNick = title.match(/"([^"]+)"/);if (matchNick && matchNick) { rawNames.push(matchNick);}// Safely pull aliases from Obsidian Frontmatter/Propertieslet rawAliases = current.file?.aliases || current.aliases || [];if (typeof rawAliases === "string") { rawNames.push(rawAliases);} else if (rawAliases && typeof rawAliases[Symbol.iterator] === "function") { for (let a of rawAliases) { if (a && typeof a === "string") rawNames.push(a); }}// 2. Build sanitized search terms (ensures terms are >= 2 chars to avoid matching "_" or empty strings)let searchTerms = new Set();for (let name of rawNames) { if (!name || typeof name !== "string") continue; let lower = name.toLowerCase().trim().replace(/["']/g, ""); let slug = lower.replace(/[\s-]+/g, "_").replace(/^_+|_+\$/g, ""); if (lower.length >= 2) searchTerms.add(lower); if (slug.length >= 2) searchTerms.add(slug);}// 3. Search npc-images for matching character fileslet matches = app.vault.getFiles().filter(f => { let pathLower = f.path.toLowerCase(); if (!pathLower.includes("npc-images")) return false; // Exclude the fallback image from character search if (fallbackImage && pathLower.includes(fallbackImage.toLowerCase())) return false; let imgName = f.name.toLowerCase(); return Array.from(searchTerms).some(term => term && imgName.includes(term));});// 4. Prioritize files with "avatar" in the name, otherwise take first matchlet match = matches.find(f => f.name.toLowerCase().includes("avatar")) || (matches.length > 0 ? matches : null);// 5. Render artwork, fallback image, or clean text messageif (match) { dv.paragraph(`![[${match.path}|cover]]`);} else { // Flexible fallback image lookup (works with filename, full path, or partial match) let fallbackFile = null; if (fallbackImage && fallbackImage.trim() !== "") { let cleanFB = fallbackImage.toLowerCase().trim(); fallbackFile = app.vault.getFiles().find(f => f.path.toLowerCase() === cleanFB || f.path.toLowerCase().includes(cleanFB) || f.name.toLowerCase().includes(cleanFB) ); } if (fallbackFile) { dv.paragraph(`![[${fallbackFile.path}|cover]]`); } else { dv.paragraph(`*No artwork generated yet for ${title}*`); }}
// 5. Render artwork or fallback
if (match) {
dv.paragraph(![[${match.path}|cover]]);
} else {
let fallbackFile = fallbackImage ? app.vault.getAbstractFileByPath(fallbackImage) : null;
if (fallbackFile) {
dv.paragraph(![[${fallbackFile.path}|cover]]);
} else {
dv.paragraph(*No artwork generated yet for ${title}*);
}
}
### Character Bio
An agent of [[Aldovian Empire|Aldovia]], commanded by the Ministry of Faith.
Appearance & Personality
Visuals & Mannerisms
let current = dv.current();let title = current.name || current.file.name;// --- CONFIGURATION ---// Set to your silhouette image path or filename (e.g., "npc-images/silhouette.webp" or "silhouette.webp").// If the file doesn't exist or is set to "", it will gracefully display the text message below.let fallbackImage = "npc-images/silhouette.webp"; // ---------------------// 1. Gather all potential names (Title, quoted nicknames, and Obsidian Aliases)let rawNames = [title];// Extract quoted nickname if present in title (e.g., Wulfric "The Bear" -> The Bear)let matchNick = title.match(/"([^"]+)"/);if (matchNick && matchNick) { rawNames.push(matchNick);}// Safely pull aliases from Obsidian Frontmatter/Propertieslet rawAliases = current.file?.aliases || current.aliases || [];if (typeof rawAliases === "string") { rawNames.push(rawAliases);} else if (rawAliases && typeof rawAliases[Symbol.iterator] === "function") { for (let a of rawAliases) { if (a && typeof a === "string") rawNames.push(a); }}// 2. Build sanitized search terms (ensures terms are >= 2 chars to avoid matching "_" or empty strings)let searchTerms = new Set();for (let name of rawNames) { if (!name || typeof name !== "string") continue; let lower = name.toLowerCase().trim().replace(/["']/g, ""); let slug = lower.replace(/[\s-]+/g, "_").replace(/^_+|_+\$/g, ""); if (lower.length >= 2) searchTerms.add(lower); if (slug.length >= 2) searchTerms.add(slug);}// 3. Search npc-images for matching character fileslet matches = app.vault.getFiles().filter(f => { let pathLower = f.path.toLowerCase(); if (!pathLower.includes("npc-images")) return false; // Exclude the fallback image from character search if (fallbackImage && pathLower.includes(fallbackImage.toLowerCase())) return false; let imgName = f.name.toLowerCase(); return Array.from(searchTerms).some(term => term && imgName.includes(term));});// 4. Prioritize files with "avatar" in the name, otherwise take first matchlet match = matches.find(f => f.name.toLowerCase().includes("avatar")) || (matches.length > 0 ? matches : null);// 5. Render artwork, fallback image, or clean text messageif (match) { dv.paragraph(`![[${match.path}|cover]]`);} else { // Flexible fallback image lookup (works with filename, full path, or partial match) let fallbackFile = null; if (fallbackImage && fallbackImage.trim() !== "") { let cleanFB = fallbackImage.toLowerCase().trim(); fallbackFile = app.vault.getFiles().find(f => f.path.toLowerCase() === cleanFB || f.path.toLowerCase().includes(cleanFB) || f.name.toLowerCase().includes(cleanFB) ); } if (fallbackFile) { dv.paragraph(`![[${fallbackFile.path}|cover]]`); } else { dv.paragraph(`*No artwork generated yet for ${title}*`); }}
// 5. Render artwork or fallback
if (match) {
dv.paragraph(![[${match.path}|cover]]);
} else {
let fallbackFile = fallbackImage ? app.vault.getAbstractFileByPath(fallbackImage) : null;
if (fallbackFile) {
dv.paragraph(![[${fallbackFile.path}|cover]]);
} else {
dv.paragraph(*No artwork generated yet for ${title}*);
}
}
Languages Common, Dwarvish, Elvish, telepathy 120ft.
Challenge 7 (XP 2,900; PB +3)
Proficiency Bonus +3
Actor. Auster has advantage on Deception and Performance checks when trying to pass himself off as a different person, and he can mimic the speech of another person or the sounds made by other creatures that he has heard (for at least 1 minute).
Spellcasting. Auster casts one of the following spells, requiring no Material, Verbal, or Somatic components and using Charisma as the spellcasting ability (spell save DC 19):
Multiattack. Auster makes two Mind Sliver attacks or a Mind Sliver attack and any other spell.
Mind Sliver. The target must succeed on a DC 19 Intelligence saving throw or take 10 (3d6) Psychic damage and subtract 1d4 from the next saving throw it makes before the end of the target’s next turn.
Reactions
Spellcasting. Auster casts one of the following spells, requiring no Material, Verbal, or Somatic components and using Charisma as the spellcasting ability (spell save DC 19):