generated from obsidianmd/obsidian-sample-plugin
-
Notifications
You must be signed in to change notification settings - Fork 4
/
statblockrenderer.ts
121 lines (103 loc) · 3.46 KB
/
statblockrenderer.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import { MarkdownRenderChild } from "obsidian";
export class StatblockRenderer extends MarkdownRenderChild {
statblockEl: HTMLDivElement;
constructor(containerEl: HTMLElement, private params: any) {
super(containerEl);
this.statblockEl = this.containerEl.createDiv({ cls: "statblock-13a" });
this.statblockEl.createDiv({ cls: "fl-r em", text: params.source });
this.statblockEl.createEl("h1", { cls: "sc nomargin", text: params.name });
if (params.blurb) {
this.statblockEl.createDiv({ cls: "em", text: params.blurb });
}
if (this.roleText !== undefined) {
const role = this.statblockEl.createDiv({ cls: "nomargin" });
role.createSpan({ cls: "em", text: this.roleText });
if (params.tag) {
role.createSpan({ cls: "sc", text: ` [${params.tag}]` });
}
}
if (params.initiative !== undefined) {
this.statblockEl.createDiv({
text: `Initiative: ${bonus(params.initiative)}`,
cls: params.vuln ? "nomargin" : undefined,
});
}
if (params.vuln !== undefined) {
this.statblockEl.createDiv({
text: `Vulnerability: ${params.vuln}`,
});
}
for (const attack of params.attacks || []) {
this.renderAttack(attack);
}
for (const trait of params.traits || []) {
this.renderSimpleItem(trait);
}
if (params.specials?.length > 0) {
this.statblockEl.createEl("h2", { text: "Nastier Specials" });
for (const special of params.specials) {
this.renderSimpleItem(special);
}
}
const numbers = this.statblockEl.createDiv({ cls: "numbers" });
const defenses = numbers.createDiv();
defenses.createDiv({ cls: "bold", text: `AC ${params.ac}` });
defenses.createDiv({ text: `PD ${params.pd}` });
defenses.createDiv({ text: `MD ${params.md}` });
numbers.createDiv({ cls: "bold", text: `HP ${params.hp}` });
numbers.createDiv("");
}
get roleText(): string | undefined {
if (this.params.level === undefined) return undefined;
const ordinalRules = new Intl.PluralRules("en", { type: "ordinal" });
const suffixes = {
zero: "th",
one: "st",
two: "nd",
few: "rd",
other: "th",
many: "th",
};
const suffix = suffixes[ordinalRules.select(this.params.level)];
const nth = `${this.params.level}${suffix}`;
return capitalize(
[this.params.size, `${nth} level`, this.params.role]
.join(" ")
.trim()
);
}
renderAttack(attack: any) {
const attackEl = this.statblockEl.createDiv({ cls: "attack" });
if (attack.tag) {
attackEl.createSpan({ cls: "em", text: `[${attack.tag}] ` });
}
const titleParts = [
attack.type === "ranged" ? "R:" : "",
attack.type === "close" ? "C:" : "",
attack.name,
attack.attack,
attack.detail ? `(${attack.detail})` : "",
];
attackEl.createSpan({ cls: "bold", text: titleParts.join(" ").trim() });
attackEl.createSpan({ text: ` — ${attack.hit}` });
for (const extra of attack.extras ?? []) {
const div = attackEl.createDiv();
div.createSpan({ cls: "em", text: `${extra.name}: ` });
div.createSpan({ text: extra.description });
}
}
renderSimpleItem(trait: any) {
const traitEl = this.statblockEl.createDiv();
traitEl.createEl('h5', { cls: "em", text: `${trait.name}: ` });
const descriptionEl = traitEl.createDiv();
descriptionEl.innerHTML = trait.description;
}
}
function capitalize(str: string): string {
const lower = str.toLowerCase();
return lower[0].toUpperCase() + lower.slice(1);
}
function bonus(stat: number | string): string {
if (stat === 0) return stat.toString();
return stat > 0 ? `+${stat}` : `${stat}`;
}