#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 本文件由 360问AI 生成。
"""
终极硬核荒野求生 2D - PyGame版
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⏱ 超慢时间 | 🌗昼夜交替 | 🌸四季轮转
🦴6部位肢体 | 🩸流血骨折感染 | 🧠精神值 | 🥶冻伤
⚔️回合战斗+暴击 | 🐺夜袭 | 🍖食物腐烂 | 🤒疾病
"""

import pygame
import random
import math
import time
import sys

# ─── 常量 ──────────────────────────────────────────
WIDTH, HEIGHT = 1200, 800
TILE_SIZE = 32
MAP_W, MAP_H = 80, 60
FPS = 60

# 时间系统: 2秒现实 = 1游戏分钟 → 1天(24h) = 48分钟现实
SECONDS_PER_GAME_MINUTE = 2.0
MINUTES_PER_HOUR = 60
HOURS_PER_DAY = 24
DAYS_PER_SEASON = 10

# 颜色
C_BLACK = (0, 0, 0)
C_WHITE = (255, 255, 255)
C_GRAY = (128, 128, 128)
C_DARK = (30, 30, 30)
C_RED = (220, 50, 50)
C_GREEN = (50, 180, 50)
C_DARKGREEN = (30, 80, 30)
C_BLUE = (50, 100, 200)
C_LIGHTBLUE = (135, 206, 250)
C_BROWN = (139, 90, 43)
C_DARKBROWN = (80, 50, 20)
C_YELLOW = (230, 200, 50)
C_ORANGE = (230, 150, 50)
C_PURPLE = (150, 50, 180)
C_SKY_DAY = (100, 170, 240)
C_SKY_DUSK = (200, 120, 80)
C_SKY_NIGHT = (10, 10, 40)
C_WATER = (40, 80, 160)
C_DEEPWATER = (20, 40, 100)
C_SAND = (210, 190, 140)
C_SNOW_GROUND = (230, 235, 245)
C_ROCK = (130, 130, 130)
C_DARKROCK = (80, 80, 80)
C_GRASS = (70, 150, 50)
C_DARKGRASS = (50, 120, 35)
C_FOREST = (30, 90, 25)
C_DIRT = (120, 90, 50)
C_TREE_TRUNK = (100, 65, 30)
C_LEAF = (40, 130, 35)
C_AUTUMN_LEAF = (200, 130, 30)
C_DEAD_LEAF = (100, 80, 50)
C_FIRE = (255, 140, 30)
C_SHELTER = (110, 80, 45)

# 地形类型
TILE_GRASS = 0
TILE_FOREST = 1
TILE_WATER = 2
TILE_DEEPWATER = 3
TILE_ROCK = 4
TILE_SAND = 5
TILE_SNOW = 6
TILE_DIRT = 7
TILE_CAVE = 8
TILE_BUSH = 9
TILE_DARKGRASS = 10

TILE_NAMES = {
    TILE_GRASS: "草地", TILE_FOREST: "森林", TILE_WATER: "浅水",
    TILE_DEEPWATER: "深水", TILE_ROCK: "岩石", TILE_SAND: "沙地",
    TILE_SNOW: "雪地", TILE_DIRT: "泥地", TILE_CAVE: "山洞",
    TILE_BUSH: "灌木丛", TILE_DARKGRASS: "深草丛"
}

TILE_WALKABLE = {
    TILE_GRASS: True, TILE_FOREST: True, TILE_WATER: True,
    TILE_DEEPWATER: False, TILE_ROCK: False, TILE_SAND: True,
    TILE_SNOW: True, TILE_DIRT: True, TILE_CAVE: True,
    TILE_BUSH: True, TILE_DARKGRASS: True
}

# 季节
SEASON_SPRING = 0
SEASON_SUMMER = 1
SEASON_AUTUMN = 2
SEASON_WINTER = 3
SEASON_NAMES = ["春", "夏", "秋", "冬"]

# 天气
WEATHER_CLEAR = 0
WEATHER_CLOUDY = 1
WEATHER_RAIN = 2
WEATHER_STORM = 3
WEATHER_WIND = 4
WEATHER_BLIZZARD = 5
WEATHER_NAMES = ["晴", "多云", "小雨", "暴雨", "大风", "暴雪"]

# 身体部位
BP_HEAD = "头部"
BP_TORSO = "躯干"
BP_LARM = "左臂"
BP_RARM = "右臂"
BP_LLEG = "左腿"
BP_RLEG = "右腿"
BODY_PARTS = [BP_HEAD, BP_TORSO, BP_LARM, BP_RARM, BP_LLEG, BP_RLEG]
BP_MAX_HP = {BP_HEAD: 80, BP_TORSO: 180, BP_LARM: 60, BP_RARM: 60, BP_LLEG: 60, BP_RLEG: 60}

# 物品定义
ITEMS = {
    "wood": ("木材", C_BROWN, "material"),
    "stone": ("石头", C_ROCK, "material"),
    "fiber": ("纤维", (100, 140, 60), "material"),
    "herb": ("草药", (80, 180, 80), "material"),
    "berry": ("浆果", (180, 40, 60), "food"),
    "mushroom": ("蘑菇", (180, 140, 80), "food"),
    "raw_meat": ("生肉", (200, 80, 80), "food"),
    "cooked_meat": ("烤肉", (160, 100, 60), "food"),
    "raw_fish": ("生鱼", (100, 140, 180), "food"),
    "cooked_fish": ("烤鱼", (180, 150, 100), "food"),
    "stew": ("炖汤", (170, 120, 60), "food"),
    "tea": ("草药茶", (120, 180, 100), "food"),
    "dirty_water": ("脏水", (100, 130, 90), "food"),
    "clean_water": ("净水", (80, 180, 220), "food"),
    "bandage": ("绷带", (220, 210, 200), "medical"),
    "splint": ("夹板", (160, 130, 80), "medical"),
    "poultice": ("药膏", (80, 160, 80), "medical"),
    "antibiotic": ("抗生素", (60, 200, 120), "medical"),
    "stone_axe": ("石斧", (150, 140, 120), "weapon"),
    "stone_spear": ("石矛", (160, 150, 130), "weapon"),
    "bone_knife": ("骨刃", (200, 190, 170), "weapon"),
    "wood_club": ("木棍", (140, 100, 50), "weapon"),
    "torch": ("火把", (220, 160, 40), "tool"),
    "trap": ("陷阱", (130, 110, 70), "tool"),
    "fur": ("兽皮", (160, 130, 90), "material"),
    "bone": ("骨头", (220, 210, 190), "material"),
    "rope": ("绳索", (150, 130, 70), "material"),
    "leather_armor": ("皮甲", (140, 100, 60), "armor"),
    "iron_armor": ("铁甲", (160, 165, 170), "armor"),
    "water_filter": ("净水器", (100, 160, 180), "tool"),
}

# 食物数据: (饱食, 水分, HP恢复, 精神, 可否生吃, 腐烂时间h)
FOOD_DATA = {
    "berry": (10, 3, 0, 1, True, 72),
    "mushroom": (6, 0, 0, 0, True, 48),
    "raw_meat": (12, 0, -5, -2, True, 48),
    "cooked_meat": (40, 0, 10, 5, False, 120),
    "raw_fish": (10, 5, -3, -1, True, 36),
    "cooked_fish": (35, 5, 8, 4, False, 96),
    "stew": (55, 20, 25, 12, False, 72),
    "tea": (0, 15, 8, 10, False, 48),
    "dirty_water": (0, 20, -8, -3, True, 99999),
    "clean_water": (0, 35, 0, 2, True, 99999),
}

# 武器数据: (攻击力, 速度)
WEAPON_DATA = {
    None: ("拳头", 5, 1.0),
    "wood_club": ("木棍", 12, 0.9),
    "stone_axe": ("石斧", 18, 0.8),
    "stone_spear": ("石矛", 22, 0.85),
    "bone_knife": ("骨刃", 30, 1.0),
}

# 护甲数据: (防御力)
ARMOR_DATA = {
    None: 0,
    "leather_armor": 8,
    "iron_armor": 20,
}

# 配方
RECIPES = [
    ("wood_club", {"wood": 4}, "武器", "简易武器 ATK+12"),
    ("stone_axe", {"wood": 3, "stone": 3, "fiber": 2}, "武器", "伐木利器 ATK+18"),
    ("stone_spear", {"wood": 5, "stone": 3, "fiber": 3}, "武器", "狩猎利器 ATK+22"),
    ("bone_knife", {"bone": 4, "fiber": 3, "wood": 2}, "武器", "锋利无比 ATK+30"),
    ("torch", {"wood": 2, "fiber": 2}, "工具", "夜间照明 驱赶野兽"),
    ("trap", {"wood": 6, "fiber": 4, "rope": 1}, "工具", "放置后自动捕猎"),
    ("bandage", {"fiber": 4}, "医疗", "止血"),
    ("splint", {"wood": 3, "fiber": 2}, "医疗", "固定骨折"),
    ("poultice", {"herb": 3, "fiber": 1}, "医疗", "抗感染+回血"),
    ("antibiotic", {"herb": 5, "clean_water": 2, "fiber": 1}, "医疗", "根治感染/疾病"),
    ("rope", {"fiber": 6}, "材料", "合成高级物品"),
    ("leather_armor", {"fur": 5, "fiber": 4}, "护甲", "DEF+8 保暖"),
    ("iron_armor", {"stone": 10, "fur": 3, "fiber": 4, "bone": 2}, "护甲", "DEF+20 重型"),
    ("water_filter", {"wood": 4, "stone": 3, "fiber": 4}, "工具", "脏水→净水"),
    ("stew", {"cooked_meat": 1, "clean_water": 1, "herb": 1}, "食物", "饱食+55 水分+20 HP+25"),
    ("tea", {"herb": 3, "clean_water": 1}, "食物", "恢复精神 治疗感冒"),
]

# 野兽定义
BEASTS = {
    "rabbit": {"name": "野兔", "hp": 25, "atk": 4, "def": 0, "spd": 5, "xp": 8,
               "color": (180, 150, 120), "size": 8, "aggro": False,
               "drops": [("raw_meat", 1, 0.8), ("fur", 1, 0.3)]},
    "snake": {"name": "毒蛇", "hp": 35, "atk": 18, "def": 1, "spd": 4, "xp": 15,
              "color": (80, 150, 60), "size": 10, "aggro": True,
              "drops": [("raw_meat", 1, 0.6), ("herb", 1, 0.4)], "poison": 0.5},
    "wolf": {"name": "灰狼", "hp": 100, "atk": 24, "def": 5, "spd": 4, "xp": 40,
             "color": (130, 130, 140), "size": 14, "aggro": True,
             "drops": [("raw_meat", 2, 0.9), ("fur", 2, 0.7), ("bone", 1, 0.5)]},
    "boar": {"name": "野猪", "hp": 130, "atk": 20, "def": 8, "spd": 3, "xp": 35,
             "color": (130, 80, 50), "size": 16, "aggro": True,
             "drops": [("raw_meat", 3, 0.9), ("fur", 2, 0.8), ("bone", 2, 0.6)]},
    "bear": {"name": "棕熊", "hp": 250, "atk": 40, "def": 14, "spd": 2, "xp": 80,
             "color": (110, 75, 40), "size": 22, "aggro": True,
             "drops": [("raw_meat", 5, 1.0), ("fur", 3, 0.9), ("bone", 3, 0.8)]},
    "leopard": {"name": "山豹", "hp": 150, "atk": 35, "def": 6, "spd": 5, "xp": 60,
                "color": (210, 180, 80), "size": 16, "aggro": True,
                "drops": [("raw_meat", 3, 0.9), ("fur", 3, 0.8), ("bone", 2, 0.6)]},
    "wolfpack": {"name": "狼群", "hp": 200, "atk": 32, "def": 8, "spd": 4, "xp": 70,
                 "color": (100, 100, 110), "size": 20, "aggro": True,
                 "drops": [("raw_meat", 4, 1.0), ("fur", 3, 0.8), ("bone", 2, 0.7)]},
}

