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/index.ts CHANGED
@@ -4,10 +4,11 @@ import { Command } from 'commander';
4
4
  import fs from 'fs/promises';
5
5
  import path from 'path';
6
6
  import readline from 'readline';
7
+ import clipboardy from 'clipboardy';
7
8
 
8
9
  import pack from '../package.json';
9
10
 
10
- import { sortFolder, formatCommand, formatJSON, formatTSV, fromatMap, parseJSON, parseStatblock, stub, adjustmentMap, prediceateMap, formatTable, formatHP, newtracker, parseTracker, formatTracker } from './app';
11
+ import { sortFolder, formatCommand, formatJSON, formatTSV, fromatMap, parseJSON, parseStatblock, stub, flatMap, formatTable, formatHP, newtracker, parseTracker, formatTracker, screenPlay, diceMap } from './app';
11
12
 
12
13
  const program = new Command(pack.name);
13
14
 
@@ -57,27 +58,41 @@ program.command('sort')
57
58
  console.log(await sortFolder());
58
59
  });
59
60
 
61
+ program.command('convert')
62
+ .description('Convert a discord export file into a txt log')
63
+ .argument('<file>', 'input file')
64
+ .option('-o, --output <file>', 'output file')
65
+ .action(async (argument, option) => {
66
+ const file = await fs.readFile(argument, { encoding: 'utf-8' });
67
+
68
+ if (option.output) {
69
+ await fs.writeFile(option.output, screenPlay(file), { encoding: 'utf-8' })
70
+ } else {
71
+ console.log(screenPlay(file));
72
+ }
73
+ });
74
+
60
75
  program.command('flat')
61
76
  .description('Generte a special flat npc tsv')
62
77
  .option('-n, --name <string>', 'npc name')
63
78
  .option('-o, --output <file>', 'output json')
64
79
  .action(async (option) => {
65
80
  if (option.output) {
66
- await fs.writeFile(option.output, fromatMap(option.name ?? 'flat', prediceateMap), { encoding: 'utf-8' })
81
+ await fs.writeFile(option.output, fromatMap(option.name ?? 'flat', flatMap), { encoding: 'utf-8' })
67
82
  } else {
68
- console.log(fromatMap(option.name ?? 'flat', prediceateMap));
83
+ console.log(fromatMap(option.name ?? 'flat', flatMap));
69
84
  }
70
85
  });
71
86
 
72
- program.command('adjustment')
73
- .description('Generte a special adjustment npc tsv')
87
+ program.command('dice')
88
+ .description('Generte a special dice npc tsv')
74
89
  .option('-n, --name <string>', 'npc name')
75
90
  .option('-o, --output <file>', 'output json')
76
91
  .action(async (option) => {
77
92
  if (option.output) {
78
- await fs.writeFile(option.output, fromatMap(option.name ?? 'adjustment', adjustmentMap()), { encoding: 'utf-8' })
93
+ await fs.writeFile(option.output, fromatMap(option.name ?? 'dice', diceMap), { encoding: 'utf-8' })
79
94
  } else {
80
- console.log(fromatMap(option.name ?? 'adjustment', adjustmentMap()));
95
+ console.log(fromatMap(option.name ?? 'dice', diceMap));
81
96
  }
82
97
  });
83
98
 
@@ -119,7 +134,6 @@ program.command('tsv')
119
134
  .argument('<file>', 'input json')
120
135
  .option('-o, --output <file>', 'output tsv')
121
136
  .option('-s, --secretDC', 'produce secret DCs')
122
- .option('-r, --recallDC', 'produce recall DCs')
123
137
  .option('-d, --defaultSkills', 'produce values for untrained skills')
