""" ------------------------------------------------------------------------ Name: Gavin Paki Paki Project: Target shooter game Standard: 91896 (AS2.7) V.1 School: Tauranga Boys' College Python: 3.11.9 ------------------------------------------------------------------------ """ import pygame import random import sys # Initialize Pygame pygame.init() # Screen dimensions WIDTH, HEIGHT = 1920, 1080 screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Target Shooter Game") # Colours WHITE = (255, 255, 255) BLACK = (0, 0, 0) RED = (255, 0, 0) BLUE = (0, 0, 255) # Fonts font = pygame.font.Font(None, 74) small_font = pygame.font.Font(None, 36) # Clock clock = pygame.time.Clock() # Game variables target_spawn_rate = 1000 # in milliseconds last_shot_time = 0 game_active = True game_duration = 100 # 100 seconds (1 minute 40 seconds) target_counter = 0 # Variable for however many targets are shown ingame score = 0 # Total score for one game total_clicks = 0 # Total amount of clicks pressed ingame successful_hits = 0 # Total amount of clicks that connected with a target (Used for hit accuracy) # Range for the difficulties in the game difficulty = { "Level 1": {"speed_range": (1, 3), "spawn_rate": 1500}, "Level 2": {"speed_range": (1, 5), "spawn_rate": 1000}, "Level 3": {"speed_range": (3, 7), "spawn_rate": 500}, } current_difficulty = "Level 1" # Difficulty game starts with # Define class for targets class Target(pygame.sprite.Sprite): def __init__(self, is_multiplier=False): super().__init__() if is_multiplier: self.image = pygame.Surface((40, 40)) # Smaller sized multiplier target self.image.fill(BLUE) self.multiplier = 5 # Multiplier target's worth 5 points self.speed = random.randint(7, 7) # speed up the multiplier target else: self.image = pygame.Surface((55, 55)) self.image.fill(RED) self.multiplier = 1 # Regular target's worth 1 point self.speed = random.randint(*difficulty[current_difficulty]["speed_range"]) self.rect = self.image.get_rect() self.rect.x = random.randint(0, WIDTH - self.rect.width) self.rect.y = random.randint(-100, -self.rect.height) # Target's spawn above the screen and fall into view def update(self): self.rect.y += self.speed if self.rect.y > HEIGHT: # Target reaches the bottom of the screen self.rect.y = -self.rect.height self.rect.x = random.randint(0, WIDTH - self.rect.width) self.speed = random.randint(*difficulty[current_difficulty]["speed_range"]) if game_active: subtract_points(self.multiplier) # Subtract points based on the target's multiplier if ingame def subtract_points(points): global score score -= points if score < 0: score = 0 # Sprite groups all_sprites = pygame.sprite.Group() # All of the sprites ingame targets = pygame.sprite.Group() # The targets encountered ingame menu_targets = pygame.sprite.Group() # aesthetic targets in the menu raining down # Load the crosshair image and scale to fit crosshair_image = pygame.image.load("crosshair.png") crosshair_image = pygame.transform.scale(crosshair_image, (40, 40)) crosshair_rect = crosshair_image.get_rect() def main_menu(): # Timer for main menu aesthetic target spawns pygame.time.set_timer(pygame.USEREVENT + 2, 500) # Spawn a new target every 500 milliseconds for the menu # Initialize the counter for spawning multiplier targets menu_target_counter = 0 while True: screen.fill(WHITE) for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_RETURN: pygame.time.set_timer(pygame.USEREVENT + 2, 0) # Stop spawning targets for the menu menu_targets.empty() # Clear the menu targets return elif event.key == pygame.K_r: pygame.time.set_timer(pygame.USEREVENT + 2, 0) show_rules() pygame.time.set_timer(pygame.USEREVENT + 2, 500) # Resume spawning targets after showing rules elif event.key == pygame.K_s: pygame.time.set_timer(pygame.USEREVENT + 2, 0) show_settings() pygame.time.set_timer(pygame.USEREVENT + 2, 500) # Resume spawning targets after showing settings elif event.type == pygame.USEREVENT + 2: menu_target_counter += 1 is_multiplier = (menu_target_counter % 10 == 0) target = Target(is_multiplier) menu_targets.add(target) # Update and draw menu targets in the background menu_targets.update() menu_targets.draw(screen) # Draw the menu text title = font.render("Target shooter Game", True, BLACK) play_text = small_font.render("Press ENTER to Play", True, BLACK) rules_text = small_font.render("Press R to View Rules", True, BLACK) settings_text = small_font.render("Press S for Settings", True, BLACK) screen.blit(title, (WIDTH // 2 - title.get_width() // 2, HEIGHT // 2 - title.get_height() // 2 - 100)) screen.blit(play_text, (WIDTH // 2 - play_text.get_width() // 2, HEIGHT // 2 - play_text.get_height() // 2)) screen.blit(rules_text, (WIDTH // 2 - rules_text.get_width() // 2, HEIGHT // 2 - rules_text.get_height() // 2 + 50)) screen.blit(settings_text, (WIDTH // 2 - settings_text.get_width() // 2, HEIGHT // 2 - settings_text.get_height() // 2 + 100)) # Update the display pygame.display.flip() # Limit the frame rate clock.tick(60) # Rules tab def show_rules(): while True: screen.fill(WHITE) rules_title = font.render("Game Rules", True, BLACK) rules = [ # Text to be displayed on the rules tab "Aim at the targets with your mouse and use Lmb to shoot.", "You need 75 points to win.", "Blue targets are worth 5 points.", "Red targets are worth 1 point.", "Blue targets move faster than reds, and are smaller", "If a red target reaches the bottom, you lose a point.", "If A blue target reaches the bottom, you lose 5 points", "Every time you complete a level (Start at level 1) You should move on to the next.", "Good luck!" ] screen.blit(rules_title, (WIDTH // 2 - rules_title.get_width() // 2, 50)) for i, rule in enumerate(rules): rule_text = small_font.render(rule, True, BLACK) screen.blit(rule_text, (WIDTH // 2 - rule_text.get_width() // 2, 150 + i * 40)) back_text = small_font.render("Press B to go back", True, BLACK) screen.blit(back_text, (WIDTH // 2 - back_text.get_width() // 2, HEIGHT - 100)) pygame.display.flip() for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_b: return # Settings tab def show_settings(): global current_difficulty, target_spawn_rate while True: screen.fill(WHITE) settings_title = font.render("Settings", True, BLACK) difficulty_text = small_font.render(f"Difficulty: {current_difficulty}", True, BLACK) change_difficulty_text = small_font.render("Press D to Change Level, move up to the next level after a game to advance!", True, BLACK) back_text = small_font.render("Press B to go back", True, BLACK) screen.blit(settings_title, (WIDTH // 2 - settings_title.get_width() // 2, 50)) screen.blit(difficulty_text, (WIDTH // 2 - difficulty_text.get_width() // 2, 150)) screen.blit(change_difficulty_text, (WIDTH // 2 - change_difficulty_text.get_width() // 2, 200)) screen.blit(back_text, (WIDTH // 2 - back_text.get_width() // 2, HEIGHT - 100)) pygame.display.flip() for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_b: return elif event.key == pygame.K_d: # Cycle through difficulty levels difficulty_levels = list(difficulty.keys()) current_index = difficulty_levels.index(current_difficulty) current_difficulty = difficulty_levels[(current_index + 1) % len(difficulty_levels)] target_spawn_rate = difficulty[current_difficulty]["spawn_rate"] # draw the Game over screen def game_over_screen(score): if score >= 75: message = "Game over. You Win!" else: message = "Game over. You Lose!" hit_accuracy = (successful_hits / total_clicks) * 100 if total_clicks > 0 else 0 # Calculate the target shooting accuracy while True: screen.fill(WHITE) message_text = font.render(message, True, BLACK) score_text = small_font.render(f"Score: {score}", True, BLACK) accuracy_text = small_font.render(f"Hit Accuracy: {hit_accuracy:.2f}%", True, BLACK) play_again_text = small_font.render("Press ENTER to play again", True, BLACK) screen.blit(message_text, (WIDTH // 2 - message_text.get_width() // 2, HEIGHT // 2 - message_text.get_height() // 2 - 100)) screen.blit(score_text, (WIDTH // 2 - score_text.get_width() // 2, HEIGHT // 2 - score_text.get_height() // 2)) screen.blit(accuracy_text, (WIDTH // 2 - accuracy_text.get_width() // 2, HEIGHT // 2 - accuracy_text.get_height() // 2 + 50)) screen.blit(play_again_text, (WIDTH // 2 - play_again_text.get_width() // 2, HEIGHT // 2 - play_again_text.get_height() // 2 + 100)) pygame.display.flip() for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_RETURN: return # Main game loop, contains more variables, backgrounds and the a check timer function. def game(): global last_shot_time, game_active, target_counter, score, total_clicks, successful_hits score = 0 target_counter = 0 total_clicks = 0 successful_hits = 0 all_sprites.empty() targets.empty() game_active = True pygame.mouse.set_visible(False) start_time = pygame.time.get_ticks() pygame.time.set_timer(pygame.USEREVENT + 1, target_spawn_rate) while game_active: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() elif event.type == pygame.USEREVENT + 1: target_counter += 1 is_multiplier = (target_counter % 10 == 0) target = Target(is_multiplier) all_sprites.add(target) targets.add(target) elif event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: total_clicks += 1 for target in targets: if target.rect.collidepoint(event.pos): score += target.multiplier # Apply the multiplier successful_hits += 1 target.kill() crosshair_rect.center = pygame.mouse.get_pos() all_sprites.update() # Check timer elapsed_time = (pygame.time.get_ticks() - start_time) / 1000 remaining_time = max(0, game_duration - elapsed_time) if remaining_time <= 0: game_active = False pygame.mouse.set_visible(True) game_over_screen(score) break # Draw the background and texts screen.fill(WHITE) all_sprites.draw(screen) screen.blit(crosshair_image, crosshair_rect) score_text = small_font.render(f"Score: {score}", True, BLACK) screen.blit(score_text, (10, 10)) timer_text = small_font.render(f"Time: {int(remaining_time)}", True, BLACK) screen.blit(timer_text, (WIDTH - 150, 10)) pygame.display.flip() clock.tick(60) while True: main_menu() game()