# ─── 身体部位类 ──────────────────────────────────
class BodyPart:
    def __init__(self, name, max_hp):
        self.name = name
        self.hp = max_hp
        self.max_hp = max_hp
        self.bleeding = False
        self.bleed_timer = 0
        self.fractured = False
        self.infected = False
        self.frostbite = False
        self.crippled = False

    @property
    def ratio(self):
        return max(0, self.hp / self.max_hp) if self.max_hp > 0 else 0

    def take_damage(self, dmg):
        if self.crippled:
            return 0
        actual = max(1, dmg)
        self.hp = max(0, self.hp - actual)
        if self.hp <= 0:
            self.crippled = True
            self.bleeding = True
        elif random.random() < 0.25:
            self.bleeding = True
        return actual

    def heal(self, amount):
        if self.crippled:
            return
        self.hp = min(self.max_hp, self.hp + amount)

    def stop_bleed(self):
        self.bleeding = False
        self.bleed_timer = 0

    def fix_fracture(self):
        self.fractured = False

    def cure_infection(self):
        self.infected = False

    def get_status(self):
        parts = []
        if self.crippled: parts.append("💀残废")
        if self.bleeding: parts.append("🩸流血")
        if self.fractured: parts.append("🦴骨折")
        if self.infected: parts.append("🦠感染")
        if self.frostbite: parts.append("🥶冻伤")
        return " ".join(parts) if parts else "正常"


# ─── 玩家类 ──────────────────────────────────────
class Player:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.px = x * TILE_SIZE + TILE_SIZE // 2
        self.py = y * TILE_SIZE + TILE_SIZE // 2

        # 生存属性
        self.hp = 100
        self.max_hp = 100
        self.hunger = 100.0
        self.thirst = 100.0
        self.stamina = 100.0
        self.mental = 100.0
        self.body_temp = 36.5
        self.energy = 100.0

        # 肢体
        self.body = {}
        for bp in BODY_PARTS:
            self.body[bp] = BodyPart(bp, BP_MAX_HP[bp])

        # 状态
        self.poisoned = 0.0  # 中毒剩余分钟
        self.sick = 0.0  # 生病剩余分钟
        self.disease_name = ""

        # 装备
        self.weapon = None
        self.armor = None

        # 背包
        self.inventory = {}
        self.inventory["wood"] = 3
        self.inventory["fiber"] = 3

        # 食物腐烂追踪
        self.food_timers = {}  # item -> expire game hour

        # 等级
        self.level = 1
        self.xp = 0
        self.xp_next = 50

        # 统计
        self.kills = 0
        self.days = 0
        self.steps = 0

    def get_total_hp(self):
        total = 0
        for bp in BODY_PARTS:
            total += max(0, self.body[bp].hp)
        return total

    def is_alive(self):
        return self.body[BP_TORSO].hp > 0

    def get_attack(self):
        base = WEAPON_DATA[self.weapon][1] if self.weapon in WEAPON_DATA else 5
        base += self.level * 2
        # 手臂骨折/残废减攻
        if self.body[BP_RARM].fractured:
            base *= 0.5
        if self.body[BP_RARM].crippled:
            base *= 0.3
        if self.body[BP_LARM].fractured:
            base *= 0.8
        if self.body[BP_LARM].crippled:
            base *= 0.6
        return max(1, int(base))

    def get_defense(self):
        base = ARMOR_DATA.get(self.armor, 0)
        if self.body[BP_TORSO].fractured:
            base *= 0.5
        return int(base)

    def get_speed(self):
        spd = 3.0
        if self.body[BP_LLEG].fractured:
            spd *= 0.4
        if self.body[BP_LLEG].crippled:
            spd *= 0.3
        if self.body[BP_RLEG].fractured:
            spd *= 0.4
        if self.body[BP_RLEG].crippled:
            spd *= 0.3
        if self.stamina < 20:
            spd *= 0.5
        if self.energy < 15:
            spd *= 0.6
        return max(0.5, spd)

    def take_damage_to_part(self, dmg, part=None):
        if part is None:
            weights = [8, 30, 15, 15, 16, 16]  # head, torso, larm, rarm, lleg, rleg
            part = random.choices(BODY_PARTS, weights=weights, k=1)[0]
        bp = self.body[part]
        if bp.crippled:
            part = BP_TORSO
            bp = self.body[part]
        actual = bp.take_damage(dmg)
        # 概率骨折
        if actual > 15 and random.random() < 0.20:
            bp.fractured = True
        return part, actual

    def add_item(self, item, count=1):
        self.inventory[item] = self.inventory.get(item, 0) + count

    def remove_item(self, item, count=1):
        if item in self.inventory:
            self.inventory[item] -= count
            if self.inventory[item] <= 0:
                del self.inventory[item]

    def has_item(self, item, count=1):
        return self.inventory.get(item, 0) >= count

    def add_xp(self, amount):
        self.xp += amount
        while self.xp >= self.xp_next:
            self.xp -= self.xp_next
            self.level += 1
            self.xp_next = int(self.xp_next * 1.6)
            self.max_hp += 10
            # 升级小幅恢复
            for bp in BODY_PARTS:
                self.body[bp].heal(15)
            return True
        return False


# ─── 野兽实体 ────────────────────────────────────
class Beast:
    def __init__(self, btype, x, y, night_mult=1.0):
        data = BEASTS[btype]
        self.btype = btype
        self.name = data["name"]
        self.hp = int(data["hp"] * night_mult)
        self.max_hp = self.hp
        self.atk = int(data["atk"] * night_mult)
        self.defense = data["def"]
        self.spd = data["spd"]
        self.xp = data["xp"]
        self.color = data["color"]
        self.size = data["size"]
        self.aggro = data["aggro"]
        self.drops = data["drops"]
        self.poison_chance = data.get("poison", 0)
        self.x = x
        self.y = y
        self.px = x * TILE_SIZE + TILE_SIZE // 2
        self.py = y * TILE_SIZE + TILE_SIZE // 2
        self.alive = True
        self.move_timer = 0
        self.target_x = x
        self.target_y = y

    def ai_move(self, game_map, player, is_night):
        self.move_timer += 1
        if self.move_timer < (6 - self.spd) * 4:
            return
        self.move_timer = 0

        dx = player.x - self.x
        dy = player.y - self.y
        dist = abs(dx) + abs(dy)

        if self.aggro and dist < (8 if is_night else 5):
            # 追击玩家
            if abs(dx) > abs(dy):
                self.x += 1 if dx > 0 else -1
            else:
                self.y += 1 if dy > 0 else -1
        else:
            # 随机巡逻
            if random.random() < 0.3:
                self.x += random.choice([-1, 0, 1])
                self.y += random.choice([-1, 0, 1])

        self.x = max(0, min(MAP_W - 1, self.x))
        self.y = max(0, min(MAP_H - 1, self.y))
        self.px = self.x * TILE_SIZE + TILE_SIZE // 2
        self.py = self.y * TILE_SIZE + TILE_SIZE // 2


# ─── 陷阱 ────────────────────────────────────────
class Trap:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.active = True
        self.cooldown = 0
        self.caught = None

    def update(self):
        if not self.active:
            self.cooldown -= 1
            if self.cooldown <= 0:
                self.active = True
                self.caught = None


