151 lines
9.1 KiB
Python
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)
|