# GEISTERSTUNDE IM DUNKELSCHLOSS
# Kompatibel mit pyxelstudio.net
# Steuerung: Pfeiltasten/WASD | P=Pause | R=Neustart | SPACE=Start
import pyxel
import random
import math

# ── Konstanten ──────────────────────────────────────────────────
W = 256; H = 256; FPS = 30
SIGHT = 40; SIGHT_BIG = 62; G_SIGHT = 50
SPD = 2; SPD_F = 4; SPD_S = 1
MAX_LIVES = 5; HUD = 36

M_MENU = 0; M_TUT = 1; M_LSEL = 2; M_READY = 3
M_PLAY = 4; M_PAUSE = 5; M_OVER = 6; M_WLVL = 7; M_WGAME = 8
PU_L = 0; PU_S = 1; PU_I = 2; PU_H = 3

# ── Leveldaten ──────────────────────────────────────────────────
# (Name, Flusen, Zeit_s, Waerter, WSpd, Vasen, Ketten,
#  Fallt, Herd, Mag, PUs, Boden1, Boden2, MapTyp)
# Bodenfarben nur 1,2,3,5 (dunkel, lesbar)
LEVELS = [
    ("Eingangshalle",  20,120,1,1.0, 3,0,0,0,0,2, 5,1,0),
    ("Rittersaal",     25,115,1,1.0, 4,2,0,0,0,2, 1,5,1),
    ("Bibliothek",     25,110,1,1.0, 5,3,0,0,0,2, 3,5,0),
    ("Kueche",         30,105,1,1.0, 6,4,0,0,0,3, 5,3,2),
    ("Weinkeller",     30,100,1,1.0, 7,5,0,0,0,3, 1,5,0),
    ("Thronsaal",      35, 95,1,1.5, 8,5,0,0,0,3, 5,1,3),
    ("Folterkammer",   35, 90,1,1.5, 8,6,0,0,0,3, 1,5,1),
    ("Gefaengnis",     40, 85,2,1.0, 7,5,2,0,0,3, 5,3,2),
    ("Turmzimmer",     40, 85,2,1.0, 7,6,3,0,0,3, 3,5,0),
    ("Schatzkammer",   45, 80,2,1.0, 8,6,3,0,0,4, 1,5,3),
    ("Zauberturm",     45, 80,2,1.0, 8,7,4,0,0,3, 5,1,1),
    ("Ballsaal",       50, 75,2,1.0, 9,7,4,0,0,3, 1,5,2),
    ("Ritterruestung", 50, 75,2,1.0, 8,6,3,2,0,3, 3,5,0),
    ("Folterkammer2",  55, 70,2,1.0, 8,7,4,3,0,2, 5,1,3),
    ("Drachenhort",    55, 70,2,1.0, 9,7,4,4,0,2, 1,5,1),
    ("Geheimgang",     60, 65,3,1.0, 8,6,3,2,0,3, 5,3,2),
    ("Ritualraum",     60, 65,3,1.0, 9,7,4,3,0,3, 3,5,0),
    ("Magierturm",     65, 60,3,1.0, 8,6,3,2,2,2, 1,5,3),
    ("Verflucht",      65, 55,3,1.0, 8,7,4,3,3,2, 5,1,1),
    ("Endkampf",       70, 50,4,1.0,10,8,5,4,3,2, 1,5,2),
]
LSH = ["Halle","Ritter","Biblio","Kueche","Keller","Thron",
       "Folter","Kerker","Turm","Schatz","Zauber","Ball",
       "Ruestg","Foltr2","Drache","Geheim","Ritual","Magier","Verfl","Endkpf"]
LICO = ["gate","sword","book","pot","barrel","crown","chain","bars",
        "tower","coin","wand","mask","armor","rack","egg","door",
        "altar","penta","skull","eye"]

# ── MAP: 4 Typen, alle Gaenge mind. 60px ────────────────────────
def get_rects(t):
    T = HUD+8; B = H-8; L = 8; R = W-8
    cx = W//2; cy = (T+B)//2
    if t == 0:
        return [(L,T,R,B)]
    elif t == 1:
        return [(cx-56,T,cx+56,B),(L,cy-50,R,cy+50)]
    elif t == 2:
        return [(L,T,R,T+100),(L,T,L+110,B)]
    else:
        return [(L,T,R,T+90),(cx-64,T,cx+64,B)]

def in_map(x, y, rects):
    for x1,y1,x2,y2 in rects:
        if x1 <= x <= x2 and y1 <= y <= y2:
            return True
    return False

# ── BEWEGUNG: Integer, X/Y getrennt, kein Drift ─────────────────
def mv(x, y, dx, dy, rects):
    nx = x + dx; ny = y + dy
    if in_map(nx, ny, rects): return nx, ny
    if in_map(nx, y,  rects): return nx, y
    if in_map(x,  ny, rects): return x,  ny
    return x, y

# ── SPAWN-POSITION: FEHLER BEHOBEN ──────────────────────────────
# Fallback gibt jetzt zufaellige Punkte aus verschiedenen Rects
# statt immer denselben Mittelpunkt
def rpos(rects, used, gap=20):
    # 1000 Versuche mit zufaelligen Rects
    for _ in range(1000):
        r = random.choice(rects)
        pad = 16
        x1 = r[0]+pad; x2 = r[2]-pad
        y1 = r[1]+pad; y2 = r[3]-pad
        if x1 >= x2 or y1 >= y2:
            continue
        x = random.randint(x1, x2)
        y = random.randint(y1, y2)
        ok = True
        for px, py in used:
            if (x-px)*(x-px)+(y-py)*(y-py) < gap*gap:
                ok = False
                break
        if ok:
            return x, y
    # Fallback: kleineren Gap versuchen (halbieren, dann viertel)
    for reduced_gap in [gap//2, gap//4, 8]:
        for _ in range(200):
            r = random.choice(rects)
            pad = 12
            x1 = r[0]+pad; x2 = r[2]-pad
            y1 = r[1]+pad; y2 = r[3]-pad
            if x1 >= x2 or y1 >= y2:
                continue
            x = random.randint(x1, x2)
            y = random.randint(y1, y2)
            ok = True
            for px, py in used:
                if (x-px)*(x-px)+(y-py)*(y-py) < reduced_gap*reduced_gap:
                    ok = False
                    break
            if ok:
                return x, y
    # Letzter Fallback: irgendwo in einem zufaelligen Rect
    r = random.choice(rects)
    pad = 10
    x1 = max(r[0]+pad, r[0]+1); x2 = min(r[2]-pad, r[2]-1)
    y1 = max(r[1]+pad, r[1]+1); y2 = min(r[3]-pad, r[3]-1)
    if x1 >= x2: x1 = r[0]+1; x2 = r[2]-1
    if y1 >= y2: y1 = r[1]+1; y2 = r[3]-1
    return random.randint(min(x1,x2), max(x1,x2)), random.randint(min(y1,y2), max(y1,y2))

def d2(x1,y1,x2,y2): return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)
def dist(x1,y1,x2,y2): return math.sqrt(d2(x1,y1,x2,y2))

# ── GLOBALER ZUSTAND ────────────────────────────────────────────
g = {
    "mode":M_MENU, "done":[False]*20, "lvl":0, "lives":3,
    "tick":0, "hs":0, "dtot":0, "dgot":0, "tf":0,
    "why":"", "pts":[], "scroll":0,
    "gh":None, "guards":[], "dust":[], "vasen":[], "chains":[],
    "traps":[], "stoves":[], "magic":[], "pups":[], "dots":[],
    "rects":[], "boost":0, "slow":0, "imm":0, "wt":0, "ict":0,
    "kl":False, "kr":False, "ku":False, "kd":False,
}

def rkeys():
    g["kl"] = False; g["kr"] = False; g["ku"] = False; g["kd"] = False

# ── PARTIKEL ────────────────────────────────────────────────────
def addp(x, y, n, cols, sp=1.5, li=14):
    for _ in range(n):
        a = random.random()*6.28
        s = random.uniform(0.3, sp)
        g["pts"].append([float(x), float(y), math.cos(a)*s, math.sin(a)*s, random.choice(cols), li])

def updp():
    live = []
    for p in g["pts"]:
        p[0] += p[2]; p[1] += p[3]; p[5] -= 1
        if p[5] > 0:
            live.append(p)
    g["pts"] = live

def drwp(gx, gy, sr):
    sr2 = (sr+20)*(sr+20)
    for p in g["pts"]:
        if (p[0]-gx)*(p[0]-gx)+(p[1]-gy)*(p[1]-gy) < sr2:
            pyxel.pset(int(p[0]), int(p[1]), p[4])

