import pygame from engine.plants import RARITY_COLORS, plant_rarity from ui.scaling import make_window, request_quit, to_logical WIN_W, WIN_H = 1040, 720 BG = (24, 30, 24) TEXT = (230, 235, 230) MUTED = (150, 155, 150) HINT = (255, 220, 120) BTN = (60, 90, 60) BTN_HOVER = (85, 125, 85) DANGER = (150, 70, 70) DANGER_HOVER = (180, 90, 90) class ConfirmScreen: """A simple yes/no popup (e.g. "really leave the tower?").""" def __init__(self, title, message, yes_label="Leave", no_label="Stay"): pygame.init() self.window, self.scale = make_window(WIN_W, WIN_H) self.screen = pygame.Surface((WIN_W, WIN_H)) pygame.display.set_caption("Flora — Tower") self.clock = pygame.time.Clock() self.font = pygame.font.SysFont("segoeui", 30) self.bold = pygame.font.SysFont("segoeui", 20, bold=True) self.small = pygame.font.SysFont("segoeui", 16) self.tiny = pygame.font.SysFont("segoeui", 13) self.title = title self.message = message self.yes_label = yes_label self.no_label = no_label self.yes_rect = pygame.Rect(WIN_W // 2 + 16, WIN_H - 96, 180, 44) self.no_rect = pygame.Rect(WIN_W // 2 - 196, WIN_H - 96, 180, 44) def run(self): running = True result = False while running: self.clock.tick(60) for event in pygame.event.get(): if event.type == pygame.QUIT: request_quit() running = False elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: pos = to_logical(event.pos, self.scale) if self.yes_rect.collidepoint(pos): result = True running = False elif self.no_rect.collidepoint(pos): result = False running = False self._draw() pygame.transform.smoothscale(self.screen, self.window.get_size(), self.window) pygame.display.flip() return result def _draw(self): self.screen.fill(BG) title = self.font.render(self.title, True, HINT) self.screen.blit(title, title.get_rect(center=(WIN_W // 2, 100))) for i, line in enumerate(self.message.split("\n")): t = self.small.render(line, True, TEXT) self.screen.blit(t, t.get_rect(center=(WIN_W // 2, 150 + i * 26))) self._button(self.no_rect, self.no_label, BTN, BTN_HOVER) self._button(self.yes_rect, self.yes_label, DANGER, DANGER_HOVER) def _button(self, rect, label, base, hover): mouse = pygame.mouse.get_pos() bg = hover if rect.collidepoint(mouse) else base pygame.draw.rect(self.screen, bg, rect, border_radius=8) pygame.draw.rect(self.screen, (150, 190, 150), rect, 2, border_radius=8) t = self.bold.render(label, True, TEXT) self.screen.blit(t, t.get_rect(center=rect.center)) class TowerScreen: def __init__(self, stage, total_stages, reward_text, totals_text, mode="battle", enemies=()): pygame.init() self.window, self.scale = make_window(WIN_W, WIN_H) self.screen = pygame.Surface((WIN_W, WIN_H)) pygame.display.set_caption("Flora — Tower") self.clock = pygame.time.Clock() self.font = pygame.font.SysFont("segoeui", 36) self.bold = pygame.font.SysFont("segoeui", 20, bold=True) self.small = pygame.font.SysFont("segoeui", 16) self.tiny = pygame.font.SysFont("segoeui", 13) self.stage = stage self.total = total_stages self.reward_text = reward_text self.totals_text = totals_text self.mode = mode # battle | lost | cleared | summary self.enemies = list(enemies) self.left_rect = pygame.Rect(WIN_W // 2 - 320, WIN_H - 90, 200, 44) self.mid_rect = pygame.Rect(WIN_W // 2 - 100, WIN_H - 90, 200, 44) self.right_rect = pygame.Rect(WIN_W // 2 + 120, WIN_H - 90, 200, 44) def run(self): running = True result = "exit" while running: self.clock.tick(60) for event in pygame.event.get(): if event.type == pygame.QUIT: request_quit() running = False elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: pos = to_logical(event.pos, self.scale) if self.mode == "battle": if self.left_rect.collidepoint(pos): result = "exit" running = False elif self.mid_rect.collidepoint(pos): result = "adjust" running = False elif self.right_rect.collidepoint(pos): result = "next" running = False elif self.mode == "lost": if self.left_rect.collidepoint(pos): result = "retry" running = False elif self.mid_rect.collidepoint(pos): result = "change" running = False elif self.right_rect.collidepoint(pos): result = "giveup" running = False else: result = "exit" running = False self._draw() pygame.transform.smoothscale(self.screen, self.window.get_size(), self.window) pygame.display.flip() return result def _draw(self): self.screen.fill(BG) if self.mode == "battle": self._draw_battle() elif self.mode == "lost": self._draw_lost() else: self._draw_summary() def _button(self, rect, label, base, hover): mouse = pygame.mouse.get_pos() bg = hover if rect.collidepoint(mouse) else base pygame.draw.rect(self.screen, bg, rect, border_radius=8) pygame.draw.rect(self.screen, (150, 190, 150), rect, 2, border_radius=8) t = self.bold.render(label, True, TEXT) self.screen.blit(t, t.get_rect(center=rect.center)) def _draw_battle(self): title = self.font.render("Tower", True, HINT) self.screen.blit(title, title.get_rect(center=(WIN_W // 2, 70))) st = self.bold.render(f"Stage {self.stage} defeated!", True, TEXT) self.screen.blit(st, st.get_rect(center=(WIN_W // 2, 150))) info = self.small.render(f"Next: Stage {self.stage + 1} / {self.total}", True, MUTED) self.screen.blit(info, info.get_rect(center=(WIN_W // 2, 190))) rw = self.small.render(f"Stage reward: {self.reward_text}", True, TEXT) self.screen.blit(rw, rw.get_rect(center=(WIN_W // 2, 240))) tt = self.small.render(f"Total so far: {self.totals_text}", True, MUTED) self.screen.blit(tt, tt.get_rect(center=(WIN_W // 2, 270))) self._button(self.left_rect, "Exit tower", BTN, BTN_HOVER) self._button(self.mid_rect, "Adjust team", BTN, BTN_HOVER) self._button(self.right_rect, "Next stage", BTN, BTN_HOVER) def _draw_lost(self): title = self.font.render("Tower ended", True, HINT) self.screen.blit(title, title.get_rect(center=(WIN_W // 2, 70))) sub = self.bold.render(f"Defeated at stage {self.stage}", True, TEXT) self.screen.blit(sub, sub.get_rect(center=(WIN_W // 2, 130))) rw = self.small.render(f"Rewards earned: {self.totals_text}", True, TEXT) self.screen.blit(rw, rw.get_rect(center=(WIN_W // 2, 180))) if self.enemies: eh = self.bold.render("Enemy team this stage:", True, HINT) self.screen.blit(eh, eh.get_rect(midtop=(WIN_W // 2, 224))) total_w = sum(self.small.size(n)[0] for n in self.enemies) + 14 * (len(self.enemies) - 1) x = (WIN_W - total_w) // 2 for name in self.enemies: rar = plant_rarity(name) surf = self.small.render(name, True, RARITY_COLORS.get(rar, TEXT)) self.screen.blit(surf, (x, 254)) x += surf.get_width() + 14 self._button(self.left_rect, "Retry", BTN, BTN_HOVER) self._button(self.mid_rect, "Change team", BTN, BTN_HOVER) self._button(self.right_rect, "Give up", DANGER, DANGER_HOVER) def _draw_summary(self): if self.mode == "cleared": title = self.font.render("Tower cleared!", True, HINT) sub = self.bold.render(f"Cleared all {self.total} stages!", True, TEXT) else: title = self.font.render("Tower ended", True, HINT) sub = self.bold.render(f"Reached stage {self.stage}", True, TEXT) self.screen.blit(title, title.get_rect(center=(WIN_W // 2, 90))) self.screen.blit(sub, sub.get_rect(center=(WIN_W // 2, 170))) rw = self.small.render(f"Rewards earned: {self.totals_text}", True, TEXT) self.screen.blit(rw, rw.get_rect(center=(WIN_W // 2, 230))) hint = self.tiny.render("Click anywhere to return to the menu", True, MUTED) self.screen.blit(hint, hint.get_rect(center=(WIN_W // 2, 280)))