# ─── 游戏主类 ────────────────────────────────────
class Game:
    def __init__(self):
        pygame.init()
        self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
        pygame.display.set_caption("终极硬核荒野求生 2D")
        self.clock = pygame.time.Clock()
        self.font_sm = pygame.font.SysFont("simhei", 14)
        self.font_md = pygame.font.SysFont("simhei", 18)
        self.font_lg = pygame.font.SysFont("simhei", 24)
        self.font_xl = pygame.font.SysFont("simhei", 36)
        self.font_title = pygame.font.SysFont("simhei", 48)

        self.running = True
        self.game_map = self._generate_map()
        self.explored = [[False]*MAP_W for _ in range(MAP_H)]

        # 找到出生点
        sx, sy = MAP_W // 2, MAP_H // 2
        for r in range(20):
            for dy in range(-r, r + 1):
                for dx in range(-r, r + 1):
                    nx, ny = sx + dx, sy + dy
                    if 0 <= nx < MAP_W and 0 <= ny < MAP_H:
                        if TILE_WALKABLE.get(self.game_map[ny][nx], False):
                            sx, sy = nx, ny
                            r = 999
                            break
                if r >= 999:
                    break
            if r >= 999:
                break

        self.player = Player(sx, sy)
        self.beasts = []
        self.traps = []
        self.particles = []

        # 时间
        self.game_minute = 360  # 6:00开始
        self.total_minutes = 0
        self.real_time_accum = 0.0

        # 天气
        self.weather = WEATHER_CLEAR
        self.weather_timer = 0
        self.weather_particles = []

        # 相机
        self.cam_x = 0
        self.cam_y = 0

        # 消息
        self.messages = []
        self.msg_time = 0

        # 状态
        self.state = "world"  # world, inventory, craft, combat, build, eat, heal, status, help, gameover
        self.combat_beast = None
        self.combat_log = []
        self.combat_player_turn = True
        self.combat_fleeing = False

        # UI
        self.scroll = 0
        self.selected_idx = 0
        self.interact_hint = ""

        # 篝火
        self.campfires = {}  # (x,y) -> fuel minutes remaining

        # 庇护所
        self.shelters = set()

        # 围墙
        self.walls = set()

        # 初始化
        self._spawn_beasts(20)
        self._reveal_area(sx, sy, 6)
        self.log("你醒来发现自己身处荒野之中...", C_YELLOW)
        self.log("WASD移动 | E互动 | I背包 | C制作 | B建造", C_WHITE)
        self.log("F进食 | H治疗 | M状态 | F1帮助", C_WHITE)

    def _generate_map(self):
        m = [[TILE_GRASS]*MAP_W for _ in range(MAP_H)]

        # 河流
        for _ in range(2):
            rx, ry = random.randint(5, MAP_W - 5), 0
            for y in range(MAP_H):
                for dx in range(-1, 2):
                    nx = rx + dx
                    if 0 <= nx < MAP_W:
                        m[y][nx] = TILE_WATER
                m[y][rx] = TILE_DEEPWATER
                rx += random.choice([-1, 0, 0, 1])
                rx = max(2, min(MAP_W - 3, rx))

        # 湖泊
        for _ in range(3):
            cx, cy = random.randint(10, MAP_W - 10), random.randint(10, MAP_H - 10)
            r = random.randint(3, 6)
            for dy in range(-r, r + 1):
                for dx in range(-r, r + 1):
                    if dx*dx + dy*dy <= r*r:
                        nx, ny = cx + dx, cy + dy
                        if 0 <= nx < MAP_W and 0 <= ny < MAP_H:
                            if dx*dx + dy*dy <= (r - 1)**2:
                                m[ny][nx] = TILE_DEEPWATER
                            else:
                                m[ny][nx] = TILE_WATER

        # 森林
        for _ in range(15):
            cx, cy = random.randint(5, MAP_W - 5), random.randint(5, MAP_H - 5)
            r = random.randint(4, 10)
            for dy in range(-r, r + 1):
                for dx in range(-r, r + 1):
                    if dx*dx + dy*dy <= r*r:
                        nx, ny = cx + dx, cy + dy
                        if 0 <= nx < MAP_W and 0 <= ny < MAP_H:
                            if m[ny][nx] == TILE_GRASS:
                                if random.random() < 0.7:
                                    m[ny][nx] = TILE_FOREST
                                else:
                                    m[ny][nx] = TILE_BUSH

        # 岩石区
        for _ in range(8):
            cx, cy = random.randint(5, MAP_W - 5), random.randint(5, MAP_H - 5)
            r = random.randint(2, 5)
            for dy in range(-r, r + 1):
                for dx in range(-r, r + 1):
                    if dx*dx + dy*dy <= r*r:
                        nx, ny = cx + dx, cy + dy
                        if 0 <= nx < MAP_W and 0 <= ny < MAP_H:
                            if m[ny][nx] in (TILE_GRASS, TILE_DIRT):
                                m[ny][nx] = TILE_ROCK

        # 散布
        for y in range(MAP_H):
            for x in range(MAP_W):
                if m[y][x] == TILE_GRASS:
                    r = random.random()
                    if r < 0.05:
                        m[y][x] = TILE_FOREST
                    elif r < 0.08:
                        m[y][x] = TILE_BUSH
                    elif r < 0.12:
                        m[y][x] = TILE_DARKGRASS
                    elif r < 0.14:
                        m[y][x] = TILE_ROCK

        # 山洞
        for _ in range(4):
            cx, cy = random.randint(5, MAP_W - 5), random.randint(5, MAP_H - 5)
            if m[cy][cx] in (TILE_ROCK, TILE_FOREST):
                m[cy][cx] = TILE_CAVE
                for dy in range(-1, 2):
                    for dx in range(-1, 2):
                        nx, ny = cx + dx, cy + dy
                        if 0 <= nx < MAP_W and 0 <= ny < MAP_H:
                            if m[ny][nx] == TILE_GRASS:
                                m[ny][nx] = TILE_DIRT

        # 沙地围绕水域
        for y in range(MAP_H):
            for x in range(MAP_W):
                if m[y][x] in (TILE_WATER, TILE_DEEPWATER):
                    for dy in range(-1, 2):
                        for dx in range(-1, 2):
                            nx, ny = x + dx, y + dy
                            if 0 <= nx < MAP_W and 0 <= ny < MAP_H:
                                if m[ny][nx] == TILE_GRASS and random.random() < 0.4:
                                    m[ny][nx] = TILE_SAND

        # 清理出生点
        cx, cy = MAP_W // 2, MAP_H // 2
        for dy in range(-3, 4):
            for dx in range(-3, 4):
                nx, ny = cx + dx, cy + dy
                if 0 <= nx < MAP_W and 0 <= ny < MAP_H:
                    m[ny][nx] = TILE_GRASS

        return m

    def _reveal_area(self, cx, cy, radius):
        for dy in range(-radius, radius + 1):
            for dx in range(-radius, radius + 1):
                nx, ny = cx + dx, cy + dy
                if 0 <= nx < MAP_W and 0 <= ny < MAP_H:
                    if dx*dx + dy*dy <= radius*radius:
                        self.explored[ny][nx] = True

    def _spawn_beasts(self, count):
        for _ in range(count):
            for _attempt in range(50):
                bx = random.randint(2, MAP_W - 3)
                by = random.randint(2, MAP_H - 3)
                if self.game_map[by][bx] in (TILE_GRASS, TILE_FOREST, TILE_BUSH, TILE_DARKGRASS):
                    dist = abs(bx - self.player.x) + abs(by - self.player.y)
                    if dist > 10:
                        weights = [30, 15, 20, 15, 5, 8, 7]
                        btype = random.choices(list(BEASTS.keys()), weights=weights, k=1)[0]
                        self.beasts.append(Beast(btype, bx, by))
                        break

    def log(self, msg, color=C_WHITE):
        self.messages.append((msg, color, time.time()))
        if len(self.messages) > 60:
            self.messages.pop(0)

    # ─── 时间系统 ────────────────────────────────
    @property
    def game_hour(self):
        return (self.game_minute // MINUTES_PER_HOUR) % HOURS_PER_DAY

    @property
    def game_day(self):
        return self.game_minute // (MINUTES_PER_HOUR * HOURS_PER_DAY) + 1

    @property
    def season(self):
        return ((self.game_day - 1) // DAYS_PER_SEASON) % 4

    @property
    def is_night(self):
        h = self.game_hour
        return h < 6 or h >= 20

    @property
    def is_dusk(self):
        return 18 <= self.game_hour < 20

    @property
    def is_dawn(self):
        return 5 <= self.game_hour < 7

    def get_daylight(self):
        h = self.game_hour
        if 7 <= h < 18:
            return 1.0
        elif h == 6 or h == 18:
            return 0.7
        elif h == 5 or h == 19:
            return 0.4
        elif h == 20 or h == 4:
            return 0.2
        else:
            return 0.08

    def update_time(self, dt):
        self.real_time_accum += dt
        while self.real_time_accum >= SECONDS_PER_GAME_MINUTE:
            self.real_time_accum -= SECONDS_PER_GAME_MINUTE
            self.game_minute += 1
            self.total_minutes += 1
            self._on_game_minute()

    def _on_game_minute(self):
        p = self.player
        minute = self.game_minute % MINUTES_PER_HOUR

        # 每10分钟
        if minute % 10 == 0:
            # 饱食度
            p.hunger = max(0, p.hunger - 0.5)
            # 水分(更快)
            thirst_decay = 0.7
            if self.season == SEASON_SUMMER:
                thirst_decay += 0.3
            if self.weather in (WEATHER_STORM, WEATHER_BLIZZARD):
                thirst_decay += 0.2
            p.thirst = max(0, p.thirst - thirst_decay)

        # 每30分钟
        if minute % 30 == 0:
            # 体力恢复
            if self.state == "world":
                p.stamina = min(100, p.stamina + 3)
            # 精力消耗
            p.energy = max(0, p.energy - 1.5)

            # 精神值
            if self.is_night:
                near_fire = self._near_campfire()
                in_shelter = self._in_shelter()
                if near_fire and in_shelter:
                    p.mental = min(100, p.mental + 2)
                elif near_fire:
                    p.mental = max(0, p.mental - 1)
                else:
                    p.mental = max(0, p.mental - 3)
            else:
                p.mental = min(100, p.mental + 0.5)

        # 每60分钟(1小时)
        if minute == 0:
            hour = self.game_hour
            # 体温
            self._update_temperature()
            # 流血
            for bp in BODY_PARTS:
                body = p.body[bp]
                if body.bleeding:
                    body.hp = max(0, body.hp - 2)
                    body.bleed_timer += 1
                    if body.bleed_timer > 10 and random.random() < 0.15:
                        body.infected = True
                    if body.hp <= 0:
                        body.crippled = True
                if body.infected:
                    body.hp = max(0, body.hp - 1)
                    if body.hp <= 0:
                        body.crippled = True
                if body.frostbite:
                    body.hp = max(0, body.hp - 1)

            # 中毒
            if p.poisoned > 0:
                p.poisoned -= 1
                p.hp = max(0, p.hp - 2)
                p.hunger = max(0, p.hunger - 1)
                if p.poisoned <= 0:
                    self.log("毒素已消散", C_GREEN)

            # 疾病
            if p.sick > 0:
                p.sick -= 1
                p.hp = max(0, p.hp - 1)
                p.mental = max(0, p.mental - 1)
                if p.sick <= 0:
                    self.log("疾病已痊愈", C_GREEN)
                    p.disease_name = ""

            # 饥渴伤害
            if p.hunger <= 0:
                p.body[BP_TORSO].take_damage(3)
                self.log("饥饿正在侵蚀你的身体!", C_RED)
            if p.thirst <= 0:
                p.body[BP_TORSO].take_damage(5)
                self.log("严重脱水! 你需要水!", C_RED)

            # 庇护所休息回血
            if self._in_shelter():
                for bp in BODY_PARTS:
                    p.body[bp].heal(2)
                p.stamina = min(100, p.stamina + 5)

            # 食物腐烂
            self._check_food_rot()

            # 陷阱
            for trap in self.traps:
                trap.update()
                if trap.active and trap.caught is None:
                    # 检查附近野兽
                    for beast in self.beasts:
                        if beast.alive and abs(beast.x - trap.x) <= 1 and abs(beast.y - trap.y) <= 1:
                            if beast.btype in ("rabbit", "snake"):
                                beast.alive = False
                                trap.active = False
                                trap.cooldown = 600
                                for item, cnt, _ in beast.drops:
                                    if random.random() < 0.8:
                                        trap.caught = (item, cnt)
                                        self.log(f"陷阱捕获了 {ITEMS[item][0]}x{cnt}!", C_GREEN)
                                break

            # 补充野兽
            alive = sum(1 for b in self.beasts if b.alive)
            if alive < 12:
                self._spawn_beasts(3)

            # 天气变化
            self.weather_timer -= 1
            if self.weather_timer <= 0:
                self._change_weather()

    def _update_temperature(self):
        p = self.player
        target = 36.5
        if self.season == SEASON_WINTER:
            target -= 3.0
        elif self.season == SEASON_SUMMER:
            target += 1.5
        if self.weather == WEATHER_BLIZZARD:
            target -= 5.0
        if self.weather in (WEATHER_RAIN, WEATHER_STORM):
            target -= 2.0
        if self._in_shelter():
            target += 2.0
        if self._near_campfire():
            target += 3.0
        if p.armor == "leather_armor":
            target += 1.0
        if p.armor == "iron_armor":
            target += 0.5

        diff = target - p.body_temp
        p.body_temp += diff * 0.08
        p.body_temp = max(30.0, min(42.0, p.body_temp))

        # 体温伤害
        if p.body_temp < 34.0:
            for bp in [BP_LARM, BP_RARM, BP_LLEG, BP_RLEG]:
                if random.random() < 0.2:
                    p.body[bp].frostbite = True
                    p.body[bp].take_damage(3)
            self.log(f"体温过低 {p.body_temp:.1f}°C 冻伤风险!", (100, 180, 255))
        elif p.body_temp > 39.5:
            p.body[BP_TORSO].take_damage(4)
            p.thirst = max(0, p.thirst - 3)
            self.log(f"中暑! {p.body_temp:.1f}°C!", C_RED)

    def _change_weather(self):
        season_w = [
            {WEATHER_CLEAR: 35, WEATHER_CLOUDY: 25, WEATHER_RAIN: 25, WEATHER_STORM: 8, WEATHER_WIND: 7},
            {WEATHER_CLEAR: 40, WEATHER_CLOUDY: 20, WEATHER_RAIN: 15, WEATHER_STORM: 18, WEATHER_WIND: 7},
            {WEATHER_CLEAR: 20, WEATHER_CLOUDY: 25, WEATHER_RAIN: 20, WEATHER_STORM: 10, WEATHER_WIND: 25},
            {WEATHER_CLEAR: 15, WEATHER_CLOUDY: 20, WEATHER_WIND: 20, WEATHER_BLIZZARD: 30, WEATHER_RAIN: 15},
        ]
        w = season_w[self.season]
        self.weather = random.choices(list(w.keys()), weights=list(w.values()), k=1)[0]
        self.weather_timer = random.randint(120, 480)
        self.log(f"天气: {WEATHER_NAMES[self.weather]}", C_LIGHTBLUE)
        self._update_weather_particles()

    def _update_weather_particles(self):
        self.weather_particles = []
        count = 0
        if self.weather == WEATHER_RAIN:
            count = 80
        elif self.weather == WEATHER_STORM:
            count = 150
        elif self.weather == WEATHER_BLIZZARD:
            count = 100
        elif self.weather == WEATHER_WIND:
            count = 40
        for _ in range(count):
            self.weather_particles.append({
                'x': random.randint(0, WIDTH),
                'y': random.randint(0, HEIGHT),
                'spd': random.uniform(3, 8),
                'size': random.uniform(1, 3),
                'wind': random.uniform(-1, 1),
            })

    def _check_food_rot(self):
        p = self.player
        current_hour = self.game_minute / MINUTES_PER_HOUR
        to_remove = []
        for item, expire in list(p.food_timers.items()):
            if current_hour >= expire:
                if p.has_item(item):
                    p.remove_item(item)
                    self.log(f"{ITEMS[item][0]} 腐烂了!", (150, 100, 50))
                    p.mental = max(0, p.mental - 3)
                to_remove.append(item)
        for item in to_remove:
            del p.food_timers[item]

    def _near_campfire(self):
        px, py = self.player.x, self.player.y
        for dy in range(-2, 3):
            for dx in range(-2, 3):
                if (px + dx, py + dy) in self.campfires and self.campfires[(px + dx, py + dy)] > 0:
                    return True
        return False

    def _in_shelter(self):
        px, py = self.player.x, self.player.y
        for dy in range(-1, 2):
            for dx in range(-1, 2):
                if (px + dx, py + dy) in self.shelters:
                    return True
        return False

    # ─── 互动 ────────────────────────────────────
    def interact(self):
        p = self.player
        px, py = p.x, p.y
        tile = self.game_map[py][px]

        # 检查周围8格
        for dy in range(-1, 2):
            for dx in range(-1, 2):
                nx, ny = px + dx, py + dy
                if not (0 <= nx < MAP_W and 0 <= ny < MAP_H):
                    continue
                t = self.game_map[ny][nx]

                if t == TILE_FOREST:
                    # 砍树
                    if p.stamina < 8:
                        self.log("体力不足!", C_RED)
                        return
                    p.stamina -= 8
                    amt = 2 + (3 if p.weapon == "stone_axe" else 0)
                    p.add_item("wood", amt)
                    if random.random() < 0.3:
                        p.add_item("fiber", 1)
                    if random.random() < 0.15:
                        p.add_item("herb", 1)
                    self.game_map[ny][nx] = TILE_DIRT
                    self.log(f"砍伐获得 木材x{amt}", C_GREEN)
                    return

                if t == TILE_BUSH:
                    if p.stamina < 4:
                        self.log("体力不足!", C_RED)
                        return
                    p.stamina -= 4
                    if random.random() < 0.5:
                        self._add_food("berry", random.randint(1, 3))
                        self.log("采集到 浆果", C_GREEN)
                    elif random.random() < 0.4:
                        self._add_food("mushroom", random.randint(1, 2))
                        self.log("采集到 蘑菇", C_GREEN)
                    else:
                        p.add_item("herb", random.randint(1, 2))
                        self.log("采集到 草药", C_GREEN)
                    if random.random() < 0.3:
                        p.add_item("fiber", 1)
                    self.game_map[ny][nx] = TILE_GRASS
                    return

                if t == TILE_ROCK:
                    if p.stamina < 10:
                        self.log("体力不足!", C_RED)
                        return
                    p.stamina -= 10
                    amt = 2 + (2 if p.weapon in ("stone_axe", "stone_spear") else 0)
                    p.add_item("stone", amt)
                    if random.random() < 0.2:
                        p.add_item("bone", 1)
                    self.log(f"采矿获得 石头x{amt}", C_GREEN)
                    return

                if t == TILE_WATER:
                    p.add_item("dirty_water", 1)
                    self.log("取得 脏水 (需要煮沸!)", C_ORANGE)
                    return

                if t == TILE_DARKGRASS:
                    if p.stamina < 3:
                        self.log("体力不足!", C_RED)
                        return
                    p.stamina -= 3
                    p.add_item("fiber", random.randint(1, 3))
                    if random.random() < 0.2:
                        p.add_item("herb", 1)
                    self.game_map[ny][nx] = TILE_GRASS
                    self.log("采集纤维", C_GREEN)
                    return

        # 当前格互动
        if tile == TILE_CAVE:
            self.log("山洞: 可以休息避风", C_YELLOW)
            return

        # 营火互动
        if (px, py) in self.campfires:
            fuel = self.campfires[(px, py)]
            if fuel > 0:
                # 烹饪
                if p.has_item("dirty_water"):
                    p.remove_item("dirty_water")
                    self._add_food("clean_water", 1)
                    self.log("脏水已煮沸 → 净水!", C_GREEN)
                    return
                if p.has_item("raw_meat"):
                    p.remove_item("raw_meat")
                    self._add_food("cooked_meat", 1)
                    self.log("生肉已烤熟!", C_GREEN)
                    return
                if p.has_item("raw_fish"):
                    p.remove_item("raw_fish")
                    self._add_food("cooked_fish", 1)
                    self.log("生鱼已烤熟!", C_GREEN)
                    return
                # 添加燃料
                if p.has_item("wood"):
                    p.remove_item("wood")
                    self.campfires[(px, py)] += 60
                    self.log("添加燃料 +60分钟", C_ORANGE)
                    return
                self.log("没有可烹饪/添加的物品", C_GRAY)
            else:
                if p.has_item("wood", 2):
                    p.remove_item("wood", 2)
                    self.campfires[(px, py)] = 120
                    self.log("重新点燃营火!", C_ORANGE)
                else:
                    self.log("营火已熄灭, 需要2木材重新点燃", C_RED)
            return

        # 陷阱拾取
        for trap in self.traps:
            if trap.x == px and trap.y == py and trap.caught:
                item, cnt = trap.caught
                p.add_item(item, cnt)
                self.log(f"从陷阱收取 {ITEMS[item][0]}x{cnt}", C_GREEN)
                trap.caught = None
                return

        self.log("附近没有可互动的物体", C_GRAY)

    def _add_food(self, item, count=1):
        p = self.player
        p.add_item(item, count)
        if item in FOOD_DATA:
            current_hour = self.game_minute / MINUTES_PER_HOUR
            p.food_timers[item] = current_hour + FOOD_DATA[item][5]

    # ─── 进食 ────────────────────────────────────
    def eat(self, item):
        p = self.player
        if item not in FOOD_DATA or not p.has_item(item):
            return
        fd = FOOD_DATA[item]
        p.hunger = min(100, p.hunger + fd[0])
        p.thirst = min(100, p.thirst + fd[1])
        if fd[2] > 0:
            for bp in BODY_PARTS:
                p.body[bp].heal(max(1, fd[2] // len(BODY_PARTS)))
        elif fd[2] < 0:
            p.body[BP_TORSO].take_damage(abs(fd[2]))
        p.mental = min(100, max(0, p.mental + fd[3]))
        p.remove_item(item)
        if item in p.food_timers:
            del p.food_timers[item]
        self.log(f"食用了 {ITEMS[item][0]}  饱食+{fd[0]} 水分+{fd[1]}", C_GREEN)

        # 脏水风险
        if item == "dirty_water" and random.random() < 0.30:
            p.poisoned = 300
            p.sick = 600
            p.disease_name = "水中毒"
            self.log("脏水让你生病了!", C_RED)
        if item in ("raw_meat", "raw_fish") and random.random() < 0.15:
            p.sick = 480
            p.disease_name = "食物中毒"
            self.log("生食让你生病了!", C_RED)

    # ─── 制作 ────────────────────────────────────
    def craft(self, idx):
        if idx < 0 or idx >= len(RECIPES):
            return
        item, req, cat, desc = RECIPES[idx]
        p = self.player
        if not all(p.has_item(k, v) for k, v in req.items()):
            self.log("材料不足!", C_RED)
            return
        for k, v in req.items():
            p.remove_item(k, v)

        if cat == "武器":
            p.weapon = item
            self.log(f"制作并装备了 {ITEMS[item][0]}!", C_GREEN)
        elif cat == "护甲":
            p.armor = item
            self.log(f"穿上了 {ITEMS[item][0]}!", C_GREEN)
        elif item == "stew" or item == "tea":
            self._add_food(item, 1)
            self.log(f"制作了 {ITEMS[item][0]}", C_GREEN)
        elif item == "water_filter":
            if p.has_item("dirty_water"):
                cnt = p.inventory.get("dirty_water", 0)
                p.remove_item("dirty_water", cnt)
                self._add_food("clean_water", cnt)
                self.log(f"净水器过滤了{cnt}份脏水", C_GREEN)
            p.add_item(item, 1)
            self.log(f"制作了 {ITEMS[item][0]}", C_GREEN)
        else:
            p.add_item(item, 1)
            self.log(f"制作了 {ITEMS[item][0]}", C_GREEN)

    # ─── 建造 ────────────────────────────────────
    def build(self, btype):
        p = self.player
        px, py = p.x, p.y
        if btype == "campfire":
            req = {"wood": 5, "stone": 3}
            if not all(p.has_item(k, v) for k, v in req.items()):
                self.log("材料不足! 需要: 木材x5 石头x3", C_RED)
                return
            for k, v in req.items():
                p.remove_item(k, v)
            self.campfires[(px, py)] = 180
            self.log("建造了营火!", C_ORANGE)
        elif btype == "shelter":
            req = {"wood": 15, "fiber": 8, "stone": 5}
            if not all(p.has_item(k, v) for k, v in req.items()):
                self.log("材料不足! 需要: 木材x15 纤维x8 石头x5", C_RED)
                return
            for k, v in req.items():
                p.remove_item(k, v)
            self.shelters.add((px, py))
            self.log("建造了庇护所!", C_GREEN)
        elif btype == "wall":
            req = {"stone": 8, "wood": 3}
            if not all(p.has_item(k, v) for k, v in req.items()):
                self.log("材料不足! 需要: 石头x8 木材x3", C_RED)
                return
            for k, v in req.items():
                p.remove_item(k, v)
            self.walls.add((px, py))
            self.log("建造了围墙!", C_GREEN)
        elif btype == "trap":
            if not p.has_item("trap"):
                self.log("没有陷阱! 先制作陷阱", C_RED)
                return
            p.remove_item("trap")
            self.traps.append(Trap(px, py))
            self.log("放置了陷阱!", C_GREEN)

    # ─── 治疗 ────────────────────────────────────
    def heal(self, action):
        p = self.player
        if action == "bandage":
            if not p.has_item("bandage"):
                self.log("没有绷带!", C_RED)
                return
            for bp in BODY_PARTS:
                if p.body[bp].bleeding:
                    p.body[bp].stop_bleed()
                    p.body[bp].heal(15)
                    p.remove_item("bandage")
                    self.log(f"绷带止住了{bp}的出血", C_GREEN)
                    return
            self.log("没有流血的伤口", C_GRAY)
        elif action == "splint":
            if not p.has_item("splint"):
                self.log("没有夹板!", C_RED)
                return
            for bp in BODY_PARTS:
                if p.body[bp].fractured:
                    p.body[bp].fix_fracture()
                    p.body[bp].heal(10)
                    p.remove_item("splint")
                    self.log(f"夹板固定了{bp}的骨折", C_GREEN)
                    return
            self.log("没有骨折的部位", C_GRAY)
        elif action == "poultice":
            if not p.has_item("poultice"):
                self.log("没有药膏!", C_RED)
                return
            for bp in BODY_PARTS:
                if p.body[bp].infected:
                    p.body[bp].cure_infection()
                    p.body[bp].heal(20)
                    p.remove_item("poultice")
                    self.log(f"药膏治疗了{bp}的感染", C_GREEN)
                    return
            # 没有感染就回血
            for bp in BODY_PARTS:
                p.body[bp].heal(10)
            p.remove_item("poultice")
            self.log("药膏恢复了各部位HP", C_GREEN)
        elif action == "antibiotic":
            if not p.has_item("antibiotic"):
                self.log("没有抗生素!", C_RED)
                return
            p.remove_item("antibiotic")
            for bp in BODY_PARTS:
                p.body[bp].cure_infection()
            p.sick = 0
            p.disease_name = ""
            self.log("抗生素治疗了所有感染和疾病!", C_GREEN)

    # ─── 战斗 ────────────────────────────────────
    def start_combat(self, beast):
        self.state = "combat"
        self.combat_beast = beast
        self.combat_log = [f"遭遇了 {beast.name}!", f"HP:{beast.hp} ATK:{beast.atk} DEF:{beast.defense}"]
        self.combat_player_turn = True
        self.combat_fleeing = False
        if self.is_night:
            self.combat_log.append("⚠ 夜间野兽更加凶猛!")

    def combat_attack(self):
        b = self.combat_beast
        p = self.player
        if not b or not b.alive or not self.combat_player_turn:
            return

        atk = p.get_attack()
        crit = random.random() < 0.12
        if crit:
            atk = int(atk * 1.8)
        dmg = max(1, atk - b.defense // 2)
        b.hp -= dmg

        msg = f"你攻击{b.name}, 造成{dmg}伤害"
        if crit:
            msg += " (暴击!)"
        self.combat_log.append(msg)

        if b.hp <= 0:
            b.alive = False
            p.kills += 1
            leveled = p.add_xp(b.xp)
            self.combat_log.append(f"击杀了{b.name}! +{b.xp}XP")
            if leveled:
                self.combat_log.append(f"升级! Lv.{p.level}")
            for item, cnt, chance in b.drops:
                if random.random() < chance:
                    if item in FOOD_DATA:
                        self._add_food(item, cnt)
                    else:
                        p.add_item(item, cnt)
                    self.combat_log.append(f"  获得 {ITEMS[item][0]}x{cnt}")
            self.state = "world"
            self.log(f"击杀了{b.name}!", C_GREEN)
            return

        self.combat_player_turn = False
        # 野兽反击
        self._beast_attack()

    def _beast_attack(self):
        b = self.combat_beast
        p = self.player
        night_mult = 1.3 if self.is_night else 1.0
        atk = int(b.atk * night_mult)
        part, actual = p.take_damage_to_part(atk, None)
        actual = max(1, actual - p.get_defense() // 2)

        self.combat_log.append(f"{b.name}攻击了你的{part}, 造成{actual}伤害")

        bp = p.body[part]
        if bp.bleeding:
            self.combat_log.append(f"  {part}在流血!")
        if bp.fractured:
            self.combat_log.append(f"  {part}骨折了!")

        # 蛇毒
        if b.btype == "snake" and random.random() < b.poison_chance:
            p.poisoned = 300
            self.combat_log.append("  你中毒了!")

        if not p.is_alive():
            self.combat_log.append("你倒下了...")
            self.state = "gameover"
            return

        self.combat_player_turn = True

    def combat_flee(self):
        p = self.player
        b = self.combat_beast
        chance = 40
        if p.body[BP_LLEG].fractured:
            chance -= 20
        if p.body[BP_LLEG].crippled:
            chance -= 15
        if p.body[BP_RLEG].fractured:
            chance -= 20
        if p.body[BP_RLEG].crippled:
            chance -= 15
        if self.is_night:
            chance -= 15

        if random.randint(1, 100) <= chance:
            self.combat_log.append("你成功逃脱了!")
            self.state = "world"
            self.log("成功逃离战斗", C_YELLOW)
        else:
            self.combat_log.append("逃跑失败!")
            self._beast_attack()

    def combat_heal(self):
        p = self.player
        if p.has_item("bandage"):
            self.heal("bandage")
        elif p.has_item("herb"):
            p.remove_item("herb")
            for bp in BODY_PARTS:
                p.body[bp].heal(8)
            self.log("使用草药恢复HP", C_GREEN)
        else:
            self.log("没有可用的医疗物品", C_RED)

    # ─── 渲染 ────────────────────────────────────
    def render(self):
        self.screen.fill(C_BLACK)

        if self.state == "gameover":
            self._render_gameover()
        elif self.state == "combat":
            self._render_combat()
        elif self.state == "help":
            self._render_help()
        else:
            self._render_world()
            self._render_hud()
            self._render_messages()

            if self.state == "inventory":
                self._render_inventory()
            elif self.state == "craft":
                self._render_craft()
            elif self.state == "build":
                self._render_build()
            elif self.state == "eat":
                self._render_eat()
            elif self.state == "heal":
                self._render_heal()
            elif self.state == "status":
                self._render_status()

        pygame.display.flip()

    def _render_world(self):
        # 相机跟随
        p = self.player
        self.cam_x = p.px - WIDTH // 2
        self.cam_y = p.py - HEIGHT // 2 + 40
        self.cam_x = max(0, min(MAP_W * TILE_SIZE - WIDTH, self.cam_x))
        self.cam_y = max(0, min(MAP_H * TILE_SIZE - HEIGHT + 80, self.cam_y))

        start_x = max(0, int(self.cam_x // TILE_SIZE))
        start_y = max(0, int(self.cam_y // TILE_SIZE))
        end_x = min(MAP_W, start_x + WIDTH // TILE_SIZE + 2)
        end_y = min(MAP_H, start_y + HEIGHT // TILE_SIZE + 2)

        for ty in range(start_y, end_y):
            for tx in range(start_x, end_x):
                if not self.explored[ty][tx]:
                    continue
                tile = self.game_map[ty][tx]
                sx = tx * TILE_SIZE - int(self.cam_x)
                sy = ty * TILE_SIZE - int(self.cam_y)

                if tile == TILE_GRASS:
                    c = C_GRASS if (tx + ty) % 3 != 0 else C_DARKGRASS
                    pygame.draw.rect(self.screen, c, (sx, sy, TILE_SIZE, TILE_SIZE))
                elif tile == TILE_FOREST:
                    pygame.draw.rect(self.screen, C_DARKGRASS, (sx, sy, TILE_SIZE, TILE_SIZE))
                    pygame.draw.rect(self.screen, C_TREE_TRUNK, (sx + 12, sy + 16, 8, 16))
                    leaf_c = C_LEAF
                    if self.season == SEASON_AUTUMN:
                        leaf_c = C_AUTUMN_LEAF
                    elif self.season == SEASON_WINTER:
                        leaf_c = C_DEAD_LEAF
                    pygame.draw.circle(self.screen, leaf_c, (sx + 16, sy + 10), 12)
                elif tile == TILE_WATER:
                    wave = int(math.sin(time.time() * 2 + tx + ty) * 10)
                    c = (40 + wave, 80 + wave, 160)
                    pygame.draw.rect(self.screen, c, (sx, sy, TILE_SIZE, TILE_SIZE))
                elif tile == TILE_DEEPWATER:
                    wave = int(math.sin(time.time() * 1.5 + tx + ty) * 5)
                    c = (20 + wave, 40 + wave, 100)
                    pygame.draw.rect(self.screen, c, (sx, sy, TILE_SIZE, TILE_SIZE))
                elif tile == TILE_ROCK:
                    pygame.draw.rect(self.screen, C_ROCK, (sx, sy, TILE_SIZE, TILE_SIZE))
                    pygame.draw.polygon(self.screen, C_DARKROCK,
                                        [(sx + 4, sy + 28), (sx + 16, sy + 4), (sx + 28, sy + 28)])
                elif tile == TILE_SAND:
                    pygame.draw.rect(self.screen, C_SAND, (sx, sy, TILE_SIZE, TILE_SIZE))
                elif tile == TILE_SNOW:
                    pygame.draw.rect(self.screen, C_SNOW_GROUND, (sx, sy, TILE_SIZE, TILE_SIZE))
                elif tile == TILE_DIRT:
                    pygame.draw.rect(self.screen, C_DIRT, (sx, sy, TILE_SIZE, TILE_SIZE))
                elif tile == TILE_CAVE:
                    pygame.draw.rect(self.screen, C_DARKROCK, (sx, sy, TILE_SIZE, TILE_SIZE))
                    pygame.draw.circle(self.screen, (20, 20, 30), (sx + 16, sy + 16), 10)
                elif tile == TILE_BUSH:
                    pygame.draw.rect(self.screen, C_GRASS, (sx, sy, TILE_SIZE, TILE_SIZE))
                    pygame.draw.circle(self.screen, (60, 130, 50), (sx + 16, sy + 18), 10)
                    for ddx, ddy in [(8, 14), (22, 16), (14, 22), (20, 10)]:
                        pygame.draw.circle(self.screen, (180, 40, 60), (sx + ddx, sy + ddy), 2)
                elif tile == TILE_DARKGRASS:
                    pygame.draw.rect(self.screen, C_DARKGRASS, (sx, sy, TILE_SIZE, TILE_SIZE))
                    for i in range(4):
                        gx = sx + 4 + i * 8
                        pygame.draw.line(self.screen, (50, 130, 40), (gx, sy + 28), (gx + 2, sy + 8), 2)

                # 围墙
                if (tx, ty) in self.walls:
                    pygame.draw.rect(self.screen, (110, 100, 90), (sx, sy, TILE_SIZE, TILE_SIZE))
                    pygame.draw.rect(self.screen, (80, 70, 60), (sx + 2, sy + 2, TILE_SIZE - 4, TILE_SIZE - 4), 2)

                # 庇护所
                if (tx, ty) in self.shelters:
                    pygame.draw.rect(self.screen, C_GRASS, (sx, sy, TILE_SIZE, TILE_SIZE))
                    pygame.draw.rect(self.screen, C_SHELTER, (sx + 2, sy + 8, 28, 22))
                    pygame.draw.polygon(self.screen, (140, 90, 50),
                                        [(sx, sy + 10), (sx + 16, sy), (sx + 32, sy + 10)])
                    pygame.draw.rect(self.screen, (80, 60, 35), (sx + 12, sy + 18, 8, 12))

                # 营火
                if (tx, ty) in self.campfires:
                    fuel = self.campfires[(tx, ty)]
                    pygame.draw.rect(self.screen, C_DIRT, (sx, sy, TILE_SIZE, TILE_SIZE))
                    pygame.draw.circle(self.screen, C_DARKBROWN, (sx + 16, sy + 18), 8)
                    if fuel > 0:
                        flicker = math.sin(time.time() * 8) * 3
                        pygame.draw.circle(self.screen, C_FIRE, (sx + 16, sy + 14 + int(flicker)), 7)
                        pygame.draw.circle(self.screen, (255, 200, 60), (sx + 16, sy + 12 + int(flicker)), 4)
                        # 光晕
                        glow = pygame.Surface((TILE_SIZE * 3, TILE_SIZE * 3), pygame.SRCALPHA)
                        pygame.draw.circle(glow, (255, 180, 60, 25), (TILE_SIZE * 3 // 2, TILE_SIZE * 3 // 2),
                                           TILE_SIZE * 3 // 2)
                        self.screen.blit(glow, (sx - TILE_SIZE, sy - TILE_SIZE), special_flags=pygame.BLEND_RGBA_ADD)

                # 陷阱
                for trap in self.traps:
                    if trap.x == tx and trap.y == ty:
                        if trap.active:
                            pygame.draw.rect(self.screen, (130, 110, 70), (sx + 6, sy + 6, 20, 20), 2)
                            pygame.draw.line(self.screen, (130, 110, 70), (sx + 6, sy + 6), (sx + 26, sy + 26), 1)
                            pygame.draw.line(self.screen, (130, 110, 70), (sx + 26, sy + 6), (sx + 6, sy + 26), 1)
                        elif trap.caught:
                            pygame.draw.circle(self.screen, C_GREEN, (sx + 16, sy + 16), 6)

        # 野兽
        for beast in self.beasts:
            if not beast.alive:
                continue
            bsx = beast.px - int(self.cam_x)
            bsy = beast.py - int(self.cam_y)
            if -TILE_SIZE < bsx < WIDTH + TILE_SIZE and -TILE_SIZE < bsy < HEIGHT + TILE_SIZE:
                btx, bty = beast.x, beast.y
                if 0 <= btx < MAP_W and 0 <= bty < MAP_H and self.explored[bty][btx]:
                    pygame.draw.circle(self.screen, beast.color, (int(bsx), int(bsy)), beast.size)
                    pygame.draw.circle(self.screen, C_WHITE, (int(bsx) - 3, int(bsy) - 3), 2)
                    pygame.draw.circle(self.screen, C_WHITE, (int(bsx) + 3, int(bsy) - 3), 2)
                    pygame.draw.circle(self.screen, C_BLACK, (int(bsx) - 3, int(bsy) - 3), 1)
                    pygame.draw.circle(self.screen, C_BLACK, (int(bsx) + 3, int(bsy) - 3), 1)
                    # 血条
                    if beast.hp < beast.max_hp:
                        bw = beast.size * 2
                        hp_r = beast.hp / beast.max_hp
                        pygame.draw.rect(self.screen, (60, 0, 0),
                                         (int(bsx) - bw // 2, int(bsy) - beast.size - 6, bw, 3))
                        pygame.draw.rect(self.screen, C_RED,
                                         (int(bsx) - bw // 2, int(bsy) - beast.size - 6, int(bw * hp_r), 3))
                    # 名字
                    ns = self.font_sm.render(beast.name, True, C_WHITE)
                    self.screen.blit(ns, (int(bsx) - ns.get_width() // 2, int(bsy) - beast.size - 18))

        # 玩家
        psx = p.px - int(self.cam_x)
        psy = p.py - int(self.cam_y)
        pygame.draw.circle(self.screen, (220, 190, 150), (int(psx), int(psy) - 8), 8)
        pygame.draw.rect(self.screen, (60, 100, 180), (int(psx) - 6, int(psy), 12, 14))
        pygame.draw.rect(self.screen, (80, 60, 40), (int(psx) - 5, int(psy) + 14, 4, 10))
        pygame.draw.rect(self.screen, (80, 60, 40), (int(psx) + 1, int(psy) + 14, 4, 10))
        if p.weapon:
            pygame.draw.line(self.screen, (180, 160, 120), (int(psx) + 8, int(psy) + 2),
                             (int(psx) + 18, int(psy) - 6), 2)
        if p.armor:
            ac = (140, 100, 60) if p.armor == "leather_armor" else (160, 165, 170)
            pygame.draw.rect(self.screen, ac, (int(psx) - 7, int(psy) + 1, 14, 12), 1)

        # 昼夜覆盖
        daylight = self.get_daylight()
        if daylight < 1.0:
            overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
            alpha = int((1.0 - daylight) * 200)
            overlay.fill((10, 10, 40, alpha))
            self.screen.blit(overlay, (0, 0))

            # 营火光圈
            if self._near_campfire() or p.has_item("torch"):
                light = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
                pygame.draw.circle(light, (255, 200, 100, 60), (int(psx), int(psy)), 120)
                self.screen.blit(light, (0, 0), special_flags=pygame.BLEND_RGBA_ADD)

        # 天气粒子
        self._render_weather()

        # 幻觉
        if p.mental < 15:
            if int(time.time() * 3) % 4 == 0:
                purple = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
                purple.fill((100, 0, 150, 30))
                self.screen.blit(purple, (0, 0))

    def _render_weather(self):
        for wp in self.weather_particles:
            if self.weather in (WEATHER_RAIN, WEATHER_STORM):
                wp['y'] += wp['spd'] * 2
                wp['x'] += wp['wind'] + 1
                if wp['y'] > HEIGHT:
                    wp['y'] = -10
                    wp['x'] = random.randint(0, WIDTH)
                pygame.draw.line(self.screen, (150, 180, 255, 180),
                                 (int(wp['x']), int(wp['y'])),
                                 (int(wp['x'] + wp['wind'] * 2), int(wp['y'] + 8)), 1)
            elif self.weather == WEATHER_BLIZZARD:
                wp['y'] += wp['spd'] * 0.5
                wp['x'] += math.sin(wp['y'] * 0.02) * 0.5 + wp['wind']
                if wp['y'] > HEIGHT:
                    wp['y'] = -10
                    wp['x'] = random.randint(0, WIDTH)
                pygame.draw.circle(self.screen, (240, 245, 255), (int(wp['x']), int(wp['y'])), int(wp['size']))
            elif self.weather == WEATHER_WIND:
                wp['x'] += wp['spd'] * 3
                wp['y'] += wp['wind'] * 0.5
                if wp['x'] > WIDTH:
                    wp['x'] = -10
                    wp['y'] = random.randint(0, HEIGHT)
                pygame.draw.line(self.screen, (200, 200, 200, 80),
                                 (int(wp['x']), int(wp['y'])),
                                 (int(wp['x'] + 12), int(wp['y'])), 1)

    def _render_hud(self):
        p = self.player
        hud_y = HEIGHT - 80
        pygame.draw.rect(self.screen, (20, 20, 25), (0, hud_y, WIDTH, 80))
        pygame.draw.line(self.screen, (60, 60, 70), (0, hud_y), (WIDTH, hud_y), 2)

        # 状态条
        bars = [
            ("HP", p.body[BP_TORSO].hp, p.body[BP_TORSO].max_hp, C_RED),
            ("饱食", p.hunger, 100, C_ORANGE),
            ("水分", p.thirst, 100, C_BLUE),
            ("体力", p.stamina, 100, C_YELLOW),
            ("精神", p.mental, 100, C_PURPLE),
        ]
        bx = 8
        for label, val, mx, color in bars:
            lbl = self.font_sm.render(label, True, C_WHITE)
            self.screen.blit(lbl, (bx, hud_y + 4))
            bar_x = bx + 30
            bar_w = 80
            pygame.draw.rect(self.screen, (40, 40, 40), (bar_x, hud_y + 6, bar_w, 10))
            fill = int(bar_w * max(0, val) / max(1, mx))
            pygame.draw.rect(self.screen, color, (bar_x, hud_y + 6, fill, 10))
            vs = self.font_sm.render(f"{int(val)}", True, C_WHITE)
            self.screen.blit(vs, (bar_x + bar_w + 4, hud_y + 4))
            bx += 130

        # 体温
        tc = C_WHITE
        if p.body_temp < 35:
            tc = (100, 180, 255)
        elif p.body_temp > 38.5:
            tc = (255, 100, 80)
        ts = self.font_sm.render(f"体温:{p.body_temp:.1f}°C", True, tc)
        self.screen.blit(ts, (bx + 10, hud_y + 4))

        # 时间行
        y2 = hud_y + 22
        hour = self.game_hour
        minute = self.game_minute % MINUTES_PER_HOUR
        time_str = f"第{self.game_day}天 {int(hour):02d}:{int(minute):02d}"
        self.screen.blit(self.font_sm.render(time_str, True, C_WHITE), (8, y2))

        sc = [(100, 200, 100), (255, 200, 50), (230, 150, 50), (150, 200, 255)]
        self.screen.blit(self.font_sm.render(f"{SEASON_NAMES[self.season]}季", True, sc[self.season]),
                         (110, y2))
        self.screen.blit(self.font_sm.render(f"天气:{WEATHER_NAMES[self.weather]}", True, (180, 200, 220)),
                         (170, y2))
        self.screen.blit(self.font_sm.render(f"Lv.{p.level} XP:{p.xp}/{p.xp_next}", True, C_YELLOW),
                         (280, y2))

        wpn = WEAPON_DATA[p.weapon][0] if p.weapon in WEAPON_DATA else "空手"
        arm = ITEMS[p.armor][0] if p.armor else "无"
        self.screen.blit(self.font_sm.render(f"武器:{wpn} 防具:{arm}", True, (180, 180, 180)),
                         (420, y2))

        # 状态效果
        effects = []
        if p.poisoned > 0: effects.append("☠毒")
        if p.sick > 0: effects.append(f"🤒{p.disease_name}")
        for bp in BODY_PARTS:
            body = p.body[bp]
            if body.bleeding: effects.append(f"🩸{bp}")
            if body.fractured: effects.append(f"🦴{bp}")
            if body.infected: effects.append(f"🦠{bp}")
            if body.frostbite: effects.append(f"🥶{bp}")
            if body.crippled: effects.append(f"💀{bp}")
        if effects:
            self.screen.blit(self.font_sm.render(" ".join(effects[:8]), True, C_RED), (8, y2 + 16))

        # 操作提示
        help_t = "I-背包 C-制作 B-建造 F-进食 H-治疗 M-状态 F1-帮助"
        self.screen.blit(self.font_sm.render(help_t, True, (120, 120, 130)),
                         (WIDTH - 450, y2 + 16))

        # 人体模型(右上)
        self._render_body_model(WIDTH - 110, 10)

    def _render_body_model(self, bx, by):
        bg = pygame.Surface((100, 120), pygame.SRCALPHA)
        bg.fill((0, 0, 0, 130))
        self.screen.blit(bg, (bx, by))

        positions = {
            BP_HEAD: (bx + 50, by + 15, 12, 12),
            BP_TORSO: (bx + 50, by + 40, 18, 24),
            BP_LARM: (bx + 28, by + 38, 10, 20),
            BP_RARM: (bx + 72, by + 38, 10, 20),
            BP_LLEG: (bx + 42, by + 72, 10, 22),
            BP_RLEG: (bx + 58, by + 72, 10, 22),
        }

        for bp in BODY_PARTS:
            body = self.player.body[bp]
            cx, cy, w, h = positions[bp]

            if body.crippled:
                c = (60, 60, 60)
            elif body.infected:
                c = (150, 80, 50)
            elif body.bleeding:
                c = (200, 50, 50)
            elif body.fractured:
                c = (200, 150, 50)
            elif body.frostbite:
                c = (100, 150, 220)
            elif body.ratio > 0.7:
                c = (80, 180, 80)
            elif body.ratio > 0.4:
                c = (200, 180, 50)
            else:
                c = (200, 80, 50)

            pygame.draw.rect(self.screen, c, (cx - w // 2, cy - h // 2, w, h))
            pygame.draw.rect(self.screen, (200, 200, 200), (cx - w // 2, cy - h // 2, w, h), 1)

        # 标签
        self.screen.blit(self.font_sm.render("身体", True, C_WHITE), (bx + 35, by + 100))

    def _render_messages(self):
        now = time.time()
        visible = [(m, c) for m, c, t in self.messages if now - t < 8]
        y = HEIGHT - 80 - len(visible[-5:]) * 16 - 4
        for msg, color in visible[-5:]:
            surf = self.font_sm.render(msg, True, color)
            bg = pygame.Surface((surf.get_width() + 8, 16), pygame.SRCALPHA)
            bg.fill((0, 0, 0, 130))
            self.screen.blit(bg, (4, y))
            self.screen.blit(surf, (8, y))
            y += 16

    def _render_overlay(self, title, items, key_func=None):
        px, py = 80, 40
        pw, ph = WIDTH - 160, HEIGHT - 160
        bg = pygame.Surface((pw, ph), pygame.SRCALPHA)
        bg.fill((20, 20, 30, 235))
        self.screen.blit(bg, (px, py))
        pygame.draw.rect(self.screen, (100, 100, 120), (px, py, pw, ph), 2)

        title_s = self.font_lg.render(title, True, C_WHITE)
        self.screen.blit(title_s, (px + 20, py + 10))

        close = self.font_sm.render("[ESC] 关闭  [↑↓] 滚动  [数字键/Enter] 选择", True, C_GRAY)
        self.screen.blit(close, (px + 20, py + ph - 25))

        return px, py + 45, pw, ph - 70

    def _render_inventory(self):
        px, py, pw, ph = self._render_overlay("📦 背包", [])
        p = self.player
        items = list(p.inventory.items())
        y = py
        if not items:
            self.screen.blit(self.font_md.render("背包空空如也...", True, C_GRAY), (px + 20, y))
            return

        categories = {"材料": [], "食物": [], "医疗": [], "武器": [], "护甲": [], "工具": []}
        for item_id, count in items:
            if item_id in ITEMS:
                cat_name = {"material": "材料", "food": "食物", "medical": "医疗",
                            "weapon": "武器", "armor": "护甲", "tool": "工具"}.get(ITEMS[item_id][2], "其他")
                categories[cat_name].append((item_id, count))

        for cat, cat_items in categories.items():
            if not cat_items:
                continue
            self.screen.blit(self.font_md.render(f"── {cat} ──", True, C_YELLOW), (px + 20, y))
            y += 22
            for item_id, count in cat_items:
                name, color, _ = ITEMS[item_id]
                icon = pygame.Surface((10, 10))
                icon.fill(color)
                self.screen.blit(icon, (px + 40, y + 3))
                status = ""
                if item_id in p.food_timers:
                    current_hour = self.game_minute / MINUTES_PER_HOUR
                    remaining = p.food_timers[item_id] - current_hour
                    if remaining < 24:
                        status = f" ⚠{int(remaining)}h后腐烂"
                text = f"{name} x{count}{status}"
                self.screen.blit(self.font_sm.render(text, True, C_WHITE), (px + 56, y))
                y += 18
            y += 6

    def _render_craft(self):
        px, py, pw, ph = self._render_overlay("🔨 制作台", [])
        p = self.player
        visible = RECIPES[self.scroll:self.scroll + 14]

        for i, (item_id, req, cat, desc) in enumerate(visible):
            idx = self.scroll + i
            can = all(p.has_item(k, v) for k, v in req.items())
            by = py + i * 38
            bc = (40, 50, 60) if can else (30, 30, 35)
            pygame.draw.rect(self.screen, bc, (px + 10, by, pw - 20, 34))

            name = ITEMS[item_id][0]
            nc = C_WHITE if can else C_GRAY
            self.screen.blit(self.font_md.render(f"{idx + 1}. {name} [{cat}]", True, nc), (px + 20, by + 2))

            mats = []
            for mat, cnt in req.items():
                have = p.inventory.get(mat, 0)
                mc = C_GREEN if have >= cnt else C_RED
                mats.append(f"{ITEMS[mat][0]}:{have}/{cnt}")
            self.screen.blit(self.font_sm.render("  ".join(mats), True, C_WHITE if can else C_GRAY),
                             (px + 20, by + 18))
            self.screen.blit(self.font_sm.render(desc, True, (150, 150, 160)),
                             (px + pw - 250, by + 8))

    def _render_build(self):
        px, py, pw, ph = self._render_overlay("🏗 建造", [])
        p = self.player
        builds = [
            ("campfire", "营火", {"wood": 5, "stone": 3}, "温暖/烹饪/照明"),
            ("shelter", "庇护所", {"wood": 15, "fiber": 8, "stone": 5}, "遮风挡雨/恢复"),
            ("wall", "石墙", {"stone": 8, "wood": 3}, "阻挡野兽"),
            ("trap", "放置陷阱", {"trap": 1}, "自动捕猎"),
        ]
        for i, (bid, name, req, desc) in enumerate(builds):
            can = all(p.has_item(k, v) for k, v in req.items())
            by = py + i * 48
            bc = (40, 50, 40) if can else (30, 30, 30)
            pygame.draw.rect(self.screen, bc, (px + 10, by, pw - 20, 44))
            nc = C_WHITE if can else C_GRAY
            self.screen.blit(self.font_md.render(f"{i + 1}. {name}", True, nc), (px + 20, by + 4))
            mats = []
            for mat, cnt in req.items():
                have = p.inventory.get(mat, 0)
                mats.append(f"{ITEMS[mat][0]}:{have}/{cnt}")
            self.screen.blit(self.font_sm.render("  ".join(mats), True, C_WHITE if can else C_GRAY),
                             (px + 20, by + 24))
            self.screen.blit(self.font_sm.render(desc, True, (140, 160, 140)),
                             (px + pw - 200, by + 12))

    def _render_eat(self):
        px, py, pw, ph = self._render_overlay("🍖 进食", [])
        p = self.player
        foods = [(k, v) for k, v in p.inventory.items() if k in FOOD_DATA]
        if not foods:
            self.screen.blit(self.font_md.render("没有可食用的物品", True, C_GRAY), (px + 20, py))
            return
        for i, (item_id, count) in enumerate(foods):
            fd = FOOD_DATA[item_id]
            name = ITEMS[item_id][0]
            by = py + i * 30
            pygame.draw.rect(self.screen, (40, 35, 30), (px + 10, by, pw - 20, 26))
            text = f"{i + 1}. {name} x{count}  (饱食+{fd[0]} 水分+{fd[1]} HP{'+' if fd[2] >= 0 else ''}{fd[2]})"
            self.screen.blit(self.font_sm.render(text, True, C_WHITE), (px + 20, by + 6))

    def _render_heal(self):
        px, py, pw, ph = self._render_overlay("💊 治疗", [])
        p = self.player

        # 身体状态
        y = py
        for bp in BODY_PARTS:
            body = p.body[bp]
            r = body.ratio
            c = C_GREEN if r > 0.6 else (C_YELLOW if r > 0.3 else C_RED)
            if body.crippled:
                c = (80, 80, 80)
            eff = body.get_status()
            text = f"{bp}: {body.hp}/{body.max_hp} {eff}"
            self.screen.blit(self.font_sm.render(text, True, c), (px + 20, y))
            y += 18

        y += 10
        self.screen.blit(self.font_md.render("可用治疗:", True, C_YELLOW), (px + 20, y))
        y += 24

        actions = []
        if p.has_item("bandage"):
            bleeding = [bp for bp in BODY_PARTS if p.body[bp].bleeding]
            if bleeding:
                actions.append(("bandage", f"1. 绷带 → 止血 ({', '.join(bleeding)})"))
        if p.has_item("splint"):
            fractured = [bp for bp in BODY_PARTS if p.body[bp].fractured]
            if fractured:
                actions.append(("splint", f"2. 夹板 → 固定 ({', '.join(fractured)})"))
        if p.has_item("poultice"):
            actions.append(("poultice", "3. 药膏 → 抗感染/回血"))
        if p.has_item("antibiotic"):
            actions.append(("antibiotic", "4. 抗生素 → 根治感染/疾病"))

        if not actions:
            self.screen.blit(self.font_sm.render("没有可用的治疗手段", True, C_GRAY), (px + 40, y))
        for i, (act, text) in enumerate(actions):
            self.screen.blit(self.font_sm.render(text, True, C_WHITE), (px + 40, y))
            y += 20

    def _render_status(self):
        self.screen.fill((15, 15, 25))
        p = self.player
        self.screen.blit(self.font_xl.render("📊 详细状态", True, C_WHITE),
                         (WIDTH // 2 - 80, 20))

        x1, y = 60, 70
        attrs = [
            ("等级", f"Lv.{p.level} XP:{p.xp}/{p.xp_next}"),
            ("攻击力", str(p.get_attack())),
            ("防御力", str(p.get_defense())),
            ("武器", WEAPON_DATA[p.weapon][0] if p.weapon in WEAPON_DATA else "空手"),
            ("防具", ITEMS[p.armor][0] if p.armor else "无"),
            ("", ""),
            ("饱食度", f"{p.hunger:.1f}/100"),
            ("水分", f"{p.thirst:.1f}/100"),
            ("体力", f"{p.stamina:.1f}/100"),
            ("精神值", f"{p.mental:.1f}/100"),
            ("体温", f"{p.body_temp:.1f}°C"),
            ("", ""),
            ("存活天数", f"第{self.game_day}天"),
            ("击杀野兽", str(p.kills)),
            ("步数", str(p.steps)),
        ]
        for label, val in attrs:
            if not label:
                y += 8
                continue
            self.screen.blit(self.font_md.render(f"{label}:", True, C_GRAY), (x1, y))
            self.screen.blit(self.font_md.render(val, True, C_WHITE), (x1 + 100, y))
            y += 24

        x2 = WIDTH // 2 + 40
        y = 70
        self.screen.blit(self.font_lg.render("肢体状态", True, C_WHITE), (x2, y))
        y += 30
        for bp in BODY_PARTS:
            body = p.body[bp]
            r = body.ratio
            bar_w = 150
            c = C_GREEN if r > 0.6 else (C_YELLOW if r > 0.3 else C_RED)
            if body.crippled:
                c = (60, 60, 60)
            self.screen.blit(self.font_md.render(bp, True, C_WHITE), (x2, y))
            pygame.draw.rect(self.screen, (40, 40, 40), (x2 + 50, y + 4, bar_w, 14))
            pygame.draw.rect(self.screen, c, (x2 + 50, y + 4, int(bar_w * r), 14))
            self.screen.blit(self.font_sm.render(f"{body.hp}/{body.max_hp}", True, C_WHITE),
                             (x2 + 50 + bar_w + 8, y + 4))
            eff = body.get_status()
            if eff != "正常":
                self.screen.blit(self.font_sm.render(eff, True, C_RED), (x2 + 60, y + 20))
                y += 12
            y += 26

        if p.poisoned > 0:
            self.screen.blit(self.font_md.render(f"☠ 中毒: {int(p.poisoned)}分钟", True, (150, 255, 50)),
                             (x2, y))
            y += 24
        if p.sick > 0:
            self.screen.blit(
                self.font_md.render(f"🤒 {p.disease_name}: {int(p.sick)}分钟", True, (255, 150, 50)),
                (x2, y))

        self.screen.blit(self.font_sm.render("[ESC] 关闭", True, C_GRAY),
                         (WIDTH // 2 - 40, HEIGHT - 40))

    def _render_combat(self):
        self.screen.fill((25, 15, 15))
        b = self.combat_beast
        p = self.player
        if not b:
            return

        # 标题
        self.screen.blit(self.font_xl.render(f"⚔ 战斗: {b.name}", True, C_RED),
                         (WIDTH // 2 - 100, 20))

        # 野兽信息
        bar_w = 300
        bx = WIDTH // 2 - bar_w // 2
        hp_r = b.hp / b.max_hp
        pygame.draw.rect(self.screen, (40, 0, 0), (bx, 75, bar_w, 18))
        pygame.draw.rect(self.screen, C_RED, (bx, 75, int(bar_w * hp_r), 18))
        self.screen.blit(self.font_md.render(f"HP:{b.hp}/{b.max_hp} ATK:{b.atk} DEF:{b.defense}", True, C_WHITE),
                         (bx, 96))

        # 野兽图像
        pygame.draw.circle(self.screen, b.color, (WIDTH // 2, 155), b.size * 2)
        pygame.draw.circle(self.screen, C_WHITE, (WIDTH // 2 - 8, 148), 5)
        pygame.draw.circle(self.screen, C_WHITE, (WIDTH // 2 + 8, 148), 5)
        pygame.draw.circle(self.screen, C_BLACK, (WIDTH // 2 - 8, 148), 3)
        pygame.draw.circle(self.screen, C_BLACK, (WIDTH // 2 + 8, 148), 3)

        # 玩家肢体
        self.screen.blit(self.font_md.render("你的身体:", True, C_WHITE), (50, 210))
        y = 234
        for bp in BODY_PARTS:
            body = p.body[bp]
            r = body.ratio
            c = C_GREEN if r > 0.6 else (C_YELLOW if r > 0.3 else C_RED)
            if body.crippled:
                c = (80, 80, 80)
            eff = body.get_status()
            text = f"{bp}: {body.hp}/{body.max_hp} {eff}"
            self.screen.blit(self.font_sm.render(text, True, c), (70, y))
            y += 18

        # 战斗日志
        self.screen.blit(self.font_md.render("战斗日志:", True, C_YELLOW), (50, 380))
        for i, msg in enumerate(self.combat_log[-10:]):
            self.screen.blit(self.font_sm.render(msg, True, C_WHITE), (70, 402 + i * 18))

        # 操作按钮
        btn_y = HEIGHT - 60
        btns = [("[A] 攻击", (200, 60, 60)), ("[H] 用药", (60, 180, 60)), ("[F] 逃跑", (60, 60, 200))]
        for i, (text, color) in enumerate(btns):
            bx = WIDTH // 2 - 200 + i * 160
            pygame.draw.rect(self.screen, color, (bx, btn_y, 130, 36))
            pygame.draw.rect(self.screen, C_WHITE, (bx, btn_y, 130, 36), 2)
            self.screen.blit(self.font_md.render(text, True, C_WHITE), (bx + 15, btn_y + 8))

        self.screen.blit(
            self.font_md.render(f"ATK:{p.get_attack()} DEF:{p.get_defense()} Lv.{p.level}", True, C_YELLOW),
            (WIDTH - 250, btn_y + 8))

    def _render_gameover(self):
        self.screen.fill((10, 5, 5))
        p = self.player

        self.screen.blit(self.font_title.render("你 死 了", True, C_RED),
                         (WIDTH // 2 - 100, 60))

        cause = "未知原因"
        if p.body[BP_TORSO].hp <= 0:
            cause = "躯干致命伤"
        if p.hunger <= 0 and p.thirst <= 0:
            cause = "饥饿与脱水"
        elif p.hunger <= 0:
            cause = "饥饿"
        elif p.thirst <= 0:
            cause = "脱水"
        if p.mental <= 0:
            cause = "精神崩溃"
        if p.body_temp < 32:
            cause = "冻死"

        self.screen.blit(self.font_lg.render(f"死因: {cause}", True, C_ORANGE),
                         (WIDTH // 2 - 80, 130))

        stats = [
            f"存活天数: {p.days}",
            f"等级: Lv.{p.level}",
            f"击杀野兽: {p.kills}",
            f"步数: {p.steps}",
        ]
        score = p.days * 100 + p.kills * 50 + p.level * 30 + p.steps
        grade = 'F' if score < 200 else 'D' if score < 500 else 'C' if score < 1000 else 'B' if score < 2000 else 'A' if score < 4000 else 'S'

        y = 200
        for s in stats:
            self.screen.blit(self.font_md.render(s, True, C_WHITE), (WIDTH // 2 - 80, y))
            y += 30

        self.screen.blit(self.font_xl.render(f"总评分: {score}  [{grade}]", True, C_YELLOW),
                         (WIDTH // 2 - 120, y + 20))
        self.screen.blit(self.font_md.render("[R] 重新开始  [Q] 退出", True, C_GRAY),
                         (WIDTH // 2 - 100, HEIGHT - 60))

    def _render_help(self):
        self.screen.fill((15, 20, 30))
        self.screen.blit(self.font_xl.render("📖 操作指南", True, C_WHITE),
                         (WIDTH // 2 - 80, 20))

        x1, y = 80, 80
        sections = [
            ("移动", [
                "WASD / 方向键 - 移动角色",
                "E - 互动(砍树/采矿/采集/取水/烹饪)",
            ]),
            ("菜单", [
                "I - 背包  C - 制作  B - 建造",
                "F - 进食  H - 治疗  M - 状态",
                "F1 - 帮助  ESC - 关闭/暂停",
            ]),
            ("战斗", [
                "靠近野兽自动进入战斗",
                "A-攻击 H-用药 F-逃跑",
                "夜间野兽更强 逃跑更难",
            ]),
            ("生存要点", [
                "饱食/水分持续下降 归零掉血",
                "肢体受伤→流血→感染→残废",
                "躯干HP归零=死亡",
                "夜间无营火→精神崩溃",
                "冬季暴雪→冻伤 夏季→中暑",
                "脏水/生肉→中毒/疾病",
                "食物会腐烂 尽快烤熟保存",
            ]),
        ]
        for section, lines in sections:
            self.screen.blit(self.font_md.render(f"【{section}】", True, C_YELLOW), (x1, y))
            y += 26
            for line in lines:
                self.screen.blit(self.font_sm.render(f"  {line}", True, C_WHITE), (x1, y))
                y += 20
            y += 12

        self.screen.blit(self.font_sm.render("[ESC] 关闭", True, C_GRAY),
                         (WIDTH // 2 - 40, HEIGHT - 40))

    # ─── 主循环 ──────────────────────────────────
    def run(self):
        last_time = time.time()
        move_timer = 0

        while self.running:
            dt = time.time() - last_time
            last_time = time.time()

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

                if event.type == pygame.KEYDOWN:
                    if self.state == "gameover":
                        if event.key == pygame.K_r:
                            self.__init__()
                        elif event.key == pygame.K_q:
                            self.running = False
                        continue

                    if self.state == "combat":
                        if event.key == pygame.K_a:
                            self.combat_attack()
                        elif event.key == pygame.K_h:
                            self.combat_heal()
                        elif event.key == pygame.K_f:
                            self.combat_flee()
                        continue

                    if self.state == "world":
                        if event.key == pygame.K_e:
                            self.interact()
                        elif event.key == pygame.K_i:
                            self.state = "inventory"
                        elif event.key == pygame.K_c:
                            self.state = "craft"
                            self.scroll = 0
                        elif event.key == pygame.K_b:
                            self.state = "build"
                        elif event.key == pygame.K_f:
                            self.state = "eat"
                        elif event.key == pygame.K_h:
                            self.state = "heal"
                        elif event.key == pygame.K_m:
                            self.state = "status"
                        elif event.key == pygame.K_F1:
                            self.state = "help"
                        elif event.key == pygame.K_ESCAPE:
                            pass
                        continue

                    # 菜单中
                    if event.key == pygame.K_ESCAPE:
                        self.state = "world"
                        continue

                    if self.state == "craft":
                        if event.key == pygame.K_UP:
                            self.scroll = max(0, self.scroll - 1)
                        elif event.key == pygame.K_DOWN:
                            self.scroll = min(max(0, len(RECIPES) - 14), self.scroll + 1)
                        elif pygame.K_1 <= event.key <= pygame.K_9:
                            self.craft(self.scroll + (event.key - pygame.K_1))
                        elif event.key == pygame.K_0:
                            self.craft(self.scroll + 9)
                        elif event.key == pygame.K_RETURN:
                            self.craft(self.scroll)

                    elif self.state == "build":
                        if pygame.K_1 <= event.key <= pygame.K_4:
                            builds = ["campfire", "shelter", "wall", "trap"]
                            self.build(builds[event.key - pygame.K_1])

                    elif self.state == "eat":
                        if pygame.K_1 <= event.key <= pygame.K_9:
                            foods = [k for k in self.player.inventory if k in FOOD_DATA]
                            idx = event.key - pygame.K_1
                            if idx < len(foods):
                                self.eat(foods[idx])

                    elif self.state == "heal":
                        if event.key == pygame.K_1:
                            self.heal("bandage")
                        elif event.key == pygame.K_2:
                            self.heal("splint")
                        elif event.key == pygame.K_3:
                            self.heal("poultice")
                        elif event.key == pygame.K_4:
                            self.heal("antibiotic")

            # 移动
            if self.state == "world":
                move_timer += dt
                if move_timer >= 0.15:
                    move_timer = 0
                    keys = pygame.key.get_pressed()
                    dx, dy = 0, 0
                    if keys[pygame.K_w] or keys[pygame.K_UP]:
                        dy = -1
                    if keys[pygame.K_s] or keys[pygame.K_DOWN]:
                        dy = 1
                    if keys[pygame.K_a] or keys[pygame.K_LEFT]:
                        dx = -1
                    if keys[pygame.K_d] or keys[pygame.K_RIGHT]:
                        dx = 1

                    if dx != 0 or dy != 0:
                        p = self.player
                        nx, ny = p.x + dx, p.y + dy
                        if 0 <= nx < MAP_W and 0 <= ny < MAP_H:
                            tile = self.game_map[ny][nx]
                            if TILE_WALKABLE.get(tile, False) and (nx, ny) not in self.walls:
                                p.x = nx
                                p.y = ny
                                p.px = nx * TILE_SIZE + TILE_SIZE // 2
                                p.py = ny * TILE_SIZE + TILE_SIZE // 2
                                p.steps += 1
                                p.stamina = max(0, p.stamina - 0.3)
                                self._reveal_area(nx, ny, 5 if not self.is_night else 3)

                                # 遭遇野兽
                                for beast in self.beasts:
                                    if beast.alive and beast.x == nx and beast.y == ny:
                                        self.start_combat(beast)
                                        break

                # 更新野兽AI
                for beast in self.beasts:
                    if beast.alive:
                        beast.ai_move(self.game_map, self.player, self.is_night)
                        if beast.x == self.player.x and beast.y == self.player.y:
                            self.start_combat(beast)

            # 更新时间
            if self.state not in ("gameover", "help"):
                self.update_time(dt)

            # 营火燃料
            for key in list(self.campfires.keys()):
                if self.campfires[key] > 0:
                    self.campfires[key] -= dt / SECONDS_PER_GAME_MINUTE
                    if self.campfires[key] <= 0:
                        self.campfires[key] = 0

            # 死亡检查
            if not self.player.is_alive() and self.state != "gameover":
                self.state = "gameover"

            self.render()
            self.clock.tick(FPS)

        pygame.quit()
        sys.exit()


if __name__ == "__main__":
    game = Game()
    game.run()
