Source code for game.ui.hud

"""HUD and UI elements."""

import pygame
import sys
from ..utils.constants import *
from ..utils.localization import localization_manager

[docs] class HUD: """Heads-up display for game information.""" def __init__(self): pygame.font.init()
[docs] self.font = pygame.font.Font(None, UI_FONT_SIZE)
[docs] self.button_font = pygame.font.Font(None, 20)
[docs] self.info_font = pygame.font.Font(None, 22)
[docs] self.buttons = { 'send_pig': pygame.Rect(10, SCREEN_HEIGHT - 180, BUTTON_WIDTH, BUTTON_HEIGHT), 'emergency_feast': pygame.Rect(10, SCREEN_HEIGHT - 120, BUTTON_WIDTH, BUTTON_HEIGHT), 'add_pigs': pygame.Rect(10, SCREEN_HEIGHT - 60, BUTTON_WIDTH, BUTTON_HEIGHT), 'exit': pygame.Rect(SCREEN_WIDTH - 170, SCREEN_HEIGHT - 80, 150, 60), 'restart': pygame.Rect(SCREEN_WIDTH - 170, SCREEN_HEIGHT - 80, 150, 60) }
[docs] def handle_click(self, pos, game_manager, game_state_str): if game_state_str == "MAIN_MENU": if self.buttons['exit'].collidepoint(pos): game_manager.quit_game() elif game_state_str == "PLAYING": if self.buttons['send_pig'].collidepoint(pos): game_manager.send_pig_away() elif self.buttons['emergency_feast'].collidepoint(pos): game_manager.emergency_feast() elif self.buttons['add_pigs'].collidepoint(pos): game_manager.add_pigs(5) elif game_state_str in ["PAUSED", "GAME_OVER"]: if self.buttons['restart'].collidepoint(pos): game_manager.restart_game()
[docs] def render(self, screen, game_state): """Render the HUD.""" # Only render game info and action buttons if we are NOT on the main menu if not game_state.get('main_menu', False): self._render_info_panel(screen, game_state) self._render_buttons(screen, game_state) # Render overlays and context-specific buttons based on the current state if game_state.get('paused', False): self._render_pause_overlay(screen) self._draw_restart_button(screen) # Draw RESTART button elif game_state.get('game_over', False): self._render_game_over_overlay(screen, game_state) self._draw_restart_button(screen) # Draw RESTART button elif game_state.get('main_menu', False): self._draw_exit_button(screen) # Draw EXIT button
def _render_info_panel(self, screen, game_state): """Render a compact game information panel with a design similar to the stability graph.""" # Define panel properties to be small and unobtrusive panel_width = 200 panel_height = 85 panel_rect = pygame.Rect(10, 10, panel_width, panel_height) # Draw semi-transparent background, matching the graph style panel_surface = pygame.Surface(panel_rect.size, pygame.SRCALPHA) panel_surface.fill((20, 20, 20, 180)) screen.blit(panel_surface, panel_rect.topleft) # --- Render Text using the new smaller font --- line_height = self.info_font.get_linesize() start_x = panel_rect.x + 10 start_y = panel_rect.y + 10 # Score score_text = self.info_font.render(localization_manager.get('score_label', score=game_state['score']), True, (255, 255, 255)) screen.blit(score_text, (start_x, start_y)) # Time remaining - Pass the raw number, not a pre-formatted string time_text = self.info_font.render(localization_manager.get('time_label', time=game_state['time_remaining']), True, (255, 255, 255)) screen.blit(time_text, (start_x, start_y + line_height)) # Feast cooldown if game_state['feast_cooldown'] > 0: # Pass the raw number here as well cooldown_text = self.info_font.render(localization_manager.get('feast_cd_label', cooldown=game_state['feast_cooldown']), True, (255, 200, 200)) screen.blit(cooldown_text, (start_x, start_y + line_height * 2)) def _render_buttons(self, screen, game_state): """Render action buttons for gameplay ONLY.""" # Send Pig button pygame.draw.rect(screen, (0, 150, 0), self.buttons['send_pig']) pygame.draw.rect(screen, (255, 255, 255), self.buttons['send_pig'], 2) send_text = self.button_font.render(localization_manager.get('send_pig_button'), True, (255, 255, 255)) screen.blit(send_text, send_text.get_rect(center=self.buttons['send_pig'].center)) # Emergency Feast button pygame.draw.rect(screen, (150, 0, 0), self.buttons['emergency_feast']) pygame.draw.rect(screen, (255, 255, 255), self.buttons['emergency_feast'], 2) feast_text = self.button_font.render(localization_manager.get('feast_button'), True, (255, 255, 255)) screen.blit(feast_text, feast_text.get_rect(center=self.buttons['emergency_feast'].center)) # Add Pigs button pygame.draw.rect(screen, (0, 100, 150), self.buttons['add_pigs']) pygame.draw.rect(screen, (255, 255, 255), self.buttons['add_pigs'], 2) add_text = self.button_font.render(localization_manager.get('add_pigs_button'), True, (255, 255, 255)) screen.blit(add_text, add_text.get_rect(center=self.buttons['add_pigs'].center)) def _render_degradation_bar(self, screen, game_state): """Render environmental degradation bar.""" bar_rect = pygame.Rect(SCREEN_WIDTH - 220, 10, 200, 20) pygame.draw.rect(screen, (255, 255, 255), bar_rect, 1) fill_width = (1.0 - game_state['degradation']) * (bar_rect.width - 2) color = pygame.Color(0, 255, 0).lerp(pygame.Color(255, 0, 0), game_state['degradation']) fill_rect = pygame.Rect(bar_rect.x + 1, bar_rect.y + 1, fill_width, 18) pygame.draw.rect(screen, color, fill_rect) label = self.font.render(localization_manager.get('health_label'), True, (255, 255, 255)) screen.blit(label, (SCREEN_WIDTH - 220, 35)) def _render_pause_overlay(self, screen): """Render the pause screen overlay.""" overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA) overlay.fill((0, 0, 0, 128)) screen.blit(overlay, (0, 0)) pause_text = pygame.font.Font(None, 72).render(localization_manager.get('paused_title'), True, (255, 255, 255)) screen.blit(pause_text, pause_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2))) instruction_text = self.font.render(localization_manager.get('resume_instruction'), True, (255, 255, 255)) inst_rect = instruction_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 50)) screen.blit(instruction_text, inst_rect) def _render_game_over_overlay(self, screen, game_state): """Render the game over screen overlay, now with high scores.""" overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA) overlay.fill((0, 0, 0, 180)) screen.blit(overlay, (0, 0)) # --- Game Over Title --- game_over_text = pygame.font.Font(None, 72).render(localization_manager.get('game_over_title'), True, (255, 255, 255)) text_rect = game_over_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 4)) screen.blit(game_over_text, text_rect) # --- Win/Loss Message --- if game_state.get('is_win', False): end_msg = localization_manager.get('win_message') color = (152, 251, 152) # Light green for win else: end_msg = localization_manager.get('lose_message') color = (255, 182, 193) # Light red for lose end_text = pygame.font.Font(None, 36).render(end_msg, True, color) end_rect = end_text.get_rect(center=(SCREEN_WIDTH // 2, text_rect.bottom + 40)) screen.blit(end_text, end_rect) # --- Final Score --- score_text = pygame.font.Font(None, 48).render(localization_manager.get('final_score_label', score=game_state['score']), True, (255, 255, 255)) score_rect = score_text.get_rect(center=(SCREEN_WIDTH // 2, end_rect.bottom + 40)) screen.blit(score_text, score_rect) # --- Performance Feedback --- y_offset = score_rect.bottom + 30 # Environment Feedback final_degradation = game_state.get('final_degradation') if final_degradation is not None: if final_degradation < 0.25: env_key = 'env_perf_excellent' elif final_degradation < 0.5: env_key = 'env_perf_good' elif final_degradation < 0.9: env_key = 'env_perf_bad' else: env_key = 'env_perf_disaster' env_text = self.info_font.render(localization_manager.get(env_key), True, (220, 220, 220)) env_rect = env_text.get_rect(center=(SCREEN_WIDTH // 2, y_offset)) screen.blit(env_text, env_rect) y_offset += self.info_font.get_linesize() # Happiness Feedback final_happiness = game_state.get('final_happiness') if final_happiness is not None: if final_happiness >= 0.75: hap_key = 'hap_perf_ecstatic' elif final_happiness >= 0.5: hap_key = 'hap_perf_content' elif final_happiness >= 0.25: hap_key = 'hap_perf_unhappy' else: hap_key = 'hap_perf_miserable' hap_text = self.info_font.render(localization_manager.get(hap_key), True, (220, 220, 220)) hap_rect = hap_text.get_rect(center=(SCREEN_WIDTH // 2, y_offset)) screen.blit(hap_text, hap_rect) y_offset += self.info_font.get_linesize() # --- High Scores Display --- high_scores_title = pygame.font.Font(None, 48).render(localization_manager.get('high_scores_title'), True, (255, 255, 255)) hs_title_rect = high_scores_title.get_rect(center=(SCREEN_WIDTH // 2, y_offset + 40)) screen.blit(high_scores_title, hs_title_rect) y_offset = hs_title_rect.bottom + 10 # Loop through only the top 3 scores for i, entry in enumerate(game_state.get('high_scores', [])[:3]): name = entry.get('name', '???') score = entry.get('score', 0) score_entry_text = f"{i + 1}. {name} - {score}" score_entry = self.info_font.render(score_entry_text, True, (200, 200, 200)) entry_rect = score_entry.get_rect(center=(SCREEN_WIDTH // 2, y_offset)) screen.blit(score_entry, entry_rect) y_offset += self.info_font.get_linesize() def _draw_exit_button(self, screen): """Draws the exit button.""" rect = self.buttons['exit'] mouse_pos = pygame.mouse.get_pos() color = (200, 80, 80) if rect.collidepoint(mouse_pos) else (150, 50, 50) pygame.draw.rect(screen, color, rect, border_radius=15) pygame.draw.rect(screen, (255, 255, 255), rect, 3, border_radius=15) text_surf = self.button_font.render(localization_manager.get('exit_button', default="Exit"), True, (255, 255, 255)) text_rect = text_surf.get_rect(center=rect.center) screen.blit(text_surf, text_rect) def _draw_restart_button(self, screen): """Draws the restart button.""" rect = self.buttons['restart'] mouse_pos = pygame.mouse.get_pos() color = (200, 150, 0) if rect.collidepoint(mouse_pos) else (150, 100, 0) pygame.draw.rect(screen, color, rect, border_radius=15) pygame.draw.rect(screen, (255, 255, 255), rect, 3, border_radius=15) text_surf = self.button_font.render(localization_manager.get('restart_button', default="Restart"), True, (255, 255, 255)) text_rect = text_surf.get_rect(center=rect.center) screen.blit(text_surf, text_rect)