import pyxel
import random
import math

W = 256
H = 256

DIFFICULTY = {
    "EASY":   {"enemy_speed": 0.8,  "spawn_base": 110, "boss_hp_mult": 0.7,  "coin_mult": 1.2},
    "NORMAL": {"enemy_speed": 1.0,  "spawn_base": 85,  "boss_hp_mult": 1.0,  "coin_mult": 1.0},
    "HARD":   {"enemy_speed": 1.35, "spawn_base": 62,  "boss_hp_mult": 1.4,  "coin_mult": 0.85},
}

BLACK=0; NAVY=1; PURPLE=2; GREEN=3
BROWN=4; DKBLUE=5; LGRAY=6; WHITE=7
RED=8; ORANGE=9; YELLOW=10; LIME=11
CYAN=12; GRAY=13; PINK=14; BEIGE=15

INTRO_TEXTS = [
    ["XENAR COMMANDERS", "ANNIHILATE THEM ALL"],
    ["YOU ARE VIPER 7",  "EARTHS LAST PILOT"],
    ["MOTHERSHIP",       "DOCK HERE TO UPGRADE"],
]

# Shop: upgrade-upgrades (always 3 offered)
SHOP_ITEMS_UPG = [
    {"id": "shield_dur",    "name": "SHIELD+",    "desc": "Shield +3s",    "cost": 15, "max": 4},
    {"id": "laser_dur",     "name": "LASER+",     "desc": "Laser +2s",     "cost": 15, "max": 4},
    {"id": "double_dur",    "name": "2X LONG",    "desc": "2x shot +3s",   "cost": 15, "max": 4},
    {"id": "coin_val",      "name": "SALVAGE",    "desc": "Coins x1.25",   "cost": 20, "max": 4},
    {"id": "auto_coin",     "name": "AUTOMINER",  "desc": "Passiv coins",  "cost": 25, "max": 3},
    {"id": "coin_kill",     "name": "LOOTER",     "desc": "+1 coin/kill",  "cost": 20, "max": 3},
    {"id": "spread",        "name": "SPREAD+",    "desc": "+2 shots/stk",  "cost": 20, "max": 6},
    {"id": "pierce",        "name": "PIERCE",     "desc": "Shots pierce",  "cost": 25, "max": 5},
    {"id": "bounce",        "name": "BOUNCE",     "desc": "Shots bounce",  "cost": 20, "max": 4},
    {"id": "split",         "name": "SPLIT",      "desc": "Shot splits x2","cost": 25, "max": 3},
    {"id": "kill_burst",    "name": "KILL BURST", "desc": "Kill=splitters","cost": 20, "max": 5},
    {"id": "chain_light",   "name": "CHAIN LTN",  "desc": "Kill chains",   "cost": 30, "max": 4},
    {"id": "burn_mark",     "name": "BURN MARK",  "desc": "Hit stacks DoT","cost": 20, "max": 5},
    {"id": "shield_nova",   "name": "SH NOVA",    "desc": "Shield->AoE",   "cost": 25, "max": 3},
    {"id": "greed_power",   "name": "GREED->PWR", "desc": "Coins->tmp dmg","cost": 25, "max": 5},
    {"id": "kill_streak_fr","name": "KILLSTREAK", "desc": "Kills->firerate","cost": 20, "max": 5},
]

# Shop: weapons/ship (always 3 offered)
SHOP_ITEMS_BUILD = [
    {"id": "auto_fire",    "name": "AUTO GUN",   "desc": "Autofire->laser","cost": 20, "max": 5},
    {"id": "tracker",      "name": "TRACKER",    "desc": "Homing shots",   "cost": 30, "max": 3},
    {"id": "damage",       "name": "DAMAGE+",    "desc": "Bullet dmg+1",   "cost": 25, "max": 3},
    {"id": "fire",         "name": "RAPID GUN",  "desc": "Fire rate+1",    "cost": 15, "max": 5},
    {"id": "double",       "name": "TWIN GUNS",  "desc": "Perm 2x shot",   "cost": 25, "max": 1},
    {"id": "hp",           "name": "HULL+",      "desc": "Max HP +1",      "cost": 20, "max": 4},
    {"id": "regen",        "name": "NANOHEAL",   "desc": "HP regen",       "cost": 25, "max": 3},
    {"id": "speed",        "name": "THRUST+",    "desc": "Speed +1",       "cost": 15, "max": 5},
    {"id": "wingman_lvl",  "name": "WINGMAN",    "desc": "Wingman/upg",    "cost": 35, "max": 3},
    {"id": "bomb_stock",   "name": "BOMB RACK",  "desc": "+1 bomb (X)",    "cost": 20, "max": 3},
    {"id": "orbitals",     "name": "ORBITALS",   "desc": "+1 satellite",   "cost": 30, "max": 4},
    {"id": "glass_cannon", "name": "GLASS CANN", "desc": "Dmg++ -1 HP",    "cost": 30, "max": 3},
    {"id": "speed_damage", "name": "SPD=DMG",    "desc": "Speed->bonus dmg","cost": 25, "max": 4},
    {"id": "wingman_swarm","name": "WM SWARM",   "desc": "+1 WM inherit",  "cost": 40, "max": 4},
]


def _ct(cx, y, text, col):
    pyxel.text(cx - len(text) * 2, y, text, col)


# 5×5 pixel-art bitmap font (each char = 5 rows of 5 cols, 'X'=on '.'=off)
_BIG_FONT = {
    ' ': ["     ","     ","     ","     ","     "],
    'A': [".XXX.","X...X","XXXXX","X...X","X...X"],
    'B': ["XXXX.","X...X","XXXX.","X...X","XXXX."],
    'C': [".XXX.","X....","X....","X.....",".XXX."],
    'D': ["XXXX.","X...X","X...X","X...X","XXXX."],
    'E': ["XXXXX","X....","XXXX.","X....","XXXXX"],
    'F': ["XXXXX","X....","XXXX.","X....","X...."],
    'G': [".XXX.","X....","X..XX","X...X",".XXXX"],
    'H': ["X...X","X...X","XXXXX","X...X","X...X"],
    'I': [".XXX.","..X..","..X..","..X..",".XXX."],
    'J': ["..XXX","....X","....X","X...X",".XXX."],
    'K': ["X...X","X..X.","XXX..","X..X.","X...X"],
    'L': ["X....","X....","X....","X....","XXXXX"],
    'M': ["X...X","XX.XX","X.X.X","X...X","X...X"],
    'N': ["X...X","XX..X","X.X.X","X..XX","X...X"],
    'O': [".XXX.","X...X","X...X","X...X",".XXX."],
    'P': ["XXXX.","X...X","XXXX.","X....","X...."],
    'Q': [".XXX.","X...X","X.X.X","X..XX",".XXXX"],
    'R': ["XXXX.","X...X","XXXX.","X.X..","X..XX"],
    'S': [".XXXX","X.....",".XXX.","....X","XXXX."],
    'T': ["XXXXX","..X..","..X..","..X..","..X.."],
    'U': ["X...X","X...X","X...X","X...X",".XXX."],
    'V': ["X...X","X...X","X...X",".X.X.","..X.."],
    'W': ["X...X","X...X","X.X.X","XX.XX","X...X"],
    'X': ["X...X",".X.X.","..X..",".X.X.","X...X"],
    'Y': ["X...X",".X.X.","..X..","..X..","..X.."],
    'Z': ["XXXXX","...X.","..X..",".X...","XXXXX"],
}


def init_sounds():
    pyxel.sounds[0].set("c4", "s", "6", "f", 5)
    pyxel.sounds[1].set("g2f2e2", "n", "543", "f", 8)
    pyxel.sounds[2].set("c2a1g1", "n", "765", "f", 14)
    pyxel.sounds[3].set("c3e3g3c4e4g4c4", "t", "4567654", "n", 18)
    pyxel.sounds[4].set("c4e4g4", "t", "567", "n", 12)
    pyxel.sounds[5].set("a2f2", "s", "76", "f", 10)
    pyxel.sounds[6].set("e3g3b3", "t", "567", "n", 14)
    pyxel.sounds[7].set("g2f2e2d2c2", "t", "76543", "f", 25)
    pyxel.sounds[8].set(
        "c3e3g3c4g3e3c3e3g3b3g3e3f3a3c4a3",
        "t", "5556666655566666", "n", 35)
    pyxel.sounds[9].set(
        "c3c3g2g2a2a2g2f2f2e2e2d2d2c2",
        "t", "44445555444455", "n", 48)
    pyxel.sounds[10].set("c2g1", "n", "76", "f", 6)
    pyxel.sounds[11].set("c1c1", "n", "77", "f", 8)


def init_music():
    # === UI / Menu Music — Lofi Jazz Chiptune in A minor ===
    # Two channels: ch3 = melody (triangle), ch2 = bass+harmony (pulse)
    # All phrases: 16 notes × speed 48 = 768 ticks each → loop = 1536 ticks

    # Melody A — ascends from E4 through A4, gracefully resolves back
    pyxel.sounds[20].set(
        "e4g4a4g4e4c4d4e4f4e4d4c4a3b3c4r",
        "t", "4567775556666544", "nnnnvnnnnnvnnnnn", 48)
    # Melody B — step-wise climb through C major, settles on E
    pyxel.sounds[21].set(
        "c4d4e4f4g4f4e4d4c4e4g4a4g4f4e4r",
        "t", "4556776656777644", "nnnnnvnnnnnnvnnn", 48)

    # Bass+Harmony A — root bass stabs (t) interlaced with chord tones (p)
    # Am root / Am chord / F root / F chord  (8 notes × 96 = 768 ticks)
    pyxel.sounds[22].set(
        "a1c3e3a1f1a2c3f1",
        "tppttppt", "45554455", "nnnnnnnn", 96)
    # Bass+Harmony B — C root / C chord / G root / G chord
    pyxel.sounds[23].set(
        "c2e3g3c2g1b2d3g1",
        "tppttppt", "45554455", "nnnnnnnn", 96)

    # === In-Game Music — Driving Chiptune in A minor ===
    # Speed 32 (faster/more tense than UI), staccato rests give punch
    # 16 notes × 32 = 512 ticks per phrase | bass: 8 notes × 64 = 512 ticks

    # Melody A — punchy staccato with octave leaps, forward momentum
    pyxel.sounds[30].set(
        "a4e4a4c4b4ra4g4f4c4f4ra4g4e4r",
        "t", "6776755676755655", "nnnnnnnnnnnnnnnn", 32)
    # Melody B — ascending run that climbs then falls back, builds tension
    pyxel.sounds[31].set(
        "c4b4a4g4f4e4f4g4a4b4c4d4e4rd4r",
        "t", "6677665566777655", "nnnnnnnnnnnnnnvn", 32)

    # Bass A — driving pulse on Am and F roots (pulse wave = punchy)
    pyxel.sounds[32].set(
        "a1a1a1a1f1f1f1f1",
        "p", "67575757", "nnnnnnnn", 64)
    # Bass B — driving pulse on C and G roots
    pyxel.sounds[33].set(
        "c2c2c2c2g1g1g1g1",
        "p", "67575757", "nnnnnnnn", 64)


def play_ui_music():
    pyxel.play(3, [20, 21], loop=True)   # melody on ch 3
    pyxel.play(2, [22, 23], loop=True)   # bass+harmony on ch 2


def play_game_music():
    stop_music()
    pyxel.play(3, [30, 31], loop=True)   # melody on ch 3
    pyxel.play(2, [32, 33], loop=True)   # bass on ch 2


def stop_music():
    pyxel.stop(2)
    pyxel.stop(3)


class Particle:
    def __init__(self, x, y, col, vx=None, vy=None, life=None):
        self.x = float(x)
        self.y = float(y)
        self.col = col
        self.vx = vx if vx is not None else random.uniform(-2.5, 2.5)
        self.vy = vy if vy is not None else random.uniform(-3.0, 0.5)
        self.life = life if life is not None else random.randint(10, 22)
        self.max_life = self.life

    def update(self):
        self.x += self.vx
        self.y += self.vy
        self.vy += 0.12
        self.life -= 1
        return self.life > 0

    def draw(self):
        if self.life > self.max_life * 0.6:
            col = self.col
        elif self.life > self.max_life * 0.3:
            col = ORANGE
        else:
            col = RED
        pyxel.pset(int(self.x), int(self.y), col)


class BossBullet:
    def __init__(self, x, y, vx, vy, col=RED):
        self.x = float(x)
        self.y = float(y)
        self.vx = vx
        self.vy = vy
        self.col = col
        self.alive = True

    def draw(self):
        pyxel.pset(int(self.x),     int(self.y),     WHITE)
        pyxel.pset(int(self.x) + 1, int(self.y),     self.col)
        pyxel.pset(int(self.x),     int(self.y) + 1, self.col)
        pyxel.pset(int(self.x) + 1, int(self.y) + 1, self.col)


