From dda8238a1264c6e299a8b0171d0c333f2bd33ce6 Mon Sep 17 00:00:00 2001 From: slaguru666 <111923774+slaguru666@users.noreply.github.com> Date: Wed, 23 Sep 2026 01:20:13 +0100 Subject: [PATCH] Generated NPCs get the full skill list, like everything else does MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "When I create a new NPC or Threat they do not have a set of skills" was the original report, and the kit fix did not cover all of it. A generated NPC carried its posting's core plus two support — about eight rows — so a GM asked for Listen or Dodge or Spot by a player had nothing on the sheet to roll. generateCharacter has called grantFullSkillList since it was written, and build-packs gives all 62 to every packed creature, so the generated NPC was the only actor in the game without them. It calls it now, in the same place the PC path does: after the items, before stowOverload. check-generator holds all three construction paths to it. A shared finishing step that one of three callers omits is invisible from inside that caller, which is why it is worth a check rather than a memory. Proved by removing the call again. Co-Authored-By: Claude Opus 5 --- ringbrp.mjs | 10 ++++++++++ tools/check-generator.mjs | 26 +++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/ringbrp.mjs b/ringbrp.mjs index fc19255..a844a68 100644 --- a/ringbrp.mjs +++ b/ringbrp.mjs @@ -4230,6 +4230,16 @@ export async function generateNPC({ it.system = { ...it.system, ammo: { ...it.system.ammo, reserve: (Number(it.system.ammo.capacity) || 0) * 2 } }; } if (items.length) await actor.createEmbeddedDocuments("Item", items); + /* Every skill on the sheet, untrained ones at their printed base. + * + * This was the first thing reported about the generator — "when I create a new NPC or + * Threat they do not have a set of skills" — and it was true in a way the kit fix does + * not cover: a generated NPC carried its posting's core plus two support and nothing + * else, about eight rows, so a GM asked for Listen or Dodge or Spot by a player had + * nothing on the sheet to roll. generateCharacter has called this since it was written + * and build-packs gives all 62 to every packed creature, so the generated NPC was the + * only actor in the game without them. */ + await grantFullSkillList(actor); // Issued is not carried. A Containment officer drawing four gear items should not // begin the fight permanently slower for owning her own breaching charges — the same // reason the PC path calls this. diff --git a/tools/check-generator.mjs b/tools/check-generator.mjs index 0e3dc2b..b0205fe 100644 --- a/tools/check-generator.mjs +++ b/tools/check-generator.mjs @@ -118,7 +118,31 @@ if (typeof drawKitFor === "function") { } } -/* ---------- 4. the engine must actually ask ---------- */ +/* ---------- 4. every path that builds an actor must finish the sheet ---------- */ +{ + const src = await readFile(path.join(ROOT, "ringbrp.mjs"), "utf8"); + /* generateCharacter has called grantFullSkillList since it was written, and + build-packs gives all 62 skills to every packed creature. generateNPC did not, so a + generated adversary carried about eight rows and a GM asked for Listen or Dodge had + nothing on the sheet to roll — the first thing reported about this generator. A + shared finishing step that one of three callers omits is invisible from inside that + caller, which is what makes it worth naming here rather than remembering. */ + for (const fn of ["generateCharacter", "generateNPC", "generateCreature"]) { + const at = src.indexOf(`export async function ${fn}(`); + if (at === -1) { problems.push(`ringbrp.mjs no longer defines ${fn}`); continue; } + const after = src.slice(at + 10); + const ends = [after.indexOf("\nexport "), after.indexOf("\nasync function "), after.indexOf("\nfunction ")] + .filter(i => i !== -1); + const body = after.slice(0, ends.length ? Math.min(...ends) : after.length); + if (!/grantFullSkillList\s*\(/.test(body)) { + problems.push(`${fn} never calls grantFullSkillList — the actor it builds reaches the ` + + `table with only the skills it was explicitly given, and every other actor in the ` + + `game has all 62 with the untrained ones at their printed base`); + } + } +} + +/* ---------- 5. the engine must actually ask for the kit ---------- */ { const src = await readFile(path.join(ROOT, "ringbrp.mjs"), "utf8"); const start = src.indexOf("export async function generateNPC(");