import pygame
import random
import sys

# ====================== 初始化 ======================
pygame.init()
pygame.mixer.init()

SCREEN_W, SCREEN_H = 900, 600
screen = pygame.display.set_mode((SCREEN_W, SCREEN_H))
pygame.display.set_caption("植物大战僵尸 · 完整版")
clock = pygame.time.Clock()
FPS = 60

# 颜色
WHITE = (255,255,255)
BLACK = (0,0,0)
YELLOW = (255,220,0)
GRAY = (100,100,100)
RED = (200,0,0)
GREEN = (0,200,0)
BLUE = (0,150,255)
GRID_COLOR = (0, 80, 0)  # 网格线颜色（深绿色，不刺眼）

# 草坪行 & 网格参数（核心！控制格子对齐）
ROWS_Y = [80, 150, 220, 290, 360]
GRID_W = 80    # 每个格子宽度
GRID_H = 70    # 每个格子高度
GRID_START_X = 30  # 网格左上角X坐标
GRID_START_Y = 80  # 网格左上角Y坐标
GRID_COLS = 10     # 网格列数
GRID_ROWS = 5      # 网格行数

# ====================== 游戏状态 ======================
MENU = 0
PLAYING = 1
GAME_OVER = 2
WIN = 3
game_state = MENU

# ====================== 游戏数值 ======================
sun_total = 150
sun_list = []
SUN_VALUE = 25

selected_plant = None

wave = 1
zombie_total = 1  # 可自行修改僵尸总数
zombie_killed = 0

# 开局安全时间：15秒不刷僵尸
SAFE_TIME = 15000
game_start_time = 0

# 植物卡片
CARD_POS = {
    "peashooter": (20, 20),
    "sunflower": (90, 20),
    "wallnut": (160, 20),
    "snowpea": (230, 20),
}

PLANT_COST = {
    "peashooter": 100,
    "sunflower": 50,
    "wallnut": 50,
    "snowpea": 175,
}

plant_cd = {k:0 for k in CARD_POS}
CD_TIME = {
    "peashooter": 3000,
    "sunflower": 2500,
    "wallnut": 2000,
    "snowpea": 4000,
}

# ====================== 精灵组 ======================
all_sprites = pygame.sprite.Group()
plant_group = pygame.sprite.Group()
zombie_group = pygame.sprite.Group()
bullet_group = pygame.sprite.Group()
explosion_group = pygame.sprite.Group()

# ====================== 加载资源（找不到自动色块） ======================
def load_img(name, size=(50,50)):
    try:
        img = pygame.image.load(name).convert_alpha()
        return pygame.transform.scale(img, size)
    except:
        surf = pygame.Surface(size, pygame.SRCALPHA)
        color = {
            "bg.png": (34, 139, 34),
            "menu_bg.png": (20, 20, 20),
            "sun.png": (255, 220, 0),
            "card_bg.png": (80, 80, 80),
            "peashooter.png": (0, 200, 0),
            "sunflower.png": (255, 200, 0),
            "wallnut.png": (120, 120, 120),
            "snowpea.png": (0, 150, 255),
            "normal_zombie.png": (200, 0, 0),
            "fast_zombie.png": (255, 165, 0),
            "giant_zombie.png": (139,69,19),
            "bullet.png": (255,255,0),
            "snow_bullet.png": (0,150,255),
            "explode.png": (255,69,0)
        }.get(name, (random.randint(100,200), random.randint(100,200), random.randint(100,200)))
        pygame.draw.rect(surf, color, surf.get_rect())
        return surf

def load_sound(name):
    try:
        return pygame.mixer.Sound(name)
    except:
        return None

# 图片
bg = load_img("bg.png", (SCREEN_W, SCREEN_H))
menu_bg = load_img("menu_bg.png", (SCREEN_W, SCREEN_H))
sun_img = load_img("sun.png", (30,30))
card_bg = load_img("card_bg.png", (60,80))
explode_img = load_img("explode.png", (80,80))

# 音效
sfx_shoot = load_sound("shoot.wav")
sfx_hit = load_sound("hit.wav")
sfx_eat = load_sound("eat.wav")
sfx_sun = load_sound("sun.wav")
sfx_explode = load_sound("explode.wav")
sfx_win = load_sound("win.wav")
sfx_lose = load_sound("lose.wav")

