@scrthq/runlog 0.0.43 → 0.0.45

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.
Files changed (2) hide show
  1. package/dist/runlog.js +66 -8
  2. package/package.json +1 -1
package/dist/runlog.js CHANGED
@@ -26448,13 +26448,15 @@ var Action = external_exports.lazy(
26448
26448
  do: external_exports.literal("modCounter").describe("Adjust a counter."),
26449
26449
  counter: Id.describe("Id of the counter to change."),
26450
26450
  by: external_exports.number().int().optional().describe("Amount to add; negative subtracts."),
26451
- set: external_exports.number().int().optional().describe("Absolute value to set, overriding `by`.")
26451
+ set: external_exports.number().int().optional().describe("Absolute value to set, overriding `by`."),
26452
+ setFrom: external_exports.string().min(1).optional().describe("Set it to a value bound earlier \u2014 a roll's total or a prompt's answer \u2014 by name. Overrides `by` and `set`.")
26452
26453
  }).strict().describe("Adjust a counter."),
26453
26454
  external_exports.object({
26454
26455
  do: external_exports.literal("modResource").describe("Adjust a resource."),
26455
26456
  resource: Id.describe("Id of the resource to change."),
26456
26457
  by: external_exports.number().int().optional().describe("Amount to add; negative spends."),
26457
- set: external_exports.number().int().optional().describe("Absolute value to set, overriding `by`.")
26458
+ set: external_exports.number().int().optional().describe("Absolute value to set, overriding `by`."),
26459
+ setFrom: external_exports.string().min(1).optional().describe("Set it to a value bound earlier \u2014 a roll's total or a prompt's answer \u2014 by name. Overrides `by` and `set`.")
26458
26460
  }).strict().describe("Adjust a resource, clamped to its declared min and max."),
