Source for Sean's Star Sagas stuff
1// Licensed under the Open Software License version 3.0
2// OR
3// Licensed under the MIT License.
4
5#let accent = red.darken(50%).saturate(-25%)
6#let paper-color = rgb("#fae6cf")
7
8#let template(content) = {
9 set page(fill: paper-color)
10 set text(font: "Gentium Book Plus", features: ("ss02",))
11 show heading: set text(font: "Ysabeau Office", weight: 800)
12 show heading: set par(leading: 0.325em)
13 show heading: set align(center)
14
15 content
16}
17
18#let modifier(number) = {
19 let mod = calc.floor((number - 10) / 2)
20 if (mod < 0) [#mod] else { [+#mod] }
21}
22
23#let stat-template(content) = {
24 set text(font: "Ubuntu Sans", number-type: "lining")
25 show strong: set text(fill: accent)
26 set line(stroke: (paint: accent))
27 let statblock-gradient = gradient.linear(angle: 45deg, white, white.darken(10%))
28
29 block(
30 fill: statblock-gradient,
31 width: 100%,
32 inset: 0.65em,
33 breakable: false,
34 content,
35 )
36}
37
38#let sourceref(abbr) = super[\[#abbr\]]
39
40#let statblock-template(
41 slot-title: [],
42 slot-ability-scores: [],
43 slot-named-ability-sections: [],
44) = {}
45
46#let npc-statblock(
47 name: [],
48 description: [],
49 str: 10,
50 dex: 10,
51 con: 10,
52 int: 10,
53 wis: 10,
54 cha: 10,
55 attrs: (),
56 stats: (),
57 feats: (),
58 role: [],
59 role-detail: [],
60 num-columns: 1,
61 named-ability-sections: (),
62) = {
63 set align(start + top)
64 show: stat-template
65 set columns(num-columns)
66
67 let padding = 0.65em
68 let stroke-width = 0.75pt
69 let thin-stroke = stroke-width + accent
70 let thick-stroke = stroke-width * 2 + accent
71
72 set par(spacing: padding, leading: padding)
73 set stack(spacing: padding)
74 set line(stroke: stroke-width)
75
76 show: columns
77
78 let statline(content) = {
79 set par(hanging-indent: 1em)
80
81 content
82 }
83
84 let action-heading = block.with(
85 width: 100%,
86 inset: (bottom: padding / 2),
87 stroke: (bottom: stroke-width + accent),
88 above: 1em,
89 )
90
91 {
92 set par(leading: padding / 2)
93 grid(
94 columns: (1fr, auto),
95 grid.cell(
96 stroke: (
97 right: thin-stroke,
98 top: thick-stroke,
99 bottom: thin-stroke,
100 ),
101 inset: (y: padding, right: padding),
102 stack(
103 dir: ttb,
104 text(size: 1.5em, smallcaps[*#name*]),
105 emph(description),
106 ),
107 ),
108 grid.cell(
109 stroke: (
110 left: thin-stroke,
111 top: thick-stroke,
112 bottom: thin-stroke,
113 ),
114 inset: (top: padding, left: padding),
115 {
116 set align(top + right)
117 {
118 strong(role)
119
120 par(role-detail)
121 }
122 },
123 ),
124 )
125 }
126
127 {
128 show: statline
129 stack(
130 dir: ttb,
131 ..attrs.map(par),
132 )
133 }
134
135 line(stroke: 1.5pt, length: 100%)
136
137 {
138 set block(width: 100%)
139 set align(center)
140 grid(
141 columns: 6,
142 column-gutter: padding,
143 stack(dir: ttb, [*STR*], [#str (#modifier(str))]),
144 stack(dir: ttb, [*DEX*], [#dex (#modifier(dex))]),
145 stack(dir: ttb, [*CON*], [#con (#modifier(con))]),
146 stack(dir: ttb, [*INT*], [#int (#modifier(int))]),
147 stack(dir: ttb, [*WIS*], [#wis (#modifier(wis))]),
148 stack(dir: ttb, [*CHA*], [#cha (#modifier(cha))]),
149 )
150 }
151
152 line(stroke: 1.5pt, length: 100%)
153
154 {
155 show: statline
156 stack(
157 dir: ttb,
158 ..stats.map(par),
159 )
160 }
161
162
163 if (
164 feats.len() > 0 or named-ability-sections.len() > 0
165 ) { line(length: 100%) }
166
167 if (feats.len() > 0) {
168 stack(dir: ttb, ..feats)
169 }
170
171 if (named-ability-sections.len() > 0) {
172 for section in named-ability-sections {
173 action-heading(upper(strong(section.title)))
174
175 stack(dir: ttb, ..section.abilities)
176 }
177 }
178}
179
180#let hero-spread-template(insert-page: true, content) = {
181 set page(numbering: none, columns: 1)
182 set text(font: "Ysabeau Office", size: 14pt)
183 show heading: set text(
184 size: 4em,
185 font: "Reforma 1969",
186 features: (
187 "ss01",
188 "smcp",
189 ),
190 style: "italic",
191 )
192
193 if (insert-page == true) {
194 pagebreak()
195 }
196
197 content
198}
199
200#let hero-heading-template(insert-page: true, content) = {
201 if (insert-page == true) {
202 pagebreak()
203 }
204
205 show: place.with(
206 top + center,
207 float: true,
208 scope: "parent",
209 )
210
211 show heading: set text(
212 font: "Reforma 1918",
213 size: 28pt,
214 weight: 900,
215 tracking: 0pt,
216 )
217
218 content
219}
220
221#let hero-heading-par(content) = {
222 let padding = 1em
223 show: block.with(width: 100%, inset: (y: padding))
224 set align(start)
225 set text(font: "Ysabeau Office")
226
227 content
228}
229
230#let sidebar-template(title: [], content) = {
231 set text(size: 1em, font: "Ubuntu Sans")
232 show heading: set text(1em, font: "Ubuntu Sans")
233 show: block.with(fill: white, inset: 0.65em)
234
235 if (title != []) {
236 text(size: 1em, title)
237 }
238
239 content
240}
241
242#let templates = (
243 base: template,
244 stat: stat-template,
245 chapter-heading: hero-heading-template,
246 chapter-heading-par: hero-heading-par,
247 chapter-intro: hero-spread-template,
248 sidebar: sidebar-template,
249)