import os import random import sys import time import pygame import pygame.sprite # for annotations from pygame.event import EventType from pygame.sprite import Group from alien import Alien from bullets import Bullet from game_items import GameItems from game_stats import GameStats from settings import Settings from ship import Ship def check_events(ai_settings: Settings, stats: GameStats, game_items: GameItems): """Respond to keypresses and mouse events.""" # Watch for keyboard and mouse events. for event in pygame.event.get(): if event.type == pygame.QUIT: quit_game(stats) elif event.type == pygame.KEYDOWN: check_keydown_events(event, ai_settings, stats, game_items) elif event.type == pygame.KEYUP: check_keyup_events(event, game_items.ship) elif event.type == pygame.MOUSEBUTTONDOWN: check_mousedown_events(ai_settings, stats, game_items) def quit_game(stats: GameStats): """Save the highscore and exit the game.""" filename = os.path.join('.', 'save/highscore.txt') with open(filename, 'w') as f: f.write(str(stats.high_score)) sys.exit() def update_screen(ai_settings: Settings, stats: GameStats, game_items: GameItems): """Update images on the screen and flip to the new screen.""" # Redraw the screen during each pass through the loop. game_items.screen.fill(ai_settings.bg_color) # Redraw all bullets behind ship and aliens. for bullet in game_items.bullets.sprites(): bullet.draw_bullet() # Draw ship. game_items.ship.blitme() # Draw alien. game_items.aliens.draw(game_items.screen) # Display scorecard. game_items.sb.show_score() # Draw button. if not stats.game_active: game_items.play_button.draw_button() # game_items.restart_button.draw_button() # game_items.cancel_button.draw_button() # Make the most recent screen visible. pygame.display.flip() def check_keydown_events(event: EventType, ai_settings: Settings , stats: GameStats, game_items: GameItems): """Respond when key is being pressed.""" if event.key == pygame.K_RIGHT: # Move ship to the right. game_items.ship.moving_right = True elif event.key == pygame.K_LEFT: # Move ship to the left. game_items.ship.moving_left = True elif event.key == pygame.K_SPACE: fire_bullet(ai_settings, game_items) elif event.key == pygame.K_q: quit_game(stats) elif event.key == pygame.K_RETURN: # ENTER key start_new_game(ai_settings, stats, game_items) def check_keyup_events(event: EventType, ship: Ship): """Respond when key is stopped being pressed.""" if event.key == pygame.K_RIGHT: ship.moving_right = False elif event.key == pygame.K_LEFT: ship.moving_left = False def check_mousedown_events(ai_settings: Settings, stats: GameStats, game_items: GameItems): """Respond when mouse button is pressed.""" mouse_x, mouse_y = pygame.mouse.get_pos() # Start new game when "Play" button is clicked. play_button_clicked = game_items.play_button.rect.collidepoint(mouse_x, mouse_y) if play_button_clicked: start_new_game(ai_settings, stats, game_items) def start_new_game(ai_settings, stats, game_items): """Start a new game when the player clicks play_button or presses Enter key.""" if not stats.game_active: # Resets game statistics. stats.reset_stats() stats.game_active = True ai_settings.initialize_dynamic_settings() # Reset scoreboard. game_items.sb.prep_score() game_items.sb.prep_high_score() game_items.sb.prep_level() game_items.sb.prep_ships() # Empty bullets and aliens group. game_items.bullets.empty() game_items.aliens.empty() # Create new fleet and center the ship. create_fleet(ai_settings, game_items) game_items.ship.center_ship() # Hide mouse. pygame.mouse.set_visible(False) def update_bullets(ai_settings: Settings, stats: GameStats, game_items: GameItems): """Update the status and position of bullets.""" game_items.bullets.update(stats) # Get rid of bullets that have disappeared. for bullet in game_items.bullets.copy(): if bullet.rect.bottom <= 0: game_items.bullets.remove(bullet) check_bullet_alien_collision(ai_settings, stats, game_items) def check_bullet_alien_collision(ai_settings: Settings, stats: GameStats, game_items: GameItems): """Update the game when a bullet hits an alien(s).""" # Get rid of bullet and aliens that have collided. collision = pygame.sprite.groupcollide(game_items.bullets, game_items.aliens, True, True) if collision: for aliens_hit_list in collision.values(): stats.score += ai_settings.alien_points * len(aliens_hit_list) game_items.sb.prep_score() check_high_score(stats, game_items) # Create new fleet after fleet is empty. if len(game_items.aliens.sprites()) == 0: game_items.bullets.empty() ai_settings.increase_speed() stats.level += 1 game_items.sb.prep_level() create_fleet(ai_settings, game_items) def update_aliens(ai_settings: Settings, stats: GameStats, game_items: GameItems): """Update position for each alien.""" check_fleet_edges(ai_settings, game_items.aliens) game_items.aliens.update(stats) # Collision between ship and aliens. if pygame.sprite.spritecollideany(game_items.ship, game_items.aliens): ship_hit(ai_settings, stats, game_items) check_aliens_bottom(ai_settings, stats, game_items) def fire_bullet(ai_settings: Settings, game_items: GameItems): """Fire a bullet if limit not reached.""" # Create a new bullet and add it to the bullets group. if len(game_items.bullets) < ai_settings.bullets_allowed: new_bullet = Bullet(ai_settings, game_items.screen, game_items.ship) game_items.bullets.add(new_bullet) def create_fleet(ai_settings: Settings, game_items: GameItems): """Create a full fleet of aliens.""" alien = Alien(ai_settings, game_items.screen) number_aliens_x = get_number_aliens_x(ai_settings, alien.rect.width) number_rows = get_number_rows(ai_settings, game_items.ship.rect.height, alien.rect.height) for row_number in range(number_rows): for alien_number in range(number_aliens_x): create_alien(ai_settings, game_items, alien_number, row_number) def create_alien(ai_settings: Settings, game_items: GameItems , alien_number: int, row_number: int): """Create a single Alien.""" RAND_NO_X = random.randint(-ai_settings.alien_random_x, ai_settings.alien_random_x) RAND_NO_Y = random.randint(-ai_settings.alien_random_y, ai_settings.alien_random_y) # Change directions to default. ai_settings.set_default_alien_directions() alien = Alien(ai_settings, game_items.screen) alien_width = alien.rect.width alien.x = alien_width + (ai_settings.alien_density_factor_x * alien_width * alien_number) + RAND_NO_X alien.rect.x = alien.x alien_height = alien.rect.height alien.y = 100 + (ai_settings.alien_density_factor_y * alien_height * row_number) + RAND_NO_Y alien.rect.y = alien.y alien.drop_dist = alien.y + ai_settings.alien_drop_dist game_items.aliens.add(alien) def get_number_aliens_x(ai_settings: Settings, alien_width: int): """Return number of aliens that can fit horizontally.""" available_space_x = ai_settings.screen_width - 2 * alien_width number_aliens_x = available_space_x / (alien_width * ai_settings.alien_density_factor_x) return int(number_aliens_x) def get_number_rows(ai_settings: Settings, ship_height: int, alien_height: int): """Return number of rows of aliens that can fit vertically.""" available_space_y = (ai_settings.screen_height - alien_height * ai_settings.alien_ship_dist_factor - alien_height - ship_height) number_rows = available_space_y / (alien_height * ai_settings.alien_density_factor_y) return int(number_rows) def change_fleet_directions(ai_settings: Settings, aliens: Group, direction: int): """Drop down the fleet and change the direction.""" for alien in aliens: if alien.y <= alien.drop_dist: ai_settings.alien_direction_y = 1 ai_settings.alien_direction_x = 0 else: ai_settings.alien_direction_y = 0 ai_settings.alien_direction_x = direction def check_fleet_edges(ai_settings: Settings, aliens: Group): """Respond appropriately when any alien reaches edge.""" for alien in aliens: if alien.check_edges('left'): change_fleet_directions(ai_settings, aliens, direction=+1) break elif alien.check_edges('right'): change_fleet_directions(ai_settings, aliens, direction=-1) break def ship_hit(ai_settings: Settings, stats: GameStats, game_items: GameItems): """Respond to ship being hit by an alien.""" if stats.ships_left > 0: # Decrement ships left. stats.ships_left -= 1 # Update scorecard. game_items.sb.prep_ships() # Empty bullets and aliens. game_items.bullets.empty() game_items.aliens.empty() # Create a new fleet and center the ship. create_fleet(ai_settings, game_items) game_items.ship.center_ship() # Pause. time.sleep(0.5) else: stats.game_active = False pygame.mouse.set_visible(True) def check_aliens_bottom(ai_settings: Settings, stats: GameStats, game_items: GameItems): """Check if aliens reached bottom of the screen.""" for alien in game_items.aliens: if alien.rect.bottom >= alien.screen_rect.bottom: # Behave like ship_hit. ship_hit(ai_settings, stats, game_items) break def check_high_score(stats: GameStats, game_items: GameItems): """Check if high score is achieved.""" if stats.score > stats.high_score: stats.high_score = stats.score game_items.sb.prep_high_score()