# ── BODEN & WAND ────────────────────────────────────────────────
def drw_floor(wx, wy, c1, c2, t):
    if wx%16 == 0 or wy%16 == 0:
        pyxel.pset(wx, wy, 0)
        return
    tx = wx%16; ty = wy%16
    base = c1 if (wx//16+wy//16)%2 == 0 else c2
    if tx == (t//14)%14+1 and ty == 2:
        pyxel.pset(wx, wy, min(6, base+1))
        return
    if tx == 1 or ty == 1:
        pyxel.pset(wx, wy, min(6, base+1))
    else:
        pyxel.pset(wx, wy, base)

def drw_wall(wx, wy):
    if wx%12 == 0 or wy%9 == 0:
        pyxel.pset(wx, wy, 0)
    elif (wx//12+wy//9)%2 == 0:
        pyxel.pset(wx, wy, 5)
    else:
        pyxel.pset(wx, wy, 3)

def drw_bg(gx, gy, sr, rects, c1, c2, t):
    sr2 = sr*sr
    for dy in range(-sr, sr+1):
        for dx in range(-sr, sr+1):
            if dx*dx+dy*dy > sr2:
                continue
            wx = gx+dx; wy = gy+dy
            if not (0 <= wx < W and 0 <= wy < H):
                continue
            if in_map(wx, wy, rects):
                bord = (not in_map(wx-1,wy,rects) or not in_map(wx+1,wy,rects)
                        or not in_map(wx,wy-1,rects) or not in_map(wx,wy+1,rects))
                if bord:
                    pyxel.pset(wx, wy, 6)
                else:
                    drw_floor(wx, wy, c1, c2, t)
            else:
                drw_wall(wx, wy)

# ── GEIST ───────────────────────────────────────────────────────
def drw_ghost():
    gh = g["gh"]; x = gh["x"]; y = gh["y"]
    af = gh["af"]; t = g["tick"]
    inv = gh["it"] > 0; slow = g["slow"] > 0; imm = g["imm"] > 0
    oy = 1 if af == 1 else (-1 if af == 3 else 0)
    tax = [0,2,-2,0][af]
    if inv:
        for dy in range(-6, 7):
            for dx in range(-5, 6):
                if dx*dx+(dy-oy)*(dy-oy) < 26 and (dx+dy)%2 == 0:
                    pyxel.pset(x+dx, y+dy, 6)
        return
    bc = 12; dc = 1
    if slow:
        bc = 8 if (t//4)%2 == 0 else 6
        dc = 5
    if imm and (t//5)%2 == 0:
        bc = 7; dc = 12
    pyxel.circ(x, y-1+oy, 5, dc)
    pyxel.circ(x-1, y+2+oy, 4, dc)
    pyxel.circ(x+1, y+2+oy, 4, dc)
    pyxel.rect(x-4, y-1+oy, 9, 5, dc)
    pyxel.circ(x, y-1+oy, 4, bc)
    pyxel.circ(x-1, y+2+oy, 3, bc)
    pyxel.circ(x+1, y+2+oy, 3, bc)
    pyxel.rect(x-3, y-1+oy, 7, 5, bc)
    pyxel.pset(x-1, y-2+oy, 7); pyxel.pset(x, y-2+oy, 7)
    for ox, o2 in [(-4,1),(4,1),(-3,3),(3,3)]:
        pyxel.pset(x+ox, y+o2+oy, dc)
    for tx2 in range(-2, 3):
        pyxel.pset(x+tx2+tax, y+6+oy, bc if abs(tx2) < 2 else dc)
    pyxel.pset(x+tax, y+7+oy, bc)
    pyxel.pset(x-1+tax, y+7+oy, dc)
    pyxel.pset(x+1+tax, y+7+oy, dc)
    if not gh["bl"]:
        for ox, o2 in [(-2,-2),(-2,-1),(-1,-2),(2,-2),(2,-1),(1,-2)]:
            pyxel.pset(x+ox, y+o2+oy, 0)
        pyxel.pset(x-3, y-2+oy, 7); pyxel.pset(x+3, y-2+oy, 7)
    else:
        pyxel.line(x-3, y-2+oy, x-1, y-2+oy, 0)
        pyxel.line(x+1, y-2+oy, x+3, y-2+oy, 0)
    for ox, o2 in [(-2,1),(-1,2),(0,2),(1,2),(2,1)]:
        pyxel.pset(x+ox, y+o2+oy, 0)
    if imm:
        r2 = 9 + int(math.sin(t*0.25)*2)
        for ai in range(10):
            ang = ai*math.pi/5 + t*0.08
            px2 = x+int(math.cos(ang)*r2)
            py2 = y+int(math.sin(ang)*r2)
            if 0 <= px2 < W and 0 <= py2 < H:
                pyxel.pset(px2, py2, 12 if ai%2 == 0 else 7)

# ── WAERTER ─────────────────────────────────────────────────────
def drw_guard(gd):
    x = gd["x"]; y = gd["y"]; af = gd["af"]; lt = gd["lt"]
    boost = g["boost"] > 0; ch = gd["ch"]
    ll = -2 if af==0 else (2 if af==2 else 0)
    lr =  2 if af==0 else (-2 if af==2 else 0)
    co =  1 if af==0 else (-1 if af==2 else 0)
    pyxel.rect(x-4+co,y-2,9,10,8)
    pyxel.line(x-2+co,y-2,x-3+co,y+7,5)
    pyxel.line(x+1+co,y-2,x+2+co,y+7,5)
    pyxel.line(x-4+co,y+8,x+4+co,y+8,5)
    pyxel.rect(x-3,y+6+ll,3,5,5); pyxel.rect(x-3,y+9+ll,3,2,3)
    pyxel.rect(x+1,y+6+lr,3,5,5); pyxel.rect(x+1,y+9+lr,3,2,3)
    pyxel.rect(x-3,y-2,7,8,8)
    pyxel.pset(x-4,y-2,8); pyxel.pset(x+4,y-2,8)
    pyxel.pset(x,y,5); pyxel.pset(x,y+2,5); pyxel.pset(x,y+4,5)
    pyxel.rect(x-3,y+4,7,2,5); pyxel.pset(x,y+4,6)
    pyxel.rect(x-3,y-10,7,8,5)
    pyxel.rect(x-2,y-9,5,6,15)
    pyxel.pset(x-3,y-9,5); pyxel.pset(x+3,y-9,5)
    ec = 8 if ch else 0
    pyxel.pset(x-1,y-8,ec); pyxel.pset(x+1,y-8,ec)
    if ch:
        pyxel.pset(x-1,y-7,8); pyxel.pset(x+1,y-7,8)
    pyxel.pset(x-2,y-9,5); pyxel.pset(x+2,y-9,5)
    pyxel.line(x-1,y-6,x+1,y-6,5)
    pyxel.rect(x-4,y-11,9,2,5)
    pyxel.rect(x-3,y-14,7,4,5)
    pyxel.rect(x-2,y-17,5,4,5)
    pyxel.rect(x-1,y-19,3,3,5)
    pyxel.pset(x,y-20,5)
    pyxel.rect(x-3,y-13,7,1,6)
    pyxel.pset(x,y-13,7)
    lc = 8 if (boost and (g["tick"]//2)%2 == 0) else (7 if lt < 3 else 6)
    pyxel.line(x+4,y-2,x+6,y,5)
    pyxel.rect(x+5,y-1,5,6,5)
    pyxel.rectb(x+5,y-1,5,6,6)
    pyxel.rect(x+6,y,3,4,lc)
    pyxel.pset(x+6,y,7)
    if lt < 3 and not boost:
        pyxel.pset(x+10,y+1,6)
    if boost and (g["tick"]//3)%2 == 0:
        for bx2,by2 in [(-7,0),(7,0),(0,-14),(0,8)]:
            if 0 <= x+bx2 < W and 0 <= y+by2 < H:
                pyxel.pset(x+bx2, y+by2, 8)

# ── ITEMS ───────────────────────────────────────────────────────
def drw_dust(d):
    x = d["x"]; y = d["y"]; af = d["af"]; t = g["tick"]
    if t%6 < 3:
        pyxel.pset(x-3,y,5); pyxel.pset(x+3,y,5)
        pyxel.pset(x,y-3,5); pyxel.pset(x,y+3,5)
    pyxel.pset(x,y,7); pyxel.pset(x-1,y,6); pyxel.pset(x+1,y,6)
    pyxel.pset(x,y-1,7); pyxel.pset(x,y+1,6)
    if af == 0:
        pyxel.pset(x-1,y-1,7); pyxel.pset(x+1,y-1,6)
        pyxel.pset(x-1,y+1,5); pyxel.pset(x+1,y+1,6)
    else:
        pyxel.pset(x-1,y-1,6); pyxel.pset(x+1,y-1,7)
        pyxel.pset(x-1,y+1,6); pyxel.pset(x+1,y+1,5)

def drw_pu(pu):
    t2 = pu["t"]; typ = pu["type"]
    bob = int(math.sin(t2*0.12)*2)
    x = pu["x"]; y = pu["y"]+bob
    bc = [10,12,6,8][typ]
    pyxel.rect(x-5,y-5,11,11,0)
    pyxel.rectb(x-5,y-5,11,11,bc)
    pyxel.line(x-4,y-4,x+4,y-4,6)
    if typ == PU_L:
        ro = (t2//6)%8
        for i in range(8):
            ang = (i+ro)*math.pi/4
            for r2 in [3,4,5]:
                px2 = x+int(math.cos(ang)*r2)
                py2 = y+int(math.sin(ang)*r2)
                if abs(px2-x) <= 4 and abs(py2-y) <= 4:
                    pyxel.pset(px2, py2, 10 if r2 < 5 else 9)
        pyxel.circ(x,y,2,10); pyxel.pset(x,y,7)
    elif typ == PU_S:
        pyxel.line(x-4,y-2,x-1,y,12); pyxel.line(x-1,y,x-4,y+2,12)
        pyxel.line(x,y-2,x+3,y,12);   pyxel.line(x+3,y,x,y+2,12)
        pyxel.pset(x+4,y,7)
    elif typ == PU_I:
        pyxel.circ(x,y-1,3,12)
        pyxel.rect(x-3,y+1,7,4,12)
        pyxel.circ(x-2,y+3,2,12); pyxel.circ(x+2,y+3,2,12)
        for i in range(-3,4):
            for j in range(-1,5):
                if (i+j)%2 == 0 and abs(i) <= 3 and 0 <= j <= 4:
                    pyxel.pset(x+i, y+j, 0)
        pyxel.pset(x-1,y-1,7); pyxel.pset(x+1,y-1,7)
    elif typ == PU_H:
        for ox,oy in [(-3,-1),(-2,-2),(-1,-3),(0,-2),(1,-3),(2,-2),(3,-1),
                      (-3,0),(-2,1),(-1,2),(0,3),(1,2),(2,1),(3,0),
                      (-2,-1),(-1,-1),(0,-1),(1,-1),(2,-1),(0,0)]:
            pyxel.pset(x+ox, y+oy, 8)
        pyxel.pset(x-1,y-1,9)
        if (t2//6)%2 == 0:
            pyxel.rectb(x-4,y-4,9,9,8)

def drw_vase(v):
    x = v["x"]; y = v["y"]
    pyxel.circ(x,y+2,5,9)
    pyxel.pset(x+4,y,15); pyxel.pset(x+5,y+1,7)
    for i in range(5): pyxel.pset(x-4+i, y-1+i//2, 4)
    pyxel.rect(x-3,y-4,7,4,9)
    pyxel.rect(x-1,y-7,3,4,9)
    pyxel.rect(x-2,y-9,5,3,4)
    pyxel.rect(x-1,y-8,3,2,9)
    pyxel.pset(x-5,y+1,9); pyxel.pset(x+5,y+1,9)
    for i in range(-3,4):
        pyxel.pset(x+i, y, 10 if (i+3)%2 == 0 else 9)
    pyxel.pset(x-2,y-2,7)

def drw_chain(c):
    x = c["x"]; y = c["y"]
    for i in range(6):
        ox = -10+i*4; oy = 1 if i%2 == 0 else -1
        pyxel.rect(x+ox-2,y+oy-1,5,3,6)
        pyxel.rectb(x+ox-2,y+oy-1,5,3,7)
        pyxel.pset(x+ox,y+oy,0)
        pyxel.pset(x+ox-1,y+oy-1,7)

def drw_trap(td):
    x = td["x"]; y = td["y"]
    pyxel.rect(x-7,y-7,15,15,4)
    pyxel.rect(x-5,y-5,11,11,0)
    for i in range(-6,7,4):
        pyxel.line(x+i,y-7,x+i+1,y+6,5)
    for d3 in range(1,5):
        pyxel.line(x-d3,y-d3,x+d3,y-d3,5)
    pyxel.rect(x+4,y-7,3,4,6)
    pyxel.pset(x+5,y-6,7)

def drw_stove(s):
    x = s["x"]; y = s["y"]; pt = s["pt"]
    pyxel.rect(x-6,y-6,13,13,6)
    pyxel.rectb(x-6,y-6,13,13,5)
    pyxel.rect(x-4,y-4,9,9,5)
    for nx2,ny2 in [(-5,-5),(4,-5),(-5,4),(4,4)]:
        pyxel.pset(x+nx2, y+ny2, 7)
    ic = 8 if pt < 5 else 9
    oc = 9 if pt < 5 else 8
    pyxel.circ(x,y,3,oc); pyxel.circ(x,y,2,ic); pyxel.pset(x,y,7)
    if (g["tick"]//3)%2 == 0:
        for hx,hy in [(-2,-5),(0,-6),(2,-5)]:
            if 0 <= x+hx < W and 0 <= y+hy < H:
                pyxel.pset(x+hx, y+hy, 9)
    pyxel.rect(x+6,y-1,4,3,6)
    pyxel.pset(x+7,y,7)

def drw_magic(m):
    x = m["x"]; y = m["y"]; pa = m["pa"]; t = g["tick"]
    cc = 13 if (t//4)%2 == 0 else 14
    pyxel.circb(x,y,8,cc)
    pyxel.circb(x,y,6,14 if cc == 13 else 13)
    pp = []
    for i in range(5):
        a = -math.pi/2+i*2*math.pi/5
        pp.append((x+int(math.cos(a)*6), y+int(math.sin(a)*6)))
    for i in range(5):
        ax,ay = pp[i]; bx,by = pp[(i+2)%5]
        pyxel.line(ax,ay,bx,by,13)
    for px2,py2 in pp:
        pyxel.pset(px2,py2,14)
    pyxel.pset(x, y, 14 if (t//3)%2 == 0 else 13)
    for i in range(8):
        a = pa+i*math.pi/4
        px2 = x+int(math.cos(a)*10); py2 = y+int(math.sin(a)*10)
        if 0 <= px2 < W and 0 <= py2 < H:
            pyxel.pset(px2, py2, 13 if i%2 == 0 else 7)

# ── ICONS (8x8, animiert) ────────────────────────────────────────
def drw_icon(name, x, y, a):
    c = 7; c2 = 12
    if name == "gate":
        pyxel.rect(x,y+3,2,5,c); pyxel.rect(x+6,y+3,2,5,c)
        pyxel.line(x,y+3,x+7,y+3,c); pyxel.circ(x+4,y+3,2,c2)
        pyxel.rect(x+3,y+4,2,2+a%3,0)
    elif name == "sword":
        pyxel.line(x+4,y,x+4,y+5,c); pyxel.line(x+1,y+3,x+7,y+3,c2)
        pyxel.rect(x+3,y+5,3,3,c)
        if a < 4: pyxel.pset(x+3,y+a,7)
    elif name == "book":
        pyxel.rect(x,y+1,4,6,c); pyxel.rect(x+4,y+1,4,6,c2)
        pyxel.line(x+4,y,x+4,y+7,0)
        pyxel.line(x+1,y+3,x+3,y+3,7); pyxel.line(x+5,y+3,x+7,y+3,7)
        if a%4 == 0: pyxel.line(x+4,y+1,x+6,y+2,7)
    elif name == "pot":
        pyxel.rect(x+1,y+3,6,4,c); pyxel.rect(x+2,y+2,4,2,c2)
        pyxel.line(x,y+4,x,y+6,c); pyxel.line(x+7,y+4,x+7,y+6,c)
        pyxel.pset(x+2,y+1-a%2,7); pyxel.pset(x+4,y-a%2,7)
    elif name == "barrel":
        pyxel.rect(x+1,y,6,8,c)
        pyxel.line(x,y+2,x,y+5,c); pyxel.line(x+7,y+2,x+7,y+5,c)
        pyxel.line(x+1,y+2,x+6,y+2,c2); pyxel.line(x+1,y+5,x+6,y+5,c2)
    elif name == "crown":
        pyxel.rect(x,y+4,8,4,c2)
        pyxel.line(x,y+4,x,y,c); pyxel.line(x+7,y+4,x+7,y,c)
        pyxel.line(x+3,y+4,x+3,y+1,c); pyxel.line(x+4,y+4,x+4,y+1,c)
        jc = 7 if a < 4 else c2
        pyxel.pset(x,y,jc); pyxel.pset(x+7,y,jc); pyxel.pset(x+3,y+1,jc)
    elif name == "chain":
        for i in range(4):
            ox = i*2; oy = 1 if i%2 == 0 else 0
            pyxel.rect(x+ox,y+2+oy,2,3,c)
            pyxel.pset(x+ox+1,y+2+oy,c2)
        shake = 1 if a%4 < 2 else 0
        pyxel.rect(x,y+6,8,2,c2)
        pyxel.circ(x+7+shake,y+7,1,c)
    elif name == "bars":
        for i in range(4): pyxel.line(x+1+i*2,y,x+1+i*2,y+7,c)
        pyxel.line(x,y+2,x+7,y+2,c2); pyxel.line(x,y+5,x+7,y+5,c2)
    elif name == "tower":
        pyxel.rect(x+2,y+2,4,6,c); pyxel.rect(x+1,y,6,3,c)
        for i in range(3): pyxel.pset(x+1+i*2,y,0)
        pyxel.rect(x+3,y+4,2,4,0)
        pyxel.pset(x+4,y+4,c2 if a%4 < 2 else 7)
    elif name == "coin":
        for cx2,cy2 in [(2,2),(5,2),(2,5),(5,5)]:
            pyxel.circ(x+cx2,y+cy2,2,c2); pyxel.pset(x+cx2,y+cy2,c)
        pyxel.pset(x+2 if a < 4 else x+5, y+1 if a < 4 else y+4, 7)
    elif name == "wand":
        pyxel.line(x,y+7,x+5,y+2,c); pyxel.line(x+1,y+7,x+6,y+2,c)
        sa = a*math.pi/4
        for i in range(4):
            ang = sa+i*math.pi/2
            pyxel.pset(x+6+int(math.cos(ang)*2), y+1+int(math.sin(ang)*2), c2)
        pyxel.pset(x+6,y+1,7)
    elif name == "mask":
        pyxel.circ(x+4,y+4,3,c)
        pyxel.pset(x+2,y+3,0); pyxel.pset(x+6,y+3,0)
        pyxel.line(x+3,y+5,x+5,y+5,0)
        pyxel.pset(x+2,y+2+a%2,c2); pyxel.pset(x+6,y+2+a%2,c2)
    elif name == "armor":
        pyxel.rect(x+1,y+3,6,5,c); pyxel.circ(x+4,y+3,3,c)
        pyxel.line(x+1,y+5,x+6,y+5,c2); pyxel.rect(x+2,y+5,4,1,0)
        if a < 8: pyxel.pset(x+1+a%6,y+4,7)
    elif name == "rack":
        pyxel.rect(x,y+4,8,3,c)
        pyxel.rect(x,y+3,2,5,c2); pyxel.rect(x+6,y+3,2,5,c2)
        pyxel.line(x+2,y+2,x+6,y+2,c)
        ryo = a%4//2
        for i in range(4): pyxel.line(x+1+i*2,y+4+ryo,x+1+i*2,y+6+ryo,0)
    elif name == "egg":
        pyxel.circ(x+4,y+4,4,c)
        if a > 4: pyxel.line(x+4,y+1,x+5,y+3,0)
        pyxel.pset(x+2,y+2,c2)
        pyxel.pset(x+6,y+2,c2 if (a+2)%4 > 1 else 7)
        pyxel.pset(x+4,y,7)
    elif name == "door":
        pyxel.rect(x+1,y,5,8,c); pyxel.rectb(x+1,y,5,8,c2)
        gap = a%4; pyxel.line(x+1+gap,y+1,x+1+gap,y+7,0)
        pyxel.pset(x+5,y+4,c2)
    elif name == "altar":
        pyxel.rect(x+1,y+5,6,3,c); pyxel.line(x+1,y+5,x+6,y+5,c2)
        pyxel.rect(x+1,y+2,2,4,7); pyxel.rect(x+5,y+2,2,4,7)
        fc = 7 if a < 4 else c2
        pyxel.pset(x+2,y+1,fc); pyxel.pset(x+2,y,8 if a%2 == 0 else c)
        pyxel.pset(x+6,y+1,fc); pyxel.pset(x+6,y,8 if a%2 == 1 else c)
    elif name == "penta":
        cc2 = 13 if a < 4 else 14
        pp2 = []
        for i in range(5):
            an = -math.pi/2+i*2*math.pi/5
            pp2.append((x+4+int(math.cos(an)*3), y+4+int(math.sin(an)*3)))
        for i in range(5):
            ax,ay = pp2[i]; bx,by = pp2[(i+2)%5]
            pyxel.line(ax,ay,bx,by,cc2)
        pyxel.circb(x+4,y+4,4,c2)
        pa = a*math.pi/4
        pyxel.pset(x+4+int(math.cos(pa)*4), y+4+int(math.sin(pa)*4), 7)
    elif name == "skull":
        pyxel.circ(x+4,y+3,3,c); pyxel.rect(x+2,y+5,4,3,c)
        pyxel.pset(x+2,y+3,0); pyxel.pset(x+4,y+3,0); pyxel.pset(x+6,y+3,0)
        pyxel.pset(x+2,y+6,0); pyxel.pset(x+4,y+6,0); pyxel.pset(x+6,y+6,0)
        if a%4 < 2:
            pyxel.pset(x+2,y+3,8); pyxel.pset(x+6,y+3,8)
    elif name == "eye":
        pyxel.rect(x,y+4,8,4,c)
        pyxel.line(x,y+4,x,y,c); pyxel.line(x+7,y+4,x+7,y,c)
        pyxel.line(x,y,x+7,y,c)
        ex = x+3+a%3; pyxel.circ(ex,y+6,2,c2); pyxel.pset(ex,y+6,0)

# ── HUD ─────────────────────────────────────────────────────────
def drw_hud():
    gh = g["gh"]; lvl = g["lvl"]
    secs = max(0, g["tf"]//FPS)
    name = LEVELS[lvl][0]; t = g["tick"]
    for bx in range(W):
        for by in range(HUD):
            if (bx+by)%2 == 0: pyxel.pset(bx,by,0)
    pyxel.line(0,HUD,W,HUD,5)
    pyxel.text(2,2,"LV"+str(lvl+1)+" "+name[:13],7)
    pyxel.text(2,11,str(g["dgot"])+"/"+str(g["dtot"])+" Flusen",6)
    tc = 8 if secs <= 10 else (6 if secs <= 25 else 7)
    pyxel.text(124,2,"ZEIT",6); pyxel.text(124,11,str(secs)+"s",tc)
    for i in range(MAX_LIVES):
        hx = 162+i*10; hy = 7
        col = 8 if i < g["lives"] else 5
        for ox,oy in [(-2,-1),(-1,-2),(0,-2),(1,-2),(2,-1),(-2,0),(2,0),(-1,1),(1,1),(0,2)]:
            pyxel.pset(hx+ox,hy+oy,col)
        if i < g["lives"]: pyxel.pset(hx-1,hy-1,9)
    px = 2
    for tk,mf,lb,cl in [("lt",10*FPS,"LHT",10),("st",5*FPS,"SPD",12),("it",5*FPS,"INV",6)]:
        v = gh[tk]
        if v > 0:
            pyxel.text(px,22,lb,cl)
            pyxel.rect(px,29,18,2,3)
            pyxel.rect(px,29,max(1,(v*18)//mf),2,cl)
            px += 24
    if g["imm"] > 0:
        pyxel.text(px,22,"IMMUN",12 if (t//5)%2 == 0 else 7)
    if g["slow"] > 0 and (t//5)%2 == 0:
        pyxel.text(px+32,22,"SLOW",8)
    if g["boost"] > 0 and (t//4)%2 == 0:
        pyxel.text(W-40,22,"BOOST!",8)

# ── UI HELFER ───────────────────────────────────────────────────
def drw_btn(x, y, w, h, lbl, bg, bd):
    pyxel.rect(x+2,y+2,w,h,0)
    pyxel.rect(x,y,w,h,bg)
    pyxel.rectb(x,y,w,h,bd)
    pyxel.line(x+1,y+1,x+w-2,y+1,bd)
    tx = x+(w-len(lbl)*4)//2; ty = y+(h-6)//2
    pyxel.text(tx,ty,lbl,7)
    if x <= pyxel.mouse_x <= x+w and y <= pyxel.mouse_y <= y+h:
        pyxel.rectb(x+1,y+1,w-2,h-2,7)

def mbtn(x, y, w, h):
    return (pyxel.btnp(pyxel.MOUSE_BUTTON_LEFT)
            and x <= pyxel.mouse_x <= x+w
            and y <= pyxel.mouse_y <= y+h)

def ctxt(s, y, col):
    pyxel.text((W-len(s)*4)//2, y, s, col)

# ── SOUNDS (positionale Args, pyxelstudio-kompatibel) ───────────
def setup_snd():
    pyxel.sound(0).set("c3e3g3c4","pppp","5421","nnnf",6)
    pyxel.sound(1).set("a1f1d1","nnn","755","nff",5)
    pyxel.sound(2).set("g2c2g1c1","nnnn","7531","nfnf",4)
    pyxel.sound(3).set("c2e2g2c3e3","ppppp","35753","nnnns",5)
    pyxel.sound(4).set("c3b2a2g2f2e2d2c2","tttttttt","77777777","nnnnnnff",8)
    pyxel.sound(5).set("c2e2g2c3g2e2c3","ppppppp","5577775","nnnnnnn",7)
    pyxel.sound(6).set("c2c2g2c3e3g3c4c4","pppppppp","57777777","nnnnnnns",6)
    pyxel.sound(7).set(
        "c2e2g2e2c2d2f2a2f2d2e2g2b2g2e2c2",
        "pppppppppppppppp",
        "24242424242424242424242424242424",
        "vvvvvvvvvvvvvvvv",20)
    pyxel.play(0,7,loop=True)

def snd(i): pyxel.play(1,i)

# ── LEVEL LADEN ─────────────────────────────────────────────────
def load(idx):
    g["lvl"] = idx; rkeys()
    row = LEVELS[idx]
    nm = row[0]; nd = row[1]; ts = row[2]
    ng = row[3]; gs = row[4]; nv = row[5]; nc = row[6]
    nt = row[7]; nst = row[8]; nm2 = row[9]; np2 = row[10]
    c1 = row[11]; c2 = row[12]; mtyp = row[13]
    rects = get_rects(mtyp); g["rects"] = rects
    g["mode"] = M_READY
    g["dtot"] = nd; g["dgot"] = 0; g["tf"] = ts*FPS
    g["why"] = ""; g["pts"] = []
    g["boost"] = 0; g["slow"] = 0; g["imm"] = 90; g["wt"] = 0

    r0 = max(rects, key=lambda r:(r[2]-r[0])*(r[3]-r[1]))
    gx0 = (r0[0]+r0[2])//2; gy0 = (r0[1]+r0[3])//2
    used = [(gx0,gy0)]
    g["gh"] = {"x":gx0,"y":gy0,"af":0,"at":0,"bt":0,"bl":False,"lt":0,"st":0,"it":0}

    guards = []
    for _ in range(ng):
        px,py = rpos(rects,used,80)
        used.append((px,py))
        a = random.random()*6.28
        sp = gs*0.95
        guards.append({
            "x":px,"y":py,"fx":0.0,"fy":0.0,
            "dx":math.cos(a)*sp,"dy":math.sin(a)*sp,
            "sp":sp,"ct":random.randint(40,80),
            "at":0,"af":0,"lt":0,
            "alert_t":0,"tx":None,"ty":None,
            "ch":False,"lsx":None,"lsy":None,"srt":0
        })
    g["guards"] = guards

    def mk(n, ex=None):
        out = []
        for _ in range(n):
            px,py = rpos(rects,used,22)
            used.append((px,py))
            obj = {"x":px,"y":py,"alive":True}
            if ex:
                obj.update(ex)
            out.append(obj)
        return out

    dust = []
    for _ in range(nd):
        px,py = rpos(rects,used,18)
        used.append((px,py))
        dust.append({"x":px,"y":py,"alive":True,"af":0,"at":0})
    g["dust"] = dust
    g["vasen"]  = mk(nv)
    g["chains"] = mk(nc)
    g["traps"]  = mk(nt)
    g["stoves"] = mk(nst,{"pt":0})
    g["magic"]  = mk(nm2,{"pa":0.0})
    pups = []
    for _ in range(np2):
        px,py = rpos(rects,used,20)
        used.append((px,py))
        pups.append({"x":px,"y":py,"alive":True,"type":random.randint(0,3),"t":0})
    g["pups"] = pups
    dots = []
    for _ in range(16):
        r = random.choice(rects)
        px = random.randint(r[0]+4, max(r[0]+5,r[2]-4))
        py = random.randint(r[1]+4, max(r[1]+5,r[3]-4))
        dots.append([float(px),float(py),random.uniform(-0.1,0.1),random.uniform(-0.1,0.1)])
    g["dots"] = dots

def alrt(nx, ny):
    for gd in g["guards"]:
        gd["tx"] = nx; gd["ty"] = ny; gd["alert_t"] = 90; gd["ch"] = False

def over(why):
    g["why"] = why; g["mode"] = M_OVER; rkeys()

# ── UPDATE SPIELFELD ────────────────────────────────────────────
def upd():
    gh = g["gh"]; rects = g["rects"]
    if pyxel.btnp(pyxel.KEY_P):
        g["mode"] = M_PAUSE; rkeys(); return
    if pyxel.btnp(pyxel.KEY_R):
        g["lives"] = 3; load(g["lvl"]); return

    # WASM-sicheres Key-Tracking (kein pyxel.btn())
    if pyxel.btnp(pyxel.KEY_LEFT)  or pyxel.btnp(pyxel.KEY_A): g["kl"] = True
    if pyxel.btnp(pyxel.KEY_RIGHT) or pyxel.btnp(pyxel.KEY_D): g["kr"] = True
    if pyxel.btnp(pyxel.KEY_UP)    or pyxel.btnp(pyxel.KEY_W): g["ku"] = True
    if pyxel.btnp(pyxel.KEY_DOWN)  or pyxel.btnp(pyxel.KEY_S): g["kd"] = True
    if pyxel.btnr(pyxel.KEY_LEFT)  or pyxel.btnr(pyxel.KEY_A): g["kl"] = False
    if pyxel.btnr(pyxel.KEY_RIGHT) or pyxel.btnr(pyxel.KEY_D): g["kr"] = False
    if pyxel.btnr(pyxel.KEY_UP)    or pyxel.btnr(pyxel.KEY_W): g["ku"] = False
    if pyxel.btnr(pyxel.KEY_DOWN)  or pyxel.btnr(pyxel.KEY_S): g["kd"] = False

    sp = SPD_F if gh["st"] > 0 else (SPD_S if g["slow"] > 0 else SPD)
    dx = 0; dy = 0
    if g["kl"]: dx -= sp
    if g["kr"]: dx += sp
    if g["ku"]: dy -= sp
    if g["kd"]: dy += sp
    if dx != 0 or dy != 0:
        nx,ny = mv(gh["x"],gh["y"],dx,dy,rects)
        gh["x"] = nx; gh["y"] = ny

    if gh["lt"] > 0: gh["lt"] -= 1
    if gh["st"] > 0: gh["st"] -= 1
    if gh["it"] > 0: gh["it"] -= 1
    if g["slow"]  > 0: g["slow"]  -= 1
    if g["boost"] > 0: g["boost"] -= 1
    if g["imm"]   > 0: g["imm"]   -= 1

    gh["at"] += 1
    if gh["at"] >= 6: gh["at"] = 0; gh["af"] = (gh["af"]+1)%4
    gh["bt"] += 1
    if gh["bt"] >= 40: gh["bt"] = 0; gh["bl"] = True
    if gh["bl"]:
        gh["bt"] += 1
        if gh["bt"] >= 3: gh["bl"] = False; gh["bt"] = 0

    gx = gh["x"]; gy = gh["y"]; inv = gh["it"] > 0

    # Waerter bewegen (float-Akkumulator, integer-Schritte)
    for gd in g["guards"]:
        boost = 1.5 if g["boost"] > 0 else 1.0
        eff = gd["sp"]*boost
        dtg = dist(gx,gy,gd["x"],gd["y"])
        tdx = 0.0; tdy = 0.0
        if gd["alert_t"] > 0 and gd["tx"] is not None:
            ddx = float(gd["tx"]-gd["x"]); ddy = float(gd["ty"]-gd["y"])
            d3 = math.sqrt(ddx*ddx+ddy*ddy)
            if d3 > 2: tdx = ddx/d3*eff*0.8; tdy = ddy/d3*eff*0.8
            gd["alert_t"] -= 1
            if gd["alert_t"] <= 0: gd["tx"] = None; gd["ty"] = None
        elif not inv and dtg < G_SIGHT:
            gd["ch"] = True; gd["lsx"] = gx; gd["lsy"] = gy; gd["srt"] = 30
            ddx = float(gx-gd["x"]); ddy = float(gy-gd["y"])
            d3 = math.sqrt(ddx*ddx+ddy*ddy)
            if d3 > 1: tdx = ddx/d3*eff; tdy = ddy/d3*eff
        elif gd["ch"] and gd["srt"] > 0:
            gd["srt"] -= 1
            if gd["lsx"] is not None:
                ddx = float(gd["lsx"]-gd["x"]); ddy = float(gd["lsy"]-gd["y"])
                d3 = math.sqrt(ddx*ddx+ddy*ddy)
                if d3 > 2: tdx = ddx/d3*eff*0.7; tdy = ddy/d3*eff*0.7
            if gd["srt"] <= 0: gd["ch"] = False
        else:
            gd["ch"] = False; gd["ct"] -= 1
            if gd["ct"] <= 0:
                gd["ct"] = random.randint(30,70)
                a = random.random()*6.28
                gd["dx"] = math.cos(a)*gd["sp"]
                gd["dy"] = math.sin(a)*gd["sp"]
            tdx = gd["dx"]*boost; tdy = gd["dy"]*boost
        gd["fx"] += tdx; gd["fy"] += tdy
        idx = int(gd["fx"]); idy = int(gd["fy"])
        if idx != 0 or idy != 0:
            nx,ny = mv(gd["x"],gd["y"],idx,idy,rects)
            if nx == gd["x"] and ny == gd["y"]:
                a = random.random()*6.28; gd["ct"] = 8
                gd["dx"] = math.cos(a)*gd["sp"]
                gd["dy"] = math.sin(a)*gd["sp"]
                gd["fx"] = 0.0; gd["fy"] = 0.0
            else:
                gd["x"] = nx; gd["y"] = ny
                gd["fx"] -= idx; gd["fy"] -= idy
        gd["at"] += 1
        if gd["at"] >= 8: gd["at"] = 0; gd["af"] = (gd["af"]+1)%4
        gd["lt"] = (gd["lt"]+1)%6

    for d in g["dust"]:
        if d["alive"]:
            d["at"] += 1
            if d["at"] >= 8: d["at"] = 0; d["af"] = 1-d["af"]
    for s in g["stoves"]: s["pt"] = (s["pt"]+1)%10
    for m in g["magic"]:  m["pa"] = (m["pa"]+0.06)%6.28
    for pu in g["pups"]:
        if pu["alive"]: pu["t"] += 1
    for dot in g["dots"]:
        dot[0] += dot[2]; dot[1] += dot[3]
        if not in_map(int(dot[0]),int(dot[1]),rects):
            dot[2] *= -1; dot[3] *= -1
            dot[0] += dot[2]*2; dot[1] += dot[3]*2
    updp()

    # Staubflusen und PUs: immer aktiv
    for d in g["dust"]:
        if d["alive"] and dist(gx,gy,d["x"],d["y"]) < 9:
            d["alive"] = False; g["dgot"] += 1
            addp(d["x"],d["y"],8,[7,6,5],1.5,12); snd(0)

    for pu in g["pups"]:
        if pu["alive"] and dist(gx,gy,pu["x"],pu["y"]) < 11:
            pu["alive"] = False; snd(3); t2 = pu["type"]
            if t2 == PU_L: gh["lt"] = 10*FPS; addp(pu["x"],pu["y"],10,[10,7],2,18)
            elif t2 == PU_S: gh["st"] = 5*FPS; addp(pu["x"],pu["y"],10,[12,7],2,18)
            elif t2 == PU_I: gh["it"] = 5*FPS; addp(pu["x"],pu["y"],10,[6,7],2,18)
            elif t2 == PU_H:
                if g["lives"] < MAX_LIVES: g["lives"] += 1
                addp(pu["x"],pu["y"],10,[8,9,7],2,18)

    # Hindernisse und Waerter NUR wenn nicht immun
    if g["imm"] <= 0:
        for v in g["vasen"]:
            if v["alive"] and dist(gx,gy,v["x"],v["y"]) < 10:
                v["alive"] = False; g["lives"] -= 1; g["slow"] = 45
                addp(v["x"],v["y"],10,[9,5,7],2.5,16)
                alrt(v["x"],v["y"]); snd(1)
                if g["lives"] <= 0: snd(4); over("Vase zerbrochen!"); return

        for c in g["chains"]:
            if c["alive"] and dist(gx,gy,c["x"],c["y"]) < 12:
                c["alive"] = False; g["boost"] = 90; g["slow"] = 30
                alrt(c["x"],c["y"]); addp(c["x"],c["y"],6,[6,7],1.5,12); snd(2)

        for td in g["traps"]:
            if td["alive"] and dist(gx,gy,td["x"],td["y"]) < 9:
                snd(4); over("In die Falltuer gestuerzt!"); return

        for s in g["stoves"]:
            if s["alive"] and dist(gx,gy,s["x"],s["y"]) < 10:
                g["lives"] -= 1; g["slow"] = 60
                alrt(s["x"],s["y"]); addp(s["x"],s["y"],8,[8,9,7],1.5,12); snd(1)
                nx,ny = mv(gh["x"],gh["y"],8,0,rects)
                gh["x"] = nx; gh["y"] = ny
                if g["lives"] <= 0: snd(4); over("Auf Herdplatte verbrannt!"); return

        for m in g["magic"]:
            if m["alive"] and dist(gx,gy,m["x"],m["y"]) < 9:
                m["alive"] = False; g["lives"] -= 1; g["slow"] = 45
                addp(m["x"],m["y"],16,[13,14,7],3,22); snd(1)
                for gd in g["guards"]:
                    np2 = rpos(rects,[(gx,gy)],20)
                    gd["x"] = np2[0]; gd["y"] = np2[1]
                    gd["fx"] = 0.0; gd["fy"] = 0.0
                    gd["alert_t"] = 0; gd["tx"] = None; gd["ty"] = None
                    gd["ch"] = True; gd["lsx"] = gx; gd["lsy"] = gy; gd["srt"] = 60
                if g["lives"] <= 0: snd(4); over("Magische Falle!"); return

        if not inv:
            for gd in g["guards"]:
                if dist(gx,gy,gd["x"],gd["y"]) < 14:
                    snd(4); over("Vom Waerter gefangen!"); return

    g["tf"] -= 1
    if g["tf"] <= 0: snd(4); over("Zeit abgelaufen!"); return

    if g["dgot"] >= g["dtot"]:
        g["done"][g["lvl"]] = True
        done = sum(g["done"])
        if done > g["hs"]: g["hs"] = done
        g["wt"] = 0
        if g["lvl"] >= 19: snd(6); g["mode"] = M_WGAME
        else: snd(5); g["mode"] = M_WLVL

# ── UPDATE HAUPT ────────────────────────────────────────────────
def update():
    g["tick"] = (g["tick"]+1)%3600
    g["ict"] = (g["ict"]+1)%8
    m = g["mode"]
    if m == M_MENU:
        if mbtn(88,116,80,24): g["mode"] = M_LSEL
        if mbtn(88,148,80,24): g["mode"] = M_TUT
    elif m == M_TUT:
        if pyxel.btn(pyxel.KEY_DOWN) or pyxel.btn(pyxel.KEY_S):
            g["scroll"] = min(g["scroll"]+2,140)
        if pyxel.btn(pyxel.KEY_UP) or pyxel.btn(pyxel.KEY_W):
            g["scroll"] = max(g["scroll"]-2,0)
        if mbtn(88,236,80,16): g["mode"] = M_MENU; g["scroll"] = 0
        if pyxel.btnp(pyxel.KEY_ESCAPE): g["mode"] = M_MENU; g["scroll"] = 0
    elif m == M_LSEL:
        if mbtn(88,232,80,16): g["mode"] = M_MENU; return
        for i in range(20):
            c = i%4; r = i//4; bx = 6+c*62; by = 24+r*44
            if mbtn(bx,by,58,40): g["lives"] = 3; load(i); return
    elif m == M_READY:
        if mbtn(78,190,100,26): rkeys(); g["mode"] = M_PLAY
        if pyxel.btnp(pyxel.KEY_SPACE): rkeys(); g["mode"] = M_PLAY
        if pyxel.btnp(pyxel.KEY_ESCAPE): g["mode"] = M_LSEL
    elif m == M_PLAY:
        upd()
    elif m == M_PAUSE:
        if pyxel.btnp(pyxel.KEY_P) or pyxel.btnp(pyxel.KEY_ESCAPE):
            rkeys(); g["mode"] = M_PLAY
        if mbtn(88,118,80,20): rkeys(); g["mode"] = M_PLAY
        if mbtn(88,148,80,20): rkeys(); g["mode"] = M_MENU
    elif m == M_OVER:
        if pyxel.btnp(pyxel.KEY_R): g["lives"] = 3; load(g["lvl"]); return
        if mbtn(70,118,116,20): g["lives"] = 3; load(g["lvl"])
        if mbtn(70,148,116,20): rkeys(); g["mode"] = M_MENU
    elif m == M_WLVL:
        g["wt"] += 1
        if g["wt"] > 20 and (pyxel.btnp(pyxel.KEY_SPACE) or pyxel.btnp(pyxel.MOUSE_BUTTON_LEFT)):
            load(g["lvl"]+1)
    elif m == M_WGAME:
        g["wt"] += 1
        if mbtn(48,200,80,20): g["done"] = [False]*20; g["lives"] = 3; load(0)
        if mbtn(132,200,80,20): rkeys(); g["mode"] = M_MENU

# ── DRAW SPIELFELD ──────────────────────────────────────────────
def drw_play():
    if g["gh"] is None: return
    gh = g["gh"]; gx = gh["x"]; gy = gh["y"]
    sr = SIGHT_BIG if gh["lt"] > 0 else SIGHT
    sr2 = sr*sr
    rects = g["rects"]; lvl = g["lvl"]
    c1 = LEVELS[lvl][11]; c2 = LEVELS[lvl][12]; t = g["tick"]
    drw_bg(gx,gy,sr,rects,c1,c2,t)
    for dot in g["dots"]:
        dx2 = int(dot[0])-gx; dy2 = int(dot[1])-gy
        if dx2*dx2+dy2*dy2 < sr2:
            pyxel.pset(int(dot[0]),int(dot[1]),5)
    sr_t2 = (sr*65//100)*(sr*65//100)
    for td in g["traps"]:
        if td["alive"] and (td["x"]-gx)*(td["x"]-gx)+(td["y"]-gy)*(td["y"]-gy) < sr_t2:
            drw_trap(td)
    for v in g["vasen"]:
        if v["alive"] and d2(gx,gy,v["x"],v["y"]) < sr2: drw_vase(v)
    for c in g["chains"]:
        if c["alive"] and d2(gx,gy,c["x"],c["y"]) < sr2: drw_chain(c)
    for s in g["stoves"]:
        if s["alive"] and d2(gx,gy,s["x"],s["y"]) < sr2: drw_stove(s)
    for m in g["magic"]:
        if m["alive"] and d2(gx,gy,m["x"],m["y"]) < sr2: drw_magic(m)
    for d in g["dust"]:
        if d["alive"] and d2(gx,gy,d["x"],d["y"]) < sr2: drw_dust(d)
    for pu in g["pups"]:
        if pu["alive"] and d2(gx,gy,pu["x"],pu["y"]) < sr2: drw_pu(pu)
    for gd in g["guards"]:
        if d2(gx,gy,gd["x"],gd["y"]) < sr2: drw_guard(gd)
    drwp(gx,gy,sr); drw_ghost()
    sr_s = (sr-7)*(sr-7)
    for y in range(H):
        for x in range(W):
            dv = (x-gx)*(x-gx)+(y-gy)*(y-gy)
            if dv > sr2: pyxel.pset(x,y,0)
            elif dv > sr_s and (x+y)%2 == 0: pyxel.pset(x,y,0)
    drw_hud()

# ── DRAW SCREENS ────────────────────────────────────────────────
def drw_menu():
    t = g["tick"]
    for row in range(168):
        pyxel.line(0,row,W,row,1 if 28 < row < 148 else 0)
    random.seed(42)
    for i in range(100):
        sx = random.randint(0,W); sy = random.randint(0,145)
        ph = random.randint(0,28); sp2 = random.randint(8,22)
        col = 7 if (t+ph)//sp2%3 == 0 else (6 if (t+ph)//sp2%3 == 1 else 5)
        pyxel.pset(sx,sy,col)
        if random.random() < 0.15 and col == 7:
            pyxel.pset(sx+1,sy,5); pyxel.pset(sx-1,sy,5)
            pyxel.pset(sx,sy+1,5); pyxel.pset(sx,sy-1,5)
    random.seed()
    sp3 = (t*2)%310; sx2 = sp3-22; sy2 = 10+sp3//8
    if 0 <= sx2 < W-12:
        for i in range(8):
            if 0 <= sx2-i < W: pyxel.pset(sx2-i,sy2-i//2,[7,7,6,6,5,5,0,0][i])
    pyxel.circ(210,34,18,7); pyxel.circ(222,27,13,1)
    pyxel.pset(203,36,6); pyxel.pset(206,41,6)
    if (t//22)%2 == 0: pyxel.circb(210,34,20,6)
    co = int((t*0.2)%320)-40
    for cxc,cyc,cr in [(co,26,8),(co+18,23,12),(co+38,28,7),
                       (co+125,19,10),(co+148,16,13),(co+170,21,8)]:
        if -15 < cxc < W+5:
            pyxel.circ(cxc,cyc,cr//3,1)
            pyxel.circ(cxc+cr//3,cyc-2,cr//4,1)
            pyxel.circ(cxc+cr//2,cyc,cr//3,1)
    pyxel.rect(46,162,164,98,1)
    pyxel.rect(36,152,24,108,5); pyxel.rect(196,152,24,108,5)
    pyxel.rect(86,138,22,122,3); pyxel.rect(148,138,22,122,3)
    for i in range(4):
        pyxel.rect(87+i*5,134,4,6,5); pyxel.pset(89+i*5,134,3)
        pyxel.rect(147+i*5,134,4,6,5); pyxel.pset(149+i*5,134,3)
    for i in range(9):
        pyxel.rect(49+i*18,158,10,6,5); pyxel.pset(54+i*18,158,3)
    for i in range(4):
        pyxel.rect(37+i*5,148,4,6,5); pyxel.rect(197+i*5,148,4,6,5)
    for tx,ty in [(97,138),(157,138),(47,152),(207,152)]:
        pyxel.tri(tx,ty,tx+10,ty,tx+5,ty-12,2)
        pyxel.line(tx,ty,tx+10,ty,3)
    pyxel.rect(108,200,40,60,0); pyxel.circ(128,200,20,0)
    pyxel.rectb(108,200,40,60,5)
    wins = [(102,167,5),(122,167,7),(142,167,9),(162,167,11),(102,182,7),(162,182,13)]
    for idx2,(fx,fy,fr) in enumerate(wins):
        wc = 7 if (t//fr)%2 == 0 else 6
        pyxel.rect(fx,fy,6,8,wc); pyxel.pset(fx+1,fy+1,min(7,wc+2))
    pyxel.line(46,162,210,162,3)
    wind = int(math.sin(t*0.018)*2)
    random.seed(11)
    for tx in range(-7,W+14,12):
        th = random.randint(14,28); wv = wind if th > 20 else wind//2
        pyxel.line(tx+5,H,tx+5,H-th//4,5)
        pyxel.tri(tx,H-th//4,tx+5+wv,H-th,tx+11,H-th//4,2)
        pyxel.tri(tx+1,H-th//4-3,tx+5+wv,H-th-3,tx+10,H-th//4-3,
                  3 if random.random() < 0.5 else 2)
    random.seed()
    for bi in range(5):
        bx = int((t*(1.4+bi*0.3)+bi*56)%330)-28
        by2 = 68+bi*11+int(math.sin(t*0.05+bi*1.1)*7)
        wp = (t+bi*6)%12
        wy = -2 if wp < 3 else (0 if wp < 6 else (2 if wp < 9 else 0))
        if not (-12 < bx < W+12): continue
        pyxel.pset(bx,by2,5); pyxel.pset(bx+1,by2,5)
        for sg in range(3):
            wyo = wy*(sg+1)//3
            pyxel.pset(bx-(sg+1)*2,by2+wyo,5)
            pyxel.pset(bx+(sg+1)*2+1,by2+wyo,5)
        pyxel.pset(bx-6,by2+wy//2-1,5)
        pyxel.pset(bx+7,by2+wy//2-1,5)
        pyxel.pset(bx,by2-2,5); pyxel.pset(bx+1,by2-2,5)
    pyxel.rect(8,26,240,36,0); pyxel.rectb(8,26,240,36,6)
    pyxel.line(9,27,246,27,6)
    col1 = 7 if (t//16)%2 == 0 else 6
    ctxt("GEISTERSTUNDE",34,col1); ctxt("IM DUNKELSCHLOSS",46,6)
    drw_btn(88,116,80,24,"SPIELEN",2,6)
    drw_btn(88,148,80,24,"TUTORIAL",1,6)
    if g["hs"] > 0:
        pyxel.text(6,H-10,"BESTLEISTUNG: "+str(g["hs"])+" Level",5)
    gby = 228+int(math.sin(t*0.07)*6); af_m = (t//7)%4
    bo = [0,1,-1,0][af_m]; tax2 = [0,2,-2,0][af_m]
    pyxel.circ(234,gby+bo,6,1); pyxel.rect(228,gby+bo,13,5,1)
    pyxel.circ(230,gby+3+bo,4,1); pyxel.circ(238,gby+3+bo,4,1)
    pyxel.circ(234,gby+bo,5,12); pyxel.rect(229,gby+bo,11,5,12)
    pyxel.circ(231,gby+3+bo,3,12); pyxel.circ(237,gby+3+bo,3,12)
    pyxel.pset(231,gby-2+bo,7); pyxel.pset(232,gby-3+bo,7)
    pyxel.pset(232,gby-1+bo,0); pyxel.pset(232,gby-2+bo,0)
    pyxel.pset(236,gby-1+bo,0); pyxel.pset(236,gby-2+bo,0)
    pyxel.pset(231,gby-2+bo,7); pyxel.pset(237,gby-2+bo,7)
    for ox,oy in [(-2,1),(-1,2),(0,2),(2,1),(1,2)]:
        pyxel.pset(234+ox,gby+oy+bo,0)
    for tx3 in range(-2,3):
        pyxel.pset(234+tx3+tax2,gby+7+bo,12 if abs(tx3) < 2 else 1)
    if t%4 < 2: pyxel.circb(234,gby+bo,7,1)

def drw_ready():
    pyxel.cls(0); t = g["tick"]; lvl = g["lvl"]
    name = LEVELS[lvl][0]; nd = LEVELS[lvl][1]
    ng = LEVELS[lvl][3]; ts = LEVELS[lvl][2]
    random.seed(lvl+5)
    for _ in range(60):
        pyxel.pset(random.randint(0,W),random.randint(0,H),random.choice([5,5,6,0]))
    random.seed()
    pyxel.rect(20,36,216,188,1); pyxel.rectb(20,36,216,188,6)
    pyxel.line(21,37,234,37,6)
    col_n = 7 if (t//10)%2 == 0 else 12
    ctxt("LEVEL "+str(lvl+1),46,col_n)
    nx2 = (W-len(name)*4)//2; pyxel.text(nx2,58,name,6)
    pyxel.line(21,70,234,70,5)
    pyxel.text(30,78,"ZIEL:",7)
    pyxel.text(30,90,str(nd)+" Staubflusen einsammeln",6)
    pyxel.text(30,106,"GEGNER:",7)
    pyxel.text(30,118,str(ng)+" Waerter patrouillieren",6)
    pyxel.text(30,134,"ZEIT:",7)
    pyxel.text(30,146,str(ts)+" Sekunden verfuegbar",6)
    pyxel.line(21,162,234,162,5)
    pyxel.text(30,170,"3 Sek. Immunitat beim Start!",5)
    pulse = 1 if (t//6)%2 == 0 else 0
    drw_btn(78,190-pulse,100,26,"SPIELEN  [SPACE]",2,6)
    pyxel.text(86,224,"ESC = Levelauswahl",5)

def drw_lsel():
    t = g["tick"]; ia = g["ict"]
    pyxel.cls(1)
    random.seed(33)
    for _ in range(40):
        pyxel.pset(random.randint(0,W),random.randint(0,H),random.choice([0,0,0,5]))
    random.seed()
    pyxel.rect(4,4,W-8,18,0); pyxel.rectb(4,4,W-8,18,6)
    pyxel.line(5,5,W-5,5,6); ctxt("LEVELAUSWAHL",8,7)
    if g["hs"] > 0: pyxel.text(W-58,9,"HS:"+str(g["hs"]),5)
    mx2 = pyxel.mouse_x; my2 = pyxel.mouse_y
    for i in range(20):
        c = i%4; r = i//4; bx = 6+c*62; by = 24+r*44; bw = 58; bh = 40
        done = g["done"][i]; hov = (bx <= mx2 <= bx+bw and by <= my2 <= by+bh)
        pyxel.rect(bx+2,by+2,bw,bh,0)
        bg = 2 if done else 1
        for row in range(bh): pyxel.line(bx,by+row,bx+bw-1,by+row,bg)
        bd = 6 if done else 5
        pyxel.rectb(bx,by,bw,bh,0); pyxel.rectb(bx+1,by+1,bw-2,bh-2,bd)
        pyxel.line(bx+2,by+2,bx+bw-3,by+2,6 if done else bd)
        pyxel.rect(bx+2,by+2,12,12,0); pyxel.rectb(bx+2,by+2,12,12,bd)
        drw_icon(LICO[i],bx+3,by+3,(ia+i*3)%8)
        pyxel.text(bx+16,by+4,str(i+1),7)
        pyxel.text(bx+16,by+13,LSH[i],7)
        if done:
            sx = bx+bw-9; sy = by+bh-11; sp = (t//8+i)%4
            pyxel.pset(sx+1,sy,7 if sp < 2 else 6)
            pyxel.pset(sx,sy+1,7); pyxel.pset(sx+2,sy+1,7); pyxel.pset(sx+1,sy+2,7)
        if hov:
            pyxel.rectb(bx,by,bw,bh,7); pyxel.rectb(bx+1,by+1,bw-2,bh-2,7)
    drw_btn(88,232,80,16,"ZURUCK",1,6)

def drw_tut():
    sy = g["scroll"]
    pyxel.cls(1)
    random.seed(77)
    for _ in range(30):
        pyxel.pset(random.randint(0,W),random.randint(0,H),random.choice([0,0,0,5]))
    random.seed()
    pyxel.rect(0,0,W,20,0); pyxel.line(0,20,W,20,5)
    ctxt("TUTORIAL",3,7); pyxel.text(2,11,"Auf/Ab = Scrollen",5)
    def tp(x,y): return x,y-sy+22
    def tt(x,y,s,col=6):
        tx2,ty2 = tp(x,y)
        if 22 <= ty2 < H-22: pyxel.text(tx2,ty2,s,col)
    def sec(y,lbl):
        tx2,ty2 = tp(4,y)
        if 22 <= ty2 < H-22:
            pyxel.rect(tx2,ty2-1,W-8,10,0)
            pyxel.line(tx2,ty2-1,W-4,ty2-1,6)
            pyxel.text(tx2,ty2,lbl,7)
            pyxel.line(tx2,ty2+8,W-4,ty2+8,5)
    def ent(y,ic,title,desc):
        tx2,ty2 = tp(14,y)
        if 22 <= ty2 < H-22:
            pyxel.circb(tx2-6,ty2+3,4,ic); pyxel.pset(tx2-6,ty2+3,ic)
            pyxel.text(tx2,ty2,title,7); pyxel.text(tx2,ty2+9,desc,5)
    sec(0,"STEUERUNG")
    tt(8,12,"Pfeiltasten / WASD = Bewegen",7)
    tt(8,22,"P=Pause   R=Neustart   SPACE=Start",6)
    sec(38,"CHARAKTERE")
    ent(50,12,"GEIST (du)","Sammle alle Staubflusen ein!")
    ent(74,7,"STAUBFLUSE","Weisse Kneuel – alle einsammeln!")
    ent(98,8,"WAERTER","Sicht 50px, verfolgt und faengt dich!")
    sec(122,"HINDERNISSE")
    ent(134,9,"VASE","-1 Leben + Laerm + kurz LANGSAM")
    ent(158,6,"KETTE","Waerter 50% schneller + du LANGSAM")
    ent(182,0,"FALLTUER","Im Dunkeln kaum sichtbar = Tod!")
    ent(206,8,"HERDPLATTE","-1 Leben + Laerm + LANGSAM (aktiv)")
    ent(230,13,"MAGISCHE FALLE","-1 Leben + alle Waerter kommen!")
    sec(258,"POWER-UPS")
    ent(270,10,"LICHT (10s)","Sichtfeld stark vergroessert")
    ent(294,12,"SPEED (5s)","Doppelte Geschwindigkeit")
    ent(318,6,"UNSICHTBAR (5s)","Waerter sehen dich nicht")
    ent(342,8,"HERZ","+1 Leben (maximal 5)")
    sec(368,"TIPPS")
    tt(8,380,"Du bist schneller als Waerter!",7)
    tt(8,390,"Beim Start: 3 Sek. Immunitat!",6)
    tt(8,400,"Falltuer: erst spaet sichtbar!",6)
    total = 420; vis = H-44; smax = total-vis
    if total > vis:
        bh2 = max(16,(vis*vis)//total)
        bary = 22+(sy*(vis-bh2))//(smax if smax > 0 else 1)
        pyxel.rect(W-4,22,4,vis,0); pyxel.rect(W-4,bary,4,bh2,5)
    pyxel.line(0,H-22,W,H-22,5)
    drw_btn(88,H-20,80,16,"ZURUCK",1,6)

def drw_pause():
    for x in range(52,204):
        for y in range(90,188):
            if (x+y)%2 == 0: pyxel.pset(x,y,0)
    pyxel.rect(64,96,128,90,1); pyxel.rectb(64,96,128,90,6)
    pyxel.line(65,97,190,97,6); ctxt("PAUSE",104,7)
    drw_btn(88,118,80,20,"WEITER",2,6)
    drw_btn(88,148,80,20,"ZUM MENUE",1,6)
    pyxel.text(72,176,"ESC oder P = Weiter",5)

def drw_over():
    pyxel.cls(0); t = g["tick"]
    col = 8 if (t//5)%2 == 0 else 6
    pyxel.rect(20,56,216,110,1); pyxel.rectb(20,56,216,110,6)
    pyxel.line(21,57,234,57,6); ctxt("GAME  OVER",64,col)
    pyxel.line(22,76,233,76,5)
    r = g["why"]; pyxel.text(max(4,(W-len(r)*4)//2),82,r,7)
    pyxel.line(22,94,233,94,5)
    if g["hs"] > 0:
        hs = "Best: "+str(g["hs"])+" Lv"
        pyxel.text((W-len(hs)*4)//2,100,hs,5)
    drw_btn(70,118,116,20,"NEU STARTEN",8,6)
    drw_btn(70,148,116,20,"ZUM MENUE",1,6)
    pyxel.text(54,176,"R = schnell neu starten",5)

def drw_wlvl():
    pyxel.cls(0); t = g["wt"]; tick = g["tick"]
    random.seed(tick//6)
    for _ in range(60):
        pyxel.pset(random.randint(0,W),random.randint(0,H),random.choice([0,0,5,6]))
    random.seed()
    for ri in range(3):
        r2 = 6+ri*10+(t*3)%38; col = [12,6,7][ri]
        if r2 < 65:
            for ai in range(10):
                ang = ai*math.pi/5
                rx = 128+int(math.cos(ang)*r2); ry = 110+int(math.sin(ang)*r2)
                if 0 <= rx < W and 0 <= ry < H: pyxel.pset(rx,ry,col)
    pyxel.rect(34,70,188,118,1); pyxel.rectb(34,70,188,118,6)
    pyxel.line(35,71,220,71,6)
    if t > 3: ctxt("LEVEL GESCHAFFT!",80,7)
    if t > 8: pyxel.line(35,94,220,94,5)
    if t > 12:
        lvl = g["lvl"]; name = LEVELS[lvl][0]
        pyxel.text(44,100,"Raum: "+name,6)
        pyxel.text(44,112,str(g["dgot"])+" Staubflusen gesammelt",6)
    if t > 20:
        pyxel.line(35,128,220,128,5)
        pyxel.text(44,134,"Naechster Raum wartet...",7)
        pyxel.text(44,148,"Leertaste oder Klick!",6)
        for si in range(6):
            sa = si*math.pi/3+tick*0.1; sr2 = 22+(tick%20)
            sx = 128+int(math.cos(sa)*sr2); sy = 110+int(math.sin(sa)*sr2)
            if 35 <= sx <= 220 and 70 <= sy <= 185:
                pyxel.pset(sx,sy,7 if (tick+si)%3 == 0 else 6)

def drw_wgame():
    pyxel.cls(0); t = g["wt"]; tick = g["tick"]
    random.seed(tick//8)
    for _ in range(70):
        pyxel.pset(random.randint(0,W),random.randint(0,H),random.choice([0,0,0,5,6]))
    random.seed()
    for fi in range(5):
        fa = fi*math.pi*2/5+tick*0.07; fr = 26+fi*8+t%35
        if fr < 90:
            for ai in range(8):
                ang = fa+ai*math.pi/4
                fx = 128+int(math.cos(ang)*fr); fy = 116+int(math.sin(ang)*fr)
                if 0 <= fx < W and 0 <= fy < H: pyxel.pset(fx,fy,[7,6,12,5,6][fi])
    pyxel.rect(18,46,220,130,0); pyxel.rectb(18,46,220,130,6)
    pyxel.line(19,47,236,47,6)
    gby = 92+int(math.sin(tick*0.08)*4)
    pyxel.circ(128,gby,11,1); pyxel.rect(117,gby+2,23,8,1)
    pyxel.circ(120,gby+6,7,1); pyxel.circ(128,gby+7,7,1); pyxel.circ(136,gby+6,7,1)
    pyxel.circ(128,gby,10,12); pyxel.rect(118,gby+2,21,8,12)
    pyxel.circ(121,gby+6,6,12); pyxel.circ(128,gby+7,6,12); pyxel.circ(135,gby+6,6,12)
    pyxel.pset(124,gby-2,0); pyxel.pset(132,gby-2,0); pyxel.pset(128,gby+1,0)
    pyxel.pset(122,gby-3,7); pyxel.pset(134,gby-3,7)
    ctxt("HERZLICHEN",120,7); ctxt("GLUECKWUNSCH!",132,6)
    pyxel.text(22,148,"Alle 20 Level gemeistert!",7)
    if g["hs"] > 0:
        hs = "Bestleistung: "+str(g["hs"])+" Level"
        pyxel.text((W-len(hs)*4)//2,160,hs,5)
    drw_btn(48,200,80,20,"NEUES SPIEL",2,6)
    drw_btn(132,200,80,20,"ZUM MENUE",1,6)

# ── DRAW HAUPT ──────────────────────────────────────────────────
def draw():
    pyxel.cls(0); m = g["mode"]
    if   m == M_MENU:  drw_menu()
    elif m == M_TUT:   drw_tut()
    elif m == M_LSEL:  drw_lsel()
    elif m == M_READY: drw_ready()
    elif m == M_PLAY:  drw_play()
    elif m == M_WLVL:  drw_wlvl()
    elif m == M_PAUSE: drw_play(); drw_pause()
    elif m == M_OVER:  drw_over()
    elif m == M_WGAME: drw_wgame()

# ── START (pyxelstudio.net: kein title=, init+run am Ende) ──────
pyxel.init(W,H,fps=FPS)
pyxel.mouse(True)
setup_snd()
pyxel.run(update,draw)