Beispiel #1
0
    def __init__(self, game, position, speed=15):
        """Initialise the laser bullets.

        Args:
            game:
                The running Game instance.
            position:
                The position the bullet starts from.
            speed:
                Optional speed at which the bullet travels. Default is 15
                pixels per frame.
        """
        super().__init__()
        # Load the bullet and its rect.
        self.image, self.rect = load_png('laser_bullet')

        self._game = game
        self._position = position
        self._speed = speed

        # The area within which the bullet is travelling.
        screen = pygame.display.get_surface()
        self._area = screen.get_rect()

        # Whether the bullet is visible.
        # It may not be visible if it went off screen without hitting a brick,
        # or if it hit a brick and was destroyed as a result.
        self.visible = False
Beispiel #2
0
    def __init__(self, start_pos, start_angle, base_speed, top_speed=15,
                 normalisation_rate=0.02,
                 off_screen_callback=None):
        """
        Initialise a new Ball with the given arguments.

        If supplied, the off_screen_callback will be invoked whenever the
        ball leaves the screen. This callable takes a single argument: the
        ball sprite instance.

        Args:
            start_pos:
                The starting coordinates of the ball. A 2 element sequence.
            start_angle:
                The starting angle of the ball in radians.
            base_speed:
                The baseline speed of the ball. Collisions with objects may
                momentarily increase/decrease the speed of the ball, but the
                ball will always try to gradually settle back to the base
                speed.
            top_speed:
                The maximum permitted speed of the ball. Collisions with
                objects may increase the speed of the ball, but the speed
                will never go above the top_speed.
            normalisation_rate:
                The per-frame rate at which the ball is brought back to base
                speed, should the speed have changed due to collision with
                an object.
            off_screen_callback:
                A callable that will be called if the ball goes off the edge
                of the screen. It takes a single argument: the ball sprite
                instance.
        """
        super().__init__()
        self.image, self.rect = load_png('ball')
        self.rect.x, self.rect.y = start_pos
        self.visible = True
        self.speed = base_speed
        self.base_speed = base_speed
        self.normalisation_rate = normalisation_rate
        self.angle = start_angle

        self._start_pos = start_pos
        self._start_angle = start_angle
        self._top_speed = top_speed
        self._off_screen_callback = off_screen_callback
        self._anchor = None

        # The area within which the ball is in play.
        screen = pygame.display.get_surface()
        self._area = screen.get_rect()

        # The sprites the ball can collide with.
        self._collidable_sprites = pygame.sprite.Group()

        # The actions associated with the collidable sprites.
        # This dictionary is keyed by the collidable sprite. The value is
        # a 3-element tuple corresponding to the bounce strategy, speed
        # adjustment and collision callback for that sprite.
        self._collision_data = {}
Beispiel #3
0
    def __init__(self, round_class=Round1, lives=3):
        """Initialise a new Game.

        Args:
            round_class:
                The class of the round to start, default Round1.
            lives:
                Optional number of lives for the player, default 3.
        """
        # Keep track of the score and lives throughout the game.
        self.lives = lives
        self.score = 0

        # Reference to the main screen.
        self._screen = pygame.display.get_surface()

        # The life graphic.
        self._life_img, _ = load_png('paddle_life.png')
        # The life graphic positions.
        self._life_rects = []

        # The current round.
        self.round = round_class(TOP_OFFSET)

        # The sprites in the game.
        self.paddle = Paddle(left_offset=self.round.edges.left.rect.width,
                             right_offset=self.round.edges.right.rect.width,
                             bottom_offset=60,
                             speed=PADDLE_SPEED)

        ball = Ball(start_pos=self.paddle.rect.midtop,
                    start_angle=BALL_START_ANGLE_RAD,
                    base_speed=BALL_BASE_SPEED,
                    top_speed=BALL_TOP_SPEED,
                    normalisation_rate=BALL_SPEED_NORMALISATION_RATE,
                    off_screen_callback=self._off_screen)

        # The game starts with a single ball in play initially.
        self.balls = [ball]

        # The currently applied powerup, if any.
        self.active_powerup = None

        # The current enemies in the game.
        self.enemies = []

        # Hold a reference to all the sprites for redrawing purposes.
        self.sprites = []

        # Create event handlers required by the game.
        self._create_event_handlers()

        # Whether the game is finished.
        self.over = False

        # The current game state which handles the behaviour for the
        # current stage of the game.
        self.state = GameStartState(self)
