/** * What a creature's POWER actually does in a fight. * * Every statblock in content.mjs carries its tactics as prose, and 41 of them contain a * POWER. R-274 found that none of them reach `simulate.mjs`: the harness reads * characteristics and weapons and has no idea a redcap ignores the thing that makes every * other defender worse. So the redcap has been measured — in check-lethality, in * check-focus, in every figure this project has published about it — as a creature that * tires, which its own entry says it does not. * * This is the classification, not a second copy of the prose. content.mjs remains the * text; this says which of those powers are fight rules, which of the fight rules the * harness can express, and — for the ones it cannot — why, in one line, so the gap is a * recorded decision rather than an absence. * * wired the simulator applies it; `effect` says how * outOfCombat not a fight rule at all — a scene, a social problem, a clock * notSimulable a fight rule the harness cannot express, with the reason * * check-powers refuses any POWER in content.mjs that is not in one of the three, so a * creature added tomorrow cannot quietly arrive unclassified. That is the whole point: * the defect was not that the powers were unimplemented, it was that nothing said so. * * EFFECTS THE SIMULATOR UNDERSTANDS * defenceStacking: "ignores" the cumulative penalty never applies to its defences * damageFactor: n damage it takes is multiplied by n, floored * attackFactor: n its attack rating is multiplied by n, floored */ import { POWERS_FOLKLORE } from "./powers-folklore.mjs"; import { POWERS_WATER } from "./powers-water.mjs"; import { POWERS_ANOMALOUS } from "./powers-anomalous.mjs"; import { POWERS_BEASTS } from "./powers-beasts.mjs"; /* The bestiary's own classifications, one file per band, merged here. * * Kept in separate files for the same reason the creatures are: four of them were * written at once, and a single shared object literal is the one thing concurrent * authors cannot both edit. Spread last so this file's hand-written entries always win * a key collision — the originals were argued over and measured, and a band file must * not silently take one of them over. * * Not one of the 99 was wired. That is the correct outcome rather than a shortfall: * every effect the harness offers describes the CREATURE — its own attack rating, the * damage it takes, whether it feels the defence-stacking penalty — and essentially every * power in the bestiary is conditional on a place, a posture, a round count, a weapon * type, an attacker count, a named agent, or Coherence. R-275 is what wiring one of * those anyway costs: 25.1% to 75.2% on a single creature. */ export const POWERS = { /* ---- wired: the harness applies these ---- */ redcap: { name: "NOT TIRED", kind: "wired", effect: { defenceStacking: "ignores" }, note: "every parry it makes is at full skill" }, the_courier: { name: "THE CONSIGNMENT", kind: "wired", effect: { attackFactor: 0.5 }, note: "it will not fight with the carrying limb, so it attacks at half until the consignment is taken" }, /* ---- notSimulable: fight rules the harness cannot express ---- */ /* Wired first, and reverted on the measurement. Halving everything the supporter takes tripled its wipe rate — 25.1% to 75.2% against the frozen party — because the harness has no ground and so applied "while it stands on ground the borough holds" unconditionally. That is a 50-point swing resting on an assumption the harness cannot state, which is the same reason THE PULL is not wired: a power conditional on a place cannot be simulated by something with no places in it. R-275. */ supporter: { name: "ARGENT AND GULES", kind: "notSimulable", why: "half damage only on ground the borough holds, and the harness has no ground — wiring it " + "unconditionally moved its wipe rate 25.1% to 75.2%, which is the assumption speaking" }, the_choir: { name: "ONE VOICE", kind: "notSimulable", why: "costs Coherence, which the harness does not model" }, hollow_man: { name: "THE FILE IS YOURS NOW", kind: "notSimulable", why: "an opposed POWx5 whose stake is Coherence, which the harness does not model" }, carrion_file: { name: "AMENDMENT", kind: "notSimulable", why: "removes a trained skill permanently; the harness has no character to amend" }, the_understudy: { name: "REHEARSED", kind: "notSimulable", why: "acts before the agent it is copying, and the harness has no notion of which agent that is" }, the_predecessor: { name: "HANDOVER", kind: "notSimulable", why: "takes an agent's action for a task the harness does not model" }, grindylow: { name: "THE PULL", kind: "notSimulable", why: "only in water, and resolves as a grapple into the ASPHYXIA rules" }, nuckelavee: { name: "THE BLIGHT", kind: "notSimulable", why: "a CONx5 each round for 1 point; the harness rolls no characteristics" }, surveyor: { name: "ALREADY SURVEYED", kind: "notSimulable", why: "cover and ambush, neither of which the harness has" }, spriggan: { name: "WHAT IT REALLY IS", kind: "notSimulable", why: "runs at a smaller statblock until the barrow is disturbed, which is a scene state" }, long_walker: { name: "DISTANCE MEANS NOTHING", kind: "notSimulable", why: "damage buys movement rather than injury; the harness has no ground" }, the_stanchion: { name: "DRIVEN", kind: "notSimulable", why: "its armour is already on the statblock; the rest is about a crossing" }, the_margin: { name: "NOT MENTIONED", kind: "notSimulable", why: "a Spot penalty for looking, and the harness never looks" }, the_relay: { name: "WHILE QUORATE", kind: "notSimulable", why: "body count holds a crossing open; nothing about the fight itself" }, /* ---- outOfCombat: not fight rules ---- */ barghest: { name: "THE SUMMONS", kind: "outOfCombat" }, kelpie: { name: "ADHESION", kind: "outOfCombat", why: "its own entry says run ASPHYXIA and not a combat" }, boggart: { name: "THE WRONG NAME", kind: "outOfCombat" }, churchgrim: { name: "THE GROUND IS MINE", kind: "outOfCombat" }, the_tenant: { name: "TENANCY", kind: "outOfCombat" }, precedent: { name: "COUNTERSIGNED", kind: "outOfCombat" }, quarantine_unit: { name: "STERILISATION ORDER", kind: "outOfCombat" }, cuckoo_array: { name: "HOUSE RULES", kind: "outOfCombat" }, auditor: { name: "THE DISCREPANCY", kind: "outOfCombat" }, washer_ford: { name: "THE COLLAR", kind: "outOfCombat" }, lantern_man: { name: "THE SHORTER WAY", kind: "outOfCombat" }, knockers: { name: "THE KNOCK", kind: "outOfCombat" }, hedley_kow: { name: "THE SWAP", kind: "outOfCombat" }, apple_man: { name: "THE RECKONING", kind: "outOfCombat" }, the_committee: { name: "RESOLVED", kind: "outOfCombat" }, night_registry: { name: "CORRECTLY FILED", kind: "outOfCombat" }, the_locum: { name: "OF COURSE", kind: "outOfCombat" }, lost_property: { name: "UNCLAIMED", kind: "outOfCombat" }, the_minutes: { name: "AS AGREED", kind: "outOfCombat" }, waiting_room: { name: "SHORTLY", kind: "outOfCombat" }, switchboard: { name: "PUTTING YOU THROUGH", kind: "outOfCombat" }, the_census: { name: "ENUMERATED", kind: "outOfCombat", why: "its own entry says the threat is not a combat one" }, the_yield: { name: "THE COUNT", kind: "outOfCombat" }, the_overwriter: { name: "THE CORRECTION", kind: "outOfCombat" }, the_arrears: { name: "THE PRINCIPAL", kind: "outOfCombat" }, /* The starter's two. Unclassified until check-powers began reading the shared spec list rather than NPCS — they are scenario cast, so the guard had never seen them. Both happen during a fight and neither can be expressed: the harness has no documents to present and no doors to seal. */ bp_inspector: { name: "NO DESTINATION SHOWN", kind: "notSimulable", why: "it will not strike anyone formally offering a document — a ticket, a warrant card held flat — " + "and the harness has nothing to offer and no notion of a round spent offering it" }, bp_conductor: { name: "ALL PASSENGERS REMAIN ABOARD", kind: "notSimulable", why: "once per fight it seals every door in a carriage, and the harness has no doors, no carriage " + "and nowhere to be shut out of; opening one costs an action it also cannot spend" }, ...POWERS_FOLKLORE, ...POWERS_WATER, ...POWERS_ANOMALOUS, ...POWERS_BEASTS }; export const EFFECT_KINDS = ["defenceStacking", "damageFactor", "attackFactor"]; /** The fight effect for a creature, or null. Only `wired` powers have one. */ export function combatPowerFor(key) { const p = POWERS[key]; return p && p.kind === "wired" ? p.effect : null; }