124
138
  .action(async (argument, option) => {
125
139
  const file = await fs.readFile(argument, { encoding: 'utf-8' });
@@ -128,7 +142,7 @@ program.command('tsv')
128
142
 
129
143
  const output = option.output ? option.output : path.join(path.parse(argument).dir, path.parse(argument).name + '.tsv');
130
144
 
131
- await fs.writeFile(output, formatTSV(stats, option.secretDC, option.defaultSkills, option.recallDC), { encoding: 'utf-8' });
145
+ await fs.writeFile(output, formatTSV(stats, option.secretDC, option.defaultSkills), { encoding: 'utf-8' });
132
146
  });
133
147
 
134
148
  program.command('command')
@@ -136,7 +150,6 @@ program.command('command')
136
150
  .argument('<file>', 'input json')
137
151
  .option('-o, --output <file>', 'output txt')
138
152
  .option('-s, --secretDC', 'produce secret DCs')
139
- .option('-r, --recallDC', 'produce recall DCs')
140
153
  .option('-d, --defaultSkills', 'produce values for untrained skills')
141
154
  .action(async (argument, option) => {
142
155
  const file = await fs.readFile(argument, { encoding: 'utf-8' });
@@ -145,7 +158,7 @@ program.command('command')
145
158
 
146
159
  const output = option.output ? option.output : path.join(path.parse(argument).dir, path.parse(argument).name + '-command.txt');
147
160
 
148
- await fs.writeFile(output, formatCommand(stats, option.secretDC, option.defaultSkills, option.recallDC), { encoding: 'utf-8' });
161
+ await fs.writeFile(output, formatCommand(stats, option.secretDC, option.defaultSkills), { encoding: 'utf-8' });
149
162
  });
150
163
 
151
164
  program.command('newtracker')
@@ -193,7 +206,10 @@ program.command('track')
193
206
  await fs.writeFile(option.output, formatTracker(tracker), { encoding: 'utf-8' })
194
207
  } else {
195
208
  console.clear();
196
- console.log(formatTracker(tracker));
209
+
210
+ const track = formatTracker(tracker);
211
+ clipboardy.writeSync(track)
212
+ console.log(track);
197
213
  }
198
214
  }
199
215
 
