Generated NPCs get the full skill list, like everything else does

"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 <noreply@anthropic.com>
This commit is contained in:
slaguru666
2026-09-23 01:20:13 +01:00
co-authored by Claude Opus 5
parent 98ca282b89
commit dda8238a12
2 changed files with 35 additions and 1 deletions
+10
View File
@@ -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.
+25 -1
View File
@@ -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(");