import sys import pygame from game.config import ( WINDOW_WIDTH, WINDOW_HEIGHT, FPS, COLOR_BG, COLOR_WHITE, INITIAL_GOLD, INITIAL_LIVES, UI_TOP_HEIGHT, UI_BOTTOM_HEIGHT, GAME_HEIGHT, TOWER_DATA, TOTAL_WAVES, ) from game.map import GameMap from game.enemy import Enemy from game.tower import Tower from game.projectile import Projectile from game.wave import WaveManager from game.ui import UI class Game: def __init__(self): pygame.init() self.screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT)) pygame.display.set_caption("塔防游戏") self.clock = pygame.time.Clock() self.font = pygame.font.SysFont("microsoftyahei", 20) self.small_font = pygame.font.SysFont("microsoftyahei", 16) self.big_font = pygame.font.SysFont("microsoftyahei", 48) self.reset() def reset(self): self.game_map = GameMap() self.enemies = [] self.towers = [] self.projectiles = [] self.wave_mgr = WaveManager() self.ui = UI() self.gold = INITIAL_GOLD self.lives = INITIAL_LIVES self.game_over = False self.won = False def run(self): while True: dt = self.clock.tick(FPS) / 1000.0 dt = min(dt, 0.05) for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() self._handle_event(event) if not self.game_over: self._update(dt) self._draw() def _handle_event(self, event): if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: pos = event.pos if self.game_over: return action = self.ui.handle_click(pos, self.gold) if action == "wave": if self.wave_mgr.has_more_waves() and not self.wave_mgr.wave_active: self.wave_mgr.start_next_wave() return tower_type = self.ui.handle_grid_click(pos) if tower_type: col, row = self.game_map.pixel_to_grid(*pos) if self.game_map.is_buildable(col, row): occupied = any(t.col == col and t.row == row for t in self.towers) price = TOWER_DATA[tower_type]["price"] if not occupied and self.gold >= price: px, py = self.game_map.grid_to_pixel(col, row) self.towers.append(Tower(tower_type, col, row, px, py)) self.gold -= price self.ui.selected_tower = None return self.ui.selected_tower = None if event.type == pygame.KEYDOWN: if event.key == pygame.K_r and self.game_over: self.reset() if event.key == pygame.K_ESCAPE: self.ui.selected_tower = None def _update(self, dt): if self.ui.selected_tower is not None: dt *= 0.3 self.wave_mgr.update(dt, self.enemies) for e in self.enemies: e.update(dt, self.enemies) if e.reached_end: self.lives -= 2 if e.type == "boss" else 1 if self.lives <= 0: self.lives = 0 self.game_over = True self.won = False for t in self.towers: t.update(dt, self.enemies, self.projectiles) for p in self.projectiles: p.update(dt, self.enemies) for e in self.enemies: if not e.alive and not e.reached_end: self.gold += e.reward e.reward = 0 self.enemies = [e for e in self.enemies if e.alive] self.projectiles = [p for p in self.projectiles if p.alive] if self.wave_mgr.all_waves_done or (self.wave_mgr.current_wave >= TOTAL_WAVES and not self.wave_mgr.wave_active and not self.enemies): if not self.game_over: self.game_over = True self.won = True def _draw(self): self.screen.fill(COLOR_BG) self.game_map.draw(self.screen) for t in self.towers: t.draw(self.screen) for e in self.enemies: e.draw(self.screen) for p in self.projectiles: p.draw(self.screen) mx, my = pygame.mouse.get_pos() self.ui.draw_placement_preview(self.screen, mx, my, self.game_map) self.ui.draw( self.screen, self.gold, self.lives, self.wave_mgr.current_wave, self.wave_mgr.wave_active, self.wave_mgr.has_more_waves(), self.font, self.small_font, ) self.ui.draw_slow_overlay(self.screen) if self.game_over: self.ui.draw_game_over(self.screen, self.won, self.big_font) pygame.display.flip() if __name__ == "__main__": Game().run()