import pyxel
import random
from dataclasses import dataclass

# ============================================================
# THERMOTRAIL 512  -  pyxelstudio.net kompatibel
# Nur sichere Pyxel-Funktionen: rect, rectb, circ, line,
# pset, tri, text, cls  — kein ellipse, circb, blt, load
# ============================================================

WIDTH  = 512
HEIGHT = 512
TILE   = 24
GRID_W = WIDTH  // TILE   # 21
GRID_H = HEIGHT // TILE   # 21

ROW_BUFFER = 100
TEMP_MIN   = -2
TEMP_MAX   =  2

# Sinus-Tabelle (256 Eintraege, 0..255 -> -100..100)
_SIN = []
_COS = []
for _i in range(256):
    import math as _m
    _SIN.append(_m.sin(_i * 2 * _m.pi / 256))
    _COS.append(_m.cos(_i * 2 * _m.pi / 256))

def isin(deg256): return _SIN[deg256 % 256]
def icos(deg256): return _COS[deg256 % 256]

LEVEL_CONFIG = [
    (3, 5, "EASY"),
    (5, 4, "MEDIUM"),
    (7, 3, "HARD"),
]

GRASS     = 0
WATER     = 1
HOT_WATER = 2
ICE       = 3
STONE     = 4
HOT_STONE = 5
LAVA      = 6
FORTRESS  = 7
DEEP_ICE  = 8
MUD       = 9

def clamp(v, lo, hi): return max(lo, min(hi, v))
def irnd(a, b): return random.randint(a, b)

# ============================================================
@dataclass
class Cell:
    base:    int
    key:     bool = False
    animal:  bool = False
    fortress:bool = False

# ============================================================
class Player:
    def __init__(self, char_type=0):
        self.char_type   = char_type
        self.x           = GRID_W // 2
        self.y           = 4
        self.move_cd     = 0
        self.cold_meter  = 0
        self.heat_meter  = 0
        self.grass_timer = 0
        self.alive       = True
        self.death_reason= ""
        self.anim        = 0
        self.facing      = 1
        self.step        = 0

    def reset(self):
        self.x = GRID_W // 2; self.y = 4
        self.move_cd = 0; self.cold_meter = 0; self.heat_meter = 0
        self.grass_timer = 0; self.alive = True
        self.death_reason = ""; self.anim = 0
        self.facing = 1; self.step = 0

    @property
    def cold_limit(self): return 600 if self.char_type == 0 else 300
    @property
    def heat_limit(self): return 600 if self.char_type == 1 else 300

