Portrait 360x480 table with regular-sized flippers
Rotate the playfield to portrait like a real pinball machine: - canvas 480x360 landscape -> 360x480 portrait; playfield 308px wide, ~390px tall, with HUD, title/game-over screens and HUD re-laid out for the narrow format (score/best top row, balls/stars/fever below) - flippers reduced to a regular ~quarter-width size (82px) pivoting at the lower corners, tips meeting over a wide centre drain - table elements re-placed for the taller field: 3 star lanes up top, candy bumpers mid-field, slingshots and floor-level outlane guard posts at the bottom; data-checked with the autoplay bot (~350 bumper hits / 30k frames, centre+side drains, no stuck balls) - probes & autoplay policy updated for the portrait geometry; all 13 pixel-probes pass
This commit is contained in:
+32
-27
@@ -1,60 +1,65 @@
|
||||
"""Pinball physics: pure logic, no rendering. Testable headless.
|
||||
|
||||
Coordinate system: y grows downward. 480x360 virtual canvas.
|
||||
Coordinate system: y grows downward. 360x480 portrait virtual canvas.
|
||||
"""
|
||||
|
||||
import math
|
||||
import random
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Playfield geometry
|
||||
# Playfield geometry (portrait - like a proper pinball machine)
|
||||
# ---------------------------------------------------------------------------
|
||||
CV_W, CV_H = 480, 360
|
||||
PLAY_L, PLAY_R, PLAY_T, PLAY_B = 20.0, 460.0, 52.0, 346.0
|
||||
CV_W, CV_H = 360, 480
|
||||
PLAY_L, PLAY_R, PLAY_T, PLAY_B = 26.0, 334.0, 60.0, 448.0
|
||||
|
||||
GRAVITY = 0.24 # px/frame^2
|
||||
MAX_SPEED = 13.0 # speed cap (prevents tunneling; ~300px of rise)
|
||||
RESTITUTION = 0.42
|
||||
BALL_R = 7.0
|
||||
|
||||
FLIP_LEN = 154.0
|
||||
FLIP_REST = math.radians(6.0) # tips slightly lowered toward center drain
|
||||
FLIP_UP = -math.radians(52.0) # fired position
|
||||
FLIP_PIVOT_L = (62.0, 338.0)
|
||||
FLIP_PIVOT_R = (418.0, 338.0)
|
||||
FLIP_RADIUS = 5.5
|
||||
# regular-sized flippers (about a quarter of the playfield width each),
|
||||
# pivoting near the lower corners with their tips meeting at the centre
|
||||
FLIP_LEN = 82.0
|
||||
FLIP_REST = math.radians(5.0) # tips slightly lowered toward the drain
|
||||
FLIP_UP = -math.radians(48.0) # fired position
|
||||
FLIP_PIVOT_L = (78.0, 430.0)
|
||||
FLIP_PIVOT_R = (282.0, 430.0)
|
||||
FLIP_RADIUS = 5.0
|
||||
FLIP_SNAP_UP = 0.36 # rad/frame while firing
|
||||
FLIP_SNAP_DOWN = 0.05 # gentle return speed
|
||||
FLIP_BOUNCE = 0.12
|
||||
|
||||
# elements
|
||||
BUMPERS = [
|
||||
dict(pos=(150.0, 180.0), r=23.0, color="pink"),
|
||||
dict(pos=(330.0, 180.0), r=23.0, color="mint"),
|
||||
dict(pos=(240.0, 240.0), r=23.0, color="lemon"),
|
||||
dict(pos=(102.0, 205.0), r=22.0, color="pink"),
|
||||
dict(pos=(258.0, 205.0), r=22.0, color="mint"),
|
||||
dict(pos=(180.0, 300.0), r=22.0, color="lemon"),
|
||||
]
|
||||
SLING_KICK_L = (0.60, -0.80)
|
||||
SLING_KICK_R = (-0.60, -0.80)
|
||||
SLINGS = [
|
||||
# upper slingshots just below the two outer bumpers - kick toward centre
|
||||
dict(pos=(150.0, 282.0), r=11.0, kick=SLING_KICK_L),
|
||||
dict(pos=(330.0, 282.0), r=11.0, kick=SLING_KICK_R),
|
||||
# guard posts at the outlane mouths: wall-huggers get bounced back into
|
||||
# play instead of silently draining - the centre gap is the real risk
|
||||
dict(pos=(44.0, 314.0), r=12.0, kick=(0.80, -0.60)),
|
||||
dict(pos=(436.0, 314.0), r=12.0, kick=(-0.80, -0.60)),
|
||||
# slingshots in the lower side areas - kick toward the centre
|
||||
dict(pos=(92.0, 336.0), r=11.0, kick=SLING_KICK_L),
|
||||
dict(pos=(268.0, 336.0), r=11.0, kick=SLING_KICK_R),
|
||||
# guard posts above the outlane mouths: wall-huggers bounce back into
|
||||
# play - the centre gap is the real risk
|
||||
dict(pos=(52.0, 408.0), r=13.0, kick=(0.80, -0.60)),
|
||||
dict(pos=(308.0, 408.0), r=13.0, kick=(-0.80, -0.60)),
|
||||
# little floor-level posts that seal the outlane mouths
|
||||
dict(pos=(46.0, 432.0), r=9.0, kick=(0.72, -0.69)),
|
||||
dict(pos=(314.0, 432.0), r=9.0, kick=(-0.72, -0.69)),
|
||||
]
|
||||
STARS = [(168.0, 92.0), (240.0, 92.0), (312.0, 92.0)]
|
||||
STARS = [(120.0, 96.0), (180.0, 96.0), (240.0, 96.0)]
|
||||
|
||||
CANNON = (440.0, 152.0)
|
||||
CANNON = (300.0, 170.0)
|
||||
CANNON_R = 17.0
|
||||
LAUNCH_DIR = (-0.94, -0.34)
|
||||
LAUNCH_DIR = (-0.93, -0.36)
|
||||
LAUNCH_MIN, LAUNCH_MAX = 7.0, 13.5
|
||||
|
||||
DRAIN_Y = 362.0
|
||||
CENTER_HOLE = (200.0, 280.0)
|
||||
OUTLANE_L = 46.0
|
||||
OUTLANE_R = 434.0
|
||||
DRAIN_Y = 458.0
|
||||
CENTER_HOLE = (150.0, 210.0)
|
||||
OUTLANE_L = 44.0
|
||||
OUTLANE_R = 316.0
|
||||
|
||||
|
||||
def clamp(v, lo, hi):
|
||||
|
||||
Reference in New Issue
Block a user