Beispiel #4
0
    def cancel_open_door(self):
        """Cancel any requests to open a door.

        A request may have been made to open the door, but the door may not
        yet be open, as a short random delay happens before the door is opened.
        This method will cancel such requests.
        """
        self._open_queue.clear()
        self._door_open_animation = None
        self._door_close_animation = None
        self.image, _ = load_png('edge_top')
Beispiel #5
0
    def __init__(self, left_offset=0, right_offset=0, bottom_offset=0,
                 speed=10):
        """
        Create a new Paddle instance.

        The paddle will travel the entire width of the screen, unless the
        left and right offsets are specified which can restrict its travel.
        A bottom offset can also be supplied which defines how far from the
        bottom of the screen the paddle floats.

        Args:
            left_offset:
                Optional offset in pixels from the left of the screen that
                will restrict the maximum travel of the paddle.
            right_offset:
                Optional offset in pixels from the right of the screen that
                will restrict the maximum travel of the paddle.
            bottom_offset:
                The distance the paddle sits above the bottom of the screen.
            speed:
                Optional speed of the paddle in pixels per frame.
        """
        super().__init__()

        # The speed of the paddle movement in pixels per frame.
        self.speed = speed

        # The current movement in pixels. A negative value will trigger the
        # paddle to move left, a positive value to move right.
        self._move = 0

        # This toggles visibility of the paddle.
        self.visible = True

        # Load the default paddle image.
        self.image, self.rect = load_png('paddle')

        # Create the area the paddle can move laterally in.
        screen = pygame.display.get_surface().get_rect()
        self.area = pygame.Rect(screen.left + left_offset,
                                screen.height - bottom_offset,
                                screen.width - left_offset - right_offset,
                                self.rect.height)
        # Position the paddle.
        self.rect.center = self.area.center

        # A list of no-args callables that will be called on ball collision.
        self.ball_collide_callbacks = []

        # The current paddle state.
        self._state = NormalState(self)
Beispiel #6
0
    def __init__(self, side):
        """Initialise a new SideEdge specifying which side - either 'left'
        or 'right'.

        Args:
            side:
                The side - either 'left' or 'right'.
        """
        if side not in ('left', 'right'):
            raise AttributeError("Side must be either 'left' or 'right'")

        super().__init__()

        self.image, self.rect = load_png('edge_%s' % side)
        self.visible = True
Beispiel #7
0
    def __init__(self):
        super().__init__()

        self.image, self.rect = load_png('edge_top')
        self._image_sequence = {
            DOOR_TOP_LEFT: load_png_sequence(DOOR_TOP_LEFT),
            DOOR_TOP_RIGHT: load_png_sequence(DOOR_TOP_RIGHT)
        }
        self._door_open_animation = None
        self._door_close_animation = None
        self._open_queue = []
        self._open_until = 0

        self._update_count = 0

        self.visible = True
Beispiel #8
0
 def __init__(self, brick_colour, round_no, powerup_cls=None):
     super().__init__()
     self.colour = brick_colour
     self.image, self.rect = load_png('brick_{}'.format(brick_colour.name))
     self._image_sequence = [
         image for image, _ in load_png_sequence('brick_{}'.format(
             brick_colour.name))
     ]
     self._animation = None
     self.collision_count = 0
     self.powerup_cls = powerup_cls
     if brick_colour == BrickColour.silver:
         self.value = brick_colour.value * round_no
     else:
         self.value = brick_colour.value
     if brick_colour == BrickColour.silver:
         self._destroy_after = 3
     elif brick_colour == BrickColour.gold:
         #self._destroy_after = -1
         self._destroy_after = 5  #---- kong ----
     else:
         self._destroy_after = 1
