Cite as
pret/pokepinball
Original
https://github.com/pret/pokepinball
Pinned
pret/pokepinball @ 0a6f6dca71548a726e6808250b303d9e97a8e2f8
Accessed
2026-10-22
Licence
none stated (no LICENSE file; GitHub reports no license)
Credit
The pret community, 16 GitHub contributors (2026-10-22), led in commit count by huderlem, PikalaxALT, chaos-lord, rayrobdod, and dannye. See https://github.com/pret/pokepinball/graphs/contributors. The labels and comments quoted in claims (e.g. "gravity added to y velocity every frame") are theirs.
Notes
Builds the English Game Boy Color ROM, "Pokemon Pinball (U) [C][!].gb", sha1 9402014d14969432142abfde728c6f1a10ee4dac (README, rom.sha1). That hash matches No-Intro's "Pokemon Pinball (USA, Australia) (Rumble Version) (SGB Enhanced) (GB Compatible)", the US and Australian release, not the Japanese original or the European builds. The atlas fetches only the assembly source, the README, and rom.sha1; the collision masks, flipper tables, and tilt tables are binary files it doesn't fetch. Line numbers in claims refer to the pinned commit.

Cited on

Claims (12)

In the US and Australian Pokémon Pinball, the main loop runs the game's screen logic once per frame. During play that logic adds gravity, caps the velocity, handles the tilts and, on the lower half of a table, the flippers. It then tests for collisions, bounces the ball if one touches, and moves the ball last.home.asm L2114–2120engine/pinball_game.asm L69–150Code
Gravity adds 11/256 of a pixel per frame to the ball's downward speed every frame while gravity is on. The ball's position and velocity are both 8.8 fixed point: whole pixels in the high byte, 256ths in the low.home.asm L2377–2392wram.asm L271–288Code
Each axis of the ball's velocity is capped on its own: a high byte above 7 becomes 7 and one below −7 becomes −7, the fraction kept, so the caps are 7 255/256 px per frame down or right and −7 up or left (computed), and a diagonal ball goes faster than a straight one. The move is limited again: a velocity of 5 px per frame or more moves the ball 4 255/256 px that frame, and one below −4 moves it 4 255/256 px the other way.home.asm L2394–2415home.asm L2436–2458Code
To find a wall, the game tests 16 points around the ball against 1-bit collision masks for the tiles under it. The first and last points of the longest run of touching points index a 256-entry table, CollisionForceAngles, for the surface's normal angle, in 256ths of a turn. Two more tables with the same index push the ball back out of the wall.home.asm L2735home.asm L2878–2922home.asm L2345–2357data/collision/collision_deltas.asm L1–3Code
On a hit, the game rotates the ball's velocity so the surface's normal points up. A ball moving away is left alone. A ball moving in bounces back at a quarter of its speed into the surface, plus an eighth for each step of the hit's amplification. Its speed along the surface gains half the stored spin, in 256ths of a pixel per frame; the new spin is that speed × 4 / 256. The velocity is rotated back. A speed of 3 px per frame or more into the surface also sets the rumble's duration to 1 and, unless a flipper was hit, plays a sound.engine/pinball_game.asm L101–148home.asm L2528–2625Code
Each flipper's position runs from 0 to 15 in 8.8 fixed point. Holding its button adds 0x0333 (3.2 positions) each frame and releasing it subtracts 0x0333, so a flipper swings fully up in 5 frames and fully down in 5 (computed). While the flippers are disabled, a held button counts as released. A flipper resting at either end records no change.engine/pinball_game/flippers.asm L14–76Code
Flipper collisions come from precomputed tables. For each flipper position from last frame's to this frame's, a 48 × 32-pixel grid gives the contact's distance from the pivot and the surface normal there; the right flipper reads the left's grid mirrored. A hit bounces the ball with amplification 1, then pushes it out along the normal by FlipperRadiusMagnitudes at that distance times 4 × the flipper's change this frame. A still flipper adds no push, and one moving down adds none either. The push at the tip of a rising flipper is 12.6 px per frame (computed); next frame's cap holds each axis to under 8.engine/pinball_game/flippers.asm L202–297engine/pinball_game/flippers.asm L167–171engine/pinball_game/flippers.asm L622–673engine/pinball_game/flippers.asm L423–430engine/pinball_game/flippers.asm L800–833engine/pinball_game.asm L115–125Code
Pokémon Pinball's tilt is a nudge with no tilt-out: the tilt routines keep no warning count and never end a ball. Holding a tilt button (left, right, or up; D-pad Down, B, and Select by default) runs its counter and screen shake for 3 frames (4 for up), and on each of those frames moves the ball 1 pixel if the ball is visible and gravity and tilt are on. That tilt then waits until the button is released and its counter has run back to 0. While a tilt is pushing, a ball touching a surface gets a velocity from a table for that tilt, read by the normal angle. Left and right together cancel.home/tilt.asm L1–240engine/pinball_game.asm L101–104engine/options_screen.asm L1080–1087Code
In Catch 'Em mode on the Red field, each Voltorb hit flips 4 of the billboard's 24 tiles and scores 100,000, so 6 hits reveal the Pokémon from a blank board (computed). The Pokémon appears once all 24 are flipped and the ball is on the table's lower half.engine/pinball_game/catchem_mode/catchem_mode_red_field.asm L231–264engine/pinball_game/catchem_mode/catchem_mode_red_field.asm L28–34Code
Once the Pokémon appears, each registered hit scores 300,000 and steps a counter from 0 to 3, lighting part of the CATCH bar; a hit with the counter at 3 catches it, so the fourth registered hit catches. Mew counts a hit only when a separate byte of its own rolls over at 256, so it would need 1,024 hits from a zero byte (computed). When the timer runs out on Mew, the game marks Mew caught in the Pokédex anyway.engine/pinball_game/catchem_mode/catchem_mode_red_field.asm L96–148engine/pinball_game/catchem_mode/catchem_mode_blue_field.asm L99–120engine/pinball_game/catchem_mode.asm L866–888engine/pinball_game/catchem_mode/catchem_mode_red_field.asm L212–227Code
Every Pokémon gets the same 2 minutes to be caught: all 151 entries of CatchEmTimerData are 0 seconds and 2 minutes. Starting the mode also starts a 60-second ball saver that saves without limit, and the saver from before is restored when the mode ends.data/catchem_timer_values.asm L1–157engine/pinball_game/catchem_mode.asm L118–131engine/pinball_game/ball_saver/ball_saver_catchem_mode.asm L1–32Code
Each table starts with a 20-second ball saver, and every ball lost for good starts a new one. While it runs, a drained ball is saved: the first save leaves the timer alone, and the second cuts it to 1 second and 1 frame. The timer counts down 60 frames a second, and not while the ball is at x 154 or more. Set to 20 seconds, it lasts 1,259 frames, about 21 seconds, if the ball stays left of x 154 throughout (computed). The slot cave can award 30, 60, or 90 seconds instead.engine/pinball_game/ball_saver/ball_saver_20.asm L1–12engine/pinball_game/ball_loss/ball_loss_red_field.asm L1–31engine/pinball_game/object_collision/red_stage_resolve_collision.asm L53–75engine/pinball_game/stage_init/init_red_field.asm L43engine/pinball_game/slot.asm L161–164Code