Files
RingBRP/postings.mjs
T
slaguru666andClaude Opus 5 43508259ac Consolidation pass after a Codex review
Four real defects, each verified against the source before acting on it.

Trade expertise was coupled to agency rank. A probationary burglar came out at 35-50%
and a veteran at 65-80%, as though fifteen years of picking locks were something the
department conferred. TRADE_BANDS is now fixed at 50-65/30-50 at every rank, and
INDUCTION scales with service instead (20-32 probationary to 45-60 veteran) because
that half genuinely is the department's. The two axes were the wrong way round. This
is the thing I flagged myself after PAPER HARBOUR and then left alone — flagging a
defect is not the same as fixing it.

Bonus points were sprayed across every skill in the game. grantFullSkillList runs
immediately before the loop and puts all fifty-nine skills on the sheet, and the loop
picked from actor.items, so a veteran's ninety points landed anywhere. The comment two
hundred lines above has always said "over the core skills"; the code never did.

"Random species" passed "baseline" into generators that support random perfectly well.

No behavioural tests. The strongest point in the review: the three guards are static
and none of them can tell whether a rule is READ, which is the only kind of defect this
project has ever shipped. tools/check-behaviour.mjs now runs 28 deterministic tests,
gated into the build, covering R-231/232/243/245/246, graded defences and the lamp
rule. Proved it bites by re-breaking R-246.

The register moved to postings.mjs, a Foundry-neutral module the engine, rulebook,
guards and tests all import normally — they were previously pulled out of the engine
with a regex and eval, which worked and was a trap. My first version of that used
`export … from`, which re-exports without local bindings, so ROLES was undefined and
the system threw on init: node --check passes that, loading it in Foundry does not.

Also: the contact doc comment still said "only OFFER moves standing"; check-kits
accepted mas/app, which are the DISPLAY names of siz/cha, so a posting declaring one
would have passed and produced an undefined characteristic; package.json disagreed with
the manifest; README counts were three revisions stale and are now generated at build.

Deferred with reasons: splitting the 6,700-line engine into modules. Right, and a
multi-session refactor whose risk is exactly the silent breakage demonstrated above.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 22:57:20 +01:00

408 lines
26 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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","knowledge","anomaly_lore","language:own","computer_use","insight"],
support: ["spot","art","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: ["fast_talk","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","anthropology","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: ["knowledge","art","anomaly_lore","research","status"],
support: ["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","status","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","anthropology","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 };