pf2e-sage-stats 0.1.1 → 0.2.1
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/README.md +34 -79
- package/dist/app.js +171 -77
- package/dist/index.js +13 -65
- package/package.json +1 -1
- package/src/app.ts +176 -155
- package/src/index.ts +15 -62
package/src/app.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import zod from 'zod';
|
|
2
2
|
import { TSV } from 'tsv';
|
|
3
|
-
import { capitalize, entries, startCase, toLower, kebabCase } from 'lodash';
|
|
3
|
+
import { capitalize, entries, startCase, toLower, kebabCase, keys, difference, groupBy, mapValues } from 'lodash';
|
|
4
4
|
import dedent from 'dedent-js';
|
|
5
5
|
import abbreviate from 'abbreviate';
|
|
6
6
|
import pluralize from 'pluralize';
|
|
7
|
+
import { readdir, rename, stat } from 'fs/promises';
|
|
7
8
|
|
|
8
9
|
const schema = zod.object({
|
|
9
10
|
name: zod.string(),
|
|
@@ -25,6 +26,8 @@ const schema = zod.object({
|
|
|
25
26
|
}).default({}),
|
|
26
27
|
ac: zod.coerce.number().default(10),
|
|
27
28
|
perception: zod.coerce.number().default(0),
|
|
29
|
+
perceptionInfo: zod.array(zod.string()).default([]),
|
|
30
|
+
initiativeInfo: zod.array(zod.string()).default([]),
|
|
28
31
|
skills: zod.object({
|
|
29
32
|
acrobatics: zod.coerce.number().optional(),
|
|
30
33
|
arcana: zod.coerce.number().optional(),
|
|
@@ -36,7 +39,7 @@ const schema = zod.object({
|
|
|
36
39
|
medicine: zod.coerce.number().optional(),
|
|
37
40
|
nature: zod.coerce.number().optional(),
|
|
38
41
|
occultism: zod.coerce.number().optional(),
|
|
39
|
-
|
|
42
|
+
performance: zod.coerce.number().optional(),
|
|
40
43
|
religion: zod.coerce.number().optional(),
|
|
41
44
|
society: zod.coerce.number().optional(),
|
|
42
45
|
stealth: zod.coerce.number().optional(),
|
|
@@ -50,12 +53,12 @@ const schema = zod.object({
|
|
|
50
53
|
melee: zod.record(zod.string(), zod.object({
|
|
51
54
|
mod: zod.coerce.number().default(0),
|
|
52
55
|
desc: zod.string(),
|
|
53
|
-
damage: zod.string(),
|
|
56
|
+
damage: zod.string().optional(),
|
|
54
57
|
})).default({}),
|
|
55
58
|
ranged: zod.record(zod.string(), zod.object({
|
|
56
59
|
mod: zod.coerce.number().default(0),
|
|
57
60
|
desc: zod.string(),
|
|
58
|
-
damage: zod.string(),
|
|
61
|
+
damage: zod.string().optional(),
|
|
59
62
|
})).default({}),
|
|
60
63
|
spells: zod.object({
|
|
61
64
|
attack: zod.coerce.number().optional(),
|
|
@@ -64,7 +67,42 @@ const schema = zod.object({
|
|
|
64
67
|
extra: zod.record(zod.string(), zod.coerce.number()).default({}),
|
|
65
68
|
extraDCs: zod.record(zod.string(), zod.coerce.number()).default({}),
|
|
66
69
|
extraDice: zod.record(zod.string(), zod.coerce.string()).default({}),
|
|
67
|
-
|
|
70
|
+
classDC: zod.coerce.number().default(10),
|
|
71
|
+
untrained: zod.coerce.number().default(0),
|
|
72
|
+
bump: zod.object({
|
|
73
|
+
default: zod.coerce.number().default(0),
|
|
74
|
+
attack: zod.coerce.number().optional(),
|
|
75
|
+
ac: zod.coerce.number().optional(),
|
|
76
|
+
saves: zod.coerce.number().optional(),
|
|
77
|
+
skills: zod.coerce.number().optional(),
|
|
78
|
+
spells: zod.coerce.number().optional(),
|
|
79
|
+
perception: zod.coerce.number().optional(),
|
|
80
|
+
hp: zod.coerce.number().optional(),
|
|
81
|
+
}).default({ default: 0 }),
|
|
82
|
+
bullwark: zod.coerce.number().default(0),
|
|
83
|
+
saveInfo: zod.object({
|
|
84
|
+
fortitude: zod.array(zod.string()).default([]),
|
|
85
|
+
reflex: zod.array(zod.string()).default([]),
|
|
86
|
+
will: zod.array(zod.string()).default([]),
|
|
87
|
+
}).default({}),
|
|
88
|
+
skillInfo: zod.object({
|
|
89
|
+
acrobatics: zod.array(zod.string()).optional(),
|
|
90
|
+
arcana: zod.array(zod.string()).optional(),
|
|
91
|
+
athletics: zod.array(zod.string()).optional(),
|
|
92
|
+
crafting: zod.array(zod.string()).optional(),
|
|
93
|
+
deception: zod.array(zod.string()).optional(),
|
|
94
|
+
diplomacy: zod.array(zod.string()).optional(),
|
|
95
|
+
intimidation: zod.array(zod.string()).optional(),
|
|
96
|
+
medicine: zod.array(zod.string()).optional(),
|
|
97
|
+
nature: zod.array(zod.string()).optional(),
|
|
98
|
+
occultism: zod.array(zod.string()).optional(),
|
|
99
|
+
performance: zod.array(zod.string()).optional(),
|
|
100
|
+
religion: zod.array(zod.string()).optional(),
|
|
101
|
+
society: zod.array(zod.string()).optional(),
|
|
102
|
+
stealth: zod.array(zod.string()).optional(),
|
|
103
|
+
survival: zod.array(zod.string()).optional(),
|
|
104
|
+
thievery: zod.array(zod.string()).optional(),
|
|
105
|
+
}).default({}),
|
|
68
106
|
})
|
|
69
107
|
|
|
70
108
|
export type Schema = zod.infer<typeof schema>;
|
|
@@ -89,6 +127,8 @@ export const stub: Required<Schema> = {
|
|
|
89
127
|
},
|
|
90
128
|
ac: 10,
|
|
91
129
|
perception: 0,
|
|
130
|
+
perceptionInfo: [],
|
|
131
|
+
initiativeInfo: [],
|
|
92
132
|
skills: {
|
|
93
133
|
acrobatics: 0,
|
|
94
134
|
arcana: 0,
|
|
@@ -100,7 +140,7 @@ export const stub: Required<Schema> = {
|
|
|
100
140
|
medicine: 0,
|
|
101
141
|
nature: 0,
|
|
102
142
|
occultism: 0,
|
|
103
|
-
|
|
143
|
+
performance: 0,
|
|
104
144
|
religion: 0,
|
|
105
145
|
society: 0,
|
|
106
146
|
stealth: 0,
|
|
@@ -116,17 +156,16 @@ export const stub: Required<Schema> = {
|
|
|
116
156
|
extra: {},
|
|
117
157
|
extraDCs: {},
|
|
118
158
|
extraDice: {},
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
xxl: 'xxl',
|
|
159
|
+
classDC: 10,
|
|
160
|
+
untrained: 0,
|
|
161
|
+
bump: { default: 0 },
|
|
162
|
+
bullwark: 0,
|
|
163
|
+
saveInfo: {
|
|
164
|
+
fortitude: [],
|
|
165
|
+
reflex: [],
|
|
166
|
+
will: [],
|
|
167
|
+
},
|
|
168
|
+
skillInfo: {},
|
|
130
169
|
};
|
|
131
170
|
|
|
132
171
|
export const prediceateMap: Record<string, string> = {
|
|
@@ -137,97 +176,15 @@ export const prediceateMap: Record<string, string> = {
|
|
|
137
176
|
concealed: 'd20 >= 5 flat;',
|
|
138
177
|
h: 'd20 >= 11 flat;',
|
|
139
178
|
hidden: 'd20 >= 11 flat;',
|
|
140
|
-
none: '',
|
|
141
179
|
};
|
|
142
180
|
|
|
143
|
-
export const flatMap = Object.entries(prediceateMap).flatMap(([k, v]) => Object.entries(formatMap).map(([fk, fv]): [string, string] => (
|
|
144
|
-
[`${fk}_${k}`, `${fv} ${v}`]
|
|
145
|
-
))).reduce<Record<string, string>>((p, [k, v]) => { p[k] = v; return p }, {});
|
|
146
|
-
|
|
147
|
-
export const valueNameMap: (tag: string | null) => Record<string, string> = (tag: string | null) => ({
|
|
148
|
-
...([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20].map((v): [string, string] => (
|
|
149
|
-
[`+${v}`, tag ? ` ⟮+${v} ${tag}⟯` : ` ⟮+${v}⟯`]
|
|
150
|
-
)).reduce<Record<string, string>>((p, [k, v]) => { p[k] = v; return p }, {})),
|
|
151
|
-
...([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20].map((v): [string, string] => (
|
|
152
|
-
[`${v}`, tag ? ` ⟮+${v} ${tag}⟯` : ` ⟮+${v}⟯`]
|
|
153
|
-
)).reduce<Record<string, string>>((p, [k, v]) => { p[k] = v; return p }, {})),
|
|
154
|
-
...([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20].map((v): [string, string] => (
|
|
155
|
-
[`-${v}`, tag ? ` ⟮-${v} ${tag}⟯` : ` ⟮-${v}⟯`]
|
|
156
|
-
)).reduce<Record<string, string>>((p, [k, v]) => { p[k] = v; return p }, {})),
|
|
157
|
-
'+0': '',
|
|
158
|
-
'0': '',
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
export const valueMap: () => Record<string, string> = () => ({
|
|
162
|
-
...([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20].map((v): [string, string] => (
|
|
163
|
-
[`+${v}`, `+${v}`]
|
|
164
|
-
)).reduce<Record<string, string>>((p, [k, v]) => { p[k] = v; return p }, {})),
|
|
165
|
-
...([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20].map((v): [string, string] => (
|
|
166
|
-
[`${v}`, `+${v}`]
|
|
167
|
-
)).reduce<Record<string, string>>((p, [k, v]) => { p[k] = v; return p }, {})),
|
|
168
|
-
...([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20].map((v): [string, string] => (
|
|
169
|
-
[`-${v}`, `-${v}`]
|
|
170
|
-
)).reduce<Record<string, string>>((p, [k, v]) => { p[k] = v; return p }, {})),
|
|
171
|
-
'+0': '+0',
|
|
172
|
-
'0': '+0',
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
export const diceNameMap: () => Record<string, string> = () => ({
|
|
176
|
-
...([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20].map((v): [string, string] => (
|
|
177
|
-
[`(${v})`, ` ⟮take ${v}⟯`]
|
|
178
|
-
)).reduce<Record<string, string>>((p, [k, v]) => { p[k] = v; return p }, {})),
|
|
179
|
-
'1': '',
|
|
180
|
-
'+1': '',
|
|
181
|
-
'-1': '',
|
|
182
|
-
'2': ' ⟮fortune⟯',
|
|
183
|
-
'+2': ' ⟮fortune⟯',
|
|
184
|
-
'-2': ' ⟮misfortune⟯',
|
|
185
|
-
'a': ' ⟮fortune⟯',
|
|
186
|
-
'adv': ' ⟮fortune⟯',
|
|
187
|
-
'advantage': ' ⟮fortune⟯',
|
|
188
|
-
'd': ' ⟮misfortune⟯',
|
|
189
|
-
'dis': ' ⟮misfortune⟯',
|
|
190
|
-
'disadvantage': ' ⟮misfortune⟯',
|
|
191
|
-
'f': ' ⟮fortune⟯',
|
|
192
|
-
'for': ' ⟮fortune⟯',
|
|
193
|
-
'fortune': ' ⟮fortune⟯',
|
|
194
|
-
'm': ' ⟮misfortune⟯',
|
|
195
|
-
'mis': ' ⟮misfortune⟯',
|
|
196
|
-
'misfortune': ' ⟮misfortune⟯',
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
export const diceMap: () => Record<string, string> = () => ({
|
|
200
|
-
...([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20].map((v): [string, string] => (
|
|
201
|
-
[`(${v})`, `(${v})`]
|
|
202
|
-
)).reduce<Record<string, string>>((p, [k, v]) => { p[k] = v; return p }, {})),
|
|
203
|
-
'1': '1',
|
|
204
|
-
'+1': '1',
|
|
205
|
-
'-1': '1',
|
|
206
|
-
'2': '+2',
|
|
207
|
-
'+2': '+2',
|
|
208
|
-
'-2': '-2',
|
|
209
|
-
'a': '+2',
|
|
210
|
-
'adv': '+2',
|
|
211
|
-
'advantage': '+2',
|
|
212
|
-
'd': '-2',
|
|
213
|
-
'dis': '-2',
|
|
214
|
-
'disadvantage': '-2',
|
|
215
|
-
'f': '+2',
|
|
216
|
-
'for': '+2',
|
|
217
|
-
'fortune': '+2',
|
|
218
|
-
'm': '-2',
|
|
219
|
-
'mis': '-2',
|
|
220
|
-
'misfortune': '-2',
|
|
221
|
-
});
|
|
222
|
-
|
|
223
181
|
export const adjustmentMap: () => Record<string, string> = () => ({
|
|
224
|
-
'incredibly-easy': '
|
|
225
|
-
'very-easy': '
|
|
226
|
-
'easy': '
|
|
227
|
-
'
|
|
228
|
-
'hard': '
|
|
229
|
-
'
|
|
230
|
-
'incredibly-hard': ' ⟮Incredibly Hard⟯',
|
|
182
|
+
'incredibly-easy': '`(Incredibly Easy)`',
|
|
183
|
+
'very-easy': '`(Very Easy)`',
|
|
184
|
+
'easy': '`(Easy)`',
|
|
185
|
+
'hard': '`(Hard)`',
|
|
186
|
+
'very-hard': '`(Very Easy)`',
|
|
187
|
+
'incredibly-hard': '`(Incredibly Hard)`',
|
|
231
188
|
});
|
|
232
189
|
|
|
233
190
|
export const fromatMap = (name: string, map: Record<string, string>) => {
|
|
@@ -317,16 +274,20 @@ const locateStrikes = (statblock: string, alias: string): Record<string, Schema[
|
|
|
317
274
|
|
|
318
275
|
if (name && attack && dice) {
|
|
319
276
|
const _name = name.toLowerCase()
|
|
320
|
-
.replace(/\s+/g, ' ')
|
|
321
277
|
.replace(/\+\d/g, '')
|
|
322
278
|
.replace(/Weapon\s+Striking(\s+\((Greater|Major)\))?/gi, '')
|
|
279
|
+
.replace(/((Greater|Major)\s+)?Striking/gi, '')
|
|
323
280
|
.replace(/\(Agile\)/gi, '')
|
|
281
|
+
.replace(/\(Finesse\)/gi, '')
|
|
324
282
|
.replace(/\(\+\)/gi, '')
|
|
283
|
+
.replace(/\(\)/gi, '')
|
|
284
|
+
.replace(/\s+/g, ' ')
|
|
325
285
|
.trim();
|
|
326
286
|
|
|
327
287
|
const bps = /(?<=[^\w']|^)[BbPpSs](?=[^\w]|$)/g;
|
|
328
288
|
|
|
329
289
|
let _traits = traits && traits.toLowerCase()
|
|
290
|
+
.replace(/\(\)/gi, '')
|
|
330
291
|
.replace(/\s+/g, ' ')
|
|
331
292
|
.replace(bps, (m) => m.toUpperCase())
|
|
332
293
|
.replace(/1d/, 'd')
|
|
@@ -335,6 +296,9 @@ const locateStrikes = (statblock: string, alias: string): Record<string, Schema[
|
|
|
335
296
|
const _dice = dice
|
|
336
297
|
.replace(/\s+/g, ' ')
|
|
337
298
|
.replace(bps, (m) => ({ b: 'bludgeoning', p: 'piercing', s: 'slashing' }[m.toLowerCase()] ?? m))
|
|
299
|
+
.replace(/Fire/, 'fire')
|
|
300
|
+
.replace(/Cold/, 'cold')
|
|
301
|
+
.replace(/Electricity/, 'electricity')
|
|
338
302
|
.trim();
|
|
339
303
|
|
|
340
304
|
const twoHand = _traits.match(/(two-hand|two-handed)\s+(d\d+)/);
|
|
@@ -389,15 +353,17 @@ const locateSpells = (statblock: string): Schema['spells'] | null => {
|
|
|
389
353
|
export const parseStatblock = (name: string | null, _statblock: string, alias: string | null): Schema => {
|
|
390
354
|
const defaults = schema.parse({ name: name ?? 'Unknown' });
|
|
391
355
|
|
|
356
|
+
const improvisation = _statblock.match(/Untrained\s+Improvisation/);
|
|
357
|
+
const plate = _statblock.match(/Full\s+Plate/)
|
|
358
|
+
|
|
392
359
|
const statblock = _statblock
|
|
393
360
|
.replace(/^Items.*$/m, '')
|
|
361
|
+
.replace('', '')
|
|
394
362
|
.replace(/–/g, '-') + '\n';
|
|
395
363
|
|
|
396
|
-
const improvisation = !!statblock.match(/Untrained\s+Improvisation/);
|
|
397
|
-
|
|
398
364
|
const basic = locateName(statblock);
|
|
399
365
|
|
|
400
|
-
|
|
366
|
+
const result: Schema = {
|
|
401
367
|
...defaults,
|
|
402
368
|
...basic,
|
|
403
369
|
...(name && { name: name }),
|
|
@@ -434,7 +400,7 @@ export const parseStatblock = (name: string | null, _statblock: string, alias: s
|
|
|
434
400
|
...locateInt('medicine', statblock, 'Skills'),
|
|
435
401
|
...locateInt('nature', statblock, 'Skills'),
|
|
436
402
|
...locateInt('occultism', statblock, 'Skills'),
|
|
437
|
-
...locateInt('
|
|
403
|
+
...locateInt('performance', statblock, 'Skills'),
|
|
438
404
|
...locateInt('religion', statblock, 'Skills'),
|
|
439
405
|
...locateInt('society', statblock, 'Skills'),
|
|
440
406
|
...locateInt('stealth', statblock, 'Skills'),
|
|
@@ -459,26 +425,31 @@ export const parseStatblock = (name: string | null, _statblock: string, alias: s
|
|
|
459
425
|
...locateInt('stealth', statblock, null, false, 'Stealth DC'),
|
|
460
426
|
},
|
|
461
427
|
extraDice: {},
|
|
462
|
-
improvisation,
|
|
463
428
|
};
|
|
429
|
+
|
|
430
|
+
result.untrained = improvisation ? (result.level >= 7 ? result.level : result.level >= 5 ? result.level - 1 : result.level - 2) : 0;
|
|
431
|
+
result.bullwark = plate ? Math.max(3 - result.attributes.dexterity, 0) : 0;
|
|
432
|
+
|
|
433
|
+
return result;
|
|
464
434
|
}
|
|
465
435
|
|
|
466
|
-
const dc2DC = (secret: boolean) => ([key, value]: [string | number, number]): [string, string] => [`dc.${key}`, secret ? `||${value}||` : `${value}`];
|
|
467
|
-
const mod2DC = (secret: boolean) => ([key, value]: [string | number, number]): [string, string] => [`dc.${key}`, secret ? `||${10 + value}||` : `${10 + value}`];
|
|
468
|
-
const
|
|
436
|
+
const dc2DC = (secret: boolean, bump: number) => ([key, value]: [string | number, number]): [string, string] => [`dc.${key}`, secret ? `||${value + bump}||` : `${value + bump}`];
|
|
437
|
+
const mod2DC = (secret: boolean, bump: number) => ([key, value]: [string | number, number]): [string, string] => [`dc.${key}`, secret ? `||${10 + value + bump}||` : `${10 + value + bump}`];
|
|
438
|
+
const toLoreMod = ([key, value]: [string | number, { mod: number; name: string; }]): [string, number] => [`lore.${key}`, value.mod];
|
|
439
|
+
const toLoreName = ([key, value]: [string | number, { mod: number; name: string; }]): [string, string] => [`lore.${key}.name`, value.name];
|
|
469
440
|
const toMelee = ([key, value]: [string, Schema['melee'][string]]): [string, number | string][] => [
|
|
470
441
|
[`melee.${key}`, value.mod],
|
|
471
442
|
[`melee.${key}.desc`, value.desc],
|
|
472
|
-
[`melee.${key}.damage`, value.damage],
|
|
443
|
+
...(value.damage ? [[`melee.${key}.damage`, value.damage] as [string, string]] : []),
|
|
473
444
|
];
|
|
474
445
|
|
|
475
446
|
const toRanged = ([key, value]: [string, Schema['ranged'][string]]): [string, number | string][] => [
|
|
476
447
|
[`ranged.${key}`, value.mod],
|
|
477
448
|
[`ranged.${key}.desc`, value.desc],
|
|
478
|
-
[`ranged.${key}.damage`, value.damage],
|
|
449
|
+
...(value.damage ? [[`ranged.${key}.damage`, value.damage] as [string, string]] : []),
|
|
479
450
|
];
|
|
480
451
|
|
|
481
|
-
const recallDCs = (level: number, secret: boolean): [string, string][] => ((value) => [
|
|
452
|
+
const recallDCs = (level: number, secret: boolean, bump: number): [string, string][] => ((value) => [
|
|
482
453
|
['dc.recall.incredibly-easy', secret ? `||${value - 10}||` : `${value - 10}`],
|
|
483
454
|
['dc.recall.very-easy', secret ? `||${value - 5}||` : `${value - 5}`],
|
|
484
455
|
['dc.recall.easy', secret ? `||${value - 2}||` : `${value - 2}`],
|
|
@@ -487,10 +458,10 @@ const recallDCs = (level: number, secret: boolean): [string, string][] => ((valu
|
|
|
487
458
|
['dc.recall.hard', secret ? `||${value + 2}||` : `${value + 2}`],
|
|
488
459
|
['dc.recall.very-hard', secret ? `||${value + 5}||` : `${value + 5}`],
|
|
489
460
|
['dc.recall.incredibly-hard', secret ? `||${value + 10}||` : `${value + 10}`],
|
|
490
|
-
])(dcByLevel[Math.max(Math.min(level + 1, dcByLevel.length - 1), 0)]);
|
|
461
|
+
])(dcByLevel[Math.max(Math.min(level + 1, dcByLevel.length - 1), 0)] + bump);
|
|
491
462
|
|
|
492
|
-
export const flatten = (stats: Schema, secretDC: boolean = false, defaultSkills: boolean = false, recallDC: boolean = false
|
|
493
|
-
const untrained = stats.
|
|
463
|
+
export const flatten = (stats: Schema, secretDC: boolean = false, defaultSkills: boolean = false, recallDC: boolean = false): Record<string, string | number> => {
|
|
464
|
+
const untrained = stats.untrained;
|
|
494
465
|
const skills = defaultSkills ? {
|
|
495
466
|
acrobatics: untrained + stats.attributes.dexterity,
|
|
496
467
|
arcana: untrained + stats.attributes.intelligence,
|
|
@@ -502,7 +473,7 @@ export const flatten = (stats: Schema, secretDC: boolean = false, defaultSkills:
|
|
|
502
473
|
medicine: untrained + stats.attributes.wisdom,
|
|
503
474
|
nature: untrained + stats.attributes.wisdom,
|
|
504
475
|
occultism: untrained + stats.attributes.intelligence,
|
|
505
|
-
|
|
476
|
+
performance: untrained + stats.attributes.charisma,
|
|
506
477
|
religion: untrained + stats.attributes.wisdom,
|
|
507
478
|
society: untrained + stats.attributes.intelligence,
|
|
508
479
|
stealth: untrained + stats.attributes.dexterity,
|
|
@@ -511,7 +482,11 @@ export const flatten = (stats: Schema, secretDC: boolean = false, defaultSkills:
|
|
|
511
482
|
...stats.skills,
|
|
512
483
|
} : stats.skills;
|
|
513
484
|
|
|
514
|
-
let melee = entries(stats.melee)
|
|
485
|
+
let melee = entries(stats.melee).map(([k, v]): [string, typeof v] => [k, {
|
|
486
|
+
mod: v.mod + (stats.bump.attack ?? stats.bump.default),
|
|
487
|
+
desc: v.desc,
|
|
488
|
+
damage: stats.bump.default > 0 ? `${v.damage} +${stats.bump.default}` : v.damage,
|
|
489
|
+
}]);
|
|
515
490
|
if (melee.length > 0) {
|
|
516
491
|
melee = [
|
|
517
492
|
['default', melee[0][1]],
|
|
@@ -519,7 +494,11 @@ export const flatten = (stats: Schema, secretDC: boolean = false, defaultSkills:
|
|
|
519
494
|
];
|
|
520
495
|
}
|
|
521
496
|
|
|
522
|
-
let ranged = entries(stats.ranged)
|
|
497
|
+
let ranged = entries(stats.ranged).map(([k, v]): [string, typeof v] => [k, {
|
|
498
|
+
mod: v.mod + (stats.bump.attack ?? stats.bump.default),
|
|
499
|
+
desc: v.desc,
|
|
500
|
+
damage: v.damage && stats.bump.default > 0 ? `${v.damage} +${stats.bump.default}` : v.damage,
|
|
501
|
+
}]);
|
|
523
502
|
if (ranged.length > 0) {
|
|
524
503
|
ranged = [
|
|
525
504
|
['default', ranged[0][1]],
|
|
@@ -527,61 +506,79 @@ export const flatten = (stats: Schema, secretDC: boolean = false, defaultSkills:
|
|
|
527
506
|
];
|
|
528
507
|
}
|
|
529
508
|
|
|
509
|
+
const maxhp = stats.bump.default > 0 ? (stats.bump.hp ? (stats.bump.hp + stats.maxhp) : Math.max(Math.floor(stats.maxhp * 1.1), stats.maxhp + 10)) : stats.maxhp;
|
|
510
|
+
|
|
511
|
+
const saveInfo = {
|
|
512
|
+
...stats.saveInfo,
|
|
513
|
+
reflex: stats.bullwark > 0 ? [...stats.saveInfo.reflex, `+${stats.bullwark} more vs damage`] : stats.saveInfo.reflex,
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
const untrainedSkillInfo = entries(mapValues(groupBy(difference(keys(skills), keys(stats.skills)), (k) => k), () => ['untrained']));
|
|
517
|
+
const specifiedSkillInfo = entries(stats.skillInfo);
|
|
518
|
+
|
|
519
|
+
const skillInfo = mapValues(groupBy([...untrainedSkillInfo, ...specifiedSkillInfo], ([k]) => k), (list) => list.reduce<string[]>((p, [, v]) => [...p, ...v], []))
|
|
520
|
+
|
|
530
521
|
return [
|
|
531
522
|
['name', stats.name],
|
|
532
523
|
...(stats.alias ? [['alias', stats.alias]] : []),
|
|
533
524
|
['gamesystem', 'pf2e'],
|
|
534
525
|
['level', stats.level],
|
|
535
526
|
['lvl', stats.level],
|
|
536
|
-
['
|
|
527
|
+
['hp', maxhp],
|
|
528
|
+
['maxhp', maxhp],
|
|
537
529
|
...entries(stats.attributes),
|
|
538
530
|
...entries(stats.attributes).map(([key, value]) => [key.substring(0,3), value]),
|
|
539
|
-
['ac', secretDC ? `||${stats.ac}||` : stats.ac],
|
|
540
|
-
...entries(stats.saves),
|
|
541
|
-
['fort', stats.saves.fortitude],
|
|
542
|
-
['ref', stats.saves.reflex],
|
|
543
|
-
['perception', stats.perception],
|
|
544
|
-
...entries(skills),
|
|
545
|
-
...entries(stats.lores).
|
|
546
|
-
...(
|
|
547
|
-
...entries(stats.saves).map(([key]) => [`${key}.desc`, `${capitalize(key)} Save`]),
|
|
548
|
-
['fort.desc', 'Fortitude Save'],
|
|
549
|
-
['ref.desc', 'Reflex Save'],
|
|
550
|
-
['perception.desc', 'Perception Check'],
|
|
551
|
-
...entries(skills).map(([key]) => [`${key}.desc`, `${capitalize(key)} Check`]),
|
|
552
|
-
...entries(stats.lores).map(([key, value]) => [`lore.${key}.desc`, `${value.name} Lore Check`]),
|
|
553
|
-
...(stats.spells.attack ? [['spells.desc', 'Spell Attack']] : []),
|
|
554
|
-
]: []),
|
|
531
|
+
['ac', secretDC ? `||${stats.ac + (stats.bump.ac ?? stats.bump.default)}||` : stats.ac + (stats.bump.ac ?? stats.bump.default)],
|
|
532
|
+
...entries(stats.saves).map(([k, v]) => [k, v + (stats.bump.saves ?? stats.bump.default)]),
|
|
533
|
+
['fort', stats.saves.fortitude + (stats.bump.saves ?? stats.bump.default)],
|
|
534
|
+
['ref', stats.saves.reflex + (stats.bump.saves ?? stats.bump.default)],
|
|
535
|
+
['perception', stats.perception + (stats.bump.perception ?? stats.bump.default)],
|
|
536
|
+
...entries(skills.performance ? { ...skills, perfomance: skills.performance } : skills).map(([k, v]) => [k, v + (stats.bump.skills ?? stats.bump.default)]),
|
|
537
|
+
...entries(stats.lores).map(toLoreMod).map(([k, v]) => [k, v + (stats.bump.skills ?? stats.bump.default)]),
|
|
538
|
+
...entries(stats.lores).map(toLoreName),
|
|
555
539
|
...melee.flatMap(toMelee),
|
|
556
540
|
...ranged.flatMap(toRanged),
|
|
557
541
|
...entries(stats.extra),
|
|
558
|
-
...(stats.spells.attack ? [['spells', stats.spells.attack]] : []),
|
|
559
|
-
...(stats.spells.dc ? [dc2DC(secretDC)(['spells', stats.spells.dc])] : []),
|
|
560
|
-
...
|
|
542
|
+
...(stats.spells.attack ? [['spells', stats.spells.attack + (stats.bump.attack ?? stats.bump.default)]] : []),
|
|
543
|
+
...(stats.spells.dc ? [dc2DC(secretDC, stats.bump.spells ?? stats.bump.default)(['spells', stats.spells.dc])] : []),
|
|
544
|
+
...(stats.classDC ? [['classDC', secretDC ? `||${stats.classDC + stats.bump.default}||` : `${stats.classDC + stats.bump.default}`]] : []),
|
|
545
|
+
...(stats.classDC ? [dc2DC(secretDC, stats.bump.default)(['class', stats.classDC])] : []),
|
|
546
|
+
dc2DC(secretDC, stats.bump.default)(['level', dcByLevel[Math.max(Math.min(stats.level + 1, dcByLevel.length - 1), 0)]]),
|
|
547
|
+
dc2DC(secretDC, stats.bump.ac ?? stats.bump.default)(['ac', stats.ac]),
|
|
548
|
+
...entries(stats.extraDCs).map(dc2DC(secretDC, stats.bump.default)),
|
|
561
549
|
...entries(stats.extraDice),
|
|
562
550
|
...(secretDC ? [
|
|
563
|
-
...entries(stats.saves).map(mod2DC(true)),
|
|
564
|
-
mod2DC(true)(['perception', stats.perception]),
|
|
565
|
-
mod2DC(true)(['fort', stats.saves.fortitude]),
|
|
566
|
-
mod2DC(true)(['ref', stats.saves.reflex]),
|
|
567
|
-
...entries(skills).map(mod2DC(true)),
|
|
568
|
-
...entries(stats.extra).map(mod2DC(true)),
|
|
551
|
+
...entries(stats.saves).map(mod2DC(true, (stats.bump.saves ?? stats.bump.default))),
|
|
552
|
+
mod2DC(true, stats.bump.default)(['perception', stats.perception]),
|
|
553
|
+
mod2DC(true, (stats.bump.saves ?? stats.bump.default))(['fort', stats.saves.fortitude]),
|
|
554
|
+
mod2DC(true, (stats.bump.saves ?? stats.bump.default))(['ref', stats.saves.reflex]),
|
|
555
|
+
...entries(skills).map(mod2DC(true, (stats.bump.skills ?? stats.bump.default))),
|
|
556
|
+
...entries(stats.extra).map(mod2DC(true, stats.bump.default)),
|
|
569
557
|
] : []),
|
|
570
558
|
...(recallDC ? [
|
|
571
|
-
...recallDCs(stats.level, secretDC),
|
|
559
|
+
...recallDCs(stats.level, secretDC, stats.bump.default),
|
|
560
|
+
] : []),
|
|
561
|
+
...(stats.perceptionInfo.length > 0 ? [
|
|
562
|
+
[`perception.info`, `\`(${stats.perceptionInfo.join(', ')})\``]
|
|
572
563
|
] : []),
|
|
564
|
+
...(stats.initiativeInfo.length > 0 ? [
|
|
565
|
+
[`initiative.info`, `\`(${stats.initiativeInfo.join(', ')})\``]
|
|
566
|
+
] : []),
|
|
567
|
+
...entries(saveInfo).filter(([, v]) => v.length > 0).map(([k, v]) => [`${k}.info`, `\`(${v.join(', ')})\``]),
|
|
568
|
+
...entries(skillInfo).filter(([, v]) => v.length > 0).map(([k, v]) => [`${k}.info`, `\`(${v.join(', ')})\``]),
|
|
569
|
+
['out', secretDC ? 'xs' : 'm'],
|
|
573
570
|
].reduce<Record<string | number, string | number>>((p, [key, value]) => { p[key] = value; return p }, {});
|
|
574
571
|
}
|
|
575
572
|
|
|
576
|
-
export const formatTSV = (stats: Schema, secretDC: boolean = false, defaultSkills: boolean = false, recallDC: boolean = false
|
|
577
|
-
TSV.stringify([flatten(stats, secretDC, defaultSkills, recallDC
|
|
573
|
+
export const formatTSV = (stats: Schema, secretDC: boolean = false, defaultSkills: boolean = false, recallDC: boolean = false): string => (
|
|
574
|
+
TSV.stringify([flatten(stats, secretDC, defaultSkills, recallDC)])
|
|
578
575
|
);
|
|
579
576
|
|
|
580
|
-
export const formatCommand = (stats: Schema, secretDC: boolean = false, defaultSkills: boolean = false, recallDC: boolean = false
|
|
581
|
-
Object.entries(flatten(stats, secretDC, defaultSkills, recallDC
|
|
577
|
+
export const formatCommand = (stats: Schema, secretDC: boolean = false, defaultSkills: boolean = false, recallDC: boolean = false): string => (
|
|
578
|
+
Object.entries(flatten(stats, secretDC, defaultSkills, recallDC)).map(([k, v]) => `${k}="${v}"`).join(' ')
|
|
582
579
|
);
|
|
583
580
|
|
|
584
|
-
export const formatTable = (table:
|
|
581
|
+
export const formatTable = (table: string, season: string, scenario: string, _name: string, gm: string, players: string) => {
|
|
585
582
|
const name = _name.replace(/[\s-_]/g, ' ').replace(/[^a-zA-Z0-9 ]/g, '');
|
|
586
583
|
|
|
587
584
|
const gameName = `PF2 T${table} S${season} ${String(scenario).padStart(2, '0')} ${name}`;
|
|
@@ -621,3 +618,27 @@ export const formatHP = (arg: string) => {
|
|
|
621
618
|
|
|
622
619
|
return _formatHP(negative ? maxhp - hp : hp, maxhp);
|
|
623
620
|
};
|
|
621
|
+
|
|
622
|
+
export const sortFolder = async () => {
|
|
623
|
+
const dir = await readdir('.');
|
|
624
|
+
|
|
625
|
+
const summary = await Promise.all(dir.map(async (file) => {
|
|
626
|
+
const stats = await stat(file);
|
|
627
|
+
|
|
628
|
+
const timestamp = parseInt(stats.birthtimeMs.toFixed(0));
|
|
629
|
+
|
|
630
|
+
return { file, timestamp };
|
|
631
|
+
}))
|
|
632
|
+
|
|
633
|
+
const transformed = summary.sort((a, b) => a.timestamp - b.timestamp).map((s, index) => {
|
|
634
|
+
const prefix = String(index + 1).padStart(3, '0');
|
|
635
|
+
|
|
636
|
+
const name = s.file.replace('RU PFS Online - ', '').replace(/\s\[.+\]/, '');
|
|
637
|
+
|
|
638
|
+
return { oldname: s.file, newname: name.startsWith('ooc') ? name : `${prefix} ${name}` };
|
|
639
|
+
})
|
|
640
|
+
|
|
641
|
+
await Promise.all(transformed.map(async (t) => rename(t.oldname, t.newname)));
|
|
642
|
+
|
|
643
|
+
return transformed;
|
|
644
|
+
};
|