Files
mygame/game/ui.py
2026-05-20 20:21:27 +08:00

118 lines
4.9 KiB
Python

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,
)
class UI:
def __init__(self):
self.selected_tower = None
self.tower_buttons = []
self.wave_button = pygame.Rect(WINDOW_WIDTH - 140, WINDOW_HEIGHT - UI_BOTTOM_HEIGHT + 10, 130, 40)
self._init_tower_buttons()
def _init_tower_buttons(self):
types = ["arrow", "cannon", "slow"]
for i, t in enumerate(types):
rect = pygame.Rect(10 + i * 160, WINDOW_HEIGHT - UI_BOTTOM_HEIGHT + 10, 150, 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 <= 5 else COLOR_GREEN)
surface.blit(lives_text, (200, 10))
wave_text = font.render(f"波次: {wave_num}/{TOTAL_WAVES}", True, COLOR_WHITE)
surface.blit(wave_text, (380, 10))
# 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 + 20
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)
name_text = small_font.render(f"{data['name']} {data['price']}G", True, COLOR_WHITE)
surface.blit(name_text, (icon_x + 15, 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)