Beispiel #9
0
 def _display_logo(self):
     image, _ = load_png('logo.png')
     self._screen.blit(image, (5, 0))
Beispiel #10
0
 def enter(self):
     """Set the default paddle graphic."""
     pos = self.paddle.rect.center
     self.paddle.image, self.paddle.rect = load_png('paddle')
     self.paddle.rect.center = pos
Beispiel #11
0
    def __init__(self,
                 start_pos,
                 start_angle,
                 base_speed,
                 top_speed=15,
                 normalisation_rate=0.02,
                 off_screen_callback=None):
        """
        Initialise a new Ball with the given arguments.

        If supplied, the off_screen_callback will be invoked whenever the
        ball leaves the screen. This callable takes a single argument: the
        ball sprite instance.

        Args:
            start_pos:
                The starting coordinates of the ball. A 2 element sequence.
            start_angle:
                The starting angle of the ball in radians.
            base_speed:
                The baseline speed of the ball. Collisions with objects may
                momentarily increase/decrease the speed of the ball, but the
                ball will always try to gradually settle back to the base
                speed.
            top_speed:
                The maximum permitted speed of the ball. Collisions with
                objects may increase the speed of the ball, but the speed
                will never go above the top_speed.
            normalisation_rate:
                The per-frame rate at which the ball is brought back to base
                speed, should the speed have changed due to collision with
                an object.
            off_screen_callback:
                A callable that will be called if the ball goes off the edge
                of the screen. It takes a single argument: the ball sprite
                instance.
        """
        super().__init__()
        self.image, self.rect = load_png('ball')
        self.rect.x, self.rect.y = start_pos
        self.visible = True
        self.speed = base_speed
        self.base_speed = base_speed
        self.normalisation_rate = normalisation_rate
        self.angle = start_angle

        self._start_pos = start_pos
        self._start_angle = start_angle
        self._top_speed = top_speed
        self._off_screen_callback = off_screen_callback
        self._anchor = None

        # The area within which the ball is in play.
        screen = pygame.display.get_surface()
        self._area = screen.get_rect()

        # The sprites the ball can collide with.
        self._collidable_sprites = pygame.sprite.Group()

        # The actions associated with the collidable sprites.
        # This dictionary is keyed by the collidable sprite. The value is
        # a 3-element tuple corresponding to the bounce strategy, speed
        # adjustment and collision callback for that sprite.
        self._collision_data = {}
Beispiel #12
0
 def __init__(self, side):
     if side not in ('left', 'right'):
         raise AttributeError("side must be either 'left' or 'right'")
     super().__init__()
     self.image, self.rect = load_png('edge_%s' % side)
     self.visible = True
Beispiel #13
0
 def cancel_open_door(self):
     self._open_queue.clear()
     self._door_open_animation = None
     self._door_close_animation = None
     self.image, _ = load_png('edge_top')
