A train has been running the same four miles of track since 3 November 1881. It
has never stopped. Sixty-one people are aboard and all of them are alive.
The supernatural is science fiction underneath, which is the point. A railway
company laid track to a filed survey instead of to the ground, then described the
line as being where it was not — ten thousand times, in timetables and waybills
and telegraph orders, for six months, at a constant speed. Reality keeps a record
of what belongs where, and that is the most patient argument there is. The record
gave way with No. 9 inside it, and the RUNNING is what holds the crossing open: a
standing wave sustained by its own motion the way a bow sustains a note.
So the players must board a train that cannot stop, and then find that sixty-one
people at six passes is four hours they do not have. The ending is what they
choose to do about arithmetic they cannot beat, in front of a conductor holding
the manifest. No roll resolves it.
It is also the case that teaches Standing Order 9: NOTHING MODERN GOES THROUGH.
Using a modern object across costs a Coherence and moves the clock; carrying it
costs nothing; anchors are exempt. The catalogue already had the 1881 issue —
Webley, Martini-Henry, oil lantern, brass compass, pocket watch, buff coat,
laudanum — so the constraint needed no new content and gives the table something
to feel in the first twenty minutes.
Ships as docs/scenarios/THROUGH_TRAIN.md and a Foundry Adventure: 7 GM pages, 6
handouts including the survey that started it and a manifest deliberately held
back to Act Three, the case file at Containment 6 / Resources 5 / Clearance 4
with its four legs loaded, four of the people aboard, and 7 plates to show.
Two fixes from playing it:
- Bhattacharya's RESEARCH was 1%. Placing the new skills last night I swapped
Occult into Registry's core and took Research out — a records office that
cannot look anything up. Research is back in core; Antiquities gets its
Knowledge back the same way.
- The boarding was unrunnable. At halved skill the set-piece put four agents on
12–31% and all four missed, which stalls the one scene the scenario is built
around. It is an average roll now and only a fumble is a fall, with the
reason written into the text so nobody "fixes" it back. Re-run: 3 of 4 aboard
on the first pass and one waiting for the next, which is the shape it wanted.
Version 1.3.0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
408 lines
26 KiB
JavaScript
408 lines
26 KiB
JavaScript
/**
|
||
* THE REGISTER — postings, trades, tiers and what stores hands out.
|
||
*
|
||
* Pure data, and deliberately free of Foundry: the engine imports it, the rulebook
|
||
* generator imports it, the guards import it and the tests import it. Until now the
|
||
* guards and the rulebook read these tables out of `ringbrp.mjs` with a regular
|
||
* expression and `eval`, because that file reaches for `game` on load and cannot be
|
||
* imported outside a browser. That worked and it was a trap — a table could be
|
||
* reformatted into something the regex no longer matched and every check that
|
||
* depended on it would quietly stop checking.
|
||
*
|
||
* Nothing in this file may import from the runtime or touch a Foundry global. The
|
||
* same rule rules.mjs lives by, for the same reason.
|
||
*/
|
||
|
||
/* ---------------- roles ---------------- */
|
||
/** Each role is a skill emphasis, a kit, a talent leaning and a rank feel. */
|
||
export const ROLES = {
|
||
lead: { label: "Field Lead",
|
||
blurb: "Runs the team on the ground and signs for what happens to it. Firearms, tradecraft and the nerve to go first.", stat: "dex",
|
||
core: ["firearm:pistol","dodge","tradecraft","navigate","athletics","stealth"],
|
||
support: ["insight","spot","first_aid","melee_weapon:knife","pilot:ground_car"],
|
||
talents: ["dead_reckon","first_through","hard_landing","cover_work"],
|
||
kit: ["pistol_med","soft_armour","cover_id","key","torch", "warrant_card", "cordon_kit", "door_wedges"] },
|
||
technical: { label: "Technical",
|
||
blurb: "Field systems, electronics and the physics of a site that is behaving incorrectly. Builds and breaks the equipment.", stat: "int",
|
||
core: ["engineering:field_systems","repair:electronics","computer_use","anomaly_lore","science:physics"],
|
||
support: ["repair:mechanical","research","first_aid","firearm:pistol","demolition"],
|
||
talents: ["chain_custody","requisition","clean_scene","echo_sense"],
|
||
kit: ["pistol_light","stab_vest","toolkit","scanner","power_cell", "density_scanner", "probe", "thermometer"] },
|
||
talker: { label: "Liaison",
|
||
blurb: "The face the department shows a witness. Reads a room, holds a cover, and gets the sentence nobody meant to say.", stat: "cha",
|
||
core: ["insight","persuade","fast_talk","bargain","anthropology","language:trade_cant"],
|
||
support: ["tradecraft","xenology:baseline","spot","status","firearm:pistol"],
|
||
talents: ["read_room","cover_work","debriefer","wrong_memory"],
|
||
kit: ["pistol_light","jacket","cover_id","notebook","camera", "blanket", "clipboard", "vacuum_flask"] },
|
||
medic: { label: "Physician",
|
||
blurb: "Keeps agents alive and works out what a body has been through. The only posting that can undo a Major Wound.", stat: "int",
|
||
core: ["medicine","first_aid","science:biology","insight","xenology:baseline"],
|
||
support: ["anthropology","spot","dodge","firearm:pistol","research"],
|
||
talents: ["triage","debriefer","read_room","not_quite_here"],
|
||
kit: ["pistol_light","stab_vest","medkit","fieldmedic","sample_case", "analgesics", "blanket", "specimen_jars"] },
|
||
heavy: { label: "Containment",
|
||
blurb: "Containment. Goes through the door, holds the corridor, and puts things in boxes that do not want to be in boxes.", stat: "str",
|
||
core: ["firearm:rifle","brawl","melee_weapon:prybar","athletics","dodge"],
|
||
support: ["demolition","first_aid","repair:mechanical","spot","melee_weapon:shield"],
|
||
talents: ["hard_landing","chain_custody","first_through","clean_scene"],
|
||
kit: ["rifle_med","breach_suit","prybar","riot_shield","restraints","climbing_kit", "bolt_croppers", "cordon_kit", "ear_defenders"] },
|
||
/* ---------------------------------------------------------------
|
||
The rest of the establishment.
|
||
|
||
Five postings were never the whole agency — they were the ones a
|
||
field team fields. These are the other twenty on the books: the
|
||
registry that keeps the record crossings are made against, the
|
||
archaic postings the agency has never got round to abolishing, and
|
||
the specialists nobody calls until the day they are the only thing
|
||
that helps. Each carries its own kit, and each kit has at least one
|
||
thing no other posting is issued.
|
||
--------------------------------------------------------------- */
|
||
|
||
registry: { label: "Registry Officer",
|
||
blurb: "Keeps the record that a crossing is made against. Knows what the file says and, more usefully, what it used to say.", stat: "edu",
|
||
core: ["research","occult","knowledge","anomaly_lore","language:own"],
|
||
support: ["computer_use","insight","spot","first_aid","tradecraft","firearm:pistol"],
|
||
talents: ["chain_custody","wrong_memory","debriefer","quorum"],
|
||
kit: ["spectacles","notebook","handset","jacket","pistol_light", "carbon_book", "date_stamp", "seals"] },
|
||
|
||
survey: { label: "Ground Survey Officer",
|
||
blurb: "Reads ground. Establishes where a site actually is, how it is shaped, and which way out will still be there later.", stat: "int",
|
||
core: ["navigate","engineering:field_systems","science:physics","spot","athletics"],
|
||
support: ["track","stealth","first_aid","repair:mechanical","firearm:pistol"],
|
||
talents: ["dead_reckon","first_through","hard_landing","echo_sense"],
|
||
kit: ["survey_kit","brass_compass","binocs","oilcloth_coat","rope", "probe", "groundsheet", "chalk"] },
|
||
|
||
anchor: { label: "Anchor Officer",
|
||
blurb: "Holds the door open. Transposition, and the discipline to stay filed correctly while doing it.", stat: "pow",
|
||
core: ["transposition","anomaly_lore","insight","listen","dodge"],
|
||
support: ["knowledge","spot","first_aid","tradecraft","research"],
|
||
talents: ["out_of_step","borrowed_hour","not_quite_here","distributed"],
|
||
kit: ["anchor","threshold_key","handset","power_cell","soft_armour", "locator_tags", "cold_light", "blanket"] },
|
||
|
||
interview: { label: "Interview Officer",
|
||
blurb: "Sits with the witness afterwards. Patient, unhurried, and very hard to lie to twice.", stat: "cha",
|
||
core: ["insight","persuade","listen","language:own","anthropology"],
|
||
support: ["psychology","spot","tradecraft","first_aid","firearm:pistol"],
|
||
talents: ["debriefer","read_room","wrong_memory","unblinking"],
|
||
kit: ["dictaphone","cover_id","notebook","jacket","pistol_light", "carbon_book", "vacuum_flask", "clipboard"] },
|
||
|
||
warden: { label: "Night Warden",
|
||
blurb: "Sits with a site overnight so that nobody else has to. Listens, watches, and writes down the times.", stat: "con",
|
||
core: ["listen","spot","stealth","dodge","brawl"],
|
||
support: ["first_aid","melee_weapon:shield","athletics","melee_weapon:club","anomaly_lore"],
|
||
talents: ["unblinking","out_of_step","first_through","clean_scene"],
|
||
kit: ["oil_lantern","ward","cudgel","riot_shield","stab_vest","torch", "vacuum_flask", "stopwatch", "blanket"] },
|
||
|
||
stores: { label: "Stores Officer",
|
||
blurb: "Requisition, custody and the paperwork that moves materiel. Can get you a thing the department says it does not have.", stat: "edu",
|
||
core: ["bargain","status","knowledge","repair:mechanical","computer_use"],
|
||
support: ["insight","research","first_aid","firearm:pistol","tradecraft"],
|
||
talents: ["requisition","chain_custody","quorum","debriefer"],
|
||
kit: ["sealed_cannister","spare_cells","rations","water","jacket", "carbon_book", "seals", "master_keys"] },
|
||
|
||
transport: { label: "Transport Officer",
|
||
blurb: "Drives, flies, and gets a wounded team off a site. Keeps the vehicle running with what is in the boot.", stat: "dex",
|
||
core: ["pilot:ground_car","pilot:aircraft","navigate","repair:mechanical","spot"],
|
||
support: ["dodge","athletics","first_aid","firearm:pistol","listen"],
|
||
talents: ["dead_reckon","hard_landing","first_through","borrowed_hour"],
|
||
kit: ["beacon","revolver","impact_vest","torch","rope", "master_keys", "groundsheet", "blanket"] },
|
||
|
||
pathology: { label: "Pathology Officer",
|
||
blurb: "Examines what is left. Establishes what a thing was, which is usually the question the case turns on.", stat: "int",
|
||
core: ["medicine","science:biology","xenology:baseline","insight","research"],
|
||
support: ["first_aid","spot","religion","dodge","melee_weapon:knife"],
|
||
talents: ["triage","chain_custody","unblinking","clean_scene"],
|
||
kit: ["chirurgeon_kit","gas_mask","envsuit","sample_case","dagger", "specimen_jars", "swab_kit", "barrier_gloves"] },
|
||
|
||
materials: { label: "Materials Officer",
|
||
blurb: "Substances, residues and the objects a site leaves behind. Handles what nobody else is cleared to touch.", stat: "int",
|
||
core: ["science:physics","science:botany","repair:electronics","research","anomaly_lore"],
|
||
support: ["engineering:field_systems","spot","first_aid","computer_use","firearm:pistol"],
|
||
talents: ["chain_custody","echo_sense","requisition","quorum"],
|
||
kit: ["field_kit","geiger","breather","stab_vest","pistol_light", "specimen_jars", "barrier_gloves", "seal_foam"] },
|
||
|
||
recording: { label: "Recording Officer",
|
||
blurb: "Documents the site as found. Photographs, measurements and an exhibit trail that survives a tribunal.", stat: "dex",
|
||
core: ["art","spot","computer_use","sleight_of_hand","stealth"],
|
||
support: ["repair:electronics","insight","first_aid","listen","firearm:pistol"],
|
||
talents: ["clean_scene","chain_custody","cover_work","echo_sense"],
|
||
kit: ["optic_visor","data_wafer","evidence_tape","jacket","pistol_light", "uv_lamp", "cordon_kit", "chalk"] },
|
||
|
||
access: { label: "Access Officer",
|
||
blurb: "Gets the team into places the owner has locked, and out again without a record of either.", stat: "dex",
|
||
core: ["sleight_of_hand","stealth","repair:mechanical","spot","dodge"],
|
||
support: ["computer_use","athletics","first_aid","tradecraft","melee_weapon:knife"],
|
||
talents: ["first_through","clean_scene","cover_work","out_of_step"],
|
||
kit: ["iron_manacles","monoblade","key","toolkit","soft_armour", "lock_rakes", "master_keys", "door_wedges"] },
|
||
|
||
disposal: { label: "Disposal Officer",
|
||
blurb: "Renders a thing safe, or renders it gone. Demolition, containment and a very steady hand.", stat: "int",
|
||
core: ["demolition","repair:electronics","engineering:field_systems","spot","dodge"],
|
||
support: ["first_aid","athletics","anomaly_lore","firearm:rifle","melee_weapon:axe"],
|
||
talents: ["first_through","hard_landing","chain_custody","unblinking"],
|
||
kit: ["field_net","entrenching_tool","hardsuit","toolkit","torch", "seal_foam", "cordon_kit", "ear_defenders"] },
|
||
|
||
overwatch: { label: "Overwatch Officer",
|
||
blurb: "Holds the long angle and tells the team what is coming. Shoots when nothing else will do.", stat: "dex",
|
||
core: ["firearm:rifle","spot","stealth","dodge","athletics"],
|
||
support: ["navigate","first_aid","listen","track","firearm:pistol"],
|
||
talents: ["dead_reckon","unblinking","out_of_step","hard_landing"],
|
||
kit: ["rifle_heavy","binocs","soft_armour","rations","torch", "spyglass", "groundsheet", "cold_light"] },
|
||
|
||
rigging: { label: "Rigging Officer",
|
||
blurb: "Height, depth and confined access. Gets people and equipment up and down shafts that were not built for it.", stat: "str",
|
||
core: ["athletics","repair:mechanical","engineering:field_systems","dodge","melee_weapon:axe"],
|
||
support: ["first_aid","spot","navigate","brawl","stealth"],
|
||
talents: ["hard_landing","first_through","requisition","distributed"],
|
||
kit: ["boarding_axe","climbing_kit","rope","impact_vest","torch", "bolt_croppers", "probe", "door_wedges"] },
|
||
|
||
translation: { label: "Translation Officer",
|
||
blurb: "Language, sign and the things said in neither. Reads a document nobody else in the building can.", stat: "int",
|
||
core: ["language:trade_cant","language:sign","language:own","anthropology","insight"],
|
||
support: ["research","knowledge","first_aid","xenology:baseline","firearm:pistol"],
|
||
talents: ["debriefer","read_room","quorum","wrong_memory"],
|
||
kit: ["wax_tablet","notebook","handset","jacket","pistol_light", "quill_ink", "carbon_book", "spectacles"] },
|
||
|
||
naturalist: { label: "Field Naturalist",
|
||
blurb: "Living things, and things that are pretending to be. Tracks, samples and identifies what came through.", stat: "int",
|
||
core: ["science:botany","science:biology","track","scent","spot"],
|
||
support: ["navigate","stealth","first_aid","athletics","melee_weapon:sword"],
|
||
talents: ["dead_reckon","echo_sense","unblinking","triage"],
|
||
kit: ["machete","sample_case","binocs","oilcloth_coat","rations", "specimen_jars", "swab_kit", "groundsheet"] },
|
||
|
||
antiquities: { label: "Antiquities Officer",
|
||
blurb: "An archaic posting the department never abolished. Old objects, older records, and the manners to ask about both.", stat: "edu",
|
||
core: ["religion","art","anomaly_lore","research","status"],
|
||
support: ["knowledge","insight","spot","first_aid","melee_weapon:sword","firearm:pistol"],
|
||
talents: ["chain_custody","wrong_memory","borrowed_hour","quorum"],
|
||
kit: ["pocket_watch","tallow_candle","duelling_sword","brigandine","flintlock", "quill_ink", "sealing_wax", "sand_glass"] },
|
||
|
||
pursuit: { label: "Pursuit Officer",
|
||
blurb: "Follows what has left the site. Ground, scent, and the stamina to keep going after it stops being sensible.", stat: "con",
|
||
core: ["track","athletics","spot","listen","stealth"],
|
||
support: ["navigate","dodge","first_aid","scent","firearm:rifle"],
|
||
talents: ["dead_reckon","first_through","out_of_step","unblinking"],
|
||
kit: ["carbine","restraints","soft_armour","rations","torch", "locator_tags", "cold_light", "wake_tablets"] },
|
||
|
||
settlements: { label: "Settlements Officer",
|
||
blurb: "Makes the arrangement, files it, and never speaks of the terms again. Most cases close here rather than in a fight.", stat: "cha",
|
||
core: ["bargain","status","insight","persuade","knowledge"],
|
||
support: ["research","anthropology","first_aid","tradecraft","firearm:pistol"],
|
||
talents: ["read_room","debriefer","requisition","quorum"],
|
||
kit: ["quiet_ledger","cover_id","notebook","jacket","pistol_light", "carbon_book", "sealing_wax", "vacuum_flask"] },
|
||
|
||
signals: { label: "Signals Officer",
|
||
blurb: "Communications, interception and the electronics of a site that is broadcasting when it should not be.", stat: "int",
|
||
core: ["repair:electronics","computer_use","listen","engineering:field_systems","spot"],
|
||
support: ["research","first_aid","dodge","firearm:pistol","anomaly_lore"],
|
||
talents: ["echo_sense","distributed","quorum","requisition"],
|
||
kit: ["survey_drone","handset","power_cell","stab_vest","pistol_light", "field_slate", "locator_tags", "uv_lamp"] }
|
||
};
|
||
|
||
/** Experience tiers: how good, and what the agency calls you. */
|
||
/**
|
||
* What every officer is issued regardless of posting, what seniority adds, and the
|
||
* one thing that is theirs rather than the department's.
|
||
*
|
||
* An agent used to leave creation with five things and an anchor, which is not what
|
||
* a real posting looks like — most of this job is small equipment and paperwork.
|
||
*/
|
||
/**
|
||
* TRADES — where the recruit came from before any of this.
|
||
*
|
||
* A posting is what the department made of you. A TRADE is what you were, and the
|
||
* department does not recruit specialists in crossings because there is no such
|
||
* degree: it recruits a doctor, a joiner, a detective sergeant, and puts them
|
||
* through six weeks at the training centre.
|
||
*
|
||
* So a trade character is built in two halves. The trade is genuine expertise, rated
|
||
* at the same bands a posting's skills are — a doctor is a real doctor. INDUCTION is
|
||
* the thin agency layer on top, and it is deliberately thin, because six weeks is not
|
||
* a career and the gap between the two is the whole texture of playing one.
|
||
*/
|
||
export const TRADES = {
|
||
builder: { label: "Construction Worker",
|
||
blurb: "Twenty years of sites, scaffold and other people's mistakes. Knows what a building is doing before it does it.", stat: "str",
|
||
core: ["athletics","repair:mechanical","melee_weapon:prybar","spot","brawl"],
|
||
support: ["demolition","engineering:field_systems","first_aid","listen","dodge"],
|
||
talents: ["hard_landing","first_through","clean_scene","unblinking"],
|
||
kit: ["prybar","toolkit","ear_defenders","site_overalls","groundsheet"] },
|
||
|
||
doctor: { label: "Doctor",
|
||
blurb: "A&E, mostly. Used to people at their worst and to being the calmest thing in the room.", stat: "edu",
|
||
core: ["medicine","first_aid","science:biology","insight","knowledge"],
|
||
support: ["research","psychology","persuade","listen","spot"],
|
||
talents: ["triage","debriefer","unblinking","clean_scene"],
|
||
kit: ["medkit","trauma_unit","analgesics","specimen_jars","swab_kit"] },
|
||
|
||
professor: { label: "Professor",
|
||
blurb: "A chair in something narrow, and a reading list nobody else has finished. Argues for a living.", stat: "edu",
|
||
core: ["knowledge","research","occult","language:own","status"],
|
||
support: ["insight","persuade","science:botany","listen","art"],
|
||
talents: ["debriefer","read_room","requisition","wrong_memory"],
|
||
kit: ["carbon_book","notebook","spectacles","clipboard","quill_ink"] },
|
||
|
||
engineer: { label: "Engineer",
|
||
blurb: "Chartered, and unimpressed. Wants to know what it is made of and what happens when it stops.", stat: "int",
|
||
core: ["repair:mechanical","repair:electronics","engineering:field_systems","computer_use","demolition"],
|
||
support: ["athletics","spot","knowledge","pilot:ground_car","first_aid"],
|
||
talents: ["hard_landing","requisition","chain_custody","first_through"],
|
||
kit: ["toolkit","spare_cells","probe","power_cell","field_slate"] },
|
||
|
||
police: { label: "Law Enforcement",
|
||
blurb: "Fifteen years on division. Knows how a statement is taken and how a scene is lost.", stat: "con",
|
||
core: ["firearm:pistol","spot","insight","brawl","tradecraft"],
|
||
support: ["fast_talk","listen","athletics","dodge","first_aid"],
|
||
talents: ["read_room","chain_custody","clean_scene","first_through"],
|
||
kit: ["revolver","restraints","stab_vest","evidence_tape","clipboard"] },
|
||
|
||
it: { label: "IT Specialist",
|
||
blurb: "Second line, then infrastructure. Has read the logs nobody else reads and believes them over people.", stat: "int",
|
||
core: ["computer_use","repair:electronics","research","knowledge","spot"],
|
||
support: ["insight","listen","tradecraft","sleight_of_hand","engineering:field_systems"],
|
||
talents: ["requisition","chain_custody","debriefer","echo_sense"],
|
||
kit: ["data_wafer","scanner","toolkit","cell_stack","field_slate"] },
|
||
|
||
scientist: { label: "Scientist",
|
||
blurb: "Postdoc, a lab, and a method. Will not say a thing is impossible, only that it is not yet measured.", stat: "edu",
|
||
core: ["science:physics","science:biology","research","knowledge","repair:electronics"],
|
||
support: ["science:botany","computer_use","spot","first_aid","listen"],
|
||
talents: ["chain_custody","requisition","debriefer","echo_sense"],
|
||
kit: ["scanner","specimen_jars","geiger","thermometer","sample_case"] },
|
||
|
||
linguist: { label: "Linguist",
|
||
blurb: "Six languages properly and four enough to argue in. Hears grammar where other people hear noise.", stat: "edu",
|
||
core: ["language:own","language:sign","language:trade_cant","anthropology","insight"],
|
||
support: ["research","knowledge","persuade","listen","status"],
|
||
talents: ["debriefer","read_room","wrong_memory","cover_work"],
|
||
kit: ["dictaphone","notebook","wax_tablet","carbon_book","translator_pin"] },
|
||
|
||
entertainer: { label: "Entertainer",
|
||
blurb: "Clubs, cruise ships and one season on television. Can hold a room that does not want to be held.", stat: "cha",
|
||
core: ["art","persuade","fast_talk","status","insight"],
|
||
support: ["athletics","sleight_of_hand","listen","language:own","dodge"],
|
||
talents: ["cover_work","read_room","debriefer","ambidextrous"],
|
||
kit: ["dictaphone","cover_id","camera","pocket_watch","vacuum_flask"] },
|
||
|
||
criminal: { label: "Criminal",
|
||
blurb: "Nothing organised. Doors, locks and other people's assumptions, and a caution the department has quietly lost.", stat: "dex",
|
||
core: ["stealth","sleight_of_hand","fast_talk","spot","dodge"],
|
||
support: ["brawl","melee_weapon:knife","bargain","insight","athletics"],
|
||
talents: ["cover_work","clean_scene","ambidextrous","read_room"],
|
||
kit: ["lock_rakes","dagger","master_keys","door_wedges","black_mirror"] }
|
||
};
|
||
|
||
/**
|
||
* INDUCTION — the agency half of a trade character.
|
||
*
|
||
* Six weeks at the training centre: how to hold a cordon, how to write it up, which
|
||
* end of the issued sidearm to point, and enough Anomaly Lore to know what you are
|
||
* looking at. It is rated LOW on purpose. A doctor recruited last year is still a
|
||
* far better doctor than they are a Custodian, and that gap is the point of playing
|
||
* one — a posting is a career, this is a course.
|
||
*/
|
||
/** The eight the data model defines, and what each is rolled with. */
|
||
export const CHARACTERISTIC_DICE = {
|
||
str: "3d6", con: "3d6", siz: "2d6+6", int: "2d6+6",
|
||
pow: "3d6", dex: "3d6", cha: "3d6", edu: "3d6+3"
|
||
};
|
||
|
||
/**
|
||
* What a TRADE is worth, independent of Custodian seniority.
|
||
*
|
||
* Trades used to be rated on the agency tier bands, which had it exactly backwards:
|
||
* a probationary burglar came out on 35–50% and a veteran one on 65–80%, as though
|
||
* fifteen years of picking locks were something the department conferred. Prior
|
||
* career is prior career. It does not improve because somebody was promoted, and it
|
||
* does not start low because somebody is new.
|
||
*
|
||
* Agency rank still governs everything that IS the department's doing: the induction
|
||
* band, the talents, the issue and the bonus points.
|
||
*/
|
||
export const TRADE_BANDS = { core: [50, 65], support: [30, 50] };
|
||
|
||
export const INDUCTION = {
|
||
label: "Induction",
|
||
blurb: "Six weeks at the training centre, and a warrant card that is still stiff.",
|
||
// Induction is the department's, so unlike the trade it DOES improve with service.
|
||
// A veteran who came in through a trade has been on the courses for years.
|
||
band: [25, 40],
|
||
bandByTier: {
|
||
probationary: [20, 32], officer: [25, 40], senior: [35, 50], veteran: [45, 60]
|
||
},
|
||
skills: ["tradecraft", "anomaly_lore", "first_aid", "firearm:pistol", "dodge"],
|
||
kit: ["pistol_light", "stab_vest", "cordon_kit", "cover_id", "ward"]
|
||
};
|
||
|
||
export const STANDING_ISSUE = ["anchor", "handset", "warrant_card", "dressings",
|
||
"torch", "chalk", "whistle", "notebook"];
|
||
|
||
/** Seniority is issued more, and is trusted with more. */
|
||
export const TIER_ISSUE = {
|
||
probationary: [],
|
||
officer: ["cover_id"],
|
||
senior: ["cover_id", "seals", "master_keys"],
|
||
veteran: ["cover_id", "seals", "master_keys", "carbon_book", "trauma_unit"]
|
||
};
|
||
|
||
/**
|
||
* One personal effect. Not issued, not logged, and the reason the anchor works —
|
||
* an agent who carries nothing of their own has nothing to be filed against.
|
||
*/
|
||
export const PERSONAL_EFFECTS = ["pocket_watch", "spectacles", "brass_compass",
|
||
"vacuum_flask", "notebook", "camera", "tallow_candle",
|
||
"wax_tablet", "sand_glass", "returning_coin"];
|
||
|
||
export const TIERS = {
|
||
probationary: { label: "Probationary", rank: "Probationary", core: [35, 50], support: [20, 35], talents: 1, bonusPts: 0 },
|
||
officer: { label: "Field Officer", rank: "Field Officer", core: [45, 60], support: [25, 45], talents: 2, bonusPts: 20 },
|
||
senior: { label: "Senior Field Officer", rank: "Senior Field Officer", core: [55, 70], support: [30, 50], talents: 3, bonusPts: 50 },
|
||
veteran: { label: "Case Officer", rank: "Case Officer", core: [65, 80], support: [35, 55], talents: 4, bonusPts: 90 }
|
||
};
|
||
|
||
/** NPC threat tiers — bystanders through anomalies, and the things in between. */
|
||
/**
|
||
* Threat tiers. `attacks` pairs a weapon with the EXACT skill that fires it —
|
||
* they are granted together or not at all. Previously the tier chose the weapon
|
||
* and an unrelated random role chose the skills, so most generated NPCs could not
|
||
* use what they were holding: eight Apexes in ten had no usable attack.
|
||
*/
|
||
export const THREATS = {
|
||
bystander: { label: "Bystander", hpMul: 1.0, armour: 0, skill: [15, 30], talents: 0, attacks: [] },
|
||
trained: { label: "Trained", hpMul: 1.0, armour: 1, skill: [30, 45], talents: 0,
|
||
attacks: [{ weapon: "dagger", skill: "melee_weapon:knife" }] },
|
||
armed: { label: "Armed", hpMul: 1.0, armour: 2, skill: [40, 55], talents: 0,
|
||
attacks: [{ weapon: "pistol_med", skill: "firearm:pistol" }] },
|
||
dangerous: { label: "Dangerous", hpMul: 1.2, armour: 3, skill: [50, 65], talents: 1,
|
||
attacks: [{ weapon: "rifle_med", skill: "firearm:rifle" },
|
||
{ weapon: "baton", skill: "melee_weapon:club" }] },
|
||
anomalous: { label: "Anomalous", hpMul: 1.5, armour: 5, skill: [55, 75], talents: 2,
|
||
attacks: [{ weapon: "grasp", skill: "brawl" }] },
|
||
apex: { label: "Apex", hpMul: 2.0, armour: 7, skill: [65, 85], talents: 3,
|
||
attacks: [{ weapon: "grasp", skill: "brawl" },
|
||
{ weapon: "unmaker", skill: "energy_weapon:rifle" }] }
|
||
};
|
||
|
||
/** Species: characteristic shifts and the location profile they use. */
|
||
export const SPECIES = {
|
||
baseline: { label: "Baseline", profile: "baseline", shift: {} },
|
||
vesh: { label: "Vesh", profile: "vesh", shift: { dex: +2, cha: -2, pow: +1 }, talents: ["unblinking","out_of_step"] },
|
||
cadence: { label: "Cadence", profile: "cadence", shift: { con: +2, str: -2, int: +1 }, talents: ["distributed","quorum"] }
|
||
};
|
||
|
||
/* ---------------- name pools ---------------- */
|
||
const GIVEN = ["Ceyla","Dunn","Rakhi","Tomas","Ambaro","Nils","Halina","Ottoline","Bez","Marek",
|
||
"Sunniva","Idris","Perpetua","Jorge","Nkechi","Vasha","Emeric","Lark","Tobin","Saoirse",
|
||
"Anneke","Ruben","Ilse","Cadogan","Mirren","Fenn","Orla","Kester","Zofia","Rune"];
|
||
const FAMILY = ["Marspont","Halvorsen","Oyelaran-Vost","Ekwueme","Sandoval","Achterberg","Quist",
|
||
"Delacroix-Ng","Bramall","Iwu","Fenwick","Oyelaran","Strand","Adeyemi","Kowalczyk","Rees-Mogg",
|
||
"Vanterpool","Okonkwo","Lindqvist","Brennan","Achebe","Dumont","Sarraceno","Halloran","Vance"];
|
||
const NPC_NAMES = { bystander:["Local","Witness","Neighbour","Publican","Warden","Ferryman"],
|
||
trained:["Constable","Ranger","Fisher","Guard","Surveyor"],
|
||
armed:["Contractor","Cleaner","Sentry","Enforcer"],
|
||
dangerous:["Team lead","Specialist","Hunter","Marksman"],
|
||
anomalous:["Revenant","Echo","Walker","The thing in the water"],
|
||
apex:["Displaced","The one that came back","Apex"] };
|
||
|
||
export const NAME_POOLS = { GIVEN, FAMILY, NPC_NAMES };
|