Files
RingBRP/tools/roster.mjs
T
slaguru666andClaude Opus 5 f576c7977d Add a second intake: six more agents, with portraits
Written for the group that actually plays. Three postings and three trades
again, and none of the six repeats a posting or a trade already on the sheet,
so the roster now covers sixteen different jobs and no two players can pick the
same character by accident.

  Gareth Pollard      Night Warden          Field Officer
  Dominic Ashcroft    Anchor Officer        Senior Field Officer
  Neil Braithwaite    Pathology Officer     Field Officer
  Kate Renshaw        Interview Officer     Senior Field Officer
  Danny Sharrock      Construction Worker   Probationary
  Colin Ferriby       IT Specialist         Probationary

Each also gets a before-the-department entry, because without one they cannot
play OPEN DAY — the prologue builds its civilians off the BEFORE table, and the
build throws on a roster agent that has no entry in it. Two of the six were
already trades, so their civilian self is the same job with none of the kit.

Two guards had to be corrected rather than satisfied, both for the same reason:
they had encoded an accident of the roster being exactly ten.

  - "the ten cover all ten trades exactly once" cannot survive a sixteenth
    agent. The property that actually matters is that no trade is missing, so
    that is what it asserts now.
  - "the roster should be half trades" would mean picking characters to satisfy
    a ratio. It now asserts both halves are present and neither dwarfs the
    other, and separately that no job appears twice — which is the check with
    real teeth, and it passes.

The portrait pass caught an arithmetic slip of mine: Sharrock came back looking
early forties against a sheet that said 35, and his biography says twenty years
on sites, which would have him starting at fifteen. He is 42.

Verified in Foundry: 36 pregens in the pack, all twelve import, sheets render
with their portraits.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 17:19:58 +01:00

318 lines
19 KiB
JavaScript