# ====================== 绘制网格线 ======================
def draw_grid():
    # 画竖线（列）
    for col in range(GRID_COLS + 1):
        x = GRID_START_X + col * GRID_W
        pygame.draw.line(screen, GRID_COLOR, (x, GRID_START_Y), (x, GRID_START_Y + GRID_ROWS * GRID_H), 2)
    
    # 画横线（行）
    for row in range(GRID_ROWS + 1):
        y = GRID_START_Y + row * GRID_H
        pygame.draw.line(screen, GRID_COLOR, (GRID_START_X, y), (GRID_START_X + GRID_COLS * GRID_W, y), 2)

# ====================== 爆炸特效 ======================
class Explosion(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = explode_img
        self.rect = self.image.get_rect(center=(x,y))
        self.timer = 0
        if sfx_explode:
            sfx_explode.play()

    def update(self):
        self.timer += 1
        if self.timer > 15:
            self.kill()

# ====================== 子弹 ======================
class Bullet(pygame.sprite.Sprite):
    def __init__(self, x, y, snow=False):
        super().__init__()
        self.snow = snow
        self.image = load_img("snow_bullet.png" if snow else "bullet.png", (12,6))
        self.rect = self.image.get_rect(center=(x,y))
        self.damage = 25

    def update(self):
        self.rect.x += 6
        if self.rect.left > SCREEN_W:
            self.kill()

# ====================== 植物基类 ======================
class Plant(pygame.sprite.Sprite):
    def __init__(self, x, y, hp, cost):
        super().__init__()
        self.max_hp = hp
        self.hp = hp
        self.cost = cost

    def update(self):
        if self.hp <= 0:
            self.kill()

# 豌豆射手
class Peashooter(Plant):
    def __init__(self, x, y):
        super().__init__(x,y, 60, 100)
        self.image = load_img("peashooter.png", (60, 60))  # 适配格子大小
        self.rect = self.image.get_rect(center=(x + GRID_W//2, y + GRID_H//2))  # 居中对齐格子
        self.last = pygame.time.get_ticks()

    def update(self):
        super().update()
        now = pygame.time.get_ticks()
        if now - self.last > 1200:
            bullet = Bullet(self.rect.right, self.rect.centery)
            bullet_group.add(bullet)
            all_sprites.add(bullet)
            if sfx_shoot:
                sfx_shoot.play()
            self.last = now

# 寒冰射手
class SnowPea(Plant):
    def __init__(self, x, y):
        super().__init__(x,y, 60, 175)
        self.image = load_img("snowpea.png", (60, 60))
        self.rect = self.image.get_rect(center=(x + GRID_W//2, y + GRID_H//2))
        self.last = pygame.time.get_ticks()

    def update(self):
        super().update()
        now = pygame.time.get_ticks()
        if now - self.last > 1500:
            bullet = Bullet(self.rect.right, self.rect.centery, snow=True)
            bullet_group.add(bullet)
            all_sprites.add(bullet)
            if sfx_shoot:
                sfx_shoot.play()
            self.last = now

# 向日葵 2秒一阳光
class Sunflower(Plant):
    def __init__(self, x, y):
        super().__init__(x,y, 60, 50)
        self.image = load_img("sunflower.png", (60, 60))
        self.rect = self.image.get_rect(center=(x + GRID_W//2, y + GRID_H//2))
        self.sun_cd = 2000
        self.last = pygame.time.get_ticks()

    def update(self):
        super().update()
        now = pygame.time.get_ticks()
        if now - self.last > self.sun_cd:
            sun_list.append({
                "x": self.rect.centerx,
                "y": self.rect.centery,
                "timer": now
            })
            self.last = now

# 坚果
class WallNut(Plant):
    def __init__(self, x, y):
        super().__init__(x,y, 300, 50)
        self.image = load_img("wallnut.png", (70, 70))
        self.rect = self.image.get_rect(center=(x + GRID_W//2, y + GRID_H//2))

# ====================== 僵尸（所有僵尸血量=100） ======================
class Zombie(pygame.sprite.Sprite):
    def __init__(self, img_name, hp, speed, damage):
        super().__init__()
        self.image = load_img(img_name, (60,80))
        self.rect = self.image.get_rect(
            topleft=(SCREEN_W, random.choice(ROWS_Y))
        )
        self.max_hp = hp
        self.hp = hp
        self.speed = speed
        self.damage = damage
        self.attacking = False
        self.attack_cd = 800
        self.last_attack = 0

    def update(self):
        global zombie_killed
        if self.hp <= 0:
            zombie_killed += 1
            exp = Explosion(self.rect.centerx, self.rect.centery)
            explosion_group.add(exp)
            all_sprites.add(exp)
            self.kill()
            return

        if not self.attacking:
            self.rect.x -= self.speed

        if self.rect.left < 50:
            global game_state
            game_state = GAME_OVER
            if sfx_lose:
                sfx_lose.play()

# 普通僵尸：血量=100、速度=2
class NormalZombie(Zombie):
    def __init__(self):
        super().__init__("normal_zombie.png", 100, 2, 10)

# 黄色快速僵尸：血量=100、速度=2
class FastZombie(Zombie):
    def __init__(self):
        super().__init__("fast_zombie.png", 100, 2, 10)

# 巨人僵尸：血量=100、速度=3
class GiantZombie(Zombie):
    def __init__(self):
        super().__init__("giant_zombie.png", 100, 3, 10)

# ====================== 阳光 ======================
def draw_sun():
    for s in sun_list:
        screen.blit(sun_img, (s["x"]-15, s["y"]-15))

def collect_sun(pos):
    global sun_total
    for i in reversed(range(len(sun_list))):
        s = sun_list[i]
        if abs(pos[0]-s["x"]) < 20 and abs(pos[1]-s["y"]) < 20:
            sun_total += SUN_VALUE
            if sfx_sun:
                sfx_sun.play()
            sun_list.pop(i)
            break

# ====================== 种植植物（严格对齐网格） ======================
def try_plant(plant_name, mx, my):
    global sun_total, selected_plant
    cost = PLANT_COST[plant_name]
    if sun_total < cost:
        return
    if pygame.time.get_ticks() < plant_cd[plant_name]:
        return

    # 计算对齐到网格的坐标（核心！）
    col = (mx - GRID_START_X) // GRID_W
    row = (my - GRID_START_Y) // GRID_H
    
    # 限制在网格范围内
    if col < 0 or col >= GRID_COLS or row < 0 or row >= GRID_ROWS:
        return
    
    # 计算格子左上角坐标
    tx = GRID_START_X + col * GRID_W
    ty = GRID_START_Y + row * GRID_H

    # 检查该格子是否已有植物（避免重叠）
    for plant in plant_group:
        if abs(plant.rect.centerx - (tx + GRID_W//2)) < 10 and abs(plant.rect.centery - (ty + GRID_H//2)) < 10:
            return

    p = None
    if plant_name == "peashooter":
        p = Peashooter(tx, ty)
    elif plant_name == "sunflower":
        p = Sunflower(tx, ty)
    elif plant_name == "wallnut":
        p = WallNut(tx, ty)
    elif plant_name == "snowpea":
        p = SnowPea(tx, ty)

    if p:
        plant_group.add(p)
        all_sprites.add(p)
        sun_total -= cost
        plant_cd[plant_name] = pygame.time.get_ticks() + CD_TIME[plant_name]
        selected_plant = None

# ====================== 战斗逻辑 ======================
def zombie_attack_plant():
    for z in zombie_group:
        hits = pygame.sprite.spritecollide(z, plant_group, False)
        if hits:
            z.attacking = True
            now = pygame.time.get_ticks()
            if now - z.last_attack > z.attack_cd:
                hits[0].hp -= z.damage
                if sfx_eat:
                    sfx_eat.play()
                z.last_attack = now
        else:
            z.attacking = False

def bullet_hit_zombie():
    for b in bullet_group:
        hits = pygame.sprite.spritecollide(b, zombie_group, False)
        for z in hits:
            z.hp -= b.damage
            if sfx_hit:
                sfx_hit.play()
            if b.snow:
                z.speed = max(0.3, z.speed * 0.6)  # 寒冰减速后最低0.3
            b.kill()

# ====================== 血量条 ======================
def draw_health_bar():
    for z in zombie_group:
        w = 50
        h = 5
        x = z.rect.centerx - w//2
        y = z.rect.top - 10
        pygame.draw.rect(screen, RED, (x,y,w,h))
        ratio = z.hp / z.max_hp
        pygame.draw.rect(screen, GREEN, (x,y,w*ratio,h))

    for p in plant_group:
        w = 40
        h = 4
        x = p.rect.centerx - w//2
        y = p.rect.top - 8
        pygame.draw.rect(screen, RED, (x,y,w,h))
        ratio = p.hp / p.max_hp
        pygame.draw.rect(screen, GREEN, (x,y,w*ratio,h))

# ====================== 生成僵尸（带15秒安全时间） ======================
spawn_timer = 0
def spawn_zombie():
    global spawn_timer
    now = pygame.time.get_ticks()
    
    # 关键：开局15秒内不刷僵尸
    if now - game_start_time < SAFE_TIME:
        return

    if now - spawn_timer > 1600 and zombie_killed < zombie_total:
        r = random.random()
        if r < 0.6:
            z = NormalZombie()
        elif r < 0.9:
            z = FastZombie()
        else:
            z = GiantZombie()
        zombie_group.add(z)
        all_sprites.add(z)
        spawn_timer = now

# ====================== UI ======================
def draw_ui():
    font = pygame.font.Font(None, 40)
    screen.blit(font.render(f"阳光: {sun_total}", True, YELLOW), (350, 20))
    screen.blit(font.render(f"第{wave}波 剩余:{max(0,zombie_total-zombie_killed)}", True, WHITE), (550, 20))

    for name, (x,y) in CARD_POS.items():
        screen.blit(card_bg, (x,y))
        cost = PLANT_COST[name]
        screen.blit(pygame.font.Font(None,24).render(f"{cost}", True, YELLOW), (x+5,y+5))
        now = pygame.time.get_ticks()
        if now < plant_cd[name]:
            t = round((plant_cd[name]-now)/1000,1)
            screen.blit(pygame.font.Font(None,30).render(f"{t}", True, WHITE), (x+10,y+30))

# ====================== 菜单 ======================
def draw_menu():
    screen.blit(menu_bg, (0,0))
    font_big = pygame.font.Font(None, 100)
    font_small = pygame.font.Font(None, 50)
    title = font_big.render("植物大战僵尸", True, GREEN)
    start = font_small.render("点击开始游戏", True, WHITE)
    screen.blit(title, (SCREEN_W//2 - title.get_width()//2, 150))
    screen.blit(start, (SCREEN_W//2 - start.get_width()//2, 350))

# ====================== 结束界面 ======================
def draw_end():
    font = pygame.font.Font(None, 80)
    if game_state == GAME_OVER:
        txt = font.render("游戏失败！僵尸进家了", True, RED)
    elif game_state == WIN:
        txt = font.render("恭喜你胜利！", True, GREEN)
    else:
        return
    screen.blit(txt, (SCREEN_W//2 - txt.get_width()//2, SCREEN_H//2))
    tip = pygame.font.Font(None,40).render("点击返回菜单", True, WHITE)
    screen.blit(tip, (SCREEN_W//2 - tip.get_width()//2, 400))

# ====================== 重置游戏 ======================
def reset_game():
    global sun_total, zombie_killed, wave, game_state
    global selected_plant, game_start_time
    sun_total = 150
    zombie_killed = 0
    wave = 1
    selected_plant = None
    sun_list.clear()

    all_sprites.empty()
    plant_group.empty()
    zombie_group.empty()
    bullet_group.empty()
    explosion_group.empty()

    # 记录游戏开始时间
    game_start_time = pygame.time.get_ticks()

# ====================== 主循环 ======================
def main():
    global game_state, selected_plant
    running = True
    while running:
        clock.tick(FPS)
        screen.fill(BLACK)

        if game_state == MENU:
            draw_menu()
        else:
            screen.blit(bg, (0,0))
            draw_grid()  # 绘制网格线
            draw_sun()
            all_sprites.update()
            if game_state == PLAYING:
                spawn_zombie()
                zombie_attack_plant()
                bullet_hit_zombie()
                if zombie_killed >= zombie_total:
                    game_state = WIN
                    if sfx_win:
                        sfx_win.play()
            all_sprites.draw(screen)
            draw_health_bar()
            draw_ui()
            draw_end()

        for e in pygame.event.get():
            if e.type == pygame.QUIT:
                running = False

            if e.type == pygame.MOUSEBUTTONDOWN:
                if game_state == MENU:
                    reset_game()
                    game_state = PLAYING
                elif game_state in (GAME_OVER, WIN):
                    game_state = MENU
                elif game_state == PLAYING:
                    mx, my = e.pos
                    collect_sun((mx,my))
                    if my < 100:
                        selected_plant = None
                        if 20<=mx<=80: selected_plant="peashooter"
                        elif 90<=mx<=150: selected_plant="sunflower"
                        elif 160<=mx<=220: selected_plant="wallnut"
                        elif 230<=mx<=290: selected_plant="snowpea"
                    else:
                        if selected_plant:
                            try_plant(selected_plant, mx, my)

        pygame.display.flip()

    pygame.quit()
    sys.exit()

if __name__ == "__main__":
    main()