crashtest-r0ket/tools/game/py-pong/pypong/__init__.py

151 lines
9.1 KiB
Python

import pygame, math, random, entity
def load_image(path):
surface = pygame.image.load(path)
surface.convert()
pygame.surfarray.pixels3d(surface)[:,:,0:1:] = 0
return surface
def line_line_intersect(x1, y1, x2, y2, x3, y3, x4, y4):
# Taken from http://paulbourke.net/geometry/lineline2d/
# Denominator for ua and ub are the same, so store this calculation
d = float((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1))
# n_a and n_b are calculated as seperate values for readability
n_a = float((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3))
n_b = float((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3))
# Make sure there is not a division by zero - this also indicates that
# the lines are parallel.
# If n_a and n_b were both equal to zero the lines would be on top of each
# other (coincidental). This check is not done because it is not
# necessary for this implementation (the parallel check accounts for this).
if d == 0:
return False
# Calculate the intermediate fractional point that the lines potentially intersect.
ua = n_a / d
ub = n_b / d
# The fractional point will be between 0 and 1 inclusive if the lines
# intersect. If the fractional calculation is larger than 1 or smaller
# than 0 the lines would need to be longer to intersect.
if ua >= 0. and ua <= 1. and ub >= 0. and ub <= 1.:
return [x1 + (ua * (x2 - x1)), y1 + (ua * (y2 - y1))]
return False
class Game(object):
def __init__(self, player_left, player_right, configuration):
self.player_left = player_left
self.player_right = player_right
self.configuration = configuration
self.background = pygame.Surface(configuration['screen_size'])
self.sprites = pygame.sprite.OrderedUpdates()
line = entity.Line(load_image(configuration['line_image']), self.sprites)
line.rect.topleft = ((configuration['screen_size'][0]-line.rect.width)/2, 0)
paddle_image = load_image(configuration['paddle_image'])
self.paddle_left = entity.Paddle(configuration['paddle_velocity'], paddle_image, configuration['paddle_bounds'], self.sprites)
self.paddle_right = entity.Paddle(configuration['paddle_velocity'], paddle_image, configuration['paddle_bounds'], self.sprites)
self.paddle_left.rect.topleft = (self.configuration['paddle_left_position'], (self.configuration['screen_size'][1]-self.paddle_left.rect.height)/2)
self.paddle_right.rect.topleft = (self.configuration['paddle_right_position'], (self.configuration['screen_size'][1]-self.paddle_left.rect.height)/2)
digit_images = [load_image(configuration['digit_image'] % n) for n in xrange(10)]
self.score_left = entity.Score(digit_images, self.sprites)
self.score_left.rect.topleft = configuration['score_left_position']
self.score_right = entity.Score(digit_images, self.sprites)
self.score_right.rect.topleft = configuration['score_right_position']
ball_image = load_image(configuration['ball_image'])
self.ball = entity.Ball(self.configuration['ball_velocity'], ball_image, self.sprites)
self.bounds = pygame.Rect(20, 0, configuration['screen_size'][0]-ball_image.get_width()-20, configuration['screen_size'][1]-ball_image.get_height())
self.sound_missed = pygame.mixer.Sound(configuration['sound_missed'])
self.sound_paddle = pygame.mixer.Sound(configuration['sound_paddle'])
self.sound_wall = pygame.mixer.Sound(configuration['sound_wall'])
self.reset_game(random.random()<0.5)
self.running = True
def play_sound(self, sound):
if self.configuration['sound']:
sound.play()
def reset_game(self, serveLeft=True):
y = self.configuration['screen_size'][1] - self.ball.rect.height
self.ball.position_x = (self.configuration['screen_size'][0]-self.ball.rect.width)/2.0
self.ball.position_y = y * random.random()
self.ball.velocity = self.configuration['ball_velocity']
a = random.random() * math.pi / 2. - math.pi / 4.
self.ball.velocity_vec[0] = self.ball.velocity * math.cos(a)
self.ball.velocity_vec[1] = self.ball.velocity * math.sin(a)
if random.random() < 0.5:
self.ball.velocity_vec[1] = -self.ball.velocity_vec[1]
if serveLeft:
self.ball.velocity_vec[0] *= -1
def update(self):
# Store previous ball position for line-line intersect test later
ball_position_x = self.ball.position_x
ball_position_y = self.ball.position_y
# Update sprites and players
self.sprites.update()
self.player_left.update(self.paddle_left, self)
self.player_right.update(self.paddle_right, self)
# Paddle collision check. Could probably just do a line-line intersect but I think I prefer having the pixel-pefect result of a rect-rect intersect test as well.
if self.ball.rect.x < self.bounds.centerx:
# Left side bullet-through-paper check on ball and paddle
if self.ball.velocity_vec[0] < 0:
intersect_point = line_line_intersect(
self.paddle_left.rect.right, self.paddle_left.rect.top,
self.paddle_left.rect.right, self.paddle_left.rect.bottom,
ball_position_x-self.ball.rect.width/2, ball_position_y+self.ball.rect.height/2,
self.ball.position_x-self.ball.rect.width/2, self.ball.position_y+self.ball.rect.height/2
)
if intersect_point:
self.ball.position_y = intersect_point[1]-self.ball.rect.height/2
if intersect_point or (self.paddle_left.rect.colliderect(self.ball.rect) and self.ball.rect.right > self.paddle_left.rect.right):
self.ball.position_x = self.paddle_left.rect.right
velocity = self.paddle_left.calculate_bounce(min(1,max(0,(self.ball.rect.centery - self.paddle_left.rect.y)/float(self.paddle_left.rect.height))))
self.ball.velocity = min(self.configuration['ball_velocity_max'], self.ball.velocity * self.configuration['ball_velocity_bounce_multiplier'])
self.ball.velocity_vec[0] = velocity[0] * self.ball.velocity
self.ball.velocity_vec[1] = velocity[1] * self.ball.velocity
self.player_left.hit()
self.play_sound(self.sound_paddle)
else:
# Right side bullet-through-paper check on ball and paddle.
if self.ball.velocity_vec[0] > 0:
intersect_point = line_line_intersect(
self.paddle_right.rect.left, self.paddle_right.rect.top,
self.paddle_right.rect.left, self.paddle_right.rect.bottom,
ball_position_x-self.ball.rect.width/2, ball_position_y+self.ball.rect.height/2,
self.ball.position_x-self.ball.rect.width/2, self.ball.position_y+self.ball.rect.height/2
)
if intersect_point:
self.ball.position_y = intersect_point[1]-self.ball.rect.height/2
if intersect_point or (self.paddle_right.rect.colliderect(self.ball.rect) and self.ball.rect.x < self.paddle_right.rect.x):
self.ball.position_x = self.paddle_right.rect.x - self.ball.rect.width
velocity = self.paddle_right.calculate_bounce(min(1,max(0,(self.ball.rect.centery - self.paddle_right.rect.y)/float(self.paddle_right.rect.height))))
self.ball.velocity = min(self.configuration['ball_velocity_max'], self.ball.velocity * self.configuration['ball_velocity_bounce_multiplier'])
self.ball.velocity_vec[0] = -velocity[0] * self.ball.velocity
self.ball.velocity_vec[1] = velocity[1] * self.ball.velocity
self.player_right.hit()
self.play_sound(self.sound_paddle)
# Bounds collision check
if self.ball.rect.y < self.bounds.top:
self.ball.position_y = float(self.bounds.top)
self.ball.velocity_vec[1] = -self.ball.velocity_vec[1]
self.play_sound(self.sound_wall)
elif self.ball.rect.y > self.bounds.bottom:
self.ball.position_y = float(self.bounds.bottom)
self.ball.velocity_vec[1] = -self.ball.velocity_vec[1]
self.play_sound(self.sound_wall)
# Check the ball is still in play
if self.ball.rect.x < self.bounds.x:
self.player_left.lost()
self.player_right.won()
self.score_right.score += 1
self.reset_game(False)
self.play_sound(self.sound_missed)
if self.ball.rect.x > self.bounds.right:
self.player_left.won()
self.player_right.lost()
self.score_left.score += 1
self.reset_game(True)
self.play_sound(self.sound_missed)
def draw(self, display_surface):
self.sprites.clear(display_surface, self.background)
return self.sprites.draw(display_surface)