import pygame
import sys
import math
import random
from pygame.locals import *
import heapq

# ---------- 初始化 ----------
pygame.init()
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("三角洲行动 · FPS · 全Boss技能")
clock = pygame.time.Clock()

# ---------- 字体 ----------
def load_font(size):
    font_names = ['simhei', 'microsoft yahei', 'pingfang sc', 'noto sans sc', 'arial']
    for name in font_names:
        try:
            return pygame.font.SysFont(name, size)
        except:
            continue
    return pygame.font.Font(None, size)

font = load_font(24)
small_font = load_font(18)
big_font = load_font(48)

pygame.mouse.set_visible(False)
pygame.event.set_grab(True)

# ---------- 游戏状态 ----------
SPAWN_COOLDOWN = 180  # 敌人生成冷却时间（帧数）  

STATE_MENU = 0
STATE_GAME = 1
STATE_BOSS = 2
game_state = STATE_MENU

MODE_ENDLESS = 0
MODE_CLASSIC = 1
current_mode = MODE_ENDLESS

selected_boss_index = 0
boss_names = ["典狱长格赫罗斯", "雷丝", "赛伊德", "渡鸦", "德穆兰"]

# ---------- 大地图 20x20 ----------
MAP = [
    [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,1,1,1,0,0,1,1,0,0,0,1,1,0,0,0,1,0,1],
    [1,0,1,0,0,0,0,0,1,0,0,0,0,1,0,0,0,1,0,1],
    [1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,0,0,0,1],
    [1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,1,1,1,0,0,0,0,0,1,1,1,0,0,0,0,0,0,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,0,1],
    [1,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1],
    [1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1],
    [1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,1,1,1,0,0,0,0,0,1,1,1,0,0,0,0,1,0,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
]
MAP_SIZE = len(MAP)

# ---------- 玩家属性 ----------
player_x, player_y = 10.5, 10.5
player_angle = 0.0
player_pitch = 0.0
PLAYER_RADIUS = 0.3
NORMAL_SPEED = 0.08
SLIDE_SPEED = 0.14
CROUCH_SPEED = 0.04
MOVE_SPEED = NORMAL_SPEED
MOUSE_SENSITIVITY = 0.002
FOV = math.pi / 3
NUM_RAYS = 400

player_hp = 100
max_hp = 100
player_height = 0.0
vertical_vel = 0.0
is_grounded = True
is_sliding = False
slide_timer = 0
SLIDE_DURATION = 20
JUMP_FORCE = -0.3
GRAVITY = 0.015

invincible = False
invincible_timer = 0
INVINCIBLE_DURATION = 30

lean_offset = 0.0
LEAN_AMOUNT = 0.5

# 医疗充能
heal_charges = 100
max_heal_charges = 100
heal_active = False
heal_timer = 0

# 负面状态
player_slowed = False
player_slow_timer = 0
player_distorted = False

# ---------- 武器系统 ----------
class Weapon:
    def __init__(self, name, damage, clip_size, fire_rate, recoil, reload_time, color, price=0):
        self.name = name
        self.damage = damage
        self.clip_size = clip_size
        self.fire_rate = fire_rate
        self.recoil = math.radians(recoil)
        self.reload_time = reload_time
        self.color = color
        self.price = price
        self.cooldown_frames = int(60 / fire_rate) if fire_rate > 0 else 999

weapons = [
    Weapon("手枪",     25, 12, 3.0, 0.8, 1.5, (80,80,80), 0),
    Weapon("冲锋枪",   20, 30, 8.0, 1.5, 2.2, (100,100,150), 300),
    Weapon("霰弹枪",   45, 8,  1.2, 3.5, 2.8, (150,100,50), 600),
    Weapon("狙击枪",   80, 5,  0.8, 5.0, 3.5, (50,80,50), 1000),
    Weapon("步枪",     30, 25, 5.0, 2.0, 2.5, (120,80,60), 800),
    Weapon("轻机枪",   22, 50, 10.0, 1.8, 3.2, (80,60,100), 1200),
    Weapon("左轮手枪", 60, 6,  1.5, 4.0, 2.0, (120,120,120), 500)
]
current_weapon_index = 0
current_weapon = weapons[current_weapon_index]
clip_ammo = current_weapon.clip_size
reserve_ammo = 300
is_reloading = False
reload_timer = 0
RELOAD_TIME = int(current_weapon.reload_time * 60)

shot_cooldown = 0
MAX_SHOT_COOLDOWN = current_weapon.cooldown_frames

money = 500
KILL_REWARD = 100
shop_open = False
# ---------- 技能区域 ----------
class FireZone:
    def __init__(self, x, y, duration=300, damage=10, radius=2.0):
        self.x = x
        self.y = y
        self.duration = duration
        self.max_duration = duration
        self.damage = damage
        self.radius = radius
        self.alive = True
        self.damage_cooldown = 0
    
    def update(self):
        self.duration -= 1
        if self.duration <= 0:
            self.alive = False

class PoisonZone:
    def __init__(self, x, y, duration=360, radius=2.5):
        self.x = x
        self.y = y
        self.duration = duration
        self.max_duration = duration
        self.radius = radius
        self.alive = True
    
    def update(self):
        self.duration -= 1
        if self.duration <= 0:
            self.alive = False

fire_zones = []
poison_zones = []

# ---------- A*寻路 ----------
def astar(start, goal, map_data, map_size):
    def heuristic(a, b):
        return abs(a[0] - b[0]) + abs(a[1] - b[1])
    
    def get_neighbors(node):
        x, y = node
        neighbors = []
        for dx, dy in [(0,1), (1,0), (0,-1), (-1,0), (1,1), (1,-1), (-1,1), (-1,-1)]:
            nx, ny = x + dx, y + dy
            if 0 <= nx < map_size and 0 <= ny < map_size:
                if map_data[ny][nx] == 0:
                    cost = 1.414 if dx != 0 and dy != 0 else 1.0
                    neighbors.append(((nx, ny), cost))
        return neighbors
    
    start = (int(start[0]), int(start[1]))
    goal = (int(goal[0]), int(goal[1]))
    
    frontier = []
    heapq.heappush(frontier, (0, start))
    came_from = {start: None}
    cost_so_far = {start: 0}
    
    while frontier:
        _, current = heapq.heappop(frontier)
        if current == goal:
            break
        for next_node, cost in get_neighbors(current):
            new_cost = cost_so_far[current] + cost
            if next_node not in cost_so_far or new_cost < cost_so_far[next_node]:
                cost_so_far[next_node] = new_cost
                priority = new_cost + heuristic(next_node, goal)
                heapq.heappush(frontier, (priority, next_node))
                came_from[next_node] = current
    
    if goal not in came_from:
        return []
    
    path = []
    current = goal
    while current != start:
        path.append(current)
        current = came_from[current]
    path.reverse()
    return path
# ---------- 子弹（玩家）----------
class Bullet:
    def __init__(self, x, y, angle, pitch, speed=0.25, damage=25):
        self.x = x
        self.y = y
        self.angle = angle
        self.pitch = pitch
        self.speed = speed
        self.damage = damage
        self.alive = True
        self.life = 120

    def update(self):
        horiz_speed = self.speed * math.cos(self.pitch)
        self.x += math.cos(self.angle) * horiz_speed
        self.y += math.sin(self.angle) * horiz_speed
        self.life -= 1
        if self.life <= 0:
            self.alive = False
            return
        map_x, map_y = int(self.x), int(self.y)
        if 0 <= map_x < MAP_SIZE and 0 <= map_y < MAP_SIZE:
            if MAP[map_y][map_x] == 1:
                self.alive = False

bullets = []

# ---------- 敌人子弹 ----------
class EnemyBullet:
    def __init__(self, x, y, angle, speed=0.15, damage=10, is_fire=False):
        self.x = x
        self.y = y
        self.angle = angle
        self.speed = speed
        self.damage = damage
        self.alive = True
        self.life = 80
        self.is_fire = is_fire

    def update(self):
        self.x += math.cos(self.angle) * self.speed
        self.y += math.sin(self.angle) * self.speed
        self.life -= 1
        if self.life <= 0:
            self.alive = False
            return
        map_x, map_y = int(self.x), int(self.y)
        if 0 <= map_x < MAP_SIZE and 0 <= map_y < MAP_SIZE:
            if MAP[map_y][map_x] == 1:
                self.alive = False

enemy_bullets = []

# ---------- 护卫队 ----------
class Guard:
    def __init__(self, x, y, guard_type="assault"):
        self.x = x
        self.y = y
        self.alive = True
        self.radius = 0.4
        self.type = guard_type
        self.speed = 0.018
        self.attack_cooldown = 0
        self.shoot_cooldown = 0
        self.shoot_interval = 40
        self.shoot_range = 7.0
        self.bullet_speed = 0.16
        self.path = []
        self.path_timer = 0
        
        if guard_type == "shield":
            self.hp = 150
            self.max_hp = 150
            self.attack_damage = 15
            self.color = (100, 100, 150)
            self.has_shield = True
        else:
            self.hp = 120
            self.max_hp = 120
            self.attack_damage = 20
            self.color = (150, 80, 80)
            self.has_shield = False

    def update(self, px, py):
        if not self.alive: return
        
        dx = px - self.x
        dy = py - self.y
        dist = math.hypot(dx, dy)
        
        self.path_timer -= 1
        if self.path_timer <= 0:
            self.path = astar((self.x, self.y), (px, py), MAP, MAP_SIZE)
            self.path_timer = 30
        
        if len(self.path) > 0 and dist > 2.0:
            next_node = self.path[0]
            target_x, target_y = next_node[0] + 0.5, next_node[1] + 0.5
            dx = target_x - self.x
            dy = target_y - self.y
            d = math.hypot(dx, dy)
            if d > 0.1:
                self.x += (dx / d) * self.speed
                self.y += (dy / d) * self.speed
            if d < 0.3:
                self.path.pop(0)
        elif dist > 1.5:
            self.x += (dx / dist) * self.speed
            self.y += (dy / dist) * self.speed
        
        if self.attack_cooldown > 0:
            self.attack_cooldown -= 1
        if self.shoot_cooldown > 0:
            self.shoot_cooldown -= 1
        
        if dist < self.shoot_range and self.shoot_cooldown <= 0:
            angle = math.atan2(py - self.y, px - self.x)
            enemy_bullets.append(EnemyBullet(self.x, self.y, angle,
                                             speed=self.bullet_speed, damage=self.attack_damage))
            self.shoot_cooldown = self.shoot_interval
        
        if dist < 1.2 and self.attack_cooldown == 0:
            global player_hp, invincible, invincible_timer
            if not invincible:
                player_hp -= self.attack_damage
                invincible = True
                invincible_timer = INVINCIBLE_DURATION
            self.attack_cooldown = 50

guards = []

# ---------- 德穆兰战车 ----------
class Tank:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.alive = True
        self.radius = 0.8
        self.hp = 200
        self.max_hp = 200
        self.speed = 0.012
        self.shoot_cooldown = 0
        self.shoot_interval = 15
        self.shoot_range = 9.0
        self.bullet_speed = 0.2
        self.attack_damage = 15
        self.color = (80, 80, 100)
        self.path = []
        self.path_timer = 0

    def update(self, px, py):
        if not self.alive: return
        
        dx = px - self.x
        dy = py - self.y
        dist = math.hypot(dx, dy)
        
        self.path_timer -= 1
        if self.path_timer <= 0:
            self.path = astar((self.x, self.y), (px, py), MAP, MAP_SIZE)
            self.path_timer = 40
        
        if len(self.path) > 0 and dist > 3.0:
            next_node = self.path[0]
            target_x, target_y = next_node[0] + 0.5, next_node[1] + 0.5
            dx = target_x - self.x
            dy = target_y - self.y
            d = math.hypot(dx, dy)
            if d > 0.1:
                self.x += (dx / d) * self.speed
                self.y += (dy / d) * self.speed
            if d < 0.5:
                self.path.pop(0)
        
        if self.shoot_cooldown > 0:
            self.shoot_cooldown -= 1
        
        if dist < self.shoot_range and self.shoot_cooldown <= 0:
            angle = math.atan2(py - self.y, px - self.x)
            enemy_bullets.append(EnemyBullet(self.x, self.y, angle,
                                             speed=self.bullet_speed, damage=self.attack_damage))
            self.shoot_cooldown = self.shoot_interval

tanks = []
# ---------- Boss ----------
class Boss:
    def __init__(self, name, x, y):
        self.name = name
        self.x = x
        self.y = y
        self.alive = True
        self.radius = 0.8
        self.hp = 500
        self.max_hp = 500
        self.speed = 0.018
        self.attack_cooldown = 0
        self.attack_damage = 25
        self.shoot_cooldown = 0
        self.shoot_interval = 20
        self.shoot_range = 12.0
        self.bullet_speed = 0.2
        self.phase = 1
        self.summon_timer = 0
        self.color = (200, 50, 50)
        self.path = []
        self.path_timer = 0

        if name == "典狱长格赫罗斯":
            self.attack_damage = 30
            self.shoot_interval = 25
            self.burst_count = 4
        elif name == "雷丝":
            self.attack_damage = 20
            self.shoot_interval = 30
            self.burst_count = 3
            self.spread = 0.2
        elif name == "赛伊德":
            self.attack_damage = 25
            self.shoot_interval = 15
            self.bullet_speed = 0.2
            self.fire_cooldown = 0
        elif name == "渡鸦":
            self.attack_damage = 25
            self.shoot_interval = 20
            self.poison_cooldown = 0
        elif name == "德穆兰":
            self.attack_damage = 45
            self.shoot_interval = 40
            self.bullet_speed = 0.35

    def update(self, px, py):
        global player_hp, invincible, invincible_timer
        if not self.alive: return
        
        dx = px - self.x
        dy = py - self.y
        dist = math.hypot(dx, dy)
        
        self.path_timer -= 1
        if self.path_timer <= 0:
            self.path = astar((self.x, self.y), (px, py), MAP, MAP_SIZE)
            self.path_timer = 20
        
        if len(self.path) > 0 and dist > 2.5:
            next_node = self.path[0]
            target_x, target_y = next_node[0] + 0.5, next_node[1] + 0.5
            dx = target_x - self.x
            dy = target_y - self.y
            d = math.hypot(dx, dy)
            if d > 0.1:
                self.x += (dx / d) * self.speed
                self.y += (dy / d) * self.speed
            if d < 0.4:
                self.path.pop(0)
        elif dist > 2.0:
            self.x += (dx / dist) * self.speed
            self.y += (dy / dist) * self.speed
        
        if self.attack_cooldown > 0:
            self.attack_cooldown -= 1
        if self.shoot_cooldown > 0:
            self.shoot_cooldown -= 1
        if self.summon_timer > 0:
            self.summon_timer -= 1
        if hasattr(self, 'fire_cooldown'):
            if self.fire_cooldown > 0:
                self.fire_cooldown -= 1
        if hasattr(self, 'poison_cooldown'):
            if self.poison_cooldown > 0:
                self.poison_cooldown -= 1

        if self.hp <= self.max_hp * 0.5 and self.phase == 1:
            self.phase = 2
            if self.name == "典狱长格赫罗斯":
                self.shoot_interval = 15
            elif self.name == "雷丝":
                self.shoot_interval = 20
                self.attack_damage = 30
            elif self.name == "赛伊德":
                self.shoot_interval = 7
            elif self.name == "渡鸦":
                self.shoot_interval = 12
            elif self.name == "德穆兰":
                self.shoot_interval = 25

        if dist < self.shoot_range and self.shoot_cooldown <= 0:
            angle = math.atan2(py - self.y, px - self.x)

            if self.name == "典狱长格赫罗斯":
                for i in range(4):
                    spread = random.uniform(-0.05, 0.05)
                    enemy_bullets.append(EnemyBullet(self.x, self.y, angle + spread,
                                                     speed=self.bullet_speed, damage=30))
                self.shoot_cooldown = self.shoot_interval

            elif self.name == "雷丝":
                for i in range(3):
                    spread = random.uniform(-self.spread, self.spread)
                    enemy_bullets.append(EnemyBullet(self.x, self.y, angle + spread,
                                                     speed=self.bullet_speed, damage=20))
                self.shoot_cooldown = self.shoot_interval

            elif self.name == "赛伊德":
                enemy_bullets.append(EnemyBullet(self.x, self.y, angle,
                                                 speed=self.bullet_speed, damage=self.attack_damage))
                if self.fire_cooldown <= 0:
                    fire_zones.append(FireZone(px, py))
                    self.fire_cooldown = 180
                if self.phase == 2 and random.random() < 0.1:
                    self.x += random.uniform(-1.5, 1.5)
                    self.y += random.uniform(-1.5, 1.5)
                self.shoot_cooldown = self.shoot_interval

            elif self.name == "渡鸦":
                enemy_bullets.append(EnemyBullet(self.x, self.y, angle + 0.1,
                                                 speed=self.bullet_speed, damage=self.attack_damage))
                enemy_bullets.append(EnemyBullet(self.x, self.y, angle - 0.1,
                                                 speed=self.bullet_speed, damage=self.attack_damage))
                if self.poison_cooldown <= 0:
                    poison_zones.append(PoisonZone(px, py))
                    self.poison_cooldown = 240
                self.shoot_cooldown = self.shoot_interval

            elif self.name == "德穆兰":
                enemy_bullets.append(EnemyBullet(self.x, self.y, angle,
                                                 speed=self.bullet_speed, damage=self.attack_damage))
                self.shoot_cooldown = self.shoot_interval

        if dist < 1.8 and self.attack_cooldown == 0:
            if self.name == "渡鸦" and dist < 1.5:
                player_hp -= 100
                invincible = True
                invincible_timer = INVINCIBLE_DURATION
                self.attack_cooldown = 120
            else:
                player_hp -= self.attack_damage
                invincible = True
                invincible_timer = INVINCIBLE_DURATION
                self.attack_cooldown = 50

boss = None

# ---------- 普通敌人（无尽模式）----------
class Enemy:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.alive = True
        self.radius = 0.4
        self.hp = 30
        self.max_hp = 30
        self.speed = 0.02
        self.attack_cooldown = 0
        self.attack_damage = 10
        self.shoot_cooldown = 0
        self.shoot_interval = 90
        self.shoot_range = 8.0
        self.bullet_speed = 0.15
        self.path = []
        self.path_timer = 0

    def update(self, px, py):
        if not self.alive: return
        dx = px - self.x
        dy = py - self.y
        dist = math.hypot(dx, dy)
        
        self.path_timer -= 1
        if self.path_timer <= 0:
            self.path = astar((self.x, self.y), (px, py), MAP, MAP_SIZE)
            self.path_timer = 45
        
        if len(self.path) > 0 and dist > 1.5:
            next_node = self.path[0]
            target_x, target_y = next_node[0] + 0.5, next_node[1] + 0.5
            dx = target_x - self.x
            dy = target_y - self.y
            d = math.hypot(dx, dy)
            if d > 0.1:
                self.x += (dx / d) * self.speed
                self.y += (dy / d) * self.speed
            if d < 0.4:
                self.path.pop(0)
        elif dist > 1.2:
            self.x += (dx / dist) * self.speed
            self.y += (dy / dist) * self.speed
        
        if self.attack_cooldown > 0:
            self.attack_cooldown -= 1
        if self.shoot_cooldown > 0:
            self.shoot_cooldown -= 1
        
        if dist < self.shoot_range and self.shoot_cooldown <= 0:
            angle_to_player = math.atan2(py - self.y, px - self.x)
            enemy_bullets.append(EnemyBullet(self.x, self.y, angle_to_player,
                                             speed=self.bullet_speed, damage=self.attack_damage))
            self.shoot_cooldown = self.shoot_interval

enemies = []

def spawn_enemy():
    while True:
        ex = random.uniform(1.5, MAP_SIZE-1.5)
        ey = random.uniform(1.5, MAP_SIZE-1.5)
        if MAP[int(ey)][int(ex)] == 0:
            if math.hypot(ex - player_x, ey - player_y) > 5.0:
                enemies.append(Enemy(ex, ey))
                break

def spawn_boss_guards(boss_name):
    guards.clear()
    tanks.clear()
    base_x, base_y = boss.x, boss.y
    
    if boss_name == "典狱长格赫罗斯":
        for i in range(2):
            guards.append(Guard(base_x + random.uniform(-2, 2), base_y + random.uniform(-2, 2), "shield"))
        for i in range(3):
            guards.append(Guard(base_x + random.uniform(-2, 2), base_y + random.uniform(-2, 2), "assault"))
    elif boss_name == "雷丝":
        for i in range(2):
            guards.append(Guard(base_x + random.uniform(-2, 2), base_y + random.uniform(-2, 2), "shield"))
        for i in range(2):
            guards.append(Guard(base_x + random.uniform(-2, 2), base_y + random.uniform(-2, 2), "assault"))
    elif boss_name == "渡鸦":
        for i in range(4):
            g = Guard(base_x + random.uniform(-2, 2), base_y + random.uniform(-2, 2), "assault")
            g.hp = 200
            g.max_hp = 200
            g.attack_damage = 30
            guards.append(g)
    elif boss_name == "德穆兰":
        for i in range(2):
            tanks.append(Tank(base_x + random.uniform(-3, 3), base_y + random.uniform(-3, 3)))
# ---------- 碰撞检测 ----------
def can_move_to(new_x, new_y):
    for dy in [-PLAYER_RADIUS, 0, PLAYER_RADIUS]:
        for dx in [-PLAYER_RADIUS, 0, PLAYER_RADIUS]:
            cx = new_x + dx
            cy = new_y + dy
            map_x = int(cx)
            map_y = int(cy)
            if 0 <= map_x < MAP_SIZE and 0 <= map_y < MAP_SIZE:
                if MAP[map_y][map_x] == 1:
                    cell_left = map_x
                    cell_right = map_x + 1
                    cell_top = map_y
                    cell_bottom = map_y + 1
                    closest_x = max(cell_left, min(cx, cell_right))
                    closest_y = max(cell_top, min(cy, cell_bottom))
                    dist = math.hypot(cx - closest_x, cy - closest_y)
                    if dist < PLAYER_RADIUS:
                        return False
    return True

# ---------- 射线 ----------
def cast_ray(ax, ay, angle, pitch):
    dx = math.cos(angle) * math.cos(pitch)
    dy = math.sin(angle) * math.cos(pitch)
    map_x, map_y = int(ax), int(ay)
    delta_dist_x = abs(1 / dx) if dx != 0 else 1e30
    delta_dist_y = abs(1 / dy) if dy != 0 else 1e30
    step_x = 1 if dx > 0 else -1
    step_y = 1 if dy > 0 else -1
    if dx > 0:
        side_dist_x = (map_x + 1 - ax) * delta_dist_x
    else:
        side_dist_x = (ax - map_x) * delta_dist_x
    if dy > 0:
        side_dist_y = (map_y + 1 - ay) * delta_dist_y
    else:
        side_dist_y = (ay - map_y) * delta_dist_y
    hit = False
    side = 0
    while not hit:
        if side_dist_x < side_dist_y:
            side_dist_x += delta_dist_x
            map_x += step_x
            side = 0
        else:
            side_dist_y += delta_dist_y
            map_y += step_y
            side = 1
        if map_x < 0 or map_x >= MAP_SIZE or map_y < 0 or map_y >= MAP_SIZE:
            return 40.0, side, (ax + dx*40, ay + dy*40)
        if MAP[map_y][map_x] == 1:
            hit = True
    if side == 0:
        perp_dist = (map_x - ax + (1 - step_x) / 2) / dx
    else:
        perp_dist = (map_y - ay + (1 - step_y) / 2) / dy
    hit_x = ax + dx * perp_dist
    hit_y = ay + dy * perp_dist
    return perp_dist, side, (hit_x, hit_y)

# ---------- 绘制枪械模型 ----------
def draw_weapon_model(surf, weapon, is_reloading=False):
    w, h = surf.get_size()
    body_rect = pygame.Rect(w//2 - 40, h - 100, 80, 40)
    pygame.draw.rect(surf, weapon.color, body_rect)
    pygame.draw.rect(surf, (40,40,40), body_rect, 2)
    barrel_rect = pygame.Rect(w//2 + 30, h - 95, 60, 15)
    pygame.draw.rect(surf, (40,40,40), barrel_rect)
    grip_rect = pygame.Rect(w//2 - 50, h - 90, 15, 40)
    pygame.draw.rect(surf, (60,50,40), grip_rect)
    mag_rect = pygame.Rect(w//2 - 30, h - 80, 20, 35)
    pygame.draw.rect(surf, (30,30,30), mag_rect)
    pygame.draw.circle(surf, (200,200,200), (w//2 + 70, h - 88), 5)
    name_surf = small_font.render(weapon.name, True, (255,255,255))
    surf.blit(name_surf, (w//2 - 30, h - 120))
    if is_reloading:
        reload_text = small_font.render("换弹中...", True, (255,200,0))
        surf.blit(reload_text, (w//2 - 30, h - 140))

# ---------- 绘制商店 ----------
def draw_shop(surf, money, weapons, current_idx):
    overlay = pygame.Surface(surf.get_size(), pygame.SRCALPHA)
    overlay.fill((0,0,0,180))
    surf.blit(overlay, (0,0))

    title = font.render("武器商店 (按数字键1-7购买, B关闭)", True, (255,255,0))
    surf.blit(title, (SCREEN_WIDTH//2 - title.get_width()//2, 80))

    y_offset = 150
    for i, wp in enumerate(weapons):
        color = (0,255,0) if i == current_idx else (200,200,200)
        text = f"{i+1}. {wp.name} - 伤害:{wp.damage} 弹容:{wp.clip_size} 射速:{wp.fire_rate:.1f}/s 价格:{wp.price}"
        if i == 0:
            text += " (默认)"
        line = small_font.render(text, True, color)
        surf.blit(line, (SCREEN_WIDTH//2 - 350, y_offset))
        y_offset += 35
    
    heal_text = font.render("8. 医疗充能 +50 价格:$100", True, (200,200,200))
    surf.blit(heal_text, (SCREEN_WIDTH//2 - 350, y_offset))
    y_offset += 35

    money_text = font.render(f"当前金钱: ${money}  医疗充能: {heal_charges}", True, (255,255,0))
    surf.blit(money_text, (SCREEN_WIDTH//2 - 100, y_offset + 20))
# ---------- 重置游戏 ----------
def reset_game():
    global player_x, player_y, player_angle, player_pitch, player_hp, max_hp, player_height, vertical_vel, is_grounded, is_sliding, slide_timer
    global invincible, invincible_timer, lean_offset, current_weapon_index, current_weapon, clip_ammo, reserve_ammo
    global is_reloading, reload_timer, RELOAD_TIME, shot_cooldown, MAX_SHOT_COOLDOWN, money, shop_open, score, wave, spawn_timer
    global bullets, enemy_bullets, enemies, boss, guards, tanks, fire_zones, poison_zones
    global player_slowed, player_slow_timer, player_distorted, heal_charges, heal_active, heal_timer

    player_x, player_y = 10.5, 10.5
    player_angle = 0.0
    player_pitch = 0.0
    player_hp = max_hp
    player_height = 0.0
    vertical_vel = 0.0
    is_grounded = True
    is_sliding = False
    slide_timer = 0
    invincible = False
    invincible_timer = 0
    lean_offset = 0.0
    player_slowed = False
    player_slow_timer = 0
    player_distorted = False
    heal_charges = 100
    heal_active = False
    heal_timer = 0
    current_weapon_index = 0
    current_weapon = weapons[current_weapon_index]
    clip_ammo = current_weapon.clip_size
    reserve_ammo = 300
    is_reloading = False
    reload_timer = 0
    RELOAD_TIME = int(current_weapon.reload_time * 60)
    shot_cooldown = 0
    MAX_SHOT_COOLDOWN = current_weapon.cooldown_frames
    money = 500
    shop_open = False
    score = 0
    wave = 1
    spawn_timer = 0
    bullets.clear()
    enemy_bullets.clear()
    enemies.clear()
    guards.clear()
    tanks.clear()
    fire_zones.clear()
    poison_zones.clear()
    boss = None

# ---------- 主循环 ----------
running = True
while running:
    dt = clock.tick(60) / 1000.0

    for event in pygame.event.get():
        if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
            if game_state == STATE_MENU:
                running = False
            else:
                game_state = STATE_MENU
                pygame.mouse.set_visible(True)
                pygame.event.set_grab(False)

        if event.type == KEYDOWN:
            if game_state == STATE_MENU:
                if event.key == K_1:
                    current_mode = MODE_ENDLESS
                    reset_game()
                    for _ in range(4):
                        spawn_enemy()
                    game_state = STATE_GAME
                    pygame.mouse.set_visible(False)
                    pygame.event.set_grab(True)
                elif event.key == K_2:
                    current_mode = MODE_CLASSIC
                    reset_game()
                    for _ in range(4):
                        spawn_enemy()
                    game_state = STATE_GAME
                    pygame.mouse.set_visible(False)
                    pygame.event.set_grab(True)
                elif event.key == K_3:
                    game_state = STATE_BOSS
                    selected_boss_index = 0
                elif event.key == K_q:
                    running = False

            elif game_state == STATE_BOSS:
                if event.key == K_UP:
                    selected_boss_index = (selected_boss_index - 1) % len(boss_names)
                elif event.key == K_DOWN:
                    selected_boss_index = (selected_boss_index + 1) % len(boss_names)
                elif event.key == K_RETURN:
                    reset_game()
                    boss = Boss(boss_names[selected_boss_index], 15.0, 10.0)
                    spawn_boss_guards(boss_names[selected_boss_index])
                    game_state = STATE_GAME
                    pygame.mouse.set_visible(False)
                    pygame.event.set_grab(True)
                elif event.key == K_ESCAPE:
                    game_state = STATE_MENU

            elif game_state == STATE_GAME:
                if shop_open:
                    if event.key == K_b:
                        shop_open = False
                    elif event.key in [K_1, K_2, K_3, K_4, K_5, K_6, K_7]:
                        idx = event.key - K_1
                        if idx < len(weapons):
                            wp = weapons[idx]
                            if money >= wp.price:
                                money -= wp.price
                                current_weapon_index = idx
                                current_weapon = wp
                                clip_ammo = current_weapon.clip_size
                                RELOAD_TIME = int(current_weapon.reload_time * 60)
                                MAX_SHOT_COOLDOWN = current_weapon.cooldown_frames
                    elif event.key == K_8:
                        if money >= 100 and heal_charges < max_heal_charges:
                            money -= 100
                            heal_charges = min(max_heal_charges, heal_charges + 50)
                else:
                    if event.key == K_SPACE and is_grounded:
                        vertical_vel = JUMP_FORCE
                        is_grounded = False
                    if event.key == K_LCTRL and is_grounded and not is_sliding:
                        is_sliding = True
                        slide_timer = SLIDE_DURATION
                        player_height = -0.5
                    if event.key == K_r and not is_reloading and clip_ammo < current_weapon.clip_size and reserve_ammo > 0:
                        is_reloading = True
                        reload_timer = RELOAD_TIME
                    if event.key == K_b:
                        shop_open = True
                    if event.key == K_v:
                        heal_active = True
                        heal_timer = 0

        if event.type == KEYUP:
            if event.key == K_v:
                heal_active = False
                heal_timer = 0

    if game_state == STATE_MENU:
        screen.fill((20,20,30))
        title = big_font.render("三角洲行动", True, (255,200,0))
        screen.blit(title, (SCREEN_WIDTH//2 - title.get_width()//2, 100))
        opt1 = font.render("1. 无尽模式 (无限波次)", True, (255,255,255))
        screen.blit(opt1, (SCREEN_WIDTH//2 - 150, 250))
        opt2 = font.render("2. 经典模式 (5波通关)", True, (255,255,255))
        screen.blit(opt2, (SCREEN_WIDTH//2 - 150, 300))
        opt3 = font.render("3. Boss挑战", True, (255,255,255))
        screen.blit(opt3, (SCREEN_WIDTH//2 - 150, 350))
        opt4 = font.render("Q. 退出", True, (200,200,200))
        screen.blit(opt4, (SCREEN_WIDTH//2 - 150, 400))
        pygame.display.flip()
        continue

    if game_state == STATE_BOSS:
        screen.fill((30,20,20))
        title = big_font.render("选择 Boss", True, (255,100,100))
        screen.blit(title, (SCREEN_WIDTH//2 - title.get_width()//2, 80))
        for i, name in enumerate(boss_names):
            color = (255,255,0) if i == selected_boss_index else (200,200,200)
            text = font.render(name, True, color)
            screen.blit(text, (SCREEN_WIDTH//2 - 120, 200 + i * 50))
        hint = small_font.render("↑↓选择  Enter确认  ESC返回", True, (180,180,180))
        screen.blit(hint, (SCREEN_WIDTH//2 - 100, 500))
        pygame.display.flip()
        continue

    # ---------- 游戏进行中 ----------
    keys = pygame.key.get_pressed()
    mouse_buttons = pygame.mouse.get_pressed()

    lean_left = keys[K_q] and not shop_open
    lean_right = keys[K_e] and not shop_open
    if lean_left and not lean_right:
        lean_offset = -LEAN_AMOUNT
    elif lean_right and not lean_left:
        lean_offset = LEAN_AMOUNT
    else:
        lean_offset = 0.0

    if shot_cooldown > 0:
        shot_cooldown -= 1

    mouse_dx, mouse_dy = pygame.mouse.get_rel()
    player_angle += mouse_dx * MOUSE_SENSITIVITY
    player_pitch -= mouse_dy * MOUSE_SENSITIVITY
    player_pitch = max(-1.2, min(1.2, player_pitch))

    # 负面状态更新
    if player_slowed:
        player_slow_timer -= 1
        if player_slow_timer <= 0:
            player_slowed = False

    if not shop_open:
        base_speed = NORMAL_SPEED
        if player_slowed:
            base_speed *= 0.5
        
        if is_sliding:
            MOVE_SPEED = SLIDE_SPEED
        elif keys[K_LCTRL]:
            MOVE_SPEED = CROUCH_SPEED
            player_height = 0.5
        else:
            MOVE_SPEED = base_speed
            if not is_sliding and is_grounded:
                player_height = 0.0

        move_x = move_y = 0
        if keys[K_w]:
            move_x += math.cos(player_angle) * MOVE_SPEED
            move_y += math.sin(player_angle) * MOVE_SPEED
        if keys[K_s]:
            move_x -= math.cos(player_angle) * MOVE_SPEED
            move_y -= math.sin(player_angle) * MOVE_SPEED
        if keys[K_a]:
            move_x += math.sin(player_angle) * MOVE_SPEED
            move_y -= math.cos(player_angle) * MOVE_SPEED
        if keys[K_d]:
            move_x -= math.sin(player_angle) * MOVE_SPEED
            move_y += math.cos(player_angle) * MOVE_SPEED

        if move_x != 0 or move_y != 0:
            if can_move_to(player_x + move_x, player_y):
                player_x += move_x
            if can_move_to(player_x, player_y + move_y):
                player_y += move_y

        if not is_grounded:
            player_height += vertical_vel
            vertical_vel += GRAVITY
            if player_height <= 0.0:
                player_height = 0.0
                is_grounded = True
                vertical_vel = 0.0

        if is_sliding:
            slide_timer -= 1
            if slide_timer <= 0:
                is_sliding = False
                player_height = 0.0

        if invincible:
            invincible_timer -= 1
            if invincible_timer <= 0:
                invincible = False

        if is_reloading:
            reload_timer -= 1
            if reload_timer <= 0:
                needed = current_weapon.clip_size - clip_ammo
                take = min(needed, reserve_ammo)
                clip_ammo += take
                reserve_ammo -= take
                is_reloading = False

        # 医疗充能（按住V）
        if heal_active:
            heal_timer += 1
            if heal_timer >= 180:  # 3秒后开始生效
                if player_hp < max_hp and heal_charges >= 20:
                    player_hp = min(max_hp, player_hp + 20)
                    heal_charges -= 20
                    heal_timer = 120  # 每秒触发一次
                else:
                    heal_active = False
        else:
            heal_timer = 0

        # 射击
        if (not shop_open and not is_reloading and 
            clip_ammo > 0 and shot_cooldown <= 0 and 
            mouse_buttons[0]):
            bullets.append(Bullet(player_x, player_y, player_angle, player_pitch,
                                  damage=current_weapon.damage))
            clip_ammo -= 1
            shot_cooldown = current_weapon.cooldown_frames
            recoil_amount = random.uniform(0.5, 1.0) * current_weapon.recoil
            player_pitch += recoil_amount
            player_pitch = min(1.2, player_pitch)

        # 更新技能区域
        for fz in fire_zones[:]:
            fz.update()
            if not fz.alive:
                fire_zones.remove(fz)
            elif math.hypot(fz.x - player_x, fz.y - player_y) < fz.radius:
                if not invincible:
                    player_hp -= fz.damage
                    invincible = True
                    invincible_timer = INVINCIBLE_DURATION
        
        for pz in poison_zones[:]:
            pz.update()
            if not pz.alive:
                poison_zones.remove(pz)
            elif math.hypot(pz.x - player_x, pz.y - player_y) < pz.radius:
                player_slowed = True
                player_slow_timer = 30
                player_distorted = True
            else:
                player_distorted = False

        # 更新玩家子弹
        for b in bullets[:]:
            b.update()
            if not b.alive:
                bullets.remove(b)
                continue
            for e in enemies:
                if e.alive and math.hypot(b.x - e.x, b.y - e.y) < e.radius:
                    e.hp -= b.damage
                    b.alive = False
                    if e.hp <= 0:
                        e.alive = False
                        score += 100
                        money += KILL_REWARD
                    break
            for g in guards:
                if g.alive and math.hypot(b.x - g.x, b.y - g.y) < g.radius:
                    g.hp -= b.damage
                    b.alive = False
                    if g.hp <= 0:
                        g.alive = False
                        score += 80
                        money += 50
                    break
            for t in tanks:
                if t.alive and math.hypot(b.x - t.x, b.y - t.y) < t.radius:
                    t.hp -= b.damage
                    b.alive = False
                    if t.hp <= 0:
                        t.alive = False
                        score += 150
                        money += 100
                    break
            if boss and boss.alive and math.hypot(b.x - boss.x, b.y - boss.y) < boss.radius:
                boss.hp -= b.damage
                b.alive = False
                if boss.hp <= 0:
                    boss.alive = False
                    score += 1000
                    money += 500

        # 敌方子弹碰撞
        for eb in enemy_bullets[:]:
            eb.update()
            if not eb.alive:
                enemy_bullets.remove(eb)
                continue
            if math.hypot(eb.x - player_x, eb.y - player_y) < PLAYER_RADIUS:
                if not invincible:
                    player_hp -= eb.damage
                    invincible = True
                    invincible_timer = INVINCIBLE_DURATION
                eb.alive = False

        # 更新各单位
        if boss and boss.alive:
            boss.update(player_x, player_y)
        
        for g in guards[:]:
            if g.alive:
                g.update(player_x, player_y)
            else:
                guards.remove(g)
        
        for t in tanks[:]:
            if t.alive:
                t.update(player_x, player_y)
            else:
                tanks.remove(t)
        
        for e in enemies[:]:
            if e.alive:
                e.update(player_x, player_y)
            else:
                enemies.remove(e)

        # 波次控制
        if boss is None:
            if current_mode == MODE_ENDLESS:
                if len(enemies) == 0:
                    wave += 1
                    for _ in range(3 + wave):
                        spawn_enemy()
                elif spawn_timer <= 0 and len(enemies) < 15:
                    spawn_enemy()
                    spawn_timer = SPAWN_COOLDOWN
                else:
                    spawn_timer -= 1
            else:
                if len(enemies) == 0:
                    if wave >= 5:
                        game_state = STATE_MENU
                        pygame.mouse.set_visible(True)
                        pygame.event.set_grab(False)
                    else:
                        wave += 1
                        for _ in range(3 + wave):
                            spawn_enemy()

        if player_hp <= 0:
            game_state = STATE_MENU
            pygame.mouse.set_visible(True)
            pygame.event.set_grab(False)
    # ---------- 绘制 ----------
    screen.fill((0,0,0))
    pygame.draw.rect(screen, (30,30,40), (0,0, SCREEN_WIDTH, SCREEN_HEIGHT//2))
    pygame.draw.rect(screen, (60,60,70), (0, SCREEN_HEIGHT//2, SCREEN_WIDTH, SCREEN_HEIGHT//2))

    if lean_offset != 0:
        perp_angle = player_angle + (-math.pi/2 if lean_offset < 0 else math.pi/2)
        ray_start_x = player_x + abs(lean_offset) * math.cos(perp_angle)
        ray_start_y = player_y + abs(lean_offset) * math.sin(perp_angle)
    else:
        ray_start_x = player_x
        ray_start_y = player_y

    ray_angle = player_angle - FOV/2
    col_width = SCREEN_WIDTH / NUM_RAYS
    for i in range(NUM_RAYS):
        dist, side, _ = cast_ray(ray_start_x, ray_start_y, ray_angle, player_pitch)
        if dist < 0.1: dist = 0.1
        dist *= math.cos(ray_angle - player_angle)
        wall_height = int(SCREEN_HEIGHT / dist)
        pitch_offset = int(math.tan(player_pitch) * SCREEN_HEIGHT * 0.5)
        height_offset = int(player_height * 50) + pitch_offset
        wall_top = (SCREEN_HEIGHT - wall_height) // 2 + height_offset
        brightness = 1.0 - min(dist / 30.0, 1.0)
        if side == 0:
            color = (180 * brightness, 100 * brightness, 80 * brightness)
        else:
            color = (200 * brightness, 140 * brightness, 100 * brightness)
        pygame.draw.rect(screen, color, (i * col_width, wall_top, col_width + 1, wall_height))
        ray_angle += FOV / NUM_RAYS

    # 绘制燃烧区域（半透明特效）
    for fz in fire_zones:
        dx = fz.x - player_x
        dy = fz.y - player_y
        dist = math.hypot(dx, dy)
        if dist < 20:
            angle_to = math.atan2(dy, dx)
            angle_diff = (angle_to - player_angle + math.pi) % (2*math.pi) - math.pi
            screen_x = SCREEN_WIDTH/2 + (angle_diff / (FOV/2)) * SCREEN_WIDTH/2
            if 0 < screen_x < SCREEN_WIDTH:
                flicker = 0.8 + 0.2 * math.sin(pygame.time.get_ticks() * 0.01)
                base_alpha = int(120 * (fz.duration / fz.max_duration) * flicker)
                size = int(40 * (1 + 0.1 * math.sin(pygame.time.get_ticks() * 0.005)))
                
                fire_surf = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA)
                pygame.draw.circle(fire_surf, (180, 30, 0, base_alpha//2), 
                                   (int(screen_x), SCREEN_HEIGHT//2), size + 10)
                pygame.draw.circle(fire_surf, (255, 80, 0, base_alpha), 
                                   (int(screen_x), SCREEN_HEIGHT//2), size)
                pygame.draw.circle(fire_surf, (255, 220, 50, base_alpha*2), 
                                   (int(screen_x), SCREEN_HEIGHT//2), size//2)
                screen.blit(fire_surf, (0,0))

    # 绘制毒气区域（半透明特效）
    for pz in poison_zones:
        dx = pz.x - player_x
        dy = pz.y - player_y
        dist = math.hypot(dx, dy)
        if dist < 20:
            angle_to = math.atan2(dy, dx)
            angle_diff = (angle_to - player_angle + math.pi) % (2*math.pi) - math.pi
            screen_x = SCREEN_WIDTH/2 + (angle_diff / (FOV/2)) * SCREEN_WIDTH/2
            if 0 < screen_x < SCREEN_WIDTH:
                progress = pz.duration / pz.max_duration
                alpha = int(100 * progress)
                size = int(45 * (1 + 0.1 * math.sin(pygame.time.get_ticks() * 0.003)))
                
                poison_surf = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA)
                pygame.draw.circle(poison_surf, (50, 200, 50, alpha//2), 
                                   (int(screen_x), SCREEN_HEIGHT//2), size + 15)
                pygame.draw.circle(poison_surf, (0, 180, 0, alpha), 
                                   (int(screen_x), SCREEN_HEIGHT//2), size)
                for _ in range(8):
                    offset_x = random.randint(-size, size)
                    offset_y = random.randint(-size, size)
                    if offset_x**2 + offset_y**2 < size**2:
                        pygame.draw.circle(poison_surf, (100, 255, 100, alpha), 
                                           (int(screen_x + offset_x), SCREEN_HEIGHT//2 + offset_y), 2)
                screen.blit(poison_surf, (0,0))

    # 绘制Boss
    if boss and boss.alive:
        dx = boss.x - player_x
        dy = boss.y - player_y
        dist = math.hypot(dx, dy)
        angle_to = math.atan2(dy, dx)
        angle_diff = (angle_to - player_angle + math.pi) % (2*math.pi) - math.pi
        if abs(angle_diff) < FOV/1.5 and dist > 0.3:
            wall_dist, _, _ = cast_ray(ray_start_x, ray_start_y, angle_to, player_pitch)
            if dist < wall_dist:
                screen_x = SCREEN_WIDTH/2 + (angle_diff / (FOV/2)) * SCREEN_WIDTH/2
                size = int(300 / dist)
                pitch_offset = int(math.tan(player_pitch) * SCREEN_HEIGHT * 0.5)
                vertical_offset = int(player_height * 30) + pitch_offset
                rect = pygame.Rect(0, 0, size, size)
                rect.center = (screen_x, SCREEN_HEIGHT//2 + vertical_offset)
                if boss.name == "典狱长格赫罗斯":
                    boss.color = (80,80,120)
                elif boss.name == "雷丝":
                    boss.color = (200,80,80)
                elif boss.name == "赛伊德":
                    boss.color = (100,120,80)
                elif boss.name == "渡鸦":
                    boss.color = (120,80,160)
                elif boss.name == "德穆兰":
                    boss.color = (180,180,200)
                pygame.draw.rect(screen, boss.color, rect)
                pygame.draw.rect(screen, (255,0,0), rect, 3)
                hp_ratio = boss.hp / boss.max_hp
                bar_w = size
                pygame.draw.rect(screen, (255,0,0), (rect.x, rect.y - 10, bar_w, 8))
                pygame.draw.rect(screen, (0,255,0), (rect.x, rect.y - 10, bar_w * hp_ratio, 8))
                name_surf = small_font.render(boss.name, True, (255,255,255))
                screen.blit(name_surf, (rect.x, rect.y - 30))

    # 绘制护卫队
    for g in guards:
        if not g.alive: continue
        dx = g.x - player_x
        dy = g.y - player_y
        dist = math.hypot(dx, dy)
        angle_to = math.atan2(dy, dx)
        angle_diff = (angle_to - player_angle + math.pi) % (2*math.pi) - math.pi
        if abs(angle_diff) < FOV/1.5 and dist > 0.3:
            wall_dist, _, _ = cast_ray(ray_start_x, ray_start_y, angle_to, player_pitch)
            if dist < wall_dist:
                screen_x = SCREEN_WIDTH/2 + (angle_diff / (FOV/2)) * SCREEN_WIDTH/2
                size = int(150 / dist)
                pitch_offset = int(math.tan(player_pitch) * SCREEN_HEIGHT * 0.5)
                vertical_offset = int(player_height * 30) + pitch_offset
                rect = pygame.Rect(0, 0, size, size)
                rect.center = (screen_x, SCREEN_HEIGHT//2 + vertical_offset)
                pygame.draw.rect(screen, g.color, rect)
                hp_ratio = g.hp / g.max_hp
                bar_w = size
                pygame.draw.rect(screen, (0,200,0), (rect.x, rect.y - 10, bar_w * hp_ratio, 5))

    # 绘制战车
    for t in tanks:
        if not t.alive: continue
        dx = t.x - player_x
        dy = t.y - player_y
        dist = math.hypot(dx, dy)
        angle_to = math.atan2(dy, dx)
        angle_diff = (angle_to - player_angle + math.pi) % (2*math.pi) - math.pi
        if abs(angle_diff) < FOV/1.5 and dist > 0.3:
            wall_dist, _, _ = cast_ray(ray_start_x, ray_start_y, angle_to, player_pitch)
            if dist < wall_dist:
                screen_x = SCREEN_WIDTH/2 + (angle_diff / (FOV/2)) * SCREEN_WIDTH/2
                size = int(200 / dist)
                pitch_offset = int(math.tan(player_pitch) * SCREEN_HEIGHT * 0.5)
                vertical_offset = int(player_height * 30) + pitch_offset
                rect = pygame.Rect(0, 0, size, size)
                rect.center = (screen_x, SCREEN_HEIGHT//2 + vertical_offset)
                pygame.draw.rect(screen, t.color, rect)
                pygame.draw.rect(screen, (0,0,0), rect, 2)
                hp_ratio = t.hp / t.max_hp
                bar_w = size
                pygame.draw.rect(screen, (0,200,0), (rect.x, rect.y - 10, bar_w * hp_ratio, 5))

    # 绘制普通敌人
    for e in enemies:
        dx = e.x - player_x
        dy = e.y - player_y
        dist = math.hypot(dx, dy)
        angle_to = math.atan2(dy, dx)
        angle_diff = (angle_to - player_angle + math.pi) % (2*math.pi) - math.pi
        if abs(angle_diff) < FOV/1.5 and dist > 0.3:
            wall_dist, _, _ = cast_ray(ray_start_x, ray_start_y, angle_to, player_pitch)
            if dist < wall_dist:
                screen_x = SCREEN_WIDTH/2 + (angle_diff / (FOV/2)) * SCREEN_WIDTH/2
                size = int(200 / dist)
                pitch_offset = int(math.tan(player_pitch) * SCREEN_HEIGHT * 0.5)
                vertical_offset = int(player_height * 30) + pitch_offset
                rect = pygame.Rect(0, 0, size, size)
                rect.center = (screen_x, SCREEN_HEIGHT//2 + vertical_offset)
                pygame.draw.rect(screen, (0, 200, 0), rect)
                hp_ratio = e.hp / e.max_hp
                bar_w = size
                pygame.draw.rect(screen, (0, 200, 0), (rect.x, rect.y - 10, bar_w * hp_ratio, 5))
                eye_offset = int(size * 0.2)
                pygame.draw.circle(screen, (255, 0, 0),
                                   (int(screen_x - eye_offset), int(SCREEN_HEIGHT//2 - eye_offset + vertical_offset)),
                                   max(2, size//8))

    # 绘制玩家子弹
    for b in bullets:
        dx = b.x - player_x
        dy = b.y - player_y
        dist = math.hypot(dx, dy)
        angle_to = math.atan2(dy, dx)
        angle_diff = (angle_to - player_angle + math.pi) % (2*math.pi) - math.pi
        if abs(angle_diff) < FOV/1.5 and dist > 0.2:
            wall_dist, _, _ = cast_ray(ray_start_x, ray_start_y, angle_to, player_pitch)
            if dist < wall_dist:
                screen_x = SCREEN_WIDTH/2 + (angle_diff / (FOV/2)) * SCREEN_WIDTH/2
                size = max(4, int(30 / dist))
                pitch_offset = int(math.tan(player_pitch) * SCREEN_HEIGHT * 0.5)
                vertical_offset = int(player_height * 30) + pitch_offset
                rect = pygame.Rect(0, 0, size, size)
                rect.center = (screen_x, SCREEN_HEIGHT//2 + vertical_offset)
                pygame.draw.rect(screen, (255, 255, 0), rect)
                pygame.draw.rect(screen, (255, 200, 0), rect.inflate(-2, -2))

    # 绘制敌人子弹
    for eb in enemy_bullets:
        dx = eb.x - player_x
        dy = eb.y - player_y
        dist = math.hypot(dx, dy)
        angle_to = math.atan2(dy, dx)
        angle_diff = (angle_to - player_angle + math.pi) % (2*math.pi) - math.pi
        if abs(angle_diff) < FOV/1.5 and dist > 0.2:
            wall_dist, _, _ = cast_ray(ray_start_x, ray_start_y, angle_to, player_pitch)
            if dist < wall_dist:
                screen_x = SCREEN_WIDTH/2 + (angle_diff / (FOV/2)) * SCREEN_WIDTH/2
                size = max(4, int(30 / dist))
                pitch_offset = int(math.tan(player_pitch) * SCREEN_HEIGHT * 0.5)
                vertical_offset = int(player_height * 30) + pitch_offset
                col = (255, 100, 0) if eb.is_fire else (255, 0, 0)
                pygame.draw.circle(screen, col,
                                   (int(screen_x), int(SCREEN_HEIGHT//2 + vertical_offset)), size//2)

    # 准星
    cx, cy = SCREEN_WIDTH//2, SCREEN_HEIGHT//2
    pygame.draw.line(screen, (255,255,255), (cx-12, cy), (cx+12, cy), 2)
    pygame.draw.line(screen, (255,255,255), (cx, cy-12), (cx, cy+12), 2)

    # 探头指示
    if lean_offset != 0:
        lean_text = small_font.render("<< 左探头" if lean_offset < 0 else "右探头 >>", True, (255,255,0))
        screen.blit(lean_text, (SCREEN_WIDTH//2 - lean_text.get_width()//2, SCREEN_HEIGHT - 100))

    draw_weapon_model(screen, current_weapon, is_reloading)

    # UI
    hp_bar_width = 200
    hp_fill = int(hp_bar_width * (player_hp / max_hp))
    pygame.draw.rect(screen, (60,60,60), (10, 10, hp_bar_width, 20))
    pygame.draw.rect(screen, (0,200,0), (10, 10, hp_fill, 20))
    hp_text = font.render(f"生命: {player_hp}", True, (255,255,255))
    screen.blit(hp_text, (10, 35))
    mode_text = "无尽模式" if current_mode == MODE_ENDLESS else "经典模式"
    if boss:
        mode_text = f"Boss: {boss.name}"
    score_text = font.render(f"得分: {score}  波次: {wave}  金钱: ${money}  {mode_text}", True, (255,255,255))
    screen.blit(score_text, (10, 60))
    help_text = small_font.render("WASD移动 | 按住左键连射 | R换弹 | Q/E探头 | 空格跳跃 | Ctrl滑铲 | B商店 | V医疗", True, (200,200,200))
    screen.blit(help_text, (10, SCREEN_HEIGHT-30))

    # 医疗充能显示
    heal_text = small_font.render(f"医疗充能: {heal_charges}", True, (200, 200, 200))
    screen.blit(heal_text, (SCREEN_WIDTH - 150, SCREEN_HEIGHT - 80))
    if heal_active:
        charging_text = small_font.render("治疗中...", True, (0, 255, 0))
        screen.blit(charging_text, (SCREEN_WIDTH - 150, SCREEN_HEIGHT - 100))

    if player_slowed:
        slow_text = small_font.render("减速", True, (0, 255, 0))
        screen.blit(slow_text, (SCREEN_WIDTH - 80, 80))

    ammo_text = f"{clip_ammo} / {reserve_ammo}"
    ammo_surf = font.render(ammo_text, True, (255,255,255))
    screen.blit(ammo_surf, (SCREEN_WIDTH - 100, SCREEN_HEIGHT - 50))
    if is_reloading:
        bar_width = 100
        bar_x = SCREEN_WIDTH - 120
        bar_y = SCREEN_HEIGHT - 30
        progress = 1 - (reload_timer / RELOAD_TIME)
        pygame.draw.rect(screen, (80,80,80), (bar_x, bar_y, bar_width, 10))
        pygame.draw.rect(screen, (0,200,200), (bar_x, bar_y, bar_width * progress, 10))
    elif shot_cooldown > 0:
        cd_bar_x = SCREEN_WIDTH - 120
        cd_bar_y = SCREEN_HEIGHT - 70
        cd_progress = shot_cooldown / MAX_SHOT_COOLDOWN
        pygame.draw.rect(screen, (80,80,80), (cd_bar_x, cd_bar_y, 100, 5))
        pygame.draw.rect(screen, (200,150,0), (cd_bar_x, cd_bar_y, 100 * cd_progress, 5))

    # 小地图
    minimap_size = 180
    minimap_surf = pygame.Surface((minimap_size, minimap_size))
    minimap_surf.fill((50,50,50))
    tile_draw_size = minimap_size // MAP_SIZE
    for my in range(MAP_SIZE):
        for mx in range(MAP_SIZE):
            if MAP[my][mx] == 1:
                pygame.draw.rect(minimap_surf, (100,100,100),
                                 (mx*tile_draw_size, my*tile_draw_size, tile_draw_size, tile_draw_size))
    px = int(player_x * tile_draw_size)
    py = int(player_y * tile_draw_size)
    pygame.draw.circle(minimap_surf, (0,200,0), (px, py), 3)
    line_end_x = px + int(math.cos(player_angle) * 12)
    line_end_y = py + int(math.sin(player_angle) * 12)
    pygame.draw.line(minimap_surf, (0,255,0), (px, py), (line_end_x, line_end_y), 1)
    for e in enemies:
        ex = int(e.x * tile_draw_size)
        ey = int(e.y * tile_draw_size)
        pygame.draw.circle(minimap_surf, (255,0,0), (ex, ey), 2)
    for g in guards:
        if g.alive:
            gx = int(g.x * tile_draw_size)
            gy = int(g.y * tile_draw_size)
            pygame.draw.circle(minimap_surf, (200,100,0), (gx, gy), 2)
    for t in tanks:
        if t.alive:
            tx = int(t.x * tile_draw_size)
            ty = int(t.y * tile_draw_size)
            pygame.draw.circle(minimap_surf, (100,100,200), (tx, ty), 3)
    if boss and boss.alive:
        bx = int(boss.x * tile_draw_size)
        by = int(boss.y * tile_draw_size)
        pygame.draw.circle(minimap_surf, (255,0,0), (bx, by), 5)
    screen.blit(minimap_surf, (SCREEN_WIDTH - minimap_size - 10, 10))

    if shop_open:
        draw_shop(screen, money, weapons, current_weapon_index)

    if invincible and (invincible_timer // 5) % 2 == 0:
        pygame.draw.rect(screen, (255,255,255), (0,0, SCREEN_WIDTH, SCREEN_HEIGHT), 5)

    pygame.display.flip()

pygame.quit()
sys.exit()