@@ -214,66 +230,337 @@ program.command('track')
214
230
  while (true) {
215
231
  await new Promise((resolve) => setTimeout(resolve, 500));
216
232
 
217
- const input = await new Promise<string>((resolve) => (
233
+ let input = await new Promise<string>((resolve) => (
218
234
  rl.question('> ', resolve)
219
235
  ));
220
236
 
221
- const hpmod = input.match(/([^\d\s+-]+)\s*([+-]?\d+)/);
237
+ const commands = [{
238
+ regex: /^\s*[bB]\s*(\d+)/,
239
+ map: (match: RegExpMatchArray) => ({ command: 'block' as const, index: parseInt(match[1], 10) })
240
+ }, {
241
+ regex: /^\s*reset/i,
242
+ map: () => ({ command: 'reset' as const })
243
+ }, {
244
+ regex: /^\s*heal/i,
245
+ map: () => ({ command: 'heal' as const })
246
+ }, {
247
+ regex: /^\s*exit/i,
248
+ map: () => ({ command: 'exit' as const })
249
+ }, {
250
+ regex: /^\s*([^\d\s+-]+)\s*([+-]?\d+)/,
251
+ map: (match: RegExpMatchArray) => {
252
+ if (match[2].match(/^[+-]/)) {
253
+ return { command: 'mod-hp' as const, tag: match[1], value: parseInt(match[2]) };
254
+ } else {
255
+ return { command: 'set-hp' as const, tag: match[1], value: parseInt(match[2]) };
256
+ }
257
+ }
258
+ }, {
259
+ regex: /^\s*(\S+)\s+[tT]\s*([+-]?\d+)/,
260
+ map: (match: RegExpMatchArray) => {
261
+ if (match[2].match(/^[+-]/)) {
262
+ return { command: 'mod-thp' as const, tag: match[1], value: parseInt(match[2]) };
263
+ } else {
264
+ return { command: 'set-thp' as const, tag: match[1], value: parseInt(match[2]) };
265
+ }
266
+ }
267
+ }, {
268
+ regex: /^\s*(\S+)\s+[iI]\s*([+-]?\d+)/,
269
+ map: (match: RegExpMatchArray) => {
270
+ if (match[2].match(/^[+-]/)) {
271
+ return { command: 'mod-init' as const, tag: match[1], value: parseInt(match[2]) };
272
+ } else {
273
+ return { command: 'set-init' as const, tag: match[1], value: parseInt(match[2]) };
274
+ }
275
+ },
276
+ }, {
277
+ regex: /^\s*(\S+)\s+[sS]?\s*=?"([^"]*)"/,
278
+ map: (match: RegExpMatchArray) => {
279
+ return { command: 'set-conditions' as const, tag: match[1], value: match[2] };
280
+ },
281
+ }, {
282
+ regex: /^\s*(\S+)\s+[sS]?\s*\+"([^"]*)"/,
283
+ map: (match: RegExpMatchArray) => {
284
+ return { command: 'add-conditions' as const, tag: match[1], value: match[2] };
285
+ },
286
+ }, {
287
+ regex: /^\s*(\S+)\s+[sS]?\s*-"([^"]*)"/,
288
+ map: (match: RegExpMatchArray) => {
289
+ return { command: 'rem-conditions' as const, tag: match[1], value: match[2] };
290
+ },
291
+ }, {
292
+ regex: /^\s*(\S+)\s+[sS]?\s*\+\+"([^"]*)"/,
293
+ map: (match: RegExpMatchArray) => {
294
+ return { command: 'inc-conditions' as const, tag: match[1], value: match[2] };
295
+ },
296
+ }, {
297
+ regex: /^\s*(\S+)\s+[sS]?\s*--"([^"]*)"/,
298
+ map: (match: RegExpMatchArray) => {
299
+ return { command: 'dec-conditions' as const, tag: match[1], value: match[2] };
300
+ },
301
+ }, {
302
+ regex: /^\s*(\S+)\s+[cC]/,
303
+ map: (match: RegExpMatchArray) => ({ command: 'check' as const, tag: match[1] }),
304
+ }, {
305
+ regex: /^\s*(\S+)\s+[aA]/,
306
+ map: (match: RegExpMatchArray) => ({ command: 'arrow' as const, tag: match[1] }),
307
+ }, {
308
+ regex: /^\s*(\S+)\s+[eE]/,
309
+ map: (match: RegExpMatchArray) => ({ command: 'empty' as const, tag: match[1] }),
310
+ }, {
311
+ regex: /^\s*(\S+)\s+[xX]/,
312
+ map: (match: RegExpMatchArray) => ({ command: 'cross' as const, tag: match[1] }),
313
+ }, {
314
+ regex: /^\s*(\S+)\s+[dD]/,
315
+ map: (match: RegExpMatchArray) => ({ command: 'delete' as const, tag: match[1] }),
316
+ }, {
317
+ regex: /^\s*(\S+)\s+[wW]/,
318
+ map: (match: RegExpMatchArray) => ({ command: 'wait' as const, tag: match[1] }),
319
+ }];
320
+
321
+ const actions: ReturnType<typeof commands[number]['map']>[] = [];
322
+
323
+ while (true) {
324
+ const command = commands.find((c) => input.match(c.regex));
325
+
326
+ if (!command) {
327
+ break;
328
+ }
329
+
330
+ const match = input.match(command.regex);
222
331
 
223
- if (hpmod) {
224
- const file = await fs.readFile(argument, { encoding: 'utf-8' });
332
+ if (!match) {
333
+ break;
334
+ }
225
335
 
226
- const tracker = parseTracker(file);
336
+ const action = command.map(match);
227
337
 
228
- const index = tracker.findIndex((c) => c.alias === hpmod[1])
338
+ input = input.slice(match[0].length);
229
339
 
230
- if (index >= 0) {
231
- let hp = tracker[index].hp;
232
- let thp = tracker[index].temphp;
340
+ actions.push(action)
341
+ }
233
342
 
234
- if (hpmod[2].match(/^[+-]/)) {
235
- const delta = parseInt(hpmod[2])
343
+ const file = await fs.readFile(argument, { encoding: 'utf-8' });
236
344
 
237
- if (delta < 0) {
238
- thp += delta;
239
- hp += Math.min(thp, 0)
345
+ let tracker = parseTracker(file);
346
+
347
+ for (const action of actions) {
348
+ if (action.command === 'block') {
349
+ const sorted = [...tracker].sort((a, b) => (a.init === b.init) ? (b.foe ? 1 : 0) - (a.foe ? 1 : 0) : b.init - a.init);
350
+
351
+ const blocks = [[sorted[0]]];
352
+
353
+ for (let i=1; i < sorted.length; ++i) {
354
+ if (sorted[i].foe === blocks[blocks.length-1][0].foe) {
355
+ blocks[blocks.length-1].push(sorted[i])
240
356
  } else {
241
- hp += delta;
357
+ blocks.push([sorted[i]])
358
+ }
359
+ }
360
+
361
+ for (let i=0; i < blocks.length; ++i) {
362
+ if (i < action.index) {
363
+ blocks[i].forEach((b) => b.state = 'check')
364
+ } else if (i === action.index) {
365
+ blocks[i].forEach((b) => b.state = 'arrow')
366
+ } else {
367
+ blocks[i].forEach((b) => b.state = 'empty')
242
368
  }
243
- } else {
244
- hp = parseInt(hpmod[2]);
245
369
  }
370
+ } else if (action.command === 'wait') {
371
+ const sorted = [...tracker].sort((a, b) => (a.init === b.init) ? (b.foe ? 1 : 0) - (a.foe ? 1 : 0) : b.init - a.init);
246
372
 
247
- tracker[index].temphp = Math.max(0, thp)
248
- tracker[index].hp = Math.min(tracker[index].maxhp, Math.max(0, hp))
373
+ const blocks = [[sorted[0]]];
249
374
 
250
- await fs.writeFile(argument, JSON.stringify(tracker, undefined, 2), { encoding: 'utf-8' })
251
- }
252
- }
375
+ for (let i=1; i < sorted.length; ++i) {
376
+ if (sorted[i].foe === blocks[blocks.length-1][0].foe) {
377
+ blocks[blocks.length-1].push(sorted[i])
378
+ } else {
379
+ blocks.push([sorted[i]])
380
+ }
381
+ }
253
382
 
254
- const thpmod = input.match(/(\S+)\s+[tT]\s*([+-]?\d+)/);
383
+ const blockIndex = blocks.findIndex((b) => b.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag))
384
+ const block = blocks[blockIndex + 1]
385
+ const item = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)
255
386
 
256
- if (thpmod) {
257
- const file = await fs.readFile(argument, { encoding: 'utf-8' });
387
+ if (block && item) {
388
+ item.init = (block[block.length - 1]?.init ?? 0) - 1;
389
+ }
390
+ } else if (action.command === 'check') {
391
+ const item = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)
258
392
 
259
- const tracker = parseTracker(file);
393
+ if (item) {
394
+ item.state = "check"
395
+ }
396
+ } else if (action.command === 'arrow') {
397
+ const item = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)
260
398
 
261
- const index = tracker.findIndex((c) => c.alias === thpmod[1])
399
+ if (item) {
400
+ item.state = "arrow"
401
+ }
402
+ } else if (action.command === 'empty') {
403
+ const item = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)
262
404
 
