pf2e-sage-stats 0.2.4 → 0.2.6

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/src/app.ts CHANGED
@@ -4,6 +4,7 @@ import { capitalize, entries, startCase, toLower, kebabCase, keys, difference, g
4
4
  import dedent from 'dedent-js';
5
5
  import abbreviate from 'abbreviate';
6
6
  import pluralize from 'pluralize';
7
+ import { JSDOM } from 'jsdom';
7
8
  import { readdir, rename, stat } from 'fs/promises';
8
9
 
9
10
  const schema = zod.object({
@@ -172,6 +173,7 @@ export const stub: Required<Schema> = {
172
173
  const childSchema = zod.object({
173
174
  name: zod.string().default(""),
174
175
  alias: zod.string().default(""),
176
+ id: zod.string().default(""),
175
177
  hp: zod.coerce.number().default(0),
176
178
  temphp: zod.coerce.number().default(0),
177
179
  maxhp: zod.coerce.number().default(10),
@@ -186,6 +188,7 @@ const charSchema = zod.object({
186
188
  state: zod.union([zod.literal('empty'), zod.literal('arrow'), zod.literal('check'), zod.literal('cross')]).default('empty'),
187
189
  name: zod.string().default(""),
188
190
  alias: zod.string().default(""),
191
+ id: zod.string().default(""),
189
192
  hp: zod.coerce.number().default(0),
190
193
  temphp: zod.coerce.number().default(0),
191
194
  maxhp: zod.coerce.number().default(10),
@@ -195,7 +198,7 @@ const charSchema = zod.object({
195
198
 
196
199
  export type CharSchema = zod.infer<typeof charSchema>;
197
200
 
198
- export const prediceateMap: Record<string, string> = {
201
+ export const flatMap: Record<string, string> = {
199
202
  ...([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20].map((v): [string, string] => (
200
203
  [`${v}`, `d20 >= ${v} flat;`]
201
204
  )).reduce<Record<string, string>>((p, [k, v]) => { p[k] = v; return p }, {})),
@@ -205,14 +208,24 @@ export const prediceateMap: Record<string, string> = {
205
208
  hidden: 'd20 >= 11 flat;',
206
209
  };
207
210
 
208
- export const adjustmentMap: () => Record<string, string> = () => ({
209
- 'incredibly-easy': '`(Incredibly Easy)`',
210
- 'very-easy': '`(Very Easy)`',
211
- 'easy': '`(Easy)`',
212
- 'hard': '`(Hard)`',
213
- 'very-hard': '`(Very Easy)`',
214
- 'incredibly-hard': '`(Incredibly Hard)`',
215
- });
211
+ export const diceMap: Record<string, string> = {
212
+ ...([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20].map((v): [string, string] => (
213
+ [`${v}`, `(${v})`]
214
+ )).reduce<Record<string, string>>((p, [k, v]) => { p[k] = v; return p }, {})),
215
+ a: '+2',
216
+ adv: '+2',
217
+ advantage: '+2',
218
+ d: '-2',
219
+ dis: '-2',
220
+ disadvantage: '-2',
221
+ f: '+2',
222
+ for: '+2',
223
+ fort: '+2',
224
+ fortune: '+2',
225
+ m: '-2',
226
+ mis: '-2',
227
+ misfortune: '-2',
228
+ };
216
229
 
217
230
  export const fromatMap = (name: string, map: Record<string, string>) => {
218
231
  return TSV.stringify([{
@@ -306,10 +319,7 @@ const locateStrikes = (statblock: string, alias: string): Record<string, Schema[
306
319
  .replace(/\+\d/g, '')
307
320
  .replace(/Weapon\s+Striking(\s+\((Greater|Major)\))?/gi, '')
308
321
  .replace(/((Greater|Major)\s+)?Striking/gi, '')
309
- .replace(/\(Agile\)/gi, '')
310
- .replace(/\(Finesse\)/gi, '')
311
- .replace(/\(\+\)/gi, '')
312
- .replace(/\(\)/gi, '')
322
+ .replace(/\(.*\)/gi, '')
313
323
  .replace(/\s+/g, ' ')
314
324
  .trim();
315
325
 
@@ -320,6 +330,7 @@ const locateStrikes = (statblock: string, alias: string): Record<string, Schema[
320
330
  .replace(/\s+/g, ' ')
321
331
  .replace(bps, (m) => m.toUpperCase())
322
332
  .replace(/1d/, 'd')
333
+ .replace(/jousting\s+d\d+/g, 'jousting')
323
334
  .trim();
324
335
 
325
336
  const _dice = dice
@@ -328,6 +339,10 @@ const locateStrikes = (statblock: string, alias: string): Record<string, Schema[
328
339
  .replace(/Fire/, 'fire')
329
340
  .replace(/Cold/, 'cold')
330
341
  .replace(/Electricity/, 'electricity')
342
+ .replace(/Sonic/, 'sonic')
343
+ .replace(/Spirit/, 'spirit')
344
+ .replace(/Acid/, 'acid')
345
+ .replace(/[pP]lus\s(\d+d)/, '+$1')
331
346
  .trim();
332
347
 
333
348
  const twoHand = _traits.match(/(two-hand|two-handed)\s+(d\d+)/);
@@ -479,18 +494,7 @@ const toRanged = ([key, value]: [string, Schema['ranged'][string]]): [string, nu
479
494
  ...(value.damage ? [[`ranged.${key}.damage`, value.damage] as [string, string]] : []),
480
495
  ];
481
496
 
482
- const recallDCs = (level: number, secret: boolean, bump: number): [string, string][] => ((value) => [
483
- ['dc.recall.incredibly-easy', secret ? `||${value - 10}||` : `${value - 10}`],
484
- ['dc.recall.very-easy', secret ? `||${value - 5}||` : `${value - 5}`],
485
- ['dc.recall.easy', secret ? `||${value - 2}||` : `${value - 2}`],
486
- ['dc.recall.default', secret ? `||${value}||` : `${value}`],
487
- ['dc.recall', secret ? `||${value}||` : `${value}`],
488
- ['dc.recall.hard', secret ? `||${value + 2}||` : `${value + 2}`],
489
- ['dc.recall.very-hard', secret ? `||${value + 5}||` : `${value + 5}`],
490
- ['dc.recall.incredibly-hard', secret ? `||${value + 10}||` : `${value + 10}`],
491
- ])(dcByLevel[Math.max(Math.min(level + 1, dcByLevel.length - 1), 0)] + bump);
492
-
493
- export const flatten = (stats: Schema, secretDC: boolean = false, defaultSkills: boolean = false, recallDC: boolean = false): Record<string, string | number> => {
497
+ export const flatten = (stats: Schema, secretDC: boolean = false, defaultSkills: boolean = false): Record<string, string | number> => {
494
498
  const untrained = stats.untrained;
495
499
  const skills = defaultSkills ? {
496
500
  acrobatics: untrained + stats.attributes.dexterity,
@@ -595,9 +599,6 @@ export const flatten = (stats: Schema, secretDC: boolean = false, defaultSkills:
595
599
  ...entries(skills).map(mod2DC(true, (stats.bump.skills ?? stats.bump.default))),
596
600
  ...entries(stats.extra).map(mod2DC(true, stats.bump.default)),
597
601
  ] : []),
598
- ...(recallDC ? [
599
- ...recallDCs(stats.level, secretDC, stats.bump.default),
600
- ] : []),
601
602
  ...(stats.perceptionInfo.length > 0 ? [
602
603
  [`perception.info`, `\`(${stats.perceptionInfo.join(', ')})\``]
603
604
  ] : []),
@@ -610,12 +611,12 @@ export const flatten = (stats: Schema, secretDC: boolean = false, defaultSkills:
610
611
  ].reduce<Record<string | number, string | number>>((p, [key, value]) => { p[key] = value; return p }, {});
611
612
  }
612
613
 
613
- export const formatTSV = (stats: Schema, secretDC: boolean = false, defaultSkills: boolean = false, recallDC: boolean = false): string => (
614
- TSV.stringify([flatten(stats, secretDC, defaultSkills, recallDC)])
614
+ export const formatTSV = (stats: Schema, secretDC: boolean = false, defaultSkills: boolean = false): string => (
615
+ TSV.stringify([flatten(stats, secretDC, defaultSkills)])
615
616
  );
616
617
 
617
- export const formatCommand = (stats: Schema, secretDC: boolean = false, defaultSkills: boolean = false, recallDC: boolean = false): string => (
618
- Object.entries(flatten(stats, secretDC, defaultSkills, recallDC)).map(([k, v]) => `${k}="${v}"`).join(' ')
618
+ export const formatCommand = (stats: Schema, secretDC: boolean = false, defaultSkills: boolean = false): string => (
619
+ Object.entries(flatten(stats, secretDC, defaultSkills)).map(([k, v]) => `${k}="${v}"`).join(' ')
619
620
  );
620
621
 
621
622
  export const formatTable = (table: string, season: string, scenario: string, _name: string, gm: string, players: string) => {
@@ -629,7 +630,7 @@ export const formatTable = (table: string, season: string, scenario: string, _na
629
630
  @TableBot create "${gameName}" --gm ${gm} --players ${players} --table-name ${tableName} --ooc-table-name ooc-${tableName} --category Game Tables
630
631
  \`\`\`
631
632
  \`\`\`
632
- sage! game create name="${gameName}" gameSystem="pf2e" ic=" #${tableName} " ooc=" #ooc-${tableName} " gms=" ${gm} " players=" @${gameName} " dialogPost="post" diceSecret="gm" diceCrit="timestwo" diceOutput=M gmCharName="Хронист" diceSecret="gm"
633
+ sage! game create name="${gameName}" gameSystem="pf2e" ic=" #${tableName} " ooc=" #ooc-${tableName} " gms=" ${gm} " players=" @${gameName} " dialogPost="post" diceSecret="gm" diceCrit="timestwo" diceOutput=M gmCharName="Хронист"
633
634
  \`\`\`
634
635
  `;
635
636
  };
@@ -697,8 +698,11 @@ export const newtracker = (status: string) => {
697
698
  };
698
699
 
699
700
  export const formatTracker = (tracker: CharSchema[]) => {
701
+ const aliasSpace = (t: { alias: string }) => (t.alias.length > 0 ? 0 : 5);
702
+ const nameLengthWithAlias = (t: { name: string, alias: string }) => t.name.length - aliasSpace(t);
703
+
700
704
  const nameLength = tracker.reduce((p, c) => {
701
- const length = c.children.reduce((pp, cc) => pp > cc.name.length + 1 ? pp : cc.name.length + 1, c.name.length);
705
+ const length = c.children.reduce((pp, cc) => pp > nameLengthWithAlias(cc) ? pp : nameLengthWithAlias(cc), nameLengthWithAlias(c));
702
706
 
703
707
  return p > length ? p : length;
704
708
  }, 0);
@@ -737,10 +741,12 @@ export const formatTracker = (tracker: CharSchema[]) => {
737
741
  ls.push('')
738
742
  }
739
743
 
740
- const hp = char.foe ? (
744
+ const hp = !char.foe && char.hp > 0 ? (
745
+ `${char.hp}${char.temphp > 0 ? `+${char.temphp}` : ''}/${char.maxhp}`.padStart(hpLength)
746
+ ) : char.hp > 0 ? (
741
747
  `-${char.maxhp - char.hp}${char.temphp > 0 ? `+${char.temphp}` : ''}`.padStart(hpLength)
742
748
  ) : (
743
- `${char.hp}${char.temphp > 0 ? `+${char.temphp}` : ''}/${char.maxhp}`.padStart(hpLength)
749
+ `${char.hp}${char.temphp > 0 ? `+${char.temphp}` : ''}`.padStart(hpLength)
744
750
  );
745
751
 
746
752
  const hpBar = (value: number, max: number) => (
@@ -754,7 +760,7 @@ export const formatTracker = (tracker: CharSchema[]) => {
754
760
  );
755
761
 
756
762
  ls.push((
757
- `${stateMap[char.state]}**\` ${char.name.padEnd(nameLength)} ▏${char.alias.padEnd(3)} ▏${hp} \`** ${hpBar(char.hp, char.maxhp)} `
763
+ `${stateMap[char.hp > 0 ? char.state : 'cross']}**\` ${char.name.padEnd(nameLength + aliasSpace(char))}${char.alias.length > 0 ? ` ▏${char.alias.padEnd(3)}` : ''} ▏${hp} \`** ${hpBar(char.hp, char.maxhp)} `
758
764
  ))
759
765
 
760
766
  if (char.conditions.length > 0) {
@@ -762,14 +768,16 @@ export const formatTracker = (tracker: CharSchema[]) => {
762
768
  }
763
769
 
764
770
  for (const child of char.children) {
765
- const hhp = char.foe ? (
771
+ const hhp = !char.foe && child.hp > 0 ? (
772
+ `${child.hp}${child.temphp > 0 ? `+${child.temphp}` : ''}/${child.maxhp}`.padStart(hpLength)
773
+ ) : child.hp > 0 ? (
766
774
  `-${child.maxhp - child.hp}${child.temphp > 0 ? `+${child.temphp}` : ''}`.padStart(hpLength)
767
775
  ) : (
768
- `${child.hp}${child.temphp > 0 ? `+${child.temphp}` : ''}/${child.maxhp}`.padStart(hpLength)
776
+ `${child.hp}${child.temphp > 0 ? `+${child.temphp}` : ''}`.padStart(hpLength)
769
777
  );
770
778
 
771
779
  ls.push((
772
- `:smallnode:**\`- ${child.name.padEnd(nameLength - 1)} ▏${child.alias.padEnd(3)} ▏${hhp} \`** ${hpBar(child.hp, child.maxhp)} `
780
+ `:smallnode:**\` ${child.name.padEnd(nameLength + aliasSpace(child))}${child.alias.length > 0 ? ` ▏${child.alias.padEnd(3)}` : ''} ▏${hhp} \`** ${hpBar(child.hp, child.maxhp)} `
773
781
  ))
774
782
 
775
783
  if (child.conditions.length > 0) {
@@ -808,3 +816,52 @@ export const sortFolder = async () => {
808
816
 
809
817
  return transformed;
810
818
  };
819
+
820
+ export const screenPlay = (htmlContent: string) => {
821
+ const dom = new JSDOM(htmlContent);
822
+ const document = dom.window.document;
823
+
824
+ const outputLines = [];
825
+
826
+ const preambleEntry = document.querySelector('.preamble__entry');
827
+ if (preambleEntry) {
828
+ outputLines.push(`SCENE: ${preambleEntry.textContent}`);
829
+ outputLines.push('');
830
+ }
831
+
832
+ // Process all chatlog messages
833
+ const messages = document.querySelectorAll('.chatlog__message');
834
+
835
+ messages.forEach(message => {
836
+ const authorElement = message.querySelector('.chatlog__author');
837
+ const contentElement = message.querySelector('.chatlog__content');
838
+
839
+ if (!authorElement || !contentElement) return;
840
+
841
+ const author = authorElement.textContent?.trim() ?? '';
842
+ const content = contentElement.textContent?.trim() ?? '';
843
+
844
+ // Skip bot tags in character names
845
+ const characterName = author.replace(/\s*BOT$/, '');
846
+
847
+ // Add character line
848
+ outputLines.push(`${characterName.toUpperCase()}`);
849
+
850
+ // Add dialogue/content (wrap long lines)
851
+ const lines = content.split('\n').map((c) => (
852
+ c
853
+ .replace(/(—|--)/g, '-')
854
+ // eslint-disable-next-line no-irregular-whitespace
855
+ .replace(/(​|​)/g, '')
856
+ .trim()
857
+ )).filter((c) => c.length);
858
+
859
+ for (const line of lines) {
860
+ outputLines.push(` ${line}`);
861
+ }
862
+
863
+ outputLines.push('');
864
+ });
865
+
866
+ return outputLines.join('\n');
867
+ }