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
Former fey of the [[Unseelie Court|Winter Court]], servant of [[Dread Ethel]]. Deceased following the party thwarting their actions in Chevid using an enslaved [[Elder Brain]].
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}*);
}
}
- **Mannerisms:**
Recent Activity
This section displays every time this entity is mentioned in session summaries.
The party found that within the Elder Brain lair it was shackled by spectral chains. There was an elven appearing Fey present who introduced herself as Caraway. Caraway implied that she was formerly of the Winter Court but now served Dread Ethel. Caraway commanded the Elder Brain on behalf of Dread Ethel for whatever plot she was carrying out in Chevid and the Storm Coast region. Caraway was slain and upon this happening the chains disappeared, allowing the Elder Brain to immediately escape.
Caraway
Medium Fey, Chaotic Neutral
Armor Class :; 17 (natural armor)
Hit Points 165 (30d8 + 30)
Speed 40 ft., Climb 15 ft.
Initiative +4 (14)
STR
DEX
CON
INT
WIS
CHA
11 (+0)
19 (+4)
12 (+1)
11 (+0)
15 (+2)
20 (+5)
Saving Throws Wis +6
Skills Intimidation +9, Stealth +8
Damage Resistances Bludgeoning, Piercing, and Slashing from attacks not made with cold iron weapons
Immunities Charmed, Frightened
Senses Blindsight 30 ft., Passive Perception 12
Languages Common, Elvish, Sylvan
Challenge 12 (XP 8,400; PB +4)
Proficiency Bonus +4
Distortion Gaze. Those who meet the gaze of Caraway experience the world seeming to twist at unnatural angles beneath their feet. When a creature that can see Caraway’s eyes starts its turn within 30 feet of Caraway, Caraway can force the creature to make a DC 17 Wisdom saving throw if Caraway isn’t incapacitated and can see the creature. On a failed save, the creature is disoriented until the start of its next turn. While disoriented, a creature can’t take the Dash or Disengage action, and when it moves for the first time on its turn, it must succeed on a DC 17 Dexterity saving throw or fall prone.
Unless surprised, a creature can avert its eyes to avoid the saving throw at the start of its turn. If the creature does so, it can’t see Caraway until the start of its next turn, when it can avert its eyes again. If the creature looks at Caraway in the meantime, it must immediately make the save.
Feysight. Caraway has advantage on saving throws against being blinded.
Magic Resistance. Caraway has advantage on saving throws against spells and other magical effects.
Actions
Multiattack. Caraway makes three Claw attacks. It can replace one attack with a use of Heart-Stopping Stare.
Claw.Melee Weapon Attack: +8 to hit, reach 5 ft., one target. Hit: 13 (2d8 + 4) slashing damage plus 9 (2d8) psychic damage. If the target is disoriented by Distortion Gaze, Caraway regains hp equal to the amount of psychic damage dealt.
Heart-Stopping Stare. Caraway terrifies a creature within 30 feet of it with a look. The target must succeed on a DC 17 Wisdom saving throw or take 13 (3d8) psychic damage and be stunned until the end of its next turn. Caraway regains hp equal to the psychic damage dealt.
Spellcasting. Caraway casts one of the following spells, requiring no verbal or material components and using Charisma as the spellcasting ability (spell save DC 17):
At will: detect thoughts, fear
2/day each: charm person, command