import pyxel
import math
import random
 
SCREEN_W   = 512
SCREEN_H   = 512
DUCK_W     = 16
DUCK_H     = 26
FPS        = 30
TIME_LIMIT = 200
WIN_SCORE  = 1000
WIN_SCORE2 = 1500
GRAVITY    = 0.40
JUMP_V     = -7.5
SPEED      = 2
 
T_STONE  = 0
T_WATER  = 1
T_PLAT   = 2
T_FROG   = 3
T_THORN  = 4
T_NEST   = 5
T_SPIKE  = 6
T_NEST2  = 7
 
STATE_INTRO = 0
STATE_PLAY  = 1
 
GROUND_Y = SCREEN_H - 12
 
HILL_X  = 720
HILL_W  = 160
HILL_H  = 60
 
HILL2_X = 1300
HILL2_W = 160
HILL2_H = 70
 
 
class DuckSprite:
    FRAME0 = [
        "0000333330000000",
        "0003333333000000",
        "0003333333000000",
        "000333b333000000",
        "000333b3aaaa0000",
        "00033307aaaa0000",
        "0003333333000000",
        "0003333333000000",
        "00cccccccc000000",
        "0ccccccccccd0000",
        "ccccccccccdd0000",
        "ccccccccccdd0000",
        "ccc7777cccdd0000",
        "ccc7777ccccc0000",
        "0cc7777cccc00000",
        "00ccccccc0000000",
        "00ccccccc0000000",
        "000ccc0000000000",
        "000ccc0000000000",
        "000aa00000000000",
        "00aaaa0000000000",
        "0aaaaa0000000000",
        "0000000000000000",
        "0000000000000000",
        "0000000000000000",
        "0000000000000000",
    ]
    FRAME1 = [
        "0000333330000000",
        "0003333333000000",
        "0003333333000000",
        "000333b333000000",
        "000333b3aaaa0000",
        "00033307aaaa0000",
        "0003333333000000",
        "0003333333000000",
        "ddcccccccc000000",
        "dddcccccccccdddd",
        "ddddccccccccddddd",
        "0dddccccccccdddd",
        "00ddccc7777cddd0",
        "000dccc7777cc000",
        "0000cc7777cc0000",
        "00000ccccc000000",
        "00000ccccc000000",
        "000ccc00000000000",
        "000ccc00000000000",
        "000aa00000000000",
        "00aaaa0000000000",
        "0aaaaa0000000000",
        "0000000000000000",
        "0000000000000000",
        "0000000000000000",
        "0000000000000000",
    ]
    FRAME2 = [
        "0000333330000000",
        "0003333333000000",
        "0003333333000000",
        "000333b333000000",
        "000333b3a0000000",
        "00033307a0000000",
        "0003333333000000",
        "ddcccccccc000000",
        "dddcccccccccdddd",
        "0dddccccccccddd0",
        "00ddccc7777cdd00",
        "000dccc7777cc000",
        "0000cc7777cc0000",
        "00000ccccc000000",
        "00000ccccc000000",
        "000ccc00000000000",
        "000ccc00000000000",
        "000aa00000000000",
        "00aaaa0000000000",
        "0aaaaa0000000000",
        "0000000000000000",
        "0000000000000000",
        "0000000000000000",
        "0000000000000000",
        "0000000000000000",
        "0000000000000000",
    ]
    NEST_DUCK = [
        "003333000",
        "033b33000",
        "030aaa000",
        "0ccccc000",
        "0c777c000",
        "0ccccc000",
        "000cc0000",
        "000aa0000",
        "000000000",
    ]
    NEST_DUCK2 = [
        "009999000",
        "099b99000",
        "090aaa000",
        "0eeeee000",
        "0e777e000",
        "0eeeee000",
        "000ee0000",
        "000aa0000",
        "000000000",
    ]
    HEX = {c: i for i, c in enumerate("0123456789abcdef")}
 
    def __init__(self):
        self._load_frames()
 
    def _load_frames(self):
        img = pyxel.images[0]
        for fi, frame_data in enumerate([self.FRAME0, self.FRAME1, self.FRAME2]):
            ox = fi * 32
            for ry, row in enumerate(frame_data[:26]):
                row = row[:16].ljust(16, '0')
                for rx, ch in enumerate(row):
                    img.pset(ox + rx, ry, self.HEX.get(ch, 0))
        for ry, row in enumerate(self.NEST_DUCK):
            row = row[:9].ljust(9, '0')
            for rx, ch in enumerate(row):
                img.pset(96 + rx, ry, self.HEX.get(ch, 0))
        for ry, row in enumerate(self.NEST_DUCK2):
            row = row[:9].ljust(9, '0')
            for rx, ch in enumerate(row):
                img.pset(108 + rx, ry, self.HEX.get(ch, 0))
 
    def draw(self, x, y, frame=0, flip=False):
        u = frame * 32
        w = -16 if flip else 16
        pyxel.blt(int(x), int(y), 0, u, 0, w, 26, 0)
 
    def draw_nest_duck(self, x, y):
        pyxel.blt(int(x), int(y), 0, 96, 0, 9, 9, 0)
 
    def draw_nest_duck2(self, x, y):
        pyxel.blt(int(x), int(y), 0, 108, 0, 9, 9, 0)
 
 
