# Pyxel Studio – Ice Cave Explorer (Enhanced)

import pyxel
import random
import math
from collections import deque

# ── Konstanten ────────────────────────────────────────────────────────────────
TILE  = 8
COLS  = 30
ROWS  = 24
W     = COLS * TILE
H     = ROWS * TILE

COL_BG        = 1
COL_BG2       = 0
COL_WALL      = 5
COL_WALL_HL   = 6
COL_CRYSTAL   = [12, 10, 14, 15]
COL_ENEMY     = 12
COL_ENEMY_EYE = 8
COL_EXIT_OFF  = 3
COL_EXIT_ON   = 11
COL_SNOW      = 6
COL_HUD_BG    = 0
COL_HUD_TEXT  = 7
COL_HUD_ACC   = 12
COL_TITLE     = 15

AVATARS = [
    {"name": "FORSCHER", "titel": "Der Entdecker",      "preis": 0,    "head": 7,  "helmet": 12, "body": 13, "style": 0},
    {"name": "NINJA",    "titel": "Schatten d.Hoehle",  "preis": 300,  "head": 5,  "helmet": 8,  "body": 0,  "style": 1},
    {"name": "ROBOTER",  "titel": "Eismaschine 3000",   "preis": 600,  "head": 6,  "helmet": 10, "body": 5,  "style": 2},
    {"name": "GEIST",    "titel": "Seele d.Gletschers", "preis": 900,  "head": 15, "helmet": 7,  "body": 1,  "style": 3},
    {"name": "FEUER",    "titel": "Meister d.Chaos",    "preis": 1500, "head": 9,  "helmet": 8,  "body": 4,  "style": 4},
]

# ── Sounds initialisieren ─────────────────────────────────────────────────────
def init_sounds():
    # Sound 0: Kristall einsammeln – heller Kling-Ton
    s = pyxel.sound(0)
    s.set("e4e4g4", "ttt", "765", "nnn", 8)

    # Sound 1: Spieler trifft Schaden – tiefer Bumm
    s = pyxel.sound(1)
    s.set("c2b1a1", "nnn", "765", "fff", 10)

    # Sound 2: Level abgeschlossen – freudige Melodie mit Fanfare
    s = pyxel.sound(2)
    s.set("c3e3g3c4e4g4c4", "ttttttt", "7654321", "nnnnnnn", 7)

    # Sound 3: Menü-Navigation – kurzes Klick
    s = pyxel.sound(3)
    s.set("a3", "p", "5", "f", 4)

    # Sound 4: Kauf erfolgreich – Fanfare
    s = pyxel.sound(4)
    s.set("c3e3g3", "ttt", "765", "nnn", 6)

    # Sound 5: Kauf fehlgeschlagen – Fehler-Ton
    s = pyxel.sound(5)
    s.set("c2b1", "nn", "54", "ff", 8)

    # Sound 6: Spieler stirbt (ein Leben verloren) – dramatischer Einbruch
    s = pyxel.sound(6)
    s.set("g3f3e3d3c3b2a2g2", "nnnnnnnn", "76665544", "ffffffff", 10)

    # Sound 7: Game Over – langer dramatischer Absturz mit Echo
    s = pyxel.sound(7)
    s.set("e3d3c3b2a2g2f2e2d2c2", "nnnnnnnnnn", "7666554433", "ffffffffff", 12)

    # Sound 8: Neuer Highscore! – triumphale Melodie
    s = pyxel.sound(8)
    s.set("c3e3g3c4g3e3c4", "ttttttt", "7777777", "nnnnnnn", 6)

    # Sound 9: Level-Übergang Jingle – kurze aufsteigende Sequenz
    s = pyxel.sound(9)
    s.set("c3d3e3f3g3a3b3c4", "tttttttt", "77777777", "nnnnnnnn", 8)