263
- if (index >= 0) {
264
- let thp = tracker[index].temphp;
405
+ if (item) {
406
+ item.state = "empty"
407
+ }
408
+ } else if (action.command === 'cross') {
409
+ const item = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)
265
410
 
266
- if (thpmod[2].match(/^[+-]/)) {
267
- thp += parseInt(thpmod[2]);
268
- } else {
269
- thp = parseInt(thpmod[2]);
411
+ if (item) {
412
+ item.state = "cross"
413
+ }
414
+ } else if (action.command === 'delete') {
415
+ tracker = tracker.filter((c) => !(c.id === action.tag || c.alias === action.tag || c.name == action.tag)).map((p) => ({
416
+ ...p,
417
+ children: p.children.filter((c) => !(c.id === action.tag || c.alias === action.tag || c.name == action.tag)),
418
+ }))
419
+ } else if (action.command === 'exit') {
420
+ process.exit(0)
421
+ } else if (action.command === 'heal') {
422
+ tracker.forEach((char) => {
423
+ char.hp = char.maxhp
424
+
425
+ char.children.forEach((child) => {
426
+ child.hp = child.maxhp
427
+ })
428
+ })
429
+ } else if (action.command === 'reset') {
430
+ tracker.forEach((char) => {
431
+ char.state = 'empty'
432
+ })
433
+ } else if (action.command === 'set-thp') {
434
+ const item = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag) ?? (
435
+ tracker.flatMap((c) => c.children).find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)
436
+ )
437
+
438
+ if (item) {
439
+ item.temphp = Math.max(0, action.value)
440
+ }
441
+ } else if (action.command === 'set-hp') {
442
+ const item = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag) ?? (
443
+ tracker.flatMap((c) => c.children).find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)
444
+ )
445
+
446
+ if (item) {
447
+ item.hp = Math.min(item.maxhp, Math.max(0, action.value))
448
+ }
449
+ } else if (action.command === 'set-init') {
450
+ const item = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)
451
+
452
+ if (item) {
453
+ item.init = Math.max(0, action.value)
454
+ }
455
+ } else if (action.command === 'set-conditions') {
456
+ const item = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag) ?? (
457
+ tracker.flatMap((c) => c.children).find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)
458
+ )
459
+
460
+ if (item) {
461
+ item.conditions = action.value.trim()
462
+ }
463
+ } else if (action.command === 'add-conditions') {
464
+ const item = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag) ?? (
465
+ tracker.flatMap((c) => c.children).find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)
466
+ )
467
+
468
+ if (item) {
469
+ const conditions = [...item.conditions.split(',').map((c) => c.toLowerCase().trim()).filter((c) => c.length), action.value.toLowerCase().trim()];
470
+
471
+ item.conditions = conditions.join(', ')
472
+ }
473
+ } else if (action.command === 'rem-conditions') {
474
+ const item = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag) ?? (
475
+ tracker.flatMap((c) => c.children).find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)
476
+ )
477
+
478
+ if (item) {
479
+ const conditions = item.conditions.split(',').map((c) => c.toLowerCase().trim()).filter((c) => c.length).filter((c) => !c.includes(action.value.toLowerCase().trim()));
480
+
481
+ item.conditions = conditions.join(', ')
482
+ }
483
+ } else if (action.command === 'inc-conditions') {
484
+ const item = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag) ?? (
485
+ tracker.flatMap((c) => c.children).find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)
486
+ )
487
+
488
+ if (item) {
489
+ const conditions = item.conditions.split(',').map((c) => c.toLowerCase().trim()).filter((c) => c.length).map((c) => {
490
+ if (c.includes(action.value.toLowerCase().trim())) {
491
+ const match = c.match(/(.*)\s+(\d+)/);
492
+
493
+ if (match && match[1] && match[2]) {
494
+ return `${match[1].trim()} ${parseInt(match[2]) + 1}`
495
+ }
496
+ }
497
+
498
+ return c;
499
+ });
500
+
501
+ item.conditions = conditions.join(', ')
502
+ }
503
+ } else if (action.command === 'dec-conditions') {
504
+ const item = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag) ?? (
505
+ tracker.flatMap((c) => c.children).find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)
506
+ )
507
+
508
+ if (item) {
509
+ const conditions = item.conditions.split(',').map((c) => c.toLowerCase().trim()).filter((c) => c.length).map((c) => {
510
+ if (c.includes(action.value.toLowerCase().trim())) {
511
+ const match = c.match(/(.*)\s+(\d+)/);
512
+
513
+ if (match && match[1] && match[2]) {
514
+ if (parseInt(match[2]) > 1) {
515
+ return `${match[1].trim()} ${parseInt(match[2]) - 1}`
516
+ } else {
517
+ return match[1].trim()
518
+ }
519
+ }
520
+ }
521
+
522
+ return c;
523
+ });
524
+
525
+ item.conditions = conditions.join(', ')
526
+ }
527
+ } else if (action.command === 'mod-thp') {
528
+ const item = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag) ?? (
529
+ tracker.flatMap((c) => c.children).find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)
530
+ )
531
+
532
+ if (item) {
533
+ item.temphp = Math.max(0, item.temphp + action.value)
270
534
  }