class World:
    def __init__(self):
        random.seed(42)
        self.camera_x    = 0.0
        self.obstacles   = self._make_obstacles()
        self._gen_decorations()
        self.particles   = []
        self.fog_timer   = 0
        self.fog_active  = 0
        self.fog_alpha   = 0
        self._schedule_fog()
        self.cloud_offset = 0.0
        self.mud_bubbles  = []
 
    def _gen_decorations(self):
        self.trees = []
        x = 80
        while x < 1500:
            h = random.randint(55, 110)
            self.trees.append((x, h))
            x += random.randint(80, 160)
        self.bushes = []
        x = 50
        while x < 1400:
            w = random.randint(18, 38)
            self.bushes.append((x, w))
            x += random.randint(55, 120)
        self.clouds = []
        for _ in range(22):
            self.clouds.append([
                random.randint(0, 1400),
                random.randint(18, 110),
                random.randint(40, 90)
            ])
        self.mountain_xs = [100, 280, 500, 700, 900, 1100, 1350]
 
    def _make_obstacles(self):
        H = GROUND_Y
        obs = []
        obs += [
            (160,  H-18,  20, 18, T_STONE),
            (240,  H-20,  24, 20, T_STONE),
            (330,  H-10,  38, 10, T_WATER),
            (340,  H-52,  32,  8, T_PLAT),
            (410,  H-18,  10, 18, T_SPIKE),
        ]
        obs += [
            (540,  H-20,  22, 20, T_STONE),
            (610,  H-10,  50, 10, T_WATER),
            (618,  H-56,  36,  8, T_PLAT),
            (662,  H-56,  32,  8, T_PLAT),
            (700,  H-18,  10, 18, T_SPIKE),
            (715,  H-18,  10, 18, T_SPIKE),
        ]
        obs += [
            (830,  H-20,  26, 20, T_STONE),
            (900,  H-20,  26, 20, T_STONE),
            (960,  H-18,  10, 18, T_SPIKE),
            (975,  H-18,  10, 18, T_SPIKE),
            (990,  H-18,  10, 18, T_SPIKE),
            (1010, H-58,  38,  8, T_PLAT),
        ]
        obs += [
            (1070, H-10,  44, 10, T_WATER),
            (1080, H-60,  32,  8, T_PLAT),
            (1120, H-60,  28,  8, T_PLAT),
            (1200, H-20,  22, 20, T_STONE),
            (1240, H-18,  10, 18, T_SPIKE),
        ]
        obs += [(HILL_X, GROUND_Y - HILL_H - 4, HILL_W, 10, T_NEST)]
        obs += [
            (820,  H-18,  10, 18, T_SPIKE),
            (1360, H-20,  24, 20, T_STONE),
            (1420, H-10,  40, 10, T_WATER),
            (1430, H-58,  36,  8, T_PLAT),
            (1470, H-18,  10, 18, T_SPIKE),
            (1485, H-18,  10, 18, T_SPIKE),
            (1520, H-20,  22, 20, T_STONE),
            (1560, H-20,  26, 20, T_STONE),
        ]
        obs += [(HILL2_X, GROUND_Y - HILL2_H - 4, HILL2_W, 10, T_NEST2)]
        return obs
 
    def _schedule_fog(self):
        # Pause zwischen Nebel-Phasen: zusammen mit der 4s-Dauer ergibt das
        # einen Zyklus von 12 Sekunden zwischen zwei Nebelstarts.
        self.fog_timer = FPS * 8
 
    def update(self, camera_x):
        self.camera_x = camera_x
        self.cloud_offset += 0.15
        for p in self.particles[:]:
            p[0] += p[2]; p[1] += p[3]; p[3] += 0.2; p[6] -= 1
            if p[6] <= 0:
                self.particles.remove(p)
        if self.fog_active > 0:
            self.fog_active -= 1
            half = FPS * 1   # letzte 1s = ausblenden
            full = FPS * 3   # erste 1s = einblenden, danach 2s voll
            if self.fog_active > full:
                self.fog_alpha = min(130, self.fog_alpha + 5)
            elif self.fog_active < half:
                self.fog_alpha = max(0, self.fog_alpha - 4)
            else:
                self.fog_alpha = 130
        else:
            self.fog_timer -= 1
            if self.fog_timer <= 0:
                self.fog_active = FPS * 4   # Nebel dauert insgesamt 4 Sekunden
                self.fog_alpha  = 0
                self._schedule_fog()
        if random.randint(0, 3) == 0:
            for (ox, oy, ow, oh, t) in self.obstacles:
                if t == T_WATER:
                    sx = ox - camera_x
                    if -ow < sx < SCREEN_W + ow:
                        bx = ox + random.randint(2, ow - 2)
                        self.mud_bubbles.append([
                            bx, float(oy + oh - 2),
                            random.uniform(-0.3, 0.3),
                            random.uniform(-1.2, -0.4),
                            random.randint(8, 16)
                        ])
        for b in self.mud_bubbles[:]:
            b[0] += b[2]; b[1] += b[3]; b[4] -= 1
            if b[4] <= 0:
                self.mud_bubbles.remove(b)
 
    def spawn_blood(self, x, y):
        for _ in range(22):
            ang = random.uniform(0, math.pi * 2)
            spd = random.uniform(0.8, 4.5)
            self.particles.append([x+8, y+10, math.cos(ang)*spd, math.sin(ang)*spd-2.0, 0, 0, random.randint(12,22), 'blood'])
 
    def spawn_stars(self, x, y):
        for _ in range(10):
            ang = random.uniform(0, math.pi * 2)
            spd = random.uniform(0.6, 2.5)
            self.particles.append([x+8, y, math.cos(ang)*spd, math.sin(ang)*spd-1.2, 0, 0, random.randint(14,20), 'star'])
 
    def spawn_hearts(self, x, y):
        for _ in range(12):
            ang = random.uniform(0, math.pi * 2)
            spd = random.uniform(0.5, 2.0)
            self.particles.append([x+8, y, math.cos(ang)*spd, math.sin(ang)*spd-1.5, 0, 0, random.randint(20,35), 'heart'])
 
    def draw_bg(self):
        if self.fog_active > 0 and self.fog_alpha > 20:
            t = self.fog_alpha / 130.0
            sky_col = 1 if t > 0.5 else 12
            pyxel.rect(0, 0, SCREEN_W, GROUND_Y, sky_col)
            fc = pyxel.frame_count
            for yi in range(0, GROUND_Y, 8):
                if (yi // 8 + fc // 4) % 3 == 0:
                    pyxel.rect(0, yi, SCREEN_W, 2, 5)
        else:
            pyxel.rect(0, 0, SCREEN_W, GROUND_Y, 12)
        self._draw_sun()
        self._draw_clouds()
        self._draw_mountains()
        self._draw_trees()
        self._draw_ground()
        self._draw_bushes()
 
    def _draw_sun(self):
        if self.fog_active > 0 and self.fog_alpha > 40:
            return
        sx, sy = 430, 55
        fc = pyxel.frame_count
        for k in range(12):
            ang = k * math.pi / 6 + fc * 0.008
            x1 = sx + int(math.cos(ang) * 28); y1 = sy + int(math.sin(ang) * 28)
            x2 = sx + int(math.cos(ang) * 36); y2 = sy + int(math.sin(ang) * 36)
            pyxel.line(x1, y1, x2, y2, 10)
        pyxel.circ(sx, sy, 22, 10)
        pyxel.circ(sx, sy, 18, 9)
        pyxel.circ(sx, sy, 14, 10)
 
    def _draw_clouds(self):
        fc = pyxel.frame_count
        storm = self.fog_active > 0 and self.fog_alpha > 30
        col_main = 5 if storm else 7
        col_shadow = 0 if storm else 6
        for cloud in self.clouds:
            cx = int(cloud[0] - self.camera_x * 0.25 + self.cloud_offset) % (SCREEN_W + 150) - 80
            cy, cw = cloud[1], cloud[2]
            if storm:
                cx += int(math.sin(fc * 0.03 + cloud[1]) * 2)
            pyxel.circ(cx,         cy,     cw//4+2, col_main)
            pyxel.circ(cx+cw//3,   cy-4,   cw//3+3, col_main)
            pyxel.circ(cx+cw//2,   cy,     cw//4+2, col_main)
            pyxel.circ(cx+cw*2//3, cy-2,   cw//4+1, col_main)
            pyxel.rect(cx,         cy,     cw+4,    cw//3+2, col_main)
            pyxel.rect(cx+2,       cy+cw//3-1, cw, 2, col_shadow)
 
    def _draw_mountains(self):
        for mx in self.mountain_xs:
            mxs = int(mx - self.camera_x * 0.12)
            if mxs + 140 < 0 or mxs - 140 > SCREEN_W:
                continue
            mh = 80 + (mx % 50)
            pyxel.tri(mxs-70, GROUND_Y-2, mxs+70, GROUND_Y-2, mxs, GROUND_Y-2-mh, 5)
            pyxel.tri(mxs-40, GROUND_Y-2, mxs+40, GROUND_Y-2, mxs, GROUND_Y-2-mh+15, 6)
            pyxel.tri(mxs-18, GROUND_Y-2-mh+22, mxs+18, GROUND_Y-2-mh+22, mxs, GROUND_Y-2-mh, 7)
 
    def _draw_trees(self):
        for (tx, th) in self.trees:
            tsx = int(tx - self.camera_x * 0.65)
            if tsx + 30 < 0 or tsx > SCREEN_W + 30:
                continue
            trunk_h = th // 3
            bx = tsx
            pyxel.rect(bx-4, GROUND_Y-trunk_h, 8, trunk_h, 4)
            pyxel.rect(bx-2, GROUND_Y-trunk_h, 3, trunk_h, 14)
            base_y = GROUND_Y - trunk_h
            for layer in range(3):
                lw = 32 - layer * 8
                ly = base_y - layer * (th // 4)
                lh = th // 3 + 4
                pyxel.tri(bx-lw, ly+lh, bx+lw, ly+lh, bx, ly, 3)
                pyxel.tri(bx-lw+4, ly+lh, bx+lw-4, ly+lh, bx, ly+4, 11)
                pyxel.tri(bx-lw, ly+lh, bx, ly+lh, bx, ly, 2)
 
    def _draw_ground(self):
        pyxel.rect(0, GROUND_Y, SCREEN_W, 4, 11)
        pyxel.rect(0, GROUND_Y+2, SCREEN_W, 2, 3)
        pyxel.rect(0, GROUND_Y+4, SCREEN_W, SCREEN_H - GROUND_Y - 4, 4)
        pyxel.rect(0, GROUND_Y+6, SCREEN_W, SCREEN_H - GROUND_Y - 6, 14)
        for gx in range(0, SCREEN_W, 6):
            pyxel.line(gx,   GROUND_Y, gx,   GROUND_Y-4, 11)
            pyxel.pset(gx,   GROUND_Y-5, 3)
            pyxel.line(gx+3, GROUND_Y, gx+3, GROUND_Y-3, 3)
 
    def _draw_bushes(self):
        for (bx, bw) in self.bushes:
            bxs = int(bx - self.camera_x)
            if bxs + bw + 10 < 0 or bxs > SCREEN_W + 10:
                continue
            by = GROUND_Y
            r  = bw // 2
            pyxel.circ(bxs,       by,   r-1, 2)
            pyxel.circ(bxs+r,     by-3, r+2, 3)
            pyxel.circ(bxs+bw-2,  by,   r-1, 2)
            pyxel.circ(bxs+r-2,   by-5, r-3, 11)
            pyxel.circ(bxs+r+3,   by-4, r-4, 11)
            for fi in range(4):
                fx  = bxs + 4 + fi * (bw // 4)
                fy  = by - r // 2 - random.randint(0, 4)
                col = 8 if fi % 2 == 0 else 10
                pyxel.circ(fx, fy, 2, col)
                pyxel.pset(fx, fy-1, 7)
 
    def draw_hill_and_nest(self, duck_sprite, score):
        sx = int(HILL_X - self.camera_x)
        if sx + HILL_W + 20 < 0 or sx - 20 > SCREEN_W:
            return
        for xi in range(HILL_W):
            t_val = xi / HILL_W
            h_val = int(math.sin(t_val * math.pi) * HILL_H)
            base_y = GROUND_Y
            pyxel.line(sx+xi, base_y, sx+xi, base_y - h_val + 3, 4)
            if h_val > 0:
                pyxel.line(sx+xi, base_y - h_val + 3, sx+xi, base_y - h_val, 11)
            if xi % 5 == 0 and h_val > 5:
                pyxel.pset(sx+xi, base_y - h_val - 1, 3)
        nx = sx + HILL_W // 2 - 18
        ny = GROUND_Y - HILL_H - 4
        pyxel.circ(nx+18, ny+12, 18, 4)
        pyxel.circ(nx+18, ny+10, 15, 14)
        pyxel.circ(nx+18, ny+8,  11, 4)
        for si in range(6):
            sx2 = nx + 4 + si * 5
            pyxel.line(sx2, ny+5, sx2+3, ny+12, 10)
            pyxel.line(sx2+2, ny+4, sx2+4, ny+11, 9)
        pyxel.circb(nx+18, ny+12, 18, 2)
        pyxel.circb(nx+18, ny+12, 19, 14)
        pyxel.circ(nx+10, ny+5, 5, 7)
        pyxel.circ(nx+18, ny+3, 5, 13)
        pyxel.circ(nx+26, ny+5, 5, 7)
        pyxel.pset(nx+9,  ny+3, 7)
        pyxel.pset(nx+17, ny+1, 7)
        pyxel.pset(nx+25, ny+3, 7)
        duck_sprite.draw_nest_duck(nx + 12, ny - 7)
        if score < WIN_SCORE:
            pyxel.text(nx, ny - 20, f"{WIN_SCORE} Pkt!", 9)
        elif score >= WIN_SCORE:
            fc  = pyxel.frame_count
            col = 10 if (fc // 6) % 2 == 0 else 9
            pyxel.circb(nx+18, ny+6, 24, col)
            pyxel.text(nx+2, ny - 20, "Zwischenziel!", 11)
 
    def draw_hill2_and_nest(self, duck_sprite, score):
        sx = int(HILL2_X - self.camera_x)
        if sx + HILL2_W + 20 < 0 or sx - 20 > SCREEN_W:
            return
        for xi in range(HILL2_W):
            t_val = xi / HILL2_W
            h_val = int(math.sin(t_val * math.pi) * HILL2_H)
            base_y = GROUND_Y
            pyxel.line(sx+xi, base_y, sx+xi, base_y - h_val + 3, 4)
            if h_val > 0:
                pyxel.line(sx+xi, base_y - h_val + 3, sx+xi, base_y - h_val, 10)
            if xi % 4 == 0 and h_val > 5:
                pyxel.pset(sx+xi, base_y - h_val - 1, 9)
                pyxel.pset(sx+xi+2, base_y - h_val - 2, 10)
        nx = sx + HILL2_W // 2 - 22
        ny = GROUND_Y - HILL2_H - 4
        pyxel.circ(nx+22, ny+14, 22, 4)
        pyxel.circ(nx+22, ny+12, 18, 14)
        pyxel.circ(nx+22, ny+10, 13, 4)
        for si in range(8):
            sx2 = nx + 3 + si * 5
            pyxel.line(sx2, ny+6, sx2+3, ny+14, 10)
            pyxel.line(sx2+2, ny+5, sx2+4, ny+13, 9)
        pyxel.circb(nx+22, ny+14, 22, 9)
        pyxel.circb(nx+22, ny+14, 23, 10)
        pyxel.circ(nx+9,  ny+6, 5, 7)
        pyxel.circ(nx+17, ny+4, 5, 13)
        pyxel.circ(nx+27, ny+4, 5, 13)
        pyxel.circ(nx+35, ny+6, 5, 7)
        for ex, ey in [(nx+8, ny+4), (nx+16, ny+2), (nx+26, ny+2), (nx+34, ny+4)]:
            pyxel.pset(ex, ey, 7)
        duck_sprite.draw_nest_duck(nx + 12, ny - 7)
        duck_sprite.draw_nest_duck2(nx + 24, ny - 7)
        fc = pyxel.frame_count
        if score >= WIN_SCORE2:
            hx = nx + 20; hy = ny - 14
            col = 8 if (fc // 4) % 2 == 0 else 15
            pyxel.pset(hx-1, hy, col); pyxel.pset(hx+1, hy, col)
            pyxel.pset(hx, hy+1, col); pyxel.pset(hx-2, hy-1, col); pyxel.pset(hx+2, hy-1, col)
            col2 = 10 if (fc // 5) % 2 == 0 else 9
            pyxel.circb(nx+22, ny+8, 28, col2); pyxel.circb(nx+22, ny+8, 31, col2)
            pyxel.text(nx, ny - 28, "YOU ARE THE WINNER!", 10)
            pyxel.line(nx+20, ny-20, nx+20, ny-10, 10)
            pyxel.tri(nx+17, ny-10, nx+23, ny-10, nx+20, ny-5, 10)
        elif score >= WIN_SCORE:
            pyxel.text(nx+2, ny - 20, "ZIEL!", 10)
            pyxel.line(nx+20, ny-14, nx+20, ny-8, 10)
            pyxel.tri(nx+17, ny-8, nx+23, ny-8, nx+20, ny-4, 10)
        else:
            pyxel.text(nx, ny - 20, f"{WIN_SCORE2} Pkt!", 9)
 
    def draw_obstacles(self):
        for i, (ox, oy, ow, oh, t) in enumerate(self.obstacles):
            sx = int(ox - self.camera_x)
            if sx + ow + 20 < 0 or sx - 20 > SCREEN_W:
                continue
            if   t == T_STONE: self._draw_stone(sx, oy, ow, oh)
            elif t == T_WATER: self._draw_mud(sx, oy, ow, oh)
            elif t == T_PLAT:  self._draw_platform(sx, oy, ow, oh)
            elif t == T_SPIKE: self._draw_spike(sx, oy, ow, oh)
        for b in self.mud_bubbles:
            bsx = int(b[0] - self.camera_x); bsy = int(b[1])
            if 0 <= bsx < SCREEN_W and 0 <= bsy < SCREEN_H:
                r = max(1, b[4] // 6)
                pyxel.circb(bsx, bsy, r, 4)
 
    def _draw_stone(self, sx, oy, ow, oh):
        pyxel.rect(sx, oy, ow, oh, 0)
        pyxel.rect(sx+1, oy+1, ow-2, oh-2, 5)
        pyxel.line(sx+3, oy+3, sx+7, oy+8, 0)
        pyxel.line(sx+ow-5, oy+4, sx+ow-2, oy+9, 0)
        pyxel.line(sx+2, oy+oh-4, sx+6, oy+oh-2, 0)
        pyxel.line(sx+1, oy+1, sx+ow-3, oy+1, 6)
        pyxel.pset(sx+1, oy+2, 6)
        pyxel.rectb(sx, oy, ow, oh, 0)
        pyxel.rect(sx+1, oy+oh, ow-1, 2, 0)
 
    def _draw_mud(self, sx, oy, ow, oh):
        fc = pyxel.frame_count
        pyxel.rect(sx-2, oy-2, ow+4, oh+4, 0)
        pyxel.rect(sx, oy, ow, oh, 4)
        pyxel.rect(sx+1, oy+2, ow-2, oh-3, 14)
        for xi in range(0, ow-3, 6):
            wave = int(math.sin(fc * 0.12 + xi * 0.4) * 2)
            yy   = oy + 3 + max(0, min(oh-5, wave))
            pyxel.line(sx+xi, yy, sx+xi+4, yy, 4)
        for xi in range(2, ow-2, 9):
            pyxel.circ(sx+xi, oy+oh//2, 2, 0)
        pyxel.line(sx, oy, sx+ow-1, oy, 0)
        pyxel.line(sx, oy+1, sx+ow-1, oy+1, 4)
        pyxel.text(sx+2, oy+2, "~", 0)
 
    def _draw_platform(self, sx, oy, ow, oh):
        pyxel.rect(sx, oy, ow, oh, 0)
        pyxel.rect(sx+1, oy+1, ow-2, oh-2, 5)
        pyxel.rect(sx+1, oy+1, ow-2, 2, 6)
        for xi in range(4, ow-2, 7):
            pyxel.line(sx+xi, oy+1, sx+xi, oy+oh-2, 0)
        pyxel.rectb(sx, oy, ow, oh, 0)
        pyxel.pset(sx+2, oy+2, 7); pyxel.pset(sx+ow-3, oy+2, 7)
 
    def _draw_spike(self, sx, oy, ow, oh):
        n_spikes = max(1, ow // 10)
        sw = ow // n_spikes
        for si in range(n_spikes):
            x0 = sx + si * sw; x1 = x0 + sw; xm = x0 + sw // 2
            pyxel.tri(x0, oy+oh, x1, oy+oh, xm, oy, 0)
            pyxel.line(xm, oy, x0, oy+oh, 5)
            pyxel.line(xm, oy, x1, oy+oh, 6)
            pyxel.pset(xm, oy, 7)
        pyxel.rect(sx, oy+oh-3, ow, 3, 0)
        pyxel.line(sx, oy+oh-3, sx+ow-1, oy+oh-3, 5)
 
    def draw_particles(self):
        for p in self.particles:
            x, y = int(p[0]), int(p[1])
            if not (0 <= x < SCREEN_W and 0 <= y < SCREEN_H):
                continue
            if p[7] == 'blood':
                size = max(1, p[6] // 6)
                if size > 1:
                    pyxel.circ(x, y, size, 8); pyxel.circ(x, y, max(1, size-1), 2)
                else:
                    pyxel.pset(x, y, 8)
            elif p[7] == 'heart':
                col = 8 if p[6] % 6 < 3 else 15
                pyxel.pset(x, y, col); pyxel.pset(x+1, y, col); pyxel.pset(x, y+1, col)
            else:
                col = [10, 9, 7, 11][p[6] % 4]
                pyxel.pset(x, y, col)
                if p[6] > 8:
                    pyxel.pset(x+1, y, col); pyxel.pset(x, y+1, col)
 
    def draw_fog(self):
        if self.fog_alpha <= 0:
            return
        alpha = self.fog_alpha
        # Dichterer Nebel: kleinerer step, mehr Punkte
        step = max(1, 4 - alpha // 50)
        fc   = pyxel.frame_count
        # Regen-/Sturmstriche bei starkem Nebel
        if alpha > 60:
            for ri in range(50):
                rx = (ri * 37 + fc * 7) % SCREEN_W
                ry = (ri * 53 + fc * 10) % SCREEN_H
                pyxel.line(rx, ry, rx-1, ry+5, 1)
        # Nebel-Pixel — dichter durch höheren Schwellwert
        for yi in range(0, SCREEN_H, step):
            wave = int(math.sin(fc * 0.04 + yi * 0.03) * 6)
            for xi in range(0, SCREEN_W, step):
                noise = (xi * 7 + yi * 13 + fc * 3) % 100
                threshold = min(95, alpha)   # bis zu 95% Abdeckung
                if noise < threshold:
                    col = 1 if alpha > 80 else 5
                    pyxel.pset(xi + wave % 3, yi, col)
        # Zweite Schicht für Extra-Dichte
        if alpha > 60:
            for yi in range(0, SCREEN_H, step + 1):
                for xi in range(0, SCREEN_W, step + 1):
                    noise2 = (xi * 11 + yi * 17 + fc * 5 + 50) % 100
                    if noise2 < alpha // 2:
                        pyxel.pset(xi, yi, 5)
 
 
class Game:
    def __init__(self):
        self.duck  = DuckSprite()
        self.world = World()
        self.state = STATE_INTRO
        self._reset_state()
 
    def _reset_state(self):
        self.player_x     = 30.0
        self.player_y     = float(GROUND_Y - DUCK_H)
        self.y_vel        = 0.0
        self.is_jumping   = False
        self.on_platform  = False
        self.camera_x     = 0.0
        self.score        = 0
        self.time_left    = float(TIME_LIMIT)
        self.game_over    = False
        self.game_won     = False
        self.game_won2    = False
        self.happy_timer  = 0
        self.last_passed  = 0
        self.sprite_frame = 0
        self.dead_timer   = 0
        self.world.particles.clear()
        self.world.mud_bubbles.clear()
        self.world.camera_x = 0.0
 
    def update(self):
        # ---- INTRO ----
        if self.state == STATE_INTRO:
            if (pyxel.btnp(pyxel.KEY_SPACE) or pyxel.btnp(pyxel.KEY_RETURN)
                    or pyxel.btnp(pyxel.KEY_RIGHT) or pyxel.btnp(pyxel.KEY_UP)):
                self.state = STATE_PLAY
            return
 
        # ---- SPIEL ----
        if self.game_over:
            if self.dead_timer > 0:
                self.dead_timer -= 1
            if pyxel.btnp(pyxel.KEY_R):
                self._reset_state()
            return
 
        if self.game_won2:
            if pyxel.btnp(pyxel.KEY_R):
                self._reset_state()
            return
 
        if pyxel.btn(pyxel.KEY_RIGHT):
            self.camera_x += SPEED
            self.score    += SPEED
        if pyxel.btn(pyxel.KEY_LEFT):
            self.camera_x = max(0.0, self.camera_x - SPEED)
        if (pyxel.btnp(pyxel.KEY_UP) or pyxel.btnp(pyxel.KEY_SPACE)) and not self.is_jumping:
            self.is_jumping  = True
            self.on_platform = False
            self.y_vel       = JUMP_V
            pyxel.play(1, 1)
 
        if self.is_jumping:
            self.sprite_frame = 1
        elif self.happy_timer > 0:
            self.sprite_frame = 2
        else:
            self.sprite_frame = 0
 
        self.y_vel    += GRAVITY
        self.player_y += self.y_vel
        ground_level   = float(GROUND_Y - DUCK_H)
        if self.player_y >= ground_level:
            self.player_y   = ground_level
            self.y_vel      = 0.0
            self.is_jumping = False
 
        self.time_left -= 1.0 / FPS
        if self.time_left <= 0:
            self.time_left = 0
            self._die()
            return
 
        self.world.update(self.camera_x)
 
        world_x = self.camera_x + self.player_x
        for ox, oy, ow, oh, t in self.world.obstacles:
            if t in (T_STONE, T_WATER, T_SPIKE) and ox + ow < world_x - 5:
                if ox > self.last_passed:
                    self.last_passed = ox
                    self.happy_timer = 50
                    self.world.spawn_stars(self.player_x, self.player_y)
                    pyxel.play(0, 4)
 
        if self.happy_timer > 0:
            self.happy_timer -= 1
        self._check_collisions()
 
    def _die(self):
        if not self.game_over:
            self.game_over  = True
            self.dead_timer = 30
            self.world.spawn_blood(self.player_x, self.player_y)
            pyxel.play(2, 2)
 
    def _check_collisions(self):
        px = self.player_x; py = self.player_y
        pw = DUCK_W;        ph = DUCK_H - 4
        self.on_platform = False
        for i, (ox, oy, ow, oh, t) in enumerate(self.world.obstacles):
            sx = ox - self.camera_x
            if sx + ow < -pw or sx > SCREEN_W + pw:
                continue
            actual_oy = oy
            if t in (T_PLAT, T_NEST, T_NEST2):
                prev_y    = py - self.y_vel
                in_x      = px + pw > sx and sx + ow > px
                in_y      = py + ph >= actual_oy and py + ph <= actual_oy + oh + abs(self.y_vel) + 1
                was_above = prev_y + ph <= actual_oy + 2
                if in_x and in_y and was_above and self.y_vel >= 0:
                    self.player_y    = float(actual_oy - ph)
                    self.y_vel       = 0.0
                    self.is_jumping  = False
                    self.on_platform = True
                    if t == T_NEST and self.score >= WIN_SCORE and not self.game_won:
                        self.game_won = True
                        self.world.spawn_stars(self.player_x, self.player_y)
                        pyxel.play(0, 4)
                    if t == T_NEST2 and self.score >= WIN_SCORE2:
                        self.game_won2 = True
                        self.world.spawn_hearts(self.player_x, self.player_y)
                        pyxel.play(3, 3)
                continue
            if not (px + pw > sx and sx + ow > px and py + ph > actual_oy and py < actual_oy + oh):
                continue
            if t in (T_STONE, T_WATER, T_SPIKE):
                self._die()
                return
 
    # ================================================================
    # DRAW
    # ================================================================
    def draw(self):
        if self.state == STATE_INTRO:
            self._draw_intro()
            return
 
        self.world.draw_bg()
        self.world.draw_hill_and_nest(self.duck, self.score)
        self.world.draw_hill2_and_nest(self.duck, self.score)
        self.world.draw_obstacles()
        self.world.draw_particles()
 
        if not self.game_over or self.dead_timer > 15:
            self.duck.draw(self.player_x, self.player_y, frame=self.sprite_frame)
 
        if self.happy_timer > 0:
            fc = pyxel.frame_count
            hx, hy = int(self.player_x), int(self.player_y)
            for k in range(4):
                ang  = fc * 0.16 + k * 1.57
                sx2  = hx + 8 + int(math.cos(ang) * 10)
                sy2  = hy - 6 + int(math.sin(ang) * 4)
                col  = [10, 9, 7, 11][k]
                pyxel.pset(sx2, sy2, col)
 
        self.world.draw_fog()
        self._draw_hud()
 
        if self.game_over:
            self._draw_game_over()
        if self.game_won2:
            self._draw_win2()
        elif self.game_won:
            self._draw_win_intermediate()
 
    # ================================================================
    # INTRO SCREEN
    # ================================================================
    def _draw_intro(self):
        fc = pyxel.frame_count
 
        # Hintergrund: Nachthimmel
        pyxel.rect(0, 0, SCREEN_W, SCREEN_H, 1)
 
        # Sterne — blinken
        star_pos = [
            (23,18),(58,9),(97,31),(134,14),(178,6),(212,27),(267,11),(311,22),
            (349,8),(388,19),(431,13),(467,28),(503,7),(38,45),(82,52),(155,38),
            (228,50),(295,42),(362,35),(425,48),(489,41),(19,65),(73,58),(140,71),
            (195,62),(250,55),(320,68),(380,59),(445,72),(498,61),(12,88),(60,82),
        ]
        for i, (sx, sy) in enumerate(star_pos):
            bright = (fc + i * 11) % 28
            col = 7 if bright < 14 else (6 if bright < 21 else 5)
            pyxel.pset(sx, sy, col)
 
        # Mond
        pyxel.circ(460, 52, 20, 7)
        pyxel.circ(470, 46, 15, 1)   # Beissauschnitt → Halbmond
        pyxel.circ(460, 52, 20, 7)
 
        # Berge im Hintergrund
        for mx, mh in [(55,70),(155,95),(270,80),(390,105),(490,72)]:
            pyxel.tri(mx-60, GROUND_Y, mx+60, GROUND_Y, mx, GROUND_Y-mh, 5)
            pyxel.tri(mx-28, GROUND_Y, mx+28, GROUND_Y, mx, GROUND_Y-mh+18, 6)
            # Schneekappe
            pyxel.tri(mx-12, GROUND_Y-mh+22, mx+12, GROUND_Y-mh+22, mx, GROUND_Y-mh, 7)
 
        # Boden
        pyxel.rect(0, GROUND_Y, SCREEN_W, SCREEN_H - GROUND_Y, 2)
        pyxel.rect(0, GROUND_Y, SCREEN_W, 3, 11)
        pyxel.rect(0, GROUND_Y+3, SCREEN_W, 2, 3)
 
        # Gras-Halme
        for gx in range(0, SCREEN_W, 6):
            pyxel.line(gx, GROUND_Y, gx, GROUND_Y-3, 11)
            pyxel.pset(gx, GROUND_Y-4, 3)
 
        # Büsche
        for bx in [30, 115, 210, 305, 400, 490]:
            pyxel.circ(bx,    GROUND_Y,   10, 2)
            pyxel.circ(bx+12, GROUND_Y-4, 13, 3)
            pyxel.circ(bx+24, GROUND_Y,   9,  2)
            pyxel.circ(bx+10, GROUND_Y-12,8,  11)
 
        # Laternen / atmosphärische Lichter
        for lx in [90, 260, 420]:
            pyxel.line(lx, GROUND_Y-30, lx, GROUND_Y, 6)
            pyxel.rect(lx-4, GROUND_Y-38, 9, 9, 0)
            pyxel.rect(lx-3, GROUND_Y-37, 7, 7, 10)
            # Lichtschein
            glow_col = 10 if (fc // 10 + lx) % 2 == 0 else 9
            pyxel.pset(lx-5, GROUND_Y-33, glow_col)
            pyxel.pset(lx+5, GROUND_Y-33, glow_col)
            pyxel.pset(lx,   GROUND_Y-40, glow_col)
 
        # Ente links — wackelt und bewegt sich
        duck_x = 28
        duck_y = GROUND_Y - DUCK_H
        wobble  = int(math.sin(fc * 0.15) * 2)
        fr_idx  = 1 if (fc // 10) % 2 == 0 else 0
        self.duck.draw(duck_x, duck_y + wobble, frame=fr_idx)
 
        # Herzen über der Ente
        for hi in range(3):
            hx  = duck_x + 18 + hi * 9
            hy  = duck_y - 10 - hi * 7 + int(math.sin(fc * 0.11 + hi * 1.3) * 3)
            hcol = 8 if (fc // 5 + hi) % 2 == 0 else 15
            pyxel.pset(hx,   hy,   hcol);  pyxel.pset(hx+1, hy,   hcol)
            pyxel.pset(hx,   hy+1, hcol);  pyxel.pset(hx-1, hy-1, hcol)
            pyxel.pset(hx+2, hy-1, hcol)
 
        # Nest rechts mit Partner-Ente
        nx = SCREEN_W - 72
        ny = GROUND_Y - 24
        pyxel.circ(nx+12, ny+12, 14, 4)
        pyxel.circ(nx+12, ny+10, 11, 14)
        pyxel.circ(nx+12, ny+8,  7,  4)
        for si in range(4):
            pyxel.line(nx+2+si*5, ny+5, nx+4+si*5, ny+12, 10)
        self.duck.draw_nest_duck(nx+4,  ny-8)
        self.duck.draw_nest_duck2(nx+14, ny-8)
 
        # Gestrichelte Linie Ente → Nest mit Pfeil
        arr_y  = GROUND_Y - 35
        px_s   = duck_x + 20
        px_e   = nx - 6
        for xi in range(px_s, px_e, 10):
            pyxel.pset(xi,   arr_y, 9)
            pyxel.pset(xi+1, arr_y, 9)
            pyxel.pset(xi+2, arr_y, 9)
        pyxel.tri(px_e, arr_y-3, px_e, arr_y+3, px_e+5, arr_y, 9)
 
        # ---- TITELBOX ----
        # äusserer Rahmen
        pyxel.rect(55, 14, 402, 44, 0)
        pyxel.rectb(55, 14, 402, 44, 9)
        pyxel.rectb(56, 15, 400, 42, 10)
        # Titel
        title_col = 10 if (fc // 7) % 2 == 0 else 9
        pyxel.text(88, 24, "* ENTCHEN-ABENTEUER *", title_col)
        pyxel.text(96, 38, "Finde den Weg zu deiner Familie!", 7)
 
        # ---- TRENNLINIE ----
        pyxel.line(10, 64, SCREEN_W-10, 64, 5)
 
        # ---- INFO: GESCHICHTE (links oben) ----
        ix, iy = 10, 70
        pyxel.rect(ix, iy, 158, 86, 0)
        pyxel.rectb(ix, iy, 158, 86, 12)
        pyxel.text(ix+4, iy+4, "DEINE MISSION", 12)
        pyxel.line(ix+4, iy+12, ix+150, iy+12, 5)
        pyxel.text(ix+4, iy+16, "Du hast dich verirrt und", 7)
        pyxel.text(ix+4, iy+24, "bist von der Familie", 7)
        pyxel.text(ix+4, iy+32, "getrennt. Ueberquere", 7)
        pyxel.text(ix+4, iy+40, "Steine, Schlamm und", 7)
        pyxel.text(ix+4, iy+48, "Stacheln - trotze dem", 7)
        pyxel.text(ix+4, iy+56, "Sturm und komm heim!", 7)
        pyxel.text(ix+4, iy+68, "Du schaffst das!", 11)
        pyxel.text(ix+4, iy+76, "Beeil dich: 200 Sek.!", 8)
 
        # ---- INFO: STEUERUNG (mitte oben) ----
        sx2, sy2 = 174, 70
        pyxel.rect(sx2, sy2, 110, 86, 0)
        pyxel.rectb(sx2, sy2, 110, 86, 13)
        pyxel.text(sx2+4, sy2+4, "STEUERUNG", 13)
        pyxel.line(sx2+4, sy2+12, sx2+102, sy2+12, 5)
        # Tasten-Kästchen + Beschriftung
        for label, desc, row in [
            (" -> ", "laufen",   0),
            (" <- ", "zurueck",  1),
            (" UP ", "springen", 2),
            ("SPC" , "springen", 3),
            (" R  ", "neu start",4),
        ]:
            ky = sy2 + 18 + row * 13
            pyxel.rect(sx2+4, ky, 22, 9, 5)
            pyxel.rectb(sx2+4, ky, 22, 9, 7)
            pyxel.text(sx2+5, ky+1, label, 7)
            pyxel.text(sx2+30, ky+1, desc, 6)
 
        # ---- INFO: GEFAHREN (rechts oben) ----
        gx2, gy2 = 290, 70
        pyxel.rect(gx2, gy2, 112, 86, 0)
        pyxel.rectb(gx2, gy2, 112, 86, 8)
        pyxel.text(gx2+4, gy2+4, "GEFAHREN", 8)
        pyxel.line(gx2+4, gy2+12, gx2+104, gy2+12, 5)
        dangers = [
            ("[X]", "Stein",     6),
            ("[~]", "Schlamm",   14),
            ("[^]", "Stacheln",  8),
            ("[#]", "Plattform", 11),
            ("[*]", "Sturm",     5),
        ]
        for sym, name, col in dangers:
            idx = dangers.index((sym, name, col))
            dy  = gy2 + 18 + idx * 13
            pyxel.text(gx2+4,  dy, sym,  col)
            pyxel.text(gx2+24, dy, name, 7)
 
        # ---- INFO: PLATTFORM-TIPP ----
        pyxel.text(gx2+4, gy2+83, "Plattf. = sicher!", 11)
 
        # ---- ZIELE: Banner ----
        zx, zy = 408, 70
        pyxel.rect(zx, zy, 96, 86, 0)
        pyxel.rectb(zx, zy, 96, 86, 10)
        pyxel.text(zx+4, zy+4, "ZIELE", 10)
        pyxel.line(zx+4, zy+12, zx+88, zy+12, 5)
        # Zwischenziel
        pyxel.rect(zx+4, zy+17, 88, 18, 5)
        pyxel.text(zx+6, zy+19, "1000 Punkte", 7)
        pyxel.text(zx+6, zy+27, "Nest 1", 9)
        # Pfeil runter
        pyxel.line(zx+48, zy+36, zx+48, zy+42, 5)
        pyxel.tri(zx+44, zy+42, zx+52, zy+42, zx+48, zy+46, 5)
        # Echtes Ziel
        z2col = 10 if (fc // 8) % 2 == 0 else 9
        pyxel.rect(zx+4, zy+49, 88, 18, 2)
        pyxel.rectb(zx+4, zy+49, 88, 18, z2col)
        pyxel.text(zx+6, zy+51, "1500 Punkte", 7)
        pyxel.text(zx+6, zy+59, "Familie!!", z2col)
        # Zeit
        pyxel.line(zx+4, zy+70, zx+88, zy+70, 5)
        tcol = 8 if (fc // 12) % 2 == 0 else 7
        pyxel.text(zx+4, zy+74, "Zeit: 200s", tcol)
        pyxel.text(zx+4, zy+82, "Beeil dich!", 8)
 
        # ---- TRENNLINIE 2 ----
        pyxel.line(10, 162, SCREEN_W-10, 162, 5)
 
        # ---- HINDERNISSE: kleine Pixel-Vorschau ----
        pyxel.text(10, 166, "HINDERNISSE:", 6)
        # Stein-Miniatur
        pyxel.rect(80, 164, 16, 12, 5)
        pyxel.rectb(80, 164, 16, 12, 0)
        pyxel.text(80, 178, "Stein", 6)
        # Schlamm-Miniatur
        pyxel.rect(140, 164, 20, 8, 14)
        pyxel.rect(140, 162, 20, 3, 4)
        pyxel.text(138, 178, "Schlamm", 6)
        # Stacheln-Miniatur
        for si in range(3):
            sx3 = 210 + si * 7
            pyxel.tri(sx3, 176, sx3+6, 176, sx3+3, 164, 0)
            pyxel.line(sx3+3, 164, sx3, 176, 5)
        pyxel.text(210, 178, "Stacheln", 6)
        # Plattform-Miniatur
        pyxel.rect(278, 170, 28, 6, 5)
        pyxel.rectb(278, 170, 28, 6, 0)
        pyxel.text(278, 178, "Plattform", 6)
        # Sturm-Miniatur
        for yi2 in range(164, 176, 3):
            for xi2 in range(358, 390, 3):
                if (xi2 + yi2) % 6 == 0:
                    pyxel.pset(xi2, yi2, 5)
        pyxel.text(360, 178, "Sturm", 6)
 
        # ---- TRENNLINIE 3 ----
        pyxel.line(10, 188, SCREEN_W-10, 188, 5)
 
        # ---- TIPPS ----
        pyxel.text(10, 192, "TIPPS:", 11)
        pyxel.text(10, 202, "- Springe ueber Schlamm und Stacheln!", 7)
        pyxel.text(10, 210, "- Nutze Plattformen als Sprungbrett.", 7)
        pyxel.text(10, 218, "- Im Sturm: langsam und vorsichtig.", 5)
        pyxel.text(10, 226, "- Passierte Hindernisse = Sterne + Punkte!", 10)
 
        # ---- TRENNLINIE 4 ----
        pyxel.line(10, 236, SCREEN_W-10, 236, 5)
 
        # ---- START-AUFFORDERUNG ----
        sc = 10 if (fc // 10) % 2 == 0 else 7
        start_x = SCREEN_W // 2 - 72
        pyxel.rect(start_x - 4, 242, 156, 18, 0)
        pyxel.rectb(start_x - 4, 242, 156, 18, sc)
        pyxel.text(start_x, 247, "LEERTASTE zum Starten!", sc)
 
        pyxel.text(SCREEN_W//2 - 52, 264, "Viel Glueck, kleine Ente!", 6)
 
    def _draw_hud(self):
        pyxel.rect(0, 0, SCREEN_W, 20, 1)
        pyxel.text(6,   6, f"Zeit: {int(self.time_left)}s", 7)
        pyxel.text(110, 6, f"Punkte: {self.score}/{WIN_SCORE2}", 10)
        bw     = SCREEN_W - 12
        filled1 = int(bw * min(self.score, WIN_SCORE)  / WIN_SCORE2)
        filled2 = int(bw * min(self.score, WIN_SCORE2) / WIN_SCORE2)
        pyxel.rect(6, 16, bw,      4, 5)
        pyxel.rect(6, 16, filled2, 4, 3)
        pyxel.rect(6, 16, filled1, 4, 11)
        pyxel.rectb(6, 16, bw, 4, 7)
        mid = int(bw * WIN_SCORE / WIN_SCORE2)
        pyxel.line(6 + mid, 15, 6 + mid, 21, 9)
        if self.world.fog_active > 0 and self.world.fog_alpha > 10:
            fc  = pyxel.frame_count
            col = 8 if (fc // 8) % 2 == 0 else 7
            pyxel.text(SCREEN_W // 2 - 32, 26, "!! STURM !!", col)
        if self.score < 60:
            pyxel.text(SCREEN_W // 2 - 52, 32, "Pfeiltasten bewegen / Leertaste springen", 6)
        if self.score < WIN_SCORE:
            nest_sx = int(HILL_X - self.camera_x)
            if 0 < nest_sx < SCREEN_W:
                pyxel.text(6, 26, f"Zwischenziel: {WIN_SCORE} Pkt!", 9)
        elif self.score < WIN_SCORE2:
            nest2_sx = int(HILL2_X - self.camera_x)
            if 0 < nest2_sx < SCREEN_W:
                pyxel.text(6, 26, f"Weiter! Noch {WIN_SCORE2 - self.score} Pkt!", 10)
 
    def _draw_game_over(self):
        pyxel.rect(80, 180, 352, 152, 0)
        pyxel.rectb(80, 180, 352, 152, 8)
        pyxel.rectb(81, 181, 350, 150, 2)
        fc  = pyxel.frame_count
        col = 8 if (fc // 6) % 2 == 0 else 2
        pyxel.text(192, 200, "GAME OVER", col)
        pyxel.text(152, 218, "Die Ente hat das Zeitliche gesegnet...", 7)
        pyxel.text(196, 238, f"Punkte: {self.score}", 9)
        pyxel.text(192, 256, "R = Neu starten", 13)
 
    def _draw_win_intermediate(self):
        pyxel.rect(90, 200, 332, 60, 0)
        pyxel.rectb(90, 200, 332, 60, 11)
        pyxel.text(150, 212, "Zwischenziel erreicht!", 11)
        pyxel.text(130, 226, "Weiter zum echten Ziel bei 1500!", 10)
        pyxel.text(170, 240, "Drueck weiter...", 6)
 
    def _draw_win2(self):
        fc  = pyxel.frame_count
        col = 10 if (fc // 4) % 2 == 0 else 9
        pyxel.rect(50, 150, 412, 200, 0)
        pyxel.rectb(50, 150, 412, 200, col)
        pyxel.rectb(51, 151, 410, 198, 10)
        pyxel.rectb(52, 152, 408, 196, 9)
        for hk in range(6):
            hx = 70 + hk * 62; hy = 162
            hcol = 8 if (fc // 3 + hk) % 2 == 0 else 15
            pyxel.pset(hx,   hy,   hcol); pyxel.pset(hx+1, hy,   hcol)
            pyxel.pset(hx,   hy+1, hcol); pyxel.pset(hx-1, hy-1, hcol)
            pyxel.pset(hx+2, hy-1, hcol)
        pyxel.text(168, 175, "YOU ARE THE", col)
        pyxel.text(164, 188, "W I N N E R !", col)
        stars = "*" * ((fc // 8) % 4 + 1)
        pyxel.text(220, 200, stars, 10)
        pyxel.text(118, 216, "Die Familie ist wieder vereint!", 7)
        pyxel.text(126, 230, "Die Enten leben gluecklich", 11)
        pyxel.text(138, 242, "und sind fuereinander da.", 11)
        pyxel.text(160, 260, f"Endpunkte: {self.score}", 10)
        pyxel.text(152, 274, f"Zeit: {int(TIME_LIMIT - self.time_left)}s", 7)
        pyxel.text(168, 292, "R = Neu starten", 13)
 
 
def setup_sounds():
    pyxel.sound(0).set("c2e2g2c3",       "s", "6677",    "n", 12)
    pyxel.sound(1).set("c3e3",           "s", "76",      "n", 5)
    pyxel.sound(2).set("g1e1c1",         "n", "754",     "n", 8)
    pyxel.sound(3).set("c2e2g2c3e3g3c4", "s", "5566677", "n", 10)
    pyxel.sound(4).set("c3e3g3",         "s", "777",     "n", 6)
 
 
pyxel.init(SCREEN_W, SCREEN_H, title="Entchen-Abenteuer", fps=FPS)
setup_sounds()
game = Game()
pyxel.run(game.update, game.draw)