import pygame
import pygame.gfxdraw
import random
import sys
import math
from enum import Enum
from collections import deque

# ==================== 初始化 ====================
pygame.init()
WINDOW_WIDTH, WINDOW_HEIGHT = 1280, 720
FPS = 60

# ==================== 颜色（已调亮） ====================
COLORS = {
    'bg': (60, 60, 70),          # 原 (25,25,35) → 明显提亮
    'wall_l0': (205, 180, 145), 'floor_l0': (235, 215, 185),
    'wall_l1': (150, 150, 160), 'floor_l1': (90, 90, 100),
    'wall_l2': (120, 90, 80),   'floor_l2': (80, 65, 60),
    'wall_l3': (100, 130, 150), 'floor_l3': (70, 100, 120),
    'wall_l4': (160, 140, 120), 'floor_l4': (200, 180, 160),
    'player': (70, 150, 230), 'player_glow': (120, 200, 255),
    'exit': (60, 200, 80), 'exit_glow': (100, 255, 150),
    'exit_danger': (200, 60, 60), 'exit_danger_glow': (255, 100, 100),
    'item': (255, 220, 100), 'item_glow': (255, 240, 150),
    'monster': (200, 50, 50), 'monster_eye': (255, 255, 200),
    'ui_bg': (10, 10, 15, 200), 'text': (230, 220, 200),
    'text_dark': (160, 150, 140), 'danger': (230, 80, 80),
    'sanity_bar': (80, 200, 80), 'sanity_bar_low': (230, 80, 80),
    'stamina_bar': (100, 180, 255),
}

# ==================== 常量 ====================
MAP_W, MAP_H = 60, 60
CELL_SIZE = 32
VIEW_RADIUS = 12
TORCH_RADIUS = 5
TORCH_DURATION = 300
SANITY_MAX = 100
SANITY_DRAIN = 0.012
MOVE_SPEED = 0.12
MOVE_SPEED_SPRINT = 0.22
SPRINT_COST = 0.3
STAMINA_MAX = 100
STAMINA_REGEN = 0.08
HOLD_DELAY = 10
MOVE_COOLDOWN = 2

# ==================== 字体 ====================
def get_font(size):
    return pygame.font.Font(None, size)

font_small = get_font(20)
font_medium = get_font(28)
font_large = get_font(42)
font_title = get_font(56)

# ==================== 辅助 ====================
def dist(a, b):
    return math.hypot(a[0]-b[0], a[1]-b[1])

def clamp(v, lo, hi):
    return max(lo, min(hi, v))

