How a game of Portal Pinball scores, which choices a player makes and what they change, and the
settings that shape a game. Every value here is a draft: calibrated 2026-10-09 against
simulated games (CHANGES #89), not yet against real play on the cabinet.
Where things live:
| What | Where |
|---|---|
| Points per shot | each feature mode's variable_player (modes/orbits, ramps, aerial, portal, dropbank, lanes, slings, skillshot; pops in modes/base) |
| Story awards, standups, Drp1 | modes/scoring/config/scoring.yaml |
| What every shot gives today, generated from the configs | design/shot-map.md, "Scoring today" |
| The calibration | tools/sim/scoring.py with tools/sim/playthrough.py |
| Operator settings | machinefolder/config/settings.yaml (MPF's service menu) |
| What each chapter asks for | design/rule-sheets.md |
Three layers, from small to large:
modes/scoring only listens to the story's own events (a test done, a jackpot...), so the story
modes themselves carry no scoring.
Per hit (feature modes, plus the standups and Drp1 in modes/scoring):
| Shot | Points | Shot | Points | |
|---|---|---|---|---|
| Left Orbit / Propulsion Run | 5,000 | Turret Gauntlet exit | 10,000 | |
| Excursion Funnel / Hard Light Bridge (ramps) | 6,000 | Drop target bank (all 3 down) | 6,000, +2,000 per repeat | |
| Orange Portal / Blue Portal | 4,000 / 5,000 | Super Button | 2,000 | |
| Aerial Faith Plate | 7,000 | Standup (any of 7) | 1,000 | |
| Incinerator | 6,000 | Drp1 (Rattmann's panel) | 3,000 | |
| Dropper lock (VUK6) | 3,000 | Skill shot (the lit top lane) | 5,000 |
Sets and combos:
| Set or combo | Points |
|---|---|
| Both orbits | 15,000 |
| Both ramps / the ramp combo | 15,000 / 10,000 |
| The portal sequence (dropper, Orange Portal, exit) | 25,000; each exit stage 5,000; all open 50,000 |
| The Incinerator finisher (Incinerator, then the Super Button) | 10,000 |
| Top lanes (LIE) / bottom lanes (CAKE) | 500 a letter, 5,000 a set |
| The sling combo | 2,500 |
| Pops / slings | 150 / 100 |
Base values, times the period's multiplier (multiplied out in scoring.yaml):
| Moment | Base | Period 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| A test done | 10,000 | 10,000 | 12,500 | 15,000 | 17,500 | 20,000 |
| A lock | 25,000 | 25,000 | 31,250 | 37,500 | 43,750 | 50,000 |
| A multiball start | 50,000 | 50,000 | 62,500 | 75,000 | 87,500 | 100,000 |
| A jackpot | 25,000 | 25,000 | 31,250 | 37,500 | 43,750 | 50,000 |
| A super jackpot | 100,000 | - | 125,000 | 150,000 | 175,000 | 200,000 |
| A chapter reached | 30,000 | 30,000 | 37,500 | 45,000 | 52,500 | 60,000 |
| A choice made (a made shot only: a time-out scores nothing, D33, owner 2026-10-24) | 50,000 | 50,000 | - | 75,000 | (in the ending) | - |
| The DECISIVE bonus (a held choice picked in its first 10 s, D33, owner 2026-10-24) | 50,000 | 50,000 | 62,500 | 75,000 | 87,500 | - |
| An ending | 100,000 | - | - | 150,000 | 175,000 | 200,000 |
What counts as what, per period:
modes/choice_window, scoring.yaml).arrows); Wheatley's spot 10,000, the singing turret spared 25,000, the laserstory_layers); period 2's Fine Print (the catch 12,500, the contract 25,000), thestory_p2_side, story_p2); Turret Air 15,000 a launch and Turretstory_p3_side).Outside the calibration (older feature-phase awards, not yet re-balanced, TODO): the
progression tiers 5,000 each (modes/progression), the super wizard 50,000 (modes/super_wizard),
the drop-bank multiball start 3,000 (modes/multiball).
End-of-ball bonus (modes/bonus): 100 per shot made that ball.
A player's choices shape the route and the ending; each choice made also scores (above).
| Choice | When | Options and what they change |
|---|---|---|
| C1 the cube | 1B, after the cube chain | Burn it (the Incinerator) or keep it (the Blue Portal). The cube comes along or is thrown after you in the endings. |
| C2 comply / defy | all of periods 1-3 (modes/story_meter) |
A meter filled by choices (D31): C1 burn +3 / keep -3, each Rattmann arrow made -1, tests 0. At the last core it lights the fitting shot - spare her (the Super Button) or burn it (the Incinerator, E8) - and the player's shot decides. Built today: tests +1, Drp1 -1, the meter decides on its own (phase 2 changes it). |
| C3 escape / dive deeper | after period 3's climax | Escape (the Turret Gauntlet exit): E1 with GLaDOS spared (the end), E2 with her destroyed (dragged back into period 4). Dive deeper (the Blue Portal): straight into period 4. Follow the arrow (period 5 first) waits for Rattmann's hints. |
| C4 the CAROLINE letters | from 1B to period 4's choice | The 8 major shots in a hidden order, one letter each, kept across balls. All 8 unlock E5. |
| C5 home / up | after period 4's super jackpot | Home with Cave (the Left Orbit): E3. Up to GLaDOS (the Hard Light Bridge): E4, or E5 with all 8 letters. Then period 5. |
The endings: E1 the surface; E2 dragged back; E3 home by the era dial; E4 Caroline
deleted; E5 she remembers; E6 the box opens (after period 5); E7 Wheatley in space
(not built); E8 GLaDOS destroyed. A game that ends inside a period plays that period's
small ending (modes/small_ending) and scores nothing extra. Not built yet: C0 (Wheatley),
C1b (Cave or Caroline at period 2's locks), the hints and the arrow route.
| Setting | Value | Where |
|---|---|---|
| Balls per game | 3 (MPF's default) | game: in the machine config (not set) |
| Players | 1-4 (MPF's default) | idem |
| Ball save | 15 / 10 / 8 / 5 s by difficulty level (8 s = Advanced Testing) from the launch (armed when the ball leaves the plunger lane, once per ball), 2 s grace, the saved ball relaunched | modes/base, ball_saves: ball_save_start |
| Multiball shoot-again | 10 s on every story multiball | each period's multiballs: |
| Extra balls | none awarded yet | - |
| Tilt | none yet (no tilt switch) | TODO.md "Tilt" |
| Match | MPF's match mode at game end | modes: list, match |
| High scores | MPF's high-score mode; the PA comments when the initials go in (with PA taunts on) | modes/high_score |
| Machine statistics | endings found, cubes kept or burned, comply / defy, turrets, games... (the attract's stat pages) | config/machine-stats.yaml |
Operator settings (config/settings.yaml, MPF's service menu):
| Setting | Options | Default |
|---|---|---|
| Attract PA | Off; A1-A6 (welcome styles and intervals) | A4 after the nines, every 10 min |
| Non-employee welcome | Off; B1-B3 | B3 (with the tribe taunt) |
| Multiball start PA | Off; Probably lethal; Smooth jazz | Probably lethal |
| PA taunts | Off / On | On |
| Attract callouts | Off; Rare; Normal | Normal |
Chamber sign (sign_style) |
Full; number, title, big icons; like the games (number, icons); story lines, no test list (design/SCREENS.md) |
Number, title, big icons |
Testing protocol (difficulty) |
Bring Your Daughter to Work Day; Standard Testing; Advanced Testing; Military Android (section 6) | Bring Your Daughter to Work Day |
climax_choice_time, planned, D31) |
replaced by the choice window per level (T7, D33) | - |
Not settings yet (values in config, to become settings once tuned): the scoring values. The
story's counts and timers and the ball save follow the difficulty level (section 6).
The operator setting difficulty (owner, 2026-10-10, from IDEAS.md: "a more beginner friendly
device, so less rejects and less specific needs for an order of shots ... 4 levels"; proposed,
walked through and built the same day, CHANGES #97-#99). It turns four dials - the gates, the
counts, the timers and the rejects - and never the story order (the cube chain, the waves, the
climax) or a choice (C0-C5: a choice is a story decision, not a skill test) - only how long a choice waits (T7, D33). 3A keeps its timed
tests 3 at a time even on level 0: six neurotoxin clocks at once is chaos, not easy. Points are the same at every level. The level shows once, at game start, on 1A's
card: "TESTING PROTOCOL: ".
| Level | Name | Feel |
|---|---|---|
| 0 (default) | Bring Your Daughter to Work Day | every optional gate open, about half the counts, double the clocks, no rejects |
| 1 | Standard Testing | tests in parallel, the lock still the reward, a bit easier and slower |
| 2 | Advanced Testing | the build before the setting: today's gates, counts, clocks and rejects |
| 3 | Military Android | the rule sheet to the letter, higher counts, shorter clocks, more rejects |
How it's built: the difficulty mode (modes/difficulty) starts with every ball and copies
the level's value of each count and clock into a player variable (dl_*, declared in
config/difficulty.yaml); the story's counters and timers and the ball save read those, and the
chamber signs show them ("BOUNCE THE PELLET 15 TIMES"). So one level's numbers live in one file.
The unit tests run the story at Advanced Testing and tests/test_difficulty.py checks every
level; the playtests run at Advanced Testing unless --setting difficulty=N.
Built so far: the counts (C1-C9, C11), the clocks T1-T3 and the ball save T6, the start
card's line (CHANGES #97); the gates G1, G2, G4, G5 and G9 (#98); the rejects R1 and R3 (#99).
An opened lock (G4, G5) is parallel, not skippable: the chapter still waits for both parts (an
accrual). Not per level: R2 and R6 are already the gentle version at every level; R4, T4 and
T5 wait for their features; C10 stays 6 at every level (the dial's six decades are its art; G9
makes it easier instead).
Update these tables in the same change as any count, timer, gate or reject, and fill the
"Measured" column from cabinet play. The columns: Beginner = level 0, Normal = 1, Hard = 2,
Expert = 3.
| # | Where | Gate | Kind | Today | Beginner | Normal | Hard | Expert |
|---|---|---|---|---|---|---|---|---|
| G1 | 1A | Left Orbit test waits until another test is done (3 lit at a time) | presentation | gated | open | open | gated | gated |
| G2 | 1B | 6 tests, 3 lit at a time (rule sheet); each finished test opens the next | presentation | all 6 lit | all lit | all lit | all lit | 3 at a time |
| G3 | 3A | timed tests 3 at a time | presentation | 3 at a time | 3 at a time (the clocks) | 3 at a time | 3 at a time | 3 at a time |
| G4 | 1C | turret bank and exit wait for both locks | payoff | gated | open (any order; the escape waits for all 3) | gated | gated | gated |
| G5 | 2A/2B/2C | each decade's lock waits for its gel test | payoff | gated | open (any order; the decade waits for both) | gated | gated | gated |
| G6 | 1B | the cube chain: dropper -> Super Button (the cube drops) -> burn/keep | hardware + story | in order | in order | in order | in order | in order |
| G7 | 3B | waves 1 -> 2 -> 3, then the tour, then the super jackpot | story | in order | in order | in order | in order | in order |
| G8 | climax | rocket -> core falls -> burn, 4 times | story | in order | in order | in order | in order | in order |
| G9 | 4 | the era dial: 6 shots one at a time (a Faith Plate hit lights the next too) | the mode | plate helps | two lit (the plate's help always on) | plate helps | plate helps | no plate help |
| # | Where | What | Built in | Today | Beginner | Normal | Hard | Expert | Measured |
|---|---|---|---|---|---|---|---|---|---|
| C1 | 1A | tests needed of 4 | story_p1.yaml |
3 | 2 | 3 | 3 | 4 | |
| C2 | 1B | pop hits (pellets) | story_p1.yaml |
25 | 15 | 20 | 25 | 35 | |
| C3 | 1B | Propulsion Run / Excursion Funnel | story_p1.yaml |
2 each | 1 | 2 | 2 | 3 | |
| C4 | 1B | lit turrets (standups) | story_p1.yaml |
3 | 2 | 3 | 3 | 4 | |
| C5 | 2A | pop and sling hits | story_p2.yaml |
30 | 15 | 20 | 30 | 40 | |
| C6 | 2B | orbit shots | story_p2.yaml |
6 | 3 | 4 | 6 | 8 | |
| C7 | 2C | portal transfers | story_p2.yaml |
4 | 2 | 3 | 4 | 5 | |
| C8 | 2C | lemon jackpots to light the super | story_p2.yaml |
5 | 3 | 4 | 5 | 6 | |
| C9 | 3A | timed tests to light Her Cores | story_p3.yaml |
4 | 2 | 3 | 4 | 5 | |
| C10 | 4 | dial clicks to light the door | story_p4.yaml |
6 | 6 | 6 | 6 | 6 | |
| C11 | 4 | ride jackpots to light the super | story_p4.yaml |
5 | 3 | 4 | 5 | 6 |
A live-play window (a shot to make before it closes) is 20 s at least, on every level (owner, 2026-11-02).
| # | Where | What | Built in | Today | Beginner | Normal | Hard | Expert | Measured |
|---|---|---|---|---|---|---|---|---|---|
| T1 | 3A | neurotoxin clock per timed test | story_p3.yaml t3a_*_clock |
30 s | 60 s | 45 s | 30 s | 20 s | |
| T2 | 3B | defend window after a wave | p3b_defend_clock |
25 s | 35 s | 30 s | 25 s | 20 s | |
| T3 | climax | rocket: a major shot after Drp1 | p3c_rocket_clock |
25 s | 35 s | 30 s | 25 s | 20 s | |
| T4 | 1C | escape hatch once the bank is down | rule sheet (not built as a timer yet) | ~20 s | none | 30 s | 20 s | 20 s | |
| T5 | 2C end | red phone standup | p2_phone_clock |
20 s | 20 s | 20 s | 20 s | 20 s | |
| T6 | all | ball save | base.yaml |
8 s | 15 s | 10 s | 8 s | 5 s | |
| T7 | every held choice | the choice window: 10 s decisive (the DECISIVE bonus, section 3), then the grace; C1 and LEAVE FOR GOOD keep their own (D33) | not built | 40 s (the last core) | never times out (nag every 20 s) | 30 s | 20 s | 15 s |
| # | Where | What happens today | Beginner | Normal | Hard | Expert |
|---|---|---|---|---|---|---|
| R1 | climax | the first core is refused once (2 Incinerator hits; the rule sheet says 1-2) | never | once | once | every core once |
| R2 | 3A | a timed-out test stays lit, its clock restarts (the rule sheet's "back in the backlog" isn't built) | the same | the same | the same | the same |
| R3 | 3B | a defend time-out resets the current wave | wave stays down, the defend clock restarts | resets the wave | resets the wave | resets the wave |
| R4 | 1C | the hatch closes: the turrets redeploy (not built, see T4) | no hatch clock | redeploy | redeploy | redeploy |
| R5 | 1B | C1: only a made shot counts | unchanged (it's a choice, not a skill gate) | |||
| R6 | 3A drain | the rule sheet's "resumes with at least 20 s" isn't built: a test's clock restarts in full on the next ball, at every level |
tools/sim/scoring.py runs the scoring table against 2,000 simulated games per skill level
(tools/sim/playthrough.py's model: shot rates, aim and ball times per skill, drafts until the
cabinet measures them) and checks three targets:
| Target | Result (2026-10-18, the cube chain without the pedestal) |
|---|---|
| The story pays most: 55-75% of an average game | 68% |
| Toys stay small: pops, slings and lanes at most 15% for every skill | 14% (beginner) |
| Skill counts: expert median / beginner median 8-40x | 35x |
Typical games: a beginner ~180,000 (4.5 min), an average player ~720,000 (9 min), an expert ~6.3
million (26 min, often to E6). Tried and dropped: story awards x the period number 1-5 (the story
took 95% of an expert's score, the spread 79x); x1-3 with half the shot values (spread 45x).
Re-checking:
tools/sim/scoring.py and run.venv/Scripts/python tools/sim/scoring.py (exit code 0 = all targets pass).variable_player, ormodes/scoring/config/scoring.yaml (base x the period's multiplier).tools/story/shot_map.py and the unit tests (several assert point values).SKILLS intools/sim/playthrough.py and check the targets again.design/research/story-chain/decided-options.md, CHANGES #102), with the