import math import pygame from game.config import ( WINDOW_WIDTH, WINDOW_HEIGHT, UI_TOP_HEIGHT, UI_BOTTOM_HEIGHT, CELL_SIZE, COLOR_BLACK, COLOR_WHITE, COLOR_GOLD, COLOR_RED, COLOR_GREEN, COLOR_DARK_GRAY, COLOR_GRAY, COLOR_BG, TOWER_DATA, INITIAL_LIVES, TOTAL_WAVES, COLOR_LIGHT_BLUE, ) class UI: def __init__(self): self.selected_tower = None self.tower_buttons = [] self.wave_button = pygame.Rect(WINDOW_WIDTH - 120, WINDOW_HEIGHT - UI_BOTTOM_HEIGHT + 10, 110, 40) self._init_tower_buttons() def _init_tower_buttons(self): types = ["arrow", "cannon", "slow", "sniper", "poison", "lightning"] btn_w = 120 gap = 5 total_w = len(types) * btn_w + (len(types) - 1) * gap start_x = (WINDOW_WIDTH - 120 - 20 - total_w) // 2 + 10 for i, t in enumerate(types): rect = pygame.Rect(start_x + i * (btn_w + gap), WINDOW_HEIGHT - UI_BOTTOM_HEIGHT + 10, btn_w, 40) self.tower_buttons.append((rect, t)) def handle_click(self, pos, gold): for rect, t in self.tower_buttons: if rect.collidepoint(pos): price = TOWER_DATA[t]["price"] if gold >= price: self.selected_tower = t if self.selected_tower != t else None return None if self.wave_button.collidepoint(pos): return "wave" return None def handle_grid_click(self, pos): if self.selected_tower is None: return None return self.selected_tower def draw(self, surface, gold, lives, wave_num, wave_active, has_more, font, small_font): # Top bar pygame.draw.rect(surface, COLOR_DARK_GRAY, (0, 0, WINDOW_WIDTH, UI_TOP_HEIGHT)) gold_text = font.render(f"金币: {gold}", True, COLOR_GOLD) surface.blit(gold_text, (10, 10)) lives_text = font.render(f"生命: {lives}", True, COLOR_RED if lives <= 3 else COLOR_GREEN) surface.blit(lives_text, (160, 10)) wave_text = font.render(f"波次: {wave_num}/{TOTAL_WAVES}", True, COLOR_WHITE) surface.blit(wave_text, (310, 10)) # Slow-time indicator if self.selected_tower: slow_text = small_font.render("[ 子弹时间 ]", True, (100, 180, 255)) surface.blit(slow_text, (500, 12)) # Bottom bar pygame.draw.rect(surface, COLOR_DARK_GRAY, (0, WINDOW_HEIGHT - UI_BOTTOM_HEIGHT, WINDOW_WIDTH, UI_BOTTOM_HEIGHT)) for rect, t in self.tower_buttons: data = TOWER_DATA[t] selected = self.selected_tower == t bg = (80, 80, 120) if selected else COLOR_GRAY pygame.draw.rect(surface, bg, rect, border_radius=5) pygame.draw.rect(surface, COLOR_WHITE if selected else COLOR_BLACK, rect, 2, border_radius=5) icon_x = rect.x + 18 icon_y = rect.y + rect.height // 2 if t == "arrow": pygame.draw.rect(surface, data["color"], (icon_x - 5, icon_y - 5, 10, 10)) elif t == "cannon": pygame.draw.circle(surface, data["color"], (icon_x, icon_y), 7) elif t == "slow": pts = [(icon_x, icon_y-7), (icon_x+7, icon_y), (icon_x, icon_y+7), (icon_x-7, icon_y)] pygame.draw.polygon(surface, data["color"], pts) elif t == "sniper": pts = [(icon_x, icon_y-8), (icon_x+7, icon_y+6), (icon_x-7, icon_y+6)] pygame.draw.polygon(surface, data["color"], pts) elif t == "poison": pts = [] for i in range(6): angle = math.pi / 3 * i - math.pi / 6 pts.append((icon_x + int(7 * math.cos(angle)), icon_y + int(7 * math.sin(angle)))) pygame.draw.polygon(surface, data["color"], pts) elif t == "lightning": pts = [ (icon_x-1, icon_y-7), (icon_x+4, icon_y-2), (icon_x, icon_y-2), (icon_x+2, icon_y+7), (icon_x-3, icon_y+1), (icon_x+1, icon_y+1), ] pygame.draw.polygon(surface, data["color"], pts) name_text = small_font.render(f"{data['name']} {data['price']}G", True, COLOR_WHITE) surface.blit(name_text, (icon_x + 12, icon_y - 7)) # Wave button if has_more: btn_color = COLOR_GREEN if not wave_active else COLOR_GRAY pygame.draw.rect(surface, btn_color, self.wave_button, border_radius=5) pygame.draw.rect(surface, COLOR_BLACK, self.wave_button, 2, border_radius=5) btn_text = small_font.render("开始波次" if not wave_active else "进行中...", True, COLOR_WHITE) surface.blit(btn_text, (self.wave_button.x + 10, self.wave_button.y + 12)) def draw_placement_preview(self, surface, mx, my, game_map): if self.selected_tower is None: return col, row = game_map.pixel_to_grid(mx, my) if col < 0 or row < 0: return px, py = game_map.grid_to_pixel(col, row) data = TOWER_DATA[self.selected_tower] buildable = game_map.is_buildable(col, row) color = (*data["color"][:3], 60) if buildable else (255, 0, 0, 60) preview = pygame.Surface((CELL_SIZE, CELL_SIZE), pygame.SRCALPHA) preview.fill(color) surface.blit(preview, (col * CELL_SIZE, row * CELL_SIZE + UI_TOP_HEIGHT)) if buildable: pygame.draw.circle(surface, COLOR_WHITE, (px, py), data["range"], 1) def draw_game_over(self, surface, won, font): overlay = pygame.Surface((WINDOW_WIDTH, WINDOW_HEIGHT), pygame.SRCALPHA) overlay.fill((0, 0, 0, 150)) surface.blit(overlay, (0, 0)) text = "胜利!" if won else "失败!" color = COLOR_GOLD if won else COLOR_RED rendered = font.render(text, True, color) rect = rendered.get_rect(center=(WINDOW_WIDTH // 2, WINDOW_HEIGHT // 2 - 20)) surface.blit(rendered, rect) hint = font.render("按 R 重新开始", True, COLOR_WHITE) hint_rect = hint.get_rect(center=(WINDOW_WIDTH // 2, WINDOW_HEIGHT // 2 + 30)) surface.blit(hint, hint_rect) def draw_slow_overlay(self, surface): if self.selected_tower is None: return border = pygame.Surface((WINDOW_WIDTH, WINDOW_HEIGHT), pygame.SRCALPHA) pygame.draw.rect(border, (100, 150, 255, 25), (0, 0, WINDOW_WIDTH, WINDOW_HEIGHT)) for side_w in [4]: pygame.draw.rect(border, (100, 150, 255, 60), (0, 0, side_w, WINDOW_HEIGHT)) pygame.draw.rect(border, (100, 150, 255, 60), (WINDOW_WIDTH - side_w, 0, side_w, WINDOW_HEIGHT)) pygame.draw.rect(border, (100, 150, 255, 60), (0, 0, WINDOW_WIDTH, side_w)) pygame.draw.rect(border, (100, 150, 255, 60), (0, WINDOW_HEIGHT - side_w, WINDOW_WIDTH, side_w)) surface.blit(border, (0, 0))