535
+ } else if (action.command === 'mod-hp') {
536
+ const item = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag) ?? (
537
+ tracker.flatMap((c) => c.children).find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)
538
+ )
271
539
 
272
- tracker[index].temphp = Math.max(0, thp)
540
+ if (item) {
541
+ let hp = item.hp;
542
+ let thp = item.temphp;
273
543
 
274
- await fs.writeFile(argument, JSON.stringify(tracker, undefined, 2), { encoding: 'utf-8' })
544
+ if (action.value < 0) {
545
+ thp += action.value;
546
+ hp += Math.min(thp, 0)
547
+ } else {
548
+ hp += action.value;
549
+ }
550
+
551
+ item.temphp = Math.max(0, thp)
552
+ item.hp = Math.min(item.maxhp, Math.max(0, hp))
553
+ }
554
+ } else if (action.command === 'mod-init') {
555
+ const item = tracker.find((c) => c.id === action.tag || c.alias === action.tag || c.name == action.tag)
556
+
557
+ if (item) {
558
+ item.init = Math.max(0, item.init + action.value)
559
+ }
275
560
  }
276
561
  }
562
+
563
+ await fs.writeFile(argument, JSON.stringify(tracker, undefined, 2), { encoding: 'utf-8' })
277
564
  }
278
565
  })(),
