Implement RUNDOWN v1: rules, ladders, GM sheet, console
- rules/RUNDOWN.md: the complete system, self-contained - reference/obstacle-ladders.md: eight six-rung ladders across foot, ground vehicle and spinner terrain - reference/converting-from-raw.md: switching from the published chase rules mid-campaign or mid-chase, plus rulings the conversion needs - reference/rundown-gm-sheet.html: one-page printable GM sheet - gm-utility/rundown-console.html + modules/rundown.js: touch console matching the REP-DETECT panel style, no dependencies, runs from file:// Console logic verified by gm-utility/test/rundown.test.js — 42 assertions driving the real module through a stub DOM, covering LEAD movement and clamping, round capping, Overtime drift, undo, Collateral pips and every outcome band. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -4,31 +4,60 @@
|
|||||||
|
|
||||||
A complete, dice-only replacement for the published chase subsystem. No card
|
A complete, dice-only replacement for the published chase subsystem. No card
|
||||||
deck, no randomiser tables, no components beyond the dice already on the table.
|
deck, no randomiser tables, no components beyond the dice already on the table.
|
||||||
Every player has a job in every round.
|
Every player has a job in every round, and a chase fits inside fifteen minutes.
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
| | |
|
| | |
|
||||||
|---|---|
|
|---|---|
|
||||||
| **Version** | v1 — design spec only |
|
| **Version** | v1 draft — complete, unplaytested |
|
||||||
| **Playtests** | 0 |
|
| **Playtests** | 0 of 4 (see the spec, §7) |
|
||||||
| **Remaining** | Rules text, GM sheet, obstacle ladders, console module, playtest |
|
| **Console tests** | 42 passing |
|
||||||
|
|
||||||
The approved design lives in
|
**Not table-ready yet.** The rules are complete and the console logic is
|
||||||
[`docs/superpowers/specs/2026-07-23-rundown-cardless-chase-design.md`](docs/superpowers/specs/2026-07-23-rundown-cardless-chase-design.md).
|
verified, but nothing here has faced players. The status line changes when a
|
||||||
|
live chase runs inside the fifteen-minute budget.
|
||||||
|
|
||||||
## What it is
|
## Contents
|
||||||
|
|
||||||
|
| File | What it is |
|
||||||
|
|---|---|
|
||||||
|
| [`rules/RUNDOWN.md`](rules/RUNDOWN.md) | **The system.** Self-contained — a GM with only this file can run a chase. |
|
||||||
|
| [`reference/rundown-gm-sheet.html`](reference/rundown-gm-sheet.html) | One-page printable GM sheet: roles, procedure, outcomes, blank ladder, round log. |
|
||||||
|
| [`reference/obstacle-ladders.md`](reference/obstacle-ladders.md) | Eight ready-to-run ladders — market, rooftops, storm drain, transit, freight yard, arterial, spinner, corporate atrium. |
|
||||||
|
| [`reference/converting-from-raw.md`](reference/converting-from-raw.md) | Switching from the published chase rules, mid-campaign or mid-chase. |
|
||||||
|
| [`gm-utility/rundown-console.html`](gm-utility/rundown-console.html) | Touch console for a tablet: LEAD track, round counter, ladder stepper, Collateral, live outcome. |
|
||||||
|
| [`docs/superpowers/specs/`](docs/superpowers/specs/) | The design spec, with the calibration maths and the playtest plan. |
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
- **LEAD** — one accumulating 0–8 track replaces the published range categories.
|
- **LEAD** — one accumulating 0–8 track replaces the published range categories.
|
||||||
- **Four roles** — Runner, Cutter, Spotter, Pressure. Everyone rolls, every round.
|
0 is *hands on them*, 8 is *gone*.
|
||||||
- **Obstacle Ladder** — six authored obstacles in fixed escalating order, one per
|
- **Four roles** — Runner, Cutter, Spotter, Pressure. Everyone rolls, every
|
||||||
round, replacing the obstacle deck.
|
round, on four different skills.
|
||||||
- **Collateral** — the pursuit's cost to the city, not to the character sheet.
|
- **Obstacle Ladder** — six authored obstacles played in fixed escalating order,
|
||||||
- **Six-round budget** with a bounded Overtime, so a chase fits a convention slot
|
one per round. Rung 6 always forces the ending. This replaces the deck.
|
||||||
by construction.
|
- **Collateral** — the pursuit's cost to the city, spent after the scene. It
|
||||||
|
never touches Health, Resolve, or Humanity.
|
||||||
|
- **Six rounds**, plus a bounded three-round Overtime, so a chase fits a
|
||||||
|
convention slot by construction rather than by GM intervention.
|
||||||
|
|
||||||
One engine covers foot, ground vehicle, and spinner pursuits as swappable
|
One engine covers foot, ground-vehicle and spinner pursuits as swappable terrain
|
||||||
terrain layers, and works identically whether the PCs are hunting or running.
|
layers, and runs identically whether the PCs are hunting or running.
|
||||||
|
|
||||||
|
**The trade this system makes:** the published rules hide your maneuver behind a
|
||||||
|
card, which is the only thing the deck is really for. RUNDOWN reveals the
|
||||||
|
obstacle *before* anyone declares. You lose the bluff; you gain a table that all
|
||||||
|
rolls and a round that takes seconds instead of a minute.
|
||||||
|
|
||||||
|
## Running the console
|
||||||
|
|
||||||
|
Open `gm-utility/rundown-console.html` in any browser — no server, no build, no
|
||||||
|
dependencies. State persists across a refresh. To verify the chase logic:
|
||||||
|
|
||||||
|
```
|
||||||
|
node gm-utility/test/rundown.test.js
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,378 @@
|
|||||||
|
/* RUNDOWN — chase console
|
||||||
|
Cardless chase rules for the Blade Runner RPG.
|
||||||
|
Self-contained, no dependencies, works from file:// v1 */
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/* ---------------- ladders ---------------- */
|
||||||
|
|
||||||
|
var LADDERS = [
|
||||||
|
{
|
||||||
|
name: 'The Night Market', terrain: 'foot', start: 4, coll: 'standard',
|
||||||
|
rungs: [
|
||||||
|
{ t: 'Noodle stalls', d: 'A wall of stools and elbows.', toll: 'Mobility, or lose your roll', tilt: 'Prey +1', c: 1 },
|
||||||
|
{ t: 'Steam-cart collision', d: 'Scalding white-out, someone screaming about their cart.', toll: 'Mobility, or lose your roll', tilt: '—', c: 1 },
|
||||||
|
{ t: 'Monk procession', d: 'Slow, chanting, impossible to shove through.', toll: 'Manipulation, or your role fails', tilt: 'Prey +1', c: 1 },
|
||||||
|
{ t: 'Holo-ad burst', d: 'A four-storey geisha blooms across the alley.', toll: 'Observation, or lose your roll', tilt: '—', c: 0 },
|
||||||
|
{ t: 'Plastic curtain maze', d: 'Hanging sheeting, animoid tanks, forty ways out.', toll: 'Mobility, or lose your roll', tilt: 'Prey +1 · Spotter at disadvantage', c: 0 },
|
||||||
|
{ t: 'THE SHRINE ALLEY', d: 'A dead end behind a paper shrine. Three metres of wall, no door, an old woman watching.', toll: 'None — nowhere left to go', tilt: 'Pursuers +1', c: 0 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Sector 12 Rooftops', terrain: 'foot', start: 3, coll: 'standard',
|
||||||
|
rungs: [
|
||||||
|
{ t: 'Gravel and standing water', d: 'No grip anywhere.', toll: 'Mobility, or lose your roll', tilt: '—', c: 0 },
|
||||||
|
{ t: 'Aerial forest', d: 'Dishes, guy-lines, a decade of illegal cabling.', toll: 'Mobility, or lose your roll', tilt: 'Prey +1', c: 0 },
|
||||||
|
{ t: 'The service gap', d: 'Four metres between blocks, forty storeys down.', toll: 'Mobility — failure counts as a failed Gambit: out, and hurt', tilt: 'Pursuers +1', c: 0 },
|
||||||
|
{ t: 'Downdraft', d: 'A spinner passes low and the roof becomes a wind tunnel.', toll: 'Force, or lose your roll', tilt: '—', c: 0 },
|
||||||
|
{ t: 'Skylight run', d: 'Old glass over an occupied floor.', toll: 'Mobility, or you go through it', tilt: 'Prey +1', c: 1 },
|
||||||
|
{ t: 'THE PARAPET', d: 'The block ends. Below is the arterial, moving fast.', toll: 'None', tilt: 'Pursuers +1', c: 0 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'The Storm Drain', terrain: 'foot', start: 4, coll: 'standard',
|
||||||
|
rungs: [
|
||||||
|
{ t: 'The grade', d: 'A long wet slope, no handholds.', toll: 'Mobility, or lose your roll', tilt: '—', c: 0 },
|
||||||
|
{ t: 'Dark', d: 'The lighting failed years ago.', toll: 'Observation, or disadvantage next round', tilt: 'Prey +1', c: 0 },
|
||||||
|
{ t: 'Sluice discharge', d: 'A side pipe dumps hard across the channel.', toll: 'Force, or lose your roll', tilt: '—', c: 0 },
|
||||||
|
{ t: 'The junction', d: 'Four identical mouths, one right answer.', toll: 'Observation, or your role fails', tilt: 'Prey +1 · Spotter shines', c: 0 },
|
||||||
|
{ t: 'Rising water', d: 'Knee-deep and climbing, the sound enormous.', toll: 'Stamina or Force, or lose your roll', tilt: 'Pursuers +1', c: 0 },
|
||||||
|
{ t: 'THE OUTFALL GRATE', d: 'The channel ends at a rusted grate and open air beyond.', toll: 'None', tilt: 'Pursuers +1', c: 0 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Transit Interchange', terrain: 'foot', start: 4, coll: 'standard',
|
||||||
|
rungs: [
|
||||||
|
{ t: 'The concourse', d: 'A river of commuters going the other way.', toll: 'Mobility, or lose your roll', tilt: 'Prey +1', c: 1 },
|
||||||
|
{ t: 'Turnstile bank', d: 'Everyone stops. Nobody stops for you.', toll: 'Connections or Tech, or lose your roll', tilt: '—', c: 1 },
|
||||||
|
{ t: 'Down escalator, wrong way', d: 'Against the grain, in front of everyone.', toll: 'Mobility, or lose your roll', tilt: 'Prey +1', c: 1 },
|
||||||
|
{ t: 'Transit police', d: 'Two officers who have noticed.', toll: 'Manipulation, or your role fails and you take 1 Collateral', tilt: '—', c: 1 },
|
||||||
|
{ t: 'The platform', d: 'A train arriving, doors about to close.', toll: 'Observation, or lose your roll', tilt: 'Prey +1', c: 1 },
|
||||||
|
{ t: 'THE CLOSING DOORS', d: 'One carriage, both of you — or one of you.', toll: 'None', tilt: '—', c: 1 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'The Freight Yard', terrain: 'ground vehicle', start: 4, coll: 'doubled',
|
||||||
|
rungs: [
|
||||||
|
{ t: 'Standing water and rail', d: 'The surface will not hold a hard turn.', toll: 'Driving, or lose your roll', tilt: '—', c: 0 },
|
||||||
|
{ t: 'Container canyon', d: 'Blind corners, no sightlines.', toll: 'Observation, or disadvantage next round', tilt: 'Prey +1', c: 0 },
|
||||||
|
{ t: 'Gantry crane', d: 'A load swinging across the lane.', toll: 'Driving, or lose your roll', tilt: '—', c: 0 },
|
||||||
|
{ t: 'Yard hauler', d: 'Forty metres of articulated freight, reversing.', toll: 'Driving, or your role fails', tilt: 'Prey +1', c: 0 },
|
||||||
|
{ t: 'Fuel apron', d: 'Hard surface, wide open, and a very good reason not to shoot.', toll: 'Driving · Firearms Pressure here is double Collateral', tilt: 'Pursuers +1', c: 1 },
|
||||||
|
{ t: 'THE GATE', d: 'Closed, manned, and lowering.', toll: 'None — through it is 3 Collateral', tilt: 'Pursuers +1', c: 1 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Sepulveda Arterial, rain', terrain: 'ground vehicle', start: 4, coll: 'doubled',
|
||||||
|
rungs: [
|
||||||
|
{ t: 'Spray', d: 'The car ahead throws a wall of it.', toll: 'Observation, or lose your roll', tilt: '—', c: 0 },
|
||||||
|
{ t: 'Lane discipline', d: 'The automated flow does not want you doing this.', toll: 'Driving or Tech, or lose your roll', tilt: 'Prey +1', c: 1 },
|
||||||
|
{ t: 'Underpass', d: 'Dark, loud, three lanes narrowing to two.', toll: 'Driving, or lose your roll', tilt: '—', c: 1 },
|
||||||
|
{ t: 'Stalled vehicle', d: 'Hazards on, occupants inside.', toll: 'Driving, or lose your roll and take 1 Collateral', tilt: 'Pursuers +1', c: 1 },
|
||||||
|
{ t: 'Interchange ramp', d: 'Four ways off, decided in a second and a half.', toll: 'Observation, or your role fails', tilt: 'Prey +1 · Spotter decisive', c: 0 },
|
||||||
|
{ t: 'THE CHECKPOINT', d: 'LAPD barrier across all lanes, lights on.', toll: 'None', tilt: 'Pursuers +1', c: 1 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Over the Wall', terrain: 'spinner', start: 5, coll: 'halved',
|
||||||
|
rungs: [
|
||||||
|
{ t: 'Traffic lane', d: 'You are both in a corridor you are not cleared for.', toll: 'Driving or Connections, or lose your roll', tilt: '—', c: 0 },
|
||||||
|
{ t: 'Rain shear', d: 'The weather between the towers is its own animal.', toll: 'Driving, or lose your roll', tilt: '—', c: 0 },
|
||||||
|
{ t: 'Ad blimp', d: 'Three hundred metres of illuminated hull, directly across.', toll: 'Driving, or your role fails', tilt: 'Prey +1', c: 0 },
|
||||||
|
{ t: 'Transponder ping', d: 'Traffic control wants to know who you are.', toll: 'Tech or Connections, or disadvantage next round', tilt: 'Pursuers +1', c: 0 },
|
||||||
|
{ t: 'The span', d: 'A bridge deck with clearance nobody sane would take.', toll: 'Driving — taking it is a Gambit; refusing costs your roll', tilt: 'Prey +1', c: 0 },
|
||||||
|
{ t: 'THE WALL', d: 'The sector boundary, and a hard deck beyond it that is not your jurisdiction.', toll: 'None', tilt: '—', c: 0 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'The Atrium', terrain: 'foot · corporate', start: 3, coll: 'standard',
|
||||||
|
rungs: [
|
||||||
|
{ t: 'The floor', d: 'Wet-polished stone, no traction at speed.', toll: 'Mobility, or lose your roll', tilt: '—', c: 1 },
|
||||||
|
{ t: 'Reception cordon', d: 'A desk, a rope line, and a very calm young man.', toll: 'Manipulation, or your role fails', tilt: 'Prey +1', c: 1 },
|
||||||
|
{ t: 'Elevator bank', d: 'Twelve doors, one of them closing.', toll: 'Observation, or lose your roll', tilt: 'Prey +1', c: 1 },
|
||||||
|
{ t: 'Corporate security', d: 'Two of them. Polite, armed, and not police.', toll: 'Manipulation or Connections, or your role fails and you take 1 Collateral', tilt: '—', c: 1 },
|
||||||
|
{ t: 'The gallery', d: 'A mezzanine over the whole atrium, in full view of everyone.', toll: 'Mobility, or lose your roll', tilt: 'Pursuers +1', c: 1 },
|
||||||
|
{ t: 'THE SEALED FLOOR', d: 'The building has decided. Every door on this level locks at once.', toll: 'None', tilt: 'Pursuers +1', c: 1 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
var OUTCOMES = [
|
||||||
|
{ lead: [0, 0], nm: 'ENGAGED', cls: 'live', d: 'Hands on them. Combat, restraint, or a conversation at contact range.' },
|
||||||
|
{ lead: [1, 2], nm: 'CORNERED', cls: 'live', d: 'Trapped, not held. A standoff, a negotiation, or a jump nobody should survive.' },
|
||||||
|
{ lead: [3, 4], nm: 'LOST, BUT', cls: 'done', d: 'Gone from sight — and you have a direction, a dropped thing, a plate, a witness.' },
|
||||||
|
{ lead: [5, 7], nm: 'GONE', cls: 'bad', d: 'Clean. The street still remembers, and so do you.' },
|
||||||
|
{ lead: [8, 8], nm: 'GHOST', cls: 'bad', d: 'You never had them. They were better than you, or they were never alone.' }
|
||||||
|
];
|
||||||
|
|
||||||
|
var COLL_NOTES = [
|
||||||
|
'Nothing yet. The city has not noticed.',
|
||||||
|
'Noise. Someone remembers your face.',
|
||||||
|
'Noise. Someone remembers your face — and will talk to the wrong person.',
|
||||||
|
'A report gets filed. LAPD attention, or a supervisor’s question.',
|
||||||
|
'A report gets filed, and the case just got harder.',
|
||||||
|
'A body, or a burned asset. Something you needed is gone.',
|
||||||
|
'A body, or a burned asset. Someone above you is now personally involved.'
|
||||||
|
];
|
||||||
|
|
||||||
|
var MAX_ROUNDS = 6, MAX_OT = 3, KEY = 'rundown.v1';
|
||||||
|
|
||||||
|
/* ---------------- state ---------------- */
|
||||||
|
|
||||||
|
var S, history = [];
|
||||||
|
|
||||||
|
function fresh(li) {
|
||||||
|
var idx = (typeof li === 'number') ? li : 0;
|
||||||
|
return {
|
||||||
|
ladder: idx,
|
||||||
|
lead: LADDERS[idx].start,
|
||||||
|
round: 1,
|
||||||
|
coll: 0,
|
||||||
|
pursuer: 0,
|
||||||
|
prey: 0,
|
||||||
|
overtime: false,
|
||||||
|
ended: null,
|
||||||
|
peeked: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function load() {
|
||||||
|
try {
|
||||||
|
var raw = localStorage.getItem(KEY);
|
||||||
|
if (raw) {
|
||||||
|
var p = JSON.parse(raw);
|
||||||
|
if (p && typeof p.lead === 'number' && LADDERS[p.ladder]) return p;
|
||||||
|
}
|
||||||
|
} catch (e) { /* storage unavailable — run in memory */ }
|
||||||
|
return fresh(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
try { localStorage.setItem(KEY, JSON.stringify(S)); } catch (e) { /* ignore */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
function snapshot() {
|
||||||
|
history.push(JSON.stringify(S));
|
||||||
|
if (history.length > 30) history.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
function clamp(v, lo, hi) { return v < lo ? lo : (v > hi ? hi : v); }
|
||||||
|
|
||||||
|
function $(id) { return document.getElementById(id); }
|
||||||
|
|
||||||
|
/* ---------------- render ---------------- */
|
||||||
|
|
||||||
|
function currentRungIndex() {
|
||||||
|
// In overtime the Terminator stands; otherwise rung follows the round.
|
||||||
|
return clamp(S.round, 1, MAX_ROUNDS) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function outcomeFor(lead) {
|
||||||
|
for (var i = 0; i < OUTCOMES.length; i++) {
|
||||||
|
if (lead >= OUTCOMES[i].lead[0] && lead <= OUTCOMES[i].lead[1]) return OUTCOMES[i];
|
||||||
|
}
|
||||||
|
return OUTCOMES[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderLead() {
|
||||||
|
var track = $('leadTrack');
|
||||||
|
track.innerHTML = '';
|
||||||
|
for (var i = 0; i <= 8; i++) {
|
||||||
|
var d = document.createElement('div');
|
||||||
|
d.className = 'n' + (i === S.lead ? ' cur' : '') + (i === 0 || i === 8 ? ' term' : '');
|
||||||
|
if (i === S.lead && i === 0) d.className += ' zero';
|
||||||
|
if (i === S.lead && i === 8) d.className += ' eight';
|
||||||
|
d.innerHTML = i + (i === 0 ? '<small>ENGAGED</small>' : (i === 8 ? '<small>GHOST</small>' : ''));
|
||||||
|
(function (v) {
|
||||||
|
d.addEventListener('click', function () { snapshot(); S.lead = v; S.ended = null; commit(); });
|
||||||
|
})(i);
|
||||||
|
track.appendChild(d);
|
||||||
|
}
|
||||||
|
var L = LADDERS[S.ladder];
|
||||||
|
$('leadSub').textContent = L.terrain + ' · start ' + L.start;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderRound() {
|
||||||
|
var v = $('roundVal');
|
||||||
|
if (S.overtime) {
|
||||||
|
var ot = S.round - MAX_ROUNDS;
|
||||||
|
v.innerHTML = 'OT' + ot + '<span style="font-size:15px;color:var(--dim)">/' + MAX_OT + '</span>';
|
||||||
|
v.className = 'val over';
|
||||||
|
} else {
|
||||||
|
v.innerHTML = S.round + '<span style="font-size:15px;color:var(--dim)">/' + MAX_ROUNDS + '</span>';
|
||||||
|
v.className = 'val';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function rungHTML(r, n, isTerm) {
|
||||||
|
return '<div class="obstacle' + (isTerm ? ' term' : '') + '">' +
|
||||||
|
'<div class="n">Rung ' + n + ' of 6' + (isTerm ? ' — Terminator' : '') + '</div>' +
|
||||||
|
'<div class="t">' + r.t + (r.c ? '<span class="crowd">CROWD</span>' : '') + '</div>' +
|
||||||
|
'<div class="d">' + r.d + '<br>' +
|
||||||
|
'<b>Toll:</b> ' + r.toll + ' · <span class="tilt"><b>Tilt:</b> ' + r.tilt + '</span>' +
|
||||||
|
(r.c ? '<br><span class="tilt">Failed Toll here costs 1 Collateral.</span>' : '') +
|
||||||
|
'</div></div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderObstacle() {
|
||||||
|
var L = LADDERS[S.ladder];
|
||||||
|
var i = currentRungIndex();
|
||||||
|
var isTerm = (i === 5);
|
||||||
|
var html = rungHTML(L.rungs[i], i + 1, isTerm);
|
||||||
|
if (S.overtime) {
|
||||||
|
html += '<div class="muted" style="margin-bottom:10px">Overtime — the Terminator stands. No new rungs. LEAD −1 at the start of each round. Collateral doubled.</div>';
|
||||||
|
}
|
||||||
|
$('obstacleBox').innerHTML = html;
|
||||||
|
$('ladderName').textContent = L.name;
|
||||||
|
$('peekBtn').disabled = (i >= 5 || S.overtime);
|
||||||
|
$('peekOut').innerHTML = '';
|
||||||
|
if (S.peeked && i < 5) {
|
||||||
|
var nx = L.rungs[i + 1];
|
||||||
|
$('peekOut').innerHTML = '<b style="color:var(--teal)">Next — rung ' + (i + 2) + ':</b> ' +
|
||||||
|
nx.t + '. <i>' + nx.toll + '</i>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderNet() {
|
||||||
|
$('pursuerVal').textContent = S.pursuer;
|
||||||
|
$('preyVal').textContent = S.prey;
|
||||||
|
var net = S.pursuer - S.prey;
|
||||||
|
var el = $('netVal');
|
||||||
|
el.textContent = (net > 0 ? '+' : '') + net;
|
||||||
|
el.className = 'v ' + (net > 0 ? 'pos' : (net < 0 ? 'neg' : 'zero'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderColl() {
|
||||||
|
var box = $('pips');
|
||||||
|
box.innerHTML = '';
|
||||||
|
for (var i = 1; i <= 6; i++) {
|
||||||
|
var d = document.createElement('div');
|
||||||
|
d.className = (i <= S.coll ? 'on' : '') + (i <= S.coll && i >= 5 ? ' hot' : '');
|
||||||
|
d.textContent = i;
|
||||||
|
(function (v) {
|
||||||
|
d.addEventListener('click', function () {
|
||||||
|
snapshot();
|
||||||
|
S.coll = (S.coll === v) ? v - 1 : v; // tap the current pip to step back
|
||||||
|
commit();
|
||||||
|
});
|
||||||
|
})(i);
|
||||||
|
box.appendChild(d);
|
||||||
|
}
|
||||||
|
$('collNote').textContent = COLL_NOTES[clamp(S.coll, 0, 6)];
|
||||||
|
$('collSub').textContent = S.overtime ? 'doubled in overtime' : 'the city pays';
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderOutcome() {
|
||||||
|
var o = outcomeFor(S.lead);
|
||||||
|
var el = $('outcome');
|
||||||
|
el.className = 'outcome ' + o.cls;
|
||||||
|
var head = S.ended ? 'CHASE OVER — ' : '';
|
||||||
|
el.innerHTML = '<div class="nm">' + head + o.nm + '</div><div class="dsc">' + o.d + '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderButtons() {
|
||||||
|
var atEnd = (S.round >= MAX_ROUNDS && !S.overtime);
|
||||||
|
$('overtimeBtn').disabled = !(atEnd && !S.ended);
|
||||||
|
$('overtimeBtn').textContent = S.overtime ? 'Overtime running' : 'Call Overtime';
|
||||||
|
$('undoBtn').disabled = history.length === 0;
|
||||||
|
$('applyBtn').disabled = !!S.ended;
|
||||||
|
$('applyBtn').textContent = S.ended ? 'Chase over' : 'Apply & next round';
|
||||||
|
}
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
renderLead(); renderRound(); renderObstacle();
|
||||||
|
renderNet(); renderColl(); renderOutcome(); renderButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
function commit() { save(); render(); }
|
||||||
|
|
||||||
|
/* ---------------- actions ---------------- */
|
||||||
|
|
||||||
|
function applyRound() {
|
||||||
|
if (S.ended) return;
|
||||||
|
snapshot();
|
||||||
|
|
||||||
|
S.lead = clamp(S.lead - (S.pursuer - S.prey), 0, 8);
|
||||||
|
S.pursuer = 0; S.prey = 0; S.peeked = false;
|
||||||
|
|
||||||
|
if (S.lead === 0 || S.lead === 8) { S.ended = true; commit(); return; }
|
||||||
|
|
||||||
|
var lastRound = S.overtime ? (MAX_ROUNDS + MAX_OT) : MAX_ROUNDS;
|
||||||
|
if (S.round >= lastRound) { S.ended = true; commit(); return; }
|
||||||
|
|
||||||
|
S.round += 1;
|
||||||
|
if (S.overtime) {
|
||||||
|
// Overtime drift: LEAD closes by 1 at the start of each overtime round.
|
||||||
|
S.lead = clamp(S.lead - 1, 0, 8);
|
||||||
|
if (S.lead === 0) S.ended = true;
|
||||||
|
}
|
||||||
|
commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
function undo() {
|
||||||
|
if (!history.length) return;
|
||||||
|
S = JSON.parse(history.pop());
|
||||||
|
commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
function callOvertime() {
|
||||||
|
snapshot();
|
||||||
|
S.overtime = true;
|
||||||
|
S.ended = null;
|
||||||
|
S.round = MAX_ROUNDS + 1;
|
||||||
|
S.lead = clamp(S.lead - 1, 0, 8); // first overtime round drifts immediately
|
||||||
|
if (S.lead === 0) S.ended = true;
|
||||||
|
commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
snapshot();
|
||||||
|
S = fresh(S.ladder);
|
||||||
|
commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------- wire up ---------------- */
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
S = load();
|
||||||
|
|
||||||
|
var pick = $('ladderPick');
|
||||||
|
LADDERS.forEach(function (l, i) {
|
||||||
|
var o = document.createElement('option');
|
||||||
|
o.value = i;
|
||||||
|
o.textContent = l.name + ' — ' + l.terrain;
|
||||||
|
pick.appendChild(o);
|
||||||
|
});
|
||||||
|
pick.value = S.ladder;
|
||||||
|
pick.addEventListener('change', function () {
|
||||||
|
snapshot();
|
||||||
|
S = fresh(parseInt(pick.value, 10));
|
||||||
|
commit();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll('[data-adj]').forEach(function (b) {
|
||||||
|
b.addEventListener('click', function () {
|
||||||
|
var p = b.getAttribute('data-adj').split(',');
|
||||||
|
S[p[0]] = clamp(S[p[0]] + parseInt(p[1], 10), 0, 12);
|
||||||
|
renderNet();
|
||||||
|
save();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('applyBtn').addEventListener('click', applyRound);
|
||||||
|
$('undoBtn').addEventListener('click', undo);
|
||||||
|
$('overtimeBtn').addEventListener('click', callOvertime);
|
||||||
|
$('peekBtn').addEventListener('click', function () { S.peeked = true; commit(); });
|
||||||
|
$('resetBtn').addEventListener('click', function () {
|
||||||
|
if (window.confirm('Reset the chase?')) reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', init);
|
||||||
|
} else {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -0,0 +1,229 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
||||||
|
<title>RUNDOWN — Chase Console</title>
|
||||||
|
<style>
|
||||||
|
:root{
|
||||||
|
--bg:#07090b; --panel:#0e1216; --panel2:#131a20; --line:#1f2b33;
|
||||||
|
--teal:#5bd8c4; --teal-dim:#2a6e64; --amber:#e8a33d; --red:#d05353;
|
||||||
|
--ink:#cfd8dc; --dim:#748088;
|
||||||
|
--tap:54px;
|
||||||
|
}
|
||||||
|
*{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent}
|
||||||
|
html,body{height:100%}
|
||||||
|
body{
|
||||||
|
background:
|
||||||
|
radial-gradient(ellipse at 20% 0%, #14202a 0%, transparent 55%),
|
||||||
|
radial-gradient(ellipse at 85% 100%, #1a1410 0%, transparent 50%),
|
||||||
|
var(--bg);
|
||||||
|
background-attachment:fixed;
|
||||||
|
color:var(--ink);font-family:"Avenir Next","Segoe UI",Arial,sans-serif;
|
||||||
|
overscroll-behavior:none;user-select:none;
|
||||||
|
}
|
||||||
|
#app{display:flex;flex-direction:column;height:100dvh}
|
||||||
|
|
||||||
|
/* header */
|
||||||
|
header{display:flex;align-items:center;gap:12px;padding:10px 14px;border-bottom:1px solid var(--line);background:rgba(10,14,17,.75);backdrop-filter:blur(4px)}
|
||||||
|
header .logo{font-size:11px;letter-spacing:.34em;color:var(--teal);text-transform:uppercase;line-height:1.3}
|
||||||
|
header .logo b{display:block;color:var(--ink);font-size:15px;letter-spacing:.22em}
|
||||||
|
header .spacer{flex:1}
|
||||||
|
.roundbox{text-align:right;line-height:1.25}
|
||||||
|
.roundbox .lbl{font-size:9px;letter-spacing:.24em;color:var(--dim);text-transform:uppercase}
|
||||||
|
.roundbox .val{font-family:"Courier New",monospace;font-size:26px;color:var(--amber);font-weight:700}
|
||||||
|
.roundbox .val.over{color:var(--red)}
|
||||||
|
a.home{color:var(--dim);text-decoration:none;font-size:20px;padding:0 6px}
|
||||||
|
|
||||||
|
main{flex:1;overflow-y:auto;padding:14px;padding-bottom:20px}
|
||||||
|
.card{background:rgba(14,18,22,.92);border:1px solid var(--line);border-radius:8px;padding:14px;margin-bottom:14px}
|
||||||
|
.card h2{font-size:11px;letter-spacing:.3em;text-transform:uppercase;color:var(--teal);margin-bottom:12px;display:flex;justify-content:space-between;align-items:center;gap:8px}
|
||||||
|
.card h2 .sub{color:var(--dim);letter-spacing:.06em;text-transform:none;font-size:11px;font-weight:400}
|
||||||
|
|
||||||
|
button.big{
|
||||||
|
background:var(--panel2);border:1px solid var(--line);color:var(--ink);border-radius:8px;
|
||||||
|
min-height:var(--tap);padding:10px 16px;font-size:15px;cursor:pointer;font-family:inherit;letter-spacing:.04em;
|
||||||
|
}
|
||||||
|
button.big:active{background:#1a252e}
|
||||||
|
button.primary{background:#123a35;border-color:var(--teal-dim);color:var(--teal);font-weight:700}
|
||||||
|
button.danger{background:#3a1616;border-color:#6e2a2a;color:var(--red)}
|
||||||
|
button.amber{background:#3a2c12;border-color:#6e5a2a;color:var(--amber)}
|
||||||
|
button:disabled{opacity:.3;pointer-events:none}
|
||||||
|
.row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}
|
||||||
|
.grow{flex:1}
|
||||||
|
.muted{color:var(--dim);font-size:12px;line-height:1.5}
|
||||||
|
|
||||||
|
/* LEAD track */
|
||||||
|
.lead{display:flex;gap:5px;margin-bottom:10px}
|
||||||
|
.lead .n{
|
||||||
|
flex:1;text-align:center;padding:14px 2px 10px;border:1px solid var(--line);border-radius:8px;
|
||||||
|
background:#0a0f13;font-family:"Courier New",monospace;font-size:20px;font-weight:800;color:var(--dim);cursor:pointer;
|
||||||
|
}
|
||||||
|
.lead .n small{display:block;font-family:inherit;font-size:8px;letter-spacing:.12em;font-weight:600;margin-top:3px;color:var(--dim)}
|
||||||
|
.lead .n.cur{background:#123a35;border-color:var(--teal);color:var(--teal);box-shadow:0 0 16px rgba(91,216,196,.22)}
|
||||||
|
.lead .n.term{border-color:#33424c}
|
||||||
|
.lead .n.cur.zero{background:#123a35;border-color:var(--teal)}
|
||||||
|
.lead .n.cur.eight{background:#3a1616;border-color:var(--red);color:var(--red);box-shadow:0 0 16px rgba(208,83,83,.25)}
|
||||||
|
.leadends{display:flex;justify-content:space-between;font-size:9px;letter-spacing:.16em;text-transform:uppercase;color:var(--dim);margin-bottom:12px}
|
||||||
|
|
||||||
|
/* net entry */
|
||||||
|
.net{display:grid;grid-template-columns:1fr auto 1fr;gap:10px;align-items:end}
|
||||||
|
.stepper{background:var(--panel2);border:1px solid var(--line);border-radius:8px;padding:8px}
|
||||||
|
.stepper .lbl{font-size:9px;letter-spacing:.2em;color:var(--dim);text-transform:uppercase;text-align:center;margin-bottom:6px}
|
||||||
|
.stepper .ctrl{display:flex;align-items:center;gap:6px}
|
||||||
|
.stepper .ctrl button{flex:0 0 44px;min-height:44px;font-size:20px;padding:0}
|
||||||
|
.stepper .ctrl .v{flex:1;text-align:center;font-family:"Courier New",monospace;font-size:28px;font-weight:800;color:var(--ink)}
|
||||||
|
.netout{text-align:center;padding-bottom:14px}
|
||||||
|
.netout .lbl{font-size:9px;letter-spacing:.2em;color:var(--dim);text-transform:uppercase}
|
||||||
|
.netout .v{font-family:"Courier New",monospace;font-size:30px;font-weight:800}
|
||||||
|
.netout .v.pos{color:var(--teal)} .netout .v.neg{color:var(--red)} .netout .v.zero{color:var(--dim)}
|
||||||
|
|
||||||
|
/* obstacle */
|
||||||
|
.obstacle{background:#101820;border:1px solid var(--amber);border-radius:10px;padding:14px;margin-bottom:10px}
|
||||||
|
.obstacle .n{color:var(--amber);font-size:11px;letter-spacing:.24em;margin-bottom:7px;text-transform:uppercase}
|
||||||
|
.obstacle .t{font-size:18px;font-weight:700;line-height:1.35;margin-bottom:8px;user-select:text}
|
||||||
|
.obstacle .d{font-size:13px;line-height:1.6;color:var(--ink);user-select:text}
|
||||||
|
.obstacle .d b{color:var(--teal)}
|
||||||
|
.obstacle .d .tilt{color:var(--amber)}
|
||||||
|
.obstacle.term{border-color:var(--red)}
|
||||||
|
.obstacle.term .n{color:var(--red)}
|
||||||
|
.crowd{display:inline-block;font-size:9px;letter-spacing:.16em;border:1px solid var(--red);color:var(--red);padding:1px 6px;border-radius:3px;margin-left:8px;vertical-align:middle}
|
||||||
|
|
||||||
|
/* collateral */
|
||||||
|
.pips{display:flex;gap:8px;margin-bottom:10px}
|
||||||
|
.pips div{
|
||||||
|
flex:1;height:46px;border:2px solid var(--line);border-radius:8px;background:#0a0f13;cursor:pointer;
|
||||||
|
display:flex;align-items:center;justify-content:center;font-family:"Courier New",monospace;font-size:15px;color:var(--dim);font-weight:700;
|
||||||
|
}
|
||||||
|
.pips div.on{background:#3a2c12;border-color:var(--amber);color:var(--amber)}
|
||||||
|
.pips div.on.hot{background:#3a1616;border-color:var(--red);color:var(--red)}
|
||||||
|
.collnote{font-size:13px;line-height:1.55;color:var(--ink);border-left:2px solid var(--amber);padding-left:10px}
|
||||||
|
|
||||||
|
/* outcome */
|
||||||
|
.outcome{border-radius:10px;padding:14px;border:1px solid var(--line);background:#0a0f13}
|
||||||
|
.outcome .nm{font-size:22px;font-weight:800;letter-spacing:.1em;margin-bottom:6px}
|
||||||
|
.outcome .dsc{font-size:13px;line-height:1.55;color:var(--dim)}
|
||||||
|
.outcome.live{border-color:var(--teal)} .outcome.live .nm{color:var(--teal)}
|
||||||
|
.outcome.done{border-color:var(--amber)} .outcome.done .nm{color:var(--amber)}
|
||||||
|
.outcome.bad{border-color:var(--red)} .outcome.bad .nm{color:var(--red)}
|
||||||
|
|
||||||
|
/* roles table */
|
||||||
|
table.roles{width:100%;border-collapse:collapse;font-size:13px}
|
||||||
|
table.roles td{padding:7px 6px;border-top:1px solid var(--line);vertical-align:top;line-height:1.45}
|
||||||
|
table.roles tr:first-child td{border-top:none}
|
||||||
|
table.roles td:first-child{color:var(--teal);font-weight:700;white-space:nowrap;letter-spacing:.04em}
|
||||||
|
table.roles td:nth-child(2){color:var(--dim);white-space:nowrap;font-size:11px}
|
||||||
|
table.roles .cost{color:var(--amber)}
|
||||||
|
|
||||||
|
select{
|
||||||
|
background:#0a0f13;border:1px solid var(--line);color:var(--ink);border-radius:8px;
|
||||||
|
padding:0 10px;min-height:var(--tap);font-size:15px;font-family:inherit;flex:1;
|
||||||
|
}
|
||||||
|
details summary{cursor:pointer;list-style:none;color:var(--dim);font-size:11px;letter-spacing:.2em;text-transform:uppercase;padding:4px 0}
|
||||||
|
details summary::-webkit-details-marker{display:none}
|
||||||
|
details[open] summary{color:var(--teal)}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app">
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<a class="home" href="index.html" title="Hub">⌂</a>
|
||||||
|
<div class="logo">Chase Console<b>RUNDOWN</b></div>
|
||||||
|
<div class="spacer"></div>
|
||||||
|
<div class="roundbox">
|
||||||
|
<div class="lbl">Round</div>
|
||||||
|
<div class="val" id="roundVal">1<span style="font-size:15px;color:var(--dim)">/6</span></div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h2>Lead <span class="sub" id="leadSub">standard start</span></h2>
|
||||||
|
<div class="lead" id="leadTrack"></div>
|
||||||
|
<div class="leadends"><span>◀ pursuers gain</span><span>prey gains ▶</span></div>
|
||||||
|
|
||||||
|
<div class="net">
|
||||||
|
<div class="stepper">
|
||||||
|
<div class="lbl">Pursuers</div>
|
||||||
|
<div class="ctrl">
|
||||||
|
<button class="big" data-adj="pursuer,-1">−</button>
|
||||||
|
<div class="v" id="pursuerVal">0</div>
|
||||||
|
<button class="big" data-adj="pursuer,1">+</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="netout">
|
||||||
|
<div class="lbl">Net</div>
|
||||||
|
<div class="v zero" id="netVal">0</div>
|
||||||
|
</div>
|
||||||
|
<div class="stepper">
|
||||||
|
<div class="lbl">Prey</div>
|
||||||
|
<div class="ctrl">
|
||||||
|
<button class="big" data-adj="prey,-1">−</button>
|
||||||
|
<div class="v" id="preyVal">0</div>
|
||||||
|
<button class="big" data-adj="prey,1">+</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row" style="margin-top:12px">
|
||||||
|
<button class="big primary grow" id="applyBtn">Apply & next round</button>
|
||||||
|
<button class="big" id="undoBtn">Undo</button>
|
||||||
|
</div>
|
||||||
|
<div class="muted" style="margin-top:8px">
|
||||||
|
Side total = highest single Runner + all Pressure + banked Cutter spent.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h2>Obstacle <span class="sub" id="ladderName">—</span></h2>
|
||||||
|
<div id="obstacleBox"></div>
|
||||||
|
<div class="row">
|
||||||
|
<select id="ladderPick"></select>
|
||||||
|
<button class="big" id="peekBtn" title="Spotter: reveal next rung">Peek next</button>
|
||||||
|
</div>
|
||||||
|
<div class="muted" id="peekOut" style="margin-top:8px"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h2>Collateral <span class="sub" id="collSub">the city pays</span></h2>
|
||||||
|
<div class="pips" id="pips"></div>
|
||||||
|
<div class="collnote" id="collNote"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h2>Outcome <span class="sub">if it ended now</span></h2>
|
||||||
|
<div class="outcome" id="outcome"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<details>
|
||||||
|
<summary>▸ Roles reference</summary>
|
||||||
|
<table class="roles" style="margin-top:10px">
|
||||||
|
<tr><td>RUNNER</td><td>Mobility<br>Driving</td><td>Successes move LEAD. Free. The default.</td></tr>
|
||||||
|
<tr><td>PRESSURE</td><td>Firearms<br>Manipulation<br>Connections</td><td>Successes move LEAD. <span class="cost">1 Collateral each.</span></td></tr>
|
||||||
|
<tr><td>SPOTTER</td><td>Observation</td><td>Per success: negate a Toll · remove 1 Collateral · +1 advantage die · reveal next rung.</td></tr>
|
||||||
|
<tr><td>CUTTER</td><td>Mobility<br>Driving</td><td>Banks successes, spend all at once later. No successes = out 1 round, bank lost.</td></tr>
|
||||||
|
<tr><td>GAMBIT</td><td>once each</td><td>Disadvantage. Win = double successes. Lose = out of the chase, <span class="cost">+1 Collateral.</span></td></tr>
|
||||||
|
</table>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h2>Chase</h2>
|
||||||
|
<div class="row">
|
||||||
|
<button class="big amber grow" id="overtimeBtn">Call Overtime</button>
|
||||||
|
<button class="big danger" id="resetBtn">Reset</button>
|
||||||
|
</div>
|
||||||
|
<div class="muted" id="otNote" style="margin-top:8px">
|
||||||
|
Overtime: max 3 rounds, Terminator stands, LEAD −1 each round, Collateral doubled.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="modules/rundown.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
// Minimal DOM stub — drives the real rundown.js through its actual code paths.
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
class El {
|
||||||
|
constructor(tag){ this.tag=tag; this._id=''; this.className=''; this._html='';
|
||||||
|
this._text=''; this.children=[]; this.style={}; this.disabled=false; this.value='';
|
||||||
|
this.attrs={}; this.handlers={}; }
|
||||||
|
// real DOM: assigning innerHTML replaces all child nodes
|
||||||
|
set innerHTML(v){ this._html=String(v); this.children=[]; } get innerHTML(){ return this._html; }
|
||||||
|
set id(v){ this._id=v; reg[v]=this; } get id(){ return this._id; }
|
||||||
|
set textContent(v){ this._text=String(v); } get textContent(){ return this._text; }
|
||||||
|
addEventListener(ev,fn){ (this.handlers[ev]=this.handlers[ev]||[]).push(fn); }
|
||||||
|
click(){ (this.handlers.click||[]).forEach(f=>f.call(this)); }
|
||||||
|
appendChild(c){ this.children.push(c); return c; }
|
||||||
|
setAttribute(k,v){ this.attrs[k]=v; } getAttribute(k){ return this.attrs[k]; }
|
||||||
|
}
|
||||||
|
|
||||||
|
const reg = {};
|
||||||
|
const IDS = ['leadTrack','leadSub','roundVal','obstacleBox','ladderName','peekBtn','peekOut',
|
||||||
|
'pursuerVal','preyVal','netVal','pips','collNote','collSub','outcome','overtimeBtn',
|
||||||
|
'undoBtn','applyBtn','resetBtn','ladderPick'];
|
||||||
|
IDS.forEach(i => { const e = new El('div'); e.id = i; });
|
||||||
|
|
||||||
|
const adjBtns = [['pursuer',-1],['pursuer',1],['prey',-1],['prey',1]].map(([k,d]) => {
|
||||||
|
const b = new El('button'); b.setAttribute('data-adj', `${k},${d}`); return b;
|
||||||
|
});
|
||||||
|
|
||||||
|
global.document = {
|
||||||
|
readyState: 'complete',
|
||||||
|
getElementById: id => reg[id] || null,
|
||||||
|
createElement: t => new El(t),
|
||||||
|
querySelectorAll: sel => sel === '[data-adj]' ? adjBtns : [],
|
||||||
|
addEventListener: () => {},
|
||||||
|
};
|
||||||
|
const store = {};
|
||||||
|
global.localStorage = {
|
||||||
|
getItem: k => (k in store ? store[k] : null),
|
||||||
|
setItem: (k,v) => { store[k]=v; },
|
||||||
|
};
|
||||||
|
global.window = { confirm: () => true };
|
||||||
|
|
||||||
|
eval(fs.readFileSync(process.argv[2] || __dirname + "/../modules/rundown.js",'utf8'));
|
||||||
|
|
||||||
|
// ---- helpers ----
|
||||||
|
const leadNow = () => reg.leadTrack.children.findIndex(c => c.className.includes('cur'));
|
||||||
|
const roundNow = () => reg.roundVal.innerHTML.replace(/<[^>]*>/g,'');
|
||||||
|
const outcomeNow = () => (reg.outcome.innerHTML.match(/<div class="nm">([^<]*)<\/div>/)||[])[1];
|
||||||
|
const rungNow = () => (reg.obstacleBox.innerHTML.match(/Rung (\d)/)||[])[1];
|
||||||
|
const collNow = () => reg.pips.children.filter(c=>c.className.includes('on')).length;
|
||||||
|
// drive through the real +/- buttons rather than poking state
|
||||||
|
const setNet = (p,q) => {
|
||||||
|
for(let i=0;i<p;i++) adjBtns[1].click();
|
||||||
|
for(let i=0;i<q;i++) adjBtns[3].click();
|
||||||
|
};
|
||||||
|
|
||||||
|
let pass=0, fail=0;
|
||||||
|
const eq=(label,got,want)=>{ const ok=String(got)===String(want);
|
||||||
|
console.log(`${ok?' ok ':' FAIL'} ${label}: got ${got}${ok?'':` want ${want}`}`); ok?pass++:fail++; };
|
||||||
|
|
||||||
|
console.log('\n=== 1. initial state (Night Market, start 4) ===');
|
||||||
|
eq('LEAD', leadNow(), 4); eq('round', roundNow(), '1/6');
|
||||||
|
eq('rung', rungNow(), 1); eq('outcome band', outcomeNow(), 'LOST, BUT');
|
||||||
|
|
||||||
|
console.log('\n=== 2. pursuers net +2 -> LEAD 2, round 2, rung 2 ===');
|
||||||
|
setNet(2,0); reg.applyBtn.click();
|
||||||
|
eq('LEAD', leadNow(), 2); eq('round', roundNow(), '2/6');
|
||||||
|
eq('rung', rungNow(), 2); eq('outcome', outcomeNow(), 'CORNERED');
|
||||||
|
|
||||||
|
console.log('\n=== 3. prey wins the round, net -3 -> LEAD 5 ===');
|
||||||
|
setNet(1,4); reg.applyBtn.click();
|
||||||
|
eq('LEAD', leadNow(), 5); eq('round', roundNow(), '3/6'); eq('outcome', outcomeNow(), 'GONE');
|
||||||
|
|
||||||
|
console.log('\n=== 4. undo restores LEAD 2 / round 2 AND the entered totals ===');
|
||||||
|
reg.undoBtn.click();
|
||||||
|
eq('LEAD', leadNow(), 2); eq('round', roundNow(), '2/6');
|
||||||
|
eq('pursuer entry restored', reg.pursuerVal.textContent, 1);
|
||||||
|
eq('prey entry restored', reg.preyVal.textContent, 4);
|
||||||
|
|
||||||
|
console.log('\n=== 5. LEAD hits 0 -> ENGAGED, chase ends, apply locked ===');
|
||||||
|
for(let i=0;i<4;i++) adjBtns[2].click(); // clear prey back to 0
|
||||||
|
eq('prey cleared', reg.preyVal.textContent, 0);
|
||||||
|
setNet(1,0); // pursuer 1 + 1 = 2, net +2 from LEAD 2
|
||||||
|
reg.applyBtn.click();
|
||||||
|
eq('LEAD', leadNow(), 0); eq('outcome', outcomeNow(), 'CHASE OVER — ENGAGED');
|
||||||
|
eq('apply disabled', reg.applyBtn.disabled, true);
|
||||||
|
eq('round did NOT advance', roundNow(), '2/6');
|
||||||
|
|
||||||
|
console.log('\n=== 6. reset, then run to the round cap ===');
|
||||||
|
reg.resetBtn.click();
|
||||||
|
eq('LEAD after reset', leadNow(), 4); eq('round after reset', roundNow(), '1/6');
|
||||||
|
for (let r=1; r<6; r++) reg.applyBtn.click(); // five net-0 rounds
|
||||||
|
eq('round at cap', roundNow(), '6/6'); eq('rung at cap', rungNow(), 6);
|
||||||
|
eq('LEAD unchanged by net 0', leadNow(), 4);
|
||||||
|
eq('overtime offered', reg.overtimeBtn.disabled, false);
|
||||||
|
|
||||||
|
console.log('\n=== 7. round 6 applied with no overtime -> chase ends on the cap ===');
|
||||||
|
reg.applyBtn.click();
|
||||||
|
eq('ended at cap', outcomeNow(), 'CHASE OVER — LOST, BUT');
|
||||||
|
eq('apply disabled', reg.applyBtn.disabled, true);
|
||||||
|
|
||||||
|
console.log('\n=== 8. overtime: drift -1 per round, Terminator stands, hard ceiling ===');
|
||||||
|
reg.overtimeBtn.click();
|
||||||
|
eq('LEAD drifted on entry', leadNow(), 3);
|
||||||
|
eq('round label', roundNow(), 'OT1/3');
|
||||||
|
eq('rung stays Terminator', rungNow(), 6);
|
||||||
|
reg.applyBtn.click();
|
||||||
|
eq('OT2 LEAD drift', leadNow(), 2); eq('OT2 label', roundNow(), 'OT2/3');
|
||||||
|
reg.applyBtn.click();
|
||||||
|
eq('OT3 LEAD drift', leadNow(), 1); eq('OT3 label', roundNow(), 'OT3/3');
|
||||||
|
reg.applyBtn.click();
|
||||||
|
eq('ceiling reached, ended', reg.applyBtn.disabled, true);
|
||||||
|
eq('final outcome', outcomeNow(), 'CHASE OVER — CORNERED');
|
||||||
|
|
||||||
|
console.log('\n=== 9. collateral pips: tap to set, tap current to step back ===');
|
||||||
|
reg.resetBtn.click();
|
||||||
|
reg.pips.children[3].click(); eq('tap pip 4 -> 4', collNow(), 4);
|
||||||
|
reg.pips.children[3].click(); eq('tap pip 4 again -> 3', collNow(), 3);
|
||||||
|
reg.pips.children[0].click(); eq('tap pip 1 -> 1', collNow(), 1);
|
||||||
|
|
||||||
|
console.log('\n=== 10. LEAD clamps at 8 (GHOST), never overflows ===');
|
||||||
|
reg.resetBtn.click();
|
||||||
|
setNet(0,9); reg.applyBtn.click();
|
||||||
|
eq('clamped to 8', leadNow(), 8); eq('outcome', outcomeNow(), 'CHASE OVER — GHOST');
|
||||||
|
|
||||||
|
console.log(`\n${pass} passed, ${fail} failed\n`);
|
||||||
|
process.exit(fail ? 1 : 0);
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
# Converting From the Published Chase Rules
|
||||||
|
|
||||||
|
For GMs who already run the core rulebook's chase subsystem and want to swap in
|
||||||
|
RUNDOWN — mid-campaign, mid-session, or mid-chase.
|
||||||
|
|
||||||
|
This file assumes you own the core rulebook. It does not reproduce its rules.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. What maps to what
|
||||||
|
|
||||||
|
| Published concept | RUNDOWN equivalent |
|
||||||
|
|---|---|
|
||||||
|
| Range categories (Engaged → Extreme) | **LEAD**, a 0–8 number |
|
||||||
|
| Maneuver selected in secret | **Role**, declared in the open |
|
||||||
|
| Obstacle drawn or rolled | **Ladder rung**, authored and played in order |
|
||||||
|
| Pursuer group collapsing to one roller | **Everyone rolls**, side total takes the highest Runner |
|
||||||
|
| Chase runs until someone breaks off | **Six rounds**, then the outcome table |
|
||||||
|
| Consequences narrated ad hoc | **Collateral**, a 0–6 track spent after the scene |
|
||||||
|
|
||||||
|
### Range → LEAD
|
||||||
|
|
||||||
|
If you need to convert a position mid-chase:
|
||||||
|
|
||||||
|
| Published range | LEAD |
|
||||||
|
|---|---|
|
||||||
|
| Engaged | **0** |
|
||||||
|
| Short | **2** |
|
||||||
|
| Medium | **4** |
|
||||||
|
| Long | **6** |
|
||||||
|
| Extreme | **8** |
|
||||||
|
|
||||||
|
Read it the other way for the reverse. The mapping is deliberately coarse —
|
||||||
|
LEAD carries more resolution than five categories do, which is the point of the
|
||||||
|
change.
|
||||||
|
|
||||||
|
### Maneuver → Role
|
||||||
|
|
||||||
|
Players who know the published rules will reach for the maneuver names. Answer
|
||||||
|
in roles:
|
||||||
|
|
||||||
|
| They say | They mean |
|
||||||
|
|---|---|
|
||||||
|
| "I chase them" / "I run" | **RUNNER** |
|
||||||
|
| "I try to cut them off" | **CUTTER** |
|
||||||
|
| "I shoot" / "I shout at them" / "I call it in" | **PRESSURE** |
|
||||||
|
| "I watch where they're going" / "I look for a shortcut" | **SPOTTER** |
|
||||||
|
| "I hide" | Not a role — see §4 |
|
||||||
|
| "I block the alley" | **PRESSURE** using Force, GM's call |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Switching mid-chase
|
||||||
|
|
||||||
|
You can do this at a round boundary without stopping play.
|
||||||
|
|
||||||
|
1. **Convert the current range to LEAD** using the table above.
|
||||||
|
2. **Say the round number out loud.** If you are three rounds in, you have
|
||||||
|
three rungs left before the Terminator — write three obstacles, or take rungs
|
||||||
|
4, 5 and 6 from a ladder in `obstacle-ladders.md`.
|
||||||
|
3. **Have everyone declare a role.** Anyone who does not is a Runner.
|
||||||
|
4. **Carry Collateral forward at 0.** Do not retroactively bill anyone for what
|
||||||
|
happened before the switch — narrate it instead.
|
||||||
|
|
||||||
|
The one thing you cannot carry over is a maneuver already selected in secret.
|
||||||
|
Discard it and let them declare openly. Nobody has ever complained.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. What you are giving up
|
||||||
|
|
||||||
|
Be honest with your table about the trade, because it is a real one.
|
||||||
|
|
||||||
|
**You lose the bluff.** The published chase's simultaneous hidden selection
|
||||||
|
creates a rock-paper-scissors read — guessing whether they will break left or
|
||||||
|
turn and fight. RUNDOWN deletes that. In exchange you lose the 30–60 seconds
|
||||||
|
per round it costs, and the choice moves from *guessing what they will do* to
|
||||||
|
*deciding what this obstacle is worth to you*.
|
||||||
|
|
||||||
|
**You lose per-maneuver specificity.** Four roles cover less ground than the
|
||||||
|
published maneuver list. Anything not covered is a Pressure roll with the GM
|
||||||
|
choosing the skill, or a Gambit.
|
||||||
|
|
||||||
|
**You gain a table that all rolls.** This is the trade that motivated the whole
|
||||||
|
system. If your group is two players, the published rules work fine and you may
|
||||||
|
not need this. At four or more, the difference is the entire scene.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Rulings the conversion needs
|
||||||
|
|
||||||
|
**"I hide."** Hiding is not a role, because RUNDOWN has no hidden state. Treat
|
||||||
|
an attempt to break line of sight as a **Gambit** on a Runner roll using
|
||||||
|
Stealth: succeed and you double your successes (you are simply gone that
|
||||||
|
round); fail and you are out of the chase, spotted, at whatever LEAD stands.
|
||||||
|
|
||||||
|
**"I stand and shoot."** That is PRESSURE with Firearms. It moves LEAD and it
|
||||||
|
costs Collateral. If a player wants a *lethal* shot rather than a suppressive
|
||||||
|
one, stop the chase and resolve it as combat — RUNDOWN abstracts gunfire into
|
||||||
|
pressure, and a killing shot is a different scene.
|
||||||
|
|
||||||
|
**"I ram them."** PRESSURE with Force or Driving, minimum 2 Collateral on a
|
||||||
|
success, and ask whether they are ending the chase deliberately. Often they are.
|
||||||
|
|
||||||
|
**Vehicles against pedestrians.** Use §9 of the rules — each side rolls its own
|
||||||
|
terrain layer, Collateral takes the higher rate. A spinner over a runner on
|
||||||
|
foot starts at LEAD 5 and closes fast; that is correct, and the ladder is what
|
||||||
|
keeps it interesting.
|
||||||
|
|
||||||
|
**Prey with no relevant skill.** An unathletic target who bolts is not a chase,
|
||||||
|
it is a capture. Do not roll. RUNDOWN is for when the outcome is in doubt.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Running both
|
||||||
|
|
||||||
|
Nothing stops you keeping the published chase for two-hander scenes and using
|
||||||
|
RUNDOWN when the whole table is involved. They share the same dice, the same
|
||||||
|
skills, and the same fiction. The only cost is remembering which one you called.
|
||||||
|
|
||||||
|
If you do, tell the players which system is live **before** they declare —
|
||||||
|
that is the only place the two get confused in practice.
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
# Obstacle Ladders
|
||||||
|
|
||||||
|
**Eight ready-to-run ladders for RUNDOWN.** Pick one, set the starting LEAD,
|
||||||
|
run it. Each is six rungs, played one per round in the order written. Rung 6 is
|
||||||
|
always a Terminator — it forces the chase to break one way or the other.
|
||||||
|
|
||||||
|
**Reading a rung:**
|
||||||
|
|
||||||
|
- **Toll** — a roll to avoid a penalty. Everyone the obstacle touches makes it.
|
||||||
|
A failed Toll in crowded terrain also generates **1 Collateral**.
|
||||||
|
- **Tilt** — which side the terrain favours. Usually one advantage die.
|
||||||
|
|
||||||
|
Crowded rungs are marked **[C]** — a failed Toll there costs Collateral.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. THE NIGHT MARKET — *foot*
|
||||||
|
|
||||||
|
**Start LEAD 3–4.** Steam, frying oil, six languages, nobody looking up.
|
||||||
|
The classic. Bodies everywhere, and every one of them a reason not to shoot.
|
||||||
|
|
||||||
|
| # | Obstacle | Toll | Tilt |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | **Noodle stalls** — a wall of stools and elbows **[C]** | Mobility, or lose your roll | Prey +1 |
|
||||||
|
| 2 | **Steam-cart collision** — scalding white-out, someone screaming about their cart **[C]** | Mobility, or lose your roll | Neither |
|
||||||
|
| 3 | **Monk procession** — slow, chanting, impossible to shove through **[C]** | Manipulation, or your role fails this round | Prey +1 |
|
||||||
|
| 4 | **Holo-ad burst** — a four-storey geisha blooms across the alley, blinding everyone | Observation, or lose your roll | Neither |
|
||||||
|
| 5 | **Plastic curtain maze** — hanging sheeting, animoid tanks, forty ways out | Mobility, or lose your roll | Prey +1; Spotter rolls at disadvantage |
|
||||||
|
| 6 | **THE SHRINE ALLEY** *(Terminator)* — a dead end behind a paper shrine. Three metres of wall, no door, an old woman watching | None — there is nowhere left to go | Pursuers +1 |
|
||||||
|
|
||||||
|
**Terminator note:** the best place in the sector to catch someone gently. If
|
||||||
|
the prey reaches this rung still ahead, they are choosing the wall over you —
|
||||||
|
play that.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. SECTOR 12 ROOFTOPS — *foot*
|
||||||
|
|
||||||
|
**Start LEAD 3.** Wet tar, aerial clutter, a two-hundred-metre drop on every
|
||||||
|
side. Gambits are the whole texture here — expect three of them.
|
||||||
|
|
||||||
|
| # | Obstacle | Toll | Tilt |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | **Gravel and standing water** — no grip anywhere | Mobility, or lose your roll | Neither |
|
||||||
|
| 2 | **Aerial forest** — dishes, guy-lines, a decade of illegal cabling | Mobility, or lose your roll | Prey +1 |
|
||||||
|
| 3 | **The service gap** — four metres between blocks, forty storeys down | Mobility. **Failure here is a Gambit failure** — you are out of the chase and hurt | Pursuers +1 |
|
||||||
|
| 4 | **Downdraft** — a spinner passes low and the roof turns into a wind tunnel | Force, or lose your roll | Neither |
|
||||||
|
| 5 | **Skylight run** — old glass over an occupied floor **[C]** | Mobility, or you go through it | Prey +1 |
|
||||||
|
| 6 | **THE PARAPET** *(Terminator)* — the block ends. Below is the arterial, moving fast | None | Pursuers +1 |
|
||||||
|
|
||||||
|
**Terminator note:** somebody is going to jump. Decide before you start whether
|
||||||
|
this city's rooftops have a next building close enough, and tell the players.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. THE STORM DRAIN — *foot*
|
||||||
|
|
||||||
|
**Start LEAD 4.** Concrete throat under the city, ankle-deep and rising. No
|
||||||
|
crowd to hurt — Collateral here is structural and personal, not civilian.
|
||||||
|
|
||||||
|
| # | Obstacle | Toll | Tilt |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | **The grade** — a long wet slope, no handholds | Mobility, or lose your roll | Neither |
|
||||||
|
| 2 | **Dark** — the lighting failed years ago | Observation, or roll at disadvantage next round | Prey +1 |
|
||||||
|
| 3 | **Sluice discharge** — a side pipe dumps hard across the channel | Force, or lose your roll | Neither |
|
||||||
|
| 4 | **The junction** — four identical mouths, one right answer | Observation, or your role fails this round | Prey +1; Spotter shines here |
|
||||||
|
| 5 | **Rising water** — knee-deep and climbing, the sound enormous | Stamina or Force, or lose your roll | Pursuers +1 |
|
||||||
|
| 6 | **THE OUTFALL GRATE** *(Terminator)* — the channel ends at a rusted grate and open air beyond | None | Pursuers +1 |
|
||||||
|
|
||||||
|
**Terminator note:** the grate is the question. Locked, and it is a capture.
|
||||||
|
Loose, and it is a fifteen-metre drop into a settling pond.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. TRANSIT INTERCHANGE — *foot*
|
||||||
|
|
||||||
|
**Start LEAD 4.** Six levels, forty thousand people an hour, cameras on every
|
||||||
|
face. The most expensive place in the city to draw a weapon.
|
||||||
|
|
||||||
|
| # | Obstacle | Toll | Tilt |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | **The concourse** — a river of commuters going the other way **[C]** | Mobility, or lose your roll | Prey +1 |
|
||||||
|
| 2 | **Turnstile bank** — everyone stops, nobody stops for you **[C]** | Connections or Tech, or lose your roll | Neither |
|
||||||
|
| 3 | **Down escalator, wrong way** **[C]** | Mobility, or lose your roll | Prey +1 |
|
||||||
|
| 4 | **Transit police** — two officers who have noticed **[C]** | Manipulation, or your role fails and you generate 1 Collateral | Neither |
|
||||||
|
| 5 | **The platform** — a train arriving, doors about to close **[C]** | Observation, or lose your roll | Prey +1 |
|
||||||
|
| 6 | **THE CLOSING DOORS** *(Terminator)* — one carriage, both of you, or one of you | None | Neither |
|
||||||
|
|
||||||
|
**Terminator note:** if the prey makes the train and the pursuers do not, that
|
||||||
|
is **GONE**, not a failure — the platform cameras have a face and a departure
|
||||||
|
time. Say so out loud.
|
||||||
|
|
||||||
|
**Collateral warning:** every rung here is crowded. A table that reaches for
|
||||||
|
Pressure in a transit hall will max the track by round four. That is the lesson,
|
||||||
|
not a punishment — let it happen and bill them later.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. THE FREIGHT YARD — *ground vehicle*
|
||||||
|
|
||||||
|
**Start LEAD 4. Collateral doubled.** Container stacks, gantry cranes, and
|
||||||
|
nobody around to see it except the people who own the containers.
|
||||||
|
|
||||||
|
| # | Obstacle | Toll | Tilt |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | **Standing water and rail** — the surface will not hold a hard turn | Driving, or lose your roll | Neither |
|
||||||
|
| 2 | **Container canyon** — blind corners, no sightlines | Observation, or roll at disadvantage next round | Prey +1 |
|
||||||
|
| 3 | **Gantry crane** — a load swinging across the lane | Driving, or lose your roll | Neither |
|
||||||
|
| 4 | **Yard hauler** — forty metres of articulated freight, reversing | Driving, or your role fails this round | Prey +1 |
|
||||||
|
| 5 | **Fuel apron** — hard surface, wide open, and a very good reason not to shoot | Driving. **Any Firearms Pressure here generates double Collateral** | Pursuers +1 |
|
||||||
|
| 6 | **THE GATE** *(Terminator)* — closed, manned, and lowering | None | Pursuers +1 |
|
||||||
|
|
||||||
|
**Terminator note:** the gate is a choice between stopping and going through it.
|
||||||
|
Going through it is 3 Collateral and a report that writes itself.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. SEPULVEDA ARTERIAL, RAIN — *ground vehicle*
|
||||||
|
|
||||||
|
**Start LEAD 4. Collateral doubled.** Eight lanes, standing water, everyone
|
||||||
|
doing ninety because the traffic system says it is safe.
|
||||||
|
|
||||||
|
| # | Obstacle | Toll | Tilt |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | **Spray** — the car ahead throws a wall of it | Observation, or lose your roll | Neither |
|
||||||
|
| 2 | **Lane discipline** — the automated flow does not want you doing this **[C]** | Driving or Tech, or lose your roll | Prey +1 |
|
||||||
|
| 3 | **Underpass** — dark, loud, three lanes narrowing to two **[C]** | Driving, or lose your roll | Neither |
|
||||||
|
| 4 | **Stalled vehicle** — hazards on, occupants inside **[C]** | Driving, or lose your roll and generate 1 Collateral | Pursuers +1 |
|
||||||
|
| 5 | **Interchange ramp** — four ways off, decided in a second and a half | Observation, or your role fails this round | Prey +1; Spotter decisive |
|
||||||
|
| 6 | **THE CHECKPOINT** *(Terminator)* — LAPD barrier across all lanes, lights on **[C]** | None | Pursuers +1 |
|
||||||
|
|
||||||
|
**Terminator note:** the checkpoint ends this one way or another. If the prey
|
||||||
|
runs it, that is a citywide alert and someone else's arrest — the PCs get
|
||||||
|
**LOST, BUT** with a very good lead and a very bad afternoon.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. OVER THE WALL — *spinner / aerial*
|
||||||
|
|
||||||
|
**Start LEAD 5. Collateral halved, but every point is severe.** Nothing below
|
||||||
|
you for six hundred metres, and everything below you is somebody's roof.
|
||||||
|
|
||||||
|
| # | Obstacle | Toll | Tilt |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | **Traffic lane** — you are both in a corridor you are not cleared for | Driving or Connections, or lose your roll | Neither |
|
||||||
|
| 2 | **Rain shear** — the weather between the towers is its own animal | Driving, or lose your roll | Neither |
|
||||||
|
| 3 | **Ad blimp** — three hundred metres of illuminated hull, directly across | Driving, or your role fails this round | Prey +1 |
|
||||||
|
| 4 | **Transponder ping** — traffic control wants to know who you are | Tech or Connections, or roll at disadvantage next round | Pursuers +1 |
|
||||||
|
| 5 | **The span** — a bridge deck with clearance nobody sane would take | Driving. **Taking it is a Gambit**; refusing costs your roll | Prey +1 |
|
||||||
|
| 6 | **THE WALL** *(Terminator)* — the sector boundary, and a hard deck beyond it that is not your jurisdiction | None | Neither |
|
||||||
|
|
||||||
|
**Terminator note:** the Wall is jurisdictional, not physical. Crossing it means
|
||||||
|
the chase is over and the paperwork has begun. If the prey crosses, that is
|
||||||
|
**GONE** — you know exactly where they went and you cannot follow.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. THE ATRIUM — *foot, corporate interior*
|
||||||
|
|
||||||
|
**Start LEAD 3.** Polished stone, filtered air, and four hundred employees who
|
||||||
|
have never seen anybody run. Every camera works. Every camera is owned.
|
||||||
|
|
||||||
|
| # | Obstacle | Toll | Tilt |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | **The floor** — wet-polished stone, no traction at speed **[C]** | Mobility, or lose your roll | Neither |
|
||||||
|
| 2 | **Reception cordon** — a desk, a rope line, and a very calm young man **[C]** | Manipulation, or your role fails this round | Prey +1 |
|
||||||
|
| 3 | **Elevator bank** — twelve doors, one of them closing **[C]** | Observation, or lose your roll | Prey +1 |
|
||||||
|
| 4 | **Corporate security** — two of them, polite, armed, and not police **[C]** | Manipulation or Connections, or your role fails and you generate 1 Collateral | Neither |
|
||||||
|
| 5 | **The gallery** — a mezzanine walkway over the whole atrium, in full view of everyone **[C]** | Mobility, or lose your roll | Pursuers +1 |
|
||||||
|
| 6 | **THE SEALED FLOOR** *(Terminator)* — the building has decided. Every door on this level locks at once | None | Pursuers +1 |
|
||||||
|
|
||||||
|
**Terminator note:** the building sealing is not on the PCs' side. Everyone is
|
||||||
|
now inside with corporate security, and the question of who leaves is a
|
||||||
|
negotiation, not a chase.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Writing your own
|
||||||
|
|
||||||
|
Five minutes, six lines:
|
||||||
|
|
||||||
|
1. **Pick the location** and picture somebody sprinting through it.
|
||||||
|
2. **List six things that would interrupt them.** Not six dangers — six
|
||||||
|
*interruptions*. Crowds, doors, weather, machinery, other people's business.
|
||||||
|
3. **Order them from annoying to impassable.**
|
||||||
|
4. **Give each a Toll** — the skill that gets you past it, and what it costs to
|
||||||
|
fail. "Lose your roll this round" is the default and is usually right.
|
||||||
|
5. **Tilt about half of them.** Crowds favour the prey. Open ground favours the
|
||||||
|
pursuers. Leave some neutral.
|
||||||
|
6. **Make rung 6 force the ending.** A wall, a barrier, a drop, a door that
|
||||||
|
locks, a vehicle leaving. Something that cannot be run past.
|
||||||
|
|
||||||
|
Mark crowded rungs **[C]** so the Collateral rule is visible at the table.
|
||||||
|
|
||||||
|
**One thing to check before you run it:** read your six rungs in order and ask
|
||||||
|
whether they escalate. If rung 2 is scarier than rung 5, swap them. The ladder
|
||||||
|
is the scene's shape — that is the whole reason it is written down.
|
||||||
@@ -0,0 +1,237 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>RUNDOWN — GM Sheet</title>
|
||||||
|
<style>
|
||||||
|
@page { size: A4 portrait; margin: 10mm; }
|
||||||
|
*{box-sizing:border-box;margin:0;padding:0}
|
||||||
|
body{
|
||||||
|
font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif;
|
||||||
|
font-size:8.4pt; line-height:1.32; color:#111; background:#fff;
|
||||||
|
padding:10mm; max-width:210mm; margin:0 auto;
|
||||||
|
}
|
||||||
|
h1{font-size:17pt;letter-spacing:.16em;font-weight:800;margin-bottom:1px}
|
||||||
|
h1 .sub{display:block;font-size:7.2pt;letter-spacing:.22em;font-weight:400;color:#555;margin-top:3px;text-transform:uppercase}
|
||||||
|
h2{
|
||||||
|
font-size:7.4pt;letter-spacing:.2em;text-transform:uppercase;font-weight:800;
|
||||||
|
border-bottom:1.5px solid #111;padding-bottom:2px;margin-bottom:5px;
|
||||||
|
}
|
||||||
|
header{display:flex;justify-content:space-between;align-items:flex-end;border-bottom:2.5px solid #111;padding-bottom:5px;margin-bottom:8px}
|
||||||
|
header .lead-set{font-size:7.2pt;color:#333;text-align:right;line-height:1.5}
|
||||||
|
header .lead-set b{letter-spacing:.06em}
|
||||||
|
|
||||||
|
.cols{display:grid;grid-template-columns:1fr 1fr;gap:8px}
|
||||||
|
.box{border:1px solid #999;padding:6px 7px;margin-bottom:7px;break-inside:avoid}
|
||||||
|
table{width:100%;border-collapse:collapse;font-size:8pt}
|
||||||
|
th,td{text-align:left;padding:2.5px 4px;vertical-align:top;border-bottom:.5px solid #ccc}
|
||||||
|
th{font-size:6.8pt;letter-spacing:.13em;text-transform:uppercase;color:#444;border-bottom:1px solid #888}
|
||||||
|
tr:last-child td{border-bottom:none}
|
||||||
|
.role{font-weight:800;letter-spacing:.05em;white-space:nowrap}
|
||||||
|
.skill{color:#555;font-size:7.4pt;white-space:nowrap}
|
||||||
|
.k{font-weight:700}
|
||||||
|
|
||||||
|
/* LEAD track */
|
||||||
|
.track{display:flex;gap:3px;margin:5px 0 3px}
|
||||||
|
.track .n{
|
||||||
|
flex:1;text-align:center;border:1px solid #666;padding:7px 0 6px;
|
||||||
|
font-size:12pt;font-weight:800;font-family:"Courier New",monospace;
|
||||||
|
}
|
||||||
|
.track .n.end{background:#111;color:#fff;border-color:#111}
|
||||||
|
.track .n small{display:block;font-size:5.4pt;letter-spacing:.1em;font-weight:600;font-family:inherit;margin-top:1px}
|
||||||
|
.tracknote{display:flex;justify-content:space-between;font-size:6.6pt;letter-spacing:.14em;text-transform:uppercase;color:#444}
|
||||||
|
|
||||||
|
/* Collateral pips */
|
||||||
|
.pips{display:flex;gap:5px;margin:4px 0 5px}
|
||||||
|
.pips div{width:19px;height:19px;border:1.5px solid #333;border-radius:50%}
|
||||||
|
.pips div:nth-child(n+5){border-color:#111;border-width:2.5px}
|
||||||
|
|
||||||
|
/* procedure */
|
||||||
|
ol.proc{list-style:none;counter-reset:p;font-size:8.2pt}
|
||||||
|
ol.proc li{counter-increment:p;padding:2.5px 0 2.5px 20px;position:relative;border-bottom:.5px solid #ddd}
|
||||||
|
ol.proc li:last-child{border-bottom:none}
|
||||||
|
ol.proc li::before{
|
||||||
|
content:counter(p);position:absolute;left:0;top:2.5px;width:14px;height:14px;
|
||||||
|
background:#111;color:#fff;font-size:7pt;font-weight:800;text-align:center;line-height:14px;border-radius:2px;
|
||||||
|
}
|
||||||
|
.formula{background:#f0f0f0;border-left:2.5px solid #111;padding:4px 7px;margin:5px 0;font-size:7.8pt;line-height:1.45}
|
||||||
|
|
||||||
|
/* ladder */
|
||||||
|
.ladder td{height:19px;border-bottom:.5px solid #999}
|
||||||
|
.ladder td.num{width:16px;text-align:center;font-weight:800;font-family:"Courier New",monospace;border-right:1px solid #999;background:#f2f2f2}
|
||||||
|
.ladder tr:last-child td{border-bottom:1.5px solid #111}
|
||||||
|
.ladder tr:last-child td.num{background:#111;color:#fff}
|
||||||
|
.fill{border-bottom:.5px solid #999;height:19px}
|
||||||
|
|
||||||
|
.note{font-size:7pt;color:#444;line-height:1.4;margin-top:4px;font-style:italic}
|
||||||
|
footer{margin-top:8px;padding-top:5px;border-top:1px solid #999;font-size:6.2pt;color:#666;line-height:1.45;text-align:center}
|
||||||
|
@media print{ body{padding:0} .noprint{display:none} }
|
||||||
|
.noprint{position:fixed;top:8px;right:8px;background:#111;color:#fff;border:none;padding:8px 14px;font-size:9pt;border-radius:4px;cursor:pointer;font-family:inherit;letter-spacing:.1em}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<button class="noprint" onclick="window.print()">PRINT</button>
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<h1>RUNDOWN<span class="sub">Cardless chases · Blade Runner RPG</span></h1>
|
||||||
|
<div class="lead-set">
|
||||||
|
<b>START LEAD</b> 3 close · 4 standard · 5 distant<br>
|
||||||
|
<b>6 ROUNDS</b> ~15 min · Overtime max 3
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- LEAD TRACK -->
|
||||||
|
<div class="box">
|
||||||
|
<h2>LEAD — mark it, move it once a round</h2>
|
||||||
|
<div class="track">
|
||||||
|
<div class="n end">0<small>ENGAGED</small></div>
|
||||||
|
<div class="n">1</div><div class="n">2</div><div class="n">3</div><div class="n">4</div>
|
||||||
|
<div class="n">5</div><div class="n">6</div><div class="n">7</div>
|
||||||
|
<div class="n end">8<small>GHOST</small></div>
|
||||||
|
</div>
|
||||||
|
<div class="tracknote"><span>◀ Pursuers gain</span><span>Prey gains ▶</span></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cols">
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<!-- ROLES -->
|
||||||
|
<div class="box">
|
||||||
|
<h2>Roles — everyone takes one, every round</h2>
|
||||||
|
<table>
|
||||||
|
<tr><th>Role</th><th>Roll</th><th>Effect</th></tr>
|
||||||
|
<tr>
|
||||||
|
<td class="role">RUNNER</td><td class="skill">Mobility<br>Driving</td>
|
||||||
|
<td>Successes move LEAD. <span class="k">Free.</span> The default — declare nothing, you are this.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="role">PRESSURE</td><td class="skill">Firearms<br>Manipulation<br>Connections</td>
|
||||||
|
<td>Successes move LEAD. <span class="k">1 Collateral each.</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="role">SPOTTER</td><td class="skill">Observation</td>
|
||||||
|
<td>Spend each success: negate a Toll · <span class="k">remove 1 Collateral</span> · +1 advantage die to an ally · reveal next rung.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="role">CUTTER</td><td class="skill">Mobility<br>Driving</td>
|
||||||
|
<td><span class="k">Banks</span> successes — spend all at once later. No successes = out 1 round, bank lost.</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<div class="note">Fast is expensive. Pure Runners win slowly and clean; Pressure wins fast and bills the city.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- PROCEDURE -->
|
||||||
|
<div class="box">
|
||||||
|
<h2>The round</h2>
|
||||||
|
<ol class="proc">
|
||||||
|
<li><b>Reveal the obstacle</b> — before anyone declares.</li>
|
||||||
|
<li><b>Prey</b> declares a role and rolls.</li>
|
||||||
|
<li><b>Pursuers</b> declare in any order and roll.</li>
|
||||||
|
<li><b>Net</b> → move LEAD.</li>
|
||||||
|
<li><b>Collateral.</b> Narrate. Next rung.</li>
|
||||||
|
</ol>
|
||||||
|
<div class="formula">
|
||||||
|
<b>SIDE TOTAL</b> = highest <u>single</u> Runner result
|
||||||
|
+ all Pressure successes + banked Cutter spent<br>
|
||||||
|
<b>NET</b> = pursuers − prey → <b>+</b> lowers LEAD · <b>−</b> raises it
|
||||||
|
</div>
|
||||||
|
<div class="note">NPC side: <b>one roll per round</b>, whatever the table size. A real edge is +1 advantage die, never an extra roll.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- GAMBIT -->
|
||||||
|
<div class="box">
|
||||||
|
<h2>Gambit — once each per chase</h2>
|
||||||
|
<table>
|
||||||
|
<tr><td class="k" style="width:62px">Roll</td><td>At <b>disadvantage</b>. Modifies your role roll — not an extra roll.</td></tr>
|
||||||
|
<tr><td class="k">Success</td><td><b>Double</b> your successes this round.</td></tr>
|
||||||
|
<tr><td class="k">Failure</td><td><b>Out of the chase</b>, terrain-appropriate consequence, +1 Collateral.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<!-- COLLATERAL -->
|
||||||
|
<div class="box">
|
||||||
|
<h2>Collateral — the city pays, never the sheet</h2>
|
||||||
|
<div class="pips"><div></div><div></div><div></div><div></div><div></div><div></div></div>
|
||||||
|
<table>
|
||||||
|
<tr><td class="k" style="width:34px">+1</td><td>each Pressure success · each failed Toll in a crowd · each failed Gambit</td></tr>
|
||||||
|
<tr><td class="k">−1</td><td>each Spotter success, same round it is generated</td></tr>
|
||||||
|
</table>
|
||||||
|
<table style="margin-top:4px">
|
||||||
|
<tr><th>Spend after</th><th>Cost</th></tr>
|
||||||
|
<tr><td class="k">1–2</td><td>Noise. Someone remembers your face.</td></tr>
|
||||||
|
<tr><td class="k">3–4</td><td>A report gets filed. LAPD attention, or the case gets harder.</td></tr>
|
||||||
|
<tr><td class="k">5–6</td><td>A body, or a burned asset. Something you needed is gone.</td></tr>
|
||||||
|
</table>
|
||||||
|
<div class="note">Never spent during the chase. Never modifies a roll. Capped at 6.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- OUTCOMES -->
|
||||||
|
<div class="box">
|
||||||
|
<h2>Outcomes — LEAD 0, LEAD 8, or end of round 6</h2>
|
||||||
|
<table>
|
||||||
|
<tr><td class="k" style="width:34px">0</td><td><b>ENGAGED</b> — hands on them.</td></tr>
|
||||||
|
<tr><td class="k">1–2</td><td><b>CORNERED</b> — trapped, not held. Standoff, negotiation, or a jump.</td></tr>
|
||||||
|
<tr><td class="k">3–4</td><td><b>LOST, BUT</b> — gone, and you have a direction, an object, a plate, a witness.</td></tr>
|
||||||
|
<tr><td class="k">5–7</td><td><b>GONE</b> — clean. The street remembers.</td></tr>
|
||||||
|
<tr><td class="k">8</td><td><b>GHOST</b> — you never had them.</td></tr>
|
||||||
|
</table>
|
||||||
|
<div class="note">No outcome is a dead end. LOST, BUT is the ordinary failure and it still hands you the next scene.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- OVERTIME + TERRAIN -->
|
||||||
|
<div class="box">
|
||||||
|
<h2>Overtime & terrain</h2>
|
||||||
|
<table>
|
||||||
|
<tr><td colspan="2" style="padding-bottom:4px"><b>OVERTIME</b> — max 3 rounds, GM's call at end of round 6. Terminator stands, no new rungs. <b>LEAD −1 at the start of each round.</b> Collateral doubled.</td></tr>
|
||||||
|
<tr><th>Terrain</th><th>Runner / Cutter · Start · Collateral</th></tr>
|
||||||
|
<tr><td class="k">Foot</td><td>Mobility · LEAD 3–4 · standard</td></tr>
|
||||||
|
<tr><td class="k">Vehicle</td><td>Driving · LEAD 4 · <b>doubled</b></td></tr>
|
||||||
|
<tr><td class="k">Spinner</td><td>Driving · LEAD 5 · <b>halved</b>, each point severe</td></tr>
|
||||||
|
</table>
|
||||||
|
<div class="note">Mixed chase: each side uses its own layer; Collateral takes the higher rate.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- LADDER -->
|
||||||
|
<div class="box">
|
||||||
|
<h2>Obstacle ladder — six rungs, in order, one per round. Rung 6 forces the ending.</h2>
|
||||||
|
<table class="ladder">
|
||||||
|
<tr><th style="width:16px"></th><th style="width:34%">Obstacle</th><th style="width:33%">Toll</th><th>Tilt</th></tr>
|
||||||
|
<tr><td class="num">1</td><td></td><td></td><td></td></tr>
|
||||||
|
<tr><td class="num">2</td><td></td><td></td><td></td></tr>
|
||||||
|
<tr><td class="num">3</td><td></td><td></td><td></td></tr>
|
||||||
|
<tr><td class="num">4</td><td></td><td></td><td></td></tr>
|
||||||
|
<tr><td class="num">5</td><td></td><td></td><td></td></tr>
|
||||||
|
<tr><td class="num">6</td><td></td><td></td><td></td></tr>
|
||||||
|
</table>
|
||||||
|
<div class="note">Toll = a roll or lose your roll this round. Tilt = who the terrain favours, usually +1 die. Mark crowded rungs [C] — a failed Toll there costs 1 Collateral.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ROUND LOG -->
|
||||||
|
<div class="box">
|
||||||
|
<h2>Round log</h2>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:16px">#</th><th style="width:30%">Roles taken</th><th style="width:12%">Net</th><th style="width:12%">LEAD</th><th style="width:12%">Coll.</th><th>Note</th></tr>
|
||||||
|
<tr><td class="k">1</td><td class="fill"></td><td class="fill"></td><td class="fill"></td><td class="fill"></td><td class="fill"></td></tr>
|
||||||
|
<tr><td class="k">2</td><td class="fill"></td><td class="fill"></td><td class="fill"></td><td class="fill"></td><td class="fill"></td></tr>
|
||||||
|
<tr><td class="k">3</td><td class="fill"></td><td class="fill"></td><td class="fill"></td><td class="fill"></td><td class="fill"></td></tr>
|
||||||
|
<tr><td class="k">4</td><td class="fill"></td><td class="fill"></td><td class="fill"></td><td class="fill"></td><td class="fill"></td></tr>
|
||||||
|
<tr><td class="k">5</td><td class="fill"></td><td class="fill"></td><td class="fill"></td><td class="fill"></td><td class="fill"></td></tr>
|
||||||
|
<tr><td class="k">6</td><td class="fill"></td><td class="fill"></td><td class="fill"></td><td class="fill"></td><td class="fill"></td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
RUNDOWN v1 — unofficial fan supplement. Not affiliated with, endorsed by, or sponsored by Free League Publishing
|
||||||
|
or the rights holders of Blade Runner. Requires the Blade Runner RPG core rulebook to play. Not for sale.
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,330 @@
|
|||||||
|
# RUNDOWN
|
||||||
|
|
||||||
|
**Cardless chase rules for the Blade Runner RPG.**
|
||||||
|
*v1 draft — unofficial fan supplement. Requires the core rulebook.*
|
||||||
|
|
||||||
|
> Nobody in this city runs because they think they'll get away. They run because
|
||||||
|
> stopping means answering the question.
|
||||||
|
|
||||||
|
RUNDOWN replaces the chase subsystem in the core rulebook. It needs **no cards,
|
||||||
|
no tables to roll on, and no components beyond the dice already in front of
|
||||||
|
you**. Every player has a job in every round, and the whole chase fits in
|
||||||
|
fifteen minutes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. What you need
|
||||||
|
|
||||||
|
- The dice you already use: an attribute die and a skill die per roll.
|
||||||
|
- **One number written where everyone can see it** — the LEAD.
|
||||||
|
- **Six obstacles**, written before play. Take one from
|
||||||
|
`reference/obstacle-ladders.md` or write your own (§7).
|
||||||
|
|
||||||
|
That is all. No deck, no board, no tokens.
|
||||||
|
|
||||||
|
**Resolution is unchanged from the core rules.** Roll your attribute die and
|
||||||
|
your skill die. **6–9 is one success. 10+ is two successes.** Advantage adds a
|
||||||
|
d6; disadvantage drops your lowest die.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. LEAD
|
||||||
|
|
||||||
|
**LEAD is how far ahead the prey is.** One number, 0 to 8.
|
||||||
|
|
||||||
|
| | |
|
||||||
|
|---|---|
|
||||||
|
| **LEAD 0** | **ENGAGED.** Chase over — hands on them. |
|
||||||
|
| **LEAD 8** | **GHOST.** Chase over — they broke away clean. |
|
||||||
|
|
||||||
|
Set the starting LEAD when you call the chase:
|
||||||
|
|
||||||
|
| Start | When |
|
||||||
|
|---|---|
|
||||||
|
| **3** | **Close.** A stumble away. They were nearly in reach when they bolted. |
|
||||||
|
| **4** | **Standard.** They got a jump on you. *Use this when unsure.* |
|
||||||
|
| **5** | **Distant.** You spotted them across the plaza, the lot, the traffic lane. |
|
||||||
|
|
||||||
|
LEAD goes **down** when the pursuers gain and **up** when the prey does. It
|
||||||
|
moves once per round, by one number, and that number is the only arithmetic in
|
||||||
|
the system.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Roles
|
||||||
|
|
||||||
|
At the top of the chase, **every participant takes a role**. Any number can
|
||||||
|
share a role, and anyone may change role between rounds.
|
||||||
|
|
||||||
|
**RUNNER is the default.** A player who declares nothing is a Runner.
|
||||||
|
|
||||||
|
### RUNNER — *close the gap, or open it*
|
||||||
|
**Roll:** Mobility (on foot) or Driving (vehicle, spinner)
|
||||||
|
Successes move LEAD. Costs nothing. The safe, steady option.
|
||||||
|
|
||||||
|
### PRESSURE — *make them flinch*
|
||||||
|
**Roll:** Firearms, Manipulation, or Connections
|
||||||
|
You act on the prey without moving: a warning shot, a shouted order down an
|
||||||
|
alley, a call ahead to someone who owes you, a traffic signal killed from a
|
||||||
|
terminal.
|
||||||
|
|
||||||
|
Successes move LEAD — **but every success generates 1 Collateral (§6).**
|
||||||
|
|
||||||
|
### SPOTTER — *read the ground*
|
||||||
|
**Roll:** Observation
|
||||||
|
Spend each success on **one** of:
|
||||||
|
|
||||||
|
- **Negate one Toll penalty** suffered by anyone on your side this round.
|
||||||
|
- **Remove 1 Collateral** generated this round.
|
||||||
|
- **Grant one advantage die** to an ally's next roll.
|
||||||
|
- **Reveal the next rung** of the obstacle ladder before it is played.
|
||||||
|
|
||||||
|
### CUTTER — *take the other way round*
|
||||||
|
**Roll:** Mobility (on foot) or Driving (vehicle, spinner)
|
||||||
|
A parallel route — a fire escape, a service corridor, a side street, an overhead
|
||||||
|
pass. Your successes are **banked, not applied.**
|
||||||
|
|
||||||
|
Banked successes may be spent **all at once, in any later round**, added to that
|
||||||
|
round's side total. On a roll with **no successes**, you are out of the chase
|
||||||
|
for one round and **your bank is lost**.
|
||||||
|
|
||||||
|
### How the roles pay off
|
||||||
|
|
||||||
|
| Role | Buys you | Costs you |
|
||||||
|
|---|---|---|
|
||||||
|
| **RUNNER** | Steady progress | Nothing |
|
||||||
|
| **PRESSURE** | Speed | Collateral |
|
||||||
|
| **SPOTTER** | Safety, and Collateral back | A round of progress |
|
||||||
|
| **CUTTER** | A decisive later round | Risk of losing everything |
|
||||||
|
|
||||||
|
A table of pure Runners will win a chase slowly and cleanly. A table that leans
|
||||||
|
on Pressure will win it fast and leave a mess behind. **The quick way to catch
|
||||||
|
someone in this city is the expensive way.** That is the whole point.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. The round
|
||||||
|
|
||||||
|
Six rounds, maximum. Roughly fifteen minutes.
|
||||||
|
|
||||||
|
> **1. The GM reveals this round's obstacle.**
|
||||||
|
> **2. The prey declares their role and rolls.**
|
||||||
|
> **3. The pursuers declare in any order and roll.**
|
||||||
|
> **4. Work out the net. Move the LEAD.**
|
||||||
|
> **5. Stamp Collateral. Narrate. Next round.**
|
||||||
|
|
||||||
|
**The obstacle is revealed before anyone declares.** Nothing is hidden and
|
||||||
|
nothing is simultaneous — that is what removes the cards. Players choose their
|
||||||
|
role knowing what the street just threw at them, so the choice is a decision
|
||||||
|
instead of a guess.
|
||||||
|
|
||||||
|
### Working out the net
|
||||||
|
|
||||||
|
Each side totals up:
|
||||||
|
|
||||||
|
> the **highest single RUNNER result** on that side — *not the sum*
|
||||||
|
> **+** all PRESSURE successes
|
||||||
|
> **+** any banked CUTTER successes spent this round
|
||||||
|
|
||||||
|
Then:
|
||||||
|
|
||||||
|
> **NET = pursuer total − prey total**
|
||||||
|
> Positive → **LEAD goes down** by that much.
|
||||||
|
> Negative → **LEAD goes up** by that much.
|
||||||
|
> Zero → LEAD is unchanged. The round still happened; Collateral still lands.
|
||||||
|
|
||||||
|
Extra Runners make a side **more reliable, not more powerful.** A second Runner
|
||||||
|
is worth about half a success; a fourth is worth about a fifth of one. Four
|
||||||
|
people running down the same alley is four chances at a good roll, not four
|
||||||
|
times the speed.
|
||||||
|
|
||||||
|
### Running the other side
|
||||||
|
|
||||||
|
**An NPC makes one roll per round**, in a role of the GM's choosing. Do not roll
|
||||||
|
for a mob. If the opposition has a real advantage — a bike, local knowledge,
|
||||||
|
a head start through a door that locks — give them **one advantage die**, not
|
||||||
|
another roll.
|
||||||
|
|
||||||
|
This holds the GM's dice load at one roll per round whether there are two
|
||||||
|
players or six.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Gambits
|
||||||
|
|
||||||
|
**Once per chase, each participant may declare a Gambit** — a named, specific
|
||||||
|
risk. *"I go over the gap."* *"I take the spinner under the span."*
|
||||||
|
|
||||||
|
A Gambit modifies **your role roll for that round**. It is not an extra roll.
|
||||||
|
|
||||||
|
| | |
|
||||||
|
|---|---|
|
||||||
|
| **Roll** | At **disadvantage** — drop your lowest die. |
|
||||||
|
| **Success** | **Double your successes** this round. |
|
||||||
|
| **Failure** | You are **out of the chase**, with a consequence the terrain suggests, and you generate **1 Collateral**. |
|
||||||
|
|
||||||
|
A Gambit is the moment somebody talks about afterwards. It is capped at one
|
||||||
|
each so it stays that.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Collateral
|
||||||
|
|
||||||
|
Chases in this city cost the city. **Collateral is a track from 0 to 6.** It
|
||||||
|
never touches a character sheet — not Health, not Resolve, not Humanity.
|
||||||
|
|
||||||
|
**Generated by:**
|
||||||
|
|
||||||
|
- each **PRESSURE success** — 1 each
|
||||||
|
- each **failed Toll in crowded terrain** — 1 each
|
||||||
|
- each **failed Gambit** — 1 each
|
||||||
|
|
||||||
|
**Removed by:** SPOTTER successes, one for one, in the round it is generated.
|
||||||
|
|
||||||
|
**Capped at 6.** Beyond that, narrate it and stop counting.
|
||||||
|
|
||||||
|
### Spending it — after the chase, never during
|
||||||
|
|
||||||
|
| Collateral | What it costs you |
|
||||||
|
|---|---|
|
||||||
|
| **1–2** | Noise. Someone remembers your face. A witness who will talk to the wrong person. |
|
||||||
|
| **3–4** | A report gets filed. LAPD attention, a supervisor's question, or a fresh complication in the case. |
|
||||||
|
| **5–6** | A body, or a burned asset. Something you were going to need is gone. |
|
||||||
|
|
||||||
|
Collateral never modifies a roll and is never spent in the moment. It is a
|
||||||
|
budget the GM draws on in the scenes that follow — which is exactly what makes
|
||||||
|
players argue about whether to reach for Pressure.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. The Obstacle Ladder
|
||||||
|
|
||||||
|
**Six obstacles, written in advance, played in fixed order, one per round.**
|
||||||
|
This replaces both the obstacle deck and any table you would otherwise roll on.
|
||||||
|
|
||||||
|
Rung 1 is a nuisance. Rung 6 is a wall.
|
||||||
|
|
||||||
|
Each rung is one line carrying a **Toll** and a **Tilt**:
|
||||||
|
|
||||||
|
- **Toll** — a roll to avoid a penalty. Anyone the obstacle touches makes it.
|
||||||
|
- **Tilt** — which side the terrain favours, usually one advantage die.
|
||||||
|
|
||||||
|
```
|
||||||
|
[3] TRANSIT PLATFORM — Toll: Mobility or lose your roll this round.
|
||||||
|
Tilt: prey (+1 die). A crowd is the runner's friend.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rung 6 is always a Terminator
|
||||||
|
|
||||||
|
The last rung is a feature that **forces resolution**: a dead end, a rail line,
|
||||||
|
a roof edge, a checkpoint, a canal. The chase has to break one way or the other
|
||||||
|
against it. Write it that way.
|
||||||
|
|
||||||
|
### Why authored, not random
|
||||||
|
|
||||||
|
- Pacing is built in — the scene has a written shape and a written ending.
|
||||||
|
- The obstacles are about *this* market, *this* rooftop, *this* freight yard.
|
||||||
|
- No lookup, no randomiser, no shuffling.
|
||||||
|
- The GM knows what round four looks like before round one starts.
|
||||||
|
|
||||||
|
**Writing one takes about five minutes.** Pick a location, list six things in it
|
||||||
|
that would interrupt a person running flat out, order them from *annoying* to
|
||||||
|
*impassable*, and give each a Toll. Eight finished ladders are in
|
||||||
|
`reference/obstacle-ladders.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Ending it
|
||||||
|
|
||||||
|
The chase ends at **LEAD 0**, at **LEAD 8**, or at the **end of round 6** —
|
||||||
|
whichever comes first.
|
||||||
|
|
||||||
|
| LEAD | Outcome | |
|
||||||
|
|---|---|---|
|
||||||
|
| **0** | **ENGAGED** | Hands on them. Combat, restraint, or a conversation at contact range. |
|
||||||
|
| **1–2** | **CORNERED** | Trapped, not held. A standoff, a negotiation, or a jump nobody should survive. |
|
||||||
|
| **3–4** | **LOST, BUT** | Gone from sight — and you have a direction, a dropped thing, a plate, a witness. |
|
||||||
|
| **5–7** | **GONE** | Clean. The street still remembers, and so do you. |
|
||||||
|
| **8** | **GHOST** | You never had them. They were better than you, or they were never alone. |
|
||||||
|
|
||||||
|
**No outcome is a dead end.** LOST, BUT is the ordinary failure and it still
|
||||||
|
hands you the next scene. GONE and GHOST still leave you the Collateral, and
|
||||||
|
they leave you what the prey gave away by *how* they ran — the route they knew,
|
||||||
|
the door that was unlocked for them, the fact that they never once looked back.
|
||||||
|
|
||||||
|
### Overtime
|
||||||
|
|
||||||
|
If round 6 ends with the chase unresolved and the table is leaning in, the GM
|
||||||
|
may call **Overtime — at most three more rounds.**
|
||||||
|
|
||||||
|
- **No new obstacles.** The Terminator stands and applies every round.
|
||||||
|
- **LEAD drops by 1 at the start of each Overtime round**, before any rolls.
|
||||||
|
The pursuit closes. The city closes in.
|
||||||
|
- **Collateral generation is doubled.**
|
||||||
|
|
||||||
|
Overtime always ends: nine rounds is the hard ceiling, and the automatic drift
|
||||||
|
drags any close chase to ground. At the end of it, read the outcome table
|
||||||
|
whatever the number says.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Terrain
|
||||||
|
|
||||||
|
One engine, three skins. **The procedure never changes** — only this table.
|
||||||
|
|
||||||
|
| | RUNNER / CUTTER | SPOTTER | Start LEAD | Collateral |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| **Foot** | Mobility | Observation | 3–4 | Standard |
|
||||||
|
| **Ground vehicle** | Driving | Observation | 4 | **Doubled** — a car in a crowd |
|
||||||
|
| **Spinner / aerial** | Driving | Observation *(plotting vectors)* | 5 | **Halved**, but every point is severe |
|
||||||
|
|
||||||
|
**Mixed chases** — a spinner over a runner on foot, a car chasing a pedestrian
|
||||||
|
through an arcade — use each side's own layer for its own rolls, and the
|
||||||
|
**higher** of the two Collateral rates.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. If the PCs are the ones running
|
||||||
|
|
||||||
|
Nothing changes. The roles, the ladder, the net, and the outcome table are all
|
||||||
|
symmetric. Swap which side wants LEAD to go up.
|
||||||
|
|
||||||
|
The only thing to watch: **the outcome table reads from the pursuers' side.**
|
||||||
|
When the PCs are prey, invert it — LEAD 0 means *they caught you*, LEAD 8 means
|
||||||
|
*you are a ghost*, and LOST, BUT means you got away but left something behind
|
||||||
|
that they will follow.
|
||||||
|
|
||||||
|
Collateral still accrues to whoever generates it. A PC who shoots their way out
|
||||||
|
of a crowded transit hall owns that, even running.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Quick reference
|
||||||
|
|
||||||
|
**LEAD 0–8.** Start 3 close / 4 standard / 5 distant. 0 = caught, 8 = gone.
|
||||||
|
|
||||||
|
**Round:** obstacle → prey declares and rolls → pursuers declare and roll →
|
||||||
|
net → move LEAD → Collateral → narrate.
|
||||||
|
|
||||||
|
**Side total** = highest single Runner + all Pressure + banked Cutter spent.
|
||||||
|
**Net** = pursuers − prey.
|
||||||
|
|
||||||
|
| Role | Roll | Does |
|
||||||
|
|---|---|---|
|
||||||
|
| RUNNER | Mobility / Driving | Moves LEAD. Free. |
|
||||||
|
| PRESSURE | Firearms / Manipulation / Connections | Moves LEAD. 1 Collateral per success. |
|
||||||
|
| SPOTTER | Observation | Negate Toll / remove Collateral / grant advantage die / reveal next rung. |
|
||||||
|
| CUTTER | Mobility / Driving | Banks successes; spend all at once later. No successes = out a round, bank lost. |
|
||||||
|
|
||||||
|
**Gambit:** once each. Disadvantage. Win = double successes. Lose = out, +1 Collateral.
|
||||||
|
|
||||||
|
**Six rounds.** Six rungs. Rung 6 forces the end. Overtime = 3 more, −1 LEAD a
|
||||||
|
round, double Collateral.
|
||||||
|
|
||||||
|
**Outcomes:** 0 ENGAGED · 1–2 CORNERED · 3–4 LOST, BUT · 5–7 GONE · 8 GHOST.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Unofficial fan supplement. Not affiliated with, endorsed by, or sponsored by
|
||||||
|
Free League Publishing or the rights holders of Blade Runner. Requires the Blade
|
||||||
|
Runner RPG core rulebook to play. Not for sale.*
|
||||||
Reference in New Issue
Block a user