Beispiel #14
0
    def __init__(self, brick_colour, round_no, powerup_cls=None):
        """Initialise a new Brick using the specified BrickColour enum.

        When a brick is initialised with the specified BrickColour, a file
        named 'brick_<colour>.png' will be loaded from the graphics folder -
        where <colour> corresponds to the name attribute of the BrickColour
        enum. That file must exist.

        In addition, the initialiser will also attempt to load an image
        sequence named 'brick_<colour>_N.png' from the graphics folder
        which will be used to animate the brick when  Brick.animate() is
        called. This image sequence is optional, and if the files do not
        exist, then triggering Brick.animate() will have no effect.

        The round number must also be supplied which is used to generate the
        score value for certain brcks.

        Lastly, optionally specify the class of a powerup which will fall
        from the brick when the brick is struck by the ball - via the
        powerup_cls attribute.

        Args:
            brick_colour:
                A BrickColour enum instance. A png file named
                'brick_<colour>.png' must exist in the graphics folder where
                <colour> corresponds to the enum name attribute.
            round_no:
                The current round number used to generate the brick score
                value.
            powerup_cls:
                Optional class of a PowerUp that will be used when the ball
                strikes this brick (default None).
        """
        super().__init__()
        self.colour = brick_colour
        # Load the brick graphic.
        self.image, self.rect = load_png('brick_{}'.format(brick_colour.name))

        # Load the images/rects required for any animation.
        self._image_sequence = [image for image, _ in load_png_sequence(
            'brick_{}'.format(brick_colour.name))]
        self._animation = None

        # The number of ball collisions with this brick.
        self.collision_count = 0

        # The class of the powerup.
        self.powerup_cls = powerup_cls

        # The score value for this brick.
        if brick_colour == BrickColour.silver:
            # The score for silver bricks is a product of the brick value
            # and round number.
            self.value = brick_colour.value * round_no
        else:
            self.value = brick_colour.value

        # The number of collisions before the brick gets destroyed.
        if brick_colour == BrickColour.silver:
            self._destroy_after = 2
        elif brick_colour == BrickColour.gold:
            # Gold bricks are never destroyed.
            self._destroy_after = -1
        else:
            self._destroy_after = 1
Beispiel #15
0
 def enter(self):
     """Set the default paddle graphic."""
     pos = self.paddle.rect.center
     self.paddle.image, self.paddle.rect = load_png('paddle')
     self.paddle.rect.center = pos
Beispiel #16
0
    def __init__(self, brick_colour, round_no, powerup_cls=None):
        """Initialise a new Brick using the specified BrickColour enum.

        When a brick is initialised with the specified BrickColour, a file
        named 'brick_<colour>.png' will be loaded from the graphics folder -
        where <colour> corresponds to the name attribute of the BrickColour
        enum. That file must exist.

        In addition, the initialiser will also attempt to load an image
        sequence named 'brick_<colour>_N.png' from the graphics folder
        which will be used to animate the brick when  Brick.animate() is
        called. This image sequence is optional, and if the files do not
        exist, then triggering Brick.animate() will have no effect.

        The round number must also be supplied which is used to generate the
        score value for certain brcks.

        Lastly, optionally specify the class of a powerup which will fall
        from the brick when the brick is struck by the ball - via the
        powerup_cls attribute.

        Args:
            brick_colour:
                A BrickColour enum instance. A png file named
                'brick_<colour>.png' must exist in the graphics folder where
                <colour> corresponds to the enum name attribute.
            round_no:
                The current round number used to generate the brick score
                value.
            powerup_cls:
                Optional class of a PowerUp that will be used when the ball
                strikes this brick (default None).
        """
        super().__init__()
        self.colour = brick_colour
        # Load the brick graphic.
        self.image, self.rect = load_png('brick_{}'.format(brick_colour.name))

        # Load the images/rects required for any animation.
        self._image_sequence = [
            image for image, _ in load_png_sequence('brick_{}'.format(
                brick_colour.name))
        ]
        self._animation = None

        # The number of ball collisions with this brick.
        self.collision_count = 0

        # The class of the powerup.
        self.powerup_cls = powerup_cls

        # The score value for this brick.
        if brick_colour == BrickColour.silver:
            # The score for silver bricks is a product of the brick value
            # and round number.
            self.value = brick_colour.value * round_no
        else:
            self.value = brick_colour.value

        # The number of collisions before the brick gets destroyed.
        if brick_colour == BrickColour.silver:
            self._destroy_after = 2
        elif brick_colour == BrickColour.gold:
            # Gold bricks are never destroyed.
            self._destroy_after = -1
        else:
            self._destroy_after = 1
Beispiel #17
0
 def _display_logo(self):
     image, _ = load_png('logo.png')
     self._screen.blit(image, (5, 0))