279
566
  ])
package/status.txt DELETED
@@ -1,5 +0,0 @@
1
- ## Status: Player Characters
2
- Асотил (64/64 HP):
3
- Брон (24/24 HP):
4
- Нтаанди (20/20 HP):
5
- Перитон (22/22 HP):
package/tracker.json DELETED
@@ -1,62 +0,0 @@
1
- [
2
- {
3
- "init": 19,
4
- "foe": false,
5
- "state": "empty",
6
- "name": "Перитон",
7
- "alias": "pe",
8
- "hp": 22,
9
- "temphp": 0,
10
- "maxhp": 22,
11
- "conditions": "",
12
- "children": []
13
- },
14
- {
15
- "init": 21,
16
- "foe": false,
17
- "state": "empty",
18
- "name": "Асотил",
19
- "alias": "as",
20
- "hp": 64,
21
- "temphp": 0,
22
- "maxhp": 64,
23
- "conditions": "",
24
- "children": []
25
- },
26
- {
27
- "init": 19,
28
- "foe": true,
29
- "state": "empty",
30
- "name": "Псина",
31
- "alias": "hnd",
32
- "hp": 70,
33
- "temphp": 0,
34
- "maxhp": 80,
35
- "conditions": "-1 to AC and saves",
36
- "children": []
37
- },
38
- {
39
- "init": 18,
40
- "foe": false,
41
- "state": "empty",
42
- "name": "Брон",
43
- "alias": "br",
44
- "hp": 24,
45
- "temphp": 0,
46
- "maxhp": 24,
47
- "conditions": "hidden",
48
- "children": []
49
- },
50
- {
51
- "init": 25,
52
- "foe": false,
53
- "state": "arrow",
54
- "name": "Нтаанди",
55
- "alias": "nt",
56
- "hp": 13,
57
- "temphp": 0,
58
- "maxhp": 20,
59
- "conditions": "",
60
- "children": []
61
- }
62
- ]