class AlienInvasion:
    """Overall class used to manage game assets and behaviour."""
    def __init__(self):
        """Initialise the game, and create game resources."""
        pygame.init()
        self.settings = Settings()
        self.finder = DataFileFinder()

        # Uncomment code to run Alien Invasion in full screen mode.
        # self.screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
        # self.settings.screen_width = self.screen.get_rect().width
        # self.settings.screen_height = self.screen.get_rect().height

        # Comment line out when trying to run Alien Invasion in full screen
        # mode.
        self.screen = pygame.display.set_mode(
            (self.settings.screen_width, self.settings.screen_height))

        pygame.display.set_caption("Alien Invasion")

        # Create an instance to store game statistics, and create a scoreboard.
        self.stats = GameStats(self)
        self.sb = Scoreboard(self)

        self.spaceship = Spaceship(self)
        self.bullets = pygame.sprite.Group()
        self.aliens = pygame.sprite.Group()

        self._create_wave()

        # Create game title.
        self.title = GameTitle(self)

        # Create help text.
        self.help = Help(self)

        # Make buttons.
        self.play_button = Button(
            self,
            pygame.image.load(
                self.finder.find_data_file('play_button.bmp', 'images')))
        self.help_button = Button(
            self,
            pygame.image.load(
                self.finder.find_data_file('help_button.bmp', 'images')))
        self.exit_button = Button(
            self,
            pygame.image.load(
                self.finder.find_data_file('exit_button.bmp', 'images')))
        self.resume_button = Button(
            self,
            pygame.image.load(
                self.finder.find_data_file('resume_button.bmp', 'images')))
        self.quit_button = Button(
            self,
            pygame.image.load(
                self.finder.find_data_file('quit_button.bmp', 'images')))
        self.back_button = Button(
            self,
            pygame.image.load(
                self.finder.find_data_file('back_button.bmp', 'images')))
        self.ok_button = Button(
            self,
            pygame.image.load(
                self.finder.find_data_file('ok_button.bmp', 'images')))

        # Give spacing between buttons.
        self.help_button.rect.y += self.help_button.rect.height + 20
        self.exit_button.rect.y += (self.exit_button.rect.height + 20) * 2
        self.quit_button.rect.y += self.quit_button.rect.height + 20
        self.back_button.rect.y = (self.help.how_to_text_rects[-1].bottom + 50)
        self.ok_button.rect.y = self.screen.get_rect().bottom - 250

        # Create texts used in the game.
        self.get_ready_text = Text(self, 'Get Ready...', 24)
        self.get_ready_text.text_rect.y -= 48
        self.incoming_wave_text = Text(self, 'Incoming Wave...', 36)

    def run_game(self):
        """Start the main loop for the game."""
        while True:
            self._check_events()
            self._check_timer()

            if (self.stats.game_active and not self.stats.game_paused
                    and not self.stats.help_active
                    and not self.stats.game_over):
                self.spaceship.update()
                self._update_bullets()

                # Make sure aliens aren't updated when prep text is being shown
                # to the user.
                if not self.stats.get_ready and not self.stats.incoming_wave:
                    self._update_aliens()

            self._update_screen()

    def _check_events(self):
        # Repspond to keyboard and mouse events.
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                self._check_keydown_events(event)
            elif event.type == pygame.KEYUP:
                self._check_keyup_events(event)
            elif event.type == pygame.MOUSEBUTTONUP:
                mouse_pos = pygame.mouse.get_pos()
                self._check_play_button(mouse_pos)
                self._check_help_button(mouse_pos)
                self._check_exit_button(mouse_pos)
                self._check_resume_button(mouse_pos)
                self._check_quit_button(mouse_pos)
                self._check_back_button(mouse_pos)
                self._check_ok_button(mouse_pos)

    def _check_play_button(self, mouse_pos):
        """Start a new game when the player clicks Play."""
        button_clicked = self.play_button.rect.collidepoint(mouse_pos)
        if (button_clicked and not self.stats.game_active
                and not self.stats.game_paused and not self.stats.help_active
                and not self.stats.game_over):
            # Reset the game settings.
            self.settings.initialise_dynamic_settings

            # Reset game statistics.
            self.stats.reset_stats()
            self.stats.game_active = True
            self.sb.prep_score()
            self.sb.prep_wave()
            self.sb.prep_spaceships()

            # Get rid of any remaining aliens and bullets.
            self.aliens.empty()
            self.bullets.empty()

            # Create a new wave and center the spaceship.
            self._create_wave()
            self.spaceship.center_spaceship()

            # Hide the mouse cursor.
            pygame.mouse.set_visible(False)

            # Show get ready text.
            self.stats.get_ready = True
            self._check_get_ready_counter()

    def _check_help_button(self, mouse_pos):
        """Show help text when the player clicks Help."""
        button_clicked = self.help_button.rect.collidepoint(mouse_pos)
        if (button_clicked and not self.stats.game_active
                and not self.stats.game_paused and not self.stats.help_active
                and not self.stats.game_over):
            self.stats.help_active = True

    def _check_exit_button(self, mouse_pos):
        """Exit game when the player clicks Exit."""
        button_clicked = self.exit_button.rect.collidepoint(mouse_pos)
        if (button_clicked and not self.stats.game_active
                and not self.stats.game_paused and not self.stats.help_active
                and not self.stats.game_over):
            # Exit the game.
            sys.exit()

    def _check_resume_button(self, mouse_pos):
        """Resume current game when the player clicks Resume."""
        button_clicked = self.resume_button.rect.collidepoint(mouse_pos)
        if (button_clicked and self.stats.game_active
                and self.stats.game_paused and not self.stats.help_active
                and not self.stats.game_over):

            if hasattr(self, 'timer'):
                if (self.timer != None and not self.timer.active):
                    self.timer.resume()
            self.stats.game_paused = False
            # Hide the mouse cursor.
            pygame.mouse.set_visible(False)

    def _check_quit_button(self, mouse_pos):
        """Go back to main menu when the player clicks Quit."""
        button_clicked = self.quit_button.rect.collidepoint(mouse_pos)
        if (button_clicked and self.stats.game_active
                and self.stats.game_paused and not self.stats.help_active
                and not self.stats.game_over):
            self.stats.game_active = False
            self.stats.game_paused = False
            self.stats.help_active = False

    def _check_back_button(self, mouse_pos):
        """Go back to main menu when the player clicks Back."""
        button_clicked = self.back_button.rect.collidepoint(mouse_pos)
        if (button_clicked and not self.stats.game_active
                and not self.stats.game_paused and self.stats.help_active
                and not self.stats.game_over):
            self.stats.game_active = False
            self.stats.game_paused = False
            self.stats.help_active = False

    def _check_ok_button(self, mouse_pos):
        """Go back to main menu when the player clicks Ok."""
        button_clicked = self.ok_button.rect.collidepoint(mouse_pos)
        game_over = (self.stats.game_active and not self.stats.game_paused
                     and not self.stats.help_active and self.stats.game_over)
        if button_clicked and game_over:
            self.stats.game_active = False
            self.stats.game_paused = False
            self.stats.help_active = False
            self.stats.game_over = False

    def _check_keydown_events(self, event):
        """Respond to keypresses."""
        if event.key == pygame.K_RIGHT:
            # Move the spaceship to the right.
            self.spaceship.moving_right = True
        elif event.key == pygame.K_LEFT:
            # Move the spaceship to the left.
            self.spaceship.moving_left = True
        elif event.key == pygame.K_q:
            sys.exit()
        elif (event.key == pygame.K_SPACE and self.stats.game_active
              and not self.stats.get_ready and not self.stats.incoming_wave):
            self._fire_bullet()
        elif event.key == pygame.K_p and self.stats.game_active:
            self.stats.game_paused = True
            # Show the mouse cursor.
            pygame.mouse.set_visible(True)

    def _check_keyup_events(self, event):
        """Respond to key releases."""
        if event.key == pygame.K_RIGHT:
            self.spaceship.moving_right = False
        elif event.key == pygame.K_LEFT:
            self.spaceship.moving_left = False

    def _check_timer(self):
        """
        Check whether there are any threads being used that need to be paused.
        """
        if hasattr(self, 'timer'):
            if (self.timer != None and self.stats.game_paused
                    and self.timer.active):
                self.timer.pause()

    def _create_wave(self):
        """Create the wave of aliens."""
        # Create an alien and find the number of aliens in a row.
        # Determine equal spacing between all aliens in the row.
        alien = Alien(self)
        alien_width, alien_height = alien.rect.size
        available_space_x = self.settings.screen_width - (2 * alien_width)
        number_of_aliens_x = available_space_x // (2 * alien_width)
        alien_spacing_x = (number_of_aliens_x *
                           alien_width) // (number_of_aliens_x - 1)

        # Determine the number of rows of aliens that fit on the screen.
        spaceship_height = self.spaceship.rect.height
        avialable_space_y = self.settings.screen_height - (
            (5 * alien_height) + spaceship_height)
        number_of_rows = avialable_space_y // (2 * alien_height)

        # Create the full wave of aliens.
        for row_number in range(number_of_rows):
            for alien_number in range(number_of_aliens_x):
                self._create_alien(alien_number, alien_spacing_x, row_number)

    def _create_alien(self, alien_number, alien_spacing_x, row_number):
        """Create an alien and place it in the row."""
        alien = Alien(self)
        alien_width, alien_height = alien.rect.size
        alien.x = alien_width + (
            (alien_width + alien_spacing_x) * alien_number)
        alien.rect.x = alien.x
        alien.rect.y = alien_height + 50 + (2 * alien_height * row_number)
        self.aliens.add(alien)

    def _check_wave_edges(self):
        """Respond appropriately if any aliens have reached the edge."""
        for alien in self.aliens.sprites():
            if alien.check_edges():
                self._change_wave_direction()
                break

    def _change_wave_direction(self):
        """Drop the entire wave and change the wave's direction."""
        for alien in self.aliens.sprites():
            alien.rect.y += self.settings.wave_drop_speed
        self.settings.wave_direction *= -1

    def _fire_bullet(self):
        """Create a new bullet and add it to the bullets group."""
        if len(self.bullets) < self.settings.bullets_allowed:
            new_bullet = Bullet(self)
            self.bullets.add(new_bullet)
            self.stats.bullets_fired += 1

    def _update_bullets(self):
        """Update position of bullets and get rid of old bullets."""
        # Update bullet positons.
        self.bullets.update()

        # Get rid of bullets that have disappeared.
        for bullet in self.bullets.copy():
            if bullet.rect.bottom <= 0:
                self.bullets.remove(bullet)

        self._check_bullet_alien_collisions()

    def _check_bullet_alien_collisions(self):
        """Respond to bullet-alien collisions."""
        # Remove any bullets and aliens that have collided.
        collisions = pygame.sprite.groupcollide(self.bullets, self.aliens,
                                                True, True)

        if collisions:
            for aliens in collisions.values():
                self.stats.score += self.settings.alien_points * len(aliens)
                self.stats.aliens_destroyed += len(aliens)
            self.sb.prep_score()
            self.sb.check_high_score()

        if not self.aliens:
            # Destroy existing bullets and create new wave.
            self.bullets.empty()
            self.settings.increase_speed()

            # Increase wave.
            self._create_wave()
            self.stats.wave += 1
            self.sb.prep_wave()

            # Pause to allow player to get ready.
            self.stats.incoming_wave = True

            # Create timer to use when waiting for incoming wave of aliens.
            self.timer = ResumableTimer(1.0, self._incoming_wave)
            self.timer.start()

    def _spaceship_hit(self):
        """Respond to the spaceship being hit by an alien."""
        if self.stats.spaceships_left > 0:
            # Decrement spaceships_left, and update scoreboard.
            self.stats.spaceships_left -= 1
            self.sb.prep_spaceships()

            # Check to see if the has anymore spaceships left.
            if self.stats.spaceships_left <= 0:
                self.stats.game_over = True
                pygame.mouse.set_visible(True)
                return

            # Get rid of any remaining aliens and bullets.
            self.aliens.empty()
            self.bullets.empty()

            # Create wave of aliens and center the spaceship.
            self._create_wave()
            self.spaceship.center_spaceship()

            # Create timer to use when waiting for incoming wave of aliens.
            self.stats.get_ready = True
            self._check_get_ready_counter()

    def _check_get_ready_counter(self):
        if self.stats.get_ready_counter > 0:
            self.stats.get_ready_counter -= 1
            self.timer = ResumableTimer(1.0, self._check_get_ready_counter)
            self.timer.start()
        else:
            self.stats.get_ready = False
            self.stats.reset_counter()
            self.timer = None

    def _incoming_wave(self):
        self.stats.incoming_wave = False
        self.timer = None

    def _check_aliens_bottom(self):
        """Check if any aliens have reached the bottom of the screen."""
        screen_rect = self.screen.get_rect()
        for alien in self.aliens.sprites():
            if alien.rect.bottom >= screen_rect.bottom - 20:
                self._spaceship_hit()
                break

    def _update_aliens(self):
        """
        Check if the wave is at an edge, then update the positions of all 
        aliens in the wave.
        """
        self._check_wave_edges()
        self.aliens.update()

        # Look for alien-ship collisions.
        if pygame.sprite.spritecollideany(self.spaceship, self.aliens):
            self._spaceship_hit()

        # Look for aliens hitting the bottom of the screen.
        self._check_aliens_bottom()

    def _update_screen(self):
        # Update images on the screen, and flip to the new screen.
        self.screen.fill(self.settings.bg_colour)

        # Draw the spaceship and the score information.
        if self.stats.game_active and not self.stats.game_over:
            self.spaceship.blitme()
            for bullet in self.bullets.sprites():
                bullet.draw_bullet()

            # Draw the score information.
            self.sb.show_score()

        # Draw get ready text.
        if (self.stats.game_active and not self.stats.game_over
                and self.stats.get_ready and not self.stats.incoming_wave):
            self.get_ready_text.draw_text()

            # Create count to show on screen.
            count = str(self.stats.get_ready_counter + 1)
            self.counter_text = Text(self, count, 48)

            # Draw count.
            self.counter_text.draw_text()

        # Draw incoming wave text.
        if (self.stats.game_active and not self.stats.game_over
                and not self.stats.get_ready and self.stats.incoming_wave):
            self.incoming_wave_text.draw_text()

        # Draw aliens.
        if (self.stats.game_active and not self.stats.game_over
                and not self.stats.get_ready and not self.stats.incoming_wave):
            self.aliens.draw(self.screen)

        # Draw the play, help and quit button if the game is invactive.
        if (not self.stats.game_active and not self.stats.game_paused
                and not self.stats.help_active and not self.stats.game_over):
            self.title.draw_title()
            self.play_button.draw_button()
            self.help_button.draw_button()
            self.exit_button.draw_button()

        # Draw the resume and back button if the game is paused.
        if (self.stats.game_active and self.stats.game_paused
                and not self.stats.help_active and not self.stats.game_over):
            self.resume_button.draw_button()
            self.quit_button.draw_button()

        # Draw user game stats if the game is over.
        if (self.stats.game_active and not self.stats.game_paused
                and not self.stats.help_active and self.stats.game_over):
            self.stats.show_stats()
            self.ok_button.draw_button()

        # Draw the help text and button if the game is paused.
        if (not self.stats.game_active and not self.stats.game_paused
                and self.stats.help_active and not self.stats.game_over):
            self.help.show_help()
            self.back_button.draw_button()

        # Make the most recently drawn screen visible.
        pygame.display.flip()
