87 lines
2.6 KiB
Markdown
87 lines
2.6 KiB
Markdown
# 🗂 NPC Influence Dashboard
|
||
|
||
> Scale: −10 … 0 … +10 (0 = Neutral; positive = more influential, negative = less)
|
||
> Source: **#npc** • Excluding: **NPC Template.md** (change the name below if yours differs)
|
||
|
||
---
|
||
|
||
## 🧾 SMH Index (who calls it what?)
|
||
```dataview
|
||
TABLE rows.file.link AS NPCs, rows.Faction AS Factions
|
||
FROM #npc
|
||
WHERE file.name != "NPC Template" AND SMH_Acronym
|
||
GROUP BY SMH_Acronym
|
||
SORT SMH_Acronym ASC
|
||
```
|
||
|
||
|
||
## 📊 Visual Bands (Emoji + Bipolar Progress Bars)
|
||
```dataviewjs
|
||
// ===== Source (tag-based; exclude template by name) =====
|
||
const PAGES = dv.pages('#npc')
|
||
.where(p => p.file.name != "NPC Template")
|
||
.where(p => Number.isFinite(+p.Level_of_Influence))
|
||
.map(p => ({
|
||
name: p.file.link,
|
||
faction: p.Faction ?? "—",
|
||
score: +p.Level_of_Influence
|
||
}));
|
||
|
||
const MAX = 10; // absolute ends: -10 .. +10
|
||
const WIDTH = 21; // must be 2*MAX+1 to center on 0
|
||
const CENTER = 10; // index of the 0 mark (10 for WIDTH=21)
|
||
|
||
// ===== Helpers =====
|
||
function tier(s) {
|
||
if (s === 0) return "😐 Neutral";
|
||
if (s <= -7) return "😡 Hostile";
|
||
if (s <= -1) return "😕 Unfriendly";
|
||
if (s >= 7) return "🛡 Allied";
|
||
return "🙂 Friendly";
|
||
}
|
||
|
||
// Build a centered gauge line like: −10 ───────┼──────── +10
|
||
// and a second line placing a ▼ under the current position.
|
||
function gauge(score) {
|
||
const idx = Math.max(0, Math.min(WIDTH - 1, Math.round(score + MAX)));
|
||
const ticks = Array(WIDTH).fill("─");
|
||
ticks[CENTER] = "┼"; // zero mark
|
||
const bar = ticks.join("");
|
||
|
||
const leftLabel = "−10 ";
|
||
const rightLabel = " +10";
|
||
const line1 = `${leftLabel}${bar}${rightLabel}`;
|
||
|
||
// Use nbsp so alignment holds inside tables
|
||
const arrowOffset = leftLabel.length + idx;
|
||
const arrowLine = " ".repeat(arrowOffset) + "▼";
|
||
|
||
return `<code>${line1}<br>${arrowLine}</code>`;
|
||
}
|
||
|
||
// ===== Groups =====
|
||
const groups = {
|
||
"😡 Hostile (−10 to −7)": PAGES.filter(p => p.score <= -7),
|
||
"😕 Unfriendly (−6 to −1)": PAGES.filter(p => p.score >= -6 && p.score <= -1),
|
||
"😐 Neutral (0)": PAGES.filter(p => p.score === 0),
|
||
"🙂 Friendly (+1 to +6)": PAGES.filter(p => p.score >= 1 && p.score <= 6),
|
||
"🛡 Allied (+7 to +10)": PAGES.filter(p => p.score >= 7)
|
||
};
|
||
|
||
// ===== Render =====
|
||
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", "Gauge", "Tier"],
|
||
rows.map(r => [
|
||
r.name,
|
||
r.faction,
|
||
r.score,
|
||
dv.span(gauge(r.score)),
|
||
tier(r.score)
|
||
])
|
||
);
|
||
}
|
||
```
|