@scrthq/runlog 0.0.44 → 0.0.46
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/dist/runlog.js +111 -12
- package/package.json +1 -1
package/dist/runlog.js
CHANGED
|
@@ -26525,6 +26525,9 @@ var EntryBase = {
|
|
|
26525
26525
|
tags: external_exports.array(external_exports.string()).optional().describe("Free tags, for cross-referencing and for filtering results out in some modes."),
|
|
26526
26526
|
points: external_exports.number().int().min(0).optional().describe(
|
|
26527
26527
|
"What a contestant earns for completing this result in a moderated mode. A result with points is a challenge the moderator can award; one without is an effect that lands on everyone."
|
|
26528
|
+
),
|
|
26529
|
+
needs: external_exports.array(Id).optional().describe(
|
|
26530
|
+
"Requirements this result depends on, by id from the pack's `requires`. A run that said it lacks one of them draws again instead of landing here \u2014 a barbell movement for someone without a barbell, an oven dish in a kitchen without one."
|
|
26528
26531
|
)
|
|
26529
26532
|
};
|
|
26530
26533
|
var LookupEntry = external_exports.object({
|
|
@@ -26715,6 +26718,20 @@ var ResourceDef = external_exports.object({
|
|
|
26715
26718
|
}).strict().describe(
|
|
26716
26719
|
"A numeric track the player fills or spends: a progress clock, a word count, training volume, stock on hand."
|
|
26717
26720
|
);
|
|
26721
|
+
var Clock = external_exports.object({
|
|
26722
|
+
kind: external_exports.enum(["stopwatch", "timer"]).describe("A stopwatch counts up from the start of the unit; a timer counts down from `minutes`."),
|
|
26723
|
+
minutes: external_exports.number().positive().max(24 * 60).optional().describe("How long a timer runs. Required for a timer; ignored by a stopwatch."),
|
|
26724
|
+
label: external_exports.string().optional().describe("What the clock is called on screen. Defaults to the unit's name and number."),
|
|
26725
|
+
auto: external_exports.boolean().default(true).describe("Start it when the unit is entered and stop it when the unit closes. Off, the player starts it by hand; it still stops with the unit.")
|
|
26726
|
+
}).strict().refine((c) => c.kind !== "timer" || c.minutes !== void 0, { message: "a timer needs minutes", path: ["minutes"] }).describe("A clock the unit runs: `stopwatch` to time it, `timer` with `minutes` to limit it. The time lands in the log when the unit closes.");
|
|
26727
|
+
var Requirement = external_exports.object({
|
|
26728
|
+
id: Id.describe("Unique among requirements. Table entries name it in `needs`."),
|
|
26729
|
+
label: external_exports.string().min(1).describe("What it is, in the words a person would use: Rocket League, a potter's wheel, an oven."),
|
|
26730
|
+
kind: external_exports.enum(["game", "platform", "software", "equipment", "supplies", "space", "other"]).default("other").describe("What sort of thing it is, so a catalog can say 'needs a game' or 'needs equipment' at a glance."),
|
|
26731
|
+
optional: external_exports.boolean().default(false).describe("Nice to have rather than needed. A player says at the start whether they have it, and results that `need` it are drawn again when they do not."),
|
|
26732
|
+
note: external_exports.string().optional().describe("A line on where to get it, which version, or what will do instead."),
|
|
26733
|
+
url: external_exports.string().url().optional().describe("Where to find it.")
|
|
26734
|
+
}).strict().describe("A physical or software requirement: the game and the system to play it on, a mod or training pack, a wheel, a kitchen, supplies.");
|
|
26718
26735
|
var ChecklistItem = external_exports.union([
|
|
26719
26736
|
external_exports.string().min(1),
|
|
26720
26737
|
external_exports.object({
|
|
@@ -26864,6 +26881,7 @@ var Mode = external_exports.object({
|
|
|
26864
26881
|
).optional().describe(
|
|
26865
26882
|
"Per-unit overrides, for modes with a fixed shape \u2014 'unit three always suffers a consequence', and the like."
|
|
26866
26883
|
),
|
|
26884
|
+
clock: Clock.optional().describe("This mode's clock on every unit, instead of the pack's `unit.clock`."),
|
|
26867
26885
|
moderated: external_exports.object({
|
|
26868
26886
|
contestants: external_exports.object({
|
|
26869
26887
|
min: external_exports.number().int().min(1).default(2).describe("Fewest contestants."),
|
|
@@ -26944,6 +26962,11 @@ var Pack = external_exports.object({
|
|
|
26944
26962
|
author: external_exports.string().optional().describe("Who wrote the game."),
|
|
26945
26963
|
description: external_exports.string().optional().describe("One or two sentences on what the game is."),
|
|
26946
26964
|
homepage: external_exports.string().url().optional().describe("Where to find out more."),
|
|
26965
|
+
category: external_exports.string().min(1).max(40).optional().describe("What kind of thing this is, in a word or two, for a catalog to group by: everyday, games, craft, fitness, cooking, writing\u2026"),
|
|
26966
|
+
tags: external_exports.array(external_exports.string().min(1).max(40)).max(12).optional().describe("Free tags for a catalog to filter by: the game it is for, the hobby, the shape of play. Short, and in the words a person would search for."),
|
|
26967
|
+
requires: external_exports.array(Requirement).max(20).optional().describe(
|
|
26968
|
+
"What a person needs before playing, shown in the catalog and the rulebook: the game and a system that runs it, mods or training packs, a wheel, a kitchen, supplies. Mark the ones that are nice to have `optional`; results can `need` those and be drawn again for a player who lacks them."
|
|
26969
|
+
),
|
|
26947
26970
|
license: License,
|
|
26948
26971
|
capabilities: external_exports.array(Capability).default([]).describe(
|
|
26949
26972
|
"Engine features this pack needs. Declaring them lets an older app refuse the pack with a clear message instead of misplaying it."
|
|
@@ -26953,6 +26976,7 @@ var Pack = external_exports.object({
|
|
|
26953
26976
|
),
|
|
26954
26977
|
vocabulary: Vocabulary,
|
|
26955
26978
|
unit: external_exports.object({
|
|
26979
|
+
clock: Clock.optional().describe("A clock every unit runs. A mode can set its own with `clock`."),
|
|
26956
26980
|
createsSubject: external_exports.boolean().default(true).describe("Whether entering a unit produces a new subject."),
|
|
26957
26981
|
min: external_exports.number().int().min(1).default(1).describe("Fewest units in a run."),
|
|
26958
26982
|
max: external_exports.number().int().min(1).default(20).describe("Most units in a run.")
|
|
@@ -27300,6 +27324,7 @@ function lintPack(pack) {
|
|
|
27300
27324
|
}
|
|
27301
27325
|
usesDecks = true;
|
|
27302
27326
|
break;
|
|
27327
|
+
case "startStopwatch":
|
|
27303
27328
|
case "startTimer":
|
|
27304
27329
|
usesTimers = true;
|
|
27305
27330
|
break;
|
|
@@ -27320,6 +27345,18 @@ function lintPack(pack) {
|
|
|
27320
27345
|
}
|
|
27321
27346
|
});
|
|
27322
27347
|
}
|
|
27348
|
+
const requirements = new Map((pack.requires ?? []).map((r) => [r.id, r]));
|
|
27349
|
+
for (const [tableId, table] of Object.entries(pack.tables)) {
|
|
27350
|
+
table.entries.forEach((e, i) => {
|
|
27351
|
+
for (const need of e.needs ?? []) {
|
|
27352
|
+
const r = requirements.get(need);
|
|
27353
|
+
if (!r) d.push(err("ref/unknown-requirement", `tables.${tableId}.entries[${i}].needs`, `needs unknown requirement ${need}`));
|
|
27354
|
+
else if (!r.optional) {
|
|
27355
|
+
d.push(warn("requirement/never-lacked", `tables.${tableId}.entries[${i}].needs`, `needs ${need}, which is not optional, so no run can lack it; the entry will never be skipped`));
|
|
27356
|
+
}
|
|
27357
|
+
}
|
|
27358
|
+
});
|
|
27359
|
+
}
|
|
27323
27360
|
const predicateSites = [];
|
|
27324
27361
|
for (const [tableId, table] of Object.entries(pack.tables)) {
|
|
27325
27362
|
table.entries.forEach(
|
|
@@ -27591,7 +27628,8 @@ function lintPack(pack) {
|
|
|
27591
27628
|
requireCap("decks", usesDecks, "uses decks");
|
|
27592
27629
|
requireCap("resources", usesResources, "uses resources");
|
|
27593
27630
|
requireCap("counters", counterIds.size > 0, "declares counters");
|
|
27594
|
-
|
|
27631
|
+
const unitClock = Boolean(pack.unit.clock) || Object.values(pack.modes).some((m) => m.clock);
|
|
27632
|
+
requireCap("timers", usesTimers || unitClock, "runs a clock");
|
|
27595
27633
|
requireCap(
|
|
27596
27634
|
"backwardTargeting",
|
|
27597
27635
|
usesResolveTarget || !!pack.targeting && pack.targeting.strategy !== "none",
|
|
@@ -27966,6 +28004,14 @@ function howConsulted(table) {
|
|
|
27966
28004
|
return "Find the row whose key matches what was drawn.";
|
|
27967
28005
|
}
|
|
27968
28006
|
}
|
|
28007
|
+
function an(word, capital = false) {
|
|
28008
|
+
const w = word.trim().toLowerCase();
|
|
28009
|
+
const vowel = /^[aeiou]/.test(w);
|
|
28010
|
+
const soundsConsonant = /^(u[^aeiou]|un[aeiou]|uni|use|usu|ute|eu|one(?![a-z])|onc)/.test(w) || /^u[st][aeiou]/.test(w);
|
|
28011
|
+
const silentH = /^(hour|honest|honor|heir|herb)/.test(w);
|
|
28012
|
+
const article = vowel && !soundsConsonant || silentH ? "an" : "a";
|
|
28013
|
+
return `${capital ? article[0].toUpperCase() + article.slice(1) : article} ${word}`;
|
|
28014
|
+
}
|
|
27969
28015
|
function nouns(pack) {
|
|
27970
28016
|
const v = pack.vocabulary;
|
|
27971
28017
|
return {
|
|
@@ -28082,6 +28128,8 @@ function actionInWords(pack, a) {
|
|
|
28082
28128
|
}
|
|
28083
28129
|
case "startTimer":
|
|
28084
28130
|
return a.label ? `${a.label} (${a.minutes} min)` : `start a ${a.minutes}-minute timer`;
|
|
28131
|
+
case "startStopwatch":
|
|
28132
|
+
return a.label ? `start the ${a.label} stopwatch` : "start a stopwatch";
|
|
28085
28133
|
case "note":
|
|
28086
28134
|
return a.text;
|
|
28087
28135
|
case "setFlag":
|
|
@@ -28110,7 +28158,7 @@ function triggerPointInWords(pack, on) {
|
|
|
28110
28158
|
case "immediately":
|
|
28111
28159
|
return "Then";
|
|
28112
28160
|
case "onEnterUnit":
|
|
28113
|
-
return `When
|
|
28161
|
+
return `When ${an(n.unit)} begins`;
|
|
28114
28162
|
case "onDeclareSubject":
|
|
28115
28163
|
return `When the ${n.subject} is declared`;
|
|
28116
28164
|
case "afterWork":
|
|
@@ -28272,13 +28320,17 @@ function stepDetail(pack, step) {
|
|
|
28272
28320
|
function whatYouNeed(pack) {
|
|
28273
28321
|
const n = nouns(pack);
|
|
28274
28322
|
const items = [];
|
|
28323
|
+
for (const r of pack.requires ?? []) {
|
|
28324
|
+
items.push(`${cap(r.label)}${r.optional ? " (optional; results that need it are drawn again without it)" : ""}${r.note ? ` \u2014 ${r.note}` : ""}${r.url ? ` (${r.url})` : ""}.`);
|
|
28325
|
+
}
|
|
28275
28326
|
const dice = diceInWords(diceNeeded(pack));
|
|
28276
28327
|
if (dice) items.push(cap(dice) + ".");
|
|
28277
28328
|
for (const deck of Object.values(pack.decks ?? {})) {
|
|
28278
28329
|
if (deck.kind === "standard52") items.push(`A standard deck of playing cards${deck.includeJokers ? " with the jokers in" : ""}.`);
|
|
28279
28330
|
else items.push(`${plural(deck.cards.length, "card", "cards")} for ${deck.title} \u2014 write them out, or use the app.`);
|
|
28280
28331
|
}
|
|
28281
|
-
if (pack.capabilities.includes("timers")) items.push("A timer.");
|
|
28332
|
+
if (pack.capabilities.includes("timers") && !pack.unit.clock) items.push("A timer.");
|
|
28333
|
+
if (pack.unit.clock) items.push(pack.unit.clock.kind === "timer" ? `A clock: every ${n.unit} is timed, ${pack.unit.clock.minutes} minutes. The app keeps it.` : `A clock: every ${n.unit} is timed. The app keeps it.`);
|
|
28282
28334
|
items.push(pack.journal?.enabled === false ? `Somewhere to keep the ${n.run} log.` : `Somewhere to keep the ${n.run} log \u2014 the run log sheet, or the app.`);
|
|
28283
28335
|
return items;
|
|
28284
28336
|
}
|
|
@@ -28349,11 +28401,12 @@ function summaryDoc(pack) {
|
|
|
28349
28401
|
const n = nouns(pack);
|
|
28350
28402
|
const b = new Builder();
|
|
28351
28403
|
b.p(pack.description);
|
|
28404
|
+
b.p([pack.category ? `Category: ${pack.category}` : "", pack.tags?.length ? `Tags: ${pack.tags.join(", ")}` : ""].filter(Boolean).join(" \xB7 "), "muted");
|
|
28352
28405
|
b.p(license(pack), "muted");
|
|
28353
28406
|
b.h(2, "How it plays");
|
|
28354
28407
|
const unitRange = `${pack.unit.min === pack.unit.max ? pack.unit.min : `${pack.unit.min}\u2013${pack.unit.max}`}`;
|
|
28355
28408
|
b.p(
|
|
28356
|
-
|
|
28409
|
+
`${an(n.run, true)} is a series of ${n.units}${pack.unit.createsSubject ? `, each producing ${an(n.subject)}` : ""}; ${unitRange} of them, depending on the mode. Each ${n.unit} moves through ${plural(pack.phases.length, "phase", "phases")}: ${pack.phases.map((p) => p.label).join(", ")}.`
|
|
28357
28410
|
);
|
|
28358
28411
|
b.table(["Mode", "Length", "Players", "About"], modeRows(pack, { notes: false }));
|
|
28359
28412
|
b.h(2, "What is inside");
|
|
@@ -28363,7 +28416,7 @@ function summaryDoc(pack) {
|
|
|
28363
28416
|
if (tables.length) parts.push(`${plural(tables.length, "table", "tables")}: ${tables.map((t) => `${t.title} (${howConsulted(t).replace(/\.$/, "").toLowerCase()}, ${plural(t.entries.length, "entry", "entries")})`).join("; ")}.`);
|
|
28364
28417
|
if (decks.length) parts.push(`${plural(decks.length, "deck", "decks")}: ${decks.map((d) => d.kind === "cards" ? `${d.title} (${plural(d.cards.length, "card", "cards")})` : `${d.title} (a standard deck)`).join("; ")}.`);
|
|
28365
28418
|
const states = Object.values(pack.states ?? {});
|
|
28366
|
-
if (states.length) parts.push(`${plural(states.length, "state", "states")}
|
|
28419
|
+
if (states.length) parts.push(`${plural(states.length, "state", "states")} ${an(n.subject)} or ${n.run} can carry: ${states.map((s) => s.label).join(", ")}.`);
|
|
28367
28420
|
const counters = Object.values(pack.counters ?? {}).filter((c) => !c.hidden);
|
|
28368
28421
|
if (counters.length) parts.push(`${plural(counters.length, "tally", "tallies")}: ${counters.map((c) => c.label).join(", ")}.`);
|
|
28369
28422
|
const resources = Object.values(pack.resources ?? {});
|
|
@@ -28388,14 +28441,14 @@ function rulebookDoc(pack) {
|
|
|
28388
28441
|
b.list(whatYouNeed(pack));
|
|
28389
28442
|
b.h(2, "Terms");
|
|
28390
28443
|
b.terms([
|
|
28391
|
-
{ term: cap(n.run), text: `One session of play.
|
|
28444
|
+
{ term: cap(n.run), text: `One session of play. ${an(n.run, true)} is a series of ${n.units}, and ends when you say so or when the game does.` },
|
|
28392
28445
|
{ term: cap(n.unit), text: `One round. Every ${n.unit} passes through the same phases${pack.unit.createsSubject ? ` and produces one ${n.subject}` : ""}.` },
|
|
28393
|
-
...pack.unit.createsSubject ? [{ term: cap(n.subject), text: `The thing
|
|
28394
|
-
{ term: cap(n.finalize), text: `Closing
|
|
28446
|
+
...pack.unit.createsSubject ? [{ term: cap(n.subject), text: `The thing ${an(n.unit)} makes. States attach to it, and consequences can land on it later.` }] : [],
|
|
28447
|
+
{ term: cap(n.finalize), text: `Closing ${an(n.unit)}. Once ${n.finalize.toLowerCase()}d, what it made is fixed unless a rule says otherwise.` },
|
|
28395
28448
|
...Object.entries(pack.vocabulary.terms ?? {}).map(([term, text]) => ({ term, text }))
|
|
28396
28449
|
]);
|
|
28397
28450
|
if (pack.hierarchy?.length) b.p(`When rules contradict each other, the more specific wins, in this order: ${pack.hierarchy.join(" > ")}.`, "note");
|
|
28398
|
-
b.h(2,
|
|
28451
|
+
b.h(2, `${an(n.unit, true)}, step by step`);
|
|
28399
28452
|
b.list(flowSteps(pack, { detail: true }), true);
|
|
28400
28453
|
if (pack.journal?.enabled !== false) b.p(`${pack.journal?.prompt ? `After each ${n.unit}: \u201C${pack.journal.prompt}\u201D` : `Keep a line of notes per ${n.unit}.`}${pack.journal?.required ? " A note is required before moving on." : ""}`);
|
|
28401
28454
|
b.h(2, "Modes");
|
|
@@ -28430,7 +28483,7 @@ function rulebookDoc(pack) {
|
|
|
28430
28483
|
["State", "On", "Means"],
|
|
28431
28484
|
states.map(([, s]) => [
|
|
28432
28485
|
s.short ? `${s.label} (${s.short})` : s.label,
|
|
28433
|
-
s.scope === "run" ? `the ${n.run}` : s.scope === "contestant" ? "a contestant" :
|
|
28486
|
+
s.scope === "run" ? `the ${n.run}` : s.scope === "contestant" ? "a contestant" : an(n.subject),
|
|
28434
28487
|
[s.description ?? "", ...(s.semantics ?? []).map(semanticInWords), s.group ? `Replaces any other \u201C${s.group}\u201D state.` : "", s.until === "unitEnd" ? `Lifts when the ${n.unit} closes.` : ""].filter(Boolean).join(" ")
|
|
28435
28488
|
])
|
|
28436
28489
|
);
|
|
@@ -28475,7 +28528,7 @@ function rulebookDoc(pack) {
|
|
|
28475
28528
|
b.list(pack.triggers.map((t) => sentence(cap(triggerInWords(pack, t)))));
|
|
28476
28529
|
}
|
|
28477
28530
|
if (pack.endings?.length) {
|
|
28478
|
-
b.h(2, `Ending
|
|
28531
|
+
b.h(2, `Ending ${an(n.run)}`);
|
|
28479
28532
|
b.terms(pack.endings.map((e) => ({ term: e.label, text: [e.text ?? "", e.requires?.length ? `Available when ${conditionsInWords(pack, e.requires)}.` : ""].filter(Boolean).join(" ") })));
|
|
28480
28533
|
}
|
|
28481
28534
|
return { kind: "rulebook", layout: "book", title: pack.title, subtitle: byline(pack), blocks: b.blocks };
|
|
@@ -28487,7 +28540,7 @@ function quickstartDoc(pack) {
|
|
|
28487
28540
|
b.h(2, "What you need");
|
|
28488
28541
|
b.list(whatYouNeed(pack));
|
|
28489
28542
|
b.h(2, "The words");
|
|
28490
|
-
b.p(
|
|
28543
|
+
b.p(`${an(n.run, true)} is one session. It is made of ${n.units}${pack.unit.createsSubject ? `, and each ${n.unit} makes one ${n.subject}` : ""}. Closing a ${n.unit} is called \u201C${n.finalize}\u201D.`);
|
|
28491
28544
|
b.h(2, `Each ${n.unit}`);
|
|
28492
28545
|
b.list(flowSteps(pack, { detail: false }), true);
|
|
28493
28546
|
const d = pack.modes[pack.defaultMode];
|
|
@@ -28817,6 +28870,8 @@ function initialState(pack, e) {
|
|
|
28817
28870
|
outcomes: [],
|
|
28818
28871
|
obligations: [],
|
|
28819
28872
|
firedOnce: [],
|
|
28873
|
+
lacks: e.lacks ?? [],
|
|
28874
|
+
clocks: [],
|
|
28820
28875
|
contestants: [],
|
|
28821
28876
|
awards: [],
|
|
28822
28877
|
ending: null
|
|
@@ -29030,6 +29085,50 @@ function reduce(pack, log) {
|
|
|
29030
29085
|
state.status = "ended";
|
|
29031
29086
|
state.ending = event.ending;
|
|
29032
29087
|
break;
|
|
29088
|
+
case "ClockStarted":
|
|
29089
|
+
if (!state.clocks.some((c) => c.id === event.clock)) {
|
|
29090
|
+
state.clocks.push({
|
|
29091
|
+
id: event.clock,
|
|
29092
|
+
kind: event.kind,
|
|
29093
|
+
label: event.label,
|
|
29094
|
+
seconds: event.kind === "timer" ? event.seconds ?? null : null,
|
|
29095
|
+
unit: state.unit,
|
|
29096
|
+
status: "running",
|
|
29097
|
+
startedAt: event.at,
|
|
29098
|
+
runningSince: event.at,
|
|
29099
|
+
accumulatedMs: 0,
|
|
29100
|
+
elapsedMs: null,
|
|
29101
|
+
expired: false
|
|
29102
|
+
});
|
|
29103
|
+
}
|
|
29104
|
+
break;
|
|
29105
|
+
case "ClockPaused": {
|
|
29106
|
+
const c = state.clocks.find((x) => x.id === event.clock);
|
|
29107
|
+
if (c && c.status === "running" && c.runningSince) {
|
|
29108
|
+
c.accumulatedMs += Math.max(0, Date.parse(event.at) - Date.parse(c.runningSince));
|
|
29109
|
+
c.runningSince = null;
|
|
29110
|
+
c.status = "paused";
|
|
29111
|
+
}
|
|
29112
|
+
break;
|
|
29113
|
+
}
|
|
29114
|
+
case "ClockResumed": {
|
|
29115
|
+
const c = state.clocks.find((x) => x.id === event.clock);
|
|
29116
|
+
if (c && c.status === "paused") {
|
|
29117
|
+
c.runningSince = event.at;
|
|
29118
|
+
c.status = "running";
|
|
29119
|
+
}
|
|
29120
|
+
break;
|
|
29121
|
+
}
|
|
29122
|
+
case "ClockStopped": {
|
|
29123
|
+
const c = state.clocks.find((x) => x.id === event.clock);
|
|
29124
|
+
if (c && c.status !== "done") {
|
|
29125
|
+
c.elapsedMs = event.elapsedMs;
|
|
29126
|
+
c.runningSince = null;
|
|
29127
|
+
c.status = "done";
|
|
29128
|
+
c.expired = event.expired === true;
|
|
29129
|
+
}
|
|
29130
|
+
break;
|
|
29131
|
+
}
|
|
29033
29132
|
case "ContestantAdded":
|
|
29034
29133
|
if (!state.contestants.some((c) => c.id === event.contestant)) {
|
|
29035
29134
|
state.contestants.push({ id: event.contestant, name: event.name, states: [] });
|