class Game:

    SKINS = [
        # shape "fighter" = classic pointed fighter
        {"name": "VIPER",   "shape": "fighter",
         "col_body": DKBLUE, "col_wing": LGRAY,   "col_nose": WHITE,
         "col_engine": CYAN,   "col_cockpit": CYAN,   "locked": False},
        {"name": "PHOENIX", "shape": "fighter",
         "col_body": RED,    "col_wing": ORANGE,  "col_nose": YELLOW,
         "col_engine": YELLOW, "col_cockpit": WHITE,  "locked": False},
        {"name": "NOVA",    "shape": "fighter",
         "col_body": PURPLE, "col_wing": PINK,    "col_nose": WHITE,
         "col_engine": PINK,   "col_cockpit": YELLOW, "locked": False},
        # shape "delta" = wide flat delta wing
        {"name": "COBRA",   "shape": "delta",
         "col_body": GREEN,  "col_wing": LIME,    "col_nose": WHITE,
         "col_engine": LIME,   "col_cockpit": YELLOW, "locked": False},
        {"name": "RAPTOR",  "shape": "delta",
         "col_body": ORANGE, "col_wing": YELLOW,  "col_nose": WHITE,
         "col_engine": RED,    "col_cockpit": WHITE,  "locked": False},
        # shape "needle" = long thin interceptor
        {"name": "NEEDLE",  "shape": "needle",
         "col_body": CYAN,   "col_wing": WHITE,   "col_nose": WHITE,
         "col_engine": CYAN,   "col_cockpit": DKBLUE, "locked": False},
        {"name": "GHOST",   "shape": "needle",
         "col_body": LGRAY,  "col_wing": WHITE,   "col_nose": CYAN,
         "col_engine": PURPLE, "col_cockpit": PURPLE, "locked": False},
        # shape "heavy" = wide armored cruiser
        {"name": "TITAN",   "shape": "heavy",
         "col_body": NAVY,   "col_wing": LGRAY,   "col_nose": WHITE,
         "col_engine": ORANGE, "col_cockpit": CYAN,   "locked": False},
        # Locked skins
        {"name": "SHADOW",  "shape": "fighter",
         "col_body": BLACK,  "col_wing": NAVY,    "col_nose": CYAN,
         "col_engine": CYAN,   "col_cockpit": CYAN,
         "locked": True, "unlock_kills": 50},
        {"name": "GOLD",    "shape": "heavy",
         "col_body": YELLOW, "col_wing": ORANGE,  "col_nose": WHITE,
         "col_engine": RED,    "col_cockpit": WHITE,
         "locked": True, "unlock_kills": 150},
    ]

    def __init__(self):
        pyxel.init(W, H, title="SPACE FIGHT - ROGUE EDITION", display_scale=2, fps=60)
        init_sounds()
        init_music()
        self.state = "intro"
        self.intro_page = 0
        self.intro_timer = 0
        self.selected_diff = "NORMAL"
        self.selected_skin = 0
        self.unlocked_skins = {i: not s["locked"] for i, s in enumerate(self.SKINS)}
        self.highscore = 0
        self.all_time_kills = 0
        # 3-layer parallax stars: [x, y, speed, layer, style]
        # style: 0=dot, 1=small cross, 2=medium cross, 3=sparkle
        self.stars = []
        for _ in range(60):
            self.stars.append([random.randint(0, W), random.randint(0, H),
                                random.uniform(0.05, 0.2), 0,
                                random.randint(0, 1)])
        for _ in range(60):
            self.stars.append([random.randint(0, W), random.randint(0, H),
                                random.uniform(0.3, 0.55), 1,
                                random.randint(1, 3)])
        for _ in range(45):
            self.stars.append([random.randint(0, W), random.randint(0, H),
                                random.uniform(0.7, 1.3), 2, 0])
        self.menu_timer = 0
        self.music_on = True
        self._setup_music()
        self.reset_game(first=True)
        pyxel.run(self.update, self.draw)

    def _setup_music(self):
        if self.music_on:
            play_ui_music()

    def reset_game(self, first=False):
        diff = DIFFICULTY[self.selected_diff]
        self.diff_cfg         = diff
        self.player_x         = float(W // 2 - 8)
        self.player_y         = float(H - 30)
        self.player_speed     = 2.5
        self.player_hp        = 3
        self.player_max_hp    = 3
        self.fire_rate        = 12
        self.fire_timer       = 0
        self.invincible       = 0
        self.bullets          = []   # [x, y, vx, vy]
        self.enemies          = []
        self.particles        = []
        self.boss_bullets     = []
        self.powerups         = []
        self.boss             = None
        self.boss_dying       = None
        self.score            = 0
        self.coins            = 0
        self.wave             = 1
        self.kills            = 0
        self.kills_since_boss = 0
        self.kills_for_boss   = 25
        self.bosses_defeated  = 0
        self.enemy_timer      = 0
        self.powerup_timer    = 0
        self.wingman          = None   # kept for compat; real list is self.wingmen
        self.shield_active    = False
        self.shield_timer     = 0
        self.double_shot      = False
        self.double_timer     = 0
        self.run_kills        = 0
        self.show_boss_text   = 0
        self.bomb_count       = 0
        self.laser_timer      = 0
        self.timeslow_timer   = 0
        self.magnet_timer     = 0
        self.cutscene_timer   = 0
        self.cutscene_ship_y  = float(H - 30)
        self.regen_timer      = 0
        self.auto_coin_timer  = 0
        self.auto_fire_timer  = 0
        self.shoot_key_held   = False   # rising-edge tracker for manual fire
        self.player_tilt      = 0.0    # -1.0=left lean, 0=center, +1.0=right lean
        self.shop_offer       = []
        self.shop_cursor      = 0
        self.perks = {
            "speed": 0, "fire": 0, "hp": 0, "shield_dur": 0,
            "wingman_lvl": 0, "double": 0, "bomb_stock": 0,
            "auto_fire": 0, "tracker": 0, "damage": 0,
            "regen": 0, "laser_dur": 0, "double_dur": 0,
            "coin_val": 0, "auto_coin": 0, "coin_kill": 0,
            # Familie A
            "spread": 0, "pierce": 0, "bounce": 0, "split": 0,
            "orbitals": 0,
            # Familie B
            "kill_burst": 0, "chain_light": 0, "burn_mark": 0, "shield_nova": 0,
            # Familie C
            "greed_power": 0, "kill_streak_fr": 0,
            # Familie D
            "glass_cannon": 0, "speed_damage": 0, "wingman_swarm": 0,
        }
        self.orbitals         = []   # [{"angle": float, "shoot_t": int}]
        self.orbital_timer    = 0
        self.kill_streak      = 0
        self.kill_streak_timer= 0
        self.greed_power_timer= 0
        self.chain_fx         = []   # [(x1,y1,x2,y2,timer)]
        self.wingmen          = []   # list of wingman dicts (replaces single wingman)
        if not first:
            self.state = "game"
            play_game_music()

    # ──────────────── UPDATE ────────────────
    def update(self):
        self.menu_timer += 1
        self._scroll_stars()
        if   self.state == "intro":    self._update_intro()
        elif self.state == "menu":     self._update_menu()
        elif self.state == "skin":     self._update_skin()
        elif self.state == "diff":     self._update_diff()
        elif self.state == "game":     self._update_game()
        elif self.state == "cutscene": self._update_cutscene()
        elif self.state == "shop":     self._update_shop()
        elif self.state in ("gameover", "victory"): self._update_endscreen()

    def _scroll_stars(self):
        if self.state == "cutscene":
            m = 3.5
        elif self.state == "game" and self.timeslow_timer > 0:
            m = 0.18
        elif self.state in ("menu", "intro", "skin", "diff"):
            m = 0.25
        else:
            m = 1.0
        for s in self.stars:
            s[1] += s[2] * m * 60 / 60
            if s[1] > H + 2:
                s[0] = random.randint(0, W)
                s[1] = -2.0

    def _update_intro(self):
        self.intro_timer += 1
        if pyxel.btnp(pyxel.KEY_RETURN) or pyxel.btnp(pyxel.KEY_SPACE) or pyxel.btnp(pyxel.KEY_S):
            self.intro_page += 1
            self.intro_timer = 0
            if self.intro_page >= len(INTRO_TEXTS):
                self.state = "menu"

    def _update_menu(self):
        if pyxel.btnp(pyxel.KEY_RETURN) or pyxel.btnp(pyxel.KEY_S):
            self.state = "skin"
        if pyxel.btnp(pyxel.KEY_H):
            self.music_on = not self.music_on
            if self.music_on:
                self._setup_music()
            else:
                stop_music()

    def _update_skin(self):
        if pyxel.btnp(pyxel.KEY_LEFT):
            self.selected_skin = (self.selected_skin - 1) % len(self.SKINS)
        if pyxel.btnp(pyxel.KEY_RIGHT):
            self.selected_skin = (self.selected_skin + 1) % len(self.SKINS)
        if pyxel.btnp(pyxel.KEY_RETURN) or pyxel.btnp(pyxel.KEY_S):
            if self.unlocked_skins[self.selected_skin]:
                self.state = "diff"

    def _update_diff(self):
        diffs = list(DIFFICULTY.keys())
        idx = diffs.index(self.selected_diff)
        if pyxel.btnp(pyxel.KEY_LEFT):
            self.selected_diff = diffs[(idx - 1) % len(diffs)]
        if pyxel.btnp(pyxel.KEY_RIGHT):
            self.selected_diff = diffs[(idx + 1) % len(diffs)]
        if pyxel.btnp(pyxel.KEY_RETURN) or pyxel.btnp(pyxel.KEY_S):
            self.reset_game()

    def _update_endscreen(self):
        if pyxel.btnp(pyxel.KEY_RETURN) or pyxel.btnp(pyxel.KEY_S):
            self.state = "menu"
            play_ui_music()

    def _update_cutscene(self):
        self.cutscene_timer += 1
        self.cutscene_ship_y -= 1.4
        if (self.cutscene_timer >= 160
                or pyxel.btnp(pyxel.KEY_RETURN)
                or pyxel.btnp(pyxel.KEY_SPACE)):
            self.state = "shop"
            self._generate_shop()

    # ──────────────── GAME UPDATE ───────────
    def _update_game(self):
        diff = self.diff_cfg

        if self.invincible > 0:     self.invincible -= 1
        if self.shield_timer > 0:
            self.shield_timer -= 1
            self.shield_active = self.shield_timer > 0
        if self.laser_timer > 0:    self.laser_timer -= 1
        if self.timeslow_timer > 0: self.timeslow_timer -= 1
        if self.magnet_timer > 0:   self.magnet_timer -= 1
        if self.double_timer > 0:   self.double_timer -= 1

        # HP regen
        if self.perks.get("regen", 0) > 0:
            intervals = [600, 400, 250]
            regen_int = intervals[min(self.perks["regen"] - 1, 2)]
            self.regen_timer += 1
            if self.regen_timer >= regen_int:
                self.regen_timer = 0
                max_hp = self.player_max_hp
                if self.player_hp < max_hp:
                    self.player_hp += 1

        # Passive coins
        if self.perks.get("auto_coin", 0) > 0:
            coin_int = max(55, 180 - self.perks["auto_coin"] * 50)
            self.auto_coin_timer += 1
            if self.auto_coin_timer >= coin_int:
                self.auto_coin_timer = 0
                self.coins += 1
                self.score += 1

        # Auto-fire level 5 = permanent laser
        if self.perks.get("auto_fire", 0) >= 5:
            if self.laser_timer <= 0:
                self.laser_timer = 45

        slow = 0.3 if self.timeslow_timer > 0 else 1.0

        # Player movement
        spd = self.player_speed + self.perks["speed"] * 0.4
        if pyxel.btn(pyxel.KEY_LEFT)  or pyxel.btn(pyxel.KEY_A): self.player_x -= spd
        if pyxel.btn(pyxel.KEY_RIGHT) or pyxel.btn(pyxel.KEY_D): self.player_x += spd
        if pyxel.btn(pyxel.KEY_UP)    or pyxel.btn(pyxel.KEY_W): self.player_y -= spd * 0.7
        if pyxel.btn(pyxel.KEY_DOWN)  or pyxel.btn(pyxel.KEY_S): self.player_y += spd * 0.7
        self.player_x = max(0, min(self.player_x, W - 19))
        self.player_y = max(H // 2, min(self.player_y, H - 20))

        # Tilt animation
        _tilt_left  = pyxel.btn(pyxel.KEY_LEFT)  or pyxel.btn(pyxel.KEY_A)
        _tilt_right = pyxel.btn(pyxel.KEY_RIGHT) or pyxel.btn(pyxel.KEY_D)
        _tilt_target = -1.0 if _tilt_left else (1.0 if _tilt_right else 0.0)
        self.player_tilt += (_tilt_target - self.player_tilt) * 0.15

        if pyxel.btnp(pyxel.KEY_X) and self.bomb_count > 0:
            self._use_bomb()

        # Manual shooting — rising-edge only (new press, not held)
        self.fire_timer += 1
        streak_bonus = min(self.kill_streak, 10) * self.perks.get("kill_streak_fr", 0) // 3
        gc_bonus     = self.perks.get("glass_cannon", 0) * 2
        greed_bonus  = 2 if self.greed_power_timer > 0 else 0
        eff_rate = max(4, self.fire_rate - self.perks["fire"] * 2 - streak_bonus - gc_bonus - greed_bonus)
        shoot_down = pyxel.btn(pyxel.KEY_SPACE) or pyxel.btn(pyxel.KEY_Z)
        shoot_new  = shoot_down and not self.shoot_key_held  # first frame of press
        self.shoot_key_held = shoot_down
        if shoot_new and self.fire_timer >= eff_rate:
            self.fire_timer = 0
            self._fire_shot()

        # Auto-fire levels 1-4
        af_lvl = self.perks.get("auto_fire", 0)
        if 1 <= af_lvl <= 4:
            af_int = max(8, 28 - af_lvl * 5)
            self.auto_fire_timer += 1
            if self.auto_fire_timer >= af_int:
                self.auto_fire_timer = 0
                self._fire_shot(silent=True)

        # Laser beam damage
        if self.laser_timer > 0:
            lx = self.player_x + 7
            for e in self.enemies[:]:
                if abs(e["x"] + 7 - lx) < 10 and e["y"] < self.player_y:
                    e["hp"] -= 0.18
                    if e["hp"] <= 0:
                        self._kill_enemy(e)
            if self.boss and not self.boss_dying:
                if abs(self.boss["x"] + 24 - lx) < 28:
                    self.boss["hp"] -= 0.12
                    if self.boss["hp"] <= 0:
                        self._kill_boss()

        # Move bullets with optional homing
        tracker_lvl = self.perks.get("tracker", 0)
        split_lvl   = self.perks.get("split", 0)
        new_splits  = []
        for b in self.bullets:
            if tracker_lvl > 0:
                targets = [e for e in self.enemies if e["y"] < b[1]]
                if not targets and self.boss and not self.boss_dying:
                    bx2 = self.boss["x"] + 24
                    by2 = self.boss["y"] + 15
                    dx = bx2 - b[0]; dy = by2 - b[1]
                    dist = max(1.0, math.sqrt(dx*dx + dy*dy))
                    turn = 0.25 + tracker_lvl * 0.15
                    b[2] += dx / dist * turn
                    b[3] += dy / dist * turn
                elif targets:
                    nearest = min(targets, key=lambda e: (e["x"]+7-b[0])**2 + (e["y"]-b[1])**2)
                    dx = nearest["x"] + 7 - b[0]; dy = nearest["y"] - b[1]
                    dist = max(1.0, math.sqrt(dx*dx + dy*dy))
                    turn = 0.25 + tracker_lvl * 0.15
                    b[2] += dx / dist * turn
                    b[3] += dy / dist * turn
                spd2 = math.sqrt(b[2]*b[2] + b[3]*b[3])
                if spd2 > 0.1:
                    b[2] = b[2] / spd2 * 6.0
                    b[3] = b[3] / spd2 * 6.0
            b[0] += b[2]
            b[1] += b[3]
            # BOUNCE: reflect off side walls
            if len(b) > 6 and b[6] > 0:
                if b[0] < 1:
                    b[0] = 1.0; b[2] = abs(b[2]); b[6] -= 1
                elif b[0] > W - 3:
                    b[0] = W - 3.0; b[2] = -abs(b[2]); b[6] -= 1
            # SPLIT: split bullet at halfway point
            if split_lvl > 0 and len(b) > 5 and not b[5]:
                origin_y = b[7] if len(b) > 7 else self.player_y
                if b[1] < origin_y - 70:
                    b[5] = True
                    spd2 = math.sqrt(b[2]*b[2] + b[3]*b[3])
                    for ang_off in [-0.45, 0.45]:
                        nb = self._make_bullet(b[0], b[1],
                            b[2]*math.cos(ang_off) - b[3]*math.sin(ang_off),
                            b[2]*math.sin(ang_off) + b[3]*math.cos(ang_off))
                        nb[5] = True  # don't split again
                        new_splits.append(nb)
        self.bullets.extend(new_splits)
        self.bullets = [b for b in self.bullets if -8 < b[1] < H + 8 and -8 < b[0] < W + 8]

        # Boss bullets
        for bb in self.boss_bullets:
            bb.x += bb.vx * slow
            bb.y += bb.vy * slow
            if bb.x < -5 or bb.x > W + 5 or bb.y < -5 or bb.y > H + 5:
                bb.alive = False
        self.boss_bullets = [bb for bb in self.boss_bullets if bb.alive]

        # Boss dying animation
        if self.boss_dying:
            bd = self.boss_dying
            bd["timer"] += 1
            if bd["timer"] % 4 == 0:
                for _ in range(8):
                    self.particles.append(Particle(
                        bd["x"] + random.randint(-28, 28),
                        bd["y"] + random.randint(-18, 18),
                        random.choice([YELLOW, ORANGE, RED, WHITE, CYAN])))
            if bd["timer"] % 14 == 0 and bd["timer"] < 80:
                pyxel.play(0, 10)
            if bd["timer"] >= 90:
                self.boss_dying = None
                self.cutscene_timer = 0
                self.cutscene_ship_y = float(self.player_y)
                self.state = "cutscene"

        # Trigger boss
        if not self.boss and not self.boss_dying and self.kills_since_boss >= self.kills_for_boss:
            self.kills_since_boss = 0
            self._spawn_boss()

        if not self.boss and not self.boss_dying:
            self._spawn_enemies(diff)

        # Powerup spawn
        self.powerup_timer += 1
        if self.powerup_timer > 200:
            self.powerup_timer = 0
            r = random.random()
            if   r < 0.10: pt = "life"
            elif r < 0.24: pt = "shield"
            elif r < 0.38: pt = "rapid"
            elif r < 0.50: pt = "double_pu"
            elif r < 0.62: pt = "timeslow"
            elif r < 0.74: pt = "laser"
            elif r < 0.86: pt = "magnet"
            else:          pt = "coin_big"
            self.powerups.append({
                "x": float(random.randint(10, W - 20)),
                "y": 0.0, "type": pt, "t": 0,
            })

        # Move enemies
        base_spd = diff["enemy_speed"] * (1.0 + self.wave * 0.04) * slow
        for e in self.enemies:
            if e["type"] == "zigzag":
                e["t"] += 0.14 * slow
                e["x"] = max(0, min(e["x"] + math.sin(e["t"]) * 2.0, W - 14))
            elif e["type"] == "swooper":
                e["t"] += 0.06 * slow
                e["x"] = max(0, min(e["x"] + math.sin(e["t"]) * 3.0, W - 14))
            e["y"] += base_spd * e.get("spd_mult", 1.0)

        if self.boss and not self.boss_dying:
            self._update_boss(slow)

        for p in self.powerups:
            p["y"] += 1.2
            p["t"] = p.get("t", 0) + 1

        if self.magnet_timer > 0:
            for p in self.powerups:
                if p["type"] == "coin_big":
                    dx = self.player_x + 7 - p["x"]
                    dy = self.player_y + 9 - p["y"]
                    dist = max(1.0, math.sqrt(dx*dx + dy*dy))
                    p["x"] += dx / dist * 5
                    p["y"] += dy / dist * 5

        self._check_bullet_enemy()
        self._check_bullet_boss()
        self._check_player_enemy()
        self._check_player_boss_bullets()
        self._check_player_powerups()
        self._check_enemies_passed()

        if self.wingmen:
            self._update_wingman()
        self._update_orbitals()
        self._update_burn_marks()
        self._update_kill_streak()

        self.particles = [p for p in self.particles if p.update()]
        self.powerups  = [p for p in self.powerups  if p["y"] < H + 10]

        if self.player_hp <= 0:
            self.all_time_kills += self.run_kills
            if self.score > self.highscore:
                self.highscore = self.score
            self._unlock_skins()
            pyxel.play(0, 7)
            self.state = "gameover"

    def _update_orbitals(self):
        orb_lvl = self.perks.get("orbitals", 0)
        # Ensure correct count
        while len(self.orbitals) < orb_lvl:
            angle = math.pi * 2 / max(1, orb_lvl) * len(self.orbitals)
            self.orbitals.append({"angle": angle, "shoot_t": 0})
        while len(self.orbitals) > orb_lvl:
            self.orbitals.pop()
        cx = self.player_x + 7; cy = self.player_y + 9
        self.orbital_timer += 1
        for orb in self.orbitals:
            orb["angle"] += 0.07
            orb["shoot_t"] += 1
            if orb["shoot_t"] >= 40:
                orb["shoot_t"] = 0
                ox = cx + math.cos(orb["angle"]) * 22
                oy = cy + math.sin(orb["angle"]) * 22
                # Shoot toward nearest enemy or straight up
                if self.enemies:
                    tgt = min(self.enemies, key=lambda e: (e["x"]+7-ox)**2+(e["y"]+6-oy)**2)
                    dx = tgt["x"]+7 - ox; dy = tgt["y"]+6 - oy
                    dist = max(1.0, math.sqrt(dx*dx+dy*dy))
                    vx2, vy2 = dx/dist*6, dy/dist*6
                else:
                    vx2, vy2 = 0, -6
                nb = [ox, oy, vx2, vy2, 0, True, 0, oy]
                self.bullets.append(nb)

    def _update_burn_marks(self):
        if not self.perks.get("burn_mark", 0): return
        self.menu_timer  # reuse menu_timer for burn tick
        if self.menu_timer % 30 == 0:
            for e in self.enemies[:]:
                if e.get("burn", 0) > 0:
                    e["hp"] -= 0.5
                    e["burn"] = max(0, e["burn"] - 1)
                    self._spawn_hit_particles(e["x"]+7, e["y"]+6, 2, ORANGE)
                    if e["hp"] <= 0:
                        self._kill_enemy(e)

    def _update_kill_streak(self):
        if self.kill_streak > 0:
            self.kill_streak_timer += 1
            if self.kill_streak_timer > 120:
                self.kill_streak = 0
                self.kill_streak_timer = 0
        if self.greed_power_timer > 0:
            self.greed_power_timer -= 1
        # Tick chain lightning visuals
        self.chain_fx = [[x1,y1,x2,y2,t-1] for x1,y1,x2,y2,t in self.chain_fx if t > 1]

    def _make_bullet(self, x, y, vx, vy):
        """Create a bullet list with all extra fields."""
        pierce = self.perks.get("pierce", 0)
        bounce = self.perks.get("bounce", 0)
        # [x, y, vx, vy, pierce_left, split_done, bounce_left, origin_y]
        return [float(x), float(y), float(vx), float(vy), pierce, False, bounce, float(y)]

    def _fire_shot(self, silent=False, from_wingman=False, wx=None, wy=None):
        if from_wingman:
            px, py = float(wx), float(wy)
        else:
            px, py = self.player_x + 7, float(self.player_y)
        spd = 6.0
        spread_lvl = self.perks.get("spread", 0) if not from_wingman else min(self.perks.get("spread", 0), 2)
        # Center shot
        self.bullets.append(self._make_bullet(px, py, 0.0, -spd))
        # SPREAD+ extra shots
        for i in range(spread_lvl):
            ang = 0.22 + i * 0.13
            self.bullets.append(self._make_bullet(px, py, -math.sin(ang)*spd, -math.cos(ang)*spd))
            self.bullets.append(self._make_bullet(px, py,  math.sin(ang)*spd, -math.cos(ang)*spd))
        # TWIN GUNS / double shot
        if not from_wingman and (self.double_shot or self.perks.get("double", 0) or self.double_timer > 0):
            self.bullets.append(self._make_bullet(px - 5, py + 4, 0.0, -spd))
            self.bullets.append(self._make_bullet(px + 5, py + 4, 0.0, -spd))
        if not silent:
            pyxel.play(0, 0)

    def _use_bomb(self):
        self.bomb_count -= 1
        for e in self.enemies[:]:
            self._kill_enemy(e)
        if self.boss and not self.boss_dying:
            self.boss["hp"] -= 25
            if self.boss["hp"] <= 0:
                self._kill_boss()
        self.boss_bullets.clear()
        for _ in range(70):
            self.particles.append(Particle(
                random.randint(0, W), random.randint(0, H),
                random.choice([YELLOW, ORANGE, RED, WHITE, CYAN])))

    def _spawn_enemies(self, diff):
        spawn_t = max(28, diff["spawn_base"] - self.wave * 2)
        self.enemy_timer += 1
        if self.enemy_timer < spawn_t:
            return
        self.enemy_timer = 0
        x = float(random.randint(10, W - 22))
        r = random.random()
        w = self.wave
        if w <= 2:
            etype = "normal"
        elif w <= 5:
            etype = "tank" if r < 0.25 else ("zigzag" if r < 0.45 else "normal")
        elif w <= 9:
            etype = "tank" if r<0.2 else ("zigzag" if r<0.5 else ("swooper" if r<0.7 else "normal"))
        else:
            etype = "tank" if r<0.15 else ("zigzag" if r<0.35 else ("swooper" if r<0.52 else ("bomber" if r<0.65 else "normal")))
        hp_map  = {"normal": 1, "tank": 3, "zigzag": 1, "swooper": 2, "bomber": 2}
        spd_map = {"normal": 1.0, "tank": 0.65, "zigzag": 1.1, "swooper": 1.3, "bomber": 0.8}
        self.enemies.append({
            "x": x, "y": -14.0, "type": etype,
            "hp": hp_map.get(etype, 1), "max_hp": hp_map.get(etype, 1),
            "t": 0.0, "spd_mult": spd_map.get(etype, 1.0),
        })

    def _spawn_boss(self):
        m   = self.diff_cfg["boss_hp_mult"]
        n   = self.bosses_defeated
        # Ab Boss 5: exponentieller Wachstumsfaktor (1.0 für n<4, dann 1.35^(n-3))
        exp = 1.35 ** max(0, n - 3)
        hp  = int((50 + n * 28) * m * exp)
        spd = (1.4 + n * 0.13) * (1.0 + max(0, n - 3) * 0.18)
        sr  = max(5, int((max(15, 72 - n * 5)) / exp))
        self.boss = {
            "num": n + 1, "x": float(W // 2 - 24), "y": 20.0,
            "hp": hp, "max_hp": hp,
            "dir": 1, "speed": spd, "timer": 0,
            "shoot_timer": 0, "shoot_rate": sr, "phase": 1,
        }
        self.show_boss_text = 150
        self.enemies.clear()
        self.boss_bullets.clear()
        self.wave = min(20, self.wave + 1)

    def _update_boss(self, slow=1.0):
        b = self.boss
        b["timer"] += 1
        b["shoot_timer"] += 1
        hp_pct = b["hp"] / b["max_hp"]
        b["phase"] = 3 if hp_pct < 0.25 else (2 if hp_pct < 0.5 else 1)
        phase_spd = b["speed"] * (1.0 + (b["phase"] - 1) * 0.3) * slow
        b["x"] += b["dir"] * phase_spd
        if b["num"] >= 3:
            b["y"] = 20 + math.sin(b["timer"] * 0.05) * 14
        if b["x"] < 4 or b["x"] > W - 52:
            b["dir"] *= -1
        shoot_rate = max(12, b["shoot_rate"] - (b["phase"] - 1) * 10)
        if b["shoot_timer"] >= shoot_rate:
            b["shoot_timer"] = 0
            self._boss_shoot()
        interval = max(55, 120 - b["num"] * 7)
        if b["num"] >= 2 and b["timer"] % interval == 0:
            etype = random.choice(["normal", "zigzag", "swooper"])
            self.enemies.append({
                "x": b["x"] + 24, "y": b["y"] + 28,
                "type": etype, "hp": 1, "max_hp": 1,
                "t": 0.0, "spd_mult": 1.0,
            })

    def _boss_shoot(self):
        b = self.boss
        bx = b["x"] + 24; by = b["y"] + 28
        px = self.player_x + 7; py = self.player_y
        dx = px - bx; dy = py - by
        dist = max(1.0, math.sqrt(dx*dx + dy*dy))
        spd = 2.0 + min(b["num"], 6) * 0.35 + (b["phase"] - 1) * 0.4
        vx = dx / dist * spd; vy = dy / dist * spd
        col_list = [RED, ORANGE, PINK, YELLOW, CYAN, LIME]
        col = col_list[min(b["num"] - 1, len(col_list) - 1)]
        self.boss_bullets.append(BossBullet(bx, by, vx, vy, col))
        if b["phase"] >= 2:
            ang = math.atan2(dy, dx)
            for off in [-0.3, 0.3]:
                a2 = ang + off
                self.boss_bullets.append(BossBullet(bx, by, math.cos(a2)*spd*0.9, math.sin(a2)*spd*0.9, col))
        if b["phase"] == 3 and b["num"] >= 3:
            for i in range(8):
                a = math.pi * 2 / 8 * i
                self.boss_bullets.append(BossBullet(bx, by, math.cos(a)*1.9, math.sin(a)*1.9, PINK))
        pyxel.play(1, 5)

    def _update_wingman(self):
        fire_rate = max(20, 35 - self.perks.get("wingman_lvl", 0) * 5)
        for i, wm in enumerate(self.wingmen):
            # Offset each wingman to the side
            side = -1 if i % 2 == 0 else 1
            gap  = 22 + (i // 2) * 14
            tx   = self.player_x + side * gap
            ty   = self.player_y + 5
            wm["x"] += (tx - wm["x"]) * 0.08
            wm["y"] += (ty - wm["y"]) * 0.08
            wm["timer"] += 1
            if wm["timer"] % fire_rate == 0:
                self._fire_shot(silent=True, from_wingman=True, wx=wm["x"]+7, wy=wm["y"])

    def _overlap(self, x1, y1, w1, h1, x2, y2, w2, h2):
        return x1 < x2+w2 and x1+w1 > x2 and y1 < y2+h2 and y1+h1 > y2

    def _check_bullet_enemy(self):
        spd_total = self.player_speed + self.perks.get("speed", 0) * 0.4
        dmg = (1 + self.perks.get("damage", 0)
               + self.perks.get("glass_cannon", 0) * 2
               + int(spd_total * self.perks.get("speed_damage", 0) * 0.35)
               + (self.perks.get("kill_streak_fr", 0) * min(self.kill_streak, 10) // 5)
               + (2 if self.greed_power_timer > 0 else 0))
        dmg = max(1, dmg)
        burn_lvl = self.perks.get("burn_mark", 0)
        for e in self.enemies[:]:
            for b in self.bullets[:]:
                if self._overlap(b[0], b[1], 3, 6, e["x"], e["y"], 14, 12):
                    e["hp"] -= dmg
                    if burn_lvl > 0:
                        e["burn"] = min(e.get("burn", 0) + 1, burn_lvl * 2)
                    pierce_left = b[4] if len(b) > 4 else 0
                    if pierce_left > 0:
                        b[4] -= 1   # pierce through
                    elif b in self.bullets:
                        self.bullets.remove(b)
                    if e["hp"] <= 0:
                        self._kill_enemy(e)
                    else:
                        self._spawn_hit_particles(e["x"]+7, e["y"]+6, 3, ORANGE)
                    break

    def _kill_enemy(self, e):
        if e in self.enemies: self.enemies.remove(e)
        self._spawn_hit_particles(e["x"]+7, e["y"]+6, 10, YELLOW)
        base = {"tank": 3, "bomber": 2}.get(e["type"], 1)
        base += self.perks.get("coin_kill", 0)
        coin_val = int(base * (1.0 + self.perks.get("coin_val", 0) * 0.25))
        self.coins += coin_val; self.score += coin_val
        self.kills += 1; self.run_kills += 1; self.kills_since_boss += 1
        if self.score % 15 == 0: self.wave = min(20, self.wave + 1)
        if random.random() < 0.07:
            pt = random.choice(["life", "shield", "rapid", "coin_big", "timeslow", "laser", "magnet"])
            self.powerups.append({"x": e["x"], "y": e["y"], "type": pt, "t": 0})
        # KILL BURST: splitter bullets in all directions
        kb = self.perks.get("kill_burst", 0)
        if kb > 0:
            n_shards = kb * 2
            for i in range(n_shards):
                ang = math.pi * 2 / n_shards * i
                spd = 3.5
                nb = [e["x"]+7, e["y"]+6, math.cos(ang)*spd, math.sin(ang)*spd,
                      0, True, 0, e["y"]+6]
                self.bullets.append(nb)
        # CHAIN LIGHTNING: damage next enemies in chain
        cl = self.perks.get("chain_light", 0)
        if cl > 0:
            remaining = list(self.enemies)
            last_x, last_y = e["x"]+7, e["y"]+6
            for _ in range(cl):
                if not remaining: break
                nearest = min(remaining, key=lambda en: (en["x"]+7-last_x)**2+(en["y"]+6-last_y)**2)
                remaining.remove(nearest)
                self.chain_fx.append([last_x, last_y, nearest["x"]+7, nearest["y"]+6, 12])
                nearest["hp"] -= 2
                if nearest["hp"] <= 0:
                    self._kill_enemy(nearest)
                    last_x, last_y = nearest["x"]+7, nearest["y"]+6
                else:
                    self._spawn_hit_particles(nearest["x"]+7, nearest["y"]+6, 4, CYAN)
                    last_x, last_y = nearest["x"]+7, nearest["y"]+6
        # KILL STREAK fire rate ramp
        self.kill_streak += 1
        self.kill_streak_timer = 0
        # GREED→POWER: trigger power buff
        if self.perks.get("greed_power", 0) > 0 and coin_val > 0:
            self.greed_power_timer = max(self.greed_power_timer, 120 + self.perks["greed_power"] * 30)
        pyxel.play(0, 1)

    def _check_bullet_boss(self):
        if not self.boss or self.boss_dying: return
        spd_total = self.player_speed + self.perks.get("speed", 0) * 0.4
        dmg = (1 + self.perks.get("damage", 0)
               + self.perks.get("glass_cannon", 0) * 2
               + int(spd_total * self.perks.get("speed_damage", 0) * 0.35)
               + (2 if self.greed_power_timer > 0 else 0))
        dmg = max(1, dmg)
        for b in self.bullets[:]:
            if self._overlap(b[0], b[1], 3, 6, self.boss["x"], self.boss["y"], 48, 30):
                pierce_left = b[4] if len(b) > 4 else 0
                if pierce_left <= 0 and b in self.bullets:
                    self.bullets.remove(b)
                elif pierce_left > 0:
                    b[4] -= 1
                self.boss["hp"] -= dmg
                self._spawn_hit_particles(self.boss["x"]+24, self.boss["y"]+15, 5, YELLOW)
                if self.boss["hp"] <= 0:
                    self._kill_boss()
                break

    def _kill_boss(self):
        if not self.boss: return
        bx = int(self.boss["x"] + 24); by = int(self.boss["y"] + 15)
        num = self.boss["num"]
        bonus = int((20 + self.bosses_defeated * 12) * self.diff_cfg["coin_mult"])
        self.coins += bonus; self.score += bonus
        self.bosses_defeated += 1
        self.boss = None
        self.boss_bullets.clear()
        self.boss_dying = {"x": bx, "y": by, "timer": 0, "num": num}
        pyxel.play(0, 3)

    def _check_player_enemy(self):
        if self.invincible > 0: return
        for e in self.enemies[:]:
            if self._overlap(self.player_x, self.player_y, 14, 18, e["x"], e["y"], 14, 12):
                self.enemies.remove(e)
                self._hit_player()

    def _check_player_boss_bullets(self):
        if self.invincible > 0: return
        for bb in self.boss_bullets[:]:
            if self._overlap(self.player_x, self.player_y, 14, 18, bb.x-2, bb.y-2, 6, 6):
                bb.alive = False
                self._hit_player()

    def _hit_player(self):
        if self.shield_active:
            self.shield_active = False; self.shield_timer = 0; self.invincible = 60
            # SHIELD NOVA: AoE on shield break
            nova_lvl = self.perks.get("shield_nova", 0)
            if nova_lvl > 0:
                nova_r = 40 + nova_lvl * 20
                cx = self.player_x + 7; cy = self.player_y + 9
                for en in self.enemies[:]:
                    dx = en["x"]+7 - cx; dy = en["y"]+6 - cy
                    if dx*dx + dy*dy <= nova_r*nova_r:
                        self._kill_enemy(en)
                if self.boss and not self.boss_dying:
                    dx = self.boss["x"]+24 - cx; dy = self.boss["y"]+15 - cy
                    if dx*dx + dy*dy <= nova_r*nova_r:
                        self.boss["hp"] -= nova_lvl * 5
                        if self.boss["hp"] <= 0: self._kill_boss()
                for _ in range(30):
                    self.particles.append(Particle(
                        cx + random.randint(-nova_r, nova_r),
                        cy + random.randint(-nova_r, nova_r),
                        random.choice([CYAN, WHITE, PURPLE])))
            pyxel.play(0, 4); return
        self.player_hp -= 1
        self.invincible = 90
        self._spawn_hit_particles(self.player_x+7, self.player_y+9, 14, RED)
        pyxel.play(0, 2)

    def _check_player_powerups(self):
        for p in self.powerups[:]:
            if self._overlap(self.player_x, self.player_y, 14, 18, p["x"], p["y"], 12, 12):
                self.powerups.remove(p)
                self._apply_powerup(p["type"])
                pyxel.play(0, 4)

    def _apply_powerup(self, ptype):
        if ptype == "life":
            self.player_hp = min(self.player_max_hp, self.player_hp + 1)
        elif ptype == "shield":
            dur = 300 + self.perks.get("shield_dur", 0) * 180
            self.shield_timer = dur; self.shield_active = True
        elif ptype == "rapid":
            self.perks["fire"] = min(6, self.perks.get("fire", 0) + 2)
        elif ptype == "double_pu":
            base = 240 + self.perks.get("double_dur", 0) * 180
            self.double_timer = base
        elif ptype == "coin_big":
            val = int(8 * self.diff_cfg["coin_mult"])
            self.coins += val; self.score += val
            if self.perks.get("greed_power", 0) > 0:
                self.greed_power_timer = max(self.greed_power_timer,
                                             120 + self.perks["greed_power"] * 40)
        elif ptype == "timeslow":
            self.timeslow_timer = 360
        elif ptype == "laser":
            base = 240 + self.perks.get("laser_dur", 0) * 120
            self.laser_timer = base
        elif ptype == "magnet":
            self.magnet_timer = 480

    def _check_enemies_passed(self):
        for e in self.enemies[:]:
            if e["y"] > H + 10:
                self.enemies.remove(e)
                self.player_hp -= 1; self.invincible = 45

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

    def _unlock_skins(self):
        total = self.all_time_kills + self.run_kills
        for i, s in enumerate(self.SKINS):
            if s.get("locked") and not self.unlocked_skins[i]:
                if total >= s.get("unlock_kills", 9999):
                    self.unlocked_skins[i] = True

    # ──────────────── SHOP ──────────────────
    def _generate_shop(self):
        avail_upg = [it for it in SHOP_ITEMS_UPG
                     if self.perks.get(it["id"], 0) < it["max"]]
        avail_build = [it for it in SHOP_ITEMS_BUILD
                       if self.perks.get(it["id"], 0) < it["max"]
                       and self._item_allowed(it["id"])]
        random.shuffle(avail_upg)
        random.shuffle(avail_build)
        self.shop_offer  = avail_upg[:3] + avail_build[:3]
        self.shop_cursor = 0

    def _item_allowed(self, item_id):
        if item_id == "tracker"      and self.perks.get("auto_fire",  0) > 0: return False
        if item_id == "auto_fire"    and self.perks.get("tracker",    0) > 0: return False
        if item_id == "glass_cannon" and self.player_max_hp <= 1:            return False
        return True

    def _update_shop(self):
        items = self.shop_offer
        if not items:
            if pyxel.btnp(pyxel.KEY_ESCAPE) or pyxel.btnp(pyxel.KEY_Q):
                self._leave_shop()
            return
        n   = len(items)
        row = self.shop_cursor // 3
        col = self.shop_cursor % 3
        row_count = min(3, n - row * 3)

        if pyxel.btnp(pyxel.KEY_LEFT) or pyxel.btnp(pyxel.KEY_A):
            col = (col - 1) % row_count
            self.shop_cursor = row * 3 + col
        if pyxel.btnp(pyxel.KEY_RIGHT) or pyxel.btnp(pyxel.KEY_D):
            col = (col + 1) % row_count
            self.shop_cursor = row * 3 + col
        if pyxel.btnp(pyxel.KEY_UP) or pyxel.btnp(pyxel.KEY_W):
            new_row = 1 - row
            max_col = min(3, n - new_row * 3) - 1
            if max_col >= 0:
                self.shop_cursor = new_row * 3 + min(col, max_col)
        if pyxel.btnp(pyxel.KEY_DOWN) or pyxel.btnp(pyxel.KEY_S):
            new_row = 1 - row
            max_col = min(3, n - new_row * 3) - 1
            if max_col >= 0:
                self.shop_cursor = new_row * 3 + min(col, max_col)

        if pyxel.btnp(pyxel.KEY_RETURN) or pyxel.btnp(pyxel.KEY_Z) or pyxel.btnp(pyxel.KEY_SPACE):
            it = items[self.shop_cursor]
            if self.coins >= it["cost"]:
                self.coins -= it["cost"]
                self._buy_upgrade(it)
                pyxel.play(0, 6)
                self.shop_offer.pop(self.shop_cursor)
                self.shop_cursor = max(0, min(self.shop_cursor, len(self.shop_offer) - 1))

        if pyxel.btnp(pyxel.KEY_ESCAPE) or pyxel.btnp(pyxel.KEY_Q):
            self._leave_shop()

    def _leave_shop(self):
        self.state = "game"
        play_game_music()

    def _buy_upgrade(self, it):
        uid = it["id"]
        if uid == "wingman_lvl":
            if not self.wingmen:
                self.wingmen.append({"x": self.player_x - 20, "y": self.player_y + 5, "timer": 0})
            self.perks["wingman_lvl"] = min(3, self.perks.get("wingman_lvl", 0) + 1)
        elif uid == "wingman_swarm":
            self.wingmen.append({"x": self.player_x + 20, "y": self.player_y + 5, "timer": 0})
            self.perks["wingman_swarm"] = min(it["max"], self.perks.get("wingman_swarm", 0) + 1)
        elif uid == "hp":
            self.player_max_hp += 1
            self.player_hp = min(self.player_hp + 1, self.player_max_hp)
        elif uid == "double":
            self.double_shot = True; self.perks["double"] = 1
        elif uid == "bomb_stock":
            self.perks["bomb_stock"] = min(3, self.perks.get("bomb_stock", 0) + 1)
            self.bomb_count += 1
        elif uid == "glass_cannon":
            # Trade -1 max HP for big damage boost
            if self.player_max_hp > 1:
                self.player_max_hp -= 1
                self.player_hp = min(self.player_hp, self.player_max_hp)
            self.perks["glass_cannon"] = min(it["max"], self.perks.get("glass_cannon", 0) + 1)
        else:
            self.perks[uid] = min(it["max"], self.perks.get(uid, 0) + 1)

    # ──────────────── DRAW ──────────────────
    def draw(self):
        pyxel.cls(BLACK)
        self._draw_stars()
        if   self.state == "intro":    self._draw_intro()
        elif self.state == "menu":     self._draw_menu()
        elif self.state == "skin":     self._draw_skin()
        elif self.state == "diff":     self._draw_diff()
        elif self.state == "game":     self._draw_game()
        elif self.state == "cutscene": self._draw_cutscene()
        elif self.state == "shop":     self._draw_shop()
        elif self.state == "gameover": self._draw_gameover()
        elif self.state == "victory":  self._draw_victory()

    def _draw_stars(self):
        t = self.menu_timer
        # Far-layer color palette (dim)
        FAR_COLS  = [DKBLUE, NAVY, PURPLE, DKBLUE, LGRAY, PURPLE]
        # Mid-layer color palette (vibrant)
        MID_COLS  = [WHITE, CYAN, PINK, PURPLE, LGRAY, WHITE, CYAN]
        # Near-layer colors (bright)
        NEAR_COLS = [WHITE, CYAN, YELLOW, WHITE]

        for s in self.stars:
            lyr   = s[3]
            style = s[4] if len(s) > 4 else 0
            x, y  = int(s[0]), int(s[1])
            if not (0 <= x < W and 0 <= y < H):
                continue
            h        = int(s[0] * 11 + s[2] * 53) % 100  # stable per-star hash
            twinkle  = (t + int(s[0] * 7)) % 80           # twinkle phase

            if lyr == 0:  # far — dim dots, occasional small cross
                base = FAR_COLS[h % len(FAR_COLS)]
                col  = WHITE if twinkle < 2 else base
                pyxel.pset(x, y, col)
                if style >= 1:
                    if 0 < x < W-1: pyxel.pset(x-1, y, NAVY); pyxel.pset(x+1, y, NAVY)
                    if 0 < y < H-1: pyxel.pset(x, y-1, NAVY); pyxel.pset(x, y+1, NAVY)

            elif lyr == 1:  # mid — colorful crosses
                base = MID_COLS[h % len(MID_COLS)]
                col  = WHITE if twinkle < 5 else base
                dim  = DKBLUE
                pyxel.pset(x, y, col)
                if style >= 1:
                    if 0 < x < W-1:
                        pyxel.pset(x-1, y, col if twinkle < 5 else dim)
                        pyxel.pset(x+1, y, col if twinkle < 5 else dim)
                    if 0 < y < H-1:
                        pyxel.pset(x, y-1, col if twinkle < 5 else dim)
                        pyxel.pset(x, y+1, col if twinkle < 5 else dim)
                if style >= 2:
                    if 1 < x < W-2: pyxel.pset(x-2, y, NAVY); pyxel.pset(x+2, y, NAVY)
                    if 1 < y < H-2: pyxel.pset(x, y-2, NAVY); pyxel.pset(x, y+2, NAVY)
                if style >= 3:  # 4-point sparkle diagonals
                    for dx2, dy2 in [(-1,-1),(1,-1),(-1,1),(1,1)]:
                        nx, ny = x+dx2, y+dy2
                        if 0 <= nx < W and 0 <= ny < H:
                            pyxel.pset(nx, ny, NAVY)

            else:  # near — bright with speed trails
                col = NEAR_COLS[h % len(NEAR_COLS)]
                if self.state in ("game", "cutscene"):
                    trail = max(1, int(s[2] * 5))
                    for tj in range(1, trail + 1):
                        ty = y - tj
                        if 0 <= ty < H:
                            pyxel.pset(x, ty, NAVY if tj > trail//2 else DKBLUE)
                pyxel.pset(x, y, col)

    # ── Big pixel-art title text ──
    def _draw_title_text(self, cx, y, text, col, outline=NAVY, glow=None):
        """Draw text with thick outline + optional glow for a cool retro title look."""
        x = cx - len(text) * 2
        # Thick 2-pixel outline
        for dx in range(-2, 3):
            for dy in range(-2, 3):
                if dx != 0 or dy != 0:
                    pyxel.text(x+dx, y+dy, text, outline)
        # Inner glow ring
        if glow is not None:
            for dx, dy in [(-1,0),(1,0),(0,-1),(0,1)]:
                pyxel.text(x+dx, y+dy, text, glow)
        # Main text
        pyxel.text(x, y, text, col)

    # ── Intro ──
    def _draw_intro(self):
        t    = self.intro_timer
        page = min(self.intro_page, len(INTRO_TEXTS) - 1)
        line1, line2 = INTRO_TEXTS[page]

        if page == 0:
            self._draw_xenar_commander(W // 2, 88, t)
            self._draw_big_text(W // 2, 10, line1, CYAN, scale=2, glow1=NAVY, glow2=DKBLUE)

        elif page == 1:
            sx = W // 2 - 7
            sy = 65 + int(math.sin(t * 0.05) * 5)
            pyxel.circb(sx + 7, sy + 9, 14, DKBLUE)
            self._draw_player_sprite(sx, sy, self.SKINS[self.selected_skin])
            for i in range(4):
                pyxel.pset(sx + 5 + random.randint(-2, 2), sy + 18 + i*2,
                           ORANGE if i < 3 else RED)
            self._draw_big_text(W // 2, 10, line1, CYAN, scale=2, glow1=NAVY, glow2=DKBLUE)

        elif page == 2:
            self._draw_mothership(W // 2 - 30, 48)
            self._draw_big_text(W // 2, 10, line1, PURPLE, scale=2, glow1=NAVY, glow2=PINK)

        if t > 22:
            self._draw_big_text(W // 2, 130, line2, LGRAY, scale=1, glow1=NAVY)

        if t > 50:
            blink = CYAN if t % 24 < 12 else DKBLUE
            _ct(W // 2, H - 14, "PRESS  ENTER  /  SPACE", blink)

    # ── Big pixel-art text helpers ──
    def _draw_big_chars(self, x0, y0, text, scale, col):
        char_w = 5 * scale + scale
        for ci, ch in enumerate(text.upper()):
            rows = _BIG_FONT.get(ch, _BIG_FONT[' '])
            cx0 = x0 + ci * char_w
            for ri, row in enumerate(rows):
                for cj, px in enumerate(row):
                    if px == 'X':
                        pyxel.rect(cx0 + cj * scale, y0 + ri * scale, scale, scale, col)

    def _draw_big_text(self, cx, y, text, col, scale=2, glow1=None, glow2=None):
        char_w   = 5 * scale + scale
        total_w  = len(text) * char_w - scale
        x0       = cx - total_w // 2
        if glow1 is not None:
            for ox, oy in [(-2,0),(2,0),(0,-2),(0,2),(-2,-2),(2,-2),(-2,2),(2,2)]:
                self._draw_big_chars(x0 + ox, y + oy, text, scale, glow1)
        if glow2 is not None:
            for ox, oy in [(-1,0),(1,0),(0,-1),(0,1),(-1,-1),(1,-1),(-1,1),(1,1)]:
                self._draw_big_chars(x0 + ox, y + oy, text, scale, glow2)
        self._draw_big_chars(x0, y, text, scale, col)

    # ── Menu ──
    def _draw_menu(self):
        t = self.menu_timer
        # Very slow sin pulse — never flickers fast, just breathes
        pulse = (math.sin(t * 0.018) + 1.0) * 0.5   # 0..1, ~5.8 s period
        shimmer = pulse > 0.78                         # briefly true → WHITE core

        # ── Decorative enemies drifting slowly down ──
        draw_fns = [self._draw_enemy_normal, self._draw_enemy_zigzag,
                    self._draw_enemy_swooper, self._draw_enemy_bomber,
                    self._draw_enemy_tank]
        for i in range(5):
            ex = int(14 + i * 46 + math.sin(t * 0.011 + i * 1.25) * 14)
            ey = int((t * 0.32 + i * 50) % (H + 30)) - 12
            if 0 <= ex < W - 16 and -14 <= ey < H:
                draw_fns[i % len(draw_fns)](ex, ey)

        # ── Title panel ──
        ty   = 10
        ph   = 64
        pyxel.rect(5, ty, W - 10, ph, BLACK)
        for sl in range(ty + 1, ty + ph, 2):           # CRT scanlines
            pyxel.line(7, sl, W - 8, sl, NAVY)
        bc = CYAN if pulse > 0.45 else DKBLUE
        pyxel.rectb(5, ty, W - 10, ph, bc)
        pyxel.rectb(6, ty + 1, W - 12, ph - 2, DKBLUE)
        pyxel.line(5, ty,        W - 6, ty,        CYAN)
        pyxel.line(5, ty + ph - 1, W - 6, ty + ph - 1, CYAN)
        for cx2, cy2 in [(5,ty),(W-6,ty),(5,ty+ph-1),(W-6,ty+ph-1)]:
            pyxel.pset(cx2, cy2, WHITE)

        # ── "SPACE FIGHT" — 3× pixel-art font, stable CYAN/WHITE, dark glow ──
        title_col  = WHITE if shimmer else CYAN
        glow_mid   = PURPLE if pulse < 0.5 else DKBLUE
        self._draw_big_text(W // 2, ty + 4, "SPACE FIGHT", title_col,
                            scale=3, glow1=NAVY, glow2=glow_mid)

        # ── "ROGUE EDITION" — 2× pixel-art font, PINK, gentle dark glow ──
        sub_col = PINK
        self._draw_big_text(W // 2, ty + 26, "ROGUE EDITION", sub_col,
                            scale=2, glow1=NAVY, glow2=DKBLUE)

        # tagline + divider
        _ct(W // 2, ty + 44, "DEFEND  EARTH  FROM  THE  XENAR", LGRAY)
        div_y = ty + 56
        pyxel.line(14, div_y, W - 15, div_y, DKBLUE)
        pyxel.pset(W // 2, div_y, CYAN if pulse > 0.5 else WHITE)

        # ── Player ship floating in centre ──
        sx = W // 2 - 7
        sy = 108 + int(math.sin(t * 0.05) * 5)
        pyxel.circb(sx + 7, sy + 9, 17, NAVY)
        pyxel.circb(sx + 7, sy + 9, 14, DKBLUE if pulse < 0.5 else NAVY)
        self._draw_player_sprite(sx, sy, self.SKINS[self.selected_skin])
        for i in range(3):
            pyxel.pset(sx + 5 + random.randint(-1, 1), sy + 18 + i * 2,
                       ORANGE if i < 2 else RED)

        # ── PRESS START — CRT scanline box, slow blink ──
        bx, bby, bw, bh = 38, 148, W - 76, 28
        pyxel.rect(bx, bby, bw, bh, BLACK)
        for sl in range(bby + 1, bby + bh, 2):
            pyxel.line(bx + 1, sl, bx + bw - 2, sl, NAVY)
        b_c = WHITE if (t // 25) % 2 == 0 else CYAN   # slow blink ~2.4 s
        pyxel.rectb(bx, bby, bw, bh, b_c)
        pyxel.rectb(bx + 1, bby + 1, bw - 2, bh - 2, DKBLUE)
        for cx2, cy2 in [(bx,bby),(bx+bw-1,bby),(bx,bby+bh-1),(bx+bw-1,bby+bh-1)]:
            pyxel.pset(cx2, cy2, WHITE)
        pyxel.pset(bx + 4, bby + 9, CYAN); pyxel.pset(bx + 5, bby + 9, CYAN)
        pyxel.pset(bx + bw - 5, bby + 9, CYAN); pyxel.pset(bx + bw - 6, bby + 9, CYAN)
        blink_c = WHITE if (t // 30) % 2 == 0 else CYAN
        _ct(W // 2, bby + 5,  "PRESS  ENTER  /  S", blink_c)
        _ct(W // 2, bby + 17, "H = TOGGLE  MUSIC",  GRAY)

        # ── Stats ──
        if self.highscore > 0:
            _ct(W // 2, 184, f"HIGHSCORE: {self.highscore}", YELLOW)
        _ct(W // 2, 194, f"TOTAL KILLS: {self.all_time_kills}", LGRAY)

    # ── Skin ──
    def _draw_skin(self):
        self._draw_title_text(W//2, 22, "SELECT  YOUR  SHIP", CYAN, NAVY, DKBLUE)
        _ct(W//2, 35, "LEFT / RIGHT     ENTER = OK", LGRAY)
        skin   = self.SKINS[self.selected_skin]
        locked = not self.unlocked_skins[self.selected_skin]
        sx = W // 2 - 7
        sy = 84 + int(math.sin(self.menu_timer * 0.05) * 4)
        pyxel.rect(sx-4, sy-4, 26, 30, DKBLUE if not locked else NAVY)
        pyxel.rectb(sx-4, sy-4, 26, 30, CYAN if not locked else GRAY)
        if locked:
            pyxel.text(W//2-12, sy+5, "LOCKED", RED)
            req = f"KILLS: {skin.get('unlock_kills','?')}"
            _ct(W//2, sy+16, req, GRAY)
        else:
            self._draw_player_sprite(sx, sy, skin)
            pyxel.pset(sx+5, sy+20, ORANGE)
            pyxel.pset(sx+10, sy+20, ORANGE)
            pyxel.pset(sx+7, sy+22, RED)
        _ct(W//2, sy+52, skin["name"], CYAN if not locked else GRAY)
        # Thumbnails
        # Two rows of thumbnails (5 per row)
        for i, s in enumerate(self.SKINS):
            row2 = i // 5; col2 = i % 5
            px2 = 8 + col2 * 48; py2 = 170 + row2 * 28
            sel = i == self.selected_skin
            pyxel.rect(px2-2, py2-2, 18, 22, CYAN if sel else BLACK)
            pyxel.rectb(px2-2, py2-2, 18, 22, CYAN if sel else (LGRAY if self.unlocked_skins[i] else NAVY))
            if self.unlocked_skins[i]:
                self._draw_player_sprite(px2, py2, self.SKINS[i])
            else:
                pyxel.rect(px2, py2, 14, 18, NAVY)
                _ct(px2+7, py2+6, "?", GRAY)

    # ── Diff ──
    def _draw_diff(self):
        self._draw_title_text(W//2, 48, "SELECT  DIFFICULTY", CYAN, NAVY, DKBLUE)
        _ct(W//2, 62, "LEFT / RIGHT     ENTER = START", LGRAY)
        diffs = list(DIFFICULTY.keys())
        descs = {
            "EASY":   (GREEN,  "Slower enemies, more coins"),
            "NORMAL": (YELLOW, "Balanced challenge"),
            "HARD":   (RED,    "Fast & aggressive"),
        }
        for i, d in enumerate(diffs):
            x   = W//2 - 66 + i * 46
            y   = 112
            sel = d == self.selected_diff
            bc  = descs[d][0] if sel else NAVY
            pyxel.rect(x-2, y-2, 44, 32, BLACK)
            pyxel.rectb(x-2, y-2, 44, 32, bc)
            if sel:
                pyxel.rectb(x-1, y-1, 42, 30, bc)
            pyxel.text(x+2, y+4, d, WHITE if sel else LGRAY)
            pyxel.text(x+2, y+15, str(i+1)+"*"*i, bc)
        desc = descs[self.selected_diff][1]
        pyxel.rect(20, 156, W-40, 14, BLACK)
        pyxel.rectb(20, 156, W-40, 14, LGRAY)
        _ct(W//2, 160, desc, LGRAY)

    # ── Game ──
    def _draw_game(self):
        for p in self.particles: p.draw()
        skin = self.SKINS[self.selected_skin]
        if self.invincible == 0 or self.menu_timer % 6 < 3:
            self._draw_player_sprite(int(self.player_x), int(self.player_y), skin)
        if self.shield_active:
            r = 12 + int(math.sin(self.menu_timer * 0.2) * 2)
            pyxel.circb(int(self.player_x)+7, int(self.player_y)+9, r, CYAN)
        if self.laser_timer > 0:
            lx = int(self.player_x) + 7
            for ly in range(0, int(self.player_y), 3):
                pyxel.pset(lx,   ly, CYAN if ly%6<3 else WHITE)
                pyxel.pset(lx-1, ly, DKBLUE)
                pyxel.pset(lx+1, ly, DKBLUE)
        if self.timeslow_timer > 0:
            for xb in range(0, W, 6):
                pyxel.pset(xb, 0,   CYAN); pyxel.pset(xb, H-1, CYAN)
            for yb in range(0, H, 6):
                pyxel.pset(0, yb, CYAN); pyxel.pset(W-1, yb, CYAN)
        for wm in self.wingmen:
            wsk = self.SKINS[min(self.selected_skin+1, len(self.SKINS)-1)]
            self._draw_player_sprite(int(wm["x"]), int(wm["y"]), wsk, use_tilt=False)
        # Orbitals
        cx = int(self.player_x) + 7; cy = int(self.player_y) + 9
        for orb in self.orbitals:
            ox = int(cx + math.cos(orb["angle"]) * 22)
            oy = int(cy + math.sin(orb["angle"]) * 22)
            pyxel.circb(ox, oy, 3, CYAN)
            pyxel.pset(ox, oy, WHITE)
        # Chain lightning FX
        for x1,y1,x2,y2,_ in self.chain_fx:
            pyxel.line(int(x1),int(y1),int(x2),int(y2), CYAN)
        for b in self.bullets:
            self._draw_bullet(int(b[0]), int(b[1]))
        for bb in self.boss_bullets: bb.draw()
        for e in self.enemies:       self._draw_enemy(e)
        for p in self.powerups:      self._draw_powerup(p)
        if self.boss and not self.boss_dying:
            self._draw_boss()
        if self.boss_dying:
            bd = self.boss_dying
            t2 = bd["timer"]
            for r_f, col in [(1.0, YELLOW), (0.65, ORANGE), (0.35, RED)]:
                r2 = min(int(t2 * r_f * 0.75), 70)
                if r2 > 0:
                    c2 = col if t2 % 8 < 4 else WHITE
                    pyxel.circb(bd["x"], bd["y"], r2, c2)
        self._draw_hud()
        if self.show_boss_text > 0:
            self.show_boss_text -= 1
            if self.boss:
                n   = self.boss["num"]
                msg = f"XENAR COMMANDER #{n}"
                if self.show_boss_text % 8 < 5:
                    _ct(W//2,   H//2-4, msg, YELLOW)
                    _ct(W//2+1, H//2-3, msg, ORANGE)

    # ── Cutscene ──
    def _draw_cutscene(self):
        t = self.cutscene_timer
        ms_y = 28 + int(math.sin(t * 0.05) * 3)
        self._draw_mothership(W//2 - 30, ms_y)
        ship_y = max(ms_y + 42, int(self.cutscene_ship_y))
        self._draw_player_sprite(W//2 - 7, ship_y, self.SKINS[self.selected_skin], use_tilt=False)
        for i in range(4):
            pyxel.pset(W//2 - 2 + random.randint(-3, 3), ship_y + 18 + i*3,
                        ORANGE if i < 3 else RED)
        if t > 50:
            bx2 = W // 2
            for by2 in range(ms_y + 40, ship_y, 4):
                col = CYAN if (by2 + t) % 8 < 4 else DKBLUE
                pyxel.pset(bx2-2, by2, col); pyxel.pset(bx2, by2, col); pyxel.pset(bx2+2, by2, col)
        _ct(W//2, H-52, "RETURNING TO MOTHERSHIP...", CYAN if t%20<10 else WHITE)
        if t > 70: _ct(W//2, H-40, "DOCKING IN PROGRESS...", LGRAY)
        if t > 110:
            _ct(W//2, H-20, "ENTER/SPACE = SKIP", GRAY if t%20<10 else DKBLUE)

    # ── HUD ──
    def _draw_hud(self):
        pyxel.rect(0, 0, 72, 42, BLACK)
        pyxel.rectb(0, 0, 72, 42, NAVY)
        pyxel.text(4, 4,  f"SCORE {self.score}", WHITE)
        pyxel.text(4, 13, f"COINS {self.coins}", YELLOW)
        pyxel.text(4, 22, f"WAVE  {self.wave}",  CYAN)
        boss_label = f"BOSS #{self.bosses_defeated + 1}"
        pyxel.text(4, 31, boss_label, RED if self.boss else PURPLE)
        max_hp = self.player_max_hp
        for i in range(max_hp):
            hx  = W - 12 - i * 10
            col = RED if i < self.player_hp else NAVY
            self._draw_heart(hx, 4, col)
        indicators = []
        if self.double_shot or self.perks.get("double"):
            indicators.append(("2x", LIME))
        elif self.double_timer > 0:
            indicators.append((f"2x{self.double_timer//60+1}", LIME))
        if self.shield_active:           indicators.append(("SH", CYAN))
        if self.laser_timer > 0:         indicators.append(("LZ", CYAN))
        if self.timeslow_timer > 0:      indicators.append(("SL", PURPLE))
        if self.magnet_timer > 0:        indicators.append(("MG", YELLOW))
        if self.wingmen:                 indicators.append((f"WM{len(self.wingmen)}", YELLOW))
        if self.bomb_count > 0:          indicators.append((f"B{self.bomb_count}", ORANGE))
        if self.perks.get("auto_fire"):    indicators.append((f"AF{self.perks['auto_fire']}", PINK))
        if self.perks.get("tracker"):      indicators.append(("TRK", CYAN))
        if self.perks.get("spread"):       indicators.append((f"SP{self.perks['spread']}", LIME))
        if self.perks.get("orbitals"):     indicators.append((f"O{self.perks['orbitals']}", CYAN))
        if self.kill_streak >= 3:          indicators.append((f"KS{self.kill_streak}", ORANGE))
        if self.greed_power_timer > 0:     indicators.append(("PWR", YELLOW))
        if self.perks.get("glass_cannon"): indicators.append((f"GC{self.perks['glass_cannon']}", RED))
        for i, (label, col) in enumerate(indicators):
            pyxel.text(4, H - 20 + i * 9, label, col)
        if not self.boss and not self.boss_dying:
            prog = min(1.0, self.kills_since_boss / self.kills_for_boss)
            pyxel.text(W-50, H-18, "BOSS", LGRAY)
            pyxel.rect(W-50, H-8, 46, 4, NAVY)
            pyxel.rect(W-50, H-8, int(46*prog), 4, ORANGE)
            pyxel.rectb(W-50, H-8, 46, 4, LGRAY)
        hi = f"HI {self.highscore}"
        pyxel.text(W - len(hi)*4 - 2, 4, hi, LGRAY)

    # ── Shop ──
    def _draw_shop(self):
        for i in range(0, W, 16): pyxel.line(i, 0, i, H, NAVY)
        for i in range(0, H, 16): pyxel.line(0, i, W, i, NAVY)
        pyxel.rect(6, 6, W-12, H-12, BLACK)
        pyxel.rectb(6, 6, W-12, H-12, CYAN)
        pyxel.rectb(7, 7, W-14, H-14, DKBLUE)
        # Header
        pyxel.rect(6, 6, W-12, 44, NAVY)
        pyxel.rectb(6, 6, W-12, 44, CYAN)
        self._draw_mothership(W-68, 8)
        self._draw_title_text(W//2, 11, "MOTHERSHIP  UPGRADES", CYAN, NAVY, WHITE)
        _ct(W//2, 21, f"CREDITS: {self.coins}", YELLOW)
        _ct(W//2, 31, "ARROWS=MOVE  ENTER=BUY  Q=LEAVE", LGRAY)

        if not self.shop_offer:
            _ct(W//2, H//2,    "[ NO MORE UPGRADES ]", GRAY)
            _ct(W//2, H//2+14, "PRESS Q TO CONTINUE",  LGRAY)
            return

        items  = self.shop_offer
        card_w = 74
        card_h = 82

        # Row labels
        pyxel.rect(8, 52, 60, 8, BLACK)
        pyxel.text(10, 53, "UPGRADES:", PURPLE)
        pyxel.rect(8, 142, 88, 8, BLACK)
        pyxel.text(10, 143, "WEAPONS & SHIP:", ORANGE)

        for i, it in enumerate(items):
            row = i // 3
            col = i % 3
            cx  = 10 + col * (card_w + 4)
            cy  = 60 + row * 90
            sel = i == self.shop_cursor
            can = self.coins >= it["cost"]
            bc  = CYAN if sel else (DKBLUE if can else NAVY)
            pyxel.rect(cx, cy, card_w, card_h, BLACK)
            pyxel.rectb(cx, cy, card_w, card_h, bc)
            if sel:
                pyxel.rectb(cx+1, cy+1, card_w-2, card_h-2, DKBLUE)
            pyxel.text(cx+2, cy+3,  it["name"][:9], WHITE if sel else LGRAY)
            pyxel.text(cx+2, cy+13, f"{it['cost']}c", YELLOW if can else RED)
            lvl = self.perks.get(it["id"], 0)
            bar_seg = max(1, (card_w - 4) // it["max"])
            for lv in range(it["max"]):
                lc = LIME if lv < lvl else NAVY
                pyxel.rect(cx + 2 + lv * bar_seg, cy + 23, bar_seg - 1, 4, lc)
            desc  = it["desc"]
            words = desc.split()
            line  = ""; dy = 32
            for w in words:
                if len(line) + len(w) + 1 > 10:
                    pyxel.text(cx+2, cy+dy, line, LGRAY)
                    line = w; dy += 9
                else:
                    line = (line + " " + w).strip()
            if line:
                pyxel.text(cx+2, cy+dy, line, LGRAY)

    # ── Game Over ──
    def _draw_gameover(self):
        pyxel.rect(20, 52, W-40, 136, BLACK)
        pyxel.rectb(20, 52, W-40, 136, RED)
        pyxel.rectb(21, 53, W-42, 134, ORANGE)
        self._draw_title_text(W//2, 64, "GAME  OVER",  RED,    BLACK, ORANGE)
        self._draw_title_text(W//2, 78, "XENAR WINS",  ORANGE, BLACK, RED)
        pyxel.line(20, 94, W-20, 94, DKBLUE)
        _ct(W//2, 100, f"SCORE:  {self.score}",                WHITE)
        _ct(W//2, 112, f"HIGHSCORE: {self.highscore}",         YELLOW)
        _ct(W//2, 124, f"COMMANDERS: {self.bosses_defeated}",  CYAN)
        _ct(W//2, 136, f"KILLS: {self.run_kills}",              LGRAY)
        pyxel.line(20, 148, W-20, 148, DKBLUE)
        _ct(W//2, 158, "ENTER / S  TO MENU", LGRAY)
        t = self.menu_timer
        if t % 30 < 15:
            pyxel.rectb(20, 52, W-40, 136, RED)

    def _draw_victory(self):
        t   = self.menu_timer
        self._draw_title_text(W//2, 60, "MISSION COMPLETE",
                              YELLOW if t%20<10 else ORANGE, BLACK, WHITE)
        _ct(W//2, 92,  f"SCORE: {self.score}",         WHITE)
        _ct(W//2, 104, f"HIGHSCORE: {self.highscore}", YELLOW)
        _ct(W//2, 116, f"KILLS: {self.run_kills}",      CYAN)
        if t % 15 == 0:
            for _ in range(20):
                self.particles.append(Particle(
                    random.randint(20, W-20), random.randint(20, H//2),
                    random.choice([YELLOW, ORANGE, RED, CYAN, LIME, PINK]),
                    vx=random.uniform(-3, 3), vy=random.uniform(-4, 1),
                    life=random.randint(15, 35)))
        for p in self.particles: p.update(); p.draw()
        _ct(W//2, 155, "ENTER / S  TO MENU", LGRAY)

    # ──────────────── SPRITES ───────────────
    def _draw_ship_silhouette(self, x, y, col):
        """Ship silhouette for tilt shadow — 19 px wide, 16 px tall."""
        pyxel.tri(x+9, y,    x+6, y+5,  x+12, y+5,  col)  # nose
        pyxel.rect(x+6, y+5, 6, 9, col)                     # fuselage
        pyxel.tri(x,    y+13, x+6, y+7, x+6, y+14, col)    # left wing
        pyxel.tri(x+18, y+13, x+12, y+7, x+12, y+14, col)  # right wing
        pyxel.rect(x+4,  y+13, 4, 3, col)                   # left engine pod
        pyxel.rect(x+10, y+13, 4, 3, col)                   # right engine pod

    def _draw_ship_flames(self, x, y):
        """Animated dual-engine flame — white core, yellow body, orange/red tip."""
        t  = self.menu_timer
        oL = math.sin(t * 0.31)
        oR = math.sin(t * 0.31 + 1.07)
        oW = math.sin(t * 0.65 + 0.4)

        # Engine centres: left x+6, right x+12  (pods end at y+15)
        for ex, osc in ((x+6, oL), (x+12, oR)):
            fl_len = 5 + int(osc * 2.5)   # 2–7 px

            for i in range(fl_len):
                fy = y + 16 + i
                if not (0 <= fy < H):
                    continue
                frac = i / max(fl_len - 1, 1)

                if   frac < 0.18: core = WHITE;  side = YELLOW
                elif frac < 0.45: core = YELLOW; side = ORANGE
                elif frac < 0.78: core = ORANGE; side = RED
                else:             core = RED;    side = RED

                if i == 0:
                    pyxel.pset(ex - 1, fy, ORANGE)
                    pyxel.pset(ex,     fy, WHITE)
                    pyxel.pset(ex + 1, fy, ORANGE)
                elif i == 1:
                    pyxel.pset(ex, fy, core)
                    pyxel.pset(ex + (1 if oW > 0 else -1), fy, side)
                else:
                    pyxel.pset(ex, fy, core)
                    if i == 2 and abs(oW) > 0.6:
                        pyxel.pset(ex + (1 if osc > 0 else -1), fy, side)

    def _draw_player_sprite(self, x, y, skin, use_tilt=True):
        """All skins share one silhouette (19 px wide).  Tilt = NAVY shadow."""
        cb = skin["col_body"]; cw = skin["col_wing"]
        cn = skin["col_nose"]; ce = skin["col_engine"]; cc = skin["col_cockpit"]

        # --- Shadow tilt ---
        if use_tilt and abs(self.player_tilt) > 0.15:
            sp = int(self.player_tilt * 4)
            self._draw_ship_silhouette(x + sp, y + abs(sp), NAVY)

        # --- Flames ---
        self._draw_ship_flames(x, y)

        # --- Wings ---
        pyxel.tri(x,    y+13, x+6,  y+7, x+6,  y+14, cw)
        pyxel.tri(x+18, y+13, x+12, y+7, x+12, y+14, cw)
        pyxel.pset(x+1,  y+11, NAVY)
        pyxel.pset(x+17, y+11, NAVY)

        # --- Engine pods ---
        pyxel.rect(x+4,  y+13, 4, 3, ce)
        pyxel.rect(x+10, y+13, 4, 3, ce)
        pyxel.pset(x+5,  y+15, cc)
        pyxel.pset(x+11, y+15, cc)

        # --- Fuselage ---
        pyxel.rect(x+6, y+5, 6, 9, cb)

        # --- Cockpit canopy ---
        pyxel.rect(x+7, y+6, 4, 5, cc)
        pyxel.pset(x+7,  y+6, WHITE)
        pyxel.pset(x+10, y+6, WHITE)

        # --- Nose ---
        pyxel.tri(x+9, y, x+6, y+5, x+12, y+5, cn)
        pyxel.pset(x+9, y+1, WHITE)

    def _draw_mothership(self, x, y):
        t = self.menu_timer
        blink = CYAN if t%10<5 else DKBLUE
        fl    = ORANGE if t%6<3 else RED
        pyxel.rect(x+5,  y+18, 50, 12, NAVY)
        pyxel.rect(x+10, y+14, 40, 18, DKBLUE)
        pyxel.rect(x+15, y+8,  30, 10, DKBLUE)
        pyxel.rect(x+20, y+3,  20, 8,  PURPLE)
        pyxel.circ(x+30, y+10, 7, NAVY)
        pyxel.circb(x+30, y+10, 7, CYAN)
        pyxel.pset(x+27, y+8, blink); pyxel.pset(x+30, y+7, blink); pyxel.pset(x+33, y+8, blink)
        for i in range(6): pyxel.pset(x+12+i*7, y+20, blink)
        pyxel.tri(x+5,  y+24, x,      y+30, x+10, y+25, DKBLUE)
        pyxel.tri(x+55, y+24, x+60, y+30, x+50, y+25, DKBLUE)
        for ex in [x+12, x+25, x+38, x+48]:
            pyxel.rect(ex, y+30, 4, 3, PURPLE)
            pyxel.pset(ex+1, y+33, fl)
        pyxel.rect(x+22, y+31, 16, 3, LGRAY)
        pyxel.rect(x+24, y+32, 12, 1, CYAN)

    def _draw_xenar_commander(self, cx, cy, t):
        n_tent = 6
        # Tentacles drawn first (behind body)
        for i in range(n_tent):
            ang  = math.pi * (0.12 + 0.76 * i / (n_tent - 1))
            wave = math.sin(t * 0.05 + i * 1.1) * 10
            x0 = int(cx + math.cos(ang) * 18)
            y0 = int(cy + math.sin(ang) * 14 + 4)
            x1 = int(cx + math.cos(ang) * 34 + wave)
            y1 = int(cy + math.sin(ang) * 32 + math.sin(t * 0.04 + i) * 5 + 4)
            for s in range(7):
                f   = s / 6
                px2 = int(x0 + (x1 - x0) * f)
                py2 = int(y0 + (y1 - y0) * f)
                r   = max(1, 3 - int(f * 2))
                pyxel.circ(px2, py2, r, PURPLE if f < 0.5 else NAVY)
            pyxel.pset(x1, y1, CYAN)   # suction tip

        # Body
        pyxel.circ(cx, cy, 22, PURPLE)
        pyxel.circ(cx, cy - 3, 17, NAVY)
        pyxel.circ(cx - 4, cy - 8, 4, PURPLE)  # dome highlight
        for mx, my in [(-8, 4), (0, 6), (8, 4)]:
            pyxel.pset(cx + mx, cy + my, DKBLUE)

        # Eyes
        blink_closed = (t % 90) < 6
        eye_col = CYAN if (t // 15) % 2 == 0 else PINK
        if not blink_closed:
            for ex in (cx - 8, cx + 8):
                pyxel.circ(ex, cy - 2, 5, BLACK)
                pyxel.circ(ex, cy - 2, 3, eye_col)
                pyxel.pset(ex, cy - 3, WHITE)
        else:
            pyxel.line(cx - 12, cy - 2, cx - 4, cy - 2, NAVY)
            pyxel.line(cx +  4, cy - 2, cx + 12, cy - 2, NAVY)

        # Beak
        pyxel.tri(cx - 4, cy + 6, cx + 4, cy + 6, cx, cy + 10, NAVY)
        pyxel.pset(cx, cy + 7, DKBLUE)

        # Pulsing outline
        pulse_col = PINK if t % 50 < 25 else CYAN
        pyxel.circb(cx, cy, 23, pulse_col)

    def _draw_bullet(self, x, y):
        pyxel.pset(x, y, WHITE); pyxel.pset(x+1, y, WHITE)
        pyxel.rect(x, y+1, 2, 4, CYAN)
        pyxel.pset(x, y+5, NAVY); pyxel.pset(x+1, y+5, NAVY)

    def _draw_heart(self, x, y, col):
        pyxel.pset(x+1, y, col); pyxel.pset(x+3, y, col)
        pyxel.rect(x, y+1, 2, 2, col); pyxel.rect(x+3, y+1, 2, 2, col)
        pyxel.rect(x+1, y+2, 3, 2, col)
        pyxel.tri(x, y+3, x+5, y+3, x+2, y+6, col)
        pyxel.pset(x+1, y+1, WHITE)

    def _draw_enemy(self, e):
        x, y = int(e["x"]), int(e["y"])
        if e["hp"] < e["max_hp"]:
            bw = 12
            pyxel.rect(x, y-3, bw, 2, NAVY)
            pyxel.rect(x, y-3, max(1, int(bw * e["hp"] / e["max_hp"])), 2, GREEN)
        if e.get("burn", 0) > 0 and self.menu_timer % 4 < 2:
            pyxel.pset(x+3, y+2, ORANGE)
            pyxel.pset(x+7, y+1, RED)
            pyxel.pset(x+11, y+3, ORANGE)
        t = e["type"]
        if   t == "normal":  self._draw_enemy_normal(x, y)
        elif t == "tank":    self._draw_enemy_tank(x, y)
        elif t == "zigzag":  self._draw_enemy_zigzag(x, y)
        elif t == "swooper": self._draw_enemy_swooper(x, y)
        elif t == "bomber":  self._draw_enemy_bomber(x, y)

    def _draw_enemy_normal(self, x, y):
        """Slime alien — blob body (r=6), glowing eye, 4 tentacles."""
        t = self.menu_timer
        pulse = t % 14 < 7
        body  = PURPLE if pulse else PINK
        inner = PINK   if pulse else PURPLE
        eye   = CYAN   if pulse else WHITE
        pyxel.circ(x+7, y+6, 6, body)
        pyxel.circ(x+7, y+5, 4, inner)
        pyxel.pset(x+5, y+5, eye); pyxel.pset(x+9, y+5, eye)
        pyxel.pset(x+5, y+4, eye); pyxel.pset(x+9, y+4, eye)
        pyxel.pset(x+4, y+6, WHITE)
        pyxel.line(x+5, y+8, x+9, y+8, NAVY); pyxel.pset(x+7, y+8, RED)
        wave = 1 if t % 12 < 6 else 0
        for i, ox in enumerate([x+2, x+5, x+9, x+12]):
            wv = 1 if (i + wave) % 2 == 0 else 0
            pyxel.pset(ox, y+12+wv, body)
            pyxel.pset(ox, y+13,    PURPLE)

    def _draw_enemy_tank(self, x, y):
        """Carapace beast — 16-wide shell, 3 eyes, pulsing core."""
        t = self.menu_timer
        pulse = t % 16 < 8
        armor = DKBLUE if pulse else PURPLE
        eyes  = RED    if pulse else ORANGE
        core  = GREEN  if pulse else LIME
        pyxel.rect(x, y+3, 16, 9, NAVY)
        pyxel.rect(x+2, y, 12, 5, armor)
        pyxel.line(x, y+6,  x+15, y+6,  armor)
        pyxel.line(x, y+10, x+15, y+10, armor)
        pyxel.pset(x+2,  y+4, eyes); pyxel.pset(x+8,  y+4, eyes)
        pyxel.pset(x+13, y+4, eyes)
        pyxel.circ(x+8, y+8, 3, core); pyxel.pset(x+8, y+7, WHITE)
        pyxel.pset(x,    y+4, LGRAY); pyxel.pset(x,    y+7, LGRAY)
        pyxel.pset(x+15, y+4, LGRAY); pyxel.pset(x+15, y+7, LGRAY)
        for i, ox in enumerate([x+2, x+6, x+10, x+14]):
            w2 = 1 if (t + i * 18) % 14 < 7 else 0
            pyxel.pset(ox+w2, y+12, PURPLE)
            pyxel.pset(ox,    y+13, NAVY)

    def _draw_enemy_zigzag(self, x, y):
        """Plasma jellyfish — luminous bell (r=6), electric tendrils."""
        t = self.menu_timer
        pulse = t % 10 < 5
        bell  = CYAN   if pulse else WHITE
        glow  = DKBLUE if pulse else NAVY
        trail = PURPLE if pulse else PINK
        pyxel.circ(x+7, y+5, 6, bell)
        pyxel.circ(x+7, y+5, 4, glow)
        pyxel.pset(x+7, y+3, WHITE)
        pyxel.pset(x+5, y+4, WHITE); pyxel.pset(x+9, y+4, WHITE)
        for cx2 in [x+1, x+4, x+10, x+13]:
            pyxel.pset(cx2, y+9, CYAN if pulse else PINK)
        for i, ox in enumerate([x+2, x+5, x+8, x+11]):
            wv = 1 if (t // 3 + i) % 2 == 0 else -1
            pyxel.pset(ox + wv, y+12, trail)
            pyxel.pset(ox,      y+13, PURPLE)
            pyxel.pset(ox - wv, y+14, NAVY)

    def _draw_enemy_swooper(self, x, y):
        """Manta-ray alien — 16-wide wings, clawed tips."""
        t = self.menu_timer
        pulse = t % 8 < 4
        wing  = ORANGE if pulse else BROWN
        core  = RED    if pulse else ORANGE
        pyxel.tri(x,    y+9, x+8, y+1, x+8, y+10, wing)
        pyxel.tri(x+16, y+9, x+8, y+1, x+8, y+10, wing)
        pyxel.line(x+1,  y+8, x+6,  y+3, BROWN)
        pyxel.line(x+15, y+8, x+10, y+3, BROWN)
        pyxel.line(x+2,  y+9, x+5,  y+6, BROWN)
        pyxel.line(x+14, y+9, x+11, y+6, BROWN)
        pyxel.rect(x+6, y+2, 4, 9, core)
        pyxel.pset(x+8, y+3, WHITE); pyxel.pset(x+7, y+3, RED)
        pyxel.pset(x+8, y+8, ORANGE)
        pyxel.pset(x,    y+10, YELLOW); pyxel.pset(x+1,  y+11, YELLOW)
        pyxel.pset(x+15, y+10, YELLOW); pyxel.pset(x+16, y+11, YELLOW)
        pyxel.pset(x+4,  y+11, BROWN);  pyxel.pset(x+12, y+11, BROWN)

    def _draw_enemy_bomber(self, x, y):
        """Spore pod — r=7 body, volatile core, danger ring."""
        t = self.menu_timer
        pulse = t % 12 < 6
        skin2 = DKBLUE if pulse else NAVY
        spot  = PURPLE if pulse else DKBLUE
        core2 = RED    if pulse else ORANGE
        pyxel.circ(x+7, y+7, 7, skin2)
        pyxel.circ(x+7, y+6, 5, NAVY if pulse else DKBLUE)
        for bx2, by2 in [(x+2,y+2),(x+12,y+2),(x+0,y+7),(x+14,y+7),
                         (x+4,y+12),(x+10,y+12),(x+7,y+1)]:
            pyxel.pset(bx2, by2, spot)
        pyxel.circ(x+7, y+7, 2, core2)
        pyxel.pset(x+7, y+6, WHITE)
        if pulse:
            pyxel.circb(x+7, y+7, 4, RED)
        pyxel.pset(x+6, y+14, DKBLUE); pyxel.pset(x+7, y+15, NAVY)

    def _draw_boss(self):
        b = self.boss
        if not b: return
        x, y  = int(b["x"]), int(b["y"])
        blink  = WHITE if self.menu_timer%10<5 else YELLOW
        blink2 = RED   if self.menu_timer%8<4  else ORANGE
        t      = self.menu_timer
        style  = (b["num"] - 1) % 3
        if   style == 0: self._draw_boss1(x, y, blink, blink2, b["phase"], t)
        elif style == 1: self._draw_boss2(x, y, blink, blink2, b["phase"], t)
        else:            self._draw_boss3(x, y, blink, blink2, b["phase"], t)
        # HP bar
        pyxel.rect(28, 6, 144, 5, NAVY)
        hp_w = int(144 * b["hp"] / b["max_hp"])
        hp_c = GREEN if b["hp"] > b["max_hp"]*0.5 else (YELLOW if b["hp"] > b["max_hp"]*0.25 else RED)
        pyxel.rect(28, 6, hp_w, 5, hp_c)
        pyxel.rectb(28, 6, 144, 5, LGRAY)
        label = f"XENAR CMD #{b['num']}"
        _ct(W//2, 13, label, WHITE)

    def _draw_boss1(self, x, y, blink, blink2, phase, t):
        pyxel.rect(x+6,  y+8,  36, 14, DKBLUE); pyxel.rect(x+12, y+3, 24, 8, NAVY)
        pyxel.rect(x+16, y,    16,  5, PURPLE)
        pyxel.rect(x+2,  y+10,  6,  5, NAVY);   pyxel.rect(x+40, y+10, 6, 5, NAVY)
        pyxel.rect(x,    y+11,  4,  2, LGRAY);   pyxel.rect(x+44, y+11, 4, 2, LGRAY)
        pyxel.rect(x+18, y+4,  12,  5, CYAN)
        pyxel.pset(x+19, y+5, blink); pyxel.pset(x+23, y+5, blink); pyxel.pset(x+27, y+5, blink)
        pyxel.tri(x+6,  y+20, x,    y+28, x+12, y+22, DKBLUE)
        pyxel.tri(x+42, y+20, x+48, y+28, x+36, y+22, DKBLUE)
        fl = ORANGE if t%6<3 else RED
        pyxel.rect(x+4,  y+21, 6, 4, PURPLE); pyxel.rect(x+38, y+21, 6, 4, PURPLE)
        pyxel.pset(x+7,  y+25, fl); pyxel.pset(x+41, y+25, fl)
        if phase >= 2:
            pyxel.pset(x+10, y+15, blink2); pyxel.pset(x+38, y+15, blink2)
            pyxel.rect(x+14, y+18, 20, 3, LGRAY)
            pyxel.pset(x+15, y+18, blink2); pyxel.pset(x+32, y+18, blink2)

    def _draw_boss2(self, x, y, blink, blink2, phase, t):
        pyxel.rect(x+4,  y+7,  40, 16, PURPLE)
        pyxel.rect(x+4,  y+7,  20,  8, NAVY); pyxel.rect(x+24, y+7, 20, 8, NAVY)
        pyxel.rect(x+10, y+2,  28,  8, DKBLUE); pyxel.rect(x+15, y-1, 18, 5, PURPLE)
        pyxel.rect(x,    y+10,  6,  4, NAVY);   pyxel.rect(x+42, y+10, 6, 4, NAVY)
        pyxel.rect(x-4,  y+10,  5,  2, LGRAY);  pyxel.rect(x-4, y+12, 5, 2, CYAN)
        pyxel.rect(x+47, y+10,  5,  2, LGRAY);  pyxel.rect(x+47, y+12, 5, 2, CYAN)
        pyxel.rect(x+16, y+3,  16,  5, CYAN)
        pyxel.pset(x+17, y+4, blink); pyxel.pset(x+22, y+4, blink); pyxel.pset(x+27, y+4, blink)
        pyxel.tri(x+4,  y+21, x-3,  y+29, x+10, y+23, PURPLE)
        pyxel.tri(x+44, y+21, x+51, y+29, x+38, y+23, PURPLE)
        fl = ORANGE if t%6<3 else RED
        pyxel.rect(x+6,  y+22, 5, 5, RED); pyxel.rect(x+20, y+21, 5, 6, ORANGE)
        pyxel.rect(x+37, y+22, 5, 5, RED)
        pyxel.pset(x+8, y+27, fl); pyxel.pset(x+22, y+27, fl); pyxel.pset(x+39, y+27, fl)
        if phase >= 2:
            pyxel.pset(x+12, y+18, blink2); pyxel.pset(x+35, y+18, blink2)
        if phase == 3:
            for i in range(0, 40, 6):
                pyxel.pset(x+4+i, y+21, RED if t%4<2 else ORANGE)

    def _draw_boss3(self, x, y, blink, blink2, phase, t):
        pyxel.rect(x+2,  y+6,  44, 18, RED); pyxel.rect(x+2, y+6, 44, 4, ORANGE)
        pyxel.rect(x,    y+8,   6,  7, NAVY); pyxel.rect(x+42, y+8, 6, 7, NAVY)
        pyxel.rect(x+7,  y+3,   6,  7, PURPLE); pyxel.rect(x+35, y+3, 6, 7, PURPLE)
        pyxel.rect(x-4,  y+10,  5,  2, YELLOW); pyxel.rect(x+47, y+10, 5, 2, YELLOW)
        pyxel.rect(x+8,  y-1,   3,  5, YELLOW); pyxel.rect(x+37, y-1, 3, 5, YELLOW)
        pyxel.rect(x+14, y,    20,  8, DKBLUE); pyxel.rect(x+18, y-3, 12, 5, PURPLE)
        pyxel.rect(x+15, y+1,  18,  7, NAVY)
        for px2 in [x+16, x+21, x+26, x+30]: pyxel.pset(px2, y+3, blink)
        pyxel.pset(x+18, y+5, blink2); pyxel.pset(x+28, y+5, blink2)
        pyxel.tri(x+2,  y+22, x-6,  y+30, x+10, y+24, RED)
        pyxel.tri(x+46, y+22, x+54, y+30, x+38, y+24, RED)
        fl  = ORANGE if t%4<2 else RED
        fl2 = YELLOW if t%4<2 else ORANGE
        for ex in [x+3, x+14, x+29, x+38]:
            pyxel.rect(ex, y+23, 6, 5, PURPLE)
            pyxel.pset(ex+2, y+28, fl); pyxel.pset(ex+2, y+29, fl2)
        if phase >= 2:
            for i in range(0, 44, 4):
                pyxel.pset(x+2+i, y+22, RED if t%4<2 else ORANGE)
        if phase == 3:
            for i in range(6):
                pyxel.pset(x+2+i*7, y+6, YELLOW if t%6<3 else RED)

    def _draw_powerup(self, p):
        x, y = int(p["x"]), int(p["y"])
        t    = p.get("t", 0)
        bob  = int(math.sin(t * 0.15) * 2)
        pt   = p["type"]
        y   += bob
        if pt == "life":
            self._draw_heart(x, y, RED)
        elif pt == "shield":
            r   = 5 + (t % 12 < 6)
            col = CYAN if t%10<5 else DKBLUE
            pyxel.circb(x+5, y+5, r, col); pyxel.pset(x+5, y+5, col)
        elif pt == "rapid":
            c = ORANGE if t%6<3 else YELLOW
            pyxel.tri(x+4, y, x, y+6, x+5, y+6, c)
            pyxel.tri(x+5, y+4, x+9, y+4, x+4, y+10, c)
        elif pt == "double_pu":
            c = LIME if t%6<3 else GREEN
            pyxel.rect(x, y+1, 2, 6, c); pyxel.rect(x+4, y+1, 2, 6, c)
            pyxel.pset(x+1, y, c); pyxel.pset(x+5, y, c)
            pyxel.text(x, y+8, "TMP", c)
        elif pt == "coin_big":
            c = YELLOW if t%8<4 else ORANGE
            pyxel.circ(x+5, y+5, 5, c)
            pyxel.text(x+3, y+3, "$", BROWN)
        elif pt == "timeslow":
            c = PURPLE if t%8<4 else PINK
            pyxel.circb(x+5, y+5, 5, c)
            pyxel.text(x+3, y+3, "S", c)
        elif pt == "laser":
            c = CYAN if t%6<3 else WHITE
            pyxel.line(x+5, y, x+5, y+10, c)
            pyxel.pset(x+4, y+1, c); pyxel.pset(x+6, y+1, c)
            pyxel.pset(x+4, y+9, c); pyxel.pset(x+6, y+9, c)
        elif pt == "magnet":
            c = YELLOW if t%6<3 else ORANGE
            pyxel.circb(x+5, y+5, 4, c)
            pyxel.pset(x+5, y+1, c); pyxel.pset(x+2, y+3, c); pyxel.pset(x+8, y+3, c)


Game()