@scrthq/runlog 0.0.45 → 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 +78 -12
- package/package.json +1 -1
package/dist/runlog.js
CHANGED
|
@@ -26718,6 +26718,12 @@ var ResourceDef = external_exports.object({
|
|
|
26718
26718
|
}).strict().describe(
|
|
26719
26719
|
"A numeric track the player fills or spends: a progress clock, a word count, training volume, stock on hand."
|
|
26720
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.");
|
|
26721
26727
|
var Requirement = external_exports.object({
|
|
26722
26728
|
id: Id.describe("Unique among requirements. Table entries name it in `needs`."),
|
|
26723
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."),
|
|
@@ -26875,6 +26881,7 @@ var Mode = external_exports.object({
|
|
|
26875
26881
|
).optional().describe(
|
|
26876
26882
|
"Per-unit overrides, for modes with a fixed shape \u2014 'unit three always suffers a consequence', and the like."
|
|
26877
26883
|
),
|
|
26884
|
+
clock: Clock.optional().describe("This mode's clock on every unit, instead of the pack's `unit.clock`."),
|
|
26878
26885
|
moderated: external_exports.object({
|
|
26879
26886
|
contestants: external_exports.object({
|
|
26880
26887
|
min: external_exports.number().int().min(1).default(2).describe("Fewest contestants."),
|
|
@@ -26969,6 +26976,7 @@ var Pack = external_exports.object({
|
|
|
26969
26976
|
),
|
|
26970
26977
|
vocabulary: Vocabulary,
|
|
26971
26978
|
unit: external_exports.object({
|
|
26979
|
+
clock: Clock.optional().describe("A clock every unit runs. A mode can set its own with `clock`."),
|
|
26972
26980
|
createsSubject: external_exports.boolean().default(true).describe("Whether entering a unit produces a new subject."),
|
|
26973
26981
|
min: external_exports.number().int().min(1).default(1).describe("Fewest units in a run."),
|
|
26974
26982
|
max: external_exports.number().int().min(1).default(20).describe("Most units in a run.")
|
|
@@ -27316,6 +27324,7 @@ function lintPack(pack) {
|
|
|
27316
27324
|
}
|
|
27317
27325
|
usesDecks = true;
|
|
27318
27326
|
break;
|
|
27327
|
+
case "startStopwatch":
|
|
27319
27328
|
case "startTimer":
|
|
27320
27329
|
usesTimers = true;
|
|
27321
27330
|
break;
|
|
@@ -27619,7 +27628,8 @@ function lintPack(pack) {
|
|
|
27619
27628
|
requireCap("decks", usesDecks, "uses decks");
|
|
27620
27629
|
requireCap("resources", usesResources, "uses resources");
|
|
27621
27630
|
requireCap("counters", counterIds.size > 0, "declares counters");
|
|
27622
|
-
|
|
27631
|
+
const unitClock = Boolean(pack.unit.clock) || Object.values(pack.modes).some((m) => m.clock);
|
|
27632
|
+
requireCap("timers", usesTimers || unitClock, "runs a clock");
|
|
27623
27633
|
requireCap(
|
|
27624
27634
|
"backwardTargeting",
|
|
27625
27635
|
usesResolveTarget || !!pack.targeting && pack.targeting.strategy !== "none",
|
|
@@ -27994,6 +28004,14 @@ function howConsulted(table) {
|
|
|
27994
28004
|
return "Find the row whose key matches what was drawn.";
|
|
27995
28005
|
}
|
|
27996
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
|
+
}
|
|
27997
28015
|
function nouns(pack) {
|
|
27998
28016
|
const v = pack.vocabulary;
|
|
27999
28017
|
return {
|
|
@@ -28110,6 +28128,8 @@ function actionInWords(pack, a) {
|
|
|
28110
28128
|
}
|
|
28111
28129
|
case "startTimer":
|
|
28112
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";
|
|
28113
28133
|
case "note":
|
|
28114
28134
|
return a.text;
|
|
28115
28135
|
case "setFlag":
|
|
@@ -28138,7 +28158,7 @@ function triggerPointInWords(pack, on) {
|
|
|
28138
28158
|
case "immediately":
|
|
28139
28159
|
return "Then";
|
|
28140
28160
|
case "onEnterUnit":
|
|
28141
|
-
return `When
|
|
28161
|
+
return `When ${an(n.unit)} begins`;
|
|
28142
28162
|
case "onDeclareSubject":
|
|
28143
28163
|
return `When the ${n.subject} is declared`;
|
|
28144
28164
|
case "afterWork":
|
|
@@ -28309,7 +28329,8 @@ function whatYouNeed(pack) {
|
|
|
28309
28329
|
if (deck.kind === "standard52") items.push(`A standard deck of playing cards${deck.includeJokers ? " with the jokers in" : ""}.`);
|
|
28310
28330
|
else items.push(`${plural(deck.cards.length, "card", "cards")} for ${deck.title} \u2014 write them out, or use the app.`);
|
|
28311
28331
|
}
|
|
28312
|
-
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.`);
|
|
28313
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.`);
|
|
28314
28335
|
return items;
|
|
28315
28336
|
}
|
|
@@ -28385,7 +28406,7 @@ function summaryDoc(pack) {
|
|
|
28385
28406
|
b.h(2, "How it plays");
|
|
28386
28407
|
const unitRange = `${pack.unit.min === pack.unit.max ? pack.unit.min : `${pack.unit.min}\u2013${pack.unit.max}`}`;
|
|
28387
28408
|
b.p(
|
|
28388
|
-
|
|
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(", ")}.`
|
|
28389
28410
|
);
|
|
28390
28411
|
b.table(["Mode", "Length", "Players", "About"], modeRows(pack, { notes: false }));
|
|
28391
28412
|
b.h(2, "What is inside");
|
|
@@ -28395,7 +28416,7 @@ function summaryDoc(pack) {
|
|
|
28395
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("; ")}.`);
|
|
28396
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("; ")}.`);
|
|
28397
28418
|
const states = Object.values(pack.states ?? {});
|
|
28398
|
-
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(", ")}.`);
|
|
28399
28420
|
const counters = Object.values(pack.counters ?? {}).filter((c) => !c.hidden);
|
|
28400
28421
|
if (counters.length) parts.push(`${plural(counters.length, "tally", "tallies")}: ${counters.map((c) => c.label).join(", ")}.`);
|
|
28401
28422
|
const resources = Object.values(pack.resources ?? {});
|
|
@@ -28420,14 +28441,14 @@ function rulebookDoc(pack) {
|
|
|
28420
28441
|
b.list(whatYouNeed(pack));
|
|
28421
28442
|
b.h(2, "Terms");
|
|
28422
28443
|
b.terms([
|
|
28423
|
-
{ 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.` },
|
|
28424
28445
|
{ term: cap(n.unit), text: `One round. Every ${n.unit} passes through the same phases${pack.unit.createsSubject ? ` and produces one ${n.subject}` : ""}.` },
|
|
28425
|
-
...pack.unit.createsSubject ? [{ term: cap(n.subject), text: `The thing
|
|
28426
|
-
{ 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.` },
|
|
28427
28448
|
...Object.entries(pack.vocabulary.terms ?? {}).map(([term, text]) => ({ term, text }))
|
|
28428
28449
|
]);
|
|
28429
28450
|
if (pack.hierarchy?.length) b.p(`When rules contradict each other, the more specific wins, in this order: ${pack.hierarchy.join(" > ")}.`, "note");
|
|
28430
|
-
b.h(2,
|
|
28451
|
+
b.h(2, `${an(n.unit, true)}, step by step`);
|
|
28431
28452
|
b.list(flowSteps(pack, { detail: true }), true);
|
|
28432
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." : ""}`);
|
|
28433
28454
|
b.h(2, "Modes");
|
|
@@ -28462,7 +28483,7 @@ function rulebookDoc(pack) {
|
|
|
28462
28483
|
["State", "On", "Means"],
|
|
28463
28484
|
states.map(([, s]) => [
|
|
28464
28485
|
s.short ? `${s.label} (${s.short})` : s.label,
|
|
28465
|
-
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),
|
|
28466
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(" ")
|
|
28467
28488
|
])
|
|
28468
28489
|
);
|
|
@@ -28507,7 +28528,7 @@ function rulebookDoc(pack) {
|
|
|
28507
28528
|
b.list(pack.triggers.map((t) => sentence(cap(triggerInWords(pack, t)))));
|
|
28508
28529
|
}
|
|
28509
28530
|
if (pack.endings?.length) {
|
|
28510
|
-
b.h(2, `Ending
|
|
28531
|
+
b.h(2, `Ending ${an(n.run)}`);
|
|
28511
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(" ") })));
|
|
28512
28533
|
}
|
|
28513
28534
|
return { kind: "rulebook", layout: "book", title: pack.title, subtitle: byline(pack), blocks: b.blocks };
|
|
@@ -28519,7 +28540,7 @@ function quickstartDoc(pack) {
|
|
|
28519
28540
|
b.h(2, "What you need");
|
|
28520
28541
|
b.list(whatYouNeed(pack));
|
|
28521
28542
|
b.h(2, "The words");
|
|
28522
|
-
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.`);
|
|
28523
28544
|
b.h(2, `Each ${n.unit}`);
|
|
28524
28545
|
b.list(flowSteps(pack, { detail: false }), true);
|
|
28525
28546
|
const d = pack.modes[pack.defaultMode];
|
|
@@ -28850,6 +28871,7 @@ function initialState(pack, e) {
|
|
|
28850
28871
|
obligations: [],
|
|
28851
28872
|
firedOnce: [],
|
|
28852
28873
|
lacks: e.lacks ?? [],
|
|
28874
|
+
clocks: [],
|
|
28853
28875
|
contestants: [],
|
|
28854
28876
|
awards: [],
|
|
28855
28877
|
ending: null
|
|
@@ -29063,6 +29085,50 @@ function reduce(pack, log) {
|
|
|
29063
29085
|
state.status = "ended";
|
|
29064
29086
|
state.ending = event.ending;
|
|
29065
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
|
+
}
|
|
29066
29132
|
case "ContestantAdded":
|
|
29067
29133
|
if (!state.contestants.some((c) => c.id === event.contestant)) {
|
|
29068
29134
|
state.contestants.push({ id: event.contestant, name: event.name, states: [] });
|