02-23-2026, 04:05 AM
BLINKY BUMPERS
Bumpers don't just watch the ball – they feel when it fails!
The whole machine has personality:
Happy when you're playing well,
Sad when you drain...
and ready to cheer up the moment you hit ENTER to try again
Your score = how long you stay in play
Inspired by Kevin's Pinball
https://www.naalaa.com/forum/thread-129.html
![[Image: bvKY5.gif]](https://s12.gifyu.com/images/bvKY5.gif)
Bumpers don't just watch the ball – they feel when it fails!
The whole machine has personality:
Happy when you're playing well,
Sad when you drain...
and ready to cheer up the moment you hit ENTER to try again
Your score = how long you stay in play
Inspired by Kevin's Pinball
https://www.naalaa.com/forum/thread-129.html
![[Image: bvKY5.gif]](https://s12.gifyu.com/images/bvKY5.gif)
Code:
'============================================================
'BLINKY BUMPERS
'
'Bumpers don't just watch the ball – they feel when it fails!
'The whole machine has personality:
' Happy when you're playing well,
' Sad when you drain...
' and ready to cheer up the moment you hit ENTER to try again
'
'Your score = how long you stay in play
'
'Inspired by Kevin's Pinball
'https://www.naalaa.com/forum/thread-129.html
'============================================================
#win32
set window "Blinky Bumpers", 700, 600
set redraw off
randomize time()
' sfx.Noise(duration, freq, vol)
include "sfx.n7"
sfx = SFX()
sfx.SetSampleRate(8000)
visible sfxSound= sfx.Noise(0.01,261.6*10, 50)
'createfont(<font name>[, <bold>[, <italic>[, <underline>[, <smooth>]]]])
arial20 = createfont("arial", 20, true, false, false, true)
set font arial20
' Constants
constant MIN_DIST_SQ = 100
constant MAX_PULL = 80
constant PLUNGER_X = 570
constant REST_Y = 410
constant DRAIN_Y = 495
constant ARC_CX = 350
constant ARC_CY = 350
constant ARC_R = 250
constant ARC_STEPS = 360
constant ANGLE_STEP = 0.0087 '= PI / ARC_STEPS
constant BUMPER_RADIUS = 50
constant EYE_MAX_OFFSET = 25
constant EYE_RADIUS = 20
constant SMILE_DEPTH = 50
constant SMILE_STEPS = 300
constant FRAME_TIME = 0.01
' Ball state
visible x = PLUNGER_X
visible y = REST_Y
visible vx = 0
visible vy = 0
visible started = false
visible drained = false
visible score = 0
' Plunger state
visible plungerY = REST_Y
visible spaceDown = false
visible prevSpaceDown = false
' Misc state
visible bumper4_happy = true
visible noseX = 350
'------------
' Main loop
'------------
while not keydown(KEY_ESCAPE, true)
prevSpaceDown = spaceDown
spaceDown = keydown(KEY_SPACE)
' Restart
if keydown(KEY_RETURN, true)
randomize time()
x = PLUNGER_X
y = REST_Y
vx = 0
vy = 0
started = false
drained = false
score = 0 ' Reset timer on restart
plungerY = REST_Y
bumper4_happy = true
noseX = 350 ' Reset nose to center
endif
' arrow keys
if keydown(KEY_LEFT) then noseX = max(130, noseX - 4)
if keydown(KEY_RIGHT) then noseX = min(570, noseX + 4)
if not started
plungerY = min(max(plungerY + spaceDown * 7 - 4, REST_Y), REST_Y + MAX_PULL)
y = plungerY
' Launch on release
if prevSpaceDown and not spaceDown and plungerY > REST_Y + 5
vy = -(plungerY - REST_Y) * 0.15
started = true
endif
x = PLUNGER_X
vx = 0
else
if not drained
' Ball movement
x = x + vx
y = y + vy
score = score + FRAME_TIME
if y >= DRAIN_Y and vy > 0
drained = true
bumper4_happy = false
endif
if not drained
handleCollisions()
endif
else
vx = 0
vy = 0
endif
endif
' Rendering
set color 0,0,0; cls
drawObstacles()
drawEyes()
drawPlunger()
drawBall()
' Infobox
set color 255, 255, 255
set caret width()/2,height()-20; center "Press SPACE BAR to launch"
set caret width()/2,520; center "SCORE: " + int(score * 10)
set caret 620,480 ; wln round((plungerY - REST_Y) / MAX_PULL * 100) + "%"
if drained
set caret 10,10
wln "Here's a little secret..."
wln "Use LEFT and RIGHT to move the nose...It's a paddle!"
wln
wln "ENTER to restart (and cheer up!)"
endif
redraw
fwait 120
wend
'-----------
' Functions
'-----------
function drawBall()
set color 255, 0, 0
draw ellipse x, y, 10, 10, true
endfunc
function drawPlunger()
set color 100, 100, 100
draw rect PLUNGER_X - 10, REST_Y, 20, 85, true
set color 255, 255, 255
draw rect PLUNGER_X - 10, plungerY + 10, 20, 10, true
endfunc
function drawEyes()
' Left bumper eye (225, 250)
dx = x - 225
dy = y - 250
dist = sqr(dx*dx + dy*dy)
if dist > 0.1
scale = EYE_MAX_OFFSET / dist
if scale > 1 then scale = 1
eye_x = 225 + dx * scale
eye_y = 250 + dy * scale
else
eye_x = 225
eye_y = 250
endif
set color 0, 0, 0
draw ellipse eye_x, eye_y, EYE_RADIUS, EYE_RADIUS, true
' Right bumper eye (475, 250)
dx = x - 475
dy = y - 250
dist = sqr(dx*dx + dy*dy)
if dist > 0.1
scale = EYE_MAX_OFFSET / dist
if scale > 1 then scale = 1
eye_x = 475 + dx * scale
eye_y = 250 + dy * scale
else
eye_x = 475
eye_y = 250
endif
set color 0, 0, 0
draw ellipse eye_x, eye_y, EYE_RADIUS, EYE_RADIUS, true
endfunc
function collideWithCircle(ox, oy)
dx = x - ox
dy = y - oy
if dx*dx + dy*dy >= MIN_DIST_SQ then return
dist = sqr(dx*dx + dy*dy)
if dist < 1 then dist = 1
x = x + dx / dist * 5
y = y + dy / dist * 5
vx = dx / dist * 2.5
vy = dy / dist * 2.5
play sound sfxSound
endfunc
function handleCollisions()
' Top arc
angle = 0
for i = 0 to ARC_STEPS
collideWithCircle(ARC_CX + ARC_R * cos(angle), ARC_CY - ARC_R * sin(angle))
angle = angle + ANGLE_STEP
next
' Side walls
for j = 320 to 500 step 10
collideWithCircle(100, j)
collideWithCircle(600, j)
next
' Bottom wall
hit_bottom = false
for i = 100 to 600 step 10
dx = x - i
dy = y - 500
if dx*dx + dy*dy < MIN_DIST_SQ
hit_bottom = true
endif
collideWithCircle(i, 500)
next
if hit_bottom
drained = true
bumper4_happy = false
endif
' Bumper 1 (x=130)
for j = 410 to 500 step 12
collideWithCircle(130, j)
next
' Bumper 2 (225, 250)
for i = 0 to 360 step 30
a = i * PI / 180
collideWithCircle(225 + 40 * cos(a), 250 + 40 * sin(a))
next
' Bumper 3 (475, 250)
for i = 0 to 360 step 30
a = i * PI / 180
collideWithCircle(475 + 40 * cos(a), 250 + 40 * sin(a))
next
' Bumper 4 - Mood Arc
for i = 0 to SMILE_STEPS
t = i / SMILE_STEPS
arc_x = 225 + t * 250
if bumper4_happy
arc_y = 410 + SMILE_DEPTH * sin(t * PI)
else
arc_y = 410 - SMILE_DEPTH * sin(t * PI)
endif
collideWithCircle(arc_x, arc_y)
next
' Bumper 5 - Nose
for i = noseX - 30 to noseX + 30 step 10
collideWithCircle(i, 350)
next
endfunc
function drawObstacles()
set color 255, 255, 255
' Top arc
angle = 0
for i = 0 to ARC_STEPS
ox = ARC_CX + ARC_R * cos(angle)
oy = ARC_CY - ARC_R * sin(angle)
draw ellipse ox, oy, 4, 4, true
angle = angle + ANGLE_STEP
next
' Bottom wall
for i = 100 to 600
draw ellipse i, 500, 4, 4, true
next
' Side walls
for j = 320 to 500
draw ellipse 100, j, 4, 4, true
draw ellipse 600, j, 4, 4, true
next
' Bumpers
for k = 410 to 500
draw ellipse 130, k, 4, 4, true
next
draw ellipse 225,250,50,50,true
draw ellipse 475,250,50,50,true
' Bumper 4 - Mood Arc
for i = 0 to SMILE_STEPS
t = i / SMILE_STEPS
arc_x = 225 + t * 250
if bumper4_happy
arc_y = 410 + SMILE_DEPTH * sin(t * PI)
else
arc_y = 410 - SMILE_DEPTH * sin(t * PI)
endif
draw ellipse arc_x, arc_y, 6, 6, true
next
' Bumper 5 - Nose
for j = noseX - 30 to noseX + 30
draw ellipse j, 350, 6, 6, true
next
endfunc

