/
ap1k
/
PyGame_Practice
Обзор
Документация
Войти
/
ap1k
/
PyGame_Practice
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/scenes/leaderboard_scene.py
306 строк
14 KB
Roman
The all game
27 май 2025, 15:26
27 май 2025, 15:26
4ea7de9
Код
Авторство
О чём код?
""" LeaderboardScene class - displays the leaderboard and allows entering a name for high scores """ import pygame import math import time from src.core.scene import Scene from src.utils.resource_loader import ResourceLoader class LeaderboardScene(Scene): """ Leaderboard scene Implements the Scene interface """ def __init__(self, game): super().__init__(game) # Fonts self.font_small = ResourceLoader().get_font("small") self.font_medium = ResourceLoader().get_font("medium") self.font_large = ResourceLoader().get_font("large") # Background self.background = ResourceLoader().get_image("background") # Player name input self.player_name = "" self.input_active = True self.input_done = False # Check if the current score is a high score self.is_high_score = self._check_high_score() def _check_high_score(self): """Check if the current score is a high score""" # If there are fewer than 10 scores, it's automatically a high score if len(self.game.state.leaderboard) < 10: return True # Check if the current score is higher than the lowest score on the leaderboard if self.game.state.score > min(entry["score"] for entry in self.game.state.leaderboard): return True return False def update(self): """Update the leaderboard scene""" # Nothing to update if not waiting for input if not self.input_active or self.input_done: return def render(self, screen): """Render the leaderboard scene""" config = self.game.config # Draw the background with a starfield effect screen.blit(self.background, (0, 0)) # Add some moving stars in the background current_time = pygame.time.get_ticks() for i in range(20): # Calculate star position based on time x = (self.game.width // 2) + int(math.cos(current_time / 1000 + i) * 300) y = (self.game.height // 2) + int(math.sin(current_time / 1000 + i) * 200) # Only draw stars within the screen bounds if 0 <= x < self.game.width and 0 <= y < self.game.height: # Pulsating star size size = 1 + int(math.sin(current_time / 200 + i * 0.5) * 1.5) pygame.draw.circle(screen, config.colors["white"], (x, y), size) # Draw a decorative header bar header_height = 100 header = pygame.Surface((self.game.width, header_height)) header.fill(config.colors["dark_blue"]) # Add a gradient to the header for i in range(header_height): line_color = (25, 25, 112, 255 - i * 2) # Fade out the dark blue pygame.draw.line(header, line_color, (0, i), (self.game.width, i)) screen.blit(header, (0, 0)) # Draw the leaderboard title with a glow effect pulse = (math.sin(current_time / 300) + 1) / 4 + 0.5 # Value between 0.5 and 1 title_color = (int(255 * pulse), int(215 * pulse), 0) # Pulsating gold title_text = self.font_large.render("LEADERBOARD", True, title_color) title_shadow = self.font_large.render("LEADERBOARD", True, config.colors["black"]) title_rect = title_text.get_rect(center=(self.game.width // 2, 50)) screen.blit(title_shadow, (title_rect.x + 2, title_rect.y + 2)) # Shadow screen.blit(title_text, title_rect) # If we're waiting for the player to enter their name if self.input_active and self.is_high_score and not self.input_done: self._render_name_input(screen) else: # Draw the leaderboard entries self._render_leaderboard(screen) # Draw the instructions to restart with a pulsating effect pulse = (math.sin(current_time / 500) + 1) / 2 # Value between 0 and 1 instruction_color = (255, 255, int(pulse * 255)) # Pulsating cyan restart_text = self.font_medium.render("Press SPACE to play again", True, instruction_color) restart_rect = restart_text.get_rect(center=(self.game.width // 2, self.game.height - 50)) screen.blit(restart_text, restart_rect) def _render_name_input(self, screen): """Render the name input screen""" config = self.game.config current_time = pygame.time.get_ticks() # Create a semi-transparent panel for the input form panel_width, panel_height = 400, 300 panel_x = (self.game.width - panel_width) // 2 panel_y = (self.game.height - panel_height) // 2 - 30 panel = pygame.Surface((panel_width, panel_height), pygame.SRCALPHA) panel.fill((0, 0, 50, 180)) # Semi-transparent dark blue # Add a border to the panel with a pulsating effect pulse = (math.sin(current_time / 300) + 1) / 2 # Value between 0 and 1 border_color = (int(200 * pulse) + 55, int(200 * pulse) + 55, 255) # Pulsating blue-white # Draw the panel with border screen.blit(panel, (panel_x, panel_y)) pygame.draw.rect(screen, border_color, (panel_x, panel_y, panel_width, panel_height), 3) # Draw the high score message with a glowing effect high_score_text = self.font_large.render("New High Score!", True, config.colors["yellow"]) high_score_rect = high_score_text.get_rect(center=(self.game.width // 2, panel_y + 50)) # Add a shadow for depth shadow_text = self.font_large.render("New High Score!", True, config.colors["black"]) screen.blit(shadow_text, (high_score_rect.x + 2, high_score_rect.y + 2)) screen.blit(high_score_text, high_score_rect) # Draw the score with a colorful effect score_color = (255, int(200 * pulse) + 55, 0) # Pulsating orange score_text = self.font_medium.render(f"Score: {self.game.state.score}", True, score_color) score_rect = score_text.get_rect(center=(self.game.width // 2, panel_y + 100)) screen.blit(score_text, score_rect) # Draw the name input prompt prompt_text = self.font_medium.render("Enter your name:", True, config.colors["white"]) prompt_rect = prompt_text.get_rect(center=(self.game.width // 2, panel_y + 150)) screen.blit(prompt_text, prompt_rect) # Draw the name input box with a glowing effect input_box = pygame.Rect(self.game.width // 2 - 120, panel_y + 180, 240, 40) pygame.draw.rect(screen, border_color, input_box, 2) # Fill the input box with a semi-transparent background input_bg = pygame.Surface((236, 36), pygame.SRCALPHA) input_bg.fill((255, 255, 255, 30)) # Very light semi-transparent white screen.blit(input_bg, (input_box.x + 2, input_box.y + 2)) # Draw the entered name name_text = self.font_medium.render(self.player_name, True, config.colors["white"]) name_rect = name_text.get_rect(center=(self.game.width // 2, panel_y + 200)) screen.blit(name_text, name_rect) # Draw a blinking cursor at the end of the name if int(current_time / 500) % 2 == 0: # Blink every 500ms cursor_x = name_rect.right + 2 pygame.draw.line(screen, config.colors["white"], (cursor_x, panel_y + 190), (cursor_x, panel_y + 210), 2) # Draw the instructions instructions_text = self.font_small.render("Press ENTER when done", True, config.colors["light_blue"]) instructions_rect = instructions_text.get_rect(center=(self.game.width // 2, panel_y + 240)) screen.blit(instructions_text, instructions_rect) def _render_leaderboard(self, screen): """Render the leaderboard entries""" config = self.game.config current_time = pygame.time.get_ticks() # Create a semi-transparent panel for the leaderboard panel_width, panel_height = 700, 400 panel_x = (self.game.width - panel_width) // 2 panel_y = 120 panel = pygame.Surface((panel_width, panel_height), pygame.SRCALPHA) panel.fill((0, 0, 50, 150)) # Semi-transparent dark blue screen.blit(panel, (panel_x, panel_y)) # Add a decorative border to the panel pygame.draw.rect(screen, config.colors["light_blue"], (panel_x, panel_y, panel_width, panel_height), 2) # Draw column headers with a gradient background header_height = 40 header_bg = pygame.Surface((panel_width - 4, header_height), pygame.SRCALPHA) for i in range(header_height): alpha = 150 - i * 3 # Gradient transparency pygame.draw.line(header_bg, (0, 100, 200, alpha), (0, i), (panel_width - 4, i)) screen.blit(header_bg, (panel_x + 2, panel_y + 2)) # Draw column headers headers = ["Rank", "Name", "Score", "Enemies", "Time"] header_widths = [0.1, 0.3, 0.2, 0.2, 0.2] # Proportion of panel width header_positions = [] current_x = panel_x + 10 for i, (header, width) in enumerate(zip(headers, header_widths)): header_width = int(panel_width * width) header_positions.append(current_x + header_width // 2) header_text = self.font_medium.render(header, True, config.colors["yellow"]) screen.blit(header_text, (header_positions[i] - header_text.get_width() // 2, panel_y + 10)) current_x += header_width # Draw horizontal separator line pygame.draw.line(screen, config.colors["light_blue"], (panel_x + 2, panel_y + header_height + 2), (panel_x + panel_width - 2, panel_y + header_height + 2), 1) # Draw the leaderboard entries y_position = panel_y + header_height + 10 row_height = 35 for i, entry in enumerate(self.game.state.leaderboard): # Highlight alternate rows for better readability if i % 2 == 0: row_bg = pygame.Surface((panel_width - 4, row_height), pygame.SRCALPHA) row_bg.fill((255, 255, 255, 20)) # Very light semi-transparent white screen.blit(row_bg, (panel_x + 2, y_position)) # Highlight the player's score if it matches is_player_score = (self.game.state.score == entry["score"] and not self.input_active and self.is_high_score) text_color = config.colors["orange"] if is_player_score else config.colors["white"] # Add a pulsating effect to the player's score if is_player_score: pulse = (math.sin(current_time / 300) + 1) / 2 # Value between 0 and 1 text_color = (255, int(200 * pulse) + 55, 0) # Pulsating orange # Rank with medal emoji for top 3 rank_prefix = "" if i == 0: rank_prefix = "🥇 " elif i == 1: rank_prefix = "🥈 " elif i == 2: rank_prefix = "🥉 " rank_text = self.font_small.render(f"{rank_prefix}{i+1}", True, text_color) screen.blit(rank_text, (header_positions[0] - rank_text.get_width() // 2, y_position + 5)) # Name name_text = self.font_small.render(entry["name"], True, text_color) screen.blit(name_text, (header_positions[1] - name_text.get_width() // 2, y_position + 5)) # Score score_text = self.font_small.render(f"{entry['score']}", True, text_color) screen.blit(score_text, (header_positions[2] - score_text.get_width() // 2, y_position + 5)) # Enemies enemies_text = self.font_small.render(f"{entry['enemies']}", True, text_color) screen.blit(enemies_text, (header_positions[3] - enemies_text.get_width() // 2, y_position + 5)) # Time - format as MM:SS minutes = int(entry['time']) // 60 seconds = int(entry['time']) % 60 time_text = self.font_small.render(f"{minutes:02d}:{seconds:02d}", True, text_color) screen.blit(time_text, (header_positions[4] - time_text.get_width() // 2, y_position + 5)) y_position += row_height # Don't render more rows than can fit in the panel if y_position > panel_y + panel_height - row_height: break def handle_event(self, event): """Handle pygame events""" if self.input_active and self.is_high_score and not self.input_done: # Handle name input if event.type == pygame.KEYDOWN: if event.key == pygame.K_RETURN: # Submit the name if self.player_name: self.game.state.add_to_leaderboard(self.player_name) self.input_active = False self.input_done = True elif event.key == pygame.K_BACKSPACE: # Remove the last character self.player_name = self.player_name[:-1] else: # Add the character if it's a valid input and the name isn't too long if len(self.player_name) < 15 and event.unicode.isalnum(): self.player_name += event.unicode else: # Handle restart if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE: # Reset the game state self.game.state.reset() # Change to the game scene self.game.change_scene("game")