import pyxel
import random
import math
 
W = 180
H = 200
PW = 16
PH = 16
frame = 1
 
class Game:
 
    def __init__(self):
        pyxel.init(W, H, title="Parcours Jump - Osteredition")
        pyxel.load("res.pyxres")
 
        pyxel.sound(20).set(
            "e4 g4 a4 g4 e4 d4 c4 d4 "
            "e4 g4 a4 b4 a4 g4 e4 d4",
            "p", "6", "n", 30)
        pyxel.sound(21).set(
            "c4 e4 g4 e4 c4 e4 g4 e4 "
            "d4 f4 a4 f4 d4 f4 a4 f4",
            "p", "5", "n", 30)
        pyxel.sound(22).set(
            "c2 c2 g2 g2 a2 a2 g2 g2 "
            "f2 f2 c2 c2 g2 g2 c2 c2",
            "t", "4", "n", 30)
        pyxel.sound(23).set(
            "c2 r c2 r c2 r c2 r "
            "c2 r c2 r c2 r c2 r",
            "n", "7", "n", 15)
        pyxel.music(0).set([20], [21], [22], [23])
 
        random.seed(42)
        self.bg_eggs = []
        for i in range(30):
            ex = random.randint(5, W - 12)
            ey = random.randint(-8000, H)
            color = random.choice([8, 9, 10, 11, 14])
            stripe = random.choice([7, 6, 5])
            self.bg_eggs.append({"x": ex, "y": ey, "c": color, "s": stripe})
 
        self.bg_grass = []
        for i in range(8000):
            gx = random.randint(0, W - 1)
            gy = random.randint(-8000, H)
            gh = random.randint(3, 9)
            self.bg_grass.append({"x": gx, "y": gy, "h": gh})
 
        random.seed()
 
        self.highscore = 0
        self.start()
        pyxel.run(self.update, self.draw)
 
    def start(self):
        self.px = W // 2
        self.py = H - 28
        self.vy = 0
        self.cam = 0
        self.max_height = 0
        self.score_penalty = 0
        self.playing = False
        self.dead = False
        self.plats = []
        self.spikes = []
        self.carrots = []
        self.count = 0
        self.current_plat = None
        self.boost_jumps = 0
        self.boost_gold = False
        self.boost_gold_held = False
        self.immunity_active = False
        self.immunity_timer = 0
        self.penalty_timer = 0
        self.egg_cooldown = 0
 
        self.basket_active = False
        self.basket_x = W // 2 - 40
        # basket_y wird erst gesetzt wenn Score 15000 erreicht
        self.basket_y = None
        self.basket_landed = False
        self.win_timer = 0
        # Merken welche Kameraposition beim Aktivieren herrschte
        self.basket_cam_at_spawn = None
 
        y = H - 20
        x = W // 2
        for i in range(10):
            x = max(10, min(W - 45, x + random.randint(-40, 40)))
            self.add_plat(x, y, random.randint(30, 45))
            y -= random.randint(26, 35)
 
        self.px = self.plats[0]["x"] + self.plats[0]["w"] // 2
        self.py = self.plats[0]["y"] - PH
 
    @property
    def score(self):
        return max(0, self.max_height - self.score_penalty)
 
    def add_plat(self, x, y, w):
        diff = self.count // 20
        if diff == 0:
            t = "s"
        elif diff == 1:
            t = "h" if random.random() < 0.4 else "s"
        elif diff == 2:
            r = random.random()
            t = "h" if r < 0.35 else ("v" if r < 0.55 else "s")
        elif diff == 3:
            r = random.random()
            t = "h" if r < 0.30 else ("v" if r < 0.50 else "s")
        else:
            r = random.random()
            t = "h" if r < 0.25 else ("v" if r < 0.45 else "s")
 
        self.plats.append({
            "x": x, "y": y, "w": w, "t": t,
            "d": random.choice([-1, 1]),
            "r": random.randint(15, 30), "by": y
        })
 
        margin = int(PW * 1.5)
        if random.random() < 0.6 and w > margin * 2 + 8:
            sx = random.randint(x + margin, x + w - margin - 8)
            self.spikes.append({"x": sx, "y": y - 6})
 
        cx = x + w // 2 - 4
        gold_allowed = self.score < 11000
        if self.count > 0 and self.count % 20 == 0 and random.random() < 0.10 and gold_allowed:
            self.carrots.append({"x": cx, "y": y - 8, "plat": self.plats[-1], "gold": True})
        elif self.count > 0 and self.count % 16 == 0 and random.random() < 0.40:
            self.carrots.append({"x": cx, "y": y - 8, "plat": self.plats[-1], "gold": False})
 
        self.count += 1
 
    def spawn(self):
        while len(self.plats) < 25:
            last = min(self.plats, key=lambda p: p["y"])
            ny = last["y"] - random.randint(26, 36)
            w = random.randint(22, 30)
            x = max(10, min(W - w - 10, last["x"] + random.randint(-45, 45)))
            self.add_plat(x, ny, w)
 
    def update(self):
        if not self.playing and not self.dead:
            if pyxel.btnp(pyxel.KEY_SPACE):
                self.playing = True
                pyxel.playm(0, loop=True)
            return
 
        if self.dead or self.basket_landed:
            if pyxel.btnp(pyxel.KEY_R):
                self.start()
            if self.basket_landed:
                self.win_timer += 1
            return
 
        if pyxel.btn(pyxel.KEY_LEFT):
            self.px -= 2.5
        if pyxel.btn(pyxel.KEY_RIGHT):
            self.px += 2.5
        self.px = max(0, min(W - PW, self.px))
 
        if pyxel.btnp(pyxel.KEY_UP) and self.current_plat is not None:
            self.vy = -8
            self.current_plat = None
 
        if pyxel.btnp(pyxel.KEY_SPACE) and self.current_plat is not None and self.boost_jumps > 0:
            if self.boost_gold_held:
                self.vy = -8 * (5 / 3) * 5
                self.boost_gold_held = False
                self.immunity_active = True
                self.immunity_timer = 600
            elif self.boost_gold:
                self.vy = -8 * (5 / 3) * 5
            else:
                self.vy = -8 * (5 / 3)
            self.boost_jumps -= 1
            if self.boost_jumps == 0:
                self.boost_gold = False
            self.current_plat = None
 
        if self.immunity_active:
            self.immunity_timer -= 1
            if self.immunity_timer <= 0:
                self.immunity_active = False
                self.immunity_timer = 0
                self.boost_gold = False
 
        self.vy += 0.8
        self.py += self.vy
 
        diff = self.count // 20
        spd = min(1 + diff * 0.3, 3.5)
 
        for p in self.plats:
            if p["t"] == "h":
                p["x"] += p["d"] * spd
                if p["x"] < 5 or p["x"] + p["w"] > W - 5:
                    p["d"] *= -1
            elif p["t"] == "v":
                p["y"] += p["d"] * spd
                if p["y"] > p["by"] + p["r"]:
                    p["d"] = -1
                if p["y"] < p["by"] - p["r"]:
                    p["d"] = 1
 
        if self.current_plat is not None:
            p = self.current_plat
            if self.px + PW > p["x"] and self.px < p["x"] + p["w"]:
                self.py = p["y"] - PH
                self.vy = 0
                if p["t"] == "h":
                    self.px += p["d"] * spd
                if p["t"] == "v":
                    self.py = p["y"] - PH
            else:
                self.current_plat = None
        else:
            if self.vy >= 0:
                for p in self.plats:
                    if (self.px + PW > p["x"] and self.px < p["x"] + p["w"]
                            and self.py + PH >= p["y"]
                            and self.py + PH <= p["y"] + 8 + self.vy + 1):
                        self.py = p["y"] - PH
                        self.vy = 0
                        self.current_plat = p
                        break
 
        for s in self.spikes:
            if (self.px < s["x"] + 8 and self.px + PW > s["x"]
                    and self.py < s["y"] + 8 and self.py + PH > s["y"]):
                self.die()
                return
 
        for c in self.carrots[:]:
            p = c["plat"]
            c["x"] = p["x"] + p["w"] // 2 - 4
            c["y"] = p["y"] - 8
            if (self.px < c["x"] + 8 and self.px + PW > c["x"]
                    and self.py < c["y"] + 8 and self.py + PH > c["y"]):
                if c["gold"]:
                    self.boost_jumps = 1
                    self.boost_gold = True
                    self.boost_gold_held = True
                    self.immunity_active = False
                    self.immunity_timer = 0
                else:
                    self.boost_jumps = 3
                    self.boost_gold = False
                    self.boost_gold_held = False
                self.carrots.remove(c)
 
        # Kamera nachführen
        if self.py < H / 2 + self.cam:
            self.cam = self.py - H / 2
 
        # Höchsthöhe aktualisieren
        h = int(-self.cam)
        if h > self.max_height:
            self.max_height = h
 
        # Ei-Kollision
        if self.immunity_active:
            pass
        elif self.egg_cooldown > 0:
            self.egg_cooldown -= 1
        else:
            for e in self.bg_eggs:
                ex = e["x"]
                ey = e["y"]
                if (self.px < ex + 8 and self.px + PW > ex + 1
                        and self.py < ey + 9 and self.py + PH > ey):
                    self.score_penalty = min(
                        self.score_penalty + 1000,
                        self.max_height
                    )
                    self.penalty_timer = 90
                    self.egg_cooldown = 60
                    break
 
        if self.penalty_timer > 0:
            self.penalty_timer -= 1
 
        # ── Korb aktivieren wenn Score 15000 erreicht ──
        # Korb erscheint ca. 1.5 Bildschirmhöhen über dem Spieler – erreichbar!
        if self.score >= 15000 and not self.basket_active:
            self.basket_active = True
            self.basket_cam_at_spawn = int(self.cam)
            # Korb 1.5 * H über der aktuellen Spielerposition, mittig auf einer Plattform
            self.basket_y = int(self.py) - int(H * 1.5)
 
        # ── Korb-Kollision: Spieler landet auf Korb-Oberrand ──
        if self.basket_active and not self.basket_landed and self.basket_y is not None:
            bx = self.basket_x
            by = self.basket_y
            basket_w = 80
            basket_top = by + 10
            if (self.vy >= 0
                    and self.px + PW > bx
                    and self.px < bx + basket_w
                    and self.py + PH >= basket_top
                    and self.py + PH <= basket_top + 8 + self.vy + 1):
                self.py = basket_top - PH
                self.vy = 0
                self.basket_landed = True
                pyxel.stop()
                if self.score > self.highscore:
                    self.highscore = self.score
                return
 
        # Plattformen weiter spawnen, solange der Korb noch nicht erreicht wurde
        # (damit der Spieler Sprungbasis bis zum Korb hat)
        if not self.basket_landed:
            self.spawn()
 
        # ── Kamera-Deckel: nur wenn Korb aktiv, nicht zu weit drüber ──
        if self.basket_active and self.basket_y is not None:
            ceiling = self.basket_y - H + PH
            if self.py < ceiling:
                self.py = ceiling
                if self.vy < 0:
                    self.vy = 0
 
        if self.current_plat is not None and self.current_plat not in self.plats:
            self.current_plat = None
        self.plats   = [p for p in self.plats   if p["y"] < self.cam + H + 120]
        self.carrots = [c for c in self.carrots  if c["y"] < self.cam + H + 120]
        self.spikes  = [s for s in self.spikes   if s["y"] < self.cam + H + 120]
 
        if self.py > self.cam + H + 20:
            self.die()
 
    def die(self):
        if self.score > self.highscore:
            self.highscore = self.score
        self.dead = True
 
    def draw_egg(self, x, y, color, stripe_color):
        pyxel.rect(x + 2, y,     4, 2, color)
        pyxel.rect(x + 1, y + 2, 6, 5, color)
        pyxel.rect(x + 2, y + 7, 4, 2, color)
        pyxel.rect(x + 1, y + 4, 6, 1, stripe_color)
 
    def draw_grass_blade(self, x, y, h):
        pyxel.line(x, y, x, y - h, 11)
        pyxel.pset(x - 1, y - h + 1, 3)
        pyxel.pset(x + 1, y - h,     3)
        if h > 4:
            pyxel.pset(x, y - h // 2, 3)
 
    def draw_basket_big(self, x, y):
        bw = 80
        pyxel.line(x + 15, y + 10, x + 15, y + 2, 4)
        pyxel.line(x + 65, y + 10, x + 65, y + 2, 4)
        pyxel.line(x + 15, y + 2,  x + 65, y + 2, 4)
        pyxel.rect(x,      y + 10, bw,     24, 4)
        pyxel.rect(x + 2,  y + 11, bw - 4, 22, 9)
        pyxel.rect(x,      y + 10, 5,  24, 4)
        pyxel.rect(x + 75, y + 10, 5,  24, 4)
        for i in range(8):
            pyxel.line(x + i * 9,     y + 10, x + i * 9 + 5, y + 34, 5)
            pyxel.line(x + i * 9 + 5, y + 10, x + i * 9,     y + 34, 5)
        eier = [
            (x + 4,  y + 22, 8,  7),
            (x + 14, y + 19, 10, 7),
            (x + 24, y + 22, 11, 7),
            (x + 34, y + 19, 9,  7),
            (x + 44, y + 22, 8,  7),
            (x + 54, y + 19, 10, 7),
            (x + 64, y + 22, 14, 7),
        ]
        for ex, ey, ec, es in eier:
            self.draw_egg(ex, ey, ec, es)
 
    def draw_basket(self, x, y):
        pyxel.rect(x,      y + 10, 28, 8, 4)
        pyxel.rect(x + 1,  y + 11, 26, 6, 9)
        pyxel.rect(x,      y + 10, 3,  8, 4)
        pyxel.rect(x + 25, y + 10, 3,  8, 4)
        for i in range(4):
            pyxel.line(x + i * 6,     y + 10, x + i * 6 + 3, y + 18, 5)
            pyxel.line(x + i * 6 + 3, y + 10, x + i * 6,     y + 18, 5)
        pyxel.line(x + 5,  y + 10, x + 5,  y + 4, 4)
        pyxel.line(x + 22, y + 10, x + 22, y + 4, 4)
        pyxel.line(x + 5,  y + 4,  x + 22, y + 4, 4)
        eier = [
            (x + 3,  y + 8, 8,  7),
            (x + 9,  y + 6, 10, 7),
            (x + 15, y + 8, 11, 7),
            (x + 20, y + 6, 9,  7)
        ]
        for ex, ey, ec, es in eier:
            self.draw_egg(ex, ey, ec, es)
 
    def draw_background(self, cam):
        for row in range(H):
            world_y = row + cam
            if world_y < -5000:
                c = 3
            elif world_y < -2000:
                c = 11
            elif world_y < 0:
                c = 3
            else:
                c = 11
            pyxel.line(0, row, W, row, c)
 
        for g in self.bg_grass:
            sy = g["y"] - cam
            if -10 < sy < H + 5:
                self.draw_grass_blade(g["x"], int(sy), g["h"])
 
        for e in self.bg_eggs:
            sy = e["y"] - cam
            if -15 < sy < H + 5:
                if self.immunity_active and (pyxel.frame_count // 4) % 2 == 0:
                    pass
                else:
                    self.draw_egg(e["x"], int(sy), e["c"], e["s"])
 
    def draw(self):
        global frame
        pyxel.cls(0)
 
        # ── Startbildschirm ──────────────────────────────────────────────
        if not self.playing and not self.dead:
            for row in range(H):
                pyxel.line(0, row, W, row, 3 if row < H // 2 else 11)
            for g in self.bg_grass[:80]:
                sy = int(g["y"] % H)
                self.draw_grass_blade(g["x"], sy, g["h"])
            for e in self.bg_eggs[:12]:
                sy = int(e["y"] % H)
                if sy > 10:
                    self.draw_egg(e["x"], sy, e["c"], e["s"])
 
            pyxel.rect(14, 8, 152, 22, 1)
            pyxel.rectb(14, 8, 152, 22, 9)
            pyxel.text(28, 13, "P A R C O U R S  J U M P", 10)
 
            pyxel.rect(14, 95, 152, 82, 1)
            pyxel.rectb(14, 95, 152, 82, 7)
            pyxel.text(20, 100, "-- STEUERUNG --", 7)
            pyxel.text(20, 112, "<  >   Spieler bewegen", 6)
            pyxel.text(20, 122, "OBEN   Springen", 6)
            pyxel.line(20, 133, 160, 133, 5)
            self.draw_egg(20, 137, 8, 7)
            pyxel.text(31, 138, "Eier beruehren: -1000!", 8)
            pyxel.text(31, 147, "(min. 0 Punkte)", 13)
 
            pyxel.rect(14, 38, 152, 55, 1)
            pyxel.rectb(14, 38, 152, 55, 9)
            pyxel.text(20, 43, "-- KAROTTEN --", 9)
            pyxel.tri(20, 57, 24, 49, 28, 57, 9)
            pyxel.pset(24, 48, 11)
            pyxel.text(32, 50, "Karotten sammeln!", 7)
            pyxel.text(32, 60, "SPACE = Boost-Sprung", 10)
            pyxel.tri(20, 76, 24, 68, 28, 76, 10)
            pyxel.pset(24, 67, 7)
            pyxel.text(32, 70, "Gold = Boost-Sprung", 10)
            pyxel.text(32, 79, "danach 10s IMMUN!", 10)
 
            pyxel.rect(14, 179, 152, 12, 1)
            pyxel.rectb(14, 179, 152, 12, 7)
            pyxel.rect(20, 183, 14, 4, 4)
            pyxel.text(37, 182, "Statisch", 4)
            pyxel.rect(75, 183, 14, 4, 9)
            pyxel.text(92, 182, "Horiz.", 9)
            pyxel.rect(123, 183, 14, 4, 5)
            pyxel.text(140, 182, "Vert.", 5)
 
            if (pyxel.frame_count // 20) % 2 == 0:
                pyxel.rect(34, 193, 112, 8, 8)
                pyxel.text(42, 195, "SPACE = STARTEN!", 0)
            return
 
        # ── Spielwelt ────────────────────────────────────────────────────
        self.draw_background(int(self.cam))
        pyxel.camera(0, self.cam)
 
        for p in self.plats:
            if p["t"] == "s":
                body_c, top_c = 4, 5
            elif p["t"] == "h":
                body_c, top_c = 9, 10
            else:
                body_c, top_c = 5, 4
            pyxel.rect(p["x"], p["y"], p["w"], 4, body_c)
            pyxel.line(p["x"], p["y"], p["x"] + p["w"] - 1, p["y"], top_c)
 
        for s in self.spikes:
            pyxel.tri(s["x"], s["y"] + 8, s["x"] + 4, s["y"], s["x"] + 8, s["y"] + 8, 8)
 
        for c in self.carrots:
            if c["gold"]:
                pyxel.tri(c["x"], c["y"] + 8, c["x"] + 4, c["y"], c["x"] + 8, c["y"] + 8, 10)
                pyxel.pset(c["x"] + 4, c["y"] - 1, 7)
                pyxel.pset(c["x"] + 3, c["y"] - 2, 7)
                pyxel.pset(c["x"] + 5, c["y"] - 2, 7)
            else:
                pyxel.tri(c["x"], c["y"] + 8, c["x"] + 4, c["y"], c["x"] + 8, c["y"] + 8, 9)
                pyxel.pset(c["x"] + 4, c["y"] - 1, 11)
 
        if self.basket_active and self.basket_y is not None:
            self.draw_basket_big(self.basket_x, self.basket_y)
            t = pyxel.frame_count
            glitzer_farben = [7, 10, 9, 8]
            for i in range(8):
                angle = math.radians((t * 3 + i * 45) % 360)
                gx = int(self.basket_x + 40 + math.cos(angle) * 48)
                gy = int(self.basket_y + 20 + math.sin(angle) * 22)
                pyxel.pset(gx, gy, glitzer_farben[i % 4])
 
            # Pfeil zum Korb wenn er noch nicht sichtbar ist
            basket_screen_y = self.basket_y - self.cam
            if basket_screen_y < 5:
                # Korb ist noch über dem Bildschirm: Pfeil nach oben zeigen
                arrow_x = self.basket_x + 40 - 3
                if (pyxel.frame_count // 10) % 2 == 0:
                    pyxel.tri(arrow_x, 8, arrow_x + 6, 8, arrow_x + 3, 3, 10)
                    pyxel.text(arrow_x - 8, 12, "KORB", 10)
 
        frame = 1 if self.vy != 0 else 0
        pyxel.blt(self.px, self.py, 0, frame * 16, 16, PW, PH, 0)
 
        if self.immunity_active and (pyxel.frame_count // 6) % 2 == 0:
            pyxel.rectb(int(self.px) - 2, int(self.py) - 2, PW + 4, PH + 4, 10)
        elif self.boost_gold_held and (pyxel.frame_count // 8) % 2 == 0:
            pyxel.rectb(int(self.px) - 2, int(self.py) - 2, PW + 4, PH + 4, 9)
 
        pyxel.camera()
 
        # ── HUD ──────────────────────────────────────────────────────────
        pyxel.text(5, 5, f"Punkte:{self.score}  HI:{self.highscore}", 7)
 
        if self.penalty_timer > 0:
            pyxel.text(5, 23, "-1000 !", 8)
 
        if self.boost_jumps > 0:
            if self.boost_gold_held:
                pyxel.text(5, 14, f"GOLD x{self.boost_jumps} SPACE=Boost+IMMUN!", 10)
            elif self.immunity_active:
                sek = self.immunity_timer // 60
                pyxel.text(5, 14, f"GOLD-BOOST x{self.boost_jumps} [IMMUN {sek}s]", 10)
            else:
                pyxel.text(5, 14, f"BOOST x{self.boost_jumps} = SPACE", 9)
        elif self.immunity_active:
            sek = self.immunity_timer // 60
            pyxel.text(5, 14, f"IMMUN noch {sek}s", 10)
 
        if 14000 <= self.score < 15000:
            if (pyxel.frame_count // 15) % 2 == 0:
                pyxel.text(5, 185, "Osterkorb kommt bald!", 10)
        elif self.basket_active and not self.basket_landed:
            if (pyxel.frame_count // 20) % 2 == 0:
                pyxel.text(5, 185, "Springe auf den Korb!", 8)
 
        # ── Gewonnen! ────────────────────────────────────────────────────
        if self.basket_landed:
            pyxel.rect(10, 50, 160, 100, 1)
            pyxel.rectb(10, 50, 160, 100, 10)
            pyxel.rectb(11, 51, 158, 98, 9)
            pyxel.text(30, 57, "** GLUECKWUNSCH **", 10)
            pyxel.text(20, 70,  "Herzlichen Glueckwunsch!", 9)
            pyxel.text(20, 82,  "Du hast den", 7)
            pyxel.text(20, 91,  "Osterhasentest", 9)
            pyxel.text(20, 100, "bestanden!", 9)
            self.draw_egg(132, 62, 8,  7)
            self.draw_egg(145, 70, 10, 7)
            self.draw_egg(132, 82, 11, 7)
            self.draw_egg(145, 90, 9,  7)
            pyxel.text(5, 5, f"Punkte:{self.score}  HI:{self.highscore}", 7)
            pyxel.text(35, 132, "R = nochmal spielen", 6)
            return
 
        # ── Game Over ────────────────────────────────────────────────────
        if self.dead:
            pyxel.rect(30, 70, 120, 55, 1)
            pyxel.text(65, 80, "GAME OVER", 8)
            pyxel.text(55, 95, f"SCORE: {self.score}", 7)
            pyxel.text(45, 105, f"BEST:  {self.highscore}", 10)
            pyxel.text(40, 118, "R = restart", 7)
 
Game()