# ============================================================
class Game:
    def __init__(self):
        pyxel.init(WIDTH, HEIGHT, title="ThermoTrail 512", fps=30)
        pyxel.mouse(False)

        self.state          = "menu"
        self.selected_char  = 0
        self.current_level  = 0

        # Score pro Level speichern fuer den Win-Screen
        self.level_scores   = [0, 0, 0]

        # Story-Screen
        self.story_panel  = 0      # aktuelles Panel (0..4)
        self.story_char   = 0      # Anzahl bereits angezeigter Zeichen (Typewriter)
        self.story_timer  = 0      # Frame-Zaehler fuer Typewriter-Speed
        self.story_bg_off = 0      # horizontaler Parallax-Offset fuer Hintergrund

        self.temp  = 0
        self.score = 0
        self.keys  = 0

        self.rows = []
        self.last_generated_index = -1

        self.player = Player(0)

        self.fortress_spawned = False
        self.fortress_row     = -1

        self.msg       = ""
        self.msg_timer = 0
        self.msg_col   = 7

        self.eagle_warned = False

        self.bg_stars      = []
        self.bg_far_peaks  = []
        self.bg_near_peaks = []
        self.embers = []
        self.snow   = []
        self.menu_particles = []

        self._setup_bg()
        self._setup_audio()

        self._lobby_started = False  # play lobby on first update frame
        pyxel.run(self.update, self.draw)

    # ----------------------------------------------------------
    # AUDIO
    # ----------------------------------------------------------
    def _setup_audio(self):
        pyxel.sounds[0].set(
            "e3 g3 a3 g3 e3 d3 e3 r "
            "c3 e3 g3 e3 d3 c3 d3 r "
            "a2 c3 e3 c3 a2 g2 a2 r "
            "b2 d3 g3 d3 b2 a2 g2 r",
            "t","6","n",20)
        pyxel.sounds[1].set(
            "c2 c2 c2 c2 a1 a1 a1 a1 f1 f1 f1 f1 g1 g1 g1 g1",
            "t","4","n",20)
        pyxel.sounds[2].set(
            "c1 r c1 r c1 r c1 r c1 r c1 r c1 r c1 r",
            "n","5","f",10)
        pyxel.sounds[3].set("c3 e3 g3 c4",       "p","7","n",8)
        pyxel.sounds[4].set("c3 e3 g3 c4 g3 c4", "t","7","f",18)
        pyxel.sounds[5].set("a2 b2 d3",           "s","6","n",6)
        pyxel.sounds[6].set("d3 b2 g2",           "t","5","n",6)
        pyxel.sounds[7].set("f2 d2 c2",           "n","6","f",12)
        pyxel.sounds[8].set("c2 c#2 d2 f2 g2",    "n","7","f",12)
        pyxel.sounds[9].set("c4 a3 c4 a3",        "p","6","f",6)
        pyxel.sounds[10].set("c3 e3 g3 c4 e4",    "p","7","f",18)
        pyxel.sounds[11].set("g4 e4 c4",          "t","7","f",12)
        pyxel.musics[0].set([0],[1],[2],[])
        # Lobby jingle: bright, catchy 3-part harmony
        # Melody: upbeat ascending/descending motif in C major
        pyxel.sounds[12].set(
            "c3 e3 g3 e3 f3 a3 g3 e3 "
            "d3 f3 a3 f3 e3 g3 c4 r "
            "c4 b3 a3 g3 f3 e3 d3 c3 "
            "e3 g3 c4 g3 e3 c3 r r",
            "p","7777777777777777","nnnnnnnnnnnnnnnn",18)
        # Counter-melody: lower harmonics
        pyxel.sounds[13].set(
            "c2 c2 g2 g2 a2 a2 g2 r "
            "f2 f2 c2 c2 g2 g2 c2 r "
            "a2 a2 e2 e2 f2 f2 c2 r "
            "c2 e2 g2 e2 c2 r r r",
            "t","5555555555555555","nnnnnnnnnnnnnnnn",18)
        # Percussion pulse
        pyxel.sounds[14].set(
            "c1 r r c1 r r c1 r "
            "c1 r r c1 r r c1 r "
            "c1 r r c1 r r c1 r "
            "c1 r c1 r c1 r r r",
            "n","6464646464646464","ffffffffffffffff",18)
        pyxel.musics[1].set([12],[13],[14],[])
        # Story jingle: slow, dramatic, minor key (A minor)
        # Brooding melody with tension and mystery
        pyxel.sounds[15].set(
            "a2 r c3 r e3 r d3 r "
            "c3 r a2 r b2 r r r "
            "f2 r a2 r c3 r b2 r "
            "a2 r g2 r a2 r r r",
            "t","6666666666666666","nnnnffnnnnffnnff",25)
        # Low drone: dark sustained bass
        pyxel.sounds[16].set(
            "a1 r r r a1 r r r "
            "f1 r r r e1 r r r "
            "d1 r r r f1 r r r "
            "a1 r r r e1 r r r",
            "t","4444444444444444","nnnnnnnnnnnnnnnn",25)
        # Atmospheric shimmer: high tremolo accents
        pyxel.sounds[17].set(
            "r r e4 r r r c4 r "
            "r r a3 r r r b3 r "
            "r r f3 r r r e3 r "
            "r r c3 r r r r r",
            "p","5555555555555555","ffnnffnnffnnffnn",25)
        pyxel.musics[2].set([15],[16],[17],[])

    def _play_bgm(self):  pyxel.playm(0, loop=True)
    def _stop_bgm(self):
        pyxel.stop(0); pyxel.stop(1); pyxel.stop(2)
    def _play_lobby(self): pyxel.playm(1, loop=True)
    def _stop_lobby(self): pyxel.stop(0); pyxel.stop(1); pyxel.stop(2)
    def _play_story_music(self): pyxel.playm(2, loop=True)
    def _stop_story_music(self): pyxel.stop(0); pyxel.stop(1); pyxel.stop(2)

    def _sfx(self, n): pyxel.play(3, n)

    # ----------------------------------------------------------
    # BACKGROUND
    # ----------------------------------------------------------
    def _setup_bg(self):
        self.bg_stars = [
            {"x": random.random()*WIDTH, "y": random.random()*110,
             "c": random.choice([5,6,7,12])} for _ in range(70)]

        x = -20
        while x < WIDTH+60:
            self.bg_far_peaks.append((x, irnd(30,80), irnd(20,55)))
            x += irnd(20,44)

        x = -10
        while x < WIDTH+50:
            self.bg_near_peaks.append((x, irnd(22,55), irnd(22,70)))
            x += irnd(16,30)

        self.embers = [
            {"x": random.random()*WIDTH, "y": random.random()*HEIGHT,
             "vx": random.uniform(-0.2,0.2), "vy": random.uniform(-0.8,-0.3),
             "r": random.choice([1,1,2])} for _ in range(30)]

        self.snow = [
            {"x": random.random()*WIDTH, "y": random.random()*HEIGHT,
             "vx": random.uniform(-0.3,0.2), "vy": random.uniform(0.3,0.9),
             "r": random.choice([1,1,1,2])} for _ in range(40)]

        self.menu_particles = [
            {"x": random.random()*WIDTH, "y": random.random()*HEIGHT,
             "vx": random.uniform(-0.4,0.4), "vy": random.uniform(-0.5,0.5),
             "c": random.choice([9,10,12,6,7]), "r": random.choice([1,1,2,2,3])}
            for _ in range(60)]

    def _upd_particles(self):
        if self.temp >= 1:
            for p in self.embers:
                p["x"] += p["vx"]; p["y"] += p["vy"]
                if p["y"] < -4: p["y"] = HEIGHT+4; p["x"] = random.random()*WIDTH
                if p["x"] < -4: p["x"] = WIDTH+4
                if p["x"] > WIDTH+4: p["x"] = -4
        if self.temp <= -1:
            for p in self.snow:
                p["x"] += p["vx"]; p["y"] += p["vy"]
                if p["y"] > HEIGHT+4: p["y"] = -4; p["x"] = random.random()*WIDTH
                if p["x"] < -4: p["x"] = WIDTH+4
                if p["x"] > WIDTH+4: p["x"] = -4

    def _upd_menu_particles(self):
        for p in self.menu_particles:
            p["x"] += p["vx"]; p["y"] += p["vy"]
            if p["x"] < 0:      p["x"] = WIDTH
            if p["x"] > WIDTH:  p["x"] = 0
            if p["y"] < 0:      p["y"] = HEIGHT
            if p["y"] > HEIGHT: p["y"] = 0

    # ----------------------------------------------------------
    # WORLD
    # ----------------------------------------------------------
    def _reset_world(self):
        self.temp = 0; self.score = 0; self.keys = 0
        self.msg = ""; self.msg_timer = 0
        self.eagle_warned = False
        self.rows = []; self.last_generated_index = -1
        self.fortress_spawned = False; self.fortress_row = -1
        self.player = Player(self.selected_char)
        self.player.reset()
        for i in range(ROW_BUFFER):
            self.rows.append(self._make_row(i))
            self.last_generated_index = i
        self.rows[self.player.y][self.player.x] = Cell(STONE)

    def _ensure_rows(self, target):
        while self.last_generated_index < target + 22:
            idx = self.last_generated_index + 1
            self.rows.append(self._make_row(idx))
            self.last_generated_index = idx

    def _make_row(self, ri):
        lvl  = self.current_level
        diff = min(1.0, ri / (80.0 + lvl*20))
        row  = []
        for x in range(GRID_W):
            if ri < 6:
                t = STONE
            elif ri < 12:
                t = random.choice([STONE]*5+[GRASS]*3+[ICE]+[WATER])
            else:
                if lvl == 0:
                    pool = [GRASS]*4+[STONE]*4+[WATER]*3+[ICE]*2+[HOT_WATER]*2+[HOT_STONE]*2+[LAVA]
                elif lvl == 1:
                    pool = [GRASS]*2+[STONE]*3+[WATER]*2+[ICE]*3+[HOT_WATER]*3+[HOT_STONE]*3+[LAVA]*3+[DEEP_ICE]
                else:
                    pool = [GRASS]+[STONE]*2+[WATER]*2+[ICE]*2+[HOT_WATER]*3+[HOT_STONE]*2+[LAVA]*4+[DEEP_ICE]*3+[MUD]
                t = random.choice(pool)
                if ri < 20 and t == LAVA: t = HOT_STONE
                if diff > 0.5 and random.random() < 0.12:
                    if t == WATER:   t = HOT_WATER
                    elif t == STONE: t = HOT_STONE
                    elif t == ICE and lvl >= 1: t = DEEP_ICE

            c = Cell(base=t)
            if t in (WATER, HOT_WATER) and random.random() < 0.18:
                c.animal = True
            kn = LEVEL_CONFIG[self.current_level][0]
            if self.keys < kn and ri > 10 and random.random() < 0.04 + lvl*0.01:
                c.key = True
            row.append(c)

        safe = sum(1 for c in row if c.base in (GRASS,STONE,ICE,WATER,MUD))
        if safe < 4:
            for _ in range(4):
                row[irnd(0,GRID_W-1)].base = random.choice([STONE,GRASS])

        found = False
        for c in row:
            if c.key:
                if found: c.key = False
                found = True
        return row

    def _spawn_fortress(self):
        if self.fortress_spawned: return
        self.fortress_spawned = True
        self.fortress_row = self.player.y + irnd(15,25)
        self._ensure_rows(self.fortress_row)
        row = [Cell(STONE) for _ in range(GRID_W)]
        cx  = irnd(4, GRID_W-5)
        for dx in range(-2,3): row[clamp(cx+dx,0,GRID_W-1)] = Cell(STONE)
        row[cx] = Cell(FORTRESS, fortress=True)
        self.rows[self.fortress_row] = row
        self._msg("!! FORTRESS APPEARED !!", 100, 10)

    # ----------------------------------------------------------
    # TERRAIN LOGIC
    # ----------------------------------------------------------
    def _eff(self, cell):
        t = cell.base
        if t == WATER:
            return ICE if self.temp <= -1 else WATER
        if t == HOT_WATER:
            if self.temp <= -2: return ICE
            if self.temp <= -1: return WATER
            return HOT_WATER
        if t == ICE:
            return WATER if self.temp >= 1 else ICE
        if t == DEEP_ICE:
            if self.temp >= 2: return WATER
            if self.temp >= 1: return ICE
            return DEEP_ICE
        if t == HOT_STONE:
            return STONE if self.temp <= -1 else HOT_STONE
        if t == LAVA:
            if self.temp <= -2: return STONE
            if self.temp <= -1: return HOT_STONE
            return LAVA
        return t

    def _safe(self, cell):
        e = self._eff(cell)
        if e in (GRASS,ICE,STONE,FORTRESS,MUD,DEEP_ICE): return True
        if e in (WATER,HOT_WATER) and cell.animal: return True
        return False

    def _is_hot(self, cell):
        return self._eff(cell) in (HOT_STONE,HOT_WATER,LAVA)

    def _is_cold(self, cell):
        return self._eff(cell) in (ICE,DEEP_ICE) and self.temp <= -1

    def _msg(self, text, dur=60, col=7):
        self.msg=text; self.msg_timer=dur; self.msg_col=col

    # ----------------------------------------------------------
    # GAME STATE
    # ----------------------------------------------------------
    def _start(self, level=0):
        self.current_level = level
        self.state = "play"
        self._reset_world()
        self._play_bgm()

    def _lose(self, reason):
        self.player.alive = False
        self.player.death_reason = reason
        self._stop_bgm(); self.state = "lose"

    def _level_complete(self):
        """Called when fortress is reached. Advances level or triggers final win."""
        self._stop_bgm()
        # Aktuellen Score speichern
        self.level_scores[self.current_level] = self.score

        if self.current_level < 2:
            # Noch mehr Level vorhanden → Level-Clear-Screen zeigen
            self._sfx(4)
            self.state = "level_clear"
        else:
            # Letztes Level (Level 3) geschafft → echter Sieg
            self._sfx(4); self._sfx(10)
            self.state = "win"

    def _advance_to_next_level(self):
        """Startet das naechste Level nach dem Level-Clear-Screen."""
        next_level = self.current_level + 1
        self._start(next_level)

    # ----------------------------------------------------------
    # INPUT
    # ----------------------------------------------------------
    def _input_menu(self):
        if pyxel.btnp(pyxel.KEY_1): self.selected_char = 0
        if pyxel.btnp(pyxel.KEY_2): self.selected_char = 1
        if pyxel.btnp(pyxel.KEY_RETURN): self._enter_story()

    def _input_play(self):
        if self.player.move_cd > 0: self.player.move_cd -= 1

        if pyxel.btnp(pyxel.KEY_Q):
            old = self.temp; self.temp = clamp(self.temp-1,TEMP_MIN,TEMP_MAX)
            if self.temp != old: self._msg("Temperature  -1",40,12); self._sfx(6)

        if pyxel.btnp(pyxel.KEY_E):
            old = self.temp; self.temp = clamp(self.temp+1,TEMP_MIN,TEMP_MAX)
            if self.temp != old: self._msg("Temperature  +1",40,9); self._sfx(5)

        if self.player.move_cd == 0:
            dx = dy = 0
            if   pyxel.btnp(pyxel.KEY_A) or pyxel.btnp(pyxel.KEY_LEFT):  dx = -1
            elif pyxel.btnp(pyxel.KEY_D) or pyxel.btnp(pyxel.KEY_RIGHT): dx =  1
            elif pyxel.btnp(pyxel.KEY_W) or pyxel.btnp(pyxel.KEY_UP):    dy =  1
            elif pyxel.btnp(pyxel.KEY_S) or pyxel.btnp(pyxel.KEY_DOWN):  dy = -1
            if dx != 0 or dy != 0:
                if dx != 0: self.player.facing = dx
                self._move(dx, dy)

    def _move(self, dx, dy):
        nx = self.player.x+dx; ny = self.player.y+dy
        if nx < 0 or nx >= GRID_W or ny < 0: return
        self._ensure_rows(ny)
        self.player.x = nx; self.player.y = ny
        spd = LEVEL_CONFIG[self.current_level][1]
        cell = self.rows[ny][nx]
        if self._eff(cell) == MUD: spd += 3
        self.player.move_cd = spd
        self.player.step   += 1
        self.score = max(self.score, self.player.y)
        if cell.key:
            cell.key = False; self.keys += 1
            self._sfx(3)
            kn = LEVEL_CONFIG[self.current_level][0]
            self._msg(f"Key {self.keys}/{kn}", 70, 10)
            if self.keys >= kn: self._spawn_fortress()

    # ----------------------------------------------------------
    # UPDATE
    # ----------------------------------------------------------
    def update(self):
        if self.state == "story":
            self._upd_menu_particles(); self._upd_story()
        elif self.state == "menu":
            self._upd_menu_particles(); self._input_menu(); return

        if self.state == "level_clear":
            self._upd_particles()
            if pyxel.btnp(pyxel.KEY_RETURN):
                self._advance_to_next_level()
            return

        if self.state in ("lose","win"):
            if pyxel.btnp(pyxel.KEY_RETURN): self._stop_bgm(); self.state="menu"
            return

        self._input_play()

        if self.player.move_cd > 0: self.player.anim = (self.player.anim+1)%20
        else:                        self.player.anim = 0

        self._upd_particles()
        self._ensure_rows(self.player.y+GRID_H+10)
        self._survival()

        if self.msg_timer > 0:
            self.msg_timer -= 1
            if self.msg_timer <= 0: self.msg = ""

        cell = self.rows[self.player.y][self.player.x]
        if self._eff(cell) == FORTRESS:
            self._level_complete()

    def _survival(self):
        cell = self.rows[self.player.y][self.player.x]
        eff  = self._eff(cell)

        if not self._safe(cell):
            if eff in (WATER,HOT_WATER): self._sfx(7); self._lose("Fell into water!")
            elif eff == LAVA:            self._sfx(8); self._lose("Fell into lava!")
            else:                                      self._lose("Dangerous ground!")
            return

        if eff == GRASS: self.player.grass_timer += 1
        else:            self.player.grass_timer = max(0, self.player.grass_timer-3)

        if self.player.grass_timer >= 85 and not self.eagle_warned:
            self._sfx(9); self.eagle_warned = True
        if self.player.grass_timer < 40: self.eagle_warned = False
        if self.player.grass_timer > 280: self._lose("The eagle got you!"); return

        cg = hg = 0
        if self.temp <= -2: cg += 2
        elif self.temp == -1: cg += 1
        if self.temp >= 2: hg += 2
        elif self.temp == 1: hg += 1

        if self._is_cold(cell): cg += 2 if eff==DEEP_ICE else 1
        if self._is_hot(cell):
            if eff == LAVA:                  hg += 999
            elif eff in (HOT_STONE,HOT_WATER): hg += 2

        self.player.cold_meter += cg
        self.player.heat_meter += hg
        if cg == 0: self.player.cold_meter = max(0, self.player.cold_meter-2)
        if hg == 0: self.player.heat_meter = max(0, self.player.heat_meter-2)

        if self.player.cold_meter >= self.player.cold_limit:
            self._lose("You froze to death!"); return
        if self.player.heat_meter >= self.player.heat_limit:
            self._lose("You burned to death!"); return

    # ===========================================================
    # DRAW HELPERS
    # ===========================================================
    def _sky_col(self):
        if self.temp <= -1: return 1,5,13
        if self.temp >= 1:  return 2,9,10
        return 1,13,6

    def _draw_sky(self):
        top,mid,low = self._sky_col()
        pyxel.cls(top)
        for y in range(100,200): pyxel.line(0,y,WIDTH,y,mid)
        for y in range(200,260): pyxel.line(0,y,WIDTH,y,low)

    def _draw_sun(self):
        if self.temp <= -1:
            pyxel.circ(420,65,22,7); pyxel.circ(424,61,22,5)
        elif self.temp >= 1:
            pyxel.circ(420,66,26,10); pyxel.circ(420,66,18,9)
            for i in range(8):
                idx = i * 32
                ex  = int(420 + 40*icos(idx))
                ey  = int(66  + 40*isin(idx))
                pyxel.line(420,66,ex,ey,10)
        else:
            pyxel.circ(420,66,20,7)

    def _draw_stars(self):
        if self.temp <= -1:
            for s in self.bg_stars:
                pyxel.pset(int(s["x"]),int(s["y"]),s["c"])

    def _draw_far_mts(self, cam):
        col = 5 if self.temp<=0 else 8
        for x,w,h in self.bg_far_peaks:
            px = int((x - cam*0.15) % (WIDTH+80)) - 30
            pyxel.tri(px,210,px+w//2,210-h,px+w,210,col)

    def _draw_near_mts(self, cam):
        col = 13 if self.temp<=-1 else (2 if self.temp>=1 else 1)
        for x,w,h in self.bg_near_peaks:
            px = int((x - cam*0.4) % (WIDTH+50)) - 20
            pyxel.tri(px,256,px+w//2,256-h,px+w,256,col)

    def _draw_particles(self):
        if self.temp >= 1:
            for p in self.embers:
                c = 10 if p["r"]==2 else 9
                if p["r"]==1: pyxel.pset(int(p["x"]),int(p["y"]),c)
                else: pyxel.circ(int(p["x"]),int(p["y"]),1,c)
        if self.temp <= -1:
            for p in self.snow:
                c = 7 if p["r"]==2 else 12
                if p["r"]==1: pyxel.pset(int(p["x"]),int(p["y"]),c)
                else: pyxel.circ(int(p["x"]),int(p["y"]),1,c)

    # ----------------------------------------------------------
    # TILES
    # ----------------------------------------------------------
    def _draw_cell(self, sx, sy, cell):
        eff = self._eff(cell)
        T   = TILE
        BASE = {GRASS:3,WATER:12,HOT_WATER:9,ICE:6,STONE:5,
                HOT_STONE:8,LAVA:10,FORTRESS:13,DEEP_ICE:1,MUD:4}
        pyxel.rect(sx,sy,T,T,BASE.get(eff,0))

        if eff == GRASS:
            pyxel.rect(sx,sy,T,5,11)
            for i in range(0,T,5):
                pyxel.line(sx+i,sy,sx+i,sy+6,3)
            for i in range(0,T,4):
                gy = sy+7+((i//4)%3)*2
                pyxel.line(sx+i,gy,sx+i+1,gy-2,11)

        elif eff == WATER:
            pyxel.rect(sx,sy,T,T,1)
            pyxel.rect(sx,sy+5,T,T-5,12)
            wv = (pyxel.frame_count//6)%4
            for i in range(0,T,5):
                pyxel.line(sx+i,sy+6+wv,sx+i+3,sy+6+wv,7)
                pyxel.line(sx+i,sy+14+(wv%2),sx+i+2,sy+14+(wv%2),7)

        elif eff == HOT_WATER:
            pyxel.rect(sx,sy,T,T,2)
            pyxel.rect(sx,sy+5,T,T-5,9)
            for i in range(0,T,5):
                pyxel.line(sx+i,sy+8,sx+i+3,sy+8,7)
            pyxel.circ(sx+5,sy+3,2,7); pyxel.circ(sx+14,sy+2,2,7)

        elif eff == ICE:
            pyxel.rect(sx,sy,T,T,6)
            pyxel.rect(sx+1,sy+1,T-2,T-2,12)
            pyxel.line(sx+3,sy+3,sx+20,sy+20,7)
            pyxel.line(sx+20,sy+3,sx+3,sy+20,7)
            pyxel.line(sx+11,sy+1,sx+11,sy+22,7)
            pyxel.line(sx+1,sy+11,sx+22,sy+11,6)

        elif eff == DEEP_ICE:
            pyxel.rect(sx,sy,T,T,13)
            pyxel.rect(sx+1,sy+1,T-2,T-2,1)
            pyxel.line(sx+3,sy+3,sx+20,sy+20,6)
            pyxel.line(sx+20,sy+3,sx+3,sy+20,6)
            pyxel.line(sx+11,sy+1,sx+11,sy+22,6)
            pyxel.line(sx+1,sy+11,sx+22,sy+11,13)
            pyxel.pset(sx+5,sy+5,7); pyxel.pset(sx+17,sy+17,7)
            pyxel.pset(sx+5,sy+17,7); pyxel.pset(sx+17,sy+5,7)

        elif eff == STONE:
            pyxel.rect(sx,sy,T,T,5)
            pyxel.rectb(sx+1,sy+1,T-2,T-2,1)
            pyxel.line(sx+8,sy+1,sx+8,sy+22,1)
            pyxel.line(sx+1,sy+12,sx+22,sy+12,1)
            pyxel.pset(sx+4,sy+6,6); pyxel.pset(sx+14,sy+6,6)
            pyxel.pset(sx+4,sy+17,6); pyxel.pset(sx+14,sy+17,6)

        elif eff == HOT_STONE:
            pyxel.rect(sx,sy,T,T,8)
            pyxel.rectb(sx+1,sy+1,T-2,T-2,2)
            pyxel.line(sx+8,sy+1,sx+8,sy+22,2)
            pyxel.line(sx+1,sy+12,sx+22,sy+12,2)
            pyxel.pset(sx+4,sy+6,10); pyxel.pset(sx+14,sy+6,10)
            pyxel.pset(sx+4,sy+17,9); pyxel.pset(sx+14,sy+17,9)

        elif eff == LAVA:
            ph = (pyxel.frame_count//4)%3
            pyxel.rect(sx,sy,T,T,8)
            for i in range(0,T,6):
                pyxel.line(sx,sy+i+ph,sx+T,sy+i+ph,2)
            pyxel.circ(sx+5,sy+5,2,9); pyxel.circ(sx+16,sy+14,3,9)
            pyxel.circ(sx+9,sy+18,2,7)

        elif eff == MUD:
            for i in range(0,T,7):
                pyxel.pset(sx+i,sy+(i%T),5)
                pyxel.pset(sx+(i+4)%T,sy+i,5)

        elif eff == FORTRESS:
            pyxel.rect(sx,sy,T,T,1)
            pyxel.rect(sx+3,sy+8,18,14,5)
            pyxel.rect(sx+4,sy+4,4,6,5)
            pyxel.rect(sx+16,sy+4,4,6,5)
            pyxel.rect(sx+8,sy+10,8,12,0)
            pyxel.rectb(sx+3,sy+8,18,14,7)
            pyxel.rectb(sx+4,sy+4,4,6,7)
            pyxel.rectb(sx+16,sy+4,4,6,7)
            pyxel.pset(sx+6,sy+6,10); pyxel.pset(sx+18,sy+6,10)

        if cell.animal and eff in (WATER,HOT_WATER):
            pyxel.rect(sx+5,sy+9,11,5,4)
            pyxel.rect(sx+14,sy+10,3,3,4)
            pyxel.pset(sx+15,sy+10,0)

        if cell.key:
            kx=sx+T//2-4; ky=sy+T//2-4
            pyxel.circ(kx+2,ky+2,3,10)
            pyxel.rect(kx+2,ky+2,1,1,9)
            pyxel.line(kx+5,ky+2,kx+11,ky+2,10)
            pyxel.line(kx+9,ky+2,kx+9,ky+5,10)
            pyxel.line(kx+11,ky+2,kx+11,ky+4,10)

    # ----------------------------------------------------------
    # PLAYER
    # ----------------------------------------------------------
    def _draw_player(self, sx, sy, char_type=None, move_cd=None, step=None, facing=None):
        ct  = char_type if char_type is not None else self.player.char_type
        cd  = move_cd   if move_cd   is not None else self.player.move_cd
        stp = step      if step      is not None else self.player.step
        fac = facing    if facing    is not None else self.player.facing

        walk  = cd > 0
        phase = stp % 4
        bob   = 1 if walk and phase in (1,3) else 0

        if ct == 0:
            CJ  = 12; CJ2 = 1; CTR = 5; CBT = 1; CVI = 6; CSC = 12
        else:
            CJ  = 9;  CJ2 = 8; CTR = 4; CBT = 8; CVI = 10; CSC = 9

        CSK = 15; CO = 0; CWH = 7; CHR = 4
        b = bob

        pyxel.rect(sx+5, sy+25+b, 12, 2, 1)
        pyxel.line(sx+4, sy+25+b, sx+4, sy+26+b, 1)
        pyxel.line(sx+17,sy+25+b, sx+17,sy+26+b, 1)

        if ct == 0 and self.temp <= -1:
            fc = pyxel.frame_count
            for dx2,dy2 in [(-3,0),(22,0),(-3,10),(22,10),(5,-5),(14,-5)]:
                if (fc//6 + dx2 + dy2) % 3 == 0:
                    pyxel.pset(sx+dx2, sy+dy2+b, 6)
        elif ct == 1 and self.temp >= 1:
            fc = pyxel.frame_count
            for dx2,dy2 in [(-2,2),(21,2),(-2,12),(21,12),(4,-4),(15,-4)]:
                if (fc//5 + dy2) % 3 == 0:
                    pyxel.pset(sx+dx2, sy+dy2+b, 9)

        pyxel.rect(sx+4,  sy+0+b, 14, 2, CJ)
        pyxel.rect(sx+5,  sy-2+b, 12, 3, CJ)
        pyxel.line(sx+5,  sy-2+b, sx+16,sy-2+b, CO)
        pyxel.line(sx+4,  sy-2+b, sx+4, sy+1+b, CO)
        pyxel.line(sx+17, sy-2+b, sx+17,sy+1+b, CO)
        pyxel.line(sx+5,  sy+1+b, sx+16,sy+1+b, CVI)
        pyxel.pset(sx+10, sy-1+b, CVI)
        pyxel.pset(sx+11, sy-1+b, CVI)

        pyxel.rect(sx+4, sy+3+b, 13, 8, CSK)
        pyxel.rect(sx+5, sy+10+b, 11, 1, CSK)
        pyxel.line(sx+4, sy+3+b, sx+16,sy+3+b, CJ)
        pyxel.line(sx+4, sy+3+b, sx+4, sy+10+b, CO)
        pyxel.line(sx+16,sy+3+b, sx+16,sy+10+b, CO)
        pyxel.line(sx+5, sy+11+b,sx+15,sy+11+b, CO)

        if fac >= 0:
            pyxel.pset(sx+7,  sy+5+b, CHR)
            pyxel.pset(sx+8,  sy+5+b, CHR)
            pyxel.pset(sx+11, sy+5+b, CO)
            pyxel.pset(sx+12, sy+5+b, CO)
            pyxel.pset(sx+11, sy+6+b, CWH)
            pyxel.pset(sx+12, sy+6+b, CWH)
            pyxel.pset(sx+12, sy+5+b, 13)
            pyxel.line(sx+10,sy+4+b,sx+13,sy+4+b, CHR)
            pyxel.line(sx+6, sy+4+b,sx+8, sy+4+b, CHR)
            pyxel.pset(sx+13,sy+7+b, CJ2)
            pyxel.line(sx+10,sy+9+b,sx+13,sy+9+b, CJ2)
        else:
            pyxel.pset(sx+11, sy+5+b, CHR)
            pyxel.pset(sx+12, sy+5+b, CHR)
            pyxel.pset(sx+7,  sy+5+b, CO)
            pyxel.pset(sx+8,  sy+5+b, CO)
            pyxel.pset(sx+7,  sy+6+b, CWH)
            pyxel.pset(sx+8,  sy+6+b, CWH)
            pyxel.pset(sx+7,  sy+5+b, 13)
            pyxel.line(sx+6, sy+4+b,sx+9, sy+4+b, CHR)
            pyxel.line(sx+11,sy+4+b,sx+13,sy+4+b, CHR)
            pyxel.pset(sx+6, sy+7+b, CJ2)
            pyxel.line(sx+7, sy+9+b,sx+10,sy+9+b, CJ2)

        if ct == 0:
            pyxel.rect(sx+3, sy+10+b, 15, 3, CSC)
            pyxel.line(sx+3, sy+10+b, sx+17,sy+10+b, CJ2)
            pyxel.line(sx+3, sy+12+b, sx+17,sy+12+b, CJ2)
            if fac >= 0:
                pyxel.line(sx+14,sy+12+b,sx+17,sy+15+b, CSC)
                pyxel.line(sx+15,sy+12+b,sx+18,sy+15+b, CSC)
            else:
                pyxel.line(sx+5, sy+12+b,sx+2, sy+15+b, CSC)
                pyxel.line(sx+4, sy+12+b,sx+1, sy+15+b, CSC)
        else:
            pyxel.line(sx+3, sy+10+b, sx+17,sy+10+b, CSC)
            pyxel.line(sx+3, sy+11+b, sx+17,sy+11+b, CSC)
            pyxel.pset(sx+10,sy+12+b, CVI)

        TY = 13
        pyxel.rect(sx+4, sy+TY+b, 13, 7, CJ)
        pyxel.line(sx+10,sy+TY+b, sx+10,sy+TY+6+b, CJ2)
        pyxel.rect(sx+5, sy+TY+1+b, 3, 2, CJ2)
        pyxel.line(sx+16,sy+TY+b, sx+16,sy+TY+6+b, CJ2)
        pyxel.line(sx+3, sy+TY+b, sx+3, sy+TY+6+b, CO)
        pyxel.line(sx+17,sy+TY+b, sx+17,sy+TY+6+b, CO)

        if fac >= 0:
            pyxel.rect(sx+17,sy+TY+b,   4, 6, CTR)
            pyxel.rect(sx+18,sy+TY+1+b, 2, 4, CJ)
            pyxel.rectb(sx+17,sy+TY+b,  4, 6, CO)
        else:
            pyxel.rect(sx+0, sy+TY+b,   4, 6, CTR)
            pyxel.rect(sx+1, sy+TY+1+b, 2, 4, CJ)
            pyxel.rectb(sx+0,sy+TY+b,   4, 6, CO)

        AT = TY + 1
        if walk:
            if phase in (0,2):
                pyxel.line(sx+17,sy+AT+b, sx+19,sy+AT+4+b, CJ)
                pyxel.line(sx+16,sy+AT+b, sx+18,sy+AT+4+b, CJ)
                pyxel.line(sx+3, sy+AT+b, sx+1, sy+AT+4+b, CJ)
                pyxel.line(sx+4, sy+AT+b, sx+2, sy+AT+4+b, CJ)
                pyxel.rect(sx+18,sy+AT+4+b, 2, 2, CBT)
                pyxel.rect(sx+0, sy+AT+4+b, 2, 2, CBT)
            else:
                pyxel.line(sx+17,sy+AT+b, sx+15,sy+AT+5+b, CJ)
                pyxel.line(sx+16,sy+AT+b, sx+14,sy+AT+5+b, CJ)
                pyxel.line(sx+3, sy+AT+b, sx+5, sy+AT+5+b, CJ)
                pyxel.line(sx+4, sy+AT+b, sx+6, sy+AT+5+b, CJ)
                pyxel.rect(sx+13,sy+AT+5+b, 2, 2, CBT)
                pyxel.rect(sx+5, sy+AT+5+b, 2, 2, CBT)
        else:
            pyxel.line(sx+17,sy+AT+b, sx+18,sy+AT+5+b, CJ)
            pyxel.line(sx+16,sy+AT+b, sx+17,sy+AT+5+b, CJ)
            pyxel.line(sx+3, sy+AT+b, sx+2, sy+AT+5+b, CJ)
            pyxel.line(sx+4, sy+AT+b, sx+3, sy+AT+5+b, CJ)
            pyxel.rect(sx+17,sy+AT+5+b, 2, 2, CBT)
            pyxel.rect(sx+1, sy+AT+5+b, 2, 2, CBT)

        BY = TY + 7
        pyxel.rect(sx+4,sy+BY+b, 13, 2, CO)
        pyxel.pset(sx+10,sy+BY+b,   CVI)
        pyxel.pset(sx+10,sy+BY+1+b, CVI)

        LY = BY + 2
        if walk:
            if phase in (0,2):
                pyxel.rect(sx+4, sy+LY+b,   5, 6, CTR)
                pyxel.rect(sx+11,sy+LY+2+b, 5, 4, CTR)
                pyxel.rect(sx+3, sy+LY+5+b, 6, 3, CBT)
                pyxel.rect(sx+10,sy+LY+5+b, 5, 3, CBT)
                pyxel.line(sx+3, sy+LY+7+b, sx+8, sy+LY+7+b, CJ2)
                pyxel.line(sx+10,sy+LY+7+b, sx+14,sy+LY+7+b, CJ2)
            else:
                pyxel.rect(sx+11,sy+LY+b,   5, 6, CTR)
                pyxel.rect(sx+4, sy+LY+2+b, 5, 4, CTR)
                pyxel.rect(sx+10,sy+LY+5+b, 6, 3, CBT)
                pyxel.rect(sx+3, sy+LY+5+b, 5, 3, CBT)
                pyxel.line(sx+10,sy+LY+7+b, sx+15,sy+LY+7+b, CJ2)
                pyxel.line(sx+3, sy+LY+7+b, sx+7, sy+LY+7+b, CJ2)
        else:
            pyxel.rect(sx+4, sy+LY+b, 5, 6, CTR)
            pyxel.rect(sx+11,sy+LY+b, 5, 6, CTR)
            pyxel.rect(sx+3, sy+LY+5+b, 6, 3, CBT)
            pyxel.rect(sx+11,sy+LY+5+b, 6, 3, CBT)
            pyxel.line(sx+3, sy+LY+7+b, sx+8, sy+LY+7+b, CJ2)
            pyxel.line(sx+11,sy+LY+7+b, sx+16,sy+LY+7+b, CJ2)
        pyxel.line(sx+9,sy+LY+b, sx+9,sy+LY+5+b, CJ2)

        kn    = LEVEL_CONFIG[self.current_level][0]
        total = min(self.keys, kn)
        if kn > 0:
            bx = sx + 2
            by2 = sy + b - 8
            pyxel.rect(bx-1, by2-1, kn*5+1, 5, 0)
            for i in range(kn):
                kc = 10 if i < total else 5
                pyxel.rect(bx+i*5, by2, 4, 3, kc)
                if i < total:
                    pyxel.pset(bx+i*5+1, by2+1, 9)


    # ----------------------------------------------------------
    # WELT ZEICHNEN
    # ----------------------------------------------------------
    def _draw_world(self):
        cam = max(0, self.player.y-8)
        self._draw_sky()
        self._draw_stars()
        self._draw_sun()
        self._draw_far_mts(cam)
        self._draw_near_mts(cam)
        self._draw_particles()

        gc = 13 if self.temp<=-1 else (2 if self.temp>=1 else 1)
        pyxel.rect(0,260,WIDTH,HEIGHT-260,gc)

        for sr in range(GRID_H-4):
            wr = cam+sr
            if wr >= len(self.rows): continue
            sy2 = HEIGHT-(sr+1)*TILE
            for x in range(GRID_W):
                self._draw_cell(x*TILE, sy2, self.rows[wr][x])


        psx = self.player.x*TILE
        psy = HEIGHT-((self.player.y-cam+1)*TILE)
        self._draw_player(psx, psy)

    # ----------------------------------------------------------
    # UI
    # ----------------------------------------------------------
    def _draw_ui(self):
        pyxel.rect(0,0,WIDTH,30,0)
        pyxel.rect(0,30,WIDTH,2,1)

        kn  = LEVEL_CONFIG[self.current_level][0]
        lbl = LEVEL_CONFIG[self.current_level][2]

        tc = 12 if self.temp<0 else (9 if self.temp>0 else 7)
        pyxel.rect(4,3,56,22,1)
        pyxel.rectb(4,3,56,22,tc)
        pyxel.text(8,6,"TEMP",5)
        pyxel.text(8,16,f"{self.temp:+d}",tc)

        pyxel.rect(68,3,80,22,1)
        pyxel.rectb(68,3,80,22,10)
        pyxel.text(72,6,"KEYS",5)
        pyxel.text(72,16,f"{self.keys}/{kn}",10)

        pyxel.rect(156,3,70,22,1)
        pyxel.rectb(156,3,70,22,13)
        pyxel.text(160,6,"LEVEL",5)
        pyxel.text(160,16,f"{self.current_level+1} {lbl}",13)

        pyxel.rect(234,3,70,22,1)
        pyxel.rectb(234,3,70,22,11)
        pyxel.text(238,6,"ROW",5)
        pyxel.text(238,16,f"{self.score}",11)

        cw = min(90, self.player.cold_meter*90//max(1,self.player.cold_limit))
        pyxel.rect(312,3,96,11,1)
        pyxel.rectb(312,3,96,11,12)
        pyxel.rect(312,3,cw,11,12)
        pyxel.text(314,6,"COLD",0)

        hw = min(90, self.player.heat_meter*90//max(1,self.player.heat_limit))
        pyxel.rect(312,16,96,11,1)
        pyxel.rectb(312,16,96,11,8)
        pyxel.rect(312,16,hw,11,8)
        pyxel.text(314,19,"HEAT",0)

        if self.eagle_warned and (pyxel.frame_count//8)%2==0:
            pyxel.text(416,6,"!!EAGLE!!",8)
        pyxel.text(416,18,"Q/E=TEMP",5)

        if self.msg:
            mw = len(self.msg)*4+8
            mx = (WIDTH-mw)//2
            pyxel.rect(mx,34,mw,14,0)
            pyxel.rectb(mx,34,mw,14,self.msg_col)
            pyxel.text(mx+4,38,self.msg,self.msg_col)

    # ----------------------------------------------------------
    # MENU
    # ----------------------------------------------------------
    def _draw_menu(self):
        pyxel.cls(0)

        for p in self.menu_particles:
            if p["r"] == 1: pyxel.pset(int(p["x"]),int(p["y"]),p["c"])
            else:           pyxel.circ(int(p["x"]),int(p["y"]),p["r"]-1,p["c"])

        pyxel.rect(34,24,WIDTH-68,80,0)
        pyxel.rectb(34,24,WIDTH-68,80,10)
        pyxel.rectb(36,26,WIDTH-72,76,9)
        title = "THERMOTRAIL"
        tx = (WIDTH - len(title)*4)//2
        pyxel.text(tx+1,38+1,title,8)
        pyxel.text(tx,  38,  title,10)
        sub = "512  EDITION"
        pyxel.text((WIDTH-len(sub)*4)//2, 54, sub, 9)
        desc = "ENDLESS RUNNER WITH TEMPERATURE"
        pyxel.text((WIDTH-len(desc)*4)//2, 70, desc, 7)

        sy0 = 122
        pyxel.rect(14,sy0-4,WIDTH-28,TILE+20,1)
        pyxel.rectb(14,sy0-4,WIDTH-28,TILE+20,5)
        samples = [
            (Cell(GRASS),     "Grass"),
            (Cell(WATER,animal=True),"Water"),
            (Cell(ICE),       "Ice"),
            (Cell(HOT_STONE), "HotRock"),
            (Cell(LAVA),      "Lava"),
            (Cell(DEEP_ICE),  "DeepIce"),
            (Cell(MUD),       "Mud"),
            (Cell(FORTRESS,fortress=True),"Fortress"),
        ]
        for i,(c,lb) in enumerate(samples):
            cx2 = 20 + i*(TILE+8)
            self._draw_cell(cx2, sy0, c)
            pyxel.text(cx2+(TILE-len(lb)*4)//2+2, sy0+TILE+3, lb, 6)

        sel_y = 182
        pyxel.rect(34,sel_y,WIDTH-68,84,0)
        pyxel.rectb(34,sel_y,WIDTH-68,84,7)
        pyxel.text(38,sel_y+4,"CHOOSE CHARACTER:  1  or  2",7)

        c0c = 10 if self.selected_char==0 else 5
        pyxel.rect(44,sel_y+18,120,58,1)
        pyxel.rectb(44,sel_y+18,120,58,c0c)
        self._draw_player(50, sel_y+24, char_type=0, move_cd=0, step=0, facing=1)
        self._draw_player(80, sel_y+24, char_type=0, move_cd=3, step=1, facing=1)
        pyxel.text(114,sel_y+22,"[1]",c0c)
        pyxel.text(114,sel_y+32,"COLD-",c0c)
        pyxel.text(114,sel_y+40,"WARRIOR",c0c)
        pyxel.text(114,sel_y+52,"Cold",7)
        pyxel.text(114,sel_y+60,"resistant",7)

        c1c = 10 if self.selected_char==1 else 5
        pyxel.rect(178,sel_y+18,120,58,1)
        pyxel.rectb(178,sel_y+18,120,58,c1c)
        self._draw_player(184, sel_y+24, char_type=1, move_cd=0, step=0, facing=1)
        self._draw_player(214, sel_y+24, char_type=1, move_cd=3, step=1, facing=1)
        pyxel.text(248,sel_y+22,"[2]",c1c)
        pyxel.text(248,sel_y+32,"HEAT-",c1c)
        pyxel.text(248,sel_y+40,"HERO",c1c)
        pyxel.text(248,sel_y+52,"Heat",7)
        pyxel.text(248,sel_y+60,"resistant",7)

        lv_y = 280
        pyxel.rect(34,lv_y,WIDTH-68,76,0)
        pyxel.rectb(34,lv_y,WIDTH-68,76,13)
        pyxel.text(38,lv_y+4,"LEVELS",13)
        info=[
            ("LVL 1  EASY","3 keys   basic terrain",11),
            ("LVL 2  MEDIUM","5 keys   deep ice + more lava",9),
            ("LVL 3  HARD","7 keys   mud + extreme terrain",8),
        ]
        for i,(t,d,c) in enumerate(info):
            pyxel.text(50, lv_y+16+i*18, t, c)
            pyxel.text(190,lv_y+16+i*18, d, 7)

        ct_y = 370
        pyxel.rect(34,ct_y,WIDTH-68,68,0)
        pyxel.rectb(34,ct_y,WIDTH-68,68,6)
        pyxel.text(38,ct_y+4,"CONTROLS",6)
        pyxel.text(50,ct_y+16,"WASD / ARROW KEYS  =  Move",7)
        pyxel.text(50,ct_y+26,"Q = lower temp          E = raise temp",7)
        pyxel.text(50,ct_y+36,"Changing temp makes dangerous tiles safe!",7)
        pyxel.text(50,ct_y+46,"Goal: collect keys, then reach the fortress",7)
        pyxel.text(50,ct_y+56,"Warning: stay on grass too long - eagle attack!",7)

        if (pyxel.frame_count//20)%2 == 0:
            pr = ">>  PRESS ENTER TO START  <<"
            pyxel.text((WIDTH-len(pr)*4)//2, 452, pr, 10)

    # ----------------------------------------------------------
    # LEVEL CLEAR  (zwischen den Levels)
    # ----------------------------------------------------------
    def _draw_level_clear(self):
        self._draw_world()
        self._draw_ui()

        completed_level = self.current_level   # gerade abgeschlossenes Level (0-basiert)
        next_level      = completed_level + 1  # naechstes Level (0-basiert)
        kn  = LEVEL_CONFIG[completed_level][0]
        lbl = LEVEL_CONFIG[completed_level][2]

        # Haupt-Panel
        px = 60; py = 140; pw = WIDTH-120; ph = 200
        pyxel.rect(px, py, pw, ph, 0)
        pyxel.rectb(px, py, pw, ph, 10)
        pyxel.rectb(px+2, py+2, pw-4, ph-4, 9)

        # Titel mit Level-Nummer
        title = f"LEVEL {completed_level+1} COMPLETE!"
        pyxel.text((WIDTH-len(title)*4)//2, py+14, title, 10)

        # Trennlinie
        pyxel.line(px+10, py+28, px+pw-10, py+28, 5)

        # Stats
        pyxel.text(px+20, py+38, f"Row reached:     {self.score}", 11)
        pyxel.text(px+20, py+52, f"Keys collected:  {self.keys}/{kn}", 10)
        pyxel.text(px+20, py+66, f"Difficulty:      {lbl}", 13)

        # Naechstes Level ankuendigen
        pyxel.line(px+10, py+84, px+pw-10, py+84, 5)
        nkn  = LEVEL_CONFIG[next_level][0]
        nlbl = LEVEL_CONFIG[next_level][2]
        next_title = f"NEXT: LEVEL {next_level+1}  [{nlbl}]"
        pyxel.text((WIDTH-len(next_title)*4)//2, py+94, next_title, 9)
        pyxel.text(px+20, py+110, f"Required: {nkn} keys", 7)

        # Neue Herausforderungen je nach Level
        if next_level == 1:
            pyxel.text(px+20, py+124, "New: Deep Ice, more Lava!", 8)
        elif next_level == 2:
            pyxel.text(px+20, py+124, "New: Mud + extreme terrain!", 8)

        # Blinkendes Enter
        if (pyxel.frame_count//20)%2 == 0:
            pr = "ENTER = CONTINUE TO LEVEL " + str(next_level+1)
            pyxel.text((WIDTH-len(pr)*4)//2, py+ph-20, pr, 7)

    # ----------------------------------------------------------
    # LOSE / WIN
    # ----------------------------------------------------------
    def _draw_lose(self):
        self._draw_world()
        pyxel.rect(60,170,WIDTH-120,150,0)
        pyxel.rectb(60,170,WIDTH-120,150,8)
        pyxel.text((WIDTH-len("GAME OVER")*4)//2,186,"GAME OVER",8)
        reason = self.player.death_reason
        pyxel.text(80,206,reason[:34],7)
        if len(reason)>34: pyxel.text(80,216,reason[34:],7)
        pyxel.text(80,232,f"Level {self.current_level+1}   Row: {self.score}",13)
        kn = LEVEL_CONFIG[self.current_level][0]
        pyxel.text(80,246,f"Keys: {self.keys}/{kn}",10)
        if (pyxel.frame_count//20)%2==0:
            pr = "ENTER = BACK TO MENU"
            pyxel.text((WIDTH-len(pr)*4)//2,300,pr,7)

    def _draw_win(self):
        self._draw_world()
        pyxel.rect(50,120,WIDTH-100,240,1)
        pyxel.rectb(50,120,WIDTH-100,240,10)
        pyxel.rectb(52,122,WIDTH-104,236,9)

        title = "ALL 3 LEVELS COMPLETE!"
        pyxel.text((WIDTH-len(title)*4)//2, 136, title, 10)
        pyxel.line(60,152, WIDTH-60,152, 5)

        pyxel.text(70,160,"You are a true ThermoTrail legend!", 7)

        # Scores aller 3 Level anzeigen
        pyxel.line(60,174, WIDTH-60,174, 5)
        pyxel.text(70,180,"RESULTS:", 13)
        level_cols = [11, 9, 8]
        for i in range(3):
            lbl = LEVEL_CONFIG[i][2]
            sc  = self.level_scores[i]
            kn  = LEVEL_CONFIG[i][0]
            pyxel.text(70, 194+i*16, f"Level {i+1} [{lbl}]:", level_cols[i])
            pyxel.text(200, 194+i*16, f"Row {sc}   Keys {kn}/{kn}", 7)

        # Festungs-Symbol
        pyxel.line(60,250, WIDTH-60,250, 5)
        self._draw_cell(WIDTH//2-TILE//2, 258, Cell(FORTRESS,fortress=True))

        if (pyxel.frame_count//20)%2==0:
            pr = "ENTER = MAIN MENU"
            pyxel.text((WIDTH-len(pr)*4)//2, 332, pr, 7)


    # ----------------------------------------------------------
    # STORY
    # ----------------------------------------------------------
    # 5 Panels: Titel-Bild + 4 Text-Szenen
    STORY_PANELS = [
        {
            "title": "THE THERMOCOLLAPSE",
            "lines": [
                "Once, all the world lived in balance.",
                "Ice to the north. Fire to the south.",
                "Water and stone held the middle ground.",
                "Order reigned. Peace held.",
            ],
            "scene": "world_ok",
        },
        {
            "title": "THE CATASTROPHE",
            "lines": [
                "Then the Thermocollapse struck.",
                "Lava tore through frozen glaciers.",
                "Boiling water swallowed the stone fields.",
                "Every climate zone collapsed into one.",
            ],
            "scene": "chaos",
        },
        {
            "title": "THE FORTRESSES",
            "lines": [
                "The Ancients hid the Order Fortresses",
                "deep within the chaos zones.",
                "Each fortress is sealed by magic locks.",
                "Only a full set of keys can open them.",
            ],
            "scene": "fortress",
        },
        {
            "title": "YOUR MISSION",
            "lines": [
                "You are the last ThermoWalker.",
                "Travel through the chaos zones.",
                "Gather every key scattered within.",
                "Reach the fortress. Restore the world.",
            ],
            "scene": "hero",
        },
        {
            "title": "YOUR WEAPON: TEMPERATURE",
            "lines": [
                "Your only tool is the ThermoGauge.",
                "Cool the world - lava turns to stone.",
                "Heat it up - ice becomes water.",
                "Q = cooler                E = warmer",
            ],
            "scene": "tool",
        },
    ]

    def _enter_story(self):
        self.story_panel     = 0
        self.story_char      = 0
        self.story_timer     = 0
        self.story_bg_off    = 0
        self.story_input_cd  = 8
        self._stop_lobby()
        self._play_story_music()
        self.state = "story"

    def _story_total_chars(self):
        panel = self.STORY_PANELS[self.story_panel]
        return sum(len(l) for l in panel["lines"])

    def _story_advance(self):
        total = self._story_total_chars()
        if self.story_char < total:
            self.story_char = total
        elif self.story_panel < len(self.STORY_PANELS) - 1:
            self.story_panel    += 1
            self.story_char      = 0
            self.story_timer     = 0
            self.story_input_cd  = 6
        else:
            self._stop_story_music(); self._start(0)

    def _upd_story(self):
        self.story_bg_off = (self.story_bg_off + 1) % (WIDTH * 2)
        # Input-Cooldown beim Panel-Eintritt abbauen
        if hasattr(self, 'story_input_cd') and self.story_input_cd > 0:
            self.story_input_cd -= 1
            return
        total = self._story_total_chars()
        if self.story_char < total:
            self.story_timer += 1
            if self.story_timer >= 2:
                self.story_timer = 0
                self.story_char += 1
        if pyxel.btnp(pyxel.KEY_SPACE):
            self._story_advance()
        # ENTER only works on the last panel to start the game
        if pyxel.btnp(pyxel.KEY_RETURN):
            is_last = (self.story_panel == len(self.STORY_PANELS) - 1)
            total = self._story_total_chars()
            if is_last and self.story_char >= total:
                self._stop_story_music(); self._start(0)
            else:
                self._story_advance()

    # ---- Szenen-Illustrationen -----------------------------------
    def _story_draw_scene(self, scene, fc):
        """Zeichnet eine kleine animierte Szene oben im Panel (ca. 180px hoch)."""
        cx = WIDTH // 2
        if scene == "world_ok":
            # Ruhige geteilte Welt: links Eis, rechts Lava, Mitte Gras/Stein
            pyxel.rect(0, 40, WIDTH//3, 190, 1)          # Eisseite dunkelblau
            pyxel.rect(WIDTH//3, 40, WIDTH//3, 190, 3)    # Gruen/Stein
            pyxel.rect(2*WIDTH//3, 40, WIDTH//3, 190, 2)  # Lava-Seite rot
            # Eis-Kristalle links
            for i in range(3):
                bx = 40 + i*50; by = 120
                pyxel.tri(bx, by, bx+10, by-40, bx+20, by, 6)
                pyxel.tri(bx+2, by, bx+10, by-30, bx+18, by, 12)
            # Gras Mitte
            pyxel.rect(WIDTH//3, 160, WIDTH//3, 30, 3)
            for i in range(0, WIDTH//3, 8):
                pyxel.line(WIDTH//3+i, 160, WIDTH//3+i+3, 148, 11)
            # Lava rechts: animierte Blasen
            ph = (fc // 8) % 6
            pyxel.circ(2*WIDTH//3+30, 155+ph, 6, 10)
            pyxel.circ(2*WIDTH//3+70, 148+(ph*2)%10, 9, 9)
            pyxel.circ(2*WIDTH//3+50, 165-(ph%4), 4, 8)
            # Trennlinien
            pyxel.line(WIDTH//3, 40, WIDTH//3, 230, 7)
            pyxel.line(2*WIDTH//3, 40, 2*WIDTH//3, 230, 7)
            # Sonne mittig oben
            pyxel.circ(cx, 70, 18, 10)
            for i in range(6):
                idx = i*42
                pyxel.line(cx, 70, int(cx+28*icos(idx)), int(70+28*isin(idx)), 9)
            # Text-Label
            pyxel.text(14, 220, "ICE", 6)
            pyxel.text(cx-8, 220, "LAND", 11)
            pyxel.text(2*WIDTH//3+10, 220, "FIRE", 9)

        elif scene == "chaos":
            # Chaos: alles durchgemischt, Risse, Explosionen
            pyxel.rect(0, 40, WIDTH, 190, 0)
            # Bunte Chaos-Bloecke
            blocks = [
                (20,  80,  60, 50, 10), (90,  60,  50, 70,  6),
                (150, 90,  70, 40, 12), (230, 50,  50, 80,  9),
                (290, 100, 60, 60,  8), (360, 70,  55, 50, 10),
                (420, 55,  70, 70,  1), (60,  150, 80, 40,  3),
                (170, 155, 65, 35,  5), (310, 145, 75, 45,  6),
            ]
            for bx,by,bw,bh,bc in blocks:
                pyxel.rect(bx, by, bw, bh, bc)
            # Explosions-Funken (animiert)
            sparks = [(130,95),(255,62),(400,88),(180,170),(340,158)]
            for i,(sx2,sy2) in enumerate(sparks):
                ph = (fc//4 + i*7) % 12
                r = [2,3,4,5,4,3,2,1,0,0,0,0][ph]
                if r > 0:
                    pyxel.circ(sx2, sy2, r, 10 if i%2==0 else 9)
            # Risse
            pyxel.line(100, 40,  80, 230, 8)
            pyxel.line(280, 40, 300, 230, 8)
            pyxel.line(420, 40, 440, 230, 8)
            # Chaotischer Himmel
            pyxel.rect(0, 40, WIDTH, 20, 2)
            for i in range(0, WIDTH, 30):
                jitter = (i * 7 + fc // 3) % 8
                pyxel.pset(i + jitter, 48, 9)
            pyxel.text(cx - 36, 215, "ALL IN CHAOS!", 8)

        elif scene == "fortress":
            # Festung auf einem Felsen, mit Schloessern
            pyxel.rect(0, 40, WIDTH, 190, 1)
            # Himmel mit Sternen
            for i in range(0, WIDTH, 22):
                twinkle = (fc // 10 + i) % 3
                pyxel.pset(i + 5, 55 + (i % 40), 7 if twinkle == 0 else 5)
            # Felsen
            pyxel.tri(cx-80, 230, cx, 140, cx+80, 230, 5)
            pyxel.tri(cx-60, 230, cx+5, 148, cx+70, 230, 13)
            # Festungsmauern
            pyxel.rect(cx-50, 145, 100, 70, 5)
            pyxel.rect(cx-45, 140,  20, 20, 5)
            pyxel.rect(cx+25, 140,  20, 20, 5)
            pyxel.rect(cx-5,  155,  10, 60,  0)   # Tuer
            pyxel.rectb(cx-50, 145, 100, 70, 7)
            pyxel.rectb(cx-45, 140,  20, 20, 7)
            pyxel.rectb(cx+25, 140,  20, 20, 7)
            # Schloss-Symbole (3 Stueck animiert leuchtend)
            for i, kx in enumerate([cx-30, cx-5, cx+20]):
                pulse = (fc // 8 + i * 5) % 10
                kc = 10 if pulse < 5 else 9
                pyxel.circ(kx+5, 165, 4, kc)
                pyxel.rect(kx+2, 168, 6, 4, kc)
                pyxel.pset(kx+5, 166, 0)
            # Turm-Fahne
            ph = (fc // 6) % 6
            pyxel.line(cx, 140, cx, 118, 7)
            pyxel.tri(cx, 118, cx+18-ph, 122, cx, 130, 8)
            pyxel.text(cx-32, 215, "SEALED!", 10)

        elif scene == "hero":
            # Held laeuft durch Chaos-Landschaft von links nach rechts
            pyxel.rect(0, 40, WIDTH, 190, 1)
            # Boden-Tiles (vereinfacht)
            tile_types = [5, 3, 10, 6, 5, 8, 3, 12, 5, 6, 10, 3, 5, 8, 6, 3, 10, 5, 12, 3, 5, 6]
            tile_cols  = {3:3, 5:5, 6:6, 8:8, 10:10, 12:12}
            scroll = (fc * 2) % (len(tile_types) * TILE)
            for i, t in enumerate(tile_types * 2):
                tx = i * TILE - scroll
                if -TILE < tx < WIDTH:
                    pyxel.rect(tx, 185, TILE, TILE, tile_cols.get(t, 5))
                    pyxel.rectb(tx, 185, TILE, TILE, 0)
            # Held in der Mitte (laufend)
            step = (fc // 6) % 4
            self._draw_player(cx - 10, 148,
                              char_type=self.selected_char,
                              move_cd=3, step=step, facing=1)
            # Schluessel-Icon das er einsammelt
            kx2 = cx + 60 - (fc * 3) % 120
            if kx2 > cx - 20:
                pyxel.circ(kx2, 170, 4, 10)
                pyxel.rect(kx2, 170, 1, 1, 9)
                pyxel.line(kx2+4, 170, kx2+9, 170, 10)
            pyxel.text(14, 215, "YOU ARE THE CHOSEN ONE!", 11)

        elif scene == "tool":
            # Temperatur-Regler Demo: links Eis->Wasser, rechts Lava->Stein
            pyxel.rect(0, 40, WIDTH, 190, 0)
            # Linke Haelfte: Kaelte-Effekt
            pyxel.rect(10, 55, 220, 155, 1)
            pyxel.text(14, 60, "TEMP -2: LAVA TURNS TO STONE", 6)
            # Lava -> Stein Animation
            phase_l = (fc // 15) % 2
            if phase_l == 0:
                pyxel.rect(30, 90, 70, 70, 10)   # Lava
                pyxel.rect(30+6, 90+6, 70-12, 70-12, 9)
                pyxel.text(34, 175, "LAVA", 10)
            else:
                pyxel.rect(30, 90, 70, 70, 5)    # Stein
                pyxel.rectb(31, 91, 68, 68, 1)
                pyxel.text(34, 175, "STONE!", 7)
            # Pfeil
            pyxel.line(110, 125, 130, 125, 12)
            pyxel.tri(128, 120, 140, 125, 128, 130, 12)
            # Eis -> Wasser
            if phase_l == 0:
                pyxel.rect(145, 90, 70, 70, 6)   # Eis
                pyxel.text(148, 175, "ICE", 6)
            else:
                pyxel.rect(145, 90, 70, 70, 12)  # Wasser
                pyxel.text(148, 175, "WATER!", 12)

            # Rechte Haelfte: Waerme-Effekt
            pyxel.rect(WIDTH-230, 55, 220, 155, 2)
            pyxel.text(WIDTH-226, 60, "TEMP +2: ICE MELTS TO WATER", 9)
            phase_r = (fc // 15 + 1) % 2
            if phase_r == 0:
                pyxel.rect(WIDTH-210, 90, 70, 70, 12)
                pyxel.text(WIDTH-206, 175, "WATER", 12)
            else:
                pyxel.rect(WIDTH-210, 90, 70, 70, 6)
                pyxel.text(WIDTH-206, 175, "ICE!", 6)
            pyxel.line(WIDTH-130, 125, WIDTH-110, 125, 9)
            pyxel.tri(WIDTH-112, 120, WIDTH-100, 125, WIDTH-112, 130, 9)
            if phase_r == 0:
                pyxel.rect(WIDTH-125, 90, 70, 70, 8)
                pyxel.text(WIDTH-121, 175, "HOT!", 8)
            else:
                pyxel.rect(WIDTH-125, 90, 70, 70, 9)
                pyxel.text(WIDTH-121, 175, "HOTTER", 9)

            # Regler-Symbol unten mittig
            pyxel.rect(cx-30, 210, 60, 8, 5)
            slider_x = cx - 30 + (fc // 4) % 60
            pyxel.rect(slider_x, 207, 6, 14, 7)
            pyxel.text(cx-16, 222, "Q  REGLER  E", 7)

    def _draw_story(self):
        fc = pyxel.frame_count
        panel = self.STORY_PANELS[self.story_panel]

        # Hintergrund: animierter Sternenhimmel
        pyxel.cls(0)
        for i, s in enumerate(self.bg_stars):
            twinkle = (fc // 8 + i) % 3
            pyxel.pset(int(s["x"]), int(s["y"] * 0.5) + 5,
                       s["c"] if twinkle != 0 else 0)

        # Panel-Nummer Punkte (5 Punkte oben)
        dot_total = len(self.STORY_PANELS)
        for i in range(dot_total):
            dx = WIDTH//2 - dot_total*8 + i*16
            col = 7 if i == self.story_panel else 5
            pyxel.circ(dx, 18, 3 if i == self.story_panel else 2, col)

        # Szenen-Illustration (obere Haelfte)
        self._story_draw_scene(panel["scene"], fc)

        # Trennlinie
        pyxel.rect(0, 232, WIDTH, 2, 5)

        # Titel des Panels
        title = panel["title"]
        tx = (WIDTH - len(title)*4) // 2
        pyxel.text(tx+1, 239, title, 0)    # Schatten
        pyxel.text(tx,   238, title, 10)

        # Typewriter-Text
        chars_left = self.story_char
        for i, line in enumerate(panel["lines"]):
            shown = line[:chars_left]
            chars_left = max(0, chars_left - len(line))
            col = 7 if i < 3 else 11   # letzte Zeile heller
            pyxel.text(20, 254 + i*13, shown, col)

        # "Weiter"-Hinweis (blinkt nur wenn Text fertig)
        total = self._story_total_chars()
        if self.story_char >= total:
            if (fc // 18) % 2 == 0:
                is_last = (self.story_panel == len(self.STORY_PANELS) - 1)
                hint = "ENTER = START GAME" if is_last else "SPACE = CONTINUE"
                hw = len(hint)*4 + 8
                hx = (WIDTH - hw) // 2
                pyxel.rect(hx, 309, hw, 12, 0)
                pyxel.rectb(hx, 309, hw, 12, 10)
                pyxel.text(hx+4, 312, hint, 10)

        # Panel-Zaehler
        pyxel.text(4, 312, f"{self.story_panel+1}/{len(self.STORY_PANELS)}", 5)


    # ----------------------------------------------------------
    # MAIN DRAW / UPDATE
    # ----------------------------------------------------------
    def draw(self):
        if self.state == "story":         self._draw_story()
        elif self.state == "menu":        self._draw_menu()
        elif self.state == "play":        self._draw_world(); self._draw_ui()
        elif self.state == "level_clear": self._draw_level_clear()
        elif self.state == "lose":        self._draw_lose()
        elif self.state == "win":         self._draw_win()

    def update(self):
        if not self._lobby_started:
            self._lobby_started = True
            self._play_lobby()
        if self.state == "story":
            self._upd_story()
        elif self.state == "menu":
            self._upd_menu_particles(); self._input_menu()
        elif self.state == "level_clear":
            self._upd_particles()
            if pyxel.btnp(pyxel.KEY_RETURN):
                self._advance_to_next_level()
        elif self.state in ("lose","win"):
            if pyxel.btnp(pyxel.KEY_RETURN): self._stop_bgm(); self.state="menu"; self._play_lobby()
        elif self.state == "play":
            self._input_play()
            if self.player.move_cd>0: self.player.anim=(self.player.anim+1)%20
            else: self.player.anim=0
            self._upd_particles()
            self._ensure_rows(self.player.y+GRID_H+10)
            self._survival()
            if self.msg_timer>0:
                self.msg_timer-=1
                if self.msg_timer<=0: self.msg=""
            cell=self.rows[self.player.y][self.player.x]
            if self._eff(cell)==FORTRESS:
                self._level_complete()

Game()