# ==================== 纹理生成 ====================
def make_texture(base_color, variation=20):
    surf = pygame.Surface((CELL_SIZE, CELL_SIZE))
    surf.fill(base_color)
    for _ in range(CELL_SIZE * CELL_SIZE // 6):
        x = random.randint(0, CELL_SIZE-1)
        y = random.randint(0, CELL_SIZE-1)
        offset = random.randint(-variation//2, variation//2)
        c = tuple(clamp(base_color[i]+offset, 0, 255) for i in range(3))
        surf.set_at((x, y), c)
    for i in range(0, CELL_SIZE, 4):
        pygame.draw.line(surf, (base_color[0]-10, base_color[1]-10, base_color[2]-10), (0, i), (CELL_SIZE, i), 1)
    return surf

TEXTURE_CACHE = {}
def get_texture(theme_key, tile_type):
    key = (theme_key, tile_type)
    if key in TEXTURE_CACHE:
        return TEXTURE_CACHE[key]
    walls = [(205,180,145), (150,150,160), (120,90,80), (100,130,150), (160,140,120)]
    floors = [(235,215,185), (90,90,100), (80,65,60), (70,100,120), (200,180,160)]
    wall_c = walls[theme_key % len(walls)]
    floor_c = floors[theme_key % len(floors)]
    tex = make_texture(wall_c, 15) if tile_type == 'wall' else make_texture(floor_c, 20)
    TEXTURE_CACHE[key] = tex
    return tex

# ==================== 房间类 ====================
class Room:
    def __init__(self, x, y, w, h):
        self.x, self.y, self.w, self.h = x, y, w, h
        self.cx, self.cy = x + w//2, y + h//2

# ==================== 层级生成器 ====================
def generate_level0(w, h):
    dungeon = [[1 for _ in range(w)] for _ in range(h)]
    rooms = []
    for _ in range(60):
        rw, rh = random.randint(4, 11), random.randint(4, 11)
        rx, ry = random.randint(1, w-rw-1), random.randint(1, h-rh-1)
        new = Room(rx, ry, rw, rh)
        if not any(rx < r.x+r.w+1 and rx+rw+1 > r.x and ry < r.y+r.h+1 and ry+rh+1 > r.y for r in rooms):
            rooms.append(new)
            for iy in range(ry, ry+rh):
                for ix in range(rx, rx+rw):
                    dungeon[iy][ix] = 0
    for i in range(len(rooms)-1):
        x1,y1 = rooms[i].cx, rooms[i].cy
        x2,y2 = rooms[i+1].cx, rooms[i+1].cy
        for x in range(min(x1,x2), max(x1,x2)+1): dungeon[y1][x] = 0
        for y in range(min(y1,y2), max(y1,y2)+1): dungeon[y][x2] = 0
    furniture = []
    for r in rooms:
        if random.random() < 0.3:
            fx, fy = r.x+random.randint(1,r.w-2), r.y+random.randint(1,r.h-2)
            if dungeon[fy][fx] == 0:
                dungeon[fy][fx] = 2; furniture.append((fx, fy))
    return dungeon, rooms, furniture

def generate_level1(w, h):
    dungeon = [[1 for _ in range(w)] for _ in range(h)]
    for y in range(2, h-2):
        for x in range(2, w-2):
            if (x % 3 != 0) or (y % 3 != 0):
                dungeon[y][x] = 0
    for _ in range(100):
        x, y = random.randint(3, w-3), random.randint(3, h-3)
        if random.random() < 0.6:
            dungeon[y][x] = 0
    furniture = []
    for y in range(3, h-3, 3):
        for x in range(3, w-3, 3):
            if dungeon[y][x] == 0 and random.random() < 0.5:
                dungeon[y][x] = 2; furniture.append((x, y))
    start, exit = Room(2,2,4,4), Room(w-6,h-6,4,4)
    for iy in range(start.y, start.y+start.h):
        for ix in range(start.x, start.x+start.w): dungeon[iy][ix] = 0
    for iy in range(exit.y, exit.y+exit.h):
        for ix in range(exit.x, exit.x+exit.w): dungeon[iy][ix] = 0
    return dungeon, [start, exit], furniture

def generate_level2(w, h):
    dungeon = [[1 for _ in range(w)] for _ in range(h)]
    def split(x, y, w, h):
        if w < 4 or h < 4: return
        if w > h:
            cut = random.randint(x+2, x+w-2)
            for i in range(y, y+h): dungeon[i][cut] = dungeon[i][cut-1] = 0
            split(x, y, cut-x, h); split(cut, y, w-(cut-x), h)
        else:
            cut = random.randint(y+2, y+h-2)
            for i in range(x, x+w): dungeon[cut][i] = dungeon[cut-1][i] = 0
            split(x, y, w, cut-y); split(x, cut, w, h-(cut-y))
    split(2,2,w-4,h-4)
    for i in range(2,w-2): dungeon[2][i] = dungeon[h-3][i] = 0
    for i in range(2,h-2): dungeon[i][2] = dungeon[i][w-3] = 0
    start, exit = (3,3), (w-4,h-4)
    dungeon[start[1]][start[0]] = dungeon[exit[1]][exit[0]] = 0
    return dungeon, [Room(start[0]-1,start[1]-1,3,3), Room(exit[0]-1,exit[1]-1,3,3)], []

def generate_level3(w, h):
    dungeon = [[1 for _ in range(w)] for _ in range(h)]
    rooms = []
    for _ in range(30):
        rw, rh = random.randint(6, 12), random.randint(6, 12)
        rx, ry = random.randint(2, w-rw-2), random.randint(2, h-rh-2)
        new = Room(rx, ry, rw, rh)
        if not any(rx < r.x+r.w+2 and rx+rw+2 > r.x and ry < r.y+r.h+2 and ry+rh+2 > r.y for r in rooms):
            rooms.append(new)
            for iy in range(ry, ry+rh):
                for ix in range(rx, rx+rw):
                    dungeon[iy][ix] = 0
    for i in range(len(rooms)-1):
        x1,y1 = rooms[i].cx, rooms[i].cy
        x2,y2 = rooms[i+1].cx, rooms[i+1].cy
        for x in range(min(x1,x2), max(x1,x2)+1): dungeon[y1][x] = 0
        for y in range(min(y1,y2), max(y1,y2)+1): dungeon[y][x2] = 0
    furniture = []
    for r in rooms:
        if random.random() < 0.4:
            fx, fy = r.x+random.randint(1,r.w-2), r.y+random.randint(1,r.h-2)
            if dungeon[fy][fx] == 0:
                dungeon[fy][fx] = 2; furniture.append((fx, fy))
    return dungeon, rooms, furniture

def generate_level4(w, h):
    dungeon = [[1 for _ in range(w)] for _ in range(h)]
    for y in range(2, h-2, 2):
        for x in range(2, w-2, 2):
            if random.random() < 0.7:
                dungeon[y][x] = 0
                if random.random() < 0.5: dungeon[y+1][x] = 0
                if random.random() < 0.5: dungeon[y][x+1] = 0
    for i in range(2, w-2): dungeon[2][i] = dungeon[h-3][i] = 0
    for i in range(2, h-2): dungeon[i][2] = dungeon[i][w-3] = 0
    start, exit = (3,3), (w-4,h-4)
    dungeon[start[1]][start[0]] = dungeon[exit[1]][exit[0]] = 0
    return dungeon, [Room(start[0]-1,start[1]-1,3,3), Room(exit[0]-1,exit[1]-1,3,3)], []

LEVEL_GENERATORS = [generate_level0, generate_level1, generate_level2, generate_level3, generate_level4]

# ==================== 物品、怪物、玩家 ====================
class ItemType(Enum):
    KEY=1; NOTE=2; ALMOND_WATER=3; TORCH=4; BANDAGE=5; PASSWORD=6; LEVER=7; BATTERY=8; MEDKIT=9; MAP=10

class Item:
    def __init__(self,x,y,typ,extra=""):
        self.x,self.y,self.type,self.extra,self.collected = x,y,typ,extra,False
    @staticmethod
    def get_color(typ):
        return {(1):(200,180,100),(2):(220,220,180),(3):(180,220,255),(4):(255,220,100),
                (5):(255,180,180),(6):(200,200,200),(7):(180,180,220),(8):(255,200,50),
                (9):(255,100,100),(10):(200,200,255)}.get(typ,(200,200,200))
    @staticmethod
    def get_emoji(typ):
        return {1:"🔑",2:"📄",3:"🧊",4:"🔦",5:"🩹",6:"🔐",7:"🎛️",8:"🔋",9:"💊",10:"🗺️"}.get(typ,"❓")

MONSTER_TYPES = ["hunter","smiler","chaser","stalker","brute"]
class Monster:
    def __init__(self,x,y,mtype="hunter"):
        self.x,self.y,self.type,self.state,self.move_timer,self.direction,self.attack_cooldown,self.health = x,y,mtype,"idle",0,random.choice([(0,1),(0,-1),(1,0),(-1,0)]),0,1
        if mtype=="hunter":   self.detect_range,self.speed,self.chase_speed,self.damage,self.attack_range,self.color=6,0.02,0.07,12,1.2,(180,50,50)
        elif mtype=="smiler": self.detect_range,self.speed,self.chase_speed,self.damage,self.attack_range,self.color,self.invisible=4,0.015,0.04,10,1.0,(255,200,200),True
        elif mtype=="chaser": self.detect_range,self.speed,self.chase_speed,self.damage,self.attack_range,self.color=12,0.05,0.12,15,0.8,(200,0,0)
        elif mtype=="stalker":self.detect_range,self.speed,self.chase_speed,self.damage,self.attack_range,self.color=3,0.01,0.03,8,1.0,(80,80,80)
        elif mtype=="brute":  self.detect_range,self.speed,self.chase_speed,self.damage,self.attack_range,self.color,self.health=5,0.008,0.02,25,1.5,(150,50,150),3
        self.speed*=random.uniform(0.9,1.1); self.chase_speed*=random.uniform(0.9,1.1)
    def update(self, player_pos, dungeon, player):
        dx,dy = player_pos[0]-self.x, player_pos[1]-self.y
        d=math.hypot(dx,dy)
        see = self.has_line_of_sight(player_pos, dungeon)
        if self.type=="smiler" and player.sanity>60: see=False
        self.state = "chase" if d<self.detect_range and see else "idle"
        if self.attack_cooldown>0: self.attack_cooldown-=1
        if d<self.attack_range and self.attack_cooldown==0:
            self.attack_cooldown=30; return True
        if self.state=="idle":
            self.move_timer-=1
            if self.move_timer<=0:
                self.direction=random.choice([(0,1),(0,-1),(1,0),(-1,0)])
                self.move_timer=random.randint(20,80)
            self.try_move(self.direction[0], self.direction[1], dungeon)
        else:
            if d>0.5:
                step_x, step_y = dx/d*self.chase_speed, dy/d*self.chase_speed
                self.try_move(step_x,0,dungeon); self.try_move(0,step_y,dungeon)
        return False
    def try_move(self,dx,dy,dungeon):
        nx,ny = self.x+dx, self.y+dy
        gx,gy = int(round(nx)), int(round(ny))
        if 0<=gx<len(dungeon[0]) and 0<=gy<len(dungeon) and dungeon[gy][gx]==0:
            self.x,self.y = nx,ny; return True
        return False
    def has_line_of_sight(self, target, dungeon):
        x0,y0 = int(round(self.x)), int(round(self.y))
        x1,y1 = int(round(target[0])), int(round(target[1]))
        dx,dy = abs(x1-x0), -abs(y1-y0)
        sx = 1 if x0<x1 else -1; sy = 1 if y0<y1 else -1
        err = dx+dy
        while True:
            if (x0,y0)==(x1,y1): return True
            if dungeon[y0][x0] in (1,2): return False
            e2 = 2*err
            if e2>=dy: err+=dy; x0+=sx
            if e2<=dx: err+=dx; y0+=sy
    def take_damage(self):
        self.health-=1; return self.health<=0

class Player:
    def __init__(self,x,y):
        self.x,self.y,self.grid_x,self.grid_y = float(x),float(y),x,y
        self.moving,self.move_progress,self.move_from,self.move_to = False,0.0,(0.0,0.0),(0.0,0.0)
        self.move_speed,self.move_queue,self.hold_timer,self.move_cooldown = MOVE_SPEED,deque(),0,0
        self.sanity,self.health,self.max_health,self.stamina,self.max_stamina = SANITY_MAX,100,100,STAMINA_MAX,STAMINA_MAX
        self.inventory,self.inv_limit,self.torch_active,self.torch_timer = [],8,False,0
        self.steps,self.attack_cooldown,self.hit_flash=0,0,0
        self.has_key,self.found_password,self.password,self.sprinting=False,False,"",False
        self.levers_pulled,self.monsters_killed,self.items_collected,self.levels_completed=0,0,0,0
    def start_move(self,dx,dy,dungeon,sprint=False):
        if self.moving or self.move_cooldown>0:
            self.move_queue.append((dx,dy)); return
        nx,ny = self.grid_x+dx, self.grid_y+dy
        if 0<=nx<len(dungeon[0]) and 0<=ny<len(dungeon):
            cell = dungeon[int(ny)][int(nx)]
            if cell==0:
                self.move_from=(self.x,self.y); self.move_to=(float(nx),float(ny))
                self.moving=True; self.move_progress=0.0
                self.grid_x,self.grid_y=int(nx),int(ny)
                self.move_speed = MOVE_SPEED_SPRINT if sprint else MOVE_SPEED
                self.steps+=1; self.move_cooldown=MOVE_COOLDOWN; return True
            elif cell==3: return "locked"
            elif cell==4: return "password"
            elif cell==5: return "lever"
        return False
    def update(self,dungeon):
        if self.move_cooldown>0: self.move_cooldown-=1
        if self.moving:
            self.move_progress+=self.move_speed
            if self.move_progress>=1.0:
                self.move_progress=1.0; self.x,self.y=self.move_to; self.moving=False
                if self.move_queue:
                    dx,dy=self.move_queue.popleft(); self.start_move(dx,dy,dungeon,self.sprinting)
            else:
                t=self.move_progress; t=t*t*(3-2*t)
                self.x = self.move_from[0]+(self.move_to[0]-self.move_from[0])*t
                self.y = self.move_from[1]+(self.move_to[1]-self.move_from[1])*t
        if not self.sprinting: self.stamina=min(self.max_stamina,self.stamina+STAMINA_REGEN)
        self.sanity=max(0,self.sanity-SANITY_DRAIN*(0.8+0.2*(1-self.sanity/SANITY_MAX)))
        if self.torch_active:
            self.torch_timer-=1
            if self.torch_timer<=0: self.torch_active=False
        if self.attack_cooldown>0: self.attack_cooldown-=1
        if self.hit_flash>0: self.hit_flash-=1
    def get_pos(self): return (self.x,self.y)
    def get_cell(self): return (int(round(self.grid_x)), int(round(self.grid_y)))
    def use_item(self,idx):
        if idx<len(self.inventory):
            typ = self.inventory.pop(idx)
            if typ==ItemType.ALMOND_WATER: self.sanity=min(SANITY_MAX,self.sanity+25)
            elif typ==ItemType.TORCH: self.torch_active=True; self.torch_timer=TORCH_DURATION
            elif typ==ItemType.BANDAGE: self.health=min(self.max_health,self.health+20)
            elif typ==ItemType.KEY: self.has_key=True
            elif typ==ItemType.PASSWORD: self.found_password=True
            elif typ==ItemType.LEVER: self.levers_pulled+=1
            elif typ==ItemType.BATTERY:
                if self.torch_active: self.torch_timer=min(TORCH_DURATION,self.torch_timer+150)
                else: self.torch_active=True; self.torch_timer=150
            elif typ==ItemType.MEDKIT: self.health=min(self.max_health,self.health+40)
            elif typ==ItemType.MAP: pass
            self.items_collected+=1; return True
        return False
    def pick_item(self,items):
        cx,cy=self.get_cell()
        for item in items:
            if not item.collected and abs(item.x-cx)<=1 and abs(item.y-cy)<=1:
                if len(self.inventory)<self.inv_limit:
                    if item.type==ItemType.KEY:
                        self.has_key=True; item.collected=True; self.items_collected+=1; return True
                    if item.type==ItemType.PASSWORD:
                        self.found_password=True; self.password=item.extra; item.collected=True; self.items_collected+=1; return True
                    if item.type==ItemType.LEVER:
                        self.levers_pulled+=1; item.collected=True; self.items_collected+=1; return True
                    self.inventory.append(item.type); item.collected=True; self.items_collected+=1; return True
        return False

# ==================== 层级类（带分支出口） ====================
class Level:
    def __init__(self, level_id, previous_path=None):
        self.id = level_id
        self.theme_key = level_id % 5
        gen = LEVEL_GENERATORS[self.theme_key]
        self.dungeon, self.rooms, self.furniture = gen(MAP_W, MAP_H)
        self.width, self.height = len(self.dungeon[0]), len(self.dungeon)
        if self.rooms:
            self.start = (self.rooms[0].cx, self.rooms[0].cy)
            self.exit = (self.rooms[-1].cx, self.rooms[-1].cy)
        else:
            self.start, self.exit = (2,2), (MAP_W-3, MAP_H-3)
            self.dungeon[self.start[1]][self.start[0]] = 0
            self.dungeon[self.exit[1]][self.exit[0]] = 0
        # 生成两个出口传送门（安全/危险）
        self.exit_portals = []
        for i, (dx,dy) in enumerate([(2,0),(-2,0)]):
            px, py = self.exit[0]+dx, self.exit[1]+dy
            if 0<=px<self.width and 0<=py<self.height and self.dungeon[py][px]==0:
                self.exit_portals.append({'pos':(px,py), 'type':'safe' if i==0 else 'danger'})
        if len(self.exit_portals)<2:
            for (dx,dy) in [(0,2),(0,-2)]:
                px, py = self.exit[0]+dx, self.exit[1]+dy
                if 0<=px<self.width and 0<=py<self.height and self.dungeon[py][px]==0:
                    self.exit_portals.append({'pos':(px,py), 'type':'safe' if len(self.exit_portals)==0 else 'danger'})
                    break
        self.items, self.monsters = [], []
        self.locked_doors, self.password_doors, self.lever_doors = [], [], []
        self.chase_monster = None
        self.setup_level()

    def setup_level(self):
        base = [ItemType.ALMOND_WATER, ItemType.TORCH, ItemType.BANDAGE]
        if random.random()<0.3: base.append(ItemType.MEDKIT)
        if random.random()<0.2: base.append(ItemType.MAP)
        puzzle = self.id % 3
        if puzzle==0: base.append(ItemType.KEY); self.place_locked_door()
        elif puzzle==1: base.append(ItemType.LEVER); self.place_lever_door(1+(self.id%3))
        else: base.append(ItemType.PASSWORD); self.place_password_door()
        self.spawn_items(base, 12+self.id%6)
        mc = 2+(self.id%5)+(self.id//10)
        self.spawn_monsters(mc)
        if self.id>5 and random.random()<0.2: self.place_chase_sequence()

    def spawn_items(self, types, count):
        for _ in range(count):
            x,y = random.randint(2,self.width-3), random.randint(2,self.height-3)
            if self.dungeon[y][x]==0 and (x,y)!=self.start and (x,y)!=self.exit:
                typ=random.choice(types); extra=""
                if typ==ItemType.PASSWORD: extra=str(random.randint(1000,9999))
                elif typ==ItemType.NOTE: extra="Clue"
                self.items.append(Item(x,y,typ,extra))
    def spawn_monsters(self,count):
        avail = [t for t in MONSTER_TYPES if t!="brute" or self.id>=5]
        for _ in range(count):
            for _ in range(100):
                x,y=random.randint(2,self.width-3), random.randint(2,self.height-3)
                if self.dungeon[y][x]==0 and (x,y)!=self.start and (x,y)!=self.exit and dist((x,y),self.start)>6:
                    self.monsters.append(Monster(x,y,random.choice(avail))); break
    def place_locked_door(self):
        ex,ey=self.exit
        for dx,dy in [(1,0),(-1,0),(0,1),(0,-1)]:
            nx,ny=ex+dx,ey+dy
            if 0<=nx<self.width and 0<=ny<self.height and self.dungeon[ny][nx]==0:
                self.dungeon[ny][nx]=3; self.locked_doors.append((nx,ny)); break
    def place_lever_door(self, req):
        ex,ey=self.exit
        for dx,dy in [(1,0),(-1,0),(0,1),(0,-1)]:
            nx,ny=ex+dx,ey+dy
            if 0<=nx<self.width and 0<=ny<self.height and self.dungeon[ny][nx]==0:
                self.dungeon[ny][nx]=5; self.lever_doors.append((nx,ny,req)); break
    def place_password_door(self):
        ex,ey=self.exit
        for dx,dy in [(1,0),(-1,0),(0,1),(0,-1)]:
            nx,ny=ex+dx,ey+dy
            if 0<=nx<self.width and 0<=ny<self.height and self.dungeon[ny][nx]==0:
                self.dungeon[ny][nx]=4; self.password_doors.append((nx,ny)); break
    def place_chase_sequence(self):
        ex,ey=self.exit
        for _ in range(50):
            x,y=random.randint(ex-5,ex+5),random.randint(ey-5,ey+5)
            if 0<=x<self.width and 0<=y<self.height and self.dungeon[y][x]==0 and dist((x,y),self.start)>10:
                self.chase_monster=Monster(x,y,"chaser"); break
    def unlock_door(self,x,y,player):
        if (x,y) in self.locked_doors and player.has_key:
            self.dungeon[y][x]=0; self.locked_doors.remove((x,y)); return True
        return False
    def unlock_lever(self,x,y,player):
        for door in self.lever_doors:
            if door[0]==x and door[1]==y:
                if player.levers_pulled>=door[2]:
                    self.dungeon[y][x]=0; self.lever_doors.remove(door); return True
                return False
        return False
    def unlock_password(self,x,y,player,password_input):
        if (x,y) in self.password_doors and player.found_password and player.password==password_input:
            self.dungeon[y][x]=0; self.password_doors.remove((x,y)); return True
        return False
    def get_theme_colors(self):
        theme = self.theme_key
        walls = [(205,180,145),(150,150,160),(120,90,80),(100,130,150),(160,140,120)]
        floors = [(235,215,185),(90,90,100),(80,65,60),(70,100,120),(200,180,160)]
        return walls[theme], tuple(c-30 for c in walls[theme]), floors[theme]

# ==================== 故事管理器（分支逻辑与结局） ====================
class StoryManager:
    def __init__(self):
        self.path = []
        self.current_level_id = 0
        self.total_monsters_killed = 0
        self.total_items_collected = 0
        self.sanity_history = []

    def get_next_level(self, current_level, player, portal_type):
        lid = current_level.id
        self.path.append(lid)

        branch_map = {
            0: {'safe': 1, 'danger': 3},
            1: {'safe': 4, 'danger': 2},
            2: {'safe': 1, 'danger': 3},
            3: {'safe': 4, 'danger': 2},
            4: {'safe': -1, 'danger': 1},
        }
        if portal_type == 'safe':
            nxt = branch_map.get(lid, {}).get('safe', 1)
        else:
            nxt = branch_map.get(lid, {}).get('danger', 3)

        self.total_monsters_killed += player.monsters_killed
        self.total_items_collected += player.items_collected
        self.sanity_history.append(player.sanity)

        if nxt == -1:
            return None, 'escape'
        if player.sanity < 20 and len(self.path) >= 2:
            return None, 'madness'
        if self.total_monsters_killed >= 20 and len(self.path) >= 2:
            return None, 'slayer'
        if self.total_items_collected >= 30 and len(self.path) >= 2:
            return None, 'collector'

        return nxt, None

    def get_ending_text(self, ending_type, player):
        if ending_type == 'escape':
            return "You escaped the Backrooms! You are a true survivor."
        elif ending_type == 'madness':
            return "Your mind shattered in the darkness. You are now one of them."
        elif ending_type == 'slayer':
            return "You killed everything in your path, but lost yourself in the carnage."
        elif ending_type == 'collector':
            return "You hoarded treasures, but found only emptiness. Was it worth it?"
        else:
            return "Your fate remains uncertain. You wander forever."

# ==================== 游戏主类 ====================
class Game:
    def __init__(self):
        self.screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
        pygame.display.set_caption("Backrooms - Branching Paths")
        self.clock = pygame.time.Clock()
        self.running = True
        self.state = "playing"
        self.story = StoryManager()
        self.current_level_id = 0
        self.level = Level(self.current_level_id)
        self.player = Player(self.level.start[0], self.level.start[1])
        self.camera_x, self.camera_y = 0, 0
        self.flicker, self.whisper_timer, self.whisper_text = 0, 0, ""
        self.hallucination = False
        self.input_password, self.show_password_prompt, self.password_door_target = "", False, None
        self.keys_pressed = {}
        self.transition_timer, self.transition_text = 0, ""
        self.game_over_reason = ""
        self.ending_type = None
        self.vignette_surf = self.create_vignette()
        self.noise_surf = self.create_noise()

    def create_vignette(self):
        surf = pygame.Surface((WINDOW_WIDTH, WINDOW_HEIGHT), pygame.SRCALPHA)
        for r in range(0, max(WINDOW_WIDTH, WINDOW_HEIGHT)//2, 2):
            alpha = int(80 * (r / (max(WINDOW_WIDTH, WINDOW_HEIGHT)//2)))  # 整体降低晕影强度
            pygame.draw.circle(surf, (0,0,0,clamp(alpha,0,80)), (WINDOW_WIDTH//2, WINDOW_HEIGHT//2), r)
        return surf
    def create_noise(self):
        surf = pygame.Surface((WINDOW_WIDTH, WINDOW_HEIGHT))
        for _ in range(500):
            x,y = random.randint(0,WINDOW_WIDTH-1), random.randint(0,WINDOW_HEIGHT-1)
            c = random.randint(30,80)
            surf.set_at((x,y),(c,c,c))
        surf.set_alpha(20); return surf
    def reset_game(self):
        self.__init__()

    def handle_events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.running = False
            if event.type == pygame.KEYDOWN:
                if self.state != "playing":
                    if event.key == pygame.K_r: self.reset_game()
                    if event.key == pygame.K_ESCAPE: self.running = False
                    continue
                if self.show_password_prompt:
                    if event.key == pygame.K_RETURN:
                        if self.level.unlock_password(self.password_door_target[0], self.password_door_target[1], self.player, self.input_password):
                            self.show_password_prompt = False
                            self.whisper_text = "Password correct!"; self.whisper_timer=60
                        else:
                            self.whisper_text = "Wrong password!"; self.whisper_timer=60
                            self.input_password = ""
                    elif event.key == pygame.K_BACKSPACE:
                        self.input_password = self.input_password[:-1]
                    elif event.key == pygame.K_ESCAPE:
                        self.show_password_prompt = False
                    else:
                        if event.unicode.isdigit() and len(self.input_password)<8:
                            self.input_password += event.unicode
                    continue
                if event.key == pygame.K_e:
                    self.player.pick_item(self.level.items)
                if event.key == pygame.K_SPACE:
                    self.player_attack()
                if event.key in [pygame.K_1,pygame.K_2,pygame.K_3,pygame.K_4,pygame.K_5,pygame.K_6,pygame.K_7,pygame.K_8]:
                    self.player.use_item(event.key - pygame.K_1)
                if event.key in (pygame.K_UP,pygame.K_w): self.keys_pressed['up']=True
                if event.key in (pygame.K_DOWN,pygame.K_s): self.keys_pressed['down']=True
                if event.key in (pygame.K_LEFT,pygame.K_a): self.keys_pressed['left']=True
                if event.key in (pygame.K_RIGHT,pygame.K_d): self.keys_pressed['right']=True
            if event.type == pygame.KEYUP:
                if event.key in (pygame.K_UP,pygame.K_w): self.keys_pressed['up']=False
                if event.key in (pygame.K_DOWN,pygame.K_s): self.keys_pressed['down']=False
                if event.key in (pygame.K_LEFT,pygame.K_a): self.keys_pressed['left']=False
                if event.key in (pygame.K_RIGHT,pygame.K_d): self.keys_pressed['right']=False
                if event.key in (pygame.K_LSHIFT,pygame.K_RSHIFT): self.player.sprinting=False

    def player_attack(self):
        px,py = self.player.get_pos()
        for monster in self.level.monsters[:]:
            if dist((monster.x,monster.y),(px,py))<1.8:
                if monster.take_damage():
                    self.level.monsters.remove(monster)
                    self.player.monsters_killed += 1
                    self.whisper_text="Monster defeated!"; self.whisper_timer=30
                else:
                    self.whisper_text="Monster hit!"; self.whisper_timer=20
                return
        if self.level.chase_monster and dist((self.level.chase_monster.x,self.level.chase_monster.y),(px,py))<1.8:
            if self.level.chase_monster.take_damage():
                self.level.chase_monster=None
                self.player.monsters_killed+=1
                self.whisper_text="Chaser defeated!"; self.whisper_timer=30
            else:
                self.whisper_text="Chaser hit!"; self.whisper_timer=20

    def handle_movement(self):
        if self.state!="playing" or self.show_password_prompt or self.player.moving:
            return
        keys = pygame.key.get_pressed()
        sprint = keys[pygame.K_LSHIFT] or keys[pygame.K_RSHIFT]
        if sprint and self.player.stamina>5:
            self.player.sprinting=True; self.player.stamina=max(0,self.player.stamina-SPRINT_COST)
        else:
            self.player.sprinting=False
        dx,dy = 0,0
        if self.keys_pressed.get('up',False): dy=-1
        elif self.keys_pressed.get('down',False): dy=1
        elif self.keys_pressed.get('left',False): dx=-1
        elif self.keys_pressed.get('right',False): dx=1
        if dx==0 and dy==0:
            self.player.hold_timer=0; return
        if self.player.hold_timer==0:
            self.try_move(dx,dy,sprint); self.player.hold_timer=1
        elif self.player.hold_timer>=HOLD_DELAY:
            if self.player.move_cooldown==0:
                self.try_move(dx,dy,sprint)
            self.player.hold_timer=HOLD_DELAY
        else:
            self.player.hold_timer+=1

    def try_move(self,dx,dy,sprint):
        result = self.player.start_move(dx,dy,self.level.dungeon,sprint)
        if result=="locked":
            self.whisper_text="Need a key!"; self.whisper_timer=60
        elif result=="password":
            nx,ny = self.player.grid_x+dx, self.player.grid_y+dy
            self.show_password_prompt=True; self.password_door_target=(nx,ny); self.input_password=""
        elif result=="lever":
            nx,ny = self.player.grid_x+dx, self.player.grid_y+dy
            if self.level.unlock_lever(nx,ny,self.player):
                self.whisper_text=f"Lever activated! ({self.player.levers_pulled})"; self.whisper_timer=60
            else:
                self.whisper_text="Need more levers!"; self.whisper_timer=60

    def update(self):
        if self.state != "playing":
            return
        self.handle_movement()
        self.player.update(self.level.dungeon)
        ppos = self.player.get_pos()

        for monster in self.level.monsters[:]:
            if monster.update(ppos, self.level.dungeon, self.player):
                self.player.health -= monster.damage; self.player.hit_flash=12
                if self.player.health<=0:
                    self.state="gameover"; self.game_over_reason="monster"
        if self.level.chase_monster:
            if self.level.chase_monster.update(ppos, self.level.dungeon, self.player):
                self.player.health -= self.level.chase_monster.damage; self.player.hit_flash=12
                if self.player.health<=0:
                    self.state="gameover"; self.game_over_reason="chaser"

        cx,cy = self.player.get_cell()
        for portal in self.level.exit_portals:
            if (cx,cy) == portal['pos']:
                nxt, ending = self.story.get_next_level(self.level, self.player, portal['type'])
                if ending:
                    self.ending_type = ending
                    self.state = "win"
                    return
                elif nxt is not None:
                    self.current_level_id = nxt
                    self.level = Level(self.current_level_id)
                    self.player = Player(self.level.start[0], self.level.start[1])
                    self.player.levers_pulled = 0
                    self.whisper_text = f"Entering Level {self.current_level_id}"; self.whisper_timer=90
                    self.transition_text = f"Level {self.current_level_id}"; self.transition_timer=60
                    return

        for monster in self.level.monsters:
            if dist((monster.x,monster.y),(self.player.x,self.player.y))<5:
                self.player.sanity=max(0,self.player.sanity-0.06)
        if self.level.chase_monster and dist((self.level.chase_monster.x,self.level.chase_monster.y),(self.player.x,self.player.y))<8:
            self.player.sanity=max(0,self.player.sanity-0.08)
        if self.player.sanity<=0:
            self.state="gameover"; self.game_over_reason="sanity"

        if random.random()<0.003: self.flicker=random.randint(15,40)
        if self.flicker>0: self.flicker-=1
        if self.player.sanity<30 and random.random()<0.01: self.hallucination=True
        else: self.hallucination=False
        if self.whisper_timer>0: self.whisper_timer-=1
        else: self.whisper_text=""
        if self.transition_timer>0: self.transition_timer-=1
        px,py = self.player.get_pos()
        self.camera_x = px*CELL_SIZE - WINDOW_WIDTH//2
        self.camera_y = py*CELL_SIZE - WINDOW_HEIGHT//2

    # ==================== 渲染（已调亮） ====================
    def render(self):
        self.screen.fill(COLORS['bg'])
        offset_x, offset_y = -self.camera_x, -self.camera_y
        view_radius = VIEW_RADIUS + (TORCH_RADIUS if self.player.torch_active else 0)
        px, py = self.player.get_pos()
        wall_col, _, floor_col = self.level.get_theme_colors()
        theme_key = self.level.theme_key
        wall_tex, floor_tex = get_texture(theme_key,'wall'), get_texture(theme_key,'floor')

        for y in range(max(0,int(py-view_radius-1)), min(self.level.height, int(py+view_radius+2))):
            for x in range(max(0,int(px-view_radius-1)), min(self.level.width, int(px+view_radius+2))):
                d = math.hypot(x-px, y-py)
                if d>view_radius+1: continue
                rect = pygame.Rect(offset_x+x*CELL_SIZE, offset_y+y*CELL_SIZE, CELL_SIZE, CELL_SIZE)
                cell = self.level.dungeon[y][x]
                if cell in (0,2,3,4,5):
                    self.screen.blit(floor_tex, rect)
                else:
                    self.screen.blit(wall_tex, rect)

        # 特殊物体
        for y in range(max(0,int(py-view_radius-1)), min(self.level.height, int(py+view_radius+2))):
            for x in range(max(0,int(px-view_radius-1)), min(self.level.width, int(px+view_radius+2))):
                d=math.hypot(x-px,y-py)
                if d>view_radius+1: continue
                rect=pygame.Rect(offset_x+x*CELL_SIZE, offset_y+y*CELL_SIZE, CELL_SIZE, CELL_SIZE)
                cell=self.level.dungeon[y][x]
                if cell==2:
                    pygame.draw.rect(self.screen,(60,50,40),rect); pygame.draw.rect(self.screen,(100,80,60),rect,2)
                elif cell==3:
                    pygame.draw.rect(self.screen,(80,60,40),rect); pygame.draw.rect(self.screen,(200,180,100),rect,3)
                    self.screen.blit(font_small.render("🔒",True,(200,200,100)),(rect.x+6,rect.y+4))
                elif cell==4:
                    pygame.draw.rect(self.screen,(70,70,90),rect); pygame.draw.rect(self.screen,(150,150,200),rect,3)
                    self.screen.blit(font_small.render("🔐",True,(200,200,150)),(rect.x+6,rect.y+4))
                elif cell==5:
                    pygame.draw.rect(self.screen,(90,90,130),rect); pygame.draw.rect(self.screen,(200,200,255),rect,3)
                    self.screen.blit(font_small.render("🎛️",True,(200,200,255)),(rect.x+6,rect.y+4))

        # 物品
        for item in self.level.items:
            if item.collected: continue
            if dist((item.x,item.y),(px,py))<=view_radius+1:
                rect=pygame.Rect(offset_x+item.x*CELL_SIZE, offset_y+item.y*CELL_SIZE, CELL_SIZE, CELL_SIZE)
                base=Item.get_color(item.type)
                glow=(min(255,base[0]+80),min(255,base[1]+80),min(255,base[2]+80))
                g=pygame.Surface((CELL_SIZE,CELL_SIZE),pygame.SRCALPHA)
                pygame.draw.circle(g,(*glow,150),(CELL_SIZE//2,CELL_SIZE//2),CELL_SIZE//2)
                self.screen.blit(g,rect)
                self.screen.blit(font_small.render(Item.get_emoji(item.type),True,(255,255,255)),(rect.x+6,rect.y+4))

        # 怪物
        for monster in self.level.monsters:
            if dist((monster.x,monster.y),(px,py))<=view_radius+1:
                rect=pygame.Rect(offset_x+monster.x*CELL_SIZE-CELL_SIZE//2, offset_y+monster.y*CELL_SIZE-CELL_SIZE//2, CELL_SIZE, CELL_SIZE)
                if monster.type=="smiler":
                    if self.player.sanity<60:
                        pygame.gfxdraw.filled_circle(self.screen, rect.centerx, rect.centery, CELL_SIZE//2, (255,200,200))
                        pygame.gfxdraw.aacircle(self.screen, rect.centerx, rect.centery, CELL_SIZE//2, (255,200,200))
                        for i in range(-3,4):
                            tx,ty=rect.centerx+i*4, rect.centery+4
                            pygame.draw.rect(self.screen,(255,255,255),(tx-2,ty-2,4,4))
                    else:
                        pygame.gfxdraw.aacircle(self.screen, rect.centerx, rect.centery, CELL_SIZE//2, (100,100,100))
                else:
                    pygame.gfxdraw.filled_circle(self.screen, rect.centerx, rect.centery, CELL_SIZE//2-2, monster.color)
                    pygame.gfxdraw.aacircle(self.screen, rect.centerx, rect.centery, CELL_SIZE//2-2, monster.color)
                    dx,dy=px-monster.x, py-monster.y
                    ang=math.atan2(dy,dx)
                    for side in (-1,1):
                        ex=rect.centerx+math.cos(ang+side*0.6)*6
                        ey=rect.centery+math.sin(ang+side*0.6)*6
                        pygame.gfxdraw.filled_circle(self.screen, int(ex), int(ey), 3, (255,255,200))
                        pygame.gfxdraw.aacircle(self.screen, int(ex), int(ey), 3, (255,255,200))
                    if monster.health>1:
                        bar_w,bar_h=CELL_SIZE,4
                        bar_x,bar_y=rect.x, rect.y-8
                        pygame.draw.rect(self.screen,(60,60,60),(bar_x,bar_y,bar_w,bar_h))
                        pygame.draw.rect(self.screen,(255,0,0),(bar_x,bar_y,(bar_w*monster.health)//3,bar_h))

        if self.level.chase_monster:
            m=self.level.chase_monster
            if dist((m.x,m.y),(px,py))<=view_radius+2:
                rect=pygame.Rect(offset_x+m.x*CELL_SIZE-CELL_SIZE, offset_y+m.y*CELL_SIZE-CELL_SIZE, CELL_SIZE*2, CELL_SIZE*2)
                g=pygame.Surface((CELL_SIZE*2,CELL_SIZE*2),pygame.SRCALPHA)
                pygame.draw.circle(g,(255,0,0,80),(CELL_SIZE,CELL_SIZE),CELL_SIZE)
                self.screen.blit(g,rect)
                crect=pygame.Rect(offset_x+m.x*CELL_SIZE-CELL_SIZE//2, offset_y+m.y*CELL_SIZE-CELL_SIZE//2, CELL_SIZE, CELL_SIZE)
                pygame.gfxdraw.filled_circle(self.screen, crect.centerx, crect.centery, CELL_SIZE//2, (200,0,0))
                pygame.gfxdraw.aacircle(self.screen, crect.centerx, crect.centery, CELL_SIZE//2, (200,0,0))
                dx,dy=px-m.x, py-m.y; ang=math.atan2(dy,dx)
                for side in (-1,1):
                    ex=crect.centerx+math.cos(ang+side*0.6)*8
                    ey=crect.centery+math.sin(ang+side*0.6)*8
                    pygame.gfxdraw.filled_circle(self.screen, int(ex), int(ey), 4, (255,50,50))
                    pygame.gfxdraw.aacircle(self.screen, int(ex), int(ey), 4, (255,50,50))
                    pygame.gfxdraw.filled_circle(self.screen, int(ex), int(ey), 2, (255,255,200))

        # 出口传送门
        for portal in self.level.exit_portals:
            ex,ey=portal['pos']
            if dist((ex,ey),(px,py))<=view_radius+2:
                rect=pygame.Rect(offset_x+ex*CELL_SIZE, offset_y+ey*CELL_SIZE, CELL_SIZE, CELL_SIZE)
                is_safe = portal['type']=='safe'
                color = COLORS['exit'] if is_safe else COLORS['exit_danger']
                glow_col = COLORS['exit_glow'] if is_safe else COLORS['exit_danger_glow']
                g=pygame.Surface((CELL_SIZE*2,CELL_SIZE*2),pygame.SRCALPHA)
                pygame.draw.circle(g,(*glow_col,120),(CELL_SIZE,CELL_SIZE),CELL_SIZE)
                self.screen.blit(g,(rect.x-CELL_SIZE//2, rect.y-CELL_SIZE//2))
                pygame.draw.rect(self.screen,color,rect)
                pygame.draw.rect(self.screen,(200,255,200) if is_safe else (255,200,200), rect.inflate(-4,-4),2)
                txt = "SAFE" if is_safe else "DANGER"
                self.screen.blit(font_small.render(txt,True,(0,80,0) if is_safe else (80,0,0)),(rect.x+4,rect.y+6))

        # 玩家
        ppos=self.player.get_pos()
        prect=pygame.Rect(offset_x+ppos[0]*CELL_SIZE-CELL_SIZE//2, offset_y+ppos[1]*CELL_SIZE-CELL_SIZE//2, CELL_SIZE, CELL_SIZE)
        g=pygame.Surface((CELL_SIZE*2,CELL_SIZE*2),pygame.SRCALPHA)
        pygame.draw.circle(g,(*COLORS['player_glow'],80),(CELL_SIZE,CELL_SIZE),CELL_SIZE)
        self.screen.blit(g,(prect.x-CELL_SIZE//2, prect.y-CELL_SIZE//2))
        col = (255,0,0) if self.player.hit_flash>0 else COLORS['player']
        pygame.gfxdraw.filled_circle(self.screen, prect.centerx, prect.centery, CELL_SIZE//2-2, col)
        pygame.gfxdraw.aacircle(self.screen, prect.centerx, prect.centery, CELL_SIZE//2-2, col)
        ex,ey = prect.centerx-4, prect.centery-3
        pygame.gfxdraw.filled_circle(self.screen, ex-2, ey, 3, (255,255,255))
        pygame.gfxdraw.filled_circle(self.screen, ex+6, ey, 3, (255,255,255))
        pygame.gfxdraw.filled_circle(self.screen, ex-1, ey+1, 1, (0,0,0))
        pygame.gfxdraw.filled_circle(self.screen, ex+7, ey+1, 1, (0,0,0))

        # 视野遮罩（已调亮）
        fog=pygame.Surface((WINDOW_WIDTH,WINDOW_HEIGHT),pygame.SRCALPHA)
        center=(WINDOW_WIDTH//2,WINDOW_HEIGHT//2)
        radius_px=view_radius*CELL_SIZE
        for r in range(radius_px,0,-5):
            alpha=int(120 * (1 - r/radius_px)) if r<radius_px else 0  # 降低不透明度（原200）
            if alpha>0: pygame.draw.circle(fog,(0,0,0,min(alpha,120)),center,r)
        pygame.draw.circle(fog,(0,0,0,160),center,radius_px+1,1)     # 外围柔和（原255）
        self.screen.blit(fog,(0,0))

        # 晕影（已减半）
        self.screen.blit(self.vignette_surf,(0,0))
        if self.player.sanity<50: self.screen.blit(self.noise_surf,(0,0))

        if self.flicker>0:
            ov=pygame.Surface((WINDOW_WIDTH,WINDOW_HEIGHT)); ov.fill((0,0,0)); ov.set_alpha(random.randint(30,150)); self.screen.blit(ov,(0,0))
        if self.hallucination:
            self.screen.blit(self.screen,(random.randint(-4,4),random.randint(-4,4)))

        self.render_ui()
        if self.show_password_prompt: self.render_password_prompt()
        if self.transition_timer>0:
            ov=pygame.Surface((WINDOW_WIDTH,WINDOW_HEIGHT)); ov.fill((0,0,0)); ov.set_alpha(min(255,self.transition_timer*4)); self.screen.blit(ov,(0,0))
            txt=font_title.render(self.transition_text,True,(255,255,255))
            self.screen.blit(txt,(WINDOW_WIDTH//2-txt.get_width()//2,WINDOW_HEIGHT//2))
        if self.state=="win": self.render_win()
        elif self.state=="gameover": self.render_gameover()
        pygame.display.flip()

    def render_ui(self):
        ui=pygame.Surface((WINDOW_WIDTH,110),pygame.SRCALPHA); ui.fill((10,10,15,200)); self.screen.blit(ui,(0,WINDOW_HEIGHT-110))
        bx,by=20,WINDOW_HEIGHT-95; bw,bh=120,16
        ratio=self.player.sanity/SANITY_MAX
        pygame.draw.rect(self.screen,(60,60,60),(bx,by,bw,bh),border_radius=4)
        col=COLORS['sanity_bar'] if ratio>0.3 else COLORS['sanity_bar_low']
        pygame.draw.rect(self.screen,col,(bx+2,by+2,int((bw-4)*ratio),bh-4),border_radius=3)
        self.screen.blit(font_small.render(f"Sanity {int(self.player.sanity)}%",True,(255,255,255)),(bx+6,by+2))
        hx=bx+bw+10; hw=100
        pygame.draw.rect(self.screen,(60,60,60),(hx,by,hw,bh),border_radius=4)
        pygame.draw.rect(self.screen,(200,50,50),(hx+2,by+2,int((hw-4)*(self.player.health/self.player.max_health)),bh-4),border_radius=3)
        self.screen.blit(font_small.render(f"HP {int(self.player.health)}%",True,(255,255,255)),(hx+6,by+2))
        sx=hx+hw+10; sw=100
        pygame.draw.rect(self.screen,(60,60,60),(sx,by,sw,bh),border_radius=4)
        pygame.draw.rect(self.screen,COLORS['stamina_bar'],(sx+2,by+2,int((sw-4)*(self.player.stamina/self.player.max_stamina)),bh-4),border_radius=3)
        self.screen.blit(font_small.render(f"Stamina {int(self.player.stamina)}%",True,(255,255,255)),(sx+6,by+2))
        ix=sx+sw+10
        self.screen.blit(font_small.render(f"Lv {self.current_level_id}",True,COLORS['text']),(ix,by))
        self.screen.blit(font_small.render(f"Steps: {self.player.steps}",True,COLORS['text']),(ix,by+20))
        if self.player.levers_pulled>0:
            self.screen.blit(font_small.render(f"Levers: {self.player.levers_pulled}",True,(200,200,255)),(ix,by+40))
        invx, invy=20,WINDOW_HEIGHT-65
        self.screen.blit(font_small.render("Items [1-8]:",True,COLORS['text']),(invx,invy))
        invx+=120
        for i,typ in enumerate(self.player.inventory):
            col=Item.get_color(typ)
            rect=pygame.Rect(invx+i*36,invy,32,28)
            pygame.draw.rect(self.screen,col,rect,border_radius=4)
            pygame.draw.rect(self.screen,(200,200,200),rect,1,border_radius=4)
            self.screen.blit(font_small.render(Item.get_emoji(typ),True,(0,0,0)),(rect.x+6,rect.y+4))
            self.screen.blit(font_small.render(str(i+1),True,(180,180,180)),(rect.x+22,rect.y+16))
        if self.player.torch_active:
            self.screen.blit(font_small.render(f"🔦 {self.player.torch_timer//60}s",True,(255,220,100)),(WINDOW_WIDTH-180,WINDOW_HEIGHT-95))
        if self.player.has_key:
            self.screen.blit(font_small.render("🔑 Has Key",True,(200,200,100)),(WINDOW_WIDTH-180,WINDOW_HEIGHT-75))
        if self.player.sprinting:
            self.screen.blit(font_small.render("💨 Sprinting",True,(100,200,255)),(WINDOW_WIDTH-180,WINDOW_HEIGHT-55))
        if self.whisper_timer>0:
            t=font_medium.render(self.whisper_text,True,COLORS['danger'])
            self.screen.blit(t,(WINDOW_WIDTH//2-t.get_width()//2,80))
        tips="WASD move | Shift sprint | Space attack | E pickup | R reset"
        self.screen.blit(font_small.render(tips,True,COLORS['text_dark']),(WINDOW_WIDTH-450,WINDOW_HEIGHT-30))

    def render_password_prompt(self):
        ov=pygame.Surface((WINDOW_WIDTH,WINDOW_HEIGHT),pygame.SRCALPHA); ov.fill((0,0,0,180)); self.screen.blit(ov,(0,0))
        rect=pygame.Rect(WINDOW_WIDTH//2-180,WINDOW_HEIGHT//2-70,360,100)
        pygame.draw.rect(self.screen,(50,50,60),rect,border_radius=8); pygame.draw.rect(self.screen,(200,200,200),rect,2,border_radius=8)
        self.screen.blit(font_medium.render("Enter Password (4-8 digits):",True,(255,255,255)),(rect.x+20,rect.y+15))
        self.screen.blit(font_medium.render("*"*len(self.input_password),True,(100,200,255)),(rect.x+20,rect.y+55))
        self.screen.blit(font_small.render("Enter to confirm  ESC to cancel",True,(180,180,180)),(rect.x+20,rect.y+80))

    def render_win(self):
        ov=pygame.Surface((WINDOW_WIDTH,WINDOW_HEIGHT)); ov.fill((0,0,0)); ov.set_alpha(180); self.screen.blit(ov,(0,0))
        ending_text = self.story.get_ending_text(self.ending_type, self.player)
        lines = ending_text.split('. ')
        y=WINDOW_HEIGHT//2-60
        for line in lines:
            if line:
                t=font_medium.render(line+'.',True,(200,255,200))
                self.screen.blit(t,(WINDOW_WIDTH//2-t.get_width()//2,y)); y+=40
        title=font_title.render("🏁 Journey Ends",True,(100,255,150))
        self.screen.blit(title,(WINDOW_WIDTH//2-title.get_width()//2,y+20))
        self.screen.blit(font_small.render("Press R to restart  ESC to quit",True,(180,180,180)),(WINDOW_WIDTH//2-200,y+80))

    def render_gameover(self):
        ov=pygame.Surface((WINDOW_WIDTH,WINDOW_HEIGHT)); ov.fill((0,0,0)); ov.set_alpha(200); self.screen.blit(ov,(0,0))
        t=font_title.render("💀 You are lost...",True,(255,80,80))
        self.screen.blit(t,(WINDOW_WIDTH//2-t.get_width()//2,WINDOW_HEIGHT//2-60))
        reason="Sanity lost" if self.game_over_reason=="sanity" else "Health depleted"
        sub=font_medium.render(f"Reason: {reason}",True,(200,200,200))
        self.screen.blit(sub,(WINDOW_WIDTH//2-sub.get_width()//2,WINDOW_HEIGHT//2))
        self.screen.blit(font_small.render("Press R to restart  ESC to quit",True,(180,180,180)),(WINDOW_WIDTH//2-200,WINDOW_HEIGHT//2+50))

    def run(self):
        while self.running:
            self.handle_events()
            self.update()
            self.render()
            self.clock.tick(FPS)
        pygame.quit(); sys.exit()

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