pf2e-subsystems 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 +110 -0
- package/bin/cli.mjs +86 -0
- package/data/reference.generated.js +6 -0
- package/data/subsystems.generated.js +7 -0
- package/dist/icon.svg +7 -0
- package/dist/index.html +1930 -0
- package/dist/manifest.webmanifest +13 -0
- package/dist/sw.js +60 -0
- package/notice.md +132 -0
- package/package.json +52 -0
- package/src/content.js +234 -0
- package/src/engine.js +1320 -0
- package/src/icon.svg +7 -0
- package/src/manifest.webmanifest +13 -0
- package/src/styles.css +294 -0
- package/src/sw.js +60 -0
- package/src/template.html +69 -0
- package/tools/build.py +62 -0
- package/tools/build_reference.py +252 -0
- package/tools/build_subsystems.py +315 -0
- package/tools/test.mjs +370 -0
package/dist/index.html
ADDED
|
@@ -0,0 +1,1930 @@
|
|
|
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 Subsystems">
|
|
10
|
+
<meta name="description" content="Run Pathfinder 2e's Victory Point subsystems at the table: chases, infiltrations, influence encounters and research, with the point bookkeeping enforced instead of remembered.">
|
|
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='%23a87c4a' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'><path d='M3 20h18'/><path d='M6 20v-4'/><path d='M11 20v-8'/><path d='M16 20v-12'/><path d='M3 7h18' stroke-dasharray='2.5 2.5'/></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("pf2eSubsystems.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 Subsystems</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 other three tools, so
|
|
30
|
+
the four look like a set.
|
|
31
|
+
============================================================ */
|
|
32
|
+
:root{
|
|
33
|
+
--accent:#a87c4a;
|
|
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,input,select,textarea,details.ref{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
|
+
|
|
98
|
+
/* ---- Tabs ---- */
|
|
99
|
+
nav.tabs{
|
|
100
|
+
position:sticky;top:0;z-index:20;background:var(--surface);border-bottom:1px solid var(--line);
|
|
101
|
+
display:flex;gap:2px;justify-content:center;padding:0 8px;overflow-x:auto;scrollbar-width:none;
|
|
102
|
+
}
|
|
103
|
+
nav.tabs::-webkit-scrollbar{display:none;}
|
|
104
|
+
nav.tabs button{
|
|
105
|
+
background:none;border:none;color:var(--muted);font-family:inherit;font-weight:700;font-size:.9rem;
|
|
106
|
+
padding:12px 14px;display:inline-flex;align-items:center;gap:7px;white-space:nowrap;
|
|
107
|
+
border-bottom:2px solid transparent;
|
|
108
|
+
}
|
|
109
|
+
nav.tabs button .icn{width:1.05rem;height:1.05rem;}
|
|
110
|
+
nav.tabs button:hover{color:var(--ink);}
|
|
111
|
+
nav.tabs button.on{color:var(--accent-text);border-bottom-color:var(--accent);}
|
|
112
|
+
|
|
113
|
+
/* ---- Layout ---- */
|
|
114
|
+
.wrap{max-width:1000px;margin:0 auto;padding:22px 20px 70px;}
|
|
115
|
+
.meta{font-size:.9rem;color:var(--muted);}
|
|
116
|
+
.meta b{color:var(--ink);font-weight:600;}
|
|
117
|
+
.empty{text-align:center;color:var(--muted);padding:34px 20px;}
|
|
118
|
+
|
|
119
|
+
/* ============================================================
|
|
120
|
+
COMPONENTS
|
|
121
|
+
|
|
122
|
+
The run board is built for a laptop propped behind a screen and for a
|
|
123
|
+
phone held in one hand: big targets, the two numbers that matter in the
|
|
124
|
+
largest type on the page, and nothing that needs a second tap to read.
|
|
125
|
+
============================================================ */
|
|
126
|
+
|
|
127
|
+
.dim{color:var(--muted);}
|
|
128
|
+
.glyph{color:var(--accent-text);font-weight:800;}
|
|
129
|
+
.card{
|
|
130
|
+
background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);
|
|
131
|
+
padding:16px 18px;margin:0 0 16px;box-shadow:var(--shadow);
|
|
132
|
+
}
|
|
133
|
+
.card h2{margin:0 0 .5em;}
|
|
134
|
+
.card.done{border-left:4px solid var(--heal);}
|
|
135
|
+
.row{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}
|
|
136
|
+
.row.spread{justify-content:space-between;}
|
|
137
|
+
.rowbtns{display:flex;gap:8px;flex:0 0 auto;}
|
|
138
|
+
.rowbtns.wrap{flex-wrap:wrap;margin-top:10px;}
|
|
139
|
+
.rowbtns.tight{gap:5px;margin-top:8px;}
|
|
140
|
+
|
|
141
|
+
.btn{
|
|
142
|
+
background:var(--accent);color:var(--accent-ink);border:1px solid var(--accent);
|
|
143
|
+
border-radius:11px;padding:9px 14px;font-weight:800;font-size:.88rem;
|
|
144
|
+
}
|
|
145
|
+
.btn:hover{filter:brightness(1.08);}
|
|
146
|
+
.btn.ghost{background:var(--surface-2);color:var(--ink);border-color:var(--line);}
|
|
147
|
+
.btn.ghost:hover{border-color:var(--accent-dim);}
|
|
148
|
+
.chipbtn{
|
|
149
|
+
background:var(--surface-2);border:1px solid var(--line);border-radius:9px;
|
|
150
|
+
padding:5px 9px;font-size:.78rem;font-weight:700;color:var(--muted);
|
|
151
|
+
}
|
|
152
|
+
.chipbtn:hover{color:var(--ink);border-color:var(--accent-dim);}
|
|
153
|
+
.chipbtn.danger:hover{color:var(--harm);border-color:var(--harm);}
|
|
154
|
+
.menubtn.off{opacity:.45;}
|
|
155
|
+
|
|
156
|
+
.pill{
|
|
157
|
+
display:inline-block;background:var(--surface-2);border:1px solid var(--line);
|
|
158
|
+
border-radius:999px;padding:2px 10px;font-size:.76rem;font-weight:800;color:var(--muted);
|
|
159
|
+
}
|
|
160
|
+
.pill.mode{margin:6px 0;}
|
|
161
|
+
.note{
|
|
162
|
+
border-left:3px solid var(--accent);background:color-mix(in srgb,var(--accent) 6%,transparent);
|
|
163
|
+
border-radius:0 9px 9px 0;padding:9px 13px;margin:10px 0 0;font-size:.87rem;color:var(--muted);
|
|
164
|
+
}
|
|
165
|
+
.note.warn{border-left-color:var(--warn);background:color-mix(in srgb,var(--warn) 8%,transparent);}
|
|
166
|
+
.note.heal{border-left-color:var(--heal);background:color-mix(in srgb,var(--heal) 9%,transparent);}
|
|
167
|
+
.hint{display:block;font-size:.8rem;color:var(--muted);margin-top:5px;}
|
|
168
|
+
|
|
169
|
+
/* ---- Meters: the two numbers that matter ---- */
|
|
170
|
+
.meters{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;margin-bottom:16px;}
|
|
171
|
+
.meter{
|
|
172
|
+
background:var(--surface);border:1px solid var(--line);border-top:3px solid var(--accent);
|
|
173
|
+
border-radius:var(--radius);padding:13px 16px 12px;box-shadow:var(--shadow);
|
|
174
|
+
}
|
|
175
|
+
.meter.hazard{border-top-color:var(--harm);}
|
|
176
|
+
.meter.goal{border-top-color:var(--heal);}
|
|
177
|
+
.meter.edge{border-top-color:var(--info);}
|
|
178
|
+
.meterhead{display:flex;align-items:baseline;justify-content:space-between;gap:10px;}
|
|
179
|
+
.meterhead span:first-child{font-weight:800;font-size:.84rem;color:var(--muted);}
|
|
180
|
+
.mval{font-size:1.7rem;font-weight:800;letter-spacing:-.02em;line-height:1.1;}
|
|
181
|
+
.bar{position:relative;height:8px;border-radius:5px;background:var(--surface-2);margin:9px 0 2px;overflow:hidden;}
|
|
182
|
+
.fill{display:block;height:100%;background:var(--accent);transition:width .2s ease;}
|
|
183
|
+
.meter.hazard .fill{background:var(--harm);}
|
|
184
|
+
.meter.goal .fill{background:var(--heal);}
|
|
185
|
+
.tick{position:absolute;top:0;bottom:0;width:2px;background:var(--surface);opacity:.85;}
|
|
186
|
+
|
|
187
|
+
/* ---- Obstacles and checks ---- */
|
|
188
|
+
.chks{display:flex;flex-wrap:wrap;gap:7px;margin:8px 0;}
|
|
189
|
+
.chk{
|
|
190
|
+
display:inline-block;background:var(--surface-2);border:1px solid var(--line);
|
|
191
|
+
border-radius:9px;padding:4px 10px;font-size:.83rem;font-weight:700;
|
|
192
|
+
}
|
|
193
|
+
.chk.resistance{border-color:var(--harm);color:var(--harm);}
|
|
194
|
+
.chk.weakness{border-color:var(--heal);color:var(--heal);}
|
|
195
|
+
.perpc{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px;}
|
|
196
|
+
.pcchip{
|
|
197
|
+
background:var(--surface-2);border:1px solid var(--line);border-radius:999px;
|
|
198
|
+
padding:3px 11px;font-size:.8rem;font-weight:700;color:var(--muted);
|
|
199
|
+
}
|
|
200
|
+
.pcchip.on{background:color-mix(in srgb,var(--heal) 16%,var(--surface));border-color:var(--heal);color:var(--ink);}
|
|
201
|
+
.srcrow{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:8px 0;border-bottom:1px solid var(--line);}
|
|
202
|
+
.srcrow:last-child{border-bottom:none;}
|
|
203
|
+
.srcrow.dry{opacity:.5;}
|
|
204
|
+
.srcname{font-weight:700;flex:1;min-width:120px;}
|
|
205
|
+
|
|
206
|
+
/* ---- The per-PC button rows ---- */
|
|
207
|
+
.pcrow{
|
|
208
|
+
display:flex;align-items:center;gap:10px;flex-wrap:wrap;
|
|
209
|
+
padding:10px 0;border-bottom:1px solid var(--line);
|
|
210
|
+
}
|
|
211
|
+
.pcrow:last-of-type{border-bottom:none;}
|
|
212
|
+
.pcname{font-weight:800;min-width:96px;flex:0 0 auto;}
|
|
213
|
+
.degs{display:flex;gap:6px;flex-wrap:wrap;align-items:center;}
|
|
214
|
+
.deglabel{font-size:.74rem;font-weight:800;text-transform:uppercase;letter-spacing:.6px;color:var(--muted);min-width:62px;}
|
|
215
|
+
.deg{
|
|
216
|
+
border:1px solid var(--line);background:var(--surface-2);color:var(--ink);
|
|
217
|
+
border-radius:10px;padding:8px 12px;font-weight:800;font-size:.83rem;min-height:38px;
|
|
218
|
+
}
|
|
219
|
+
.deg:hover{border-color:currentColor;}
|
|
220
|
+
.deg.heal{color:var(--heal);}
|
|
221
|
+
.deg.ok{color:var(--ink);}
|
|
222
|
+
.deg.warn{color:var(--warn);}
|
|
223
|
+
.deg.harm{color:var(--harm);}
|
|
224
|
+
.deg.ghost{color:var(--muted);}
|
|
225
|
+
.deg.edge{color:var(--info);border-color:var(--info);}
|
|
226
|
+
|
|
227
|
+
/* ---- Thresholds and the log ---- */
|
|
228
|
+
.throw{display:flex;gap:11px;align-items:flex-start;padding:8px 0;border-bottom:1px solid var(--line);opacity:.55;}
|
|
229
|
+
.throw:last-child{border-bottom:none;}
|
|
230
|
+
.throw.hit{opacity:1;}
|
|
231
|
+
.throw.hit .pill{background:var(--accent);color:var(--accent-ink);border-color:var(--accent);}
|
|
232
|
+
.log .logrow{display:flex;gap:9px;align-items:baseline;padding:5px 0;font-size:.87rem;color:var(--muted);border-bottom:1px solid var(--line);}
|
|
233
|
+
.log .logrow:last-child{border-bottom:none;}
|
|
234
|
+
.log .logrow b{color:var(--ink);}
|
|
235
|
+
|
|
236
|
+
/* ---- Prep ---- */
|
|
237
|
+
.picker{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:11px;margin-bottom:12px;}
|
|
238
|
+
.pick{
|
|
239
|
+
display:flex;flex-direction:column;gap:3px;text-align:left;
|
|
240
|
+
background:var(--surface-2);border:1px solid var(--line);border-top:3px solid var(--accent);
|
|
241
|
+
border-radius:12px;padding:13px 15px;color:var(--ink);
|
|
242
|
+
}
|
|
243
|
+
.pick:hover{border-color:var(--accent-dim);}
|
|
244
|
+
.pick .dim{font-size:.82rem;}
|
|
245
|
+
.encrow{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:9px 0;border-bottom:1px solid var(--line);}
|
|
246
|
+
.encrow:last-child{border-bottom:none;}
|
|
247
|
+
.encrow.on{background:color-mix(in srgb,var(--accent) 7%,transparent);border-radius:9px;padding-left:9px;padding-right:9px;}
|
|
248
|
+
.encopen{
|
|
249
|
+
flex:1;min-width:180px;display:flex;align-items:baseline;gap:9px;text-align:left;
|
|
250
|
+
background:none;border:none;color:var(--ink);font-family:inherit;font-size:.95rem;padding:0;
|
|
251
|
+
}
|
|
252
|
+
.encopen .dim{font-size:.82rem;}
|
|
253
|
+
|
|
254
|
+
.fld{margin:14px 0;}
|
|
255
|
+
.flabel{display:block;font-size:.76rem;font-weight:800;text-transform:uppercase;letter-spacing:.7px;color:var(--muted);margin-bottom:5px;}
|
|
256
|
+
.inp{
|
|
257
|
+
background:var(--surface-2);border:1px solid var(--line);border-radius:10px;
|
|
258
|
+
padding:9px 12px;color:var(--ink);font-family:inherit;font-size:.92rem;width:100%;max-width:100%;
|
|
259
|
+
}
|
|
260
|
+
.inp:focus{outline:none;border-color:var(--accent);}
|
|
261
|
+
.inp.num{width:84px;flex:0 0 auto;}
|
|
262
|
+
.inp[type="color"]{width:58px;height:40px;padding:3px;}
|
|
263
|
+
.obrow{display:flex;gap:7px;align-items:center;margin-bottom:7px;flex-wrap:wrap;}
|
|
264
|
+
.obrow .inp{flex:1;min-width:120px;}
|
|
265
|
+
.radio{display:block;padding:6px 0;font-size:.9rem;cursor:pointer;}
|
|
266
|
+
.radio input{margin-right:7px;}
|
|
267
|
+
.factrow{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin:9px 0;}
|
|
268
|
+
.hidden-fact{
|
|
269
|
+
background:none;border:1px dashed var(--line);border-radius:9px;
|
|
270
|
+
padding:4px 11px;font-size:.8rem;font-weight:700;color:var(--muted);font-family:inherit;
|
|
271
|
+
}
|
|
272
|
+
.hidden-fact:hover{border-color:var(--accent);color:var(--accent-text);}
|
|
273
|
+
|
|
274
|
+
/* ---- Reference ---- */
|
|
275
|
+
details.ref{
|
|
276
|
+
border:1px solid var(--line);border-radius:11px;background:var(--surface-2);
|
|
277
|
+
margin-bottom:8px;overflow:hidden;
|
|
278
|
+
}
|
|
279
|
+
details.ref summary{
|
|
280
|
+
cursor:pointer;padding:11px 14px;font-weight:800;font-size:.93rem;
|
|
281
|
+
display:flex;align-items:baseline;justify-content:space-between;gap:12px;list-style:none;
|
|
282
|
+
}
|
|
283
|
+
details.ref summary::-webkit-details-marker{display:none;}
|
|
284
|
+
details.ref[open] summary{border-bottom:1px solid var(--line);}
|
|
285
|
+
.cite{font-size:.74rem;font-weight:700;color:var(--muted);flex:0 0 auto;}
|
|
286
|
+
.refbody{padding:12px 15px;font-size:.92rem;}
|
|
287
|
+
.refbody p{margin:.5em 0;max-width:70ch;}
|
|
288
|
+
.refbody h2,.refbody h3,.refbody h4{margin:1em 0 .3em;font-size:.96rem;}
|
|
289
|
+
.refbody table{width:100%;border-collapse:collapse;margin:10px 0;font-size:.86rem;display:block;overflow-x:auto;}
|
|
290
|
+
.refbody th,.refbody td{border:1px solid var(--line);padding:6px 9px;text-align:left;vertical-align:top;}
|
|
291
|
+
.refbody th{background:var(--surface);font-weight:800;}
|
|
292
|
+
.actbody{
|
|
293
|
+
border-left:3px solid var(--accent-dim);padding:4px 0 4px 12px;margin:6px 0 10px;
|
|
294
|
+
color:var(--muted);font-size:.89rem;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/* ---- Toast ---- */
|
|
298
|
+
.toast{
|
|
299
|
+
position:fixed;left:50%;bottom:26px;transform:translate(-50%,14px);
|
|
300
|
+
background:var(--ink);color:var(--bg);border-radius:11px;padding:10px 17px;
|
|
301
|
+
font-weight:700;font-size:.88rem;opacity:0;pointer-events:none;
|
|
302
|
+
transition:opacity .18s ease,transform .18s ease;z-index:60;max-width:90vw;text-align:center;
|
|
303
|
+
}
|
|
304
|
+
.toast.show{opacity:1;transform:translate(-50%,0);}
|
|
305
|
+
|
|
306
|
+
/* ============================================================
|
|
307
|
+
RESPONSIVE
|
|
308
|
+
============================================================ */
|
|
309
|
+
@media (max-width:620px){
|
|
310
|
+
.wrap{padding:16px 13px 64px;}
|
|
311
|
+
.card{padding:14px 14px;}
|
|
312
|
+
.pcrow{flex-direction:column;align-items:stretch;gap:7px;}
|
|
313
|
+
.pcname{min-width:0;}
|
|
314
|
+
.degs{justify-content:space-between;}
|
|
315
|
+
.deg{flex:1;min-width:0;padding:10px 6px;}
|
|
316
|
+
.deglabel{flex:0 0 100%;min-width:0;}
|
|
317
|
+
.mval{font-size:1.5rem;}
|
|
318
|
+
.picker{grid-template-columns:1fr;}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
</style>
|
|
322
|
+
</head>
|
|
323
|
+
<body>
|
|
324
|
+
|
|
325
|
+
<header class="top">
|
|
326
|
+
<div class="tophead">
|
|
327
|
+
<h1><span class="apptitle-ic" id="titleIcon"></span> PF2e Subsystems</h1>
|
|
328
|
+
<div class="headbtns">
|
|
329
|
+
<button class="menubtn" id="undoBtn" onclick="undoLast()" title="Undo the last change" aria-label="Undo">
|
|
330
|
+
<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="M9 14 4 9l5-5"/><path d="M4 9h10a6 6 0 0 1 0 12h-3"/></svg>
|
|
331
|
+
Undo
|
|
332
|
+
</button>
|
|
333
|
+
<button class="menubtn" onclick="openMenu()" title="Settings" aria-label="Settings">
|
|
334
|
+
<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>
|
|
335
|
+
</button>
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
<div class="sub" id="headSub"></div>
|
|
339
|
+
</header>
|
|
340
|
+
|
|
341
|
+
<nav class="tabs" id="tabs"></nav>
|
|
342
|
+
|
|
343
|
+
<main class="wrap" id="views"></main>
|
|
344
|
+
|
|
345
|
+
<div class="toast" id="toast"></div>
|
|
346
|
+
|
|
347
|
+
<script>
|
|
348
|
+
/* data/subsystems.generated.js — AUTO-GENERATED. Do not hand-edit.
|
|
349
|
+
Rebuild with: npm run build:sub
|
|
350
|
+
Source: foundryvtt/pf2e GM Screen journal (Paizo content, ORC). */
|
|
351
|
+
const GENERATED_SUB_META = {"generated": "2026-09-13", "source": "foundryvtt/pf2e", "sourceBranch": "v14-dev", "sourceCommit": "cf1adff", "pages": 9, "obstacles": 42, "sources": [{"title": "Pathfinder GM Core", "license": "ORC", "count": 9}]};
|
|
352
|
+
const GENERATED_SUBSYSTEMS = [{"slug": "victory-points", "name": "Victory Points", "section": "Subsystem and Variant Rules", "book": "Pathfinder GM Core", "pages": "184-186", "html": "<h3>Accumulating Rolls</h3><p><strong>Critical Success</strong> The PCs gain 2 Victory Points.</p><p><strong>Success</strong> The PCs gain 1 Victory Point.</p><p><strong>Critical Failure</strong> The PCs lose 1 Victory Point.</p><p>This means that the result of a PC's check usually results in the party gaining either 1 or no Victory Points. However, specialized PCs have a solid chance of earning the party 2 Victory Points, and hare‑brained schemes have a fair chance of losing the PCs 1 Victory Point.</p><h3>Diminishing Rolls</h3><p><strong>Critical Success</strong> If regaining ground is possible, the PCs gain 1 Victory Point. Otherwise, as success.</p><p><strong>Success</strong> The PCs avoid losing any Victory Points.</p><p><strong>Failure</strong> The PCs lose 1 Victory Point.</p><p><strong>Critical Failure</strong> The PCs lose 2 Victory Points.</p><table class=\"pf2e remaster\"><caption><strong>Table: Victory Point Scales</strong></caption><tbody><tr><th><strong>Duration of Challenge</strong></th><th><strong>VP End Point</strong></th><th><strong>VP Thresholds</strong></th></tr><tr><td>Quick encounter</td><td>3-5</td><td>-</td></tr><tr><td>Long encounter</td><td>7-10</td><td>4</td></tr><tr><td>Most of a session</td><td>15-25</td><td>5, 10, 15</td></tr><tr><td>Adventure-wide, sideline</td><td>15-20</td><td>5, 10, 15</td></tr><tr><td>Adventure-wise, forefront</td><td>25-50</td><td>10, 20, 30, 40</td></tr></tbody></table>"}, {"slug": "influence", "name": "Influence", "section": "Subsystem and Variant Rules", "book": "Pathfinder GM Core", "pages": "187-189", "html": "<p>During each round of an influence encounter, each PC can act once to either Influence or Discover.</p><h3><span class=\"actref\" data-action=\"influence\">Influence</span></h3><h3><span class=\"actref\" data-action=\"discover\">Discover</span></h3>"}, {"slug": "research", "name": "Research", "section": "Subsystem and Variant Rules", "book": "Pathfinder GM Core", "pages": "190-191", "html": "<p>In the research subsystem, PCs accumulate Research Points and learn new information or gain other benefits upon reaching specific thresholds. This subsystem is great for granting PCs more in-depth pieces of information as they continue to explore an area at large. Here, time passes in rounds spanning anywhere from 10 or so minutes to a full day. Each round, the characters use the Research exploration activity to gain Research Points (RP). As time passes and the party earns more RP, they gain knowledge and rewards, but also might face consequences or events. Some of these events might interrupt the round with a different kind of encounter (disrupting the Research activity), such as a social encounter with an intelligent book or a combat encounter with a guardian.</p><p>Research challenges work best when the PCs face a time constraint, rival research group, or other form of external condition that presents additional pressures—if the PCs have all the time in the world to safely peruse a library or ruin, you can usually simplify things to a simple skill check, since the PCs are free to keep rolling until they uncover everything there is to find.</p><h2>Building a Research Challenge</h2><p>A research challenge has two components: the library, which is an area containing the various research checks PCs need to attempt to learn about the topic (as well as obstacles that the PCs face while doing so); and a research stat block, which details the topic being researched and contains the information, rewards, and additional complications that happen when the PCs reach certain thresholds of RP.</p><h3>Designing the Library</h3><p>Designing the Library \"Library\" is the general term to designate the setting of the PCs' research. Despite the name, it doesn't necessarily consist of a quiet hall full of books. It could be an Astral memory palace, a gallery of iconographic artwork, or even a mansion where the party is questioning the guests. In many ways, designing a library is similar to designing any other dungeon, castle, or adventure locale, with various rooms or other areas, each with its own complications for the PCs to overcome. If your library is closer to a dungeon crawl on a tactical map, these are likely hazards or encounters with hostile creatures; if your library is meant to be meandered through more narratively, these might instead be skill-based or social encounters with NPCs.</p><p>Throughout the library, you'll place research checks. These describe the task that the party is doing to Research—perusing books, alchemically testing samples, or talking to a stubborn librarian—and a number of skills and DCs the party can use with the Research activity, in order from the lowest DC (the skill that works best) to the highest DC.</p><h3><span class=\"actref\" data-action=\"research\">Research</span></h3><ul class=\"tags paizo-style\"><li class=\"tag traits\"><p>CONCENTRATION</p></li><li class=\"tag traits\"><p>EXPLORATION</p></li></ul><p>If a skill isn't listed but a player provides a strong explanation for using it, you can add it at an appropriate DC (usually the highest listed DC). Academia Lore and Library Lore will often be listed in these entries, but also try to engage the whole party by varying the skills listed in each check; perhaps the books on aeromancy are all flying high overhead, requiring a round of Athletics checks to represent climbing up ladders to retrieve them. A variety of skills and decisions, along with vivid descriptions, are the key to an engaging and memorable research challenge, rather than just a string of die rolls by the investigator or wizard.</p><p>Each research check also has a maximum RP value. After reaching this value, the PCs can't gain any more RP from that research check (they don't gain more information by reading the same book over and over!) so they'll need to continue exploring to find more locations for further research. You can use this to guide your PCs through your library by placing research checks in various sections you know you want them to explore—an easy check near the entrance to introduce them to the topic is usually helpful as well! Be sure that your library provides enough RP for players to meet all the research thresholds in the stat block.</p><h3>Designing the Stat Block</h3><p>Once you've decided what your library looks like and how the areas and research options are structured, it's time to build the stat block and set research thresholds for each topic. Thresholds are your opportunity to reward your PCs with intriguing new information (like the history of the town they're in), tactical intel (such as a legendary monster's only weakness), uncommon or rare options (like a lost spell in the library's depths), or anything else. A mix of rewards is best! Backstory can be interesting, but isn't much of a reward on its own. Research that uncovers only backstory should appear only at the lowest thresholds.</p><p>Reaching thresholds can also change the state of the library, in the PCs' favor or otherwise. In a dusty room, the first threshold might simply be cleaning and organizing the library to lower subsequent checks, but in a haunted library, ghosts might start appearing as their resting place is increasingly disturbed. Reaching a research threshold can do just about anything, but it should always do something.</p><p>Think about the constraints preventing the PCs from researching freely, and use those constraints to determine how many Research Points they must earn to reach each threshold. You don't need to evenly space thresholds—you could require very few Research Points for crucial clues you want to ensure the PCs receive and a much larger number to reach the final threshold that grants a special reward.</p><p>In a published adventure, you'll find stat blocks presented in the format below. In some cases, this information appears in a single stat block instead, but the rules function the same.</p><h2>RESEARCH TOPIC'S NAME RESEARCH (LEVEL)</h2><ul class=\"tags paizo-style\"><li class=\"tag traits\"><p>traits</p></li></ul><p><strong>Research Checks</strong> The checks PCs can attempt to conduct research listed alongside their locations, tagged with area codes or page references when appropriate.</p><hr><p><strong>Research Thresholds</strong> Each threshold lists the number of RP required to reach it, followed by the effects for meeting that threshold. Thresholds are listed in order from first (requiring the fewest RP) to last (the highest threshold).</p><p>Beneath the stat block, you can list any events that occur based on timing rather than the PCs' RP total (such as a threatening message arriving on the third day), as well as any other important details.</p>"}, {"slug": "chases", "name": "Chases", "section": "Subsystem and Variant Rules", "book": "Pathfinder GM Core", "pages": "192-195", "html": "<p>Chases are a special type of encounter. Each round, the pursued character or characters act first, then the pursuing characters act. Typically, to reduce variance, the PCs roll checks to progress while their opponents proceed at a steady pace, but if you want to emphasize the back-and-forth nature of a particular chase, you could have both sides roll instead. Characters in the same group can act in whatever order they prefer, each taking a turn. A character must act on their turn. If they pass their turn or are unable to act, they're unable to help the group and automatically cause the group to lose 1 Chase Point.</p><h2>Obstacles</h2><p>Each obstacle requires a certain number of Chase Points to overcome. Typically, half the obstacles require 1 point fewer than the number of party members, and half require 2 points fewer (with a minimum of 1 Chase Point per obstacle). Particularly challenging obstacles might require more. Typically, there are multiple ways to overcome an obstacle; for example, characters could evade a guard or bribe them to look away. Each approach usually requires a skill check or Perception check, but sometimes a saving throw, an attack roll, or something even more unusual, like casting a certain spell.</p><p>On a character's turn, they describe what they do to help the group get past the obstacle. They then attempt any required roll, or perform the required action for a choice without a check. If they attempt a roll, the result determines how many Chase Points the character gains.</p><p><strong>Critical Success</strong> The PCs gain 2 Chase Points.</p><p><strong>Success</strong> The PCs gain 1 Chase Point.</p><p><strong>Critical Failure</strong> The PCs lose 1 Chase Point.</p><p>If the means of bypassing the obstacle helps automatically without requiring a check—such as using a certain spell to assist—the PCs typically get 1 Chase Point. You can increase that to 2 if you feel the action is extremely helpful.</p><p>Chase Points represent the ability of the whole group to bypass the obstacle. A character who critically succeeds is able to help the other characters continue onward, while one who critically fails needs extra assistance. Players often have ideas for ways to overcome the obstacle beyond the choices you created for the obstacle. If their idea is applicable, you'll need to determine the DC and skill, or other statistic being used for that approach. This is great as long as it's creative, but be wary of a situation where a character who is legendary at a skill tries to justify how they can bypass every obstacle with that skill, such as using Acrobatics to tumble around them all, or the like. You can determine that some tactics just won't work against certain obstacles, or would help only one character without benefiting the rest and therefore aren't all that useful.</p><p>Once the PCs accumulate enough Chase Points to overcome the obstacle, they move on to the next. Extra Chase Points don't carry over to the next obstacle. However, anyone who hasn't already taken their turn that round can still take it against the new obstacle. Consequently, the characters best suited to overcoming the current obstacle might act first, since the remaining characters might be better suited against the next one. The number of Chase Points the PCs have can never fall below 0.</p><h2>Building a Chase</h2><p>When building a chase, first build your obstacles, and then decide how far ahead the pursued character or characters begin and at what pace the NPCs will move. Having the NPCs clear one obstacle per round is a good rule of thumb, but it could vary depending on the situation, and should especially be slower against obstacles that require more than the typical number of Chase Points to overcome.</p><p>Select or build obstacles highlighting a variety of different skills and other options so everyone in the party has a moment to shine. When choosing what skills can bypass a given obstacle, ensure a variety of approaches can work. If you've already decided that an obstacle uses Stealth, selecting Thievery as the other option doesn't really offer opportunities for different types of characters, since those who are good at Thievery are very likely the same ones who are good at Stealth. On the other hand, offering Athletics as an alternative gives a champion who's terrible at Stealth a way to help. The group can help cover for a character who is less capable at a particular obstacle, but it's more fun for players when you present substantially different options for each obstacle.</p><p>Use the following guidelines to determine how many obstacles you need for your chase. These numbers assume that the pursued party can reach a certain location to end the chase (as described in Ending Chases). If there's no such escape, you might need more obstacles.</p><p><strong>Short:</strong> 6 obstacles, about 10–20 minutes of game time</p><p><strong>Medium:</strong> 8 obstacles, about 15–25 minutes of game time</p><p><strong>Long:</strong> 10 obstacles, about 20–30 minutes of game time</p><h2>Running a Chase</h2><p>When running a chase, narrate the scene and give vivid descriptions of the obstacles the PCs face, rather than just reading off a list of skills and immediately having the players start rolling dice and making checks. A chase is a framework for roleplaying, not just a dice game. Encourage the PCs to describe what they're doing, and how they're helping their comrades overcome each obstacle.</p><p>Typically, it's best to tell the players the DCs of the default options, so they can make informed decisions. At the very least, you should indicate the relative difficulty of the clear paths.</p><p>Try to make it feel like the PCs are really part of a chase scene, like in a movie. As each side makes progress, describe how they pull ahead or close the gap. PCs far from their foes might hear shouts in the distance. As they get closer, they catch glimpses, and then finally see their quarry in full view once they're on the enemies' heels. Think about how the events of the chase affect the environment as well. For instance, if a kaiju is chasing after the PCs, after the PCs overcome an obstacle consisting of a thick copse of trees, you could describe how the kaiju flattens the trees beneath its feet as it stomps after them.</p><h3>If the PCs Get Stuck</h3><p>Sometimes, despite their best efforts, an obstacle will stymie the PCs over and over again. In most cases, after 3 rounds of the PCs struggling with an obstacle that requires the standard number of Chase Points, it's a good idea to just say they found another way around it. If presenting another way around the obstacle just doesn't make sense, such as if a spherical barrier completely blocks the PCs, you might introduce an NPC or other outside force that can help them bypass it, but at a high cost.</p><table class=\"pf2e remaster\"><tbody><tr><th colspan=\"2\"><h4><strong>Underground Obstacles</strong></h4></th></tr><tr><td><p><strong>Crumbling Corridor (1st)</strong></p></td><td><p><span class=\"chk\" data-skill=\"acrobatics\" data-dc=\"13\">Acrobatics DC 13</span> to avoid disturbing the walls, <span class=\"chk\" data-skill=\"crafting\" data-dc=\"15\">Crafting DC 15</span> to shore up the walls;</p><p><strong>Quaking Corridor (11th)</strong> <span class=\"chk\" data-skill=\"acrobatics\" data-dc=\"25\">Acrobatics DC 25</span>, <span class=\"chk\" data-skill=\"crafting\" data-dc=\"30\">Crafting DC 30</span></p></td></tr><tr><td><p><strong>Fungus Grotto (1st)</strong></p></td><td><p><span class=\"chk\" data-skill=\"fortitude\" data-dc=\"15\">Fortitude DC 15</span> to endure poisonous spore, <span class=\"chk\" data-skill=\"survival\" data-dc=\"13\">Survival DC 13</span> to avoid the mushrooms;</p><p><strong>Virulent Fungi (5th)</strong> <span class=\"chk\" data-skill=\"fortitude\" data-dc=\"20\">Fortitude DC 20</span>, <span class=\"chk\" data-skill=\"survival\" data-dc=\"18\">Survival DC 18</span></p></td></tr><tr><td><p><strong>Pit Trap (1st)</strong></p></td><td><p><span class=\"chk\" data-skill=\"athletics\" data-dc=\"13\">Athletics DC 13</span> to quickly climb out, <span class=\"chk\" data-skill=\"perception\" data-dc=\"15\">Perception DC 15</span> to spot the trap before it's triggered;</p><p><strong>Exceptional Pit Trap (5th)</strong> <span class=\"chk\" data-skill=\"athletics\" data-dc=\"20\">Athletics DC 20</span>, <span class=\"chk\" data-skill=\"perception\" data-dc=\"18\">Perception DC 18</span></p></td></tr><tr><td><p><strong>Wandering Gelatinous Cube (1st)</strong></p></td><td><p><span class=\"chk\" data-skill=\"occultism\" data-dc=\"18\">Occultism DC 18</span> to identify its deficiencies, <span class=\"chk\" data-skill=\"stealth\" data-dc=\"15\">Stealth DC 15</span> to sneak past;</p><p><strong>Wandering Black Pudding (7th)</strong> <span class=\"chk\" data-skill=\"occultism\" data-dc=\"24\">Occultism DC 24</span>, <span class=\"chk\" data-skill=\"stealth\" data-dc=\"19\">Stealth DC 19</span></p></td></tr><tr><td><p><strong>Collapsed Tunnel (5th)</strong></p></td><td><p><span class=\"chk\" data-skill=\"athletics\" data-dc=\"20\">Athletics DC 20</span> to dig through, <span class=\"chk\" data-skill=\"perception\" data-dc=\"18\">Perception DC 18</span> to find a secret door around;</p><p><strong>Ancient Collapse (12th)</strong> <span class=\"chk\" data-skill=\"athletics\" data-dc=\"30\">Athletics DC 30</span>, <span class=\"chk\" data-skill=\"perception\" data-dc=\"28\">Perception DC 28</span></p></td></tr><tr><td><p><strong>Pendulum Trap (5th)</strong></p></td><td><p><span class=\"chk\" data-skill=\"reflex\" data-dc=\"20\">Reflex DC 20</span> to dodge the blades, <span class=\"chk\" data-skill=\"thievery\" data-dc=\"15\">Thievery DC 15</span> to disable the tap;</p><p><strong>Panoply of Pendulums (12th)</strong> <span class=\"chk\" data-skill=\"reflex\" data-dc=\"30\">Reflex DC 30</span>, <span class=\"chk\" data-skill=\"thievery\" data-dc=\"28\">Thievery DC 28</span></p></td></tr><tr><td><p><strong>Wooden Portcullis (8th)</strong></p></td><td><p><span class=\"chk\" data-skill=\"acrobatics\" data-dc=\"25\">Acrobatics DC 25</span> to squeeze through, <span class=\"chk\" data-skill=\"athletics\" data-dc=\"20\">Athletics DC 20</span> to lift the gate;</p><p><strong>Iron Portcullis (11th)</strong> <span class=\"chk\" data-skill=\"acrobatics\" data-dc=\"25\">Acrobatics DC 25</span>, <span class=\"chk\" data-skill=\"athletics\" data-dc=\"30\">Athletics DC 30</span></p></td></tr></tbody></table><table class=\"pf2e remaster\"><tbody><tr><th colspan=\"2\"><h4><strong>Urban Obstacles</strong></h4></th></tr><tr><td><p><strong>Crowd (1st)</strong></p></td><td><p><span class=\"chk\" data-skill=\"acrobatics\" data-dc=\"15\">Acrobatics DC 15</span> or Athletics to weave or push through, <span class=\"chk\" data-skill=\"society\" data-dc=\"13\">Society DC 13</span> to follow the flow;</p><p><strong>Festival Crowd (4th)</strong> <span class=\"chk\" data-skill=\"athletics\" data-dc=\"20\">Athletics DC 20</span>, <span class=\"chk\" data-skill=\"society\" data-dc=\"18\">Society DC 18</span></p></td></tr><tr><td><p><strong>Fruit Cart (1st)</strong></p></td><td><p><span class=\"chk\" data-skill=\"athletics\" data-dc=\"13\">Athletics DC 13</span> to vault over or smash through, <span class=\"chk\" data-skill=\"intimidation\" data-dc=\"15\">Intimidation DC 15</span> to make the merchant move it;</p><p><strong>Merchant Pavilion (5th)</strong> <span class=\"chk\" data-skill=\"athletics\" data-dc=\"20\">Athletics DC 20</span>, <span class=\"chk\" data-skill=\"intimidation\" data-dc=\"22\">Intimidation DC 22</span></p></td></tr><tr><td><p><strong>Guard Dog (1st)</strong></p></td><td><p><span class=\"chk\" data-skill=\"nature\" data-dc=\"14\">Nature DC 14</span> to calm, <span class=\"chk\" data-skill=\"stealth\" data-dc=\"16\">Stealth DC 16</span> to sneak past;</p><p><strong>Guard Roc (9th)</strong> <span class=\"chk\" data-skill=\"nature\" data-dc=\"26\">Nature DC 26</span>, <span class=\"chk\" data-skill=\"stealth\" data-dc=\"28\">Stealth DC 28</span></p></td></tr><tr><td><p><strong>Rickety Rooftops (1st)</strong></p></td><td><p><span class=\"chk\" data-skill=\"acrobatics\" data-dc=\"15\">Acrobatics DC 15</span> to cross clotheslines, <span class=\"chk\" data-skill=\"athletics\" data-dc=\"13\">Athletics DC 13</span> to jump from roof to roof;</p><p><strong>Crumbling, Steep Rooftops (5th)</strong> <span class=\"chk\" data-skill=\"acrobatics\" data-dc=\"18\">Acrobatics DC 18</span>, <span class=\"chk\" data-skill=\"athletics\" data-dc=\"20\">Athletics DC 20</span></p></td></tr><tr><td><p><strong>Wooden Fence (1st)</strong></p></td><td><p><span class=\"chk\" data-skill=\"athletics\" data-dc=\"13\">Athletics DC 13</span> to climb over, <span class=\"chk\" data-skill=\"thievery\" data-dc=\"15\">Thievery DC 15</span> to unlock a gate;</p><p><strong>High Iron Fence (8th)</strong> <span class=\"chk\" data-skill=\"athletics\" data-dc=\"20\">Athletics DC 20</span>, <span class=\"chk\" data-skill=\"thievery\" data-dc=\"25\">Thievery DC 25</span></p></td></tr><tr><td><p><strong>Gang of Hooligans (2nd)</strong></p></td><td><p><span class=\"chk\" data-skill=\"deception\" data-dc=\"13\">Deception DC 13</span> to trick, <span class=\"chk\" data-skill=\"stealth\" data-dc=\"15\">Stealth DC 15</span> to sneak past;</p><p><strong>Elite Criminals (12th)</strong> <span class=\"chk\" data-skill=\"deception\" data-dc=\"28\">Deception DC 28</span>, <span class=\"chk\" data-skill=\"stealth\" data-dc=\"30\">Stealth DC 30</span></p></td></tr><tr><td><p><strong>Twisting Alleyways (2nd)</strong></p></td><td><p><span class=\"chk\" data-skill=\"perception\" data-dc=\"17\">Perception DC 17</span> to find a path, <span class=\"chk\" data-skill=\"society\" data-dc=\"13\">Society DC 13</span> to recall a map;</p><p><strong>Multi-Story Maze (7th)</strong> <span class=\"chk\" data-skill=\"perception\" data-dc=\"22\">Perception DC 22</span>, <span class=\"chk\" data-skill=\"society\" data-dc=\"20\">Society DC 20</span></p></td></tr></tbody></table><table class=\"pf2e remaster\"><tbody><tr><th colspan=\"2\"><h4><strong>Wilderness Obstacles</strong></h4></th></tr><tr><td><p><strong>Deep Mud (1st)</strong></p></td><td><p><span class=\"chk\" data-skill=\"athletics\" data-dc=\"15\">Athletics DC 15</span> to slog through, <span class=\"chk\" data-skill=\"perception\" data-dc=\"13\">Perception DC 13</span> to find a path;</p><p><strong>Horrid Bog (5th)</strong> <span class=\"chk\" data-skill=\"athletics\" data-dc=\"20\">Athletics DC 20</span>, <span class=\"chk\" data-skill=\"perception\" data-dc=\"18\">Perception DC 18</span></p></td></tr><tr><td><p><strong>Downpour (1st)</strong></p></td><td><p><span class=\"chk\" data-skill=\"fortitude\" data-dc=\"13\">Fortitude DC 13</span> to push through, <span class=\"chk\" data-skill=\"nature\" data-dc=\"15\">Nature DC 15</span> to predict the weather;</p><p><strong>Magical Thunderstorm (5th)</strong> <span class=\"chk\" data-skill=\"fortitude\" data-dc=\"30\">Fortitude DC 30</span>, <span class=\"chk\" data-skill=\"nature\" data-dc=\"25\">Nature DC 25</span></p></td></tr><tr><td><p><strong>Rope Bridge (1st)</strong></p></td><td><p><span class=\"chk\" data-skill=\"acrobatics\" data-dc=\"15\">Acrobatics DC 15</span> to cross carefully, <span class=\"chk\" data-skill=\"crafting\" data-dc=\"13\">Crafting DC 13</span> to make repairs;</p><p><strong>Solitary Frayed Rope (11th)</strong> <span class=\"chk\" data-skill=\"acrobatics\" data-dc=\"25\">Acrobatics DC 25</span>, <span class=\"chk\" data-skill=\"crafting\" data-dc=\"30\">Crafting DC 30</span></p></td></tr><tr><td><p><strong>Rushing River (1st)</strong></p></td><td><p><span class=\"chk\" data-skill=\"athletics\" data-dc=\"15\">Athletics DC 15</span> to swim or hop across stones, <span class=\"chk\" data-skill=\"survival\" data-dc=\"13\">Survival DC 13</span> to find a ford nearby;</p><p><strong>Flash Flood (5th)</strong> <span class=\"chk\" data-skill=\"athletics\" data-dc=\"20\">Athletics DC 20</span>, <span class=\"chk\" data-skill=\"survival\" data-dc=\"18\">Survival DC 18</span></p></td></tr><tr><td><p><strong>Steep Hills (1st)</strong></p></td><td><p><span class=\"chk\" data-skill=\"athletics\" data-dc=\"13\">Athletics DC 13</span> to climb across, <span class=\"chk\" data-skill=\"perception\" data-dc=\"15\">Perception DC 15</span> to find easier path;</p><p><strong>Rugged Mountain (5th)</strong> <span class=\"chk\" data-skill=\"athletics\" data-dc=\"20\">Athletics DC 20</span>, <span class=\"chk\" data-skill=\"perception\" data-dc=\"18\">Perception DC 18</span></p></td></tr><tr><td><p><strong>Swarm of Wasps (1st)</strong></p></td><td><p><span class=\"chk\" data-skill=\"fortitude\" data-dc=\"15\">Fortitude DC 15</span> to endure stings, <span class=\"chk\" data-skill=\"survival\" data-dc=\"13\">Survival DC 13</span> to smoke them out;</p><p><strong>Those Aren't Wasps! (5th)</strong> <span class=\"chk\" data-skill=\"fortitude\" data-dc=\"20\">Fortitude DC 20</span>, <span class=\"chk\" data-skill=\"survival\" data-dc=\"18\">Survival DC 18</span></p></td></tr><tr><td><p><strong>Tangled Forest (2nd)</strong></p></td><td><p><span class=\"chk\" data-skill=\"perception\" data-dc=\"17\">Perception DC 17</span> to find the way, <span class=\"chk\" data-skill=\"survival\" data-dc=\"13\">Survival DC 13</span> to plot a path;</p><p><strong>Enchanted Forest (5th)</strong> <span class=\"chk\" data-skill=\"perception\" data-dc=\"20\">Perception DC 20</span>, <span class=\"chk\" data-skill=\"survival\" data-dc=\"18\">Survival DC 18</span></p></td></tr></tbody></table>"}, {"slug": "infiltration", "name": "Infiltration", "section": "Subsystem and Variant Rules", "book": "Pathfinder GM Core", "pages": "196-199", "html": "<h2>Obstacles in Play</h2><p>On a character's turn, the character describes what they do to get past the obstacle. They then attempt any required check (or perform the required action, if their choice doesn't require a check). The result of the check determines how many Infiltration Points the character gains toward overcoming that obstacle—or whether they instead raise suspicions and accrue Awareness Points!</p><p><strong>Critical Success</strong> The PC gains 2 Infiltration Points.</p><p><strong>Success</strong> The PC gains 1 Infiltration Point.</p><p><strong>Failure</strong> The PCs accrue 1 Awareness Point.</p><p><strong>Critical Failure</strong> The PCs accrue 2 Awareness Points.</p><p>If the character's actions automatically help without requiring a check, like using a spell, they usually gain 1 IP, but you can award 2 for particularly helpful actions.</p><h2>Preparation Activities</h2><h3><span class=\"actref\" data-action=\"bribe-contact\">Bribe Contact</span></h3><h3><span class=\"actref\" data-action=\"forge-documents\">Forge Documents</span></h3><h3><span class=\"actref\" data-action=\"gain-contact\">Gain Contact</span></h3><h3><span class=\"actref\" data-action=\"gossip\">Gossip</span></h3><h3><span class=\"actref\" data-action=\"gossip\">Gossip</span></h3><h3><span class=\"actref\" data-action=\"scout-location\">Scout Location</span></h3><h3><span class=\"actref\" data-action=\"secure-disguises\">Secure Disguises</span></h3>"}, {"slug": "reputation", "name": "Reputation", "section": "Subsystem and Variant Rules", "book": "Pathfinder GM Core", "pages": "200-201", "html": "<table class=\"pf2e remaster\"><caption><strong>REPUTATIONS</strong></caption><tbody><tr><th>Reputation</th><th>Reputation Points</th><th>Raised By</th><th>Lowered By</th></tr><tr><td>Revered</td><td>30 to 50</td><td>Major favor</td><td>Moderate or major disservice</td></tr><tr><td>Admired</td><td>15 to 29</td><td>Major favor</td><td>Any disservice</td></tr><tr><td>Liked</td><td>5 to 14</td><td>Moderate or major favor</td><td>Any disservice</td></tr><tr><td>Ignored</td><td>-4 to 4</td><td>Any favor</td><td>Any disservice</td></tr><tr><td>Disliked</td><td>-5 to -14</td><td>Any favor</td><td>Moderate or major disservice</td></tr><tr><td>Hated</td><td>-15 to -29</td><td>Any favor</td><td>Major disservice</td></tr><tr><td>Hunted</td><td>-30 -50</td><td>Moderate or major favor</td><td>Major disservice</td></tr></tbody></table><h2>Reputations</h2><h3>Ignored</h3><p>The PCs either aren't on this group's radar or the group knows about the PCs but is generally ambivalent to them. This is the default for most PCs with most groups, and it carries no special benefits or detriments.</p><h3>Liked</h3><p>The PCs have gained this group's favor. Many members of the group know about the PCs, and those who do are usually friendly to them. At this reputation, only moderate and major favors accrue Reputation Points; it takes more to impress the group further.</p><h3>Admired</h3><p>The PCs have earned this group's admiration. The majority of the group knows about the PCs and have an extremely favorable opinion toward them. Many members of the group are helpful toward the PCs, and those who aren't are friendly. Only major favors accrue Reputation Points.</p><h3>Revered</h3><p>The group reveres the PCs as heroes and celebrities. Every member has heard of the PCs, is helpful toward them, and would take major risks to assist them. Only major favors accrue Reputation Points, and only moderate or major disservices can reduce them.</p><h3>Disliked</h3><p>The PCs have a poor reputation among members of this group. Many members of the group know about the PCs, and are usually unfriendly to them. At this reputation, only moderate and major disservices reduce Reputation Points.</p><h3>Hated</h3><p>The PCs have earned this group's ire. The vast majority of the group knows about the PCs and have an extremely unfavorable opinion toward them. Many members of the group are hostile toward the PCs, and those who aren't are unfriendly. When presented an easy opportunity to hurt the PCs, the group will jump at the chance. Only major disservices can still reduce Reputation Points.</p><h3>Hunted</h3><p>The group actively hunts the PCs as scapegoats or nemeses, even at significant cost to itself. Every member has heard of the PCs, is hostile toward them, and would take major risks to thwart or destroy them. Only major disservices can still reduce Reputation Points, and only moderate or major favors can increase them.</p>"}, {"slug": "duels", "name": "Duels", "section": "Subsystem and Variant Rules", "book": "Pathfinder GM Core", "pages": "202-203", "html": "<h1>Setting Up a Duel</h1><p>Participants must willingly agree to the duel and to abide by its rules. If one of the duelists breaks the duel's rules (and, more importantly, is caught doing so), that duelist loses, taking any penalties agreed upon when the challenge was accepted. Here are some sample dueling rules.</p><h2>Compete Alone</h2><p>It's customary that each participant must compete alone and may receive no help from outside sources. However, some duels pit pairs of combatants against one another (either all together or as tag teams).</p><h2>Compete Alone</h2><p>The participants agree to the tools, including weapons and magic items, before the duel starts. Most combat duels that don't involve magic limit participants to melee weapons and prohibit the use of poison. Some duels forbid the use of polearms and other reach weapons. A spellcasting duel might agree upon a roughly equal number of magic items, and a cap on the power (in game terms, the level) of the items. Some spellcasting duels might prohibit certain types of spells, such as summoning or healing.</p><h2>Duration</h2><p>Combat duels typically last either until first blood (hitting and dealing damage) or until one of the duelists is knocked out. Most duels allow a participant to yield, which means they concede victory to their opponent, though sometimes this could diminish their social standing. Duels of talent usually involve several equal turns in which the duelists display their ability.</p><h2>Adjudication</h2><p>Most duels are overseen by a third party who ensures the duelists don't break the duel's rules—inadvertently or by cheating. Where duels are legal, this is typically a constable or magistrate. In other places, a cleric or other respected figure serves as judge. Typically the GM plays the judge.</p><h1>Combat Duels</h1><p>Unlike the other subsystems in this chapter, a combat duel works almost the same as a normal combat encounter, with a few exceptions. These rules require exceptional focus between two duelists and a third-party arbiter, and thus are not available in a normal combat.</p><h2>Initiative and Dueling Actions</h2><p>Unlike in typical combat, the duelists roll initiative checks at the beginning of each round of combat. Each round the combatant can choose to use Deception, Intimidation, or Perception when they roll initiative. Because a duelist might act twice in a row, durations that last until the beginning of a duelist's next turn might function oddly. A duelist acting second can choose to avoid such abilities that turn, or can choose to act second if they win initiative on the next round.</p><p>If the duelist is at least trained in whichever initiative choice they pick, they gain the corresponding dueling reaction that round: Bullying Press for Intimidation, Deceptive Sidestep for Deception, or Sense Weakness for Perception. Neither duelist is aware of what type of roll the other used for initiative—surprise and the use of dueling actions are a part of dueling strategies. Familiars and companions, even when allowed in the duel, can't use these actions, nor can bystanders.</p><h3><span class=\"actref\" data-action=\"bullying-press\">Bullying Press</span></h3><hr><h3><span class=\"actref\" data-action=\"deceptive-sidestep\">Deceptive Sidestep</span></h3><hr><h3><span class=\"actref\" data-action=\"sense-weakness\">Sense Weakness</span></h3><hr><h2>Ending the Duel</h2><p>The duel ends when the duel's judge confirms the victory condition, when the judge determines one of the duelists has cheated, or when one of the duelists yields. Note that if either of the combatants attempts to continue the duel after its end, the combatants should roll initiative and proceed with normal combat.</p><h1>Spellcasting Duels</h1><p>Like combat duels, spellcasting duels take place in encounter mode, but their rules are not available during normal combat. They are typically more organized affairs than combat duels. Many spellcasting duels prohibit any sort of combat but spellcasting. They typically have the duelists take turns casting a turn's worth of spells, giving their rivals a chance to counter the spells if they can.</p><h2>Initiative and Dueling Actions</h2><p>In most cases, each duelist rolls initiative normally and proceeds in that initiative order throughout the duel, unlike in a combat duel. Each duelist can roll an Arcana, Nature, Occultism, or Religion check instead of Perception. If they are trained at that skill, they gain the tradition focus of that skill, which allows them to hone in on a magical tradition corresponding to the check they made for their initiative roll. They also gain the Dueling Counter reaction, which enables a duelist to counter their opponent's spell if their tradition focus matches the tradition of that spell. They also gain the Change Tradition Focus action, which changes the duelist's tradition focus to a different tradition. Familiars and companions, even when allowed in the duel, can't use these actions, nor can bystanders.</p><p>When mixing a spellcasting duel and a combat duel, use the initiative rules for combat dueling, but allow the duelist to roll Arcana, Nature, Occultism, or Religion for their initiative checks. They still gain the Dueling Counter reaction and Change Tradition Focus action, though Change Tradition Focus is less useful in combat duels.</p><h3><span class=\"actref\" data-action=\"dueling-counter\">Dueling Counter</span></h3><hr><h3><span class=\"actref\" data-action=\"change-tradition-focus\">Change Tradition Focus</span></h3><h2>Ending the Duel</h2><p>Just like a combat duel, a spellcasting duel ends when the duel's judge confirms the victory condition, when the judge determines one of the duelists has cheated, or when one of the duelists yields. As with a combat duel, if either of the combatants attempts to continue the duel after its end, the combatants should roll initiative and proceed with normal combat.</p>"}, {"slug": "leadership", "name": "Leadership", "section": "Subsystem and Variant Rules", "book": "Pathfinder GM Core", "pages": "204-205", "html": "<p>Leadership is a long-term subsystem wherein one or more PCs establish a fledgling organization and accumulate followers over time. This subsystem provides a flexible way to reflect the party's growing influence in and connection to the world around them through downtime events and periodic benefits similar to those gained from joining an existing in-world organization. The organization can come into play only during downtime, or it can provide adventure seeds or even play a major role in a campaign.</p><p><strong>ORGANIZATION STATISTICS BY LEVEL</strong></p><table class=\"pf2e remaster\"><thead><tr><th>Level</th><th>Max Followers</th><th>Lieutenant Level</th><th>Lieutenants</th><th>Levels</th></tr></thead><tbody><tr><td>1</td><td>1-2</td><td>0</td><td>-</td><td>-</td></tr><tr><td>2</td><td>3-4</td><td>0</td><td>-</td><td>-</td></tr><tr><td>3</td><td>5-6</td><td>0</td><td>1</td><td>1</td></tr><tr><td>4</td><td>7-9</td><td>0</td><td>1</td><td>1</td></tr><tr><td>5</td><td>10-13</td><td>0</td><td>1</td><td>1</td></tr><tr><td>6</td><td>14-18</td><td>1</td><td>2</td><td>2</td></tr><tr><td>7</td><td>19-27</td><td>1</td><td>2</td><td>2</td></tr><tr><td>8</td><td>28-36</td><td>1</td><td>3</td><td>2-3</td></tr><tr><td>9</td><td>37-53</td><td>1</td><td>4-5</td><td>2-3</td></tr><tr><td>10</td><td>54-75</td><td>2</td><td>6-7</td><td>3-4</td></tr><tr><td>11</td><td>76-99</td><td>2</td><td>8-10</td><td>3-4</td></tr><tr><td>12</td><td>100-150</td><td>2</td><td>11-15</td><td>3-5</td></tr><tr><td>13</td><td>151-215</td><td>2</td><td>16-22</td><td>3-5</td></tr><tr><td>14</td><td>216-300</td><td>3</td><td>23-30</td><td>4-6</td></tr><tr><td>15</td><td>301-425</td><td>3</td><td>31-42</td><td>4-6</td></tr><tr><td>16</td><td>426-600</td><td>3</td><td>43-60</td><td>4-7</td></tr><tr><td>17</td><td>601-850</td><td>3</td><td>61-85</td><td>4-7</td></tr><tr><td>18</td><td>851-1,200</td><td>4</td><td>86-120</td><td>5-8</td></tr><tr><td>19</td><td>1,201-1,700</td><td>4</td><td>121-170</td><td>5-8</td></tr><tr><td>20</td><td>1,701-2,400</td><td>4</td><td>171-240</td><td>5-9</td></tr></tbody></table><h3>Running Leadership</h3><p>When running a game with the leadership subsystem, adding new NPCs who join the PCs' cause becomes a type of reward you should grant the PCs consistently throughout the campaign. For a typical rate of growth, give the PCs enough followers for the organization to level up at roughly the same rate as the PCs. In some cases, you might want to vary that rate—sometimes drastically. Perhaps the 10th-level PCs have a 20th-level organization with top lieutenants nearly as powerful as the PCs, or maybe the PCs start gathering followers only at 15th level and start their organization from scratch as a 1st-level organization.</p><h3>Leadership Events</h3><p>During downtime, or over the course of long stretches of time when the PCs aren't in downtime but their organization is operating in the background, you should periodically provide special leadership events. These are in addition to the events you would normally provide the PCs during downtime. These should generally be a good mix between the following three categories.</p><p><strong>Opportunity</strong>: An opportunity offers the PCs a chance to make a consequential decision that shapes the organization moving forward. For instance, the Aspis Consortium offers the PCs a deal to store their goods in the PCs' mercenary fort. Accepting gives the PCs money, but they'll have to deal with the consortium's unsavory goods. Rejecting the proposal might upset the consortium, but it allows the PCs to show their priorities between conscience and coin.</p><p><strong>Trouble:</strong> Something's gone wrong with the organization. Perhaps the thieves' guild is dealing with a clever new guard captain and needs help from the PCs to stay safe. Typically the resources the PCs need to invest to solve the troubles should be less than the benefits the PCs gain from windfalls to ensure that the organization is providing a substantial benefit to the PCs, rather than simply evening out.</p><p><strong>Windfall:</strong> The organization has received an unexpected benefit that helps the PCs. This is typically access to an uncommon option: perhaps the organization researched a new spell or discovered the techniques for an uncommon feat. However, it could be extra money or resources, clues for the PCs' adventures, political prestige, or anything else you can think of. The players might even be able to suggest a few options for their followers and lieutenants to work toward to give you some ideas.</p>"}, {"slug": "hexploration", "name": "Hexploration", "section": "Subsystem and Variant Rules", "book": "Pathfinder GM Core", "pages": "208-209", "html": "<p><strong>RANDOM TERRAIN TYPE</strong></p><table class=\"pf2e remaster\"><thead><tr><th>1d20</th><th>Result</th></tr></thead><tbody><tr><td>1-3</td><td>Plains</td></tr><tr><td>4-5</td><td>Desert</td></tr><tr><td>6-7</td><td>Aquatic (lake, sea, or ocean)</td></tr><tr><td>8-9</td><td>Mountain</td></tr><tr><td>10-11</td><td>Forest</td></tr><tr><td>12</td><td>Swamp</td></tr><tr><td>13</td><td>Arctic</td></tr><tr><td>14-20</td><td>Match the previous hex</td></tr></tbody></table><p><strong>RANDOM TERRAIN FEATURE</strong></p><table class=\"pf2e remaster\"><thead><tr><th>1d20</th><th>Result</th></tr></thead><tbody><tr><td>1-3</td><td>Landmark A feature of some significance that distinguishes the hex as noteworthy.</td></tr><tr><td>4-6</td><td>Secret The hex contains a secret the party uncovers upon exploring the hex.</td></tr><tr><td>7-9</td><td>Resource The hex contains some valuable resource appropriate to the terrain.</td></tr><tr><td>10-20</td><td>Standard A standard representation of the terrain type.</td></tr></tbody></table><p><strong>HEXPLORATION ACTIVITIES PER DAY</strong></p><table class=\"pf2e remaster\"><thead><tr><th>Speed</th><th>Activities per Day</th></tr></thead><tbody><tr><td>10 feet or less</td><td>1/2</td></tr><tr><td>15-25 feet</td><td>1</td></tr><tr><td>30-40 feet</td><td>2</td></tr><tr><td>45-55 feet</td><td>3</td></tr><tr><td>60 feet or more</td><td>4</td></tr></tbody></table><h2>Group Activities</h2><h3><span class=\"actref\" data-action=\"travel\">Travel</span></h3><hr><h3><span class=\"actref\" data-action=\"reconnoiter\">Reconnoiter</span></h3><hr><h2>Individual Activities</h2><h3><span class=\"actref\" data-action=\"fortify-camp\">Fortify Camp</span></h3><hr><h3><span class=\"actref\" data-action=\"map-the-area\">Map the Area</span></h3><hr><h4><strong>Existing Activities</strong></h4><p>Characters can use the Subsist downtime activity, which follows the same rules but assumes they're using it after 8 hours or less of exploration. Any skill feats or other abilities that apply to Subsist normally still apply here. In general, the various {Exploration Activities} (except Hustle) can be used as individual hexploration activities, as can {Skill Actions}, at the GM's discretion.</p><h2>Random Encounters</h2><p><strong>RANDOM ENCOUNTER CHANCE</strong></p><table class=\"pf2e remaster\"><thead><tr><th>Terrain Type</th><th>Flat Check DC*</th></tr></thead><tbody><tr><td>Aquatic</td><td>17</td></tr><tr><td>Arctic</td><td>17</td></tr><tr><td>Desert</td><td>17</td></tr><tr><td>Forest</td><td>14</td></tr><tr><td>Mountain</td><td>16</td></tr><tr><td>Plains</td><td>12</td></tr><tr><td>Swamp</td><td>14</td></tr><tr></tr><tr><td colspan=\"2\">*On a road or river, decrease the DC by 2. If PCs are flying, increase the DC by 3, but choose a hazard or monster that is relevant to flying PCs.</td></tr></tbody></table><p><strong>RANDOM ENCOUNTER TYPE</strong></p><table class=\"pf2e remaster\"><thead><tr><th>1d10</th><th>Encounter</th></tr></thead><tbody><tr><td>1-5</td><td>Harmless</td></tr><tr><td>6-7</td><td>Hazard</td></tr><tr><td>8-10</td><td>Creature</td></tr></tbody></table>"}];
|
|
353
|
+
const GENERATED_CHASE_OBSTACLES = [{"table": "Underground", "name": "Crumbling Corridor", "level": 1, "options": [{"skill": "acrobatics", "dc": 13, "how": "avoid disturbing the walls"}, {"skill": "crafting", "dc": 15, "how": "shore up the walls"}]}, {"table": "Underground", "name": "Quaking Corridor", "level": 11, "options": [{"skill": "acrobatics", "dc": 25, "how": null}, {"skill": "crafting", "dc": 30, "how": null}]}, {"table": "Underground", "name": "Fungus Grotto", "level": 1, "options": [{"skill": "fortitude", "dc": 15, "how": "endure poisonous spore"}, {"skill": "survival", "dc": 13, "how": "avoid the mushrooms"}]}, {"table": "Underground", "name": "Virulent Fungi", "level": 5, "options": [{"skill": "fortitude", "dc": 20, "how": null}, {"skill": "survival", "dc": 18, "how": null}]}, {"table": "Underground", "name": "Pit Trap", "level": 1, "options": [{"skill": "athletics", "dc": 13, "how": "quickly climb out"}, {"skill": "perception", "dc": 15, "how": "spot the trap before it's triggered"}]}, {"table": "Underground", "name": "Exceptional Pit Trap", "level": 5, "options": [{"skill": "athletics", "dc": 20, "how": null}, {"skill": "perception", "dc": 18, "how": null}]}, {"table": "Underground", "name": "Wandering Gelatinous Cube", "level": 1, "options": [{"skill": "occultism", "dc": 18, "how": "identify its deficiencies"}, {"skill": "stealth", "dc": 15, "how": "sneak past"}]}, {"table": "Underground", "name": "Wandering Black Pudding", "level": 7, "options": [{"skill": "occultism", "dc": 24, "how": null}, {"skill": "stealth", "dc": 19, "how": null}]}, {"table": "Underground", "name": "Collapsed Tunnel", "level": 5, "options": [{"skill": "athletics", "dc": 20, "how": "dig through"}, {"skill": "perception", "dc": 18, "how": "find a secret door around"}]}, {"table": "Underground", "name": "Ancient Collapse", "level": 12, "options": [{"skill": "athletics", "dc": 30, "how": null}, {"skill": "perception", "dc": 28, "how": null}]}, {"table": "Underground", "name": "Pendulum Trap", "level": 5, "options": [{"skill": "reflex", "dc": 20, "how": "dodge the blades"}, {"skill": "thievery", "dc": 15, "how": "disable the tap"}]}, {"table": "Underground", "name": "Panoply of Pendulums", "level": 12, "options": [{"skill": "reflex", "dc": 30, "how": null}, {"skill": "thievery", "dc": 28, "how": null}]}, {"table": "Underground", "name": "Wooden Portcullis", "level": 8, "options": [{"skill": "acrobatics", "dc": 25, "how": "squeeze through"}, {"skill": "athletics", "dc": 20, "how": "lift the gate"}]}, {"table": "Underground", "name": "Iron Portcullis", "level": 11, "options": [{"skill": "acrobatics", "dc": 25, "how": null}, {"skill": "athletics", "dc": 30, "how": null}]}, {"table": "Urban", "name": "Crowd", "level": 1, "options": [{"skill": "acrobatics", "dc": 15, "how": "or Athletics to weave or push through"}, {"skill": "society", "dc": 13, "how": "follow the flow"}]}, {"table": "Urban", "name": "Festival Crowd", "level": 4, "options": [{"skill": "athletics", "dc": 20, "how": null}, {"skill": "society", "dc": 18, "how": null}]}, {"table": "Urban", "name": "Fruit Cart", "level": 1, "options": [{"skill": "athletics", "dc": 13, "how": "vault over or smash through"}, {"skill": "intimidation", "dc": 15, "how": "make the merchant move it"}]}, {"table": "Urban", "name": "Merchant Pavilion", "level": 5, "options": [{"skill": "athletics", "dc": 20, "how": null}, {"skill": "intimidation", "dc": 22, "how": null}]}, {"table": "Urban", "name": "Guard Dog", "level": 1, "options": [{"skill": "nature", "dc": 14, "how": "calm"}, {"skill": "stealth", "dc": 16, "how": "sneak past"}]}, {"table": "Urban", "name": "Guard Roc", "level": 9, "options": [{"skill": "nature", "dc": 26, "how": null}, {"skill": "stealth", "dc": 28, "how": null}]}, {"table": "Urban", "name": "Rickety Rooftops", "level": 1, "options": [{"skill": "acrobatics", "dc": 15, "how": "cross clotheslines"}, {"skill": "athletics", "dc": 13, "how": "jump from roof to roof"}]}, {"table": "Urban", "name": "Crumbling, Steep Rooftops", "level": 5, "options": [{"skill": "acrobatics", "dc": 18, "how": null}, {"skill": "athletics", "dc": 20, "how": null}]}, {"table": "Urban", "name": "Wooden Fence", "level": 1, "options": [{"skill": "athletics", "dc": 13, "how": "climb over"}, {"skill": "thievery", "dc": 15, "how": "unlock a gate"}]}, {"table": "Urban", "name": "High Iron Fence", "level": 8, "options": [{"skill": "athletics", "dc": 20, "how": null}, {"skill": "thievery", "dc": 25, "how": null}]}, {"table": "Urban", "name": "Gang of Hooligans", "level": 2, "options": [{"skill": "deception", "dc": 13, "how": "trick"}, {"skill": "stealth", "dc": 15, "how": "sneak past"}]}, {"table": "Urban", "name": "Elite Criminals", "level": 12, "options": [{"skill": "deception", "dc": 28, "how": null}, {"skill": "stealth", "dc": 30, "how": null}]}, {"table": "Urban", "name": "Twisting Alleyways", "level": 2, "options": [{"skill": "perception", "dc": 17, "how": "find a path"}, {"skill": "society", "dc": 13, "how": "recall a map"}]}, {"table": "Urban", "name": "Multi-Story Maze", "level": 7, "options": [{"skill": "perception", "dc": 22, "how": null}, {"skill": "society", "dc": 20, "how": null}]}, {"table": "Wilderness", "name": "Deep Mud", "level": 1, "options": [{"skill": "athletics", "dc": 15, "how": "slog through"}, {"skill": "perception", "dc": 13, "how": "find a path"}]}, {"table": "Wilderness", "name": "Horrid Bog", "level": 5, "options": [{"skill": "athletics", "dc": 20, "how": null}, {"skill": "perception", "dc": 18, "how": null}]}, {"table": "Wilderness", "name": "Downpour", "level": 1, "options": [{"skill": "fortitude", "dc": 13, "how": "push through"}, {"skill": "nature", "dc": 15, "how": "predict the weather"}]}, {"table": "Wilderness", "name": "Magical Thunderstorm", "level": 5, "options": [{"skill": "fortitude", "dc": 30, "how": null}, {"skill": "nature", "dc": 25, "how": null}]}, {"table": "Wilderness", "name": "Rope Bridge", "level": 1, "options": [{"skill": "acrobatics", "dc": 15, "how": "cross carefully"}, {"skill": "crafting", "dc": 13, "how": "make repairs"}]}, {"table": "Wilderness", "name": "Solitary Frayed Rope", "level": 11, "options": [{"skill": "acrobatics", "dc": 25, "how": null}, {"skill": "crafting", "dc": 30, "how": null}]}, {"table": "Wilderness", "name": "Rushing River", "level": 1, "options": [{"skill": "athletics", "dc": 15, "how": "swim or hop across stones"}, {"skill": "survival", "dc": 13, "how": "find a ford nearby"}]}, {"table": "Wilderness", "name": "Flash Flood", "level": 5, "options": [{"skill": "athletics", "dc": 20, "how": null}, {"skill": "survival", "dc": 18, "how": null}]}, {"table": "Wilderness", "name": "Steep Hills", "level": 1, "options": [{"skill": "athletics", "dc": 13, "how": "climb across"}, {"skill": "perception", "dc": 15, "how": "find easier path"}]}, {"table": "Wilderness", "name": "Rugged Mountain", "level": 5, "options": [{"skill": "athletics", "dc": 20, "how": null}, {"skill": "perception", "dc": 18, "how": null}]}, {"table": "Wilderness", "name": "Swarm of Wasps", "level": 1, "options": [{"skill": "fortitude", "dc": 15, "how": "endure stings"}, {"skill": "survival", "dc": 13, "how": "smoke them out"}]}, {"table": "Wilderness", "name": "Those Aren't Wasps!", "level": 5, "options": [{"skill": "fortitude", "dc": 20, "how": null}, {"skill": "survival", "dc": 18, "how": null}]}, {"table": "Wilderness", "name": "Tangled Forest", "level": 2, "options": [{"skill": "perception", "dc": 17, "how": "find the way"}, {"skill": "survival", "dc": 13, "how": "plot a path"}]}, {"table": "Wilderness", "name": "Enchanted Forest", "level": 5, "options": [{"skill": "perception", "dc": 20, "how": null}, {"skill": "survival", "dc": 18, "how": null}]}];
|
|
354
|
+
const GENERATED_VP_SCALES = [{"duration": "Quick encounter", "endPoint": "3-5", "thresholds": []}, {"duration": "Long encounter", "endPoint": "7-10", "thresholds": [4]}, {"duration": "Most of a session", "endPoint": "15-25", "thresholds": [5, 10, 15]}, {"duration": "Adventure-wide, sideline", "endPoint": "15-20", "thresholds": [5, 10, 15]}, {"duration": "Adventure-wise, forefront", "endPoint": "25-50", "thresholds": [10, 20, 30, 40]}];
|
|
355
|
+
|
|
356
|
+
</script>
|
|
357
|
+
<script>
|
|
358
|
+
/* data/reference.generated.js — AUTO-GENERATED. Do not hand-edit.
|
|
359
|
+
Rebuild with: npm run build:ref
|
|
360
|
+
Source: foundryvtt/pf2e condition + action data (Paizo content, OGL/ORC). */
|
|
361
|
+
const GENERATED_REF_META = {"generated": "2026-09-13", "source": "foundryvtt/pf2e", "sourceCommit": "cf1adff", "conditions": 43, "actions": 120, "sources": [{"title": "Pathfinder Player Core", "license": "ORC", "count": 132}, {"title": "Pathfinder GM Core", "license": "ORC", "count": 17}, {"title": "Pathfinder Treasure Vault (Remastered)", "license": "ORC", "count": 3}, {"title": "Pathfinder Gamemastery Guide", "license": "OGL", "count": 3}, {"title": "Pathfinder Dark Archive (Remastered)", "license": "ORC", "count": 3}, {"title": "Pathfinder Player Core 2", "license": "ORC", "count": 2}, {"title": "Pathfinder Secrets of Magic", "license": "OGL", "count": 2}, {"title": "Pathfinder Impossible Magic", "license": "ORC", "count": 1}]};
|
|
362
|
+
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"}];
|
|
363
|
+
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": "bribe-contact", "name": "Bribe Contact", "category": "interaction", "traits": ["downtime", "secret"], "exploration": false, "actionType": "passive", "actions": null, "description": "Cost A bribe worth at least one-tenth of the Currency per Additional PC listed on Party Treasure by level. Doubling this amount grants a +2 circumstance bonus to the check.\n\nRequirements You've successfully Gained a Contact.\n\nYou offer a bribe to your contact to help the heist in some way. Attempt a hard or very hard Deception or Diplomacy check.\n\nSuccess The contact accepts the bribe and you gain 1 EP.\n\nFailure You believe you successfully Bribed your Contact and gained 1 EP, but in fact the contact informs the opposition of the attempted bribery, adding 1 AP to the infiltration. The GM can reveal that this Edge Point grants no benefit at any point during the infiltration, as befits the story.\n\nCritical Failure As failure, but adding 2 AP to the infiltration."}, {"slug": "bullying-press", "name": "Bullying Press", "category": "offensive", "traits": ["flourish"], "exploration": false, "actionType": "reaction", "actions": null, "description": "Trigger You attempt a melee Strike against your opponent, but haven't rolled yet.\n\nRequirements You're in a duel, you're trained in Intimidation, and you rolled an Intimidation check for initiative this round.\n\nIf you hit, your opponent becomes Frightened 1. If your opponent is using Perception for initiative when this ability is used, they become Frightened 2 instead."}, {"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": "change-tradition-focus", "name": "Change Tradition Focus", "category": "interaction", "traits": [], "exploration": false, "actionType": "action", "actions": 1, "description": "Requirements You are in a duel and are trained in the skill for the tradition you're changing your focus to (Arcana for arcane, Occultism for occult, Nature for primal, or Religion for divine).\n\nYou change your tradition focus to another magical tradition."}, {"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": "deceptive-sidestep", "name": "Deceptive Sidestep", "category": "defensive", "traits": ["misfortune"], "exploration": false, "actionType": "reaction", "actions": null, "description": "Trigger An opponent hits, but does not critically hit you, with a melee Strike.\n\nRequirements You're in a duel, you're trained in Deception, and you rolled a Deception check for initiative this round.\n\nYou draw your enemy in and pull away at the last moment. The triggering opponent must roll again and take the second result. If your opponent is using Intimidation for initiative when this ability is used, they also take a -2 circumstance penalty to the second attack roll."}, {"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": "discover", "name": "Discover", "category": "interaction", "traits": ["concentrate", "secret"], "exploration": false, "actionType": "passive", "actions": null, "description": "You watch or study an NPC to learn more about that NPC's preferences. Attempt a Perception check or an appropriate skill check determined by the GM. The DC is found in the NPC's influence stat block.\n\nCritical Success Choose two of the options detailed in Success below; you can choose the same option twice to learn two pieces of information from the same category.\n\nSuccess Choose one of the following: You learn which skill that can Influence the NPC has the lowest DC (skipping any skills that you already know), one of the NPC's personal biases, one of the NPC's resistances, or one of the NPC's weaknesses.\n\nFailure You learn no information.\n\nCritical Failure Choose a piece of information to learn about, as success, but the information is incorrect."}, {"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": "dueling-counter", "name": "Dueling Counter", "category": "defensive", "traits": ["magical"], "exploration": false, "actionType": "reaction", "actions": null, "description": "Trigger Your opponent Casts a Spell from the same tradition as your tradition focus.\n\nRequirements You are in a duel and have a tradition focus.\n\nExpend a prepared spell or spell slot. You then attempt to counteract the triggering spell with the expended spell."}, {"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": "forge-documents", "name": "Forge Documents", "category": "interaction", "traits": ["downtime", "secret"], "exploration": false, "actionType": "passive", "actions": null, "description": "You prepare forgeries that might serve as convincing props. Attempt a hard or very hard Society check.\n\nSuccess You create convincing forgeries and gain 1 EP you can use only when presenting some form of paperwork.\n\nFailure You create unconvincing documents. You gain 1 EP that (unknown to you) grants no benefit when used.\n\nCritical Failure As a failure, but a PC who tries to use the Edge Point gets a critical failure, even if they use the Edge Point after rolling a failure."}, {"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": "gain-contact", "name": "Gain Contact", "category": "interaction", "traits": ["downtime"], "exploration": false, "actionType": "passive", "actions": null, "description": "You try to make contact with an individual who can aid you in the infiltration. Attempt a normal, hard, or very hard DC Diplomacy or Society check, or a check using a Lore skill appropriate to your prospective contact.\n\nSuccess You make contact and gain 1 EP.\n\nFailure You fail to make contact.\n\nCritical Failure You insult or spook the contact in some way. Future attempts take a -2 circumstance penalty.\n\nSpecial Multiple critical failures might cause the contact to work against the PCs in some way, likely increasing the party's Awareness Points."}, {"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": "gossip", "name": "Gossip", "category": "interaction", "traits": ["downtime", "secret"], "exploration": false, "actionType": "passive", "actions": null, "description": "You seek out rumors about the infiltration's target. Attempt a normal, hard, or very hard Diplomacy check.\n\nCritical Success You gain inside information about the location or group you're trying to infiltrate. This grants you a +2 circumstance bonus to future checks you attempt for preparation activities for this infiltration. If you share this information, those you share it with also gain this bonus.\n\nSuccess You gain inside information about the place or group you're attempting to infiltrate that aids your planning.\n\nFailure You learn nothing.\n\nCritical Failure You hear a few mistaken rumors and take a -2 circumstance penalty to your next check for a preparation activity. Word spreads around that you're asking after that group or individual, increasing your Awareness Points by 1."}, {"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": "influence", "name": "Influence", "category": "interaction", "traits": ["concentrate", "linguistic"], "exploration": false, "actionType": "passive", "actions": null, "description": "You attempt to make a favorable impression on an NPC to convince the NPC to support your cause. Attempt a skill check to impress that NPC. The DC and skills which can apply can be found in the NPC's stat block.\n\nCritical Success You gain 2 Influence Points with the chosen NPC.\n\nSuccess You gain 1 Influence Point with the chosen NPC.\n\nFailure You gain no Influence Points with the chosen NPC.\n\nCritical Failure You lose 1 Influence Point with the chosen NPC."}, {"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": "map-the-area", "name": "Map the Area", "category": "interaction", "traits": ["concentrate"], "exploration": false, "actionType": "passive", "actions": null, "description": "As long as your group has successfully Reconnoitered the hex, you can use this activity to create an accurate map of the hex with a successful Survival check (typically at a trained or expert DC). When you have an accurate map of the hex, the DC of any check to navigate that hex is reduced by 2."}, {"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": "psychic-defense", "name": "Psychic Defense", "category": "defensive", "traits": ["concentrate", "magical"], "exploration": false, "actionType": "reaction", "actions": null, "description": "Trigger Your opponent uses a psychic manifestation against you.\n\nRequirements You're in a psychic duel and are psychically centered.\n\nYou gain the benefit matching your psychic center against the triggering manifestation. Then your psychic center is expended.\n\nArmor of Insight (Perception) Sensing the incoming attack, you make your mind resist psychic harm. You gain resistance equal to half your level (minimum 1) to mental damage against the triggering effect. This resistance is doubled if you're a master in Perception and tripled if you're legendary.\n\nEmpathic Orbit (Diplomacy) Displaying empathy, you sow doubt in your attacker to diminish their resolve for psychic combat. The opponent is Stupefied 1 until the end of their next turn or Stupefied 2 if you're legendary in Diplomacy.\n\nIre's Spear (Intimidation) Blazing anger surges in your mind, causing backlash to anyone who harms you. If the manifestation damages you, the opponent takes damage equal to the counteract rank of the manifestation it used against you. This damage is doubled if you're a master of Intimidation or tripled if you're legendary.\n\nRational Labyrinth (Occultism) Analytically breaking down the attack, you realign your mind's defenses into a puzzle designed to confound it. You gain a +2 status bonus to your Will save or Will DC, or a +4 status bonus if the effect has the emotion trait.\n\nSensory Phantasm (Deception) Using your guile, you send psychic illusions through your mental link to your foe, making yourself harder to pinpoint. You're Concealed against the opponent until the end of its next turn."}, {"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": "recenter", "name": "Recenter", "category": "offensive", "traits": ["concentrate"], "exploration": false, "actionType": "action", "actions": 1, "description": "Requirements You're in a psychic duel and are trained in the statistic matching the psychic center you are changing to.\n\nYou adopt the psychic center matching the emotion of your choice. If you were already psychically centered, you lose your old psychic center when you adopt the new one."}, {"slug": "reconnoiter", "name": "Reconnoiter", "category": "interaction", "traits": ["concentrate"], "exploration": false, "actionType": "passive", "actions": null, "description": "You spend time surveying and exploring a specific area, getting the lay of the land and looking for unusual features and specific sites. Reconnoitering a single hex takes a number of hexploration activities equal to the number of Travel activities necessary to traverse the hex-1 for open terrain, 2 for difficult terrain, and 3 for greater difficult terrain. Traveling on roads doesn't lessen the time required to Reconnoiter. Once the hex has been Reconnoitered, you can Map the Area to reduce your chance of getting lost in that hex. You automatically find any special feature that doesn't require a check to find, and you attempt the appropriate checks to find hidden special features.\n\nFor instance, if you were looking for an obvious rock formation among some hills, you would spend 2 hexploration activities to Reconnoiter the hex, and you'd find the rock formation. But if you were looking for a hidden tengu monastery somewhere in some deep forests, after spending 2 activities to Reconnoiter the forest hex, you would have to succeed at a Perception check as part of your Reconnoiter activity to find the monastery."}, {"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": "scout-location", "name": "Scout Location", "category": "interaction", "traits": ["downtime", "secret"], "exploration": false, "actionType": "passive", "actions": null, "description": "You spend time observing the place or group you wish to infiltrate. Attempt a normal, hard, or very hard DC Perception, Society or Stealth check.\n\nSuccess You make observations that provide 1 EP.\n\nFailure You learn nothing particularly noteworthy.\n\nCritical Failure You misjudge some aspect of what you observed, gaining 1 EP that results in a critical failure instead of a success when used, even if a PC uses the Edge Point after rolling a failure."}, {"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": "secure-disguises", "name": "Secure Disguises", "category": "interaction", "traits": ["downtime"], "exploration": false, "actionType": "passive", "actions": null, "description": "You seek to procure or create disguises. Attempt a normal, hard, or very hard Crafting, Deception, Performance, or Society check.\n\nSuccess You procure or creates disguises, gaining 1 EP that can be used only to maintain a cover identity.\n\nFailure Your efforts result in an unusable disguise."}, {"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": "sense-weakness", "name": "Sense Weakness", "category": "interaction", "traits": [], "exploration": false, "actionType": "reaction", "actions": null, "description": "Trigger You attempt a melee Strike against your opponent, but haven't rolled yet.\n\nRequirements You're in a duel, you're trained in Perception, and you rolled a Perception check for initiative this round.\n\nYou pick a precise moment to attack, giving you an edge. Your opponent is Off Guard against the attack. If your opponent is using Deception for initiative when this ability is used, they are instead off-guard until the start of their next turn."}, {"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": "tap-ley-line", "name": "Tap Ley Line", "category": "interaction", "traits": ["concentrate"], "exploration": false, "actionType": "action", "actions": 1, "description": "You attempt to manipulate the magical essence of a ley line that you're aware of within 30 feet. The GM determines the DC based on the hard DC for the ley line's level.\n\nCritical Success You gain the ley line's benefits until the end of your next turn.\n\nSuccess You gain the ley line's benefits until the end of your turn.\n\nFailure You take mental damage equal to 1d6 × the ley line's level. You can't Tap the Ley Line again for 1 hour.\n\nCritical Failure As failure, and you're subject to the ley line's backlash effect. You can't Tap the Ley Line again for 24 hours."}, {"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": "travel", "name": "Travel", "category": "interaction", "traits": ["move"], "exploration": false, "actionType": "passive", "actions": null, "description": "You progress toward moving into an adjacent hex. In open terrain, like a plain, using 1 Travel activity allows you to move from one hex to an adjacent hex. Traversing a hex with difficult terrain (such as a typical forest or desert) requires 2 Travel activities, and hexes of greater difficult terrain (such as a steep mountain or typical swamp) require 3 Travel activities to traverse. Traveling along a road uses a terrain type one step better than the surrounding terrain. For example, if you are traveling on a road over a mountain pass, the terrain is difficult terrain instead of greater difficult terrain.\n\nThe Travel activity assumes you are walking overland. If you are flying or traveling on water, most hexes are open terrain, though there are exceptions. Flying into storms or high winds count as difficult or greater difficult terrain. Traveling down a river is open terrain, but traveling upriver is difficult or greater difficult terrain."}, {"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."}];
|
|
364
|
+
|
|
365
|
+
</script>
|
|
366
|
+
<script>
|
|
367
|
+
/* ============================================================
|
|
368
|
+
src/content.js — the app's own configuration.
|
|
369
|
+
|
|
370
|
+
Every word of rules PROSE comes from data/subsystems.generated.js and
|
|
371
|
+
data/reference.generated.js, which are built from the open-source Foundry
|
|
372
|
+
pf2e data so they can't drift from GM Core. This file is the other half:
|
|
373
|
+
the machine-readable shape of each subsystem — its point pools, how a
|
|
374
|
+
degree of success moves them, and the defaults a GM can override.
|
|
375
|
+
|
|
376
|
+
engine.js only renders what's here. Adding a sixth subsystem means adding
|
|
377
|
+
an entry to SUBSYSTEMS, not writing a new screen.
|
|
378
|
+
============================================================ */
|
|
379
|
+
|
|
380
|
+
/* The four outcomes, in the order they appear on every button row. */
|
|
381
|
+
const DEGREES = [
|
|
382
|
+
{ key: "critSuccess", label: "Critical success", short: "Crit succ", tone: "heal" },
|
|
383
|
+
{ key: "success", label: "Success", short: "Success", tone: "ok" },
|
|
384
|
+
{ key: "failure", label: "Failure", short: "Failure", tone: "warn" },
|
|
385
|
+
{ key: "critFailure", label: "Critical failure", short: "Crit fail", tone: "harm" },
|
|
386
|
+
];
|
|
387
|
+
|
|
388
|
+
/* Where the rules say "the GM decides", the app asks instead of choosing.
|
|
389
|
+
These strings surface in the prep editor next to the field in question. */
|
|
390
|
+
const GM_CALLS = {
|
|
391
|
+
obstacleCost:
|
|
392
|
+
"How many points an obstacle needs is yours to set. The default follows " +
|
|
393
|
+
"GM Core's rule of thumb; raise it for a particularly demanding obstacle.",
|
|
394
|
+
awarenessFail:
|
|
395
|
+
"When the infiltration fails is yours to set. The default is twice the " +
|
|
396
|
+
"Infiltration Points the job needs, with a complication at each quarter.",
|
|
397
|
+
roundLength:
|
|
398
|
+
"A round is however long you say it is — the book gives a range, not a number.",
|
|
399
|
+
thresholds:
|
|
400
|
+
"Thresholds and what reaching one grants are entirely yours; the book " +
|
|
401
|
+
"gives scales, not contents.",
|
|
402
|
+
thresholdProgress:
|
|
403
|
+
"Counted in obstacles cleared, not points — the points reset at every " +
|
|
404
|
+
"obstacle, so they are not what measures progress across the whole run.",
|
|
405
|
+
researchCap:
|
|
406
|
+
"Each research check has a maximum RP. Past it the PCs learn nothing new " +
|
|
407
|
+
"from that source and have to go looking elsewhere.",
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
const SUBSYSTEMS = {
|
|
411
|
+
/* ---------------------------------------------------------- */
|
|
412
|
+
chase: {
|
|
413
|
+
key: "chase",
|
|
414
|
+
name: "Chase",
|
|
415
|
+
glyph: "»",
|
|
416
|
+
rulesPage: "chases",
|
|
417
|
+
blurb: "A run of obstacles, one action per PC per round. The group's " +
|
|
418
|
+
"points clear the obstacle in front of them — and the extras " +
|
|
419
|
+
"don't carry over.",
|
|
420
|
+
pools: [{ key: "cp", name: "Chase Points", abbr: "CP", min: 0, role: "goal" }],
|
|
421
|
+
scale: {
|
|
422
|
+
critSuccess: { cp: 2 },
|
|
423
|
+
success: { cp: 1 },
|
|
424
|
+
failure: {},
|
|
425
|
+
critFailure: { cp: -1 },
|
|
426
|
+
},
|
|
427
|
+
structure: "sequence",
|
|
428
|
+
/* Chase Points reset at every obstacle, so a threshold counted in points
|
|
429
|
+
would be meaningless. What advances across a chase is obstacles cleared. */
|
|
430
|
+
thresholdBasis: "progress",
|
|
431
|
+
/* GM Core: half the obstacles need one fewer point than the party size,
|
|
432
|
+
half need two fewer, minimum 1. */
|
|
433
|
+
obstacleCost: { base: "party", offsets: [1, 2], min: 1 },
|
|
434
|
+
presets: [
|
|
435
|
+
{ key: "short", label: "Short", obstacles: 6, note: "about 10–20 minutes of game time" },
|
|
436
|
+
{ key: "medium", label: "Medium", obstacles: 8, note: "about 15–25 minutes" },
|
|
437
|
+
{ key: "long", label: "Long", obstacles: 10, note: "about 20–30 minutes" },
|
|
438
|
+
],
|
|
439
|
+
extras: [
|
|
440
|
+
{ key: "cantAct", label: "Couldn't act", deltas: { cp: -1 },
|
|
441
|
+
note: "A PC who passes their turn or can't act costs the group 1 CP." },
|
|
442
|
+
{ key: "bypass", label: "Bypassed it", deltas: { cp: 1 },
|
|
443
|
+
note: "An action that gets past the obstacle without a check — a spell, " +
|
|
444
|
+
"usually 1 CP, or 2 if it was exceptionally helpful." },
|
|
445
|
+
],
|
|
446
|
+
obstacleLibrary: true,
|
|
447
|
+
asks: ["obstacleCost", "thresholdProgress"],
|
|
448
|
+
},
|
|
449
|
+
|
|
450
|
+
/* ---------------------------------------------------------- */
|
|
451
|
+
infiltration: {
|
|
452
|
+
key: "infiltration",
|
|
453
|
+
name: "Infiltration",
|
|
454
|
+
glyph: "◑",
|
|
455
|
+
rulesPage: "infiltration",
|
|
456
|
+
blurb: "Two pools running against each other: points toward the job, and " +
|
|
457
|
+
"the opposition slowly noticing you. A failed check costs you both.",
|
|
458
|
+
pools: [
|
|
459
|
+
{ key: "ip", name: "Infiltration Points", abbr: "IP", min: 0, role: "goal" },
|
|
460
|
+
{ key: "ap", name: "Awareness Points", abbr: "AP", min: 0, role: "hazard" },
|
|
461
|
+
],
|
|
462
|
+
scale: {
|
|
463
|
+
critSuccess: { ip: 2 },
|
|
464
|
+
success: { ip: 1 },
|
|
465
|
+
failure: { ap: 1 },
|
|
466
|
+
critFailure: { ap: 2 },
|
|
467
|
+
},
|
|
468
|
+
/* The one everybody forgets at the table. */
|
|
469
|
+
roundEnd: { ap: 1 },
|
|
470
|
+
roundEndNote: "Each infiltration round that passes adds 1 Awareness Point.",
|
|
471
|
+
structure: "sequence",
|
|
472
|
+
/* As with a chase: progress is obstacles, not the points inside one. The
|
|
473
|
+
ladder that matters here is Awareness, and that one is derived. */
|
|
474
|
+
thresholdBasis: "progress",
|
|
475
|
+
obstacleModes: [
|
|
476
|
+
{ key: "group", label: "Group", note: "The party pools its points; cleared once for everyone." },
|
|
477
|
+
{ key: "individual", label: "Individual", note: "Every PC has to earn the points separately." },
|
|
478
|
+
],
|
|
479
|
+
/* Failure at twice the IP the job needs, with a complication each quarter. */
|
|
480
|
+
awarenessRule: { multiplier: 2, steps: 4 },
|
|
481
|
+
edge: {
|
|
482
|
+
key: "ep", name: "Edge Points", abbr: "EP",
|
|
483
|
+
note: "Earned by preparation activities beforehand. Spend 1 to turn a " +
|
|
484
|
+
"failure or critical failure into a success — no points to the " +
|
|
485
|
+
"opposition, 1 IP to you.",
|
|
486
|
+
},
|
|
487
|
+
prepActivities: [
|
|
488
|
+
{ slug: "bribe-contact", name: "Bribe Contact" },
|
|
489
|
+
{ slug: "forge-documents", name: "Forge Documents" },
|
|
490
|
+
{ slug: "gain-contact", name: "Gain Contact" },
|
|
491
|
+
{ slug: "gossip", name: "Gossip" },
|
|
492
|
+
{ slug: "scout-location", name: "Scout Location" },
|
|
493
|
+
{ slug: "secure-disguises", name: "Secure Disguises" },
|
|
494
|
+
],
|
|
495
|
+
extras: [
|
|
496
|
+
{ key: "helped", label: "Helped without a check", deltas: { ip: 1 },
|
|
497
|
+
note: "A spell or action that just works — usually 1 IP, 2 if it was " +
|
|
498
|
+
"particularly helpful." },
|
|
499
|
+
{ key: "noticed", label: "Something went wrong", deltas: { ap: 1 },
|
|
500
|
+
note: "Anything disruptive the rules don't price. Your call." },
|
|
501
|
+
],
|
|
502
|
+
asks: ["awarenessFail", "obstacleCost", "thresholdProgress"],
|
|
503
|
+
},
|
|
504
|
+
|
|
505
|
+
/* ---------------------------------------------------------- */
|
|
506
|
+
influence: {
|
|
507
|
+
key: "influence",
|
|
508
|
+
name: "Influence",
|
|
509
|
+
glyph: "◇",
|
|
510
|
+
rulesPage: "influence",
|
|
511
|
+
blurb: "A social encounter in rounds. Each PC either works on the NPC or " +
|
|
512
|
+
"works out what the NPC actually responds to.",
|
|
513
|
+
pools: [{ key: "ip", name: "Influence Points", abbr: "IP", min: 0, role: "goal" }],
|
|
514
|
+
scale: {
|
|
515
|
+
critSuccess: { ip: 2 },
|
|
516
|
+
success: { ip: 1 },
|
|
517
|
+
failure: {},
|
|
518
|
+
critFailure: { ip: -1 },
|
|
519
|
+
},
|
|
520
|
+
structure: "single",
|
|
521
|
+
roundLength: { low: "15 minutes", high: "an hour" },
|
|
522
|
+
/* Each PC acts once per round, choosing one of these. */
|
|
523
|
+
actions: [
|
|
524
|
+
{ key: "influence", name: "Influence", slug: "influence", scored: true },
|
|
525
|
+
{ key: "discover", name: "Discover", slug: "discover", scored: false,
|
|
526
|
+
/* How many facts a Discover reveals. -1 means the GM feeds them a
|
|
527
|
+
plausible lie, which the app records but does not invent. */
|
|
528
|
+
reveals: { critSuccess: 2, success: 1, failure: 0, critFailure: -1 } },
|
|
529
|
+
],
|
|
530
|
+
discoverNote:
|
|
531
|
+
"On a critical failure the PC learns something incorrect. The app marks " +
|
|
532
|
+
"the round; what the lie is, is yours.",
|
|
533
|
+
asks: ["roundLength", "thresholds"],
|
|
534
|
+
},
|
|
535
|
+
|
|
536
|
+
/* ---------------------------------------------------------- */
|
|
537
|
+
research: {
|
|
538
|
+
key: "research",
|
|
539
|
+
name: "Research",
|
|
540
|
+
glyph: "❖",
|
|
541
|
+
rulesPage: "research",
|
|
542
|
+
blurb: "Points against thresholds that hand over information — with each " +
|
|
543
|
+
"source drying up once it has given all it has.",
|
|
544
|
+
pools: [{ key: "rp", name: "Research Points", abbr: "RP", min: 0, role: "goal" }],
|
|
545
|
+
scale: {
|
|
546
|
+
critSuccess: { rp: 2 },
|
|
547
|
+
success: { rp: 1 },
|
|
548
|
+
failure: {},
|
|
549
|
+
critFailure: { rp: -1 },
|
|
550
|
+
},
|
|
551
|
+
structure: "checks",
|
|
552
|
+
roundLength: { low: "10 minutes", high: "a full day" },
|
|
553
|
+
checkNote:
|
|
554
|
+
"List the skills for a source lowest DC first — that's the order the " +
|
|
555
|
+
"book uses, and it reads as a hint to the players.",
|
|
556
|
+
asks: ["researchCap", "roundLength", "thresholds"],
|
|
557
|
+
},
|
|
558
|
+
|
|
559
|
+
/* ---------------------------------------------------------- */
|
|
560
|
+
custom: {
|
|
561
|
+
key: "custom",
|
|
562
|
+
name: "Victory Points",
|
|
563
|
+
glyph: "◆",
|
|
564
|
+
rulesPage: "victory-points",
|
|
565
|
+
blurb: "The bare framework the other four are built on. Name your points, " +
|
|
566
|
+
"set your thresholds, and run anything — reputation, a duel, a " +
|
|
567
|
+
"hexploration, a race against a rival crew.",
|
|
568
|
+
pools: [{ key: "vp", name: "Victory Points", abbr: "VP", min: 0, role: "goal" }],
|
|
569
|
+
structure: "single",
|
|
570
|
+
renameable: true,
|
|
571
|
+
/* The two structures GM Core defines. The scale comes from the mode. */
|
|
572
|
+
modes: [
|
|
573
|
+
{ key: "accumulating", label: "Accumulating",
|
|
574
|
+
note: "Start at nothing and climb toward the end point.",
|
|
575
|
+
scale: {
|
|
576
|
+
critSuccess: { vp: 2 },
|
|
577
|
+
success: { vp: 1 },
|
|
578
|
+
failure: {},
|
|
579
|
+
critFailure: { vp: -1 },
|
|
580
|
+
} },
|
|
581
|
+
{ key: "diminishing", label: "Diminishing",
|
|
582
|
+
note: "Start at the end point and try not to lose it. On a critical " +
|
|
583
|
+
"success the PCs gain a point only if regaining ground is " +
|
|
584
|
+
"possible — otherwise treat it as a success.",
|
|
585
|
+
startAtGoal: true,
|
|
586
|
+
scale: {
|
|
587
|
+
critSuccess: { vp: 1 },
|
|
588
|
+
success: {},
|
|
589
|
+
failure: { vp: -1 },
|
|
590
|
+
critFailure: { vp: -2 },
|
|
591
|
+
} },
|
|
592
|
+
],
|
|
593
|
+
/* Presets come from the VP Scales table in the generated data. */
|
|
594
|
+
scalePresets: true,
|
|
595
|
+
asks: ["thresholds"],
|
|
596
|
+
},
|
|
597
|
+
};
|
|
598
|
+
|
|
599
|
+
/* The order they appear in the "new encounter" picker. */
|
|
600
|
+
const SUBSYSTEM_ORDER = ["chase", "infiltration", "influence", "research", "custom"];
|
|
601
|
+
|
|
602
|
+
</script>
|
|
603
|
+
<script>
|
|
604
|
+
/* ============================================================
|
|
605
|
+
src/engine.js — data-driven runner for the GM Core subsystems.
|
|
606
|
+
|
|
607
|
+
One Victory Point core (apply deltas, clamp, log, fire thresholds, advance
|
|
608
|
+
the round) with three structural layers on top of it: a sequence of
|
|
609
|
+
obstacles, a single open pool, or a set of capped research checks. Which
|
|
610
|
+
layer a subsystem uses, and what its numbers are, is declared in
|
|
611
|
+
content.js — nothing below hard-codes a subsystem's arithmetic.
|
|
612
|
+
============================================================ */
|
|
613
|
+
|
|
614
|
+
const KEY = "pf2eSubsystems.v1";
|
|
615
|
+
const LOG_CAP = 200;
|
|
616
|
+
|
|
617
|
+
let library = null;
|
|
618
|
+
|
|
619
|
+
/* ============================================================
|
|
620
|
+
SMALL HELPERS
|
|
621
|
+
============================================================ */
|
|
622
|
+
const byId = (id) => document.getElementById(id);
|
|
623
|
+
const clone = (o) => JSON.parse(JSON.stringify(o));
|
|
624
|
+
|
|
625
|
+
function esc(s) {
|
|
626
|
+
return String(s == null ? "" : s).replace(/[&<>"']/g, (c) => (
|
|
627
|
+
{ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c]));
|
|
628
|
+
}
|
|
629
|
+
function uid() {
|
|
630
|
+
return Date.now().toString(36) + Math.random().toString(36).slice(2, 7);
|
|
631
|
+
}
|
|
632
|
+
function b64encode(str) { return btoa(unescape(encodeURIComponent(str))); }
|
|
633
|
+
function b64decode(str) { return decodeURIComponent(escape(atob(str))); }
|
|
634
|
+
|
|
635
|
+
function toast(msg) {
|
|
636
|
+
const t = byId("toast");
|
|
637
|
+
t.textContent = msg;
|
|
638
|
+
t.classList.add("show");
|
|
639
|
+
clearTimeout(t._t);
|
|
640
|
+
t._t = setTimeout(() => t.classList.remove("show"), 1800);
|
|
641
|
+
}
|
|
642
|
+
function titleCase(s) {
|
|
643
|
+
return String(s || "").replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
644
|
+
}
|
|
645
|
+
function plural(n, one, many) { return n === 1 ? one : (many || one + "s"); }
|
|
646
|
+
|
|
647
|
+
/* ============================================================
|
|
648
|
+
PERSISTENCE
|
|
649
|
+
============================================================ */
|
|
650
|
+
function blankLibrary() {
|
|
651
|
+
return { v: 1, settings: { themeMode: "auto", custom: null }, encounters: [], activeId: null };
|
|
652
|
+
}
|
|
653
|
+
function load() {
|
|
654
|
+
try {
|
|
655
|
+
const raw = localStorage.getItem(KEY);
|
|
656
|
+
library = raw ? JSON.parse(raw) : blankLibrary();
|
|
657
|
+
} catch (e) {
|
|
658
|
+
library = blankLibrary();
|
|
659
|
+
}
|
|
660
|
+
if (!library || typeof library !== "object") library = blankLibrary();
|
|
661
|
+
library.settings = library.settings || { themeMode: "auto", custom: null };
|
|
662
|
+
library.encounters = Array.isArray(library.encounters) ? library.encounters : [];
|
|
663
|
+
}
|
|
664
|
+
function save() {
|
|
665
|
+
try {
|
|
666
|
+
localStorage.setItem(KEY, JSON.stringify(library));
|
|
667
|
+
} catch (e) {
|
|
668
|
+
toast("Couldn't save — this browser's storage is full or blocked.");
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/* ============================================================
|
|
673
|
+
THEME
|
|
674
|
+
============================================================ */
|
|
675
|
+
function applyTheme() {
|
|
676
|
+
const s = library.settings;
|
|
677
|
+
let mode = s.themeMode || "auto";
|
|
678
|
+
if (mode !== "light" && mode !== "dark") {
|
|
679
|
+
mode = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
680
|
+
? "dark" : "light";
|
|
681
|
+
}
|
|
682
|
+
document.documentElement.dataset.theme = mode;
|
|
683
|
+
if (s.custom && s.custom.accent) {
|
|
684
|
+
document.documentElement.style.setProperty("--accent", s.custom.accent);
|
|
685
|
+
} else {
|
|
686
|
+
document.documentElement.style.removeProperty("--accent");
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
function setThemeMode(m) { library.settings.themeMode = m; save(); applyTheme(); renderMenu(); }
|
|
690
|
+
function setAccent(v) {
|
|
691
|
+
library.settings.custom = library.settings.custom || {};
|
|
692
|
+
library.settings.custom.accent = v;
|
|
693
|
+
save(); applyTheme();
|
|
694
|
+
}
|
|
695
|
+
function resetTheme() { library.settings.custom = null; save(); applyTheme(); renderMenu(); }
|
|
696
|
+
|
|
697
|
+
/* ============================================================
|
|
698
|
+
GENERATED DATA LOOKUPS
|
|
699
|
+
============================================================ */
|
|
700
|
+
const SUB_PAGES = {};
|
|
701
|
+
(GENERATED_SUBSYSTEMS || []).forEach((p) => { SUB_PAGES[p.slug] = p; });
|
|
702
|
+
|
|
703
|
+
const ACTIONS_BY_SLUG = {};
|
|
704
|
+
(typeof GENERATED_ACTIONS !== "undefined" ? GENERATED_ACTIONS : []).forEach((a) => {
|
|
705
|
+
ACTIONS_BY_SLUG[a.slug] = a;
|
|
706
|
+
});
|
|
707
|
+
const CONDITIONS = typeof GENERATED_CONDITIONS !== "undefined" ? GENERATED_CONDITIONS : [];
|
|
708
|
+
const ACTIONS = typeof GENERATED_ACTIONS !== "undefined" ? GENERATED_ACTIONS : [];
|
|
709
|
+
|
|
710
|
+
/* ============================================================
|
|
711
|
+
THE ENCOUNTER MODEL
|
|
712
|
+
|
|
713
|
+
`def` is the prep: what you build before the session and what an export
|
|
714
|
+
code carries. `run` is the live state: pools, round, progress, log.
|
|
715
|
+
Keeping them apart is what lets "Reset run" hand you the same prepped
|
|
716
|
+
chase back, and what keeps a shared code free of last week's dice.
|
|
717
|
+
============================================================ */
|
|
718
|
+
function subOf(enc) { return SUBSYSTEMS[enc.subsystem]; }
|
|
719
|
+
|
|
720
|
+
/* The custom subsystem lets the GM rename its pool, so pools are read from
|
|
721
|
+
the encounter rather than straight off the definition. */
|
|
722
|
+
function poolsOf(enc) {
|
|
723
|
+
const sub = subOf(enc);
|
|
724
|
+
if (sub.renameable && enc.def.pointName) {
|
|
725
|
+
return [Object.assign({}, sub.pools[0], {
|
|
726
|
+
name: enc.def.pointName,
|
|
727
|
+
abbr: enc.def.pointAbbr || sub.pools[0].abbr,
|
|
728
|
+
})];
|
|
729
|
+
}
|
|
730
|
+
return sub.pools;
|
|
731
|
+
}
|
|
732
|
+
function scaleOf(enc) {
|
|
733
|
+
const sub = subOf(enc);
|
|
734
|
+
if (sub.modes) {
|
|
735
|
+
const m = sub.modes.find((x) => x.key === enc.def.mode) || sub.modes[0];
|
|
736
|
+
return m.scale;
|
|
737
|
+
}
|
|
738
|
+
return sub.scale;
|
|
739
|
+
}
|
|
740
|
+
function modeOf(enc) {
|
|
741
|
+
const sub = subOf(enc);
|
|
742
|
+
if (!sub.modes) return null;
|
|
743
|
+
return sub.modes.find((x) => x.key === enc.def.mode) || sub.modes[0];
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
function newObstacle(name, cost, opts) {
|
|
747
|
+
return Object.assign({
|
|
748
|
+
id: uid(), name: name || "", note: "", cost: cost || 1, mode: "group", options: [],
|
|
749
|
+
}, opts || {});
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/* GM Core's rule of thumb: half the obstacles need one fewer point than the
|
|
753
|
+
party size, half need two fewer, minimum 1. */
|
|
754
|
+
function suggestedCost(rule, partySize, index) {
|
|
755
|
+
if (!rule) return 1;
|
|
756
|
+
const offset = rule.offsets[index % rule.offsets.length];
|
|
757
|
+
return Math.max(rule.min, partySize - offset);
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
function newEncounter(subKey, opts) {
|
|
761
|
+
opts = opts || {};
|
|
762
|
+
const sub = SUBSYSTEMS[subKey];
|
|
763
|
+
const partySize = opts.partySize || 4;
|
|
764
|
+
const def = {
|
|
765
|
+
participants: opts.participants
|
|
766
|
+
|| Array.from({ length: partySize }, (_, i) => "PC " + (i + 1)),
|
|
767
|
+
thresholds: [],
|
|
768
|
+
note: "",
|
|
769
|
+
};
|
|
770
|
+
|
|
771
|
+
if (subKey === "chase") {
|
|
772
|
+
const preset = sub.presets.find((p) => p.key === (opts.preset || "medium"));
|
|
773
|
+
def.obstacles = Array.from({ length: preset.obstacles }, (_, i) =>
|
|
774
|
+
newObstacle("Obstacle " + (i + 1),
|
|
775
|
+
suggestedCost(sub.obstacleCost, partySize, i)));
|
|
776
|
+
def.presetKey = preset.key;
|
|
777
|
+
} else if (subKey === "infiltration") {
|
|
778
|
+
def.obstacles = [newObstacle("First obstacle", 2), newObstacle("Second obstacle", 2)];
|
|
779
|
+
def.edgeStart = 0;
|
|
780
|
+
def.apFail = null; // null = follow awarenessRule
|
|
781
|
+
} else if (subKey === "influence") {
|
|
782
|
+
def.npc = {
|
|
783
|
+
name: opts.npcName || "The NPC",
|
|
784
|
+
discoveryDC: 15,
|
|
785
|
+
skills: [{ skill: "Diplomacy", dc: 18 }],
|
|
786
|
+
resistances: [],
|
|
787
|
+
weaknesses: [],
|
|
788
|
+
};
|
|
789
|
+
def.roundMinutes = "30 minutes";
|
|
790
|
+
} else if (subKey === "research") {
|
|
791
|
+
def.checks = [{ id: uid(), name: "First source", skill: "Society", dc: 15, cap: 3 }];
|
|
792
|
+
def.roundLabel = "1 hour";
|
|
793
|
+
} else if (subKey === "custom") {
|
|
794
|
+
const preset = opts.scale || null;
|
|
795
|
+
def.mode = opts.mode || "accumulating";
|
|
796
|
+
def.pointName = opts.pointName || "Victory Points";
|
|
797
|
+
def.pointAbbr = opts.pointAbbr || "VP";
|
|
798
|
+
def.goal = preset ? goalFromScale(preset) : 10;
|
|
799
|
+
if (preset) {
|
|
800
|
+
def.thresholds = preset.thresholds.map((at) => ({ at, label: "", effect: "" }));
|
|
801
|
+
def.scaleLabel = preset.duration;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
const enc = {
|
|
806
|
+
id: uid(),
|
|
807
|
+
subsystem: subKey,
|
|
808
|
+
name: opts.name || sub.name,
|
|
809
|
+
createdAt: new Date().toISOString(),
|
|
810
|
+
def,
|
|
811
|
+
run: null,
|
|
812
|
+
};
|
|
813
|
+
enc.run = freshRun(enc);
|
|
814
|
+
return enc;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/* "3-5" / "25-50" -> a number to aim at. The book gives a range; take the top
|
|
818
|
+
of it and let the GM change it. */
|
|
819
|
+
function goalFromScale(preset) {
|
|
820
|
+
const nums = String(preset.endPoint).match(/\d+/g) || ["10"];
|
|
821
|
+
return parseInt(nums[nums.length - 1], 10);
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
function freshRun(enc) {
|
|
825
|
+
const run = { pools: {}, round: 1, obstacleIndex: 0, perPC: {}, checks: {}, revealed: [], fired: [], log: [] };
|
|
826
|
+
poolsOf(enc).forEach((p) => { run.pools[p.key] = 0; });
|
|
827
|
+
const mode = modeOf(enc);
|
|
828
|
+
if (mode && mode.startAtGoal) run.pools[poolsOf(enc)[0].key] = enc.def.goal || 0;
|
|
829
|
+
if (subOf(enc).edge) run.pools.ep = enc.def.edgeStart || 0;
|
|
830
|
+
return run;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
function activeEnc() {
|
|
834
|
+
if (!library.activeId) return null;
|
|
835
|
+
return library.encounters.find((e) => e.id === library.activeId) || null;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/* ============================================================
|
|
839
|
+
THE VICTORY POINT CORE
|
|
840
|
+
|
|
841
|
+
Every mutation goes through act(). It snapshots the run first, so undo is
|
|
842
|
+
an exact restore rather than an attempt to re-derive the inverse — which
|
|
843
|
+
matters most at an obstacle boundary, where advancing discards points and
|
|
844
|
+
no arithmetic could put them back.
|
|
845
|
+
============================================================ */
|
|
846
|
+
function act(enc, entry, mutate) {
|
|
847
|
+
const before = clone(enc.run);
|
|
848
|
+
delete before.log;
|
|
849
|
+
mutate();
|
|
850
|
+
enc.run.log.push(Object.assign({
|
|
851
|
+
id: uid(),
|
|
852
|
+
at: new Date().toISOString(),
|
|
853
|
+
round: enc.run.round,
|
|
854
|
+
before,
|
|
855
|
+
}, entry));
|
|
856
|
+
if (enc.run.log.length > LOG_CAP) enc.run.log.splice(0, enc.run.log.length - LOG_CAP);
|
|
857
|
+
save();
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
function addDeltas(enc, deltas) {
|
|
861
|
+
const pools = poolsOf(enc);
|
|
862
|
+
Object.keys(deltas || {}).forEach((k) => {
|
|
863
|
+
const pool = pools.find((p) => p.key === k);
|
|
864
|
+
const min = pool ? (pool.min != null ? pool.min : 0) : 0;
|
|
865
|
+
const next = (enc.run.pools[k] || 0) + deltas[k];
|
|
866
|
+
enc.run.pools[k] = Math.max(min, next);
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
/* What a threshold is counted against. In a sequence the points reset at every
|
|
871
|
+
obstacle, so progress is the obstacle count; everywhere else it is the pool. */
|
|
872
|
+
function thresholdValue(enc) {
|
|
873
|
+
return subOf(enc).thresholdBasis === "progress"
|
|
874
|
+
? enc.run.obstacleIndex
|
|
875
|
+
: (enc.run.pools[poolsOf(enc)[0].key] || 0);
|
|
876
|
+
}
|
|
877
|
+
function thresholdUnit(enc) {
|
|
878
|
+
return subOf(enc).thresholdBasis === "progress" ? "obstacles cleared" : poolsOf(enc)[0].name;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
/* Thresholds fire once, in order, the first time the count reaches them. */
|
|
882
|
+
function checkThresholds(enc) {
|
|
883
|
+
const value = thresholdValue(enc);
|
|
884
|
+
const hits = [];
|
|
885
|
+
(enc.def.thresholds || []).slice()
|
|
886
|
+
.sort((a, b) => a.at - b.at)
|
|
887
|
+
.forEach((t, i) => {
|
|
888
|
+
const id = "t" + i + ":" + t.at;
|
|
889
|
+
if (value >= t.at && enc.run.fired.indexOf(id) === -1) {
|
|
890
|
+
enc.run.fired.push(id);
|
|
891
|
+
hits.push(t);
|
|
892
|
+
}
|
|
893
|
+
});
|
|
894
|
+
return hits;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
/* Awareness thresholds are derived, not stored: a complication at each
|
|
898
|
+
quarter of the failure point, which itself defaults to twice the IP goal. */
|
|
899
|
+
function awarenessLadder(enc) {
|
|
900
|
+
const sub = subOf(enc);
|
|
901
|
+
if (!sub.awarenessRule) return null;
|
|
902
|
+
const goal = ipGoal(enc);
|
|
903
|
+
const fail = enc.def.apFail || goal * sub.awarenessRule.multiplier;
|
|
904
|
+
const step = Math.max(1, Math.round(fail / sub.awarenessRule.steps));
|
|
905
|
+
const marks = [];
|
|
906
|
+
for (let v = step; v < fail; v += step) marks.push(v);
|
|
907
|
+
return { fail, step, marks };
|
|
908
|
+
}
|
|
909
|
+
/* A resistance raises the DC of the approach it names; a weakness lowers it.
|
|
910
|
+
Which approach maps to which skill is the table's call, so the app shows the
|
|
911
|
+
arithmetic beside the base DC rather than guessing at it. */
|
|
912
|
+
function adjustedDC(baseDC, mods) {
|
|
913
|
+
return (mods || []).reduce((dc, m) => dc + (m.dc || 0), baseDC);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
function ipGoal(enc) {
|
|
917
|
+
return (enc.def.obstacles || []).reduce((sum, o) => sum + (o.cost || 0)
|
|
918
|
+
* (o.mode === "individual" ? (enc.def.participants || []).length : 1), 0);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
/* ---- the actions a GM takes at the table ---- */
|
|
922
|
+
|
|
923
|
+
function score(encId, actor, degree, opts) {
|
|
924
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
925
|
+
if (!enc) return;
|
|
926
|
+
opts = opts || {};
|
|
927
|
+
const scale = scaleOf(enc);
|
|
928
|
+
let deltas = scale[degree] || {};
|
|
929
|
+
let label = (DEGREES.find((d) => d.key === degree) || {}).label || degree;
|
|
930
|
+
|
|
931
|
+
/* An Edge Point turns a failure into a success: the success line of the
|
|
932
|
+
scale, and nothing to the opposition. */
|
|
933
|
+
if (opts.edge) {
|
|
934
|
+
if ((enc.run.pools.ep || 0) < 1) { toast("No Edge Points left."); return; }
|
|
935
|
+
deltas = Object.assign({}, scale.success, { ep: -1 });
|
|
936
|
+
label = "Edge Point spent — counts as a success";
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
act(enc, { kind: "score", actor, degree, label }, () => {
|
|
940
|
+
addDeltas(enc, deltas);
|
|
941
|
+
if (opts.edge) enc.run.pools.ep = Math.max(0, (enc.run.pools.ep || 0) - 1);
|
|
942
|
+
if (enc.def.checks && opts.checkId) bumpCheck(enc, opts.checkId, deltas);
|
|
943
|
+
if (subOf(enc).structure === "sequence") creditObstacle(enc, actor, deltas);
|
|
944
|
+
checkThresholds(enc);
|
|
945
|
+
});
|
|
946
|
+
renderAll();
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
/* Research: a source stops paying out once it has given its maximum. */
|
|
950
|
+
function bumpCheck(enc, checkId, deltas) {
|
|
951
|
+
const check = (enc.def.checks || []).find((c) => c.id === checkId);
|
|
952
|
+
if (!check) return;
|
|
953
|
+
const poolKey = poolsOf(enc)[0].key;
|
|
954
|
+
const gained = deltas[poolKey] || 0;
|
|
955
|
+
const already = enc.run.checks[checkId] || 0;
|
|
956
|
+
if (gained <= 0) { enc.run.checks[checkId] = Math.max(0, already + gained); return; }
|
|
957
|
+
const room = Math.max(0, (check.cap || 0) - already);
|
|
958
|
+
const allowed = Math.min(gained, room);
|
|
959
|
+
/* addDeltas already credited the full amount; take back what the cap
|
|
960
|
+
wouldn't allow. */
|
|
961
|
+
if (allowed < gained) {
|
|
962
|
+
enc.run.pools[poolKey] = Math.max(0, (enc.run.pools[poolKey] || 0) - (gained - allowed));
|
|
963
|
+
}
|
|
964
|
+
enc.run.checks[checkId] = already + allowed;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
/* Infiltration's individual obstacles are earned per PC; group ones pool. */
|
|
968
|
+
function creditObstacle(enc, actor, deltas) {
|
|
969
|
+
const ob = currentObstacle(enc);
|
|
970
|
+
if (!ob) return;
|
|
971
|
+
const poolKey = poolsOf(enc)[0].key;
|
|
972
|
+
const gained = deltas[poolKey] || 0;
|
|
973
|
+
if (ob.mode === "individual" && actor) {
|
|
974
|
+
enc.run.perPC[ob.id] = enc.run.perPC[ob.id] || {};
|
|
975
|
+
enc.run.perPC[ob.id][actor] = Math.max(0, (enc.run.perPC[ob.id][actor] || 0) + gained);
|
|
976
|
+
}
|
|
977
|
+
maybeAdvance(enc);
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
function currentObstacle(enc) {
|
|
981
|
+
const list = enc.def.obstacles || [];
|
|
982
|
+
return list[enc.run.obstacleIndex] || null;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
function obstacleCleared(enc) {
|
|
986
|
+
const ob = currentObstacle(enc);
|
|
987
|
+
if (!ob) return false;
|
|
988
|
+
if (ob.mode === "individual") {
|
|
989
|
+
const names = (enc.def.participants || []).filter(Boolean);
|
|
990
|
+
const got = enc.run.perPC[ob.id] || {};
|
|
991
|
+
return names.length > 0 && names.every((n) => (got[n] || 0) >= ob.cost);
|
|
992
|
+
}
|
|
993
|
+
return (enc.run.pools[poolsOf(enc)[0].key] || 0) >= ob.cost;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
/* Clearing an obstacle resets the pool to zero — extra points do NOT carry
|
|
997
|
+
over to the next one. This is the rule hands get wrong most often, and it
|
|
998
|
+
is why undo is snapshot-based. */
|
|
999
|
+
function maybeAdvance(enc) {
|
|
1000
|
+
const ob = currentObstacle(enc);
|
|
1001
|
+
if (!ob || !obstacleCleared(enc)) return;
|
|
1002
|
+
const poolKey = poolsOf(enc)[0].key;
|
|
1003
|
+
if (ob.mode !== "individual") enc.run.pools[poolKey] = 0;
|
|
1004
|
+
enc.run.obstacleIndex += 1;
|
|
1005
|
+
enc.run._justCleared = ob.name || "that obstacle";
|
|
1006
|
+
checkThresholds(enc);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
function extra(encId, key) {
|
|
1010
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1011
|
+
if (!enc) return;
|
|
1012
|
+
const ex = (subOf(enc).extras || []).find((x) => x.key === key);
|
|
1013
|
+
if (!ex) return;
|
|
1014
|
+
act(enc, { kind: "extra", label: ex.label }, () => {
|
|
1015
|
+
addDeltas(enc, ex.deltas);
|
|
1016
|
+
if (subOf(enc).structure === "sequence") creditObstacle(enc, null, ex.deltas);
|
|
1017
|
+
checkThresholds(enc);
|
|
1018
|
+
});
|
|
1019
|
+
renderAll();
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
function discover(encId, actor, degree) {
|
|
1023
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1024
|
+
if (!enc) return;
|
|
1025
|
+
const action = subOf(enc).actions.find((a) => a.key === "discover");
|
|
1026
|
+
const n = action.reveals[degree];
|
|
1027
|
+
const label = n < 0 ? "Discover — learned something false"
|
|
1028
|
+
: n === 0 ? "Discover — learned nothing"
|
|
1029
|
+
: "Discover — " + n + " " + plural(n, "fact");
|
|
1030
|
+
act(enc, { kind: "discover", actor, degree, label, reveals: n }, () => {
|
|
1031
|
+
enc.run.revealHint = n;
|
|
1032
|
+
});
|
|
1033
|
+
renderAll();
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
function revealFact(encId, kind, index) {
|
|
1037
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1038
|
+
if (!enc) return;
|
|
1039
|
+
const id = kind + ":" + index;
|
|
1040
|
+
if (enc.run.revealed.indexOf(id) !== -1) return;
|
|
1041
|
+
act(enc, { kind: "reveal", label: "Revealed a " + kind }, () => {
|
|
1042
|
+
enc.run.revealed.push(id);
|
|
1043
|
+
});
|
|
1044
|
+
renderAll();
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
function advanceRound(encId) {
|
|
1048
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1049
|
+
if (!enc) return;
|
|
1050
|
+
const sub = subOf(enc);
|
|
1051
|
+
act(enc, { kind: "round", label: "Round " + (enc.run.round + 1) + " begins" }, () => {
|
|
1052
|
+
enc.run.round += 1;
|
|
1053
|
+
if (sub.roundEnd) addDeltas(enc, sub.roundEnd);
|
|
1054
|
+
checkThresholds(enc);
|
|
1055
|
+
});
|
|
1056
|
+
renderAll();
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
function adjustPool(encId, poolKey, delta) {
|
|
1060
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1061
|
+
if (!enc) return;
|
|
1062
|
+
const d = {}; d[poolKey] = delta;
|
|
1063
|
+
act(enc, { kind: "manual", label: (delta > 0 ? "+" : "") + delta + " " + poolKey.toUpperCase() }, () => {
|
|
1064
|
+
if (poolKey === "ep") enc.run.pools.ep = Math.max(0, (enc.run.pools.ep || 0) + delta);
|
|
1065
|
+
else addDeltas(enc, d);
|
|
1066
|
+
if (subOf(enc).structure === "sequence") maybeAdvance(enc);
|
|
1067
|
+
checkThresholds(enc);
|
|
1068
|
+
});
|
|
1069
|
+
renderAll();
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
function undoLast() {
|
|
1073
|
+
const enc = activeEnc();
|
|
1074
|
+
if (!enc || !enc.run.log.length) { toast("Nothing to undo."); return; }
|
|
1075
|
+
const last = enc.run.log.pop();
|
|
1076
|
+
const log = enc.run.log;
|
|
1077
|
+
enc.run = Object.assign(clone(last.before), { log });
|
|
1078
|
+
save();
|
|
1079
|
+
toast("Undid: " + last.label);
|
|
1080
|
+
renderAll();
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
function resetRun(encId) {
|
|
1084
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1085
|
+
if (!enc) return;
|
|
1086
|
+
if (!confirm("Reset the live run? The prep — obstacles, thresholds, the NPC — is kept.")) return;
|
|
1087
|
+
enc.run = freshRun(enc);
|
|
1088
|
+
save();
|
|
1089
|
+
toast("Run reset.");
|
|
1090
|
+
renderAll();
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
/* ============================================================
|
|
1094
|
+
LIBRARY ACTIONS
|
|
1095
|
+
============================================================ */
|
|
1096
|
+
function openEncounter(id) { library.activeId = id; save(); go("run"); }
|
|
1097
|
+
|
|
1098
|
+
function createEncounter(subKey, opts) {
|
|
1099
|
+
const enc = newEncounter(subKey, opts);
|
|
1100
|
+
library.encounters.unshift(enc);
|
|
1101
|
+
library.activeId = enc.id;
|
|
1102
|
+
save();
|
|
1103
|
+
go("prep");
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
function duplicateEncounter(id) {
|
|
1107
|
+
const src = library.encounters.find((e) => e.id === id);
|
|
1108
|
+
if (!src) return;
|
|
1109
|
+
const copy = clone(src);
|
|
1110
|
+
copy.id = uid();
|
|
1111
|
+
copy.name = src.name + " (copy)";
|
|
1112
|
+
copy.createdAt = new Date().toISOString();
|
|
1113
|
+
copy.run = freshRun(copy);
|
|
1114
|
+
library.encounters.unshift(copy);
|
|
1115
|
+
save();
|
|
1116
|
+
toast("Duplicated.");
|
|
1117
|
+
renderAll();
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
function deleteEncounter(id) {
|
|
1121
|
+
const enc = library.encounters.find((e) => e.id === id);
|
|
1122
|
+
if (!enc) return;
|
|
1123
|
+
if (!confirm('Delete "' + enc.name + '"? This cannot be undone.')) return;
|
|
1124
|
+
library.encounters = library.encounters.filter((e) => e.id !== id);
|
|
1125
|
+
if (library.activeId === id) library.activeId = (library.encounters[0] || {}).id || null;
|
|
1126
|
+
save();
|
|
1127
|
+
renderAll();
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
function renameEncounter(id, name) {
|
|
1131
|
+
const enc = library.encounters.find((e) => e.id === id);
|
|
1132
|
+
if (!enc) return;
|
|
1133
|
+
enc.name = name || enc.name;
|
|
1134
|
+
save();
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
/* Export carries the prep only — never the run. */
|
|
1138
|
+
function exportEncounter(id) {
|
|
1139
|
+
const enc = library.encounters.find((e) => e.id === id);
|
|
1140
|
+
if (!enc) return;
|
|
1141
|
+
const payload = { t: "pf2e-subsystem", v: 1, subsystem: enc.subsystem, name: enc.name, def: enc.def };
|
|
1142
|
+
const code = b64encode(JSON.stringify(payload));
|
|
1143
|
+
navigator.clipboard && navigator.clipboard.writeText(code)
|
|
1144
|
+
.then(() => toast("Code copied to the clipboard."))
|
|
1145
|
+
.catch(() => prompt("Copy this code:", code));
|
|
1146
|
+
if (!navigator.clipboard) prompt("Copy this code:", code);
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
function importEncounter(code) {
|
|
1150
|
+
let payload;
|
|
1151
|
+
try {
|
|
1152
|
+
payload = JSON.parse(b64decode(String(code || "").trim()));
|
|
1153
|
+
} catch (e) {
|
|
1154
|
+
toast("That doesn't look like an encounter code.");
|
|
1155
|
+
return false;
|
|
1156
|
+
}
|
|
1157
|
+
if (!payload || payload.t !== "pf2e-subsystem" || !SUBSYSTEMS[payload.subsystem]) {
|
|
1158
|
+
toast("That code isn't from this app.");
|
|
1159
|
+
return false;
|
|
1160
|
+
}
|
|
1161
|
+
const enc = {
|
|
1162
|
+
id: uid(),
|
|
1163
|
+
subsystem: payload.subsystem,
|
|
1164
|
+
name: payload.name || SUBSYSTEMS[payload.subsystem].name,
|
|
1165
|
+
createdAt: new Date().toISOString(),
|
|
1166
|
+
def: payload.def,
|
|
1167
|
+
run: null,
|
|
1168
|
+
};
|
|
1169
|
+
enc.run = freshRun(enc);
|
|
1170
|
+
library.encounters.unshift(enc);
|
|
1171
|
+
library.activeId = enc.id;
|
|
1172
|
+
save();
|
|
1173
|
+
toast("Imported.");
|
|
1174
|
+
renderAll();
|
|
1175
|
+
return true;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
/* ============================================================
|
|
1179
|
+
NAVIGATION
|
|
1180
|
+
============================================================ */
|
|
1181
|
+
const VIEWS = [
|
|
1182
|
+
{ key: "run", label: "Run" },
|
|
1183
|
+
{ key: "prep", label: "Prep" },
|
|
1184
|
+
{ key: "reference", label: "Reference" },
|
|
1185
|
+
];
|
|
1186
|
+
let current = "prep";
|
|
1187
|
+
let menuOpen = false;
|
|
1188
|
+
|
|
1189
|
+
function go(view) {
|
|
1190
|
+
current = view;
|
|
1191
|
+
menuOpen = false;
|
|
1192
|
+
renderAll();
|
|
1193
|
+
window.scrollTo(0, 0);
|
|
1194
|
+
}
|
|
1195
|
+
function openMenu() { menuOpen = true; renderAll(); }
|
|
1196
|
+
function closeMenu() { menuOpen = false; renderAll(); }
|
|
1197
|
+
|
|
1198
|
+
function renderAll() {
|
|
1199
|
+
renderHeader();
|
|
1200
|
+
renderTabs();
|
|
1201
|
+
const views = byId("views");
|
|
1202
|
+
if (menuOpen) { views.innerHTML = menuHtml(); return; }
|
|
1203
|
+
if (current === "run") views.innerHTML = runHtml();
|
|
1204
|
+
else if (current === "prep") views.innerHTML = prepHtml();
|
|
1205
|
+
else views.innerHTML = referenceHtml();
|
|
1206
|
+
wireInputs();
|
|
1207
|
+
}
|
|
1208
|
+
function renderMenu() { if (menuOpen) renderAll(); }
|
|
1209
|
+
|
|
1210
|
+
function renderHeader() {
|
|
1211
|
+
const enc = activeEnc();
|
|
1212
|
+
const sub = enc ? subOf(enc) : null;
|
|
1213
|
+
byId("headSub").innerHTML = enc
|
|
1214
|
+
? '<span class="glyph">' + esc(sub.glyph) + "</span> " + esc(enc.name)
|
|
1215
|
+
+ ' <span class="dim">· ' + esc(sub.name) + "</span>"
|
|
1216
|
+
: '<span class="dim">Nothing open — start one in Prep.</span>';
|
|
1217
|
+
const undo = byId("undoBtn");
|
|
1218
|
+
const can = enc && enc.run.log.length;
|
|
1219
|
+
undo.disabled = !can;
|
|
1220
|
+
undo.classList.toggle("off", !can);
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
function renderTabs() {
|
|
1224
|
+
byId("tabs").innerHTML = VIEWS.map((v) =>
|
|
1225
|
+
'<button id="nav-' + v.key + '" class="tab' + (current === v.key && !menuOpen ? " on" : "")
|
|
1226
|
+
+ '" onclick="go(\'' + v.key + '\')">' + esc(v.label) + "</button>").join("");
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
/* ============================================================
|
|
1230
|
+
THE RUN BOARD
|
|
1231
|
+
============================================================ */
|
|
1232
|
+
function runHtml() {
|
|
1233
|
+
const enc = activeEnc();
|
|
1234
|
+
if (!enc) return empty("No encounter open", "Open or build one in <b>Prep</b>.");
|
|
1235
|
+
const sub = subOf(enc);
|
|
1236
|
+
const parts = [];
|
|
1237
|
+
|
|
1238
|
+
parts.push(roundBarHtml(enc, sub));
|
|
1239
|
+
parts.push(metersHtml(enc, sub));
|
|
1240
|
+
|
|
1241
|
+
if (sub.structure === "sequence") parts.push(obstacleHtml(enc, sub));
|
|
1242
|
+
if (sub.structure === "checks") parts.push(checksHtml(enc, sub));
|
|
1243
|
+
if (enc.subsystem === "influence") parts.push(npcHtml(enc, sub));
|
|
1244
|
+
|
|
1245
|
+
parts.push(participantsHtml(enc, sub));
|
|
1246
|
+
parts.push(thresholdStateHtml(enc));
|
|
1247
|
+
parts.push(logHtml(enc));
|
|
1248
|
+
return parts.join("");
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
function roundBarHtml(enc, sub) {
|
|
1252
|
+
const len = sub.roundLength
|
|
1253
|
+
? ' <span class="dim">· a round is ' + esc(enc.def.roundMinutes || enc.def.roundLabel
|
|
1254
|
+
|| (sub.roundLength.low + " to " + sub.roundLength.high)) + "</span>"
|
|
1255
|
+
: "";
|
|
1256
|
+
const note = sub.roundEnd
|
|
1257
|
+
? '<div class="note warn">' + esc(sub.roundEndNote) + "</div>" : "";
|
|
1258
|
+
return '<section class="card">'
|
|
1259
|
+
+ '<div class="row spread">'
|
|
1260
|
+
+ "<h2>Round " + enc.run.round + len + "</h2>"
|
|
1261
|
+
+ '<div class="rowbtns">'
|
|
1262
|
+
+ '<button class="btn" onclick="advanceRound(\'' + enc.id + '\')">Advance round →</button>'
|
|
1263
|
+
+ '<button class="btn ghost" onclick="resetRun(\'' + enc.id + '\')">Reset run</button>'
|
|
1264
|
+
+ "</div></div>" + note + "</section>";
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
function metersHtml(enc, sub) {
|
|
1268
|
+
const pools = poolsOf(enc);
|
|
1269
|
+
const cells = pools.map((p) => {
|
|
1270
|
+
const v = enc.run.pools[p.key] || 0;
|
|
1271
|
+
const target = meterTarget(enc, p);
|
|
1272
|
+
return '<div class="meter ' + (p.role === "hazard" ? "hazard" : "goal") + '">'
|
|
1273
|
+
+ '<div class="meterhead"><span>' + esc(p.name) + "</span>"
|
|
1274
|
+
+ '<span class="mval">' + v + (target ? ' <span class="dim">/ ' + target + "</span>" : "") + "</span></div>"
|
|
1275
|
+
+ meterPips(enc, p, v, target)
|
|
1276
|
+
+ '<div class="rowbtns tight">'
|
|
1277
|
+
+ '<button class="chipbtn" onclick="adjustPool(\'' + enc.id + "','" + p.key + "',-1)\">−1</button>"
|
|
1278
|
+
+ '<button class="chipbtn" onclick="adjustPool(\'' + enc.id + "','" + p.key + "',1)\">+1</button>"
|
|
1279
|
+
+ "</div></div>";
|
|
1280
|
+
});
|
|
1281
|
+
if (sub.edge) {
|
|
1282
|
+
const ep = enc.run.pools.ep || 0;
|
|
1283
|
+
cells.push('<div class="meter edge"><div class="meterhead"><span>'
|
|
1284
|
+
+ esc(sub.edge.name) + '</span><span class="mval">' + ep + "</span></div>"
|
|
1285
|
+
+ '<div class="note">' + esc(sub.edge.note) + "</div>"
|
|
1286
|
+
+ '<div class="rowbtns tight">'
|
|
1287
|
+
+ '<button class="chipbtn" onclick="adjustPool(\'' + enc.id + "',\'ep\',-1)\">−1</button>"
|
|
1288
|
+
+ '<button class="chipbtn" onclick="adjustPool(\'' + enc.id + "',\'ep\',1)\">+1</button>"
|
|
1289
|
+
+ "</div></div>");
|
|
1290
|
+
}
|
|
1291
|
+
return '<section class="meters">' + cells.join("") + "</section>";
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
function meterTarget(enc, pool) {
|
|
1295
|
+
if (pool.role === "hazard") {
|
|
1296
|
+
const lad = awarenessLadder(enc);
|
|
1297
|
+
return lad ? lad.fail : null;
|
|
1298
|
+
}
|
|
1299
|
+
if (subOf(enc).structure === "sequence") {
|
|
1300
|
+
const ob = currentObstacle(enc);
|
|
1301
|
+
return ob ? ob.cost : null;
|
|
1302
|
+
}
|
|
1303
|
+
return enc.def.goal || null;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
function meterPips(enc, pool, value, target) {
|
|
1307
|
+
if (!target) return "";
|
|
1308
|
+
const marks = pool.role === "hazard"
|
|
1309
|
+
? ((awarenessLadder(enc) || {}).marks || [])
|
|
1310
|
+
: (subOf(enc).thresholdBasis === "progress"
|
|
1311
|
+
? [] : (enc.def.thresholds || []).map((t) => t.at));
|
|
1312
|
+
const width = Math.min(100, target ? (value / target) * 100 : 0);
|
|
1313
|
+
const ticks = marks.filter((m) => m > 0 && m < target).map((m) =>
|
|
1314
|
+
'<span class="tick" style="left:' + ((m / target) * 100) + '%" title="'
|
|
1315
|
+
+ esc(m) + '"></span>').join("");
|
|
1316
|
+
return '<div class="bar"><span class="fill" style="width:' + width + '%"></span>'
|
|
1317
|
+
+ ticks + "</div>";
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
function obstacleHtml(enc, sub) {
|
|
1321
|
+
const list = enc.def.obstacles || [];
|
|
1322
|
+
const ob = currentObstacle(enc);
|
|
1323
|
+
if (!ob) {
|
|
1324
|
+
return '<section class="card done"><h2>All obstacles cleared</h2>'
|
|
1325
|
+
+ '<p class="dim">' + list.length + " of " + list.length
|
|
1326
|
+
+ " done. Whatever they were chasing, they caught it.</p></section>";
|
|
1327
|
+
}
|
|
1328
|
+
const cleared = enc.run._justCleared;
|
|
1329
|
+
delete enc.run._justCleared;
|
|
1330
|
+
const banner = cleared
|
|
1331
|
+
? '<div class="note heal">Cleared <b>' + esc(cleared) + "</b> — leftover points don't carry over.</div>"
|
|
1332
|
+
: "";
|
|
1333
|
+
const opts = (ob.options || []).map((o) =>
|
|
1334
|
+
'<span class="chk">' + esc(titleCase(o.skill)) + " DC " + o.dc
|
|
1335
|
+
+ (o.how ? ' <span class="dim">to ' + esc(o.how) + "</span>" : "") + "</span>").join("");
|
|
1336
|
+
let progress;
|
|
1337
|
+
if (ob.mode === "individual") {
|
|
1338
|
+
const got = enc.run.perPC[ob.id] || {};
|
|
1339
|
+
progress = '<div class="perpc">' + (enc.def.participants || []).filter(Boolean).map((n) =>
|
|
1340
|
+
'<span class="pcchip' + ((got[n] || 0) >= ob.cost ? " on" : "") + '">'
|
|
1341
|
+
+ esc(n) + " " + (got[n] || 0) + "/" + ob.cost + "</span>").join("") + "</div>";
|
|
1342
|
+
} else {
|
|
1343
|
+
progress = '<p class="dim">' + (enc.run.pools[poolsOf(enc)[0].key] || 0)
|
|
1344
|
+
+ " of " + ob.cost + " " + esc(poolsOf(enc)[0].abbr) + "</p>";
|
|
1345
|
+
}
|
|
1346
|
+
return '<section class="card">' + banner
|
|
1347
|
+
+ '<div class="row spread"><h2>' + esc(ob.name || "Obstacle") + "</h2>"
|
|
1348
|
+
+ '<span class="pill">' + (enc.run.obstacleIndex + 1) + " of " + list.length + "</span></div>"
|
|
1349
|
+
+ (ob.note ? "<p>" + esc(ob.note) + "</p>" : "")
|
|
1350
|
+
+ (opts ? '<div class="chks">' + opts + "</div>" : "")
|
|
1351
|
+
+ '<div class="pill mode">' + esc(ob.mode === "individual" ? "Individual" : "Group") + "</div>"
|
|
1352
|
+
+ progress + "</section>";
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
function checksHtml(enc, sub) {
|
|
1356
|
+
const checks = enc.def.checks || [];
|
|
1357
|
+
if (!checks.length) return "";
|
|
1358
|
+
return '<section class="card"><h2>Sources</h2>'
|
|
1359
|
+
+ '<p class="dim">' + esc(GM_CALLS.researchCap) + "</p>"
|
|
1360
|
+
+ checks.map((c) => {
|
|
1361
|
+
const got = enc.run.checks[c.id] || 0;
|
|
1362
|
+
const dry = got >= (c.cap || 0);
|
|
1363
|
+
return '<div class="srcrow' + (dry ? " dry" : "") + '">'
|
|
1364
|
+
+ '<span class="srcname">' + esc(c.name) + "</span>"
|
|
1365
|
+
+ '<span class="chk">' + esc(c.skill) + " DC " + c.dc + "</span>"
|
|
1366
|
+
+ '<span class="pill">' + got + "/" + (c.cap || 0) + " RP"
|
|
1367
|
+
+ (dry ? " · spent" : "") + "</span></div>";
|
|
1368
|
+
}).join("") + "</section>";
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
function npcHtml(enc, sub) {
|
|
1372
|
+
const npc = enc.def.npc || {};
|
|
1373
|
+
const shown = (kind, list) => (list || []).map((r, i) => {
|
|
1374
|
+
const id = kind + ":" + i;
|
|
1375
|
+
const open = enc.run.revealed.indexOf(id) !== -1;
|
|
1376
|
+
if (!open) {
|
|
1377
|
+
return '<button class="hidden-fact" onclick="revealFact(\'' + enc.id + "','"
|
|
1378
|
+
+ kind + "'," + i + ')">Reveal a ' + kind + "</button>";
|
|
1379
|
+
}
|
|
1380
|
+
const base = (npc.skills || []).map((s) => s.dc).sort((a, b) => a - b)[0];
|
|
1381
|
+
const shift = base != null
|
|
1382
|
+
? ' <span class="dim">(' + base + " \u2192 " + adjustedDC(base, [r]) + ")</span>"
|
|
1383
|
+
: "";
|
|
1384
|
+
return '<span class="chk ' + kind + '">' + esc(r.label)
|
|
1385
|
+
+ " " + (r.dc > 0 ? "+" : "") + r.dc + " DC" + shift + "</span>";
|
|
1386
|
+
}).join("");
|
|
1387
|
+
return '<section class="card"><div class="row spread"><h2>' + esc(npc.name || "The NPC")
|
|
1388
|
+
+ '</h2><span class="pill">Discovery DC ' + (npc.discoveryDC || "—") + "</span></div>"
|
|
1389
|
+
+ '<div class="chks">' + (npc.skills || []).map((s) =>
|
|
1390
|
+
'<span class="chk">' + esc(s.skill) + " DC " + s.dc + "</span>").join("") + "</div>"
|
|
1391
|
+
+ ((npc.resistances || []).length
|
|
1392
|
+
? '<div class="factrow"><span class="flabel">Resistances</span>' + shown("resistance", npc.resistances) + "</div>" : "")
|
|
1393
|
+
+ ((npc.weaknesses || []).length
|
|
1394
|
+
? '<div class="factrow"><span class="flabel">Weaknesses</span>' + shown("weakness", npc.weaknesses) + "</div>" : "")
|
|
1395
|
+
+ '<div class="note">' + esc(sub.discoverNote) + "</div></section>";
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
function participantsHtml(enc, sub) {
|
|
1399
|
+
const names = (enc.def.participants || []).filter(Boolean);
|
|
1400
|
+
if (!names.length) {
|
|
1401
|
+
return empty("No participants yet", "Add their names in <b>Prep</b> so the rows have labels.");
|
|
1402
|
+
}
|
|
1403
|
+
const isInfluence = enc.subsystem === "influence";
|
|
1404
|
+
const checks = enc.def.checks || [];
|
|
1405
|
+
const rows = names.map((n) => {
|
|
1406
|
+
const degBtns = DEGREES.map((d) =>
|
|
1407
|
+
'<button class="deg ' + d.tone + '" onclick="scoreFrom(\'' + enc.id + "','"
|
|
1408
|
+
+ esc(n).replace(/'/g, "\\'") + "','" + d.key + '\')">' + esc(d.short) + "</button>").join("");
|
|
1409
|
+
const discBtns = isInfluence ? DEGREES.map((d) =>
|
|
1410
|
+
'<button class="deg ghost" onclick="discover(\'' + enc.id + "','"
|
|
1411
|
+
+ esc(n).replace(/'/g, "\\'") + "','" + d.key + '\')">' + esc(d.short) + "</button>").join("") : "";
|
|
1412
|
+
const edgeBtn = sub.edge
|
|
1413
|
+
? '<button class="deg edge" onclick="scoreFrom(\'' + enc.id + "','"
|
|
1414
|
+
+ esc(n).replace(/'/g, "\\'") + '\',\'failure\',true)" title="'
|
|
1415
|
+
+ esc(sub.edge.note) + '">Spend EP</button>'
|
|
1416
|
+
: "";
|
|
1417
|
+
return '<div class="pcrow"><div class="pcname">' + esc(n) + "</div>"
|
|
1418
|
+
+ '<div class="degs">' + (isInfluence ? '<span class="deglabel">Influence</span>' : "")
|
|
1419
|
+
+ degBtns + edgeBtn + "</div>"
|
|
1420
|
+
+ (isInfluence ? '<div class="degs"><span class="deglabel">Discover</span>' + discBtns + "</div>" : "")
|
|
1421
|
+
+ "</div>";
|
|
1422
|
+
}).join("");
|
|
1423
|
+
const picker = checks.length > 1
|
|
1424
|
+
? '<div class="row"><label class="flabel" for="srcPick">Reading</label>'
|
|
1425
|
+
+ '<select id="srcPick" class="inp">' + checks.map((c) =>
|
|
1426
|
+
'<option value="' + c.id + '">' + esc(c.name) + " · " + esc(c.skill)
|
|
1427
|
+
+ " DC " + c.dc + "</option>").join("") + "</select></div>"
|
|
1428
|
+
: "";
|
|
1429
|
+
const extras = (sub.extras || []).map((x) =>
|
|
1430
|
+
'<button class="btn ghost" title="' + esc(x.note) + '" onclick="extra(\''
|
|
1431
|
+
+ enc.id + "','" + x.key + '\')">' + esc(x.label) + "</button>").join("");
|
|
1432
|
+
return '<section class="card"><h2>This round</h2>' + picker + rows
|
|
1433
|
+
+ (extras ? '<div class="rowbtns wrap">' + extras + "</div>" : "") + "</section>";
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
/* The source picker is read at click time, so it survives a re-render. */
|
|
1437
|
+
function scoreFrom(encId, actor, degree, edge) {
|
|
1438
|
+
const pick = byId("srcPick");
|
|
1439
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1440
|
+
const only = enc && (enc.def.checks || []).length === 1 ? enc.def.checks[0].id : null;
|
|
1441
|
+
score(encId, actor, degree, { edge: !!edge, checkId: pick ? pick.value : only });
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
function thresholdStateHtml(enc) {
|
|
1445
|
+
const list = (enc.def.thresholds || []).slice().sort((a, b) => a.at - b.at);
|
|
1446
|
+
if (!list.length) return "";
|
|
1447
|
+
const v = thresholdValue(enc);
|
|
1448
|
+
return '<section class="card"><h2>Thresholds</h2>'
|
|
1449
|
+
+ '<p class="dim">Counted in ' + esc(thresholdUnit(enc)) + " — at " + v + " now.</p>"
|
|
1450
|
+
+ list.map((t, i) => {
|
|
1451
|
+
const hit = v >= t.at;
|
|
1452
|
+
return '<div class="throw' + (hit ? " hit" : "") + '">'
|
|
1453
|
+
+ '<span class="pill">' + t.at + "</span>"
|
|
1454
|
+
+ "<span><b>" + esc(t.label || "Threshold " + (i + 1)) + "</b>"
|
|
1455
|
+
+ (t.effect ? '<br><span class="dim">' + esc(t.effect) + "</span>" : "") + "</span></div>";
|
|
1456
|
+
}).join("") + "</section>";
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
function logHtml(enc) {
|
|
1460
|
+
const log = enc.run.log.slice(-12).reverse();
|
|
1461
|
+
if (!log.length) return "";
|
|
1462
|
+
return '<section class="card log"><h2>What happened</h2>' + log.map((e) =>
|
|
1463
|
+
'<div class="logrow"><span class="pill">R' + e.round + "</span>"
|
|
1464
|
+
+ (e.actor ? "<b>" + esc(e.actor) + "</b> " : "") + esc(e.label) + "</div>").join("")
|
|
1465
|
+
+ "</section>";
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
/* ============================================================
|
|
1469
|
+
PREP
|
|
1470
|
+
============================================================ */
|
|
1471
|
+
function prepHtml() {
|
|
1472
|
+
const enc = activeEnc();
|
|
1473
|
+
return newEncounterHtml() + libraryHtml() + (enc ? editorHtml(enc) : "");
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
function newEncounterHtml() {
|
|
1477
|
+
return '<section class="card"><h2>Start something</h2><div class="picker">'
|
|
1478
|
+
+ SUBSYSTEM_ORDER.map((k) => {
|
|
1479
|
+
const s = SUBSYSTEMS[k];
|
|
1480
|
+
return '<button class="pick" onclick="startNew(\'' + k + '\')">'
|
|
1481
|
+
+ '<span class="glyph">' + esc(s.glyph) + "</span>"
|
|
1482
|
+
+ "<b>" + esc(s.name) + "</b>"
|
|
1483
|
+
+ '<span class="dim">' + esc(s.blurb) + "</span></button>";
|
|
1484
|
+
}).join("") + "</div>"
|
|
1485
|
+
+ '<div class="row"><input id="impCode" class="inp" placeholder="…or paste an encounter code">'
|
|
1486
|
+
+ '<button class="btn" onclick="importEncounter(byId(\'impCode\').value)">Import</button></div>'
|
|
1487
|
+
+ "</section>";
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
function startNew(subKey) {
|
|
1491
|
+
const opts = {};
|
|
1492
|
+
if (subKey === "chase") {
|
|
1493
|
+
const p = prompt("Short, medium or long? (6, 8 or 10 obstacles)", "medium");
|
|
1494
|
+
if (p === null) return;
|
|
1495
|
+
opts.preset = /^s/i.test(p) ? "short" : /^l/i.test(p) ? "long" : "medium";
|
|
1496
|
+
const n = parseInt(prompt("How many PCs?", "4"), 10);
|
|
1497
|
+
if (n > 0) opts.partySize = n;
|
|
1498
|
+
} else if (subKey === "custom") {
|
|
1499
|
+
const scales = GENERATED_VP_SCALES || [];
|
|
1500
|
+
const list = scales.map((s, i) => (i + 1) + ") " + s.duration + " — " + s.endPoint + " VP").join("\n");
|
|
1501
|
+
const pick = prompt("How long is this challenge?\n\n" + list + "\n\nNumber:", "2");
|
|
1502
|
+
if (pick === null) return;
|
|
1503
|
+
const idx = parseInt(pick, 10) - 1;
|
|
1504
|
+
if (scales[idx]) opts.scale = scales[idx];
|
|
1505
|
+
const nm = prompt("What are the points called?", "Victory Points");
|
|
1506
|
+
if (nm) { opts.pointName = nm; opts.pointAbbr = nm.split(/\s+/).map((w) => w[0]).join("").toUpperCase().slice(0, 3); }
|
|
1507
|
+
} else if (subKey === "influence") {
|
|
1508
|
+
const nm = prompt("Who are they working on?", "The NPC");
|
|
1509
|
+
if (nm === null) return;
|
|
1510
|
+
opts.npcName = nm;
|
|
1511
|
+
}
|
|
1512
|
+
createEncounter(subKey, opts);
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
function libraryHtml() {
|
|
1516
|
+
if (!library.encounters.length) return "";
|
|
1517
|
+
return '<section class="card"><h2>Saved</h2>' + library.encounters.map((e) => {
|
|
1518
|
+
const s = subOf(e);
|
|
1519
|
+
const on = e.id === library.activeId;
|
|
1520
|
+
return '<div class="encrow' + (on ? " on" : "") + '">'
|
|
1521
|
+
+ '<button class="encopen" onclick="openEncounter(\'' + e.id + '\')">'
|
|
1522
|
+
+ '<span class="glyph">' + esc(s.glyph) + "</span><b>" + esc(e.name) + "</b>"
|
|
1523
|
+
+ '<span class="dim">' + esc(s.name) + " · round " + e.run.round + "</span></button>"
|
|
1524
|
+
+ '<div class="rowbtns tight">'
|
|
1525
|
+
+ '<button class="chipbtn" onclick="exportEncounter(\'' + e.id + '\')">Code</button>'
|
|
1526
|
+
+ '<button class="chipbtn" onclick="duplicateEncounter(\'' + e.id + '\')">Copy</button>'
|
|
1527
|
+
+ '<button class="chipbtn danger" onclick="deleteEncounter(\'' + e.id + '\')">Delete</button>'
|
|
1528
|
+
+ "</div></div>";
|
|
1529
|
+
}).join("") + "</section>";
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
function editorHtml(enc) {
|
|
1533
|
+
const sub = subOf(enc);
|
|
1534
|
+
const asks = (sub.asks || []).map((k) =>
|
|
1535
|
+
'<div class="note">' + esc(GM_CALLS[k]) + "</div>").join("");
|
|
1536
|
+
return '<section class="card"><h2>Editing: ' + esc(enc.name) + "</h2>"
|
|
1537
|
+
+ field("Name", '<input class="inp" value="' + esc(enc.name)
|
|
1538
|
+
+ '" oninput="renameEncounter(\'' + enc.id + '\', this.value); renderHeader()">')
|
|
1539
|
+
+ field("Participants", '<input class="inp" value="'
|
|
1540
|
+
+ esc((enc.def.participants || []).join(", "))
|
|
1541
|
+
+ '" oninput="setParticipants(\'' + enc.id + '\', this.value)">'
|
|
1542
|
+
+ '<span class="hint">Comma separated. These label the rows on the run board.</span>')
|
|
1543
|
+
+ (sub.structure === "sequence" ? obstacleEditorHtml(enc) : "")
|
|
1544
|
+
+ (sub.structure === "checks" ? checkEditorHtml(enc) : "")
|
|
1545
|
+
+ (enc.subsystem === "influence" ? npcEditorHtml(enc) : "")
|
|
1546
|
+
+ (sub.modes ? modeEditorHtml(enc, sub) : "")
|
|
1547
|
+
+ thresholdEditorHtml(enc)
|
|
1548
|
+
+ asks
|
|
1549
|
+
+ "</section>";
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
function field(label, inner) {
|
|
1553
|
+
return '<div class="fld"><label class="flabel">' + esc(label) + "</label>" + inner + "</div>";
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
function setParticipants(id, value) {
|
|
1557
|
+
const enc = library.encounters.find((e) => e.id === id);
|
|
1558
|
+
if (!enc) return;
|
|
1559
|
+
enc.def.participants = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1560
|
+
save();
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
function obstacleEditorHtml(enc) {
|
|
1564
|
+
const list = enc.def.obstacles || [];
|
|
1565
|
+
return '<div class="fld"><label class="flabel">Obstacles</label>'
|
|
1566
|
+
+ list.map((o, i) =>
|
|
1567
|
+
'<div class="obrow">'
|
|
1568
|
+
+ '<input class="inp" value="' + esc(o.name) + '" oninput="setObstacle(\''
|
|
1569
|
+
+ enc.id + "','" + o.id + '\',\'name\',this.value)">'
|
|
1570
|
+
+ '<input class="inp num" type="number" min="1" value="' + o.cost
|
|
1571
|
+
+ '" oninput="setObstacle(\'' + enc.id + "','" + o.id + '\',\'cost\',this.value)">'
|
|
1572
|
+
+ (enc.subsystem === "infiltration"
|
|
1573
|
+
? '<select class="inp" onchange="setObstacle(\'' + enc.id + "','" + o.id
|
|
1574
|
+
+ '\',\'mode\',this.value)">'
|
|
1575
|
+
+ subOf(enc).obstacleModes.map((m) => '<option value="' + m.key + '"'
|
|
1576
|
+
+ (o.mode === m.key ? " selected" : "") + ">" + esc(m.label) + "</option>").join("")
|
|
1577
|
+
+ "</select>" : "")
|
|
1578
|
+
+ '<button class="chipbtn danger" onclick="removeObstacle(\'' + enc.id + "','"
|
|
1579
|
+
+ o.id + '\')">×</button></div>').join("")
|
|
1580
|
+
+ '<div class="rowbtns wrap"><button class="btn ghost" onclick="addObstacle(\''
|
|
1581
|
+
+ enc.id + '\')">Add obstacle</button>'
|
|
1582
|
+
+ (subOf(enc).obstacleLibrary
|
|
1583
|
+
? '<button class="btn ghost" onclick="pickObstacle(\'' + enc.id
|
|
1584
|
+
+ '\')">Pick from GM Core (' + (GENERATED_CHASE_OBSTACLES || []).length + ")</button>" : "")
|
|
1585
|
+
+ "</div></div>";
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
function setObstacle(encId, obId, key, value) {
|
|
1589
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1590
|
+
const ob = enc && (enc.def.obstacles || []).find((o) => o.id === obId);
|
|
1591
|
+
if (!ob) return;
|
|
1592
|
+
ob[key] = key === "cost" ? Math.max(1, parseInt(value, 10) || 1) : value;
|
|
1593
|
+
save();
|
|
1594
|
+
}
|
|
1595
|
+
function addObstacle(encId) {
|
|
1596
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1597
|
+
if (!enc) return;
|
|
1598
|
+
const n = (enc.def.obstacles || []).length;
|
|
1599
|
+
enc.def.obstacles.push(newObstacle("Obstacle " + (n + 1),
|
|
1600
|
+
suggestedCost(subOf(enc).obstacleCost, (enc.def.participants || []).length || 4, n)));
|
|
1601
|
+
save(); renderAll();
|
|
1602
|
+
}
|
|
1603
|
+
function removeObstacle(encId, obId) {
|
|
1604
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1605
|
+
if (!enc) return;
|
|
1606
|
+
enc.def.obstacles = enc.def.obstacles.filter((o) => o.id !== obId);
|
|
1607
|
+
if (enc.run.obstacleIndex >= enc.def.obstacles.length) {
|
|
1608
|
+
enc.run.obstacleIndex = Math.max(0, enc.def.obstacles.length - 1);
|
|
1609
|
+
}
|
|
1610
|
+
save(); renderAll();
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
function pickObstacle(encId) {
|
|
1614
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1615
|
+
if (!enc) return;
|
|
1616
|
+
const lib = GENERATED_CHASE_OBSTACLES || [];
|
|
1617
|
+
const term = prompt("Search GM Core's sample obstacles by name, terrain or level\n"
|
|
1618
|
+
+ "(e.g. \"urban\", \"rooftops\", \"5\"):", "");
|
|
1619
|
+
if (term === null) return;
|
|
1620
|
+
const t = term.trim().toLowerCase();
|
|
1621
|
+
const hits = lib.filter((o) => !t
|
|
1622
|
+
|| o.name.toLowerCase().indexOf(t) !== -1
|
|
1623
|
+
|| o.table.toLowerCase().indexOf(t) !== -1
|
|
1624
|
+
|| String(o.level) === t);
|
|
1625
|
+
if (!hits.length) { toast("Nothing matched."); return; }
|
|
1626
|
+
const list = hits.slice(0, 20).map((o, i) =>
|
|
1627
|
+
(i + 1) + ") " + o.name + " (" + o.table + ", level " + o.level + ") — "
|
|
1628
|
+
+ o.options.map((x) => titleCase(x.skill) + " DC " + x.dc).join(", ")).join("\n");
|
|
1629
|
+
const pick = parseInt(prompt(list + "\n\nNumber:", "1"), 10) - 1;
|
|
1630
|
+
const chosen = hits[pick];
|
|
1631
|
+
if (!chosen) return;
|
|
1632
|
+
const n = (enc.def.obstacles || []).length;
|
|
1633
|
+
enc.def.obstacles.push(newObstacle(chosen.name,
|
|
1634
|
+
suggestedCost(subOf(enc).obstacleCost, (enc.def.participants || []).length || 4, n),
|
|
1635
|
+
{ options: chosen.options.map((o) => ({ skill: o.skill, dc: o.dc, how: o.how })) }));
|
|
1636
|
+
save(); renderAll();
|
|
1637
|
+
toast("Added " + chosen.name + ".");
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
function checkEditorHtml(enc) {
|
|
1641
|
+
return '<div class="fld"><label class="flabel">Research checks</label>'
|
|
1642
|
+
+ (enc.def.checks || []).map((c) =>
|
|
1643
|
+
'<div class="obrow">'
|
|
1644
|
+
+ '<input class="inp" value="' + esc(c.name) + '" oninput="setCheck(\'' + enc.id + "','" + c.id + '\',\'name\',this.value)">'
|
|
1645
|
+
+ '<input class="inp" value="' + esc(c.skill) + '" oninput="setCheck(\'' + enc.id + "','" + c.id + '\',\'skill\',this.value)">'
|
|
1646
|
+
+ '<input class="inp num" type="number" value="' + c.dc + '" oninput="setCheck(\'' + enc.id + "','" + c.id + '\',\'dc\',this.value)">'
|
|
1647
|
+
+ '<input class="inp num" type="number" min="1" value="' + c.cap + '" oninput="setCheck(\'' + enc.id + "','" + c.id + '\',\'cap\',this.value)">'
|
|
1648
|
+
+ '<button class="chipbtn danger" onclick="removeCheck(\'' + enc.id + "','" + c.id + '\')">×</button></div>').join("")
|
|
1649
|
+
+ '<span class="hint">Name · skill · DC · maximum RP. ' + esc(subOf(enc).checkNote) + "</span>"
|
|
1650
|
+
+ '<div class="rowbtns"><button class="btn ghost" onclick="addCheck(\'' + enc.id + '\')">Add a source</button></div></div>';
|
|
1651
|
+
}
|
|
1652
|
+
function setCheck(encId, cid, key, value) {
|
|
1653
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1654
|
+
const c = enc && (enc.def.checks || []).find((x) => x.id === cid);
|
|
1655
|
+
if (!c) return;
|
|
1656
|
+
c[key] = (key === "dc" || key === "cap") ? (parseInt(value, 10) || 0) : value;
|
|
1657
|
+
save();
|
|
1658
|
+
}
|
|
1659
|
+
function addCheck(encId) {
|
|
1660
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1661
|
+
if (!enc) return;
|
|
1662
|
+
enc.def.checks.push({ id: uid(), name: "Another source", skill: "Society", dc: 18, cap: 2 });
|
|
1663
|
+
save(); renderAll();
|
|
1664
|
+
}
|
|
1665
|
+
function removeCheck(encId, cid) {
|
|
1666
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1667
|
+
if (!enc) return;
|
|
1668
|
+
enc.def.checks = enc.def.checks.filter((c) => c.id !== cid);
|
|
1669
|
+
save(); renderAll();
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
function npcEditorHtml(enc) {
|
|
1673
|
+
const npc = enc.def.npc;
|
|
1674
|
+
const rows = (kind) => (npc[kind] || []).map((r, i) =>
|
|
1675
|
+
'<div class="obrow"><input class="inp" value="' + esc(r.label)
|
|
1676
|
+
+ '" oninput="setFact(\'' + enc.id + "','" + kind + "'," + i + ',\'label\',this.value)">'
|
|
1677
|
+
+ '<input class="inp num" type="number" value="' + r.dc + '" oninput="setFact(\''
|
|
1678
|
+
+ enc.id + "','" + kind + "'," + i + ',\'dc\',this.value)">'
|
|
1679
|
+
+ '<button class="chipbtn danger" onclick="removeFact(\'' + enc.id + "','" + kind
|
|
1680
|
+
+ "'," + i + ')">×</button></div>').join("")
|
|
1681
|
+
+ '<div class="rowbtns"><button class="btn ghost" onclick="addFact(\'' + enc.id
|
|
1682
|
+
+ "','" + kind + '\')">Add a ' + kind + "</button></div>";
|
|
1683
|
+
return field("NPC", '<input class="inp" value="' + esc(npc.name)
|
|
1684
|
+
+ '" oninput="setNpc(\'' + enc.id + '\',\'name\',this.value)">')
|
|
1685
|
+
+ field("Discovery DC", '<input class="inp num" type="number" value="' + npc.discoveryDC
|
|
1686
|
+
+ '" oninput="setNpc(\'' + enc.id + '\',\'discoveryDC\',this.value)">')
|
|
1687
|
+
+ '<div class="fld"><label class="flabel">Influence skills</label>'
|
|
1688
|
+
+ (npc.skills || []).map((s, i) =>
|
|
1689
|
+
'<div class="obrow"><input class="inp" value="' + esc(s.skill)
|
|
1690
|
+
+ '" oninput="setSkill(\'' + enc.id + "'," + i + ',\'skill\',this.value)">'
|
|
1691
|
+
+ '<input class="inp num" type="number" value="' + s.dc + '" oninput="setSkill(\''
|
|
1692
|
+
+ enc.id + "'," + i + ',\'dc\',this.value)">'
|
|
1693
|
+
+ '<button class="chipbtn danger" onclick="removeSkill(\'' + enc.id + "'," + i
|
|
1694
|
+
+ ')">×</button></div>').join("")
|
|
1695
|
+
+ '<span class="hint">Lowest DC first — that is the order the book uses.</span>'
|
|
1696
|
+
+ '<div class="rowbtns"><button class="btn ghost" onclick="addSkill(\'' + enc.id
|
|
1697
|
+
+ '\')">Add a skill</button></div></div>'
|
|
1698
|
+
+ '<div class="fld"><label class="flabel">Resistances (raise a DC)</label>' + rows("resistance") + "</div>"
|
|
1699
|
+
+ '<div class="fld"><label class="flabel">Weaknesses (lower a DC)</label>' + rows("weakness") + "</div>";
|
|
1700
|
+
}
|
|
1701
|
+
function setNpc(encId, key, value) {
|
|
1702
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1703
|
+
if (!enc) return;
|
|
1704
|
+
enc.def.npc[key] = key === "discoveryDC" ? (parseInt(value, 10) || 0) : value;
|
|
1705
|
+
save();
|
|
1706
|
+
}
|
|
1707
|
+
function setSkill(encId, i, key, value) {
|
|
1708
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1709
|
+
if (!enc) return;
|
|
1710
|
+
enc.def.npc.skills[i][key] = key === "dc" ? (parseInt(value, 10) || 0) : value;
|
|
1711
|
+
save();
|
|
1712
|
+
}
|
|
1713
|
+
function addSkill(encId) {
|
|
1714
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1715
|
+
enc.def.npc.skills.push({ skill: "Deception", dc: 20 });
|
|
1716
|
+
save(); renderAll();
|
|
1717
|
+
}
|
|
1718
|
+
function removeSkill(encId, i) {
|
|
1719
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1720
|
+
enc.def.npc.skills.splice(i, 1);
|
|
1721
|
+
save(); renderAll();
|
|
1722
|
+
}
|
|
1723
|
+
function factList(enc, kind) {
|
|
1724
|
+
return kind === "resistance" ? enc.def.npc.resistances : enc.def.npc.weaknesses;
|
|
1725
|
+
}
|
|
1726
|
+
function setFact(encId, kind, i, key, value) {
|
|
1727
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1728
|
+
factList(enc, kind)[i][key] = key === "dc" ? (parseInt(value, 10) || 0) : value;
|
|
1729
|
+
save();
|
|
1730
|
+
}
|
|
1731
|
+
function addFact(encId, kind) {
|
|
1732
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1733
|
+
factList(enc, kind).push({ label: kind === "resistance" ? "Flattery" : "Honesty",
|
|
1734
|
+
dc: kind === "resistance" ? 3 : -3 });
|
|
1735
|
+
save(); renderAll();
|
|
1736
|
+
}
|
|
1737
|
+
function removeFact(encId, kind, i) {
|
|
1738
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1739
|
+
factList(enc, kind).splice(i, 1);
|
|
1740
|
+
save(); renderAll();
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
function modeEditorHtml(enc, sub) {
|
|
1744
|
+
return field("Structure", sub.modes.map((m) =>
|
|
1745
|
+
'<label class="radio"><input type="radio" name="mode" value="' + m.key + '"'
|
|
1746
|
+
+ (enc.def.mode === m.key ? " checked" : "")
|
|
1747
|
+
+ ' onchange="setMode(\'' + enc.id + "','" + m.key + '\')"> <b>' + esc(m.label)
|
|
1748
|
+
+ '</b> <span class="dim">' + esc(m.note) + "</span></label>").join(""))
|
|
1749
|
+
+ field("End point", '<input class="inp num" type="number" value="' + (enc.def.goal || 0)
|
|
1750
|
+
+ '" oninput="setGoal(\'' + enc.id + '\', this.value)">'
|
|
1751
|
+
+ (enc.def.scaleLabel ? '<span class="hint">GM Core scale: ' + esc(enc.def.scaleLabel) + "</span>" : ""));
|
|
1752
|
+
}
|
|
1753
|
+
function setMode(encId, mode) {
|
|
1754
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1755
|
+
enc.def.mode = mode;
|
|
1756
|
+
enc.run = freshRun(enc);
|
|
1757
|
+
save(); renderAll();
|
|
1758
|
+
toast("Structure changed — the run was reset.");
|
|
1759
|
+
}
|
|
1760
|
+
function setGoal(encId, value) {
|
|
1761
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1762
|
+
enc.def.goal = Math.max(1, parseInt(value, 10) || 1);
|
|
1763
|
+
save();
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
function thresholdEditorHtml(enc) {
|
|
1767
|
+
return '<div class="fld"><label class="flabel">Thresholds ('
|
|
1768
|
+
+ esc(thresholdUnit(enc)) + ')</label>'
|
|
1769
|
+
+ (enc.def.thresholds || []).map((t, i) =>
|
|
1770
|
+
'<div class="obrow"><input class="inp num" type="number" value="' + t.at
|
|
1771
|
+
+ '" oninput="setThreshold(\'' + enc.id + "'," + i + ',\'at\',this.value)">'
|
|
1772
|
+
+ '<input class="inp" placeholder="What it is" value="' + esc(t.label)
|
|
1773
|
+
+ '" oninput="setThreshold(\'' + enc.id + "'," + i + ',\'label\',this.value)">'
|
|
1774
|
+
+ '<input class="inp" placeholder="What it grants" value="' + esc(t.effect)
|
|
1775
|
+
+ '" oninput="setThreshold(\'' + enc.id + "'," + i + ',\'effect\',this.value)">'
|
|
1776
|
+
+ '<button class="chipbtn danger" onclick="removeThreshold(\'' + enc.id + "'," + i
|
|
1777
|
+
+ ')">×</button></div>').join("")
|
|
1778
|
+
+ '<div class="rowbtns"><button class="btn ghost" onclick="addThreshold(\'' + enc.id
|
|
1779
|
+
+ '\')">Add a threshold</button></div></div>';
|
|
1780
|
+
}
|
|
1781
|
+
function setThreshold(encId, i, key, value) {
|
|
1782
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1783
|
+
enc.def.thresholds[i][key] = key === "at" ? (parseInt(value, 10) || 0) : value;
|
|
1784
|
+
save();
|
|
1785
|
+
}
|
|
1786
|
+
function addThreshold(encId) {
|
|
1787
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1788
|
+
enc.def.thresholds.push({ at: 5, label: "", effect: "" });
|
|
1789
|
+
save(); renderAll();
|
|
1790
|
+
}
|
|
1791
|
+
function removeThreshold(encId, i) {
|
|
1792
|
+
const enc = library.encounters.find((e) => e.id === encId);
|
|
1793
|
+
enc.def.thresholds.splice(i, 1);
|
|
1794
|
+
save(); renderAll();
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
/* ============================================================
|
|
1798
|
+
REFERENCE
|
|
1799
|
+
============================================================ */
|
|
1800
|
+
let refFilter = "";
|
|
1801
|
+
|
|
1802
|
+
function referenceHtml() {
|
|
1803
|
+
const meta = GENERATED_SUB_META;
|
|
1804
|
+
return '<section class="card"><h2>How these work</h2>'
|
|
1805
|
+
+ '<p class="dim">Paizo\'s own text, generated from the open Foundry pf2e data '
|
|
1806
|
+
+ "(" + esc(meta.source) + " " + esc(meta.sourceCommit || meta.sourceBranch)
|
|
1807
|
+
+ ", " + esc(meta.generated) + "), so it can't drift from the book.</p>"
|
|
1808
|
+
+ SUBSYSTEM_ORDER.concat(["reputation", "duels", "leadership", "hexploration"])
|
|
1809
|
+
.map((k) => {
|
|
1810
|
+
const sub = SUBSYSTEMS[k];
|
|
1811
|
+
const page = SUB_PAGES[sub ? sub.rulesPage : k];
|
|
1812
|
+
if (!page) return "";
|
|
1813
|
+
return '<details class="ref"><summary>' + esc(page.name)
|
|
1814
|
+
+ (sub ? "" : ' <span class="dim">· reference only</span>')
|
|
1815
|
+
+ '<span class="cite">' + esc(page.book) + " pg. " + esc(page.pages) + "</span></summary>"
|
|
1816
|
+
+ '<div class="refbody">' + inlineActions(page.html) + "</div></details>";
|
|
1817
|
+
}).join("")
|
|
1818
|
+
+ "</section>" + lookupHtml();
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
/* The subsystem pages name actions rather than repeating them; splice the
|
|
1822
|
+
action's own text in where the marker sits. */
|
|
1823
|
+
function inlineActions(html) {
|
|
1824
|
+
return html.replace(/<span class="actref" data-action="([^"]+)">([^<]*)<\/span>/g,
|
|
1825
|
+
(m, slug, name) => {
|
|
1826
|
+
const a = ACTIONS_BY_SLUG[slug];
|
|
1827
|
+
if (!a) return "<b>" + esc(name) + "</b>";
|
|
1828
|
+
return "<b>" + esc(a.name) + "</b>" + actionMeta(a)
|
|
1829
|
+
+ '<div class="actbody">' + esc(a.description).replace(/\n+/g, "<br>") + "</div>";
|
|
1830
|
+
});
|
|
1831
|
+
}
|
|
1832
|
+
function actionMeta(a) {
|
|
1833
|
+
const bits = [];
|
|
1834
|
+
if (a.actions) bits.push(a.actions + " " + plural(a.actions, "action"));
|
|
1835
|
+
if (a.actionType && a.actionType !== "action") bits.push(a.actionType);
|
|
1836
|
+
(a.traits || []).forEach((t) => bits.push(t));
|
|
1837
|
+
return bits.length ? ' <span class="dim">(' + esc(bits.join(", ")) + ")</span>" : "";
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
function lookupHtml() {
|
|
1841
|
+
const t = refFilter.trim().toLowerCase();
|
|
1842
|
+
const match = (x) => !t || x.name.toLowerCase().indexOf(t) !== -1;
|
|
1843
|
+
const conds = CONDITIONS.filter(match);
|
|
1844
|
+
const acts = ACTIONS.filter(match);
|
|
1845
|
+
const card = (x, kind) => '<details class="ref"><summary>' + esc(x.name)
|
|
1846
|
+
+ '<span class="cite">' + kind + "</span></summary>"
|
|
1847
|
+
+ '<div class="refbody">' + esc(x.description || "").replace(/\n+/g, "<br>")
|
|
1848
|
+
+ "</div></details>";
|
|
1849
|
+
return '<section class="card"><h2>Conditions and actions</h2>'
|
|
1850
|
+
+ '<input id="refFilter" class="inp" placeholder="Search ' + CONDITIONS.length
|
|
1851
|
+
+ " conditions and " + ACTIONS.length + ' actions" value="' + esc(refFilter) + '">'
|
|
1852
|
+
+ (t ? conds.map((c) => card(c, "condition")).join("")
|
|
1853
|
+
+ acts.map((a) => card(a, "action")).join("")
|
|
1854
|
+
: '<p class="dim">Type to search.</p>')
|
|
1855
|
+
+ "</section>";
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
/* ============================================================
|
|
1859
|
+
MENU
|
|
1860
|
+
============================================================ */
|
|
1861
|
+
function menuHtml() {
|
|
1862
|
+
const s = library.settings;
|
|
1863
|
+
const meta = GENERATED_SUB_META;
|
|
1864
|
+
return '<section class="card"><div class="row spread"><h2>Settings</h2>'
|
|
1865
|
+
+ '<button class="btn ghost" onclick="closeMenu()">Done</button></div>'
|
|
1866
|
+
+ field("Theme", ["auto", "light", "dark"].map((m) =>
|
|
1867
|
+
'<label class="radio"><input type="radio" name="theme" value="' + m + '"'
|
|
1868
|
+
+ ((s.themeMode || "auto") === m ? " checked" : "")
|
|
1869
|
+
+ ' onchange="setThemeMode(\'' + m + '\')"> ' + m + "</label>").join(""))
|
|
1870
|
+
+ field("Accent", '<input class="inp" type="color" value="'
|
|
1871
|
+
+ esc((s.custom && s.custom.accent) || "#a87c4a")
|
|
1872
|
+
+ '" oninput="setAccent(this.value)"> '
|
|
1873
|
+
+ '<button class="btn ghost" onclick="resetTheme()">Reset</button>')
|
|
1874
|
+
+ '<div class="note">Everything you type stays in this browser, on this device. '
|
|
1875
|
+
+ "There is no account and no server. Use an encounter's <b>Code</b> button to "
|
|
1876
|
+
+ "move prep to another device.</div>"
|
|
1877
|
+
+ '<div class="note dim">Rules text: ' + esc(meta.source) + " @ "
|
|
1878
|
+
+ esc(meta.sourceCommit || "?") + " (" + esc(meta.sourceBranch) + "), built "
|
|
1879
|
+
+ esc(meta.generated) + ". " + esc(meta.pages) + " subsystem pages, "
|
|
1880
|
+
+ esc(meta.obstacles) + " sample obstacles. "
|
|
1881
|
+
+ meta.sources.map((x) => esc(x.title) + " (" + esc(x.license) + ")").join(", ")
|
|
1882
|
+
+ ".</div></section>";
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
/* ============================================================
|
|
1886
|
+
BOOT
|
|
1887
|
+
============================================================ */
|
|
1888
|
+
function empty(title, body) {
|
|
1889
|
+
return '<section class="card empty"><h2>' + esc(title) + "</h2><p>" + body + "</p></section>";
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
/* Re-renders blow away focus, so the two free-typing inputs are rewired by
|
|
1893
|
+
hand rather than re-rendered on every keystroke. */
|
|
1894
|
+
function wireInputs() {
|
|
1895
|
+
const f = byId("refFilter");
|
|
1896
|
+
if (f) {
|
|
1897
|
+
f.oninput = () => {
|
|
1898
|
+
refFilter = f.value;
|
|
1899
|
+
const pos = f.selectionStart;
|
|
1900
|
+
byId("views").innerHTML = referenceHtml();
|
|
1901
|
+
wireInputs();
|
|
1902
|
+
const again = byId("refFilter");
|
|
1903
|
+
if (again) { again.focus(); again.setSelectionRange(pos, pos); }
|
|
1904
|
+
};
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
function boot() {
|
|
1909
|
+
load();
|
|
1910
|
+
applyTheme();
|
|
1911
|
+
const icon = byId("titleIcon");
|
|
1912
|
+
if (icon) {
|
|
1913
|
+
icon.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" '
|
|
1914
|
+
+ 'stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">'
|
|
1915
|
+
+ '<path d="M3 20h18"/><path d="M6 20v-4"/><path d="M11 20v-8"/>'
|
|
1916
|
+
+ '<path d="M16 20v-12"/><path d="M3 7h18" stroke-dasharray="2.5 2.5"/></svg>';
|
|
1917
|
+
}
|
|
1918
|
+
current = library.encounters.length ? "run" : "prep";
|
|
1919
|
+
renderAll();
|
|
1920
|
+
if ("serviceWorker" in navigator && location.protocol.indexOf("http") === 0) {
|
|
1921
|
+
navigator.serviceWorker.register("sw.js").catch(() => {});
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
</script>
|
|
1926
|
+
<script>
|
|
1927
|
+
(function(){ try { boot(); } catch(e){ document.getElementById("views").textContent = "Failed to start: " + e.message; } })();
|
|
1928
|
+
</script>
|
|
1929
|
+
</body>
|
|
1930
|
+
</html>
|