"""
超级马里奥风格小游戏 - Pygame 实现
功能：移动、跳跃、踩敌人、吃蘑菇、过关
"""

import pygame
import sys
import random

# ==================== 初始化 ====================
pygame.init()
pygame.display.set_caption("超级马里奥 - Pygame")

# ==================== 常量 ====================
SCREEN_W, SCREEN_H = 800, 600
FPS = 60
TILE = 40  # 格子大小

# 颜色
SKY_BLUE = (92, 148, 252)
GROUND_GREEN = (0, 168, 0)
DIRT_BROWN = (139, 90, 43)
BRICK_RED = (200, 80, 40)
COIN_YELLOW = (255, 215, 0)
MUSHROOM_RED = (220, 40, 40)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
PIPE_GREEN = (0, 180, 0)
FLAG_RED = (220, 30, 30)
CLOUD_WHITE = (255, 255, 255)

# 物理
GRAVITY = 0.6
JUMP_V = -13
MOVE_SPEED = 4
MAX_FALL = 14

# ==================== 工具函数 ====================
def load_img(w, h, color, draw_func=None):
    """创建带颜色的 surface，可选自定义绘制"""
    s = pygame.Surface((w, h), pygame.SRCALPHA)
    s.fill(color)
    if draw_func:
        draw_func(s)
    return s

def draw_mario_sprite(surf, frame=0, power=False):
    """在 surf 上绘制马里奥像素画 (32x48)"""
    surf.fill((0, 0, 0, 0))
    # 帽子
    pygame.draw.rect(surf, (220, 30, 30), (4, 0, 24, 8))
    pygame.draw.rect(surf, (220, 30, 30), (0, 6, 32, 6))
    # 脸部
    pygame.draw.rect(surf, (255, 200, 160), (6, 12, 20, 10))
    # 胡子
    pygame.draw.rect(surf, (60, 40, 20), (8, 18, 16, 3))
    # 眼睛
    pygame.draw.rect(surf, BLACK, (10, 14, 4, 4))
    pygame.draw.rect(surf, BLACK, (20, 14, 4, 4))
    # 身体（红/绿）
    body_c = (220, 30, 30) if not power else (30, 100, 220)
    pygame.draw.rect(surf, body_c, (4, 22, 24, 14))
    # 背带
    pygame.draw.rect(surf, (30, 80, 200), (10, 22, 4, 14))
    pygame.draw.rect(surf, (30, 80, 200), (18, 22, 4, 14))
    # 手臂
    pygame.draw.rect(surf, (255, 200, 160), (0, 24, 6, 10))
    pygame.draw.rect(surf, (255, 200, 160), (26, 24, 6, 10))
    # 裤子
    pygame.draw.rect(surf, (30, 60, 180), (4, 36, 24, 8))
    # 腿
    leg_off = 2 if frame == 1 else (-2 if frame == 2 else 0)
    pygame.draw.rect(surf, (100, 60, 20), (6 + leg_off, 44, 8, 6))
    pygame.draw.rect(surf, (100, 60, 20), (18 - leg_off, 44, 8, 6))
    # 鞋子
    pygame.draw.rect(surf, (160, 80, 20), (4 + leg_off, 48, 12, 4))
    pygame.draw.rect(surf, (160, 80, 20), (16 - leg_off, 48, 12, 4))

def draw_goomba_sprite(surf, frame=0):
    """板栗仔"""
    surf.fill((0, 0, 0, 0))
    # 身体
    pygame.draw.rect(surf, (160, 100, 60), (4, 8, 24, 20))
    # 脚
    f = 2 if frame == 1 else -2
    pygame.draw.rect(surf, (120, 70, 30), (2, 26, 10, 6))
    pygame.draw.rect(surf, (120, 70, 30), (20, 26, 10, 6))
    # 眼睛
    pygame.draw.rect(surf, WHITE, (8, 14, 6, 6))
    pygame.draw.rect(surf, WHITE, (18, 14, 6, 6))
    pygame.draw.rect(surf, BLACK, (10, 16, 3, 3))
    pygame.draw.rect(surf, BLACK, (20, 16, 3, 3))
    # 眉毛
    pygame.draw.rect(surf, (120, 70, 30), (7, 12, 8, 3))
    pygame.draw.rect(surf, (120, 70, 30), (17, 12, 8, 3))

def draw_coin_surf(surf):
    surf.fill((0, 0, 0, 0))
    pygame.draw.circle(surf, COIN_YELLOW, (12, 12), 11)
    pygame.draw.circle(surf, (255, 240, 100), (12, 12), 8)
    pygame.draw.rect(surf, COIN_YELLOW, (10, 4, 4, 16))

