pf2e-sage-stats 0.2.1 → 0.2.3
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 +9 -0
- package/dist/app.js +114 -5
- package/dist/index.js +126 -0
- package/package.json +1 -1
- package/src/app.ts +172 -6
- package/src/index.ts +133 -1
- package/status.txt +5 -0
- package/tracker.json +62 -0
package/README.md
CHANGED
|
@@ -96,3 +96,12 @@ sage! macro set name="perception" dice="[{{p}::out:m} {flat::{f:none}:} {d:1}d20
|
|
|
96
96
|
```
|
|
97
97
|
sage! macro set name="lore" dice="[{{p}::out:m} {flat::{f:none}:} {d:1}d20+{{p}::lore.{0}:{a:0}} {m} {v} {{p}::name:} (mod `{m:+0}`) {{p}::lore.{0}.info:} {{p}::lore.{0}.name:} Lore Check {utils::{t:known}:vs} {{t:none}::name:} {utils::{t:known}:`{r:Spell} DC ({c:+0} DC) `} {dc} {utils::{t:known}:dc} {{t:none}::dc.{r:spells}:} {c} {...}]" cat=Skills tier=Server -y
|
|
98
98
|
```
|
|
99
|
+
```
|
|
100
|
+
sage! macro set name="assurance" dice="[{{p}::out:m} {flat::{f:none}:} (10)d20+{0:0}-10 {v} {{p}::name:} Assurance Check {utils::{t:known}:vs} {{t:none}::name:} {utils::{t:known}:`{r:Spell} DC ({c:+0} DC) `} {dc} {utils::{t:known}:dc} {{t:none}::dc.{r:spells}:} {c} {...}]" cat=Skills tier=Server -y
|
|
101
|
+
```
|
|
102
|
+
```
|
|
103
|
+
sage! macro set name="skill" dice="[{{p}::out:m} {flat::{f:none}:} d20+{0:0} {v} {{p}::name:} Skill Check {utils::{t:known}:vs} {{t:none}::name:} {utils::{t:known}:`{r:Spell} DC ({c:+0} DC) `} {dc} {utils::{t:known}:dc} {{t:none}::dc.{r:spells}:} {c} {...}]" cat=Skills tier=Server -y
|
|
104
|
+
```
|
|
105
|
+
```
|
|
106
|
+
sage! macro set name="secret" dice="[{{p}::out:m} {flat::{f:none}:} d20+{{p}::{0}:{a:0}} {v} {{p}::name:} Secret {0} Check {utils::{t:known}:vs} {{t:none}::name:} {utils::{t:known}:`{r:Spell} DC ({c:+0} DC) `} {dc} {utils::{t:known}:dc} {{t:none}::dc.{r:spells}:} {c} {...}]" cat=Skills tier=Server -y
|
|
107
|
+
```
|
package/dist/app.js
CHANGED
|
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.sortFolder = exports.formatHP = exports.formatTable = exports.formatCommand = exports.formatTSV = exports.flatten = exports.parseStatblock = exports.parseJSON = exports.formatJSON = exports.fromatMap = exports.adjustmentMap = exports.prediceateMap = exports.stub = void 0;
|
|
15
|
+
exports.sortFolder = exports.formatTracker = exports.newtracker = exports.formatHP = exports.formatTable = exports.formatCommand = exports.formatTSV = exports.flatten = exports.parseStatblock = exports.parseTracker = exports.parseJSON = exports.formatJSON = exports.fromatMap = exports.adjustmentMap = exports.prediceateMap = exports.stub = void 0;
|
|
16
16
|
const zod_1 = __importDefault(require("zod"));
|
|
17
17
|
const tsv_1 = require("tsv");
|
|
18
18
|
const lodash_1 = require("lodash");
|
|
@@ -63,6 +63,7 @@ const schema = zod_1.default.object({
|
|
|
63
63
|
lores: zod_1.default.record(zod_1.default.string(), zod_1.default.object({
|
|
64
64
|
mod: zod_1.default.coerce.number().default(0),
|
|
65
65
|
name: zod_1.default.string(),
|
|
66
|
+
info: zod_1.default.array(zod_1.default.string()).default([]),
|
|
66
67
|
})).default({}),
|
|
67
68
|
melee: zod_1.default.record(zod_1.default.string(), zod_1.default.object({
|
|
68
69
|
mod: zod_1.default.coerce.number().default(0),
|
|
@@ -178,6 +179,26 @@ exports.stub = {
|
|
|
178
179
|
},
|
|
179
180
|
skillInfo: {},
|
|
180
181
|
};
|
|
182
|
+
const childSchema = zod_1.default.object({
|
|
183
|
+
name: zod_1.default.string().default(""),
|
|
184
|
+
alias: zod_1.default.string().default(""),
|
|
185
|
+
hp: zod_1.default.coerce.number().default(0),
|
|
186
|
+
temphp: zod_1.default.coerce.number().default(0),
|
|
187
|
+
maxhp: zod_1.default.coerce.number().default(10),
|
|
188
|
+
conditions: zod_1.default.string().default(''),
|
|
189
|
+
});
|
|
190
|
+
const charSchema = zod_1.default.object({
|
|
191
|
+
init: zod_1.default.coerce.number().default(0),
|
|
192
|
+
foe: zod_1.default.boolean().default(false),
|
|
193
|
+
state: zod_1.default.union([zod_1.default.literal('empty'), zod_1.default.literal('arrow'), zod_1.default.literal('check'), zod_1.default.literal('cross')]).default('empty'),
|
|
194
|
+
name: zod_1.default.string().default(""),
|
|
195
|
+
alias: zod_1.default.string().default(""),
|
|
196
|
+
hp: zod_1.default.coerce.number().default(0),
|
|
197
|
+
temphp: zod_1.default.coerce.number().default(0),
|
|
198
|
+
maxhp: zod_1.default.coerce.number().default(10),
|
|
199
|
+
conditions: zod_1.default.string().default(''),
|
|
200
|
+
children: zod_1.default.array(childSchema).default([]),
|
|
201
|
+
});
|
|
181
202
|
exports.prediceateMap = Object.assign(Object.assign({}, ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20].map((v) => ([`${v}`, `d20 >= ${v} flat;`])).reduce((p, [k, v]) => { p[k] = v; return p; }, {}))), { c: 'd20 >= 5 flat;', concealed: 'd20 >= 5 flat;', h: 'd20 >= 11 flat;', hidden: 'd20 >= 11 flat;' });
|
|
182
203
|
const adjustmentMap = () => ({
|
|
183
204
|
'incredibly-easy': '`(Incredibly Easy)`',
|
|
@@ -197,6 +218,8 @@ const formatJSON = (stats) => JSON.stringify(stats, undefined, 2);
|
|
|
197
218
|
exports.formatJSON = formatJSON;
|
|
198
219
|
const parseJSON = (json) => schema.parse(JSON.parse(json));
|
|
199
220
|
exports.parseJSON = parseJSON;
|
|
221
|
+
const parseTracker = (json) => zod_1.default.array(charSchema).parse(JSON.parse(json));
|
|
222
|
+
exports.parseTracker = parseTracker;
|
|
200
223
|
const locateName = (statblock) => {
|
|
201
224
|
const regex = /^([^()]+?)(\s+\(\d+\))?\s+\S+\s+(-?\d+)$/m;
|
|
202
225
|
const match = statblock.match(regex);
|
|
@@ -227,7 +250,7 @@ const locateInts = (statblock, section, alias, signed = true) => {
|
|
|
227
250
|
]);
|
|
228
251
|
}
|
|
229
252
|
}
|
|
230
|
-
return output.reduce((p, [key, name, value]) => { p[key] = { mod: value, name: name }; return p; }, {});
|
|
253
|
+
return output.reduce((p, [key, name, value]) => { p[key] = { mod: value, name: name, info: [] }; return p; }, {});
|
|
231
254
|
};
|
|
232
255
|
const locateIntsAfter = (statblock, alias, signed = true) => {
|
|
233
256
|
const regex = new RegExp(`${alias}([\\s\\S]*?)(${signed ? '[+-]' : '-?'}\\d+)`, 'g');
|
|
@@ -244,7 +267,7 @@ const locateIntsAfter = (statblock, alias, signed = true) => {
|
|
|
244
267
|
]);
|
|
245
268
|
}
|
|
246
269
|
}
|
|
247
|
-
return output.reduce((p, [key, name, value]) => { p[key] = { mod: value, name: name }; return p; }, {});
|
|
270
|
+
return output.reduce((p, [key, name, value]) => { p[key] = { mod: value, name: name, info: [] }; return p; }, {});
|
|
248
271
|
};
|
|
249
272
|
const locateStrikes = (statblock, alias) => {
|
|
250
273
|
const regex = new RegExp(`${alias}(\\s+\\[.+\\])?\\s+(.+?)\\s+(\\(x\\d+\\)\\s+)?([+-]\\d+)\\s*([\\S\\s]*?),\\s+(Damage|Effect)\\s+(\\S+\\s+\\S+.*?)(?=$|\\s+Melee|\\s+Ranged)`, 'gm');
|
|
@@ -340,6 +363,7 @@ const dc2DC = (secret, bump) => ([key, value]) => [`dc.${key}`, secret ? `||${va
|
|
|
340
363
|
const mod2DC = (secret, bump) => ([key, value]) => [`dc.${key}`, secret ? `||${10 + value + bump}||` : `${10 + value + bump}`];
|
|
341
364
|
const toLoreMod = ([key, value]) => [`lore.${key}`, value.mod];
|
|
342
365
|
const toLoreName = ([key, value]) => [`lore.${key}.name`, value.name];
|
|
366
|
+
const toLoreInfo = ([key, value]) => [`lore.${key}.info`, `\`(${value.info.join(', ')})\``];
|
|
343
367
|
const toMelee = ([key, value]) => [
|
|
344
368
|
[`melee.${key}`, value.mod],
|
|
345
369
|
[`melee.${key}.desc`, value.desc],
|
|
@@ -364,6 +388,11 @@ const flatten = (stats, secretDC = false, defaultSkills = false, recallDC = fals
|
|
|
364
388
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
365
389
|
const untrained = stats.untrained;
|
|
366
390
|
const skills = defaultSkills ? Object.assign({ acrobatics: untrained + stats.attributes.dexterity, arcana: untrained + stats.attributes.intelligence, athletics: untrained + stats.attributes.strength, crafting: untrained + stats.attributes.intelligence, deception: untrained + stats.attributes.charisma, diplomacy: untrained + stats.attributes.charisma, intimidation: untrained + stats.attributes.charisma, medicine: untrained + stats.attributes.wisdom, nature: untrained + stats.attributes.wisdom, occultism: untrained + stats.attributes.intelligence, performance: untrained + stats.attributes.charisma, religion: untrained + stats.attributes.wisdom, society: untrained + stats.attributes.intelligence, stealth: untrained + stats.attributes.dexterity, survival: untrained + stats.attributes.wisdom, thievery: untrained + stats.attributes.dexterity }, stats.skills) : stats.skills;
|
|
391
|
+
const lores = defaultSkills ? Object.assign(Object.assign({}, stats.lores), { other: {
|
|
392
|
+
mod: untrained + stats.attributes.intelligence,
|
|
393
|
+
name: "Other",
|
|
394
|
+
info: ['untrained']
|
|
395
|
+
} }) : stats.lores;
|
|
367
396
|
let melee = (0, lodash_1.entries)(stats.melee).map(([k, v]) => {
|
|
368
397
|
var _a;
|
|
369
398
|
return [k, {
|
|
@@ -413,8 +442,9 @@ const flatten = (stats, secretDC = false, defaultSkills = false, recallDC = fals
|
|
|
413
442
|
['ref', stats.saves.reflex + ((_d = stats.bump.saves) !== null && _d !== void 0 ? _d : stats.bump.default)],
|
|
414
443
|
['perception', stats.perception + ((_e = stats.bump.perception) !== null && _e !== void 0 ? _e : stats.bump.default)],
|
|
415
444
|
...(0, lodash_1.entries)(skills.performance ? Object.assign(Object.assign({}, skills), { perfomance: skills.performance }) : skills).map(([k, v]) => { var _a; return [k, v + ((_a = stats.bump.skills) !== null && _a !== void 0 ? _a : stats.bump.default)]; }),
|
|
416
|
-
...(0, lodash_1.entries)(
|
|
417
|
-
...(0, lodash_1.entries)(
|
|
445
|
+
...(0, lodash_1.entries)(lores).map(toLoreMod).map(([k, v]) => { var _a; return [k, v + ((_a = stats.bump.skills) !== null && _a !== void 0 ? _a : stats.bump.default)]; }),
|
|
446
|
+
...(0, lodash_1.entries)(lores).map(toLoreName),
|
|
447
|
+
...(0, lodash_1.entries)(lores).filter(([, v]) => v.info.length > 0).map(toLoreInfo),
|
|
418
448
|
...melee.flatMap(toMelee),
|
|
419
449
|
...ranged.flatMap(toRanged),
|
|
420
450
|
...(0, lodash_1.entries)(stats.extra),
|
|
@@ -485,6 +515,85 @@ const formatHP = (arg) => {
|
|
|
485
515
|
return _formatHP(negative ? maxhp - hp : hp, maxhp);
|
|
486
516
|
};
|
|
487
517
|
exports.formatHP = formatHP;
|
|
518
|
+
const newtracker = (status) => {
|
|
519
|
+
var _a, _b;
|
|
520
|
+
const regex = /((.+)\s+\((\d+)\/(\d+)\s+HP\):(.*)(\n>(.+)\s+\((\d+)\/(\d+)\s+HP\):(.*))*)/gm;
|
|
521
|
+
const match = status.match(regex);
|
|
522
|
+
const tracker = zod_1.default.array(charSchema).parse([]);
|
|
523
|
+
for (const m of match !== null && match !== void 0 ? match : []) {
|
|
524
|
+
const mmatch = m.match(/([^>]+)\s+\((\d+)\/(\d+)\s+HP\):(.*)/);
|
|
525
|
+
const cmatch = m.match(/>\s+(\S+)\s+\((\d+)\/(\d+)\s+HP\):(.*)/g);
|
|
526
|
+
const char = charSchema.parse({
|
|
527
|
+
name: mmatch === null || mmatch === void 0 ? void 0 : mmatch[1],
|
|
528
|
+
hp: mmatch === null || mmatch === void 0 ? void 0 : mmatch[2],
|
|
529
|
+
maxhp: mmatch === null || mmatch === void 0 ? void 0 : mmatch[3],
|
|
530
|
+
conditions: (_a = mmatch === null || mmatch === void 0 ? void 0 : mmatch[4]) === null || _a === void 0 ? void 0 : _a.trim(),
|
|
531
|
+
});
|
|
532
|
+
for (const c of cmatch !== null && cmatch !== void 0 ? cmatch : []) {
|
|
533
|
+
const fmatch = c.match(/>\s+(\S+)\s+\((\d+)\/(\d+)\s+HP\):(.*)/);
|
|
534
|
+
const child = childSchema.parse({
|
|
535
|
+
name: fmatch === null || fmatch === void 0 ? void 0 : fmatch[1],
|
|
536
|
+
hp: fmatch === null || fmatch === void 0 ? void 0 : fmatch[2],
|
|
537
|
+
maxhp: fmatch === null || fmatch === void 0 ? void 0 : fmatch[3],
|
|
538
|
+
conditions: (_b = fmatch === null || fmatch === void 0 ? void 0 : fmatch[4]) === null || _b === void 0 ? void 0 : _b.trim(),
|
|
539
|
+
});
|
|
540
|
+
char.children.push(child);
|
|
541
|
+
}
|
|
542
|
+
tracker.push(char);
|
|
543
|
+
}
|
|
544
|
+
return tracker;
|
|
545
|
+
};
|
|
546
|
+
exports.newtracker = newtracker;
|
|
547
|
+
const formatTracker = (tracker) => {
|
|
548
|
+
const nameLength = tracker.reduce((p, c) => {
|
|
549
|
+
const length = c.children.reduce((pp, cc) => pp > cc.name.length + 1 ? pp : cc.name.length + 1, c.name.length);
|
|
550
|
+
return p > length ? p : length;
|
|
551
|
+
}, 0);
|
|
552
|
+
const hpLength = tracker.reduce((p, c) => {
|
|
553
|
+
const hpLength = c.maxhp.toString().length + 1 + c.hp.toString().length + (c.temphp > 1 ? c.temphp.toString().length + 1 : 0);
|
|
554
|
+
const length = c.children.reduce((pp, cc) => {
|
|
555
|
+
const hhpLength = cc.maxhp.toString().length + 1 + cc.hp.toString().length + (cc.temphp > 1 ? cc.temphp.toString().length + 1 : 0);
|
|
556
|
+
return pp > hhpLength ? pp : hhpLength;
|
|
557
|
+
}, hpLength);
|
|
558
|
+
return p > length ? p : length;
|
|
559
|
+
}, 0);
|
|
560
|
+
const stateMap = {
|
|
561
|
+
empty: ':emptynode:',
|
|
562
|
+
arrow: ':arrownode:',
|
|
563
|
+
check: ':checknode:',
|
|
564
|
+
cross: ':crossnode:',
|
|
565
|
+
};
|
|
566
|
+
const hpMap = [
|
|
567
|
+
':beaten:',
|
|
568
|
+
':bruised:',
|
|
569
|
+
':wounded:',
|
|
570
|
+
':limping:',
|
|
571
|
+
':dying:',
|
|
572
|
+
].reverse();
|
|
573
|
+
const lines = tracker.sort((a, b) => (a.init === b.init) ? (b.foe ? 1 : 0) - (a.foe ? 1 : 0) : b.init - a.init).flatMap((char, index) => {
|
|
574
|
+
const ls = [];
|
|
575
|
+
if (tracker[index - 1] && tracker[index - 1].foe !== char.foe) {
|
|
576
|
+
ls.push('');
|
|
577
|
+
}
|
|
578
|
+
const hp = char.foe ? (`-${char.maxhp - char.hp}${char.temphp > 0 ? `+${char.temphp}` : ''}`.padStart(hpLength)) : (`${char.hp}${char.temphp > 0 ? `+${char.temphp}` : ''}/${char.maxhp}`.padStart(hpLength));
|
|
579
|
+
const hpBar = (value, max) => (value <= 0 ? (':crossed:') : value >= max ? (':healthy:') : (hpMap[Math.round((hpMap.length - 1) * value / max)]));
|
|
580
|
+
ls.push((`${stateMap[char.state]}**\` ${char.name.padEnd(nameLength)} ▏${char.alias.padEnd(3)} ▏${hp} \`** ${hpBar(char.hp, char.maxhp)} `));
|
|
581
|
+
if (char.conditions.length > 0) {
|
|
582
|
+
ls.push(`-# ${char.conditions}`);
|
|
583
|
+
}
|
|
584
|
+
for (const child of char.children) {
|
|
585
|
+
const hhp = char.foe ? (`-${child.maxhp - child.hp}${child.temphp > 0 ? `+${child.temphp}` : ''}`.padStart(hpLength)) : (`${child.hp}${child.temphp > 0 ? `+${child.temphp}` : ''}/${child.maxhp}`.padStart(hpLength));
|
|
586
|
+
ls.push((`:smallnode:**\`- ${child.name.padEnd(nameLength - 1)} ▏${child.alias.padEnd(3)} ▏${hhp} \`** ${hpBar(child.hp, child.maxhp)} `));
|
|
587
|
+
if (child.conditions.length > 0) {
|
|
588
|
+
ls.push(`-# ${child.conditions}`);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return ls;
|
|
592
|
+
});
|
|
593
|
+
lines.push(':spacer:');
|
|
594
|
+
return lines.join('\n');
|
|
595
|
+
};
|
|
596
|
+
exports.formatTracker = formatTracker;
|
|
488
597
|
const sortFolder = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
489
598
|
const dir = yield (0, promises_1.readdir)('.');
|
|
490
599
|
const summary = yield Promise.all(dir.map((file) => __awaiter(void 0, void 0, void 0, function* () {
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
10
10
|
});
|
|
11
11
|
};
|
|
12
|
+
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
13
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
14
|
+
var m = o[Symbol.asyncIterator], i;
|
|
15
|
+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
16
|
+
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
17
|
+
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
18
|
+
};
|
|
12
19
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
13
20
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
14
21
|
};
|
|
@@ -16,6 +23,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
16
23
|
const commander_1 = require("commander");
|
|
17
24
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
18
25
|
const path_1 = __importDefault(require("path"));
|
|
26
|
+
const readline_1 = __importDefault(require("readline"));
|
|
19
27
|
const package_json_1 = __importDefault(require("../package.json"));
|
|
20
28
|
const app_1 = require("./app");
|
|
21
29
|
const program = new commander_1.Command(package_json_1.default.name);
|
|
@@ -144,4 +152,122 @@ program.command('command')
|
|
|
144
152
|
const output = option.output ? option.output : path_1.default.join(path_1.default.parse(argument).dir, path_1.default.parse(argument).name + '-command.txt');
|
|
145
153
|
yield promises_1.default.writeFile(output, (0, app_1.formatCommand)(stats, option.secretDC, option.defaultSkills, option.recallDC), { encoding: 'utf-8' });
|
|
146
154
|
}));
|
|
155
|
+
program.command('newtracker')
|
|
156
|
+
.argument('<file>', 'input text file')
|
|
157
|
+
.option('-o, --output <file>', 'output file name')
|
|
158
|
+
.action((argument, option) => __awaiter(void 0, void 0, void 0, function* () {
|
|
159
|
+
const file = yield promises_1.default.readFile(argument, { encoding: 'utf-8' });
|
|
160
|
+
const tracker = (0, app_1.newtracker)(file);
|
|
161
|
+
if (option.output) {
|
|
162
|
+
yield promises_1.default.writeFile(option.output, JSON.stringify(tracker, undefined, 2), { encoding: 'utf-8' });
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
console.log(JSON.stringify(tracker, undefined, 2));
|
|
166
|
+
}
|
|
167
|
+
}));
|
|
168
|
+
program.command('tracker')
|
|
169
|
+
.argument('<file>', 'input text file')
|
|
170
|
+
.option('-o, --output <file>', 'output file name')
|
|
171
|
+
.action((argument, option) => __awaiter(void 0, void 0, void 0, function* () {
|
|
172
|
+
const file = yield promises_1.default.readFile(argument, { encoding: 'utf-8' });
|
|
173
|
+
const tracker = (0, app_1.parseTracker)(file);
|
|
174
|
+
if (option.output) {
|
|
175
|
+
yield promises_1.default.writeFile(option.output, (0, app_1.formatTracker)(tracker), { encoding: 'utf-8' });
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
console.log((0, app_1.formatTracker)(tracker));
|
|
179
|
+
}
|
|
180
|
+
}));
|
|
181
|
+
program.command('track')
|
|
182
|
+
.argument('<file>', 'input text file')
|
|
183
|
+
.option('-o, --output <file>', 'output file name')
|
|
184
|
+
.action((argument, option) => __awaiter(void 0, void 0, void 0, function* () {
|
|
185
|
+
const iterator = promises_1.default.watch(argument);
|
|
186
|
+
const invoke = (filename) => __awaiter(void 0, void 0, void 0, function* () {
|
|
187
|
+
const file = yield promises_1.default.readFile(filename, { encoding: 'utf-8' });
|
|
188
|
+
const tracker = (0, app_1.parseTracker)(file);
|
|
189
|
+
if (option.output) {
|
|
190
|
+
yield promises_1.default.writeFile(option.output, (0, app_1.formatTracker)(tracker), { encoding: 'utf-8' });
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
console.clear();
|
|
194
|
+
console.log((0, app_1.formatTracker)(tracker));
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
yield invoke(argument);
|
|
198
|
+
yield Promise.all([
|
|
199
|
+
(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
200
|
+
var _a, e_1, _b, _c;
|
|
201
|
+
var _d;
|
|
202
|
+
try {
|
|
203
|
+
for (var _e = true, iterator_1 = __asyncValues(iterator), iterator_1_1; iterator_1_1 = yield iterator_1.next(), _a = iterator_1_1.done, !_a; _e = true) {
|
|
204
|
+
_c = iterator_1_1.value;
|
|
205
|
+
_e = false;
|
|
206
|
+
const value = _c;
|
|
207
|
+
yield invoke((_d = value.filename) !== null && _d !== void 0 ? _d : argument);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
211
|
+
finally {
|
|
212
|
+
try {
|
|
213
|
+
if (!_e && !_a && (_b = iterator_1.return)) yield _b.call(iterator_1);
|
|
214
|
+
}
|
|
215
|
+
finally { if (e_1) throw e_1.error; }
|
|
216
|
+
}
|
|
217
|
+
}))(),
|
|
218
|
+
(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
219
|
+
const rl = readline_1.default.createInterface({
|
|
220
|
+
input: process.stdin,
|
|
221
|
+
output: process.stdout
|
|
222
|
+
});
|
|
223
|
+
while (true) {
|
|
224
|
+
yield new Promise((resolve) => setTimeout(resolve, 500));
|
|
225
|
+
const input = yield new Promise((resolve) => (rl.question('>', resolve)));
|
|
226
|
+
const hpmod = input.match(/([^\d+-]+)\s*([+-]?\d+)/);
|
|
227
|
+
if (hpmod) {
|
|
228
|
+
const file = yield promises_1.default.readFile(argument, { encoding: 'utf-8' });
|
|
229
|
+
const tracker = (0, app_1.parseTracker)(file);
|
|
230
|
+
const index = tracker.findIndex((c) => c.alias === hpmod[1]);
|
|
231
|
+
if (index >= 0) {
|
|
232
|
+
let hp = tracker[index].hp;
|
|
233
|
+
let thp = tracker[index].temphp;
|
|
234
|
+
if (hpmod[2].match(/^[+-]/)) {
|
|
235
|
+
const delta = parseInt(hpmod[2]);
|
|
236
|
+
if (delta < 0) {
|
|
237
|
+
thp += delta;
|
|
238
|
+
hp += Math.min(thp, 0);
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
hp += delta;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
hp = parseInt(hpmod[2]);
|
|
246
|
+
}
|
|
247
|
+
tracker[index].temphp = Math.max(0, thp);
|
|
248
|
+
tracker[index].hp = Math.min(tracker[index].maxhp, Math.max(0, hp));
|
|
249
|
+
yield promises_1.default.writeFile(argument, JSON.stringify(tracker, undefined, 2), { encoding: 'utf-8' });
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
const thpmod = input.match(/(\S+)\s+[tT]\s*([+-]?\d+)/);
|
|
253
|
+
if (thpmod) {
|
|
254
|
+
const file = yield promises_1.default.readFile(argument, { encoding: 'utf-8' });
|
|
255
|
+
const tracker = (0, app_1.parseTracker)(file);
|
|
256
|
+
const index = tracker.findIndex((c) => c.alias === thpmod[1]);
|
|
257
|
+
if (index >= 0) {
|
|
258
|
+
let thp = tracker[index].temphp;
|
|
259
|
+
if (thpmod[2].match(/^[+-]/)) {
|
|
260
|
+
thp += parseInt(thpmod[2]);
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
thp = parseInt(thpmod[2]);
|
|
264
|
+
}
|
|
265
|
+
tracker[index].temphp = Math.max(0, thp);
|
|
266
|
+
yield promises_1.default.writeFile(argument, JSON.stringify(tracker, undefined, 2), { encoding: 'utf-8' });
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}))(),
|
|
271
|
+
]);
|
|
272
|
+
}));
|
|
147
273
|
program.parse();
|
package/package.json
CHANGED
package/src/app.ts
CHANGED
|
@@ -49,6 +49,7 @@ const schema = zod.object({
|
|
|
49
49
|
lores: zod.record(zod.string(), zod.object({
|
|
50
50
|
mod: zod.coerce.number().default(0),
|
|
51
51
|
name: zod.string(),
|
|
52
|
+
info: zod.array(zod.string()).default([]),
|
|
52
53
|
})).default({}),
|
|
53
54
|
melee: zod.record(zod.string(), zod.object({
|
|
54
55
|
mod: zod.coerce.number().default(0),
|
|
@@ -168,6 +169,32 @@ export const stub: Required<Schema> = {
|
|
|
168
169
|
skillInfo: {},
|
|
169
170
|
};
|
|
170
171
|
|
|
172
|
+
const childSchema = zod.object({
|
|
173
|
+
name: zod.string().default(""),
|
|
174
|
+
alias: zod.string().default(""),
|
|
175
|
+
hp: zod.coerce.number().default(0),
|
|
176
|
+
temphp: zod.coerce.number().default(0),
|
|
177
|
+
maxhp: zod.coerce.number().default(10),
|
|
178
|
+
conditions: zod.string().default(''),
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
export type ChildSchema = zod.infer<typeof childSchema>;
|
|
182
|
+
|
|
183
|
+
const charSchema = zod.object({
|
|
184
|
+
init: zod.coerce.number().default(0),
|
|
185
|
+
foe: zod.boolean().default(false),
|
|
186
|
+
state: zod.union([zod.literal('empty'), zod.literal('arrow'), zod.literal('check'), zod.literal('cross')]).default('empty'),
|
|
187
|
+
name: zod.string().default(""),
|
|
188
|
+
alias: zod.string().default(""),
|
|
189
|
+
hp: zod.coerce.number().default(0),
|
|
190
|
+
temphp: zod.coerce.number().default(0),
|
|
191
|
+
maxhp: zod.coerce.number().default(10),
|
|
192
|
+
conditions: zod.string().default(''),
|
|
193
|
+
children: zod.array(childSchema).default([]),
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
export type CharSchema = zod.infer<typeof charSchema>;
|
|
197
|
+
|
|
171
198
|
export const prediceateMap: Record<string, string> = {
|
|
172
199
|
...([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20].map((v): [string, string] => (
|
|
173
200
|
[`${v}`, `d20 >= ${v} flat;`]
|
|
@@ -199,6 +226,8 @@ const dcByLevel = [13, 14, 15, 16, 18, 19, 20, 22, 23, 24, 26, 27, 28, 30, 31, 3
|
|
|
199
226
|
export const formatJSON = (stats: Schema): string => JSON.stringify(stats, undefined, 2);
|
|
200
227
|
export const parseJSON = (json: string): Schema => schema.parse(JSON.parse(json));
|
|
201
228
|
|
|
229
|
+
export const parseTracker = (json: string): CharSchema[] => zod.array(charSchema).parse(JSON.parse(json));
|
|
230
|
+
|
|
202
231
|
const locateName = (statblock: string): { name: string; level: number } | null => {
|
|
203
232
|
const regex = /^([^()]+?)(\s+\(\d+\))?\s+\S+\s+(-?\d+)$/m;
|
|
204
233
|
|
|
@@ -221,7 +250,7 @@ const locateInt = <T extends string>(name: T, statblock: string, section: string
|
|
|
221
250
|
return null;
|
|
222
251
|
}
|
|
223
252
|
|
|
224
|
-
const locateInts = (statblock: string, section: string, alias: string, signed: boolean = true): Record<string, { mod: number; name: string; }> => {
|
|
253
|
+
const locateInts = (statblock: string, section: string, alias: string, signed: boolean = true): Record<string, { mod: number; name: string; info: string[] }> => {
|
|
225
254
|
const regex = new RegExp(`(${section}|,)([^,]*?)${alias}\\s+(${signed ? '[+-]' : '-?'}\\d+)`, 'g');
|
|
226
255
|
|
|
227
256
|
let matches: RegExpExecArray | null = null;
|
|
@@ -239,10 +268,10 @@ const locateInts = (statblock: string, section: string, alias: string, signed: b
|
|
|
239
268
|
}
|
|
240
269
|
}
|
|
241
270
|
|
|
242
|
-
return output.reduce<Record<string, { mod: number; name: string; }>>((p, [key, name, value]) => { p[key] = { mod: value, name: name }; return p; }, {});
|
|
271
|
+
return output.reduce<Record<string, { mod: number; name: string; info: string[] }>>((p, [key, name, value]) => { p[key] = { mod: value, name: name, info: [] }; return p; }, {});
|
|
243
272
|
}
|
|
244
273
|
|
|
245
|
-
const locateIntsAfter = (statblock: string, alias: string, signed: boolean = true): Record<string, { mod: number; name: string; }> => {
|
|
274
|
+
const locateIntsAfter = (statblock: string, alias: string, signed: boolean = true): Record<string, { mod: number; name: string; info: string[] }> => {
|
|
246
275
|
const regex = new RegExp(`${alias}([\\s\\S]*?)(${signed ? '[+-]' : '-?'}\\d+)`, 'g');
|
|
247
276
|
|
|
248
277
|
let matches: RegExpExecArray | null = null;
|
|
@@ -260,7 +289,7 @@ const locateIntsAfter = (statblock: string, alias: string, signed: boolean = tru
|
|
|
260
289
|
}
|
|
261
290
|
}
|
|
262
291
|
|
|
263
|
-
return output.reduce<Record<string, { mod: number; name: string; }>>((p, [key, name, value]) => { p[key] = { mod: value, name: name }; return p; }, {});
|
|
292
|
+
return output.reduce<Record<string, { mod: number; name: string; info: string[] }>>((p, [key, name, value]) => { p[key] = { mod: value, name: name, info: [] }; return p; }, {});
|
|
264
293
|
}
|
|
265
294
|
|
|
266
295
|
const locateStrikes = (statblock: string, alias: string): Record<string, Schema['melee'][string]> => {
|
|
@@ -437,6 +466,7 @@ const dc2DC = (secret: boolean, bump: number) => ([key, value]: [string | number
|
|
|
437
466
|
const mod2DC = (secret: boolean, bump: number) => ([key, value]: [string | number, number]): [string, string] => [`dc.${key}`, secret ? `||${10 + value + bump}||` : `${10 + value + bump}`];
|
|
438
467
|
const toLoreMod = ([key, value]: [string | number, { mod: number; name: string; }]): [string, number] => [`lore.${key}`, value.mod];
|
|
439
468
|
const toLoreName = ([key, value]: [string | number, { mod: number; name: string; }]): [string, string] => [`lore.${key}.name`, value.name];
|
|
469
|
+
const toLoreInfo = ([key, value]: [string | number, { mod: number; name: string; info: string[] }]): [string, string] => [`lore.${key}.info`, `\`(${value.info.join(', ')})\``];
|
|
440
470
|
const toMelee = ([key, value]: [string, Schema['melee'][string]]): [string, number | string][] => [
|
|
441
471
|
[`melee.${key}`, value.mod],
|
|
442
472
|
[`melee.${key}.desc`, value.desc],
|
|
@@ -482,6 +512,15 @@ export const flatten = (stats: Schema, secretDC: boolean = false, defaultSkills:
|
|
|
482
512
|
...stats.skills,
|
|
483
513
|
} : stats.skills;
|
|
484
514
|
|
|
515
|
+
const lores = defaultSkills ? {
|
|
516
|
+
...stats.lores,
|
|
517
|
+
other: {
|
|
518
|
+
mod: untrained + stats.attributes.intelligence,
|
|
519
|
+
name: "Other",
|
|
520
|
+
info: ['untrained']
|
|
521
|
+
}
|
|
522
|
+
} : stats.lores
|
|
523
|
+
|
|
485
524
|
let melee = entries(stats.melee).map(([k, v]): [string, typeof v] => [k, {
|
|
486
525
|
mod: v.mod + (stats.bump.attack ?? stats.bump.default),
|
|
487
526
|
desc: v.desc,
|
|
@@ -534,8 +573,9 @@ export const flatten = (stats: Schema, secretDC: boolean = false, defaultSkills:
|
|
|
534
573
|
['ref', stats.saves.reflex + (stats.bump.saves ?? stats.bump.default)],
|
|
535
574
|
['perception', stats.perception + (stats.bump.perception ?? stats.bump.default)],
|
|
536
575
|
...entries(skills.performance ? { ...skills, perfomance: skills.performance } : skills).map(([k, v]) => [k, v + (stats.bump.skills ?? stats.bump.default)]),
|
|
537
|
-
...entries(
|
|
538
|
-
...entries(
|
|
576
|
+
...entries(lores).map(toLoreMod).map(([k, v]) => [k, v + (stats.bump.skills ?? stats.bump.default)]),
|
|
577
|
+
...entries(lores).map(toLoreName),
|
|
578
|
+
...entries(lores).filter(([, v]) => v.info.length > 0).map(toLoreInfo),
|
|
539
579
|
...melee.flatMap(toMelee),
|
|
540
580
|
...ranged.flatMap(toRanged),
|
|
541
581
|
...entries(stats.extra),
|
|
@@ -619,6 +659,132 @@ export const formatHP = (arg: string) => {
|
|
|
619
659
|
return _formatHP(negative ? maxhp - hp : hp, maxhp);
|
|
620
660
|
};
|
|
621
661
|
|
|
662
|
+
export const newtracker = (status: string) => {
|
|
663
|
+
const regex = /((.+)\s+\((\d+)\/(\d+)\s+HP\):(.*)(\n>(.+)\s+\((\d+)\/(\d+)\s+HP\):(.*))*)/gm;
|
|
664
|
+
|
|
665
|
+
const match = status.match(regex);
|
|
666
|
+
|
|
667
|
+
const tracker = zod.array(charSchema).parse([]);
|
|
668
|
+
|
|
669
|
+
for (const m of match ?? []) {
|
|
670
|
+
const mmatch = m.match(/([^>]+)\s+\((\d+)\/(\d+)\s+HP\):(.*)/)
|
|
671
|
+
const cmatch = m.match(/>\s+(\S+)\s+\((\d+)\/(\d+)\s+HP\):(.*)/g)
|
|
672
|
+
|
|
673
|
+
const char = charSchema.parse({
|
|
674
|
+
name: mmatch?.[1],
|
|
675
|
+
hp: mmatch?.[2],
|
|
676
|
+
maxhp: mmatch?.[3],
|
|
677
|
+
conditions: mmatch?.[4]?.trim(),
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
for (const c of cmatch ?? []) {
|
|
681
|
+
const fmatch = c.match(/>\s+(\S+)\s+\((\d+)\/(\d+)\s+HP\):(.*)/)
|
|
682
|
+
|
|
683
|
+
const child = childSchema.parse({
|
|
684
|
+
name: fmatch?.[1],
|
|
685
|
+
hp: fmatch?.[2],
|
|
686
|
+
maxhp: fmatch?.[3],
|
|
687
|
+
conditions: fmatch?.[4]?.trim(),
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
char.children.push(child);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
tracker.push(char);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
return tracker;
|
|
697
|
+
};
|
|
698
|
+
|
|
699
|
+
export const formatTracker = (tracker: CharSchema[]) => {
|
|
700
|
+
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);
|
|
702
|
+
|
|
703
|
+
return p > length ? p : length;
|
|
704
|
+
}, 0);
|
|
705
|
+
|
|
706
|
+
const hpLength = tracker.reduce((p, c) => {
|
|
707
|
+
const hpLength = c.maxhp.toString().length + 1 + c.hp.toString().length + (c.temphp > 1 ? c.temphp.toString().length + 1 : 0);
|
|
708
|
+
|
|
709
|
+
const length = c.children.reduce((pp, cc) => {
|
|
710
|
+
const hhpLength = cc.maxhp.toString().length + 1 + cc.hp.toString().length + (cc.temphp > 1 ? cc.temphp.toString().length + 1 : 0);
|
|
711
|
+
|
|
712
|
+
return pp > hhpLength ? pp : hhpLength;
|
|
713
|
+
}, hpLength);
|
|
714
|
+
|
|
715
|
+
return p > length ? p : length;
|
|
716
|
+
}, 0);
|
|
717
|
+
|
|
718
|
+
const stateMap: Record<CharSchema['state'], string> = {
|
|
719
|
+
empty: ':emptynode:',
|
|
720
|
+
arrow: ':arrownode:',
|
|
721
|
+
check: ':checknode:',
|
|
722
|
+
cross: ':crossnode:',
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
const hpMap = [
|
|
726
|
+
':beaten:',
|
|
727
|
+
':bruised:',
|
|
728
|
+
':wounded:',
|
|
729
|
+
':limping:',
|
|
730
|
+
':dying:',
|
|
731
|
+
].reverse();
|
|
732
|
+
|
|
733
|
+
const lines = tracker.sort((a, b) => (a.init === b.init) ? (b.foe ? 1 : 0) - (a.foe ? 1 : 0) : b.init - a.init).flatMap((char, index) => {
|
|
734
|
+
const ls: string[] = [];
|
|
735
|
+
|
|
736
|
+
if (tracker[index - 1] && tracker[index - 1].foe !== char.foe) {
|
|
737
|
+
ls.push('')
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
const hp = char.foe ? (
|
|
741
|
+
`-${char.maxhp - char.hp}${char.temphp > 0 ? `+${char.temphp}` : ''}`.padStart(hpLength)
|
|
742
|
+
) : (
|
|
743
|
+
`${char.hp}${char.temphp > 0 ? `+${char.temphp}` : ''}/${char.maxhp}`.padStart(hpLength)
|
|
744
|
+
);
|
|
745
|
+
|
|
746
|
+
const hpBar = (value: number, max: number) => (
|
|
747
|
+
value <= 0 ? (
|
|
748
|
+
':crossed:'
|
|
749
|
+
) : value >= max ? (
|
|
750
|
+
':healthy:'
|
|
751
|
+
) : (
|
|
752
|
+
hpMap[Math.round((hpMap.length - 1) * value / max)]
|
|
753
|
+
)
|
|
754
|
+
);
|
|
755
|
+
|
|
756
|
+
ls.push((
|
|
757
|
+
`${stateMap[char.state]}**\` ${char.name.padEnd(nameLength)} ▏${char.alias.padEnd(3)} ▏${hp} \`** ${hpBar(char.hp, char.maxhp)} `
|
|
758
|
+
))
|
|
759
|
+
|
|
760
|
+
if (char.conditions.length > 0) {
|
|
761
|
+
ls.push(`-# ${char.conditions}`)
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
for (const child of char.children) {
|
|
765
|
+
const hhp = char.foe ? (
|
|
766
|
+
`-${child.maxhp - child.hp}${child.temphp > 0 ? `+${child.temphp}` : ''}`.padStart(hpLength)
|
|
767
|
+
) : (
|
|
768
|
+
`${child.hp}${child.temphp > 0 ? `+${child.temphp}` : ''}/${child.maxhp}`.padStart(hpLength)
|
|
769
|
+
);
|
|
770
|
+
|
|
771
|
+
ls.push((
|
|
772
|
+
`:smallnode:**\`- ${child.name.padEnd(nameLength - 1)} ▏${child.alias.padEnd(3)} ▏${hhp} \`** ${hpBar(child.hp, child.maxhp)} `
|
|
773
|
+
))
|
|
774
|
+
|
|
775
|
+
if (child.conditions.length > 0) {
|
|
776
|
+
ls.push(`-# ${child.conditions}`)
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
return ls;
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
lines.push(':spacer:')
|
|
784
|
+
|
|
785
|
+
return lines.join('\n');
|
|
786
|
+
}
|
|
787
|
+
|
|
622
788
|
export const sortFolder = async () => {
|
|
623
789
|
const dir = await readdir('.');
|
|
624
790
|
|
package/src/index.ts
CHANGED
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
import fs from 'fs/promises';
|
|
5
5
|
import path from 'path';
|
|
6
|
+
import readline from 'readline';
|
|
6
7
|
|
|
7
8
|
import pack from '../package.json';
|
|
8
9
|
|
|
9
|
-
import { sortFolder, formatCommand, formatJSON, formatTSV, fromatMap, parseJSON, parseStatblock, stub, adjustmentMap, prediceateMap, formatTable, formatHP } from './app';
|
|
10
|
+
import { sortFolder, formatCommand, formatJSON, formatTSV, fromatMap, parseJSON, parseStatblock, stub, adjustmentMap, prediceateMap, formatTable, formatHP, newtracker, parseTracker, formatTracker } from './app';
|
|
10
11
|
|
|
11
12
|
const program = new Command(pack.name);
|
|
12
13
|
|
|
@@ -147,5 +148,136 @@ program.command('command')
|
|
|
147
148
|
await fs.writeFile(output, formatCommand(stats, option.secretDC, option.defaultSkills, option.recallDC), { encoding: 'utf-8' });
|
|
148
149
|
});
|
|
149
150
|
|
|
151
|
+
program.command('newtracker')
|
|
152
|
+
.argument('<file>', 'input text file')
|
|
153
|
+
.option('-o, --output <file>', 'output file name')
|
|
154
|
+
.action(async (argument, option) => {
|
|
155
|
+
const file = await fs.readFile(argument, { encoding: 'utf-8' });
|
|
156
|
+
|
|
157
|
+
const tracker = newtracker(file);
|
|
158
|
+
|
|
159
|
+
if (option.output) {
|
|
160
|
+
await fs.writeFile(option.output, JSON.stringify(tracker, undefined, 2), { encoding: 'utf-8' })
|
|
161
|
+
} else {
|
|
162
|
+
console.log(JSON.stringify(tracker, undefined, 2));
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
program.command('tracker')
|
|
167
|
+
.argument('<file>', 'input text file')
|
|
168
|
+
.option('-o, --output <file>', 'output file name')
|
|
169
|
+
.action(async (argument, option) => {
|
|
170
|
+
const file = await fs.readFile(argument, { encoding: 'utf-8' });
|
|
171
|
+
|
|
172
|
+
const tracker = parseTracker(file);
|
|
173
|
+
|
|
174
|
+
if (option.output) {
|
|
175
|
+
await fs.writeFile(option.output, formatTracker(tracker), { encoding: 'utf-8' })
|
|
176
|
+
} else {
|
|
177
|
+
console.log(formatTracker(tracker));
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
program.command('track')
|
|
182
|
+
.argument('<file>', 'input text file')
|
|
183
|
+
.option('-o, --output <file>', 'output file name')
|
|
184
|
+
.action(async (argument, option) => {
|
|
185
|
+
const iterator = fs.watch(argument);
|
|
186
|
+
|
|
187
|
+
const invoke = async (filename: string) => {
|
|
188
|
+
const file = await fs.readFile(filename, { encoding: 'utf-8' });
|
|
189
|
+
|
|
190
|
+
const tracker = parseTracker(file);
|
|
191
|
+
|
|
192
|
+
if (option.output) {
|
|
193
|
+
await fs.writeFile(option.output, formatTracker(tracker), { encoding: 'utf-8' })
|
|
194
|
+
} else {
|
|
195
|
+
console.clear();
|
|
196
|
+
console.log(formatTracker(tracker));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
await invoke(argument);
|
|
201
|
+
|
|
202
|
+
await Promise.all([
|
|
203
|
+
(async () => {
|
|
204
|
+
for await (const value of iterator) {
|
|
205
|
+
await invoke(value.filename ?? argument);
|
|
206
|
+
}
|
|
207
|
+
})(),
|
|
208
|
+
(async () => {
|
|
209
|
+
const rl = readline.createInterface({
|
|
210
|
+
input: process.stdin,
|
|
211
|
+
output: process.stdout
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
while (true) {
|
|
215
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
216
|
+
|
|
217
|
+
const input = await new Promise<string>((resolve) => (
|
|
218
|
+
rl.question('>', resolve)
|
|
219
|
+
));
|
|
220
|
+
|
|
221
|
+
const hpmod = input.match(/([^\d+-]+)\s*([+-]?\d+)/);
|
|
222
|
+
|
|
223
|
+
if (hpmod) {
|
|
224
|
+
const file = await fs.readFile(argument, { encoding: 'utf-8' });
|
|
225
|
+
|
|
226
|
+
const tracker = parseTracker(file);
|
|
227
|
+
|
|
228
|
+
const index = tracker.findIndex((c) => c.alias === hpmod[1])
|
|
229
|
+
|
|
230
|
+
if (index >= 0) {
|
|
231
|
+
let hp = tracker[index].hp;
|
|
232
|
+
let thp = tracker[index].temphp;
|
|
233
|
+
|
|
234
|
+
if (hpmod[2].match(/^[+-]/)) {
|
|
235
|
+
const delta = parseInt(hpmod[2])
|
|
236
|
+
|
|
237
|
+
if (delta < 0) {
|
|
238
|
+
thp += delta;
|
|
239
|
+
hp += Math.min(thp, 0)
|
|
240
|
+
} else {
|
|
241
|
+
hp += delta;
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
hp = parseInt(hpmod[2]);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
tracker[index].temphp = Math.max(0, thp)
|
|
248
|
+
tracker[index].hp = Math.min(tracker[index].maxhp, Math.max(0, hp))
|
|
249
|
+
|
|
250
|
+
await fs.writeFile(argument, JSON.stringify(tracker, undefined, 2), { encoding: 'utf-8' })
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const thpmod = input.match(/(\S+)\s+[tT]\s*([+-]?\d+)/);
|
|
255
|
+
|
|
256
|
+
if (thpmod) {
|
|
257
|
+
const file = await fs.readFile(argument, { encoding: 'utf-8' });
|
|
258
|
+
|
|
259
|
+
const tracker = parseTracker(file);
|
|
260
|
+
|
|
261
|
+
const index = tracker.findIndex((c) => c.alias === thpmod[1])
|
|
262
|
+
|
|
263
|
+
if (index >= 0) {
|
|
264
|
+
let thp = tracker[index].temphp;
|
|
265
|
+
|
|
266
|
+
if (thpmod[2].match(/^[+-]/)) {
|
|
267
|
+
thp += parseInt(thpmod[2]);
|
|
268
|
+
} else {
|
|
269
|
+
thp = parseInt(thpmod[2]);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
tracker[index].temphp = Math.max(0, thp)
|
|
273
|
+
|
|
274
|
+
await fs.writeFile(argument, JSON.stringify(tracker, undefined, 2), { encoding: 'utf-8' })
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
})(),
|
|
279
|
+
])
|
|
280
|
+
});
|
|
281
|
+
|
|
150
282
|
|
|
151
283
|
program.parse();
|
package/status.txt
ADDED
package/tracker.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
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
|
+
]
|