# Pyxel Studio
import math
import random

import pyxel


WIDTH = 512
HEIGHT = 512
FPS = 60


class C:
    NIGHT = 0
    WHITE = 1
    STONE_LIGHT = 2
    STONE_DARK = 3
    GRASS = 4
    GRASS_LIGHT = 5
    BROWN = 6
    DARK_BROWN = 7
    GOLD = 8
    RED = 9
    ORANGE = 10
    ICE = 11
    BLUE = 12
    POISON = 13
    PURPLE = 14
    SHADOW = 15


PALETTE = {
    C.NIGHT: 0x0A0A1A,
    C.WHITE: 0xFFFFFF,
    C.STONE_LIGHT: 0xD4C5A9,
    C.STONE_DARK: 0x8B7A6A,
    C.GRASS: 0x3A5A3A,
    C.GRASS_LIGHT: 0x4A6A4A,
    C.BROWN: 0x6A4A2A,
    C.DARK_BROWN: 0x4A2A1A,
    C.GOLD: 0xFFD700,
    C.RED: 0xE94560,
    C.ORANGE: 0xFF6B35,
    C.ICE: 0x8AD4E9,
    C.BLUE: 0x4AC9E9,
    C.POISON: 0x4AC94A,
    C.PURPLE: 0x533483,
    C.SHADOW: 0x2A3A4A,
}


PATH = [
    (-24, 407), (52, 407), (52, 356), (126, 356), (126, 405),
    (205, 405), (205, 340), (286, 340), (286, 286), (96, 286),
    (96, 230), (178, 230), (178, 174), (258, 174), (258, 232),
    (342, 232), (342, 150), (416, 150), (416, 104), (488, 104),
]


MAPS = [
    {"name": "Gruene Schleife", "style": "meadow", "path": PATH},
    {"name": "Mondgarten", "style": "moon", "path": [
        (-24, 392), (70, 392), (70, 318), (144, 318), (144, 380),
        (224, 380), (224, 302), (302, 302), (302, 246), (146, 246),
        (146, 188), (230, 188), (230, 130), (316, 130), (316, 188),
        (404, 188), (404, 104), (488, 104),
    ]},
    {"name": "Kristallbach", "style": "crystal", "path": [
        (-24, 412), (92, 412), (92, 362), (44, 362), (44, 292),
        (148, 292), (148, 354), (240, 354), (240, 274), (332, 274),
        (332, 214), (238, 214), (238, 154), (352, 154), (352, 104), (488, 104),
    ]},
    {"name": "Aschepfad", "style": "ember", "path": [
        (-24, 406), (44, 406), (44, 340), (116, 340), (116, 274),
        (58, 274), (58, 210), (156, 210), (156, 150), (242, 150),
        (242, 226), (330, 226), (330, 300), (410, 300), (410, 192),
        (452, 192), (452, 104), (488, 104),
    ]},
    {"name": "Nebelpass", "style": "mist", "path": [
        (-24, 384), (58, 384), (58, 420), (152, 420), (152, 344),
        (238, 344), (238, 398), (322, 398), (322, 322), (406, 322),
        (406, 250), (308, 250), (308, 178), (396, 178), (396, 104), (488, 104),
    ]},
    {"name": "Sonnenhof", "style": "sun", "path": [
        (-24, 408), (96, 408), (96, 330), (176, 330), (176, 408),
        (272, 408), (272, 326), (352, 326), (352, 254), (266, 254),
        (266, 182), (180, 182), (180, 112), (300, 112), (300, 164),
        (424, 164), (424, 104), (488, 104),
    ]},
    {"name": "Dornenhain", "style": "thorn", "path": [
        (-24, 412), (62, 412), (62, 356), (118, 356), (118, 302),
        (62, 302), (62, 236), (140, 236), (140, 292), (222, 292),
        (222, 224), (306, 224), (306, 162), (380, 162), (380, 214),
        (440, 214), (440, 104), (488, 104),
    ]},
    {"name": "Runenkreis", "style": "rune", "path": [
        (-24, 390), (60, 390), (60, 312), (124, 312), (124, 242),
        (196, 242), (196, 312), (276, 312), (276, 386), (360, 386),
        (360, 306), (438, 306), (438, 230), (352, 230), (352, 150),
        (432, 150), (432, 104), (488, 104),
    ]},
    {"name": "Koenigsbogen", "style": "royal", "path": [
        (-24, 418), (80, 418), (80, 360), (160, 360), (160, 302),
        (240, 302), (240, 244), (160, 244), (160, 186), (80, 186),
        (80, 128), (196, 128), (196, 186), (312, 186), (312, 128),
        (428, 128), (428, 104), (488, 104),
    ]},
    {"name": "Schattenufer", "style": "shadow", "path": [
        (-24, 398), (104, 398), (104, 354), (52, 354), (52, 286),
        (180, 286), (180, 342), (300, 342), (300, 286), (422, 286),
        (422, 226), (284, 226), (284, 168), (398, 168), (398, 104), (488, 104),
    ]},
    {"name": "Froststufen", "style": "frost", "path": [
        (-24, 414), (54, 414), (54, 370), (132, 370), (132, 326),
        (210, 326), (210, 282), (288, 282), (288, 238), (210, 238),
        (210, 194), (288, 194), (288, 150), (366, 150), (366, 104), (488, 104),
    ]},
    {"name": "Langer Wall", "style": "wall", "path": [
        (-24, 420), (44, 420), (44, 372), (96, 372), (96, 420),
        (154, 420), (154, 372), (212, 372), (212, 420), (270, 420),
        (270, 352), (338, 352), (338, 284), (406, 284), (406, 216),
        (338, 216), (338, 148), (428, 148), (428, 104), (488, 104),
    ]},
]


DIFFICULTIES = {
    "Einfach": {"hp": 1.10, "speed": 1.00, "damage": 0.98, "count": 0.96, "gold": 0.78, "start_gold": 230},
    "Normal": {"hp": 1.34, "speed": 1.09, "damage": 1.20, "count": 1.17, "gold": 0.62, "start_gold": 195},
    "Schwer": {"hp": 1.78, "speed": 1.22, "damage": 1.55, "count": 1.46, "gold": 0.50, "start_gold": 165},
}

BASE_REWARD_MULT = 0.46


WEATHERS = [
    {"name": "Sonnig", "weight": 30, "min": 1, "max": 3, "z_speed": 1.00, "range": 1.00, "weapon_speed": 1.00, "fade": 1.00, "sky": C.GOLD},
    {"name": "Regen", "weight": 18, "min": 1, "max": 2, "z_speed": 0.90, "range": 1.00, "weapon_speed": 1.00, "fade": 1.00, "sky": C.BLUE},
    {"name": "Sturm", "weight": 8, "min": 1, "max": 1, "z_speed": 0.80, "range": 0.80, "weapon_speed": 1.00, "fade": 1.00, "sky": C.SHADOW},
    {"name": "Nacht", "weight": 10, "min": 1, "max": 1, "z_speed": 1.00, "range": 1.00, "weapon_speed": 1.00, "fade": 0.80, "sky": C.NIGHT},
    {"name": "Nebel", "weight": 8, "min": 1, "max": 1, "z_speed": 1.00, "range": 0.70, "weapon_speed": 1.00, "fade": 1.00, "sky": C.STONE_DARK},
    {"name": "Vollmond", "weight": 7, "min": 1, "max": 1, "z_speed": 1.20, "range": 1.00, "weapon_speed": 1.00, "fade": 1.00, "sky": C.ICE},
    {"name": "Schnee", "weight": 12, "min": 1, "max": 2, "z_speed": 0.70, "range": 1.00, "weapon_speed": 0.80, "fade": 1.00, "sky": C.WHITE},
]


DROP_TYPES = [
    {"name": "Goldbeutel", "weight": 28, "color": C.GOLD, "kind": "gold"},
    {"name": "Herzsplitter", "weight": 12, "color": C.RED, "kind": "heal"},
    {"name": "Seelensplitter", "weight": 4, "color": C.PURPLE, "kind": "soul"},
    {"name": "Frostkugel", "weight": 8, "color": C.ICE, "kind": "freeze"},
    {"name": "Feuerflasche", "weight": 8, "color": C.ORANGE, "kind": "burn"},
    {"name": "Tempo-Trank", "weight": 7, "color": C.BLUE, "kind": "speed"},
    {"name": "Kraft-Trank", "weight": 7, "color": C.POISON, "kind": "damage"},
]


SHOP_OFFERS = [
    ("Angriffswelle", "25s +45% DMG", 5, C.POISON),
    ("Schnelle Haende", "25s Tempo", 5, C.BLUE),
    ("Burg reparieren", "+10 Burg-HP", 4, C.RED),
    ("Gold-Finder", "+12% Gold", 14, C.GOLD),
    ("Reichweiten-Elixier", "30s mehr Range", 6, C.ICE),
    ("Frostbombe", "alle Zombies slow", 7, C.BLUE),
    ("Meteor-Flasche", "starker Flaechentreffer", 9, C.ORANGE),
    ("Schutzsiegel", "+6 max Burg-HP", 12, C.WHITE),
]


ACHIEVEMENTS = [
    {"id": "kills", "name": "Zombiejagd", "stat": "kills", "targets": [50, 150, 400, 900, 1800], "gold": [55, 110, 230, 420, 800], "soul": [0, 0, 1, 2, 4]},
    {"id": "waves", "name": "Wellenbrecher", "stat": "waves", "targets": [3, 6, 10, 16, 25], "gold": [45, 90, 180, 330, 650], "soul": [0, 1, 1, 2, 4]},
    {"id": "bosses", "name": "Bossjaeger", "stat": "bosses", "targets": [1, 3, 6, 10, 18], "gold": [80, 170, 340, 620, 1100], "soul": [1, 2, 3, 5, 8]},
    {"id": "build", "name": "Baumeister", "stat": "weapons", "targets": [4, 8, 14, 22, 35], "gold": [50, 100, 210, 390, 720], "soul": [0, 0, 1, 2, 3]},
    {"id": "upgrade", "name": "Schmied", "stat": "upgrades", "targets": [4, 10, 20, 35, 60], "gold": [55, 115, 240, 450, 850], "soul": [0, 0, 1, 2, 4]},
    {"id": "skills", "name": "Magiekunst", "stat": "abilities", "targets": [5, 15, 35, 75, 150], "gold": [60, 120, 260, 500, 900], "soul": [0, 1, 2, 3, 5]},
    {"id": "items", "name": "Sammler", "stat": "items", "targets": [5, 15, 40, 90, 180], "gold": [40, 85, 190, 360, 700], "soul": [0, 0, 1, 2, 4]},
    {"id": "pets", "name": "Tierfreund", "stat": "pets", "targets": [1, 2, 3, 5, 6], "gold": [70, 150, 320, 620, 1000], "soul": [0, 1, 2, 4, 6]},
]


PETS = [
    {"id": 1, "name": "Eule", "role": "Goldsammler", "cost": 18, "color": C.GOLD},
    {"id": 2, "name": "Fuchs", "role": "Tempo", "cost": 24, "color": C.ORANGE},
    {"id": 3, "name": "Wolf", "role": "Schaden", "cost": 28, "color": C.STONE_LIGHT},
    {"id": 4, "name": "Baer", "role": "Burgschutz", "cost": 34, "color": C.BROWN},
    {"id": 5, "name": "Einhorn", "role": "Heilung", "cost": 40, "color": C.ICE},
    {"id": 6, "name": "Drache", "role": "Feuer", "cost": 50, "color": C.RED},
    {"id": 7, "name": "Phoenix", "role": "Revive", "cost": 60, "color": C.ORANGE},
    {"id": 8, "name": "Schildkroete", "role": "Slow", "cost": 32, "color": C.POISON},
    {"id": 9, "name": "Geist", "role": "Schwaecher", "cost": 44, "color": C.WHITE},
    {"id": 10, "name": "Golem", "role": "Range", "cost": 46, "color": C.STONE_DARK},
    {"id": 11, "name": "Fee", "role": "Starkes Tempo", "cost": 58, "color": C.PURPLE},
    {"id": 12, "name": "Rabe", "role": "Bossgold", "cost": 42, "color": C.SHADOW},
    {"id": 13, "name": "Hund", "role": "Nahslow", "cost": 26, "color": C.BROWN},
    {"id": 14, "name": "Katze", "role": "Rabatt", "cost": 38, "color": C.GOLD},
    {"id": 15, "name": "Pinguin", "role": "Eis", "cost": 48, "color": C.ICE},
    {"id": 16, "name": "Schlange", "role": "Gift", "cost": 52, "color": C.POISON},
    {"id": 17, "name": "Falke", "role": "Fernkampf", "cost": 62, "color": C.GOLD},
    {"id": 18, "name": "Wal", "role": "Gigant", "cost": 70, "color": C.BLUE},
    {"id": 19, "name": "Dino", "role": "Urzeit", "cost": 80, "color": C.GRASS_LIGHT},
    {"id": 20, "name": "Legendrache", "role": "Allround", "cost": 120, "color": C.PURPLE},
]

