vault commit: 2026-04-08 20:23, 4 files changed

This commit is contained in:
Jason McPherson
2026-04-08 20:23:53 -05:00
parent c5ad89f8d1
commit e869a5cf8c
4 changed files with 364 additions and 1 deletions
@@ -1,5 +1,5 @@
---
Level_of_Influence: 6
Level_of_Influence: 7
Faction:
SMH_Acronym: Synchronized Mushroom Hiccup
Tags:
@@ -0,0 +1,174 @@
---
Level_of_Influence: 0
Faction:
SMH_Acronym:
Tags:
- npc
---
# Saara Kallaste
*Journalist/Reporter*
## 🎨 Artwork
![[Saara Kallaste.png]]
*Optional caption / alt text*
---
## 🧑 NPC Overview
- **Full Name:** Saara Kallaste
- **Race / Ancestry:** Wood Elf
- **Gender / Pronouns:** She/Her
- **Occupation / Role:** Journalist/Reporter
- **Faction / Allegiance:**
- **SMH (what THEY call it):** `= this.SMH_Acronym`
- **Notable Traits:** (quirks, mannerisms, appearance)
- **First Impression:** (how the party perceives them)
---
## 🤝 Relationship with Party
- **Level of Influence:** `= this.Level_of_Influence` (10 to +10)
- **Notes on Influence:** (what actions improve/worsen standing)
- ***Discovery Skills***
- ***Influence Skills***
- ***Weaknesses***
- ***Resistances***
- ***Biases***
---
## 🧠 Intel (auto)
```dataviewjs
const THIS = dv.current();
const thisName = String(THIS.file.name);
const thisLC = thisName.toLowerCase();
// Scan faction & NPC notes that have Intel arrays
const pages = dv.pages("#npc or #faction").where(p => p.Intel);
// Does an Intel entry reference THIS NPC?
function mentionsThisNPC(entry) {
const sum = String(entry.summary ?? "");
// 1) Explicit wikilink in summary
if (sum.includes(`[[${thisName}]]`) || sum.includes(`[[${thisName}|`)) return true;
// 2) Source field (string/link/list)
const src = entry.source;
const arr = Array.isArray(src) ? src : (src ? [src] : []);
for (const s of arr) {
if (typeof s === "string") {
// quoted link or plain text
const unq = s.replace(/^"+|"+$/g, ""); // drop surrounding quotes if any
if (unq.includes(`[[${thisName}]]`) || unq.includes(`[[${thisName}|`)) return true;
// if they typed just the name
if (unq.replace(/\[\[|\]\]/g, "").trim().toLowerCase() === thisLC) return true;
} else if (s && typeof s === "object" && s.path) {
// wikilink object
const base = s.path.split("/").pop().replace(/\.md$/i, "").toLowerCase();
if (base === thisLC) return true;
}
}
// Optional (looser): plain text mention in summary
// return sum.toLowerCase().includes(thisLC);
return false;
}
// Collect rows: 1) own Intel, 2) mentions from other notes
const rows = [];
// Own Intel (from this NPC note)
for (const it of (THIS.Intel ?? [])) {
rows.push({
from: THIS.file.link,
date: it.date ?? "",
type: it.type ?? "",
summary: String(it.summary ?? ""),
session: it.session ?? "",
source: it.source ?? "",
rel: it.reliability ?? "",
cred: it.credibility ?? "",
tags: Array.isArray(it.tags) ? it.tags.join(", ") : (it.tags ?? "")
});
}
// Mentions elsewhere
for (const p of pages.where(p => p.file.path !== THIS.file.path)) {
for (const it of (p.Intel ?? [])) {
if (mentionsThisNPC(it)) {
rows.push({
from: p.file.link,
date: it.date ?? "",
type: it.type ?? "",
summary: String(it.summary ?? ""),
session: it.session ?? "",
source: it.source ?? "",
rel: it.reliability ?? "",
cred: it.credibility ?? "",
tags: Array.isArray(it.tags) ? it.tags.join(", ") : (it.tags ?? "")
});
}
}
}
// Sort newest first
rows.sort((a,b) => String(b.date).localeCompare(String(a.date)));
if (!rows.length) {
dv.paragraph("_No intel yet._");
} else {
dv.paragraph(`_Entries: ${rows.length} (own ${(THIS.Intel ?? []).length} + mentions ${Math.max(0, rows.length - (THIS.Intel ?? []).length)})_`);
dv.table(
["From Note","Date","Type","Summary","Session","Source","Rel","Cred","Tags"],
rows.map(r => [r.from, r.date, r.type, r.summary, r.session, r.source, r.rel, r.cred, r.tags])
);
}
```
```button
name Add Intel
type command
action Templater: Insert Add Intel Entry to NPC
```
---
## 🏛️ Relations & Factions
- **Primary Faction:** [[Faction Name]]
- **Allied Factions:** [[Faction A]], [[Faction B]]
- **Rival Factions:** [[Faction C]]
- **Personal Enemies / Allies:** [[NPC or PC]]
---
## 📖 History & Background
- **Origin / Past:**
- **Known Motivations:**
- **Secrets / Rumors:**
---
## 🎭 Roleplay Notes
- **Personality:** (e.g., stern, practical, loyal)
- **Speech Style / Quirks:** (catchphrases, accent, tics)
- **Appearance Details:** (clothing, scars, distinguishing features)
---
## 🗺️ Miscellaneous
- **Current Location:**
- **Important Items / Assets:**
- **Plot Hooks / Ties to PCs:**
- **Session Notes / Updates:**
- 2026-04-08: …
---
## 🔗 Connections
- **Related NPCs:**
- [[NPC A]] (Mentor)
- [[NPC B]] (Rival)
- **PC Ties:** (how the party knows them)
@@ -0,0 +1,189 @@
---
Date: 2026-04-08
Session_Name: Session 26 - Two Holes, Many Problems
Tags:
- pf2e
- session
---
# Session 26 - Two Holes, Many Problems
> **Date:** `= this.Date` • **Name:** `= this.Session_Name`
---
## 📄 Session Summary (auto)
<!-- SUMMARY-START -->
*No summary yet — click “Generate Summary” below.*
<!-- SUMMARY-END -->
```button
name ✍️ Generate AI Summary
type template
action Templates/Generate AI Session Summary.md
templater true
```
## 🧭 Where / When
- **Region / Location:**
- Y'athyr
- **In-game Date / Time:**
- **Weather / Conditions:**
---
## 👥 PCs Present / Absent
- **Present:**
- **Absent:**
---
## ⭐ Highlights (feed the auto-summary)
> Add one or more `Highlight::` lines. The button will build a summary from these.
- Highlight::
- Highlight::
- Highlight::
---
## 🎯 Quests & Progress
- **Active Objectives:**
- **Progress / Resolutions:**
---
## 🗣️ NPCs Met / Factions
- **NPCs:** [[ ]] [[ ]] [[ ]]
- **Factions Touched:** [[ ]] [[ ]]
---
## ⚔️ Encounters
- **Social / Exploration:**
- We pick up with [[Faylen Valora]] attempting to meet with a Journalist that she had found last session.
- They agree to meet in about an hour's time at the Dire Seagull -- which is the bar that [[Val]] introduced us to; her normal haunt.
- [[Faylen Valora]] leases the Private Room at the back of the bar - ordering some Tea.
- She waits almost 45 minutes before the Journalist shows up.
- [[Faylen Valora]] scolds the journalist for being late and evidently not owning a watch.
- The Journalist introduces herself as [[Saara Kallaste]] (Pronounced Sar-uh)
- [[Saara Kallaste]] explains that she is not from Y'athyr, but has lived here for a few years. She grew up out in the Trackless - before moving here.
- They gloss past the failing of [[Faylen Valora]]'s brother.
- Talk very briefly about the mission to the Trackless.
- [[Faylen Valora]] asks [[Saara Kallaste]] about her opinions on [[House Elidari]] but she does not have one.
- [[Faylen Valora]] proposes a plan in which [[Saara Kallaste]] will get to make a name for herself, as a groundbreaking journalist - by publishing the Truth. She would write on particular categories that [[Faylen Valora]] selects -- most relevantly:
- [[Iniana Asase]]
- [[Lord Ellin Vaust]]
- [[Elthea Rinvale]]
- [[Saara Kallaste]] offers resistance to the idea, saying that she works for one of the Major Publications. All of her articles need to be approved by her Editor. She is currently reporting on Fitting Navy Ships with brand new sails. She can only write about the things that are selected for her - and she doubts that she'd be able to get articles by her editor.
- [[Faylen Valora]] shifts to making their own paper, but [[Saara Kallaste]] inquires whether or not she has the means, capital or distribution that starting a new paper would need.
- [[Faylen Valora]] just needs someone to write and investigate - and she also offers a modest salary of more than 15 gold a month in addition to Protection. A real sense of pride and accomplishment in her work.
- [[Saara Kallaste]] would not have full editorial control, but [[Faylen Valora]] proposes that things will be a vote for the rest party; if not outright being discussed later.
- The group reconvenes at the SMH, discussing any findings/progress that has been made.
-
- **Combat:**
- **Outcome / Loot:**
---
## 🧠 Intel Captured (auto)
```dataviewjs
const THIS = dv.current();
const sessName = THIS.file.name;
const sessDate = THIS.Date ?? dv.current().file.frontmatter?.Date ?? "";
const sessLC = sessName.toLowerCase();
const pages = dv.pages("#npc or #faction").where(p => p.Intel);
function matchesSessionField(sessionField) {
const list = Array.isArray(sessionField) ? sessionField : [sessionField];
for (const s of list) {
if (!s) continue;
if (typeof s === "object" && s.path) {
const base = s.path.split("/").pop().replace(/\.md$/i,"");
if (base.toLowerCase() === sessLC) return true;
} else {
const text = String(s);
if (text.includes(`[[${sessName}]]`)) return true;
if (text.toLowerCase() === sessLC) return true;
}
}
return false;
}
const rows = [];
for (const p of pages) {
for (const it of p.Intel) {
let ok = false;
if (it.session) ok = matchesSessionField(it.session);
if (!ok && sessDate && it.date) ok = String(it.date) === String(sessDate); // fallback
if (ok) {
rows.push({
from: p.file.link,
date: it.date ?? "",
type: it.type ?? "",
summary: String(it.summary ?? ""),
source: it.source ?? "",
rel: it.reliability ?? "",
cred: it.credibility ?? "",
tags: Array.isArray(it.tags) ? it.tags.join(", ") : (it.tags ?? "")
});
}
}
}
rows.sort((a,b)=> String(b.date).localeCompare(String(a.date)));
if (!rows.length) {
dv.paragraph("_No intel captured for this session yet._");
} else {
dv.table(
["From", "Date", "Type", "Summary", "Source", "Rel", "Cred", "Tags"],
rows.map(r => [r.from, r.date, r.type, r.summary, r.source, r.rel, r.cred, r.tags])
);
}
```
---
## 📦 Treasure & Rewards
- **Items:**
- **Coin:**
- **XP / Milestones:**
---
## 🔜 Next Hooks / To-Dos
- [ ]
- [ ]
- [ ]
Binary file not shown.

After

Width:  |  Height:  |  Size: 806 KiB