# ── Avatar zeichnen (detaillierter) ──────────────────────────────────────────
def draw_avatar(x, y, avatar, anim=0.0, moving=False, invincible=0, frame=0):
    if invincible > 0 and (invincible // 4) % 2 == 0:
        return
    bob = int(math.sin(anim) * (1 if moving else 0))
    s  = avatar["style"]
    hd = avatar["head"]
    hm = avatar["helmet"]
    bd = avatar["body"]

    if s == 0:  # Forscher – Abenteurer mit Helm und Rucksack
        # Beine mit Animation
        if moving and (frame // 4) % 2 == 0:
            pyxel.rect(x+2, y+8+bob, 2, 3, bd)
            pyxel.rect(x+5, y+7+bob, 2, 2, bd)
        else:
            pyxel.rect(x+2, y+8+bob, 2, 2, bd)
            pyxel.rect(x+5, y+8+bob, 2, 2, bd)
        # Körper mit Jacke-Detail
        pyxel.rect(x+1, y+4+bob, 6, 5, bd)
        pyxel.pset(x+3, y+5+bob, 13)   # Jackenknopf 1
        pyxel.pset(x+3, y+7+bob, 13)   # Jackenknopf 2
        # Rucksack rechts
        pyxel.rect(x+6, y+5+bob, 2, 3, 4)
        pyxel.pset(x+6, y+5+bob, 9)
        # Arme
        if moving and (frame // 4) % 2 == 0:
            pyxel.pset(x, y+5+bob, bd); pyxel.pset(x+7, y+6+bob, bd)
        else:
            pyxel.pset(x, y+6+bob, bd); pyxel.pset(x+7, y+5+bob, bd)
        # Kopf
        pyxel.rect(x+1, y+bob, 6, 5, hd)
        # Helm mit Krempe (vor Gesicht, damit Helm oben liegt)
        pyxel.rect(x+1, y+bob, 6, 2, hm)
        pyxel.pset(x, y+1+bob, hm); pyxel.pset(x+7, y+1+bob, hm)
        pyxel.pset(x+3, y-1+bob, 7)
        # Gesicht: neugierige Augen + breites Lächeln
        pyxel.pset(x+2, y+2+bob, 7); pyxel.pset(x+5, y+2+bob, 7)  # Augen weiß
        pyxel.pset(x+2, y+3+bob, 0); pyxel.pset(x+5, y+3+bob, 0)  # Pupillen
        pyxel.pset(x+2, y+4+bob, 0); pyxel.pset(x+5, y+4+bob, 0)  # Mundwinkel
        pyxel.pset(x+3, y+5+bob, 0); pyxel.pset(x+4, y+5+bob, 0)  # Mundmitte

    elif s == 1:  # Ninja – schlanke dunkle Gestalt mit Schal
        # Beine
        if moving and (frame // 3) % 2 == 0:
            pyxel.pset(x+2, y+9+bob, bd); pyxel.pset(x+5, y+8+bob, bd)
            pyxel.rect(x+2, y+7+bob, 2, 2, bd); pyxel.rect(x+5, y+7+bob, 1, 1, bd)
        else:
            pyxel.rect(x+2, y+7+bob, 4, 3, bd)
        # Körper schmal
        pyxel.rect(x+2, y+3+bob, 4, 5, bd)
        pyxel.rect(x+1, y+4+bob, 1, 3, bd)  # linker Arm
        pyxel.rect(x+6, y+4+bob, 1, 3, bd)  # rechter Arm
        # Schal flatternd
        sc_bob = (frame // 6) % 3
        pyxel.pset(x+1, y+3+bob-sc_bob, hm)
        pyxel.pset(x+6, y+4+bob-sc_bob, hm)
        # Kopf
        pyxel.rect(x+1, y+bob, 6, 4, hd)
        # Maske (untere Hälfte bedeckt)
        pyxel.rect(x+1, y+2+bob, 6, 2, hm)
        # Gesicht: schmale ernste Augen über der Maske
        pyxel.pset(x+2, y+1+bob, 0); pyxel.pset(x+5, y+1+bob, 0)   # Augen dunkel
        pyxel.pset(x+2, y+bob, hd);  pyxel.pset(x+5, y+bob, hd)    # Augenbrauen (hautfarben = Lücke im Stirnband)
        # Augenbrauen zusammengezogen (ernst)
        pyxel.pset(x+3, y+bob, 0); pyxel.pset(x+4, y+bob, 0)
        # Mund hinter Maske angedeutet (Nasenfalten sichtbar)
        pyxel.pset(x+3, y+2+bob, hd); pyxel.pset(x+4, y+2+bob, hd)
        # Shuriken auf Rücken
        pyxel.pset(x+7, y+3+bob, 7)
        pyxel.pset(x+6, y+2+bob, 7)
        # Kopfband
        pyxel.pset(x+1, y+bob, bd); pyxel.pset(x+6, y+bob, bd)
        # Ninja-Stirnband-Zeichen
        pyxel.pset(x+3, y-1+bob, bd); pyxel.pset(x+4, y-1+bob, bd)

    elif s == 2:  # Roboter – eckig, mechanisch, LED-Details
        # Beine eckig
        if moving and (frame // 5) % 2 == 0:
            pyxel.rect(x+1, y+8+bob, 3, 3, bd); pyxel.rect(x+5, y+7+bob, 3, 2, hd)
        else:
            pyxel.rect(x+1, y+8+bob, 3, 2, bd); pyxel.rect(x+5, y+8+bob, 3, 2, bd)
        # Körper eckig mit Panel-Details
        pyxel.rect(x+1, y+3+bob, 6, 5, bd)
        pyxel.rectb(x+2, y+4+bob, 4, 3, hm)  # Panel-Rahmen
        # LED-Anzeige
        led_col = hm if (frame // 8) % 2 == 0 else 8
        pyxel.pset(x+3, y+5+bob, led_col)
        pyxel.pset(x+5, y+5+bob, led_col)
        # Arme eckig
        pyxel.rect(x, y+4+bob, 1, 4, hd); pyxel.rect(x+7, y+4+bob, 1, 4, hd)
        # Hand-Greifer
        pyxel.pset(x, y+8+bob, hm); pyxel.pset(x+7, y+8+bob, hm)
        # Kopf eckig
        pyxel.rect(x+1, y+bob, 6, 4, hd)
        # Augen als große LED-Blöcke
        eye_col = hm if (frame // 10) % 3 != 0 else 8
        pyxel.rect(x+2, y+1+bob, 2, 2, eye_col)
        pyxel.rect(x+5, y+1+bob, 2, 2, eye_col)
        # Pupille (dunkler Punkt in der Mitte jedes Auges)
        pyxel.pset(x+3, y+2+bob, 0); pyxel.pset(x+6, y+2+bob, 0)
        # Mund als LED-Balken (animiert wie ein Display)
        mouth_phase = (frame // 12) % 3
        if mouth_phase == 0:   # gerade Linie
            pyxel.line(x+2, y+3+bob, x+6, y+3+bob, hm)
        elif mouth_phase == 1: # Lächeln
            pyxel.pset(x+2, y+3+bob, hm); pyxel.pset(x+6, y+3+bob, hm)
            pyxel.pset(x+3, y+4+bob, hm); pyxel.pset(x+5, y+4+bob, hm)
        else:                  # "sprechen" (O-Form)
            pyxel.pset(x+3, y+3+bob, hm); pyxel.pset(x+5, y+3+bob, hm)
            pyxel.pset(x+4, y+4+bob, hm)
        # Antenne
        pyxel.pset(x+4, y-1+bob, hm)
        pyxel.pset(x+3, y-2+bob, hm); pyxel.pset(x+5, y-2+bob, hm)
        # Schrauben
        pyxel.pset(x+1, y+bob, 7); pyxel.pset(x+6, y+bob, 7)

    elif s == 3:  # Geist – transparent, schwebend, ethereal
        # Kein fester Boden – nur schwebend
        float_bob = int(math.sin(anim * 0.7) * 2)
        # Geister-Schwanz
        tail_phase = (frame // 8) % 3
        if   tail_phase == 0: pyxel.pset(x+2, y+9+float_bob, hd); pyxel.pset(x+5, y+10+float_bob, hd)
        elif tail_phase == 1: pyxel.pset(x+3, y+10+float_bob, hd); pyxel.pset(x+6, y+9+float_bob, hd)
        else:                 pyxel.pset(x+2, y+10+float_bob, hd); pyxel.pset(x+4, y+9+float_bob, hd)
        # Körper semi-transparent (Punkte)
        pyxel.rect(x+2, y+2+float_bob, 4, 6, bd)
        pyxel.rectb(x+1, y+1+float_bob, 6, 8, hd)
        # Innen-Glühen
        if (frame // 4) % 2 == 0:
            pyxel.pset(x+3, y+4+float_bob, hm)
            pyxel.pset(x+4, y+6+float_bob, hm)
        # Gesicht: große leuchtende Geisteraugen
        pyxel.pset(x+2, y+3+float_bob, hm); pyxel.pset(x+5, y+3+float_bob, hm)  # Augen Kern
        pyxel.pset(x+2, y+4+float_bob, hm); pyxel.pset(x+5, y+4+float_bob, hm)  # Augen untere Reihe
        pyxel.pset(x+3, y+3+float_bob, 7);  pyxel.pset(x+6, y+3+float_bob, 7)   # Augen-Glanz
        # Wellenförmiger Geister-Mund
        mouth_wave = (frame // 8) % 2
        if mouth_wave == 0:
            pyxel.pset(x+2, y+6+float_bob, hm); pyxel.pset(x+4, y+5+float_bob, hm); pyxel.pset(x+6, y+6+float_bob, hm)
        else:
            pyxel.pset(x+2, y+5+float_bob, hm); pyxel.pset(x+4, y+6+float_bob, hm); pyxel.pset(x+6, y+5+float_bob, hm)
        # Geister-Halo
        if (frame // 6) % 2 == 0:
            pyxel.pset(x+3, y+float_bob, hd); pyxel.pset(x+4, y-1+float_bob, hd)
            pyxel.pset(x+1, y+1+float_bob, hd); pyxel.pset(x+6, y+1+float_bob, hd)
        # Schimmernde Partikel
        if (frame // 12) % 4 == 0:
            pyxel.pset(x + (frame // 3) % 8, y + (frame // 5) % 10 + float_bob, hm)

    elif s == 4:  # Feuer – Flammen-Charakter mit Feuer-Effekten
        # Beine (Feuerfüße)
        if moving and (frame // 4) % 2 == 0:
            pyxel.pset(x+2, y+8+bob, 9); pyxel.pset(x+5, y+7+bob, 8)
            pyxel.rect(x+2, y+6+bob, 2, 2, bd); pyxel.pset(x+5, y+6+bob, bd)
        else:
            pyxel.rect(x+2, y+6+bob, 4, 3, bd)
        # Körper mit Flammen-Muster
        pyxel.rect(x+1, y+3+bob, 6, 4, bd)
        pyxel.pset(x+2, y+4+bob, 9); pyxel.pset(x+5, y+4+bob, 8)  # Flammen-Punkte
        pyxel.pset(x+3, y+5+bob, 10); pyxel.pset(x+4, y+3+bob, 9)
        # Arme mit Feuer-Effekt
        pyxel.pset(x, y+5+bob, bd); pyxel.pset(x+7, y+5+bob, bd)
        arm_fire = (frame // 4) % 3
        if arm_fire == 0: pyxel.pset(x, y+4+bob, 8); pyxel.pset(x+7, y+4+bob, 9)
        elif arm_fire == 1: pyxel.pset(x, y+4+bob, 9); pyxel.pset(x+7, y+4+bob, 10)
        else: pyxel.pset(x, y+4+bob, 10); pyxel.pset(x+7, y+4+bob, 8)
        # Kopf
        pyxel.rect(x+1, y+bob, 6, 4, hd)
        # Helm mit Flammen
        pyxel.rect(x+2, y+1+bob, 4, 2, hm)
        # Gesicht: wütende Augen mit Glut
        pyxel.pset(x+2, y+2+bob, 9); pyxel.pset(x+5, y+2+bob, 9)   # Augen orange
        pyxel.pset(x+2, y+1+bob, 8); pyxel.pset(x+5, y+1+bob, 8)   # Augen-Glut rot
        # Wütende Augenbrauen (schräg nach innen)
        pyxel.pset(x+2, y+bob, 8); pyxel.pset(x+3, y+bob-1+bob, 8)   # linke Braue schräg
        pyxel.pset(x+5, y+bob, 8); pyxel.pset(x+4, y+bob-1+bob, 8)   # rechte Braue schräg
        # Zähne-Grinsen (animiert)
        grin_phase = (frame // 10) % 2
        pyxel.pset(x+2, y+3+bob, 0); pyxel.pset(x+6, y+3+bob, 0)   # Mundwinkel
        if grin_phase == 0:
            pyxel.pset(x+3, y+3+bob, 7); pyxel.pset(x+5, y+3+bob, 7)  # Zähne weiß
            pyxel.pset(x+4, y+4+bob, 7)
        else:
            pyxel.pset(x+3, y+4+bob, 7); pyxel.pset(x+5, y+4+bob, 7)  # Zähne weiß tiefer
        # Flammen aus Kopf (animiert)
        p = (frame // 2) % 4
        flame_x = [x+2, x+3, x+4, x+5]
        flame_y = [y-2+bob, y-3+bob, y-2+bob, y-3+bob]
        for fi in range(4):
            fc = [8, 9, 10, 9][fi]
            offset = (p + fi) % 4
            pyxel.pset(flame_x[fi], flame_y[fi] - offset // 2, fc)
        # Funken
        if (frame // 3) % 5 == 0:
            pyxel.pset(x + random.randint(0, 7), y - random.randint(0, 3) + bob, 10)

# ── Karten-Generator MIT Flood-Fill-Fix ──────────────────────────────────────
def flood_fill_floors(grid, start_r, start_c):
    visited = set()
    queue = deque()
    if grid[start_r][start_c] == 0:
        queue.append((start_r, start_c))
        visited.add((start_r, start_c))
    while queue:
        r, c = queue.popleft()
        for dr, dc in [(-1,0),(1,0),(0,-1),(0,1)]:
            nr, nc = r+dr, c+dc
            if 0 <= nr < ROWS and 0 <= nc < COLS and (nr,nc) not in visited and grid[nr][nc] == 0:
                visited.add((nr, nc))
                queue.append((nr, nc))
    return visited

def connect_map(grid, start_r=2, start_c=2):
    max_iter = 30
    for _ in range(max_iter):
        reachable = flood_fill_floors(grid, start_r, start_c)
        all_floors = {(r,c) for r in range(1, ROWS-1) for c in range(1, COLS-1) if grid[r][c] == 0}
        unreachable = all_floors - reachable
        if not unreachable:
            break
        ur, uc = min(unreachable, key=lambda pos: abs(pos[0]-start_r)+abs(pos[1]-start_c))
        best = min(reachable, key=lambda pos: abs(pos[0]-ur)+abs(pos[1]-uc))
        br, bc = best
        step_c = 1 if bc > uc else -1
        cc = uc
        while cc != bc:
            if 0 < ur < ROWS-1 and 0 < cc < COLS-1:
                grid[ur][cc] = 0
            cc += step_c
        step_r = 1 if br > ur else -1
        rr = ur
        while rr != br:
            if 0 < rr < ROWS-1 and 0 < bc < COLS-1:
                grid[rr][bc] = 0
            rr += step_r
    return grid

def generate_map():
    grid = []
    for r in range(ROWS):
        row = []
        for c in range(COLS):
            if r == 0 or r == ROWS-1 or c == 0 or c == COLS-1:
                row.append(1)
            else:
                row.append(1 if random.random() < 0.40 else 0)
        grid.append(row)
    for _ in range(4):
        new = [row[:] for row in grid]
        for r in range(1, ROWS-1):
            for c in range(1, COLS-1):
                walls = sum(grid[r+dr][c+dc] for dr in (-1,0,1) for dc in (-1,0,1))
                new[r][c] = 1 if walls >= 5 else 0
        grid = new

    for dr in range(-3, 4):
        for dc in range(-3, 4):
            nr, nc = 2+dr, 2+dc
            if 0 < nr < ROWS-1 and 0 < nc < COLS-1:
                grid[nr][nc] = 0

    for dr in range(-3, 4):
        for dc in range(-3, 4):
            er, ec = ROWS-4, COLS-4
            nr, nc = er+dr, ec+dc
            if 0 < nr < ROWS-1 and 0 < nc < COLS-1:
                grid[nr][nc] = 0

    grid = connect_map(grid, start_r=2, start_c=2)
    return grid

def is_floor(grid, r, c):
    return 0 <= r < ROWS and 0 <= c < COLS and grid[r][c] == 0

def random_floor(grid, excludes):
    for _ in range(2000):
        r = random.randint(1, ROWS-2)
        c = random.randint(1, COLS-2)
        if is_floor(grid, r, c) and (r, c) not in excludes:
            return r, c
    for r in range(1, ROWS-1):
        for c in range(1, COLS-1):
            if is_floor(grid, r, c) and (r, c) not in excludes:
                return r, c
    return 2, 2

def random_floor_far(grid, excludes, from_r, from_c, min_dist=8):
    for _ in range(3000):
        r = random.randint(1, ROWS-2)
        c = random.randint(1, COLS-2)
        if not is_floor(grid, r, c): continue
        if (r, c) in excludes: continue
        if abs(r - from_r) + abs(c - from_c) >= min_dist:
            return r, c
    return random_floor(grid, excludes)

# ── Partikel ──────────────────────────────────────────────────────────────────
class Particle:
    def __init__(self, x, y, col):
        angle = random.uniform(0, math.pi*2)
        speed = random.uniform(0.4, 2.5)
        self.x = x; self.y = y
        self.vx = math.cos(angle)*speed
        self.vy = math.sin(angle)*speed - 0.8
        self.col = col
        self.life = random.randint(20, 45)
        self.max_life = self.life

    def update(self):
        self.x += self.vx; self.y += self.vy
        self.vy += 0.06;   self.life -= 1

    def draw(self):
        if self.life > self.max_life // 2:
            pyxel.pset(int(self.x), int(self.y), self.col)

# ── Schneeflocken ─────────────────────────────────────────────────────────────
class Snowflake:
    def __init__(self):
        self.reset(random.randint(0, H))

    def reset(self, y=0):
        self.x = random.uniform(0, W)
        self.y = float(y)
        self.speed = random.uniform(0.2, 0.6)
        self.drift = random.uniform(-0.15, 0.15)

    def update(self):
        self.y += self.speed; self.x += self.drift
        if self.y > H: self.reset()

    def draw(self):
        pyxel.pset(int(self.x) % W, int(self.y), COL_SNOW)

# ── Spieler ───────────────────────────────────────────────────────────────────
class Player:
    def __init__(self, avatar):
        self.r = 2; self.c = 2
        self.px = float(self.c*TILE); self.py = float(self.r*TILE)
        self.tr = self.r; self.tc = self.c
        self.moving = False; self.dir = 1
        self.anim = 0.0; self.invincible = 0
        self.avatar = avatar

    def start_move(self, dr, dc, grid):
        nr, nc = self.r+dr, self.c+dc
        if is_floor(grid, nr, nc):
            self.tr, self.tc = nr, nc; self.moving = True
            if dc != 0: self.dir = 1 if dc > 0 else -1

    def update(self):
        if self.invincible > 0: self.invincible -= 1
        self.anim += 0.15
        if self.moving:
            tx = float(self.tc*TILE); ty = float(self.tr*TILE)
            dx = tx - self.px; dy = ty - self.py
            d = math.sqrt(dx*dx + dy*dy)
            if d <= 4.0:
                self.px, self.py = tx, ty
                self.r, self.c = self.tr, self.tc; self.moving = False
            else:
                self.px += (dx/d)*4.0; self.py += (dy/d)*4.0

    def draw(self):
        draw_avatar(int(self.px), int(self.py), self.avatar,
                    self.anim, self.moving, self.invincible, pyxel.frame_count)

# ── Gegner ────────────────────────────────────────────────────────────────────
class Enemy:
    def __init__(self, r, c, speed):
        self.r = r; self.c = c
        self.px = float(c*TILE); self.py = float(r*TILE)
        self.vr = 0; self.vc = 0
        self.timer = random.randint(5, 25)
        self.anim = random.uniform(0, math.pi*2)
        self.speed = speed

    def choose_dir(self, grid, player):
        dirs = [(-1,0),(1,0),(0,-1),(0,1)]
        random.shuffle(dirs)
        if random.random() < 0.45:
            pr, pc = player.r, player.c
            dr = (1 if pr > self.r else -1) if pr != self.r else 0
            dc = (1 if pc > self.c else -1) if pc != self.c else 0
            if dr and is_floor(grid, self.r+dr, self.c): self.vr, self.vc = dr, 0; return
            if dc and is_floor(grid, self.r, self.c+dc): self.vr, self.vc = 0, dc; return
        for dr, dc in dirs:
            if is_floor(grid, self.r+dr, self.c+dc): self.vr, self.vc = dr, dc; return

    def update(self, grid, player):
        self.anim += 0.1; self.timer -= 1
        if self.timer <= 0:
            self.timer = random.randint(10, 25)
            self.choose_dir(grid, player)
            nr, nc = self.r+self.vr, self.c+self.vc
            if is_floor(grid, nr, nc): self.r, self.c = nr, nc
        tx = float(self.c*TILE); ty = float(self.r*TILE)
        self.px += (tx-self.px)*(0.15*self.speed)
        self.py += (ty-self.py)*(0.15*self.speed)

    def draw(self):
        x = int(self.px); y = int(self.py)
        t = pyxel.frame_count
        pulse = 1 if (t//6)%2==0 else 0
        pyxel.rect(x+1, y+2, 6, 5, COL_ENEMY)
        pyxel.rect(x+2, y+1, 4, 7, COL_ENEMY)
        if pulse: pyxel.rectb(x+1, y+1, 6, 7, COL_WALL_HL)
        pyxel.pset(x+2, y+3, COL_ENEMY_EYE); pyxel.pset(x+5, y+3, COL_ENEMY_EYE)
        s2 = (t//8)%2
        pyxel.pset(x+4, y-s2, COL_ENEMY)
        pyxel.pset(x+1, y+1-s2, COL_ENEMY)
        pyxel.pset(x+6, y+1-s2, COL_ENEMY)

# ── Kristall ──────────────────────────────────────────────────────────────────
class Crystal:
    def __init__(self, r, c, ctype):
        self.r = r; self.c = c; self.ctype = ctype
        self.anim = random.uniform(0, math.pi*2); self.collected = False

    def update(self):
        if not self.collected: self.anim += 0.08

    def draw(self):
        if self.collected: return
        x = self.c*TILE + TILE//2
        y = self.r*TILE + TILE//2 + int(math.sin(self.anim)*1.5)
        col = COL_CRYSTAL[self.ctype]; t = pyxel.frame_count
        if (t//5)%2==0:
            pyxel.pset(x, y-4, col); pyxel.pset(x-3, y, col); pyxel.pset(x+3, y, col)
        pyxel.line(x, y-3, x-2, y+1, col); pyxel.line(x, y-3, x+2, y+1, col)
        pyxel.line(x-2, y+1, x, y+3, col); pyxel.line(x+2, y+1, x, y+3, col)
        pyxel.pset(x, y, col)
        pyxel.pset(x, y-1, 7 if (t//3)%2==0 else col)

# ── Hauptspiel ────────────────────────────────────────────────────────────────
class App:
    def __init__(self):
        pyxel.init(W, H, title="Ice Cave Explorer", fps=30)
        init_sounds()
        saved = self._load_save()
        self.total_crystals  = saved.get("crystals", 0)
        self.owned           = set(saved.get("owned", [0]))
        self.selected_avatar = saved.get("avatar", 0)
        self.highscore_level = saved.get("highscore_level", 1)
        self.highscore_score = saved.get("highscore_score", 0)
        self.title_cursor    = 0
        self.shop_cursor     = 0
        self.shop_msg        = ""
        self.shop_msg_timer  = 0
        self.snow      = [Snowflake() for _ in range(35)]
        self.particles = []
        self.level = 1; self.score = 0; self.lives = 3
        self.state = "title"
        self.new_highscore = False   # Flag: neuer Rekord diese Runde?
        self.flash = 0; self.timer = 0
        self._init_level()
        pyxel.run(self.update, self.draw)

    # ── Level Init ────────────────────────────────────────────────────────────
    def _init_level(self):
        av = AVATARS[self.selected_avatar]
        self.grid = generate_map()
        self.player = Player(av)

        start_r, start_c = 2, 2
        excl = {(start_r, start_c), (ROWS-4, COLS-4)}

        n_cryst = 4 + self.level * 2
        self.crystals = []
        for i in range(n_cryst):
            r, c = random_floor_far(self.grid,
                                    excl | {(cr.r, cr.c) for cr in self.crystals},
                                    start_r, start_c, min_dist=5)
            self.crystals.append(Crystal(r, c, i % 4))

        n_enem = 1 + int(self.level * 1.3)
        cryst_pos = {(cr.r, cr.c) for cr in self.crystals}
        self.enemies = []
        for _ in range(n_enem):
            r, c = random_floor_far(self.grid,
                                    excl | cryst_pos | {(e.r, e.c) for e in self.enemies},
                                    start_r, start_c, min_dist=8)
            self.enemies.append(Enemy(r, c, 0.6 + self.level * 0.12))

        self.exit_r = ROWS-4; self.exit_c = COLS-4
        self.particles = []
        self.flash = 0; self.timer = 0

    # ── Highscore prüfen und ggf. speichern ───────────────────────────────────
    def _check_highscore(self):
        """Prüft ob neuer Highscore, gibt True zurück wenn ja."""
        new_hs = False
        if self.level > self.highscore_level:
            self.highscore_level = self.level
            new_hs = True
        if self.score > self.highscore_score:
            self.highscore_score = self.score
            new_hs = True
        if new_hs:
            self._save_game()
        return new_hs

    # ── Update ────────────────────────────────────────────────────────────────
    def update(self):
        for s in self.snow: s.update()
        self.particles = [p for p in self.particles if p.life > 0]
        for p in self.particles: p.update()
        if self.shop_msg_timer > 0: self.shop_msg_timer -= 1

        if self.state == "title":     self._update_title(); return
        if self.state == "shop":      self._update_shop();  return

        if self.state == "gameover":
            if pyxel.btnp(pyxel.KEY_SPACE) or pyxel.btnp(pyxel.KEY_RETURN):
                self.level = 1; self.score = 0; self.lives = 3
                self.new_highscore = False
                self._init_level(); self.state = "play"
            if pyxel.btnp(pyxel.KEY_ESCAPE) or pyxel.btnp(pyxel.KEY_M):
                self._save_game()
                self.state = "title"
            return

        if self.state == "dead":
            self.timer += 1; self.flash = max(0, self.flash-4)
            if self.timer > 60: self._init_level(); self.state = "play"
            return

        if self.state == "nextlevel":
            self.timer += 1; self.flash = max(0, self.flash-3)
            if self.timer > 90:
                self.level += 1; self._init_level(); self.state = "play"
            return

        # ── play ──
        if pyxel.btnp(pyxel.KEY_ESCAPE) or pyxel.btnp(pyxel.KEY_M):
            pyxel.play(3, 3)
            self._save_game()
            self.state = "title"
            return

        self.flash = max(0, self.flash-2)
        for cr in self.crystals: cr.update()
        self.player.update()
        if not self.player.moving:
            self._handle_input(); self._check_pickup()
        for e in self.enemies:
            e.update(self.grid, self.player)
            if self.player.invincible == 0:
                dx = e.px+4 - (self.player.px+4)
                dy = e.py+4 - (self.player.py+4)
                if math.sqrt(dx*dx + dy*dy) < 6:
                    self._hit_player()

    def _update_title(self):
        if pyxel.btnp(pyxel.KEY_UP)   or pyxel.btnp(pyxel.KEY_W):
            self.title_cursor = (self.title_cursor-1) % 2
            pyxel.play(3, 3)
        if pyxel.btnp(pyxel.KEY_DOWN) or pyxel.btnp(pyxel.KEY_S):
            self.title_cursor = (self.title_cursor+1) % 2
            pyxel.play(3, 3)
        if pyxel.btnp(pyxel.KEY_SPACE) or pyxel.btnp(pyxel.KEY_RETURN):
            if self.title_cursor == 0:
                self.level = 1; self.score = 0; self.lives = 3
                self.new_highscore = False
                self._init_level(); self.state = "play"
            else:
                self.shop_cursor = 0; self.state = "shop"

    def _update_shop(self):
        if pyxel.btnp(pyxel.KEY_LEFT)  or pyxel.btnp(pyxel.KEY_A):
            self.shop_cursor = (self.shop_cursor-1) % len(AVATARS)
            pyxel.play(3, 3)
        if pyxel.btnp(pyxel.KEY_RIGHT) or pyxel.btnp(pyxel.KEY_D):
            self.shop_cursor = (self.shop_cursor+1) % len(AVATARS)
            pyxel.play(3, 3)
        if pyxel.btnp(pyxel.KEY_SPACE) or pyxel.btnp(pyxel.KEY_RETURN):
            idx = self.shop_cursor; av = AVATARS[idx]
            if idx in self.owned:
                self.selected_avatar = idx
                self.shop_msg = "AKTIV: " + av["name"]
                pyxel.play(3, 4)
            elif self.total_crystals >= av["preis"]:
                self.total_crystals -= av["preis"]
                self.owned.add(idx); self.selected_avatar = idx
                self.shop_msg = "GEKAUFT: " + av["name"] + "!"
                pyxel.play(3, 4)
                self._save_game()
            else:
                self.shop_msg = "FEHLEN: " + str(av["preis"]-self.total_crystals) + " K"
                pyxel.play(3, 5)
            self.shop_msg_timer = 70
        if pyxel.btnp(pyxel.KEY_ESCAPE) or pyxel.btnp(pyxel.KEY_M):
            pyxel.play(3, 3)
            self.state = "title"

    def _handle_input(self):
        dr, dc = 0, 0
        if   pyxel.btn(pyxel.KEY_UP)    or pyxel.btn(pyxel.KEY_W): dr = -1
        elif pyxel.btn(pyxel.KEY_DOWN)  or pyxel.btn(pyxel.KEY_S): dr =  1
        elif pyxel.btn(pyxel.KEY_LEFT)  or pyxel.btn(pyxel.KEY_A): dc = -1
        elif pyxel.btn(pyxel.KEY_RIGHT) or pyxel.btn(pyxel.KEY_D): dc =  1
        if dr or dc: self.player.start_move(dr, dc, self.grid)

    def _check_pickup(self):
        pr, pc = self.player.r, self.player.c
        for cr in self.crystals:
            if not cr.collected and cr.r == pr and cr.c == pc:
                cr.collected = True
                self.score += 100 * self.level
                self.total_crystals += 1
                self._save_game()
                self._spawn_fx(cr.c*TILE+4, cr.r*TILE+4, COL_CRYSTAL[cr.ctype], 18)
                pyxel.play(3, 0)
        if all(cr.collected for cr in self.crystals) and pr == self.exit_r and pc == self.exit_c:
            self.score += 500 * self.level
            self._spawn_fx(pc*TILE, pr*TILE, 11, 40)
            # Level-Übergang Sound (Jingle)
            pyxel.play(3, 9)
            self.state = "nextlevel"; self.flash = 200; self.timer = 0

    def _hit_player(self):
        self.lives -= 1
        self.player.invincible = 90
        self.flash = 200
        self._spawn_fx(int(self.player.px)+4, int(self.player.py)+4, 8, 25)
        if self.lives <= 0:
            # Highscore prüfen
            self.new_highscore = self._check_highscore()
            if self.new_highscore:
                pyxel.play(3, 8)   # Neuer Highscore – Triumphmelodie
            else:
                pyxel.play(3, 7)   # Game Over – dramatischer Sound
            self._save_game()
            self.state = "gameover"
        else:
            pyxel.play(3, 6)   # Schaden – dramatischer Absturz
            self.state = "dead"; self.timer = 0

    def _spawn_fx(self, x, y, col, n):
        for _ in range(n): self.particles.append(Particle(x, y, col))

    # ── Draw ──────────────────────────────────────────────────────────────────
    def draw(self):
        pyxel.cls(COL_BG2)
        if self.state == "title": self._draw_title(); return
        if self.state == "shop":  self._draw_shop();  return
        self._draw_map()
        self._draw_exit()
        for cr in self.crystals: cr.draw()
        for e  in self.enemies:  e.draw()
        self.player.draw()
        for s in self.snow: s.draw()
        for p in self.particles: p.draw()
        self._draw_hud()
        self._draw_flash()
        if self.state == "nextlevel" and self.timer > 10:
            self._draw_nextlevel_screen()
        if self.state == "gameover":
            self._draw_gameover()

    # ── Level-Abschluss-Screen ────────────────────────────────────────────────
    def _draw_nextlevel_screen(self):
        # Halbtransparenter Overlay
        for r in range(H//2-22, H//2+30, 2):
            for c in range(20, W-20, 2):
                pyxel.pset(c + r%2, r, 0)
        # Rahmen
        pyxel.rectb(18, H//2-24, W-36, 54, 11)
        pyxel.rectb(19, H//2-23, W-38, 52, 6)

        self._draw_centered("LEVEL " + str(self.level) + " KOMPLETT!", H//2-18, 11)
        self._draw_centered("BONUS: +" + str(500*self.level) + " PTS", H//2-6, 10)

        # Kleiner Fortschrittsbalken für Level
        bar_w = 80; bar_x = (W - bar_w) // 2
        pyxel.rectb(bar_x, H//2+6, bar_w, 6, 6)
        fill = min(bar_w-2, int((self.level / 10) * (bar_w-2)))
        pyxel.rect(bar_x+1, H//2+7, fill, 4, 11)
        pyxel.text(bar_x + bar_w//2 - 12, H//2+8, "LV "+str(self.level), 7)

        # Nächstes Level Vorschau
        next_enem = 1 + int((self.level+1) * 1.3)
        self._draw_centered("NAECHSTES LV: " + str(next_enem) + " FEINDE", H//2+16, 6)

    # ── Titelbildschirm ───────────────────────────────────────────────────────
    def _draw_title(self):
        t = pyxel.frame_count
        for r in range(0, H, TILE):
            for c in range(0, W, TILE):
                col = COL_WALL if (r//TILE + c//TILE + t//20) % 4 == 0 else COL_BG
                pyxel.rect(c, r, TILE, TILE, col)
        for s in self.snow: s.draw()
        for p in self.particles: p.draw()

        t1 = "ICE CAVE"; t2 = "EXPLORER"
        pyxel.text((W-len(t1)*4)//2+1, H//2-35, t1, 0)
        pyxel.text((W-len(t1)*4)//2,   H//2-36, t1, COL_TITLE)
        pyxel.text((W-len(t2)*4)//2+1, H//2-25, t2, 0)
        pyxel.text((W-len(t2)*4)//2,   H//2-26, t2, COL_HUD_ACC)

        # Kristall-Deko animiert
        for i, cx in enumerate([W//2-20, W//2, W//2+20]):
            cy = H//2-10 + int(math.sin(t*0.1+i)*2)
            col = COL_CRYSTAL[i % 4]
            pyxel.line(cx, cy-3, cx-2, cy+1, col); pyxel.line(cx, cy-3, cx+2, cy+1, col)
            pyxel.line(cx-2, cy+1, cx, cy+3, col); pyxel.line(cx+2, cy+1, cx, cy+3, col)

        # ── HIGHSCORE-ANZEIGE ──────────────────────────────────────────────
        pyxel.rect(W//2 - 52, H//2 - 2, 104, 16, COL_HUD_BG)
        pyxel.rectb(W//2 - 52, H//2 - 2, 104, 16, COL_WALL_HL)
        hs_col = 10 if (t // 15) % 2 == 0 else COL_TITLE
        hs_txt = "REKORD: LV " + str(self.highscore_level) + "  PTS " + str(self.highscore_score)
        pyxel.text((W - len(hs_txt)*4)//2, H//2 + 1, hs_txt, hs_col)

        items = ["  SPIELEN  ", "   SHOP    "]
        for i, label in enumerate(items):
            my = H//2 + 20 + i*14
            sel = (self.title_cursor == i)
            pyxel.rect((W-len(label)*4)//2-2, my-2, len(label)*4+4, 10, COL_HUD_ACC if sel else COL_WALL)
            pyxel.text((W-len(label)*4)//2, my, label, 0 if sel else COL_HUD_TEXT)

        ks = "KRISTALLE: " + str(self.total_crystals)
        pyxel.text((W-len(ks)*4)//2, H-12, ks, 10)
        hint = "W/S AUSW.  SPACE OK"
        pyxel.text((W-len(hint)*4)//2, H-6, hint, COL_WALL_HL)

    # ── Shop ──────────────────────────────────────────────────────────────────
    def _draw_shop(self):
        pyxel.rect(0, 0, W, H, COL_BG2)
        for i in range(0, W, 16): pyxel.line(i, 0, i, H, COL_BG)
        for i in range(0, H, 16): pyxel.line(0, i, W, i, COL_BG)

        pyxel.text(1, 2, "AVATAR SHOP", 0)
        pyxel.text(0, 1, "AVATAR SHOP", COL_TITLE)
        ks = "K: " + str(self.total_crystals)
        pyxel.text(W-len(ks)*4-2, 1, ks, 10)

        card_w = 40; card_h = 92
        total_w = len(AVATARS)*card_w + (len(AVATARS)-1)*4
        sx = (W - total_w) // 2
        cy0 = 14

        for i, av in enumerate(AVATARS):
            cx   = sx + i*(card_w+4)
            sel  = (self.shop_cursor == i)
            own  = (i in self.owned)
            act  = (i == self.selected_avatar)
            border = COL_TITLE if sel else (11 if act else COL_WALL)
            pyxel.rect( cx,   cy0,   card_w,   card_h,   COL_BG)
            pyxel.rectb(cx,   cy0,   card_w,   card_h,   border)
            if sel: pyxel.rectb(cx-1, cy0-1, card_w+2, card_h+2, COL_HUD_ACC)

            draw_avatar(cx + card_w//2 - 4, cy0+10, av,
                        pyxel.frame_count*0.15, False, 0, pyxel.frame_count)

            nlen = len(av["name"])*4
            pyxel.text(cx+(card_w-nlen)//2, cy0+28, av["name"],
                       COL_TITLE if sel else COL_HUD_TEXT)

            tl = av["titel"]
            words = tl.split()
            line1 = words[0]
            line2 = " ".join(words[1:]) if len(words) > 1 else ""
            pyxel.text(cx+1, cy0+37, line1, COL_WALL_HL)
            if line2: pyxel.text(cx+1, cy0+44, line2, COL_WALL_HL)

            if act:
                st = "AKTIV"
                pyxel.text(cx+(card_w-len(st)*4)//2, cy0+67, st, 11)
            elif own:
                st = "BESITZ"
                pyxel.text(cx+(card_w-len(st)*4)//2, cy0+67, st, 12)
            else:
                pr = str(av["preis"]) + "K"
                pyxel.text(cx+(card_w-len(pr)*4)//2, cy0+67, pr, 10)
                if self.total_crystals >= av["preis"] and (pyxel.frame_count//10)%2==0:
                    pyxel.rectb(cx+1, cy0+1, card_w-2, card_h-2, 10)

            if not own:
                pyxel.rectb(cx+card_w//2-3, cy0+78, 6, 5, COL_WALL_HL)
                pyxel.line( cx+card_w//2-2, cy0+78, cx+card_w//2+2, cy0+78, COL_BG2)
                pyxel.pset( cx+card_w//2,   cy0+80, COL_WALL_HL)

        if self.shop_msg_timer > 0:
            ml = len(self.shop_msg)*4
            mx = (W-ml)//2
            pyxel.rect(mx-3, H-22, ml+6, 10, 0)
            pyxel.text(mx, H-21, self.shop_msg, 10)

        ctrl = "A/D AUSW.  SPACE KAUF/WAEHLEN  M/ESC ZURUECK"
        pyxel.text(max(0,(W-len(ctrl)*4)//2), H-8, ctrl, COL_WALL_HL)

    # ── Spielkarte ────────────────────────────────────────────────────────────
    def _draw_map(self):
        for r in range(ROWS):
            for c in range(COLS):
                x, y = c*TILE, r*TILE
                if self.grid[r][c] == 1:
                    pyxel.rect(x, y, TILE, TILE, COL_WALL)
                    pyxel.line(x, y, x+TILE-1, y, COL_WALL_HL)
                    pyxel.line(x, y, x, y+TILE-1, COL_WALL_HL)
                    if (r*13+c*7) % 9 == 0:
                        pyxel.pset(x+3, y+3, COL_WALL_HL)
                else:
                    pyxel.rect(x, y, TILE, TILE, COL_BG if (r+c)%2==0 else COL_BG2)
                    if (r*5+c*11) % 7 == 0:
                        pyxel.pset(x+2, y+4, COL_WALL)

    def _draw_exit(self):
        x = self.exit_c*TILE; y = self.exit_r*TILE
        all_done = all(cr.collected for cr in self.crystals)
        col = COL_EXIT_ON if all_done else COL_EXIT_OFF
        t = pyxel.frame_count
        pyxel.rectb(x+1, y+1, TILE-2, TILE-2, col)
        if all_done and (t//4)%2==0: pyxel.rectb(x, y, TILE, TILE, 11)
        pyxel.pset(x+4, y+2, col)
        pyxel.line(x+3, y+3, x+5, y+3, col)
        pyxel.line(x+2, y+4, x+6, y+4, col)
        pyxel.line(x+3, y+5, x+5, y+5, col)

    def _draw_hud(self):
        pyxel.rect(0, 0, W, 9, COL_HUD_BG)
        pyxel.line(0, 9, W, 9, COL_WALL_HL)
        collected = sum(1 for cr in self.crystals if cr.collected)
        total = len(self.crystals)
        av = AVATARS[self.selected_avatar]
        pyxel.text(2,  1, "LV:"+str(self.level),          COL_HUD_ACC)
        pyxel.text(28, 1, "K:"+str(collected)+"/"+str(total), COL_HUD_TEXT)
        pyxel.text(68, 1, "PTS:"+str(self.score),          COL_HUD_TEXT)
        pyxel.text(118,1, "GES:"+str(self.total_crystals), 10)
        pyxel.text(162,1, av["name"],                       av["helmet"])
        for i in range(self.lives):
            pyxel.pset(218+i*5, 4, 8)
            pyxel.pset(218+i*5+1, 3, 8)
            pyxel.pset(218+i*5-1, 3, 8)
        pyxel.text(W-50, H-6, "M/ESC=MENU", COL_WALL)

    def _draw_flash(self):
        if self.flash > 0:
            col = 11 if self.state == "nextlevel" else 8
            if self.flash // 40 > 0:
                for r in range(0, H, 4):
                    for c in range(0, W, 4):
                        pyxel.pset(c, r, col)

    def _draw_centered(self, text, y, col):
        x = (W - len(text)*4) // 2
        pyxel.text(x+1, y+1, text, 0)
        pyxel.text(x,   y,   text, col)

    def _draw_gameover(self):
        t = pyxel.frame_count
        # Dunkler Overlay
        for r in range(8, H-8, 2):
            for c in range(0, W, 2):
                pyxel.pset(c + r%2, r, 0)

        # Rahmen mit Blinken
        frame_col = 8 if (t // 10) % 2 == 0 else COL_WALL_HL
        pyxel.rectb(10, H//2-30, W-20, 72, frame_col)
        pyxel.rectb(11, H//2-29, W-22, 70, 0)

        # GAME OVER
        self._draw_centered("GAME OVER",  H//2-24, 8)

        # ── HIGHSCORE-BEREICH ──────────────────────────────────────────────
        # Trennlinie
        pyxel.line(20, H//2-14, W-20, H//2-14, COL_WALL_HL)

        # Erreichte Stats
        self._draw_centered("LEVEL ERREICHT: " + str(self.level), H//2-10, 7)
        self._draw_centered("PUNKTE: " + str(self.score),          H//2 - 2, 7)

        # Trennlinie
        pyxel.line(20, H//2+6, W-20, H//2+6, COL_WALL_HL)

        # Highscore
        hs_col = 10 if not self.new_highscore else (COL_TITLE if (t//8)%2==0 else 10)
        self._draw_centered("REKORD: LV " + str(self.highscore_level) + \
                            "  PTS " + str(self.highscore_score), H//2+10, hs_col)

        if self.new_highscore:
            nh_col = COL_TITLE if (t//6)%2==0 else 10
            self._draw_centered("** NEUER REKORD! **", H//2+20, nh_col)
            offset = 28
        else:
            offset = 18

        # Steuerhinweise
        pyxel.line(20, H//2+offset+4, W-20, H//2+offset+4, COL_WALL_HL)
        self._draw_centered("SPACE = NEUSTART",     H//2+offset+8,  6)
        self._draw_centered("M/ESC = HAUPTMENU",    H//2+offset+16, COL_WALL_HL)

    # ── Spielstand speichern / laden ─────────────────────────────────────────
    def _save_game(self):
        try:
            data = (str(self.total_crystals) + "|" +
                    ",".join(str(i) for i in sorted(self.owned)) + "|" +
                    str(self.selected_avatar) + "|" +
                    str(self.highscore_level) + "|" +
                    str(self.highscore_score))
            pyxel.user_data = data
        except Exception:
            pass

    def _load_save(self):
        try:
            data = pyxel.user_data
            if data and "|" in data:
                parts = data.split("|")
                crystals = int(parts[0])
                owned    = [int(x) for x in parts[1].split(",") if x]
                avatar   = int(parts[2]) if len(parts) > 2 else 0
                hs_level = int(parts[3]) if len(parts) > 3 else 1
                hs_score = int(parts[4]) if len(parts) > 4 else 0
                owned  = [i for i in owned if 0 <= i < len(AVATARS)]
                avatar = avatar if 0 <= avatar < len(AVATARS) else 0
                if 0 not in owned: owned.append(0)
                return {"crystals": crystals, "owned": owned, "avatar": avatar,
                        "highscore_level": hs_level, "highscore_score": hs_score}
        except Exception:
            pass
        return {"crystals": 0, "owned": [0], "avatar": 0, "highscore_level": 1, "highscore_score": 0}


# ── Start ─────────────────────────────────────────────────────────────────────
App()