Files
RingBRP/tools/powers.mjs
T
slaguru666andClaude Opus 5 32ca982369 R-275: the harness had never read a creature's power
41 statblocks carry a POWER in their tactics and simulate.mjs read none of them,
so a redcap that ignores the cumulative defence penalty has been measured as a
creature that tires -- in check-lethality, in check-focus, and in every figure
published about it. The defect was not that the powers were unimplemented, it was
that nothing said they were not.

powers.mjs classifies all 41: 2 wired, 14 notSimulable with a stated reason, 25
not fight rules. check-powers refuses an unclassified POWER and refuses a
notSimulable without a reason -- and it does not test that the harness imports a
power, it fights the creature with and without and requires the two to disagree.

Moved: the courier 9.4% to 1.0% wiped (it attacks at half while carrying), the
redcap 0.7% to 0.9% (small, because these fights rarely spend a second defence).

ARGENT AND GULES was wired and then un-wired: it tripled the supporter's wipe rate
to 75.2% because the harness has no ground and applied the borough-ground condition
unconditionally. Same reason THE PULL is not wired. I had wired one and refused the
other on identical facts.

Lethality and focus re-recorded, bestiary regenerated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 11:05:11 +01:00

95 lines
6.5 KiB
JavaScript

/**
* 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
*/
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" }
};
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;
}