@scrthq/runlog 0.0.41 → 0.0.43
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 +108 -11
- package/package.json +1 -1
package/dist/runlog.js
CHANGED
|
@@ -26318,6 +26318,9 @@ var Predicate = external_exports.lazy(
|
|
|
26318
26318
|
"Tests whether any earlier subject was created from an entry carrying this tag \u2014 for results that only make sense once something of a given kind exists."
|
|
26319
26319
|
),
|
|
26320
26320
|
external_exports.object({ modeIs: external_exports.array(Id).min(1).describe("Mode ids in which this holds true.") }).strict().describe("Tests which mode the run is being played in."),
|
|
26321
|
+
external_exports.object({ phaseDone: Id.describe("Id of a phase in the flow.") }).strict().describe(
|
|
26322
|
+
"True once the named phase has been completed in the current unit. This is how a move that records an outcome is kept off the screen until there is something to have an outcome about."
|
|
26323
|
+
),
|
|
26321
26324
|
external_exports.object({ not: Predicate }).strict().describe("True when the inner predicate is false."),
|
|
26322
26325
|
external_exports.object({ allOf: external_exports.array(Predicate).min(1) }).strict().describe("True only when every listed predicate holds."),
|
|
26323
26326
|
external_exports.object({ anyOf: external_exports.array(Predicate).min(1) }).strict().describe("True when at least one listed predicate holds.")
|
|
@@ -26380,6 +26383,7 @@ var Action = external_exports.lazy(
|
|
|
26380
26383
|
do: external_exports.literal("rollOn").describe("Roll on another table and resolve whatever comes up."),
|
|
26381
26384
|
table: Id.describe("Id of the table to roll on."),
|
|
26382
26385
|
times: external_exports.number().int().min(1).max(10).optional().describe("How many times to roll. Defaults to 1."),
|
|
26386
|
+
timesFrom: external_exports.string().min(1).optional().describe("Name a total bound earlier with `roll \u2026 into`; that many times, instead of `times`. How a pack rolls for how many to roll."),
|
|
26383
26387
|
choose: external_exports.enum(["one", "all"]).optional().describe(
|
|
26384
26388
|
"With times > 1: `one` lets the player pick a single result to apply, `all` applies every result. Defaults to `all`."
|
|
26385
26389
|
),
|
|
@@ -26516,7 +26520,10 @@ var EntryBase = {
|
|
|
26516
26520
|
grants: external_exports.array(Id).optional().describe(
|
|
26517
26521
|
"States applied to the relevant subject on resolution. Shorthand for an applyState trigger."
|
|
26518
26522
|
),
|
|
26519
|
-
tags: external_exports.array(external_exports.string()).optional().describe("Free tags, for cross-referencing and for filtering results out in some modes.")
|
|
26523
|
+
tags: external_exports.array(external_exports.string()).optional().describe("Free tags, for cross-referencing and for filtering results out in some modes."),
|
|
26524
|
+
points: external_exports.number().int().min(0).optional().describe(
|
|
26525
|
+
"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."
|
|
26526
|
+
)
|
|
26520
26527
|
};
|
|
26521
26528
|
var LookupEntry = external_exports.object({
|
|
26522
26529
|
...EntryBase,
|
|
@@ -26636,7 +26643,8 @@ var Capability = external_exports.enum([
|
|
|
26636
26643
|
"journal",
|
|
26637
26644
|
"bandsResolution",
|
|
26638
26645
|
"opposedResolution",
|
|
26639
|
-
"keyedResolution"
|
|
26646
|
+
"keyedResolution",
|
|
26647
|
+
"moderated"
|
|
26640
26648
|
]).describe("An engine feature this pack needs in order to play correctly.");
|
|
26641
26649
|
var Vocabulary = external_exports.object({
|
|
26642
26650
|
run: external_exports.object({
|
|
@@ -26667,6 +26675,9 @@ var StateDef = external_exports.object({
|
|
|
26667
26675
|
).optional().describe(
|
|
26668
26676
|
"What this state means mechanically. Without it the engine would have to know what your state names mean, which is exactly what keeps an engine game-specific. `makesUntargetable` removes the subject from targeting; `excludesFromResult` leaves it in the run but out of the finished piece; `removesFromPlay` takes it out entirely."
|
|
26669
26677
|
),
|
|
26678
|
+
group: Id.optional().describe(
|
|
26679
|
+
"States in the same group are mutually exclusive on one holder: applying one removes the others. Use it for outcomes that cannot both be true, like landed and missed."
|
|
26680
|
+
),
|
|
26670
26681
|
description: external_exports.string().optional().describe("Explanation shown to the player on hover.")
|
|
26671
26682
|
}).strict().describe("A persistent condition that sticks to a subject, or to the run, once applied.");
|
|
26672
26683
|
var CounterDef = external_exports.object({
|
|
@@ -26703,12 +26714,16 @@ var ChecklistItem = external_exports.union([
|
|
|
26703
26714
|
external_exports.string().min(1),
|
|
26704
26715
|
external_exports.object({
|
|
26705
26716
|
text: external_exports.string().min(1).describe("The point to tick off."),
|
|
26717
|
+
optional: external_exports.boolean().optional().describe("May be left unticked: closing the unit does not wait for it. For boxes that record an outcome rather than promise one."),
|
|
26718
|
+
tally: Id.optional().describe(
|
|
26719
|
+
"Each box ticked under this point adds one to this counter, and unticking takes it back. With `shows`, one box per result; without, the point's own box."
|
|
26720
|
+
),
|
|
26706
26721
|
shows: external_exports.object({
|
|
26707
26722
|
table: external_exports.string().describe("Results from this table are listed under the item."),
|
|
26708
26723
|
scope: external_exports.enum(["unit", "subject", "run"]).default("unit").describe(
|
|
26709
26724
|
"Which results: those rolled this unit, those that reached this unit's subject, or every one in the run."
|
|
26710
26725
|
)
|
|
26711
|
-
}).strict().describe("What to look at while ticking this: a table's results, listed under the point with a box each.")
|
|
26726
|
+
}).strict().optional().describe("What to look at while ticking this: a table's results, listed under the point with a box each.")
|
|
26712
26727
|
}).strict().describe("A point with the results it is about shown beneath it.")
|
|
26713
26728
|
]).describe("A point to tick off: plain text, or text with the table results it is about.");
|
|
26714
26729
|
var Step = external_exports.discriminatedUnion("kind", [
|
|
@@ -26844,6 +26859,16 @@ var Mode = external_exports.object({
|
|
|
26844
26859
|
).optional().describe(
|
|
26845
26860
|
"Per-unit overrides, for modes with a fixed shape \u2014 'unit three always suffers a consequence', and the like."
|
|
26846
26861
|
),
|
|
26862
|
+
moderated: external_exports.object({
|
|
26863
|
+
contestants: external_exports.object({
|
|
26864
|
+
min: external_exports.number().int().min(1).default(2).describe("Fewest contestants."),
|
|
26865
|
+
max: external_exports.number().int().min(1).default(10).describe("Most contestants.")
|
|
26866
|
+
}).strict().default({ min: 2, max: 10 }).describe("How many people race. The moderator is not one of them."),
|
|
26867
|
+
award: external_exports.enum(["first", "everyone"]).default("first").describe("Whether only the first to finish a challenge scores it, or everyone who finishes does."),
|
|
26868
|
+
firstBonus: external_exports.number().int().min(0).default(0).describe("Extra points for finishing first, on top of the result's own. Meaningful with `everyone`.")
|
|
26869
|
+
}).strict().optional().describe(
|
|
26870
|
+
"Moderated play: one person runs the game on the device, a roster of named contestants races every drawn result, and the moderator awards points. Results with `points` are the challenges; everything else lands on everyone."
|
|
26871
|
+
),
|
|
26847
26872
|
notes: external_exports.array(external_exports.string()).optional().describe("Reminders shown to the player in this mode.")
|
|
26848
26873
|
}).strict().describe(
|
|
26849
26874
|
"A set of deltas over the base ruleset. Keeping modes as deltas rather than separate rulesets is what stops them drifting apart as the pack evolves."
|
|
@@ -26856,6 +26881,9 @@ var MoveDef = external_exports.object({
|
|
|
26856
26881
|
),
|
|
26857
26882
|
oncePerRun: external_exports.boolean().default(false).describe("Whether the move is spent after a single use."),
|
|
26858
26883
|
do: external_exports.array(Action).min(1).describe("What happens when the player takes it."),
|
|
26884
|
+
finalizes: external_exports.boolean().optional().describe(
|
|
26885
|
+
"Taking this move also closes the current unit: the rest of its flow is recorded as done and the unit is finalized. For moves that are outcomes, like landed or missed, so the player is not asked to close the unit a second time."
|
|
26886
|
+
),
|
|
26859
26887
|
/** Where the move belongs in the interface. */
|
|
26860
26888
|
when: external_exports.enum(["anytime", "betweenUnits", "beforeEnding"]).default("anytime").describe(
|
|
26861
26889
|
"When the move is offered: at any point, only between units, or only when the player is deciding whether to stop."
|
|
@@ -27332,6 +27360,9 @@ function lintPack(pack) {
|
|
|
27332
27360
|
}
|
|
27333
27361
|
}
|
|
27334
27362
|
}
|
|
27363
|
+
if ("phaseDone" in p && !pack.phases.some((ph) => ph.id === p.phaseDone)) {
|
|
27364
|
+
d.push(err("ref/unknown-phase", site.path, `predicate references unknown phase ${p.phaseDone}`));
|
|
27365
|
+
}
|
|
27335
27366
|
});
|
|
27336
27367
|
}
|
|
27337
27368
|
const seenPhaseIds = /* @__PURE__ */ new Set();
|
|
@@ -27350,8 +27381,11 @@ function lintPack(pack) {
|
|
|
27350
27381
|
}
|
|
27351
27382
|
const points = step.kind === "manual" ? step.checklist : step.kind === "finalizeUnit" ? step.confirm : void 0;
|
|
27352
27383
|
points?.forEach((item, ii) => {
|
|
27353
|
-
|
|
27354
|
-
|
|
27384
|
+
const field = step.kind === "manual" ? "checklist" : "confirm";
|
|
27385
|
+
if (typeof item !== "string" && item.tally && !counterIds.has(item.tally)) {
|
|
27386
|
+
d.push(err("ref/unknown-counter", `${path}.${field}[${ii}]`, `tallies unknown counter ${item.tally}`));
|
|
27387
|
+
}
|
|
27388
|
+
if (typeof item !== "string" && item.shows && !tableIds.has(item.shows.table)) {
|
|
27355
27389
|
d.push(err("ref/unknown-table", `${path}.${field}[${ii}]`, `shows unknown table ${item.shows.table}`));
|
|
27356
27390
|
}
|
|
27357
27391
|
});
|
|
@@ -27535,6 +27569,17 @@ function lintPack(pack) {
|
|
|
27535
27569
|
);
|
|
27536
27570
|
}
|
|
27537
27571
|
};
|
|
27572
|
+
const moderatedModes = Object.values(pack.modes).filter((m) => m.moderated);
|
|
27573
|
+
const scoringEntries = Object.values(pack.tables).some((t) => t.entries.some((e) => (e.points ?? 0) > 0));
|
|
27574
|
+
requireCap("moderated", moderatedModes.length > 0, "has a moderated mode");
|
|
27575
|
+
if (moderatedModes.length > 0 && !scoringEntries) {
|
|
27576
|
+
d.push(warn("mode/nothing-to-award", "modes", "a mode is moderated, but no table entry carries points, so there is nothing to award"));
|
|
27577
|
+
}
|
|
27578
|
+
for (const [modeId, m] of Object.entries(pack.modes)) {
|
|
27579
|
+
if (m.moderated && m.moderated.contestants.max < m.moderated.contestants.min) {
|
|
27580
|
+
d.push(err("mode/contestant-range", `modes.${modeId}.moderated.contestants`, `max (${m.moderated.contestants.max}) is below min (${m.moderated.contestants.min})`));
|
|
27581
|
+
}
|
|
27582
|
+
}
|
|
27538
27583
|
requireCap("decks", usesDecks, "uses decks");
|
|
27539
27584
|
requireCap("resources", usesResources, "uses resources");
|
|
27540
27585
|
requireCap("counters", counterIds.size > 0, "declares counters");
|
|
@@ -27593,7 +27638,8 @@ var IMPLEMENTED_CAPABILITIES = [
|
|
|
27593
27638
|
"journal",
|
|
27594
27639
|
"bandsResolution",
|
|
27595
27640
|
"opposedResolution",
|
|
27596
|
-
"keyedResolution"
|
|
27641
|
+
"keyedResolution",
|
|
27642
|
+
"moderated"
|
|
27597
27643
|
];
|
|
27598
27644
|
function parsePack(input2) {
|
|
27599
27645
|
if (typeof input2 !== "object" || input2 === null) {
|
|
@@ -27969,6 +28015,7 @@ function predicateInWords(pack, p) {
|
|
|
27969
28015
|
if ("subjectHasState" in p) return `${targetInWords(pack, p.of)} is ${label(pack, "states", p.subjectHasState)}`;
|
|
27970
28016
|
if ("priorSubjectTagged" in p) return `an earlier ${n.subject} was tagged \u201C${p.priorSubjectTagged}\u201D`;
|
|
27971
28017
|
if ("modeIs" in p) return `playing ${p.modeIs.map((m) => label(pack, "modes", m)).join(" or ")}`;
|
|
28018
|
+
if ("phaseDone" in p) return `${pack.phases.find((ph) => ph.id === p.phaseDone)?.label ?? p.phaseDone} is done`;
|
|
27972
28019
|
if ("not" in p) return `not (${predicateInWords(pack, p.not)})`;
|
|
27973
28020
|
if ("allOf" in p) return p.allOf.map((q) => predicateInWords(pack, q)).join(" and ");
|
|
27974
28021
|
if ("anyOf" in p) return p.anyOf.map((q) => predicateInWords(pack, q)).join(" or ");
|
|
@@ -28085,6 +28132,11 @@ function modeLength(pack, mode) {
|
|
|
28085
28132
|
return `as many ${n.units} as you like${max ? `, up to ${max}` : ""}`;
|
|
28086
28133
|
}
|
|
28087
28134
|
function modePlayers(mode) {
|
|
28135
|
+
const m = mode.moderated;
|
|
28136
|
+
if (m) {
|
|
28137
|
+
const n = m.contestants.min === m.contestants.max ? `${m.contestants.min}` : `${m.contestants.min}\u2013${m.contestants.max}`;
|
|
28138
|
+
return `moderated, ${n} contestants, ${m.award === "everyone" ? "everyone who finishes scores" : "first to finish scores"}${m.firstBonus ? ` (+${m.firstBonus} for first)` : ""}`;
|
|
28139
|
+
}
|
|
28088
28140
|
const p = mode.players;
|
|
28089
28141
|
if (!p || p.min <= 1 && p.max <= 1) return "solo";
|
|
28090
28142
|
return p.min === p.max ? `${p.min} players` : `${p.min}\u2013${p.max} players`;
|
|
@@ -28273,7 +28325,8 @@ function tableRows(pack, table, opts) {
|
|
|
28273
28325
|
if (e.requires?.length) extras.push(`Only if ${conditionsInWords(pack, e.requires)}; otherwise roll again.`);
|
|
28274
28326
|
}
|
|
28275
28327
|
const text = [e.title ? `${e.title}. ` : "", sentence(e.text), ...extras.map((x) => ` ${x}`)].join("");
|
|
28276
|
-
|
|
28328
|
+
const pts = e.points !== void 0 && e.points > 0 ? `${keys[i] ?? ""} \xB7 ${e.points} pt${e.points === 1 ? "" : "s"}` : keys[i] ?? "";
|
|
28329
|
+
return [pts, text];
|
|
28277
28330
|
});
|
|
28278
28331
|
}
|
|
28279
28332
|
function license(pack) {
|
|
@@ -28370,7 +28423,7 @@ function rulebookDoc(pack) {
|
|
|
28370
28423
|
states.map(([, s]) => [
|
|
28371
28424
|
s.short ? `${s.label} (${s.short})` : s.label,
|
|
28372
28425
|
s.scope === "run" ? `the ${n.run}` : `a ${n.subject}`,
|
|
28373
|
-
[s.description ?? "", ...(s.semantics ?? []).map(semanticInWords)].filter(Boolean).join(" ")
|
|
28426
|
+
[s.description ?? "", ...(s.semantics ?? []).map(semanticInWords), s.group ? `Replaces any other \u201C${s.group}\u201D state.` : ""].filter(Boolean).join(" ")
|
|
28374
28427
|
])
|
|
28375
28428
|
);
|
|
28376
28429
|
}
|
|
@@ -28401,7 +28454,7 @@ function rulebookDoc(pack) {
|
|
|
28401
28454
|
moves.map(([, m]) => [
|
|
28402
28455
|
m.label,
|
|
28403
28456
|
[whenInWords(m.when, n), m.oncePerRun ? `once per ${n.run}` : "", m.available?.length ? `if ${conditionsInWords(pack, m.available)}` : ""].filter(Boolean).join("; "),
|
|
28404
|
-
[m.description ?? "", sentence(cap(actionsInWords(pack, m.do)))].filter(Boolean).join(" ")
|
|
28457
|
+
[m.description ?? "", sentence(cap(actionsInWords(pack, m.do))), m.finalizes ? `Closes the ${n.unit}.` : ""].filter(Boolean).join(" ")
|
|
28405
28458
|
])
|
|
28406
28459
|
);
|
|
28407
28460
|
}
|
|
@@ -28743,6 +28796,7 @@ function initialState(pack, e) {
|
|
|
28743
28796
|
unit: 0,
|
|
28744
28797
|
phasesDone: [],
|
|
28745
28798
|
stepsDone: [],
|
|
28799
|
+
checks: [],
|
|
28746
28800
|
subjects: [],
|
|
28747
28801
|
runStates: [],
|
|
28748
28802
|
counters,
|
|
@@ -28755,6 +28809,8 @@ function initialState(pack, e) {
|
|
|
28755
28809
|
outcomes: [],
|
|
28756
28810
|
obligations: [],
|
|
28757
28811
|
firedOnce: [],
|
|
28812
|
+
contestants: [],
|
|
28813
|
+
awards: [],
|
|
28758
28814
|
ending: null
|
|
28759
28815
|
};
|
|
28760
28816
|
}
|
|
@@ -28824,6 +28880,7 @@ function reduce(pack, log) {
|
|
|
28824
28880
|
state.unit += 1;
|
|
28825
28881
|
state.phasesDone = [];
|
|
28826
28882
|
state.stepsDone = [];
|
|
28883
|
+
state.checks = [];
|
|
28827
28884
|
resetThisUnit = /* @__PURE__ */ new Set();
|
|
28828
28885
|
if (state.forcedUnits > 0) state.forcedUnits -= 1;
|
|
28829
28886
|
if (pack.unit.createsSubject) {
|
|
@@ -28861,12 +28918,18 @@ function reduce(pack, log) {
|
|
|
28861
28918
|
});
|
|
28862
28919
|
break;
|
|
28863
28920
|
case "StateApplied": {
|
|
28864
|
-
const
|
|
28921
|
+
const def = pack.states?.[event.state];
|
|
28922
|
+
const scope = def?.scope ?? "subject";
|
|
28923
|
+
const rivals = (held) => def?.group ? held.filter((s) => s === event.state || pack.states?.[s]?.group !== def.group) : held;
|
|
28865
28924
|
if (scope === "run" || event.subject === void 0) {
|
|
28925
|
+
state.runStates = rivals(state.runStates);
|
|
28866
28926
|
if (!state.runStates.includes(event.state)) state.runStates.push(event.state);
|
|
28867
28927
|
} else {
|
|
28868
28928
|
const subject = subjectById(state, event.subject);
|
|
28869
|
-
if (subject
|
|
28929
|
+
if (subject) {
|
|
28930
|
+
subject.states = rivals(subject.states);
|
|
28931
|
+
if (!subject.states.includes(event.state)) subject.states.push(event.state);
|
|
28932
|
+
}
|
|
28870
28933
|
}
|
|
28871
28934
|
break;
|
|
28872
28935
|
}
|
|
@@ -28955,6 +29018,40 @@ function reduce(pack, log) {
|
|
|
28955
29018
|
state.status = "ended";
|
|
28956
29019
|
state.ending = event.ending;
|
|
28957
29020
|
break;
|
|
29021
|
+
case "ContestantAdded":
|
|
29022
|
+
if (!state.contestants.some((c) => c.id === event.contestant)) {
|
|
29023
|
+
state.contestants.push({ id: event.contestant, name: event.name });
|
|
29024
|
+
}
|
|
29025
|
+
break;
|
|
29026
|
+
case "ContestantRemoved":
|
|
29027
|
+
state.contestants = state.contestants.filter((c) => c.id !== event.contestant);
|
|
29028
|
+
break;
|
|
29029
|
+
case "Awarded":
|
|
29030
|
+
if (!state.awards.some((a) => a.contestant === event.contestant && a.outcome === event.outcome)) {
|
|
29031
|
+
state.awards.push({
|
|
29032
|
+
contestant: event.contestant,
|
|
29033
|
+
outcome: event.outcome,
|
|
29034
|
+
table: event.table,
|
|
29035
|
+
entryId: event.entryId,
|
|
29036
|
+
points: event.points,
|
|
29037
|
+
at: event.at
|
|
29038
|
+
});
|
|
29039
|
+
}
|
|
29040
|
+
break;
|
|
29041
|
+
case "AwardRevoked":
|
|
29042
|
+
state.awards = state.awards.filter((a) => !(a.contestant === event.contestant && a.outcome === event.outcome));
|
|
29043
|
+
break;
|
|
29044
|
+
case "Checked": {
|
|
29045
|
+
const key = `${event.step}|${event.item}`;
|
|
29046
|
+
if (event.on) {
|
|
29047
|
+
if (!state.checks.includes(key)) state.checks.push(key);
|
|
29048
|
+
} else {
|
|
29049
|
+
state.checks = state.checks.filter((k) => k !== key);
|
|
29050
|
+
}
|
|
29051
|
+
break;
|
|
29052
|
+
}
|
|
29053
|
+
case "Corrected":
|
|
29054
|
+
break;
|
|
28958
29055
|
case "Undone":
|
|
28959
29056
|
break;
|
|
28960
29057
|
}
|