for entry in ACHIEVEMENTS:
    entry["gold"] = [max(25, int(value * 0.72)) for value in entry["gold"]]
    entry["soul"] = [0 if i < 2 else min(value, 1 + i // 4) for i, value in enumerate(entry["soul"])]

for pet in PETS:
    pet["cost"] = int(pet["cost"] * 1.45)


PET_BY_ID = {pet["id"]: pet for pet in PETS}

PET_HINTS = {
    1: "Mehr Gold, spaeter etwas Soul bei Bossen.",
    2: "Alle Waffen schiessen schneller.",
    3: "Alle Waffen machen mehr Schaden.",
    4: "Mehr Burg-HP, spaeter Regeneration.",
    5: "Heilt die Burg nach jeder Welle.",
    6: "Verbrennt nahe Zombies in kleinen Salven.",
    7: "Rettet die Burg einmal vor Defeat.",
    8: "Alle Zombies bewegen sich langsamer.",
    9: "Neue Zombies starten mit weniger HP.",
    10: "Alle Waffen bekommen mehr Reichweite.",
    11: "Starker Tempo-Bonus fuer Waffen.",
    12: "Boss-Zombies geben mehr Belohnung.",
    13: "Verlangsamt Zombies nahe beim Pet.",
    14: "Waffen und Upgrades werden billiger.",
    15: "Friert nahe Gegner und Todesechos ein.",
    16: "Gibt Waffen und Pet-Angriffen Gift.",
    17: "Sehr viel Reichweite, spaeter Schaden.",
    18: "Sehr viele Burg-HP und Regeneration.",
    19: "Neue Zombies haben deutlich weniger HP.",
    20: "Teurer Allround-Bonus fuer alles.",
}


def point_segment_distance(px, py, ax, ay, bx, by):
    vx = bx - ax
    vy = by - ay
    wx = px - ax
    wy = py - ay
    length_sq = vx * vx + vy * vy
    if length_sq == 0:
        return math.hypot(px - ax, py - ay)
    t = max(0.0, min(1.0, (wx * vx + wy * vy) / length_sq))
    cx = ax + vx * t
    cy = ay + vy * t
    return math.hypot(px - cx, py - cy)


def distance_to_path(px, py):
    best = 9999
    for i in range(len(PATH) - 1):
        ax, ay = PATH[i]
        bx, by = PATH[i + 1]
        best = min(best, point_segment_distance(px, py, ax, ay, bx, by))
    return best


def generate_grid_slots():
    slots = []

    def add_slot(px, py):
        px = int(round(px))
        py = int(round(py))
        if not (18 <= px <= 488 and 96 <= py <= 418):
            return
        if px > 404 and py < 138:
            return
        d = distance_to_path(px, py)
        if d < 25 or d > 86:
            return
        for sx, sy in slots:
            if math.hypot(px - sx, py - sy) < 20:
                return
        slots.append((px, py))

    # First place a tactical ring of valid cells along every path segment.
    # These cells make melee towers useful even near tight corners.
    for i in range(len(PATH) - 1):
        ax, ay = PATH[i]
        bx, by = PATH[i + 1]
        dx = bx - ax
        dy = by - ay
        length = max(1, math.hypot(dx, dy))
        nx = -dy / length
        ny = dx / length
        steps = max(1, int(length // 22))
        for step in range(steps + 1):
            t = step / steps
            px = ax + dx * t
            py = ay + dy * t
            for side in (-1, 1):
                for offset in (28, 46, 64):
                    add_slot(px + nx * offset * side, py + ny * offset * side)

    # Corners need extra diagonal cells, otherwise short-range weapons feel bad.
    for cx, cy in PATH[1:-1]:
        for ox, oy in [(-34, -34), (34, -34), (-34, 34), (34, 34), (-50, 0), (50, 0), (0, -50), (0, 50)]:
            add_slot(cx + ox, cy + oy)

    # Fill remaining open areas with a quiet invisible grid.
    for y in range(102, 414, 24):
        for x in range(22, 490, 24):
            add_slot(x, y)
    return slots


SLOTS = generate_grid_slots()


WEAPONS = [
    {"id": 1, "name": "Holzschwert", "cost": 20, "damage": 5, "range": 1.0, "speed": 1.5,
     "special": "Doppelschlag", "upgrade": [10, 20, 40, 80], "kind": "blade"},
    {"id": 2, "name": "Steinspeer", "cost": 35, "damage": 8, "range": 1.5, "speed": 1.2,
     "special": "Durchdringt 2", "upgrade": [15, 30, 60, 120], "kind": "spear"},
    {"id": 3, "name": "Kurzbogen", "cost": 50, "damage": 10, "range": 3.0, "speed": 1.0,
     "special": "3 Pfeile", "upgrade": [20, 40, 80, 160], "kind": "arrow"},
    {"id": 4, "name": "Axt", "cost": 65, "damage": 15, "range": 1.0, "speed": 1.0,
     "special": "Kritisch 40%", "upgrade": [25, 50, 100, 200], "kind": "axe"},
    {"id": 5, "name": "Langbogen", "cost": 80, "damage": 18, "range": 4.0, "speed": 0.9,
     "special": "Doppelschuss", "upgrade": [30, 60, 120, 240], "kind": "arrow"},
    {"id": 6, "name": "Eispfeil", "cost": 100, "damage": 12, "range": 4.5, "speed": 0.8,
     "special": "Einfrieren", "upgrade": [40, 80, 160, 320], "kind": "ice"},
    {"id": 7, "name": "Feuerpfeil", "cost": 120, "damage": 22, "range": 4.0, "speed": 0.8,
     "special": "Verbrennen", "upgrade": [45, 90, 180, 360], "kind": "fire"},
    {"id": 8, "name": "Katapult", "cost": 150, "damage": 35, "range": 2.5, "speed": 2.5,
     "special": "Radius 2.5", "upgrade": [60, 120, 240, 480], "kind": "stone"},
    {"id": 9, "name": "Blitzstab", "cost": 180, "damage": 28, "range": 3.5, "speed": 1.8,
     "special": "Kettenblitz", "upgrade": [70, 140, 280, 560], "kind": "lightning"},
    {"id": 10, "name": "Giftturm", "cost": 200, "damage": 15, "range": 3.0, "speed": 1.0,
     "special": "Gift 8s", "upgrade": [80, 160, 320, 640], "kind": "poison"},
    {"id": 11, "name": "Kanone", "cost": 250, "damage": 55, "range": 5.0, "speed": 4.0,
     "special": "Explosion", "upgrade": [100, 200, 400, 800], "kind": "cannon"},
    {"id": 12, "name": "Flammenwerfer", "cost": 300, "damage": 30, "range": 2.0, "speed": 0.5,
     "special": "Lange Flamme", "upgrade": [120, 240, 480, 960], "kind": "flame"},
    {"id": 13, "name": "Kristallkugel", "cost": 350, "damage": 40, "range": 4.0, "speed": 2.0,
     "special": "3. Schuss Boom", "upgrade": [140, 280, 560, 1120], "kind": "magic"},
    {"id": 14, "name": "Eiskanone", "cost": 400, "damage": 25, "range": 5.0, "speed": 3.0,
     "special": "2 einfrieren", "upgrade": [160, 320, 640, 1280], "kind": "icecannon"},
    {"id": 15, "name": "Tesla-Turm", "cost": 450, "damage": 35, "range": 4.5, "speed": 1.5,
     "special": "8 Ziele", "upgrade": [180, 360, 720, 1440], "kind": "tesla"},
    {"id": 16, "name": "Nekro-Stab", "cost": 500, "damage": 20, "range": 3.0, "speed": 0.8,
     "special": "Seelenraub", "upgrade": [200, 400, 800, 1600], "kind": "necro"},
    {"id": 17, "name": "Drachenspeer", "cost": 600, "damage": 60, "range": 2.5, "speed": 1.5,
     "special": "Feuerflaeche", "upgrade": [240, 480, 960, 1920], "kind": "dragon"},
    {"id": 18, "name": "Orkanbogen", "cost": 750, "damage": 50, "range": 6.0, "speed": 1.0,
     "special": "4 Pfeile", "upgrade": [300, 600, 1200, 2400], "kind": "storm"},
    {"id": 19, "name": "Schattenklinge", "cost": 900, "damage": 80, "range": 1.5, "speed": 0.8,
     "special": "Unsichtbar", "upgrade": [360, 720, 1440, 2880], "kind": "shadow"},
    {"id": 20, "name": "Goettlicher Hammer", "cost": 1200, "damage": 120, "range": 3.0, "speed": 1.5,
     "special": "5x5 Flaeche", "upgrade": [500, 1000, 2000, 4000], "kind": "divine"},
    {"id": 21, "name": "Runendolch", "cost": 180, "damage": 24, "range": 1.4, "speed": 0.55,
     "special": "Blutmarke", "upgrade": [90, 180, 360, 720], "kind": "rune"},
    {"id": 22, "name": "Sonnenlinse", "cost": 340, "damage": 18, "range": 5.2, "speed": 0.35,
     "special": "Dauerstrahl", "upgrade": [150, 300, 600, 1200], "kind": "beam"},
    {"id": 23, "name": "Dornenwurzel", "cost": 460, "damage": 22, "range": 3.2, "speed": 1.3,
     "special": "Wurzeln", "upgrade": [190, 380, 760, 1520], "kind": "root"},
    {"id": 24, "name": "Sternenwerfer", "cost": 640, "damage": 42, "range": 4.6, "speed": 1.15,
     "special": "Suchstern", "upgrade": [260, 520, 1040, 2080], "kind": "star"},
    {"id": 25, "name": "Obsidianobelisk", "cost": 900, "damage": 55, "range": 4.0, "speed": 2.2,
     "special": "Schwarzer Riss", "upgrade": [360, 720, 1440, 2880], "kind": "obelisk"},
]


LEGENDARIES = [
    {"id": 101, "name": "Holzklinge", "cost": 0, "damage": 40, "range": 2.5, "speed": 0.85,
     "special": "Dreifach-Klinge", "upgrade": [200, 400, 800, 1600], "kind": "legend_blade"},
    {"id": 102, "name": "Steinaxt", "cost": 0, "damage": 70, "range": 2.0, "speed": 1.0,
     "special": "Steinschock", "upgrade": [240, 480, 960, 1920], "kind": "legend_axe"},
    {"id": 103, "name": "Feuerbogen", "cost": 0, "damage": 85, "range": 5.5, "speed": 0.75,
     "special": "Flammenregen", "upgrade": [300, 600, 1200, 2400], "kind": "legend_firebow"},
    {"id": 104, "name": "Blitzeis", "cost": 0, "damage": 75, "range": 5.0, "speed": 1.1,
     "special": "Frostblitz", "upgrade": [340, 680, 1360, 2720], "kind": "legend_icebolt"},
    {"id": 105, "name": "Feuerkanone", "cost": 0, "damage": 120, "range": 5.5, "speed": 2.4,
     "special": "Lava-Explosion", "upgrade": [420, 840, 1680, 3360], "kind": "legend_cannon"},
    {"id": 106, "name": "Giftkugel", "cost": 0, "damage": 80, "range": 4.5, "speed": 1.4,
     "special": "Toxische Nova", "upgrade": [460, 920, 1840, 3680], "kind": "legend_poison"},
    {"id": 107, "name": "Feuerorkan", "cost": 0, "damage": 95, "range": 6.0, "speed": 0.65,
     "special": "Brennender Wind", "upgrade": [520, 1040, 2080, 4160], "kind": "legend_storm"},
    {"id": 108, "name": "Eisschatten", "cost": 0, "damage": 105, "range": 4.0, "speed": 0.7,
     "special": "Schwarzer Frost", "upgrade": [560, 1120, 2240, 4480], "kind": "legend_frost"},
    {"id": 109, "name": "Schattenstab", "cost": 0, "damage": 90, "range": 4.0, "speed": 0.55,
     "special": "Seelenriss", "upgrade": [600, 1200, 2400, 4800], "kind": "legend_necro"},
    {"id": 110, "name": "Goettlicher Blitz", "cost": 0, "damage": 150, "range": 6.0, "speed": 1.0,
     "special": "Himmelskette", "upgrade": [700, 1400, 2800, 5600], "kind": "legend_light"},
    {"id": 111, "name": "Goettlicher Speer", "cost": 0, "damage": 180, "range": 4.0, "speed": 0.95,
     "special": "Drachenurteil", "upgrade": [800, 1600, 3200, 6400], "kind": "legend_spear"},
]


MERGES = {
    frozenset([1, 3]): (500, 101),
    frozenset([2, 4]): (600, 102),
    frozenset([5, 7]): (800, 103),
    frozenset([6, 9]): (900, 104),
    frozenset([8, 11]): (1000, 105),
    frozenset([10, 13]): (1100, 106),
    frozenset([12, 18]): (1200, 107),
    frozenset([14, 19]): (1300, 108),
    frozenset([16, 19]): (1400, 109),
    frozenset([15, 20]): (1600, 110),
    frozenset([17, 20]): (1800, 111),
}

MERGES = {key: (int(cost * 1.65), result_id) for key, (cost, result_id) in MERGES.items()}


ZOMBIES = [
    {"id": 0, "name": "Fusssoldat", "hp": 30, "speed": 1.0, "damage": 5, "coins": 10, "size": 16, "color": C.POISON},
    {"id": 1, "name": "Laeufer", "hp": 22, "speed": 1.8, "damage": 4, "coins": 12, "size": 16, "color": C.BLUE},
    {"id": 2, "name": "Panzer", "hp": 85, "speed": 0.55, "damage": 10, "coins": 18, "size": 18, "color": C.STONE_DARK},
    {"id": 3, "name": "Exploder", "hp": 38, "speed": 1.0, "damage": 20, "coins": 20, "size": 16, "color": C.ORANGE},
    {"id": 4, "name": "Riese", "hp": 180, "speed": 0.38, "damage": 25, "coins": 40, "size": 28, "color": C.GRASS_LIGHT},
    {"id": 5, "name": "Eis-Zombie", "hp": 60, "speed": 0.82, "damage": 8, "coins": 18, "size": 16, "color": C.ICE},
    {"id": 6, "name": "Feuer-Zombie", "hp": 65, "speed": 0.88, "damage": 8, "coins": 18, "size": 16, "color": C.RED},
    {"id": 7, "name": "Gift-Zombie", "hp": 52, "speed": 0.92, "damage": 7, "coins": 18, "size": 16, "color": C.POISON},
    {"id": 8, "name": "Knochen-Zombie", "hp": 45, "speed": 1.1, "damage": 6, "coins": 16, "size": 16, "color": C.WHITE},
    {"id": 9, "name": "Schatten-Zombie", "hp": 40, "speed": 1.42, "damage": 12, "coins": 24, "size": 16, "color": C.SHADOW},
    {"id": 10, "name": "Krieger-Zombie", "hp": 105, "speed": 0.75, "damage": 15, "coins": 32, "size": 18, "color": C.STONE_LIGHT},
    {"id": 11, "name": "Magier-Zombie", "hp": 70, "speed": 0.78, "damage": 16, "coins": 35, "size": 16, "color": C.PURPLE},
    {"id": 12, "name": "Bestien-Zombie", "hp": 120, "speed": 1.15, "damage": 18, "coins": 45, "size": 20, "color": C.GRASS_LIGHT},
    {"id": 13, "name": "Untoter Reiter", "hp": 160, "speed": 1.28, "damage": 22, "coins": 65, "size": 32, "color": C.SHADOW},
    {"id": 14, "name": "Boss-Zombie", "hp": 500, "speed": 0.30, "damage": 50, "coins": 200, "size": 48, "color": C.GOLD},
]


def balance_weapon_prices():
    for item in WEAPONS:
        item["cost"] = int(item["cost"] * 2.0)
        item["upgrade"] = [int(cost * 2.35) for cost in item["upgrade"]]
    for item in LEGENDARIES:
        item["upgrade"] = [int(cost * 1.9) for cost in item["upgrade"]]


balance_weapon_prices()


def make_specs():
    specs = {}
    for item in WEAPONS + LEGENDARIES:
        specs[item["id"]] = item
    return specs


WEAPON_BY_ID = make_specs()


def dist(a, b):
    return math.hypot(a[0] - b[0], a[1] - b[1])


def clamp(value, lo, hi):
    return max(lo, min(hi, value))


def calculate_wave_stats(wave):
    count = 7 + int(wave * 2.55)
    if wave > 18:
        count += int((wave - 18) * 0.9)
    if wave > 50:
        count += (wave - 50) * 3
    if wave > 100:
        count += (wave - 100) * 6
    upgrade_level = min(6, wave // 20 + 1)
    late_hp = max(0, wave - 16) ** 1.15 * 0.006
    late_damage = max(0, wave - 24) * 0.010
    late_speed = max(0, wave - 32) * 0.003
    hp_mult = min(17.0, 1.0 + (wave - 1) * 0.076 + late_hp)
    speed_mult = min(3.7, 1.0 + (wave - 1) * 0.015 + late_speed)
    damage_mult = min(7.2, 1.0 + (wave - 1) * 0.046 + late_damage)
    return count, upgrade_level, hp_mult, speed_mult, damage_mult


def wave_reward_mult(wave):
    early = max(0, wave - 6) * 0.018
    late = max(0, wave - 22) * 0.010
    return max(0.42, 1.0 - min(0.58, early + late))


def weighted_zombies_for_wave(wave):
    if wave <= 5:
        return [(0, 80), (1, 20)]
    if wave <= 10:
        return [(0, 60), (1, 25), (2, 10), (3, 5)]
    if wave <= 15:
        return [(0, 40), (1, 20), (2, 15), (3, 10), (4, 5), (5, 5), (6, 5)]
    if wave <= 20:
        return [(0, 30), (1, 15), (2, 15), (3, 10), (4, 5), (5, 5), (6, 5), (7, 5), (8, 5), (9, 5)]
    if wave <= 30:
        return [(0, 20), (1, 10), (2, 15), (3, 10), (4, 5), (5, 5), (6, 5), (7, 5), (8, 5), (9, 5), (10, 5), (11, 5), (12, 5), (13, 5)]
    if wave <= 50:
        return [(0, 15), (1, 10), (2, 10), (3, 10), (4, 5), (5, 5), (6, 5), (7, 5), (8, 5), (9, 5), (10, 5), (11, 5), (12, 5), (13, 5), (14, 5)]
    return [(0, 10), (1, 10), (2, 10), (3, 10), (4, 5), (5, 5), (6, 5), (7, 5), (8, 5), (9, 5), (10, 5), (11, 5), (12, 5), (13, 5), (14, 10)]


def choose_weighted(options):
    total = sum(weight for _, weight in options)
    roll = random.randint(1, total)
    acc = 0
    for zid, weight in options:
        acc += weight
        if roll <= acc:
            return zid
    return options[-1][0]


class PathMath:
    def __init__(self):
        self.segments = []
        self.total = 0
        for i in range(len(PATH) - 1):
            a = PATH[i]
            b = PATH[i + 1]
            length = dist(a, b)
            self.segments.append((a, b, length, self.total))
            self.total += length

    def point_at(self, distance):
        d = clamp(distance, 0, self.total)
        for a, b, length, start in self.segments:
            if d <= start + length:
                t = 0 if length == 0 else (d - start) / length
                return (a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t)
        return PATH[-1]


PATH_MATH = PathMath()


class Weapon:
    def __init__(self, spec_id, slot_index):
        self.spec_id = spec_id
        self.slot_index = slot_index
        self.x, self.y = SLOTS[slot_index]
        self.level = 1
        self.cooldown = random.randint(0, 25)
        self.shots = 0
        self.pulse = 0
        self.ability_charge = 0

    @property
    def spec(self):
        return WEAPON_BY_ID[self.spec_id]

    def damage(self, damage_boost):
        base = self.spec["damage"]
        level_mult = 1.0 + (self.level - 1) * 0.45
        return int(base * level_mult * damage_boost)

    def range_px(self):
        bonus = 1.15 if self.level >= 5 and self.spec["kind"] in ("flame", "storm", "legend_storm") else 1.0
        return int(self.spec["range"] * 42 * bonus)

    def cooldown_frames(self, speed_boost):
        base = max(8, int(self.spec["speed"] * 45 / speed_boost))
        return max(5, base - (self.level - 1) * 4)

    def upgrade_cost(self):
        if self.level >= 5:
            return None
        return self.spec["upgrade"][self.level - 1]

    def ability_need(self):
        base = 22 if self.spec_id >= 100 else 30
        return max(10, base - self.level * 2)

    def ability_ready(self):
        return self.ability_charge >= self.ability_need()


class Zombie:
    def __init__(self, zombie_id, wave, hp_mult, speed_mult, damage_mult):
        spec = ZOMBIES[zombie_id]
        self.id = zombie_id
        self.name = spec["name"]
        self.size = spec["size"]
        self.color = spec["color"]
        boss_bonus = 1
        if zombie_id == 14 and wave >= 100:
            boss_bonus += (wave - 100) * 0.025
        self.max_hp = int(spec["hp"] * hp_mult * boss_bonus)
        if zombie_id == 14 and wave % 10 == 0:
            self.max_hp = max(self.max_hp, 500 + max(0, wave - 10) * 75)
        self.hp = self.max_hp
        self.base_speed = spec["speed"] * speed_mult
        self.damage = int(spec["damage"] * damage_mult)
        self.coins = int(spec["coins"] * (1 + wave * 0.02))
        if zombie_id == 14:
            self.coins = max(self.coins, 200 + max(0, wave - 10) * 10)
        self.distance = -random.randint(0, 80)
        self.x, self.y = PATH[0]
        self.dead = False
        self.reached_castle = False
        self.freeze = 0
        self.burn = 0
        self.poison = 0
        self.slow = 0
        self.flash = 0
        self.tick_counter = 0
        self.shield = 0
        if zombie_id in (2, 10):
            self.shield = max(14, self.max_hp // 4)
        if zombie_id == 14:
            self.shield = max(80, self.max_hp // 5)

    def update(self):
        self.tick_counter += 1
        if self.flash > 0:
            self.flash -= 1
        if self.freeze > 0:
            self.freeze -= 1
        if self.burn > 0:
            self.burn -= 1
            if self.tick_counter % 30 == 0:
                self.take_damage(3 + self.max_hp // 90, C.ORANGE)
        if self.poison > 0:
            self.poison -= 1
            if self.tick_counter % 30 == 0:
                self.take_damage(4 + self.max_hp // 100, C.POISON)

        speed = self.base_speed * 0.45
        if self.freeze > 0:
            speed *= 0.48
        if self.slow > 0:
            self.slow -= 1
            speed *= 0.72
        self.distance += speed
        self.x, self.y = PATH_MATH.point_at(self.distance)
        if self.distance >= PATH_MATH.total:
            self.reached_castle = True
            self.dead = True

    def take_damage(self, amount, _color=C.WHITE):
        if self.shield > 0:
            blocked = min(self.shield, amount // 2 + 1)
            self.shield -= blocked
            amount -= blocked
        self.hp -= amount
        self.flash = 4
        if self.hp <= 0:
            self.dead = True


class Projectile:
    def __init__(self, x1, y1, x2, y2, color, life=12, radius=1, style="line"):
        self.x1 = x1
        self.y1 = y1
        self.x2 = x2
        self.y2 = y2
        self.color = color
        self.life = life
        self.max_life = life
        self.radius = radius
        self.style = style

    def update(self):
        self.life -= 1


class Effect:
    def __init__(self, x, y, color, kind="spark", life=18, radius=8):
        self.x = x
        self.y = y
        self.color = color
        self.kind = kind
        self.life = life
        self.max_life = life
        self.radius = radius

    def update(self):
        self.life -= 1


class FloatingText:
    def __init__(self, x, y, text, color):
        self.x = x
        self.y = y
        self.text = text
        self.color = color
        self.life = 60

    def update(self):
        self.life -= 1
        self.y -= 0.25


class DropItem:
    def __init__(self, x, y, kind, color, amount):
        self.x = clamp(x, 18, 492)
        self.y = clamp(y, 92, 418)
        self.kind = kind
        self.color = color
        self.amount = amount
        self.life = FPS * 12
        self.pulse = random.randint(0, 30)

    def update(self):
        self.life -= 1
        self.pulse += 1

    def contains(self, mx, my):
        return dist((self.x, self.y), (mx, my)) <= 14


class Pet:
    def __init__(self, pet_id, level=1):
        self.pet_id = pet_id
        self.level = level
        self.x = random.randint(64, 418)
        self.y = random.randint(128, 398)
        self.phase = random.random() * math.pi * 2
        self.cooldown = random.randint(20, 90)

    @property
    def spec(self):
        return PET_BY_ID[self.pet_id]

    def update(self):
        self.phase += 0.035
        self.x += math.cos(self.phase) * 0.34
        self.y += math.sin(self.phase * 0.8) * 0.24
        self.x = clamp(self.x, 38, 430)
        self.y = clamp(self.y, 104, 410)
        if self.cooldown > 0:
            self.cooldown -= 1

    def upgrade_cost(self):
        return int(self.spec["cost"] * (1.35 + self.level * 0.85))


class Button:
    def __init__(self, x, y, w, h, label, color, text_color=C.WHITE):
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.label = label
        self.color = color
        self.text_color = text_color

    def contains(self, x, y):
        return self.x <= x <= self.x + self.w and self.y <= y <= self.y + self.h


class Game:
    def __init__(self):
        pyxel.init(WIDTH, HEIGHT, title="BurgDefense Ultimate", fps=FPS)
        pyxel.mouse(True)
        for idx, value in PALETTE.items():
            if hasattr(pyxel, "colors"):
                pyxel.colors[idx] = value
        self.setup_audio()
        self.reset_state()
        pyxel.run(self.update, self.draw)

    def setup_audio(self):
        self.audio_ready = False
        try:
            pyxel.sound(0).set("c3e3", "pp", "33", "nn", 18)          # place
            pyxel.sound(1).set("c4", "p", "2", "n", 10)              # shot
            pyxel.sound(2).set("g3c4", "tp", "32", "nn", 16)       # upgrade
            pyxel.sound(3).set("f3c3", "nn", "43", "ff", 12)       # hit/death
            pyxel.sound(4).set("c4e4g4", "sss", "333", "nnn", 20)  # ability
            pyxel.sound(5).set("g2c3", "pp", "43", "ff", 20)       # damage
            pyxel.sound(6).set("c2e2g2c3", "tttt", "2222", "nnnn", 28)
            pyxel.sound(7).set("g1c2e2g2", "ssss", "1111", "nnnn", 36)
            pyxel.sound(8).set("c3", "p", "1", "n", 6)              # soft click
            pyxel.sound(9).set("c2g1c1", "ttt", "321", "fff", 30)  # defeat
            pyxel.music(0).set([6], [7], [], [])
            self.audio_ready = True
        except Exception:
            self.audio_ready = False

    def safe_play(self, sound_id):
        if not getattr(self, "audio_ready", False):
            return
        try:
            pyxel.play(3, sound_id)
        except Exception:
            self.audio_ready = False

    def start_music(self):
        if not getattr(self, "audio_ready", False):
            return
        try:
            pyxel.playm(0, loop=True)
        except Exception:
            self.audio_ready = False

    def reset_state(self):
        self.screen = "boot"
        self.map_page = 0
        self.map_index = 0
        self.pending_map_index = None
        self.difficulty_name = "Normal"
        self.game_speed = 1
        self.weapon_panel_open = False
        self.merge_overview_open = False
        self.merge_confirm = None
        self.loading_timer = 0
        self.cutscene_frame = 0
        self.prepare_run_state()

    def prepare_run_state(self):
        self.configure_map(self.map_index)
        random.seed(7)
        self.stars = [(random.randint(0, 511), random.randint(0, 425), random.randint(0, 3)) for _ in range(120)]
        self.trees = self.make_trees()
        self.pebbles = self.make_pebbles()
        self.torches = self.make_torches()
        self.field_flora = self.make_field_flora()

        diff = DIFFICULTIES[self.difficulty_name]
        self.gold = diff["start_gold"]
        self.soul = 0
        self.castle_hp = 20
        self.castle_max_hp = 20
        self.kills = 0
        self.wave = 0
        self.time = 0
        self.best_wave = 0
        self.game_over = False
        self.paused = False
        self.auto_wave = False

        self.weapons = []
        self.zombies = []
        self.projectiles = []
        self.effects = []
        self.texts = []
        self.drop_items = []
        self.pets = []
        self.wave_queue = []
        self.spawn_timer = 0
        self.next_wave_delay = 90

        self.weapon_scroll = 0
        self.selected_shop_weapon = 1
        self.placement_ready = False
        self.selected_slot = None
        self.weapon_popup_open = False
        self.show_range = False
        self.merge_source = None
        self.merge_mode = False
        self.shop_open = False
        self.achievements_open = False
        self.pet_shop_open = False
        self.pet_page = 0
        self.message = "Oeffne Waffen rechts und platziere auf freie Flaechen."
        self.message_timer = 180

        self.damage_boost_timer = 0
        self.speed_boost_timer = 0
        self.range_boost_timer = 0
        self.gold_bonus = 1.0
        self.revive_charges = 0
        self.weather_name = "Sonnig"
        self.weather_waves_left = 0
        self.stats = {
            "kills": 0,
            "waves": 0,
            "bosses": 0,
            "weapons": 0,
            "upgrades": 0,
            "abilities": 0,
            "items": 0,
            "pets": 0,
            "gold": 0,
            "merges": 0,
        }
        self.achievements = {entry["id"]: 0 for entry in ACHIEVEMENTS}

        self.buttons = [
            Button(10, 480, 78, 18, "VERKAUF", C.RED),
            Button(96, 480, 84, 18, "UPGRADE", C.POISON, C.NIGHT),
            Button(188, 480, 76, 18, "MERGE", C.PURPLE),
            Button(272, 480, 112, 18, "SPECIAL SHOP", C.ORANGE, C.NIGHT),
            Button(392, 480, 108, 18, "NAECHSTE WELLE", C.SHADOW),
        ]

        self.show_message("Klicke auf START, dann platziere Waffen auf freien Feldern.", C.GOLD)

    def start_game(self):
        self.prepare_run_state()
        self.screen = "game"
        self.start_music()
        self.show_message("Baue zuerst Waffen. WELLE startet den Angriff.", C.GOLD)

    def configure_map(self, index):
        global PATH, PATH_MATH, SLOTS
        self.map_index = index % len(MAPS)
        PATH = [tuple(point) for point in MAPS[self.map_index]["path"]]
        PATH_MATH = PathMath()
        SLOTS = generate_grid_slots()

    def make_trees(self):
        trees = []
        for y in range(92, 420, 38):
            trees.append((8 + (y // 38) % 3 * 5, y, 0))
            trees.append((482 - (y // 38) % 3 * 7, y + 11, 1))
        for x in range(20, 470, 64):
            if x not in (280, 344, 408):
                trees.append((x, 86 + (x // 64) % 2 * 16, 2))
        return trees

    def make_pebbles(self):
        pebbles = []
        for i, (x, y) in enumerate(PATH):
            random.seed(100 + i)
            for _ in range(8):
                pebbles.append((x + random.randint(-13, 13), y + random.randint(-13, 13), random.randint(0, 2)))
        return pebbles

    def make_torches(self):
        torches = []
        for i, (x, y) in enumerate(PATH[1:-1]):
            if i % 2 == 0 and 20 < x < 470 and 100 < y < 410:
                offset = -24 if i % 4 == 0 else 24
                torches.append((x + offset, y))
        return torches[:12]

    def make_field_flora(self):
        random.seed(17)
        flora = []
        style = MAPS[self.map_index]["style"] if hasattr(self, "map_index") else "meadow"
        colors = {
            "frost": [C.ICE, C.WHITE],
            "crystal": [C.ICE, C.PURPLE],
            "ember": [C.ORANGE, C.RED],
            "shadow": [C.PURPLE, C.STONE_DARK],
            "rune": [C.PURPLE, C.GOLD],
            "sun": [C.GOLD, C.STONE_LIGHT],
            "royal": [C.GOLD, C.WHITE],
        }.get(style, [C.GRASS_LIGHT, C.POISON])
        for _ in range(18):
            x = random.randint(12, 492)
            y = random.randint(92, 420)
            too_close_to_path = distance_to_path(x, y) < 34
            if not too_close_to_path:
                flora.append((x, y, random.choice(colors), random.randint(0, 2)))
        return flora

    def show_message(self, text, color=C.WHITE):
        self.message = text
        self.message_timer = 180
        self.texts.append(FloatingText(166, 78, text[:33], color))

    def current_weather(self):
        for weather in WEATHERS:
            if weather["name"] == self.weather_name:
                return weather
        return WEATHERS[0]

    def advance_weather(self):
        if self.weather_waves_left <= 0:
            total = sum(item["weight"] for item in WEATHERS)
            roll = random.randint(1, total)
            acc = 0
            chosen = WEATHERS[0]
            for item in WEATHERS:
                acc += item["weight"]
                if roll <= acc:
                    chosen = item
                    break
            self.weather_name = chosen["name"]
            self.weather_waves_left = random.randint(chosen["min"], chosen["max"])
        self.weather_waves_left -= 1

    def weather_zombie_speed_mult(self):
        return self.current_weather()["z_speed"]

    def weather_weapon_range_mult(self):
        return self.current_weather()["range"]

    def weather_weapon_speed_mult(self):
        return self.current_weather()["weapon_speed"]

    def weather_zombie_fade(self):
        return self.current_weather()["fade"]

    def weather_effect_text(self):
        name = self.weather_name
        if name == "Sonnig":
            return "kein Effekt"
        if name == "Regen":
            return "Zombies -10% Tempo"
        if name == "Sturm":
            return "Zombies -20%, Range -20%"
        if name == "Nacht":
            return "Zombies dunkler"
        if name == "Nebel":
            return "Range -30%"
        if name == "Vollmond":
            return "Zombies +20% Tempo"
        if name == "Schnee":
            return "Zombies -30%, Waffen langsamer"
        return ""

    def effective_range(self, weapon):
        bonus = 1.0
        if self.range_boost_timer > 0:
            bonus += 0.20
        return int(weapon.range_px() * self.weather_weapon_range_mult() * self.pet_range_mult() * bonus)

    def start_wave(self):
        self.wave += 1
        self.best_wave = max(self.best_wave, self.wave)
        self.stats["waves"] = max(self.stats["waves"], self.wave)
        self.advance_weather()
        regen = self.pet_wave_regen()
        if regen > 0 and self.wave > 1:
            before = self.castle_hp
            self.castle_hp = min(self.castle_max_hp, self.castle_hp + regen)
            if self.castle_hp > before:
                self.texts.append(FloatingText(430, 56, "+" + str(self.castle_hp - before), C.RED))
        count, _upgrade, hp_mult, speed_mult, damage_mult = calculate_wave_stats(self.wave)
        diff = DIFFICULTIES[self.difficulty_name]
        count = max(3, int(count * diff["count"]))
        hp_mult *= diff["hp"]
        speed_mult *= diff["speed"]
        damage_mult *= diff["damage"]
        self.wave_queue = []
        weights = weighted_zombies_for_wave(self.wave)
        for _ in range(count):
            zid = choose_weighted(weights)
            if zid == 14 and self.wave % 10 != 0 and self.wave < 31:
                zid = random.choice([0, 1, 2, 3])
            self.wave_queue.append((zid, hp_mult, speed_mult, damage_mult))
        boss_interval = 6 if self.difficulty_name == "Schwer" else 8 if self.difficulty_name == "Normal" else 10
        if self.wave % boss_interval == 0:
            self.wave_queue.append((14, hp_mult, speed_mult, damage_mult))
            self.show_message("Boss-Welle! Halte die Burg!", C.RED)
        else:
            self.show_message("Welle " + str(self.wave) + " startet. Wetter: " + self.weather_name, C.GOLD)
        self.spawn_timer = 30

    def update(self):
        if pyxel.btnp(pyxel.KEY_R):
            self.reset_state()
            return
        if self.screen == "boot":
            if pyxel.btnp(pyxel.MOUSE_BUTTON_LEFT):
                self.handle_boot_click(pyxel.mouse_x, pyxel.mouse_y)
            return
        if self.screen == "tutorial":
            if pyxel.btnp(pyxel.MOUSE_BUTTON_LEFT):
                self.handle_tutorial_click(pyxel.mouse_x, pyxel.mouse_y)
            return
        if self.screen == "menu":
            if pyxel.btnp(pyxel.MOUSE_BUTTON_LEFT):
                self.handle_menu_click(pyxel.mouse_x, pyxel.mouse_y)
            return
        if self.screen == "difficulty":
            if pyxel.btnp(pyxel.MOUSE_BUTTON_LEFT):
                self.handle_difficulty_click(pyxel.mouse_x, pyxel.mouse_y)
            return
        if self.screen == "loading":
            self.loading_timer -= 1
            if self.loading_timer <= 0:
                self.cutscene_frame = 0
                self.screen = "cutscene"
            return
        if self.screen == "cutscene":
            self.cutscene_frame += 1
            if pyxel.btnp(pyxel.MOUSE_BUTTON_LEFT) or self.cutscene_frame > 420:
                self.start_game()
            return
        if self.screen == "defeat":
            if pyxel.btnp(pyxel.MOUSE_BUTTON_LEFT):
                self.handle_defeat_click(pyxel.mouse_x, pyxel.mouse_y)
            return
        if self.game_over:
            return
        if pyxel.btnp(pyxel.KEY_P):
            self.paused = not self.paused
        if self.paused:
            if pyxel.btnp(pyxel.MOUSE_BUTTON_LEFT):
                self.handle_pause_click(pyxel.mouse_x, pyxel.mouse_y)
            return

        self.handle_input()
        for _ in range(self.game_speed):
            self.update_game_tick()

    def update_game_tick(self):
        self.time += 1
        if self.message_timer > 0:
            self.message_timer -= 1
        if self.damage_boost_timer > 0:
            self.damage_boost_timer -= 1
        if self.speed_boost_timer > 0:
            self.speed_boost_timer -= 1
        if self.range_boost_timer > 0:
            self.range_boost_timer -= 1

        self.update_wave_spawning()
        self.update_zombies()
        self.update_pets()
        self.update_weapons()
        self.update_visuals()

        if self.castle_hp <= 0:
            if self.revive_charges > 0:
                self.revive_charges -= 1
                self.castle_hp = max(1, self.castle_max_hp // 2)
                self.effects.append(Effect(456, 92, C.ORANGE, "phoenix", 54, 48))
                self.show_message("Phoenix rettet die Burg!", C.ORANGE)
            else:
                self.castle_hp = 0
                self.game_over = True
                self.screen = "defeat"
                self.safe_play(9)
                self.show_message("Burg gefallen. R fuer Neustart.", C.RED)

    def handle_boot_click(self, mx, my):
        if 186 <= mx <= 326 and 292 <= my <= 326:
            self.screen = "menu"
            return
        if 186 <= mx <= 326 and 334 <= my <= 358:
            self.screen = "tutorial"

    def handle_tutorial_click(self, mx, my):
        if 186 <= mx <= 326 and 374 <= my <= 402:
            self.screen = "boot"

    def handle_menu_click(self, mx, my):
        if 52 <= mx <= 96 and 430 <= my <= 458:
            self.map_page = max(0, self.map_page - 1)
            return
        if 416 <= mx <= 460 and 430 <= my <= 458:
            self.map_page = min(1, self.map_page + 1)
            return
        start = self.map_page * 6
        for i in range(6):
            idx = start + i
            if idx >= len(MAPS):
                continue
            col = i % 3
            row = i // 3
            x = 46 + col * 154
            y = 132 + row * 128
            if x <= mx <= x + 126 and y <= my <= y + 98:
                self.pending_map_index = idx
                self.screen = "difficulty"
                return

    def handle_difficulty_click(self, mx, my):
        if 188 <= mx <= 322 and 356 <= my <= 382:
            self.screen = "menu"
            return
        names = ["Einfach", "Normal", "Schwer"]
        for i, name in enumerate(names):
            x = 88 + i * 116
            y = 278
            if x <= mx <= x + 94 and y <= my <= y + 44:
                self.map_index = self.pending_map_index if self.pending_map_index is not None else 0
                self.difficulty_name = name
                self.configure_map(self.map_index)
                self.loading_timer = 45
                self.screen = "loading"
                return

    def handle_defeat_click(self, mx, my):
        if 142 <= mx <= 246 and 330 <= my <= 358:
            self.loading_timer = 30
            self.screen = "loading"
            return
        if 266 <= mx <= 370 and 330 <= my <= 358:
            self.reset_state()
            return

    def handle_pause_click(self, mx, my):
        if 180 <= mx <= 332 and 214 <= my <= 244:
            self.paused = False
            self.show_message("Weiter geht's.", C.GOLD)
            return
        if 180 <= mx <= 332 and 254 <= my <= 284:
            self.prepare_run_state()
            self.screen = "game"
            return
        if 180 <= mx <= 332 and 294 <= my <= 324:
            self.reset_state()
            return

    def collect_drop_at(self, mx, my):
        for item in list(self.drop_items):
            if item.contains(mx, my):
                self.apply_drop_item(item)
                self.drop_items.remove(item)
                self.stats["items"] += 1
                self.safe_play(2)
                return True
        return False

    def handle_achievements_click(self, mx, my):
        if 426 <= mx <= 486 and 88 <= my <= 110:
            self.achievements_open = False
            return
        for i, entry in enumerate(ACHIEVEMENTS):
            y = 126 + i * 34
            if 356 <= mx <= 426 and y + 8 <= my <= y + 27:
                self.claim_achievement(entry)
                return

    def handle_pet_shop_click(self, mx, my):
        if 434 <= mx <= 492 and 88 <= my <= 110:
            self.pet_shop_open = False
            return
        if 86 <= mx <= 130 and 396 <= my <= 420:
            self.pet_page = max(0, self.pet_page - 1)
            return
        if 382 <= mx <= 426 and 396 <= my <= 420:
            self.pet_page = min(4, self.pet_page + 1)
            return
        start = self.pet_page * 4
        for i in range(4):
            idx = start + i
            if idx >= len(PETS):
                continue
            y = 124 + i * 64
            if 350 <= mx <= 430 and y + 20 <= my <= y + 42:
                self.buy_or_upgrade_pet(PETS[idx]["id"])
                return

    def handle_input(self):
        if pyxel.btnp(pyxel.KEY_LEFT):
            self.weapon_scroll = max(0, self.weapon_scroll - 1)
        if pyxel.btnp(pyxel.KEY_RIGHT):
            self.weapon_scroll = min(max(0, len(WEAPONS) - 9), self.weapon_scroll + 1)
        if pyxel.btnp(pyxel.MOUSE_BUTTON_LEFT):
            mx = pyxel.mouse_x
            my = pyxel.mouse_y
            self.safe_play(8)
            if self.merge_confirm is not None:
                self.handle_merge_confirm_click(mx, my)
                return
            if self.merge_overview_open:
                self.handle_merge_overview_click(mx, my)
                return
            if self.achievements_open:
                self.handle_achievements_click(mx, my)
                return
            if self.pet_shop_open:
                self.handle_pet_shop_click(mx, my)
                return
            if self.shop_open:
                self.handle_shop_click(mx, my)
                return
            if self.weapon_popup_open and self.handle_weapon_popup_click(mx, my):
                return
            if self.weapon_panel_open and self.handle_weapon_panel_click(mx, my):
                return
            if self.handle_top_button_click(mx, my):
                return
            if 474 <= mx <= 510 and 134 <= my <= 212:
                self.weapon_panel_open = not self.weapon_panel_open
                return
            self.handle_field_click(mx, my)

    def handle_top_button_click(self, mx, my):
        if 186 <= mx <= 210 and 55 <= my <= 73:
            self.game_speed = 1
            return True
        if 214 <= mx <= 238 and 55 <= my <= 73:
            self.game_speed = 2
            return True
        if 242 <= mx <= 266 and 55 <= my <= 73:
            self.game_speed = 4
            return True
        if 278 <= mx <= 326 and 55 <= my <= 73:
            if not self.zombies and not self.wave_queue:
                self.start_wave()
            else:
                self.show_message("Welle laeuft noch.", C.RED)
            return True
        if 332 <= mx <= 382 and 55 <= my <= 73:
            self.auto_wave = not self.auto_wave
            self.show_message("Auto-Welle: " + ("AN" if self.auto_wave else "AUS"), C.GOLD)
            return True
        if 388 <= mx <= 438 and 55 <= my <= 73:
            self.merge_overview_open = True
            return True
        if 444 <= mx <= 508 and 55 <= my <= 73:
            self.shop_open = True
            return True
        if 278 <= mx <= 326 and 34 <= my <= 52:
            self.achievements_open = True
            return True
        if 332 <= mx <= 382 and 34 <= my <= 52:
            self.pet_shop_open = True
            return True
        if 388 <= mx <= 438 and 34 <= my <= 52:
            self.paused = True
            return True
        if 444 <= mx <= 508 and 34 <= my <= 52:
            self.prepare_run_state()
            self.screen = "game"
            return True
        return False

    def handle_field_click(self, mx, my):
        if self.collect_drop_at(mx, my):
            return
        clicked_weapon = self.weapon_at(mx, my)
        if clicked_weapon is not None:
            if clicked_weapon.ability_ready() and dist((mx, my), (clicked_weapon.x + 13, clicked_weapon.y - 12)) <= 8:
                self.activate_ability(clicked_weapon)
                return
            if self.can_weapon_merge(clicked_weapon) and dist((mx, my), (clicked_weapon.x, clicked_weapon.y - 19)) <= 9:
                self.open_merge_confirm_for(clicked_weapon)
                return
            if self.merge_mode and self.merge_source is not None and clicked_weapon is not self.merge_source:
                self.try_merge(self.merge_source, clicked_weapon)
            else:
                self.selected_slot = clicked_weapon.slot_index
                self.weapon_popup_open = True
                self.show_range = True
                self.show_message(clicked_weapon.spec["name"] + " Stufe " + str(clicked_weapon.level), C.WHITE)
            return

        clicked_slot = self.slot_at(mx, my)
        if clicked_slot is not None:
            if self.weapon_in_slot(clicked_slot) is None and self.placement_ready:
                self.place_weapon(clicked_slot)
            elif self.weapon_in_slot(clicked_slot) is None:
                self.show_message("Erst rechts eine Waffe auswaehlen.", C.STONE_LIGHT)
            else:
                self.selected_slot = clicked_slot
                self.weapon_popup_open = True
                self.show_range = True
            return
        self.selected_slot = None
        self.weapon_popup_open = False
        self.show_range = False
        self.merge_mode = False
        self.merge_source = None

    def handle_weapon_panel_click(self, mx, my):
        if not (350 <= mx <= 511 and 84 <= my <= 426):
            return False
        if 490 <= mx <= 506 and 90 <= my <= 106:
            self.weapon_panel_open = False
            return True
        if 358 <= mx <= 376 and 394 <= my <= 418:
            self.weapon_scroll = max(0, self.weapon_scroll - 1)
            return True
        if 482 <= mx <= 502 and 394 <= my <= 418:
            self.weapon_scroll = min(max(0, len(WEAPONS) - 9), self.weapon_scroll + 1)
            return True
        for i in range(9):
            item_index = self.weapon_scroll + i
            if item_index >= len(WEAPONS):
                continue
            row = i // 3
            col = i % 3
            x = 360 + col * 48
            y = 112 + row * 88
            if x <= mx <= x + 42 and y <= my <= y + 68:
                self.selected_shop_weapon = WEAPONS[item_index]["id"]
                self.placement_ready = True
                self.weapon_panel_open = False
                self.show_message("Ausgewaehlt: " + WEAPONS[item_index]["name"], C.GOLD)
                return True
        return True

    def handle_weapon_popup_click(self, mx, my):
        weapon = self.selected_weapon()
        if weapon is None:
            return False
        x, y, w, h = self.weapon_popup_rect(weapon)
        if not (x <= mx <= x + w and y <= my <= y + h):
            return False
        if x + w - 16 <= mx <= x + w - 4 and y + 4 <= my <= y + 16:
            self.weapon_popup_open = False
            self.show_range = False
            return True
        if x + 8 <= mx <= x + 68 and y + h - 24 <= my <= y + h - 8:
            self.upgrade_selected()
            return True
        if x + 74 <= mx <= x + 124 and y + h - 24 <= my <= y + h - 8:
            self.sell_selected()
            return True
        if x + 130 <= mx <= x + 190 and y + h - 24 <= my <= y + h - 8:
            self.activate_ability(weapon)
            return True
        if self.can_weapon_merge(weapon) and x + 130 <= mx <= x + 190 and y + h - 46 <= my <= y + h - 30:
            self.open_merge_confirm_for(weapon)
            return True
        return True

    def handle_bottom_click(self, mx, my):
        if 6 <= mx <= 22 and 440 <= my <= 464:
            self.weapon_scroll = max(0, self.weapon_scroll - 1)
            return
        if 486 <= mx <= 506 and 440 <= my <= 464:
            self.weapon_scroll = min(len(WEAPONS) - 8, self.weapon_scroll + 1)
            return
        for i in range(8):
            item_index = self.weapon_scroll + i
            if item_index >= len(WEAPONS):
                continue
            x = 28 + i * 56
            if x <= mx <= x + 44 and 438 <= my <= 466:
                self.selected_shop_weapon = WEAPONS[item_index]["id"]
                self.show_message("Ausgewaehlt: " + WEAPONS[item_index]["name"], C.GOLD)
                return
        for button in self.buttons:
            if button.contains(mx, my):
                self.run_button(button.label)
                return

    def handle_shop_click(self, mx, my):
        if 438 <= mx <= 500 and 92 <= my <= 110:
            self.shop_open = False
            return
        for i, offer in enumerate(SHOP_OFFERS):
            col = i % 2
            row = i // 2
            x = 76 + col * 190
            y = 136 + row * 56
            w = 170
            h = 44
            name, _desc, cost, _color = offer
            if x <= mx <= x + w and y <= my <= y + h:
                self.buy_shop_item(name, cost)
                return

    def run_button(self, label):
        if label == "VERKAUF":
            self.sell_selected()
        elif label == "UPGRADE":
            self.upgrade_selected()
        elif label == "MERGE":
            self.begin_merge()
        elif label == "SPECIAL SHOP":
            self.shop_open = True
        elif label == "NAECHSTE WELLE":
            if not self.zombies and not self.wave_queue:
                self.start_wave()
            else:
                self.show_message("Welle laeuft noch.", C.RED)

    def achievement_value(self, entry):
        return self.stats.get(entry["stat"], 0)

    def claim_achievement(self, entry):
        stage = self.achievements[entry["id"]]
        if stage >= len(entry["targets"]):
            self.show_message("Errungenschaft schon komplett.", C.GOLD)
            return
        target = entry["targets"][stage]
        if self.achievement_value(entry) < target:
            self.show_message("Ziel noch nicht erreicht.", C.RED)
            return
        gold = entry["gold"][stage]
        soul = entry["soul"][stage]
        self.gold += gold
        self.soul += soul
        self.stats["gold"] += gold
        self.achievements[entry["id"]] = stage + 1
        self.effects.append(Effect(256, 164, C.GOLD, "sunburst", 34, 70))
        self.show_message("Belohnung: +" + str(gold) + " Gold, +" + str(soul) + " Soul.", C.GOLD)

    def choose_drop_type(self):
        total = sum(item["weight"] for item in DROP_TYPES)
        roll = random.randint(1, total)
        acc = 0
        for item in DROP_TYPES:
            acc += item["weight"]
            if roll <= acc:
                return item
        return DROP_TYPES[0]

    def maybe_spawn_drop(self, zombie, reward):
        chance = 0.10
        if zombie.id == 14:
            chance = 1.0
        elif zombie.id in (4, 10, 12, 13):
            chance = 0.18
        if random.random() > chance:
            return
        drop = self.choose_drop_type()
        amount = max(1, int(reward * random.uniform(0.35, 0.70)))
        if drop["kind"] == "soul":
            amount = 1
        if drop["kind"] in ("heal", "freeze", "burn", "speed", "damage"):
            amount = 1
        self.drop_items.append(DropItem(zombie.x, zombie.y, drop["kind"], drop["color"], amount))

    def apply_drop_item(self, item):
        if item.kind == "gold":
            self.gold += item.amount
            self.stats["gold"] += item.amount
            self.texts.append(FloatingText(item.x, item.y - 12, "+" + str(item.amount), C.GOLD))
        elif item.kind == "heal":
            self.castle_hp = min(self.castle_max_hp, self.castle_hp + 3)
            self.texts.append(FloatingText(item.x, item.y - 12, "+HP", C.RED))
        elif item.kind == "soul":
            self.soul += item.amount
            self.texts.append(FloatingText(item.x, item.y - 12, "+" + str(item.amount) + "S", C.PURPLE))
        elif item.kind == "freeze":
            for z in self.nearby_zombies(item.x, item.y, 72):
                z.freeze = max(z.freeze, FPS)
                z.slow = max(z.slow, FPS * 3)
            self.effects.append(Effect(item.x, item.y, C.ICE, "snow", 28, 72))
        elif item.kind == "burn":
            for z in self.nearby_zombies(item.x, item.y, 68):
                z.burn = max(z.burn, FPS * 5)
                z.take_damage(22 + self.wave * 2, C.ORANGE)
            self.effects.append(Effect(item.x, item.y, C.ORANGE, "firewall", 30, 64))
        elif item.kind == "speed":
            self.speed_boost_timer = max(self.speed_boost_timer, FPS * 12)
            self.texts.append(FloatingText(item.x, item.y - 12, "SPD", C.BLUE))
        elif item.kind == "damage":
            self.damage_boost_timer = max(self.damage_boost_timer, FPS * 12)
            self.texts.append(FloatingText(item.x, item.y - 12, "DMG", C.POISON))

    def pet_level(self, pet_id):
        for pet in self.pets:
            if pet.pet_id == pet_id:
                return pet.level
        return 0

    def pet_bonus_value(self, pet_id, values):
        level = self.pet_level(pet_id)
        if level <= 0:
            return 0
        return values[min(level, len(values)) - 1]

    def pet_gold_mult(self):
        bonus = 0
        bonus += self.pet_bonus_value(1, [0.10, 0.15, 0.20, 0.25, 0.30])
        bonus += self.pet_bonus_value(20, [0.15, 0.20, 0.25, 0.30, 0.35])
        return 1.0 + bonus

    def pet_damage_mult(self):
        bonus = 0
        bonus += self.pet_bonus_value(3, [0.10, 0.15, 0.20, 0.25, 0.30])
        bonus += self.pet_bonus_value(2, [0, 0, 0, 0.05, 0.10])
        bonus += self.pet_bonus_value(8, [0, 0, 0, 0.05, 0.10])
        bonus += self.pet_bonus_value(10, [0, 0, 0, 0.05, 0.10])
        bonus += self.pet_bonus_value(11, [0, 0, 0, 0.05, 0.10])
        bonus += self.pet_bonus_value(17, [0, 0, 0, 0.10, 0.20])
        bonus += self.pet_bonus_value(19, [0, 0, 0, 0.10, 0.20])
        bonus += self.pet_bonus_value(20, [0.15, 0.20, 0.25, 0.30, 0.35])
        return 1.0 + bonus

    def pet_speed_mult(self):
        bonus = 0
        bonus += self.pet_bonus_value(2, [0.10, 0.15, 0.20, 0.25, 0.30])
        bonus += self.pet_bonus_value(3, [0, 0, 0, 0.05, 0.10])
        bonus += self.pet_bonus_value(11, [0.20, 0.25, 0.30, 0.35, 0.40])
        bonus += self.pet_bonus_value(20, [0.15, 0.20, 0.25, 0.30, 0.35])
        return 1.0 + bonus

    def pet_range_mult(self):
        bonus = 0
        bonus += self.pet_bonus_value(10, [0.20, 0.25, 0.30, 0.35, 0.40])
        bonus += self.pet_bonus_value(17, [0.30, 0.35, 0.40, 0.45, 0.50])
        return 1.0 + bonus

    def pet_zombie_speed_mult(self):
        slow = 0
        slow += self.pet_bonus_value(8, [0.10, 0.15, 0.20, 0.25, 0.30])
        return max(0.62, 1.0 - slow)

    def pet_zombie_hp_mult(self):
        reduction = 0
        reduction += self.pet_bonus_value(9, [0.10, 0.15, 0.20, 0.25, 0.30])
        reduction += self.pet_bonus_value(19, [0.20, 0.25, 0.30, 0.35, 0.40])
        return max(0.55, 1.0 - reduction)

    def pet_wave_regen(self):
        regen = 0
        regen += self.pet_bonus_value(4, [0, 0, 0, 1, 2])
        regen += self.pet_bonus_value(5, [1, 2, 3, 4, 5])
        regen += self.pet_bonus_value(18, [0, 0, 0, 2, 4])
        return regen

    def pet_cost_mult(self):
        discount = self.pet_bonus_value(14, [0.10, 0.15, 0.20, 0.25, 0.30])
        return max(0.70, 1.0 - discount)

    def pet_upgrade_cost_mult(self):
        discount = self.pet_bonus_value(14, [0, 0, 0, 0.10, 0.20])
        return max(0.80, 1.0 - discount)

    def pet_extra_soul(self, boss):
        extra = 0
        if boss:
            extra += self.pet_bonus_value(12, [0, 0, 0, 1, 2])
            extra += self.pet_bonus_value(20, [0, 0, 0, 1, 2])
            owl = self.pet_level(1)
            if owl >= 4:
                extra += 1
        return int(extra)

    def pet_castle_bonus(self):
        bonus = 0
        bonus += self.pet_bonus_value(4, [10, 15, 20, 25, 30])
        bonus += self.pet_bonus_value(18, [30, 40, 50, 60, 70])
        bonus += int(20 * self.pet_bonus_value(20, [0.15, 0.20, 0.25, 0.30, 0.35]))
        return bonus

    def apply_pet_castle_bonuses(self):
        old = self.castle_max_hp
        self.castle_max_hp = 20 + self.pet_castle_bonus()
        if self.castle_max_hp > old:
            self.castle_hp += self.castle_max_hp - old
        self.castle_hp = min(self.castle_hp, self.castle_max_hp)
        phoenix = self.pet_level(7)
        self.revive_charges = 0
        if phoenix > 0:
            self.revive_charges = 1 if phoenix <= 3 else 2

    def buy_or_upgrade_pet(self, pet_id):
        owned = None
        for pet in self.pets:
            if pet.pet_id == pet_id:
                owned = pet
                break
        spec = PET_BY_ID[pet_id]
        if owned is None:
            if len(self.pets) >= 6:
                self.show_message("Maximal 6 Pets gleichzeitig.", C.RED)
                return
            cost = spec["cost"]
            if self.soul < cost:
                self.show_message(spec["name"] + " kostet " + str(cost) + " Soul.", C.RED)
                return
            self.soul -= cost
            self.pets.append(Pet(pet_id))
            self.stats["pets"] = len(self.pets)
            self.apply_pet_castle_bonuses()
            self.effects.append(Effect(256, 220, spec["color"], "heart", 34, 38))
            self.show_message(spec["name"] + " begleitet dich.", spec["color"])
            return
        if owned.level >= 5:
            self.show_message(spec["name"] + " ist schon Level 5.", C.GOLD)
            return
        cost = owned.upgrade_cost()
        if self.soul < cost:
            self.show_message("Upgrade kostet " + str(cost) + " Soul.", C.RED)
            return
        self.soul -= cost
        owned.level += 1
        self.apply_pet_castle_bonuses()
        self.effects.append(Effect(owned.x, owned.y, spec["color"], "heart", 28, 28))
        self.show_message(spec["name"] + " Level " + str(owned.level), spec["color"])

    def slot_at(self, mx, my):
        best_index = None
        best_distance = 999
        for i, (x, y) in enumerate(SLOTS):
            d = abs(mx - x) + abs(my - y)
            if d < best_distance:
                best_distance = d
                best_index = i
        if best_index is not None and best_distance <= 18:
            return best_index
        return None

    def weapon_at(self, mx, my):
        for weapon in self.weapons:
            if weapon.x - 24 <= mx <= weapon.x + 24 and weapon.y - 24 <= my <= weapon.y + 24:
                return weapon
        return None

    def weapon_in_slot(self, slot_index):
        for weapon in self.weapons:
            if weapon.slot_index == slot_index:
                return weapon
        return None

    def selected_weapon(self):
        if self.selected_slot is None:
            return None
        return self.weapon_in_slot(self.selected_slot)

    def place_weapon(self, slot_index):
        if not self.placement_ready:
            self.show_message("Erst rechts eine Waffe auswaehlen.", C.STONE_LIGHT)
            return
        spec = WEAPON_BY_ID[self.selected_shop_weapon]
        cost = int(spec["cost"] * self.pet_cost_mult())
        if self.gold < cost:
            self.show_message("Zu wenig Gold fuer " + spec["name"], C.RED)
            return
        self.gold -= cost
        weapon = Weapon(self.selected_shop_weapon, slot_index)
        self.weapons.append(weapon)
        self.stats["weapons"] += 1
        self.selected_slot = None
        self.show_range = False
        self.placement_ready = False
        self.effects.append(Effect(weapon.x, weapon.y, C.GOLD, "ring", 24, 18))
        self.safe_play(0)
        self.show_message(spec["name"] + " platziert.", C.POISON)

    def sell_selected(self):
        weapon = self.selected_weapon()
        if weapon is None:
            self.show_message("Keine Waffe ausgewaehlt.", C.RED)
            return
        refund = int(weapon.spec.get("cost", 0) * 0.55 + sum(weapon.spec["upgrade"][:weapon.level - 1]) * 0.35)
        if weapon.spec_id >= 100:
            refund += 250 * weapon.level
        self.gold += max(10, refund)
        self.weapons.remove(weapon)
        self.selected_slot = None
        self.show_message("Verkauft fuer " + str(max(10, refund)) + " Gold.", C.GOLD)

    def upgrade_selected(self):
        weapon = self.selected_weapon()
        if weapon is None:
            self.show_message("Keine Waffe ausgewaehlt.", C.RED)
            return
        cost = weapon.upgrade_cost()
        if cost is None:
            self.show_message("Schon Stufe 5. Bereit zum Mergen.", C.GOLD)
            return
        cost = int(cost * self.pet_upgrade_cost_mult())
        if self.gold < cost:
            self.show_message("Upgrade kostet " + str(cost) + " Gold.", C.RED)
            return
        self.gold -= cost
        weapon.level += 1
        self.stats["upgrades"] += 1
        weapon.pulse = 40
        self.effects.append(Effect(weapon.x, weapon.y, C.POISON, "burst", 28, 24))
        self.safe_play(2)
        self.show_message(weapon.spec["name"] + " auf Stufe " + str(weapon.level), C.POISON)

    def begin_merge(self):
        weapon = self.selected_weapon()
        if weapon is None:
            self.show_message("Erst eine Stufe-5-Waffe waehlen.", C.RED)
            return
        if weapon.level < 5:
            self.show_message("Mergen geht erst ab Stufe 5.", C.RED)
            return
        self.merge_mode = True
        self.merge_source = weapon
        self.show_message("Waehle die zweite passende Stufe-5-Waffe.", C.PURPLE)

    def try_merge(self, a, b):
        if a.level < 5 or b.level < 5:
            self.show_message("Beide Waffen muessen Stufe 5 sein.", C.RED)
            return
        key = frozenset([a.spec_id, b.spec_id])
        if key not in MERGES:
            self.show_message("Diese Waffen passen nicht zusammen.", C.RED)
            return
        cost, result_id = MERGES[key]
        if self.gold < cost:
            self.show_message("Merge kostet " + str(cost) + " Gold.", C.RED)
            return
        self.gold -= cost
        slot = a.slot_index
        x, y = a.x, a.y
        self.weapons.remove(a)
        self.weapons.remove(b)
        merged = Weapon(result_id, slot)
        self.weapons.append(merged)
        self.selected_slot = slot
        self.merge_mode = False
        self.merge_source = None
        self.stats["merges"] += 1
        self.effects.append(Effect(x, y, C.PURPLE, "nova", 42, 36))
        self.effects.append(Effect(b.x, b.y, C.GOLD, "nova", 42, 32))
        self.show_message("Legendaer: " + merged.spec["name"] + "!", C.GOLD)

    def weapon_popup_rect(self, weapon):
        w = 196
        h = 116
        x = weapon.x + 22
        y = weapon.y - 72
        if x + w > 506:
            x = weapon.x - w - 22
        if x < 6:
            x = 6
        y = max(86, min(304, y))
        return x, y, w, h

    def current_and_next_stats(self, weapon):
        current_damage = weapon.damage(1.0)
        current_range = self.effective_range(weapon)
        if weapon.level >= 5:
            return current_damage, current_range, None, None
        old = weapon.level
        weapon.level += 1
        next_damage = weapon.damage(1.0)
        next_range = self.effective_range(weapon)
        weapon.level = old
        return current_damage, current_range, next_damage, next_range

    def possible_merge_partners(self, weapon):
        if weapon.level < 5:
            return []
        partners = []
        for other in self.weapons:
            if other is weapon or other.level < 5:
                continue
            key = frozenset([weapon.spec_id, other.spec_id])
            if key in MERGES:
                cost, result_id = MERGES[key]
                partners.append((other, cost, result_id))
        return partners

    def can_weapon_merge(self, weapon):
        return len(self.possible_merge_partners(weapon)) > 0

    def open_merge_confirm_for(self, weapon):
        partners = self.possible_merge_partners(weapon)
        if not partners:
            self.show_message("Keine passende Max-Waffe gefunden.", C.RED)
            return
        other, cost, result_id = partners[0]
        self.merge_confirm = (weapon, other, cost, result_id)

    def handle_merge_confirm_click(self, mx, my):
        if self.merge_confirm is None:
            return
        if 178 <= mx <= 244 and 278 <= my <= 304:
            a, b, _cost, _result = self.merge_confirm
            self.merge_confirm = None
            self.try_merge(a, b)
            return
        if 268 <= mx <= 334 and 278 <= my <= 304:
            self.merge_confirm = None
            return

    def all_available_merges(self):
        rows = []
        for i, a in enumerate(self.weapons):
            if a.level < 5:
                continue
            for b in self.weapons[i + 1:]:
                if b.level < 5:
                    continue
                key = frozenset([a.spec_id, b.spec_id])
                if key in MERGES:
                    cost, result_id = MERGES[key]
                    rows.append((a, b, cost, result_id))
        return rows

    def handle_merge_overview_click(self, mx, my):
        if 432 <= mx <= 488 and 92 <= my <= 112:
            self.merge_overview_open = False
            return
        rows = self.all_available_merges()
        for i, row in enumerate(rows[:7]):
            y = 138 + i * 38
            if 350 <= mx <= 430 and y + 8 <= my <= y + 28:
                a, b, cost, result_id = row
                self.merge_confirm = (a, b, cost, result_id)
                self.merge_overview_open = False
                return

    def activate_ability(self, weapon):
        if not weapon.ability_ready():
            self.show_message("Ability laedt noch.", C.RED)
            return
        weapon.ability_charge = 0
        self.stats["abilities"] += 1
        self.safe_play(4)
        kind = weapon.spec["kind"]
        power = weapon.damage(1.0) + weapon.level * 10
        if self.activate_unique_ability(weapon, power):
            return
        if kind in ("fire", "flame", "dragon", "legend_firebow", "legend_storm", "legend_cannon"):
            for z in self.zombies:
                if dist((weapon.x, weapon.y), (z.x, z.y)) <= self.effective_range(weapon) + 40:
                    z.take_damage(power, C.ORANGE)
                    z.burn = max(z.burn, FPS * 6)
            self.effects.append(Effect(weapon.x, weapon.y, C.ORANGE, "sunburst", 34, self.effective_range(weapon)))
            self.show_message("Ability: Feuerwelle!", C.ORANGE)
        elif kind in ("ice", "icecannon", "legend_icebolt", "legend_frost"):
            for z in self.zombies:
                if dist((weapon.x, weapon.y), (z.x, z.y)) <= self.effective_range(weapon) + 30:
                    z.freeze = max(z.freeze, FPS * 2)
                    z.take_damage(power // 2, C.ICE)
            self.effects.append(Effect(weapon.x, weapon.y, C.ICE, "snow", 32, self.effective_range(weapon)))
            self.show_message("Ability: Froststopp!", C.ICE)
        elif kind in ("poison", "legend_poison", "root"):
            for z in self.zombies:
                if dist((weapon.x, weapon.y), (z.x, z.y)) <= self.effective_range(weapon) + 25:
                    z.poison = max(z.poison, FPS * 10)
                    z.slow = max(z.slow, FPS * 2)
            self.effects.append(Effect(weapon.x, weapon.y, C.POISON, "roots", 30, self.effective_range(weapon)))
            self.show_message("Ability: Giftgarten!", C.POISON)
        elif kind in ("lightning", "tesla", "legend_light", "beam", "star"):
            targets = sorted(self.zombies, key=lambda z: z.distance, reverse=True)[:10]
            for z in targets:
                z.take_damage(power, C.GOLD)
                self.projectiles.append(Projectile(weapon.x, weapon.y, z.x, z.y, C.GOLD, 12, 1, "bolt"))
            self.effects.append(Effect(weapon.x, weapon.y, C.GOLD, "storm", 28, 36))
            self.show_message("Ability: Himmelsladung!", C.GOLD)
        elif kind in ("necro", "legend_necro", "shadow", "obelisk"):
            for z in self.zombies:
                if dist((weapon.x, weapon.y), (z.x, z.y)) <= self.effective_range(weapon) + 20:
                    z.take_damage(power, C.PURPLE)
                    z.slow = max(z.slow, FPS * 2)
            self.soul += 1
            self.effects.append(Effect(weapon.x, weapon.y, C.PURPLE, "void", 36, self.effective_range(weapon)))
            self.show_message("Ability: Seelenriss!", C.PURPLE)
        elif kind in ("divine", "legend_spear", "legend_blade", "legend_axe"):
            self.castle_hp = min(self.castle_max_hp, self.castle_hp + 4)
            for z in self.zombies:
                if dist((weapon.x, weapon.y), (z.x, z.y)) <= self.effective_range(weapon) + 60:
                    z.take_damage(power, C.WHITE)
            self.effects.append(Effect(weapon.x, weapon.y, C.WHITE, "heal", 40, self.effective_range(weapon)))
            self.show_message("Ability: Lichturteil + Heilung!", C.WHITE)
        else:
            for z in self.nearby_zombies(weapon.x, weapon.y, self.effective_range(weapon) + 20, 6):
                z.take_damage(power, C.WHITE)
            self.effects.append(Effect(weapon.x, weapon.y, C.WHITE, "burst", 28, 42))
            self.show_message("Ability aktiviert.", C.WHITE)

    def activate_unique_ability(self, weapon, power):
        wid = weapon.spec_id
        r = self.effective_range(weapon)
        targets = self.nearby_zombies(weapon.x, weapon.y, r + 40, 12)
        if wid == 1:
            for z in targets[:5]:
                z.take_damage(power, C.WHITE)
                self.effects.append(Effect(z.x, z.y, C.WHITE, "blade_cleave", 18, 28))
            self.effects.append(Effect(weapon.x, weapon.y, C.WHITE, "blade_cleave", 24, 46))
            self.show_message("Skill: Klingenwirbel", C.WHITE)
        elif wid == 2:
            for z in sorted(self.zombies, key=lambda zz: zz.distance, reverse=True)[:6]:
                if dist((weapon.x, weapon.y), (z.x, z.y)) <= r + 60:
                    z.take_damage(power + 8, C.STONE_LIGHT)
                    self.projectiles.append(Projectile(weapon.x, weapon.y, z.x, z.y, C.STONE_LIGHT, 16, 1, "arrow"))
            self.effects.append(Effect(weapon.x, weapon.y, C.STONE_LIGHT, "spear_wall", 22, 56))
            self.show_message("Skill: Speerwall", C.STONE_LIGHT)
        elif wid == 3:
            for z in targets[:8]:
                z.take_damage(power, C.GOLD)
                self.projectiles.append(Projectile(z.x, 82, z.x, z.y, C.GOLD, 18, 1, "arrow"))
            self.effects.append(Effect(weapon.x, weapon.y, C.GOLD, "arrow_rain", 28, r))
            self.show_message("Skill: Pfeilhagel", C.GOLD)
        elif wid == 4:
            self.area_damage(weapon.x, weapon.y, r + 20, power + 10, C.RED)
            for z in targets:
                z.slow = max(z.slow, FPS)
            self.effects.append(Effect(weapon.x, weapon.y, C.RED, "quake", 28, r))
            self.show_message("Skill: Axtbeben", C.RED)
        elif wid == 5:
            far = sorted(self.zombies, key=lambda z: z.distance, reverse=True)[:3]
            for z in far:
                z.take_damage(power * 2, C.GOLD)
                self.projectiles.append(Projectile(weapon.x, weapon.y, z.x, z.y, C.GOLD, 20, 1, "arrow"))
            self.effects.append(Effect(weapon.x, weapon.y, C.GOLD, "burst", 18, 26))
            self.show_message("Skill: Falkenschuss", C.GOLD)
        elif wid == 6:
            for z in targets:
                z.freeze = max(z.freeze, FPS * 2)
                z.take_damage(power // 2, C.ICE)
            self.effects.append(Effect(weapon.x, weapon.y, C.ICE, "snow", 34, r))
            self.show_message("Skill: Eisnetz", C.ICE)
        elif wid == 7:
            for z in targets:
                z.burn = max(z.burn, FPS * 8)
                z.take_damage(power, C.ORANGE)
            self.effects.append(Effect(weapon.x, weapon.y, C.ORANGE, "sunburst", 30, r))
            self.show_message("Skill: Brandregen", C.ORANGE)
        elif wid == 8:
            for z in targets[:5]:
                self.area_damage(z.x, z.y, 34, power + 12, C.STONE_DARK)
                self.projectiles.append(Projectile(weapon.x, 90, z.x, z.y, C.STONE_DARK, 20, 3, "orb"))
                self.effects.append(Effect(z.x, z.y, C.ORANGE, "meteor", 24, 34))
            self.show_message("Skill: Steinlawine", C.STONE_DARK)
        elif wid == 9:
            for z in targets[:9]:
                z.take_damage(power, C.GOLD)
                self.projectiles.append(Projectile(weapon.x, weapon.y, z.x, z.y, C.GOLD, 12, 1, "bolt"))
                self.effects.append(Effect(z.x, z.y, C.GOLD, "thunderbolt", 18, 32))
            self.show_message("Skill: Blitznetz", C.GOLD)
        elif wid == 10:
            for z in targets:
                z.poison = max(z.poison, FPS * 12)
                z.slow = max(z.slow, FPS)
            self.effects.append(Effect(weapon.x, weapon.y, C.POISON, "roots", 26, r))
            self.show_message("Skill: Toxinnebel", C.POISON)
        elif wid == 11:
            for z in targets[:4]:
                self.area_damage(z.x, z.y, 46, power + 20, C.ORANGE)
                self.projectiles.append(Projectile(weapon.x, weapon.y, z.x, z.y, C.ORANGE, 18, 3, "orb"))
                self.effects.append(Effect(z.x, z.y, C.ORANGE, "cannon_blast", 24, 42))
            self.show_message("Skill: Kanonensalve", C.ORANGE)
        elif wid == 12:
            for z in targets:
                z.take_damage(power, C.ORANGE)
                z.burn = max(z.burn, FPS * 9)
            self.effects.append(Effect(weapon.x, weapon.y, C.ORANGE, "firewall", 30, r))
            self.show_message("Skill: Feuerwand", C.ORANGE)
        elif wid == 13:
            for z in targets[:10]:
                z.take_damage(power, C.PURPLE)
                self.projectiles.append(Projectile(weapon.x, weapon.y, z.x, z.y, C.PURPLE, 15, 1, "rune"))
            self.effects.append(Effect(weapon.x, weapon.y, C.PURPLE, "crystal", 26, r))
            self.show_message("Skill: Kristallsplitter", C.PURPLE)
        elif wid == 14:
            for z in targets[:8]:
                z.freeze = max(z.freeze, FPS)
                z.take_damage(power, C.ICE)
            self.effects.append(Effect(weapon.x, weapon.y, C.ICE, "snow", 36, r))
            self.show_message("Skill: Eisgefaengnis", C.ICE)
        elif wid == 15:
            for z in targets[:12]:
                z.take_damage(power, C.GOLD)
                self.projectiles.append(Projectile(weapon.x, weapon.y, z.x, z.y, C.GOLD, 10, 1, "bolt"))
                self.effects.append(Effect(z.x, z.y, C.GOLD, "thunderbolt", 16, 40))
            self.show_message("Skill: Teslafeld", C.GOLD)
        elif wid == 16:
            for z in targets[:7]:
                z.take_damage(power, C.PURPLE)
                z.slow = max(z.slow, FPS)
            self.castle_hp = min(self.castle_max_hp, self.castle_hp + 2)
            self.soul += 1
            self.effects.append(Effect(weapon.x, weapon.y, C.PURPLE, "void", 34, r))
            self.show_message("Skill: Seelenraub", C.PURPLE)
        elif wid == 17:
            for z in targets:
                z.take_damage(power + 18, C.ORANGE)
                z.burn = max(z.burn, FPS * 7)
            self.effects.append(Effect(weapon.x, weapon.y, C.ORANGE, "dragon_flame", 34, r))
            self.show_message("Skill: Drachenatem", C.ORANGE)
        elif wid == 18:
            for z in targets:
                z.slow = max(z.slow, FPS * 2)
                z.take_damage(power // 2, C.BLUE)
            self.effects.append(Effect(weapon.x, weapon.y, C.BLUE, "tornado", 34, r))
            self.show_message("Skill: Orkanauge", C.BLUE)
        elif wid == 19:
            victims = sorted(self.zombies, key=lambda z: z.hp, reverse=True)[:4]
            for z in victims:
                z.take_damage(power * 2, C.PURPLE)
                self.effects.append(Effect(z.x, z.y, C.PURPLE, "slash", 18, 18))
            self.effects.append(Effect(weapon.x, weapon.y, C.PURPLE, "void", 28, r))
            self.show_message("Skill: Schattenstich", C.PURPLE)
        elif wid == 20:
            self.castle_hp = min(self.castle_max_hp, self.castle_hp + 5)
            for z in targets:
                z.take_damage(power + 25, C.WHITE)
            self.effects.append(Effect(weapon.x, weapon.y, C.WHITE, "heal", 38, r))
            self.show_message("Skill: Goettliches Urteil", C.WHITE)
        elif wid == 21:
            for z in targets[:8]:
                z.take_damage(power, C.PURPLE)
                z.poison = max(z.poison, FPS * 5)
            self.effects.append(Effect(weapon.x, weapon.y, C.PURPLE, "crystal", 28, r))
            self.show_message("Skill: Runenfalle", C.PURPLE)
        elif wid == 22:
            for z in sorted(self.zombies, key=lambda z: z.distance, reverse=True)[:9]:
                z.take_damage(power + 14, C.GOLD)
                self.projectiles.append(Projectile(weapon.x, weapon.y, z.x, z.y, C.GOLD, 16, 1, "bolt"))
                self.effects.append(Effect(z.x, z.y, C.GOLD, "solar_beam", 18, 38))
            self.show_message("Skill: Sonnenstrahl", C.GOLD)
        elif wid == 23:
            for z in targets:
                z.slow = max(z.slow, FPS * 2)
                z.take_damage(power // 2, C.POISON)
            self.effects.append(Effect(weapon.x, weapon.y, C.POISON, "roots", 36, r))
            self.show_message("Skill: Dornenkaefig", C.POISON)
        elif wid == 24:
            for z in targets[:10]:
                z.take_damage(power, C.GOLD)
                self.projectiles.append(Projectile(z.x, 82, z.x, z.y, C.GOLD, 20, 2, "orb"))
            self.effects.append(Effect(weapon.x, weapon.y, C.GOLD, "starfall", 34, r))
            self.show_message("Skill: Sternenfall", C.GOLD)
        elif wid == 25:
            for z in targets:
                z.take_damage(power, C.PURPLE)
                z.slow = max(z.slow, FPS * 2)
            self.effects.append(Effect(weapon.x, weapon.y, C.PURPLE, "void", 40, r))
            self.show_message("Skill: Obsidianriss", C.PURPLE)
        else:
            return False
        return True

    def buy_shop_item(self, name, cost):
        if self.soul < cost:
            self.show_message("Zu wenig Soul Coins.", C.RED)
            return
        self.soul -= cost
        if name == "Angriffswelle":
            self.damage_boost_timer = FPS * 25
            self.show_message("+45% Schaden fuer 25 Sekunden.", C.POISON)
        elif name == "Schnelle Haende":
            self.speed_boost_timer = FPS * 25
            self.show_message("+45% Angriffstempo.", C.BLUE)
        elif name == "Burg reparieren":
            self.castle_hp = min(self.castle_max_hp, self.castle_hp + 10)
            self.show_message("Burg repariert.", C.RED)
        elif name == "Gold-Finder":
            self.gold_bonus += 0.12
            self.show_message("Gold dauerhaft +12%.", C.GOLD)
        elif name == "Reichweiten-Elixier":
            self.range_boost_timer = FPS * 30
            self.show_message("+20% Reichweite fuer 30 Sekunden.", C.ICE)
        elif name == "Frostbombe":
            for z in self.zombies:
                z.slow = max(z.slow, FPS * 4)
                z.freeze = max(z.freeze, FPS)
            self.effects.append(Effect(250, 252, C.ICE, "snow", 46, 190))
            self.show_message("Frostbombe gezundet.", C.ICE)
        elif name == "Meteor-Flasche":
            if self.zombies:
                target = max(self.zombies, key=lambda z: z.distance)
                self.area_damage(target.x, target.y, 78, 160 + self.wave * 8, C.ORANGE)
                self.effects.append(Effect(target.x, target.y, C.ORANGE, "meteor", 42, 72))
            self.show_message("Meteor-Flasche geworfen.", C.ORANGE)
        elif name == "Schutzsiegel":
            self.castle_max_hp += 6
            self.castle_hp = min(self.castle_max_hp, self.castle_hp + 6)
            self.show_message("Burg dauerhaft stabiler.", C.WHITE)

    def apply_spawn_modifiers(self, zombie):
        hp_factor = self.pet_zombie_hp_mult()
        if hp_factor != 1.0:
            zombie.max_hp = max(1, int(zombie.max_hp * hp_factor))
            zombie.hp = zombie.max_hp
        zombie.base_speed *= self.weather_zombie_speed_mult() * self.pet_zombie_speed_mult()

    def update_wave_spawning(self):
        if self.wave_queue:
            self.spawn_timer -= 1
            if self.spawn_timer <= 0:
                zid, hp_mult, speed_mult, damage_mult = self.wave_queue.pop(0)
                zombie = Zombie(zid, self.wave, hp_mult, speed_mult, damage_mult)
                self.apply_spawn_modifiers(zombie)
                self.zombies.append(zombie)
                self.spawn_timer = max(10, 42 - self.wave // 3)
        elif not self.zombies and self.auto_wave:
            self.next_wave_delay -= 1
            if self.next_wave_delay <= 0:
                self.next_wave_delay = 90
                self.start_wave()
        elif not self.zombies:
            self.next_wave_delay = 90

    def update_zombies(self):
        for zombie in self.zombies:
            zombie.update()
        self.update_zombie_specials()
        for zombie in list(self.zombies):
            if zombie.dead:
                if zombie.reached_castle:
                    self.castle_hp -= zombie.damage
                    self.effects.append(Effect(464, 58, C.RED, "burst", 22, 22))
                    self.safe_play(5)
                    self.show_message("Burg -" + str(zombie.damage), C.RED)
                else:
                    if zombie.id == 3:
                        self.area_damage(zombie.x, zombie.y, 44, max(12, zombie.max_hp // 4), C.ORANGE)
                    boss_bonus = 1.0
                    if zombie.id == 14:
                        boss_bonus += self.pet_bonus_value(12, [0.50, 0.75, 1.00, 1.25, 1.50])
                    reward = max(1, int(zombie.coins * BASE_REWARD_MULT * wave_reward_mult(self.wave) * self.gold_bonus * self.pet_gold_mult() * boss_bonus * DIFFICULTIES[self.difficulty_name]["gold"]))
                    self.gold += reward
                    self.kills += 1
                    self.stats["kills"] = self.kills
                    self.stats["gold"] += reward
                    self.maybe_spawn_drop(zombie, reward)
                    for weapon in self.weapons:
                        weapon.ability_charge = min(weapon.ability_need(), weapon.ability_charge + (3 if zombie.id == 14 else 1))
                    if self.pet_level(15) > 0:
                        for near in self.nearby_zombies(zombie.x, zombie.y, 46 + self.pet_level(15) * 4, 4):
                            near.freeze = max(near.freeze, FPS + self.pet_level(15) * 12)
                    if zombie.id == 14:
                        self.stats["bosses"] += 1
                        self.soul += 1 + self.wave // 45 + self.pet_extra_soul(True)
                        self.effects.append(Effect(zombie.x, zombie.y, C.GOLD, "nova", 46, 38))
                        self.safe_play(4)
                    else:
                        self.effects.append(Effect(zombie.x, zombie.y, zombie.color, "spark", 18, 12))
                        if self.kills % 5 == 0:
                            self.safe_play(3)
                    self.texts.append(FloatingText(zombie.x - 5, zombie.y - 16, "+" + str(reward), C.GOLD))
                self.zombies.remove(zombie)

    def update_zombie_specials(self):
        for zombie in self.zombies:
            if zombie.dead:
                continue
            if zombie.id == 11 and zombie.tick_counter % 90 == 0:
                healed = 0
                for other in self.nearby_zombies(zombie.x, zombie.y, 58, 5):
                    if other is zombie or other.dead:
                        continue
                    before = other.hp
                    other.hp = min(other.max_hp, other.hp + max(3, other.max_hp // 18))
                    if other.hp > before:
                        healed += 1
                        self.effects.append(Effect(other.x, other.y, C.PURPLE, "spark", 14, 9))
                if healed:
                    self.effects.append(Effect(zombie.x, zombie.y, C.PURPLE, "ring", 18, 28))
            elif zombie.id == 7 and zombie.tick_counter % 75 == 0:
                for other in self.nearby_zombies(zombie.x, zombie.y, 38, 4):
                    if other is not zombie:
                        other.slow = max(other.slow, 30)
            elif zombie.id == 14 and zombie.tick_counter % 120 == 0:
                zombie.shield = min(zombie.max_hp // 4, zombie.shield + max(12, zombie.max_hp // 30))
                self.effects.append(Effect(zombie.x, zombie.y, C.GOLD, "ring", 20, 34))

    def update_pets(self):
        for pet in self.pets:
            pet.update()
            level = pet.level
            if pet.pet_id == 6 and pet.cooldown <= 0:
                for z in self.nearby_zombies(pet.x, pet.y, 58, 3 + level // 2):
                    z.burn = max(z.burn, FPS * 3)
                    z.take_damage(4 + level * 4, C.ORANGE)
                    self.projectiles.append(Projectile(pet.x, pet.y, z.x, z.y, C.ORANGE, 14, 2, "flame"))
                pet.cooldown = max(34, 82 - level * 7)
            elif pet.pet_id == 13:
                radius = 54 + level * 4
                for z in self.nearby_zombies(pet.x, pet.y, radius, 4):
                    z.slow = max(z.slow, FPS)
            elif pet.pet_id == 16 and pet.cooldown <= 0:
                for z in self.nearby_zombies(pet.x, pet.y, 70, 3 + level):
                    z.poison = max(z.poison, FPS * 3)
                    self.projectiles.append(Projectile(pet.x, pet.y, z.x, z.y, C.POISON, 16, 1, "bubble"))
                pet.cooldown = max(42, 96 - level * 8)
            elif pet.pet_id == 15 and pet.cooldown <= 0:
                for z in self.nearby_zombies(pet.x, pet.y, 50, 2):
                    z.slow = max(z.slow, FPS * 2)
                    self.effects.append(Effect(z.x, z.y, C.ICE, "snow", 16, 18))
                pet.cooldown = max(70, 135 - level * 8)

    def update_weapons(self):
        damage_boost = 1.45 if self.damage_boost_timer > 0 else 1.0
        speed_boost = 1.45 if self.speed_boost_timer > 0 else 1.0
        damage_boost *= self.pet_damage_mult()
        speed_boost *= self.pet_speed_mult() * self.weather_weapon_speed_mult()
        for weapon in self.weapons:
            if weapon.pulse > 0:
                weapon.pulse -= 1
            weapon.cooldown -= 1
            if weapon.cooldown <= 0:
                target = self.find_target(weapon)
                if target is not None:
                    self.fire_weapon(weapon, target, damage_boost)
                    weapon.cooldown = weapon.cooldown_frames(speed_boost)

    def find_target(self, weapon):
        candidates = []
        radius = self.effective_range(weapon)
        for zombie in self.zombies:
            if zombie.distance >= 0 and dist((weapon.x, weapon.y), (zombie.x, zombie.y)) <= radius:
                candidates.append(zombie)
        if not candidates:
            return None
        candidates.sort(key=lambda z: z.distance, reverse=True)
        return candidates[0]

    def nearby_zombies(self, x, y, radius, limit=None):
        hits = [z for z in self.zombies if dist((x, y), (z.x, z.y)) <= radius]
        hits.sort(key=lambda z: z.distance, reverse=True)
        if limit:
            return hits[:limit]
        return hits

    def fire_weapon(self, weapon, target, damage_boost):
        kind = weapon.spec["kind"]
        damage = weapon.damage(damage_boost)
        weapon.shots += 1
        color = self.projectile_color(kind)
        style = self.projectile_style(kind)
        self.projectiles.append(Projectile(weapon.x, weapon.y, target.x, target.y, color, 14, 2, style))
        if pyxel.frame_count % 8 == 0:
            self.safe_play(1)
        if kind in ("blade", "axe", "spear", "dragon", "shadow", "divine", "legend_blade", "legend_axe", "legend_spear", "rune"):
            self.melee_hit(weapon, target, damage, kind)
        elif kind in ("stone", "cannon", "icecannon", "magic", "legend_cannon", "obelisk"):
            self.area_hit(weapon, target, damage, kind)
        elif kind in ("lightning", "tesla", "legend_light", "legend_icebolt", "beam"):
            self.chain_hit(weapon, target, damage, kind)
        elif kind in ("flame", "legend_storm", "root"):
            self.flame_hit(weapon, target, damage, kind)
        else:
            self.arrow_hit(weapon, target, damage, kind)
        snake = self.pet_level(16)
        if snake > 0:
            target.poison = max(target.poison, FPS * (2 + snake))

    def projectile_color(self, kind):
        if "ice" in kind or "frost" in kind:
            return C.ICE
        if "fire" in kind or kind in ("flame", "dragon", "legend_storm"):
            return C.ORANGE
        if kind in ("lightning", "tesla", "legend_light", "legend_icebolt"):
            return C.GOLD
        if kind in ("beam", "star"):
            return C.GOLD
        if kind in ("root", "poison", "legend_poison"):
            return C.POISON
        if kind in ("rune", "obelisk") or "shadow" in kind or "necro" in kind:
            return C.PURPLE
        return C.WHITE

    def projectile_style(self, kind):
        if kind in ("arrow", "storm", "fire", "legend_firebow"):
            return "arrow"
        if kind == "star":
            return "star"
        if kind == "spear" or kind == "legend_spear":
            return "spear"
        if kind in ("blade", "axe", "shadow", "divine", "legend_blade", "legend_axe", "dragon", "rune"):
            return "slash"
        if kind in ("stone",):
            return "rock"
        if kind in ("cannon", "legend_cannon"):
            return "shell"
        if kind in ("ice", "icecannon", "legend_icebolt", "legend_frost"):
            return "ice"
        if kind in ("magic", "obelisk"):
            return "crystal"
        if kind in ("lightning", "tesla", "legend_light", "legend_icebolt", "beam"):
            return "bolt"
        if kind in ("flame", "legend_storm"):
            return "flame"
        if kind in ("poison", "legend_poison", "root"):
            return "root" if kind == "root" else "bubble"
        if kind in ("magic", "necro", "shadow", "legend_necro"):
            return "rune"
        return "slash"

    def melee_hit(self, weapon, target, damage, kind):
        hits = 1
        if weapon.level >= 5 and kind == "blade":
            hits = 2
        if kind == "legend_blade":
            hits = 3
        if weapon.level >= 5 and kind in ("axe", "shadow", "divine") and random.random() < 0.4:
            damage *= 2
            self.effects.append(Effect(target.x, target.y, C.RED, "slash", 16, 16))
        for _ in range(hits):
            target.take_damage(damage, self.projectile_color(kind))
        if kind == "rune":
            target.poison = max(target.poison, FPS * 4)
            target.slow = max(target.slow, FPS)
            if weapon.shots % 4 == 0:
                self.area_damage(target.x, target.y, 32, damage, C.PURPLE)
        if kind in ("dragon", "legend_spear", "divine", "legend_axe"):
            self.area_damage(target.x, target.y, 36, max(8, damage // 2), C.ORANGE)
        self.effects.append(Effect(target.x, target.y, self.projectile_color(kind), "slash", 15, 12))

    def arrow_hit(self, weapon, target, damage, kind):
        arrow_count = 1
        if weapon.level >= 5 and kind == "arrow":
            arrow_count = 3 if weapon.spec_id == 3 else 2
        if kind == "storm":
            arrow_count = 4 if weapon.level >= 5 else 3
        if kind == "legend_firebow":
            arrow_count = 5
        if kind == "star":
            arrow_count = 2 + min(3, weapon.level)
        targets = self.nearby_zombies(target.x, target.y, 46, arrow_count)
        for z in targets:
            z.take_damage(damage, self.projectile_color(kind))
            if kind in ("fire", "legend_firebow"):
                z.burn = max(z.burn, FPS * 6)
            if kind in ("ice",):
                z.freeze = max(z.freeze, FPS)
            if kind == "star":
                z.slow = max(z.slow, 20)
            self.effects.append(Effect(z.x, z.y, self.projectile_color(kind), "spark", 12, 8))

    def flame_hit(self, weapon, target, damage, kind):
        if kind == "root":
            hits = self.nearby_zombies(target.x, target.y, 42, 5)
            for z in hits:
                z.take_damage(max(3, damage // 2), C.POISON)
                z.slow = max(z.slow, FPS)
            self.effects.append(Effect(target.x, target.y, C.POISON, "ring", 18, 34))
            return
        radius = 48 if kind == "legend_storm" else 34
        hits = self.nearby_zombies(target.x, target.y, radius, 8)
        for z in hits:
            z.take_damage(max(4, damage // 3), C.ORANGE)
            z.burn = max(z.burn, FPS * 5)
        self.effects.append(Effect(target.x, target.y, C.ORANGE, "flame", 18, radius // 2))

    def area_hit(self, weapon, target, damage, kind):
        radius = 30
        if weapon.level >= 5 or kind in ("legend_cannon", "magic"):
            radius = 46
        if kind == "icecannon":
            radius = 38
        if kind == "legend_cannon":
            radius = 58
        if kind == "obelisk":
            radius = 42
        self.area_damage(target.x, target.y, radius, damage, self.projectile_color(kind))
        if kind in ("icecannon",):
            for z in self.nearby_zombies(target.x, target.y, radius, 2 if weapon.level >= 5 else 1):
                z.freeze = max(z.freeze, FPS * 2)
        if kind == "magic" and weapon.shots % 3 == 0:
            self.area_damage(target.x, target.y, 52, damage, C.PURPLE)
        if kind == "obelisk":
            for z in self.nearby_zombies(target.x, target.y, radius, 6):
                z.slow = max(z.slow, FPS)

    def chain_hit(self, weapon, target, damage, kind):
        chain = 2
        if weapon.level >= 5:
            chain = 5 if kind == "lightning" else 8
        if kind in ("legend_light", "legend_icebolt"):
            chain = 10
        hits = self.nearby_zombies(target.x, target.y, self.effective_range(weapon), chain)
        last_x, last_y = weapon.x, weapon.y
        for z in hits:
            z.take_damage(damage, C.GOLD)
            if kind in ("legend_icebolt",):
                z.freeze = max(z.freeze, FPS)
            self.projectiles.append(Projectile(last_x, last_y, z.x, z.y, C.GOLD, 8, 1, "bolt"))
            last_x, last_y = z.x, z.y
        self.effects.append(Effect(target.x, target.y, C.GOLD, "lightning", 14, 18))

    def area_damage(self, x, y, radius, damage, color):
        for z in self.nearby_zombies(x, y, radius):
            falloff = 1.0 - dist((x, y), (z.x, z.y)) / max(1, radius) * 0.35
            z.take_damage(max(1, int(damage * falloff)), color)
            if color == C.POISON:
                z.poison = max(z.poison, FPS * 8)
        self.effects.append(Effect(x, y, color, "ring", 20, radius))

    def update_visuals(self):
        for item in self.projectiles:
            item.update()
        for item in list(self.projectiles):
            if item.life <= 0:
                self.projectiles.remove(item)
        for item in self.drop_items:
            item.update()
        for item in list(self.drop_items):
            if item.life <= 0:
                self.drop_items.remove(item)
        for effect in self.effects:
            effect.update()
        for effect in list(self.effects):
            if effect.life <= 0:
                self.effects.remove(effect)
        for text in self.texts:
            text.update()
        for text in list(self.texts):
            if text.life <= 0:
                self.texts.remove(text)

    def draw(self):
        pyxel.cls(C.NIGHT)
        if self.screen == "boot":
            self.draw_boot_screen()
            return
        if self.screen == "tutorial":
            self.draw_tutorial_screen()
            return
        if self.screen == "menu":
            self.draw_main_menu()
            return
        if self.screen == "difficulty":
            self.draw_difficulty_select()
            return
        if self.screen == "loading":
            self.draw_loading_screen()
            return
        if self.screen == "cutscene":
            self.draw_cutscene()
            return
        self.draw_background()
        self.draw_play_area_frame()
        self.draw_path()
        self.draw_torches()
        self.draw_weather_overlay()
        self.draw_slots()
        self.draw_castle()
        self.draw_range_overlay()
        self.draw_weapons()
        self.draw_pets()
        self.draw_zombies()
        self.draw_drop_items()
        self.draw_projectiles()
        self.draw_effects()
        self.draw_top_ui()
        self.draw_bottom_ui()
        self.draw_floating_texts()
        if self.shop_open:
            self.draw_shop()
        if self.achievements_open:
            self.draw_achievements()
        if self.pet_shop_open:
            self.draw_pet_shop()
        if self.weapon_panel_open:
            self.draw_weapon_panel()
        if self.weapon_popup_open:
            self.draw_weapon_popup()
        if self.merge_overview_open:
            self.draw_merge_overview()
        if self.merge_confirm is not None:
            self.draw_merge_confirm()
        self.draw_hover_tooltip()
        if self.paused:
            self.draw_pause()
        if self.screen == "defeat":
            self.draw_game_over()

    def draw_background(self):
        for x, y, phase in self.stars:
            twinkle = (pyxel.frame_count // 18 + phase) % 4
            col = C.WHITE if twinkle == 0 else C.STONE_LIGHT
            pyxel.pset(x, y, col)
        style = MAPS[self.map_index]["style"]
        base = C.GRASS
        accent = C.GRASS_LIGHT
        if style in ("frost", "crystal"):
            base = C.SHADOW
            accent = C.ICE
        elif style in ("ember",):
            base = C.SHADOW
            accent = C.ORANGE
        elif style in ("shadow", "rune"):
            base = C.NIGHT
            accent = C.PURPLE
        elif style in ("sun", "royal", "wall"):
            base = C.STONE_DARK
            accent = C.GOLD
        weather = self.weather_name
        if weather == "Sonnig":
            accent = C.GOLD
        elif weather == "Vollmond":
            accent = C.ICE
        elif weather in ("Nacht", "Sturm"):
            base = C.NIGHT
        elif weather == "Nebel":
            accent = C.STONE_LIGHT
        elif weather == "Schnee":
            accent = C.WHITE
        pyxel.rect(0, 80, 512, 350, base)
        for x in range(-30, 560, 56):
            pyxel.circ(x + 12, 420, 58, C.SHADOW)
            pyxel.circ(x + 42, 416, 44, C.SHADOW)
        for x, y, col, kind in self.field_flora:
            if kind == 0:
                pyxel.line(x, y, x, y + 5, C.BROWN)
                pyxel.circ(x, y - 2, 4, col)
            elif kind == 1:
                pyxel.tri(x, y - 5, x - 5, y + 5, x + 5, y + 5, col)
                pyxel.line(x, y + 4, x, y + 8, C.BROWN)
            else:
                pyxel.rect(x - 3, y - 3, 7, 7, col)
                pyxel.rectb(x - 3, y - 3, 7, 7, C.STONE_DARK)
        for x, y, kind in self.trees:
            self.draw_tree(x, y, kind)

    def draw_weather_overlay(self):
        name = self.weather_name
        if name == "Regen":
            for i in range(34):
                x = (i * 37 + pyxel.frame_count * 3) % 512
                y = 86 + (i * 19 + pyxel.frame_count * 5) % 332
                pyxel.line(x, y, x - 2, y + 5, C.BLUE)
        elif name == "Sturm":
            for i in range(22):
                x = (i * 51 + pyxel.frame_count * 4) % 512
                y = 88 + (i * 23 + pyxel.frame_count * 6) % 330
                pyxel.line(x, y, x - 3, y + 6, C.BLUE)
            if pyxel.frame_count % 90 < 8:
                x = 80 + (pyxel.frame_count * 7) % 340
                pyxel.line(x, 86, x - 18, 136, C.GOLD)
                pyxel.line(x - 18, 136, x + 4, 134, C.WHITE)
                pyxel.line(x + 4, 134, x - 20, 190, C.GOLD)
        elif name == "Nacht":
            for i in range(18):
                x = (i * 29 + 13) % 512
                y = 92 + (i * 17) % 300
                if (pyxel.frame_count // 20 + i) % 3 == 0:
                    pyxel.pset(x, y, C.WHITE)
        elif name == "Nebel":
            for y in (124, 178, 238, 304, 370):
                shift = (pyxel.frame_count // 2 + y) % 90
                for x in range(-90, 540, 90):
                    pyxel.line(x + shift, y, x + shift + 54, y, C.STONE_LIGHT)
                    pyxel.line(x + shift + 16, y + 8, x + shift + 76, y + 8, C.SHADOW)
        elif name == "Vollmond":
            pyxel.circ(54, 108, 16, C.ICE)
            pyxel.circ(60, 104, 15, C.SHADOW)
        elif name == "Schnee":
            for i in range(36):
                x = (i * 41 + pyxel.frame_count) % 512
                y = 88 + (i * 27 + pyxel.frame_count * 2) % 330
                pyxel.pset(x, y, C.WHITE)
                pyxel.pset(x + 1, y, C.ICE)
                pyxel.pset(x, y + 1, C.ICE)
        elif name == "Sonnig":
            pyxel.circ(48, 110, 13, C.GOLD)
            pyxel.circb(48, 110, 18, C.GOLD)

    def draw_tree(self, x, y, kind):
        pyxel.rect(x + 7, y + 8, 3, 10, C.BROWN)
        if kind == 0:
            pyxel.circ(x + 8, y + 6, 8, C.GRASS_LIGHT)
            pyxel.circ(x + 4, y + 10, 6, C.GRASS)
            pyxel.circ(x + 12, y + 10, 6, C.GRASS)
        elif kind == 1:
            pyxel.tri(x + 8, y - 2, x, y + 12, x + 16, y + 12, C.GRASS_LIGHT)
            pyxel.tri(x + 8, y + 4, x + 1, y + 17, x + 15, y + 17, C.GRASS)
        else:
            pyxel.circ(x + 8, y + 8, 7, C.GRASS)
            pyxel.circb(x + 8, y + 8, 7, C.GRASS_LIGHT)

    def draw_play_area_frame(self):
        pyxel.rect(0, 0, 512, 80, C.NIGHT)
        pyxel.line(0, 79, 511, 79, C.STONE_DARK)
        pyxel.line(0, 430, 511, 430, C.STONE_DARK)
        pyxel.rect(0, 430, 512, 82, C.NIGHT)
        pyxel.rectb(3, 83, 506, 344, C.STONE_DARK)
        pyxel.rectb(4, 84, 504, 342, C.STONE_LIGHT)

    def draw_path(self):
        half = 16
        for i in range(len(PATH) - 1):
            ax, ay = PATH[i]
            bx, by = PATH[i + 1]
            if ax == bx:
                y = min(ay, by)
                h = abs(by - ay)
                pyxel.rect(ax - half - 4, y - half - 4, 40, h + 40, C.SHADOW)
            else:
                x = min(ax, bx)
                w = abs(bx - ax)
                pyxel.rect(x - half - 4, ay - half - 4, w + 40, 40, C.SHADOW)
        for i in range(len(PATH) - 1):
            ax, ay = PATH[i]
            bx, by = PATH[i + 1]
            if ax == bx:
                y = min(ay, by)
                h = abs(by - ay)
                pyxel.rect(ax - half, y - half, 32, h + 32, C.STONE_LIGHT)
                pyxel.rectb(ax - half, y - half, 32, h + 32, C.STONE_DARK)
                for ty in range(y - half, y + h + half, 16):
                    pyxel.line(ax - half + 2, ty, ax + half - 2, ty, C.STONE_DARK)
                    pyxel.pset(ax - 6, ty + 7, C.WHITE)
                    pyxel.pset(ax + 7, ty + 11, C.STONE_DARK)
                    pyxel.line(ax - half + 4, ty + 3, ax - half + 11, ty + 3, C.WHITE)
                    pyxel.line(ax + half - 5, ty + 12, ax + half - 13, ty + 12, C.STONE_DARK)
            else:
                x = min(ax, bx)
                w = abs(bx - ax)
                pyxel.rect(x - half, ay - half, w + 32, 32, C.STONE_LIGHT)
                pyxel.rectb(x - half, ay - half, w + 32, 32, C.STONE_DARK)
                for tx in range(x - half, x + w + half, 16):
                    pyxel.line(tx, ay - half + 2, tx, ay + half - 2, C.STONE_DARK)
                    pyxel.pset(tx + 6, ay - 5, C.WHITE)
                    pyxel.pset(tx + 10, ay + 8, C.STONE_DARK)
                    pyxel.line(tx + 3, ay - half + 4, tx + 11, ay - half + 4, C.WHITE)
                    pyxel.line(tx + 4, ay + half - 5, tx + 14, ay + half - 5, C.STONE_DARK)
        for x, y in PATH:
            pyxel.circ(x, y, 17, C.STONE_LIGHT)
            pyxel.circb(x, y, 17, C.STONE_DARK)
            pyxel.circ(x, y, 5, C.STONE_DARK)
            pyxel.pset(x - 2, y - 2, C.WHITE)
        for x, y, kind in self.pebbles:
            col = C.STONE_DARK if kind == 0 else C.WHITE if kind == 1 else C.STONE_LIGHT
            pyxel.pset(x, y, col)
            if kind == 2:
                pyxel.pset(x + 1, y, C.STONE_DARK)

    def draw_torches(self):
        for x, y in self.torches:
            flicker = (pyxel.frame_count // 6 + x + y) % 3
            pyxel.rect(x - 1, y - 2, 2, 12, C.BROWN)
            pyxel.rect(x - 3, y + 8, 6, 2, C.STONE_DARK)
            pyxel.tri(x - 4, y - 3, x, y - 12 - flicker, x + 4, y - 3, C.ORANGE)
            pyxel.tri(x - 2, y - 4, x, y - 9 - flicker, x + 2, y - 4, C.GOLD)
            if flicker == 0:
                pyxel.circb(x, y - 6, 8, C.ORANGE)

    def draw_slots(self):
        hover_slot = self.slot_at(pyxel.mouse_x, pyxel.mouse_y)
        for i, (x, y) in enumerate(SLOTS):
            occupied = self.weapon_in_slot(i) is not None
            if not occupied and (i + pyxel.frame_count // 20) % 9 == 0:
                pyxel.pset(x, y, C.GRASS_LIGHT)
            if not occupied and i == hover_slot and self.placement_ready:
                affordable = self.gold >= WEAPON_BY_ID[self.selected_shop_weapon]["cost"]
                col = C.POISON if affordable else C.RED
                pyxel.circb(x, y, 12, col)
                pyxel.circb(x, y, 7, C.WHITE)
                pyxel.line(x - 5, y, x + 5, y, col)
                pyxel.line(x, y - 5, x, y + 5, col)
            if i == self.selected_slot:
                pyxel.circb(x, y, 16, C.GOLD)
                pyxel.circb(x, y, 14, C.WHITE)
            if self.merge_source is not None and self.merge_source.slot_index == i:
                pyxel.circb(x, y, 18, C.PURPLE)

    def draw_slot_floor(self, x, y, slot_index):
        pyxel.line(x - 18, y - 14, x + 18, y - 14, C.STONE_DARK)
        pyxel.line(x - 18, y + 14, x + 18, y + 14, C.STONE_DARK)
        pyxel.line(x - 14, y - 18, x - 14, y + 18, C.STONE_DARK)
        pyxel.line(x + 14, y - 18, x + 14, y + 18, C.STONE_DARK)
        if slot_index % 2 == 0:
            pyxel.pset(x - 17, y - 17, C.WHITE)
            pyxel.pset(x + 16, y + 16, C.STONE_DARK)
        else:
            pyxel.pset(x + 17, y - 17, C.WHITE)
            pyxel.pset(x - 16, y + 16, C.STONE_DARK)

    def draw_range_overlay(self):
        weapon = self.selected_weapon()
        if weapon is not None and self.show_range:
            r = self.effective_range(weapon)
            pyxel.circb(weapon.x, weapon.y, r, C.GOLD)
            pyxel.circb(weapon.x, weapon.y, max(1, r - 1), C.STONE_LIGHT)
        if self.merge_mode and self.merge_source is not None:
            self.draw_merge_candidates()

    def draw_merge_candidates(self):
        source = self.merge_source
        pyxel.circb(source.x, source.y, 31, C.PURPLE)
        for weapon in self.weapons:
            if weapon is source:
                continue
            key = frozenset([source.spec_id, weapon.spec_id])
            if weapon.level >= 5 and key in MERGES:
                cost, result_id = MERGES[key]
                col = C.GOLD if self.gold >= cost else C.RED
                pyxel.line(source.x, source.y, weapon.x, weapon.y, col)
                pyxel.circb(weapon.x, weapon.y, 31, col)
                pyxel.text(weapon.x - 16, weapon.y - 33, WEAPON_BY_ID[result_id]["name"][:8], col)
            elif weapon.level >= 5:
                pyxel.circb(weapon.x, weapon.y, 27, C.STONE_DARK)

    def draw_weapons(self):
        for weapon in self.weapons:
            if weapon.pulse > 0 or weapon.level >= 5 or weapon.spec_id >= 100:
                col = C.GOLD if weapon.level >= 5 else C.POISON
                rad = 13 + (pyxel.frame_count // 6) % 3
                pyxel.circb(weapon.x, weapon.y, rad, col)
            self.draw_weapon_field(weapon.x, weapon.y, weapon.spec_id, weapon.level)
            if self.can_weapon_merge(weapon):
                self.draw_merge_badge(weapon.x, weapon.y - 19)
            if weapon.ability_ready():
                self.draw_ability_badge(weapon.x + 13, weapon.y - 12)

    def draw_weapon_field(self, cx, cy, spec_id, level):
        pyxel.circ(cx, cy + 8, 9, C.SHADOW)
        pyxel.rect(cx - 7, cy + 5, 14, 5, C.STONE_DARK)
        pyxel.rectb(cx - 7, cy + 5, 14, 5, C.STONE_LIGHT)
        self.draw_weapon_icon(cx - 8, cy - 12, spec_id, level, 1)
        start = cx - 7
        for i in range(5):
            col = C.GOLD if i < level else C.NIGHT
            pyxel.pset(start + i * 3, cy + 13, col)
            pyxel.pset(start + i * 3 + 1, cy + 13, col)

    def draw_merge_badge(self, x, y):
        pyxel.circ(x, y, 6, C.PURPLE)
        pyxel.circb(x, y, 6, C.WHITE)
        pyxel.text(x - 3, y - 3, "M", C.WHITE)

    def draw_ability_badge(self, x, y):
        pyxel.circ(x, y, 5, C.GOLD)
        pyxel.circb(x, y, 5, C.WHITE)
        pyxel.pset(x, y - 2, C.NIGHT)
        pyxel.line(x - 2, y + 1, x + 2, y + 1, C.NIGHT)

    def draw_weapon_large(self, cx, cy, spec_id, level):
        spec = WEAPON_BY_ID[spec_id]
        kind = spec["kind"]
        pyxel.elli(cx - 11, cy + 11, 22, 6, C.SHADOW) if hasattr(pyxel, "elli") else pyxel.rect(cx - 11, cy + 10, 22, 4, C.SHADOW)
        pyxel.line(cx - 9, cy + 13, cx + 9, cy + 13, C.NIGHT)
        if spec_id >= 100:
            self.draw_legend_large(cx, cy, kind, level)
            self.draw_level_gems(cx, cy, level)
            return
        if kind == "blade":
            blade = C.GOLD if level >= 5 else C.STONE_LIGHT
            pyxel.rect(cx - 3, cy + 3, 6, 14, C.BROWN)
            pyxel.rect(cx - 11, cy + 1, 22, 4, C.STONE_DARK)
            pyxel.line(cx, cy + 1, cx + 12, cy - 16, blade)
            pyxel.line(cx + 2, cy + 1, cx + 14, cy - 14, C.WHITE)
            pyxel.line(cx - 2, cy + 1, cx + 10, cy - 17, C.STONE_DARK)
            if level >= 5:
                pyxel.line(cx - 12, cy - 12, cx + 11, cy + 11, C.GOLD)
                pyxel.line(cx - 8, cy - 16, cx + 15, cy + 7, C.WHITE)
        elif kind == "spear":
            pyxel.rect(cx - 2, cy - 15, 4, 31, C.BROWN)
            pyxel.tri(cx, cy - 23, cx - 8, cy - 11, cx + 8, cy - 11, C.GOLD if level >= 5 else C.STONE_LIGHT)
            pyxel.line(cx - 5, cy - 9, cx + 5, cy - 9, C.STONE_DARK)
            pyxel.pset(cx - 1, cy - 1, C.WHITE)
            pyxel.pset(cx + 1, cy + 7, C.STONE_DARK)
        elif kind == "arrow":
            pyxel.circb(cx - 2, cy, 14, C.GOLD if level >= 5 else C.BROWN)
            pyxel.circb(cx - 1, cy, 11, C.BROWN)
            pyxel.line(cx + 3, cy - 15, cx + 3, cy + 15, C.WHITE)
            pyxel.line(cx - 15, cy, cx + 15, cy, C.BROWN)
            pyxel.tri(cx + 16, cy, cx + 8, cy - 4, cx + 8, cy + 4, C.STONE_LIGHT)
            if level >= 5:
                pyxel.line(cx - 12, cy - 6, cx + 14, cy - 6, C.GOLD)
                pyxel.line(cx - 12, cy + 6, cx + 14, cy + 6, C.GOLD)
        elif kind == "axe":
            pyxel.line(cx - 4, cy + 16, cx + 5, cy - 12, C.BROWN)
            pyxel.line(cx - 2, cy + 16, cx + 7, cy - 12, C.DARK_BROWN)
            head = C.RED if level >= 5 else C.STONE_LIGHT
            pyxel.rect(cx + 2, cy - 17, 13, 11, head)
            pyxel.tri(cx + 2, cy - 17, cx - 11, cy - 11, cx + 2, cy - 4, C.WHITE)
            pyxel.tri(cx + 13, cy - 16, cx + 20, cy - 9, cx + 13, cy - 3, C.STONE_DARK)
        elif kind == "ice":
            pyxel.line(cx - 13, cy + 13, cx + 13, cy - 13, C.WHITE)
            pyxel.line(cx - 10, cy + 14, cx + 14, cy - 10, C.BLUE)
            pyxel.tri(cx + 14, cy - 14, cx + 5, cy - 14, cx + 14, cy - 4, C.ICE)
            for ox, oy in [(-9, -8), (0, -17), (8, 5), (-2, 9)]:
                self.draw_crystal(cx + ox, cy + oy, C.ICE)
        elif kind == "fire":
            pyxel.line(cx - 13, cy + 13, cx + 13, cy - 13, C.SHADOW)
            pyxel.tri(cx + 15, cy - 15, cx + 4, cy - 14, cx + 14, cy - 3, C.ORANGE)
            pyxel.tri(cx + 9, cy - 17, cx - 1, cy + 1, cx + 14, cy + 2, C.RED)
            pyxel.tri(cx + 7, cy - 10, cx + 2, cy, cx + 11, cy, C.GOLD)
        elif kind == "stone":
            pyxel.rect(cx - 13, cy + 8, 24, 8, C.BROWN)
            pyxel.circ(cx - 9, cy + 17, 4, C.NIGHT)
            pyxel.circ(cx + 9, cy + 17, 4, C.NIGHT)
            pyxel.line(cx - 5, cy + 8, cx + 12, cy - 13, C.DARK_BROWN)
            pyxel.line(cx - 3, cy + 9, cx + 14, cy - 12, C.BROWN)
            pyxel.circ(cx + 14, cy - 15, 6, C.STONE_DARK)
            pyxel.pset(cx + 12, cy - 17, C.WHITE)
        elif kind == "lightning":
            pyxel.rect(cx - 2, cy - 16, 4, 32, C.GOLD)
            pyxel.circ(cx, cy - 17, 6, C.BLUE)
            pyxel.circb(cx, cy - 17, 6, C.WHITE)
            self.draw_bolt(cx - 14, cy - 13, C.GOLD)
            self.draw_bolt(cx + 7, cy - 10, C.ICE)
        elif kind == "poison":
            pyxel.rect(cx - 9, cy - 12, 18, 25, C.GRASS)
            pyxel.rectb(cx - 9, cy - 12, 18, 25, C.POISON)
            pyxel.rect(cx - 4, cy - 6, 8, 5, C.GOLD)
            for i in range(4):
                bx = cx - 12 + i * 8
                by = cy - 14 - ((pyxel.frame_count // 7 + i) % 7)
                pyxel.circ(bx, by, 3, C.POISON)
                pyxel.circb(bx, by, 3, C.WHITE)
        elif kind == "cannon":
            pyxel.rect(cx - 13, cy + 5, 18, 9, C.STONE_LIGHT)
            pyxel.rect(cx - 1, cy - 1, 23, 8, C.STONE_DARK)
            pyxel.rectb(cx - 1, cy - 1, 23, 8, C.WHITE)
            pyxel.circ(cx - 9, cy + 15, 4, C.NIGHT)
            pyxel.circ(cx + 9, cy + 15, 4, C.NIGHT)
            pyxel.pset(cx + 22, cy + 1, C.RED)
        elif kind == "flame":
            pyxel.circ(cx - 9, cy + 7, 8, C.RED)
            pyxel.rectb(cx - 17, cy - 1, 16, 16, C.STONE_DARK)
            pyxel.rect(cx - 1, cy - 5, 17, 7, C.STONE_LIGHT)
            pyxel.rectb(cx - 1, cy - 5, 17, 7, C.WHITE)
            pyxel.tri(cx + 16, cy - 11, cx + 28, cy - 2, cx + 16, cy + 8, C.ORANGE)
            pyxel.tri(cx + 14, cy - 6, cx + 22, cy - 1, cx + 14, cy + 4, C.GOLD)
        elif kind == "magic":
            pyxel.rect(cx - 9, cy + 12, 18, 5, C.GOLD)
            pyxel.circ(cx, cy - 2, 13, C.ICE)
            pyxel.circb(cx, cy - 2, 13, C.PURPLE)
            pyxel.circb(cx, cy - 2, 9, C.WHITE)
            pyxel.line(cx - 16, cy - 17, cx + 16, cy + 13, C.PURPLE)
            pyxel.line(cx + 13, cy - 15, cx - 14, cy + 15, C.PURPLE)
        elif kind == "icecannon":
            pyxel.rect(cx - 12, cy + 5, 18, 9, C.WHITE)
            pyxel.rect(cx - 1, cy - 3, 23, 8, C.ICE)
            pyxel.rectb(cx - 1, cy - 3, 23, 8, C.WHITE)
            pyxel.circ(cx - 8, cy + 15, 4, C.WHITE)
            pyxel.circ(cx + 8, cy + 15, 4, C.WHITE)
            self.draw_crystal(cx + 12, cy - 13, C.BLUE)
            self.draw_crystal(cx - 13, cy - 8, C.ICE)
        elif kind == "tesla":
            pyxel.rect(cx - 6, cy - 15, 12, 30, C.BLUE)
            pyxel.rectb(cx - 6, cy - 15, 12, 30, C.WHITE)
            pyxel.tri(cx, cy - 24, cx - 10, cy - 13, cx + 10, cy - 13, C.GOLD)
            self.draw_bolt(cx - 18, cy - 13, C.ICE)
            self.draw_bolt(cx + 10, cy - 9, C.GOLD)
            pyxel.circb(cx, cy - 12, 18, C.BLUE)
        elif kind == "necro":
            pyxel.rect(cx - 2, cy - 16, 4, 32, C.PURPLE)
            pyxel.circ(cx, cy - 17, 8, C.WHITE)
            pyxel.pset(cx - 3, cy - 19, C.NIGHT)
            pyxel.pset(cx + 3, cy - 19, C.NIGHT)
            pyxel.line(cx - 3, cy - 14, cx + 3, cy - 14, C.NIGHT)
            pyxel.circb(cx, cy - 17, 12, C.POISON)
        elif kind == "dragon":
            pyxel.rect(cx - 2, cy - 17, 4, 34, C.GOLD)
            pyxel.tri(cx, cy - 25, cx - 12, cy - 11, cx + 12, cy - 11, C.ORANGE)
            pyxel.tri(cx - 5, cy - 17, cx - 17, cy - 7, cx - 4, cy - 8, C.RED)
            pyxel.tri(cx + 5, cy - 17, cx + 17, cy - 7, cx + 4, cy - 8, C.RED)
            pyxel.pset(cx, cy - 20, C.WHITE)
        elif kind == "storm":
            pyxel.circb(cx - 2, cy, 16, C.WHITE)
            pyxel.circb(cx - 1, cy, 12, C.BLUE)
            pyxel.line(cx + 5, cy - 17, cx + 5, cy + 17, C.ICE)
            pyxel.line(cx - 17, cy - 7, cx + 19, cy - 13, C.BLUE)
            pyxel.line(cx - 17, cy + 7, cx + 19, cy + 13, C.BLUE)
            pyxel.circb(cx, cy, 20, C.ICE)
        elif kind == "shadow":
            pyxel.circb(cx, cy, 17, C.PURPLE)
            pyxel.line(cx - 15, cy + 12, cx + 14, cy - 15, C.NIGHT)
            pyxel.line(cx - 13, cy + 14, cx + 16, cy - 13, C.RED)
            pyxel.rect(cx - 18, cy + 12, 10, 4, C.DARK_BROWN)
            if level >= 5:
                pyxel.circb(cx, cy, 21, C.RED)
        elif kind == "divine":
            pyxel.rect(cx - 9, cy - 20, 18, 15, C.GOLD)
            pyxel.rectb(cx - 9, cy - 20, 18, 15, C.WHITE)
            pyxel.rect(cx - 3, cy - 5, 6, 24, C.WHITE)
            pyxel.circb(cx, cy - 4, 22, C.WHITE)
            pyxel.pset(cx, cy - 25, C.WHITE)
        self.draw_level_gems(cx, cy, level)

    def draw_legend_large(self, cx, cy, kind, level):
        pulse = (pyxel.frame_count // 5) % 4
        pyxel.circb(cx, cy, 22 + pulse, C.GOLD)
        pyxel.circb(cx, cy, 17 + pulse, C.WHITE)
        if kind == "legend_blade":
            pyxel.line(cx - 16, cy + 14, cx + 15, cy - 18, C.GOLD)
            pyxel.line(cx - 9, cy + 16, cx + 18, cy - 10, C.WHITE)
            pyxel.line(cx - 18, cy - 1, cx + 8, cy - 23, C.GOLD)
        elif kind == "legend_axe":
            pyxel.line(cx - 6, cy + 17, cx + 8, cy - 17, C.BROWN)
            pyxel.circ(cx + 7, cy - 11, 11, C.STONE_LIGHT)
            pyxel.line(cx - 15, cy - 5, cx + 19, cy - 17, C.GOLD)
        elif kind == "legend_firebow":
            pyxel.circb(cx - 3, cy, 18, C.ORANGE)
            pyxel.line(cx + 7, cy - 19, cx + 7, cy + 19, C.GOLD)
            pyxel.tri(cx + 16, cy, cx - 2, cy - 10, cx - 2, cy + 10, C.RED)
        elif kind == "legend_icebolt":
            self.draw_bolt(cx - 13, cy - 18, C.ICE)
            self.draw_bolt(cx + 4, cy - 16, C.GOLD)
            self.draw_crystal(cx + 4, cy + 4, C.WHITE)
            self.draw_crystal(cx - 6, cy - 3, C.ICE)
        elif kind == "legend_cannon":
            pyxel.rect(cx - 16, cy + 2, 30, 12, C.GOLD)
            pyxel.rect(cx + 7, cy - 7, 20, 10, C.ORANGE)
            pyxel.circ(cx + 28, cy - 2, 8, C.RED)
            pyxel.circ(cx - 10, cy + 16, 5, C.NIGHT)
            pyxel.circ(cx + 11, cy + 16, 5, C.NIGHT)
        elif kind == "legend_poison":
            pyxel.circ(cx, cy, 17, C.PURPLE)
            pyxel.circb(cx, cy, 17, C.POISON)
            pyxel.circ(cx, cy, 8, C.POISON)
            pyxel.circb(cx, cy, 22, C.PURPLE)
        elif kind == "legend_storm":
            pyxel.circb(cx, cy, 18, C.BLUE)
            pyxel.line(cx - 19, cy - 6, cx + 21, cy - 15, C.ORANGE)
            pyxel.line(cx - 20, cy + 6, cx + 22, cy + 14, C.GOLD)
            pyxel.circb(cx, cy, 24, C.ICE)
        elif kind == "legend_frost":
            pyxel.line(cx - 16, cy + 15, cx + 16, cy - 16, C.SHADOW)
            pyxel.line(cx - 14, cy + 17, cx + 18, cy - 14, C.PURPLE)
            self.draw_crystal(cx - 9, cy - 8, C.ICE)
            self.draw_crystal(cx + 10, cy + 6, C.PURPLE)
        elif kind == "legend_necro":
            pyxel.rect(cx - 3, cy - 18, 6, 35, C.PURPLE)
            pyxel.circ(cx, cy - 17, 10, C.GOLD)
            pyxel.pset(cx - 4, cy - 20, C.NIGHT)
            pyxel.pset(cx + 4, cy - 20, C.NIGHT)
            pyxel.circb(cx, cy - 17, 17, C.POISON)
        elif kind == "legend_light":
            self.draw_bolt(cx - 18, cy - 18, C.WHITE)
            self.draw_bolt(cx - 2, cy - 23, C.GOLD)
            self.draw_bolt(cx + 12, cy - 14, C.ICE)
            pyxel.circb(cx, cy, 24, C.GOLD)
        elif kind == "legend_spear":
            pyxel.rect(cx - 2, cy - 20, 4, 38, C.GOLD)
            pyxel.tri(cx, cy - 29, cx - 15, cy - 10, cx + 15, cy - 10, C.WHITE)
            pyxel.tri(cx, cy - 24, cx - 10, cy - 7, cx + 10, cy - 7, C.ORANGE)

    def draw_level_gems(self, cx, cy, level):
        start = cx - 10
        for i in range(5):
            col = C.GOLD if i < level else C.STONE_DARK
            pyxel.rect(start + i * 5, cy + 21, 3, 3, col)
            pyxel.pset(start + i * 5 + 1, cy + 21, C.WHITE if i < level else C.NIGHT)

    def draw_weapon_icon(self, x, y, spec_id, level=1, scale=1):
        spec = WEAPON_BY_ID[spec_id]
        kind = spec["kind"]
        if spec_id >= 100:
            self.draw_legend_icon(x, y, kind, level)
            return
        if kind == "blade":
            blade = C.GOLD if level >= 5 else C.STONE_LIGHT
            pyxel.rect(x + 7, y + 9, 3, 6, C.BROWN)
            pyxel.rect(x + 5, y + 8, 7, 1, C.STONE_DARK)
            pyxel.line(x + 8, y + 8, x + 13, y + 2, blade)
            pyxel.line(x + 9, y + 8, x + 14, y + 3, C.WHITE)
            if level >= 5:
                pyxel.line(x + 4, y + 4, x + 12, y + 12, C.GOLD)
        elif kind == "spear":
            tip = C.GOLD if level >= 5 else C.STONE_LIGHT
            pyxel.line(x + 8, y + 14, x + 8, y + 3, C.BROWN)
            pyxel.tri(x + 8, y, x + 4, y + 5, x + 12, y + 5, tip)
            pyxel.pset(x + 6, y + 9, C.STONE_DARK)
            pyxel.pset(x + 10, y + 11, C.STONE_DARK)
        elif kind == "arrow":
            glow = C.GOLD if level >= 5 else C.BROWN
            pyxel.circb(x + 7, y + 8, 6, glow)
            pyxel.line(x + 9, y + 2, x + 9, y + 14, C.WHITE)
            pyxel.line(x + 1, y + 8, x + 13, y + 8, C.BROWN)
            pyxel.tri(x + 13, y + 8, x + 10, y + 6, x + 10, y + 10, C.STONE_LIGHT)
            if level >= 5:
                pyxel.line(x + 2, y + 5, x + 14, y + 5, C.GOLD)
                pyxel.line(x + 2, y + 11, x + 14, y + 11, C.GOLD)
        elif kind == "axe":
            head = C.RED if level >= 5 else C.STONE_LIGHT
            pyxel.line(x + 6, y + 14, x + 9, y + 4, C.BROWN)
            pyxel.rect(x + 7, y + 2, 7, 6, head)
            pyxel.tri(x + 7, y + 2, x + 2, y + 5, x + 7, y + 8, C.WHITE)
        elif kind == "ice":
            pyxel.line(x + 2, y + 13, x + 13, y + 2, C.WHITE)
            pyxel.tri(x + 13, y + 2, x + 9, y + 2, x + 13, y + 6, C.ICE)
            self.draw_crystal(x + 4, y + 3, C.BLUE)
            self.draw_crystal(x + 10, y + 10, C.ICE)
        elif kind == "fire":
            pyxel.line(x + 2, y + 13, x + 13, y + 2, C.SHADOW)
            pyxel.tri(x + 13, y + 2, x + 9, y + 3, x + 13, y + 7, C.ORANGE)
            pyxel.tri(x + 9, y + 2, x + 6, y + 8, x + 12, y + 8, C.RED)
            pyxel.pset(x + 10, y + 5, C.GOLD)
        elif kind == "stone":
            pyxel.rect(x + 2, y + 11, 10, 3, C.BROWN)
            pyxel.circ(x + 3, y + 14, 2, C.NIGHT)
            pyxel.circ(x + 12, y + 14, 2, C.NIGHT)
            pyxel.line(x + 5, y + 11, x + 12, y + 4, C.DARK_BROWN)
            pyxel.circ(x + 12, y + 3, 3, C.STONE_DARK)
        elif kind == "lightning":
            pyxel.line(x + 8, y + 14, x + 8, y + 3, C.GOLD)
            pyxel.circ(x + 8, y + 3, 3, C.BLUE)
            self.draw_bolt(x + 3, y + 2, C.GOLD)
            self.draw_bolt(x + 10, y + 5, C.ICE)
        elif kind == "poison":
            pyxel.rect(x + 4, y + 4, 8, 10, C.GRASS)
            pyxel.rectb(x + 4, y + 4, 8, 10, C.POISON)
            pyxel.rect(x + 7, y + 6, 2, 2, C.GOLD)
            pyxel.circ(x + 3, y + 4 + (pyxel.frame_count // 8) % 4, 2, C.POISON)
        elif kind == "cannon":
            pyxel.rect(x + 5, y + 7, 9, 4, C.STONE_DARK)
            pyxel.rect(x + 2, y + 9, 6, 4, C.STONE_LIGHT)
            pyxel.circ(x + 4, y + 13, 2, C.NIGHT)
            pyxel.circ(x + 12, y + 13, 2, C.NIGHT)
            pyxel.pset(x + 14, y + 8, C.RED)
        elif kind == "flame":
            pyxel.circ(x + 5, y + 10, 4, C.RED)
            pyxel.rect(x + 8, y + 6, 6, 3, C.STONE_LIGHT)
            pyxel.tri(x + 14, y + 4, x + 14, y + 12, x + 9, y + 8, C.ORANGE)
            pyxel.pset(x + 13, y + 8, C.GOLD)
        elif kind == "magic":
            pyxel.rect(x + 5, y + 13, 7, 2, C.GOLD)
            pyxel.circ(x + 8, y + 7, 6, C.ICE)
            pyxel.circb(x + 8, y + 7, 6, C.PURPLE)
            pyxel.line(x + 2, y + 1, x + 14, y + 13, C.PURPLE)
        elif kind == "icecannon":
            pyxel.rect(x + 5, y + 6, 9, 4, C.ICE)
            pyxel.rect(x + 2, y + 9, 6, 4, C.WHITE)
            self.draw_crystal(x + 10, y + 1, C.BLUE)
            self.draw_crystal(x + 4, y + 3, C.ICE)
        elif kind == "tesla":
            pyxel.rect(x + 6, y + 3, 5, 12, C.BLUE)
            pyxel.tri(x + 8, y, x + 4, y + 4, x + 12, y + 4, C.GOLD)
            self.draw_bolt(x + 1, y + 3, C.ICE)
            self.draw_bolt(x + 11, y + 6, C.GOLD)
        elif kind == "necro":
            pyxel.line(x + 8, y + 14, x + 8, y + 3, C.PURPLE)
            pyxel.circ(x + 8, y + 4, 4, C.WHITE)
            pyxel.pset(x + 6, y + 3, C.NIGHT)
            pyxel.pset(x + 10, y + 3, C.NIGHT)
            pyxel.circb(x + 8, y + 4, 6, C.POISON)
        elif kind == "dragon":
            pyxel.line(x + 8, y + 14, x + 8, y + 3, C.GOLD)
            pyxel.tri(x + 8, y, x + 3, y + 6, x + 13, y + 6, C.ORANGE)
            pyxel.line(x + 5, y + 8, x + 12, y + 11, C.RED)
        elif kind == "storm":
            pyxel.circb(x + 7, y + 8, 7, C.WHITE)
            pyxel.line(x + 10, y + 1, x + 10, y + 15, C.ICE)
            pyxel.line(x + 2, y + 5, x + 14, y + 2, C.BLUE)
            pyxel.line(x + 2, y + 11, x + 14, y + 14, C.BLUE)
        elif kind == "shadow":
            pyxel.line(x + 3, y + 12, x + 13, y + 3, C.NIGHT)
            pyxel.line(x + 4, y + 13, x + 14, y + 4, C.RED)
            pyxel.rect(x + 1, y + 12, 5, 2, C.DARK_BROWN)
            if level >= 5:
                pyxel.circb(x + 8, y + 8, 7, C.RED)
        elif kind == "rune":
            pyxel.line(x + 8, y + 14, x + 8, y + 3, C.PURPLE)
            pyxel.rectb(x + 4, y + 4, 8, 8, C.GOLD)
            pyxel.line(x + 4, y + 12, x + 12, y + 4, C.WHITE)
        elif kind == "beam":
            pyxel.circ(x + 8, y + 8, 6, C.GOLD)
            pyxel.circb(x + 8, y + 8, 7, C.WHITE)
            pyxel.line(x + 8, y + 1, x + 8, y + 15, C.GOLD)
            pyxel.line(x + 1, y + 8, x + 15, y + 8, C.WHITE)
        elif kind == "root":
            pyxel.line(x + 8, y + 14, x + 8, y + 5, C.BROWN)
            pyxel.line(x + 8, y + 10, x + 3, y + 14, C.POISON)
            pyxel.line(x + 8, y + 9, x + 13, y + 14, C.POISON)
            pyxel.circ(x + 8, y + 4, 4, C.POISON)
            pyxel.pset(x + 8, y + 2, C.WHITE)
        elif kind == "star":
            pyxel.line(x + 8, y + 1, x + 8, y + 15, C.GOLD)
            pyxel.line(x + 1, y + 8, x + 15, y + 8, C.GOLD)
            pyxel.line(x + 3, y + 3, x + 13, y + 13, C.WHITE)
            pyxel.line(x + 13, y + 3, x + 3, y + 13, C.WHITE)
        elif kind == "obelisk":
            pyxel.tri(x + 8, y, x + 3, y + 14, x + 13, y + 14, C.PURPLE)
            pyxel.rectb(x + 4, y + 5, 9, 9, C.STONE_DARK)
            pyxel.line(x + 8, y + 3, x + 8, y + 12, C.WHITE)
        elif kind == "divine":
            pyxel.rect(x + 5, y + 2, 8, 7, C.GOLD)
            pyxel.line(x + 8, y + 9, x + 8, y + 15, C.WHITE)
            pyxel.circb(x + 8, y + 8, 8, C.WHITE)
            pyxel.pset(x + 8, y, C.WHITE)

    def draw_legend_icon(self, x, y, kind, level):
        pulse = (pyxel.frame_count // 5) % 3
        pyxel.circb(x + 8, y + 8, 8 + pulse, C.GOLD)
        if kind == "legend_blade":
            pyxel.line(x + 2, y + 13, x + 14, y + 1, C.GOLD)
            pyxel.line(x + 5, y + 14, x + 15, y + 4, C.WHITE)
            pyxel.line(x + 1, y + 9, x + 10, y, C.GOLD)
        elif kind == "legend_axe":
            pyxel.line(x + 5, y + 15, x + 10, y + 2, C.BROWN)
            pyxel.circ(x + 10, y + 5, 5, C.STONE_LIGHT)
            pyxel.line(x + 2, y + 9, x + 14, y + 2, C.GOLD)
        elif kind == "legend_firebow":
            pyxel.circb(x + 7, y + 8, 7, C.ORANGE)
            pyxel.line(x + 10, y + 1, x + 10, y + 15, C.GOLD)
            pyxel.tri(x + 12, y + 8, x + 7, y + 4, x + 7, y + 12, C.RED)
        elif kind == "legend_icebolt":
            self.draw_bolt(x + 3, y + 2, C.ICE)
            self.draw_crystal(x + 9, y + 8, C.WHITE)
            pyxel.line(x + 2, y + 14, x + 14, y + 2, C.GOLD)
        elif kind == "legend_cannon":
            pyxel.rect(x + 2, y + 8, 12, 5, C.GOLD)
            pyxel.circ(x + 13, y + 8, 4, C.ORANGE)
            pyxel.circ(x + 4, y + 14, 2, C.NIGHT)
            pyxel.circ(x + 12, y + 14, 2, C.NIGHT)
        elif kind == "legend_poison":
            pyxel.circ(x + 8, y + 8, 7, C.PURPLE)
            pyxel.circb(x + 8, y + 8, 7, C.POISON)
            pyxel.circ(x + 8, y + 8, 3, C.POISON)
        elif kind == "legend_storm":
            pyxel.circb(x + 8, y + 8, 7, C.BLUE)
            pyxel.line(x + 1, y + 7, x + 15, y + 4, C.ORANGE)
            pyxel.line(x + 1, y + 11, x + 15, y + 13, C.GOLD)
        elif kind == "legend_frost":
            pyxel.line(x + 2, y + 13, x + 13, y + 2, C.SHADOW)
            self.draw_crystal(x + 3, y + 3, C.ICE)
            self.draw_crystal(x + 10, y + 9, C.PURPLE)
        elif kind == "legend_necro":
            pyxel.line(x + 8, y + 15, x + 8, y + 2, C.PURPLE)
            pyxel.circ(x + 8, y + 4, 5, C.GOLD)
            pyxel.circb(x + 8, y + 4, 7, C.POISON)
        elif kind == "legend_light":
            self.draw_bolt(x + 2, y + 1, C.WHITE)
            self.draw_bolt(x + 7, y + 0, C.GOLD)
            self.draw_bolt(x + 11, y + 3, C.ICE)
        elif kind == "legend_spear":
            pyxel.line(x + 8, y + 15, x + 8, y + 2, C.GOLD)
            pyxel.tri(x + 8, y, x + 2, y + 7, x + 14, y + 7, C.WHITE)
            pyxel.tri(x + 8, y + 2, x + 4, y + 8, x + 12, y + 8, C.ORANGE)

    def draw_crystal(self, x, y, color):
        pyxel.pset(x, y - 2, color)
        pyxel.line(x - 2, y, x, y - 2, color)
        pyxel.line(x, y - 2, x + 2, y, C.WHITE)
        pyxel.line(x - 2, y, x, y + 3, color)
        pyxel.line(x + 2, y, x, y + 3, C.BLUE)

    def draw_bolt(self, x, y, color):
        pyxel.line(x + 3, y, x, y + 6, color)
        pyxel.line(x, y + 6, x + 4, y + 6, color)
        pyxel.line(x + 4, y + 6, x + 1, y + 13, color)

    def draw_drop_items(self):
        for item in self.drop_items:
            x = int(item.x)
            y = int(item.y + math.sin(item.pulse / 8) * 2)
            pyxel.circb(x, y, 8, item.color)
            if item.kind == "gold":
                self.draw_coin(x - 4, y - 4)
            elif item.kind == "heal":
                self.draw_heart(x - 4, y - 4)
            elif item.kind == "soul":
                self.draw_soul(x - 4, y - 4)
            elif item.kind == "freeze":
                self.draw_crystal(x, y, C.ICE)
            elif item.kind == "burn":
                pyxel.tri(x - 5, y + 5, x, y - 7, x + 5, y + 5, C.ORANGE)
                pyxel.tri(x - 2, y + 3, x, y - 4, x + 2, y + 3, C.GOLD)
            elif item.kind == "speed":
                pyxel.line(x - 6, y + 4, x + 4, y - 6, C.BLUE)
                pyxel.tri(x + 5, y - 7, x + 1, y - 2, x + 8, y - 1, C.WHITE)
            elif item.kind == "damage":
                pyxel.line(x - 5, y + 5, x + 5, y - 5, C.POISON)
                pyxel.line(x - 2, y - 5, x + 5, y - 5, C.WHITE)

    def draw_pets(self):
        for pet in sorted(self.pets, key=lambda p: p.y):
            self.draw_pet(int(pet.x), int(pet.y), pet)

    def draw_pet(self, x, y, pet):
        pid = pet.pet_id
        col = pet.spec["color"]
        pyxel.line(x - 6, y + 8, x + 7, y + 8, C.SHADOW)
        if pid == 1:
            pyxel.circ(x, y, 6, col)
            pyxel.tri(x - 6, y - 3, x - 12, y - 9, x - 6, y + 3, C.BROWN)
            pyxel.tri(x + 6, y - 3, x + 12, y - 9, x + 6, y + 3, C.BROWN)
            pyxel.circ(x - 2, y - 2, 2, C.WHITE)
            pyxel.circ(x + 2, y - 2, 2, C.WHITE)
            pyxel.pset(x - 2, y - 2, C.NIGHT)
            pyxel.pset(x + 2, y - 2, C.NIGHT)
        elif pid == 2:
            pyxel.tri(x - 7, y - 5, x - 2, y - 12, x, y - 4, col)
            pyxel.tri(x + 7, y - 5, x + 2, y - 12, x, y - 4, col)
            pyxel.rect(x - 7, y - 5, 14, 10, col)
            pyxel.tri(x + 7, y + 1, x + 16, y - 5, x + 12, y + 5, C.WHITE)
            pyxel.pset(x - 3, y - 2, C.NIGHT)
        elif pid == 3:
            pyxel.rect(x - 8, y - 4, 16, 11, col)
            pyxel.tri(x - 7, y - 4, x - 4, y - 12, x, y - 4, col)
            pyxel.tri(x + 7, y - 4, x + 4, y - 12, x, y - 4, col)
            pyxel.line(x - 9, y + 3, x - 13, y + 8, C.STONE_DARK)
            pyxel.line(x + 9, y + 3, x + 13, y + 8, C.STONE_DARK)
        elif pid == 4:
            pyxel.circ(x, y, 8, col)
            pyxel.circ(x - 5, y - 6, 3, col)
            pyxel.circ(x + 5, y - 6, 3, col)
            pyxel.rect(x - 5, y + 3, 10, 5, C.DARK_BROWN)
        elif pid == 5:
            pyxel.circ(x, y, 7, C.WHITE)
            pyxel.tri(x, y - 7, x - 3, y - 15, x + 3, y - 7, C.GOLD)
            pyxel.line(x - 8, y + 1, x - 13, y + 7, C.ICE)
            pyxel.line(x + 8, y + 1, x + 13, y + 7, C.ICE)
        elif pid in (6, 20):
            pyxel.rect(x - 8, y - 5, 16, 11, col)
            pyxel.tri(x - 6, y - 5, x - 15, y - 11, x - 9, y + 2, C.ORANGE)
            pyxel.tri(x + 6, y - 5, x + 15, y - 11, x + 9, y + 2, C.ORANGE)
            pyxel.tri(x + 8, y - 2, x + 16, y - 7, x + 13, y + 3, C.RED)
            pyxel.pset(x + 6, y - 3, C.WHITE)
        elif pid == 7:
            pyxel.circ(x, y, 6, C.ORANGE)
            pyxel.tri(x - 5, y - 1, x - 14, y - 8, x - 8, y + 7, C.RED)
            pyxel.tri(x + 5, y - 1, x + 14, y - 8, x + 8, y + 7, C.GOLD)
            pyxel.tri(x, y + 4, x - 4, y + 12, x + 4, y + 12, C.RED)
        elif pid == 8:
            pyxel.circ(x, y, 8, C.POISON)
            pyxel.circb(x, y, 8, C.STONE_DARK)
            pyxel.circ(x + 9, y - 2, 4, C.GRASS_LIGHT)
            pyxel.line(x - 4, y - 1, x + 4, y + 5, C.STONE_DARK)
        elif pid == 9:
            pyxel.circ(x, y - 2, 7, C.WHITE)
            pyxel.tri(x - 7, y + 1, x, y + 13, x + 7, y + 1, C.WHITE)
            pyxel.pset(x - 2, y - 3, C.PURPLE)
            pyxel.pset(x + 2, y - 3, C.PURPLE)
        elif pid == 10:
            pyxel.rect(x - 8, y - 8, 16, 16, C.STONE_DARK)
            pyxel.rectb(x - 8, y - 8, 16, 16, C.STONE_LIGHT)
            pyxel.rect(x - 5, y - 13, 10, 5, C.STONE_LIGHT)
        elif pid == 11:
            pyxel.circ(x, y, 4, C.PURPLE)
            pyxel.circb(x - 5, y - 2, 5, C.ICE)
            pyxel.circb(x + 5, y - 2, 5, C.ICE)
            pyxel.pset(x, y - 7, C.WHITE)
        elif pid == 12:
            pyxel.rect(x - 5, y - 4, 10, 8, C.SHADOW)
            pyxel.tri(x - 5, y - 3, x - 14, y - 9, x - 6, y + 2, C.NIGHT)
            pyxel.tri(x + 5, y - 3, x + 14, y - 9, x + 6, y + 2, C.NIGHT)
            pyxel.tri(x + 5, y - 1, x + 12, y - 4, x + 8, y + 2, C.GOLD)
        elif pid == 13:
            pyxel.rect(x - 8, y - 3, 16, 8, C.BROWN)
            pyxel.circ(x + 8, y - 5, 5, C.BROWN)
            pyxel.tri(x + 5, y - 8, x + 4, y - 14, x + 9, y - 8, C.BROWN)
            pyxel.line(x - 8, y - 1, x - 15, y - 7, C.BROWN)
        elif pid == 14:
            pyxel.circ(x, y, 6, C.GOLD)
            pyxel.tri(x - 5, y - 4, x - 4, y - 11, x, y - 5, C.GOLD)
            pyxel.tri(x + 5, y - 4, x + 4, y - 11, x, y - 5, C.GOLD)
            pyxel.line(x + 5, y + 4, x + 13, y + 1, C.GOLD)
        elif pid == 15:
            pyxel.elli(x - 6, y - 7, 12, 15, C.ICE) if hasattr(pyxel, "elli") else pyxel.rect(x - 6, y - 7, 12, 15, C.ICE)
            pyxel.rect(x - 4, y - 4, 8, 9, C.WHITE)
            pyxel.tri(x, y - 2, x - 4, y + 2, x + 4, y + 2, C.ORANGE)
        elif pid == 16:
            pyxel.line(x - 11, y + 2, x - 4, y - 4, C.POISON)
            pyxel.line(x - 4, y - 4, x + 4, y + 3, C.POISON)
            pyxel.line(x + 4, y + 3, x + 12, y - 3, C.POISON)
            pyxel.pset(x + 13, y - 4, C.WHITE)
        elif pid == 17:
            pyxel.rect(x - 4, y - 4, 8, 8, C.GOLD)
            pyxel.tri(x - 4, y - 3, x - 16, y - 12, x - 8, y + 3, C.BROWN)
            pyxel.tri(x + 4, y - 3, x + 16, y - 12, x + 8, y + 3, C.BROWN)
            pyxel.tri(x + 4, y - 1, x + 10, y - 4, x + 7, y + 2, C.WHITE)
        elif pid == 18:
            pyxel.elli(x - 13, y - 7, 26, 14, C.BLUE) if hasattr(pyxel, "elli") else pyxel.rect(x - 13, y - 7, 26, 14, C.BLUE)
            pyxel.tri(x - 13, y, x - 21, y - 6, x - 19, y + 6, C.BLUE)
            pyxel.pset(x + 8, y - 4, C.WHITE)
            pyxel.line(x + 2, y - 7, x + 6, y - 14, C.ICE)
        elif pid == 19:
            pyxel.rect(x - 9, y - 6, 18, 12, C.GRASS_LIGHT)
            pyxel.circ(x + 9, y - 9, 5, C.GRASS_LIGHT)
            pyxel.tri(x - 9, y - 2, x - 18, y - 8, x - 14, y + 5, C.GRASS_LIGHT)
            pyxel.line(x - 4, y + 6, x - 7, y + 13, C.BROWN)
            pyxel.line(x + 5, y + 6, x + 8, y + 13, C.BROWN)
        pyxel.text(x - 2, y + 12, str(pet.level), C.GOLD)

    def draw_zombies(self):
        for zombie in sorted(self.zombies, key=lambda z: z.y):
            self.draw_zombie(zombie)

    def draw_zombie(self, z):
        x = int(z.x)
        y = int(z.y)
        s = z.size
        c = C.WHITE if z.flash > 0 else z.color
        if self.weather_zombie_fade() < 1.0 and z.flash <= 0:
            c = C.SHADOW if z.id not in (5, 14) else z.color
        bob = 1 if (pyxel.frame_count // 12 + z.id) % 2 == 0 else -1
        pyxel.line(x - max(5, s // 3), y + s // 2 + 3, x + max(5, s // 3), y + s // 2 + 3, C.SHADOW)
        if z.id in (2, 10, 14) and z.shield > 0:
            pyxel.circb(x, y, max(10, s // 2 + 4), C.ICE if z.id != 14 else C.GOLD)
        if z.id == 11:
            pyxel.circb(x, y, 18 + (pyxel.frame_count // 12) % 4, C.PURPLE)
        if z.id == 13:
            self.draw_rider_zombie(x, y + bob, c)
        elif z.id == 14:
            self.draw_boss_zombie(x, y + bob, c)
        else:
            self.draw_zombie_body(x, y, z, c, bob)
        if z.freeze > 0:
            pyxel.circb(x, y, max(9, s // 2), C.ICE)
            self.draw_crystal(x + s // 3, y - s // 3, C.ICE)
        if z.burn > 0:
            pyxel.tri(x - 4, y - s // 2, x, y - s // 2 - 8, x + 4, y - s // 2, C.ORANGE)
        if z.poison > 0:
            pyxel.circb(x, y, max(8, s // 2), C.POISON)
        self.draw_health_bar(x, y - s // 2 - 9, s, z.hp, z.max_hp)
        if z.shield > 0:
            self.draw_shield_bar(x, y - s // 2 - 14, s, z.shield, max(1, z.max_hp // 4))

    def draw_shield_bar(self, x, y, width, shield, max_shield):
        w = max(12, width)
        fill = int(w * min(shield, max_shield) / max_shield)
        pyxel.rect(x - w // 2, y, w, 2, C.SHADOW)
        pyxel.rect(x - w // 2, y, fill, 2, C.ICE)
        pyxel.pset(x - w // 2, y, C.WHITE)
        pyxel.pset(x + w // 2 - 1, y, C.WHITE)

    def draw_zombie_body(self, x, y, z, c, bob):
        torso = c
        outline = C.NIGHT
        pyxel.circ(x, y - 7, 5, torso)
        pyxel.circb(x, y - 7, 5, outline)
        pyxel.pset(x - 2, y - 9, C.WHITE)
        pyxel.pset(x - 2, y - 7, outline)
        pyxel.pset(x + 2, y - 7, outline)
        pyxel.line(x - 2, y - 3, x + 2, y - 4, outline)
        pyxel.rect(x - 5, y - 1, 10, 12, torso)
        pyxel.rectb(x - 5, y - 1, 10, 12, outline)
        pyxel.rect(x - 6, y + 1, 12, 4, torso)
        pyxel.line(x - 5, y + 1, x - 8, y + 8 - bob, torso)
        pyxel.line(x + 5, y + 1, x + 8, y + 8 + bob, torso)
        pyxel.line(x - 2, y + 10, x - 7, y + 17 + bob, torso)
        pyxel.line(x + 2, y + 10, x + 7, y + 17 - bob, torso)
        pyxel.pset(x - 6, y + 17 + bob, outline)
        pyxel.pset(x + 6, y + 17 - bob, outline)
        pyxel.line(x - 3, y + 2, x + 3, y + 8, C.WHITE if z.id in (8, 10) else C.GRASS_LIGHT)
        pyxel.pset(x - 1, y - 9, C.WHITE)
        self.draw_zombie_details(z, x, y, bob)

    def draw_zombie_details(self, z, x, y, bob):
        if z.id == 1:
            pyxel.line(x - 6, y + 13, x - 10, y + 15, C.BLUE)
            pyxel.line(x + 6, y + 13, x + 10, y + 15, C.BLUE)
        elif z.id == 2:
            pyxel.rectb(x - 6, y, 12, 12, C.STONE_LIGHT)
            pyxel.rect(x - 7, y + 3, 4, 8, C.STONE_DARK)
        elif z.id == 3:
            pyxel.circ(x + 5, y + 3, 3, C.RED)
            pyxel.pset(x + 5, y + 2, C.GOLD)
        elif z.id == 4:
            pyxel.circ(x, y + 5, 11, C.GRASS_LIGHT)
            pyxel.circb(x, y + 5, 11, C.NIGHT)
            pyxel.circ(x, y - 9, 8, C.GRASS_LIGHT)
            pyxel.circb(x, y - 9, 8, C.NIGHT)
            pyxel.line(x - 10, y + 4, x - 18, y + 12, C.GRASS_LIGHT)
            pyxel.line(x + 10, y + 4, x + 18, y + 12, C.GRASS_LIGHT)
            pyxel.pset(x - 3, y - 7, C.RED)
            pyxel.pset(x + 3, y - 7, C.RED)
        elif z.id == 5:
            self.draw_crystal(x + 6, y - 8, C.ICE)
            self.draw_crystal(x - 6, y - 1, C.BLUE)
        elif z.id == 6:
            pyxel.tri(x - 4, y - 7, x, y - 15, x + 4, y - 7, C.ORANGE)
        elif z.id == 7:
            pyxel.circ(x - 6, y - 4, 2, C.POISON)
            pyxel.circ(x + 7, y + 1, 2, C.POISON)
        elif z.id == 8:
            pyxel.line(x - 3, y - 3, x + 3, y - 3, C.NIGHT)
            pyxel.line(x - 4, y + 4, x + 4, y + 10, C.NIGHT)
        elif z.id == 9:
            pyxel.circb(x, y + 1, 10, C.PURPLE)
            pyxel.pset(x - 2, y - 4, C.RED)
            pyxel.pset(x + 2, y - 4, C.RED)
        elif z.id == 10:
            pyxel.rect(x - 9, y + 1, 5, 9, C.STONE_LIGHT)
            pyxel.line(x + 6, y - 1, x + 9, y + 9, C.STONE_DARK)
        elif z.id == 11:
            pyxel.line(x + 7, y - 7, x + 7, y + 8, C.GOLD)
            pyxel.circ(x + 7, y - 7, 2, C.PURPLE)
            pyxel.circb(x, y, 12, C.PURPLE)
        elif z.id == 12:
            pyxel.line(x - 5, y + 3, x - 10, y + 7, C.WHITE)
            pyxel.line(x + 5, y + 3, x + 10, y + 7, C.WHITE)
            pyxel.rect(x - 7, y + 4, 14, 9, C.GRASS_LIGHT)

    def draw_rider_zombie(self, x, y, c):
        pyxel.rect(x - 14, y + 1, 24, 10, C.SHADOW)
        pyxel.rectb(x - 14, y + 1, 24, 10, C.STONE_DARK)
        pyxel.line(x - 9, y + 11, x - 13, y + 17, C.STONE_LIGHT)
        pyxel.line(x + 5, y + 11, x + 9, y + 17, C.STONE_LIGHT)
        pyxel.rect(x - 4, y - 11, 9, 13, c)
        pyxel.rect(x - 3, y - 18, 7, 7, C.BLUE)
        pyxel.line(x + 5, y - 10, x + 18, y - 18, C.WHITE)
        pyxel.circb(x - 3, y - 3, 16, C.STONE_LIGHT)

    def draw_boss_zombie(self, x, y, c):
        pyxel.line(x - 22, y + 36, x + 23, y + 36, C.SHADOW)
        pyxel.rect(x - 16, y - 5, 32, 29, C.STONE_DARK)
        pyxel.rectb(x - 16, y - 5, 32, 29, C.GOLD)
        pyxel.rect(x - 20, y - 8, 40, 9, C.GOLD)
        pyxel.rectb(x - 20, y - 8, 40, 9, C.STONE_DARK)
        pyxel.rect(x - 25, y - 2, 9, 22, C.GOLD)
        pyxel.rect(x + 16, y - 2, 9, 22, C.GOLD)
        pyxel.line(x - 25, y + 18, x - 34, y + 30, C.GOLD)
        pyxel.line(x + 25, y + 18, x + 34, y + 29, C.GOLD)
        pyxel.rect(x - 11, y - 29, 22, 18, c)
        pyxel.rectb(x - 11, y - 29, 22, 18, C.NIGHT)
        pyxel.rect(x - 13, y - 33, 26, 5, C.GOLD)
        pyxel.tri(x - 13, y - 33, x - 7, y - 44, x - 1, y - 33, C.GOLD)
        pyxel.tri(x - 1, y - 33, x + 6, y - 46, x + 13, y - 33, C.GOLD)
        pyxel.pset(x - 4, y - 23, C.BLUE)
        pyxel.pset(x + 4, y - 23, C.BLUE)
        pyxel.line(x - 4, y - 17, x + 4, y - 18, C.NIGHT)
        pyxel.line(x + 31, y - 27, x + 31, y + 24, C.GOLD)
        pyxel.circ(x + 31, y - 30, 5, C.RED)
        pyxel.circb(x + 31, y - 30, 5, C.WHITE)
        pyxel.rect(x - 12, y + 24, 7, 14, C.STONE_DARK)
        pyxel.rect(x + 5, y + 24, 7, 14, C.STONE_DARK)
        pyxel.rectb(x - 12, y + 24, 7, 14, C.GOLD)
        pyxel.rectb(x + 5, y + 24, 7, 14, C.GOLD)
        pyxel.circb(x, y + 7, 24, C.GOLD)

    def draw_health_bar(self, x, y, width, hp, max_hp):
        w = max(12, width)
        pyxel.rect(x - w // 2, y, w, 3, C.RED)
        fill = int(w * max(0, hp) / max_hp)
        pyxel.rect(x - w // 2, y, fill, 3, C.POISON)
        pyxel.rectb(x - w // 2, y, w, 3, C.NIGHT)

    def draw_projectiles(self):
        for p in self.projectiles:
            alpha_t = p.life / max(1, p.max_life)
            x = p.x2 + (p.x1 - p.x2) * alpha_t
            y = p.y2 + (p.y1 - p.y2) * alpha_t
            ix = int(x)
            iy = int(y)
            dx = p.x2 - p.x1
            dy = p.y2 - p.y1
            length = max(1, math.hypot(dx, dy))
            ux = dx / length
            uy = dy / length
            px = -uy
            py = ux
            if p.style == "arrow":
                tail_x = int(ix - ux * 12)
                tail_y = int(iy - uy * 12)
                pyxel.line(tail_x, tail_y, ix, iy, p.color)
                pyxel.tri(ix, iy, int(ix - ux * 5 + px * 3), int(iy - uy * 5 + py * 3), int(ix - ux * 5 - px * 3), int(iy - uy * 5 - py * 3), C.WHITE)
                pyxel.line(int(tail_x - px * 2), int(tail_y - py * 2), int(tail_x + px * 2), int(tail_y + py * 2), C.BROWN)
                pyxel.pset(ix, iy, C.WHITE)
            elif p.style == "spear":
                tail_x = int(ix - ux * 18)
                tail_y = int(iy - uy * 18)
                pyxel.line(tail_x, tail_y, ix, iy, C.BROWN)
                pyxel.line(int(tail_x + px), int(tail_y + py), int(ix + px), int(iy + py), C.STONE_DARK)
                pyxel.tri(ix, iy, int(ix - ux * 7 + px * 4), int(iy - uy * 7 + py * 4), int(ix - ux * 7 - px * 4), int(iy - uy * 7 - py * 4), C.STONE_LIGHT)
                pyxel.pset(ix, iy, C.WHITE)
            elif p.style == "rock":
                trail_x = int(ix - ux * 10)
                trail_y = int(iy - uy * 10)
                pyxel.line(trail_x, trail_y, ix, iy, C.BROWN)
                pyxel.circ(ix, iy, 4, C.STONE_DARK)
                pyxel.rect(ix - 2, iy - 3, 4, 3, C.STONE_LIGHT)
                pyxel.pset(ix + 1, iy + 1, C.NIGHT)
            elif p.style == "shell":
                pyxel.line(int(ix - ux * 13), int(iy - uy * 13), ix, iy, C.SHADOW)
                pyxel.circ(ix, iy, 4, C.NIGHT)
                pyxel.circb(ix, iy, 4, C.ORANGE)
                pyxel.pset(int(ix - ux * 6), int(iy - uy * 6), C.STONE_LIGHT)
                pyxel.pset(int(ix - ux * 9), int(iy - uy * 9), C.STONE_DARK)
            elif p.style == "ice":
                tail_x = int(ix - ux * 11)
                tail_y = int(iy - uy * 11)
                pyxel.line(tail_x, tail_y, ix, iy, C.WHITE)
                pyxel.tri(ix, iy, int(ix - ux * 9 + px * 5), int(iy - uy * 9 + py * 5), int(ix - ux * 4 - px * 4), int(iy - uy * 4 - py * 4), C.ICE)
                pyxel.tri(ix, iy, int(ix - ux * 6 + px * 2), int(iy - uy * 6 + py * 2), int(ix - ux * 2 - px * 2), int(iy - uy * 2 - py * 2), C.WHITE)
            elif p.style == "crystal":
                pyxel.line(int(p.x1), int(p.y1), ix, iy, C.PURPLE)
                pyxel.tri(ix, iy - 6, ix - 5, iy, ix, iy + 6, p.color)
                pyxel.tri(ix, iy - 6, ix + 5, iy, ix, iy + 6, C.ICE)
                pyxel.circb(ix, iy, 7, C.WHITE)
            elif p.style == "star":
                pyxel.line(int(ix - ux * 12), int(iy - uy * 12), ix, iy, C.GOLD)
                pyxel.line(ix - 5, iy, ix + 5, iy, C.GOLD)
                pyxel.line(ix, iy - 5, ix, iy + 5, C.WHITE)
                pyxel.line(ix - 3, iy - 3, ix + 3, iy + 3, C.GOLD)
                pyxel.line(ix + 3, iy - 3, ix - 3, iy + 3, C.WHITE)
            elif p.style == "orb":
                pyxel.line(int(ix - ux * 10), int(iy - uy * 10), ix, iy, C.STONE_DARK)
                pyxel.circ(ix, iy, 3 + (p.life % 2), p.color)
                pyxel.circb(ix, iy, 4, C.WHITE)
            elif p.style == "bolt":
                mx = int((p.x1 + ix) / 2)
                my = int((p.y1 + iy) / 2) + (p.life % 5) - 2
                pyxel.line(int(p.x1), int(p.y1), mx, my, p.color)
                pyxel.line(mx, my, ix, iy, C.WHITE)
            elif p.style == "flame":
                pyxel.tri(int(ix - ux * 13), int(iy - uy * 13), int(ix + px * 6), int(iy + py * 6), int(ix - px * 6), int(iy - py * 6), C.ORANGE)
                pyxel.tri(int(ix - ux * 8), int(iy - uy * 8), int(ix + px * 3), int(iy + py * 3), int(ix - px * 3), int(iy - py * 3), C.GOLD)
            elif p.style == "bubble":
                pyxel.circ(ix, iy, 4, p.color)
                pyxel.circb(ix, iy, 5, C.WHITE)
                pyxel.pset(ix - 1, iy - 1, C.WHITE)
            elif p.style == "root":
                pyxel.line(int(p.x1), int(p.y1), ix, iy, C.BROWN)
                pyxel.line(int(p.x1 + px * 2), int(p.y1 + py * 2), int(ix + px * 2), int(iy + py * 2), C.POISON)
                for step in range(2, 10, 3):
                    tx = int(ix - ux * step)
                    ty = int(iy - uy * step)
                    pyxel.pset(int(tx + px * 3), int(ty + py * 3), C.POISON)
                    pyxel.pset(int(tx - px * 3), int(ty - py * 3), C.POISON)
            elif p.style == "rune":
                pyxel.line(int(p.x1), int(p.y1), ix, iy, p.color)
                pyxel.rectb(ix - 3, iy - 3, 7, 7, p.color)
                pyxel.line(ix - 3, iy + 3, ix + 3, iy - 3, C.WHITE)
            else:
                pyxel.line(int(p.x1), int(p.y1), ix, iy, p.color)
                pyxel.line(ix - 3, iy + 3, ix + 3, iy - 3, C.WHITE)

    def draw_effects(self):
        for e in self.effects:
            t = 1.0 - e.life / max(1, e.max_life)
            r = max(1, int(e.radius * t))
            if e.kind == "ring":
                pyxel.circb(int(e.x), int(e.y), r, e.color)
                pyxel.circb(int(e.x), int(e.y), max(1, r // 2), C.WHITE)
            elif e.kind == "burst":
                for i in range(8):
                    angle = i * math.pi / 4 + t
                    x2 = e.x + math.cos(angle) * r
                    y2 = e.y + math.sin(angle) * r
                    pyxel.line(int(e.x), int(e.y), int(x2), int(y2), e.color)
            elif e.kind == "nova":
                pyxel.circb(int(e.x), int(e.y), r, e.color)
                pyxel.circb(int(e.x), int(e.y), max(1, r - 5), C.GOLD)
                for i in range(12):
                    angle = i * math.pi / 6
                    x2 = e.x + math.cos(angle) * r
                    y2 = e.y + math.sin(angle) * r
                    pyxel.pset(int(x2), int(y2), C.WHITE)
            elif e.kind == "slash":
                pyxel.line(int(e.x - r), int(e.y + r), int(e.x + r), int(e.y - r), e.color)
                pyxel.line(int(e.x - r // 2), int(e.y + r), int(e.x + r), int(e.y - r // 2), C.WHITE)
            elif e.kind == "flame":
                pyxel.tri(int(e.x - r), int(e.y + r), int(e.x), int(e.y - r), int(e.x + r), int(e.y + r), e.color)
                pyxel.tri(int(e.x - r // 2), int(e.y + r // 2), int(e.x), int(e.y - r // 2), int(e.x + r // 2), int(e.y + r // 2), C.GOLD)
            elif e.kind == "lightning":
                self.draw_bolt(int(e.x - 5), int(e.y - 8), e.color)
                self.draw_bolt(int(e.x + 2), int(e.y - 5), C.WHITE)
            elif e.kind == "sunburst":
                for i in range(16):
                    angle = i * math.pi / 8
                    inner = r // 3
                    x1 = e.x + math.cos(angle) * inner
                    y1 = e.y + math.sin(angle) * inner
                    x2 = e.x + math.cos(angle) * r
                    y2 = e.y + math.sin(angle) * r
                    pyxel.line(int(x1), int(y1), int(x2), int(y2), C.ORANGE if i % 2 else C.GOLD)
                pyxel.circb(int(e.x), int(e.y), max(2, r // 3), C.WHITE)
            elif e.kind == "snow":
                pyxel.circb(int(e.x), int(e.y), r, C.ICE)
                for i in range(10):
                    angle = i * math.pi / 5 + t
                    x2 = e.x + math.cos(angle) * r
                    y2 = e.y + math.sin(angle) * r
                    self.draw_crystal(int(x2), int(y2), C.ICE)
            elif e.kind == "roots":
                for i in range(12):
                    angle = i * math.pi / 6
                    x2 = e.x + math.cos(angle) * r
                    y2 = e.y + math.sin(angle) * r
                    pyxel.line(int(e.x), int(e.y), int(x2), int(y2), C.BROWN)
                    pyxel.pset(int(x2), int(y2), C.POISON)
                pyxel.circb(int(e.x), int(e.y), max(3, r // 4), C.POISON)
            elif e.kind == "storm":
                for i in range(6):
                    angle = i * math.pi / 3 + t
                    x2 = e.x + math.cos(angle) * r
                    y2 = e.y + math.sin(angle) * r
                    self.draw_bolt(int((e.x + x2) / 2), int((e.y + y2) / 2), C.GOLD if i % 2 else C.WHITE)
                pyxel.circb(int(e.x), int(e.y), r, C.GOLD)
            elif e.kind == "void":
                pyxel.circb(int(e.x), int(e.y), r, C.PURPLE)
                pyxel.circb(int(e.x), int(e.y), max(2, r - 7), C.NIGHT)
                for i in range(8):
                    angle = i * math.pi / 4 - t
                    x2 = e.x + math.cos(angle) * r
                    y2 = e.y + math.sin(angle) * r
                    pyxel.line(int(x2), int(y2), int(e.x), int(e.y), C.PURPLE)
            elif e.kind == "heal":
                pyxel.circb(int(e.x), int(e.y), r, C.WHITE)
                for i in range(5):
                    ox = int(math.cos(i * 1.25 + t) * r * 0.45)
                    oy = int(math.sin(i * 1.25 + t) * r * 0.45)
                    pyxel.line(int(e.x + ox - 3), int(e.y + oy), int(e.x + ox + 3), int(e.y + oy), C.POISON)
                    pyxel.line(int(e.x + ox), int(e.y + oy - 3), int(e.x + ox), int(e.y + oy + 3), C.POISON)
            elif e.kind == "quake":
                for i in range(5):
                    yy = int(e.y + (i - 2) * 8)
                    pyxel.line(int(e.x - r), yy, int(e.x + r), yy + (i % 2) * 3, C.STONE_DARK)
                    pyxel.line(int(e.x - r // 2), yy + 2, int(e.x + r // 2), yy - 2, C.RED)
                pyxel.circb(int(e.x), int(e.y), r, C.RED)
            elif e.kind == "firewall":
                for i in range(-3, 4):
                    fx = int(e.x + i * r / 4)
                    pyxel.tri(fx - 6, int(e.y + 12), fx, int(e.y - 18 - (i % 2) * 5), fx + 6, int(e.y + 12), C.ORANGE)
                    pyxel.tri(fx - 3, int(e.y + 7), fx, int(e.y - 10), fx + 3, int(e.y + 7), C.GOLD)
            elif e.kind == "crystal":
                pyxel.circb(int(e.x), int(e.y), r, C.PURPLE)
                for i in range(12):
                    angle = i * math.pi / 6 + t
                    x2 = e.x + math.cos(angle) * r
                    y2 = e.y + math.sin(angle) * r
                    self.draw_crystal(int(x2), int(y2), C.PURPLE if i % 2 else C.ICE)
            elif e.kind == "tornado":
                for i in range(5):
                    rr = max(3, r - i * 7)
                    yy = int(e.y + i * 5 - 12)
                    pyxel.circb(int(e.x), yy, rr, C.BLUE if i % 2 else C.WHITE)
                pyxel.line(int(e.x), int(e.y - r), int(e.x), int(e.y + r), C.ICE)
            elif e.kind == "starfall":
                for i in range(9):
                    angle = i * math.pi * 2 / 9
                    sx = int(e.x + math.cos(angle) * r * 0.55)
                    sy = int(e.y + math.sin(angle) * r * 0.35)
                    pyxel.line(sx - 4, sy, sx + 4, sy, C.GOLD)
                    pyxel.line(sx, sy - 4, sx, sy + 4, C.WHITE)
            elif e.kind == "blade_cleave":
                swing = int(r * (0.35 + t * 0.65))
                pyxel.line(int(e.x - swing), int(e.y + swing // 2), int(e.x + swing), int(e.y - swing // 2), C.WHITE)
                pyxel.line(int(e.x - swing + 4), int(e.y + swing // 2 + 3), int(e.x + swing + 4), int(e.y - swing // 2 + 3), C.STONE_LIGHT)
                pyxel.tri(int(e.x + swing), int(e.y - swing // 2), int(e.x + swing - 9), int(e.y - swing // 2 + 2), int(e.x + swing - 3), int(e.y - swing // 2 + 10), C.GOLD)
            elif e.kind == "spear_wall":
                for i in range(-3, 4):
                    sx = int(e.x + i * 12)
                    top = int(e.y - r + abs(i) * 4)
                    bot = int(e.y + r // 2)
                    pyxel.line(sx, top, sx, bot, C.STONE_LIGHT)
                    pyxel.tri(sx, top - 8, sx - 4, top, sx + 4, top, C.WHITE)
            elif e.kind == "arrow_rain":
                for i in range(12):
                    sx = int(e.x - r // 2 + i * max(5, r // 6))
                    sy = int(e.y - r + (i % 4) * 9 + t * 34)
                    pyxel.line(sx, sy, sx + 5, sy + 10, C.GOLD)
                    pyxel.tri(sx + 5, sy + 10, sx + 1, sy + 7, sx + 7, sy + 7, C.WHITE)
            elif e.kind == "meteor":
                fall = int(28 * t)
                pyxel.line(int(e.x - 18), int(e.y - 40 + fall), int(e.x), int(e.y - 5 + fall), C.ORANGE)
                pyxel.circ(int(e.x), int(e.y - 5 + fall), 5, C.STONE_DARK)
                pyxel.circb(int(e.x), int(e.y - 5 + fall), 6, C.ORANGE)
                if t > 0.55:
                    pyxel.line(int(e.x - r), int(e.y), int(e.x + r), int(e.y), C.ORANGE)
                    pyxel.line(int(e.x), int(e.y - r // 2), int(e.x), int(e.y + r // 2), C.GOLD)
            elif e.kind == "thunderbolt":
                top = int(e.y - r - 22)
                pyxel.line(int(e.x), top, int(e.x - 7), int(e.y - 10), C.WHITE)
                pyxel.line(int(e.x - 7), int(e.y - 10), int(e.x + 5), int(e.y - 7), C.GOLD)
                pyxel.line(int(e.x + 5), int(e.y - 7), int(e.x - 2), int(e.y + 10), C.WHITE)
                pyxel.line(int(e.x - 12), int(e.y + 8), int(e.x + 12), int(e.y + 8), C.GOLD)
            elif e.kind == "cannon_blast":
                pyxel.rect(int(e.x - r // 2), int(e.y + 5), r, 4, C.STONE_DARK)
                for i in range(8):
                    angle = i * math.pi / 4
                    x2 = e.x + math.cos(angle) * r * t
                    y2 = e.y + math.sin(angle) * r * t
                    pyxel.circ(int(x2), int(y2), 3, C.ORANGE if i % 2 else C.STONE_DARK)
                pyxel.tri(int(e.x - 8), int(e.y + 4), int(e.x), int(e.y - 13), int(e.x + 8), int(e.y + 4), C.GOLD)
            elif e.kind == "dragon_flame":
                for i in range(5):
                    length = int(r * (0.35 + t * 0.55)) - i * 8
                    pyxel.tri(int(e.x), int(e.y), int(e.x + length), int(e.y - 20 + i * 8), int(e.x + length), int(e.y + 20 - i * 4), C.ORANGE if i % 2 else C.RED)
                pyxel.tri(int(e.x + 8), int(e.y - 6), int(e.x + r // 2), int(e.y), int(e.x + 8), int(e.y + 6), C.GOLD)
            elif e.kind == "solar_beam":
                pyxel.line(int(e.x), int(e.y - r - 20), int(e.x), int(e.y + 12), C.GOLD)
                pyxel.line(int(e.x - 2), int(e.y - r - 16), int(e.x - 2), int(e.y + 8), C.WHITE)
                pyxel.line(int(e.x + 2), int(e.y - r - 16), int(e.x + 2), int(e.y + 8), C.WHITE)
                pyxel.tri(int(e.x - 12), int(e.y + 8), int(e.x), int(e.y - 4), int(e.x + 12), int(e.y + 8), C.GOLD)
            elif e.kind == "heart":
                self.draw_heart(int(e.x - 4), int(e.y - 4))
                pyxel.circb(int(e.x), int(e.y), r, e.color)
                for i in range(6):
                    angle = i * math.pi / 3 + t
                    pyxel.pset(int(e.x + math.cos(angle) * r), int(e.y + math.sin(angle) * r), C.WHITE)
            elif e.kind == "phoenix":
                wing = max(8, r)
                pyxel.tri(int(e.x), int(e.y - 8), int(e.x - wing), int(e.y + 10), int(e.x - 6), int(e.y + 3), C.ORANGE)
                pyxel.tri(int(e.x), int(e.y - 8), int(e.x + wing), int(e.y + 10), int(e.x + 6), int(e.y + 3), C.GOLD)
                pyxel.tri(int(e.x - 5), int(e.y + 8), int(e.x), int(e.y - 20), int(e.x + 5), int(e.y + 8), C.RED)
                pyxel.circb(int(e.x), int(e.y), max(4, r // 2), C.WHITE)
            else:
                pyxel.circ(int(e.x), int(e.y), max(1, r // 2), e.color)

    def draw_castle(self):
        x = 438
        y = 76
        pyxel.circ(x + 29, y + 34, 36, C.SHADOW)
        pyxel.rect(x + 6, y + 18, 48, 34, C.GOLD)
        pyxel.rectb(x + 6, y + 18, 48, 34, C.STONE_DARK)
        for i in range(0, 48, 10):
            pyxel.rect(x + 6 + i, y + 10, 6, 10, C.GOLD)
            pyxel.rectb(x + 6 + i, y + 10, 6, 10, C.STONE_DARK)
        pyxel.rect(x, y + 15, 13, 39, C.STONE_LIGHT)
        pyxel.rectb(x, y + 15, 13, 39, C.STONE_DARK)
        pyxel.rect(x + 47, y + 15, 13, 39, C.STONE_LIGHT)
        pyxel.rectb(x + 47, y + 15, 13, 39, C.STONE_DARK)
        pyxel.tri(x - 3, y + 15, x + 6, y + 3, x + 16, y + 15, C.RED)
        pyxel.tri(x + 44, y + 15, x + 53, y + 3, x + 63, y + 15, C.RED)
        pyxel.rect(x + 24, y + 35, 13, 17, C.RED)
        pyxel.rectb(x + 24, y + 35, 13, 17, C.NIGHT)
        pyxel.line(x + 12, y + 27, x + 50, y + 27, C.STONE_DARK)
        pyxel.line(x + 12, y + 40, x + 48, y + 40, C.STONE_DARK)
        pyxel.line(x + 18, y + 9, x + 18, y - 10, C.GOLD)
        pyxel.rect(x + 19, y - 10, 15, 8, C.WHITE)
        pyxel.tri(x + 22, y - 9, x + 27, y - 5, x + 22, y - 2, C.RED)
        for i in range(min(self.castle_hp, 20)):
            hx = 7 + (i % 10) * 10
            hy = 8 + (i // 10) * 10
            self.draw_heart(hx, hy)

    def draw_heart(self, x, y):
        pyxel.pset(x + 2, y + 1, C.WHITE)
        pyxel.pset(x + 5, y + 1, C.WHITE)
        pyxel.rect(x + 1, y + 2, 3, 3, C.RED)
        pyxel.rect(x + 4, y + 2, 3, 3, C.RED)
        pyxel.rect(x + 2, y + 5, 4, 2, C.RED)
        pyxel.pset(x + 3, y + 7, C.RED)
        pyxel.pset(x + 4, y + 7, C.RED)

    def draw_top_ui(self):
        pyxel.rect(0, 0, 512, 80, C.NIGHT)
        pyxel.line(0, 79, 511, 79, C.STONE_DARK)
        pyxel.text(8, 8, "BURG", C.STONE_LIGHT)
        pyxel.text(36, 8, str(self.castle_hp) + "/" + str(self.castle_max_hp), C.WHITE)
        pyxel.text(8, 22, "Map " + str(self.map_index + 1) + "  " + self.difficulty_name, C.STONE_LIGHT)
        pyxel.text(138, 8, "Welle", C.STONE_LIGHT)
        pyxel.text(170, 8, str(self.wave), C.GOLD)
        pyxel.text(138, 22, "Kills", C.STONE_LIGHT)
        pyxel.text(170, 22, str(self.kills), C.GOLD)
        seconds = self.time // FPS
        pyxel.text(138, 36, "Zeit", C.STONE_LIGHT)
        pyxel.text(170, 36, str(seconds // 60).rjust(2, "0") + ":" + str(seconds % 60).rjust(2, "0"), C.GOLD)
        self.draw_coin(230, 10)
        pyxel.text(242, 8, str(self.gold), C.GOLD)
        self.draw_soul(230, 29)
        pyxel.text(242, 27, str(self.soul), C.PURPLE)
        self.draw_weather_icon(88, 34)
        pyxel.text(104, 35, self.weather_name[:8], C.WHITE)
        pyxel.text(104, 47, self.weather_effect_text()[:27], C.STONE_LIGHT)
        self.draw_speed_button(186, 55, 1)
        self.draw_speed_button(214, 55, 2)
        self.draw_speed_button(242, 55, 4)
        self.draw_small_button(278, 55, 48, 18, "WELLE", C.SHADOW)
        self.draw_small_button(332, 55, 50, 18, "AUTO" if self.auto_wave else "MAN", C.BLUE if self.auto_wave else C.SHADOW)
        self.draw_small_button(388, 55, 50, 18, "MERGE", C.PURPLE)
        self.draw_small_button(444, 55, 64, 18, "SHOP", C.ORANGE)
        self.draw_small_button(278, 34, 48, 18, "ZIELE", C.GOLD)
        self.draw_small_button(332, 34, 50, 18, "PETS", C.POISON)
        self.draw_small_button(388, 34, 50, 18, "PAUSE", C.STONE_DARK)
        self.draw_small_button(444, 34, 64, 18, "RESTART", C.RED)
        if self.damage_boost_timer > 0:
            pyxel.text(8, 50, "DMG+", C.POISON)
        if self.speed_boost_timer > 0:
            pyxel.text(45, 50, "SPD+", C.BLUE)
        if self.range_boost_timer > 0:
            pyxel.text(82, 50, "RNG+", C.ICE)
        if self.message_timer > 0:
            pyxel.text(8, 74, self.message[:58], C.WHITE)

    def draw_small_button(self, x, y, w, h, label, color):
        hover = x <= pyxel.mouse_x <= x + w and y <= pyxel.mouse_y <= y + h
        pyxel.rect(x, y, w, h, C.GRASS_LIGHT if hover else color)
        pyxel.rectb(x, y, w, h, C.WHITE)
        pyxel.text(x + max(4, (w - len(label) * 4) // 2), y + 6, label, C.NIGHT if color in (C.ORANGE, C.GOLD, C.POISON) else C.WHITE)

    def draw_speed_button(self, x, y, speed):
        active = self.game_speed == speed
        hover = x <= pyxel.mouse_x <= x + 24 and y <= pyxel.mouse_y <= y + 18
        pyxel.rect(x, y, 24, 18, C.GOLD if active else C.GRASS_LIGHT if hover else C.SHADOW)
        pyxel.rectb(x, y, 24, 18, C.WHITE if active else C.STONE_DARK)
        pyxel.text(x + 5, y + 6, "x" + str(speed), C.NIGHT if active else C.WHITE)

    def draw_weather_icon(self, x, y):
        name = self.weather_name
        pyxel.rect(x - 2, y - 2, 14, 14, C.SHADOW)
        pyxel.rectb(x - 2, y - 2, 14, 14, C.STONE_DARK)
        if name == "Sonnig":
            pyxel.circ(x + 5, y + 5, 4, C.GOLD)
            for dx, dy in [(0, -6), (0, 6), (-6, 0), (6, 0)]:
                pyxel.pset(x + 5 + dx, y + 5 + dy, C.GOLD)
        elif name == "Regen":
            pyxel.circ(x + 4, y + 3, 4, C.STONE_LIGHT)
            pyxel.line(x + 2, y + 8, x, y + 11, C.BLUE)
            pyxel.line(x + 7, y + 8, x + 5, y + 11, C.BLUE)
        elif name == "Sturm":
            pyxel.circ(x + 4, y + 3, 4, C.STONE_DARK)
            pyxel.line(x + 6, y + 6, x + 3, y + 10, C.GOLD)
            pyxel.line(x + 3, y + 10, x + 8, y + 9, C.WHITE)
        elif name == "Nacht":
            pyxel.circ(x + 5, y + 5, 4, C.ICE)
            pyxel.circ(x + 7, y + 4, 4, C.SHADOW)
            pyxel.pset(x + 1, y + 1, C.WHITE)
            pyxel.pset(x + 10, y + 8, C.WHITE)
        elif name == "Nebel":
            pyxel.line(x, y + 3, x + 10, y + 3, C.STONE_LIGHT)
            pyxel.line(x + 1, y + 6, x + 9, y + 6, C.WHITE)
            pyxel.line(x, y + 9, x + 10, y + 9, C.STONE_LIGHT)
        elif name == "Vollmond":
            pyxel.circ(x + 5, y + 5, 5, C.ICE)
            pyxel.pset(x + 3, y + 4, C.WHITE)
            pyxel.pset(x + 7, y + 7, C.WHITE)
        elif name == "Schnee":
            pyxel.pset(x + 5, y + 1, C.WHITE)
            pyxel.line(x + 5, y + 2, x + 5, y + 10, C.WHITE)
            pyxel.line(x + 1, y + 6, x + 9, y + 6, C.WHITE)
            pyxel.line(x + 2, y + 3, x + 8, y + 9, C.ICE)
            pyxel.line(x + 8, y + 3, x + 2, y + 9, C.ICE)

    def draw_coin(self, x, y):
        pyxel.circ(x + 4, y + 4, 4, C.GOLD)
        pyxel.circb(x + 4, y + 4, 4, C.STONE_DARK)
        pyxel.pset(x + 4, y + 2, C.WHITE)

    def draw_soul(self, x, y):
        pyxel.circ(x + 4, y + 4, 4, C.PURPLE)
        pyxel.pset(x + 2, y + 3, C.WHITE)
        pyxel.pset(x + 6, y + 3, C.WHITE)
        pyxel.line(x + 1, y + 7, x + 7, y + 7, C.WHITE)

    def draw_bottom_ui(self):
        pyxel.rect(0, 430, 512, 82, C.NIGHT)
        pyxel.line(0, 430, 511, 430, C.STONE_LIGHT)
        selected_spec = WEAPON_BY_ID[self.selected_shop_weapon]
        pyxel.text(10, 442, "Ausgewaehlt:", C.STONE_LIGHT)
        self.draw_weapon_icon(92, 436, selected_spec["id"])
        pyxel.text(112, 442, selected_spec["name"][:18], C.WHITE)
        pyxel.text(112, 456, "Preis " + str(int(selected_spec["cost"] * self.pet_cost_mult())) + "  DMG " + str(selected_spec["damage"]) + "  RNG " + str(selected_spec["range"]), C.GOLD)
        mode_text = "Platzieren aktiv: klicke ein freies Feld." if self.placement_ready else "Waffen rechts waehlen. Danach genau eine Waffe platzieren."
        pyxel.text(10, 486, mode_text + " Waffe anklicken fuer Stats, Upgrade, Ability und Merge.", C.STONE_LIGHT)
        weapon = self.selected_weapon()
        if weapon:
            up = weapon.upgrade_cost()
            up_text = "MAX" if up is None else str(up)
            pyxel.text(306, 442, weapon.spec["name"][:17] + " L" + str(weapon.level) + " Up:" + up_text, C.WHITE)
        pyxel.rect(474, 134, 36, 78, C.GOLD if self.weapon_panel_open else C.SHADOW)
        pyxel.rectb(474, 134, 36, 78, C.WHITE)
        pyxel.text(482, 148, "WA", C.NIGHT if self.weapon_panel_open else C.WHITE)
        pyxel.text(482, 160, "FF", C.NIGHT if self.weapon_panel_open else C.WHITE)
        pyxel.text(482, 172, "EN", C.NIGHT if self.weapon_panel_open else C.WHITE)
        pyxel.text(482, 192, ">", C.NIGHT if self.weapon_panel_open else C.WHITE)

    def draw_hover_tooltip(self):
        if self.shop_open or self.achievements_open or self.pet_shop_open or self.game_over or self.weapon_panel_open or self.weapon_popup_open or self.merge_overview_open or self.merge_confirm is not None:
            return
        mx = pyxel.mouse_x
        my = pyxel.mouse_y
        lines = []
        border = C.WHITE
        if my < 430:
            weapon = self.weapon_at(mx, my)
            if weapon is not None:
                spec = weapon.spec
                lines = [
                    spec["name"] + "  L" + str(weapon.level),
                    "DMG " + str(weapon.damage(1.0)) + "  RNG " + str(spec["range"]),
                    "Spezial: " + spec["special"][:22],
                ]
                up = weapon.upgrade_cost()
                if up is None:
                    lines.append("MAX: fuer Merge bereit")
                    border = C.GOLD
                else:
                    lines.append("Upgrade: " + str(up) + " Gold")
            else:
                slot = self.slot_at(mx, my)
                if slot is not None and self.weapon_in_slot(slot) is None:
                    spec = WEAPON_BY_ID[self.selected_shop_weapon]
                    lines = [
                        "Freier Slot " + str(slot + 1),
                        "Klick: " + spec["name"],
                        "Preis: " + str(spec["cost"]) + " Gold",
                    ]
                    border = C.POISON if self.gold >= spec["cost"] else C.RED
        else:
            for i in range(8):
                item_index = self.weapon_scroll + i
                if item_index >= len(WEAPONS):
                    continue
                x = 28 + i * 56
                if x <= mx <= x + 44 and 438 <= my <= 466:
                    spec = WEAPONS[item_index]
                    lines = [
                        spec["name"],
                        "Preis " + str(spec["cost"]) + "  DMG " + str(spec["damage"]),
                        "RNG " + str(spec["range"]) + "  SPD " + str(spec["speed"]),
                        spec["special"][:28],
                    ]
                    border = C.GOLD
                    break
        if not lines:
            return
        width = max(len(line) for line in lines) * 4 + 12
        height = len(lines) * 10 + 8
        x = min(mx + 12, WIDTH - width - 4)
        y = max(84, min(my + 12, HEIGHT - height - 84))
        pyxel.rect(x, y, width, height, C.NIGHT)
        pyxel.rectb(x, y, width, height, border)
        pyxel.rectb(x + 2, y + 2, width - 4, height - 4, C.STONE_DARK)
        for i, line in enumerate(lines):
            col = C.GOLD if i == 0 else C.STONE_LIGHT
            pyxel.text(x + 6, y + 6 + i * 10, line, col)

    def draw_floating_texts(self):
        for text in self.texts:
            pyxel.text(int(text.x), int(text.y), text.text, text.color)

    def draw_boot_screen(self):
        pyxel.cls(C.NIGHT)
        for x, y, phase in self.stars:
            if (pyxel.frame_count // 20 + phase) % 4 != 2:
                pyxel.pset(x, y, C.WHITE)
        pyxel.rect(92, 88, 328, 286, C.SHADOW)
        pyxel.rectb(92, 88, 328, 286, C.GOLD)
        pyxel.rectb(98, 94, 316, 274, C.STONE_LIGHT)
        pyxel.text(160, 126, "BURGDEFENSE ULTIMATE", C.GOLD)
        pyxel.text(162, 144, "Clean Pixel Tower Defense", C.STONE_LIGHT)
        self.draw_title_castle(256, 216)
        pyxel.rect(154, 260, 204, 10, C.NIGHT)
        pyxel.rectb(154, 260, 204, 10, C.WHITE)
        fill = 196 + int(math.sin(pyxel.frame_count / 18) * 4)
        pyxel.rect(158, 263, fill, 4, C.GOLD)
        hover = 186 <= pyxel.mouse_x <= 326 and 292 <= pyxel.mouse_y <= 326
        pyxel.rect(186, 292, 140, 34, C.GOLD if hover else C.NIGHT)
        pyxel.rectb(186, 292, 140, 34, C.WHITE)
        pyxel.text(240, 305, "LEVELS", C.NIGHT if hover else C.GOLD)
        hover_tutorial = 186 <= pyxel.mouse_x <= 326 and 334 <= pyxel.mouse_y <= 358
        pyxel.rect(186, 334, 140, 24, C.STONE_LIGHT if hover_tutorial else C.SHADOW)
        pyxel.rectb(186, 334, 140, 24, C.WHITE)
        pyxel.text(232, 343, "TUTORIAL", C.NIGHT if hover_tutorial else C.WHITE)
        pyxel.text(142, 362, "Waehle danach Map und Schwierigkeit.", C.STONE_LIGHT)

    def draw_tutorial_screen(self):
        pyxel.cls(C.NIGHT)
        for x, y, phase in self.stars:
            if (pyxel.frame_count // 22 + phase) % 4 != 1:
                pyxel.pset(x, y, C.WHITE)
        pyxel.rect(58, 58, 396, 358, C.SHADOW)
        pyxel.rectb(58, 58, 396, 358, C.GOLD)
        pyxel.rectb(64, 64, 384, 346, C.STONE_LIGHT)
        pyxel.text(214, 82, "TUTORIAL", C.GOLD)
        rows = [
            ("1", "Waehle Level und Schwierigkeit."),
            ("2", "Kaufe rechts Waffen und platziere sie neben den Weg."),
            ("3", "Starte Wellen selbst oder schalte AUTO an."),
            ("4", "Klicke Waffen fuer Upgrade, Range, Merge und Skill."),
            ("5", "Sammle Drops, nutze Wetter, Shop, Pets und Ziele."),
            ("6", "Halte Zombies von der Burg fern."),
        ]
        for i, (num, text) in enumerate(rows):
            y = 124 + i * 34
            pyxel.circ(88, y + 5, 8, C.GOLD)
            pyxel.text(86, y + 2, num, C.NIGHT)
            pyxel.text(110, y + 2, text, C.WHITE)
        pyxel.text(104, 338, "Tipp: Mehr Startgold hilft am Anfang, aber spaeter", C.STONE_LIGHT)
        pyxel.text(104, 352, "werden Gold und Soul bewusst knapper.", C.STONE_LIGHT)
        self.draw_small_button(186, 374, 140, 28, "ZURUECK", C.SHADOW)

    def draw_main_menu(self):
        pyxel.cls(C.NIGHT)
        for x, y, phase in self.stars:
            if (pyxel.frame_count // 18 + phase) % 3 != 1:
                pyxel.pset(x, y, C.WHITE)
        pyxel.text(154, 34, "BURGDEFENSE ULTIMATE", C.GOLD)
        pyxel.text(126, 50, "Waehle eine Map. Danach waehlst du die Schwierigkeit.", C.STONE_LIGHT)
        start = self.map_page * 6
        for i in range(6):
            idx = start + i
            if idx >= len(MAPS):
                continue
            col = i % 3
            row = i // 3
            x = 46 + col * 154
            y = 132 + row * 128
            hover = x <= pyxel.mouse_x <= x + 126 and y <= pyxel.mouse_y <= y + 98
            pyxel.rect(x, y, 126, 98, C.SHADOW)
            pyxel.rectb(x, y, 126, 98, C.GOLD if hover else C.STONE_DARK)
            pyxel.text(x + 8, y + 8, "Level " + str(idx + 1), C.GOLD)
            pyxel.text(x + 8, y + 20, MAPS[idx]["name"][:18], C.WHITE)
            self.draw_map_preview(x + 8, y + 34, 110, 48, MAPS[idx]["path"], MAPS[idx]["style"])
        self.draw_small_button(52, 430, 44, 28, "<", C.SHADOW)
        self.draw_small_button(416, 430, 44, 28, ">", C.SHADOW)
        pyxel.text(220, 438, "Seite " + str(self.map_page + 1) + "/2", C.STONE_LIGHT)

    def draw_map_preview(self, x, y, w, h, path, style):
        bg = C.GRASS
        if style in ("frost", "crystal"):
            bg = C.SHADOW
        elif style in ("ember", "shadow"):
            bg = C.NIGHT
        elif style in ("sun", "royal"):
            bg = C.STONE_DARK
        pyxel.rect(x, y, w, h, bg)
        pyxel.rectb(x, y, w, h, C.STONE_LIGHT)
        last = None
        for px, py in path:
            sx = x + int((px + 24) / 536 * w)
            sy = y + int((py - 80) / 350 * h)
            sx = max(x + 2, min(x + w - 3, sx))
            sy = max(y + 2, min(y + h - 3, sy))
            if last:
                pyxel.line(last[0], last[1], sx, sy, C.STONE_LIGHT)
                pyxel.line(last[0], last[1] + 1, sx, sy + 1, C.STONE_DARK)
            last = (sx, sy)
        pyxel.rect(x + w - 14, y + 4, 10, 14, C.GOLD)
        pyxel.rectb(x + w - 14, y + 4, 10, 14, C.WHITE)

    def draw_difficulty_select(self):
        pyxel.cls(C.NIGHT)
        idx = self.pending_map_index if self.pending_map_index is not None else 0
        pyxel.rect(82, 64, 348, 330, C.SHADOW)
        pyxel.rectb(82, 64, 348, 330, C.GOLD)
        pyxel.text(176, 86, "Level " + str(idx + 1) + ": " + MAPS[idx]["name"], C.GOLD)
        self.draw_map_preview(156, 116, 200, 86, MAPS[idx]["path"], MAPS[idx]["style"])
        pyxel.text(142, 228, "Schwierigkeit auswaehlen", C.WHITE)
        data = [
            ("Einfach", "mehr Gold, weniger Druck", C.POISON),
            ("Normal", "ausgewogen", C.GOLD),
            ("Schwer", "mehr Gegner und Bosse", C.RED),
        ]
        for i, (name, desc, col) in enumerate(data):
            x = 88 + i * 116
            y = 278
            hover = x <= pyxel.mouse_x <= x + 94 and y <= pyxel.mouse_y <= y + 44
            pyxel.rect(x, y, 94, 44, C.GRASS_LIGHT if hover else C.NIGHT)
            pyxel.rectb(x, y, 94, 44, col)
            pyxel.text(x + 18, y + 10, name, col)
            pyxel.text(x + 8, y + 26, desc[:20], C.STONE_LIGHT)
        self.draw_small_button(188, 356, 134, 26, "ZURUECK", C.SHADOW)

    def draw_loading_screen(self):
        pyxel.cls(C.NIGHT)
        pyxel.text(194, 198, "Lade Verteidigung...", C.GOLD)
        pyxel.rect(134, 226, 244, 12, C.SHADOW)
        pyxel.rectb(134, 226, 244, 12, C.WHITE)
        fill = int((45 - max(0, self.loading_timer)) / 45 * 240)
        pyxel.rect(136, 228, max(0, fill), 8, C.GOLD)
        idx = self.map_index
        pyxel.text(160, 254, "Map " + str(idx + 1) + " - " + MAPS[idx]["name"] + " / " + self.difficulty_name, C.STONE_LIGHT)

    def draw_cutscene(self):
        pyxel.cls(C.NIGHT)
        pyxel.rect(38, 42, 436, 366, C.NIGHT)
        pyxel.rectb(38, 42, 436, 366, C.GOLD)
        pyxel.rect(52, 72, 408, 242, C.SHADOW)
        pyxel.rectb(52, 72, 408, 242, C.STONE_LIGHT)
        self.draw_cutscene_hall()
        pyxel.text(182, 58, "VOR DER BELAGERUNG", C.GOLD)
        f = self.cutscene_frame
        if f < 140:
            text = "Die Prinzessin steht vor ihrem Volk."
            self.draw_cutscene_people(False, False)
            self.draw_princess(256, 188, False, False)
        elif f < 280:
            text = "Der Kronenzauber zerbricht."
            self.draw_cutscene_people(False, True)
            self.draw_princess(256, 188, True, False)
            self.draw_cutscene_magic(256, 184, f)
        else:
            text = "Das Volk wird zur Horde. Die Burg muss halten."
            self.draw_cutscene_people(True, False)
            self.draw_princess(256, 188, False, True)
            pyxel.tri(242, 128, 256, 106, 270, 128, C.RED)
            pyxel.rect(246, 128, 20, 8, C.RED)
            pyxel.text(253, 130, "!", C.WHITE)
        pyxel.rect(86, 326, 340, 42, C.NIGHT)
        pyxel.rectb(86, 326, 340, 42, C.STONE_DARK)
        pyxel.text(106, 342, text, C.WHITE)
        self.draw_small_button(186, 378, 140, 22, "SKIP", C.SHADOW)

    def draw_cutscene_hall(self):
        pyxel.rect(68, 92, 376, 202, C.STONE_DARK)
        pyxel.rect(84, 112, 344, 112, C.NIGHT)
        pyxel.rectb(84, 112, 344, 112, C.STONE_LIGHT)
        for x in range(92, 430, 64):
            pyxel.rect(x, 106, 16, 116, C.SHADOW)
            pyxel.rectb(x, 106, 16, 116, C.STONE_LIGHT)
            pyxel.rect(x + 2, 102, 12, 4, C.GOLD)
            pyxel.tri(x - 3, 102, x + 8, 88, x + 19, 102, C.GOLD)
        pyxel.rect(212, 120, 88, 70, C.SHADOW)
        pyxel.rectb(212, 120, 88, 70, C.GOLD)
        pyxel.rect(230, 138, 52, 38, C.PURPLE)
        pyxel.rectb(230, 138, 52, 38, C.GOLD)
        pyxel.rect(100, 226, 312, 42, C.BROWN)
        pyxel.line(100, 226, 412, 226, C.GOLD)
        pyxel.rect(224, 236, 64, 36, C.RED)
        pyxel.rectb(224, 236, 64, 36, C.GOLD)
        pyxel.line(52, 314, 460, 314, C.GOLD)

    def draw_princess(self, x, y, casting, afraid):
        pyxel.line(x - 16, y + 30, x + 16, y + 30, C.SHADOW)
        pyxel.rect(x - 8, y - 40, 16, 11, C.GOLD)
        pyxel.tri(x - 10, y - 40, x - 5, y - 53, x, y - 40, C.GOLD)
        pyxel.tri(x, y - 40, x + 5, y - 53, x + 10, y - 40, C.GOLD)
        pyxel.circ(x, y - 30, 8, C.WHITE)
        pyxel.rect(x - 7, y - 37, 14, 5, C.GOLD)
        pyxel.rect(x - 8, y - 25, 16, 9, C.WHITE)
        dress = C.PURPLE if casting else C.ICE
        pyxel.tri(x - 18, y - 18, x + 18, y - 18, x, y + 32, dress)
        pyxel.tri(x - 10, y - 16, x + 10, y - 16, x, y + 22, C.WHITE)
        pyxel.rectb(x - 12, y - 18, 24, 34, C.STONE_LIGHT)
        eye = C.RED if afraid else C.NIGHT
        pyxel.pset(x - 3, y - 31, eye)
        pyxel.pset(x + 3, y - 31, eye)
        pyxel.line(x - 12, y - 12, x - 26, y - 23 if casting else y - 6, C.WHITE)
        pyxel.line(x + 12, y - 12, x + 26, y - 23 if casting else y - 6, C.WHITE)
        if casting:
            pyxel.circb(x, y - 12, 30, C.PURPLE)
            pyxel.circb(x, y - 12, 20, C.GOLD)
            pyxel.line(x - 26, y - 23, x - 78, y + 38, C.PURPLE)
            pyxel.line(x + 26, y - 23, x + 78, y + 38, C.PURPLE)

    def draw_cutscene_magic(self, x, y, f):
        pulse = 12 + (f // 6) % 10
        pyxel.circb(x, y - 12, pulse + 16, C.PURPLE)
        pyxel.circb(x, y - 12, pulse + 6, C.GOLD)
        for i in range(10):
            a = i * math.pi * 2 / 10 + f / 22
            px = x + math.cos(a) * (pulse + 14)
            py = y - 12 + math.sin(a) * (pulse * 0.6 + 7)
            self.draw_crystal(int(px), int(py), C.PURPLE if i % 2 else C.GOLD)
        for tx in (142, 178, 214, 298, 334, 370):
            midx = (x + tx) // 2
            pyxel.line(x, y - 20, midx, 218, C.GOLD)
            pyxel.line(midx, 218, tx, 234, C.PURPLE)
            pyxel.pset(tx, 234, C.WHITE)

    def draw_cutscene_people(self, zombies, worried):
        xs = [132, 166, 200, 312, 346, 380]
        for i, x in enumerate(xs):
            y = 246 + (i % 2) * 4
            robe = [C.BLUE, C.STONE_LIGHT, C.BROWN, C.GOLD, C.ICE, C.ORANGE][i]
            skin = C.POISON if zombies else C.WHITE
            pyxel.line(x - 10, y + 16, x + 10, y + 16, C.SHADOW)
            pyxel.rect(x - 6, y - 34, 12, 5, C.BROWN if not zombies else C.GRASS)
            pyxel.circ(x, y - 27, 7, skin)
            pyxel.rect(x - 7, y - 20, 14, 7, skin if zombies else robe)
            pyxel.tri(x - 10, y - 16, x + 10, y - 16, x, y + 12, C.POISON if zombies else robe)
            pyxel.rectb(x - 8, y - 19, 16, 24, C.NIGHT)
            eye = C.RED if zombies else C.PURPLE if worried else C.NIGHT
            pyxel.pset(x - 3, y - 30, eye)
            pyxel.pset(x + 3, y - 30, eye)
            if zombies:
                pyxel.line(x - 8, y - 11, x - 18, y - 20, C.POISON)
                pyxel.line(x + 8, y - 11, x + 18, y - 20, C.POISON)
                pyxel.line(x - 4, y + 5, x - 11, y + 15, C.POISON)
                pyxel.line(x + 4, y + 5, x + 10, y + 15, C.POISON)
                pyxel.pset(x - 1, y - 21, C.WHITE)
            else:
                pyxel.line(x - 8, y - 12, x - 15, y - 8 if not worried else y - 20, skin)
                pyxel.line(x + 8, y - 12, x + 15, y - 8 if not worried else y - 20, skin)
                pyxel.line(x - 4, y + 7, x - 8, y + 16, robe)
                pyxel.line(x + 4, y + 7, x + 8, y + 16, robe)

    def draw_weapon_panel(self):
        pyxel.rect(350, 84, 162, 342, C.NIGHT)
        pyxel.rectb(350, 84, 162, 342, C.GOLD)
        pyxel.text(366, 94, "WAFFEN", C.GOLD)
        pyxel.text(421, 94, "Klick waehlt", C.STONE_LIGHT)
        pyxel.rect(490, 90, 16, 16, C.RED)
        pyxel.rectb(490, 90, 16, 16, C.WHITE)
        pyxel.text(495, 95, "x", C.WHITE)
        for i in range(9):
            item_index = self.weapon_scroll + i
            if item_index >= len(WEAPONS):
                continue
            row = i // 3
            col = i % 3
            x = 360 + col * 48
            y = 112 + row * 88
            spec = WEAPONS[item_index]
            selected = spec["id"] == self.selected_shop_weapon
            cost = int(spec["cost"] * self.pet_cost_mult())
            affordable = self.gold >= cost
            pyxel.rect(x, y, 42, 68, C.SHADOW)
            pyxel.rectb(x, y, 42, 68, C.GOLD if selected else C.STONE_DARK)
            self.draw_weapon_icon(x + 13, y + 8, spec["id"])
            pyxel.text(x + 4, y + 30, spec["name"][:8], C.WHITE if affordable else C.STONE_DARK)
            pyxel.text(x + 5, y + 43, str(cost), C.GOLD if affordable else C.RED)
            pyxel.text(x + 5, y + 55, "D" + str(spec["damage"]), C.STONE_LIGHT)
        self.draw_small_button(358, 394, 18, 24, "<", C.SHADOW)
        self.draw_small_button(482, 394, 20, 24, ">", C.SHADOW)

    def draw_weapon_popup(self):
        weapon = self.selected_weapon()
        if weapon is None:
            return
        x, y, w, h = self.weapon_popup_rect(weapon)
        pyxel.rect(x, y, w, h, C.NIGHT)
        pyxel.rectb(x, y, w, h, C.GOLD)
        pyxel.rectb(x + 2, y + 2, w - 4, h - 4, C.STONE_DARK)
        pyxel.text(x + 8, y + 8, weapon.spec["name"][:22] + " L" + str(weapon.level), C.GOLD)
        pyxel.text(x + w - 13, y + 7, "x", C.RED)
        dmg, rng, ndmg, nrng = self.current_and_next_stats(weapon)
        pyxel.text(x + 8, y + 24, "DMG " + str(dmg) + (" -> " + str(ndmg) if ndmg else ""), C.WHITE)
        pyxel.text(x + 8, y + 36, "RNG " + str(rng) + (" -> " + str(nrng) if nrng else ""), C.WHITE)
        up = weapon.upgrade_cost()
        up_text = "MAX" if up is None else str(int(up * self.pet_upgrade_cost_mult())) + " Gold"
        pyxel.text(x + 8, y + 48, "Upgrade: " + up_text, C.STONE_LIGHT)
        need = weapon.ability_need()
        fill = int(58 * min(need, weapon.ability_charge) / need)
        pyxel.text(x + 8, y + 62, "Ability", C.STONE_LIGHT)
        pyxel.rect(x + 62, y + 62, 60, 7, C.SHADOW)
        pyxel.rect(x + 63, y + 63, fill, 5, C.GOLD)
        if self.can_weapon_merge(weapon):
            self.draw_small_button(x + 130, y + h - 46, 60, 16, "MERGE", C.PURPLE)
        self.draw_small_button(x + 8, y + h - 24, 60, 16, "UP", C.POISON)
        self.draw_small_button(x + 74, y + h - 24, 50, 16, "SELL", C.RED)
        self.draw_small_button(x + 130, y + h - 24, 60, 16, "SKILL", C.GOLD)

    def draw_merge_overview(self):
        pyxel.rect(64, 82, 384, 304, C.NIGHT)
        pyxel.rectb(64, 82, 384, 304, C.PURPLE)
        pyxel.text(86, 98, "MOEGLICHE MERGES", C.GOLD)
        self.draw_small_button(432, 92, 56, 20, "ZU", C.RED)
        rows = self.all_available_merges()
        if not rows:
            pyxel.text(92, 126, "Noch kein aktiver Merge. Rezepte:", C.STONE_LIGHT)
            recipes = []
            for key, (cost, result_id) in MERGES.items():
                ids = list(key)
                recipes.append((WEAPON_BY_ID[ids[0]]["name"], WEAPON_BY_ID[ids[1]]["name"], cost, WEAPON_BY_ID[result_id]["name"]))
            for i, (a_name, b_name, cost, result_name) in enumerate(recipes[:11]):
                y = 146 + i * 19
                pyxel.text(92, y, a_name[:10] + " + " + b_name[:10], C.WHITE)
                pyxel.text(226, y, "=> " + result_name[:15], C.PURPLE)
                pyxel.text(356, y, str(cost), C.GOLD)
            pyxel.text(92, 364, "Beide Zutaten muessen Level 5 sein.", C.GOLD)
            return
        for i, (a, b, cost, result_id) in enumerate(rows[:7]):
            y = 138 + i * 38
            pyxel.rect(84, y, 334, 30, C.SHADOW)
            pyxel.rectb(84, y, 334, 30, C.STONE_DARK)
            pyxel.text(94, y + 6, a.spec["name"][:12] + " + " + b.spec["name"][:12], C.WHITE)
            pyxel.text(94, y + 18, "=> " + WEAPON_BY_ID[result_id]["name"] + "   " + str(cost) + " Gold", C.GOLD)
            self.draw_small_button(350, y + 8, 68, 18, "MERGE", C.PURPLE)

    def draw_merge_confirm(self):
        if self.merge_confirm is None:
            return
        a, b, cost, result_id = self.merge_confirm
        pyxel.rect(126, 190, 260, 134, C.NIGHT)
        pyxel.rectb(126, 190, 260, 134, C.GOLD)
        pyxel.text(154, 208, "Merge bestaetigen?", C.GOLD)
        pyxel.text(148, 230, a.spec["name"][:15] + " + " + b.spec["name"][:15], C.WHITE)
        pyxel.text(148, 246, "wird zu: " + WEAPON_BY_ID[result_id]["name"], C.PURPLE)
        pyxel.text(148, 262, "Kosten: " + str(cost) + " Gold", C.GOLD if self.gold >= cost else C.RED)
        self.draw_small_button(178, 278, 66, 26, "JA", C.POISON)
        self.draw_small_button(268, 278, 66, 26, "NEIN", C.RED)

    def draw_achievements(self):
        pyxel.rect(42, 78, 428, 330, C.NIGHT)
        pyxel.rectb(42, 78, 428, 330, C.GOLD)
        pyxel.rectb(48, 84, 416, 318, C.STONE_DARK)
        pyxel.text(70, 96, "ERRUNGENSCHAFTEN", C.GOLD)
        self.draw_small_button(426, 88, 60, 22, "ZU", C.RED)
        for i, entry in enumerate(ACHIEVEMENTS):
            stage = self.achievements[entry["id"]]
            y = 126 + i * 34
            pyxel.rect(64, y, 362, 28, C.SHADOW)
            pyxel.rectb(64, y, 362, 28, C.STONE_DARK)
            if stage >= len(entry["targets"]):
                pyxel.text(76, y + 7, entry["name"] + " komplett", C.GOLD)
                pyxel.text(270, y + 7, "OK", C.POISON)
                continue
            target = entry["targets"][stage]
            value = self.achievement_value(entry)
            gold = entry["gold"][stage]
            soul = entry["soul"][stage]
            ready = value >= target
            pyxel.text(76, y + 5, entry["name"][:14], C.WHITE)
            pyxel.text(178, y + 5, str(min(value, target)) + "/" + str(target), C.STONE_LIGHT)
            pyxel.text(256, y + 5, "+" + str(gold) + "G +" + str(soul) + "S", C.GOLD)
            self.draw_small_button(356, y + 7, 70, 18, "HOLEN" if ready else "OFFEN", C.POISON if ready else C.STONE_DARK)

    def draw_pet_shop(self):
        pyxel.rect(46, 78, 420, 354, C.NIGHT)
        pyxel.rectb(46, 78, 420, 354, C.POISON)
        pyxel.rectb(52, 84, 408, 342, C.STONE_DARK)
        pyxel.text(70, 96, "PET-SHOP  Soul: " + str(self.soul) + "  Aktiv: " + str(len(self.pets)) + "/6", C.GOLD)
        self.draw_small_button(434, 88, 58, 22, "ZU", C.RED)
        start = self.pet_page * 4
        for i in range(4):
            idx = start + i
            if idx >= len(PETS):
                continue
            spec = PETS[idx]
            y = 124 + i * 64
            pet = None
            for owned in self.pets:
                if owned.pet_id == spec["id"]:
                    pet = owned
                    break
            pyxel.rect(70, y, 360, 56, C.SHADOW)
            pyxel.rectb(70, y, 360, 56, spec["color"])
            preview = type("PetPreview", (), {})()
            preview.pet_id = spec["id"]
            preview.level = pet.level if pet else 1
            preview.spec = spec
            self.draw_pet(92, y + 21, preview)
            level_text = "L" + str(pet.level) if pet else "neu"
            pyxel.text(120, y + 7, spec["name"] + " - " + spec["role"], C.WHITE)
            pyxel.text(120, y + 20, level_text, C.GOLD if pet else C.STONE_LIGHT)
            pyxel.text(120, y + 33, PET_HINTS.get(spec["id"], "")[:34], C.STONE_LIGHT)
            if pet is None:
                label = "KAUF " + str(spec["cost"])
                color = C.POISON if self.soul >= spec["cost"] and len(self.pets) < 6 else C.STONE_DARK
            elif pet.level >= 5:
                label = "MAX"
                color = C.GOLD
            else:
                label = "UP " + str(pet.upgrade_cost())
                color = C.POISON if self.soul >= pet.upgrade_cost() else C.STONE_DARK
            self.draw_small_button(350, y + 20, 80, 22, label, color)
        self.draw_small_button(86, 396, 44, 24, "<", C.SHADOW)
        self.draw_small_button(382, 396, 44, 24, ">", C.SHADOW)
        pyxel.text(210, 404, "Seite " + str(self.pet_page + 1) + "/5", C.STONE_LIGHT)

    def draw_shop(self):
        pyxel.rect(54, 88, 404, 294, C.NIGHT)
        pyxel.rectb(54, 88, 404, 294, C.GOLD)
        pyxel.rectb(58, 92, 396, 286, C.PURPLE)
        pyxel.text(74, 104, "SPEZIAL-SHOP", C.GOLD)
        pyxel.text(74, 119, "Soul Coins: " + str(self.soul), C.PURPLE)
        pyxel.rect(438, 92, 62, 18, C.RED)
        pyxel.rectb(438, 92, 62, 18, C.WHITE)
        pyxel.text(452, 98, "ZU", C.WHITE)
        for i, offer in enumerate(SHOP_OFFERS):
            col = i % 2
            row = i // 2
            x = 76 + col * 190
            y = 136 + row * 56
            name, desc, cost, color = offer
            self.draw_shop_card(x, y, name, desc, cost, color)

    def draw_shop_card(self, x, y, name, desc, cost, color):
        pyxel.rect(x, y, 170, 44, C.SHADOW)
        pyxel.rectb(x, y, 170, 44, color)
        pyxel.text(x + 10, y + 8, name, C.WHITE)
        pyxel.text(x + 10, y + 20, desc, C.STONE_LIGHT)
        self.draw_soul(x + 10, y + 31)
        pyxel.text(x + 23, y + 32, str(cost), C.PURPLE)

    def draw_title_screen(self):
        pyxel.rect(0, 0, WIDTH, HEIGHT, C.NIGHT)
        for x, y, phase in self.stars:
            if (pyxel.frame_count // 12 + phase) % 3 != 1:
                pyxel.pset(x, y, C.WHITE)
        pyxel.rect(36, 52, 440, 348, C.SHADOW)
        pyxel.rectb(36, 52, 440, 348, C.GOLD)
        pyxel.rectb(42, 58, 428, 336, C.STONE_LIGHT)
        pyxel.text(154, 82, "BURGDEFENSE ULTIMATE", C.GOLD)
        pyxel.text(151, 96, "pixel tower defense", C.STONE_LIGHT)
        self.draw_title_castle(232, 168)
        self.draw_weapon_large(118, 215, 7, 5)
        self.draw_weapon_large(178, 235, 15, 4)
        self.draw_weapon_large(334, 232, 103, 2)
        self.draw_weapon_large(392, 214, 20, 3)
        self.draw_title_zombie_line()
        pyxel.rect(176, 318, 160, 34, C.GOLD)
        pyxel.rectb(176, 318, 160, 34, C.WHITE)
        pyxel.text(226, 331, "START", C.NIGHT)
        pyxel.text(108, 366, "Klick: starten    Maus: Waffe kaufen und platzieren", C.WHITE)
        pyxel.text(143, 380, "Unsichtbares Raster: fast jede freie Flaeche ist nutzbar", C.STONE_LIGHT)

    def draw_title_castle(self, x, y):
        pyxel.rect(x - 31, y + 8, 62, 38, C.GOLD)
        pyxel.rectb(x - 31, y + 8, 62, 38, C.STONE_DARK)
        for i in range(5):
            pyxel.rect(x - 30 + i * 13, y, 8, 10, C.GOLD)
            pyxel.rectb(x - 30 + i * 13, y, 8, 10, C.STONE_DARK)
        pyxel.rect(x - 43, y + 4, 18, 42, C.STONE_LIGHT)
        pyxel.rect(x + 25, y + 4, 18, 42, C.STONE_LIGHT)
        pyxel.tri(x - 47, y + 4, x - 34, y - 12, x - 21, y + 4, C.RED)
        pyxel.tri(x + 21, y + 4, x + 34, y - 12, x + 47, y + 4, C.RED)
        pyxel.rect(x - 8, y + 27, 16, 19, C.RED)
        pyxel.circb(x, y + 18, 48, C.WHITE)

    def draw_title_zombie_line(self):
        for i, zid in enumerate([0, 1, 2, 5, 6, 9, 11, 14]):
            z = type("PreviewZombie", (), {})()
            spec = ZOMBIES[zid]
            z.id = zid
            z.size = 20 if zid != 14 else 34
            z.color = spec["color"]
            z.flash = 0
            z.freeze = 0
            z.burn = 0
            z.poison = 0
            z.shield = 20 if zid in (2, 14) else 0
            z.hp = 10
            z.max_hp = 10
            self.draw_zombie_preview(100 + i * 45, 285, z)

    def draw_zombie_preview(self, x, y, z):
        old_frame = pyxel.frame_count
        self.draw_zombie_body(x, y, z, z.color, 0)
        if z.id == 14:
            pyxel.circb(x, y, 21, C.GOLD)

    def draw_pause(self):
        pyxel.rect(128, 174, 256, 170, C.NIGHT)
        pyxel.rectb(128, 174, 256, 170, C.GOLD)
        pyxel.rectb(136, 182, 240, 154, C.STONE_DARK)
        pyxel.text(232, 194, "PAUSE", C.GOLD)
        self.draw_small_button(180, 214, 152, 30, "WEITER", C.POISON)
        self.draw_small_button(180, 254, 152, 30, "RESTART", C.ORANGE)
        self.draw_small_button(180, 294, 152, 30, "MENU", C.SHADOW)

    def draw_game_over(self):
        pyxel.rect(76, 112, 360, 260, C.NIGHT)
        pyxel.rectb(76, 112, 360, 260, C.RED)
        pyxel.rectb(82, 118, 348, 248, C.GOLD)
        pyxel.text(218, 134, "DEFEAT", C.RED)
        self.draw_map_preview(104, 158, 118, 64, MAPS[self.map_index]["path"], MAPS[self.map_index]["style"])
        pyxel.text(242, 158, "Level " + str(self.map_index + 1), C.GOLD)
        pyxel.text(242, 172, MAPS[self.map_index]["name"][:22], C.WHITE)
        pyxel.text(242, 188, "Modus: " + self.difficulty_name, C.STONE_LIGHT)
        pyxel.line(104, 238, 408, 238, C.STONE_DARK)
        pyxel.text(122, 256, "Erreichte Welle", C.STONE_LIGHT)
        pyxel.text(256, 256, str(self.best_wave), C.GOLD)
        pyxel.text(122, 272, "Kills", C.STONE_LIGHT)
        pyxel.text(256, 272, str(self.kills), C.WHITE)
        pyxel.text(122, 288, "Gold uebrig", C.STONE_LIGHT)
        pyxel.text(256, 288, str(self.gold), C.GOLD)
        pyxel.text(122, 304, "Waffen gebaut", C.STONE_LIGHT)
        pyxel.text(256, 304, str(len(self.weapons)), C.POISON)
        self.draw_small_button(142, 330, 104, 28, "NEUSTART", C.POISON)
        self.draw_small_button(266, 330, 104, 28, "MENU", C.SHADOW)


Game()