from typing import Callable, Tuple from pydantic import BaseModel from learn_sql_model.optional import _optional_import_ pygame = _optional_import_("pygame", group="game") screen_sizes = [ (480, 360), # 360p (640, 480), # VGA (800, 600), # SVGA (1024, 768), # XGA (1280, 720), # HD 720p (1366, 768), # HD 1366x768 (1600, 900), # HD+ 1600x900 (1920, 1080), # Full HD 1080p (2560, 1440), # 2K / QHD 1440p (3840, 2160), # 4K / UHD 2160p ] class MenuItem(BaseModel): display_text: str on_click: Callable = None text_color: Tuple[str, str, str] = (0, 0, 0) class Menu: def __init__(self, game): pygame.font.init() self.game = game self.hamburger = Hamburger(game) self.padding = 10 self.font_size = 50 self.line_height = 55 self.menu_width = min( max(200, self.game.screen.get_width() * 0.8), self.game.screen.get_width() ) self.menu_height = min( max(200, self.game.screen.get_height() * 0.8), self.game.screen.get_height() ) self.x = (self.game.screen.get_width() - self.menu_width) / 2 self.y = (self.game.screen.get_height() - self.menu_height) / 2 self.color = (100, 100, 100) self.is_menu_open = False self.surface = pygame.Surface((self.menu_width, self.menu_height)) self.font = pygame.font.SysFont("", self.font_size) self.screen_size_index = False @property def items(self) -> list[MenuItem]: return [ MenuItem( display_text="Menu", on_click=lambda: print("clicked on me, the menu"), ), MenuItem( display_text="Screen Size", on_click=self.next_screen_size, ), MenuItem( display_text=f"{self.game.screen.get_width()}x{self.game.screen.get_height()}", color=(50, 0, 0), on_click=self.next_screen_size, ), MenuItem( display_text=f"{self.game.player.hero.name}", color=(50, 0, 0), on_click=self.game.player.rename_hero, ), MenuItem( display_text="quit", color=(50, 0, 0), on_click=lambda: self.game.quit(), ), ] def render(self): if self.is_menu_open: self.surface.fill(self.color) pos = (self.padding, self.padding) for item in self.items: text = self.font.render(item.display_text, True, item.text_color) self.surface.blit(text, pos) pos = (pos[0], pos[1] + self.line_height) self.game.screen.blit(self.surface, (self.x, self.y)) self.hamburger.render() def next_screen_size(self): if self.screen_size_index is False: self.screen = pygame.display.set_mode(screen_sizes[0]) self.screen_size_index = 0 if self.screen_size_index == len(screen_sizes) - 1: self.screen_size_index = 0 else: self.screen_size_index += 1 self.screen = pygame.display.set_mode(screen_sizes[self.screen_size_index]) def get_mouse_pos(self): "get mouse position relative to self.surface" x, y = pygame.mouse.get_pos() return x - self.x, y - self.y def handle_events(self, events): self.hamburger.handle_events(self, events) for event in events: if event.type == pygame.MOUSEBUTTONDOWN and self.is_menu_open: if event.button == 1: # Left mouse button self.handle_click() def handle_click(self): pos = self.get_mouse_pos() pos_idx = int(pos[1] // self.line_height) if pos_idx > len(self.items): return if pos_idx < 0: return self.items[pos_idx].on_click() class Hamburger: def __init__(self, game): self.game = game self.hamburger_width = 50 self.bar_height = self.hamburger_width / 4 self.bar_spacing = self.hamburger_width / 20 self.hamburger_height = self.bar_height * 3 + self.bar_spacing * 2 self.x = self.game.screen.get_width() - self.hamburger_width - 20 self.y = 20 self.color = (100, 100, 100) self.rect = pygame.Rect( self.x, self.y, self.hamburger_width, self.hamburger_height ) self.surface = pygame.Surface((self.hamburger_width, self.hamburger_height)) def render(self): pygame.draw.rect( self.surface, self.color, (0, 0, self.hamburger_width, self.bar_height), ) pygame.draw.rect( self.surface, self.color, ( 0, self.bar_height + self.bar_spacing, self.hamburger_width, self.bar_height, ), ) pygame.draw.rect( self.surface, self.color, ( 0, 2 * (self.bar_height + self.bar_spacing), self.hamburger_width, self.bar_height, ), ) self.game.screen.blit(self.surface, (self.x, self.y)) def handle_events(self, menu: Menu, events): for event in events: if event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: # Left mouse button self.handle_click(menu) def handle_click(self, menu): pos = pygame.mouse.get_pos() if self.rect.collidepoint(pos): menu.is_menu_open = not menu.is_menu_open