vault backup: 2025-10-02 17:18
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
<%*
|
||||
/* Add Intel entry to CURRENT Faction note, with vault-aware suggestions & link completion. */
|
||||
|
||||
const file = app.workspace.getActiveFile();
|
||||
if (!file) { new Notice("Open a Faction note first."); return; }
|
||||
|
||||
const today = tp.date.now("YYYY-MM-DD");
|
||||
|
||||
// ---------- TYPE ----------
|
||||
new Notice("Step 1/6 — Pick Intel TYPE");
|
||||
const TYPE_OPTIONS = ["interaction","report","rumor","asset","op"];
|
||||
const type = await tp.system.suggester(TYPE_OPTIONS.map(x => `Type: ${x}`), TYPE_OPTIONS);
|
||||
if (!type) { new Notice("Cancelled."); return; }
|
||||
|
||||
// ---------- SOURCE (suggest existing notes as [[Note]]) ----------
|
||||
new Notice("Step 2/6 — Choose SOURCE");
|
||||
const allFiles = app.vault.getMarkdownFiles();
|
||||
const names = allFiles.map(f => f.basename).sort((a,b)=>a.localeCompare(b, undefined, {sensitivity:"base"}));
|
||||
const wikiChoices = ["(manual entry)…", ...names.map(n => `[[${n}]]`)];
|
||||
let sourcePick = await tp.system.suggester(wikiChoices, wikiChoices);
|
||||
if (!sourcePick) { new Notice("Cancelled."); return; }
|
||||
let source = sourcePick === "(manual entry)…"
|
||||
? (await tp.system.prompt("Source (free text or [[Link]])")) ?? ""
|
||||
: sourcePick;
|
||||
|
||||
// ---------- SUMMARY (free text, then auto-complete [[links]]) ----------
|
||||
new Notice("Step 3/6 — Write SUMMARY (you can include [[links]])");
|
||||
let summary = await tp.system.prompt("Summary (what happened)?\nTip: You can include [[Note]] names; we’ll auto-complete them next.") ?? "";
|
||||
|
||||
// Helper: resolve [[links]] in the summary to existing notes (prompt if ambiguous)
|
||||
async function resolveWikiLinks(text) {
|
||||
// Find [[...]] occurrences
|
||||
const re = /\[\[([^[\]|#]+)(?:#[^\]|]+)?(?:\|[^\]]+)?\]\]/g;
|
||||
let m, out = text, offset = 0;
|
||||
while ((m = re.exec(text)) !== null) {
|
||||
const raw = m[0], term = m[1].trim();
|
||||
const exact = names.find(n => n.toLowerCase() === term.toLowerCase());
|
||||
let replacement = raw;
|
||||
if (exact) {
|
||||
replacement = `[[${exact}]]`;
|
||||
} else {
|
||||
// Offer matching notes to replace this token
|
||||
const matches = names.filter(n => n.toLowerCase().includes(term.toLowerCase())).slice(0,50);
|
||||
const disp = [`Keep as typed: [[${term}]]`, ...matches.map(n => `→ [[${n}]]`)];
|
||||
const vals = [null, ...matches];
|
||||
const pick = await tp.system.suggester(disp, vals);
|
||||
if (pick) replacement = `[[${pick}]]`;
|
||||
}
|
||||
// Splice into output string at the correct position (accounting for prior replacements)
|
||||
const start = m.index + offset;
|
||||
out = out.slice(0, start) + replacement + out.slice(start + raw.length);
|
||||
offset += replacement.length - raw.length;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
summary = await resolveWikiLinks(summary);
|
||||
|
||||
// Optional: append more links via suggester loop
|
||||
while (true) {
|
||||
const addMore = await tp.system.suggester(
|
||||
["(done)","Append a wikilink…"], ["__done__","__add__"]
|
||||
);
|
||||
if (!addMore || addMore === "__done__") break;
|
||||
const pick = await tp.system.suggester(names.map(n => `[[${n}]]`), names.map(n => `[[${n}]]`));
|
||||
if (pick) summary = (summary ? summary + " " : "") + pick;
|
||||
}
|
||||
|
||||
// ---------- RELIABILITY / CREDIBILITY ----------
|
||||
new Notice("Step 4/6 — Reliability (A–E)");
|
||||
const relDisplay = ["A — confirmed","B — likely","C — uncertain","D — doubtful","E — unknown"];
|
||||
const relValues = ["A","B","C","D","E"];
|
||||
const reliability = (await tp.system.suggester(relDisplay, relValues)) ?? "C";
|
||||
|
||||
new Notice("Step 5/6 — Credibility (1–5)");
|
||||
const credDisplay = ["5 — strong","4 — good","3 — fair","2 — weak","1 — very weak"];
|
||||
const credValues = ["5","4","3","2","1"];
|
||||
const credibility = (await tp.system.suggester(credDisplay, credValues)) ?? "3";
|
||||
|
||||
// ---------- TAGS (multi-select from existing vault tags) ----------
|
||||
new Notice("Step 6/6 — Add TAGS (optional)");
|
||||
const tagMap = app.metadataCache.getTags?.() ?? {};
|
||||
let allTags = Array.from(new Set(Object.keys(tagMap).map(t => t.replace(/^#/, "")))).sort();
|
||||
const chosen = new Set();
|
||||
while (true) {
|
||||
const remaining = allTags.filter(t => !chosen.has(t));
|
||||
const display = ["(done)", "(new tag)…", ...remaining.map(t => `#${t}`)];
|
||||
const values = ["__done__", "__new__", ...remaining];
|
||||
const pick = await tp.system.suggester(display, values);
|
||||
if (!pick || pick === "__done__") break;
|
||||
if (pick === "__new__") {
|
||||
const newTag = (await tp.system.prompt("New tag (without #)"))?.trim();
|
||||
if (newTag) { chosen.add(newTag); if (!allTags.includes(newTag)) allTags.push(newTag); }
|
||||
} else {
|
||||
chosen.add(pick);
|
||||
}
|
||||
}
|
||||
const tags = Array.from(chosen);
|
||||
|
||||
// ---------- Update frontmatter safely ----------
|
||||
await app.fileManager.processFrontMatter(file, (fm) => {
|
||||
if (!Array.isArray(fm.Intel)) fm.Intel = [];
|
||||
fm.Intel.unshift({
|
||||
date: today,
|
||||
type,
|
||||
summary,
|
||||
source,
|
||||
reliability,
|
||||
credibility: Number(credibility),
|
||||
tags
|
||||
});
|
||||
});
|
||||
|
||||
new Notice("Intel entry added.");
|
||||
%>
|
||||
@@ -0,0 +1,10 @@
|
||||
# Session XX - X
|
||||
## Information
|
||||
Date:
|
||||
Players:
|
||||
DM:
|
||||
## Overview
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
---
|
||||
# Reputation: -10 (very bad) … 0 (neutral) … +10 (very good)
|
||||
Reputation: 0
|
||||
Faction_Type: # e.g., Noble House, Guild, Cult, Order, Syndicate
|
||||
Motto:
|
||||
Ideology: # beliefs, alignment, edicts/anathema
|
||||
Symbol:
|
||||
Colors:
|
||||
Headquarters:
|
||||
Territories:
|
||||
-
|
||||
Allies:
|
||||
-
|
||||
Rivals:
|
||||
-
|
||||
SMH_Acronym:
|
||||
# Intelligence & Interactions log (array of objects; keep newest on top)
|
||||
Intel:
|
||||
# - date: 2025-10-02
|
||||
# type: interaction # interaction | report | rumor | asset | op
|
||||
# summary: Met with Faylen; agreed to discreet escort for supply barge.
|
||||
# source: [[Faylen Valora]] # free text or wikilink
|
||||
# reliability: A # A (confirmed) … E (unknown)
|
||||
# credibility: 4 # 1 (weak) … 5 (strong)
|
||||
# tags: [meeting, docks, payment]
|
||||
Tags:
|
||||
- faction
|
||||
---
|
||||
|
||||
# <% tp.file.title %>
|
||||
*One-line role/description — e.g., “Elven High House controlling Y’athir’s docks.”*
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Artwork
|
||||
![[<% tp.file.title %>.png]]
|
||||
*Optional caption / artist credit*
|
||||
|
||||
---
|
||||
|
||||
## 🧭 Identity & Heraldry
|
||||
- **Motto:** `= default(this.Motto, "—")`
|
||||
- **Symbol:** `= default(this.Symbol, "—")`
|
||||
- **Colors:** `= default(this.Colors, "—")`
|
||||
- **Headquarters:** `= default(this.Headquarters, "—")`
|
||||
- **Territories:** `= this.Territories`
|
||||
|
||||
---
|
||||
|
||||
## 📜 Ideology & Goals
|
||||
- **Ideology / Alignment:** `= default(this.Ideology, "—")`
|
||||
- **Primary Goals:**
|
||||
-
|
||||
- **Taboos / Anathema:**
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
## 🤝 Relationship with Party
|
||||
- **Reputation:** `= this.Reputation`
|
||||
|
||||
---
|
||||
|
||||
## 🧑💼 Leadership & Structure
|
||||
- **Leader(s):** [[NPC A]], [[NPC B]]
|
||||
- **Structure:** (council, hierarchy, cells, chapters)
|
||||
- **Key Offices:** (Intelligence, Logistics, Priory, Treasury, etc.)
|
||||
|
||||
---
|
||||
|
||||
## 🧰 Assets & Operations
|
||||
- **Resources:** (coin, troops, mages, ships, safehouses, contacts)
|
||||
- **Notable Holdings:**
|
||||
- **Typical Ops / Tactics:**
|
||||
|
||||
---
|
||||
|
||||
## 🕊️ Diplomacy & Relations
|
||||
- **Allies:** `= this.Allies`
|
||||
- **Rivals:** `= this.Rivals`
|
||||
- **Stance toward “SMH” (their wording):** `= default(this.SMH_Acronym, "—")`
|
||||
|
||||
---
|
||||
|
||||
## 🧠 Intelligence & Mentions
|
||||
```button
|
||||
name ➕ Add Intel
|
||||
type command
|
||||
action Templater: Insert Add Intel Entry to Faction
|
||||
```
|
||||
```dataviewjs
|
||||
const THIS = dv.current();
|
||||
const thisName = String(THIS.file.name);
|
||||
const thisLC = thisName.toLowerCase();
|
||||
|
||||
const factions = dv.pages("#faction");
|
||||
|
||||
// mention detector (same logic as your table)
|
||||
function mentionsThisFaction(entry) {
|
||||
const sum = String(entry.summary ?? "");
|
||||
if (sum.includes(`[[${thisName}]]`) || sum.includes(`[[${thisName}|`)) return true;
|
||||
const src = entry.source;
|
||||
const list = Array.isArray(src) ? src : (src ? [src] : []);
|
||||
for (const s of list) {
|
||||
if (typeof s === "string") {
|
||||
if (s.includes(`[[${thisName}]]`) || s.includes(`[[${thisName}|`)) return true;
|
||||
if (s.toLowerCase() === thisLC) return true;
|
||||
} else if (s && typeof s === "object" && s.path) {
|
||||
const base = s.path.split("/").pop().replace(/\.md$/i,"").toLowerCase();
|
||||
if (base === thisLC) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const own = (THIS.Intel ?? []).length;
|
||||
|
||||
let mentions = 0;
|
||||
for (const p of factions.where(p => p.file.name !== thisName)) {
|
||||
for (const it of (p.Intel ?? [])) if (mentionsThisFaction(it)) mentions++;
|
||||
}
|
||||
|
||||
dv.paragraph(`_Entries: ${own + mentions} (own ${own} + mentions ${mentions})_`);
|
||||
```
|
||||
```dataviewjs
|
||||
// Current faction
|
||||
const THIS = dv.current();
|
||||
const thisName = String(THIS.file.name);
|
||||
const thisLC = thisName.toLowerCase();
|
||||
|
||||
// All faction pages (including this one)
|
||||
const factions = dv.pages("#faction");
|
||||
|
||||
// Mention detector: summary has [[This Faction]] OR source points at this faction
|
||||
function mentionsThisFaction(entry) {
|
||||
const sum = String(entry.summary ?? "");
|
||||
if (sum.includes(`[[${thisName}]]`) || sum.includes(`[[${thisName}|`)) return true;
|
||||
|
||||
const src = entry.source;
|
||||
const list = Array.isArray(src) ? src : (src ? [src] : []);
|
||||
for (const s of list) {
|
||||
if (typeof s === "string") {
|
||||
if (s.includes(`[[${thisName}]]`) || s.includes(`[[${thisName}|`)) return true;
|
||||
if (s.toLowerCase() === thisLC) return true; // fallback: plain text match
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Collect rows
|
||||
const rows = [];
|
||||
|
||||
// 1) Own intel
|
||||
for (const it of (THIS.Intel ?? [])) {
|
||||
rows.push({
|
||||
date: it.date ?? "",
|
||||
from: THIS.file.link,
|
||||
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 ?? "")
|
||||
});
|
||||
}
|
||||
|
||||
// 2) Mentions from other factions
|
||||
for (const p of factions.where(p => p.file.name !== thisName)) {
|
||||
for (const it of (p.Intel ?? [])) {
|
||||
if (mentionsThisFaction(it)) {
|
||||
rows.push({
|
||||
date: it.date ?? "",
|
||||
from: p.file.link,
|
||||
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 ?? "")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort newest first
|
||||
rows.sort((a,b) => String(b.date).localeCompare(String(a.date)));
|
||||
|
||||
if (!rows.length) {
|
||||
dv.paragraph("_No intel yet._");
|
||||
} else {
|
||||
dv.table(
|
||||
["Date","From Faction","Type","Summary","Source","Rel","Cred","Tags"],
|
||||
rows.map(r => [r.date, r.from, r.type, r.summary, r.source, r.rel, r.cred, r.tags])
|
||||
);
|
||||
}
|
||||
```
|
||||
> Add new entries in the **frontmatter** under `Intel:` (keep newest on top). Suggested fields: `date, type, summary, source, reliability, credibility, tags`.
|
||||
|
||||
---
|
||||
|
||||
## 🔗 Related NPCs (auto)
|
||||
```dataview
|
||||
TABLE link(file.name) AS NPC, number(Level_of_Influence) AS Influence, default(SMH_Acronym, "—") AS SMH
|
||||
FROM #npc
|
||||
WHERE file.name != "NPC Template"
|
||||
AND (
|
||||
Faction = this.file.link
|
||||
OR lower(string(Faction)) = lower(this.file.name)
|
||||
OR contains(lower(string(Faction)), lower(this.file.name))
|
||||
)
|
||||
SORT number(Level_of_Influence) DESC
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📌 Plot Hooks & Notes
|
||||
-
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
## 🗒 Session Updates
|
||||
- <% tp.date.now("YYYY-MM-DD") %>: …
|
||||
@@ -0,0 +1,100 @@
|
||||
# 🗂 NPC Influence Dashboard
|
||||
|
||||
> Scale: −10 … 0 … +10 (0 = Neutral; positive = more influential, negative = less)
|
||||
|
||||
---
|
||||
|
||||
## 😡 Hostile (−10 to −7)
|
||||
```dataview
|
||||
TABLE link(file.name) AS NPC, Faction, Level_of_Influence
|
||||
FROM "NPCs"
|
||||
WHERE Level_of_Influence <= -7
|
||||
SORT Level_of_Influence ASC
|
||||
```
|
||||
|
||||
## 😕 Unfriendly (-6 to -1)
|
||||
```dataview
|
||||
TABLE link(file.name) AS NPC, Faction, Level_of_Influence
|
||||
FROM "NPCs"
|
||||
WHERE Level_of_Influence >= -6 AND Level_of_Influence <= -1
|
||||
SORT Level_of_Influence ASC
|
||||
```
|
||||
|
||||
## 😐 Neutral (0)
|
||||
```dataview
|
||||
TABLE link(file.name) AS NPC, Faction, Level_of_Influence
|
||||
FROM "NPCs"
|
||||
WHERE Level_of_Influence = 0
|
||||
SORT file.name ASC
|
||||
```
|
||||
|
||||
## 🙂 Friendly (+1 to +6)
|
||||
```dataview
|
||||
TABLE link(file.name) AS NPC, Faction, Level_of_Influence
|
||||
FROM "NPCs"
|
||||
WHERE Level_of_Influence >= 1 AND Level_of_Influence <= 6
|
||||
SORT Level_of_Influence DESC
|
||||
```
|
||||
|
||||
## 🛡 Allied (+7 to +10)
|
||||
```dataview
|
||||
TABLE link(file.name) AS NPC, Faction, Level_of_Influence
|
||||
FROM "NPCs"
|
||||
WHERE Level_of_Influence >= 7
|
||||
SORT Level_of_Influence DESC
|
||||
```
|
||||
|
||||
## 📊 Visual Bands (Emoji + Bipolar Progress Bars)
|
||||
```dataview
|
||||
// ===== Settings =====
|
||||
const FOLDER = "NPCs";
|
||||
const MAX = 10; // absolute max in either direction
|
||||
|
||||
// ===== Helpers =====
|
||||
const tier = s =>
|
||||
s === 0 ? "Neutral" :
|
||||
s < 0 ? (s <= -7 ? "Hostile" : "Unfriendly") :
|
||||
(s >= 7 ? "Allied" : "Friendly");
|
||||
|
||||
const face = s =>
|
||||
s === 0 ? "😐" :
|
||||
s < 0 ? (s <= -7 ? "😡" : "😕") :
|
||||
(s >= 7 ? "🛡️" : "🙂");
|
||||
|
||||
// Bipolar bar: [LLLLL|RRRRR]
|
||||
function bipolarBar(score) {
|
||||
const leftFilled = Math.min(MAX, Math.max(0, -score));
|
||||
const rightFilled = Math.min(MAX, Math.max(0, score));
|
||||
const leftEmpty = MAX - leftFilled;
|
||||
const rightEmpty = MAX - rightFilled;
|
||||
const L = "█".repeat(leftFilled) + "░".repeat(leftEmpty);
|
||||
const R = "█".repeat(rightFilled) + "░".repeat(rightEmpty);
|
||||
return `[${L}|${R}]`;
|
||||
}
|
||||
|
||||
// ===== Fetch =====
|
||||
const pages = dv.pages(`"${FOLDER}"`)
|
||||
.where(p => Number.isFinite(+p.Level_of_Influence))
|
||||
.map(p => ({
|
||||
name: p.file.link,
|
||||
faction: p.Faction ?? "",
|
||||
score: +p.Level_of_Influence
|
||||
}));
|
||||
|
||||
const groups = {
|
||||
"😡 Hostile (−10 to −7)": pages.filter(p => p.score <= -7).sort((a,b)=>a.score-b.score),
|
||||
"😕 Unfriendly (−6 to −1)": pages.filter(p => p.score >= -6 && p.score <= -1).sort((a,b)=>a.score-b.score),
|
||||
"😐 Neutral (0)": pages.filter(p => p.score === 0).sort((a,b)=>String(a.name).localeCompare(String(b.name))),
|
||||
"🙂 Friendly (+1 to +6)": pages.filter(p => p.score >= 1 && p.score <= 6).sort((a,b)=>b.score-a.score),
|
||||
"🛡 Allied (+7 to +10)": pages.filter(p => p.score >= 7).sort((a,b)=>b.score-a.score),
|
||||
};
|
||||
|
||||
for (const [title, rows] of Object.entries(groups)) {
|
||||
dv.header(2, title);
|
||||
if (!rows.length) { dv.paragraph("_None yet._"); continue; }
|
||||
dv.table(
|
||||
["NPC", "Faction", "Score", "Bar", "Tier"],
|
||||
rows.map(r => [r.name, r.faction, r.score, bipolarBar(r.score), `${face(r.score)} ${tier(r.score)}`])
|
||||
);
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
# Influence: -10 (very bad) … 0 (neutral) … +10 (very good)
|
||||
Level_of_Influence: 0
|
||||
Faction:
|
||||
SMH_Acronym: #e.g., "So Much Honor" / "Saints Marching Harbor"
|
||||
Tags: [npc]
|
||||
---
|
||||
|
||||
# <% tp.file.title %>
|
||||
*Role/Description — e.g., "Captain of the Guard", "Elven Scholar", "Local Merchant"*
|
||||
|
||||
## 🎨 Artwork
|
||||
![[<% tp.file.title %>.png]]
|
||||
*Optional caption / alt text*
|
||||
|
||||
---
|
||||
|
||||
## 🧑 NPC Overview
|
||||
- **Full Name:**
|
||||
- **Race / Ancestry:**
|
||||
- **Gender / Pronouns:**
|
||||
- **Occupation / Role:**
|
||||
- **Faction / Allegiance:** <% tp.frontmatter.Faction ?? "" %>
|
||||
- **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***
|
||||
|
||||
---
|
||||
|
||||
## 🏛️ 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:**
|
||||
- <% tp.date.now("YYYY-MM-DD") %>: …
|
||||
|
||||
---
|
||||
|
||||
## 🔗 Connections
|
||||
- **Related NPCs:**
|
||||
- [[NPC A]] (Mentor)
|
||||
- [[NPC B]] (Rival)
|
||||
- **PC Ties:** (how the party knows them)
|
||||
Reference in New Issue
Block a user