26459
26461
  external_exports.object({
26460
26462
  do: external_exports.literal("grantCard").describe("Draw cards into the player's hand."),
@@ -26523,6 +26525,9 @@ var EntryBase = {
26523
26525
  tags: external_exports.array(external_exports.string()).optional().describe("Free tags, for cross-referencing and for filtering results out in some modes."),
26524
26526
  points: external_exports.number().int().min(0).optional().describe(
26525
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."
26526
26531
  )
26527
26532
  };
26528
26533
  var LookupEntry = external_exports.object({
@@ -26669,7 +26674,10 @@ var StateDef = external_exports.object({
26669
26674
  short: external_exports.string().max(8).optional().describe(
26670
26675
  "Compact form for embedding in the name of the thing itself \u2014 a file, a layer, a DAW track. Keep it very short, e.g. VF."
26671
26676
  ),
26672
- scope: external_exports.enum(["subject", "run"]).default("subject").describe("Whether this attaches to one subject or to the whole run."),
26677
+ scope: external_exports.enum(["subject", "run", "contestant"]).default("subject").describe(
26678
+ "Whether this attaches to one subject, to the whole run, or \u2014 in moderated play \u2014 to one contestant on the roster, marked by the moderator: spared from a curse, disqualified, and so on."
26679
+ ),
26680
+ until: external_exports.enum(["unitEnd"]).optional().describe("When the state lifts by itself. `unitEnd`: it is taken off everything it is on when the unit closes \u2014 a curse for this region, a cure for this region."),
26673
26681
  semantics: external_exports.array(
26674
26682
  external_exports.enum(["blocksEdit", "makesUntargetable", "excludesFromResult", "locksValue", "removesFromPlay"])
26675
26683
  ).optional().describe(
@@ -26710,6 +26718,14 @@ var ResourceDef = external_exports.object({
26710
26718
  }).strict().describe(
26711
26719
  "A numeric track the player fills or spends: a progress clock, a word count, training volume, stock on hand."
26712
26720
  );
26721
+ var Requirement = external_exports.object({
26722
+ id: Id.describe("Unique among requirements. Table entries name it in `needs`."),
26723
+ 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."),
26724
+ 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."),
26725
+ 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."),
26726
+ note: external_exports.string().optional().describe("A line on where to get it, which version, or what will do instead."),
26727
+ url: external_exports.string().url().optional().describe("Where to find it.")
26728
+ }).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.");
26713
26729
  var ChecklistItem = external_exports.union([
26714
26730
  external_exports.string().min(1),
26715
26731
  external_exports.object({
@@ -26939,6 +26955,11 @@ var Pack = external_exports.object({
26939
26955
  author: external_exports.string().optional().describe("Who wrote the game."),
26940
26956
  description: external_exports.string().optional().describe("One or two sentences on what the game is."),
26941
26957
  homepage: external_exports.string().url().optional().describe("Where to find out more."),
26958
+ 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"),
26959
+ 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."),
26960
+ requires: external_exports.array(Requirement).max(20).optional().describe(
26961
+ "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."
26962
+ ),
26942
26963
  license: License,
26943
26964
  capabilities: external_exports.array(Capability).default([]).describe(
26944
26965
  "Engine features this pack needs. Declaring them lets an older app refuse the pack with a clear message instead of misplaying it."
@@ -27259,6 +27280,7 @@ function lintPack(pack) {
27259
27280
  let usesResources = resourceIds.size > 0;
27260
27281
  let usesTimers = false;
27261
27282
  let usesResolveTarget = false;
27283
+ const movedCounters = /* @__PURE__ */ new Set();
27262
27284
  for (const site of collectActionSites(pack)) {
27263
27285
  walkActions(site.actions, (a) => {
27264
27286
  switch (a.do) {
@@ -27277,6 +27299,7 @@ function lintPack(pack) {
27277
27299
  if (!counterIds.has(a.counter)) {
27278
27300
  d.push(err("ref/unknown-counter", site.path, `references unknown counter ${a.counter}`));
27279
27301
  }
27302
+ movedCounters.add(a.counter);
27280
27303
  break;
27281
27304
  case "modResource":
27282
27305
  if (!resourceIds.has(a.resource)) {
@@ -27313,6 +27336,18 @@ function lintPack(pack) {
27313
27336
  }
27314
27337
  });
27315
27338
  }
27339
+ const requirements = new Map((pack.requires ?? []).map((r) => [r.id, r]));
27340
+ for (const [tableId, table] of Object.entries(pack.tables)) {
27341
+ table.entries.forEach((e, i) => {
27342
+ for (const need of e.needs ?? []) {
27343
+ const r = requirements.get(need);
27344
+ if (!r) d.push(err("ref/unknown-requirement", `tables.${tableId}.entries[${i}].needs`, `needs unknown requirement ${need}`));
27345
+ else if (!r.optional) {
27346
+ 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`));
27347
+ }
27348
+ }
27349
+ });
27350
+ }
27316
27351
  const predicateSites = [];
27317
27352
  for (const [tableId, table] of Object.entries(pack.tables)) {
27318
27353
  table.entries.forEach(
@@ -27385,6 +27420,7 @@ function lintPack(pack) {
27385
27420
  if (typeof item !== "string" && item.tally && !counterIds.has(item.tally)) {
27386
27421
  d.push(err("ref/unknown-counter", `${path}.${field}[${ii}]`, `tallies unknown counter ${item.tally}`));
27387
27422
  }
27423
+ if (typeof item !== "string" && item.tally) movedCounters.add(item.tally);
27388
27424
  if (typeof item !== "string" && item.shows && !tableIds.has(item.shows.table)) {
27389
27425
  d.push(err("ref/unknown-table", `${path}.${field}[${ii}]`, `shows unknown table ${item.shows.table}`));
27390
27426
  }
@@ -27493,8 +27529,8 @@ function lintPack(pack) {
27493
27529
  d.push(err("ref/unknown-state", path, `hooks unknown state ${sel.state}`));
27494
27530
  }
27495
27531
  }
27496
- if (!counter.incrementOn?.length && !counter.triggers?.length) {
27497
- d.push(warn("counter/inert", path, "counter has no incrementOn and no triggers; nothing will ever change it"));
27532
+ if (!counter.incrementOn?.length && !counter.triggers?.length && !movedCounters.has(counterId)) {
27533
+ d.push(warn("counter/inert", path, "counter has no incrementOn, no triggers, and nothing moves it; it will never change"));
27498
27534
  }
27499
27535
  }
27500
27536
  if (pack.targeting?.strategy === "anchoredOffset") {
@@ -28264,6 +28300,9 @@ function stepDetail(pack, step) {
28264
28300
  function whatYouNeed(pack) {
28265
28301
  const n = nouns(pack);
28266
28302
  const items = [];
28303
+ for (const r of pack.requires ?? []) {
28304
+ 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})` : ""}.`);
28305
+ }
28267
28306
  const dice = diceInWords(diceNeeded(pack));
28268
28307
  if (dice) items.push(cap(dice) + ".");
28269
28308
  for (const deck of Object.values(pack.decks ?? {})) {
@@ -28341,6 +28380,7 @@ function summaryDoc(pack) {
28341
28380
  const n = nouns(pack);
28342
28381
  const b = new Builder();
28343
28382
  b.p(pack.description);
28383
+ b.p([pack.category ? `Category: ${pack.category}` : "", pack.tags?.length ? `Tags: ${pack.tags.join(", ")}` : ""].filter(Boolean).join(" \xB7 "), "muted");
28344
28384
  b.p(license(pack), "muted");
28345
28385
  b.h(2, "How it plays");
28346
28386
  const unitRange = `${pack.unit.min === pack.unit.max ? pack.unit.min : `${pack.unit.min}\u2013${pack.unit.max}`}`;
@@ -28422,8 +28462,8 @@ function rulebookDoc(pack) {
28422
28462
  ["State", "On", "Means"],
28423
28463
  states.map(([, s]) => [
28424
28464
  s.short ? `${s.label} (${s.short})` : s.label,
28425
- s.scope === "run" ? `the ${n.run}` : `a ${n.subject}`,
28426
- [s.description ?? "", ...(s.semantics ?? []).map(semanticInWords), s.group ? `Replaces any other \u201C${s.group}\u201D state.` : ""].filter(Boolean).join(" ")
28465
+ s.scope === "run" ? `the ${n.run}` : s.scope === "contestant" ? "a contestant" : `a ${n.subject}`,
28466
+ [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(" ")
28427
28467
  ])
28428
28468
  );
28429
28469
  }
@@ -28809,6 +28849,7 @@ function initialState(pack, e) {
28809
28849
  outcomes: [],
28810
28850
  obligations: [],
28811
28851
  firedOnce: [],
28852
+ lacks: e.lacks ?? [],
28812
28853
  contestants: [],
28813
28854
  awards: [],
28814
28855
  ending: null
@@ -28985,6 +29026,10 @@ function reduce(pack, log) {
28985
29026
  for (const subject of state.subjects) {
28986
29027
  if (subject.unit === state.unit) subject.finalized = true;
28987
29028
  }
29029
+ const lifts = (id) => pack.states?.[id]?.until === "unitEnd";
29030
+ state.runStates = state.runStates.filter((id) => !lifts(id));
29031
+ for (const subject of state.subjects) subject.states = subject.states.filter((id) => !lifts(id));
29032
+ for (const who of state.contestants) who.states = who.states.filter((id) => !lifts(id));
28988
29033
  break;
28989
29034
  }
28990
29035
  case "JournalWritten":
@@ -29020,12 +29065,25 @@ function reduce(pack, log) {
29020
29065
  break;
29021
29066
  case "ContestantAdded":
29022
29067
  if (!state.contestants.some((c) => c.id === event.contestant)) {
29023
- state.contestants.push({ id: event.contestant, name: event.name });
29068
+ state.contestants.push({ id: event.contestant, name: event.name, states: [] });
29024
29069
  }
29025
29070
  break;
29026
29071
  case "ContestantRemoved":
29027
29072
  state.contestants = state.contestants.filter((c) => c.id !== event.contestant);
29028
29073
  break;
29074
+ case "ContestantStateApplied": {
29075
+ const who = state.contestants.find((c) => c.id === event.contestant);
29076
+ if (!who) break;
29077
+ const group = pack.states?.[event.state]?.group;
29078
+ if (group) who.states = who.states.filter((s) => s === event.state || pack.states?.[s]?.group !== group);
29079
+ if (!who.states.includes(event.state)) who.states.push(event.state);
29080
+ break;
29081
+ }
29082
+ case "ContestantStateRemoved": {
29083
+ const who = state.contestants.find((c) => c.id === event.contestant);
29084
+ if (who) who.states = who.states.filter((s) => s !== event.state);
29085
+ break;
29086
+ }
29029
29087
  case "Awarded":
29030
29088
  if (!state.awards.some((a) => a.contestant === event.contestant && a.outcome === event.outcome)) {
29031
29089
  state.awards.push({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scrthq/runlog",
3
- "version": "0.0.43",
3
+ "version": "0.0.45",
4
4
  "description": "Validate, lint, bundle and test Runlog rule packs.",
5
5
  "type": "module",
6
6
  "license": "MIT",