Beispiel #2
0
class AlienInvasion():
    '''Classe geral para gerir os componentes e o comportamento do jogo'''

    def __init__(self):
        """Inicializa o jogo e cria os recursos do mesmo"""
        pygame.init()
        self.settings = Settings()
        self.clock = pygame.time.Clock()

        # Carregando a janela do jogo
        self.screen = pygame.display.set_mode(
            (self.settings.width, self.settings.height))

        pygame.display.set_caption('Alien Invasion')

        self.game_stats = GameStats(self)
        self.scoreboard = Scoreboard(self)
        self.easy_game_button = button.EasyModeButton(self, 'Easy')
        self.normal_game_button = button.NormalModeButton(self, 'Normal')
        self.hard_game_button = button.HardModeButton(self, 'Hard')
        self.spaceship = Spaceship(self)
        self.bullets = pygame.sprite.Group()
        self.aliens = pygame.sprite.Group()

        self._create_fleet()

    def _check_events(self):
        '''Responde aos inputs de mouse e teclado'''
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    self._fire_bullet()
            elif event.type == pygame.MOUSEBUTTONDOWN:
                mouse_position = pygame.mouse.get_pos()
                self._check_easy_game_button(mouse_position)
                self._check_normal_game_button(mouse_position)
                self._check_hard_game_button(mouse_position)

        self.spaceship.update()

    def _check_easy_game_button(self, mouse_position):
        '''Começa um novo jogo quando o jogador clica em Start'''
        button_clicked = self.easy_game_button.rect.collidepoint(
            mouse_position)
        if button_clicked and not self.game_stats.game_playing:
            self._start_new_easy_mode_game()

    def _check_normal_game_button(self, mouse_position):
        '''Começa um novo jogo quando o jogador clica em Start'''
        button_clicked = self.normal_game_button.rect.collidepoint(
            mouse_position)
        if button_clicked and not self.game_stats.game_playing:
            self._start_new_normal_mode_game()

    def _check_hard_game_button(self, mouse_position):
        '''Começa um novo jogo quando o jogador clica em Start'''
        button_clicked = self.hard_game_button.rect.collidepoint(
            mouse_position)
        if button_clicked and not self.game_stats.game_playing:
            self._start_new_hard_mode_game()

    def _start_new_easy_mode_game(self):
        '''Cria um novo jogo destruindo os aliens e as balas restantes'''
        self.settings.easy_mode_dynamic_settings()
        self.game_stats.reset_stats()
        self.game_stats.game_playing = True
        self.scoreboard._score()
        self.scoreboard._wave()
        self.scoreboard._lives()

        self.aliens.empty()
        self.bullets.empty()

        self._create_fleet()
        self.spaceship.center_spaceship()

        pygame.mouse.set_visible(False)

    def _start_new_normal_mode_game(self):
        '''Cria um novo jogo destruindo os aliens e as balas restantes'''
        self.settings.normal_mode_dynamic_settings()
        self.game_stats.reset_stats()
        self.game_stats.game_playing = True
        self.scoreboard._score()
        self.scoreboard._wave()
        self.scoreboard._lives()

        self.aliens.empty()
        self.bullets.empty()

        self._create_fleet()
        self.spaceship.center_spaceship()

        pygame.mouse.set_visible(False)

    def _start_new_hard_mode_game(self):
        '''Cria um novo jogo destruindo os aliens e as balas restantes'''
        self.settings.hard_mode_dynamic_settings()
        self.game_stats.reset_stats()
        self.game_stats.game_playing = True
        self.scoreboard._score()
        self.scoreboard._wave()
        self.scoreboard._lives()

        self.aliens.empty()
        self.bullets.empty()

        self._create_fleet()
        self.spaceship.center_spaceship()

        pygame.mouse.set_visible(False)

    def _fire_bullet(self):
        '''Cria uma nova bala e a adiciona no grupo bullets
        se o número de balas na tela for menor que o permitido'''
        if len(self.bullets) < self.settings.bullet_allowed:
            new_bullet = Bullet(self)
            self.bullets.add(new_bullet)

    def _create_fleet(self):
        '''Cria uma frota de aliens'''
        # Cria um alien
        alien = Alien(self)
        # Acha o número de aliens que cabem numa linha
        # Pega a largura e a altura de um alien
        alien_width, alien_height = alien.rect.size
        # Calcula o espaço horizontal vazio
        space_x = self.settings.width - (2 * alien_width)
        # Calcula quantos aliens cabem nesse espaço vazio
        number_aliens_x = space_x // (2 * alien_width)

        # Determina o número de linhas que cabem na tela
        spaceship_height = self.spaceship.rect.height
        space_y = (self.settings.height -
                   (3 * alien_height) - spaceship_height)
        number_rows = space_y // (4 * alien_height)

        # Cria uma frota de aliens
        for row_number in range(number_rows):
            for alien_number in range(number_aliens_x):
                self._create_alien(alien_number, row_number)

    def _create_alien(self, alien_number, row_number):
        '''Cria um alien e o coloca na linha'''
        alien = Alien(self)
        alien_width, alien_height = alien.rect.size
        # Define a coordenada x onde ele vai ser colocado
        alien.x = alien_width + 2 * alien_width * alien_number
        # Define a posição do rect
        alien.rect.x = alien.x
        alien.rect.y = alien.rect.height + 2 * alien_height * row_number
        # Adiciona o novo alien para o grupo aliens
        self.aliens.add(alien)

    def _check_fleet_edges(self):
        '''Responde de acordo se um alien encostar no limite'''
        for alien in self.aliens.sprites():
            if alien.check_edges():
                self._change_fleet_direction()
                break

    def _change_fleet_direction(self):
        '''Faz toda a frota descer e muda a direção da mesma'''
        for alien in self.aliens.sprites():
            alien.rect.y += self.settings.fleet_drop_speed
        self.settings.fleet_direction *= -1

    def _spaceship_hit(self):
        '''Ação que ocorre quando a nave é acertada por um alien'''
        # Diminui o número de vidas
        if self.game_stats.spaceship_lives > 0:
            self.game_stats.spaceship_lives -= 1
            self.scoreboard._lives()

            # Destrói os aliens e as balas restantes
            self.aliens.empty()
            self.bullets.empty()

            # Cria uma nova frota
            self._create_fleet()
            # Centraliza a nave
            self.spaceship.center_spaceship()

            # Pausa
            sleep(0.5)
        else:
            self.game_stats.game_playing = False
            pygame.mouse.set_visible(True)

    def _update_bullets(self):
        '''Atualiza a posição das balas
        e exclui as que não aparecem mais na tela'''

        # Atualiza a posição das balas
        self.bullets.update()

        # Excluindo as balas que não estão mais aparecendo na tela
        for bullet in self.bullets.copy():
            if bullet.rect.bottom <= 0:
                self.bullets.remove(bullet)

        self._check_bullet_alien_collisions()

    def _check_bullet_alien_collisions(self):
        '''Colisão entre as balas e os aliens'''

        # Checa por qualquer bala que acertou os aliens,
        # e caso acerte exclui a bala e o alien
        collisions = pygame.sprite.groupcollide(self.bullets, self.aliens,
                                                True, True)

        if collisions:
            for aliens in collisions.values():
                self.game_stats.score += self.settings.alien_points * \
                    len(aliens)
            self.scoreboard._score()
            self.scoreboard.check_highscore()

        if not self.aliens:
            # Destrói balas existentes
            self.bullets.empty()
            self._create_fleet()
            self.settings.increase_speed()

            self.game_stats.wave += 1
            self.scoreboard._wave()

    def _update_screen(self):
        '''Preenche a tela com as imagens, e muda para um nova janela'''
        self.screen.fill(self.settings.bg_color)
        self.spaceship.draw_ship()
        for bullet in self.bullets.sprites():
            bullet.draw_bullet()
        self.aliens.draw(self.screen)

        self.scoreboard.draw_score()

        if not self.game_stats.game_playing:
            # self.easy_game_button.draw_button()
            self.draw_buttons()

        pygame.display.flip()

    def draw_buttons(self):
        '''Desenha os botôes na tela'''
        self.easy_game_button.draw_button()
        self.normal_game_button.draw_button()
        self.hard_game_button.draw_button()

    def _check_aliens_in_bottom(self):
        '''Verifica se nenhum alien chegou na parte de baixo da tela'''
        screen_rect = self.screen.get_rect()
        # Passa por cada alien no grupo de aliens
        # verificando se encostou no limite de baixo da tela
        for alien in self.aliens.sprites():
            if alien.rect.bottom >= screen_rect.bottom:
                self._spaceship_hit()
                break

    def _update_aliens(self):
        '''Atualiza a posição de todos os aliens da frota
            após verificar se a frota está no limite da janela'''
        self._check_fleet_edges()
        self.aliens.update()

        # Vê se um alien atingiu a nave,
        # e caso atinja o número de vidas diminui em 1
        if pygame.sprite.spritecollideany(self.spaceship, self.aliens):
            self._spaceship_hit()

        self._check_aliens_in_bottom()

    def run_game(self):
        '''Inicia o loop principal do jogo'''
        while True:
            self.clock.tick(self.settings.fps)
            self._check_events()
            if self.game_stats.game_playing:
                self.spaceship.update()
                self._update_bullets()
                self._update_aliens()

            self._update_screen()