/**
* THE DUTY ROSTER — ten ready-to-play agents for the compendium.
*
* Written compactly: a posting or a trade, a rank, eight characteristics and a face.
* Skills and kit are expanded from the register at build time by the same helper the
* starter scenario uses, so a roster agent cannot drift from the generator. Change
* TRADE_BANDS and every doctor on this list changes with it.
*
* Postings and trades in equal numbers on purpose. A group picking from this sheet
* sees both halves of character creation without anybody having to explain them, and
* no two entries share a posting or a trade.
*
* Portraits are generated art, one per agent, in art/portraits.
*/
export const ROSTER = [
/* ---------------- postings: career department ---------------- */
{ key: "pc_holloway", name: "Marta Holloway", role: "lead", rank: "Senior Field Officer",
portrait: "pc_holloway", chosenStyle: "reflex", species: "baseline",
age: { chronological: 44, physiological: 44 },
ch: { str: 11, siz: 12, con: 13, int: 15, pow: 14, dex: 14, cha: 14, edu: 13 },
talents: ["first_through", "read_room", "clean_scene"],
bio: "<p>Nineteen years, four of them on secondment to an office that no longer "
+ "appears in the directory. Signs for everything and reads it first.</p>"
+ "<p><em>Anchor:</em> a wedding ring she does not wear, kept in a coin envelope "
+ "in her jacket.</p>" },
{ key: "pc_okonkwo", name: "Ben Okonkwo", role: "heavy", rank: "Field Officer",
portrait: "pc_okonkwo", chosenStyle: "reflex", species: "baseline",
age: { chronological: 33, physiological: 33 },
ch: { str: 17, siz: 16, con: 16, int: 11, pow: 11, dex: 13, cha: 11, edu: 10 },
talents: ["hard_landing", "first_through"],
bio: "<p>Goes through the door and holds the corridor while everyone else does the "
+ "clever part. Has never once complained about being the one who is hit.</p>"
+ "<p><em>Anchor:</em> his sister's voice note, saved and never deleted.</p>" },
{ key: "pc_sandoval", name: "Rey Sandoval", role: "overwatch", rank: "Field Officer",
portrait: "pc_sandoval", chosenStyle: "deliberate", species: "baseline",
age: { chronological: 37, physiological: 37 },
ch: { str: 12, siz: 12, con: 13, int: 14, pow: 13, dex: 17, cha: 10, edu: 12 },
talents: ["first_through", "unblinking"],
bio: "<p>Takes the long lane and the long view. Keeps a private list of the shots "
+ "she chose not to take, and will not discuss it.</p>"
+ "<p><em>Anchor:</em> a paperback with somebody else's name inside the cover.</p>" },
{ key: "pc_bhattacharya", name: "Arun Bhattacharya", role: "registry",
rank: "Senior Field Officer", portrait: "pc_bhattacharya",
chosenStyle: "deliberate", species: "baseline",
age: { chronological: 56, physiological: 56 },
ch: { str: 10, siz: 11, con: 11, int: 17, pow: 15, dex: 11, cha: 12, edu: 17 },
talents: ["chain_custody", "debriefer", "requisition"],
bio: "<p>Thirty years in the registry. Has read files that were withdrawn "
+ "before he finished them and remembers what was in them anyway.</p>"
+ "<p><em>Anchor:</em> a brass slide rule that belonged to his predecessor.</p>" },
{ key: "pc_rahimi", name: "Soraya Rahimi", role: "settlements", rank: "Field Officer",
portrait: "pc_rahimi", chosenStyle: "deliberate", species: "baseline",
age: { chronological: 41, physiological: 41 },
ch: { str: 10, siz: 11, con: 12, int: 15, pow: 15, dex: 11, cha: 17, edu: 15 },
talents: ["read_room", "debriefer"],
bio: "<p>Writes the arrangement that everyone can live with, then holds both sides "
+ "to it. Has never raised her voice on an operation.</p>"
+ "<p><em>Anchor:</em> her mother's tea glass, chipped, carried in a sock.</p>" },
/* ---------------- trades: recruited from an ordinary job ---------------- */
{ key: "pc_nkemdirim", name: "Dr Chidi Nkemdirim", trade: "doctor", rank: "Probationary",
portrait: "pc_nkemdirim", chosenStyle: "deliberate", species: "baseline",
age: { chronological: 32, physiological: 32 },
ch: { str: 10, siz: 11, con: 12, int: 16, pow: 13, dex: 13, cha: 13, edu: 18 },
talents: ["triage", "unblinking"],
bio: "<p>Six years of emergency medicine and eleven months of this. Still the "
+ "calmest person in the room, and still startled by what the room contains.</p>"
+ "<p><em>Anchor:</em> a pager that has not worked since 2019.</p>" },
{ key: "pc_kowalczyk", name: "Marek Kowalczyk", trade: "engineer", rank: "Probationary",
portrait: "pc_kowalczyk", chosenStyle: "reflex", species: "baseline",
age: { chronological: 30, physiological: 30 },
ch: { str: 13, siz: 13, con: 13, int: 17, pow: 11, dex: 14, cha: 11, edu: 15 },
talents: ["hard_landing", "requisition"],
bio: "<p>Chartered, unimpressed, and the only person who asks what a thing is made "
+ "of before asking what it wants.</p>"
+ "<p><em>Anchor:</em> the first tool he ever bought, resharpened forty times.</p>" },
{ key: "pc_finch", name: "Wren Finch", trade: "criminal", rank: "Probationary",
portrait: "pc_finch", chosenStyle: "reflex", species: "baseline",
age: { chronological: 26, physiological: 26 },
ch: { str: 10, siz: 10, con: 12, int: 14, pow: 12, dex: 18, cha: 14, edu: 9 },
talents: ["cover_work", "clean_scene"],
bio: "<p>Opens things without leaving a mark, which is the part she is proud of. "
+ "Signed the Act in an interview room, which is the part she is not.</p>"
+ "<p><em>Anchor:</em> a door key to a flat that was demolished.</p>" },
{ key: "pc_agyeman", name: "Joseph Agyeman", trade: "police", rank: "Field Officer",
portrait: "pc_agyeman", chosenStyle: "reflex", species: "baseline",
age: { chronological: 52, physiological: 52 },
ch: { str: 14, siz: 14, con: 14, int: 13, pow: 13, dex: 12, cha: 14, edu: 12 },
talents: ["read_room", "chain_custody", "first_through"],
bio: "<p>Twenty-six years on division before somebody handed him a file with no "
+ "crime number. Knows how a statement is taken and how a scene is lost.</p>"
+ "<p><em>Anchor:</em> his old warrant number, written inside his watch strap.</p>" },
{ key: "pc_lindqvist", name: "Professor Åke Lindqvist", trade: "professor",
rank: "Field Officer", portrait: "pc_lindqvist",
chosenStyle: "deliberate", species: "baseline",
age: { chronological: 63, physiological: 63 },
ch: { str: 9, siz: 11, con: 10, int: 17, pow: 16, dex: 10, cha: 14, edu: 19 },
talents: ["debriefer", "wrong_memory", "read_room"],
bio: "<p>A chair in comparative folklore and a reading list nobody else has "
+ "finished. Was consulted once and never quite sent home.</p>"
+ "<p><em>Anchor:</em> a photograph of a lecture theatre, taken from the back.</p>" },
/* ---------------- second intake ---------------- */
/* Six more, written for the group that actually plays. Three postings and three
trades again, and none of the six repeats a posting or a trade already on the
sheet above, so the roster covers sixteen different jobs and no two players
pick the same character by accident. */
{ key: "pc_pollard", name: "Gareth Pollard", role: "warden", rank: "Field Officer",
portrait: "pc_pollard", chosenStyle: "reflex", species: "baseline",
age: { chronological: 47, physiological: 47 },
ch: { str: 14, siz: 15, con: 16, int: 12, pow: 13, dex: 12, cha: 11, edu: 10 },
talents: ["unblinking", "first_through"],
bio: "<p>Sits with a site all night so that nobody else has to, and writes down the "
+ "times. Has never once been found asleep and never once complained about it.</p>"
+ "<p><em>Anchor:</em> a wind-up alarm clock that has not kept time since 2003.</p>" },
{ key: "pc_ashcroft", name: "Dominic Ashcroft", role: "anchor", rank: "Senior Field Officer",
portrait: "pc_ashcroft", chosenStyle: "deliberate", species: "baseline",
age: { chronological: 38, physiological: 41 },
ch: { str: 11, siz: 12, con: 13, int: 15, pow: 17, dex: 13, cha: 12, edu: 14 },
talents: ["out_of_step", "borrowed_hour", "not_quite_here"],
bio: "<p>Holds the door open while everyone else goes through it, and stays filed "
+ "correctly for as long as that takes. Three years older than his birthday says "
+ "and does not discuss the difference.</p>"
+ "<p><em>Anchor:</em> his own school tie, cut in half; the department has the "
+ "other half.</p>" },
{ key: "pc_braithwaite", name: "Neil Braithwaite", role: "pathology", rank: "Field Officer",
portrait: "pc_braithwaite", chosenStyle: "deliberate", species: "baseline",
age: { chronological: 51, physiological: 51 },
ch: { str: 11, siz: 12, con: 12, int: 17, pow: 12, dex: 13, cha: 10, edu: 17 },
talents: ["chain_custody", "unblinking", "triage"],
bio: "<p>Establishes what a thing was, which is usually the question the case turns "
+ "on. Talks to the table, not to the room, and is right often enough that "
+ "nobody minds.</p>"
+ "<p><em>Anchor:</em> a fountain pen his father used to sign death "
+ "certificates.</p>" },
{ key: "pc_renshaw", name: "Kate Renshaw", role: "interview", rank: "Senior Field Officer",
portrait: "pc_renshaw", chosenStyle: "deliberate", species: "baseline",
age: { chronological: 44, physiological: 44 },
ch: { str: 10, siz: 11, con: 12, int: 15, pow: 14, dex: 12, cha: 17, edu: 15 },
talents: ["debriefer", "read_room", "wrong_memory"],
bio: "<p>Sits with the witness afterwards, for as long as it takes, and is very hard "
+ "to lie to twice. Gets more out of an hour and a pot of tea than the rest of "
+ "the department gets out of a warrant.</p>"
+ "<p><em>Anchor:</em> a hair clip that belonged to a woman whose statement she "
+ "never finished taking.</p>" },
{ key: "pc_sharrock", name: "Danny Sharrock", trade: "builder", rank: "Probationary",
portrait: "pc_sharrock", chosenStyle: "reflex", species: "baseline",
age: { chronological: 42, physiological: 42 },
ch: { str: 16, siz: 15, con: 15, int: 12, pow: 10, dex: 14, cha: 12, edu: 9 },
talents: ["hard_landing", "first_through"],
bio: "<p>Twenty years of sites and other people's mistakes. Knows what a building is "
+ "about to do a good few seconds before it does it, and says so, loudly.</p>"
+ "<p><em>Anchor:</em> a bent nail from the first roof he ever stripped, kept in "
+ "a tobacco tin.</p>" },
{ key: "pc_ferriby", name: "Colin Ferriby", trade: "it", rank: "Probationary",
portrait: "pc_ferriby", chosenStyle: "deliberate", species: "baseline",
age: { chronological: 29, physiological: 29 },
ch: { str: 10, siz: 12, con: 11, int: 17, pow: 12, dex: 13, cha: 10, edu: 14 },
talents: ["echo_sense", "chain_custody"],
bio: "<p>Second line, then infrastructure. Has read the logs nobody else reads and "
+ "believes them over people, which is either the reason he is here or the "
+ "reason he was let go.</p>"
+ "<p><em>Anchor:</em> a dead hard drive with his own handwriting on the "
+ "label.</p>" }
];
/**
* THE SAME TEN, BEFORE THE DEPARTMENT FOUND THEM.
*
* Same person, same characteristics, same face — a civilian who has not been inducted
* and has never been issued anything. Skills and tools come from the trade alone, so a
* player can hold the two sheets side by side and see exactly what the job did to them.
* (The linguist is the one who already speaks trade cant before she arrives. It is a
* language, and she is a linguist.)
*
* Five of the ten went into the department straight into a posting rather than out of a
* trade, so this is where they came from. Between them the ten cover all ten trades
* exactly once, which means this set also shows a table the whole trade half of
* character creation without anybody having to read the rules for it.
*
* `job` replaces the rank line on the sheet. The second paragraph of each biography is
* the seam: what they did that got them noticed. It is the hook a GM pulls on.
*/
export const BEFORE = {
pc_holloway: { trade: "linguist", job: "Court Interpreter", age: 25,
bio: "<p>Interpreting at magistrates' courts and immigration tribunals. Ten hours a "
+ "day saying other people's worst sentences in the first person, and going home "
+ "able to repeat every one of them.</p>"
+ "<p><em>How they found her:</em> she interpreted a statement nobody had asked "
+ "her to hear, and then wrote down what the room had done while it was said.</p>" },
pc_okonkwo: { trade: "builder", job: "Steel Erector", age: 28,
bio: "<p>Ten storeys up on a frame with no floor in it yet, in weather that sends "
+ "everyone else down the ladder. Never late, never sick, and never the one who "
+ "says the job cannot be done.</p>"
+ "<p><em>How they found him:</em> he carried two men off a site that had gone "
+ "wrong in a way the report could not describe, and he did not ask a single "
+ "question about it.</p>" },
pc_sandoval: { trade: "it", job: "Network Field Engineer", age: 31,
bio: "<p>Alone at the top of a mast in the dark with a laptop and a flask, fixing "
+ "links for towns that only notice the network when it stops. Long climbs, long "
+ "views, long silences.</p>"
+ "<p><em>How they found her:</em> a relay logged forty minutes of traffic from a "
+ "site decommissioned in 1994, and she kept the file instead of clearing it.</p>" },
pc_bhattacharya: { trade: "scientist", job: "Research Chemist", age: 26,
bio: "<p>An industrial laboratory, a bench, and a notebook filled in the same hand "
+ "every day for four years. Slow, exact, and constitutionally unable to leave a "
+ "discrepancy alone.</p>"
+ "<p><em>How they found him:</em> he noticed a batch record amended by somebody "
+ "who had never worked there, and he raised it in writing. Twice.</p>" },
pc_rahimi: { trade: "entertainer", job: "Local Radio Presenter", age: 33,
bio: "<p>Mid-morning phone-in on a station nobody outside the county has heard of. "
+ "Six years of listening to people who rang up angry and hung up calmer.</p>"
+ "<p><em>How they found her:</em> she talked a caller down from something on air "
+ "for eleven minutes, and the recording of those minutes does not play back the "
+ "same way twice.</p>" },
pc_nkemdirim: { trade: "doctor", job: "Registrar, Emergency Medicine", age: 31,
bio: "<p>Six years of A&amp;E and the flat calm that comes with it. The one the "
+ "department turns to when nobody else will say the decision out loud.</p>"
+ "<p><em>How they found them:</em> a patient came in with an injury that had "
+ "healed in the wrong direction, and the notes they wrote were too good to leave "
+ "in the system.</p>" },
pc_kowalczyk: { trade: "engineer", job: "Structural Engineer", age: 29,
bio: "<p>Chartered, practical, and happiest with a survey and a set of drawings that "
+ "disagree. Asks what a thing is made of before he asks what it is for.</p>"
+ "<p><em>How they found him:</em> he condemned a footbridge on load figures that "
+ "were correct, for a span he had measured three times and got three "
+ "answers.</p>" },
pc_finch: { trade: "criminal", job: "Between Things", age: 25,
bio: "<p>Not organised, and not caught either — doors, locks and other people's "
+ "assumptions, worked quietly and left tidy. Rents by the week and pays cash.</p>"
+ "<p><em>How they found her:</em> she was inside a building she had no business "
+ "in on the afternoon it stopped being on the street it was on, and she was the "
+ "only witness who could describe the inside.</p>" },
pc_agyeman: { trade: "police", job: "Detective Sergeant", age: 50,
bio: "<p>Twenty-six years on division. Takes a statement properly, keeps a scene, "
+ "and has watched three generations of the same families come through the same "
+ "door.</p>"
+ "<p><em>How they found him:</em> a file with no crime number landed on his desk "
+ "and he worked it anyway, on his own time, and got further than he was meant "
+ "to.</p>" },
pc_lindqvist: { trade: "professor", job: "Professor of Comparative Folklore", age: 59,
bio: "<p>A chair, a reading list, and forty years of stories about places that take "
+ "people and give them back wrong. Treats them as evidence rather than as "
+ "literature.</p>"
+ "<p><em>How they found him:</em> he was consulted once, as an academic "
+ "courtesy, and gave an answer so specific that he was never quite sent "
+ "home.</p>" },
pc_pollard: { trade: "police", job: "Custody Sergeant", age: 39,
bio: "<p>Nights, on the custody desk, for eleven years. Signs people in, signs them "
+ "out, and writes down the times, which is the entire job and most of the "
+ "reason it is done properly.</p>"
+ "<p><em>How they found him:</em> he logged a detainee in and out on the same "
+ "sheet, in his own hand, forty minutes apart, and there is no detainee.</p>" },
pc_ashcroft: { trade: "scientist", job: "Research Physicist", age: 31,
bio: "<p>A university group, a beamline, and six years of measuring one thing very "
+ "carefully. Comfortable with results that are correct and unwelcome.</p>"
+ "<p><em>How they found him:</em> he published a null result nobody wanted, twice, "
+ "and the second time he included the run his supervisor had asked him to "
+ "drop.</p>" },
pc_braithwaite: { trade: "doctor", job: "Consultant Histopathologist", age: 41,
bio: "<p>Twenty years of establishing what a thing was. Talks to the specimen rather "
+ "than the room, and is right often enough that nobody minds.</p>"
+ "<p><em>How they found him:</em> he reported a sample as being from a tissue "
+ "type he could not name, in writing, under his own signature, and refused to "
+ "amend it.</p>" },
pc_renshaw: { trade: "entertainer", job: "Actor", age: 37,
bio: "<p>Eleven years of rep, tour and two soaps, and a decade before that of front "
+ "of house. Learned to read a room from the wrong side of the lights and never "
+ "stopped doing it.</p>"
+ "<p><em>How they found her:</em> she took a witness statement off a stranger in "
+ "a foyer, unasked, in longhand, and it is the best one anybody took.</p>" },
pc_sharrock: { trade: "builder", job: "Site Foreman", age: 39,
bio: "<p>Twenty years of sites, scaffold and other people's mistakes, and a voice "
+ "that carries across one. Knows what a building is about to do.</p>"
+ "<p><em>How they found him:</em> he stopped a pour because a wall was in the "
+ "wrong place, and he was right, and the drawings agreed with him afterwards "
+ "and had not before.</p>" },
pc_ferriby: { trade: "it", job: "Infrastructure Engineer", age: 28,
bio: "<p>Second line, then infrastructure. Has read the logs nobody reads and "
+ "believes them over people, which has cost him two jobs.</p>"
+ "<p><em>How they found him:</em> he kept a packet capture his employer told him "
+ "twice to delete, because the timestamps in it do not run in order.</p>" }
};