pf2e-primer 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +31 -0
- package/README.md +106 -0
- package/bin/cli.mjs +86 -0
- package/data/reference.generated.js +6 -0
- package/dist/icon.svg +6 -0
- package/dist/index.html +2275 -0
- package/dist/manifest.webmanifest +13 -0
- package/dist/sw.js +60 -0
- package/notice.md +124 -0
- package/package.json +47 -0
- package/src/content.js +547 -0
- package/src/engine.js +1210 -0
- package/src/icon.svg +6 -0
- package/src/manifest.webmanifest +13 -0
- package/src/styles.css +445 -0
- package/src/sw.js +60 -0
- package/src/template.html +67 -0
- package/tools/build.py +60 -0
- package/tools/build_reference.py +249 -0
- package/tools/test.mjs +277 -0
package/dist/index.html
ADDED
|
@@ -0,0 +1,2275 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
|
6
|
+
<meta name="theme-color" content="#14161b">
|
|
7
|
+
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
8
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
9
|
+
<meta name="apple-mobile-web-app-title" content="PF2e Primer">
|
|
10
|
+
<meta name="description" content="A hands-on introduction to Pathfinder 2e for players who already know tabletop RPGs — the three pillars of play, with live demos of every core rule.">
|
|
11
|
+
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%233f7d8c' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'><circle cx='12' cy='5' r='2'/><path d='M12 7v13'/><path d='M8 11h8'/><path d='M5 14a7 7 0 0 0 14 0'/></svg>">
|
|
12
|
+
<!-- Set the theme before first paint (avoids a flash); the engine reconciles on load. -->
|
|
13
|
+
<script>
|
|
14
|
+
(function(){try{
|
|
15
|
+
var s=(JSON.parse(localStorage.getItem("pf2ePrimer.v1"))||{}).settings||{};
|
|
16
|
+
var mode=s.themeMode||"auto";
|
|
17
|
+
if(mode!=="light"&&mode!=="dark") mode=(window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches)?"dark":"light";
|
|
18
|
+
var root=document.documentElement; root.dataset.theme=mode;
|
|
19
|
+
if(s.custom&&s.custom.accent) root.style.setProperty("--accent",s.custom.accent);
|
|
20
|
+
}catch(e){}})();
|
|
21
|
+
</script>
|
|
22
|
+
<link rel="manifest" href="manifest.webmanifest">
|
|
23
|
+
<link rel="apple-touch-icon" href="icon.svg">
|
|
24
|
+
<title>PF2e Primer</title>
|
|
25
|
+
<style>
|
|
26
|
+
/* ============================================================
|
|
27
|
+
THEME TOKENS — base tokens flip with [data-theme]; --accent is
|
|
28
|
+
set by JS. Shades derive with color-mix() so one colour drives
|
|
29
|
+
the whole page. Same token names as the party tracker and the
|
|
30
|
+
spellbook, so the three tools look like a set.
|
|
31
|
+
============================================================ */
|
|
32
|
+
:root{
|
|
33
|
+
--accent:#3f7d8c;
|
|
34
|
+
--accent-ink:#ffffff;
|
|
35
|
+
--accent-dim:color-mix(in srgb, var(--accent) 55%, var(--line));
|
|
36
|
+
--accent-soft:color-mix(in srgb, var(--accent) 14%, transparent);
|
|
37
|
+
--accent-text:var(--accent);
|
|
38
|
+
--radius:14px;
|
|
39
|
+
}
|
|
40
|
+
:root, :root[data-theme="dark"]{
|
|
41
|
+
color-scheme:dark;
|
|
42
|
+
--bg:#14161b; --surface:#1d2027; --surface-2:#262a33;
|
|
43
|
+
--ink:#e8eaf0; --muted:#9aa1b0; --line:#2f3440;
|
|
44
|
+
--heal:#5bbf7e; --harm:#d9637a; --info:#6fa8dc; --warn:#d6a94a;
|
|
45
|
+
--accent-text:color-mix(in srgb, var(--accent) 78%, #ffffff);
|
|
46
|
+
--shadow:0 1px 2px rgba(0,0,0,.4), 0 4px 14px rgba(0,0,0,.22);
|
|
47
|
+
}
|
|
48
|
+
:root[data-theme="light"]{
|
|
49
|
+
color-scheme:light;
|
|
50
|
+
--bg:#f4f5f8; --surface:#ffffff; --surface-2:#eef0f4;
|
|
51
|
+
--ink:#1b1e24; --muted:#5d6470; --line:#dfe3ea;
|
|
52
|
+
--heal:#2f9e5b; --harm:#c43a57; --info:#2b6cb0; --warn:#9a6b12;
|
|
53
|
+
--accent-text:color-mix(in srgb, var(--accent) 72%, #000);
|
|
54
|
+
--shadow:0 1px 2px rgba(20,30,50,.08), 0 4px 14px rgba(20,30,50,.06);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent;}
|
|
58
|
+
html,body{margin:0;padding:0;background:var(--bg);}
|
|
59
|
+
body{
|
|
60
|
+
color:var(--ink);
|
|
61
|
+
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
|
62
|
+
font-size:16px; line-height:1.55; -webkit-text-size-adjust:100%;
|
|
63
|
+
}
|
|
64
|
+
h1,h2,h3{margin:0 0 .4em;line-height:1.2;letter-spacing:-.01em;}
|
|
65
|
+
h1{font-size:1.5rem;font-weight:800;}
|
|
66
|
+
h2{font-size:1.12rem;font-weight:800;}
|
|
67
|
+
h3{font-size:.98rem;font-weight:800;}
|
|
68
|
+
p{margin:.55em 0;}
|
|
69
|
+
b,strong{font-weight:700;}
|
|
70
|
+
i,em{font-style:italic;}
|
|
71
|
+
a{color:var(--accent-text);}
|
|
72
|
+
.hide{display:none !important;}
|
|
73
|
+
.center{text-align:center;}
|
|
74
|
+
.icn{display:inline-block;width:1em;height:1em;flex:0 0 auto;vertical-align:-.14em;}
|
|
75
|
+
button{font-family:inherit;cursor:pointer;}
|
|
76
|
+
button:disabled{cursor:default;}
|
|
77
|
+
button,.chip,input,select,textarea,details.ref,.pbtn{transition:background-color .15s ease,border-color .15s ease,color .15s ease;}
|
|
78
|
+
|
|
79
|
+
/* ---- Header ---- */
|
|
80
|
+
header.top{
|
|
81
|
+
background:linear-gradient(180deg, color-mix(in srgb, var(--accent) 10%, var(--surface)), var(--surface));
|
|
82
|
+
border-bottom:1px solid var(--line);
|
|
83
|
+
padding:12px 20px 0; position:relative; z-index:30;
|
|
84
|
+
}
|
|
85
|
+
.tophead{display:flex;align-items:center;justify-content:space-between;gap:10px;max-width:1000px;margin:0 auto;}
|
|
86
|
+
header.top h1{margin:0;display:flex;align-items:center;gap:9px;font-size:1.22rem;}
|
|
87
|
+
.apptitle-ic{display:inline-flex;color:var(--accent-text);width:1.3rem;height:1.3rem;}
|
|
88
|
+
header.top .sub{color:var(--muted);font-size:.82rem;margin:3px auto 0;max-width:1000px;}
|
|
89
|
+
.headbtns{display:flex;gap:8px;flex:0 0 auto;}
|
|
90
|
+
.menubtn{
|
|
91
|
+
background:var(--surface-2);border:1px solid var(--line);border-radius:11px;
|
|
92
|
+
height:40px;min-width:40px;padding:0 12px;color:var(--muted);
|
|
93
|
+
display:inline-flex;align-items:center;justify-content:center;gap:7px;font-weight:800;font-size:.85rem;
|
|
94
|
+
}
|
|
95
|
+
.menubtn .icn{width:1.15rem;height:1.15rem;}
|
|
96
|
+
.menubtn.on{background:var(--accent);color:var(--accent-ink);border-color:var(--accent);}
|
|
97
|
+
.progress{height:3px;background:var(--surface-2);max-width:1000px;margin:10px auto 0;border-radius:2px;overflow:hidden;}
|
|
98
|
+
.progress span{display:block;height:100%;background:var(--accent);transition:width .3s ease;}
|
|
99
|
+
|
|
100
|
+
/* ---- Tabs ---- */
|
|
101
|
+
nav.tabs{
|
|
102
|
+
position:sticky;top:0;z-index:20;background:var(--surface);border-bottom:1px solid var(--line);
|
|
103
|
+
display:flex;gap:2px;justify-content:center;padding:0 8px;overflow-x:auto;scrollbar-width:none;
|
|
104
|
+
}
|
|
105
|
+
nav.tabs::-webkit-scrollbar{display:none;}
|
|
106
|
+
nav.tabs button{
|
|
107
|
+
background:none;border:none;color:var(--muted);font-family:inherit;font-weight:700;font-size:.9rem;
|
|
108
|
+
padding:12px 14px;display:inline-flex;align-items:center;gap:7px;white-space:nowrap;
|
|
109
|
+
border-bottom:2px solid transparent;
|
|
110
|
+
}
|
|
111
|
+
nav.tabs button .icn{width:1.05rem;height:1.05rem;}
|
|
112
|
+
nav.tabs button:hover{color:var(--ink);}
|
|
113
|
+
nav.tabs button.on{color:var(--accent-text);border-bottom-color:var(--accent);}
|
|
114
|
+
|
|
115
|
+
/* ---- Layout ---- */
|
|
116
|
+
.wrap{max-width:1000px;margin:0 auto;padding:22px 20px 70px;}
|
|
117
|
+
.chaphead{margin-bottom:22px;}
|
|
118
|
+
.chaphead h1{display:flex;align-items:center;gap:11px;}
|
|
119
|
+
.chaphead h1 .icn{color:var(--accent-text);width:1.5rem;height:1.5rem;}
|
|
120
|
+
.lede{color:var(--muted);font-size:1.02rem;max-width:62ch;margin:0;}
|
|
121
|
+
.sec{margin:0 0 26px;}
|
|
122
|
+
.sec p{max-width:68ch;}
|
|
123
|
+
.meta{font-size:.9rem;color:var(--muted);}
|
|
124
|
+
.meta b{color:var(--ink);font-weight:600;}
|
|
125
|
+
.hintline{font-size:.88rem;color:var(--accent-text);display:flex;gap:7px;align-items:flex-start;margin-top:10px;}
|
|
126
|
+
.hintline .icn{margin-top:.25em;}
|
|
127
|
+
.empty{text-align:center;color:var(--muted);padding:34px 20px;}
|
|
128
|
+
|
|
129
|
+
/* ---- Callouts ---- */
|
|
130
|
+
.callout{
|
|
131
|
+
border:1px solid var(--line);border-left:4px solid var(--accent);border-radius:11px;
|
|
132
|
+
background:var(--surface);padding:14px 18px;margin:0 0 26px;box-shadow:var(--shadow);
|
|
133
|
+
}
|
|
134
|
+
.callout.warn{border-left-color:var(--warn);background:color-mix(in srgb,var(--warn) 7%,var(--surface));}
|
|
135
|
+
.callout.tip{border-left-color:var(--accent);}
|
|
136
|
+
.callout p{margin:.3em 0;max-width:66ch;}
|
|
137
|
+
|
|
138
|
+
/* ---- Bridge table ---- */
|
|
139
|
+
.bridge{border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;background:var(--surface);box-shadow:var(--shadow);}
|
|
140
|
+
.brow{display:grid;grid-template-columns:1fr 34px 1.35fr;gap:12px;align-items:center;padding:12px 16px;border-bottom:1px solid var(--line);}
|
|
141
|
+
.brow:last-child{border-bottom:none;}
|
|
142
|
+
.brow:nth-child(odd){background:color-mix(in srgb,var(--surface-2) 55%,var(--surface));}
|
|
143
|
+
.bfrom{color:var(--muted);font-size:.93rem;}
|
|
144
|
+
.barrow{display:flex;justify-content:center;color:var(--accent-text);}
|
|
145
|
+
.barrow .icn{width:1.05rem;height:1.05rem;}
|
|
146
|
+
.bto{font-size:.95rem;}
|
|
147
|
+
|
|
148
|
+
/* ---- Pillar → mode map ---- */
|
|
149
|
+
.pmap{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:12px;}
|
|
150
|
+
.pcol{
|
|
151
|
+
display:flex;flex-direction:column;gap:3px;text-align:left;
|
|
152
|
+
background:var(--surface);border:1px solid var(--line);border-top:3px solid var(--accent);
|
|
153
|
+
border-radius:12px;padding:14px 16px;color:var(--ink);box-shadow:var(--shadow);
|
|
154
|
+
}
|
|
155
|
+
.pcol:hover{border-color:var(--accent-dim);}
|
|
156
|
+
.pcol.wide{width:100%;border-top-color:var(--muted);}
|
|
157
|
+
.ppillar{font-size:.7rem;text-transform:uppercase;letter-spacing:.7px;color:var(--muted);font-weight:800;}
|
|
158
|
+
.pmode{font-size:1.02rem;font-weight:800;color:var(--accent-text);}
|
|
159
|
+
.pgrain{font-size:.8rem;color:var(--muted);font-variant-numeric:tabular-nums;}
|
|
160
|
+
.pnote{font-size:.88rem;margin-top:5px;}
|
|
161
|
+
|
|
162
|
+
/* ---- Concept cards ---- */
|
|
163
|
+
.cardgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(215px,1fr));gap:12px;}
|
|
164
|
+
.ccard{background:var(--surface);border:1px solid var(--line);border-radius:12px;padding:14px 16px;box-shadow:var(--shadow);}
|
|
165
|
+
.ccard h3{color:var(--accent-text);margin-bottom:.25em;}
|
|
166
|
+
.ccard p{font-size:.92rem;margin:0;}
|
|
167
|
+
|
|
168
|
+
/* ---- Steps ---- */
|
|
169
|
+
ol.steps{margin:0;padding-left:0;counter-reset:step;list-style:none;}
|
|
170
|
+
ol.steps li{
|
|
171
|
+
counter-increment:step;position:relative;padding:9px 0 9px 42px;border-bottom:1px dotted var(--line);font-size:.95rem;
|
|
172
|
+
}
|
|
173
|
+
ol.steps li:last-child{border-bottom:none;}
|
|
174
|
+
ol.steps li::before{
|
|
175
|
+
content:counter(step);position:absolute;left:0;top:8px;
|
|
176
|
+
width:26px;height:26px;border-radius:50%;background:var(--accent-soft);color:var(--accent-text);
|
|
177
|
+
border:1px solid var(--accent-dim);display:flex;align-items:center;justify-content:center;font-weight:800;font-size:.82rem;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/* ---- GM notes ---- */
|
|
181
|
+
.gmsec{display:none;}
|
|
182
|
+
body.gmon .gmsec{
|
|
183
|
+
display:block;background:color-mix(in srgb,var(--info) 8%,var(--surface));
|
|
184
|
+
border:1px dashed color-mix(in srgb,var(--info) 45%,var(--line));border-radius:var(--radius);
|
|
185
|
+
padding:14px 18px;margin-bottom:26px;
|
|
186
|
+
}
|
|
187
|
+
.gmhead{display:flex;align-items:center;gap:8px;font-weight:800;color:var(--info);font-size:.75rem;text-transform:uppercase;letter-spacing:.8px;margin-bottom:6px;}
|
|
188
|
+
.gmsec p{font-size:.93rem;margin:.4em 0;max-width:70ch;}
|
|
189
|
+
|
|
190
|
+
/* ---- Tool links ---- */
|
|
191
|
+
.toolcard{
|
|
192
|
+
display:flex;flex-direction:column;gap:3px;text-decoration:none;color:var(--ink);
|
|
193
|
+
background:var(--surface);border:1px solid var(--line);border-radius:12px;padding:13px 16px;margin:8px 0;box-shadow:var(--shadow);
|
|
194
|
+
}
|
|
195
|
+
.toolcard:hover{border-color:var(--accent-dim);}
|
|
196
|
+
.tname{font-weight:800;color:var(--accent-text);display:flex;align-items:center;gap:8px;}
|
|
197
|
+
.tnote{font-size:.88rem;color:var(--muted);}
|
|
198
|
+
|
|
199
|
+
/* ---- Reference disclosure ---- */
|
|
200
|
+
details.ref{border:1px solid var(--line);border-radius:11px;margin:8px 0;background:var(--surface);overflow:hidden;}
|
|
201
|
+
details.ref summary{padding:11px 14px;cursor:pointer;list-style:none;display:flex;align-items:center;justify-content:space-between;gap:10px;}
|
|
202
|
+
details.ref summary::-webkit-details-marker{display:none;}
|
|
203
|
+
details.ref[open]{border-color:var(--accent-dim);}
|
|
204
|
+
.refname{font-weight:700;}
|
|
205
|
+
.refkind{font-size:.68rem;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);background:var(--surface-2);border:1px solid var(--line);border-radius:6px;padding:1px 7px;white-space:nowrap;}
|
|
206
|
+
.refbody{padding:0 14px 14px;font-size:.92rem;line-height:1.6;}
|
|
207
|
+
|
|
208
|
+
/* ---- Search + chips ---- */
|
|
209
|
+
.searchbar{display:flex;align-items:center;gap:8px;background:var(--surface-2);border:1px solid var(--line);border-radius:10px;padding:0 12px;margin-bottom:12px;}
|
|
210
|
+
.searchbar .icn{color:var(--muted);width:1.1rem;height:1.1rem;}
|
|
211
|
+
.searchbar input{border:none;background:none;padding:11px 0;}
|
|
212
|
+
.searchbar input:focus{border:none;}
|
|
213
|
+
.chips{display:flex;flex-wrap:wrap;gap:6px;margin:10px 0;}
|
|
214
|
+
.chip{
|
|
215
|
+
background:var(--surface);border:1px solid var(--line);color:var(--muted);
|
|
216
|
+
border-radius:8px;padding:6px 11px;font-size:.84rem;font-weight:700;
|
|
217
|
+
}
|
|
218
|
+
.chip.on{background:var(--accent);color:var(--accent-ink);border-color:var(--accent);}
|
|
219
|
+
|
|
220
|
+
/* ---- Quick card ---- */
|
|
221
|
+
.qcard{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px;}
|
|
222
|
+
.qbox{background:var(--surface);border:1px solid var(--line);border-radius:12px;padding:14px 16px;box-shadow:var(--shadow);}
|
|
223
|
+
.qbox h3{color:var(--accent-text);text-transform:uppercase;font-size:.72rem;letter-spacing:.7px;}
|
|
224
|
+
.qbox .big{font-size:1.12rem;font-weight:800;margin:.2em 0 .5em;font-variant-numeric:tabular-nums;}
|
|
225
|
+
.qbox ul{margin:0;padding-left:18px;font-size:.89rem;}
|
|
226
|
+
.qbox li{margin:.25em 0;}
|
|
227
|
+
|
|
228
|
+
/* ---- Glossary ---- */
|
|
229
|
+
dl.gloss{margin:0;display:grid;grid-template-columns:minmax(140px,190px) 1fr;gap:0;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;background:var(--surface);}
|
|
230
|
+
dl.gloss dt{font-weight:800;color:var(--accent-text);padding:11px 14px;border-bottom:1px solid var(--line);font-size:.92rem;}
|
|
231
|
+
dl.gloss dd{margin:0;padding:11px 14px;border-bottom:1px solid var(--line);font-size:.92rem;}
|
|
232
|
+
dl.gloss dt:nth-last-of-type(1),dl.gloss dd:nth-last-of-type(1){border-bottom:none;}
|
|
233
|
+
|
|
234
|
+
/* ============================================================
|
|
235
|
+
DEMOS
|
|
236
|
+
============================================================ */
|
|
237
|
+
.demo{
|
|
238
|
+
background:var(--surface);border:1px solid var(--accent-dim);border-radius:var(--radius);
|
|
239
|
+
padding:16px 18px;box-shadow:var(--shadow);
|
|
240
|
+
}
|
|
241
|
+
.demohead{display:flex;align-items:center;gap:9px;color:var(--accent-text);}
|
|
242
|
+
.demohead h2{margin:0;color:var(--ink);}
|
|
243
|
+
.demohead .icn{width:1.2rem;height:1.2rem;}
|
|
244
|
+
.demobody{margin-top:12px;}
|
|
245
|
+
.controls{display:flex;flex-wrap:wrap;gap:12px;align-items:flex-end;margin-bottom:12px;}
|
|
246
|
+
label.field{display:block;margin:0;}
|
|
247
|
+
label.field.inline{flex:1;min-width:180px;}
|
|
248
|
+
label.field.narrow{flex:0 0 120px;min-width:120px;}
|
|
249
|
+
label.field .name{display:block;font-weight:700;margin-bottom:5px;font-size:.8rem;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);}
|
|
250
|
+
input,select,textarea{
|
|
251
|
+
width:100%;padding:10px;font-size:.98rem;border-radius:10px;font-family:inherit;
|
|
252
|
+
background:var(--surface-2);color:var(--ink);border:1px solid var(--line);appearance:none;-webkit-appearance:none;accent-color:var(--accent);
|
|
253
|
+
}
|
|
254
|
+
input:focus,select:focus{outline:none;border-color:var(--accent);}
|
|
255
|
+
select{background-image:linear-gradient(45deg,transparent 50%,var(--muted) 50%),linear-gradient(135deg,var(--muted) 50%,transparent 50%);
|
|
256
|
+
background-position:calc(100% - 20px) center,calc(100% - 14px) center;background-size:6px 6px,6px 6px;background-repeat:no-repeat;padding-right:38px;}
|
|
257
|
+
input[type="color"]{padding:4px;height:42px;cursor:pointer;}
|
|
258
|
+
input[type="number"]{-moz-appearance:textfield;}
|
|
259
|
+
.row{display:flex;gap:12px;}
|
|
260
|
+
.row.tight{gap:8px;align-items:center;flex-wrap:wrap;}
|
|
261
|
+
.btn{
|
|
262
|
+
display:flex;align-items:center;justify-content:center;gap:8px;width:100%;padding:12px;border-radius:11px;border:none;
|
|
263
|
+
background:var(--accent);color:var(--accent-ink);font-weight:800;font-size:1rem;margin:12px 0;box-shadow:var(--shadow);
|
|
264
|
+
}
|
|
265
|
+
.btn.secondary{background:var(--surface-2);color:var(--accent-text);border:1px solid var(--accent-dim);box-shadow:none;}
|
|
266
|
+
.btn.sm{width:auto;padding:9px 15px;font-size:.9rem;margin:0;border-radius:9px;}
|
|
267
|
+
.btn:disabled{opacity:.45;}
|
|
268
|
+
.btn:active{transform:translateY(1px);}
|
|
269
|
+
.chapfoot{border-top:1px solid var(--line);padding-top:18px;margin-top:34px;}
|
|
270
|
+
.chapfoot .btn{max-width:420px;margin:0 auto 8px;}
|
|
271
|
+
|
|
272
|
+
/* ---- Foe / NPC card ---- */
|
|
273
|
+
.foecard{
|
|
274
|
+
flex:1;min-width:230px;display:flex;flex-direction:column;gap:2px;
|
|
275
|
+
background:var(--surface-2);border:1px solid var(--line);border-radius:10px;padding:9px 13px;
|
|
276
|
+
}
|
|
277
|
+
.foecard .meta{font-size:.8rem;}
|
|
278
|
+
|
|
279
|
+
/* ---- Result card ---- */
|
|
280
|
+
.out{margin-top:12px;}
|
|
281
|
+
.resultcard{
|
|
282
|
+
background:var(--surface-2);border:1px solid var(--line);border-left:4px solid var(--line);
|
|
283
|
+
border-radius:11px;padding:14px 16px;
|
|
284
|
+
}
|
|
285
|
+
.resultcard.csucc,.resultcard.succ{border-left-color:var(--heal);}
|
|
286
|
+
.resultcard.fail{border-left-color:var(--warn);}
|
|
287
|
+
.resultcard.cfail{border-left-color:var(--harm);}
|
|
288
|
+
.rline{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap;}
|
|
289
|
+
.rmath{color:var(--muted);font-size:.9rem;font-variant-numeric:tabular-nums;}
|
|
290
|
+
.rtotal{font-size:1.7rem;font-weight:800;font-variant-numeric:tabular-nums;}
|
|
291
|
+
.rdeg{margin:8px 0;}
|
|
292
|
+
.rwhy{font-size:.93rem;margin:.35em 0;}
|
|
293
|
+
.nat{font-weight:800;padding:0 4px;border-radius:5px;}
|
|
294
|
+
.nat.nat20{color:var(--heal);background:color-mix(in srgb,var(--heal) 20%,transparent);}
|
|
295
|
+
.nat.nat1{color:var(--harm);background:color-mix(in srgb,var(--harm) 20%,transparent);}
|
|
296
|
+
.deg{
|
|
297
|
+
display:inline-block;font-size:.72rem;font-weight:800;text-transform:uppercase;letter-spacing:.4px;
|
|
298
|
+
padding:3px 9px;border-radius:6px;
|
|
299
|
+
}
|
|
300
|
+
.deg.csucc{background:color-mix(in srgb,var(--heal) 24%,var(--surface));color:var(--heal);}
|
|
301
|
+
.deg.succ{background:color-mix(in srgb,var(--heal) 13%,var(--surface));color:var(--heal);}
|
|
302
|
+
.deg.fail{background:color-mix(in srgb,var(--warn) 17%,var(--surface));color:var(--warn);}
|
|
303
|
+
.deg.cfail{background:color-mix(in srgb,var(--harm) 19%,var(--surface));color:var(--harm);}
|
|
304
|
+
|
|
305
|
+
/* ---- Distribution bars ---- */
|
|
306
|
+
.dist{display:flex;flex-direction:column;gap:5px;margin:10px 0;}
|
|
307
|
+
.distrow{display:grid;grid-template-columns:130px 1fr 28px;gap:10px;align-items:center;font-size:.85rem;}
|
|
308
|
+
.dlbl{color:var(--muted);}
|
|
309
|
+
.dbar{height:12px;background:var(--surface);border:1px solid var(--line);border-radius:7px;overflow:hidden;}
|
|
310
|
+
.dfill{display:block;height:100%;}
|
|
311
|
+
.dfill.csucc{background:var(--heal);}
|
|
312
|
+
.dfill.succ{background:color-mix(in srgb,var(--heal) 60%,var(--surface));}
|
|
313
|
+
.dfill.fail{background:var(--warn);}
|
|
314
|
+
.dfill.cfail{background:var(--harm);}
|
|
315
|
+
.dnum{text-align:right;font-weight:800;font-variant-numeric:tabular-nums;}
|
|
316
|
+
|
|
317
|
+
/* ---- Action pips & palette ---- */
|
|
318
|
+
.pips{display:flex;gap:8px;margin:4px 0 8px;flex-wrap:wrap;}
|
|
319
|
+
.pip{
|
|
320
|
+
flex:1;min-width:120px;min-height:52px;border:1px dashed var(--line);border-radius:11px;
|
|
321
|
+
background:var(--surface-2);display:flex;align-items:center;justify-content:center;padding:6px 10px;text-align:center;
|
|
322
|
+
}
|
|
323
|
+
.pip.used{border-style:solid;border-color:var(--accent);background:var(--accent-soft);}
|
|
324
|
+
.pip.react{flex:0 0 110px;border-color:color-mix(in srgb,var(--info) 40%,var(--line));}
|
|
325
|
+
.pipname{font-weight:700;font-size:.85rem;}
|
|
326
|
+
.pipname.empty{color:var(--muted);font-weight:600;}
|
|
327
|
+
.palette{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:8px;margin:10px 0;}
|
|
328
|
+
.pbtn{
|
|
329
|
+
display:flex;flex-direction:column;align-items:flex-start;gap:2px;text-align:left;
|
|
330
|
+
background:var(--surface-2);border:1px solid var(--line);border-left:3px solid var(--line);
|
|
331
|
+
border-radius:10px;padding:9px 12px;color:var(--ink);
|
|
332
|
+
}
|
|
333
|
+
.pbtn:hover:not(:disabled){border-color:var(--accent-dim);border-left-color:var(--accent);}
|
|
334
|
+
.pbtn.dis,.pbtn:disabled{opacity:.4;}
|
|
335
|
+
.pbtn.k-attack{border-left-color:var(--harm);}
|
|
336
|
+
.pbtn.k-spell{border-left-color:var(--info);}
|
|
337
|
+
.pbtn.k-skill{border-left-color:var(--accent);}
|
|
338
|
+
.pbtn.k-defend{border-left-color:var(--heal);}
|
|
339
|
+
.pbtn.k-move{border-left-color:var(--muted);}
|
|
340
|
+
.pcost{font-size:.72rem;color:var(--accent-text);letter-spacing:1px;}
|
|
341
|
+
.plabel{font-weight:700;font-size:.9rem;line-height:1.25;}
|
|
342
|
+
.pmap{font-size:.75rem;color:var(--muted);font-variant-numeric:tabular-nums;}
|
|
343
|
+
.pblock{font-size:.72rem;color:var(--harm);font-weight:700;}
|
|
344
|
+
|
|
345
|
+
/* ---- Turn / event log ---- */
|
|
346
|
+
ol.turnlog{list-style:none;margin:12px 0 0;padding:0;display:flex;flex-direction:column;gap:8px;}
|
|
347
|
+
.tstep{
|
|
348
|
+
background:var(--surface-2);border:1px solid var(--line);border-left:4px solid var(--line);
|
|
349
|
+
border-radius:10px;padding:10px 13px;
|
|
350
|
+
}
|
|
351
|
+
.tstep.csucc,.tstep.succ{border-left-color:var(--heal);}
|
|
352
|
+
.tstep.fail{border-left-color:var(--warn);}
|
|
353
|
+
.tstep.cfail{border-left-color:var(--harm);}
|
|
354
|
+
.tshead{display:flex;align-items:center;gap:9px;font-weight:800;flex-wrap:wrap;margin-bottom:3px;}
|
|
355
|
+
.tsmath{font-size:.88rem;color:var(--muted);font-variant-numeric:tabular-nums;}
|
|
356
|
+
.tswhy{font-size:.85rem;color:var(--muted);font-style:italic;margin-top:2px;}
|
|
357
|
+
.tsdetail{font-size:.92rem;margin-top:5px;}
|
|
358
|
+
.turnsum{
|
|
359
|
+
margin-top:12px;padding:12px 14px;border-radius:10px;
|
|
360
|
+
background:var(--accent-soft);border:1px solid var(--accent-dim);font-size:.95rem;
|
|
361
|
+
}
|
|
362
|
+
.turnsum .meta{display:block;margin-top:4px;}
|
|
363
|
+
|
|
364
|
+
/* ---- Dying tracker ---- */
|
|
365
|
+
.dyhead{display:flex;align-items:center;gap:12px;flex-wrap:wrap;}
|
|
366
|
+
.hpbar{flex:1;min-width:140px;max-width:280px;height:10px;border-radius:6px;background:var(--surface-2);border:1px solid var(--line);overflow:hidden;}
|
|
367
|
+
.hpfill{display:block;height:100%;background:var(--heal);transition:width .25s ease;}
|
|
368
|
+
.hpfill.mid{background:var(--warn);}
|
|
369
|
+
.hpfill.low{background:var(--harm);}
|
|
370
|
+
.hpnum{font-weight:800;font-variant-numeric:tabular-nums;}
|
|
371
|
+
.dychips{display:flex;gap:7px;flex-wrap:wrap;margin:10px 0;}
|
|
372
|
+
.statchip{
|
|
373
|
+
font-size:.78rem;font-weight:800;border-radius:7px;padding:3px 9px;
|
|
374
|
+
background:var(--surface-2);border:1px solid var(--line);color:var(--muted);
|
|
375
|
+
}
|
|
376
|
+
.statchip.bad{background:color-mix(in srgb,var(--harm) 17%,var(--surface));border-color:color-mix(in srgb,var(--harm) 40%,var(--line));color:var(--harm);}
|
|
377
|
+
.statchip.warn{background:color-mix(in srgb,var(--warn) 15%,var(--surface));border-color:color-mix(in srgb,var(--warn) 40%,var(--line));color:var(--warn);}
|
|
378
|
+
.statchip.good{background:color-mix(in srgb,var(--heal) 14%,var(--surface));border-color:color-mix(in srgb,var(--heal) 40%,var(--line));color:var(--heal);}
|
|
379
|
+
|
|
380
|
+
/* ---- Exploration / downtime board ---- */
|
|
381
|
+
.exphead,.exprow{display:grid;grid-template-columns:76px 110px 1.1fr 110px 1.5fr;gap:12px;align-items:center;}
|
|
382
|
+
.exphead{font-size:.7rem;text-transform:uppercase;letter-spacing:.6px;color:var(--muted);font-weight:800;padding:0 0 8px;border-bottom:1px solid var(--line);}
|
|
383
|
+
.exprow{padding:10px 0;border-bottom:1px solid var(--line);}
|
|
384
|
+
.exprow.down{grid-template-columns:110px 1.1fr 150px 1.5fr;}
|
|
385
|
+
.expord{display:flex;align-items:center;gap:4px;}
|
|
386
|
+
.ordbtn{background:var(--surface-2);border:1px solid var(--line);color:var(--muted);border-radius:7px;padding:4px;display:inline-flex;}
|
|
387
|
+
.ordbtn:disabled{opacity:.35;}
|
|
388
|
+
.ordbtn .icn{width:.95rem;height:.95rem;}
|
|
389
|
+
.ordnum{font-weight:800;color:var(--accent-text);min-width:14px;text-align:center;}
|
|
390
|
+
.expwho{display:flex;flex-direction:column;}
|
|
391
|
+
.expwho .meta{font-size:.78rem;}
|
|
392
|
+
.expsel select{padding:8px;font-size:.9rem;}
|
|
393
|
+
.govmod{font-size:1.05rem;color:var(--accent-text);}
|
|
394
|
+
.expnote{font-size:.86rem;color:var(--muted);}
|
|
395
|
+
.secretchip{
|
|
396
|
+
display:inline-flex;align-items:center;gap:4px;font-size:.72rem;font-weight:700;white-space:nowrap;
|
|
397
|
+
background:color-mix(in srgb,var(--info) 12%,var(--surface));border:1px solid color-mix(in srgb,var(--info) 40%,var(--line));
|
|
398
|
+
color:var(--info);border-radius:6px;padding:1px 7px;
|
|
399
|
+
}
|
|
400
|
+
.secretchip .icn{width:.85rem;height:.85rem;}
|
|
401
|
+
ul.notelist{margin:14px 0 0;padding-left:18px;font-size:.9rem;}
|
|
402
|
+
ul.notelist li{margin:.35em 0;}
|
|
403
|
+
|
|
404
|
+
/* ---- Attitude ladder ---- */
|
|
405
|
+
.ladder{display:flex;flex-direction:column;gap:4px;margin:12px 0;}
|
|
406
|
+
.rung{
|
|
407
|
+
display:flex;align-items:center;justify-content:space-between;gap:10px;
|
|
408
|
+
border:1px solid var(--line);border-radius:9px;padding:8px 13px;background:var(--surface-2);color:var(--muted);font-weight:700;font-size:.9rem;
|
|
409
|
+
}
|
|
410
|
+
.rung.below{opacity:.65;}
|
|
411
|
+
.rung.on{background:var(--accent-soft);border-color:var(--accent);color:var(--accent-text);}
|
|
412
|
+
.rhere{font-size:.78rem;font-weight:800;}
|
|
413
|
+
|
|
414
|
+
/* ---- Menu ---- */
|
|
415
|
+
.seg{display:flex;gap:8px;margin:6px 0 12px;}
|
|
416
|
+
.seg button{flex:1;padding:11px;border-radius:10px;border:1px solid var(--line);background:var(--surface-2);color:var(--ink);font-weight:700;}
|
|
417
|
+
.seg button.on{background:var(--accent);color:var(--accent-ink);border-color:var(--accent);}
|
|
418
|
+
.swatchrow{display:flex;align-items:center;gap:10px;margin:8px 0;}
|
|
419
|
+
.swatchrow label{flex:1;font-weight:600;font-size:.92rem;}
|
|
420
|
+
.swatchrow input[type="color"]{flex:0 0 54px;width:54px;}
|
|
421
|
+
|
|
422
|
+
/* ---- Toast ---- */
|
|
423
|
+
.toast{
|
|
424
|
+
position:fixed;bottom:28px;left:50%;transform:translateX(-50%);
|
|
425
|
+
background:var(--accent);color:var(--accent-ink);font-weight:800;padding:12px 20px;border-radius:30px;
|
|
426
|
+
z-index:50;opacity:0;transition:opacity .25s;pointer-events:none;box-shadow:0 4px 16px rgba(0,0,0,.35);max-width:90vw;text-align:center;
|
|
427
|
+
}
|
|
428
|
+
.toast.show{opacity:1;}
|
|
429
|
+
|
|
430
|
+
/* ============================================================
|
|
431
|
+
RESPONSIVE
|
|
432
|
+
============================================================ */
|
|
433
|
+
@media (max-width:860px){
|
|
434
|
+
.pmap{grid-template-columns:1fr;}
|
|
435
|
+
.brow{grid-template-columns:1fr;gap:4px;padding:12px 14px;}
|
|
436
|
+
.barrow{justify-content:flex-start;transform:rotate(90deg);width:1.05rem;}
|
|
437
|
+
dl.gloss{grid-template-columns:1fr;}
|
|
438
|
+
dl.gloss dt{border-bottom:none;padding-bottom:0;}
|
|
439
|
+
.exphead{display:none;}
|
|
440
|
+
.exprow,.exprow.down{grid-template-columns:1fr;gap:7px;padding:14px 0;}
|
|
441
|
+
.expord{order:-1;}
|
|
442
|
+
.distrow{grid-template-columns:112px 1fr 26px;}
|
|
443
|
+
}
|
|
444
|
+
@media (max-width:560px){
|
|
445
|
+
.wrap{padding:16px 13px 70px;}
|
|
446
|
+
header.top{padding:10px 13px 0;}
|
|
447
|
+
h1{font-size:1.3rem;}
|
|
448
|
+
.palette{grid-template-columns:1fr 1fr;}
|
|
449
|
+
.pip{min-width:0;}
|
|
450
|
+
nav.tabs{justify-content:flex-start;}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/* ---- Print: the quick card is the only thing worth paper ---- */
|
|
454
|
+
@media print{
|
|
455
|
+
header.top,nav.tabs,.demo,.chapfoot,.toast,.gmsec{display:none !important;}
|
|
456
|
+
body{background:#fff;color:#000;}
|
|
457
|
+
.qbox{break-inside:avoid;border:1px solid #999;}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/* ---- Pregen cards ---- */
|
|
461
|
+
.pcard h3{color:var(--ink);}
|
|
462
|
+
.pcard .meta{font-size:.85rem;}
|
|
463
|
+
.statrow{display:flex;gap:8px;flex-wrap:wrap;margin:10px 0;}
|
|
464
|
+
.statbox{
|
|
465
|
+
flex:1;min-width:62px;text-align:center;background:var(--surface-2);
|
|
466
|
+
border:1px solid var(--line);border-radius:9px;padding:6px 5px;
|
|
467
|
+
}
|
|
468
|
+
.statbox b{display:block;font-size:1.05rem;color:var(--accent-text);font-variant-numeric:tabular-nums;}
|
|
469
|
+
.statbox span{display:block;font-size:.62rem;text-transform:uppercase;letter-spacing:.5px;color:var(--muted);}
|
|
470
|
+
.refnote{font-weight:400;color:var(--muted);font-size:.85rem;margin-left:6px;}
|
|
471
|
+
|
|
472
|
+
</style>
|
|
473
|
+
</head>
|
|
474
|
+
<body>
|
|
475
|
+
|
|
476
|
+
<header class="top">
|
|
477
|
+
<div class="tophead">
|
|
478
|
+
<h1><span class="apptitle-ic" id="titleIcon"></span> PF2e Primer</h1>
|
|
479
|
+
<div class="headbtns">
|
|
480
|
+
<button class="menubtn" id="gmBtn" onclick="toggleGmMode()" title="GM mode" aria-label="Toggle GM mode">
|
|
481
|
+
<svg class="icn" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 12S6 6 12 6s9.5 6 9.5 6-3.5 6-9.5 6-9.5-6-9.5-6z"/><circle cx="12" cy="12" r="2.4"/></svg>
|
|
482
|
+
GM
|
|
483
|
+
</button>
|
|
484
|
+
<button class="menubtn" onclick="openMenu()" title="Settings" aria-label="Settings">
|
|
485
|
+
<svg class="icn" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="8" r="3.4"/><path d="M5.5 20a6.5 6.5 0 0 1 13 0"/></svg>
|
|
486
|
+
</button>
|
|
487
|
+
</div>
|
|
488
|
+
</div>
|
|
489
|
+
<div class="sub" id="headSub"></div>
|
|
490
|
+
<div class="progress" id="progressBar"><span style="width:0%"></span></div>
|
|
491
|
+
</header>
|
|
492
|
+
|
|
493
|
+
<nav class="tabs" id="tabs"></nav>
|
|
494
|
+
|
|
495
|
+
<main class="wrap" id="views"></main>
|
|
496
|
+
|
|
497
|
+
<div class="toast" id="toast"></div>
|
|
498
|
+
|
|
499
|
+
<script>
|
|
500
|
+
/* data/reference.generated.js — AUTO-GENERATED. Do not hand-edit.
|
|
501
|
+
Rebuild with: npm run build:ref
|
|
502
|
+
Source: foundryvtt/pf2e condition + action data (Paizo content, OGL/ORC). */
|
|
503
|
+
const GENERATED_REF_META = {"generated": "2026-09-10", "source": "foundryvtt/pf2e", "sourceCommit": "8f586ff", "conditions": 43, "actions": 101, "sources": [{"title": "Pathfinder Player Core", "license": "ORC", "count": 132}, {"title": "Pathfinder Treasure Vault (Remastered)", "license": "ORC", "count": 3}, {"title": "Pathfinder GM Core", "license": "ORC", "count": 3}, {"title": "Pathfinder Player Core 2", "license": "ORC", "count": 2}, {"title": "Pathfinder Secrets of Magic", "license": "OGL", "count": 2}, {"title": "Pathfinder Gamemastery Guide", "license": "OGL", "count": 1}, {"title": "Pathfinder Dark Archive (Remastered)", "license": "ORC", "count": 1}]};
|
|
504
|
+
const GENERATED_CONDITIONS = [{"slug": "blinded", "name": "Blinded", "description": "You can't see. All normal terrain is difficult terrain to you. You can't detect anything using vision. You automatically critically fail Perception checks that require you to be able to see, and if vision is your only precise sense, you take a –4 status penalty to Perception checks. You are immune to visual effects. Blinded overrides Dazzled.", "valued": false, "group": "senses"}, {"slug": "broken", "name": "Broken", "description": "Broken is a condition that affects only objects. An object is broken when damage has reduced its Hit Points to equal or less than its Broken Threshold. A broken object can't be used for its normal function, nor does it grant bonuses—with the exception of armor. Broken armor still grants its item bonus to AC, but it also imparts a status penalty to AC depending on its category: –1 for broken light armor, –2 for broken medium armor, or –3 for broken heavy armor.\n\nA broken item still imposes penalties and limitations normally incurred by carrying, holding, or wearing it. For example, broken armor would still impose its Dexterity modifier cap, check penalty, and so forth. If an effect makes an item broken automatically and the item has more HP than its Broken Threshold, that effect also reduces the item's current HP to the Broken Threshold.", "valued": false, "group": null}, {"slug": "clumsy", "name": "Clumsy", "description": "Your movements become clumsy and inexact. Clumsy always includes a value. You take a status penalty equal to the condition value to Dexterity-based rolls and DCs, including AC, Reflex saves, ranged attack rolls, and skill checks using Acrobatics, Stealth, and Thievery.", "valued": true, "group": "abilities"}, {"slug": "concealed", "name": "Concealed", "description": "You are difficult for one or more creatures to see due to thick fog or some other obscuring feature. You can be concealed to some creatures but not others. While concealed, you can still be Observed, but you're tougher to target. A creature that you're concealed from must succeed at a Flat check when targeting you with an attack, spell, or other effect. If the check fails, you aren't affected. Area effects aren't subject to this flat check.", "valued": false, "group": "senses"}, {"slug": "confused", "name": "Confused", "description": "You don't have your wits about you, and you attack wildly. You are Off Guard, you don't treat anyone as your ally (though they might still treat you as theirs), and you can't Delay, Ready, or use reactions.\n\nYou use all your actions to Strike or cast offensive cantrips, though the GM can have you use other actions to facilitate attack, such as draw a weapon, move so target is in reach, and so forth. Your targets are determined randomly by the GM. If you have no other viable targets, you target yourself, automatically hitting but not scoring a critical hit. If it's impossible for you to attack or cast spells, you babble incoherently, wasting your actions.\n\nEach time you take damage from an attack or spell, you can attempt a Flat check to recover from your confusion and end the condition.", "valued": false, "group": null}, {"slug": "controlled", "name": "Controlled", "description": "You have been commanded, magically dominated, or otherwise had your will subverted. The controller dictates how you act and can make you use any of your actions, including attacks, reactions, or even Delay. The controller usually doesn't have to spend their own actions when controlling you.", "valued": false, "group": null}, {"slug": "cursebound", "name": "Cursebound", "description": "Your oracular curse is constricting around you as you receive divine punishment after drawing too deeply on your mystery's powers. Cursebound is a condition that affects only creatures with an oracular curse, and cursebound always includes a value. Your specific oracular curse imposes unique negative effects depending on your cursebound value. You can remove the cursebound condition only by Refocusing.", "valued": true, "group": "abilities"}, {"slug": "dazzled", "name": "Dazzled", "description": "Your eyes are overstimulated or your vision is swimming. If vision is your only precise sense, all creatures and objects are Concealed from you.", "valued": false, "group": "senses"}, {"slug": "deafened", "name": "Deafened", "description": "You can't hear. You automatically critically fail Perception checks that require you to be able to hear. You take a –2 status penalty to Perception checks for initiative and checks that involve sound but also rely on other senses. If you perform an action that has the auditory trait, you must succeed at a Flat check or the action is lost; attempt the check after spending the action but before any effects are applied. You are immune to auditory effects while deafened.", "valued": false, "group": "senses"}, {"slug": "doomed", "name": "Doomed", "description": "Your soul has been gripped by a powerful force that calls you closer to death. Doomed always includes a value. The Dying value at which you die is reduced by your doomed value. If your maximum dying value is reduced to 0, you instantly die. When you die, you're no longer doomed.\n\nYour doomed value decreases by 1 each time you get a full night's rest.", "valued": true, "group": "death"}, {"slug": "drained", "name": "Drained", "description": "Your health and vitality have been depleted as you've lost blood, life force, or some other essence. Drained always includes a value. You take a status penalty equal to your drained value on Constitution-based rolls and DCs, such as Fortitude saves. You also lose a number of Hit Points equal to your level (minimum 1) times the drained value, and your maximum Hit Points are reduced by the same amount. For example, if you become drained 3 and you're a 3rd-level character, you lose 9 Hit Points and reduce your maximum Hit Points by 9. Losing these Hit Points doesn't count as taking damage.\n\nEach time you get a full night's rest, your drained value decreases by 1. This increases your maximum Hit Points, but you don't immediately recover the lost Hit Points.", "valued": true, "group": "abilities"}, {"slug": "dying", "name": "Dying", "description": "You are bleeding out or otherwise at death's door. While you have this condition, you are Unconscious. Dying always includes a value, and if it ever reaches dying 4, you die. When you're dying, you must attempt a recovery check at the start of your turn each round to determine whether you get better or worse. Your dying condition increases by 1 if you take damage while dying, or by 2 if you take damage from an enemy's critical hit or a critical failure on your save.\n\nIf you lose the dying condition by succeeding at a recovery check and are still at 0 Hit Points, you remain unconscious, but you can wake up as described in that condition. You lose the dying condition automatically and wake up if you ever have 1 Hit Point or more. Any time you lose the dying condition, you gain the Wounded 1 condition, or increase your wounded condition value by 1 if you already have that condition.", "valued": true, "group": "death"}, {"slug": "encumbered", "name": "Encumbered", "description": "You are carrying more weight than you can manage. While you're encumbered, you're Clumsy 1 and take a 10-foot penalty to all your Speeds. As with all penalties to your Speed, this can't reduce your Speed below 5 feet.", "valued": false, "group": null}, {"slug": "enfeebled", "name": "Enfeebled", "description": "You're physically weakened. Enfeebled always includes a value. When you are enfeebled, you take a status penalty equal to the condition value to Strength-based rolls and DCs, including Strength-based melee attack rolls, Strength-based damage rolls, and Athletics checks.", "valued": true, "group": "abilities"}, {"slug": "fascinated", "name": "Fascinated", "description": "You're compelled to focus your attention on something, distracting you from whatever else is going on around you. You take a –2 status penalty to Perception and skill checks, and you can't use concentrate actions unless they (or their intended consequences) are related to the subject of your fascination, as determined by the GM. For instance, you might be able to Seek and Recall Knowledge about the subject, but you likely couldn't cast a spell targeting a different creature. This condition ends if a creature uses hostile actions against you or any of your allies.", "valued": false, "group": null}, {"slug": "fatigued", "name": "Fatigued", "description": "You're tired and can't summon much energy. You take a –1 status penalty to AC and saving throws. You can't use exploration activities performed while traveling.\n\nYou recover from fatigue after a full night's rest.", "valued": false, "group": null}, {"slug": "fleeing", "name": "Fleeing", "description": "You're forced to run away due to fear or some other compulsion. On your turn, you must spend each of your actions trying to escape the source of the fleeing condition as expediently as possible (such as by using move actions to flee, or opening doors barring your escape). The source is usually the effect or creature that gave you the condition, though some effects might define something else as the source. You can't Delay or Ready while fleeing.", "valued": false, "group": null}, {"slug": "friendly", "name": "Friendly", "description": "This condition reflects a creature's disposition toward a particular character, and only supernatural effects (like a spell) can impose this condition on a PC. A creature that is friendly to a character likes that character. It is likely to agree to Requests from that character as long as they are simple, safe, and don't cost too much to fulfill. If the character (or one of their allies) uses hostile actions against the creature, the creature gains a worse attitude condition depending on the severity of the hostile action, as determined by the GM.", "valued": false, "group": "attitudes"}, {"slug": "frightened", "name": "Frightened", "description": "You're gripped by fear and struggle to control your nerves. The frightened condition always includes a value. You take a status penalty equal to this value to all your checks and DCs. Unless specified otherwise, at the end of each of your turns, the value of your frightened condition decreases by 1.", "valued": true, "group": null}, {"slug": "grabbed", "name": "Grabbed", "description": "You're held in place by another creature, giving you the Off Guard and Immobilized conditions. If you attempt a manipulate action while grabbed, you must succeed at a Flat check or it is lost; roll the check after spending the action, but before any effects are applied.", "valued": false, "group": null}, {"slug": "helpful", "name": "Helpful", "description": "This condition reflects a creature's disposition toward a particular character, and only supernatural effects (like a spell) can impose this condition on a PC. A creature that is helpful to a character wishes to actively aid that character. It will accept reasonable Requests from that character, as long as such requests aren't at the expense of the helpful creature's goals or quality of life. If the character (or one of their allies) uses a hostile action against the creature, the creature gains a worse attitude condition depending on the severity of the hostile action, as determined by the GM.", "valued": false, "group": "attitudes"}, {"slug": "hidden", "name": "Hidden", "description": "While you're hidden from a creature, that creature knows the space you're in but can't tell precisely where you are. You typically become hidden by using Stealth to Hide. When Seeking a creature using only imprecise senses, it remains hidden, rather than Observed. A creature you're hidden from is Off Guard to you, and it must succeed at a Flat check when targeting you with an attack, spell, or other effect or it fails to affect you. Area effects aren't subject to this flat check.\n\nA creature might be able to use the seek action to try to observe you.", "valued": false, "group": "detection"}, {"slug": "hostile", "name": "Hostile", "description": "This condition reflects a creature's disposition toward a particular character, and only supernatural effects (like a spell) can impose on a PC. A creature hostile to a character actively seeks to harm that character. It doesn't necessarily attack, but it won't accept Requests from the character.", "valued": false, "group": "attitudes"}, {"slug": "immobilized", "name": "Immobilized", "description": "You are incapable of movement. You can't use any actions that have the move trait. If you're immobilized by something holding you in place and an external force would move you out of your space, the force must succeed at a check against either the DC of the effect holding you in place or the relevant defense (usually Fortitude DC) of the monster holding you in place.", "valued": false, "group": null}, {"slug": "indifferent", "name": "Indifferent", "description": "This condition reflects a creature's disposition toward a particular character, and only supernatural effects (like a spell) can impose this condition on a PC. A creature that is indifferent to a character doesn't really care one way or the other about that character. Assume a creature's attitude to a given character is indifferent unless specified otherwise.", "valued": false, "group": "attitudes"}, {"slug": "invisible", "name": "Invisible", "description": "You can't be seen. You're Undetected to everyone. Creatures can Seek to detect you; if a creature succeeds at its Perception check against your Stealth DC, you become Hidden to that creature until you Sneak to become undetected again. If you become invisible while someone can already see you, you start out hidden to them (instead of undetected) until you successfully Sneak. You can't become Observed while invisible except via special abilities or magic.", "valued": false, "group": "senses"}, {"slug": "observed", "name": "Observed", "description": "Anything in plain view is observed by you. If a creature takes measures to avoid detection, such as by using Stealth to Hide, it can become Hidden or Undetected instead of observed. If you have another precise sense besides sight, you might be able to observe a creature or object using that sense instead. You can observe a creature with only your precise senses. When Seeking a creature using only imprecise senses, it remains hidden, rather than observed.", "valued": false, "group": "detection"}, {"slug": "off-guard", "name": "Off-Guard", "description": "You're distracted or otherwise unable to focus your full attention on defense. You take a –2 circumstance penalty to AC. Some effects give you the off-guard condition only to certain creatures or against certain attacks. Others—especially conditions—can make you off-guard against everything. If a rule doesn't specify that the condition applies only to certain circumstances, it applies to all of them, such as \"The target is off-guard.\"", "valued": false, "group": null}, {"slug": "paralyzed", "name": "Paralyzed", "description": "You're frozen in place. You have the Off Guard condition and can't act except to Recall Knowledge and use actions that require only your mind (as determined by the GM). Your senses still function, but only in the areas you can perceive without moving, so you can't Seek.", "valued": false, "group": null}, {"slug": "persistent-damage", "name": "Persistent Damage", "description": "You are taking damage from an ongoing effect, such as from being lit on fire. This appears as \"X persistent [type] damage,\" where \"X\" is the amount of damage dealt and \"[type]\" is the damage type. Like normal damage, it can be doubled or halved based on the results of an attack roll or saving throw. Instead of taking persistent damage immediately, you take it at the end of each of your turns as long as you have the condition, rolling any damage dice anew each time. After you take persistent damage, roll a Flat check to see if you recover from the persistent damage. If you succeed, the condition ends.", "valued": false, "group": null}, {"slug": "petrified", "name": "Petrified", "description": "You have been turned to stone. You can't act, nor can you sense anything. You become an object with a Bulk double your normal Bulk (typically 12 for a petrified Medium creature or 6 for a petrified Small creature), AC 9, Hardness 8, and the same current Hit Points you had when alive. You don't have a Broken Threshold. When the petrified condition ends, you have the same number of Hit Points you had as a statue. If the statue is destroyed, you immediately die. While petrified, your mind and body are in stasis, so you don't age or notice the passing of time.", "valued": false, "group": null}, {"slug": "prone", "name": "Prone", "description": "You're lying on the ground. You are Off Guard and take a –2 circumstance penalty to attack rolls. The only move actions you can use while you're prone are Crawl and Stand. Standing up ends the prone condition. You can Take Cover while prone to hunker down and gain greater cover against ranged attacks, even if you don't have an object to get behind, which grants you a +4 circumstance bonus to AC against ranged attacks (but you remain off-guard).\n\nIf you would be knocked prone while you're Climbing or Flying, you fall. You can't be knocked prone when Swimming.", "valued": false, "group": null}, {"slug": "quickened", "name": "Quickened", "description": "You're able to act more quickly. You gain 1 additional action at the start of your turn each round. Many effects that make you quickened require you use this extra action only in certain ways. If you become quickened from multiple sources, you can use the extra action you've been granted for any single action allowed by any of the effects that made you quickened. Because quickened has its effect at the start of your turn, you don't immediately gain actions if you become quickened during your turn.", "valued": false, "group": null}, {"slug": "restrained", "name": "Restrained", "description": "You're tied up and can barely move, or a creature has you pinned. You have the Off Guard and Immobilized conditions, and you can't use any attack or manipulate actions except to attempt to Escape or Force Open your bonds. Restrained overrides Grabbed.", "valued": false, "group": null}, {"slug": "sickened", "name": "Sickened", "description": "", "valued": true, "group": null}, {"slug": "slowed", "name": "Slowed", "description": "You have fewer actions. Slowed always includes a value. When you regain your actions, reduce the number of actions regained by your slowed value. Because you regain actions at the start of your turn, you don't immediately lose actions if you become slowed during your turn.", "valued": true, "group": null}, {"slug": "stunned", "name": "Stunned", "description": "You've become senseless. You can't act. Stunned usually includes a value, which indicates how many total actions you lose, possibly over multiple turns, from being stunned. Each time you regain actions, reduce the number you regain by your stunned value, then reduce your stunned value by the number of actions you lost. For example, if you were stunned 4, you would lose all 3 of your actions on your turn, reducing you to stunned 1; on your next turn, you would lose 1 more action, and then be able to use your remaining 2 actions normally. Stunned might also have a duration instead, such as \"stunned for 1 minute,\" causing you to lose all your actions for the duration.\n\nStunned overrides Slowed. If the duration of your stunned condition ends while you are slowed, you count the actions lost to the stunned condition toward those lost to being slowed. So, if you were stunned 1 and slowed 2 at the beginning of your turn, you would lose 1 action from stunned, and then lose only 1 additional action by being slowed, so you would still have 1 action remaining to use that turn.", "valued": true, "group": null}, {"slug": "stupefied", "name": "Stupefied", "description": "Your thoughts and instincts are clouded. Stupefied always includes a value. You take a status penalty equal to this value on Intelligence-, Wisdom-, and Charisma-based rolls and DCs, including Will saving throws, spell attack modifiers, spell DCs, and skill checks that use these attribute modifiers. Any time you attempt to Cast a Spell while stupefied, the spell is disrupted unless you succeed at a Flat check with a DC equal to 5 + your stupefied value.", "valued": true, "group": "abilities"}, {"slug": "unconscious", "name": "Unconscious", "description": "You're sleeping or have been knocked out. You can't act. You take a –4 status penalty to AC, Perception, and Reflex saves, and you have the Blinded and Off Guard conditions. When you gain this condition, you fall Prone and drop items you're holding unless the effect states otherwise or the GM determines you're positioned so you wouldn't.\n\nIf you're unconscious because you're Dying, you can't wake up while you have 0 Hit Points. If you are restored to 1 Hit Point or more, you lose the dying and unconscious conditions and can act normally on your next turn.\n\nIf you are unconscious and at 0 Hit Points, but not dying, you return to 1 Hit Point and awaken after sufficient time passes. The GM determines how long you remain unconscious, from a minimum of 10 minutes to several hours. If you are healed, you lose the unconscious condition and can act normally on your next turn.\n\nIf you're unconscious and have more than 1 Hit Point (typically because you are asleep or unconscious due to an effect), you wake up in one of the following ways.\n\n• You take damage, though if the damage reduces you to 0 Hit Points, you remain unconscious and gain the dying condition as normal.\n\n• You receive healing, other than the natural healing you get from resting.\n\n• Someone shakes you awake with an Interact action.\n\n• Loud noise around you might wake you. At the start of your turn, you automatically attempt a Perception check against the noise's DC (or the lowest DC if there is more than one noise), waking up if you succeed. If creatures are attempting to stay quiet around you, this Perception check uses their Stealth DCs. Some effects make you sleep so deeply that they don't allow you this Perception check.\n\n• If you are simply asleep, the GM decides you wake up either because you have had a restful night's sleep or something disrupted that rest.", "valued": false, "group": "death"}, {"slug": "undetected", "name": "Undetected", "description": "When you are undetected by a creature, that creature can't see you at all, has no idea what space you occupy, and can't target you, though you still can be affected by abilities that target an area. When you're undetected by a creature, that creature is Off Guard to you.\n\nA creature you're undetected by can guess which square you're in to try targeting you. It must pick a square and attempt an attack. This works like targeting a Hidden creature (requiring a Flat check), but the flat check and attack roll are rolled in secret by the GM, who doesn't reveal whether the attack missed due to failing the flat check, failing the attack roll, or choosing the wrong square. They can Seek to try to find you.", "valued": false, "group": "detection"}, {"slug": "unfriendly", "name": "Unfriendly", "description": "This condition reflects a creature's disposition toward a particular character, and only supernatural effects (like a spell) can impose this condition on a PC. A creature that is unfriendly to a character dislikes and distrusts that character. The unfriendly creature won't accept Requests from the character.", "valued": false, "group": "attitudes"}, {"slug": "unnoticed", "name": "Unnoticed", "description": "If you're unnoticed by a creature, that creature has no idea you're present. When you're unnoticed, you're also Undetected. This matters for abilities that can be used only against targets totally unaware of your presence.", "valued": false, "group": "detection"}, {"slug": "wounded", "name": "Wounded", "description": "You have been seriously injured. If you lose the Dying condition and do not already have the wounded condition, you become wounded 1. If you already have the wounded condition when you lose the dying condition, your wounded condition value increases by 1. If you gain the dying condition while wounded, increase your dying condition value by your wounded value.\n\nThe wounded condition ends if someone successfully restores Hit Points to you using Treat Wounds, or if you are restored to full Hit Points by any means and rest for 10 minutes.", "valued": true, "group": "death"}];
|
|
505
|
+
const GENERATED_ACTIONS = [{"slug": "administer-first-aid", "name": "Administer First Aid", "category": "", "traits": ["manipulate"], "exploration": false, "actionType": "action", "actions": 2, "description": "Requirements You're wearing or holding a Healer's Toolkit.\n\nYou perform first aid on an adjacent creature that is Dying or Bleeding. If a creature is both dying and bleeding, choose which ailment you're trying to treat before you roll. You can Administer First Aid again to attempt to remedy the other effect.\n\n• Stabilize Attempt a [[/act administer-first-aid variant=stabilize]]{Medicine} check on a creature that has 0 Hit Points and the dying condition. The DC is equal to 5 + that creature's recovery roll DC (typically 15 + its dying value).\n• Stop Bleeding Attempt a [[/act administer-first-aid variant=stop-bleeding]]{Medicine} check on a creature that is taking persistent bleed damage. The DC is usually the DC of the effect that caused the bleed.\n\nSuccess If you're trying to stabilize, the target loses the dying condition (but remains Unconscious). If you're trying to stop bleeding, the target benefits from an assisted recovery with the lowered DC for particularly appropriate help.\n\nCritical Failure If you were trying to stabilize, the target's dying value increases by 1. If you were trying to stop bleeding, the target immediately takes an amount of damage equal to its persistent bleed damage."}, {"slug": "affix-a-fulu", "name": "Affix a Fulu", "category": "interaction", "traits": ["manipulate"], "exploration": false, "actionType": "action", "actions": 1, "description": "You affix a fulu to an armor, weapon, shield, creature, or structure that's beside or in the same square as you. A creature can remove a fulu from itself or an unattended object in its reach with a single action."}, {"slug": "affix-a-talisman", "name": "Affix a Talisman", "category": "", "traits": ["exploration", "manipulate"], "exploration": true, "actionType": "passive", "actions": null, "description": "Requirements You must use a Repair Toolkit\n\nYou spend 10 minutes affixing a talisman to an item, placing the item on a stable surface and using the repair toolkit with both hands. You can also use this activity to remove a talisman. Attaching more than one talisman to an item deactivates all the talismans. They must be removed and re-affixed before they can be used again."}, {"slug": "aid", "name": "Aid", "category": "", "traits": [], "exploration": false, "actionType": "reaction", "actions": null, "description": "Trigger An ally is about to use an action that requires a skill check or attack roll.\n\nRequirements The ally is willing to accept your aid, and you have prepared to help (see below).\n\nYou try to help your ally with a task. To use this reaction, you must first prepare to help, usually by using an action during your turn. You must explain to the GM exactly how you're trying to help, and they determine whether you can Aid your ally.\n\nWhen you use your Aid reaction, attempt a skill check or attack roll of a type decided by the GM. The typical DC is 15, but the GM might adjust this DC for particularly hard or easy tasks. The GM can add any relevant traits to your preparatory action or to your Aid reaction depending on the situation, or even allow you to Aid checks other than skill checks and attack rolls.\n\nCritical Success You grant your ally a +2 circumstance bonus to the triggering check. If you're a master with the check you attempted, the bonus is +3, and if you're legendary, it's +4.\n\nSuccess You grant your ally a +1 circumstance bonus to the triggering check.\n\nCritical Failure Your ally takes a –1 circumstance penalty to the triggering check.\n\nEffect: Aid"}, {"slug": "arrest-a-fall", "name": "Arrest a Fall", "category": "", "traits": [], "exploration": false, "actionType": "reaction", "actions": null, "description": "Trigger You fall.\n\nRequirements You have a fly Speed.\n\nYou attempt an Acrobatics check or Reflex save to slow your fall. The DC is typically 15, but it might be higher due to air turbulence or other circumstances.\n\nSuccess You take no damage from the fall."}, {"slug": "avert-gaze", "name": "Avert Gaze", "category": "", "traits": [], "exploration": false, "actionType": "action", "actions": 1, "description": "You avert your gaze from danger, such as a medusa's gaze. You gain a +2 circumstance bonus to saves against visual abilities that require you to look at a creature or object, such as a medusa's petrifying gaze. Your gaze remains averted until the start of your next turn."}, {"slug": "avoid-notice", "name": "Avoid Notice", "category": "defensive", "traits": ["exploration"], "exploration": true, "actionType": "passive", "actions": null, "description": "You attempt a [[/act avoid-notice]]{Stealth} check to avoid notice while traveling at half speed. If you're Avoiding Notice at the start of an encounter, you usually roll a Stealth check instead of a Perception check both to determine your initiative and to see if the enemies notice you (based on their Perception DCs, as normal for Sneak, regardless of their initiative check results)."}, {"slug": "balance", "name": "Balance", "category": "", "traits": ["move"], "exploration": false, "actionType": "action", "actions": 1, "description": "Requirements You are in a square that contains a narrow surface, uneven ground, or another similar feature.\n\nYou move across a narrow surface or uneven ground, attempting an [[/act balance]]{Acrobatics} check against its Balance DC. You are Off Guard while on a narrow surface or uneven ground.\n\nCritical Success You move up to your Speed.\n\nSuccess You move up to your Speed, treating it as difficult terrain (every 5 feet costs 10 feet of movement).\n\nFailure You must remain stationary to keep your balance (wasting the action) or you fall. If you fall, your turn ends.\n\nCritical Failure You fall and your turn ends.\nSample Balance Tasks\n• Untrained tangled roots, uneven cobblestones\n• Trained wooden beam\n• Expert deep, loose gravel\n• Master tightrope, smooth sheet of ice\n• Legendary razor's edge, chunks of floor falling in midair"}, {"slug": "borrow-an-arcane-spell", "name": "Borrow an Arcane Spell", "category": "", "traits": ["concentrate", "exploration"], "exploration": true, "actionType": "passive", "actions": null, "description": "If you're an arcane spellcaster who prepares from a spellbook, you can attempt to prepare a spell from someone else's spellbook. The GM sets the DC for the check based on the spell's rank and rarity; it's typically a bit easier than Learning the Spell.\n\nSuccess You prepare the borrowed spell as part of your normal spell preparation.\n\nFailure You fail to prepare the spell, but the spell slot remains available for you to prepare a different spell. You can't try to prepare this spell until the next time you prepare spells."}, {"slug": "burrow", "name": "Burrow", "category": "", "traits": ["move"], "exploration": false, "actionType": "action", "actions": 1, "description": "Requirements You have a burrow Speed.\n\nYou dig your way through dirt, sand, or a similar loose material at a rate up to your burrow Speed. You can't burrow through rock or other substances denser than dirt unless you have an ability that allows you to do so."}, {"slug": "cast-a-spell", "name": "Cast a Spell", "category": "interaction", "traits": [], "exploration": false, "actionType": "passive", "actions": null, "description": "Spells can vary in how many actions they take, as shown in the spell's stat block. You cast cantrips, spells from spell slots, and focus spells using the same process, but must expend the spell when casting a spell from a spell slot and must spend 1 Focus Point to cast a focus spell. Some rules will refer to the Cast a Spell activity, such as \"if the next action you use is to Cast a Spell.\" Any spell qualifies as a Cast a Spell activity, and any characteristics of the spell use those of the specific spell you're casting.\n\nCosts and Loci Some spells require you to pay a cost or provide a locus. If the spell lists a cost, you must have the listed money, valuable materials, or other resources to cast the spell (such as gems or magical reagents), and they're expended during the casting.\n\nA locus is an object that funnels or directs the magical energy of the spell but is not consumed in its casting. As part of Casting the Spell, you retrieve the locus (if necessary, and if you have a free hand), and you can put it away again if you so choose. Loci tend to be expensive, and you need to acquire them in advance to cast the spell, but they aren't expended like costs are. Unless noted otherwise, a locus has negligible Bulk.\n\nLong Casting Times Some spells take minutes or hours to cast. You can't use other actions or reactions while casting such a spell, though at the GM's discretion, you might be able to speak a few sentences. As with other activities that take a long time, these spells have the exploration trait, and you can't cast them in an encounter. If combat breaks out while you're casting one, your spell is disrupted.\n\nDisrupted and Lost Spells Some abilities and spells can disrupt a spell, causing it to have no effect and be lost. When you lose a spell, you've already expended the spell slot and spent the spell's costs and actions. If a spell is disrupted during a Sustain action, the spell immediately ends."}, {"slug": "climb", "name": "Climb", "category": "interaction", "traits": ["move"], "exploration": false, "actionType": "action", "actions": 1, "description": "Requirements You have both hands free\n\nYou attempt an [[/act climb]]{Athletics} check to move a maximum distance of 5 feet up, down, or across an incline. You're Off Guard while climbing unless you have a climb Speed. The GM determines the DC based on the nature of the incline and environmental circumstances; you might get an automatic critical success on an incline that's trivial to climb. If your land Speed is 40 feet or higher, increase the maximum distance by 5 feet for every 20 feet of Speed above 20 feet.\n\nCritical Success You move along the incline, increasing the maximum distance by 5 feet.\n\nSuccess You move along the incline.\n\nCritical Failure You fall. If you began the climb on stable ground, you fall and land Prone.\nSample Climb Tasks\n• Untrained ladder, steep slope, low-branched tree\n• Trained rigging, rope, typical tree\n• Expert wall with small handholds and footholds\n• Master ceiling with handholds and footholds, rock wall\n• Legendary smooth surface"}, {"slug": "coerce", "name": "Coerce", "category": "interaction", "traits": ["auditory", "concentrate", "emotion", "exploration", "linguistic", "mental"], "exploration": true, "actionType": "passive", "actions": null, "description": "With threats either veiled or overt, you attempt to bully a creature into doing what you want. You must spend at least 1 minute of conversation with the creature. At the end of the conversation, attempt an [[/act coerce]]{Intimidation} check against the target's Will DC, modified by any circumstances the GM determines. (The attitudes referenced in the effects below are summarized in the Changing Attitudes section at the bottom)\n\nCritical Success The target gives you the information you seek or agrees to follow your directives so long as they aren't likely to harm the target in any way. The target continues to comply for an amount of time determined by the GM but not exceeding 1 day, at which point the target becomes unfriendly (if it wasn't already unfriendly or hostile). However, the target is too scared of you to retaliate—at least in the short term.\n\nSuccess As critical success, but once the target becomes unfriendly, they might decide to act against you—for example, by reporting you to the authorities or assisting your enemies.\n\nFailure The target doesn't do what you say, and if they were not already unfriendly or hostile, they become unfriendly.\n\nCritical Failure The target refuses to comply, becomes hostile if they weren't already, and is temporarily immune to your Coercion for at least 1 week.\n\nChanging Attitudes\n\nYour influence on NPCs is measured with a set of attitudes that reflect how they view your character. These are only a brief summary of a creature's disposition. The GM will supply additional nuance based on the history and beliefs of the characters you're interacting with, and their attitudes can change in accordance with the story. The attitudes are detailed in the Conditions Appendix and are summarized here.\n\n• Helpful: Willing to help you and responds favorably to your requests.\n• Friendly: Has a good attitude toward you, but won't necessarily stick their neck out to help you.\n• Indifferent: Doesn't care about you either way. (Most NPCs start out indifferent.)\n• Unfriendly: Dislikes you and doesn't want to help you.\n• Hostile: Actively works against you—and might attack you just because of their dislike.\n\nNo one can ever change the attitude of a player character with these skills. You can roleplay interactions with player characters, and even use Diplomacy results if the player wants a mechanical sense of how convincing or charming a character is, but players make the ultimate decisions about how their characters respond."}, {"slug": "command-an-animal", "name": "Command an Animal", "category": "interaction", "traits": ["auditory", "concentrate"], "exploration": false, "actionType": "action", "actions": 1, "description": "You issue an order to an animal. Attempt a [[/act command-an-animal]]{Nature} check against the animal's Will DC. The GM might adjust the DC if the animal has a good attitude toward you, you suggest a course of action it was predisposed toward, or you offer it a treat.\n\nYou automatically fail if the animal is hostile or unfriendly to you. If the animal is helpful to you, increase your degree of success by one step. You might be able to Command an Animal more easily with a feat like Ride.\n\nMost animals know the Drop Prone, Leap, Seek, Stand, Stride, and Strike basic actions. If an animal knows an activity, such as a horse's Gallop, you can Command the Animal to perform the activity, but you must spend as many actions on Command an Animal as the activity's number of actions. You can also spend multiple actions to Command the Animal to perform that number of basic actions on its next turn; for instance, you could spend 3 actions to Command an Animal to Stride three times or to Stride twice and then make a Strike.\n\nSuccess The animal does as you command on its next turn.\n\nFailure The animal is hesitant or resistant, and it does nothing.\n\nCritical Failure The animal misbehaves or misunderstands, and it takes some other action determined by the GM."}, {"slug": "compose-missive", "name": "Compose Missive", "category": "interaction", "traits": ["exploration", "manipulate"], "exploration": true, "actionType": "passive", "actions": null, "description": "You spend 10 minutes drawing, writing, or inscribing, covering the missive's surface with text, images, or embossing."}, {"slug": "conceal-an-object", "name": "Conceal an Object", "category": "interaction", "traits": ["manipulate", "secret"], "exploration": false, "actionType": "action", "actions": 1, "description": "You hide a small object on your person (such as a weapon of light Bulk). When you try to sneak a concealed object past someone who might notice it, the GM rolls your [[/act conceal-an-object]]{Stealth} check and compares it to this passive observer's Perception DC. Once the GM rolls your check for a concealed object, that same result is used no matter how many passive observers you try to sneak it past. If a creature is specifically searching you for an item, it can attempt a Perception check against your Stealth DC (finding the object on success).\n\nYou can also conceal an object somewhere other than your person, such as among undergrowth or in a secret compartment within a piece of furniture. In this case, characters Seeking in an area compare their Perception check results to your Stealth DC to determine whether they find the object.\n\nSuccess The object remains undetected.\n\nFailure The searcher finds the object."}, {"slug": "cover-tracks", "name": "Cover Tracks", "category": "defensive", "traits": ["concentrate", "exploration", "move"], "exploration": true, "actionType": "passive", "actions": null, "description": "You cover your tracks, moving up to half your travel Speed, using the Travel Speed rules. You don't need to attempt a Survival check to cover your tracks, but anyone tracking you must succeed at a Survival check check against your Survival DC if it is higher than the normal DC to Track.\n\nIn some cases, you might Cover Tracks in an encounter. In this case, Cover Tracks is a single action and doesn't have the exploration trait."}, {"slug": "craft", "name": "Craft", "category": "interaction", "traits": ["downtime", "manipulate"], "exploration": false, "actionType": "passive", "actions": null, "description": "You can make an item from raw materials. You need the Alchemical Crafting skill feat to create alchemical items and the Magical Crafting feat to create magic items.\n\nTo craft an item, you must meet the following requirements:\n\n• The item is your level or lower. An item that doesn't list a level is level 0. If the item is 9th level or higher, you must be a master in Crafting, and if it's 17th or higher, you must be legendary.\n• The item must be common, or you must otherwise have access to it.\n• You have an appropriate set of tools and, in many cases, a workshop. For example, you need access to a smithy to forge a metal shield, or an Alchemist's Lab to produce alchemical items.\n• You must supply raw materials worth at least half the item's Price. You always expend at least that amount of raw materials when you Craft successfully. If you're in a settlement, you can usually spend currency to get the amount of raw materials you need, except in the case of rarer precious materials.\n\nYou attempt a Crafting check check after you spend 2 days of work setting up, or 1 day if you have the item's formula. The GM determines the DC to Craft the item based on its level, rarity, and other circumstances.\n\nIf your attempt to create the item is successful, you expend the raw materials you supplied. You can pay the remaining portion of the item's Price in materials to complete the item immediately, or you can spend additional downtime days working on it. For each additional day you spend, reduce the value of the materials you need to expend to complete the item. This amount is determined using the Income Earned table, based on your proficiency rank in Crafting and using your own level instead of a task level.\n\nAfter any of these downtime days, you can complete the item by spending the remaining portion of its Price in materials. If the downtime days you spend are interrupted,you can return to finish the item later, continuing where you left off.\n\nCritical Success Your attempt is successful. Each additional day spent Crafting reduces the materials needed to complete the item by an amount based on your level + 1 and your proficiency rank in Crafting.\n\nSuccess Your attempt is successful. Each additional day spent Crafting reduces the materials needed to complete the item by an amount based on your level and your proficiency rank.\n\nFailure You fail to complete the item. You can salvage the raw materials you supplied for their full value. If you want to try again, you must start over.\n\nCritical Failure You fail to complete the item. You ruin 10% of the raw materials you supplied, but you can salvage the rest. If you want to try again, you must start over."}, {"slug": "crawl", "name": "Crawl", "category": "defensive", "traits": ["move"], "exploration": false, "actionType": "action", "actions": 1, "description": "Requirements You are prone and your Speed is at least 10 feet.\n\nYou move 5 feet by crawling and continue to stay Prone."}, {"slug": "create-forgery", "name": "Create Forgery", "category": "interaction", "traits": ["downtime", "secret"], "exploration": false, "actionType": "passive", "actions": null, "description": "Requirements You provide the proper writing materials for your forgery.\n\nYou create a forged document, usually over the course of a day or a week. The GM rolls a secret DC 20 [[/act create-forgery]]{Society} check. If you need to forge a specific person's handwriting, you need a sample of that person's handwriting. Otherwise, you need only to have seen a similar document, and you gain up to a +4 circumstance bonus to the check (the GM determines the bonus).\n\nSuccess The forgery is of good enough quality that passive observers can't notice the fake (but see Examining Forgeries).\n\nFailure The forgery has some obvious signs of being a fake, potentially allowing passive observers to detect it. Each time a passive observer sees the document, the GM compares your check result to the observer's Perception DC or Society DC, whichever is higher. If your result doesn't exceed a passive observer's DC, that observer knows the document is a forgery.\n\nExamining Forgeries\n\nA creature on the lookout for forgeries, even one who was fooled on a passive glance, can take time to closely examine a document to see if it's a forgery. They apply different techniques and analysis methods to look beyond the surface elements and attempt a secret Perception or Society check against the forger's Society DC; any bonus you had to create the forgery initially applies to this DC. On a success, the examiner knows the document is a forgery. On a failure, they think the document is genuine and can't try again unless they get a new reason to be suspicious of the document. If a PC examines a genuine document, the GM might still pretend to roll a secret check before revealing the document is genuine."}, {"slug": "create-a-diversion", "name": "Create a Diversion", "category": "interaction", "traits": ["mental"], "exploration": false, "actionType": "action", "actions": 1, "description": "With a [[/act create-a-diversion variant=gesture]]{gesture}, a [[/act create-a-diversion variant=trick]]{trick}, or some [[/act create-a-diversion variant=distracting-words]]{distracting words}, you can create a diversion that draws creatures' attention elsewhere. If you use a gesture or trick, this action gains the manipulate trait. If you use distracting words, it gains the auditory and linguistic traits.\n\nAttempt a single Deception check and compare it to the Perception DCs of the creatures whose attention you're trying to divert. Whether or not you succeed, creatures you attempt to divert gain a +4 circumstance bonus to their Perception DCs against your attempts to Create a Diversion for 1 minute.\n\nSuccess You become Hidden to each creature whose Perception DC is less than or equal to your result. (The hidden condition allows you to Sneak away.) This lasts until the end of your turn or until you do anything except Step or use the Stealth skill to Hide or Sneak. If you Strike a creature, the creature remains Off Guard against that attack, and you then become observed. If you do anything else, you become observed just before you act unless the GM determines otherwise.\n\nFailure You don't divert the attention of any creatures whose Perception DC exceeds your result, and those creatures are aware you were trying to trick them."}, {"slug": "decipher-writing", "name": "Decipher Writing", "category": "interaction", "traits": ["concentrate", "exploration", "secret"], "exploration": true, "actionType": "passive", "actions": null, "description": "You attempt to decipher complicated writing or literature on an obscure topic. This usually takes 1 minute per page of text, but might take longer (typically an hour per page for decrypting ciphers or the like). The text must be in a language you can read, though the GM might allow you to attempt to decipher text written in an unfamiliar language using [[/act decipher-writing statistic=society]]{Society} instead.\n\nThe DC is determined by the GM based on the state or complexity of the document. The GM might have you roll one check for a short text or a check for each section of a larger text.\n\nSkill\n\nTypically used for\n\n[[/act decipher-writing statistic=arcana]]{Arcana}\n\nWriting about magic or science\n\n[[/act decipher-writing statistic=occultism]]{Occultism}\n\nEsoteric texts about mysteries and philosophy\n\n[[/act decipher-writing statistic=religion]]{Religion}\n\nScripture\n\n[[/act decipher-writing statistic=society]]{Society}\n\nCoded messages or archaic documents\n\nCritical Success You understand the true meaning of the text.\n\nSuccess You understand the true meaning of the text. If it was a coded document, you know the general meaning but might not have a word-for-word translation.\n\nFailure You can't understand the text and take a –2 circumstance penalty to further checks to decipher it.\n\nCritical Failure You believe you understand the text on that page, but you have in fact misconstrued its message.\n\nSample Decipher Tasks\n\nTrained entry-level philosophy treatise\n\nExpert complex code, such as a cipher\n\nMaster spymaster's code or advanced research notes\n\nLegendary esoteric planar text written in metaphor by an ancient celestial"}, {"slug": "deconstruct", "name": "Deconstruct", "category": "interaction", "traits": ["downtime"], "exploration": false, "actionType": "passive", "actions": null, "description": "You deconstruct an item to provide the starting point to convert it into a new item. You need the Alchemical Crafting skill feat to deconstruct alchemical items and the Magical Crafting skill feat to deconstruct magic items.\n\nTo Deconstruct an item, you must meet the following requirements.\n\n• The item is your level or lower. An item that doesn't list a level is level 0. If the item is 9th level or higher, you must be a master in Crafting, and if it's 16th or higher, you must be legendary.\n\n• The item isn't a cursed item, artifact, or other item that is similarly hard to destroy. The item isn't a consumable item.\n\n• The item has a listed Price.\n\n• You must have an appropriate set of tools and, in many cases, a workshop. For example, you need access to a smithy to deconstruct a metal shield or an alchemist's lab to de-concoct alchemical items.\n\nAt the start of this process, you must decide if you're using the deconstructed item to build a new, similar item, of if you are simply breaking it down for raw ingredients that can be used at a later date for any item. In either case, this activity takes 1 day to perform, but if you're using the item to create a new, similar item, that day can be counted as one of the crafting days for the new item.\n\nAt the end of the activity, you must attempt a Crafting check. The GM sets the DC of this check based on the level of the item you are attempting to deconstruct, its rarity, and other circumstances.\n\nCritical Success If you are deconstructing the item to make a new, similar item, you can apply 80% of the cost of the deconstructed item to the new item. If you are deconstructing the item for raw materials alone, you can apply 55% of the cost of the deconstructed item to a single new item. In either case, if this is in excess of the new item's cost, the remainder is lost.\n\nSuccess As critical success, but you can only apply 75% of the deconstructed item's cost to the new similar item and 50% of the deconstructed item's cost to any single item.\n\nFailure You fail to deconstruct the item, wasting your time. You can try again.\n\nCritical Failure You fail to deconstruct the item and damage it in the process. You must either repair it before attempting again, or you can attempt to deconstruct it again but lose 5% of the value of the item."}, {"slug": "defend", "name": "Defend", "category": "defensive", "traits": ["exploration"], "exploration": true, "actionType": "passive", "actions": null, "description": "You move at half your travel speed with your shield raised. If combat breaks out, you gain the benefits of Raising a Shield before your first turn begins."}, {"slug": "delay", "name": "Delay", "category": "interaction", "traits": [], "exploration": false, "actionType": "free", "actions": null, "description": "Trigger Your turn begins.\n\nYou wait for the right moment to act. The rest of your turn doesn't happen yet. Instead, you're removed from the initiative order. You can return to the initiative order as a free action triggered by the end of any other creature's turn. This permanently changes your initiative to the new position. You can't use reactions until you return to the initiative order. If you Delay an entire round without returning to the initiative order, the actions from the Delayed turn are lost, your initiative doesn't change, and your next turn occurs at your original position in the initiative order.\n\nWhen you Delay, any Persistent Damage or other negative effects that normally occur at the start or end of your turn occur immediately when you use the Delay action. Any beneficial effects that would end at any point during your turn also end. The GM might determine that other effects end when you Delay as well. Essentially, you can't Delay to avoid negative consequences that would happen on your turn or to extend beneficial effects that would end on your turn."}, {"slug": "demoralize", "name": "Demoralize", "category": "offensive", "traits": ["auditory", "concentrate", "emotion", "fear", "mental"], "exploration": false, "actionType": "action", "actions": 1, "description": "With a sudden shout, a well-timed taunt, or a cutting put-down, you can shake an enemy's resolve. Choose a creature within 30 feet of you who you're aware of. Attempt an [[/act demoralize]]{Intimidation} check against that target's Will DC. If the target doesn't understand the language you are speaking, or you're not speaking a language, you take a –4 circumstance penalty to the check. Regardless of your result, the target is temporarily immune to your attempts to Demoralize it for 10 minutes.\n\nCritical Success The target becomes Frightened 2.\n\nSuccess The target becomes Frightened 1."}, {"slug": "detect-magic", "name": "Detect Magic", "category": "interaction", "traits": ["concentrate", "exploration"], "exploration": true, "actionType": "passive", "actions": null, "description": "You cast Detect Magic at regular intervals. You move at half your travel speed or slower. You have no chance of accidentally overlooking a magic aura at a travel speed up to 300 feet per minute, but must be traveling no more than 150 feet per minute to detect magic auras before the party moves into them."}, {"slug": "disable-a-device", "name": "Disable a Device", "category": "interaction", "traits": ["manipulate"], "exploration": false, "actionType": "action", "actions": 2, "description": "This action allows you to disarm a trap or another complex device. Often, a device requires numerous successes before becoming disabled, depending on its construction and complexity. A Thieves' Toolkit is helpful and sometimes even required to Disable a Device, as determined by the GM, and sometimes a device requires a higher proficiency rank in Thievery to disable it.\n\nYour [[/act disable-device]]{Thievery} check result determines your progress.\n\nCritical Success You disable the device, or you achieve two successes toward disabling a device requiring more than one success. You leave no trace of your tampering, and you can rearm the device later, if that type of device can be rearmed.\n\nSuccess You disable the device, or you achieve one success toward disabling a device that requires more than one success.\n\nCritical Failure You trigger the device."}, {"slug": "disarm", "name": "Disarm", "category": "offensive", "traits": ["attack"], "exploration": false, "actionType": "action", "actions": 1, "description": "Requirements You have at least one hand free. The target can't be more than one size larger than you.\n\nYou try to knock an item out of a creature's grasp. Attempt an [[/act disarm]]{Athletics} check against the target's Reflex DC.\n\nCritical Success You knock the item out of the target's grasp. It falls to the ground in the target's space.\n\nSuccess You weaken your target's grasp on the item. Further attempts to Disarm the target of that item gain a +2 circumstance bonus, and the target takes a –2 circumstance penalty to attacks with the item or other checks requiring a firm grasp on the item. The creature can end the effect by Interacting to change its grip on the item; otherwise, it lasts as long as the creature holds the item.\n\nEffect: Disarm (Success)\n\nCritical Failure You lose your balance and become Off Guard until the start of your next turn."}, {"slug": "dismiss", "name": "Dismiss", "category": "interaction", "traits": ["concentrate"], "exploration": false, "actionType": "action", "actions": 1, "description": "You end an effect that states you can Dismiss it. Dismissing ends the entire effect unless noted otherwise."}, {"slug": "drop-prone", "name": "Drop Prone", "category": "defensive", "traits": ["move"], "exploration": false, "actionType": "action", "actions": 1, "description": "You fall Prone."}, {"slug": "earn-income", "name": "Earn Income", "category": "interaction", "traits": ["downtime"], "exploration": false, "actionType": "passive", "actions": null, "description": "You can use a skill to earn money during downtime. You must be trained in the skill to do so. This takes time to set up, and your income depends on your proficiency rank and how lucrative a task you can find. Because this process requires a significant amount of time and involves tracking things outside the progress of adventures, it won't come up in every campaign. The most typical ways to earn income, detailed further in this section are:\n\n• Crafting goods for the market (Crafting)\n• Practicing a Trade (Lore)\n• Staging a Performance (Performance)\n\nIn some cases, the GM might let you use a different skill to Earn Income through specialized work. Usually, this is scholarly work, such as using Religion in a monastery to study old texts—but giving sermons at a church would still fall under Performance instead of Religion. You also might be able to use physical skills to make money, such as using Acrobatics to perform feats in a circus or Thievery to pick pockets. If you're using a skill other than Crafting, Lore, or Performance, the DC tends to be significantly higher.\nEarn Income\n\nDowntime\n\nYou use one of your skills to make money during downtime. The GM assigns a task level representing the most lucrative job available. You can search for lower-level tasks, with the GM determining whether you find any. Sometimes you can attempt to find better work than the initial offerings, though this takes time and requires using the Diplomacy skill to Gather Information, doing some research, or socializing.\n\nWhen you take on a job, the GM secretly sets the DC of your skill check. After your first day of work, you roll to determine your earnings. You gain an amount of income based on your result, the task's level, and your proficiency rank (as listed on the Income Earned table).\n\nYou can continue working at the task on subsequent days without needing to roll again. For each day you spend after the first, you earn the same amount as the first day, up until the task's completion. The GM determines how long you can work at the task. Most tasks last a week or two, though some can take months or even years.\nTable 4-2: Income EarnedTask LevelFailureTrainedExpertMasterLegendary\n01 cp5 cp5 cp5 cp5 cp\n12 cp2 sp2 sp2 sp2 sp\n24 cp3 sp3 sp3 sp3 sp\n38 cp5 sp5 sp5 sp5 sp\n41 sp7 sp8 sp8 sp8 sp\n52 sp9 sp1 gp1 gp1 gp\n63 sp1 gp, 5 sp2 gp2 gp2 gp\n74 sp2 gp2 gp, 5 sp2 gp, 5 sp2 gp, 5 sp\n85 sp2 gp, 5 sp3 gp3 gp3 gp\n96 sp3 gp4 gp4 gp4 gp\n107 sp4 gp5 gp6 gp6 gp\n118 sp5 gp6 gp8 gp8 gp\n129 sp6 gp8 gp10 gp10 gp\n131 gp7 gp10 gp15 gp15 gp\n141 gp, 5 sp8 gp15 gp20 gp20 gp\n152 gp10 gp20 gp28 gp28 gp\n162 gp, 5 sp13 gp25 gp36 gp40 gp\n173 gp15 gp30 gp45 gp55 gp\n184 gp20 gp45 gp70 gp90 gp\n196 gp30 gp60 gp100 gp130 gp\n208 gp40 gp75 gp150 gp200 gp\n20 (critical success)-50 gp90 gp175 gp300 gp\n\nCritical Success You do outstanding work. Gain the amount of currency listed for the task level + 1 and your proficiency rank.\n\nSuccess You do competent work. Gain the amount of currency listed for the task level and your proficiency rank.\n\nFailure You do shoddy work and get paid the bare minimum for your time. Gain the amount of currency listed in the failure column for the task level. The GM will likely reduce how long you can continue at the task.\n\nCritical Failure You earn nothing for your work and are fired immediately. You can't continue at the task. Your reputation suffers, potentially making it difficult for you to find rewarding jobs in that community in the future.\nSample Earn Income Tasks\n\nThese examples use Alcohol Lore to work in a bar or Legal Lore to perform legal work.\n\n• Trained bartend, do legal research\n• Expert curate drink selection, present minor court cases\n• Master run a large brewery, present important court cases\n• Legendary run an international brewing franchise, present a case in Hell's courts\nCrafting Goods for the Market [Crafting]\n\nUsing Crafting, you can work at producing common items for the market. It's usually easy to find work making basic items whose level is 1 or 2 below your settlement's level.\n\nHigher-level tasks represent special commissions, which might require you to Craft a specific item using the Craft downtime activity and sell it to a buyer at full Price. These opportunities don't occur as often and might have special requirements—or serious consequences if you disappoint a prominent client.\nPracticing a Trade [Lore]\n\nYou apply the practical benefits of one of your Lore specialties during downtime by practicing your trade. This is most effective for Lore specialties such as business, law, or sailing, where there's high demand for workers. The GM might increase the DC or determine only low-level tasks are available if you're attempting to use an obscure Lore skill to earn income. You might also need specialized tools to accept a job, like mining tools to work in a mine or a merchant's scale to buy and sell valuables in a market.\nStaging a Performance [Performance]\n\nYou perform for an audience to make money. The available audiences determine the level of your task, since more discerning audiences are harder to impress but provide a bigger payout. The GM determines the task level based on the audiences available. Performing for a typical audience of commoners on the street is a level 0 task, but a performance for a group of artisans with more refined tastes might be a 2nd- or 3rd-level task, and ones for merchants, nobility, and royalty are increasingly higher level.\n\nYour degree of success determines whether you moved your audience and whether you were rewarded with applause or rotten fruit.\n\nEnding or Interrupting Tasks\n\nWhen a task you're doing is complete, or if you stop in the middle of one, you normally have to find a new task if you want to keep Earning Income. For instance, if you quit your job working at the docks, you'll need to find another place of employment instead of picking up where you left off. This usually takes 1 day or more of downtime looking for leads on new jobs.\n\nHowever, you might pause a task due to an adventure or event that wouldn't prevent you from returning to the old job later. The GM might decide that you can pick up where you left off, assuming the task hasn't been completed by others in your absence. Whether you roll a new skill check when you resume is also up to the GM. Generally speaking, if you had a good initial roll and want to keep it, you can, but if you had a bad initial roll, you can't try for a better one by pausing to do something else. If your statistics changed during the break—usually because you leveled up while adventuring—you can attempt a new check."}, {"slug": "escape", "name": "Escape", "category": "interaction", "traits": ["attack"], "exploration": false, "actionType": "action", "actions": 1, "description": "You attempt to escape from being Grabbed, Immobilized, or Restrained. Choose one creature, object, spell effect, hazard, or other impediment imposing any of those conditions on you. Attempt a check using your [[/act escape statistic=unarmed]]{unarmed attack modifier} against the DC of the effect. This is typically the Athletics DC of a creature grabbing you, the Thievery DC of a creature who tied you up, the spell DC for a spell effect, or the listed Escape DC of an object, hazard, or other impediment. You can attempt an [[/act escape statistic=acrobatics]]{Acrobatics} or [[/act escape statistic=athletics]]{Athletics} check instead of using your attack modifier if you choose (but this action still has the attack trait).\n\nCritical Success You get free and remove the grabbed, immobilized, and restrained conditions imposed by your chosen target. You can then Stride up to 5 feet.\n\nSuccess You get free and remove the grabbed, immobilized, and restrained conditions imposed by your chosen target.\n\nCritical Failure You don't get free, and you can't attempt to Escape again until your next turn."}, {"slug": "feint", "name": "Feint", "category": "offensive", "traits": ["mental"], "exploration": false, "actionType": "action", "actions": 1, "description": "Requirements You are within melee reach of the target you attempt to Feint.\n\nWith a misleading flourish, you leave an opponent unprepared for your real attack. Attempt a [[/act feint]]{Deception} check against your target's Perception DC.\n\nCritical Success You throw your enemy's defenses against you entirely off. The target is Off Guard against melee attacks that you attempt against it until the end of your next turn.\n\nSuccess Your foe is fooled, but only momentarily. The target is off-guard against the next melee attack that you attempt against it before the end of your current turn.\n\nCritical Failure Your feint backfires. You are off-guard against melee attacks the target attempts against you until the end of your next turn."}, {"slug": "fly", "name": "Fly", "category": "interaction", "traits": ["move"], "exploration": false, "actionType": "action", "actions": 1, "description": "Requirements You have a fly Speed.\n\nYou move through the air up to your fly Speed. Moving upward (straight up or diagonally) uses the rules for moving through difficult terrain. You can move straight down 10 feet for every 5 feet of movement you spend. If you Fly to the ground, you don't take falling damage. You can use an action to Fly 0 feet to hover in place. If you're airborne at the end of your turn and didn't use a Fly action this round, you fall."}, {"slug": "follow-the-expert", "name": "Follow the Expert", "category": "interaction", "traits": ["auditory", "concentrate", "exploration", "visual"], "exploration": true, "actionType": "passive", "actions": null, "description": "Choose an ally attempting a recurring skill check while exploring, such as climbing, or performing a different exploration tactic that requires a skill check (like Avoiding Notice). The ally must be at least an expert in that skill and must be willing to provide assistance. While Following the Expert, you match their tactic or attempt similar skill checks.\n\nThanks to your ally's assistance, you can add your level as a proficiency bonus to the associated skill check, even if you're untrained. Additionally, you gain a circumstance bonus to your skill check based on your ally's proficiency (+2 for expert, +3 for master, and +4 for legendary).\n\nEffect: Follow The Expert"}, {"slug": "force-open", "name": "Force Open", "category": "interaction", "traits": ["attack"], "exploration": false, "actionType": "action", "actions": 1, "description": "Using your body, a lever, or some other tool, you attempt to forcefully open a door, window, container or heavy gate. With a high enough result, you can even smash through walls. Without a crowbar, prying something open takes a –2 item penalty to the [[/act force-open]]{Athletics} check to Force Open.\n\nCritical Success You open the door, window, container, or gate and can avoid damaging it in the process.\n\nSuccess You break the door, window, container, or gate open, and it gains the Broken condition. If it's especially sturdy, the GM might have it take damage but not be broken.\n\nCritical Failure Your attempt jams the door, window, container, or gate shut, imposing a –2 circumstance penalty on future attempts to Force it Open.\nSample Force Open Tasks\n• Untrained fabric, flimsy glass\n• Trained ice, sturdy glass\n• Expert flimsy wooden door, wooden portcullis\n• Master sturdy wooden door, iron portcullis, metal bar\n• Legendary stone or iron door"}, {"slug": "fortify-camp", "name": "Fortify Camp", "category": "interaction", "traits": [], "exploration": false, "actionType": "passive", "actions": null, "description": "You can spend time fortifying your camp for defense with a successful Crafting check (typically at a trained or expert DC). Anyone keeping watch or defending the camp gains a +2 circumstance bonus to initiative rolls and Perception checks to Seek creatures attempting to sneak up on the camp."}, {"slug": "gather-information", "name": "Gather Information", "category": "interaction", "traits": ["exploration", "secret"], "exploration": true, "actionType": "passive", "actions": null, "description": "You canvass local markets, taverns, and gathering places in an attempt to learn about a specific individual or topic. The GM determines the DC of the [[/act gather-information]]{Diplomacy} check and the amount of time it takes (typically 2 hours, but sometimes more), along with any benefit you might be able to gain by spending coin on bribes, drinks, or gifts.\n\nSuccess You collect information about the individual or topic. The GM determines the specifics.\n\nCritical Failure You collect incorrect information about the individual or topic.\nSample Gather Information Tasks\n• Untrained talk of the town\n• Trained common rumor\n• Expert obscure rumor, poorly guarded secret\n• Master well-guarded or esoteric information\n• Legendary information known only to an incredibly select few, or only to extraordinary beings"}, {"slug": "grab-an-edge", "name": "Grab an Edge", "category": "interaction", "traits": ["manipulate"], "exploration": false, "actionType": "reaction", "actions": null, "description": "Trigger You fall from or past an edge or handhold.\n\nRequirements Your hands are not tied behind your back or otherwise restrained.\n\nWhen you fall off or past an edge or other handhold, you can try to grab it, potentially stopping your fall. You must succeed at your choice of an Acrobatics check or a Reflex save, usually at the Climb DC. If you grab the edge or handhold, you can then Climb up using Athletics.\n\nCritical Success You grab the edge or handhold, whether or not you have a hand free, typically by using a suitable held item to catch yourself (catching a battle axe on a ledge, for example). You still take damage from the distance fallen so far, but you treat the fall as though it were 30 feet shorter.\n\nSuccess If you have at least one hand free, you grab the edge or handhold, stopping your fall. You still take damage from the distance fallen so far, but you treat the fall as though it were 20 feet shorter. If you have no hands free, you continue to fall as if you had failed the check.\n\nCritical Failure You continue to fall, and if you've fallen 20 feet or more before you use this reaction, you take 10 bludgeoning damage from the impact for every 20 feet fallen."}, {"slug": "grapple", "name": "Grapple", "category": "interaction", "traits": ["attack"], "exploration": false, "actionType": "action", "actions": 1, "description": "Requirements You have at least one free hand and your target is no more than one size larger than you.\n\nYou attempt to grab a creature or object with your free hand. Attempt an [[/act grapple]]{Athletics} check against the target's Fortitude DC. You can grapple a target you already have Grabbed or Restrained without having a hand free.\n\nCritical Success Your target is restrained until the end of your next turn unless you move or your target Escapes.\n\nSuccess Your target is grabbed until the end of your next turn unless you move or your target Escapes.\n\nFailure You fail to grab your target. If you already had the target grabbed or restrained using a Grapple, those conditions on the target end.\n\nCritical Failure If you already had the target grabbed or restrained, it breaks free. Your target can either grab you, as if it succeeded at using the Grapple action against you, or force you to fall and land Prone."}, {"slug": "grow", "name": "Grow", "category": "interaction", "traits": ["downtime", "manipulate"], "exploration": false, "actionType": "passive", "actions": null, "description": "You can grow an item from a living thing, most commonly a plant. You need the Alchemical Crafting skill feat to Grow an alchemical item, the Magical Crafting skill feat to Grow a magic item, and the Snare Crafting feat to Grow a snare. To Grow an item, you must meet the following requirements.\n\n• The item is your level or lower. An item that doesn't list a level is level 0. If the item is 9th level or higher, you must be a master in Crafting, and if it's 16th or higher, you must be legendary.\n• You have the formula for the item; see Getting Formulas for more information.\n• You have an appropriate set of tools for growing the item. While cultivation and gardening tools are typical for plants, you might also use a different technique that requires a different set of tools. For instance, if you play music to help your plants grow, you might use a musical instrument instead.\n• You must supply special fertilizers or other magical nutrients worth at least half the item's Price. You always expend at least that quantity of fertilizers and magical nutrients when you Grow successfully. If you're in a settlement, you can usually spend currency to get the amount of magical nutrients you need, except in the case of rarer precious materials. You can also bring them with you in advance or forage for them with a skill like Herbalism Lore, gaining an amount of value based on the rules for Earn Income.\n\nYou must spend 4 days at work, at which point you attempt a Crafting check. The GM determines the DC to Grow the item based on its level, rarity, and other circumstances. Depending on the specifics of the type of item, it might be easier to Grow than it is to Craft, or vice versa; typically, the GM can represent that by making an easy or hard DC adjustment.\n\nIf your attempt to create the item is successful, you expend the fertilizers and other magical nutrients you supplied. You can pay the remaining portion of the item's Price in additional growth accelerants to complete the item immediately, or you can spend additional downtime days cultivating the item. For each additional day taken, reduce the value of the accelerants you need to complete the item. This amount is determined using the Income Earned table, based on your proficiency rank in Crafting and using your own level instead of a task level. After any of these downtime days, you can complete the item by spending the remaining portion of its Price in accelerants. If the downtime days you spend are interrupted, you can return to finish the item later, continuing where you left off.\n\nYou also have the option to allow the item to grow mostly untended, only stopping to supervise it occasionally, though the pace is much slower without your direct intervention. At the end of each season in which you spent at least 1 day of downtime to Grow the item, roll an additional Crafting check and reduce the value of accelerants you need to expend to complete the item by the corresponding amount.\n\nCritical Success Your attempt is successful. Each additional day spent Growing reduces the materials needed to complete the item by an amount based on your level + 1 and your proficiency rank in Crafting.\n\nSuccess Your attempt is successful. Each additional day spent Growing reduces the materials needed to complete the item by an amount based on your level and your proficiency rank.\n\nFailure You fail to complete the item. You can salvage the raw materials you supplied for their full value. If you want to try again, you must start over.\n\nCritical Failure You fail to complete the item. You ruin 10% of the fertilizers and nutrients you supplied, but you can salvage the rest. If you want to try again, you must start over."}, {"slug": "hide", "name": "Hide", "category": "interaction", "traits": ["secret"], "exploration": false, "actionType": "action", "actions": 1, "description": "You huddle behind cover or greater cover or deeper into concealment to become Hidden, rather than Observed. The GM rolls your [[/act hide]]{Stealth} check in secret and compares the result to the Perception DC of each creature you're observed by but that you have cover or greater cover against or are Concealed from. You get a +2 circumstance bonus to your check if you have standard cover (or +4 from greater cover).\n\nSuccess If the creature could see you, you're now Hidden from it instead of observed. If you were hidden from or Undetected by the creature, you retain that condition.\n\nIf you successfully become hidden to a creature but then cease to have cover or greater cover against it or be concealed from it, you become observed again. You cease being hidden if you do anything except Hide, Sneak, or Step. If you attempt to Strike a creature, the creature remains off-guard against that attack, and you then become observed. If you do anything else, you become observed just before you act unless the GM determines otherwise. The GM might allow you to perform a particularly unobtrusive action without being noticed, possibly requiring another Stealth check.\n\nIf a creature uses Seek to make you observed by it, you must successfully Hide to become hidden from it again."}, {"slug": "high-jump", "name": "High Jump", "category": "interaction", "traits": ["move"], "exploration": false, "actionType": "action", "actions": 2, "description": "You Stride, then attempt a DC 30 [[/act high-jump]]{Athletics} check to jump vertically. If you didn't Stride at least 10 feet, you automatically fail. This DC might be increased or decreased due to the situation, as determined by the GM.\n\nCritical Success You Leap up to 8 feet vertically and 10 feet horizontally.\n\nSuccess You Leap up to 5 feet vertically and 5 feet horizontally.\n\nFailure You Leap normally.\n\nCritical Failure You fall Prone in your space."}, {"slug": "hustle", "name": "Hustle", "category": "interaction", "traits": ["exploration", "move"], "exploration": true, "actionType": "passive", "actions": null, "description": "You strain yourself to move at double your travel speed. You can Hustle only for a number of minutes equal to your Constitution modifier × 10 (minimum 10 minutes). If you are in a group that is Hustling, use the lowest Constitution modifier among everyone to determine how fast the group can Hustle together."}, {"slug": "identify-alchemy", "name": "Identify Alchemy", "category": "interaction", "traits": ["concentrate", "exploration", "secret"], "exploration": true, "actionType": "passive", "actions": null, "description": "Requirements You're holding or wearing an Alchemist's Toolkit.\n\nYou can identify the nature of an alchemical item with 10 minutes of testing using alchemist's toolkit. If your attempt is interrupted in any way, you must start over.\n\nSuccess You identify the item and the means of activating it.\n\nFailure You fail to identify the item but can try again.\n\nCritical Failure You misidentify the item as another item of the GM's choice."}, {"slug": "identify-magic", "name": "Identify Magic", "category": "interaction", "traits": ["concentrate", "exploration", "secret"], "exploration": true, "actionType": "passive", "actions": null, "description": "Once you discover that an item, location, or ongoing effect is magical, you can spend 10 minutes to try to identify the particulars of its magic. If your attempt is interrupted, you must start over. The GM sets the DC for your check. Cursed magic or esoteric subjects usually have higher DCs or might even be impossible to identify using this activity alone. Heightening a spell doesn't increase the DC to identify it.\n\nCritical Success You learn all the attributes of the magic, including its name (for an effect), what it does, any means of activating it (for an item or location), and whether it is cursed.\n\nSuccess For an item or location, you get a sense of what it does and learn any means of activating it. For an ongoing effect (such as a spell with a duration), you learn the effect's name and what it does. You can't try again in hopes of getting a critical success.\n\nFailure You fail to identify the magic and can't try again for 1 day.\n\nCritical Failure You misidentify the magic as something else of the GM's choice."}, {"slug": "impersonate", "name": "Impersonate", "category": "interaction", "traits": ["concentrate", "exploration", "manipulate", "secret"], "exploration": true, "actionType": "passive", "actions": null, "description": "You create a disguise to pass yourself off as someone or something you are not. Assembling a convincing disguise takes 10 minutes and requires a Disguise Kit, but a simpler, quicker disguise might do the job if you're not trying to imitate a specific individual, at the GM's discretion.\n\nIn most cases, creatures have a chance to detect your deception only if they use the Seek action to attempt Perception checks against your Deception DC. If you attempt to directly interact with someone while disguised, the GM rolls a secret [[/act impersonate]]{Deception} check for you against that creature's Perception DC instead.\n\nIf you're disguised as a specific individual, the GM might give creatures you interact with a circumstance bonus based on how well they know the person you're imitating, or the GM might roll a secret Deception check even if you aren't directly interacting with others.\n\nSuccess You trick the creature into thinking you're the person you're disguised as. You might have to attempt a new check if your behavior changes.\n\nFailure The creature can tell you're not who you claim to be.\n\nCritical Failure The creature can tell you're not who you claim to be, and it recognizes you if it would know you without a disguise."}, {"slug": "interact", "name": "Interact", "category": "interaction", "traits": ["manipulate"], "exploration": false, "actionType": "action", "actions": 1, "description": "You use your hand or hands to manipulate an object or the terrain. You can grab an unattended or stored object, draw a weapon, swap a held item for another, open a door, or achieve a similar effect. On rare occasions, you might have to attempt a skill check to determine if your Interact action was successful."}, {"slug": "invest-an-item", "name": "Invest an Item", "category": "interaction", "traits": [], "exploration": false, "actionType": "passive", "actions": null, "description": "You invest your energy in an item with the invested trait as you don it. This process requires 1 or more Interact actions, usually taking the same amount of time it takes to don the item. Once you've Invested the Item, you benefit from its constant magical abilities as long as you meet its other requirements (for most invested items, the only other requirement is that you must be wearing the item). This investiture lasts until you remove the item.\n\nYou can invest no more than 10 items per day. If you remove an invested item, it loses its investiture. The item still counts against your daily limit after it loses its investiture. You reset the limit during your daily preparations, at which point you Invest your Items anew. If you're still wearing items you had invested the previous day, you can typically keep them invested on the new day, but they still count against your limit."}, {"slug": "investigate", "name": "Investigate", "category": "interaction", "traits": ["concentrate", "exploration"], "exploration": true, "actionType": "passive", "actions": null, "description": "You seek out information about your surroundings while traveling at half speed. You use Recall Knowledge as a secret check to discover clues among the various things you can see and engage with as you journey along. You can use any skill that has a Recall Knowledge action while Investigating, but the GM determines whether the skill is relevant to the clues you could find."}, {"slug": "leap", "name": "Leap", "category": "interaction", "traits": ["move"], "exploration": false, "actionType": "action", "actions": 1, "description": "You take a short horizontal or vertical jump. Jumping a greater distance requires using the Athletics skill for a High Jump or Long Jump.\n\n• Horizontal Jump up to 10 feet horizontally if your Speed is at least 15 feet, or up to 15 feet horizontally if your speed is at least 30 feet. You land in the space where your Leap ends (meaning you can typically clear a 5-foot gap, or a 10-foot gap if your Speed is 30 feet or more). You can't make a horizontal Leap if your Speed is less than 15 feet.\n• Vertical Jump up to 3 feet vertically and 5 feet horizontally onto an elevated surface."}, {"slug": "learn-name", "name": "Learn Name", "category": "interaction", "traits": ["downtime", "secret"], "exploration": false, "actionType": "passive", "actions": null, "description": "You spend a week trying to discover and learn a creature's name. The exact form of your effort varies depending on the skill you use, the resources you have available, and other circumstances. Decide if you are searching for the name of a specific individual or for names in general. If you're looking for the name of an individual, you must be able to clearly identify that individual; for example, \"the general leading the invasion\" is enough, but \"the person who killed the duchess\" isn't, if you don't know who killed the duchess. If you're searching for names more generally, name one creature type.\n\nThe GM chooses a DC, typically based on the level of the creature in question. If you're seeking names more generally, the DC is typically based on the level of the creature whose name the GM decides to provide, usually a creature from the chosen type of your level or lower. The GM might modify the DC of the task based on the resources you have available, or on using an unusually appropriate or inappropriate skill, or on other circumstances. Attempt a check with a skill that could be used to Recall Knowledge about the creature's type. After attempting to Learn a Name, you typically can't try to learn the name of the same individual again unless you gain access to a substantial new source of information, as determined by the GM.\n\nCritical Success You find one or more private names of the specific individual you chose, or the private name of a creature with the type you chose and a level equal to the task level. You also find hidden fragments of their true name and, at the GM's discretion, you might find a clue leading to an adventure where you can learn the rest of the true name.\n\nSuccess As critical success, except you find only one private name and don't find hidden fragments of their true name.\n\nCritical Failure If you were searching for the name of a specific individual, you find no new information and that individual becomes aware of your efforts. If you were searching for a general name of a specific type, you find a creature's name or names likely to get you in trouble, possibly the names of a different type of creature entirely."}, {"slug": "learn-a-spell", "name": "Learn a Spell", "category": "interaction", "traits": ["concentrate", "exploration"], "exploration": true, "actionType": "passive", "actions": null, "description": "Magical Traditions and Skills\n\nEach magical tradition has a corresponding skill, as shown on the table below. You must have the trained proficiency rank in a skill to use it to Identify Magic or Learn a Spell. Something without a specific tradition, such as an item with the magical trait, can be identified using any of these skills.\nMagical TraditionCorresponding Skill\nArcaneArcana\nDivineReligion\nOccultOccultism\nPrimalNature\n\nRequirements You have a spellcasting class feature, and the spell you want to learn is on your magical tradition's spell list.\n\nYou can gain access to a new spell of your tradition from someone who knows that spell or from magical writing like a spellbook or scroll. If you can cast spells of multiple traditions, you can Learn a Spell of any of those traditions, but you must use the corresponding skill to do so. For example, if you were a cleric with the bard multiclass archetype, you couldn't use Religion to add an occult spell to your bardic spell repertoire.\n\nTo learn the spell, you must do the following:\n\n• Spend 1 hour per spell rank, during which you must remain in conversation with a person who knows the spell or have the magical writing in your possession.\n• Have materials with the Price indicated in the Learning a Spell table.\n• Attempt a skill check for the skill corresponding to your tradition (DC determined by the GM, often close to the DC on the Learning a Spell Table). Uncommon or rare spells have higher DCs; full guidelines for the GM appear on page 52 of GM Core.\nLearning a SpellSpell RankPriceTypical DC\n1st or cantrip2 gp15\n2nd6 gp18\n3rd16 gp20\n4th36 gp23\n5th70 gp26\n6th140 gp28\n7th300 gp31\n8th650 gp34\n9th1,500 gp36\n10th7,000 gp41\n\nCritical Success You expend half the materials and learn the spell.\n\nSuccess You expend the materials and learn the spell.\n\nFailure You fail to learn the spell but can try again after you gain a level. The materials aren't expended.\n\nCritical Failure As failure, except you expend half the materials."}, {"slug": "lie", "name": "Lie", "category": "interaction", "traits": ["auditory", "concentrate", "linguistic", "mental", "secret"], "exploration": false, "actionType": "passive", "actions": null, "description": "You try to fool someone with an untruth. Doing so takes at least 1 round, or longer if the lie is elaborate. You roll a single [[/act lie]]{Deception} check and compare it against the Perception DC of every creature you are trying to fool. The GM might give them a circumstance bonus based on the situation and the nature of the lie you are trying to tell. Elaborate or highly unbelievable lies are much harder to get a creature to believe than simpler and more believable lies, and some lies are so big that it's impossible to get anyone to believe them.\n\nAt the GM's discretion, if a creature initially believes your lie, it might attempt a Perception check later to Sense Motive against your Deception DC to realize it's a lie. This usually happens if the creature discovers enough evidence to counter your statements.\n\nSuccess The target believes your lie.\n\nFailure The target doesn't believe your lie and gains a +4 circumstance bonus against your attempts to Lie for the duration of your conversation. The target is also more likely to be suspicious of you in the future."}, {"slug": "long-jump", "name": "Long Jump", "category": "interaction", "traits": ["move"], "exploration": false, "actionType": "action", "actions": 2, "description": "You Stride, then attempt a DC 15 [[/act long-jump]]{Athletics} check to make a long jump in the direction you were Striding. If you didn't Stride at least 10 feet, you automatically fail your check. The GM might increase or decrease this DC depending on the situation.\n\nSuccess You Leap up to a distance equal to your check result rounded down to the nearest 5 feet. You can't jump farther than your land Speed.\n\nFailure You make a normal horizontal Leap.\n\nCritical Failure You make a normal horizontal Leap, then fall and land Prone."}, {"slug": "long-term-rest", "name": "Long-Term Rest", "category": "interaction", "traits": ["downtime"], "exploration": false, "actionType": "passive", "actions": null, "description": "You can spend an entire day and night resting during downtime to recover Hit Points equal to your Constitution modifier (minimum 1) multiplied by twice your level."}, {"slug": "make-an-impression", "name": "Make an Impression", "category": "interaction", "traits": ["auditory", "concentrate", "exploration", "linguistic", "mental"], "exploration": true, "actionType": "passive", "actions": null, "description": "With at least 1 minute of conversation, during which you engage in charismatic overtures, flattery, and other acts of goodwill, you seek to make a good impression on someone to make them temporarily agreeable. At the end of the conversation, attempt a [[/act make-an-impression]]{Diplomacy} check against the Will DC of one target. You can instead choose up to five targets if you take a –2 penalty. The GM might add other bonuses or penalties based on the situation. Any impression you make lasts for only the current social interaction unless the GM decides otherwise. See Changing Attitudes below for a summary of the attitude conditions.\n\nCritical Success The target's attitude toward you improves by two steps.\n\nSuccess The target's attitude toward you improves by one step.\n\nCritical Failure The target's attitude toward you decreases by one step.\n\nChanging Attitudes\n\nYour influence on NPCs is measured with a set of attitudes that reflect how they view your character. These are only a brief summary of a creature's disposition. The GM will supply additional nuance based on the history and beliefs of the characters you're interacting with, and their attitudes can change in accordance with the story. The attitudes are detailed in the Conditions Appendix and are summarized here.\n\n• Helpful: Willing to help you and responds favorably to your requests.\n• Friendly: Has a good attitude toward you, but won't necessarily stick their neck out to help you.\n• Indifferent: Doesn't care about you either way. (Most NPCs start out indifferent.)\n• Unfriendly: Dislikes you and doesn't want to help you.\n• Hostile: Actively works against you—and might attack you just because of their dislike.\n\nNo one can ever change the attitude of a player character with these skills. You can roleplay interactions with player characters, and even use Diplomacy results if the player wants a mechanical sense of how convincing or charming a character is, but players make the ultimate decisions about how their characters respond."}, {"slug": "maneuver-in-flight", "name": "Maneuver in Flight", "category": "interaction", "traits": ["move"], "exploration": false, "actionType": "action", "actions": 1, "description": "Requirements You have a fly Speed.\n\nYou try a difficult maneuver while flying. Attempt an [[/act maneuver-in-flight]]{Acrobatics} check. The GM determines what maneuvers are possible, but they rarely allow you to move farther than your fly Speed.\n\nSuccess You succeed at the maneuver.\n\nFailure Your maneuver fails. The GM chooses if you simply can't move or if some other detrimental effect happens. The outcome should be appropriate for the maneuver you attempted (for instance, being blown off course if you were trying to fly against a strong wind).\n\nCritical Failure As failure, but the consequence is more dire.\nSample Maneuver in Flight Tasks\n• Trained steep ascent or descent\n• Expert fly against the wind\n• Master reverse direction\n• Legendary fly through gale force winds"}, {"slug": "mount", "name": "Mount", "category": "interaction", "traits": ["move"], "exploration": false, "actionType": "action", "actions": 1, "description": "Requirements You are adjacent to a creature that is at least one size larger than you and is willing to be your mount.\n\nYou move onto the creature and ride it. If you're already mounted, you can instead use this action to dismount, moving off the mount into a space adjacent to it."}, {"slug": "palm-an-object", "name": "Palm an Object", "category": "interaction", "traits": ["manipulate"], "exploration": false, "actionType": "action", "actions": 1, "description": "You pick up a small, unattended object and try not to be noticed. Roll a single [[/act palm-an-object]]{Thievery} check against the Perception DCs of all creatures who are currently observing you. You can typically only Palm Objects of negligible Bulk, though the GM might determine otherwise depending on the situation.\n\nSuccess The creature doesn't notice you Palming the Object.\n\nFailure The creature notices you Palming the Object."}, {"slug": "perform", "name": "Perform", "category": "interaction", "traits": ["concentrate"], "exploration": false, "actionType": "action", "actions": 1, "description": "When making a brief performance—one song, a quick dance, or a few jokes—you use the Perform action. This action is most useful when you want to prove your capability or impress someone quickly. Performing rarely has an impact on its own, but it might influence the DCs of subsequent Diplomacy checks against the observers, or even change their attitudes if the GM sees fit.\n\nPerformance\n\nAdditional Traits\n\nExamples\n\n[[/act perform variant=acting]]{Acting}\n\nAuditory, linguistic, and visual\n\nDrama, pantomime, puppetry\n\n[[/act perform variant=comedy]]{Comedy}\n\nAuditory, linguistic, and visual\n\nBuffoonery, joke telling, limericks\n\n[[/act perform variant=dance]]{Dance}\n\nMove and visual\n\nBallet, huara, jig, macru\n\n[[/act perform variant=keyboards]]{Play Keyboard}\n\nAuditory and manipulate\n\nHarpsichord, organ, piano\n\n[[/act perform variant=oratory]]{Oratory}\n\nAuditory and linguistic\n\nEpic, ode, poetry, storytelling\n\n[[/act perform variant=percussion]]{Play Percussion}\n\nAuditory and manipulate\n\nChimes, drum, gong, xylophone\n\n[[/act perform variant=singing]]{Singing}\n\nAuditory and linguistic\n\nBallad, chant, melody, rhyming\n\n[[/act perform variant=strings]]{Play Strings}\n\nAuditory and manipulate\n\nFiddle, harp, lute, viol\n\n[[/act perform variant=winds]]{Play Winds}\n\nAuditory and manipulate\n\nBagpipe, flute, recorder, trumpet\n\nCritical Success Your performance impresses the observers, and they're likely to share stories of your ability.\n\nSuccess You prove yourself, and observers appreciate the quality of your performance.\n\nFailure Your performance falls flat.\n\nCritical Failure You demonstrate only incompetence.\nSample Perform Tasks\n• Untrained audience of commoners\n• Trained audience of artisans\n• Expert audience of merchants or minor nobles\n• Master audience of high nobility or minor royalty\n• Legendary audience of major royalty or otherworldly beings"}, {"slug": "pick-a-lock", "name": "Pick a Lock", "category": "interaction", "traits": ["manipulate"], "exploration": false, "actionType": "action", "actions": 2, "description": "Requirements You're holding or wearing a Thieves' Toolkit.\n\nOpening a lock without a key is very similar to Disabling a Device, but the DC of the check is determined by the complexity and construction of the lock you are attempting to pick. Locks of higher quality might require multiple successes to unlock. If you lack the proper tools, the GM might let you use improvised picks, which are treated as a shoddy toolkit.\n\nCritical Success You unlock the lock, or you achieve two successes toward opening a lock that requires more than one success. You leave no trace of your tampering.\n\nSuccess You open the lock, or you achieve one success toward opening a lock that requires more than one success. You leave behind damage that indicates the lock was picked on close scrutiny.\n\nCritical Failure You break your toolkit and leave behind obvious damage. Fixing a broken toolkit requires using Crafting to Repair it or else swapping in replacement picks (costing 3 sp, or 3 gp for an infiltrator thieves' toolkit)."}, {"slug": "plummeting-roll", "name": "Plummeting Roll", "category": "defensive", "traits": [], "exploration": false, "actionType": "reaction", "actions": null, "description": "Trigger You fall at least 10 feet and take no damage from the fall\n\nEffect You tuck and roll with the motion. You land on your feet and Stride up to half your Speed"}, {"slug": "point-out", "name": "Point Out", "category": "interaction", "traits": ["auditory", "manipulate", "visual"], "exploration": false, "actionType": "action", "actions": 1, "description": "Requirements A creature is undetected by one or more of your allies but isn't undetected by you.\n\nYou indicate a creature that you can see to one or more allies, gesturing in a direction and describing the distance verbally. That creature is Hidden to your allies, rather than Undetected. This works only for allies who can see you and are in a position where they could potentially detect the target. If your allies can't hear or understand you, they must succeed at a Perception check against the creature's Stealth DC or they misunderstand and believe the target is in a different location."}, {"slug": "psychometric-assessment", "name": "Psychometric Assessment", "category": "interaction", "traits": ["concentrate", "emotion", "exploration", "mental", "occult"], "exploration": true, "actionType": "passive", "actions": null, "description": "Requirements Your bare hands are touching an object in which you detected psychometric resonance\n\nEffect You spend 1 minute concentrating on the object to get a vision of the face of the person who imbued the item with such emotion in the first place. If the associated emotion is painfully negative, you might take 1d6 mental damage, as determined by the GM."}, {"slug": "raise-a-shield", "name": "Raise a Shield", "category": "defensive", "traits": [], "exploration": false, "actionType": "action", "actions": 1, "description": "Requirements You are wielding a shield.\n\nYou position your shield to protect yourself. When you have Raised a Shield, you gain its listed circumstance bonus to AC. Your shield remains raised until the start of your next turn."}, {"slug": "ready", "name": "Ready", "category": "interaction", "traits": ["concentrate"], "exploration": false, "actionType": "action", "actions": 2, "description": "You prepare to use an action that will occur outside your turn. Choose a single action or free action you can use, and designate a trigger. Your turn then ends. If the trigger you designated occurs before the start of your next turn, you can use the chosen action as a reaction (provided you still meet the requirements to use it). You can't Ready a free action that already has a trigger.\n\nIf you have a multiple attack penalty and your readied action is an attack action, your readied attack takes the multiple attack penalty you had at the time you used Ready. This is one of the few times the multiple attack penalty applies when it's not your turn."}, {"slug": "recall-knowledge", "name": "Recall Knowledge", "category": "interaction", "traits": ["concentrate", "secret"], "exploration": false, "actionType": "action", "actions": 1, "description": "You attempt a skill check to try to remember a bit of knowledge regarding a topic related to that skill. Suggest which skill you'd like to use and ask the GM one question. The GM determines the DC. You might need to collaborate with the GM to narrow down the question or skills, and you can decide not to Recall Knowledge before committing to the action if you don't like your options.\n\nCritical Success You recall the knowledge accurately. The GM answers your question truthfully and either tells you additional information or context, or answers one follow-up question.\n\nSuccess You recall the knowledge accurately. The GM answers your question truthfully.\n\nCritical Failure You recall incorrect information. The GM answers your question falsely (or decides to give you no information, as on a failure)."}, {"slug": "refocus", "name": "Refocus", "category": "interaction", "traits": ["concentrate", "exploration"], "exploration": true, "actionType": "passive", "actions": null, "description": "Requirements You have a focus pool.\n\nYou spend 10 minutes performing deeds to restore your magical connection. This restores 1 Focus Point to your focus pool. The deeds you need to perform are specified in the class or ability that gives you your focus spells. These deeds can usually overlap with other tasks that relate to the source of your focus spells. For instance, a cleric with focus spells from a holy deity can usually Refocus while tending the wounds of their allies."}, {"slug": "release", "name": "Release", "category": "interaction", "traits": ["manipulate"], "exploration": false, "actionType": "free", "actions": null, "description": "You release something you're holding in your hand or hands. This might mean dropping an item, removing one hand from your weapon while continuing to hold it in another hand, releasing a rope suspending a chandelier, or performing a similar action. Unlike most manipulate actions, Release does not trigger reactions that can be triggered by actions with the manipulate trait (such as Reactive Strike).\n\nIf you want to prepare to Release something outside of your turn, use the Ready activity."}, {"slug": "repair", "name": "Repair", "category": "interaction", "traits": ["exploration", "manipulate"], "exploration": true, "actionType": "passive", "actions": null, "description": "Requirements You are holding or wearing a Repair Kit\n\nYou spend 10 minutes attempting to fix a damaged item, placing the item on a stable surface and using the repair kit with both hands. The GM sets the DC, but it's usually about the same DC to Repair a given item as it is to Craft it in the first place. You can't Repair a destroyed item.\n\nCritical Success You restore 10 Hit Points to the item, plus an additional 10 Hit Points per proficiency rank you have in Crafting (a total of 20 HP if you're trained, 30 HP if you're an expert, 40 HP if you're a master, or 50 HP if you're legendary).\n\nSuccess You restore 5 Hit Points to the item, plus an additional 5 per proficiency rank you have in Crafting (for a total of 10 HP if you are trained, 15 HP if you're an expert, 20 HP if you're a master, or 25 HP if you're legendary).\n\nCritical Failure You deal [[/r {2d6}]]{2d6 damage} to the item. Apply the item's Hardness to this damage."}, {"slug": "repeat-a-spell", "name": "Repeat a Spell", "category": "interaction", "traits": ["concentrate", "exploration"], "exploration": true, "actionType": "passive", "actions": null, "description": "You repeatedly cast the same spell while moving at half speed. Typically, this spell is a cantrip that you want to have in effect in the event a combat breaks out, and it must be one you can cast in 2 actions or fewer. Repeating a spell that requires making complex decisions, such as Figment, can make you Fatigued, as determined by the GM."}, {"slug": "reposition", "name": "Reposition", "category": "offensive", "traits": ["attack"], "exploration": false, "actionType": "action", "actions": 1, "description": "Requirements You either have at least one hand free, or you're grabbing or restraining the target. The target can't be more than one size larger than you.\n\nYou muscle a creature or object around. Attempt an [[/act reposition]]{Athletics} check against the target's Fortitude DC.\n\nCritical Success You move the creature up to 10 feet. It must remain within your reach during this movement, and you can't move it into or through obstacles.\n\nSuccess You move the target up to 5 feet. It must remain within your reach during this movement, and you can't move it into or through obstacles.\n\nCritical Failure The target can move you up to 5 feet as though it successfully Repositioned you."}, {"slug": "request", "name": "Request", "category": "interaction", "traits": ["auditory", "concentrate", "linguistic", "mental"], "exploration": false, "actionType": "action", "actions": 1, "description": "You can make a request of a creature that's friendly or helpful to you. You must couch the request in terms that the target would accept given their current attitude toward you. The GM sets the DC of the [[/act request]]{Diplomacy} check based on the difficulty of the request. Some requests are unsavory or impossible, and even a helpful NPC would never agree to them.\n\nCritical Success The target agrees to your request without qualifications.\n\nSuccess The target agrees to your request, but they might demand added provisions or alterations to the request.\n\nFailure The target refuses the request, though they might propose an alternative that is less extreme.\n\nCritical Failure Not only does the target refuse the request, but their attitude toward you decreases by one step due to the temerity of the request."}, {"slug": "research", "name": "Research", "category": "interaction", "traits": ["concentrate", "exploration"], "exploration": true, "actionType": "passive", "actions": null, "description": "You comb through information to learn more about the topic at hand. Choose your research topic, section of the library, or other division depending on the form of research, and attempt a skill check. The skills to use and the DC for the check depend on the specific research task, and the Research activity gains any traits appropriate to the type of research (such as linguistic when perusing books).\n\nCritical Success You gain 2 RP.\n\nSuccess You gain 1 RP.\n\nCritical Failure You make a false discovery and lose 1 RP."}, {"slug": "retraining", "name": "Retraining", "category": "interaction", "traits": ["downtime"], "exploration": false, "actionType": "passive", "actions": null, "description": "Retraining offers a way to alter your character choices, which is helpful when you want to take your character in a new direction or change decisions that didn't meet your expectations. You can retrain feats, skills, and some selectable class features. You can't retrain your ancestry, heritage, background, class, or attribute modifiers. You can't perform other downtime activities while retraining.\n\nRetraining usually requires you to spend time learning from a teacher, whether that entails physical training, studying at a library, or falling into shared magical trances. Your GM determines whether you can get proper training or whether something can be retrained at all. In some cases, you'll have to pay your instructor. Some abilities can be difficult or impossible to retrain (for instance, witch can retrain their patron only in extraordinary circumstances).\n\nWhen retraining, you generally can't make choices you couldn't make when you selected the original option. For instance, you can't replace a skill feat you chose at 2nd level for a 4th-level one, or for one that requires prerequisites you didn't meet at the time you took the original feat. If you don't remember whether you met the prerequisites at the time, ask your GM to make the call. If you cease to meet the prerequisites for an ability due to retraining, you can't use that ability. You might need to retrain several abilities in sequence in order to get all the abilities you want.\nFeats\n\nYou can spend a week of downtime retraining to swap out one of your feats. Remove the old feat and replace it with another of the same type. For example, you could swap a skill feat for another skill feat, but not for a wizard feat.\nSkills\n\nYou can spend a week of downtime retraining to swap out one of your skill increases. Reduce your proficiency rank in the skill losing its increase by one step and increase your proficiency rank in another skill by one step. The new proficiency rank has to be equal to or lower than the proficiency rank you traded away. For instance, if your bard is a master in Performance and Stealth, and an expert in Occultism, you could reduce the character's proficiency in Stealth to expert and become a master in Occultism, but you couldn't reassign that skill increase to become legendary in Performance. Keep track of your level when you reassign skill increases; the level at which your skill proficiencies changed can influence your ability to retrain feats with skill prerequisites.\n\nYou can also spend a week to retrain an initial trained skill you selected during character creation.\nClass Features\n\nYou can change a class feature that required a choice, making a different choice instead. Some, like changing a spell in your spell repertoire, take a week. The GM will tell you how long it takes to retrain larger choices like a druid order or a wizard school—it is always at least a month."}, {"slug": "scout", "name": "Scout", "category": "interaction", "traits": ["concentrate", "exploration"], "exploration": true, "actionType": "passive", "actions": null, "description": "You scout ahead and behind the group to watch danger, moving at half speed. At the start of the next encounter, every creature in your party gains a +1 circumstance bonus to their initiative rolls.\n\nEffect: Scout"}, {"slug": "search", "name": "Search", "category": "interaction", "traits": ["concentrate", "exploration"], "exploration": true, "actionType": "passive", "actions": null, "description": "You Seek meticulously for hidden doors, concealed hazards, and so on. You can usually make an educated guess as to which locations are best to check and move at half speed, but if you want to be thorough and guarantee you checked everything, you need to travel at a Speed of no more than 300 feet per minute, or 150 feet per minute to ensure you check everything before you walk into it. You can always move more slowly while Searching to cover the area more thoroughly, and the Expeditious Search feat increases these maximum Speeds. If you come across a secret door, item, or hazard while Searching, the GM will attempt a free secret check to Seek to see if you notice the hidden object or hazard. In locations with many objects to search, you have to stop and spend significantly longer to search thoroughly."}, {"slug": "seek", "name": "Seek", "category": "interaction", "traits": ["concentrate", "secret"], "exploration": false, "actionType": "action", "actions": 1, "description": "You scan an area for signs of creatures or objects, possibly including secret doors or hazards. Choose an area to scan. The GM determines the area you can scan with one Seek action—almost always 30 feet or less in any dimension. The GM might impose a penalty if you search far away from you or adjust the number of actions it takes to Seek a particularly cluttered area.\n\nThe GM attempts a single secret [[/act seek]]{Perception} check for you and compares the result to the Stealth DCs of any Undetected or Hidden creatures in the area, or the DC to detect each object in the area (as determined by the GM or by someone Concealing the Object). A creature you detect might remain hidden, rather than becoming Observed, if you're using an imprecise sense or if an effect (such as Invisibility) prevents the subject from being observed.\n\nCritical Success Any undetected or hidden creature you critically succeeded against becomes observed by you. You learn the location of objects in the area you critically succeeded against.\n\nSuccess Any undetected creature you suceeded against becomes hidden from you instead of undetected, and any hidden creature you succeeded against becomes observed by you. You learn the location of any object or get a clue to its whereabouts, as determined by the GM."}, {"slug": "sense-direction", "name": "Sense Direction", "category": "interaction", "traits": ["exploration", "secret"], "exploration": true, "actionType": "passive", "actions": null, "description": "Using the stars, the position of the sun, traits of the geography or flora, or the behavior of fauna, you can stay oriented in the wild. Typically, you attempt a [[/act sense-direction]]{Survival} check only once per day, but some environments or changes might necessitate rolling more often. The GM determines the DC and how long this activity takes (usually just a minute or so). More unusual locales or those you're unfamiliar with might require you to have a minimum proficiency rank to Sense Direction. Without a Compass, you take a –2 item penalty to checks to Sense Direction.\n\nCritical Success You get an excellent sense of where you are. If you are in an environment with cardinal directions, you know them exactly.\n\nSuccess You gain enough orientation to avoid becoming hopelessly lost. If you are in an environment with cardinal directions, you have a sense of those directions.\nSense Direction Tasks\n• Untrained determine a cardinal direction using the sun\n• Trained find an overgrown path in a forest\n• Expert navigate a hedge maze\n• Master navigate a byzantine labyrinth or relatively featureless desert\n• Legendary navigate an ever-changing dream realm"}, {"slug": "sense-motive", "name": "Sense Motive", "category": "interaction", "traits": ["concentrate", "secret"], "exploration": false, "actionType": "action", "actions": 1, "description": "You try to tell whether a creature's behavior is abnormal. Choose one creature and assess it for odd body language, signs of nervousness, and other indicators that it might be trying to deceive someone. The GM attempts a single secret [[/act sense-motive]]{Perception} check for you and compares the result to the Deception DC of the creature, the DC of a spell affecting the creature's mental state, or another appropriate DC determined by the GM. You typically can't try to Sense the Motive of the same creature again until the situation changes significantly.\n\nCritical Success You determine the creature's true intentions and get a solid idea of any mental magic affecting it.\n\nSuccess You can tell whether the creature is behaving normally, but you don't know its exact intentions or what magic might be affecting it.\n\nFailure You detect what a deceptive creature wants you to believe. If they're not being deceptive, you believe they're behaving normally.\n\nCritical Failure You get a false sense of the creature's intentions."}, {"slug": "shove", "name": "Shove", "category": "offensive", "traits": ["attack"], "exploration": false, "actionType": "action", "actions": 1, "description": "Requirements You have at least one hand free. The target can't be more than one size larger than you.\n\nYou push a creature away from you. Attempt an [[/act shove]]{Athletics} check against your target's Fortitude DC.\n\nCritical Success You push your target up to 10 feet away from you. You can Stride after it, but you must move the same distance and in the same direction.\n\nSuccess You push your target back 5 feet. You can Stride after it, but you must move the same distance and in the same direction.\n\nCritical Failure You lose your balance, fall, and land Prone."}, {"slug": "sneak", "name": "Sneak", "category": "defensive", "traits": ["move", "secret"], "exploration": false, "actionType": "action", "actions": 1, "description": "You attempt to move to another place while becoming or staying undetected. Stride up to half your Speed. (You can use Sneak while Burrowing, Climbing, Flying, or Swimming instead of Striding if you have the corresponding movement type; you must move at half that Speed.)\n\nAt the end of your movement, the GM rolls your [[/act sneak]] check in secret and compares the result to the Perception DC of each creature you were Hidden from or Undetected by at the start of your movement. If you have cover or greater cover from the creature throughout your Stride, you gain the +2 circumstance bonus from cover (or +4 from greater cover) to your Stealth check. Because you're moving, the bonus increase from Taking Cover doesn't apply. You don't get to roll against a creature if, at the end of your movement, you neither are Concealed from it nor have cover or greater cover against it. You automatically become observed by such a creature.\n\nSuccess You're undetected by the creature during your movement and remain undetected by the creature at the end of it.\n\nYou become observed as soon as you do anything other than Hide, Sneak, or Step. If you attempt to Strike a creature, the creature remains Off Guard against that attack, and you then become observed. If you do anything else, you become observed just before you act unless the GM determines otherwise. The GM might allow you to perform a particularly unobtrusive action without being noticed, possibly requiring another Stealth check. If you speak or make a deliberate loud noise, you become hidden instead of undetected.\n\nIf a creature uses Seek and you become hidden to it as a result, you must Sneak if you want to become undetected by that creature again.\n\nFailure A telltale sound or other sign gives your position away, though you still remain unseen. You're hidden from the creature throughout your movement and remain so.\n\nCritical Failure You're spotted! You're observed by the creature throughout your movement and remain so. If you're Invisible and were hidden from the creature, instead of being observed you're hidden throughout your movement and remain so."}, {"slug": "squeeze", "name": "Squeeze", "category": "interaction", "traits": ["exploration", "move"], "exploration": true, "actionType": "passive", "actions": null, "description": "You contort yourself to [[/act squeeze]]{squeeze} through a space so small you can barely fit through. This action is for exceptionally small spaces; many tight spaces are difficult terrain that you can move through more quickly and without a check.\n\nCritical Success You squeeze through the tight space in 1 minute per 10 feet of squeezing.\n\nSuccess You squeeze through in 1 minute per 5 feet.\n\nCritical Failure You become stuck in the tight space. While you're stuck, you can spend 1 minute attempting another Acrobatics check at the same DC. Any result on that check other than a critical failure causes you to become unstuck.\nSample Squeeze Tasks\n• Trained space barely fitting your shoulders\n• Master space barely fitting your head"}, {"slug": "stand", "name": "Stand", "category": "interaction", "traits": ["move"], "exploration": false, "actionType": "action", "actions": 1, "description": "You stand up from Prone."}, {"slug": "steal", "name": "Steal", "category": "interaction", "traits": ["manipulate"], "exploration": false, "actionType": "action", "actions": 1, "description": "You try to take a small object from another creature without being noticed. Typically, you can Steal only an object of negligible Bulk, you must have a free hand, and you automatically fail if the creature who has the object is in combat or on guard.\n\nAttempt a [[/act steal]]{Thievery} check to determine if you successfully Steal the object. The DC is usually the Perception DC of the creature wearing the object. It's easiest to steal an object that is worn but not closely guarded (like a loosely carried pouch filled with coins, or an object within such a pouch). The GM might increase the DC if the object is protected or if the nature of the object makes it harder to steal (such as a very small item in a large pack, or a sheet of parchment mixed in with other documents). For instance, the DC is typically 5 higher if the object is in a pocket, held in a creature's hand, or similarly protected.\n\nYou might also need to compare your Thievery check result against the Perception DCs of observers other than the person wearing the object. The GM might impose a circumstance penalty to the DCs of observers who are distracted.\n\nSuccess You steal the item without the bearer noticing, or an observer doesn't see you take or attempt to take the item.\n\nFailure The item's bearer notices your attempt before you can take the object, or an observer sees you take or attempt to take the item. The GM determines the response of any creature that notices your theft."}, {"slug": "step", "name": "Step", "category": "interaction", "traits": ["move"], "exploration": false, "actionType": "action", "actions": 1, "description": "Requirements Your Speed is at least 10 feet.\n\nYou carefully move 5 feet. Unlike most types of movement, Stepping doesn't trigger reactions, such as Reactive Strike, that can be triggered by move actions or upon leaving or entering a square.\n\nYou can't Step into difficult terrain, and you can't Step using a Speed other than your land Speed."}, {"slug": "stride", "name": "Stride", "category": "interaction", "traits": ["move"], "exploration": false, "actionType": "action", "actions": 1, "description": "You move up to your Speed."}, {"slug": "strike", "name": "Strike", "category": "offensive", "traits": ["attack"], "exploration": false, "actionType": "action", "actions": 1, "description": "You attack with a weapon you're wielding or with an unarmed attack, targeting one creature within your reach (for a melee attack) or within range (for a ranged attack). Roll an attack roll using the attack modifier for the weapon or unarmed attack you're using, and compare the result to the target creature's AC to determine the effect.\n\nCritical Success You make a damage roll according to the weapon or unarmed attack and deal double damage.\n\nSuccess You make a damage roll according to the weapon or unarmed attack and deal damage."}, {"slug": "subsist", "name": "Subsist", "category": "interaction", "traits": ["downtime"], "exploration": false, "actionType": "passive", "actions": null, "description": "You try to provide food and shelter for yourself, and possibly others as well, with a standard of living. This typically uses [[/act subsist statistic=society]]{Society} if you're in a settlement or [[/act subsist statistic=survival]]{Survival} if you're in the wild. The GM determines the DC based on the nature of the place where you're trying to Subsist. You might need a minimum proficiency rank to Subsist in particularly strange environments. Unlike most downtime activities, you can Subsist after 8 hours or less of exploration, but if you do, you take a –5 penalty.\nSample Subsist Tasks\n• Untrained a lush forest with calm weather or a large city with plentiful resources\n• Trained typical hillside or village\n• Expert typical mountains or insular hamlet\n• Master typical desert or city under siege\n• Legendary barren wasteland or city of undead\n\nCritical Success You either provide a subsistence living for yourself and one additional creature, or you improve your own food and shelter, granting yourself a comfortable living.\n\nSuccess You find enough food and shelter with basic protection from the elements to provide you a subsistence living.\n\nFailure You're exposed to the elements and don't get enough food, becoming Fatigued until you attain sufficient food and shelter.\n\nCritical Failure You attract trouble, eat something you shouldn't, or otherwise worsen your situation. You take a –2 circumstance penalty to checks to Subsist for 1 week. You don't find any food at all; if you don't have any stored up, you're in danger of starving or dying of thirst if you continue failing.\n\nEffect: Adverse Subsist Situation"}, {"slug": "sustain", "name": "Sustain", "category": "interaction", "traits": ["concentrate"], "exploration": false, "actionType": "action", "actions": 1, "description": "Choose one of your effects that has a sustained duration or lists a special benefit when you Sustain it. Most such effects come from spells or magic item activations. If the effect has a sustained duration, its duration extends until the end of your next turn. (Sustaining more than once in the same turn doesn't extend the duration to subsequent turns.) If an ability can be sustained but doesn't list how long, it can be sustained up to 10 minutes.\n\nAn effect might list an additional benefit that occurs if you Sustain it, and this can even appear on effects that don't have a sustained duration. If the effect has both a special benefit and a sustained duration, your Sustain action extends the duration as well as having the special benefit.\n\nIf your Sustain action is disrupted, the ability ends."}, {"slug": "sustain-an-effect", "name": "Sustain an Effect", "category": "interaction", "traits": ["concentrate", "exploration"], "exploration": true, "actionType": "passive", "actions": null, "description": "You Sustain one effect with a sustained duration while moving at half speed. Most such effects can be sustained for 10 minutes, though some specify they can be sustained for a different duration. Sustaining an effect that requires making complex decisions, such as Spiritual Armament, can make you Fatigued, as determined by the GM."}, {"slug": "swim", "name": "Swim", "category": "interaction", "traits": ["move"], "exploration": false, "actionType": "action", "actions": 1, "description": "You attempt an [[/act swim]]{Athletics} check to move a maximum distance of 10 feet through water. The GM determines the DC based on the turbulence or danger of the water; in most instances of calm water, you get an automatic critical success. If your land Speed is 40 feet or higher, increase the maximum possible distance by 5 feet for every 20 feet of Speed above 20 feet.\n\nIf you end your turn in water and haven't succeeded at a Swim action that turn, you sink 10 feet or get moved by the current, as determined by the GM. This doesn't apply if your last action on your turn was to enter the water.\n\nCritical Success You move through the water, increasing the maximum distance by 5 feet.\n\nSuccess You move through the water.\n\nCritical Failure You make no progress. If you're holding your breath, you lose 1 round of air.\nSample Swim Tasks\n\nUntrained lake or other still water\n\nTrained flowing water, like a river\n\nExpert swiftly flowing river\n\nMaster stormy sea\n\nLegendary maelstrom, waterfall"}, {"slug": "take-cover", "name": "Take Cover", "category": "interaction", "traits": [], "exploration": false, "actionType": "action", "actions": 1, "description": "Requirements You are benefiting from standard cover, are near a feature that allows you to take cover, or are Prone.\n\nYou press yourself against a wall or duck behind an obstacle to take better advantage of cover. If you would have standard cover, you instead gain greater cover, which provides a +4 circumstance bonus to AC; to Reflex saves against area effects; and to Stealth checks to [[/act hide]], [[/act sneak]], or otherwise avoid detection. Otherwise, you gain standard cover (a +2 circumstance bonus instead). If you're prone, you gain greater cover against ranged attacks. Take Cover lasts until you move from your current space, use an attack action, become Unconscious, or end it as a free action."}, {"slug": "track", "name": "Track", "category": "interaction", "traits": ["concentrate", "exploration", "move"], "exploration": true, "actionType": "passive", "actions": null, "description": "You follow tracks, moving at up to half your travel Speed, using the Travel Speed rules. After a successful check to Track, you can continue following the tracks at half your Speed without attempting additional checks for up to 1 hour.\n\nIn some cases, you might Track in an encounter. In this case, Track is a single action and doesn't have the exploration trait, but you might need to roll more often because you're in a tense situation. The GM determines how often you must attempt this check.\n\nYou attempt your [[/act track]]{Survival} check when you start Tracking, once every hour you continue tracking, and any time something significant changes in the trail. The GM determines the DCs for such checks, depending on the freshness of the trail, the weather, and the type of ground.\nSample Track Tasks\n• Untrained the path of a large army following a road\n• Trained relatively fresh tracks of a rampaging bear through the plains\n• Expert a nimble panther's tracks through a jungle, tracks obscured by rainfall\n• Master tracks obscured by winter snow, tracks of a mouse or smaller creature, tracks left on surfaces that can't hold prints like bare rock\n• Legendary old tracks through a windy desert's sands, tracks obscured by a major blizzard or hurricane\n\nSuccess You find the trail or continue to follow the one you're already following.\n\nFailure You lose the trail but can try again after a 1-hour delay.\n\nCritical Failure You lose the trail and can't try again for 24 hours."}, {"slug": "treat-disease", "name": "Treat Disease", "category": "interaction", "traits": ["downtime", "manipulate"], "exploration": false, "actionType": "passive", "actions": null, "description": "Requirements You're wearing or holding a Healer's Toolkit\n\nYou spend at least 8 hours caring for a diseased creature. Attempt a [[/act treat-disease]]{Medicine} check against the disease's DC. After you attempt to Treat a Disease for a creature, you can't try again until after that creature's next save against the disease.\n\nCritical Success You grant the creature a +4 circumstance bonus to its next saving throw against the disease.\n\nSuccess You grant the creature a +2 circumstance bonus to its next saving throw against the disease.\n\nCritical Failure Your efforts cause the creature to take a −2 circumstance penalty to its next save against the disease.\n\nEffect: Treat Disease"}, {"slug": "treat-poison", "name": "Treat Poison", "category": "interaction", "traits": ["manipulate"], "exploration": false, "actionType": "action", "actions": 1, "description": "Requirements You're wearing or holding a Healer's Toolkit\n\nYou treat a patient to prevent the spread of poison. Attempt a [[/act treat-poison]]{Medicine} check against the poison's DC. After you attempt to Treat a Poison for a creature, you can't try again until after the next time that creature attempts a save against the poison.\n\nCritical Success You grant the creature a +4 circumstance bonus to its next saving throw against the poison.\n\nSuccess You grant the creature a +2 circumstance bonus to its next saving throw against the poison.\n\nCritical Failure Your efforts cause the creature to take a −2 circumstance penalty to its next save against the poison.\n\nEffect: Treat Poison"}, {"slug": "treat-wounds", "name": "Treat Wounds", "category": "interaction", "traits": ["exploration", "healing", "manipulate"], "exploration": true, "actionType": "passive", "actions": null, "description": "Requirements You're wearing or holding a Healer's Toolkit.\n\nYou spend 10 minutes treating one injured living creature (targeting yourself, if you so choose). The target is then temporarily immune to Treat Wounds actions for 1 hour, but this interval overlaps with the time you spent treating (so a patient can be treated once per hour, not once per 70 minutes).\n\nThe Medicine check DC is usually 15, though the GM might adjust it based on the circumstances, such as treating a patient outside in a storm, or treating magically cursed wounds. If you're an expert in Medicine, you can instead attempt a DC 20 check to increase the Hit Points regained by 10; if you're a master of Medicine, you can instead attempt a DC 30 check to increase the Hit Points regained by 30; and if you're legendary, you can instead attempt a DC 40 check to increase the Hit Points regained by 50. The damage dealt on a critical failure remains the same.\n\nIf you succeed at your check, you can continue treating the target to grant additional healing. If you treat it for a total of 1 hour, double the Hit Points it regains from Treat Wounds.\n\nThe result of your Medicine check determines how many Hit Points the target regains.\n\nTreat Wounds\n\nCritical Success The target regains [[/r 4d8[healing] #Treat Wounds]] Hit Points and loses the Wounded condition.\n\nSuccess The target regains [[/r 2d8[healing] #Treat Wounds]] Hit Points, and loses the wounded condition.\n\nCritical Failure The target takes [[/r 1d8[damage] #Treat Wounds (Critical Failure)]] damage."}, {"slug": "trip", "name": "Trip", "category": "offensive", "traits": ["attack"], "exploration": false, "actionType": "action", "actions": 1, "description": "Requirements You have at least one hand free. Your target can't be more than one size larger than you.\n\nYou try to knock a creature to the ground. Attempt an [[/act trip]]{Athletics} check against the target's Reflex DC.\n\nCritical Success The target falls, lands Prone, and takes 1d6 bludgeoning damage.\n\nSuccess The target falls and lands prone.\n\nCritical Failure You lose your balance, fall, and land prone."}, {"slug": "tumble-through", "name": "Tumble Through", "category": "interaction", "traits": ["move"], "exploration": false, "actionType": "action", "actions": 1, "description": "You Stride up to your Speed. During this movement, you can try to move through the space of one enemy. Attempt an [[/act tumble-through]]{Acrobatics} check against the enemy's Reflex DC as soon as you try to enter its space. You can Tumble Through using Climb, Fly, Swim, or another action instead of Stride in the appropriate environment.\n\nSuccess You move through the enemy's space, treating the squares in its space as difficult terrain (every 5 feet costs 10 feet of movement). If you don't have enough Speed to move all the way through its space, you get the same effect as a failure.\n\nFailure Your movement ends, and you trigger reactions as if you had moved out of the square you started in."}];
|
|
506
|
+
|
|
507
|
+
</script>
|
|
508
|
+
<script>
|
|
509
|
+
/* ============================================================
|
|
510
|
+
CONTENT — the primer itself. Everything you might want to
|
|
511
|
+
reword for your table lives in this file; engine.js only
|
|
512
|
+
renders it. Rules text quoted at the table is NOT here: it
|
|
513
|
+
comes verbatim from data/reference.generated.js (Foundry
|
|
514
|
+
pf2e, Paizo ORC/OGL) so the primer can't drift from the book.
|
|
515
|
+
============================================================ */
|
|
516
|
+
|
|
517
|
+
/* Swap these five strings to re-flavour every example in the app. */
|
|
518
|
+
const FLAVOR = {
|
|
519
|
+
ship: "Salt Mercy",
|
|
520
|
+
port: "Bracklewater",
|
|
521
|
+
npc: "Quartermaster Bel",
|
|
522
|
+
npcRole: "runs the dockside stores",
|
|
523
|
+
foe: "Deck Thug",
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
/* ============================================================
|
|
527
|
+
PREGENS — two level-1 characters the demos roll with.
|
|
528
|
+
Every number is hand-computed from the level-1 build shown in
|
|
529
|
+
`build`, so a player can check the arithmetic against the book:
|
|
530
|
+
proficiency bonus = level + rank (T2 / E4 / M6 / L8), and
|
|
531
|
+
untrained adds nothing at all.
|
|
532
|
+
============================================================ */
|
|
533
|
+
const PREGENS = [
|
|
534
|
+
{
|
|
535
|
+
id: "mari",
|
|
536
|
+
name: 'Marisol "Mari" Vane',
|
|
537
|
+
short: "Mari",
|
|
538
|
+
cls: "Fighter",
|
|
539
|
+
ancestry: "Human",
|
|
540
|
+
level: 1,
|
|
541
|
+
blurb: `Deckhand off the ${FLAVOR.ship} who got tired of being the one holding the rope. Boarding sword, steel shield, short fuse.`,
|
|
542
|
+
build: "Str 18 · Dex 14 · Con 14 · Int 10 · Wis 12 · Cha 10 — scale mail, steel shield, boarding sword (longsword) & dagger",
|
|
543
|
+
abilities: { str: 4, dex: 2, con: 2, int: 0, wis: 1, cha: 0 },
|
|
544
|
+
hpParts: { ancestry: 8, cls: 10, con: 2 },
|
|
545
|
+
armor: { item: 3, dexCap: 2, rank: "trained", name: "scale mail" },
|
|
546
|
+
hp: 20,
|
|
547
|
+
ac: 18,
|
|
548
|
+
acNote: "10 + 2 Dex (capped by scale mail) + 3 armour + 3 proficiency (trained, +2, plus your level)",
|
|
549
|
+
shieldBonus: 2,
|
|
550
|
+
perception: 6,
|
|
551
|
+
perceptionRank: "expert",
|
|
552
|
+
saves: { fortitude: 7, reflex: 7, will: 4 },
|
|
553
|
+
saveRanks: { fortitude: "expert", reflex: "expert", will: "trained" },
|
|
554
|
+
skills: {
|
|
555
|
+
athletics: 7, acrobatics: 5, intimidation: 3,
|
|
556
|
+
medicine: 4, survival: 4, "sailing lore": 3,
|
|
557
|
+
},
|
|
558
|
+
classDC: 17,
|
|
559
|
+
hero: 1,
|
|
560
|
+
tag: "the one who hits things",
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
id: "sable",
|
|
564
|
+
name: "Sable Quist",
|
|
565
|
+
short: "Sable",
|
|
566
|
+
cls: "Bard",
|
|
567
|
+
ancestry: "Gnome",
|
|
568
|
+
level: 1,
|
|
569
|
+
blurb: `Shanty-singer, rumour-broker, and the reason half of ${FLAVOR.port} still speaks to the crew.`,
|
|
570
|
+
build: "Str 10 · Dex 14 · Con 14 · Int 12 · Wis 10 · Cha 18 — leather armour, rapier, a very loud voice",
|
|
571
|
+
abilities: { str: 0, dex: 2, con: 2, int: 1, wis: 0, cha: 4 },
|
|
572
|
+
hpParts: { ancestry: 8, cls: 8, con: 2 },
|
|
573
|
+
armor: { item: 1, dexCap: 4, rank: "trained", name: "leather armour" },
|
|
574
|
+
hp: 18,
|
|
575
|
+
ac: 16,
|
|
576
|
+
acNote: "10 + 2 Dex + 1 armour + 3 proficiency (trained, +2, plus your level)",
|
|
577
|
+
shieldBonus: 0,
|
|
578
|
+
perception: 3,
|
|
579
|
+
perceptionRank: "trained",
|
|
580
|
+
saves: { fortitude: 5, reflex: 5, will: 5 },
|
|
581
|
+
saveRanks: { fortitude: "trained", reflex: "trained", will: "expert" },
|
|
582
|
+
skills: {
|
|
583
|
+
diplomacy: 7, performance: 7, deception: 7, occultism: 4,
|
|
584
|
+
society: 4, stealth: 5, acrobatics: 5, "sailing lore": 4,
|
|
585
|
+
},
|
|
586
|
+
spellAttack: 7,
|
|
587
|
+
spellDC: 17,
|
|
588
|
+
hero: 1,
|
|
589
|
+
tag: "the one who talks and sings",
|
|
590
|
+
},
|
|
591
|
+
];
|
|
592
|
+
|
|
593
|
+
/* Rank bonuses, and which ability each skill runs off. The demos and the
|
|
594
|
+
test suite both use these to re-derive every number on both sheets:
|
|
595
|
+
modifier = ability + rank + level (untrained adds nothing at all). */
|
|
596
|
+
const RANK_BONUS = { untrained: 0, trained: 2, expert: 4, master: 6, legendary: 8 };
|
|
597
|
+
const SKILL_ABILITY = {
|
|
598
|
+
acrobatics: "dex", arcana: "int", athletics: "str", crafting: "int",
|
|
599
|
+
deception: "cha", diplomacy: "cha", intimidation: "cha", medicine: "wis",
|
|
600
|
+
nature: "wis", occultism: "int", performance: "cha", religion: "wis",
|
|
601
|
+
society: "int", stealth: "dex", survival: "wis", thievery: "dex",
|
|
602
|
+
"sailing lore": "int",
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
/* ============================================================
|
|
606
|
+
THE TEACHING DUMMY — not an official statblock. A plausible
|
|
607
|
+
level-1 brawler with round numbers, so the demos have
|
|
608
|
+
something to roll against. Its DCs are 10 + its modifier,
|
|
609
|
+
which is exactly how the GM turns any modifier into a DC.
|
|
610
|
+
============================================================ */
|
|
611
|
+
const FOE = {
|
|
612
|
+
name: FLAVOR.foe,
|
|
613
|
+
blurb: "A bruiser off a rival crew. Made up for this page — your GM's monsters come from the Monster Core.",
|
|
614
|
+
ac: 16,
|
|
615
|
+
hp: 20,
|
|
616
|
+
saves: { fortitude: 7, reflex: 5, will: 3 },
|
|
617
|
+
perception: 5,
|
|
618
|
+
strike: { label: "cutlass", bonus: 8, dmg: "1d6+3", dtype: "slashing" },
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
/* ============================================================
|
|
622
|
+
TURN BUILDER — the palette each pregen can spend actions on.
|
|
623
|
+
cost how many of the three actions it eats
|
|
624
|
+
attack counts toward the multiple attack penalty
|
|
625
|
+
agile agile weapons take the smaller MAP (-4/-8)
|
|
626
|
+
check a skill check against one of the foe's DCs
|
|
627
|
+
============================================================ */
|
|
628
|
+
const TURN_ACTIONS = {
|
|
629
|
+
mari: [
|
|
630
|
+
{ key: "sword", label: "Strike — boarding sword", cost: 1, slug: "strike", attack: true,
|
|
631
|
+
bonus: 9, dmg: "1d8+4", dtype: "slashing", kind: "attack" },
|
|
632
|
+
{ key: "dagger", label: "Strike — dagger (agile)", cost: 1, slug: "strike", attack: true, agile: true,
|
|
633
|
+
bonus: 9, dmg: "1d4+4", dtype: "piercing", kind: "attack",
|
|
634
|
+
hint: "Agile weapons take a smaller multiple attack penalty: −4 then −8." },
|
|
635
|
+
{ key: "trip", label: "Trip", cost: 1, slug: "trip", attack: true, kind: "attack",
|
|
636
|
+
check: { skill: "Athletics", bonus: 7, vs: "reflex" }, effect: "trip",
|
|
637
|
+
hint: "Trip is an attack too — it takes the multiple attack penalty and it adds to it." },
|
|
638
|
+
{ key: "grapple", label: "Grapple", cost: 1, slug: "grapple", attack: true, kind: "attack",
|
|
639
|
+
check: { skill: "Athletics", bonus: 7, vs: "fortitude" }, effect: "grapple" },
|
|
640
|
+
{ key: "demoralize", label: "Demoralize", cost: 1, slug: "demoralize", kind: "skill",
|
|
641
|
+
check: { skill: "Intimidation", bonus: 3, vs: "will" }, effect: "demoralize",
|
|
642
|
+
hint: "Not an attack — no multiple attack penalty, ever." },
|
|
643
|
+
{ key: "raise", label: "Raise a Shield", cost: 1, slug: "raise-a-shield", kind: "defend", effect: "raise",
|
|
644
|
+
hint: "+2 circumstance to your AC until the start of your next turn." },
|
|
645
|
+
{ key: "stride", label: "Stride", cost: 1, slug: "stride", kind: "move" },
|
|
646
|
+
{ key: "step", label: "Step", cost: 1, slug: "step", kind: "move",
|
|
647
|
+
hint: "5 feet, and it doesn't trigger reactions." },
|
|
648
|
+
{ key: "seek", label: "Seek", cost: 1, slug: "seek", kind: "skill", effect: "seek" },
|
|
649
|
+
{ key: "interact", label: "Interact — draw a weapon", cost: 1, slug: "interact", kind: "other" },
|
|
650
|
+
],
|
|
651
|
+
sable: [
|
|
652
|
+
{ key: "anthem", label: "Courageous Anthem", cost: 1, kind: "spell", effect: "anthem",
|
|
653
|
+
spell: { name: "Courageous Anthem", rank: "cantrip (focus)", text: "You and all allies in the area gain a +1 status bonus to attack rolls, damage rolls, and saves against fear effects." },
|
|
654
|
+
hint: "A focus cantrip. One action, and it improves every attack you make afterwards this round." },
|
|
655
|
+
{ key: "tk", label: "Telekinetic Projectile", cost: 2, kind: "spell", attack: true, spellAttack: true,
|
|
656
|
+
bonus: 7, dmg: "2d6", dtype: "bludgeoning",
|
|
657
|
+
spell: { name: "Telekinetic Projectile", rank: "cantrip", text: "Make a spell attack roll. If you hit, you deal 2d6 bludgeoning, piercing, or slashing damage — as appropriate for the object you hurled." },
|
|
658
|
+
hint: "Spell attack rolls take the multiple attack penalty just like a Strike." },
|
|
659
|
+
{ key: "fear", label: "Cast Fear (rank 1)", cost: 2, kind: "spell", effect: "fear",
|
|
660
|
+
save: { name: "Will", dc: 17 },
|
|
661
|
+
spell: { name: "Fear", rank: "rank 1", text: "The target attempts a Will save. Critical Success: unaffected. Success: frightened 1. Failure: frightened 2. Critical Failure: frightened 3 and fleeing for 1 round." },
|
|
662
|
+
hint: "You don't roll to hit — the target rolls to resist. Your spell DC is the number it has to beat." },
|
|
663
|
+
{ key: "soothe", label: "Cast Soothe (rank 1)", cost: 2, kind: "spell", effect: "soothe",
|
|
664
|
+
spell: { name: "Soothe", rank: "rank 1", text: "The target regains 1d10+4 Hit Points and gains a +2 status bonus to saves against mental effects for 1 minute." },
|
|
665
|
+
hint: "Healing is a spell slot well spent when someone is dying." },
|
|
666
|
+
{ key: "feint", label: "Feint", cost: 1, slug: "feint", kind: "skill",
|
|
667
|
+
check: { skill: "Deception", bonus: 7, vs: "perception" }, effect: "feint" },
|
|
668
|
+
{ key: "stride", label: "Stride", cost: 1, slug: "stride", kind: "move" },
|
|
669
|
+
{ key: "step", label: "Step", cost: 1, slug: "step", kind: "move" },
|
|
670
|
+
{ key: "hide", label: "Hide", cost: 1, slug: "hide", kind: "skill" },
|
|
671
|
+
{ key: "seek", label: "Seek", cost: 1, slug: "seek", kind: "skill", effect: "seek" },
|
|
672
|
+
{ key: "interact", label: "Interact — pull a rope loose", cost: 1, slug: "interact", kind: "other" },
|
|
673
|
+
],
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
/* Reactions are listed apart: you get one, and it happens on someone else's turn. */
|
|
677
|
+
const REACTIONS = [
|
|
678
|
+
{ slug: "aid", label: "Aid", note: "Prepare on your turn, then help an ally's check. Success: +1 circumstance bonus." },
|
|
679
|
+
{ slug: "grab-an-edge", label: "Grab an Edge", note: "When you fall off something — the sea is full of edges." },
|
|
680
|
+
{ slug: "arrest-a-fall", label: "Arrest a Fall", note: "If you can fly or Cast a Spell to catch yourself." },
|
|
681
|
+
];
|
|
682
|
+
|
|
683
|
+
/* ============================================================
|
|
684
|
+
EXPLORATION — the activity board. `governing` is the statistic
|
|
685
|
+
the GM most often rolls or references for you; `secret` marks
|
|
686
|
+
the ones the GM usually rolls behind the screen.
|
|
687
|
+
============================================================ */
|
|
688
|
+
const EXPLORATION_ACTIVITIES = [
|
|
689
|
+
{ slug: "search", label: "Search", governing: "Perception", secret: true,
|
|
690
|
+
blurb: "You sweep for hidden doors, traps and lurkers as you go. Half speed." },
|
|
691
|
+
{ slug: "avoid-notice", label: "Avoid Notice", governing: "Stealth", secret: true,
|
|
692
|
+
blurb: "You move so as not to be seen. Sets you up to start a fight hidden." },
|
|
693
|
+
{ slug: "scout", label: "Scout", governing: "—", secret: false,
|
|
694
|
+
blurb: "You range ahead and back. Everyone gets +1 to initiative." },
|
|
695
|
+
{ slug: "defend", label: "Defend", governing: "—", secret: false,
|
|
696
|
+
blurb: "Shield up, half speed. You're ready when it goes wrong." },
|
|
697
|
+
{ slug: "investigate", label: "Investigate", governing: "a knowledge skill", secret: true,
|
|
698
|
+
blurb: "You Recall Knowledge as you travel, about whatever's around you." },
|
|
699
|
+
{ slug: "follow-the-expert", label: "Follow the Expert", governing: "an ally's skill", secret: false,
|
|
700
|
+
blurb: "Copy someone better than you and borrow their proficiency. The untrained character's best friend." },
|
|
701
|
+
{ slug: "hustle", label: "Hustle", governing: "Constitution", secret: false,
|
|
702
|
+
blurb: "Double travel speed, for as long as your Constitution holds out." },
|
|
703
|
+
{ slug: "detect-magic", label: "Detect Magic", governing: "—", secret: false,
|
|
704
|
+
blurb: "Cast detect magic over and over as you walk." },
|
|
705
|
+
{ slug: "track", label: "Track", governing: "Survival", secret: true,
|
|
706
|
+
blurb: "Follow a trail. Half speed." },
|
|
707
|
+
{ slug: "cover-tracks", label: "Cover Tracks", governing: "Survival", secret: false,
|
|
708
|
+
blurb: "Hide the trail you're leaving. Half speed." },
|
|
709
|
+
{ slug: "repeat-a-spell", label: "Repeat a Spell", governing: "—", secret: false,
|
|
710
|
+
blurb: "Keep a one-minute spell running as you travel." },
|
|
711
|
+
{ slug: "refocus", label: "Refocus", governing: "—", secret: false,
|
|
712
|
+
blurb: "Ten minutes of quiet gets a focus point back." },
|
|
713
|
+
];
|
|
714
|
+
|
|
715
|
+
/* Things the party does when they stop, rather than while moving. */
|
|
716
|
+
const EXPLORATION_STOPS = [
|
|
717
|
+
{ slug: "treat-wounds", label: "Treat Wounds", note: "Ten minutes and a healer's toolkit. This is your between-fights healing — DC 15 for a trained medic." },
|
|
718
|
+
{ slug: "recall-knowledge", label: "Recall Knowledge", note: "One action or a moment's thought: what do I know about this thing?" },
|
|
719
|
+
{ slug: "seek", label: "Seek", note: "The single-action version, once you're standing in the room that worries you." },
|
|
720
|
+
{ slug: "sense-motive", label: "Sense Motive", note: "Is this person lying to me? The GM rolls it in secret." },
|
|
721
|
+
{ slug: "gather-information", label: "Gather Information", note: "A couple of hours in the taverns of " + FLAVOR.port + "." },
|
|
722
|
+
];
|
|
723
|
+
|
|
724
|
+
/* ============================================================
|
|
725
|
+
SOCIAL — the attitude ladder and the moves that shift it.
|
|
726
|
+
Outcome rules mirror the action text in the reference data.
|
|
727
|
+
============================================================ */
|
|
728
|
+
const ATTITUDES = ["hostile", "unfriendly", "indifferent", "friendly", "helpful"];
|
|
729
|
+
|
|
730
|
+
const SOCIAL_MOVES = [
|
|
731
|
+
{ key: "impression", slug: "make-an-impression", label: "Make an Impression", skill: "Diplomacy",
|
|
732
|
+
vs: "Will DC", time: "1 minute of conversation",
|
|
733
|
+
outcomes: { cs: "+2 steps", s: "+1 step", f: "no change", cf: "−1 step" } },
|
|
734
|
+
{ key: "request", slug: "request", label: "Request", skill: "Diplomacy",
|
|
735
|
+
vs: "a DC the GM sets", time: "1 action",
|
|
736
|
+
needs: "friendly", outcomes: { cs: "they agree, no strings", s: "they agree, with conditions", f: "refused, maybe a counter-offer", cf: "refused, −1 step" } },
|
|
737
|
+
{ key: "coerce", slug: "coerce", label: "Coerce", skill: "Intimidation",
|
|
738
|
+
vs: "Will DC", time: "1 minute of threats",
|
|
739
|
+
outcomes: { cs: "they comply — then turn unfriendly", s: "they comply — then turn unfriendly and may act against you", f: "refused, and now unfriendly", cf: "refused, now hostile, immune for a week" } },
|
|
740
|
+
{ key: "lie", slug: "lie", label: "Lie", skill: "Deception",
|
|
741
|
+
vs: "Perception DC", time: "1 round or more",
|
|
742
|
+
outcomes: { cs: "believed", s: "believed", f: "not believed, +4 against your next lies", cf: "not believed, +4 against your next lies" } },
|
|
743
|
+
{ key: "demoralize", slug: "demoralize", label: "Demoralize", skill: "Intimidation",
|
|
744
|
+
vs: "Will DC", time: "1 action — this one works in a fight",
|
|
745
|
+
outcomes: { cs: "frightened 2", s: "frightened 1", f: "nothing", cf: "nothing" } },
|
|
746
|
+
];
|
|
747
|
+
|
|
748
|
+
/* ============================================================
|
|
749
|
+
DOWNTIME — what you do with days you're not adventuring.
|
|
750
|
+
Deliberately no income table: the numbers depend on a task
|
|
751
|
+
level your GM assigns, and Earn Income's own text (in Cards)
|
|
752
|
+
explains the process.
|
|
753
|
+
============================================================ */
|
|
754
|
+
const DOWNTIME_ACTIVITIES = [
|
|
755
|
+
{ slug: "earn-income", label: "Earn Income", skill: "Crafting, Performance or a Lore",
|
|
756
|
+
blurb: "Take a job. Your GM sets a task level; your proficiency and your roll set the daily rate." },
|
|
757
|
+
{ slug: "craft", label: "Craft", skill: "Crafting",
|
|
758
|
+
blurb: "Make the thing instead of buying it. Days of work, and you need the formula." },
|
|
759
|
+
{ slug: "learn-a-spell", label: "Learn a Spell", skill: "your magic skill",
|
|
760
|
+
blurb: "Spellcasters: add something new to the book or the repertoire." },
|
|
761
|
+
{ slug: "retraining", label: "Retraining", skill: "—",
|
|
762
|
+
blurb: "Change a feat or a skill you regret. Weeks, not minutes — and the whole point is that regret is fixable." },
|
|
763
|
+
{ slug: "long-term-rest", label: "Long-Term Rest", skill: "—",
|
|
764
|
+
blurb: "Full days of doing nothing but healing. The slow, free option." },
|
|
765
|
+
{ slug: "research", label: "Research", skill: "a knowledge skill",
|
|
766
|
+
blurb: "Dig through a library or an archive for a specific answer." },
|
|
767
|
+
{ slug: "treat-disease", label: "Treat Disease", skill: "Medicine",
|
|
768
|
+
blurb: "Days of nursing someone through something nasty." },
|
|
769
|
+
{ slug: "subsist", label: "Subsist", skill: "Survival or Society",
|
|
770
|
+
blurb: "Feed and shelter yourself when there's no coin for an inn." },
|
|
771
|
+
];
|
|
772
|
+
|
|
773
|
+
/* ============================================================
|
|
774
|
+
THE BRIDGE — what you already know, and what PF2e does with it.
|
|
775
|
+
============================================================ */
|
|
776
|
+
const BRIDGE = [
|
|
777
|
+
["Move, action, maybe a bonus action", "Three actions. Spend them on anything, in any order — three Strikes if you like."],
|
|
778
|
+
["Roll high, hit or miss", "Four outcomes. Beat the DC by 10 and it's a critical success; miss it by 10 and it's a critical failure."],
|
|
779
|
+
["Natural 20 is a crit", "A natural 20 moves your result one step better. A 20 on what would have been a failure is just a success."],
|
|
780
|
+
["Advantage / disadvantage", "Named bonuses: circumstance, status, item. Two of the same type don't stack — only the biggest applies."],
|
|
781
|
+
["Proficiency bonus by level", "Your level plus a rank bonus (trained +2, expert +4, master +6, legendary +8). Untrained adds nothing at all — not even your level."],
|
|
782
|
+
["Race and class", "Ancestry, heritage, background and class — each one hands you feats to pick at level 1."],
|
|
783
|
+
["Death saving throws", "A dying value that climbs, recovery checks to shake it off, and a wounded condition that remembers."],
|
|
784
|
+
["Concentration on a spell", "Sustain a Spell: an action, on your turn, every round you want it to keep going."],
|
|
785
|
+
["\"I roll Persuasion\"", "Named skill actions with four written outcomes: Make an Impression, Coerce, Demoralize, Trip, Feint."],
|
|
786
|
+
["Short rest, hit dice", "Treat Wounds: ten minutes, a Medicine check, and most of the party's hit points back."],
|
|
787
|
+
];
|
|
788
|
+
|
|
789
|
+
/* ============================================================
|
|
790
|
+
GLOSSARY — the jargon that trips up players coming from
|
|
791
|
+
another system.
|
|
792
|
+
============================================================ */
|
|
793
|
+
const GLOSSARY = [
|
|
794
|
+
["Action", "One of the three things you do on your turn. The ◆ symbol. Three of them, plus one reaction."],
|
|
795
|
+
["Activity", "Something that eats more than one action, or minutes and hours of exploration time. Casting most spells is a two-action activity."],
|
|
796
|
+
["MAP", "Multiple Attack Penalty. Your second attack in a turn is at −5, the third at −10 (−4 and −8 with an agile weapon). It resets every turn."],
|
|
797
|
+
["DC", "Difficulty Class — the number to beat. Any modifier can become a DC: add 10 to it. Your AC is just your DC to be hit."],
|
|
798
|
+
["Degrees of success", "Critical success, success, failure, critical failure. Ten over or ten under moves you a step; a natural 20 or 1 moves you a step."],
|
|
799
|
+
["Secret check", "A roll the GM makes for you, so you don't know how well you did. Perception, Stealth and knowledge checks are often secret."],
|
|
800
|
+
["Flat check", "A d20 with no modifiers at all, against a fixed DC — for concealment, persistent damage, and recovery from dying."],
|
|
801
|
+
["Proficiency rank", "Untrained, trained, expert, master, legendary. Worth +0, +2, +4, +6, +8 — plus your level, unless you're untrained."],
|
|
802
|
+
["Status / circumstance / item", "The three bonus types. Same type doesn't stack; different types do. This is why a shield (+2 circumstance) and a spell (+1 status) both help."],
|
|
803
|
+
["Off-guard", "−2 circumstance to AC. What used to be called flat-footed. Being flanked or prone does it to you."],
|
|
804
|
+
["Hero point", "You start each session with one. Spend it to reroll anything; spend all of them to cheat death."],
|
|
805
|
+
["Focus point", "Fuel for a class's signature spells. Ten minutes of Refocus gets one back."],
|
|
806
|
+
["Encounter / exploration / downtime", "The three gears the game shifts between: rounds, minutes, days."],
|
|
807
|
+
];
|
|
808
|
+
|
|
809
|
+
/* ============================================================
|
|
810
|
+
CHAPTERS — the primer proper. Section types are rendered by
|
|
811
|
+
engine.js: prose, note, bridge, map, cards, steps, demo, ref,
|
|
812
|
+
gm (GM mode only), links.
|
|
813
|
+
============================================================ */
|
|
814
|
+
const CHAPTERS = [
|
|
815
|
+
/* ---------------------------------------------------------- */
|
|
816
|
+
{
|
|
817
|
+
key: "start", label: "Start", icon: "compass",
|
|
818
|
+
title: "Weighing anchor",
|
|
819
|
+
lede: "You already know how to play a roleplaying game. This page is only about what Pathfinder does differently — and it's concentrated in three places.",
|
|
820
|
+
sections: [
|
|
821
|
+
{ type: "prose", h: "The short version", p: [
|
|
822
|
+
"Pathfinder 2e is fussy in exactly one direction: it wants your <b>specific</b> intention. Not \"I attack\" but \"I Strike with my sword, then Trip him, then Raise my Shield.\" In return it gives you a game where those choices genuinely matter, and where the rules for them are written down instead of adjudicated fresh every time.",
|
|
823
|
+
"Three things to take away today: <b>how a roll resolves</b> (four outcomes, not two), <b>how a turn is spent</b> (three actions, no categories), and <b>which gear the game is in</b> (encounter, exploration, downtime). Everything else you can look up at the table.",
|
|
824
|
+
] },
|
|
825
|
+
{ type: "bridge", h: "What you know → what PF2e does" },
|
|
826
|
+
{ type: "prose", h: "The one mechanic", p: [
|
|
827
|
+
"Roll a d20, add a modifier, compare to a Difficulty Class. That's the whole game. Attacks compare to Armour Class, spells compare to your spell DC, jumping a gap compares to a DC the GM picked. AC is not a special case — it's just the DC to hit you.",
|
|
828
|
+
"Where PF2e differs is what happens next. You don't just hit or miss.",
|
|
829
|
+
] },
|
|
830
|
+
{ type: "cards", h: "Four outcomes, every time", items: [
|
|
831
|
+
{ h: "Critical success", p: "You beat the DC by 10 or more. Double damage on an attack; the best version of whatever you were attempting." },
|
|
832
|
+
{ h: "Success", p: "You met or beat the DC." },
|
|
833
|
+
{ h: "Failure", p: "You missed it by 1 to 9." },
|
|
834
|
+
{ h: "Critical failure", p: "You missed the DC by 10 or more. Usually something actively goes wrong." },
|
|
835
|
+
] },
|
|
836
|
+
{ type: "note", tone: "tip", p: [
|
|
837
|
+
"<b>Natural 20 and natural 1 are not results — they're nudges.</b> A 20 on the die moves your outcome one step better, a 1 moves it one step worse. A natural 20 that still doesn't beat the DC is a success, not a critical. A natural 1 on a roll that beat the DC by 12 is still a success.",
|
|
838
|
+
] },
|
|
839
|
+
{ type: "pregens", h: "The two characters the demos roll with" },
|
|
840
|
+
{ type: "demo", id: "check", h: "Try it",
|
|
841
|
+
p: "Pick who's rolling and what they're rolling against. The maths is shown every time, including the two nudges." },
|
|
842
|
+
{ type: "prose", h: "Where your modifier comes from", p: [
|
|
843
|
+
"Almost every modifier in the game is <b>ability modifier + proficiency</b>, and proficiency is <b>your level + a rank bonus</b>: trained +2, expert +4, master +6, legendary +8. Untrained adds <i>nothing</i> — not the rank, not your level.",
|
|
844
|
+
"That last part is the design in a nutshell. The gap between a trained character and an untrained one is small at level 1 and enormous at level 10, and it means a character who is trained in the right skill is always the right person for the job.",
|
|
845
|
+
"So Mari's Athletics of +7 is Str +4, trained +2, and level 1. Sable's Will save of +5 is Wis +0, expert +4, and level 1. You can rebuild every number on both sheets from that sentence.",
|
|
846
|
+
] },
|
|
847
|
+
{ type: "cards", h: "Three kinds of bonus, and they don't all stack", items: [
|
|
848
|
+
{ h: "Circumstance", p: "From the situation: cover, a raised shield, good footing. Only the largest circumstance bonus applies." },
|
|
849
|
+
{ h: "Status", p: "From spells and conditions: a bard's anthem, being frightened. Only the largest status bonus applies — and status penalties work the same way." },
|
|
850
|
+
{ h: "Item", p: "From gear: armour, a magic weapon. Only the largest item bonus applies." },
|
|
851
|
+
{ h: "Different types stack", p: "Shield (+2 circumstance) plus Courageous Anthem (+1 status) is +3. Two spells both giving +1 status is still +1." },
|
|
852
|
+
] },
|
|
853
|
+
{ type: "prose", h: "Hero points", p: [
|
|
854
|
+
"You start each session with one, and the GM hands more out for good play. Spend one to <b>reroll any check you just made</b> — you take the new result, so it's a gamble, not a safety net. Spend <i>all</i> of them to avoid dying outright.",
|
|
855
|
+
"Take the reroll. New players sit on hero points until the session ends and they evaporate.",
|
|
856
|
+
] },
|
|
857
|
+
{ type: "map", h: "Three pillars, three gears" },
|
|
858
|
+
{ type: "prose", p: [
|
|
859
|
+
"You know the three pillars — fighting, exploring, talking. PF2e agrees, but it slices the game a different way: into three <b>modes</b>, which are really three time scales. Combat has its own mode. Exploration has its own mode. Talking doesn't — it happens in whichever mode you're already in. And there's a third mode, downtime, that covers the days and weeks nobody else's rules bother with.",
|
|
860
|
+
"The rest of this page is one tab per pillar, plus one for downtime. Poke at every demo; they all roll real dice against real numbers.",
|
|
861
|
+
] },
|
|
862
|
+
{ type: "gm", h: "At the table", p: [
|
|
863
|
+
"This is the session-zero tab. Fifteen minutes, tops.",
|
|
864
|
+
"Do the degrees-of-success demo <b>live, on the shared screen</b>: set DC 15 and roll ten times, and let them watch how often a critical happens on its own. That single fact — criticals are common, and they come from beating the DC, not from the die face — reframes the whole game for a 5e player.",
|
|
865
|
+
"Don't teach the bonus types yet. Point at the card, say \"the app will tell you when it matters\", and move on. It only clicks once someone has a shield and a bard at the same time.",
|
|
866
|
+
"Ask each of them now: <i>what does your character want, and who do they already know in " + FLAVOR.port + "?</i> You'll use both answers in the Talk tab.",
|
|
867
|
+
] },
|
|
868
|
+
],
|
|
869
|
+
},
|
|
870
|
+
|
|
871
|
+
/* ---------------------------------------------------------- */
|
|
872
|
+
{
|
|
873
|
+
key: "fight", label: "Fight", icon: "sword",
|
|
874
|
+
title: "Encounter mode",
|
|
875
|
+
lede: "Rounds of six seconds. Initiative order. Three actions each. This is the mode with the most rules, and it's the one PF2e is proudest of.",
|
|
876
|
+
sections: [
|
|
877
|
+
{ type: "prose", h: "Starting a fight", p: [
|
|
878
|
+
"Everyone rolls initiative — usually Perception, but if you were Avoiding Notice you roll Stealth instead, and if you were talking your way in the GM might call for Deception. What you were doing in exploration mode decides what you roll. That's the join between the two gears.",
|
|
879
|
+
"Then you go round the table. On your turn: <b>three actions</b>, in any combination. There is no move-versus-action distinction. Moving is an action. Drawing a weapon is an action. Standing up is an action.",
|
|
880
|
+
] },
|
|
881
|
+
{ type: "cards", h: "The three-action turn", items: [
|
|
882
|
+
{ h: "◆ Single action", p: "Strike. Stride. Raise a Shield. Demoralize. Trip. Most things." },
|
|
883
|
+
{ h: "◆◆ Two actions", p: "Most spells. Some weapon feats. An activity that spans two of your three." },
|
|
884
|
+
{ h: "◆◆◆ Three actions", p: "Your whole turn: a big spell, a Sudden Charge, a full sprint." },
|
|
885
|
+
{ h: "⤳ One reaction", p: "Off your turn, when its trigger fires. Aid an ally, Attack of Opportunity if you have it. You get one per round and most level-1 characters have very few options — that's normal." },
|
|
886
|
+
] },
|
|
887
|
+
{ type: "reactions", h: "Your one reaction",
|
|
888
|
+
p: "You get one per round and it happens on someone else's turn. A level-1 character has very few — these three are the ones anybody can use." },
|
|
889
|
+
{ type: "note", tone: "warn", p: [
|
|
890
|
+
"<b>The multiple attack penalty is the price of three Strikes.</b> Your first attack each turn is at full value; your second is at −5 and your third at −10. Agile weapons soften it to −4 and −8. It resets at the start of your turn, and it counts <i>attack</i> actions — Trip, Grapple, Shove and spell attacks all pay into it and suffer from it.",
|
|
891
|
+
"This is why the third Strike is usually the wrong choice. Trip, Demoralize, raise a shield, or move somewhere better instead.",
|
|
892
|
+
] },
|
|
893
|
+
{ type: "demo", id: "turn", h: "Build a turn",
|
|
894
|
+
p: "Spend three actions and run them against the " + FLAVOR.foe + ". Every roll shows its arithmetic — including the penalty stacking up, and what happens to the target's AC once it's frightened or prone." },
|
|
895
|
+
{ type: "prose", h: "Attacks, and what a critical does", p: [
|
|
896
|
+
"An attack roll is d20 + ability + proficiency + item bonus against the target's AC. Beat the AC by 10 and it's a critical hit: <b>double the damage</b> (roll the dice, add the modifiers, then double the total — that's the whole rule).",
|
|
897
|
+
"Damage is not a separate to-hit; there's no second roll to confirm. And because criticals come from beating the DC by 10, anything that lowers a target's AC — flanking, being prone, being frightened — pushes real criticals into your range. Debuffs are damage in this system.",
|
|
898
|
+
] },
|
|
899
|
+
{ type: "prose", h: "Spells work the other way round", p: [
|
|
900
|
+
"Attack spells roll against AC like anything else. But most spells make the <b>target</b> roll, against your spell DC, and the four degrees decide how much of the spell lands. A successful save usually still means something happened — half damage, or a weaker version of the condition.",
|
|
901
|
+
"That's the second half of the caster's job: you are rarely wasting a turn, you're just choosing between a big effect that might be resisted and a small effect that definitely lands.",
|
|
902
|
+
] },
|
|
903
|
+
{ type: "ref", h: "Conditions you'll meet in your first fight", kind: "condition",
|
|
904
|
+
slugs: ["off-guard", "frightened", "prone", "persistent-damage", "grabbed", "slowed", "stunned", "clumsy", "enfeebled"],
|
|
905
|
+
p: "Real text from the rules. Frightened is worth reading twice: it's a status penalty to <i>everything</i>, checks and DCs alike, which is why Demoralize is a genuinely good use of an action." },
|
|
906
|
+
{ type: "prose", h: "When you drop", p: [
|
|
907
|
+
"Nobody dies at 0 hit points in PF2e. You go <b>dying 1</b>, unconscious, and at the start of each of your turns you roll a flat d20 against DC 10 + your dying value to see whether you claw back. Get to dying 4 and you're gone.",
|
|
908
|
+
"Every time you shake off dying you gain <b>wounded 1</b>, and the next time you drop, your wounded value is added to your dying value. The system remembers. Getting picked up twice in a fight is survivable; three times is how characters actually die.",
|
|
909
|
+
] },
|
|
910
|
+
{ type: "demo", id: "dying", h: "Walk it through",
|
|
911
|
+
p: "Take Mari down and try to get her back. Everything here is a real roll against a real DC." },
|
|
912
|
+
{ type: "steps", h: "Your first turn, in order", items: [
|
|
913
|
+
"Look at what your three actions could do — say it out loud, badly, and let the table help.",
|
|
914
|
+
"Ask the GM the target's AC only if you're allowed to know it. Otherwise just roll and say the total.",
|
|
915
|
+
"Roll the d20, add your modifier, say the <b>total</b>. Don't say \"I rolled a 14\" — the total is what matters.",
|
|
916
|
+
"If your total beats their AC by 10, say so — that's a critical hit and it doubles your damage.",
|
|
917
|
+
"Roll damage, add the modifier, done. Then decide whether that second Strike at −5 is really better than a Trip.",
|
|
918
|
+
"At the end of your turn, say what you're doing with your reaction, if you have one.",
|
|
919
|
+
] },
|
|
920
|
+
{ type: "ref", h: "The basic actions, in the book's own words", kind: "action",
|
|
921
|
+
slugs: ["strike", "stride", "step", "raise-a-shield", "trip", "grapple", "demoralize", "seek", "aid", "delay", "ready", "escape", "stand"] },
|
|
922
|
+
{ type: "gm", h: "At the table", p: [
|
|
923
|
+
"Run the turn builder as a competition: give them both three actions against the " + FLAVOR.foe + " and let them find the Demoralize-then-Strike line themselves. It lands harder discovered than explained.",
|
|
924
|
+
"First real fight: <b>two weak enemies, not one boss</b>. Two bodies teaches flanking, off-guard, and why the third Strike is a trap. One boss teaches that PF2e monsters have a lot of hit points.",
|
|
925
|
+
"Say \"that's a critical\" out loud the first three times it happens on either side, and name why — <i>beat the AC by ten</i>. They'll be doing the arithmetic themselves by the second fight.",
|
|
926
|
+
"Have someone drop on purpose if the dice don't do it for you. Better they meet the dying rules while it's recoverable and you've got a Soothe in the party.",
|
|
927
|
+
"Hand out a hero point the first time either of them does something in character during a fight. That's the whole lesson about hero points.",
|
|
928
|
+
] },
|
|
929
|
+
],
|
|
930
|
+
},
|
|
931
|
+
|
|
932
|
+
/* ---------------------------------------------------------- */
|
|
933
|
+
{
|
|
934
|
+
key: "explore", label: "Explore", icon: "compass2",
|
|
935
|
+
title: "Exploration mode",
|
|
936
|
+
lede: "Minutes and hours. No initiative, no grid, no counting actions — but you're still choosing something, and what you choose decides how the next fight starts.",
|
|
937
|
+
sections: [
|
|
938
|
+
{ type: "prose", h: "The middle gear", p: [
|
|
939
|
+
"Between fights, the game doesn't stop having rules — it changes their grain. You're not spending actions any more; you're declaring what you're <b>doing continuously</b> as the party moves. That declaration is called an exploration activity, and you should always have one.",
|
|
940
|
+
"This matters more than it sounds. If you're Searching, you get a check to spot the tripwire. If you're Avoiding Notice, you roll Stealth for initiative and start the fight hidden. If you declared nothing, you get nothing.",
|
|
941
|
+
] },
|
|
942
|
+
{ type: "demo", id: "explore", h: "Give everyone a job",
|
|
943
|
+
p: "Set each character's activity and see what it buys them — and which of those checks your GM will be rolling in secret." },
|
|
944
|
+
{ type: "prose", h: "Some of your dice aren't yours", p: [
|
|
945
|
+
"Searching, sneaking, and remembering things are <b>secret checks</b>: the GM rolls them for you, behind the screen, because knowing you rolled a 3 on Perception tells you something your character doesn't know. If your GM asks for your Perception modifier at the start of a session and never mentions it again, this is why.",
|
|
946
|
+
"It feels strange for one session and then it feels obviously correct. You'll notice you stop metagaming without having to try.",
|
|
947
|
+
] },
|
|
948
|
+
{ type: "ref", h: "What you're actually doing out there", kind: "action",
|
|
949
|
+
slugs: ["search", "avoid-notice", "scout", "investigate", "follow-the-expert", "hustle", "defend", "track"],
|
|
950
|
+
p: "Follow the Expert deserves special mention: it lets a character with no training in a skill borrow a trained ally's proficiency. It's the answer to \"but I'm terrible at Stealth\"." },
|
|
951
|
+
{ type: "prose", h: "Recall Knowledge", p: [
|
|
952
|
+
"One action in a fight, or a moment's thought outside one: you ask the GM a question about something in front of you, using the skill that would know. Arcana for the summoned thing, Nature for the beast, Society for the guild insignia, a Lore skill for anything you took Lore in.",
|
|
953
|
+
"Critical failure means the GM lies to you with a straight face. That's not the GM being unfair — it's written in the action. It's also the single best argument for having two characters with different knowledge skills.",
|
|
954
|
+
] },
|
|
955
|
+
{ type: "prose", h: "Healing between fights", p: [
|
|
956
|
+
"Ten minutes and a healer's toolkit gets a Medicine check to <b>Treat Wounds</b> — a chunk of hit points back, repeatable once an hour per patient. This is the system's short rest, and it's the reason someone in the party should be trained in Medicine even if nobody wants to be a healer.",
|
|
957
|
+
"A full night's rest heals you for your Constitution modifier times your level, and clears fatigue. Ten quiet minutes also gets a focus point back with Refocus.",
|
|
958
|
+
] },
|
|
959
|
+
{ type: "stops", h: "When the party stops moving" },
|
|
960
|
+
{ type: "links" },
|
|
961
|
+
{ type: "gm", h: "At the table", p: [
|
|
962
|
+
"Ask for exploration activities <b>every time the scenery changes</b>, in the same words each session, until they say it without being asked. \"You come up the beach road. What's everyone doing?\"",
|
|
963
|
+
"Collect their Perception, Stealth and knowledge modifiers on a card at session zero, so you can roll secret checks without breaking the scene to ask. The party tracker linked above is exactly this, with the passive DCs already worked out.",
|
|
964
|
+
"The first time someone declares Search and finds the trap, say so plainly: <i>you found it because you said you were looking.</i> That single moment does more teaching than this whole tab.",
|
|
965
|
+
"Let a critical failure on Recall Knowledge actually give them a confident wrong answer. Then let it bite. They'll never forget the four degrees again.",
|
|
966
|
+
] },
|
|
967
|
+
],
|
|
968
|
+
},
|
|
969
|
+
|
|
970
|
+
/* ---------------------------------------------------------- */
|
|
971
|
+
{
|
|
972
|
+
key: "talk", label: "Talk", icon: "speech",
|
|
973
|
+
title: "The pillar without a mode",
|
|
974
|
+
lede: "Social play doesn't get its own gear. It happens inside whichever mode you're in — a tense parley in encounter mode, a night of drinking in exploration, a season of favours in downtime.",
|
|
975
|
+
sections: [
|
|
976
|
+
{ type: "prose", h: "Roleplay first, dice second", p: [
|
|
977
|
+
"PF2e's social rules are not there to replace the conversation. You say what your character says. <i>Then</i>, if it's genuinely uncertain and something's at stake, the GM names an action and you roll to find out how well it landed. A good pitch might get a circumstance bonus; a tactless one might get a penalty.",
|
|
978
|
+
"What the rules add is <b>consequence with teeth</b>. Every social action has four written outcomes, and the bad ones cost you something specific — a door that closes, a friend who turns wary.",
|
|
979
|
+
] },
|
|
980
|
+
{ type: "prose", h: "Attitude is a real number", p: [
|
|
981
|
+
"Every NPC has an attitude toward you: <b>hostile, unfriendly, indifferent, friendly, helpful</b>. Most people start indifferent. Social actions move NPCs up and down that ladder, and where they sit decides what they'll agree to.",
|
|
982
|
+
"You can only <b>Request</b> something of someone who's already friendly or helpful. So the actual skill of social play in this system is: make an impression first, ask second. Walking up to a stranger and asking for the harbour manifest is a failed roll waiting to happen.",
|
|
983
|
+
] },
|
|
984
|
+
{ type: "demo", id: "attitude", h: "Work on " + FLAVOR.npc,
|
|
985
|
+
p: "Indifferent to start, Will DC 15. Try to get to friendly, then ask for something — and watch what Coerce costs you even when it works." },
|
|
986
|
+
{ type: "cards", h: "The moves", items: [
|
|
987
|
+
{ h: "Make an Impression — Diplomacy", p: "A minute of charm against their Will DC. Critical success moves them two steps. The opener." },
|
|
988
|
+
{ h: "Request — Diplomacy", p: "One action, and they must already be friendly. Critical failure knocks them back a step for having the cheek to ask." },
|
|
989
|
+
{ h: "Coerce — Intimidation", p: "Threats. It works, and then they're unfriendly afterwards <i>even on a critical success</i>. A tool with a bill attached." },
|
|
990
|
+
{ h: "Lie — Deception", p: "Against their Perception DC. Failure gives them +4 against everything you say for the rest of the conversation." },
|
|
991
|
+
{ h: "Demoralize — Intimidation", p: "The one that works mid-fight. One action, 30 feet, and they're frightened." },
|
|
992
|
+
{ h: "Sense Motive — Perception", p: "Is this person straight with me? Secret check, so ask and then trust what you're told — or don't." },
|
|
993
|
+
] },
|
|
994
|
+
{ type: "note", tone: "tip", p: [
|
|
995
|
+
"<b>Aid is the most under-used action in the game.</b> Say how you're helping, prepare it on your turn, and when your ally rolls, you roll too — a success hands them +1, a critical success +2. Two characters working one NPC is mechanically better than the charming one going alone, and it's how you get the quiet player into a social scene.",
|
|
996
|
+
] },
|
|
997
|
+
{ type: "ref", h: "The social actions, in the book's own words", kind: "action",
|
|
998
|
+
slugs: ["make-an-impression", "request", "coerce", "lie", "demoralize", "sense-motive", "feint", "gather-information", "impersonate", "aid"] },
|
|
999
|
+
{ type: "ref", h: "The attitudes", kind: "condition",
|
|
1000
|
+
slugs: ["hostile", "unfriendly", "indifferent", "friendly", "helpful"] },
|
|
1001
|
+
{ type: "gm", h: "At the table", p: [
|
|
1002
|
+
"Give every named NPC an attitude before the scene and say it out loud when it changes. \"Bel goes from indifferent to friendly\" teaches the ladder in one sentence.",
|
|
1003
|
+
"Never make them roll for the conversation itself — make them roll when the conversation has a decision point. If there's no way to fail interestingly, just let them have it.",
|
|
1004
|
+
"Reward the pitch with the dice: a genuinely good argument is a +1 or +2 circumstance bonus, and saying so out loud tells them that talking well is mechanically worth doing.",
|
|
1005
|
+
"Watch for the Coerce trap. Let it work the first time, then let the unfriendly consequence land two scenes later. That's the lesson the text is trying to teach.",
|
|
1006
|
+
"If one player is doing all the talking, call for Aid explicitly: \"" + FLAVOR.npc + " glances at your friend — are you backing this up?\"",
|
|
1007
|
+
] },
|
|
1008
|
+
],
|
|
1009
|
+
},
|
|
1010
|
+
|
|
1011
|
+
/* ---------------------------------------------------------- */
|
|
1012
|
+
{
|
|
1013
|
+
key: "ashore", label: "Ashore", icon: "anchor",
|
|
1014
|
+
title: "Downtime mode",
|
|
1015
|
+
lede: "Days and weeks between adventures. The gear most games don't have rules for — which is exactly why it's worth knowing this one does.",
|
|
1016
|
+
sections: [
|
|
1017
|
+
{ type: "prose", h: "The slowest gear", p: [
|
|
1018
|
+
"When the party is in port with nothing chasing them, time moves in days. You each say what you're spending those days on, roll once or twice, and the GM tells you what a week of it bought.",
|
|
1019
|
+
"It's optional in the sense that some campaigns never stop moving. But it's where a character gets a trade, a reputation, a workshop, and the chance to un-pick a level-1 decision they've come to regret.",
|
|
1020
|
+
] },
|
|
1021
|
+
{ type: "demo", id: "downtime", h: "Plan a week ashore",
|
|
1022
|
+
p: "Give each character something to do with the days and see what it needs from you and the GM." },
|
|
1023
|
+
{ type: "prose", h: "Retraining deserves a paragraph of its own", p: [
|
|
1024
|
+
"You can spend downtime <b>swapping out a feat or a skill</b> you don't enjoy. Not a house rule, not GM mercy — a written activity with a time cost.",
|
|
1025
|
+
"Say this to a new player at character creation and watch their shoulders drop. Nobody has to get the build right on day one. Pick the thing that sounds fun; if it isn't, a week ashore fixes it.",
|
|
1026
|
+
] },
|
|
1027
|
+
{ type: "ref", h: "What a week can buy", kind: "action",
|
|
1028
|
+
slugs: ["earn-income", "craft", "learn-a-spell", "retraining", "long-term-rest", "research", "treat-disease", "subsist", "repair"],
|
|
1029
|
+
p: "Earn Income's rate depends on a task level your GM assigns and on your proficiency rank — the table lives in Player Core, and it's the one number this app deliberately doesn't try to remember for you." },
|
|
1030
|
+
{ type: "gm", h: "At the table", p: [
|
|
1031
|
+
"One downtime turn per port, even if it's only \"you have four days, one activity each\". It costs five minutes and it makes the world feel like it continues without them.",
|
|
1032
|
+
"Ask for a sentence of colour with each declaration — <i>where</i> are they earning that income, and who did they annoy doing it? That's where your next hook comes from, free.",
|
|
1033
|
+
"Tell them about Retraining out loud at session one, before either of them has committed to a build they're unsure about.",
|
|
1034
|
+
"Downtime is the natural place to hand out the campaign's ship-and-crew business if you're running one. Repairs, wages, and cargo all fit here without needing a subsystem.",
|
|
1035
|
+
] },
|
|
1036
|
+
],
|
|
1037
|
+
},
|
|
1038
|
+
|
|
1039
|
+
/* ---------------------------------------------------------- */
|
|
1040
|
+
{
|
|
1041
|
+
key: "cards", label: "Cards", icon: "cards",
|
|
1042
|
+
title: "Table cards",
|
|
1043
|
+
lede: "The bits worth having open during play. Search the whole rules set below, or keep the quick card on screen.",
|
|
1044
|
+
sections: [
|
|
1045
|
+
{ type: "quickcard" },
|
|
1046
|
+
{ type: "glossary", h: "The jargon" },
|
|
1047
|
+
{ type: "browser" },
|
|
1048
|
+
{ type: "links" },
|
|
1049
|
+
{ type: "gm", h: "At the table", p: [
|
|
1050
|
+
"Leave this tab open on a phone or a second screen during the first two sessions. The search box covers every basic, skill, exploration and downtime action plus all 43 conditions — it will answer most rules questions faster than the index will.",
|
|
1051
|
+
"The quick card is the only thing here worth printing.",
|
|
1052
|
+
] },
|
|
1053
|
+
],
|
|
1054
|
+
},
|
|
1055
|
+
];
|
|
1056
|
+
|
|
1057
|
+
</script>
|
|
1058
|
+
<script>
|
|
1059
|
+
/* ============================================================
|
|
1060
|
+
ENGINE — renders CHAPTERS from content.js, drives the demos,
|
|
1061
|
+
and searches the Foundry-derived rules text. No framework,
|
|
1062
|
+
no build step to use: the whole app is one file.
|
|
1063
|
+
============================================================ */
|
|
1064
|
+
|
|
1065
|
+
const REF_META = (typeof GENERATED_REF_META !== "undefined") ? GENERATED_REF_META : {};
|
|
1066
|
+
const CONDITIONS = (typeof GENERATED_CONDITIONS !== "undefined") ? GENERATED_CONDITIONS : [];
|
|
1067
|
+
const ACTIONS = (typeof GENERATED_ACTIONS !== "undefined") ? GENERATED_ACTIONS : [];
|
|
1068
|
+
const CONDITION_BY_SLUG = {}; CONDITIONS.forEach((c) => { CONDITION_BY_SLUG[c.slug] = c; });
|
|
1069
|
+
const ACTION_BY_SLUG = {}; ACTIONS.forEach((a) => { ACTION_BY_SLUG[a.slug] = a; });
|
|
1070
|
+
const PC_BY_ID = {}; PREGENS.forEach((p) => { PC_BY_ID[p.id] = p; });
|
|
1071
|
+
|
|
1072
|
+
/* ============================================================
|
|
1073
|
+
ICONS — one stroke path per name, inheriting the accent.
|
|
1074
|
+
============================================================ */
|
|
1075
|
+
const ICONS = {
|
|
1076
|
+
compass: '<circle cx="12" cy="12" r="9"/><path d="M15.5 8.5 13 13l-4.5 2.5L11 11z"/>',
|
|
1077
|
+
compass2: '<path d="M12 3v2M12 19v2M3 12h2M19 12h2"/><circle cx="12" cy="12" r="7"/><path d="M9 15l2-4 4-2-2 4z"/>',
|
|
1078
|
+
sword: '<path d="M14.5 3.5 20 9l-8.5 8.5-2.5-.5-.5-2.5z"/><path d="M6 15l3 3"/><path d="M4.5 20.5 7 18"/><path d="M3.5 16.5 7.5 20.5"/>',
|
|
1079
|
+
speech: '<path d="M20 12.5c0 3.6-3.6 6.5-8 6.5a9.8 9.8 0 0 1-2.6-.34L5 20.5l1.2-3A6.6 6.6 0 0 1 4 12.5C4 8.9 7.6 6 12 6s8 2.9 8 6.5z"/>',
|
|
1080
|
+
anchor: '<circle cx="12" cy="5" r="2"/><path d="M12 7v13"/><path d="M8 11h8"/><path d="M5 14a7 7 0 0 0 14 0"/>',
|
|
1081
|
+
cards: '<rect x="3.5" y="6" width="12" height="14" rx="2"/><path d="M8 3.5h8.5a2 2 0 0 1 2 2V16"/>',
|
|
1082
|
+
menu: '<circle cx="12" cy="8" r="3.4"/><path d="M5.5 20a6.5 6.5 0 0 1 13 0"/>',
|
|
1083
|
+
roll: '<rect x="4" y="4" width="16" height="16" rx="3"/><circle cx="8.5" cy="8.5" r="1.1" fill="currentColor" stroke="none"/><circle cx="15.5" cy="15.5" r="1.1" fill="currentColor" stroke="none"/><circle cx="12" cy="12" r="1.1" fill="currentColor" stroke="none"/>',
|
|
1084
|
+
d20: '<path d="M12 2.8 20 7.4v9.2L12 21.2 4 16.6V7.4z"/><path d="M12 2.8 8 12l4 9.2L16 12z"/><path d="M4 7.4 8 12h8l4-4.6"/>',
|
|
1085
|
+
check: '<path d="M5 12.5 10 17.5 19 7"/>',
|
|
1086
|
+
x: '<path d="M6 6l12 12M18 6 6 18"/>',
|
|
1087
|
+
plus: '<path d="M12 5v14M5 12h14"/>',
|
|
1088
|
+
minus: '<path d="M5 12h14"/>',
|
|
1089
|
+
chevron: '<path d="M6 9l6 6 6-6"/>',
|
|
1090
|
+
arrow: '<path d="M5 12h14M13 6l6 6-6 6"/>',
|
|
1091
|
+
up: '<path d="M12 19V5M6 11l6-6 6 6"/>',
|
|
1092
|
+
down: '<path d="M12 5v14M6 13l6 6 6-6"/>',
|
|
1093
|
+
search: '<circle cx="11" cy="11" r="7"/><path d="M20 20l-3.5-3.5"/>',
|
|
1094
|
+
eye: '<path d="M2.5 12S6 6 12 6s9.5 6 9.5 6-3.5 6-9.5 6-9.5-6-9.5-6z"/><circle cx="12" cy="12" r="2.4"/>',
|
|
1095
|
+
secret: '<path d="M3 3l18 18"/><path d="M10.6 6.3A8.7 8.7 0 0 1 12 6c6 0 9.5 6 9.5 6a15 15 0 0 1-3.3 3.8M7 7.6A15 15 0 0 0 2.5 12S6 18 12 18a8.8 8.8 0 0 0 3.4-.68"/>',
|
|
1096
|
+
heart: '<path d="M12 20s-7-4.4-7-9.5A3.8 3.8 0 0 1 12 8a3.8 3.8 0 0 1 7 2.5C19 15.6 12 20 12 20z"/>',
|
|
1097
|
+
star: '<path d="M12 3.5l2.5 5.3 5.5.7-4 4 1 5.6L12 16.5 6.9 19l1-5.6-4-4 5.5-.7z"/>',
|
|
1098
|
+
shield: '<path d="M12 3 5 5.5v5.5c0 4 2.9 7 7 8.5 4.1-1.5 7-4.5 7-8.5V5.5z"/>',
|
|
1099
|
+
book: '<path d="M5 4.5A1.5 1.5 0 0 1 6.5 3H19v15H6.5A1.5 1.5 0 0 0 5 19.5z"/><path d="M5 19.5A1.5 1.5 0 0 1 6.5 18H19v3H6.5A1.5 1.5 0 0 1 5 19.5z"/>',
|
|
1100
|
+
install: '<path d="M12 3v11M8 10l4 4 4-4"/><path d="M5 20h14"/>',
|
|
1101
|
+
reset: '<path d="M4 12a8 8 0 1 1 2.5 5.8"/><path d="M4 19v-5h5"/>',
|
|
1102
|
+
link: '<path d="M10 13a4 4 0 0 0 5.7 0l2.6-2.6a4 4 0 0 0-5.7-5.7L11.2 6"/><path d="M14 11a4 4 0 0 0-5.7 0l-2.6 2.6a4 4 0 0 0 5.7 5.7L12.8 18"/>',
|
|
1103
|
+
};
|
|
1104
|
+
function iconSvg(name, cls) {
|
|
1105
|
+
const p = ICONS[name] || "";
|
|
1106
|
+
return `<svg class="icn${cls ? " " + cls : ""}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${p}</svg>`;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
/* ============================================================
|
|
1110
|
+
STATE — settings and reading progress, in this browser only.
|
|
1111
|
+
============================================================ */
|
|
1112
|
+
const LS_KEY = "pf2ePrimer.v1";
|
|
1113
|
+
let state = loadState();
|
|
1114
|
+
|
|
1115
|
+
function defaultState() {
|
|
1116
|
+
return { settings: { themeMode: "auto", custom: null, gm: false }, done: {} };
|
|
1117
|
+
}
|
|
1118
|
+
function loadState() {
|
|
1119
|
+
try {
|
|
1120
|
+
const raw = JSON.parse(localStorage.getItem(LS_KEY));
|
|
1121
|
+
if (!raw || typeof raw !== "object") return defaultState();
|
|
1122
|
+
const d = defaultState();
|
|
1123
|
+
return {
|
|
1124
|
+
settings: Object.assign(d.settings, raw.settings || {}),
|
|
1125
|
+
done: raw.done && typeof raw.done === "object" ? raw.done : {},
|
|
1126
|
+
};
|
|
1127
|
+
} catch (e) { return defaultState(); }
|
|
1128
|
+
}
|
|
1129
|
+
let _storageWarned = false;
|
|
1130
|
+
function saveState() {
|
|
1131
|
+
try { localStorage.setItem(LS_KEY, JSON.stringify(state)); }
|
|
1132
|
+
catch (e) { if (!_storageWarned) { _storageWarned = true; toast("Couldn't save progress in this browser"); } }
|
|
1133
|
+
}
|
|
1134
|
+
function gmMode() { return !!state.settings.gm; }
|
|
1135
|
+
function setGmMode(on) {
|
|
1136
|
+
state.settings.gm = !!on; saveState();
|
|
1137
|
+
document.body.classList.toggle("gmon", gmMode());
|
|
1138
|
+
renderHeader(); renderAll();
|
|
1139
|
+
toast(gmMode() ? "GM mode on — table notes shown" : "GM mode off");
|
|
1140
|
+
}
|
|
1141
|
+
function toggleGmMode() { setGmMode(!gmMode()); }
|
|
1142
|
+
|
|
1143
|
+
/* ============================================================
|
|
1144
|
+
SMALL HELPERS
|
|
1145
|
+
============================================================ */
|
|
1146
|
+
function sign(n) { n = Number(n) || 0; return (n >= 0 ? "+" : "") + n; }
|
|
1147
|
+
function escapeHtml(s) { return String(s == null ? "" : s).replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ }[c])); }
|
|
1148
|
+
function textToHtml(s) { return escapeHtml(s).replace(/\n/g, "<br>"); }
|
|
1149
|
+
function titleCase(s) { s = String(s || ""); return s ? s.charAt(0).toUpperCase() + s.slice(1) : ""; }
|
|
1150
|
+
/* Skill keys are lower-case ("sailing lore"); every word gets a capital. */
|
|
1151
|
+
function skillLabel(s) { return String(s || "").replace(/(^|\s)([a-z])/g, (m, sp, c) => sp + c.toUpperCase()); }
|
|
1152
|
+
function clamp(n, lo, hi) { return Math.max(lo, Math.min(hi, n)); }
|
|
1153
|
+
function byId(id) { return document.getElementById(id); }
|
|
1154
|
+
function setHTML(id, html) { const el = byId(id); if (el) el.innerHTML = html; }
|
|
1155
|
+
|
|
1156
|
+
/* ============================================================
|
|
1157
|
+
DICE & DEGREES OF SUCCESS — the one mechanic, in code.
|
|
1158
|
+
============================================================ */
|
|
1159
|
+
function d20() { return 1 + Math.floor(Math.random() * 20); }
|
|
1160
|
+
function dN(n) { return 1 + Math.floor(Math.random() * n); }
|
|
1161
|
+
|
|
1162
|
+
/* 0 critical failure · 1 failure · 2 success · 3 critical success */
|
|
1163
|
+
const DEG_LABEL = ["Critical failure", "Failure", "Success", "Critical success"];
|
|
1164
|
+
const DEG_CLASS = ["cfail", "fail", "succ", "csucc"];
|
|
1165
|
+
|
|
1166
|
+
/* The whole resolution rule: ten over or ten under moves a step,
|
|
1167
|
+
then a natural 20 or 1 moves one more. */
|
|
1168
|
+
function degreeOf(total, dc, nat) {
|
|
1169
|
+
let step = total >= dc + 10 ? 3 : total >= dc ? 2 : total <= dc - 10 ? 0 : 1;
|
|
1170
|
+
if (nat === 20) step = Math.min(3, step + 1);
|
|
1171
|
+
else if (nat === 1) step = Math.max(0, step - 1);
|
|
1172
|
+
return step;
|
|
1173
|
+
}
|
|
1174
|
+
/* Why it came out that way, in words — the point of the whole demo. */
|
|
1175
|
+
function degreeReason(total, dc, nat) {
|
|
1176
|
+
const base = total >= dc + 10 ? 3 : total >= dc ? 2 : total <= dc - 10 ? 0 : 1;
|
|
1177
|
+
const by = total - dc;
|
|
1178
|
+
let why;
|
|
1179
|
+
if (base === 3) why = `beat DC ${dc} by ${by} — ten or more over is a critical success`;
|
|
1180
|
+
else if (base === 2) why = `beat DC ${dc} by ${by}`;
|
|
1181
|
+
else if (base === 1) why = `missed DC ${dc} by ${Math.abs(by)}`;
|
|
1182
|
+
else why = `missed DC ${dc} by ${Math.abs(by)} — ten or more under is a critical failure`;
|
|
1183
|
+
if (nat === 20 && base < 3) why += `; natural 20 moves it one step better`;
|
|
1184
|
+
else if (nat === 20) why += `; already a critical success, the natural 20 adds nothing`;
|
|
1185
|
+
else if (nat === 1 && base > 0) why += `; natural 1 moves it one step worse`;
|
|
1186
|
+
else if (nat === 1) why += `; already a critical failure, the natural 1 adds nothing`;
|
|
1187
|
+
return why;
|
|
1188
|
+
}
|
|
1189
|
+
function rollCheck(mod, dc) {
|
|
1190
|
+
const nat = d20(), total = nat + mod;
|
|
1191
|
+
const deg = degreeOf(total, dc, nat);
|
|
1192
|
+
return { nat, mod, total, dc, deg, why: degreeReason(total, dc, nat) };
|
|
1193
|
+
}
|
|
1194
|
+
/* "1d8+4" / "2d6" -> {total, detail} */
|
|
1195
|
+
function rollDamage(expr, bonusDmg) {
|
|
1196
|
+
const m = /^(\d+)d(\d+)\s*([+-]\s*\d+)?$/.exec(String(expr).trim());
|
|
1197
|
+
if (!m) return { total: 0, detail: expr };
|
|
1198
|
+
const n = Number(m[1]), faces = Number(m[2]);
|
|
1199
|
+
const flat = m[3] ? Number(m[3].replace(/\s+/g, "")) : 0;
|
|
1200
|
+
const extra = Number(bonusDmg) || 0;
|
|
1201
|
+
const dice = []; let sum = 0;
|
|
1202
|
+
for (let i = 0; i < n; i++) { const r = dN(faces); dice.push(r); sum += r; }
|
|
1203
|
+
const total = sum + flat + extra;
|
|
1204
|
+
const parts = [`${n}d${faces} (${dice.join("+")})`];
|
|
1205
|
+
if (flat) parts.push(sign(flat).replace("+", "+ ").replace("-", "− "));
|
|
1206
|
+
if (extra) parts.push(`+ ${extra} status`);
|
|
1207
|
+
return { total, detail: `${parts.join(" ")} = ${total}` };
|
|
1208
|
+
}
|
|
1209
|
+
function degChip(deg) { return `<span class="deg ${DEG_CLASS[deg]}">${DEG_LABEL[deg]}</span>`; }
|
|
1210
|
+
function natSpan(nat) {
|
|
1211
|
+
const cls = nat === 20 ? "nat nat20" : nat === 1 ? "nat nat1" : "nat";
|
|
1212
|
+
return `<span class="${cls}">${nat}</span>`;
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
/* ============================================================
|
|
1216
|
+
NAVIGATION
|
|
1217
|
+
============================================================ */
|
|
1218
|
+
const VIEWS = CHAPTERS.map((c) => c.key);
|
|
1219
|
+
let _view = VIEWS[0];
|
|
1220
|
+
|
|
1221
|
+
function go(key) {
|
|
1222
|
+
if (key === "menu") return openMenu();
|
|
1223
|
+
if (VIEWS.indexOf(key) === -1) key = VIEWS[0];
|
|
1224
|
+
_view = key;
|
|
1225
|
+
byId("view-menu").classList.add("hide");
|
|
1226
|
+
VIEWS.forEach((k) => {
|
|
1227
|
+
byId("view-" + k).classList.toggle("hide", k !== key);
|
|
1228
|
+
const b = byId("nav-" + k); if (b) b.classList.toggle("on", k === key);
|
|
1229
|
+
});
|
|
1230
|
+
render(key);
|
|
1231
|
+
window.scrollTo(0, 0);
|
|
1232
|
+
}
|
|
1233
|
+
function currentView() { return byId("view-menu").classList.contains("hide") ? _view : "menu"; }
|
|
1234
|
+
function render(key) {
|
|
1235
|
+
const ch = CHAPTERS.find((c) => c.key === key);
|
|
1236
|
+
if (ch) setHTML("view-" + key, chapterHTML(ch));
|
|
1237
|
+
mountDemos(key);
|
|
1238
|
+
}
|
|
1239
|
+
function renderAll() { renderHeader(); const v = currentView(); if (v === "menu") renderMenu(); else render(v); }
|
|
1240
|
+
function openMenu() {
|
|
1241
|
+
VIEWS.forEach((k) => byId("view-" + k).classList.add("hide"));
|
|
1242
|
+
byId("view-menu").classList.remove("hide");
|
|
1243
|
+
renderMenu(); window.scrollTo(0, 0);
|
|
1244
|
+
}
|
|
1245
|
+
function closeMenu() { go(_view); }
|
|
1246
|
+
let _toastTimer = null;
|
|
1247
|
+
function toast(msg) {
|
|
1248
|
+
const t = byId("toast"); if (!t) return;
|
|
1249
|
+
t.textContent = msg; t.classList.add("show");
|
|
1250
|
+
clearTimeout(_toastTimer); _toastTimer = setTimeout(() => t.classList.remove("show"), 1800);
|
|
1251
|
+
}
|
|
1252
|
+
function renderHeader() {
|
|
1253
|
+
const done = CHAPTERS.filter((c) => state.done[c.key]).length;
|
|
1254
|
+
const pct = Math.round((done / CHAPTERS.length) * 100);
|
|
1255
|
+
setHTML("progressBar", `<span style="width:${pct}%"></span>`);
|
|
1256
|
+
const sub = byId("headSub");
|
|
1257
|
+
if (sub) sub.innerHTML = gmMode()
|
|
1258
|
+
? `<b>GM mode</b> — table notes are showing · ${done}/${CHAPTERS.length} tabs read`
|
|
1259
|
+
: `Pathfinder 2e (Remaster), for people who've played something else · ${done}/${CHAPTERS.length} tabs read`;
|
|
1260
|
+
const gb = byId("gmBtn");
|
|
1261
|
+
if (gb) { gb.classList.toggle("on", gmMode()); gb.title = gmMode() ? "GM mode on" : "GM mode off"; }
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
/* ============================================================
|
|
1265
|
+
CHAPTER RENDERING
|
|
1266
|
+
============================================================ */
|
|
1267
|
+
function chapterHTML(ch) {
|
|
1268
|
+
const body = ch.sections.map((s) => sectionHTML(s, ch)).join("");
|
|
1269
|
+
const idx = VIEWS.indexOf(ch.key);
|
|
1270
|
+
const next = CHAPTERS[idx + 1];
|
|
1271
|
+
const isDone = !!state.done[ch.key];
|
|
1272
|
+
const foot = `
|
|
1273
|
+
<div class="chapfoot">
|
|
1274
|
+
<button class="btn ${isDone ? "secondary" : ""}" onclick="markRead('${ch.key}')">
|
|
1275
|
+
${iconSvg(isDone ? "check" : "check")} ${isDone ? "Read" : "Got it"}${next ? " — on to " + escapeHtml(next.label) : ""}
|
|
1276
|
+
</button>
|
|
1277
|
+
${next ? `<p class="meta center">Next: <b>${escapeHtml(next.title)}</b></p>` : `<p class="meta center">That's the lot. Go and play.</p>`}
|
|
1278
|
+
</div>`;
|
|
1279
|
+
return `
|
|
1280
|
+
<div class="chaphead">
|
|
1281
|
+
<h1>${iconSvg(ch.icon)} ${escapeHtml(ch.title)}</h1>
|
|
1282
|
+
<p class="lede">${escapeHtml(ch.lede)}</p>
|
|
1283
|
+
</div>
|
|
1284
|
+
${body}${foot}`;
|
|
1285
|
+
}
|
|
1286
|
+
function markRead(key) {
|
|
1287
|
+
state.done[key] = true; saveState(); renderHeader();
|
|
1288
|
+
const idx = VIEWS.indexOf(key);
|
|
1289
|
+
const next = CHAPTERS[idx + 1];
|
|
1290
|
+
if (next) go(next.key); else { render(key); toast("Primer finished"); }
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
function sectionHTML(s, ch) {
|
|
1294
|
+
switch (s.type) {
|
|
1295
|
+
case "prose": return proseHTML(s);
|
|
1296
|
+
case "note": return noteHTML(s);
|
|
1297
|
+
case "bridge": return bridgeHTML(s);
|
|
1298
|
+
case "map": return mapHTML(s);
|
|
1299
|
+
case "cards": return cardsHTML(s);
|
|
1300
|
+
case "steps": return stepsHTML(s);
|
|
1301
|
+
case "demo": return demoShellHTML(s);
|
|
1302
|
+
case "ref": return refHTML(s);
|
|
1303
|
+
case "gm": return gmHTML(s);
|
|
1304
|
+
case "links": return linksHTML();
|
|
1305
|
+
case "quickcard": return quickCardHTML();
|
|
1306
|
+
case "glossary": return glossaryHTML(s);
|
|
1307
|
+
case "browser": return browserShellHTML();
|
|
1308
|
+
case "pregens": return pregensHTML(s);
|
|
1309
|
+
case "reactions": return annotatedRefHTML(s, REACTIONS);
|
|
1310
|
+
case "stops": return annotatedRefHTML(s, EXPLORATION_STOPS);
|
|
1311
|
+
default: return "";
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
function h2(s) { return s.h ? `<h2>${escapeHtml(s.h)}</h2>` : ""; }
|
|
1315
|
+
function proseHTML(s) {
|
|
1316
|
+
return `<section class="sec">${h2(s)}${(s.p || []).map((p) => `<p>${p}</p>`).join("")}</section>`;
|
|
1317
|
+
}
|
|
1318
|
+
function noteHTML(s) {
|
|
1319
|
+
return `<aside class="callout ${s.tone === "warn" ? "warn" : "tip"}">${(s.p || []).map((p) => `<p>${p}</p>`).join("")}</aside>`;
|
|
1320
|
+
}
|
|
1321
|
+
function bridgeHTML(s) {
|
|
1322
|
+
const rows = BRIDGE.map(([a, b]) => `
|
|
1323
|
+
<div class="brow">
|
|
1324
|
+
<div class="bfrom">${escapeHtml(a)}</div>
|
|
1325
|
+
<div class="barrow">${iconSvg("arrow")}</div>
|
|
1326
|
+
<div class="bto">${b}</div>
|
|
1327
|
+
</div>`).join("");
|
|
1328
|
+
return `<section class="sec">${h2(s)}<div class="bridge">${rows}</div></section>`;
|
|
1329
|
+
}
|
|
1330
|
+
function mapHTML(s) {
|
|
1331
|
+
const cols = [
|
|
1332
|
+
{ pillar: "Fighting", mode: "Encounter mode", grain: "6-second rounds", note: "Initiative, three actions a turn, a grid if you like one.", tab: "fight" },
|
|
1333
|
+
{ pillar: "Exploring", mode: "Exploration mode", grain: "minutes & hours", note: "Activities instead of actions. The GM rolls some of your dice.", tab: "explore" },
|
|
1334
|
+
{ pillar: "Talking", mode: "any mode", grain: "however long it takes", note: "No gear of its own — a parley mid-fight is still encounter mode.", tab: "talk" },
|
|
1335
|
+
];
|
|
1336
|
+
return `<section class="sec">${h2(s)}
|
|
1337
|
+
<div class="pmap">
|
|
1338
|
+
${cols.map((c) => `
|
|
1339
|
+
<button class="pcol" onclick="go('${c.tab}')">
|
|
1340
|
+
<span class="ppillar">${escapeHtml(c.pillar)}</span>
|
|
1341
|
+
<span class="pmode">${escapeHtml(c.mode)}</span>
|
|
1342
|
+
<span class="pgrain">${escapeHtml(c.grain)}</span>
|
|
1343
|
+
<span class="pnote">${escapeHtml(c.note)}</span>
|
|
1344
|
+
</button>`).join("")}
|
|
1345
|
+
</div>
|
|
1346
|
+
<button class="pcol wide" onclick="go('ashore')">
|
|
1347
|
+
<span class="ppillar">Everything else</span>
|
|
1348
|
+
<span class="pmode">Downtime mode</span>
|
|
1349
|
+
<span class="pgrain">days & weeks</span>
|
|
1350
|
+
<span class="pnote">The gear your last game probably didn't have: earning a living, crafting, and un-picking a build you regret.</span>
|
|
1351
|
+
</button>
|
|
1352
|
+
</section>`;
|
|
1353
|
+
}
|
|
1354
|
+
function cardsHTML(s) {
|
|
1355
|
+
return `<section class="sec">${h2(s)}
|
|
1356
|
+
<div class="cardgrid">
|
|
1357
|
+
${(s.items || []).map((i) => `<div class="ccard"><h3>${i.h}</h3><p>${i.p}</p></div>`).join("")}
|
|
1358
|
+
</div></section>`;
|
|
1359
|
+
}
|
|
1360
|
+
function stepsHTML(s) {
|
|
1361
|
+
return `<section class="sec">${h2(s)}
|
|
1362
|
+
<ol class="steps">${(s.items || []).map((i) => `<li>${i}</li>`).join("")}</ol></section>`;
|
|
1363
|
+
}
|
|
1364
|
+
function gmHTML(s) {
|
|
1365
|
+
return `<section class="sec gmsec">
|
|
1366
|
+
<div class="gmhead">${iconSvg("eye")} ${escapeHtml(s.h || "At the table")}</div>
|
|
1367
|
+
${(s.p || []).map((p) => `<p>${p}</p>`).join("")}
|
|
1368
|
+
</section>`;
|
|
1369
|
+
}
|
|
1370
|
+
function linksHTML() {
|
|
1371
|
+
/* Relative on purpose. On GitHub Pages this page is /pf2eprimer/, so "../pf2epartytracker/"
|
|
1372
|
+
lands on the sibling; self-hosted in the PF2e Toolbox the same path is redirected to
|
|
1373
|
+
/tracker/. Absolute GitHub URLs would send someone reading this offline, on a boat,
|
|
1374
|
+
out to the internet for a tool sitting on the same server. */
|
|
1375
|
+
const tools = [
|
|
1376
|
+
{ name: "PF2e Party Tracker", url: "../pf2epartytracker/",
|
|
1377
|
+
note: "GM-side: everyone's passive DCs on one screen, secret checks rolled for the whole party at once, and the exploration activity board from this tab." },
|
|
1378
|
+
{ name: "PF2e Spellbook", url: "../pf2espellcards/",
|
|
1379
|
+
note: "Player-side: prepare the day's spells, track castings and focus points, and see what a spell actually does at the rank you're casting it." },
|
|
1380
|
+
];
|
|
1381
|
+
return `<section class="sec">
|
|
1382
|
+
<h2>The other two tools</h2>
|
|
1383
|
+
<p class="meta">Same idea as this page — one file, works offline, nothing leaves your device.</p>
|
|
1384
|
+
${tools.map((t) => `
|
|
1385
|
+
<a class="toolcard" href="${t.url}" target="_blank" rel="noopener">
|
|
1386
|
+
<span class="tname">${iconSvg("link")} ${escapeHtml(t.name)}</span>
|
|
1387
|
+
<span class="tnote">${escapeHtml(t.note)}</span>
|
|
1388
|
+
</a>`).join("")}
|
|
1389
|
+
</section>`;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
/* ---- The two pregens, with the arithmetic left in ---- */
|
|
1393
|
+
function pregensHTML(s) {
|
|
1394
|
+
return `<section class="sec">${h2(s)}
|
|
1395
|
+
<div class="cardgrid">${PREGENS.map((pc) => {
|
|
1396
|
+
const topSkills = Object.keys(pc.skills).slice(0, 4)
|
|
1397
|
+
.map((k) => `${skillLabel(k)} ${sign(pc.skills[k])}`).join(" · ");
|
|
1398
|
+
return `<div class="ccard pcard">
|
|
1399
|
+
<h3>${escapeHtml(pc.name)}</h3>
|
|
1400
|
+
<p class="meta">Level ${pc.level} ${escapeHtml(pc.ancestry)} ${escapeHtml(pc.cls)} — ${escapeHtml(pc.tag)}</p>
|
|
1401
|
+
<p>${escapeHtml(pc.blurb)}</p>
|
|
1402
|
+
<div class="statrow">
|
|
1403
|
+
<span class="statbox"><b>${pc.hp}</b><span>HP</span></span>
|
|
1404
|
+
<span class="statbox"><b>${pc.ac}</b><span>AC</span></span>
|
|
1405
|
+
<span class="statbox"><b>${sign(pc.perception)}</b><span>Perception</span></span>
|
|
1406
|
+
<span class="statbox"><b>${sign(pc.saves.fortitude)}/${sign(pc.saves.reflex)}/${sign(pc.saves.will)}</b><span>Fort / Ref / Will</span></span>
|
|
1407
|
+
</div>
|
|
1408
|
+
<p class="meta">${escapeHtml(topSkills)}${pc.spellDC ? ` · spell DC ${pc.spellDC}` : ""}</p>
|
|
1409
|
+
<p class="meta"><b>AC ${pc.ac}</b> = ${escapeHtml(pc.acNote)}. <b>HP ${pc.hp}</b> = ${pc.hpParts.ancestry} ancestry + ${pc.hpParts.cls} class + ${pc.hpParts.con} Constitution.</p>
|
|
1410
|
+
<p class="meta">${escapeHtml(pc.build)}</p>
|
|
1411
|
+
</div>`;
|
|
1412
|
+
}).join("")}</div>
|
|
1413
|
+
<p class="meta">Made up for this page, and legal level-1 builds. Every modifier on them is ability + proficiency, and proficiency is your level plus the rank bonus.</p>
|
|
1414
|
+
</section>`;
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
/* ---- Rules text pulled straight from the Foundry-derived data ---- */
|
|
1418
|
+
function refHTML(s) {
|
|
1419
|
+
const items = (s.slugs || []).map((slug) => {
|
|
1420
|
+
const isCond = s.kind === "condition";
|
|
1421
|
+
const rec = isCond ? CONDITION_BY_SLUG[slug] : ACTION_BY_SLUG[slug];
|
|
1422
|
+
if (!rec) return "";
|
|
1423
|
+
const kind = isCond ? (rec.valued ? "condition · has a value" : "condition") : actionCost(rec);
|
|
1424
|
+
return `<details class="ref">
|
|
1425
|
+
<summary><span class="refname">${escapeHtml(rec.name)}</span><span class="refkind">${escapeHtml(kind)}</span></summary>
|
|
1426
|
+
<div class="refbody">${textToHtml(rec.description)}</div>
|
|
1427
|
+
</details>`;
|
|
1428
|
+
}).join("");
|
|
1429
|
+
return `<section class="sec">${h2(s)}${s.p ? `<p class="meta">${s.p}</p>` : ""}${items}</section>`;
|
|
1430
|
+
}
|
|
1431
|
+
/* A ref list where each entry carries our own one-line note above the
|
|
1432
|
+
book's text — used for the reaction and "party has stopped" lists. */
|
|
1433
|
+
function annotatedRefHTML(s, list) {
|
|
1434
|
+
const items = list.map((entry) => {
|
|
1435
|
+
const rec = ACTION_BY_SLUG[entry.slug];
|
|
1436
|
+
if (!rec) return "";
|
|
1437
|
+
return `<details class="ref">
|
|
1438
|
+
<summary>
|
|
1439
|
+
<span class="refname">${escapeHtml(entry.label)} <span class="refnote">${escapeHtml(entry.note)}</span></span>
|
|
1440
|
+
<span class="refkind">${escapeHtml(actionCost(rec))}</span>
|
|
1441
|
+
</summary>
|
|
1442
|
+
<div class="refbody">${textToHtml(rec.description)}</div>
|
|
1443
|
+
</details>`;
|
|
1444
|
+
}).join("");
|
|
1445
|
+
return `<section class="sec">${h2(s)}${s.p ? `<p class="meta">${s.p}</p>` : ""}${items}</section>`;
|
|
1446
|
+
}
|
|
1447
|
+
function actionCost(a) {
|
|
1448
|
+
const n = a.actions;
|
|
1449
|
+
if (a.actionType === "reaction") return "⤳ reaction";
|
|
1450
|
+
if (a.actionType === "free") return "◇ free";
|
|
1451
|
+
if (n === 1) return "◆ 1 action";
|
|
1452
|
+
if (n === 2) return "◆◆ 2 actions";
|
|
1453
|
+
if (n === 3) return "◆◆◆ 3 actions";
|
|
1454
|
+
if (a.traits && a.traits.indexOf("downtime") !== -1) return "downtime activity";
|
|
1455
|
+
if (a.exploration) return "exploration activity";
|
|
1456
|
+
return "activity";
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
/* ============================================================
|
|
1460
|
+
QUICK CARD & GLOSSARY
|
|
1461
|
+
============================================================ */
|
|
1462
|
+
function quickCardHTML() {
|
|
1463
|
+
return `<section class="sec">
|
|
1464
|
+
<h2>The quick card</h2>
|
|
1465
|
+
<div class="qcard">
|
|
1466
|
+
<div class="qbox">
|
|
1467
|
+
<h3>Every roll</h3>
|
|
1468
|
+
<p class="big">d20 + modifier vs DC</p>
|
|
1469
|
+
<ul>
|
|
1470
|
+
<li><b>Beat it by 10+</b> → critical success</li>
|
|
1471
|
+
<li><b>Meet or beat it</b> → success</li>
|
|
1472
|
+
<li><b>Miss by 1–9</b> → failure</li>
|
|
1473
|
+
<li><b>Miss by 10+</b> → critical failure</li>
|
|
1474
|
+
<li>Natural <b>20</b> moves one step better, natural <b>1</b> one step worse</li>
|
|
1475
|
+
</ul>
|
|
1476
|
+
</div>
|
|
1477
|
+
<div class="qbox">
|
|
1478
|
+
<h3>Your turn</h3>
|
|
1479
|
+
<p class="big">◆ ◆ ◆ + ⤳</p>
|
|
1480
|
+
<ul>
|
|
1481
|
+
<li>Three actions, any mix, any order</li>
|
|
1482
|
+
<li>One reaction, on someone else's turn</li>
|
|
1483
|
+
<li>2nd attack <b>−5</b>, 3rd <b>−10</b> (agile <b>−4 / −8</b>)</li>
|
|
1484
|
+
<li>The penalty resets at the start of your turn</li>
|
|
1485
|
+
<li>Trip, Grapple, Shove and spell attacks are attacks too</li>
|
|
1486
|
+
</ul>
|
|
1487
|
+
</div>
|
|
1488
|
+
<div class="qbox">
|
|
1489
|
+
<h3>Modifiers</h3>
|
|
1490
|
+
<p class="big">ability + level + rank</p>
|
|
1491
|
+
<ul>
|
|
1492
|
+
<li>Trained <b>+2</b> · expert <b>+4</b> · master <b>+6</b> · legendary <b>+8</b></li>
|
|
1493
|
+
<li>Untrained adds <b>nothing</b> — not even your level</li>
|
|
1494
|
+
<li>Any modifier becomes a DC by adding <b>10</b></li>
|
|
1495
|
+
<li>Circumstance / status / item: same type doesn't stack</li>
|
|
1496
|
+
</ul>
|
|
1497
|
+
</div>
|
|
1498
|
+
<div class="qbox">
|
|
1499
|
+
<h3>Going down</h3>
|
|
1500
|
+
<p class="big">dying 1 → 4 = dead</p>
|
|
1501
|
+
<ul>
|
|
1502
|
+
<li>0 HP: <b>dying 1</b> (dying 2 from a critical hit)</li>
|
|
1503
|
+
<li>Start of your turn: flat check vs <b>DC 10 + dying</b></li>
|
|
1504
|
+
<li>Success −1 dying, critical success −2, failure +1, critical failure +2</li>
|
|
1505
|
+
<li>Shaking it off gives you <b>wounded 1</b> — it adds to your next dying value</li>
|
|
1506
|
+
<li>Spend <b>all</b> your hero points to escape death</li>
|
|
1507
|
+
</ul>
|
|
1508
|
+
</div>
|
|
1509
|
+
</div>
|
|
1510
|
+
</section>`;
|
|
1511
|
+
}
|
|
1512
|
+
function glossaryHTML(s) {
|
|
1513
|
+
return `<section class="sec">${h2(s)}
|
|
1514
|
+
<dl class="gloss">${GLOSSARY.map(([t, d]) => `<dt>${escapeHtml(t)}</dt><dd>${escapeHtml(d)}</dd>`).join("")}</dl>
|
|
1515
|
+
</section>`;
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
/* ============================================================
|
|
1519
|
+
RULES BROWSER — every condition and action in the bundle.
|
|
1520
|
+
============================================================ */
|
|
1521
|
+
let _browseFilter = "all";
|
|
1522
|
+
function browserShellHTML() {
|
|
1523
|
+
return `<section class="sec">
|
|
1524
|
+
<h2>Look something up</h2>
|
|
1525
|
+
<p class="meta">${CONDITIONS.length} conditions and ${ACTIONS.length} actions — basic, skill, exploration and downtime — in Paizo's own words.</p>
|
|
1526
|
+
<div class="searchbar">${iconSvg("search")}<input type="search" id="browseQ" placeholder="frightened, demoralize, treat wounds…" oninput="renderBrowseList()"></div>
|
|
1527
|
+
<div class="chips" id="browseChips"></div>
|
|
1528
|
+
<div id="browseList"></div>
|
|
1529
|
+
</section>`;
|
|
1530
|
+
}
|
|
1531
|
+
const BROWSE_FILTERS = [
|
|
1532
|
+
{ key: "all", label: "Everything" },
|
|
1533
|
+
{ key: "condition", label: "Conditions" },
|
|
1534
|
+
{ key: "basic", label: "Basic actions" },
|
|
1535
|
+
{ key: "skill", label: "Skill actions" },
|
|
1536
|
+
{ key: "exploration", label: "Exploration" },
|
|
1537
|
+
{ key: "downtime", label: "Downtime" },
|
|
1538
|
+
];
|
|
1539
|
+
function setBrowseFilter(k) { _browseFilter = k; renderBrowseChips(); renderBrowseList(); }
|
|
1540
|
+
function renderBrowseChips() {
|
|
1541
|
+
setHTML("browseChips", BROWSE_FILTERS.map((f) =>
|
|
1542
|
+
`<button class="chip${_browseFilter === f.key ? " on" : ""}" onclick="setBrowseFilter('${f.key}')">${escapeHtml(f.label)}</button>`).join(""));
|
|
1543
|
+
}
|
|
1544
|
+
function browseMatches() {
|
|
1545
|
+
const q = ((byId("browseQ") || {}).value || "").trim().toLowerCase();
|
|
1546
|
+
const hit = (name, desc) => !q || name.toLowerCase().indexOf(q) !== -1 || desc.toLowerCase().indexOf(q) !== -1;
|
|
1547
|
+
const out = [];
|
|
1548
|
+
if (_browseFilter === "all" || _browseFilter === "condition") {
|
|
1549
|
+
CONDITIONS.forEach((c) => { if (hit(c.name, c.description)) out.push({ name: c.name, kind: c.valued ? "condition · has a value" : "condition", desc: c.description, sort: 0 }); });
|
|
1550
|
+
}
|
|
1551
|
+
ACTIONS.forEach((a) => {
|
|
1552
|
+
const t = a.traits || [];
|
|
1553
|
+
const isDown = t.indexOf("downtime") !== -1;
|
|
1554
|
+
const isExp = !!a.exploration;
|
|
1555
|
+
const cat = a.category || "";
|
|
1556
|
+
let bucket = "skill";
|
|
1557
|
+
if (isDown) bucket = "downtime";
|
|
1558
|
+
else if (isExp) bucket = "exploration";
|
|
1559
|
+
else if (cat === "basic" || cat === "interaction" || cat === "defensive" || cat === "offensive") bucket = "basic";
|
|
1560
|
+
if (_browseFilter !== "all" && _browseFilter !== bucket) return;
|
|
1561
|
+
if (_browseFilter === "condition") return;
|
|
1562
|
+
if (hit(a.name, a.description)) out.push({ name: a.name, kind: actionCost(a), desc: a.description, sort: 1 });
|
|
1563
|
+
});
|
|
1564
|
+
out.sort((x, y) => x.name.localeCompare(y.name));
|
|
1565
|
+
return out;
|
|
1566
|
+
}
|
|
1567
|
+
function renderBrowseList() {
|
|
1568
|
+
const items = browseMatches();
|
|
1569
|
+
if (!items.length) { setHTML("browseList", `<p class="empty">Nothing matches that.</p>`); return; }
|
|
1570
|
+
setHTML("browseList", items.slice(0, 200).map((i) => `
|
|
1571
|
+
<details class="ref">
|
|
1572
|
+
<summary><span class="refname">${escapeHtml(i.name)}</span><span class="refkind">${escapeHtml(i.kind)}</span></summary>
|
|
1573
|
+
<div class="refbody">${textToHtml(i.desc)}</div>
|
|
1574
|
+
</details>`).join("")
|
|
1575
|
+
+ (items.length > 200 ? `<p class="meta center">…and ${items.length - 200} more. Narrow the search.</p>` : ""));
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
/* ============================================================
|
|
1579
|
+
DEMOS — shells are rendered with the chapter, then mounted.
|
|
1580
|
+
============================================================ */
|
|
1581
|
+
function demoShellHTML(s) {
|
|
1582
|
+
return `<section class="sec demo">
|
|
1583
|
+
<div class="demohead">${iconSvg("roll")}<h2>${escapeHtml(s.h || "Try it")}</h2></div>
|
|
1584
|
+
${s.p ? `<p class="meta">${escapeHtml(s.p)}</p>` : ""}
|
|
1585
|
+
<div id="demo-${s.id}" class="demobody"></div>
|
|
1586
|
+
</section>`;
|
|
1587
|
+
}
|
|
1588
|
+
function mountDemos(key) {
|
|
1589
|
+
const ch = CHAPTERS.find((c) => c.key === key);
|
|
1590
|
+
if (!ch) return;
|
|
1591
|
+
ch.sections.forEach((s) => {
|
|
1592
|
+
if (s.type === "demo" && DEMOS[s.id]) DEMOS[s.id]();
|
|
1593
|
+
if (s.type === "browser") { renderBrowseChips(); renderBrowseList(); }
|
|
1594
|
+
});
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
/* ---------------------------------------------------------------
|
|
1598
|
+
1. THE CHECK — degrees of success, with the arithmetic shown.
|
|
1599
|
+
--------------------------------------------------------------- */
|
|
1600
|
+
let checkState = { who: "mari", what: null, dc: 15 };
|
|
1601
|
+
function checkOptions(pc) {
|
|
1602
|
+
const out = [];
|
|
1603
|
+
out.push({ key: "perception", label: `Perception ${sign(pc.perception)}`, mod: pc.perception, rank: pc.perceptionRank });
|
|
1604
|
+
Object.keys(pc.saves).forEach((k) => out.push({ key: "save-" + k, label: `${titleCase(k)} save ${sign(pc.saves[k])}`, mod: pc.saves[k], rank: pc.saveRanks[k] }));
|
|
1605
|
+
Object.keys(pc.skills).forEach((k) => out.push({ key: "sk-" + k, label: `${skillLabel(k)} ${sign(pc.skills[k])}`, mod: pc.skills[k], rank: "trained" }));
|
|
1606
|
+
if (pc.spellAttack) out.push({ key: "spellatk", label: `Spell attack ${sign(pc.spellAttack)}`, mod: pc.spellAttack, rank: "trained" });
|
|
1607
|
+
out.push({ key: "untrained", label: `An untrained skill ${sign(0)}`, mod: 0, rank: "untrained" });
|
|
1608
|
+
return out;
|
|
1609
|
+
}
|
|
1610
|
+
const DC_PRESETS = [
|
|
1611
|
+
{ label: "Simple DC 15 (trained)", dc: 15 },
|
|
1612
|
+
{ label: `${FOE.name}'s AC 16`, dc: 16 },
|
|
1613
|
+
{ label: `${FOE.name}'s Will DC 13`, dc: 13 },
|
|
1614
|
+
{ label: `${FOE.name}'s Fortitude DC 17`, dc: 17 },
|
|
1615
|
+
{ label: "A hard one: DC 20", dc: 20 },
|
|
1616
|
+
];
|
|
1617
|
+
const DEMOS = {};
|
|
1618
|
+
DEMOS.check = function () {
|
|
1619
|
+
const host = byId("demo-check"); if (!host) return;
|
|
1620
|
+
const pc = PC_BY_ID[checkState.who] || PREGENS[0];
|
|
1621
|
+
const opts = checkOptions(pc);
|
|
1622
|
+
if (!checkState.what || !opts.find((o) => o.key === checkState.what)) checkState.what = opts[0].key;
|
|
1623
|
+
host.innerHTML = `
|
|
1624
|
+
<div class="controls">
|
|
1625
|
+
<label class="field inline"><span class="name">Who's rolling</span>
|
|
1626
|
+
<select onchange="checkSet('who', this.value)">
|
|
1627
|
+
${PREGENS.map((p) => `<option value="${p.id}"${p.id === checkState.who ? " selected" : ""}>${escapeHtml(p.short)} — ${escapeHtml(p.ancestry)} ${escapeHtml(p.cls)}</option>`).join("")}
|
|
1628
|
+
</select></label>
|
|
1629
|
+
<label class="field inline"><span class="name">Rolling what</span>
|
|
1630
|
+
<select onchange="checkSet('what', this.value)">
|
|
1631
|
+
${opts.map((o) => `<option value="${o.key}"${o.key === checkState.what ? " selected" : ""}>${escapeHtml(o.label)}</option>`).join("")}
|
|
1632
|
+
</select></label>
|
|
1633
|
+
<label class="field inline narrow"><span class="name">Against DC</span>
|
|
1634
|
+
<input type="number" id="checkDC" value="${checkState.dc}" onchange="checkSet('dc', this.value)"></label>
|
|
1635
|
+
</div>
|
|
1636
|
+
<div class="chips">${DC_PRESETS.map((p) => `<button class="chip${p.dc === checkState.dc ? " on" : ""}" onclick="checkSet('dc', ${p.dc})">${escapeHtml(p.label)}</button>`).join("")}</div>
|
|
1637
|
+
<div class="row tight">
|
|
1638
|
+
<button class="btn sm" onclick="rollTheCheck(1)">${iconSvg("d20")} Roll once</button>
|
|
1639
|
+
<button class="btn sm secondary" onclick="rollTheCheck(20)">Roll twenty</button>
|
|
1640
|
+
</div>
|
|
1641
|
+
<div id="checkOut" class="out"></div>`;
|
|
1642
|
+
};
|
|
1643
|
+
function checkSet(k, v) {
|
|
1644
|
+
if (k === "dc") checkState.dc = clamp(Math.round(Number(v) || 0), 1, 60);
|
|
1645
|
+
else checkState[k] = v;
|
|
1646
|
+
if (k === "who") checkState.what = null;
|
|
1647
|
+
DEMOS.check();
|
|
1648
|
+
}
|
|
1649
|
+
function rollTheCheck(times) {
|
|
1650
|
+
const pc = PC_BY_ID[checkState.who];
|
|
1651
|
+
const opt = checkOptions(pc).find((o) => o.key === checkState.what);
|
|
1652
|
+
const dc = checkState.dc;
|
|
1653
|
+
if (times === 1) {
|
|
1654
|
+
const r = rollCheck(opt.mod, dc);
|
|
1655
|
+
setHTML("checkOut", `
|
|
1656
|
+
<div class="resultcard ${DEG_CLASS[r.deg]}">
|
|
1657
|
+
<div class="rline"><span class="rmath">d20 ${natSpan(r.nat)} ${sign(r.mod)} =</span> <span class="rtotal">${r.total}</span> <span class="rmath">vs DC ${dc}</span></div>
|
|
1658
|
+
<div class="rdeg">${degChip(r.deg)}</div>
|
|
1659
|
+
<p class="rwhy">${escapeHtml(titleCase(r.why))}.</p>
|
|
1660
|
+
<p class="meta">${escapeHtml(pc.short)}'s ${escapeHtml(opt.label.replace(/\s[+-]\d+$/, ""))} is ${sign(opt.mod)} — ${escapeHtml(opt.rank)}${opt.rank === "untrained" ? ", so nothing is added at all, not even her level" : `, so that's the rank bonus plus level ${pc.level}`}.</p>
|
|
1661
|
+
</div>`);
|
|
1662
|
+
} else {
|
|
1663
|
+
const counts = [0, 0, 0, 0];
|
|
1664
|
+
for (let i = 0; i < times; i++) counts[rollCheck(opt.mod, dc).deg]++;
|
|
1665
|
+
const bars = [3, 2, 1, 0].map((d) => `
|
|
1666
|
+
<div class="distrow">
|
|
1667
|
+
<span class="dlbl">${DEG_LABEL[d]}</span>
|
|
1668
|
+
<span class="dbar"><span class="dfill ${DEG_CLASS[d]}" style="width:${(counts[d] / times) * 100}%"></span></span>
|
|
1669
|
+
<span class="dnum">${counts[d]}</span>
|
|
1670
|
+
</div>`).join("");
|
|
1671
|
+
setHTML("checkOut", `<div class="resultcard">
|
|
1672
|
+
<p class="meta">Twenty rolls of ${sign(opt.mod)} against DC ${dc}:</p>
|
|
1673
|
+
<div class="dist">${bars}</div>
|
|
1674
|
+
<p class="rwhy">${counts[3] ? `${counts[3]} critical success${counts[3] === 1 ? "" : "es"} out of twenty — nobody rolled a 20 for most of those. Criticals come from beating the DC by ten.` : `No criticals that time. Drop the DC by five and roll again.`}</p>
|
|
1675
|
+
</div>`);
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
/* ---------------------------------------------------------------
|
|
1680
|
+
2. THE TURN — three actions, the multiple attack penalty, and
|
|
1681
|
+
what debuffs do to a target's numbers.
|
|
1682
|
+
--------------------------------------------------------------- */
|
|
1683
|
+
let turnState = { who: "mari", picks: [] };
|
|
1684
|
+
function turnSpent() { return turnState.picks.reduce((n, a) => n + a.cost, 0); }
|
|
1685
|
+
DEMOS.turn = function () {
|
|
1686
|
+
const host = byId("demo-turn"); if (!host) return;
|
|
1687
|
+
const pc = PC_BY_ID[turnState.who];
|
|
1688
|
+
const palette = TURN_ACTIONS[turnState.who] || [];
|
|
1689
|
+
const spent = turnSpent(), left = 3 - spent;
|
|
1690
|
+
const pips = [0, 1, 2].map((i) => {
|
|
1691
|
+
let label = "", used = false, k = 0;
|
|
1692
|
+
turnState.picks.forEach((a) => { if (i >= k && i < k + a.cost) { label = a.label; used = true; } k += a.cost; });
|
|
1693
|
+
return `<div class="pip${used ? " used" : ""}">${used ? `<span class="pipname">${escapeHtml(label)}</span>` : `<span class="pipname empty">◆ free</span>`}</div>`;
|
|
1694
|
+
}).join("");
|
|
1695
|
+
/* Live MAP preview, so the penalty is visible before it bites. */
|
|
1696
|
+
let attacks = 0;
|
|
1697
|
+
turnState.picks.forEach((a) => { if (a.attack) attacks++; });
|
|
1698
|
+
host.innerHTML = `
|
|
1699
|
+
<div class="controls">
|
|
1700
|
+
<label class="field inline"><span class="name">Whose turn</span>
|
|
1701
|
+
<select onchange="turnSet(this.value)">
|
|
1702
|
+
${PREGENS.map((p) => `<option value="${p.id}"${p.id === turnState.who ? " selected" : ""}>${escapeHtml(p.short)} — ${escapeHtml(p.cls)}</option>`).join("")}
|
|
1703
|
+
</select></label>
|
|
1704
|
+
<div class="foecard">
|
|
1705
|
+
<b>${escapeHtml(FOE.name)}</b>
|
|
1706
|
+
<span class="meta">AC ${FOE.ac} · HP ${FOE.hp} · Fort DC ${10 + FOE.saves.fortitude} · Ref DC ${10 + FOE.saves.reflex} · Will DC ${10 + FOE.saves.will} · Perception DC ${10 + FOE.perception}</span>
|
|
1707
|
+
</div>
|
|
1708
|
+
</div>
|
|
1709
|
+
<div class="pips">${pips}<div class="pip react"><span class="pipname empty">⤳ reaction</span></div></div>
|
|
1710
|
+
<p class="meta">${left ? `${left} action${left === 1 ? "" : "s"} left.` : "Turn full."} ${attacks ? `Next attack takes <b>${mapPenaltyLabel(attacks)}</b>.` : "No attacks yet — the first one is at full value."}</p>
|
|
1711
|
+
<div class="palette">
|
|
1712
|
+
${palette.map((a, i) => {
|
|
1713
|
+
const dis = a.cost > left;
|
|
1714
|
+
return `<button class="pbtn k-${a.kind}${dis ? " dis" : ""}" ${dis ? "disabled" : ""} onclick="turnPick(${i})">
|
|
1715
|
+
<span class="pcost">${"◆".repeat(a.cost)}</span>
|
|
1716
|
+
<span class="plabel">${escapeHtml(a.label)}</span>
|
|
1717
|
+
${a.attack && !dis ? `<span class="pmap">${attacks ? mapPenaltyLabel(attacks, a.agile) : "full"}</span>` : ""}
|
|
1718
|
+
</button>`;
|
|
1719
|
+
}).join("")}
|
|
1720
|
+
</div>
|
|
1721
|
+
<div class="row tight">
|
|
1722
|
+
<button class="btn sm" onclick="runTurn()" ${turnState.picks.length ? "" : "disabled"}>${iconSvg("d20")} Run the turn</button>
|
|
1723
|
+
<button class="btn sm secondary" onclick="turnClear()">${iconSvg("reset")} Clear</button>
|
|
1724
|
+
</div>
|
|
1725
|
+
<div id="turnOut" class="out"></div>
|
|
1726
|
+
${turnState.picks.length && turnState.picks[turnState.picks.length - 1].hint
|
|
1727
|
+
? `<p class="hintline">${iconSvg("star")} ${escapeHtml(turnState.picks[turnState.picks.length - 1].hint)}</p>` : ""}`;
|
|
1728
|
+
};
|
|
1729
|
+
function mapPenalty(nAttacks, agile) {
|
|
1730
|
+
if (nAttacks <= 0) return 0;
|
|
1731
|
+
if (nAttacks === 1) return agile ? -4 : -5;
|
|
1732
|
+
return agile ? -8 : -10;
|
|
1733
|
+
}
|
|
1734
|
+
function mapPenaltyLabel(nAttacks, agile) { const p = mapPenalty(nAttacks, agile); return p ? `${p}` : "full"; }
|
|
1735
|
+
function turnSet(who) { turnState.who = who; turnState.picks = []; setHTML("turnOut", ""); DEMOS.turn(); }
|
|
1736
|
+
function turnPick(i) {
|
|
1737
|
+
const a = (TURN_ACTIONS[turnState.who] || [])[i];
|
|
1738
|
+
if (!a || a.cost > 3 - turnSpent()) return;
|
|
1739
|
+
turnState.picks.push(a); setHTML("turnOut", ""); DEMOS.turn();
|
|
1740
|
+
}
|
|
1741
|
+
function turnClear() { turnState.picks = []; setHTML("turnOut", ""); DEMOS.turn(); }
|
|
1742
|
+
|
|
1743
|
+
function runTurn() {
|
|
1744
|
+
const pc = PC_BY_ID[turnState.who];
|
|
1745
|
+
/* The foe's live state — this is the whole point of the demo: watch
|
|
1746
|
+
the numbers you're rolling against move as the turn goes on. */
|
|
1747
|
+
const foe = { frightened: 0, prone: false, grabbed: false, offGuardTo: false, hp: FOE.hp };
|
|
1748
|
+
let attacks = 0, anthem = 0, dmgTotal = 0;
|
|
1749
|
+
const log = [];
|
|
1750
|
+
const foeAC = () => FOE.ac - foe.frightened - (foe.prone || foe.offGuardTo ? 2 : 0);
|
|
1751
|
+
const foeDC = (save) => 10 + FOE.saves[save] - foe.frightened;
|
|
1752
|
+
const acNote = () => {
|
|
1753
|
+
const bits = [];
|
|
1754
|
+
if (foe.frightened) bits.push(`−${foe.frightened} frightened (status)`);
|
|
1755
|
+
if (foe.prone) bits.push("−2 prone/off-guard (circumstance)");
|
|
1756
|
+
else if (foe.offGuardTo) bits.push("−2 off-guard (circumstance)");
|
|
1757
|
+
return bits.length ? ` (AC ${FOE.ac} ${bits.join(" ")} = ${foeAC()})` : "";
|
|
1758
|
+
};
|
|
1759
|
+
|
|
1760
|
+
turnState.picks.forEach((a) => {
|
|
1761
|
+
/* --- attack rolls: Strikes and spell attacks --- */
|
|
1762
|
+
if (a.attack && a.bonus != null) {
|
|
1763
|
+
const pen = mapPenalty(attacks, a.agile);
|
|
1764
|
+
const mod = a.bonus + pen + anthem;
|
|
1765
|
+
const dc = foeAC();
|
|
1766
|
+
const r = rollCheck(mod, dc);
|
|
1767
|
+
attacks++;
|
|
1768
|
+
const bits = [`${sign(a.bonus)} base`];
|
|
1769
|
+
if (pen) bits.push(`${pen} multiple attack penalty`);
|
|
1770
|
+
if (anthem) bits.push(`+${anthem} status (Courageous Anthem)`);
|
|
1771
|
+
if (r.deg >= 2) {
|
|
1772
|
+
const dmg = rollDamage(a.dmg, anthem);
|
|
1773
|
+
const dealt = r.deg === 3 ? dmg.total * 2 : dmg.total;
|
|
1774
|
+
dmgTotal += dealt;
|
|
1775
|
+
log.push({ deg: r.deg, head: a.label,
|
|
1776
|
+
math: `d20 ${natSpan(r.nat)} ${sign(mod)} = ${r.total} vs AC ${dc}${acNote()}`,
|
|
1777
|
+
detail: `${bits.join(", ")}. Damage ${dmg.detail}${r.deg === 3 ? ` — <b>doubled for the critical hit: ${dealt}</b>` : ""}. ${escapeHtml(a.dtype)}.`,
|
|
1778
|
+
why: r.why });
|
|
1779
|
+
} else {
|
|
1780
|
+
log.push({ deg: r.deg, head: a.label,
|
|
1781
|
+
math: `d20 ${natSpan(r.nat)} ${sign(mod)} = ${r.total} vs AC ${dc}${acNote()}`,
|
|
1782
|
+
detail: `${bits.join(", ")}. No damage.`, why: r.why });
|
|
1783
|
+
}
|
|
1784
|
+
return;
|
|
1785
|
+
}
|
|
1786
|
+
/* --- skill checks against one of the foe's DCs --- */
|
|
1787
|
+
if (a.check) {
|
|
1788
|
+
const pen = a.attack ? mapPenalty(attacks, false) : 0;
|
|
1789
|
+
const mod = a.check.bonus + pen;
|
|
1790
|
+
const dc = a.check.vs === "perception" ? 10 + FOE.perception - foe.frightened : foeDC(a.check.vs);
|
|
1791
|
+
const r = rollCheck(mod, dc);
|
|
1792
|
+
if (a.attack) attacks++;
|
|
1793
|
+
let outcome = "";
|
|
1794
|
+
if (a.effect === "demoralize") {
|
|
1795
|
+
if (r.deg === 3) { foe.frightened = Math.max(foe.frightened, 2); outcome = `${FOE.name} is <b>frightened 2</b> — every check and DC it has drops by 2, including its AC.`; }
|
|
1796
|
+
else if (r.deg === 2) { foe.frightened = Math.max(foe.frightened, 1); outcome = `${FOE.name} is <b>frightened 1</b> — that's −1 to its AC as well as its rolls.`; }
|
|
1797
|
+
else outcome = "No effect — and it's immune to your Demoralize for ten minutes either way.";
|
|
1798
|
+
} else if (a.effect === "trip") {
|
|
1799
|
+
if (r.deg === 3) { foe.prone = true; const d = rollDamage("1d6"); dmgTotal += d.total; outcome = `Knocked <b>prone</b> and takes ${d.detail} bludgeoning.`; }
|
|
1800
|
+
else if (r.deg === 2) { foe.prone = true; outcome = "Knocked <b>prone</b> — that's off-guard, so −2 to its AC until it stands, and standing costs it an action."; }
|
|
1801
|
+
else if (r.deg === 0) outcome = "You lose your footing and fall prone yourself.";
|
|
1802
|
+
else outcome = "It keeps its feet.";
|
|
1803
|
+
} else if (a.effect === "grapple") {
|
|
1804
|
+
if (r.deg === 3) { foe.grabbed = true; outcome = "<b>Restrained</b> until the end of your next turn."; }
|
|
1805
|
+
else if (r.deg === 2) { foe.grabbed = true; outcome = "<b>Grabbed</b> until the end of your next turn."; }
|
|
1806
|
+
else if (r.deg === 0) outcome = "It reverses the grip — you're the one who's grabbed.";
|
|
1807
|
+
else outcome = "It shrugs you off.";
|
|
1808
|
+
} else if (a.effect === "feint") {
|
|
1809
|
+
if (r.deg === 3) { foe.offGuardTo = true; outcome = "<b>Off-guard</b> to your melee attacks until the end of your next turn."; }
|
|
1810
|
+
else if (r.deg === 2) { foe.offGuardTo = true; outcome = "<b>Off-guard</b> against your next melee attack this turn."; }
|
|
1811
|
+
else if (r.deg === 0) outcome = "It reads you completely — you're off-guard to it instead.";
|
|
1812
|
+
else outcome = "It doesn't buy it.";
|
|
1813
|
+
}
|
|
1814
|
+
log.push({ deg: r.deg, head: a.label,
|
|
1815
|
+
math: `${a.check.skill} d20 ${natSpan(r.nat)} ${sign(mod)} = ${r.total} vs DC ${dc}${pen ? ` (includes ${pen} multiple attack penalty)` : ""}`,
|
|
1816
|
+
detail: outcome, why: r.why });
|
|
1817
|
+
return;
|
|
1818
|
+
}
|
|
1819
|
+
/* --- saves the target rolls against your spell DC --- */
|
|
1820
|
+
if (a.save) {
|
|
1821
|
+
const dc = a.save.dc;
|
|
1822
|
+
const mod = FOE.saves.will - foe.frightened;
|
|
1823
|
+
const nat = d20(), total = nat + mod;
|
|
1824
|
+
const deg = degreeOf(total, dc, nat);
|
|
1825
|
+
/* The target's degree is what happened to it, so a good save is bad news for you. */
|
|
1826
|
+
let outcome = "";
|
|
1827
|
+
if (a.effect === "fear") {
|
|
1828
|
+
if (deg === 3) outcome = "Unaffected — it critically succeeded on its save.";
|
|
1829
|
+
else if (deg === 2) { foe.frightened = Math.max(foe.frightened, 1); outcome = "It saved, but it's still <b>frightened 1</b>. Even a success usually leaves something behind."; }
|
|
1830
|
+
else if (deg === 1) { foe.frightened = Math.max(foe.frightened, 2); outcome = "<b>Frightened 2</b> — −2 to everything it rolls and every DC it has."; }
|
|
1831
|
+
else { foe.frightened = Math.max(foe.frightened, 3); outcome = "<b>Frightened 3 and fleeing</b> for a round. It runs."; }
|
|
1832
|
+
}
|
|
1833
|
+
log.push({ deg: 3 - deg, head: a.label,
|
|
1834
|
+
math: `${FOE.name} rolls Will: d20 ${natSpan(nat)} ${sign(mod)} = ${total} vs your spell DC ${dc}${foe.frightened ? " (its modifier already includes being frightened)" : ""}`,
|
|
1835
|
+
detail: outcome, why: degreeReason(total, dc, nat) + " — for the target", saveRoll: true, targetDeg: deg });
|
|
1836
|
+
return;
|
|
1837
|
+
}
|
|
1838
|
+
/* --- everything else: effects and flavour --- */
|
|
1839
|
+
if (a.effect === "anthem") {
|
|
1840
|
+
anthem = 1;
|
|
1841
|
+
log.push({ deg: 2, head: a.label, math: "No roll", detail: "You and every ally in earshot get <b>+1 status to attack rolls, damage rolls and saves against fear</b> for the round. One action, and it makes both of your attacks better.", why: "" });
|
|
1842
|
+
} else if (a.effect === "raise") {
|
|
1843
|
+
log.push({ deg: 2, head: a.label, math: "No roll", detail: `Your AC goes from ${pc.ac} to <b>${pc.ac + pc.shieldBonus}</b> until the start of your next turn — a +${pc.shieldBonus} circumstance bonus.`, why: "" });
|
|
1844
|
+
} else if (a.effect === "soothe") {
|
|
1845
|
+
const h = rollDamage("1d10+4");
|
|
1846
|
+
log.push({ deg: 2, head: a.label, math: "No roll to hit — healing just lands", detail: `Restores ${h.detail} Hit Points and gives +2 status to saves against mental effects for a minute.`, why: "" });
|
|
1847
|
+
} else if (a.effect === "seek") {
|
|
1848
|
+
log.push({ deg: 2, head: a.label, math: "The GM rolls this one in secret", detail: "Perception against the Stealth DC of anything hiding. You'll be told what you find, not what you rolled.", why: "" });
|
|
1849
|
+
} else {
|
|
1850
|
+
log.push({ deg: 2, head: a.label, math: "No roll", detail: a.hint || "Spent an action.", why: "" });
|
|
1851
|
+
}
|
|
1852
|
+
});
|
|
1853
|
+
|
|
1854
|
+
const state2 = [];
|
|
1855
|
+
if (foe.frightened) state2.push(`frightened ${foe.frightened}`);
|
|
1856
|
+
if (foe.prone) state2.push("prone");
|
|
1857
|
+
if (foe.grabbed) state2.push("grabbed");
|
|
1858
|
+
if (foe.offGuardTo) state2.push("off-guard to you");
|
|
1859
|
+
setHTML("turnOut", `
|
|
1860
|
+
<ol class="turnlog">
|
|
1861
|
+
${log.map((l) => `
|
|
1862
|
+
<li class="tstep ${DEG_CLASS[l.deg]}">
|
|
1863
|
+
<div class="tshead">${escapeHtml(l.head)} ${l.math !== "No roll" && !l.saveRoll ? degChip(l.deg) : l.saveRoll ? `<span class="deg ${DEG_CLASS[l.targetDeg]}">target: ${DEG_LABEL[l.targetDeg].toLowerCase()}</span>` : ""}</div>
|
|
1864
|
+
<div class="tsmath">${l.math}</div>
|
|
1865
|
+
${l.why ? `<div class="tswhy">${escapeHtml(titleCase(l.why))}.</div>` : ""}
|
|
1866
|
+
${l.detail ? `<div class="tsdetail">${l.detail}</div>` : ""}
|
|
1867
|
+
</li>`).join("")}
|
|
1868
|
+
</ol>
|
|
1869
|
+
<div class="turnsum">
|
|
1870
|
+
<b>${dmgTotal}</b> damage to a ${FOE.hp}-hit-point enemy${state2.length ? `, and it's now <b>${escapeHtml(state2.join(", "))}</b>` : ""}.
|
|
1871
|
+
${dmgTotal >= FOE.hp ? " That's it down." : ""}
|
|
1872
|
+
<span class="meta">Your multiple attack penalty resets at the start of your next turn.</span>
|
|
1873
|
+
</div>`);
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
/* ---------------------------------------------------------------
|
|
1877
|
+
3. GOING DOWN — dying, recovery checks, wounded, hero points.
|
|
1878
|
+
--------------------------------------------------------------- */
|
|
1879
|
+
let dyingState = null;
|
|
1880
|
+
function freshDying() { const pc = PC_BY_ID.mari; return { hp: pc.hp, max: pc.hp, dying: 0, wounded: 0, hero: 1, log: [] }; }
|
|
1881
|
+
DEMOS.dying = function () {
|
|
1882
|
+
const host = byId("demo-dying"); if (!host) return;
|
|
1883
|
+
if (!dyingState) dyingState = freshDying();
|
|
1884
|
+
const s = dyingState, pc = PC_BY_ID.mari;
|
|
1885
|
+
const dead = s.dying >= 4;
|
|
1886
|
+
const pct = (s.hp / s.max) * 100;
|
|
1887
|
+
host.innerHTML = `
|
|
1888
|
+
<div class="dywrap">
|
|
1889
|
+
<div class="dyhead">
|
|
1890
|
+
<b>${escapeHtml(pc.short)}</b>
|
|
1891
|
+
<span class="hpbar"><span class="hpfill ${pct > 50 ? "" : pct > 0 ? "mid" : "low"}" style="width:${pct}%"></span></span>
|
|
1892
|
+
<span class="hpnum">${s.hp} / ${s.max}</span>
|
|
1893
|
+
</div>
|
|
1894
|
+
<div class="dychips">
|
|
1895
|
+
<span class="statchip${s.dying ? " bad" : ""}">dying ${s.dying}</span>
|
|
1896
|
+
<span class="statchip${s.wounded ? " warn" : ""}">wounded ${s.wounded}</span>
|
|
1897
|
+
<span class="statchip${s.hero ? " good" : ""}">${s.hero} hero point${s.hero === 1 ? "" : "s"}</span>
|
|
1898
|
+
${dead ? `<span class="statchip bad">dead</span>` : s.dying ? `<span class="statchip bad">unconscious</span>` : ""}
|
|
1899
|
+
</div>
|
|
1900
|
+
<div class="palette">
|
|
1901
|
+
<button class="pbtn k-attack" onclick="dyingHit(12)" ${dead ? "disabled" : ""}><span class="plabel">Take a 12-damage hit</span></button>
|
|
1902
|
+
<button class="pbtn k-attack" onclick="dyingHit(25, true)" ${dead ? "disabled" : ""}><span class="plabel">Take a critical hit (25)</span></button>
|
|
1903
|
+
<button class="pbtn k-skill" onclick="dyingRecover()" ${!s.dying || dead ? "disabled" : ""}><span class="plabel">Roll a recovery check</span></button>
|
|
1904
|
+
<button class="pbtn k-spell" onclick="dyingHero()" ${!s.dying || !s.hero || dead ? "disabled" : ""}><span class="plabel">Spend all hero points</span></button>
|
|
1905
|
+
<button class="pbtn k-defend" onclick="dyingHeal()" ${dead ? "disabled" : ""}><span class="plabel">Someone casts Soothe</span></button>
|
|
1906
|
+
<button class="pbtn k-other" onclick="dyingReset()"><span class="plabel">Reset</span></button>
|
|
1907
|
+
</div>
|
|
1908
|
+
<ol class="turnlog">${s.log.map((l) => `<li class="tstep ${l.cls}"><div class="tsmath">${l.text}</div></li>`).join("")}</ol>
|
|
1909
|
+
</div>`;
|
|
1910
|
+
};
|
|
1911
|
+
function dyLog(text, cls) { dyingState.log.unshift({ text, cls: cls || "succ" }); DEMOS.dying(); }
|
|
1912
|
+
function dyingHit(amount, crit) {
|
|
1913
|
+
const s = dyingState;
|
|
1914
|
+
if (s.dying) {
|
|
1915
|
+
s.dying += crit ? 2 : 1;
|
|
1916
|
+
dyLog(`Hit while already down. Dying goes up by ${crit ? "2 (it was a critical hit)" : "1"} — <b>dying ${s.dying}</b>.${s.dying >= 4 ? " That's four. She's dead." : ""}`, s.dying >= 4 ? "cfail" : "fail");
|
|
1917
|
+
return;
|
|
1918
|
+
}
|
|
1919
|
+
s.hp = Math.max(0, s.hp - amount);
|
|
1920
|
+
if (s.hp > 0) { dyLog(`Takes ${amount}. Down to <b>${s.hp}</b> hit points.`, "fail"); return; }
|
|
1921
|
+
const start = (crit ? 2 : 1) + s.wounded;
|
|
1922
|
+
s.dying = start;
|
|
1923
|
+
dyLog(`Dropped to 0 by ${crit ? "a critical hit" : "the hit"}. She's <b>dying ${start}</b> and unconscious`
|
|
1924
|
+
+ (crit ? " — a critical hit starts you at dying 2" : "")
|
|
1925
|
+
+ (s.wounded ? `, plus ${s.wounded} for already being wounded ${s.wounded}` : "") + ".", "cfail");
|
|
1926
|
+
}
|
|
1927
|
+
function dyingRecover() {
|
|
1928
|
+
const s = dyingState;
|
|
1929
|
+
const dc = 10 + s.dying;
|
|
1930
|
+
const nat = d20();
|
|
1931
|
+
const deg = degreeOf(nat, dc, nat); /* a flat check: no modifiers at all */
|
|
1932
|
+
let msg = `Recovery check — a <b>flat check</b>, no modifiers: d20 ${natSpan(nat)} vs DC ${dc} (10 + dying ${s.dying}). ${DEG_LABEL[deg]}. `;
|
|
1933
|
+
if (deg === 3) s.dying -= 2;
|
|
1934
|
+
else if (deg === 2) s.dying -= 1;
|
|
1935
|
+
else if (deg === 1) s.dying += 1;
|
|
1936
|
+
else s.dying += 2;
|
|
1937
|
+
if (s.dying <= 0) {
|
|
1938
|
+
s.dying = 0; s.wounded += 1;
|
|
1939
|
+
msg += `Dying is gone — but losing it always leaves you <b>wounded ${s.wounded}</b>, which gets added to the dying value next time she drops. Still at 0 hit points and unconscious until someone heals her.`;
|
|
1940
|
+
dyLog(msg, "succ");
|
|
1941
|
+
} else if (s.dying >= 4) {
|
|
1942
|
+
s.dying = 4;
|
|
1943
|
+
msg += `That's <b>dying 4</b>. She's dead.`;
|
|
1944
|
+
dyLog(msg, "cfail");
|
|
1945
|
+
} else {
|
|
1946
|
+
msg += `Now <b>dying ${s.dying}</b>.`;
|
|
1947
|
+
dyLog(msg, deg >= 2 ? "succ" : "fail");
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
function dyingHero() {
|
|
1951
|
+
const s = dyingState;
|
|
1952
|
+
if (!s.hero) return;
|
|
1953
|
+
s.hero = 0; s.dying = 0; s.wounded += 1;
|
|
1954
|
+
dyLog(`Spent every hero point to cheat death: the dying condition goes away and she's stable at 0 hit points. Losing dying still leaves her <b>wounded ${s.wounded}</b>. This is why you start each session with one and why you should spend it.`, "csucc");
|
|
1955
|
+
}
|
|
1956
|
+
function dyingHeal() {
|
|
1957
|
+
const s = dyingState;
|
|
1958
|
+
const h = rollDamage("1d10+4");
|
|
1959
|
+
s.hp = Math.min(s.max, s.hp + h.total);
|
|
1960
|
+
const wasDying = s.dying;
|
|
1961
|
+
if (s.dying) s.dying = 0;
|
|
1962
|
+
dyLog(`Soothe restores ${h.detail} hit points — she's on <b>${s.hp}</b>.`
|
|
1963
|
+
+ (wasDying ? ` Any hit points at all end dying and unconsciousness: she's awake, and can act normally next turn. She keeps wounded ${s.wounded}.` : ""), "csucc");
|
|
1964
|
+
}
|
|
1965
|
+
function dyingReset() { dyingState = freshDying(); DEMOS.dying(); }
|
|
1966
|
+
|
|
1967
|
+
/* ---------------------------------------------------------------
|
|
1968
|
+
4. EXPLORATION — everyone declares an activity.
|
|
1969
|
+
--------------------------------------------------------------- */
|
|
1970
|
+
let exploreState = { mari: "search", sable: "avoid-notice", order: ["mari", "sable"] };
|
|
1971
|
+
DEMOS.explore = function () {
|
|
1972
|
+
const host = byId("demo-explore"); if (!host) return;
|
|
1973
|
+
const rows = exploreState.order.map((id, i) => {
|
|
1974
|
+
const pc = PC_BY_ID[id];
|
|
1975
|
+
const act = EXPLORATION_ACTIVITIES.find((a) => a.slug === exploreState[id]) || EXPLORATION_ACTIVITIES[0];
|
|
1976
|
+
const mod = explorationMod(pc, act);
|
|
1977
|
+
return `<div class="exprow">
|
|
1978
|
+
<div class="expord">
|
|
1979
|
+
<button class="ordbtn" onclick="expMove(${i},-1)" ${i === 0 ? "disabled" : ""}>${iconSvg("up")}</button>
|
|
1980
|
+
<span class="ordnum">${i + 1}</span>
|
|
1981
|
+
<button class="ordbtn" onclick="expMove(${i},1)" ${i === exploreState.order.length - 1 ? "disabled" : ""}>${iconSvg("down")}</button>
|
|
1982
|
+
</div>
|
|
1983
|
+
<div class="expwho"><b>${escapeHtml(pc.short)}</b><span class="meta">${escapeHtml(pc.cls)}</span></div>
|
|
1984
|
+
<div class="expsel">
|
|
1985
|
+
<select onchange="expSet('${id}', this.value)">
|
|
1986
|
+
${EXPLORATION_ACTIVITIES.map((a) => `<option value="${a.slug}"${a.slug === act.slug ? " selected" : ""}>${escapeHtml(a.label)}</option>`).join("")}
|
|
1987
|
+
</select>
|
|
1988
|
+
</div>
|
|
1989
|
+
<div class="expgov">${mod == null ? `<span class="meta">${escapeHtml(act.governing)}</span>` : `<b class="govmod">${sign(mod)}</b><span class="meta"> ${escapeHtml(act.governing)}</span>`}</div>
|
|
1990
|
+
<div class="expnote">${escapeHtml(act.blurb)}${act.secret ? ` <span class="secretchip">${iconSvg("secret")} the GM rolls this</span>` : ""}</div>
|
|
1991
|
+
</div>`;
|
|
1992
|
+
}).join("");
|
|
1993
|
+
const stealthy = exploreState.order.every((id) => exploreState[id] === "avoid-notice");
|
|
1994
|
+
const searching = exploreState.order.some((id) => exploreState[id] === "search");
|
|
1995
|
+
const scouting = exploreState.order.some((id) => exploreState[id] === "scout");
|
|
1996
|
+
const notes = [];
|
|
1997
|
+
notes.push(stealthy
|
|
1998
|
+
? "Everyone is Avoiding Notice, so everyone rolls <b>Stealth</b> for initiative when the fight starts — and starts it hidden."
|
|
1999
|
+
: "Whoever is Avoiding Notice rolls <b>Stealth</b> for initiative; everyone else rolls Perception.");
|
|
2000
|
+
notes.push(searching
|
|
2001
|
+
? "Someone is Searching, so the GM gets a secret Perception check against anything hidden in your path. That tripwire has a chance of being spotted."
|
|
2002
|
+
: "<b>Nobody is Searching.</b> The GM makes no check for hidden doors or traps in your path — you walk into them.");
|
|
2003
|
+
if (scouting) notes.push("Scouting gives the whole party <b>+1 to initiative</b>.");
|
|
2004
|
+
notes.push("Marching order matters: the front of the line meets whatever is in the corridor first.");
|
|
2005
|
+
host.innerHTML = `
|
|
2006
|
+
<div class="exphead">
|
|
2007
|
+
<div>Order</div><div>Who</div><div>Activity</div><div>Rolls</div><div>What it buys</div>
|
|
2008
|
+
</div>
|
|
2009
|
+
<div class="expboard">${rows}</div>
|
|
2010
|
+
<ul class="notelist">${notes.map((n) => `<li>${n}</li>`).join("")}</ul>`;
|
|
2011
|
+
};
|
|
2012
|
+
function explorationMod(pc, act) {
|
|
2013
|
+
const g = (act.governing || "").toLowerCase();
|
|
2014
|
+
if (g === "perception") return pc.perception;
|
|
2015
|
+
if (pc.skills && pc.skills[g] != null) return pc.skills[g];
|
|
2016
|
+
if (g === "constitution") return null;
|
|
2017
|
+
return null;
|
|
2018
|
+
}
|
|
2019
|
+
function expSet(id, slug) { exploreState[id] = slug; DEMOS.explore(); }
|
|
2020
|
+
function expMove(i, d) {
|
|
2021
|
+
const o = exploreState.order, j = i + d;
|
|
2022
|
+
if (j < 0 || j >= o.length) return;
|
|
2023
|
+
const t = o[i]; o[i] = o[j]; o[j] = t;
|
|
2024
|
+
DEMOS.explore();
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
/* ---------------------------------------------------------------
|
|
2028
|
+
5. ATTITUDE — the social ladder, and what each move costs.
|
|
2029
|
+
--------------------------------------------------------------- */
|
|
2030
|
+
let socialState = { who: "sable", attitude: 2, log: [], immune: false, lieBonus: 0 };
|
|
2031
|
+
DEMOS.attitude = function () {
|
|
2032
|
+
const host = byId("demo-attitude"); if (!host) return;
|
|
2033
|
+
const s = socialState, pc = PC_BY_ID[s.who];
|
|
2034
|
+
const ladder = ATTITUDES.map((a, i) => `
|
|
2035
|
+
<div class="rung${i === s.attitude ? " on" : ""}${i < s.attitude ? " below" : ""}">
|
|
2036
|
+
<span class="rname">${titleCase(a)}</span>
|
|
2037
|
+
${i === s.attitude ? `<span class="rhere">${escapeHtml(FLAVOR.npc)} is here</span>` : ""}
|
|
2038
|
+
</div>`).reverse().join("");
|
|
2039
|
+
host.innerHTML = `
|
|
2040
|
+
<div class="controls">
|
|
2041
|
+
<label class="field inline"><span class="name">Who's talking</span>
|
|
2042
|
+
<select onchange="socialSet(this.value)">
|
|
2043
|
+
${PREGENS.map((p) => `<option value="${p.id}"${p.id === s.who ? " selected" : ""}>${escapeHtml(p.short)} — ${escapeHtml(p.cls)}</option>`).join("")}
|
|
2044
|
+
</select></label>
|
|
2045
|
+
<div class="foecard">
|
|
2046
|
+
<b>${escapeHtml(FLAVOR.npc)}</b>
|
|
2047
|
+
<span class="meta">${escapeHtml(FLAVOR.npcRole)} in ${escapeHtml(FLAVOR.port)} · Will DC 15 · Perception DC 15</span>
|
|
2048
|
+
</div>
|
|
2049
|
+
</div>
|
|
2050
|
+
<div class="ladder">${ladder}</div>
|
|
2051
|
+
<div class="palette">
|
|
2052
|
+
${SOCIAL_MOVES.filter((m) => m.key !== "demoralize").map((m) => {
|
|
2053
|
+
const mod = socialMod(pc, m);
|
|
2054
|
+
const blocked = m.needs && s.attitude < ATTITUDES.indexOf(m.needs);
|
|
2055
|
+
const immune = m.key === "coerce" && s.immune;
|
|
2056
|
+
return `<button class="pbtn k-skill${blocked || immune ? " dis" : ""}" ${blocked || immune ? "disabled" : ""} onclick="socialDo('${m.key}')">
|
|
2057
|
+
<span class="plabel">${escapeHtml(m.label)}</span>
|
|
2058
|
+
<span class="pmap">${escapeHtml(m.skill)} ${sign(mod)}${mod === 0 ? " untrained" : ""}</span>
|
|
2059
|
+
${blocked ? `<span class="pblock">needs friendly</span>` : immune ? `<span class="pblock">immune for a week</span>` : ""}
|
|
2060
|
+
</button>`;
|
|
2061
|
+
}).join("")}
|
|
2062
|
+
<button class="pbtn k-other" onclick="socialReset()"><span class="plabel">Reset the scene</span></button>
|
|
2063
|
+
</div>
|
|
2064
|
+
<ol class="turnlog">${s.log.map((l) => `<li class="tstep ${l.cls}"><div class="tsmath">${l.text}</div></li>`).join("")}</ol>`;
|
|
2065
|
+
};
|
|
2066
|
+
function socialMod(pc, move) {
|
|
2067
|
+
const key = move.skill.toLowerCase();
|
|
2068
|
+
return (pc.skills && pc.skills[key] != null) ? pc.skills[key] : 0;
|
|
2069
|
+
}
|
|
2070
|
+
function socialSet(who) { socialState.who = who; DEMOS.attitude(); }
|
|
2071
|
+
function socialReset() { socialState = { who: socialState.who, attitude: 2, log: [], immune: false, lieBonus: 0 }; DEMOS.attitude(); }
|
|
2072
|
+
function socLog(text, cls) { socialState.log.unshift({ text, cls: cls || "succ" }); DEMOS.attitude(); }
|
|
2073
|
+
function socialDo(key) {
|
|
2074
|
+
const s = socialState, pc = PC_BY_ID[s.who];
|
|
2075
|
+
const move = SOCIAL_MOVES.find((m) => m.key === key);
|
|
2076
|
+
const mod = socialMod(pc, move);
|
|
2077
|
+
const dc = (key === "lie" ? 15 + s.lieBonus : 15);
|
|
2078
|
+
const r = rollCheck(mod, dc);
|
|
2079
|
+
const before = s.attitude;
|
|
2080
|
+
const untrained = mod === 0 ? ` <span class="meta">(${escapeHtml(pc.short)} is untrained in ${escapeHtml(move.skill)} — untrained adds nothing, not even her level)</span>` : "";
|
|
2081
|
+
let outcome = "";
|
|
2082
|
+
if (key === "impression") {
|
|
2083
|
+
if (r.deg === 3) s.attitude = clamp(s.attitude + 2, 0, 4);
|
|
2084
|
+
else if (r.deg === 2) s.attitude = clamp(s.attitude + 1, 0, 4);
|
|
2085
|
+
else if (r.deg === 0) s.attitude = clamp(s.attitude - 1, 0, 4);
|
|
2086
|
+
outcome = r.deg === 3 ? "Two steps better." : r.deg === 2 ? "One step better." : r.deg === 1 ? "No change — a plain failure just doesn't move them." : "One step worse. You've made it awkward.";
|
|
2087
|
+
} else if (key === "request") {
|
|
2088
|
+
if (r.deg === 3) outcome = "Agreed, no conditions.";
|
|
2089
|
+
else if (r.deg === 2) outcome = "Agreed — but they want something in return.";
|
|
2090
|
+
else if (r.deg === 1) outcome = "Refused. They might suggest something smaller.";
|
|
2091
|
+
else { s.attitude = clamp(s.attitude - 1, 0, 4); outcome = "Refused, and one step worse for the cheek of asking."; }
|
|
2092
|
+
} else if (key === "coerce") {
|
|
2093
|
+
if (r.deg >= 2) {
|
|
2094
|
+
s.attitude = Math.min(s.attitude, 1);
|
|
2095
|
+
outcome = r.deg === 3
|
|
2096
|
+
? "They talk — and then they're <b>unfriendly</b>, permanently. Even the critical success costs you the relationship."
|
|
2097
|
+
: "They talk — and then they're <b>unfriendly</b>, and may well report you.";
|
|
2098
|
+
} else if (r.deg === 1) { s.attitude = Math.min(s.attitude, 1); outcome = "Nothing, and now they're <b>unfriendly</b>."; }
|
|
2099
|
+
else { s.attitude = 0; s.immune = true; outcome = "<b>Hostile</b>, and immune to your threats for a week. This is the worst outcome in the social rules."; }
|
|
2100
|
+
} else if (key === "lie") {
|
|
2101
|
+
if (r.deg >= 2) outcome = "Believed — for now. If they find evidence later they get a Perception check to catch it.";
|
|
2102
|
+
else { s.lieBonus = 4; outcome = "Not believed, and they get <b>+4 against everything else you say</b> this conversation."; }
|
|
2103
|
+
}
|
|
2104
|
+
const moved = s.attitude !== before ? ` ${titleCase(ATTITUDES[before])} → <b>${titleCase(ATTITUDES[s.attitude])}</b>.` : "";
|
|
2105
|
+
socLog(`<b>${escapeHtml(move.label)}</b> — ${escapeHtml(move.skill)}: d20 ${natSpan(r.nat)} ${sign(mod)} = ${r.total} vs DC ${dc}. ${degChip(r.deg)} ${escapeHtml(titleCase(r.why))}. ${outcome}${moved}${untrained}`,
|
|
2106
|
+
DEG_CLASS[r.deg]);
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
/* ---------------------------------------------------------------
|
|
2110
|
+
6. DOWNTIME — a week ashore.
|
|
2111
|
+
--------------------------------------------------------------- */
|
|
2112
|
+
let downState = { days: 5, mari: "earn-income", sable: "learn-a-spell" };
|
|
2113
|
+
DEMOS.downtime = function () {
|
|
2114
|
+
const host = byId("demo-downtime"); if (!host) return;
|
|
2115
|
+
const rows = PREGENS.map((pc) => {
|
|
2116
|
+
const act = DOWNTIME_ACTIVITIES.find((a) => a.slug === downState[pc.id]) || DOWNTIME_ACTIVITIES[0];
|
|
2117
|
+
return `<div class="exprow down">
|
|
2118
|
+
<div class="expwho"><b>${escapeHtml(pc.short)}</b><span class="meta">${escapeHtml(pc.cls)}</span></div>
|
|
2119
|
+
<div class="expsel">
|
|
2120
|
+
<select onchange="downSet('${pc.id}', this.value)">
|
|
2121
|
+
${DOWNTIME_ACTIVITIES.map((a) => `<option value="${a.slug}"${a.slug === act.slug ? " selected" : ""}>${escapeHtml(a.label)}</option>`).join("")}
|
|
2122
|
+
</select>
|
|
2123
|
+
</div>
|
|
2124
|
+
<div class="expgov"><span class="meta">${escapeHtml(act.skill)}</span></div>
|
|
2125
|
+
<div class="expnote">${escapeHtml(act.blurb)}</div>
|
|
2126
|
+
</div>`;
|
|
2127
|
+
}).join("");
|
|
2128
|
+
const asks = [];
|
|
2129
|
+
PREGENS.forEach((pc) => {
|
|
2130
|
+
const slug = downState[pc.id];
|
|
2131
|
+
if (slug === "earn-income") asks.push(`<b>${escapeHtml(pc.short)}</b> needs a <b>task level</b> from the GM — that plus her proficiency rank sets the daily rate.`);
|
|
2132
|
+
if (slug === "craft") asks.push(`<b>${escapeHtml(pc.short)}</b> needs the formula and half the item's price up front, then days of work.`);
|
|
2133
|
+
if (slug === "learn-a-spell") asks.push(`<b>${escapeHtml(pc.short)}</b> needs access to the spell — a teacher, a scroll, or someone's spellbook — and the materials.`);
|
|
2134
|
+
if (slug === "retraining") asks.push(`<b>${escapeHtml(pc.short)}</b> needs the GM to agree what she's retraining into, and usually a teacher.`);
|
|
2135
|
+
if (slug === "long-term-rest") asks.push(`<b>${escapeHtml(pc.short)}</b> just needs somewhere safe and boring.`);
|
|
2136
|
+
if (slug === "research") asks.push(`<b>${escapeHtml(pc.short)}</b> needs a library worth the name, and a question worth asking.`);
|
|
2137
|
+
});
|
|
2138
|
+
host.innerHTML = `
|
|
2139
|
+
<div class="controls">
|
|
2140
|
+
<label class="field inline narrow"><span class="name">Days ashore</span>
|
|
2141
|
+
<input type="number" min="1" max="60" value="${downState.days}" onchange="downSet('days', this.value)"></label>
|
|
2142
|
+
<div class="foecard"><b>${escapeHtml(FLAVOR.port)}</b><span class="meta">${downState.days} day${downState.days === 1 ? "" : "s"} before the ${escapeHtml(FLAVOR.ship)} sails</span></div>
|
|
2143
|
+
</div>
|
|
2144
|
+
<div class="expboard">${rows}</div>
|
|
2145
|
+
<ul class="notelist">
|
|
2146
|
+
${asks.map((a) => `<li>${a}</li>`).join("")}
|
|
2147
|
+
<li>Everyone also heals: ${downState.days} full day${downState.days === 1 ? "" : "s"} of rest is a lot of hit points, and it clears <b>wounded</b> once you're back to full.</li>
|
|
2148
|
+
</ul>`;
|
|
2149
|
+
};
|
|
2150
|
+
function downSet(k, v) {
|
|
2151
|
+
if (k === "days") downState.days = clamp(Math.round(Number(v) || 1), 1, 60);
|
|
2152
|
+
else downState[k] = v;
|
|
2153
|
+
DEMOS.downtime();
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
/* ============================================================
|
|
2157
|
+
MENU
|
|
2158
|
+
============================================================ */
|
|
2159
|
+
function renderMenu() {
|
|
2160
|
+
const done = CHAPTERS.filter((c) => state.done[c.key]).length;
|
|
2161
|
+
setHTML("menuBody", `
|
|
2162
|
+
<h2>How you're using this</h2>
|
|
2163
|
+
<div class="seg">
|
|
2164
|
+
<button class="${!gmMode() ? "on" : ""}" onclick="setGmMode(false)">Player</button>
|
|
2165
|
+
<button class="${gmMode() ? "on" : ""}" onclick="setGmMode(true)">GM</button>
|
|
2166
|
+
</div>
|
|
2167
|
+
<p class="meta">GM mode adds an <b>At the table</b> block to the bottom of every tab: what to run, in what order, and what to say. Player mode hides them, so you can hand the same link to your players.</p>
|
|
2168
|
+
|
|
2169
|
+
<h2 style="margin-top:26px">Progress</h2>
|
|
2170
|
+
<p class="meta">${done} of ${CHAPTERS.length} tabs marked read. Kept in this browser only.</p>
|
|
2171
|
+
<button class="btn secondary" onclick="resetProgress()">${iconSvg("reset")} Clear progress</button>
|
|
2172
|
+
|
|
2173
|
+
<h2 style="margin-top:26px">Appearance</h2>
|
|
2174
|
+
<div class="seg">
|
|
2175
|
+
${["auto", "light", "dark"].map((m) => `<button class="${state.settings.themeMode === m ? "on" : ""}" onclick="setThemeMode('${m}')">${titleCase(m)}</button>`).join("")}
|
|
2176
|
+
</div>
|
|
2177
|
+
<div class="swatchrow"><label for="accentPick">Accent colour</label>
|
|
2178
|
+
<input type="color" id="accentPick" value="${(state.settings.custom && state.settings.custom.accent) || DEFAULT_ACCENT}" oninput="setCustomColor('accent', this.value)"></div>
|
|
2179
|
+
<button class="btn secondary" onclick="resetTheme()">Reset colours</button>
|
|
2180
|
+
|
|
2181
|
+
<h2 style="margin-top:26px">Take it with you</h2>
|
|
2182
|
+
<p class="meta">This page is one self-contained file. Save it and it works on a plane, in a basement, or on a boat.</p>
|
|
2183
|
+
<div class="row tight">
|
|
2184
|
+
<button class="btn secondary" onclick="downloadOffline()">${iconSvg("install")} Save offline copy</button>
|
|
2185
|
+
<button class="btn secondary" id="installBtn" style="display:none" onclick="installApp()">Install app</button>
|
|
2186
|
+
</div>
|
|
2187
|
+
|
|
2188
|
+
<h2 style="margin-top:26px">Where the rules text comes from</h2>
|
|
2189
|
+
<p class="meta">The quoted condition and action text in this app is Paizo content under the ORC and OGL licences, taken from the open-source Foundry VTT <b>pf2e</b> project — ${REF_META.conditions || 0} conditions and ${REF_META.actions || 0} actions, generated ${escapeHtml(REF_META.generated || "")}${REF_META.sourceCommit ? ` from commit ${escapeHtml(REF_META.sourceCommit)}` : ""}. The explanations around it are ours, and the two pregenerated characters are made up. Unofficial fan tool, not affiliated with Paizo.</p>
|
|
2190
|
+
<p class="meta">${(REF_META.sources || []).map((s) => `${escapeHtml(s.title)} (${escapeHtml(s.license)})`).join(" · ")}</p>
|
|
2191
|
+
|
|
2192
|
+
<button class="btn secondary" style="margin-top:22px" onclick="closeMenu()">← Back to the primer</button>`);
|
|
2193
|
+
}
|
|
2194
|
+
function resetProgress() {
|
|
2195
|
+
state.done = {}; saveState(); renderHeader(); renderMenu(); toast("Progress cleared");
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
/* ============================================================
|
|
2199
|
+
THEME
|
|
2200
|
+
============================================================ */
|
|
2201
|
+
const DEFAULT_ACCENT = "#3f7d8c";
|
|
2202
|
+
function hexToRgb(h) { h = (h || "").replace("#", "").trim(); if (h.length === 3) h = h.split("").map((c) => c + c).join(""); const n = parseInt(h || "000000", 16); return { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255 }; }
|
|
2203
|
+
function relLuminance(hex) { const { r, g, b } = hexToRgb(hex); const f = (v) => { v /= 255; return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4); }; return 0.2126 * f(r) + 0.7152 * f(g) + 0.0722 * f(b); }
|
|
2204
|
+
function inkFor(hex) { return relLuminance(hex) < 0.48 ? "#ffffff" : "#15171c"; }
|
|
2205
|
+
function resolveThemeMode() {
|
|
2206
|
+
const m = state.settings.themeMode;
|
|
2207
|
+
if (m === "light" || m === "dark") return m;
|
|
2208
|
+
return (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) ? "dark" : "light";
|
|
2209
|
+
}
|
|
2210
|
+
function applyTheme() {
|
|
2211
|
+
const root = document.documentElement;
|
|
2212
|
+
root.dataset.theme = resolveThemeMode();
|
|
2213
|
+
const c = state.settings.custom || {};
|
|
2214
|
+
const accent = c.accent || DEFAULT_ACCENT;
|
|
2215
|
+
root.style.setProperty("--accent", accent);
|
|
2216
|
+
root.style.setProperty("--accent-ink", inkFor(accent));
|
|
2217
|
+
const meta = document.querySelector('meta[name="theme-color"]');
|
|
2218
|
+
if (meta) meta.setAttribute("content", resolveThemeMode() === "dark" ? "#14161b" : "#f4f5f8");
|
|
2219
|
+
}
|
|
2220
|
+
function setThemeMode(m) { state.settings.themeMode = m; saveState(); applyTheme(); renderMenu(); }
|
|
2221
|
+
function setCustomColor(key, val) { state.settings.custom = state.settings.custom || {}; state.settings.custom[key] = val; saveState(); applyTheme(); }
|
|
2222
|
+
function resetTheme() { state.settings.custom = null; saveState(); applyTheme(); renderMenu(); }
|
|
2223
|
+
|
|
2224
|
+
/* ============================================================
|
|
2225
|
+
PWA / OFFLINE
|
|
2226
|
+
============================================================ */
|
|
2227
|
+
let deferredInstall = null;
|
|
2228
|
+
function isHeadless() { try { return /jsdom/i.test(navigator.userAgent) || !("onbeforeinstallprompt" in window || "serviceWorker" in navigator); } catch (e) { return true; } }
|
|
2229
|
+
function setupInstall() {
|
|
2230
|
+
if (isHeadless()) return;
|
|
2231
|
+
window.addEventListener("beforeinstallprompt", (e) => { e.preventDefault(); deferredInstall = e; showInstallButton(true); });
|
|
2232
|
+
window.addEventListener("appinstalled", () => { deferredInstall = null; showInstallButton(false); toast("App installed"); });
|
|
2233
|
+
if ("serviceWorker" in navigator && location.protocol.startsWith("http")) {
|
|
2234
|
+
navigator.serviceWorker.register("sw.js").catch(() => {});
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
function showInstallButton(show) { const b = byId("installBtn"); if (b) b.style.display = show ? "" : "none"; }
|
|
2238
|
+
function installApp() { if (!deferredInstall) return; deferredInstall.prompt(); deferredInstall.userChoice.finally(() => { deferredInstall = null; showInstallButton(false); }); }
|
|
2239
|
+
function downloadOffline() {
|
|
2240
|
+
const doIt = (html) => {
|
|
2241
|
+
const blob = new Blob([html], { type: "text/html" });
|
|
2242
|
+
const a = document.createElement("a"); a.href = URL.createObjectURL(blob);
|
|
2243
|
+
a.download = "pf2e-primer.html"; document.body.appendChild(a); a.click();
|
|
2244
|
+
setTimeout(() => { URL.revokeObjectURL(a.href); a.remove(); }, 1000);
|
|
2245
|
+
};
|
|
2246
|
+
fetch(location.href).then((r) => r.text()).then(doIt).catch(() => doIt("<!doctype html>" + document.documentElement.outerHTML));
|
|
2247
|
+
}
|
|
2248
|
+
|
|
2249
|
+
/* ============================================================
|
|
2250
|
+
BOOT
|
|
2251
|
+
============================================================ */
|
|
2252
|
+
function buildNav() {
|
|
2253
|
+
setHTML("tabs", CHAPTERS.map((c) =>
|
|
2254
|
+
`<button id="nav-${c.key}" onclick="go('${c.key}')">${iconSvg(c.icon)}<span>${escapeHtml(c.label)}</span></button>`).join(""));
|
|
2255
|
+
setHTML("views", CHAPTERS.map((c) => `<section id="view-${c.key}" class="hide"></section>`).join("")
|
|
2256
|
+
+ `<section id="view-menu" class="hide"><div id="menuBody"></div></section>`);
|
|
2257
|
+
}
|
|
2258
|
+
function boot() {
|
|
2259
|
+
applyTheme();
|
|
2260
|
+
document.body.classList.toggle("gmon", gmMode());
|
|
2261
|
+
buildNav();
|
|
2262
|
+
renderHeader();
|
|
2263
|
+
go(VIEWS[0]);
|
|
2264
|
+
setupInstall();
|
|
2265
|
+
}
|
|
2266
|
+
/* Injected at the end of <body>, so the DOM is already there — boot
|
|
2267
|
+
synchronously and the first paint is the rendered app. */
|
|
2268
|
+
if (typeof document !== "undefined") boot();
|
|
2269
|
+
|
|
2270
|
+
</script>
|
|
2271
|
+
<script>
|
|
2272
|
+
(function(){ try { document.getElementById("titleIcon").innerHTML = iconSvg("anchor"); } catch(e){} })();
|
|
2273
|
+
</script>
|
|
2274
|
+
</body>
|
|
2275
|
+
</html>
|