def draw_mushroom_surf(surf):
    surf.fill((0, 0, 0, 0))
    # 帽子
    pygame.draw.rect(surf, MUSHROOM_RED, (2, 0, 28, 16))
    pygame.draw.circle(surf, MUSHROOM_RED, (8, 8), 6)
    pygame.draw.circle(surf, MUSHROOM_RED, (24, 8), 6)
    pygame.draw.circle(surf, WHITE, (8, 6), 3)
    pygame.draw.circle(surf, WHITE, (24, 6), 3)
    # 脸
    pygame.draw.rect(surf, (255, 220, 180), (6, 14, 20, 14))
    # 眼睛
    pygame.draw.rect(surf, BLACK, (10, 18, 3, 4))
    pygame.draw.rect(surf, BLACK, (19, 18, 3, 4))
    # 嘴
    pygame.draw.rect(surf, BLACK, (12, 24, 8, 2))

def draw_brick_surf(surf):
    surf.fill((0, 0, 0, 0))
    pygame.draw.rect(surf, BRICK_RED, (0, 0, TILE, TILE))
    pygame.draw.rect(surf, (160, 50, 20), (0, 0, TILE, 4))
    pygame.draw.rect(surf, (160, 50, 20), (0, TILE//2, TILE, 3))
    pygame.draw.rect(surf, (160, 50, 20), (TILE//2, 0, 3, TILE//2))
    pygame.draw.rect(surf, (160, 50, 20), (TILE//4*3, TILE//2, 3, TILE//2))

def draw_ground_surf(surf):
    surf.fill((0, 0, 0, 0))
    pygame.draw.rect(surf, DIRT_BROWN, (0, 0, TILE, TILE))
    for _ in range(5):
        x, y = random.randint(2, TILE-4), random.randint(2, TILE-4)
        pygame.draw.rect(surf, (110, 70, 35), (x, y, 4, 3))

def draw_question_surf(surf):
    surf.fill((0, 0, 0, 0))
    pygame.draw.rect(surf, (240, 180, 40), (0, 0, TILE, TILE))
    pygame.draw.rect(surf, (200, 140, 20), (0, 0, TILE, 3))
    pygame.draw.rect(surf, (200, 140, 20), (0, TILE-3, TILE, 3))
    pygame.draw.rect(surf, (200, 140, 20), (0, 0, 3, TILE))
    pygame.draw.rect(surf, (200, 140, 20), (TILE-3, 0, 3, TILE))
    # ?
    font = pygame.font.Font(None, 28)
    t = font.render("?", True, (180, 100, 0))
    surf.blit(t, (12, 8))

def draw_pipe_surf(surf):
    surf.fill((0, 0, 0, 0))
    pygame.draw.rect(surf, PIPE_GREEN, (0, 0, TILE, TILE))
    pygame.draw.rect(surf, (0, 220, 0), (2, 0, TILE-4, 4))
    pygame.draw.rect(surf, (0, 140, 0), (0, TILE-4, TILE, 4))

def draw_pipe_body_surf(surf):
    """管道管身（无顶部管口）"""
    surf.fill((0, 0, 0, 0))
    pygame.draw.rect(surf, PIPE_GREEN, (0, 0, TILE, TILE))
    pygame.draw.rect(surf, (0, 140, 0), (0, 0, 3, TILE))
    pygame.draw.rect(surf, (0, 140, 0), (TILE-3, 0, 3, TILE))
    # 纵向纹理线
    pygame.draw.rect(surf, (0, 155, 0), (TILE//3, 0, 2, TILE))
    pygame.draw.rect(surf, (0, 155, 0), (TILE//3*2, 0, 2, TILE))

def draw_flag_surf(surf):
    surf.fill((0, 0, 0, 0))
    pygame.draw.rect(surf, (180, 180, 180), (14, 0, 4, 48))
    pygame.draw.polygon(surf, FLAG_RED, [(18, 0), (38, 8), (18, 16)])
    pygame.draw.polygon(surf, FLAG_RED, [(18, 16), (32, 22), (18, 30)])

# ==================== 预渲染精灵 ====================
SPRITES = {}
def init_sprites():
    # 马里奥
    for f in range(3):
        s = pygame.Surface((32, 52), pygame.SRCALPHA)
        draw_mario_sprite(s, f, False)
        SPRITES[f'mario_{f}'] = s
        s2 = pygame.Surface((32, 52), pygame.SRCALPHA)
        draw_mario_sprite(s2, f, True)
        SPRITES[f'mario_p_{f}'] = s2
    # 翻转马里奥
    for k in list(SPRITES.keys()):
        if 'mario' in k:
            SPRITES[k + '_r'] = pygame.transform.flip(SPRITES[k], True, False)
    # 板栗仔
    for f in range(2):
        s = pygame.Surface((32, 32), pygame.SRCALPHA)
        draw_goomba_sprite(s, f)
        SPRITES[f'goomba_{f}'] = s
    # 金币
    s = pygame.Surface((24, 24), pygame.SRCALPHA)
    draw_coin_surf(s)
    SPRITES['coin'] = s
    # 蘑菇
    s = pygame.Surface((32, 32), pygame.SRCALPHA)
    draw_mushroom_surf(s)
    SPRITES['mushroom'] = s
    # 砖块
    s = pygame.Surface((TILE, TILE), pygame.SRCALPHA)
    draw_brick_surf(s)
    SPRITES['brick'] = s
    # 地面
    s = pygame.Surface((TILE, TILE), pygame.SRCALPHA)
    draw_ground_surf(s)
    SPRITES['ground'] = s
    # 问号
    s = pygame.Surface((TILE, TILE), pygame.SRCALPHA)
    draw_question_surf(s)
    SPRITES['question'] = s
    # 管道
    s = pygame.Surface((TILE, TILE*2), pygame.SRCALPHA)
    draw_pipe_surf(s)
    SPRITES['pipe'] = s
    # 管身
    s = pygame.Surface((TILE, TILE), pygame.SRCALPHA)
    draw_pipe_body_surf(s)
    SPRITES['pipe_body'] = s
    # 旗杆
    s = pygame.Surface((40, 52), pygame.SRCALPHA)
    draw_flag_surf(s)
    SPRITES['flag'] = s
    # 云
    s = pygame.Surface((80, 40), pygame.SRCALPHA)
    pygame.draw.ellipse(s, CLOUD_WHITE, (10, 10, 60, 30))
    pygame.draw.ellipse(s, CLOUD_WHITE, (0, 15, 40, 25))
    pygame.draw.ellipse(s, CLOUD_WHITE, (40, 5, 40, 25))
    SPRITES['cloud'] = s

init_sprites()

# ==================== 关卡定义 ====================
# 符号： . 空  # 地面  B 砖块  ? 问号  P 管道  F 旗杆  M 马里奥  G 板栗仔  C 硬币
# 所有关卡统一15行：上面12行是空中，下面3行是地面层

# ===== 关卡1：草原世界（教学关，温和引入所有机制）=====
LEVEL = [
    "..............................................................................................",
    "..............................................................................................",
    "..............................................................................................",
    "..............................................................................................",
    "..............................................................................???.............",
    "...........................???.................................................................",
    "..............................................................................................",
    "..............BBBB........................................BBBB...................................",
    "..............................................................................................",
    "....????..............?....................????..................?...............................",
    "......................................................................G......................",
    "..............................................................................................",
    "..M..........G...................G.................G...............C..............G............F.",
    "###############################PPPPPP####################PPPPPPPPPP##############################",
    "###############################PPPPPP####################PPPPPPPPPP##############################",
    "###############################PPPPPP####################PPPPPPPPPP##############################",
]

# ===== 关卡2：洞穴冒险（更长、更多敌人、复杂跳跃）=====
LEVEL2 = [
    "............................................................................................................",
    "............................................................................................................",
    "............................................................................................................",
    "...........................................................................???..............................",
    ".......................................???...............................................................",
    "......................BBBB...........................................BBBB.................................",
    "............................................................................................................",
    "...........????.................................BBBB......................................???............",
    "............................................................................................................",
    "...................?......................................???............................................",
    "...........................................................................................############.....",
    "............................................................................................................",
    "..............G..............G........................G..............G..............G...................",
    "..M............C..............................G..........................G...................C.......F.",
    "###############################PPPPPP############################PPPPPPPPPP##############################",
    "###############################PPPPPP############################PPPPPPPPPP##############################",
    "###############################PPPPPP############################PPPPPPPPPP##############################",
]

# ===== 关卡3：天空城堡（最长、最难、密集挑战）=====
LEVEL3 = [
    "............................................................................................................................",
    "............................................................................................................................",
    "............................................................................................................................",
    "...................................???...............................................???.....................................",
    "............................................................................................................................",
    "..............BBBB........................................BBBB........................................BBBB.................",
    "............................................................................................................................",
    "....????.........................BBBB...........................................????.......................................",
    "............................................................................................................................",
    "..............???......................................???......................................???.......................",
    "............................................................................................................................",
    "...................G..............................G..............................G..............................G...........",
    "..............C..............C..............C..............C..............C..............C..............C..............C...",
    "............................................................................................................................",
    "..M............G................................G................................G......................................F.",
    "###############################PPPPPP####################PPPPPPPPPP####################PPPPPP##############################",
    "###############################PPPPPP####################PPPPPPPPPP####################PPPPPP##############################",
    "###############################PPPPPP####################PPPPPPPPPP####################PPPPPP##############################",
]

# ==================== 游戏对象 ====================
class Camera:
    def __init__(self):
        self.x = 0
        self.y = 0
    def update(self, target):
        self.x = target.x - SCREEN_W // 3
        self.y = 0
        if self.x < 0: self.x = 0
    def apply(self, rect):
        return pygame.Rect(rect.x - self.x, rect.y - self.y, rect.w, rect.h)

class Mario:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.w, self.h = 28, 48
        self.vx = 0
        self.vy = 0
        self.on_ground = False
        self.facing = 1  # 1右 -1左
        self.anim_frame = 0
        self.anim_timer = 0
        self.powered = False
        self.invincible = 0
        self.dead = False
        self.dead_timer = 0
        self.score = 0
        self.coins = 0
        self.lives = 3
        self.win = False

    @property
    def rect(self):
        return pygame.Rect(self.x, self.y, self.w, self.h)

    def update(self, level, platforms, enemies, items, particles):
        if self.dead:
            self.dead_timer += 1
            self.vy += GRAVITY
            self.y += self.vy
            if self.dead_timer > 120:
                self.lives -= 1
                if self.lives <= 0:
                    return "gameover"
                return "respawn"
            return None

        # 输入
        keys = pygame.key.get_pressed()
        jump_pressed = keys[pygame.K_SPACE] or keys[pygame.K_UP] or keys[pygame.K_w]
        if keys[pygame.K_LEFT] or keys[pygame.K_a]:
            self.vx = -MOVE_SPEED
            self.facing = -1
        elif keys[pygame.K_RIGHT] or keys[pygame.K_d]:
            self.vx = MOVE_SPEED
            self.facing = 1
        else:
            self.vx = 0

        if jump_pressed and self.on_ground:
            self.vy = JUMP_V
            self.on_ground = False

        # 重力
        if self.on_ground and not jump_pressed:
            self.vy = 0
        else:
            self.vy += GRAVITY
            if self.vy > MAX_FALL:
                self.vy = MAX_FALL

        # 水平移动 + 碰撞
        self.x += self.vx
        self.check_collide_x(platforms)

        # 垂直移动 + 碰撞
        self.y += self.vy
        self.on_ground = False
        self.check_collide_y(platforms)

        # 动画
        self.anim_timer += 1
        if self.anim_timer > 6:
            self.anim_timer = 0
            if self.vx != 0:
                self.anim_frame = (self.anim_frame + 1) % 3
            else:
                self.anim_frame = 0

        # 无敌计时
        if self.invincible > 0:
            self.invincible -= 1

        # 死亡检测（掉出屏幕底部）
        if self.y > level.height_px + 100:
            self.die()

        # 敌人碰撞
        for e in enemies[:]:
            if e.dead: continue
            if self.rect.colliderect(e.rect):
                if self.vy > 0 and self.rect.bottom - e.rect.top < 20:
                    # 踩死
                    e.die()
                    self.vy = -8
                    self.score += 100
                    for _ in range(8):
                        particles.append(Particle(e.rect.centerx, e.rect.centery,
                                                  random.randint(-3,3), random.randint(-5,-1),
                                                  (160,100,60)))
                else:
                    self.take_hit()

        # 道具碰撞
        for item in items[:]:
            if item.dead: continue
            item.update()
            if self.rect.colliderect(item.rect):
                if item.kind == 'coin':
                    self.coins += 1
                    self.score += 200
                    item.dead = True
                elif item.kind == 'mushroom':
                    self.powered = True
                    self.score += 1000
                    item.dead = True
                    for _ in range(15):
                        particles.append(Particle(item.rect.centerx, item.rect.centery,
                                                  random.randint(-4,4), random.randint(-6,0),
                                                  (220,40,40)))

        return None

    def check_collide_x(self, platforms):
        for p in platforms:
            if self.rect.colliderect(p.rect):
                if self.vx > 0:
                    self.x = p.rect.left - self.w
                elif self.vx < 0:
                    self.x = p.rect.right
                self.vx = 0

    def check_collide_y(self, platforms):
        for p in platforms:
            if self.rect.colliderect(p.rect):
                if self.vy > 0:
                    self.y = p.rect.top - self.h
                    self.vy = 0
                    self.on_ground = True
                elif self.vy < 0:
                    self.y = p.rect.bottom
                    self.vy = 0

    def take_hit(self):
        if self.invincible > 0: return
        if self.powered:
            self.powered = False
            self.invincible = 90
        else:
            self.die()

    def die(self):
        if self.dead: return
        self.dead = True
        self.vy = -10
        self.dead_timer = 0

    def draw(self, screen, cam):
        if self.dead:
            # 死亡动画 - 旋转缩小
            scale = max(0.2, 1 - self.dead_timer / 60)
            s = pygame.Surface((32, 52), pygame.SRCALPHA)
            pygame.draw.circle(s, (220,30,30), (16, 26), int(20*scale))
            pygame.draw.circle(s, (255,200,160), (16, 16), int(10*scale))
            r = cam.apply(self.rect)
            screen.blit(s, (r.x-2, r.y))
            return
        if self.invincible > 0 and (self.invincible // 4) % 2 == 0:
            return  # 闪烁
        key = f'mario_{"p_" if self.powered else ""}{self.anim_frame}{"_r" if self.facing==-1 else ""}'
        s = SPRITES[key]
        r = cam.apply(self.rect)
        screen.blit(s, (r.x-2, r.y))

class Platform:
    """普通地面/砖块"""
    def __init__(self, x, y, kind):
        self.x = x
        self.y = y
        self.w = TILE
        self.h = TILE
        self.kind = kind  # 'ground' 'brick' 'question' 'pipe'
        self.hit = False  # 问号被顶过
        self.bonus_spawned = False

    @property
    def rect(self):
        return pygame.Rect(self.x, self.y, self.w, self.h)

    def hit_by_mario(self, mario, items):
        if self.kind == 'question' and not self.hit:
            self.hit = True
            self.kind = 'ground'
            # 随机出蘑菇或金币
            if random.random() < 0.4 and not mario.powered:
                items.append(Item(self.x, self.y - 32, 'mushroom'))
            else:
                items.append(Item(self.x, self.y - 32, 'coin'))
                mario.score += 50
                mario.coins += 1
            return True
        elif self.kind == 'brick':
            if mario.powered:
                # 碎裂
                return 'break'
            else:
                # 顶一下动画
                return True
        return False

    def draw(self, screen, cam):
        r = cam.apply(self.rect)
        if self.kind == 'ground':
            screen.blit(SPRITES['ground'], r)
        elif self.kind == 'brick':
            screen.blit(SPRITES['brick'], r)
        elif self.kind == 'question':
            screen.blit(SPRITES['question'], r)
        elif self.kind == 'pipe':
            # 管道分段绘制：顶部画管口，下面画管身
            segs = getattr(self, 'pipe_segments', self.h // TILE)
            # 第一格（管口）
            r_top = pygame.Rect(r.x, r.y, TILE, TILE)
            screen.blit(SPRITES['pipe'], r_top)
            # 后续格（管身，用同一个精灵重复）
            for s in range(1, segs):
                r_seg = pygame.Rect(r.x, r.y + s * TILE, TILE, TILE)
                # 用管身部分（去掉顶部绿色边缘）
                screen.blit(SPRITES['pipe_body'], r_seg)

class Enemy:
    def __init__(self, x, y, kind='goomba'):
        self.x = x
        self.y = y
        self.w, self.h = 28, 28
        self.vx = -1
        self.vy = 0
        self.kind = kind
        self.dead = False
        self.dead_timer = 0
        self.anim_frame = 0
        self.anim_timer = 0

    @property
    def rect(self):
        return pygame.Rect(self.x, self.y, self.w, self.h)

    def update(self, platforms):
        if self.dead:
            self.dead_timer += 1
            self.vy += GRAVITY
            self.y += self.vy
            return
        self.vy += GRAVITY
        if self.vy > MAX_FALL: self.vy = MAX_FALL
        self.x += self.vx
        self.y += self.vy
        self.vy = 0

        # 平台边缘检测 + 碰撞
        on_platform = False
        for p in platforms:
            if self.rect.colliderect(p.rect):
                if self.vy >= 0 and self.rect.bottom - p.rect.top < 20:
                    self.y = p.rect.top - self.h
                    on_platform = True
                elif self.vy < 0:
                    self.y = p.rect.bottom

        # 简单巡逻：遇到障碍或边缘就掉头
        if not on_platform:
            self.vx = -self.vx
            self.x += self.vx * 2
        else:
            # 检查前方是否有墙
            probe = pygame.Rect(self.x + (self.w if self.vx>0 else -4), self.y+self.h-2, 4, 4)
            wall = any(probe.colliderect(p.rect) for p in platforms)
            # 检查前方是否悬空
            probe2 = pygame.Rect(self.x + (self.w+2 if self.vx>0 else -6), self.y+self.h+2, 4, 6)
            gap = not any(probe2.colliderect(p.rect) for p in platforms)
            if wall or gap:
                self.vx = -self.vx

        self.anim_timer += 1
        if self.anim_timer > 20:
            self.anim_timer = 0
            self.anim_frame = 1 - self.anim_frame

    def die(self):
        if self.dead: return
        self.dead = True
        self.vy = -6

    def draw(self, screen, cam):
        r = cam.apply(self.rect)
        if self.dead:
            # 压扁
            h = max(4, self.h - self.dead_timer * 2)
            s = SPRITES['goomba_0']
            s2 = pygame.transform.scale(s, (self.w, h))
            screen.blit(s2, (r.x, r.y + self.h - h))
            return
        s = SPRITES[f'goomba_{self.anim_frame}']
        screen.blit(s, (r.x-2, r.y-2))

class Item:
    def __init__(self, x, y, kind):
        self.x = x
        self.y = y
        self.w, self.h = 24, 24
        self.kind = kind
        self.vx = 1 if kind == 'mushroom' else 0
        self.vy = 0
        self.dead = False
        self.bob = 0

    @property
    def rect(self):
        return pygame.Rect(self.x, self.y, self.w, self.h)

    def update(self):
        if self.kind == 'mushroom':
            self.vy += GRAVITY
            if self.vy > MAX_FALL: self.vy = MAX_FALL
            self.x += self.vx
            self.y += self.vy
        else:
            self.bob += 0.15

    def draw(self, screen, cam):
        r = cam.apply(self.rect)
        if self.kind == 'coin':
            offset = int(math.sin(self.bob) * 4)
            s = SPRITES['coin']
            s2 = pygame.transform.scale(s, (20, 24))
            screen.blit(s2, (r.x+2, r.y+offset))
        elif self.kind == 'mushroom':
            screen.blit(SPRITES['mushroom'], (r.x-4, r.y-4))

class Particle:
    def __init__(self, x, y, vx, vy, color):
        self.x = x; self.y = y; self.vx = vx; self.vy = vy
        self.color = color; self.life = 30; self.size = random.randint(2,5)
    def update(self):
        self.x += self.vx; self.y += self.vy; self.vy += 0.3; self.life -= 1
    def draw(self, screen, cam):
        alpha = max(0, self.life * 8)
        c = (*self.color, alpha)
        s = pygame.Surface((self.size*2, self.size*2), pygame.SRCALPHA)
        pygame.draw.circle(s, c, (self.size, self.size), self.size)
        screen.blit(s, (self.x - cam.x - self.size, self.y - cam.y - self.size))

class FloatingText:
    def __init__(self, x, y, text, color=(255,255,0)):
        self.x = x; self.y = y; self.text = text; self.color = color
        self.life = 40; self.font = pygame.font.Font(None, 22)
    def update(self):
        self.y -= 1; self.life -= 1
    def draw(self, screen, cam):
        s = self.font.render(self.text, True, self.color)
        screen.blit(s, (self.x - cam.x, self.y - cam.y))

# ==================== 关卡加载 ====================
class Level:
    def __init__(self, layout):
        self.layout = layout
        self.h = len(layout)
        self.w = len(layout[0])
        self.height_px = self.h * TILE
        self.width_px = self.w * TILE
        self.platforms = []
        self.enemies = []
        self.items = []
        self.particles = []
        self.floating_texts = []
        self.mario = None
        self.flag_x = 0
        self.bg_elements = []
        self.parse()

    def parse(self):
        for row_i, row in enumerate(self.layout):
            for col_i, ch in enumerate(row):
                x = col_i * TILE
                y = row_i * TILE
                if ch == 'M':
                    self.mario = Mario(x, y)
                elif ch == '#':
                    self.platforms.append(Platform(x, y, 'ground'))
                elif ch == 'B':
                    self.platforms.append(Platform(x, y, 'brick'))
                elif ch == '?':
                    self.platforms.append(Platform(x, y, 'question'))
                elif ch == 'P':
                    # 管道占2格高（垂直方向连续两个P）
                    # 只在第一行P（顶部）创建完整的2格高碰撞体
                    is_top = (row_i == 0 or self.layout[row_i-1][col_i] != 'P')
                    if is_top:
                        # 检查下面有多少连续的P
                        pipe_h = 1
                        while row_i + pipe_h < len(self.layout) and self.layout[row_i+pipe_h][col_i] == 'P':
                            pipe_h += 1
                        self.platforms.append(Platform(x, y, 'pipe'))
                        self.platforms[-1].h = TILE * pipe_h
                        # 标记为管道段，用于渲染
                        self.platforms[-1].pipe_segments = pipe_h
                elif ch == 'G':
                    self.enemies.append(Enemy(x, y-4))
                elif ch == 'C':
                    self.items.append(Item(x+8, y+8, 'coin'))
                elif ch == 'F':
                    self.flag_x = x

        # 背景装饰（根据地图宽度动态生成）
        cloud_count = max(8, self.w // 3)
        hill_count = max(5, self.w // 4)
        for _ in range(cloud_count):
            self.bg_elements.append({
                'type': 'cloud',
                'x': random.randint(0, self.width_px),
                'y': random.randint(20, 150),
                'speed': random.uniform(0.1, 0.5)
            })
        for _ in range(hill_count):
            self.bg_elements.append({
                'type': 'hill',
                'x': random.randint(0, self.width_px),
                'y': SCREEN_H - TILE * 3 - 20,
            })
        # 远处山脉
        for _ in range(max(3, self.w // 6)):
            self.bg_elements.append({
                'type': 'mountain',
                'x': random.randint(0, self.width_px),
                'y': SCREEN_H - TILE * 4 - 40,
            })

    def update(self):
        result = self.mario.update(self, self.platforms, self.enemies, self.items, self.particles)
        # 更新敌人
        for e in self.enemies[:]:
            e.update(self.platforms)
            if e.dead and e.dead_timer > 30:
                self.enemies.remove(e)
        # 更新道具
        for item in self.items[:]:
            item.update()
            if item.dead: self.items.remove(item)
            # 道具落地
            if item.kind == 'mushroom':
                for p in self.platforms:
                    if item.rect.colliderect(p.rect):
                        item.y = p.rect.top - item.h
                        item.vy = 0
                        break
            # 道具掉出地图
            if item.y > self.height_px + 200:
                item.dead = True
        # 更新粒子
        for p in self.particles[:]:
            p.update()
            if p.life <= 0: self.particles.remove(p)
        # 浮动文字
        for ft in self.floating_texts[:]:
            ft.update()
            if ft.life <= 0: self.floating_texts.remove(ft)

        # 检查旗杆过关
        if self.mario.rect.colliderect(pygame.Rect(self.flag_x, 0, 40, SCREEN_H)):
            self.mario.win = True

        # 顶砖块检测
        if self.mario.vy < 0:
            head = pygame.Rect(self.mario.x, self.mario.y - 4, self.mario.w, 4)
            for p in self.platforms[:]:
                if head.colliderect(p.rect):
                    if p.kind == 'brick' and self.mario.powered:
                        self.particles.append(Particle(p.rect.centerx, p.rect.centery, 0, -3, BRICK_RED))
                        self.platforms.remove(p)
                        self.mario.score += 50
                    elif p.kind in ('question', 'brick'):
                        result2 = p.hit_by_mario(self.mario, self.items)
                        if result2 == 'break':
                            self.particles.append(Particle(p.rect.centerx, p.rect.centery, 0, -3, BRICK_RED))
                            self.platforms.remove(p)
                            self.mario.score += 50
                    # 顶到普通砖块的小动画
                    if p.kind == 'brick':
                        self.floating_texts.append(FloatingText(p.rect.x, p.rect.y - 20, "BONK!", (200,200,200)))

        return result

    def draw(self, screen, cam):
        # 背景
        screen.fill(SKY_BLUE)
        # 背景元素
        for el in self.bg_elements:
            if el['type'] == 'cloud':
                r = pygame.Rect(el['x'] - cam.x * el['speed'], el['y'], 80, 40)
                screen.blit(SPRITES['cloud'], r)
            elif el['type'] == 'hill':
                # 简单小山丘
                x = el['x'] - cam.x * 0.3
                y = el['y']
                pygame.draw.ellipse(screen, (0, 140, 0), (x-30, y-20, 100, 60))
                pygame.draw.ellipse(screen, GROUND_GREEN, (x-20, y-30, 70, 50))
            elif el['type'] == 'mountain':
                # 远处山脉（视差更慢）
                x = el['x'] - cam.x * 0.15
                y = el['y']
                pts = [(x-50, y+60), (x, y), (x+50, y+60)]
                pygame.draw.polygon(screen, (100, 100, 120), pts)
                # 雪顶
                pts2 = [(x-15, y+18), (x, y), (x+15, y+18)]
                pygame.draw.polygon(screen, CLOUD_WHITE, pts2)

        # 平台
        for p in self.platforms:
            p.draw(screen, cam)
        # 旗杆
        if self.flag_x:
            r = pygame.Rect(self.flag_x, 0, 40, SCREEN_H)
            r2 = cam.apply(r)
            screen.blit(SPRITES['flag'], r2)
        # 道具
        for item in self.items:
            item.draw(screen, cam)
        # 敌人
        for e in self.enemies:
            e.draw(screen, cam)
        # 马里奥
        self.mario.draw(screen, cam)
        # 粒子
        for p in self.particles:
            p.draw(screen, cam)
        # 浮动文字
        for ft in self.floating_texts:
            ft.draw(screen, cam)

# ==================== HUD ====================
def draw_hud(screen, mario, level_num, total_levels):
    font = pygame.font.Font(None, 28)
    # 分数
    s = font.render(f"SCORE: {mario.score}", True, WHITE)
    screen.blit(s, (20, 12))
    # 金币
    s = font.render(f"COINS: {mario.coins}", True, COIN_YELLOW)
    screen.blit(s, (220, 12))
    # 生命
    s = font.render(f"LIVES: {mario.lives}", True, WHITE)
    screen.blit(s, (400, 12))
    # 世界
    s = font.render(f"WORLD {level_num}/{total_levels}", True, WHITE)
    screen.blit(s, (600, 12))
    # 能量条
    if mario.powered:
        bar_w = 100
        pygame.draw.rect(screen, (30,100,220), (SCREEN_W - 140, 48, bar_w, 12))
        pygame.draw.rect(screen, WHITE, (SCREEN_W - 140, 48, bar_w, 12), 2)

def draw_start_screen(screen):
    screen.fill(SKY_BLUE)
    font_big = pygame.font.Font(None, 72)
    font_sm = pygame.font.Font(None, 36)
    t = font_big.render("SUPER MARIO", True, (220, 40, 40))
    screen.blit(t, (SCREEN_W//2 - t.get_width()//2, 150))
    t2 = font_sm.render("Press SPACE to Start", True, WHITE)
    screen.blit(t2, (SCREEN_W//2 - t2.get_width()//2, 280))
    t3 = font_sm.render("Arrow Keys / WASD to Move & Jump", True, WHITE)
    screen.blit(t3, (SCREEN_W//2 - t3.get_width()//2, 340))
    t4 = font_sm.render("SPACE / UP / W to Jump", True, WHITE)
    screen.blit(t4, (SCREEN_W//2 - t4.get_width()//2, 380))
    # 画个小马里奥
    screen.blit(SPRITES['mario_0'], (SCREEN_W//2 - 16, 430))
    pygame.display.flip()

def draw_game_over(screen, mario):
    s = pygame.Surface((SCREEN_W, SCREEN_H), pygame.SRCALPHA)
    s.fill((0, 0, 0, 180))
    screen.blit(s, (0, 0))
    font = pygame.font.Font(None, 64)
    t = font.render("GAME OVER", True, (220, 40, 40))
    screen.blit(t, (SCREEN_W//2 - t.get_width()//2, SCREEN_H//2 - 50))
    font2 = pygame.font.Font(None, 32)
    t2 = font2.render(f"Final Score: {mario.score}", True, WHITE)
    screen.blit(t2, (SCREEN_W//2 - t2.get_width()//2, SCREEN_H//2 + 20))
    t3 = font2.render("Press R to Restart", True, WHITE)
    screen.blit(t3, (SCREEN_W//2 - t3.get_width()//2, SCREEN_H//2 + 60))
    pygame.display.flip()

def draw_win_screen(screen, mario, is_final=False):
    s = pygame.Surface((SCREEN_W, SCREEN_H), pygame.SRCALPHA)
    s.fill((0, 0, 0, 120))
    screen.blit(s, (0, 0))
    font = pygame.font.Font(None, 64)
    txt = "YOU WIN!" if is_final else "LEVEL CLEAR!"
    c = FLAG_RED if is_final else COIN_YELLOW
    t = font.render(txt, True, c)
    screen.blit(t, (SCREEN_W//2 - t.get_width()//2, SCREEN_H//2 - 50))
    font2 = pygame.font.Font(None, 32)
    t2 = font2.render(f"Score: {mario.score}  Coins: {mario.coins}", True, WHITE)
    screen.blit(t2, (SCREEN_W//2 - t2.get_width()//2, SCREEN_H//2 + 20))
    if not is_final:
        t3 = font2.render("Press SPACE for Next Level", True, WHITE)
        screen.blit(t3, (SCREEN_W//2 - t3.get_width()//2, SCREEN_H//2 + 60))
    else:
        t3 = font2.render("Press R to Play Again", True, WHITE)
        screen.blit(t3, (SCREEN_W//2 - t3.get_width()//2, SCREEN_H//2 + 60))
    pygame.display.flip()

# ==================== 主循环 ====================
import math

def main():
    screen = pygame.display.set_mode((SCREEN_W, SCREEN_H))
    clock = pygame.time.Clock()
    total_levels = 3
    current_level_idx = 0
    levels_data = [LEVEL, LEVEL2, LEVEL3]
    state = "start"  # start, play, gameover, win, level_clear
    cam = Camera()
    level = None
    mario = None

    def load_level(idx):
        nonlocal level, mario, state
        level = Level(levels_data[idx])
        mario = level.mario
        cam.x = 0
        cam.y = 0
        state = "play"

    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            if event.type == pygame.KEYDOWN:
                if state == "start" and event.key == pygame.K_SPACE:
                    current_level_idx = 0
                    load_level(0)
                elif state == "gameover" and event.key == pygame.K_r:
                    current_level_idx = 0
                    load_level(0)
                elif state == "level_clear" and event.key == pygame.K_SPACE:
                    current_level_idx += 1
                    if current_level_idx >= total_levels:
                        state = "win_final"
                    else:
                        load_level(current_level_idx)
                elif state == "win_final" and event.key == pygame.K_r:
                    current_level_idx = 0
                    load_level(0)

        if state == "start":
            draw_start_screen(screen)
        elif state == "play":
            result = level.update()
            cam.update(mario)
            if result == "gameover":
                state = "gameover"
            elif result == "respawn":
                # 重置马里奥
                mario.x = 80
                mario.y = 200
                mario.vx = 0
                mario.vy = 0
                mario.dead = False
                mario.invincible = 60
                mario.powered = False
            if mario.win:
                if current_level_idx + 1 >= total_levels:
                    state = "win_final"
                else:
                    state = "level_clear"

            level.draw(screen, cam)
            draw_hud(screen, mario, current_level_idx + 1, total_levels)

            # 过关提示条
            if mario.win:
                s = pygame.Surface((SCREEN_W, 60), pygame.SRCALPHA)
                s.fill((0, 0, 0, 160))
                screen.blit(s, (0, SCREEN_H//2 - 30))
                font = pygame.font.Font(None, 36)
                t = font.render("LEVEL CLEAR! Press SPACE" if current_level_idx+1<total_levels else "YOU WIN! Press R", True, COIN_YELLOW)
                screen.blit(t, (SCREEN_W//2 - t.get_width()//2, SCREEN_H//2 - 12))

            pygame.display.flip()
        elif state == "gameover":
            draw_game_over(screen, mario)
        elif state in ("level_clear", "win_final"):
            level.draw(screen, cam)
            draw_hud(screen, mario, current_level_idx + 1, total_levels)
            draw_win_screen(screen, mario, is_final=(state == "win_final"))

        clock.tick(FPS)

    pygame.quit()
    sys.exit()

if __name__ == "__main__":
    main()